zangband/0000755000000000000000000000000010250356274011344 5ustar rootrootzangband/lib/0000755000000000000000000000000010250356274012112 5ustar rootrootzangband/lib/apex/0000755000000000000000000000000010250356274013047 5ustar rootrootzangband/lib/apex/readme.txt0000755000000000000000000000015410250356274015050 0ustar rootrootIf you want to compete against my high scores (no wins yet), rename z_scores.raw to scores.raw --ty zangband/lib/apex/z_scores.raw0000755000000000000000000002520010250356274015413 0ustar rootroot2.8.1 6789727 2105177 169959801/15/98MaximumSpider 0m16 3 50 75 50 75Gerard, Strongman of Amber2.8.1 2302968 798940 312646011/08/97Sarastro IV 0m 7 2 41 52 41 52a Plasma hound2.8.1 854009 306559 290138309/28/973rd Burglar 0m 6 3 37 43 37 43a Drolem2.8.1 548135 97848 131393003/05/98Goro II 0m12 8 34 38 34 39a Gravity hound2.8.1 252311 37868 144766011/30/97Destroyer I 0m23 5 31 37 31 37a Multi-hued hound2.8.1 213162 32037 221681812/22/97Fafner I 0m12 2 31 34 31 35Kavlax the Many-Headed2.8.1 12871 722 91325103/29/98Boris Godunov 4 0m26 6 20 12 20 12a Chaos shapechanger2.8.1 8426 248 59046009/01/97Inquisitor I 0m 5 2 20 11 20 11a Blue yeek2.8.1 7718 2873 49399408/22/97Lyre I 0m 0 3 21 8 21 8a Baby black dragon2.8.1 3126 1239 56634507/23/97Irmeli 0f 7 5 15 5 15 6Grishnakh, the Hill Orc2.8.1 2720 796 32224103/19/98Lugosi I 0m26 2 13 8 13 8a Skeleton human2.8.1 2609 153 47165610/03/97Zarathustra II 0m 1 2 14 9 14 9Brodda, the Easterling2.8.1 2524 2114 16198001/19/98Bruce Lee I 0m 6 8 14 7 14 7an Orc shaman2.8.1 2162 2644 20070603/15/98Abo-Minotaur #5 0m29 7 12 8 12 8a Crypt Creep2.8.1 2035 1131 50282008/26/97Death Ranger I 0m 1 4 13 6 13 7a Death sword2.8.1 1881 1656 40233609/29/97Mr. Overkill 0m 5 1 12 6 12 6a Chaos shapechanger2.8.1 1857 1692 66623008/18/97Tomuttaja I 0m 6 1 13 6 13 6a Snaga2.8.1 1783 861 46058908/31/97NormaSnockers 5 0f 7 1 12 6 12 6a Death sword2.8.1 1578 322 19378003/08/98AboMinotaur II 0m29 9 11 7 11 7a Cave orc2.8.1 1378 496 21087908/28/97Oberon II 0m 3 1 10 6 11 6a Novice paladin2.8.1 1370 788 12201603/15/98Nicolai I 0m26 2 10 6 10 6a Disembodied hand that strangl2.8.1 1340 624 21187208/28/97Raimo I 0m 7 3 10 4 10 6a Manes2.8.1 1331 199 26084708/24/97Sarastro II 0m 7 2 11 5 11 5an Ochre jelly2.8.1 1073 285 14395901/19/98BlackReaver 0m24 6 9 4 9 4a King cobra2.8.1 1050 417 11151703/07/98Abo-Minotaur I 0m29 7 9 5 9 5a Snaga2.8.1 1012 189 14702610/03/97Delta 0m 5 4 8 6 8 6a Hunting hawk of Julian2.8.1 978 49 19130903/12/98Obi-Wan II 0m27 6 9 3 9 3an Ewok2.8.1 970 141 16883508/10/97Gurnemanz 0m 7 5 9 4 9 5a Death sword2.8.1 929 625 4421701/06/98ScubaZombie 0m25 0 9 6 9 6a Novice ranger2.8.1 921 633 8155901/25/98Gaia II 0f11 8 8 5 8 5a Phantom warrior2.8.1 613 302 5656612/25/97Drizzzzt 0m19 3 6 3 6 4a Snaga2.8.1 598 195 7535903/22/98Ibrahim II 0m23 2 6 3 6 3a Death sword2.8.1 598 186 6047303/23/98Shub-Niggurath 0m29 4 6 4 6 4a Q(?)2.8.1 585 113 6181101/15/98Osmin I 0m10 7 7 3 7 3a Phantom warrior2.8.1 554 440 8038608/24/97Elric V 0m 1 4 7 2 7 3a Cave spider2.8.1 499 156 5681003/12/98Abo-Minotaur #4 0m29 7 7 1 7 2a Snaga2.8.1 424 386 9183308/24/97Sarastro I 0m 7 2 7 2 7 2a Disembodied hand that strangl2.8.1 407 107 4602201/20/98Gaia 0f 9 8 5 2 5 2a Cave orc2.8.1 392 43 7744003/16/98Ghiaurov IV 0m26 2 5 2 5 2starvation2.8.1 390 110 5095303/23/98Q 0m2910 6 2 6 2a Cave spider2.8.1 388 153 9578408/30/97Klingsor++ 0m 7 1 6 2 6 2Freesia2.8.1 368 108 7476003/16/98Boris Godunov 0m26 2 5 2 5 2a Cave spider2.8.1 352 47 8095903/22/98Puff I 0m20 1 4 2 4 2Fang, Farmer Maggot's dog2.8.1 351 173 2551503/12/98Abo-Minotaur #3 0m29 7 5 2 5 2a Cave spider2.8.1 350 208 3978703/23/98Eddie I 0m25 8 5 2 5 2a Cave spider2.8.1 337 53 5168003/12/98Destructor 0m2910 5 2 5 2a Novice warrior2.8.1 318 118 2424401/15/98Max Carnage 0m11 7 5 2 5 2an Alien queen(?)2.8.1 304 155 6677508/30/97NormaSnockers 3 0f 7 1 4 2 4 2a Novice ranger2.8.1 303 90 4837203/16/98Eddie I 0m29 2 4 2 4 2a Manes2.8.1 279 268 6714207/15/97Dworkin II 0m 5 1 4 2 4 2Freesia2.8.1 278 163 2226503/16/98Ghiaurov IV 0m26 2 3 2 3 2a Novice warrior2.8.1 275 2 1978310/03/97Sarastro III 0m 7 2 4 2 4 2a Red naga2.8.1 257 155 1248103/19/98Ibrahim I 0m23 2 3 2 3 2a Cave spider2.8.1 249 44 2118011/30/97Malekith I 0m19 1 3 2 3 2Fang, Farmer Maggot's dog2.8.1 245 80 790303/29/98Ed Hunter I 0m29 7 3 2 3 2a Cave spider2.8.1 240 249 1087003/12/98Johan af Gran 0m29 9 2 2 2 2Freesia2.8.1 231 134 2685508/30/97NormaSnockers 4 0f 7 1 2 2 2 2a Silver jelly2.8.1 213 28 868008/31/97Lohengrin I 0m 8 5 1 2 1 2a Novice ranger2.8.1 158 41 3296608/28/97Oberon I 0m 3 1 3 1 3 1Fang, Farmer Maggot's dog2.8.1 147 192 2457703/23/98Hunding I 0m10 4 3 0 3 1a Mean looking mercenary2.8.1 125 3 916003/23/98Ktulu Jr 0m2110 2 1 2 1poison2.8.1 122 6 1075108/08/97Dworkin II 0m 1 1 2 1 2 1a fire trap2.8.1 111 0 684508/07/97Klingsor I 0m 4 1 1 1 1 1a Kobold2.8.1 111 100 440303/23/98Boris Godunov 3 0m26 6 1 1 1 1Fang, Farmer Maggot's dog2.8.1 110 78 847303/23/98Ktulu Jr II 0m2110 1 1 1 1a Nibelung2.8.1 109 1 935608/22/97Elric I 0m 2 1 1 1 1 1a pit trap2.8.1 108 11 669008/22/97Elric IV 0m 1 1 1 1 1 1a fatal wound2.8.1 108 50 541503/08/98Obi-Wan 0m27 6 1 1 1 1a Small kobold2.8.1 107 36 1270308/24/97Alberich I 0m 5 1 1 1 1 1a Newt2.8.1 106 0 223908/22/97Elric III 0m 2 1 1 1 1 1a Small kobold2.8.1 106 0 690503/12/98Tankki I 0m29 1 1 1 1 1a Small kobold2.8.1 105 1023 768208/10/97Sir Rounded 0m 7 5 1 1 1 1a Large brown snake2.8.1 105 5 391301/25/98Goro I 0m12 8 1 1 1 1a Snaga2.8.1 105 1 726403/12/98Tankki I 0m23 1 1 1 1 1a Small kobold2.8.1 102 100 94803/16/98Ghiaurov III 0m26 2 1 1 1 1a Newt2.8.1 101 79 7303/23/98Boris Godunov 2 0m26 6 1 1 1 1a Wild cat2.8.1 100 174 13612/18/97Test 0f 0 0 1 1 1 1a Kobold2.8.1 100 284 51512/20/97Test 0f 0 0 1 1 1 1a Kobold2.8.1 100 100 157003/16/98Ghiaurov I 0m26 2 1 1 1 1a fatal wound2.8.1 100 116 73803/16/98Ghiaurov II 0m26 2 1 1 1 1a Kobold2.8.1 4 407 534307/13/97Dworkin I 0m 5 1 1 0 1 0a Mean looking mercenary2.8.1 4 66 304908/22/97Elric II 0m 2 1 1 0 1 0an Agent of black market2.8.1 4 130 309708/30/97Norma Snockers 0f 7 1 1 0 1 0a Battle scarred veteran2.8.1 4 143 503008/30/97NormaSnockers 2 0f 7 1 1 0 1 0a Mean looking mercenary2.8.1 0 2 139810/02/97Zarathustra 0m 1 2 1 0 1 0an Agent of black marketzangband/lib/apex/makefile.zb0000644000000000000000000000046410250356274015165 0ustar rootrootsubdir = ./lib/apex/ ## makefile.zb files += lib/apex/readme.txt lib/apex/z_scores.raw srcfiles += lib/apex/makefile.zb INSTALL += \ touch "$(DESTDIR)/lib/apex/scores.raw"; \ if [ -n "$(GAMEGROUP)" ]; then \ chown -R $(GAMEGROUP) "$(DESTDIR)/lib/apex"; \ chmod -R g+w "$(DESTDIR)/lib/apex"; \ fi; zangband/lib/bone/0000755000000000000000000000000010250356274013035 5ustar rootrootzangband/lib/bone/makefile.zb0000644000000000000000000000033110250356274015144 0ustar rootrootsubdir = ./lib/bone/ ## makefile.zb srcfiles += lib/bone/makefile.zb INSTALL += \ if [ -n "$(GAMEGROUP)" ]; then \ chown -R $(GAMEGROUP) "$(DESTDIR)/lib/bone"; \ chmod -R 070 "$(DESTDIR)/lib/bone"; \ fi; zangband/lib/data/0000755000000000000000000000000010250356274013023 5ustar rootrootzangband/lib/data/makefile.zb0000644000000000000000000000100710250356274015133 0ustar rootrootsubdir = ./lib/data/ srcfiles += lib/data/makefile.zb ## makefile.zb INSTALL += \ touch "$(DESTDIR)/lib/data/a_info.raw";\ touch "$(DESTDIR)/lib/data/e_info.raw";\ touch "$(DESTDIR)/lib/data/f_info.raw";\ touch "$(DESTDIR)/lib/data/k_info.raw";\ touch "$(DESTDIR)/lib/data/r_info.raw";\ touch "$(DESTDIR)/lib/data/v_info.raw";\ touch "$(DESTDIR)/lib/data/misc.raw";\ if [ -n "$(GAMEGROUP)" ]; then \ chown -R root:$(GAMEGROUP) "$(DESTDIR)/lib/data";\ chmod -R g+w "$(DESTDIR)/lib/data";\ fi; zangband/lib/edit/0000755000000000000000000000000010250356274013037 5ustar rootrootzangband/lib/edit/a_info.txt0000755000000000000000000015555310250356274015054 0ustar rootroot# File: a_info.txt # This file is used to initialize the "lib/raw/a_info.raw" file, which # is used to initialize the "artifact" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/raw/a_info.raw" file. # The artifact indexes are defined in "defines.h", and must not be # changed. # Artifacts 1-15 are "special", 16-63 are "armor", and 64-127 are # "weapons". # === Understanding a_info.txt === # N: serial number : item name # I: tval : sval : pval # W: depth : rarity : weight : cost # P: base armor class : base damage : plus to-hit : plus to-dam : plus # to-ac # F: flag | flag |etc # 'N' indicates the beginning of an entry. The serial number must # increase for each new item and artifact activations depend on the # serial number. # 'I' is for basic information. The tval is for the type of item, the # sval identifies the subtype and the pval indicates the amount of # effect the item has, if applicable. # 'W' is for extra information. Depth is the depth the object is # normally found at, rarity determines how common the object is, # weight is in tenth-pounds and cost is the item's value. # 'P' is for power information. The items base armor class, its base # damage and pluses to-hit, to-dam and to-ac. # 'F' is for flags. These are fairly self-explanatory. As many F: # lines may be used as are needed to specify all the flags and flags # are separated by the '|' symbol. # Version stamp (required) V:2.7.5 # The Phial of Galadriel N:1:of Galadriel I:39:4:1 W:20:1:10:2000 P:0:1d1:0:0:0 F:ACTIVATE | SENSE | INSTA_ART | LITE L:USE: msgf("The phial wells with clear light...") L:USE: lite_area(damroll(2, 15), 3) L:USE: object.timeout = rand_range(10, 20) L:DESC: return "illumination every 10-20 turns" # The Star of Elendil N:2:of Elendil I:39:5:1 W:52:25:5:5000 P:0:1d1:0:0:0 F:ACTIVATE | SEE_INVIS | HOLD_LIFE | INSTA_ART | SPEED | LITE L:USE: msgf("The star shines brightly...") L:USE: map_area() L:USE: lite_area(damroll(2, 15), 3) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "magic mapping and illumination every 50-100 turns" # The Arkenstone of Thrain -> JoJ # Was +2 WIS/DEX N:3:of Judgement I:39:6:3 W:80:50:5:15000 P:0:1d1:0:0:0 F:ACTIVATE | SEE_INVIS | HOLD_LIFE | RES_CONF | RES_CHAOS | F:INSTA_ART | SPEED | WIS | INT | LITE L:USE: msgf("The Jewel flashes bright red!") L:USE: wiz_lite() L:USE: msgf("The Jewel drains your vitality...") L:USE: take_hit(damroll(3, 8), "the Jewel of Judgement") L:USE: detect_traps(TRUE); detect_doors(); detect_stairs() L:USE: if (get_check("Activate recall? ") ~= nil) then word_of_recall() end L:USE: object.timeout = rand_range(20, 40) L:DESC: return "dangerous clairvoyance and recall every 20-40 turns" L:TIMED: if randint0(999) == 0 and bAnd(player.flags[2], TR2_NO_MAGIC) == 0 L:TIMED: and not player.tim_invuln then L:TIMED: msgf("The Jewel of Judgement drains life from you!") L:TIMED: take_hit(player.lev, "The Jewel of Judgement") L:TIMED: end L:SPOIL: return "Damages player randomly" # The Amulet of Carlammas N:4:of Carlammas I:40:10:2 W:80:10:3:5000 F:CON | HIDE_TYPE | ACTIVATE | RES_FIRE | INSTA_ART L:USE: msgf("The amulet lets out a shrill wail...") L:USE: inc_protevil(randint1(25) + 3 * player.lev) L:USE: object.timeout = rand_range(225, 450) L:DESC: return "protection from evil every 225-450 turns" # The Amulet of Ingwe N:5:of Ingwe I:40:11:3 W:95:30:3:15000 F:WIS | CHR | INFRA | HIDE_TYPE | F:SEE_INVIS | FREE_ACT | ACTIVATE | F:RES_ACID | RES_COLD | RES_ELEC | F:INSTA_ART L:USE: msgf("The amulet floods the area with goodness...") L:USE: dispel_evil(player.lev * 5) L:USE: object.timeout = rand_range(300, 600) L:DESC: return "dispel evil (level * 5) every 300-600 turns" # The Necklace of the Dwarves N:6:of the Dwarves I:40:12:3 W:100:50:3:20000 F:STR | CON | INFRA | HIDE_TYPE | F:SEE_INVIS | FREE_ACT | REGEN | LITE | INSTA_ART # The Incandescent Globe of Sawall N:7:of Sawall I:39:3:3 W:35:10:5:4000 P:0:1d1:0:0:0 F:INSTA_ART | INFRA | SH_FIRE | SH_ELEC | LITE # The Ring of Barahir -> Frakir N:8:'Frakir' I:45:32:1 W:80:25:2:7500 F:STR | INT | WIS | DEX | CON | CHR | STEALTH | HIDE_TYPE | F:RES_POIS | ACTIVATE | SEE_INVIS | SENSE | INSTA_ART L:USE: msgf("You order Frakir to strangle your opponent.") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: if drain_life(dir, 200) then object.timeout = rand_range(100, 200) end L:DESC: return "a strangling attack (200) every 100-200 turns" # The Ring of Tulkas N:9:of Tulkas I:45:33:4 W:100:50:2:17500 F:STR | DEX | CON | HIDE_TYPE | ACTIVATE | SPEED | INSTA_ART L:USE: msgf("The ring glows brightly...") L:USE: inc_fast(rand_range(75, 150)) L:USE: object.timeout = rand_range(150, 300) L:DESC: return "haste self (75-150) every 150-300 turns" # The Ring of Power (Narya) N:10:of Power (Narya) I:45:34:1 W:100:30:2:100000 P:0:1d1:10:10:0 F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | F:ACTIVATE | FREE_ACT | SEE_INVIS | F:SLOW_DIGEST | REGEN | SUST_STR | SUST_DEX | IM_FIRE | F:INSTA_ART L:USE: msgf("The ring glows deep red...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_FIRE, dir, 250, 3) L:USE: object.timeout = rand_range(225, 450) L:DESC: return "fire ball (250, rad. 3) every 225-450 turns" # The Ring of Power (Nenya) N:11:of Power (Nenya) I:45:35:2 W:105:40:2:200000 P:0:1d1:11:11:0 F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | F:ACTIVATE | HOLD_LIFE | FREE_ACT | SEE_INVIS | F:FEATHER | REGEN | SHOW_MODS | F:SUST_INT | SUST_WIS | IM_COLD | F:INSTA_ART L:USE: msgf("The ring glows bright white...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_COLD, dir, 400, 3) L:USE: object.timeout = rand_range(325, 650) L:DESC: return "frost ball (400, rad. 3) every 325-650 turns" L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" # The Ring of Power (Vilya) N:12:of Power (Vilya) I:45:36:3 W:105:50:2:300000 P:0:1d1:12:12:0 F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | F:ACTIVATE | HOLD_LIFE | FREE_ACT | SEE_INVIS | F:FEATHER | SLOW_DIGEST | REGEN | F:SUST_INT | SUST_WIS | SUST_STR | SUST_DEX | IM_ELEC | F:INSTA_ART L:USE: msgf("The ring glows bright white...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_ELEC, dir, 500, 3) L:USE: object.timeout = rand_range(425, 850) L:DESC: return "lightning ball (500, rad. 3) every 425-850 turns" L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" # The Ring of Power (The One Ring) N:13:of Power (The One Ring) I:45:37:5 W:100:100:2:5000000 P:0:1d1:15:15:0 F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | F:ACTIVATE | CURSED | HEAVY_CURSE | PERMA_CURSE | F:AGGRAVATE | DRAIN_EXP | SEE_INVIS | REGEN | TY_CURSE | F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | F:SUST_STR | SUST_DEX | SUST_CON | F:SUST_INT | SUST_WIS | SUST_CHR | F:INSTA_ART L:USE: msgf("The ring glows intensely black...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: ring_of_power(dir) L:USE: object.timeout = rand_range(450, 900) L:DESC: return "bizarre things every 450-900 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability & high resist" N:14:of Elemental Mastery I:45:50:4 W:105:50:2:30000 F:STR | INT | DEX | SPEED | STEALTH | HIDE_TYPE | F:RES_FIRE | RES_COLD | RES_ACID | RES_ELEC | RES_POIS | F:TELEPATHY | F:INSTA_ART | ACTIVATE L:USE: msgf("The ring glows in multiple colours...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_MISSILE, dir, 800, 3) L:USE: object.timeout = rand_range(250, 500) L:DESC: return "ball of the elements (800, rad. 3) every 250-500 turns" # Artifact 15 is unused # The Multi-Hued Dragon Scale Mail 'Razorback' # Was (-2) INT | WIS | STEALTH | HIDE_TYPE, (-3) to hit, 30 pounds (?) N:16:'Razorback' I:38:6:0 W:90:9:500:40000 P:30:2d4:-4:0:25 F:RES_FIRE | RES_COLD | RES_POIS | RES_LITE | RES_DARK | F:LITE | SEE_INVIS | AGGRAVATE | FREE_ACT | IM_ELEC | F:ACTIVATE | INSTA_ART L:USE: msgf("Your armor is surrounded by lightning...") L:USE: scatter_ball(damroll(5, 3), GF_ELEC, 1000, 3) L:USE: object.timeout = 100 L:DESC: return "star ball (1000) every 100 turns"; # The Power Dragon Scale Mail 'Bladeturner' # Was (-3) DEX | HIDE_TYPE, (-4) to hit, 50 pounds (?) N:17:'Bladeturner' I:38:30:0 W:95:3:600:50000 P:50:2d4:-8:0:35 F:HOLD_LIFE | REGEN | F:RES_ACID | RES_FIRE | RES_COLD | RES_ELEC | RES_POIS | FEATHER | F:RES_NETHER | RES_NEXUS | RES_CHAOS | RES_LITE | RES_DARK | F:RES_SHARDS | RES_SOUND | RES_DISEN | RES_BLIND | RES_CONF | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | F:ACTIVATE | INSTA_ART L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe the elements.") L:USE: fire_ball(GF_MISSILE, dir, 1000, 4) L:USE: msgf("Your armor glows many colours...") L:USE: clear_afraid() L:USE: inc_shero(rand_range(50, 100)) L:USE: hp_player(30) L:USE: inc_blessed(rand_range(50, 100)) L:USE: inc_oppose_acid(rand_range(50, 100)) L:USE: inc_oppose_elec(rand_range(50, 100)) L:USE: inc_oppose_fire(rand_range(50, 100)) L:USE: inc_oppose_cold(rand_range(50, 100)) L:USE: inc_oppose_pois(rand_range(50, 100)) L:USE: object.timeout = 100 L:DESC: return "berserk, bless, resistance, and breathe elements (1000) every 100 turns" # The Robe of the Thaumaturgist N:18:of the Thaumaturgist I:36:2:4 W:18:11:20:10000 P:2:0d0:0:0:3 F:INT | STEALTH | FREE_ACT | RES_CONF | RES_BLIND | RES_NEXUS | HIDE_TYPE F:INSTA_ART # The Adamantite Plate Mail 'Soulkeeper' N:19:'Soulkeeper' I:37:30:2 W:100:9:420:30000 P:40:2d4:-4:0:20 F:CON | HOLD_LIFE | SUST_CON | F:RES_ACID | RES_COLD | RES_DARK | RES_NETHER | RES_NEXUS | RES_CHAOS | F:ACTIVATE | INSTA_ART L:USE: msgf("Your armor glows a bright white...") L:USE: msgf("You feel much better...") L:USE: hp_player(1000) L:USE: clear_cut() L:USE: object.timeout = 888 L:DESC: return "heal (1000) every 888 turns" # The Full Plate Armour of Isildur N:20:of Isildur I:37:15:1 W:52:3:300:15000 P:25:2d4:0:0:25 F:CON | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:RES_SOUND | RES_CONF | RES_NEXUS | INSTA_ART # The Metal Brigandine Armour of the Rohirrim N:21:of the Rohirrim I:37:9:2 W:52:3:200:13000 P:19:1d4:0:0:15 F:STR | DEX | HIDE_TYPE | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_CONF | RES_SOUND F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Mithril Chain Mail of Lohengrin -> the Crusader N:22:of the Crusader I:37:20:4 W:70:3:150:135000 P:28:1d4:-1:0:20 F:LUCK_10 | WIS | INT | SEE_INVIS | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS | F:HOLD_LIFE | RES_NETHER | RES_DARK | RES_FEAR | F:ACTIVATE | SLAY_EVIL | INSTA_ART L:USE: msgf("A heavenly choir sings...") L:USE: clear_poisoned() L:USE: clear_cut() L:USE: clear_stun() L:USE: clear_confused() L:USE: clear_blind() L:USE: inc_hero(rand_range(25, 50)) L:USE: hp_player(777) L:USE: object.timeout = 300 L:DESC: return "curing, heroism and heal (777) every 300 turns" D:This armor is said to have been forged by an angel and D:imbued with celestial power. # The Mithril Plate Mail of Celeborn -> Julian N:23:of Julian I:37:3:4 W:70:3:250:15000 P:15:2d4:-2:0:40 F:STR | CHR | HIDE_TYPE | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_DARK | F:RES_DISEN | ACTIVATE | INSTA_ART L:USE: msgf("Your armor glows deep blue...") L:USE: genocide(TRUE) L:USE: object.timeout = 500 L:DESC: return "genocide every 500 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Chain Mail of Arvedui -> Command N:24:of Command I:37:4:2 W:35:3:220:9000 P:14:1d4:-2:0:15 F:STR | CHR | HIDE_TYPE | INSTA_ART F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD L:USE: msgf("Your armor shines with a brilliant white light.") L:USE: charm_monsters(player.lev * 2) L:USE: object.timeout = 600 L:DESC: return "mass charm every 600 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "2 random high resists" D:This finely wrought suit of mail seems worthy of a great king. D:Every detail is perfect, and the whole radiates an aura of D:total confidence and power. # The Augmented Chain Mail of Caspanion N:25:of Caspanion I:37:6:3 W:43:9:270:14000 P:16:1d4:-2:0:20 F:INT | WIS | CON | HIDE_TYPE | INSTA_ART F:RES_ACID | RES_POIS | RES_CONF | ACTIVATE L:USE: msgf("Your armor glows bright red...") L:USE: destroy_doors_touch() L:USE: object.timeout = 10 L:DESC: return "trap and door destruction every 10 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Metal Scale Mail 'Nemovebla' N:26:'Nemovebla' I:37:3:4 W:25:8:225:14000 P:13:1d4:0:0:10 F:STR | CON | RES_NEXUS | RES_NETHER | NO_TELE | SLOW_DIGEST | HIDE_TYPE F:INSTA_ART # The Hagaromo 'Tenyo's Despair' N:27:'Tenyo's Despair' I:36:16:4 W:45:3:20:13000 P:2:0d0:0:0:20 F:STEALTH | FEATHER | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_DARK | F:ACTIVATE | SUST_STR | SUST_INT | SUST_WIS | SUST_DEX | SUST_CON | SUST_CHR F:INSTA_ART L:USE: msgf("Your robe shines brightly.") L:USE: cure_potion(500, TRUE, TRUE, TRUE) L:USE: local d = randint1(100) L:USE: if d <= 50 then L:USE: inc_blessed(rand_range(25, 50)) L:USE: elseif d <= 75 then L:USE: turn_monsters(player.lev * 4) L:USE: else L:USE: summon_controlled(SUMMON_ANGEL) L:USE: end L:USE: object.timeout = 1000 L:DESC: return "heavenly blessing and heal (500) every 1000 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" D:According to legend, a celestial maiden was bathing in a stream when D:a man came and took her hagaromo. Unable to return to heaven without D:it, she had no choice but to marry the man and hope to eventually D:find her robe. # The Leather Scale Mail 'Thalkettoth' N:28:'Thalkettoth' I:36:11:3 W:35:3:60:9000 P:11:1d1:-1:0:25 F:DEX | HIDE_TYPE | RES_ACID | RES_SHARDS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Leather Jacket of Melman N:29:of Melman I:36:12:2 W:52:30:120:10000 P:12:1d2:0:0:20 F:HOLD_LIFE | INT | WIS | RES_FIRE | RES_COLD | INSTA_ART # The Small Metal Shield of Thorin N:30:of Thorin I:34:3:4 W:52:6:65:24000 P:5:1d3:0:0:25 F:STR | CON | HIDE_TYPE | FREE_ACT | IM_ACID | RES_SOUND | RES_CHAOS F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Large Leather Shield of Celegorm N:31:of Celegorm I:34:4:0 W:52:3:60:8000 P:6:1d4:0:0:20 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_LITE | RES_DARK F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Large Metal Shield of Anarion N:32:of Anarion I:34:5:0 W:70:9:120:7000 P:8:1d5:0:0:20 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | SUST_STR | SUST_INT | F:SUST_WIS | SUST_DEX | SUST_CON | SUST_CHR | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Hard Leather Cap of Sawall N:33:of Sawall I:32:2:3 W:52:20:15:10000 P:2:0d0:0:0:18 F:INT | WIS | CHR | SUST_INT | SUST_WIS | SUST_CHR | RES_BLIND F:INSTA_ART # The Massive Iron Crown of Morgoth N:34:of Chaos I:33:50:125 W:100:1:20:10000000 P:0:1d1:0:0:0 F:STR | INT | WIS | DEX | CON | CHR | INFRA | HIDE_TYPE | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS | F:RES_LITE | RES_DARK | RES_CONF | RES_NEXUS | F:LITE | SEE_INVIS | TELEPATHY | F:CURSED | HEAVY_CURSE | PERMA_CURSE | NO_TELE | INSTA_ART | QUESTITEM # The Iron Crown of Beruthiel N:35:of Beruthiel I:33:10:-25 W:70:12:20:0 P:0:1d1:0:0:25 F:STR | DEX | CON | HIDE_TYPE | F:FREE_ACT | SEE_INVIS | TELEPATHY | CURSED F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" # The Hard Leather Cap of Thranduil N:36:of Thranduil I:32:2:2 W:35:2:15:8500 P:2:0d0:0:0:10 F:INT | WIS | HIDE_TYPE | RES_BLIND | TELEPATHY F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Metal Cap of Thengel N:37:of Thengel I:32:3:3 W:17:2:20:4000 P:3:1d1:0:0:12 F:WIS | CHR | HIDE_TYPE | INSTA_ART # The Steel Helm of Hammerhand N:38:of Hammerhand I:32:6:3 W:35:2:60:10000 P:6:1d3:0:0:20 F:STR | DEX | CON | HIDE_TYPE | RES_ACID | RES_NEXUS F:INSTA_ART # The _DRAGON_ Helm of Dor-Lomin N:39:of Dor-Lomin I:32:7:4 W:70:12:75:20000 P:8:1d3:0:0:20 F:STR | DEX | CON | HIDE_TYPE | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_LITE | RES_BLIND | F:LITE | SEE_INVIS | TELEPATHY | ACTIVATE | INSTA_ART L:USE: turn_monsters(40 + player.lev) L:USE: object.timeout = 3 * (player.lev + 10) L:DESC: return "turn monsters every 33-180 turns" # The Iron Helm 'Holhenneth' N:40:'Holhenneth' I:32:5:2 W:35:5:75:7500 P:5:1d3:0:0:10 F:INT | WIS | SENSE | HIDE_TYPE | RES_BLIND | SEE_INVIS | ACTIVATE F:INSTA_ART L:USE: msgf("Your helm glows bright white...") L:USE: msgf("An image forms in your mind...") L:USE: detect_all() L:USE: object.timeout = rand_range(55, 110) L:DESC: return "detection every 55-110 turns" # The Iron Helm of Gorlim -> Terror Mask (for warriors only...) # Variable bonus: +1 str per +5 ac N:41:'Terror Mask' I:32:5:-1 W:35:5:75:0 P:5:1d3:25:25:10 F:INT | WIS | SENSE | HIDE_TYPE | SHOW_MODS | F:SEE_INVIS | NO_MAGIC | IM_COLD | ACTIVATE | F:RES_DISEN | RES_FEAR | FREE_ACT | RES_ACID | RES_COLD | RES_POIS | F:TELEPORT | INSTA_ART | EASY_ENCHANT L:USE: turn_monsters(40 + player.lev) L:USE: object.timeout = 3 * (player.lev + 10) L:DESC: return "turn monsters every 33-180 turns" L:MAKE: if player.rp.pclass == CLASS_WARRIOR then L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: else L:MAKE: set_obj_flag(object, 2, TR2_CURSED) L:MAKE: set_obj_flag(object, 2, TR2_HEAVY_CURSE) L:MAKE: set_obj_flag(object, 2, TR2_AGGRAVATE) L:MAKE: set_obj_flag(object, 2, TR2_TY_CURSE) L:MAKE: end L:BONUS: b.stat[A_STR] = b.stat[STAT_STR] + object.to_a / 5 L:SPOIL: return "Random ability & high resist (warrior); curse, heavy curse, aggravate, ty curse (non-warrior); +1 str / +5 ac" # The Golden Crown of Gondor -> Amber N:42:of Amber I:33:11:3 W:70:40:30:25000 P:0:1d1:0:0:15 F:STR | WIS | CON | HIDE_TYPE | SPEED | F:RES_COLD | RES_FIRE | RES_LITE | RES_BLIND | RES_ELEC | RES_CONF | F:RES_CHAOS | LITE | SEE_INVIS | REGEN | ACTIVATE | INSTA_ART L:USE: msgf("Your crown glows deep blue...") L:USE: msgf("You feel a warm tingling inside...") L:USE: hp_player(700) L:USE: clear_cut() L:USE: object.timeout = 250 L:DESC: return "heal (700) every 250 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability & high resist" # The Filthy Rag of Keri the Humble N:43:of Keri the Humble I:36:1:1 W:9:6:20:4500 P:1:0d0:0:0:20 F:STR | CON | CHR | HIDE_TYPE | F:SUST_STR | SLOW_DIGEST | ACTIVATE | INSTA_ART L:USE: msgf("Your rag feels warm for a moment...") L:USE: create_food() L:USE: object.timeout = 100 L:DESC: return "create food every 100 turns" # The Cloak 'Colluin' N:44:'Colluin' I:35:1:0 W:9:45:10:7000 P:1:0d0:0:0:15 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS | ACTIVATE F:INSTA_ART L:USE: msgf("Your cloak glows many colours...") L:USE: inc_oppose_acid(rand_range(20, 40)) L:USE: inc_oppose_elec(rand_range(20, 40)) L:USE: inc_oppose_fire(rand_range(20, 40)) L:USE: inc_oppose_cold(rand_range(20, 40)) L:USE: inc_oppose_pois(rand_range(20, 40)) L:USE: object.timeout = 111 L:DESC: return "resistance (20-40) every 111 turns" # The Cloak 'Holcolleth' -> 'Thief's Shadow' # Variable bonus: +2 stealth, +1 per +2 ac N:45:'Thief's Shadow' I:35:1:2 W:9:45:10:5000 P:1:0d0:0:0:4 F:DEX | STEALTH | HIDE_TYPE | RES_LITE | ACTIVATE | INSTA_ART | EASY_ENCHANT L:USE: msgf("Your cloak glows deep blue...") L:USE: sleep_monsters_touch() L:USE: object.timeout = 55 L:DESC: return "sleep nearby monsters every 55 turns" L:BONUS: b.skills[SKILL_STL] = b.skills[SKILL_STL] + object.to_a / 2 L:SPOIL: return "+1 stealth / +2 ac" D:This fine cloak of dark cloth allows its wearer to blend D:perfectly with the shadows. # The Cloak of Thingol -> the Alchemist N:46:of the Alchemist I:35:1:3 W:17:90:10:11000 P:1:0d0:0:0:18 F:DEX | LUCK_10 | HIDE_TYPE | F:FREE_ACT | RES_ACID | RES_FIRE | RES_COLD | ACTIVATE | INSTA_ART L:USE: msgf("Your cloak glows bright yellow...") L:USE: recharge(130) L:USE: object.timeout = 70 L:DESC: return "recharge item every 70 turns" L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" D:This cloak was created by the gnome Samagar the Scorched to D:protect him from his frequent magical mishaps. # The Cloak of Thorongil # Variable bonus: +1 sensing per 10 levels N:47:of Thorongil I:35:1:0 W:9:20:10:4000 P:1:0d0:0:0:10 F:FREE_ACT | RES_ACID | SEE_INVIS | INSTA_ART L:BONUS: b.skills[SKILL_SNS] = b.skills[SKILL_SNS] + player.lev / 2 L:BONUS: b.skills[SKILL_FOS] = b.skills[SKILL_FOS] + player.lev / 2 L:SPOIL: return "+1 sense / 10 levels" # The Cloak 'Colannon' N:48:'Colannon' I:35:1:3 W:5:10:10:3000 P:1:0d0:0:0:15 F:STEALTH | RES_ACID | ACTIVATE | INSTA_ART L:USE: msgf("Your cloak twists space around you...") L:USE: teleport_player(100) L:USE: object.timeout = 45 L:DESC: return "teleport every 45 turns" # The Shadow Cloak of Luthien N:49:of Luthien I:35:6:2 W:70:40:5:15000 P:6:0d0:0:0:20 F:INT | WIS | CHR | HIDE_TYPE | SPEED | STEALTH | F:RES_ACID | RES_FIRE | RES_COLD | ACTIVATE | F:RES_DARK | RES_LITE | INSTA_ART L:USE: msgf("Your cloak glows a deep red...") L:USE: restore_level() L:USE: object.timeout = 450 L:DESC: return "restore life levels every 450 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Shadow Cloak of Tuor # Variable bonus: +3 dex, +1 per 10 levels; rogues only N:50:of Tuor I:35:6:4 W:70:40:5:35000 P:6:0d0:0:0:12 F:STEALTH | FREE_ACT | IM_ACID | SEE_INVIS | INSTA_ART L:BONUS: if player.rp.pclass == CLASS_ROGUE then L:BONUS: b.stat[A_DEX] = b.stat[STAT_DEX] + 3 + player.lev / 10 L:BONUS: end L:SPOIL: return "+3 DEX +1 / 10 levels (rogues)" D:This cloak, woven from strands of solid shadow, is particularly dangerous D:when worn by a skilled assassin. # Artifact 51 is unused # The Set of Leather Gloves 'Cambeleg' -> of the Ogre # Variable bonus: +1 str and +1 con per +2 ac N:52:of the Ogre I:31:1:3 W:17:6:5:7000 P:1:0d0:6:6:6 F:STR | CON | HIDE_TYPE | SHOW_MODS | INSTA_ART | EASY_ENCHANT L:ALTER: object.pval = object.to_a / 2 L:SPOIL: return "Pval = +1 / +2 ac" D:Legend says that these gloves provide their wearer with the D:strength and skill of an ogre. # The Set of Leather Gloves 'Cammithrim' N:53:'Cammithrim' I:31:1:0 W:17:3:5:5000 P:1:0d0:0:0:10 F:FREE_ACT | RES_LITE | SUST_CON | LITE | ACTIVATE | INSTA_ART L:USE: msgf("Your gloves glow extremely brightly...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_MISSILE, dir, damroll(3, 6)) L:USE: object.timeout = 2 L:DESC: return "magic missile (3d6) every 2 turns" # The Set of Gauntlets 'Paurhach' N:54:'Paurhach' I:31:2:0 W:17:20:25:3500 P:2:1d1:0:0:15 F:RES_FIRE | ACTIVATE | INSTA_ART L:USE: msgf("Your gauntlets are covered in fire...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_FIRE, dir, damroll(11, 8)) L:USE: object.timeout = rand_range(8, 16) L:DESC: return "fire bolt (11d8) every 8-16 turns" # The Set of Gauntlets 'Paurnimmen' -> of Corwin N:55:of Corwin I:31:2:4 W:17:20:25:6000 P:2:1d1:0:0:15 F:SUST_CON | CON | REGEN | RES_COLD | ACTIVATE | INSTA_ART L:USE: msgf("Your gauntlets are covered in frost...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_COLD, dir, damroll(8, 8)) L:USE: object.timeout = rand_range(7, 14) L:DESC: return "frost bolt (8d8) every 7-14 turns" # The Set of Gauntlets 'Pauraegen' N:56:'Pauraegen' I:31:2:0 W:17:20:25:3500 P:2:1d1:0:0:15 F:RES_ELEC | ACTIVATE | INSTA_ART L:USE: msgf("Your gauntlets are covered in sparks...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_ELEC, dir, damroll(6, 8)) L:USE: object.timeout = rand_range(6, 12) L:DESC: return "lightning bolt (6d8) every 6-12 turns" # The Set of Gauntlets 'Paurnen' N:57:'Paurnen' I:31:2:0 W:17:20:25:3500 P:2:1d1:0:0:15 F:RES_ACID | ACTIVATE | INSTA_ART L:USE: msgf("Your gauntlets are covered in acid...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_ACID, dir, damroll(8, 8)) L:USE: object.timeout = rand_range(5, 10) L:DESC: return "acid bolt (8d8) every 5-10 turns" # The Set of Gauntlets 'Camlost' -> of Thanos N:58:of Thanos I:31:2:2 W:17:20:25:0 P:2:1d1:-11:-12:0 F:STR | DEX | HIDE_TYPE | RES_DISEN | RES_NEXUS | IM_FIRE | F:HOLD_LIFE | RES_NETHER | RES_CONF | RES_CHAOS | RES_POIS | IM_COLD | F:AGGRAVATE | CURSED | SHOW_MODS | HEAVY_CURSE | TY_CURSE | TELEPORT F:INSTA_ART # The Set of Cesti of Fingolfin N:59:of Fingolfin I:31:5:4 W:70:15:40:13000 P:5:1d1:10:10:20 F:DEX | HIDE_TYPE | F:FREE_ACT | RES_ACID | ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your cesti grows magical spikes...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_ARROW, dir, 250) L:USE: object.timeout = rand_range(90, 180) L:DESC: return "a magical arrow (250) every 90-180 turns" L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" # The Pair of Hard Leather Boots of Feanor N:60:of Feanor I:30:3:15 W:70:120:40:300000 P:3:1d1:0:0:20 F:SPEED | F:RES_NEXUS | ACTIVATE | INSTA_ART L:USE: msgf("Your boots glow bright green...") L:USE: inc_fast(rand_range(20, 40)) L:USE: object.timeout = 200 L:DESC: return "haste self (20-40) every 200 turns" # The Pair of Soft Leather Boots 'Dal-i-thalion' -> of Flora N:61:of Flora I:30:2:5 W:17:25:20:14000 P:2:1d1:0:0:15 F:DEX | HIDE_TYPE | CHR | SUST_CHR | ACTIVATE | FREE_ACT | F:RES_NETHER | RES_CHAOS | SUST_CON | INSTA_ART L:USE: msgf("Your boots glow deep blue...") L:USE: clear_afraid() L:USE: clear_poisoned() L:USE: object.timeout = 5 L:DESC: return "remove fear and cure poison every 5 turns" # The Pair of Metal Shod Boots of Thror N:62:of Thror I:30:6:3 W:52:25:80:15000 P:6:1d1:0:0:20 F:STR | CON | HIDE_TYPE | SPEED | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Pair of Soft Leather Boots of Bleys # Variable bonus: +1 speed per 5 levels (rounded up) N:63:of Bleys I:30:2:4 W:52:20:20:25000 P:4:1d1:4:4:16 F:DEX | RES_NEXUS | SUST_DEX | FREE_ACT | INSTA_ART L:BONUS: b.pspeed = (player.lev + 4) / 5 L:SPOIL: return "+1 speed / 5 levels" D:These ordinary looking boots are said to grow with their wearer. # The Main Gauche of Maedhros N:64:of Maedhros I:23:5:3 W:26:30:30:17500 P:0:2d5:12:15:0 F:INT | DEX | HIDE_TYPE | SPEED | F:SLAY_TROLL | SLAY_GIANT | FREE_ACT | SEE_INVIS | SHOW_MODS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Dagger 'Angrist' -> of Caine N:65:of Caine I:23:4:4 W:35:80:12:15000 P:0:2d4:10:15:5 F:DEX | HIDE_TYPE | STEALTH | SENSE | F:SLAY_EVIL | SLAY_TROLL | SLAY_ORC | BRAND_POIS | F:FREE_ACT | RES_DARK | SUST_DEX | SEE_INVIS | SHOW_MODS | THROW F:INSTA_ART # The Dagger 'Narthanc' # Variable bonus: +1 str per 5 levels (rounded up) # Unique power: Damage increases 10% per level (applied after all other mods) N:66:'Narthanc' I:23:4:0 W:7:10:12:6000 P:0:1d6:4:6:0 F:BRAND_FIRE | RES_FIRE | ACTIVATE | SHOW_MODS | LITE | F:THROW | RETURN | INSTA_ART L:USE: msgf("Your dagger is covered in fire...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_FIRE, dir, damroll(11, 8)) L:USE: object.timeout = rand_range(8, 16) L:DESC: return "fire bolt(11d8) every 8-16 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:BONUS: b.stat[A_STR] = (player.lev + 4) / 5 L:HIT: dam = dam + (dam * player.lev) / 10 L:SPOIL: return "Random high resist; +1 STR / 5 levels; +10% dam / level" # The Dagger 'Nimthanc' # Variable bonus: +1 con per 5 levels (rounded up) # Unique power: Damage increases 10% per level (applied after all other mods) N:67:'Nimthanc' I:23:4:0 W:5:10:12:5000 P:0:1d6:4:6:0 F:BRAND_COLD | RES_COLD | ACTIVATE | SHOW_MODS | THROW | RETURN | INSTA_ART L:USE: msgf("Your dagger is covered in frost...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_COLD, dir, damroll(8, 8)) L:USE: object.timeout = rand_range(7, 14) L:DESC: return "frost bolt (8d8) every 7-14 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:BONUS: b.stat[A_CON] = (player.lev + 4) / 5 L:HIT: dam = dam + (dam * player.lev) / 10 L:SPOIL: return "Random high resist; +1 CON / 5 levels; +10% dam / level" # The Dagger 'Dethanc' # Variable bonus: +1 dex per 5 levels (rounded up) # Unique power: Damage increases 10% per level (applied after all other mods) N:68:'Dethanc' I:23:4:0 W:9:10:12:5000 P:0:1d6:4:6:0 F:BRAND_ELEC | RES_ELEC | ACTIVATE | SHOW_MODS | THROW | RETURN | INSTA_ART L:USE: msgf("Your dagger is covered in sparks...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_bolt(GF_ELEC, dir, damroll(6, 8)) L:USE: object.timeout = rand_range(6, 12) L:DESC: return "lightning bolt (6d8) every 6-12 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:BONUS: b.stat[A_DEX] = (player.lev + 4) / 5 L:HIT: dam = dam + (dam * player.lev) / 10 L:SPOIL: return "Random high resist; +1 DEX / 5 levels; +10% dam / level" # The Dagger of Rilia # Variable bonus: +1 int per 10 levels (rounded up) N:69:of Rilia I:23:4:0 W:9:40:12:10000 P:0:2d4:4:3:0 F:SLAY_ORC | RES_POIS | RES_DISEN | ACTIVATE | SHOW_MODS | BRAND_POIS | F:THROW | RETURN | INSTA_ART L:USE: msgf("Your dagger throbs deep green...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_POIS, dir, 25, 3) L:USE: object.timeout = rand_range(4, 8) L:DESC: return "stinking cloud (25) every 4-8 turns" L:BONUS: b.stat[A_INT] = (player.lev + 9) / 10 L:SPOIL: return "+1 INT / 10 levels" # The Dagger 'Belangil' -> of Fiona N:70:of Fiona I:23:4:2 W:17:40:12:25000 P:0:2d4:6:9:0 F:DEX | HIDE_TYPE | SPEED | BLOWS | BRAND_COLD | RES_COLD | F:SEE_INVIS | SLOW_DIGEST | REGEN | F:ACTIVATE | SHOW_MODS | BRAND_POIS | THROW | INSTA_ART L:USE: msgf("Your dagger is covered in frost...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_COLD, dir, 100, 2) L:USE: object.timeout = rand_range(5, 10) L:DESC: return "frost ball (100) every 5-10 turns" # The Bastard Sword 'Calris' N:71:'Calris' I:23:21:5 W:52:15:140:25000 P:0:4d4:-20:20:0 F:CON | HIDE_TYPE | F:KILL_DRAGON | SLAY_EVIL | SLAY_DEMON | SLAY_TROLL | RES_DISEN | F:AGGRAVATE | CURSED | HEAVY_CURSE | SHOW_MODS | INSTA_ART # The Broad Sword 'Arunruth' -> 'Grayswandir' # Unique power: Damage increases +1 per 200' of depth N:72:'Grayswandir' I:23:11:4 W:35:45:50:50000 P:0:2d7:20:12:0 F:STR | DEX | CON | SUST_CON | SUST_STR | F:REGEN | FREE_ACT | SEE_INVIS | F:RES_CHAOS | RES_NETHER | HOLD_LIFE | RES_FEAR | F:RES_COLD | BRAND_COLD | F:SLAY_DEMON | SLAY_EVIL | SLAY_DRAGON | SLAY_UNDEAD | F:SLOW_DIGEST | SHOW_MODS | HIDE_TYPE | BLESSED | INSTA_ART L:HIT: dam = dam + player.depth / 4 L:SPOIL: return "+1 dam / 200'" # The Broad Sword 'Glamdring' N:73:'Glamdring' I:23:16:1 W:35:20:150:10000 P:0:2d5:10:15:0 F:SENSE | F:SLAY_EVIL | BRAND_FIRE | SLAY_ORC | RES_FIRE | RES_LITE | LITE | F:SLOW_DIGEST | SHOW_MODS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Broad Sword 'Aeglin' -> 'Nothung' N:74:'Nothung' I:23:16:4 W:35:90:150:15000 P:0:2d5:12:16:0 F:SENSE | BRAND_ELEC | SLAY_ORC | KILL_DRAGON | RES_FEAR | F:RES_ELEC | LITE | RES_FIRE | RES_POIS | F:SLOW_DIGEST | SHOW_MODS | INSTA_ART # The Broad Sword 'Orcrist' N:75:'Orcrist' I:23:16:3 W:35:20:150:8000 P:0:2d5:10:15:0 F:STEALTH | SLAY_EVIL | BRAND_COLD | SLAY_ORC | RES_COLD | LITE | F:SLOW_DIGEST | SHOW_MODS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Two-Handed Sword 'Gurthang' N:76:'Gurthang' I:23:25:2 W:52:30:200:12500 P:0:3d6:13:17:0 F:STR | HIDE_TYPE | F:KILL_DRAGON | SLAY_TROLL | FREE_ACT | SLOW_DIGEST | REGEN | SHOW_MODS F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Two-Handed Sword 'Zarcuthra' N:77:'Zarcuthra' I:23:25:4 W:52:180:250:30000 P:0:4d6:19:21:0 F:STR | CHR | INFRA | HIDE_TYPE | VORPAL | F:KILL_DRAGON | SLAY_ANIMAL | SLAY_EVIL | BRAND_FIRE | LITE | F:SLAY_UNDEAD | SLAY_DEMON | SLAY_TROLL | SLAY_GIANT | SLAY_ORC | F:RES_FIRE | RES_CONF | RES_CHAOS | FREE_ACT | SEE_INVIS | AGGRAVATE | F:SHOW_MODS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Two-Handed Sword 'Mormegil' -> Twilight N:78:'Twilight' I:23:25:10 W:52:15:250:0 P:0:4d6:-40:-60:-50 F:SPEED | IM_FIRE | RES_FIRE | BRAND_FIRE | RES_DISEN | RES_FEAR | F:AGGRAVATE | CURSED | HEAVY_CURSE | SHOW_MODS | TY_CURSE | LITE F:INSTA_ART # The Cutlass 'Gondricam' N:79:'Gondricam' I:23:12:3 W:35:8:110:10000 P:0:1d10:10:11:0 F:DEX | STEALTH | HIDE_TYPE | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | FEATHER | F:SEE_INVIS | REGEN | SHOW_MODS | INSTA_ART # The Executioner's Sword 'Crisdurian' N:80:'Crisdurian' I:23:28:0 W:70:15:260:20000 P:0:4d5:18:19:0 F:SLAY_DRAGON | SLAY_EVIL | SLAY_UNDEAD | SLAY_TROLL | SLAY_GIANT | F:SLAY_ORC | SEE_INVIS | SHOW_MODS | VORPAL | BRAND_POIS F:INSTA_ART # The Katana 'Aglarang' -> of Groo # Unique power: Double damage if wielder is a warrior N:81:of Groo I:23:20:2 W:52:25:50:20000 P:0:5d4:0:0:0 F:DEX | HIDE_TYPE | VORPAL | F:SUST_DEX | SHOW_MODS | SPEED | INSTA_ART L:HIT: if player.rp.pclass == CLASS_WARRIOR then L:HIT: damage = damage * 2 L:HIT: end L:SPOIL: return "Double damage (warrior)" # The Long Sword 'Ringil' N:82:'Ringil' I:23:17:10 W:35:120:130:150000 P:0:4d5:22:25:0 F:SPEED | REGEN | ACTIVATE | SHOW_MODS | F:SLAY_EVIL | BRAND_COLD | SLAY_UNDEAD | SLAY_DEMON | SLAY_TROLL | F:FREE_ACT | RES_COLD | RES_LITE | LITE | SEE_INVIS | SLOW_DIGEST | F:INSTA_ART L:USE: msgf("Your sword glows an intense blue...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_COLD, dir, 200, 2) L:USE: object.timeout = 300 L:DESC: return "frost ball (200) every 300 turns" # The Long Sword 'Anduril' N:83:'Anduril' I:23:17:4 W:35:40:130:12000 P:0:2d5:10:15:5 F:STR | HIDE_TYPE | F:SLAY_EVIL | BRAND_FIRE | SLAY_TROLL | SLAY_ORC | FREE_ACT | F:RES_FIRE | SUST_DEX | SEE_INVIS | ACTIVATE | SHOW_MODS | LITE F:INSTA_ART L:USE: msgf("Your sword glows an intense red...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_FIRE, dir, 150, 2) L:USE: object.timeout = 400 L:DESC: return "fire ball (150) every 400 turns" L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Long Sword 'Anguirel' -> Werewindle N:84:'Werewindle' I:23:17:2 W:35:30:130:25000 P:0:3d5:8:12:0 F:STR | CON | HIDE_TYPE | CHAOTIC | REGEN | SLOW_DIGEST | F:SLAY_EVIL | BRAND_ELEC | SLAY_DEMON | FREE_ACT | RES_ELEC | F:RES_LITE | LITE | SEE_INVIS | SHOW_MODS | ACTIVATE | SPEED | F:SUST_CON | SUST_STR | SUST_INT | SUST_WIS | RES_ELEC | RES_DARK | F:RES_DISEN | RES_FEAR | SPEED | INSTA_ART L:USE: werewindle() L:USE: object.timeout = 35 L:DESC: return "a getaway every 35 turns" # The Long Sword 'Elvagil'-> 'Chainsword' N:85:'Chainsword' I:23:16:2 W:52:15:130:6666 P:0:9d5:-30:7:0 F:DEX | CHR | HIDE_TYPE | SPEED | AGGRAVATE | F:SLAY_TROLL | SLAY_ORC | SEE_INVIS | SHOW_MODS | INSTA_ART L:TIMED: if randint0(100) == 0 then L:TIMED: local line = get_rnd_line("chainswd.txt") L:TIMED: if line then msgf(line) end L:TIMED: end # The Rapier 'Forasgil' N:86:'Forasgil' I:23:7:0 W:26:8:40:7500 P:0:1d12:12:19:0 F:SLAY_ANIMAL | BRAND_COLD | RES_COLD | RES_LITE | LITE | SHOW_MODS F:INSTA_ART # The Sabre 'Careth Asdriag' N:87:'Careth Asdriag' I:23:11:1 W:26:8:50:10000 P:0:1d14:6:8:0 F:BLOWS | SLAY_DRAGON | SLAY_ANIMAL | SLAY_TROLL | SLAY_GIANT | F:SLAY_ORC | SHOW_MODS | INSTA_ART # The Small Sword 'Sting' # Dependent power: pval and to-hit doubled for hobbits. N:88:'Sting' I:23:8:2 W:35:15:75:30000 P:0:2d6:7:8:0 F:STR | DEX | CON | SLAY_EVIL | SLAY_UNDEAD | SLAY_ORC | F:BLOWS | FREE_ACT | RES_LITE | LITE | SEE_INVIS | SHOW_MODS F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: if player.rp.prace == RACE_HOBBIT then L:MAKE: object.pval = 4 L:MAKE: object.to_h = 14 L:MAKE: end L:SPOIL: return "Random high resist; double pval and +skill (hobbit)" D:In a human's hands, no more than an enchanted large dagger, but a fearsome weapon D:in the hands of a hobbit. # The Scimitar 'Haradekket' -> Soulsword N:89:'Soulsword' I:23:18:2 W:35:8:130:20000 P:0:3d5:9:11:0 F:INT | WIS | SEE_INVIS | BLESSED | F:SLAY_ANIMAL | SLAY_EVIL | SLAY_UNDEAD | SLAY_DRAGON | SLAY_DEMON | F:RES_CHAOS | RES_DISEN | RES_NEXUS | RES_NETHER | HOLD_LIFE | F:SHOW_MODS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Short Sword Gilettar -> of Merlin # Dependent power: +2 sp for mages and high mages # Unique power: +1 dam per 10 maximum sp (applied after other bonuses). N:90:of Merlin I:23:10:2 W:35:8:80:25000 P:0:1d10:3:7:0 F:BLOWS | F:SLAY_ANIMAL | SLOW_DIGEST | REGEN | SHOW_MODS | SEE_INVIS | RES_DISEN F:INSTA_ART L:MAKE: if (player.rp.pclass == CLASS_MAGE) or (player.rp.pclass == CLASS_HIGH_MAGE) then L:MAKE: set_obj_flag(object, 0, TR0_SP) L:MAKE: end L:HIT: dam = dam + player.msp / 10 L:SPOIL: return "+2 sp (mage or high mage); +1 dam / 10 max sp" D:This fine sword is most powerful in the hands of a skilled magic user. # The Blade of Chaos 'Doomcaller' # Was (-5) CON | HIDE_TYPE, +0 armor N:91:'Doomcaller' I:23:30:0 W:100:25:180:25000 P:0:6d5:18:28:-50 F:KILL_DRAGON | SLAY_ANIMAL | SLAY_EVIL | BRAND_COLD | SLAY_TROLL | F:SLAY_ORC | FREE_ACT | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:RES_CONF | RES_CHAOS | SEE_INVIS | TELEPATHY | AGGRAVATE | SHOW_MODS | F:CHAOTIC | VORPAL | BRAND_FIRE | BRAND_POIS | LITE | INSTA_ART # The Long Sword 'Vorpal Blade' # Unique power: Increased vorpality N:92:'Vorpal Blade' I:23:17:2 W:80:30:150:35000 P:0:4d5:32:32:0 F:VORPAL | SLAY_EVIL | F:FREE_ACT | LITE | SEE_INVIS | SLOW_DIGEST | REGEN | SPEED | STR | DEX F:INSTA_ART L:HIT: vorpal_chance = 1 L:SPOIL: return "Extra vorpality" # The Beaked Axe of Theoden N:93:of Theoden I:22:10:3 W:35:15:180:9500 P:0:2d6:8:10:0 F:WIS | CON | HIDE_TYPE | F:SLAY_DRAGON | TELEPATHY | SLOW_DIGEST | ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your axe blade glows black...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: drain_life(dir, 200) L:USE: object.timeout = 400 L:DESC: return "drain life (200) every 400 turns" # The Glaive of Pain N:94:of Pain I:22:13:0 W:52:25:190:21000 P:0:7d6:0:30:0 F:SHOW_MODS | INSTA_ART # The Halberd 'Osondir' N:95:'Osondir' I:22:15:3 W:35:8:190:10000 P:0:3d5:6:9:0 F:CHR | HIDE_TYPE | F:BRAND_FIRE | SLAY_UNDEAD | SLAY_GIANT | RES_FIRE | RES_SOUND | F:FEATHER | SEE_INVIS | SHOW_MODS | LITE | INSTA_ART # The Pike 'Til-i-arc' N:96:'Til-i-arc' I:22:8:2 W:35:15:160:16000 P:0:2d5:10:12:10 F:INT | HIDE_TYPE | F:BRAND_COLD | BRAND_FIRE | SLAY_DEMON | SLAY_TROLL | SLAY_GIANT | F:RES_FIRE | RES_COLD | SUST_INT | SLOW_DIGEST | SHOW_MODS | LITE F:INSTA_ART # The Spear 'Aeglos' -> Runespear (of Odin) N:97:'Runespear' I:22:2:4 W:26:45:50:29000 P:0:1d20:15:25:5 F:INT | WIS | HIDE_TYPE | BRAND_FIRE | BRAND_ELEC | F:SLAY_TROLL | SLAY_ORC | SLAY_GIANT | F:FREE_ACT | RES_FIRE | RES_ELEC | RES_LITE | F:SLOW_DIGEST | ACTIVATE | BLESSED | SHOW_MODS | LITE | THROW | RETURN | F:INSTA_ART L:USE: msgf("Your spear crackles with electricity...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_ELEC, dir, 200, 3) L:USE: object.timeout = 500 L:DESC: return "lightning ball (200) every 500 turns" # The Spear of Orome -> Destiny N:98:of Destiny I:22:2:4 W:26:45:50:25000 P:0:1d10:15:15:0 F:INT | WIS | INFRA | HIDE_TYPE | SENSE | BRAND_FIRE | F:SLAY_GIANT | SLAY_EVIL | SLAY_DEMON | SLAY_UNDEAD | SLAY_DRAGON | F:RES_FIRE | RES_LITE | HOLD_LIFE | RES_FEAR | LITE | FEATHER | F:LITE | SEE_INVIS | ACTIVATE | BLESSED | SHOW_MODS | THROW F:INSTA_ART L:USE: msgf("Your spear pulsates...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: wall_to_mud(dir) L:USE: object.timeout = 5 L:DESC: return "stone to mud every 5 turns" # The Spear 'Nimloth' -> Hagen # Variable bonus: +1 speed per 5 levels (rounded up) N:99:of Hagen I:22:2:3 W:26:12:50:30000 P:0:1d10:11:13:0 F:STEALTH | RES_DARK | INFRA | F:BRAND_COLD | SLAY_UNDEAD | RES_COLD | SEE_INVIS | SHOW_MODS | THROW F:INSTA_ART L:BONUS: b.pspeed = (player.level + 4) / 5 L:SPOIL: return "+1 speed / 5 levels" # The Spear 'Soulsucker' N:100:'Soulsucker' I:22:2:4 W:35:20:50:3000 P:0:1d8:16:20:0 F:STEALTH | SENSE | VAMPIRIC | BRAND_POIS | RES_POIS | RES_DARK | F:RES_NETHER | HIDE_TYPE | SHOW_MODS | SEE_INVIS | DRAIN_EXP F:INSTA_ART # The Great Axe of Durin N:101:of Durin I:22:25:3 W:52:90:230:22000 P:0:4d4:10:20:15 F:CON | HIDE_TYPE | F:KILL_DRAGON | SLAY_DEMON | SLAY_TROLL | SLAY_ORC | FREE_ACT | F:RES_ACID | RES_FIRE | RES_LITE | RES_DARK | RES_CHAOS | SHOW_MODS F:INSTA_ART # The Great Axe of Eonwe N:102:of Eonwe I:22:25:2 W:52:120:230:33000 P:0:4d4:15:18:8 F:STR | INT | WIS | DEX | CON | CHR | HIDE_TYPE | F:SLAY_EVIL | BRAND_COLD | SLAY_UNDEAD | F:SLAY_ORC | FREE_ACT | IM_COLD | SEE_INVIS | ACTIVATE | F:BLESSED | SHOW_MODS | INSTA_ART L:USE: msgf("Your great axe lets out a long, shrill note...") L:USE: mass_genocide(TRUE) L:USE: object.timeout = 1000 L:DESC: return "mass genocide every 1000 turns" # The Battle Axe of Balli Stonehand N:103:of Balli Stonehand I:22:22:3 W:52:15:170:20000 P:0:3d8:8:11:5 F:STR | CON | STEALTH | HIDE_TYPE | F:SLAY_DEMON | SLAY_TROLL | SLAY_ORC | FREE_ACT | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_BLIND | FEATHER | F:SEE_INVIS | REGEN | SHOW_MODS | INSTA_ART # The Battle Axe 'Lotharang' N:104:'Lotharang' I:22:22:1 W:52:15:170:8000 P:0:2d8:4:3:0 F:STR | DEX | HIDE_TYPE | F:SLAY_TROLL | SLAY_ORC | ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your battle axe radiates deep purple...") L:USE: hp_player(100) L:USE: inc_cut(-50) L:USE: object.timeout = rand_range(3, 6) L:DESC: return "cure wounds (100) every 3-6 turns" # The Lochaber Axe 'Mundwine' -> of the Dwarves N:105:of the Dwarves I:22:28:10 W:52:8:250:16000 P:0:3d8:12:17:0 F:SLAY_EVIL | TUNNEL | INFRA | SENSE | SLAY_GIANT | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_FEAR | F:SHOW_MODS | INSTA_ART # The Broad Axe 'Barukkheled' N:106:'Barukkheled' I:22:11:3 W:35:8:160:10000 P:0:2d6:13:19:0 F:CON | HIDE_TYPE | F:SLAY_EVIL | SLAY_TROLL | SLAY_GIANT | SLAY_ORC | F:SEE_INVIS | SHOW_MODS | INSTA_ART # The Trident of Wrath N:107:of Wrath I:22:5:2 W:26:35:300:18000 P:0:1d24:16:18:0 F:STR | DEX | HIDE_TYPE | CHAOTIC | F:SLAY_EVIL | SLAY_UNDEAD | RES_LITE | RES_DARK | SEE_INVIS | F:BLESSED | SHOW_MODS | INSTA_ART # The Trident of Ulmo N:108:of Ulmo I:22:5:4 W:52:90:70:40000 P:0:1d32:15:19:0 F:DEX | HIDE_TYPE | F:SLAY_DRAGON | SLAY_ANIMAL | FREE_ACT | HOLD_LIFE | IM_ACID | F:RES_NETHER | SEE_INVIS | SLOW_DIGEST | REGEN | ACTIVATE | F:BLESSED | SHOW_MODS | INSTA_ART L:USE: msgf("Your trident glows deep red...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: teleport_monster(dir) L:USE: object.timeout = 150 L:DESC: return "teleport away every 150 turns" L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" # The Scythe 'Avavir' N:109:'Avavir' I:22:17:3 W:70:8:250:18000 P:0:5d3:8:8:10 F:DEX | CHR | HIDE_TYPE | F:BRAND_COLD | BRAND_FIRE | FREE_ACT | RES_FIRE | RES_COLD | F:RES_LITE | LITE | SEE_INVIS | ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your scythe glows soft white...") L:USE: word_of_recall() L:USE: object.timeout = 200 L:DESC: return "word of recall every 200 turns" # The Long Sword of the Dawn N:110:of the Dawn I:23:17:3 W:70:120:130:29000 P:0:3d5:20:20:0 F:ACTIVATE | LITE | BRAND_FIRE | FREE_ACT | RES_FIRE | INFRA | F:SLAY_EVIL | SLAY_DRAGON | SLAY_UNDEAD | SLAY_DEMON | VORPAL | F:CHR | SUST_CHR | RES_FEAR | RES_LITE | RES_BLIND | REGEN | SHOW_MODS F:INSTA_ART L:USE: msgf("You summon the Legion of the Dawn.") L:USE: summon_specific(-1, player.px, player.py, player.depth, SUMMON_DAWN, TRUE, TRUE, TRUE) L:USE: object.timeout = rand_range(500, 1000) L:DESC: return "summon the Legion of the Dawn every 500-1000 turns" L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Mighty Hammer 'Grond' # Was 30 pounds (?), -10 to speed # The weight (100 pounds) of Grond yields about -4 to speed # Note that it takes 18/200 strength to wield Grond correctly N:111:'Grond' I:21:50:0 W:100:1:1000:500000 P:0:9d9:5:25:10 F:KILL_DRAGON | SLAY_ANIMAL | SLAY_EVIL | IMPACT | SLAY_UNDEAD | NO_MAGIC | F:SLAY_DEMON | SLAY_TROLL | SLAY_ORC | RES_ACID | RES_ELEC | RES_FIRE | F:RES_COLD | SEE_INVIS | TELEPATHY | AGGRAVATE | SHOW_MODS | INSTA_ART F:QUESTITEM # The Flail 'Totila' N:112:'Totila' I:21:13:2 W:35:8:150:9000 P:0:3d6:6:8:0 F:STEALTH | F:SLAY_EVIL | BRAND_FIRE | RES_FIRE | RES_CONF | ACTIVATE | F:SHOW_MODS | LITE | INSTA_ART L:USE: msgf("Your flail glows in scintillating colours...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: confuse_monster(dir, 50) L:USE: object.timeout = 15 L:DESC: return "confuse monster every 15 turns" # The Two-Handed Flail 'Thunderfist' N:113:'Thunderfist' I:21:18:4 W:75:38:300:16000 P:0:3d6:5:18:0 F:STR | HIDE_TYPE | F:SLAY_ANIMAL | BRAND_FIRE | BRAND_ELEC | SLAY_TROLL | SLAY_ORC | F:RES_ELEC | RES_FIRE | RES_DARK | SHOW_MODS | LITE | INSTA_ART # The Ball-and-Chain 'Whirlwind' N:114:'Whirlwind' I:21:6:2 W:60:35:150:32000 P:0:2d4:13:18:0 F:DEX | SPEED | BLOWS | CHAOTIC | SLAY_EVIL | SUST_DEX | F:FREE_ACT | RES_FEAR | RES_NEXUS | RES_CHAOS | NO_TELE | F:SHOW_MODS | FEATHER | ACTIVATE | INSTA_ART | HIDE_TYPE | L:USE: msgf("Your ball and chain swings through the air...") L:USE: whirlwind_attack() L:USE: object.timeout = rand_range(50, 100) L:DESC: return "whirlwind attack every 50-100 turns" # The Morning Star 'Firestar' N:115:'Firestar' I:21:12:0 W:35:15:150:5500 P:0:2d6:5:7:2 F:BRAND_FIRE | RES_FIRE | ACTIVATE | SHOW_MODS | LITE | INSTA_ART L:USE: msgf("Your morning star rages in fire...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_FIRE, dir, 200, 3) L:USE: object.timeout = 100 L:DESC: return "fire ball (200, rad. 3) every 100 turns" # The Scythe of Energy # Unique power: does double damage when the player is hasted. N:116:of Energy I:22:17:4 W:70:25:250:25000 P:0:5d3:15:5:0 F:CON | BRAND_ELEC | IM_ELEC | HIDE_TYPE | SHOW_MODS | KILL_DRAGON F:INSTA_ART | ACTIVATE L:USE: msgf("Your scythe glows bright green...") L:USE: inc_fast(rand_range(20, 40)) L:USE: object.timeout = rand_range(100, 200) L:DESC: return "haste self (20-40) every 100-200 turns" L:HIT: if player.tim.fast > 0 then L:HIT: dam = dam * 2 L:HIT: end L:SPOIL: return "Double damage when hasted" D:A metal pole tipped with an arc of pure blue energy, the pulses of D:the blade ripping through opponents all the more deadly as you move D:faster. # The War Hammer of Aule N:117:of Aule I:21:8:4 W:70:75:120:25000 P:0:7d3:19:21:5 F:WIS | HIDE_TYPE | F:KILL_DRAGON | SLAY_EVIL | BRAND_ELEC | SLAY_UNDEAD | SLAY_DEMON | F:FREE_ACT | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_NEXUS | F:SEE_INVIS | SHOW_MODS | INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability & high resist" # The Quarterstaff 'Nar-i-vagil' N:118:'Nar-i-vagil' I:21:3:3 W:35:18:150:17000 P:0:1d18:10:20:0 F:INT | HIDE_TYPE | F:SLAY_ANIMAL | BRAND_FIRE | RES_FIRE | SHOW_MODS | LITE | INSTA_ART # The Quarterstaff 'Eriril' -> of the Wandering Wizard N:119:of the Wandering Wizard I:21:3:2 W:35:18:150:18000 P:0:1d18:3:5:0 F:INT | SP | HIDE_TYPE | F:RES_DARK | RES_LITE | LITE | SEE_INVIS | ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your quarterstaff glows yellow...") L:USE: if ident_spell() then object.timeout = 10 end L:DESC: return "identify every 10 turns" D:This quarterstaff has been owned by many different mages. Each owner D:has become a noteworthy mage, but none has kept the staff, choosing D:to instead pass it to a new keeper. # The Quarterstaff of Olorin -> Gandalf N:120:of Gandalf I:21:3:4 W:52:105:150:30000 P:0:1d30:10:13:0 F:INT | WIS | CHR | HIDE_TYPE | SENSE | BRAND_FIRE | F:SLAY_EVIL | BRAND_FIRE | SLAY_TROLL | SLAY_ORC | LITE | F:HOLD_LIFE | RES_FIRE | RES_NETHER | SEE_INVIS | ACTIVATE | SHOW_MODS F:INSTA_ART L:USE: msgf("Your quarterstaff glows brightly...") L:USE: detect_all() L:USE: probing() L:USE: identify_fully() L:USE: object.timeout = 1000 L:DESC: return "probing, detection and full id every 1000 turns" L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:SPOIL: return "Random ability" # The Mace of Disruption 'Deathwreaker' N:121:'Deathwreaker' I:21:20:6 W:100:38:400:44444 P:0:6d8:18:18:0 F:STR | TUNNEL | HIDE_TYPE | NO_TELE | F:SLAY_DRAGON | SLAY_ANIMAL | SLAY_EVIL | SLAY_UNDEAD | BRAND_FIRE | F:IM_FIRE | RES_DARK | RES_CONF | RES_CHAOS | RES_DISEN | AGGRAVATE | F:LITE | SHOW_MODS | BRAND_POIS | VAMPIRIC | INSTA_ART # The Lucerne Hammer 'Turmil' N:122:'Turmil' I:22:12:4 W:35:15:120:13000 P:0:2d5:10:6:8 F:WIS | INFRA | HIDE_TYPE | BLESSED | F:BRAND_COLD | SLAY_ORC | RES_COLD | RES_LITE | LITE | REGEN | F:ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your hammer glows white...") L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: drain_life(dir, 200) L:USE: object.timeout = 70 L:DESC: return "drain life (200) every 70 turns" L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist" # The Sling 'Catapult' N:123:'Catapult' I:19:2:2 W:12:8:5:8500 P:0:0d0:20:10:0 F:DEX | INFRA | HIDE_TYPE | SHOW_MODS | XTRA_MIGHT | ACTIVATE F:INSTA_ART L:USE: msgf("Your sling hums...") L:USE: clear_afraid() L:USE: hp_player(45) L:USE: object.timeout = 10 L:DESC: return "heal (45) every 10 turns" # The Long Bow 'Belthronding' N:124:'Belthronding' I:19:13:3 W:70:20:40:15000 P:0:0d0:20:22:0 F:DEX | STEALTH | HIDE_TYPE | RES_DISEN | XTRA_SHOTS | SHOW_MODS F:INSTA_ART # The Long Bow of Bard N:125:of Bard I:19:13:3 W:52:20:40:12000 P:0:0d0:17:19:0 F:DEX | HIDE_TYPE | FREE_ACT | XTRA_MIGHT | SHOW_MODS | INSTA_ART # The Light Crossbow 'Cubragol' -> of Brand N:126:of Brand I:19:23:10 W:80:25:60:70000 P:0:0d0:10:14:0 F:SPEED | RES_FIRE | XTRA_MIGHT | ACTIVATE | SHOW_MODS | INSTA_ART L:USE: msgf("Your crossbow glows deep red...") L:USE: brand_bolts() L:USE: object.timeout = 999 L:DESC: return "fire branding of bolts every 999 turns" L:MAKE: add_ego_power(EGO_XTRA_POWER, object) L:SPOIL: return "Random ability or high resist" # The Short Bow of Marksmanship N:127:of Marksmanship I:19:12:4 W:25:20:30:21000 P:0:0d0:35:15:0 F:DEX | SENSE | INFRA | SUST_DEX | RES_BLIND | HIDE_TYPE | SHOW_MODS | F:LITE | SEE_INVIS | XTRA_MIGHT | XTRA_SHOTS | INSTA_ART # The Cloak 'Soulless One' N:128:'Soulless One' I:35:1:4 W:45:40:10:7000 P:1:0d0:0:0:25 F:STEALTH | RES_DARK | RES_NEXUS | DRAIN_EXP | DRAIN_STATS | PASS_WALL | F:CURSED | AUTO_CURSE | INSTA_ART D:This cloak looks ordinary, but you have a strange feeling that D:there is something very wrong about it. # The Long Sword 'Luck Blade' # Variable bonus: +10 saves, +1 per +2 to-hit or to-dam N:129:'Luck Blade' I:23:17:0 W:20:25:130:15000 P:0:4d5:13:14:0 F:VORPAL | LUCK_10 | BLESSED | FREE_ACT | LITE | INSTA_ART | EASY_ENCHANT # Luck bonus depending on enchantment - normally an extra +13, # but it will go down if the luck blade is disenchanted, and up # if it is enchanted more. L:BONUS: b.skills[SKILL_SAV] = b.skills[SKILL_SAV] + (object.to_h + object.to_d) / 2 L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:SPOIL: return "Random high resist; +1 save / +2 skill or +10% deadliness" D:A sword said to grant incredible luck to its wielder. N:130:'Stormbringer' I:23:30:2 W:95:0:180:20000 P:0:6d6:16:16:0 F:VAMPIRIC | STR | CON | FREE_ACT | HOLD_LIFE | RES_NEXUS F:RES_CHAOS | RES_NETHER | RES_CONF | INSTA_ART | CURSED F:HEAVY_CURSE | NO_TELE | QUESTITEM L:MAKE: if (one_in_(2) ~= 0) then L:MAKE: set_obj_flag(object, 2, TR2_DRAIN_EXP) L:MAKE: else L:MAKE: set_obj_flag(object, 2, TR2_AGGRAVATE) L:MAKE: end L:SPOIL: return "Drain exp or aggravate" D:The Stormbringer is now under your control. It still thrists for souls. # The Trident of Hunger # Unique power: Very high hunger, but regenerate 2 hp per turn # Unique power: Triple damage when the player is hungry N:131:of Hunger I:22:5:3 W:40:25:70:16000 P:0:2d12:8:20:0 F:RES_FEAR | RES_FIRE | RES_COLD | RES_CHAOS | SLAY_ANIMAL F:VAMPIRIC | STR F:INSTA_ART | CURSED | AUTO_CURSE L:TIMED: set_food(player.food - 5) L:TIMED: hp_player(2) L:HIT: if player.food < PY_FOOD_ALERT then L:HIT: dam = dam * 3 L:HIT: end L:SPOIL: return "Accelerated regen; triple damage when hungry" D:This trident has the likeness of a demonic face carved on the hilt. When D:you are hungry the mouth opens to reveal dozens of sharp teeth. # The Flail of the Miser # Note: This is twice as heavy as a normal flail # Unique power: One extra damage per 100,000 au N:132:of the Miser I:21:13:-2 W:60:25:300:20000 P:0:2d6:12:6:0 F:HOLD_LIFE | CHR F:INSTA_ART L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) L:HIT: dam = dam + player.au / 100000 L:SPOIL: return "Random ability & high resist; +1 dam / 100,000 au" D:A metal flail made of pure gold, enchanted to be harder than steel. D:It would look perfect in the center of a huge pile of treasure... # The Cloak of the Jade Dragon # Variable bonus: +1 speed per 10 levels (round up), monks only # Unique power: Recharge is faster for monks N:133:of the Jade Dragon I:35:1:0 W:95:20:10:24000 P:1:0d0:8:8:35 F:ACTIVATE | RES_LITE | RES_DARK | RES_NETHER | RES_NEXUS | RES_DISEN F:HOLD_LIFE | SLOW_DIGEST | REGEN F:SUST_STR | SUST_CON | SUST_DEX | SUST_INT | SUST_WIS | SUST_CHR F:INSTA_ART L:USE: msgf("Your cloak glows bright green...") L:USE: inc_fast(rand_range(75, 150)) L:USE: object.timeout = rand_range(150, 300) L:DESC: return "haste self (75-150) every 150-300 turns" L:BONUS: if player.rp.pclass == CLASS_MONK then L:BONUS: b.pspeed = (player.lev + 9) / 10 L:BONUS: end L:SPOIL:return "Speed +1 / 10 levels (monk)" zangband/lib/edit/e_info.txt0000755000000000000000000003340010250356274015042 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2005/06/04 12:53:28 $ # File: e_info.txt # This file is used to initialize the "lib/raw/e_info.raw" file, which is # used to initialize the "ego-item" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/raw/e_info.raw" file. # The ego-item indexes are defined in "defines.h", and must not be changed. # Ego-items 1-63 are "armor", and 64-127 are "weapons" # Note that every "ego-item" type has a different "index", and can only be # created from items that belong to a certain "slot" in the equipment, if # one assumes that "ammo" belongs to an imaginary slot (23). However, it # is possible for several "ego-item" types to have the same "textual name", # such as with "Armor of Resistance" and "Shield of Resistance". # === Understanding e_info.txt === # N: serial number : ego type # X: ego type slot : rating # C: max to-hit : max to-dam : max to-ac : pval # W: depth : rarity : weight : cost # F: flag | flag | etc # 'N' indicates the beginning of an entry. The serial number must # increase for each new item. # 'X' is for extra information - inventory slot and rating. Slots # range from 23 (for ammunition) to 35 (for boots). Rating affects # level feelings. # 'C' is for creation bonusses - maximum to-hit, to-dam, to-ac, and # pval. Cursed ego-items will negate the given value. # 'W' is for extra information. Depth is the depth the object is # normally found at, rarity determines how common the object is, # weight is in tenth-pounds and cost is the items value. # 'F' is for flags. These are fairly self-explanatory. As many F: # lines may be used as are needed to specify all the flags and flags # are separated by the '|' symbol. # Note that the value of any flags and bonuses is counted automatically; # the cost is only needed if the item should have its cost adjusted from # the calculated value. # Version stamp (required) V:2.7.5 ### Body Armor ### N:4:of Resist Acid X:30:16 W:0:4:0:0 F:RES_ACID | IGNORE_ACID N:5:of Resist Lightning X:30:10 W:0:4:0:0 F:RES_ELEC | IGNORE_ELEC N:6:of Resist Fire X:30:14 W:0:4:0:0 F:RES_FIRE | IGNORE_FIRE N:7:of Resist Cold X:30:12 W:0:4:0:0 F:RES_COLD | IGNORE_COLD N:8:of Resistance X:30:20 W:15:8:0:1000 C:0:0:10:0 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: if (one_in_(4) ~= 0) then set_obj_flag(object, 1, TR1_RES_POIS) end L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) # OnePower N:9:of Elvenkind X:30:25 W:30:16:0:1000 C:0:0:10:3 F:STEALTH | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) # 10 (unused) # OnePower N:11:of Permanence X:30:30 W:0:1:0:5000 C:0:0:10:0 F:SUST_STR | SUST_DEX | SUST_CON | SUST_INT | SUST_WIS | SUST_CHR | F:HOLD_LIFE | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) ### Lites ### N:12:of Everburning X:29:10 W:0:2:0:0 C:0:0:0:0 F:LITE N:13:of Warmth X:29:13 W:0:3:0:0 C:0:0:0:0 F:RES_COLD N:14:of Vision X:29:15 W:18:10:0:0 C:0:0:0:6 F:SEE_INVIS | RES_BLIND | SENSE N:15:of Radiance X:29:13 W:15:10:0:0 C:0:0:0:4 F:RES_DARK | CHR ### Shields ### N:16:of Resist Acid X:32:16 W:0:4:0:0 F:RES_ACID | IGNORE_ACID N:17:of Resist Lightning X:32:10 W:0:2:0:0 F:RES_ELEC | IGNORE_ELEC N:18:of Resist Fire X:32:14 W:0:3:0:0 F:RES_FIRE | IGNORE_FIRE N:19:of Resist Cold X:32:12 W:0:2:0:0 F:RES_COLD | IGNORE_COLD N:20:of Resistance X:32:20 W:25:8:0:1000 C:0:0:10:0 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: if (one_in_(4) ~= 0) then set_obj_flag(object, 1, TR1_RES_POIS) end L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) N:21:of Reflection X:32:20 W:50:8:0:0 C:0:0:5:0 F:REFLECT F:IGNORE_ELEC | IGNORE_ACID | IGNORE_COLD | IGNORE_FIRE # 22 (unused) # 23 (unused) ### Crowns and Helms ### N:24:of Intelligence X:33:13 C:0:0:0:2 W:0:2:0:0 F:INT | SUST_INT N:25:of Wisdom X:33:13 W:0:2:0:00 C:0:0:0:2 F:WIS | SUST_WIS N:26:of Beauty X:33:8 W:0:2:0:0 C:0:0:0:4 F:CHR | SUST_CHR N:27:of the Magi X:21:15 W:60:2:0:0 C:0:0:0:3 F:INT | SUST_INT | SP L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) N:28:of Might X:21:19 W:50:2:0:0 C:0:0:0:3 F:STR | DEX | CON | SUST_STR | SUST_DEX | SUST_CON | FREE_ACT L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) N:29:of Lordliness X:21:17 W:20:2:0:0 C:0:0:0:3 F:WIS | CHR | SUST_WIS | SUST_CHR L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) N:30:of Seeing X:33:8 W:30:2:0:0 C:0:0:0:5 F:SENSE | RES_BLIND | SEE_INVIS L:MAKE: if (one_in_(7) ~= 0) then set_obj_flag(object, 2, TR2_TELEPATHY) end N:31:of Infravision X:33:11 W:0:4:0:0 C:0:0:0:5 F:INFRA | HIDE_TYPE N:32:of Light X:33:6 W:0:2:0:0 F:LITE | RES_LITE N:33:of Telepathy X:21:20 W:30:2:0:10000 F:TELEPATHY N:34:of Regeneration X:21:10 W:0:2:0:0 F:REGEN N:35:of Teleportation X:33:0 W:0:2:0:-100 F:TELEPORT N:36:of Stupidity X:33:0 C:0:0:0:-5 W:0:2:0:-100 F:INT N:37:of Naivety X:33:0 C:0:0:0:-5 W:0:2:0:-100 F:WIS N:38:of Ugliness X:33:0 C:0:0:0:-5 W:0:2:0:-100 F:CHR N:39:of Sickliness X:33:0 C:0:0:0:-5 W:0:2:0:-100 F:STR | DEX | CON ### Cloaks ### N:40:of Protection X:31:10 W:0:1:0:0 C:0:0:10:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD N:41:of Stealth X:31:10 W:0:1:0:0 C:0:0:0:3 F:STEALTH # OnePower N:42:of Aman X:31:20 W:50:8:0:0 C:0:0:20:3 F:STEALTH | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) # Aura, Fire N:43:of Immolation X:31:16 W:50:8:0:0 C:0:0:4:0 F:IGNORE_ACID | IGNORE_FIRE | SH_FIRE | RES_FIRE N:44:of Enveloping X:31:0 W:0:1:0:-100 C:-10:-10:0:0 F:SHOW_MODS N:45:of Vulnerability X:31:0 W:0:1:0:-100 C:0:0:-50:0 F:AGGRAVATE N:46:of Irritation X:31:0 W:0:1:0:-100 C:-15:-15:0:0 F:AGGRAVATE | SHOW_MODS # Aura, Electricity N:47:of Electricity X:31:16 W:50:8:0:0 C:0:0:4:0 F:IGNORE_ACID | IGNORE_ELEC | SH_ELEC | RES_ELEC ### Gloves ### N:48:of Free Action X:34:11 F:FREE_ACT W:0:1:0:0 N:49:of Slaying X:34:17 W:30:2:0:1500 C:6:6:0:0 F:SHOW_MODS N:50:of Agility X:34:14 W:0:3:0:0 C:0:0:0:5 F:DEX | HIDE_TYPE N:51:of Power X:34:22 W:20:4:0:0 C:5:5:0:5 F:STR | SHOW_MODS | HIDE_TYPE L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) N:52:(Ghoul Touch) X:34:22 W:40:4:0:0 C:6:6:0:0 F:GHOUL_TOUCH | SHOW_MODS # 53 (unused) N:54:of Weakness X:34:0 W:0:1:0:-100 C:0:0:0:-10 F:STR N:55:of Clumsiness X:34:0 W:0:1:0:-100 C:0:0:0:-10 F:DEX ### Boots ### N:56:of Levitation X:35:7 W:30:4:0:0 F:FEATHER L:MAKE: if (one_in_(2) ~= 0) then add_ego_power(EGO_XTRA_HI_RESIST, object) end N:57:of Stealth X:35:16 W:0:1:0:0 C:0:0:0:8 F:STEALTH N:58:of Free Action X:35:15 W:20:1:0:0 F:FREE_ACT N:59:of Speed X:35:25 W:20:4:0:1000 C:0:0:0:10 F:SPEED | HIDE_TYPE N:60:of the Wild X:35:5 W:0:4:0:0 F:WILD_WALK N:61:of Noise X:35:0 W:0:1:0:-100 F:AGGRAVATE N:62:of Slowness X:35:0 W:20:4:0:-100 C:0:0:0:-5 F:SPEED N:63:of Annoyance X:35:0 W:35:1:0:-100 C:0:0:0:-10 F:SPEED | AGGRAVATE ### Weapons ### # OneSustain N:64:(Holy Avenger) X:24:30 W:15:6:0:0 C:6:6:4:4 F:WIS | F:SLAY_EVIL | SLAY_UNDEAD | SLAY_DEMON | F:SEE_INVIS | BLESSED | RES_FEAR L:MAKE: if player.rp.pclass == CLASS_PALADIN then L:MAKE: set_obj_flag(object, 3, TR3_LUCK_10) L:MAKE: object.cost = object.cost - 3000 L:MAKE: end L:MAKE: add_ego_power(EGO_XTRA_SUSTAIN, object) L:MAKE: if (one_in_(4) ~= 0) then L:MAKE: set_obj_flag(object, 0, TR0_BLOWS) L:MAKE: object.pval = randint1(4) - object.dd / 2 L:MAKE: if object.pval < 1 then object.pval = 1 end L:MAKE: end # OneSustain N:65:(Defender) X:24:25 W:15:6:0:0 C:4:4:8:4 F:STEALTH | F:FREE_ACT | SEE_INVIS | FEATHER | REGEN | F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 1, TR1_RES_POIS) end L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_SUSTAIN, object) # OneAbility N:66:(Blessed) X:24:20 W:10:6:0:0 C:0:0:0:3 F:WIS | F:BLESSED L:MAKE: add_ego_power(EGO_XTRA_ABILITY, object) # 67 (unused) N:68:of Westernesse X:24:20 W:15:6:0:0 C:5:5:0:2 F:STR | DEX | CON | F:SLAY_ORC | SLAY_TROLL | SLAY_GIANT | F:FREE_ACT | SEE_INVIS L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 1, TR1_RES_FEAR) end N:69:of Extra Attacks X:24:20 W:50:3:0:500 F:BLOWS L:MAKE: object.pval = randint1(3) - object.dd / 2 L:MAKE: if object.pval < 1 then object.pval = 1 end N:70:of Slaying X:24:15 W:75:3:0:0 L:MAKE: object.ds = object.ds + (object.ds * randint1(5)) / 5 L:MAKE: if (one_in_(5) ~= 0) then set_obj_flag(object, 0, TR0_BRAND_POIS) end # 71 (unused) # The "Elemental" brands (4) (6) N:72:of Melting X:24:15 W:10:6:0:0 F:BRAND_ACID | RES_ACID | IGNORE_ACID N:73:of Shocking X:24:20 W:0:6:0:0 F:BRAND_ELEC | RES_ELEC | IGNORE_ELEC N:74:of Burning X:24:20 W:0:6:0:-500 F:BRAND_FIRE | RES_FIRE | IGNORE_FIRE | LITE N:75:of Freezing X:24:15 W:10:6:0:0 F:BRAND_COLD | RES_COLD | IGNORE_COLD N:76:of Venom X:24:20 W:10:6:0:0 F:BRAND_POIS | RES_POIS N:77:(Chaotic) X:24:28 W:0:6:0:0 F:CHAOTIC | RES_CHAOS | PATRON | IGNORE_ELEC | IGNORE_ACID | IGNORE_FIRE L:MAKE: if player.rp.pclass == CLASS_CHAOS_WARRIOR then L:MAKE: set_obj_flag(object, 0, TR0_SP) L:MAKE: object.pval = 1 L:MAKE: object.cost = object.cost - 2500 L:MAKE: end L:MAKE: add_ego_power(EGO_XTRA_ANY_RESIST, object) N:78:of Sharpness X:24:20 W:50:2:0:0 F:VORPAL | TUNNEL L:MAKE: object.pval = m_bonus(5, level) + 1 N:79:of Earthquakes X:24:20 W:50:2:0:0 F:TUNNEL | IMPACT L:MAKE: object.pval = m_bonus(3, level) + 3 L:MAKE: if (one_in_(3) ~= 0) then L:MAKE: set_obj_flag(object, 0, TR0_BLOWS) L:MAKE: object.pval = object.pval - object.dd / 2 L:MAKE: if object.pval < 1 then object.pval = 1 end L:MAKE: end # The "Slay" brands (8) N:80:of Slay Animal X:24:18 W:30:3:0:0 F:SLAY_ANIMAL N:81:of Slay Evil X:24:18 W:20:3:0:0 F:SLAY_EVIL N:82:of Slay Undead X:24:18 W:20:3:0:0 F:SLAY_UNDEAD | HOLD_LIFE N:83:of Slay Demon X:24:14 W:30:2:0:0 F:SLAY_DEMON N:84:of Slay Orc X:24:10 W:0:2:0:0 F:SLAY_ORC N:85:of Slay Troll X:24:10 W:40:2:0:0 F:SLAY_TROLL N:86:of Slay Giant X:24:14 W:60:2:0:0 F:SLAY_GIANT N:87:of Slay Dragon X:24:18 W:40:3:0:0 F:SLAY_DRAGON L:MAKE: add_ego_power(EGO_XTRA_LO_RESIST, object) # The "Kill" brands (8) N:88:of *Slay* Animal X:24:20 W:40:15:0:0 C:0:0:0:2 F:INT | SLAY_ANIMAL | REGEN N:89:of *Slay* Evil X:24:20 W:80:15:0:0 C:0:0:0:2 F:WIS | SLAY_EVIL | BLESSED | RES_FEAR N:90:of *Slay* Undead X:24:24 W:50:15:0:0 C:0:0:0:2 F:WIS | SLAY_UNDEAD | SEE_INVIS | RES_NETHER N:91:of *Slay* Demon X:24:16 W:40:10:0:0 C:0:0:0:2 F:INT | SLAY_DEMON N:92:of *Slay* Orc X:24:14 W:20:10:0:0 C:0:0:0:2 F:DEX | SLAY_ORC N:93:of *Slay* Troll X:24:14 W:50:10:0:0 C:0:0:0:2 F:STR | SLAY_TROLL N:94:of *Slay* Giant X:24:16 W:50:10:0:0 C:0:0:0:2 F:STR | SLAY_GIANT N:95:of *Slay* Dragon X:24:24 W:70:15:0:0 C:0:0:0:1 F:CON | SLAY_DRAGON | KILL_DRAGON L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 1, TR1_RES_POIS) end L:MAKE: add_ego_power(EGO_XTRA_LO_RESIST, object) L:MAKE: add_ego_power(EGO_XTRA_LO_RESIST, object) N:96:(Vampiric) X:24:25 W:70:3:0:0 F:VAMPIRIC | HOLD_LIFE # 97 (unused) N:98:(Trump Weapon) X:24:22 W:30:3:0:500 C:4:4:0:2 F:SLAY_EVIL | TELEPORT | FREE_ACT | SENSE | F:REGEN | SLOW_DIGEST | RES_NEXUS | ACTIVATE L:USE: msgf("Your weapon twists space around you...") L:USE: teleport_player(100) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "teleport every 50-100 turns" L:MAKE: if (one_in_(5) ~= 0) then set_obj_flag(object, 0, TR0_SLAY_DEMON) end L:MAKE: if (one_in_(7) ~= 0) then add_ego_power(EGO_XTRA_ABILITY, object) end L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) N:99:(Pattern Weapon) X:24:26 W:50:6:0:0 C:6:6:0:2 F:STR | CON | F:SLAY_EVIL | SLAY_DEMON | SLAY_UNDEAD | F:FREE_ACT | SEE_INVIS | L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 1, TR1_HOLD_LIFE) end L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 0, TR0_DEX) end L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 1, TR1_RES_FEAR) end L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) N:100:of Digging # Hack - digging items have a "slot" of 22 X:22:4 W:0:1:0:0 C:0:0:0:5 F:TUNNEL | BRAND_ACID | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD # 101 (unused) N:102:of Morgul X:24:0 W:5:1:0:0 C:-20:-20:-10:0 F:SEE_INVIS | AGGRAVATE | HEAVY_CURSE | CURSED # 103 (unused) ### Missile Launchers ### N:104:of Accuracy X:25:10 W:0:1:0:50 C:15:5:0:0 N:105:of Velocity X:25:10 W:20:1:0:50 C:5:15:0:0 N:106:of Hunting X:25:10 W:0:4:0:0 C:8:8:0:0 F:WILD_SHOT | SLAY_ANIMAL # 107 (unused) N:108:of Extra Might X:25:20 W:40:1:0:0 C:5:10:0:0 F:XTRA_MIGHT L:MAKE: add_ego_power(EGO_XTRA_ANY_RESIST, object) N:109:of Extra Shots X:25:20 C:10:5:0:0 W:50:1:0:0 F:XTRA_SHOTS # 110 (unused) # 111 (unused) ### Ammo ### N:112:of Hurt Animal X:23:10 W:0:1:0:0 F:SLAY_ANIMAL N:113:of Hurt Evil X:23:10 W:0:1:0:0 F:SLAY_EVIL # 114 (unused) # 115 (unused) # 116 (unused) N:117:of Returning X:23:5 F:RETURN W:0:1:0:0 N:118:of Explosion X:23:10 F:EXPLODE W:30:1:0:0 N:119:of Hurt Dragon X:23:10 F:SLAY_DRAGON W:0:1:0:0 N:120:of Slaying X:23:15 W:0:1:0:0 C:12:12:0:0 L:MAKE: object.ds = object.ds + (object.ds * randint1(5)) / 5 N:121:of Shocking X:23:10 F:BRAND_ELEC | IGNORE_ELEC W:0:1:0:0 N:122:of Flame X:23:10 F:BRAND_FIRE | IGNORE_FIRE W:0:1:0:0 N:123:of Frost X:23:10 F:BRAND_COLD | IGNORE_COLD W:0:1:0:0 N:124:of Wounding X:23:5 W:0:1:0:20 C:6:6:0:0 N:125:of Backbiting X:23:0 W:0:1:0:-100 C:-50:-50:0:0 ### Special Broken Items ### # Destroyed Weapon N:126:(Shattered) X:0:0 W:0:0:0:-100 C:-5:-5:0:0 # Destroyed Body Armor N:127:(Blasted) X:0:0 W:0:0:0:-100 C:0:0:-10:0 zangband/lib/edit/f_info.txt0000755000000000000000000001535010250356274015047 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2005/06/04 12:53:28 $ # File: f_info.txt # This file is used to initialize the "lib/raw/f_info.raw" file, which is # used to initialize the "terrain feature" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/raw/f_info.raw" file. # Note that the terrain feature are grouped into very regular groups, # such that each of the bits in the feature type conveys information. # Note that terrain feature zero contains the "darkness" picture. # === Understanding f_info.txt === # N:serial number:terrain name # G:symbol:color # F: flag | flag | etc # N' indicates the beginning of an entry. The serial number must # increase for each new item. # 'G' is for graphics - symbol and color. There are 16 colors, as # follows: # D - Dark Gray w - White s - Gray o - Orange # r - Red g - Green b - Blue u - Brown # d - Black W - Light Gray v - Violet y - Yellow # R - Light Red G - Light Green B - Light Blue U - Light Brown # 'F' is for flags. These are fairly self-explanatory. As many F: # lines may be used as are needed to specify all the flags and flags # are separated by the '|' symbol # Version stamp (required) V:2.7.5 # 0x00 --> nothing N:0:nothing G: :w # 0x01 --> open floor N:1:open floor G:.:w F:USE_TRANS # 0x02 --> invisible trap (drawn as open floor) N:2:invisible trap G:.:w F:USE_TRANS # 0x03 --> glyph of warding N:3:glyph of warding G:;:y F:USE_TRANS # 0x04 --> open door N:4:open door G:':U F:ICKY | OBJECT | MARK # 0x05 --> broken door N:5:broken door G:':u F:ICKY | OBJECT | MARK # 0x06 --> up stairs N:6:up staircase G:<:w F:USE_TRANS | ICKY | PERM | OBJECT | MARK # 0x07 --> down stairs N:7:down staircase G:>:w F:USE_TRANS | ICKY | PERM | OBJECT | MARK N:8:sand G:.:y F:USE_TRANS N:9:salt G:.:W F:USE_TRANS N:10:wet mud G:.:u F:USE_TRANS N:11:dry mud G:.:D F:USE_TRANS N:12:tiled floor G:.:r F:USE_TRANS N:13:wooden floor G:.:s F:USE_TRANS N:14:pebbles G:.:D F:USE_TRANS N:15:solidified lava G:::D F:USE_TRANS # Gap for old traps # 0x2x --> closed door N:32:door G:+:U F:BLOCK | ICKY | OBJECT | MARK # Pillar N:33:pillar G:#:D F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x30 --> secret door N:48:granite wall G:#:w F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x31 --> rubble N:49:pile of rubble G:::w F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x32 --> magma vein N:50:magma vein G:#:s F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x33 --> quartz vein N:51:quartz vein G:#:W F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x34 --> magma vein + treasure # This terrain type may not actually be needed... N:52:magma vein G:#:s F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x35 --> quartz vein + treasure # This terrain type may not actually be needed... N:53:quartz vein G:#:W F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x36 --> magma vein + known treasure N:54:magma vein with treasure G:*:o F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x37 --> quartz vein + known treasure N:55:quartz vein with treasure G:*:o F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x38 --> granite wall -- basic N:56:granite wall G:#:w F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x39 --> granite wall -- inner N:57:granite wall G:#:w F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x3A --> granite wall -- outer N:58:granite wall G:#:w F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x3B --> granite wall -- solid N:59:granite wall G:#:w F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK # 0x3C --> permanent wall -- basic N:60:permanent wall G:#:w F:BLOCK | USE_TRANS | ICKY | PERM | OBJECT | MARK # 0x3D --> permanent wall -- inner N:61:permanent wall G:#:w F:BLOCK | USE_TRANS | ICKY | PERM | OBJECT | MARK # 0x3E --> permanent wall -- outer N:62:permanent wall G:#:w F:BLOCK | USE_TRANS | ICKY | PERM | OBJECT | MARK # 0x3F --> permanent wall -- solid N:63:permanent wall G:#:w F:BLOCK | USE_TRANS | ICKY | PERM | OBJECT | MARK N:64:explosive rune G:*:R F:USE_TRANS | OBJECT | MARK N:65:Pattern startpoint G:*:w F:ICKY | PERM | OBJECT | PATTERN | MARK N:66:section of the Pattern G:*:B F:ICKY | PERM | OBJECT | PATTERN | MARK N:67:section of the Pattern G:*:b F:ICKY | PERM | OBJECT | PATTERN | MARK N:68:section of the Pattern G:*:B F:ICKY | PERM | OBJECT | PATTERN | MARK N:69:section of the Pattern G:*:b F:ICKY | PERM | OBJECT | PATTERN | MARK N:70:section of the Pattern G:*:W F:ICKY | PERM | OBJECT | PATTERN | MARK N:71:section of the Pattern (discharged) G:*:W F:ICKY | PERM | OBJECT | PATTERN | MARK N:72:Pattern exit G:*:w F:ICKY | PERM | OBJECT | PATTERN | MARK N:73:corrupted section of the Pattern G:*:D F:ICKY | PERM | OBJECT | PATTERN | MARK # 0x53 --> terrain -- deep water N:83:deep water G:~:b F:USE_TRANS | ICKY | MARK N:84:shallow water G:~:B F:USE_TRANS | MARK N:85:deep lava G:~:R F:USE_TRANS | ICKY | MARK N:86:shallow lava G:~:r F:USE_TRANS | MARK N:87:dark pit G:#:D F:USE_TRANS | ICKY | OBJECT | MARK N:88:dirt G:.:u F:USE_TRANS N:89:patch of grass G:.:g F:USE_TRANS | OBJECT N:90:compact rune G:^:D F:USE_TRANS | OBJECT N:91:invisible wall G:.:w F:USE_TRANS | ICKY N:92:very deep water G:~:D F:USE_TRANS | ICKY | MARK N:93:deep acid G:~:g F:USE_TRANS | ICKY | MARK N:94:shallow acid G:~:G F:USE_TRANS | MARK N:95:submerged tree G:%:b F:USE_TRANS | ICKY | OBJECT N:96:tree G:%:G F:HALF_LOS | USE_TRANS | ICKY | OBJECT N:97:rock face G:::u F:HALF_LOS | USE_TRANS | ICKY | OBJECT N:98:snow covered rock face G:::W F:HALF_LOS | USE_TRANS | ICKY | OBJECT N:99:boulder G:::y F:HALF_LOS | USE_TRANS | ICKY | OBJECT N:100:pine tree G:%:g F:HALF_LOS | USE_TRANS | ICKY | OBJECT N:101:snow covered tree G:%:w F:HALF_LOS | USE_TRANS | ICKY | OBJECT N:102:obelisk G:;:b F:HALF_LOS | USE_TRANS | ICKY | OBJECT | MARK N:103:pillar G:#:w F:HALF_LOS | USE_TRANS | ICKY | OBJECT | MARK N:112:stone fence G:::b F:HALF_LOS | USE_TRANS | ICKY | PERM | OBJECT | MARK N:113:well G:~:v F:HALF_LOS | USE_TRANS | ICKY | PERM | OBJECT | MARK N:114:fountain G:~:U F:HALF_LOS | USE_TRANS | ICKY | PERM | OBJECT | MARK N:115:jungle G:%:D F:BLOCK | USE_TRANS | ICKY | OBJECT | MARK N:128:bush G:%:y F:USE_TRANS | OBJECT N:129:dead bush G:%:s F:USE_TRANS | OBJECT N:130:long grass G:;:U F:USE_TRANS #rock on general terrain N:131:rock G:::U F:USE_TRANS | OBJECT #rock on snow N:132:rock G:::U F:USE_TRANS | OBJECT # dead tree on general terrain N:133:dead tree G:%:u F:USE_TRANS | ICKY | OBJECT # dead tree on snow N:134:dead tree G:%:u F:USE_TRANS | ICKY | OBJECT N:135:snow G:.:W F:USE_TRANS N:136:thick swamp G:;:g F:USE_TRANS | ICKY | MARK N:137:swamp G:;:G F:USE_TRANS | MARK zangband/lib/edit/k_info.txt0000755000000000000000000032335610250356274015064 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2005/06/04 12:53:28 $ # File: k_info.txt # This file is used to initialize the "lib/raw/k_info.raw" file, which is # used to initialize the "object kind" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/raw/k_info.raw" file. # Available slots (?): # 29, 29, 90, 177, 178, 179, 182, 183, 195, 196, 205, # 268, 270, 293, 299, 350, 351, 399, 413, 414, 423-479 # XXX XXX Add some "IGNORE_XXX" flags to Rings, Amulets, etc. # The old "MULTI_HUED" objects are now "violet", and no other object # is violet, so all six violet objects can be made "multi-hued", though # this would be a heinous hack. XXX XXX # Note that object zero is used for the "stack" picture (unused). # === Understanding k_info.txt === # N: serial number : & object name~ # G: symbol : color # I: tval : sval : pval # W: depth : rarity : weight : cost # P: base armor class : base damage : plus to-hit : plus to-dam : plus to-ac # A: depth/rarity : depth/rarity : etc # F: flag | flag | etc # 'N' indicates the beginning of an entry. The serial number must # increase for each new item. The '&' and '~' symbols are use to # include articles and pluralization as necessary to ensure # grammatical correctness in object descriptions. # 'G' is for graphics - symbol and color. There are 16 colors, as # follows: # D - Dark Gray w - White s - Gray o - Orange # r - Red g - Green b - Blue u - Brown # d - Black W - Light Gray v - Violet y - Yellow # R - Light Red G - Light Green B - Light Blue U - Light Brown # 'I' is for basic information. The tval is for the type of item, the # sval identifies the subtype and the pval indicates the amount of # effect the item has, if applicable. # 'W' is for extra information. Depth is the depth the object is # normally found at, rarity determines how common the object is, # weight is in tenth-pounds and cost is the items value. # 'P' is for power information. The items base armor class, its base # damage and pluses to-hit, to-dam and to-ac. # 'A' is for allocation - depth and rarity, in pairs. This allows an # item to have multiple natural depths and rarities. It is used to # ensure that certain vital items such as food and identify scrolls # are found throughout the dungeon. # 'F' is for flags. These are fairly self-explanatory. As many F: # lines may be used as are needed to specify all the flags and flags # are separated by the '|' symbol. # 'L' is for lua scripts: # USE - self-explanatory. returns used,ident # SMASH - potion smash effect. returns angry,ident # Version stamp (required) V:2.7.5 ##### Something special ##### N:0:something G:&:w ##### Mushrooms ##### N:1:Blindness G:,:d I:80:1:500 W:9:0:1:0 A:9/2 F:EASY_KNOW L:USE: ident = not player_res(TR1_RES_BLIND) and inc_blind(rand_range(200, 400)) D:This mushroom causes temporary blindness when eaten. N:2:Paranoia G:,:d I:80:2:500 W:9:0:1:0 A:9/2 F:EASY_KNOW L:USE: ident = not player_res(TR1_RES_FEAR) and inc_afraid(rand_range(10, 20)) D:This mushroom causes temporary fear when eaten. N:3:Confusion G:,:d I:80:3:500 W:9:0:1:0 A:9/2 F:EASY_KNOW L:USE: ident = not player_res(TR1_RES_CONF) and inc_confused(rand_range(10, 20)) D:This mushroom causes temporary confusion when eaten. N:4:Hallucination G:,:d I:80:4:500 W:17:0:1:0 A:17/2 F:EASY_KNOW L:USE: ident = not player_res(TR1_RES_CHAOS) and inc_image(rand_range(250, 500)) D:This mushroom causes temporary hallucination when eaten. Groovy, man! N:5:Cure Poison G:,:d I:80:12:500 W:9:0:1:60 A:9/1 F:EASY_KNOW L:USE: ident = clear_poisoned() D:This mushroom completely cures all poisons when eaten. N:6:Cure Blindness G:,:d I:80:13:500 W:9:0:1:50 A:9/1 F:EASY_KNOW L:USE: ident = clear_blind() D:This mushroom completely cures blindness when eaten. N:7:Cure Paranoia G:,:d I:80:14:500 W:9:0:1:25 A:9/1 F:EASY_KNOW L:USE: ident = clear_afraid() D:This mushroom completely cures fear when eaten. N:8:Cure Confusion G:,:d I:80:15:500 W:9:0:1:50 A:9/1 F:EASY_KNOW L:USE: ident = clear_confused() D:This mushroom completely cures confusion when eaten. N:9:Weakness G:,:d I:80:6:500 W:9:0:1:0 A:9/2 P:0:5d5:0:0:0 F:EASY_KNOW L:USE: take_hit(damroll(6, 6), "poisonous food"); do_dec_stat(A_STR) L:USE: ident = TRUE D:This mushroom deals damage and drains strength when eaten. N:10:Unhealth G:,:d I:80:10:500 W:26:0:1:100 A:26/1 P:0:10d10:0:0:0 F:EASY_KNOW L:USE: take_hit(damroll(10, 10), "poisonous food"); do_dec_stat(A_CON) L:USE: ident = TRUE D:This mushroom deals damage and drains constitution when eaten. N:11:Restore Constitution G:,:d I:80:18:500 W:25:0:1:350 A:25/1 F:EASY_KNOW L:USE: ident = do_res_stat(A_CON) D:This mushroom cures constitution drain when eaten. N:12:Restoring G:,:d I:80:19:500 W:50:0:1:1000 A:35/28:52/14:65/4 F:EASY_KNOW L:USE: if do_res_stat(A_STR) then ident = TRUE end L:USE: if do_res_stat(A_INT) then ident = TRUE end L:USE: if do_res_stat(A_WIS) then ident = TRUE end L:USE: if do_res_stat(A_DEX) then ident = TRUE end L:USE: if do_res_stat(A_CON) then ident = TRUE end L:USE: if do_res_stat(A_CHR) then ident = TRUE end D:This mushroom cures all stat drains when eaten. N:13:Stupidity G:,:d I:80:8:500 W:26:0:1:0 A:26/3 F:EASY_KNOW L:USE: take_hit(damroll(8, 8), "poisonous food"); do_dec_stat(A_INT) L:USE: ident = TRUE D:This mushroom causes damage and drains intelligence when eaten. N:14:Naivety G:,:d I:80:9:500 W:26:0:1:0 A:26/3 F:EASY_KNOW L:USE: take_hit(damroll(8, 8), "poisonous food"); do_dec_stat(A_WIS) L:USE: ident = TRUE D:This mushroom causes damage and drains wisdom when eaten. N:15:Poison G:,:d I:80:0:500 W:9:0:1:0 A:9/2 P:0:4d4:0:0:0 F:EASY_KNOW L:USE: ident = res_pois_lvl() ~= 0 and inc_poisoned(rand_range(10, 20)) D:This mushroom is poisonous. N:16:Sickness G:,:d I:80:7:500 W:17:0:1:0 A:17/2 P:0:4d4:0:0:0 F:EASY_KNOW L:USE: take_hit(damroll(6, 6), "poisonous food"); do_dec_stat(A_CON) L:USE: ident = TRUE D:This mushroom causes damage and drains constitution when eaten. N:17:Paralysis G:,:d I:80:5:500 W:35:0:1:0 A:35/2 F:EASY_KNOW L:USE: ident = not player_res(TR1_FREE_ACT) and inc_paralyzed(rand_range(10, 20)) D:This mushroom causes temporary paralysis when eaten. N:18:Restore Strength G:,:d I:80:17:500 W:25:0:1:350 A:25/1 F:EASY_KNOW L:USE: ident = do_res_stat(A_STR) D:This mushroom restores strength drain when eaten. N:19:Disease G:,:d I:80:11:500 W:35:0:1:300 A:35/2 P:0:15d15:0:0:0 F:EASY_KNOW L:USE: take_hit(damroll(10, 10), "poisonous food"); do_dec_stat(A_STR) L:USE: ident = TRUE D:This mushroom causes damage and drains strength when eaten. N:20:Cure Serious Wounds G:,:d I:80:16:500 W:16:0:1:75 A:16/1 F:EASY_KNOW L:USE: ident = hp_player(damroll(4, 8)) D:This mushroom cures 4d8 hitpoints when eaten. ##### Normal Food ##### N:21:& Ration~ of Food G:,:U I:80:35:5000 W:0:0:10:3 A:1/1:9/1:17/1 F:EASY_KNOW L:USE: msgf("That tastes good.") D:Hard bread, dried fruit, and other high-density food. N:22:& Hard Biscuit~ G:,:U I:80:32:500 W:0:0:2:1 A:1/1 F:EASY_KNOW L:USE: msgf("That tastes good.") D:A biscuit, cooked hard to last. N:23:& Strip~ of Venison G:,:u I:80:33:1500 W:0:0:2:2 A:1/1 F:EASY_KNOW L:USE: msgf("That tastes good.") D:A piece of dried meat, from the haunch of a deer. N:24:& Slime Mold~ G:,:g I:80:36:3000 W:1:0:5:2 A:1/1 F:EASY_KNOW L:USE: msgf("That tastes good.") D:A green, slimy... something. N:25:& Piece~ of Elvish Waybread G:,:B I:80:37:7500 W:10:0:3:10 A:10/1:20/1:30/1:40/1 F:EASY_KNOW L:USE: msgf("That tastes good."); clear_poisoned(); hp_player(damroll(4, 8)) D:Made by elves, this extremely nutritious trail bread also D:cures poison and restores 4d8 hp when eaten. N:26:& Pint~ of Fine Ale G:,:y I:80:38:500 W:0:0:5:1 A:1/3 F:EASY_KNOW L:USE: msgf("That tastes good.") D:A jug of thick ale, to keep your hunger away. N:27:& Pint~ of Fine Wine G:,:r I:80:39:1000 W:0:0:10:2 A:1/3 F:EASY_KNOW L:USE: msgf("That tastes good.") D:A rich, fruity wine to slake your hunger and thirst. ##### Extra digger ##### N:28:& Mattock~ G:\:D I:20:7:2 W:30:0:250:700 A:30/12:50/1 P:0:1d8:0:0:0 F:SHOW_MODS | TUNNEL D:The digger that is wieldy enough to double as a weapon. ##### Edged Weapons ##### N:29:& No-dachi~ G:|:W I:23:27:0 W:61:0:190:1000 A:61/3:80/1 P:0:5d4:0:0:0 F:SHOW_MODS D:A large sword used by samurai, designed to be carried across the back. N:30:& Broken Dagger~ G:|:D I:23:1:0 W:0:0:5:1 A:1/1 P:0:1d1:-2:-4:0 F:SHOW_MODS D:This may have once been a servicable dagger, but now it's little more D:than a broken stub. N:31:& Bastard Sword~ G:|:W I:23:21:0 W:26:0:140:350 A:26/1 P:0:3d4:0:0:0 F:SHOW_MODS D:A large sword designed to be equally useful in a one-handed or D:two-handed grip. N:32:& Scimitar~ G:|:W I:23:18:0 W:17:0:130:250 A:17/1 P:0:2d5:0:0:0 F:SHOW_MODS D:A large saber with a curved blade, originating in the East. N:33:& Tulwar~ G:|:W I:23:15:0 W:9:0:100:200 A:9/1 P:0:2d4:0:0:0 F:SHOW_MODS N:34:& Broad Sword~ G:|:W I:23:16:0 W:17:0:150:255 A:17/1:26/1 P:0:2d5:0:0:0 F:SHOW_MODS N:35:& Short Sword~ G:|:W I:23:10:0 W:9:0:80:90 A:9/1 P:0:1d7:0:0:0 F:SHOW_MODS N:36:& Blade~ of Chaos G:|:v I:23:30:0 W:95:0:180:4000 A:95/4 P:0:6d5:0:0:0 F:RES_CHAOS | SHOW_MODS D:The air seems to twist around this violet blade... N:37:& Two-Handed Sword~ G:|:W I:23:25:0 W:53:0:200:775 A:53/1:65/1:80/1 P:0:3d6:0:0:0 F:SHOW_MODS N:38:& Main Gauche~ G:|:W I:23:5:0 W:5:0:30:25 A:5/2 P:0:1d5:0:0:0 F:SHOW_MODS N:39:& Cutlass~ G:|:W I:23:12:0 W:9:0:110:85 A:9/1 P:0:1d7:0:0:0 F:SHOW_MODS D:A short curving sword often used by sailors. N:40:& Executioner's Sword~ G:|:r I:23:28:0 W:65:0:260:850 A:65/1:85/1 P:0:4d5:0:0:0 F:SHOW_MODS N:41:& Katana~ G:|:W I:23:20:0 W:35:0:120:400 A:35/1 P:0:3d4:0:0:0 F:SHOW_MODS N:42:& Long Sword~ G:|:W I:23:17:0 W:17:0:130:300 A:17/1:35/1 P:0:2d5:0:0:0 F:SHOW_MODS N:43:& Dagger~ G:|:W I:23:4:0 W:0:0:12:10 A:1/1:9/2 P:0:1d4:0:0:0 F:THROW | SHOW_MODS N:44:& Rapier~ G:|:W I:23:7:0 W:9:0:40:42 A:9/1 P:0:1d6:0:0:0 F:SHOW_MODS D:A long, slender two-edged sword with a cuplike hilt. N:45:& Sabre~ G:|:W I:23:11:0 W:9:0:50:50 A:9/1 P:0:1d7:0:0:0 F:SHOW_MODS D:A light dueling sword with a tapered, flexible blade. N:46:& Small Sword~ G:|:W I:23:8:0 W:9:0:75:48 A:9/1 P:0:1d6:0:0:0 F:SHOW_MODS N:47:& Broken Sword~ G:|:D I:23:2:0 W:0:0:30:2 A:0/1 P:0:1d2:-2:-4:0 F:SHOW_MODS D:This was once the blade of a valiant warrior, but now it's D:ruined beyond repair. ##### Hafted Weapons ##### N:48:& Ball-and-Chain~ G:\:D I:21:6:0 W:35:0:150:200 A:35/2 P:0:2d4:0:0:0 F:SHOW_MODS N:49:& Whip~ G:\:D I:21:2:0 W:5:0:30:30 A:5/1 P:0:1d3:0:0:0 F:SHOW_MODS N:50:& Flail~ G:\:D I:21:13:0 W:17:0:150:350 A:17/1:25/1 P:0:2d6:0:0:0 F:SHOW_MODS N:51:& Two-Handed Flail~ G:\:y I:21:18:0 W:60:0:280:590 A:60/1:80/1 P:0:3d6:0:0:0 F:SHOW_MODS N:52:& Morning Star~ G:\:D I:21:12:0 W:17:0:150:400 A:17/1 P:0:2d6:0:0:0 F:SHOW_MODS N:53:& Mace~ G:\:D I:21:5:0 W:9:0:120:130 A:9/1 P:0:2d4:0:0:0 F:SHOW_MODS N:54:& Quarterstaff~ G:\:U I:21:3:0 W:30:0:150:200 A:30/12:50/1 P:0:1d18:0:0:0 F:SHOW_MODS N:55:& War Hammer~ G:\:D I:21:8:0 W:9:0:120:225 A:9/1 P:0:3d3:0:0:0 F:SHOW_MODS N:56:& Lead-Filled Mace~ G:\:D I:21:15:0 W:26:0:180:500 A:26/1 P:0:3d4:0:0:0 F:SHOW_MODS N:57:& Mace~ of Disruption G:\:v I:21:20:0 W:100:0:400:4300 A:100/2 P:0:5d8:0:0:0 F:SLAY_UNDEAD | SHOW_MODS D:Dust and dry spatters decorate the head of this mighty weapon. N:58:& Lucerne Hammer~ G:/:B I:22:12:0 W:17:0:120:375 A:17/1 P:0:2d5:0:0:0 F:SHOW_MODS ##### Polearms ##### N:59:& Beaked Axe~ G:/:s I:22:10:0 W:26:0:180:400 A:26/1 P:0:2d6:0:0:0 F:SHOW_MODS N:60:& Glaive~ G:/:s I:22:13:0 W:35:0:190:380 A:35/1 P:0:2d6:0:0:0 F:SHOW_MODS N:61:& Halberd~ G:/:s I:22:15:0 W:43:0:190:430 A:43/1 P:0:3d5:0:0:0 F:SHOW_MODS N:62:& Awl-Pike~ G:/:s I:22:4:0 W:30:0:160:340 A:30/1 P:0:1d15:0:0:0 F:SHOW_MODS N:63:& Pike~ G:/:s I:22:8:0 W:26:0:160:360 A:26/1 P:0:2d5:0:0:0 F:SHOW_MODS N:64:& Spear~ G:/:s I:22:2:0 W:5:0:50:40 A:5/5:9/1 P:0:1d6:0:0:0 F:SHOW_MODS N:65:& Trident~ G:/:y I:22:5:0 W:15:0:70:120 A:15/1 P:0:1d12:0:0:0 F:SHOW_MODS N:66:& Lance~ G:/:s I:22:20:0 W:30:0:300:300 A:30/5:40/1 P:0:1d17:0:0:0 F:SHOW_MODS N:67:& Great Axe~ G:/:s I:22:25:0 W:65:0:230:500 A:65/1:95/1 P:0:4d4:0:0:0 F:SHOW_MODS N:68:& Battle Axe~ G:/:s I:22:22:0 W:26:0:170:340 A:26/1 P:0:2d8:0:0:0 F:SHOW_MODS N:69:& Lochaber Axe~ G:/:D I:22:28:0 W:70:0:250:750 A:70/1:90/1 P:0:3d8:0:0:0 F:SHOW_MODS N:70:& Broad Axe~ G:/:s I:22:11:0 W:26:0:160:310 A:26/1 P:0:2d6:0:0:0 F:SHOW_MODS N:71:& Scythe~ G:/:s I:22:17:0 W:70:0:250:800 A:70/1:92/1 P:0:5d3:0:0:0 F:SHOW_MODS N:72:& Scythe~ of Slicing G:/:r I:22:30:0 W:93:0:250:3500 A:93/1:105/1 P:0:8d4:0:0:0 F:SHOW_MODS D:This blade seems to cut down foes as normal scythes do wheat. ##### Bows, Crossbows, Slings ##### N:73:& Short Bow~ G:}:U I:19:12:0 W:5:0:30:50 A:5/1:60/1 F:SHOW_MODS D:Just anout anyone can shoot an arrow with a bow. N:74:& Long Bow~ G:}:U I:19:13:0 W:17:0:40:120 A:17/1:70/1 F:SHOW_MODS D:The arrows leave the long bow for the most damage. N:75:& Light Crossbow~ G:}:s I:19:23:0 W:26:0:60:140 A:26/1:80/1 F:SHOW_MODS D:Slower but deadlier than bows. N:76:& Heavy Crossbow~ G:}:s I:19:24:0 W:52:0:100:300 A:52/1:90/1 F:SHOW_MODS D:One shot can take care of small fry. N:77:& Sling~ G:}:u I:19:2:0 W:1:0:5:5 A:1/1:50/1 F:SHOW_MODS D:Small enough for kids to handle. ##### Missiles ##### N:78:& Arrow~ G:{:U I:17:1:0 W:10:0:2:5 A:10/20:17/2:43/2 P:0:1d12:0:0:0 F:SHOW_MODS D:Standard issue. N:79:& Seeker Arrow~ G:{:G I:17:2:0 W:50:0:2:50 A:50/20:80/2:90/1:105/1 P:0:1d20:0:0:0 F:SHOW_MODS D:This arrow has a superior flight. N:80:& Bolt~ G:{:s I:18:0:0 W:5:0:3:5 A:5/1:43/2:70/2 P:0:1d10:0:0:0 F:SHOW_MODS D:Standard issue. N:81:& Seeker Bolt~ G:{:B I:18:2:0 W:60:0:3:75 A:60/40:90/4:100/4 P:0:1d20:0:0:0 F:SHOW_MODS D:This bolt is the best. N:82:& Rounded Pebble~ G:{:s I:16:0:0 W:0:0:6:1 A:0/1:5/1 P:0:2d2:0:0:0 F:SHOW_MODS D:Heavy and easy to break. You can find better. N:83:& Iron Shot~ G:{:s I:16:1:0 W:10:0:8:2 A:10/1:17/1 P:0:4d2:0:0:0 F:SHOW_MODS D:Standard issue. ##### Shovels and Picks ##### N:84:& Shovel~ G:\:s I:20:1:1 W:4:0:60:10 A:4/4 P:0:1d2:0:0:0 F:SHOW_MODS | TUNNEL D:A basic digging tool. N:85:& Gnomish Shovel~ G:\:G I:20:2:2 W:35:0:60:100 A:35/4 P:0:1d2:0:0:0 F:SHOW_MODS | TUNNEL D:A well-crafted digging tool, ready to burrow. N:86:& Dwarven Shovel~ G:\:B I:20:3:3 W:55:0:120:300 A:55/1 P:0:1d3:0:0:0 F:SHOW_MODS | TUNNEL D:An excellent digging tool, guaranteed to quickly shift rock. N:87:& Pick~ G:\:s I:20:4:1 W:17:0:150:50 A:17/16 P:0:1d3:0:0:0 F:SHOW_MODS | TUNNEL D:A basic digging tool. N:88:& Orcish Pick~ G:\:g I:20:5:2 W:52:0:150:300 A:52/4 P:0:1d3:0:0:0 F:SHOW_MODS | TUNNEL D:A crude but effective digging tool. N:89:& Dwarven Pick~ G:\:b I:20:6:3 W:75:0:200:600 A:75/1 P:0:1d4:0:0:0 F:SHOW_MODS | TUNNEL D:The greatest of digging tools that cuts through any rock with ease. ##### Armor ##### N:90:& Elven Cloak~ G:(:G I:35:2:0 W:52:0:5:3000 A:52/4:95/1 P:4:0d0:0:0:4 F:IGNORE_ACID | IGNORE_COLD | IGNORE_FIRE | IGNORE_ELEC F:STEALTH | SENSE D:This finely woven cloak seems to blend in with any background. N:91:& Pair~ of Soft Leather Boots G:]:U I:30:2:0 W:5:0:20:7 A:5/1 P:2:1d1:0:0:0 N:92:& Pair~ of Hard Leather Boots G:]:U I:30:3:0 W:9:0:40:12 A:9/1:43/1 P:3:1d1:0:0:0 N:93:& Pair~ of Metal Shod Boots G:]:s I:30:6:0 W:20:0:80:50 A:20/5:35/1:80/1 P:6:1d1:0:0:0 N:94:& Hard Leather Cap~ G:]:u I:32:2:0 W:5:0:15:12 A:5/1 P:2:0d0:0:0:0 N:95:& Metal Cap~ G:]:s I:32:3:0 W:17:0:20:30 A:17/1 P:3:1d1:0:0:0 N:96:& Iron Helm~ G:]:s I:32:5:0 W:35:0:75:75 A:35/1 P:5:1d3:0:0:0 N:97:& Steel Helm~ G:]:W I:32:6:0 W:45:0:60:220 A:45/1 P:6:1d3:0:0:0 N:98:& Iron Crown~ G:]:s I:33:10:0 W:60:0:20:200 A:60/1 P:0:1d1:0:0:0 N:99:& Golden Crown~ G:]:y I:33:11:0 W:75:0:30:500 A:75/1 P:0:1d1:0:0:0 F:IGNORE_ACID N:100:& Jewel Encrusted Crown~ G:]:v I:33:12:0 W:80:0:40:1000 A:80/1 P:0:1d1:0:0:0 F:IGNORE_ACID N:101:& Robe~ G:(:b I:36:2:0 W:1:0:20:4 A:1/1:80/1 P:2:0d0:0:0:0 N:102:& Filthy Rag~ G:(:D I:36:1:0 W:0:0:20:1 A:0/1 P:1:0d0:0:0:-1 D:This looks like it was once a fine robe, but now it's ripped and tattered. N:103:& Soft Leather Armour~ G:(:U I:36:4:0 W:5:0:80:18 A:5/1 P:4:0d0:0:0:0 N:104:& Soft Studded Leather~ G:(:U I:36:5:0 W:5:0:90:35 A:5/1 P:5:1d1:0:0:0 N:105:& Hard Leather Armour~ G:(:U I:36:6:0 W:9:0:100:150 A:9/1 P:6:1d1:-1:0:0 N:106:& Hard Studded Leather~ G:(:U I:36:7:0 W:17:0:110:250 A:17/1 P:7:1d2:-1:0:0 N:107:& Leather Scale Mail~ G:(:U I:36:11:0 W:26:0:140:500 A:26/1 P:11:1d1:-1:0:0 N:108:& Metal Scale Mail~ G:[:s I:37:3:0 W:43:0:250:600 A:43/1 P:13:1d4:-2:0:0 N:109:& Chain Mail~ G:[:s I:37:4:0 W:43:0:220:800 A:43/1 P:14:1d4:-2:0:0 N:110:& Rusty Chain Mail~ G:[:r I:37:1:0 W:25:0:200:550 A:43/1 P:14:1d4:-5:0:-8 D:This rusted heap still offers small protection, but hinders movement. N:111:& Augmented Chain Mail~ G:[:s I:37:6:0 W:52:0:270:1200 A:52/1 P:16:1d4:-2:0:0 N:112:& Bar Chain Mail~ G:[:s I:37:8:0 W:61:0:280:1500 A:61/1 P:18:1d4:-2:0:0 N:113:& Metal Brigandine Armour~ G:[:s I:37:9:0 W:61:0:290:1750 A:61/1 P:19:1d4:-3:0:0 N:114:& Partial Plate Armour~ G:[:W I:37:12:0 W:70:0:260:2000 A:70/1 P:22:1d6:-3:0:0 N:115:& Metal Lamellar Armour~ G:[:W I:37:13:0 W:70:0:340:2500 A:70/1 P:23:1d6:-3:0:0 N:116:& Full Plate Armour~ G:[:W I:37:15:0 W:75:0:380:5000 A:75/1 P:25:2d4:-3:0:0 N:117:& Ribbed Plate Armour~ G:[:W I:37:18:0 W:80:0:380:6000 A:80/1 P:28:2d4:-3:0:0 N:118:& Adamantite Plate Mail~ G:[:G I:37:30:0 W:95:0:420:20000 A:95/2 P:40:2d4:-4:0:0 F:IGNORE_ACID N:119:& Mithril Plate Mail~ G:[:B I:37:25:0 W:90:0:300:15000 A:90/1 P:35:2d4:-3:0:0 F:IGNORE_ACID N:120:& Mithril Chain Mail~ G:[:B I:37:20:0 W:85:0:150:10000 A:85/1 P:28:1d4:-1:0:0 F:IGNORE_ACID N:121:& Double Chain Mail~ G:[:s I:37:7:0 W:52:0:250:1500 A:52/1 P:16:1d4:-2:0:0 # This shield does not belong here N:122:& Shield~ of Deflection G:[:B I:34:10:0 W:95:0:100:5000 A:95/2 P:14:1d4:0:0:10 F:IGNORE_ACID ### The Cloaks ### N:123:& Cloak~ G:(:g I:35:1:0 W:1:0:10:3 A:1/1:35/1 P:1:0d0:0:0:0 N:124:& Shadow Cloak~ G:(:D I:35:6:0 W:100:0:5:7500 A:100/1 P:6:0d0:0:0:4 F:RES_DARK | RES_LITE D:A cloak that appears to be woven out of strands of pure darkness. ### The Gloves ### N:125:& Set~ of Leather Gloves G:]:U I:31:1:0 W:1:0:5:3 A:1/1 P:1:0d0:0:0:0 N:126:& Set~ of Gauntlets G:]:U I:31:2:0 W:17:0:25:35 A:17/1:40/1 P:2:1d1:0:0:0 N:127:& Set~ of Cesti G:]:W I:31:5:0 W:45:0:40:100 A:45/1 P:5:1d1:0:0:0 ### The shields ### N:128:& Small Leather Shield~ G:):U I:34:2:0 W:5:0:50:30 A:5/1 P:3:1d2:0:0:0 N:129:& Large Leather Shield~ G:):U I:34:4:0 W:26:0:100:120 A:26/1:60/1 P:6:1d4:0:0:0 N:130:& Small Metal Shield~ G:):s I:34:3:0 W:17:0:65:50 A:17/1:40/1 P:5:1d3:0:0:0 N:131:& Large Metal Shield~ G:):s I:34:5:0 W:52:0:120:400 A:52/1:70/1 P:8:1d5:0:0:0 ##### Rings ##### # Note that most rings with a pval-dependent value # cost the same as a +1 bonus (higher bonuses # increase the cost automatically), and most rings # which give a single flag are costed at half the # price of the flag. N:132:Strength G:=:d I:45:24:5 W:52:0:2:500 A:52/1 F:STR | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you strong. N:133:Dexterity G:=:d I:45:26:5 W:52:0:2:500 A:52/1 F:DEX | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you feel limber. N:134:Constitution G:=:d I:45:27:5 W:52:0:2:500 A:52/1 F:CON | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you feel healthy. N:135:Intelligence G:=:d I:45:25:5 W:52:0:2:500 A:52/1 F:INT | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you feel smart. # Ring of speed costs 5000 + value of bonus N:136:Speed G:=:d I:45:31:10 W:100:0:2:5500 A:100/1 F:SPEED | HIDE_TYPE D:This ring almost vibrates with power. L:MAKE: object.pval = randint1(object.pval / 2) + m_bonus(object.pval, level) L:MAKE: while (one_in_(2) ~= 0) do object.pval = object.pval + 1 end L:MAKE: allow_curse = TRUE L:MAKE: rating_boost = 25 N:137:Sensing G:=:d I:45:23:5 W:9:0:2:50 A:9/2 F:SENSE | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you feel aware. N:138:Teleportation G:=:d I:45:4:0 W:9:0:2:125 A:9/2 F:CURSED | TELEPORT | EASY_KNOW D:Just looking at this ring makes you want to jump. N:139:Slow Digestion G:=:d I:45:6:0 W:9:0:2:125 A:9/2 F:SLOW_DIGEST | EASY_KNOW D:Just looking at this ring makes you feel full. N:140:Resist Fire G:=:d I:45:8:0 W:17:0:2:250 A:17/2 F:RES_FIRE | IGNORE_FIRE | EASY_KNOW D:Just looking at this ring makes you feel warm. N:141:Resist Cold G:=:d I:45:9:0 W:17:0:2:250 A:17/2 F:RES_COLD | IGNORE_COLD | EASY_KNOW D:Just looking at this ring makes you feel cold. N:142:Levitation G:=:d I:45:7:0 W:9:0:2:125 A:9/2 F:FEATHER | EASY_KNOW D:Just looking at this ring makes you feel up in the air. N:143:Poison Immunity G:=:d I:45:20:0 W:60:0:2:5000 A:50/8:85/2 F:IM_POIS | EASY_KNOW D:Just looking at this ring makes you feel poisonous. N:144:Free Action G:=:d I:45:21:0 W:25:0:2:500 A:25/2 F:FREE_ACT | EASY_KNOW D:Just looking at this ring makes you feel unhindered. N:145:Weakness G:=:d I:45:2:-5 W:9:0:2:0 A:9/2 F:CURSED | STR | HIDE_TYPE L:MAKE: object.pval = -(1 + m_bonus(-(object.pval), level)) D:Just looking at this ring makes you feel like a weakling. N:146:Flames G:=:d I:45:18:0 W:75:0:2:1500 A:75/1 P:0:0d0:0:0:15 F:RES_FIRE | IGNORE_FIRE | ACTIVATE L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_FIRE, dir, 100, 2) L:USE: inc_oppose_fire(rand_range(20, 40)) L:USE: object.timeout = rand_range(25, 50) L:DESC: return "ball of fire (100) and resist fire (20+ turns)" L:MAKE: object.to_a = rand_range(5, 10) + m_bonus(10, level) D:This ring feels warm to the touch. N:147:Acid G:=:d I:45:17:0 W:75:0:2:1500 A:75/1 P:0:0d0:0:0:15 F:RES_ACID | IGNORE_ACID | ACTIVATE L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_ACID, dir, 100, 2) L:USE: inc_oppose_acid(rand_range(20, 40)) L:USE: object.timeout = rand_range(25, 50) L:DESC: return "ball of acid (100) and resist acid (20+ turns)" L:MAKE: object.to_a = rand_range(5, 10) + m_bonus(10, level) D:This ring is covered with a strange sheen. N:148:Ice G:=:d I:45:19:0 W:75:0:2:1500 A:75/1 P:0:0d0:0:0:15 F:RES_COLD | IGNORE_COLD | ACTIVATE L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: fire_ball(GF_COLD, dir, 100, 2) L:USE: inc_oppose_cold(rand_range(20, 40)) L:USE: object.timeout = rand_range(25, 50) L:DESC: return "ball of cold (100) and resist cold (20+ turns)" L:MAKE: object.to_a = rand_range(5, 10) + m_bonus(10, level) D:This ring feels cool to the touch. N:149:Woe G:=:d I:45:0:-5 W:75:0:2:0 A:75/2 F:CURSED | TELEPORT | WIS | CHR | HIDE_TYPE L:MAKE: object.pval = -(1 + m_bonus(-(object.pval), level)) L:MAKE: object.to_a = -(5 + m_bonus(10, level)) D:Just looking at this ring makes you feel in trouble. N:150:Stupidity G:=:d I:45:3:-5 W:9:0:2:0 A:9/2 F:CURSED | INT | HIDE_TYPE L:MAKE: object.pval = -(1 + m_bonus(-(object.pval), level)) D:Just looking at this ring makes you feel like a dummy. # The value of a ring of combat is in the bonuses N:151:Combat G:=:d I:45:29:0 W:35:0:2:100 A:35/1 L:MAKE: object.to_d = rand_range(5, 13) + m_bonus(10, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you feel damaging. # The value of a ring of skill is in the bonuses N:152:Skill G:=:d I:45:28:0 W:35:0:2:100 A:35/1 L:MAKE: object.to_h = rand_range(5, 13) + m_bonus(10, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you spoil for a fight. # The value of a ring of protection is in the bonuses N:153:Protection G:=:d I:45:16:0 W:17:0:2:100 A:17/2 L:MAKE: object.to_a = rand_range(5, 13) + m_bonus(10, level) L:MAKE: allow_curse = TRUE D:Just looking at this ring makes you feel protected. N:154:Aggravate Monster G:=:d I:45:1:0 W:9:0:2:0 A:9/3 F:CURSED | AGGRAVATE | EASY_KNOW D:Just looking at this ring makes you feel like waking up the neighbourhood. N:155:See Invisible G:=:d I:45:22:0 W:30:0:2:250 A:30/3:52/1 F:SEE_INVIS | EASY_KNOW D:You can only just see this ring. N:156:Sustain Strength G:=:d I:45:10:0 W:20:0:2:100 A:20/8:52/8 F:SUST_STR | EASY_KNOW D:With this ring old age will not wear out your strength. N:157:Sustain Intelligence G:=:d I:45:11:0 W:20:0:2:100 A:20/8:52/8 F:SUST_INT | EASY_KNOW D:With this ring old age will not wear out your intelligence. N:158:Sustain Wisdom G:=:d I:45:12:0 W:20:0:2:100 A:20/8:52/8 F:SUST_WIS | EASY_KNOW D:With this ring old age will not wear out your wisdom. N:159:Sustain Constitution G:=:d I:45:13:0 W:20:0:2:100 A:20/8:52/8 F:SUST_CON | EASY_KNOW D:With this ring old age will not wear out your health. N:160:Sustain Dexterity G:=:d I:45:14:0 W:20:0:2:100 A:20/8:52/8 F:SUST_DEX | EASY_KNOW D:With this ring old age will not wear out your dextrousness. N:161:Sustain Charisma G:=:d I:45:15:0 W:20:0:2:100 A:20/16:52/16 F:SUST_CHR | EASY_KNOW D:With this ring old age will not wear out your good looks. # The value of a ring of slaying is in the bonuses N:162:Slaying G:=:d I:45:30:0 W:65:0:2:200 A:65/1 F:SHOW_MODS L:MAKE: object.to_d = randint1(7) + m_bonus(10, level) L:MAKE: object.to_h = randint1(7) + m_bonus(10, level) L:MAKE: allow_curse = TRUE D:With this ring you wil be an allround fighter. ##### Amulets ##### N:163:Brilliance G:":d I:40:6:4 W:35:0:3:1000 A:35/2:85/1 F:INT | WIS | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:With this amulet you feel o so smart. N:164:Charisma G:":d I:40:7:4 W:20:0:3:250 A:20/2 F:CHR | HIDE_TYPE L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:With this amulet you feel o so cute. N:165:Sensing G:":d I:40:5:5 W:12:0:3:50 A:12/2 F:SENSE | HIDE_TYPE L:MAKE: object.pval = randint1(5) + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:With this amulet you feel aware of traps. N:166:Teleportation G:":d I:40:1:0 W:26:0:3:125 A:26/2 F:CURSED | TELEPORT | EASY_KNOW D:With this amulet you can go anywhere. N:167:Slow Digestion G:":d I:40:3:0 W:26:0:3:125 A:26/2 F:SLOW_DIGEST | EASY_KNOW D:With this amulet you will not feel hungry too often. N:168:Resist Acid G:":d I:40:4:0 W:35:0:3:250 A:35/2 F:RES_ACID | IGNORE_ACID | EASY_KNOW D:With this amulet you feel uncorrosive. N:169:Berserk Strength G:":d I:40:2:0 W:33:0:3:10 A:33/3 F:AGGRAVATE L:MAKE: object.to_d = randint1(7) + m_bonus(10, level) L:MAKE: object.to_h = randint1(7) + m_bonus(10, level) L:MAKE: object.to_a = -(rand_range(5, 10)) D:This amulet seems to growl silently as you touch it. ##### Extra armor ##### N:170:& Double Ring Mail~ G:[:s I:37:5:0 W:43:0:230:1000 A:43/1 P:15:1d4:-2:0:0 ##### Additional amulets ##### # Amulet of the magi is worth an extra 2000 au for no reason N:171:the Magi G:":d I:40:8:5 W:75:0:3:3750 A:75/4:100/3 P:0:0d0:0:0:3 F:FREE_ACT | SEE_INVIS | SENSE | F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: object.pval = randint1(5) + m_bonus(object.pval, level) L:MAKE: object.to_a = randint1(5) + m_bonus(5, level) L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 2, TR2_SLOW_DIGEST) end L:MAKE: rating_boost = 25 D:With this amulet you will not be tricked often. N:172:Destruction G:":d I:40:0:-5 W:80:0:3:0 A:80/2 P:0:7d7:-5:-5:5 F:CURSED | HEAVY_CURSE | STR | INT | WIS | DEX | CON | CHR | F:TY_CURSE | NO_TELE | NO_MAGIC | REGEN | HIDE_TYPE L:MAKE: object.pval = -(randint1(5) + m_bonus(-(object.pval), level)) L:MAKE: object.to_a = -(randint1(5) + m_bonus(5, level)) D:With this amulet you feel like a disaster. ##### Scrolls ##### N:173:Enchant Weapon Skill G:?:d I:70:17:0 W:26:0:5:250 A:26/1 F:EASY_KNOW L:USE: if not enchant_spell(1, 0, 0) then result = FALSE end L:USE: ident = TRUE D:This scroll makes a single weapon easier to hit with. D:It might not work on a weapon that's already enchanted. N:174:Enchant Weapon Deadliness G:?:d I:70:18:0 W:26:0:5:250 A:26/1 F:EASY_KNOW L:USE: if not enchant_spell(0, 1, 0) then result = FALSE end L:USE: ident = TRUE D:This scroll makes a single weapon more damaging. D:It might not work on a weapon that's already enchanted. N:175:Enchant Armor G:?:d I:70:16:0 W:26:0:5:250 A:26/1 F:EASY_KNOW L:USE: if not enchant_spell(0, 0, 1) then result = FALSE end L:USE: ident = TRUE D:This scroll makes a single piece of armor more effective protection. D:It might not work on armor that's already enchanted. N:176:Identify G:?:d I:70:12:0 W:5:0:5:50 A:1/1:9/1:17/1:52/1 F:EASY_KNOW L:USE: if not ident_scroll(object.k_idx) then result = FALSE end L:USE: ident = TRUE D:This scroll identifies one item. N:177:*Identify* G:?:d I:70:13:0 W:30:0:5:500 A:30/1:60/1:90/1 F:EASY_KNOW L:USE: if not identify_fully() then result = FALSE end L:USE: ident = TRUE D:This scroll identifies one item and reveals all of its powers. N:178:Rumour G:?:d I:70:51:0 W:1:0:5:10 A:1/1 F:EASY_KNOW L:USE: msgf("There is a message on the scroll. It says:"); message_flush() L:USE: msgf(get_rumor()); message_flush() L:USE: msgf("The scroll disappears in a puff of smoke!") L:USE: ident = TRUE D:Someone has written something on this scroll. N:179:Logrus G:?:d I:70:50:0 W:80:0:5:1000 A:80/1 F:IGNORE_FIRE | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC F:EASY_KNOW L:USE: fire_ball(GF_CHAOS, 0, 400, 4) L:USE: if not player_res(TR1_RES_CHAOS) then take_hit(rand_range(150, 300), L:USE: "a Scroll of Logrus") end L:USE: ident = TRUE D:The chaotic runes on this scroll look very dangerous. D:It creates a chaos burst (400) centered on you, but will hurt you as well. N:180:Remove Curse G:?:d I:70:14:0 W:17:0:5:100 A:17/1:35/2:65/2 F:EASY_KNOW L:USE: if remove_curse() then L:USE: msgf("You feel as if someone is watching over you.") L:USE: ident = TRUE end D:This scroll will remove curses that prevent you from removing equipment. D:It isn't powerful enough to affect some curses. N:181:Light G:?:d I:70:24:0 W:0:0:5:15 A:0/1:5/1 F:EASY_KNOW L:USE: ident = lite_area(damroll(2, 8), 2) D:This scroll lights the room you are in. It also deals a small amount D:of damage to nearby creatures that are vulnerable to light. N:182:Fire G:?:d I:70:48:0 W:75:0:5:500 A:75/1 F:IGNORE_FIRE | EASY_KNOW L:USE: fire_ball(GF_FIRE, 0, 350, 4) L:USE: if res_fire_lvl() > 3 then take_hit(rand_range(100, 200), L:USE: "a Scroll of Fire") end L:USE: ident = TRUE D:This scroll creates a fire burst (350) centered on you, but D:will hurt you as well. N:183:Ice G:?:d I:70:49:0 W:75:0:5:600 A:75/1 F:IGNORE_COLD | EASY_KNOW L:USE: fire_ball(GF_COLD, 0, 300, 4) L:USE: if res_fire_lvl() > 3 then take_hit(rand_range(50, 100), L:USE: "a Scroll of Ice") end L:USE: ident = TRUE D:This scroll creates a cold burst (300) centered on you, but D:will hurt you as well. N:184:Summon Monster G:?:d I:70:4:0 W:1:0:5:0 A:1/1 F:EASY_KNOW L:USE: ident = summon_monsters(randint1(3), 0) D:This scroll summons monsters from elsewhere... and they won't D:be happy about it! N:185:Phase Door G:?:d I:70:8:0 W:1:0:5:15 A:1/1 F:EASY_KNOW L:USE: teleport_player(10) L:USE: ident = TRUE D:This scroll teleports you to a random location with a range of 100'. N:186:Teleportation G:?:d I:70:9:0 W:17:0:5:40 A:17/1 F:EASY_KNOW L:USE: teleport_player(100) L:USE: ident = TRUE D:This scroll teleports you to a random location with a range of 1000' N:187:Teleport Level G:?:d I:70:10:0 W:30:0:5:150 A:30/1 F:EASY_KNOW L:USE: teleport_player_level() L:USE: ident = TRUE D:This scroll teleports you up or down one level at random. N:188:Monster Confusion G:?:d I:70:36:0 W:9:0:5:30 A:9/2 F:EASY_KNOW L:USE: if player.state.confusing == 0 then L:USE: msgf("Your hands begin to glow.") L:USE: player.state.confusing = TRUE L:USE: player.redraw = bOr(player.redraw, PR_STATUS) L:USE: ident = TRUE L:USE: end D:This scroll creates a magical field that will confuse the next D:monster you hit. N:189:Magic Mapping G:?:d I:70:25:0 W:9:0:5:100 A:9/2 F:EASY_KNOW L:USE: map_area() L:USE: ident = TRUE D:This scroll magically reveals the dungeon around you. N:190:Rune of Protection G:?:d I:70:38:0 W:75:0:5:1000 A:75/2:100/4 F:EASY_KNOW L:USE: if not warding_glyph() then result = FALSE end L:USE: ident = TRUE D:This scroll creates a magical glyph that will keep monsters D:from crossing it. N:191:*Remove Curse* G:?:d I:70:15:0 W:75:0:5:8000 A:75/1:100/1 F:EASY_KNOW L:USE: if remove_all_curse() then L:USE: msgf("You feel as if someone is watching over you.") L:USE: ident = TRUE L:USE: end D:This scroll removes all but the most powerful curses from your D:equipment, allowing you to remove cursed items. N:192:Treasure Detection G:?:d I:70:26:0 W:0:0:5:5 A:0/1 F:EASY_KNOW L:USE: ident = detect_treasure() L:USE: if detect_objects_gold() then ident = TRUE end D:This scroll magically reveals the location of gems and D:precious metals nearby. N:193:Object Detection G:?:d I:70:27:0 W:0:0:5:5 A:0/1 F:EASY_KNOW L:USE: ident = detect_objects_normal() D:This scroll magically reveals the location of objects nearby. N:194:Trap Detection G:?:d I:70:28:0 W:9:0:5:5 A:9/1 F:EASY_KNOW L:USE: ident = detect_traps(ident) D:This scroll magically reveals the location of traps nearby. ##### Extra ammunition ##### N:195:& Sheaf Arrow~ G:{:o I:17:2:0 W:35:0:4:20 A:35/5:52/3:75/1 P:0:1d16:0:0:0 F:SHOW_MODS D:An arrow with detailed work on it. N:196:& Mithril Shot~ G:{:B I:16:2:0 W:43:0:8:15 A:43/3:75/2 P:0:6d2:0:0:0 F:SHOW_MODS D:Nice and shiny, these deliver the goods. ##### Additional scrolls ##### N:197:Door/Stair Location G:?:d I:70:29:0 W:9:0:5:35 A:9/2 F:EASY_KNOW L:USE: ident = detect_doors() L:USE: if detect_stairs() then ident = TRUE end D:This scroll magically reveals the location of doors and stairs nearby. N:198:Acquirement G:?:d I:70:46:0 W:50:0:5:100000 A:35/8 F:EASY_KNOW L:USE: acquirement(player.px, player.py, 1, TRUE, FALSE) L:USE: ident = TRUE D:This scroll transports a powerful item to your location. D:You will get better items on deeper dungeon levels. N:199:*Acquirement* G:?:d I:70:47:0 W:90:0:5:200000 A:90/16 F:EASY_KNOW L:USE: acquirement(player.px, player.py, rand_range(2, 3), TRUE, FALSE) L:USE: ident = TRUE D:This scroll transports several powerful items to your location. D:You will get better items on deeper dungeon levels. N:200:Mass Genocide G:?:d I:70:45:0 W:75:0:5:5000 A:75/4:85/4 F:EASY_KNOW L:USE: mass_genocide(TRUE) L:USE: ident = TRUE D:The evil runes on this scroll make you tremble. D:It destroys all nearby monsters totally. You won't get any experience D:or treasure from them. N:201:Detect Invisible G:?:d I:70:30:0 W:1:0:5:15 A:1/2 F:EASY_KNOW L:USE: ident = detect_monsters_invis() D:This scroll magically detects nearby invisible monsters. N:202:Aggravate Monster G:?:d I:70:1:0 W:9:0:5:0 A:9/2 F:EASY_KNOW L:USE: msgf("There is a high pitched humming noise.") L:USE: aggravate_monsters(0) L:USE: ident = TRUE D:This scroll attracts monsters, and enrages those already nearby. N:203:Trap Creation G:?:d I:70:7:0 W:5:0:5:0 A:5/20:17/2 F:EASY_KNOW L:USE: ident = trap_creation() D:This scroll creates traps around you. N:204:Trap/Door Destruction G:?:d I:70:39:0 W:10:0:5:50 A:10/8:17/2 F:EASY_KNOW L:USE: ident = destroy_doors_touch() D:This scroll will destroy traps and doors within 10' of you. N:205:Artifact Creation G:?:d I:70:52:0 W:100:0:5:300000 A:100/16 F:EASY_KNOW L:USE: if not artifact_scroll() then result = FALSE end L:USE: ident = TRUE D:This powerful scroll will turn a normal item into a unique D:artifact. Be careful, sometimes artifacts have drawbacks as D:well as benefits. It can't affect an item that already D:has special powers. N:206:Recharging G:?:d I:70:22:0 W:40:0:5:200 A:40/1 F:EASY_KNOW L:USE: if not recharge(130) then result = FALSE end L:USE: ident = TRUE D:This scroll will recharge a wand, staff or rod. There's a small D:chance it will fail and blow up the item you're trying to recharge. N:207:Genocide G:?:d I:70:44:0 W:65:0:5:1000 A:65/1 F:EASY_KNOW L:USE: genocide(TRUE) L:USE: ident = TRUE D:The evil runes on this scroll make you shiver. D:It will totally destroy all monsters on the current level of D:a single letter you select. You won't get any experience or treasure D:from these monsters. N:208:Darkness G:?:d I:70:0:0 W:1:0:5:0 A:1/2 F:EASY_KNOW L:USE: if not player_res(TR1_RES_BLIND) and not player_res(TR1_RES_DARK) then L:USE: inc_blind(rand_range(3, 8)) L:USE: end L:USE: ident = unlite_area(10, 3) D:This scroll creates a sphere of magical darkness centered on you. D:It also causes temporary blindness. N:209:Protection from Evil G:?:d I:70:37:0 W:52:0:5:200 A:52/1 F:EASY_KNOW L:USE: ident = inc_protevil(3 * player.lev + randint1(25)) D:This scroll provides temporary protection from evil monsters. N:210:Satisfy Hunger G:?:d I:70:32:0 W:5:0:5:10 A:5/1:20/1:45/1:75/1 F:EASY_KNOW L:USE: ident = set_food(PY_FOOD_MAX - 1) D:This scroll makes you instantly full. N:211:Dispel Undead G:?:d I:70:42:0 W:65:0:5:300 A:65/1 F:EASY_KNOW L:USE: ident = dispel_undead(60) D:This scroll dispels nearby undead (60). N:212:*Enchant Weapon* G:?:d I:70:21:0 W:50:0:5:500 A:50/5:75/1 F:EASY_KNOW L:USE: if not enchant_spell(randint1(5), randint1(5), 0) then result = FALSE end L:USE: ident = TRUE D:This scroll magically enchants one weapon. D:It might not work on a weapon that's already enchanted. N:213:Curse Weapon G:?:d I:70:3:0 W:75:0:5:0 A:75/1 F:EASY_KNOW L:USE: ident = curse_weapon() D:This scroll curses your current weapon, making it worse than useless. N:214:*Enchant Armor* G:?:d I:70:20:0 W:50:0:5:500 A:50/5:75/1:92/1 F:EASY_KNOW L:USE: if not enchant_spell(0, 0, rand_range(2, 7)) then result = FALSE end L:USE: ident = TRUE D:This scroll magically enchants one piece of armor. D:It might not work on armor that's already enchanted. N:215:Curse Armor G:?:d I:70:2:0 W:75:0:5:0 A:75/1 F:EASY_KNOW L:USE: ident = curse_armor() D:This scroll curses the armor you are wearing, making it worse than useless. N:216:Summon Undead G:?:d I:70:5:0 W:15:0:5:0 A:26/1 F:EASY_KNOW L:USE: ident = summon_monsters(randint1(3), SUMMON_UNDEAD) D:This scroll summons several undead monsters to your location. D:They won't be happy about being summoned. N:217:Blessing G:?:d I:70:33:0 W:1:0:5:15 A:1/2 F:EASY_KNOW L:USE: ident = inc_blessed(rand_range(6, 18)) D:This scroll gives you a temporary blessing (6-18 turns), improving D:your armor class and weapon skill. N:218:Holy Chant G:?:d I:70:34:0 W:15:0:5:50 A:15/2 F:EASY_KNOW L:USE: ident = inc_blessed(rand_range(12, 36)) D:This scroll gives you a temporary blessing (12-36 turns), improving D:your armor class and weapon skill. N:219:Holy Prayer G:?:d I:70:35:0 W:30:0:5:120 A:30/2 F:EASY_KNOW L:USE: ident = inc_blessed(rand_range(24, 72)) D:This scroll gives you a temporary blessing (24-72 turns), improving D:your armor class and weapon skill. N:220:Word of Recall G:?:d I:70:11:0 W:5:0:5:70 A:1/3:7/1 F:EASY_KNOW L:USE: word_of_recall() L:USE: ident = TRUE D:This scroll has two uses. If read within a dungeon, it returns you D:to the surface. If read near the entrance of a dungeon, it takes you to D:the last level of that dungeon you recalled out from. N:221:*Destruction* G:?:d I:70:41:0 W:65:0:5:500 A:65/1 F:EASY_KNOW L:USE: if not destroy_area(player.px, player.py, 15) then L:USE: msgf("The dungeon trembles...") end L:USE: ident = TRUE D:This powerful scroll destroys the nearby dungeon, turning it into a D:jumble of smashed rock. ##### Potions ##### N:222:Slime Mold Juice G:!:d I:75:2:400 W:0:0:4:2 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("You feel less thirsty.") L:USE: ident = TRUE L:SMASH:result = TRUE D:A green liquid that tries to climb out of the bottle. N:223:Apple Juice G:!:d I:75:1:250 W:0:0:4:1 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("You feel less thirsty.") L:USE: ident = TRUE L:SMASH:result = TRUE D:A cloudy brown liquid that smells of apple. N:224:Water G:!:d I:75:0:200 W:0:0:4:1 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("You feel less thirsty.") L:USE: ident = TRUE L:SMASH:result = TRUE D:This water has been boiled and is safe to drink. N:225:Strength G:!:d I:75:48:0 W:40:0:4:10000 A:33/6:39/6:43/3:53/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_inc_stat(A_STR) D:This potion permanently increases your strength. N:226:Weakness G:!:d I:75:16:0 W:5:0:4:0 A:5/2 P:0:3d12:0:0:0 F:EASY_KNOW L:USE: ident = do_dec_stat(A_STR) L:SMASH:result = TRUE D:This potion drains your strength. N:227:Restore Strength G:!:d I:75:42:0 W:15:0:4:300 A:15/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_res_stat(A_STR) D:This potion cures strength drain. N:228:Intelligence G:!:d I:75:49:0 W:40:0:4:10000 A:33/6:39/6:43/3:53/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_inc_stat(A_INT) D:This potion permanently increases your intelligence. N:229:Stupidity G:!:d I:75:17:0 W:35:0:4:0 A:35/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_dec_stat(A_INT) L:SMASH:result = TRUE D:This potion drains your intelligence. N:230:Restore Intelligence G:!:d I:75:43:0 W:15:0:4:300 A:15/1 P:0:1d1:0:0:0 L:USE: ident = do_res_stat(A_INT) F:EASY_KNOW D:This potion cures intelligence drain. N:231:Wisdom G:!:d I:75:50:0 W:40:0:4:10000 A:33/6:39/6:43/3:53/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_inc_stat(A_WIS) D:This potion permanently increases your wisdom. N:232:Naivety G:!:d I:75:18:0 W:35:0:4:0 A:35/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_dec_stat(A_WIS) L:SMASH:result = TRUE D:This potion drains your wisdom. N:233:Restore Wisdom G:!:d I:75:44:0 W:15:0:4:300 A:15/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_res_stat(A_WIS) D:This potion cures wisdom drain. N:234:Charisma G:!:d I:75:53:0 W:35:0:4:5000 A:29/6:35/6:39/3:49/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_inc_stat(A_CHR) D:This potion permanently increases your charisma. N:235:Ugliness G:!:d I:75:21:0 W:35:0:4:0 A:35/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_dec_stat(A_CHR) L:SMASH:result = TRUE D:This potion drains your charisma. N:236:Restore Charisma G:!:d I:75:47:0 W:15:0:4:300 A:15/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_res_stat(A_CHR) D:This potion cures charisma drain. N:237:Curing G:!:d I:75:61:100 W:18:0:4:500 A:18/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = cure_potion(150, TRUE, TRUE, TRUE) L:USE: if clear_image() then ident = TRUE end L:SMASH:project(who, 2, x, y, damroll(6, 3), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures blindness, confusion, poison, cuts, and hallucination, D:and cures 150 hit points. # Note! Invulnerability decreases your food... N:238:Invulnerability G:!:d I:75:62:-2500 W:105:0:4:100000 A:105/9 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: inc_invuln(rand_range(7, 14)) L:USE: ident = TRUE D:This potion makes you temporarily invulnerable to damage (7-14 turns). N:239:New Life G:!:d I:75:63:100 W:100:0:4:50000 A:75/20:100/10:115/5 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: do_cmd_rerate(); cure_all_mutations() L:USE: ident = TRUE D:This potion removes all mutations and rerolls your hitpoints. D:Be careful, your new hitpoints might be worse than your old ones. N:240:Cure Serious Wounds G:!:d I:75:35:100 W:5:0:4:75 A:5/1:15/1:25/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = cure_potion(75, TRUE, TRUE, FALSE) L:SMASH:project(who, 2, x, y, damroll(4, 3), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures blindness and confusion and restores 75 hit points. N:241:Cure Critical Wounds G:!:d I:75:36:100 W:20:0:4:300 A:20/1:30/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = cure_potion(150, TRUE, TRUE, TRUE) L:SMASH:project(who, 2, x, y, damroll(6, 3), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures blindness, confusion, poison and cuts, and D:restores 150 hit points. N:242:Healing G:!:d I:75:37:200 W:26:0:4:700 A:26/1:52/1:85/1:100/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = cure_potion(300, TRUE, TRUE, TRUE) L:SMASH:project(who, 2, x, y, damroll(10, 10), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures blindness, confusion, poison and cuts, and D:restores 300 hit points. N:243:Constitution G:!:d I:75:52:0 W:40:0:4:10000 A:33/6:39/6:43/3:53/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_inc_stat(A_CON) D:This potion permanently increases your constitution. N:244:Experience G:!:d I:75:59:0 W:75:0:4:50000 A:75/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if player.exp < PY_MAX_EXP then L:USE: local ee = (player.exp / 2) + 10 L:USE: if ee > 100000 then ee = 100000 end L:USE: msgf("You feel more experienced.") L:USE: gain_exp(ee) L:USE: end L:USE: ident = TRUE D:This potion increases your experience by 50%, with a maximum D:gain of 100,000 experience points. N:245:Sleep G:!:d I:75:11:100 W:0:0:4:0 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("You fall asleep."); do_dream(); inc_paralyzed(rand_range(4, 8)) L:USE: ident = TRUE L:SMASH:project(who, 2, x, y, 0, GF_OLD_SLEEP, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:This potion puts you to sleep. If it is thrown and D:shatters, it puts monsters in the splash area to sleep. N:246:Blindness G:!:d I:75:7:0 W:0:0:4:0 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = not player_res(TR1_RES_BLIND) and inc_blind(rand_range(100, 200)) L:SMASH:project(who, 2, x, y, 0, GF_DARK, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:This potion causes temporary blindness. N:247:Booze G:!:d I:75:9:50 W:0:0:4:1 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = not player_res(TR1_RES_CONF) and inc_confused(rand_range(15, 35)) L:USE: if not player_res(TR1_RES_CHAOS) and (one_in_(2) ~= 0) and L:USE: inc_image(rand_range(150, 300)) then ident = TRUE end L:USE: if not player_res(TR1_RES_CHAOS) and (one_in_(13) ~= 0) then L:USE: ident = TRUE L:USE: if player.depth > 0 then L:USE: if (one_in_(3) ~= 0) then lose_all_info() else wiz_dark() end L:USE: teleport_player(100) L:USE: wiz_dark() L:USE: else L:USE: teleport_player(250) L:USE: end L:USE: msgf("You wake up somewhere with a sore head...") L:USE: msgf("You can't remember a thing, or how you got here!") L:USE: end L:SMASH:project(who, 2, x, y, 0, GF_OLD_CONF, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:A drunken alchemist accidentally poured ale into a potion mix. D:It causes temporary confusion, and might also have other effects. N:248:Poison G:!:d I:75:6:0 W:5:0:4:5 A:5/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = res_pois_lvl() ~= 0 and inc_poisoned(rand_range(10, 25)) L:SMASH:project(who, 2, x, y, damroll(3, 6), GF_POIS, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:This potion poisons you if you drink it. If it is thrown and shatters, D:it deals 3d6 damage to monsters caught in the splash area. N:249:Speed G:!:d I:75:29:0 W:2:0:4:100 A:2/16:65/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_fast(rand_range(15, 40)) L:SMASH:project(who, 2, x, y, 0, GF_OLD_SPEED, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion increases your speed for 15-40 turns. N:250:Slowness G:!:d I:75:4:50 W:1:0:4:0 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_slow(rand_range(15, 40)) L:SMASH:project(who, 2, x, y, 5, GF_OLD_SLOW, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:This potion temporarily decreases your speed. N:251:Dexterity G:!:d I:75:51:0 W:40:0:4:10000 A:33/6:39/6:43/3:53/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_inc_stat(A_DEX) D:This potion permanently increases your dexterity. N:252:Restore Dexterity G:!:d I:75:45:0 W:15:0:4:300 A:15/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_res_stat(A_DEX) D:This potion cures dexterity drain. N:253:Restore Constitution G:!:d I:75:46:0 W:15:0:4:300 A:15/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_res_stat(A_CON) D:This potion cures constitution drain. N:254:Lose Memories G:!:d I:75:13:0 W:17:0:4:0 A:17/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if not player_res(TR1_HOLD_LIFE) and player.exp > 0 then L:USE: msgf("You feel your memories fade.") L:USE: lose_exp(player.exp / 4) L:USE: ident = TRUE L:USE: end L:SMASH:angry = TRUE D:This potion drains your experience. N:255:Salt Water G:!:d I:75:5:0 W:0:0:4:1 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("The potion makes you vomit!") L:USE: if player.food > PY_FOOD_STARVE - 1 then set_food(PY_FOOD_STARVE - 1) end L:USE: clear_poisoned() L:USE: inc_paralyzed(4) L:USE: ident = TRUE L:SMASH:return TRUE,FALSE D:This alchemical concoction is used to treat poisoning victims by D:making them vomit. N:256:Enlightenment G:!:d I:75:56:0 W:43:0:4:800 A:43/1:75/1:105/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("An image of your surroundings forms in your mind...") L:USE: wiz_lite() L:USE: ident = TRUE D:This potion grants magical knowledge of the current dungeon level. N:257:Heroism G:!:d I:75:32:0 W:1:0:4:25 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = clear_afraid() L:USE: if inc_hero(rand_range(25, 50)) then ident = TRUE end L:USE: if hp_player(10) then ident = TRUE end D:This potion makes you more heroic for 25-50 turns, increasing your D:weapon skill. N:258:Berserk Strength G:!:d I:75:33:0 W:5:0:4:100 A:5/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = clear_afraid() L:USE: if inc_shero(rand_range(25, 50)) then ident = TRUE end L:USE: if hp_player(30) then ident = TRUE end D:This potion makes you go berserk for 25-50 turns, increasing your D:weapon skill but making you easier to hit. It also restores D:30 hit points. N:259:Boldness G:!:d I:75:28:0 W:1:0:4:10 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = clear_afraid() D:This potion cures temporary fear. N:260:Restore Life Levels G:!:d I:75:41:0 W:45:0:4:1500 A:45/1:60/1:80/1:100/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = restore_level() D:This potion cures experience drain. N:261:Resist Heat G:!:d I:75:30:0 W:4:0:4:30 A:4/5:15/1:30/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_oppose_fire(rand_range(10, 20)) D:This potion grants temporary resistance to fire (10-20 turns). D:This resistance stacks with that given by items. N:262:Resist Cold G:!:d I:75:31:0 W:4:0:4:30 A:4/5:15/1:30/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_oppose_cold(rand_range(10, 20)) D:This potion grants temporary resistance to cold (10-20 turns). D:This resistance stacks with that given by items. N:263:Detect Invisible G:!:d I:75:25:0 W:3:0:4:50 A:5/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_tim_invis(rand_range(12, 24)) D:This potion allows you to see invisible monsters for 12-24 turns. N:264:Slow Poison G:!:d I:75:26:0 W:1:0:4:20 A:1/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_poisoned(-player.tim.poisoned / 2) D:This potion reduces the effect of poison. N:265:Neutralize Poison G:!:d I:75:27:0 W:5:0:4:75 A:9/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = clear_poisoned() D:This potion cures poison. N:266:Restore Mana G:!:d I:75:40:0 W:43:0:4:350 A:43/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = restore_mana() L:SMASH:project(who, 1, x, y, damroll(10, 10), GF_MANA, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:The liquid in this bottle sparks with flashes of energy. D:It restores you to full mana when drunk. N:267:Infra-vision G:!:d I:75:24:0 W:5:0:4:20 A:5/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = inc_tim_infra(rand_range(100, 200)) D:This potion grants infravision of 30' for 100-200 turns. N:268:Resistance G:!:d I:75:60:100 W:35:0:4:250 A:35/1:55/1:85/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW L:USE: inc_oppose_acid(rand_range(20, 40)) L:USE: inc_oppose_elec(rand_range(20, 40)) L:USE: inc_oppose_fire(rand_range(20, 40)) L:USE: inc_oppose_cold(rand_range(20, 40)) L:USE: inc_oppose_pois(rand_range(20, 40)) L:USE: ident = TRUE D:This potion grants resistance to acid, lightning, fire, cold, D:and poison for 20-40 turns. This resistance stacks with that D:granted by items. ##### Wands ##### N:269:Light G:-:d I:65:7:17 W:3:0:10:200 A:5/1 P:0:1d1:0:0:0 L:USE: msgf("A line of blue shimmering light appears.") L:USE: lite_line(dir, damroll(6, 8)) L:USE: ident = TRUE D:This wand creates a line of light that deals 6d8 damage to D:susceptible monsters. N:270:Tame Monster G:-:d I:65:17:9 W:52:0:10:1000 A:52/2 P:0:1d1:0:0:0 L:USE: ident = charm_monster(dir, 45) D:This wand charms a monster, making it a pet. N:271:Frost Bolts G:-:d I:65:19:12 W:35:0:10:400 A:23/2:35/1:50/1 P:0:1d1:0:0:0 L:USE: ident = fire_bolt_or_beam(20, GF_COLD, dir, damroll(6, 8)) D:This wand fires a frost bolt (6d8). N:272:Fire Bolts G:-:d I:65:18:15 W:50:0:10:500 A:26/2:52/1:67/1 P:0:1d1:0:0:0 L:USE: ident = fire_bolt_or_beam(20, GF_FIRE, dir, damroll(10, 8)) D:This wand fires a fire bolt (10d8). # More common now... N:273:Stone to Mud G:-:d I:65:6:12 W:10:0:10:300 A:17/1:65/1 P:0:1d1:0:0:0 L:USE: ident = wall_to_mud(dir) D:This wand turns rock into mud, destroying it. N:274:Polymorph G:-:d I:65:13:15 W:35:0:10:300 A:35/2 P:0:1d1:0:0:0 L:USE: ident = poly_monster(dir) D:This wand polymorphs a monster into a new random monster. D:It won't affect unique monsters. N:275:Heal Monster G:-:d I:65:0:29 W:2:0:10:50 A:4/2 P:0:1d1:0:0:0 L:USE: ident = heal_monster(dir) D:This wand heals 4d6 hitpoints to a monster. N:276:Haste Monster G:-:d I:65:1:29 W:2:0:10:50 A:4/2 P:0:1d1:0:0:0 L:USE: ident = speed_monster(dir) D:This wand permanently increases a monster's speed. D:It won't work on a monster that's already hasted. N:277:Slow Monster G:-:d I:65:9:17 W:4:0:10:200 A:4/2 P:0:1d1:0:0:0 L:USE: ident = slow_monster(dir) D:This wand permanently decreases a monster's speed. N:278:Confuse Monster G:-:d I:65:10:19 W:5:0:10:250 A:5/2 P:0:1d1:0:0:0 L:USE: ident = confuse_monster(dir, 20) D:This wand confuses a monster. N:279:Sleep Monster G:-:d I:65:8:24 W:5:0:10:250 A:9/2 P:0:1d1:0:0:0 L:USE: ident = sleep_monster(dir) D:This wand puts a monster to sleep. N:280:Drain Life G:-:d I:65:12:7 W:75:0:10:1200 A:60/1 P:0:1d1:0:0:0 L:USE: ident = drain_life(dir, 150) D:This wand drains life from a monster, dealing 150 damage. N:281:Trap/Door Destruction G:-:d I:65:5:15 W:17:0:10:100 A:17/2 P:0:1d1:0:0:0 L:USE: ident = destroy_door(dir) D:This wand destroys traps and doors in a straight line. N:282:Magic Missile G:-:d I:65:15:17 W:2:0:10:100 A:4/1 P:0:1d1:0:0:0 L:USE: ident = fire_bolt_or_beam(20, GF_MISSILE, dir, damroll(2, 6)) D:This wand deals 2d6 unresistable damage. N:283:Clone Monster G:-:d I:65:2:9 W:15:0:10:0 A:26/1:75/2 P:0:1d1:0:0:0 L:USE: ident = clone_monster(dir) D:This wand creates a duplicate of a monster. N:284:Scare Monster G:-:d I:65:11:9 W:17:0:10:300 A:17/4 P:0:1d1:0:0:0 L:USE: ident = fear_monster(dir, 20) D:This wand frightens a monster. N:285:Teleport Other G:-:d I:65:3:12 W:35:0:10:350 A:35/1 P:0:1d1:0:0:0 L:USE: ident = teleport_monster(dir) D:This wand teleports away all monsters caught in the beam. D:Teleported monsters reappear elsewhere on the same level. N:286:Disarming G:-:d I:65:4:10 W:35:0:10:300 A:35/1 P:0:1d1:0:0:0 L:USE: ident = disarm_trap(dir) D:This wand disarms traps in a straight line. N:287:Lightning Balls G:-:d I:65:21:13 W:41:0:10:1200 A:41/1:61/1 P:0:1d1:0:0:0 F:IGNORE_ELEC L:USE: ident = fire_ball(GF_ELEC, dir, 75, 2) D:This wand fires a lightning ball (75). N:288:Cold Balls G:-:d I:65:23:9 W:45:0:10:1500 A:45/1:65/1 P:0:1d1:0:0:0 F:IGNORE_COLD L:USE: ident = fire_ball(GF_COLD, dir, 100, 2) D:This wand fires a cold ball (100). N:289:Fire Balls G:-:d I:65:22:7 W:55:0:10:1800 A:55/1:75/1 P:0:1d1:0:0:0 F:IGNORE_FIRE L:USE: ident = fire_ball(GF_FIRE, dir, 150, 2) D:This wand fires a fire ball (150). N:290:Stinking Cloud G:-:d I:65:14:15 W:5:0:10:200 A:9/1 P:0:1d1:0:0:0 L:USE: ident = fire_ball(GF_POIS, dir, 15, 2) D:This wand fires a poison ball (15). N:291:Acid Balls G:-:d I:65:20:8 W:55:0:10:1650 A:55/1:75/1 P:0:1d1:0:0:0 F:IGNORE_ACID L:USE: ident = fire_ball(GF_ACID, dir, 125, 2) D:This wand fires an acid ball (125). N:292:Wonder G:-:d I:65:24:24 W:3:0:10:150 A:5/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: wand_of_wonder(dir) L:USE: ident = TRUE D:This wand has a random effect, which may be helpful or harmful. ##### Extra ammunition ##### N:293:& Flight Arrow~ G:{:y I:17:0:0 W:5:0:1:1 A:5/2:10/2 P:0:1d8:0:0:0 F:SHOW_MODS D:Yes, it is an arrow. Surely you can do better than this twig. ##### Additional wands ##### N:294:Acid Bolts G:-:d I:65:16:15 W:40:0:10:950 A:35/2:52/1:62/1 P:0:1d1:0:0:0 L:USE: ident = fire_bolt_or_beam(20, GF_ACID, dir, damroll(6, 8)) D:This wand fires an acid bolt (6d8). N:295:Dragon's Flame G:-:d I:65:26:5 W:75:0:10:5000 A:75/4:100/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: ident = fire_ball(GF_FIRE, dir, 250, 3) D:This wand fires a large fire ball (250). N:296:Dragon's Frost G:-:d I:65:27:5 W:75:0:10:5000 A:75/4:100/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: ident = fire_ball(GF_COLD, dir, 200, 3) D:This wand fires a large cold ball (200). N:297:Dragon's Breath G:-:d I:65:28:5 W:75:0:10:6000 A:75/4:100/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local choice = randint1(5) L:USE: if choice == 1 then ident = fire_ball(GF_ACID, dir, 250, 3) L:USE: elseif choice == 2 then ident = fire_ball(GF_ELEC, dir, 150, 3) L:USE: elseif choice == 3 then ident = fire_ball(GF_FIRE, dir, 200, 3) L:USE: elseif choice == 4 then ident = fire_ball(GF_COLD, dir, 200, 3) L:USE: else ident = fire_ball(GF_POIS, dir, 200, 3) L:USE: end D:This wand fires a large ball of a random element (150 to 250). N:298:Annihilation G:-:d I:65:25:4 W:85:0:10:8000 A:85/4 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: ident = fire_ball(GF_DISINTEGRATE, dir, rand_range(125, 225), 2) D:This wand fires a disintegration ball (125-225). N:299:Rockets G:-:d I:65:29:4 W:95:0:10:20000 A:95/4 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: msgf("You launch a rocket!"); fire_ball(GF_ROCKET, dir, 250, 2) L:USE: ident = TRUE D:This wand fires a rocket (250). ##### Staffs ##### N:300:Trap Location G:_:d I:55:12:12 W:5:0:50:50 A:5/1 P:0:1d2:0:0:0 L:USE: ident = detect_traps(ident) D:This staff magically detects nearby traps. N:301:Treasure Location G:_:d I:55:10:29 W:5:0:50:50 A:9/1 P:0:1d2:0:0:0 L:USE: ident = detect_treasure() L:USE: if detect_objects_gold() then ident = TRUE end D:This staff magically detects nearby gems and precious metals. N:302:Object Location G:_:d I:55:11:22 W:5:0:50:100 A:9/2 P:0:1d2:0:0:0 L:USE: ident = detect_objects_normal() D:This staff magically detects nearby objects. N:303:Teleportation G:_:d I:55:4:10 W:35:0:50:2000 A:35/1 P:0:1d2:0:0:0 L:USE: teleport_player(100) L:USE: ident = TRUE D:This staff teleports you up to 1000'. N:304:Earthquakes G:_:d I:55:28:9 W:65:0:50:350 A:65/1 P:0:1d2:0:0:0 L:USE: if not earthquake(player.px, player.py, 10) then L:USE: msgf("The dungeon trembles...") end L:USE: ident = TRUE D:This staff creates an earthquake which collapses part of the dungeon. N:305:Summoning G:_:d I:55:3:5 W:10:0:50:0 A:17/4:55/1 P:0:1d2:0:0:0 L:USE: ident = summon_monsters(randint1(4), 0) D:This staff summons several monsters to your location. D:The monsters summoned will not be pleased. N:306:Light G:_:d I:55:8:29 W:5:0:50:250 A:7/1 P:0:1d2:0:0:0 L:USE: ident = lite_area(damroll(2, 8), 2) D:This staff lights the room you are in and deals some damage to nearby D:monsters that are vulnerable to light. N:307:*Destruction* G:_:d I:55:29:5 W:80:0:50:5000 A:80/1:100/1 P:0:1d2:0:0:0 L:USE: ident = destroy_area(player.px, player.py, 15) D:This staff destroys the nearby dungeon, turning it into a jumble D:of blasted rock. N:308:Starlight G:_:d I:55:7:12 W:15:0:50:500 A:15/2 P:0:1d2:0:0:0 L:USE: if player.tim.blind == 0 then L:USE: msgf("The end of the staff glows brightly...") L:USE: end L:USE: starlite() L:USE: ident = TRUE D:This staff shoots several rays of light in random directions. D:Each ray deals 6d8 damage to vulnerable monsters caught in the beam. N:309:Haste Monsters G:_:d I:55:2:17 W:10:0:50:0 A:17/2 P:0:1d2:0:0:0 L:USE: ident = speed_monsters() D:This staff permanently increases the speed of all nearby monsters. D:Monsters that are already hasted aren't affected. N:310:Slow Monsters G:_:d I:55:21:12 W:17:0:50:400 A:17/2 P:0:1d2:0:0:0 L:USE: ident = slow_monsters() D:This staff permanently decreases the speed of all nearby monsters. D:Monsters that are already slowed aren't affected. N:311:Sleep Monsters G:_:d I:55:20:12 W:15:0:50:400 A:15/2 P:0:1d2:0:0:0 L:USE: ident = sleep_monsters() D:This staff puts all nearby monsters to sleep. N:312:Cure Light Wounds G:_:d I:55:16:12 W:5:0:50:400 A:9/2 P:0:1d2:0:0:0 L:USE: ident = hp_player(50) D:This staff restores 50 hit points. N:313:Detect Invisible G:_:d I:55:14:24 W:5:0:50:200 A:9/2 P:0:1d2:0:0:0 L:USE: ident = detect_monsters_invis() D:This staff magically detects nearby invisible monsters. N:314:Speed G:_:d I:55:22:8 W:50:0:50:5000 A:50/1 P:0:1d2:0:0:0 L:USE: ident = inc_fast(rand_range(15, 45)) D:This staff increases your speed for 15-45 turns. N:315:Slowness G:_:d I:55:1:17 W:30:0:50:0 A:50/4 P:0:1d2:0:0:0 L:USE: ident = inc_slow(rand_range(15, 45)) D:This staff temporarily decreases your speed. N:316:Door/Stair Location G:_:d I:55:13:15 W:9:0:50:150 A:9/2 P:0:1d2:0:0:0 L:USE: ident = detect_doors() L:USE: if detect_stairs() then ident = TRUE end D:This staff magically detects nearby doors and stairs. N:317:Remove Curse G:_:d I:55:6:8 W:55:0:50:200 A:55/4 P:0:1d2:0:0:0 L:USE: ident = remove_curse() L:USE: if ident and player.tim.blind == 0 then L:USE: msgf("The staff glows blue for a moment...") L:USE: end D:This staff removes curses from equipment, allowing you to take D:it off. It might not affect powerful curses. N:318:Detect Evil G:_:d I:55:15:24 W:15:0:50:150 A:15/4 P:0:1d2:0:0:0 L:USE: ident = detect_monsters_evil() D:This staff magically detects nearby evil monsters. N:319:Curing G:_:d I:55:17:8 W:33:0:50:500 A:33/4 P:0:1d2:0:0:0 L:USE: ident = cure_potion(150, TRUE, TRUE, TRUE) L:USE: if clear_image() then ident = TRUE end D:This staff cures blindness, confusion, poison, cuts, and D:hallucination, and restores 150 hit points. N:320:Dispel Evil G:_:d I:55:24:8 W:75:0:50:2000 A:75/1 P:0:1d2:0:0:0 L:USE: ident = dispel_evil(60) D:This staff deals 60 damage to all evil monsters nearby. N:321:Probing G:_:d I:55:23:9 W:20:0:50:100 A:20/4 P:0:1d2:0:0:0 L:USE: ident = probing() D:This staff reveals information about nearby monsters. N:322:Darkness G:_:d I:55:0:17 W:5:0:50:50 A:9/1 P:0:1d2:0:0:0 L:USE: ident = not player_res(TR1_RES_BLIND) and not player_res(TR1_RES_DARK) L:USE: and inc_blind(rand_range(4, 8)) L:USE: if unlite_area(10, 3) then ident = TRUE end D:This staff creates a sphere of magical darkness. It also causes D:temporary blindness. N:323:Genocide G:_:d I:55:27:4 W:95:0:50:8000 A:95/4 P:0:1d2:0:0:0 L:USE: genocide(TRUE) L:USE: ident = TRUE D:This staff completely destroys all monsters on the current dungeon level D:of a single letter you select. You don't get experience or treasure D:from those monsters. N:324:Power G:_:d I:55:25:5 W:95:0:50:10000 A:95/2 P:0:1d2:0:0:0 L:USE: dispel_monsters(300) L:USE: ident = TRUE D:This staff deals 300 damage to all nearby monsters. N:325:the Magi G:_:d I:55:19:5 W:100:0:50:15000 A:100/2 P:0:1d2:0:0:0 L:USE: ident = do_res_stat(A_INT) L:USE: if restore_mana() then ident = TRUE end D:This staff restores you to full mana. It also cures intelligence drain. N:326:Perception G:_:d I:55:5:21 W:17:0:50:400 A:17/1:35/1:60/1 P:0:1d2:0:0:0 L:USE: result = ident_spell() L:USE: ident = TRUE D:This staff identifies an item. N:327:Holiness G:_:d I:55:26:5 W:100:0:50:10000 A:100/2 P:0:1d2:0:0:0 L:USE: if dispel_evil(300) then ident = TRUE end L:USE: if inc_protevil(randint1(25) + 3 * player.lev) then ident = TRUE end L:USE: if clear_poisoned() then ident = TRUE end L:USE: if clear_afraid() then ident = TRUE end L:USE: if hp_player(50) then ident = TRUE end L:USE: if clear_stun() then ident = TRUE end L:USE: if clear_cut() then ident = TRUE end D:This staff has many beneficial effects. It deals 300 damage to nearby D:evil monsters; grants temporary protection from evil; cures poison, fear, D:stunning, and cuts; and restores 50 hit points. N:328:Enlightenment G:_:d I:55:9:11 W:22:0:50:750 A:22/1 P:0:1d2:0:0:0 L:USE: map_area() L:USE: ident = TRUE D:This staff magically reveals the nearby dungeon. N:329:Healing G:_:d I:55:18:4 W:65:0:50:5000 A:65/2 P:0:1d2:0:0:0 L:USE: ident = hp_player(300) L:USE: if clear_stun() then ident = TRUE end L:USE: if clear_cut() then ident = TRUE end D:This staff cures stunning and cuts and restores 300 hit points. ##### Basic Books ##### N:330:[Book of Common Prayer] G:?:W I:90:0:0 W:17:0:30:100 A:17/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A small, light-gray book, containing the simplest of good prayers D:to lighten the way. N:331:[High Mass] G:?:W I:90:1:0 W:20:0:30:1000 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A white book with invocations of healing, protection and D:fighting evil. N:332:[Book of the Unicorn] G:?:w I:90:2:0 W:65:0:30:25000 A:65/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A white grimoire with great spells of banishing evil. N:333:[Blessings of the Grail] G:?:w I:90:3:0 W:95:0:30:100000 A:95/3 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A blindingly white tome, shining with the awesome power of Life. N:334:[Beginner's Handbook] G:?:B I:91:0:0 W:17:0:30:100 A:17/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A small light-blue book with simple sorcerous spells of detection, D:teleportation and clouding the minds of your foes. N:335:[Master Sorcerer's Handbook] G:?:B I:91:1:0 W:20:0:30:1000 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A blue book containing sorcerous spells of knowledge, speed, D:and hindering your enemies. N:336:[Pattern Sorcery] G:?:b I:91:2:0 W:65:0:30:25000 A:65/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A deep-blue tome, containing great magics of mind over matter. N:337:[Grimoire of Power] G:?:b I:91:3:0 W:95:0:30:100000 A:95/3 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A mighty grimoire glowing with eldritch blue from the magics D:it contains. ##### Chests ##### N:338:& Small wooden chest~ G:&:o I:7:1:0 W:9:0:250:20 A:9/1 P:0:2d3:0:0:0 D:It just may contain some gold and items. Open it and find out. N:339:& Large wooden chest~ G:&:U I:7:5:0 W:26:0:500:200 A:26/1 P:0:2d5:0:0:0 D:Maybe there still is something in it. Open it and find out. N:340:& Small iron chest~ G:&:D I:7:2:0 W:43:0:300:200 A:43/1 P:0:2d4:0:0:0 D:It just may contain some gold and items. Open it and find out. N:341:& Large iron chest~ G:&:s I:7:6:0 W:61:0:1000:500 A:61/1 P:0:2d6:0:0:0 D:Maybe there still is something in it. Open it and find out. N:342:& Small steel chest~ G:&:s I:7:3:0 W:70:0:500:500 A:70/1 P:0:2d4:0:0:0 D:It just may contain some gold and items. Open it and find out. N:343:& Large steel chest~ G:&:W I:7:7:0 W:80:0:1000:1000 A:80/1 P:0:2d6:0:0:0 D:Maybe there still is something in it. Open it and find out. N:344:& Ruined chest~ G:&:u I:7:0:0 W:30:0:250:0 A:30/10:90/1 D:A small chest of rotted wood. Whatever it once held has already been taken. ##### Various Stuff ##### N:345:& Iron Spike~ G:~:W I:5:0:0 W:1:0:2:1 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A small metal spike, useful for jamming doors. N:346:& Wooden Torch~ G:~:u I:39:0:4000 W:1:0:30:2 A:1/1:10/1 P:0:1d1:0:0:0 F:EASY_KNOW D:With its light you can ward off the dark. N:347:& Brass Lantern~ G:~:U I:39:1:7500 W:5:0:50:150 A:5/1:15/1:25/1 P:0:1d1:0:0:0 F:EASY_KNOW | IGNORE_FIRE D:If it has oil in it a lantern burns more brightly than a torch. N:348:& Flask~ of oil G:!:y I:77:0:7500 W:1:0:10:3 A:1/1:7/1:15/1 P:0:2d6:0:0:0 F:EASY_KNOW D:Empty this flask in a lantern to keep it fueled. D:Or throw it at a monster which doesn't like fire. N:349:& Empty Bottle~ G:!:w I:2:1:0 W:0:0:2:1 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW D:It is empty, very empty indeed. ##### Here are the Rods ##### N:350:Havoc G:-:d I:66:28:250 W:95:0:15:30000 A:75/8:100/16 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: call_chaos(); ident = TRUE D:This rod calls forth a random attack. N:351:Door/Stair Location G:-:d I:66:1:70 W:12:0:15:250 A:12/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if detect_doors() then ident = TRUE end L:USE: if detect_stairs() then ident = TRUE end D:This rod magically detects nearby doors and stairs. N:352:Trap Location G:-:d I:66:0:15 W:10:0:15:100 A:10/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = detect_traps(ident) D:This rod magically detects nearby traps. N:353:Probing G:-:d I:66:7:50 W:25:0:15:200 A:25/8 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = probing() D:This rod reveals information about nearby monsters. N:354:Recall G:-:d I:66:3:60 W:40:0:15:4000 A:26/8:52/4 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: word_of_recall(); ident = TRUE D:This rod has two effects. If used in the dungeon, it takes you to D:the surface. If used near a dungeon entrance, it takes you to the depth D:you last recalled out from. N:355:Illumination G:-:d I:66:4:16 W:15:0:15:250 A:15/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = lite_area(damroll(4, 8), 2) D:This rod lights the room you are in and deals damage to nearby monsters D:that are vulnerable to light. N:356:Light G:-:d I:66:15:9 W:13:0:15:600 A:9/2:17/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("A line of blue shimmering light appears.") L:USE: lite_line(dir, damroll(6, 8)); ident = TRUE D:This rod creates a line of light that deals 6d8 damage to vulnerable D:monsters. N:357:Lightning Bolts G:-:d I:66:21:11 W:35:0:15:2000 A:30/2:35/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_bolt_or_beam(10, GF_ELEC, dir, damroll(5, 8)) D:This rod fires a lightning bolt (5d8). N:358:Frost Bolts G:-:d I:66:23:13 W:43:0:15:3000 A:33/2:43/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_bolt_or_beam(10, GF_COLD, dir, damroll(6, 8)) D:This rod fires a frost bolt (6d8). N:359:Fire Bolts G:-:d I:66:22:15 W:52:0:15:3500 A:36/2:52/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_bolt_or_beam(10, GF_FIRE, dir, damroll(10, 8)) D:This rod fires a fire bolt (10d8). N:360:Polymorph G:-:d I:66:19:25 W:32:0:15:600 A:28/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if poly_monster(dir) then ident = TRUE end D:This rod transforms a monster into another random monster. D:It doesn't work on uniques. N:361:Slow Monster G:-:d I:66:17:20 W:26:0:15:1000 A:26/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if slow_monster(dir) then ident = TRUE end D:This rod permanently reduces a monster's speed. It doesn't work D:on a monster that's already slowed. N:362:Sleep Monster G:-:d I:66:16:18 W:26:0:15:1500 A:26/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if sleep_monster(dir) then ident = TRUE end D:This rod puts a monster to sleep. N:363:Drain Life G:-:d I:66:18:23 W:67:0:15:10000 A:67/8:95/4 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if drain_life(dir, 150) then ident = TRUE end D:This rod drains life from a monster, dealing 150 damage. N:364:Teleport Other G:-:d I:66:13:3 W:40:0:15:2000 A:40/4:70/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if teleport_monster(dir) then ident = TRUE end D:This rod teleports monsters away in a straight line. Teleported D:monsters appear elsewhere on the same level. N:365:Disarming G:-:d I:66:14:22 W:32:0:15:500 A:32/2:61/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if disarm_trap(dir) then ident = TRUE end D:This rod disarms traps in a straight line. N:366:Lightning Balls G:-:d I:66:25:23 W:49:0:15:5000 A:49/2:80/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_ball(GF_ELEC, dir, 75, 2) D:This rod fires a lightning ball (75). N:367:Cold Balls G:-:d I:66:27:25 W:85:0:15:6000 A:52/2:85/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_ball(GF_COLD, dir, 100, 2) D:This rod fires a cold ball (100). N:368:Fire Balls G:-:d I:66:26:30 W:95:0:15:7000 A:67/2:95/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_ball(GF_FIRE, dir, 150, 2) D:This rod fires a fire ball (150). N:369:Acid Balls G:-:d I:66:24:27 W:95:0:15:6000 A:61/2:95/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_ball(GF_ACID, dir, 125, 2) D:This rod fires an acid ball (125). N:370:Acid Bolts G:-:d I:66:20:12 W:75:0:15:3000 A:45/2:65/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8)) D:This rod fires an acid bolt (6d8). N:371:Enlightenment G:-:d I:66:5:99 W:70:0:15:8000 A:40/8:80/4 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: map_area(); ident = TRUE D:This rod magically reveals the nearby dungeon. N:372:Perception G:-:d I:66:2:10 W:75:0:15:13000 A:43/16:75/8 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if not ident_spell() then use_charge = FALSE end; ident = TRUE D:This rod identifies one object. N:373:Curing G:-:d I:66:8:999 W:80:0:15:4000 A:58/8:90/4 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_curing() D:This rod cures blindness, confusion, poison, cuts, and hallucination, D:and restores 200 hit points. N:374:Healing G:-:d I:66:9:999 W:100:0:15:20000 A:65/16:100/8 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if hp_player(500) then ident = TRUE end L:USE: if clear_stun() then ident = TRUE end L:USE: if clear_cut() then ident = TRUE end D:This rod cures stunning and cuts and restores 500 hit points. N:375:Detection G:-:d I:66:6:99 W:52:0:15:5000 A:26/16:52/8 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if detect_all() then ident = TRUE end D:This rod magically detects nearby doors, traps, stairs, items, and D:monsters. N:376:Restoration G:-:d I:66:10:999 W:100:0:15:25000 A:65/32:100/16 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if restore_level() then ident = TRUE end L:USE: if restore_all_stats() then ident = TRUE end D:This rod cures all stat and experience drains. N:377:Speed G:-:d I:66:11:99 W:105:0:15:50000 A:78/32:105/16 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: if inc_fast(rand_range(15, 45)) then ident = TRUE end D:This rod increases your speed for 15-45 turns. N:378:Pesticide G:-:d I:66:12:3 W:23:0:15:500 A:23/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = fire_ball(GF_POIS, dir, 8, 3) D:This rod fires a poison ball (8). ##### Special Books (note resistances) ##### N:379:[Call of the Wild] G:?:G I:92:0:0 W:17:0:30:100 A:17/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A small, light-green book with nature rites of detection, curing, D:and other aid. N:380:[Nature Mastery] G:?:G I:92:1:0 W:20:0:30:1000 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A green book with rites showing both the power and enlightenment D:of nature's magics. N:381:[Nature's Gifts] G:?:g I:92:2:0 W:75:0:30:25000 A:75/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A sheaf of papers in deep-green leather bindings, filled with D:powerful spells of nature's protection. N:382:[Nature's Wrath] G:?:g I:92:3:0 W:100:0:30:100000 A:100/2 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A mottled-green grimoire, vibrating with the power of nature's D:destructive magics. N:383:[Sign of Chaos] G:?:R I:93:0:0 W:17:0:30:100 A:17/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A light-red book, containing minor chaotic offensive magics. N:384:[Chaos Mastery] G:?:R I:93:1:0 W:20:0:30:1000 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A red book with many spells of causing destruction and chaos. N:385:[Chaos Channels] G:?:r I:93:2:0 W:75:0:30:25000 A:75/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A deep-red codex, filled with ways of manifesting chaos upon this D:reality or even twisting life and reality itself. N:386:[Armageddon Tome] G:?:r I:93:3:0 W:105:0:30:100000 A:105/3 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A fiery-red tome that sizzles with power, barely holding in check D:the most destructive magics imaginable. N:387:[Black Prayers] G:?:D I:94:0:0 W:17:0:30:100 A:17/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A small dark-gray book, containing minor curses and ways of hurting. N:388:[Black Mass] G:?:D I:94:1:0 W:20:0:30:1000 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A black book with many evil incantations and spells of pain, terror, D:and death. ##### Junk ##### N:389:& Shard~ of Pottery G:~:r I:3:3:0 W:0:0:5:0 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW D:Not enough to assemble a pot. N:390:& Broken Stick~ G:~:r I:3:6:0 W:0:0:3:0 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW D:Not even big enough for a dog to fetch. ##### Skeletons ##### N:391:& Broken Skull~ G:~:w I:1:1:0 W:0:0:1:0 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW D:Something tampled onthis skull. N:392:& Broken Bone~ G:~:w I:1:2:0 W:0:0:2:0 A:0/1 P:0:1d1:0:0:0 F:EASY_KNOW D:Something sucked out the marrow. N:393:& Canine Skeleton~ G:~:w I:1:4:0 W:1:0:10:0 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW D:No more sun for this doggie. N:394:& Rodent Skeleton~ G:~:w I:1:3:0 W:1:0:10:0 A:1/1 P:0:1d1:0:0:0 F:EASY_KNOW D:You can find plenty of these. N:395:& Human Skeleton~ G:~:w I:1:8:0 W:9:0:60:0 A:9/1 P:0:1d2:0:0:0 F:EASY_KNOW D:The remains of an earlier adventurer, perhaps? N:396:& Dwarf Skeleton~ G:~:w I:1:7:0 W:9:0:50:0 A:9/1 P:0:1d2:0:0:0 F:EASY_KNOW D:The remains of an earlier adventurer, perhaps? N:397:& Elf Skeleton~ G:~:w I:1:6:0 W:9:0:40:0 A:9/1 P:0:1d2:0:0:0 F:EASY_KNOW D:The remains of an earlier adventurer, perhaps? N:398:& Gnome Skeleton~ G:~:w I:1:5:0 W:9:0:30:0 A:9/1 P:0:1d2:0:0:0 F:EASY_KNOW D:The remains of an earlier adventurer, perhaps? ##### Additional weapon ##### N:399:& Great Hammer~ G:\:D I:21:19:0 W:70:0:300:1350 A:70/1:105/1 P:0:4d6:0:0:0 F:SHOW_MODS ##### Dragon Scale Mail ##### N:400:& Black Dragon Scale Mail~ G:[:s I:38:1:0 W:60:0:200:17500 A:60/8 P:30:2d4:-2:0:10 F:RES_ACID | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe acid.") L:USE: fire_ball(GF_ACID, dir, 430, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe acid (430) every 50-100 turns" N:401:& Blue Dragon Scale Mail~ G:[:b I:38:2:0 W:40:0:200:15000 A:40/8 P:30:2d4:-2:0:10 F:RES_ELEC | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe lightning.") L:USE: fire_ball(GF_ELEC, dir, 330, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe lightning (330) every 50-100 turns" N:402:& White Dragon Scale Mail~ G:[:w I:38:3:0 W:50:0:200:20000 A:50/8 P:30:2d4:-2:0:10 F:RES_COLD | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe frost.") L:USE: fire_ball(GF_COLD, dir, 370, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe frost (370) every 50-100 turns" N:403:& Red Dragon Scale Mail~ G:[:r I:38:4:0 W:80:0:200:50000 A:80/8 P:30:2d4:-2:0:10 F:RES_FIRE | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe fire.") L:USE: fire_ball(GF_FIRE, dir, 670, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe fire (670) every 50-100 turns" N:404:& Green Dragon Scale Mail~ G:[:g I:38:5:0 W:70:0:200:40000 A:70/8 P:30:2d4:-2:0:10 F:RES_POIS | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe poison gas.") L:USE: fire_ball(GF_POIS, dir, 500, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe poison gas (500) every 50-100 turns" N:405:& Multi-Hued Dragon Scale Mail~ G:[:v I:38:6:0 W:100:0:200:75000 A:100/16 P:30:2d4:-2:0:10 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: local chance = randint1(5) L:USE: local damtype; local damname L:USE: if chance == 1 then damtype = GF_ELEC; damname = "lightning" L:USE: elseif chance == 2 then damtype = GF_COLD; damname = "frost" L:USE: elseif chance == 3 then damtype = GF_ACID; damname = "acid" L:USE: elseif chance == 4 then damtype = GF_FIRE; damname = "fire" L:USE: else damtype = GF_POIS; damname = "poison gas" L:USE: end L:USE: msgf("You breathe "..damname..".") L:USE: fire_ball(damtype, dir, 840, 2) L:USE: object.timeout = rand_range(25, 50) L:DESC: return "breathe multi-hued (840) every 25-50 turns" N:406:& Pseudo Dragon Scale Mail~ G:[:v I:38:10:0 W:65:0:200:30000 A:65/16 P:30:2d4:-2:0:10 F:RES_LITE | RES_DARK | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: local chance = randint1(2) L:USE: local damtype; local damname L:USE: if chance == 1 then damtype = GF_LITE; damname = "light" L:USE: else damtype = GF_DARK; damname = "darkness" L:USE: end L:USE: fire_ball(damtype, dir, 670, 2) L:USE: object.timeout = rand_range(30, 60) L:DESC: return "breathe light/darkness (670) every 30-60 turns" N:407:& Law Dragon Scale Mail~ G:[:B I:38:12:0 W:80:0:200:40000 A:80/16 P:30:2d4:-2:0:10 F:RES_SOUND | RES_SHARDS | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: local chance = randint1(2) L:USE: local damtype; local damname L:USE: if chance == 1 then damtype = GF_SOUND; damname = "sound" L:USE: else damtype = GF_SHARDS; damname = "shards" L:USE: end L:USE: fire_ball(damtype, dir, 750, 2) L:USE: object.timeout = rand_range(30, 60) L:DESC: return "breathe sound/shards (750) every 30-60 turns" N:408:& Bronze Dragon Scale Mail~ G:[:U I:38:14:0 W:55:0:200:17500 A:55/8 P:30:2d4:-2:0:10 F:RES_CONF | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe confusion.") L:USE: fire_ball(GF_CONFUSION, dir, 400, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe confusion (400) every 50-100 turns" N:409:& Gold Dragon Scale Mail~ G:[:y I:38:16:0 W:65:0:200:20000 A:65/8 P:30:2d4:-2:0:10 F:RES_SOUND | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe sound.") L:USE: fire_ball(GF_SOUND, dir, 430, 2) L:USE: object.timeout = rand_range(50, 100) L:DESC: return "breathe sound (430) every 50-100 turns" N:410:& Chaos Dragon Scale Mail~ G:[:v I:38:18:0 W:75:0:200:35000 A:75/16 P:30:2d4:-2:0:10 F:RES_CHAOS | RES_DISEN | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: local chance = randint1(2) L:USE: local damtype; local damname L:USE: if chance == 1 then damtype = GF_CHAOS; damname = "chaos" L:USE: else damtype = GF_DISENCHANT; damname = "disenchantment" L:USE: end L:USE: fire_ball(damtype, dir, 740, 2) L:USE: object.timeout = rand_range(30, 60) L:DESC: return "breathe chaos/disenchant (740) every 30-60 turns" N:411:& Balance Dragon Scale Mail~ G:[:v I:38:20:0 W:90:0:200:50000 A:90/16 P:30:2d4:-2:0:10 F:RES_CHAOS | RES_DISEN | RES_SOUND | RES_SHARDS | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: local chance = randint1(4) L:USE: local damtype; local damname L:USE: if chance == 1 then damtype = GF_SOUND; damname = "sound" L:USE: elseif chance == 2 then damtype = GF_SHARDS; damname = "shards" L:USE: elseif chance == 3 then damtype = GF_CHAOS; damname = "chaos" L:USE: else damtype = GF_DISENCHANT; damname = "disenchantment" L:USE: end L:USE: msgf("You breathe "..damname..".") L:USE: fire_ball(damtype, dir, 840, 2) L:USE: object.timeout = rand_range(30, 60) L:DESC: return "breathe balance (840) every 30-60 turns" N:412:& Power Dragon Scale Mail~ G:[:v I:38:30:0 W:110:0:250:175000 A:110/64 P:40:2d4:-3:0:15 F:RES_ACID | RES_FIRE | RES_COLD | RES_ELEC | RES_POIS | F:RES_NETHER | RES_NEXUS | RES_CHAOS | RES_LITE | RES_DARK | F:RES_SHARDS | RES_SOUND | RES_DISEN | RES_CONF | F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:USE: local success; local dir; success, dir = get_aim_dir() L:USE: if not success then return end L:USE: msgf("You breathe the elements.") L:USE: fire_ball(GF_MISSILE, dir, 1000, 2) L:USE: object.timeout = rand_range(30, 60) L:DESC: return "breathe the elements (1000) every 30-60 turns" ## PDSM has been restored to (almost) its former glory in Zangband N:413:& Dragon Helm~ G:]:G I:32:7:0 W:80:0:50:10000 A:80/20:105/20 P:8:1d3:0:0:10 F:IGNORE_ACID | IGNORE_FIRE | IGNORE_ELEC | IGNORE_COLD D:Made from dragon bones it retains some magical properities. N:414:& Dragon Shield~ G:[:G I:34:6:0 W:80:0:100:10000 A:80/20:105/20 P:11:1d7:0:0:10 F:IGNORE_ACID | IGNORE_FIRE | IGNORE_ELEC | IGNORE_COLD D:Made from dragon bones it retains some magical properities. ##### Extra potions ##### N:415:Death G:!:d I:75:23:0 W:85:0:4:0 A:85/4 P:0:20d20:0:0:0 F:EASY_KNOW L:USE: msgf("A feeling of Death flows through your body.") L:USE: take_hit(5000, "a potion of Death") L:USE: ident = TRUE L:SMASH:project(who, 1, x, y, damroll(25, 25), GF_DEATH_RAY, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:This potion deals 5000 damage. If it is thrown and shatters, it D:deals 25d25 damage to each monster caught in the splash area. N:416:Ruination G:!:d I:75:15:0 W:70:0:4:0 A:70/8 P:0:20d20:0:0:0 F:EASY_KNOW L:USE: msgf("Your nerves and muscles feel weak and lifeless!") L:USE: take_hit(damroll(10, 10), "a potion of Ruination) L:USE: dec_stat(A_DEX, 25, TRUE) L:USE: dec_stat(A_WIS, 25, TRUE) L:USE: dec_stat(A_CON, 25, TRUE) L:USE: dec_stat(A_STR, 25, TRUE) L:USE: dec_stat(A_CHR, 25, TRUE) L:USE: dec_stat(A_INT, 25, TRUE) L:USE: ident = TRUE L:SMASH:project(who, 1, x, y, damroll(25, 25), GF_SHARDS, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:This potion deals damage and permanently drains all your stats. D:If it is thrown and shatters, it deals 25d25 damage to each monster D:caught in the splash area. N:417:Detonations G:!:d I:75:22:0 W:90:0:4:10000 A:90/8 P:0:25d25:0:0:0 F:EASY_KNOW L:USE: msgf("Massive explosions rupture your body!") L:USE: take_hit(damroll(50, 20), "a potion of Detonations") L:USE: inc_stun(75) L:USE: inc_cut(5000) L:USE: ident = TRUE L:SMASH:project(who, 1, x, y, damroll(25, 25), GF_SHARDS, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return TRUE,TRUE D:The bottle this potion is in looks very fragile. D:It deals massive damage if drunk. If it is thrown and shatters, it D:deals 25d25 damage to each monster caught in the splash area. N:418:Augmentation G:!:d I:75:55:0 W:50:0:4:20000 A:45/8 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = FALSE L:USE: if do_inc_stat(A_STR) then ident = TRUE end L:USE: if do_inc_stat(A_INT) then ident = TRUE end L:USE: if do_inc_stat(A_WIS) then ident = TRUE end L:USE: if do_inc_stat(A_DEX) then ident = TRUE end L:USE: if do_inc_stat(A_CON) then ident = TRUE end L:USE: if do_inc_stat(A_CHR) then ident = TRUE end D:This potion permanently increases all your stats. N:419:*Healing* G:!:d I:75:38:0 W:65:0:4:1500 A:65/4:75/2:85/1:95/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = cure_potion(1200, TRUE, TRUE, TRUE) L:SMASH:project(who, 2, x, y, damroll(50, 50), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures blindness, confusion, poison, and cuts, and D:restores 1200 hit points. N:420:Life G:!:d I:75:39:0 W:85:0:4:5000 A:85/4:100/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: do_life_potion() L:SMASH:project(who, 2, x, y, damroll(50, 50), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures poison, blindness, confusion, hallucination, stunning, D:cuts, and all stat and experience drains, and restores 5000 hit points. N:421:Self Knowledge G:!:d I:75:58:0 W:45:0:4:1000 A:45/20:65/3 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("You begin to know yourself a little better...") L:USE: message_flush() L:USE: self_knowledge() L:USE: ident = TRUE D:This potion reveals all magical effects on you. N:422:*Enlightenment* G:!:d I:75:57:0 W:95:0:4:20000 A:95/4 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: msgf("You begin to feel more enlightened...") L:USE: message_flush() L:USE: wiz_lite() L:USE: do_inc_stat(A_INT) L:USE: do_inc_stat(A_WIS) L:USE: detect_traps(TRUE); detect_doors(); detect_stairs() L:USE: detect_treasure(); detect_objects_gold(); detect_objects_normal() L:USE: identify_pack() L:USE: self_knowledge() L:USE: ident = TRUE D:This potion grants a surge of enlightnment. You detect everyhing on D:the current dungeon level, all objects you are carrying are identified, D:magical effects on you are revealed, and your intelligence and wisdom D:are permanently increased. N:423:[Black Channels] G:?:v I:94:2:0 W:75:0:30:25000 A:75/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:This tome of blackest midnight holds spells to bind evil to oneself D:and bringing darkness and death upon others. N:424:[Necronomicon] G:?:v I:94:3:0 W:95:0:30:100000 A:95/3 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:This mighty grimoire of death seems to suck all life and light from D:around it. Death in all it's forms is controlled by the rituals within. # Additional rings # This is cheaper than it should be N:425:Fear Resistance G:=:d I:45:38:0 W:17:0:2:250 A:17/2 F:RES_FEAR | EASY_KNOW D:With this ring no foe is scary. N:426:Light and Darkness Resistance G:=:d I:45:39:0 W:52:0:2:1500 A:52/2 F:RES_LITE | RES_DARK | EASY_KNOW D:Is the colour of this ring light or dark? You can not make up your mind. N:427:Nether Resistance G:=:d I:45:40:0 W:60:0:2:3500 A:60/2 F:RES_NETHER | HOLD_LIFE | EASY_KNOW D:With this ring the netherworld has no hold on you. N:428:Nexus Resistance G:=:d I:45:41:0 W:42:0:2:750 A:42/2 F:RES_NEXUS | EASY_KNOW D:With this ring you will not be warped about. N:429:Sound Resistance G:=:d I:45:42:0 W:45:0:2:750 A:45/2 F:RES_SOUND | EASY_KNOW D:With this ring you can listen to loud music. N:430:Confusion Resistance G:=:d I:45:43:0 W:39:0:2:750 A:39/2 F:RES_CONF | EASY_KNOW D:With this ring your mind will not be tricked. N:431:Shard Resistance G:=:d I:45:44:0 W:43:0:2:750 A:43/2 F:RES_SHARDS | EASY_KNOW D:With this ring your skin becomes like a hide. N:432:Disenchantment Resistance G:=:d I:45:45:0 W:100:0:2:750 A:100/10 F:RES_DISEN | EASY_KNOW D:With this ring your equipment remains polished. N:433:Chaos Resistance G:=:d I:45:46:0 W:75:0:2:750 A:75/2 F:RES_CHAOS | RES_CONF | EASY_KNOW D:With this ring you have a grip. N:434:Blindness Resistance G:=:d I:45:47:0 W:20:0:2:750 A:20/4 F:RES_BLIND | EASY_KNOW D:This ring keeps your eyes open. N:435:Lordly Protection G:=:d I:45:48:0 W:105:0:2:6000 A:100/5 F:RES_DISEN | RES_POIS | HOLD_LIFE | FREE_ACT L:MAKE: add_ego_power(EGO_XTRA_HI_RESIST, object) L:MAKE: while (one_in_(4) ~= 0) do add_ego_power(EGO_XTRA_HI_RESIST, object) end L:MAKE: object.to_a = rand_range(10, 15) + m_bonus(10, level) L:MAKE: rating_boost = 5 D:A ring that clearly was meant for some powerful lord. # This is worth 2500 + the value of the plusses N:436:Extra Attacks G:=:d I:45:49:1 W:75:0:2:5000 A:75/8:100/2 F:BLOWS L:MAKE: if (one_in_(7) ~= 0) then object.pval = 2 end L:MAKE: allow_curse = TRUE D:This ring swings about. N:437:Cure Light Wounds G:!:d I:75:34:50 W:0:0:4:35 A:0/1:3/1 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = cure_potion(38, TRUE, FALSE, FALSE) L:SMASH:project(who, 2, x, y, damroll(2, 3), GF_OLD_HEAL, L:SMASH: (PROJECT_JUMP + PROJECT_ITEM + PROJECT_KILL)); return FALSE,TRUE D:This potion cures blindness and restores 38 hit points. N:438:Clumsiness G:!:d I:75:19:0 W:9:0:4:0 A:9/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_dec_stat(A_DEX) L:SMASH:return TRUE,FALSE D:This potion drains your dexterity. N:439:Sickliness G:!:d I:75:20:0 W:17:0:4:0 A:17/2 P:0:1d1:0:0:0 F:EASY_KNOW L:USE: ident = do_dec_stat(A_CON) L:SMASH:return TRUE,FALSE D:This potion drains your constitution. ##### Items 440 to 444 reserved (old "fake" objects) ##### ##### Items 445 to 479 reserved (old "terrain" objects) ##### ##### And here starts the gold/gems ##### N:480:copper G:$:u I:100:1:0 W:1:0:0:3 N:481:copper G:$:u I:100:2:0 W:1:0:0:4 N:482:copper G:$:u I:100:3:0 W:1:0:0:5 N:483:silver G:$:s I:100:4:0 W:1:0:0:6 N:484:silver G:$:s I:100:5:0 W:1:0:0:7 N:485:silver G:$:s I:100:6:0 W:1:0:0:8 N:486:garnets G:$:r I:100:7:0 W:1:0:0:9 N:487:garnets G:$:r I:100:8:0 W:1:0:0:10 N:488:gold G:$:y I:100:9:0 W:1:0:0:12 N:489:gold G:$:y I:100:10:0 W:1:0:0:14 N:490:gold G:$:y I:100:11:0 W:1:0:0:16 N:491:opals G:$:W I:100:12:0 W:1:0:0:18 N:492:sapphires G:$:b I:100:13:0 W:1:0:0:20 N:493:rubies G:$:r I:100:14:0 W:1:0:0:24 N:494:diamonds G:$:w I:100:15:0 W:1:0:0:28 N:495:emeralds G:$:g I:100:16:0 W:1:0:0:32 N:496:mithril G:$:B I:100:17:0 W:1:0:0:40 N:497:adamantite G:$:G I:100:18:0 W:1:0:0:80 ##### Objects 498 and 499 are the "Morgoth Artifacts" ##### # These objects, like objects 500 to 511, are never created # without being turned into artifacts. N:498:& Mighty Hammer~ G:\:D I:21:50:0 W:15:0:200:1000 P:0:3d9:0:0:0 F:SHOW_MODS F:INSTA_ART N:499:& Massive Iron Crown~ G:]:D I:33:50:0 W:44:0:20:1000 P:0:1d1:0:0:0 F:INSTA_ART ##### Objects 500 to 511 are the "Special Artifacts" ##### # These objects do not specify "full names" because the artifact name # is added in "obj-desc.c" via the saved 'quark' of the name, generated # on creation of the artifact, by copying it from a_info.txt # # These objects do specify a "base name", which is used when the object # is "aware" (always true for lites) # # The Lites (and the One Ring) specify "physical colors", which # over-ride the "flavor" colors, if any. Note that the "One Ring" # also has a specific check for "unknown" in which it changes its # name to "a plain gold ring". See "obj-desc.c" (Hack XXX) # # Note that ALL artifacts are given "IGNORE_ACID/ELEC/FIRE/COLD", # so we do not need to specify that here. # # The only reason for having six different "ring" templates and three # different "amulet" templates is to allow each "special artifact" to # have a different "color" and "flavor", and also to allow the use of # special "base names" (such as "Necklace"). # # It is also nice to simplify the artifact creation code. (Otherwise # these would be special cases.) Also, this allows us to have a the # debug command that creates objects able to create these artifacts. # The Phial of Galadriel -- see artifact list N:500:& Phial~ G:!:y I:39:4:0 W:5:20:10:10000 P:0:1d1:0:0:0 F:INSTA_ART | LITE # The Star of Elendil -- see artifact list N:501:& Star~ G:*:y I:39:5:0 W:20:30:5:25000 P:0:1d1:0:0:0 F:INSTA_ART | LITE # The Arkenstone of Thrain -- replaced with Jewel of Judgement N:502:& Jewel~ G:*:R I:39:6:0 W:50:50:5:60000 P:0:1d1:0:0:0 F:INSTA_ART | LITE # The Amulet of Carlammas -- see artifact list N:503:& Amulet~ G:":d I:40:10:0 W:10:30:3:60000 F:INSTA_ART # The Amulet of Ingwe -- see artifact list N:504:& Amulet~ G:":d I:40:11:0 W:30:30:3:90000 F:INSTA_ART # The Necklace of the Dwarves -- see artifact list N:505:& Necklace~ G:":d I:40:12:0 W:50:20:3:75000 F:INSTA_ART # The Ring of Barahir -- see artifact list N:506:& Ring~ G:=:d I:45:32:0 W:10:30:2:65000 F:INSTA_ART # The Ring of Tulkas -- see artifact list N:507:& Ring~ G:=:d I:45:33:0 W:30:30:2:150000 F:INSTA_ART # The Ring of Power (Narya) -- see artifact list N:508:& Ring~ G:=:d I:45:34:0 W:50:30:2:100000 F:INSTA_ART # The Ring of Power (Nenya) -- see artifact list N:509:& Ring~ G:=:d I:45:35:0 W:70:30:2:200000 F:INSTA_ART # The Ring of Power (Vilya) -- see artifact list N:510:& Ring~ G:=:d I:45:36:0 W:90:30:2:300000 F:INSTA_ART # The Ring of Power (The One Ring) -- see artifact list N:511:& Ring~ G:=:y I:45:37:0 W:110:0:2:5000000 F:INSTA_ART ### Room for new objects added after 511 (Zangband 2.1.0): 512-575 # Trump Spellbooks N:512:[Conjurings & Tricks] G:?:U I:95:0:0 W:17:0:30:100 A:17/1 P:0:1d1:0:0:0 F:EASY_KNOW D:This light-brown deck of cards contains small magics of teleportation. N:513:[Deck of Many Things] G:?:U I:95:1:0 W:20:0:30:1000 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A amber deck of cards, containing magics of summoning and movement. N:514:[Trumps of Doom] G:?:o I:95:2:0 W:65:0:30:25000 A:65/1 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:A exquisite deck of cards, containing great summoning magics. N:515:[Five Aces] G:?:o I:95:3:0 W:95:0:30:100000 A:95/3 P:0:1d1:0:0:0 F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD F:EASY_KNOW D:The most powerful of all trump decks, these mystical cards contain D:great magics of summoning and knowledge. # Arcane Spellbooks N:516:[Cantrips for Beginners] G:?:s I:96:0:0 W:10:0:30:100 A:10/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A small drab gray spellbook, containing the simplest of common D:utility spells. N:517:[Minor Arcana] G:?:s I:96:1:0 W:15:0:30:250 A:15/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A gray spellbook with spells of detection and protection. N:518:[Major Arcana] G:?:s I:96:2:0 W:20:0:30:1000 A:20/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A gray spellbook with a mixture of common medium-level spells. N:519:[Manual of Mastery] G:?:s I:96:3:0 W:30:0:30:2500 A:35/1 P:0:1d1:0:0:0 F:EASY_KNOW D:A gray spellbook containing high-level utility spells. N:520:Reflection G:":d I:40:9:0 W:85:0:3:2700 A:85/4 F:REFLECT | EASY_KNOW F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD D:Wearing this amulet you feel you can evade any bolt. #521 and 522 cannot have EASY_KNOW because they may be cursed N:521:Anti-Magic G:":d I:40:13:0 W:85:0:3:2700 A:85/4 F:NO_MAGIC F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: allow_curse = TRUE D:With this amulet magic is not your worry. N:522:Anti-Teleportation G:":d I:40:14:0 W:52:0:3:2700 A:52/4 F:NO_TELE F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: allow_curse = TRUE D:With this amulet you stay firmly rooted. #523 cannot have EASY_KNOW because it can get random resistances N:523:Resistance G:":d I:40:15:0 W:75:0:3:2200 A:75/4 F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD L:MAKE: if (one_in_(3) ~= 0) then add_ego_power(EGO_XTRA_HI_RESIST, object) end L:MAKE: if (one_in_(5) ~= 0) then set_obj_flag(object, 1, TR1_RES_POIS) end D:With this amulet you confront the elements confidently. ##### New arms ##### N:524:& Zweihander~ G:|:w I:23:29:0 W:65:0:280:580 A:65/3:85/3 P:0:4d6:0:0:0 F:SHOW_MODS N:525:& Tanto~ G:|:W I:23:6:0 W:5:0:20:30 A:5/2 P:0:1d5:0:0:0 F:SHOW_MODS N:526:& Splint Mail~ G:[:D I:37:10:0 W:61:0:250:950 A:61/1 P:19:1d4:-2:0:0 N:527:& Do-maru~ G:[:s I:37:11:0 W:70:0:290:1020 A:70/2 P:20:1d4:-2:0:0 N:528:& Trifurcate Spear~ G:/:o I:22:26:0 W:61:0:140:400 A:61/3 P:0:2d9:0:0:0 F:SHOW_MODS N:529:& Three Piece Rod~ G:\:u I:21:11:0 W:35:0:120:350 A:35/3 P:0:3d3:0:0:0 F:SHOW_MODS N:530:& O-yoroi~ G:[:D I:37:16:0 W:75:0:320:1150 A:75/2 P:24:1d4:-2:0:0 N:531:& Fur Cloak~ G:(:W I:35:3:0 W:35:0:30:200 A:35/2:52/2 P:3:0d0:0:0:0 N:532:& Lajatang~ G:/:s I:22:14:0 W:43:0:175:330 A:43/2 P:0:2d7:0:0:0 F:SHOW_MODS N:533:& Hatchet~ G:/:s I:22:1:0 W:17:0:60:120 A:17/8 P:0:1d5:0:0:0 F:SHOW_MODS ##### New armor ##### N:535:& Rhino Hide Armour~ G:(:s I:36:8:0 W:26:0:110:400 A:26/1 P:8:1d1:-1:0:0 N:536:& Leather Jacket~ G:(:U I:36:12:0 W:35:0:130:700 A:35/3 P:12:1d2:-1:0:0 ##### New weapons ##### N:537:& Sickle~ G:/:s I:22:3:0 W:17:0:70:110 A:17/3 P:0:2d3:0:0:0 F:SHOW_MODS N:538:& Tetsubo~ G:\:u I:21:16:0 W:43:0:190:570 A:43/2 P:0:2d7:0:0:0 F:SHOW_MODS N:539:& Nunchaku~ G:\:u I:21:4:0 W:20:0:60:120 A:20/2 P:0:2d3:0:0:0 F:SHOW_MODS N:540:& Bo Staff~ G:\:U I:21:14:0 W:60:0:160:310 A:60/1 P:0:1d20:0:0:0 F:SHOW_MODS N:541:& Jo Staff~ G:\:U I:21:7:0 W:19:0:70:200 A:19/2 P:0:1d10:0:0:0 F:SHOW_MODS N:542:& Club~ G:\:u I:21:1:0 W:0:0:100:3 A:0/1 P:0:2d2:0:0:0 F:SHOW_MODS N:543:& Broad Spear~ G:/:w I:22:7:0 W:60:0:100:450 A:60/2 P:0:1d20:0:0:0 F:SHOW_MODS N:544:& Khopesh~ G:|:W I:23:14:0 W:17:0:130:190 A:17/2 P:0:2d4:0:0:0 F:SHOW_MODS N:545:& Flamberge~ G:|:W I:23:26:0 W:65:0:230:600 A:65/2:87/2 P:0:3d7:0:0:0 F:SHOW_MODS N:546:& Claymore~ G:|:W I:23:23:0 W:65:0:200:600 A:65/2 P:0:2d8:0:0:0 F:SHOW_MODS N:547:& Espadon~ G:|:W I:23:24:0 W:65:0:200:600 A:65/3 P:0:2d9:0:0:0 F:SHOW_MODS N:548:& Great Scimitar~ G:|:W I:23:22:0 W:85:0:240:1050 A:85/3:98/3 P:0:4d5:0:0:0 F:SHOW_MODS N:549:& Wakizashi~ G:|:W I:23:13:0 W:26:0:90:210 A:26/3 P:0:2d4:0:0:0 F:SHOW_MODS N:550:& Naginata~ G:/:s I:22:9:0 W:43:0:150:410 A:43/2 P:0:2d6:0:0:0 F:SHOW_MODS N:551:& Fauchard~ G:/:s I:22:6:0 W:82:0:155:700 A:82/1 P:0:1d25:0:0:0 F:SHOW_MODS N:552:& Guisarme~ G:/:s I:22:16:0 W:37:0:165:320 A:37/1 P:0:2d5:0:0:0 F:SHOW_MODS N:553:& Heavy Lance~ G:/:s I:22:29:0 W:88:0:400:700 A:88/2:98/1 P:0:1d35:0:0:0 F:SHOW_MODS N:554:& Basillard~ G:|:w I:23:9:0 W:42:0:80:550 A:42/1 P:0:1d15:0:0:0 F:SHOW_MODS N:555:& Ninjato~ G:|:s I:23:19:0 W:52:0:100:600 A:52/1 P:0:1d18:0:0:0 F:SHOW_MODS N:556:& Ring Mail~ G:[:s I:37:2:0 W:35:0:200:550 A:35/1 P:12:1d4:-2:0:0 N:557:& Cord Armour~ G:(:y I:36:9:0 W:9:0:80:200 A:9/2 P:6:1d1:0:0:0 N:558:& Paper Armour~ G:(:w I:36:3:0 W:9:0:30:50 A:9/3 P:4:1d1:0:0:0 D:This unusual armor is made from many layers of paper glued together. N:559:& Padded Armour~ G:(:y I:36:10:0 W:4:0:60:30 A:4/1 P:4:1d1:0:0:0 N:560:& Kabuto~ G:]:D I:32:8:0 W:75:0:80:300 A:75/3 P:7:1d2:0:0:0 N:561:& Stone and Hide Armour~ G:(:U I:36:15:0 W:61:0:200:1300 A:61/4 P:15:1d1:-1:0:0 N:562:& Jingasa~ G:]:s I:32:4:0 W:28:0:30:60 A:28/2 P:4:1d2:0:0:0 N:563:& Haramakido~ G:[:D I:37:14:0 W:56:0:280:1320 A:56/2 P:17:1d4:-2:0:0 N:564:& Incandescent Globe~ G:!:y I:39:3:0 W:30:50:5:20000 P:0:1d1:0:0:0 F:INSTA_ART | LITE N:565:& Diamond Edge~ G:|:w I:23:31:0 W:120:0:150:20000 A:100/16 P:0:7d5:0:0:0 F:SHOW_MODS | VORPAL D:You don't dare testing this blade on your fingernail, it is so sharp. N:566:Mundanity G:?:d I:70:23:0 W:23:0:5:0 A:23/10:63/10 F:EASY_KNOW L:USE: result = mundane_spell() L:USE: ident = TRUE D:Your objects seem very ordinary all of a sudden. N:567:& Magical Figurine~ of # G:`:g I:8:0:0 W:50:0:50:100 A:26/6:52/3:90/1 P:0:1d1:0:0:0 F:EASY_KNOW D:This small statuette seems to move. N:568:& Wooden Statue~ of # G:`:u I:9:0:0 W:5:0:200:75 A:9/3 P:0:1d2:0:0:0 F:EASY_KNOW D:An unpolished statue made of wood. N:569:& Clay Statue~ of # G:`:U I:9:1:0 W:5:0:500:50 A:17/3 P:0:1d3:0:0:0 F:EASY_KNOW D:A hardbaked status made of clay. N:570:& Stone Statue~ of # G:`:s I:9:2:0 W:5:0:500:100 A:26/3 P:0:2d3:0:0:0 F:EASY_KNOW D:This status has been cut out of a rock. N:571:& Iron Statue~ of # G:`:D I:9:3:0 W:5:0:500:200 A:35/3 P:0:2d4:0:0:0 F:EASY_KNOW D:This statue was poured from molten iron. N:572:& Copper Statue~ of # G:`:o I:9:4:0 W:20:0:500:500 A:43/3 P:0:2d4:0:0:0 F:EASY_KNOW D:This copper statue needs polishing. N:573:& Silver Statue~ of # G:`:W I:9:5:0 W:32:0:500:1000 A:52/3 P:0:2d4:0:0:0 F:EASY_KNOW D:This silver statue needs varnishing. N:574:& Golden Statue~ of # G:`:y I:9:6:0 W:50:0:500:2500 A:63/3 P:0:2d4:0:0:0 F:EASY_KNOW D:This golden statue needs polishing. N:575:& Ivory Statue~ of # G:`:w I:9:7:0 W:40:0:500:2000 A:70/3 P:0:1d4:0:0:0 F:EASY_KNOW D:This ivory statue needs cleaning. N:576:& Mithril Statue~ of # G:`:B I:9:8:0 W:70:0:500:10000 A:95/3 P:0:2d5:0:0:0 F:EASY_KNOW D:This mithril statue needs polishing. N:577:& Ornate Statue~ of # G:`:v I:9:9:0 W:60:0:500:5000 A:80/3 P:0:2d4:0:0:0 F:EASY_KNOW D:This ornate statue needs dusting. #N:578:& Skeleton~ #G:~:w #I:10:0:0 #W:32:0:500:0 #A:1/2 #P:0:1d1:0:0:0 #N:579:& corpse~ #G:~:v #I:10:1:0 #W:32:0:1000:0 #A:1/2 #P:0:1d1:0:0:0 N:580:& T-shirt~ G:~:w I:36:0:0 W:1:0:10:0 P:1:1d1:0:0:0 ##### New Bolt ##### N:581:& Steel Bolt~ G:{:W I:18:1:0 W:43:0:3:25 A:43/2:70/2 P:0:1d15:0:0:0 F:SHOW_MODS D:This bolt will not fall apart upon hitting the target. ##### New artifact ring ##### # The Ring of Power (Vilya) -- see artifact list N:582:& Ring~ G:=:d I:45:50:0 W:100:20:2:300000 F:INSTA_ART ##### New rings ##### N:583:Resist Fire and Cold G:=:d I:45:51:0 W:21:0:2:850 A:21/4 F:RES_FIRE | RES_COLD | IGNORE_FIRE | IGNORE_COLD | EASY_KNOW D:Your finger alternates between hot and cold. N:584:the Cat G:=:d I:45:52:4 W:13:0:2:80 A:13/2 F:STEALTH | INFRA L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: if (one_in_(3) ~= 0) then set_obj_flag(object, 1, TR1_SUST_DEX) end L:MAKE: allow_curse = TRUE D:A ring for the inobtrusive. # This is cheaper than it should be N:585:Fate G:=:d I:45:53:0 W:15:0:2:500 A:15/5:30/5 F:STRANGE_LUCK | EASY_KNOW D:Odd things happen to the wearer of this ring. N:586:Wizardry G:=:d I:45:54:2 W:60:0:2:2500 A:75/2 F:SP L:MAKE: object.pval = 1 + m_bonus(object.pval, level) L:MAKE: allow_curse = TRUE D:Wearing this ring seems to clarify your mind. ##### New amulets ##### N:587:Protection from Evil G:":d I:40:16:0 W:35:0:3:750 A:35/2 F:SLAY_EVIL | EASY_KNOW D:This amulet boosts your skills against the powers of evil. N:588:Protection from Undead G:":d I:40:17:4 W:35:0:3:750 A:35/2 F:SLAY_UNDEAD | WIS L:MAKE: object.pval = 1 + m_bonus(object.pval, level) D:This amulet boosts your skills against the powers of the undead. N:589:Luck G:":d I:40:18:0 W:50:0:3:1500 A:50/1 F:LUCK_10 L:MAKE: add_ego_power(EGO_XTRA_POWER, object) D:This lucky amulet boosts your confidence. ##### New weapon types ##### N:590:& Elfblade~ G:|:w I:23:32:0 W:90:0:140:15000 A:70/20:90/20 P:0:4d4:0:0:0 D:You can tell by the Elvish runes that this blade is favored by Elves. N:591:& Whip~ of Entanglement G:\:D I:21:21:0 W:20:0:30:350 A:20/5 P:0:1d6:0:0:0 F:SLAY_ANIMAL | SHOW_MODS D:A whip to handle animals with. N:592:& Psiblade~ G:|:B I:23:33:0 W:50:0:120:1400 A:50/4 P:0:3d4:0:0:0 F:PSI_CRIT | LITE | SHOW_MODS D:The blade of this sword is made of pure magic. N:593:& Hammer~ of Returning G:\:D I:21:22:0 W:9:0:85:675 A:31/4 P:0:3d4:0:0:0 F:THROW | RETURN | SHOW_MODS D:An ornate metal hammer with runes inscribed on the haft and head. # Hagaromo (feathered robe) - worn by tenyo (celestial maidens) # in japanese mythology N:594:& Hagaromo~ G:(:v I:36:16:0 W:40:0:20:750 A:40/2 P:2:0d0:0:0:0 F:FEATHER | F:SUST_STR | SUST_INT | SUST_WIS | F:SUST_DEX | SUST_CON | SUST_CHR D:A heavenly robe covered with soft feathers. L:MAKE: object.to_a = object.to_a + randint1(10) + m_bonus(10, level) # Amulet of Sustenance N:595:Sustenance G:":d I:40:19:0 W:12:0:3:300 A:12/5 F:EASY_KNOW D:With this amulet you will never be hungry again.. L:TIMED: if player.food < PY_FOOD_FULL + 500 then set_food(player.food + 10) end zangband/lib/edit/misc.txt0000755000000000000000000000302710250356274014540 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2005/06/04 12:53:28 $ # File: misc.txt # This file is used to initialize the "lib/data/z_info.raw" file, which is # used to initialize the "array sizes" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/data/z_info.raw" file. # Version stamp (required) V:2.7.5 # Maximum number of feature types M:F:160 # Maximum number of object kinds M:K:596 # Maximum number of artifacts M:A:134 # Maximum number of ego-item types M:E:128 # Maximum number of monster races M:R:892 # Maximum number of vaults M:V:126 # Maximum number of quests M:Q:125 # Maximum number of field types M:U:147 # Maximum size for "fld_list[]" M:D:512 # Maximum number of nodes in the wilderness decision tree M:W:N:250 # Maximum number of wilderness block generation types M:W:T:300 # Maximum number of "places" in the wilderness (towns + quests) M:W:P:60 # Maximum number of active "regions" in dungeon / wilderness M:G:10 # This needs to be larger than STORE_CACHE_AMNT * 24 # Maximum number of objects M:O:3000 # Maximum number of monsters M:M:1024 # # Array sizes (in bytes) for some initialization stuff # # Size of the "fake" array for reading in names of monsters, objects, # artifacts, store-owners, player-races, ... M:N:40960 # Size of the "fake" array for reading in the descriptions of monsters, # vaults, and the player-histories M:T:122880 zangband/lib/edit/r_info.txt0000755000000000000000000141206210250356274015065 0ustar rootroot# File: r_info.txt # With new monsters for Zangband 2.2 (or 2.3) # This file is used to initialize the "lib/raw/r_info.raw" file, which is # used to initialize the "monster race" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/raw/r_info.raw" file. # Zangband notes: # Semantics of S_AMBERITES: Since all non-Tolkienian unique Ringwraiths # have been removed (i.e. all except the Witch-king of Angmar and # Khamul), the spell summons unique Amberites instead of Ringwraiths. # Currently, "unique" monsters are just "special" monster races, with # the requirement that only one monster of that race can exist at a time, # and when it is killed, it can never again be generated. # ATTR_CLEAR monsters acquire their attr from the item/floor below them, # and use "white" for the recall window. See "cave.c" for info. # ATTR_MULTI monsters have a "flickering" attr, and use "violet" for the # recall window. See "cave.c" for info. # CHAR_CLEAR monsters use special symbols (.) as given below, # and use those symbols for the recall window. # CHAR_MIMIC monsters use special symbols (!, ?, =) as given below, # and use those symbols for the recall window. # Note that there are (a few) normal monsters who are "violet" but not # ATTR_MULTI, and a lot of monsters which are "white" but not ATTR_CLEAR. # Note that currently CHAR_CLEAR monsters are treated # as normal monsters that are just a little hard to see. # CHAR_MIMIC monsters a special-cased in the targetting code. They can not # be looked at or targetted unless 'noticed'. The monster-list code also # handles this flag. # Note that the monster list underwent several changes for Angband 2.7.9, # including some monster name changes, some symbol redistributions, and # some color changes. # The Umber Hulk joined the Xorn/Xaren (X). The ticks (t) joined the # spiders (S). The townspeople (t) left the people (p). The "Jabberwock" # became the "Chaos beetle" (K). The major demons (&) became (U) and the # minor demons (I) became (u). Multiplying insects (fleas, fruit flies, # hummerhorns) became (I), visually "matching" the multiplying lice (l). # The "ant lions" (a) became "ants" (a). The mummified monsters (M) # joined the zombified monsters (z). The multi-headed hydras (M) left # the reptiles (R). The snakes (J) left the reptiles (R). # Some of the old "red" or "brown" monsters became "pink" if they lower # strength, while some of the old "fire" monsters became simply "red" # monsters. The "dragons" and "hounds" and related monsters underwent # a "color scheme regularization" ('w' = White/Cold, 's' = Black/Acid, # 'o' = Lite/Dark, 'r' = Red/Fire, 'g' = Green/Poison, 'b' = Blue/Elec, # 'u' = Brown/Earth/Force, 'D' = Dark/etc, 'W' = Stone/Inertia/Gravity/etc, # 'v' = Multihued/Chaos/Disenchantment/etc, 'y' = Gold/Sound, 'R' = Nexus, # 'G' = Nether, 'B' = Left-overs, and 'U' = Bronze/Confusion). # In several situations, two or more monsters with identical symbols and # colors were changed so that maximal information is conveyed by the symbol # and color. # The "people" (p), with more than 50 entries, got a new "color scheme" # ('w' = Paladin, 's' = Knight, 'o' = Mystic, 'r' = Mage, 'g' = High Priest, # 'b' = Thief, 'u' = Warrior, 'D' = Death knight, 'W' = Ranger/Archer, # 'v' = Sorcerer, 'y' = Ninja, 'R' = High Mage, 'G' = Priest, 'B' = High # Thief, 'U' = High Warrior). Note that most non-unique "people" already # had these colors, or colors close to these colors. A similar color scheme # was enforced for the "humanoid" (h) monsters as well, more or less. # TY: This is no longer entirely accurate. The monster coloring has been # changed 'back' to pre-2.7.* coloring in several cases. For example, I # prefer "black" thief caracters. Also color can be (and should) be used to # convey information, but more importantly it is a visual presentation # of the creature and should be what the creature "looks" like. # Many of the "unique" monsters were changed to "match" the "base" monster # from which they were derived. # Mushrooms look just like food (and use the "," symbol for both the recall # window and for normal display), Creeping coins look just like coins (and # use the "$" symbol for both the recall window and for normal display), and # Trappers/Lurkers can never be seen (and use the "." symbol for the recall # window). All other monsters use "alphabetic" symbols, and "alphabetic" # symbols are used only for monsters. # The "x" symbol is free for use as an "attr/char mapping" for annoying # monsters, such as magic mushrooms, drolems, etc. # There are still too many "p" monsters, perhaps they should be broken up. # As always, you can enforce any "visual picture" you want with a "pref file". # Note that monster zero is used for the "player" picture. # === Understanding r_info.txt === # N: serial number : monster name # G: symbol : color # I: speed : hit points : vision : armor class : alertness # W: depth : rarity : unused (always 0) : experience for kill # O: treasure : combat items: magic items # B: attack method : attack effect : damage # S: spell frequency | # S: spell type | spell type | etc # F: flag | flag | etc # D: Description # 'N' indicates the beginning of an entry. The serial number must # increase for each new item. Entry 0 is used for the player. # 'G' is for graphics - symbol and color. There are 16 colors, as # follows: # D - Dark Gray w - White s - Gray o - Orange # r - Red g - Green b - Blue u - Brown # d - Black W - Light Gray v - Violet y - Yellow # R - Light Red G - Light Green B - Light Blue U - Light Brown # 'I' is for information - speed, health, vision in tens of feet, # armor class, and alertness. 110 is normal speed. Alertness ranges # from 0 (ever vigilant for intruders) to 255 (prefers to ignore # intruders). # 'W' is for more information - level, rarity, and experience for # killing. The third slot is unused. # 'B' is for blows - method of attack, effect of attack, and damage # from attack. There may be up to four of these lines; effect and # damage are optional. # 'O' is for object theme. The three numbers represent the percent # chance to drop treasure, combat and magic items. The chance to # drop tools is defined as 100 - the sum of these three numbers. # Note that monsters do not drop junk. Junk can only be generated # in the dungeon when you enter it. # 'S' is for spells. The first S: line must be S:1_IN_X with X the # number of monster turns, on average, before the monster will cast # one of its spells. X must not be zero. # 'F' is for flags. These are fairly self-explanatory. As many F: # lines may be used as are needed to specify all the flags and flags # are separated by the '|' symbol. # 'D' is for description. As many D: lines may be used as are needed # to describe the monster. Note that lines may need spaces at their # ends to prevent words from running together in the monster memory. # Version stamp (required) V:2.7.5 ##### The Player ##### N:0:Player G:@:w ##### Town monsters ##### N:1:Filthy street urchin G:t:D I:110:1d4:4:2:40 W:0:2:0:0 O:0:0:0 B:BEG B:TOUCH:EAT_GOLD F:MALE | EVIL | WILD_TOWN F:RAND_25 | FRIENDS | F:TAKE_ITEM | OPEN_DOOR | DROP_CORPSE | DROP_SKELETON D:He looks squalid and thoroughly revolting. N:2:Scrawny cat G:f:U I:110:1d2:30:3:10 W:0:3:0:0 O:0:0:0 B:CLAW:HURT:1d1 F:RAND_25 | WILD_TOWN F:ANIMAL | DROP_CORPSE | DROP_SKELETON D:A skinny little furball with sharp claws and a menacing look. N:3:Sparrow G:B:U I:110:1d1:30:2:10 W:0:3:0:0 O:0:0:0 B:BITE:HURT:1d1 F:RAND_25 | CAN_FLY | WILD_TOWN F:ANIMAL | DROP_SKELETON D:Utterly harmless, except when angry. #Instead, we have chaffinches for wilderness N:4:Chaffinch G:B:r I:110:1d1:30:2:10 W:0:3:0:0 O:0:0:0 B:BITE:HURT:1d1 F:RAND_25 | CAN_FLY | WILD_FOREST1 | WILD_FOREST2 | WILD_SWAMP1 | WILD_GRASS F:ANIMAL | DROP_SKELETON D:Utterly harmless, except when angry. N:5:Wild rabbit G:r:U I:110:1d2:30:2:10 W:0:3:0:0 O:0:0:0 B:BITE:HURT:1d1 F:RAND_50 | WILD_FOREST1 | WILD_FOREST2 | WILD_GRASS F:ANIMAL | DROP_SKELETON | DROP_CORPSE D:It is not a carnivore, but will defend itself if you stray too D:close. N:6:Woodsman G:p:g I:110:1d9:10:5:255 W:0:1:0:0 O:0:50:0 B:HIT:HURT:1d6 F:MALE | WILD_FOREST1 | WILD_FOREST2 F:RAND_25 | DROP_SKELETON | DROP_CORPSE | F:ONLY_GOLD | DROP_60 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR D:He has an axe with a sharp and strong edge. N:7:Scruffy little dog G:C:U I:110:1d3:20:3:5 W:0:3:0:0 O:0:0:0 B:BITE:HURT:1d1 F:RAND_25 | DROP_SKELETON | DROP_CORPSE | WILD_TOWN F:ANIMAL D:A thin flea-ridden mutt, growling as you get close. N:8:Farmer Maggot G:h:w I:110:1d350:40:10:3 W:0:3:0:0 O:0:100:0 B:HIT:HURT:1d3 F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | FRIENDLY F:FORCE_MAXHP | WILD_TOWN F:ONLY_ITEM | DROP_90 | DROP_GOOD | DROP_GREAT F:OPEN_DOOR | BASH_DOOR | LITE_1 F:NO_CONF | NO_SLEEP D:Before you appears a broad thick-set hobbit with a round red face. D:"What's wrong with old Maggot?" asked Pippin. "He's a good friend to D:all the Brandybucks. Of course he's a terror to trespassers, and keeps D:ferocious dogs -- but after all, folk down here are near the border and D:have to be more on their guard." N:9:Blubbering idiot G:t:W I:110:1d2:6:1:0 W:0:1:0:0 O:0:0:0 B:DROOL F:MALE | DROP_CORPSE | DROP_SKELETON | WILD_TOWN F:RAND_25 | F:TAKE_ITEM D:He tends to blubber a lot. N:10:Hobo G:t:g I:110:1d2:6:1:0 W:0:1:0:0 O:0:0:0 B:DROOL F:MALE | DROP_SKELETON | DROP_CORPSE | WILD_TOWN F:RAND_25 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR D:Ugly doesn't begin to describe him. N:11:Raving lunatic G:t:G I:120:1d12:6:1:0 W:0:1:0:0 O:0:0:0 B:DROOL F:MALE | DROP_CORPSE | DROP_SKELETON | WILD_TOWN F:RAND_25 | F:TAKE_ITEM D:Drooling and comical, but then, what do you expect? N:12:Pitiful looking beggar G:t:U I:110:1d4:10:2:40 W:0:1:0:0 O:0:0:0 B:BEG F:MALE | DROP_SKELETON | DROP_CORPSE F:RAND_25 | WILD_TOWN F:TAKE_ITEM | OPEN_DOOR D:You just can't help feeling sorry for him. N:13:Mangy looking leper G:t:u I:110:1d1:10:2:50 W:0:1:0:0 O:0:0:0 B:BEG B:TOUCH:DISEASE F:MALE | DROP_CORPSE | DROP_SKELETON F:RAND_25 | WILD_TOWN F:TAKE_ITEM | OPEN_DOOR D:You feel it isn't safe to touch him. N:14:Agent of black market G:t:D I:110:1d16:10:10:99 W:0:1:0:0 O:25:50:25 B:HIT:HURT:1d6 B:TOUCH:EAT_ITEM F:MALE | DROP_CORPSE | DROP_SKELETON F:DROP_60 | WILD_TOWN | F:WILD_SWAMP1 | WILD_SWAMP2 | WILD_FOREST1 | WILD_FOREST2 | WILD_MOUNT1 F:WILD_MOUNT2 | WILD_GRASS F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL D:He 'finds' new wares for the Black Market. From unwary adventurers... N:15:Singing, happy drunk G:t:y I:110:1d6:10:3:0 W:0:1:0:0 O:0:0:0 B:BEG F:MALE | F:RAND_50 | DROP_SKELETON | DROP_CORPSE F:ONLY_GOLD | DROP_60 | WILD_TOWN F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR D:He makes you glad to be sober. N:16:Aimless looking merchant G:t:o I:110:1d9:10:3:255 W:0:1:0:0 O:0:0:0 B:HIT:HURT:1d3 F:MALE | F:RAND_50 | F:ONLY_GOLD | DROP_60 | DROP_SKELETON | DROP_CORPSE F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_TOWN | LITE_1 D:The typical ponce around town, with purse jingling, and looking for more D:amulets of adornment to buy. N:17:Mean looking mercenary G:t:R I:110:1d32:10:10:250 W:0:1:0:0 O:0:100:0 B:HIT:HURT:1d10 F:MALE | DROP_SKELETON | DROP_CORPSE F:RAND_50 | DROP_90 | WILD_TOWN | WILD_FOREST1 | WILD_FOREST2 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | LITE_1 F:EVIL D:No job is too low for him. N:18:Battle scarred veteran G:t:r I:110:1d48:10:16:250 W:0:1:0:0 O:25:50:25 B:HIT:HURT:2d6 F:MALE | DROP_SKELETON | DROP_CORPSE F:RAND_50 | DROP_90 | WILD_TOWN | LITE_1 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR D:He doesn't take to strangers kindly. N:19:Martti Ihrasaari G:P:w I:109:1d525:50:15:4 W:0:4:0:0 O:50:50:0 B:HIT:HURT:1d3 F:UNIQUE | MALE | GIANT | CAN_SPEAK | FRIENDLY F:FORCE_MAXHP | DROP_CORPSE | WILD_TOWN F:ONLY_ITEM | DROP_90 | DROP_GOOD | LITE_1 F:OPEN_DOOR | BASH_DOOR | SILLY | F:NO_CONF | NO_SLEEP D:He weighs 127 kg. He is the former president of some remote country. ##### Normal monsters ##### N:20:Grey mold G:m:s I:110:1d2:2:2:0 W:2:1:0:9 O:0:0:0 B:HIT:HURT:1d4 B:HIT:HURT:1d4 F:NEVER_MOVE | DUN_LAIR | DUN_RUIN | DUN_MINE | DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | DROP_CORPSE F:NO_CONF | NO_SLEEP | NO_FEAR D:A small strange growth. N:21:Large white snake G:J:w I:100:1d18:4:17:99 W:1:1:0:4 O:0:0:0 B:BITE:HURT:1d1 B:CRUSH:HURT:1d1 F:RAND_50 | DUN_LAIR | DUN_MINE | WILD_FOREST1 | WILD_FOREST2 F:DUN_DARKWATER F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL D:It is about eight feet long. N:22:Blinking dot G:,:s I:110:1d2:2:2:0 W:3:1:0:23 O:0:0:0 B:SPORE:CONFUSE:1d4 F:NEVER_MOVE | DUN_TOWER F:STUPID | EMPTY_MIND | F:IM_POIS | DROP_CORPSE F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK D:Is it there or is it not? N:23:Newt G:R:y I:110:1d12:8:8:30 W:1:1:0:1 O:0:0:0 B:BITE:HURT:1d3 B:BITE:HURT:1d3 F:WEIRD_MIND | CAN_SWIM | DUN_DARKWATER | DUN_LAIR | WILD_FOREST1 | WILD_SWAMP1 F:WILD_SWAMP2 | WILD_WASTE1 | WILD_SHORE | WILD_MOUNT1 | WILD_MOUNT2 | F:WILD_TOWN F:ANIMAL | DROP_CORPSE D:A small, harmless lizard. N:24:Giant white centipede G:c:w I:110:2d8:7:8:40 W:3:1:0:11 O:0:0:0 B:BITE:HURT:1d2 B:STING:HURT:1d2 F:DUN_LAIR | DUN_RUIN | DUN_MINE | WILD_FOREST1 F:DUN_DARKWATER F:RAND_50 | DROP_SKELETON | F:WEIRD_MIND | BASH_DOOR | F:ANIMAL D:It is about four feet long and carnivorous. N:25:White icky thing G:i:w I:110:1d15:12:6:10 W:1:1:0:1 O:0:0:0 B:TOUCH:HURT:1d2 F:DUN_DARKWATER | DUN_LAIR | DUN_MINE F:RAND_50 | RAND_25 | CAN_SWIM | F:EMPTY_MIND | DROP_CORPSE D:It is a smallish, slimy, icky creature. N:26:Clear icky thing G:i:B I:110:1d10:12:6:10 W:1:1:0:1 O:0:0:0 B:TOUCH:HURT:1d2 F:DUN_DARKWATER | DUN_LAIR | DUN_MINE F:ATTR_CLEAR | CAN_SWIM | F:RAND_50 | RAND_25 | F:INVISIBLE | EMPTY_MIND | DROP_CORPSE D:It is a smallish, slimy, icky, blobby creature. N:27:Giant white mouse G:r:w I:110:1d3:8:4:20 W:2:1:0:2 O:0:0:0 B:BITE:HURT:1d2 F:DUN_LAIR | DUN_DARKWATER | DUN_RUIN F:RAND_50 | WILD_GRASS | F:MULTIPLY | CAN_SWIM | F:ANIMAL | DROP_CORPSE D:It is about three feet long with large teeth. N:28:Large brown snake G:J:u I:100:1d24:4:15:99 W:1:1:0:3 O:0:0:0 B:BITE:HURT:1d3 B:CRUSH:HURT:1d4 F:DUN_LAIR | DUN_RUIN F:DUN_DARKWATER F:RAND_25 | CAN_SWIM | F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL D:It is about eight feet long. N:29:Small kobold G:k:G I:110:1d14:20:10:10 W:1:1:0:4 O:0:50:0 B:HIT:HURT:1d5 F:DUN_DARKWATER | DUN_MINE | WILD_WASTE1 F:DROP_60 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | IM_POIS D:It is a squat and ugly humanoid figure. N:30:Kobold G:k:g I:110:2d12:20:12:10 W:3:1:0:17 O:0:50:0 B:HIT:HURT:1d8 F:DROP_60 | DUN_DARKWATER | DUN_MINE | WILD_WASTE1 F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | IM_POIS D:It is a small, dog-headed humanoid. N:31:White worm mass G:w:w I:100:1d15:7:3:10 W:2:1:0:1 O:0:0:0 B:CRAWL:POISON:1d2 F:DUN_LAIR | DUN_DARKWATER F:RAND_50 | RAND_25 | CAN_SWIM | F:STUPID | WEIRD_MIND | MULTIPLY | F:ANIMAL | IM_POIS | HURT_LITE | NO_FEAR D:It is a large slimy mass of worms. N:32:Floating eye G:e:b I:80:1d6:2:6:10 W:2:1:0:11 O:0:0:0 B:GAZE:PARALYZE F:DUN_DARKWATER | DUN_TEMPLE | DUN_MINE F:CAN_FLY | DROP_CORPSE F:HURT_LITE | NO_FEAR D:A disembodied eye, floating a few feet above the ground. N:33:Rock lizard G:R:U I:110:1d12:20:5:15 W:2:1:0:8 O:0:0:0 B:BITE:HURT:1d2 F:DUN_LAIR | DUN_RUIN | DUN_MINE F:ANIMAL | CAN_SWIM | WILD_MOUNT1 | WILD_MOUNT2 F:DROP_CORPSE D:It is a small lizard with a hardened hide. N:34:Grid bug G:I:v I:110:1d8:10:4:10 W:1:4:0:1 O:0:0:0 B:BITE:ELEC:1d4 F:DUN_TOWER | DUN_LAIR F:RAND_25 | FRIENDS | CAN_FLY | F:STUPID | WEIRD_MIND | LITE_1 F:ANIMAL | NO_FEAR | IM_ELEC D:A strange electric bug. N:35:Jackal G:C:U I:110:2d2:10:5:10 W:4:1:0:13 O:0:0:0 B:BITE:HURT:1d1 F:DUN_LAIR | WILD_FOREST1 | WILD_GRASS F:FRIENDS | F:ANIMAL | DROP_SKELETON | DROP_CORPSE D:It is a yapping snarling dog, dangerous when in a pack. N:36:Soldier ant G:a:u I:110:1d10:10:5:10 W:1:1:0:4 O:0:0:0 B:BITE:HURT:1d2 F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON | F:ANIMAL | DUN_LAIR | DUN_MINE | WILD_GRASS F:DUN_DARKWATER D:A large ant with powerful mandibles. N:37:Fruit bat G:b:v I:120:1d4:20:11:10 W:2:1:0:9 O:0:0:0 B:BITE:HURT:1d1 F:ANIMAL | CAN_FLY | DUN_LAIR | DUN_MINE | WILD_FOREST1 | WILD_FOREST2 | F:WILD_SWAMP1 | WILD_SWAMP2 | DROP_CORPSE D:A fast-moving pest. N:38:Insect swarm G:I:u I:120:2d2:20:8:10 W:3:1:0:8 O:0:0:0 B:BITE:HURT:1d2 B:STING:HURT:1d2 F:ANIMAL | WEIRD_MIND | CAN_FLY | RAND_25 | DUN_LAIR | DUN_RUIN | DUN_DARKWATER F:WILD_GRASS | WILD_FOREST1 | WILD_FOREST2 | WILD_SWAMP1 | WILD_SWAMP2 D:A lone insect may be harmless, but there's a whole swarm of D:them here! N:39:Greater hell-beast G:U:s I:115:2d563:10:75:99 W:3:6:0:84 O:0:0:0 B:GAZE B:GAZE B:CRUSH F:DUN_MINE | DUN_CAVERN | DUN_HORROR | DUN_HELL F:DUN_DARKWATER F:EVIL | IM_FIRE | SILLY | F:RES_NETH | RES_WATE | RES_PLAS | RES_DISE | RES_NEXU | F:KILL_WALL | FORCE_MAXHP | CAN_SWIM | DROP_CORPSE S:1_IN_9 | S:TPORT | BLINK | TELE_AWAY D:This unholy abomination will crush you. Flee while you can! N:40:Shrieker mushroom patch G:,:R I:110:1d1:4:3:0 W:5:1:0:13 O:0:0:0 F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW | F:STUPID | EMPTY_MIND | F:IM_POIS | DUN_LAIR | DUN_DARKWATER | WILD_SWAMP1 | WILD_SWAMP2 | DROP_CORPSE | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_4 | S:SHRIEK D:Yum! These look quite tasty. N:41:Blubbering icky thing G:i:g I:110:1d28:14:6:10 W:1:1:0:1 O:25:25:25 B:CRAWL:POISON:1d4 B:CRAWL:EAT_FOOD B:DROOL B:DROOL F:DUN_LAIR | DUN_DARKWATER F:RAND_50 | DROP_90 | CAN_SWIM | DROP_CORPSE F:EMPTY_MIND | TAKE_ITEM | KILL_BODY | F:IM_POIS D:It is a smallish, slimy, icky, hungry creature. N:42:Metallic green centipede G:c:G I:120:1d15:5:10:10 W:2:1:0:9 O:0:0:0 B:CRAWL:HURT:1d1 F:RAND_50 | DUN_LAIR | DUN_MINE | WILD_MOUNT1 | WILD_FOREST2 | DROP_SKELETON F:DUN_DARKWATER F:WEIRD_MIND | BASH_DOOR | F:ANIMAL D:It is about four feet long and carnivorous. N:43:Novice warrior G:p:U I:110:4d7:20:13:5 W:7:1:0:27 O:25:50:0 B:HIT:HURT:1d7 B:HIT:HURT:1d6 F:MALE | F:DROP_60 | DUN_CITY | DUN_RUIN | DUN_TEMPLE | DUN_TOWER | WILD_GRASS F:DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | LITE_1 D:He looks inexperienced but tough. N:44:Novice rogue G:p:D I:110:3d8:20:11:5 W:5:1:0:22 O:50:25:0 B:HIT:HURT:1d6 B:TOUCH:EAT_GOLD F:MALE | F:DROP_60 | DROP_SKELETON | DROP_CORPSE F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR F:DUN_CITY | DUN_RUIN | DUN_TEMPLE | DUN_TOWER | WILD_GRASS F:EVIL D:A rather shifty individual. N:45:Novice priest G:p:B I:110:5d5:20:9:10 W:9:1:0:91 O:25:0:50 B:HIT:HURT:1d5 F:MALE | F:FORCE_SLEEP | GOOD | DUN_TEMPLE | DUN_CITY | DUN_RUIN F:DROP_SKELETON | DROP_CORPSE F:DROP_60 | WILD_GRASS | LITE_1 F:OPEN_DOOR | BASH_DOOR S:1_IN_12 | S:HEAL | SCARE | CAUSE_1 D:He is tripping over his priestly robes. N:46:Novice mage G:p:R I:110:2d10:20:7:5 W:3:1:0:17 O:25:0:75 B:HIT:HURT:1d4 F:MALE | F:FORCE_SLEEP | DUN_TOWER | DUN_CITY | DUN_RUIN F:DROP_SKELETON | DROP_CORPSE F:DROP_60 | WILD_GRASS F:OPEN_DOOR | BASH_DOOR | LITE_1 S:1_IN_3 | S:BLINK | BLIND | CONF | MISSILE D:He is leaving behind a trail of dropped spell components. N:47:Yellow mushroom patch G:,:y I:110:1d1:2:3:0 W:2:1:0:7 O:0:0:0 B:SPORE:TERRIFY:1d6 F:NEVER_MOVE | DUN_LAIR | DUN_MINE | DUN_RUIN | WILD_SWAMP1 | WILD_SWAMP2 F:DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:Yum! It looks quite tasty. N:48:White jelly G:j:w I:120:2d24:2:1:99 W:3:1:0:30 O:0:0:0 B:TOUCH:POISON:1d2 F:NEVER_MOVE | DUN_MINE | DUN_TOWER | DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR D:Its a large pile of white flesh. N:49:Giant black ant G:a:D I:110:2d8:8:12:80 W:4:1:0:18 O:0:0:0 B:BITE:HURT:1d4 F:RAND_25 | F:WEIRD_MIND | DROP_SKELETON F:BASH_DOOR | DUN_MINE | WILD_FOREST1 | WILD_FOREST2 | WILD_GRASS | F:DUN_DARKWATER F:ANIMAL D:It is about three feet long. N:50:Salamander G:R:o I:110:4d6:8:12:80 W:7:1:0:40 O:0:0:0 B:BITE:FIRE:1d3 F:RAND_25 | CAN_SWIM | DUN_TOWER | DUN_LAIR | WILD_MOUNT2 | WILD_WASTE2 | DROP_CORPSE F:DUN_DARKWATER F:ANIMAL | IM_FIRE D:A small black and orange lizard. N:51:White harpy G:H:w I:110:3d4:16:18:10 W:5:1:0:31 O:0:0:0 B:CLAW:HURT:1d1 B:CLAW:HURT:1d1 B:BITE:HURT:1d2 F:FEMALE | CAN_FLY | DUN_LAIR | WILD_MOUNT1 | WILD_MOUNT2 F:RAND_50 | DROP_CORPSE F:ANIMAL | EVIL D:A flying, screeching bird with a woman's face. N:52:Blue yeek G:y:b I:110:2d6:18:15:10 W:4:1:0:33 O:25:0:0 B:HIT:HURT:1d5 F:DROP_60 | DUN_MINE | DUN_CITY F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:ANIMAL | IM_ACID | LITE_1 D:A small humanoid figure. N:53:Grip, Farmer Maggot's dog G:C:w I:120:3d8:30:17:0 W:6:2:0:110 O:0:0:0 B:BITE:HURT:1d6 F:UNIQUE | CAN_SPEAK | DUN_LAIR | DUN_DARKWATER F:FORCE_MAXHP | RAND_25 | DROP_CORPSE F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP | NO_FEAR D:A rather vicious dog belonging to Farmer Maggot. It thinks you are D:stealing mushrooms. N:54:Wolf, Farmer Maggot's dog G:C:w I:120:3d8:30:17:0 W:6:2:0:110 O:0:0:0 B:BITE:HURT:1d6 F:UNIQUE | CAN_SPEAK | DUN_LAIR | DUN_DARKWATER F:FORCE_MAXHP | RAND_25 | DROP_CORPSE F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP | NO_FEAR D:A rather vicious dog belonging to Farmer Maggot. It thinks you are D:stealing mushrooms. N:55:Fang, Farmer Maggot's dog G:C:w I:120:3d8:30:17:0 W:6:2:0:110 O:0:0:0 B:BITE:HURT:1d6 F:UNIQUE | CAN_SPEAK | DUN_LAIR | DUN_DARKWATER F:FORCE_MAXHP | RAND_25 | DROP_CORPSE F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP | NO_FEAR D:A rather vicious dog belonging to Farmer Maggot. It thinks you are D:stealing mushrooms. N:56:Giant green frog G:R:g I:110:1d16:12:8:30 W:1:2:0:3 O:0:0:0 B:BITE:HURT:1d3 F:RAND_25 | WILD_SHORE | WILD_SWAMP1 | WILD_SWAMP2 | DROP_CORPSE F:BASH_DOOR | F:ANIMAL D:It is as big as a wolf. N:57:Freesia G:f:u I:120:3d8:30:17:0 W:6:1:0:91 O:0:0:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 F:UNIQUE | DUN_LAIR | WILD_FOREST1 F:FORCE_MAXHP | DROP_SKELETON F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP D:A striped housecat who enjoys hunting. N:58:Green worm mass G:w:g I:100:1d20:7:3:10 W:2:1:0:2 O:0:0:0 B:CRAWL:ACID:1d3 F:RAND_50 | RAND_25 | DUN_LAIR | DUN_DARKWATER | DUN_TEMPLE | WILD_FOREST2 F:STUPID | WEIRD_MIND | MULTIPLY | F:ANIMAL | IM_ACID | CAN_SWIM | F:HURT_LITE | NO_FEAR D:It is a large slimy mass of worms. N:59:Large yellow snake G:J:y I:100:2d11:5:20:75 W:4:1:0:23 O:0:0:0 B:BITE:HURT:1d4 B:CRUSH:HURT:1d6 F:RAND_25 | CAN_SWIM | DUN_LAIR | DUN_DARKWATER | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | WILD_FOREST1 | WILD_GRASS F:ANIMAL D:It is about ten feet long. N:60:Cave spider G:S:D I:120:3d3:8:14:80 W:6:1:0:46 O:0:0:0 B:BITE:HURT:1d4 F:FRIENDS | DUN_MINE | DUN_LAIR | DUN_CAVERN F:DUN_DARKWATER F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | HURT_LITE D:It is a black spider that moves in fits and starts. N:61:Crow G:B:s I:120:4d4:40:15:0 W:7:2:0:58 O:0:0:0 B:BITE:HURT:1d2 B:BITE:HURT:1d2 F:ANIMAL | CAN_FLY | DROP_CORPSE | FORCE_MAXHP | WILD_GRASS F:WILD_FOREST1 | WILD_FOREST2 D:It is a hooded crow, gray except for the black wings and head. N:62:Wild cat G:f:U I:120:4d3:40:15:0 W:7:2:0:93 O:0:0:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 F:BASH_DOOR | DUN_LAIR | DUN_TOWER | DROP_SKELETON | DROP_CORPSE F:ANIMAL | WILD_MOUNT1 D:A larger than normal feline, hissing loudly. Its velvet claws conceal a D:fistful of needles. N:63:Smeagol G:h:u I:130:3d14:20:24:5 W:6:2:0:380 O:50:50:0 B:TOUCH:EAT_GOLD F:UNIQUE | MALE | CAN_SWIM | DROP_SKELETON | DROP_CORPSE F:FORCE_MAXHP | CAN_SPEAK | SMART | F:RAND_50 | RAND_25 | DUN_TOWER | DUN_MINE | DUN_RUIN | WILD_FOREST1 | WILD_MOUNT1 F:ONLY_ITEM | DROP_90 | DROP_GOOD | DROP_GREAT | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | INVISIBLE F:EVIL D:"Down the face of a precipice, sheer and almost smooth it seemed in the pale D:moonlight, a small black shape was moving with its thin limbs splayed out. D:Maybe its soft clinging hands and toes were used, but it looked as if it was D:just creeping down on sticky pads, like some large prowling thing of D:insect-kind. And it was coming down head first, as if it was smelling its D:way. Now and again it lifted its head slowly, turning it right back on its D:long skinny neck, and the hobbits caught a glimpse of two small pale gleaming D:lights, its eyes that blinked at the moon for a moment and then were quickly D:lidded again." N:64:Green ooze G:j:g I:120:2d5:8:12:80 W:3:2:0:8 O:50:0:25 B:CRAWL:ACID:1d3 F:DUN_DARKWATER | DUN_TEMPLE | DUN_MINE | DUN_RUIN F:RAND_50 | RAND_25 | DROP_90 | F:STUPID | EMPTY_MIND | F:IM_ACID | CAN_SWIM | F:NO_CONF | NO_SLEEP | NO_FEAR D:It's green and it's oozing. N:65:Poltergeist G:G:s I:130:3d3:8:13:10 W:5:1:0:70 O:50:5:30 B:TOUCH:TERRIFY F:RAND_50 | RAND_25 | CAN_FLY | DUN_TEMPLE | DUN_GRAVE F:DROP_60 | DROP_90 | F:INVISIBLE | COLD_BLOOD | PASS_WALL | TAKE_ITEM | F:EVIL | UNDEAD | F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_15 | S:BLINK D:It is a ghastly, ghostly form. N:66:Yellow jelly G:j:y I:120:3d22:2:1:99 W:5:1:0:57 O:0:0:0 B:TOUCH:POISON:1d3 F:NEVER_MOVE | DUN_LAIR | DUN_RUIN F:DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_15 | S:DRAIN_MANA D:It's a large pile of yellow flesh. N:67:Metallic blue centipede G:c:B I:120:3d5:6:11:15 W:5:1:0:62 O:0:0:0 B:CRAWL:HURT:1d2 F:RAND_50 | DROP_SKELETON | DUN_LAIR | DUN_MINE F:DUN_DARKWATER F:WEIRD_MIND | BASH_DOOR | WILD_FOREST1| F:ANIMAL D:It is about four feet long and carnivorous. N:68:Raven G:B:D I:120:4d4:40:15:0 W:8:2:0:66 O:0:0:0 B:BITE:HURT:1d3 B:BITE:HURT:1d3 F:ANIMAL | CAN_FLY | DROP_CORPSE | FORCE_MAXHP | WILD_GRASS | F:WILD_FOREST1 | WILD_FOREST2 D:Larger than a crow, pitch black. N:69:Giant white louse G:I:w I:120:1d1:6:3:10 W:5:1:0:3 O:0:0:0 B:BITE:HURT:1d1 F:RAND_50 | RAND_25 | F:MULTIPLY | WEIRD_MIND | F:ANIMAL | DUN_LAIR | DUN_TEMPLE | DUN_RUIN D:It is six inches long. N:70:Piranha G:l:B I:120:5d2:20:10:5 W:9:1:0:91 O:0:0:0 B:BITE:HURT:9d1 F:FRIENDS | AQUATIC | ANIMAL | DUN_DARKWATER | DUN_MINE F:WILD_FOREST1 | WILD_FOREST2 D:Bloodthirsty fish who can smell your blood from a great distance. N:71:Black naga G:n:D I:110:2d18:16:20:120 W:4:1:0:23 O:0:75:20 B:CRUSH:HURT:1d8 F:FEMALE | DUN_LAIR | DUN_RUIN | DUN_TEMPLE F:DUN_DARKWATER F:RAND_25 | DROP_60 | DROP_CORPSE F:BASH_DOOR | CAN_SWIM | F:EVIL D:A large black serpent's body with a female torso. N:72:Spotted mushroom patch G:,:o I:110:1d1:2:3:0 W:5:1:0:19 O:0:0:0 B:SPORE:POISON:2d4 F:NEVER_MOVE | DUN_TOWER | DUN_RUIN | DUN_DARKWATER F:WILD_SWAMP1 | WILD_SWAMP2 | F:STUPID | EMPTY_MIND | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:Yum! It looks quite tasty. N:73:Silver jelly G:j:W I:120:2d32:2:1:99 W:3:2:0:23 O:0:0:0 B:TOUCH:EAT_LITE:1d3 B:TOUCH:EAT_LITE:1d3 F:NEVER_MOVE | DUN_TOWER | DUN_DARKWATER | DUN_LAIR F:STUPID | EMPTY_MIND | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_15 | S:DRAIN_MANA D:It is a large pile of silver flesh that sucks all light from its D:surroundings. N:74:Scruffy looking hobbit G:h:s I:110:3d4:16:11:10 W:6:1:0:30 O:0:50:0 B:HIT:HURT:1d4 B:TOUCH:EAT_GOLD F:MALE | DUN_RUIN | DUN_MINE F:DROP_60 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL D:A short little guy, in bedraggled clothes. He appears to be looking D:for a good tavern. N:75:Giant white ant G:a:w I:110:4d6:8:12:80 W:7:1:0:40 O:0:0:0 B:BITE:HURT:1d4 F:WEIRD_MIND | BASH_DOOR | DUN_MINE | DUN_LAIR | WILD_GRASS F:DUN_DARKWATER F:ANIMAL | DROP_SKELETON D:It is about two feet long and has sharp pincers. N:76:Yellow mold G:m:y I:110:2d24:2:4:99 W:4:1:0:25 O:0:0:0 B:HIT:HURT:1d4 F:NEVER_MOVE | DUN_MINE | DUN_RUIN F:DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a strange growth on the dungeon floor. N:77:Metallic red centipede G:c:R I:120:4d8:8:13:20 W:7:1:0:67 O:0:0:0 B:CRAWL:HURT:1d4 F:RAND_25 | DUN_MINE F:DUN_DARKWATER F:WEIRD_MIND | BASH_DOOR | WILD_FOREST1 | DROP_SKELETON F:ANIMAL D:It is about four feet long and carnivorous. N:78:Yellow worm mass G:w:y I:100:2d12:7:3:10 W:4:2:0:3 O:0:0:0 B:CRAWL:LOSE_DEX:1d3 F:RAND_50 | RAND_25 | CAN_SWIM | DUN_DARKWATER | DUN_LAIR | DUN_MINE F:STUPID | WEIRD_MIND | MULTIPLY | F:ANIMAL | HURT_LITE | NO_FEAR D:It is a large slimy mass of worms. N:79:Clear worm mass G:w:B I:100:2d6:7:3:10 W:3:2:0:2 O:0:0:0 B:CRAWL:POISON:1d2 F:ATTR_CLEAR | CAN_SWIM | DUN_DARKWATER | DUN_LAIR | DUN_MINE F:RAND_50 | RAND_25 | F:STUPID | WEIRD_MIND | INVISIBLE | MULTIPLY | F:ANIMAL | F:IM_POIS | HURT_LITE | NO_FEAR D:It is a disgusting mass of poisonous worms. N:80:Radiation eye G:e:R I:110:3d5:2:5:10 W:6:1:0:42 O:0:0:0 B:GAZE:LOSE_STR:1d6 F:NEVER_MOVE | CAN_FLY | DROP_CORPSE | DUN_TOWER | DUN_TEMPLE F:DUN_DARKWATER F:HURT_LITE | NO_FEAR | LITE_1 S:1_IN_11 | S:DRAIN_MANA D:A disembodied eye, crackling with energy. N:81:Yellow light G:*:y I:120:4d3:8:10:30 W:8:1:0:41 O:0:0:0 B:EXPLODE:BLIND F:EMPTY_MIND | CAN_FLY | NONLIVING | DUN_TOWER | LITE_1 | LITE_2 F:DUN_DARKWATER D:A fast-moving bright light. N:82:Cave lizard G:R:u I:110:4d4:8:12:80 W:7:1:0:49 O:0:0:0 B:BITE:HURT:1d5 F:ANIMAL | CAN_SWIM | DROP_CORPSE | DUN_MINE | DUN_LAIR D:It is an armoured lizard with a powerful bite. N:83:Novice ranger G:p:g I:110:6d6:20:10:5 W:7:1:0:93 O:25:50:25 B:HIT:HURT:1d5 B:HIT:HURT:1d5 F:MALE | F:FORCE_SLEEP | DROP_SKELETON | DROP_CORPSE F:DROP_60 | LITE_1 F:OPEN_DOOR | BASH_DOOR | DUN_CITY | DUN_TEMPLE | DUN_TOWER | WILD_FOREST1 | WILD_FOREST2 | S:1_IN_9 | S:ARROW | MISSILE D:An agile hunter, ready and relaxed. N:84:Blue jelly G:j:b I:110:3d24:2:1:99 W:5:1:0:22 O:0:0:0 B:TOUCH:COLD:1d6 F:NEVER_MOVE | COLD_BLOOD | DUN_TOWER | DUN_MINE | DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_COLD | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR D:It's a large pile of pulsing blue flesh. N:85:Creeping copper coins G:$:u I:100:4d10:3:20:10 W:8:2:0:51 O:100:0:0 B:HIT:HURT:1d4 B:TOUCH:POISON:2d4 F:CHAR_MIMIC | ONLY_GOLD | DROP_1D2 | DUN_MINE F:COLD_BLOOD | BASH_DOOR | F:ANIMAL | IM_POIS | F:NO_CONF | NO_SLEEP D:It is a pile of coins. N:86:Giant white rat G:r:W I:110:2d2:8:6:30 W:5:1:0:9 O:0:0:0 B:BITE:POISON:1d3 F:RAND_25 | DUN_LAIR | DUN_RUIN | DUN_DARKWATER F:MULTIPLY | F:ANIMAL D:It is a very vicious rodent. N:87:Snotling G:o:G I:110:2d10:20:20:30 W:4:1:0:25 O:25:50:0 B:HIT:HURT:1d6 F:MALE | DUN_MINE | DUN_CITY F:FRIENDS | DROP_60 | RAND_50 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | WILD_FOREST1 | WILD_FOREST2 | F:EVIL | ORC | HURT_LITE D:A pathetic breed of tiny snaga-like creatures. They are running D:wild and screaming all the time! N:88:Swordfish G:l:W I:120:4d10:14:15:20 W:8:2:0:97 O:0:0:0 B:STING:HURT:5d1 B:STING:HURT:5d1 F:ANIMAL | AQUATIC | DUN_DARKWATER | DUN_MINE | DUN_TEMPLE F:WILD_GRASS | WILD_SHORE D:A fish with a swordlike upper jaw. N:89:Blue worm mass G:w:b I:100:3d10:7:4:10 W:5:1:0:3 O:0:0:0 B:CRAWL:COLD:1d4 F:RAND_50 | RAND_25 | DUN_RUIN | DUN_DARKWATER | DUN_LAIR F:STUPID | WEIRD_MIND | COLD_BLOOD | MULTIPLY | F:ANIMAL | IM_COLD | CAN_SWIM | F:HURT_LITE | NO_FEAR D:It is a large slimy mass of worms. N:90:Large grey snake G:J:s I:100:2d18:6:22:50 W:4:1:0:20 O:0:0:0 B:BITE:HURT:1d5 B:CRUSH:HURT:1d8 F:RAND_25 | CAN_SWIM | DROP_SKELETON | DROP_CORPSE F:DUN_DARKWATER F:BASH_DOOR | DUN_LAIR | WILD_FOREST2 F:ANIMAL D:It is about ten feet long. N:91:Skeleton kobold G:s:w I:110:4d10:20:16:40 W:8:1:0:77 O:0:0:0 B:HIT:HURT:1d7 F:DUN_GRAVE | DUN_MINE F:DUN_DARKWATER F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a small animated kobold skeleton. N:92:Ewok G:h:G I:110:3d4:10:15:10 W:6:2:0:19 O:50:0:50 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:DUN_TOWER | WILD_FOREST2 | SILLY | LITE_1 F:DROP_60 | OPEN_DOOR | BASH_DOOR | FRIENDS | DROP_CORPSE | S:1_IN_8 S:ARROW D:A cute, furry little animal, full of merchandising potential. N:93:Novice mage G:p:R I:110:3d7:20:6:10 W:6:2:0:68 O:25:0:75 B:HIT:HURT:1d4 F:MALE | F:FORCE_SLEEP | F:FRIENDS | DROP_60 | DUN_TOWER | DUN_MINE | DUN_CITY | WILD_GRASS | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | LITE_2 S:1_IN_3 | S:BLINK | BLIND | CONF | MISSILE D:He is leaving behind a trail of dropped spell components. N:94:Green naga G:n:g I:110:4d14:18:20:120 W:7:1:0:44 O:0:25:0 B:CRUSH:HURT:1d8 B:SPIT:ACID:2d6 F:FEMALE | DUN_LAIR | DUN_RUIN F:DUN_DARKWATER F:RAND_25 | TAKE_ITEM | DROP_60 | DROP_CORPSE F:BASH_DOOR | CAN_SWIM | WILD_SHORE | F:EVIL | IM_ACID D:A large green serpent with a female's torso. Her green skin glistens with D:acid. N:95:Giant leech G:w:v I:120:4d9:10:15:50 W:8:1:0:56 O:0:0:0 B:BITE:HURT:3d1 B:BITE:HURT:3d1 F:ANIMAL | AQUATIC | WEIRD_MIND | RAND_25 | DUN_DARKWATER | DUN_LAIR D:Yech! The disgusting thing only wants your blood! N:96:Barracuda G:l:b I:120:6d9:20:30:20 W:11:2:0:215 O:0:0:0 B:BITE:HURT:10d1 B:BITE:HURT:1d10 F:AQUATIC | ANIMAL | WILD_SHORE | DUN_DARKWATER | DUN_LAIR | DUN_TEMPLE D:A predatory fish with razor-sharp teeth. N:97:Novice paladin G:p:w I:110:4d9:20:15:5 W:7:1:0:140 O:0:75:25 B:HIT:HURT:1d7 B:HIT:HURT:1d7 F:MALE | F:FORCE_SLEEP | DUN_TEMPLE | DUN_MINE | DUN_CITY F:DROP_60 | WILD_GRASS | DROP_SKELETON | DROP_CORPSE | F:OPEN_DOOR | BASH_DOOR | LITE_1 S:1_IN_9 | S:SCARE | CAUSE_1 D:An adventurer both devoutly religious and skillful in combat. D:He seems to consider you an agent of the devil. N:98:Zog G:h:b I:110:4d18:20:25:20 W:7:1:0:49 O:50:0:25 B:HIT:HURT:1d8 B:HIT:HURT:1d8 B:DROOL F:DUN_LAIR | DUN_MINE | DUN_CITY | SILLY | F:DUN_DARKWATER F:EVIL | OPEN_DOOR | BASH_DOOR | DROP_90 | DROP_SKELETON D:Drooling, insectoid aliens with disgusting habits. N:99:Blue ooze G:j:b I:110:2d5:8:11:80 W:4:1:0:8 O:50:25:25 B:CRAWL:COLD:1d4 F:RAND_50 | RAND_25 | DROP_60 | DUN_TEMPLE | DUN_TOWER | DUN_LAIR | DUN_DARKWATER F:STUPID | EMPTY_MIND | CAN_SWIM | F:IM_COLD | F:NO_CONF | NO_SLEEP | NO_FEAR D:It's blue and it's oozing. N:100:Green glutton ghost G:G:g I:130:3d4:10:12:10 W:6:1:0:57 O:30:30:30 B:TOUCH:EAT_FOOD:1d1 F:RAND_50 | RAND_25 | DUN_GRAVE | DUN_RUIN | DUN_TOWER | SILLY | F:DUN_DARKWATER F:DROP_60 | DROP_90 | CAN_FLY | F:INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | NO_CONF | NO_SLEEP D:It is a very ugly green ghost with a voracious appetite. N:101:Green jelly G:j:g I:120:3d46:2:1:99 W:6:1:0:65 O:0:0:0 B:TOUCH:ACID:1d2 F:NEVER_MOVE | DUN_RUIN | DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_ACID | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a large pile of pulsing green flesh. N:102:Large kobold G:k:w I:110:4d23:20:20:30 W:8:1:0:80 O:0:90:0 B:HIT:HURT:1d10 F:DROP_90 | DUN_MINE | DUN_CITY F:DUN_DARKWATER F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | IM_POIS D:It a man-sized figure with the all too recognizable face of a kobold. N:103:Grey icky thing G:i:s I:110:2d12:14:10:15 W:3:1:0:11 O:0:0:0 B:TOUCH:HURT:1d5 F:RAND_50 | CAN_SWIM | DROP_CORPSE | DUN_DARKWATER F:EMPTY_MIND D:It is a smallish, slimy, icky, nasty creature. N:104:Disenchanter eye G:e:v I:100:3d14:2:8:10 W:5:2:0:29 O:0:0:0 B:GAZE:UN_BONUS F:ATTR_MULTI | ATTR_ANY | RES_DISE | DROP_CORPSE | DUN_TOWER F:NEVER_MOVE | CAN_FLY | F:HURT_LITE | NO_FEAR S:1_IN_9 | S:DRAIN_MANA D:A disembodied eye, crackling with magic. N:105:Red worm mass G:w:r I:100:3d10:7:8:10 W:6:1:0:8 O:0:0:0 B:CRAWL:FIRE:1d6 F:RAND_50 | RAND_25 | DUN_LAIR | DUN_MINE | DUN_RUIN F:STUPID | EMPTY_MIND | MULTIPLY | BASH_DOOR | F:ANIMAL | IM_FIRE | CAN_SWIM | F:HURT_LITE | NO_FEAR D:It is a large slimy mass of worms. N:106:Copperhead snake G:J:o I:110:5d4:6:16:1 W:9:1:0:120 O:0:0:0 B:BITE:POISON:2d4 F:RAND_50 | CAN_SWIM | DUN_LAIR | DUN_RUIN | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | WILD_GRASS | WILD_FOREST1 | WILD_FOREST2 F:ANIMAL | IM_POIS D:It has a copper head and sharp venomous fangs. N:107:Death sword G:|:W I:130:6d6:20:20:0 W:12:5:0:230 O:50:0:50 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:NEVER_MOVE | NONLIVING | NO_FEAR | DUN_TOWER | DUN_MINE | DUN_RUIN F:STUPID | EMPTY_MIND | COLD_BLOOD | CHAR_MIMIC | NO_CONF | NO_SLEEP | F:DROP_90 | EVIL | IM_FIRE | FORCE_MAXHP | IM_POIS | D:A bloodthirsty blade lurking for prey. Beware! N:108:Purple mushroom patch G:,:v I:110:1d1:2:1:0 W:8:2:0:51 O:0:0:0 B:SPORE:LOSE_CON:1d2 B:SPORE:LOSE_CON:1d2 B:SPORE:LOSE_CON:1d2 F:NEVER_MOVE | F:STUPID | EMPTY_MIND | DUN_TOWER | DUN_LAIR | DUN_DARKWATER F:NO_CONF | NO_SLEEP | NO_FEAR D:Yum! It looks quite tasty. N:109:Novice priest G:p:B I:110:7d3:20:9:5 W:13:2:0:140 O:20:50:20 B:HIT:HURT:1d5 F:MALE | GOOD | F:FORCE_SLEEP | DUN_TEMPLE | DUN_MINE | DUN_CITY F:DROP_SKELETON | DROP_CORPSE | F:FRIENDS | DROP_60 | WILD_GRASS F:OPEN_DOOR | BASH_DOOR | LITE_2 S:1_IN_12 | S:HEAL | SCARE | CAUSE_1 D:He is tripping over his priestly robes. N:110:Novice warrior G:p:U I:110:4d7:20:13:5 W:8:2:0:36 O:0:100:0 B:HIT:HURT:1d7 B:HIT:HURT:1d6 F:MALE | F:FRIENDS | DROP_60 | DUN_TEMPLE | DUN_TOWER | DUN_MINE | DUN_CITY F:WILD_GRASS | LITE_2 F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE D:He looks inexperienced but tough. N:111:Nibelung G:h:D I:110:4d6:20:15:5 W:8:2:0:36 O:90:0:0 B:HIT:HURT:1d6 B:TOUCH:EAT_GOLD F:MALE | F:FRIENDS | DROP_60 | DUN_MINE | DUN_CITY F:DUN_DARKWATER F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:HURT_LITE | RES_DISE | DROP_SKELETON | DROP_CORPSE D:Night dwarfs collecting new riches for their master, Alberich. N:112:Disembodied hand that strangled people G:z:g I:130:6d7:30:20:20 W:11:2:0:260 O:0:0:0 B:CRUSH:HURT:1d8 F:DUN_TOWER | DUN_TEMPLE | DUN_RUIN F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_POIS | CAN_FLY | SILLY F:NO_CONF | NO_SLEEP | NO_FEAR D:Even today, nobody knows where it lurks... N:113:Brown mold G:m:u I:110:4d22:2:12:99 W:8:1:0:91 O:0:0:0 B:HIT:CONFUSE:1d4 F:NEVER_MOVE | DUN_LAIR | DUN_DARKWATER | DUN_MINE F:STUPID | EMPTY_MIND | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:A strange brown growth on the dungeon floor. N:114:Giant brown bat G:b:u I:130:5d5:10:12:30 W:10:1:0:178 O:0:0:0 B:BITE:HURT:1d5 F:RAND_50 | CAN_FLY | DUN_DARKWATER | DUN_LAIR | DUN_MINE F:WILD_MOUNT1 | WILD_MOUNT2 F:WILD_FOREST1 | WILD_FOREST2 F:ANIMAL | DROP_CORPSE D:It screeches as it attacks. N:115:Rat-thing G:r:R I:120:6d11:12:15:20 W:11:1:0:210 O:0:0:0 B:BITE:HURT:2d2 B:BITE:HURT:3d2 F:EVIL | ANIMAL | DROP_CORPSE | DUN_MINE | DUN_RUIN | DUN_TEMPLE F:DUN_DARKWATER S:1_IN_9 S:SCARE D:A ratlike creature with a humanlike face. "The bones of the D:tiny paws, it is rumoured, imply prehensile characteristics D:more typical of a diminutive monkey than a of a rat; while the D:small skull with its savage yellow fangs is of the utmost D:anomalousness, appearing from certain angles like a miniature, D:monstrously degrade parody of a human skull." N:116:Novice archer G:p:G I:120:4d9:20:10:5 W:8:2:0:102 O:20:50:0 B:HIT:HURT:1d4 B:HIT:HURT:1d4 F:MALE | DUN_TEMPLE | DUN_TOWER | DUN_MINE | DUN_CITY F:FORCE_SLEEP | WILD_GRASS | DROP_SKELETON | DROP_CORPSE F:ONLY_GOLD | DROP_1D2 | LITE_1 F:OPEN_DOOR | BASH_DOOR S:1_IN_3 | S:ARROW D:A nasty little fellow with a bow and arrow. N:117:Creeping silver coins G:$:s I:100:5d13:4:18:10 W:10:2:0:83 O:100:0:0 B:HIT:HURT:1d6 B:TOUCH:POISON:2d6 F:CHAR_MIMIC | ONLY_GOLD | DROP_60 | DROP_1D2 | DUN_MINE F:COLD_BLOOD | BASH_DOOR | F:ANIMAL | IM_POIS | NO_CONF | NO_SLEEP | D:It is a pile of coins, crawling forward on thousands of tiny legs. N:118:Snaga G:o:G I:110:3d18:20:16:30 W:6:1:0:50 O:20:50:5 B:HIT:HURT:1d8 F:MALE | DUN_MINE | DUN_CITY F:FRIENDS | DROP_60 | WILD_FOREST1 | WILD_WASTE1 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | HURT_LITE D:He is one of the many weaker 'slave' orcs, often mistakenly known as a D:goblin. N:119:Rattlesnake G:J:r I:110:5d7:6:12:1 W:10:1:0:102 O:0:0:0 B:BITE:POISON:2d8 F:RAND_50 | CAN_SWIM | DUN_LAIR | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | WILD_WASTE1 | WILD_MOUNT1 | WILD_MOUNT2 F:ANIMAL | IM_POIS D:It is recognized by the hard-scaled end of its body that is often rattled D:to frighten its prey. N:120:Giant slug G:w:U I:100:6d14:10:12:25 W:11:1:0:175 O:0:0:0 B:BITE:ACID:2d4 B:BITE:ACID:2d4 F:DUN_DARKWATER | DUN_LAIR | WILD_FOREST1 F:ANIMAL | EMPTY_MIND | KILL_ITEM | KILL_BODY | CAN_SWIM F:DROP_CORPSE S:1_IN_10 S:BR_ACID D:It is slowly making its way towards you, eating everything in D:its path... N:121:Giant pink frog G:R:R I:110:4d8:12:16:50 W:8:2:0:82 O:0:0:0 B:BITE:LOSE_STR:2d4 F:RAND_50 | WILD_SHORE | WILD_SWAMP1 | WILD_SWAMP2 F:DUN_DARKWATER F:BASH_DOOR | DROP_CORPSE F:ANIMAL D:It looks poisonous. N:122:Dark elf G:h:D I:110:6d9:20:10:20 W:11:2:0:135 O:20:20:50 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:MALE | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | F:DROP_90 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | HURT_LITE S:1_IN_10 | S:CONF | DARKNESS | MISSILE D:An elven figure with jet black skin and white hair, his eyes are large and D:twisted with evil. N:123:Zombified kobold G:z:s I:110:5d8:20:14:30 W:10:1:0:114 O:0:0:0 B:HIT:HURT:1d3 B:HIT:HURT:1d3 F:DUN_GRAVE | DUN_RUIN F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is an animated kobold corpse. Flesh falls off in large chunks as it D:shambles forward. N:124:Crypt Creep G:s:D I:110:5d8:20:15:14 W:10:2:0:159 O:0:0:0 B:CLAW:HURT:1d2 B:CLAW:HURT:1d2 B:BITE:POISON F:RAND_25 | DUN_GRAVE F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | FRIENDS | F:EVIL | UNDEAD | IM_POIS | F:NO_CONF | NO_SLEEP | HURT_LITE S:1_IN_10 S:CAUSE_1 | S_UNDEAD D:Frightening skeletal figures in black robes. N:125:Rotting corpse G:z:R I:110:6d8:20:15:20 W:12:1:0:144 O:0:0:0 B:CLAW:POISON:1d3 B:CLAW:POISON:1d3 F:OPEN_DOOR | BASH_DOOR | FRIENDS | DUN_GRAVE F:NO_CONF | NO_SLEEP | UNDEAD | EVIL | NO_FEAR | IM_POIS F:IM_COLD | COLD_BLOOD | EMPTY_MIND D:Corpses awakened from their sleep by dark sorceries. N:126:Cave orc G:o:U I:110:5d15:20:18:30 W:9:1:0:97 O:20:80:0 B:HIT:HURT:1d8 F:MALE | DUN_MINE | DUN_CITY F:FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | HURT_LITE D:He is often found in huge numbers in deep caves. N:127:Wood spider G:S:U I:120:7d2:8:10:60 W:13:2:0:200 O:0:0:0 B:BITE:HURT:1d3 B:STING:POISON:1d4 F:FRIENDS | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | WILD_FOREST1 | WILD_FOREST2 F:ANIMAL | IM_POIS D:It scuttles towards you. N:128:Manes G:u:R I:110:5d10:20:18:30 W:10:2:0:51 O:0:0:0 B:HIT:HURT:1d8 F:FRIENDS | DUN_HELL | DUN_TEMPLE F:OPEN_DOOR | BASH_DOOR | F:EVIL | DEMON | IM_FIRE | NO_FEAR D:It is a minor but aggressive demon. N:129:Bloodshot eye G:e:r I:110:6d11:2:6:10 W:12:3:0:115 O:0:0:0 B:GAZE:BLIND:2d6 F:NEVER_MOVE | CAN_FLY | DROP_CORPSE | DUN_TOWER | DUN_MINE | DUN_LAIR F:HURT_LITE | NO_FEAR S:1_IN_7 | S:DRAIN_MANA D:A disembodied eye, bloodshot and nasty. N:130:Red naga G:n:R I:110:6d11:20:40:120 W:11:2:0:145 O:50:0:50 B:CRUSH:HURT:1d10 B:BITE:LOSE_STR:1d4 F:FEMALE | CAN_SWIM | DUN_LAIR | WILD_SHORE | F:DUN_DARKWATER F:RAND_25 | DROP_60 | F:TAKE_ITEM | BASH_DOOR | DROP_CORPSE | F:EVIL D:A large red snake with a woman's torso. N:131:Red jelly G:j:R I:110:5d32:2:4:99 W:9:1:0:80 O:0:0:0 B:TOUCH:LOSE_STR:1d5 F:NEVER_MOVE | DUN_TOWER | DUN_DARKWATER F:STUPID | EMPTY_MIND | F:HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a large pulsating mound of red flesh. N:132:Green icky thing G:i:G I:110:5d6:14:16:20 W:9:2:0:29 O:0:0:0 B:TOUCH:ACID:2d5 F:RAND_50 | CAN_SWIM | DROP_CORPSE | DUN_MINE | DUN_RUIN | DUN_DARKWATER F:EMPTY_MIND | LITE_2 F:IM_ACID D:It is a smallish, slimy, icky, acidic creature. N:133:Lost soul G:G:u I:110:6d2:12:8:10 W:12:2:0:38 O:70:0:30 B:HIT:HURT:2d2 B:TOUCH:LOSE_WIS F:RAND_50 | DROP_60 | DROP_90 | CAN_FLY | DUN_GRAVE | DUN_MINE | DUN_TEMPLE F:DUN_DARKWATER F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL | F:EVIL | UNDEAD | F:IM_COLD | NO_CONF | NO_SLEEP S:1_IN_15 | S:TPORT | DRAIN_MANA D:It is almost insubstantial. N:134:Night lizard G:R:b I:110:7d3:20:20:30 W:13:2:0:175 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 F:ANIMAL | CAN_SWIM | DUN_LAIR | DUN_MINE | WILD_MOUNT2 | DROP_CORPSE F:DUN_DARKWATER D:It is a black lizard with overlapping scales and a powerful jaw. N:135:Mughash the Kobold Lord G:k:b I:110:5d24:20:15:20 W:9:3:0:330 O:0:100:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:HIT:HURT:1d10 F:UNIQUE | MALE | CAN_SPEAK F:FORCE_MAXHP | DUN_MINE F:ESCORT | ESCORTS | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | IM_POIS D:Strong and powerful, for a kobold. N:136:Skeleton orc G:s:w I:110:7d8:20:18:40 W:13:1:0:205 O:0:0:0 B:HIT:HURT:2d8 F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | UNDEAD | DUN_GRAVE | DUN_CITY | DUN_MINE F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is an animated orc skeleton. N:137:Wormtongue, Agent of Saruman G:p:D I:115:6d32:20:15:20 W:12:2:0:630 O:10:50:40 B:HIT:HURT:1d5 B:HIT:HURT:1d5 B:TOUCH:EAT_GOLD F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_GREAT | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON F:EVIL | RES_TELE | LITE_2 S:1_IN_5 | S:HEAL | SLOW | TRAPS | BO_COLD | BA_POIS D:He's been spying for Saruman. He is a snivelling wretch with no morals. D:"At [Theoden's] feet upon the steps sat a wizened figure of a man, with a D:pale wise face and heavy-lidded eyes...he laughed grimly, as he lifted his D:heavy lids for a moment and gazed on the strangers with dark eyes." N:138:Robin Hood, the Outlaw G:p:G I:120:12d10:20:15:20 W:12:2:0:1250 O:20:80:0 B:HIT:HURT:1d5 B:HIT:HURT:1d5 B:TOUCH:EAT_GOLD B:TOUCH:EAT_ITEM F:UNIQUE | MALE | FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_GREAT F:WILD_FOREST1 | WILD_FOREST2 | WILD_GRASS | DUN_LAIR F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | EVIL | DROP_SKELETON | DROP_CORPSE S:1_IN_5 S:ARROW | HEAL | TRAPS D:The legendary archer steals from the rich (you qualify). N:139:Nurgling G:u:U I:110:4d15:20:16:30 W:8:2:0:25 O:0:0:0 B:BITE:DISEASE:1d8 F:FRIENDS | DUN_LAIR | DUN_HELL | DUN_TEMPLE F:OPEN_DOOR | BASH_DOOR | IM_POIS | F:EVIL | DEMON | IM_FIRE | NO_FEAR D:It is a minor demon servitor of Nurgle. It looks like a hairless D:teddy bear, with twisted eyes and rotting, ghoulish skin. N:140:Lagduf, the Snaga G:o:u I:110:6d24:20:20:30 W:11:2:0:660 O:10:90:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:HIT:HURT:1d9 B:HIT:HURT:1d9 F:UNIQUE | MALE | F:FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DUN_MINE F:OPEN_DOOR | BASH_DOOR | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:EVIL | ORC D:A captain of a regiment of weaker orcs, Lagduf keeps his troop in order D:with displays of excessive violence. D:"I've told you twice that Gorbag's swine got to the gate first, and none D:of ours got out. Lagduf and Muzgash ran through, but they were shot." N:141:Brown yeek G:y:u I:110:6d4:18:12:10 W:12:1:0:190 O:0:50:0 B:HIT:HURT:1d6 F:DROP_60 | DROP_CORPSE | DUN_MINE F:OPEN_DOOR | BASH_DOOR | F:ANIMAL | IM_ACID | LITE_1 D:It is a strange small humanoid. N:142:Novice ranger G:p:g I:110:6d6:20:8:5 W:11:1:0:160 O:0:80:0 B:HIT:HURT:1d5 B:HIT:HURT:1d5 F:MALE | DUN_TOWER | DUN_MINE | DUN_LAIR | DUN_CITY F:WILD_FOREST1 | WILD_FOREST2 F:FORCE_SLEEP | FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | LITE_2 S:1_IN_9 | S:ARROW | MISSILE D:An agile hunter, ready and relaxed. N:143:Giant salamander G:R:r I:110:9d4:6:20:1 W:17:1:0:380 O:0:0:0 B:BITE:FIRE:3d6 F:FORCE_SLEEP | DUN_TOWER | DUN_LAIR | WILD_MOUNT2 | WILD_WASTE2 F:DUN_DARKWATER F:RAND_25 | LITE_1 F:ANIMAL | IM_FIRE | CAN_SWIM | DROP_CORPSE S:1_IN_9 S:BR_FIRE D:A large black and yellow lizard. You'd better run away! N:144:Space monster G:.:d I:110:7d21:30:15:20 W:14:2:0:185 O:0:0:0 B:HIT:TERRIFY:1d4 F:DUN_MINE | DUN_PLANAR | DUN_HORROR | DUN_TOWER F:PASS_WALL | NO_CONF | NO_SLEEP | NONLIVING | IM_ACID | CAN_FLY D:A black hole in the fabric of reality. N:145:Carnivorous flying monkey G:H:R I:110:7d22:30:15:20 W:14:2:0:220 O:0:0:0 B:CLAW:HURT:1d10 B:CLAW:HURT:1d10 B:BITE:HURT:5d1 F:ANIMAL | CAN_FLY | DUN_TEMPLE | DUN_HORROR F:WILD_FOREST1 | WILD_FOREST2 | WILD_MOUNT2 F:DROP_CORPSE D:It looks fantastic, yet frightening. N:146:Green mold G:m:g I:110:6d22:2:15:75 W:11:2:0:145 O:0:0:0 B:HIT:TERRIFY:1d4 F:NEVER_MOVE | DUN_RUIN | DUN_DARKWATER | WILD_FOREST2 F:STUPID | EMPTY_MIND | F:IM_ACID | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a strange growth on the dungeon floor. N:147:Novice paladin G:p:w I:110:10d4:20:15:5 W:19:2:0:430 O:30:60:10 B:HIT:HURT:1d7 B:HIT:HURT:1d7 F:MALE | GOOD | DUN_TEMPLE | DUN_MINE | DUN_CITY F:WILD_GRASS | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | F:FRIENDS | DROP_60 | LITE_2 F:OPEN_DOOR | BASH_DOOR S:1_IN_9 | S:SCARE | CAUSE_1 D:He thinks you are an agent of the devil. N:148:Lemure G:u:o I:110:6d14:20:16:30 W:11:3:0:63 O:0:0:0 B:HIT:HURT:1d8 F:FRIENDS | DUN_HELL | DUN_LAIR | DUN_CAVERN F:OPEN_DOOR | BASH_DOOR | F:EVIL | DEMON | IM_FIRE | NO_FEAR D:It is the larval form of a major demon. N:149:Hill orc G:o:s I:110:5d17:20:16:30 W:10:1:0:114 O:10:80:10 B:HIT:HURT:1d10 F:MALE | DUN_LAIR | DUN_MINE | DUN_CITY F:FRIENDS | DROP_60 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | ORC | HURT_LITE D:He is a hardy well-weathered survivor. N:150:Bandit G:p:D I:110:7d14:20:12:10 W:14:2:0:150 O:30:70:0 B:HIT:HURT:2d4 B:TOUCH:EAT_GOLD F:MALE | DUN_LAIR | DUN_RUIN | DUN_DARKWATER | WILD_GRASS | WILD_FOREST1 F:DROP_1D2 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL D:He is after your cash! N:151:Hunting hawk of Julian G:B:u I:120:2d24:30:15:10 W:3:2:0:27 O:0:0:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d4 F:ANIMAL | NO_FEAR | CAN_FLY | WILD_FOREST1 | WILD_FOREST2 | DROP_CORPSE D:Trained to hunt and kill without fear. N:152:Phantom warrior G:G:B I:110:6d4:20:15:40 W:12:1:0:30 O:0:0:0 B:HIT:HURT:1d11 B:HIT:HURT:1d11 F:DUN_GRAVE | DUN_TOWER F:PASS_WALL | NO_SLEEP | FRIENDS | COLD_BLOOD | NONLIVING | F:NO_FEAR | EMPTY_MIND | CAN_FLY D:Creatures that are half real, half illusion. N:153:Gremlin G:u:u I:110:5d5:30:15:20 W:10:3:0:44 O:0:0:0 B:CLAW:EAT_FOOD:1d2 B:CLAW:EAT_FOOD:1d2 B:BITE:EAT_FOOD:1d3 F:DUN_TEMPLE | DUN_HORROR F:MULTIPLY | IM_POIS | HURT_LITE | EVIL | DEMON | OPEN_DOOR | F:TAKE_ITEM | CAN_SWIM | SILLY D:Don't splash water on them, and don't feed them after midnight! N:154:Yeti G:Y:w I:110:8d9:20:12:10 W:15:3:0:320 O:0:0:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d4 F:OPEN_DOOR | BASH_DOOR | DUN_LAIR | WILD_MOUNT2 | DROP_CORPSE F:ANIMAL | IM_COLD D:A large white figure covered in shaggy fur. N:155:Bloodshot icky thing G:i:r I:110:5d8:14:10:20 W:10:3:0:38 O:0:0:0 B:TOUCH:HURT:1d4 B:CRAWL:ACID:2d4 F:RAND_50 | F:EMPTY_MIND | CAN_SWIM | DROP_CORPSE | F:IM_POIS | DUN_DARKWATER | DUN_LAIR | WILD_WASTE1 S:1_IN_11 | S:DRAIN_MANA D:It is a strange, slimy, icky creature. N:156:Giant grey rat G:r:s I:110:2d2:8:6:20 W:14:1:0:62 O:0:0:0 B:BITE:POISON:1d4 F:RAND_25 | F:MULTIPLY | DUN_DARKWATER | DUN_LAIR | DUN_RUIN F:ANIMAL | IM_POIS D:It is a rodent of unusual size. N:157:Black harpy G:H:D I:120:4d5:16:11:10 W:8:1:0:155 O:0:0:0 B:CLAW:HURT:1d2 B:CLAW:HURT:1d2 B:BITE:HURT:1d3 F:FEMALE | CAN_FLY | DUN_LAIR | WILD_MOUNT1 | WILD_MOUNT2 | DROP_CORPSE F:RAND_25 | F:ANIMAL | EVIL D:A woman's face on the body of a vicious black bird. N:158:Skaven G:r:G I:110:7d11:15:20:20 W:14:1:0:44 O:40:40:20 B:HIT:HURT:1d4 B:HIT:HURT:1d4 F:EVIL | FRIENDS | DROP_60 | DROP_90 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | MALE | DUN_DARKWATER | DUN_RUIN F:WILD_WASTE1 | WILD_WASTE2 | WILD_SWAMP1 | WILD_SWAMP2 D:A mutated rat-creature from the great waste, it is vaguely D:humanoid in appearance and walks on its hind legs. This race D:serves chaos fervently and is greatly feared by others. N:159:The wounded bear G:q:r I:110:5d16:10:18:10 W:10:1:0:108 O:0:0:0 B:CLAW:HURT:1d5 B:CLAW:HURT:1d5 B:BITE:HURT:1d10 F:BASH_DOOR | FORCE_MAXHP | FORCE_SLEEP | UNIQUE | DROP_CORPSE F:ANIMAL | WILD_FOREST1 | WILD_FOREST2 | WILD_GRASS | WILD_MOUNT1 D:A wounded bear, who has occasionally attacked humans. N:160:Portuguese man-o-war G:j:v I:110:1d80:20:15:75 W:2:2:0:11 O:0:0:0 B:TOUCH:PARALYZE:1d6 B:TOUCH:PARALYZE:1d6 F:ANIMAL | AQUATIC | IM_POIS | WILD_SHORE | WILD_OCEAN D:A strange water creature, whose touch can be deadly. N:161:Rock mole G:r:s I:110:7d15:20:15:75 W:14:2:0:390 O:0:0:0 B:BITE:HURT:1d10 B:BITE:HURT:1d10 F:WEIRD_MIND | BASH_DOOR | KILL_WALL | KILL_ITEM | DROP_CORPSE F:ANIMAL | DUN_MINE F:DUN_DARKWATER D:Despite its minuscule size, this mole creature has fangs powerful D:enough to bore through solid rock. N:162:Orc shaman G:o:b I:110:8d6:20:10:20 W:15:1:0:210 O:0:0:100 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:MALE | F:FORCE_SLEEP | DROP_90 | DUN_CITY | WILD_FOREST1 | WILD_FOREST2 | F:WILD_SWAMP1 | WILD_SWAMP2 | WILD_MOUNT1 | WILD_MOUNT2 F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | ORC | HURT_LITE S:1_IN_8 | S:BLINK | CAUSE_1 | MISSILE D:An orc dressed in skins who gestures wildly. N:163:Baby blue dragon G:d:B I:110:7d10:20:15:70 W:13:2:0:400 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:DUN_LAIR | DUN_CAVERN | DUN_TOWER F:FORCE_MAXHP | FORCE_SLEEP | DROP_CORPSE F:ONLY_GOLD | DROP_60 | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | DRAGON | IM_ELEC S:1_IN_11 | S:BR_ELEC D:This hatchling dragon is still soft, its eyes unaccustomed to light and D:its scales a pale blue. N:164:Baby white dragon G:d:W I:110:7d10:20:15:70 W:13:2:0:400 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:DUN_LAIR | DUN_CAVERN | DUN_TOWER F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE F:ONLY_GOLD | DROP_60 | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | F:EVIL | DRAGON | IM_COLD S:1_IN_11 | S:BR_COLD D:This hatchling dragon is still soft, its eyes unaccustomed to light and D:its scales a pale white. N:165:Baby green dragon G:d:G I:110:7d10:20:15:70 W:14:2:0:500 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:DUN_LAIR | DUN_CAVERN | DUN_TOWER F:DUN_DARKWATER F:FORCE_MAXHP | FORCE_SLEEP | F:ONLY_GOLD | DROP_60 | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | CAN_FLY | DROP_CORPSE F:EVIL | DRAGON | IM_POIS S:1_IN_11 | S:BR_POIS D:This hatchling dragon is still soft, its eyes unaccustomed to light and D:its scales a sickly green. N:166:Baby black dragon G:d:s I:110:7d10:20:15:70 W:13:2:0:400 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:DUN_LAIR | DUN_CAVERN | DUN_TOWER F:DUN_DARKWATER F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE F:ONLY_GOLD | DROP_60 | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | F:EVIL | DRAGON | IM_ACID S:1_IN_11 | S:BR_ACID D:This hatchling dragon is still soft, its eyes unaccustomed to light and D:its scales a dull black. N:167:Baby red dragon G:d:R I:110:7d12:20:15:70 W:14:2:0:470 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:DUN_LAIR | DUN_CAVERN | DUN_TOWER F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE F:ONLY_GOLD | DROP_60 | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | F:EVIL | DRAGON | IM_FIRE S:1_IN_11 | S:BR_FIRE D:This hatchling dragon is still soft, its eyes unaccustomed to light and D:its scales a pale red. N:168:Giant red ant G:a:R I:110:9d3:12:17:60 W:17:2:0:225 O:0:0:0 B:BITE:HURT:1d4 B:STING:LOSE_STR:1d4 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_MINE | WILD_MOUNT1 | DROP_SKELETON F:DUN_DARKWATER F:ANIMAL D:It is large and has venomous mandibles. N:169:Brodda, the Easterling G:p:o I:110:6d25:20:20:20 W:12:2:0:530 O:10:90:0 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:HIT:HURT:1d12 F:UNIQUE | MALE | EVIL | DUN_RUIN F:FORCE_MAXHP | CAN_SPEAK | WILD_WASTE1 | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | LITE_2 D:A nasty piece of work, Brodda picks on defenseless women and children. D:"And Morwen was gone. Empty stood her house, broken and cold. It was D:more than a year since she departed to Doriath. Brodda the Easterling D:(who had wedded Morwen's kinswoman Airin) had plundered her house, and D:taken all that was left of her goods." N:170:Bloodfang the Wolf G:C:R I:120:5d6:30:18:20 W:10:1:0:145 O:0:0:0 B:BITE:HURT:1d10 B:BITE:HURT:1d10 F:BASH_DOOR | WILD_GRASS | WILD_FOREST1 | DROP_CORPSE F:ANIMAL | UNIQUE | FORCE_MAXHP D:It has been terrorizing the nearby villages. N:171:King cobra G:J:g I:110:8d7:8:15:1 W:15:2:0:280 O:0:0:0 B:SPIT:BLIND:1d4 B:BITE:POISON:5d4 F:RAND_50 | DUN_LAIR | WILD_SWAMP1 | WILD_SWAMP2 | WILD_FOREST1 F:DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | CAN_SWIM | F:ANIMAL | IM_POIS D:It is a large snake with a hooded face. N:172:Eagle G:B:u I:120:2d28:30:20:10 W:3:2:0:12 O:0:0:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d6 F:ANIMAL | CAN_FLY | WILD_WASTE1 | WILD_MOUNT2 | WILD_FOREST2 F:DROP_CORPSE D:A magnificent huge predatory bird. N:173:War bear G:q:u I:110:8d9:10:25:10 W:16:1:0:205 O:0:0:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:HURT:1d6 F:WEIRD_MIND | BASH_DOOR | FRIENDS | DROP_SKELETON | DROP_CORPSE F:ANIMAL | DUN_TEMPLE | DUN_CITY D:Bears with tusks, trained to kill. N:174:Killer bee G:I:y I:120:3d2:12:25:10 W:17:2:0:270 O:0:0:0 B:STING:POISON:1d4 B:STING:LOSE_STR:1d4 F:WEIRD_MIND | FRIENDS | CAN_FLY | DUN_RUIN | WILD_FOREST1 F:ANIMAL D:It is poisonous and aggressive. N:175:Giant spider G:S:s I:110:7d10:8:10:80 W:14:2:0:205 O:0:0:0 B:BITE:POISON:3d6 B:BITE:POISON:3d6 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_MINE | DROP_SKELETON | WILD_WASTE1 F:DUN_DARKWATER F:ANIMAL | IM_POIS D:It is a vast black spider whose bulbous body is bloated with poison. N:176:Giant white tick G:S:w I:100:9d8:12:15:20 W:17:2:0:225 O:0:0:0 B:BITE:POISON:2d6 F:WEIRD_MIND | BASH_DOOR | CAN_FLY | F:ANIMAL | IM_POIS | DUN_LAIR | DUN_DARKWATER | DUN_RUIN D:It is moving slowly towards you. N:177:The Borshin G:g:w I:110:9d17:40:20:0 W:17:2:0:460 O:0:0:0 B:HIT:HURT:2d11 B:CRUSH:HURT:2d15 B:TOUCH:TERRIFY F:DUN_TOWER | DUN_MINE F:BASH_DOOR | UNIQUE | FORCE_MAXHP | NO_CONF | NO_SLEEP | F:IM_POIS | IM_COLD | NO_FEAR D:Pallid and twisted, this creature hates the very sight of you. D:"It looked like something that had started out to be a man but had never D:quite made it. It had been stepped on, twisted, had holes poked into the D:sickly dough of its head-bulge. Bones showed through the transparent flesh D:of its torso and its short legs were as thick as trees, terminating in D:disk-shaped pads from which dozens of long toes hung like roots or worms. D:its arms were longer than its entire body. it was a crushed slug, a thing D:that had been frozen and thawed before it was fully baked. It was - D:'It is the Borshin', said the Lord of Bats." N:178:Dark elven mage G:h:v I:120:8d7:20:10:20 W:16:1:0:420 O:0:0:100 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:MALE | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | IM_POIS | HURT_LITE S:1_IN_3 | S:BLIND | CONF | MISSILE | DARKNESS | BA_POIS D:A dark elven figure, dressed all in black, hurling spells at you. N:179:Kamikaze yeek G:y:u I:113:8d3:18:15:10 W:15:1:0:400 O:0:0:0 B:EXPLODE:HURT:10d2 F:DUN_MINE | SILLY | F:OPEN_DOOR | BASH_DOOR | LITE_1 F:ANIMAL | IM_ACID | NO_FEAR D:The evil wizard Bruce has trained them to be living weapons. N:180:Orfax, Son of Boldor G:y:B I:120:5d16:18:16:10 W:9:3:0:950 O:0:50:50 B:HIT:HURT:1d9 B:HIT:HURT:1d8 B:INSULT B:INSULT F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DUN_RUIN | DUN_MINE F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:SMART | OPEN_DOOR | BASH_DOOR | F:ANIMAL | EVIL | IM_ACID | LITE_2 S:1_IN_4 | S:HEAL | BLINK | TELE_TO | SLOW | CONF | S:S_MONSTER D:He's just like daddy! He knows mighty spells, but fortunately he is a D:yeek. N:181:Servant of Glaaki G:z:G I:110:8d10:20:10:20 W:16:1:0:245 O:0:0:0 B:CRUSH:HURT:1d8 B:CLAW:DISEASE:1d3 F:DUN_RUIN | DUN_GRAVE F:DUN_DARKWATER F:OPEN_DOOR | BASH_DOOR | FRIENDS | HURT_LITE | F:NO_CONF | NO_SLEEP | UNDEAD | EVIL | NO_FEAR | IM_POIS F:IM_COLD | COLD_BLOOD S:1_IN_12 S:CAUSE_1 | SCARE D:"...the hand of a corpse -- bloodless and skeletal, and with D:impossibly long, cracked nails." N:182:Dark elven warrior G:h:D I:110:7d11:20:14:20 W:14:1:0:205 O:10:90:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 F:MALE | DUN_TOWER | DUN_TEMPLE | DUN_CITY F:DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | HURT_LITE S:1_IN_12 S:MISSILE D:A dark elven figure in armour and ready with his sword. N:183:Sand-dweller G:u:U I:110:8d8:20:10:20 W:15:1:0:124 O:20:50:20 B:CLAW:HURT:1d6 B:CLAW:HURT:1d6 F:FRIENDS | DUN_MINE | DUN_RUIN | DUN_HELL F:WILD_WASTE1 | WILD_WASTE2 | DROP_SKELETON F:OPEN_DOOR | BASH_DOOR | HURT_LITE | EVIL | DROP_60 | DROP_90 F:MALE D:"Rough-skinned, large-eyed, large-eared, with a horrible, D:distorted resemblance to the koala bear facially, though D:his body had an appearance of emaciation." N:184:Clear mushroom patch G:,:w I:120:1d1:4:1:0 W:14:2:0:44 O:0:0:0 B:SPORE:HURT:1d1 F:ATTR_CLEAR | DUN_DARKWATER | DUN_RUIN | DUN_MINE F:NEVER_MOVE | INVISIBLE | COLD_BLOOD | MULTIPLY | F:STUPID | EMPTY_MIND | F:NO_CONF | NO_SLEEP | NO_FEAR D:Yum! It looks quite tasty. N:185:Quiver slot G:,:U I:120:1d1:4:1:0 W:14:2:0:53 O:0:0:0 B:SPORE:CONFUSE:1d1 F:DUN_MINE | DUN_TOWER F:NEVER_MOVE | COLD_BLOOD | MULTIPLY | F:STUPID | EMPTY_MIND | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_5 S:ARROW D:It looks weird. N:186:Grishnakh, the Hill Orc G:o:u I:110:8d20:20:20:20 W:16:3:0:1450 O:0:100:0 B:HIT:HURT:1d12 B:HIT:HURT:1d10 B:HIT:HURT:1d12 B:HIT:HURT:1d10 F:UNIQUE | MALE | CAN_SPEAK | FORCE_MAXHP | ESCORT F:DUN_LAIR | DUN_MINE | DUN_CITY | WILD_MOUNT1 F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_SKELETON | DROP_CORPSE | F:OPEN_DOOR | BASH_DOOR | EVIL | ORC | IM_POIS D:He is a cunning and devious orc. D:"Grishnakh [was] a short crook-legged creature, very broad and with D:long arms that hung almost to the ground." N:187:Giant piranha G:l:B I:120:9d5:30:20:10 W:17:2:0:195 O:0:0:0 B:BITE:HURT:9d1 B:BITE:HURT:9d1 F:NO_SLEEP | DUN_DARKWATER | DUN_TEMPLE | WILD_SHORE | WILD_FOREST2 F:FRIENDS | AQUATIC | ANIMAL D:A very large and bloodthirsty fish. N:188:Owlbear G:H:o I:110:8d12:20:10:20 W:15:1:0:340 O:0:0:0 B:CLAW:HURT:1d6 B:CLAW:HURT:1d3 B:CRUSH:HURT:1d10 F:DUN_LAIR | DUN_MINE F:EVIL | ANIMAL | OPEN_DOOR | BASH_DOOR | DROP_CORPSE D:A bizarre bear-creature with the claws and the face of an owl. N:189:Blue horror G:u:B I:110:7d15:20:25:20 W:14:3:0:215 O:0:0:0 B:CLAW:TERRIFY:1d6 B:CLAW:TERRIFY:1d8 F:FRIENDS | DUN_HORROR | DUN_TOWER | DUN_CAVERN F:DUN_DARKWATER F:OPEN_DOOR | BASH_DOOR | F:EVIL | DEMON | IM_FIRE | NO_FEAR | NO_CONF D:An ugly screaming little demon servant of Tzeentch. N:190:Hairy mold G:m:U I:110:7d13:2:6:70 W:14:2:0:215 O:0:0:0 B:HIT:POISON:1d10 F:NEVER_MOVE | DUN_LAIR | DUN_LAIR | DUN_MINE F:DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a strange hairy growth on the dungeon floor. N:191:Grizzly bear G:q:u I:110:3d37:10:25:10 W:6:1:0:46 O:0:0:0 B:CLAW:HURT:1d10 B:CLAW:HURT:1d10 B:BITE:HURT:1d15 F:BASH_DOOR | WILD_FOREST2 | WILD_MOUNT1 | WILD_MOUNT2 | DROP_CORPSE F:ANIMAL D:A huge, beastly bear. N:192:Disenchanter mold G:m:v I:110:7d13:2:6:50 W:13:2:0:107 O:0:0:0 B:TOUCH:UN_BONUS:1d6 F:NEVER_MOVE | DUN_TOWER | DUN_TEMPLE | DUN_MINE F:DUN_DARKWATER F:STUPID | EMPTY_MIND | RES_DISE | F:IM_POIS | ATTR_MULTI | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_11 | S:DRAIN_MANA D:It is a strange glowing growth on the dungeon floor. N:193:Pseudo dragon G:d:v I:110:9d16:20:30:40 W:17:2:0:780 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:DUN_MINE | DUN_LAIR F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE | F:DROP_60 | BASH_DOOR | DRAGON S:1_IN_11 | S:CONF | SCARE | BR_LITE | BR_DARK D:A small relative of the dragon that inhabits dark caves. N:194:Tengu G:u:b I:120:8d13:20:16:30 W:15:1:0:230 O:0:0:0 B:HIT:HURT:1d8 F:OPEN_DOOR | BASH_DOOR | DUN_HELL | DUN_MINE | DUN_TEMPLE | DUN_RUIN F:EVIL | DEMON | IM_FIRE | NO_FEAR | RES_TELE | CAN_FLY S:1_IN_3 | S:BLINK | TELE_TO | TELE_AWAY | TPORT D:It is a fast-moving demon that blinks quickly in and out of existence; no D:other demon matches its teleporting mastery. N:195:Creeping gold coins G:$:y I:100:8d12:5:18:10 W:16:3:0:175 O:100:0:0 B:HIT:HURT:2d5 B:TOUCH:POISON:3d5 F:CHAR_MIMIC | ONLY_GOLD | DROP_90 | DROP_1D2 | DUN_MINE F:COLD_BLOOD | BASH_DOOR | F:ANIMAL | F:IM_POIS | NO_CONF | NO_SLEEP D:It is a pile of coins, crawling forward on thousands of tiny legs. N:196:Wolf G:C:u I:120:9d4:30:15:20 W:18:1:0:370 O:0:0:0 B:BITE:HURT:1d8 F:RAND_25 | F:FRIENDS | F:BASH_DOOR | DUN_LAIR | DUN_TEMPLE | WILD_FOREST1 | WILD_FOREST2 F:WILD_WASTE1 | WILD_WASTE2 | WILD_MOUNT1 | WILD_MOUNT2 F:ANIMAL | DROP_CORPSE D:It howls and snaps at you. N:197:Giant fruit fly G:I:U I:120:2d2:8:7:10 W:14:6:0:44 O:0:0:0 B:BITE:HURT:1d2 F:RAND_50 | RAND_25 | CAN_FLY | DUN_LAIR | DUN_DARKWATER F:MULTIPLY | WEIRD_MIND | F:ANIMAL D:A fast-breeding, annoying pest. N:198:Panther G:f:D I:120:8d7:40:15:2 W:16:2:0:540 O:0:0:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 F:BASH_DOOR | DUN_LAIR | DUN_TEMPLE | WILD_FOREST1 | WILD_FOREST2 | WILD_GRASS F:DROP_SKELETON | DROP_CORPSE F:ANIMAL D:A large black cat, stalking you with intent. It thinks you're its next D:meal. N:199:Tax collector G:p:D I:110:8d6:20:16:10 W:16:3:0:185 O:90:0:10 B:TOUCH:EAT_GOLD B:TOUCH:EAT_ITEM F:MALE | DUN_DARKWATER | DUN_TEMPLE | WILD_GRASS | SILLY | F:DROP_1D2 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | DROP_SKELETON D:He is eyeing your purse suspiciously. N:200:Hobbes the Tiger G:f:y I:120:9d9:40:15:0 W:18:2:0:940 O:0:0:0 B:CLAW:HURT:1d11 B:CLAW:HURT:1d11 B:BITE:HURT:1d4 F:DUN_LAIR | DUN_TEMPLE F:BASH_DOOR | UNIQUE | FORCE_MAXHP | NO_CONF | NO_SLEEP F:ANIMAL | MALE | CAN_SPEAK | DROP_CORPSE | SILLY D:Fast-moving, with a taste for tuna sandwiches. N:201:Shadow creature G:h:s I:110:7d8:12:10:16 W:14:2:0:150 O:20:30:50 B:HIT:HURT:1d7 B:HIT:HURT:1d7 F:DUN_RUIN | DUN_TEMPLE F:BASH_DOOR | OPEN_DOOR | FRIENDS | DROP_60 | IM_POIS | NO_SLEEP | NO_CONF F:MALE | DROP_SKELETON D:"There was something unusual about their appearance... For one thing, D:all had uniformly bloodshot eyes. Very, very bloodshot eyes. With them, D:though, the condition seemed normal. For another, all had an extra joint D:to each finger and thumb, and sharp, forward-curving spurs on the backs D:of their hands. All of them had prominent jaws (and) forty-four teeth, D:most of them longer than human teeth, and several looking to be much D:sharper. Their flesh was grayish and hard and shiny. There were D:undoubtedly other differences also, but those were sufficient to prove D:a point of some sort." N:202:Undead mass G:j:u I:110:9d6:70:10:5 W:18:2:0:260 O:0:0:0 B:TOUCH:DISEASE:1d6 B:TOUCH:LOSE_CON:1d6 F:DUN_MINE | DUN_TOWER F:DUN_DARKWATER F:UNDEAD | EMPTY_MIND | NO_CONF | NO_SLEEP | IM_POIS | IM_COLD | NO_FEAR | F:HURT_LITE | COLD_BLOOD | EVIL | NEVER_MOVE | MULTIPLY D:A sickening mound of decaying flesh, bones, hands and so on. It seems to D:be growing. N:203:Chaos shapechanger G:H:v I:110:8d17:10:10:12 W:15:2:0:270 O:20:50:20 B:HIT:HURT:1d5 B:HIT:CONFUSE:1d3 F:DUN_TEMPLE | DUN_MINE | DUN_LAIR F:DROP_60 | EVIL | SHAPECHANGER | ATTR_MULTI | ATTR_ANY S:1_IN_5 S:BO_FIRE | BO_COLD | CONF D:A vaguely humanoid form constantly changing its appearance. N:204:Baby multi-hued dragon G:d:v I:110:9d8:20:15:60 W:18:2:0:830 O:50:50:0 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BITE:HURT:1d5 F:ATTR_MULTI | DUN_TOWER | DUN_CAVERN | DUN_LAIR F:FORCE_MAXHP | FORCE_SLEEP | F:ONLY_GOLD | DROP_60 | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | DRAGON | CAN_FLY | F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS S:1_IN_11 | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS D:This hatchling dragon is still soft, its eyes unaccustomed to light and D:its scales shimmering with a hint of colour. N:205:Vorpal bunny G:r:w I:120:10d7:40:20:0 W:19:3:0:950 O:0:0:0 B:BITE:HURT:6d1 B:BITE:HURT:7d1 F:BASH_DOOR | DUN_LAIR | DUN_TOWER | WILD_MOUNT2 | DROP_CORPSE F:ANIMAL | SILLY S:1_IN_8 S:BLINK D:It looks very cute, except for the razor sharp teeth. It moans D:ominously as it jumps at your throat! N:206:Old Man Willow G:%:s I:110:3d107:20:10:20 W:6:5:0:220 O:0:50:50 B:TOUCH:PARALYZE B:TOUCH:PARALYZE B:CRUSH:HURT:2d12 S:1_IN_10 S:TELE_TO F:ANIMAL | NEVER_MOVE | COLD_BLOOD | WILD_FOREST2 | F:EMPTY_MIND | UNIQUE | FORCE_MAXHP | FORCE_SLEEP | F:RES_WATE | IM_POIS | IM_ACID F:DROP_90 | DROP_GOOD | ONLY_ITEM D:"...a huge willow-tree, old and hoary. Enormous it looked, its D:sprawling branches going up like racing arms with may long- D:lingered hands, its knotted and twisted trunk gaping in wide D:fissures that creaked faintly as the boughs moved." N:207:Hippocampus G:H:B I:110:10d13:12:7:10 W:20:1:0:380 O:0:0:0 B:BITE:HURT:2d5 B:BITE:HURT:2d5 F:AQUATIC | DUN_LAIR | DUN_DARKWATER | WILD_WASTE2 | DROP_CORPSE F:ANIMAL D:A truly strange hybrid of a horse and a fish. N:208:Zombified orc G:z:s I:110:8d8:20:12:25 W:16:1:0:320 O:0:0:0 B:HIT:HURT:1d4 B:HIT:HURT:1d4 B:HIT:HURT:1d6 F:DUN_GRAVE | DUN_MINE | DUN_CITY F:DUN_DARKWATER F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | UNDEAD | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a shambling orcish corpse leaving behind a trail of flesh. N:209:Hippogriff G:H:U I:110:9d14:12:7:10 W:18:1:0:380 O:0:0:0 B:HIT:HURT:2d5 B:BITE:HURT:2d5 F:BASH_DOOR | CAN_FLY | DUN_LAIR | DUN_TEMPLE | WILD_MOUNT2 | WILD_GRASS F:ANIMAL | DROP_CORPSE D:A strange hybrid of eagle, lion and horse. It looks weird. N:210:Black mamba G:J:D I:120:10d6:10:14:1 W:19:3:0:580 O:0:0:0 B:BITE:POISON:8d4 F:RAND_50 | F:BASH_DOOR | CAN_SWIM | DUN_LAIR | DUN_DARKWATER | DUN_MINE | WILD_FOREST1 | WILD_FOREST2 F:WILD_SWAMP1 | WILD_SWAMP2 F:ANIMAL | IM_POIS | DROP_SKELETON | DROP_CORPSE D:It has glistening black skin, a sleek body and highly venomous fangs. N:211:White wolf G:C:w I:120:7d5:30:14:20 W:14:1:0:280 O:0:0:0 B:BITE:HURT:1d3 B:BITE:HURT:1d6 F:RAND_25 | F:FRIENDS | F:BASH_DOOR | DUN_LAIR | WILD_WASTE1 | F:ANIMAL | IM_COLD | DROP_SKELETON | DROP_CORPSE D:A large and muscled wolf from the northern wastes. Its breath is cold and D:icy and its fur coated in frost. N:212:Grape jelly G:j:v I:110:8d36:2:1:99 W:16:3:0:330 O:0:0:0 B:TOUCH:EXP_10 F:NEVER_MOVE | DUN_TOWER | DUN_MINE | DUN_RUIN F:DUN_DARKWATER F:STUPID | EMPTY_MIND | F:IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR | LITE_1 S:1_IN_11 | S:DRAIN_MANA D:It is a pulsing mound of glowing flesh. N:213:Nether worm mass G:w:D I:100:8d4:10:6:3 W:16:4:0:41 O:0:0:0 B:TOUCH:EXP_10 F:DUN_TOWER | DUN_MINE F:RAND_50 | RAND_25 | CAN_SWIM | F:STUPID | WEIRD_MIND | MULTIPLY | BASH_DOOR | F:ANIMAL | HURT_LITE | NO_FEAR D:It is a disgusting mass of dark worms, eating each other, the floor, D:the air, you.... N:214:Abyss worm mass G:w:D I:100:14d3:10:7:3 W:28:4:0:110 O:0:0:0 B:CRAWL:EXP_10 F:RAND_50 | RAND_25 | F:DUN_MINE | DUN_DARKWATER | DUN_TOWER F:STUPID | WEIRD_MIND | MULTIPLY | BASH_DOOR | EVIL | CAN_SWIM | F:ANIMAL | HURT_LITE | NO_FEAR | KILL_WALL | COLD_BLOOD | INVISIBLE D:Even more disgusting dark worms, their essence that of unbeing. N:215:Golfimbul, the Hill Orc Chief G:o:u I:110:8d23:20:30:20 W:16:3:0:2350 O:10:90:0 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:HIT:HURT:1d10 B:HIT:HURT:1d10 F:UNIQUE | MALE | F:FORCE_MAXHP | DUN_MINE | DUN_CITY | WILD_WASTE1 F:ESCORT | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | CAN_SPEAK | F:EVIL | ORC | IM_FIRE | IM_COLD D:A leader of a band of raiding orcs, he picks on hobbits. N:216:Swordsman G:p:U I:110:9d10:20:20:20 W:17:1:0:235 O:0:100:0 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:MALE | DUN_TOWER | DUN_TEMPLE | DUN_CITY | DUN_MINE | WILD_GRASS F:DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | LITE_2 D:A warrior of considerable skill. N:217:Skaven shaman G:r:g I:110:5d12:20:10:20 W:10:1:0:108 O:0:0:100 B:HIT:HURT:1d7 B:HIT:HURT:1d7 F:MALE | DUN_RUIN | WILD_SWAMP2 | WILD_WASTE1 | F:FORCE_SLEEP | DROP_90 | LITE_2 F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | S:1_IN_8 | S:BLINK | CAUSE_1 | MISSILE | CONF | SCARE D:The shaman of a skaven tribe gets his powers from a mystic D:stone corrupted by chaos, called a Warp Stone. N:218:Gazer G:e:b I:110:13d5:15:9:40 W:25:1:0:330 O:0:0:0 B:GAZE:PARALYZE:1d2 B:GAZE:CONFUSE:1d2 F:CAN_FLY | IM_POIS | DUN_TOWER | DUN_MINE | DUN_LAIR F:DUN_DARKWATER F:WILD_FOREST2 | DROP_CORPSE | EVIL S:1_IN_8 S:HOLD | CONF D:A floating eye surrounded by number of smaller eyestalks. Its D:gaze seems mesmerizing. N:219:Knight archer G:p:U I:110:13d6:20:20:20 W:17:1:0:410 O:0:50:0 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:MALE | DUN_TOWER | DUN_TEMPLE | DUN_CITY F:DROP_1D2 | LITE_2 F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE S:1_IN_8 S:ARROW D:A warrior trained in melee as well as missile weapons. N:220:Ixitxachitl G:l:s I:110:10d8:20:15:20 W:20:1:0:650 O:0:0:0 B:STING:POISON:2d5 B:STING:POISON:2d5 F:ANIMAL | EVIL | AQUATIC | IM_POIS | DUN_DARKWATER | DUN_LAIR | WILD_SHORE D:A devil ray of the depths. N:221:Mine-dog G:C:u I:120:9d4:30:15:20 W:17:4:0:450 O:0:0:0 B:EXPLODE:HURT:6d6 F:RAND_50 | DUN_MINE | SILLY F:FRIENDS | F:BASH_DOOR | ANIMAL D:An explosive charge has been attached to this poor animal, who D:has been trained to search for its target and detonate. N:222:Hellcat G:f:R I:120:9d7:20:15:30 W:17:1:0:260 O:0:0:0 B:CLAW:HURT:1d5 B:CLAW:HURT:1d5 B:BITE:HURT:1d8 F:DUN_LAIR | DUN_RUIN F:ANIMAL | WEIRD_MIND | FRIENDS | RAND_25 | IM_FIRE | EVIL D:It is as large as a tiger, its yellow eyes are pupilless. N:223:Moon beast G:q:W I:120:8d9:30:15:20 W:16:1:0:520 O:50:0:40 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 B:BUTT:HURT:1d6 F:DUN_TEMPLE | DUN_LAIR F:DROP_1D2 | ONLY_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | IM_FIRE | ANIMAL | DROP_CORPSE S:1_IN_6 S:HEAL | BLIND | DARKNESS | CONF | CAUSE_2 D:"Great greyish-white slippery things which could expand and D:contract at will, and whose principle shape... was that of a D:sort of toad without any eyes, but with a curious vibrating mass D:of short pink tentacles on the end of its blung, vague snout." N:224:Master yeek G:y:g I:110:10d8:18:15:10 W:20:2:0:500 O:0:0:100 B:HIT:HURT:1d8 F:FORCE_SLEEP | DUN_MINE | DUN_LAIR F:DROP_60 | F:OPEN_DOOR | BASH_DOOR | LITE_2 F:ANIMAL | EVIL | IM_ACID | DROP_CORPSE S:1_IN_4 | S:BLINK | TPORT | BLIND | SLOW | BA_POIS | S:S_MONSTER D:A small humanoid that radiates some power. N:225:Priest G:p:B I:110:12d6:20:15:40 W:23:1:0:910 O:0:20:80 B:HIT:HURT:2d3 B:HIT:HURT:2d3 F:MALE | GOOD | DUN_TEMPLE | DUN_CITY F:FORCE_SLEEP | F:DROP_1D2 | LITE_2 F:SMART | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE S:1_IN_3 | S:HEAL | SCARE | CAUSE_2 | S:S_MONSTER D:A robed humanoid dedicated to his god. N:226:Dark elven priest G:h:b I:120:8d7:20:15:30 W:15:1:0:510 O:0:10:90 B:HIT:HURT:1d9 B:HIT:HURT:1d10 F:MALE | DUN_TEMPLE | DUN_CITY F:FORCE_SLEEP | F:ONLY_ITEM | DROP_1D2 | F:SMART | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | HURT_LITE S:1_IN_5 | S:HEAL | BLIND | CONF | CAUSE_2 | DARKNESS | MISSILE D:A dark elven figure, dressed all in black, chanting curses and waiting to D:deliver your soul to hell. N:227:Air spirit G:E:B I:130:9d6:12:20:20 W:17:2:0:480 O:0:0:0 B:HIT:HURT:1d3 F:DUN_TOWER | DUN_LAIR | DUN_PLANAR F:RAND_50 | RAND_25 | NONLIVING | F:EMPTY_MIND | INVISIBLE | COLD_BLOOD | BASH_DOOR | F:EVIL | IM_POIS | CAN_FLY | F:NO_CONF | NO_SLEEP | NO_FEAR D:A whirlwind of sentient air. N:228:Skeleton human G:s:w I:110:10d6:20:15:30 W:19:1:0:390 O:0:0:0 B:HIT:HURT:1d8 F:DUN_GRAVE | DUN_RUIN F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is an animated human skeleton. N:229:Zombified human G:z:s I:110:10d8:20:12:20 W:19:1:0:360 O:0:0:0 B:HIT:HURT:1d4 B:HIT:HURT:1d4 F:DUN_GRAVE | DUN_RUIN F:DUN_DARKWATER F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a shambling human corpse dropping chunks of flesh behind it. N:230:Tiger G:f:o I:120:10d9:40:10:2 W:20:2:0:660 O:0:0:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:1d6 F:BASH_DOOR | DUN_LAIR | DUN_TEMPLE | WILD_GRASS | WILD_FOREST2 | WILD_MOUNT2 F:ANIMAL | DROP_SKELETON | DROP_CORPSE D:One of the largest of its species, a sleek orange and black shape creeps D:towards you, ready to pounce. N:231:Moaning spirit G:G:U I:120:10d3:14:10:10 W:19:2:0:300 O:50:20:30 B:WAIL:TERRIFY B:TOUCH:LOSE_DEX:1d8 F:FORCE_SLEEP | RAND_25 | DUN_GRAVE | DUN_TOWER | DUN_RUIN | DUN_MINE F:DUN_DARKWATER F:DROP_60 | DROP_90 | CAN_FLY | F:INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_15 | S:TPORT | SCARE D:A ghostly apparition that shrieks horribly. N:232:Frumious bandersnatch G:c:b I:120:10d8:12:15:30 W:20:2:0:580 O:0:0:0 B:BITE:HURT:2d4 B:BITE:HURT:2d4 B:STING:HURT:2d4 F:WEIRD_MIND | BASH_DOOR | DUN_TOWER | DUN_MINE | DUN_LAIR F:DUN_DARKWATER F:ANIMAL | DROP_SKELETON D:It is a vast armoured centipede with massive mandibles and a spiked tail. D: Keep it away from sunflowers. N:233:Spotted jelly G:j:s I:120:9d9:12:5:1 W:17:3:0:310 O:0:0:0 B:TOUCH:ACID:1d10 B:TOUCH:ACID:2d6 B:TOUCH:ACID:2d6 F:NEVER_MOVE | F:STUPID | EMPTY_MIND | COLD_BLOOD | DUN_DARKWATER | DUN_LAIR | DUN_MINE F:IM_ACID | IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR D:A jelly thing. N:234:Drider G:S:b I:110:9d11:8:15:80 W:18:2:0:850 O:0:0:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:BITE:POISON:1d10 F:FORCE_SLEEP | DUN_LAIR | WILD_FOREST2 F:BASH_DOOR | DROP_SKELETON | F:EVIL | IM_POIS S:1_IN_8 | S:CONF | CAUSE_1 | DARKNESS | MISSILE | ARROW D:A dark elven torso merged with the bloated form of a giant spider. N:235:Mongbat G:b:U I:113:9d10:20:40:8 W:18:3:0:350 O:0:0:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:POISON:1d8 F:DUN_DARKWATER | DUN_MINE | DUN_RUIN | WILD_WASTE2 F:ANIMAL | EVIL | FRIENDS | CAN_FLY | FORCE_MAXHP | F:IM_COLD | IM_POIS | WEIRD_MIND | DROP_CORPSE D:Devil-bats, notoriously difficult to kill. N:236:Killer brown beetle G:K:u I:110:11d7:10:20:30 W:21:2:0:490 O:0:0:0 B:BITE:HURT:3d4 F:DUN_LAIR | DUN_MINE | DUN_TEMPLE F:DUN_DARKWATER F:WEIRD_MIND | BASH_DOOR | CAN_FLY | DROP_CORPSE F:ANIMAL D:It is a vicious insect with a tough carapace. N:237:Boldor, King of the Yeeks G:y:v I:120:9d15:18:20:10 W:18:3:0:1800 O:0:90:10 B:HIT:HURT:1d9 B:HIT:HURT:1d9 B:HIT:HURT:1d8 F:UNIQUE | MALE | DUN_MINE F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | LITE_2 F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD | F:SMART | OPEN_DOOR | BASH_DOOR | CAN_SPEAK | DROP_CORPSE F:ANIMAL | EVIL | IM_ACID S:1_IN_3 | S:HEAL | BLINK | TPORT | BLIND | SLOW | S:S_KIN D:A great yeek, powerful in magic and sorcery, but a yeek all the same. N:238:Ogre G:O:U I:110:9d9:20:16:30 W:17:2:0:310 O:10:90:0 B:HIT:HURT:2d8 F:FRIENDS | DUN_LAIR | DUN_CAVERN | DUN_CITY F:DROP_60 | WILD_WASTE1 | WILD_FOREST2 | WILD_MOUNT2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT D:A hideous, smallish giant that is often found near or with orcs. N:239:Creeping mithril coins G:$:B I:110:11d11:5:25:10 W:22:4:0:850 O:100:0:0 B:HIT:HURT:2d5 B:TOUCH:POISON:3d5 F:CHAR_MIMIC | DUN_MINE F:ONLY_GOLD | DROP_90 | DROP_2D2 | F:COLD_BLOOD | BASH_DOOR | F:ANIMAL | F:IM_POIS | NO_CONF | NO_SLEEP D:It is a pile of coins, shambling forward on thousands of tiny legs. N:240:Illusionist G:p:v I:110:12d6:20:10:10 W:23:2:0:390 O:20:0:80 B:HIT:HURT:2d2 F:MALE | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL S:1_IN_3 | S:HASTE | BLINK | TPORT | BLIND | HOLD | SLOW | CONF | DARKNESS D:A deceptive spell caster. N:241:Druid G:p:g I:110:12d8:20:10:10 W:23:2:0:1050 O:20:0:80 B:HIT:HURT:2d4 B:HIT:HURT:2d4 F:MALE | DUN_TEMPLE | DUN_CITY | WILD_FOREST1 | F:FORCE_SLEEP | DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:SMART | OPEN_DOOR | BASH_DOOR | LITE_2 F:EVIL S:1_IN_3 | S:HASTE | BLINK | BLIND | HOLD | SLOW | BO_FIRE | BO_ELEC D:A mystic at one with nature. Om. N:242:Pink horror G:u:R I:110:15d6:20:20:20 W:30:3:0:800 O:0:0:0 B:CLAW:TERRIFY:1d6 B:CLAW:TERRIFY:1d6 B:BITE:CONFUSE:1d6 F:FRIENDS | DUN_HORROR | DUN_TEMPLE | WILD_WASTE2 F:OPEN_DOOR | BASH_DOOR | F:EVIL | DEMON | IM_FIRE | NO_FEAR | NO_CONF S:1_IN_8 S:CONF | SCARE D:An ugly screaming little demon servant of Tzeentch. N:243:Cloaker G:(:g I:130:11d4:20:20:0 W:22:5:0:530 O:0:0:0 B:HIT:PARALYZE:5d5 B:HIT:TERRIFY:5d5 F:DUN_CITY | DUN_TOWER F:NEVER_MOVE | NONLIVING | NO_FEAR | F:STUPID | EMPTY_MIND | COLD_BLOOD | CHAR_MIMIC | NO_CONF | NO_SLEEP | F:DROP_90 | EVIL | IM_COLD | FORCE_MAXHP | IM_POIS | D:It resembles a normal cloak until some poor fool ventures too close! N:244:Black orc G:o:D I:110:10d8:20:36:20 W:19:2:0:540 O:10:50:20 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:MALE | DUN_MINE | DUN_CITY | WILD_MOUNT2 | DROP_SKELETON | DROP_CORPSE F:FRIENDS | DROP_60 | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | HURT_LITE S:1_IN_15 S:ARROW D:He is a large orc with powerful arms and deep black skin. N:245:Ochre jelly G:j:y I:120:10d8:12:18:1 W:19:3:0:270 O:0:0:0 B:TOUCH:ACID:1d10 B:TOUCH:ACID:2d6 B:TOUCH:ACID:2d6 F:DUN_DARKWATER | DUN_TOWER | DUN_MINE F:STUPID | EMPTY_MIND | COLD_BLOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:IM_ACID | IM_POIS | CAN_SWIM | F:NO_CONF | NO_SLEEP | NO_FEAR D:A fast moving highly acidic jelly thing, that is eating away the floor it D:rests on. N:246:Software bug G:I:r I:120:2d2:8:6:10 W:10:1:0:150 O:0:0:0 B:BITE:HURT:1d2 F:RAND_50 | RAND_25 | F:DUN_TOWER F:MULTIPLY | WEIRD_MIND | BASH_DOOR | CAN_FLY | F:ANIMAL | SILLY D:Oh no! They are everywhere! N:247:Lurker G:.:w I:110:10d15:30:20:10 W:20:3:0:780 O:0:0:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:DUN_MINE | DUN_CAVERN F:CHAR_CLEAR | CHAR_MIMIC | ATTR_CLEAR | F:NEVER_MOVE | FORCE_MAXHP | F:EMPTY_MIND | INVISIBLE | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | D:A strange creature that merges with the dungeon floor, trapping its D:victims by enveloping them within its perfectly disguised form. N:248:Nixie G:h:B I:110:11d12:20:22:50 W:21:1:0:370 O:0:0:0 B:HIT:HURT:6d3 B:HIT:HURT:6d3 F:AQUATIC | DUN_LAIR | WILD_SHORE F:MALE D:A race of fair yet belligrent sea elves. N:249:Vlasta G:R:b I:120:10d6:12:10:12 W:20:3:0:480 O:0:0:0 B:BITE:BLIND:1d10 B:BITE:BLIND:1d10 F:DUN_LAIR | DUN_MINE | DUN_CAVERN F:OPEN_DOOR | DROP_SKELETON | DROP_CORPSE D:This strange creature looks like a miniature tyrannosaurus. It has D:empty, pale eyes and a sharp beak, which it aims at your eyes D:as it jumps at you! N:250:Giant white dragon fly G:F:w I:110:11d3:20:15:50 W:22:3:0:680 O:0:0:0 B:BITE:COLD:1d6 F:FORCE_SLEEP | DUN_DARKWATER | DUN_TOWER | WILD_WASTE1 | WILD_WASTE2 F:RAND_50 | CAN_FLY | F:WEIRD_MIND | BASH_DOOR | F:ANIMAL | IM_COLD S:1_IN_10 | S:BR_COLD D:It is a large fly that drips frost. N:251:Snaga sapper G:o:G I:111:11d5:20:16:30 W:21:1:0:670 O:0:0:0 B:HIT:HURT:1d8 B:EXPLODE:HURT:20d2 F:MALE | DUN_MINE F:WILD_GRASS | WILD_MOUNT1 F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | HURT_LITE D:He is one of the many weaker 'slave' orcs, often mistakenly known as a D:goblin. He is equipped with an explosive charge. N:252:Blue icky thing G:i:b I:100:11d4:15:14:20 W:21:4:0:215 O:0:0:0 B:CRAWL:POISON:1d4 B:CRAWL:EAT_FOOD B:HIT:HURT:1d4 B:HIT:HURT:1d4 F:FORCE_SLEEP | DUN_DARKWATER | DUN_CAVERN | DUN_LAIR F:RAND_50 | F:MULTIPLY | OPEN_DOOR | BASH_DOOR | CAN_SWIM | DROP_CORPSE F:EVIL | IM_POIS S:1_IN_8 | S:BLIND | CONF | SCARE D:It is a strange, slimy, icky creature, with rudimentary intelligence, D:but evil cunning. It hungers for food, and you look tasty. N:253:Gibbering mouther G:j:o I:110:13d3:15:14:20 W:26:4:0:590 O:0:0:0 B:CRAWL:POISON:1d4 F:DUN_TOWER | DUN_CAVERN | WILD_WASTE2 F:DUN_DARKWATER F:NEVER_MOVE | MULTIPLY | EVIL | CAN_SWIM | F:IM_POIS | EMPTY_MIND | NO_FEAR | S:1_IN_7 S:SCARE | CONF | BR_LITE D:A chaotic mass of pulsating flesh, mouths and eyes. N:254:Irish wolfhound of Flora G:C:s I:120:10d7:20:14:0 W:20:2:0:550 O:0:0:0 B:BITE:HURT:1d5 B:BITE:HURT:1d5 F:DUN_LAIR | DUN_CITY F:ANIMAL | NO_FEAR | FRIENDS | DROP_SKELETON | DROP_CORPSE D:Well-trained watchdogs, obedient to death. N:255:Hill giant G:P:U I:110:12d10:20:22:50 W:23:1:0:1040 O:20:50:20 B:HIT:HURT:3d6 B:HIT:HURT:3d6 F:DROP_60 | DUN_LAIR | WILD_MOUNT1 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT | MALE D:A ten foot tall humanoid with powerful muscles. #Note! Flesh golem's CAN_SWIM is a tribute to Shelley... N:256:Flesh golem G:g:o I:110:12d6:12:15:10 W:23:1:0:440 O:0:0:0 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:DUN_TOWER F:EMPTY_MIND | BASH_DOOR | CAN_SWIM | F:IM_ELEC F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING D:A shambling humanoid monster with long scars. N:257:Warg G:C:D I:120:11d5:20:12:40 W:21:2:0:610 O:0:0:0 B:BITE:HURT:1d8 F:RAND_25 | F:FRIENDS | DUN_LAIR | DUN_CITY | WILD_FOREST2 | WILD_MOUNT1 | WILD_MOUNT2 F:DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | F:ANIMAL | EVIL D:It is a large wolf with eyes full of cunning. N:258:Cheerful leprechaun G:h:G I:115:4d2:8:6:6 W:26:2:0:510 O:100:0:0 B:TOUCH:EAT_GOLD B:TOUCH:EAT_FOOD F:DUN_LAIR | DUN_MINE F:FRIENDS | DROP_60 | ONLY_GOLD | RAND_50 | OPEN_DOOR | MALE | GOOD F:LITE_1 | SILLY S:1_IN_6 S:BLINK D:A merry little gnome. N:259:Giant flea G:I:s I:120:1d2:6:3:10 W:21:1:0:135 O:0:0:0 B:BITE:HURT:1d2 F:RAND_50 | CAN_FLY | DUN_DARKWATER | DUN_RUIN F:WEIRD_MIND | MULTIPLY | F:ANIMAL D:It makes you itch just to look at it. N:260:Ufthak of Cirith Ungol G:o:g I:110:10d22:20:25:20 W:20:3:0:3200 O:0:100:0 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:UNIQUE | MALE | F:FORCE_MAXHP | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:ESCORT | DUN_MINE | DUN_CITY F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_COLD | IM_POIS D:A strong orc guarding the pass of Cirith Ungol. He is mortally afraid of D:spiders. N:261:Clay golem G:g:U I:110:12d7:12:15:10 W:24:2:0:880 O:0:0:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:DUN_TOWER F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | F:IM_FIRE | IM_POIS | F:HURT_ROCK | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a massive animated statue made out of hardened clay. N:262:Black ogre G:O:D I:110:12d11:20:17:30 W:24:2:0:880 O:0:80:0 B:HIT:HURT:2d8 B:HIT:HURT:2d8 F:RAND_25 | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | WILD_WASTE2 | DROP_CORPSE F:FRIENDS | DROP_60 | F:OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT D:A massive orc-like figure with black skin and powerful arms. N:263:Dweller on the threshold G:Y:v I:110:10d18:30:15:0 W:19:5:0:950 O:50:0:40 B:GAZE:PARALYZE B:CLAW:UN_POWER:3d5 B:CLAW:FIRE:3d5 B:BITE:UN_BONUS:2d5 F:DUN_TOWER | DUN_TEMPLE | DUN_HORROR F:NEVER_MOVE | DROP_60 | EVIL | DROP_CORPSE F:IM_POIS | IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP F:RES_DISE S:1_IN_6 S:BO_ACID | CONF | DRAIN_MANA D:A creature like a giant purple Buddha with lots of teeth. D: It cleans them with the bones of sorcerors. N:264:Half-orc G:o:s I:110:10d12:20:20:20 W:20:3:0:560 O:30:30:30 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:MALE | DUN_CITY | WILD_GRASS | WILD_FOREST1 F:FRIENDS | DROP_60 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | ORC D:He is a hideous deformed cross-breed with man and orc, combining man's D:strength and cunning with orcish evil. N:265:Dark naga G:n:D I:110:13d15:60:32:60 W:25:2:0:1600 O:0:0:80 B:STING:HURT:1d10 B:BITE:HURT:1d10 F:FEMALE | DUN_LAIR | DUN_TEMPLE | WILD_SHORE F:DUN_DARKWATER F:RAND_25 | DROP_60 | DROP_1D2 | IM_POIS | IM_COLD | RES_WATE | F:OPEN_DOOR | BASH_DOOR | EMPTY_MIND | CAN_SWIM | DROP_CORPSE F:EVIL S:1_IN_4 S:HOLD | CONF | BO_COLD | HEAL | DARKNESS D:A giant snake-like figure with a woman's torso. Not as strong D:as other nagas, but more talented in magic. N:266:Giant octopus G:l:v I:105:14d18:60:30:60 W:27:2:0:1750 O:0:0:0 B:SPIT:BLIND:1d3 B:CRUSH:HURT:8d3 B:CRUSH:HURT:8d3 B:CRUSH:HURT:8d3 F:RAND_25 | IM_COLD | RES_WATE | AQUATIC | ANIMAL | DUN_DARKWATER | WILD_SHORE D:It doesn't move very fast, but when it does - watch out! N:267:Magic mushroom patch G:,:B I:130:1d1:40:10:0 W:23:2:0:250 O:0:0:0 F:DUN_TOWER | DUN_CAVERN | WILD_FOREST2 F:DUN_DARKWATER F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW | FRIENDS | F:STUPID | RES_TELE | F:NO_CONF | NO_SLEEP | NO_FEAR | LITE_1 S:1_IN_1 | S:BLINK | SLOW | SCARE | DARKNESS D:Yum! It looks quite tasty. It seems to glow with an unusual light. N:268:Plaguebearer of Nurgle G:z:o I:110:14d6:20:25:20 W:27:2:0:1230 O:50:20:20 B:CLAW:DISEASE:2d5 B:CLAW:DISEASE:2d5 B:BUTT:HURT:3d5 F:FORCE_MAXHP | DROP_60 | DUN_GRAVE F:DUN_DARKWATER F:OPEN_DOOR | BASH_DOOR | IM_COLD | F:EVIL | DEMON | UNDEAD | IM_POIS | NONLIVING S:1_IN_8 S:SCARE | S_ANT | CAUSE_2 | SLOW D:An unfortunate individual, who was killed by the incurable D:disease known as Nurgle's Rot, and was transformed into a D:rotting demon zombie. It has but one eye, and a single D:pale horn in its forehead. N:269:Guardian naga G:n:y I:110:11d17:20:32:120 W:21:2:0:600 O:0:0:80 B:CRUSH:HURT:2d8 B:BITE:HURT:1d8 B:BITE:HURT:1d8 F:FEMALE | DUN_TOWER | WILD_MOUNT2 | WILD_SHORE F:DUN_DARKWATER F:RAND_25 | DROP_60 | DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:EVIL D:A giant snake-like figure with a woman's torso. N:270:Wererat G:r:D I:110:12d10:10:6:10 W:23:2:0:1080 O:40:20:10 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d6 F:FORCE_SLEEP | F:ONLY_GOLD | DROP_60 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:ANIMAL | EVIL | DUN_LAIR | DUN_RUIN | WILD_WASTE1 | WILD_WASTE2 F:DUN_DARKWATER S:1_IN_9 | S:BLINK | CAUSE_2 | BO_COLD | BA_POIS | S_KIN D:A large rat with glowing red eyes. The wererat is a disgusting creature, D:relishing in filth and disease. N:271:Light hound G:Z:o I:110:12d3:30:15:0 W:23:1:0:1010 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 F:FORCE_SLEEP | DROP_CORPSE F:FRIENDS | DUN_CAVERN | DUN_PLANAR F:BASH_DOOR | LITE_1 | LITE_2 F:ANIMAL S:1_IN_5 | S:BR_LITE D:A brilliant canine form whose light hurts your eyes, even at this distance. N:272:Shadow hound G:Z:D I:110:12d3:30:15:0 W:24:1:0:1200 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 F:FORCE_SLEEP | DROP_CORPSE F:FRIENDS | DUN_CAVERN | DUN_PLANAR F:BASH_DOOR | HURT_LITE | F:ANIMAL S:1_IN_5 | S:BR_DARK D:A hole in the air in the shape of a huge hound. No light falls upon its D:form. N:273:Flying skull G:s:s I:110:12d6:30:15:20 W:24:3:0:460 O:90:0:10 B:BITE:POISON:1d3 B:BITE:LOSE_STR:1d4 F:DUN_RUIN | DUN_GRAVE F:UNDEAD | EVIL | IM_POIS | IM_COLD | WEIRD_MIND | NO_FEAR | CAN_FLY | F:NO_CONF | NO_SLEEP | DROP_60 | BASH_DOOR | FRIENDS | COLD_BLOOD D:A skullpack animated by necromantic spells. N:274:Mi-Go G:I:R I:120:12d7:20:15:20 W:24:2:0:1350 O:0:0:0 B:STING:POISON:1d4 B:BITE:LOSE_STR:1d2 F:DUN_TOWER | DUN_CAVERN | DUN_HORROR F:DUN_DARKWATER F:IM_POIS | IM_COLD | COLD_BLOOD | ANIMAL | EVIL | F:NO_SLEEP | NO_CONF | CAN_FLY | DROP_SKELETON S:1_IN_6 S:CONF | S_MONSTER | S_DEMON D:"They were pinkish things about five feet long; with crustaceous D:bodies bearing vast pairs of dorsal fins or membranous wings and D:several sets of articulate limbs, and with a sort of convoluted D:ellipsoid, covered with multitudes of very short antenna, where D:a head would ordinarily be..." N:275:Giant tarantula G:S:G I:120:13d9:8:16:80 W:25:3:0:1500 O:0:0:0 B:BITE:POISON:3d6 B:BITE:POISON:3d6 B:BITE:POISON:3d6 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | WILD_FOREST1 F:ANIMAL | IM_POIS | DROP_SKELETON D:A giant spider with hairy black and red legs. N:276:Giant clear centipede G:c:w I:110:12d3:12:15:30 W:24:2:0:500 O:0:0:0 B:BITE:HURT:2d4 B:STING:HURT:2d4 F:ATTR_CLEAR | F:INVISIBLE | WEIRD_MIND | BASH_DOOR | DUN_MINE | DUN_LAIR | WILD_MOUNT1 F:DUN_DARKWATER F:ANIMAL | DROP_SKELETON D:It is about four feet long and carnivorous. N:277:Mirkwood spider G:S:u I:120:12d4:15:13:80 W:23:2:0:1040 O:0:0:0 B:BITE:POISON:2d6 B:BITE:POISON:2d6 F:FRIENDS | DUN_LAIR | WILD_FOREST2 | F:WEIRD_MIND | BASH_DOOR | HURT_LITE | F:ANIMAL | EVIL | IM_POIS | DROP_SKELETON D:A strong and powerful spider from Mirkwood forest. Cunning and evil, it D:seeks to taste your juicy insides. N:278:Frost giant G:P:w I:110:12d10:20:25:50 W:24:1:0:1070 O:20:80:0 B:HIT:COLD:3d6 B:HIT:HURT:2d8 F:DROP_60 | DUN_LAIR | WILD_WASTE1 | WILD_WASTE2 | WILD_MOUNT2 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | GIANT | MALE | AURA_COLD | F:IM_COLD D:A twelve foot tall giant covered in furs. N:279:Griffon G:H:u I:110:12d14:12:8:10 W:24:1:0:640 O:0:0:0 B:HIT:HURT:3d4 B:BITE:HURT:2d6 F:BASH_DOOR | CAN_FLY | DUN_LAIR | WILD_FOREST2 | WILD_MOUNT1 F:WILD_MOUNT2 | WILD_GRASS | F:ANIMAL | DROP_CORPSE D:It is half lion, half eagle. It flies menacingly towards you. N:280:Homonculous G:u:U I:110:11d5:20:16:30 W:22:3:0:420 O:0:0:0 B:HIT:PARALYZE:1d2 B:HIT:HURT:1d10 F:DUN_HELL | DUN_TEMPLE | DUN_RUIN F:DUN_DARKWATER F:OPEN_DOOR | BASH_DOOR | NONLIVING | CAN_FLY | F:EVIL | DEMON | IM_FIRE | NO_FEAR D:It is a small demonic spirit full of malevolence. N:281:Gnome mage G:h:R I:110:12d4:20:10:20 W:23:2:0:700 O:20:0:80 B:HIT:HURT:1d5 F:MALE | DUN_TOWER | DUN_CITY | WILD_MOUNT1 F:FORCE_SLEEP | FRIENDS | DROP_60 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON F:EVIL | LITE_1 S:1_IN_4 | S:BLINK | DARKNESS | BO_COLD | S:S_MONSTER D:A mage of short stature. N:282:Clear hound G:Z:B I:110:11d4:30:12:0 W:21:2:0:470 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 B:BITE:HURT:1d6 F:ATTR_CLEAR | DUN_CAVERN | DUN_PLANAR F:FRIENDS | DROP_SKELETON | DROP_CORPSE F:INVISIBLE | BASH_DOOR | F:ANIMAL D:A completely translucent hound. N:283:Umber hulk G:X:u I:110:15d10:20:25:10 W:29:1:0:3700 O:0:0:0 B:GAZE:CONFUSE B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:HURT:2d6 F:EMPTY_MIND | COLD_BLOOD | DUN_MINE | DUN_PLANAR | WILD_MOUNT2 F:BASH_DOOR | KILL_WALL | DROP_SKELETON F:ANIMAL | EVIL | F:IM_POIS | F:HURT_ROCK | NO_CONF | NO_SLEEP D:This bizarre creature has glaring eyes and large mandibles capable of D:slicing through rock. N:284:Rust monster G:q:o I:110:12d15:12:28:10 W:23:2:0:630 O:0:0:0 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 F:STUPID | WEIRD_MIND | KILL_ITEM | DUN_TOWER | DUN_MINE | DUN_RUIN F:DUN_DARKWATER F:IM_ACID | IM_POIS | DROP_CORPSE | FORCE_MAXHP | F:NO_CONF | D:It is a weird, small animal with two antennae popping forth from D:its forehead. It looks hungry. N:285:Orc captain G:o:g I:110:13d11:20:30:20 W:25:3:0:1220 O:20:70:10 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:MALE | DUN_CITY | WILD_FOREST1 | WILD_GRASS | WILD_MOUNT1 F:DROP_90 | F:OPEN_DOOR | BASH_DOOR | ESCORT | DROP_SKELETON | DROP_CORPSE F:EVIL | ORC | CAN_SPEAK S:1_IN_15 S:ARROW D:An armoured orc with an air of authority. N:286:Gelatinous cube G:j:B I:110:12d23:12:6:1 W:23:4:0:750 O:50:30:20 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 F:FORCE_MAXHP | F:DROP_1D2 | DROP_4D2 | DUN_MINE | DUN_CAVERN | DUN_TOWER F:DUN_DARKWATER F:STUPID | EMPTY_MIND | COLD_BLOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:IM_ACID | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a strange, vast gelatinous structure that assumes cubic proportions D:as it lines all four walls of the corridors it patrols. Through its D:transparent jelly structure you can see treasures it has engulfed, and a D:few corpses as well. N:287:Giant green dragon fly G:F:G I:110:8d2:12:16:50 W:23:2:0:920 O:0:0:0 B:BITE:POISON:2d6 F:FORCE_SLEEP | RAND_50 | RAND_25 | DUN_LAIR | DUN_RUIN | WILD_SWAMP2 | F:DUN_DARKWATER F:WEIRD_MIND | BASH_DOOR | CAN_FLY | F:ANIMAL | IM_POIS S:1_IN_10 | S:BR_POIS D:A vast, foul-smelling dragonfly. N:288:Fire giant G:P:r I:110:13d9:20:30:50 W:26:2:0:1250 O:20:80:0 B:HIT:FIRE:3d7 B:HIT:FIRE:3d7 F:DROP_60 | F:OPEN_DOOR | BASH_DOOR | DUN_LAIR | WILD_MOUNT2 | WILD_WASTE2 F:EVIL | GIANT | MALE | AURA_FIRE | DROP_SKELETON | DROP_CORPSE | LITE_1 F:IM_FIRE D:A glowing fourteen foot tall giant. Flames drip from its red skin. N:289:Hummerhorn G:I:y O:0:0:0 I:120:2d2:8:7:10 W:22:5:0:155 B:BITE:CONFUSE:2d2 F:RAND_50 | RAND_25 | CAN_FLY | DUN_LAIR | DUN_DARKWATER F:MULTIPLY | WEIRD_MIND | F:ANIMAL D:A giant buzzing wasp, its stinger drips venom. N:290:Lizardman G:h:R I:110:12d10:20:20:20 W:23:3:0:610 O:50:50:0 B:HIT:HURT:4d4 B:HIT:HURT:4d4 F:MALE | CAN_SWIM | IM_ACID | F:FRIENDS | DROP_60 | DUN_DARKWATER | WILD_SHORE | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | LITE_1 F:EVIL D:Intelligent lizard beings from the depths. N:291:Ulfast, Son of Ulfang G:p:o I:110:12d21:20:20:40 W:23:3:0:2200 O:0:100:0 B:HIT:HURT:3d5 B:HIT:HURT:3d5 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:UNIQUE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:MALE | DUN_CITY F:FORCE_MAXHP | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL D:A short and swarthy Easterling. N:292:Hammerhead G:l:b I:115:12d10:20:30:20 W:24:3:0:1350 O:0:0:0 B:BITE:HURT:4d4 B:BUTT:HURT:4d4 B:BITE:HURT:4d4 F:ANIMAL | AQUATIC | DUN_DARKWATER | DUN_CAVERN | WILD_OCEAN D:A hungry shark with a strange head. N:293:Berserker G:p:u I:113:12d14:20:32:20 W:23:3:0:1150 O:20:80:0 B:HIT:HURT:4d4 B:HIT:HURT:4d4 B:HIT:HURT:4d4 F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | OPEN_DOOR | DROP_1D2 | F:DUN_CITY | DUN_TEMPLE F:WILD_WASTE1 | DROP_SKELETON | DROP_CORPSE | LITE_1 D:A warrior in a battle-frenzy; he'll stop only when he drops. N:294:Quasit G:u:W I:110:12d3:20:15:20 W:24:2:0:520 O:0:50:30 B:BITE:LOSE_DEX:1d6 B:CLAW:HURT:1d3 B:CLAW:HURT:1d3 F:FORCE_SLEEP | CAN_FLY | F:RAND_25 | DUN_HELL | DUN_TEMPLE F:DUN_DARKWATER F:ONLY_ITEM | DROP_1D2 | F:SMART | INVISIBLE | BASH_DOOR | F:EVIL | DEMON | IM_FIRE | NONLIVING | S:1_IN_10 | S:BLINK | TPORT | TELE_TO | TELE_LEVEL | BLIND | CONF | SCARE D:The chaotic evil master's favourite pet. N:295:Sphinx G:H:o I:110:13d16:20:40:20 W:26:2:0:2400 O:100:0:0 B:CLAW:HURT:2d6 B:CLAW:HURT:2d6 F:FORCE_SLEEP | F:ONLY_GOLD | DROP_1D2 | CAN_FLY | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | DUN_TOWER | WILD_MOUNT2 F:ANIMAL S:1_IN_11 | S:SCARE | CONF D:It will eat you if you cannot answer its riddle. N:296:Imp G:u:r I:110:12d3:20:15:20 W:24:2:0:630 O:30:20:50 B:HIT:POISON:3d4 B:HIT:POISON:3d4 F:FORCE_SLEEP | CAN_FLY | DUN_TOWER | DUN_HELL F:DUN_DARKWATER F:RAND_25 | F:ONLY_ITEM | DROP_1D2 | F:SMART | INVISIBLE | COLD_BLOOD | BASH_DOOR | F:EVIL | DEMON | IM_FIRE | RES_TELE S:1_IN_10 | S:BLINK | TPORT | TELE_TO | TELE_LEVEL | BLIND | CONF | SCARE | BO_FIRE D:The lawful evil master's favourite pet. N:297:Forest troll G:T:G I:110:14d10:20:25:40 W:27:1:0:1170 O:30:70:0 B:HIT:HURT:1d4 B:HIT:HURT:1d4 B:BITE:HURT:1d6 F:MALE | DUN_LAIR | DUN_CAVERN F:DUN_DARKWATER F:FRIENDS | DROP_60 | WILD_FOREST2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | HURT_LITE D:He is green skinned and ugly. N:298:Freezing sphere G:*:w I:120:12d3:100:15:0 W:23:1:0:1300 O:0:0:0 B:EXPLODE:COLD:8d8 F:FORCE_SLEEP | CAN_FLY | DUN_TOWER | DUN_PLANAR F:EMPTY_MIND | AURA_COLD | F:IM_COLD | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING D:The embodiment of cold. N:299:Jumping fireball G:*:r I:120:11d3:100:15:0 W:22:1:0:640 O:0:0:0 B:EXPLODE:FIRE:8d8 F:FORCE_SLEEP | CAN_FLY | DUN_TOWER | DUN_PLANAR F:EMPTY_MIND | AURA_FIRE | LITE_2 F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING D:The embodiment of heat. N:300:Ball lightning G:*:B I:120:12d3:100:15:0 W:30:1:0:2500 O:0:0:0 B:EXPLODE:ELEC:8d8 F:FORCE_SLEEP | CAN_FLY | DUN_TOWER | DUN_PLANAR F:EMPTY_MIND | AURA_ELEC | LITE_2 F:IM_ELEC | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING D:A crackling ball of energy. N:301:2-headed hydra G:M:u I:110:11d21:20:30:20 W:22:2:0:590 O:100:0:0 B:BITE:HURT:2d6 B:BITE:HURT:2d6 F:FORCE_SLEEP | DUN_DARKWATER | DUN_LAIR | WILD_SHORE | WILD_SWAMP1 | F:ONLY_GOLD | DROP_1D2 | CAN_SWIM | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:ANIMAL S:1_IN_11 | S:SCARE D:A strange reptilian hybrid with two heads, guarding its hoard. N:302:Swamp thing G:H:g I:110:13d11:20:30:30 W:25:2:0:950 O:0:0:0 B:CLAW:TERRIFY:2d5 B:CLAW:TERRIFY:5d2 F:CAN_SWIM | OPEN_DOOR | BASH_DOOR | DUN_DARKWATER | WILD_SWAMP2 D:A creature that was once human, but is now as green as moss. N:303:Water spirit G:E:b I:120:10d8:12:15:40 W:19:1:0:840 O:0:0:0 B:HIT:HURT:2d4 B:HIT:HURT:2d4 F:RAND_25 | DUN_PLANAR F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | F:EVIL | IM_POIS | CAN_FLY | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR D:A whirlpool of sentient liquid. N:304:Giant red scorpion G:S:R I:110:13d7:12:34:20 W:25:1:0:940 O:0:0:0 B:BITE:HURT:2d4 B:STING:LOSE_STR:1d7 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | WILD_GRASS | WILD_WASTE1 F:ANIMAL | DROP_SKELETON D:It is fast and poisonous. N:305:Earth spirit G:E:U I:120:10d8:10:25:50 W:20:2:0:2000 O:0:0:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:RAND_25 | DUN_PLANAR F:EMPTY_MIND | COLD_BLOOD | F:PASS_WALL | CAN_FLY | NONLIVING | F:EVIL | IM_COLD | IM_ELEC | IM_POIS | HURT_ROCK | F:NO_CONF | NO_SLEEP | NO_FEAR D:A whirling form of sentient rock. N:306:Fire spirit G:E:R I:120:9d7:16:15:20 W:18:2:0:790 O:0:0:0 B:HIT:FIRE:2d6 B:HIT:FIRE:2d6 F:RAND_25 | DUN_PLANAR F:EMPTY_MIND | BASH_DOOR | CAN_FLY | NONLIVING | F:EVIL | IM_FIRE | IM_POIS | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR | AURA_FIRE D:A whirlwind of sentient flame. N:307:Fire hound G:Z:r I:110:13d4:30:15:0 W:26:1:0:1450 O:0:0:0 B:BITE:FIRE:1d6 B:BITE:FIRE:1d6 B:BITE:FIRE:1d3 F:FORCE_SLEEP | F:FRIENDS | DUN_CAVERN | DUN_PLANAR F:BASH_DOOR | LITE_2 F:ANIMAL | IM_FIRE | DROP_SKELETON | DROP_CORPSE S:1_IN_10 | S:BR_FIRE D:Flames lick at its feet and its tongue is a blade of fire. You can feel a D:furnace heat radiating from the creature. N:308:Cold hound G:Z:W I:110:14d3:30:15:0 W:28:1:0:1800 O:0:0:0 B:BITE:COLD:1d6 B:CLAW:HURT:1d8 B:BITE:HURT:1d6 F:FORCE_SLEEP | F:FRIENDS | DUN_CAVERN | DUN_PLANAR F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | IM_COLD S:1_IN_10 | S:BR_COLD D:A hound as tall as a man, this creature appears to be composed of angular D:planes of ice. Cold radiates from it and freezes your breath in the air. N:309:Energy hound G:Z:y I:110:14d3:30:15:0 W:27:1:0:1300 O:0:0:0 B:BITE:ELEC:1d6 B:BITE:ELEC:1d6 B:BITE:ELEC:1d3 F:FORCE_SLEEP | DUN_CAVERN | DUN_PLANAR F:FRIENDS | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | LITE_2 F:ANIMAL | IM_ELEC S:1_IN_10 | S:BR_ELEC D:Saint Elmo's Fire forms a ghostly halo around this hound, and sparks sting D:your fingers as energy builds up in the air around you. N:310:Potion mimic G:!:w I:110:13d6:25:15:0 W:26:3:0:860 O:0:0:0 B:HIT:POISON:3d4 B:HIT:HURT:2d3 B:HIT:HURT:2d3 F:CHAR_MIMIC | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | NEVER_MOVE | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | S:1_IN_6 | S:BLIND | CONF | SCARE | CAUSE_2 | BO_COLD D:A strange creature that disguises itself as discarded objects to lure D:unsuspecting adventurers within reach of its venomous claws. N:311:Door mimic G:+:U I:110:13d6:25:15:0 W:26:6:0:790 O:0:0:0 B:HIT:POISON:3d4 B:HIT:CONFUSE:2d3 B:HIT:PARALYZE:2d3 F:CHAR_MIMIC | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | NEVER_MOVE | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | S:1_IN_6 | S:BLIND | CONF | SCARE | CAUSE_2 | BO_COLD D:A strange creature that disguises itself as a door to lure D:unsuspecting adventurers within reach of its venomous claws. N:312:Blink dog G:C:B I:120:13d3:20:30:10 W:25:2:0:1750 O:0:0:0 B:BITE:HURT:1d8 F:RAND_25 | DUN_TEMPLE | WILD_FOREST2 F:FRIENDS | F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL| RES_TELE S:1_IN_4 | S:BLINK | TELE_TO D:A strange magical member of the canine race, its form seems to shimmer and D:fade in front of your very eyes. N:313:Uruk G:o:w I:110:13d5:20:25:20 W:25:1:0:1100 O:20:80:0 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:MALE | DUN_CITY | WILD_MOUNT2 F:FORCE_MAXHP | FRIENDS | DROP_60 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | ORC | IM_POIS S:1_IN_12 S:ARROW D:He is a cunning orc of power, as tall as a man, and stronger. It fears D:little. N:314:Shagrat, the Orc Captain G:o:u I:110:13d22:20:30:20 W:26:2:0:5800 O:10:90:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY F:FORCE_MAXHP | F:ESCORT | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_POIS D:He is an Uruk of power and great cunning. N:315:Gorbag, the Orc Captain G:o:u I:110:13d22:20:30:20 W:26:3:0:5800 O:10:90:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY F:FORCE_MAXHP | F:ESCORT | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_POIS D:A gruesomely ugly but cunning orc, his eyes regard you with hatred. His D:powerful arms flex menacingly as he advances. N:316:Shambling mound G:,:g I:110:12d8:20:8:40 W:23:2:0:550 O:100:0:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:ONLY_GOLD | DROP_90 | DUN_LAIR | DUN_TOWER | WILD_SWAMP2 | WILD_SWAMP1 F:STUPID | EMPTY_MIND | OPEN_DOOR | BASH_DOOR | F:EVIL | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_4 | S:SHRIEK D:A pile of rotting vegetation that slides towards you with a disgusting D:stench, waking all it nears. N:317:White shark G:l:W I:120:13d6:20:25:10 W:26:1:0:1850 O:0:0:0 B:BITE:HURT:3d5 B:BITE:HURT:3d5 F:ANIMAL | AQUATIC | DUN_CAVERN | WILD_OCEAN D:Fast moving hunter of the depths, when this creature moves, D:everybody in water is in danger! N:318:Chaos beastman G:H:u I:110:15d8:20:50:30 W:30:2:0:2600 O:30:50:20 B:HIT:CONFUSE:3d5 B:HIT:EXP_20:1d5 F:DROP_1D2 | DUN_LAIR | DUN_TEMPLE | DUN_HORROR | WILD_WASTE1 | WILD_WASTE2 F:OPEN_DOOR | BASH_DOOR | NO_CONF | NO_SLEEP | DROP_CORPSE F:EVIL | IM_FIRE | ATTR_ANY | ATTR_MULTI S:1_IN_8 S:MISSILE | BO_FIRE | CONF | TPORT D:A truly loathsome thing, twisted by chaos, it is a mixture D:of several different kinds of creature. N:319:Daemonette of Slaanesh G:u:R I:120:15d5:20:25:30 W:29:2:0:3500 O:30:0:30 B:CLAW:CONFUSE:2d5 B:CLAW:CONFUSE:2d5 F:DUN_HELL | DUN_CAVERN F:FORCE_MAXHP | DROP_60 | FEMALE | F:OPEN_DOOR | BASH_DOOR | NO_CONF | NO_SLEEP | F:EVIL | DEMON | IM_POIS | IM_FIRE S:1_IN_8 S:SCARE | S_DEMON | CAUSE_2 | CONF | BO_FIRE | BO_COLD D:It is minor female demon, vaguely human-like, but with crab-like D:pincers instead of hands. She wears a rather indecent skimpy D:leather bikini, moves quickly and casts deadly spells! N:320:Giant bronze dragon fly G:F:U I:120:8d2:12:10:50 W:27:1:0:1100 O:0:0:0 F:FORCE_SLEEP | NEVER_BLOW | F:RAND_50 | RAND_25 | CAN_FLY | F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_DARKWATER | WILD_MOUNT1 | F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_9 | S:BR_CONF D:This vast gleaming bronze fly has wings which beat mesmerically fast. N:321:Stone giant G:P:s I:110:14d12:20:35:50 W:28:1:0:2350 O:10:90:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 F:DROP_60 | DUN_LAIR | DUN_MINE F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | GIANT | MALE | WILD_MOUNT2 D:It is eighteen feet tall and looking at you. N:322:Giant black dragon fly G:F:D I:120:8d2:12:10:50 W:24:2:0:720 O:0:0:0 F:FORCE_SLEEP | NEVER_BLOW | RAND_50 | RAND_25 | F:WEIRD_MIND | BASH_DOOR | CAN_FLY | DUN_LAIR | DUN_DARKWATER F:WILD_SWAMP1 | WILD_SWAMP2 F:ANIMAL | IM_ACID S:1_IN_9 | S:BR_ACID D:The size of a large bird, this fly drips caustic acid. N:323:Stone golem G:g:s I:100:16d10:12:35:10 W:31:2:0:1700 O:0:0:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 F:DUN_TOWER F:COLD_BLOOD | EMPTY_MIND | BASH_DOOR | F:IM_FIRE | IM_COLD | IM_POIS | F:HURT_ROCK | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING D:It is a massive animated statue. N:324:Red mold G:m:r I:110:13d7:2:8:70 W:25:1:0:1060 O:0:0:0 B:TOUCH:FIRE:4d4 F:NEVER_MOVE | DUN_LAIR | DUN_MINE | DUN_CAVERN F:STUPID | EMPTY_MIND | F:IM_FIRE | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a strange red growth on the dungeon floor; it seems to burn with D:flame. N:325:Giant gold dragon fly G:F:y I:120:8d2:12:10:50 W:25:2:0:1180 O:0:0:0 B:BITE:HURT:1d3 F:FORCE_SLEEP | F:RAND_50 | RAND_25 | DUN_LAIR | WILD_MOUNT2 | F:WEIRD_MIND | BASH_DOOR | F:ANIMAL | IM_FIRE | CAN_FLY S:1_IN_9 | S:BR_SOUN D:Large beating wings support this dazzling insect. A loud buzzing noise D:pervades the air. N:326:Stunwall G:#:W I:110:12d2:45:15:0 W:25:5:0:790 O:0:0:0 B:TOUCH:PARALYZE:10d1 B:TOUCH:PARALYZE:10d1 F:DUN_MINE | DUN_CAVERN | DUN_TOWER F:NEVER_MOVE | IM_COLD | COLD_BLOOD | IM_ACID | NO_FEAR | F:IM_POIS | NO_CONF | NO_SLEEP | CHAR_MIMIC F:EMPTY_MIND | HURT_ROCK D:A sentient section of wall. N:327:Ghast G:z:U I:110:15d6:40:20:20 W:30:10:0:3000 O:20:40:30 B:KICK:HURT:3d3 B:KICK:HURT:9d1 B:BITE:HURT:6d2 F:DROP_60 | DUN_GRAVE F:NO_SLEEP | NO_CONF | UNDEAD | EVIL | IM_POIS | IM_COLD | F:COLD_BLOOD | HURT_LITE | CAN_SWIM F:BASH_DOOR | OPEN_DOOR S:1_IN_10 S:ELDRITCH_HORROR D:A repulsive being which leaps on long hind legs like a kangaroo. D:Its face is curiously human despite the absence of a nose, a D:forehead, and "other important particulars". N:328:Ixitxachitl priest G:l:s I:110:14d5:20:20:20 W:28:1:0:1250 O:0:0:0 B:STING:POISON:2d7 B:STING:POISON:2d7 F:ANIMAL | EVIL | AQUATIC | IM_POIS | DUN_DARKWATER | DUN_LAIR | WILD_SHORE S:1_IN_6 S:TELE_TO | HEAL | SCARE | CAUSE_2 | BLIND | S_MONSTER D:A devil ray of the depths, with priestly magic. N:329:Huorn G:%:g I:110:6d59:40:22:20 W:11:1:0:350 O:30:30:30 B:CRUSH:HURT:3d6 B:CRUSH:HURT:3d6 B:CRUSH:HURT:3d6 B:CRUSH:HURT:3d6 F:DROP_60 | NO_SLEEP | NO_CONF | ANIMAL | WEIRD_MIND | EVIL F:RES_WATE | IM_COLD | NEVER_MOVE | WILD_FOREST1 | WILD_FOREST2 S:1_IN_9 S:BLINK | TELE_TO D:A very strong near-sentient tree, which has become hostile D:to humanoid forms of life. N:330:Bolg, Son of Azog G:o:R I:120:15d24:20:25:20 W:29:4:0:12000 O:0:100:0 B:HIT:HURT:3d6 B:HIT:HURT:3d6 B:HIT:HURT:3d6 B:HIT:HURT:3d6 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY F:FORCE_MAXHP | F:ESCORT | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_POIS D:A large and powerful orc. He looks just like his daddy. He is tall and D:fast, but fortunately blessed with orcish brains. N:331:Phase spider G:S:B I:120:15d3:15:13:80 W:30:2:0:2600 O:0:0:0 B:BITE:HURT:1d8 B:BITE:POISON:1d6 B:BITE:POISON:1d6 F:FRIENDS | DUN_TEMPLE | DUN_CAVERN | DUN_LAIR F:WILD_FOREST1 | WILD_FOREST2 | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | CAN_SWIM | F:ANIMAL | IM_POIS | RES_TELE S:1_IN_5 | S:BLINK | TELE_TO D:A spider that never seems quite there. Everywhere you look it is just D:half-seen in the corner of one eye. N:332:Lizard king G:h:R I:120:15d11:20:20:20 W:30:3:0:2600 O:50:50:0 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:WAIL:TERRIFY F:MALE | CAN_SWIM | IM_ACID | IM_POIS | WILD_SHORE | DUN_DARKWATER F:DROP_60 | DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | FORCE_MAXHP D:A lizardman leader. N:333:Landmine G:.:w I:110:14d3:30:12:10 W:28:5:0:1900 O:0:0:0 B:EXPLODE:HURT:25d2 F:DUN_TOWER | DUN_MINE F:CHAR_CLEAR | CHAR_MIMIC | ATTR_CLEAR | F:NEVER_MOVE | FORCE_MAXHP | F:EMPTY_MIND | INVISIBLE | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | D:It was left here to be used against intruders. N:334:Wyvern G:d:G I:120:17d23:20:32:20 W:33:2:0:7400 O:100:0:0 B:BITE:HURT:2d6 B:BITE:HURT:2d6 B:BITE:HURT:2d6 B:STING:POISON:1d6 F:FORCE_SLEEP | F:ONLY_GOLD | DROP_60 | DROP_90 | IM_POIS | CAN_FLY | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DRAGON | DUN_LAIR | F:WILD_FOREST1 | WILD_MOUNT2 | WILD_MOUNT1 | DROP_SKELETON | DROP_CORPSE F:ANIMAL | EVIL D:A fast-moving and deadly dragonian animal. Beware its poisonous sting! N:335:Great eagle G:B:u I:120:12d32:20:65:20 W:23:2:0:1500 O:0:0:0 B:CLAW:HURT:6d3 B:CLAW:HURT:6d3 B:BITE:HURT:3d6 F:CAN_FLY | F:WILD_MOUNT1 | WILD_MOUNT2 | WILD_WASTE2 F:ANIMAL | GOOD | DROP_CORPSE D:An agent of supernatural beings, this creature looks like a D:huge eagle. N:336:Livingstone G:#:W I:110:14d3:45:14:20 W:28:4:0:690 O:0:0:0 B:HIT:HURT:2d5 B:HIT:HURT:2d5 F:DUN_MINE | DUN_CAVERN F:MULTIPLY | NEVER_MOVE | IM_COLD | COLD_BLOOD | IM_ACID | NO_FEAR | F:IM_POIS | NO_CONF | NO_SLEEP | FRIENDS | CHAR_MIMIC | HURT_ROCK D:A sentient section of wall. N:337:Earth hound G:Z:u I:110:15d6:30:15:0 W:30:1:0:2050 O:0:0:0 B:BITE:HURT:1d8 B:BITE:HURT:1d8 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 F:FORCE_SLEEP | DUN_CAVERN | DUN_PLANAR F:FRIENDS | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | F:ANIMAL S:1_IN_10 | S:BR_SHAR D:A beautiful crystalline shape does not disguise the danger this hound D:clearly presents. Your flesh tingles as it approaches. N:338:Air hound G:Z:B I:110:15d6:30:15:0 W:30:1:0:2700 O:0:0:0 B:BITE:HURT:1d8 B:BITE:HURT:1d8 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 F:FORCE_SLEEP | DUN_CAVERN | DUN_PLANAR F:FRIENDS | CAN_FLY | F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | IM_POIS S:1_IN_10 | S:BR_POIS D:Swirling vapours surround this beast as it floats towards you, seemingly D:walking on air. Noxious gases sting your throat. N:339:Sabre-tooth tiger G:f:y I:120:15d14:40:25:0 W:30:2:0:3400 O:0:0:0 B:CLAW:HURT:1d10 B:CLAW:HURT:1d10 B:BITE:HURT:1d10 B:BITE:HURT:1d10 F:BASH_DOOR | WILD_FOREST1 | DUN_LAIR | DUN_TEMPLE | WILD_GRASS | F:ANIMAL | DROP_SKELETON | DROP_CORPSE D:A fierce and dangerous cat, its huge tusks and sharp claws would lacerate D:even the strongest armour. N:340:Water hound G:Z:b I:110:15d6:30:15:0 W:30:2:0:1750 O:0:0:0 B:BITE:ACID:2d8 B:BITE:ACID:2d8 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 F:FORCE_SLEEP | DUN_CAVERN | DUN_PLANAR F:FRIENDS | F:BASH_DOOR | CAN_SWIM | DROP_SKELETON | DROP_CORPSE F:ANIMAL | IM_ACID S:1_IN_10 | S:BR_ACID D:Liquid footprints follow this hound as it pads around the dungeon. An D:acrid smell of acid rises from the dog's pelt. N:341:Chimera G:H:y I:110:15d5:12:13:10 W:30:1:0:1600 O:0:0:0 B:BITE:HURT:1d10 B:BITE:FIRE:1d3 B:BITE:FIRE:1d3 F:FORCE_SLEEP | CAN_FLY | DROP_CORPSE | F:BASH_DOOR | DUN_HORROR | WILD_MOUNT2 | F:IM_FIRE S:1_IN_10 | S:BR_FIRE D:It is a strange concoction of lion, dragon and goat. It looks very odd D:but very avoidable. N:342:Quylthulg G:Q:W I:110:14d3:10:1:2 W:28:1:0:1500 O:0:0:0 F:DUN_CAVERN | DUN_TEMPLE F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW F:EMPTY_MIND | INVISIBLE | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_4 | S:BLINK | S:S_MONSTER D:It is a strange pulsing mound of flesh. N:343:Sasquatch G:Y:W I:120:17d16:15:20:10 W:34:3:0:4500 O:0:0:0 B:CLAW:HURT:1d10 B:CLAW:HURT:1d10 B:BITE:HURT:2d8 F:OPEN_DOOR | BASH_DOOR | DUN_LAIR | WILD_MOUNT2 F:WILD_FOREST1 | WILD_FOREST2 | WILD_WASTE2 | F:ANIMAL | IM_COLD | DROP_SKELETON | DROP_CORPSE D:A tall shaggy, furry humanoid, it could call the yeti brother. N:344:Weir G:C:W I:110:13d7:15:15:40 W:25:2:0:1050 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 F:RAND_25 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | EVIL | FRIENDS | DUN_LAIR | DUN_HORROR D:It is a tracker; half human, half beast. N:345:Whale G:l:G I:110:15d30:15:25:70 W:30:4:0:2400 O:0:0:0 B:CRUSH:HURT:1d20 B:CRUSH:HURT:1d20 F:RAND_25 | FORCE_MAXHP | RES_WATE F:ANIMAL | AQUATIC | DUN_CAVERN | WILD_OCEAN D:Although it looks like a fish and lives in water, it is in fact D:a mammal. And it is huge! N:346:Electric eel G:J:B I:110:16d11:15:20:70 W:31:2:0:2000 O:0:0:0 B:TOUCH:ELEC:2d7 B:TOUCH:ELEC:7d2 B:TOUCH:ELEC:2d7 F:AQUATIC | ANIMAL | RAND_25 | IM_ELEC | RES_WATE | DUN_DARKWATER | DUN_CAVERN | WILD_OCEAN D:This cute little serpentine creature can create a deadly voltage. D:Better watch out! N:347:Werewolf G:C:D I:110:14d22:15:18:20 W:28:2:0:1450 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 B:BITE:HURT:1d10 F:RAND_25 | DUN_LAIR | DUN_MINE | WILD_WASTE1 | WILD_WASTE2 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | F:ANIMAL | EVIL D:It is a huge wolf with eyes that glow with manly intelligence. N:348:Dark elven lord G:h:D I:120:16d13:20:20:30 W:32:2:0:4400 O:0:80:20 B:HIT:HURT:3d8 B:HIT:HURT:3d5 F:MALE | DUN_CITY | DUN_TOWER | WILD_FOREST2 F:FORCE_SLEEP | F:ONLY_ITEM | DROP_2D2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | HURT_LITE S:1_IN_5 | S:HASTE | BLIND | CONF | DARKNESS | BO_FIRE | BO_COLD | MISSILE D:A dark elven figure in armour and radiating evil power. N:349:Cloud giant G:P:B I:110:15d17:20:30:50 W:30:1:0:1850 O:10:90:0 B:HIT:ELEC:3d8 B:HIT:ELEC:3d8 F:DROP_90 | F:DUN_LAIR F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | GIANT | IM_ELEC | MALE D:It is a twenty foot tall giant wreathed in clouds. N:350:Ugluk, the Uruk G:o:u I:110:16d29:20:42:20 W:32:4:0:4400 O:10:90:0 B:HIT:HURT:3d5 B:HIT:HURT:3d5 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY | WILD_WASTE2 F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ESCORT | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_POIS D:Another of Morgoth's servants, this orc is strong and cunning. He is ugly D:and scarred from many power struggles. N:351:Blue dragon bat G:b:B I:130:6d2:12:13:50 W:30:1:0:3800 O:0:0:0 B:BITE:ELEC:1d3 F:FORCE_SLEEP | DUN_CAVERN | DUN_LAIR F:RAND_50 F:BASH_DOOR | CAN_FLY | LITE_1 F:ANIMAL | IM_ELEC S:1_IN_4 | S:BR_ELEC D:It is a glowing blue bat with a sharp tail. N:352:Scroll mimic G:?:w I:110:16d7:30:20:0 W:31:3:0:1750 O:0:0:0 B:HIT:POISON:3d4 B:HIT:POISON:3d4 B:HIT:HURT:2d3 B:HIT:HURT:2d3 F:CHAR_MIMIC | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | NEVER_MOVE | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | S:1_IN_5 | S:BLIND | CONF | SCARE | CAUSE_2 | BO_FIRE | S:S_MONSTER D:A strange creature that disguises itself as discarded objects to lure D:unsuspecting adventurers within reach of its venomous claws. N:353:Chest mimic G:&:W I:110:15d7:30:20:0 W:30:6:0:1450 O:0:0:0 B:GAZE:CONFUSE:2d3 B:CLAW:POISON:3d4 B:CLAW:POISON:3d4 B:SPIT:BLIND:2d3 F:CHAR_MIMIC | DUN_TOWER | DUN_CITY F:FORCE_SLEEP | NEVER_MOVE | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | S:1_IN_5 | S:BLIND | CONF | SCARE | CAUSE_2 | BO_POIS | BA_POIS | S:S_MONSTER D:A strange creature that disguises itself as a chest to lure D:unsuspecting adventurers within reach of its venomous claws. N:354:Fire vortex G:v:r I:110:15d4:100:12:0 W:29:1:0:1300 O:0:0:0 B:ENGULF:FIRE:3d3 F:FORCE_SLEEP | RAND_50 | CAN_FLY | DUN_PLANAR | DUN_CAVERN | ATTR_MULTI F:EMPTY_MIND | BASH_DOOR | POWERFUL | AURA_FIRE | LITE_2 F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING S:1_IN_6 | S:BR_FIRE D:A whirling maelstrom of fire. N:355:Water vortex G:v:b I:110:16d4:100:12:0 W:32:1:0:2200 O:0:0:0 B:ENGULF:ACID:3d3 F:FORCE_SLEEP | RAND_50 | DUN_PLANAR | DUN_CAVERN F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY | ATTR_MULTI F:IM_ACID | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING S:1_IN_6 | S:BR_ACID D:A caustic spinning whirlpool of water. N:356:Lugdush, the Uruk G:o:b I:110:16d32:20:45:20 W:32:3:0:12300 O:10:90:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY | WILD_WASTE1 | WILD_WASTE2 F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ESCORT | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP D:A strong and cunning orc warrior, Lugdush sneers as he insults your mother. N:357:Arch-vile G:u:W I:120:18d5:100:15:0 W:35:1:0:8200 O:0:0:0 B:CLAW:HURT:3d9 B:CLAW:HURT:3d9 F:DUN_GRAVE | DUN_RUIN F:RAND_50 | EVIL | DEMON | FORCE_SLEEP | FORCE_MAXHP | F:OPEN_DOOR | BASH_DOOR | POWERFUL | COLD_BLOOD | F:IM_FIRE | RES_NETH | NO_CONF | NO_SLEEP | NONLIVING | NO_STUN S:1_IN_4 | S:BA_FIRE | ANIM_DEAD D:A pale, corpse-like lesser demon, who moves very fast and spawns evil D:everywhere. N:358:Cold vortex G:v:W I:110:13d5:100:12:0 W:25:1:0:1250 O:0:0:0 B:ENGULF:COLD:3d3 F:DUN_PLANAR | DUN_CAVERN F:FORCE_SLEEP | RAND_50 | AURA_COLD | COLD_BLOOD | ATTR_MULTI F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY | F:IM_COLD | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING S:1_IN_6 | S:BR_COLD D:A twisting whirlpool of frost. N:359:Energy vortex G:v:y I:110:17d6:100:12:0 W:34:1:0:2900 O:0:0:0 B:ENGULF:ELEC:5d5 F:DUN_PLANAR | DUN_CAVERN F:FORCE_SLEEP | RAND_50 | CAN_FLY | ATTR_MULTI F:EMPTY_MIND | BASH_DOOR | POWERFUL | AURA_ELEC | LITE_2 F:IM_ELEC | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING S:1_IN_6 | S:BR_ELEC D:A shimmering tornado of air, sparks crackle along its length. N:360:Globefish G:l:w I:110:18d8:20:15:30 W:35:1:0:4200 O:0:0:0 B:BITE:POISON:5d5 B:BITE:POISON:5d5 F:EMPTY_MIND | BASH_DOOR | POWERFUL | AQUATIC | F:IM_POIS | NO_STUN | DUN_DARKWATER | WILD_SHORE | FORCE_MAXHP S:1_IN_7 | S:BR_POIS D:This cute fish is among the most poisonous creatures there are. N:361:Carrion G:B:W I:113:16d9:20:15:20 W:32:1:0:2800 O:40:30:0 B:CLAW:POISON:2d3 B:CLAW:POISON:2d3 B:BITE:EXP_20:1d8 F:DROP_90 | DUN_GRAVE | DUN_HELL | DUN_HORROR F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | ANIMAL | UNDEAD | CAN_FLY | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:A mummified, undead great avian creature, who now serves as D:a steed for mighty undead creatures. N:362:Mummified orc G:z:u I:110:15d7:20:14:75 W:29:1:0:1350 O:10:80:0 B:HIT:HURT:2d4 B:HIT:HURT:2d4 F:DROP_90 | DUN_GRAVE | DUN_MINE F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | UNDEAD | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is an orcish figure covered in wrappings. N:363:Killer whale G:l:w I:115:16d16:12:26:30 W:31:1:0:2900 O:0:0:0 B:BITE:HURT:8d3 B:BITE:HURT:8d3 F:AQUATIC | DUN_CAVERN | WILD_OCEAN F:ANIMAL D:An almost beautiful, deadly beast. N:364:Serpent man G:J:g I:120:16d8:20:20:20 W:32:3:0:1900 O:30:20:30 B:BITE:POISON:5d5 B:BITE:POISON:5d5 F:DUN_DARKWATER | WILD_SWAMP1 F:MALE | CAN_SWIM | IM_POIS | IM_ACID | F:DROP_60 | DROP_1D2 | FRIENDS | DROP_CORPSE | F:OPEN_DOOR | BASH_DOOR | LITE_2 F:EVIL | S:1_IN_8 S:BA_POIS | S_MONSTER | SCARE | HOLD | BO_POIS D:"They walked lithely and sinnously erect on pre-mammalian D:members, their pied and hairless bodies bending with great D:suppleness. There was a loud hissing of formulae as they D:went to and fro." N:365:Vampiric mist G:#:D I:110:15d4:12:26:10 W:30:1:0:840 O:0:0:0 B:ENGULF:EXP_VAMP:2d6 F:RAND_25 | F:IM_POIS | IM_ACID | RES_NETH | DUN_MINE | DUN_TOWER | WILD_SWAMP2 | F:EVIL | EMPTY_MIND | COLD_BLOOD | FRIENDS D:A cloud of evil, sentient mist. N:366:Killer stag beetle G:K:s I:110:17d7:12:27:30 W:33:1:0:2150 O:0:0:0 B:CLAW:HURT:1d12 B:CLAW:HURT:1d12 F:RAND_25 | DUN_LAIR | WILD_FOREST1 | DROP_CORPSE | F:WEIRD_MIND | BASH_DOOR | F:ANIMAL | CAN_FLY D:It is a giant beetle with vicious claws. N:367:Iron golem G:g:W I:110:18d38:12:40:10 W:35:2:0:3600 O:0:0:0 B:HIT:HURT:1d12 F:FORCE_SLEEP | DUN_TOWER F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | F:IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_7 | S:SLOW D:It is a massive metal statue that moves steadily towards you. N:368:Auto-roller G:g:s I:120:18d37:10:40:12 W:35:2:0:5000 O:0:0:0 B:CRUSH:HURT:1d8 B:CRUSH:HURT:1d8 B:CRUSH:HURT:1d8 B:CRUSH:HURT:1d8 F:FORCE_SLEEP | RES_TELE | DUN_MINE | DUN_CITY | DUN_TOWER | SILLY F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_COLD | IM_ELEC | IM_POIS F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING D:It looks like a huge spiked roller, moving on its own towards you. N:369:Giant yellow scorpion G:S:y I:110:15d5:12:19:20 W:30:1:0:1350 O:0:0:0 B:BITE:HURT:1d8 B:STING:POISON:2d5 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | WILD_WASTE1 F:ANIMAL | DROP_SKELETON D:It is a giant scorpion with a sharp stinger. N:370:Jade monk G:p:G I:120:21d24:30:24:10 W:41:2:0:8700 O:30:0:60 B:KICK:HURT:5d2 B:HIT:HURT:5d1 B:KICK:HURT:5d5 B:HIT:HURT:5d1 F:DUN_TEMPLE F:MALE | DROP_60 | DROP_1D2 | OPEN_DOOR | BASH_DOOR F:IM_FIRE | IM_POIS | NO_FEAR | NO_CONF | NO_SLEEP | F:DROP_CORPSE | DROP_SKELETON | LITE_2 S:1_IN_6 | S:BO_ELEC D:A green-garbed monk, trained in martial arts. N:371:Black ooze G:j:D I:90:16d3:10:10:1 W:31:1:0:118 O:30:0:50 B:TOUCH:ACID:2d6 F:RAND_50 | DROP_60 | DUN_MINE | DUN_DARKWATER | DUN_RUIN F:STUPID | EMPTY_MIND | MULTIPLY | CAN_SWIM | F:TAKE_ITEM | KILL_BODY | OPEN_DOOR | BASH_DOOR | F:IM_POIS | NO_FEAR S:1_IN_11 | S:DRAIN_MANA D:It is a strangely moving puddle. N:372:Hardened warrior G:p:u I:110:16d7:20:20:40 W:31:1:0:1400 O:0:100:0 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:MALE | DUN_TEMPLE | DUN_CITY | WILD_GRASS F:DROP_1D2 | LITE_2 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL D:A scarred warrior who moves with confidence. N:373:Azog, King of the Uruk-Hai G:o:r I:120:16d40:20:40:20 W:31:5:0:14500 O:10:90:0 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ESCORT | ESCORTS | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | ORC | IM_POIS D:He is also known as the King of Khazad-dum. His ego is renowned to be D:bigger than his head. N:374:Fleshhound of Khorne G:C:R I:120:16d18:15:15:70 W:32:3:0:3900 O:0:0:0 B:CLAW:HURT:4d1 B:CLAW:HURT:4d1 B:BITE:HURT:6d1 B:BITE:HURT:6d1 F:BASH_DOOR | DEMON | NO_STUN | NO_FEAR | DUN_HELL | DUN_CAVERN F:ANIMAL | EVIL | IM_FIRE | NO_SLEEP | NO_CONF F:RES_NETH | RES_NEXU | RES_DISE | RES_PLAS D:A revolting canine creature, a huge demon hound with a somewhat D:reptilian head. N:375:Dark elven warlock G:h:v I:120:19d3:20:8:20 W:37:1:0:6150 O:0:0:100 B:HIT:HURT:1d3 B:HIT:HURT:1d3 F:MALE | DUN_TOWER F:FORCE_SLEEP | F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | FRIENDS F:EVIL | IM_POIS | HURT_LITE S:1_IN_5 | S:CONF | MISSILE | DARKNESS | BO_MANA D:A dark elven mage with spells of frightening destructive power. N:376:Master rogue G:p:D I:120:27d4:20:18:40 W:53:2:0:13500 O:80:10:10 B:HIT:HURT:2d8 B:HIT:HURT:2d8 B:HIT:EAT_GOLD:4d4 F:DUN_TOWER | DUN_TEMPLE | DUN_CITY | DUN_RUIN | DUN_DARKWATER F:WILD_FOREST1 | WILD_WASTE1 | WILD_GRASS F:MALE | DROP_SKELETON | DROP_CORPSE F:DROP_2D2 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL D:A thief of great power and shifty speed. N:377:Red dragon bat G:b:r I:130:9d2:12:14:50 W:33:1:0:5700 O:0:0:0 B:BITE:FIRE:1d3 F:FORCE_SLEEP | RAND_50 | DUN_CAVERN | DUN_LAIR F:BASH_DOOR | CAN_FLY | DROP_CORPSE F:ANIMAL | IM_FIRE | LITE_1 S:1_IN_4 | S:BR_FIRE D:It is a sharp-tailed bat, wreathed in fire. N:378:Killer white beetle G:K:w I:110:16d7:14:27:30 W:32:1:0:1650 O:0:0:0 B:BITE:HURT:4d5 F:RAND_25 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_CAVERN | WILD_FOREST2 | DROP_CORPSE F:ANIMAL | CAN_FLY D:It is looking for prey. N:379:Ice skeleton G:s:w I:110:17d8:20:17:60 W:33:1:0:2100 O:20:0:80 B:CLAW:COLD:2d3 B:CLAW:COLD:2d3 F:ONLY_ITEM | DROP_90 | DUN_GRAVE | DUN_RUIN F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a skeleton covered in frost. N:380:Angamaite of Umbar G:p:u I:110:19d30:25:40:25 W:38:2:0:15000 O:0:80:20 B:HIT:HURT:4d6 B:HIT:HURT:4d6 B:HIT:HURT:4d6 B:HIT:HURT:4d6 F:DUN_CITY F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | IM_FIRE | IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_4 | S:SLOW | FORGET D:A Black Numenorean who hates the men of the west. N:381:Kouko G:W:G I:110:17d4:20:15:30 W:34:1:0:2050 O:20:0:40 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:TOUCH:EXP_20 F:FORCE_SLEEP | RAND_25 | DUN_GRAVE F:DROP_90 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP S:1_IN_10 | S:SCARE | DRAIN_MANA D:It is a ghostly apparition with a humanoid form. N:382:Mime, the Nibelung G:h:u I:110:17d34:20:40:10 W:34:2:0:11700 O:90:10:0 B:HIT:HURT:3d6 B:HIT:HURT:3d6 B:HIT:UN_BONUS B:TOUCH:EAT_GOLD F:DUN_MINE F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | EVIL | F:IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP | RES_DISE | RES_TELE S:1_IN_8 | S:HEAL | HASTE | BO_FIRE D:This tiny night dwarf is as greedy for gold as his brother, Alberich. N:383:Hagen, son of Alberich G:h:o I:110:18d32:20:40:10 W:35:2:0:26000 O:50:50:0 B:HIT:HURT:3d7 B:HIT:HURT:3d7 B:HIT:HURT:3d7 B:HIT:UN_BONUS F:DUN_MINE F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_90 | DROP_GOOD | DROP_CHOSEN | F:OPEN_DOOR | BASH_DOOR | EVIL | F:IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_8 | S:HEAL | HASTE | BO_FIRE D:Alberich's son, born of a mortal woman won with gold. N:384:Meneldor the Swift G:B:u I:130:8d70:20:32:20 W:16:5:0:950 O:0:0:0 B:CLAW:HURT:7d3 B:CLAW:HURT:7d3 B:BITE:HURT:3d7 F:CAN_FLY | UNIQUE | FORCE_MAXHP | DROP_CORPSE F:WILD_MOUNT2 | WILD_WASTE2 | FRIENDLY F:ANIMAL | GOOD D:An agent of supernatural beings, this creature looks like a D:huge eagle. N:385:Phantom beast G:G:b I:110:19d6:20:20:40 W:37:1:0:2350 O:0:0:0 B:HIT:HURT:40d2 B:HIT:HURT:40d2 F:DUN_TOWER | DUN_GRAVE F:PASS_WALL | NO_SLEEP | COLD_BLOOD | NONLIVING | NO_FEAR | F:FORCE_MAXHP | RES_TELE | EMPTY_MIND | CAN_FLY D:A creature that is half real, half illusion. N:386:Great white shark G:l:w I:120:17d27:20:35:20 W:34:2:0:6600 O:0:0:0 B:BITE:HURT:3d6 B:BITE:HURT:3d6 B:BITE:HURT:3d6 B:BITE:HURT:3d6 F:FORCE_SLEEP | AQUATIC | DUN_CAVERN | WILD_OCEAN F:ANIMAL D:A very large carnivorous fish. N:387:4-headed hydra G:M:U I:120:17d27:20:35:20 W:34:2:0:8100 O:100:0:0 B:BITE:HURT:2d6 B:BITE:HURT:2d6 B:BITE:HURT:2d6 B:BITE:HURT:2d6 F:FORCE_SLEEP | F:ONLY_GOLD | DROP_1D2 | DROP_2D2 | DUN_DARKWATER | DUN_LAIR | WILD_SWAMP1 | WILD_SHORE | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_SKELETON | DROP_CORPSE F:ANIMAL S:1_IN_7 | S:SCARE D:A strange reptilian hybrid with four heads, guarding its hoard. N:388:Lesser hell-beast G:U:s I:115:18d63:10:50:50 W:36:4:0:6900 O:0:0:0 B:GAZE:TERRIFY:1d4 B:GAZE:TERRIFY:1d4 B:CRUSH:HURT:44d1 F:DUN_MINE | DUN_LAIR | DUN_HELL | DUN_HORROR F:EVIL | IM_FIRE | IM_POIS F:RES_NETH | RES_WATE | RES_PLAS | RES_DISE | RES_NEXU | F:KILL_WALL | FORCE_MAXHP | CAN_SWIM | DROP_CORPSE S:1_IN_9 | S:TPORT | BLINK | TELE_AWAY D:This creature just might crush you. N:389:Tyrannosaurus G:R:g I:120:17d27:20:35:20 W:34:2:0:8100 O:0:0:0 B:CLAW:HURT:1d6 B:CLAW:HURT:1d6 B:BITE:HURT:3d6 B:BITE:HURT:3d6 F:FORCE_SLEEP | DUN_LAIR | DUN_TEMPLE | WILD_FOREST1 | WILD_FOREST2 F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_CORPSE F:ANIMAL D:A horror from prehistory, reawakened by chaos. N:390:Mummified human G:z:U I:110:17d9:20:17:60 W:34:1:0:2000 O:20:50:20 B:HIT:HURT:2d4 B:HIT:HURT:2d4 F:ONLY_ITEM | DROP_90 | DUN_RUIN | DUN_GRAVE F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a human form encased in mouldy wrappings. N:391:Vampire bat G:b:D I:120:19d4:12:20:50 W:38:2:0:4600 O:0:0:0 B:BITE:EXP_40:1d4 B:BITE:EXP_40:1d4 F:DUN_RUIN | DUN_LAIR | DUN_CAVERN | WILD_WASTE2 | WILD_MOUNT2 F:RAND_50 | COLD_BLOOD | REGENERATE | CAN_FLY | F:EVIL | ANIMAL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:An undead bat that flies at your neck hungrily. N:392:Sangahyando of Umbar G:p:o I:110:19d30:25:40:25 W:38:2:0:15000 O:0:90:10 B:HIT:HURT:4d6 B:HIT:HURT:4d6 B:HIT:HURT:4d6 B:HIT:HURT:4d6 F:DUN_CITY F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | LITE_1 F:EVIL | IM_FIRE | IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_4 | S:SLOW | FORGET D:A Black Numenorean with a blacker heart. N:393:It G:.:W I:110:20d32:25:40:25 W:39:3:0:9200 O:10:0:90 B:GAZE:BLIND:8d8 B:TOUCH:TERRIFY B:GAZE:EXP_40 B:TOUCH:EAT_ITEM F:DUN_CAVERN | WILD_SWAMP2 | WILD_WASTE2 F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD | DROP_GREAT | DROP_CORPSE F:CHAR_MIMIC | CHAR_CLEAR | ATTR_CLEAR | INVISIBLE | COLD_BLOOD F:NO_CONF | UNIQUE | FORCE_MAXHP | NO_SLEEP | CAN_SPEAK | REFLECTING | F:IM_FIRE | IM_ELEC | EMPTY_MIND | EVIL | SMART | RES_TELE | CAN_FLY S:1_IN_4 S:DRAIN_MANA | BLINK | BLIND | SCARE | CONF | S_UNDEAD | S_MONSTER | S:HEAL | TELE_AWAY | DARKNESS | S_HYDRA | TRAPS | FORGET | TELE_TO | SHRIEK D:What is It? N:394:Banshee G:G:W I:120:18d2:20:12:10 W:35:2:0:1750 O:80:0:0 B:WAIL:TERRIFY B:TOUCH:EXP_20 F:FEMALE | DUN_GRAVE F:RAND_50 | DROP_90 | F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_15 | S:TPORT | DRAIN_MANA D:It is a ghostly woman's form that wails mournfully. N:395:Carrion crawler G:c:g I:110:18d10:15:20:10 W:36:2:0:2250 O:0:0:0 B:STING:PARALYZE:2d6 B:STING:PARALYZE:2d6 F:RAND_25 | DUN_GRAVE F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON F:ANIMAL | IM_POIS D:A hideous centipede covered in slime and with glowing tentacles around its D:head. N:396:Xiclotlan G:#:D I:110:17d28:15:30:10 W:34:2:0:2250 O:0:0:0 B:CRUSH:HURT:6d4 B:CRUSH:HURT:6d4 B:BITE:HURT:3d2 F:RAND_25 | DUN_TOWER | DUN_HORROR F:EMPTY_MIND | BASH_DOOR | FORCE_MAXHP | F:IM_POIS | IM_ELEC D:"...a metallically grey tree... about sixteen feet high with D:very thick cylindrical branches... cylinders further D:divided into six flat circular extensions... and from the top D:of what I had taken for a trunk rose a featureless oval... an D:orifice gaping at the top." N:397:Silent watcher G:g:s I:110:19d64:42:40:10 W:37:5:0:7900 O:0:0:0 B:GAZE:TERRIFY B:GAZE:PARALYZE B:GAZE:LOSE_STR F:DUN_TOWER | DUN_TEMPLE | DUN_CITY F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | NONLIVING | NEVER_MOVE | F:IM_FIRE | IM_COLD | IM_POIS | EVIL | F:HURT_ROCK | HURT_LITE | F:NO_CONF | NO_SLEEP | NO_FEAR | RES_TELE S:1_IN_6 | S:SHRIEK | S_MONSTER | S_MONSTERS | HOLD | CONF | MIND_BLAST | DRAIN_MANA D:A figure carved from stone, with three vulture faces. Their eyes glow D:a malevolent light... N:398:Pukelman G:g:u I:110:20d28:12:40:10 W:40:3:0:8600 O:0:0:0 B:HIT:HURT:1d12 B:HIT:HURT:3d6 F:FORCE_SLEEP | DUN_TOWER F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | F:IM_COLD | IM_POIS | F:HURT_ROCK | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_4 | S:SLOW | CONF | BO_ACID D:A stumpy figure carved from stone, with glittering eyes. N:399:Disenchanter beast G:q:w I:110:18d39:12:30:12 W:35:2:0:1750 O:0:0:0 B:TOUCH:UN_BONUS:1d10 B:TOUCH:UN_BONUS:1d10 B:TOUCH:UN_BONUS:1d10 B:TOUCH:UN_BONUS:1d10 F:DUN_CAVERN | DUN_LAIR | WILD_FOREST1 | WILD_FOREST2 F:STUPID | WEIRD_MIND | RES_DISE | DROP_CORPSE | F:IM_ACID | IM_POIS | F:NO_CONF | S:1_IN_6 S:DRAIN_MANA D:It looks like an anteater, and there is a static feeling D:crackling around its long trunk. N:400:Dark elven druid G:h:G I:120:18d16:15:25:10 W:36:3:0:6200 O:10:0:80 B:HIT:HURT:1d7 B:HIT:HURT:1d7 B:HIT:HURT:3d8 F:MALE | DUN_LAIR | DUN_TEMPLE | WILD_FOREST2 F:FORCE_SLEEP | F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_6 | S:HEAL | CONF | DARKNESS | S:S_MONSTER | S_SPIDER | MISSILE D:A powerful dark elf, with mighty nature-controlling enchantments. N:401:Stone troll G:T:D I:110:20d9:20:20:50 W:40:1:0:6400 O:0:100:0 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:HURT:3d4 F:MALE | DUN_CAVERN | DUN_LAIR | WILD_WASTE1 | WILD_WASTE2 | WILD_MOUNT2 F:FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | HURT_LITE | HURT_ROCK D:He is a giant troll with scabrous black skin. N:402:Black G:j:d I:111:17d10:12:32:30 W:34:3:0:1350 O:0:0:0 B:ENGULF:EXP_VAMP:2d6 B:ENGULF:EXP_VAMP:2d6 F:RAND_25 | DUN_CAVERN | DUN_HORROR F:IM_COLD | IM_POIS | IM_ACID | RES_NETH | F:EVIL | EMPTY_MIND | COLD_BLOOD | FRIENDS D:The eldritch blood of Yibb-Tstll is know only as "the Black": it is D:an amorphous substance, who will suck your life and deliver it to D:Yibb-Tstll. N:403:Troll priest G:T:b I:110:20d11:20:25:30 W:40:1:0:4700 O:0:30:60 B:HIT:HURT:1d8 B:HIT:HURT:1d8 B:BITE:HURT:3d4 F:MALE | DUN_LAIR | DUN_CAVERN | WILD_FOREST2 | WILD_MOUNT2 | WILD_WASTE2 F:FORCE_SLEEP | FORCE_MAXHP | DROP_60 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_5 | S:BLINK | SCARE | CAUSE_1 | MISSILE | DARKNESS D:A troll who is so bright he knows how to read. N:404:Wereworm G:w:u I:110:19d41:15:35:20 W:37:3:0:4100 O:0:0:0 B:GAZE:EXP_20 B:CRAWL:ACID:2d4 B:BITE:HURT:1d10 B:BITE:POISON:1d6 F:BASH_DOOR | EVIL | CAN_SWIM | DUN_LAIR | DUN_CAVERN F:WILD_SWAMP1 | WILD_SWAMP2 F:ANIMAL | IM_ACID | DROP_CORPSE D:A huge wormlike shape dripping acid, twisted by evil sorcery into a foul D:monster that breeds on death. N:405:Killer crimson beetle G:K:R I:110:17d7:14:25:30 W:33:2:0:1550 O:0:0:0 B:BITE:LOSE_STR:4d4 F:RAND_25 | DUN_LAIR | DUN_CAVERN | DUN_TEMPLE | WILD_WASTE1 F:WEIRD_MIND | BASH_DOOR | DROP_CORPSE F:ANIMAL | CAN_FLY D:A giant beetle with poisonous mandibles. N:406:Vampiric ixitxachitl G:l:D I:110:20d19:20:25:20 W:40:1:0:3700 O:0:0:0 B:STING:POISON:2d8 B:STING:EXP_VAMP:2d8 F:ANIMAL | EVIL | AQUATIC | RES_NETH | IM_POIS | DUN_DARKWATER | DUN_CAVERN | WILD_SHORE S:1_IN_6 S:HEAL | SCARE | CAUSE_3 | BLIND | FORGET | HASTE D:A devil ray of the depths, with vampiric powers. N:407:Gnoph-Keh G:q:s I:110:19d12:12:25:25 W:37:2:0:6200 O:20:50:20 B:CLAW:COLD:2d4 B:CLAW:COLD:2d4 B:BUTT:HURT:2d9 F:RAND_25 | DROP_90 | DUN_LAIR F:OPEN_DOOR | BASH_DOOR | AURA_COLD | IM_COLD | F:ANIMAL | DROP_CORPSE | EVIL S:1_IN_8 S:BR_COLD | BO_ICEE | BO_COLD | BA_COLD D:A creature with a sharp horn: "the hairy myth-thing of the D:Greenland ice, that walked sometimes on two legs, sometimes D:on four, and sometimes on six." N:408:Giant grey ant G:a:s I:110:19d6:10:20:40 W:37:1:0:2200 O:0:0:0 B:BITE:HURT:2d12 F:DUN_MINE | WILD_MOUNT2 F:RAND_25 | KILL_BODY | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | F:ANIMAL D:It is an ant encased in shaggy grey fur. N:409:Kharis the Powerslave G:z:u I:110:20d36:20:20:40 W:40:4:0:14500 O:0:80:20 B:GAZE:TERRIFY B:HIT:DISEASE:6d6 B:CLAW:LOSE_CON B:CLAW:LOSE_CON F:DUN_GRAVE F:UNIQUE | MALE | CAN_SPEAK | UNDEAD | EVIL | ESCORTS | ESCORT F:FORCE_MAXHP | COLD_BLOOD | IM_POIS | IM_COLD | NO_FEAR F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | RES_TELE S:1_IN_5 | S:TRAPS | CAUSE_3 | DARKNESS | S_UNDEAD | SCARE | SLOW S:S_KIN | ANIM_DEAD D:He is out to have a revenge on you who have desecrated his tomb. N:410:Gwaihir the Windlord G:B:u I:130:13d50:20:65:20 W:26:5:0:2150 O:0:0:0 B:CLAW:HURT:15d2 B:CLAW:HURT:15d2 B:BITE:HURT:3d10 F:CAN_FLY | UNIQUE | FORCE_MAXHP | DROP_CORPSE F:WILD_MOUNT2 | WILD_WASTE2 | FRIENDLY F:ANIMAL | GOOD D:An agent of supernatural beings, this creature looks like a D:huge eagle. N:411:Giant red tick G:S:r I:110:19d5:14:27:20 W:38:1:0:3600 O:0:0:0 B:BITE:FIRE:3d6 F:RAND_25 | F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_RUIN | WILD_FOREST1 | WILD_MOUNT2 F:ANIMAL | IM_FIRE | CAN_FLY D:It is smoking and burning with great heat. N:412:Displacer beast G:f:b I:110:20d9:35:50:20 W:39:2:0:8100 O:0:0:0 B:BITE:HURT:2d8 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:HIT:HURT:1d10 F:INVISIBLE | BASH_DOOR | DROP_CORPSE | DUN_TOWER | DUN_CAVERN F:ANIMAL D:It is a huge black panther, clubbed tentacles sprouting from its shoulders. N:413:Ulwarth, Son of Ulfang G:p:U I:110:18d34:20:20:40 W:35:4:0:6900 O:40:60:0 B:HIT:HURT:4d6 B:HIT:HURT:4d6 B:HIT:HURT:4d6 F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:FORCE_MAXHP | DUN_CITY | WILD_GRASS F:ONLY_ITEM | DROP_90 | DROP_GOOD | LITE_2 F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL D:A short and swarthy Easterling. N:414:Agent of Benedict G:p:W I:110:19d6:14:27:20 W:37:3:0:1650 O:0:0:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:FRIENDS | MALE | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | OPEN_DOOR | DUN_LAIR | DUN_CITY | LITE_2 S:1_IN_8 S:BLIND | CONF | TPORT D:A faithful servitor and a skilled warrior. N:415:Cave ogre G:O:u I:110:18d11:20:17:30 W:36:1:0:3600 O:20:70:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 F:FRIENDS | DROP_60 | DROP_CORPSE | DUN_CAVERN | DUN_MINE F:OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT D:A giant orc-like figure with an awesomely muscled frame. N:416:White wraith G:W:w I:110:20d4:20:20:10 W:40:1:0:4100 O:0:0:0 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:TOUCH:EXP_20 F:FORCE_SLEEP | DUN_GRAVE F:DROP_1D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_8 | S:SCARE | CAUSE_2 | DARKNESS D:It is a tangible but ghostly form made of white fog. N:417:Angel G:A:b I:110:24d10:30:30:255 W:48:6:0:22500 O:0:40:60 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:DUN_TEMPLE | DUN_PLANAR F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR | F:ONLY_ITEM | DROP_1D2 | GOOD | CAN_FLY | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | F:IM_ACID | IM_POIS | NO_CONF | NO_SLEEP | LITE_1 S:1_IN_3 | S:BLIND | CONF | SCARE | FORGET D:A lesser angel wearing little more than a loincloth - its steely skin D:provides all the protection it needs. N:418:Ghoul G:z:U I:110:19d6:30:15:20 W:38:1:0:3200 O:0:50:40 B:CLAW:DISEASE:1d4 B:CLAW:DISEASE:1d4 B:BITE:PARALYZE:1d5 F:DUN_GRAVE | DUN_CAVERN F:DROP_60 | OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:EVIL | UNDEAD | FRIENDS | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP F:COLD_BLOOD | HURT_LITE S:1_IN_9 S:SCARE | HOLD D:Flesh is falling off in chunks from this decaying abomination. N:419:Alberich the Nibelung King G:h:D I:120:20d31:20:40:20 W:39:4:0:22500 O:90:0:10 B:HIT:UN_BONUS:3d12 B:HIT:UN_BONUS:3d12 B:TOUCH:EAT_GOLD B:TOUCH:EAT_GOLD F:DUN_MINE F:UNIQUE | MALE | CAN_SPEAK F:FORCE_MAXHP | FORCE_SLEEP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | INVISIBLE | F:EVIL | IM_FIRE | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | RES_DISE | RES_TELE S:1_IN_6 | S:HEAL | SCARE | BO_ACID | BA_ACID | TPORT | S_MONSTER D:Made invisible with his magic cap, the greedy dwarf plots for D:world domination through his riches. N:420:Hellblade G:|:v I:120:21d7:20:20:20 W:42:2:0:5300 O:0:0:0 B:HIT:EXP_20:2d13 B:HIT:EXP_20:2d13 F:DUN_TOWER | DUN_RUIN F:CHAR_MIMIC | EVIL | IM_POIS | IM_COLD | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:COLD_BLOOD | BASH_DOOR | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR | D:A deadly blade of chaos, moving of its own volition. N:421:Killer red beetle G:K:r I:110:20d4:14:25:30 W:40:1:0:4200 O:0:0:0 B:BITE:HURT:3d4 B:SPIT:FIRE:4d5 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_CAVERN F:WILD_MOUNT2 | DROP_CORPSE F:ANIMAL | IM_FIRE | CAN_FLY D:It is a giant beetle wreathed in flames. N:422:Beast of Nurgle G:q:o I:110:21d5:14:25:30 W:41:2:0:2800 O:0:0:0 B:TOUCH:POISON:3d3 B:TOUCH:DISEASE:3d3 B:BITE:ACID:4d5 F:DUN_HORROR F:WEIRD_MIND | BASH_DOOR | IM_ACID | IM_POIS | F:DEMON | EVIL | CAN_SWIM D:It walks on four legs, but mostly resembles a slug-shaped jelly. D:It even has two disgusting antennae in its head. Yecch! N:423:Creeping adamantite coins G:$:G I:120:20d18:5:25:10 W:40:4:0:7700 O:100:0:0 B:BITE:POISON:3d4 B:TOUCH:POISON:3d5 B:HIT:HURT:1d12 B:HIT:HURT:1d12 F:CHAR_MIMIC | DUN_MINE F:ONLY_GOLD | DROP_90 | DROP_2D2 | F:COLD_BLOOD | BASH_DOOR | F:ANIMAL | F:IM_POIS | NO_CONF | NO_SLEEP D:It is a pile of coins, slithering forward on thousands of tiny legs. N:424:Algroth G:T:s I:110:21d9:20:30:40 W:42:1:0:4000 O:10:80:0 B:CLAW:POISON:4d3 B:CLAW:POISON:4d3 F:FRIENDS | DROP_60 | WILD_MOUNT2 | DUN_CAVERN | DUN_LAIR F:WILD_SWAMP1 | WILD_SWAMP2 F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | TROLL D:A powerful troll form. Venom drips from its needlelike claws. N:425:Flamer of Tzeentch G:,:R I:110:21d22:10:35:20 W:41:2:0:9200 O:0:0:0 B:ENGULF:FIRE:3d6 B:ENGULF:FIRE:3d6 F:FORCE_SLEEP | BASH_DOOR | DUN_TOWER | DUN_HORROR F:IM_FIRE | IM_POIS | AURA_FIRE | DEMON | EVIL | RES_PLAS | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_3 | S:BO_FIRE | BA_FIRE D:It looks like an inverted mushroom, with two flexible arms. N:426:Roper G:#:D I:115:20d32:30:30:255 W:39:5:0:6600 O:80:0:20 B:CRUSH:PARALYZE:3d5 B:CRUSH:PARALYZE:3d5 B:CRUSH:PARALYZE:3d5 B:CRUSH:PARALYZE:3d5 F:DUN_TOWER | DUN_CAVERN | DUN_HORROR F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR | NEVER_MOVE | F:ONLY_GOLD | DROP_2D2 | EVIL F:IM_POIS | NO_CONF | NO_SLEEP | IM_FIRE | CHAR_MIMIC S:1_IN_5 | S:BA_FIRE | BA_ELEC | BA_POIS | HASTE | S:TRAPS | SHRIEK | HOLD | CONF D:This creature look like a pillar of rock. However, a closer D:inspection reveals a glaring eye and powerful tentacles, D:which crush its prey and feed it to the creature's hungry D:mouth. N:427:Headless G:H:u I:110:19d17:20:25:40 W:38:1:0:2450 O:0:100:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:FRIENDS | DROP_60 | OPEN_DOOR | BASH_DOOR | DUN_HELL | F:WILD_MOUNT1 | WILD_WASTE1 | WILD_WASTE2 | DROP_SKELETON | DROP_CORPSE F:EVIL S:1_IN_6 S:SCARE D:Headless humanoid abominations created by a magical mutation. N:428:Vibration hound G:Z:U I:110:20d9:30:15:0 W:40:3:0:5900 O:0:0:0 B:BITE:HURT:2d6 B:BITE:HURT:2d6 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 F:FORCE_SLEEP | DUN_PLANAR | DUN_CAVERN F:FRIENDS | F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP | DROP_SKELETON | DROP_CORPSE S:1_IN_5 | S:BR_SOUN D:A blurry canine form which seems to be moving as fast as the eye can D:follow. You can feel the earth resonating beneath your feet. N:429:Nexus hound G:Z:v I:110:21d9:30:15:0 W:41:3:0:5300 O:0:0:0 B:BITE:HURT:2d8 B:BITE:HURT:2d8 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 F:FORCE_SLEEP | RES_NEXU | DROP_SKELETON | DROP_CORPSE F:FRIENDS | DUN_PLANAR | DUN_CAVERN F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_5 | S:BR_NEXU D:A locus of conflicting points coalesce to form the vague shape of a huge D:hound. Or is it just your imagination? N:430:Ogre mage G:O:o I:110:24d11:20:20:30 W:48:2:0:13000 O:0:0:100 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:HURT:3d8 F:FORCE_SLEEP | F:DROP_90 | DUN_CAVERN | WILD_MOUNT1 | WILD_MOUNT2 F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | GIANT S:1_IN_4 | S:HEAL | HOLD | TRAPS | BA_COLD | S:S_MONSTER D:A hideous ogre wrapped in black sorcerous robes. N:431:Grendel G:O:g I:120:21d43:20:50:20 W:41:2:0:39000 O:0:100:0 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:HURT:6d6 F:UNIQUE | MALE | CAN_SPEAK | DUN_CAVERN | WILD_SWAMP1 | WILD_SHORE | F:FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_SWIM | LITE_1 F:EVIL | GIANT | IM_POIS D:An ogre renowned for acts of surpassing cruelty. N:432:Vampire G:V:W I:110:21d11:20:22:10 W:41:1:0:3300 O:20:50:20 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:EXP_VAMP:1d8 F:FORCE_SLEEP | DUN_RUIN | DUN_GRAVE F:COLD_BLOOD | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_9 | S:TELE_TO | HOLD | SCARE | CAUSE_2 | MIND_BLAST | FORGET | DARKNESS D:It is a humanoid with an aura of power. You notice a sharp set of front D:teeth. N:433:Gorgimera G:H:D I:110:20d18:12:27:10 W:40:2:0:4500 O:0:0:0 B:BITE:FIRE:1d3 B:BITE:FIRE:1d3 B:BITE:HURT:1d10 B:GAZE:PARALYZE:2d4 F:FORCE_SLEEP | DUN_TOWER | DUN_HORROR F:BASH_DOOR | CAN_FLY | DROP_CORPSE F:IM_FIRE S:1_IN_8 | S:BR_FIRE D:It has 3 heads - gorgon, goat and dragon - all attached to a D:lion's body. N:434:Shantak G:B:D I:120:21d17:12:27:10 W:42:20:0:6900 O:0:0:0 B:BITE:HURT:1d6 B:BITE:HURT:1d6 B:BITE:HURT:1d6 F:DUN_LAIR F:ANIMAL | IM_ACID | EVIL | CAN_FLY | DROP_CORPSE S:1_IN_6 S:SCARE | HASTE | ELDRITCH_HORROR D:A scaly bird larger than an elephants, with a horse-like head. N:435:Colbran G:g:b I:120:24d28:12:40:10 W:47:2:0:15000 O:0:0:0 B:HIT:ELEC:3d8 B:HIT:ELEC:3d8 F:FORCE_SLEEP | DUN_TOWER F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | CAN_FLY | F:IM_ELEC | IM_POIS | AURA_ELEC | REFLECTING | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | LITE_1 S:1_IN_3 | S:BO_ELEC D:A man-shaped form of living lightning, sparks and shocks crackle all over D:this madly capering figure, as it leaps and whirls around and about you. N:436:Spirit naga G:n:B I:110:19d17:20:37:120 W:37:2:0:5300 O:20:0:80 B:CRUSH:HURT:2d8 B:CRUSH:HURT:2d8 B:BITE:HURT:1d8 B:BITE:HURT:1d8 F:FEMALE | F:FORCE_SLEEP | CAN_FLY | DUN_TOWER | DUN_LAIR | DUN_RUIN F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_CORPSE F:INVISIBLE | OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP S:1_IN_4 | S:HEAL | BLIND | MIND_BLAST | DARKNESS D:A wraithly snake-like form with the torso of a beautiful woman, it is the D:most powerful of its kind. N:437:Corpser G:,:g I:112:19d19:20:37:120 W:38:2:0:5600 O:0:0:0 B:CRUSH:HURT:2d8 B:CRUSH:HURT:2d8 B:CRUSH:HURT:2d8 B:CRUSH:HURT:2d8 F:DUN_GRAVE | DUN_RUIN | WILD_GRASS | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP D:A creature who lives underground and eats surface D:dwellers whom it catches D:with its poisonous tentacle, which is the only part of the creature that D:is ever seen. Perhaps it is better not to see the part which remains D:underground... N:438:Fiend of Slaanesh G:S:R I:120:21d17:12:25:40 W:41:4:0:4100 O:0:0:0 B:STING:POISON:8d1 B:STING:LOSE_STR:8d1 B:WAIL:TERRIFY:1d4 F:BASH_DOOR | DUN_LAIR | DUN_CAVERN F:EVIL | DEMON | D:Slaanesh's pet, a large scorpion-like creature with a human face and D:reptile skin. N:439:Stairway to hell G:>:W I:120:20d5:90:20:20 W:39:5:0:3700 O:0:0:0 B:WAIL:UN_BONUS B:WAIL:EXP_20 B:WAIL:EAT_GOLD B:WAIL:EAT_ITEM F:DUN_GRAVE F:CHAR_MIMIC | COLD_BLOOD | EVIL | NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | F:UNDEAD | FORCE_MAXHP | IM_FIRE | IM_POIS | EMPTY_MIND F:NEVER_MOVE S:1_IN_15 S:S_DEMON | SHRIEK D:Often found in graveyards. N:440:5-headed hydra G:M:g I:120:20d28:20:40:20 W:40:2:0:20000 O:0:0:0 B:BITE:POISON:4d4 B:BITE:POISON:4d4 B:BITE:POISON:4d4 B:BITE:POISON:4d4 F:FORCE_SLEEP F:ONLY_GOLD | DROP_1D2 | DROP_2D2 | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | MOVE_BODY | CAN_SWIM | F:ANIMAL | IM_POIS | DUN_DARKWATER | DUN_LAIR | WILD_SHORE | WILD_SWAMP2 | S:1_IN_5 | S:SCARE | BA_POIS D:A strange reptilian hybrid with five heads dripping venom. N:441:Barney the Dinosaur G:R:v I:120:23d33:10:45:20 W:46:2:0:29000 O:10:0:90 B:SHOW:LOSE_INT B:SHOW:LOSE_WIS B:CHARGE:EAT_GOLD B:CHARGE:EAT_GOLD F:DUN_HORROR | DUN_TEMPLE F:DROP_1D2 | DROP_GOOD | DROP_GREAT | ONLY_ITEM | SILLY | F:EVIL | MALE | CAN_SPEAK | SMART | RES_TELE | CAN_SWIM | DROP_CORPSE F:ANIMAL | IM_POIS | NO_CONF | NO_SLEEP | FORCE_MAXHP | UNIQUE | FORCE_SLEEP S:1_IN_3 S:SHRIEK | CONF | S_HYDRA | SLOW | BLIND | DRAIN_MANA S:BA_POIS | BR_CHAO | FORGET | DARKNESS | BR_NUKE D:The lovable purple reptile is making a guest appearance here. Flee while D:you still can! N:442:Black knight G:p:D I:120:21d11:20:35:10 W:41:1:0:7300 O:0:90:10 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:MALE | DUN_TEMPLE | DUN_CITY | DUN_CAVERN | WILD_GRASS F:FORCE_SLEEP | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL S:1_IN_8 | S:BLIND | SCARE | CAUSE_3 | DARKNESS D:He is a figure encased in deep black plate armour; he looks at you D:menacingly. N:443:Seahorse G:l:o I:120:24d17:20:30:20 W:48:2:0:15000 O:0:0:0 B:BITE:PARALYZE:4d5 B:BITE:LOSE_DEX:4d5 B:BITE:LOSE_CON:4d5 F:FORCE_SLEEP | AQUATIC | GOOD | ANIMAL | F:IM_COLD | IM_POIS | DUN_CAVERN | WILD_OCEAN S:1_IN_5 | S:BO_WATE | BO_COLD | BO_ICEE | BO_MANA D:Your mind is filled with admiration as you view this wondrous, D:magical seahorse. N:444:Cyclops G:P:u I:120:28d22:20:45:20 W:44:2:0:13500 O:0:100:0 B:HIT:HURT:9d8 B:HIT:HURT:9d8 F:FORCE_SLEEP | F:DROP_90 | TAKE_ITEM | DUN_CAVERN | DUN_LAIR | WILD_SHORE | WILD_MOUNT1 | F:BASH_DOOR | OPEN_DOOR | MOVE_BODY | DROP_CORPSE F:EVIL | IM_POIS | IM_ACID | GIANT S:1_IN_8 | S:ARROW D:A gigantic humanoid with but one eye. N:445:Clairvoyant G:p:B I:120:20d9:100:25:10 W:40:3:0:12300 O:0:0:90 B:HIT:CONFUSE:5d5 B:HIT:TERRIFY:5d5 F:MALE | DUN_TEMPLE F:FORCE_SLEEP | DROP_90 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | SMART | LITE_2 F:EVIL S:1_IN_8 | S:BLIND | MIND_BLAST | HOLD | CAUSE_3 | FORGET | S_MONSTER D:He is using his supernatural talents to bring about your D:destruction. N:446:Giant purple worm G:w:v I:110:21d16:14:30:30 W:42:4:0:7900 O:0:0:0 B:HIT:HURT:1d8 B:BITE:ACID:2d8 B:STING:POISON:1d8 F:BASH_DOOR | DROP_CORPSE | DUN_LAIR | DUN_CAVERN F:ANIMAL | IM_ACID | IM_POIS | CAN_SWIM D:It is a massive worm form, many feet in length. Its vast maw drips acid D:and poison. N:447:Catoblepas G:q:g I:110:22d10:15:26:40 W:43:2:0:11200 O:100:0:0 B:GAZE:TERRIFY:2d4 B:GAZE:BLIND:2d4 B:BUTT:HURT:2d6 B:BITE:HURT:2d12 F:ONLY_GOLD | DROP_2D2 | DROP_CORPSE | EVIL | F:BASH_DOOR | CAN_SWIM | DUN_TOWER | DUN_LAIR | WILD_SWAMP1 | WILD_SWAMP2 F:ANIMAL | IM_POIS D:A strange ox-like form with a huge head but a thin, weak neck, it looks D:like the creation of some deranged alchemist. N:448:Lesser wall monster G:#:W I:110:22d4:20:37:40 W:43:4:0:5100 O:0:0:0 B:HIT:HURT:2d6 B:HIT:HURT:2d6 B:HIT:HURT:2d6 F:DUN_MINE | DUN_CAVERN F:FORCE_SLEEP | COLD_BLOOD | EMPTY_MIND | KILL_WALL | RAND_50 | MULTIPLY | F:BASH_DOOR | IM_COLD | IM_FIRE | IM_POIS | NONLIVING | F:HURT_ROCK | NO_CONF | NO_SLEEP | CHAR_MIMIC D:A sentient, moving section of wall. N:449:Mage G:p:r I:110:22d4:20:20:10 W:43:1:0:5900 O:10:0:90 B:HIT:HURT:2d5 B:HIT:HURT:2d5 F:MALE | DUN_TOWER | WILD_GRASS F:FORCE_SLEEP | F:ONLY_ITEM | DROP_90 | DROP_SKELETON | DROP_CORPSE F:SMART | OPEN_DOOR | BASH_DOOR | LITE_2 F:EVIL S:1_IN_3 | S:HASTE | TPORT | TELE_TO | BLIND | CONF | S:BO_FIRE | BO_COLD | BO_ELEC | S:S_MONSTER D:A fat mage with glasses and a pointy hat. N:450:Mind flayer G:h:v I:110:20d6:20:25:10 W:39:1:0:8000 O:0:10:90 B:GAZE:LOSE_INT:2d6 B:GAZE:LOSE_INT:2d6 F:DUN_RUIN | WILD_WASTE1 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP S:1_IN_8 | S:BLIND | HOLD | SCARE | MIND_BLAST | BRAIN_SMASH | FORGET D:A humanoid form with a gruesome head, tentacular mouth, and piercing D:eyes. Claws reach out for you and you feel a presence invade your mind. N:451:The Ultimate Dungeon Cleaner G:g:D I:120:22d29:10:40:12 W:44:2:0:10100 O:0:0:0 B:CRUSH:HURT:2d10 B:CRUSH:HURT:2d10 B:CRUSH:HURT:2d10 B:CRUSH:HURT:2d10 F:DUN_TOWER | DUN_CITY F:FORCE_SLEEP | FORCE_MAXHP | KILL_BODY | KILL_ITEM | UNIQUE | REFLECTING | F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_FIRE | IM_COLD | IM_POIS F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | RES_TELE D:It looks like a huge spiked roller. It was designed to keep this dungeon D:clean, and you are the biggest spot of dirt in sight. N:452:Deep one G:u:g I:120:23d14:20:25:20 W:46:20:0:10600 O:20:80:0 B:CLAW:POISON:2d4 B:CLAW:POISON:2d4 B:BITE:HURT:4d4 F:FRIENDS | DUN_CAVERN | DUN_HELL | WILD_SHORE | DROP_CORPSE F:RAND_25 | DROP_90 | F:CAN_SWIM | BASH_DOOR | RES_TELE | F:EVIL | DEMON | IM_FIRE | IM_POIS | RES_WATE S:1_IN_10 S:ELDRITCH_HORROR D:"I thought their predominant color was a greyish-green, D:though they had white bellies. They were mostly shiny and D:slippery, but the ridges of their backs were scaly. Their D:forms vaguely suggested the anthropoid, while their heads were D:the heads of fish, with prodigious bulging eyes that never D:closed. At the sides of their necks were palpitating gills and D:their long paws were webbed." N:453:Basilisk G:R:D I:120:23d19:15:45:30 W:46:3:0:17000 O:50:0:30 B:GAZE:PARALYZE B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 F:ONLY_ITEM | DROP_90 | DUN_LAIR | DUN_TEMPLE | WILD_MOUNT1 | WILD_MOUNT2 F:OPEN_DOOR | BASH_DOOR | EVIL | IM_POIS | DROP_CORPSE F:ANIMAL | NO_CONF | NO_SLEEP | CAN_SWIM S:1_IN_8 S:BR_POIS D:An evil reptile whose eyes stare deeply at you and D:makes your soul wilt! N:454:Ice troll G:T:w I:110:22d9:20:25:50 W:44:1:0:6500 O:0:100:0 B:HIT:HURT:1d5 B:HIT:HURT:1d5 B:HIT:HURT:1d5 B:BITE:COLD:3d6 F:MALE | F:FRIENDS | DROP_60 | DUN_LAIR | WILD_WASTE1 | WILD_WASTE2 F:DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | IM_COLD | HURT_LITE D:He is a white troll with powerfully clawed hands. N:455:Dhole G:w:s I:110:23d26:14:64:25 W:46:20:0:23500 O:0:0:0 B:SPIT:ACID:1d8 B:ENGULF:ACID:2d8 B:CRUSH:HURT:4d8 F:DUN_DARKWATER | DUN_CAVERN F:ANIMAL | EVIL | KILL_WALL | IM_ACID F:CAN_SWIM | FORCE_MAXHP | DROP_CORPSE S:1_IN_6 S:BR_ACID | ELDRITCH_HORROR | D:A gigantic worm dripping with acid. "...even as he looked, one D:reared up several hundred feet and leveled a bleached, viscous end D:at him." N:456:Archangel G:A:B I:110:25d12:30:34:255 W:49:6:0:26000 O:0:80:20 B:HIT:HURT:3d5 B:HIT:HURT:3d5 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:DUN_PLANAR F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR | GOOD | F:ONLY_ITEM | DROP_1D2 | CAN_FLY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | RES_TELE | LITE_1 S:1_IN_3 | S:HEAL | HASTE | BLIND | CONF | SCARE D:A lesser angel protected by an aura of holiness. Its muscular form looks D:extremely powerful next to your own frail body. N:457:Ring mimic G:=:w I:120:21d12:30:30:100 W:42:4:0:9800 O:100:0:0 B:HIT:POISON:3d4 B:HIT:POISON:3d4 B:HIT:POISON:3d4 F:CHAR_MIMIC | DUN_TOWER | DUN_CAVERN | DUN_CITY F:FORCE_SLEEP | NEVER_MOVE | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | S:1_IN_4 | S:BLIND | CONF | SCARE | CAUSE_2 | FORGET | S:BO_ACID | BO_FIRE | BO_COLD | BO_ELEC | S:S_MONSTER D:A strange creature that disguises itself as discarded objects to lure D:unsuspecting adventurers within reach of its venomous claws. N:458:Chaos tile G:.:v I:120:6d2:30:30:100 W:42:6:0:3800 O:0:0:0 B:HIT:CONFUSE:8d4 F:DUN_TEMPLE F:CHAR_MIMIC | ATTR_MULTI | ATTR_ANY | F:FORCE_SLEEP | NEVER_MOVE | MULTIPLY | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_4 | S:BLIND | CONF | SCARE | CAUSE_2 | BLINK S:S_MONSTER D:It is a floor tile corrupted by chaos. N:459:Young blue dragon G:d:b I:110:21d8:20:30:70 W:41:1:0:7500 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:HURT:1d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN F:DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | DRAGON | IM_ELEC | LITE_1 S:1_IN_11 | S:SCARE | S:BR_ELEC D:It has a form that legends are made of. Its still-tender scales are a D:deep blue in hue. Sparks crackle along its length. N:460:Young white dragon G:d:w I:110:21d8:20:30:70 W:41:1:0:7500 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:HURT:1d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN F:DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | DRAGON | IM_COLD S:1_IN_11 | S:SCARE | S:BR_COLD D:It has a form that legends are made of. Its still-tender scales are a D:frosty white in hue. Icy blasts of cold air come from it as it breathes. N:461:Young green dragon G:d:g I:110:21d7:20:30:70 W:42:1:0:8600 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:HURT:1d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN F:DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | DRAGON | IM_POIS S:1_IN_11 | S:SCARE | S:BR_POIS D:It has a form that legends are made of. Its still-tender scales are a D:deep green in hue. Foul gas seeps through its scales. N:462:Young bronze dragon G:d:U I:110:22d8:20:30:150 W:44:3:0:10500 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:HURT:1d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN F:DROP_2D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_FLY | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_11 | S:SCARE | S:BR_CONF D:It has a form that legends are made of. Its still-tender scales are a D:rich bronze hue, and its shape masks its true form. N:463:Aklash G:T:W I:110:22d10:14:32:25 W:44:4:0:6600 O:0:90:0 B:CLAW:HURT:1d6 B:CLAW:HURT:1d6 B:BITE:POISON:1d20 B:CRUSH:HURT:2d9 F:DUN_LAIR | DUN_CAVERN | WILD_SWAMP2 F:TROLL | EVIL | FRIENDS | OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:IM_POIS | IM_ACID | REGENERATE | S:1_IN_7 S:BR_POIS D:Pale, bald, fat, hairless troll creatures. Ugly beyond description. D:Not to mention how bad their breath smells... N:464:Mithril golem G:g:B I:110:24d35:12:45:10 W:47:4:0:10200 O:100:0:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:ONLY_GOLD | DROP_1D2 | DUN_TOWER F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | REFLECTING | F:IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING D:It is a massive statue of purest mithril. It looks expensive! N:465:Skeleton troll G:s:w I:110:24d6:20:25:20 W:47:1:0:8800 O:0:0:0 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:HURT:3d4 F:DUN_GRAVE F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | UNDEAD | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a troll skeleton animated by dark dweomers. N:466:Skeletal tyrannosaurus G:R:w I:120:22d16:20:25:20 W:44:2:0:11800 O:0:0:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:LOSE_CON:4d6 B:GAZE:TERRIFY F:DUN_GRAVE | DUN_CAVERN | DUN_TEMPLE F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | ANIMAL | UNDEAD | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is the skeleton of a tyrannosaurus, animated by dark dweomers. N:467:Jaws G:l:w I:120:21d31:10:40:12 W:42:2:0:11400 O:20:0:60 B:BITE:HURT:11d2 B:BITE:HURT:22d1 B:BITE:HURT:11d2 B:BITE:HURT:22d1 F:FORCE_MAXHP | TAKE_ITEM | UNIQUE | MOVE_BODY | F:WILD_OCEAN | DUN_CAVERN | F:BASH_DOOR | IM_POIS | ANIMAL | AQUATIC | F:NO_CONF | NO_SLEEP | DROP_90 D:The biggest white shark who has ever lived, it is hunting for you now! N:468:Thorondor G:B:u I:130:8d96:20:32:20 W:16:6:0:1090 O:0:0:0 B:CLAW:HURT:16d2 B:CLAW:HURT:16d2 B:BITE:HURT:4d10 F:CAN_FLY | UNIQUE | FORCE_MAXHP | DROP_CORPSE F:WILD_MOUNT2 | WILD_WASTE2 | FRIENDLY F:ANIMAL | GOOD D:An agent of supernatural being, Thorondor is the lord of eagles. N:469:Giant blue ant G:a:b I:110:21d3:10:25:60 W:42:2:0:3500 O:0:0:0 B:BITE:ELEC:5d5 F:RAND_25 | DUN_CAVERN | DUN_MINE | WILD_FOREST2 | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | F:ANIMAL | IM_ELEC D:It is a giant ant that crackles with energy. N:470:Grave wight G:W:b I:110:21d4:20:20:30 W:41:1:0:6100 O:0:50:50 B:HIT:HURT:1d7 B:HIT:HURT:1d7 B:TOUCH:EXP_20 F:DUN_GRAVE F:FORCE_SLEEP | RAND_25 | F:ONLY_ITEM | DROP_90 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | CAN_FLY | F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_8 | S:SCARE | CAUSE_3 | DARKNESS D:It is a ghostly form with eyes that haunt you. N:471:Shadow drake G:d:D I:110:24d11:25:30:30 W:47:2:0:16000 O:50:50:0 B:BITE:COLD:1d6 B:BITE:COLD:1d6 B:BITE:COLD:1d6 F:FORCE_SLEEP | RAND_25 | DUN_CAVERN | DUN_LAIR F:ONLY_ITEM | DROP_1D2 | CAN_FLY | DROP_CORPSE F:TAKE_ITEM | INVISIBLE | OPEN_DOOR | BASH_DOOR | F:ANIMAL | EVIL | DRAGON | IM_COLD S:1_IN_6 | S:HASTE | SLOW | CONF | SCARE | DARKNESS D:It is a dragon-like form wrapped in shadow. Glowing red eyes shine out in D:the dark. N:472:Manticore G:H:o I:120:22d8:12:15:10 W:43:2:0:7700 O:0:0:0 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:FORCE_SLEEP | FORCE_MAXHP | F:BASH_DOOR | CAN_FLY | DUN_LAIR | WILD_FOREST2 | DROP_CORPSE F:EVIL S:1_IN_5 | S:ARROW D:It is a winged lion's body with a human torso and a tail covered in D:vicious spikes. N:473:Giant army ant G:a:o I:120:21d4:10:20:40 W:41:3:0:3600 O:0:0:0 B:BITE:HURT:2d12 F:RAND_25 | KILL_BODY | FRIENDS | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | DUN_MINE | DUN_CAVERN F:ANIMAL D:An armoured form moving with purpose. Powerful on its own, flee when D:hordes of them march. N:474:Killer slicer beetle G:K:o I:110:22d7:14:25:30 W:44:2:0:5600 O:0:0:0 B:BITE:HURT:5d8 B:BITE:HURT:5d8 F:WEIRD_MIND | BASH_DOOR | DUN_TEMPLE | DUN_CAVERN | DUN_LAIR F:WILD_GRASS | DROP_CORPSE F:ANIMAL | CAN_FLY D:It is a beetle with deadly sharp cutting mandibles and a rock-hard D:carapace. N:475:Gorgon G:H:b I:110:23d19:12:44:20 W:46:2:0:13000 O:0:0:0 B:BUTT:HURT:3d9 B:BUTT:HURT:3d9 B:BITE:POISON:1d10 B:KICK:HURT:2d4 F:FORCE_SLEEP | ANIMAL | MOVE_BODY | DUN_LAIR | WILD_FOREST2 | F:BASH_DOOR | IM_POIS | IM_ACID | DROP_CORPSE | EVIL S:1_IN_8 | S:BR_POIS D:A bull whose skin is made of steel plates. Watch out for its D:deadly breath! N:476:Gug G:P:D I:110:23d12:14:30:30 W:46:2:0:15000 O:0:50:50 B:BITE:HURT:10d4 B:CLAW:HURT:2d7 B:CLAW:HURT:2d7 B:CLAW:HURT:2d7 F:FORCE_SLEEP | F:DROP_90 | TAKE_ITEM | DUN_LAIR | DUN_CAVERN F:BASH_DOOR | OPEN_DOOR | DROP_CORPSE F:EVIL | IM_POIS | IM_ACID | GIANT D:A hideous, beastly, four-armed unclean giant: "...large as a D:barrel... The eyes jutted two inches from each side, shaded by D:bony protuberances overgrown of the mouth. That mouth had great D:yellow fangs and ran from the top to the tally." N:477:Ghost G:G:w I:120:24d3:20:20:10 W:47:1:0:11200 O:30:50:0 B:WAIL:TERRIFY B:TOUCH:EXP_20 B:CLAW:LOSE_INT:1d6 B:CLAW:LOSE_WIS:1d6 F:FORCE_SLEEP | RAND_25 | DROP_1D2 | DUN_GRAVE F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | CAN_FLY | F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_15 | S:BLIND | HOLD | DRAIN_MANA D:You don't believe in them, and they don't believe in you. N:478:Death watch beetle G:K:B I:110:23d10:16:30:30 W:46:3:0:6600 O:0:0:0 B:BITE:HURT:5d4 B:WAIL:TERRIFY:5d6 F:WEIRD_MIND | BASH_DOOR | WILD_FOREST2 | DROP_CORPSE | DUN_GRAVE | DUN_CAVERN F:ANIMAL | CAN_FLY D:It is a giant beetle that produces a chilling sound. N:479:Ogre shaman G:O:b I:110:21d5:20:26:30 W:42:2:0:9800 O:0:0:80 B:HIT:HURT:3d6 B:HIT:HURT:3d6 B:HIT:HURT:3d6 F:FORCE_SLEEP | DUN_CAVERN | WILD_WASTE1 F:ONLY_ITEM | DROP_60 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | LITE_1 F:EVIL | GIANT S:1_IN_5 | S:TPORT | HOLD | SCARE | CAUSE_2 | TRAPS | BO_FIRE | S:S_MONSTER D:It is an ogre wrapped in furs and covered in grotesque body paints. N:480:Nexus quylthulg G:Q:v I:110:22d4:10:1:1 W:44:1:0:10600 O:0:0:0 F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW | DUN_TEMPLE | DUN_CAVERN F:INVISIBLE | EMPTY_MIND | RES_NEXU | F:NO_CONF | NO_SLEEP | NO_FEAR | RES_TELE S:1_IN_1 | S:BLINK | TELE_AWAY D:It is a very unstable, strange pulsing mound of flesh. N:481:Shelob, Spider of Darkness G:S:D I:110:26d33:8:40:80 W:51:3:0:65000 O:20:30:50 B:STING:POISON:4d5 B:BITE:LOSE_STR:1d4 B:STING:POISON:4d5 F:UNIQUE | FEMALE | DUN_CAVERN | DUN_MINE | WILD_FOREST2 F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | BASH_DOOR | F:ANIMAL | EVIL | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_2 | S:HEAL | BLIND | SLOW | CONF | SCARE | CAUSE_3 | CAUSE_4 | S:TRAPS | BR_DARK S:S_SPIDER D:Shelob is an enormous bloated spider, rumoured to have been one of the D:brood of Ungoliant the Unlight. Her poison is legendary, as is her ego, D:which may be her downfall. She used to guard the pass through Cirith D:Ungol, but has not been seen there for many eons. N:482:Giant squid G:l:g I:110:24d24:8:40:80 W:48:3:0:14000 O:0:0:0 B:CRUSH:HURT:8d4 B:CRUSH:HURT:8d4 B:CRUSH:HURT:8d4 F:IM_ACID | RES_WATE | AQUATIC | ANIMAL | IM_COLD | MOVE_BODY F:FORCE_MAXHP | DUN_CAVERN | WILD_OCEAN | S:1_IN_8 S:BR_ELEC | BR_ACID | BR_POIS D:Besides being capable of dragging whole ships underwater, D:this creature can also harm you with ranged attacks. N:483:Ghoulking G:z:D I:120:27d5:20:30:10 W:53:2:0:20000 O:20:50:30 B:CLAW:LOSE_STR:3d4 B:CLAW:DISEASE:3d4 B:CLAW:DISEASE:3d4 B:BITE:PARALYZE:3d5 F:DUN_GRAVE F:DROP_90 | OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:FORCE_MAXHP | ESCORT | FORCE_SLEEP | F:EVIL | UNDEAD | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP | F:COLD_BLOOD | HURT_LITE S:1_IN_7 S:SCARE | HOLD | DARKNESS | S_UNDEAD | ANIM_DEAD D:Flesh is falling off in chunks from this decaying abomination. N:484:Doombat G:b:b I:120:25d18:16:37:30 W:49:2:0:11400 O:0:0:0 B:BITE:FIRE:5d4 B:BITE:FIRE:5d4 B:BITE:FIRE:5d4 F:DUN_CAVERN | DUN_TEMPLE | WILD_WASTE2 | WILD_MOUNT2 F:WEIRD_MIND | BASH_DOOR | AURA_FIRE | CAN_FLY | DROP_CORPSE | F:IM_FIRE | EVIL | F:ANIMAL D:It is a fast moving creature of chaos, a gigantic black bat D:surrounded by flickering bright red flames. N:485:Ninja G:p:D I:120:24d5:20:25:10 W:47:2:0:7600 O:0:100:0 B:HIT:POISON:3d4 B:HIT:LOSE_STR:3d4 B:HIT:LOSE_STR:3d4 F:MALE | DUN_CITY | DUN_TEMPLE | WILD_GRASS | WILD_FOREST1 F:DROP_90 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE | F:EVIL | NO_CONF | NO_SLEEP D:A humanoid clothed in black who moves with blinding speed. N:486:Memory moss G:,:b I:110:1d2:30:1:5 W:44:3:0:5800 O:0:0:0 B:HIT:CONFUSE:1d4 B:HIT:CONFUSE:1d4 F:FORCE_SLEEP | NEVER_MOVE | DUN_LAIR | DUN_CAVERN | DUN_DARKWATER | WILD_FOREST2 F:STUPID | EMPTY_MIND | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:FORGET D:A mass of green vegetation. You don't remember seeing anything like it D:before. N:487:Storm giant G:P:b I:110:21d37:20:30:40 W:42:1:0:21000 O:10:90:0 B:HIT:ELEC:3d8 B:HIT:ELEC:3d8 B:HIT:ELEC:3d8 F:FORCE_SLEEP | FORCE_MAXHP | DROP_90 | DUN_LAIR | WILD_MOUNT2 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | AURA_ELEC | DROP_SKELETON | DROP_CORPSE F:EVIL | GIANT | IM_COLD | IM_ELEC | MALE | LITE_1 S:1_IN_8 | S:BLINK | TELE_TO | CONF | SCARE | BO_ELEC | BA_ELEC D:It is a twenty-five foot tall giant wreathed in lighting. N:488:Spectator G:e:B I:110:24d6:30:1:5 W:48:3:0:5500 O:0:0:0 B:GAZE:PARALYZE:1d4 B:GAZE:CONFUSE:1d4 B:GAZE:UN_BONUS F:FORCE_SLEEP | DUN_TOWER | DUN_CAVERN F:STUPID | EMPTY_MIND | CAN_FLY | DROP_CORPSE | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:FORGET | CAUSE_2 | HOLD | SLOW D:It has two small eyestalks and a large central eye. N:489:Bokrug G:R:g I:110:23d33:20:35:50 W:45:30:0:34000 O:20:0:80 B:BITE:HURT:2d5 B:CRUSH:HURT:2d10 B:CRUSH:HURT:2d10 F:UNIQUE | CAN_SPEAK | DUN_TEMPLE | DUN_LAIR F:FORCE_MAXHP | NONLIVING | ESCORT | ESCORTS | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:EVIL | IM_COLD | IM_POIS | DEMON S:1_IN_5 S:S_UNDEAD | MIND_BLAST | CAUSE_3 | SCARE | BO_WATE | S_KIN | ELDRITCH_HORROR D:A lizard-like Great Old One worshipped by the men of Sarnath. N:490:Biclops G:P:u I:120:25d22:20:45:20 W:50:5:0:22500 O:0:100:0 B:HIT:HURT:11d8 B:HIT:HURT:11d8 F:FORCE_SLEEP | DUN_CAVERN | DUN_LAIR | WILD_MOUNT1 F:DROP_90 | TAKE_ITEM | DROP_CORPSE | F:BASH_DOOR | OPEN_DOOR | MOVE_BODY | F:EVIL | IM_POIS | IM_ACID | GIANT S:1_IN_8 | S:ARROW D:Oh, no! Aaargh! It is the most unnatural, most disgusting D:creature imaginable: a two-eyed cyclops! This perversion D:of nature must be exterminated! N:491:Half-troll G:T:o I:110:23d11:20:30:50 W:46:2:0:7300 O:20:80:0 B:CLAW:HURT:1d5 B:CLAW:HURT:1d5 B:CLAW:HURT:1d5 B:BITE:HURT:2d6 F:MALE | DUN_CITY | DUN_CAVERN | WILD_WASTE1 F:FRIENDS | F:ONLY_ITEM | DROP_60 | DROP_SKELETON | DROP_CORPSE | F:OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | IM_POIS D:A huge, ugly, half-human in search of plunder. N:492:Ivory monk G:p:w I:120:23d13:25:25:7 W:45:1:0:9400 O:40:0:50 B:KICK:HURT:8d1 B:HIT:HURT:8d1 B:KICK:HURT:8d4 B:HIT:HURT:8d1 F:DUN_TEMPLE F:MALE | DROP_1D2 | OPEN_DOOR | BASH_DOOR | IM_FIRE | F:IM_POIS | NO_FEAR | NO_CONF | NO_SLEEP | DROP_CORPSE | F:DROP_SKELETON | LITE_2 S:1_IN_7 | S:HEAL D:A monk in white robes. He is skilled in martial arts. N:493:Bert the Stone Troll G:T:W I:110:25d31:20:35:50 W:50:7:0:57000 O:0:100:0 B:HIT:HURT:5d5 B:BITE:HURT:2d10 B:BITE:HURT:2d3 F:UNIQUE | MALE F:FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_90 | DROP_GOOD | DUN_CAVERN | WILD_FOREST1 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | HURT_ROCK D:Big, brawny, powerful and with a taste for hobbit. He has friends called D:Bill and Tom. N:494:Bill the Stone Troll G:T:W I:110:25d31:20:35:50 W:50:7:0:57000 O:0:100:0 B:HIT:HURT:5d5 B:BITE:HURT:2d10 B:BITE:HURT:2d3 F:UNIQUE | MALE F:FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_90 | DROP_GOOD | DUN_CAVERN | WILD_FOREST1 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | HURT_ROCK D:Big, brawny, powerful and with a taste for hobbit. He has friends called D:Bert and Tom. N:495:Tom the Stone Troll G:T:W I:110:25d31:20:35:50 W:50:7:0:57000 O:0:100:0 B:HIT:HURT:5d5 B:BITE:HURT:2d10 B:BITE:HURT:2d3 F:UNIQUE | MALE | F:FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_90 | DROP_GOOD | DUN_CAVERN | WILD_FOREST1 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | HURT_ROCK D:Big, brawny, powerful and with a taste for hobbit. He has friends called D:Bert and Bill. N:496:Cave troll G:T:u I:110:25d9:20:35:50 W:50:1:0:14500 O:20:80:0 B:HIT:HURT:3d5 B:HIT:HURT:1d8 B:HIT:HURT:1d8 B:HIT:HURT:1d8 F:MALE | DUN_CAVERN F:FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | IM_POIS | HURT_LITE D:He is a vicious monster, feared for his ferocity. N:497:Anti-paladin G:p:D I:120:27d16:30:25:30 W:54:2:0:10600 O:0:50:50 B:HIT:HURT:2d6 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:DUN_TEMPLE | DUN_CITY | DUN_CAVERN F:MALE | OPEN_DOOR | BASH_DOOR | TAKE_ITEM | DROP_60 | ONLY_ITEM | F:EVIL | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP | DROP_SKELETON | DROP_CORPSE S:1_IN_4 S:HOLD | SCARE | BLIND | CAUSE_3 | TRAPS | DARKNESS | FORGET | HASTE D:An embodiment of all the cardinal vices, he beholds you scornfully. N:498:Logrus master G:p:v I:120:26d9:30:25:5 W:52:3:0:35000 O:0:0:100 B:KICK:HURT:10d2 B:PUNCH:HURT:10d2 B:KICK:HURT:10d2 F:MALE | DUN_TEMPLE | DUN_TOWER | WILD_MOUNT1 F:FORCE_SLEEP | FORCE_MAXHP | EVIL | F:ONLY_ITEM | DROP_90 | ATTR_ANY | DROP_SKELETON | DROP_CORPSE F:INVISIBLE | OPEN_DOOR | BASH_DOOR | F:IM_ACID | IM_POIS | NO_CONF | NO_SLEEP | LITE_1 S:1_IN_6 | S:HEAL | S:S_SPIDER | BA_CHAO | S_DEMON D:An adept of chaos, feared for his skill of invoking raw Logrus. N:499:Barrow wight G:W:W I:110:25d5:20:25:10 W:50:3:0:9400 O:0:0:0 B:HIT:HURT:1d8 B:HIT:HURT:1d8 B:TOUCH:EXP_40 F:FORCE_SLEEP | FRIENDS | DUN_GRAVE | DUN_RUIN F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_8 | S:HOLD | SCARE | CAUSE_2 | DARKNESS D:It is a ghostly nightmare of an entity. N:500:Giant skeleton troll G:s:b I:110:26d13:20:30:20 W:51:1:0:13000 O:0:0:0 B:HIT:HURT:1d9 B:HIT:HURT:1d9 B:BITE:HURT:1d5 B:BITE:HURT:1d5 F:FORCE_MAXHP | DUN_RUIN | DUN_CAVERN F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | UNDEAD | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is the animated form of a massive troll. N:501:Chaos drake G:d:v I:110:25d14:25:50:30 W:49:3:0:37000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d6 F:DUN_CAVERN | DUN_LAIR | WILD_WASTE2 F:ATTR_MULTI | ATTR_ANY | CAN_FLY | FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_1D2 | RES_DISE | OPEN_DOOR | BASH_DOOR | DROP_CORPSE | F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | BR_DISE | BR_CHAO D:A dragon twisted by the forces of chaos. It seems first ugly, then fair, D:as its form shimmers and changes in front of your eyes. N:502:Law drake G:d:W I:110:29d11:25:50:30 W:58:3:0:53000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d6 F:DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 F:FORCE_SLEEP | FORCE_MAXHP | GOOD | ONLY_ITEM | DROP_1D2 | CAN_FLY | F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | BR_SOUN | BR_SHAR D:This dragon is clever and cunning. It laughs at your puny efforts to D:disturb it. N:503:Balance drake G:d:s I:110:27d13:25:45:30 W:53:3:0:45000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d6 F:ATTR_MULTI | ATTR_ANY | DUN_CAVERN | DUN_LAIR | WILD_FOREST2 F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_1D2 | RES_DISE | F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | DRAGON | GOOD | CAN_FLY | F:IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | S:BR_SOUN | BR_SHAR | BR_DISE | BR_CHAO D:A mighty dragon, the balance drake seeks to maintain the Cosmic Balance, D:and despises your feeble efforts to destroy evil. N:504:Ethereal drake G:d:o I:110:24d14:25:45:15 W:48:3:0:31000 O:40:50:10 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | DUN_LAIR | WILD_FOREST2 F:ONLY_ITEM | DROP_1D2 | CAN_FLY | F:INVISIBLE | PASS_WALL | F:EVIL | DRAGON | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | S:BR_LITE | BR_DARK D:A dragon of elemental power, with control over light and dark, the D:ethereal drake's eyes glare with white hatred from the shadows. N:505:Groo the Wanderer G:p:o I:120:26d43:20:35:50 W:52:7:0:65000 O:0:100:0 B:HIT:HURT:9d1 B:HIT:HURT:6d5 B:HIT:HURT:20d1 B:HIT:HURT:20d1 F:UNIQUE | MALE | WEIRD_MIND | CAN_SPEAK | SILLY | F:FORCE_MAXHP | DUN_LAIR | DUN_CAVERN | WILD_GRASS | DROP_CORPSE F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:DROP_CHOSEN | F:OPEN_DOOR | BASH_DOOR | F:TROLL | IM_COLD | IM_POIS D:He who laughs at Groo's brains will find there is nothing to laugh D:about... erm, nobody laughs at Groo and lives. N:506:Fasolt the Giant G:P:u I:110:26d36:20:35:50 W:51:7:0:50000 O:0:100:0 B:HIT:HURT:5d5 B:BITE:HURT:2d10 B:HIT:EAT_GOLD:2d2 F:UNIQUE | MALE | CAN_SPEAK | F:FORCE_MAXHP | DUN_LAIR | WILD_GRASS | DROP_CORPSE F:ESCORT | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT | IM_COLD | IM_POIS D:Big, brawny, powerful and with a greed for gold. N:507:Logrus ghost G:G:r I:120:26d8:20:15:10 W:51:3:0:19000 O:10:50:40 B:WAIL:TERRIFY B:TOUCH:EXP_40 B:CLAW:LOSE_INT:1d10 F:FORCE_SLEEP | RAND_25 | DUN_TEMPLE F:ONLY_ITEM | DROP_90 | DROP_1D2 | F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | CAN_FLY | F:NO_CONF | NO_SLEEP | ATTR_MULTI | ATTR_ANY | S:1_IN_15 | S:BLIND | HOLD | DRAIN_MANA | FORGET D:An almost life-like creature which is nothing more than a phantom D:created by the Logrus. N:508:Spectre G:G:G I:120:26d9:20:20:10 W:51:3:0:13500 O:0:0:100 B:WAIL:TERRIFY B:TOUCH:EXP_40 B:CLAW:LOSE_WIS:5d5 F:FORCE_SLEEP | RAND_25 | DUN_CAVERN | DUN_GRAVE F:ONLY_ITEM | DROP_1D2 | CAN_FLY | F:COLD_BLOOD | TAKE_ITEM | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_15 | S:BLIND | HOLD | DRAIN_MANA | FORGET D:A phantasmal shrieking spirit. Its wail drives the intense cold of pure D:evil deep within your body. N:509:Water troll G:T:B I:110:25d11:20:35:50 W:50:1:0:11700 O:0:0:0 B:HIT:HURT:1d9 B:HIT:HURT:1d9 B:HIT:HURT:2d2 B:HIT:HURT:2d2 F:MALE | DUN_LAIR | DUN_CAVERN | WILD_SHORE | WILD_SWAMP1 | F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:FRIENDS F:OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE D:He is a troll that reeks of brine. N:510:Fire elemental G:E:r I:110:25d7:12:25:50 W:49:2:0:13500 O:0:0:0 B:HIT:FIRE:4d6 B:HIT:FIRE:4d6 F:FORCE_SLEEP | RAND_25 | F:EMPTY_MIND | CAN_FLY | DUN_PLANAR F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | AURA_FIRE | F:EVIL | IM_FIRE | IM_POIS | NONLIVING | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:BO_FIRE D:It is a towering inferno of flames. N:511:Cherub G:A:W I:120:28d12:30:35:255 W:55:6:0:33000 O:0:90:10 B:HIT:HURT:4d3 B:HIT:HURT:3d8 B:HIT:HURT:4d3 B:HIT:HURT:3d8 F:DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | NO_FEAR | GOOD | CAN_FLY | F:ONLY_ITEM | DROP_2D2 | REFLECTING | RES_TELE | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_ACID | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | LITE_2 S:1_IN_3 | S:HEAL | HASTE | BLIND | SCARE | MIND_BLAST | BO_FIRE | S:S_MONSTERS D:It is an angel moving very quickly, wielding a holy war hammer and casting D:a volley of powerful spells in your direction. N:512:Water elemental G:E:b I:110:25d6:12:20:50 W:50:2:0:13000 O:0:0:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:HIT:HURT:1d10 F:FORCE_SLEEP | RAND_25 | DUN_PLANAR F:EMPTY_MIND | COLD_BLOOD | CAN_FLY | F:KILL_BODY | KILL_ITEM | BASH_DOOR | POWERFUL | F:EVIL | IM_POIS | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:BO_COLD D:It is a towering tempest of water. N:513:Multi-hued hound G:Z:v I:120:27d8:25:20:0 W:54:2:0:23000 O:0:0:0 B:CLAW:HURT:3d6 B:CLAW:HURT:3d6 B:BITE:HURT:4d4 B:BITE:HURT:4d4 F:FORCE_SLEEP | F:FRIENDS | DUN_PLANAR | DUN_CAVERN | WILD_WASTE2 F:BASH_DOOR | ATTR_MULTI | DROP_SKELETON | DROP_CORPSE F:ANIMAL | NO_CONF | NO_SLEEP | LITE_2 F:IM_ELEC | IM_POIS | IM_ACID | IM_FIRE | IM_COLD S:1_IN_5 S:BR_ACID | BR_POIS | BR_COLD | BR_FIRE | BR_ELEC D:Shimmering in rainbow hues, this hound is beautiful and deadly. N:514:Night stalker G:E:w I:130:26d7:20:25:20 W:51:3:0:20500 O:0:0:0 B:GAZE:HURT:6d6 B:GAZE:HURT:6d6 F:RAND_50 | DUN_CAVERN | DUN_PLANAR F:EMPTY_MIND | INVISIBLE | COLD_BLOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY | F:EVIL | IM_COLD | IM_POIS | UNDEAD F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING D:It is impossible to define its form but its violence is legendary. N:515:Carrion crawler G:c:g I:110:24d7:15:20:10 W:47:2:0:5100 O:0:0:0 B:STING:PARALYZE:2d6 B:STING:PARALYZE:2d6 F:RAND_25 | FRIENDS | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | DUN_GRAVE | DUN_CAVERN F:ANIMAL | IM_POIS D:A hideous centipede covered in slime and with glowing tentacles around its D:head. N:516:Master thief G:p:D I:130:35d5:20:25:40 W:70:2:0:63000 O:90:10:0 B:HIT:HURT:2d8 B:HIT:HURT:3d4 B:HIT:EAT_GOLD:4d4 B:HIT:EAT_ITEM:4d5 F:MALE | F:DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DUN_RUIN | DUN_CITY | DUN_TEMPLE | DUN_TOWER F:WILD_GRASS F:EVIL D:Cool and confident, fast and lithe; protect your possessions quickly! N:517:Jurt the Living Trump G:p:R I:120:27d26:20:45:40 W:54:5:0:59000 O:0:10:90 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:UNIQUE | MALE | CAN_SPEAK | AURA_ELEC | DUN_CAVERN F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | LITE_1 F:EVIL | S:1_IN_6 S:TPORT | BLINK | BA_CHAO D:A magical treatment has turned Jurt, an adept of Logrus, D:into a Living Trump. N:518:Lich G:L:w I:110:25d9:20:30:60 W:49:3:0:21000 O:0:10:90 B:TOUCH:EXP_40 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:2d8 B:TOUCH:LOSE_DEX:2d8 F:FORCE_SLEEP | FORCE_MAXHP | DROP_90 | DUN_GRAVE F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP S:1_IN_4 | S:BLINK | TELE_TO | TELE_AWAY | BLIND | HOLD | SLOW | SCARE | S:CAUSE_3 | DRAIN_MANA | BRAIN_SMASH D:It is a skeletal form dressed in robes. It radiates vastly evil power. N:519:Gas spore G:e:g I:110:28d7:30:1:5 W:55:4:0:12500 O:0:0:0 B:EXPLODE:DISEASE:40d2 F:FORCE_SLEEP | F:STUPID | EMPTY_MIND | CAN_FLY | DUN_TOWER F:NO_CONF | NO_SLEEP | NO_FEAR D:From a distance, this creature is often mistaken for the D:much more dangerous beholder. N:520:Master vampire G:V:s I:110:26d10:20:30:10 W:52:3:0:29000 O:20:50:30 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:EXP_VAMP:1d7 B:BITE:EXP_VAMP:1d7 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE | DUN_RUIN F:DROP_2D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_6 | S:TELE_TO | HOLD | CONF | SCARE | CAUSE_3 | MIND_BLAST | FORGET | S:DARKNESS | BO_NETH D:It is a humanoid form dressed in robes. Power emanates from its chilling D:frame. N:521:Oriental vampire G:V:g I:110:26d11:20:30:10 W:51:3:0:46000 O:10:50:40 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:EXP_VAMP:1d8 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE | DUN_CAVERN | DUN_DARKWATER F:DROP_2D2 | CAN_FLY | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | INVISIBLE | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_6 | S:TELE_TO | HOLD | CONF | SCARE | CAUSE_3 | MIND_BLAST | FORGET | S:DARKNESS | BO_NETH D:The oriental vampire is a mist-like creature. N:522:Greater mummy G:z:W I:110:25d22:30:34:255 W:50:3:0:11100 O:50:50:0 B:CLAW:LOSE_CON:1d6 B:CLAW:DISEASE:1d6 B:GAZE:EXP_VAMP:3d4 B:GAZE:TERRIFY:1d4 F:FORCE_SLEEP | FORCE_MAXHP | NO_FEAR | EVIL | UNDEAD | F:COLD_BLOOD | DUN_GRAVE | DUN_RUIN F:ONLY_ITEM | DROP_1D2 | DROP_90 | RES_TELE F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_3 | S:HEAL | HASTE | BLIND | SCARE | S_UNDEAD | ANIM_DEAD S:BA_POIS | BA_NETH | BA_COLD | DRAIN_MANA | S:MIND_BLAST | CAUSE_3 | DARKNESS | FORGET D:Once a powerful ruler, now an even more powerful undead menace. N:523:Bloodletter of Khorne G:U:r I:120:26d8:20:20:30 W:52:1:0:17000 O:0:100:0 B:HIT:EXP_20:10d1 B:HIT:EXP_20:10d1 B:HIT:EXP_20:10d1 F:DUN_HELL | DUN_HORROR F:FORCE_MAXHP | OPEN_DOOR | FRIENDS | DROP_60 | REGENERATE | F:ONLY_ITEM | DROP_CHOSEN | NO_FEAR | NONLIVING | F:EVIL | IM_POIS | IM_COLD | IM_FIRE | DEMON D:Slender, red-skinned demons twisting in nightmarish shapes. D:They are armed with hellblades, which will suck the life from D:your body. N:524:Giant grey scorpion G:S:s I:120:25d11:12:25:40 W:50:4:0:27000 O:0:0:0 B:BITE:HURT:1d6 B:STING:POISON:1d4 F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | WILD_MOUNT1 | DROP_SKELETON | F:ANIMAL D:It is a giant grey scorpion. It looks poisonous. N:525:Earth elemental G:E:u I:100:26d12:10:35:90 W:52:2:0:17000 O:0:0:0 B:HIT:HURT:4d6 B:HIT:HURT:4d6 B:HIT:HURT:4d6 F:FORCE_SLEEP | DUN_PLANAR F:EMPTY_MIND | COLD_BLOOD | NONLIVING | F:KILL_ITEM | KILL_BODY | PASS_WALL | POWERFUL | F:EVIL | IM_FIRE | IM_COLD | IM_POIS | HURT_ROCK | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_8 | S:BO_ACID D:It is a towering form composed of rock with fists of awesome power. N:526:Air elemental G:E:B I:120:25d5:12:25:50 W:49:2:0:15000 O:0:0:0 B:HIT:HURT:1d10 B:HIT:CONFUSE:1d4 B:HIT:HURT:1d10 F:FORCE_SLEEP | RAND_25 | DUN_PLANAR F:EMPTY_MIND | COLD_BLOOD | CAN_FLY | NONLIVING | F:KILL_BODY | KILL_ITEM | BASH_DOOR | POWERFUL | F:EVIL | IM_ACID | IM_FIRE | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_8 | S:BO_ELEC D:It is a towering tornado of winds. N:527:Doom drake G:d:v I:110:26d13:25:50:30 W:51:3:0:27000 O:50:50:0 B:CLAW:HURT:1d5 B:CLAW:HURT:1d5 B:BITE:HURT:2d6 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_90 | FRIENDS | DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 F:OPEN_DOOR | BASH_DOOR | CAN_FLY | LITE_1 F:EVIL | DRAGON | F:IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_6 | S:BR_FIRE D:Doom drakes are trained firedrakes, always moving in pairs, looking D:for a battle. N:528:Gargoyle G:u:s I:110:26d7:10:25:15 W:52:2:0:11300 O:100:0:0 B:CLAW:HURT:2d6 B:CLAW:HURT:2d6 B:BITE:HURT:1d6 F:DROP_60 | ONLY_GOLD | EVIL | DEMON | FRIENDS | HURT_LITE | F:DUN_TOWER | DUN_RUIN | WILD_MOUNT1 | WILD_WASTE1 | WILD_WASTE2 F:IM_POIS | IM_COLD | HURT_ROCK | NONLIVING S:1_IN_12 S:BR_ELEC | BR_FIRE D:A weird demon creature with a stone-like skin. N:529:Malicious leprechaun G:h:v I:120:8d2:8:7:8 W:47:4:0:1550 O:100:0:0 F:FRIENDS | INVISIBLE | RAND_25 | TAKE_ITEM | COLD_BLOOD | F:HURT_LITE | EVIL | OPEN_DOOR | MALE | DUN_MINE | DUN_CAVERN | LITE_1 B:TOUCH:EAT_GOLD B:TOUCH:EAT_ITEM S:1_IN_6 S:BLINK | TPORT | TELE_TO | CAUSE_1 D:This little creature has a fiendish gleam in its eyes. N:530:Eog golem G:g:D I:100:27d52:12:50:10 W:54:4:0:16000 O:100:0:0 B:HIT:HURT:8d6 B:HIT:HURT:8d6 B:HIT:HURT:6d6 B:HIT:HURT:6d6 F:ONLY_GOLD | DROP_1D2 | DUN_RUIN | DUN_TOWER | DUN_CAVERN F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | REFLECTING | F:IM_FIRE | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING D:It is a massive deep brown statue, striding towards you with an D:all-too-familiar purpose. Your magic surprisingly feels much less D:powerful now. N:531:Little Boy G:{:D I:120:28d4:10:40:12 W:55:2:0:19500 O:0:0:0 B:EXPLODE:SHATTER:100d2 F:DUN_MINE | DUN_CAVERN | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | REFLECTING | SILLY | F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_FIRE | IM_COLD | IM_POIS F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | RES_TELE D:A shining machine of death and destruction. N:532:Dagashi G:p:y I:120:25d8:20:30:10 W:50:4:0:6400 O:0:100:0 B:HIT:POISON:3d4 B:HIT:LOSE_STR:3d4 B:HIT:LOSE_STR:3d4 B:HIT:POISON:3d4 F:MALE | DUN_TEMPLE | WILD_GRASS F:DROP_90 | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | NO_CONF | NO_SLEEP D:A human warrior, moving with lightning speed. N:533:Headless ghost G:G:u I:120:27d13:20:15:10 W:53:3:0:29000 O:40:30:30 B:TOUCH:TERRIFY B:TOUCH:EXP_40 B:CLAW:LOSE_INT:5d5 B:CLAW:LOSE_WIS:5d5 F:FORCE_SLEEP | RAND_25 | DUN_GRAVE F:ONLY_ITEM | DROP_60 | DROP_1D2 | CAN_FLY | F:COLD_BLOOD | TAKE_ITEM | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_15 | S:BLIND | DRAIN_MANA | SCARE | BO_COLD | FORGET D:A phantasmal apparition with no head N:534:Dread G:G:o I:120:27d13:20:15:10 W:54:2:0:23500 O:0:50:50 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:LOSE_STR:3d4 F:FORCE_SLEEP | RAND_25 | DUN_GRAVE | DUN_CAVERN F:ONLY_ITEM | DROP_1D2 | F:TAKE_ITEM | INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | CAN_FLY | F:NO_CONF | NO_SLEEP S:1_IN_15 | S:BLIND | HOLD | CONF | DRAIN_MANA | BO_NETH D:It is a form that screams its presence against the eye. Death incarnate, D:its hideous black body seems to struggle against reality as the universe D:itself struggles to banish it. N:535:Leng spider G:S:v I:120:26d14:12:25:40 W:51:40:0:16000 O:0:0:0 B:BITE:POISON:3d6 B:STING:POISON:3d6 F:WEIRD_MIND | BASH_DOOR | FRIENDS | DROP_CORPSE F:ANIMAL | EVIL | DUN_LAIR | DUN_CAVERN | WILD_WASTE1 | WILD_WASTE2 S:1_IN_10 S:ELDRITCH_HORROR D:Bloated purple spiders with long, bristly legs. N:536:Star vampire G:V:r I:120:28d21:20:20:10 W:55:20:0:24500 O:0:0:0 B:CLAW:LOSE_STR:4d4 B:BITE:EXP_VAMP:4d4 B:CLAW:LOSE_STR:4d4 B:BITE:EXP_VAMP:4d4 F:DUN_GRAVE | DUN_HORROR F:EVIL | IM_FIRE | IM_ELEC | IM_POIS | F:NONLIVING | CAN_FLY | F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN S:1_IN_10 S:ELDRITCH_HORROR D:"It was red and dripping; an immensity of pulsing, moving jelly; D:a scarlet blob with myriad tentacular trunks that waved and waved. D:There were suckers on the tips of the appendages, and these were D:opening and closing with ghoulish lust... the thing was bloated and D:obscene; a headless, faceless, eyeless bulk, with the ravenous maw and D:titanic talons of a star-born monster." N:537:Smoke elemental G:E:D I:120:29d4:10:40:90 W:57:3:0:41000 O:0:0:0 B:BITE:HURT:2d6 B:BITE:HURT:2d6 F:FORCE_SLEEP | DUN_PLANAR F:EMPTY_MIND | F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | F:EVIL | IM_FIRE | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | CAN_FLY | NONLIVING S:1_IN_5 | S:DARKNESS | BO_FIRE D:It is a towering blackened form, crackling with heat. N:538:Olog G:T:g I:110:26d10:20:35:50 W:51:1:0:17000 O:10:90:0 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:BITE:HURT:2d3 B:BITE:HURT:2d3 F:DUN_CAVERN F:FORCE_MAXHP | OPEN_DOOR | FRIENDS | DROP_60 | F:SMART | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | TROLL | IM_POIS D:It is a massive intelligent troll with needle sharp fangs. N:539:Halfling slinger G:h:U I:110:24d9:20:20:30 W:47:1:0:7300 O:100:0:0 B:HIT:HURT:2d6 B:HIT:HURT:2d6 F:DUN_CITY | DUN_RUIN | DUN_CAVERN | WILD_FOREST1 | WILD_GRASS F:FORCE_MAXHP | OPEN_DOOR | FRIENDS | DROP_90 | F:SMART | EVIL | IM_POIS | MALE | DROP_SKELETON | DROP_CORPSE S:1_IN_3 S:ARROW D:A rebel halfling who has rejected the halfling tradition of archery. N:540:Gravity hound G:Z:s I:110:26d9:30:16:0 W:51:2:0:14500 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | FRIENDS | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | DUN_CAVERN | DUN_PLANAR F:ANIMAL | NO_CONF | NO_SLEEP | S:1_IN_5 | S:BR_GRAV D:Unfettered by the usual constraints of gravity, these unnatural creatures D:are walking on the walls and even the ceiling! The earth suddenly feels D:rather less solid as you see gravity warp all round the monsters. N:541:Acidic cytoplasm G:j:G I:120:25d10:12:12:1 W:49:5:0:11800 O:50:0:50 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 F:FORCE_MAXHP | TAKE_ITEM | COLD_BLOOD | DUN_MINE | DUN_CAVERN | DUN_RUIN F:DROP_1D2 | DROP_2D2 | F:STUPID | EMPTY_MIND | OPEN_DOOR | BASH_DOOR | F:IM_ACID | IM_POIS | F:NO_FEAR | NO_CONF | NO_SLEEP D:A disgusting animated blob of destruction. Flee its gruesome hunger! N:542:Inertia hound G:Z:W I:110:26d9:30:16:0 W:51:2:0:13500 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | FRIENDS | DROP_SKELETON | DROP_CORPSE | DUN_CAVERN | DUN_PLANAR F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_5 | S:BR_INER D:Bizarrely, this hound seems to be hardly moving at all, yet it approaches D:you with deadly menace. It makes you tired just to look at it. N:543:Impact hound G:Z:u I:110:26d9:30:16:0 W:51:2:0:14500 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | F:FRIENDS | F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE | DUN_CAVERN | DUN_PLANAR F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_8 | S:BR_WALL D:A deep brown shape is visible before you, its canine form strikes you with D:an almost physical force. The dungeon floor buckles as if struck by a D:powerful blow as it stalks towards you. N:544:Sea troll G:T:B I:110:27d11:20:25:50 W:54:1:0:27000 O:0:100:0 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:HIT:HURT:3d2 B:HIT:HURT:3d2 F:MALE | F:FORCE_MAXHP | AQUATIC | F:FRIENDS | DROP_60 | DUN_LAIR | WILD_OCEAN | F:OPEN_DOOR | BASH_DOOR | RES_WATE F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE D:He is a troll that reeks of brine, close relative to water trolls. N:545:Ooze elemental G:E:U I:110:28d3:10:40:90 W:55:3:0:19500 O:0:0:0 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 F:FORCE_SLEEP | F:EMPTY_MIND | COLD_BLOOD | DUN_PLANAR | WILD_SHORE | WILD_SWAMP1 | F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | F:EVIL | IM_ACID | CAN_SWIM | NONLIVING | F:IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_5 | S:BO_ACID | BA_ACID D:It is a towering mass of filth, an eyesore of ooze. N:546:Young black dragon G:d:D I:110:21d8:20:30:50 W:41:1:0:7500 O:50:50:0 B:CLAW:HURT:1d5 B:CLAW:HURT:1d5 B:BITE:HURT:1d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | DUN_LAIR | WILD_SWAMP2 | F:DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | DRAGON | IM_ACID S:1_IN_11 | S:SCARE | S:BR_ACID D:It has a form that legends are made of. Its still-tender scales are a D:darkest black hue. Acid drips from its body. N:547:Mumak G:q:s I:110:25d22:20:30:100 W:49:3:0:14000 O:0:0:0 B:BUTT:HURT:4d6 B:BUTT:HURT:4d6 B:CRUSH:HURT:4d4 F:BASH_DOOR | DROP_CORPSE | DUN_LAIR | DUN_CITY F:ANIMAL D:A massive elephantine form with eyes twisted by madness. N:548:Giant fire ant G:a:r I:110:24d5:14:30:40 W:48:1:0:6900 O:0:0:0 B:BITE:FIRE:3d12 B:BITE:FIRE:3d12 F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | DUN_LAIR | DUN_CAVERN F:ANIMAL | IM_FIRE D:A giant ant covered in shaggy fur. Its powerful jaws glow with heat. N:549:Mature white dragon G:d:w I:110:27d13:20:40:70 W:54:1:0:32000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d8 F:FORCE_SLEEP | FORCE_MAXHP | DROP_1D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | CAN_FLY | DUN_LAIR | DUN_CAVERN | WILD_WASTE2 F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_10 | S:SCARE | S:BR_COLD D:A large dragon, scales gleaming bright white. N:550:Xorn G:X:o I:110:26d5:20:40:10 W:51:2:0:17500 O:0:0:0 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:HIT:HURT:1d6 F:FORCE_MAXHP | DUN_PLANAR | DUN_MINE | DUN_CAVERN F:EMPTY_MIND | COLD_BLOOD | F:KILL_ITEM | PASS_WALL | F:IM_FIRE | IM_COLD | IM_POIS | F:HURT_ROCK | NO_CONF | NO_SLEEP D:A huge creature of the element Earth. Able to merge with its element, it D:has four huge arms protruding from its enormous torso. N:551:Rogrog the Black Troll G:T:D I:120:26d41:20:35:50 W:52:5:0:60000 O:0:100:0 B:HIT:HURT:6d6 B:BITE:HURT:2d10 B:BITE:HURT:2d3 B:SPIT:ACID:3d8 F:UNIQUE | MALE | DUN_CAVERN | DUN_LAIR F:FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_CORPSE F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | IM_COLD | IM_POIS D:A massive and cruel troll of great power, drool slides caustically down D:his muscular frame. Despite his bulk, he strikes with stunning speed. N:552:Mist giant G:#:B I:120:25d20:20:25:50 W:49:2:0:32000 O:0:0:0 B:CRUSH:HURT:4d8 B:CRUSH:HURT:4d8 B:CRUSH:HURT:4d8 B:BITE:EXP_40:3d9 F:FORCE_MAXHP | OPEN_DOOR | DUN_LAIR | DUN_DARKWATER | WILD_SWAMP1 | F:SMART | BASH_DOOR | F:EVIL | GIANT | IM_POIS | CAN_FLY D:"Two eyes, the colour of a thin, yellow wine, were set high in the D:thing's body; though it had no separate head. A mouthing, obscene slit, D:filled with fangs lay just beneath the eyes. It had no nose or ears... D:Four appendages sprang from its upper parts and its lower body D:slithered along the ground, unsupported by any limbs... incredibly D:disgusting to behold and its amorphous body gave off a stench of death D:and decay..." N:553:Pattern ghost G:G:D I:120:26d14:30:15:20 W:51:3:0:27000 O:50:50:0 B:TOUCH:EXP_80 B:TOUCH:EXP_40 B:CLAW:LOSE_INT:1d10 B:CLAW:LOSE_WIS:1d10 F:FORCE_SLEEP | F:ONLY_ITEM | DROP_90 | F:INVISIBLE | COLD_BLOOD | PASS_WALL | CAN_FLY | DUN_TEMPLE | DUN_CAVERN F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_5 | S:FORGET | MIND_BLAST D:An almost life-like creature which is nothing more than a phantom D:created by the Pattern. N:554:Grey wraith G:W:s I:110:27d5:20:30:10 W:54:1:0:18000 O:0:50:50 B:HIT:HURT:1d10 B:HIT:HURT:1d10 B:TOUCH:EXP_40 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE F:DROP_60 F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | HURT_LITE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_7 | S:HOLD | SCARE | CAUSE_3 | DARKNESS D:A tangible but ghostly form, made of grey fog. The air around it feels D:deathly cold. N:555:Revenant G:W:u I:125:27d7:30:35:5 W:53:1:0:23000 O:0:0:100 B:HIT:HURT:2d10 B:HIT:HURT:2d10 F:FORCE_SLEEP | FORCE_MAXHP | REGENERATE | DUN_GRAVE F:DROP_90 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL | UNDEAD | HURT_LITE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_4| S:BO_FIRE D:Back from the grave, to wreak vengeance upon the living. A gaunt, tall, D:skeletal figure wearing a ruined plate mail. N:556:Young multi-hued dragon G:d:v I:110:23d8:20:35:50 W:45:1:0:21000 O:50:50:0 B:CLAW:HURT:1d9 B:CLAW:HURT:1d9 B:BITE:HURT:2d10 F:ATTR_MULTI | F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:DROP_2D2 | CAN_FLY | F:OPEN_DOOR | BASH_DOOR | DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 | WILD_WASTE2 F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | F:IM_POIS | NO_CONF | NO_SLEEP | LITE_1 S:1_IN_5 | S:SCARE | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS D:It has a form that legends are made of. Beautiful scales of shimmering D:and magical colours cover it. N:557:Raal's Tome of Destruction G:?:r I:120:29d20:20:75:15 W:57:4:0:70000 O:0:0:0 F:NEVER_MOVE | NEVER_BLOW | NONLIVING | DUN_TOWER F:FORCE_SLEEP | DROP_90 | DROP_GOOD | EVIL | COLD_BLOOD | EMPTY_MIND | F:FORCE_MAXHP | NO_CONF | NO_FEAR | NO_SLEEP | CHAR_MIMIC | F:IM_ACID | IM_COLD | IM_ELEC | HURT_FIRE | RES_NETH | RES_TELE S:1_IN_2 | S:BO_ACID | BR_FIRE | BO_MANA | BR_COLD | BR_POIS | S:BO_WATE | BA_POIS | BR_NETH D:A sentient arcane tome casting spells with malevolent intent. N:558:Colossus G:g:y I:105:28d75:15:75:10 W:55:4:0:30000 O:0:0:0 B:HIT:HURT:10d10 B:HIT:HURT:10d10 B:HIT:HURT:6d6 B:HIT:HURT:6d6 F:FORCE_MAXHP | DUN_TOWER | DUN_RUIN | DUN_CAVERN F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | F:IM_FIRE | IM_COLD F:IM_POIS | NONLIVING | REFLECTING | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_8 S:ARROW D:An enormous construct resembling a titan made from stone. It strides D:purposefully towards you, swinging its slow fists with earth-shattering D:power. N:559:Young gold dragon G:d:y I:110:22d8:20:35:150 W:44:2:0:15000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d8 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:DROP_2D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | F:DRAGON S:1_IN_11 | S:SCARE | S:BR_SOUN D:It has a form that legends are made of. Its still-tender scales are a D:tarnished gold hue, and light is reflected from its form. N:560:Mature blue dragon G:d:b I:110:27d13:20:40:70 W:54:1:0:22500 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d10 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:DROP_2D2 | DROP_CORPSE F:BASH_DOOR | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | WILD_FOREST2 | F:EVIL | DRAGON | IM_ELEC | NO_CONF | NO_SLEEP | LITE_1 S:1_IN_9 | S:SCARE | S:BR_ELEC D:A large dragon, scales tinted deep blue. N:561:Mature green dragon G:d:g I:110:27d13:20:40:70 W:54:1:0:27000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d10 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:DROP_2D2 | DROP_CORPSE F:BASH_DOOR | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | WILD_FOREST2 | F:EVIL | DRAGON | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_9 | S:SCARE | S:BR_POIS D:A large dragon, scales tinted deep green. N:562:Mature bronze dragon G:d:U I:110:27d15:20:40:150 W:54:2:0:48000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d10 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | F:DROP_2D2 | CAN_FLY | F:BASH_DOOR | DROP_CORPSE F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_9 | S:CONF | SCARE | S:BR_CONF D:A large dragon with scales of rich bronze. N:563:Young red dragon G:d:r I:110:21d9:20:30:50 W:41:1:0:7500 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d8 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:DROP_1D2 | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | WILD_WASTE2 | F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | LITE_1 F:EVIL | DRAGON | IM_FIRE S:1_IN_11 | S:SCARE | S:BR_FIRE D:It has a form that legends are made of. Its still-tender scales are a D:deepest red hue. Heat radiates from its form. N:564:Nightblade G:h:D I:125:26d7:20:30:10 W:52:2:0:16000 O:0:100:0 B:HIT:POISON:8d4 B:HIT:POISON:8d4 B:HIT:LOSE_CON:2d4 F:MALE | DUN_TEMPLE | DUN_LAIR | DUN_DARKWATER | WILD_FOREST1 F:DROP_90 | INVISIBLE | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | HURT_LITE | F:EVIL | NO_CONF | NO_SLEEP D:A dark elven assassin, so stealthy that he is almost impossible to see. N:565:Trapper G:.:w I:120:26d14:30:35:10 W:52:3:0:16000 O:0:0:0 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:PARALYZE:15d1 B:HIT:PARALYZE:15d1 F:CHAR_CLEAR | CHAR_MIMIC | ATTR_CLEAR | DUN_TOWER | DUN_HORROR | DUN_CAVERN F:NEVER_MOVE | FORCE_MAXHP | F:INVISIBLE | EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | D:This creature traps unsuspecting victims D:and paralyzes them, to be slowly digested later. N:566:Bodak G:u:D I:110:27d8:10:30:90 W:53:2:0:22000 O:0:0:0 B:HIT:FIRE:4d6 B:HIT:FIRE:4d6 B:GAZE:EXP_20 F:FORCE_SLEEP | DUN_HELL | DUN_CAVERN F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | AURA_FIRE | NONLIVING | F:EVIL | DEMON | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_4 | S:BO_FIRE | BA_FIRE | S:S_DEMON D:It is a humanoid form composed of flames and hatred. N:567:Time bomb G:.:w I:130:29d4:30:20:0 W:57:5:0:5400 O:0:0:0 B:EXPLODE:TIME:30d2 F:CHAR_CLEAR | CHAR_MIMIC | ATTR_CLEAR | DUN_TOWER | DUN_TEMPLE | DUN_RUIN F:NEVER_MOVE | FORCE_MAXHP | F:EMPTY_MIND | INVISIBLE | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR | D:It was left here to be used against intruders. N:568:Mezzodaemon G:u:o I:110:27d15:10:34:90 W:54:2:0:34000 O:0:0:0 B:CLAW:HURT:5d6 B:CLAW:HURT:5d6 F:FORCE_SLEEP | PASS_WALL | INVISIBLE | DUN_HELL | DUN_CAVERN F:IM_POIS | IM_ACID | IM_FIRE | F:NO_SLEEP | NO_CONF | NO_STUN | NONLIVING | F:EVIL | DEMON S:1_IN_4 | S:BLINK | DARKNESS | S_DEMON D:An ugly demon with insect-like extremities and large bulbous eyes. N:569:Elder thing G:u:G I:110:24d15:10:35:50 W:48:3:0:21000 O:0:0:0 B:CRUSH:HURT:4d6 B:CRUSH:HURT:4d6 B:CRUSH:HURT:4d6 B:TOUCH:LOSE_WIS F:FORCE_SLEEP | OPEN_DOOR | BASH_DOOR | NONLIVING | DUN_HORROR | DUN_HELL F:DEMON | IM_POIS | IM_ACID | NO_CONF | NO_SLEEP | RES_TELE | F:CAN_SWIM | S:1_IN_4 | S:SCARE | TELE_AWAY | BA_NUKE | CAUSE_4 | BA_POIS | S:CONF | S_DEMON | S_UNDEAD D:"...some ridged barrel-shaped objects with thin D:horizontal arms radiating spoke-like from a central ring and with D:vertical knobs or bulbs projecting from the head and base of the D:barrel. Each of these knobs was the hub of a system of five long, D:flat, triangularly tapering arms arranged around it like the arms D:of a starfish." N:570:Ice elemental G:E:w I:110:28d8:10:30:90 W:55:2:0:17500 O:0:0:0 B:BITE:COLD:1d3 B:HIT:HURT:4d6 B:BITE:COLD:1d3 F:FORCE_SLEEP | F:EMPTY_MIND | COLD_BLOOD | AURA_COLD | DUN_PLANAR F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | F:EVIL | IM_COLD | IM_ELEC | CAN_SWIM | F:IM_POIS | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_5 | S:BO_ICEE | BA_COLD D:It is a towering glacier of ice. N:571:Ipsissimus G:p:D I:110:29d6:20:25:10 W:57:2:0:15000 O:10:0:90 B:HIT:HURT:2d6 B:HIT:HURT:2d6 F:MALE | F:FORCE_SLEEP | FORCE_MAXHP | DUN_TEMPLE F:ONLY_ITEM | DROP_90 | DROP_SKELETON F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL S:1_IN_3 | S:HASTE | TPORT | TELE_TO | BLIND | HOLD | SCARE | CAUSE_3 | S:BO_NETH | MIND_BLAST | FORGET | S:S_UNDEAD | S_DEMON | ANIM_DEAD D:A gaunt figure, clothed in black robes. N:572:The Greater hell magic mushroom were-quylthulg G:Q:s I:120:27d36:50:40:50 W:54:30:0:75000 O:0:50:50 B:GAZE:EXP_VAMP:4d8 B:GAZE:EXP_VAMP:4d8 B:CRUSH:ACID:8d8 B:CRUSH:ACID:8d8 F:DUN_HORROR F:FORCE_MAXHP | FORCE_SLEEP | UNIQUE | NO_STUN | NO_CONF | F:NO_SLEEP| EVIL | IM_ACID | IM_FIRE | IM_POIS | F:IM_COLD | RES_NETH | RES_WATE | RES_PLAS | RES_DISE | SMART | F:RES_NEXU | NONLIVING | RES_TELE | KILL_WALL | F:BASH_DOOR | DEMON | COLD_BLOOD | ANIMAL | CAN_SWIM | F:DROP_GOOD | DROP_GREAT | ONLY_ITEM | DROP_1D2 | SILLY S:1_IN_3 | S:BLINK | SLOW | SCARE | DARKNESS | HEAL | ANIM_DEAD S:TPORT | TELE_AWAY | HASTE | S_MONSTER | DRAIN_MANA | S:S_UNDEAD | S_DEMON | S_DRAGON | S_KIN |ELDRITCH_HORROR D:This unholy abomination will crush you too. Flee while you can! N:573:Lord Borel of Hendrake G:p:v I:120:27d47:25:50:10 W:54:2:0:96000 O:0:100:0 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:HURT:3d8 B:HIT:HURT:3d8 F:UNIQUE | MALE | CAN_SPEAK | DUN_TEMPLE | DUN_CITY F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | IM_ACID | IM_FIRE | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_4 | S:TELE_TO | S:S_KIN D:A Lord of Chaos famous for his skill at arms. N:574:Chaos spawn G:e:u I:110:26d20:20:25:20 W:51:2:0:10300 O:0:0:0 B:GAZE:HURT:10d10 B:GAZE:UN_BONUS:5d2 B:GAZE:EXP_VAMP:5d2 B:GAZE:PARALYZE:5d2 F:DUN_HORROR F:FORCE_MAXHP | BASH_DOOR | EVIL | CAN_FLY D:It has two eyestalks and a large central eye. Its gaze can kill. N:575:Mummified troll G:z:w I:110:28d7:20:30:50 W:56:1:0:19500 O:0:100:0 B:HIT:HURT:2d6 B:HIT:HURT:2d6 F:FORCE_MAXHP | F:DUN_GRAVE | DUN_CAVERN F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | TROLL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is a massive figure clothed in wrappings. You are wary of its massive D:fists. N:576:Fire angel G:d:y I:120:29d13:10:50:25 W:57:3:0:90000 O:50:50:0 B:CLAW:HURT:3d8 B:CLAW:HURT:3d8 B:BITE:HURT:5d5 B:BITE:HURT:5d5 F:DUN_TEMPLE | DUN_CAVERN F:EVIL | DRAGON | SMART | FORCE_MAXHP | NO_CONF | NO_SLEEP | IM_FIRE F:DROP_2D2 | BASH_DOOR | RES_PLAS | CAN_FLY | DROP_CORPSE | LITE_1 S:1_IN_9 S:BR_PLAS D:It is a fast, deadly dragonoid creature. It was bred and trained D:in the Courts of Chaos for assassination. N:577:Crypt thing G:L:B I:120:26d24:20:30:60 W:52:3:0:49000 O:50:50:0 B:TOUCH:EXP_40 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:2d10 B:TOUCH:LOSE_DEX:2d10 F:DUN_GRAVE F:FORCE_SLEEP | FORCE_MAXHP | DROP_90 | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP S:1_IN_3 | S:BLINK | TELE_TO | TELE_AWAY | TELE_LEVEL | S:CAUSE_3 | DRAIN_MANA | BRAIN_SMASH D:It is a skeletal form dressed in robes. D:It looks evil and devious... N:578:Chaos butterfly G:I:G I:120:28d15:40:30:10 W:55:2:0:27000 O:0:0:0 B:CLAW:HURT:3d7 B:CLAW:HURT:3d7 B:CRUSH:HURT:10d5 F:FORCE_SLEEP | F:CAN_FLY | DUN_LAIR | DUN_TOWER | DUN_CAVERN | WILD_FOREST1 | WILD_FOREST2 F:WEIRD_MIND | BASH_DOOR | ATTR_MULTI | ATTR_ANY | F:NO_CONF | NO_SLEEP | EVIL S:1_IN_9 S:BR_CONF | BR_CHAO D:"It had the appearance of a butterfly, but but a butterfly with D:wings so huge they blotted out the sun... it had a man's body, D:covered with hairs or feathers hued like a peacocks..." N:579:Time elemental G:E:G I:120:27d10:90:35:10 W:53:2:0:21000 O:0:0:0 B:TOUCH:TIME:3d4 B:TOUCH:TIME:3d4 F:DUN_PLANAR F:PASS_WALL | IM_POIS | IM_FIRE | NONLIVING | CAN_FLY | F:NO_CONF | NO_SLEEP | EVIL | EMPTY_MIND | KILL_ITEM | RAND_50 S:1_IN_7 S:SLOW | BR_TIME | D:You have not seen it yet. N:580:Flying polyp G:l:R I:115:28d9:90:35:10 W:55:20:0:20500 O:0:0:0 B:CRUSH:PARALYZE:7d4 B:CRUSH:PARALYZE:7d4 B:CRUSH:PARALYZE:7d4 F:DUN_HORROR F:PASS_WALL | INVISIBLE | FORCE_MAXHP | RES_DISE | F:IM_POIS | IM_COLD | IM_ACID | F:NO_CONF | NO_SLEEP | EVIL | CAN_FLY | S:1_IN_7 S:BR_WALL | ELDRITCH_HORROR | D:"They were only partly material and had the power of aerial motion, D:despite the absence of wings... Suggestions of monstrous plasticity D:and of temporary lapses of visibility..." N:581:The Queen Ant G:a:v I:120:29d32:30:45:10 W:57:2:0:77000 O:50:50:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d8 B:BITE:HURT:2d8 F:UNIQUE | FEMALE | GOOD | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | DUN_MINE | DUN_CAVERN F:ESCORT | ESCORTS | CAN_FLY | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:WEIRD_MIND | OPEN_DOOR | BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_2 | S:S_ANT D:She's upset because you hurt her children. N:582:Will o' the wisp G:E:y I:130:31d4:30:60:0 W:61:4:0:62000 O:0:0:0 B:HIT:HURT:1d9 B:HIT:HURT:1d9 F:FORCE_SLEEP | FORCE_MAXHP | RAND_50 | F:SMART | EMPTY_MIND | INVISIBLE | F:PASS_WALL | POWERFUL | CAN_FLY | DUN_RUIN | DUN_DARKWATER F:WILD_SWAMP1 | WILD_SWAMP2 F:IM_COLD | IM_ELEC | IM_POIS | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_2 | S:BLINK | TPORT | CONF | CAUSE_2 D:A strange ball of glowing light. It disappears and reappears and seems to D:draw you to it. You seem somehow compelled to stand still and watch its D:strange dancing motion. N:583:Shan G:I:B I:120:29d5:20:60:20 W:57:4:0:26000 O:0:0:0 F:IM_POIS | IM_COLD | COLD_BLOOD | ANIMAL | EVIL | DUN_HORROR F:NO_SLEEP | NO_CONF | CAN_FLY | NEVER_BLOW S:1_IN_2 S:CONF | HOLD | DRAIN_MANA | FORGET | MIND_BLAST | SHRIEK D:"Those huge lidless eyes which stared with hate at me, the jointed D:tendrils which seemed to twist from the head in cosmic rhythms, D:the ten legs, covered with black shining tentacles and folded into D:the pallid underbelly, and the semi-circular ridged wings covered D:with triangular scales -- all this cannot convey the soul-ripping D:horror of the shape which darted at me. I saw the three mouths D:of the thing move moistly, and then it was upon me." N:584:Magma elemental G:E:o I:110:29d8:10:30:90 W:57:2:0:34000 O:0:0:0 B:HIT:FIRE:3d7 B:HIT:HURT:4d6 B:HIT:FIRE:3d7 F:FORCE_SLEEP | F:EMPTY_MIND | AURA_FIRE | DUN_PLANAR F:KILL_ITEM | KILL_BODY | PASS_WALL | POWERFUL | F:EVIL | IM_FIRE | NONLIVING | F:IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_7 | S:BO_PLAS | BA_FIRE D:It is a towering glowing form of molten hate. N:585:Black pudding G:j:D I:110:25d10:12:12:1 W:49:5:0:2600 O:100:0:0 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 B:TOUCH:ACID:1d10 F:FORCE_MAXHP | F:FRIENDS | DUN_CAVERN F:DROP_1D2 | F:STUPID | EMPTY_MIND | COLD_BLOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | CAN_SWIM D:A lump of rotting black flesh that slurrrrrrrps across the dungeon floor. N:586:Killer iridescent beetle G:K:v I:110:28d6:16:24:30 W:56:2:0:13500 O:0:0:0 B:CLAW:ELEC:1d12 B:CLAW:ELEC:1d12 B:GAZE:PARALYZE F:ATTR_MULTI | DUN_LAIR | DUN_CAVERN | WILD_FOREST2 F:FORCE_MAXHP | F:WEIRD_MIND | BASH_DOOR | AURA_ELEC | DROP_CORPSE F:ANIMAL | IM_ELEC | CAN_FLY D:It is a giant beetle, whose carapace shimmers with vibrant energies. N:587:Nexus vortex G:v:v I:120:26d8:100:15:0 W:52:1:0:17000 O:0:0:0 B:ENGULF:HURT:5d5 F:FORCE_SLEEP | DUN_PLANAR F:RAND_50 | RAND_25 | RES_NEXU | CAN_FLY | ATTR_MULTI F:EMPTY_MIND | BASH_DOOR | POWERFUL | RES_TELE | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_6 | S:BR_NEXU D:A maelstrom of potent magical energy. N:588:Plasma vortex G:v:R I:120:25d8:100:15:0 W:49:1:0:16500 O:0:0:0 B:ENGULF:FIRE:8d8 F:FORCE_SLEEP | DUN_PLANAR F:RAND_50 | RAND_25 | RES_PLAS | AURA_FIRE | AURA_ELEC | ATTR_MULTI F:EMPTY_MIND | BASH_DOOR | POWERFUL | F:IM_FIRE | CAN_FLY | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_6 | S:BR_PLAS D:A whirlpool of intense flame, charring the stones at your feet. N:589:Mature red dragon G:d:r I:110:27d15:20:40:30 W:54:1:0:77000 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d10 B:BITE:HURT:2d12 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | WILD_WASTE2 | F:DROP_2D2 | CAN_FLY | DROP_CORPSE F:BASH_DOOR | LITE_1 F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_9 | S:CONF | SCARE | S:BR_FIRE D:A large dragon, scales tinted deep red. N:590:Mature gold dragon G:d:y I:110:29d14:20:45:150 W:57:2:0:62000 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d10 B:BITE:HURT:2d12 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | F:DROP_2D2 | CAN_FLY | DROP_CORPSE F:BASH_DOOR | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_9 | S:CONF | SCARE | S:BR_SOUN D:A large dragon with scales of gleaming gold. N:591:Crystal drake G:d:B I:120:28d17:25:45:30 W:56:2:0:77000 O:50:50:0 B:CLAW:HURT:1d4 B:CLAW:HURT:1d4 B:BITE:HURT:2d5 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | REFLECTING | DUN_CAVERN F:INVISIBLE | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | BLIND | S:BR_SHAR D:A dragon of strange crystalline form. Light shines through it, dazzling D:your eyes with spectrums of colour. N:592:Mature black dragon G:d:D I:110:27d13:20:40:30 W:54:1:0:31000 O:50:50:0 B:CLAW:HURT:1d8 B:CLAW:HURT:1d8 B:BITE:HURT:2d10 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE F:DROP_2D2 | DUN_LAIR | DUN_CAVERN | WILD_SWAMP2 | F:BASH_DOOR | F:EVIL | DRAGON | IM_ACID | NO_CONF | NO_SLEEP S:1_IN_9 | S:SCARE | S:BR_ACID D:A large dragon, with scales of deepest black. N:593:Mature multi-hued dragon G:d:v I:110:29d14:20:40:50 W:58:2:0:95000 O:50:50:0 B:CLAW:HURT:1d10 B:CLAW:HURT:1d10 B:BITE:HURT:2d12 F:ATTR_MULTI | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 F:DROP_1D2 | DROP_2D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | LITE_1 F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD | F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_5 | S:BLIND | CONF | SCARE | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS D:A large dragon, scales shimmering many colours. N:594:Sky whale G:l:B I:110:31d20:20:37:30 W:61:6:0:30000 O:50:50:0 B:CRUSH:HURT:20d2 B:CRUSH:HURT:20d2 B:CRUSH:HURT:20d2 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY F:DUN_HORROR | DUN_CAVERN | WILD_WASTE1 | WILD_WASTE2 F:DROP_2D2 | DROP_CORPSE F:BASH_DOOR | SMART | GOOD | F:NO_CONF | NO_SLEEP | RES_NEXU | RES_TELE | RES_DISE S:1_IN_9 | S:BRAIN_SMASH | CONF | SCARE | FORGET | TELE_TO | TELE_AWAY | SHRIEK D:A vastly intelligent whale from the stars. N:595:Father Dagon G:u:g I:120:30d48:12:40:50 W:60:30:0:175000 O:50:50:0 B:CLAW:POISON:8d4 B:CLAW:POISON:8d4 B:BITE:HURT:8d8 F:ESCORT | DUN_HORROR | DUN_HELL F:DROP_60 | DROP_1D2 | DROP_GOOD | ONLY_ITEM | DROP_CORPSE F:CAN_SWIM | BASH_DOOR | RES_TELE | CAN_SPEAK | F:EVIL | DEMON | IM_FIRE | IM_COLD | IM_POIS | RES_WATE | F:UNIQUE | MALE | FORCE_SLEEP | FORCE_MAXHP | POWERFUL | F:NO_CONF | NO_SLEEP | S:1_IN_7 | S:S_KIN | S_DEMON | BO_WATE | BA_WATE | BO_ACID | BA_ACID | ELDRITCH_HORROR | D:The king of the deep ones. "Vast, Polyphemous-like, and loathsome, it D:darted like a stupendous monster of nightmares..." N:596:Mother Hydra G:u:g I:120:30d48:12:60:50 W:60:30:0:225000 O:0:50:50 B:CLAW:POISON:8d4 B:CLAW:POISON:8d4 B:BITE:HURT:8d8 F:ESCORT | DUN_HORROR | DUN_HELL F:DROP_60 | DROP_1D2 | DROP_GOOD | ONLY_ITEM | DROP_CORPSE F:CAN_SWIM | BASH_DOOR | RES_TELE | CAN_SPEAK | F:EVIL | DEMON | IM_FIRE | IM_COLD | IM_POIS | RES_WATE | F:UNIQUE | FEMALE | FORCE_SLEEP | FORCE_MAXHP | POWERFUL | F:NO_CONF | NO_SLEEP | S:1_IN_7 | S:S_HYDRA | S_DEMON | DARKNESS | BA_WATE | BO_ACID | BA_ACID | ELDRITCH_HORROR D:The queen of the deep ones. "Vast, Polyphemous-like, and loathsome, it D:darted like a stupendous monster of nightmares..." N:597:Death knight G:p:s I:120:29d13:20:40:10 W:57:1:0:34000 O:0:90:0 B:HIT:HURT:6d6 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:EXP_20 F:DUN_TEMPLE | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_NETH | F:ONLY_ITEM | DROP_1D2 F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | IM_COLD | LITE_1 S:1_IN_5 | S:BLIND | SCARE | CAUSE_3 | BO_NETH | S:S_MONSTERS D:It is a humanoid form dressed in armour of an ancient form. From beneath D:its helmet, eyes glow a baleful red and seem to pierce you like lances of D:fire. N:598:Mandor, Master of the Logrus G:p:v I:120:30d18:20:35:40 W:59:5:0:130000 O:0:0:100 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:UN_POWER:5d5 B:HIT:UN_BONUS:5d5 F:UNIQUE | MALE | CAN_SPEAK | DUN_TOWER F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:SMART | OPEN_DOOR | TAKE_ITEM | BASH_DOOR | F:EVIL | RES_TELE | LITE_2 S:1_IN_2 | S:BO_FIRE | BO_COLD | HOLD | BO_MANA | S_MONSTER S:TRAPS | BO_ICEE | HEAL | BO_PLAS | BA_CHAO D:Mandor is one of the greatest Logrus Masters, a formidable magician. N:599:Time vortex G:v:G I:130:27d8:100:15:0 W:53:4:0:28000 O:0:0:0 B:ENGULF:HURT:5d5 F:FORCE_SLEEP | DUN_PLANAR F:RAND_50 | RAND_25 | F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_6 | S:BR_TIME D:You haven't seen it yet. N:600:Shimmering vortex G:v:v I:140:24d2:100:15:0 W:54:4:0:18500 O:0:0:0 F:ATTR_MULTI | DUN_PLANAR F:FORCE_SLEEP | NEVER_BLOW | F:RAND_50 | RAND_25 | CAN_FLY | LITE_2 F:EMPTY_MIND | BASH_DOOR | POWERFUL | RES_TELE | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_4 | S:BR_LITE | SHRIEK D:A strange pillar of shining light that hurts your eyes. Its shape changes D:constantly as it cuts through the air towards you. It is like a beacon, D:waking monsters from their slumber. N:601:Ancient blue dragon G:D:b I:120:28d23:20:40:80 W:55:1:0:78000 O:50:50:0 B:CLAW:HURT:4d8 B:CLAW:HURT:4d8 B:BITE:ELEC:5d8 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 F:DROP_4D2 | DROP_CORPSE | LITE_2 F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | F:EVIL | DRAGON | IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_9 | S:BLIND | CONF | SCARE | S:BR_ELEC D:A huge draconic form. Lightning crackles along its length. N:602:Ancient bronze dragon G:D:U I:120:29d23:20:45:200 W:57:2:0:102000 O:50:50:0 B:CLAW:HURT:4d8 B:CLAW:HURT:4d8 B:BITE:CONFUSE:5d10 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 F:DROP_4D2 | CAN_FLY | F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_6 | S:BLIND | CONF | SCARE | S:BR_CONF D:A huge draconic form enveloped in a cascade of colour. N:603:Beholder G:e:g I:120:28d40:30:40:10 W:56:4:0:74000 O:0:0:0 B:GAZE:EXP_20:2d4 B:GAZE:PARALYZE:2d4 B:GAZE:LOSE_INT:2d6 B:GAZE:UN_POWER:2d6 F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | CAN_FLY | F:BASH_DOOR | DROP_CORPSE | DUN_TOWER | DUN_CAVERN | DUN_LAIR F:EVIL | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | SLOW | CONF | SCARE | DRAIN_MANA | MIND_BLAST | FORGET | S:DARKNESS | BO_ACID | BO_FIRE | BO_COLD D:A disembodied eye, surrounded by twelve smaller eyes on stalks. N:604:Emperor wight G:W:y I:120:30d8:20:40:10 W:60:2:0:86000 O:0:40:60 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:TOUCH:EXP_80 B:TOUCH:EXP_80 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE F:ONLY_ITEM | DROP_2D2 | CAN_FLY | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_6 | S:HOLD | SCARE | CAUSE_3 | BO_NETH D:Your life force is torn from your body as this powerful unearthly being D:approaches. N:605:Seraph G:A:w I:120:27d13:30:34:255 W:53:6:0:28000 O:0:50:50 B:HIT:HURT:4d6 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:4d6 F:DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | SMART | NO_FEAR | GOOD | CAN_FLY | F:ONLY_ITEM | DROP_2D2 | REFLECTING | RES_TELE | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_ACID | IM_FIRE | IM_POIS | LITE_2 F:NO_CONF | NO_SLEEP S:1_IN_11 | S:HEAL | HASTE | TELE_AWAY | CONF | BO_MANA | BO_PLAS | S:S_MONSTERS | S_ANGEL D:It is an angel, fast and strong. You are stunned by its extreme holiness D:and try to resist all desires to obey it. N:606:Loge, Spirit of Fire G:E:R I:120:27d39:12:25:50 W:54:3:0:49000 O:0:0:0 B:HIT:FIRE:6d6 B:HIT:FIRE:6d6 B:HIT:FIRE:6d6 B:HIT:FIRE:6d6 F:UNIQUE | DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | F:EMPTY_MIND | CAN_SPEAK | MALE | AURA_FIRE | F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | F:EVIL | IM_FIRE | NONLIVING | LITE_2 F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_4 | S:BO_PLAS | BA_FIRE D:A towering fire elemental, Loge burns everything beyond recognition. N:607:Black wraith G:W:D I:120:30d10:20:30:10 W:59:2:0:45000 O:50:0:50 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:TOUCH:EXP_40 B:TOUCH:EXP_40 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DUN_GRAVE F:ONLY_ITEM | DROP_2D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_7 | S:BLIND | HOLD | SCARE | CAUSE_3 | BO_NETH D:A figure that seems made of void, its strangely human shape is cloaked in D:shadow. It reaches out at you. N:608:Nightgaunt G:U:D I:110:30d6:20:25:80 W:59:20:0:45000 O:50:0:50 B:STING:LOSE_STR:1d5 B:TOUCH:PARALYZE:3d4 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE F:ONLY_ITEM | DROP_60 | F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY | F:EVIL | DEMON | F:IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_7 | S:BLIND | CONF | BO_FIRE | ELDRITCH_HORROR | D:"Shocking and uncouth black things with smooth, oily, whale-like D:surfaces, unpleasant horns that curved inward toward each other, D:bat wings whose beating made no sound, ugly prehensile paws, and D:barbed tails that lashed needlessly and disquietingly. And worst D:of all, they never spoke or laughed, and never smiled because they D:had no faces at all to smile with, but only to a suggestive D:blankness where a face ought to be." N:609:Baron of hell G:U:U I:110:28d49:10:65:40 W:56:3:0:57000 O:0:0:0 B:CLAW:HURT:11d2 B:CLAW:HURT:11d2 B:CLAW:HURT:22d1 F:DUN_HELL F:IM_POIS | OPEN_DOOR | BASH_DOOR | MALE | RES_PLAS | NONLIVING | F:IM_FIRE | NO_CONF | NO_SLEEP | EVIL | DEMON | FORCE_MAXHP | RES_TELE S:1_IN_2 | S:BO_PLAS D:A minor demon lord with a goat's head, tough to kill. N:610:Scylla G:M:G I:125:29d37:20:45:20 W:58:2:0:130000 O:90:0:10 B:BITE:POISON:10d3 B:BITE:POISON:10d3 B:BITE:POISON:10d3 B:BITE:POISON:10d3 F:DUN_DARKWATER | DUN_LAIR | DUN_CAVERN F:FORCE_SLEEP | UNIQUE | CAN_SWIM | FORCE_MAXHP | F:ONLY_GOLD | DROP_1D2 | DROP_2D2 | ESCORT | DROP_CORPSE F:BASH_DOOR | MOVE_BODY | EVIL | WILD_SHORE | F:ANIMAL | IM_POIS S:1_IN_5 | S:BR_POIS | BR_FIRE | SCARE | CONF D:A strange reptilian hybrid with multiple heads dripping venom. N:611:Monastic lich G:L:u I:120:31d28:30:40:30 W:62:2:0:155000 O:0:50:50 B:KICK:HURT:24d1 B:HIT:HURT:24d1 B:CLAW:EXP_80:4d2 B:CLAW:LOSE_DEX:4d2 F:DUN_TEMPLE | DUN_GRAVE F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 | S:DRAIN_MANA | BRAIN_SMASH | ANIM_DEAD D:A skeletal form wrapped in priestly robes. Before its un-death, it D:was a monk in an evil order... N:612:Nether wraith G:W:R I:120:29d10:20:30:10 W:58:2:0:130000 O:50:0:50 B:HIT:HURT:1d12 B:HIT:HURT:1d12 B:TOUCH:EXP_80 B:TOUCH:EXP_80 F:DUN_GRAVE | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:ONLY_ITEM | DROP_2D2 | F:INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_6 | S:BLIND | SCARE | CAUSE_3 | MIND_BLAST | DARKNESS | BO_NETH D:A form that hurts the eye, death permeates the air around it. As it nears D:you, a coldness saps your soul. N:613:Fire vampire G:V:r I:120:30d16:30:66:0 W:60:4:0:79000 O:0:0:0 B:TOUCH:FIRE:1d10 B:TOUCH:LOSE_WIS:1d10 B:TOUCH:FIRE:1d10 B:TOUCH:LOSE_INT:1d10 F:DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | CAN_FLY | EVIL | F:SMART | EMPTY_MIND | AURA_FIRE | RES_PLAS | KILL_ITEM | F:PASS_WALL | IM_FIRE | IM_POIS | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | NO_STUN S:1_IN_4 | S:BLINK | TPORT | CONF | BA_FIRE | DRAIN_MANA | HASTE | BO_PLAS D:It is a living entity of fire. Fire springs up wherever this D:creature touches. N:614:7-headed hydra G:M:G I:120:25d28:20:45:20 W:50:2:0:56000 O:100:0:0 B:BITE:POISON:3d9 B:BITE:POISON:3d9 B:BITE:POISON:3d9 B:SPIT:BLIND:1d2 F:FORCE_SLEEP | DUN_DARKWATER | DUN_LAIR | WILD_SHORE | WILD_SWAMP1 | WILD_SWAMP2 F:ONLY_GOLD | DROP_1D2 | DROP_2D2 | CAN_SWIM | DROP_CORPSE F:BASH_DOOR | MOVE_BODY | F:ANIMAL | IM_POIS S:1_IN_5 | S:SCARE | BA_POIS | S:BR_POIS D:A strange reptilian hybrid with seven heads dripping venom. N:615:Moire, Queen of Rebma G:E:b I:120:29d49:12:40:50 W:57:3:0:86000 O:30:30:40 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:UNIQUE | FEMALE | DROP_2D2 | ONLY_ITEM | DROP_GOOD | F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | DROP_CORPSE | F:CAN_SPEAK | CAN_SWIM | WILD_OCEAN | F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | F:EVIL | IM_POIS | DUN_DARKWATER | WILD_SHORE | WILD_SWAMP1 | WILD_SWAMP2 F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_4 | S:BO_ICEE | BO_WATE | BA_COLD | BA_WATE D:The Queen of the Sea where the Pattern is reflected is a cunning D:diplomat and a dangerous foe. "A woman sat upon the throne... D:and her hair was green, though streaked with silver, and her eyes D:were round of moons of jade and her brows rose like the wings of D:olive gulls. Her mouth was small, her chin was small; her cheeks D:were high and wide and rounded. A circlet of white gold crossed D:her brow and there was a crystal necklace about her neck. At its D:tip there flashed a sapphire between her sweet bare breasts, whose D:nipples were also bare green. She wore scaled trunks of blue and D:a silver belt, and she held a scepter of pink coral in her hand D:and had a ring upon every finger, and each ring had a stone of D:a different blue within it. She did not smile..." N:616:Kavlax the Many-Headed G:d:v I:120:31d26:20:40:30 W:61:3:0:170000 O:50:50:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 F:DUN_LAIR | DUN_CAVERN F:UNIQUE | MALE | ATTR_MULTI | CAN_SPEAK | ATTR_ANY | F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | RES_NEXU | F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY | LITE_2 F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | F:NO_CONF | NO_SLEEP S:1_IN_4 | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_SOUN | BR_CONF | S:BR_SHAR | BR_GRAV | BR_NEXU D:A large dragon with a selection of heads, all shouting and arguing as they D:look for prey, but each with its own deadly breath weapon. N:617:Ancient white dragon G:D:w I:120:28d23:20:40:80 W:55:1:0:160000 O:50:50:0 B:CLAW:HURT:4d9 B:CLAW:HURT:4d9 B:BITE:COLD:5d12 F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | DUN_CAVERN F:WILD_WASTE2 | WILD_MOUNT2 F:DROP_4D2 | DROP_CORPSE F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_9 | S:BLIND | CONF | SCARE | S:BR_COLD D:A huge draconic form. Frost covers it from head to tail. N:618:Ancient green dragon G:D:g I:120:28d23:20:40:80 W:55:1:0:135000 O:50:50:0 B:CLAW:HURT:4d8 B:CLAW:HURT:4d8 B:BITE:HURT:5d10 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DUN_LAIR | DUN_CAVERN | WILD_SWAMP2 F:DROP_4D2 | CAN_FLY | F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_9 | S:BLIND | CONF | SCARE | S:BR_POIS D:A huge draconic form enveloped in clouds of poisonous vapour. N:619:Chthonian G:w:D I:120:27d23:20:45:20 W:53:2:0:102000 O:100:0:0 B:CRUSH:SHATTER:8d6 B:TOUCH:LOSE_CON:1d2 B:TOUCH:LOSE_CON:1d2 F:DUN_HORROR F:IM_FIRE | RES_PLAS | IM_COLD | IM_POIS | RES_TELE F:KILL_WALL | ONLY_GOLD | DROP_1D2 | DROP_2D2 | CAN_SWIM | DROP_CORPSE F:EVIL | FORCE_MAXHP S:1_IN_5 | S:SCARE | CONF | HOLD | S_DEMON | S:MIND_BLAST | HEAL | HASTE | FORGET | BRAIN_SMASH | ELDRITCH_HORROR | D:"Flowing tentacles and a pulpy gray-black elongated sack of a body... D:no distinguishing features at all other than the reaching, groping D:tentacles. Or was there -- yes! -- a lump in the upper body of D:the thing... a container of sorts for the brain, ganglia, or D:whichever diseased organ governed this horror's loathsome life!" N:620:Eldrak G:T:r I:110:30d17:20:45:50 W:59:3:0:30000 O:0:0:0 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:FORCE_MAXHP | DUN_LAIR | DUN_CAVERN F:DROP_60 | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_SHORE | WILD_FOREST2 | F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP | DROP_CORPSE D:A massive troll, larger and stronger than many men together. N:621:Ettin G:T:y I:110:30d30:20:55:30 W:59:3:0:27000 O:0:100:0 B:HIT:HURT:3d6 B:HIT:HURT:3d6 B:HIT:HURT:3d6 F:FORCE_MAXHP | F:ONLY_ITEM | DROP_60 | DUN_CAVERN | WILD_MOUNT2 | WILD_FOREST2 F:WILD_SWAMP1 | WILD_SWAMP2 F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP D:A massive troll of huge strength. Ettins are stupid but violent. N:622:Night mare G:q:v I:120:27d34:30:30:0 W:54:3:0:39000 O:100:0:0 B:BITE:EXP_80:2d6 B:HIT:HURT:3d8 B:HIT:HURT:3d8 B:HIT:CONFUSE:6d6 F:FORCE_MAXHP | DUN_GRAVE F:ONLY_GOLD | DROP_2D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP D:A fearsome skeletal horse with glowing eyes, that watch you with little D:more than a hatred of all that lives. Flying hooves don't touch the D:ground... N:623:Vampire lord G:V:b I:120:29d35:20:28:10 W:58:3:0:56000 O:0:70:30 B:HIT:HURT:1d6 B:HIT:HURT:1d6 B:BITE:EXP_VAMP:2d6 B:BITE:EXP_VAMP:2d6 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DUN_GRAVE | DUN_CAVERN F:DROP_2D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | RES_TELE F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | F:NO_CONF | NO_SLEEP S:1_IN_7 | S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA | S:BRAIN_SMASH | DARKNESS | BO_NETH D:A foul wind chills your bones as this ghastly figure approaches. N:624:Ancient black dragon G:D:D I:120:29d23:20:40:70 W:57:1:0:115000 O:50:50:0 B:CLAW:HURT:4d9 B:CLAW:HURT:4d9 B:BITE:ACID:5d10 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DUN_LAIR | DUN_CAVERN | WILD_WASTE2 F:DROP_4D2 | DROP_CORPSE F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | IM_ACID | NO_CONF | NO_SLEEP S:1_IN_9 | S:BLIND | CONF | SCARE | S:BR_ACID D:A huge draconic form. Pools of acid melt the floor around it. N:625:Weird fume G:#:v I:120:29d9:100:20:0 W:58:2:0:42000 O:0:0:0 B:ENGULF:CONFUSE:8d4 B:ENGULF:CONFUSE:8d4 F:FORCE_SLEEP | DUN_TOWER F:RAND_50 | RAND_25 | RES_NEXU | AURA_ELEC | IM_FIRE | IM_ELEC | F:EMPTY_MIND | BASH_DOOR | POWERFUL | F:CAN_FLY | ATTR_MULTI | ATTR_ANY | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_6 | S:BR_CHAO | BR_NEXU | BR_NUKE D:A swirling spiral of mist, constantly changing its appearance. N:626:Spawn of Ubbo-Sathla G:j:v I:120:30d7:100:20:50 W:60:5:0:15500 O:0:0:0 B:CRUSH:ACID:8d4 B:CRUSH:ACID:8d4 F:DUN_HORROR F:FORCE_SLEEP | ATTR_MULTI | ATTR_ANY | MULTIPLY | EVIL | F:RAND_25 | RES_NEXU | AURA_ELEC | IM_FIRE | IM_ELEC | F:EMPTY_MIND | BASH_DOOR | POWERFUL | KILL_BODY | F:CAN_SWIM | NO_CONF | NO_SLEEP | D:Weird, jelly like creatures. No two look the same. N:627:Fat Man G:{:D I:120:31d5:10:40:12 W:62:2:0:31000 O:0:0:0 B:EXPLODE:SHATTER:200d2 F:DUN_TOWER | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | REFLECTING | F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | SILLY | F:IM_FIRE | IM_COLD | IM_POIS | F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | RES_TELE D:A shining machine of death and destruction. N:628:Malekith the Accursed G:h:v I:125:29d61:20:35:10 W:57:2:0:230000 O:50:0:50 B:HIT:HURT:12d9 B:HIT:HURT:12d9 F:DUN_TOWER F:MALE | REGENERATE | UNIQUE | CAN_SPEAK | F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL | HURT_LITE | NO_CONF | NO_SLEEP | NO_STUN S:1_IN_2 | S:HEAL | TELE_TO | BLIND | CONF | CAUSE_3 | DARKNESS | FORGET | HOLD | S:S_MONSTER | S_MONSTERS | S_DEMON | TPORT | BA_NETH | MIND_BLAST | S:S_KIN D:One of the oldest and most powerful dark elves, Malekith is a master D:sorcerer devoted to evil. The left side of his face is pale, the other D:one is dark. His hair is long and white. N:629:Morgenstern, Julian's steed G:q:W I:130:27d78:20:50:50 W:53:3:0:45000 O:0:0:0 B:KICK:HURT:4d4 B:KICK:HURT:5d5 B:BITE:HURT:6d6 F:FORCE_MAXHP | UNIQUE | ANIMAL | DUN_LAIR | DUN_CAVERN | CAN_SWIM F:REGENERATE | BASH_DOOR | IM_COLD | IM_ELEC | F:IM_POIS | NO_CONF | NO_SLEEP | NO_FEAR | DROP_CORPSE D:Morgenstern is tougher and faster than any other horse: "Morgenstern D:was six hands higher than any other horse I had ever seen, and his D:eyes were the dead color of a Weimaraner dog's and his coat was all D:gray and his hooves looked like polished steel. He raced along like D:the wind..." N:630:Spirit troll G:T:v I:110:30d20:20:36:5 W:60:3:0:33000 O:0:0:0 B:HIT:HURT:3d6 B:HIT:HURT:3d5 B:HIT:HURT:3d5 F:FORCE_MAXHP | DUN_PLANAR | DUN_LAIR F:INVISIBLE | PASS_WALL | CAN_FLY | F:EVIL | TROLL | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP D:A weird troll from the elemental planes. N:631:War troll G:T:g I:120:31d12:20:50:50 W:61:3:0:28000 O:0:100:0 B:HIT:HURT:4d4 B:HIT:HURT:5d5 B:HIT:HURT:6d6 F:FORCE_MAXHP | F:DROP_60 | REGENERATE | FRIENDS | DUN_LAIR | DUN_CAVERN | WILD_WASTE1 | WILD_WASTE2 F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP | NO_FEAR D:A massive troll, equipped with a scimitar and heavy armour. N:632:Disenchanter worm mass G:w:v I:100:24d2:7:5:10 W:54:3:0:2500 O:0:0:0 B:CRAWL:UN_BONUS:1d4 F:DUN_CAVERN F:RAND_50 | RES_DISE | ATTR_MULTI | CAN_SWIM | F:STUPID | WEIRD_MIND | MULTIPLY | BASH_DOOR | LITE_1 F:ANIMAL | HURT_LITE | NO_FEAR D:It is a strange mass of squirming worms. Magical energy crackles D:around its disgusting form. N:633:Rotting quylthulg G:Q:U I:120:29d5:20:1:0 W:57:1:0:46000 O:0:0:0 F:DUN_TEMPLE F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | F:INVISIBLE | EMPTY_MIND | F:ANIMAL | EVIL | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK | TPORT | S:S_UNDEAD D:It is a pulsing flesh mound that reeks of death and putrefaction. N:634:Lesser titan G:P:y I:120:29d21:30:35:15 W:57:3:0:81000 O:0:100:0 B:HIT:CONFUSE:6d6 B:HIT:CONFUSE:6d6 B:HIT:CONFUSE:6d6 B:HIT:CONFUSE:6d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_TEMPLE | DUN_CAVERN F:DROP_2D2 | DROP_SKELETON | DROP_CORPSE F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT | MALE S:1_IN_3 | S:HEAL | TELE_TO | SCARE | S:S_MONSTERS D:It is a humanoid figure thirty feet tall that gives off an aura of power D:and hate. N:635:9-headed hydra G:M:o I:120:29d29:20:48:20 W:57:2:0:90000 O:100:0:0 B:BITE:FIRE:3d6 B:BITE:FIRE:3d6 B:BITE:FIRE:3d6 B:BITE:FIRE:3d6 F:FORCE_SLEEP | DUN_LAIR | DUN_DARKWATER | WILD_SHORE | WILD_SWAMP1 | WILD_SWAMP2 F:ONLY_GOLD | DROP_4D2 | CAN_SWIM | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_CORPSE F:ANIMAL | IM_FIRE S:1_IN_4 | S:SCARE | BO_FIRE | S:BR_FIRE D:A strange reptilian hybrid with nine smouldering heads. N:636:Enchantress G:p:y I:130:29d11:20:25:10 W:57:4:0:103000 O:0:0:100 B:HIT:HURT:2d6 B:HIT:HURT:2d6 B:HIT:HURT:2d8 F:FEMALE | DUN_TEMPLE | WILD_GRASS | WILD_FOREST1 F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | S:S_DRAGON D:This elusive female spellcaster has a special affinity for dragons, whom D:she rarely fights without. N:637:Archpriest G:p:b I:120:30d12:20:30:10 W:60:2:0:68000 O:0:0:100 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d5 F:MALE | DUN_TEMPLE | WILD_GRASS | WILD_FOREST1 F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | LITE_2 F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP S:1_IN_2 | S:HEAL | BLIND | HOLD | CONF | CAUSE_3 | ANIM_DEAD S:S_MONSTER | S_UNDEAD D:An evil priest, dressed all in black. Deadly spells hit you at an D:alarming rate as his black spiked mace rains down blow after blow on your D:pitiful frame. N:638:Sorcerer G:p:o I:130:32d10:20:25:10 W:64:2:0:180000 O:0:0:100 B:HIT:HURT:2d8 B:HIT:HURT:2d8 B:HIT:HURT:2d8 F:MALE | DUN_TOWER | WILD_GRASS | WILD_WASTE1 F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | LITE_2 F:OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLINK | TELE_TO | BLIND | CONF | CAUSE_3 | TRAPS | S:BO_ACID | BA_FIRE | BA_COLD | S:S_MONSTER | S_UNDEAD | S_DRAGON D:A human figure in robes, he moves with magically improved speed, and his D:hands are ablur with spell casting. N:639:Xaren G:X:y I:120:28d8:20:35:10 W:56:1:0:32000 O:0:0:0 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d4 F:FORCE_MAXHP | DUN_PLANAR F:EMPTY_MIND | COLD_BLOOD | F:KILL_ITEM | PASS_WALL | F:IM_FIRE | IM_COLD | IM_POIS | F:HURT_ROCK | NO_CONF | NO_SLEEP D:It is a tougher relative of the Xorn. Its hide glitters with metal ores. N:640:Jubjub bird G:B:u I:110:29d21:20:30:10 W:58:3:0:30000 O:0:0:0 B:CRUSH:HURT:8d12 B:CRUSH:HURT:8d12 B:HIT:ELEC:12d12 F:BASH_DOOR | CAN_FLY | WILD_MOUNT1 | WILD_FOREST1 | F:ANIMAL | IM_ELEC | DROP_CORPSE D:A vast legendary bird, its iron talons rake the most impenetrable of D:surfaces and its screech echoes through the many winding dungeon corridors. N:641:Minotaur G:H:U I:130:30d20:13:25:10 W:59:2:0:30000 O:0:0:0 B:BUTT:HURT:4d6 B:BUTT:HURT:4d6 B:BUTT:HURT:2d6 B:BUTT:HURT:2d6 F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:EVIL | DUN_CAVERN | DUN_MINE | WILD_MOUNT1 | WILD_MOUNT2 D:It is a cross between a human and a bull. N:642:Jasra, Brand's Mistress G:n:v I:120:31d55:30:50:5 W:61:3:0:490000 O:40:20:40 B:GAZE:EXP_80 B:GAZE:PARALYZE B:HIT:HURT:8d6 B:HIT:HURT:8d6 F:UNIQUE | FEMALE | CAN_SPEAK | DROP_CORPSE | DUN_DARKWATER F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | LITE_2 F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL | IM_ACID | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:HOLD | SCARE | CAUSE_3 | BO_FIRE | BO_PLAS | BA_ACID | S:S_HYDRA D:Brand's one-time mistress. Her face could sink a thousand D:ships, but Brand is not too selective. N:643:Death drake G:D:u I:120:31d20:25:45:80 W:61:2:0:240000 O:50:50:0 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:BITE:EXP_80:3d6 B:BITE:EXP_80:3d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | WILD_WASTE1 | WILD_WASTE2 F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | RES_TELE F:INVISIBLE | TAKE_ITEM | CAN_FLY | F:PASS_WALL | POWERFUL | MOVE_BODY | RES_NETH | F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | S:BR_NETH D:It is a dragon-like form wrapped in darkness. You cannot make out its D:true form but you sense its evil. N:644:Ancient red dragon G:D:r I:120:30d24:20:45:70 W:59:1:0:140000 O:50:50:0 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:BITE:FIRE:5d14 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 F:DROP_4D2 | CAN_FLY | LITE_2 F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_6 | S:BLIND | CONF | SCARE | S:BR_FIRE D:A huge draconic form. Wisps of smoke steam from its nostrils and the D:extreme heat surrounding it makes you gasp for breath. N:645:Ancient gold dragon G:D:y I:120:30d23:20:45:200 W:59:2:0:125000 O:50:50:0 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:BITE:HURT:5d14 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:DROP_4D2 | CAN_FLY | DUN_LAIR | DUN_CAVERN | WILD_FOREST2 F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_6 | S:BLIND | CONF | SCARE | S:BR_SOUN D:A huge draconic form wreathed in a nimbus of light. N:646:Great crystal drake G:D:B I:120:30d28:25:45:30 W:59:2:0:95000 O:50:50:0 B:CLAW:HURT:4d9 B:CLAW:HURT:4d9 B:BITE:HURT:5d12 F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | DROP_CORPSE | DUN_CAVERN F:ONLY_ITEM | DROP_4D2 | CAN_FLY | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | CONF | SCARE | S:BR_SHAR D:A huge crystalline dragon. Its claws could cut you to shreds and its D:teeth are razor sharp. Strange colours ripple through it as it moves in D:the light. N:647:Wyrd sister G:p:y I:125:29d15:20:30:10 W:57:4:0:36000 O:10:0:90 B:CLAW:HURT:2d6 B:CLAW:HURT:2d6 B:CLAW:HURT:2d8 F:FEMALE | F:FORCE_SLEEP | FORCE_MAXHP | DUN_TEMPLE | DUN_LAIR F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON F:OPEN_DOOR | BASH_DOOR | F:EVIL | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | S:S_DEMON | CONF | SCARE | DARKNESS D:This old crone is rumoured to be a witch of chaos... but you don't D:really believe in witches, do you? N:648:Clubber demon G:U:s I:110:31d10:20:25:80 W:61:2:0:30000 O:0:100:0 B:HIT:HURT:3d4 B:HIT:HURT:8d12 B:HIT:HURT:8d12 F:FORCE_SLEEP | FORCE_MAXHP | DUN_HELL F:FRIENDS | F:ONLY_ITEM | DROP_60 | F:OPEN_DOOR | BASH_DOOR | POWERFUL | NONLIVING | F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_8 | S:BLIND | CONF D:It is a demon swinging wildly with two clubs. N:649:Death quasit G:u:w I:130:32d9:20:32:0 W:63:3:0:155000 O:0:50:50 B:BITE:LOSE_DEX:3d6 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 F:FORCE_SLEEP | FORCE_MAXHP | DUN_HELL F:ONLY_ITEM | DROP_2D2 | NONLIVING | F:SMART | INVISIBLE | PASS_WALL | CAN_FLY | F:EVIL | DEMON | IM_FIRE | IM_POIS | RES_TELE F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_10 | S:BLIND | CONF | SCARE | CAUSE_3 | FORGET | S:S_DEMON D:It is a demon of small stature, but its armoured frame moves with D:lightning speed and its powers make it a tornado of death and destruction. N:650:Giganto the Gargantuan G:l:s I:110:26d58:20:35:30 W:52:6:0:42000 O:60:40:0 B:CRUSH:HURT:33d2 B:CRUSH:HURT:33d2 B:CRUSH:HURT:33d2 F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | IM_FIRE | DUN_CAVERN F:DROP_2D2 | UNIQUE | WEIRD_MIND | F:BASH_DOOR | SMART | EVIL | IM_COLD | DROP_CORPSE F:NO_CONF | NO_SLEEP | RES_WATE | WILD_OCEAN | S:1_IN_9 | S:BR_NUKE | BA_WATE D:A gargantuan mutant whale, who has grown legs that enable it to walk D:on dry land as well. N:651:Strygalldwir G:U:W I:120:28d33:90:30:10 W:56:3:0:124000 O:20:0:80 B:CLAW:HURT:5d5 B:CLAW:HURT:5d5 B:HIT:LOSE_STR:4d4 B:TOUCH:EXP_80:8d1 F:UNIQUE | CAN_SPEAK | DUN_HORROR | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:INVISIBLE | COLD_BLOOD | PASS_WALL | MOVE_BODY | NONLIVING | F:OPEN_DOOR | BASH_DOOR | IM_POIS | IM_COLD | DEMON | EVIL S:1_IN_3 | S:CAUSE_3 | HOLD | SCARE | BLIND | BO_ACID | S_DEMON | S:FORGET | BO_NETH | MIND_BLAST | DARKNESS D:A demon from the Courts of Chaos. According to Corwin, D:'it was well over six feet in height, with great branches of antlers D:growing out of its forehead. Nude, its flesh was a uniform ash-gray D:in color. It appeared to be sexless, and it had gray, leathery wings D:extending far out behind it'. N:652:Fallen angel G:A:D I:130:35d70:30:70:255 W:70:6:0:600000 O:0:50:50 B:GAZE:EXP_VAMP:4d4 B:GAZE:EXP_VAMP:4d4 B:HIT:HURT:8d6 B:HIT:HURT:8d6 F:FORCE_SLEEP | DUN_HELL F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | NO_FEAR | EVIL | REFLECTING | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_FIRE | IM_COLD | IM_POIS | RES_TELE | RES_NETH | F:CAN_FLY | LITE_2 S:1_IN_3 | S:TELE_TO | BLIND | SCARE | CAUSE_2 | CAUSE_4 | BO_MANA | S:S_DEMON | BO_NETH | INVULNER D:An angelic being who was mighty once but dared defy the Omnipotent D:to arms. N:653:Giant headless G:H:u I:110:27d20:20:25:40 W:53:2:0:10500 O:0:100:0 B:HIT:HURT:4d8 B:HIT:HURT:4d8 B:HIT:HURT:4d8 F:FRIENDS | DROP_60 | OPEN_DOOR | BASH_DOOR | F:DROP_SKELETON | DROP_CORPSE | IM_POIS | DUN_TOWER | WILD_WASTE1 F:EVIL S:1_IN_6 S:SCARE | BA_NUKE | BLIND D:Giant headless humanoid abominations created by a magical mutation. N:654:Judge Fire G:s:r I:120:33d39:90:35:10 W:66:3:0:620000 O:0:50:50 B:HIT:FIRE:5d5 B:HIT:FIRE:5d5 B:GAZE:EXP_80 B:WAIL:TERRIFY F:UNIQUE | MALE | CAN_SPEAK | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | POWERFUL | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | LITE_1 F:EVIL | UNDEAD | HURT_COLD | IM_POIS | IM_FIRE | RES_PLAS | F:NO_CONF | NO_SLEEP S:1_IN_3 | S:CAUSE_3 | BO_FIRE | BA_FIRE | BR_FIRE | BO_PLAS S:DARKNESS | S_MONSTER | S_DEMON | S_UNDEAD | TPORT | BLINK | SCARE D:One of the Dark Judges, he has come to punish your crime of living. D:He looks like a skeleton enveloped in flames. N:655:Ubbo-Sathla, the Unbegotten Source G:j:W I:120:31d46:90:40:10 W:61:30:0:390000 O:30:50:0 B:CRUSH:ACID:5d5 B:HIT:POISON:5d5 B:CRUSH:ACID:5d5 B:HIT:POISON:5d5 F:DUN_HORROR F:UNIQUE | CAN_SPEAK | NO_STUN | NO_SLEEP | NO_CONF | F:FORCE_SLEEP | FORCE_MAXHP | ESCORT | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | REGENERATE | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | KILL_BODY | F:EVIL | HURT_FIRE | IM_COLD | IM_POIS | CAN_SWIM S:1_IN_10 S:ELDRITCH_HORROR D:"There, in the gray beginning of Earth, the formless mass that was D:Ubbo-Sathla reposed amid the slime and the vapors. Headless, D:without organs or members..." N:656:Judge Mortis G:z:G I:120:32d40:90:35:10 W:64:3:0:490000 O:0:80:0 B:HIT:POISON:5d5 B:HIT:DISEASE:5d5 B:TOUCH:LOSE_ALL B:TOUCH:EXP_80 F:UNIQUE | MALE | CAN_SPEAK | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL | UNDEAD | HURT_FIRE | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | SCARE | CAUSE_3 | BO_ACID | BO_NETH | BR_POIS | S:BR_NETH | BLINK | TPORT | ANIM_DEAD | S:BO_POIS | S_UNDEAD D:Another Dark Judge, he is a rotting humanoid with a cow's skull as D:his head. N:657:Dark elven sorceror G:h:v I:130:31d16:20:25:10 W:62:2:0:140000 O:0:0:100 B:HIT:HURT:2d8 B:HIT:HURT:2d8 B:HIT:HURT:2d8 F:MALE | DUN_TOWER | WILD_FOREST2 F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_2 | S:HEAL | BLINK | TELE_TO | BLIND | CONF | CAUSE_3 | DARKNESS | S:BO_ACID | BA_FIRE | BA_COLD | ANIM_DEAD S:S_MONSTER | S_UNDEAD | S_DEMON | MISSILE D:A dark elven figure, dressed in deepest black. Power seems to crackle D:from his slender frame. N:658:Master lich G:L:W I:120:30d37:20:32:50 W:60:2:0:205000 O:10:50:30 B:TOUCH:EXP_80 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:2d12 B:TOUCH:LOSE_DEX:2d12 F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE | DUN_GRAVE F:ONLY_ITEM | DROP_2D2 | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 | S:DRAIN_MANA | BRAIN_SMASH | S:S_UNDEAD D:A skeletal form wrapped in robes. Powerful magic crackles along its bony D:fingers. N:659:Byakhee G:U:D I:110:31d11:20:20:80 W:61:30:0:51000 O:0:50:50 B:CLAW:LOSE_STR:3d4 B:BITE:EXP_20:3d4 F:FORCE_SLEEP | FORCE_MAXHP | FRIENDS | DUN_HORROR F:ONLY_ITEM | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY | F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_9 | S:BO_FIRE | S:S_DEMON | CONF | ELDRITCH_HORROR | D:"There flapped rhythmically a horde of tame, trained, hybrid D:winged things... not altogether crows, nor moles, nor buzzards, D:nor ants, nor decomposed human beings, but something I cannot D:and must not recall." N:660:Rinaldo, son of Brand G:p:w I:120:30d38:20:60:40 W:59:3:0:245000 O:0:0:100 B:HIT:HURT:8d6 B:HIT:HURT:8d6 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 F:DUN_CITY | DUN_TOWER F:UNIQUE | MALE | CAN_SPEAK | AMBERITE | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | LITE_2 F:SMART | OPEN_DOOR | BASH_DOOR | CAN_SWIM F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_2 | S:CAUSE_3 | BO_WATE | BO_MANA | BA_CHAO D:The madman's son is a Logrus Master, almost as great a D:menace as his father. N:661:Archon G:A:y I:130:34d73:30:70:255 W:68:6:0:560000 O:0:100:0 B:GAZE:TERRIFY:4d4 B:GAZE:TERRIFY:4d4 B:HIT:HURT:8d6 B:HIT:HURT:8d6 F:FORCE_SLEEP | DUN_PLANAR F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | NO_FEAR | GOOD | REFLECTING | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS | RES_TELE | CAN_FLY | LITE_2 S:1_IN_3 | S:TELE_TO | BLIND | SCARE | CAUSE_2 | CAUSE_4 | BO_MANA | S:S_ANGEL | INVULNER D:Never a more heavenly being have you seen. The very holiness of its D:presence makes you deeply respect it. Few creatures can match the powers D:of an Archon; fewer still live to tell the tale after attacking one. N:662:Formless spawn of Tsathoggua G:U:D I:110:31d10:20:25:80 W:61:20:0:59000 O:40:50:0 B:HIT:ACID:2d4 B:HIT:ACID:2d4 B:CRUSH:HURT:3d4 B:BITE:ACID:6d6 F:DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | NONLIVING | F:ONLY_ITEM | DROP_90 | REGENERATE | RES_TELE F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_SWIM | F:EVIL | DEMON | NO_CONF | NO_SLEEP | HURT_FIRE | IM_POIS S:1_IN_9 | S:BO_FIRE | BO_ACID | S:S_DEMON | MIND_BLAST | DARKNESS | ELDRITCH_HORROR | D:"...living things that oozed along stone channels... D:But they were not toads like Tsathoggua himself. Far worse -- D:they were amorphous lumps of viscous black slime that took D:temporary shapes for various purposes." N:663:Hunting horror G:U:D I:110:32d13:20:45:80 W:63:20:0:64000 O:50:40:0 B:BITE:LOSE_DEX:1d3 B:BITE:POISON:1d3 B:CRUSH:HURT:9d4 F:DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_60 | F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY | F:EVIL | DEMON | HURT_LITE | IM_POIS | F:IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_9 | S:BLIND | CONF | S_DEMON | BR_DARK | ELDRITCH_HORROR D:"And in the air there were great viperine creatures, D:which had curiously distorted heads, and grotesquely great D:clawed appendages, supporting themselves with ease by the aid D:of black rubbery wings of singularly monstrous dimensions." N:664:Undead beholder G:e:s I:120:29d66:30:50:10 W:57:4:0:101000 O:0:0:0 B:GAZE:EXP_40 B:GAZE:PARALYZE B:GAZE:LOSE_INT:2d6 B:GAZE:UN_POWER:2d6 F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | DUN_GRAVE | DUN_TOWER | DUN_CAVERN F:COLD_BLOOD | BASH_DOOR | F:EVIL | UNDEAD | CAN_FLY | F:IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_2 | S:SLOW | CAUSE_4 | DRAIN_MANA | MIND_BLAST | BRAIN_SMASH | FORGET | S:BO_MANA | ANIM_DEAD S:S_UNDEAD D:A disembodied eye, floating in the air. Black nether storms rage around D:its bloodshot pupil and light seems to bend as it sucks its power from the D:very air around it. Your soul chills as it drains your vitality for its D:evil enchantments. N:665:Shadow demon G:G:v I:120:30d5:30:15:20 W:60:3:0:29000 O:90:10:0 B:TOUCH:EXP_80 B:TOUCH:EXP_40 B:CLAW:LOSE_INT:1d10 B:CLAW:LOSE_WIS:1d10 F:FORCE_SLEEP | CAN_FLY | DUN_HELL | DUN_GRAVE F:ONLY_ITEM | DROP_90 | POWERFUL | REGENERATE | HURT_LITE | F:INVISIBLE | COLD_BLOOD | PASS_WALL | FRIENDS | RES_NETH | F:EVIL | DEMON | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_8 | S:BO_NETH D:A mighty spirit of darkness of vaguely humanoid form. Razor-edged claws D:reach out to end your life as it glides towards you, seeking to suck the D:energy from your soul to feed its power. N:666:Iron lich G:L:s I:120:30d66:30:70:10 W:60:4:0:310000 O:0:0:100 B:BUTT:COLD:3d6 B:BUTT:FIRE:3d6 B:BUTT:ELEC:3d6 F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | DUN_GRAVE F:COLD_BLOOD | BASH_DOOR | CAN_FLY | F:EVIL | UNDEAD | POWERFUL | SMART | F:IM_FIRE | IM_COLD | IM_POIS | RES_TELE F:ONLY_ITEM | DROP_60 | DROP_GOOD | F:NO_CONF | NO_SLEEP S:1_IN_2 | S:BA_WATE | BR_FIRE | BO_ICEE | BA_ELEC | BA_COLD | S:CAUSE_4 | DRAIN_MANA | BRAIN_SMASH | S:S_UNDEAD D:It is a huge, twisted grey skull floating through the air. Its cold eyes D:burn with hatred towards all who live. N:667:Dread G:G:o I:120:32d10:20:22:10 W:63:1:0:33000 O:0:50:50 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:LOSE_STR:3d4 F:FORCE_SLEEP | F:RAND_25 | FRIENDS | CAN_FLY | DUN_GRAVE F:ONLY_ITEM | DROP_60 | F:TAKE_ITEM | INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_15 | S:BLIND | HOLD | CONF | DRAIN_MANA | BO_NETH D:It is a form that screams its presence against the eye. Death incarnate, D:its hideous black body seems to struggle against reality as the universe D:itself struggles to banish it. N:668:Greater basilisk G:R:D I:120:32d44:25:50:15 W:64:2:0:145000 O:0:50:50 B:GAZE:PARALYZE B:BITE:HURT:5d12 B:BITE:HURT:5d12 B:BITE:HURT:5d12 F:DUN_RUIN | DUN_LAIR | WILD_MOUNT1 | WILD_MOUNT2 | WILD_FOREST2 F:ONLY_ITEM | DROP_90 | DROP_GOOD | POWERFUL | FORCE_MAXHP | F:OPEN_DOOR | BASH_DOOR | EVIL | IM_POIS | DROP_CORPSE F:ANIMAL | NO_CONF | NO_SLEEP | CAN_SWIM | ATTR_MULTI S:1_IN_8 S:BR_POIS | BR_DARK | BR_NETH D:A large basilisk, whose shape resembles that of a great wyrm. N:669:Charybdis G:l:r I:120:29d97:20:50:70 W:57:1:0:210000 O:50:50:0 B:ENGULF:HURT:10d8 B:ENGULF:HURT:10d8 B:ENGULF:HURT:10d8 F:AQUATIC | EVIL | UNIQUE | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_3D2 | F:SMART | POWERFUL | MOVE_BODY | NEVER_MOVE | F:IM_ACID | IM_COLD | RES_WATE | F:IM_POIS | NO_CONF | NO_SLEEP | NO_STUN | S:1_IN_5 | S:BA_WATE D:A monstrous dweller of the depths; its hungry maw has been the doom D:of innumerable sailors! N:670:Jack of Shadows G:p:s I:150:29d73:70:75:4 W:58:4:0:260000 O:20:80:0 B:HIT:HURT:5d10 B:HIT:HURT:5d10 B:HIT:EAT_ITEM:2d10 B:HIT:EAT_ITEM:2d10 F:DUN_CITY | DUN_DARKWATER | DUN_TEMPLE | DUN_TOWER F:MALE | FORCE_MAXHP | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:REGENERATE | NO_CONF | NO_SLEEP | NO_STUN | NO_FEAR | F:RES_TELE | F:ONLY_ITEM | DROP_90 | DROP_GOOD | DROP_GREAT | UNIQUE | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | S:1_IN_4 S:HEAL | BA_DARK | HASTE | CONF | D:Deriving his strength from the shadows, this king of thieves D:steals only for the challenge. N:671:Zephyr Lord G:W:v I:130:32d21:20:35:10 W:64:2:0:150000 O:0:100:0 B:HIT:EXP_20:8d2 B:HIT:LOSE_STR:8d2 B:HIT:LOSE_CON:8d2 F:DUN_PLANAR F:MALE | ATTR_MULTI | UNDEAD | EVIL | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:ONLY_ITEM | DROP_3D2 | F:SMART | OPEN_DOOR | BASH_DOOR | F:NO_STUN | NO_CONF | NO_SLEEP | S:1_IN_5 | S:SHRIEK | S_HOUND D:An undead master of the vicious Zephyr hounds. N:672:Juggernaut of Khorne G:g:D I:125:34d38:12:45:10 W:67:3:0:65000 O:0:0:0 B:BUTT:HURT:6d6 B:CRUSH:HURT:8d6 B:CRUSH:HURT:8d6 B:BUTT:HURT:6d6 F:DUN_CAVERN | DUN_TOWER F:BASH_DOOR | REFLECTING | KILL_ITEM | F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | DEMON | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | EVIL D:A great, vicious beast whose flesh is made of steel and whose D:blood is fire. It charges at you ignoring everything else. N:673:Mumak G:q:s I:110:28d20:20:30:100 W:56:2:0:19000 O:0:0:0 B:BUTT:HURT:4d6 B:BUTT:HURT:4d6 B:CRUSH:HURT:4d4 F:FRIENDS | DROP_CORPSE F:DUN_LAIR | DUN_CITY | WILD_WASTE1 | WILD_GRASS | WILD_FOREST1 F:BASH_DOOR | F:ANIMAL D:A massive elephantine form with eyes twisted by madness. N:674:Judge Fear G:W:D I:120:32d40:90:45:10 W:63:4:0:320000 O:100:0:0 B:GAZE:TERRIFY B:GAZE:EXP_40 B:GAZE:EXP_40 B:GAZE:HURT:2d20 F:UNIQUE | MALE | CAN_SPEAK | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:INVISIBLE | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS | F:HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | HOLD | SCARE | CAUSE_3 | MIND_BLAST | BRAIN_SMASH | S:S_UNDEAD | DRAIN_MANA | FORGET | ANIM_DEAD D:A Dark Judge, reputedly so frightening that his gaze can kill. N:675:Ancient multi-hued dragon G:D:v I:120:32d47:20:45:70 W:64:1:0:235000 O:50:50:0 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:BITE:HURT:6d12 F:ATTR_MULTI | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE F:ONLY_ITEM | DROP_4D2 | F:SMART | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD | LITE_2 F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_5 | S:BLIND | CONF | SCARE | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS D:A huge draconic form. Many colours ripple down its massive frame. Few D:live to see another. N:676:Ethereal dragon G:D:o I:120:30d47:25:45:15 W:60:2:0:215000 O:50:50:0 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:BITE:HURT:6d12 F:FORCE_SLEEP | FORCE_MAXHP | DUN_PLANAR | DUN_CAVERN | WILD_FOREST1 | WILD_GRASS F:ONLY_ITEM | DROP_3D2 | F:INVISIBLE | CAN_FLY | F:PASS_WALL | POWERFUL | MOVE_BODY | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_5 | S:BLIND | CONF | S:BR_LITE | BR_DARK | BR_CONF D:A huge dragon emanating from the elemental plains, the ethereal dragon is D:a master of light and dark. Its form disappears from sight as it cloaks D:itself in unearthly shadows. N:677:Dark young of Shub-Niggurath G:#:D I:120:32d29:20:37:80 W:64:20:0:59000 O:0:50:30 B:CRUSH:HURT:5d6 B:CRUSH:HURT:5d6 B:BITE:LOSE_STR:1d6 B:BITE:LOSE_STR:1d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_HORROR F:ONLY_ITEM | DROP_90 | NONLIVING | CAN_SWIM | F:OPEN_DOOR | BASH_DOOR | POWERFUL | HURT_LITE | F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_9 | S:BLIND | CAUSE_2 | ELDRITCH_HORROR | S:S_DEMON | HEAL D:"Something black in the road, something that wasn't a tree. D:Something big and black and ropy, just squatting there, waiting, D:with ropy arms squirming and reaching... It came crawling up the D:hillside... and it was the black thing of my dreams... that black, D:ropy, slimy jelly tree-thing out of the woods. It crawled up and it D:flowed up on its hoofs and mouths and snaky arms." N:678:Colour out of space G:.:v I:120:33d28:20:50:10 W:65:2:0:185000 O:40:40:10 B:TOUCH:LOSE_ALL:6d6 B:TOUCH:LOSE_ALL:6d6 F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | DUN_TOWER | DUN_HORROR F:INVISIBLE | PASS_WALL | CAN_FLY | F:ONLY_ITEM | DROP_2D2 | ATTR_MULTI | ATTR_ANY | F:CHAR_CLEAR | SMART | COLD_BLOOD | EVIL | F:IM_COLD | IM_FIRE | IM_ACID | IM_POIS | F:NO_STUN | NO_CONF | NO_SLEEP S:1_IN_9 | S:DRAIN_MANA | BR_DISE | BR_NETH | TPORT D:"The shaft of phosphorescence from the well... was no longer shining D:out, it was pouring out; and as the shapeless stream of unplaceable D:colour left the well it seemed to flow directly into the sky." N:679:Quaker, Master of Earth G:E:u I:110:33d39:10:50:90 W:65:4:0:175000 O:0:0:0 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:SHATTER:10d10 F:UNIQUE | MALE | CAN_SPEAK | DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM F:EMPTY_MIND | COLD_BLOOD | KILL_WALL | F:KILL_ITEM | KILL_BODY | PASS_WALL | POWERFUL | F:EVIL | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:HURT_ROCK | NONLIVING | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:BO_ACID | BA_ACID D:A towering stone elemental stands before you. The walls and ceiling are D:reduced to rubble as Quaker advances. N:680:Death leprechaun G:h:D I:120:12d3:8:7:8 W:64:6:0:11100 O:0:0:0 B:TOUCH:EXP_40:1d10 B:TOUCH:EAT_GOLD B:TOUCH:EAT_ITEM F:DUN_GRAVE | DUN_TOWER F:FRIENDS | INVISIBLE | RAND_25 | TAKE_ITEM | COLD_BLOOD | SMART | F:HURT_LITE | EVIL | OPEN_DOOR | MALE | UNDEAD | RES_NETH | SILLY | LITE_1 S:1_IN_6 S:BLINK | TPORT | TELE_TO | CAUSE_3 | ANIM_DEAD D:Nasty undead little creatures. They are chanting the name of the D:sinister wizard who created them: 'Bruce! Bruce..!' N:681:Chaugnar Faugn, Horror from the Hills G:q:D I:110:33d64:10:62:90 W:65:40:0:280000 O:0:0:0 B:CRUSH:HURT:10d10 B:CRUSH:HURT:10d10 B:BITE:LOSE_CON:8d2 B:BITE:LOSE_CON:8d1 F:UNIQUE | CAN_SPEAK | EVIL | DEMON | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | F:POWERFUL | CAN_SWIM F:IM_FIRE | IM_COLD | IM_POIS | F:HURT_ROCK | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:HOLD | CAUSE_4 | CONF | SCARE | FORGET | BRAIN_SMASH | S:MIND_BLAST | CAUSE_3 | S_DEMON | S_MONSTERS | ELDRITCH_HORROR D:"The ears were webbed and tentacled, the trunk terminated in D:a huge flaring disk at least a foot in diameter... Its forelimbs D:were bent stiffly at the elbow, and its hands... rested palms D:upward on its lap." N:682:Lloigor G:v:B I:120:32d33:20:50:20 W:63:20:0:165000 O:90:0:10 B:ENGULF:LOSE_CON:4d10 B:ENGULF:LOSE_CON:4d10 B:ENGULF:LOSE_CON:4d10 F:FORCE_SLEEP | PASS_WALL | FORCE_MAXHP | DUN_HORROR F:INVISIBLE | DROP_3D2 | CAN_FLY | EVIL | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | POWERFUL | NONLIVING | F:SMART | IM_FIRE | IM_COLD | IM_POIS S:1_IN_4 | S:BR_WALL | TELE_AWAY | BR_GRAV | S:S_KIN | MIND_BLAST | BLIND | BLINK | ELDRITCH_HORROR | D:"Invisible ones from the stars... They sometimes took forms... D:but existed as vortices of power in their natural state." N:683:Utgard-Loke G:P:w I:120:34d83:30:62:15 W:67:3:0:490000 O:0:100:0 B:HIT:COLD:12d12 B:HIT:COLD:12d12 B:HIT:COLD:12d12 B:HIT:COLD:12d12 F:ESCORT | UNIQUE | IM_COLD | AURA_COLD | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | DUN_LAIR | CAN_SWIM F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | MOVE_BODY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT | MALE S:1_IN_3 | S:HEAL | TELE_TO | S:S_KIN | BR_COLD D:A frost giant chieftain, unusually smart for a giant. N:684:Quachil Uttaus, Treader of the Dust G:z:D I:120:31d81:30:40:15 W:61:30:0:770000 O:0:0:100 B:CLAW:HURT:40d1 B:TOUCH:TIME:1d50 B:TOUCH:TIME:1d50 F:ESCORT | UNIQUE | IM_COLD | RES_NETH | F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE | CAN_SWIM F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | KILL_BODY | DROP_GREAT | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | DEMON | REGENERATE | S:1_IN_3 | S:BR_TIME | SLOW | HASTE | HEAL | ELDRITCH_HORROR | D:"It was a figure no larger than a young child, but sere and D:shriveled as some millenial mummy. Its hairless head, its D:unfeatured face, borne on a neck of skeleton thinness, were D:lined with a thousand reticulated wrinkles. The body was like D:that of some monstrous, withered abortion that had never drawn D:breath. The pipy arms, ending in bony claws, were outhrust as if D:ankylosed in posture of an eternal dreadful groping." N:685:Shoggoth G:j:D I:140:32d22:100:40:20 W:64:20:0:106000 O:0:0:0 B:CRUSH:ACID:5d6 B:CRUSH:ACID:5d6 B:CRUSH:ACID:5d6 B:CRUSH:HURT:9d9 F:REGENERATE | KILL_ITEM | CAN_SPEAK | DUN_HORROR F:BASH_DOOR | EVIL | DEMON | NO_CONF | NO_FEAR | NO_SLEEP | F:KILL_BODY | CAN_SWIM | F:FORCE_MAXHP | FORCE_SLEEP | HURT_LITE | POWERFUL | F:IM_ACID | IM_FIRE | RES_PLAS | IM_POIS | IM_ELEC | RES_TELE S:1_IN_10 S:ELDRITCH_HORROR | D:"The nightmare, plastic column of fetid, black iridescence oozed D:tightly onward... A shapeless congerie of protoplasmic bubbles, D:faintly self-luminous and with myriads of temporary eyes forming D:and unforming as pustules of greenish light all over the D:tunnel-filling front that bore down upon us, crushing the frantic D:penguins and slithering over glistening floor that it and its D:kind had swept so evilly free of all litter. Still came that eldritch D:mocking cry -- 'Tekeli-li! Tekeli-li!'" N:686:Judge Death G:W:D I:120:32d54:90:45:10 W:63:3:0:710000 O:0:20:80 B:CLAW:POISON:10d5 B:CLAW:POISON:10d5 B:CLAW:EXP_40:10d1 B:GAZE:TERRIFY F:UNIQUE | MALE | CAN_SPEAK | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL | UNDEAD | IM_ELEC | IM_COLD | HURT_FIRE | F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | HOLD | SCARE | CAUSE_4 | BA_FIRE | BA_NETH | ANIM_DEAD S:S_MONSTERS | S_UNDEAD | S_HI_UNDEAD | DRAIN_MANA | D:The most powerful Dark Judge, whose touch means death. N:687:Ariel, Queen of Air G:E:B I:130:30d63:12:25:50 W:60:4:0:160000 O:0:0:0 B:HIT:HURT:4d6 B:HIT:CONFUSE:1d4 B:HIT:HURT:4d6 B:HIT:CONFUSE:1d4 F:UNIQUE | FEMALE | AURA_ELEC | CAN_SPEAK | DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | F:RAND_25 | CAN_FLY | NONLIVING | F:EMPTY_MIND | COLD_BLOOD | LITE_1 F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | F:EVIL | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_5 | S:BO_ELEC | BA_COLD | BA_ELEC D:A towering air elemental, Ariel, the sorceress, avoids your blows D:with her extreme speed. N:688:11-headed hydra G:M:R I:120:32d40:20:50:20 W:64:2:0:240000 O:100:0:0 B:BITE:FIRE:3d12 B:BITE:FIRE:3d12 B:BITE:FIRE:3d12 B:BITE:FIRE:3d12 F:FORCE_SLEEP | DUN_DARKWATER | DUN_LAIR F:WILD_SHORE | WILD_SWAMP1 | WILD_SWAMP2 F:ONLY_GOLD | DROP_1D2 | DROP_4D2 | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:ANIMAL | IM_FIRE | CAN_SWIM S:1_IN_4 | S:SCARE | BO_FIRE | BO_PLAS | BA_FIRE | S:BR_FIRE D:A strange reptilian hybrid with eleven smouldering heads. N:689:High priest G:p:w I:120:33d17:20:25:10 W:65:2:0:106000 O:0:10:90 B:HIT:HURT:3d4 B:HIT:HURT:3d4 B:HIT:HURT:3d5 F:MALE | DUN_TEMPLE | WILD_GRASS | WILD_FOREST1 F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | F:SMART | OPEN_DOOR | BASH_DOOR | LITE_2 F:EVIL | NO_CONF | NO_SLEEP S:1_IN_2 | S:HEAL | BLIND | HOLD | CAUSE_4 | BRAIN_SMASH | ANIM_DEAD S:S_MONSTERS | S_UNDEAD D:A dark priest of the highest order. Powerful and evil, beware his many D:spells. N:690:Dreadmaster G:G:o I:120:35d24:20:50:10 W:70:2:0:280000 O:10:50:30 B:HIT:HURT:6d6 B:HIT:HURT:6d6 B:HIT:LOSE_STR:3d4 B:HIT:LOSE_STR:3d4 F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | DUN_GRAVE F:ONLY_ITEM | DROP_3D2 | CAN_FLY | F:SMART | TAKE_ITEM | INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_9 | S:TELE_LEVEL | BLIND | HOLD | CONF | CAUSE_4 | DRAIN_MANA | BO_NETH | S:S_UNDEAD D:It is an unlife of power almost unequaled. An affront to existence, its D:very touch abuses and disrupts the flow of life, and its unearthly limbs, D:of purest black, crush rock and flesh with ease. N:691:Drolem G:g:g I:120:31d49:25:52:30 W:62:3:0:215000 O:0:0:0 B:BITE:HURT:5d8 B:BITE:HURT:5d8 B:CLAW:POISON:3d3 B:CLAW:POISON:3d3 F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | DUN_TOWER F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:DRAGON | IM_FIRE | IM_COLD | F:IM_ELEC | IM_POIS | CAN_SWIM | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_6 | S:BLIND | SLOW | CONF | ARROW | S:BR_POIS D:A constructed dragon, the drolem has massive strength. Powerful spells D:weaved during its creation make it a fearsome adversary. Its eyes show D:little intelligence, but it has been instructed to destroy all it meets. N:692:Scatha the Worm G:D:w I:120:32d44:20:65:70 W:64:2:0:590000 O:50:50:0 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:BITE:HURT:6d14 F:UNIQUE | MALE | F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_CORPSE F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_3 | S:CONF | CAUSE_3 | S:BR_COLD D:An ancient and wise Dragon. Scatha has grown clever over the long years. D:His scales are covered with frost, and his breath sends a shower of ice D:into the air. N:693:Warrior of the Dawn G:p:R I:120:31d16:20:35:20 W:62:2:0:26000 O:0:0:0 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:IM_POIS | IM_ELEC | IM_ACID F:NO_SLEEP | NO_FEAR | F:FRIENDS | DUN_CITY | DUN_CAVERN F:MALE | OPEN_DOOR | BASH_DOOR D:Fierce, barbaric warriors, armed with great spiked clubs, and surrounded D:in an aura of scarlet. Whenever one of them is slain, another appears D:out of nowhere to take his place. N:694:Lesser black reaver G:L:D I:120:33d54:20:60:50 W:66:3:0:290000 O:0:0:100 B:HIT:UN_BONUS:4d8 B:HIT:UN_BONUS:4d8 B:HIT:LOSE_STR:4d6 B:HIT:LOSE_STR:4d6 F:FORCE_SLEEP | FORCE_MAXHP | SMART | DUN_GRAVE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | CAN_SWIM | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | RES_TELE | F:NO_CONF | NO_SLEEP | KILL_WALL | NO_FEAR S:1_IN_3 | S:TELE_TO | BLIND | HOLD | CONF | CAUSE_3 | DRAIN_MANA | S:MIND_BLAST |BA_NETH | ANIM_DEAD D:A humanoid form, black as night, advancing steadily and unstoppably. N:695:Zoth-Ommog G:R:u I:120:33d54:12:25:50 W:65:40:0:340000 O:0:0:0 B:CRUSH:HURT:25d3 B:CRUSH:HURT:25d3 B:BITE:LOSE_DEX:2d10 B:BITE:LOSE_CON:2d10 F:UNIQUE | CAN_SPEAK | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | F:RAND_25 | CAN_SWIM | F:KILL_ITEM | BASH_DOOR | POWERFUL | F:EVIL | IM_ACID | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_5 | S:S_MONSTER | SCARE | CAUSE_4 | S_HYDRA | S_SPIDER | S:BA_NETH | BA_POIS | BA_ACID | CAUSE_3 | HOLD | MIND_BLAST | ELDRITCH_HORROR D:"A body shaped like a road-based, truncated cone. A flat, blunt, D:wedge-shaped, vaguely reptilian head surmounts this conical torso, D:and the head is almost entirely hidden behind swirling tresses. D:This hair, or beard and mane, consists of thickly carved and D:coiling ropes, like serpents or worms... Through this repulsive D:Medusa-mane of ropy tendrils, two fierce, serpent-like eyes D:glare in a horrible intermingling of cold, inhuman mockery and D:what I can only describe as gloating menace." N:696:Nazgul G:W:D I:120:33d100:90:55:10 W:66:3:0:430000 O:50:50:0 B:HIT:TERRIFY:6d6 B:HIT:TERRIFY:6d6 B:HIT:EXP_80:4d6 B:HIT:EXP_80:4d6 F:MALE | F:FORCE_MAXHP | UNIQUE_7 | DUN_GRAVE | DUN_HORROR | CAN_SWIM F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | RES_TELE F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | RES_NETH | SMART | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_6 | S:SCARE | HOLD | DRAIN_MANA | BR_NETH D:A tall black cloaked Ringwraith, radiating an aura of fear. N:697:Smaug the Golden G:D:R I:120:35d40:20:50:70 W:70:2:0:810000 O:50:50:0 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:BITE:HURT:6d14 F:UNIQUE | MALE | REFLECTING | CAN_FLY | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | DUN_CAVERN | DUN_LAIR F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | LITE_2 F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:CONF | CAUSE_3 | S:BR_FIRE D:Smaug is one of the Uruloki that still survive, a fire-drake of immense D:cunning and intelligence. His speed through air is matched by few other D:dragons and his dragonfire is what legends are made of. N:698:The Stormbringer G:|:D I:120:33d37:20:50:20 W:65:2:0:490000 O:0:0:0 B:WAIL:TERRIFY B:HIT:EXP_80:64d1 B:HIT:EXP_80:64d1 B:HIT:EXP_VAMP:8d8 F:CHAR_MIMIC | EVIL | IM_POIS | IM_COLD | IM_FIRE | RES_NETH | F:FORCE_SLEEP | UNIQUE | FORCE_MAXHP | CAN_FLY | F:COLD_BLOOD | BASH_DOOR | NONLIVING | DUN_TOWER | DUN_CAVERN F:NO_CONF | NO_SLEEP | NO_FEAR | DROP_CHOSEN | D:The mightiest of hellblades, a black runesword which thirsts for D:your soul. N:699:Ultra-elite paladin G:p:w I:120:35d16:20:72:20 W:69:2:0:80000 O:0:100:0 B:HIT:HURT:3d12 B:HIT:HURT:3d12 F:DUN_TEMPLE | DUN_CITY | WILD_GRASS | WILD_FOREST1 | WILD_WASTE1 F:IM_FIRE | IM_ELEC | IM_COLD | GOOD | F:RES_NETH | RES_NEXU | RES_DISE | RES_TELE | DROP_SKELETON | DROP_CORPSE F:NO_SLEEP | NO_CONF | NO_FEAR | NO_STUN | F:DROP_1D2 | FRIENDS | REFLECTING | F:MALE | OPEN_DOOR | BASH_DOOR | FORCE_MAXHP | LITE_2 S:1_IN_12 S:HEAL D:Fighting for a good cause, and they consider you an agent of the devil. N:700:Leprechaun fanatic G:h:D I:123:12d3:8:7:8 W:63:6:0:9400 O:0:0:0 B:EXPLODE:HURT:20d3 F:FRIENDS | RAND_25 | TAKE_ITEM | SMART | DUN_TOWER | DUN_CAVERN | DUN_CITY F:EVIL | OPEN_DOOR | MALE | NO_FEAR | SILLY | LITE_1 S:1_IN_6 S:BLINK | TPORT | TELE_TO D:These leprechauns are not afraid to die for their cause. They are D:carrying explosives and making terrorist strikes... N:701:Dracolich G:D:B I:120:33d55:25:48:30 W:65:2:0:330000 O:10:50:40 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:BITE:EXP_80:3d6 B:BITE:EXP_VAMP:3d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE | DUN_DARKWATER | WILD_WASTE2 F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | RES_TELE F:COLD_BLOOD | CAN_FLY | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_6 | S:CONF | SCARE | S:BR_COLD | BR_NETH D:The skeletal form of a once-great dragon, enchanted by magic most D:perilous. Its animated form strikes with speed and drains life from its D:prey to satisfy its hunger. N:702:Greater titan G:P:v I:120:32d63:30:50:15 W:63:3:0:380000 O:0:100:0 B:HIT:CONFUSE:12d12 B:HIT:CONFUSE:12d12 B:HIT:CONFUSE:12d12 B:HIT:CONFUSE:12d12 F:DUN_TEMPLE | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | MOVE_BODY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | GIANT | MALE S:1_IN_3 | S:HEAL | TELE_TO | S:S_MONSTERS D:A forty foot tall humanoid that shakes the ground as it walks. The power D:radiating from its frame shakes your courage, its hatred inspired by your D:defiance. N:703:Dracolisk G:D:r I:120:34d53:25:48:30 W:68:3:0:520000 O:0:50:40 B:BITE:HURT:5d12 B:BITE:HURT:5d12 B:BITE:HURT:8d8 B:GAZE:PARALYZE F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY F:DUN_TEMPLE | DUN_CAVERN | WILD_FOREST1 | WILD_FOREST2 F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | RES_NETH | RES_TELE F:ANIMAL | EVIL | DRAGON | IM_ACID | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_6 | S:HOLD | SCARE | S:BR_FIRE | BR_NETH D:A mixture of dragon and basilisk, the dracolisk stares at you with deep D:piercing eyes, its evil breath burning the ground where it stands. N:704:Fastitocalon G:D:g I:120:32d99:25:75:30 W:64:3:0:260000 O:0:0:0 B:BITE:HURT:5d8 B:BITE:HURT:5d8 B:CRUSH:POISON:3d10 B:CRUSH:POISON:3d10 F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | AQUATIC | F:DUN_CAVERN | WILD_OCEAN | F:IM_FIRE | IM_ACID F:DRAGON | NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_6 | S:BR_FIRE | BR_ACID D:A huge aquatic dragon, its shell is as large as a small island. Some D:have called this creature a dragon-turtle. N:705:Spectral tyrannosaurus G:R:G I:120:32d79:25:60:30 W:63:3:0:350000 O:50:30:20 B:BITE:EXP_40:2d13 B:BITE:EXP_40:2d13 B:BITE:LOSE_STR:5d8 B:GAZE:TERRIFY F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE | DUN_CAVERN F:EVIL | UNDEAD | ANIMAL | RES_NEXU | RES_TELE F:NO_CONF | NO_FEAR | NO_SLEEP | F:IM_POIS | IM_ACID | IM_COLD | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY S:1_IN_6 | S:HOLD | SCARE | S:BR_NEXU | BR_POIS | BR_NETH D:A deadly undead horror which looks like a skeletal tyrannosaurus D:surrounded by a sickly green glow. N:706:Yibb-Tstll the Patient One G:P:D I:120:36d63:30:50:20 W:72:20:0:1350000 O:0:0:100 B:TOUCH:LOSE_ALL:1d166 B:TOUCH:LOSE_ALL:1d166 F:FORCE_SLEEP | NEVER_MOVE | REGENERATE | DUN_HORROR F:DROP_3D2 | CAN_FLY | DROP_GOOD | ONLY_ITEM | F:POWERFUL | EVIL | CAN_SPEAK | UNIQUE | FORCE_MAXHP | F:SMART | IM_FIRE | IM_COLD | IM_ACID | IM_POIS S:1_IN_4 | S:BR_NUKE | S_DEMON | DARKNESS | S_UNDEAD | ANIM_DEAD S:BR_NEXU | BR_CHAO | TELE_TO | ELDRITCH_HORROR D:"There, about the pulsating body of the Ancient One, hugely D:winged reptilian creatures without faces cluttered and clutched D:at a multitude of blackly writhing, pendulous breasts! (Its eyes) D:moved quickly, independently -- sliding with vile viscosity over D:the whole rotten surface of Yib-Tstll's pulpy, glistening head!" N:707:Ghatanothoa G:H:D I:120:37d57:20:50:20 W:73:20:0:530000 O:0:0:100 B:CLAW:LOSE_INT:5d10 B:CLAW:LOSE_WIS:5d10 B:BITE:CONFUSE:5d10 F:DUN_HORROR F:FORCE_SLEEP | PASS_WALL | UNIQUE | FORCE_MAXHP F:INVISIBLE | DROP_3D2 | CAN_FLY | DROP_GOOD | ONLY_ITEM | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | POWERFUL | EVIL | F:SMART | IM_FIRE | IM_COLD | IM_ELEC S:1_IN_4 | S:BR_WALL | TELE_AWAY | BR_GRAV | TELE_LEVEL | S_DEMON | S_UNDEAD | S:BRAIN_SMASH | HASTE | BLIND | BLINK | BR_INER | CAUSE_3 | CAUSE_4 S:ELDRITCH_HORROR | D:The chief among the creatures known as Lloigor. Ghatanothoa has D:assumed a shape which is too horrible to describe: "Nothing I could say D:could even adumbrate the loathsome, unholy, non-human, extra-galactic D:horror and hatefulness and unutterabl evil of that forbidden spawn D:of black chaos, and illimitable night." N:708:Ent G:%:G I:120:36d78:30:60:15 W:71:3:0:540000 O:30:50:20 B:CRUSH:HURT:10d11 B:CRUSH:HURT:10d11 B:CRUSH:HURT:10d11 B:CRUSH:HURT:10d11 F:FORCE_SLEEP | FORCE_MAXHP | FRIENDLY | DUN_LAIR | WILD_FOREST2 F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | MOVE_BODY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:GOOD | GIANT | MALE D:A treeherd; a sentient, moving tree. Its wrath is fearsome! N:709:Hru G:P:s I:120:38d74:30:75:15 W:76:3:0:620000 O:40:50:0 B:CRUSH:SHATTER:10d11 B:CRUSH:SHATTER:10d11 B:CRUSH:SHATTER:10d11 F:FORCE_SLEEP | FORCE_MAXHP | HURT_ROCK F:DUN_CAVERN | WILD_MOUNT1 | WILD_MOUNT2 F:DROP_3D2 | MOVE_BODY | KILL_WALL | F:SMART | KILL_ITEM | BASH_DOOR | F:EVIL | GIANT | MALE D:A rock giant, a being made of living stone. N:710:Itangast the Fire Drake G:D:r I:120:34d46:20:50:70 W:68:4:0:660000 O:50:50:0 B:CLAW:HURT:1d10 B:CLAW:HURT:1d10 B:BITE:HURT:3d14 B:BITE:HURT:4d14 F:UNIQUE | MALE | DUN_CAVERN | DUN_LAIR F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | LITE_2 F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:CONF | CAUSE_3 | S:BR_FIRE D:A mighty ancient dragon, Itangast's form scorches your flesh. Wisps of D:smoke curl up from his nostrils as he regards you with disdain. N:711:Death mold G:m:D I:140:34d42:100:60:0 W:67:1:0:108000 O:0:0:0 B:HIT:UN_BONUS:7d7 B:HIT:UN_BONUS:7d7 B:HIT:UN_BONUS:7d7 B:HIT:EXP_VAMP:5d5 F:FORCE_SLEEP | NEVER_MOVE | DUN_CAVERN | DUN_TEMPLE F:EVIL | IM_ACID | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR D:It is the epitome of all that is evil, in a mold. Its lifeless form draws D:power from sucking the souls of those that approach it, a nimbus of pure D:evil surrounds it. Luckily for you, it can't move. N:712:Fafner the Dragon G:D:G I:120:33d63:20:50:70 W:66:4:0:680000 O:50:50:0 B:CLAW:HURT:4d10 B:CLAW:HURT:4d10 B:BITE:FIRE:14d6 B:BITE:POISON:14d6 F:UNIQUE | MALE | F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | DROP_CORPSE | DUN_LAIR | DUN_CAVERN F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | LITE_2 F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP | IM_POIS S:1_IN_3 | S:BR_FIRE | BR_POIS | SCARE | CAUSE_3 | CONF D:The mighty dragon of the Norse myth, Fafner was a giant who slew his D:brother to win a treasure hoard, and then transformed himself into D:a dragon, greedily watching over his hoard. N:713:Fangorn G:%:G I:120:37d95:30:60:15 W:73:3:0:790000 O:50:50:0 B:CRUSH:HURT:12d11 B:CRUSH:HURT:12d11 B:CRUSH:HURT:12d11 B:CRUSH:HURT:12d11 F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | CAN_SPEAK F:DUN_LAIR | WILD_FOREST2 | CAN_SWIM F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | MOVE_BODY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:GOOD | GIANT | FRIENDLY | MALE D:The oldest of all ents, a respected and feared ancient creature. N:714:Zhar the Twin Obscenity G:R:D I:120:34d103:30:60:15 W:68:30:0:610000 O:20:0:80 B:CRUSH:HURT:23d10 B:CRUSH:HURT:23d10 B:CRUSH:HURT:23d10 F:DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | KILL_BODY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | CAN_SWIM | F:EVIL S:1_IN_6 S:S_DEMON | SHRIEK | CONF | SCARE | MIND_BLAST | BLIND | ELDRITCH_HORROR D:"The thing that crouched in the weird green dusk was a living mass D:of shuddering horror, a ghastly mountain of sensate, quivering D:flesh, whose tentacles, far-flung in the dim reaches of the D:subterranean cavern, emitted a strange humming sound, while from D:the depths of the creature's body came a weird and horrific D:undulation." N:715:Glaurung, Father of the Dragons G:D:r I:120:36d48:20:60:70 W:72:2:0:1500000 O:50:50:0 B:CLAW:HURT:7d12 B:CLAW:HURT:7d12 B:BITE:HURT:8d14 B:BITE:HURT:8d14 F:UNIQUE | MALE | CAN_FLY | DROP_CORPSE | DUN_LAIR | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_SPEAK | F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP | LITE_2 S:1_IN_5 | S:CONF | CAUSE_3 | S:BR_FIRE | S:S_DRAGON D:Glaurung is the father of all dragons, and was for a long time the most D:powerful. Nevertheless, he still has full command over his brood and can D:command them to appear whenever he so wishes. He is the definition of D:dragonfire. N:716:Behemoth G:H:g I:120:34d103:25:90:30 W:68:3:0:280000 O:0:0:0 B:BITE:FIRE:5d8 B:BITE:FIRE:5d8 B:CRUSH:HURT:3d15 B:CRUSH:HURT:3d15 F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | ANIMAL | DUN_CAVERN | WILD_FOREST1 F:IM_FIRE | IM_ACID | IM_COLD | DROP_CORPSE F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_9 S:BR_FIRE D:A great water-beast, with an almost unpenetrable skin. N:717:Garm, Guardian of Hel G:C:u I:120:32d66:20:60:70 W:64:2:0:780000 O:50:50:0 B:CLAW:HURT:7d13 B:CLAW:HURT:7d13 B:BITE:HURT:8d13 B:BITE:HURT:8d13 F:UNIQUE | F:FORCE_SLEEP | FORCE_MAXHP | DUN_HELL F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | CAN_SWIM F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | RES_NETH | F:ANIMAL | EVIL | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:BR_NETH | S:BR_COLD | BR_DARK | S:S_HOUND | S_UNDEAD D:Garm is a gigantic hound, whose job is to guard that none escapes D:the tortures of Hel, the Norse place of punishment for the wicked D:and cowardly dead. N:718:Greater wall monster G:#:W I:120:36d14:20:40:20 W:72:4:0:59000 O:0:0:0 B:HIT:HURT:3d10 B:HIT:HURT:3d10 B:HIT:HURT:3d10 F:DUN_CAVERN | DUN_MINE F:FORCE_SLEEP | COLD_BLOOD | EMPTY_MIND | PASS_WALL | RAND_50 | MULTIPLY | F:BASH_DOOR | IM_COLD | IM_FIRE | IM_POIS | NONLIVING | F:HURT_ROCK | NO_CONF | NO_SLEEP | CHAR_MIMIC | CAN_FLY D:A sentient, moving section of wall. N:719:Nycadaemon G:U:o I:120:34d64:20:40:80 W:67:3:0:195000 O:50:0:50 B:TOUCH:TERRIFY B:CLAW:HURT:6d6 B:CLAW:HURT:6d6 B:CLAW:HURT:6d6 F:DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | NONLIVING | F:REGENERATE | IM_ACID | IM_COLD | IM_FIRE | CAN_FLY | F:NO_SLEEP | NO_STUN | NO_CONF | EVIL | DEMON | F:INVISIBLE | KILL_WALL | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD S:1_IN_4 | S:HOLD | BLINK | CONF | S_DEMON | BRAIN_SMASH | BR_NETH D:A loathsome, sturdy, winged, horned demon, with talons that could tear D:a stone wall down. N:720:Balrog G:U:R I:120:32d66:20:40:80 W:63:3:0:580000 O:0:50:50 B:HIT:FIRE:2d6 B:HIT:HURT:5d6 B:HIT:FIRE:6d2 B:HIT:HURT:6d5 F:DUN_HELL | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | CAN_FLY | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | NONLIVING | LITE_1 F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | SMART | F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | KILL_WALL | S:1_IN_4 | S:BLIND | CONF | BRAIN_SMASH | S:BR_FIRE | BO_PLAS | BA_NETH | S_HI_UNDEAD | S_UNDEAD | S:S_DEMON D:It is a massive humanoid demon wreathed in flames. N:721:Goat of Mendes G:q:D I:120:38d40:30:33:40 W:75:3:0:280000 O:0:0:100 B:GAZE:TERRIFY B:BUTT:HURT:6d6 B:BITE:EXP_40 B:BITE:LOSE_CON F:DUN_HELL F:EVIL | DEMON | FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_1D2 F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | SMART | NONLIVING | F:NO_CONF | NO_SLEEP | HURT_LITE | IM_FIRE | IM_COLD S:1_IN_4 | S:BLIND | CONF | BRAIN_SMASH | SCARE | ANIM_DEAD S:BA_NETH | FORGET | S_UNDEAD | DRAIN_MANA | S:S_DEMON | CAUSE_4 | BA_COLD D:It is a demonic creature from the lowest hell, vaguely resembling a D:large black he-goat. N:722:Nightwing G:W:D I:120:34d40:20:60:10 W:68:4:0:360000 O:0:0:100 B:TOUCH:POISON:3d5 B:TOUCH:POISON:3d5 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 F:FORCE_SLEEP | CAN_FLY | DUN_GRAVE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | RES_TELE F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_4 | S:BLIND | SCARE | CAUSE_4 | BRAIN_SMASH | S:BO_MANA | BO_NETH | BA_NETH | S:S_UNDEAD D:Everywhere colours seem paler and the air chiller. At the centre of the D:cold stands a mighty figure. Its wings envelop you in the chill of death D:as the nightwing reaches out to draw you into oblivion. Your muscles sag D:and your mind loses all will to fight as you stand in awe of this mighty D:being. N:723:Maulotaur G:H:u I:130:33d75:13:25:10 W:66:2:0:290000 O:0:50:50 B:HIT:SHATTER:8d8 B:HIT:SHATTER:8d8 B:BUTT:HURT:4d6 B:BUTT:HURT:4d6 F:ONLY_ITEM | DROP_60 | DROP_GOOD | RES_TELE | DUN_CAVERN | DUN_MINE F:BASH_DOOR | STUPID | DROP_CORPSE F:EVIL | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP S:1_IN_5 | S:BO_PLAS | BA_FIRE D:It is a belligrent minotaur with some destructive magical arsenal, armed D:with a hammer. N:724:Nether hound G:Z:D I:120:37d10:30:40:0 W:73:2:0:110000 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | F:FRIENDS | RES_NETH | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | DUN_PLANAR | DUN_CAVERN F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_5 | S:BR_NETH D:You feel a soul-tearing chill upon viewing this beast, a ghostly form of D:darkness in the shape of a large dog. N:725:Time hound G:Z:G I:130:36d10:30:40:0 W:72:4:0:165000 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | F:FRIENDS | DUN_PLANAR | DUN_CAVERN F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_8 | S:BR_TIME D:You get a terrible sense of deja vu, or is it a premonition? All at once D:you see a little puppy and a toothless old dog. Perhaps you should give D:up and go to bed. N:726:Plasma hound G:Z:R I:120:36d10:30:40:0 W:71:2:0:100000 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | DUN_PLANAR | DUN_CAVERN F:FRIENDS | RES_PLAS | F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | IM_FIRE | NO_CONF | NO_SLEEP | LITE_2 S:1_IN_5 | S:BR_PLAS D:The very air warps as pure elemental energy stalks towards you in the D:shape of a giant hound. Your hair stands on end and your palms itch as D:you sense trouble. N:727:Demonic quylthulg G:Q:R I:120:35d8:20:1:0 W:70:1:0:78000 O:0:0:0 F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | F:INVISIBLE | EMPTY_MIND | DUN_TEMPLE | DUN_CAVERN F:ANIMAL | EVIL | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK | TPORT | S:S_DEMON D:A pile of pulsing flesh that glows with an inner hellish fire. The world D:itself seems to cry out against it. N:728:Great storm wyrm G:D:b I:120:40d43:30:65:80 W:79:2:0:940000 O:50:50:0 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:BITE:ELEC:5d14 F:DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 F:FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | CAN_FLY | ATTR_MULTI F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE F:EVIL | DRAGON | IM_ELEC | NO_CONF | NO_SLEEP | LITE_2 S:1_IN_6 | S:BLIND | CONF | SCARE | S:BR_ELEC D:A vast dragon of power. Storms and lightning crash around its titanic D:form. Deep blue scales reflect the flashes and highlight the creature's D:great muscles. It regards you with contempt. N:729:Ulik the Troll G:T:o I:130:40d62:30:60:30 W:79:4:0:940000 O:0:100:0 B:HIT:SHATTER:20d12 B:HIT:SHATTER:20d12 B:BITE:POISON:6d14 F:UNIQUE | MALE | CAN_SPEAK | DUN_LAIR | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:REGENERATE | KILL_WALL | RES_TELE | F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP | F:IM_ELEC | IM_FIRE | D:Ulik is the strongest troll who has ever lived. He could challenge D:the immortals and pound them to dust with his great strength. N:730:Baphomet the Minotaur Lord G:H:D I:130:40d48:30:48:30 W:79:4:0:1500000 O:0:80:20 B:BUTT:HURT:12d13 B:BUTT:HURT:12d13 B:HIT:HURT:10d10 B:HIT:HURT:10d10 F:UNIQUE | MALE | CAN_SPEAK | DUN_MINE F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:BASH_DOOR | DROP_CORPSE F:EVIL | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_6 | S:SLOW | ARROW | BO_MANA | BO_PLAS | BA_ELEC | S:BR_WALL D:A fearsome bull-headed demon, Baphomet swings a mighty axe as he curses D:all that defy him. N:731:Hell knight G:p:s I:120:38d28:20:65:10 W:76:1:0:460000 O:0:40:60 B:HIT:HURT:10d5 B:HIT:HURT:10d5 B:HIT:HURT:10d5 B:HIT:EXP_80 F:DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | SMART | IM_FIRE | IM_COLD | IM_POIS | F:ONLY_ITEM | DROP_90 | RES_NETH | RES_NEXU | RES_PLAS | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | EVIL S:1_IN_5 | S:BLIND | SCARE | CAUSE_3 | BA_NETH | BA_FIRE | BO_PLAS | S_MONSTERS D:It is a humanoid form dressed in armour of ancient style. From beneath D:its helmet, eyes glow with hellfire. N:732:Bull Gates G:p:D I:140:24d73:40:45:0 W:48:3:0:195000 O:50:50:0 B:CHARGE:EAT_GOLD:5d5 B:CHARGE:EAT_ITEM:5d5 B:SPIT:BLIND:10d5 B:DROOL:DISEASE:8d5 F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | SILLY | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP | EVIL | RES_TELE S:1_IN_6 | S:TRAPS D:He may not code worth a dime, but he certainly knows how to make money. N:733:Santa Claus G:h:r I:150:37d48:90:50:10 W:73:3:0:2300000 O:25:25:25 B:HIT:LOSE_CHR:5d5 B:TOUCH:LOSE_ALL:10d1 B:TOUCH:LOSE_ALL:10d1 B:CHARGE:EAT_GOLD F:DUN_HORROR F:UNIQUE | MALE | CAN_SPEAK | REFLECTING | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SWIM | SILLY | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | DROP_GREAT | F:NO_STUN | NO_SLEEP | NO_CONF | LITE_1 F:IM_ELEC | IM_POIS | IM_COLD | F:COLD_BLOOD | RES_TELE S:1_IN_3 | S:CAUSE_4 | HOLD | DRAIN_MANA | SCARE | BLIND | S:S_UNDEAD | S_HI_UNDEAD | S_HI_DRAGON | S_UNIQUE | S:BA_NETH | FORGET | TRAPS | BRAIN_SMASH | TELE_AWAY D:Why would anybody want to kill Santa Claus? To get all the presents, D:of course! N:734:Eihort, the Thing in the Labyrinth G:j:R I:120:37d63:50:45:10 W:74:30:0:1900000 O:0:0:100 B:BITE:PARALYZE:8d1 B:CRUSH:HURT:10d10 B:CRUSH:HURT:10d10 B:CRUSH:HURT:10d10 F:DUN_HORROR F:UNIQUE | CAN_SPEAK | ESCORT | REGENERATE | CAN_SWIM F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL |IM_ACID | IM_COLD | IM_POIS | F:HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_4 | S:S_UNDEAD | S_DEMON | S_MONSTER | BLINK | BA_ACID | FORGET | ELDRITCH_HORROR D:"Then came pale movement in the well, and something clambered D:up from the dark, a bloated blanched oval supported on myriad D:fleshless legs. Eyes formed in the gelatinous oval and stared D:at him." N:735:The King in Yellow G:L:y I:120:35d64:90:50:10 W:69:30:0:1650000 O:50:0:40 B:CRUSH:HURT:12d12 B:CRUSH:HURT:12d12 B:GAZE:LOSE_WIS:5d10 B:GAZE:TERRIFY:5d10 F:DUN_GRAVE F:UNIQUE | MALE | CAN_SPEAK | ATTR_MULTI | ATTR_ANY | F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL | UNDEAD | IM_ACID | IM_FIRE | IM_COLD | IM_POIS | F:HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_2 | S:S_DEMON | S_UNDEAD | S_KIN | SCARE | CAUSE_3 | CAUSE_4 | ANIM_DEAD S:TELE_AWAY | TPORT | BRAIN_SMASH | CONF | ELDRITCH_HORROR D:The King in Yellow is an Avatar of Hastur: "He has no D:face, and is twice as tall as a man. He wears pointed shoes under D:his tattered, fantastically colored robes, and a streamer of silk D:appears to fall from the pointed tip of his hood. At times he appears D:to be winged; at others, haloed." N:736:Great unclean one G:U:g I:120:37d135:30:75:20 W:74:30:0:970000 O:50:0:50 B:BITE:DISEASE:10d10 B:BITE:ACID:10d10 B:BITE:POISON:10d10 B:BITE:CONFUSE:10d10 F:DUN_HORROR | DUN_HELL F:DEMON | EVIL | NONLIVING | CAN_SWIM | F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_1D2 | F:OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | DROP_GOOD | KILL_BODY | F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP F:KILL_ITEM S:1_IN_3 S:BR_ACID | BR_POIS | BR_NUKE | SCARE | ANIM_DEAD S:CAUSE_3 | CAUSE_4 | S_DEMON | S_UNDEAD | ELDRITCH_HORROR D:This disgusting demon resembles a shambling pile of rotting D:green flesh, with dozens of mouths drooling and leaving a D:trail of foul-smelling goo behind. Nurgle must be D:proud of himself for having created this atrocity! N:737:Lord of Chaos G:p:v I:130:39d48:30:40:5 W:77:3:0:730000 O:0:0:100 B:KICK:HURT:20d2 B:KICK:HURT:10d2 B:HIT:POISON:20d1 B:HIT:LOSE_ALL:15d1 F:MALE | EVIL | DUN_TEMPLE | DUN_TOWER F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | F:ONLY_ITEM | DROP_2D2 | ATTR_MULTI | SHAPECHANGER | ATTR_ANY | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | F:IM_ACID | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | LITE_2 S:1_IN_2 | S:HEAL | MIND_BLAST | BA_CHAO | S:S_SPIDER | S_HOUND | S_DEMON D:He is one of the few true masters of the art, being extremely skillful in D:all forms of unarmed combat and controlling the Logrus D:with disdainful ease. N:738:Khamul the Easterling G:W:D I:120:37d67:90:50:10 W:74:3:0:2250000 O:0:100:0 B:HIT:HURT:10d10 B:HIT:HURT:5d5 B:TOUCH:EXP_40 B:TOUCH:EXP_40 F:UNIQUE | MALE | CAN_SPEAK | DUN_CITY F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | CAN_SWIM F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS | F:HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE | LITE_2 S:1_IN_2 | S:TELE_LEVEL | BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | BO_MANA | S:BA_FIRE | BA_COLD | BA_NETH | ANIM_DEAD | S:S_UNDEAD | S_KIN D:A warrior-king of the East. Khamul is a powerful opponent, his skill in D:combat awesome and his form twisted by evil cunning. N:739:Hound of Tindalos G:Z:s I:120:41d17:30:50:0 W:81:3:0:160000 O:0:0:0 B:BITE:TIME:2d12 B:BITE:TIME:2d12 B:CLAW:HURT:3d3 F:FORCE_SLEEP | FRIENDS | RES_NETH | DUN_CAVERN | DUN_GRAVE F:INVISIBLE | PASS_WALL | EVIL | CAN_FLY | F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_5 | S:BLINK | TELE_TO | BR_NETH | BR_TIME D:"They are lean and athirst!... All the evil in the universe D:was concentrated in their lean, hungry bodies. Or had they D:bodies? I saw them only for a moment, I cannot be certain." N:740:Lesser kraken G:l:G I:120:40d62:30:75:80 W:79:2:0:1190000 O:25:25:50 B:CRUSH:HURT:16d12 B:CRUSH:HURT:16d12 B:CRUSH:HURT:16d12 B:CRUSH:HURT:16d12 F:FORCE_SLEEP | FORCE_MAXHP | AQUATIC | DUN_CAVERN | WILD_OCEAN | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | SMART | RES_WATE | F:EVIL | IM_ELEC | NO_CONF | NO_SLEEP | IM_POIS | IM_FIRE S:1_IN_6 | S:BLIND | CONF | SCARE | CAUSE_4 | CAUSE_3 | S:BA_WATE | DARKNESS | BR_DARK | TELE_TO D:An enormously fearsome and powerful inhabitant of the depths. It D:resembles a gargantuan octopus and its evil is almost tangible. N:741:Great ice wyrm G:D:w I:120:37d57:30:85:80 W:74:2:0:910000 O:50:50:0 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:BITE:COLD:5d14 F:DUN_CAVERN | DUN_LAIR | WILD_MOUNT2 | WILD_WASTE2 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | AURA_COLD | ATTR_MULTI F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_6 | S:BLIND | CONF | SCARE | S:BR_COLD D:An immense dragon capable of awesome destruction. You have never felt D:such extreme cold, or witnessed such an icy stare. Begone quickly or feel D:its wrath! N:742:Demilich G:L:U I:120:37d57:20:50:50 W:73:2:0:350000 O:0:0:100 B:TOUCH:EXP_VAMP:1d12 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:4d12 B:TOUCH:LOSE_DEX:4d12 F:DUN_GRAVE F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 | S:DRAIN_MANA | BRAIN_SMASH | S_HI_UNDEAD | S_UNDEAD | FORGET | S:TPORT | HEAL | ANIM_DEAD D:A lich who is partially immaterial, on its way to a new, ethereal form. N:743:The Phoenix G:B:r I:120:38d67:60:65:0 W:76:3:0:1300000 O:0:50:50 B:BITE:FIRE:12d6 B:BITE:FIRE:12d6 B:HIT:FIRE:9d12 B:HIT:FIRE:9d12 F:UNIQUE | CAN_SPEAK | RES_PLAS | AURA_FIRE | DUN_CAVERN | DUN_LAIR F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | GOOD | LITE_1 | LITE_2 F:ANIMAL | IM_ACID | IM_FIRE | IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_3 | S:BO_FIRE | BO_PLAS | BA_FIRE | S:BR_FIRE | BR_LITE | BR_PLAS D:A massive glowing eagle bathed in flames. The searing heat chars your D:skin and melts your armour. N:744:Nightcrawler G:W:D I:120:38d95:20:80:10 W:75:4:0:500000 O:40:0:50 B:STING:LOSE_CON:8d8 B:STING:LOSE_CON:8d8 B:BITE:ACID:10d10 B:BITE:ACID:10d10 F:FORCE_SLEEP | SMART | KILL_WALL | CAN_SWIM | DUN_CAVERN | DUN_GRAVE F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS | RES_TELE F:HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_4 | S:BLIND | SCARE | BRAIN_SMASH | S:BO_MANA | BO_NETH | BA_NETH | S:BR_NETH | S:S_UNDEAD D:This intensely evil creature bears the form of a gargantuan black worm. D:Its gaping maw is a void of blackness, acid drips from its steely hide. D:It is like nothing you have ever seen before, and a terrible chill runs D:down your spine as you face it. N:745:Lord of Change G:B:v I:130:41d65:30:75:20 W:82:20:0:1550000 O:0:0:100 B:CLAW:CONFUSE:10d10 B:CLAW:CONFUSE:10d10 B:BITE:BLIND:12d12 F:DUN_HELL F:DEMON | EVIL | ATTR_MULTI | DROP_GOOD | MOVE_BODY | NONLIVING | F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_2D2 | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | CAN_FLY | F:IM_ACID | IM_FIRE | IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_3 S:MIND_BLAST | BA_CHAO | SCARE | BRAIN_SMASH | DRAIN_MANA | S:S_HOUND | S_DEMON | S_DRAGON | TPORT | HASTE | CONF | S:TELE_AWAY | FORGET | ELDRITCH_HORROR D:The most powerful of Tzeentch's servitors. This demon looks like D:a huge bird-creature, with the head of a predatory bird and D:fantastically multi-coloured wings. N:746:Keeper of Secrets G:H:v I:130:38d83:30:75:20 W:75:20:0:1110000 O:0:0:100 B:HIT:CONFUSE:10d10 B:HIT:TERRIFY:10d10 B:HIT:BLIND:10d10 B:HIT:TERRIFY:10d10 F:DEMON | EVIL | DROP_GOOD | NONLIVING | CAN_SWIM | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_2D2 | F:OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | MOVE_BODY | F:IM_ACID | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 S:SCARE | BRAIN_SMASH | DRAIN_MANA | S:BR_CONF | S_DEMON | S_UNDEAD | TPORT | HEAL | S:TELE_AWAY | ELDRITCH_HORROR D:This demonic keeper of forbidden secrets looks like a hairless D:minotaur with extra arms, decorated with tattoos and nose rings. D:It is the embodiment of Slaanesh's perverted magic. N:747:Shudde M'ell G:w:s I:125:36d78:20:45:20 W:71:20:0:400000 O:50:0:40 B:CRUSH:SHATTER:55d2 B:CRUSH:SHATTER:55d2 B:TOUCH:LOSE_CON:1d2 B:TOUCH:LOSE_CON:1d2 F:DUN_DARKWATER F:IM_FIRE | RES_PLAS | IM_COLD | IM_POIS | RES_TELE F:KILL_WALL | ONLY_GOLD | DROP_3D2 | CAN_SWIM | F:EVIL | FORCE_MAXHP | SMART | UNIQUE S:1_IN_5 | S:SCARE | CONF | HOLD | S_DEMON | S_KIN | S:HEAL | HASTE | FORGET | BRAIN_SMASH S:BR_DARK | BR_ACID | BR_DISE | ELDRITCH_HORROR D:The most powerful and most evil of all Chthonians. D:"A great gray thing a mile long chanting and exuding strange acids... D:charging through the depths of the earth at a fantastic speed, in a D:dreadful fury... melting basaltic rocks like butter under a blowtorch." N:748:Hand druj G:s:D I:130:35d12:20:55:10 W:69:4:0:390000 O:0:0:0 F:DUN_GRAVE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | RES_TELE F:SMART | COLD_BLOOD | CAN_SWIM | F:EVIL | UNDEAD | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_1 | S:TELE_AWAY | BLIND | CONF | SCARE | CAUSE_3 | FORGET | DARKNESS D:A skeletal hand floating in the air, motionless except for its flexing D:fingers. N:749:Eye druj G:s:D I:130:38d19:20:45:10 W:76:4:0:640000 O:0:0:0 B:GAZE:EXP_80 B:GAZE:EXP_80 F:DUN_GRAVE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | RES_TELE F:SMART | COLD_BLOOD | F:EVIL | UNDEAD | CAN_SWIM | F:IM_FIRE | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_1 | S:BO_MANA | BO_NETH | BA_NETH | S:S_UNDEAD D:A bloodshot eyeball floating in the air, you'd be forgiven for assuming it D:harmless. N:750:Skull druj G:s:D I:130:40d25:20:60:10 W:80:4:0:1550000 O:0:0:0 B:BITE:EXP_80:4d4 B:BITE:PARALYZE:4d4 B:BITE:LOSE_INT:4d4 B:BITE:LOSE_WIS:4d4 F:DUN_GRAVE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | RES_TELE F:SMART | COLD_BLOOD | LITE_1 F:EVIL | UNDEAD | CAN_SWIM | F:IM_FIRE | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_1 | S:SLOW | CAUSE_4 | MIND_BLAST | BRAIN_SMASH | TRAPS | BO_PLAS | S:BO_NETH | BA_WATE | S:S_UNDEAD D:A glowing skull possessed by sorcerous power. It need not move, but D:merely blast you with mighty magic. N:751:Chaos vortex G:v:v I:140:38d13:100:40:0 W:76:1:0:185000 O:0:0:0 F:ATTR_MULTI | ATTR_ANY | DUN_PLANAR F:FORCE_SLEEP | NEVER_BLOW | F:RAND_50 | RAND_25 | CAN_FLY | F:EMPTY_MIND | BASH_DOOR | POWERFUL | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING S:1_IN_6 | S:BR_CHAO D:Void, nothingness, spinning destructively. N:752:Aether vortex G:v:B I:130:36d14:100:20:0 W:71:2:0:121000 O:0:0:0 B:ENGULF:ELEC:5d5 B:ENGULF:FIRE:3d3 B:ENGULF:ACID:3d3 B:ENGULF:COLD:3d3 F:ATTR_MULTI | ATTR_ANY | DUN_PLANAR F:FORCE_SLEEP | CAN_FLY | F:RAND_50 | RAND_25 | AURA_COLD | AURA_FIRE | AURA_ELEC | F:EMPTY_MIND | BASH_DOOR | POWERFUL | LITE_2 F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | F:RES_NETH | RES_NEXU | RES_PLAS S:1_IN_6 | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS | BR_LITE | S:BR_DARK | BR_SOUN | BR_CONF | BR_CHAO | BR_SHAR | BR_NETH | S:BR_WALL | BR_INER | BR_TIME | BR_GRAV | BR_PLAS | BR_NEXU D:An awesome vortex of pure magic, power radiates from its frame. N:753:Nidhogg the Hel-Drake G:D:D I:120:39d83:20:66:70 W:77:2:0:1600000 O:50:50:0 B:CLAW:LOSE_CON:8d12 B:CLAW:LOSE_CON:8d12 B:BITE:EXP_VAMP:8d15 B:BITE:EXP_VAMP:8d15 F:UNIQUE | CAN_FLY | RES_NETH | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | SMART | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | IM_POIS | F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_SPEAK | IM_ACID | F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_5 | S:CAUSE_3 | BR_NETH | BR_COLD | S:BR_ACID | BR_POIS | S:S_DRAGON | S_UNDEAD D:In the bowels of Hel, the dread Nidhogg, a dragon blacker than the D:night, feasts on the essences of the dead. N:754:The Lernean Hydra G:M:G I:120:39d81:20:70:20 W:78:2:0:1240000 O:50:0:50 B:BITE:POISON:8d6 B:BITE:POISON:8d6 B:BITE:FIRE:12d6 B:BITE:FIRE:12d6 F:UNIQUE | DUN_DARKWATER F:FORCE_SLEEP | FORCE_MAXHP | SMART | DROP_CORPSE F:ONLY_GOLD | DROP_4D2 | WILD_SHORE | WILD_SWAMP2 | F:OPEN_DOOR | BASH_DOOR | KILL_BODY | POWERFUL | F:ANIMAL | IM_FIRE | IM_POIS | F:NO_CONF | NO_SLEEP | CAN_SWIM | S:1_IN_3 | S:SCARE | S:BO_FIRE | BO_PLAS | BA_FIRE | BA_POIS S:BR_FIRE | BR_POIS | S:S_HYDRA D:A massive legendary hydra. It has twelve powerful heads. Its many eyes D:stare at you as clouds of smoke and poisonous vapour rise from its D:seething form. N:755:Thuringwethil G:V:D I:130:38d74:20:72:10 W:75:4:0:1650000 O:0:50:50 B:BITE:HURT:5d8 B:BITE:EXP_VAMP:6d6 B:HIT:CONFUSE:6d6 B:HIT:CONFUSE:6d6 F:UNIQUE | FEMALE | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SPEAK | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_3 | S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA | S:BRAIN_SMASH | BA_NETH | S_KIN D:Chief messenger between Sauron and Morgoth, she is surely the most deadly D:of her vampire race. At first she is charming to meet, but her wings and D:eyes give away her true form. N:756:Great hell wyrm G:D:r I:120:39d97:40:65:40 W:78:2:0:1040000 O:50:50:0 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:CLAW:HURT:4d12 B:BITE:HURT:5d14 F:DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 | WILD_WASTE2 F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE | ATTR_MULTI F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | AURA_FIRE | LITE_1 | LITE_2 F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_6 | S:BLIND | CONF | SCARE | S:BR_FIRE D:A vast dragon of immense power. Fire leaps continuously from its huge D:form. The air around it scalds you. Its slightest glance burns you, and D:you truly realize how insignificant you are. N:757:Hastur the Unspeakable G:H:b I:120:38d103:20:75:10 W:76:40:0:2300000 O:30:30:30 B:CRUSH:HURT:14d8 B:CRUSH:HURT:14d8 B:BITE:EXP_80:6d6 B:BITE:EXP_80:6d6 F:DUN_CAVERN F:UNIQUE | CAN_SWIM | EVIL | UNDEAD | F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SPEAK | AURA_ELEC | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | POWERFUL | NONLIVING | F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_3 | S:BLIND | HOLD | SCARE | CAUSE_4 | DRAIN_MANA | S:BRAIN_SMASH | BA_WATE | S_DEMON | HASTE | ELDRITCH_HORROR S:TPORT | TELE_AWAY | TELE_TO | HEAL | BR_DARK | BR_NETH D:Its form is partially that of a D:reptile, partially that of a gigantic octopus. It will destroy you. N:758:Bloodthirster G:U:r I:144:39d81:30:90:20 W:77:30:0:1050000 O:0:100:0 B:HIT:HURT:50d1 B:HIT:HURT:50d1 B:HIT:HURT:25d2 F:DUN_HELL F:DEMON | EVIL | DROP_GOOD | REGENERATE | CAN_FLY | NONLIVING | F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_2D2 | F:OPEN_DOOR | BASH_DOOR | RES_NETH | RES_NEXU | RES_TELE | MOVE_BODY | F:IM_ACID | IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_10 S:ELDRITCH_HORROR | D:Khorne's mightiest servant, a winged hound-demon walking on D:two paws and wielding a mighty axe and a whip in the other D:two. Intelligent, bloodthirsty eyes behold you from inside D:the bloody demon armour. N:759:Draconic quylthulg G:Q:G I:120:37d14:20:1:0 W:73:3:0:135000 O:0:0:0 F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | F:INVISIBLE | EMPTY_MIND | DUN_TEMPLE | DUN_CAVERN F:ANIMAL | EVIL | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK | TPORT | S:S_DRAGON D:It looks like it was once a dragon corpse, now deeply infected with D:magical bacteria that make it pulse in a foul and degrading way. N:760:Nyogtha, the Thing that Should not Be G:j:D I:130:37d105:20:60:20 W:74:20:0:990000 O:50:0:40 B:CRUSH:ACID:10d6 B:CRUSH:ACID:10d6 B:CRUSH:ACID:10d6 B:CRUSH:HURT:16d16 F:UNIQUE | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | SMART | CAN_SWIM | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:REGENERATE | KILL_ITEM | F:BASH_DOOR | EVIL | NO_CONF | NO_SLEEP | KILL_BODY | F:HURT_LITE | POWERFUL | RES_NETH | NONLIVING | F:IM_ACID | IM_FIRE | RES_PLAS | IM_POIS | IM_COLD | RES_TELE S:1_IN_5 | S:BRAIN_SMASH | MIND_BLAST | HASTE | TPORT | ELDRITCH_HORROR | S:S_DEMON | S_UNDEAD | S_HI_UNDEAD | S_KIN | S:BR_DARK | BR_NUKE | BR_ACID | BR_POIS D:"...a little finger of blackness crept out from beneath its edge D:a great wave of iridescent blackness, neither liquid nor solid, D:a frightful gelatinous mass." N:761:Ahtu, Avatar of Nyarlathotep G:#:D I:130:36d117:30:60:15 W:72:30:0:910000 O:0:30:60 B:CRUSH:HURT:13d13 B:CRUSH:FIRE:10d10 B:CRUSH:HURT:13d13 B:CRUSH:FIRE:10d10 F:DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | CAN_SPEAK | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | MOVE_BODY | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | F:EVIL | DEMON | NONLIVING | CAN_SWIM S:1_IN_6 S:BR_FIRE | S_DEMON | CAUSE_4 | BR_PLAS | BR_NETH | BRAIN_SMASH | S:S_UNDEAD | BA_DARK | ELDRITCH_HORROR | D:"Higher already than the giants of the forest ringing it, the D:fifty-foot-thick column... sprouted a ring of tendrils, ruddy and D:golden and glittering overall with inclusions of quartz." N:762:Fundin Bluecloak G:h:G I:130:40d95:25:99:10 W:79:2:0:1400000 O:0:80:20 B:HIT:HURT:10d10 B:HIT:HURT:8d6 B:HIT:HURT:8d6 B:HIT:HURT:8d6 F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DUN_LAIR | DUN_MINE F:FORCE_SLEEP | FORCE_MAXHP | FRIENDLY | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | CAN_SWIM F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | S:1_IN_4 | S:HEAL | BLIND | CONF | SCARE | CAUSE_3 | CAUSE_4 | BRAIN_SMASH | S:FORGET | S:S_MONSTERS D:He is one of the greatest dwarven priests to walk the earth. Fundin has D:earned a high position in the church, and his skill with both weapon and D:spell only justify his position further. His combination of both dwarven D:strength and priestly wisdom are a true match for any adventurer. N:763:Dworkin Barimen G:p:v I:130:39d87:25:99:10 W:77:2:0:1550000 O:50:25:25 B:HIT:CONFUSE:4d8 B:HIT:TERRIFY:5d6 B:HIT:PARALYZE:5d6 B:HIT:BLIND:4d8 F:DUN_CAVERN | DUN_TOWER | DUN_PLANAR F:UNIQUE | MALE | AMBERITE | DROP_CORPSE | DROP_CHOSEN | F:FORCE_SLEEP | FORCE_MAXHP | SHAPECHANGER | CAN_SPEAK | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | CAN_FLY | F:OPEN_DOOR | BASH_DOOR | ATTR_MULTI | RES_DISE | ATTR_ANY | RES_TELE F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | LITE_1 S:1_IN_4 | S:HEAL | BLIND | CONF | SCARE | CAUSE_4 | BRAIN_SMASH | S:FORGET | S_MONSTERS | BA_CHAO | BLINK | TPORT | TELE_TO | S:TELE_AWAY | S_DEMON D:Once this now gnomish creature created the universe with the Jewel D:of Judgement - or so you have been told. There is little sanity left D:in his present form even if he still has the power. "He was a small D:man. Tiny, might be an even better word. He was around five feet tall D:and a hunchback. His hair and beard were as heavy as (Corwin's). The D:only distinguishing features in that great mass of fur were D:his long, hook nose and his almost black eyes, now squinted D:against the light... Dworkin: 'I am Oberon's prisoner.' Corwin: D:'Oh? None of us knew that Dad had locked you up.' (Corwin) heard D:(Dworkin) weeping. 'Yes,' he said after a long time. 'He didn't D:trust me.' 'Why not?' 'I told him I'd thought of a way to D:destroy Amber. I described it to him, and he locked me in.'" N:764:Uriel, Angel of Fire G:A:R I:130:39d99:40:80:10 W:77:3:0:1550000 O:0:100:0 B:HIT:FIRE:9d12 B:HIT:FIRE:4d6 B:HIT:HURT:10d10 B:HIT:HURT:10d10 F:DUN_PLANAR F:UNIQUE | MALE | CAN_SPEAK | NO_FEAR | GOOD | AURA_FIRE | REFLECTING | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | LITE_2 F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | RES_TELE S:1_IN_2 | S:TELE_TO | BLIND | S:BO_FIRE | BO_MANA | BA_FIRE | S:BR_FIRE | S:S_ANGEL D:A creature of godly appearance, you dare not challenge Uriel's supremacy. D:Those who stood against him before are but a memory, cremated by his D:mastery of elemental fire. N:765:Azriel, Angel of Death G:A:D I:130:37d122:40:85:10 W:73:3:0:1450000 O:0:100:0 B:TOUCH:EXP_VAMP:1d12 B:HIT:BLIND:10d5 B:HIT:HURT:10d10 B:HIT:HURT:10d10 F:DUN_PLANAR F:UNIQUE | MALE | CAN_SPEAK | RES_NETH | NO_FEAR | GOOD | F:FORCE_SLEEP | FORCE_MAXHP | SMART | REFLECTING | AURA_COLD | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | CAN_FLY | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_ACID | IM_FIRE | IM_COLD | IM_POIS | RES_TELE S:1_IN_2 | S:TELE_TO | BLIND | S:BO_MANA | BO_NETH | BA_NETH | S:BR_NETH | S:S_ANGEL D:Azriel commands awesome power, his visage holy enough to shrivel your D:soul. You shriek with disbelief as his mastery of death draws you to your D:grave. It is truly beyond all but the mightiest of warriors to stand D:against him and live. N:766:Ancalagon the Black G:D:D I:120:39d135:20:62:70 W:78:3:0:1950000 O:50:50:0 B:CLAW:HURT:5d12 B:CLAW:HURT:6d12 B:CLAW:HURT:8d12 B:BITE:HURT:10d14 F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DUN_LAIR | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | IM_ACID | IM_FIRE | F:NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | CONF | SCARE | S:BR_ACID | S:S_DRAGON | S_HI_DRAGON D:'Rushing Jaws' is his name, and death is his game. No dragon of the brood D:of Glaurung can match him. N:767:Daoloth, the Render of the Veils G:U:s I:120:38d133:20:62:70 W:76:30:0:2250000 O:0:0:100 B:TOUCH:CONFUSE:5d12 B:TOUCH:CONFUSE:5d12 B:TOUCH:CONFUSE:5d12 B:TOUCH:CONFUSE:5d12 F:UNIQUE | CAN_SPEAK | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DUN_HELL F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:IM_ACID | IM_FIRE | IM_POIS | IM_COLD | F:NEVER_MOVE | RES_NEXU | REFLECTING | PASS_WALL | F:NO_CONF | NO_SLEEP | EVIL S:1_IN_3 | S:TELE_AWAY | S_MONSTERS | TELE_LEVEL | BR_NEXU | TPORT | BLINK S:ELDRITCH_HORROR D:"Not shapeless, but so complex that the eye could recognize no D:describable shape. There were hemispheres and shining metal, D:coupled by long plastic rods. The rods were of a flat gray color, D:so that he could not make out which were nearer; they merged into D:a flat mass from which protruded individual cylinders. As he looked D:at it, he had a curious feeling that eyes gleamed from between D:these rods; but wherever he glanced at the construction, he saw D:only the spaces between them." N:768:Nightwalker G:W:D I:130:42d55:20:88:10 W:83:4:0:910000 O:30:0:70 B:HIT:UN_BONUS:10d10 B:HIT:UN_BONUS:10d10 B:HIT:UN_BONUS:7d7 B:HIT:UN_BONUS:7d7 F:FORCE_SLEEP | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | CAN_SWIM | DUN_GRAVE F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | RES_TELE F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_4 | S:BLIND | SCARE | BRAIN_SMASH | S:BO_MANA | BO_NETH | BA_NETH | S:S_UNDEAD D:A huge giant garbed in black, more massive than a titan and stronger than D:a dragon. With terrible blows, it breaks your armour from your back, D:leaving you defenseless against its evil wrath. It can smell your fear, D:and you in turn smell the awful stench of death as this ghastly figure D:strides towards you menacingly. N:769:Raphael, the Messenger G:A:v I:130:40d132:40:90:10 W:80:3:0:2500000 O:0:100:0 B:HIT:UN_BONUS:6d8 B:HIT:FIRE:4d6 B:HIT:HURT:10d10 B:HIT:HURT:10d10 F:UNIQUE | MALE | FORCE_MAXHP | CAN_SPEAK | NO_FEAR | GOOD | DUN_PLANAR F:FORCE_SLEEP | SMART | AURA_ELEC | REFLECTING | RES_TELE | F:ESCORT | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | LITE_1 | LITE_2 S:1_IN_2 | S:TELE_TO | BLIND | BO_MANA | S_ANGEL | S_KIN | INVULNER D:Commanding a legion of angels, Raphael will destroy you for your sins. He D:will crush you like the pitiful insignificant being he sees you to be. D:Your very soul will be taken into judgement by his supreme authority as he D:cleanses the world of evil. N:770:Artsi the Champion of Chaos G:h:G I:130:40d137:25:88:10 W:79:2:0:1070000 O:0:100:0 B:HIT:EXP_VAMP:6d6 B:HIT:EXP_VAMP:8d8 B:HIT:EXP_VAMP:10d10 F:UNIQUE | MALE | CAN_SPEAK | DUN_TEMPLE | CAN_SWIM F:FORCE_SLEEP | FORCE_MAXHP | SMART | DROP_CORPSE F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | RES_NEXU | RES_NETH | EVIL | LITE_2 F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | S:1_IN_4 | S:BA_FIRE | BA_CHAO | CONF | TPORT | S_DEMON | BR_CHAO | BA_MANA D:He is one of the greatest warriors of chaos to walk the earth. D:His bloody blade has slain thousands and tens of thousands, and still D:hungers for more. N:771:Saruman of Many Colours G:p:v I:120:40d95:100:50:0 W:80:1:0:1700000 O:0:0:100 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:UNIQUE | MALE | ATTR_MULTI | CAN_SPEAK | DUN_CITY | DUN_TOWER F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | RES_TELE F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:DROP_CHOSEN | CAN_FLY F:SMART | OPEN_DOOR | BASH_DOOR | F:EVIL | IM_FIRE | IM_COLD | LITE_2 F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE | S:CAUSE_4 | MIND_BLAST | FORGET | TRAPS | ANIM_DEAD | S:BO_ICEE | BA_ACID | BA_FIRE | BA_COLD | BA_WATE | S:S_UNDEAD | S_DEMON | S_DRAGON D:Originally known as the White, Saruman fell prey to Sauron's wiles. He D:searches forever for the One Ring, to become a mighty Sorcerer-King of the D:world. N:772:Gandalf the Grey G:p:v I:120:40d86:101:50:0 W:79:7:0:1180000 O:0:0:100 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 B:HIT:TERRIFY:5d5 B:HIT:TERRIFY:5d5 F:UNIQUE | MALE | CAN_SPEAK | FRIENDLY | DUN_LAIR | DUN_CAVERN | DUN_MINE F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | RES_TELE | CAN_FLY F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:SMART | OPEN_DOOR | BASH_DOOR | F:GOOD | IM_FIRE | IM_COLD | LITE_2 F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE | S:CAUSE_4 | BRAIN_SMASH | FORGET | TRAPS | S:BA_FIRE | BO_FIRE | BO_PLAS | BO_MANA | S:S_MONSTER | S_ANGEL | S_DRAGON | S_KIN D:The wizard who opposed Saruman, and in the end, was the only D:one of the Istari to succeed in his task. Gandalf is very D:wise and specializes in fire magic. N:773:Brand, Mad Visionary of Amber G:p:v I:120:40d88:100:50:0 W:79:1:0:2100000 O:0:0:100 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 B:HIT:CONFUSE:5d5 B:HIT:UN_POWER:5d5 F:DUN_TOWER F:UNIQUE | MALE | ATTR_MULTI | CAN_SPEAK | AMBERITE | ATTR_ANY | RES_TELE F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:DROP_CHOSEN | F:SMART | OPEN_DOOR | BASH_DOOR | CAN_FLY F:EVIL | IM_FIRE | IM_COLD | LITE_2 F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | CONF | S:TPORT | BLINK | BA_FIRE | BA_COLD | S:DRAIN_MANA | CAUSE_4 | BA_ACID | TELE_AWAY | S:MIND_BLAST | TRAPS | HEAL | S:HASTE | BRAIN_SMASH | BA_CHAO | BA_DARK D:Brand sees himself as a hero, the god creator D:and absolute monarch of a future world. Unfortunately he needs D:to erase the existing world to make his new world. "...a figure both D:like Bleys and (Corwin). (Corwin's) features, though smaller, his eyes, D:Bleys' hair, beardless. He wore a riding suit of green and sat atop a white D:horse... There was a quality of both strength and weakness, questing and D:abandonment about him." N:774:Shadowlord G:G:b I:120:44d48:20:75:10 W:88:2:0:1700000 O:10:10:70 B:HIT:EXP_40:6d6 B:HIT:EXP_40:6d6 B:HIT:LOSE_STR:4d6 B:GAZE:TERRIFY:4d6 F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE F:ONLY_ITEM | DROP_4D2 | F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL | F:EVIL | UNDEAD | CAN_FLY | F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_3 | S:HOLD | DRAIN_MANA | BLIND | S_UNDEAD | CONF | S:SCARE | TELE_TO | TPORT | BRAIN_SMASH | ANIM_DEAD S:BA_NETH | DARKNESS | SHRIEK D:An aura of hatred, cowardice and falsehood surrounds you as this D:cloaked figure floats towards you. N:775:Greater kraken G:l:G I:120:42d67:30:62:80 W:83:2:0:1950000 O:10:80:0 B:CRUSH:HURT:15d15 B:CRUSH:HURT:15d15 B:CRUSH:HURT:15d15 B:CRUSH:HURT:15d15 F:FORCE_SLEEP | FORCE_MAXHP | AQUATIC | DUN_CAVERN | WILD_OCEAN | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | SMART | RES_WATE | F:EVIL | IM_ELEC | NO_CONF | NO_SLEEP | IM_POIS | IM_FIRE S:1_IN_6 | S:BLIND | CONF | SCARE | CAUSE_4 | CAUSE_3 | TELE_TO | TELE_AWAY | S:BA_WATE | DARKNESS | BR_DARK | BR_ACID | BR_POIS D:An enormously fearsome and powerful inhabitant of the depths. It D:resembles a gargantuan octopus and its evil is almost tangible. N:776:Archlich G:L:B I:120:43d57:20:60:50 W:85:2:0:1850000 O:0:0:100 B:TOUCH:EXP_VAMP:1d16 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:8d12 B:TOUCH:LOSE_DEX:8d12 F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE | CAN_FLY | DUN_GRAVE | DUN_TOWER F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 | S:DRAIN_MANA | BRAIN_SMASH | S_HI_UNDEAD | S_UNDEAD | FORGET | S:TPORT | HEAL | S_KIN | S_DEMON | BA_NETH | ANIM_DEAD D:A lich who has reached its ultimate evolutionary stage: a completely D:immaterial state. N:777:Bast, Goddess of Cats G:f:o I:130:44d77:100:100:0 W:88:3:0:2300000 O:60:20:10 B:HIT:CONFUSE:12d12 B:TOUCH:LOSE_DEX:2d12 B:HIT:BLIND:10d5 B:HIT:PARALYZE:15d1 F:UNIQUE | FEMALE | CAN_SPEAK | DUN_PLANAR | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | EVIL | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:TELE_TO | HEAL | S_KIN D:"Beauty -- coolness -- aloofness -- philosophical repose -- D:self-sufficiency -- untamed mastery -- what else can we find D:these things with even half the perfection and completeness D:that mark their incarnation in the peerless and softly gliding D:cat?" N:778:Jabberwock G:H:R I:130:44d51:35:62:255 W:87:4:0:1060000 O:30:50:20 B:CLAW:HURT:15d15 B:CLAW:HURT:15d15 B:BITE:HURT:10d10 B:BITE:HURT:10d10 F:ATTR_MULTI | EVIL | DUN_CAVERN | DUN_LAIR F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE F:ONLY_ITEM | DROP_60 | DROP_90 | F:BASH_DOOR | CAN_FLY | DROP_CORPSE F:ANIMAL S:1_IN_5 | S:CAUSE_4 | S:BR_CHAO D:"Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" N:779:Chaos hound G:Z:v I:120:45d30:30:50:0 W:90:1:0:450000 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:ATTR_MULTI | ATTR_ANY | DROP_SKELETON | DROP_CORPSE F:FORCE_SLEEP | DUN_PLANAR | DUN_CAVERN F:FRIENDS | F:BASH_DOOR | F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_5 | S:BR_CHAO D:A constantly changing canine form, this hound rushes towards you as if D:expecting mayhem and chaos ahead. It appears to have an almost kamikaze D:relish for combat. You suspect all may not be as it seems. N:780:Vlad Dracula, Prince of Darkness G:V:b I:130:44d80:20:83:10 W:87:4:0:1850000 O:0:10:90 B:BITE:EXP_VAMP:8d8 B:BITE:EXP_VAMP:8d8 B:HIT:CONFUSE:8d8 B:HIT:CONFUSE:8d8 F:UNIQUE | MALE | DUN_GRAVE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SPEAK | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_3 | S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA | S:BRAIN_SMASH | BA_NETH | S_KIN | DARKNESS | BA_DARK D:The most feared of all vampires, the Prince of Darkness himself. N:781:Ultimate beholder G:e:o I:120:42d67:30:40:10 W:84:4:0:390000 O:0:0:0 B:GAZE:EXP_40:2d5 B:GAZE:PARALYZE:2d5 B:GAZE:LOSE_INT:2d6 B:GAZE:UN_POWER:2d6 F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | CAN_FLY | F:BASH_DOOR | FEMALE | SMART | DROP_CORPSE | DUN_RUIN F:EVIL | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | CONF | SCARE | DRAIN_MANA | MIND_BLAST | FORGET | S:DARKNESS | BO_ACID | BO_FIRE | BO_COLD | S_KIN | BO_MANA D:A disembodied eye, surrounded by twelve smaller eyes on stalks. D:This is a beholder hive-mother, far more powerful than normal D:beholders. N:782:Leviathan G:D:b I:120:41d86:40:90:30 W:81:3:0:1200000 O:0:0:0 B:BITE:FIRE:5d12 B:BITE:FIRE:5d12 B:CRUSH:HURT:6d15 B:CRUSH:HURT:6d15 F:FORCE_SLEEP | FORCE_MAXHP | AQUATIC | DUN_CAVERN | WILD_OCEAN | F:IM_FIRE | IM_COLD | IM_POIS | EVIL | F:DRAGON | NO_CONF | NO_SLEEP | NO_FEAR | DROP_CORPSE S:1_IN_9 S:BR_FIRE | BR_ACID | BR_DARK | S:S_DRAGON | S_HYDRA | HEAL | CONF | DARKNESS D:An enormous, evil sea-dragon. N:783:Great Wyrm of Chaos G:D:v I:120:43d74:40:85:20 W:85:2:0:3700000 O:50:50:0 B:CLAW:HURT:5d12 B:CLAW:HURT:5d12 B:CLAW:HURT:6d12 B:BITE:HURT:8d14 F:ATTR_MULTI | ATTR_ANY F:DUN_CAVERN | DUN_LAIR | WILD_WASTE2 | WILD_SWAMP2 | WILD_MOUNT2 | WILD_FOREST2 F:FORCE_SLEEP | FORCE_MAXHP | RES_DISE | DROP_CORPSE | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | F:EVIL | DRAGON | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | CONF | SCARE | S:BR_CHAO | BR_DISE | S:S_DRAGON D:A massive dragon of changing form. As you watch, it appears first fair D:and then foul. Its body is twisted by chaotic forces as it strives to D:stay real. Its very existence distorts the universe around it. N:784:Great Wyrm of Law G:D:W I:120:43d74:40:85:255 W:85:2:0:3400000 O:50:50:0 B:CLAW:HURT:5d12 B:CLAW:HURT:5d12 B:CLAW:HURT:6d12 B:BITE:HURT:8d14 F:DUN_CAVERN | DUN_LAIR | WILD_FOREST2 F:FORCE_SLEEP | FORCE_MAXHP | GOOD | DROP_CORPSE F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | ATTR_MULTI F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | CONF | SCARE | S:BR_SOUN | BR_SHAR | S:S_DRAGON D:A massive dragon of powerful intellect. It seeks to dominate the universe D:and despises all other life. It sees all who do not obey it as mere D:insects to be crushed underfoot. N:785:Great Wyrm of Balance G:D:s I:120:46d75:40:85:255 W:91:4:0:1400000 O:50:50:0 B:CLAW:HURT:5d12 B:CLAW:HURT:5d12 B:CLAW:HURT:6d12 B:BITE:HURT:8d14 F:DUN_CAVERN | DUN_LAIR | WILD_FOREST2 F:ATTR_MULTI | ATTR_ANY | GOOD | EVIL | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | RES_DISE | F:DRAGON | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | CONF | SCARE | S:BR_SOUN | BR_CHAO | BR_SHAR | BR_DISE | S:S_DRAGON | S_HI_DRAGON D:A massive dragon, it is thousands of D:years old and seeks to maintain the Cosmic Balance. It sees you as an D:upstart troublemaker without the wisdom to control your actions. N:786:Shambler G:E:W I:130:46d77:40:75:50 W:92:4:0:3100000 O:20:20:60 B:CLAW:HURT:3d12 B:CLAW:HURT:3d12 B:CRUSH:HURT:8d12 B:CRUSH:HURT:8d12 F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | DUN_PLANAR F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | CAN_SWIM | DROP_CORPSE | F:BASH_DOOR | OPEN_DOOR | POWERFUL | MOVE_BODY | F:NO_CONF | NO_SLEEP | EVIL S:1_IN_3 | S:BR_ELEC D:This elemental creature is power incarnate; it looks like a huge polar bear D:with a huge gaping maw instead of a head. N:787:Hypnos, Lord of Sleep G:P:G I:130:43d84:20:75:10 W:86:2:0:1750000 O:0:0:100 B:TOUCH:LOSE_ALL:2d25 B:GAZE:PARALYZE:1d20 B:GAZE:TERRIFY:1d20 B:GAZE:BLIND:1d20 F:UNIQUE | MALE | CAN_SPEAK | ATTR_MULTI | DUN_CAVERN | DUN_PLANAR F:RES_DISE | RES_NEXU | CAN_FLY | F:FORCE_SLEEP | FORCE_MAXHP | EVIL | SMART | DROP_CORPSE F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:NO_SLEEP | NO_CONF | NO_STUN | IM_POIS | IM_COLD | IM_FIRE S:1_IN_3 S:BR_NEXU | BR_CHAO | HOLD | SLOW | BR_INER | BA_NUKE | S:MIND_BLAST | BRAIN_SMASH | CONF | BLIND | TELE_TO | HEAL | S:TELE_AWAY | TELE_LEVEL | TPORT | S_UNDEAD | S_DEMON | DRAIN_MANA D:"Young with the youth that is outside time, and with a beauteous D:bearded face, curved, smiling lips, Olympian brow; and dense locks D:waving and poppy-crowned." N:788:Glaaki G:l:g I:130:43d85:20:75:10 W:86:20:0:2200000 O:0:0:100 B:STING:HURT:20d1 B:STING:DISEASE:20d1 B:CRUSH:HURT:3d20 F:UNIQUE | CAN_SPEAK | ATTR_MULTI | DUN_HORROR F:RES_NEXU | RES_WATE | CAN_SWIM | REGENERATE | F:FORCE_SLEEP | FORCE_MAXHP | EVIL | SMART | DEMON | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:NO_SLEEP | NO_CONF | NO_STUN | F:IM_POIS | IM_COLD | IM_ACID S:1_IN_5 S:BA_WATE | S_HYDRA | S_DRAGON | S_DEMON | SCARE | S:BLIND | CONF | CAUSE_4 | BR_POIS | ELDRITCH_HORROR D:"From an oval body protruded countless thin, pointed spines of D:multi-colored metal; at the more rounded end of the oval a D:circular, thick-lipped mouth formed the center of a spongy D:face, from which rose three yellow eyes on thin stalks. Around D:the underside of the body were many white pyramids, presumably D:used for locomotion. The diameter of the body must have been D:at least ten feet at its least wide..." N:789:Bleys, Master of Manipulation G:p:U I:130:44d80:100:50:50 W:87:1:0:2700000 O:0:100:0 B:HIT:HURT:8d15 B:HIT:HURT:8d15 B:TOUCH:EAT_ITEM F:DUN_CITY | DUN_CAVERN F:UNIQUE | MALE | CAN_SPEAK | AMBERITE | RES_TELE | DROP_CORPSE | CAN_SWIM F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | DROP_SKELETON | F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:EVIL | IM_FIRE | IM_ELEC | SMART | REGENERATE | OPEN_DOOR | BASH_DOOR | F:LITE_2 S:1_IN_3 | S:SCARE | BLIND | CONF | TPORT | BLINK | TELE_AWAY | TELE_TO S:TELE_LEVEL | BA_COLD | BA_FIRE | TRAPS | MIND_BLAST | S:S_MONSTERS D:This cunning Amberite is the master of subtle ploys. "...a fiery bearded, D:flame-crowned man, dressed all in red and orange, mainly of silk stuff, D:and he held a sword in his right hand and a glass of wine in his left, D:and the devil himself danced behind his eyes... His chin was light, but the D:beard covered it. His sword was inlaid with an elaborate filigree of a D:golden color. He wore two huge rings on his right hand and one on his D:left: an emerald, a ruby, and a sapphire, respectively." N:790:Great Wyrm of Many Colours G:D:v I:120:45d81:40:85:255 W:90:4:0:3300000 O:50:50:0 B:CLAW:HURT:6d12 B:CLAW:HURT:6d12 B:CLAW:HURT:6d12 B:BITE:HURT:9d14 F:ATTR_MULTI | CAN_FLY | EVIL | DUN_LAIR | DUN_CAVERN | WILD_MOUNT2 F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | AURA_ELEC | RES_TELE F:IM_FIRE | IM_ACID | IM_POIS | IM_COLD | IM_ELEC | AURA_COLD | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE F:DRAGON | NO_CONF | NO_SLEEP | LITE_2 S:1_IN_3 | S:BR_POIS | BR_ELEC | BR_ACID | BR_FIRE | BR_COLD | S:CONF | SCARE | BLIND D:A huge dragon whose scales shimmer in myriad hues. N:791:Fiona the Sorceress G:p:y I:130:44d78:100:50:50 W:87:1:0:1900000 O:10:0:90 B:HIT:POISON:8d15 B:HIT:POISON:8d15 B:TOUCH:LOSE_CHR B:TOUCH:LOSE_CON F:DUN_TOWER F:UNIQUE | FEMALE | CAN_SPEAK | AMBERITE | ONLY_ITEM | RES_TELE F:DROP_CHOSEN | DROP_CORPSE | DROP_SKELETON | CAN_FLY F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | LITE_2 F:DROP_3D2 | DROP_GOOD F:EVIL | IM_POIS | IM_ELEC | SMART | REGENERATE | OPEN_DOOR | BASH_DOOR | S:1_IN_3 | S:SCARE | BLIND | CONF | TPORT | BLINK | TELE_AWAY | TELE_TO S:TRAPS | CAUSE_4 | BA_POIS | S_DEMON S:S_MONSTERS D:She is beautiful and deadly. "...I have always been very fond of Fiona. D:She is certainly the loveliest, most civilized of (all Amberites)." N:792:Tselakus, the Dreadlord G:G:R I:130:44d104:20:75:10 W:88:2:0:2350000 O:0:80:20 B:HIT:HURT:10d10 B:HIT:HURT:10d10 B:HIT:LOSE_STR:4d6 B:HIT:LOSE_STR:4d6 F:UNIQUE | MALE | DUN_GRAVE F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:INVISIBLE | COLD_BLOOD | PASS_WALL | F:EVIL | UNDEAD | IM_COLD | AURA_COLD | F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | HOLD | CONF | S:BA_DARK | BA_NETH | S:S_AMBERITES | S_HI_UNDEAD | S_KIN D:This huge affront to existence twists and tears at the fabric of space. D:Darkness itself recoils from the touch of Tselakus as he leaves a trail D:of death and destruction. Mighty claws rend reality as he D:annihilates all in his path to your soul! N:793:Sky Drake G:D:B I:130:46d92:40:100:255 W:91:4:0:5800000 O:50:50:0 B:CLAW:HURT:8d12 B:CLAW:HURT:8d12 B:CLAW:HURT:8d12 B:BITE:ELEC:9d15 F:DUN_LAIR | DUN_PLANAR | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | AURA_COLD | DROP_CORPSE F:IM_ELEC | GOOD | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:BASH_DOOR | POWERFUL | MOVE_BODY | LITE_1 | LITE_2 F:DRAGON | NO_CONF | NO_SLEEP | RES_TELE S:1_IN_3 | S:BR_ELEC | BR_GRAV | BR_LITE | S:S_DRAGON | S_HI_DRAGON | S:SCARE | BLIND D:The mightiest elemental dragon of air, it can destroy you with ease. N:794:Julian, Master of Arden Forest G:p:g I:130:43d90:100:50:20 W:86:1:0:1750000 O:0:100:0 B:HIT:HURT:9d15 B:HIT:HURT:9d15 B:HIT:LOSE_CON:1d30 F:DUN_LAIR F:UNIQUE | MALE | CAN_SPEAK | AMBERITE | RES_TELE | DROP_SKELETON F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | DROP_CORPSE F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:DROP_CHOSEN | LITE_2 F:EVIL | IM_POIS | IM_ELEC | IM_COLD | SMART | REGENERATE F:OPEN_DOOR | BASH_DOOR | CAN_SWIM S:1_IN_4 | S:TPORT | TELE_TO | ARROW | SHRIEK | SCARE | DARKNESS | S:S_ANT | S_SPIDER | S_HOUND | S_HYDRA | TRAPS | BO_WATE | BO_ELEC D:Julian is at home in wild woodlands. He enjoys hunting a challenging D:prey: you. "Julian, dark hair hanging long, blue eyes containing neither D:passion nor compassion. He was dressed completely in scaled white armor, D:not silver or metallic-colored, but looking as if it had been enameled." N:795:Tiamat, Celestial Dragon of Evil G:D:v I:130:44d160:20:62:70 W:88:4:0:3600000 O:60:40:0 B:CLAW:HURT:6d12 B:CLAW:HURT:8d12 B:CLAW:HURT:8d12 B:BITE:HURT:10d14 F:ATTR_MULTI | DUN_CAVERN | DUN_HELL F:UNIQUE | FEMALE | CAN_FLY | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DRAGON | LITE_2 F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | CONF | SCARE | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS | S:S_HI_DRAGON D:Usually found guarding the first plane of Hell, Tiamat is a formidable D:opponent, her five heads breathing death to all who stand against her. N:796:The Norsa G:H:B I:130:43d163:20:62:70 W:85:40:0:2200000 O:50:0:50 B:CRUSH:ACID:8d12 B:CRUSH:FIRE:8d12 B:CRUSH:ELEC:8d12 B:CRUSH:POISON:10d14 F:ATTR_MULTI | DROP_CORPSE | DUN_LAIR | DUN_CAVERN F:UNIQUE | CAN_SPEAK | AURA_FIRE | AURA_ELEC | AURA_COLD | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | CONF | SCARE | ELDRITCH_HORROR S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS | S:S_HI_DRAGON | S_MONSTERS D:An elephantine horror with five trunks, each capable of breathing D:destructive blasts of elements. It is said that it is better to D:face the fury of a thousand raging lions than the Norsa! N:797:Rhan-Tegoth G:S:b I:130:46d137:20:62:70 W:91:40:0:8000000 O:0:50:50 B:CLAW:HURT:8d12 B:CRUSH:LOSE_STR:5d12 B:CLAW:HURT:8d12 B:CRUSH:ACID:5d12 F:CAN_SWIM | EVIL | DUN_HORROR F:UNIQUE | CAN_SPEAK | AURA_FIRE | AURA_ELEC | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | RES_WATE S:1_IN_5 | S:S_DEMON | BR_ACID | S_UNDEAD | S_KIN | CONF | SCARE | S:MIND_BLAST | BR_CONF | ELDRITCH_HORROR | D:"An almost globular torso, with six long, sinuous limbs terminating D:in clab-like claws. From the upper end a subsidiary globe bulged D:forward bubble-like; its triangle of three staring, fishy eyes, D:its foot-long and evidently flexible proboscis, and a distended D:lateral system analogous to gills, suggested that it was a head." N:798:Black reaver G:L:D I:120:45d55:20:85:50 W:90:3:0:3200000 O:0:60:40 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 B:HIT:LOSE_STR:4d6 B:HIT:LOSE_STR:4d6 F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SWIM | DUN_GRAVE | DUN_RUIN F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | IM_POIS | RES_TELE F:NO_CONF | NO_SLEEP | KILL_WALL | NO_FEAR S:1_IN_3 | S:TELE_TO | BLIND | HOLD | CONF | CAUSE_3 | CAUSE_4 | DRAIN_MANA | S:BRAIN_SMASH | BA_MANA | BA_NETH | S_UNDEAD D:A humanoid form, black as night, advancing steadily and unstoppably. D:Flee! N:799:Caine, the Conspirator G:p:D I:130:45d94:100:50:20 W:90:1:0:3100000 O:0:100:0 B:HIT:HURT:9d15 B:HIT:HURT:9d15 B:INSULT:EAT_ITEM:1d3 B:INSULT:EAT_GOLD:1d3 F:UNIQUE | MALE | CAN_SPEAK | AMBERITE | RES_TELE | DUN_CITY F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | DROP_SKELETON F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | LITE_2 F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:EVIL | IM_POIS | IM_COLD | SMART | REGENERATE | CAN_SWIM S:1_IN_4 | S:TPORT | TELE_TO | SCARE | DARKNESS | S:S_DRAGON | TRAPS | BO_WATE | BO_NETH | S_UNDEAD | S_DEMON | S:CONF | BO_ACID | FORGET | MIND_BLAST | CAUSE_4 D:Caine is perhaps the least reliable Amberite, always ready to ignore D:his pacts and promises when it suits him. "...the swarthy, dark-eyed D:countenance of Caine, dressed all in satin that was black and green, D:wearing a dark three-cornered hat set at a rakish angle, and a green D:plume of feathers trailing down in the back. He was standing in the D:profile, one arm akimbo, and toes of his boots curled upwards, and he D:wore an emerald-studded dagger at his belt... He was dark." N:800:Master quylthulg G:Q:b I:120:44d32:20:1:0 W:87:3:0:700000 O:0:0:0 F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | DUN_TEMPLE F:INVISIBLE | EMPTY_MIND | F:ANIMAL | EVIL | RES_TELE F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:S_MONSTER | S_MONSTERS | S_UNDEAD | S_DRAGON | S_HI_UNDEAD | S_HI_DRAGON D:A pulsating mound of flesh, shining with silver pulses of throbbing light. N:801:Greater draconic quylthulg G:Q:g I:120:48d22:20:1:0 W:95:3:0:1060000 O:0:0:0 F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | DUN_TEMPLE | DUN_CAVERN F:INVISIBLE | EMPTY_MIND | F:ANIMAL | EVIL | RES_TELE | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK | TELE_TO | S:S_HI_DRAGON D:A massive mound of scaled flesh, throbbing and pulsating with multi-hued D:light. N:802:Greater rotting quylthulg G:Q:u I:120:45d24:20:1:0 W:90:3:0:370000 O:0:0:0 F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | DUN_TEMPLE | DUN_GRAVE F:INVISIBLE | EMPTY_MIND | F:ANIMAL | EVIL | RES_TELE F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK | TELE_TO | S:S_HI_UNDEAD D:A massive pile of rotting flesh. A disgusting stench fills the air as it D:throbs and writhes. N:803:Null the Living Void G:.:d I:110:44d80:30:50:20 W:88:20:0:2150000 O:0:0:0 B:TOUCH:LOSE_ALL:6d16 B:TOUCH:EXP_VAMP:6d16 B:TOUCH:UN_POWER:6d16 F:DUN_PLANAR F:PASS_WALL | NO_CONF | NO_SLEEP | NONLIVING | IM_ACID | CAN_FLY F:SMART | EVIL | F:IM_COLD | RES_NETH | NO_STUN | UNIQUE | F:REGENERATE | FORCE_MAXHP S:1_IN_5 S:BR_NETH | BRAIN_SMASH | SCARE | S_UNDEAD | S_HI_UNDEAD | S:DRAIN_MANA | HEAL | ANIM_DEAD | ELDRITCH_HORROR | D:A black hole in the fabric of reality. It simply is not there. N:804:Vecna, the Emperor Lich G:L:y I:130:44d80:20:42:50 W:88:2:0:2800000 O:0:50:50 B:TOUCH:EXP_80 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:2d12 B:TOUCH:LOSE_DEX:2d12 F:UNIQUE | MALE | CAN_SPEAK | RES_TELE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM F:ESCORT | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | F:EVIL | UNDEAD | IM_COLD | F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_2 | S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 | S:BRAIN_SMASH | TRAPS | BA_MANA | S:BO_MANA | BA_NETH | S:S_MONSTERS | S_UNDEAD | S_KIN | ANIM_DEAD D:He is a highly cunning, extremely magical being, spoken of in legends. D:His head is rumored to control spells of great power. D:This ancient shadow of death wilts any living thing it passes. N:805:Omarax the Eye Tyrant G:e:y I:130:45d102:30:40:10 W:90:4:0:880000 O:0:0:0 B:GAZE:EXP_40:2d6 B:GAZE:PARALYZE:2d6 B:GAZE:UN_POWER:2d6 B:GAZE:LOSE_INT:2d6 F:UNIQUE | MALE | CAN_SPEAK | RES_TELE | DUN_TOWER F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | F:SMART | BASH_DOOR | F:EVIL | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_2 | S:BLIND | SLOW | CONF | SCARE | DRAIN_MANA | MIND_BLAST | FORGET | S:DARKNESS | BA_DARK | BO_ACID | BO_FIRE | BO_COLD | S_KIN D:A disembodied eye, floating in the air. His gaze seems to shred your D:soul and his spells crush your will. He is ancient, his history steeped D:in forgotten evils, his atrocities numerous and sickening. N:806:Tsathoggua, the Sleeper of N'kai G:r:D I:130:47d99:30:40:100 W:94:40:0:4400000 O:0:50:50 B:CRUSH:LOSE_ALL:5d6 B:CRUSH:ACID:5d6 B:CRUSH:LOSE_ALL:5d6 B:CRUSH:ACID:5d6 F:UNIQUE | CAN_SPEAK | RES_TELE | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | DROP_1D2 | F:ONLY_ITEM | DROP_GOOD | MOVE_BODY | CAN_SWIM F:SMART | BASH_DOOR | F:EVIL | IM_POIS | IM_ACID | IM_COLD | F:NO_CONF | NO_SLEEP S:1_IN_2 | S:S_DEMON | S_MONSTERS | S_UNDEAD | HOLD | SCARE | MIND_BLAST | S:BR_ACID | BR_NETH | CAUSE_4 | ELDRITCH_HORROR | D:"...the formless bulking of a couchant mass. And the mass stirred D:a little... and put forth with infinite slothfulness a huge and D:toad-shaped head. And the head opened its eyes very slowly, as if D:half awakened from slumber, so that they were visible as two slits D:of oozing phosphor in the black browless face." N:807:Gerard, Strongman of Amber G:p:u I:130:45d103:100:75:20 W:89:1:0:1900000 O:0:100:0 B:HIT:SHATTER:15d15 B:HIT:SHATTER:15d15 F:UNIQUE | MALE | CAN_SPEAK | AMBERITE | RES_TELE | DUN_LAIR F:DROP_SKELETON | DROP_CORPSE | CAN_SWIM | LITE_2 F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | KILL_WALL | F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:EVIL | IM_POIS | IM_ACID | IM_FIRE | REGENERATE F:OPEN_DOOR | BASH_DOOR | S:1_IN_4 | S:HEAL | TPORT | TELE_TO | SCARE | BLIND | S_MONSTERS D:Gerard may not be quite as devious as the other Amberites, but D:he is certainly stronger than the rest of them put together. "...a big, D:powerful man... (He) resembled (Corwin) quite strongly, save that D:his jaw was heavier, and (Corwin) knew he was bigger than him, though D:slower. His strength was a thing out of legend. He wore a dressing gown of D:blue and gray clasped around the middle with a wide, black belt, and he D:stood laughing. About his neck, on a heavy cord, there hung a silver D:hunting horn. He wore a fringe beard and a light mustache. In his right D:hand he held a goblet of wine." N:808:Ungoliant, the Unlight G:S:D I:120:44d207:8:80:80 W:88:1:0:2700000 O:20:0:80 B:BITE:POISON:3d9 B:BITE:POISON:3d9 B:STING:POISON:2d5 B:STING:POISON:2d5 F:UNIQUE | FEMALE | DUN_CAVERN | DUN_MINE F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | BASH_DOOR | CAN_SWIM F:ANIMAL | EVIL | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_3 | S:HEAL | BLIND | SLOW | CONF | SCARE | DARKNESS | BA_DARK | S:BR_POIS | BR_DARK | S:S_SPIDER D:This enormous, hideous spirit of void is in the form of a spider of D:immense proportions. She is surrounded by a cloud of Unlight as she sucks D:in all living light into her bloated body. She is always ravenously D:hungry and would even eat herself to avoid starvation. She is rumoured to D:have a foul and deadly breath. N:809:Atlach-Nacha, the Spider God G:S:D I:120:46d198:8:80:80 W:92:20:0:7800000 O:60:0:40 B:BITE:POISON:3d9 B:BITE:LOSE_STR:3d9 B:STING:POISON:2d5 B:STING:LOSE_STR:2d5 F:UNIQUE | CAN_SPEAK | RES_TELE | DUN_HORROR | DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | BASH_DOOR | MOVE_BODY | NONLIVING | F:ANIMAL | EVIL | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_3 | S:SCARE | BLIND | CONF | HOLD | BR_POIS | ELDRITCH_HORROR | S:DARKNESS | BA_DARK | BR_DARK | S_SPIDER | S_DEMON D:"...there was a kind of face on the squat ebon body, low down amid D:the several-jointed legs. The face peered up with a weird expression D:of doubt and inquiry..." N:810:Y'golonac G:H:R I:120:44d207:8:80:80 W:87:20:0:3000000 O:0:0:100 B:TOUCH:LOSE_INT:1d20 B:BITE:HURT:40d1 B:TOUCH:LOSE_WIS:1d20 B:BITE:HURT:40d1 F:UNIQUE | CAN_SPEAK | RES_TELE | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:SMART | BASH_DOOR | MOVE_BODY | NONLIVING | F:EVIL | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:SCARE | BLIND | CONF | HOLD | HASTE | DRAIN_MANA | S:CAUSE_3 | CAUSE_4 | DARKNESS | FORGET | S_DEMON | S_HOUND | S:TPORT | TELE_TO | ELDRITCH_HORROR D:"He saw why the shadow on the frosted pane yesterday had been D:headless and he screamed... but before he could scream out his D:protest his breath was cut off, as the hands descended on his D:face and the wed ret mouths opened in their palms." N:811:Aether hound G:Z:B I:120:49d28:30:50:0 W:97:2:0:1550000 O:0:0:0 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:BITE:HURT:2d12 B:CLAW:HURT:3d3 F:ATTR_MULTI | ATTR_ANY | DUN_PLANAR | DUN_CAVERN F:FORCE_SLEEP | CAN_FLY | F:FRIENDS | RES_NETH | RES_PLAS | RES_NEXU | RES_DISE | F:BASH_DOOR | AURA_FIRE | AURA_ELEC | AURA_COLD | DROP_CORPSE | F:DROP_SKELETON | LITE_2 F:ANIMAL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP S:1_IN_5 | S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS | S:BR_LITE | BR_DARK | BR_SOUN | BR_CONF | BR_CHAO | BR_SHAR | S:BR_NETH | BR_DISE | BR_WALL | BR_INER | BR_TIME | S:BR_GRAV | BR_PLAS | BR_NEXU D:A shifting, swirling form. It seems to be all colours and sizes and D:shapes, though the dominant form is that of a huge dog. You feel very D:uncertain all of a sudden. N:812:Warp demon G:U:o I:120:44d48:30:50:20 W:88:1:0:910000 O:0:100:0 B:HIT:UN_BONUS:14d11 B:HIT:UN_POWER:14d11 F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | DUN_HELL | DUN_PLANAR F:DROP_GOOD | DROP_1D2 | SMART | PASS_WALL | F:RES_NEXU | RES_NETH | NONLIVING | CAN_FLY | F:EVIL | IM_POIS | IM_ELEC | IM_COLD | IM_FIRE | DEMON S:1_IN_4 | S:HEAL | TPORT | S_DEMON | S:BRAIN_SMASH | DRAIN_MANA | BR_PLAS D:A huge demon who guards the cross-dimensional gateways. N:813:Eric the Usurper G:p:R I:130:47d87:100:50:15 W:94:1:0:7400000 O:0:100:0 B:HIT:HURT:10d15 B:HIT:HURT:10d15 B:HIT:EAT_ITEM:10d15 F:UNIQUE | MALE | CAN_SPEAK | SMART | AMBERITE | RES_TELE | DUN_CAVERN | DUN_TOWER F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | DROP_SKELETON | CAN_SWIM F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | LITE_2 F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:EVIL | IM_POIS | IM_ACID | IM_ELEC | REGENERATE S:1_IN_4 | S:BLIND | S_MONSTER | DRAIN_MANA | S_MONSTERS | S:SCARE | CONF | TPORT | TELE_TO | CAUSE_3 | BO_ACID | S:HOLD | BA_FIRE | TRAPS | TELE_AWAY | BRAIN_SMASH | S:FORGET | BO_WATE | CAUSE_4 | BO_NETH | DARKNESS | S:S_ANGEL | S_DEMON D:When Oberon disappeared, Eric seized the opportunity and claimed D:the throne, eliminating those who would have opposed him. "Handsome by D:anyone's standards, his hair was so dark, as to be almost blue. His beard D:curled around the mouth that always smiled, and he was dressed simply in a D:leather jacket and leggings, a plain cloak, high black boots, and he wore D:a red sword belt bearing a long silvery saber and clasped with a ruby, D:and his high cloak, collar round his head was lined with red and the D:trimmings of his sleeves matched it. His hands, thumbs hooked behind D:his belt, were terribly strong and prominent. A pair of black gloves D:jutted from his belt near his right tip." N:814:Yig, Father of Serpents G:R:g I:130:46d109:100:62:15 W:92:20:0:6100000 O:80:0:20 B:CLAW:POISON:8d10 B:CLAW:POISON:8d10 B:CRUSH:HURT:8d15 B:BITE:HURT:2d50 F:UNIQUE | MALE | CAN_SPEAK | SMART | RES_TELE | ANIMAL | DUN_CAVERN | DUN_PLANAR F:ESCORT | ESCORTS | CAN_SWIM | F:OPEN_DOOR | BASH_DOOR | F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:EVIL | IM_POIS | IM_ACID | IM_ELEC | REGENERATE | S:1_IN_4 | S:BR_POIS | BR_NUKE | BR_ACID | S_HYDRA | S_KIN | S_DEMON | ELDRITCH_HORROR | D:"The half-human father of serpents... the snake-god of the central D:plains tribes -- presumably the primal source of the more D:southerly Quetzalcoatl or Kukulcan -- was odd, half-anthropomorphic D:devil." N:815:Unmaker G:E:v I:120:45d7:60:25:60 W:89:4:0:850000 O:20:60:20 B:TOUCH:LOSE_ALL:10d10 B:TOUCH:UN_BONUS:10d10 B:TOUCH:UN_POWER:10d10 F:KILL_WALL | KILL_ITEM | KILL_BODY | NO_FEAR | DUN_CAVERN | DUN_TOWER F:FORCE_SLEEP | FORCE_MAXHP | MULTIPLY | CAN_FLY | F:ATTR_MULTI | SHAPECHANGER | ATTR_ANY | F:DROP_60 | DROP_GOOD | POWERFUL | AURA_ELEC | AURA_FIRE | AURA_COLD | F:BASH_DOOR | IM_ELEC | IM_FIRE | RES_NEXU F:IM_POIS | IM_ACID | RES_PLAS | RES_DISE | COLD_BLOOD | NONLIVING | RAND_50 S:1_IN_5 | S:BR_CHAO D:Summoned from the Courts of Chaos, it is a mass of sentient Logrus, D:spreading uncontrollably and destroying everything in its path. N:816:Cyberdemon G:U:u I:120:45d109:90:45:90 W:89:4:0:2150000 O:0:100:0 B:HIT:HURT:1d50 B:HIT:HURT:1d50 B:HIT:HURT:1d50 B:HIT:HURT:1d50 F:RAND_25 | EVIL | DEMON | STUPID | DUN_HELL F:DROP_2D2 | DROP_GOOD | ONLY_ITEM | RES_TELE | F:IM_POIS | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP | NONLIVING S:1_IN_4 | S:ROCKET D:Reverbrant metal steps announce the arrival of this huge creature, D:half demon half machine. It has an unsurpassable firepower. N:817:Hela, Queen of the Dead G:p:g I:130:46d113:60:55:10 W:92:3:0:7600000 O:0:0:100 B:TOUCH:LOSE_ALL:50d1 B:TOUCH:EXP_VAMP:50d1 B:TOUCH:UN_BONUS:50d1 F:UNIQUE | FEMALE | CAN_SPEAK | POWERFUL | RES_TELE | DUN_HELL | DUN_GRAVE F:FORCE_SLEEP | FORCE_MAXHP | SMART | F:ONLY_ITEM | DROP_1D2 | DROP_GREAT | DROP_GOOD | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | CAN_FLY | F:EVIL | RES_NETH | IM_COLD | IM_POIS | F:IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:CAUSE_3 | CAUSE_4 | HAND_DOOM | TELE_TO | HOLD | S:S_UNDEAD | S_HI_UNDEAD | S_HI_DRAGON | FORGET | SCARE | BLIND | S:BA_DARK | BA_NETH | HEAL | ANIM_DEAD D:The Norse ruler of Hel is a merciless queen, who ever hunts for more D:souls to add to her collection of tortured spirits. She arrives in D:an ominous green robe, a certain sign of impending doom, to claim D:as her own those who die an ignoble death. N:818:The Mouth of Sauron G:p:v I:130:46d107:60:50:10 W:91:3:0:5600000 O:0:0:100 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 B:TOUCH:UN_POWER B:TOUCH:UN_POWER F:UNIQUE | MALE | F:FORCE_SLEEP | FORCE_MAXHP | SMART | DUN_CAVERN | DUN_TOWER F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | CAN_FLY F:EVIL | IM_FIRE | IM_COLD | LITE_2 F:IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_2 | S:TELE_TO | HOLD | CAUSE_3 | TRAPS | ANIM_DEAD | S:BO_PLAS | BA_DARK | BA_MANA | BA_FIRE | BA_WATE | BA_NETH D:The Mouth of Sauron is a mighty spell caster. So old that even he cannot D:remember his own name, his power and evil are undeniable. He believes D:unshakeably that he is unbeatable and laughs as he weaves his awesome D:spells. N:819:Klingsor, Evil Master of Magic G:p:y I:130:47d105:60:50:10 W:94:3:0:6800000 O:0:0:100 B:HIT:UN_BONUS:6d8 B:HIT:UN_POWER:6d8 B:TOUCH:BLIND B:TOUCH:CONFUSE F:UNIQUE | MALE | CAN_SPEAK | POWERFUL | RES_TELE | DUN_GRAVE | DUN_TEMPLE F:FORCE_SLEEP | FORCE_MAXHP | SMART | DROP_CORPSE | DROP_SKELETON F:ONLY_ITEM | DROP_1D2 | DROP_GREAT | DROP_GOOD | F:DROP_CHOSEN | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | CAN_FLY F:EVIL | IM_FIRE | IM_COLD | LITE_2 F:IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_2 | S:CAUSE_3 | TELE_TO | BA_FIRE | DRAIN_MANA | HOLD | S:TRAPS | BA_WATE | BO_PLAS | BA_NETH | S:BA_MANA | BA_DARK | S_HI_UNDEAD | BA_CHAO | HAND_DOOM | ANIM_DEAD D:Klingsor, whose hopeless effort to join the Knights of the Grail D:was thwarted, turned to black magic and became a deadly necromancer. N:820:Corwin, Lord of Avalon G:p:r I:130:45d92:100:50:15 W:90:1:0:2600000 O:0:100:0 B:HIT:HURT:10d15 B:HIT:HURT:10d15 B:HIT:LOSE_CON:10d15 F:UNIQUE | MALE | CAN_SPEAK | SMART | AMBERITE | RES_TELE | DUN_TOWER F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | DROP_SKELETON F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | CAN_SWIM F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:DROP_CHOSEN | LITE_2 F:EVIL |IM_COLD | IM_POIS | IM_ELEC | REGENERATE S:1_IN_4 | S:SCARE | CONF | TPORT | TELE_TO | S_MONSTER | DRAIN_MANA | S:CAUSE_3 | BO_ACID | BO_MANA | HOLD | BA_FIRE | BA_COLD | S:TRAPS | TELE_AWAY | HEAL | BRAIN_SMASH | BA_WATE | BA_NETH | S:FORGET | BO_WATE | BO_NETH | CAUSE_4 | DARKNESS | S_MONSTERS | S:BO_PLAS | S_ANGEL D:Corwin is one of the most feared and respected Amberites; his skill D:and cunning are well known. "Green eyes, black hair, dressed in black D:and silver, yes. (Corwin) had on a cloak and it was slightly furled D:as by a wind. (He) had on black boots, like Eric's, and (he) too bore a D:blade, only (his) was heavier, though not quite as long as (Eric's). D:(He) had (his) gloves on and they were silver and scaled. The clasp D:at (his) neck was cast in the form of a silver rose." N:821:The Emperor Quylthulg G:Q:y I:130:46d83:30:1:0 W:92:3:0:3700000 O:25:25:25 F:UNIQUE | RES_TELE F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | DUN_TEMPLE | DUN_CAVERN F:ONLY_ITEM | DROP_4D2 | F:INVISIBLE | F:ANIMAL | EVIL | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BRAIN_SMASH | S:S_HI_UNDEAD | S_HI_DRAGON D:A huge seething mass of flesh with a rudimentary intelligence, the Emperor D:Quylthulg changes colours in front of your eyes. Pulsating first one D:colour then the next, it knows only it must bring help to protect itself. N:822:Qlzqqlzuup, the Lord of Flesh G:Q:B I:130:42d91:30:1:0 W:83:3:0:3700000 O:25:25:25 F:UNIQUE | CAN_SPEAK | RES_TELE F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | DUN_TEMPLE | DUN_CAVERN F:ONLY_ITEM | DROP_4D2 | F:INVISIBLE | ATTR_MULTI | F:ANIMAL | EVIL | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_1 | S:S_MONSTER | S_MONSTERS | S_UNDEAD | S_DEMON | S_DRAGON | S_ANGEL | S:S_HYDRA | S_SPIDER | S_ANT | S_HOUND | S_UNIQUE | S_AMBERITES | S:S_HI_UNDEAD | S_HI_DRAGON | S_KIN D:This disgusting creature squeals and snorts as it writhes on the floor. D:It pulsates with evil. Its intent is to overwhelm you with monster after D:monster, until it can greedily dine on your remains. N:823:Cthugha, the Living Flame G:E:R I:133:48d80:30:50:20 W:95:30:0:4200000 O:0:0:100 B:TOUCH:BLIND B:TOUCH:FIRE:12d10 B:TOUCH:FIRE:12d10 B:TOUCH:FIRE:12d10 F:UNIQUE | RES_TELE | AURA_FIRE | IM_FIRE | CAN_FLY | RES_PLAS | DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | NONLIVING | F:ONLY_ITEM | DROP_2D2 | SMART | F:EVIL | KILL_ITEM | KILL_BODY | F:NO_CONF | NO_SLEEP | NO_FEAR | LITE_2 S:1_IN_4 | S:BR_FIRE | S_KIN | ELDRITCH_HORROR D:"But even though we had shielded our eyes, it was impossible not D:to see the great amorphous shapes streaming skyward from the D:accursed place, nor the equally great being hovering like a cloud D:of living fire above the trees." N:824:Benedict, the Ideal Warrior G:p:W I:130:48d101:100:50:15 W:95:1:0:9000000 O:0:100:0 B:HIT:HURT:15d15 B:HIT:HURT:15d15 B:HIT:HURT:15d15 B:HIT:HURT:15d15 F:UNIQUE | MALE | CAN_SPEAK | SMART | AMBERITE | RES_TELE | DUN_LAIR F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | DROP_SKELETON | CAN_SWIM F:FORCE_SLEEP | FORCE_MAXHP | NO_CONF | NO_SLEEP | LITE_2 F:DROP_4D2 | DROP_GOOD | ONLY_ITEM | F:EVIL | IM_COLD | IM_POIS | IM_ELEC | IM_FIRE | REGENERATE S:1_IN_4 | S:SCARE | CONF | TPORT | TELE_TO | DRAIN_MANA | TRAPS | BRAIN_SMASH | S:TELE_AWAY | CAUSE_4 | S_MONSTERS | S_HI_DRAGON | S:S_DEMON | S_ANGEL | TELE_LEVEL | FORGET | HEAL D:In the field of combat Benedict rules supreme: with but one hand D:he was still superior to Corwin. "Benedict, tall and dour; thin of D:body, thin of face, wide of mind. He wore orange and yellow and brown D:and reminded me of haystacks and pumpkins and scarecrows and the D:Legend of Sleepy Hollow. He had a long, strong jaw and hazel eyes and D:brown hair that never curled. He stood beside a tan horse and leaned D:upon a lance about which was twined a rope of flowers. He seldom D:laughed." N:825:The Witch-King of Angmar G:W:D I:130:46d92:90:60:10 W:91:3:0:10600000 O:0:20:80 B:HIT:HURT:10d10 B:HIT:HURT:10d10 B:HIT:EXP_80:5d5 B:HIT:EXP_80:5d5 F:UNIQUE | MALE | CAN_SPEAK | RES_TELE | DUN_CAVERN | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | SMART | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | CAN_SWIM F:EVIL | UNDEAD | F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP S:1_IN_2 | S:TELE_AWAY | BLIND | HOLD | SCARE | CAUSE_3 | BRAIN_SMASH | S:BO_MANA | BA_NETH | S:S_KIN | S_HI_UNDEAD | S_HI_DRAGON | S_MONSTERS | ANIM_DEAD D:The Chief of the Ringwraiths. A fell being of devastating power. His D:spells are lethal and his combat blows crushingly hard. He moves at D:speed, and commands legions of evil to do his bidding. It is said that he D:is fated never to die by the hand of mortal man. N:826:Cyaegha G:e:D I:130:48d94:90:60:10 W:96:30:0:10200000 O:20:50:30 B:CRUSH:HURT:15d15 B:CRUSH:HURT:15d15 B:CRUSH:HURT:15d15 B:CRUSH:HURT:15d15 F:UNIQUE | CAN_SPEAK | RES_TELE | CAN_FLY | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | SMART | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:EVIL | DEMON | IM_ACID | IM_ELEC | F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:TELE_AWAY | BLIND | HOLD | SCARE | BRAIN_SMASH | S:BR_DARK | BA_DARK | BR_NETH | HAND_DOOM | FORGET | ELDRITCH_HORROR | S:S_HI_UNDEAD | S_DEMON | S_MONSTERS | S_HYDRA D:"...it was a gigantic eye staring down at them. Around the eye, D:the sky split; deep clefts opened through which the darkness D:began to ooze, a darkness blacker than the night, which crawled D:down as a set of slimy tentacles, taking on more form, more D:definite shape... something was standing, outlined against D:the black sky, something which had tentacles of darkness D:and a green-glowing eye." N:827:Pazuzu, Lord of Air G:B:w I:140:50d77:40:62:10 W:99:2:0:9400000 O:0:100:0 B:HIT:ELEC:12d12 B:HIT:ELEC:12d12 B:HIT:ELEC:12d12 B:HIT:ELEC:12d12 F:UNIQUE | MALE | F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DUN_PLANAR F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | F:EVIL | DEMON | IM_ACID | IM_FIRE | IM_ELEC | F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:MIND_BLAST | BO_ELEC | BO_MANA | BA_ELEC D:A winged humanoid from the Planes of Hell, Pazuzu grins inhumanely at you D:as he decides your fate. N:828:Ithaqua the Windwalker G:Y:B I:140:45d86:40:62:10 W:89:20:0:2800000 O:0:0:100 B:CLAW:COLD:12d12 B:CLAW:COLD:12d12 B:CRUSH:HURT:12d12 B:CRUSH:HURT:12d12 F:UNIQUE | CAN_SPEAK | ESCORT | RES_TELE | DUN_LAIR F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | AURA_COLD | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | AURA_ELEC | F:INVISIBLE | OPEN_DOOR | BASH_DOOR | NONLIVING | F:EVIL | DEMON | IM_ACID | IM_FIRE | IM_ELEC | F:IM_POIS | NO_CONF | NO_SLEEP S:1_IN_3 | S:BO_MANA | SCARE | BR_COLD | S_DEMON | BO_ELEC | BA_ELEC | ELDRITCH_HORROR S:MIND_BLAST | CAUSE_4 | BA_CHAO | BA_WATE | S_HI_UNDEAD | S_KIN D:The Wendigo, moving so fast that you can see little except two D:glowing eyes burning with hatred. "The stars had been blotted out... D:the great cloud which had obscured the the sky looked curiously D:like the outline of a great man. And... where the top of D:the cloud must have been, where the head of the thing should have D:been, there were two gleaming stars, burning bright -- like eyes!" N:829:Hell hound of Julian G:C:r I:120:53d7:25:40:30 W:105:4:0:1300000 O:0:0:0 B:BITE:FIRE:3d12 B:BITE:FIRE:3d12 B:BITE:FIRE:3d12 F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DUN_LAIR | DUN_HORROR F:RAND_25 | FRIENDS | AURA_FIRE | F:BASH_DOOR | MOVE_BODY | F:ANIMAL | EVIL | IM_FIRE S:1_IN_5 | S:BR_FIRE D:It is a giant dog that glows with heat. Flames pour from its nostrils. N:830:Cantoras, the Skeletal Lord G:s:B I:140:47d112:20:60:80 W:93:2:0:7000000 O:50:0:50 B:GAZE:EXP_80 B:GAZE:EXP_80 B:TOUCH:POISON:3d5 B:TOUCH:POISON:3d5 F:UNIQUE | MALE | CAN_SPEAK | RES_TELE | DUN_GRAVE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_SWIM F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_1 | S:TELE_TO | SLOW | SCARE | CAUSE_4 | BRAIN_SMASH | S:BO_ICEE | BO_MANA | BA_WATE | BA_NETH | S:S_HI_UNDEAD | ANIM_DEAD D:A legion of evil undead druj animating the skeleton of a once mighty D:sorcerer. His power is devastating and his speed unmatched in the D:underworld. Flee his wrath! N:831:Mephistopheles, Lord of Hell G:U:r I:140:46d109:20:75:50 W:91:2:0:10200000 O:0:100:0 B:GAZE:EXP_VAMP:1d15 B:GAZE:TERRIFY:1d15 B:TOUCH:FIRE:4d5 B:TOUCH:UN_POWER:4d5 F:MALE | UNIQUE | CAN_SPEAK | CAN_FLY | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | OPEN_DOOR | BASH_DOOR | MOVE_BODY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:IM_FIRE | RES_PLAS | RES_NETH | AURA_FIRE | F:NO_CONF | NO_SLEEP | NONLIVING | EVIL | DEMON | F:ESCORTS | IM_COLD | IM_POIS S:1_IN_3 | S:TELE_TO | SCARE | HOLD | BRAIN_SMASH | S:S_DEMON | S_HI_UNDEAD | S_UNDEAD | S:BR_FIRE | BR_NETH | S_CYBER | HAND_DOOM | ANIM_DEAD D:A duke of hell, in the flesh. N:832:Godzilla G:R:g I:130:48d124:50:140:20 W:96:2:0:8400000 O:100:0:0 B:CLAW:POISON:5d10 B:CLAW:POISON:5d10 B:BITE:HURT:20d10 B:CRUSH:UN_BONUS:5d12 F:UNIQUE | CAN_SWIM | DROP_CORPSE | DUN_CAVERN | DUN_LAIR F:FORCE_SLEEP | FORCE_MAXHP | RES_PLAS | RES_DISE | RES_TELE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP | IM_POIS S:1_IN_2 | S:BR_DISE | BR_PLAS | BR_NUKE | BR_POIS | BR_ACID D:Godzilla rose from the contaminated sea. D:Fear its anger, for its devastation is unmatched! N:833:Abhoth, Source of Uncleanness G:J:G I:130:47d135:50:75:0 W:94:30:0:7000000 O:0:0:100 B:TOUCH:ACID:11d11 B:TOUCH:DISEASE:11d11 B:TOUCH:ACID:11d11 B:TOUCH:POISON:11d11 F:ONLY_ITEM | DROP_1D2 | CAN_SWIM | DUN_CAVERN | DUN_HORROR F:DROP_GOOD | DROP_GREAT | F:CAN_SPEAK | EVIL | UNIQUE | NEVER_MOVE | FORCE_MAXHP | FORCE_SLEEP | F:IM_ACID | IM_POIS | IM_ELEC | RES_NETH | RES_WATE | RES_NEXU F:NO_FEAR | NO_CONF | NO_SLEEP | DEMON | SMART | F:COLD_BLOOD | RES_DISE | NO_STUN S:1_IN_6 | S:S_MONSTERS | S_DEMON | S_HI_DRAGON | S_HI_UNDEAD | HEAL | S:TELE_AWAY | TPORT | CAUSE_4 | BRAIN_SMASH | DRAIN_MANA | ELDRITCH_HORROR S:BR_NUKE | BR_POIS | BR_CHAO | BR_NEXU | ANIM_DEAD D:"...in the pool [there was] a grayish, horrid mass that nearly choked D:it from rim to rim. Here, it seemed, was the ultimate source of all D:miscreation and abomination. For the gray mass quobbed and quivered, D:and swelled perpetually; and from it, in manifold fission, were D:spawned the anatomies that crept away on every side through the D:grotto." N:834:Ymir the Ice Giant G:P:w I:130:48d126:50:80:20 W:96:2:0:6400000 O:0:100:0 B:HIT:COLD:5d10 B:HIT:HURT:5d10 B:HIT:COLD:5d10 B:HIT:HURT:5d10 F:UNIQUE | CAN_SPEAK | DUN_LAIR F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_WATE | MALE | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | EVIL | ESCORT | GIANT | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | RES_DISE | F:IM_COLD | NO_CONF | NO_SLEEP | IM_POIS | HURT_FIRE | F:COLD_BLOOD | AURA_COLD | DROP_CORPSE | CAN_SWIM S:1_IN_3 | S:BR_COLD | BO_ICEE | DARKNESS | HEAL | TELE_TO | S_KIN | S_HI_UNDEAD D:Ymir is one of the oldest beings there are. He looks like a giant D:humanoid made of ice. N:835:Loki the Trickster G:P:g I:130:46d154:50:80:20 W:91:2:0:7500000 O:0:0:100 B:HIT:BLIND:6d11 B:HIT:UN_BONUS:6d11 B:HIT:UN_POWER:6d11 F:UNIQUE | MALE | DUN_PLANAR F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_PLAS | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_GREAT | EVIL | AURA_FIRE | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | CAN_SPEAK | F:IM_FIRE | NO_CONF | NO_SLEEP | IM_POIS | HURT_COLD | GIANT | F:REGENERATE | CAN_SWIM S:1_IN_3 | S:SHRIEK | HASTE | HEAL | DRAIN_MANA | TPORT | TELE_TO | TELE_AWAY | S:TELE_LEVEL | FORGET | S_CYBER | S_DEMON | HAND_DOOM | S_HI_UNDEAD | S:S_UNIQUE | S_HI_DRAGON | BA_DARK | BA_MANA | S_AMBERITES | ANIM_DEAD S:INVULNER D:Loki, the god of mischief, is a nasty person. He will use every D:dirty trick in the book, and then some. In the end, his half-giant D:heritage is bound to show, as he will defect to the side of the D:giants and fight against the other gods of Asgard. N:836:Star-spawn of Cthulhu G:U:G I:130:48d110:90:45:90 W:95:20:0:10100000 O:0:0:100 B:CLAW:POISON:1d30 B:CLAW:ACID:1d30 B:TOUCH:UN_POWER:1d10 B:CRUSH:UN_BONUS:2d33 F:DUN_HORROR F:RAND_25 | KILL_ITEM | OPEN_DOOR | BASH_DOOR | RES_NETH | F:DROP_3D2 | ONLY_ITEM | FORCE_SLEEP | FORCE_MAXHP | F:EVIL | DEMON | IM_POIS | IM_ACID | IM_ELEC | RES_TELE | NONLIVING | F:POWERFUL | IM_FIRE | CAN_SWIM | NO_CONF | DROP_GOOD S:1_IN_3 S:SCARE | CONF | S_DEMON | S_UNDEAD | DRAIN_MANA | BR_ACID | S:BR_FIRE | TPORT | S_MONSTERS | BRAIN_SMASH | BR_NETH | S:HEAL | MIND_BLAST | BA_NUKE | ANIM_DEAD | ELDRITCH_HORROR D:The last remnants of sanity threaten to leave your brain as you D:behold this titanic bat-winged, octopus-headed unholy abomination. D:"They all lay in stone houses in their great city of R'lyeh, D:preserved by the spells of mighty Cthulhu for a glorious D:resurrection when the stars and the earth might once more D:be ready..." N:837:Surtur the Giant Fire Demon G:P:r I:130:47d153:50:80:20 W:93:2:0:8000000 O:0:100:0 B:HIT:FIRE:6d11 B:HIT:HURT:6d11 B:HIT:FIRE:6d11 B:HIT:HURT:6d11 F:UNIQUE | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_PLAS | MALE | F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | EVIL | AURA_FIRE | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | CAN_SPEAK | F:IM_FIRE | NO_CONF | NO_SLEEP | IM_POIS | HURT_COLD | GIANT | DEMON F:LITE_2 S:1_IN_5 | S:BR_FIRE | BR_PLAS | BLIND | TELE_TO | S_KIN | S_HI_UNDEAD | S:HAND_DOOM | TELE_AWAY | S_CYBER | S_DEMON D:Surtur is also one of the most ancient of all creatures. He is a demonic D:giant of fire, who is destined to set the nine worlds afire with his D:accursed sword of doom on the day of Ragnarok. N:838:The Tarrasque G:R:v I:130:46d130:50:92:20 W:92:2:0:7800000 O:20:50:25 B:HIT:HURT:10d10 B:HIT:HURT:10d10 B:TOUCH:UN_POWER B:TOUCH:UN_POWER F:UNIQUE | F:ATTR_MULTI | DUN_CAVERN | CAN_SWIM F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP S:1_IN_2 | S:BR_FIRE | BR_COLD | BR_DISE D:The Tarrasque is a massive reptile of legend, rumoured to be unkillable D:and immune to magic. Fear its anger, for its devastation is unmatched! N:839:Lungorthin, the Balrog of White Fire G:U:w I:130:47d128:20:62:80 W:94:2:0:7600000 O:0:100:0 B:HIT:FIRE:8d12 B:HIT:FIRE:8d12 B:CRUSH:HURT:8d12 B:TOUCH:UN_POWER F:UNIQUE | MALE | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | CAN_FLY | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DEMON | IM_FIRE | F:NO_CONF | NO_SLEEP | LITE_1 | LITE_2 S:1_IN_4 | S:BLIND | CONF | SCARE | S:BR_FIRE | S:S_DEMON | S_HI_UNDEAD D:A massive form cloaked in flame. Lungorthin stares balefully at you with D:eyes that smoulder red. The dungeon floor where he stands is scorched by D:the heat of his body. N:840:Draugluin, Sire of All Werewolves G:C:u I:130:46d107:80:45:90 W:92:2:0:3800000 O:0:0:100 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 B:BITE:POISON:2d6 B:BITE:POISON:2d6 F:UNIQUE | MALE | CAN_SPEAK | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | DROP_CORPSE F:RAND_25 | F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | MOVE_BODY | CAN_SWIM F:ANIMAL | EVIL | IM_POIS S:1_IN_3 | S:SCARE | S:S_MONSTERS | S_HOUND D:Draugluin provides Sauron with a fearsome personal guard. He is an D:enormous wolf inhabited with a human spirit. He is chief of all his kind. N:841:Shuma-Gorath G:e:G I:130:46d130:50:75:0 W:91:30:0:9800000 O:0:0:100 B:CRUSH:HURT:12d12 B:CRUSH:HURT:12d12 B:GAZE:LOSE_INT:2d12 B:GAZE:LOSE_WIS:2d12 F:ONLY_ITEM | DROP_60 | CAN_SWIM | DUN_HORROR F:DROP_GOOD | DROP_GREAT | F:CAN_SPEAK | EVIL | UNIQUE | NEVER_MOVE | FORCE_MAXHP | FORCE_SLEEP | F:IM_ACID | IM_POIS | IM_COLD | IM_ELEC | RES_NETH | RES_WATE | RES_NEXU F:NO_FEAR | NO_CONF | NO_SLEEP | DEMON | SMART | F:COLD_BLOOD | RES_DISE | NO_STUN S:1_IN_2 | S:FORGET | HEAL | HAND_DOOM | BA_MANA | TPORT | TELE_AWAY | S:TELE_LEVEL | CONF | BLIND | BRAIN_SMASH | DRAIN_MANA | S:S_MONSTERS | S_KIN | S_CYBER | S_HOUND | S_HYDRA | S_DEMON | S:S_UNIQUE | S_HI_UNDEAD | S_HI_DRAGON | ANIM_DEAD | ELDRITCH_HORROR D:Shuma-Gorath is one of the immortal lords of chaos. The true form of D:this blasphemous horror is a huge, all-seeing eye surrounded by tentacles. N:842:Tulzscha, the Green Flame G:E:G I:130:49d143:90:50:50 W:98:40:0:8000000 O:0:100:0 B:HIT:HURT:2d50 B:HIT:HURT:2d50 B:HIT:HURT:2d50 B:HIT:HURT:2d50 F:DUN_PLANAR F:RAND_25 | EVIL | DEMON | AURA_FIRE | AURA_COLD | F:DROP_3D2 | DROP_GOOD | ONLY_ITEM | RES_TELE | F:IM_POIS | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP | NONLIVING | F:IM_COLD | IM_ELEC | RES_NEXU | RES_NETH | RES_PLAS | F:REGENERATE | UNIQUE | CAN_FLY | LITE_2 S:1_IN_3 | S:BR_NEXU | BR_NETH | BR_COLD | BR_FIRE | ELDRITCH_HORROR D:"A belching column of sick greenish flame... spouting volcanically D:from the depths profound and inconceivable, casting no shadows as D:healthy flame should, and coating the nitrous stone with a nasty, D:venomous verdigris. For all that seething combustion no warmth D:lay, but only the clamminess of death and corruption." N:843:Oremorj the Cyberdemon Lord G:U:u I:130:49d129:90:45:90 W:97:4:0:9100000 O:0:100:0 B:HIT:HURT:2d40 B:HIT:HURT:2d40 B:HIT:HURT:2d40 B:HIT:HURT:2d40 F:RAND_25 | EVIL | DEMON | DUN_HELL F:DROP_1D2 | DROP_GOOD | ONLY_ITEM | RES_TELE | DROP_GREAT | UNIQUE | F:IM_POIS | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP | NONLIVING S:1_IN_3 | S:ROCKET | S_CYBER D:The mightiest of Cyberdemons, their lord and ruler. N:844:Kaschei the Immortal G:L:v I:130:46d92:100:50:0 W:92:3:0:6800000 O:0:0:100 B:HIT:UN_BONUS:6d8 B:HIT:UN_BONUS:6d8 B:HIT:HURT:5d5 B:HIT:HURT:5d5 F:UNIQUE | MALE | CAN_SPEAK | RES_TELE F:FORCE_SLEEP | FORCE_MAXHP | DUN_GRAVE | DUN_RUIN F:ESCORT | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY F:EVIL | UNDEAD | F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | S:1_IN_3 | S:TPORT | BLIND | SCARE | CAUSE_4 | BRAIN_SMASH | S:BA_MANA | BO_MANA | BA_FIRE | S:S_MONSTERS | S_DEMON | S_HI_UNDEAD | HAND_DOOM | ANIM_DEAD D:A stench of corruption and decay surrounds this sorcerer, who has clearly D:risen from the grave to continue his foul plots and schemes. N:845:Yog-Sothoth, the All-in-One G:j:v I:130:49d95:100:50:20 W:97:30:0:5500000 O:0:0:100 B:TOUCH:HURT:40d5 B:TOUCH:LOSE_CON:16d2 B:TOUCH:LOSE_CON:16d2 F:DUN_HORROR F:UNIQUE | CAN_SPEAK | SMART | RES_TELE F:PASS_WALL | FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | AURA_FIRE | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | ATTR_MULTI | ATTR_ANY | AURA_COLD | F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | NONLIVING | CAN_FLY | F:EVIL | IM_ACID | IM_ELEC | IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:BO_MANA | BRAIN_SMASH | BA_MANA | S_MONSTERS | S_CYBER | ELDRITCH_HORROR S:BA_CHAO | S_DEMON | S_HI_UNDEAD | S_HOUND | BR_MANA | BR_DISI D:"Great globes of light massing towards the opening... the breaking D:apart of the nearest globes, and the protoplasmic flesh that D:flowed blackly outward to join together and form that eldritch, D:hideous horror from outer space... whose mask was a congeries D:of iridescent globes... who froths as primal slime in nuclear D:chaos forever beyond the nethermost outposts of space and time!" N:846:Fenris Wolf G:C:D I:134:50d98:80:45:40 W:100:2:0:7900000 O:20:0:80 B:BITE:HURT:20d6 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 B:BITE:HURT:20d6 F:UNIQUE | CAN_SPEAK | DROP_CORPSE F:FORCE_SLEEP | FORCE_MAXHP | DUN_HELL F:RAND_25 | F:ONLY_ITEM | DROP_90 | DROP_GOOD | F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | KILL_BODY | CAN_SWIM F:ANIMAL | EVIL | IM_POIS | IM_FIRE | IM_COLD S:1_IN_8 | S:BR_DARK | BR_POIS | BR_COLD D:The immensely huge wolf who would swallow the sun to satisfy its D:hunger and leave the Norse gods for the dessert. N:847:Great Wyrm of Power G:D:y I:130:46d201:20:55:70 W:92:4:0:9000000 O:50:50:0 B:CRUSH:POISON:8d12 B:CRUSH:FIRE:8d12 B:CRUSH:ELEC:8d12 B:CRUSH:HURT:10d18 F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | DUN_LAIR | DUN_CAVERN F:AURA_FIRE | AURA_ELEC | AURA_COLD | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY | F:DRAGON | GOOD | DROP_CORPSE | F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | F:RES_NETH | RES_NEXU | RES_PLAS | RES_DISE | RES_TELE | LITE_2 S:1_IN_3 | S:S_HI_DRAGON | S_DRAGON | S_KIN | S:BR_NUKE | BR_ACID | BR_ELEC | BR_FIRE | S:BR_COLD | BR_POIS | BR_NETH | BR_LITE | BR_DARK | S:BR_CONF | BR_SOUN | BR_CHAO | BR_DISE | BR_NEXU | S:BR_TIME | BR_INER | BR_GRAV | BR_SHAR | BR_PLAS | S:BR_WALL | BR_MANA | BR_DISI D:The mightiest of all dragonkind, a great wyrm of power is seldom D:encountered in our world. It can crush stars with its might. N:848:Shub-Niggurath, Black Goat of the Woods G:U:D I:130:48d95:100:50:20 W:96:30:0:8700000 O:0:0:100 B:CRUSH:LOSE_WIS:20d5 B:CRUSH:LOSE_INT:20d5 B:BITE:LOSE_STR:10d2 B:BITE:LOSE_CON:10d2 F:DUN_HORROR F:UNIQUE | CAN_SPEAK | FEMALE | AURA_ELEC | RES_TELE F:ATTR_MULTI | FORCE_SLEEP | FORCE_MAXHP | PASS_WALL | ATTR_ANY | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | NONLIVING | CAN_FLY | F:SMART | OPEN_DOOR | BASH_DOOR | REGENERATE | DEMON | AURA_COLD F:EVIL | IM_ACID | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | CAN_SWIM S:1_IN_3 | S:BO_MANA | BRAIN_SMASH | BA_DARK | S_MONSTERS | S:CAUSE_4 | HEAL | BR_CHAO | BR_CONF | BR_POIS | BR_NUKE | S:BA_CHAO | S_DEMON | S_HI_UNDEAD | S_UNIQUE | ANIM_DEAD | ELDRITCH_HORROR D:This horrendous outer god looks like a writhing cloudy mass filled D:with mouths and tentacles. N:849:Nodens, Lord of the Great Abyss G:P:w I:130:49d108:100:50:20 W:97:3:0:7600000 O:0:50:50 B:HIT:HURT:2d66 B:HIT:HURT:2d66 F:UNIQUE | CAN_SPEAK | MALE | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | PASS_WALL | CAN_SWIM F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_90 | DROP_GREAT | F:SMART | OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY | F:GOOD | IM_ACID | IM_POIS | IM_FIRE | IM_ELEC | NO_CONF | F:NO_SLEEP | RES_NEXU | RES_NETH | RES_WATE S:1_IN_1 | S:TELE_AWAY | TELE_TO | TELE_LEVEL | TPORT | S:BO_MANA | BA_MANA | S_MONSTERS | HEAL | S:CAUSE_4 | HASTE | HAND_DOOM | S:S_ANGEL | S_UNIQUE D:The hoary Lord of the Great Abyss seems a wizened man, but appearances D:can be deceiving. N:850:Carcharoth, the Jaws of Thirst G:C:D I:130:50d105:80:65:10 W:99:2:0:10200000 O:30:10:60 B:CLAW:HURT:3d3 B:CLAW:HURT:3d3 B:BITE:POISON:4d4 B:BITE:POISON:4d4 F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DUN_CAVERN F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | F:ONLY_ITEM | DROP_90 | DROP_GOOD | AURA_FIRE | F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | MOVE_BODY | CAN_SWIM F:ANIMAL | EVIL | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP S:1_IN_4 | S:HEAL | SCARE | BRAIN_SMASH | S:BR_FIRE | S:S_HOUND D:The first guard of Angband, Carcharoth, also known as 'The Red Maw', is D:the largest wolf to ever walk the earth. He is highly intelligent and a D:deadly opponent in combat. N:851:Nyarlathotep, the Crawling Chaos G:U:r I:130:48d132:100:50:20 W:95:30:0:9000000 O:0:50:50 B:CRUSH:LOSE_CON:30d4 B:CRUSH:LOSE_STR:30d4 B:GAZE:LOSE_INT:1d50 B:GAZE:LOSE_WIS:1d50 F:DUN_HORROR F:UNIQUE | CAN_SPEAK | RES_TELE | NONLIVING | F:FORCE_SLEEP | FORCE_MAXHP | PASS_WALL | DEMON | RES_NEXU | CAN_SWIM F:SHAPECHANGER | ATTR_MULTI | ATTR_ANY | MALE | AURA_ELEC | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_GREAT | F:SMART | OPEN_DOOR | BASH_DOOR | REGENERATE | AURA_COLD | F:EVIL | IM_ACID | IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:TELE_AWAY | TELE_TO | TELE_LEVEL | TPORT | BR_NEXU | BA_CHAO | S:BA_MANA | BA_FIRE | S_MONSTERS | BRAIN_SMASH | MIND_BLAST | ELDRITCH_HORROR S:CAUSE_4 | HASTE | S_HI_UNDEAD | S_HI_DRAGON | ANIM_DEAD S:S_ANGEL | S_DEMON | HEAL | S_SPIDER | S_HOUND | S_CYBER | HAND_DOOM D:Nyarlathothep is the messenger, the heart and the soul of the outer gods. D:He is a shapechanger capable of assuming thousands of nightmarish forms. D:One of them looks like this: "A tall, slim figure with the young face of D:an antique pharaoh, gay with prismatic robes and crowned with a D:pshent that glowed with inherent light... the fascination of a D:dark god or fallen archangel, and around whose eyes there lurked D:the languid sparkle of capricious humor." N:852:Azathoth, Seething Nuclear Chaos G:E:B I:130:49d142:100:75:100 W:98:30:0:9700000 O:50:0:50 B:CRUSH:HURT:35d5 B:CRAWL:ACID:35d5 B:CRUSH:HURT:35d5 B:CRAWL:ACID:35d5 F:UNIQUE | RES_TELE | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | DEMON | AURA_FIRE | AURA_ELEC | F:ATTR_MULTI | ESCORTS | ESCORT | POWERFUL | ATTR_ANY | NONLIVING | F:KILL_ITEM | KILL_WALL | CAN_SWIM | AURA_COLD | F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | RES_DISE | F:STUPID | OPEN_DOOR | BASH_DOOR | REGENERATE | EMPTY_MIND | F:EVIL | IM_ACID | IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_5 | S:S_DEMON | BR_CHAO | BR_DISE | BR_MANA | BA_WATE | BR_DISI | ELDRITCH_HORROR D:"That last amorphous blight of nethermost confusion which D:blasphemes and bubbles at the centre of all infinity -- D:the boundless daemon sultan Azathoth, whose name no lips D:dare speak aloud, and who gnaws hungrily in inconceivable, D:unlighted chambers beyond time amidst the muffled, maddening D:beating of vile drums and the thin monotonous whine of D:accursed flutes." N:853:Cerberus, Guardian of Hades G:C:r I:130:49d143:50:80:10 W:97:1:0:10600000 O:40:50:0 B:HIT:FIRE:9d12 B:HIT:FIRE:9d12 B:HIT:FIRE:9d12 B:HIT:FIRE:9d12 F:UNIQUE | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | DROP_CORPSE F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | CAN_SWIM F:ANIMAL | EVIL | IM_FIRE | NO_CONF | NO_SLEEP S:1_IN_3 | S:BA_DARK | S:BR_FIRE | BR_NETH | S:S_HOUND D:A three-headed hell hound of fearsome aspect. Flame burns merrily from its D:hide as it snarls and roars its defiance. N:854:Jormungand the Midgard Serpent G:J:g I:130:50d216:100:100:0 W:99:1:0:20000000 O:0:0:0 B:CRUSH:HURT:30d10 B:CRUSH:HURT:30d10 B:BITE:HURT:5d60 F:DUN_PLANAR F:UNIQUE |FORCE_MAXHP | FORCE_SLEEP | AQUATIC | WILD_OCEAN | F:MOVE_BODY | KILL_WALL | IM_FIRE | IM_POIS | IM_ACID | IM_COLD | EVIL | F:RES_WATE | RES_PLAS | RES_NEXU | NO_STUN | REGENERATE | DROP_CORPSE F:NO_CONF | NO_SLEEP S:1_IN_8 S:BA_WATE D:The Midgard Serpent is so huge that its body surrounds the world of D:mortal men. It can grind gods themselves to lifeless pulp. N:855:The Destroyer G:g:W I:130:46d474:100:100:0 W:92:1:0:11700000 O:0:0:0 B:HIT:HURT:30d9 B:HIT:HURT:30d9 B:HIT:HURT:30d9 B:HIT:HURT:30d9 F:DUN_PLANAR F:UNIQUE | NONLIVING | NO_FEAR | FORCE_MAXHP | FORCE_SLEEP | REFLECTING | F:MOVE_BODY | KILL_WALL | IM_FIRE | IM_ELEC | IM_POIS | IM_COLD | F:RES_NETH | RES_WATE | RES_PLAS | RES_NEXU | NO_STUN | REGENERATE | F:NO_CONF | NO_SLEEP | CAN_FLY | CAN_SWIM F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | S:1_IN_3 | S:BR_DISI | BO_MANA D:The Destroyer was built by the Norse deities to be their ultimate weapon D:against the space gods who had arrived to judge the world. Unfortunately, D:the Destroyer has gone berserk and is destroying everything it sees. The D:mystical Destroyer is nearly indestructible, and it is said that when it D:uses its power of complete disintegration, the day of Ragnarok is near. N:856:Gothmog, the High Captain of Balrogs G:U:R I:130:50d112:100:70:0 W:100:1:0:8700000 O:0:100:0 B:HIT:FIRE:9d12 B:HIT:FIRE:9d12 B:CRUSH:HURT:8d12 B:TOUCH:UN_POWER F:UNIQUE | MALE | CAN_SPEAK | CAN_FLY | DUN_HELL F:FORCE_SLEEP | FORCE_MAXHP | F:ESCORT | ESCORTS | KILL_WALL | AURA_FIRE | NONLIVING | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | F:EVIL | DEMON | F:IM_FIRE | IM_ELEC | NO_CONF | NO_SLEEP S:1_IN_3 | S:BLIND | CONF | SCARE | S:BR_FIRE | S_KIN | S:S_DEMON | S_HI_UNDEAD | S_CYBER D:Gothmog is the Chief Balrog in Morgoth's personal guard. He is renowned D:for slaying Ecthelion the Warder of the Gates and he has never been D:defeated in combat. With his whip of flame and awesome fiery breath he D:saved his master from Ungoliant's rage. N:857:Great Cthulhu G:U:g I:130:50d140:100:70:100 W:99:20:0:10800000 O:0:50:50 B:CRUSH:HURT:50d4 B:CLAW:UN_POWER:15d2 B:CLAW:UN_BONUS:15d2 B:TOUCH:DISEASE:100d1 F:DUN_HORROR F:UNIQUE | CAN_SPEAK | DEMON | NONLIVING | F:FORCE_SLEEP | FORCE_MAXHP | ESCORT | ESCORTS | SMART | RES_PLAS | RES_NEXU | F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | REGENERATE | RES_NETH | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:DROP_GREAT | RES_DISE | RES_TELE | CAN_SWIM | F:EVIL | IM_FIRE | IM_POIS | IM_ELEC | IM_ACID | NO_CONF | NO_SLEEP | S:1_IN_3 S:TPORT | SCARE | BLIND | MIND_BLAST | BRAIN_SMASH | DRAIN_MANA | S:BR_POIS | BR_ACID | BR_FIRE | CONF | DARKNESS | FORGET | S_HI_UNDEAD | S:BR_NUKE | BR_NETH | BR_CHAO | BR_DISE | BR_DARK | BR_PLAS | BR_CONF S:BR_NEXU | S_DEMON | S_CYBER | S_KIN | BR_DISI | HAND_DOOM S:ANIM_DEAD | ELDRITCH_HORROR D:This creature is death incarnate. "A monster of vaguely anthropoid D:outline, but with an octopus-like head whose face was a mass of D:feelers, a scaly, rubbery-looking body, prodigious claws on hind D:fore feet, and long, narrow wings behind. This thing... was a D:somewhat bloated corpulence... It lumbered slobberingly into sight D:and gropingly squeezed its gelatinous green immensity through the D:black doorway... A mountain shambled or walked." # Sauron is no longer a quest monster (in Zangband), just a "normal" one. N:858:Sauron, the Sorcerer G:p:o I:130:49d150:100:80:0 W:97:1:0:10400000 O:0:0:100 B:HIT:UN_BONUS:10d12 B:HIT:UN_BONUS:10d12 B:TOUCH:UN_POWER B:TOUCH:UN_POWER F:UNIQUE | MALE | CAN_SPEAK | REFLECTING | DUN_RUIN F:FORCE_SLEEP | FORCE_MAXHP | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:DROP_CHOSEN | F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY | REGENERATE | CAN_FLY F:EVIL | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | RES_TELE | LITE_2 S:1_IN_2 | S:TPORT | TELE_LEVEL | BLIND | CONF | SCARE | CAUSE_4 | S:BRAIN_SMASH | FORGET | S:BO_ICEE | BO_MANA | BO_PLAS | S:BA_MANA | BA_FIRE | BA_WATE | BA_NETH | BA_DARK | S:S_MONSTERS | S_DEMON | S_HI_UNDEAD | S_HI_DRAGON | S:HAND_DOOM | ANIM_DEAD D:Mighty in spells and enchantments, D:he created the One Ring. His eyes glow with power and his gaze seeks to D:destroy your soul. He has many servants, and rarely fights without them. N:859:The Unicorn of Order G:q:w I:160:50d280:100:85:0 W:99:1:0:14000000 O:25:50:25 B:KICK:UN_POWER:13d13 B:KICK:UN_POWER:12d12 B:BUTT:UN_BONUS:11d11 B:BITE:TIME:10d10 F:UNIQUE | FORCE_SLEEP | FORCE_MAXHP | DUN_PLANAR F:REFLECTING | AURA_COLD | AURA_ELEC | F:ONLY_ITEM | DROP_4D2 | F:DROP_GOOD | DROP_GREAT | DROP_CHOSEN | RES_NETH | F:RES_WATE | RES_PLAS | RES_NEXU | RES_DISE | F:SMART | KILL_BODY | POWERFUL | BASH_DOOR F:REGENERATE | NONLIVING | CAN_FLY | CAN_SWIM F:GOOD | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | LITE_1 F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN | RES_TELE S:1_IN_3 | S:SHRIEK | BR_SOUN | BR_LITE | BR_INER | BR_TIME | BR_SHAR | S:BR_PLAS | BR_MANA | BR_DISI | DRAIN_MANA | HEAL | HASTE | S:TELE_AWAY | BLINK | TPORT | FORGET | S:S_MONSTERS | S_HI_DRAGON | S_AMBERITES | S_UNIQUE D:The Unicorn of Order, who once stole an eye from the great Serpent D:of chaos, is watching the events unfold with growing distress. It D:regards you as a mortal meddling in the affairs of immortals, and D:attempts to eliminate you. # Oberon is the first Quest monster in Zangband: he must be killed before # the Serpent of Chaos N:860:Oberon, King of Amber G:p:v I:145:50d156:100:82:0 W:99:1:0:12400000 O:0:100:0 B:HIT:UN_BONUS:12d12 B:HIT:UN_POWER:12d12 B:HIT:CONFUSE:10d2 B:HIT:BLIND:10d2 F:UNIQUE | QUESTOR | MALE | CAN_SPEAK | DROP_CORPSE | DUN_PLANAR F:ATTR_MULTI | AMBERITE | RES_TELE F:FORCE_SLEEP | FORCE_MAXHP | FORCE_DEPTH | F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | F:DROP_CHOSEN | REFLECTING | AURA_FIRE | AURA_ELEC F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY | REGENERATE | CAN_FLY F:EVIL | IM_FIRE | IM_COLD | IM_ELEC | POWERFUL | F:NO_CONF | NO_SLEEP | NO_FEAR | LITE_2 S:1_IN_2 | S:TPORT | SCARE | BLIND | S_DEMON | S_ANGEL | S_MONSTERS | S:TELE_TO | CONF | BO_MANA | BA_FIRE | BRAIN_SMASH | BA_NETH | S:BO_ICEE | CAUSE_4 | BA_WATE | BO_PLAS | TELE_LEVEL | TELE_AWAY | S:FORGET | DARKNESS | BA_MANA | S_HI_DRAGON | S_HI_UNDEAD | BA_CHAO S:S_AMBERITES | S_CYBER | HAND_DOOM D:Oberon, the father or grandfather of most Amberites, is as powerful D:as ever. He is afraid that you will upset the balance and will not D:let you harm the Serpent of Chaos. "Oberon, Lord of Amber, stood before D:me in his green and his gold. High, wide and thick, his beard black and D:shot with silver, his hair the same. Green rings in gold settings and D:a blade of golden color." # Morgoth, Mr normal monster! N:861:Morgoth, Lord of Darkness G:P:D I:140:50d280:100:75:0 W:100:1:0:12400000 O:0:100:0 B:HIT:SHATTER:20d10 B:HIT:SHATTER:20d10 B:HIT:LOSE_ALL:10d12 B:TOUCH:UN_POWER F:UNIQUE | MALE | F:FORCE_SLEEP | FORCE_MAXHP | FORCE_DEPTH | DUN_CAVERN F:ONLY_ITEM | DROP_4D2 | F:DROP_GOOD | DROP_GREAT | F:SMART | KILL_WALL | MOVE_BODY | AURA_COLD F:REGENERATE | F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_3 | S:BRAIN_SMASH | S:BA_MANA | BO_MANA | BA_NETH | ANIM_DEAD S:S_MONSTERS | S_UNIQUE | S_AMBERITES | S_HI_UNDEAD | S_HI_DRAGON D:He is the Master of the Pits of Angband. His figure is like a black D:mountain crowned with Lightning. He rages with everlasting anger, his D:body scarred by Fingolfin's eight mighty wounds. He can never rest from D:his pain, but seeks forever to dominate all that is light and good in the D:world. He is the origin of man's fear of darkness and created many foul D:creatures with his evil powers. Orcs, Dragons, and Trolls are his most D:foul corruptions, causing much pain and suffering in the world to please D:him. His disgusting visage, twisted with evil, is crowned with iron, the D:two remaining Silmarils forever burning him. Grond, the mighty Hammer of D:the Underworld, cries defiance as he strides towards you to crush you to a D:pulp! # You must kill the SoC to complete the final quest, and win the game. N:862:The Serpent of Chaos G:J:D I:155:50d504:111:87:0 W:100:1:0:13500000 O:0:50:50 B:CRUSH:SHATTER:22d10 B:CRUSH:SHATTER:22d10 B:BITE:LOSE_ALL:10d12 B:TOUCH:UN_POWER F:UNIQUE | QUESTOR | CAN_SPEAK | ATTR_MULTI | ATTR_ANY | DUN_HORROR F:FORCE_SLEEP | FORCE_MAXHP | FORCE_DEPTH | F:REFLECTING | AURA_FIRE | AURA_ELEC | AURA_COLD | F:ONLY_ITEM | DROP_4D2 | F:DROP_GOOD | DROP_GREAT | DROP_CHOSEN | RES_NETH | F:SMART | KILL_WALL | KILL_BODY | POWERFUL | F:REGENERATE | NONLIVING | CAN_FLY | LITE_1 F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN | RES_TELE S:1_IN_3 | S:S_MONSTERS | BR_CHAO | BA_CHAO | ROCKET | BRAIN_SMASH | S_CYBER | S:BR_NETH | HASTE | BR_MANA | S_HI_UNDEAD | S_HI_DRAGON | S_UNIQUE | S:S_AMBERITES | BR_NUKE | BR_POIS | BR_DISI | HAND_DOOM D:The Big Boss himself. Before the universe the Serpent was. The Unicorn D:of Order fought with Serpent and stole one of its eyes, known as the D:Jewel of Judgement. With the Jewel, Dworkin drew the Pattern and thus D:gave birth to the infinite worlds of shadow. Now the balance has been D:disrupted, the Pattern damaged, and all the shadow is being absorbed by D:the Serpent. Unless it is stopped, the word as we know it will come to D:an end, all order reverting to Primal Chaos. N:863:Quantum dot G:,:v I:110:3d2:20:50:0 W:54:5:0:1700 O:0:0:0 B:SPORE:LOSE_ALL:2d4 B:SPORE:UN_BONUS:2d4 B:SPORE:CONFUSE:2d4 B:SPORE:TERRIFY:2d4 F:NEVER_MOVE | DUN_TOWER F:STUPID | WEIRD_MIND | ATTR_MULTI | ATTR_ANY | QUANTUM | F:MULTIPLY | FRIENDS | F:IM_ACID | IM_COLD | IM_POIS | DROP_CORPSE F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN S:1_IN_2 | S:BLINK | MIND_BLAST D:It is there and it is not. # Note that this monster deliberately does not have the CHAR_MIMIC flag N:864:a Plain Gold Ring G:=:y I:140:55d204:100:100:10 W:110:3:0:50000000 O:100:0:0 B:TOUCH:BLIND:5d5 B:TOUCH:UN_BONUS:10d10 B:TOUCH:LOSE_ALL:10d10 B:TOUCH:EXP_80:10d10 F:DUN_CAVERN F:UNIQUE | FORCE_SLEEP | NEVER_MOVE | ONLY_ITEM | DROP_90 | F:DROP_GOOD | DROP_GREAT | REFLECTING | INVISIBLE | COLD_BLOOD | F:SMART | EMPTY_MIND | REGENERATE | POWERFUL | AURA_FIRE | F:EVIL | DEMON | NONLIVING | IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | IM_POIS | F:RES_TELE | RES_NETH | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE | F:NO_FEAR | NO_CONF | NO_STUN | NO_SLEEP S:1_IN_2 | S:BR_NETH | BR_TIME | BA_MANA | BO_MANA | HAND_DOOM | HEAL | S:S_DEMON | S_HI_UNDEAD | S_HI_DRAGON | TPORT D:It is a ring of immense power. You can hardly believe you've D:finally found the One Ring! N:865:Lourph G:S:u I:140:50d3:200:55:0 W:99:7:0:1650000 O:0:0:0 B:BITE:PARALYZE:5d5 B:BITE:PARALYZE:5d5 B:BITE:POISON:7d5 B:BITE:LOSE_ALL:8d5 F:FRIENDS | DUN_LAIR F:COLD_BLOOD | EMPTY_MIND | REGENERATE | BASH_DOOR | KILL_BODY | KILL_ITEM | F:ANIMAL | HURT_LITE | IM_ACID | IM_FIRE | IM_POIS | F:RES_TELE | RES_NETH | RES_DISE | F:NO_FEAR | NO_CONF | NO_STUN | NO_SLEEP | D:Small brown spiders that lurk in the darkness deep underground. D: An individual would be nearly harmless, but their size allows many D: to crawl where only a single Mirkwood spider could stand. Packs of D: these arachnids may scour the depths untiringly, and their hunger is D: never satisfied. N:866:Caaws G:E:D I:120:51d51:100:88:0 W:101:8:0:1300000 O:0:0:0 B:TOUCH:UN_POWER:13d13 B:TOUCH:UN_BONUS:13d13 B:TOUCH:LOSE_ALL:13d13 B:TOUCH:CONFUSE:13d13 F:FORCE_MAXHP | NEVER_MOVE | F:STUPID | F:CAN_SWIM | CAN_FLY | F:NONLIVING | COLD_BLOOD | EMPTY_MIND | REGENERATE | KILL_BODY | AURA_COLD | F:EVIL | DEMON | IM_ELEC | IM_FIRE | IM_POIS | F:RES_TELE | RES_NETH | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE | F:NO_FEAR | NO_CONF | NO_STUN | NO_SLEEP | F:DUN_HORROR | WILD_WASTE1 | WILD_SWAMP1 | S:1_IN_5 S:HEAL | INVULNER D:A thing like a massive, jet black oyster with four groping tendrils. D: Born of nether, it need not chase its prey, content to seeth D: in the darkness until some unfortunate creature wanders too close. N:867:Culverin G:I:U I:110:16d2:20:7:40 W:19:2:0:1160 O:0:0:0 F:NEVER_BLOW | RAND_25 | ANIMAL | WILD_FOREST1 | DUN_LAIR S:1_IN_1 S:ARROW | D:A many legged scuttling thing, it spits pebbles with deadly force. N:868:Behinder G:.:d I:125:23d20:10:22:80 W:45:40:0:23500 O:0:0:0 B:TOUCH:TERRIFY:5d5 B:BITE:HURT:8d4 F:CHAR_CLEAR | CHAR_MIMIC | ATTR_CLEAR | FORCE_SLEEP | F:SMART | INVISIBLE | COLD_BLOOD | EMPTY_MIND | F:OPEN_DOOR | ANIMAL | NO_FEAR | F:NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | F:DUN_CAVERN | DUN_HORROR | WILD_FOREST1 | WILD_FOREST2 | WILD_MOUNT1 | S:1_IN_10 S:ELDRITCH_HORROR | D:"The Behinder flung itself on his shoulders. Then I knew why D: nobody's supposed to see one. I wish I hadn't. To this day I D: can see it, plain as a fence at noon, and forever I will be D: able to see it." N:869:Boadile G:S:g I:120:19d12:20:15:30 W:38:20:0:6200 O:0:0:0 B:BITE:HURT:2d5 B:CRUSH:HURT:2d8 B:CRUSH:HURT:2d8 F:FORCE_MAXHP | STUPID | COLD_BLOOD | BASH_DOOR | F:ANIMAL | RES_WATE | CAN_SWIM | WILD_SHORE | DUN_CAVERN | DUN_DARKWATER F:WILD_OCEAN | D:"...Head something like a croc's, only bigger. Around forty D: feet long. Able to roll itself into a big beachball with teeth. D: Fast on land or in water - and a hell of a lot of little legs D: on each side." N:870:Ebony monk G:p:D I:120:28d19:20:28:5 W:55:3:0:39000 O:0:0:100 B:KICK:HURT:7d1 B:HIT:EXP_10:7d2 B:KICK:HURT:7d4 B:HIT:EXP_10:7d2 F:MALE | DROP_1D2 | OPEN_DOOR | BASH_DOOR | IM_COLD | F:IM_POIS | RES_NETH | NO_FEAR | NO_CONF | NO_SLEEP | DUN_TEMPLE F:DROP_CORPSE | DROP_SKELETON | LITE_2 S:1_IN_9 | S:BO_NETH D:A monk in dusky robes trained in the most lethal martial arts. N:871:Unstable worm mass G:w:o I:110:10d3:10:5:5 W:20:4:0:76 O:0:0:0 B:EXPLODE:FIRE:5d5 F:RAND_50 | RAND_25 | CAN_SWIM | DUN_CAVERN | DUN_DARKWATER F:STUPID | WEIRD_MIND | MULTIPLY | BASH_DOOR | F:ANIMAL | HURT_LITE | NO_FEAR D:A seething mass of thick, glowing worms. N:872:Auto-mobile G:g:R I:130:36d23:10:50:12 W:72:5:0:185000 O:0:0:0 B:BUTT:HURT:8d3 B:CRUSH:HURT:8d7 F:DUN_TOWER F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | KILL_BODY | KILL_ITEM | F:EMPTY_MIND | BASH_DOOR | IM_COLD | IM_ELEC | IM_POIS | SILLY F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | WILD_WASTE1 | WILD_WASTE2 D:It is a streamlined metal demon, moving under its own power to crush you. D: It rumbles hungrily. N:873:Shallow puddle G:~:B I:115:10d6:10:17:0 W:19:6:0:410 O:0:0:0 B:TOUCH:ACID:2d3 B:TOUCH:ACID:2d3 F:NEVER_MOVE | COLD_BLOOD | DUN_CAVERN | DUN_MINE | DUN_LAIR | WILD_FOREST1 F:CAN_SWIM | IM_ELEC | IM_ACID | IM_POIS | F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | F:EMPTY_MIND D:It is a placid watery pool. N:874:Deep puddle G:~:b I:125:18d6:15:40:0 W:35:6:0:3500 O:0:0:0 B:TOUCH:ACID:4d3 B:TOUCH:ACID:3d3 F:NEVER_MOVE | COLD_BLOOD | DUN_CAVERN | DUN_MINE | DUN_LAIR | WILD_FOREST2 F:CAN_SWIM | IM_ELEC | IM_ACID | IM_POIS | F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | F:EMPTY_MIND D:It is a large, rippling watery pool. N:875:Noxious fume G:#:y I:90:4d3:20:37:30 W:32:4:0:310 O:0:0:0 B:TOUCH:POISON:3d3 F:RAND_25 | RAND_50 | INVISIBLE | MULTIPLY | DUN_TOWER F:CAN_FLY | CAN_SWIM | PASS_WALL | KILL_BODY | F:IM_FIRE | IM_COLD | IM_ACID | IM_POIS | F:NONLIVING | EMPTY_MIND | COLD_BLOOD | F:WILD_MOUNT2 | WILD_WASTE1 | WILD_WASTE2 | WILD_SWAMP1 | WILD_SWAMP2 F:NO_FEAR | NO_CONF | NO_SLEEP | NO_STUN | D:It is a swirling cloud of yellowish gas. N:876:Pattern vortex G:v:R I:110:35d286:20:200:1 W:69:100:0:3100000 O:0:0:0 B:EXPLODE:LOSE_ALL:50d100 F:RAND_25 | EMPTY_MIND | REFLECTING | FORCE_MAXHP | FORCE_SLEEP | F:REGENERATE | AURA_ELEC | BASH_DOOR | KILL_BODY | F:NONLIVING | IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | IM_POIS | F:RES_TELE | RES_NETH | RES_WATE | RES_PLAS | RES_DISE | F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_FLY | LITE_2 S:1_IN_10 | S:HOLD D:A swirling, rotating red disk. A funnel thrusts out, seeking the D: one who disturbed the Pattern. N:877:Storm Troll G:T:B I:120:37d55:20:55:100 W:73:2:0:150000 O:0:100:0 B:HIT:ELEC:3d10 B:BITE:HURT:1d22 B:HIT:COLD:3d10 B:BITE:HURT:1d22 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | DUN_LAIR | WILD_WASTE1 | WILD_WASTE2 F:DROP_2D2 | SMART | TAKE_ITEM | F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | POWERFUL F:IM_ACID | IM_COLD | IM_ELEC | TROLL | EVIL | LITE_1 S:1_IN_7 | S:BA_COLD | BO_ICEE | BO_WATE | BA_ELEC D:The dungeon shakes as an enormous troll-form thunders into view, hurling D:ice and lightning in every direction. It is thirty feet tall, has six D:heads, and is enraged at your impudence. N:878:Bat of Gorgoroth G:b:g I:120:20d10:20:30:30 W:40:3:0:6100 O:0:0:0 B:BITE:POISON:1d10 B:CLAW:HURT:1d4 F:DROP_60 | RAND_25 | CAN_FLY | DUN_MINE | DUN_RUIN | DUN_CAVERN | WILD_WASTE2 F:BASH_DOOR | MOVE_BODY | FRIENDS | F:ANIMAL | IM_POIS S:1_IN_8 | S:SCARE | BR_POIS | BR_DARK D:Fed with horrid meats and grown to enormous size, this slavering creature D:seeks livelier prey. N:879:Spectral Wyrm G:D:w I:130:45d94:40:100:255 W:90:4:0:1900000 O:50:50:0 B:CLAW:HURT:10d12 B:CLAW:HURT:10d12 B:CLAW:HURT:10d12 B:BITE:UN_BONUS:8d8 F:FORCE_MAXHP | EVIL | UNDEAD | CAN_FLY | DUN_RUIN F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | F:POWERFUL | MOVE_BODY | PASS_WALL | INVISIBLE | F:DRAGON | NO_CONF | NO_SLEEP | RES_TELE | F:RES_NETH | RES_NEXU | RES_DISE | S:1_IN_3 | S:BR_NEXU | BR_NETH | BR_DISE | S:S_HI_DRAGON S:SCARE | BLINK | SHRIEK D:The tortured soul of a once powerful wyrm. N:880:Creeping dust G:.:b I:140:6d2:30:30:100 W:90:6:0:68000 O:0:0:0 B:HIT:CONFUSE:3d4 F:CHAR_MIMIC | ATTR_MULTI | ATTR_ANY | DUN_TOWER F:RAND_50 | RAND_25 | FRIENDS | F:FORCE_SLEEP | MULTIPLY | HURT_ROCK | F:EMPTY_MIND | COLD_BLOOD | F:NO_CONF | NO_SLEEP | NO_FEAR S:1_IN_2 | S:BLINK | TPORT | BO_MANA | S:S_MONSTER | S_DEMON | S_HI_UNDEAD | D:This is what happens to a mage when a powerful spell fails. N:881:Dark elven shade G:h:w I:130:36d16:20:25:10 W:72:2:0:280000 O:0:0:100 B:TOUCH:EXP_VAMP:1d16 B:TOUCH:UN_POWER B:TOUCH:LOSE_DEX:8d8 B:TOUCH:LOSE_INT:8d8 F:MALE | F:FORCE_SLEEP | FORCE_MAXHP F:ONLY_ITEM | DROP_2D2 | DUN_GRAVE | DUN_RUIN F:SMART | INVISIBLE | COLD_BLOOD | UNDEAD | F:EVIL | HURT_LITE | NO_CONF | NO_SLEEP | S:1_IN_2 | S:HEAL | BLINK | BLIND | CONF | CAUSE_3 | DARKNESS | S:ANIM_DEAD | SHRIEK | TELE_AWAY | BA_DARK | DRAIN_MANA | S:S_MONSTER | S_UNDEAD | S_DEMON | S_KIN | D:A dark elven figure that glows with an inner light. N:882:Anti-magic hound G:Z:v I:110:25d15:30:30:0 W:49:4:0:9200 O:0:0:0 B:CLAW:HURT:3d3 B:BITE:UN_BONUS:3d12 B:CLAW:HURT:3d3 B:BITE:UN_BONUS:3d12 F:DUN_PLANAR | DUN_CAVERN F:FORCE_SLEEP | FRIENDS | DROP_SKELETON | DROP_CORPSE F:BASH_DOOR | RES_DISE F:ANIMAL | NO_CONF | NO_SLEEP S:1_IN_5 | S:BR_DISE D:A field of static energy surrounds this hound. Its presence seems D:to dampen all other energies... N:883:Mana hound G:Z:B I:130:39d16:30:50:0 W:78:4:0:290000 O:0:0:0 B:BITE:HURT:25d1 B:BITE:HURT:25d1 B:BITE:HURT:25d1 B:CLAW:HURT:3d3 F:FORCE_SLEEP | F:FRIENDS | DUN_PLANAR | DUN_CAVERN F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE F:ANIMAL | NO_CONF | NO_SLEEP | LITE_1 S:1_IN_8 | S:BR_MANA D:A powerful aura radiates from this canine creature. It feels D:like... magic? N:884:Vore G:S:v I:125:27d13:20:30:40 W:54:2:0:24500 O:50:0:50 B:CLAW:HURT:4d8 B:CLAW:HURT:8d4 F:DUN_LAIR | DUN_RUIN F:DROP_1D2 | DROP_SKELETON | DROP_CORPSE F:OPEN_DOOR | BASH_DOOR | IM_POIS | IM_ACID | IM_COLD F:EVIL S:1_IN_5 S:BO_MANA D:A gaunt, vaguely humanoid torso merged with the form of a giant D:spider, ugly as sin. It screams horribly as it charges at you. N:885:Giant silver ant G:a:W I:110:28d9:14:50:20 W:55:1:0:14500 O:0:0:0 B:BITE:HURT:3d10 B:BITE:HURT:3d10 B:BITE:HURT:3d10 F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON F:WEIRD_MIND | BASH_DOOR | DUN_TOWER | DUN_RUIN F:ANIMAL | IM_ACID | IM_FIRE | IM_COLD D:This ant shines strangely, as if made out of metal. N:886:Bakeneko G:f:B I:120:12d7:25:22:40 W:23:2:0:520 O:0:0:0 B:CLAW:HURT:1d6 B:CLAW:HURT:1d6 B:BITE:ELEC:1d10 F:DUN_LAIR F:ANIMAL | FRIENDS | IM_ELEC | AURA_ELEC | EVIL S:1_IN_10 S:HEAL D:A feline surrounded by a strange energy. N:887:Iridescent worm mass G:w:v I:100:20d2:7:5:10 W:48:3:0:2300 O:0:0:0 B:CRAWL:UN_POWER:1d3 F:DUN_TOWER | DUN_CAVERN | DUN_HORROR F:RAND_50 | ATTR_MULTI | CAN_SWIM | F:STUPID | WEIRD_MIND | MULTIPLY | BASH_DOOR | LITE_1 F:ANIMAL | NO_FEAR D:This mass of worms seems to shimmer with strange power. N:888:Giant time fly G:F:G I:130:12d2:12:10:50 W:31:3:0:1800 O:0:0:0 B:BITE:PARALYZE F:FORCE_SLEEP | F:RAND_50 | RAND_25 | DUN_LAIR | DUN_RUIN | WILD_MOUNT2 | F:WEIRD_MIND | BASH_DOOR | F:ANIMAL | CAN_FLY S:1_IN_5 | S:BR_TIME | HASTE | SLOW D:It wasn't there a second ago.... N:889:Sheep G:q:w I:110:1d4:5:3:255 W:0:0:0:0 B:DROOL B:GAZE B:BUTT F:ANIMAL | FRIENDS | FRIENDLY | WILD_GRASS | F:DROP_CORPSE | DROP_SKELETON | F:STUPID | EMPTY_MIND | RAND_50 | RAND_25 D:Awww, look at the sheep isn't it cute... N:890:Warp drake G:d:o I:110:28d15:25:55:15 W:55:5:0:34500 O:40:50:10 B:CLAW:HURT:2d5 B:CLAW:HURT:2d5 B:BITE:HURT:2d8 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | WILD_FOREST2 F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | F:INVISIBLE | ATTR_CLEAR | F:CAN_FLY | PASS_WALL | F:DRAGON | NO_CONF | NO_SLEEP | F:WEIRD_MIND | F:RES_NEXU | RES_TELE | S:1_IN_6 | S:BLINK | TPORT | TELE_TO | TELE_AWAY | TELE_LEVEL | S:BR_NEXU | BR_GRAV | D:A vaguely dragon-shaped distortion in the ether; it shimmers D:like a heat haze as you study it carefully. N:891:Great Dimensional Wyrm G:D:G I:120:35d58:25:55:15 W:70:6:0:218000 O:50:50:0 B:CLAW:HURT:6d15 B:CLAW:HURT:6d15 B:BITE:HURT:8d15 F:FORCE_SLEEP | FORCE_MAXHP | DUN_CAVERN | WILD_FOREST1 | WILD_GRASS F:ONLY_ITEM | DROP_3D2 | F:INVISIBLE | ATTR_CLEAR | DROP_GOOD | F:CAN_FLY | PASS_WALL | POWERFUL | MOVE_BODY | F:SMART | WEIRD_MIND | F:DRAGON | NO_CONF | NO_SLEEP | F:RES_NEXU | RES_TELE | S:1_IN_4 | S:BR_GRAV | BR_INER | BR_NEXU | BR_TIME | S:HASTE | SLOW | HOLD | S:BLINK | TPORT | TELE_TO | TELE_AWAY | TELE_LEVEL | D:This immense, vaguely dragon-shaped distortion in the ether has D:mastered the magics of both space and time. zangband/lib/edit/v_info.txt0000755000000000000000000034224210250356274015072 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2005/06/04 12:53:28 $ # File: v_info.txt # This file is used to initialize the "lib/raw/v_info.raw" file, which is # used to initialize the "vault template" information for the Angband game. # Do not modify this file unless you know exactly what you are doing, # unless you wish to risk possible system crashes and broken savefiles. # After modifying this file, delete the "lib/raw/v_info.raw" file. # Note that the "spacing" in the "description" lines is very important! # New vault types added for Zangband -TY # === Understanding v_info.txt === # N:serial number:vault name # X:room type:rating:rows:columns # D:lines giving full layout of vault using symbols below # 'N' indicates the beginning of an entry. The serial number must # increase for each new vault. # 'X' is for extra information - room type, rating, rows, and # columns. Lesser vaults are room type 7 and have a maximum x-y size # of 33x22; greater vaults are room type 8 and have a maximum size of 66x44. # Anything larger is a *greater* vault. # 'D' lines describe the layout of the vault. Lines must be padded # with spaces to fill the dimensions specified. Vaults are described # with the following symbols: # % - outside of the vault, where corridors may be connected # # - granite # X - impenetrable rock # * - treasure or trap # + - secret door # ^ - trap # & - monster up to 5 levels out of depth (OOD) # @ - monster up to 11 levels OOD # 9 - monster up to 9 levels OOD and treasure up to 7 levels OOD # 8 - monster up to 40 levels OOD and treasure up to 20 levels OOD # , - monster up to 3 levels OOD and/or treasure up to 7 levels OOD # Use this to measure columns 1 # 1 2 3 4 5 6 7 8 9 0 # 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 # Version stamp (required) V:2.7.5 ### Lesser vaults (type 7) -- maximum size 33x22 ### # ? # N:0:Lesser vault (round) X:7:5:12:20 D: %%%%%% D: %%%..##..%%% D: %%....####....%% D: %......#**#......% D:%...,.##+##+##.,...% D:%.,.,.#*#*&#*#.,.,.% D:%.,.,.#*#&*#*#.,.,.% D:%...,.##+##+##.,...% D: %......#**#......% D: %%....####....%% D: %%%..##..%%% D: %%%%%% # ? # N:1:Lesser vault (octagon) X:7:5:14:20 D: %%%%%%%%%%%%%% D: %%.##########.%% D: %%..#..,,,,..#..%% D:%%,..#.,####,.#..,%% D:%....#.,#**#,.#....% D:%.###+,##&&##,+###.% D:%.#..,,#*&**#,,..#.% D:%.#..,,#**&*#,,..#.% D:%.###+,##&&##,+###.% D:%....#.,#**#,.#....% D:%%,..#.,####,.#..,%% D: %%..#..,,,,..#..%% D: %%.##########.%% D: %%%%%%%%%%%%%% # ? # N:2:Lesser vault (chambered octagon) X:7:5:12:20 D: %%%%%%%%%%%% D: %%%%..........%%%% D: %...###+##+###...% D:%%...#,,#,,#,,#...%% D:%.###+##+##+##+###.% D:%.#,,#&&#**#&&#,,#.% D:%.#,,#&&#**#&&#,,#.% D:%.###+##+##+##+###.% D:%%...#,,#,,#,,#...%% D: %...###+##+###...% D: %%%%..........%%%% D: %%%%%%%%%%%% # ? # N:3:Lesser vault (square) X:7:5:12:20 D:%%%%%%%%%%%%%%%%%%%% D:%*.......&........*% D:%.################.% D:%.#,.,.,.,.,.,.,.#.% D:%.#.############,#.% D:%.#,+,&&+**#&*,#.#&% D:%&#.#,*&#**+&&,+,#.% D:%.#,############.#.% D:%.#.,.,.,.,.,.,.,#.% D:%.################.% D:%*........&.......*% D:%%%%%%%%%%%%%%%%%%%% # ? # N:4:Lesser vault (diagonal \) X:7:5:12:20 D:%%%%%%%%%%%%%%%%% D:%,,,##,,,,##....%% D:%,,,,##,,,,##....%% D:%#,,,,##,,,,##....%% D:%##,,,,##,,,,##....% D:%.##,,,,,,,,,,#+...% D:%..#+,,,,,,,,,,##..% D:%...##,,,,##,,,,##.% D:%%...##,,,,##,,,,##% D: %%...##,,,,##,,,,#% D: %%...##,,,,##,,,,% D: %%%%%%%%%%%%%%%%% # ? # N:5:Lesser vault (diagonal /) X:7:5:12:20 D: %%%%%%%%%%%%%%%%% D: %%....##,,,,##,,,% D: %%....##,,,,##,,,,% D:%%....##,,,,##,,,,#% D:%....##,,,,##,,,,##% D:%...+#,,,,,,,,,,##.% D:%..##,,,,,,,,,,+#..% D:%.##,,,,##,,,,##...% D:%##,,,,##,,,,##...%% D:%#,,,,##,,,,##...%% D:%,,,,##,,,,##...%% D:%%%%%%%%%%%%%%%%% # ? # N:6:Lesser vault (rectangles) X:7:5:12:20 D:%%%%%%%%%%%%%%%%%%%% D:%,################,% D:%^#.*...&..,....,#^% D:%^#...,......&...#^% D:%^#######++#######^% D:%^+.,..&+,*+*....+^% D:%^+..*.,+.&+.,.&.+^% D:%^#######++#######^% D:%^#....,.,.....,.#^% D:%^#..&......*....#^% D:%,################,% D:%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:7:Lesser vault (spiral) X:7:5:19:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.+################.% D:%.#^#*&..,.......*#.% D:%.#.#.###########.#.% D:%.#.#.#*.,.....*#.#.% D:%.#.#.#.#######.#.#.% D:%.#.#.#.#,...*#.#.#.% D:%.#.#.#.#,###.#.#.#.% D:%.#,#,#,#,,*#,#,#,#.% D:%.#.#.#.###,#.#.#.#.% D:%.#.#.#*.,.*#.#.#.#.% D:%.#.#.#######.#.#.#.% D:%.#.#*...,...*#.#.#.% D:%.#.###########.#.#.% D:%.#*.....,....&*#^#.% D:%.################+.% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:8:Lesser vault (layers) X:7:5:21:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.########+########.% D:%.#.......,.......#.% D:%.#.#############.#.% D:%.#.#....+.#*...#.#.% D:%.#.#.####+####.#.#.% D:%.#.#.#...&...#.#.#.% D:%.#.#.#.#####.#.#.#.% D:%.#.#.#.#*,*#.#.#.#.% D:%.#^#^#^#,,,#^#^#.#.% D:%.#.#.#.#*,*#.#.#.#.% D:%.#.#.#.##+##.#.#.#.% D:%.#.#.#..+,#*.#.#.#.% D:%.#.#.#########.#.#.% D:%.#.#.....,.....#.#.% D:%.#.######+######.#.% D:%.#.....*#.+......#.% D:%.#################.% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:9:Lesser vault (bank) X:7:7:9:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.&XXXXXXXXXXXXXXXXX% D:%&&XXXXX+XXX+XX*,XXX% D:%&&+###########,*XXX% D:%.&XXX+XXX+XXX+XXXXX% D:%.&XXXXXXXXXXXXXXXXX% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:10:Lesser vault (mine) X:7:7:9:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.XXXXXXXXXXXXXXXXXX% D:%.XX##XXXX*,,XX,*,XX% D:%.*#XX,*##X*#XX***XX% D:%.XXXX*,XXXXX##,*,XX% D:%.XXXXXXXXXXXXXXXXXX% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:11:Lesser vault (maze) X:7:5:22:22 D:%%%%%%%%%%%%%%%%%%%%%% D:%.XXXXXXX............% D:%.XX,,.XXXXXXXXXXXXX.% D:%.XXXX*...&........X.% D:%.X.,,XXXXXXXXXXXX*X.% D:%.X.XXX.....*......X.% D:%.X..XX.XXXXXXXXXX.X.% D:%.XX.X.&.XX.X.....*X.% D:%.XX.XXX...*X,XXXX.^.% D:%.X.XX,XXXX.XXXX,XXX.% D:%.X.XX.....*..&.*X,X.% D:%.X.,XXXXXXXXXXX.X,X.% D:%.XXXX....*..X,X.X,X.% D:%.X....XXX.X.X.X.X.X.% D:%.X.XXXX,X.X.&.X.X.X.% D:%.X.X...*X.XXXXX.X.X.% D:%.X.X,XX&X....&..X.X.% D:%.X.XXXX.XXXXXXXXX.X.% D:%.X.....*...X*X*X..X.% D:%.XX^XXXXXX..X*X,XXX.% D:%........XXXXXXXXXXX.% D:%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:12:Lesser vault (prison) X:7:10:15:35 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.+..&..+..&..+..&..+..&..+..&..+.% D:%.###.#####.#####.#####.#####.###.% D:% #,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.% D:%.###.#####.#####.#####.#####.###.% D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.% D:%.###.#####.#####.#####.#####.###.% D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.% D:%.###.#####.#####.#####.#####.###.% D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.% D:%.###.#####.#####.#####.#####.###.% D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.% D:%.###.#####.#####.#####.#####.###.% D:%&+..&..+..&..+..&..+..&..+..&..+.% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:13:Lesser vault (camp) X:7:10:15:37 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...................................% D:%.####^^^^^^^^^^^^^^^^^^^^^^^^^####.% D:%.#,&############+++############&,#.% D:%.###+.........................+###.% D:%.^#....##+#.....#+#....##+#....#^..% D:%.^+....#,,#.....#,#....#,,#....+^..% D:%.^+....+&&+..&..+&+..&.+&&+....+^..% D:%.^+....#,,#.....#,#....#,,#....+^..% D:%.^#....#+##.....#+#....#+##....#^..% D:%.###+.........................+###.% D:%.#,&############+++############&,#.% D:%.####^^^^^^^^^^^^^^^^^^^^^^^^^####.% D:%...................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:14:Lesser vault (serpent) X:7:10:17:32 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..............................% D:%..##########################..% D:%.##...#.^.#.^.#...#...#.,.###.% D:%.#..#.*.#...#...#.^.#...#...+.% D:%.#.##########################.% D:%.#..#..#.,.#.^.#.^.#.*.#...##.% D:%.##^#.#..#...#...#...#...#..#.% D:%.#..#.####################..#.% D:%.#,##.&+,,,,,,,,,,,,,,,,,#*##.% D:%.#..##&+,,,,,,,,,,,,,,,,,#..#.% D:%.##^#######################.#.% D:%.#..#...#...#...#...#.^.#...#.% D:%.##...#.^.#.,.#.*.#.^.#...###.% D:%..##########################..% D:%..............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:15:Lesser vault (Zelazny) X:7:5:18:19 D:%%%%%%%%%%%%%%%%%%% D:%.................% D:%.###############.% D:%.+,,,,,,,,,,,,,#.% D:%.###########,,,#.% D:%.#,,,,+..##,,,##.% D:%.#,,,##.##,,,##..% D:%.#,*##.##,,,##...% D:%.#&##.##,,,##.##.% D:%.###.##,,,##.###.% D:%.##.##,,,##.##&#.% D:%...##,,,##.##*,#.% D:%..##,,,##..+,,,#.% D:%.##,,,##########.% D:%.#,,,,,,,,,,,,,+.% D:%.###############.% D:%.................% D:%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:16:Lesser vault (overlap) X:7:5:12:18 D:%%%%%%%%%%%%%%%%%% D:%................% D:%.##########.....% D:%.#,,,^^^^^+&....% D:%.#,,,##########.% D:%.#,,,#****+,,,#.% D:%.#,,,+****#,,,#.% D:%.##########,,,#.% D:%....&+^^^^^,,,#.% D:%.....##########.% D:%................% D:%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:17:Lesser vault (celtic) X:7:5:17:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.#####..#+#..#####.% D:%.#&,##.##&##.##,&#.% D:%.#+##..#*^*#..##+#.% D:%.#....###.###....#.% D:%...####..&..####...% D:%.###*##.#+#.##*###.% D:%.+&.^..&+*+&..^.&+.% D:%.###*##.#+#.##*###.% D:%...####..&..####...% D:%.#....###.###....#.% D:%.#+##..#*^*#..##+#.% D:%.#&,##.##&##.##,&#.% D:%.#####..#+#..#####.% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:18:Lesser vault (mirror) X:7:5:17:19 D:%%%%%%%%%%%%%%%%%%% D:%.................% D:%.+#############+.% D:%.##&,,,,#,,,,&##.% D:%.#&#,,,###,,,#&#.% D:%.#,,,,,,#,,,,,,#.% D:%.##,,,,,#,,,,,##.% D:%.###,,,^#^,,,###.% D:%.#######+#######.% D:%.###,,,^#^,,,###.% D:%.##,,,,,#,,,,,##.% D:%.#,,,,,,#,,,,,,#.% D:%.#&#,,,###,,,#&#.% D:%.##&,,,,#,,,,&##.% D:%.+#############+.% D:%.................% D:%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:19:Lesser vault (tower) X:7:5:18:15 D:%%%%%%%%%%%%%%% D:%.............% D:%..XXX...XXX..% D:%..X&XXXXX&X..% D:%..XX*****XX..% D:%...XX***XX...% D:%....X#+#X....% D:%....X&&&X....% D:%....X^^^X....% D:%....X#+#X....% D:%....X,,,X....% D:%....X^^^X....% D:%...XX+#+XX...% D:%..XX,&,&,XX..% D:%..X^^^*^^^X..% D:%.##+#####+##.% D:%...&.....&...% D:%%%%%%%%%%%%%%% ### Greater vaults (type 8) -- maximum size 66x44 ### # ? # N:20:Greater vault (GCV) X:8:45:17:39 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%+########X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X% D:%X8#########&X% D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X########X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X% D:%X8###�#8###&X% D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X########X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X% D:%X8#########&X% D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X########&#&+% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # ? # N:21:Greater vault (large) X:8:35:18:40 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X,#,#,#,#,#,#,#,#*@@*#,#,#,#,#,#,#,#,X% D:%X+XXXXXXXXXXXXXXXX##XXXXXXXXXXXXXXXX+X% D:%X.,..,.X&.&.,*XX******XX*,.&.&X.,...,#% D:%X..,.^^X....,XX***@@***XX,....X^^..,.#% D:%XXXXXX+X^&.&XX***@##@***XX&.&^X+XXXXXX% D:%X,.&.^^X+XXXX***@#XX#@***XXXX+X^^.,..X% D:%X..,&,.X^^^@X**@#X88X#@**#@^^^X.,..&,X% D:%X.,....X^^^@#**@#X88X#@**X@^^^X.&.,..X% D:%X...,^^X+XXXX***@#XX#@***XXXX+X^^..,.X% D:%XXXXXX+X^&.&XX***@##@***XX&.&^X+XXXXXX% D:%#.,..^^X.....XX***@@***XX,....X^^.,..X% D:%#...,..X&.&.,*XX******XX*,.&.&X..,..,X% D:%X+XXXXXXXXXXXXXXXX##XXXXXXXXXXXXXXXX+X% D:%X,#,#,#,#,#,#,#,#*@@*#,#,#,#,#,#,#,#,X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # ? # N:22:Greater vault (butterfly) X:8:25:18:40 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X*9..&XX***++^^^^^^^^^^^^++***XX&..*9X% D:%X9..&XX,,,,,XX^^^^^^^^^^XX,,,,,#X&..*X% D:%X..&#X.....,.XX^^^^^^^^XX..&....XX&..X% D:%X.&XX..,.&....XX^^^^^^XX..,...&..XX&.X% D:%X&XX..*...&.^..XX^^^^XX..*....,..,XX&X% D:%XXXX+XXXXXXXXXXXXX++XXXXXXXXXXXXX+XXXX% D:%+....,.,.X&&&&***+99+***&&&&X,.,.,...+% D:%+...,.,.,X&&&&***+99+***&&&&X.,.,....+% D:%XXXX+XXXXXXXXXXXXX++XXXXXXXXXXXXX+XXXX% D:%X&XX..*....&...XX^^^^XX...*...&,..#X&X% D:%X.&XX..&.^....XX^^^^^^XX....&....XX&.X% D:%X..&XX....&..XX^^^^^^^^XX..,..*.XX&..X% D:%X*..&#X,,,,,XX^^^^^^^^^^XX,,,,,XX&..9X% D:%X9*..&XX***++^^^^^^^^^^^^++***XX&..*9X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:23:Greater vault (castle) X:8:35:27:27 D:%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.........................% D:%..XXXXX..XXX+XXX..XXXXX..% D:%..X,,,X..X.,,,.X..X,,,X..% D:%..X,,*XXXX.&&&.XXXX*,,X..% D:%..XXXX+....&&&....+XXXX..% D:%.....X.....,,,.....X.....% D:%.....X..,XXX+XXX,..X.....% D:%.....X.XXX^^^^^XXX.X.....% D:%.&...X.X,,*****,,X.X..&..% D:%....XX.X,XXX+XXX,X.XX....% D:%....X..X,X@@@@@X,X..X....% D:%....X..X,X@999@X,X..X....% D:%....X..X,X@989@X,X..X....% D:%....X..X,X@999@X,X..X....% D:%....X..X,X@@@@@X,X..X....% D:%....XX.X,XXX+XXX,X.XX....% D:%.....X.X,,*****,,X.X.....% D:%.....X.XXX^^^^^XXX.X.....% D:%.....X..,XXX+XXX,..X.....% D:%.....X.....&&&.....X.....% D:%..XXXX+....&&&....+XXXX..% D:%..X,,*XXXX.&&&.XXXX*,,X..% D:%..X,,,X..X.,,,.X..X,,,X..% D:%..XXXXX..XX^^^XX..XXXXX..% D:%.........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:24:Greater vault (chambers) X:8:25:15:40 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%&+.^..^..^..^..^..^..^..^..^..^..^..+&% D:%+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+% D:%.X.&.^,X&^&^X****+^*^@^X.*.&..X..*.,X.% D:%^X.,.&^+^&^@X^^^^X@^*^*X....*^+.^...X^% D:%.X*..,.XXX+XXXX+XXXX+XXX.&.^..X..&,.X.% D:%^X..^.*X*..^&&@@*X,,,,,XXXX+XXX,....X^% D:%.XX+XXXXXXXXXXXXXX,*8*,X,,,,,,XXX+XXX.% D:%^X*&X.&,*.X,*&^*^X,,,,,X,,,,,,X....,X^% D:%.X&,+....*+,*&^*^XXXXXXXXXX+XXX.,...+.% D:%^X.,X.*.&.X,*&^*^+.,.&.^*.&^&^X.....X^% D:%.X^*X.,..,X,*&^*^X*.^*.,..&&&^X,..,.X.% D:%+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+% D:%&+..^..^..^..^..^..^..^..^..^..^..^.+&% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:25:Greater vault (Sierpinski) X:8:35:28:39 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..................X..................% D:%.................XXX.................% D:%.................X8X.................% D:%.....&..........XX+XX................% D:%...............XX999XX........&......% D:%......&........X@X9X@X...............% D:%..............XXXX+XXXX..............% D:%.............XX*@*@*@*XX....&........% D:%.............X@X*@*@*X@X.............% D:%............XXXX**@**XXXX............% D:%..........&XX,,,X***X,,,XX&..........% D:%..........XX,X@X,X,X,X@X,XX..........% D:%.........XXXXX+XXXXXXX+XXXXX.........% D:%........XX+,,,,,,,,,,,,,,,+XX........% D:%........X@X,,,,,,,,,,,,,,,X@X........% D:%.......XX+XX,,,,,,,,,,,,,XX+XX.......% D:%......XX,,,XX,,,,,,,,,,,XX,,,XX......% D:%......X,X,X,X,,,,,,,,,,,X,X,X,X......% D:%.....XXXX+XXXX,,,,,,,,,XXXX+XXXX.....% D:%....XX*******XX,,,,,,,XX*******XX....% D:%....X,X*****X,X,,,,,,,X,X*****X,X....% D:%...XXXX*****XXXX,,,,,XXXX*****XXXX...% D:%..XX,,,X***X,,,XX,,,XX,,,X***X,,,XX..% D:%..X,X,X,X*X,X,X,X,,,X,X,X,X*X,X,X,X..% D:%.XXXXXXXX+XXXXXXXXXXXXXXXXX+XXXXXXXX.% D:%.........&.................&.........% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # XXX slot 26 free # Topi Ylinen # N:27:Greater vault (great spiral) X:8:40:39:39 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.....................................% D:%.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X@X.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^^+.% D:%.X.X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X.X.X,..,..,..,..,..,..,..,..,..,.X.% D:%.X.X^X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.X.X.X......&...&...&...&...@..X,X.% D:%.X.X^X,X@XXXXXXXXXXXXXXXXXXXXXXX.X.X.% D:%.X.X.X.X.X&.........8.........&X*X.X.% D:%.X.X^X.X.X.XXXXXXXXXXXXXXXXXXX.X.X,X.% D:%.X.X.X,X.X.X.^.^.^.^.^.^.^.^.X.X.X.X.% D:%.X.X^X.X.X.X^XXXXXXXXXXXXXXX*X.X*X.X.% D:%.X.X.X.X&X.X.X,..,..,..,..,X.X.X.X,X.% D:%.X.X^X,X.X.X^X.XXXXXXXXXXX.X*X.X.X.X.% D:%.X.X.X.X.X.X.X.X..&..&..9X.X.X.X*X.X.% D:%.X.X^X.X&X.X^X,X9XXXXXXX.X,X*X.X.X,X.% D:%.X.X.X,X.X.X.X.X&X@@..&X.X.X.X.X.X.X.% D:%.X.X^X.X.X9X^X.X*X+XXX.X&X.X*X.X*X.X.% D:%.X.X.X.X.X.X.X,X^+8+^X.X.X,X.X.X.X,X.% D:%.X9X^X,X.X.X^X.XXX+X^X.X*X.X*X.X.X.X.% D:%.X.X.X.X&X.X.X.,.,,X.X9X.X.X.X.X*X.X.% D:%.X.X^X.X.X.X^XXXXXXX^X.X*X,X*X9X.X,X.% D:%.X.X.X,X.X.X.^.^.^.^.X.X.X.X.X.X.X.X.% D:%.X.X^X.X.X.XXXXXXXXXXX.X^X.X^X.X*X.X.% D:%.X.X.X.X8X&.....9.....&X.X,X.X.X.X,X.% D:%.X.X^X,X.XXXXXXXXXXXXXXX^X.X^X.X.X.X.% D:%.X.X.X.X..&....8......@..X.X.X.X*X.X.% D:%.X.X^X.XXXXXXXXXXXXXXXXXXX,X^X.X.X,X.% D:%.X.X.X,..,..,..,..,..,..,..X.X.X.X.X.% D:%.X.X^XXXXXXXXXXXXXXXXXXXXXXX^X.X*X.X.% D:%.X.X.*.^.*.*.*.*.*.*.*.*.*.^.X.X.X,X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.% D:%.X&.............9.............&X*X.X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X,X.% D:%.+^^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.X,X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X.% D:%.....................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:28:Greater vault (greater castle) X:8:40:25:51 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.................................................% D:%...XXXXXX...............................XXXXXX...% D:%..XX,,,,XX.............................XX,,,,XX..% D:%.XX,*99*,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,*99*,XX.% D:%.XX,*99*,+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^+,*99*,XX.% D:%..XX,,,,XXXXXXXXXXXXXXX+X+XXXXXXXXXXXXXXX,,,,XX..% D:%...XX++XX...............X...............XX++XX...% D:%....X^^X.............XXXXXXX.............X^^X....% D:%....X^^X.X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X.X^^X....% D:%....X^^X.X^^^XXXXXXXXXXXXXXXXXXXXXXX^^^X.X^^X....% D:%....X^^+&X^^XX@.+***************+.@XX^^X&+^^X....% D:%....X^^XXX^^+@.@X*9999988899999*X@.@+^^XXX^^X....% D:%....X^^+.X^^XX@.+***************+.@XX^^X.+^^X....% D:%....X^^X.X^^^XXXXXXXXXXXXXXXXXXXXXXX^^^X.X^^X....% D:%....X^^X.X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X.X^^X....% D:%....X^^X............XXXX+XXXX............X^^X....% D:%...XX++XX..........XX&.&.&.&XX..........XX++XX...% D:%..XX,,,,XXXXXXXXXXXX&.&.&.&.&XXXXXXXXXXXX,,,,XX..% D:%.XX,*99*,+********9XXXXX+XXXXX9********+,*99*,XX.% D:%.XX,*99*,XXXXXXXXXXX&.&.&.&.&XXXXXXXXXXX,*99*,XX.% D:%..XX,,,,XX.........XX&.&.&.&XX.........XX,,,,XX..% D:%...XXXXXX...........XXXX+XXXX...........XXXXXX...% D:%.................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:29:Greater vault (x-factor) X:8:25:25:26 D:%%%%%%%%%%%%%%%%%%%%%%%%%% D:%^^^^^^^^^^^^^^^^^^^^^^^^% D:%^##########++##########^% D:%^#XX,,,,,,,,,,,,,,,,XX#^% D:%^#,XX,,,,,,,,,,,,,,XX,#^% D:%^#,,XX,,,,,,,,,,,,XX,,#^% D:%^#,,,XX,,,,,,,,,,XX,,,#^% D:%^#,,,,XX,,,,,,,,XX,,,,#^% D:%^#,,,,,XX,,,,,,XX,,,,,#^% D:%^#,,,,,,XX,,,,XX,,,,,,#^% D:%^#,,,,,,,X+XX+X,,,,,,,#^% D:%^+,,,,,,,,X99X,,,,,,,,+^% D:%^+,,,,,,,,X99X,,,,,,,,+^% D:%^+,,,,,,,,X99X,,,,,,,,+^% D:%^#,,,,,,,X+XX+X,,,,,,,#^% D:%^#,,,,,,XX,,,,XX,,,,,,#^% D:%^#,,,,,XX,,,,,,XX,,,,,#^% D:%^#,,,,XX,,,,,,,,XX,,,,#^% D:%^#,,,XX,,,,,,,,,,XX,,,#^% D:%^#,,XX,,,,,,,,,,,,XX,,#^% D:%^#,XX,,,,,,,,,,,,,,XX,#^% D:%^#XX,,,,,,,,,,,,,,,,XX#^% D:%^##########++##########^% D:%^^^^^^^^^^^^^^^^^^^^^^^^% D:%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:30:Greater vault (university) X:8:30:29:38 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%....................................% D:%.##################################.% D:%.#*+..&#..,#,#..,#,#..,#,#.,#&+*#*#.% D:%.#####.#.###.#.###.#.###.#.##.###+#.% D:%.#***#.#.#*..#.#*..#.#*..#.#..#..&#.% D:%.#+###+#+###+#+###+#+###+#+##+###+#.% D:%.#...+&......................&+...#.% D:%.#.###.##########++##########.###.#.% D:%.#,#.+.#,,,,,,,,,,,,,,,,,,,,#.#*#,#.% D:%.###.#.#,.,..,..,@.,..,..,.,#.#.###.% D:%.#,..#.#,..,..,..,..,..,..,,#.+..,#.% D:%.#####.#,9&,,,9,,&,,&,,.,,,,#.#####.% D:%.+^^^+.+,,,&,,,&,,,9,,&,,&,,+.+^^^+.% D:%.+^^^+.+,.,...,...,...,...,,+.+^^^+.% D:%.###+#.#,,,,&,,&,,,9,&,,,&,,#.#####.% D:%.#*#&#.#,,,&,,,9,&,,&,,,,,9,#.#..,#.% D:%.#+#*#.#,.,...,....,....,..,#.#.###.% D:%.#.###.#,,,&,,,,&,9,,&,,,&,,#.+.#,#.% D:%.#&..+.#,,,,,,,,.,,,,,,,,,,,#.###.#.% D:%.#####.##########++##########.+...#.% D:%.#&..+&......................&#+###.% D:%.#.###+#+###+#+###+#+###+#+##+#...#.% D:%.#.+*#.#.#*..#.#*..#.#*..#.#..###,#.% D:%.#####.#.###.#.###.#.###.#.##&#*###.% D:%.#*+..&#..,#,#..,#,#..,#,#.,#.+,*,#.% D:%.##################################.% D:%....................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:31:Greater vault (nethack castle (almost)) X:8:35:19:62 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%............................................................% D:%.XXXXXXX............................................XXXXXXX.% D:%.X,,9,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,9,,X.% D:%.X,,,,,+.......^....*....^ ..........^.......*...^..+,,,,,X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%......X,.,9,,,,X,,,,,,,,,+,.,.,.,.,.,X,.,.,.+^+,.,.,.X......% D:%......X,.9*9,.,X,,,,,,,,,X,.,..,..,.,X,.,.,.X^X,.,.,.X......% D:%....&.X.&*@*&,.XXXXXXXXXXX,.,.,9,.,.,XXXXXXXX+XXXXXXXX......% D:%......+9*@8@*9.+^^^^^^^^^+,.,,9@9,,.,+^^^^^^^^^^^^^^^+......% D:%....&.X.&*@*&,.XXXXXXXXXXX,.,.,9,.,.,XXXXXXXX+XXXXXXXX......% D:%......X,.9*9,.,X,,,,,,,,,X,.,.,,,.,.,X.,.,.,X^X.,.,.,X......% D:%......X,.,9,,,,X,,,,,,,,,+,.,.,.,.,.,X.,.,.,+^+.,.,.,X......% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X,,,,,+.......^....*....^...........^.......*...^..+,,,,,X.% D:%.X,,9,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,9,,X.% D:%.XXXXXXX............................................XXXXXXX.% D:%............................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:32:Greater vault (another nethack-style castle) X:8:30:18:52 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..................................................% D:%.XXXXX......................................XXXXX.% D:%.X9,,X......................................X,,9X.% D:%.XXX+XXXXXXXXXXXXXXXXXXX++XXXXXXXXXXXXXXXXXXX+XXX.% D:%...X^^^^^^^^^^^^^^^^^^^+..+^^^^^^^^^^^^^^^^^^^X...% D:%...X^XXXXXXXXXXXXXXXXXXX++XXXXXXXXXXXXXXXXXXX^X...% D:%...X^X,,,,,,,,,,,,,,,,,X..X,,,,,,,,,X,,@@@@,X^X...% D:%...X^X,,,,,,,,,,,,,,,,,+..+,,,,,,,,,+,,,,998X^X...% D:%...X^X,,,,,,,,,,,,,,,,,+..+,,,,,,,,,+,,,,998X^X...% D:%...X^X,,,,,,,,,,,,,,,,,X..X,,,,,,,,,X,,@@@@,X^X...% D:%...X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX^X...% D:%...X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X...% D:%.XXX+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+XXX.% D:%.X9,,X......................................X,,9X.% D:%.XXXXX......................................XXXXX.% D:%..................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:33:Lesser vault (nethack-style tower) X:7:5:15:19 D:%%%%%%%%%%%%%%%%%%% D:%.................% D:%...###.###.###...% D:%...#&#.#&#.#&#...% D:%.###+###+###+###.% D:%.#.,.,.,.+.+,,,#.% D:%.###+#####.#####.% D:%...+*&*&*#.+,#...% D:%.###+#####.#####.% D:%.#.,.,.,.+.+,,,#.% D:%.###+###+###+###.% D:%...#&#.#&#.#&#...% D:%...###.###.###...% D:%.................% D:%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:34:Lesser vault (nethack, rooms) X:7:5:14:27 D:%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.........................% D:%.#+#####################.% D:%.#.^.^.^.+^+^...^#,,,,,#.% D:%.#^###+###^#####.+,,,,,#.% D:%.#.#,,,,,#.#,,,#^#,,,,,#.% D:%.#^#,,,,,#^+,,,#+#######.% D:%.#.#######.#,,,#.......#.% D:%.#^#,,,,,#^#,,,#..&.&..#.% D:%.#.#,,,,,#.#####.......#.% D:%.#^+,,,,,#^.^.^+..&.&.^+.% D:%.#######################.% D:%.........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:35:Lesser vault (nethack city) X:7:9:17:33 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...............................% D:%.############.....############.% D:%.#,,,#,,,#,,#.....#**#,,,#***#.% D:%.#,,,#,,,#,,#.....+**#,,,#***#.% D:%.#,,,#,,,#,,#.....#**#,,,#&&&#.% D:%.#^,,#,,,#+##.....####+###+###.% D:%.#+####+##.....................% D:%...............................% D:%.#+###+###....###+##...........% D:%.#^,,#^,,####.#,,,,#.######+##.% D:%.#,,,#,,,#**#.#,&&,#.+^^#,,,,#.% D:%.#,,,#,,,#*^+.#,&@,#.#^^#,,,,#.% D:%.#,,,#,,,#**#.#,,,,#.#**#,,,,#.% D:%.############.######.#########.% D:%...............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:36:Greater vault (nethack, large city) X:8:25:21:54 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%....................................................% D:%.##################################################.% D:%.#................................................#.% D:%.#.####################.########..#####.#########.#.% D:%.#.#^,,,,^#^****^#^,,^#.#^,,,,^#..#^^^#.#^,,,,,^#.#.% D:%.#.#^,,,,^#^****^#^,,^#.#^,,,,^#..+^**#.#^,,,,,^#.#.% D:%.#.#^,,,,^#^,,,,^#^^^^#.#^^^^^^#..#^^^#.#^,,,,,^#.#.% D:%.#.#^^^^^^#^^^^^^###+##.###+####..#####.#^^^^^^^#.#.% D:%.#.#+#########+###......................######+##.#.% D:%.#................................................#.% D:%.#.###+######+####......###+########..............#.% D:%.#.#^^^^^^#^^^^^^######.#^^^^^^^^^^#...###+#####..#.% D:%.#.#^,,,,^#^,,,,^#^^^^+.#^,,,,,,,,^#...#^^^^^^^#..#.% D:%.#.#^****^#^,,,,^#,,,^#.#^@999999@^#...#^,,,,,^#..#.% D:%.#.#^****^#^,,,,^#,,,^#.#^^^^^^^^^^#...#^,,,,,^#..#.% D:%.+.####################.############...#########..#.% D:%.#................................................#.% D:%.##################################################.% D:%....................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:37:Lesser vault (nethack, tiny castle) X:7:5:14:34 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%................................% D:%.#####....................#####.% D:%.#*,&#....................#&,*#.% D:%.###+######################+###.% D:%...#,,,,,,,,,,,,,#,,,,,,,,,,#...% D:%...+,,,,,,,,,,,,,+,,,,,,,,,,+...% D:%...+,,,,,,,,,,,,,+,,,,,,,,,,+...% D:%...#,,,,,,,,,,,,,#,,,,,,,,,,#...% D:%.###+######################+###.% D:%.#*,&#....................#&,*#.% D:%.#####....................#####.% D:%................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:38:Greater vault (nethack mirror) X:8:25:22:41 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.......................................% D:%...............XXXX+XXXX...............% D:%...............X**X^X**X...............% D:%....XXXXXXXXXXXX**X^X**XXXXXXXXXXXX....% D:%....X^+99999999X+XX^XX+X99999999+^X....% D:%....X^X9*******X,,X^X,,X*******9X^X....% D:%....X^X9*******X,,X^X,,X*******9X^X....% D:%.XXXX+XXXXXXXXXXX+X+X+XXXXXXXXXXX+XXXX.% D:%.X**X,,,,,,,,,,X,,,,,,,X,,,,,,,,,,X**X.% D:%.X*@+,,,,,,,,,,+,,,,8,,X,,,,,,,,,,+@*X.% D:%.X*@+,,,,,,,,,,X,,,8,,,+,,,,,,,,,,+@*X.% D:%.X**X,,,,,,,,,,X,,,,,,,X,,,,,,,,,,X**X.% D:%.XXXX+XXXXXXXXXXX+X+X+XXXXXXXXXXX+XXXX.% D:%....X^X9*******X,,X^X,,X*******9X^X....% D:%....X^X9*******X,,X^X,,X*******9X^X....% D:%....X^+99999999X+XX^XX+X99999999+^X....% D:%....XXXXXXXXXXXX**X^X**XXXXXXXXXXXX....% D:%...............X**X^X**X...............% D:%...............XXXX+XXXX...............% D:%.......................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:39:Greater vault (nethack tomb) X:8:25:13:57 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.......................................................% D:%..........XXXXX.XXXXX.XXXXX.XXXXX.XXXXX................% D:%..........X,,,X.X,,,X.X,,,X.X,,,X.X,,,X................% D:%.XXXXXXXXXX,&,XXX,&,XXX,&,XXX,&,XXX,&,XXXXXXXXXXXXXXXX.% D:%.X&^&X....^...^...^...^...^...^...^..^..X,,,,,,,,X,,9X.% D:%.+^^^+..^...^...^...^...^...^...^...^.@.+,,,,,,,,+,98X.% D:%.X&^&X....^...^...^...^...^...^...^.....X,,,,,,,,X,,9X.% D:%.XXXXXXXXXX,&,XXX,.,XXX,.,XXX,&,XXX,&,XXXXXXXXXXXXXXXX.% D:%..........X,,,X.X,,,X.X,,,X.X,,,X.X,,,X................% D:%..........XXXXX.XXXXX.XXXXX.XXXXX.XXXXX................% D:%.......................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:40:Greater vault (nethack hell level #1) X:8:30:17:55 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.....................................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.XX*XXXXXXXXXX**XXXXXXXXXXXX***********************X.% D:%.XX*X,,,,X***X+XX,,,,,,X***XXXXXXXXXXXX^^^^^^^^^^^^X.% D:%.XX,X,,,,+***X@^+,,,,,,X***+,,,,,,,,,,X^^^^^^^^^^^^X.% D:%.XX&X,,,,XXXXXXXX^^^^^^^^^^X,,,XXXXXXXXXXXXXX^^^^^^X.% D:%.XX+X....XXXX.............^X,,,+,,,,,,,,,,,9XXXXX^^X.% D:%.+^^^^^^^^^^+^^^^^^^^^^^^^^XXXXX,,,,,,,,,,,9+888+@@X.% D:%.XX+X....XXXX.............^X,,,+,,,,,,,,,,,9XXXXX^^X.% D:%.XX&X,,,,XXXXXXXX^^^^^^^^^^X,,,XXXXXXXXXXXXXX^^^^^^X.% D:%.XX,X,,,,+***X@^+,,,,,,X***+,,,,,,,,,,X^^^^^^^^^^^^X.% D:%.XX*X,,,,X***X+XX,,,,,,X***XXXXXXXXXXXX^^^^^^^^^^^^X.% D:%.XX*XXXXXXXXXX**XXXXXXXXXXXX***********************X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.....................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:41:Greater vault (nethack hell level #2) X:8:30:15:54 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%....................................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...% D:%.+^XX,+^^^^^X^^^^^^^^^^^^^^^^^^^^^X,,,X,@^X*^*+9X...% D:%.X^XX,X^XXX,X@XXXXXXXXXXXXXXXXXXX^X,X,X,X^X*X*X9X...% D:%.X^XX,X^^^X,X^^^^^^^^^^^^^^^^^^@X^X,X,X,X^X*X*X9X...% D:%.X^XXXXXX^X,XXXXXXXXXXXXXXXXXXX^X^X,X,X,X^X*X*X+XXX.% D:%.X^^^^^^X^X,X,,,,,,,,,,,,,,,,,X^X^X,X,X,X^X*X*X888X.% D:%.X+XXXX^X^X,X,XXXXXXXXXXXXXXX,X^X^X,X,X,X^X*X*X+XXX.% D:%.X,XX,X^X^X,X,X,,,,,,,,,,,,,,,X^X^X,X,X,X^X*X*X9X...% D:%.X,XX,X^X&X,X,X,XXXXXXXXXXXXXXX^X^X,X,X,X^X*X*X9X...% D:%.X,XX,+^^^X,,,X^^^^^^^^^^^^^^^^^X^@,X,,,X^@*X*+9X...% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...% D:%....................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:42:Greater vault (nethack hell level #3) X:8:30:17:55 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.....................................................% D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.........% D:%...X,,,,,,,,,,,,,,,,,,,,,XXX^^^^^^^^^^^^^^^X.........% D:%...X,^^^^^^^^^^XXXX,,,,,,+&&^XXXXXXXXXXXX^^+.........% D:%...X,^XXXXXX******X,,XXXXXXXXX^^^^******XXXX.........% D:%...X,^X****X@*XXXXXXXX,,,,,,,,,,,XXXXXXXXXXXXXX......% D:%.XXXX+X,,,,XXXX,,,,,,,,,,,,,,,,,^+************XXXXX..% D:%.X@,,,,,XX,,,,+,,XXXXXXXXXXXXXXXXX****9999****+888X..% D:%.XXXX+X,,,,XXXX,,,,,,,,,,,,,,,,,^+************XXXXX..% D:%...X,^X****X@*XXXXXXXX,,,,,,,,,,,XXXXXXXXXXXXXX......% D:%...X,^XXXXXX******X,,XXXXXXXXX^^^^******XXXX.........% D:%...X,^^^^^^^^^^XXXX,,,,,+&&^^XXXXXXXXXXXX^^+.........% D:%...X,,,,,,,,,,,,,,,,,,,,XXX^^^^^^^^^^^^^^^^X.........% D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.........% D:%.....................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:43:Lesser vault (easter egg) X:7:5:14:19 D:%%%%%%%%%%%%%%%%%%% D:%.................% D:%.###############.% D:%.#,^,^,^,^,^,^,#.% D:%.#+###########^#.% D:%.#,^,^,^,^,^,#,#.% D:%.###########,#^#.% D:%.#,^,^,^,^,#^#,#.% D:%.#+#######,#,#^#.% D:%.#,,,,,,,#^#^#,#.% D:%.#,,,,,,,#,+,#^+.% D:%.###############.% D:%.................% D:%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:44:Greater vault (nethack samurai castle) X:8:35:20:59 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.........................................................% D:%.XXXXX.............................................XXXXX.% D:%.X***X.............................................X***X.% D:%.X***XXX.....XX+XXXXXXXXXXXXXXXXXXXXXXXXX+XX.....XXX***X.% D:%.XXX@,,X.....X^&^X,,,X***X,,,X***X^^^+8X^&^X.....X,,@XXX.% D:%...X,,,XXXXXXX^^^X,,,X^^^X,,,X^^^X@@^X8X^^^XXXXXXX,,,X...% D:%...XXX^^^^^^^^^XXXXX+XXX+X+XXXXX+X+XXXXXXX^^^^^^^^^XXX...% D:%.....X^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^X.....% D:%.....X^^+,,,,,,,,+,,,,,,,,,99,,,,,,,,,,+,,,,,,,,+^^X.....% D:%.....X^^+,,,,,,,,+,,,,,,,,,99,,,,,,,,,,+,,,,,,,,+^^X.....% D:%.....X^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^X.....% D:%...XXX^^^^^^^^^XXXXXXX+X+XXXXX+X+XXX+XXXXX^^^^^^^^^XXX...% D:%...X,,,XXXXXXX^^^X8X^@@X^^^X^^^X,,,X,,,X^^^XXXXXXX,,,X...% D:%.XXX@,,X.....X^&^X8+^^^X***X***X,,,X,,,X^&^X.....X,,@XXX.% D:%.X***XXX.....XX+XXXXXXXXXXXXXXXXXXXXXXXXX+XX.....XXX***X.% D:%.X***X.............................................X***X.% D:%.XXXXX.............................................XXXXX.% D:%.........................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:45:Greater vault (nethack samurai castle #2) X:8:35:20:61 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...........................................................% D:%.XXXXXXXXXXXXX...............................XXXXXXXXXXXXX.% D:%.X,,^^^^^^^,,X...............................X,,^^^^^^^,,X.% D:%.X,,^XXXXX^,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,^XXXXX^,,X.% D:%.X,,^+999X^,,X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X,,^X999+^,,X.% D:%.X,,^XXXXX^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^XXXXX^,,X.% D:%.X^^^^^^^^^^^XXXXXX+XXXXXXXXXXXXXXXXXXX+XXXXXX^^^^^^^^^^^X.% D:%.XXXX^^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^^XXXX.% D:%....+^^^X********+,,,,,,,,,,,,,,,,,,,,,,,+********X^^^+....% D:%....+^^^X********+,,,,,,,,,,,,,,,,,,,,,,,+********X^^^+....% D:%.XXXX^^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^^XXXX.% D:%.X^^^^^^^^^^^XXXXXX+XXXXXXXXXXXXXXXXXXX+XXXXXX^^^^^^^^^^^X.% D:%.X,,^XXXXX^,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^XXXXX^,,X.% D:%.X,,^+999X^,,X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X,,^X999+^,,X.% D:%.X,,^XXXXX^,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,^XXXXX^,,X.% D:%.X,,^^^^^^^,,X...............................X,,^^^^^^^,,X.% D:%.XXXXXXXXXXXXX...............................XXXXXXXXXXXXX.% D:%...........................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:46:Greater vault (nethack spiral) X:8:30:19:38 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.........XXXXXXXXX+XXXXXXXXX........% D:%......XXXX^^^^^^^^^^^^^^^^^XXXX.....% D:%...XXXX^^^^^XXXXXXXXXXXXX^^^^^XXXX..% D:%..XX^^^^^XXXX^^^^^^^^^^^XXXX^^^^^XX.% D:%.XX^^^^XXX^^^^XXXXXXXXX^^^^XXX^^^^XX% D:%.X^^^^XX^^^^XXX,^^^^^,XXX^^^^XX^^^^X% D:%.X,,,XX,,,XXX,,^XX+XX^,,XXX,,,XX,,,X% D:%.X,,,X,,,,X,,,^XX***XX^,,,X,,,,X,,,X% D:%.X,,,X,99,X,,,^+@888@+^,,,X,,,,+,,,X% D:%.X,,,X,,,,X,,,^XX***XX^,,,X,,,,X,,,X% D:%.X,,,XX,,,XXX,,^XX+XX^,,XXX,,,XX,,,X% D:%.X,,,,XX^^^^XXX,^^^^^,XXX^^^^XX,,,,X% D:%.XX,,,,XXX^^^^XXXX+XXXX^^^^XXX,,,,XX% D:%..XX,,,**XXXX^^^^^^^^^^^XXXX,,,,,XX.% D:%...XXXX**,,,XXXXXXXXXXXXX,,,,,XXXX..% D:%......XXXX,,,,,,,,,,,,,,,,,XXXX.....% D:%.........XXXXXXXXXXXXXXXXXXX........% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:47:Greater vault (nethack building) X:8:30:16:41 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%...X^^^^^^^^^^^^^^^^X**@**X**@**X**@**X% D:%...X^^XXXXXXXXXXXX^^XXX+XXXXX+XXXXX+XXX% D:%...X^^X,,,,,,,,,,X^^X^^^^^^^^^^^^^^^^^X% D:%...X^^X,,,,,,,,,,X^^X+XXX+XXX+XXXXX+XXX% D:%.XXX^^X,,,,,,,,,,X^^^^^^X,,,X,,,X,,,,,X% D:%.+^&^^X,,,,,,,,,,+^^^^^^X,,,X,,,X,989,X% D:%.+^&^^X,,,,,,,,,,+^^^^^^X,,,X,,,X,989,X% D:%.XXX^^X,,,,,,,,,,X^^^^^^X,,,X,,,X,,,,,X% D:%...X^^X,,,,,,,,,,X^^X+XXXXX+XXX+XXX+XXX% D:%...X^^X,,,,,,,,,,X^^X^^^^^^^^^^^^^^^^^X% D:%...X^^XXXXXXXXXXXX^^XXX+XXXXX+XXXXX+XXX% D:%...X^^^^^^^^^^^^^^^^X**@**X**@**X**@**X% D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:48:Lesser vault (nethack, spiral rooms) X:7:5:13:32 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..............................% D:%....#######################...% D:%..###^+,,,,,,#^^^^^^^^^^+*###.% D:%..#,#^#,,,,,,#+############,#.% D:%..#^#^#,,,,,,#,,,+,,,,,,#^#^#.% D:%..+^+^#,,,,,,#,,,#,,,,,,#^+^+.% D:%..#^#^#,,,,,,+,,,#,,,,,,#^#^#.% D:%..#,############+#,,,,,,#^#,#.% D:%..###*+^^^^^^^^^^#,,,,,,+^###.% D:%....#######################...% D:%..............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:49:Greater vault (nethack building) X:8:30:17:54 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%....................................................% D:%........XXXXXXXXXXXXX.................XXXXXXXXXXXXX.% D:%........X***********X.................X***********X.% D:%.XXXXXXXX***********XXXXXXXXXXXXXXXXXXX***********X.% D:%.+^^^^^^X,,,,,,,,,,,X**X**X**X**X**X**X*,,,,,,,,,*X.% D:%.X^,,,,^X^,,,,,,,,,,X,@X,@X,@X,@X,@X,@X,,,,,,,,,,,X.% D:%.X^^^^^^+^^^^^^^^^^^XX+XX+XX+XX+XX+XX+X,,,,,,,,,,,X.% D:%.XXXXXXXX^^^^^^^^^^^+^^^^^^^^^^^^^^^^^+,,,,,89,,,,X.% D:%.X^^^^^^+^^^^^^^^^^^XX+XX+XX+XX+XX+XX+X,,,,,98,,,,X.% D:%.X^,,,,^X^,,,,,,,,,,X,@X,@X,@X,@X,@X,@X,,,,,,,,,,,X.% D:%.+^^^^^^X,,,,,,,,,,,X**X**X**X**X**X**X*,,,,,,,,,*X.% D:%.XXXXXXXX***********XXXXXXXXXXXXXXXXXXX***********X.% D:%........X***********X.................X***********X.% D:%........XXXXXXXXXXXXX.................XXXXXXXXXXXXX.% D:%....................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:50:Lesser vault (nethack, head) X:7:7:17:29 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%........###########........% D:%...#####################...% D:%...######,,,,,,,,,######...% D:%..#+^^^^#,,,,,,,,,#^^^^+#..% D:%..##^^^^#,,,,,,,,,+^,,^##..% D:%.#####+##,,,,,,,,,#^^^^###.% D:%.##^^^^^^,,,,,,,,,########.% D:%.####+###++#########*@+*##.% D:%.##^^^^^#^^#,,,#^@^####*##.% D:%.##^^,,^#^^#,###^#^#****##.% D:%..##^,,^#^^#,,,.^#^#@####..% D:%..#+^^^^#^^#####^#^^^^^##..% D:%...######^^^^^^^^#######...% D:%...#####################...% D:%........###########........% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:51:Lesser vault (maze of rooms) X:7:10:16:32 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..............................% D:%.#+##########################.% D:%.#^#,,,+^#*#^+,,+*#^+^#*#**@#.% D:%.#^+,,,#^+^+^######^#^#*###+#.% D:%.#########+###,,,,#^#,+@#,,,#.% D:%.#,,,,,,+^^+,,,,,,+^#######+#.% D:%.###+#######,,,,,#####,,+^^^#.% D:%.#,,,+**#**+,,,,,#^^^+,,#^,^#.% D:%.#############+###^,^#+##^^^#.% D:%.#***#^+,,#,,,,,,+^^^#^######.% D:%.#+###^#,,+,,,,,,#####^+,^^^#.% D:%.#,,,+^#,,########,,,+^#,^^^#.% D:%.##########################+#.% D:%..............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:52:Lesser vault (tetris tiles) X:7:5:20:13 D:%%%%%%%%%%%%% D:%..#######..% D:%..#**#**#..% D:%..#,###,#..% D:%..#,#^#,#..% D:%.##+#^#+##.% D:%.#&,#^#,&#.% D:%.#,,+^+,,#.% D:%.####+####.% D:%.###,^,###.% D:%.#*##^##*#.% D:%.#,,#+#,,#.% D:%.##,#^#,##.% D:%.##+#^#+##.% D:%.#,,#^#,,#.% D:%.##,+^+,##.% D:%..#,#+#,#..% D:%..###.###..% D:%...........% D:%%%%%%%%%%%%% # Topi Ylinen # N:53:Lesser vault (hospital ward) X:7:5:14:20 D:%%%%%%%%%%%%%%%%%%%% D:%..................% D:%.################.% D:%.#,,#,,#,,#,,#,,#.% D:%.#,,#,,#,,#,,#,,#.% D:%.##+##+##+##+##+#.% D:%.+..............+.% D:%.+..............+.% D:%.#+##+##+##+##+##.% D:%.#,,#,,#,,#,,#,,#.% D:%.#,,#,,#,,#,,#,,#.% D:%.################.% D:%..................% D:%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:54:Lesser vault (lesser crypt) X:7:5:13:26 D:%%%%%%%%%%%%%%%%%%%%%%%%%% D:%................###.....% D:%................#,#.....% D:%......#######.###+###...% D:%..###.#,#,#,#.#^^^^^#...% D:%.##&###+#+#+###^###^###.% D:%.+^^^+^^^^^^^+^^#9#^+,#.% D:%.##&###+#+#+###^###^###.% D:%..###.#,#,#,#.#^^^^^#...% D:%......#######.###+###...% D:%................#,#.....% D:%................###.....% D:%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:55:Lesser vault (arena) X:7:5:15:17 D:%%%%%%%%%%%%%%%%% D:%...............% D:%.+###########+.% D:%.#...........#.% D:%.#.####^####.#.% D:%.#.#,,,,,,,#.#.% D:%.#.#,,,,,,,#.#.% D:%.#.^,,,,,,,^.#.% D:%.#.#,,,,,,,#.#.% D:%.#.#,,,,,,,#.#.% D:%.#.####^####.#.% D:%.#...........#.% D:%.+###########+.% D:%...............% D:%%%%%%%%%%%%%%%%% # Topi Ylinen # N:56:Lesser vault (monster wc) X:7:5:12:12 D:%%%%%%%%%%%% D:%..#######.% D:%..#.&&&&#.% D:%..#^#####.% D:%.##...+,#.% D:%.#*...###.% D:%.#*...+,#.% D:%.#*...###.% D:%.##...+,#.% D:%..#+#####.% D:%..........% D:%%%%%%%%%%%% # Topi Ylinen # N:57:Lesser vault ('not' 'and') X:7:5:11:15 D:%%%%%%%%%%%%%%% D:%.............% D:%..#########..% D:%.#+,,,#,,,+#.% D:%.#,#,#+#,#,#.% D:%.#,,#+*+#,,#.% D:%.#,#,#+#,#,#.% D:%.#+,,,#,,,+#.% D:%..#########..% D:%.............% D:%%%%%%%%%%%%%%% # Topi Ylinen # N:58:Lesser vault (brain's lair) X:7:5:18:17 D:%%%%%%%%%%%%%%%%% D:%...............% D:%.#############.% D:%.#...........#.% D:%.#.####^####.#.% D:% #.#...&...#.#.% D:%.#.#.#####.#.#.% D:%.#.#.#,,,#.#.#.% D:%.#.#.#,,,#.#.#.% D:%.#.#.#,,,#.#.#.% D:%.#.#.##+##.#.#.% D:%.^.#..#^#..#.^.% D:%.####.#^#.####.% D:%.#,,#.#+#.#,,#.% D:%.#,,+..&..+,,#.% D:%.#############.% D:%...............% D:%%%%%%%%%%%%%%%%% # Topi Ylinen # N:59:Lesser vault (yin-yang) X:7:5:17:16 D:%%%%%%%%%%%%%%%% D:%..............% D:%.#+##########.% D:%.#&#^^^^^^^^#.% D:%.#.#^######^#.% D:%.#&#^#****#^#.% D:%.#.#^#*,**#^#.% D:%.#&#^#****#^#.% D:%.#.#+####+#^#.% D:%.#&#,,,,#.#^#.% D:%.#.#,,*,#&#^#.% D:%.#&#,,,,#.#^#.% D:%.#.######&#^#.% D:%.#&.&.&.&.#^#.% D:%.##########+#.% D:%..............% D:%%%%%%%%%%%%%%%% # Topi Ylinen # N:60:Greater vault (der el bahri) X:8:35:28:45 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X***X,,X^^^^^^^^X9988899X^^^^^^^^+******X.% D:%.X***X,,+^..,...^X9999999X^...,..^XXXX***X.% D:%.X***XXXX^......^XXXX+XXXX^......^X**XXXXX.% D:%.X^^^X,,X^..,...^^^^^^^^^^^...,..^X******X.% D:%.X@@@X,,X^.......................^X,,,,,,X.% D:%.X^^^X,,X^..,..,..,..,..,..,..,..^X,,,,,,X.% D:%.X^^^X++X^.......................^X,,,,,,X.% D:%.X^^^X^^X^..,..,..,..,..,..,..,..^X,,,,,,X.% D:%.X^^^^^^+^.......................^X,,,,,,X.% D:%.X^^^^^^X^..,..,..,..,..,..,..,..^X,,,,,,X.% D:%.XXX+XXXX^.......................^X,,,,,,X.% D:%.X***X**X^..,..,..,..,..,..,..,..^X,,,,,,X.% D:%.XXXXX**X^.......................^X,,,,,,X.% D:%.X,,,,,,+^.......................^+,,,,,,X.% D:%.X,,,,,,+^^^^^^^^^^^^^^^^^^^^^^^^^+,,,,,,X.% D:%.XXXXXXXXXXXXXXXXXXX+++XXXXXXXXXXXXXXXXXXX.% D:%.X.............^^^^^^^^^^^^..............X.% D:%.X................^^^^^^^................X.% D:%.XXXXXXXXXXXXXXXXXXX^^^XXXXXXXXXXXXXXXXXXX.% D:%.X.................X.^.X.................X.% D:%.X.X.X.X.X.X.X.X.X.X^^^X.X.X.X.X.X.X.X.X.X.% D:%.X.................X.^.X.................X.% D:%.X.X.X.X.X.X.X.X.X.X^^^X.X.X.X.X.X.X.X.X.X.% D:%.X.................X.^.X.................X.% D:%...........................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:61:Lesser vault (der el bahri sanctuary) X:7:5:12:22 D:%%%%%%%%%%%%%%%%%%%%%% D:%....................% D:%..........###.......% D:%..........#,#.......% D:%..###.###.#,#.......% D:%.##,###,###,#######.% D:%.+^^^^^^^+^^^+,,,,#.% D:%.+^^^^^^^+^^^+,,,,#.% D:%.######+###########.% D:%......###...........% D:%....................% D:%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:62:Greater vault (hypostyle of ramses III) X:8:40:38:34 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X888888+*******X*******+88888X.% D:%.XXXXXXXXX+XXXXXXXXXXX+XXXXXXXX.% D:%.X99+***+^^^+^^^^^^^+^^^+**+99X.% D:%.XXXXXXXXXXXXXX+++XXXXXXXXXXXXX.% D:%.X**X,,X,,+^X^^^^^^^X^X,,,+***X.% D:%.X**X,,X,,X^X^X...X^X^X,,,XXXXX.% D:%.X+XX,,X,,X^X^..@..^X^X,,,X***X.% D:%.X,,X,,XXXX^X^X...X^X^X^^^X***X.% D:%.X,,X,,X,*X^X^^^^^^^X^XX+XXXX+X.% D:%.X+XXX+X+XX^XXX+++XXX^X^^X,,,,X.% D:%.X^^^^^X^^+^^^^^^^^^^^+^^X,**,X.% D:%.X+XXXXXXXX^X.X...X.X^XXXX,**,X.% D:%.X^^^^^X,,X^.........^X**X,**,X.% D:%.X^X,X^X,,X^X.X.@.X.X^X++X,,,,X.% D:%.X^^^^^X,,X^.........^+^^X^^^^X.% D:%.XXX+XXX++X^XXX...XXX^XXXX+XXXX.% D:%.X^^^^^^^^+^....&....^+****+^^X.% D:%.XXXXXXXXXX^X.X...X.X^XXXXXXX^X.% D:%.X^^^^^+^^X^.........^X^^^^^^^X.% D:%.X^#X#^X^^X^X.X.&.X.X^X^,*XXXXX.% D:%.X^X9X^X^^X^.........^X^,*X***X.% D:%.X^#X#^X^^X^X.X...X.X^X^,*XXX+X.% D:%.X^^^^^X^^X^^^^^^^^^^^X^^^X,,,X.% D:%.XXXXXXX++XXXXX+++XXXXXX+XX,,,X.% D:%.X*****+^^^^^^^^^^^^^^^^^+,,,,X.% D:%.XXXXXXX^.X.X.X...X.X.X.^XXXXXX.% D:%.X,,X,,X^...............^+,,,,X.% D:%.X,,X,,X^.X.X.X...X.X.X.^XXXXXX.% D:%.X++X++X^.....&.&.&.....^+,,,,X.% D:%.X^^^^^+^.X.X.X...X.X.X.^XXXXXX.% D:%.X++X++X^...............^+,,,,X.% D:%.X,,X,,X^.X.X.X...X.X.X.^XXXXXX.% D:%.X,,X,,X^^^^^^^^^^^^^^^^^+,,,,X.% D:%.XXXXXXXXXXXXXX+++XXXXXXXXXXXXX.% D:%................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:63:Lesser vault (amada temple) X:7:10:15:30 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%............................% D:%.######################.....% D:%.#**#,,,,+^^#.....&...#.....% D:%.#**#,,,,#^^#...&.....#.....% D:%.#+#######^^#..#.#.#.##.....% D:%.#,,,,,,,#^^#^^^^^^^^^#####.% D:%.#,,,,,,,+^^+^^^^^^^^^+^^^+.% D:%.#,,,,,,,#^^#^^^^^^^^^#####.% D:%.#+#######^^#..#.#.#.##.....% D:%.#**#,,,,#^^#...&..&..#.....% D:%.#**#,,,,+^^#.........#.....% D:%.######################.....% D:%............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:64:Lesser vault (amenhotep I) X:7:10:18:34 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%................................% D:%...............######...........% D:%...............#.&&.#...........% D:%.###############.##.#...........% D:%.#**#,,,+^^^^^^^^##.###.........% D:%.#**#,,,#^^^^^^^^^^...#########.% D:%.##+#####^^#######^&#.#.......#.% D:%.#,,,,,,+^^+,*,*,+^^^^^^^^^^^^+.% D:%.#,,,,,,+^^+*,*,*+^^^^^^^^^^^^+.% D:%.##+#####^^#######^&#.#.......#.% D:%.#**#,,,#^^^^^^^^^^...#########.% D:%.#**#,,,+^^^^^^^^##.###.........% D:%.###############.##.#...........% D:%...............#.&&.#...........% D:%...............######...........% D:%................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:65:Lesser vault (hathor chapel) X:7:10:21:19 D:%%%%%%%%%%%%%%%%%%% D:%.................% D:%......##+###.....% D:%.######^^^######.% D:%.#,,#^#...#^#,,#.% D:%.#,,+^..*..^+,,#.% D:%.#,,#^#...#^#,,#.% D:%.####^.....^####.% D:%....#^^^^^^^#....% D:%....#.##+##.#....% D:%....#&#^^^#&#....% D:%....###,^,###....% D:%....#,,,,,,,#....% D:%....####+####....% D:%....#,,,,,,,#....% D:%....####+####....% D:%....#,,,,,,,#....% D:%....####+####....% D:%.....#*****#.....% D:%.....#######.....% D:%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:66:Lesser vault (osiris halls) X:7:10:17:34 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%................................% D:%.###########################....% D:%.#*,+^^^^^#^^^^^^^^^^^^+,,*#....% D:%.####^#.#^#^#.#.#.#.#.^#####....% D:%.#*,+^.&.^+^..&...&...^+,,*#....% D:%.####^#.#^#^#.#.#.#.#.^#####....% D:%.#*,+^^^^^#^^^^^^^^^^^^+,,*#....% D:%.####################++########.% D:%.#,,#,,#,,#,,#,,#,,#....#,,#,,#.% D:%.#,,#,,#,,#,,#,,#,,#....#,,#,,#.% D:%.#,,#,,#,,#,,#,,#,,#.&..#,,#,,#.% D:%.#++#++#++#++#++#++#....#++#++#.% D:%.#^^^^^^^^^^^^^^^^^^^^^^^^^^^^+.% D:%.##############################.% D:%................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:67:Lesser vault (temple of sety) X:7:10:22:25 D:%%%%%%%%%%%%%%%%%%%%%%%%% D:%.##########+##########.% D:%.#,,,,,,,#^^^#,,,,,,,#.% D:%.#,,,,,,,+^^^+,,,,,,,#.% D:%.#########^^^#########.% D:%.#,,,,,,,#^^^#,,,,,,,#.% D:%.#,,,,,,,+^^^+,,,,,,,#.% D:%.#########+###########.% D:%.#^^^+^^^+^^^^^#,.,.,#.% D:%.#.#^#^#.#^#.#.#.#.#.#.% D:%.#.&^#^.&#^.&..#,.,.,#.% D:%.#.#^+^#.#^#.#.#.#.#.#.% D:%.#^^^#^^^#^^^^^#,.,.,#.% D:%.#+#+###+###+###.#.#.#.% D:%.#,#^^^^^^^^^^^#,.,.,#.% D:%.#,#.#.#.#.#.#.#+###^#.% D:%.#,#...........#,**#^#.% D:%.#*#.#.#.#.#.#.#####+#.% D:%.#*#^^^^^^^^^^^+&^^^&#.% D:%.#######+#############.% D:%.......................% D:%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:68:Lesser vault (temple at abydos) X:7:10:21:23 D:%%%%%%%%%%%%%%%%%%%%%%% D:%^^^^^^^^^^^^^^^^^^^^^% D:%^#+#+#####+#####+#+#^% D:%^#,#,#^^^^^^^^^#,#,#^% D:%^#,#,#^#.#.#.#^#,#,#^% D:%^#,#,#^...&...^#,#,#^% D:%^#####^#.#.#.#^#####^% D:%^#*,,+^^^^^^^^^+,,*#^% D:%^#########+#########^% D:%^#*,,+^^^^^^^^^+,,*#^% D:%^#####^#.#.#.#^#####^% D:%^#*,,+^...&...^+,,*#^% D:%^#####^#.#.#.#^#####^% D:%^#*,,+^^^^^^^^^+,,*#^% D:%^##+###+##+##+###+##^% D:%^#,,,,#,#^^^#,#,,,,#^% D:%^#,##,#,#,#,#,#,##,#^% D:%^#,,,,#,#***#,#,,,,#^% D:%^###################^% D:%^^^^^^^^^^^^^^^^^^^^^% D:%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:69:Greater vault (spiral castle) X:8:35:31:25 D:%%%%%%%%%%%%%%%%%%%%%%%%% D:%.......................% D:%.XXXXX...........XXXXX.% D:%.X,,,X...........X,,,X.% D:%.X,,,X...........X,,,X.% D:%.XXX+XXXXXXXXXXXXX+XXX.% D:%...X^^^^^^^^^^^^^^^X...% D:%...X^XXXXXXXXXXXXX^X...% D:%...X^X^^^^^^^^^^^+^X...% D:%...X^X^XXXXXXXXXXX^X...% D:%...X^X^X&..&....&X^X...% D:%...X^X^X.XXXXXXX.X^X...% D:%...X^X^X.X^^^^^X.X^X...% D:%...X^X^X&X+XXX^X.X^X...% D:%...X^X^X.X*9*X^X&X^X...% D:%...+^X^X,X989X^X.X^+...% D:%...X^X^X.X*9*X^X.X^X...% D:%...X^X^X.XXX+X^X,X^X...% D:%...X^X^X..&..X^X.X^X...% D:%...X^X.XXXXXXX^X.X^X...% D:%...X^X^^^^^^^^^X&X^X...% D:%...X^XXXXXXXXXXX.X^X...% D:%...X^+...&..&...,X^X...% D:%...X^XXXXXXXXXXXXX^X...% D:%...X^^^^^^^^^^^^^^^X...% D:%.XXX+XXXXXXXXXXXXX+XXX.% D:%.X,,,X...........X,,,X.% D:%.X,,,X...........X,,,X.% D:%.XXXXX...........XXXXX.% D:%.......................% D:%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:70:Lesser vault (temple of dendereh) X:7:10:20:22 D:%%%%%%%%%%%%%%%%%%%%%% D:%....................% D:%.##################.% D:%.#***+^&#**#&^+***#.% D:%.#***#^^#,,#^^#***#.% D:%.#####++#++#++#####.% D:%.#,,,+^^^^^^^^+,,,#.% D:%.#####^######^#####.% D:%.#,,,+^#,,,,#^+,,,#.% D:%.#####^#,**,#^#####.% D:%.#,,,+^#,**,#^#***#.% D:%.#####^#,**,#^#^^^#.% D:%.#,,,+^#,**,#^##+##.% D:%.#####^#,,,,#^#,,,#.% D:%.#,,,#^##++##^#&,&#.% D:%.#,,,+^^^^^^^^##+##.% D:%.#,,,#^^^^^^^^^^^^#.% D:%.########++########.% D:%....................% D:%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:71:Greater vault (Karnak, part I) X:8:35:24:44 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X*****+^+^^^^+^^^X,,,X***X***X***X,****X.% D:%.XXXXXXXXX^XX^X,,,X,X,X,X,X,X,X*X*X,X*X*X.% D:%.X,X,X,X,X^..^X,,,X,,,X^^^X^^^X**@+^,,,,X.% D:%.X^^^^^^^+^@.^X,,,XX+XX+XXXXX+XXXXX+XXXXX.% D:%.X,X^^^X,X^XX^X***X^^^+^^^^^^^+,,*X^^^^^X.% D:%.XXXX+XXXX^^^^X,,,XXXXX^X.X.X^XXXXX^X^X^X.% D:%.X^^^^^^^XXXXXX,,,X,,,X^..@..^+,,*X^^^^&X.% D:%.X^X,X,X^X,,,,X,,,X,X,X^X.X.X^XXXXX^XXXXX.% D:%.X^^^^^^^X,,,,X^^^X,,,X^^^^^^^+,,*X^+,,*X.% D:%.XXXX+XXXXX++XXX+XXX+XXXXX+XXXXXXXX^XXXXX.% D:%.X*,+^^^^^^^^^^^^^^^^^^^^^^^^^+,,*X^+,,*X.% D:%.XXXX^X.X.X.X.X.X.X.X.X.X.X.X^XXXXX^XXXXX.% D:%.X*,+^.........&.....&.......^X,,*X^+,,*X.% D:%.XXXX^X.X.X.X.X.X.X.X.X.X.X.X^+,X*X^XXXXX.% D:%.X**X^.......................^X,,*X^+,,*X.% D:%.X,,+^X.X.X.X.X.X.X.X.X.X.X.X^XXXXX^XXXXX.% D:%.XXXX^.........&.....&.......^+^^^^^+,,*X.% D:%.X,,+^X.X.X.X.X.X.X.X.X.X.X.X^X,X,X^XXXXX.% D:%.X**X^^^^^^^^^^^^^^^^^^^^^^^^^X.*.X^+,,*X.% D:%.XXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXX.% D:%..........................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:72:Lesser vault (Karnak, part II) X:7:10:20:29 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...........................% D:%.###+######################% D:%.#,+^#,,#,,#^+^+^#*#,#^+,#.% D:%.###.#,,#,,#^#^#^#*#,#^###.% D:%.#,+^#,,#,,#^#^#^#*#+#^+,#.% D:%.###.#++#++#^#^#^#+#.#^###.% D:%.#,+^#^^^^^+^#^#^+,,,#^+,#.% D:%.###.#^^^^^+^#^#+#+###^###.% D:%.#,+^#+#+#+#^#^#,#^+,#^+,#.% D:%.###.#,#,#,#^#^#######^###.% D:%.#,+^#######+#,,+,,,,,^+,#.% D:%.###^^^^^^#,^#,,+,,,,#^###.% D:%.#,+^#,#*^##+####,,,,,^+,#.% D:%.###^,,,*^#^^^^^#,,,,#^###.% D:%.#,+^#,#*^#,,,,^+,,,,,^+,#.% D:%.###^^^^^^#^^^^^#,,,,,^###.% D:%...##########+##########...% D:%...........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:73:Greater vault (mortuary temple of sety) X:8:35:25:34 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X^+,,***X,,+^^^^^+,,X*X,,X*X9X.% D:%.X^XXXXXXX,,X^X*X^X,,X,X,,X*X9X.% D:%.X^+,,***XXXX^*@*^X,,X^X,,X@X9X.% D:%.X^XXXXXXX,,X^X*X^XXXX^X,,X^X9X.% D:%.X^+,,***X,,+^^^^^+..X^X,,X^X^X.% D:%.X^XXXXXXXXXXXX+XXXXXX+X++X+X+X.% D:%.X^X*X*X*X**X,+.+,X**X^^^^+^^^X.% D:%.X+X,X,X,X,,XXX.XXX,,X^^^^+^^^X.% D:%.X^X^X^X^X,,X,X.X,X,,X^^XXXXXXX.% D:%.X^X+X+X+X^^X,X.X,X^^X^^X,,,,,X.% D:%.X^+^^^^^X++X+X+X+X++X^^X,X,X,X.% D:%.X^XXXXXXX^^^^^^^^^^^X^^X,,,,,X.% D:%.X^X*X*X*XXXX^...^XXXX^^X,X,X,X.% D:%.X^X,X,X,X,,+^X*X^+,,X^^X,,,,,X.% D:%.X^X^X^X^XXXX^.&.^XXXX^^X,X,X,X.% D:%.X^X+X+X+X,,+^X*X^+,,X^^X,,,,,X.% D:%.X^X^^^^^XXXX^...^XXXX^^X,X,X,X.% D:%.X^X*X,X*X,,X^X*X^X,,X^^X,,,,,X.% D:%.X^+^^^^^+^^+^^^^^+^^+^^X,,,,,X.% D:%.XXXXXXXXXXXXXX+XXXXXXXXXXX+XXX.% D:%................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:74:Lesser vault (edfu) X:7:5:15:15 D:%%%%%%%%%%%%%%% D:%.............% D:%.###########.% D:%.#,+^#,#^+,#.% D:%.###+#+#+###.% D:%.#,+^...^+,#.% D:%.###.###.###.% D:%.#,+.#*#.+,#.% D:%.###.#,#.###.% D:%.#,+.#^#.#,#.% D:%.###^...^#+#.% D:%.#,+,#+#,+,#.% D:%.#####^#####.% D:%.............% D:%%%%%%%%%%%%%%% # Topi Ylinen # N:75:Greater vault (kom ombo) X:8:40:36:30 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X****X,99,X9889X,99,X****X.% D:%.X****X,,,,X9999X,,,,X****X.% D:%.X+XXXX+XXXX+XXXX+XXXX+XXXX.% D:%.X@^^^^^^^^^^^^^^^^^^^^^^@X.% D:%.X^XXXXXXXXXXXXXXXXXXXXXX^X.% D:%.X^X**X,,X,,X,,X,,X,,X**X^X.% D:%.X^X,,X+XX+XX+XXX+XX+X,,X^X.% D:%.X^X,^+^^^^^^^^^^^^^^+^,X^X.% D:%.X^XXXX^XX+XX^^XX+XX^XXXX^X.% D:%.X^X,,X^X,,,X^^X,,,X^+^,X^X.% D:%.X^X,,X^X***X^^X***X^X,*X^X.% D:%.X^X,,X^X9X9X^^X9X9X^XXXX^X.% D:%.X^X,,X^X***X^^X***X^+^,X^X.% D:%.X^X++X^X,,,X^^X,,,X^X,*X^X.% D:%.X^X^^X^XX+XX^^XX+XX^XXXX^X.% D:%.X^X^^+^^^^^^^^^^^^^^X,*X^X.% D:%.X^X,,X^^^^^^^^^^^^^^+^,X^X.% D:%.X^XXXXXXX+XXXXXX+XXXXXXX^X.% D:%.X^X,X,X,,,,,XX,,,,,X***X^X.% D:%.X^X,X^X,***,++,***,+*@*X^X.% D:%.X^X,+^+,,,,,XX,,,,,X***X^X.% D:%.X^XXX+XXX+XXXXXX+XXXX+XX^X.% D:%.X^X*,^X,,,,,XX,,,,,+^^*X^X.% D:%.X^X,,^+,,*,,++,,*,,XX+XX^X.% D:%.X^X*,*X,,,,,XX,,,,,X,,,X^X.% D:%.X^XXXXXXX+XXXXXX+XXXXXXX^X.% D:%.X^X^^^^^^^^^^^^^^^^^^^^X^X.% D:%.X^X^^XX,XX^^XX^^XX,XX^^X^X.% D:%.X^X^^^^^^^^^^^^^^^^^^^^X^X.% D:%.X^+^^XX,XX^^XX^^XX,XX^^+^X.% D:%.X,X^^^^^^^^^^^^^^^^^^^^X,X.% D:%.XXXXXXXXX+XXXXXX+XXXXXXXXX.% D:%............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:76:Lesser vault (belvoir keep) X:7:5:19:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.###...........###.% D:%.#,######+######,#.% D:%.##+^^^^+&+^^^^+##.% D:%..#&^####+####^.#..% D:%..#.^#,^+^+,,#^.#..% D:%..#.^#,^#^#,,#^.#..% D:%..#.^#,*#^##+#^.#..% D:%..#.^#,^#^#,,#^&#..% D:%..#.^#,^+^#*,#^.#..% D:%..#.^####+####^.#..% D:%.##+^^^^^^^^^^^+##.% D:%.#,######+######,#.% D:%.###...#,*,#...###.% D:%.......#*,*#.......% D:%.......#####.......% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:77:Pattern vault (ragged spiral) X:8:30:31:47 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%X@&+.............................+&@X,,,,,,X.% D:%XXXX.p...bad.bad.bad.bad.cbb.....XXXX,,,,,,X.% D:%X@&X.a.dcb.dcb.dcb.dcb.dcc.baa...X&@X,,,,,,X.% D:%XX+X.b.d.....................ad..X+XX,,,,,,X.% D:%X....c.a.dabcdabcda...........dc....X,,,,,,X.% D:%X....d.b.d........a..cbadcba...c....X,,,,,,X.% D:%X...ad.c.c.dcbadcba..c.....ad..cb...X++XXX+X.% D:%X...a..d.b.d.........cdabc..d...b...X^^X^^^X.% D:%X...b.ad.a.a..abcd.......cd.dcb.a...X**X^X+X.% D:%X..cb.a..d.b..a..d..adcb..d...b.ad..X,,X^X^X.% D:%X..c..b..c.c..d.ad..a..ba.a...a..d..X&&X^X&X.% D:%X.dc.cb..b.d..c.a..PPP..a.a.cda..dc.X^^X^X+X.% D:%X.d..c...a.a..b.b.PPAPP.d.b.c.....c.+^^X^X*X.% D:%X.da.cd..d.b..a.c.PABAP.c.b.cba..bc.X^^X^X*X.% D:%X..a..d..c.c..d.d.PPAPP.b.c...a..b..X&&X^XXX.% D:%X..ab.da.b.d..c.a..PPP..a.c...d.ab..X,,X^X,X.% D:%X...b..a.a.a..b.ab.....da.d..cd.a...XXXX+X+X.% D:%X...c..b.d.b..a..bcd.bcd..a.bc..d...X**^^^,X.% D:%X...cd.bcd.c..ad...dab...ba.b..cd...X,,,,,,X.% D:%X....d.....d...dc......dcb..a..c....X,,,XXXX.% D:%X....da....da...cbadcbad...da.bc....X,,,+,,X.% D:%X.....a.....abc...........cd..b.....X,,,X,,X.% D:%XX+X..ab......cdabcdabcdabc..ab..X+XXXXXX,,X.% D:%X@&X...bc...................da...X&@X,,,,,,X.% D:%XXXX....cddaabbccddaabbcdabcd....XXXX,,,,,&+.% D:%X@&+.............................+&@X****&&+.% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX++X.% D:%.............................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:78:Pattern vault (alternating spiral) X:8:30:29:45 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X8+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^+8X% D:%X+XXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXX+X% D:%X^X.....................................X^X% D:%X^X.................p...................X^X% D:%X^X.........adcbadcba.dcbadcbad.........X^X% D:%X@X......dcba.........d.......dcba......X@X% D:%X,X...baad.....abcdab.dabcda.....addc...X,X% D:%X,X..cb.....bcda....b......abcd.....cb..X,X% D:%X,X.dc....dab....adcb.adcb....dab....ba.X,X% D:%X,X.d....cd....cba....a..bad....bc....a.X,X% D:%X,X.a...bc...adc...PPPPP...dcb...cd...d.X,X% D:%X,X.b...b....a....PPPAPPP....b....d...c.X,X% D:%X,X.c...a....b....PPABAPP....a....a...c.X,X% D:%X,X.d...d....c....PPPAPPP....d....b...b.X,X% D:%X,X.a...dc...cda...PPPPP...bcd...cb...b.X,X% D:%X,X.b....cb....abc.......dab....dc....a.X,X% D:%X,X.bc....bad....cddaabbcd....bad....da.X,X% D:%X,X..cd.....dcba...........adcb.....cd..X,X% D:%X,X...dabc.....adcbadcbadcba.....dabc...X,X% D:%X^X......cdab.................abcd......X^X% D:%X^X.........bccddaabcdabcdabcda.........X^X% D:%X^X.....................................X^X% D:%X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X% D:%X9+^^^@,,,,,&,,,,,,,,&,,,,,,,&,,,,,,@^^^+9X% D:%XXXXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXX% D:%...........................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Topi Ylinen # N:79:Pattern vault (wandering) X:8:30:30:42 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%.X9+^^^^^^^^^^^^^^^^^+,,,,,,,,,,,,,,,+8X% D:%.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X% D:%.X,X.................................X^X% D:%.X,X....adcbadcbadcbadcbadcbadc......X^X% D:%.X,X...ba.....................cbad...X^X% D:%.X,X..cb..bad.bad.bad.bad.bad....d...X^X% D:%.X,X..c..cb.dcb.dcb.dcb.dcb.dcba.dc..X^X% D:%.X&X..d.dc.....................a..c..X^X% D:%.X&X..d.d..bbcc...cbadcba..adc.ad.b..X^X% D:%.X+X..a.a.ab..cd.dc.....ad.a.c..d.a..X+X% D:%.X^X..a.b.a....d.d..PPP..d.b.c.cd.d..X@X% D:%&X^X..b.c.ad...a.a.PPAPP.c.c.b.c..dc.X9X% D:%.+^X..b.d..dc..a.b.PABAP.b.d.b.cb..c.+8X% D:%&X^X..c.a...cb.b.c.PPAPP.a.a.a..b..b.X9X% D:%.X^X..d.b....b.c.d..PPP..d.b.d.ab..a.X@X% D:%.X+X..a.c.cdab.d.da..d...dcb.d.a..da.X+X% D:%.X&X..b.d.c....a..abcd.......c.ad.d..X^X% D:%.X&X..c.a.cbad.ab......abc..bc..d.c..X^X% D:%.X,X..d.b....d..bcdabcda.cdab..cd.b..X^X% D:%.X,X..a.bcda.c................bc..a..X^X% D:%.X,X..b....abc..dab.dab.dab.dab..da..X^X% D:%.X,X..bcda.....cd.bcd.bcd.bcd...cd...X^X% D:%.X,X.....abcdabc..............pbc....X^X% D:%.X,X.................................X^X% D:%.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X% D:%.X9+^^^^^^^^^^^^^^^^^+,,,,,,,,,,,,,,,+8X% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # This is the new v_info.txt file as created by Chris Weisiger. The # vaults are in general more dense and difficult than standard # Angband/Zangband vaults. You may use them in personal copies # freely, but public releases must have credit given to me (Chris # Weisiger) somewhere, either in the documentation, or in the file # itself. If you wish to use a vault in some other method (as a quest # vault, for example), contact me at jmartin@inreach.com. # # As a final note, due to the size of most of these vaults, they are # less likely to be generated in the dungeons of Angband. # Chris Weisiger # N:80:Lesser vault (rock-hard) X:7:10:11:20 D:%%%%%%%%%%%%%%%%%%%% D:%..................% D:%.#^############^#.% D:%.#^#,,,,,,,,,,#^#.% D:%.#^##,,,,,,,,##^#.% D:%.##^##^^^^^^##^##.% D:%.###*##&&&&##*###.% D:%.#9##^##@@##^##9#.% D:%.#####^####^#####.% D:%..................% D:%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:81:Lesser vault (planet x) X:7:10:13:15 D:%%%%%%%%%%%%%%% D:%.............% D:%.XXXXX#XXXXX.% D:%.XXX,,,,,XXX.% D:%.X,XX,,,XX,X.% D:%.X,,XX9XX,,X.% D:%.#,,9###9,,#.% D:%.X,,XX9XX,,X.% D:%.X,XX,,,XX,X.% D:%.XXX,,,,,XXX.% D:%.XXXXX#XXXXX.% D:%.............% D:%%%%%%%%%%%%%%% # Chris Weisiger # N:82:Lesser vault (miniature cell) X:7:25:5:5 D:%%%%% D:%#8#% D:%888% D:%#8#% D:%%%%% # Chris Weisiger # N:83:Lesser vault (turnabout) X:7:10:12:15 D:############### D:#.............# D:#.###########.# D:#.#9##,,,,###.# D:#.###^^##,,##.# D:#.##&^^&##,,#.# D:#.###^^^^#,,#.# D:#.#,##,,##,,#.# D:#.#9,##^#@@##.# D:#.#####^#####.# D:#.............# D:############### # Chris Weisiger # N:84:Lesser vault (rooms) X:7:10:13:19 D:%%%%%%%%%%%%%%%%%%% D:%.................% D:%.###############.% D:%.#^+,&&#*,*+&&&#.% D:%.#^#,,&#,@,#***#.% D:%.#^#9,,+*,*#,,,+.% D:%.#^#############.% D:%.#^#9,,+*,*#,,,+.% D:%.#^#,,&#,@,#***#.% D:%.#^+,&&#*,*+&&&#.% D:%.###############.% D:%.................% D:%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:85:Lesser vault (cross) X:7:10:13:20 D:%%%%%%%%%%%%%%%%%%%% D:%########++########% D:%#*....*#..#*....*#% D:%#....&,#^^#,&....#% D:%#....,##..##,....#% D:%#+#####*..*#####+#% D:%#^^.....,,.....^^#% D:%#+#####*..*#####+#% D:%#....,##..##,....#% D:%#....&,#^^#,&....#% D:%#*....*#..#*....*#% D:%########++########% D:%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:86:Lesser vault (central) X:7:12:10:12 D:############ D:#,#^^^^^^#,# D:##+######+## D:#^#^^,,^^#^# D:#^#^&@9&^#^# D:#^#^&9@&^#^# D:#^#^^,,^^#^# D:##+######+## D:#,#^^^^^^#,# D:############ # Chris Weisiger # N:87:Lesser vault (camouflaged) X:7:10:9:12 D:%%%%%%%%%%%% D:%XXXXXXXXXX% D:%#,,,,,,,,X% D:%XXXXXXXXXX% D:%X,,,,,,,,#% D:%XXXXXXXXXX% D:%#,,,,,,,,X% D:%XXXXXXXXXX% D:%%%%%%%%%%%% # Chris Weisiger # N:88:Greater vault (twisted cube) X:8:20:24:41 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.......................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.....% D:%.X...............................XX....% D:%.X..X^^XXXXXXXXXXXXXXXXXXXXX.XXX..#X...% D:%.X..XX^^XX,,,,,,,,,,,,,,,,,X.X&X+..XX..% D:%.X..X+X^^XX,,,,,,,,,,,,,,,,+.X9&XX..XX.% D:%.X..X,XX^^XXXXXXXXXXXXXXXXXX.XXXXXX..X.% D:%.X..X,,XX..................X.X.......X.% D:%.X..X,,,XX.XXXXXXXX#XXXXXXXX.XXXXXX..X.% D:%.X..X^^^^X.X,9,&***&,,,&***X.X****X..X.% D:%.X..X&&&&X.X9,&***&,8,&***&X.X^^^^X..X.% D:%.X..X^^^^X.X,&***&,8,&***&9X.X&&&&X..X.% D:%.X..X****X.X&***&,,,&***&9,X.X^^^^X..X.% D:%.X..XXXXXX.XXXXXXXX#XXXXXXXX.XX,,,X..X.% D:%.X.......X.X..................XX,,X..X.% D:%.X..XXXXXX.XXXXXXXXXXXXXXXXXX^^XX,X..X.% D:%.XX..XX&9X.+,,,,,,,,,,,,,,,,XX^^X+X..X.% D:%..XX..+X&X.X,,,,,,,,,,,,,,,,,XX^^XX..X.% D:%...X#..XXX.XXXXXXXXXXXXXXXXXXXXX^^X..X.% D:%....XX...............................X.% D:%.....XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.......................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:89:Greater vault (the reward is worth it) X:8:20:16:63 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXX##XXXXXXXXXXXXX#XXXXXXXXXXXXX##XXXXXXXXXXXXXXX% D:%X@@@@@@@@@@@.XX88XX.......,,,X^X,,,.......XX88XX.@@@@@@@@@@@X% D:%X@XXXXXXXXXX..XXXX...XXXX.,,,X^X,,,.XXXX...XXXX..XXXXXXXXXX@X% D:%X@X8888888XXX..XX...XXX@XX,,,X^X,,,XX@XXX...XX..XXX8888888X@X% D:%X@X88899999XXX.....XXX@@@X..&X^X&..X@@@XXX.....XXX99999888X@X% D:%X@X8899.....XXX...XXX@@@@@..&X^X&..@@@@@XXX...XXX.....9988X@X% D:%X@X899.......XXX...XXX......&X^X&......XXX...XXX.......998X@X% D:%X+XXX...XXX&&&XXX^^^XXX.....&X^X&.....XXX^^^XXX&&&XXX...XXX+X% D:%X@@XXX.XXXX&&&&XXX^^^XXX....&X^X&....XXX^^^XXX&&&&XXXX.XXX@@X% D:%X^^^^XXX88X^^^^^^XXX^^^XXX..&X^X&..XXX^^^XXX^^^^^^X88XXX^^^^X% D:%X&&&&&&&&XX^^^^^^^^XXX...XXX&X^X&XXX...XXX^^^^^^^^XX&&&&&&&&X% D:%X,,,,,,,XX,,,,,,,,,,,XXX&&&XXX^XXX&&&XXX,,,,,,,,,,,XX,,,,,,,X% D:%X,,,,,,X#@@@@@@@@@@@@@@XXX&&&&&&&&&XXX@@@@@@@@@@@@@@#X,,,,,,X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:90:Greater vault (the i in the storm) X:8:40:25:41 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXX% D:%X&&..^..#9X..^^^^^^^^^^^^^..X9#..^..&&X% D:%X&...#....X..XXXXXX^XXXXXX..X....#...&X% D:%X...&#..#.X9XX.@8XX^XX8@.XX9X.#..#&...X% D:%X..&9#....XXX...@X,,,X@...XXX....#9&..X% D:%X^####..#...XX..XX,.,XX..XX...#..####^X% D:%X........,#..XX^X,,.,,X^XX..#,........X% D:%X.......#.....^XX,...,XX^.....#.......X% D:%X........,#....X,,...,,X....#,........X% D:%X.......#.....XX.......XX.....#.......X% D:%X.#......,#..XX^.......^XX..#,......#.X% D:%X.9#....#....#^^...9...^^#....#....#9.X% D:%X.#......,#..XX^.......^XX..#,......#.X% D:%X.......#.....XX.......XX.....#.......X% D:%X........,#....X,,...,,X....#,........X% D:%X.......#.....^XX,...,XX^.....#.......X% D:%X........,#..XX^X,,.,,X^XX..#,........X% D:%X^####..#...XX..XX,.,XX..XX...#..####^X% D:%X..&9#....XXX...@X,,,X@...XXX....#9&..X% D:%X...&#..#.X9XX.@8XX^XX8@.XX9X.#..#&...X% D:%X&...#....X&&XXXXXX^XXXXXX&&X....#...&X% D:%X&&..^..#9X..^^^^^^^^^^^^^..X9#..^..&&X% D:%XXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:91:Greater vault (the bank from hell) X:8:30:20:31 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXX+XXXXXXXXXXXXXX% D:%X.&.&.&.&.&.#...#.&.&.&.&.&.X% D:%X.#.#.#.#.#.#...#.#.#.#.#.#.X% D:%X...........#...#...........X% D:%X##########+##+##+##########X% D:%X@........^^^^^^^^^........@X% D:%X@^^^.....^^^...^^^.....^^^@X% D:%X##+#######+#####+#######+##X% D:%X.^^^.#@..^^^...^^^..@#.^^^.X% D:%X#...##...............#.###.X% D:%X@...@#.&.&.&.&.&.&.&.#..@..X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXX+X% D:%X.+^*****X*******^+^******X.X% D:%X@X^....^X^......^X^.....*X@X% D:%X.X*****^+^*******X*******X.X% D:%X@XXXXXXXXXXXXXXXXXXXXXXXXX@X% D:%X.@.@.@.@.@.@.@.@.@.@.@.@.@.X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:92:Greater vault (target practice) X:8:20:20:61 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X@........................................................#% D:%X@XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.#% D:%X@#89XXXXX...9&..@.9....&....##..........#....#@,,,,,,,,X.#% D:%X@X9XX,,,XX......@..........XX..........XX....X@....XXX.X.#% D:%X@XXX,XXX,XX....*.,.&....*.XX***********X.....X@..XXX&+.X.#% D:%X@XX,XX@XX,X.........,9...XX&&&&&&&&&&&&X@@@@@X@XXX**&+.X.#% D:%X@XX,X999X,X&.@..,.......XXXXXXXXXXXXXXXXXX+XXXXXX*XXXX.X.#% D:%X@XX,#989X,#.....9......XX99,,,,,,,,,,#,,,,,,,XXX&*&@@X.#.#% D:%X@XX,#989X,#.......&..@.XX99,,,,,,,,,,#,,,,,,,XXX&*&@@X.#.#% D:%X@XX,X999X,X...&.&.......XXXXXXXXXXXXXXXXXX+XXXXXX*XXXX.X.#% D:%X@XX,XX@XX,X..,...........XX&&&&&&&&&&&&X@@@@@X@XXX**&+.X.#% D:%X@XXX,XXX,XX......@..@.....XX***********X.....X@..XXX&+.X.#% D:%X@X9XX,,,XX.....9...........XX..........XX....X@....XXX.X.#% D:%X@#89XXXXX..&..........9.,...##..........#....#@,,,,,,,,X.#% D:%X@XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.#% D:%X@........................................................#% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:93:Greater vault (snake maze) X:8:15:21:23 D:%%%%%%%%%%%%%%%%%%%%%%% D:%.....................% D:%.XXXXXXXXX+XXXXXXXXX.% D:%.X9X9..XXX..X,,,9X9X.% D:%.X,XXX.^.XX.X,XXXX,X.% D:%.X,,,XXX..X.X,,,,X,X.% D:%.XXX,,,XX.X.XX*X,,,X.% D:%.X...X*X..X.XXXXXX.X.% D:%.X.XXXXX.XX.X.^.X..X.% D:%.X...^........X.X.XX.% D:%.X#XXXXXXXXXXXX...*X.% D:%.X&.........^&XX.XXX.% D:%.X.XXXXXXXXXX.XX,,,X.% D:%.X.X......^...*XXX,X.% D:%.X.X,XXXXXXXX&XX,,,X.% D:%.X.X,XX9,,,,X&9X,XXX.% D:%.X^X,,XXXXX.XXXX,X*X.% D:%.X.XX99XX**......^.X.% D:%.X+XXXXXXXXXXXXXXXXX.% D:%.....................% D:%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:94:Greater vault (roundabouts one) X:8:15:21:43 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X^^^^^^^^^^^^^^^^#&^^^&^^^^^^^^^^^^^&^,,X% D:%X^^^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&.,X% D:%X^.X+^&^..^^.....#&^^^&......^^....,XX&.X% D:%X.^X^&^XXXXXXXXXXXXXXXXXXXXXXXXXXX^,,X.^X% D:%X^.X&^XX,.^^......&^^^&#.....^^&^+X^^X^.X% D:%X.^X^^X,,.^^......&^^^&#.....^^ &^X^^X.^X% D:%X^.X^^X*^^XXXXXXXXXXXXXXXXXXXXX^^&X^^X^.X% D:%X.^X^^X*^X+^,,,,,,@#8X@9 *&XX^*X^^X.^X% D:%X^^X^^X^^X^&*^^^^^@XXX@^^^^^*&^X^^X^^X^^X% D:%X^.X^^X*^XX&*....9@X8#@,,,,,,^+X^*X^^X^.X% D:%X.^X^^X&^^XXXXXXXXXXXXXXXXXXXXX^^*X^^X.^X% D:%X^.X^^X^& ^^.....#&^^^&......^^.,,X^^X^.X% D:%X.^X^^X+^&^^.....#&^^^&......^^.,XX^&X.^X% D:%X^.X,,^XXXXXXXXXXXXXXXXXXXXXXXXXXX^&^X^.X% D:%X.&XX,....^^......&^^^&#.....^^..^&^+X.^X% D:%X,.&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX^^^X% D:%X,,^&^^^^^^^^^^^^^&^^^&#^^^^^^^^^^^^^^^^X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:95:Greater vault (roundabouts two) X:8:25:25:40 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%#^X.^^+X@XX.+X^^^^^+.X...X8#@&*X*****X% D:%X^X.^XX@XX.XX^^^^^^X.X.X^XXX@.&+9....X% D:%X^X.XX&XX.XX^^^^^^^X.X.X^X8#@&*X99...X% D:%X^X.X^^X&.XXXXXXXXXX.X.X^XXXXXXXXXX++X% D:%X^X.X.*X&.X.......&..X.X^X........X^^X% D:%X^X&X.*X^^X.XXXXXXXXXX.X&X#XXXXXX.X^.X% D:%X^X.X..X^^X...@........X.X......X.X^^X% D:%X^X.X..X^^XXXXXXXXXXXXXX.XXXXXX.X.X.^X% D:%X^X.X..X^^X..,.....,....&X8#,*X.X.X^^X% D:%X^X&X,,X^^X^XXXXXXXXXXXXXX##,,X.X.X^.X% D:%X^+&X..+^^X^^^^^^^^^^^^^^^9...#.X.#^^X% D:%X^X&X,,X^^X^XXXXXXXXXXXXXX##,*X.X.X.^X% D:%X^X.X..X^^X..,.....,....&X8#,,X.X.X^^X% D:%X^X.X..X^^XXXXXXXXXXXXXX.XXXXXX.X.X^.X% D:%X^X.X..X^^X...@........X.X......X.X^^X% D:%X^X&X.*X^^X.XXXXXXXXXX.X&X#XXXXXX.X.^X% D:%X^X.X.*X&.X.......&..X.X^X........X^^X% D:%X^X.X^^X&.XXXXXXXXXX.X.X^XXXXXXXXXX++X% D:%X^X.XX&XX.XX^^^^^^^X.X.X^X8#@&*X99...X% D:%X^X.^XX@XX.XX^^^^^^X.X.X^XXX@.&+9....X% D:%#^X.^^+X@XX.+X^^^^^+.X...X8#@&*X*****X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:96:*Greater* vault (roundabouts three) X:8:30:20:97 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X,&,,,,,,,,,,,^^^^^^^^^^......XX.#X.XXX.......X.X.......XXX.X#.XX......^^^^^^^^^^,,,,,,,,,,,&,X% D:%X,&,,,,,,,,,,,^^^^^^^^^^.....XX.XX.XXX..XXXX..X.X..XXXX..XXX.XX.XX.....^^^^^^^^^^,,,,,,,,,,,&,X% D:%X,&,,,,,,,,,,,XXXXXXXXXX....XX.XX.XXX^^XX@@XX.X&X.XX@@XX^^XXX.XX.XX....XXXXXXXXXX,,,,,,,,,,,&,X% D:%X,&&&&&&&&&&XXX********XXX..XX.XX.XXX^^X****X.X&X.X****X^^XXX.XX.XX..XXX********XXX&&&&&&&&&&,X% D:%X,,,,,,,,,,XXX..XXXXXX...XX+XX.XX.XXX^^XX&&...X@X...&&XX^^XXX.XX.XX+XX...XXXXXX..XXX,,,,,,,,,,X% D:%X@@@@@@@@XXX..XXX****XXX.....XX.XX.XXX..XX...XX+XX...XX..XXX.XX.XX.....XXX****XXX..XXX@@@@@@@@X% D:%X9988899XXX..XXX.XXXX@@XXXXXX.XX.XX.XXX..XXXXX^^^XXXXX..XXX.XX.XX.XXXXXX@@XXXX.XXX..XXX9998999X% D:%X######XXX**X##&&@**XX&&&@98X^^^^^XX.##X^^^^^^^^^^^^^^^X##.XX^^^^^X89@&&&XX**@&&##X**XXX######X% D:%X######XXX**X##&&@**XX&&&@98X^^^^^XX.##X^^^^^^^^^^^^^^^X##.XX^^^^^X89@&&&XX**@&&##X**XXX######X% D:%X9988899XXX..XXX.XXXX@@XXXXXX.XX.XX.XXX..XXXXX^^^XXXXX..XXX.XX.XX.XXXXXX@@XXXX.XXX..XXX9998999X% D:%X@@@@@@@@XXX..XXX****XXX.....XX.XX.XXX..XX...XX+XX...XX..XXX.XX.XX.....XXX****XXX..XXX@@@@@@@@X% D:%X,,,,,,,,,,XXX..XXXXXX...XX+XX.XX.XXX^^XX&&...X@X...&&XX^^XXX.XX.XX+XX...XXXXXX..XXX,,,,,,,,,,X% D:%X,&&&&&&&&&&XXX********XXX..XX.XX.XXX^^X****X.X&X.X****X^^XXX.XX.XX..XXX********XXX&&&&&&&&&&,X% D:%X,&,,,,,,,,,,,XXXXXXXXXX....XX.XX.XXX^^XX@@XX.X&X.XX@@XX^^XXX.XX.XX....XXXXXXXXXX,,,,,,,,,,,&,X% D:%X,&,,,,,,,,,,,^^^^^^^^^^.....XX.XX.XXX..XXXX..X.X..XXXX..XXX.XX.XX.....^^^^^^^^^^,,,,,,,,,,,&,X% D:%X,&,,,,,,,,,,,^^^^^^^^^^......XX.#X.XXX.......X.X.......XXX.X#.XX......^^^^^^^^^^,,,,,,,,,,,&,X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:97:Greater Vault (spiral GCV) X:8:45:17:39 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X% D:%X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X8X% D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X#X% D:%X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X8X8X% D:%X#X#XXXXXXXXXXXXXXXXXXXXXXXXXXX#X#X#X% D:%X8X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8X8X8X% D:%X#X#X#XXXXXXXXXXXXXXXXXXXXXXXXXXX#X#X% D:%X8X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X8X% D:%X#X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X% D:%X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X% D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:98:Greater vault (mirrored quartet) X:8:20:21:49 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X89@@@XXX,,,,XXX....^^^^^^^....XXX,,,,XXX@@@98X% D:%X9@@#XX....XXX**....XXX#XXX....**XXX....XX#@@9X% D:%X@XXX&&&&XXX**....@XX&&@&&XX@....**XXX&&&&XXX@X% D:%XXX....XXX**......@X**...**X@......**XXX....XXX% D:%X@...XXX**.......XXXXX...XXXXX.......**XXX...@X% D:%X@.#XX**.......9XX888XX^XX888XX9.......**XX#.@X% D:%XXXX&&&&&&&&&&&9#,,,,,X^X,,,,,#9&&&&&&&&&&&XXXX% D:%XXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXX% D:%#,,,,,,,,,,,,,,,,,,,,,#8#,,,,,,,,,,,,,,,,,,,,,#% D:%XXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXX% D:%XXXX&&&&&&&&&&&9#,,,,,X^X,,,,,#9&&&&&&&&&&&XXXX% D:%X@.#XX**.......9XX888XX^XX888XX9.......**XX#.@X% D:%X@...XXX**.......XXXXX...XXXXX.......**XXX...@X% D:%XXX....XXX**......@X**...**X@......**XXX....XXX% D:%X@XXX&&&&XXX**....@XX&&@&&XX@....**XXX&&&&XXX@X% D:%X9@@#XX....XXX**....XXX#XXX....**XXX....XX#@@9X% D:%X89@@@XXX,,,,XXX**..^^^^^^^..**XXX,,,,XXX@@@98X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:99:Greater vault (jigsaw) X:8:25:16:49 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...............................................% D:%.#############################################.% D:%.#&&&&&&&&&&&&###99999999###&&&&&&&###&*&*&*&#.% D:%.#&9999999999&&&##,,,,,,##,,,,,,,,##*&*&*&*&*#.% D:%.#&99999&&&&&&&&##,,,,,,##,,,,,,,,##&*&*&*&*&#.% D:%.#&9999&&###&&###,,,###,,###@8,@8,@###*&###&*#.% D:%.#&&99&&##*##&#,,,,##9##988##@@@@@@@@#&##9##&#.% D:%.##&&&&##***########999#######9999######9@9###.% D:%.###&&##*******##,,,,,,,,,##8##@@##88##@,@,@,#.% D:%.#8####&&&&&&&##,,,,,,,,,##999####9999##@,@,@#.% D:%.#9@&@&@&@&@&@##,,,,,,,,,##,,,,,,,,,,,##,@,@,#.% D:%.#*************##,,,,,,,,,##&&&&&&&&&##,@,@,@#.% D:%.#############################################.% D:%...............................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:100:Greater vault (hellpit) X:8:20:19:60 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..........................................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X@@&....#8.#9....^^&&&&&&&&&&&&###.^^,,......+8*9+,,,,,X.% D:%.X@&@.............^^................^^,,......+9*9+,,,,,X.% D:%.X&@@.............^^@@@@@@@@@@@###..^^,,......+9*9+,,,,,X.% D:%.X.....#8....#9...^^................^^,,......+9*9+,,,,,X.% D:%.X................^^&&&&&&&&&&&&###.^^,,.&&&&&+9*9+,,,,,X.% D:%.X.......@@@@.....^^................^^,,.&@@@&+9*9+,,,,,X.% D:%.#....#8.@@@@.#9..^^@@@@@@@@@@@###..^^,,.&@9@&+8*9+,,,,,X.% D:%.X.......@@@@.....^^................^^,,.&@@@&+9*9+,,,,,X.% D:%.X................^^&&&&&&&&&&&&###.^^,,.&&&&&+9*9+,,,,,X.% D:%.X.....#8....#9...^^................^^,,......+9*9+,,,,,X.% D:%.X&@@.............^^@@@@@@@@@@@###..^^,,......+9*9+,,,,,X.% D:%.X@&@.............^^................^^,,......+9*9+,,,,,X.% D:%.X@@&....#8.#9....^^&&&&&&&&&&&&###.^^,,......+8*9+,,,,,X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%..........................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:101:Greater vault (funnel) X:8:15:21:23 D:%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXX% D:%X&&&...........***X.#% D:%X&XXXXXXXXXXXXXXX*X.X% D:%X&X&&&.......***X*X.X% D:%X.X&XXXXXXXXXX.*X.X.X% D:%X.X&X8XX,,,,,XX*X.X.X% D:%X.X.XX*,,,,,,,X.X.X.X% D:%X.X.XX@@@@@@@@@.X.X.X% D:%X.X.X89,,,,,,,,.X.X.X% D:%X.X.X89,,,,,,,,.X.X.X% D:%X.X.X89,,,,,,,,.X.X.X% D:%X.X.XX@@@@@@@@@XX.X.X% D:%X.X.XX*,,,,,,,*XX.X.X% D:%X.X*X8XX,,,,,XX8X&X.X% D:%X.X*XXXXXXXXXXXXX&X.X% D:%X*X***.........&&&X&X% D:%X*XXXXXXXXXXXXXXXXX&X% D:%X***.............&&&X% D:%XXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:102:Greater vault (false wall) X:8:20:15:64 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X% D:%X^+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^+^X% D:%X^XXXXXXXXXXXXXXXXXXXXXXXXXX+X##X+XXXXXXXXXXXXXXXXXXXXXXXXXX^X% D:%X^+@*&*@*&*@*&*@*&*@*&*@*&*@*X@@X*@*&*@*&*@*&*@*&*@*&*@*&*@+^X% D:%X^XXXXXXXXXXXXXXXXXXXXXXXXX+XX,,XX+XXXXXXXXXXXXXXXXXXXXXXXXX^X% D:%X^+&.@.&.@.&.@.&.@.&.@.&.@.&X*..*X&.@.&.@.&.@.&.@.&.@.&.@.&+^X% D:%X^XXXXXXXXXXXXXXXXXXXXXXXX+XX*..*XX+XXXXXXXXXXXXXXXXXXXXXXXX^X% D:%X^+,,,,,,,,,,,,,,,,,,,,,,,,X,,..,,X,,,,,,,,,,,,,,,,,,,,,,,,+^X% D:%X^XXXXXXXXXXXXXXXXXXXXXXX+XX##99##XX+XXXXXXXXXXXXXXXXXXXXXXX^X% D:%X^+&.@.&.@.&.@.&.@.&.@.&.@X..####..X@.&.@.&.@.&.@.&.@.&.@.&+^X% D:%X^XXXXXXXXXXXXXXXXXXXXXX+XX...##...XX+XXXXXXXXXXXXXXXXXXXXXX^X% D:%X^+*&*@*&*@*&*@*&*@*&*@*&X&@&@88&@&@X&*@*&*@*&*@*&*@*&*@*&*+^X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:103:Greater vault (divisi) X:8:35:31:55 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.....................................................% D:%.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.% D:%.X,XX@@@&&,,,,,,,,,,,,,,,X8X,,,,,,,,,,,,,,,&&@@@XX,X.% D:%.X,,XXX@&&XXXXXXXXXXXXXX^X9X^XXXXXXXXXXXXXX&&@XXX,,X.% D:%.XX,,,XXX^^^XXX#8#8#8#8X^X@X^X8#8#8#8#XXX^^^XXX,,,XX.% D:%.XXXX,,,XXX^^^XXX######X^X&X^X######XXX^^^XXX,,,XXXX.% D:%.X@@XXX,,,XXX^^^XXX#8#8X^X,X^X8#8#XXX^^^XXX,,,XXX@@X.% D:%.X&&&&XXX,,,XXX^^^XX###X^X,X^X###XX^^^XXX,,,XXX&&&&X.% D:%.X^XX..#XXX,,,XXX^^^XXXX^X,X^XXXX^^^XXX,,,XXX#..XX^X.% D:%.X^.XXX#@@XXX,,,XXX^^^XX^X,X^XX^^^XXX,,,XXX@@#XXX.^X.% D:%.X^&&&XXX&&&XXX,,,XXX^^^^X,X^^^^XXX,,,XXX&&&XXX&&&^X.% D:%.X^XX&&&XXX^^^XXX,,,XXX^^X,X^^XXX,,,XXX^^^XXX&&&XX^X.% D:%.X,,XXX&@@XXX^^^XXX,,,XXX#,#XXX,,,XXX^^^XXX@@&XXX,,X.% D:%.X,,##XXX@##XXX^^^XXX,,,XX#XX,,,XXX^^^XXX##@XXX##,,X.% D:%.XXXX888XXX999XXX^^^###,#888#,###^^^XXX999XXX888XXXX.% D:%.X,,##XXX@##XXX^^^XXX,,,XX#XX,,,XXX^^^XXX##@XXX##,,X.% D:%.X,,XXX&@@XXX^^^XXX,,,XXX#,#XXX,,,XXX^^^XXX@@&XXX,,X.% D:%.X^XX&&&XXX^^^XXX,,,XXX^^X,X^^XXX,,,XXX^^^XXX&&&XX^X.% D:%.X^&&&XXX&&&XXX,,,XXX^^^^X,X^^^^XXX,,,XXX&&&XXX&&&^X.% D:%.X^.XXX#@@XXX,,,XXX^^^XX^X,X^XX^^^XXX,,,XXX@@#XXX.^X.% D:%.X^XX..#XXX,,,XXX^^^XXXX^X,X^XXXX^^^XXX,,,XXX#..XX^X.% D:%.X&&&&XXX,,,XXX^^^XX###X^X,X^X###XX^^^XXX,,,XXX&&&&X.% D:%.X@@XXX,,,XXX^^^XXX#8#8X^X,X^X8#8#XXX^^^XXX,,,XXX@@X.% D:%.XXXX,,,XXX^^^XXX######X^X&X^X######XXX^^^XXX,,,XXXX.% D:%.XX,,,XXX^^^XXX#8#8#8#8X^X@X^X8#8#8#8#XXX^^^XXX,,,XX.% D:%.X,,XXX@&&XXXXXXXXXXXXXX^X9X^XXXXXXXXXXXXXX&&@XXX,,X.% D:%.X,XX@@@&&,,,,,,,,,,,,,,,X8X,,,,,,,,,,,,,,,&&@@@XX,X.% D:%.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.% D:%.....................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:104:Lesser vault (darwinism) X:7:25:16:33 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...............................% D:%...............................% D:%.............######............% D:%..........####&&&&####.......#.% D:%.......####,,,,,,,,,,####...##.% D:%....####...,,@###..,,...##.###.% D:%.####8...#.,,@#89#.,,...8###9#.% D:%.####8.....,,@#89#.,,...8###9#.% D:%....####...,,@###..,,...##.###.% D:%.......####,,,,,,,,,,####...##.% D:%........#.####&&&&####.#.....#.% D:%........#....######....#.......% D:%.......##.............##.......% D:%...............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:105:*Greater* vault (cyclone) X:8:40:23:91 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%XXXX^^^^^^^^^XXX^^^^^^^^^XXX^^^#XXX8.X.&.X.&.X..*X+^.^.^.^.^.^..XX*^.^.^.^.^.^.^.^.^.^#8X% D:%XX***XXX...XXX...XXX...XXX...XXX^^X*.X^X^X^X^X^X.*XXXXXXXXXXXXX..XX^^XXXXXXXXXXXXXXXXXX#X% D:%X88XXX...XXX...XXX...XXX...XXX^^^,X*.X^X^X^X^X^X..*XX8@,,,,,,@XX,.XX^^..........@&.,**X.X% D:%XXXX@&@XXX@@@XXX&&&XXX&@&XXX^^^,..X*.X^X^X^X^X^X...@X@........@X,..X...........@&..,**X.X% D:%XX...XXX...XXX...XXX...XXX^^^,....X*.X^X^X^X^X^X...@X*..*9*..@XX^^XXXX*.......@&@..,**X.X% D:%X*.XXX...XXX...XXX...XXX^^^^....,,X*.&.X.&.X.&.X..*XX*.....,XXX.XXX@@XXX*....@&..@.,**X.X% D:%X*XX...XXX...XXX...XXX^^^,.^...XX+XXXXXXXXXXXXXX.*XX*....,,XXX.XXXXXX&&XXX*.@&....@8**X.X% D:%X^^^^XXX^^^^^^^^^XXX^^^,..*^.&XX.*X&^^*****^^#X.*XX*....,XXX&&XXX@@X#X*..XX@&.....8@**X.X% D:%XXXXXXXXXXXXXXXXXX^^^,...**^.&X8.*X@XXXXXXXXXX^.XX&....XXX&&XXX*****XXX*..XXXXXXXXXXXXX.X% D:%#^^^^^^^^^^^^^9#^^^.....*99^.@X8.*#^^^*****8XXXXX8@..XX#@@XX#@@@....8XXX*.+^.^.^.^.^.^+.X% D:%XXXXXXXXXXXXXXXXXX^^^,...**^.&X8.*X@XXXXXXXXXX^.XX&....XXX&&XXX*****XXX*..XXXXXXXXXXXXX.X% D:%X^^^^XXX^^^^^^^^^XXX^^^,..*^.&XX.*X&^^*****^^#X.*XX*....,XXX&&XXX@@X#X*..XX@&.....8@**X.X% D:%X*XX...XXX...XXX...XXX^^^,.^...XX+XXXXXXXXXXXXXX.*XX*....,,XXX.XXXXXX&&XXX*.@&....@8**X.X% D:%X*.XXX...XXX...XXX...XXX^^^^....,,X*.&.X.&.X.&.X..*XX*.....,XXX.XXX@@XXX*....@&..@.,**X.X% D:%XX...XXX...XXX...XXX...XXX^^^,....X*.X^X^X^X^X^X...@X*..*9*..@XX^^XXXX*.......@&@..,**X.X% D:%XXXX@&@XXX@@@XXX&&&XXX&@&XXX^^^,..X*.X^X^X^X^X^X...@X@........@X,..X...........@&..,**X.X% D:%X88XXX...XXX...XXX...XXX...XXX^^^,X*.X^X^X^X^X^X..*XX8@,,,,,,@XX,.XX^^..........@&.,**X.X% D:%XX***XXX...XXX...XXX...XXX...XXX^^X*.X^X^X^X^X^X.*XXXXXXXXXXXXX..XX^^XXXXXXXXXXXXXXXXXX#X% D:%XXXX^^^^^^^^^XXX^^^^^^^^^XXX^^^#XXX8.X.&.X.&.X..*X+^.^.^.^.^.^..XX*^.^.^.^.^.^.^.^.^.^#8X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:106:Greater vault (curlicues one) X:8:15:23:44 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#8X^^^^^^^^X% D:%X.............XXXXXXXXXXXX^^^^XXX...**XX+X% D:%X..XXXXXXX^^^XX..........XX^^^^X...**XX&@X% D:%X.XX*....XX^^X..XXXXXXX...X^^^^X...*XX...X% D:%X.X9*.....X^^X.XX^^^^^XX..X^^^^X...*X,...X% D:%X.XX*@@@..X^^X.X9@...^^X..X^^^^X...*X,,,,X% D:%X..XXXXX..X^^X.XX@.......XX...XX&,&XX,...X% D:%X^^^^^^^^XX^^X..XXXXXXXXXX....X^^^^X&....X% D:%XXXXXXXXXX^^^X&*&*&*&,&,&,&,&XX^^^^X&..XXX% D:%#^^^^^^^^^^XXXXXXXXXXXXXXXXXX#^^^^^X@..#8X% D:%XXXXXXXXXX^^^X&*&*&*&,&,&,&,&XX^^^^X&..XXX% D:%X^^^^^^^^XX^^X..XXXXXXXXXX....X^^^^X&....X% D:%X..XXXXX..X^^X.XX@.......XX...XX&,&XX,...X% D:%X.XX*@@@..X^^X.X9@...^^X..X^^^^X...*X,,,,X% D:%X.X9*.....X^^X.XX^^^^^XX..X^^^^X...*X,...X% D:%X.XX*....XX^^X..XXXXXXX...X^^^^X...*XX...X% D:%X..XXXXXXX^^^XX..........XX^^^^X...**XX&@X% D:%X.............XXXXXXXXXXXX^^^^XXX...**XX+X% D:%X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#8X^^^^^^^^X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:107:*Greater* vault (concentricity) X:8:20:19:76 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXX##XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX##XXXXXXXXXXXXXX% D:%X88XXX99XXXXXXX**###^^XXX99###^^XXX8888XXX^^###99XXX^^###**XXXXXXX99XXX88X% D:%XXX#99XXX,,,,,XXX**XXX^^XXX99XXX^^XX##XX^^XXX99XXX^^XXX**XXX,,,,,XXX99#XXX% D:%XX99###,,XXXXX,,XXX**XXX^^XXX@@XXX^^XX^^XXX@@XXX^^XXX**XXX,,XXXXX,,###99XX% D:%X9XXX,,XXX@@@XXX,,XXX**XXX^^XXX@@XXX^^XXX@@XXX^^XXX**XXX,,XXX@@@XXX,,XXX9X% D:%XXX,,XXX@@XXX@@XXX,,XXX@@XXX^^XXX&&XXXX&&XXX^^XXX@@XXX,,XXX@@XXX@@XXX,,XXX% D:%X9,XXX@@XXX8XXX@@XXX,,XXX@@XXX^^XXX,,,,XXX^^XXX@@XXX,,XXX@@XXX8XXX@@XXX,9X% D:%X9XXX&&XXX888XXX&&XXX,,XXX@@XXX^^XXX..XXX^^XXX@@XXX,,XXX&&XXX888XXX&&XXX9X% D:%X&&&&&XXX99999@@@&&XXX,,###@@XXX^^######^^XXX@@###,,XXX&&@@@99999XXX&&&&&X% D:%X9XXX&&XXX888XXX&&XXX,,XXX@@XXX^^XXX..XXX^^XXX@@XXX,,XXX&&XXX888XXX&&XXX9X% D:%X9,XXX@@XXX8XXX@@XXX,,XXX@@XXX^^XXX,,,,XXX^^XXX@@XXX,,XXX@@XXX8XXX@@XXX,9X% D:%XXX,,XXX@@XXX@@XXX,,XXX@@XXX^^XXX&&XXXX&&XXX^^XXX@@XXX,,XXX@@XXX@@XXX,,XXX% D:%X9XXX,,XXX@@@XXX,,XXX**XXX^^XXX@@XXX^^XXX@@XXX^^XXX**XXX,,XXX@@@XXX,,XXX9X% D:%XX99###,,XXXXX,,XXX**XXX^^XXX@@XXX^^XX^^XXX@@XXX^^XXX**XXX,,XXXXX,,###99XX% D:%XXX#99XXX,,,,,XXX**XXX^^XXX99XXX^^XX##XX^^XXX99XXX^^XXX**XXX,,,,,XXX99#XXX% D:%X88XXX99XXXXXXX**###^^XXX99###^^XXX8888XXX^^###99XXX^^###**XXXXXXX99XXX88X% D:%XXXXXXXXXXXXXX##XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX##XXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:108:Greater vault (castle death) X:8:35:20:60 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+XX....+XX...X****X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX....XXX....@X****X% D:%X.........................................XXX....&.@X****X% D:%X+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX....@.&.@X****X% D:%X999X..9..X&&&&&X,,,,,X@@@@@X..&..X@@@XXX....&.@.&.@X*99*X% D:%X@@@X.&.&.X&&&&&X,,,,,X@@@@@X99.99X9XXX.X..@.&.@.&.@X****X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...X..@.&.@.&.@XX##XX% D:%+^...&.&.&+...@.@.@+...@@@@@+.....99+...+..@.&.@.&.8+^^^^X% D:%+^...&.&.&+...@.@.@+...@@@@@+.....99+...+..@.&.@.&.8+^^^^X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...X..@.&.@.&.@XX##XX% D:%X@@@X.&.&.X&&&&&X,,,,,X@@@@@X99.99X9XXX.X..@.&.@.&.@X****X% D:%X999X..9..X&&&&&X,,,,,X@@@@@X..&..X@@@XXX....&.@.&.@X*99*X% D:%X+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX....@.&.@X****X% D:%X.........................................XXX....&.@X****X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX....XXX....@X****X% D:%X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+XX....+XX...X****X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:109:Greater vault (bubbles) X:8:35:25:41 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%X8X#X8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8X% D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX% D:%XX.9.#.9.X.9.#.9.X.9.#.9.X.9.#.9.#.9.XX% D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX% D:%X8XXX8X#X8X#X8X#X8X#X8X#X8X#X8X#X8X#X8X% D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX% D:%XX.9.#.9.X.9.X.9.#.9.X.9.#.9.X.9.X.9..X% D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX% D:%X8X#X8XXX8X#X8XXX8XXX8XXX8XXX8XXX8X#X8X% D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX% D:%XX.9.X.9.#.9.X.9.#.9.#.9.#.9.#.9.X.9.XX% D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX% D:%X8X#X8X#X8X#X8XXX8XXX8XXX8XXX8X#X8X#X8X% D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX% D:%XX.9.#.9.X.9.#.9.#.9.#.9.#.9.#.9.X.9.XX% D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX% D:%X8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8X#X8X% D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX% D:%##.9.#.9.#.9.#.9.#.9.#.9.#.9.#.9.X.9.XX% D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX% D:%X8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger (edited) # N:110:*Pattern* vault (absolutely huge) X:8:50:41:82 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%................................................................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X..^..^..^..^..^..^..^..^..^..^..^..^..^..^..^.X^^^X^^^XX.X..^.^.^.^.^.^.^.^.X.% D:%.X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X^X^X^X^XX.X.XXXXXXXXXXXXXXXX.X.% D:%.X.X^X.X@X^X.X&X^X.X@X^X.X&X^X.X@X^X.X&X^X.X@XX.X.X.X.X.XX.X.^#,@,@,@,@,@,@,X.X.% D:%.X.XX^X.X,X^X.X,X^X.X,X^X.X,X^X.X,X^X.X,X^X.X,X.X.X.X.X.XX.X.^XXXXXXXXXXXXXXX.X.% D:%.X^#^X.X,X^X.X,X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#XX.X.X.X.X.XX.XX..^#9,9,9,9,9,9X.X.% D:%.XXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X.X.X^X^XX##X.XX.^XXXXXXXXXXXXX.X.% D:%.XX^XX.X.X9#&X8XXXXXXXXXXXXXXXXXX#XXXXX,,X#XXXX.X.X^^^XX..XX.XX......^#89898X.X.% D:%.X#X^X##XX#X&#XX^^^.......,,XX8#^^^^#8XXXX^^#XX.X.XXXXX.@@.XX.XXXXXX.^XXXXXXX.X.% D:%.X^XX^X#.X9X&&&X#XXXXXXXXXX,,XXXX#XXXXX99XXXX8X^X^XXXX......XX....^XX,,X,,XX^.X.% D:%.X,,XX^XXX#XXX#XX.........XX,,XXX8XXX......XXXX^^^XXX..cdab..XXXXXX^XX,,,XX^XXX.% D:%.XX,,XX^XX9#9#9X..XXXXXXX..XX,,XXXXX..@@@@...XXXXXXX..ab..cd..XX99XX^XX,XX^XX@X.% D:%.XXX,,XX^#XXXXXX.XX*....XX..X...XX...........**XXXX..cd....ab..XX**XX^XXX^XX&&X.% D:%.X*XX..XX^XX@X9X&X**9XX^^X&&X&&&&X^^.&.&.&.&.***XX..ab......cd..XX**XX^X^XX&^XX.% D:%.X**XX..XX^X##XX.XX*98X^^X@@X****X#XXXXXX....**XX..cd..bcda..ab..XX**XX^XX&*X#X.% D:%.XX..XX&&XX^X#@X..XXXXX^^X&&X*@@*X^^,,,,X,,,,*XX..ab..da..bc..cd..XX**XXX&*XX^X.% D:%.X#X.&XX,,XX^XXX..@.@.@.XX..X****XX***@@X,,^^XX..cd..bc....da..ab..XX***&*XX,,X.% D:%.X^XX&^XX&,XX^XXXXXXXXXXX..XX&&&&^XX^&X8X^^^XX..ab..da..P...bc..cd..XX^^^XX*9XX.% D:%.#&&XX^^#X,@XX^#^^^^^^^^^^XX**@@*^^#X&XXX^^X#..pb..bc..PBP...ad..ba@.#X^XX**X8X.% D:%.X^XX&^XX&,XX^XXXXXXXXXXX..XX&&&&^XX^&X8X^^^XX....da..cPP...bc..cd..XX^^^XX*9XX.% D:%.X#X.&XX,,XX^XXX..@.@.@.XX..X****XX***@@X,,^^XX...c...b....ad..ba..XX***&*XX,,X.% D:%.XX..XX&&XX^X##X..XXXXX^^X&&X*@@*X^^,,,,X,,,,*XX..ba..ad..cb..dc..XX**XXX&*XX^X.% D:%.X**XX..XX^X##XX.XX*98X^^X@@X****X#XXXXXX....**XX..dc..cbad..ba..XX**XX^XX&*X#X.% D:%.X*XX..XX^XX#X9X&X**9XX^^X&&X&&&&X^^.&.&.&.&.***XX..ba......dc..XX**XX^X^XX&^XX.% D:%.XXX,,XX^#XXXXXX.XX*....XX..X...XX...........**XXXX..dc....ba..XX**XX^XXX^XX&&X.% D:%.XX,,XX^XX9#9#9X..XXXXXXX..XX,,XXXX...@@@@...XXXXXXX..ba..dc..XX99XX^XX,XX^XX@X.% D:%.X,,XX^XXX#XXX#XX.........XX,,XXX8XXX......XXXX^^^XXX..dcba..XXXXXX^XX,,,XX^XXX.% D:%.X^XX^X#.X9X&&&X#XXXXXXXXXX,,XXXX#XXXXX99XXXX8X^X^XXXX......XX....^XX,,X,,XX^.X.% D:%.X#X^X##XX#X&#XX^^^.......,,XX8#^^^^#8XXXX^^#XX.X.XXXXX.@@.XX.XXXXXX.^XXXXXXX.X.% D:%.XX^XX.X.X9#&X8XXXXXXXXXXXXXXXXXX#XXXXX,,X#XXXX.X.X^^^XX..XX.XX......^#89898X.X.% D:%.XXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X.X.X^X^XX##X.XX.^XXXXXXXXXXXXX.X.% D:%.X^#^X.X,X^X.X,X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#XX.X.X.X.X.XX.XX..^#9,9,9,9,9,9X.X.% D:%.X.XX^X.X,X^X.X,X^X.X,X^X.X,X^X.X,X^X.X,X^X.X,X.X.X.X.X.XX.X.^XXXXXXXXXXXXXXX.X.% D:%.X.X^X.X@X^X.X&X^X.X@X^X.X&X^X.X@X^X.X&X^X.X@XX.X.X.X.X.XX.X.^#,@,@,@,@,@,@,X.X.% D:%.X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X^X^X^X^XX.X.XXXXXXXXXXXXXXXX.X.% D:%.X..^..^..^..^..^..^..^..^..^..^..^..^..^..^..^.X^^^X^^^XX.X..^.^.^.^.^.^.^.^.X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%................................................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger (edited) # N:111:*Pattern* vault (titanic) X:8:50:43:99 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.................................................................................................% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.X8X...^.^.^.^.^.^.^.^.^.^.^....XX......&@9XXXX^^^XXXX9@&......XX....^.^.^.^.^.^.^.^.^.^.^...X8X.% D:%.XX#...XXXXXXXXXXXXXXXXXXXXX...XX&&XX...XXXX***XXX***XXXX...XX&&XX...XXXXXXXXXXXXXXXXXXXXX...#XX.% D:%.X^^^^XX&,&X@,,@X****X^^^^X...XX..XX..XXX&&&&XXX&XXX&&&&XXX..XX..XX...X^^^^X****X@,,@X&,&XX....X.% D:%.X...XX&,&,#,@@,#****#^^^^X^^XX^^XX..XXX,,,,XXX@&@XXX,,,,XXX..XX^^XX^^X^^^^#****#,@@,#,&,&XX...X.% D:%.X^^XX&,&,&X@,,@X****X^^^^X^^XX^^XX.XX,,,,,XX,,X9X,,XX,,,,,XX.XX^^XX^^X^^^^X****X@,,@X&,&,&XX..X.% D:%.X..X#XXXXXXXX#XXXXXXXXX#XX^^XX^^XX.X#,,,,,XX,,XXX,,XX,,,,,#X.XX^^XX^^XX#XXXXXXXXX#XXXXXXXX#X..X.% D:%.X^XX8X@,,,X^^^^X89@@#&&&&X***XX..X..XXX,,,,XXX^^^XXX,,,,XXX..X..XX***X&&&&#@@98X^^^^X,,,@X8XX.X.% D:%.X.X88X@,,,#^**XX89@@X&&&&X***XX..XXXXXXX,,,,XXX^XXX,,,,XXXXXXX..XX***X&&&&X@@98XX**^#,,,@X88X.X.% D:%.X^XXXX@,,,X^*XXXXXXXXXXXXX****XX.......XXXX,,,,,,,,,XXXX.......XX****XXXXXXXXXXXXX*^X,,,@XXXX.X.% D:%.X.X@@@9,,,X*XX******^^^^^^*****XXXXXXX^^^^XXXXXXXXXXX^^^^XXXXXXX*****^^^^^^******XX*X,,,9@@@X.X.% D:%.X^XXX#XXXXXXX*******XXXXXXXXXXXX,,,,,XXXX,*,*,*,*,*,*,XXXX,,,,,XXXXXXXXXXXX*******XXXXXXX#XXX.X.% D:%.X.X,,,,,X^^^^^^^^XXXX.^.^.^.#,,,,999,,,,#XXXXX###XXXXX#,,,,999,,,,#.^.^.^.XXXX^^^^^^^^X,,,,,X.X.% D:%.X^X,,,,XX^^^^^XXXX..^.XXXXXXXXXX,,,,,XXXX....^...^....XXXX,,,,,XXXXXXXXXX.^..XXXX^^^^^XX,,,,X.X.% D:%.X.X,,,#X....XXXX.^.XXXX........XXXXXXX..^.XXXX...XXXX.^..XXXXXXX........XXXX.^.XXXX....X#,,,X.X.% D:%.X^X,,XX...XXXX.^.XXX.......X..XX..^..^.XXXX^^XX.XX^^XXXX.^..^..XX..X.......XXX.^.XXXX...XX,,X.X.% D:%.X.X,XX...XXXX..XXX........XX.XX^^XXXXXXX..&^^^^^^^^^&..XXXXXXX^^XX.XX........XXX..XXXX...XX,X.X.% D:%.X^XXX...XXXX^^XX&&&&&&&&&XX..XX.XX,,,,,,@.&^XXXXXXX^&.@,,,,,,XX.XX..XX&&&&&&&&&XX^^XXXX...XXX.X.% D:%.X.X@&&XXXX.^.XX..........X..XX^.XX,,,,,,..XXX.^^^.XXX..,,,,,,XX.^XX..X..........XX.^.XXXX&&@X.X.% D:%.X^X@XXXX.^.XXX...........X..XX^XX9,,,,XXXXX^..X#X..^XXXXX,,,,9XX^XX..X...........XXX.^.XXXX@X.X.% D:%.X#XXX^.^.XXX...XX*****XX.X..XX..XX9XXXX^...^XXX,XXX^...^XXXX9XX..XX..X.XX*****XX...XXX.^.^XXX#X.% D:%.X..^.^XXXX......XX&@&XX..XX..XX^^XXX...^XXXXX,,,,,XXXXX^...XXX^^XX..XX..XX&@&XX......XXXX^.^..X.% D:%.X^XXXXX....&.....XX9XX....XX..XX...^.XXXX,,,,,,9,,,,,,XXXX.^...XX..XX....XX9XX.....&....XXXXX^X.% D:%.X.X...............XXX......XX..XX@XXXX..XXX,,,,9,,,,XXX..XXXX@XX..XX......XXX...............X.X.% D:%.X^X,,9X.....XX^X.........XXXX...XXX..@@..XXX,,,9,,,XXX..@@..XXX...XXXX.........X^XX.....X9,,X^X.% D:%.X.X,,9X.&..XX^XX...&...XXX....XXXX..cbad..XXX,,9,,XXX..dcba..XXXX....XXX...&...XX^XX..&.X9,,X.X.% D:%.X^X,,9X...XX^XX......XXX...XXXX....dc..dc...XX,,,XX...ad..ad....XXXX...XXX......XX^XX...X9,,X^X.% D:%.X.X,,9X..XX^XX..&...XX...XXX....cbad....cba..XX,XX..cba....dcba....XXX...XX...&..XX^XX..X9,,X.X.% D:%.X^X*****XX^XX......XX..XXX....adc.........ad..X#X..dc.........adc....XXX..XX......XX^XX*****X^X.% D:%.X.X****XX^XX......XX..XX**...ba...cdabcda..dc..@..ad..cdabcda...cb...**XX..XX......XX^XX****X.X.% D:%.X^XXXXXX^XX.&..&..X&&XX***..cb..abc.....ab..cbapXba..bc.....abc..ba..***XX&&X..&..&.XX^XXXXXX^X.% D:%.X.X^^^^^XX........X&&X****..c...a........bc......b..ab........c...a..****X&&X........XX^^^^^X.X.% D:%.X^X#XXX^X...XXX...X&&XX***..cd..adc.......cdabPB.bcda.......adc..da..***XX&&X...XXX...X^XXX#X^X.% D:%.X.X888X^X..XX9XX..XX..XX**...da...cbad...................dcba...cd...**XX..XX..XX9XX..X^X888X.X.% D:%.X^X888X^X.XX@@@XX&&XX..XXX....abc....dcbadcbadcbadcbadcbad....abc....XXX..XX&&XX@@@XX.X^X888X^X.% D:%.X.X888#^X,,,,,,,,,@@XX...XXX....cdab.......................bcda....XXX...XX@@,,,,,,,,,X^#888X.X.% D:%.X^XXXXXXXXXXXXXXXXXXXXXX...XXXX....bcdabcdabcdabcdabcdabcdab....XXXX...XXXXXXXXXXXXXXXXXXXXXX^X.% D:%.X.^.^.^.^.^.^.^.^.^.^.^....@@@XXXX...........................XXXX@@@....^.^.^.^.^.^.^.^.^.^.^.X.% D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.% D:%.................................................................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:112:*Greater* vault (subdivisions) X:8:50:45:81 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...............................................................................% D:%.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.% D:%.X.XX,,,,,...9X...XXXXXXXX***X,,,,,&&..X..&&,,,,,X***XXXXXXXX...X9...,,,,,XX.X.% D:%.X.XXXX,,,X.............9X***XXXXXXXX&.X.&XXXXXXXX***X9.............X,,,XXXX.X.% D:%.X.XXXXXX,XXXXXXXX...X.............9X.&X&.X9.............X...XXXXXXXX,XXXXXX.X.% D:%.X.#,,,,XXX*****9X...XXXXXXXX...X....&.X.&....X...XXXXXXXX...X9*****XXX,,,,#.X.% D:%.X.XXXXX#XXXX*****.........9X...XXXXXXXXXXXXXXX...X9.........*****XXXX#XXXXX.X.% D:%.X.#,,&,,&,,XXXXXXXXXX&&&X.............X.............X&&&XXXXXXXXXX,,&,,&,,#.X.% D:%.X.XXXXXXXXX#XXXX***9X...XXXXXXXX...X@@X@@X...XXXXXXXX...X9***XXXX#XXXXXXXXX.X.% D:%.X.#,&,&,&,&,&,&XXX***.........9X...XXXXXXX...X9.........***XXX&,&,&,&,&,&,#.X.% D:%.X.XXXXXXXXXXXXX#XXXXXXXXX&&&X.......**X**.......X&&&XXXXXXXXX#XXXXXXXXXXXXX.X.% D:%.X.#,@,@,@,@,@,@,@,@XXX..X...XXXXXXXX**X**XXXXXXXX...X..XXX@,@,@,@,@,@,@,@,#.X.% D:%.X.XXXXXXXXXXXXXXXXX#XXXX..........9X**X**X9..........XXXX#XXXXXXXXXXXXXXXXX.X.% D:%.X.#,9,9,9,9,9,9,9,9,9,9XXXXXX...X...**X**...X...XXXXXX9,9,9,9,9,9,9,9,9,9,#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXX#XXXXX...XXXXXXXXXXXXX...XXXXX#XXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#9&9&9&9&9&9&9&9&9&9&9&9&XXX.......&X&.......XXX&9&9&9&9&9&9&9&9&9&9&9&9#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXX&X&XXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#9@9@9@9@9@9@9@9@9@9@9@9@9@9@XXX99X&X&X99XXX@9@9@9@9@9@9@9@9@9@9@9@9@9@9#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXX@@X@@XXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#99999999999999999999999999999999XXX#XXX99999999999999999999999999999999#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X88X88X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X#X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X#XX#XX#X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X#X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X88X88X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#99999999999999999999999999999999XXX#XXX99999999999999999999999999999999#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXX@@X@@XXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#9@9@9@9@9@9@9@9@9@9@9@9@9@9@XXX99X&X&X99XXX@9@9@9@9@9@9@9@9@9@9@9@9@9@9#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXX&X&XXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#&9&9&9&9&9&9&9&9&9&9&9&9XXX.......&X&.......XXX&9&9&9&9&9&9&9&9&9&9&9&9#.X.% D:%.X.XXXXXXXXXXXXXXXXXXXXX#XXXXXX..XXXXXXXXXXXXX...XXXXX#XXXXXXXXXXXXXXXXXXXXX.X.% D:%.X.#,9,9,9,9,9,9,9,9,9,9XXXXXXX..X...**X**...X...XXXXXX9,9,9,9,9,9,9,9,9,9,#.X.% D:%.X.XXXXXXXXXXXXXXXXX#XXXX*.........9X**X**X9.........*XXXX#XXXXXXXXXXXXXXXXX.X.% D:%.X.#,@,@,@,@,@,@,@,@XXX*9X...XXXXXXXX**X**XXXXXXXX...X9*XXX@,@,@,@,@,@,@,@,#.X.% D:%.X.XXXXXXXXXXXXX#XXXXXXXXX&&&X.......**X**.......X&&&XXXXXXXXX#XXXXXXXXXXXXX.X.% D:%.X.#,&,&,&,&,&,&XXX***.........9X...XXXXXXX...X9.........***XXX&,&,&,&,&,&,#.X.% D:%.X.XXXXXXXXX#XXXX***9X...XXXXXXXX...X@@X@@X...XXXXXXXX...X9***XXXX#XXXXXXXXX.X.% D:%.X.#,,&,,&,,XXXXXXXXXX&&&X.............X.............X&&&XXXXXXXXXX,,&,,&,,#.X.% D:%.X.XXXXX#XXXX*****.........9X....XXXXXXXXXXXXX....X9.........*****XXXX#XXXXX.X.% D:%.X.#,,,,XXX*****9X...XXXXXXXX....X...&.X.&...X....XXXXXXXX...X9*****XXX,,,,#.X.% D:%.X.XXXXXX,XXXXXXXX&&&X.............9X.&X&.X9.............X&&&XXXXXXXX,XXXXXX.X.% D:%.X.XXXX,,,X............9X****XXXXXXXX&.X.&XXXXXXXX****X9............X,,,XXXX.X.% D:%.X.XX,,,,,..9X...XXXXXXXX****X,,,,,&&..X..&&,,,,,X****XXXXXXXX...X9..,,,,,XX.X.% D:%.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.% D:%...............................................................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger (edited) # N:113:*Greater* vault (divided) X:8:50:45:81 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%XcbadcbadcbadcbadcbadcbadcbadcbadcbadcXXXcdabcdabcdabcdabcdabcdabcdabcdabcdabcX% D:%XdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXbXXXbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXdX% D:%XaX8XX999X........................^^XaXXXaX^^........................X999XX8XaX% D:%XbXX#&&XXX..XXX^^XXXXXX...XXXXXXXXX^XdXXXdX^XXXXXXXXX...XXXXXX^^XXX..XXX&&#XXbX% D:%XcXX&&XX..XXX^^XXX......XXX89@&,*.^^XcXXXcX^^.*,&@98XXX......XXX^^XXX..XX&&XXcX% D:%XdX..XX..XX^^XXX....XXXXX*XXXXXXXXXXXbXXXbXXXXXXXXXXX*XXXXX....XXX^^XX..XX..XdX% D:%XaX.XX..XX^^XX....XXX.........X.....XaXXXaX.....X.........XXX....XX^^XX..XX.XaX% D:%XbX.X..XX^^XX....XX^^^^XX...&XX^^^X#XdXXXdX#X^^^XX&...XX^^^^XX....XX^^XX..X.XbX% D:%XcX.X.XX^^XX....XX^^^^XX...&XX^^^XX,XcXXXcX,XX^^^XX&...XX^^^^XX....XX^^XX..XXcX% D:%XdX..XX^^XX,,,,XX^^^^XX...&XX^^^XX,,XbXXXbX,,XX^^^XX&...XX^^^^XX,,,,XX^^XX..XdX% D:%XaX.XX^^XX,,,,XX....XX...&XX...XX,,,XaXXXaX,,,XX...XX&...XX....XX,,,,XX^^XX.XaX% D:%XbXXX^^XX,,,,XX....XX....XX...XX&&&&Xp###pX&&&&XX...XX....XX....XX,,,,XX^^XXXbX% D:%XcXX^^^X,,,,,X.....X....&X....X@@@@XXXX#XXXX@@@@X....X&....X.....X,,,,,X^^^XXcX% D:%XdX^^^XX,,,,XX....XX...&XX...XX88XXX**X#X**XXX88XX...XX&...XX....XX,,,,XX^^^XdX% D:%XaX^^XX....XX....XX^^^^XX....X8XXX****X#X****XXX8XX...XX^^^^XX....XX....XX^^XaX% D:%XbX^^X.....X.....X^^^^^X.....XXX+...XXX#XXX...+XXXX....X^^^^^X.....X.....X^^XbX% D:%XcX^XX....XX....XX^^^^XX...XXX&&+..XX.X#X*XX..+&&XXX...XX^^^^XX....XX....XX^XcX% D:%XdX^X***********X.........XXX&&&+.X#..X#X**#X.+&&&XXX.........X***********X^XdX% D:%XaX^XXXXXXXXXXXXXXXXXXXXXXXX,,,,+XX..XXBXX**XX+,,,,XXXXXXXXXXXXXXXXXXXXXXXX^XaX% D:%XbX^^^^^^^^^^^^^^^^^^^^^X#X,XX,,XX,,#XAPAX#,,XX,,XX,X#X^^^^^^^^^^^^^^^^^^^^^XbX% D:%XcXX##XXXXXXXXXXXXXXXXXX#X,,8X,XX,9XXAcbcAXX9,XX,X8,,X#XXXXXXXXXXXXXXXXXX##XXcX% D:%XPPXX@@XX99XX^^^^^^XX...X#X,XX,,XX,,#XbabX#,,XX,,XX,X#X...XX^^^^^^XX99XX@@XXPPX% D:%XPBAXX,,XX@@XX..XX..XX..XXXX,,,,+XX..XXpXX**XX+,,,,XXXX..XX..XX..XX@@XX,.XXABPX% D:%XPAXXX.,,XX&&....XX..XX...XXX&&&+.X#..X#X**#X.+&&&XXX...XX..XX....&&XX,,.XXXAPX% D:%XAXX.X..,,XXXXXXXXXX..XX...XXX&&+..XX.X#X*XX..+&&XXX...XX..XXXXXXXXXX,,..X.XXAX% D:%XXXX.XX..,XX^^^^^^^^^^^^^^^^^XXX+...XXX#XXX...+XXX^^^^^^^^^^^^^^^^^XX,..XX.XXXX% D:%X.X...X..,,X^^^^^^^^^^^^^^^^^^^XXX****X#X****XXX^^^^^^^^^^^^^^^^^^^X,,..X...X.X% D:%X...X.XX..,X#^^^^^^^^^^^^^^^^^^^^XXX**X#X**XXX^^^^^^^^^^^^^^^^^^^^#X,..XX.X...X% D:%X.XXXXXXXX,,XXX^^^^^^^^^^^^^^^^^^^^XXXX#XXXX^^^^^^^^^^^^^^^^^^^^XXX,,XXXXXXXX.X% D:%X.......XXX,,,XX^^^^^^^^^^^^^^^^^^^^^^X#X^^^^^^^^^^^^^^^^^^^^^^XX,,,XXX.......X% D:%XXXXX*X...XX.,,XX.XX....XXXX....XXXX..X#X..XXXX....XXXX....XX.XX,,.XX...X*XXXXX% D:%X999XXXXX..X..,,X..XX..XX..XX..XX@@XX.X#X.XX@@XX..XX..XX..XX..X,,..X..XXXXX999X% D:%X99XX@@@X..XX..,XX..XXXX....XXXX&&&&XXX#XXX&&&&XXXX....XXXX..XX,..XX..X@@@XX99X% D:%X9XX....X...XX.,,XX..........XXXX,,,,,X#X,,,,,XXXX..........XX,,.XX...X....XX9X% D:%XX#.....X....X..,,X.........XX,,XX,,,,X#X,,,,XX,,XX.........X,,..X....X.....#XX% D:%XXX+X+X+X....X#..,XX.......XX,,,,XX,,,X#X,,,XX,,,,XX.......XX,..#X....X+X+X+XXX% D:%XX+X+X+XX.....XXX,,XXX....XX*,,,,*XX,,X#X,,XX*,,,,*XX....XXX,,XXX.....XX+X+X+XX% D:%X#X.....X+XXXXXXXX,,,XX..XX.X&99&X.XX,X#X,XX.X&99&X.XX..XX,,,XXXXXXXX+X.....X#X% D:%X8XX....X^^^^^^^^X..,,X..X..XX88XX..X,X#X,X..XX88XX..X..X,,..X^^^^^^^^X....XX8X% D:%X88XX888XXXXXXXX+XX..,XX.X...XXXX...X,X#X,X...XXXX...X.XX,..XX+XXXXXXXX888XX88X% D:%X888XXXX#9999999..X..,,X..............X#X..............X,,..X..9999999#XXXX888X% D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Chris Weisiger # N:114:Lesser vault (interlock) X:7:20:12:17 D:%%%%%%%%%%%%%%%%% D:%......^^^......% D:%.######+######.% D:%.#,,&9#^#9&,,#.% D:%.#,,,&#^#&,,,#.% D:%.#,,,,#^#,,,,#.% D:%.#^####^####^#.% D:%.#^#@,,9,,@#^#.% D:%.#^#,,989,,#^#.% D:%.#+#########+#.% D:%.^^^.......^^^.% D:%%%%%%%%%%%%%%%%% # Eric Bock # N:115:Lesser vault (star) X:7:25:15:23 D: %% %%%%%%% %% D: %..% %..&..% %..% D: %&..&%#&...&#%&..&% D: %......#..X..#......% D: %&..X....X....X..&% D:%%##...X.+XXX+.X...##%% D:%.&...+XXX@9@XXX+...&.% D:%&.XXXX**,999,**XXXX.&% D:%.&...+XXX@9@XXX+...&.% D:%%##...X.+XXX+.X...##%% D: %&..X....X....X..&% D: %......#..X..#......% D: %&..&%#&...&#%&..&% D: %..% %..&..% %..% D: %% %%%%%%% %% # Eric Bock # N:116:Pattern vault (grand quartet) X:8:50:44:62 D: %%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%% D:%%.X,9,,,,+^^^^^^&&&@@&&&^^^^^^^^^^^^&&&@@&&&^^^^^^+,,,,9,X.%% D:%..X,,,,9,XXXXXXXXXXXXXXXXXXXX++XXXXXXXXXXXXXXXXXXXX,9,,,,X..% D:%.XXXX+XXXX@&....................................&@XXXX+XXXX.% D:%.X9998999X&8....................................8&X9998999X.% D:%.X8999998X...aadcbadcbadcbadcbadcbadcbadcbadcba...X8999998X.% D:%.XXX+X+XXX..bb................................ad..XXX+X+XXX.% D:%..X^^^^^X...c..abcdabcdabcdaa..dabcdabcdabcdd..d...X^^^^^X..% D:XX.X*****X...d.da............b..d............aa.c...X*****X.XX D:XX.X*****X...a.d..ccbadcbad..c.cd..bbadcbadc..b.b...X*****X.XX D:%..X^^^^^X...b.c.dd.......dc.d.c..cc.......cb.c.a...X^^^^^X..% D:%.XXX+X+XXX..c.b.a..bcdaa..c.a.b..d..abcdd..b.d.d..XXX+X+XXX.% D:%.X8X,,,X8X..d.a.b..bX..bb.b.b.ba.a.da..Xa..a.a.c..X8X,,,X8X.% D:%.X@+,,,+@X..a.d.c..bad..c.a.c..a.b.d..bbb..d.b.b..X@+,,,+@X.% D:%.XXX+X+XXX..b.d.cd..Xd..d.d.cd.d.c.c..cX..cc.c.a..XXX+X+XXX.% D:%..X^^^^^X...c.c..dabcd..a.c..d.c.d.b..ddabb..d.d...X^^^^^X..% D:XX.X*****X...d.bb.......ba.b..a.b.a.aa.......ad.c...X*****X.XX D:XX.X*****X...a..aadcbadcb..a.ba.a.b..ddcbadcba..b...X*****X.XX D:%..X^^^^^X...ab...........dd.b..d.bc...........aa...X^^^^^X..% D:%.XX+XXX+XX&..bcc..bbcdabcc..cXXc..cdd..ccdabcdd..&XX+XXX+XX.% D:%.X989X989X@8...ddaa.........XdbX....aabb........8@X989X989X.% D:%.X989X989X@8........bbcc....XbdX.........aadd...8@X989X989X.% D:%.XX+XXX+XX&..cdabcdaa..ddd..aXXa..ccbadcbb..ccb..&XX+XXX+XX.% D:%..X^^^^^X...bc...........aa.d..b.dd...........ba...X^^^^^X..% D:XX.X&&&&&X...b..bbadcbadc..b.c.cb.a..bcdabcdaa..a...X&&&&&X.XX D:XX.X&&&&&X...a.cc.......cb.c.b.c..b.ab.......bb.d...X&&&&&X.XX D:%..X^^^^^X...d.d..abcda..b.d.a.d..c.a..ccbad..c.c...X^^^^^X..% D:%.XXX+X+XXX..c.a.da..Xa..a.a.d.da.ddd.ddXAAdc.d.b..XXX+X+XXX.% D:%.X@+^^^+@X..b.b.d..cba..d.b.c..a.....aAABAAc.a.a..X@+^^^+@X.% D:%.X8X^^^X8X..a.c.c..cX..cc.c.cb.b.ddd.bbXPAAb.b.d..X8X^^^X8X.% D:%.XXX+X+XXX..d.d.b..cdabb..d..b.c.c.a..ccdA.a.c.c..XXX+X+XXX.% D:%..X^^^^^X...c.a.aa.......ad..a.d.b.ab.....dd.d.b...X^^^^^X..% D:XX.X^^^^^X...b.b..ddcbadcba..da.a.a..bcdabcc..a.a...X^^^^^X.XX D:XX.X^^^^^X...a.bc............d..b.dd.........ba.d...X^^^^^X.XX D:%..X^^^^^X...d..cdabcdabcdabcc..c..ccbadcbadcb..c...X^^^^^X..% D:%.XXX+X+XXX..cc.................cd.............bb..XXX+X+XXX.% D:%.X@@@@@@@X...bbadcbadcbadcbadc..dabcdabcdabcdaa...X@@@@@@@X.% D:%.X@@@@@@@X&8.................p..................8&X@@@@@@@X.% D:%.XXXX+XXXX@&....................................&@XXXX+XXXX.% D:%..X&&&&@&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&@&&&&X..% D:%%.X&@&&&&+^^^^^^,,,,,,,^^^^^^XX^^^^^^,,,,,,,^^^^^^+&&&&@&X.%% D: %.XXXXXXXXXXXXXXXXXXXXXXXXXX+XX+XXXXXXXXXXXXXXXXXXXXXXXXXX.% D: %.....................&&&&&@@XX@@&&&&&.....................% D: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%XX%%%%%%%%%%%%%%%%%%%%%%%%%%%%% N:117:Sphere X:8:25:25:47 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.............................................% D:%......XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX......% D:%.....XX8,,XXX998XXXX98889XXXX899XXX,,8XX.....% D:%....XX8,,XX,,,,9X99XXX#XXX99X9,,,,XX,,8XX....% D:%....X8,,,X,,,,,9X.XX9#8#9XX.X9,,,,,X,,,8X....% D:%....X,,,,X..&XXXX#XX9XXX9XX#XXXX&..X,,,,X....% D:%....X,XXX#.&XX,,X@@#XX9XX#@@X,,XX&.#XXX,X....% D:%....XXX.^^&XX,,XX#XXX***XXX#XX,,XX&^^.XXX....% D:%....XX..XXXX,,XXX&&XX***XX&&XXX,,XXXX..XX....% D:%....XX+XX&&XX+X9X&XX^X.X^XX&X9X+XX&&XX+XX....% D:%....XX^^^^^XX.&.X#X^^X9X^^X#X.&.XX^^^^^XX....% D:%....+^^^^^^+^&@&+^^^^9X9^^^^+&@&^+^^^^^^+....% D:%....XX^^^^^XX.&.X#X^^X9X^^X#X.&.XX^^^^^XX....% D:%....XX+XX&&XX+X9X&XX^X.X^XX&X9X+XX&&XX+XX....% D:%....XX..XXXX,,XXX&&XX***XX&&XXX,,XXXX..XX....% D:%....XXX.^^&XX,,XX#XXX***XXX#XX,,XX&^^.XXX....% D:%....X,XXX#.&XX,,X@@#XX9XX#@@X,,XX&.#XXX,X....% D:%....X,,,,X..&XXXX#XX9XXX9XX#XXXX&..X,,,,X....% D:%....X8,,,X,,,,,9X.XX9#8#9XX.X9,,,,,X,,,8X....% D:%....XX8,,XX,,,,9X99XXX#XXX99X9,,,,XX,,8XX....% D:%.....XX8,,XXX998XXXX98889XXXX899XXX,,8XX.....% D:%......XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX......% D:%.............................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% N:118:Hourglass X:8:24:37:27 D:%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.........................% D:%.......XXXXX+XXXXX.......% D:%...XXXXX...^^^...XXXXX...% D:%..XX,,,&.XXX+XXX.&,,,XX..% D:%...XXXXXXX.^^^.XXXXXXX...% D:%...XX**...@X^X@...**XX...% D:%....X**XXXXX^XXXXX**X....% D:%...XXX,X.........X,XXX...% D:%...X9X9X..*X+X*..X9X9X...% D:%...X9XXX.*XX^XX*.XXX9X...% D:%..XX.*X9@XX...XX@9X*.XX..% D:%..X..*XX@XXX^XXX@XX*..X..% D:%..X..**X8X9X+X9X8X**..X..% D:%..X..**XXX.^^^.XXX**..X..% D:%.XX.XXX8X8.XXX.8X8XXX.XX.% D:%.X..X9X8XX.^X^.XX8X9X..X.% D:%.X+XX9XXXXXX^XXXXXX9XX+X.% D:%.X.^^X^^.........^^X^^.X.% D:%.X+XX9XXXXXX^XXXXXX9XX+X.% D:%.X..X9X8XX.^X^.XX8X9X..X.% D:%.XX.XXX8X8.XXX.8X8XXX.XX.% D:%..X..**XXX.^^^.XXX**..X..% D:%..X..**X8X9X+X9X8X**..X..% D:%..X..*XX@XXX^XXX@XX*..X..% D:%..XX.*X9@XX&.&XX@9X*.XX..% D:%...X9XXX.*XX^XX*.XXX9X...% D:%...X9X9X..*X+X*..X9X9X...% D:%...XXX,X.........X,XXX...% D:%....X**XXXXX^XXXXX**X....% D:%...XX**...@X^X@...**XX...% D:%...XXXXXXX.^^^.XXXXXXX...% D:%..XX,,,&.XXX+XXX.&,,,XX..% D:%...XXXXX...^^^...XXXXX...% D:%.......XXXXX+XXXXX.......% D:%.........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%% N:119:Symmet X:8:20:23:29 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...........................% D:%....XXXXXXXXX+XXXXXXXXX....% D:%...XX,,.....^^^.....,,XX...% D:%..XX,,.XXXXXX+XXXXXX.,,XX..% D:%.XX,,.XX9&&&X@X&&&9XX.,,XX.% D:%.X,,.XX9,...X.X...,9XX.,,X.% D:%.X..XX8,,...X&X...,,8XX..X.% D:%.X.XX9,,,.^^X^X^^.,,,9XX.X.% D:%.X.X9,,,,.^.X^X.^.,,,,9X.X.% D:%.X^XXXXXXXXX+^+XXXXXXXXX^X.% D:%.+^+@....&^^^^^^^&....@+^+.% D:%.X^XXXXXXXXX+^+XXXXXXXXX^X.% D:%.X.X9,,,,.^.X^X.^.,,,,9X.X.% D:%.X.XX9,,,.^^X^X^^.,,,9XX.X.% D:%.X..XX8,,...X&X...,,8XX..X.% D:%.X,,.XX9,...X.X...,9XX.,,X.% D:%.XX,,.XX9&&&X@X&&&9XX.,,XX.% D:%..XX,,.XXXXXX+XXXXXX.,,XX..% D:%...XX,,.....^^^.....,,XX...% D:%....XXXXXXXXX+XXXXXXXXX....% D:%...........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%% N:120:Workaround X:7:14:19:21 D:%%%%%%%%%%%%%%%%%%%%% D:%...................% D:%.XX..XXXXXXXXX..XX.% D:%.X..XX*******XX..X.% D:%...XX,,@XXX@,,XX...% D:%...X..&XX8XX&..X...% D:%..XX..XX,9,XX..XX..% D:%..X..XX,,,,,XX..X..% D:%..X.XXXXX&XXXXX.X..% D:%....^^^+&X&+^^^....% D:%..X.XXXXX&XXXXX.X..% D:%..X..XX,,,,,XX..X..% D:%..XX..XX,9,XX..XX..% D:%...X..&XX8XX&..X...% D:%...XX,,@XXX@,,XX...% D:%.X..XX*******XX..X.% D:%.XX..XXXXXXXXX..XX.% D:%...................% D:%%%%%%%%%%%%%%%%%%%%% N:121:Crown X:7:12:19:28 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%..........................% D:%.....XXXXXXXXXXXXXXXX.....% D:%..XXXX^^^^+XXXX+^^^^XXXX..% D:%.XX^^^^X+XX,XX,XX+X^^^^XX.% D:%.X9X+XXX.X,,XX,,X.XXX+X9X.% D:%.XXX9X@@.X,,XX,,X.@@X9XXX.% D:%..X88X@@.X,,XX,,X.@@X88X..% D:%..XXXXXXXXX+XX+XXXXXXXXX..% D:%..........................% D:%..XXXXXXXXX+XX+XXXXXXXXX..% D:%..X88X@@.X,,XX,,X.@@X88X..% D:%.XXX9X@@.X,,XX,,X.@@X9XXX.% D:%.X9X+XXX.X,,XX,,X.XXX+X9X.% D:%.XX^^^^X+XX,XX,XX+X^^^^XX.% D:%..XXXX^^^^+XXXX+^^^^XXXX..% D:%.....XXXXXXXXXXXXXXXX.....% D:%..........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%% N:122:Interstices X:8:22:19:45 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%...........................................% D:%.......XXXXXXXXXXXX^^^^^XXXXXXXXXXXX.......% D:%......XXXX*X*X&X@XX^^^^^XX@X&X*X*XXXX......% D:%.....XXXX*X*XX&X@X.X.X.X..@X&XX*X*XXXX.....% D:%...XXXX,XXXX*X+XX,XX.X.XX,XX+X*XXXX,XXXX...% D:%..XX89,XX9X**X.X,,X&X@X&X,,X.X**X9XX,98XX..% D:%.XXXXX#X^XX*XX.XXXX.X.X.XXXX.XX*XX^X#XXXXX.% D:%.XX8XX@XX^XX.XX^XX.X^^^X.XX^XX@XX^XX@XX8XX.% D:%.X89#9&+&.+..+^^+..X^X^X..+^^+.&+.&+&9#98X.% D:%.XX8XX@XX^XX.XX^XX.X^^^X.XX^XX@XX^XX@XX8XX.% D:%.XXXXX#X^XX*XX.XXXX.X.X.XXXX.XX*XX^X#XXXXX.% D:%..XX89,XX9X**X.X,,X&X@X&X,,X.X**X9XX,98XX..% D:%...XXXX,XXXX*X+XX,XX.X.XX,XX+X*XXXX,XXXX...% D:%.....XXXX*X*XX&X@X.X.X.X.X@X&XX*X*XXXX.....% D:%......XXXX*X*X&X@XX^^^^^XX@X&X*X*XXXX......% D:%.......XXXXXXXXXXXX^^^^^XXXXXXXXXXXX.......% D:%...........................................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% N:123:Mazer X:8:26:35:31 D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.............................% D:%........XXXXXX+XXXXXX........% D:%.........XX@.X.X.@XX.........% D:%.......XXXX+X...X+XXXX.......% D:%......XX8#..XXXXX..#8XX......% D:%.....XXXXX.X.X@X.X.XXXXX.....% D:%....XXX8#.XXX^^^XXX.#8XXX....% D:%...XXXXXXX.X.XXX.X.XXXXXXX...% D:%..XX8XX9XXX^^X@X^^XXX9XX8XX..% D:%.XXX#XXX..XXX...XXX..XXX#XXX.% D:%.X,X,X.XXX.X^XXX^X.XXX.X,X,X.% D:%.XX,X.XXX.XX^X@X^XX.XXX.X,XX.% D:%.X,X.X&XXX&&XX@XX&&XXX&X.X,X.% D:%.XX.X&X.XXXX,,X,,XXXX.X&X.XX.% D:%.X.X&X.XXXXX&X,X&XXXXX.X&X.X.% D:%.X9XX.X.XX&XXX9XXX&XX.X.XX9X.% D:%.X98XX@X..X,,9X9,,X..X@XX89X.% D:%.X9XX.X.XX&XXX9XXX&XX.X.XX9X.% D:%.X.X&X.XXXXX&X,X&XXXXX.X&X.X.% D:%.XX.X&X.XXXX,,X,,XXXX.X&X.XX.% D:%.X,X.X&XXX&&XX@XX&&XXX&X.X,X.% D:%.XX,X.XXX.XX^X@X^XX.XXX.X,XX.% D:%.X,X,X.XXX.X^XXX^X.XXX.X,X,X.% D:%.XXX#XXX..XXX...XXX..XXX#XXX.% D:%..XX8XX9XXX^^X@X^^XXX9XX8XX..% D:%...XXXXXXX.X.XXX.X.XXXXXXX...% D:%....XXX8#.XXX^^^XXX.#8XXX....% D:%.....XXXXX.X.X@X.X.XXXXX.....% D:%......XX8#..XXXXX..#8XX......% D:%.......XXXX+X...X+XXXX.......% D:%.........XX@.X.X.@XX.........% D:%........XXXXXX+XXXXXX........% D:%.............................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% N:124:Orb X:8:16:27:27 D:%%%%%%%%%%%%%%%%%%%%%%%%%%% D:%.........................% D:%.........#XXXXX#.........% D:%.......XXX,,,,,XXX.......% D:%......XXXX,,,,,XXXX......% D:%.....XXXXXX,,,XXXXXX.....% D:%....XXXXXXX+X+XXXXXXX....% D:%...X9XXXX^^^^^^^XXXX9X...% D:%..XXXX8X@X+XXX+X@X8XXXX..% D:%..X&&X9XXX^X^X^XXX9X&&X..% D:%..X&&XX+XXX^X^XXX+XX&&X..% D:%.XX.XXXX.X.X@X.X.XXXX.XX.% D:%.XX+XX9XX^XX,XX^XX9XX+XX.% D:%.+^^+^^^^^X9,9X^^^^^+^^+.% D:%.XX+XX9XX^XX,XX^XX9XX+XX.% D:%.XX.XXXX.X.X@X.X.XXXX.XX.% D:%..X&&XX+XXX^X^XXX+XX&&X..% D:%..X&&X9XXX^X^X^XXX9X&&X..% D:%..XXXX8X@X+XXX+X@X8XXXX..% D:%...X9XXXX^^^^^^^XXXX9X...% D:%....XXXXXXX+X+XXXXXXX....% D:%.....XXXXXX,,,XXXXXX.....% D:%......XXXX,,,,,XXXX......% D:%.......XXX,,,,,XXX.......% D:%.........#XXXXX#.........% D:%.........................% D:%%%%%%%%%%%%%%%%%%%%%%%%%%% N:125:X^2 X:7:8:13:15 D:%%%%%%%%%%%%%%% D:%.............% D:%.#####.#####.% D:%.#9,,#.#,,9#.% D:%.##,,#+#,,##.% D:%..###^^^###..% D:%....+^*^+....% D:%..###^^^###..% D:%.##,,#+#,,##.% D:%.#9,,#.#,,9#.% D:%.#####.#####.% D:%.............% D:%%%%%%%%%%%%%%% zangband/lib/edit/t_info.txt0000755000000000000000000015735010250356274015074 0ustar rootroot# File: t_info.txt # Defines the field types. # === Understanding t_info.txt === # N: serial number : field name # G: symbol : color # W: priority : type : counter initial value # I: flag | flag | flag # D: Data[8] # 'N' indicates the beginning of an entry. The serial number must # increase for each new item. It also is used to store the name of # the field. # 'G' is for graphics - symbol and color. There are 16 colors, as # follows: # D - Dark Gray w - White s - Gray o - Orange # r - Red g - Green b - Blue u - Brown # d - Black W - Light Gray v - Violet y - Yellow # R - Light Red G - Light Green B - Light Blue U - Light Brown # 'W' describes extra information about the field. Priority decides # which field is visually "on top". Type is a number which describes # whether the field is a trap, door, building, magic wall etc. # Counter initial value is used for timed effects / effects with counters. # 'I' is a set 'info' flags describing the type of field. These are mostly # used for "quick" recall of information. (Most other things probably can # be stored in the data[] array if required.) # 'D' 8 bytes of data for the field. How this is used depends on the # functions called in the 'F' section below. ##### Something special ##### # Nothing N:0:Nothing G:@:w W:0:0:0 D:0:0:0:0:0:0:0:0 # Invisible Wall N:1:blank G:.:w W:0:4:0 I:FEAT | NO_LOOK | NO_ENTER | NO_MAGIC | NO_OBJECT D:0:0:0:0:0:0:0:0 L:INTERT:action = ACT_TUNNEL L:INTER:if power > 40 + randint0(1600) then L:INTER: msgf("You have finished the tunnel.") L:INTER: deleteme() L:INTER:else L:INTER: msgf("You tunnel into it.") L:INTER:end L:TARGET:wall_gf() # Glyph of Warding N:2:glyph of warding G:;:y W:100:6:0 I:FEAT | VIS | NO_OBJECT | TRANS | NO_MPLACE D:0:0:0:0:0:0:0:0 L:MENTT:local race L:MENTT:race = monst_race(r_idx) L:MENTT:do_turn = TRUE L:MENTT:if (do_move == TRUE) then L:MENTT: if (bAnd(race.flags[0], RF0_NEVER_BLOW) ~= 0) and L:MENTT: (randint1(BREAK_GLYPH) < race.level) then L:MENTT: if visible == TRUE then L:MENTT: msgf("The rune of protection is broken!") L:MENTT: end L:MENTT: deleteme() L:MENTT: else L:MENTT: do_move = FALSE L:MENTT: end L:MENTT:end # Explosive Rune N:3:explosive Rune G:*:R W:100:6:0 I:FEAT | VIS | NO_OBJECT | TRANS | NO_MPLACE D:0:0:0:0:0:0:0:0 L:MENTT:local race L:MENTT:race = monst_race(r_idx) L:MENTT:do_turn = TRUE L:MENTT:if (do_move == TRUE) then L:MENTT: if (bAnd(race.flags[0], RF0_NEVER_BLOW) ~= 0) and L:MENTT: (randint1(BREAK_MINOR_GLYPH) < race.level) then L:MENTT: if (field.fx == player.px) and (field.fy = player.py) then L:MENTT: msgf("The rune explodes!") L:MENTT: fire_ball(GF_MANA, 0, 2 * ((player.lev / 2) + damroll(7, 7)), 2) L:MENTT: else L:MENTT: msgf("An explosive rune was disarmed.") L:MENTT: end L:MENTT: deleteme() L:MENTT: else L:MENTT: do_move = FALSE L:MENTT: end L:MENTT:end # Corpse N:4:corpse G:~:v W:50:7:1000 I:TEMP | VIS | IGNORE D:0:0:0:0:0:0:0:0 L:INIT:corpse_init(r_idx) L:LOAD:local race L:LOAD:race = monst_race(field.data[1] * 256 + field.data[2]) L:LOAD:set_corpse_size(field, corpse_type(race.d_char)) L:SPEC:local race L:SPEC:race = field.data[1] * 256 + field.data[2] L:SPEC:summon_cloned_creature(field.fx, field.fy, race, pet) L:SPEC:deleteme() L:LOOK:corpse_look() L:EXIT:corpse_decay() # Skeleton N:5:skeleton G:~:w W:50:7:1000 I:TEMP | VIS | IGNORE D:0:0:0:0:0:0:0:0 L:INIT:corpse_init(r_idx) L:LOAD:local race L:LOAD:race = monst_race(field.data[1] * 256 + field.data[2]) L:LOAD:set_corpse_size(field, corpse_type(race.d_char)) L:SPEC:local race L:SPEC:race = field.data[1] * 256 + field.data[2] L:SPEC:summon_cloned_creature(field.fx, field.fy, race, pet) L:SPEC:deleteme() L:LOOK:corpse_look() L:EXIT:corpse_decay() # Trapdoor N:6:trap door G:^:w W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(6) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (bAnd(player.flags[2], TR2_FEATHER) ~= 0) then L:PENTER: msgf("You fly over a trap door.") L:PENTER:else L:PENTER: msgf("You have fallen through a trap door!") L:PENTER: sound(SOUND_FALL) L:PENTER: take_hit(damroll(4, 8), "a trap door") L:PENTER: move_dun_level(1) L:PENTER:end # Pit N:7:pit G:^:s W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (bAnd(player.flags[2], TR2_FEATHER) ~= 0) then L:PENTER: msgf("You fly over a pit trap.") L:PENTER:else L:PENTER: msgf("You have fallen into a pit!") L:PENTER: take_hit(damroll(3, 8), "a pit trap") L:PENTER:end # Spiked Pit N:8:spiked pit G:^:s W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:local dam L:PENTER:hit_trap(field) L:PENTER:if (bAnd(player.flags[2], TR2_FEATHER) ~= 0) then L:PENTER: msgf("You fly over a spiked pit.") L:PENTER:else L:PENTER: msgf("You fall into a spiked pit!") L:PENTER: dam = damroll(4, 8) L:PENTER: if (randint0(100) < 50) then L:PENTER: msgf("You are impaled!") L:PENTER: dam = dam * 2 L:PENTER: inc_cut(randint1(dam)) L:PENTER: end L:PENTER: take_hit(dam, "a spiked pit trap") L:PENTER:end # Poison Pit N:9:spiked pit G:^:s W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:local dam L:PENTER:hit_trap(field) L:PENTER:if (bAnd(player.flags[2], TR2_FEATHER) ~= 0) then L:PENTER: msgf("You fly over a spiked pit.") L:PENTER:else L:PENTER: msgf("You fall into a spiked pit!") L:PENTER: dam = damroll(6, 8) L:PENTER: if (randint0(100) < 50) then L:PENTER: msgf("You are impaled on poisonous spikes!") L:PENTER: dam = dam * 2 L:PENTER: inc_cut(randint1(dam)) L:PENTER: if res_pois_lvl() <= 3 then L:PENTER: msgf("The poison does not affect you!") L:PENTER: else L:PENTER: dam = dam * 2 L:PENTER: pois_dam(10, "poison", randint1(dam)) L:PENTER: end L:PENTER: end L:PENTER: take_hit(dam, "a spiked pit trap") L:PENTER:end # evil rune N:10:evil rune G:^:G W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:50:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("There is a flash of shimmering light!") L:PENTER: evil_trap() L:PENTER: deleteme() L:PENTER:end # strange rune N:11:strange rune G:^:o W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:50:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("You hit a teleport trap!") L:PENTER: teleport_player(100) L:PENTER:end # discolored spot N:12:discolored spot G:^:u W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INIT:field.data[3] = randint0(5) L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(8) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (field.data[3] == 0) then L:PENTER: msgf("You are enveloped in flames!") L:PENTER: fire_dam(damroll(4, 6), "a fire trap") L:PENTER:elseif (field.data[3] == 1) then L:PENTER: msgf("You are splashed with acid!") L:PENTER: acid_dam(damroll(4, 6), "an acid trap") L:PENTER:elseif (field.data[3] == 2) then L:PENTER: msgf("A pungent green gas surrounds you!") L:PENTER: pois_dam(10, "poison", rand_range(10, 30)) L:PENTER:elseif (field.data[3] == 3) then L:PENTER: msgf("You are splashed with freezing liquid!") L:PENTER: cold_dam(damroll(4, 6), "a cold trap") L:PENTER:else L:PENTER: msgf("You are hit by a spark!") L:PENTER: elec_dam(damroll(4, 6), "an electric trap") L:PENTER:end # discolored spot N:13:discolored spot G:^:u W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INIT:field.data[3] = randint0(5) L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (field.data[3] == 0) then L:PENTER: msgf("You are enveloped in a ball of flames!") L:PENTER: fire_ball(GF_FIRE, 0, 350, 4) L:PENTER: fire_dam(150, "a fire trap") L:PENTER:elseif (field.data[3] == 1) then L:PENTER: msgf("You are soaked with acid!") L:PENTER: fire_ball(GF_ACID, 0, 350, 4) L:PENTER: acid_dam(150, "an acid trap") L:PENTER:elseif (field.data[3] == 2) then L:PENTER: msgf("A pungent grey gas surrounds you!") L:PENTER: fire_ball(GF_POIS, 0, 350, 4) L:PENTER: pois_dam(10, "poison", rand_range(100, 150)) L:PENTER:elseif (field.data[3] == 3) then L:PENTER: msgf("You are soaked with freezing liquid!") L:PENTER: fire_ball(GF_ICE, 0, 350, 4) L:PENTER: cold_dam(150, "a cold trap") L:PENTER:elseif (field.data[3] == 4) then L:PENTER: msgf("You are hit by lightning!") L:PENTER: fire_ball(GF_ELEC, 0, 350, 4) L:PENTER: elec_dam(150, "a lightning trap") L:PENTER:end # gas trap N:14:gas trap G:^:g W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INIT:field.data[3] = randint0(5) L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (field.data[3] == 0) then L:PENTER: msgf("A blue gas surrounds you!") L:PENTER: inc_slow(rand_range(20, 40)) L:PENTER:elseif (field.data[3] == 1) then L:PENTER: msgf("A black gas surrounds you!") L:PENTER: if (not player_res(TR1_RES_BLIND)) then L:PENTER: inc_blind(rand_range(25, 75)) L:PENTER: end L:PENTER:elseif (field.data[3] == 2) then L:PENTER: msgf("A gas of scintillating colors surrounds you!") L:PENTER: if (not player_res(TR1_RES_CONF)) then L:PENTER: inc_confused(rand_range(10, 30)) L:PENTER: end L:PENTER:elseif (field.data[3] == 3) then L:PENTER: msgf("A strange white mist surrounds you!") L:PENTER: if (not player_res(TR1_FREE_ACT)) then L:PENTER: msgf("You fall asleep.") L:PENTER: if (ironman_nightmare == TRUE) then L:PENTER: msgf("A horrible vision enters your mind.") L:PENTER: have_nightmare() L:PENTER: end L:PENTER: inc_paralyzed(rand_range(5, 15)) L:PENTER: end L:PENTER:elseif (field.data[3] == 4) then L:PENTER: msgf("A gas of scintillating colors surrounds you!") L:PENTER: if (not player_res(TR1_RES_CHAOS)) then L:PENTER: inc_image(rand_range(10, 30)) L:PENTER: end L:PENTER:end # compact rune N:15:compact rune G:^:D W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:50:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("There is a bright flash of light!") L:PENTER: project(0, 1, player.px, player.py, 0, GF_MAKE_TRAP, L:PENTER: bOr(PROJECT_HIDE, bOr(PROJECT_JUMP, PROJECT_GRID))) L:PENTER: deleteme() L:PENTER:end # dart trap N:16:dart trap G:^:r W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INIT:field.data[3] = randint0(3) L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (field.data[3] == 0) then L:PENTER: if (check_trap_hit(125) == TRUE) then L:PENTER: msgf("A small dart hits you!") L:PENTER: take_hit(randint1(4), "a dart trap") L:PENTER: do_dec_stat(A_STR) L:PENTER: else L:PENTER: msgf("A small dart barely misses you.") L:PENTER: end L:PENTER:elseif (field.data[3] == 1) then L:PENTER: if (check_trap_hit(125) == TRUE) then L:PENTER: msgf("A small dart hits you!") L:PENTER: take_hit(randint1(4), "a dart trap") L:PENTER: do_dec_stat(A_DEX) L:PENTER: else L:PENTER: msgf("A small dart barely misses you.") L:PENTER: end L:PENTER:elseif (field.data[3] == 2) then L:PENTER: if (check_trap_hit(125) == TRUE) then L:PENTER: msgf("A small dart hits you!") L:PENTER: take_hit(randint1(4), "a dart trap") L:PENTER: do_dec_stat(A_CON) L:PENTER: else L:PENTER: msgf("A small dart barely misses you.") L:PENTER: end L:PENTER:end # dart trap N:17:dart trap G:^:r W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INIT:field.data[3] = randint0(6) L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(20) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (check_trap_hit(125) == TRUE) then L:PENTER: msgf("A small dart hits you!") L:PENTER: take_hit(randint1(4), "a dart trap") L:PENTER: dec_stat(field.data[3], 30, TRUE) L:PENTER:else L:PENTER: msgf("A small dart barely misses you.") L:PENTER:end # twisted rune N:18:twisted rune G:^:U W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(40) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("Your head throbs!") L:PENTER: lose_exp(player.exp / 5) L:PENTER:end # geometric rune N:19:geometric rune G:^:v W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:100:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: if (not player_res(TR1_RES_DISEN)) then L:PENTER: msgf("There is a bright flash of light!") L:PENTER: apply_disenchant() L:PENTER: else L:PENTER: msgf("You feel the air throb.") L:PENTER: end L:PENTER:end # glowing rune N:20:glowing rune G:^:y W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:100:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("You fumble with your pack!") L:PENTER: drop_random_item() L:PENTER:end # jagged rune N:21:jagged rune G:^:b W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:75:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(15) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: gain_mutation(0) L:PENTER:end # fractured rune N:22:fractured rune G:^:b W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(15) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: lose_mutation() L:PENTER:end # faded rune N:23:faded rune G:^:D W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(2) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("Darkness surrounds you!") L:PENTER: drain_lite() L:PENTER:end # flashing rune N:24:flashing rune G:^:R W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:25:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(2) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("You suddenly feel very, very hungry!") L:PENTER: drain_food() L:PENTER:end # weird rune N:25:weird rune G:^:B W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:75:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(10) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("Your purse becomes weightless!") L:PENTER: player.au = player.au / 2 L:PENTER: player.redraw = bOr(player.redraw, PR_GOLD) L:PENTER: player.window = bOr(player.window, PW_PLAYER) L:PENTER:end # shimmering rune N:26:shimmering rune G:^:b W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("A shrill note sounds!") L:PENTER: speed_monsters() L:PENTER: deleteme() L:PENTER:end # smelly rune N:27:smelly rune G:^:b W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("You smell something musty.") L:PENTER: raise_dead(player.px, player.py, FALSE) L:PENTER:end # intricate rune N:28:intricate rune G:^:v W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:100:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("Static fills the air.") L:PENTER: drain_magic() L:PENTER:end # bright rune N:29:bright rune G:^:W W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:125:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("Shouts fill the air!") L:PENTER: aggravate_monsters(0) L:PENTER:end # blurry rune N:30:blurry rune G:^:b W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:50:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:hit_trap(field) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("Zap!") L:PENTER: summon_specific(0, player.px, player.py, player.depth, L:PENTER: 0, TRUE, FALSE, FALSE) L:PENTER: deleteme() L:PENTER:end # spiral rune N:31:spiral rune G:^:W W:100:1:0 I:FEAT | TRANS | NO_OBJECT | NO_LOOK D:0:75:0:0:0:0:0:0 L:INTERT:action = ACT_DISARM L:INTER:trap_disarm(5) L:TARGET:trap_gf() L:PENTER:disturb(FALSE) L:PENTER:if (player_save(player.depth) ~= TRUE) then L:PENTER: msgf("You are not sure what just happened!") L:PENTER: lose_all_info() L:PENTER:else L:PENTER: hit_trap(field) L:PENTER:end # Locked door N:32:locked door G:+:U W:100:2:0 I:NO_LOOK | MERGE | NO_MPLACE D:0:0:0:0:0:0:0:0 L:INIT:counter_init(32000) L:INTERT:action = ACT_OPEN L:INTER:power = power - field.counter L:INTER:-- Always have a small chance of success L:INTER:if (power < 2) then power = 2 end L:INTER: L:INTER:if (randint0(100) < power) then L:INTER: msgf("The door is unlocked.") L:INTER: -- Open the door L:INTER: cave_set_feat(field.fx, field.fy, FEAT_OPEN) L:INTER: deleteme() L:INTER:else L:INTER: msgf("You failed to unlock the door.") L:INTER: -- We know the door is locked L:INTER: field.info = bOr(field.info, FIELD_INFO_NFT_LOOK) L:INTER: field.info = bAnd(field.info, bNot(FIELD_INFO_NO_LOOK)) L:INTER:end L:TARGET:door_gf() L:MENTT:local race L:MENTT:race = monst_race(r_idx) L:MENTT:if do_move == TRUE then L:MENTT: do_move = FALSE L:MENTT: do_turn = TRUE L:MENTT: if (bAnd(race.flags[1], RF1_OPEN_DOOR) ~= 0) L:MENTT: and (allow_open == TRUE) L:MENTT: and monster_can_open(race, field.counter) == TRUE then L:MENTT: did_open_door = TRUE L:MENTT: deleteme() L:MENTT: end L:MENTT:end # Jammed door N:33:stuck door G:+:U W:100:2:0 I:NO_LOOK | MERGE | NO_MPLACE D:15:1:0:0:0:0:0:0 L:INIT:counter_init(32000) L:INTERT:action = ACT_OPEN L:INTER:if (randint0(power / 10 + adj_str_wgt[player.stat[A_STR].ind] / 2) > field.counter) then L:INTER: msgf("The door crashes open!") L:INTER: if (randint0(100) < 50) then L:INTER: -- Break down the door L:INTER: cave_set_feat(field.fx, field.fy, FEAT_BROKEN) L:INTER: else L:INTER: -- Open the door L:INTER: cave_set_feat(field.fx, field.fy, FEAT_OPEN) L:INTER: end L:INTER: deleteme() L:INTER:else L:INTER: -- We know the door is jammed L:INTER: field.info = bOr(field.info, FIELD_INFO_NFT_LOOK) L:INTER: field.info = bAnd(field.info, bNot(FIELD_INFO_NO_LOOK)) L:INTER:end L:TARGET:door_gf() L:MENTT:local race L:MENTT:race = monst_race(r_idx) L:MENTT:if do_move == TRUE then L:MENTT: do_turn = TRUE L:MENTT: do_move = FALSE L:MENTT: if (bAnd(race.flags[1], RF1_BASH_DOOR) ~= 0) L:MENTT: and (allow_open == TRUE) L:MENTT: and monster_can_open(race, field.counter) == TRUE then L:MENTT: msgf("You hear a door burst open!") L:MENTT: if (disturb_minor == TRUE) then disturb(FALSE) end L:MENTT: did_bash_door = TRUE L:MENTT: -- Fall through opening L:MENTT: do_move = TRUE L:MENTT: deleteme() L:MENTT: end L:MENTT:end # # Stores use the D: line in the following way. # data[0] is an index into the shopkeeper name array. # If this is equal to STORE_HOME, then the store will be # treated like a home in various ways. # data[1] and data[2] represent the range in levels at which # the items in the store are generated at. # data[3] - data[6] are the object theme of those items. By # using an object theme, as well as the restriction functions # many different types of shop can be made. # data[7] contains the result of logical-and'ing the ST_XXX bitflags # together for that store. # # Note L:STORE1 selects objects not in the store. # L:STORE2 selects objects in the store. # # We now use a consistent, and hopefully intuitive, naming scheme # for the advanced stores: # "Advanced" or "Unusual" means deeper items, normal price. # "Expert" means even deeper items, normal price. # "Deep" or "Expensive" means deeper items, double price. # "Custom" or "Rare" means deeper items, quadruple price. # "Arcane" means items enchanted to "good" status. # "Obscure" means deeper items enchanted to "good" status. # "Unique" means items enchanted to "great" status. # General store N:34:General Store G:1:o W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:10:0:50:0:50:0 L:SBINIT:place_sb(rand_range(105, 109), rand_range(2, 10)) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_weapon_armour(object) # Armoury N:35:Armoury G:2:U W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:5:30:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 115), rand_range(5, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_armour(object) # Weapon Smiths N:36:Weapon Smiths G:3:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:5:20:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 115), rand_range(5, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon(object) # Temple N:37:Temple G:6:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:5:20:33:33:34:0:1 L:SBINIT:place_sb(rand_range(107, 110), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Alchemy Shop N:38:Alchemy Shop G:6:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:5:30:0:0:100:0:0 L:SBINIT:place_sb(rand_range(110, 115), rand_range(10, 15) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Magic Wares N:39:Magic Wares G:6:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:20:40:0:0:100:0:0 L:SBINIT:place_sb(rand_range(110, 113), rand_range(15, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_tval(object, TV_POTION) # Black Market N:40:Black Market G:1:D W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:6:20:50:25:25:25:25:16 L:SBINIT:place_sb(150, rand_range(20, 30) * 10) L:PENTER:do_cmd_store(field) # Home N:41:Home G:8:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:7:0:0:100:100:100:100:0 L:SBINIT:place_sb(100, 0) L:PENTER:do_cmd_store(field) # Book Store N:42:Book Store G:5:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:20:40:0:0:100:0:8 L:SBINIT:place_sb(rand_range(105, 110), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_is_book(object) # Weaponmaster N:43:Weaponmaster G:7:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("E) Examine Weapons (".. factor * 3 .."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'E') then L:BUILD2: cost = factor * 3 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: if compare_weapons() == TRUE then L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: message_flush() L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:end # Zymurgist N:44:Zymurgist G:7:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("$CThe price of a recharge depends on the item.", L:BUILD1: "", L:BUILD1: "R) Recharge Items", L:BUILD1: "I) Identify Items (".. factor * 5 .."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'R') then L:BUILD2: building_recharge(factor) L:BUILD2: done = TRUE L:BUILD2: message_flush() L:BUILD2:elseif (command == 'I') then L:BUILD2: cost = factor * 5 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: identify_pack() L:BUILD2: msgf("Your posessions have been identified.") L:BUILD2: message_flush() L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:end # Magesmith (weapon) N:45:Magesmith (weapons) G:7:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("$CBased on your skill, we can improve up to (+".. L:BUILD1: player.lev / 5 .. ",+" .. 5 * (player.lev / 3) .. "%).", L:BUILD1: "", L:BUILD1: "E) Enchant Weapons (".. factor * 5 .."gp)") L:BUILD2:if (command == 'E') then L:BUILD2: enchant_item(factor * 5, TRUE, TRUE, FALSE, TRUE) L:BUILD2: message_flush() L:BUILD2: done = TRUE L:BUILD2:end # Magesmith (armour) N:46:Magesmith (armor) G:7:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("$CBased on your skill, we can improve up to +" .. player.lev / 5 .. ".", L:BUILD1: "", L:BUILD1: "E) Enchant Armour (".. factor * 5 .."gp)") L:BUILD2:if (command == 'E') then L:BUILD2: enchant_item(factor * 5, FALSE, FALSE, TRUE, FALSE) L:BUILD2: message_flush() L:BUILD2: done = TRUE L:BUILD2:end # Mutatalist N:47:Mutatalist G:9:R W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("E) Expose yourself to raw chaos (".. 50 * factor * (count_mutations() + 1).."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'E') then L:BUILD2: cost = 50 * factor * (count_mutations() + 1) L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: if (lose_mutation(0) == TRUE) then L:BUILD2: msgf("You feel oddly normal.") L:BUILD2: else L:BUILD2: gain_mutation(0) L:BUILD2: end L:BUILD2: player.au = player.au - cost L:BUILD2: message_flush() L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:end # Map Maker N:48:Map Maker G:9:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("E) Examine Map (".. factor * 5 .."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'E') then L:BUILD2: cost = factor * 5 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: msgf("You learn of the lay of the lands.") L:BUILD2: map_wilderness(20, player.wilderness_x / 16, player.wilderness_y / 16) L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2: message_flush() L:BUILD2:end # Weapon Smiths N:49:Advanced Weapon Smiths G:3:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:20:50:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon(object) # Weapon Smiths N:50:Expert Weapon Smiths G:3:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:75:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon(object) # Weapon Smiths N:51:Deep Weapon Smiths G:3:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:75:127:0:100:0:0:24 L:SBINIT:place_sb(rand_range(125, 150), rand_range(5, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon(object) # Weapon Smiths N:52:Arcane Weapon Smiths G:3:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:42 L:SBINIT:place_sb(rand_range(125, 150), rand_range(5, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon(object) # Weapon Smiths N:53:Unique Weapon Smiths G:3:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(125, 130), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon(object) # Armoury N:54:Advanced Armoury G:2:U W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:20:50:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_armour(object) # Armoury N:55:Expert Armoury G:2:U W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:75:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_armour(object) # Armoury N:56:Deep Armoury G:2:u W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:75:127:0:100:0:0:24 L:SBINIT:place_sb(rand_range(125, 150), rand_range(5, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_armour(object) # Armoury N:57:Arcane Armoury G:2:u W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:0:100:0:0:42 L:SBINIT:place_sb(rand_range(125, 150), rand_range(5, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_armour(object) # Armoury N:58:Unique Armoury G:2:u W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(125, 150), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_armour(object) # Swordsman N:59:Swordsman G:3:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:1:20:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SWORD) # Swordsman N:60:Advanced Swordsman G:3:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:20:50:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SWORD) # Swordsman N:61:Expert Swordsman G:3:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:40:75:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SWORD) # Swordsman N:62:Deep Swordsman G:3:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:75:127:0:100:0:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SWORD) # Swordsman N:63:Arcane Swordsman G:3:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SWORD) # Swordsman N:64:Unique Swordsman G:3:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SWORD) # Shieldsman N:65:Shieldsman G:2:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:1:30:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SHIELD) # Shieldsman N:66:Advanced Shieldsman G:2:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:20:50:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SHIELD) # Shieldsman N:67:Expert Shieldsman G:2:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:30:75:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SHIELD) # Shieldsman N:68:Obscure Shieldsman G:2:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:75:127:0:100:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SHIELD) # Shieldsman N:69:Arcane Shieldsman G:2:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:0:100:0:0:26 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SHIELD) # Shieldsman N:70:Unique Shieldsman G:2:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SHIELD) # Axeman N:71:Axeman G:3:D W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:1:20:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_nonsword(object) # Axeman N:72:Advanced Axeman G:3:D W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:20:50:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_nonsword(object) # Axeman N:73:Expert Axeman G:3:D W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:75:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_nonsword(object) # Axeman N:74:Deep Axeman G:3:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:75:127:0:100:0:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_nonsword(object) # Axeman N:75:Arcane Axeman G:3:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_nonsword(object) # Axeman N:76:Unique Axeman G:3:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_nonsword(object) # Ammunition N:77:Ammo Supplies G:3:o W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:1:50:0:100:0:0:8 L:SBINIT:place_sb(rand_range(105, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_ammo(object) # Ammunition N:78:Advanced Ammo Supplies G:3:o W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_ammo(object) # Ammunition N:79:Deep Ammo Supplies G:3:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_ammo(object) # Fletcher N:80:Fletcher G:3:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:1:50:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_fletcher(object) # Fletcher N:81:Advanced Fletcher G:3:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_fletcher(object) # Fletcher N:82:Arcane Fletcher G:3:G W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_fletcher(object) # Fletcher N:83:Unique Fletcher G:3:G W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:20:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_fletcher(object) # Warrior Hall N:84:Warrior Hall G:0:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:1:20:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon_armour(object) # Warrior Hall N:85:Advanced Warrior Hall G:0:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:20:50:0:100:0:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon_armour(object) # Warrior Hall N:86:Expert Warrior Hall G:0:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:40:75:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon_armour(object) # Warrior Hall N:87:Deep Warrior Hall G:0:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:75:127:0:100:0:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon_armour(object) # Warrior Hall N:88:Arcane Warrior Hall G:0:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:5:95:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon_armour(object) # Warrior Hall N:89:Unique Warrior Hall G:0:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:2:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_weapon_armour(object) # Clothes Store N:90:Clothes Store G:2:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:1:20:0:100:0:0:8 L:SBINIT:place_sb(rand_range(105, 110), rand_range(5, 10) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_soft_armour(object) # Clothes Store N:91:Expensive Clothes Store G:2:G W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:50:0:100:0:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_soft_armour(object) # Heavy Armoury N:92:Heavy Armoury G:2:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:1:30:0:100:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_pure_hard_armour(object) # Heavy Armoury N:93:Advanced Heavy Armoury G:2:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:20:60:20:80:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_pure_hard_armour(object) # Heavy Armoury N:94:Expert Heavy Armoury G:2:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:75:20:80:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_pure_hard_armour(object) # Heavy Armoury N:95:Arcane Heavy Armoury G:2:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:75:127:20:80:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_pure_hard_armour(object) # Heavy Armoury N:96:Obscure Heavy Armoury G:2:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:20:80:0:0:26 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_pure_hard_armour(object) # Heavy Armoury N:97:Unique Heavy Armoury G:2:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:0:100:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(10, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_pure_hard_armour(object) # Milliner N:98:Milliner G:2:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:1:50:50:50:0:0:8 L:SBINIT:place_sb(rand_range(105, 110), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_helm(object) # Milliner N:99:Advanced Milliner G:2:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:50:100:50:50:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_helm(object) # Milliner N:100:Arcane Milliner G:2:o W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:50:50:0:0:42 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_helm(object) # Milliner N:101:Unique Milliner G:2:o W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:20:100:50:50:0:0:62 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_helm(object) # Jeweler N:102:Jeweler G:5:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:1:30:100:0:0:0:8 L:SBINIT:place_sb(rand_range(120, 130), rand_range(20, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_jewel(object) # Jeweler N:103:Copper Jeweler G:5:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:20:50:100:0:0:0:8 L:SBINIT:place_sb(rand_range(120, 130), rand_range(20, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_jewel(object) # Jeweler N:104:Silver Jeweler G:5:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:40:70:100:0:0:0:24 L:SBINIT:place_sb(rand_range(120, 130), rand_range(20, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_jewel(object) # Jeweler N:105:Gold Jeweler G:5:R W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:60:90:100:0:0:0:40 L:SBINIT:place_sb(rand_range(130, 140), rand_range(20, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_jewel(object) # Jeweler N:106:Rare Jeweler G:5:R W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:80:120:100:0:0:0:56 L:SBINIT:place_sb(rand_range(140, 150), rand_range(20, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_jewel(object) # Statue Store N:107:Statue Store G:5:U W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:1:50:100:0:0:0:8 L:SBINIT:place_sb(rand_range(105, 115), rand_range(5, 10) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_STATUE) # Statue Store N:108:Unusual Statue Store G:5:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:50:100:100:0:0:0:8 L:SBINIT:place_sb(rand_range(105, 115), rand_range(5, 10) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_STATUE) # Figurine Store N:109:Figurine Store G:5:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:26:26:100:0:0:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_FIGURINE) # Figurine Store N:110:Expensive Figurine Store G:5:G W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:50:90:100:0:0:0:16 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_FIGURINE) # Potion Store N:111:Potion Store G:5:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:1:30:0:0:100:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_POTION) # Potion Store N:112:Expensive Potion Store G:5:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:20:40:0:0:100:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_POTION) # Potion Store N:113:Deep Potion Store G:5:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:40:70:0:0:100:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_POTION) # Potion Store N:114:Rare Potion Store G:5:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:60:90:0:0:100:0:40 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_POTION) # Potion Store N:115:Custom Potion Store G:5:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:80:120:0:0:100:0:56 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_POTION) # Scroll Store N:116:Scroll Store G:5:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:1:30:0:0:100:0:0 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SCROLL) # Scroll Store N:117:Unusual Scroll Store G:5:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:20:50:0:0:100:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SCROLL) # Scroll Store N:118:Expensive Scroll Store G:5:W W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:40:70:0:0:100:0:24 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SCROLL) # Scroll Store N:119:Deep Scroll Store G:5:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:60:90:0:0:100:0:40 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SCROLL) # Scroll Store N:120:Rare Scroll Store G:5:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:80:120:0:0:100:0:56 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_SCROLL) # Magic Store N:121:Magic Store G:6:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:1:20:0:0:100:0:8 L:SBINIT:place_sb(rand_range(120, 125), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_recharge(object) # Magic Store N:122:Advanced Magic Store G:6:r W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:20:40:0:0:100:0:8 L:SBINIT:place_sb(rand_range(120, 125), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_recharge(object) # Magic Store N:123:Expensive Magic Store G:6:R W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:40:60:0:0:100:0:24 L:SBINIT:place_sb(rand_range(120, 125), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_recharge(object) # Magic Store N:124:Rare Magic Store G:6:R W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:60:90:0:0:100:0:40 L:SBINIT:place_sb(rand_range(120, 125), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_recharge(object) # Magic Store N:125:Custom Magic Store G:6:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:5:80:120:0:0:100:0:56 L:SBINIT:place_sb(rand_range(120, 125), rand_range(10, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_recharge(object) # Book Store N:126:Rare Book Store G:5:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:20:120:0:0:100:0:56 L:SBINIT:place_sb(rand_range(120, 150), rand_range(20, 30) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_is_book(object) # Temple N:127:Large Temple G:6:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:20:50:33:33:34:0:1 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 20) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Temple N:128:High Temple G:6:G W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:50:75:33:33:34:0:17 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Temple N:129:Hidden Temple G:6:G W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:3:75:100:33:33:34:0:57 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Supplies store N:130:Supplies Store G:1:u W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:9:0:20:40:30:0 L:SBINIT:place_sb(rand_range(105, 110), rand_range(5, 10) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = FALSE L:STORE1:-- This leaves the store with scrolls, tools, ammo, and diggers (by elimination) L:STORE1:if (item_tester_hook_tval(object, TV_POTION) == TRUE) then result = TRUE end L:STORE1:if (item_tester_hook_weapon_armour(object) == TRUE) then result = TRUE end L:STORE1:if (item_tester_hook_recharge(object) == TRUE) then result = TRUE end L:STORE1:if (item_tester_hook_is_book(object) == TRUE) then result = TRUE end # Supplies store N:131:Dungeon Supplies Store G:1:U W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:20:0:10:40:50:0 L:SBINIT:place_sb(rand_range(105, 110), rand_range(5, 10) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = FALSE L:STORE1:-- This leaves the store with scrolls, tools, ammo, and diggers (by elimination) L:STORE1:if (item_tester_hook_tval(object, TV_POTION) == TRUE) then result = TRUE end L:STORE1:if (item_tester_hook_weapon_armour(object) == TRUE) then result = TRUE end L:STORE1:if (item_tester_hook_recharge(object) == TRUE) then result = TRUE end L:STORE1:if (item_tester_hook_is_book(object) == TRUE) then result = TRUE end # Black Market N:132:Large Black Market G:1:s W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:6:40:80:25:25:25:25:32 L:SBINIT:place_sb(150, rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) # Black Market N:133:Hidden Black Market G:1:w W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:6:60:100:25:25:25:25:56 L:SBINIT:place_sb(150, rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) # Alchemy Shop N:134:Advanced Alchemy Shop G:6:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:20:60:0:0:100:0:8 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Alchemy Shop N:135:Rare Alchemy Shop G:6:B W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:4:50:80:0:0:100:0:40 L:SBINIT:place_sb(rand_range(110, 125), rand_range(5, 25) * 10) L:PENTER:do_cmd_store(field) L:STORE1:result = item_tester_hook_recharge(object) # Junk store N:136:Flea Market G:1:g W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:5:30:10:10:50:8 L:SBINIT:place_sb(rand_range(105, 108), rand_range(2, 10) * 10) L:PENTER:do_cmd_store(field) # Food store N:137:Grocer G:0:U W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:0:0:25:0:0:0:100:8 L:SBINIT:place_sb(rand_range(105, 110), rand_range(5, 10) * 10) L:PENTER:do_cmd_store(field) L:STORE2:result = item_tester_hook_tval(object, TV_FOOD) # Library N:138:Library G:9:b W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:6:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("R) Read about monsters (".. 100 * factor .."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'R') then L:BUILD2: cost = 100 * factor L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: if (research_mon() == TRUE) then L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:end # Casino N:139:Casino G:0:o W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:7:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("H) Help", L:BUILD1: "I) In Between", L:BUILD1: "C) Craps", L:BUILD1: "S) Spin the wheel", L:BUILD1: "D) Dice slots") L:BUILD2:if (command == 'H') then L:BUILD2: gamble_help() L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'I') then L:BUILD2: gamble_in_between() L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'C') then L:BUILD2: gamble_craps() L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'S') then L:BUILD2: gamble_spin_wheel() L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'D') then L:BUILD2: gamble_dice_slots() L:BUILD2: done = TRUE L:BUILD2:end # Inn N:140:Inn G:0:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:8:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("E) Eat (".. factor / 20 .."gp)", L:BUILD1: "R) Rest (".. factor / 4 .."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'E') then L:BUILD2: cost = factor / 20 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: msgf("The barkeeper gives you some gruel and a beer.") L:BUILD2: set_food(PY_FOOD_MAX - 1) L:BUILD2: message_flush() L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'R') then L:BUILD2: cost = factor / 4 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: if (inn_rest() == TRUE) then L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:end # Restore stats N:141:Healer G:9:y W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:9:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_options("R) Restore Stats (".. 20 * factor .."gp)") L:BUILD2:local cost L:BUILD2:if (command == 'R') then L:BUILD2: cost = 20 * factor L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: if (building_healer() == TRUE) then L:BUILD2: player.au = player.au - cost L:BUILD2: else L:BUILD2: msgf("You are in fine health already.") L:BUILD2: end L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:end # Black Market N:142:Bazaar G:1:D W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:6:5:30:25:25:25:25:16 L:SBINIT:place_sb(rand_range(105, 110), rand_range(5, 15) * 10) L:PENTER:do_cmd_store(field) # Teleport N:143:Magetower G:9:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_magetower(factor, TRUE) L:BUILD2:local cost L:BUILD2:if (command == 'R') then L:BUILD2: cost = factor * 5 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: record_aura() L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'T') then L:BUILD2: building_magetower(factor, FALSE) L:BUILD2: done = TRUE L:BUILD2:end L:BUILD2:message_flush() # Teleport N:144:Large Magetower G:9:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:building_magetower(factor, TRUE) L:BUILD2:local cost L:BUILD2:if (command == 'R') then L:BUILD2: cost = factor * 5 L:BUILD2: if (test_gold(cost) == TRUE) then L:BUILD2: record_aura() L:BUILD2: player.au = player.au - cost L:BUILD2: end L:BUILD2: done = TRUE L:BUILD2:elseif (command == 'T') then L:BUILD2: building_magetower(factor, FALSE) L:BUILD2: done = TRUE L:BUILD2:end L:BUILD2:message_flush() # Quest-giver N:145:Small Castle G:0:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:if (build_has_quest() == TRUE) then L:BUILD1: building_options("R) Request Reward") L:BUILD1:else L:BUILD1: building_options("R) Request Quest") L:BUILD1:end L:BUILD2:if (command == 'R') then L:BUILD2: build_cmd_quest(10) L:BUILD2: done = TRUE L:BUILD2:end # Quest-giver N:146:Large Castle G:0:v W:100:3:0 I:FEAT | VIS | MARK | NFT_LOOK | NO_OBJECT | PERM D:1:0:0:0:0:0:0:0 L:SBINIT:place_sb(rand_range(90, 125), 0) L:PENTER:do_cmd_bldg(field) L:BUILD1:if (build_has_quest() == TRUE) then L:BUILD1: building_options("R) Request Reward") L:BUILD1:else L:BUILD1: building_options("R) Request Quest") L:BUILD1:end L:BUILD2:if (command == 'R') then L:BUILD2: build_cmd_quest(25) L:BUILD2: done = TRUE L:BUILD2:end zangband/lib/edit/w_info.txt0000755000000000000000000005620010250356274015067 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2002/02/10 12:47:28 $ # File: w_info.txt # Defines the types of 16x16 wilderness blocks. # These blocks are described by a bounds in 256x256x256 parameter space. # The "type" is the number of the function used in the code. # The extra data fields are passed to that function. # Note that at the moment the wilderness "lite" and "none" options # have no effect here. This may change. However, it is most likely # that they will only be referenced in the create_wilderness() function. # 'N' is for number. This is used so that the various "recursive" # wilderness block generation functions can work even when w_info.txt # is edited. (You can leave "gaps" in the list.) # 'M' is for the overhead map - this is the feature to mimic in # f_info.txt Note that the overides in the pref files change the # look of this as well as the normal features. # 'W' describes the bounds in the 3D parameter space. # The first slot is "hgtmin", the second is "hgtmax" - describing # the effects of land height. # The third and fourth slots are "popmin" and "popmax" - describing # the effects of "population denisity" on the type of landscape. # The final two slots are "lawmin" and "lawmax" these separate regions # of "good" and "evil" in the wilderness. # 'T' describes wilderness Type. The first slot is the number of the # function to call to generate the 16x16 wilderness block. # The second slot describes the "chance" field. The higher the number # - the higher the probability of the wild. gen. type being used. # 'F' describes the type of terrain so that the monster generation routines # match monsters with their favourite terrains. # 'E' is a list of eight extra parameters. These parameters are passed to # the function in the 'T' flag. This allows the same function to be used # in different ways. # Testing - only 27 types at the moment. # mudflats N:1 M:10 W:0:43:0:43:0:43 T:2:100 F:SWAMP1 | WASTE1 E:10:1:11:1:84:0:0:0 # mudflats + rock N:2 M:11 W:0:43:0:43:43:85 T:2:100 F:SWAMP1 | WASTE1 E:10:1:131:1:11:1:84:0 # rock + dirt N:3 M:88 W:0:43:0:43:85:128 T:2:100 F:WASTE1 E:131:2:88:4:129:1:8:0 # desert N:4 M:8 W:0:43:0:43:128:171 T:2:100 F:WASTE2 E:8:2:131:1:14:0:0:0 # desert + scrub N:5 M:8 W:0:43:0:43:171:213 T:2:100 F:WASTE1 E:8:1:130:1:89:10:129:0 # scrub N:6 M:89 W:0:43:0:43:213:255 T:2:100 F:WASTE1 E:130:1:89:5:128:2:129:0 # mudflats + sub.tree N:7 M:10 W:0:43:43:85:0:43 T:2:100 F:SWAMP1 | WASTE1 E:10:1:11:1:84:3:95:0 # bayou N:8 M:95 W:0:43:43:85:43:85 T:1:100 F:SWAMP1 | WASTE1 | FOREST1 E:10:50:96:100:84:150:95:255 # wasteland N:9 M:88 W:0:43:43:85:85:128 T:2:100 F:WASTE2 E:88:1:131:3:133:1:129:0 # wasteland N:10 M:88 W:0:43:43:85:128:171 T:2:100 F:WASTE2 E:8:1:131:3:133:1:129:0 # scrub N:11 M:89 W:0:43:43:85:171:213 T:2:100 F:WASTE1 E:130:1:89:5:128:1:129:0 # scrub N:12 M:89 W:0:43:43:85:213:255 T:2:100 F:WASTE1 E:130:1:89:5:128:3:129:0 # bayou N:13 M:95 W:0:43:85:128:0:43 T:1:100 F:SWAMP1 | FOREST1 E:10:50:96:100:84:150:95:255 # forest N:14 M:96 W:0:43:85:128:43:85 T:1:100 F:SWAMP1 | FOREST2 E:128:20:84:150:96:254:95:255 # beach N:15 M:8 W:0:43:85:128:85:128 T:2:100 F:WASTE1 | FOREST1 E:8:2:128:1:14:0:0:0 # beach N:16 M:8 W:0:43:85:128:128:171 T:2:100 F:WASTE1 | FOREST1 E:8:2:128:1:14:5:133:0 # beach N:17 M:8 W:0:43:85:128:171:213 T:2:100 F:WASTE1 | FOREST1 E:8:2:128:1:14:2:96:0 # scrub + grass N:18 M:130 W:0:43:85:128:213:255 T:2:100 F:WASTE1 E:130:1:89:7:128:3:129:0 # bayou + swamp N:19 M:95 W:0:43:128:171:0:43 T:1:100 F:SWAMP1 | FOREST1 E:10:50:137:100:84:150:95:255 # marsh N:20 M:137 W:0:43:128:171:43:85 T:1:100 F:SWAMP1 | FOREST1 E:89:50:137:100:84:150:95:255 # forest N:21 M:96 W:0:43:128:171:85:128 T:1:100 F:FOREST2 E:89:100:128:150:96:254:137:255 # forest N:22 M:96 W:0:43:128:171:128:171 T:1:100 F:FOREST2 E:89:100:128:150:96:254:115:255 # forest N:23 M:96 W:0:43:128:171:171:213 T:1:100 F:FOREST2 E:89:100:128:150:96:254:130:255 # scrub + grass N:24 M:130 W:0:43:128:171:213:255 T:2:100 F:WASTE1 E:89:1:130:10:128:1:129:0 # swamp N:25 M:136 W:0:43:171:213:0:43 T:1:100 F:SWAMP2 E:10:50:95:100:137:200:136:255 # swamp N:26 M:136 W:0:43:171:213:43:85 T:1:100 F:SWAMP2 E:10:50:89:100:137:200:136:255 # marsh N:27 M:137 W:0:43:171:213:85:128 T:1:100 F:SWAMP1 | FOREST1 E:89:50:137:100:96:150:95:255 # forest N:28 M:96 W:0:43:171:213:128:171 T:1:100 F:FOREST2 E:89:100:128:150:96:254:130:255 # grass N:29 M:89 W:0:43:171:213:171:213 T:2:100 E:89:5:128:5:96:1:130:0 # grass N:30 M:89 W:0:43:171:213:213:255 T:2:100 E:89:5:128:5:88:1:96:0 # thick swamp N:31 M:136 W:0:43:213:255:0:43 T:1:100 F:SWAMP2 E:130:50:10:100:136:200:137:255 # swamp N:32 M:136 W:0:43:213:255:43:85 T:1:100 F:SWAMP2 E:10:50:89:100:137:200:136:255 # marsh N:33 M:137 W:0:43:213:255:85:128 T:1:100 F:SWAMP1 | FOREST1 E:89:50:137:100:96:150:95:255 # grass N:34 M:89 W:0:43:213:255:128:171 T:2:100 E:89:5:128:5:88:1:137:0 # dirt N:35 M:88 W:0:43:213:255:171:213 T:2:100 E:88:5:128:5:89:1:14:0 # farm (Need new routine.) N:36 M:88 W:0:43:213:255:213:255 T:4:100 E:0:0:0:0:0:0:0:0 # Leave gap for later additions to first layer... # pond N:37 M:84 W:0:43:128:171:128:171 T:3:1 E:22:84:84:83:0:0:0:0 # pond N:38 M:84 W:0:43:128:171:171:213 T:3:1 E:23:84:84:83:0:0:0:0 # pond N:39 M:84 W:0:43:171:213:128:171 T:3:1 E:28:84:84:83:0:0:0:0 # pond N:40 M:84 W:0:43:171:213:171:213 T:3:1 E:29:84:84:83:0:0:0:0 # mud hole N:41 M:10 W:0:43:213:255:0:43 T:3:6 E:31:10:10:84:0:0:0:0 # mudflats N:50 M:10 W:43:85:0:43:0:43 T:2:100 F:SWAMP1 | WASTE1 E:10:1:11:1:84:2:88:0 # mudflats + rock N:51 M:11 W:43:85:0:43:43:85 T:2:100 F:SWAMP1 | WASTE1 E:10:1:131:1:11:1:84:0 # salt pan N:52 M:9 W:43:85:0:43:85:128 T:2:100 F:SWAMP1 | WASTE1 E:9:1:11:1:8:2:88:0 # desert N:53 M:8 W:43:85:0:43:128:171 T:2:100 F:WASTE2 E:8:2:131:1:14:0:0:0 # desert + scrub N:54 M:8 W:43:85:0:43:171:213 T:2:100 F:WASTE1 E:8:1:130:1:89:10:129:0 # scrub N:55 M:130 W:43:85:0:43:213:255 T:2:100 F:WASTE1 E:130:1:89:5:128:2:129:0 # dirt N:56 M:88 W:43:85:43:85:0:43 T:2:100 F:WASTE1 E:88:5:129:5:89:1:133:0 # dirt N:57 M:88 W:43:85:43:85:43:85 T:2:100 F:WASTE2 E:88:5:129:1:133:2:89:0 # wasteland N:58 M:14 W:43:85:43:85:85:128 T:2:100 F:WASTE1 | WASTE2 E:88:1:131:3:133:1:129:0 # rocky desert N:59 M:14 W:43:85:43:85:128:171 T:1:100 F:WASTE1 | MOUNT1 E:14:150:131:200:8:254:129:255 # scrub N:60 M:130 W:43:85:43:85:171:213 T:2:100 F:WASTE1 E:130:1:89:5:128:1:8:0 # scrub N:61 M:130 W:43:85:43:85:213:255 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:100:1:128:0 # grass N:62 M:89 W:43:85:85:128:0:43 T:2:100 E:89:5:128:5:88:1:84:0 # marsh N:63 M:137 W:43:85:85:128:43:85 T:1:100 F:SWAMP1 | FOREST1 E:89:50:137:100:130:150:95:255 # grass N:64 M:89 W:43:85:85:128:85:128 T:2:100 E:89:5:128:5:88:1:84:0 # scrub N:65 M:130 W:43:85:85:128:128:171 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:100:1:128:0 # pine forest N:66 M:100 W:43:85:85:128:171:213 T:1:100 F:FOREST2 E:89:100:128:150:100:254:130:255 # pine forest N:67 M:100 W:43:85:85:128:213:255 T:1:100 F:FOREST2 E:89:50:128:100:100:254:131:255 # long grass N:68 M:130 W:43:85:128:171:0:43 T:2:100 F:WASTE1 | SWAMP1 E:130:1:89:3:133:1:131:0 # long grass N:69 M:130 W:43:85:128:171:43:85 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:129:1:133:0 # scrub N:70 M:130 W:43:85:128:171:85:128 T:2:100 F:FOREST1 E:130:1:89:3:96:1:128:0 # forest N:71 M:96 W:43:85:128:171:128:171 T:1:100 F:FOREST2 E:89:50:128:100:96:254:130:255 # forest + pine forest N:72 M:100 W:43:85:128:171:171:213 T:1:100 F:FOREST1 | FOREST2 E:100:100:128:150:96:254:130:255 # pine forest N:73 M:100 W:43:85:128:171:213:255 T:1:100 F:FOREST2 E:130:50:128:100:100:254:131:255 # marsh N:74 M:137 W:43:85:171:213:0:43 T:1:100 F:SWAMP1 E:89:50:137:100:130:150:95:255 # marsh N:75 M:137 W:43:85:171:213:43:85 T:1:100 F:SWAMP1 | FOREST1 E:89:50:137:100:130:150:96:255 # forest N:76 M:96 W:43:85:171:213:85:128 T:1:100 F:FOREST1 | FOREST2 E:89:50:128:100:96:250:115:255 # forest N:77 M:96 W:43:85:171:213:128:171 T:1:100 F:FOREST2 E:89:100:128:150:96:254:115:255 # grass N:78 M:89 W:43:85:171:213:171:213 T:2:100 E:89:5:128:1:96:1:130:0 # grass N:79 M:89 W:43:85:171:213:213:255 T:2:100 E:89:5:128:5:96:1:88:0 # swamp N:80 M:136 W:43:85:213:255:0:43 T:1:100 F:SWAMP2 E:10:50:89:100:137:200:136:255 # bayou + swamp N:81 M:137 W:43:85:213:255:43:85 T:1:100 F:SWAMP1 | FOREST1 E:10:50:137:100:115:150:95:255 # jungle N:82 M:115 W:43:85:213:255:85:128 T:1:100 F:FOREST2 | FOREST1 E:89:50:115:100:96:254:130:255 # forest N:83 M:96 W:43:85:213:255:128:171 T:1:100 F:FOREST1 | FOREST2 E:89:50:128:100:96:250:115:255 # grass + forest N:84 M:128 W:43:85:213:255:171:213 T:2:100 F:FOREST1 E:89:2:128:1:96:1:130:0 # farm (Need new routine.) N:85 M:88 W:43:85:213:255:213:255 T:4:100 E:0:0:0:0:0:0:0:0 # Leave gap for later additions to second layer... # pond N:86 M:84 W:43:85:128:171:128:171 T:3:1 E:71:84:84:83:0:0:0:0 # pond N:87 M:84 W:43:85:128:171:171:213 T:3:1 E:72:84:84:83:0:0:0:0 # pond N:88 M:84 W:43:85:171:213:128:171 T:3:1 E:77:84:84:83:0:0:0:0 # pond N:89 M:84 W:43:85:171:213:171:213 T:3:1 E:78:84:84:83:0:0:0:0 # jungle clearing N:90 M:89 W:43:85:213:255:85:128 T:3:6 E:82:89:84:83:0:0:0:0 # dirt N:100 M:88 W:85:128:0:43:0:43 T:2:100 F:WASTE2 E:88:5:129:1:133:2:89:0 # wasteland N:101 M:14 W:85:128:0:43:43:85 T:2:100 F:WASTE1 | WASTE2 E:88:1:131:3:133:1:129:0 # wasteland N:102 M:133 W:85:128:0:43:85:128 T:2:100 F:WASTE1 | WASTE2 E:88:1:8:3:133:1:129:0 # desert + scrub N:103 M:8 W:85:128:0:43:128:171 T:2:100 F:WASTE2 E:8:1:130:1:89:10:129:0 # scrub N:104 M:130 W:85:128:0:43:171:213 T:2:100 F:WASTE1 E:130:1:89:5:128:2:96:0 # grass N:105 M:89 W:85:128:0:43:213:255 T:2:100 E:89:5:128:5:96:1:130:0 # dirt N:106 M:88 W:85:128:43:85:0:43 T:2:100 F:WASTE1 E:88:5:129:1:133:2:89:0 # wasteland N:107 M:14 W:85:128:43:85:43:85 T:2:100 F:WASTE2 E:88:1:130:3:133:1:129:0 # scrub N:108 M:130 W:85:128:43:85:85:128 T:2:100 F:WASTE1 E:130:1:89:5:128:2:96:0 # scrub N:109 M:130 W:85:128:43:85:128:171 T:2:100 F:WASTE1 E:130:1:89:5:8:2:96:0 # grass + forest N:110 M:128 W:85:128:43:85:171:213 T:2:100 F:FOREST1 E:89:2:128:1:96:1:130:0 # grass + pine forest N:111 M:128 W:85:128:43:85:213:255 T:2:100 F:FOREST1 E:89:2:128:1:100:1:130:0 # scrub N:112 M:130 W:85:128:85:128:0:43 T:2:100 F:WASTE1 E:130:1:89:5:88:2:96:0 # scrub N:113 M:130 W:85:128:85:128:43:85 T:2:100 F:WASTE1 E:130:1:89:5:133:2:96:0 # grass N:114 M:89 W:85:128:85:128:85:128 T:2:100 E:89:5:128:5:96:1:130:0 # forest N:115 M:96 W:85:128:85:128:128:171 T:1:100 F:FOREST2 E:89:100:128:150:96:254:100:255 # snow covered forest N:116 M:101 W:85:128:85:128:171:213 T:1:100 F:FOREST2 E:135:100:100:150:101:254:132:255 # pine forest N:117 M:100 W:85:128:85:128:213:255 T:1:100 F:FOREST2 E:130:50:128:100:100:254:131:255 # forest N:118 M:96 W:85:128:128:171:0:43 T:1:100 F:FOREST2 E:89:100:128:150:96:254:130:255 # long grass N:119 M:130 W:85:128:128:171:43:85 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:129:1:133:0 # scrub N:120 M:130 W:85:128:128:171:85:128 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:5:133:2:96:0 # forest N:121 M:96 W:85:128:128:171:128:171 T:1:100 F:FOREST1 | FOREST2 E:89:50:128:100:96:250:100:255 # pine forest N:122 M:100 W:85:128:128:171:171:213 T:1:100 F:FOREST2 E:135:50:101:100:100:254:131:255 # pine forest N:123 M:100 W:85:128:128:171:213:255 T:1:100 F:FOREST1 E:89:50:128:100:100:254:131:255 # marsh N:124 M:137 W:85:128:171:213:0:43 T:1:100 F:SWAMP1 | FOREST1 E:89:50:137:100:130:150:96:255 # forest N:125 M:96 W:85:128:171:213:43:85 T:1:100 F:FOREST1 | FOREST2 E:89:50:128:100:96:250:115:255 # forest N:126 M:96 W:85:128:171:213:85:128 T:1:100 F:FOREST2 E:130:100:128:150:96:250:115:255 # forest N:127 M:96 W:85:128:171:213:128:171 T:1:100 F:FOREST1 | FOREST2 E:89:50:128:100:96:250:100:255 # forest N:128 M:96 W:85:128:171:213:171:213 T:1:100 F:FOREST1 | FOREST2 E:130:50:128:100:96:250:100:255 # farm (Need new routine.) N:129 M:89 W:85:128:171:213:213:255 T:4:100 E:0:0:0:0:0:0:0:0 # marsh N:130 M:137 W:85:128:213:255:0:43 T:1:100 F:SWAMP2 | FOREST2 E:115:50:137:100:130:150:96:255 # jungle N:131 M:115 W:85:128:213:255:43:85 T:1:100 F:FOREST2 | FOREST1 E:137:50:115:100:96:254:130:255 # jungle N:132 M:115 W:85:128:213:255:85:128 T:1:100 F:FOREST2 | FOREST1 E:128:50:115:100:96:254:130:255 # forest N:133 M:96 W:85:128:213:255:128:171 T:1:100 F:FOREST2 E:89:50:128:100:96:250:115:255 # scrub N:134 M:130 W:85:128:213:255:171:213 T:2:100 F:FOREST1 E:130:1:89:3:96:1:128:0 # grass N:135 M:89 W:85:128:213:255:213:255 T:2:100 E:89:5:128:5:88:1:14:0 # Leave gap for later additions to third layer... # volcanic region N:150 M:15 W:128:171:0:43:0:43 T:1:100 F:MOUNT2 | WASTE2 E:15:100:14:150:133:250:97:255 # wasteland N:151 M:14 W:128:171:0:43:43:85 T:2:100 F:WASTE1 | WASTE2 E:88:1:97:3:133:1:129:0 # wasteland N:152 M:14 W:128:171:0:43:85:128 T:2:100 F:WASTE1 | WASTE2 E:88:1:97:3:130:1:129:0 # dirt N:153 M:88 W:128:171:0:43:128:171 T:2:100 F:WASTE2 E:88:5:129:1:130:2:89:0 # scrub N:154 M:130 W:128:171:0:43:171:213 T:2:100 F:WASTE1 | MOUNT1 E:130:1:89:5:14:2:96:0 # mountains N:155 M:97 W:128:171:0:43:213:255 T:1:100 F:MOUNT2 E:132:100:97:200:98:220:101:255 # wasteland N:156 M:14 W:128:171:43:85:0:43 T:2:100 F:WASTE2 E:88:1:97:3:133:1:129:0 # wasteland N:157 M:14 W:128:171:43:85:43:85 T:2:100 F:WASTE2 E:88:1:97:3:130:1:129:0 # scrub N:158 M:130 W:128:171:43:85:85:128 T:2:100 F:WASTE1 E:130:1:89:5:133:2:96:0 # scrub N:159 M:130 W:128:171:43:85:128:171 T:2:100 F:WASTE1 E:130:1:89:5:88:2:133:0 # scrub + snow N:160 M:134 W:128:171:43:85:171:213 T:2:100 F:WASTE1 E:130:1:135:5:101:2:134:0 # snow N:161 M:135 W:128:171:43:85:213:255 T:2:100 F:WASTE1 E:135:1:132:5:88:2:101:0 # scrub N:162 M:130 W:128:171:85:128:0:43 T:2:100 F:WASTE1 E:130:1:89:5:133:2:129:0 # long grass N:163 M:130 W:128:171:85:128:43:85 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:129:1:133:0 # grass N:164 M:89 W:128:171:85:128:85:128 T:2:100 E:89:5:128:5:88:1:130:0 # forest N:165 M:96 W:128:171:85:128:128:171 T:1:100 F:FOREST1 E:89:50:128:100:96:250:100:255 # snow covered forest N:166 M:101 W:128:171:85:128:171:213 T:1:100 F:FOREST2 E:135:100:100:150:101:254:130:255 # snow covered forest N:167 M:101 W:128:171:85:128:213:255 T:1:100 F:FOREST2 E:135:100:100:150:101:254:132:255 # forest N:168 M:96 W:128:171:128:171:0:43 T:1:100 F:FOREST2 E:89:50:128:100:96:250:130:255 # long grass N:169 M:130 W:128:171:128:171:43:85 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:96:1:133:0 # long grass N:170 M:130 W:128:171:128:171:85:128 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:3:96:1:133:0 # forest N:171 M:96 W:128:171:128:171:128:171 T:1:100 F:FOREST2 E:89:50:128:100:96:250:100:255 # pine forest N:172 M:100 W:128:171:128:171:171:213 T:1:100 F:FOREST2 E:130:50:128:100:100:254:131:255 # pine forest N:173 M:100 W:128:171:128:171:213:255 T:1:100 F:FOREST2 E:130:50:128:100:100:254:88:255 # forest N:174 M:96 W:128:171:171:213:0:43 T:1:100 F:FOREST1 | FOREST2 E:89:50:128:100:96:250:115:255 # forest N:175 M:96 W:128:171:171:213:43:85 T:1:100 F:FOREST2 E:130:50:128:100:96:250:115:255 # forest N:176 M:96 W:128:171:171:213:85:128 T:1:100 F:FOREST2 E:89:50:128:100:96:250:130:255 # forest N:177 M:96 W:128:171:171:213:128:171 T:1:100 F:FOREST2 E:89:50:128:100:96:250:131:255 # dirt N:178 M:88 W:128:171:171:213:171:213 T:2:100 E:88:5:128:5:89:1:14:0 # farm (Need new routine.) N:179 M:88 W:128:171:171:213:213:255 T:4:100 E:0:0:0:0:0:0:0:0 # jungle N:180 M:115 W:128:171:213:255:0:43 T:1:100 F:FOREST2 | FOREST1 E:89:50:115:100:96:254:130:255 # jungle N:181 M:115 W:128:171:213:255:43:85 T:1:100 F:FOREST2 | FOREST1 E:89:50:115:100:96:254:130:255 # forest N:182 M:96 W:128:171:213:255:85:128 T:1:100 F:FOREST2 E:89:50:128:100:96:250:115:255 # grass N:183 M:89 W:128:171:213:255:128:171 T:2:100 E:89:5:128:5:96:1:130:0 # grass N:184 M:89 W:128:171:213:255:171:213 T:2:100 E:89:5:128:5:88:1:96:0 # grass N:185 M:89 W:128:171:213:255:213:255 T:2:100 E:89:5:128:5:88:1:130:0 # Leave gap for later additions to fourth layer... # volcanic region N:200 M:15 W:171:213:0:43:0:43 T:1:100 F:MOUNT1 | MOUNT2 | WASTE1 | WASTE2 E:15:100:14:150:131:250:97:255 # volcanic region N:201 M:15 W:171:213:0:43:43:85 T:1:100 F:MOUNT2 | WASTE2 E:15:100:14:150:133:250:97:255 # badlands N:202 M:14 W:171:213:0:43:85:128 T:1:100 F:MOUNT2 | WASTE2 E:14:100:11:150:94:200:133:255 # wasteland N:203 M:14 W:171:213:0:43:128:171 T:2:100 F:WASTE1 | WASTE2 E:88:1:133:3:130:1:115:0 # pine forest N:204 M:100 W:171:213:0:43:171:213 T:1:100 F:FOREST2 E:130:50:128:100:100:254:97:255 # mountains N:205 M:97 W:171:213:0:43:213:255 T:1:100 F:MOUNT2 E:132:100:97:200:98:220:101:255 # mountains N:206 M:97 W:171:213:43:85:0:43 T:1:100 F:MOUNT2 E:132:50:134:100:98:200:15:255 # wasteland N:207 M:14 W:171:213:43:85:43:85 T:2:100 F:WASTE2 E:88:1:133:3:130:1:97:0 # wasteland N:208 M:14 W:171:213:43:85:85:128 T:2:100 F:WASTE2 E:88:1:133:3:130:1:11:0 # wasteland N:209 M:14 W:171:213:43:85:128:171 T:2:100 F:WASTE1 E:88:1:133:3:130:1:100:0 # pine forest N:210 M:100 W:171:213:43:85:171:213 T:1:100 F:WASTE1 | MOUNT1 | FOREST2 E:130:50:128:100:100:254:97:255 # mountains N:211 M:97 W:171:213:43:85:213:255 T:1:100 F:MOUNT2 | MOUNT1 E:132:50:134:100:98:200:101:255 # badlands N:212 M:14 W:171:213:85:128:0:43 T:1:100 F:MOUNT1 | WASTE2 E:14:100:11:150:94:200:133:255 # wasteland N:213 M:14 W:171:213:85:128:43:85 T:2:100 F:WASTE2 E:88:1:133:3:130:1:11:0 # scrub N:214 M:130 W:171:213:85:128:85:128 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:5:133:2:100:0 # pine forest N:215 M:100 W:171:213:85:128:128:171 T:1:100 F:FOREST2 E:130:50:128:100:100:254:135:255 # snow covered forest N:216 M:101 W:171:213:85:128:171:213 T:1:100 F:FOREST2 | MOUNT1 E:135:100:100:150:101:254:132:255 # mountains N:217 M:97 W:171:213:85:128:213:255 T:1:100 F:MOUNT2 E:132:50:134:100:98:200:101:255 # scrub N:218 M:130 W:171:213:128:171:0:43 T:2:100 F:WASTE1 | FOREST1 E:11:1:130:5:133:2:96:0 # scrub N:219 M:130 W:171:213:128:171:43:85 T:2:100 F:WASTE1 | FOREST1 E:130:1:129:5:133:2:96:0 # grass N:220 M:89 W:171:213:128:171:85:128 T:2:100 F:FOREST1 E:89:5:128:5:96:1:130:0 # forest N:221 M:96 W:171:213:128:171:128:171 T:1:100 F:FOREST2 E:89:100:128:150:96:254:130:255 # pine forest N:222 M:100 W:171:213:128:171:171:213 T:1:100 F:FOREST2 E:130:50:128:100:100:254:131:255 # pine forest N:223 M:100 W:171:213:128:171:213:255 T:1:100 F:FOREST2 E:101:50:128:100:100:254:131:255 # forest N:224 M:96 W:171:213:171:213:0:43 T:1:100 F:FOREST2 E:89:50:128:100:96:250:130:255 # forest N:225 M:96 W:171:213:171:213:43:85 T:1:100 F:FOREST2 E:130:50:128:100:96:250:115:255 # pine forest N:226 M:100 W:171:213:171:213:85:128 T:1:100 F:FOREST2 E:130:50:128:100:100:254:89:255 # pine forest N:227 M:100 W:171:213:171:213:128:171 T:1:100 F:FOREST2 E:130:50:128:100:100:254:135:255 # dirt N:228 M:88 W:171:213:171:213:171:213 T:2:100 E:88:5:128:5:89:1:14:0 # farm (Need new routine.) N:229 M:88 W:171:213:171:213:213:255 T:4:100 E:0:0:0:0:0:0:0:0 # forest N:230 M:96 W:171:213:213:255:0:43 T:1:100 F:FOREST1 | FOREST2 E:89:100:128:150:96:254:115:255 # forest N:231 M:96 W:171:213:213:255:43:85 T:1:100 F:FOREST1 | FOREST2 E:100:100:128:150:96:254:115:255 # pine forest N:232 M:100 W:171:213:213:255:85:128 T:1:100 F:FOREST2 E:130:50:128:100:100:254:131:255 # snow covered forest N:233 M:101 W:171:213:213:255:128:171 T:1:100 F:FOREST2 E:135:100:100:150:101:254:132:255 # snow N:234 M:135 W:171:213:213:255:171:213 T:2:100 E:135:1:132:5:88:2:101:0 # dirt N:235 M:88 W:171:213:213:255:213:255 T:2:100 E:88:5:128:5:89:1:135:0 # Leave gap for later additions to fifth layer... # Acid Pool N:236 M:94 W:171:213:0:43:85:128 T:3:6 E:202:94:94:93:0:0:0:0 # Acid Pool N:237 M:94 W:171:213:85:128:0:43 T:3:6 E:212:94:94:93:0:0:0:0 # Lava Pool N:238 M:86 W:171:213:0:43:0:43 T:3:6 E:200:86:86:85:0:0:0:0 # volcanic region N:250 M:15 W:213:255:0:43:0:43 T:1:100 F:MOUNT1 | MOUNT2 | WASTE1 | WASTE2 E:15:100:14:150:131:250:97:255 # volcanic region N:251 M:15 W:213:255:0:43:43:85 T:1:100 F:MOUNT2 | MOUNT1 | WASTE2 E:15:100:14:150:133:250:97:255 # badlands N:252 M:14 W:213:255:0:43:85:128 T:1:100 F:MOUNT2 | WASTE2 E:14:100:11:150:94:200:133:255 # wasteland N:253 M:14 W:213:255:0:43:128:171 T:2:100 F:MOUNT1 | WASTE2 E:130:1:11:5:88:2:97:0 # mountains N:254 M:97 W:213:255:0:43:171:213 T:1:100 F:MOUNT2 E:132:100:97:200:98:220:101:255 # mountains N:255 M:98 W:213:255:0:43:213:255 T:1:100 F:MOUNT2 E:132:50:134:100:98:200:101:255 # mountains N:256 M:97 W:213:255:43:85:0:43 T:1:100 F:MOUNT2 E:132:50:134:100:98:200:15:255 # wasteland N:257 M:14 W:213:255:43:85:43:85 T:2:100 F:WASTE2 E:88:1:133:3:130:1:97:0 # wasteland N:258 M:14 W:213:255:43:85:85:128 T:2:100 F:WASTE2 E:88:1:133:3:130:1:11:0 # scrub N:259 M:130 W:213:255:43:85:128:171 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:5:133:2:100:0 # pine forest N:260 M:100 W:213:255:43:85:171:213 T:1:100 F:FOREST2 E:130:50:128:100:100:254:135:255 # mountains N:261 M:98 W:213:255:43:85:213:255 T:1:100 F:MOUNT2 E:132:50:134:100:98:200:101:255 # badlands N:262 M:14 W:213:255:85:128:0:43 T:1:100 F:MOUNT2 | WASTE2 E:14:100:11:150:94:200:130:255 # wasteland N:263 M:88 W:213:255:85:128:43:85 T:2:100 F:WASTE2 E:88:1:133:3:130:1:11:0 # scrub N:264 M:130 W:213:255:85:128:85:128 T:2:100 F:WASTE1 | FOREST1 E:130:1:89:5:133:2:100:0 # grass N:265 M:89 W:213:255:85:128:128:171 T:2:100 E:89:5:128:5:96:1:130:0 # pine forest N:266 M:100 W:213:255:85:128:171:213 T:1:100 F:FOREST2 E:89:100:128:150:100:254:130:255 # mountains N:267 M:98 W:213:255:85:128:213:255 T:1:100 F:MOUNT1 | MOUNT2 E:132:50:134:100:98:200:101:255 # scrub N:268 M:130 W:213:255:128:171:0:43 T:2:100 F:WASTE1 | FOREST1 E:11:1:130:5:133:2:96:0 # scrub N:269 M:130 W:213:255:128:171:43:85 T:2:100 F:WASTE1 | FOREST1 E:130:1:129:5:133:2:96:0 # grass N:270 M:89 W:213:255:128:171:85:128 T:2:100 F:FOREST1 E:89:5:128:5:96:1:130:0 # grass N:271 M:89 W:213:255:128:171:128:171 T:2:100 F:FOREST1 E:89:5:128:5:96:1:130:0 # snow covered forest N:272 M:101 W:213:255:128:171:171:213 T:1:100 F:FOREST2 E:135:100:100:150:101:254:132:255 # snow covered forest N:273 M:101 W:213:255:128:171:213:255 T:1:100 F:FOREST2 E:135:100:100:150:101:254:132:255 # grass N:274 M:89 W:213:255:171:213:0:43 T:2:100 F:FOREST1 E:89:5:128:5:100:1:130:0 # grass N:275 M:89 W:213:255:171:213:43:85 T:2:100 F:FOREST1 E:89:5:128:5:100:1:130:0 # grass N:276 M:89 W:213:255:171:213:85:128 T:2:100 F:FOREST1 E:89:5:128:5:100:1:130:0 # pine forest N:277 M:100 W:213:255:171:213:128:171 T:1:100 F:FOREST2 E:89:100:128:150:100:254:130:255 # snow N:278 M:135 W:213:255:171:213:171:213 T:2:100 F:WASTE1 E:135:1:132:5:88:2:101:0 # snow N:279 M:135 W:213:255:171:213:213:255 T:2:100 F:WASTE1 E:135:1:132:5:88:2:101:0 # pine forest N:280 M:100 W:213:255:213:255:0:43 T:1:100 F:FOREST2 E:89:100:96:150:100:254:130:255 # pine forest N:281 M:100 W:213:255:213:255:43:85 T:1:100 F:FOREST2 E:89:100:96:150:100:254:130:255 # pine forest N:282 M:100 W:213:255:213:255:85:128 T:1:100 F:FOREST2 E:89:100:101:150:100:254:130:255 # tundra N:283 M:101 W:213:255:213:255:128:171 T:1:100 F:MOUNT1 | FOREST1 | WASTE1 E:135:100:132:150:101:220:134:255 # tundra N:284 M:101 W:213:255:213:255:171:213 T:1:100 F:MOUNT1 | FOREST1 | WASTE1 E:135:100:132:150:101:220:134:255 # dirt N:285 M:88 W:213:255:213:255:213:255 T:2:100 E:88:5:135:5:132:1:14:0 # Extra features..... # Acid Pool N:286 M:94 W:213:255:0:43:85:128 T:3:6 E:262:94:94:93:0:0:0:0 # Acid Pool N:287 M:94 W:213:255:85:128:0:43 T:3:6 E:252:94:94:93:0:0:0:0 # Lava Pool N:288 M:86 W:213:255:0:43:0:43 T:3:6 E:250:86:86:85:0:0:0:0 zangband/lib/edit/makefile.zb0000644000000000000000000000041510250356274015151 0ustar rootrootsubdir = ./lib/edit/ ## makefile.zb srcfiles += lib/edit/makefile.zb files += \ lib/edit/a_info.txt lib/edit/e_info.txt lib/edit/f_info.txt\ lib/edit/k_info.txt lib/edit/misc.txt lib/edit/r_info.txt\ lib/edit/v_info.txt lib/edit/t_info.txt lib/edit/w_info.txt zangband/lib/file/0000755000000000000000000000000010250356274013031 5ustar rootrootzangband/lib/file/a_low.txt0000755000000000000000000000211010250356274014670 0ustar rootrootN:*:Default 85 'Ehr Rhee' of Eastwood 'Zir Dawn' 'Lor Goth' of the Foo of Pobox of Demos of Talos of Evasion of Traveling of Balmoth of the Uncaring of Zod of Glenda 'Wind Shield' 'Van's Shield' of Lesser Shielding 'Dasmiff' 'Pizza' 'Whisperfeet' of Shifting 'Curator' 'Astrosuit' 'Combat Enviro-Suit' 'Bubble' of Clay 'Force Shield' of Glass of Illusion of Phantasm of the Jester of the Joker of the Mirror Image of Misery of the Unseen of the Scarecrow of Sand 'Sad Giant's Shield' 'Weakstone' of Halloween 'Blank' 'Sheet' 'Wind Fan' of Humility of Passion of the Humpback of Bobble 'Midnight Resistance' of Zombi 'Kid Gloves' 'Babel' 'Grand Prix' 'Krypton Egg' 'Mindbender' 'Goonie' 'Risk' 'Iron Maiden' 'Generator' of Transylvania 'Zaxxon' 'Wapiti' 'Deja-Vu' of the Apparition of the Wormhole of Holy Smoke of the Masochist of King Nothing of the Stuntman of the Kemma of the Keva 'Full Metal Hand' 'Wooden Box' of Blind Justice of the Mortals of Compactness of Proximity of Satisfaction of Gurnemanz of Tannhaeuser of Martin of Coral of Barahir 'Dal-i-Thalion' 'Paurnimmen' of Hide-and-Seek zangband/lib/file/crime.txt0000755000000000000000000000147210250356274014700 0ustar rootrootN:53:Grip, Farmer Maggot's dog N:54:Wolf, Farmer Maggot's dog N:55:Fang, Farmer Maggot's dog 0 N:*:Default 38 murder. arson. rape. assault and battery. theft. extortion. rape, assault and battery. embezzlement. embezzlement and theft. theft and murder. kidnapping. stealing. fraud. fraud and embezzlement. counterfeiting. smuggling. horse theft. shoplifting. treason. pillaging. rape and pillage. cruelty to animals. dealing drugs. murder for hire. luring souls to destruction. smashing holy relics. glorying in chaos. convenanting with Morgoth. desecration of holy places. conspiring to topple bastions. abuse of self-appointed privilege. leaguing with the underworld. spawning minions of darkness. the desolation of kingdoms. lese-majesty. obstructing justice. conspiring to obstruct justice. assault with a deadly weapon. zangband/lib/file/elvish.txt0000755000000000000000000000166310250356274015075 0ustar rootrootN:*:Default 216 adan ael in agl ar aina alda al qua am arth amon anca an dune anga anna ann on ar ien atar band bar ad bel eg brag ol breth il brith cal en gal en cam car ak cel eb cor on cu cui vie cul curu dae dag or del din dol dor draug du duin dur ear ech or edh el eith elen er ereg es gal fal as far oth faug fea fin for men fuin gaer gaur gil gir ith glin gol odh gond gor groth grod gul gurth gwaith gwath wath had hod haudh heru him hini hith hoth hyar men ia iant iath iaur ilm iluve kal gal kano kel kemen khel ek khil kir lad laure lhach lin lith lok lom lome londe los loth luin maeg mal man mel men menel mer eth min as mir mith mor moth nan nar naug dil dur nel dor nen nim orn orod os pal an pel quen quet ram ran rant ras rauko ril rim ring ris roch rom rond ros ruin ruth sarn ser eg sil sir sul tal dal tal ath tar tath ar taur tel thal thang thar thaur thin thol thon thor on til tin tir tol tum tur uial ur val wen wing yave zangband/lib/file/monfear.txt0000755000000000000000000003146610250356274015236 0ustar rootroot# This is the file for allowing uniques to speak their "own" lines # when in fear. # # Deleting this file will have no real effect on the game. Modifying it may # cause STRANGE unique lines to come up if the format's wrong, but shouldn't # crash anything. The format goes like so: # # N:45:whoever this is # 3 # says line 1 # says line 2 # says line 3 # # The number after the N: is the "monster index number" obtained from # r_info.txt. The text field after that number isn't actually used--it's # just there to help humans edit the file. The numbers on lines by # themselves are the number of lines that the monster has. Getting these # numbers wrong won't crash anything, but will produce strange lines. # # A '*' in the number field is a wildcard and applies to any monster. # It should only be placed at the end of the file, since all 'N:' lines # after it will be ignored. # # Two or more monsters can share lines; just put their N: lines in a # contiguous block. # # To stop a certain monster from having unique lines, put a # in front of # its N: line. # # Entries by Eric Bock, Matt Graham, Andrew Hill, and Topi Ylinen N:8:Farmer Maggot 2 screams, 'Don't hurt a poor helpless hobbit!' yells, 'Where are my vicious dogs when I need them?' N:19:Martti Ihrasaari 2 yells, 'I'm a head of state! You can't *do* this!' yells, 'Help! Psycho on the loose!' N:53:Grip, Farmer Maggot's dog N:54:Wolf, Farmer Maggot's dog N:55:Fang, Farmer Maggot's dog 5 whimpers. pines. limps away, howling. howls. looks at you sadly. N:63:Smeagol 5 says, 'Don't hurt us, mastersisis.' says, 'Poor Smeagol, poor Smeagol.' #LOTR Book6 Chap.3 cries, 'Don't hurt us with nassty cruel steel!' #LOTR Book6 Chap.3 begs, 'Let us live, yes, live just a little longer!' #LOTR Book4 Chap.6 says, 'A little sneak, and you say death! So just you are!' N:135:Mughash the Kobold Lord 2 screams, 'Cowards! Why did you abandon me?' begs for mercy. N:137:Wormtongue, Agent of Saruman 3 begs you to spare his miserable life. whines, 'This is not my fault!' screams, 'Help! Help!' N:138:Robin Hood, the Outlaw 3 begs you to spare his life. says, 'But I'm a GOOD guy, really!' says, 'Money? Sure, take it all back!' #N:169:Brodda, the Easterling #N:291:Ulfast, Son of Ulfang N:180:Orfax, Son of Boldor N:237:Boldor, King of the Yeeks 2 sobs, 'I didn't MEAN it...' whimpers and moans. N:200:Hobbes the Tiger 1 yells, 'Ow! Get me back to the comics!' N:140:Lagduf, the Snaga N:186:Grishnakh, the Hill Orc N:215:Golfimbul, the Hill Orc Chief N:260:Ufthak of Cirith Ungol N:285:Orc captain N:314:Shagrat, the Orc Captain N:315:Gorbag, the Orc Captain N:330:Bolg, Son of Azog N:350:Ugluk, the Uruk N:356:Lugdush, the Uruk N:373:Azog, King of the Uruk-Hai 6 screams, 'Hey, orcs have rights too!' says, 'You're just prejudiced against orc-kind, aren't you?' begs, 'Spare me and I'll get you Ringil! Really!' says, 'Next time, I'm bringing more Uruks with me!' says, 'Don't hate me because I'm ugly!' whimpers and grovels. N:382:Mime, the Nibelung 8 sobs. sobs and whines. screams, 'Ohe! Ohe! Au! Au!' pleads, 'Let me go!' wails, 'Au! Au! Au!' says, 'I was so good to you, and this is my reward?' moans, 'Such ingratitude!' says, 'Go now, on your way!' N:383:Hagen, son of Alberich 3 shouts, 'Vassals, rouse yourselves! Take your weapons, good strong weapons!' shouts, 'There is danger! Danger!' cries, 'Woe! Woe!' N:419:Alberich the Nibelung King 11 screams, 'Help! Murder! Murder!' screams, 'Aaah! Crushed! Shattered!' moans, 'Base trickery, foul deceit!' pleads, 'I have paid, now let me depart!' cries, 'O shameful humiliation!' shouts, 'Rascally rogue! Robber! Ruffian!' grumbles, 'You will regret this outrage, you wretch!' moans, 'Terrible vengeance I vow for this wrong!' says, 'Smile now, but you can never escape my curse!' wails, 'Alas! Alas! Woe is me!' moans, 'Do you mock me?' #N:392:Sangahyando of Umbar #N:380:Angamaite of Umbar #This next may be unnecessarily evil... :-] N:393:It 3 howls, 'I'll be back!' whimpers, 'They said this invisibility thing was better than it is!' teleports away. N:409:Kharis the Powerslave 3 howls, 'Nnnnooo!' says, 'I don't want to die, I'm a god, why can't I live on?' curses you. #N:413:Ulwarth, Son of Ulfang #Grendel's fear line makes sense if you've read "Beowulf"... N:431:Grendel 1 whines, 'Mommy, save me!' N:441:Barney the Dinosaur 3 begs, 'Don't! Think of the children!' screams, 'But I'm a big TV star!' sobs, 'All right! I apologize! I really really do!' #N:489:Bokrug N:493:Bert the Stone Troll N:494:Bill the Stone Troll N:495:Tom the Stone Troll N:551:Rogrog the Black Troll 3 says, 'Now, stop it!' yells, 'Ey, watch it, you cheeky sod!' screams, 'Me mates'll settle yer hash!' N:505:Groo the Wanderer 1 says, 'Oops... me get in big trouble!' # I have no idea what these next 4 should say, so.... --MG N:506:Fasolt the Giant 3 cries: 'Take my life, but not my gold!' complains: 'Why do you rush at me? I sought justice, my just payment!' whines: 'Why do you threaten me?' #N:517:Jurt the Living Trump #N:573:Lord Borel of Hendrake #N:598:Mandor, Master of the Logrus N:595:Father Dagon N:596:Mother Hydra 1 sobs, 'No! I'm an endangered aquatic species!' N:606:Loge, Spirit of Fire 1 pants and gasps. N:615:Moire, Queen of Rebma 1 wails, 'Help! Murder! Murder!' N:616:Kavlax the Many-Headed 2 says, 'This is YOUR fault!' and bites itself. blames its problems on the head you've managed to kill. N:628:Malekith the Accursed 2 says, 'C'mon! I'm sure we can work this out...' pleads for his miserable life. N:642:Jasra, Brand's Mistress 1 hisses, 'We don't die, we multiply!' #N:651:Strygalldwir N:660:Rinaldo, son of Brand N:670:Jack of Shadows 2 screams, 'Not the face! Not the face!' says, 'Yikes! Where'd I put my mail-order Cyberdemon?' #N:681:Chaugnar Faugn, Horror from the Hills N:686:Judge Death N:674:Judge Fear N:654:Judge Fire N:656:Judge Mortis 2 hisses, 'You'll never get away with thisss...' hisses, 'Hey! I've got LAWYERSSS!' #N:687:Ariel, Queen of Air N:697:Smaug the Golden 4 groans in disbelief. roars furiously. howls, 'Black Arrow? NOOOO!' howls, 'This CAN'T be happening!' N:712:Fafner the Dragon 2 wails: 'Who are you that have wounded me so? Speak me your name!' complains: 'Who kindled your childish courage to this deadly deed?' #N:713:Fangorn the Treebeard N:715:Glaurung, Father of the Dragons 2 writhes as he spouts black blood from many wounds. says, 'I shall be avenged!' #N:729:Ulik the Troll #N:730:Baphomet the Minotaur Lord N:732:Bull Gates 3 sobs, 'OK, Linux doesn't suck. Let me live?' screams, 'Is megalomania THAT bad?' apologizes for MS-DOS. N:733:Santa Claus 3 sobs, 'Think of the children you'll disappoint!' sobs, 'No, Virginia, there isn't... not any more...' attempts to buy you off with offers of goodies. #N:738:Khamul the Easterling N:743:The Phoenix 1 defiantly caws, 'I shall rise again!' #N:753:Nidhogg the Hel-Drake #N:754:The Lernean Hydra #N:755:Thuringwethil #N:762:Fundin Bluecloak #N:763:Dworkin Barimen N:764:Uriel, Angel of Fire N:765:Azriel, Angel of Death N:769:Raphael, the Messenger 3 screams, 'Help! I am undone!' says, 'The Most High hath ordained this; I must follow.' screams, 'My God, my God, why hast thou forsaken me?' N:766:Ancalagon the Black 2 writhes as he spouts acidic blood from many wounds. says, 'My friends the Wyrms of Power will get you!' #N:770:Artsi the Champion of Chaos # Saruman's first line is a paraphrase from LoTR when he insults Theoden. N:771:Saruman of Many Colours 2 says, 'Wait! Look behind you!' howls, 'Wormtongue! Where are you, you bastard?' N:772:Gandalf the Grey 2 screams, 'How have things come to this... again?!' yells, 'Ouch!' N:777:Bast, Goddess of Cats 2 spits, 'I'll be back, worse than ever!' snarls weakly. N:780:Vlad Dracula, Prince of Darkness 1 howls with pain and fear. #N:787:Hypnos, Lord of Sleep #N:792:Tselakus, the Dreadlord #N:796:The Norsa #N:805:Omarax the Eye Tyrant #N:814:Yig, Father of Serpents # I have little familiarity with the Amber books, so these are left # for others to fill in as they see fit. #N:773:Brand, Mad Visionary of Amber #N:789:Bleys, Master of Manipulation #N:791:Fiona the Sorceress #N:794:Julian, Master of Forest Amber #N:799:Caine, the Conspirator #N:807:Gerard, Strongman of Amber #N:813:Eric the Usurper N:820:Corwin, Lord of Avalon 2 says, 'Let's play fair, like the Olympic Games!' asks, 'Who am I? Where is here? Why you attack me!?" #N:824:Benedict, the Ideal Warrior N:825:The Witch-King of Angmar 1 wails, 'Nooooo!' #N:828:Ithaqua the Windwalker N:817:Hela, Queen of the Dead N:834:Ymir the Ice Giant N:835:Loki the Trickster N:837:Surtur the Giant Fire Demon 3 shouts, 'Why didn't I just stay in Asgard?' offers you everything in exchange for life. yells, 'I'll be back, with a squad of Cyberdemons!' N:655:Ubbo-Sathla, the Unbegotten Source N:695:Zoth-Ommog N:706:Yibb-Tstll the Patient One N:734:Eihort, the Thing in the Labyrinth N:735:The King in Yellow N:757:Hastur the Unspeakable N:760:Nyogtha, the Thing that Should not Be N:761:Ahtu, Avatar of Nyarlathotep N:767:Daoloth, the Render of the Veils N:788:Glaaki N:797:Rhan-Tegoth N:806:Tsathoggua, the Sleeper of N'kai N:809:Atlach-Nacha, the Spider God N:810:Y'golonac N:826:Cyaegha N:833:Abhoth, Source of Uncleanness N:841:Shuma-Gorath N:845:Yog-Sothoth, the All-in-One N:848:Shub-Niggurath, Black Goat of the Woods N:849:Nodens, Lord of the Great Abyss N:851:Nyarlathotep, the Crawling Chaos N:857:Great Cthulhu 5 sobs, 'I'm not bad, I was just born like this!' gibbers weakly. mumbles, 'kill -9 adventurer, kill -9 adventurer' oozes greenish blood from many wounds. burbles with terror. N:850:Carcharoth, the Jaws of Thirst N:846:Fenris Wolf N:840:Draugluin, Sire of All Werewolves 3 cringes and whimpers. says, 'Look, I promise I won't bite the mailman anymore!' says, 'Hey, put that rolled-up newspaper down!' #LOTR Book5 Chap.10 N:818:The Mouth of Sauron 1 cries, 'I am a herald and ambassador, and may not be assailed!' N:819:Klingsor, Evil Master of Magic 4 wails, 'Dire distress! Dire distress!' moans, 'So now you mock me who once strove after holiness?' wails, 'The pain, the pain..!' shouts, 'Ho, guards! Up! Foes are at hand!' N:830:Cantoras, the Skeletal Lord N:831:Mephistopheles, Lord of Hell N:804:Vecna, the Emperor Lich N:844:Kaschei the Immortal N:856:Gothmog, the High Captain of Balrogs N:858:Sauron, the Sorcerer N:860:Oberon, King of Amber 2 screams, 'This CAN'T be happening!' shouts, 'Kill me if you want, the Boss will getcha!' N:862:The Serpent of Chaos 1 screams, 'This CAN'T be happening!' N:*:Default lines 61 says: 'I am too young to die.' says: 'Ok, ok! I get: no more pals.' screams: 'Help, ho!' screams: 'What ho! Help!' says: 'You will pay for this!' says: 'Violence is no solution!' says: 'I thought you liked me.' says: 'Such senseless violence! I don't understand it!' screams: 'Ho! Murder! Murder!' says: 'Look, behind you!' screams: 'Run away!' screams: 'Run to the hills! Run for your lives!' says: 'Wait! Spare me and I'll make you rich! Money isn't a problem!' says: 'I'll be back...' says: 'Hey -- I've got lawyers!' says: 'All my possession for a moment of time!' says: 'Hey, it was only a joke, all right?' says: 'Stop!' says: 'Cut it out, will you?' says: 'I will not kneel. Strike!' screams: 'Cowards! Why did you not protect me?' screams: 'Idiots! I am surrounded by incompetent idiots!' says: 'I don't wanna die, I'm a god, why can't I live on?' yells: 'Someone call the Gendarmes!' screams: 'Keep that lunatic away from me!' shouts: 'Drop that weapon, now!' says: 'Fool! You don't know what you're doing!' screams for help! begs for mercy. sobs. screams: 'Help! The maniac's murdering me!' says: 'Just what is it you want? Money? Babes? I can arrange it...' says: 'Wait! Let's make a deal!' says: 'Just can't stop this surmounting terror!' says: 'If there is a God, then why has he let me die?' says: 'I know where I'm going -- out!' says: 'No hope, no life, just pain and fear.' says: 'I am a fugitive, hunted down like game.' says: 'You'll live to regret this blasphemous offence!' says: 'All my life's blood is slowly draining away...' asks: 'Should we be fighting at all?' asks: 'What are we fighting for?' asks you: 'Can you say you are proud of what you've done?' says: 'Every minute I get weaker...' says: 'All my life I've run away...' says: 'All that I see, absolute horror!' says: 'I have fallen prey to failure.' says: 'Just leave me alone!' says: 'Please, save me!' says: 'You've won a battle, but I'll win the war!' says: 'You've won this round, next time it's *my* turn!' says: 'Another time, another battlefield, *my* victory.' says: 'I've got to keep running.' says: 'It's all so futile!' says: 'Cowards live to fight another day.' says: 'Life it seems will fade away, drifting further every day.' says: 'Emptiness is filling me, to the point of agony.' says: 'Cannot stand this hell I feel!' cries: 'Someone help me, oh please God help me!' cries: 'Please! I have a mate and six siblings!' cries: 'Mama, they try and break me!' zangband/lib/file/news.txt0000755000000000000000000000150210250356274014547 0ustar rootroot ********************** ** ZAngband a.b.c ** ********************** Based on Moria: Copyright (c) 1985 Robert Alan Koeneke and Umoria: Copyright (c) 1989 James E. Wilson Angband 2.7 - 2.8: Ben Harrison < benh@phial.com > Old Zangband code: (Mr) Topi Ylinen < topiy@luukku.com > New Zangband code: Robert Ruehlmann < rr9@angband.org > ZAngband 2.3.0 - ???: The ZAngband DevTeam Visit the ZAngband Homepage at "http://www.zangband.org/" Send all comments, patches, etc, to "feedback@zangband.org". Send bug reports to "bugs@zangband.org". Press ? for help and = for the options screen while playing. zangband/lib/file/sample.txt0000755000000000000000000000005110250356274015052 0ustar rootrootN:*:Default 3 line # 1 line # 2 line # 3 zangband/lib/file/timenorm.txt0000644000000000000000000000144310250356274015426 0ustar rootrootS:0000 E:0000 D:It is midnight. S:0001 E:0259 D:It is deep night. S:0300 E:0459 D:It is early morning, but still dark. S:0500 E:0544 D:It will be day soon. S:0545 E:0559 D:The sun is rising. S:0600 E:0614 D:The sun has risen. S:0615 E:0659 D:Morning has broken. S:0700 E:0859 D:It is early morning. S:0900 E:0959 D:It is midmorning. S:1000 E:1154 D:It is late morning. S:1155 E:1159 D:It is almost noon. S:1200 E:1200 D:It is noon. S:1201 E:1459 D:It is early afternoon. S:1500 E:1559 D:It is midafternoon. S:1600 E:1659 D:It is late afternoon. S:1700 E:1729 D:It will be night soon. S:1730 E:1759 D:The sun is setting. S:1800 E:1859 D:The night has begun. S:1900 E:2059 D:It is early night. S:2100 E:2357 D:It is late night. S:2358 E:2359 D:It is almost midnight. zangband/lib/file/w_low.txt0000755000000000000000000000233610250356274014730 0ustar rootrootN:*:Default 97 'Idspispopd' 'Purple Rain' 'Uhl Khakhaas' 'Telecaster' 'Stratocaster' of Severe Pain 'Arkanna' 'Uhl Dover' 'Mallana' 'Pummeler' 'Morphain's Friend' 'Regulator' 'Mangler' 'Laik Kenegul' 'Mae Govannon' of the Uncaring 'Executioner's Friend' 'Knight Chopper' of Eddie 'Cannae' 'Revenge of Orculs' 'Whirlwind' 'Ogresmasher' 'Pod' 'Elfrist' 'Quick Kill' 'Quick Kick' 'Quick Maim' 'Luckbane' 'Wormbiter' 'Mumakbane' 'Cat's Claw' 'Shrieker' 'Baton' 'Rotten Egg' of Horror 'Necrophiliac' 'Metal-up-Your-Ass' 'Sands of Time' 'Vibrator' of Agony 'Satan's Claw' 'Flame Dart' 'Trigger-Happy' 'Phaser' 'Flamitt' 'Black Hawk' 'Invader' 'Tailgunner' 'Prowler' 'Photon Torpedo' 'Heavy Metal' 'Bad Company' 'Bad Blood' 'Crackdown' 'Budokan' 'Kick-Off' 'Kryptonite' 'Breakout' of Morgue of Low Blow 'Stunner' of Misery 'Virus' 'Whiplash' 'Wrath of Khan' of the Mad Butcher 'Fossilizer' 'Bananoid' 'Sopwith Camel' 'Patriot' of the Paranoid of the Paranoid Patriot of S&M of the Sadist of the Dominatrix 'Wrangler' 'Weekend Warrior' of Holy Smoke 'Firefighter' of Close Combat 'Bungee' of Worst Fear 'Pig's Eye' 'Pop' 'Peashooter' of Fender of Fal 'Bugslayer' 'Raid' of Vandalism of Lesser Might of the Mind 'Gilettar' 'Anguirel' 'Angrist' 'Belangil' zangband/lib/file/a_cursed.txt0000755000000000000000000000115610250356274015365 0ustar rootrootN:*:Default 50 of Misery of Horror of Agony of Severe Pain 'Leech' of Macbeth of Microsloth of Troubleseeking of the Kender 'Camlost' 'Wooden Box' of Glass of Freezing of Petrifying of Sinbad of Tristan 'Freefall' of Disappointment of Shocking of Leper of Surrendering of Doom of Woe of Suffocation of Menstruation of Confusion of the Fool of Suicide of the Masochist of Kamikaze of Wasted Years of Cursing of Damnation of Crushing of Mortality of Babality of Falsehood of Cowardice of Hatred of Sadness of Glass of Weakness of Humiliation of Teleporno 'Camlost' of Gorlim 'Man-Trap' 'Moonmaster' of Insecurity of Mooning zangband/lib/file/a_med.txt0000755000000000000000000000205510250356274014644 0ustar rootrootN:*:Default 86 'Resistor' 'Warrior's Shield' of Polonium 'Guardian' 'Protector' of Thiron of Quatis of Holiness 'Bloodshield' 'Morthog' of Tyrolia 'Pendulum' of the Champions of Ziggmund 'Defender' 'Protection of Ramses' 'Orion's Belt' 'Deluder' 'Saver' of Steel 'Steelskin' 'Lawbringer' 'Shadow' of the Shadows 'Sentinel' 'Restorer' 'Clockwork' 'Conservator' 'Cyclopean' of Crystal of the Dark of Stone 'Dam' 'Force Field' of Ivory of Jade of Lead 'Mana Web' 'Mightstone' of Runes 'Skull of Vecna' of Icarus 'Jewel Symal' 'Worfskin' 'Camelot' 'Metallicus' 'Regenerator' 'Moonstone' 'Stormcloak' 'Honesty' of Storms of Honour of Justice of Ra of the Castle Master of the Black Tiger 'Myth' of Crystallion 'Mechwarrior' of the Iron Lord of Lifeforce 'Total Eclipse' 'Windwalker' 'Balancer' of Gordion of Locutus 'Trilogy' of Super Zaxxon of the Cross of the Cleric 'Survivalist' of Kevlar 'Full Metal Jacket' of the Borg of the Mystics of the Magi of Robustness of Sussamma of Akebono of Takanohana of Sybok of the Valkyrie of Olorin of Llewella of Gondor of Belegennon zangband/lib/file/dead.txt0000755000000000000000000000276510250356274014504 0ustar rootroot _______________________ / \ ___ / \ ___ / \ ___ / RIP \ \ : : / \ / \ : _;,,,;_ : : / \,;_ _;,,,;_ | | ___ | | / \ | | : : | | _;,,,;_ ____ | | / \ | | : : | | : : | | _;,,,,;_ | | | | | | *| * * * * * * | * ________)/\\_)_/___(\/___(//_\)/_\//__\\(/_|_)_______ zangband/lib/file/error.txt0000755000000000000000000000447310250356274014736 0ustar rootrootN:*:Default 82 What game do you think you are playing anyway? Aivan sairas kaveri kun tuollaista aikoo puuhata! Insufficient data for further analysis. Non sequitur. Your facts are uncoordinated. Type '?' or '\' for help. Invalid command. What? WHAT?! You must be out of your mind! You're killing me. Are you sure? Are you sure you know what you are doing? Aww, come on! That makes no sense. I beg your pardon. Degreelessness mode on. Degreelessness mode off. Syntax error. That doesn't compute. I don't understand you. ??? Sure. Piece of cake. Error. You can't do that! Help! Come again? Sorry? Sorry, I'm not sure I understand you. What's your point? Unknown command. Command not found. An unexpected error has occurred because an error of type 42 occurred. Somehow, you think that would never work. Welcome to level 42. Don't be ridiculous! lfae aierty agnxzcg? Soyha, azho bouate! I don't fully understand you. Why would anybody want to do THAT? Yes, yes, now tell me about your childhood. Satisfied? Something is wrong here. There's something wrong with YOU. You leap up 9' and perform a miraculous 5xSpiral in the air. Aw, shaddap! Shut up, smartass! I see little point in doing that. Oh, really? Very funny. You've got to be kidding! I'm not amused. I must have misheard you. Nothing happens. Where did you learn THAT command? When all else fails, read the instructions. Why not read the instructions first? Cut it out! Nothing interesting happens. Just how exactly am I supposed to do THAT? That's morally wrong and I won't do it. I'm not gonna take this abuse. AAAAAAAAAAAAAHHHHHHHHHHHHHHHRRRRRRRRRRRGGGGGGGGGGG! No more, if you value your character's life! Give it up, guy. Disk error. (a)bort, (r)etry, (f)ail? Are you sure you want to use borg commands? You feel oddly normal. You cannot cast spells! 'I grow tired of thee, mortal.' You just completed your quest! You hear a bell toll three times! Successfully created a spoiler file. Hmmm, it seems to be cursed. There is a malignant black aura surrounding you... A terrible black aura blasts your weapon! You feel your life draining away! A mysterious force prevents you from doing that. This will mark the savefile as a cheater. Continue? (Y) You can't do that while wielding a weapon! You must first select a target. I'm afraid I can't do that, Dave. It magically summons mighty undead opponents! zangband/lib/file/monfrien.txt0000755000000000000000000001240610250356274015415 0ustar rootroot# This is the file for allowing friendly uniques to speak their "own" lines. # Deleting this file will have no real effect on the game. Modifying it may # cause STRANGE unique lines to come up if the format's wrong, but shouldn't # crash anything. The format goes like so: # # N:45:whoever this is # 3 # says line 1 # says line 2 # says line 3 # # The number after the N: is the "monster index number" obtained from # r_info.txt. The text field after that number isn't actually used--it's # just there to help humans edit the file. The numbers on lines by # themselves are the number of lines that the monster has. Getting these # numbers wrong won't crash anything, but will produce strange lines. # # A '*' in the number field is a wildcard and applies to any monster. # It should only be placed at the end of the file, since all 'N:' lines # after it will be ignored. # # Two or more monsters can share lines; just put their N: lines in a # contiguous block. # # To stop a certain monster from having unique lines, put a # in front of # its N: line. # # Entries by Eric Bock, Matt Graham, Andrew Hill, Chris Kern, and Topi Ylinen N:8:Farmer Maggot 3 seems sad about something. asks if you have seen his dogs. mumbles something about mushrooms. #N:19:Martti Ihrasaari N:53:Grip, Farmer Maggot's dog N:54:Wolf, Farmer Maggot's dog N:55:Fang, Farmer Maggot's dog 0 #N:63:Smeagol #N:135:Mughash the Kobold Lord #N:137:Wormtongue, Agent of Saruman #N:138:Robin Hood, the Outlaw #N:169:Brodda, the Easterling #N:291:Ulfast, Son of Ulfang #N:180:Orfax, Son of Boldor #N:237:Boldor, King of the Yeeks #N:200:Hobbes the Tiger #N:140:Lagduf, the Snaga #N:186:Grishnakh, the Hill Orc #N:215:Golfimbul, the Hill Orc Chief #N:260:Ufthak of Cirith Ungol #N:314:Shagrat, the Orc Captain #N:315:Gorbag, the Orc Captain #N:330:Bolg, Son of Azog #N:350:Ugluk, the Uruk #N:356:Lugdush, the Uruk #N:373:Azog, King of the Uruk-Hai #N:382:Mime, the Nibelung #N:383:Hagen, son of Alberich #N:419:Alberich the Nibelung King #N:392:Sangahyando of Umbar #N:380:Angamaite of Umbar #N:393:It #N:409:Kharis the Powerslave #N:413:Ulwarth, Son of Ulfang #N:431:Grendel #N:441:Barney the Dinosaur #N:489:Bokrug #N:493:Bert the Stone Troll #N:494:Bill the Stone Troll #N:495:Tom the Stone Troll #N:505:Groo the Wanderer #N:506:Fasolt the Giant #N:517:Jurt the Living Trump #N:551:Rogrog the Black Troll #N:573:Lord Borel of Hendrake #N:595:Father Dagon #N:596:Mother Hydra #N:598:Mandor, Master of the Logrus #N:606:Loge, Spirit of Fire #N:615:Moire, Queen of Rebma #N:616:Kavlax the Many-Headed #N:628:Malekith the Accursed #N:642:Jasra, Brand's Mistress #N:651:Strygalldwir #N:654:Judge Fire #N:656:Judge Mortis #N:660:Rinaldo, son of Brand #N:670:Jack of Shadows #N:674:Judge Fear #N:681:Chaugnar Faugn, Horror from the Hills N:685:Shoggoth 2 wobbles about disgustingly. wails, 'Tekeli-li! Tekeli-li!' #N:686:Judge Death #N:687:Ariel, Queen of Air #N:697:Smaug the Golden #N:712:Fafner the Dragon N:713:Fangorn the Treebeard 1 #LOTR Book3 Chap.4 says: 'Hoom, hm, ah well.' #N:715:Glaurung, Father of the Dragons #N:729:Ulik the Troll #N:730:Baphomet the Minotaur Lord #N:732:Bull Gates #N:733:Santa Claus #N:738:Khamul the Easterling #N:743:The Phoenix #N:753:Nidhogg the Hel-Drake #N:754:The Lernean Hydra #N:755:Thuringwethil #N:762:Fundin Bluecloak #N:763:Dworkin Barimen #N:764:Uriel, Angel of Fire #N:765:Azriel, Angel of Death #N:769:Raphael, the Messenger #N:766:Ancalagon the Black #N:767:Daoloth, the Render of the Veils #N:770:Artsi the Champion of Chaos #N:771:Saruman of Many Colours #N:772:Gandalf the Grey #N:773:Brand, Mad Visionary of Amber #N:777:Bast, Goddess of Cats #N:780:Vlad Dracula, Prince of Darkness #N:787:Hypnos, Lord of Sleep #N:789:Bleys, Master of Manipulation #N:791:Fiona the Sorceress #N:792:Tselakus, the Dreadlord #N:794:Julian, Master of Forest Amber #N:796:The Norsa #N:799:Caine, the Conspirator #N:805:Omarax the Eye Tyrant #N:807:Gerard, Strongman of Amber #N:809:Atlach-Nacha, the Spider God #N:813:Eric the Usurper #N:814:Yig, Father of Serpents #N:817:Hela, Queen of the Dead #N:820:Corwin, Lord of Avalon #N:824:Benedict, the Ideal Warrior #N:825:The Witch-King of Angmar #N:828:Ithaqua the Windwalker #N:834:Ymir the Ice Giant #N:835:Loki the Trickster #N:837:Surtur the Giant Fire Demon #N:655:Ubbo-Sathla, the Unbegotten Source #N:695:Zoth-Ommog #N:706:Yibb-Tstll the Patient One #N:734:Eihort, the Thing in the Labyrinth #N:735:The King in Yellow #N:757:Hastur the Unspeakable #N:760:Nyogtha, the Thing that Should not Be #N:761:Ahtu, Avatar of Nyarlathotep #N:788:Glaaki #N:797:Rhan-Tegoth #N:806:Tsathoggua, the Sleeper of N'kai #N:810:Y'golonac #N:826:Cyaegha #N:833:Abhoth, Source of Uncleanness #N:841:Shuma-Gorath #N:845:Yog-Sothoth, the All-in-One #N:848:Shub-Niggurath, Black Goat of the Woods #N:849:Nodens, Lord of the Great Abyss #N:851:Nyarlathotep, the Crawling Chaos #N:857:Great Cthulhu #N:850:Carcharoth, the Jaws of Thirst #N:846:Fenris Wolf #N:840:Draugluin, Sire of All Werewolves #N:830:Cantoras, the Skeletal Lord #N:831:Mephistopheles, Lord of Hell #N:818:The Mouth of Sauron #N:819:Klingsor, Evil Master of Magic #N:804:Vecna, the Emperor Lich #N:844:Kaschei the Immortal #N:856:Gothmog, the High Captain of Balrogs #N:858:Sauron, the Sorcerer #N:860:Oberon, King of Amber #N:862:The Serpent of Chaos #N:*:Default lines zangband/lib/file/readme.txt0000755000000000000000000000405710250356274015040 0ustar rootrootMost files in this directory are use by the get_rnd_line function for various purposes in the game. They can be edited and customized by the user with any ascii text editor. If you add / remove lines you should modify the index (the first line) accordingly. If you set an invalid index weird messages and other things can appear, and you can crash the game. Please see sample.txt before you try modifying the files. The a_*.txt and w_*.txt are used when the game generates a random artifact. However, instead of picking a name from the appropriate file, the game may form a new name from syllables[]. The files in this directory: a_cursed.txt Possible names for randomly generated cursed armour artifacts a_high.txt Possible names for randomly generated 'powerful' armour artifacts a_low.txt Possible names for randomly generated 'weak' armour artifacts a_med.txt Possible names for randomly generated 'medium' armour artifacts chainswd.txt Possible noise for the Chainsword crime.txt Possible crimes that speaking uniques may have committed dead.txt The tombstone picture (the death screen) death.txt Possible 'last words' when the player dies elvish.txt Syllables for the names of random artifacts error.txt Possible random error messages (instead of "Type ? for help") mondeath.txt Possible 'last words' for speaking uniques monfear.txt Possible lines for scared speaking uniques monfrien.txt Possible lines for friendly speaking uniques monspeak.txt Possible lines for speaking uniques news.txt The game intro screen readme.txt You are reading this file right now rumors.txt Possible rumours (for scrolls or rumour and shopkeepers) sample.txt A sample file for the random line selecting function silly.txt Silly monster names for hallucination w_cursed.txt Possible names for randomly generated cursed weapon artifacts w_high.txt Possible names for randomly generated 'powerful' weapon artifacts w_low.txt Possible names for randomly generated 'weak' weapon artifacts w_med.txt Possible names for randomly generated 'medium' weapon artifacts zangband/lib/file/silly.txt0000755000000000000000000000776010250356274014743 0ustar rootrootN:*:Default 327 Borg Dalek Destructor Predator Wizard of Yendor Smurf Cosmic Horror Ultimate Unspeakable Lovecraftian Nightmare Space Invader Transformer Master of the Universe He-Man Pizza Worm Bogeyman Care Bear My Little Pony IRA Terrorist Nazi Klingon Cardassian Snark Luggage Killer penguin Were-penguin Giant were-penguin Peeping-Tom Jester Battlemech Invid Amazon Teenage Mutant Ninja Turtle Stork Hippo Roadrunner Fooglebird Ghoti Whale Apocalyptic Beast Anti-Christ Dark Avenger Evil Computer Finnish Sprayer Natas Poodle Fire Hydrant Multi-hued elephant Martian Purple alligator Space ship Miniature space fleet 6-feet tall elephant Insurance salesman Benji Magnet Squid Typewriter Manitou Miniature Shetland pony Munchkin Mad Scientist Keystone Kop Magnificent big-game hunter Wicked witch B-1 Bomber Superman Batman Boy Wonder Incredible Hulk Amazing Spider-Man Orphan Fifth columnist Secret agent Spy Dog catcher Vacuum cleaner Lawnmower Man Buzzard Oyster Wombat Midget elephant Chameleon Dungeon Master Dungeon Keeper Beyonder Robocop Robocod Paradroid Dominatrix Paranoid Wagtail Great tit Pope Tribble Jedi knight Dark Jedi Master of Teras Kasi Vulcan Rancor Sarlacc Warbot Tailgunner Skinhead Paul Bunyan Machine Bungee Sumo wrestler Gargantuan sumo wrestler Karateka President King Lobo Trashman Teacher Cheshire Cat Mad Hatter March Hare Voodoo doll Rag doll Scarecrow Porcupine Stone idol Hacker Samurai Samurai Cat Phantom of the Opera X-man Wumpus Walrus Silver Surfer Chimpanzee Omnipotent being Reaperbot G. I. Joe Cobra Commander Dreadnok Autobot Battlemage Saboteur Master of Sinanju Cyborg Lemming Green Goblin Galactus Gooey Kablooie Prowler Iron Fist Stuka Mahdi Xeroc Catwoman Stupendous Man Lost boy Hunter-killer Mad butcher Vore Rotfish Heresiarch False prophet Emperor Tumtum Androgyne Thieving magpie Boast of England Red Rose Knight Unholy cow Q Red Baron Giddy goon Vogon Hutt Puppet master Wookie Giant sandworm Muad' Dib Werepotato Mindless one Alien queen Alligator man Ancient ape Ancient astronaut Astro-zombie Beast from 20000 fathoms Beast with a million eyes Black goat of the woods with a thousand young Behemoth Leviathan Big Bug Blob Khyberdemon Body snatcher Brain from Planet Ardus Scatologist Brain of Venus Brain that would not die Breath blaster Chickenstein's monster Creature from the Black Lagoon Loch Ness monster Creeping death Creeping unknown Fifty-foot woman Flumsh 4D-man Bride of Frankenstein Galaxy being Giant baby Giant chicken Krazy Kat Giant celery stalk Gorilla witch Great god Porno H-dial monster Heap Love lotus Herman munster Highest intelligence H-man Hound of the Baskervilles Wizball ID monster Mancubus Pain elemental I-ball Killdozer Killer tomato Lurking fear Madball M.U.L.E. Magnetic monster Mixed-up zombie Hunchback of Notre Dame Monster that challenged the World Monster from green hell Morlock Queen Kong Robot Kong Mecha-Godzilla Ghidhrah Saucerman Ethereal Space Brain Floating brain of Hitler Stay Puft Marshmallow Man Supercow Swamp Thing Thing Teenage Frankenstein Teenage Werewolf Terror from the year 5000 Triffid Tripod Xenophobic man Undying monster Unknown terror Unnamable Womaneater E.T. Toxic Avenger Onslaught Pacman Paradroid Bantha Ass-headed fish Wizard of Oz Juggernaut Cheshire Cat Tauntaun Heretic Other Uncanny Libido Super-ego Ego Id Boy Blunder Big nothing Parademon Zelda Super Mario Great Giana Sister Magnetic man Pink elephant Elephant man Great Unknown Lurking unknown Cenobite Cheerleader Model T-1000 Terminator Whippoorwill Moon Maniac Killer Clown Stocking Strangler Torture Doctor Beast of the Black Forest Demon of the Belfry Sex Beast Sunday Morning Slasher Computer god Evil woman Lord of this world Sadist Masochist Sado-masochist War pig Iron Man Dehumanizer Holy diver Jugulator Exciter Painkiller angry mob Who Y2K bug RNG Greater hell magic mushroom cyber-unmaker leprechaun of the Dawn Devastator, the Destroyer's Big Brother Celestial Valkyrie Diablo Eidolon Medic Tank Tank Commander Man-in-Black Spawn Neon knight Defender of the Faith Eternal idol zangband/lib/file/w_cursed.txt0000755000000000000000000000101310250356274015403 0ustar rootrootN:*:Default 41 'Stink' 'Rirgil' of Misery of Backbiting of Horror of Agony of Severe Pain of the Fool 'Leech' 'Mormegil' of Gates of Microsloth of Troubleseeking of the Kender 'Camlost' 'Coffin Nail' of Fumbling of Tristan of Seppuku of Suicide of Harakiri of Kamikaze of Breaking of Low Blow of Castration of Kidney Punch 'Rotten Egg' of Losing of Master Bates of the Bastard of Friendship of Sadness of Humiliation of King Nothing of Cowardice of Falsehood of Ilitresi 'Funeral March' 'Unholy Avenger' of Woe 'P-Shooter' zangband/lib/file/w_med.txt0000755000000000000000000000245210250356274014673 0ustar rootrootN:*:Default 106 'Skull Smasher' 'Mauler' of Gideon of Joshua 'Fang's Avenger' 'Inquisitor' 'Hacker' of Bathory 'Executioner' 'Undertaker' of the Beast 'Monster Masher' 'Beheader' 'Blood' 'Kraken' 'Impaler' 'Peacemaker' 'Liberator' 'Snickersnee' 'Werebane' 'Magicbane' 'Dragon Claw' 'Blue Steele' 'Tempest' 'Fury' 'Disruptor' 'Grimtooth' 'Serpent's Tongue' 'Cleaver' 'Bonebreaker' 'Skullbuster' 'Lawgiver' 'Mirrorbright' 'Hildeleoma' 'Anvil' 'Hammer' of Battery 'Howler' 'Roarer' 'Thunderer' 'Marauder' 'Possessor' 'Insta-Death' 'Harvester' 'Harvester of Sorrow' 'Breaker' 'Gravedigger' 'Justice-for-All' 'Fade-to-Black' 'Flamebain' of Lyf 'Side Splitter' of Ra 'Trooper' 'Killer' 'Chaser' 'Predator' 'Rampage' 'Photon Storm' of Damocles 'Vendetta' 'Retaliator' 'Deathtrack' of the Shogun of Peenemuende 'Red October' of Lebensraum 'Seek-and-Destroy' of the Purgatory 'Witchfire' 'Super Patriot' 'Pestilence' of Midnight 'Lord of Midnight' 'Lord of Flies' 'Assassin' 'Duellist' 'Pandemonium' 'Damage, Inc.' 'Bloodrain' 'Satan's Fist' 'Weapon-X' 'Weapon Alpha' of the Borg of Hatred of Pak of Sinanju of Megalomania 'Slammer' of Brute Force of Brutality of Fatality 'Delta' of Diamond of Osric of Dara of Finndo of the Valkyrie 'Aglarang' 'Arunruth' 'Haradekket' 'Mundwine' 'Cubragol' 'Punisher' 'Wolverine' 'Hunter-killer' zangband/lib/file/a_high.txt0000755000000000000000000000172510250356274015021 0ustar rootrootN:*:Default 66 of Super Resistance of the Ultimate Defense of Ultimax of Invulnerability of Immunity 'Iddqd' of Paradise of the Gods of the Everlasting of Divine Protection of Omnipotence 'Heart of Ahriman' 'Destiny Star' of Destiny 'Monolith' 'Lawmaster' of Celestial Protection of Immortality of Diamond 'Living Armour' 'Living Wall' 'Life Matrix' of Malachite 'Mana Prism' 'Power Tower' 'Citadel' 'Genesis' of the Dungeon Master of the Dungeon Keeper 'Intact' of Great Barrier of Galactic Barrier 'Battlemech' 'Battle Force' 'Meganoid' 'Imperium' of the Empire 'Elite' 'Unbalancer' 'Bloodmail of Lucifer' of the Holy Grail 'Sampo' of Merlin's Pattern of Corwin's Pattern of Infinity of Eternity 'Lifegiver' of Heart's Desire 'Day of the Dove' of the Force of the Makers of the Immortals of the Best of Thrain of Deirdre of Random of Rebma of Celeborn of Invincibility 'Aegis' 'Eternal Bastion' of Lions 'Ever-Faithful' of Collapsium of the First Day of the Isles of the Blessed zangband/lib/file/chainswd.txt0000755000000000000000000000022010250356274015367 0ustar rootrootN:*:Default 6 KILL, KILL, KILL! The Chainsword roars noisily! VROOM! VROOM! Kill, kill, kill, kill, kill, kill! Blood, blood, blood! Bloodbath! zangband/lib/file/death.txt0000755000000000000000000002455110250356274014671 0ustar rootrootN:*:Default 342 Live and let live, right..? AAAAAAAAARRRRRRRRRRRRRRGGGGGGGGGGGGGHHHHHHHHHHHHHHH!!!!!!!!!!!! AAAARRRGGGHHH!!! Somehow, I have a bad feeling about this... Strangely, all of a sudden I don't feel so good. You can see armored women on winged horses coming for you. Oh well, you can't always win. I'm too young to die! I'll be back! O, untimely death! Slave, thou hast slain me! Ouch! That smarts! Who knocked? Did anybody get the number of that truck..? Ouch. Et tu, Brute! Then fall, Caesar! O! I die, Horatio... I told you to be careful with that sword... This guy's a little crazy... Ok, ok, I get it: No more pals. No more mr. nice guy! Who turned off the light..? Join the army, see the world, they said... Mom told me there'd be days like this... Rats! Shall this fellow live? Help, ho! What ho! Help! What hast thou done? I'll be revenged on the whole pack of you! You will *pay* for this! They say blood will have blood... Violence is no solution! Yes? &#%#&#%*#*&%!!!!! F***! No time to make a testament? Ugh! Aargh! Aaagghhh! I'm melting! Oof.. Oh! Did somebody knock? Later, dude... CU! What? Who? Me? Oh, s..t! ...amen! Eeek! Aacch! I hate it when that happens. One direct hit can ruin your whole day. Oh no! Not me! Ouch. Oh no, not again. Another one bites the dust. Goodbye. Help me! Farewell, cruel world. Oh man! Doough! This is the End, my only friend. It's all over. The fat lady sang. Why does everything happen to me? I'm going down. Crapola. Pow! Bif! Bam! Zonk! I should've listened to my mother... No... a Bud light! What was that noise? Mama said there'd be days like this. It's just one of those days... I see a bright light... Mommy? Is that you? I let you hit me! Sucker shot! I didn't want to live anyway. -- Hah haa! Missed me! Ha--- Was that as close as I think it was? Monsters rejoice: the hero has been defeated. It wasn't just a job it was an adventure! I didn't like violence anyway! I thought you liked me? Such senseless violence! I don't understand it. I think this guy's a little crazy. Somehow I don't feel like killing anymore. Help me! I am undone! Hey! Killin' ain't cool. This fell sergeant, Death, is strict in his arrest... The rest is silence. Guh! It's game over, man! You've run out of life. Thou art slain. Finish him! Trust me, I know what I'm doing... Die, mortal! Kill men i' the dark! What be these bloody thieves? Ho! Murder! Murder! O! I am spoil'd, undone by villains! O murderous slave! O villain! O, falsely, falsely murder'd! A guiltless death I die. AAAAAAAAAAAAAAAAAAAAAAAAHHHHHHH! Trust me. Dammit, this thing won't die! He hit me for HOW MUCH????? Look, behind you!!! Who fed steroids to that kobold? Don't worry, be happy! I don't believe this! Oops. Oups. Can't you take a joke? Well, I didn't much like this character, anyway... Oops, sorry... didn't mean to disturb you. I never get to have any fun! Stop! Cut it out! Don't worry. I've got a plan. It didn't look so tough. Run away! All clear, guys. AGAIN!?!?! I don't like this dungeon... Maybe this wasn't such a good idea. My God will protect me. You wouldn't dare! But what about my Parry Skill? Tumbling? Don't worry - I have Pilot-7. And I've *never* done you any harm. I don't understand. It should be dead by now. I'm heir to the crown. They wouldn't dare! Hey! Where's my stomach? My hands? Ha! That's the oldest trick in the book. Cover me. Watch this. And damn'd be him that first cries, 'Hold, enough!' I will not yield. ...but like a man he died. If you cut me down, I will only become more powerful. Well, at least I tried...? What could possibly have gone wrong? You die... What's with that weirdo with the teeth? Surrender? Never! I'm sure reinforcements will get here on time. They promised. Funny, didn't *look* like a cyberpsycho.... I have a very bad feeling about this. Do something, SCHMUCK! I feel I could cast 'Speak with Dead' and talk to myself. Oh, that's just a light wound. Ach, is doch nur 'ne Fleischwunde... I thought you were on MY side... Next time, try talking! Oh shit... I'll try to teleport again. Somebody get me a Rod of Resurrection... QUICK! Uhh... oh-oh... Gee, where'd everybody go? I see it coming...aaargllhhhh! {sough} What do you mean 'aaargllhhhh'? Hey man, I've paid for this. Ay! Ay! Ay! Ohe! Ohe! Ohe! Et tu, Caesar! Then fall, Brute! Even the best laid plans... Hey, not too rough! The Random Number Generator hates me! So when I die, the first thing I will see in heaven is a score list? Can't we talk this thing over? Wait! Spare me and I'll make you rich! Money is not a problem! I hate you! By the kind gods, 'twas most ignobly done! Mein Leben! Meine Lieder! I'm the hero of this story! I CAN'T die! I thought heroes were supposed to win! Gee... thanks. You've fallen and can't get up! 911? Sure don't look good... Oh No! Here I blow again! Hey - I've got lawyers. Thanks, I needed that. I AM toast!! Scheisse! Fatality! Brutality! Toasty! And you thought Tristan was unlucky... Just wait till I get my hands on the crook who sold me this crappy armor... All is lost. Monks, monks, monks! All my possessions for a moment of time! Don't let poor Nelly starve! Wally, what is this? It is death, my boy: they have deceived me. Everyone dances with the Grim Reaper. Adios. I'm going home, babe. I am innocent, innocent, innocent! Watch where you're pointing with that sword! You nearly... Hmm, some things are better wanted than had... And they told me it was not loaded. Of course I know what I am doing. It looked harmless. Hilfe, hilfe, hilfe! Look, dad! No head! Look! I'm flying! Think I'm gonna fall for that? I'll be back... as soon as I can. 3... 2... 1... Liftoff! My wallet? In your dreams! Yes! Yes! YES! YES! YY... AAARRRGGGHH! See you later, alligator! Up, up and awaaaayyy! Been nice knowing you. But I just got a little prick! And I just wanted that fancy suit of armour you were carrying... Hey guys, where are you? Hey look... ARCHERS! I can't possibly miss... I don't care. I have a Scroll of Raise Dead. I don't care. I have a Ring of Regeneration. I have this dungeon at home, I know where everything is! This HAS to be an illusion. I attempt to disbelieve it. I thought you could be trusted. Never try to sneak in a plate mail. I'll never surrender. I'll use the Cheat Death option... I'm invincible! I'm death incarnate! Nothing can harm me! Hey, it was only a joke, all right? Hey, don't talk to me like that! I have rights, too! Just because you're big and ugly doesn't mean you can push ME around. Me first! Me first! Let me handle this. No problem. That's easy. Oh, shit. So what? Tell me this is an illusion... please! I hate the RNG... They need a twenty to hit me! I'm invincible! Trust me. CHARGE! What do you mean, how many hit points do I have? What do you mean, my GOI expired? Yeah, I knew it was dangerous, but I was thinking about the experience points. You mean you get to use the critical hit chart too? You'd have to be a GOD to smile after that hit! I'm not afraid of death. I just don't want to be there when it happens. I have such sweet thoughts. I pray you all pray for me. I shall hear in heaven. Is not this dying with courage and true greatness? I must sleep now. Nurse, nurse, what murder! What blood! I have done wrong! It is finished. That unworthy hand! That unworthy hand! I am dying. Oh, dear. I will not kneel. Strike! I have led a happy life. Dying, dying. I feel the flowers growing over me. Now it is come. Let me die to the sound of sweet music. I will now enter the Halls of Mandos. Ungrateful traitors! We perish, we disappear, but the march of time goes on forever. Youth, I forgive thee. Treason! Treason! Coward! Why did you not protect me? I am absolutely undone. It is well. I die hard, but am not afraid to go. Do let me die in peace. Nothing is real but pain now. Violent use brings violent plans. Soldier boy, made of clay, now an empty shell. Bodies fill the fields I see, the slaughter never ends. Life planned out before my birth, nothing could I say. Blood will follow blood, dying time is here. Never happy endings on these dark sets. No one to play soldier now, no one to pretend. Time for lust, time for lie, time to kiss your life goodbye. Greetings, Death, he's yours to take away. I was born for dying. The higher you walk, the farther you fall. Where's your crown, King Nothing? Exit: light - enter: night! New blood joins this earth... You labeled me, I'll label you, so I dub thee unforgiven. If you're gonna die, die with your boots on! There's a time to live, and a time to die, when it's time to meet the maker. Isn't it strange, as soon as you're born you're dying? Only the good die young, all the evil seem to live forever. I don't wanna die, I'm a god, why can't I live on? And in my last hour, I'm a slave to the power of death. Now I am cold, but a ghost lives in my veins. You got to watch them - be quick or be dead. Heaven can wait 'till another day. You'll take my life but I'll take yours too. We won't live to fight another day. As I lay forgotten and alone, without fear I draw my parting groan. Somebody please tell me that I'm dreaming! Can't it be there's been some sort of error? Is it really the end not some crazy dream? Life down there is just a strange illusion. Your body tries to leave your soul. I'm so tired of living, I might as well end today. Life, life! Death, death! How curious it is! Catch my soul 'cos it's willing to fly away! Flames? Not yet, I think. Someone call the Gendarmes! I split my brain, melt through the floor. And now the dreams end. Off to Never-Never Land! Death greets me warm, now I will just say goodbye. What is this? I've been stricken by fate! This can't be happening to me! Flash before my eyes: now it's time to die. You have been dying since the day you were born. No point asking who's to blame. But for all his power he couldn't foresee his own demise. My creator will lay my soul to rest. Was that worth dying for? Can you say you are proud of what you've done? But there are some things which cannot be excused. Why is it some of us are here just so that we'll die? The shortest straw, pulled for you. There's got to be just more to it that this or tell me why do we exist? I can't believe that really my time has come. Too much of a good thing, I guess... I really screwed up this time. Wow, what a trip! Duh! Lev Zakrevski wouldn't have fallen for that one. Resistance was futile. They couldn't hit an elephant at this dist--- Life is but a dream. See you, space cowboy. THERE ARE WORSE WAYS TO DIE. TAKE IT FROM ME. Don't worry, it wouldn't have worked anyways. zangband/lib/file/mondeath.txt0000755000000000000000000003015510250356274015400 0ustar rootroot# This is the file for allowing dying uniques to speak their "own" lines. # Deleting this file will have no real effect on the game. Modifying it may # cause STRANGE unique lines to come up if the format's wrong, but shouldn't # crash anything. The format goes like so: # # N:45:whoever this is # 3 # says line 1 # says line 2 # says line 3 # # The number after the N: is the "monster index number" obtained from # r_info.txt. The text field after that number isn't actually used--it's # just there to help humans edit the file. The numbers on lines by # themselves are the number of lines that the monster has. Getting these # numbers wrong won't crash anything, but will produce strange lines. # # A '*' in the number field is a wildcard and applies to any monster. # It should only be placed at the end of the file, since all 'N:' lines # after it will be ignored. # # Two or more monsters can share lines; just put their N: lines in a # contiguous block. # # To stop a certain monster from having unique lines, put a # in front of # its N: line. # # Entries by Eric Bock, Matt Graham, Andrew Hill, Chris Kern, and Topi Ylinen N:53:Grip, Farmer Maggot's dog N:54:Wolf, Farmer Maggot's dog N:55:Fang, Farmer Maggot's dog 0 N:63:Smeagol 1 #LOTR Book6 Chap.3 hisses, 'We'll die, yes, die into the dust...Dusst!' N:573:Lord Borel of Hendrake 1 #Amber Book5 Chap.10 'Oh, basely done! I had hoped better of thee!' N:*:Default 332 'Live and let live, right..?' 'AAAAAAAAARRRRRRRRRRRRRRRRGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHH!!!!!!!!!!!' 'AAAARRRGGGHHH!!!' 'Somehow, I have a bad feeling about this...' 'Strangely, all of a sudden I don't feel so good.' 'I can see armored women on winged horses coming for me.' 'Oh well, you can't always win.' 'I'm too young to die!' 'I'll be back!' 'O, untimely death!' 'Slave, thou hast slain me!' 'Ouch! That smarts!' 'Who knocked?' 'Did anybody get the number of that truck..?' 'Ouch.' 'Et tu, Brute! Then fall, Caesar!' 'O! I die, Horatio...' 'I told you to be careful with that sword...' 'This guy's a little crazy...' 'Ok, ok, I get it: No more pals.' 'No more mr. nice guy!' 'Who turned off the light..?' 'Join the army, see the world, they said...' 'Mom told me there'd be days like this...' 'Rats!' 'Shall this fellow live?' 'Help, ho!' 'What ho! Help!' 'What hast thou done?' 'I'll be revenged on the whole pack of you!' 'You will *pay* for this!' 'They say blood will have blood...' 'Violence is no solution!' 'Yes?' '#&%#&#%*#*&%!!!!!' 'F***!' 'No time to make a testament?' 'Ugh!' 'Aargh!' 'Aaagghhh!' 'I'm melting!' 'Oof..' 'Oh!' 'Did somebody knock?' 'Later, dude...' 'CU!' 'What? Who? Me? Oh, s..t!' '...amen!' 'Eeek!' 'Aacch!' 'I hate it when that happens.' 'One direct hit can ruin your whole day.' 'Oh no!' 'Not me!' 'Ouch.' 'Oh no, not again.' 'Another one bites the dust.' 'Goodbye.' 'Help me!' 'Farewell, cruel world.' 'Oh man!' 'Doough!' 'This is the End, my only friend.' 'It's all over.' 'The fat lady sang.' 'Why does everything happen to me?' 'I'm going down.' 'Crapola.' 'Pow!' 'Bif!' 'Bam!' 'Zonk!' 'I should've listened to my mother...' 'No... a Bud light!' 'What was that noise?' 'Mama said there'd be days like this.' 'It's just one of those days...' 'I see a bright light...' 'Mommy? Is that you?' 'I let you hit me!' 'Sucker shot!' 'I didn't want to live anyway.' '--' 'Hah haa! Missed me! Ha---' 'Was that as close as I think it was?' 'The monsters rejoice: the hero has been defeated.' 'It wasn't just a job it was an adventure!' 'I didn't like violence anyway!' 'I thought you liked me?' 'Such senseless violence! I don't understand it.' 'I think this guy's a little crazy.' 'Somehow I don't feel like killing anymore.' 'Help me! I am undone!' 'Hey! Killin' ain't cool.' 'This fell sergeant, Death, is strict in his arrest...' 'The rest is silence.' 'Guh!' 'It's game over, man!' 'Trust me, I know what I'm doing...' 'Kill men i' the dark! What be these bloody thieves?' 'Ho! Murder! Murder!' 'O! I am spoil'd, undone by villains!' 'O murderous slave! O villain!' 'O, falsely, falsely murder'd!' 'A guiltless death I die.' 'AAAAAAAAAAAAAAAAAAAAAAAAHHHHHHH!' 'Trust me.' 'Dammit, this thing won't die!' 'He hit me for HOW MUCH?????' 'Look, behind you!!!' 'Who fed steroids to that kobold?' 'Don't worry, be happy!' 'I don't believe this!' 'Oops.' 'Can't you take a joke?' 'Well, I didn't much like this character, anyway...' 'Oops, sorry... didn't mean to disturb you.' 'I never get to have any fun!' 'Stop!' 'Cut it out!' 'Don't worry. I've got a plan.' 'It didn't look so tough.' 'Run away!' 'All clear, guys.' 'AGAIN!?!?!' 'I don't like this dungeon...' 'Maybe this wasn't such a good idea.' 'My God will protect me.' 'You wouldn't dare!' 'But what about my Parry Skill? Tumbling?' 'Don't worry - I have Pilot-7.' 'And I've *never* done you any harm.' 'I don't understand. It should be dead by now.' 'I'm heir to the crown. They wouldn't dare!' 'Hey! Where's my stomach? My hands?' 'Ha! That's the oldest trick in the book.' 'Cover me.' 'Watch this.' 'And damn'd be him that first cries: Hold, enough!' 'I will not yield.' '...but like a man he died.' 'If you cut me down, I will only become more powerful.' 'Well, at least I tried...?' 'What could possibly have gone wrong?' 'What's with that weirdo with the teeth?' 'Surrender? Never!' 'I'm sure reinforcements will get here on time. They promised.' 'Funny, didn't *look* like a cyberpsycho....' 'I have a very bad feeling about this.' 'Do something, SCHMUCK!' 'I feel I could cast Speak with Dead and talk to myself.' 'Oh, that's just a light wound.' 'Ach, is doch nur 'ne Fleischwunde...' 'I thought you were on MY side...' 'Next time, try talking!' 'Oh shit... I'll try to teleport again.' 'Somebody get me a Rod of Resurrection... QUICK!' 'Uhh... oh-oh...' 'Gee, where'd everybody go?' 'I see it coming...aaargllhhhh! {sough}' 'Ay! Ay! Ay!' 'Ohe! Ohe! Ohe!' 'Et tu, Caesar! Then fall, Brute!' 'Even the best laid plans...' 'Hey, not too rough!' 'The Random Number Generator hates me!' 'So when I die, the first thing I will see in heaven is a score list?' 'Can't we talk this thing over?' 'Wait! Spare me and I'll make you rich! Money is not a problem!' 'I hate you!' 'By the kind gods, 'twas most ignobly done!' 'Mein Leben!' 'Meine Lieder!' 'I'm the hero of this story! I CAN'T die!' 'Gee... thanks.' 'I've fallen and I can't get up!' '911?' 'Sure don't look good...' 'Oh No! Here I blow again!' 'I'll be back...' 'Hey - I've got lawyers.' 'Thanks, I needed that.' 'I AM toast!!' 'Scheisse!' 'Oh, basely done! I had hoped for better of thee!' 'I am death incarnate! NOTHING can harm me!' 'And you thought Tristan was unlucky...' 'Just wait till I get my hands on the crook who sold me this crappy armor...' 'All is lost. Monks, monks, monks!' 'All my possessions for a moment of time!' 'Don't let poor Nelly starve!' 'Wally, what is this? It is death, my boy: they have deceived me.' 'Everyone dances with the Grim Reaper.' 'Adios.' 'I'm going home, babe.' 'I am innocent, innocent, innocent!' 'Watch where you're pointing with that sword! You nearly...' 'Hmm, some things are better wanted than had...' 'And they told me it was not loaded.' 'Of course I know what I am doing.' 'It looked harmless.' 'Hilfe, hilfe, hilfe!' 'Look, dad! No head!' 'Look! I'm flying!' 'Think I'm gonna fall for that?' 'I'll be back... as soon as I can.' '3... 2... 1... Liftoff!' 'My wallet? In your dreams!' 'Yes! Yes! YES! YES! YY... AAARRRGGGHH!' 'See you later, alligator!' 'Up, up and awaaaayyy!' 'Been nice knowing you.' 'But I just got a little prick!' 'And I just wanted that fancy suit of armour you were carrying...' 'Hey guys, where are you?' 'Hey look... ARCHERS!' 'I can't probably miss...' 'I don't care. I have a Scroll of Raise Dead.' 'I don't care. I have a Ring of Regeneration.' 'I have this dungeon at home, I know where everything is!' 'This HAS to be an illusion. I attempt to disbelieve it.' 'I thought you could be trusted.' 'Never try to sneak in a plate mail.' 'I'll never surrender.' 'I'll use the Cheat Death option...' 'I'm invincible!' 'Hey, it was only a joke, all right?' 'Hey, don't talk to me like that!' 'I have rights, too!' 'Just because you're big and ugly doesn't mean you can push ME around.' 'Me first! Me first!' 'Let me handle this.' 'No problem. That's easy.' 'Oh, shit.' 'So what?' 'Tell me this is an illusion... please!' 'I hate the RNG...' 'They need a twenty to hit me! I'm invincible!' 'Trust me.' 'CHARGE!' 'What do you mean, how many hit points do I have?' 'What do you mean, my GOI expired?' 'Yeah, I knew it was dangerous, but I was thinking about the XP...' 'You mean you get to use the critical hit chart too?' 'You'd have to be a GOD to smile after that hit!' 'Morons! I have morons on my payroll!' 'Idiots! I am surrounded by incompetent idiots!' 'I have such sweet thoughts' 'I pray you all pray for me.' 'Is not this dying with courage and true greatness?' 'I must sleep now.' 'Nurse, nurse, what murder! What blood! I have done wrong!' 'That unworthy hand! That unworthy hand!' 'I am dying.' 'Oh, dear.' 'I will not kneel. Strike!' 'I have led a happy life.' 'Dying, dying.' 'I feel the flowers growing over me.' 'Now it is come.' 'Let me die to the sound of sweet music.' 'I will now enter the Halls of Mandos.' 'Ungrateful traitors!' 'We perish, we disappear, but the march of time goes on forever.' 'Youth, I forgive thee.' 'Treason! Treason!' 'Cowards! Why did you not protect me?' 'I am absolutely undone.' 'It is well. I die hard, but am not afraid to go.' 'Do let me die in peace.' 'Nothing is real but pain now.' 'Violent use brings violent plans.' 'Soldier boy, made of clay, now an empty shell.' 'Bodies fill the fields I see, the slaughter never ends.' 'Life planned out before my birth, nothing could I say.' 'Blood will follow blood, dying time is here.' 'Never happy endings on these dark sets.' 'No one to play soldier now, no one to pretend.' 'Time for lust, time for lie, time to kiss your life goodbye.' 'Greetings, Death, I'm yours to take away.' 'I was born for dying.' 'The higher you walk, the farther you fall.' 'Exit: light - enter: night!' 'New blood joins this earth...' 'You labeled me, I'll label you, so I dub thee unforgiven.' 'If you're gonna die, die with your boots on!' 'There's a time to live, and a time to die, when it's time to meet the maker.' 'Isn't it strange, as soon as you're born you're dying?' 'Only the good die young, all the evil seem to live forever.' 'I don't wanna die, I'm a god, why can't I live on?' 'And in my last hour, I'm a slave to the power of death.' 'Now I am cold, but a ghost lives in my veins.' 'You got to watch them - be quick or be dead.' 'Heaven can wait till another day.' 'You'll take my life but I'll take yours too.' 'We won't live to fight another day.' 'As I lay forgotten and alone, without fear I draw my parting groan.' 'Somebody please tell me that I'm dreaming!' 'Can't it be there's been some sort of error?' 'Is it really the end not some crazy dream?' 'Life down there is just a strange illusion.' 'My life suffocates, planting seeds of hate.' 'I split my brain, melt through the floor.' 'My body tries to leave my soul.' 'I'm so tired of living, I might as well end today.' 'Life, life! Death, death! How curious it is!' 'Catch my soul 'cos it's willing to fly away!' 'Flames? Not yet, I think.' 'Someone call the Gendarmes!' 'And now the dreams end.' 'I'm off to Never-Never Land!' 'Death greets me warm, now I will just say goodbye.' 'What is this? I've been stricken by fate!' 'This can't be happening to me!' 'Flash before my eyes: now it's time to die.' 'You have been dying since the day you were born.' 'No point asking who's to blame.' 'But for all my power I couldn't foresee my own demise.' 'My creator will lay my soul to rest.' 'Was that worth dying for?' 'Can you say you are proud of what you've done?' 'But there are some things which cannot be excused.' 'Why is it some of us are here just so that we'll die?' 'The shortest straw, pulled for me.' 'There's got to be just more to it that this or tell me why do we exist?' 'I can't believe that really my time has come.' 'Too much of a good thing, I guess...' 'When the dream dies, the nightmare begins.' 'You maggots make me sick. I'll be avenged. Lucifer dwells within all of us!' 'I really screwed up this time.' 'Wow, what a trip!' 'I believe my kingdom will come' zangband/lib/file/monspeak.txt0000755000000000000000000005357110250356274015425 0ustar rootroot# This is the file for allowing uniques to speak their "own" lines. # Deleting this file will have no real effect on the game. Modifying it may # cause STRANGE unique lines to come up if the format's wrong, but shouldn't # crash anything. The format goes like so: # # N:45:whoever this is # 3 # says line 1 # says line 2 # says line 3 # # The number after the N: is the "monster index number" obtained from # r_info.txt. The text field after that number isn't actually used--it's # just there to help humans edit the file. The numbers on lines by # themselves are the number of lines that the monster has. Getting these # numbers wrong won't crash anything, but will produce strange lines. # # A '*' in the number field is a wildcard and applies to any monster. # It should only be placed at the end of the file, since all 'N:' lines # after it will be ignored. # # Two or more monsters can share lines; just put their N: lines in a # contiguous block. # # To stop a certain monster from having unique lines, put a # in front of # its N: line. # # Entries by Eric Bock, Matt Graham, Andrew Hill, Chris Kern, and Topi Ylinen N:8:Farmer Maggot 12 seems sad about something. asks if you have seen his dogs. tells you to get off his land. #LOTR Book1 Chap.4 says, "Grip! Fang! Wolf! Come on, lads!" says, "They won't harm you -- not unless I tell 'em to!" says, "Here, Grip! Fang! Heel!" exclaims, "Well, if that isn't queerer than ever?" asks, "Were you coming to visit me?" says, "I'll not light my lanterns till I turn for home." says, "I see you are in some kind of trouble." yells, "Hallo there!" says, "It's been a queer day, and no mistake." N:19:Martti Ihrasaari 3 says, 'For a Finnish President, I sure am stupid...' says, 'I routinely carry nifty things...' says, 'I need a sauna, eh?' N:53:Grip, Farmer Maggot's dog N:54:Wolf, Farmer Maggot's dog N:55:Fang, Farmer Maggot's dog 6 chases its tail. barks loudly. froths at the mouth. wags its tail. rolls over. growls. N:63:Smeagol 27 sniggers. grovels. picks his nose. pines for his precious. searches his pockets. eats some slimy creatures. mutters, 'My precious, wheres my precious?' shouts, 'No Master Hobbitsisisisis!' cries, 'The ring was ours for agesisisisis!' says, 'Smeagol sneeking! ME! Shneekingsisis!' screams, 'Nasty Hobbitsisisisis...' says, 'Come on, quickly, follow Smeagol' says, 'Every way is guarded, silly foolsis!' says, 'Nasty Bagginis, stole my precious.' says, 'She will kill them oh yes she will precious.' whines, 'Weees wants some fishises.' says, 'Whats has its got in its pocketses, hmmm?' whimpers, 'We've lost itses we have.' says, 'He'll eastus all the world if he getsitses it.' says, 'No food, no rest; Smeagol a SNEAK!' says, 'What a dainty little dish you will be for her.' says, 'Hobbitses always SOOOO Polite.' screams, 'Stop, Thief!' says, 'Makeses him drop his weapon precious.' grovels, 'He has only four fingers on the black hand.' growls, 'Not nice Hobbits, not sensible!' says, 'If you findesis it, give it us back.' N:135:Mughash the Kobold Lord 4 says, 'I may be a kobold, but I'll kick your arse!' says, 'Feel my wrath, fool!' says, 'Death and destruction make me happy!' snickers evilly. N:137:Wormtongue, Agent of Saruman 10 whines and sniggers. whispers nasty things. says, 'I'll slaughter you slowly...' #LOTR Book3 Chap.6 says, 'Lathspell I name you, Ill-news; and Ill-news is an ill guest they say.' says, 'Forbid his staff!' #LOTR Book3 Chap.6 yells, 'You lie!' #LOTR Book3 Chap.9 says, 'Let me go, let me go! I know the way!' #LOTR Book3 Chap.9 says, 'My messages are useless now!' says, 'No no!' #LOTR Book6 Chap.8 hisses, 'You told me to; you made me do it!' N:138:Robin Hood, the Outlaw 6 eyes your money pouch covetously. says, 'You look like Nottingham's man to me!' says, 'I bet I can shoot better than you...' says, 'Give 'til it hurts!' says, 'Don't force me to put an arrow in your skull...' says, 'Kevin Costner has soiled my name!' #N:169:Brodda, the Easterling #N:291:Ulfast, Son of Ulfang N:180:Orfax, Son of Boldor N:237:Boldor, King of the Yeeks 5 wonders aloud about your sexual orientation. spouts torrents of obscenities. shouts, 'YEEK! YEEK! YEEK!' says, 'I'll teach you to respect Yeeks!' says, 'Feel lucky, punk?' N:200:Hobbes the Tiger 4 says, 'Why were people put here? TIGER FOOD!' says, 'Yum! Adventurer sandwiches!' says, 'I killed Calvin, now I'll kill YOU!' says, 'I'll make your short life nasty and brutish!' N:140:Lagduf, the Snaga N:186:Grishnakh, the Hill Orc 7 says, 'Saruman is a fool, and a dirty treacherous fool.' says, 'I left a fool.' yells, 'Nazgul, Nazgul!' says, 'Fine leadership! I hope the great Ugluk will lead us out again.' hisses, 'My dear tender little fools.' says, 'Well, my little ones! Enjoying your nice rest?' snarls, 'Have you got it -- either of you?' N:215:Golfimbul, the Hill Orc Chief N:260:Ufthak of Cirith Ungol N:285:Orc captain N:314:Shagrat, the Orc Captain N:315:Gorbag, the Orc Captain N:330:Bolg, Son of Azog N:350:Ugluk, the Uruk N:356:Lugdush, the Uruk N:373:Azog, King of the Uruk-Hai 20 fingers his blade and grins evilly. snickers, 'Now, I strike a blow for *our* side!' says, 'Orcs don't get no respect... I'm gonna change that!' calls your mother nasty names. says, 'I'll bet your innards would taste real sweet...' belches and spits. scratches his armpits. says, 'I love the smell of fresh blood.' says, 'Yeeha! Another idiot to slaughter!' hawks a loogie in your direction. farts thunderously. wonders aloud how many experience points you're worth. says, 'I love being psychotic!' says, 'My brain's on fire with the feeling to kill!' says, 'I shall torture you slowly.' calls you a scum-sucking pig-dog. says, 'I shall have my way with your women!' says, 'You're not so tough, buttmunch!' says, 'Heh-heh, heh-heh, killing people is cool.' curses at you in Orcish. N:382:Mime, the Nibelung 6 says, 'Get away! This spot is mine!' says, 'I will soon close your eyes in eternal sleep.' says, 'I'll mess up all your stuff!' cries, 'You must pay me... with your life!' grins. says, 'Maybe I will just hack your head off.' N:383:Hagen, son of Alberich 10 says, 'Did you hear what the ravens said? Revenge, that is what they cry!' shouts, 'Hoiho! Hoiho! To arms! To arms!' grumbles, 'I hate the happy, and I am never glad.' cries, 'Keep away from the Ring!' boasts, 'My spear will certainly cut down the wrongful one.' cries, 'There! There shall my spear strike!' grins, 'You will die soon, handsome hero!' states, 'I am but avenging perjury.' shouts, 'Give the Ring here!' shouts, 'Hoiho! Hoiho-hoho!' N:419:Alberich the Nibelung King 16 says, 'I'll mess up all your stuff!' says, 'Give me the Rheingold, or die!' states, 'As I have renounced love, all who live shall soon renounce it!' laughs insanely. asks, 'Did you hear it? The nibelung hordes are rising from the depths!' laughs, 'Ha ha ha ha! Beware!' says, 'Beware, fool! Beware!' says, 'Envy led you here, pitiful rogue!' boasts, 'I dauntlessly defy everyone, everyone!' yells, 'Tremble, on your knees before the master of the Ring.' yells, 'Tremble with terror, abject throng!' says, 'I am watching you everywhere, expect me where you do not perceive me!' says, 'Feel my wrath, idle rascal!' says, 'I have discovered you, you stupid thief!' says, 'Are you still not afraid? You should be!' #N:392:Sangahyando of Umbar #N:380:Angamaite of Umbar #This next may be unnecessarily evil... :-] N:393:It 9 says, 'Nyah, nyah, betcha can't find me!' says, 'Come get some!' magically summons mighty undead opponents! chuckles evilly. magically summons Cyberdemons! summons special opponents! concentrates on its body. It starts moving faster. concentrates on its wounds. teleports away. N:409:Kharis the Powerslave 2 says, 'Open the gates of my hell, I will strike from the grave!' curses you. #N:413:Ulwarth, Son of Ulfang #N:431:Grendel N:441:Barney the Dinosaur 7 says, 'Cooperation! That's the magic word!' mutters, 'I *hate* those Teletubbies...' says, 'Won't you be my friend?' says, 'Let's all sing a HAPPY SONG!' mugs for the camera. simpers disgustingly. chews up a 'Tinky Winky' doll. #N:489:Bokrug N:493:Bert the Stone Troll N:494:Bill the Stone Troll N:495:Tom the Stone Troll 4 #Hobbit Chap.2 complains, 'What's a burrahobbit got to do with my pocket, anyways?' rejoices, 'No more roast mutton! Roast adventurer today!' says, 'That'll teach yer!' says, 'I won't take that from you!' N:505:Groo the Wanderer 1 says: 'A fray! A fray!' N:506:Fasolt the Giant 3 grumbles: 'Stop, greedy one! Leave something for me!' shouts: 'Back, over-bold one!' whines: 'You swindler, do you seek to vilify me?' N:517:Jurt the Living Trump 2 #Amber Book8 Chap.4 says: 'Only your death will set things right.' says: 'Don't call me clumsy!' #N:551:Rogrog the Black Troll N:573:Lord Borel of Hendrake 3 #Amber Book9 Chap.6 says: 'You smile at your own cowardice? Stand and fight, bastard!' #Amber Book10 Chap.5 says: 'I am Borel, Duke of Hendrake, Master of Arms of the Ways of Hendrake.' #Amber Book5 Chap.10 'I do not want it said that I took unfair advantage of you when I killed you.' #N:595:Father Dagon #N:596:Mother Hydra N:598:Mandor, Master of the Logrus 2 'Well, well, well.' 'It is a great pleasure to fight with such a worthy opponent as you.' #N:606:Loge, Spirit of Fire #N:615:Moire, Queen of Rebma #N:616:Kavlax the Many-Headed #N:628:Malekith the Accursed N:642:Jasra, Brand's Mistress 2 #In Amber Book8, She overtake the Keep of the Four Worlds #and turn the previous master into a coatrack 'I will turn you into a coatrack.' 'I'm a master of the Keep of the Four Worlds!' #N:651:Strygalldwir N:654:Judge Fire N:656:Judge Mortis N:674:Judge Fear N:686:Judge Death 4 hisses, 'Thisssss dungeon issss guilty.' hisses, 'Your crime isssss life.' hisses, 'The sssentencce isss death!' hisses, 'Your crime issss life. The sssentencce isss death.' #Quotes from "A salesman's tale" N:660:Rinaldo, son of Brand 2 says, 'What's a freezer? Glad you asked. It's a box to store your body!' 'If you buy a Grand-D machine, I'll throw Werewindle into the bargain.' N:670:Jack of Shadows 2 'The power of shadow is infinite.' 'With the Key of Kolwynia, I am invincible!' #N:681:Chaugnar Faugn, Horror from the Hills N:685:Shoggoth 2 barrels towards you horrifyingly. wails, 'Tekeli-li! Tekeli-li!' #N:687:Ariel, Queen of Air N:697:Smaug the Golden 8 speaks, 'I smell you and I feel your air. I hear your breath. Come along!' says, 'If you get off alive, you will be lucky.' grimaces. laughs with a devastating sound which shakes the ground. asks, 'Where are those who dare approach me?' gloats, 'I am old and strong, strong, strong.' boasts, 'My armour is like tenfold shields, no blade can pierce me.' boasts, 'My teeth are swords, my claws are spears, my breath is death.' N:712:Fafner the Dragon 5 says: 'You will make a fine meal.' says: 'I wanted a drink, now I have also found food.' says: 'My fangs are not for chattering, soon you will feel them.' says: 'My throat is well made to gulp you down.' growls: 'Come here, young braggart.' N:713:Fangorn the Treebeard 2 #LOTR Book3 Chap.4 says: 'Hoom, hm, ah well.' #LOTR Book6 Chap.6 #Too long, intentionaly :) to discourage those who defy him. booms: 'Burarum, you evileyed - blackhanded - bowlegged - flinthearted - clawfingered - foulbellied - bloodthirsty, morimaite'sincahonda, hoom, hm, ...' N:715:Glaurung, Father of the Dragons N:766:Ancalagon the Black 5 says: 'Thou shalt not escape thy doom.' #QUENTA SILMARILLION Chap.21 says: 'Evil have been all thy ways.' says: 'If thou wilt be slain, I will slay thee gladly!' says: 'Nay! At least thou art valiant.' says: 'Hail, Adventurer, son of Autoroller. Well met!' #N:729:Ulik the Troll #N:730:Baphomet the Minotaur Lord N:732:Bull Gates 10 says, '640K should be enough for ANYBODY!' says, 'Buy Windows 2000; the filesystem rocks!' says, 'Linux? Never heard of it...' says, 'Resistance is futile--you will be assimilated.' says, 'NT is the solution for ALL your needs!' hacks out some code and calls it a Service Pack. says, 'We don't have a monopoly... MacOS still exists!' wonders if he should buy a small country. says, 'Where will we let you go today? HELL!' cackles diabolically. N:733:Santa Claus 8 says, 'Ho ho ho! You're gonna die!' says, 'You're gettin' COAL in your stocking!' says, 'On Smasher, on Whoop-Ass, now dash away all!' chortles sadistically. says, 'You're on the Naughty List!' says, 'No presents for you, ever!' says, 'I'll sic my man-eating reindeer on you!' says, 'I hate Christmas so much that I've gone psychotic!' #N:738:Khamul the Easterling #N:743:The Phoenix #N:753:Nidhogg the Hel-Drake #N:754:The Lernean Hydra #N:755:Thuringwethil #N:762:Fundin Bluecloak #N:763:Dworkin Barimen N:764:Uriel, Angel of Fire N:765:Azriel, Angel of Death N:769:Raphael, the Messenger 7 says, 'Repent, evildoer!' says, 'My righteousness shall cleanse you!' says, 'Don't EVER steal from the collection plate!' says, 'God may love you, but *I* don't!' says, 'I shall smite thee with extreme prejudice!' says, 'Hope you like eternal damnation!' says, 'Verily, it is too late for thee.' #N:767:Daoloth, the Render of the Veils #N:770:Artsi the Champion of Chaos #N:771:Saruman of Many Colours N:772:Gandalf the Grey 3 #LOTR Book2 Chap.5 says, 'I am a Wielder of the Flame of Anor, you cannot pass.' says, 'The dark fire will not avail you, Flame of Udun.' says, 'Go Back to the Shadow! You cannot pass!' #N:773:Brand, Mad Visionary of Amber #N:777:Bast, Goddess of Cats #N:780:Vlad Dracula, Prince of Darkness #N:787:Hypnos, Lord of Sleep #N:789:Bleys, Master of Manipulation #N:791:Fiona the Sorceress #N:792:Tselakus, the Dreadlord #N:794:Julian, Master of Forest Amber #N:796:The Norsa #N:799:Caine, the Conspirator #N:805:Omarax the Eye Tyrant #N:807:Gerard, Strongman of Amber #N:809:Atlach-Nacha, the Spider God #N:813:Eric the Usurper #N:814:Yig, Father of Serpents #N:817:Hela, Queen of the Dead #N:820:Corwin, Lord of Avalon #N:824:Benedict, the Ideal Warrior N:825:The Witch-King of Angmar 2 #LOTR Book5 Chap.4 says, 'Do you not know Death when you see it?' #LOTR Book5 Chap.6 says, 'Thou fool. No living man may hinder me!' #N:828:Ithaqua the Windwalker #N:834:Ymir the Ice Giant #N:835:Loki the Trickster #N:837:Surtur the Giant Fire Demon N:655:Ubbo-Sathla, the Unbegotten Source N:695:Zoth-Ommog N:706:Yibb-Tstll the Patient One N:734:Eihort, the Thing in the Labyrinth N:735:The King in Yellow N:757:Hastur the Unspeakable N:760:Nyogtha, the Thing that Should not Be N:761:Ahtu, Avatar of Nyarlathotep N:788:Glaaki N:797:Rhan-Tegoth N:806:Tsathoggua, the Sleeper of N'kai N:810:Y'golonac N:826:Cyaegha N:833:Abhoth, Source of Uncleanness N:841:Shuma-Gorath N:845:Yog-Sothoth, the All-in-One N:848:Shub-Niggurath, Black Goat of the Woods N:849:Nodens, Lord of the Great Abyss N:851:Nyarlathotep, the Crawling Chaos N:857:Great Cthulhu 17 slurps and gibbers disgustingly. shrieks fit to wake the dead. oozes nasty, glistening slime all over the dungeon. farts thunderously. lets off a mind-numbing stench. howls, 'Tekeli-li! Tekeli-li!' makes a chilling slithering sound. howls, 'The OTHER GODS will feast on your brain!' hisses, 'I'll feed you to the Hounds of Tindalos...' hisses, 'Randolph Carter got off easy; you won't!' seethes and fumes sickeningly. hisses, 'I'll send you beyond Known Space to Azathoth!' waves nasty-looking tentacles about. picks its teeth with the bones of former players. opens your mind to a vista of nameless cosmic horror! opens your mind to a vista of endless 'Three's Company' reruns! snorts and slobbers with glee. N:850:Carcharoth, the Jaws of Thirst N:846:Fenris Wolf N:840:Draugluin, Sire of All Werewolves 8 barks and bellows frighteningly! says, 'Oh good, another chew toy!' says, 'Yummy! I was getting tired of chicken...' lets out an earsplitting howl. drools all over the dungeon. lifts his leg at the nearest wall. says, 'Bad adventurer! No more living for you!' snarls and howls. #LOTR Book5 Chap.10 N:818:The Mouth of Sauron 6 asks, 'Anyone with authority to treat with me? Not thou at least!' says, 'This time thou hast stuck out thy nose too far.' says, 'Thou shalt see what comes to him who defy Sauron the Great.' says, 'Take swift counsel with what little wit is left to you.' says, 'And now thou shalt endure the slow torment of years!' says, 'Do not bandy words in your insolence with the Mouth of Sauron!' N:819:Klingsor, Evil Master of Magic 6 states, 'The time has come.' bellows, 'Your master calls! Obey!' grunts, 'Beware!' states, 'You will fall into my power, you will remain my slave!' cries, 'Halt! I have the right weapon to fell you!' yells, 'I will cut you down with your master's spear!' N:830:Cantoras, the Skeletal Lord N:831:Mephistopheles, Lord of Hell N:804:Vecna, the Emperor Lich N:844:Kaschei the Immortal N:856:Gothmog, the High Captain of Balrogs N:858:Sauron, the Sorcerer N:860:Oberon, King of Amber 12 brags, 'My power is beyond compare!' snorts, 'A mere mortal dares challenge my might? HA!' says, 'Not another one! I just finished chewing on the last!' wonders aloud how many XP you're worth. leafs through 'Evil Geniuses For Dummies'. mutters, 'Another damn loser to kill...' says, 'Hell shall claim your remains!' says, 'Another 12 skulls and I get that reward from the Boss!' yawns at your pathetic efforts to kill him. says, 'Minions, slaughter this fool!' says, 'Set thine house in order, for thou shalt die...' says, 'I'm no god... God has MERCY!' N:862:The Serpent of Chaos 10 says, 'Foolish worm, you are DOOMED!' says, 'I'm the Big Bad Guy, and you're toast!' shouts, 'MOO HA HA HA! I am DEATH incarnate!' says, 'Prepare for your untimely demise!' opens up a can of Whoop-Ass (tm). picks its teeth with former adventurers' bones. says, 'Maybe I won't kill you... NOT!' yawns at your pathetic efforts to kill it. says, 'Another day, another bastard to slaughter...' says, 'I can't be bothered... minions, slaughter this fool!' N:*:Default lines 104 cackles evilly. cackles diabolically. says: 'Surrender, miserable flea!' says: 'Come get some!' says: 'Let's rock!' laughs devilishly. says: 'Flee while you can, gnat!' says: 'You are about to die, maggot!' says: 'Read your prayers!' hisses: 'Die!' says: 'You don't have a chance, moron!' says: 'Fear my wrath, fool!' says: 'Feel my fury, dolt!' says: 'Groo is a genius, compared to you!' gives you a contemptuous glance. says: 'Prepare to meet your Maker, fool!' says: 'Perish, mortal!' says: 'Your puny efforts make me laugh!' says: 'Drop dead, wimp!' says: 'You should have fled while you had the chance.' screams: 'Die by my hand!' says: 'Your last wish, punk?' says: 'Your death shall be a slow, painful one.' says: 'Your head shall be my next trophy.' screams: 'You are DOOMED!' grins sadistically. says: 'This dungeon shall be your TOMB!' laughs fiendishly. says: 'Your fate is sealed, worm.' says: 'Resistance is useless.' says: 'Hell shall soon claim your remains.' says: 'Thou shalt repent of thy cunning.' says: 'Verily, thou shalt be one dead cretin.' says: 'Surrender or die!' says: 'Savor thy breath, it be thine last.' says: 'Prepare do die, miscreant!' says: 'You're history, dude!' says: 'Feeling lucky, punk?' says: 'You're toast!' says: 'You're dead meat.' says: 'Make my day.' says: 'I shall flatten you!' says: 'I could spare you, but why?' says: 'Take this, you sissy!' says: 'Nothing can save you now!' says: 'This dungeon ain't big enough for the both of us.' says: 'I'm gonna break your face!' says: 'I hope you enjoy pain!' says: 'Give me your best blow!' says: 'Draw, if you are a man!' says: 'A time to die, fool!' bellows frighteningly! says: 'You will never leave this dungeon alive!' says: 'You'll leave this dungeon only in a wooden box!' says: 'Your mother wears army boots!' says: 'Drop that weapon, NOW!' says: 'Life ain't for you, and I'm the cure!' says: 'Resistance is futile. You will be terminated.' says: 'Sight and smell of this, it gets me going.' says: 'Victim is your name and you shall fall.' says: 'Stepping out? You'll feel our hell on your back!' says: 'Now I will waste my hate on you.' says: 'Don't tread on me!' says: 'So be it! Threaten no more!' says: 'Kill for gain or shoot to maim, but I don't need a reason.' says: 'You'll die as you lived, in a flash of the blade.' says: 'You'd better stand cos there's no turning back.' says: 'I just want to see your blood, I just want to stand and stare.' says: 'I've been looking so long for you; you won't get away from my grasp.' says: 'I'm coming after you; you can kiss your arse goodbye.' says: 'It's official; you suck!' sings: 'I hate you, you hate me, we're a helluva family.' says: 'A mere mortal dares challenge *ME*?!' says: 'There is no escape and that's for sure.' says: 'This is the end; I won't take anymore.' says: 'Say goodbye to the world you live in.' says: 'You've always been taking, but now you're giving.' says: 'My brain's on fire with the feeling to kill.' says: 'Don't try running away, because you're the one I'll find.' says: 'I was looking for you to start up a fight.' says: 'My innocent victims are slaughtered with wrath and despise!' says: 'I have found you, and there is no place to run.' says: 'My blood lust defies all my needs.' says: 'And damn'd be him that first cries: Hold, enough!' says: 'I can smell your blood, human!' says: 'Has your folly led to this?' wonders aloud how many experience points you're worth... says: 'Pride yourself on this, that you were slain by a champion.' thunders: 'May heaven have mercy on your soul, for I will have none.' screams for your blood! sighs: 'They send a poorer grade of adventurers down each year than the last.' says: 'Your life-blood will baptise my blade!' shouts: 'You will serve me in Valhalla!' snickers: 'Mommy's not here to save you now!' says: 'You're almost not worth killing...almost!' leaps towards you with death in its eye. sings: 'Cuts yer if ye stand, shoot yer if ye run.' says: 'Another adventurer? I just got through picking my teeth with the last.' says: 'Your two ears will decorate my belt.' says: 'I love all that blood.' says: 'I don't want to hurt you, I only want to kill you.' says: 'I like killing people, because it's so much fun.' screams: 'I'm out to destroy and I will cut you down!' sneers: 'You're no Lev Zakrevski.' zangband/lib/file/rumors.txt0000755000000000000000000012165710250356274015140 0ustar rootrootN:*:Default 647 They say that you can't trust rumors. You have no more Black Potions of Death. They say that smart guys hang around at 1600'. They say that smart guys hang around at 1500'. They say that tough guys hang around at 1600'. They say that tough guys hang around at 1500'. They say that handsome guys hang around at 1200'. They say that a visit to 3250' can be quite an experience. They say that a visit to 3250' will only get you killed. Throw a Potion of Blindness at a monster and it cannot cast any spells! Oberon is afraid that you will upset the balance even more! Oberon won't let you near the Serpent of Chaos. Not satisfied with the artifacts you find? Well, create your own! MAKE MONEY FAST! Find a Treasure Pit! Buy a home in the dungeon, any depth you want! Contact: McDuck & Co. They say that Alberich has forged an all-powerful Ring. A good item will not corrode. They say that Alberich lost the Tarnhelm after making himself invisible. They say that Nibelungs live in dark caves. Some weapons that slay dragons can be very deadly against them... Finding the Phial of Galadriel at 50' is nothing to be proud of. There are Black Market stores hidden deep in the dungeon, with COOL stuff! Have you ever seen a Rod of Havoc inscribed {BFG9000}? What a pity, you cannot read it! You will encounter a dark, tall stranger... A Mithril mail will not rust. An Adamantite mail will not rust. A Rusty Chain Mail cannot rust any further. If you are a mage, you will NOT want to find Raal's Tome of Destruction! You won't want to find Raal's Tome of Destruction! You won't want to find Raal's Tome of Destruction, unless you are a mage. A Wand of Death is useless against monsters that are tougher than you. A Wand of Death is of little use against foes that are dead already. Try taking off your armor before fighting a Gelatinous Cube! They say that only one sword can score *CRITICAL* hits. You have an error in object_desc()! This rumor is not true. This rumor is as true as the previous rumor. Trump power may be useful in some circumstances: free teleport! If you can fall like a feather, you need not care about gravity. They say that you should rejoice if you find a scroll labeled ""! You don't always have to kill everything you meet! If you can't beat it, leave it alone! An umber hulk can be a confusing sight. There *is* a good use for Potions of Detonations, Ruination and Death... It's a bad idea to throw away a Longsword (4d5). It's a bad idea to wield a Longsword (4d5). It's useless to bash monsters with bows - but there's one notable exception... Actually, Slime Mold Juice is not completely useless. Help me! I'm being held captive in a Vault at 2850'! Ever tried inscribing your armor {erodeproof}? Why are you wasting time reading fortunes? There is a horrible, ghastly fate awaiting you... at 2700'! You can get the Longsword 'Ringil' by doing the following: You can protect yourself from Great Wyrms of Power by doing the following: It's true name is 249. You feel like someone's pulling your leg! AAAAAAAAAAARRRRRRRRGGGGHHHHHHH! Imperial assassin looking for a job. Contact: Pak, Master of Sinanju, 2600'. Try inscribing the name of the first monster killed by it in the weapon! The richer the victim the happier the thief. Wanted: Smurfs. Good reward. Contact: Gargamel, 400'. Beware the Jabberwock, my son! The jaws that bite, the claws that catch... There's something bad about what you are carrying in your backpack... Thieves are more likely to appear if you are carrying a lot of money. Brand's sword, Werewindle, probably knows more than just one trick. They say that Scrolls of *Curse Weapon* can create powerful cursed artifacts. They say that Mjollnir will return to your hand if you throw it. They say that the Chainsword makes monsters mad with its awful noise! They say that Ringil shines so brightly that it makes monsters angry. They say that you cannot defeat the Serpent of Chaos without its missing eye. They say that all Pattern weapons are deadly against Demons of Chaos. Klingsor's Castle was destroyed by the the holy might of the Spear of Destiny. Orcs are mortally afraid of weapons that can slay them. There is a way to turn a Ring of Speed (-20) into a Ring of Speed (+20). There is no way to turn a Ring of Speed (-20) into a Ring of Speed (+20). Cool guys can resist fire. They say that death incarnate wears heavy metal boots... You feel the Longsword (t) you are carrying in your backpack is special... If you start seeing red monsters, you have probably gained infravision. They say that the dungeon is deeper than the Abyss. When all else fails, read the instructions. I've seen Paavo Vaaranen drop a Power Dragon Scale Mail! They say that the dungeon has no bottom. They say that the dungeon has a bottom. No poison is immediately deadly. I have seen a Ring of Speed (+50) in the Black Market! Telepathy works like a two-way door. Elvish waybread might negate the effects of poison. Once uncursed, Calris will become a deadly weapon. If there's a stairway to hell, there must also be a stairway to heaven. You feel your luck is turning... If you thought Death swords were bad, wait until you meet Killer katanas! Overeating can be bad for your health if there are others nearby. Cave dwellers are accustomed to darkness and rarely enjoy bright light. A creature made of stone can be slain by a spell that turns stone to mud. It is often a good idea to throw items that you don't want to eat or drink. The faster you run the more food you will burn. Invisible monsters will often expose themselves if you drop items around you. They say that the key to killing tougher monsters is called "hit&run". They say that there is no such thing as free advice. Wearing an Amulet of Doom will take you into the Dungeons of Doom. They say that when you're hungry you can get a pizza in 30 turns or it's free. You can often wrest one last charge from an empty wand if you try hard enough. Wands may recharge themselves if you leave them on the floor long enough. There is more than one way to deal with a locked door. Afraid of your valuables getting stolen? Carry more junk! Afraid of your money getting stolen? Invest it! Liar! It's not me who has snatched all the gold! Barney can make you look utterly stupid. Barney MUST die!!! If you hear something smashed into splinters, you had better watch out. Watch your step! - Choose search mode? Key (s). They say that you had better leave Greater hell-beasts alone. Kharis' tomb is somewhere in the dungeon and you won't want to desecrate it! Selling unidentified potions to shopkeepers might be safer than quaffing them. Always look out for trapdoors on "special" feeling levels! There is a way to max out your stats with Potions of Charisma & Nexus. Unique opponents will recover their health faster than other creatures. They say that only one sword, Nothung, can slay Fafner the Dragon. "So when I die, the first thing I will see in heaven is a score list?" You're going into the morgue at midnight? How dare you! I will not buy that! A Potion of Detonations is also known as nitroglycerin... There is a trap on this level! A weapon of Undead Slaying has all you need to kill a ghost. A weapon of Dragon Slaying may give you resistance to a dragon's breath attack. They say that only a Warrior will want to wear the Terror Mask. All that is shall come to an end - a dark day dawns for the gods. The One Ring is powerful, but will eventually destroy its owner. Having troubles with summoners? Door Creation is your friend! Stairway Creation may be slower than Teleport Level, but safer... I hid the Jewel in a vault at 4700'. To find it, you need to: Wands of Heal Monster are useful! Hint: ball spell, @....moo(o)ooo Guaranteed heal self - scenario: o'@, type c4c4c4c4 If it can't see you, it can't hurt you! If it can't see you, you might still be able to hurt it... The Jedi Holocron is the best artifact light source there is. I love you, you love me, we are a happy family! Even *you* can become a Living Trump with the right treatment. No animal is interested in sex if it is mortally scared. There is a plenty of Longswords around 1000'. Wagner's operas are awfully dissonant! The characters must be deaf! Groo may be as dumb as an amoeba, but he knows a good sword when he sees one. Groo is an idiot! Groo is a dolt! He is a fool! He has no mind! And now, Groo does what Groo does best! Groo is your worst nightmare. Freddy Krueger is your worst nightmare. Come to Freddy! He needs some sparring urgently. There are often stairways in graveyards: bad people are carried to hell... An urgent message from Dworkin: Pattern not found. Only a god of Thunder could ride a lightning bolt! When the day of Ragnarok comes, Surtur will set the world afire... Surtur's accursed sword, Twilight, burns with everlasting fire. Wotan (Odin) carved his Runespear from a branch of the World-Tree Ash. Hagen slew Siegfried with his spear, stealthily sneaking behind him. Powerful spells are bound in the Runes of Wotan's Spear. Flora's sexy boots will make you more attractive. Flora's sexy boots will cause the monsters to drool on you. They say that Caine is an expert assassin - and so is Fiona. They say that Corwin is extraordinarily tough. The wearer of Lohengrin's Chainmail is protected by the Holy Grail. Gurnemanz' Helmet will let you see holy mysteries hidden from men's eyes. Weapons of Flame will light your way. They say that the gods get angry if you pray too much. For any remedy there is a misery. Poison will kill you slowly. Using a Morningstar in the evening has no effect. Didn't you forget to pay? Death is just life's way of telling you you've been fired. They say that nobody can defeat his own ghost. A greedy genocide can be a fatal mistake, especially if you are low on hits. PLEASE ignore the previous rumor. There are scrolls that can be read only by mages. Some undead opponents will come back if defeated, more powerful than before! One level further down somebody is getting killed, right now. Meet me at 1900' if you are a man. Bashing a creature may sometimes stun it. One Ring to rule them all, One Ring to find them. One Ring to bring them all and in the darkness bind them. A person attuned to the Jewel could use it to erase the Pattern. Three Rings for the Elven-kings under the sky... Never carry a Potion of Detonations if there is a fire trap nearby! Laugh to scorn the power of man, for none of woman born shall harm thee! All hail thee that shalt be king hereafter! He who laughs at Groo's brains will find there is nothing to laugh about. A wise man always speaks too soon... Let us not dwell on possible bad fortunes! Appearance is only the frosting, not the cake! A feeling of Death flows through your body. Violence is no solution. Boots of Speed (+50) are no myth! You will need to Restore the Constitution if the Anarchists strike. Drain you of your sanity: Face the Thing That Should Not Be! Wearing an Amulet of Doom will make the interface graphical 3D. Whence come you then, that you have never heard of the Rhinegold? The Nibelung, Night-Alberich, in revenge, stole the Rhinegold. Since by curse it came to me, accursed be this Ring! Each shall itch to possess the Ring, but none in it shall find pleasure! Solemn treaties, with symbols of trust, carved by Wotan in his Spear. Pudpadnoy Tooboothokoot is possessed by a demon known only as "It". Merlin wore the Terror Mask and the last thing he ever saw was a big J... They say that the One Ring has a very special curse. They say that alcohol is bad for your health. What if you DON'T give a name to the artifact you create..? They say that ancient battlefields are often haunted. Beware of pits that fill the whole level! They say that the true name of wall monsters is 177. Never mind the Phial of Galadriel - the Phial of the Gods kicks its butt! A Ring of Speed? Phooey! Try looking for a Ring of *Speed*! Thisss cccity isss guilty... the crime isss life... the ssentence isss DEATH! If you hear heavy steps - watch out! A visit to the Wilderness is educational: you meet many strange animals. What happens if you wear a Ring of Extra Ring Fingers (-2) {cursed}? Damn! Those alien bastards are gonna pay for shooting up my ride! Oremor nhoj em llik tsum uoy emag siht niw ot. If I cancel tomorrow the undead will thank me today. Hellfire will burn your soul... (if you're not of evil alignment!) Never attempt to Call the Void in an enclosed space! Call the Void needs a lot of room to cast... Call the Void will grant you power over space monsters. Why doesn't Detect Monsters show invisible monsters? 'Cos you can't see 'em! I'll tell you the truth, son: your soul's gonna burn in a lake of fire! Not satisfied with being a human? Then polymorph into an Amberite! You cruelly stab the helpless, sleeping Software bug! Slab: Jus' say AarrghaarrghpleeassennononoUGH. You feel the Windows (95) on your hard disk is broken... There is a rare spellbook called [M$ PowerFools] {cursed} *** LOW HITPOINT WARNING! *** You may be a ***WINNER*** if you recall within 1000 turns! The Jewel of Judgement is actually the other eye of the Serpent of Chaos. The Serpent' dreaming eye is green, his track is moon-silver... Isn't your very head an especially prized one? Would ya like a special nice one-way trip to da underworld? Die or Deliver! Just assent with a head movement. Didn't your blood-brother end his career with an enforced suicide? Look out for gaz spreading blobs at the ceilings! Don't turn around: the Evil Eye iss jusst beehind thee .. Ya want a share of The Ultimate Dungeon Cleaner Trustee? Autorollers aren't wary bright things. The Wargs give ah very sharpee Choir tonightee! The depths are about as unreliable as a dish of over-ripe figs. May all the Crows of the Volcano pick your bones clean! Ever made your dance around enthousiastic lice without a certain staff? A staff is just like a colleague. But a reliable one! You are prepared to thrill us with a traditional heroic display? Some underworld lads would *LOVE* to blacken your name! Beware! The Castle now and then provides regrettable accidents. Never use a Staff of Insanity. Staffs of Insanity will confuse all monsters you can see. Staffs of Insanity can be used to turn the enemy against itself... Let the sun of wilderness melt the chill from your life! By combining our minds with motion we can tune for the shadow we desire. I can show ya the wayy back to chaos, if ya'r gettin homesick. Living trump you are? Your equilibrium is becoming impaired... Increase your pace to catch up with your thoughtlessness? Ya laik da show off a lott - how strong ya are, how fast ya are, ha! Solve your pattern, push on ahead, take mental notes as you proceed. Since confrontation is inevitable: give yourself a crash course of the code. It strucks you as something similar to color blindness. Contagious trap! Hey young magic coder adept, you still owe some contributions. Yes, timing is definitely against you today. Never mind. Amberites are able to sustain some pretty awful beatings. You are a secretive person, even in that incarnation, paradoxical too! Pass some time in places where the bad things dwell - be red-eyed for a day! Go on Goon! But there will be not sufficient time to flee any farther. Avoid overconfidence! Be able to gauge its range and striking angle. Try to beat them to the punch while they are still off-balance. Regain your mental footing - and detect the sounds of pursuit. Fire Angels have a vast array of senses. Shadow bloodhounds. The drug-store stuff affects your shadow-shifting ability! Not unusual to get drunk with but small successes and a Grinning Cat fading. Massage the sour muscles in your leg and rise to your feet! If you are slow in withdrawing (S)HE will manage to draw you in a clinch! Beat the air! Fly back up the high hole in the cleft at the rear of the cave. Sing: "You're not asking for the world, I'm not asking for perfection." Be (n)ever hopeful the poison will wear off eventually. A new arrival such as yours should perhaps be cautioned. No mercy to weaklings. Drifting bits of memory drawn together, assemble them into an entire fabric. From the standpoints of reason, business, caution you are wrong? Who cares! Heroes? What them are needed for: imagination, grave-garlands and necromancy. After some encounters with jellies you might feel overnarcotized or lame. It came to assail me: no escape? Yet another crawl from the Pit of Creation? If in good enough shape you'd better throw your legs over the edge... While standing in the shadows to regain stamina have visions of sugar plums... Encounter them orcs! know they are countless: no rest for you for hours! A monk might have a very foul blow, about four inches below the belt buckle. Are you really suffering withdrawal symptoms and want something crushable? Smaug and alcohol may disturb your .. was it sanity, courage or boredom? Ha, treasures! Like all vaults it was full of depths and dangers. The benefits of alchemy are my marriage, danger is my passion. Any dungeons offer mazes. But don't they lack the ZAngband sense of humor? Bodychecks, bodycounts itching all my ways but the awful software bugs... Be constantly on guard, joker, don't take anything at face value. Which is better: mind blast or mind wash? Will lost memories return gradually? Have a rest and a rumor in the inn over some comfortable pitchers of beer. The stakes are far too high for a weak bluff, so watch the scores? Be not like the others. Proceed carefully, cover yourself at all times. You wouldn't want to fool with the Trumps! Aren't they of Doom quality? Smeagols removal is high on your list of things that needed knowing or doing? Matter of recollection: find the inn and nurse some rumors for a while. Served you right: 3 quarters dead. Wasn't it purely a result of your arrogance? Confident - Cocky - Lazy - Dead. The Old Man's mantra, and a serious good one. Let your mind drift over the perversely plotted events of the past levels. Another loss? Curse your inertia at having let the situation slide for so long. So you have been another cockerel who'd crow before he thinks? Foolish heart! Wicked adventures? First and foremost: negotiate with the devil (inside)! This is the first turn of the rest of your game. Do not pour vinegar into your own mental wounds of pride. Head over heels? There comes a point in any illness when... A flash of your all-to-familiar arrogance might make you bungle. Ooh, they will sense it, you really love playing with fire. One ill turn deserves another. To say it blunt: you *cannot* afford another mistake. - What you say, really? Our dreams are too much with us. They imply a breach of security. Groo seems almost too well organized to be a mental case. Smaug is induced by pollution. Do you resist poison? They say that the Jewel of Judgement controls all the elements. They say: Droppa MaPantz is the court jester of Amber and a master of traps. They say: Spores that turn to jewel shards might smash you like bullets. They say: Inhale the crisp evening air before going downward hunting. They say that when you stop being vain you might even be dead. They say that there are ninnies, simpletons. And they never come back. They say that the vestal virgins are hidden somewhere downstairs. They say that all of them adventurers are destined for Hades. They say: Being what you have to be without whining about it. They say: Visit a tannery and get used to be tanned! They say: "Z" stands for Zelazny, Roger (+1995) and means 'He lives'(greek). Zelazny said: "A headlong rush may also result in a broken neck." Z. says: The full-scale, all-or-nothing reaction may be ok if you always win. Z. says: Couldn't we just sit down and talk about it over a couple of beers? Z. says: Somewhere there must be a gap in the icy blue logic that surrounds us. Chaos patrons are *very* moody! You gonna meet unique personalities who are even more than a challenge. To be at cross-purposes over your ways with uniques might prove fatal. No good to confront the fiendish, quick Freesia without some good gear. Being infuriated by tricky foes will make your timing less immaculate. Jellies? Some encounters are chilly though they take you no further forwards. The best way to treat enemies is to make friends with them. Charm monsters! Uniques remain mostly impervious to your attempts to confuse them. Half the treasures of a vault could be crushed by an earthquake. Vaults are immune to destruction. A chaotic weapon could haunt you with earthquakes. Chaotic weapons tend now and then to change your adversaries seriously. Mutations may not increase your beauty but surely your repertoire! The thrum of serious risk-taking heightens the suspense. Hitchcock them! They pay you the compliment of being perfectly straight? Oops, a hero! Try to line up your opponents to dispatch them one after the other. Some orcish leaders are extremely keen to meet you quite soon. Gee! You say you were nearly vanqished by the trickery of priests? Run over the facts in your head before confronting special tough guys. Kind creatures? They make you stay put if your free action is unsustained. Are you ready to vanish discreetly when those packs are after you? Make sure you can keep your wits about you when Umber Hulks draw near. Magic doors make fine fences against weak masses of critters. Traps, Summoners, Zephyrs. The foreboding of evil grows heavier with stairs. Weak dangers? You may be overwhelmed by sheer numbers. Deep waters are a nice separation against some poisonous animals. Deep waters may contain poisonous animals. If you see deep water, a flood may be near. It is best to waylay heavy magic users in narrow passages around the corners. Heart is downcast? Huh, that's beyond even your accustomed folly. Courage! Dark is the Shadow and yet your heart rejoices. In the dreadful light you will stand aghast unless your are indeed an elf. How the means unforeseen are revealed whereby Oberon might be overthrown? Saroyan: Voyald is a way of saying Void, Voyage and World at the same time. Death, next to birth is our best gift, and next to truth it is our best friend. The greatest story-teller of all is time and change, or death. Sing Fury: "This is not the time to wonder, this is not the time to cry!" How hope beyond hopes is fulfilled is yours to stumble upon. Do not grudge his chance of peril him who advances beyond hopes. Thou shalt see what comes to him who sets his foolish webs before these feet. Tolkien said: "The dread of the Ringwraith cannot be shaken off." Fearless! The world will end if you Call the Void. Potion mimics are wicked summoners. Hurry up to knock them out, quickly! Potion mimics make good beverages. Full-scale priests look innocent but are among the nastiest sort of summoners. Summoners? Victory is slipping from your grasp even as you stretch out hands. Winning vault treasures is just like pulling chestnuts out of spreading fire. A dark full of nightmares? There is only one true Nightmare! Strength is crucial. Isn't witchcraft just an embroidery in a bard's tale? Whenever doubled over force yourself upright. Who wants a bad loser? Give heartened chase when your enemies turned tail. Last bit by missile! Vlad Dracula? Die and forget, since death is forbidden - to him, not you! Vlad Dracula? He hungers after you and thirstes for you! Luck and twice luck to meet a hydra if fearless and breath-shielded you are. All your misdeeds are engraved into the very flesh-score of time itself. Such a chaos spawn will disenchant your best gear in no time at all. Trickster Rinaldo. This memory comes to you unbidden, as bitter as always. The extremely ugly man-eater Grendel got his fame via Beowulf saga. You'd like anyone who sees you outlined in an archway will turn and run? Do not run when Barney approaches, and become a happily crumbling idiot. Boldor is just a self-opinionated piece of pomposity. But lots of companions! Morgoth is the very brink where hope and despair are akin. Give little heed to the wreck and slaughter that will lay around the pits. Accidents? what accidents? Mighty were your fallen ancestors. Fortune has betrayed you but for the momentum. Confidence! On the long run - IF you're survivor - winds of fortune will not wreck you. As a spell-striking egghead you'd need convincing offensive powers! Once no Recall is left the way up will seem never ending to you. The simple scheme ended in failure. That demon called lots of companions. Sing CCR: "Before you kill me take a look at yourself." Reflection-shields! Sing Pythons: "Always look on the bright sight of death." Only a breath away! Sing Beck: "I'm a loser, baby dragon, so why don't you kill me?" Suicide, ha! Sing Morisette: "Life has a funny way, helping you out." So innovate! Sing Garbage: "The trick is keep on breathing." Wear dragon scale mail! Sing Crowded House: "Always take your weather with you!" Yeah, have a breath. The game is serious. Catch it if you can. But you can leave your head on. A Grey Mushroom of Restoring will not restore your drained life experience. Close eXamination of splendid shop-wares will provide precious insights. Bought items come *identified*, knowledge secure from blank mind attacks. You even may transmit knowledge of *identified* items by means of stacking. What average mages get by *identify* the Stone will Tell the Nature adept. Malekith the Dark elf has a remarkable repertoire of spells to catch you. Zelazny said: "You are a living example of the absurdity of things." Zelazny said: "Whenever anything outrageous happens, there's a reason for it." Zelazny said: "Sometimes it's damned hard to tell the dancer from the dance." If it's not quite visible if you caught a gear curse look it up by Ctrl-C. Any lesser titan will not just summon a monster but several combos of them. Robin Hood nowadays leads a wretched existence as a trapper and master thief. Too many different Zephyrs are the heroes death. IF caught on open ground. If you ever confront a Hru have a Teleport Other at hand! Any Hru will rage, spreading earthquake and ruin. Only Shudde is worse. You cannot escape a Hru just by blinking. Your life will be shattered! If Amulets of Resistance don't stack it's caused by different additionals. Seek melee combat with breathing foes so your treasures won't get blasted! Master Vampires won't prey on your life blood only, but love to torture DEX. Hellhound Garm is defeatable if you fence out his summoned packs in advance. Mighty reptile Zoth-Ommog hates treasures and will crush any, leaving none! Drolems are not drolleries but draconic golems with high defenses. They bite! You can't get hold on Bully Gates, he charges you and is gone immediately. Never go near Bull Gates without maximum dexterity or lose lots of money! He cackles and sneers at you and throws traps as his confetti: Bully Gates! If the singular naga Jasra finds open ground she will call her hydras on you! You must capitalize (s)ecrets to find its Keepers. You'll really love them! Never wield a Glaive of Pain. The true name of Shudde is 747. But this will not help you sufficiently. Run! I balanced all, brought all to mind. In balance with this life, this death. A visit in the Inner Temple is less expensive than bundles of Restore potions. Trolls, Paladins or Bloodletters are fine to make money - if you top them. The goddess of cats drains dexterity dramatically. Don't forget to sustain it. Scrolls of artifact creation need a plain weapon/armour to enhance. The best way to fill a gear gap is by creating your own artifact. Iron liches are *very* deadly if you're not hard to breath and summons alike. Iron liches rust easily. Maulotaurs tend to rely on fire and overwhelming shattering strength. Prepare! Think out ways to get hold on some unusual nasty unique thieves. Ooh, Santa! They say that everyone has some skeletons in the cellar. Some are harmful! GHOST said: "I would not wish to add to your probable present paranoia." One of life's smaller puzzles is how to get control of the nearer futures. The King in Yellow is strong in help. But he is also quite jumpy by himself. Glaurung and all his kinsfolk - send them where the pepper grows! It's timing. The swamps south of the Elventown is worthy of some hunting excursions! You would be amazed of what a Greater Kraken is able to give to you. In deep waters any treasures will be lost! Lure them coastwards. Any means of digging are crucial for designing the battleground as You like! You may assault nasty Zephyr packs at doubled corners safe from their breath. Only permanent walls will prevent Ethearal or Deathdrakes to come after you. Druj are not good for you. Let sleeping wyverns lie. Sort of Pit-diver you are - those who seek after artifacts beyond the Rim. A jagged tentacle of the constantly shifting kind is moving toward your leg... Raal's? Ya might have to fool with it a long while just to figure it out. Pattern and Logrus, like reason and feeling, the wells of Powers mages draw on. Apollonian and Dionysiac, light and dark, Order and Chaos need one another. The principles game of light and dark is finally to be judged esthetically. An uncursed amulet of anti-teleportation supports Your choice of battleground. Try to get unknown to your old self. No compromising awhile. The inn has rot-gut with a kick that blows the wax out of your ears! Too bad who not supports innkeepers! YOU have a stillness which is dangerous. A moral cudgel is truly not an effective weapon against Farmer Maggot. Where are you going, unbeliever? The stairs down are but seemingly easy! You might end up in the grip of sinister forces who rule by torture n infamy? The gods love us, they even created birds to throw presents down. Anybody gets the beats s/he has ordered at Fortunas Court in the end. Pay no attention. Incomers are resented and regarded as fair game! At the Courts of Chaos poisonous feuds fester behind every corner. You seem inoffensive, a mere joker, a light-weight. But your eyes ..? Your knife has certainly hacked some bad meats. It's stained. The shrewd ones are never fooled. The weak mimic the strong habits. I see you have accustomed to plenty of local shopping snobberies. You won? You truly must have a brain as sharp as a woodsman's hatchet! Mimics: striking down men from behind certainly seems to be their trademark. He looks honest. This probably means he is a complete crook. Oh, I'm mocking. Flee while you can. Any decent adventurer has to honour the priorities. The average mage soon becomes accustomed to hard work and poor leisure. Are you sure to surpass your predecessors, being more than a mere brawler? Life: to mortgage yesterdays gains in order to move on to the next intermezzo. The art of dragon mail maintenance is: Never get stuck without an escape chance. Oh I see! Now I'm not good enough! Prices? Any complaints and you'll be bumped out faster than you can breathe. Prices? If you insist on haggling you'll never advance seriously! Haggling is the only way to get the best prices. Well, I tell ya, the dungeons landlords are mulling this over gloomily! Don't you think you are on a descending curve, young firehead? You are starting to loose your temper? So what? Gnaw your pistachios! It shrieks? Don't bother! Presumably there will be just innocent bystanders. Still you might still fade out again! Been seeing so many ghostly apparitions. Even if the bluff fails them never give up! The caves are hotbed of banditry. Before I settled down, in my younger days I won the arena price. Are you not the refined sensitive type you're disguised as? However you shouldn't want to upset sensitivities hereabout. Learn xenology! Critters? If action was taken early this conspiracy could be nipped in the bed. Nothin ever so awful as the advancing everchanging Spawns of Ubbo-Sathla! Do not behold the baleful visage of The Greater magic mushroom were-quylthulg! It is worth it to best the Greater hell magic mushroom were-quylthulg! Forget your childlike lore: mushrooms are not tasty but awholotta challenge! Hi matador, like to get gored to death by an infuriated gory minotaur? Never get caught by the notorious bloodhound Judge Porn-Starr, the beholder. Gorgons are fairy tales: mighty useful to get stone sculptures for the palace. Selling blessed weapons to a sympathizing temple is not too bad an idea. I'd prefer ball sorcery or breath ability to mass genocide for good reason. Scrolls of genocide will extinct annoying monster races from your level area. *Destruction* will cause a 15x15 area to change completely, but you undamaged. There are mostly only rumors about the effects of rarer ZAngband items. What does NETHER mean? The (arch.) N. regions are the world of the dead/hell. What does NEXUS mean? It refers to connexion/bond. Location/stats get unstable. Such a Blade of Chaos (chaotic) is quite a rare and precious finding. Isn't it a scandal that chaos patrons mostly grant inferior weapon gifts? Precious Blades of Chaos (6d5) are won by defeating a Bloodletter's platoon. Nether and Life draining are not just the same, as by breath or by touching? Examination of rings of nether resistance reveals two granted intrinsics. To stand your ground against adversaries who heal-self you'll need... Vampires are not the only ones who must flee when the Dawn approaches. It's the Grail Brotherhood who is behind it all! There is no such thing as 'fearless', not unless a man is mad. But hide it! When the monsters don't eat you, ooh, let's say you're home. The GAH does not exist. Young ones are made stupid, it's their protection against life's unkindness. And what about your due contribution to All-Fool's Day celebration? None? For the shared blood of your Ancestors you'll know about depth devilries. It takes a practiced eye to see through the glitter the rotten core of Thuringwethil. I saw dragon scale mail priced 11000 gp more for having +17 instead of +16. The strong rely on strength. The not-so-strong must mint means of shrewdness. If not your might might make tremble them and flee you need good spy ability. Pay heed to the Quylthulg race: invisible, powerful summoners, able to escape. One has a devil of a time who encounters the neversleeping quylthulgs. Your pet aversion against all sorts of hounds won't lead you to anything! An inertia hound is gorgeous dinner: giving pleasure and sleepy satisfaction. Patience at ambush will help to defeat many hounds. Without catching acid I have once eliminated 10 water hounds 1 by 1 in melee! Runes of protection require space to scribe. As shopkeepers rarely change you'll be glad about any high maximum merchants. It seems not smart to have your high bonus missiles destroyed by breathers. Against the most powerful of attacks you'll need a means of healing! If you cover your back by fencing or digging no summoned will surround you. It's deciding which things are crucial that separates the wise from others. Invulnerability is impenetrable. Invulnerability may be penetrated by evil creatures. Resistance to nether will guard against invulnerability-penetration. Creatures of good alignment are not deterred by invlnerability. Being clocked backwards is just a terrible irony of fate. Time is a weird attack - not even sustained stats will save you. Stumble on a death mold: another unsettling occurrence in an unsettling world. Too many errors and one's luck would run out at last. Errorowwww! The highest bonused ring of damage I have seen was +23! The highest bonused amulets of searching/brilliance I have seen was +6! Haven't you found a Ring of Extra Attacks (+5) yet? It's your own fault if you don't use all non-cheating means of awareness. Pay ANY price for a Rod of Perception for it saves you lots of scrolls/staffs. Rods seem somewhat secure from fire/acid/lightning attacks, but get stolen! Some black humored remarks (rumors) on common morals are not easily outwitted. Ever read Ambrose Bierce, From the devil's dictionary? No, didn't steal there. Irony is a sophisticated art of talk by saying some opposite. There are Uniques that are (friendly), but alas! they won't [yet] talk to you. Ents (friendly) are equally strong as Hrus and tend to clear the vault fields. You could hate an Ent (friendly) who picks up the dragon scale mails you left. Ahtu is somewhat the evil mirror of Treebeard. Beware to confront these foes! They say that there's work to be done, and no rest this side of Heaven. There are no guarantees in life, but it's smarter to take fewer chances. Artsi is not susceptible to sorcery, ideal warrior! If ever get him in melee. It's a crying shame, but 30000's the highest you'll ever sell an item for. There is a shopkeeper who may pay 50000 gold for an item. Is it really true that a good chain armour once rusted can't be restored? Never thought about that daylight might be not just an illumination? Stone Skin is valuable as diamonds: even the tougher foes will miss you. You WILL need some means to prevent to be teleported to and fro unwanted. Without a means to teleport away certain nuisances you'll have harder labor. Hypnos, Lord of Sleep, is an awful jumpy fellow; you have to hunt him down. If you ever, ever manage to best The Stormbringer you'll be amazed! The deeper you dive the more grateful you'll be about sustaining equipment. You'll love Reflection if you ever encounter those smashing halfling legions. Surprisingly not only crossbows use bolts that could bounce... At an early stage you will be quite happy to find rods of trap detection. There is but one use for charisma boosting items: shopkeeper charming. It's a happy moment when sufficient DEX makes you "grab hold of your backpack". Even a nasty Amberite blood curse could fail if you're lucky. Don't go deeper down the cliffs unless you find secure footholds (equipment). You'd like an illustration on "sowing dragon's teeth"? Try Sorcery tower Quest! Waste of time, the crying. Fight and live - fight and die, then enter renewed. The say that to want too much was stupidity, waste of precious time and effort. Down at 650' is an ill-omened place where the swift ochre jellies roam. Below 2500' you'll need sustained maximum spell stats to secure your heal-self. The miracle of Herbal Healing is like stories told by priests - amazing. A fallen Angel is likely to stay behind a stone obstacle in chequered pits. A screaming willingness to the flash and bang of damnation, freed of the suspence. They say that all these inevitable tombstones are but blessings in disguise. Survival challenges use up superabundant energies, burn off the gland-juices. Kittens. Always going for the dramatic. Watching too many adventure holos. Young Heroes-to-be are always kept on diet, to increase their aggressiveness. Free at last from the blood curse: mirth welled up, boiled over as pure laughter. If you encounter groups of usual solitary cats look out for Bast, Cat's godess! Even protected Books are destroyed by breathers of chaos balls. It is more secure to carry precious books yourself, IF you provide resistances. Do not cast stone-to-mud while your skin is made of stone. There are but sometimes shopkeepers that pay full price of 25000gp for a Book. Being hit by Cold breathes while carrying potions might speed and heal foes! You'll rarely get a chance to fight Fiona down and off, she loves her jumpiness! Why are Poison Resistance rings higher priced than even Disenchantment rings? Are there any cumulative effects by adding another means of resistance? Well? IF there are no free places around you, the worst summoning will not itch you. Summoners may summon ethereal beings even when you are cornered. You can win against the ever-aware quylthulgs by blocking their lines of sight. Any hounds are disastrous to containment by Door Building. Extinct them quickly! Regularly the god of the merchants is the same as that of the thieves. We are all natural born victims. Without information life is quite short. Information age has not just begun today! Hermes, Loki, Prometheus are tradition. Dispel scrolls will destroy any affected monster. Dispel scrolls are unreliable. Scrolls of Dispel Evil will remove or reverse bad enchantments. Scrolls of Fire, Ice, and Chaos are good in emergencies. You will not want to read scrolls of Fire, Ice, or Chaos. Scrolls of Ice deal twice the damage of Fire, but less than Chaos. To find a Rod of Havoc means nothing else than: chaos at your fingertips. If the sages speak of Curing this comprehends the recovery of 6! traumata. The traumata to suffer: Cut, Stun, Blind, Confused, Terrified, Hallucination. Staffs of Power will dispel any monster. Staffs of Power are almost as unpleasant as Raal's Tomes. Staffs of Holiness will dispel evil monsters and also protect you. Staffs of the Magi will help eggheads to clear the head and recover energies. Those who seek knowledge of the sources must be experienced hunters of hints. You have difficulties to find survey or details in the source files? Combat! There are always people who'd like to restrict knowledge to their own circles. It can't be bad to give knowledge to the people, even if by means of rumors. Those who'd exclude people of knowledge make up a priesthood of selfishness. You don't like the rumors you get? Write your own! (And share the good ones.) If potions of blindness are useful against foes they will be priced at shops. Rumors are sort of wishing wells. Not any waters seem drinkable for those not thirsty! There are unclean waters. Well, boil them up! Time heals all wounds?? But surely time wounds all heels! Some say the RNG itself lurks deep in the caverns. They say some space monsters are not what they appear to be. Never read a scroll that asks you to use an item. There is a powerful spell which can remove even the strongest curses... You feel the Windows (2000) on your hard disk is broken... zangband/lib/file/timefun.txt0000644000000000000000000000206710250356274015246 0ustar rootrootS:0000 E:0059 D:It's the witching hour! S:0100 E:0259 D:There may be Vampires around! S:0200 E:0359 D:Only Zangband players are up now! S:0400 E:0459 D:It's really *very* late! S:0500 E:0558 D:Aren't you sleepy yet? S:0559 E:0559 D:It doesn't matter what you found! S:0600 E:0759 D:The sun is up. Time to have fun! S:0601 E:0800 D:Are you having fun yet? S:0800 E:0905 D:@$#$@$!%@$#%$@&$^#%@$!^#&#* S:0800 E:1059 D:You feel there is something special about this level. S:1100 E:1159 D:You see a maze of twisty passages, all alike. S:1155 E:1200 D:Are you having fun yet? S:1159 E:1200 D:This fortune is broken! S:1200 E:1205 D:This fortune is still broken! S:1200 E:1359 D:Uh oh, now you've done it! S:1400 E:1729 D:What did you do with the Phial? S:1730 E:1744 D:You need your chocolate vitamin! S:1745 E:1759 D:Tornado Warning! S:1759 E:1800 D:Night is coming. Danger! Danger! S:1700 E:1859 D:Take a Vampire out for dinner! S:2100 E:2159 D:Warp Factor Nine. Now! S:2200 E:2359 D:PARTY! S:2359 E:2359 D:It's almost the witching hour! zangband/lib/file/w_high.txt0000755000000000000000000000217510250356274015047 0ustar rootrootN:*:Default 87 of Ultimate Might 'Slayer' 'World Ender' 'Destructor' of Destruction 'Impulse 255' 'Disintegrator' 'Vanquisher' 'BGF-9000' 'Mjollner' 'Hand of God' 'Deathstorm' 'Devil's Instrument' 'Word of God' 'Idkfa' 'Quietus' 'Wraithverge' 'Arc of Death' of Death 'Devastator' 'Excsymyr' 'Vorax' 'Excalibur' of Muramasa 'Heartseeker' 'Bloodscourge' 'Hand of Vecna' 'Unmaker' 'Apocalypse-Now' 'Armageddon' 'Devourer' 'Soul Searer' 'Abyss' 'Cosmic Horror' 'Dragon Fang' 'Nemesis' 'Doom!' 'Death Wish' 'Die Hard' 'Exodus' 'Terminator' 'Exterminator' 'Thunderstrike' 'Bloodwych' 'Inferno' 'Warhead' 'Red Storm' 'Dominator' 'Conqueror' 'Secret Weapon' 'Angel of Death' 'Destroying Angel' 'Black Star' 'Turma' 'Surma' 'Kalma' 'Right Hand of Justice' 'Fat Boy' 'Megadeath' of the Fourth Horseman 'Apocalyptica' 'Doomsday' 'Judgement of Heaven' 'Creeping Death' 'Eternity's End' 'Word of Death' 'Weapon Omega' 'Finalizer' of Fargoal of Bleys of Gerard of Benedict of Elric of Eric of Erlik 'Aeglin' 'Aeglos' of Beowulf of Orome 'Fatal Destiny' 'Jehovah's Wrath' 'Undying Samurai' 'Eternal Verities' 'Durindana' 'Joyeuse' of the Eagle Lord 'Azriel's Own' zangband/lib/file/makefile.zb0000644000000000000000000000115510250356274015145 0ustar rootrootsubdir = ./lib/file/ ## makefile.zb srcfiles += lib/file/makefile.zb files += \ lib/file/a_low.txt lib/file/crime.txt lib/file/elvish.txt \ lib/file/monfear.txt lib/file/news.txt lib/file/sample.txt \ lib/file/timenorm.txt lib/file/w_low.txt lib/file/a_cursed.txt \ lib/file/a_med.txt lib/file/dead.txt lib/file/error.txt \ lib/file/monfrien.txt lib/file/readme.txt lib/file/silly.txt \ lib/file/w_cursed.txt lib/file/w_med.txt lib/file/a_high.txt \ lib/file/chainswd.txt lib/file/death.txt lib/file/mondeath.txt \ lib/file/monspeak.txt lib/file/rumors.txt lib/file/timefun.txt \ lib/file/w_high.txt zangband/lib/help/0000755000000000000000000000000010250356274013042 5ustar rootrootzangband/lib/help/attack.hlp0000644000000000000000000000226510250356274015023 0ustar rootrootAttacking Monsters. Please choose one of the following online help files: (0) Attacking Monsters (attack.txt) (1) Attacking from a Distance (attack.txt#DistanceAttack) (2) Attacking Monsters in Walls (attack.txt#AttackWalls) (3) Body and Shield Bashes (attack.txt#Bashing) (4) Melee Weapons (attack.txt#MeleeWeapons) (5) Missile Launchers (attack.txt#MissileLaunch) (6) Ego Weapons and Artifacts (attack.txt#EgoArtifact) (7) Magical Aids to Physical Combat (attack.txt#MagicalAids) (8) Calculating Damage (attack.txt#DamageCalc) (9) Monk Attacks (attack.txt#MonkAttacks) (a) Basic Tactics (attack.txt#MeleeTactics) (?) Help System Commands (helpinfo.txt) ***** [0] attack.txt ***** [1] attack.txt#DistanceAttack ***** [2] attack.txt#AttackWalls ***** [3] attack.txt#Bashing ***** [4] attack.txt#MeleeWeapons ***** [5] attack.txt#MissileLaunch ***** [6] attack.txt#EgoArtifact ***** [7] attack.txt#MagicalAids ***** [8] attack.txt#DamageCalc ***** [9] attack.txt#MonkAttacks ***** [a] attack.txt#MeleeTactics zangband/lib/help/attack.txt0000755000000000000000000007130710250356274015065 0ustar rootroot=== Attacking Monsters === Attacking is simple in Zangband. If you move into a creature, you attack it. If you are wielding a weapon (including digging implements which are considered to be weapons) when you do so, the damage for the weapon is used when you hit a creature. Otherwise, you will attack with your bare hands which does minimal damage (unless you are playing a monk). Melee can do more damage per turn than any other form of attack, and the basic equipment (a weapon) is easy to find. On the other hand, melee only works against adjacent monsters and takes a great deal of training and equipment to come into its own deeper in the dungeon. Upgrading to weapons with higher base damages is vital but heavy weapons are harder to master. You will have to find a compromise, depending on class, experience level, and available equipment (use the 'C'haracter screen to see how various weapons affect your melee skill). You may wield both a primary weapon for melee combat, and a bow or other missile launcher for launching missiles at the same time. Most classes will benefit from carrying an assortment of attacking magical devices. ***** --- Attacking from a Distance --- You can attack creatures from a distance by firing a missile from a bow or other missile launcher, by throwing an object or by using magical items such as wands, rods and staves. If you have chosen to play a spell casting class, you may be able to learn some spells which allow you to attack a creature from a distance. You can use distance attacks even when your target is next to you. Whenever you give a command to fire a weapon, cast a spell, or use an attacking magical device (unless the spell or device has an area effect), you will be prompted for a direction. You may choose any of the eight movement directions or press '*' to enter targeting mode. A detailed explanation of targeting mode can be found in the section on Command Descriptions (see commdesc.txt#ThrowFire [1]). You may also wish to make use of the use_old_target option which automatically selects the last target. This prevents you from having to target the same monster every time you attack it. An explanation of this option is found the section on User Interface Options (see option.txt#UserInterface [2]). ***** --- Attacking Monsters in Walls --- You should note that some creatures, for example ghosts, can pass through the dungeon walls. When such a creature is in a wall, it can not be damaged by attacks which are normally stopped by walls (this includes most types of magical attacks). You can, however, attack a creature in a wall with your weapon by trying to move into the wall space which contains the creature. If the creature is invisible and you do not have the ability to see invisible creatures, you must tunnel into the wall space containing the creature. ***** --- Body and Shield Bashes --- If a creature is positioned next to you, you may bash it. Weight is the primary factor in being able to bash something, but strength plays a role too. If a shield is currently being worn, the bash is a shield bash and will do more damage. A successful bashing will damage your opponent and may throw an opponent off balance for a number of rounds, allowing a player to get in a free hit or more. Unfortunately, the converse is also true. Having a high dexterity reduces the chance of you being thrown off balance. This is a risky attack. Note: You will automatically do shield bashes during melee combat. These bashes have a chance to stun or confuse your opponent. ***** === Melee Weapons === Carrying a weapon in your backpack does you no good. You must wield a weapon before it can be used in a fight. A secondary weapon can be kept by keeping it in the backpack, and switching it with the primary weapon when needed. Zangband assumes that your youth in the rough environment near the dungeons has taught you the relative merits of different weapons, and displays as part of their description the damage dice which define their capabilities. The dice used for a given weapon is displayed as "#d#". The first number indicates how many dice to roll, and the second indicates how many sides they have. A "2d6" weapon will give damage from 2 to 12, before considering any other bonuses. The weight of a weapon is also a consideration. In addition to their base damage, each weapon has two main magical characteristics, their bonus to your skill and their bonus to your deadliness, expressed as "(+#,+#)". A normal weapon would be "(+0,+0)" but many weapons in Zangband have bonuses to your skill and/or deadliness. These bonuses may be increased, subject to certain upper limits, by magical means through a process referred to as 'enchanting'. Some weapons are cursed, and will have penalties that hurt the player. Cursed weapons cannot be unwielded until the curse is lifted. Note that identifying a weapon will inform you of the magical bonuses and penalties and whether or not it is cursed. Although you receive any magical bonuses an unidentified weapon may possess when you wield it, those bonuses will not be added in to the displayed values of skill and deadliness on your character sheet. You must identify the weapon before the displayed values reflect the real values used. ***** === Missile Launchers === Firing a missile while wielding the appropriate launcher is the only way to get the "full" power out of the missile. You may of course throw an arrow at a monster without firing it from a bow, but you will find the effects may not be what you had hoped. Slings will fire pebbles or shots, bows will fire arrows and crossbows will fire bolts. Missiles of varying type and quality can be bought in the town and may be found throughout the dungeon. Missile launchers, have their characteristics added to those of the missile used, if the proper weapon/missile combination is used, and the launcher multiplier is applied to the total damage, making missile weapons very powerful given the proper missiles. This is especially true if both the launcher and the missile are enchanted. Hits and misses are determined by your ability to hit versus your target's armor class. A hit is a strike that does some damage; a miss may in fact reach a target, but fails to do any damage. Higher armor class makes it harder to do damage, and so leads to more misses; it will also reduce the damage from a strike that actually occurs. You can find out roughly how much damage your missile weapons will do by looking at the (+#/+#) value on the ammunition in your inventory. The two numbers indicate the average damage done per shot, and average damage done per round. These numbers take into account whether or not you have identified your ammunition and launcher. The two numbers can differ because different launchers take differing energies to fire. The varying types of missile launchers each have their own strengths and weaknesses. Which can be summarized as follows: energy to fire multiplier remarks Sling: 50 2 Short Bow: 100 2 Long Bow: 100 2 strength < 16 100 3 strength >= 16 Light Crossbow: 120 4 Heavy Crossbow: 150 5 dexterity >= 16 200 5 dexterity < 16 Bows tend to be good at dealing constant streams of damage. A sling is good for killing many small monsters - it even does more damage per round than a short bow if you can carry enough ammunition. Crossbows deal enormous amounts of damage in one shot. However, the reload time is such that a longbow will deal more damage over time. Certain classes automatically receive additional shots as they become more experienced. Rangers receive an additional shot with a bow at level 20 and at level 40 and an additional shot with a crossbow at level 30. Rogues receive an extra shot with a sling at level 20 and at level 40 and an additional shot with a thrown dagger at level 10. Warriors receive an additional shot with any missile launcher at level 40. ***** == Ego Weapons and Artifacts === In addition to the ordinary weapons your character may find in the dungeon, some of them may be endowed with additional powers. These weapons fall into two types: (1) artifacts; and (2) ego weapons. Unlike artifacts which are unique and may only be found once in each game, it is not unusual to find several ego weapons of the same type during the course of a character's adventures. In general, artifacts and ego weapons may boost one or more of your primary statistics, may confer certain abilities upon your character, may grant resistance to certain forms of attack and may be especially deadly against certain types of creature. Take note that if your weapon has two attributes that make it deadly to your opponent (for example you are fighting a demon and your weapon slays both evil and demons (demons are evil)), only the most effective slay will apply. Zangband has extended the original Angband's concept of adding random abilities to the various Ego types considerably. These can be either guaranteed or have only a varying chance of being granted. (See 'Randabil.spo' for details of the random powers of Ego Weapons). (Defender) A magical weapon that actually helps the wielder defend himself, by increasing his/her armor class, and providing resistance against damage from fire, cold, acid and lightning attacks. It also grants levitation, increases your stealth, let you see invisible creatures and protects from paralyzation and some slowing attacks. It also helps you regenerate hit-points and mana faster. (Holy Avenger) A Holy Avenger is one of the more powerful of weapons. It will increase your wisdom and your armor class and prevent you from becoming afraid. This weapon will do extra damage when used against evil, demonic and undead creatures, and will also give you the ability to see invisible creatures. These weapons are also blessed and so can be wielded by priests with no penalty. Weapon of Westernesse A Weapon of Westernesse is one of the more powerful weapons. It slays orcs, trolls and giants while increasing your strength, dexterity, and constitution. It also allows you to see invisible creatures and protects from paralyzation and some slowing attacks. (Trump Weapon) A Trump Weapon is especially deadly against evil creatures and will increase your ability to discover hidden dungeon features. It will help you regenerate hit-points and mana faster and at the same time will reduce your rate of food consumption. It provides resistance to nexus and protects from paralyzation and some slowing attacks. In addition it may cause you to teleport randomly and can be activated for teleport once every 50+1d50 turns. (Pattern Weapon) A Pattern Weapon has been embedded with a fragment of the Pattern. It will increase your strength and constitution and also has a chance of increasing your dexterity. It is especially effective when used against evil, undead and demonic creatures. It will allow you to see invisible creatures and protects from paralyzation and some slowing attacks. (Blessed Blade) A blessed blade will increase your wisdom and can be wielded by priests with no penalty. Weapon of Extra Attacks These weapons will grant the user additional attacks per round. Weapon of Sharpness (edged weapon only) These are known to occasionally score vorpal hits (see below) and will also increase your ability to tunnel through the dungeon walls. Weapon of Earthquakes (hafted weapon only) These weapons may cause an earthquake when they strike an opponent which potentially may cause other monsters in the area to take damage from falling rocks and will destroy a small portion of the surrounding dungeon. They also increase your ability to tunnel through the dungeon walls. Weapon of Slaying These weapons have a chance of being granted unusually high damage dice. Implement of Digging These digging implements increase your ability to tunnel through the dungeon walls, and have the acid brand (see below). --- The Elemental and Other Brands --- (Chaotic) These bizarre, feared weapons have been manufactured in the Courts of Chaos, and are very unpredictable in combat often producing chaotic effects when they strike your opponent. Effects include *destruction*, teleport away and vampiric drain among others. A Chaotic weapon grants resistance to chaos attacks and cannot be damaged by acid, fire and electricity. (Vampiric) These foul weapons have been created by Death magic. They lust for blood, and if such a weapon scores a hit, it greedily sucks life from the hapless victim, transferring the life energy to its master and healing them in the process. Weapon of Melting A magical weapon of acid that will inflict two times the normal damage when used against a creature that is not resistant to acid. It also provides resistance against acid attacks. Weapon of Shocking A magical weapon of lightning that will inflict two times the normal damage when used against a creature that is not resistant to electricity. It also provides resistance against lightning attacks. Weapon of Freezing A magical weapon of ice that will inflict two times the normal damage when used against a creature that is not resistant to cold. It also provides resistance against cold attacks. Weapon of Burning A magical weapon of fire that will inflict two times the normal damage when used against a creature that is not resistant to fire. It also provides resistance against fire attacks. Weapon of Poisoning A magical weapon, coated with poison, that will inflict two times the normal damage to creatures not resistant to poison. It also provides resistance against toxic and poisonous attacks. --- Weapons of Slay {Monster-Type} --- Weapon of Slay Animal This weapon is especially effective against natural creatures and will do 1.7 times the normal damage against such creatures. Weapon of Slay Evil This weapon is especially effective against evil creatures and will do 1.7 times the normal damage against such creatures. Weapon of Slay Undead This weapon is especially effective against undead creatures and will do two times the normal damage against such creatures. It will also provide resistance to life draining attacks. Weapon of Slay Demon This weapon is especially effective against demonic creatures and will do two times the normal damage against such creatures. Weapon of Slay Orc This weapon is especially effective against orcs and will do two times the normal damage against such creatures. Weapon of Slay Troll This weapon is especially effective against trolls and will do three two the normal damage against such creatures. Weapon of Slay Giant This weapon is especially effective against giant humanoids and will do two times the normal damage against such creatures. Weapon of Slay Dragon This weapon is especially effective against dragons and will do three two the normal damage against such creatures. --- Weapons of *Slay* {Monster-Type} --- Weapon of *Slay* Animal This weapon is especially effective against natural creatures and will do 1.7 times the normal damage against such creatures. It will also increase your intelligence and allow you to regenerate hit-points and mana faster. Weapon of *Slay* Evil This weapon is especially effective against evil creatures and will do 1.7 times the normal damage against such creatures. It will increase your wisdom and will also be a blessed blade. Weapon of *Slay* Undead This weapon is especially effective against undead creatures and will do two times the normal damage against such creatures. It will increase your wisdom and will also provide resistance to both nether and life-draining attacks. Finally, it will allow you to see invisible creatures. Weapon of *Slay* Demon This weapon is especially effective against demonic creatures and will do two times the normal damage against such creatures. It will also increase your intelligence. Weapon of *Slay* Orc This weapon is especially effective against orcs and will do two times the normal damage against such creatures. It will also increase your dexterity. Weapon of *Slay* Troll This weapon is especially effective against trolls and will do two times the normal damage against such creatures. It will also increase your strength. Weapon of *Slay* Giant This weapon is especially effective against giant humanoids and will do two times the normal damage against such creatures. It will also increase your strength. Weapon of *Slay* Dragon This weapon is especially effective against dragons and will do three times the normal damage against such creatures. It will also increase your constitution. --- Missile Launchers --- Launcher of Accuracy These missile launchers have an unusually high bonus to hit. Launcher of Velocity These missile launchers have an unusually high bonus to dam. Launcher of Extra Might These missile launchers have an unusually high damage multiplier. Launcher of Extra Shots These missile launchers grant additional shots per round. --- Ammunition --- Ammunition of Hurt Animal This ammunition is especially effective against natural creatures and will do 1.7 times the normal damage against such creatures. Ammunition of Hurt Evil This ammunition is especially effective against evil creatures and will do 1.7 times the normal damage against such creatures. Ammunition of Hurt Dragon This ammunition is especially effective against dragons and will do two times the normal damage against such creatures. Ammunition of Shocking This ammunition will inflict two times the normal damage when used against a creature that is not resistant to electricity. Ammunition of Flame This ammunition will inflict two times the normal damage when used against a creature that is not resistant to fire. Ammunition of Frost This ammunition will inflict two times the normal damage when used against a creature that is not resistant to cold. Ammunition of Slaying This ammunition will have unusually large damage dice. Ammunition of Wounding This ammunition will have unusually bonuses +to-hit and +to-dam. --- Other Items --- Apart from these there are some very rare and well made blades in the dungeon. These include Blades of Chaos (which grant resistance to chaos), Maces of Disruption (which are especially effective against undead creatures) and Diamond Edges and Scythes of Slicing (which may both score vorpal hits). Note on Vorpal Weapons: A weapon with the vorpal flag will have a 1-in-6 chance of doing additional damage each time it strikes. If it passes the roll it has a chance of doing it again. This continues until a roll is failed. The calculations are nasty but the net effect is an average 22% increase in damage output. ***** === Magical Aids to Physical Combat === There are several magical means of increasing your physical combat ability. The most common of these are potions of heroism and berserk strength and various scrolls (blessing, holy prayer, etc.). Typically, these grant small cumulative bonuses to your combat skill. Some magic realms contain equivalent spells. ***** === Calculating Damage === Unlike standard Angband, however you attack a monster, whether in melee or by firing or throwing missiles, the weapon or object's base number of damage dice are cumulatively multiplied by any and all applicable modifiers. Actual damage is determined by rolling the final number of dice. The biggest conceptual difference is how Skill and Deadliness (formerly known as to-hit and to-damage respectively) work: Combat Skill: (formerly called the plus to hit) Your combat skill affects your ability to hit a monster, and also determines how often you get critical hits. The more skilled you are, the better those critical hits. You know you have scored a critical hit when you get any combat message other than "you hit" (or punch) "the ". Deadliness: (formerly called the plus to damage) Deadliness acts as a percentage bonus to damage (you may inspect your current bonus on the character screen). It is not unusual for high-level characters to have bonuses in excess of 200%, and therefore triple the number of dice they roll on every blow with the Deadliness multiplier alone. ***** --- Melee --- The formula for calculating the damage done by a weapon whose base damage dice are XdY is as follows: Z = X * (bonus from any applicable slays/brands) * (the critical multiplier (if any)) * (bonus from your deadliness percentage) Then roll a Y-sided dice Z times adding each result to give the total damage. Example 1 --------- You hit a Troll with a dagger (1d4) there is one damage die : 1x damage it's a weapon of Slay Troll : 2x damage you get a critical hit for 2x damage : 2x damage you have a total bonus to Deadliness of 200% : 2x damage Thus, Z = 1 * 2 * 2 * 2 = 8 Rolling a four-sided dice 8 times yields an average damage of 20 per blow. ***** --- Bare Handed Combat --- The calculation for bare handed combat is identical to that of melee combat. With the exception of the monk class, it is assumed that your hands will do a base damage of 1d1. Thus you will get two 1d1 attacks per round. Monk bare handed combat is addressed separately below. ***** --- Missile Launchers --- The formula for calculating the damage done by a missile launcher firing ammunition whose base damage dice are XdY is as follows: Z = X * (missile launcher damage multiplier) * (bonus from any applicable slays/brands) * (the critical multiplier (if any)) * (bonus from your deadliness percentage) Then roll a Y-sided dice Z times adding each result to give the total damage. Example 1 --------- You use a long bow to hit a Dragon with an arrow (3d4) there are three damage dice : 3x damage its a long bow, and you have high strength : 3x damage it's an arrow of Hurt Dragon : 2x damage you don't get a critical hit : 1x damage you have a total bonus to Deadliness of 200% : 2x damage Thus, Z = 3 * 3 * 2 * 1 * 2 = 36 Rolling a four-sided dice 36 times yields an average damage of 90 per blow. ***** --- Throwing --- Throwing and firing are broadly similar, with five main differences: firstly, only throwing weapons have a damage multiplier, which increases as you gain experience levels (it ranges from 4 to 12). Secondly, no thrown object other than the special throwing weapons may take advantage of bonuses to Skill or Deadliness granted by your equipment. Thirdly, only throwing weapons can get critical hits when thrown. Fourthly, thrown objects may break, but throwing weapons only do so rarely. Finally, you may never throw more than one object per round. Note that throwing weapons are very rare... The formula for calculating the damage done by a thrown object whose base damage dice are XdY is as follows: Z = X * (throwing damage multiplier (if any)) * (bonus from any applicable slays/brands) * (the critical multiplier (if any)) * (bonus from your deadliness percentage) Then roll a Y-sided dice Z times adding each result to give the total damage. Example 1 --------- You throw a spear (1d6) to hit an Orc and at your level throwing weapons receive a 6x multiplier. there is one damage die : 1x damage its a throwing weapon : 6x damage no applicable slay or brand : 1x damage you get a critical hit for 3x damage : 3x damage you have a total bonus to Deadliness of 200% : 2x damage Thus, Z = 1 * 6 * 1 * 3 * 2 = 36 Rolling a six sided dice 36 times yields an average damage of 126 per blow. Example 2 --------- You throw a flail (2d6) to hit an Orc and at your level throwing weapons receive a 2x multiplier. there are two damage dice : 2x damage its not a throwing weapon : 1x damage no applicable slay or brand : 1x damage no critical hit (its not a throwing weapon) : 1x damage no Deadliness bonus (its not a throwing weapon) : 1x damage Thus, Z = 2 * 1 * 1 * 1 * 1 = 2 Rolling a six-sided dice 2 times yields an average damage of 7. ***** === Monk Attacks === The Monk character is designed to be a barehanded fighter rather than using a weapon like the other Zangband classes. As a Monk's level increases the number of attacks they get per round increases and new, increasingly powerful attacks become available. Higher level attacks have a chance to stun the Monk's opponent. While the type of attack that a Monk uses for each blow is chosen at random from the list of available attacks, at higher levels there is a bias towards the attacks which do greater damage. This is because at these levels, the game will roll several times for each blow with the highest attack type chosen. ***** --- Monks Attack Types --- Attack Name Min.lvl Damage Stun Notes ----------------------------------------------------------------------- Punch 1 1d4 - Kick 2 1d5 - Strike 3 1d6 - Knee 5 2d3 * Likely to stun male opponents Elbow 7 1d7 - Butt 9 2d4 - Ankle Kick 11 2d5 - May slow down the opponent Uppercut 13 3d5 6 Double-kick 16 6d3 8 Cat's Claw 20 4d6 - Jump Kick 25 4d7 10 Eagle's Claw 29 5d6 - Circle Kick 33 5d8 10 Iron Fist 37 6d8 10 Flying Kick 41 7d8 12 Dragon Fist 45 7d10 16 Crushing Blow 48 7d12 18 ***** === Basic Tactics === Pillardancing ------------- Requires that you be at least twice as fast as the monster you are fighting. Find a single block of wall, freestanding, and lure your enemy to it. When both you and your enemy are standing next to the pillar, hit him, and then move so that you are opposite the pillar from him. He will use his turn to move so that he's standing next to you. Hit him again, and then move again. Repeat until he's dead. Note: Some monsters move erratically, and cannot be relied upon to move in the method expected. Also, some monsters (mostly Ghosts) can move through walls, and a small number of monsters can chew through walls. Shoot'n Scoot ------------- Requires a large room, Phase Door, and some type of missile weapon. Stand at one end of the room, your enemy at the other. Fire your missile weapon at him until he gets close, and then Phase Door. Fire again, until he gets close, and repeat. By the time you run out of ammunition, he should be dead or weak enough for you to finish him HTH (Hand to Hand). Hack'n Back ----------- Requires that you be at least twice as fast as your opponent. Stand next to your enemy, hit him, and back up. He should use his turn to move towards you instead of using a missile weapon or a spell. Hit him again, back up again, repeat. This is a little more dangerous than Pillardancing, because the monster gets a chance to breathe or cast a spell, but it's easier to set up. Wail'n Bail ----------- Requires Teleport items. Fight the monster until you're almost dead, teleport out, find him, and resume fighting. This is dangerous, because you could teleport right next to some nasty that will kill you. Also, it is not generally useful for killing unique monsters, as they regenerate damage very quickly, and by the time you find them again, they will have healed what you did to them. The Anti-Summoning Corridor --------------------------- Requires a little time to set up. This can be done just about anywhere. Dig a twisting corridor into the rock, and station yourself at one end of it. When your opponent arrives, he won't be able to summon any monsters next to you. This is a very important technique for fighting many higher-end monsters which very quickly bring in a horde of other monsters. -- Original : (??) and Leon Marrick Updated : (??) Updated : Zangband DevTeam Last update: May 25, 2001 ***** Begin Hyperlinks ***** [1] commdesc.txt#ThrowFire ***** [2] option.txt#UserInterface zangband/lib/help/birth.hlp0000644000000000000000000000227010250356274014660 0ustar rootrootCreating a Character. Please choose one of the following online help files: (0) Creating a Character (birth.txt) (1) Birth Options (birth.txt#BirthOptions) (2) Character Attributes (birth.txt#CharAttributes) (3) Race Class Combinations (birth.txt#RaceClassComb) (4) Choosing Your Magic Realm (birth.txt#ChoosingMagic) (5) Class/Realm Restrictions (birth.txt#Restrictions) (6) Random Quests (birth.txt#RandomQuests) (7) The Auto-Roller (birth.txt#AutoRoller) (8) Point-based Character Generation (birth.txt#PointBased) (9) Naming Your Character (birth.txt#CharName) (a) Starting Inventory (birth.txt#StartInventory) (?) Help System Commands (helpinfo.txt) ***** [0] birth.txt ***** [1] birth.txt#BirthOptions ***** [2] birth.txt#CharAttributes ***** [3] birth.txt#RaceClassComb ***** [4] birth.txt#ChoosingMagic ***** [5] birth.txt#Restrictions ***** [6] birth.txt#RandomQuests ***** [7] birth.txt#AutoRoller ***** [8] birth.txt#PointBased ***** [9] birth.txt#CharName ***** [a] birth.txt#StartInventory zangband/lib/help/birth.txt0000755000000000000000000003316010250356274014721 0ustar rootroot=== Creating a Character === Zangband is a roleplaying game, in which you, the player, control a character in the world of Zangband. Perhaps the most important thing you control is the birth of your character, in which you choose or allow to be chosen various attributes that will affect the future life of your character. Character creation, or birth, is controlled through a variety of choices as to constraints on the type of character you wish to play, followed by a series of random calculations to generate ("roll up") a random character matching the appropriate constraints. Once your character has been generated, you will be given the choice to generate a new character obeying the same constraints, and once you have generated more than one character, you can switch back and forth between the two most recent characters, until you are presented with a character that you feel comfortable with. You may start the entire process over at any time by pressing 'ESC' at any prompt (with the exception of the autoroller (see below [1]) prompt and the prompt for the number of random quests (see below [2])). Also, on the final screen, use delete or backspace to restart. ***** === Birth Options === During character generation you may press '=' at any time to access the options screen. A more detailed description of the various options can be found in the section on the Options Page (see option.txt [3]). Here you may set yout 'Birth options' which are options set at birth and which cannot be changed again during the game. Note that you may change your birth option preferences in game but these will only have effect in your next character. Perhaps one of the most important features of the birth options is the ability to decide what type of town level you want. The various town options are discussed on the Town page (see town.txt#TownLevel [4]). Another important decision to make is whether you wish to use the Autoroller (see below [1]) or the point- based character generation system (see below [d]). ***** === Character Attributes === Once you begin character generation you will be asked to choose your character's three primary attributes - its sex, race and class. If you have selected a spellcasting class, you will also be prompted for your choice of magic realm(s). Your character's sex has a minimal effect on game play - females start with slightly more gold, males are generally heavier and so can bash more effectively. Race, class and magic realms have a far more significant effect and are discussed at some length in the Race (see charattr.txt#TheRaces [5]), Class (see charattr.txt#TheClasses [6]) and Magic Realms (see magic.txt#MagicRealms [7]) sections. --- Secondary Attributes --- Each character has a few secondary attributes, height, weight, social class, and background history, which are randomly determined, but which are affected by the sex and race of the character. In general, these attributes are only used to provide "flavor" to the character, to assist in the roll playing, but they do have a few minor effects on the game. For example, background history affects social class, which affects the amount of money the character will start with. And weight affects carrying capacity and bashing ability. ***** === Race/Class Combinations === Once a race has been chosen, you will need to pick a class. Some race/class combinations are not recommended and so certain classes are shown inside brackets. This may be because the combination is not conceptually sound or because the chosen race has stat penalties in areas where that class needs bonuses. However, any race/class combination can be chosen and experienced players may often choose unusual combinations for the challenge that they represent. It is recommended that inexperienced players choose warriors as spellcasting requires a player more familiar with dungeon survival techniques. The following table shows which classes are recommended for the various races. Warrior Mage Priest Rogue Ranger Paladin Warrior Chaos -Mage Warrior Human Yes Yes Yes Yes Yes Yes Yes Yes Half-Elf Yes Yes Yes Yes Yes Yes Yes Yes Elf Yes Yes Yes Yes Yes No Yes No Hobbit Yes Yes No Yes No No No No Gnome Yes Yes Yes Yes No No No No Dwarf Yes No Yes No No No No No Half-Orc Yes No Yes Yes No No No Yes Half-Troll Yes No Yes No No No No No Amberite Yes Yes Yes Yes Yes Yes Yes Yes High-Elf Yes Yes Yes Yes Yes No Yes No Barbarian Yes No Yes Yes Yes No No Yes Half-Ogre Yes Yes Yes No No No No No Half-Giant Yes No No No Yes No No No Half-Titan Yes Yes Yes No No Yes No No Cyclops Yes No Yes No No No No No Yeek Yes Yes Yes Yes No No No No Klackon Yes No No No Yes No No No Kobold Yes No No Yes No No No No Nibelung Yes Yes Yes Yes No No No No Dark Elf Yes Yes Yes Yes Yes No Yes Yes Draconian Yes Yes Yes No No No Yes No Mind Flayer No Yes Yes No No No Yes No Imp Yes Yes No Yes No No Yes Yes Golem Yes No No No No No No No Skeleton Yes Yes Yes Yes No No No No Zombie Yes No No No No No No No Vampire Yes Yes Yes Yes Yes Yes Yes Yes Spectre No Yes Yes Yes No No Yes No Sprite No Yes Yes Yes Yes No Yes No Beastman Yes Yes Yes Yes No No Yes Yes Mind- High Monk crafter Mage Human Yes Yes Yes Half-Elf Yes Yes Yes Elf Yes Yes Yes Hobbit No No Yes Gnome No Yes Yes Dwarf No No No Half-Orc Yes No No Half-Troll No No No Amberite Yes Yes Yes High-Elf Yes Yes Yes Barbarian No No No Half-Ogre No No Yes Half-Giant No No No Half-Titan Yes Yes Yes Cyclops No No No Yeek No Yes Yes Klackon No No No Kobold No No No Nibelung No No Yes Dark Elf Yes Yes Yes Draconian Yes Yes Yes Mind Flayer Yes Yes Yes Imp Yes Yes Yes Golem No No No Skeleton Yes Yes Yes Zombie No No No Vampire Yes Yes Yes Spectre Yes Yes Yes Sprite No Yes Yes Beastman Yes Yes Yes ***** === Choosing Your Magic Realm(s) === If you have selected a spellcasting class, you will next be prompted for your choice of magic realm(s). The magic system, as implemented in Zangband, consists of seven realms: Life, Arcane, Sorcery, Nature, Trump, Chaos and Death. In general, Life is primarily defensive but also offers spells to attack evil creatures, Arcane offers utility spells and some limited offensive capability, Sorcery offers utility and defensive spells, Nature offers both defensive and offensive spells, Trump specializes in teleportation and summoning spells and Chaos and Death are offensive. A more complete description of the magic realms and spellcasting in general can be found in the section on the Magic Realms (see magic.txt#MagicRealms [7]). Where possible, it is generally a good idea to pick one defensive realm and one offensive realm. If you pick the realms always in the same order (e.g. nature as your first realm and chaos as your second realm, not the other way around) you will be less confused when trying to pick the correct spellbook to use in the game. ***** --- Class/Realm Restrictions --- In Zangband, spellcasting classes can select either one or two realms from those available. Some classes which can learn two realms may learn their first realm 'better' than their second. Note that certain realms may be prohibited for some classes. In the table below, '1st' indicates that the realm may only be the first choice, '2nd' indicates that the realm may only be the second choice and '1st/2nd' indicates that it can be either. Classes that only have '1st' options do not get a second realm. Classes with only one '1st' option must choose this option as their first realm. Class Life Arcane Sorcery Nature Trump Chaos Death ---------------------------------------------------------------------- Mage 1st/2nd 1st/2nd 1st/2nd 1st/2nd 1st/2nd 1st/2nd 1st/2nd Priest 1st 2nd 2nd 2nd 2nd 2nd 1st Rogue 1st 1st 1st 1st Ranger 2nd 2nd 1st 2nd 2nd 2nd Paladin 1st 1st Warrior-Mage 2nd 1st 2nd 2nd 2nd 2nd 2nd Chaos Warrior 1st Monk 1st 1st 1st High Mage 1st 1st 1st 1st 1st 1st 1st ***** === RandomQuests === Once you have chosen your race, class and (if applicable) your magic realm(s), you will be asked how many random quests you wish to be assigned. You may choose any number between 0 and 49. A more detailed discussion of random quests can be found in the Dungeon section (see dungeon.txt#RandomQuests [8]). ***** === The Auto-Roller === The auto-roller is a quick means of generating start-up characters based on a set of user-selected criteria. If you choose to use the auto-roller, you will be prompted to enter a weighting from 1-100 for each stat. These weightings should give how important you think it is for each stat to be close to the maximum possible for your character. Once you have entered your desired statistics, the computer will then randomly roll successive start-up characters and rate them based on the weightings you provided. Each stat is rolled as a number from 8 to 17, with a normal distribution, and is then immediately modified based upon the race and class which you have chosen. The exact quantum of this modification can be found in the Character Attributes section (see charattr.txt#StatBonusTable [a]). ***** After a fixed number of character rolls, the computer will select the character with the highest rating and display it for you to look at. If you accept the rolled character (by pressing 'Esc'), you will be asked for its name (see below [c]). If not, you may press 'r' to resume rolling and generate another set of character or, if this is not your first set, 'p' to return to the previous character which was selected. If you select not to use the auto-roller, the computer will roll one random character at a time and then display it for you to either accept or reject. The 'r', 'p' and 'Esc' keys will work as outlined above. ***** === Point-based Character Generation === This alternative method of determining your starting statistics gives you 48 'points' which may be 'spent' on increasing your statistics according to your own preference. Using this system you are better able to customize your character but may not get such high individual statistics as are possible with th auto-roller. Note that as your stats get higher it takes more points to increase them further. Also note that should you choose not to spend all your points, you will be given 100 gold pieces for each unused point subject to an upper limit of 600 gold pieces. ***** === Naming Your Character === Once you have accepted a character you will be asked to provide a name for the character. In general, the actual choice of a name is not important, but do keep in mind that it may have some effect on the game itself. For example, on some machines, the character name determines the filename that will be used to save the character to disk. On others, the character name specifies special "pref" files. And the character name is used on the high score list. ***** === Starting Inventory === Once you have named your character, you will be prompted to press 'Esc' and, having done so, you will be brought to the town screen from where you will begin your adventuring. You should note that each character starts in the town with a small number of items in their inventory. Which items you are given will depend upon your chosen race and class and the number of such items depends on chance. For example, all classes receive either food rations or scrolls of satisfy hunger but the number of rations or scrolls received is random. Typically, you will receive a weapon, a piece of armor, some food, some torches and a magical item although there is some variance from this. -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: Feb 15, 2002 ***** Begin Hyperlinks ***** [1] birth.txt#AutoRoller ***** [2] birth.txt#RandomQuests ***** [3] option.txt ***** [4] town.txt#TownLevel ***** [5] charattr.txt#TheRaces ***** [6] charattr.txt#TheClasses ***** [7] magic.txt#MagicRealms ***** [8] dungeon.txt#RandomQuests ***** [9] charattr.txt#PrimaryStats ***** [a] charattr.txt#StatBonusTable ***** [b] birth.txt#LifeRating ***** [c] birth.txt#CharName ***** [d] birth.txt#PointBased zangband/lib/help/bldg.txt0000755000000000000000000000202310250356274014513 0ustar rootroot=== Functional Town Overview === The town is composed of both stores and buildings. Classical stores: Magic Shop: buy your wands, staffs, rings and amulets here. Alchemist: for all sorts of bubbling potions and scrolls. Weaponsmith: they deal in anything sharp and to the point. Armorer: to offer protection from the ravages of the dungeon. General Store: food, torches, ammo, some necessities. Temple Trade: those items permitted to the pious in life. Black Market: the prices are usurious, but some depths items! Home: to store some of your precious treasures. Additional buildings: Weaponmaster: Compares two weapons to allow you to see which is better. Zymurgist: Will identify items in your pack and recharge wands and staves. Magesmith (weapon): Will enchant your weapon. Magesmith (armor): Will enchant your armor. Mutalist: Will add or cure mutations. Map Maker: Will provide a map of part of the surrounding wilderness. All buildings are made of stone and unlikely to move around. This file is accurate for Zangband 2.5.3 zangband/lib/help/charattr.hlp0000644000000000000000000000146610250356274015366 0ustar rootrootCharacter Attributes. Please choose one of the following online help files: (0) Creating a Character (charattr.txt) (1) The Races (charattr.txt#TheRaces) (2) The Classes (charattr.txt#TheClasses) (3) Primary Statistics (charattr.txt#PrimaryStats) (4) Primary Skills (charattr.txt#PrimarySkills) (5) Stat Bonus Tables (charattr.txt#StatBonusTable) (6) Ability Tables (charattr.txt#SkillBonusTable) (?) Help System Commands (helpinfo.txt) ***** [0] charattr.txt ***** [1] charattr.txt#TheRaces ***** [2] charattr.txt#TheClasses ***** [3] charattr.txt#PrimaryStats ***** [4] charattr.txt#PrimarySkills ***** [5] charattr.txt#StatBonusTable ***** [6] charattr.txt#SkillBonusTable zangband/lib/help/charattr.txt0000644000000000000000000014464310250356274015427 0ustar rootroot=== Character Attributes === During the process of Character Generation (see birth.txt [1]), you will select your character's three primary attributes - its sex, race and class. If you select a spellcasting class, you will also make a choice of magic realms at that time. Your character will be randomly assigned a number of other attributes such as height, weight, social class, and background history Your choices as to sex, race, class and realm are irrevocable and will remain fixed for the entire life of that character. The only exception to this, is that the Chaos spell 'Polymorph Self' (and the mutation of the same name) may cause your race to change as one of its possible effects. In addition to these attributes, there are several statistics which are used to determine your character's relative skills and abilities as follows: --- Primary Statistics --- Each character has six primary statistics or 'stats'. These are strength, intelligence, wisdom, dexterity, constitution and charisma, which modify the abilities of the character in a variety of ways. For example, strength affects your carrying capacity, the amount of damage you to a monster when you hit it and the number of blows per round you get with a weapon. A more complete discussion of the primary statistics is contained later in this document (see below [2]). --- Experience --- Experience affects almost everything else about your character. Experience can be gained as your character kills monsters, casts spells or prays for the first time, learns about an object kind by using it, disarms traps and unlocks doors. Certain classes may also gain experience by destroying specific dungeon spell books and there are potions in the dungeon that will boost your experience if you quaff them. When your character's experience crosses certain fixed boundaries, you will attain a new experience level (up to a maximum of 50). When this happens, your hitpoints, mana (if any), certain skills such as melee fighting and bows and throws will all increase. Some races and classes will also gain new powers and abilities when crossing certain experience thresholds. Deep down inside, the real objective of the game is to increase your experience, and certain other characteristics, and also to collect useful items, to give you a decent chance against the great Serpent of Chaos. Certain monsters can "drain" your experience, and thus your level, which will cause you to lose all of the effects of the higher level. Luckily, you can restore drained experience through magical means, or by simply regaining the experience all over again. --- Gold (AU) --- Each character has some gold, which can be used to buy items and services from the shops and other buildings on the town level. Gold can be obtained by selling items to the shops, taking it from the corpses of dead monsters, mining it and by finding it lying on the dungeon floor. Each character starts out with some gold, the amount of which is based on the character's social class, charisma, sex (female characters start with more gold), and other stats (less powerful characters start with more gold). Each character also starts out with a few useful items, which may be kept, or sold to a shop-keeper for more gold. --- Armor Class --- Each character has an armor class, representing how well the character can avoid damage. Your armor class is affected by your dexterity and your equipment. A more detailed discussion of Armor Class can be found in the Combat section (see attack.txt#Armor [3]). --- Hit Points --- Each character has hit points, representing how much damage the character can sustain before he dies. Your hit points are derived from your race, class, level, and constitution, and can be boosted by magical means. Hit points may be regained by resting, or by a variety of magical means. --- Spell Points (Mana) --- Each character has spell points, or mana, which represents how many spells (or prayers) a character can cast (or pray). Your spell points (sometimes called mana) are derived from your class, level and intelligence (for spells) or wisdom (for prayers). Spell points may be regained by resting, or by a few magical means. --- Character Skills --- Each character also has several primary skills: disarming, magic devices, saving throws, stealth, searching, perception, melee and bows and throws, which are derived from the character's race, class, experience level, stats and their current equipment. These skills have fairly obvious effects, but will be described more completely below. The starting abilities of a character are based upon race and class. Abilities may be adjusted by high or low stats, and may increase with the level of the character (see below for details [4]). Each character may have one or more racially intrinsic skills and racial abilities, which may also include special resistances and activations. ***** === Gender === You character may be male or female. Males tend to be heavier but there are no significant gameplay differences between the two sexes. ***** === Races === There are thirty different races that you can choose from in Zangband. Each race has various strengths and weaknesses and its own adjustments to a character's stats and abilities. Many races also have intrinsic abilities and powers. Human The human is the base character. All other races are compared to them. Humans can choose any class and are average at everything. Humans tend to go up levels faster than any other race because of their shorter life spans. No racial adjustments or intrinsics occur to characters choosing human. Half-Elf Half-elves tend to be smarter and faster than a human, but not as strong. Half-elves are slightly better at searching, disarming, saving throws, stealth, bows, and magic, but they are not as good at hand weapons. Half-elves may choose any class and do not receive any intrinsic abilities. Elf Elves are better magicians then humans, but not as good at fighting. They tend to be smarter than either humans or half-elves and also have better wisdom. Elves are better at searching, disarming, perception, stealth, bows, and magic, but they are not as good at hand weapons. They resist light effects intrinsically. Hobbit Hobbits, or Halflings, are very good at bows, throwing, and have good saving throws. They also are very good at searching, disarming, perception, and stealth; so they make excellent rogues (but prefer to be called burglars). They will be much weaker than humans, and no good at melee fighting. Halflings have fair infravision, so they can detect warm creatures at a distance. They have their dexterity sustained. They are very fond of food, and learn, in due time, to cook a delicious meal from available ingredients. Gnome Gnomes are smaller than dwarves but larger than Halflings. They, like the Halflings, live in the earth in burrow-like homes. Gnomes make excellent mages, and have very good saving throws. They are good at searching, disarming, perception, and stealth. They have lower strength than humans so they are not very good at fighting with hand weapons. Gnomes have fair infra-vision, so they can detect warm-blooded creatures at a distance. Gnomes are protected intrinsically against paralysis and some slowing effects. At higher levels, Gnomes learn to teleport at will. Dwarf Dwarves are the headstrong miners and fighters of legend. They tend to be stronger and tougher but slower and less intelligent than humans. Because they are so headstrong and are somewhat wise, they resist spells which are cast on them. Dwarves also have very good infra-vision because they live underground. They do have one big drawback, though. Dwarves are loudmouthed and proud, singing in loud voices, arguing with themselves for no good reason, screaming out challenges at imagined foes. In other words, dwarves have miserable stealth. They can never be blinded. Dwarves also learn to study the structure of a dungeon, and can spot things that go unseen by the other races. Half-Orc Half-Orcs make excellent warriors and decent priests, but are terrible at magic. They are as bad as dwarves at stealth, and horrible at searching, disarming, and perception. Half-Orcs are, let's face it, ugly. They tend to pay more for goods in town. Half-Orcs do make good warriors and rogues, for the simple reason that Half-Orcs tend to have great constitutions and lots of hit points. Because of their preference to living underground to on the surface, Half-Orcs resist darkness attacks. A Half-Orc will learn to dispel any fear that may be upon him or her. Half-Troll Half-Trolls are incredibly strong, and have more hit points than most other races. They are also very stupid and slow. They will make great warriors and iffy priests. They are bad at searching, disarming, perception, and stealth. They are so ugly that a Half-Orc grimaces in their presence. They also happen to be fun to run... Half-trolls always have their strength sustained. At higher levels, Half-Trolls learn to enter a berserk fury, and regenerate wounds automatically. Amberites The Amberites are a reputedly immortal race, who are endowed with numerous advantages in addition to their longevity. They are very tough and their constitution cannot be reduced, and their ability to heal wounds far surpasses that of any other race. Having seen virtually everything, very little is new to them, and they gain levels much slower than the other races. But should they advance high enough, they will learn the innate Amberite powers of Pattern Mindwalking and Shadow Shifting. High-Elf High-elves are a race of immortal beings dating from the beginning of time. They are masters of all skills, and are strong and intelligent, although their wisdom is sometimes suspect. They can play most classes very well. High-elves begin their lives able to see the unseen, and resist light effects just like regular elves. However, there are few things that they have not seen already, and experience is very hard for them to gain. Barbarian Barbarians are hardy men of the north. They are fierce in combat, and their wrath is feared throughout the world. Combat is their life: they feel no fear, and they learn to enter battle frenzy at will even sooner than half-trolls. Barbarians are, however, suspicious of magic, which makes magic devices fairly hard for them to use and they are thus poorly suited to the spellcasting classes. Half-Ogre Half-Ogres are like Half-Orcs, only more so. They are big, bad, and stupid. For warriors, they have all the necessary attributes, and they can even become wizards: after all, they are related to Ogre Magi, from whom they have learned the skill of setting trapped runes once their level is high enough. Like Half-Orcs, they resist darkness, and like Half-Trolls, they have their strength sustained. Half-Giant Half-Giants are not too unusual, as there has been a tradition according to which it is a noble and brave thing to do to consort a giant (especially a giant-maid). Nevertheless, the poor offspring of such a union is seldom very popular in the world of men. Their limited intelligence makes it difficult for them to become full spellcasters, but with their huge strength they make excellent warriors. No ordinary wall can withstand the fury of a giant, or a half-giant, and at higher levels they can learn the power of magical digging. Their thick skin makes them resistant to shards, and like Half-Ogres and Half-Trolls, they have their strength sustained. Half-Titan Half-mortal descendants of the mighty titans, these immensely powerful creatures put almost any other race to shame. They may lack the fascinating special powers of certain other races, but their enhanced attributes more than make up for that. They learn to estimate the strengths of their foes, and their love for law and order makes them resistant to the effects of Chaos. Cyclops With but one eye, a Cyclops can see more than many with two eyes. They are headstrong, and loud noises bother them very little. They are not quite qualified for the magic using professions, but as a certain Mr. Ulysses can testify, their accuracy with thrown rocks can be deadly... Yeek Yeeks are among the most pathetic creatures. Fortunately, their horrible screams can scare away less confident foes, and their skin becomes more and more resistant to acid, as they gain experience. But having said that, even a mediocre monster can wipe the proverbial floor with an unwary Yeek. Klackon Klackons are bizarre semi-intelligent ant-like insectoid creatures. They make great fighters, but their mental abilities are severely limited. Obedient and well-ordered, they can never be confused. They are also very nimble, and become faster as they advance levels. They are also very acidic, inherently resisting acid, and capable of spitting acid at higher levels. Kobold Kobolds are a weak goblin race. They love poisoned weapons, and can learn to throw poisoned darts (of which they carry an unlimited supply). They are also inherently resistant to poison, and can become adequate fighters, although they are not one of the more powerful races. Nibelung The hated and persecuted race of nocturnal dwarves, these cave-dwellers are not much bothered by darkness. Their natural inclination to magical items has made them immune to effects which could drain away magical energy, and like ordinary dwarves, they can examine the dungeon to discover traps and secret doors. Dark Elf Another dark, cave-dwelling race, likewise unhampered by darkness attacks, the Dark Elves have a long tradition and knowledge of magic. With their intelligence they can become superb mages or priests, and they have an inherent magic missile attack available to them at a low level. With their keen sight, they also learn to see invisible things as their relatives High-Elves do, but at a higher level. Draconian A humanoid race with dragon-like attributes. As they advance levels, they gain new elemental resistances (up to Poison Resistance), and they also have a breath weapon, which becomes more powerful with experience. The exact type of the breath weapon depends on the Draconian's class and level. With their wings, they can easily escape any pit trap unharmed. Mind Flayer A secretive and mysterious ancient race. Their civilization may well be older than any other on our planet, and their intelligence and wisdom are naturally sustained, and are so great that they enable Mind Flayers to become more powerful spellcasters than any other race, even if their physical attributes are a good deal less admirable. As they advance levels, they gain the powers of See Invisible, Telepathy and a mind blast attack. Imp A demon-creature from the nether-world, naturally resistant to fire attacks, and capable of learning fire bolt and fire ball attacks. They are little loved by other races, but can perform fairly well in most professions. Golem A Golem is an artificial creature, built from a lifeless raw material like clay, and awakened to life. They are nearly mindless, making them useless for professions which rely on magic, but as warriors they are very tough. They are resistant to poison, they can see invisible things, and move freely. At higher levels, they also become resistant to attacks which threaten to drain away their life force. They also learn to temporarily turn their skin into a very hard, stonelike substance. In the most recent version, Golems gain very little nutrition from ordinary food. They need to collect scrolls of satisfy hunger, or perish of exhaustion when the life force animating their body runs out. In the most recent version Golems also gain natural armor class bonus from their tough body. Skeleton There are two types of skeletons: the ordinary, warrior-like skeletons, and the spell-using skeletons, which are also called liches. As undead beings, skeletons need to worry very little about poison or attacks that can drain life. They do not really use eyes for perceiving things, and are thus not fooled by invisibility. Their bones are resistant to shard attacks (not much to cut there), and they will quickly become resistant to cold. Should a skeleton be unlucky enough to lose some of his or her remaining life, he or she will learn to restore it at will. It is very hard for skeletons to eat food or drink potions. Although the magical effects of these will affect the skeleton even without entering the skeleton's (non-existent) belly, the potion or food itself will fall through the skeleton's jaws, giving no nutritional benefit. Zombie Much like Skeletons, Zombies too are undead horrors: they are resistant to life-draining attacks, and can learn to restore their life-force. Like skeletons, they become resistant to cold-based attacks (actually earlier than skeletons), resist poison and can see invisible. While still vulnerable to cuts (unlike skeletons), Zombies are resistant to Nether. Like Golems, they gain very little nutrition from the food of mortals. However, Zombies are, as the name implies, practically mindless: in this company, Groo would seem a genius. Vampire One of the mightier undead creatures, the Vampire is an awe-inspiring sight. Yet this mighty creature has a serious weakness: the bright rays of sun are its bane, and it will need to flee the surface to the deep recesses of earth until the sun finally sets. Darkness, on the other hand, only makes the Vampire stronger and they are unharmed by darkness attacks. As undead, the Vampire has a firm hold on its life force, and resists nether attacks. The Vampire also resists cold and poison based attacks. It is, however, susceptible to its perpetual hunger for fresh blood, which can only be satiated by sucking the blood from a nearby monster, which is the Vampire's special power. It should be noted that the vampires are so sensitive to daylight that even certain artifact light items which are filled with daylight will hurt them if they try to wield the items. Fortunately, the vampires do not really need these items, since they radiate an aura of 'dark light' of their own. Light resistance will, in any case, protect the vampire from the adverse effects of sunlight. Spectre Another powerful undead creature: the Spectre is a ghastly apparition, surrounded by an unearthly green glow. They exist only partially on our plane of existence: half-corporeal, they can pass through walls, although the density of the wall will hurt them in the process of doing this. The Spectre can scream an eldritch howl, which is enough to scare lesser monsters witless. As undead, they have a firm hold on their life force, see invisible, and resist poison and cold. They also resist nether; in fact, their half-corporeal form actually grows stronger from the effects of nether. At higher levels they develop telepathic abilities. Spectres make superb spellcasters, but their physical form is very weak. Like Golems and Zombies, Spectres gain almost no nutrition from ordinary food. Sprite One of the several fairy races, Sprites are very small. They have tiny wings, and can fly over traps that may open up beneath them. They enjoy sunlight intensely, and need worry little about light based attacks. Although physically among the weakest races, Sprites are very talented in magic, and can become highly skilled wizards. Sprites have the special power of spraying Sleeping Dust, and at higher levels they learn to fly faster. Beastman This race is a blasphemous abomination produced by Chaos. It is not an independent race but rather a humanoid creature, most often a human, twisted by the Chaos, or a nightmarish crossbreed of a human and a beast. All Beastmen are accustomed to Chaos so much that they are untroubled by confusion and sound, although raw logrus can still have effects on them. Beastmen revel in chaos, as it twists them more and more. Beastmen are subject to mutations: when they have been created, they receive a random mutation. After that, every time they advance a level they have a little chance of gaining yet another mutation. Ghoul This race of undead preys upon the dead and dying. Ghouls can learn to sense living creatures around them and, having killed them, can feast upon their corpses gaining nourishment. As an undead being, Ghouls naturally resist the effects of cold, poison and life-draining attacks. As they grow stronger the effects of darkness and nether upon them decreases. In addition, the Ghoul's touch may paralyze some oponents. Ghoul's make adequate fighters and poor spell casters. ***** === Classes === There are eleven different classes that you can choose from in Zangband. Each class has various strengths and weaknesses and its own adjustments to a character's stats and abilities. Many classes also have intrinsic abilities and powers. These are often linked to the character's experience level and only become available later in the game. --- The Classes --- Warrior A Warrior is a hack-and-slash character, who solves most of his problems by cutting them to pieces, but will occasionally fall back on the help of a magical device. Unfortunately, many high-level devices may be forever beyond their use. A warrior's prime statistics are his or her Strength, Dexterity and Constitution. A Warrior will be good at Fighting and Throwing/Bows, but bad at most other skills. Warriors cannot learn magic and gain experience for destroying high level spellbooks. As a warrior's experience increases, he becomes more proficient with his weapons gaining an additional attack per round with his missile weapon. As his skill improves, a warrior will become more confident in his ability to defeat his opponents and eventually will become resistant to fear attacks. Mage A Mage is a spell caster that must live by his wits as he cannot hope to simply hack his way through the dungeon like a warrior. In addition to his spellbooks, a mage should carry a range of magical devices to help him in his endeavors which he can master far more easily than anyone else. A mage is also better able to resist the effects of spells cast at him by his enemies. A mage's prime statistic is Intelligence as this determines his spell casting ability. Good Wisdom and Dexterity also help. There is no rule that says a mage cannot become a good fighter, but spells are the mage's true strength. With two notable exceptions, mages should avoid wearing armor on their hands as this can restrict their spell casting ability. Unlike other spellcasting classes, mages can freely choose any two magic realms, although they will never be as good at Life magic as a priest. Otherwise, mages tend to learn and cast all the spells in their realms better than any other character except the high mage who has concentrated his efforts so as to excel in a single realm. Priest A Priest is a character devoted to serving a higher power. They explore the dungeon in the service of their God and if treasure just happens to fall into their packs, well, so much more to the glory of their religion. A Priest's primary stat is Wisdom since this determine his success at praying to his deity. Since Priests receive new prayers as gifts from their patron deity, they cannot choose which ones they will learn. Priests are familiar with magical devices which they believe act as foci for divine intervention in the natural order of things, but are not as good as a mage in their use. Priests are good at resisting spells cast at them and make decent fighters but prefer blunt weapons over edged ones. A priest wielding an edged weapon will be so uncomfortable with it that his fighting ability will be affected unless it has first been blessed by the Gods. High level priests who practice Life magic will learn to invoke the power of their patron deity to bless such weapons. There are two types of priests in Zangband: the ordinary priests who, select Life magic as their primary realm, and the 'dark' priests, who select Death magic instead. Since the natural inclination of priests is towards Life Magic, priests who select Life magic will be able to learn their prayers faster and better than their evil colleagues. Priests can also select a secondary realm from the other five realms, and should be able to learn all spells in it although not as efficiently as mages. Rogue A Rogue is a character that prefers to live by his cunning, but is capable of fighting his way out of a tight spot. Rogues are good at locating hidden traps and doors and are the masters of disarming traps and picking locks. A rogue has a high stealth allowing him to sneak around many creatures without having to fight, or to get in a telling first blow. A rogue may also backstab a fleeing monster. A rogue is better than a warrior or paladin with magical devices, but still cannot rely on their performance. Rogues can also learn a few spells from a choice of four realms, but not the powerful offensive spells magi can use. A rogue's primary statistics are Intelligence and Dexterity but Strength and Constitution are important too. There are several subtypes of Rogues in Zangband and the exact type is determined by the realm of magic chosen. The common Thief, will probably be content with Arcane magic and its wide applicability. The Burglar, on the other hand, is more interested in the Sorcery spells, which allow him or her to do the job fast and efficiently. Assassins' partiality for Death magic is well known, and they are feared for it. Finally, there is the Card Shark, who will opt for Trump magic, and shuffles the decks with amazing proficiency. As a rogue increases in experience, his proficiency with the sling improves and he will ultimately gain two additional shots per round with his favorite missile launcher. Ranger A Ranger is a combination of a warrior and a mage who has developed a special affinity for the natural world around him. He is a good fighter and the best of the classes with a missile weapon such as a bow. A ranger has a good stealth, good perception, good searching, a good saving throw and is good with magical devices. A Ranger's primary stats are Intelligence and Dexterity since these affect his spell casting ability and his ability with his bow but, as a fighter, Strength and Constitution are important too. As a ranger's experience increases, so does his skill with his primary weapon - the bow and he will learn to notch and loose arrows very quickly. His ability with a cross bow will similarly increase but not to the same extent. Unfortunately, because a ranger is really a dual class character, more experience is required for him to advance through the levels. All rangers are trained in Nature magic, and all Nature spells are available to them. They even learn these spells almost as fast as mages. They can also select a secondary realm and may choose from any realm except Life magic, but they are slow learners of the second realm, and may find themselves unable to learn some of the highest level spells. Paladin A Paladin is a combination of a warrior and a priest. Paladins are very good fighters, second only to the warrior class, but not very good at missile weapons. A paladin lacks much in the way of abilities. He is poor at stealth, perception, searching, and magical devices but has a decent saving throw due to his divine alliance. A paladin's primary stats are Strength, Dexterity, Constitution and Wisdom since he must both fight and pray for divine intervention. The paladin receives prayers at a slower pace then the priest, but can receive even the most powerful prayers although at a higher cost and fail rate. Unlike priests, Paladins do not learn a second magic realm. Because a paladin is really a dual class character, more experience is required for him to advance through the levels. There are two types of Paladins: those trained in Life magic and their evil counterparts (the 'Death Knights') who are trained in Death magic. An 'ordinary' paladin will gain experience for destroying high-level spellbooks from all the magic realms except Life. A Death Knight, on the other hand, is very tolerant of the other realms and will only gain experience for destroying high-level Life books. As a Paladin gains in experience he will become more confident in his abilities to defeat his enemies and in his deity's power to protect him. As a result, Paladins become resistant to fear at higher levels. Warrior-Mage A Warrior-Mage is precisely what the name suggests: a cross between the warrior and mage classes. While their brothers, the rangers, specialize in Nature magic and survival skills, true Warrior-Mages attempt to reach the best of both worlds. As warriors they are much superior to the usual Mage class. The Warrior-Mage is recommended for the players who want to cast spells but whose mages tend to die too quickly. However, the power does not come without a price as Warrior-Mages require more experience to advance levels than any other class. Warrior-mages begin the game with Arcane magic, and they can freely select another realm of magic. Although they do not gain new spells as fast as regular mages, they will eventually learn every spell in both realms, thus making a very competitive choice for players who appreciate Arcane magic. Chaos-Warrior Chaos Warriors are the feared servants of the terrible Demon Lords of Chaos. Every Chaos Warrior has a Patron Demon and, when gaining a level, may receive a reward from his Patron. He might be healed or polymorphed, his stats could be increased, or he might be rewarded with an awesome weapon. His Patron Demon might, for some reason, get annoyed with him and do something fairly nasty like surround him with monsters, drain his stats or wreck his equipment or they might simply ignore him. The Demon Lords of Chaos are chaotic and unpredictable indeed. The exact type of reward depends on both the Patron Demon (different Demons give different rewards) and chance. Chaos Warriors are, as one might expect, trained in Chaos magic. They are not interested in any other form of magic. They can learn every Chaos spell. As a chaos-warrior gains in experience, he becomes more confident of his ability to defeat his enemies and will learn to resist fear at higher levels. In addition, as a result of their prolonged service to the Demon Lords of Chaos, a chaos-warrior will eventually become resistant to the effects of chaos. Monk The Monk character class is very different from all other classes. Although they can use weapons and armor just like any other class, their training in martial arts makes them much more powerful with no armor or weapons. To gain the resistances necessary for survival at higher levels a monk may need to wear some kind of armor, but if the armor he wears is too heavy, it will severely disturb his martial arts maneuvers. As a monk gains in experience he learns, new, powerful forms of attack and is able to land more blows per round. His defensive capabilities increase likewise. Fortunately, the amount of armor a monk can wear, while still fighting efficiently, also increases with experience. In addition, the monk's agility allows him to resist paralyzing attacks once he reaches a high enough level (but only if his armor is not restricting his movement). Monk's are able to move quickly and will become faster and be able to strike more quickly as they gain experience. The different sects of monks are devoted to different areas of magic. The typical monk is interested in the harmony of nature, and studies Nature magic. An idealist monk would select Life magic, and try to work to benefit his neighbor. But there are also dark monks, who practice Death magic. A monk will eventually learn all prayers in the discipline of their choice. Mindcrafter The Mindcrafter is a unique class that uses the powers of the mind instead of magic. These powers are unique to Mindcrafters, and vary from simple extrasensory powers to mental domination of others. Since these powers are developed by the practice of certain disciplines, a Mindcrafter requires no spellbooks to use them. The available powers are simply determined by the character's level. A Mindcrafter's primary stat is Wisdom since this is used to determine how well he / she can perform the psychic powers, and in combat a Mindcrafter is roughly the equivalent of a priest. Unlike the priest, however, a Mindcrafter is never penalized for wielding an edged weapon. Although the powers of a Mindcrafter may seem like magic, this is not strictly speaking the case. They are mental powers, independent of the ordinary sources of magic. Consequently, Mindcrafters are not interested in 'magic' and learn no spells or prayers. As a Mindcrafter's experience increases, so does his ability to control his body with his mind as do his mental powers. A mindcrafter will learn to control his fear early in his career and to resist becoming confused at higher levels. His practice of mental discipline will mean that eventually his wisdom will be sustained. Very experienced mindcrafters will gain the power of telepathy. High Mage High mages are mages who specialize in one particular field of magic and learn it very well - much better than the ordinary mage. For the price of giving up a second realm of magic, they gain substantial benefits in the mana costs, minimum levels, and failure rates in the spells of the realm of their specialty. A high mage's prime statistic is intelligence as this determines his spell casting ability. Good wisdom and dexterity also help. There is no rule that says a high mage cannot become a good fighter, but spells are the mage's true strength. With two notable exceptions, high mages should avoid wearing armor on their hands as this can restrict their spell casting ability. High mages may freely choose any realm but it should be noted that a high mage specializing in Life Magic will not learn it as well as a priest. ***** === Primary Statistcs === Each character has six primary "stats", strength, intelligence, wisdom, dexterity, constitution, and charisma, which modify the abilities of the character in a variety of ways. Every stat has a numerical value, ranging from a minimum of 3, up to a normal maximum of 18, and even higher, into the "percentile" range, represented as "18/01" through "18/100". Actually, every stat can be raised even above 18/100 by magical means, up to a pure maximum of 18/220, which is represented as "18/***". Traditionally, a percentile stat such as "18/50" has been thought of as representing a value part way between 18 and 19, and this is one way to think of them. However, often, the best way to view the "bonus" values after the "18/" is as "tenth" points, since it often takes the same magic to raise a stat from, say, 4 to 5, or 16 to 17, as it does from, say, 18/40 to 18/50. The important thing to remember is that almost all internal calculations "ignore" the final digit of any "bonus", so that, for example, "18/40" and "18/49" are always have the same effects. --- The Primary Statistics --- Strength Strength is critical to fighting effectively in melee combat and with missile weapons. A high strength will improve your chances of getting multiple blows with your melee weapon and, in addition, will dramatically increase the amount of damage done with each hit. Strength also has a marginal effect on your chance to hit your opponent. Characters with low strength may receive penalties. Strength is also useful in tunneling, bashing and in carrying heavy items without being slowed down. Intelligence Intelligence affects the spellcasting abilities of mage-like spellcasters (high mages, mages, warrior-mages, rangers, chaos warriors and rogues). Intelligence will affect the number of spells these classes may learn each level, the number of spell points they receive and their spell fail rates. These classes cannot learn spells if their intelligence is 7 or lower. Also, intelligent characters are better at using magic devices, picking locks and disarming traps. Wisdom Wisdom affects the ability of priest-like spellcasters (priests, paladins and monks) to use prayers. Wisdom will affect the number of spells these classes may learn each level, the number of spell points they receive and their spell fail rates. In addition, wisdom is also used to determine a mindcrafter's ability to use his or her mental powers. These classes cannot learn spells if their wisdom is 7 or lower. Wise character's will have better chances of resisting magical spells cast upon them by monsters. Dexterity Dexterity is a combination of agility and quickness. A high dexterity may allow a character to get multiple blows with lighter weapons, thus greatly increasing his kill power, and will increase his chances of hitting with any weapon and dodging blows from enemies. Dexterity is also useful in picking locks, disarming traps, and protecting yourself from some of the thieves that inhabit the dungeons. Constitution Constitution is a character's ability to resist damage to his body, and to recover from damage received. Therefore a character with a high constitution will receive more hit points and also recover them faster while resting. Charisma Charisma represents a character's personality and physical appearance. A character with a high charisma will receive better prices from store owners, whereas a character with a very low charisma may be robbed blind. A high charisma will also mean more starting money for the character. Charisma is also used when calculating the success of a mindcrafter at dominating a monster. ***** === Primary Skills === Characters possess some different abilities which can help them to survive. The starting abilities of a character are based upon race and class. Abilities may be adjusted by high or low stats, and may increase with the level of the character with the rate of increase dependent upon the level of the character. Melee Melee is the ability to hit and do damage with weapons or fists. Normally a character gets a single blow from any weapon, but if his dexterity and strength are high enough, he may receive more blows per round with lighter weapons. Strength and dexterity both modify the ability to hit an opponent. This skill increases with the level of the character. Bows and Throws Using ranged missile weapons (and throwing objects) is included in this skill. Different stats apply to different weapons, but this ability may modify the distance an object is thrown/fired, the amount of damage done, and the ability to hit a creature. This skill increases with the level of the character. Saving Throws A Saving Throw is the ability of a character to resist the effects of a spell cast on him by another person/creature. This does not include spells cast on the player by his own stupidity, such as quaffing a nasty potion. This ability increases with the level of the character, but then most high level creatures are better at casting spells, so it tends to even out. A high wisdom also increases this ability. Stealth The ability to move silently about is very useful. Characters with good stealth can usually surprise their opponents, gaining the first blow. Also, creatures may fail to notice a stealthy character entirely, allowing a player to avoid certain fights. This skill is based entirely upon race and class, and will never improve unless magically enhanced. Disarming Disarming is the ability to remove traps (safely), and includes picking locks on traps and doors. A successful disarming will gain the character some experience. A trap must be found before it can be disarmed. Dexterity and intelligence both modify the ability to disarm, and this ability increases with the level of the character. Magical Devices Using a magical device such as a wand or staff requires experience and knowledge. Spell users such as magi and priests are therefore much better at using a magical device than say a warrior. This skill is modified by intelligence, and increases with the level of the character. Perception (Searching Frequency) Perception is the ability to notice something without actively seeking it out. This skill is based entirely upon race and class, and will never improve unless magically enhanced. Searching (Searching Ability) To search is to actively look for secret doors, floor traps, and traps on chests. Rogues are the best at searching, but magi, rangers, and priests are also good at it. This skill is based entirely upon race and class, and will never improve unless magically enhanced. Infra-vision Infra-vision is the ability to see heat sources. Since most of the dungeon is cool or cold, infra-vision will not allow the player to see walls and objects. Infra-vision will allow a character to see any warm-blooded creatures up to a certain distance. This ability works equally well with or with out a light source. The majority of Zangband's creatures are cold-blooded, and will not be detected unless lit up by a light source. Most non human races have innate infra-vision ability. Human can gain infra-vision only if it is magically enhanced. ***** === Stat Bonus Tables === Each of the races and classes has certain modifications to their starting statistics an experience penalty. The experience penalty is designed to 'balance' the races with better starting statistics and abilities by requiring them to earn more experience before advancing a level. --- Table 1 - Race Statistic Bonus Table --- STR INT WIS DEX CON CHR Hit Dice Exp Penalty Human 0 0 0 0 0 0 10 +0% Half-Elf -1 +1 +1 +1 -1 +1 9 +10% Elf -1 +2 +2 +1 -2 +2 8 +20% Hobbit -2 +2 +1 +3 +2 +1 7 +10% Gnome -1 +2 0 +2 +1 -2 8 +35% Dwarf +2 -2 +2 -2 +2 -3 11 +35% Half-Orc +2 -1 0 0 +1 -4 10 +10% Half-Troll +4 -4 -2 -4 +3 -6 12 +37% Amberite +1 +2 +2 +2 +3 +2 10 +125% High-Elf +1 +3 +2 +3 +1 +5 10 +100% Barbarian +3 -2 -1 +1 +2 -2 11 +20% Half-Ogre +3 -1 -1 -1 +3 -3 12 +30% Half-Giant +4 -2 -2 -2 +3 -3 13 +50% Half-Titan +5 +1 +1 -2 +3 +1 14 +155% Cyclops +4 -3 -3 -3 +4 -6 13 +30% Yeek -2 +1 +1 +1 -2 -7 7 +0% Klackon +2 -1 -1 +1 +2 -2 12 +35% Kobold +1 -1 0 +1 0 -4 9 +25% Nibelung +1 -1 +2 0 +2 -4 11 +35% Dark Elf -1 +3 +2 +2 -2 +1 9 +50% Draconian +2 +1 +1 +1 +2 -3 11 +150% Mind Flayer -3 +4 +4 0 -2 -5 9 +40% Imp -1 -1 -1 +1 +2 -3 10 +10% Golem +4 -5 -5 -2 +4 -4 12 +100% Skeleton 0 -2 -2 0 +1 -4 10 +45% Zombie +2 -6 -6 +1 +4 -5 13 +35% Vampire +3 +3 -1 -1 +1 +2 11 +100% Spectre -5 +4 +4 +2 -3 -6 7 +80% Sprite -4 +3 +3 +3 -2 +2 7 +75% Beastman +2 -2 -1 -1 +2 -4 11 +40% Ghoul 0 -1 -1 -1 +1 -5 9 +125% --- Table 2 - Class Statistic Bonus Table --- STR INT WIS DEX CON CHR Hit Dice Exp Penalty Warrior +5 -2 -2 +2 +2 -1 +9 +0% Mage -5 +3 0 +1 -2 +1 0 +30% Priest -1 -3 +3 -1 0 +2 +2 +20% Rogue +2 +1 -2 +3 +1 -1 +6 +25% Ranger +2 +2 0 +1 +1 +1 +4 +30% Paladin +3 -3 +1 0 +2 +2 +6 +35% Warrior-Mage +2 +2 0 +1 0 +1 +4 +50% Chaos Warrior +2 +1 0 +1 +2 -2 +5 +35 Monk +2 -1 +1 +3 +2 +1 +6 +40% Mindcrafter -1 0 +3 -1 -1 +2 +2 +25% High Mage -5 +4 0 0 -2 +1 0 +30% ***** === Ability Tables === Each of the races and classes has certain modifications to their starting abilities. Players may also receive an additional level-based bonus to certain skills which is based on their class. For example mages improve their magical device skill more rapidly than warriors. As noted above, some skills will not improve unless magically enhanced. --- Table 1 - Race Skill Bonus Table --- Dsrm Dvce Save Stlh Srch Prcp Melee Bows Infra Human +0 +0 +0 +0 +0 +10 +0 +0 None Half-Elf +2 +3 +3 +1 +6 +11 -2 +3 20 feet Elf +5 +6 +6 +2 +8 +12 -6 +6 30 feet Hobbit +15 +18 +18 +5 +12 +15 -11 +6 40 feet Gnome +10 +12 +12 +3 +6 +13 -8 +0 40 feet Dwarf +2 +9 +10 -1 +7 +10 +7 +0 50 feet Half-Orc -3 -3 -3 -1 +0 +7 +3 -3 30 feet Half-Troll -5 -8 -8 -2 -1 +5 +10 -5 30 feet Amberite +4 +5 +5 +2 +3 +13 +6 +0 None High-Elf +4 +20 +20 +4 +3 +14 +0 +10 40 feet Barbarian -2 -10 +2 -1 +1 +7 +10 +0 None Half-Ogre -3 -5 -5 -2 -1 +5 +12 +0 30 feet Half-Giant -6 -8 -6 -2 -1 +5 +13 +2 30 feet Half-Titan -5 +5 +2 -2 +1 +8 +13 +0 None Cyclops -4 -5 -5 -2 -2 +5 +10 +6 10 feet Yeek +2 +4 +10 +3 +5 +15 -5 -5 20 feet Klackon +10 +5 +5 +0 -1 +10 +5 +5 20 feet Kobold -2 -3 -2 -1 +1 +8 +8 -8 30 feet Nibelung +3 +5 +10 +1 +5 +10 +5 +0 50 feet Dark Elf +5 +15 +20 +3 +8 +12 -5 +7 50 feet Draconian -2 +5 +3 +0 +1 +10 +5 +5 20 feet Mind Flayer +10 +25 +15 +2 +5 +12 -8 -5 40 feet Imp -3 +2 -1 +1 -1 +10 +5 -5 30 feet Golem -5 -5 +10 -1 -1 +8 +10 +0 40 feet Skeleton -5 -5 +5 -1 -1 +8 +8 +0 20 feet Zombie -5 -5 +8 -1 -1 +5 +10 +0 20 feet Vampire +4 +10 +10 +4 +1 +8 +5 +0 50 feet Spectre +10 +25 +20 +5 +5 +14 -10 -5 50 feet Sprite +10 +10 +10 +4 +10 +10 -8 +0 40 feet Beastman -5 -2 -1 -1 -1 +5 +9 +5 None Ghoul -3 -3 +6 +1 +0 +5 +5 +0 20 feet --- Table 2 - Class Skill Bonus Table --- Dsrm Dvce Save Stlh Srch Prcp Melee Bows Warrior 25+12 18+7 18+10 1 14/2 25+100 17+55 Mage 30+7 36+13 30+9 2 15/20 10+25 10+14 Priest 25+7 30+10 32+12 2 16/8 16+50 7+18 Rogue 45+15 32+10 28+10 5 32/24 15+70 20+40 Ranger 30+8 32+10 28+10 3 24/16 15+65 20+63 Paladin 20+7 24+10 26+11 1 12/2 19+76 10+14 Warrior-Mage 30+7 30+10 28+9 2 18/16 20+75 20+50 Chaos Warrior 20+7 25+11 25+10 1 14/12 23+90 7+40 Monk 45+15 32+11 28+10 5 32/24 12+30 14+25 Mindcrafter 30+10 30+10 30+10 3 22/16 15+30 15+20 High Mage 30+7 36+13 30+9 2 15/20 10+15 10+10 -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: October 15, 2001 ***** Begin Hyperlinks ***** [1] birth.txt ***** [2] charattr.txt#PrimaryStats ***** [3] defend.txt#Armor ***** [4] charattr.txt#PrimarySkills zangband/lib/help/command.hlp0000644000000000000000000000151310250356274015165 0ustar rootrootZangband Commands. Please choose one of the following online help files: (0) Zangband Commands (command.txt) (1) Original Keyset (command.txt#OriginalKeyset) (2) Roguelike Keyset (command.txt#RogueKeyset) (3) Special Keys (command.txt#SpecialKeys) (4) Command Counts (command.txt#CommandCounts) (5) Selection of Objects (command.txt#ObjectSelection) (6) Command Descriptions (commdesc.hlp) (7) Wizard and Debugging Modes (wizard.txt) (?) Help System Commands (helpinfo.txt) ***** [0] command.txt ***** [1] command.txt#OriginalKeyset ***** [2] command.txt#RogueKeyset ***** [3] command.txt#SpecialKeys ***** [4] command.txt#CommandCounts ***** [5] command.txt#ObjectSelection ***** [6] commdesc.hlp ***** [7] wizard.txt zangband/lib/help/command.txt0000755000000000000000000004140010250356274015223 0ustar rootroot=== Zangband Commands === Zangband commands are entered as an "underlying command" (a single key) plus a variety of optional or required arguments. You may choose how the "keyboard keys" are mapped to the "underlying commands" by choosing one of two standard "keynotes", the "original" keyset or the "roguelike" keyset. The original keyset is very similar to the "underlying" command set, with a few additions (such as the ability to use the numeric "directions" to "walk" or the "5" key to "stay still"). The roguelike keyset provides similar additions, and also allows the use of the h/j/k/l/y/u/b/n keys to "walk" (or, in combination with the shift or control keys, to run or tunnel), which thus requires a variety of key mappings to allow access to the underlying commands used for walking, running and tunneling. In particular, the "roguelike" keyset includes many more "capital" and "control" keys, as shown below. Note that any keys that are not required for access to the underlying command set may be used by the user as "command macro" triggers (see below). You may always specify any "underlying command" directly by pressing backslash ("\") plus the "underlying command" key. This is normally only used in "macro" definitions. You may often enter "control-keys" as a caret ("^") plus the key (so "^" + "p" often yields "^P"). Some commands allow an optional "repeat count", which allows you to tell the game that you wish to do the command multiple times, unless you press a key or are otherwise disturbed. To enter a "repeat count", type '0', followed by the numerical count, followed by the command. You must type "space" before entering certain commands. Skipping the numerical count yields a count of 99. An option allows certain commands (open, disarm, tunnel, etc) to auto-repeat. Some commands will prompt for extra information, such as a direction, an inventory or equipment item, a spell, a textual inscription, the symbol of a monster race, a sub-command, a verification, an amount of time, a quantity, a file name, or various other things. Normally you can hit return to choose the "default" response, or escape to cancel the command entirely. Some commands will prompt for a spell or an inventory item. Pressing space (or '*') will give you a list of choices. Pressing "-" (minus) selects the item on the floor. Pressing a lowercase letter selects the given item. Pressing a capital letter selects the given item after verification. Pressing a numeric digit '#' selects the first item (if any) whose inscription contains "@#" or "@x#", where "x" is the current "underlying command". You may only specify items which are "legal" for the command. Whenever an item inscription contains "!*" or "!x" (with "x" as above) you must verify its selection. In Zangband, there are items which occasionally teleport you away, asking for permission first. The recurring "Teleport (y/n)?" can be annoying, and this behavior can be eliminated by inscribing the object which causes the teleportation with "." (or any inscription containing the character "."). With this inscription, the object will no longer teleport you around nor keep asking you. If you want to restore the teleport ability to the object, just remove the "." from its inscription. Note that cursed items which teleport you are unaffected by the inscription. Some commands will prompt for a direction. You may enter a "compass" direction using any of the "direction keys" shown below. Sometimes, you may specify that you wish to use the current "target", by pressing "t" or "5", or that you wish to select a new target, by pressing "*" (see "Target" below). Each of the standard keysets provides some short-cuts over the "underlying commands". For example, both keysets allow you to "walk" by simply pressing an "original" direction key (or a "roguelike" direction key if you are using the roguelike keyset), instead of using the "walk" command plus a direction. The roguelike keyset allows you to "run" or "tunnel" by simply holding the shift or control modifier key down while pressing a "roguelike" direction key, instead of using the "run" or "tunnel" command plus a direction. Both keysets allow the use of the "5" key to "stand still", which is most convenient when using the original keyset. Note that on many systems, it is possible to define "macros" (or "command macros") to various keys, or key combinations, so that it is often possible to make macros which, for example, allow the use of the shift or control modifier keys, plus a numeric keypad key, to specify the "run" or "tunnel" command, with the given direction, regardless of any keymap definitions, by using the fact that you can always, for example, use "\" + "." + "6", to specify "run east". ***** === Original Keyset === Original Keyset Directions 7 8 9 4 6 1 2 3 a Aim a wand A Activate an artifact b Browse a book B (unused) c Close a door C Character description d Drop an item D Disarm a trap e Equipment list E Eat some food f Fire an item F Fuel your lantern/torch g Stay still (flip pickup) G Gain new spells/prayers h (unused) H (unused) i Inventory list I Observe an item j Jam a door J (unused) k Destroy an item K (unused) l Look around L Locate player on map m Cast a spell / use mental power M Full dungeon map n Repeat last command N (unused) o Open a door or chest O (unused) p Command your pets P (unused) q Quaff a potion Q Quit (commit suicide) r Read a scroll R Rest for a period s Search for traps/doors S Toggle search mode t Take off equipment T Dig a tunnel u Use a staff U Use bonus power (if any) v Throw an item V Version info w Wear/wield equipment W (unused) x (unused) X (unused) y (unused) Y (unused) z Zap a rod Z (unused) ! Interact with system ^A (special - wizard command) @ Interact with macros ^B (unused) # (unused) ^C (special - break) $$ User interface ^D (unused) % Interact with visuals ^E Toggle choice window ^ (special - control key) ^F Repeat level feeling & Interact with colors ^G (unused) * Target monster or location ^H (unused) ( Load screen dump ^I (special - tab) ) Dump screen dump ^J (special - linefeed) { Inscribe an object ^K (unused) } Uninscribe an object ^L (unused) [ (unused) ^M (special - return) ] (unused) ^N (unused) - Walk (flip pickup) ^O (unused) _ Enter store ^P Show previous messages + Alter grid ^Q Quit to next midi song = Set options ^R Redraw the screen ; Walk (with pickup) ^S Save and don't quit : Take notes ^T Show the day and time ' (unused) ^U (unused) " Enter a user pref command ^V (unused) , Stay still (with pickup) ^W (special - wizard mode) < Go up staircase ^X Save and quit . Run ^Y (unused) > Go down staircase ^Z (special - borg command) \ (special - bypass keymap) | Check various information ` (special - escape) ~ Check various information / Identify symbol ? Help ***** === Roguelike Keyset === Roguelike Keyset Directions y k u h l b j n a Zap a rod (Activate) A Activate an artifact b (walk - south west) B (run - south west) c Close a door C Character description d Drop an item D Disarm a trap or chest e Equipment list E Eat some food f (unused) F Fuel your lantern/torch g Stay still (flip pickup) G Gain new spells/prayers h (walk - west) H (run - west) i Inventory list I Observe an item j (walk - south) J (run - south) k (walk - north) K (run - north) l (walk - east) L (run - east) m Spell casting / mental power M Full dungeon map n (walk - south east) N (run - south east) o Open a door or chest O Use bonus power (if any) p Command your pets P Browse a book q Quaff a potion Q Quit (commit suicide) r Read a scroll R Rest for a period s Search for traps/doors S Jam a door (Spike) t Fire an item T Take off equipment u (walk - north east) U (run - north east) v Throw an item V Version info w Wear/wield equipment W Locate player on map x Look around X (unused) y (walk - north west) Y (run - north west) z Aim a wand (Zap) Z Use a staff (Zap) ! Interact with system ^A (special - wizard command) @ Interact with macros ^B (tunnel - south west) # Toggle search mode ^C (special - break) $$ (unused) ^D Destroy item % Interact with visuals ^E Toggle choice window ^ (special - control key) ^F Repeat level feeling & Interact with colors ^G (unused) * Target monster or location ^H (tunnel - west) ( Load screen dump ^I (special - tab) ) Dump screen dump ^J (tunnel - south) { Inscribe an object ^K (tunnel - north) } Uninscribe an object ^L (tunnel - east) [ (unused) ^M (tunnel - south) ] (unused) ^N (tunnel - south east) - Walk (flip pickup) ^O (unused) _ Enter store ^P Show previous messages + Alter grid ^Q Quit to next midi song = Set options ^R Redraw the screen ; Walk (with pickup) ^S Save and don't quit : Take notes ^T Dig a Tunnel ' Show the day and time ^U (tunnel - north east) " Enter a user pref command ^V Examine current target , Run ^W (special - wizard mode) < Go up staircase ^X Save and quit . Stay still (with pickup) ^Y (tunnel - north west) > Go down staircase ^Z (special - borg command) \ (special - bypass keymap) | Check information ` (special - escape) ~ Check information / Identify symbol ? Help ***** === Special Keys === Certain special keys may be intercepted by the operating system or the host machine, causing unexpected results. In general, these special keys are control keys, and often, you can disable their special effects. If you are playing on a UNIX or similar system, then Ctrl-C will interrupt Zangband. The second and third interrupt will induce a warning bell, and the fourth will induce both a warning bell and a special message, since the fifth will quit the game, after killing your character. Also, Ctrl-Z will suspend the game, and return you to the original command shell, until you resume the game with the "fg" command. There is now a compilation option to force the game to prevent the "double ctrl-z escape death trick". The Ctrl-\ and Ctrl-D and Ctrl-S keys should not be intercepted. It is often possible to specify "control-keys" without actually pressing the control key, by typing a caret ("^") followed by the key. This is useful for specifying control-key commands which might be caught by the operating system as explained above. Pressing backslash ("\") before a command will bypass all keymaps, and the next keypress will be interpreted as an "underlying command" key, unless it is a caret ("^"), in which case the keypress after that will be turned into a control-key and interpreted as a command in the underlying Zangband keyset. The backslash key is useful for creating macro actions which are not affected by any keymap definitions that may be in force, for example, the sequence "\" + "." + "6" will always mean "run east", even if the "." key has been mapped to a different underlying command. The "0" and "^" and "\" keys all have special meaning when entered at the command prompt, and there is no "useful" way to specify any of them as an "underlying command", which is okay, since they would have no effect. For many input requests or queries, the special character ESCAPE will abort the command. The "[y/n]" prompts may be answered with "y" or "n", or escape. The "-more-" message prompts may be cleared (after reading the displayed message) by pressing ESCAPE, SPACE, RETURN, LINEFEED, or by any keypress, if the "quick_messages" option is turned on. ***** === Command Counts === Some commands can be executed a fixed number of times by preceding them with a count. Counted commands will execute until the count expires, until you type any character, or until something significant happens, such as being attacked. Thus, a counted command doesn't work to attack another creature. While the command is being repeated, the number of times left to be repeated will flash by on the line at the bottom of the screen. To give a count to a command, type 0, the repeat count, and then the command. If you want to give a movement command and you are using the original command set (where the movement commands are digits), press space after the count and you will be prompted for the command. Counted commands are very useful for searching or tunneling, as they automatically terminate on success, or if you are attacked. You may also terminate any counted command (or resting or running), by typing any character. This character is ignored, but it is safest to use a SPACE or ESCAPE which are always ignored as commands in case you type the command just after the count expires. You can tell Zangband to automatically use a repeat count of 99 with commands you normally want to repeat (open, disarm, tunnel, alter, etc) by setting the "always_repeat" option. ***** === Selection of Objects === Many commands will also prompt for a particular object to be used. For example, the command to read a scroll will ask you which of the scrolls that you are carrying that you wish to read. In such cases, the selection is made by typing a letter of the alphabet. The prompt will indicate the possible letters, and will also allow you to type the key "*", which causes all of the available options to be described. The list of choices will also be shown in the Choice window, if you are using a windows environment and windows are turned on. Often you will be able to press "/" to select an object from your equipment instead of your inventory. Pressing space once will have the same effect as "*", and the second time will cancel the command and run the "i" or "e" command. The particular object may be selected by an upper case or a lower case letter. If lower case is used, the selection takes place immediately. If upper case is used, then the particular option is described, and you are given the option of confirming or retracting that choice. Upper case selection is thus safer, but requires an extra key stroke. For many commands, you can also use "-" to select an object on the floor where you are standing. This lets you read scrolls or quaff potions, for example, off the dungeon floor without picking them up. Also see objects.txt#Inscriptions [1] for a details description of how to us inscriptions to make object selection easier. -- Original : Alexander Cutler and Andy Astrand Updated : (2.7.6) by Russ Allbery (rra@cs.stanford.edu) Updated : (2.7.9) by Ben Harrison (benh@phial.com) Updated : Zangband 2.2.0 through 2.2.6c by Robert Ruehlmann Updated : Zangband DevTeam Last update: June 2, 2000 ***** Begin Hyperlinks ***** [1] objects.txt#Inscriptions zangband/lib/help/commdesc.hlp0000644000000000000000000000334510250356274015346 0ustar rootrootZangband Command Descriptions. Please choose one of the following online help files: (0) Command Descriptions (commdesc.txt) (1) Inventory Commands (commdesc.txt#Inventory) (2) Movement Commands (commdesc.txt#Movement) (3) Resting Commands (commdesc.txt#Resting) (4) Searching Commands (commdesc.txt#Searching) (5) Alter Commands (commdesc.txt#Alter) (6) Spell and Prayer Commands (commdesc.txt#SpellPrayer) (7) Object Manipulation Commands (commdesc.txt#ObjectManip) (8) Magical Item Commands (commdesc.txt#MagicalObject) (9) Throwing and Missile Weapons (commdesc.txt#ThrowFire) (a) Looking Commands (commdesc.txt#Looking) (b) Message Commands (commdesc.txt#Message) (c) Game Status Commands (commdesc.txt#GameStatus) (d) Saving and Exiting Commands (commdesc.txt#SaveExit) (e) User Preference Commands (commdesc.txt#UserPref) (f) Help Commands (commdesc.txt#Help) (g) Extra Commands (commdesc.txt#Extra) (?) Help System Commands (helpinfo.txt) ***** [0] commdesc.txt ***** [1] commdesc.txt#Inventory ***** [2] commdesc.txt#Movement ***** [3] commdesc.txt#Resting ***** [4] commdesc.txt#Searching ***** [5] commdesc.txt#Alter ***** [6] commdesc.txt#SpellPrayer ***** [7] commdesc.txt#ObjectManip ***** [8] commdesc.txt#MagicalObject ***** [9] commdesc.txt#ThrowFire ***** [a] commdesc.txt#Looking ***** [b] commdesc.txt#Message ***** [c] commdesc.txt#GameStatus ***** [d] commdesc.txt#SaveExit ***** [e] commdesc.txt#UserPref ***** [f] commdesc.txt#Help ***** [g] commdesc.txt#Extra zangband/lib/help/commdesc.txt0000644000000000000000000007427010250356274015407 0ustar rootroot=== Command Descriptions === The following command descriptions are listed as the command name plus the "underlying command" key. This is followed by the command name and "roguelike" keyset key, if different from the underlying command key. Then comes a brief description of the command, including information about alternative methods of specifying the command in each keyset, when needed. Several commands (tunnel, disarm, open) are repeated 99 times if the "always_repeat" option is set and no repeat count is given. Some commands use the "repeat count" to automatically repeat the command several times, while others use the "repeat count" as an "argument", for example, commands which need a "quantity" will use the "repeat count" instead of asking for a quantity, allowing the use of "0d" for "drop all". Commands which ask for a quantity will convert any "letters" into the maximal legal value. ***** --- Inventory Commands --- Inventory list (i) Displays a list of objects being carried but not equipped. You can carry up to 23 different items, not counting those in your equipment. Often, many identical objects can be "stacked" into a "pile" which will count as a single item. This is always true of things like potions, scrolls, and food, but you may have to set options to allow wands, staves, and other such objects to stack. Each object has a weight, and if you carry more objects than your strength permits, you will begin to slow down. Equipment list (e) Use this command to display a list of the objects currently being used by your character. Your character has 12 slots for equipment, each corresponding to a different location on the body, and each of which may contain only a single object at a time, and each of which may only contain objects of the proper "type", and which include WIELD (weapon), BOW (missile launcher), LEFT (ring), RIGHT (ring), NECK (amulet), LITE (light source), BODY (armor), OUTER (cloak), ARM (shield), HEAD (helmet), HANDS (gloves), FEET (boots). You must be using an object to receive any of its special powers. Drop an item (d) This drops an item from your inventory or equipment onto the dungeon floor. If the floor spot you are standing on already has an object in it, Zangband will attempt to drop the item onto an adjacent space. Be warned that if the floor is full and you attempt to drop something, it may disappear and be destroyed. Doors and traps are considered objects for the purpose of determining if the space is occupied. If the selected pile contains multiple items, you may specify a quantity. Destroy an item (k) or Destroy an item (^D) This destroys an item in your inventory or on the dungeon floor. If the selected pile contains multiple objects, you may specify a quantity. You must always (currently) verify this command. Wear/Wield equipment (w) To wear or wield an object in your inventory, use this command. Since only one object can be in each slot at a time, if you wear or wield an item into a slot which is already occupied, the old item will be first be taken off, and may in fact be dropped if there is no room for it in your inventory. Take off equipment (t) or Take off equipment (T) Use this command to take off a piece of equipment and return it to your inventory. Occasionally, you will run into a cursed item which cannot be removed. These items normally penalize you in some way and cannot be taken off until the curse is removed. If there is no room in your inventory for the item, your pack will overflow and you will drop the item after taking it off. ***** --- Movement Commands --- Walk (with pickup) (;) Moves one step in the given direction. The square you are moving into must not be blocked by walls or doors. You will pick up any items in the destination grid if the "always_pickup" option is set, or if the "query_pickup" option is set and you respond correctly. This command can take a count and requires a direction. You may also use the "original" direction keys (both keysets) or the "roguelike" direction keys (roguelike keyset) to walk in a direction. Walk (flip pickup) (-) This is just like normal move, except that the "Pick things up" option is inverted. In other words, if you normally pick up anything you encounter (the default), you will not pick things up when using this command. If you normally do not pick things up, you will when using this command. This command can take a count and requires a direction. Run (.) or Run (,) This command will move in the given direction, following any bends in the corridor, until you either have to make a "choice" between two directions or you are disturbed. You can configure what will disturb you by setting the disturbance options. Run requires a direction. You may also use shift plus the "roguelike" direction keys (roguelike keyset), or shift plus the "original" direction keys on the keypad (both keysets, some machines) to run in a direction. Go up staircase (<) Climbs up an up staircase you are standing on. There is always at least one staircase going up on every level except for the town level (this doesn't mean it's easy to find). Going up a staircase will take you to a new dungeon level unless you are at 50 feet (dungeon level 1), in which case you will return to the town level. Note that whenever you leave a level (not the town), you will never find it again. This means that for all intents and purposes, any objects on that level are destroyed. This includes artifacts unless the "Create characters in preserve mode" option was set when your character was created, in which case the artifacts may show up again later. Go down staircase (>) Descends a down staircase you are standing on. There are always at least two staircases going down on each level, except for the town which has only one, and "quest" levels, which have none until the quest monster is killed. Going down a staircase will take you to a new dungeon level. See "Go Up Staircase" for more info. ***** --- Resting Commands --- Stay still (with pickup) (,) or Stay still (with pickup) (.) Stays in the same square for one move. If you normally pick up objects you encounter, you will pick up whatever you are standing on. This command can take a count. You may also use the "5" key (both keysets). Stay still (flip pickup) (g) Stays in the same square for one move. If you normally pick up objects you encounter, you will not pick up whatever you are standing on. If you normally do not pick up objects, you will pick up what you are standing on. This command is normally only used when the "always_pickup" option is false. This command can take a count. Rest (R) Resting is better for you than repeatedly staying still, and can be told to automatically stop after a certain amount of time, or when various conditions are met. In any case, you always wake up when anything disturbing happens, or when you press any key. To rest, enter the Rest command, followed by the number of turns you want to rest, or "*" to rest until your hitpoints and mana are restored, or "&" to rest until you are fully "healed". This command can take a count, which is used for the number of turns to rest. ***** --- Searching Commands --- Search (s) This command can be used to locate hidden traps and secret doors in the spaces adjacent to the player. More than a single turn of searching will be required in most cases. You should always search a chest before trying to open it, since they are generally trapped. This command can take a count, which is useful if you are fairly sure of finding something eventually, since the command stops as soon as anything is found. This command can take a count. Toggle search mode (S) or Toggle search mode (#) This command will take you into and out of search mode. When first pressed, the message "Searching" will appear at the bottom of the screen. You are now taking two turns for each command, one for the command and one turn to search. This means that you are taking twice the time to move around the dungeon, and therefore twice the food. Search mode will automatically turn off if you are disturbed. You may also turn off search mode by entering the Search Mode command again. ***** --- Alter Commands --- Tunnel (T) or Tunnel (^T) Tunneling or mining is a very useful art. There are many kinds of rock, with varying hardness, including permanent rock (permanent), granite (very hard), quartz veins (hard), magma veins (soft), and rubble (very soft). Quartz and Magma veins may be displayed in a special way, and may sometimes contain treasure, in which case they will be displayed in a different way. Rubble sometimes covers an object. It is hard to tunnel unless you are wielding a heavy weapon or a shovel or a pick. Tunneling ability increases with strength and weapon weight. This command can take a count, requires a direction, and is affected by the "always_repeat" option. Open a door or chest (o) To open an object such as a door or chest, you must use this command. If the object is locked, you will attempt to pick the lock based on your disarming ability. If you open a trapped chest without disarming the traps first, the trap will be set off. Some doors will be jammed shut and may have to be forced open. You may need several tries to open a door or chest. Open can take a count, requires a direction, and is affected by the "always_repeat" option. Close a door (c) Non-intelligent and some other creatures cannot open doors, so shutting doors can be quite valuable. Broken doors cannot be closed. Close can take a count, requires a direction, and is affected by the "always_repeat" option. Jam a door (j) or Spike a door (S) Many monsters can simply open closed doors, and can eventually get through a locked door. You may therefore occasionally want to jam a door shut with iron spikes. Each spike used on the door will make it harder to bash down the door, up to a certain limit. Smaller monsters are less able to bash down doors. In order to use this command, you must be carrying iron spikes. Jam or Spike requires a direction. Disarm a trap or chest (D) You can attempt to disarm traps on the floor or on chests. If you fail, there is a chance that you will blunder and set it off. You can only disarm a trap after you have found it (usually with the Search command). Disarm can take a count, requires a direction, and is affected by the "always_repeat" option. Alter (+) This special command allows the use of a single keypress to select any of the "obvious" commands above (attack, tunnel, open, disarm, close), and, by using macros or keymaps, to combine this keypress with directions. In general, this allows the use of the "control" key plus the appropriate "direction" key (including the roguelike direction keys in roguelike mode) as a kind of generic "alter the terrain feature of an adjacent grid" command. Alter can take a count, requires a direction, and is affected by the "always_repeat" option. ***** --- Spell and Prayer Commands --- Browse a book (b) or Peruse a book (P) Only mages, rogues, and rangers can read magic books, and only priests and paladins can read prayer books. Warriors cannot read any books. When this command is used, all of the spells or prayers contained in the selected book are displayed, along with such information as their level, the amount of mana required to cast them, and whether or not you know the spell or prayer. Gain new spells or prayers (G) Use this command to actually learn new spells or prayers. When you are able to learn new spells or prayers, the word "Study" will appear on the status line at the bottom of the screen. If you have a book in your possession, containing spells or prayers which you may learn, then you may choose to study that book. If you are a mage, rogue, or ranger, you may actually choose which spell to study. If you are a priest or paladin, your gods will choose a prayer for you. There are nine books of each type, some normally found only in the dungeon. Cast a spell / Pray a prayer (m) To cast a spell, you must have previously learned the spell and must have in your inventory a book from which the spell can be read. Each spell has a chance of failure which starts out fairly large but decreases as you gain levels. If you don't have enough mana to cast a spell, you will be prompted for confirmation. If you decide to go ahead, the chance of failure is greatly increased, and you may lose a point of constitution. Since you must read the spell from a book, you cannot be blind or confused while casting, and there must be some light present. To pray effectively, you must have previously learned the prayer and must have in your inventory a book from which the prayer can be read. Each prayer has a chance of being ignored which starts out fairly large but decreases as you gain levels. If you don't have enough mana to cast a spell, you will be prompted for confirmation. If you decide to go ahead, the chance of failure is greatly increased, and you may lose a point of constitution. Since you must read the prayer from a book, you cannot be blind or confused while praying, and there must be some light present. ***** --- Object Manipulation Commands --- Eat some food (E) You must eat regularly to prevent starvation. As you grow hungry, a message will appear at the bottom of the screen saying "Hungry". If you go hungry long enough, you will become weak, then start fainting, and eventually, you may will die of starvation. You may use this command to eat food in your inventory. Note that you can sometimes find food in the dungeon, but it is not always wise to eat strange food. Fuel your lantern/torch (F) If you are using a torch and have more torches in your pack, or you are using a lantern and have flasks of oil in your pack, then your can "refuel" them with this command. Torches and Lanterns are limited in their maximal fuel. In general, two flasks will fully fuel a lantern and two torches will fully fuel a torch. Quaff a potion (q) Use this command to drink a potion. Potions affect the player in various ways, but the effects are not always immediately obvious. Read a scroll (r) Use this command to read a scroll. Scroll spells usually have an area effect, except for a few cases where they act on other objects. Reading a scroll causes the parchment to disintegrate as the scroll takes effect. Most scrolls which prompt for more information can be aborted (by pressing escape), which will stop reading the scroll before it disintegrates. Inscribe an object ({) This command inscribes a string on an object. The inscription is displayed inside curly braces after the object description. The inscription is limited to the particular object (or pile) and is not automatically transferred to all similar objects. Note that certain inscriptions have a meaning to the game, (see objects.txt#Inscriptions [1]). Uninscribe an object (}) This command removes the inscription on an object. This command will have no effect on "fake" inscriptions added by the game itself. ***** --- Magical Object Commands --- Activate an artifact (A) You have heard rumors of special weapons and armor deep in the Pits, items that can let you breath fire like a dragon or light rooms with just a thought. Should you ever be lucky enough to find such an item, this command will let you activate its special ability. Special abilities can only be used if you are wearing or wielding the item. Aim a wand (a) or Zap a wand (z) Wands must be aimed in a direction to be used. Wands are magical devices, and therefore there is a chance you will not be able to figure out how to use them if you aren't good with magical devices. They will fire a shot that affects the first object or creature encountered or fire a beam that affects anything in a given direction, depending on the wand. An obstruction such as a door or wall will generally stop the effects from traveling any farther. This command requires a direction and can use a target. Use a staff (u) or Zap a staff (Z) This command will use a staff. A staff is normally very similar to a scroll, in that they normally either have an area effect or affect a specific object. Staves are magical devices, and there is a chance you will not be able to figure out how to use them. Zap a rod (z) or Activate a rod (a) Rods are extremely powerful magical items, which cannot be burnt or shattered, and which can have either staff-like or wand-like effects, but unlike staves and wands, they don't have charges. Instead, they draw on the ambient magical energy to recharge themselves, and therefore can only be activated once every few turns. The recharging time varies depending on the type of rod. This command may require a direction (depending on the type of rod, and whether you are aware of its type) and can use a target. ***** --- Throwing and Missile Weapons --- Fire an item (f) or Fire an item (t) You may throw any object carried by your character. Depending on the weight, it may travel across the room or drop down beside you. Only one object from a pile will be thrown at a time. Note that throwing an object will often cause it to break, so be careful! If you throw something at a creature, your chances of hitting it are determined by your pluses to hit, your ability at throwing, and the object's pluses to hit. Once the creature is it, the object may or may not do any damage to it. You've heard rumors that some objects found in the dungeon can do huge amounts of damage when thrown, but you're not sure which objects those are.... Note that flasks of oil will do a fairly large chunk of damage to a monster on impact, supposedly representing fire damage, but it works against fire elementals too... If you are wielding a missile launcher compatible with the object you are throwing, then you automatically use the launcher to fire the missile with much higher range, accuracy, and damage, then you would get by just throwing the missile. Fire or Throw requires a direction. Targeting mode (see the next command) can be invoked with "*" at the "Direction?" prompt. Throw an item (v) You may throw any object carried by your character. The lighter the object, the farther you can throw it. Only one object from a stack may be thrown at a time. Throwing an object may break it. If you throw something at a monster, your chances of hitting it are determined by your pluses to hit, your ability at throwing, and the object's pluses to hit. If the object hits the monster, it may or may not do damage. Some objects, such as weapons, or flasks of oil, can do a substantial amount of damage. This command requires a direction, and can take a target. Targeting Mode (*) This will allow you to aim your spells and such at a specific monster or grid, so that you can point directly towards that monster or grid (even if this is not a "compass" direction) when you are asked for a direction. You can set a target using this command, or you can set a new target at the "Direction?" prompt when appropriate. At the targeting prompt, you have many options. First of all, targeting mode starts targeting nearby monsters which can be reached by "projectable" spells and thrown objects. In this mode, you can press "t" (or "5" or ".") to select the current monster, space to advance to the next monster, "-" to back up to the previous monster, direction keys to advance to a monster more or less in that direction, "r" to "recall" the current monster, "q" to exit targeting mode, and "p" (or "o") to stop targeting monsters and enter the mode for targeting a location on the floor or in a wall. Note that if there are no nearby monsters, you will automatically enter this mode. Note that hitting "o" is just like "p", except that the location cursor starts on the last examined monster instead of on the player. In this mode, you use the "direction" keys to move around, and the "q" key to quit, and the "t" (or "5" or ".") key to target the cursor location. Note that targeting a location is slightly "dangerous", as the target is maintained even if you are far away. To cancel an old target, simply hit "*" and then ESCAPE (or "q"). Note that when you cast a spell or throw an object at the target location, the path chosen is the "optimal" path towards that location, which may or may not be the path you want. Sometimes, by clever choice of a location on the floor for your target, you may be able to convince a thrown object or cast spell to squeeze through a hole or corridor that is blocking direct access to a different grid. Launching a ball spell or breath weapon at a location in the middle of a group of monsters can often improve the effects of that attack, since ball attacks are not stopped by interposed monsters if the ball is launched at a target. This command takes no time. ***** --- Looking Commands --- Full screen map (M) This command will show a map of the entire dungeon, reduced by a factor of nine, on the screen. Only the major dungeon features will be visible because of the scale, so even some important objects may not show up on the map. This is particularly useful in locating where the stairs are relative to your current position, or for identifying unexplored areas of the dungeon. This command takes no time. Locate player on map (L) or Where is the player (W) This command lets you scroll your map around, looking at all sectors of the current dungeon level, until you press escape, at which point the map will be re-centered on the player if necessary. To scroll the map around, simply press any of the "direction" keys. The top line will display the sector location, and the offset from your current sector. This command takes no time. Look around (l) or Examine things (x) This command is used to look around at nearby monsters (to determine their type and health) and objects (to determine their type). It is also used to find out what objects (if any) are under monsters, and if a monster is currently inside a wall. This command takes no time. When you are looking at something, you may hit space for more details, or to advance to the next interesting monster or object, or minus ("-") to go back to the previous monster or object, or a direction key to advance to the nearest interesting monster or object (if any) in that general direction, or "r" to recall information about the current monster race, or "q" or escape to stop looking around. You always start out looking at the "nearest" interesting monster or object. Observe an item (I) This command lets you observe a previously *identified* item. This will tell you things about the special powers of the object. Currently, it only makes sense for artifacts and ego-items. ***** --- Message Commands --- Repeat level feeling (^F) Repeats the feeling about the dungeon level that you got when you first entered the level. View previous messages (^P) This command shows you all the recent messages. You can scroll through them, or exit with ESCAPE. This command takes no time. Take notes (:) This command allows you to take notes, which will then appear in your message list (prefixed with "Note:"). This command takes no time. ***** --- Game Status Commands --- Character Description (C) Brings up a full description of your character, including your skill levels, your current and potential stats, and various other information. From this screen, you can change your name or use the file character description command to save your character status to a file. That command saves additional information, including your background, your inventory, and the contents of your house. Check Artifacts, Uniques and Objects (~ or |) The command opens a menu from which you can select information about seen Artifacts, Unique monsters, and identified objects. Display known artifacts This selection lists all of the artifacts that you've encountered. Any artifact that appears in this list, which you cannot seem to find, has been lost forever. The "preserve" mode will prevent you from accidentally losing any artifacts, but will also prevent you from ever getting a "special" level feeling. This command can only be used on the town level, not inside the dungeon. Display known uniques Brings up a list of known unique monsters, plus their current status. Once killed, unique monsters never show up again. Display known objects This list all 'flavored' objects (such as rings, scrolls, wands, potions, etc.) which you have identified. ***** --- Saving and Exiting Commands --- Save and Quit (Ctrl-X) To save your game so that you can return to it later, use this command. Save files will also be generated (hopefully) if the game crashes due to a system error. After you die, you can use your savefile to play again with the same options and such. Save (Ctrl-S) This command saves the game but doesn't exit Zangband. Use this frequently if you are paranoid about having the computer crash while you are playing. Quit (commit suicide) (Q) Kills your character and exits Zangband. You will be prompted to make sure you really want to do this, and then asked to verify that choice. Note that dead characters are dead forever. ***** --- User pref file commands --- See the file pref.hlp for a description of the user preference commands together with a detailed description of this function. ***** --- Help --- Help (?) Brings up the Zangband on-line help system. Note that the help files are just text files in a particular format, and that other help files may be available on the Net. In particular, there are a variety of spoiler files which do not come with the standard distribution. Check the place you got Zangband from or ask on the newsgroup rec.games.roguelike.angband about them. Identify Symbol (/) Use this command to find out what a character stands for. For instance, by pressing "/.", you can find out that the "." symbol stands for a floor spot. When used with a symbol that represents creatures, the this command will tell you only what class of creature the symbol stands for, not give you specific information about a creature you can see. To get that, use the Look command. There are three special symbols you can use with the Identify Symbol command to access specific parts of your monster memory. Typing Ctrl-A when asked for a symbol will recall details about all monsters, typing Ctrl-U will recall details about all unique monsters, and typing Ctrl-N will recall details about all non-unique monsters. If the character stands for a creature, you are asked if you want to recall details. If you answer yes, information about the creatures you have encountered with that symbol is shown in the Recall window if available, or on the screen if not. You can also answer "k" to see the list sorted by number of kills, or "p" to see the list sorted by dungeon level the monster is normally found on. Pressing ESCAPE at any point will exit this command. Game Version (V) This command will tell you what version of Zangband you are using. For more information, see the "version.txt" help file. ***** --- Extra Commands --- Toggle Choice Window (^E) Toggles the display in the choice window (if available) between your inventory and your equipment. This command only applies if you are running Zangband under a windowing environment and the choice window is available. This also redraws the choice window. Redraw Screen (^R) This command adapts to various changes in global options, and redraws all of the windows. This command should be used after changing various global properties (options, attr/char mappings, color definitions, etc). When in doubt, use it. Display Time (^T) or (') This command shows the current day and the time of day in the game. Load screen dump (left-paren) This command loads a "snap-shot" of the current screen from the file "dump.txt", and displays it on the screen. Save screen dump (right-paren) This command dumps a "snap-shot" of the current screen to the file "dump.txt", including encoded color information. Command your pets (p) If you have pets then this command allows you to give orders to your pets. You can call them to you, send them out to kill other monsters, dismiss them, and allow/disallow opening of door and picking up items. Your pets will drop all picked up items when you disallow them to pick up anything. -- Original : Alexander Cutler and Andy Astrand Updated : (2.7.6) by Russ Allbery (rra@cs.stanford.edu) Updated : (2.7.9) by Ben Harrison (benh@phial.com) Updated : Zangband 2.2.0 through 2.2.6c by Robert Ruehlmann Updated : Zangband DevTeam Last update: June 2, 2000 ***** Begin Hyperlinks ***** [1] objects.txt#Inscriptions zangband/lib/help/defend.hlp0000644000000000000000000000176110250356274015001 0ustar rootrootDefending Yourself. Please choose one of the following online help files: (0) Defending Yourself (defend.txt) (1) Armor Class (defend.txt#Armor) (2) Resisting Magical Attacks (defend.txt#ResistingMagic) (3) Ego Armor and Artifacts (defend.txt#EgoArtifact) (4) The Resistances (defend.txt#Resistances) (5) Unresistable Attacks (defend.txt#Unresistable) (6) Immunities (defend.txt#Immunities) (7) Miscellaneous Resists (defend.txt#Miscellaneous) (8) Recovering from Attacks (defend.txt#Recovering) (?) Help System Commands (helpinfo.txt) ***** [0] defend.txt ***** [1] defend.txt#Armor ***** [2] defend.txt#ResistingMagic ***** [3] defend.txt#EgoArtifact ***** [4] defend.txt#Resistances ***** [5] defend.txt#Unresistable ***** [6] defend.txt#Immunities ***** [7] defend.txt#Miscellaneous ***** [8] defend.txt#Recovering zangband/lib/help/defend.txt0000644000000000000000000006002110250356274015027 0ustar rootroot=== Defending Yourself === In Zangband, monsters may damage and otherwise harm you in a number of ways: in direct melee combat, by exploding next to you, by breathing on you and by casting spells. Some monsters referred to as "summoners" may not directly harm you but may summon other monsters to do their dirty work for them. Creatures will attack you in the same manner in which you attack them. If they move into you, they attack you. Virtually all monsters can lower your hit points by means of a melee attack, and if you lose too many, you die. Fortunately, this is also the attack most easily guarded against (by wielding armor). Each monster has a maximum of four melee attacks which may be of varying type, power and effect. In addition to pure damage, there can be a number of side-effects from a monster hitting you. For example, an especially powerful attack may stun you wound you. Stunning is cumulative and, if not addressed, may eventually result in you becoming knocked out which allows your opponent to continue attacking while you lie helpless. Wounds will cause you to lose hit points each round until you either die or your wound heals. You will slowly recover from any wound except a Mortal Wound with time and all wounds can be healed magically. In addition to cuts and stunning, some monsters melee attacks may have other consequences. These include causing you to become blind, scared, confused, poisoned or paralyzed. There are many others less common effects. Full details of monsters melee ability may be found in the section on Monsters (see monster.txt). Many monsters also have an array of magical spells which they may use against you and others can use various breath attacks on you from a distance. For example, a red dragon might breathe fire at you. You can defend yourself from the side-effects of melee attacks, monster spells and breath attacks by searching for armor that is resistant to that form of attack. As you move down deeper into the dungeon in search of better items, you will need to steadily improve your defenses against pure damage, wounds, stunning, bolts, balls, and breaths of various kinds, reductions of exp and stats, theft, and a variety of miscellaneous magical attacks. ***** === Armor Class === Your armor class (or AC) is a number that describes the amount and the quality of armor being worn. Armor class will generally run from about 0 to 150, but could become negative or greater than 150 with rarer armor or by magical means. Note the spell casters receive a penalty to their maximum mana for wielding armor that is too heavy. In addition, monks lose much of their bare handed fighting skill if their armor becomes too heavy. The larger your armor class, the more protective it is. A negative armor class would actually help get you hit. Armor protects you in three manners. One, it makes you harder to be hit for damage. A hit for no damage is the same as a miss. Two, good armor will absorb some of the damage that your character would have taken from normal attacks. Three, acid damage is reduced by wearing body armor (but the armor will be damaged instead). It is obvious that a high armor class is a must for surviving the deeper levels of Zangband. Note, however, that armor class by itself will not protect you from the side effects of monster melee attacks mentioned above. Each piece of armor has a base armor value, which, like the damage from weapons, is assumed known by the player, and a magic bonus, which will not be displayed unless the armor has been identified or was bought in a store. Armor class values are always displayed between a set of brackets as '[#]' or '[#,+#]'. The first value is the armor class of the item. The second number is the magical bonus of the item which is only displayed if known, and will always have a sign preceding the value. Note: A few rings, amulets, and weapons also have the '[+#]' notation, indicating that they provide an armor bonus. Many pieces of heavy body armor will also have a '(-#)' before the '[#,+#]', which indicates that the weight of the armor decreases your chances of hitting monsters. This can range from nonexistent for very light armor to (-8) for the very heaviest armor! ***** === Resisting Magical Attacks === Against some magical attacks (such as cause wounds, cause blindness or cause fear) the player will first get a saving throw (see the Skills page). However, against attacks such as bolts and ball spells this does not apply. Also, there is no saving throw against monster breaths. For these types of attacks, your only recourse it to find items or other means which grant you 'resistance' to that type of attack. In Zangband there are many forms of resistance which may be obtained either as bonuses from your equipment, as the result of casting a magical spell, activating certain artifacts or as the result of a mutation. Unfortunately, not all attacks available to monsters can be resisted and most can only be partially resisted. Each resistance has one or more corresponding monster attack which it 'resists'. By this we mean that it reduces the amount of damage that your character will take as a result of an attack of that kind. For example, fire resistance will reduce the damage to your character resulting from fire attacks such as a fire bolt or being breathed upon by a red dragon. Note that appropriate resistances will also negate or reduce some side-effects of monster melee attacks. There are two categories of resistances: (1) the low (or elemental) resistances - acid, fire, cold and electricity; and (2) the high resistances - poison, confusion, light, dark, blindness, sound, fear, shards, nexus, chaos, nether, gravity, disenchantment and time. In general, the low resistances are more commonly available, they can be double-resisted and the corresponding attacks have a higher maximum damage potential. A common misconception amongst new players is that having multiple sources of a particular resistance will increase your character's resistance to attacks of that form. This is not true. You will get the same benefit from wielding a single Amulet of Resist Acid as wielding a Shield of Resist Acid, an {Armor-type} of Resist Acid and an artifact weapon which grants acid resistance all at the same time. As stated above, however, it is possible to get double resistance for the low resists (and also poison). This is because these resistances are also available on a temporary basis by casting certain spells or by quaffing a Potion of Resistance. Some items may also activate for temporary resistance and it may also be gained from one of the many possible mutations. This temporary effect is cumulative with the permanent effect and will increase your protection against these kinds of attacks. ***** === Ego Armor and Artifacts === In addition to the ordinary armor items your character may find in the dungeon, some of them may be endowed with additional powers. These armors are fall into two types(1) artifacts; and (2) ego armors. Unlike artifacts which are unique and may only be found once in each game, it is not unusual to find several ego armors of the same type during the course of a character's adventures. In general, artifacts and ego armors may boost one or more of your primary statistics, may confer certain abilities upon your character and may grant resistance to certain forms of attack. Each Ego type may only be found on certain types of armor - for example, you can find a Shield of Elvenkind but not Boots of Elvenkind. Note: Zangband has extended the original Angband's concept of adding random abilities to the various Ego types considerably. These can be either guaranteed or have only a varying chance of being granted. (See 'Randabil.spo' for details of the random powers of Ego Armor). --- Armor/Shields --- of Resist Acid. A character using such an object will take only one third normal damage from any acid thrown upon him. In addition, armor so enchanted will resist the acid's effects and not be damaged by it. of Resist Lightning. A character using a resist lightning object will take only one third damage from electrical attacks. of Resist Fire. A character using a resist fire object will take only one third damage from heat and fire. of Resist Cold. A character using a resist cold object will take only one third damage from frost and cold. of Resistance. A character wearing armor with this ability will have resistance to Acid, Cold, Fire, and Lightning as explained in each part above. of Elvenkind. This is the same as Resistance armor, only generally better enchanted. It will also make you more stealthy. of Permanence (Robes Only) These robes provide resistance to fire, cold, acid, and electricity and cannot be damaged by acid. They also resist life draining attacks and sustain all your primary statistics. of Reflection (Shields Only) These shields reflect missiles and bolt spells preventing damage to the player character and often damaging the originating monster. The reflection is not 100% effective but nearly so. --- Caps/Helms/Crowns --- of Intelligence This item will both increase and sustain your intelligence. of Wisdom This item and will both increase and sustain your wisdom. of Beauty This item will both increase and sustain your charisma. of the Magi This item will both increase and sustain your intelligence, and grants resistance against fire, frost, acid, and lightning. of Might This item will both increase and sustain your strength, dexterity, and constitution and will also make you immune to any foe's attempt to slow or paralyze you. of Lordliness This item will both increase and sustain your wisdom and charisma. of Seeing This item will grant the ability to see invisible creatures, and will also increase your ability to locate traps and secret doors. In addition it will also prevent you from being blinded. of Infravision This item will increase the range of you infravision and grants infravision if you are a race which does not get it intrinsically. of Light This item provides a permanent light source. of Telepathy This item grants its wielder telepathy. of Regeneration This item will help you regenerate hit points and mana more quickly than normal, allowing you to fight longer before needing to rest. You will use food faster than normal while wearing this crown because of the regenerative effects. --- Cloaks --- of Protection These cloaks cannot be harmed by acid, fire, cold or lightning attacks but do not grant resistance to these elements. of Stealth These cloaks will make your character more stealthy. of Aman These cloaks cannot be harmed by acid, fire, cold or lightning attacks but do not grant resistance to these elements. They also make your character more stealthy. of Immolation These cloaks cannot be harmed by acid or fire attacks. They also grant resistance to fire and give out a fiery aura. of Electricity These cloaks cannot be harmed by acid or electricity attacks. They also grant resistance to electricity and give out an electric aura. --- Gloves/Gauntlets/Sets of Cesti --- of Free Action This item will make you immune to any foe's attempt to slow or paralyze you. A mage-type spellcaster who wields them will not have their mana penalized. of Slaying In addition to its armor bonus, this item will grant a bonus to your +to-hit and +to-dam. of Agility This item will increase your dexterity. A mage-type spellcaster who wields them will not have their mana penalized. of Power This item will increase your strength and will also grant a bonus to your +to-hit and +to-dam. --- Boots --- of Levitation These boots will grant you levitation. of Stealth These boots will make your character more stealthy. of Free Action This item will make you immune to any foe's attempt to slow or paralyze you. of Speed These boots will make your character more speedy. --- Special Items --- Dragon Scale Mails. These extremely rare pieces of armor come in many different colors, each protecting you against the relevant dragons. Naturally they are all resistant to acid damage. They also occasionally allow you to breathe as a dragon would! Dragon Shields and Helms These shields and helms have a chance of granting one or more random resistances. Other items Apart from these there are some very rare, and well made armors in the dungeon with not necessarily any special abilities. These include Adamantite Plate Mail, Mithril Plate Mail, Mithril Chain Mail, Shields of Deflection, and Shadow Cloaks. The first four cannot be damaged by acid because of the quality metals they contain. Shadow Cloaks grant resistance to both light and darkness attacks. ***** === The Resistances === This section describes the various attack-types available to monsters and how they may be resisted. Acid Acid resistance will cut damage from acid attacks by two-thirds. Temporary resistance will cut further damage by another two-thirds. This means that a character with both permanent and temporary resistance will only take one-ninth of the original damage. Acid resistance will also protect your wielded equipment from being corroded by acid but will not protect items in your inventory from being destroyed by acid if they are susceptible to such attacks. Fire Fire resistance will cut damage from fire attacks by two-thirds. Temporary resistance will cut further damage by another two-thirds. This means that a character with both permanent and temporary resistance will only take one-ninth of the original damage. Fire resistance will not protect items in your inventory from being destroyed by fire if they are susceptible to such attacks. Cold Cold resistance will cut damage from cold attacks by two-thirds. Temporary resistance will cut further damage by another two-thirds. This means that a character with both permanent and temporary resistance will only take one-ninth of the original damage. Cold resistance will not protect items in your inventory from being destroyed by cold if they are susceptible to such attacks. Electricity Electricity resistance will cut damage from fire attacks by two-thirds. Temporary resistance will cut further damage by another two-thirds. This means that a character with both permanent and temporary resistance will only take one-ninth of the original damage. Electricity resistance will not protect items in your inventory from being destroyed by electricity if they are susceptible to such attacks. Poison Poison resistance will cut damage from poison attacks by two-thirds. Temporary resistance will cut further damage by another two-thirds. This means that a character with both permanent and temporary resistance will only take one-ninth of the original damage. Poison resistance will also protect you from becoming 'poisoned' but characters will not be healed of their 'poisoned' status if they become resistant to poison after already being poisoned. This must be cured by other means. Confusion Confusion resistance will reduce the damage taken from confusion attacks and breaths. It will also prevent your character from becoming 'Confused' (if you are confused you will be unable to read scrolls, cast spells or prayers and will have a dramatically reduced ability to use magical devices). In particular, water attacks may confuse as a side-effect - this is prevented if you have confusion resistance Light Light breaths damage the player and cause blindness as a side-effect. Light resistance reduces the damage and also prevents blindness caused from light attacks (but not from other attacks. Dark Dark breaths damage the player and cause blindness as a side-effect. Dark resistance reduces the damage and also prevents blindness caused from dark attacks (but not from other attacks. In addition, some high-level monsters can cast Darkness Storms, these are a particularly dangerous form of attack but are resisted if you gave Dark resistance. Blindness Resist blindness will protect you from spells which blind and from being "hit to blind" (a few monsters can do this) as well as from the blindness (but not the damage) caused by light and darkness breaths. Sound Sound resistance reduces damage from sound attacks and also the stunning side effects of sound and some other attacks. (Sound, plasma, water, force, gravity and shards attacks may all stun the player as may ice-bolt attacks). Sound resistance does not protect against stunning from melee attacks. Fear Fear resistance gives immunity to attacks which make your character 'Afraid' (being afraid prevents your character from participating in melee combat but does not otherwise affect you). Shards Shards resistance will reduce the damage taken from shards attacks (this includes a Cyberdemon's Rocket Launchers). It also prevents the cuts that come from these attacks (and also ice-bolts). Some shards attacks may also stun the player, this is prevented if you have sound resistance and not by shard resistance. Nexus Nexus attacks are one of the most annoying attacks in Zangband but will rarely kill you directly. A Nexus attack will damage the player and may teleport the player, teleport-level the player or swap two of your primary statistics around. Nexus resistance will reduce the damage taken from such attacks and prevent the side-effects but will not prevent you from being teleported by non-nexus attacks (for this you will need an item granting anti-teleportation). Chaos Chaos attacks are one of the most dangerous attack types in Zangband. They have a relatively high maximum damage, and may also confuse the player, cause hallucination, reduce your character's experience and cause you to mutate. Chaos resistance will the damage taken from such attacks and negate all side-effects. Nether Another dangerous attack type. Nether attacks are common deeper in the dungeon, have a high maximum damage and will also cause your character's experience to be drained. Nether resistance will reduce the damage taken and the experience loss. It will not reduce the experience loss from non-nether attacks (for this you need hold-life). Gravity Gravity attacks damage the player and also cause you (and monsters they hit) to be teleported a short distance. A side-effect of this is that since the effect is calculated on each spot in turn, it is possible to be blinked into a spot which hasn't been calculated yet but is in line to be affected by the breath - the net result is to be hit twice, blinked twice and damaged twice by the same breath. Gravity attacks may also stun your character. Levitation provides resistance to the damaging effect of gravity, resist teleportation prevents the blinking around and resistance to sound prevents you from becoming stunned. Disenchantment Disenchantment attacks damage the player and may result in the to-hit, to-dam and to-AC bonuses on your wielded equipment being reduced. Disenchantment resistance will reduce the damage you take and will prevent the disenchantment of your equipment. ***** --- Unresistable Attacks --- There is no resistance to the following types of attacks although some of their side-effects may be resisted. Toxic Waste Resist poison will resist the damage from Toxic Waste attacks but you may still suffer some side effects unless you also have chaos resistance. Time Time attacks damage the player, drain experience and may reduce one or more of your primary statistics. Plasma Plasma can damage you, stun you and burn your equipment. Fire immunity (not resistance) prevents burning of equipment, sound resistance prevents the stunning but there is no resistance to the damage. Inertia Inertia will damage you and slow you down by -10 speed. There is no resistance to damage or effects. Mana and Disintegration There is no resistance to the mana and disintegration attacks. ***** --- Immunities --- It is also possible that you may find items which grant immunity to one or (very occasionally) more of the low resistances ie. Acid, Fire, Cold and Electricity. In this case, when wielded, you will take no damage from attacks of the relevant kind and, in addition, your equipment and inventory are safe from being damaged or destroyed by the attack. In addition free action, fear and blindness resistance may be thought of as immunities since having they protect totally from the respective form of attack. ***** --- Miscellaneous --- The following are not properly resistances but may be sensibly included on this page. Free Action Free action is immunity to paralysis: this is foolproof except in the special case of the Ancient and Foul Curse which requires a successful saving throw as well as free action to avoid should it attempt to paralyze you. Spells, potions of paralysis and being hit-to-paralyze are completely protected against. Hold-life Hold Life is protection from experience draining. It is 90% foolproof: 10% of draining attacks will still drain you, but by less than would otherwise have been the case. ***** === Recovering from Attacks === You cannot combat with the creatures in Zangband and expect to come out unscathed every time. When a monster inflicts damage on you, you will need to take steps to recover from the damage as soon as possible. --- Healing Items --- Healing is available in multiple forms including: Potions and Staves of Cure Light Wounds, Potions of Cure Serious Wounds, Potions of Cure Critical Wounds, Potions, Staves and Rods of Healing, Potions of *Healing* and Potions of Life, from weakest to strongest. Both the Life and Nature realms contains spells to heal the player. Cure Light/Serious/Critical Wounds these generally restore a small amount of hitpoints and may also remove temporary bad effects like confusion, blindness, poison and cuts. The amount of hitpoints healed and the number of other bad effects cured increases from Light to Serious to Critical. Healing restores 300 hitpoints and removes all temporary bad effects with the exception of fear and cures all wounds. *Healing* restores 1000 hitpoints and removes all temporary bad effects and cures all wounds Potions of Life restores 5000 hitpoints (about 5 times more than you'll ever have), restores all drained stats and experience, and removes all bad effects except hunger. These are very rare; save them in your home for a dangerous fight. --- Restoring Items --- From time to time your primary statistics and/or experience may be drained. Statistics can be restored by quaffing a Potion of Restore {name of stat} or a Potion of {Name of stat} or by eating a Mushroom of Restoring. Some towns may provide this service for a fee. Experience may be restored by quaffing a Potion of Restore Life Levels or using a Rod of Restoration. You may also regain it in the normal course of your adventuring or by quaffing a Potion of Experience. The Life realm has a spell called Restoration which restores any drained experience. --- Other --- There are also other items in the dungeon which may help you to recover from various things that monsters may do to you. For example, some mushrooms may help you recover from confusion, a Potion of Heroism will prevent you from becoming afraid, a Potion of Slow Poison will reduce the effects of becoming poisoned, etc. Most objects' names give a clear idea of the benefits and dangers of using them and so an exhaustive list is not provided in this document. -- Original : (??), Leon Marrick and Chris Weisiger Updated : (??) Updated : Zangband DevTeam Last update: January 13, 2000 zangband/lib/help/dungeon.hlp0000644000000000000000000000167010250356274015212 0ustar rootrootThe Town and Dungeon. Please choose one of the following online help files: (0) Town and Dungeon (dungeon.txt) (1) Symbols on Your Map (dungeon.txt#MapSymbols) (2) Within the Dungeon (dungeon.txt#WithinDungeon) (3) Objects in the Dungeon (dungeon.txt#Objects) (4) Mining (dungeon.txt#Mining) (5) Staircases, Doors, Passages & Rooms (dungeon.txt#StairsDoorsEtc) (6) Level Feelings (dungeon.txt#Feelings) (7) Random Quests (dungeon.txt#RandomQuests) (?) Help System Commands (helpinfo.txt) ***** [0] dungeon.txt ***** [1] dungeon.txt#MapSymbols ***** [2] dungeon.txt#WithinDungeon ***** [3] dungeon.txt#Objects ***** [4] dungeon.txt#Mining ***** [5] dungeon.txt#StairsDoorsEtc ***** [6] dungeon.txt#Feelings ***** [7] dungeon.txt#RandomQuests zangband/lib/help/dungeon.txt0000755000000000000000000003431710250356274015255 0ustar rootroot=== The Dungeon === Although Zangband contains an extensive town level featuring multiple towns and a large wilderness area, the bulk of your adventuring will take place in the dungeon. Symbols appearing on your screen will represent the dungeon's walls, floor, objects, features, and creatures lurking about. In order to direct your character through his adventure, you will enter single character commands (see command.txt [1]). ***** === Symbols On Your Map === Symbols on your map can be broken down into three categories: Features of the dungeon such as walls, floor, doors, and traps; Objects which can be picked up such as treasure, weapons, magical devices, etc; and creatures which may or may not move about the dungeon, but are mostly harmful to your character's well being. Some symbols are used to represent more than one type of entity, and some symbols are used to represent entities in more than one category. The "@" symbol (by default) is used to represent the character. It will not be necessary to remember all of the symbols and their meanings. The "slash" command ("/") will identify any character appearing on your map Note that you can use a "user pref file" to change any of these symbols to something you are more comfortable with. --- Features that do not block line of sight --- . A floor space % (Green) A Tree . A trap (hidden) ~ (Light Blue) Shallow Water ^ A trap (known) ~ (Dark Blue) Deep Water ; A glyph of warding ~ (Orange) Shallow Lava ' An open door ~ (Red) Deep Lava ' A broken door < A staircase up > A staircase down --- Features that block line of sight --- # A secret door # A wall + A closed door % A mineral vein + A locked door * Treasure in wall / Pattern + A jammed door : A pile of rubble ***** === Within The Dungeon === Once your character is adequately supplied with food, light, armor, and weapons, he is ready to enter the dungeon. Move on top of the '>' symbol and use the "Down" command (">"). Your character will enter a maze of interconnecting staircases and finally arrive somewhere on the first level of the dungeon. Each level of the dungeon is fifty feet high (thus dungeon level "Lev 1" is often called "50 ft"), and is divided into (large) rectangular regions (several times larger than the screen) by titanium walls. Once you leave a level by a staircase, you will never again find your way back to that region of that level, but there are an infinite number of other regions at that same "depth" that you can explore later. So be careful that you have found all the treasure before you leave a level, or you may never find it again! The monsters, of course, can use the stairs, and you may eventually encounter them again. In the dungeon, there are many things to find, but your character must survive many horrible and challenging encounters to find the treasure lying about and take it safely back to the town to sell. There are two sources for light once inside the dungeon. Permanent light which has been magically placed within rooms, and a light source carried by the player. If neither is present, the character will be unable to see. This will affect searching, picking locks, disarming traps, reading scrolls, casting spells, browsing books, etc. So be very careful not to run out of light! A character must wield a torch or lamp in order to supply his own light. A torch or lamp burns fuel as it is used, and once it is out of fuel, it stops supplying light. You will be warned as the light approaches this point. You may use the "Fuel" command ("F") to refuel your lantern (with flasks of oil) or your torch (with other torches), so it is a good idea to carry extra torches or flasks of oil, as appropriate. There are rumors of objects of exceptional power which glow with their own never-ending light. ***** === Objects In The Dungeon === The dungeons are full of objects just waiting to be picked up and used. How did they get there? Well, the main source for useful items are all the foolish adventurers that proceeded into the dungeon before you. They get killed, and the helpful creatures scatter the various treasure throughout the dungeon. Most cursed items are placed there by the joyful evil sorcerers, who enjoy a good joke when it gets you killed. One item in particular will be discussed here. The scroll of "Word of Recall" can be found within the dungeon, or bought at the temple in town. It acts in two manners, depending upon your current location. If read within the dungeon, it will teleport you back to town. If read in town, it will teleport you back down to the deepest level of the dungeon which your character has previously been on. This makes the scroll very useful for getting back to the deeper levels of Zangband. Once the scroll has been read it takes a while for the spell to act, so don't expect it to save you in a crisis. Reading a second scroll before the first has had a chance to take effect will cancel both scrolls. Since an accidental dive to a new depth (via a trapdoor, for example), may result in the Word of Recall dungeon depth being 'broken', so to speak (meaning that the next Word of Recall in town will take you back deeper than you would like to), there is a new feature in Zangband which allows you to read a scroll of Word of Recall on a different level and 'reset' the recall depth to that level (instead of the deepest level). A more complete description of Zangband objects is found elsewhere in the documentation (see objects.txt [2]). ***** === Mining === Much of the treasure within the dungeon can be found only by mining it out of the walls. Many rich strikes exist within each level, but must be found and mined. Quartz veins are the richest, yielding the most metals and gems, but magma veins will have some hordes hidden within. Mining is rather difficult without a pick or shovel. Picks and shovels have an additional magical ability expressed as '(+#)'. The higher the number, the better the magical digging ability of the tool. A pick or shovel also has plusses to hit and damage, and can be used as a weapon, because, in fact, it is one. When a vein of quartz or magma is located, the character may wield his pick or shovel and begin digging out a section. When that section is removed, he can locate another section of the vein and begin the process again. Since granite rock is much harder to dig through, it is much faster to follow the vein exactly and dig around the granite. There is an option for highlighting magma and quartz. At a certain point, it becomes more cumbersome to dig out treasure than to simply kill monsters and discover items in the dungeon to sell. However, early on mineral veins can be a wonderful source of easy treasure. If the character has a scroll, staff, or spell of treasure location, he can immediately locate all strikes of treasure within a vein shown on the screen. This makes mining much easier and more profitable. Note that a character with high strength and/or a heavy weapon does not need a shovel/pick to dig, but even the strongest character will benefit from a pick if trying to dig through a granite wall. It is sometimes possible to get a character trapped within the dungeon by using various magical spells and items. So it can be a good idea to always carry some kind of digging tool, even when you are not planning on tunneling for treasure. There are rumors of certain incredibly profitable rooms buried deep in the dungeon and completely surrounded by titanium and granite walls, requiring a digging implement or magical means to enter. The same rumors imply that these rooms are guarded by incredibly powerful monsters, so beware! ***** === Staircases, Secret Doors, Passages, and Rooms === Staircases are the manner in which you get deeper or climb out of the dungeon. The symbols for the up and down staircases are the same as the commands to use them. A "<" represents an up staircase and a ">" represents a down staircase. You must move your character over the staircase before you can use it. Each level has at least one up staircase and at least two down staircases. There are no exceptions to this rule. You may have trouble finding some well hidden secret doors, or you may have to dig through obstructions to get to them, but you can always find the stairs if you look hard enough. Stairs, like titanium walls, and the doors into shops, cannot be destroyed by any means. Many secret doors are used within the dungeon to confuse and demoralize adventurers foolish enough to enter. But with some luck, and lots of concentration, you can find these secret doors. Secret doors will sometimes hide rooms or corridors, or even entire sections of that level of the dungeon. Sometimes they simply hide small empty closets or even dead ends. Secret doors always look like granite walls, just like traps always look like normal floors. Like normal doors, a secret door may also be locked or jammed. Creatures in the dungeon will generally know and use these secret doors, and can often be counted on to leave them open behind them when they pass through. Often the creature won't know how to open a door and will simply bash it open. Once a door is bashed open, it is forever useless and cannot be closed. ***** === Level Feelings === Once you have been on a particular dungeon level for a while, you will receive a 'feeling' prompt representing what your intuition tells you about the quality of objects and the difficulty of the monsters found on that level. You may check this prompt again at any time after receiving it by pressing 'Ctrl-F'. The actual 'feeling' prompt is generated based on a number of factors. Things which increase the feeling level include the presence of vaults and certain other special rooms, out of depth objects and monsters and objects of a certain quality (for example, ego items, artifacts and other objects that are considered 'great' (see objects.txt)). A feeling is only indicative of the level at the time you entered it and has no impact on subsequent monsters generated or items dropped. In Zangband, the nastier the feeling prompt, the better the level. From worst to best, the prompts are as follows: 'What a boring place...' 'This level looks reasonably safe.' 'You don't like the look of this place.' 'You feel your luck is turning...' 'You feel nervous.' 'You have a bad feeling...' 'You have a very bad feeling...' 'This level looks very dangerous.' 'You nearly faint as horrible visions of death fill your mind!' Zangband also uses two other feeling prompts to denote special things. If you have not yet been on a level long enough to qualify for a prompt and press 'Ctrl-F', you will be given the prompt: 'Looks like any other level.' If you are playing in non-preserve mode, you may also occasionally receive the following prompt: 'You feel there is something special about this level.' A special feeling means one of two things, there is either a special room or vault on the level or there is an artifact on the level. As you get deeper on the dungeon, special feelings become increasingly less common for special room and vaults and are only commonly given when an artifact has been generated. Note it is possible that there could be both a vault and an artifact or more than one artifact on a special level so you should never leave a special level without fully exploring it unless your character's continued survival is in question. ***** === Random Quests === During character generation, you will be asked to input the number of random quests you wish to participate in. You may choose any number from 0 to 49. Random quests are always of the type 'Kill a random number of an out-of depth monster'. There is a limit to the number of levels a random quest monster may be out-of-depth but these quests can still be very dangerous. Random quests always appear on even levels and are equally spaced throughout the dungeon (for example, a choice of 49 random quests results in one such quest on every even level until level 98 (level 100 is the Serpent quest)). On entering a random quest level you will be told what the quest monster is and how many you have to kill (for example, 'This level is guarded by 10 young green dragons'). On random quest levels, no down staircases are generated until the last monster is killed which means that you cannot continue further into the dungeon until you have completed your quests. When you kill the last quest monster, the down staircase will be created and the monster will drop an item of 'excellent' quality or above. This is true even of monsters that do not normally drop items. Using the services of the Trump Tower (see the Town section) can teleport you past a particularly nasty quest but you must come back and finish it later if you want your character to be a winner. Random quests can be either 'easy' or 'hard' and you must choose which type you want during character generation. This choice is irrevocable for the duration of the character's dungeon diving career. 'Easy' means that the game tracks how many quest monsters you have killed on each attempt at the quest and adds these to the total count. This allows you to kill one or more quest monsters, leave the level and return later to finish up. 'Hard' means that each time you enter the quest level the number of quest monsters remaining is reset. In other words, you have to kill all the quest monsters in one go. 'Hard' quests can allow you to 'farm' the quest monsters by killing all but one, leaving the level and repeating. This allows you to collect and sell the dropped items and gain repeated experience for killing the monsters. This technique is frowned upon by many players and should be noted in any 'winner' post to the newsgroup. Note that to avoid some ugly abuses available to players, the Genocide, Mass-Genocide and *Destruction* spells are prevented from working on random quests monsters. Random quest monsters may also not be cloned or tamed. -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: May 29, 2000 ***** Begin Hyperlinks ***** [1] command.txt ***** [2] objects.txt zangband/lib/help/gambling.txt0000755000000000000000000000200410250356274015362 0ustar rootroot=== Gambling Rules === Between : Three 12-sided dice rolled; 2 black, 1 red. The red die must be between both black to win. If the red die. matches a black die, you lose. Pays 3 to 1 Craps: Two dice are rolled. On first roll, a 7 or 11 wins. A 2,3 or 12 loses. Otherwise roll until the first roll is matched (win) or a 7 is rolled (loss). Pays 2 to 1 Wheel: Pick a number from 0-9. If the number shows on wheel after it stops spinning, you win. Pays 8 to 1 Slots: Three dice rolled. Matches win gold. Numbers are: 1=Lemon, 2=Orange, 3=Sword, 4=Shield, 5=Plum, 6=Cherry Payoffs are as follows: Cherry Cherry Lemon 2-1 Cherry Cherry Orange 3-1 Cherry Cherry Sword 4-1 Cherry Cherry Shield 5-1 Cherry Cherry Plum 6-1 Lemon Lemon Lemon 4-1 Orange Orange Orange 16-1 Sword Sword Sword 6-1 Shield Shield Shield 25-1 Plum Plum Plum 9-1 Cherry Cherry Cherry 36-1 -- This file was last updated for Kangband 2.8.3i. zangband/lib/help/general.hlp0000644000000000000000000000150410250356274015164 0ustar rootrootAn Introduction to Zangband and general information. Please choose one of the following online help files: (0) General Information (general.txt) (1) An Overview (general.txt#Overview) (2) Winning the Game (general.txt#Winning) (3) Upon Death and Dying (general.txt#UponDeath) (4) The Zangband DevTeam (general.txt#DevTeam) (5) Zangband on the Web (general.txt#Web) (6) Version Numbering (general.txt#VersionNumber) (7) Version History (version.txt) (?) Help System Commands (helpinfo.txt) ***** [0] general.txt ***** [1] general.txt#Overview ***** [2] general.txt#Winning ***** [3] general.txt#UponDeath ***** [4] general.txt#DevTeam ***** [5] general.txt#Web ***** [6] general.txt#VersionNumber ***** [7] version.txt zangband/lib/help/general.txt0000755000000000000000000001525210250356274015230 0ustar rootroot=== General Information === This document gives a brief introduction to Zangband. You will probably want to browse through all the "help files", especially the sections on available commands (see command.txt [1] and commdesc.txt [2]), before beginning any serious adventuring... ***** === An Overview === The game of Zangband is a single player dungeon simulation. A player may choose from a number of races, classes and magic realms when creating a character (see charattr.txt [3]), and then "run" that character over a period of days, weeks, even months. Deep down inside, the real objective of the game is to increase your experience, and certain other characteristics, and also to collect useful items, to give you a decent chance against the great Serpent of Chaos, who lurks somewhere in the depths of the dungeon. The player will begin his adventure on the town level where he may acquire supplies, weapons, armor, and magical devices by bartering with various shop owners. After preparing for his adventure, the player can descend into the dungeon where fantastic adventures await his coming! ***** --- Winning The Game --- Once your character has killed Oberon, who lives on level 99 (4950') in the dungeon, a magical staircase will appear that will finally allow you to reach level 100. The Serpent of Chaos lurks on this level of his dungeon, and you will not be able to go below his level until you have killed him. Try to avoid wandering around on level 100 unless you are ready for him, since it has a habit of coming at you across the dungeon, to slay you for your impudence. The Serpent of Chaos cannot be killed by some of the easier methods used on normal creatures. The Serpent of Chaos, like all other "Unique" monsters, will simply teleport away to another region of the level if you attempt to use a spell such as destruction is upon it. The Serpent of Chaos, like some other monsters, cannot be polymorphed, slept, charmed, or genocided. Magical spells like Mana Storm and Orb of Draining are effective against it, as are some of the more powerful weapons, but it is difficult to kill and if allowed to escape for a time it will heal itself rapidly. If you should actually survive the attempt of killing the Serpent of Chaos, you will receive the status of WINNER. You may continue to explore, and may even save the game and play more later, but since you have defeated the toughest creature alive, there is really not much point. Unless you wish to listen to the rumors of a powerful ring buried somewhere in the dungeon... When you are ready to retire, simply "commit suicide" ("^K") to have your character entered into the high score list as a winner. Note that until you retire, you can still be killed, so you may want to retire before wandering into a hoard of Cyberdemons... ***** --- Upon Death and Dying --- If your character falls below 0 hit points, he has died and cannot be restored. A tombstone showing information about your character will be displayed. You are also permitted to get a record of your character, and all your equipment (identified) either on the screen or in a file. Whether your character is killed or you retire victorious, it will leave behind a reduced save file, which contains only the monster memory (see attack.txt#MonsterMemory [4]) and your option choices. It may be restored, in which case the new character is generated exactly as if the file was not there, but the new player will find his monster memory containing all the experience of past incarnations. ***** === The Zangband DevTeam === Zangband is currently maintained by a small development team which includes both Zangband's original creator, Topi Ylinen, and Robert Ruehlmann who maintained and developed the game from versions 2.01d through to 2.2.6c. Robert coordinates the efforts of the DevTeam and is the designated project leader. Since its inception, Zangband has developed a reputation for being more difficult than other Angband variants and its maintainers are known for their devious and contrary natures. The DevTeam neither confirms nor denies its intention to live up to this perception. ***** === Zangband on the Web === --- zangband.org --- Zangband.org is the official Zangband website. It contains links to the latest source code and binary downloads as well as documentation, spoilers and other useful and fun zangband-related pages. --- Sourceforge.net --- The DevTeam recently moved its cvs repository to sourceforge.net. This also allows us to make use of sourceforge.net's excellent utilities including bug tracking, 3rd-party patch submission, mailing lists, etc. If you want to follow zangband's development closely, you should get a user account with sourceforge and visit our project site located at http://sourceforge.net/projects/zangband/ frequently. --- FTP Sites --- Zangband sources and executables are uploaded regularly to the official Angband ftp site (ftp://clockwork.dementia.org/angband/). Zangband files are located in the ./Variant/ZAngband/ subdirectoy. --- ZangbandTk --- ZangbandTk is a graphical Windows port of Zangband which adds several new features and a very nice interface to Zangband. If you use Windows, you may want to take a look at the AngbandTk homepage which can be found at (http://persweb.direct.ca/dbaker/angbandtk.html/). ZangbandTk is more properly termed a variant of Zangband but, other than the graphical interface, the differences are fairly minor. ZangbandTk is typically only updated for official releases of Zangband and not for the development releases. ZangbandTk is maintained by Tim Baker. --- The Angband Newsgroup --- The Angband newsgroup (rec.games.roguelike.angband) is a forum for the discussion of all things relating to Angband and its variants (including Zangband) and is a good place to ask various questions about both game play and game design. Be sure to include '[Z]' in your subject heading so people know your question is about Zangband. --- #Angband IRC Chat --- Join the #Angband IRC channel located at irc.worldirc.org. Most of the DevTeam are regulars and sometimes we're even talking about Zangband!! ***** === Version Numbering System === Zangband uses a version numbering system similar to that of Linux. Odd minor version numbers (for example 2.3.2) denote a development version. Even minor version numbers (for example 2.2.7 ) denote a stable release. The development versions are released for beta-testing and are likely to contain bugs of varying significance. -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: February 19, 2001 ***** Begin Hyperlinks ***** [1] command.txt ***** [2] commdesc.txt ***** [3] charattr.txt ***** [4] attack.txt#MonsterMemory zangband/lib/help/help.hlp0000755000000000000000000000215010250356274014500 0ustar rootrootWelcome to the Zangband Online Help System. Please choose one of the following online help files: (0) General Information (general.hlp) (1) Creating a Character (birth.hlp) (2) Character Attributes (charattr.hlp) (3) The Town and Wilderness (town.hlp) (4) The Dungeon (dungeon.hlp) (5) Zangband Objects (objects.hlp) (6) Zangband Monsters (monster.hlp) (7) Attacking Monsters (attack.hlp) (8) Defending Yourself (defend.hlp) (9) Zangband Magic System (magic.hlp) (a) Zangband Commands (command.hlp) (b) Zangband Options (option.hlp) (c) User Preference Files (pref.hlp) (d) Zangband Spoilers (spoiler.hlp) ***** [0] general.hlp ***** [1] birth.hlp ***** [2] charattr.hlp ***** [3] town.hlp ***** [4] dungeon.hlp ***** [5] objects.hlp ***** [6] monster.hlp ***** [7] attack.hlp ***** [8] defend.hlp ***** [9] magic.hlp ***** [a] command.hlp ***** [b] option.hlp ***** [c] pref.hlp ***** [d] spoiler.hlp zangband/lib/help/helpinfo.txt0000644000000000000000000000157410250356274015416 0ustar rootroot=== Online Help Commands === Esc : Leave the online help system. ? : Get help for the online help (this file). < : Return to the previous help file or menu. Space : Advance one page (wraps to the start at the end of the file). Return : Advance one line (wraps to the start at the end of the file). + : Advance half a page. - : Back-up half a page. # : Jump to a specific line (defaults to line zero). % : Jump to a specific file (defaults to "help.hlp"). = : Highlight lines containing a string (case sensitive). / : Search for a string (use '/' + Return to continue a search). (Use '#' + '0' + Return + '/' + Return to restart a search.) | : Save the current document to a file. [0..9] [a..z] [A..Z] : Press the indicated number to activate a link. -- Original : (??) Updated : Zangband DevTeam Last update: October 3, 2000 zangband/lib/help/magic.hlp0000644000000000000000000000143310250356274014630 0ustar rootrootZangband Magic System. Please choose one of the following online help files: (0) Zangband Magic System (magic.txt) (1) Class Spell Ability (magic.txt#ClassSpellAbil) (2) The Magic Realms (magic.txt#MagicRealms) (3) On Casting Spells ... (magic.txt#CastingSpells) (4) Spell Types (magic.txt#SpellTypes) (5) Hints and Tips (magic.txt#Hints) (6) Spell Lists (magic.txt#SpellLists) (?) Help System Commands (helpinfo.txt) ***** [0] magic.txt ***** [1] magic.txt#ClassSpellAbil ***** [2] magic.txt#MagicRealms ***** [3] magic.txt#CastingSpells ***** [4] magic.txt#SpellTypes ***** [5] magic.txt#Hints ***** [6] magic.txt#SpellLists zangband/lib/help/magic.txt0000755000000000000000000005705610250356274014703 0ustar rootroot=== Zangband Magic System === In the official releases of Angband there are only two types magic spells: Magic spells and priestly prayers. If the character is a mage, ranger or a rogue (s)he can learn magic spells; if (s)he is a priest or a paladin, (s)he can learn prayers. All mages can learn the same spells and all priests can learn the same spells. Zangband uses a more complex "realms of magic" system inspired by the commercial fantasy strategy game Master of Magic (Microprose), which in turn has supposedly borrowed it from the card game Magic the Gathering (by Wizards of the Coast). The magic system, as implemented in Zangband, consists of seven realms: Life, Arcane, Sorcery, Nature, Trump, Chaos and Death. While any given realm of magic typically includes spells of a certain type or theme, each realm should have enough high-level spells to remain effective throughout the later stages of the game. The main difference lies in how they support your playing strategy. For example, some offer attacking spells which let you directly hurt your enemy, while others offer spells for protection, healing and gathering information. In general, Life is defensive although it offers some good attacking spells against evil monsters, Arcane offers utility spells with limited offensive capability, Sorcery offers utility spells and some defensive capability, Nature specializes in the elements and offers both defensive and offensive spells, Trump specializes in teleportation and summoning spells and Chaos and Death are offensive. In Zangband, spellcasting classes can select either one or two realms from those available. Note that certain realms may be prohibited for some classes. Since a character can have (at most) two realms of magic, and the old spells have been split between the existing realms, on the first glance it may seem that this system makes spell-users less powerful. However, the opposite holds true. While a given realm of magic typically includes spells of a certain type, new ultra powerful rare high-level spells have been added to many realms. Any spell realm should have enough high-level spells even for the later stages of the game. The main difference lies in how they support your playing strategy: some offer "weapon" spells which let you directly hurt your enemy, while others offer spells for protection, healing and gathering information. In standard Angband, there were 9 spellbooks for all spellcasters. In Zangband, there are 4 spellbooks per realm. Two of them can be bought in the town stores and the remaining two must be found in the dungeon (although they may occasionally appear in the Black Market). The exception to this rule is the Arcane realm since all Arcane spellbooks can be found in the magic shop or book store. Just as one might expect, Arcane magic lacks the really powerful high level spells. A character with two realms of magic will thus need to carry a maximum of 8 spellbooks while a character with only one realm of magic will only need to carry a maximum of 4 spellbooks. All realms have 32 spells, and each book contains 8 spells. ***** === Class Spell Ability === Warriors Warriors cast no spells. They hate magic. In fact, they even gain experience for destroying high level spellbooks. Mages Mages have the least restrictions in choosing and learning spells. They can freely choose any two realms when a character is created: in the current version, all seven realms are available to them, although their natural inclination makes Life magic fairly hard for them. Otherwise, a mage tends to learn and cast all the spells in his / her realms better than any other character. The ability to select both realms of magic (which no other character class can do) allows the best support for experimenting and combining different realms, and, thus, for different playing strategies as well. Priests There are two types of priests in Zangband: the 'ordinary' priests who, select Life magic as their primary realm, and the 'dark' priests, who select Death magic instead of Life magic. No priest can have both realms (unless (s)he was created in Zangband 2.0.0 or 2.0.1). Priests can also select a secondary realm from the other five realms, and should be able to learn all spells in it as well, even if not as efficiently as mages. However, when learning spells, priests cannot voluntarily decide which spells to study: they are rewarded with new prayers by their patron deities, with no money-back satisfaction guarantee. It should also be noted that since the natural inclination of a priest is towards Life magic, priests who select Life magic will be able to learn their prayers faster and better than their evil colleagues with Death magic. Rogues There are several subtypes of Rogues in Zangband: the exact 'type' is determined by the realm of magic chosen by the Rogue. The common Thief, agent of the underworld, will probably be content with Arcane magic and its wide applicability. The Burglar, on the other hand, is more interested in the Sorcery spells, which allow him or her to do the job fast and efficiently. The Assassins' partiality for Death magic is well known, and they are feared for it. Finally, there is the Card Shark, who will opt for Trump magic, and shuffles the decks with amazing proficiency. All Rogues have certain limitations on which spells they can learn, and they are not too fast to learn new spells. Rangers All rangers are trained in Nature magic, and all Nature spells are available to them. They even learn these spells almost as fast as mages. They can also select a secondary realm (from Sorcery, Arcane, Trump, Nature, Chaos and Death), but they are slow learners in them, and may find themselves unable to learn some of the highest level spells. Paladins Paladins are trained in Life magic (only), and they despise the other realms of magic (which they regard as the Devil's work). Like priests, they cannot select which prayers to learn but are rewarded with new prayers by their deities. They can learn all Life spells, but not as fast as priests. Death Knights study Death magic instead of Life magic, but in other respects they are similar to normal paladins. Death Knights can learn all Death spells. The endless enmity between these two subtypes is most evident in their attitudes to other realms of magic: an 'ordinary' detests the other realms of magic (than Life) so strongly that he or she will even gain experience for destroying their high-level spellbooks. A Death Knight, on the other hand, is very tolerant of the other realms -- to annoy Paladins, perhaps, if for no other reason. A Death Knight will, however, be very offended by the sight of Life spellbook, and will do anything to destroy it; and this will even give him or her experience, if the Life spellbook in question is a high-level one. Warrior-Mages Warrior-mages begin the game with Arcane magic, and they can freely select another realm of magic. Although they do not gain new spells as fast as regular mages, they will eventually learn every spell in both realms, thus making a very competitive choice for players who appreciate Arcane magic. Chaos Warriors Chaos Warriors are, as one might expect, trained in Chaos magic. They are not interested in any other form of magic. They can learn every Chaos spell. Monks The different sects of monks are devoted to different areas of magic. The typical monk is interested in the harmony of the nature, and studies Nature magic. An idealist monk would select Life magic, and try work to benefit his neighbor. But there also are dark monks, who specialize in Death magic. A monk can thus select any one of these three Realms. They will eventually learn all prayers in the discipline of their choice. Mindcrafter Although the powers of a Mindcrafter may seem like magic, this is not -- strictly speaking -- the case. They are mental powers, independent of the ordinary sources of magic. Consequently, Mindcrafters are not interested in 'magic' and learn no spells. High Mage High mages are mages who specialize in one particular field of magic and learn it very well -- much better than the ordinary mage. For the price of giving up a second realm of magic, they gain substantial benefits in the mana costs, minimum levels, and failure rates in the spells of the realm of their specialty. ***** === The Realms of Magic === Life Life is magic is 'good' magic; it relies mostly on healing and protective spells. It does have a few attack spells as well, but these are mostly used for harming and banishing foul minions of evil. It is rumored that there is a secret high level prayer which will make the priest (or paladin) completely impervious to all forms of hostile action. Sorcery Sorcery is a `meta` realm, including enchantment and general spells. It provides superb protection spells (such as Teleport spells for fleeing and even Globe of Invulnerability), spells to enhance your odds in combat (Slow Monster, Haste Self, Confuse Monster) and, most importantly, a vast selection of spells for gathering information: in addition to the usual detection and identify spells, one of the standard spellbooks has a spell called Identify True, which gives you full knowledge of a given object! In the rare books, there are spells with which you can enchant your items or turn unwanted items to gold. However, Sorcery has one weakness: it has no spells to deal direct damage to your enemies. Arcane Even more than Sorcery, Arcane magic is a general purpose realm of magic. It attempts to encompass all 'useful' spells from all realms, and almost succeeds, with the probable exception of *Identify*. This is the downside of Arcane magic: while Arcane does have all the necessary 'tool' spells for a dungeon delver, it has no ultra-powerful high level spells. As a consequence, all Arcane spellbooks can be bought in town. It should also be noted that the 'specialized' realms (i.e. other than Arcane) usually offer the same spell at a lower level and cost. Arcane magic is therefore perhaps not recommendable as one's only realm of magic, but it should be a very nice addition to fill up the gaps in the selection of tools spells in another realm. Trump Trump magic seems an independent source of power, although its supposed association with Chaos magic has been mentioned in several places. Although it lacks the unpredictable chaotic side-effects of Chaos magic, it has a few spells whose exact effects seem more or less random. One such spell is Shuffle: the Trump spellbooks actually consist of decks of trumps, and the Shuffle spell allows the caster to shuffle the deck and pick one card at random. The effect depends on the card picked, and is not always pleasant. In the Amber universe, the Trump gateways are also a major method of transportation: Trump magic has, indeed, an admirable selection of teleportation spells. Since the Trump gateways can also be used to summon other creatures, Trump magic has an equally impressive selection of summoning spells. However, not all monsters appreciate being drawn to another place by Trump user. The only summoned creatures whose loyalty is guaranteed are the Phantasmal Servants, who lack a will of their own (but can develop one, if you treat them badly). Nature Early levels may be rather difficult for a spellcaster relying on Nature magic, as the early spells offer only limited protection, detection and curing capabilities. However, at higher levels there are very useful offensive spells available, especially should the spellcaster be lucky enough to find an extremely rare spellbook called "Nature's Wrath". Nature also has a spell of Herbal Healing, which is the only powerful healing spell outside the realm of Life magic. Chaos There are few types of magic more unpredictable and difficult to control than Chaos magic. Chaos is the very element of unmaking, and the Chaos spells are the most terrible weapons of destruction imaginable. From Magic Missile and Acid Bolt to the medium level Fire Ball and Doom Bolt, and finally to the awesome spells of Invoke Logrus, Mana Storm and Call the Void, Chaos offers an unsurpassable arsenal of attack spells. The caster can also call on the primal forces of Chaos to induce mutations in his/her opponents and even him/herself, but otherwise, Chaos has no protective spells. Beware, though, Chaos spells are known to backfire easily and product undesired effects. This is especially true in the version 2.1.0 of Zangband and later, where the forces of Chaos can easily twist the hapless individual foolish enough to invoke them, turning them horrendous spawns of Chaos. Death There is no fouler nor more evil category of spells than the necromantic spells of Death Magic. These spells are relatively hard to learn, but at higher levels the spells give the caster power over living and the (un)dead. Poison, vampirism, death spells and even hellfire can be directed by the caster, but the most powerful spells need his / her own blood as the focus, often hurting the caster in the process of casting. Should a Death wizard find the legendary tome Necronomicon, he can expect to gain very great powers indeed, but at a cost: few that have studied the accursed tome have retained their sanity. ***** === On Casting Spells ... === Players who select spellcasting characters may notice a few unusual phenomena during the course of the game. Here's a few helps and hints on what may be happening. --- Armor and Spell Casting --- All spellcasting classes are penalized for wielding armor above a certain total combined weight. The threshold varies between 30lbs for a mage and 40lbs for a paladin. Typically, the more reliant on spells/prayers your class is deemed to be, the lower your weight allowance. If you exceed your weight limit, your mana will drop. This penalty is fairly stiff for low level characters but can generally be ignored by high level ones. --- Mage-Types and Gloves --- Spellcasters who use INT as the stat determining their spellcasting ability will be penalized heavily for wielding armor on their hands. The exception to this is that wielding gloves, gauntlets or cesti which boost DEX or which grant Free Action will result in no penalty. The rationale behind this is that the hands are needed to perform intricate gestures that accompany the casting of the spell and anything encumbering the hands would interfere with this. The priest-type classes (priest, paladin and monk) do not have the same restrictions as they are viewed as invoking the power of their deity directly through prayer. --- Casting Spells With Insufficient Mana --- Occasionally, you may try (either by accident or in desperation) to cast a spell when you have insufficient mana to do so. In these circumstances you will be told by the game that you do not have enough mana to cast the spell and then asked if you want to anyway. Casting the spell under these conditions has a drastically reduced chance of success and may also result in your constitution being damaged. It may also cause you to faint from the effort which will effectively paralyze you for several turns (even with free action). The CON drain and the paralyzation may happen regardless of whether the casting was successful or not. --- Spell Durations --- Some spells such as Haste-Self or Resistance grant an effect which wears off after a certain period of time. With very few exceptions, multiple castings of such spells are not cumulative in terms of the duration of the spell. In other words, casting a spell which has a duration of 20 turns three times will not result in a duration of 60 turns. Typically, the subsequent castings will add only a small amount to the total duration. ***** === Spell Types === --- Bolts and Beams --- A bolt spell is aimed in a direction or at a target but will hit the first monster or obstruction in its targeted direction. This may or may not be the intended target. Occasionally, you may note that your bolt spell hits multiple targets along the line of fire. This is because some bolt spells are granted a chance of 'beaming' which may be either fixed or level-dependent. A beam will hit every target within range along a 'straight' line in the direction in which it was aimed. --- Balls --- There are two things to remember about ball spells. Firstly, unlike bolt spells, they can be aimed at a specific target which allows you to target a specific monster as opposed to the first one in the direction you are aiming. Secondly, they have a radius (which varies from spell to spell). A radius value of one or more will result in the spell affecting monsters (and possibly objects, etc) around the target in addition to the target itself. --- Line-of-Sight --- Line-of-sight spells affect all monsters that that are currently in sight of your character. This includes monsters that may not currently be visible due to darkness but would be if it was light. --- Area --- Area spells affect an area around the player. The size of the area can vary considerably. For example, Light Area lights a single room while Detect Traps affects a map panel and Genocide the entire level. ***** === Hints and Tips === If you miss the `old` magic user, try picking Sorcery and Chaos magic to get the most commonly used mage spells early on (Magic Missile, Detect Monsters + Traps + etc, Identify). It is generally a good idea to pick one defensive realm and one offensive realm. For example try using life or sorcery with chaos or death magic. Nature is somewhat neutral: it has both offensive and defensive spells, but is not very generous with either, not at least early in the game. Nature should work best with characters who can use other means to survive until they get the more powerful high level spells. If you pick the realms always in the same order (e.g. nature as your first realm and chaos as your second realm, not the other way around) you will be less confused when trying to pick the correct spellbook to use in the game. If you still get confused trying to select the correct spellbook, try using macros (either the 'full' macros or inscriptions). ***** === Spell Lists === LIFE: Standard Spellbooks Book of Common Prayer 1. Detect Evil 2. Cure Light Wounds 3. Bless 4. Remove Fear 5. Call Light 6. Detect Traps and Secret Doors 7. Cure Medium Wounds 8. Satisfy Hunger High Mass 1. Remove Curse 2. Cure Poison 3. Cure Critical Wounds 4. Sense Unseen 5. Holy Orb 6. Protection from Evil 7. Healing 8. Glyph of Warding LIFE: Rare Spellbooks Book of the Unicorn This book has powerful prayers to ward off, banish and destroy the forces of evil. Blessings of the Grail This book has the most powerful prayers of protection and healing, as well as prayers of holy visions. SORCERY: Standard Spellbooks Beginner's Handbook 1. Detect Monsters 2. Phase Door 3. Detect Doors and Traps 4. Light Area 5. Confuse Monster 6. Teleport 7. Sleep Monster 8. Recharging Master Sorcerer's Handbook 1. Magic Mapping 2. Identify 3. Slow Monster 4. Mass Sleep 5. Teleport Away 6. Haste Self 7. Detection True 8. Identify True SORCERY: Rare Spellbooks Pattern Sorcery More powerful spells of detection, information and transportation. Grimoire of Power More powerful enchantments against monsters, spells to enchant items, and the Globe of Invulnerability. ARCANE: All Spellbooks Cantrips for Beginners 1. Zap 2. Wizard Lock 3. Detect Invisibility 4. Detect Monsters 5. Blink 6. Light Area 7. Trap & Door Destruction 8. Cure Light Wounds Minor Arcana 1. Detect Doors & Traps 2. Phlogiston 3. Detect Treasure 4. Detect Enchantment 5. Detect Objects 6. Cure Poison 7. Resist Cold 8. Resist Fire Major Arcana 1. Resist Lightning 2. Resist Acid 3. Cure Medium Wounds 4. Teleport 5. Stone to Mud 6. Ray of Light 7. Satisfy Hunger 8. See Invisible Manual of Mastery 1. Recharging 2. Teleport Level 3. Identify 4. Teleport Away 5. Elemental Ball 6. Detection 7. Word of Recall 8. Clairvoyance TRUMP: Standard Spellbooks Conjurings & Tricks 1. Phase Door 2. Mind Blast 3. Shuffle 4. Reset Recall 5. Teleport 6. Dimension Door 7. Trump Spying 8. Teleport Away Deck of Many Things 1. Trump Reach 2. Trump Animal 3. Phantasmal Servant 4. Trump Monster 5. Conjure Elemental 6. Teleport Level 7. Word of Recall 8. Banish TRUMP: Rare Spellbooks Trumps of Doom This tome tells you the secrets of the Living Trump treatment, as well how to deal the Joker card and how to deal death. It also has rather powerful summoning Trumps which may yet prove your own Doom... Five Aces A superb collection of the most classic summoning Trumps ever crafted. NATURE: Standard Spellbooks Call of the Wild 1. Detect Creatures 2. First Aid 3. Detect Doors and Traps 4. Foraging 5. Daylight 6. Animal Taming 7. Resist Environment 8. Cure Wounds & Poison Nature Mastery 1. Stone to Mud 2. Lightning Bolt 3. Nature Awareness 4. Frost Bolt 5. Ray of Sunlight 6. Entangle 7. Summon Animal 8. Herbal Healing NATURE: Rare Spellbooks Nature's Gifts Nature's Gifts for protection against the forces of nature and hostiles. Nature's Wrath Nature's destructive force harnessed for your use against your enemies. CHAOS: Standard Spellbooks Sign of Chaos 1. Magic Missile 2. Trap / Door Destruction 3. Flash of Light 4. Touch of Confusion 5. Mana Burst 6. Fire Bolt 7. Fist of Force 8. Teleport Self Chaos Mastery 1. Wonder 2. Chaos Bolt 3. Sonic Boom 4. Doom Bolt 5. Fire Ball 6. Teleport Other 7. Word of Destruction 8. Invoke Logrus CHAOS: Rare Spellbooks Chaos Channels Unusual spells that allow you to call on the forces of chaos to induce changes in your possessions, in your enemies and in yourself. Armageddon Tome The rarest of all spellbooks, filled with the most devastating spells. DEATH: Standard Spellbooks Black Prayers 1. Detect Unlife 2. Malediction 3. Detect Evil 4. Stinking Cloud 5. Black Sleep 6. Resist Poison 7. Horrify 8. Enslave Undead Black Mass 1. Orb of Entropy 2. Nether Bolt 3. Terror 4. Vampiric Drain 5. Poison Branding 6. Dispel Good 7. Genocide 8. Restore Life DEATH: Rare Spellbooks Black Channels Spells that turn you into a bloodthirsty killing machine, and which enable you to call upon the nether forces of darkness to wreak havoc upon your foes. Necronomicon The legendary tome of unholy visions, death and destruction. -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: May 16, 2000 zangband/lib/help/monster.hlp0000644000000000000000000000155210250356274015241 0ustar rootrootZangband Monsters. Please choose one of the following online help files: (0) Zangband Monsters (monster.txt) (1) Monster Symbols (monster.txt#MonsterSymbols) (2) Monster Color (monster.txt#MonsterColors) (3) Monster Memory (monster.txt#MonsterMemory) (4) Unique Monsters (monster.txt#Uniques) (5) Eldritch Horrors (monster.txt#EldritchHorror) (6) Pets (monster.txt#Pets) (7) Friendly Monsters (monster.txt#Friendly) (?) Help System Commands (helpinfo.txt) ***** [0] monster.txt ***** [1] monster.txt#MonsterSymbols ***** [2] monster.txt#MonsterColors ***** [3] monster.txt#MonsterMemory ***** [4] monster.txt#Uniques ***** [5] monster.txt#EldritchHorror ***** [6] monster.txt#Pets ***** [7] monster.txt#Friendly zangband/lib/help/monster.txt0000644000000000000000000002464110250356274015301 0ustar rootroot=== Zangband Monsters === There are hundreds of different creatures in Zangband each of which poses a unique challenge to the adventurer. As you progress deeper into the dungeon, the creatures will become progressively more difficult to defeat. ***** === Monster Symbols === Many monsters have the same letter symbol and sometimes color on the screen. The exact species or type of creature can be discovered by 'l'ooking at it (see commdesc.txt#Looking [1]). a Giant Ant A Angelic being b Giant Bat B Bird c Giant Centipede C Canine d Dragon D Ancient Dragon e Floating Eye E Elemental f Feline F Dragon Fly g Golem G Ghost h Humanoids H Hybrid i Icky-Thing I Insect j Jelly J Snake k Kobold K Killer Beetle l Aquatic monster L Lich m Mold M Multi-Headed Hydra n Naga N (unused) o Orc O Ogre p Human P Giant Human(oid) q Quadruped Q Quylthulg r Rodent R Reptile/Amphibian s Skeleton S Spider/Scorpion/Tick t Townsperson T Troll u Minor demon U Major demon v Vortex V Vampire w Worm or Worm Mass W Wight/Wraith x (unused) X Xorn/Xaren y Yeek Y Yeti z Zombie/Mummy Z Zephyr Hound $$ Creeping Coins , Mushroom Patch % Ent, Huorn # Fumes Note that some monsters disguise themselves by assuming the shape (ie. symbol) of common objects found in the dungeon. These include the |, ), ?, !, &, $$ and * symbols. ***** === Monster Colors === You can often determine some information about a monster based solely on the color it is described as having, like 'a White Dragon'. In general, 'White' monsters are cold-based, 'Red' are fire-based, 'Blue' are electrical, 'Black' are acidic, and 'Green' are poisonous and a 'Multi-Hued' monster is all of these. By being cold-based, we mean that the monster has either melee, magical or breath attacks that are cold based. Unfortunately this rule is applied somewhat inconsistently and there are several notable exceptions so don't be surprised. If that 'Green' monster spits acid at you. Note that this rule cannot be applied to the actual color a creature is displayed as but only to a color in its name. Other color and attack-type relationships are 'Bronze' and confusion and 'Gold' and sound. Over time the inconsistencies referred to above will probably get cleaned up. ***** === Monster Memories === Because there are so many monsters and because often different monsters share similar colors and symbols, it is very difficult to keep track of the capabilities of various creatures. Luckily, Zangband automatically keeps track of your experiences with a particular creature. This feature is called the monster memory. Your monster memory recalls the particular attacks of each creature (whether or not technically a monster) which you have suffered, as well as recalling if you have observed them to multiply or move erratically, or drop treasure, etc. Similary if you attack a creature with a magical attack and it resists, that fact is noted. If you have killed enough of a particular creature, or suffered enough attacks, recalling the monster memory may also provide you with information not otherwise available, such as a armor class or hit dice. These are not explained, but may be useful to give the relative danger of each creature. Your monster memory may be accessed by pressing '/' and then entering the symbol you wish to research. Each monster with that symbol of which you have some knowledge will displayed in turn beginning with the deepest. Alternatively, you may enter targetting mode by pressing '*' and, with the curser over the creature in question, press 'r' to recall details. The best feature about your monster memory is that it can be passed on to a new character even after you die by means of a reduced save file. Simply use your old savefile as the base for creating your new character. ***** === Unique Monsters === The monsters in Zangband are unique in that each variety of monster has its own set of physical and, where applicable, magical abilities. However, some monsters in Zangband are truly unique and this group of monsters are often referred to as "Uniques". The Unique monsters are made unique for several reasons. Primarily, their name derives from the fact that unlike regular monsters which can appear numerous times during the course of a game, the Unique monsters will never be regenerated once they have been killed and the game will never generate two copies of the same Unique monster at a time so that effectively there is only one of each Unique monster. Unique monsters are typically based on a normal monster. For example, Tom the Stone Troll is based on the Stone Troll monster. However, Unique monsters usually have more hitpoints, hit harder and cast spells more effectively than the monster on which they are based. Unique monsters are often a different color from other monsters of their type to allow the player to more easily distinguish them but this is not always true. --- Uniques' Resistance to Magic --- Unique monsters tend to be more resistant to magic than the other dungeon inhabitants. Thus, slowing, confusing, sleeping and scaring will be less effective and may not work at all. In addition, Genocide and Mass-Genocide spells will never work on Uniques and *Destruction* will teleoprt them from a level but will not kill them. --- Keeping Track of Uniques --- Since Unique monsters are often very difficult to defeat, most players will attempt to kill them all before attempting to battle the Serpent of Chaos (itself a Unique monster). This avoids the need to battle two very difficult oponents at the same time. Each time your character happens upon a new Unique, this is recorded in your monster memory. Under the knowledge command ('~') is a menu item which allows you to look at which Uniques you have met and which of these have been defeated. It is a good idea to check this periodically to gauge your progress. --- Speaking Uniques --- Occasionally, you will find that a Unique monster will speak to you as it approaches. Typically, a monster will shout obscenities and challenges to combat but they may also refer to events relating to themselves. Zangband has no mechanism to allow you to respond to the monsters (other than with violence). You may prevent Unique monsters from speaking by changing the approopriate option under the options menus. --- Rewards for Killing Uniques --- You may also find that occasionally after killing a Unique monster that the game will tell you that the Unique was wanted for certain crimes and you will be rewarded for killing it. The amount of the reward is dependent upon how deep in the dungeon the relevant Unique is normally found. ***** === Eldritch Horrors === Some of the monsters in the dungeons of Zangband are so fearsome to look upon that doing so can have serious repurcussions. When you meet one of these sanity blasting monsters, one of several nasty things may happen to you. These include having your intelligence and/or your wisdom temporarily reduced, becoming scared or confused, or suffering an attack of amnesia. ***** === Pets === A pet can be a mixed blessing. At first sight, one might think that they can make the game much easier. It is of course highly satisfying to send your pet Hell Wyrm into a Troll pit and wait outside listening for the howls of agony and terror, the sounds of guts splattering, bones crunching and so on. But one should not expect to gain any experience for such a combat: you will only gain experience for creatures to whom you deliver the death blow yourself. Secondly, your pets are often so eager to destroy your opponents that they may forget all about you, and you get trampled under their feet as they charge at your foe. Pets are also rather easily irritable and once you do something which causes the slightest discomfort to them, they will revert to their normal behavior and consider you their main target. This is something to think about before lighting up a room if you have pet orcs. Needless to say, nobody wants to be your friend if you are aggravating them. Finally, it takes a lot of mental energy to maintain the control over the charmed monsters. The first monster or the first few are 'free', but after that maintaining the control will start taxing your mana regain rate. The higher the sum of the levels of your pets the less mana you will be able to regain. Keep this in mind if you have a pet which can summon or otherwise produce more pets... --- Obtaining a pet --- You may obtain pets in several different ways. Some magic realms offer the ability to summon pets magically or to charm the creatures you meet. Mindcrafters may 'dominate' their oponents. Chaos patrons may grant pets as a gift to their devotees. Magical figurines can be thrown to create a pet and wands of charm monster may be used as their name suggests. --- Commanding Your Pets --- Your pets are fairly well trained and will respond to a variety of commands such as following you closely, staying nearby and searching out your enemies. You can command your pets using the pet command menu which can be found be pressing 'p'. ***** === Friendly Monsters === You will occasionally find a monster in the dungeon that isn't out to get you. Such monsters are described in game messages as being 'friendly'. A friendly monster will not attempt to hurt you but neither will it necessarily attack your enemies. Like pets, if you annoy a friendly monster it will turn against you. Also like pets, any monsters a friendly monster summons will also be friendly. Unlike pets, friendly monsters require no mana upkeep and can not be commanded using the pet commands. -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: May 25, 2001 ***** Begin Hyperlinks ***** [1] commdesc.txt#Looking zangband/lib/help/objects.hlp0000644000000000000000000000323510250356274015203 0ustar rootrootZangband Objects. Please choose one of the following online help files: (0) Zangband Objects (objects.txt) (1) Object Symbols (objects.txt#ObjectSymbols) (2) Equipment and Inventory (objects.txt#EquipInvent) (3) Object Stacking (objects.txt#Stacking) (4) Objects Generation (objects.txt#Generation) (5) Object Compaction (objects.txt#Compaction) (6) Cursed Objects (objects.txt#CursedObjects) (7) The Ancient and Foul Curse (objects.txt#AncientCurse) (8) Object Flavors (objects.txt#ObjectFlavors) (9) Pseudo-ID (objects.txt#PseudoID) (a) Identifying Objects (objects.txt#Identifying) (b) Ego Objects (objects.txt#EgoObjects) (c) Artifacts (objects.txt#Artifacts) (d) Random Abilities of Objects (objects.txt#RandomAbilities) (e) Object Inscriptions (objects.txt#Inscriptions) (f) Object Types (objects.txt#ObjectTypes) (?) Help System Commands (helpinfo.txt) ***** [0] objects.txt ***** [1] objects.txt#ObjectSymbols ***** [2] objects.txt#EquipInvent ***** [3] objects.txt#Stacking ***** [4] objects.txt#Generation ***** [5] objects.txt#Compaction ***** [6] objects.txt#CursedObjects ***** [7] objects.txt#AncientCurse ***** [8] objects.txt#ObjectFlavors ***** [9] objects.txt#PseudoID ***** [a] objects.txt#Identifying ***** [b] objects.txt#EgoObjects ***** [c] objects.txt#Artifacts ***** [d] objects.txt#RandomAbilities ***** [e] objects.txt#Inscriptions ***** [f] objects.txt#ObjectTypes zangband/lib/help/objects.txt0000644000000000000000000010223210250356274015234 0ustar rootroot=== Zangband Objects === The Zangband dungeons are full of objects and many monsters you meet will be carrying one or more items about their person which you may retrieve from their corpse should you be skillful enough to defeat them. You may pick up objects by moving on top of them. By setting various options (see option.txt#UserInterface [1]) you may choose to pick up items automatically or to be prompted before picking up objects. Many objects found within the dungeon have special commands for their use. Wands must be 'a'imed, staves must be 'u'sed, scrolls must be 'r'ead, and potions must be 'q'uaffed. You may, in general, not only use items in your pack, but also items on the ground, if you are standing on top of them. ***** === Object Symbols === ! A potion (or flask) / A pole-arm ? A scroll (or book) | An edged weapon , A mushroom (or food) \ A hafted weapon - A wand or rod } A sling, bow, or x-bow _ A staff { A shot, arrow, or bolt = A ring ( Soft armor " An amulet [ Hard armor $$ Gold or gems ] Misc. armor ~ Lites, Tools, Chests, etc ) A shield ~ Junk, Sticks, Skeletons, etc & Chests ` Statues or Figurines ***** === Equipment and Inventory === You can carry up to 23 different objects in your backpack while wearing and wielding up to 12 others. Note that the additional powers and abilities some objects can grant the player are only effective when they are wielded. A Shield of Resist Acid, for example, will not protect you from the effects of acid while it is in your backpack. items you wear and wield are referred to as your 'equipment' while items contained in your backpack are referred to as your 'inventory'. You may view a list of your current inventory and equipment at any time by using the 'i' and 'e' commands respectively. Although you are limited to 23 different items in your inventory, each item may actually be a 'pile' of up to 99 similar items. This allows you to carry multiple spell books, rations of food and other essential items while not using up all your inventory slots. If you somehow manage to stuff 24 items into your pack, for example, by removing an item from your head while your pack is full, then your pack will 'overflow' and the most recently added item will fall out and onto the ground. You will be warned about any command that seems likely to induce this behavior. In addition to the number of different items you may carry, you are also limited in the total amount of weight that you can carry. As you approach this value, you become slower, making it easier for monsters to chase you. Note that there is no upper bound on how much you can carry, if you do not mind being slow. The amount you may carry before becoming slowed is determined by your strength. For this purpose, the weight of both your equipment and your inventory is taken into consideration. Because of these space and weight constraints, it is important that a player learns to manage his inventory efficiently. You must juggle carefully the need for carrying essential items such as food, fuel for your light source, spellbooks (if applicable), healing items, escaping items, and the 101 other things necessary for your survival while at the same time maintaining space to carry back treasure from the dungeon for sale in the town and ensuring that you do not exceed your weight limit. ***** === Object Stacking === Normally, only one object can occupy any given floor space, which may or may not also contain one creature. Zangband's Stacking Options (see option.txt#Stacking [2]) provide a means for allowing multiple objects to share the same floor space by creating 'stacks'. Any object on the floor, including those forming part of a stack, may actually be a 'pile' of up to 99 identical items. Objects may not be created or placed on spaces where doors, rubble, traps, and staircases already exist. ***** === Object Generation === Objects are generated by Zangband when you enter a dungeon level and spread liberally around the dungeon floor. They may also be generated when a monster is killed (assuming the monster is capable of carrying objects). Each object has a 'native' depth or a depth at which it begins to be commonly found in the dungeon. It is unusual, though not impossible, to find objects significantly shallower in the dungeon than their native depths. Note that although items generated on the floor may be in 'piles', they will never be in 'stacks'. Which items are generated at the creation of the level or by the death of a monster is determined by a number of factors including the current dungeon level, the monster's native level and the native depth of the object being considered for generation. The quality of the object, in the case of monster drops, is also affected by the guaranteed quality level (if any) of that monster's drop. Monster drops are either normal, 'good' or 'great'. While these terms generally refer to equipment such as weapons and armor (for example, a 'good' drop means that any equipment generated is guaranteed to have magical pluses and a 'great' drop means that any equipment generated is guaranteed to be either an ego item or an artifact), they also encompass certain other objects. For example, Amulets of the Magi, the dungeon-only spellbooks and Rings of Speed are all considered as being 'great' objects and may therefore be dropped by a monster with a 'great' drop. Note that some monsters may drop treasure rather than objects. ***** === Object Compaction === Zangband can track a large, but finite, number of objects at any one time. If, as you kill monsters, the number of objects dropped together with the number of objects already lying on the floor exceeds this limit you will be told that the game is 'compacting objects'. When Zangband compacts objects it first attempts to combine similar objects into piles and, if this does not create sufficient space, will delete already existing items to create space for new ones. To the extent possible the compacting is performed on objects some distance away from the players current position. During compaction, items are removed in order of increasing quality with the least useful items removed first. Ego items are rarely removed from the level during the compaction process while artifacts (see below [3]) are never removed. ***** === Cursed Objects === A goodly percentage of the items generated in the dungeon or dropped by monsters will be cursed. Most cursed items will have penalties to various things such as your skill and deadliness or your armor class. They may also have other undesirable attributes. Shopkeepers will refuse to buy cursed items. Should you wield a cursed item, you will be prompted by the message "Oops, it feels deathly cold!" and the item will be automatically inscribed as being {cursed}. Once wielded, a cursed item may not be taken off until it has been uncursed. Uncursing an object will allow it to be removed but will not remove the negative effects such as the penalties mentioned above. There are several ways to uncurse objects. The Life realm contains two spells (one stronger than the other) for the purpose. There are also scrolls that you may find or purchase from the Temple which are similar to these prayers in their effect. It is also possible that in the course of enchanting an item you may lift a curse as a side-effect the enchanting process although this is not guaranteed to happen. Note: that items which can be activated (such as Dragon Scale Mail or some artifacts) become more difficult to activate when they are cursed. --- The Curses --- There are several different types of curse and more than one may be present on a single object although this is rare. Light Curse A lightly cursed item will typically have penalties to armor class, skill and deadliness, and various other things as appropriate. It may be uncursed reading a Scroll of Remove Curse or by the weaker Life realm prayer. Heavy Curse A heavily cursed item will typically have penalties to armor class, skill and deadliness, and various other things as appropriate. It may be uncursed reading a Scroll of *Remove Curse*. or by the stronger Life realm prayer. Permanent Curse Items that are permanently cursed may not be uncursed and so may not be removed once worn. You may occasionally find objects that are permanently cursed and which you are tempted to wield. Think carefully before you do so because you probably won't get a chance to change your mind. ***** === The Ancient and Foul Curse === The Ancient and Foul Curse is sometimes referred to incorrectly as 'The Curse of Topi Ylinen' after the person who first created it. Unlike the other curses discussed above, an object with the Ancient and Foul Curse can be removed at any time and as a result you will not get the "Oops, it feels deathly cold!" prompt when wielding it (unless, of course, it also has one of the other curses in which case both these behaviors will be present as a consequence of the secondary curse(s)). While the item is wielded, the curse will be randomly invoked every so often. When invoked, the curse may have one or more of several possible effects in various combinations. Fortunately, some of the worst of such combinations such as being paralyzed while Cyberdemons are summoned around you are prevented from happening. Some possible effects of the curse include: summoning monsters, summoning Cyberdemons, paralyzation (even with Free Action), teleporting the player, removing the walls around the player, amnesia, decreasing one or more primary stats both temporarily and permanently among others. Note: Although the Ancient and Foul Curse is primarily found on objects, there are a number of other occasions where you might fall foul of it. These include as side effects of miss-cast spells, setting off certain traps, as a 'reward' from a chaos-warrior's patron and as the dying curse of an Amberite. ***** === Object Flavors === Some objects (scrolls, potions, wands, rods and staves) are 'flavored items'. This means that with each game they are given a random flavor. For example, the first time you find a Potion of Heroism it might be described as a 'Dark Blue Potion'. Once you have identified it, the potion will be described as a 'Dark Blue Potion of Heroism'. Flavors vary from game to game so your next character might discover that that same Dark Blue Potion is now something entirely different. This means that each new character must set about learning exactly what each flavored items is. One of the Zangband Options allows the player to switch to 'plain' object descriptions which removes the flavored description from known flavored items so that the potion above, once identified, would be described simply as a 'Potion of Heroism'. ***** === Pseudo-ID === Occasionally, as you wander around the dungeon you will sense something about the quality of an item or items you are wielding or carrying in your backpack. This sensing ability is called 'pseudo-id' and is limited to weapons and armor. The speed and accuracy of your pseudo-id ability depends on a number of factors but the primary considerations are your class and level. Pseudo-id can be 'strong' or 'weak' and 'slow', 'medium' or 'fast'. Table 1 - Pseudo-Id ability of the Classes Class Method Speed ---------------------------------------- Warrior Strong Very Fast Mage/High-Mage Weak Slow Priest Weak Very Fast Rogue Strong Fast Ranger Strong Medium Paladin Strong Medium Warrior-Mage Weak Medium Mindcrafter Weak Medium Chaos Warrior Strong Medium Monk Weak Fast When you pseudo-id an item, it will be inscribed (see below [4]) with the quality level you have determined it to be of. This inscription will remain until you identify it (see below [5]) at which time it will be removed. The various inscriptions and their meanings are as follows: Table 2 - Strong Pseudo-Id Inscription Meaning ------------------------------------------------- terrible a cursed artifact worthless a cursed ego item tainted a cursed, "excellent" item cursed a cursed item dubious a cursed, enchanted item bad an item with negative plusses special a non-cursed artifact excellent a good ego item good an enchanted item average an average item Table 3 - Weak Pseudo-Id Inscription Meaning ------------------------------------------------ cursed a cursed item (includes bad ego items and artifacts) good an enchanted item (includes good ego items and artifacts) ***** === Identifying Objects === Although each player may, to a lessor or greater extent, rely on their pseudo-id ability to ascertain the quality of some items. For the most part you will need to identify them before you can ascertain how much use they will be to you. There are several methods of identifying items available to you. These include Scrolls of Identify and *Identify*, Staves and Rods of Perception, spells and prayers and services from town buildings among others. There is an important difference between 'identify'ing an object and '*identify*'ing an object. 'Identify'ing an object will reveal its correct description (including ego type where applicable), any plusses to combat or armor class and its bonus to stats and abilities (its 'pval') if it has one. '*Identify*'ing an item will reveal all information about an object including its additional fixed and random abilities if any. Once *Identified*, you may review the full abilities of an object by using the 'I'nspect command. Another means of identifying flavored items it to sell them to a shop. Having purchased the item, the shopkeeper will inform you what it is that you have sold. This can be a useful technique in the early game but exposes you to the risk of selling an unidentified item that had been generated out of depth and which you might have preferred to keep for yourself. Note: you will automatically have full knowledge about any item that you purchase in the town. ***** === Ego Objects === In addition to the ordinary weapons and armor your character may find in the dungeon, some of them may be endowed with additional powers. These objects fall into three types: (1) artifacts which are dealt with below and can be identified by their name; (2) Ego Weapons which are described more fully in Attacking Monsters (see attack.txt#EgoArtifact [6]); and (3) Ego Armors which are discussed more fully in Defending Yourself (see defend.txt#EgoArtifact [7]). Unlike artifacts which are unique and may only be found once in each game, it is not unusual to find several Ego Weapons or Ego Armor of the same type during the course of a character's adventures. Note that some Ego Armor types are limited to only certain types of armor. For example, you can find a Shield of Elvenkind but not Boots of Elvenkind. ***** === Artifacts === Of all the objects in Zangband that you might find, by far the most important group are artifacts. Artifacts are unique items with additional properties such as increasing or sustaining one or more of your stats and granting resistances or abilities. Often an artifact cab be 'A'ctivated for a magical effect. These can be Extremely useful - especially for classes that are weak in magic. There are two types of artifacts - fixed and random. The fixed artifacts each have entries in the lib/edit/a_info.txt file and consequently have a chance of appearing in each game you play. On the other hand, random artifacts are created by the game and have both random names and random properties. Once created, there is only a vanishingly small chance you will ever see one exactly the same again. --- (Non-) Preserve Mode --- One important thing to note when considering artifacts is your initial choice of (non-)preserve mode. in non-preserve mode, once a fixed artifact is generated (either on the dungeon floor or in a monster drop), it will never be generated again during that game. This means that if you miss the artifact by leaving the level before you pick it up it is gone forever. On the other hand, in preserve mode this behavior is only exhibited once you have identified the artifact. It is therefor safer to leave a level before it is completely explored as any fixed artifact can be regenerated in subsequent levels. --- Special feelings --- If you have stayed on the previous level long enough, on entering a new level you will receive a 'feeling' which reflects the quality of the monsters and objects on the level. Feelings are discussed in more detail in the Town and Dungeon section. One possible feeling is 'You have a special feeling about this level'. This message is only given when playing in non-preserve mode and is an indicator that you have either a monster pit, vault or artifact on the level. Artifacts always cause special feelings but pits and vaults are increasingly less likely to do so as you get deeper in the dungeon. At very deep levels, special feelings are almost always artifacts. Note: In non-preserve mode, you can still find artifacts on levels that did not give a special feeling but these will always be the result of a monster dropping them. --- Random Artifacts --- In addition to the standard artifacts which are potentially available in every game. Zangband will also occasionally generate a 'random' artifact. Any weapon or piece of armor as well Amulets of the Magi and Rings of Lordly Protection have a chance of becoming a random artifact. Note that random artifacts can be both good and bad and of greatly varying quality. Unlike standard artifacts, the in-game spoilers will not contain details of their special powers and so you must *identify* if you wish to ascertain this information. ***** === Random Abilities of Objects === In addition to the ordinary weapons, armor and other equipment your character may find in the dungeon, some such items may be endowed with additional powers such as sustains, resistances and abilities. Such items fall into three categories: (1) artifacts; (2) ego items; and (3) ordinary items which grant additional powers. In addition to their fixed powers, some of these objects may also be granted an additional power (or powers) in a random fashion. The random powers of objects may be either guaranteed (so that objects of that type will always have an additional random power) or may have a varying chance of being granted (so that only some of the objects of that type will have an additional random power). A few rare objects have the possibility of having multiple random powers. Random powers fall into three categories as follows: Extra Sustains -------------- Sustain Strength Sustain Intelligence Sustain Wisdom Sustain Dexterity Sustain Constitution Sustain Charisma Extra Resistances ----------------- Resist Blindness Resist Confusion Resist Sound Resist Shards Resist Nether Resist Nexus Resist Chaos Resist Disenchantment Resist Poison Resist Light Resist Dark Extra Powers ------------ Levitation Permanent Light See Invisible Telepathy Slow Digestion Regeneration Free Action Hold Life ***** === Object Inscriptions === You may "inscribe" any object with a textual inscription of your choice. These inscriptions are not limited in length, though you may not be able to see the whole inscription on the item. The inscription is displayed inside curly braces after the object description. The inscription is limited to the particular object (or pile) and is not automatically transferred to all similar objects. (You should review the effect of the two options for merging inscriptions to see how this can be done). The game provides some inscriptions automatically to help you keep track of your possessions. Wands and staves which are known to be empty will be inscribed with {empty}. Flavored objects which have been tried at least once but haven't been identified yet will be inscribed with {tried}. Known cursed objects are inscribed with {cursed} (or {uncursed} if they have been uncursed). Broken objects may be inscribed with {broken}. Also, any item which was purchased at a discount, implying that it is slightly "sub-standard", will be inscribed with the appropriate "discount", such as {25% off}. In addition, Zangband will occasionally place an inscription on an object for you, normally as the result of your character getting a "feeling" about the item indicating its quality. All characters will get "feelings" about weapons and armor after carrying them for a while. Automatically generated inscriptions fall into two categories - "real" and "fake". The difference between the two is that "fake" inscriptions can not be removed (although they can be covered up by another inscription). For example, the discount inscription is fake and can not be removed. --- Making use of Inscriptions --- Some players use the inscription feature to record useful and/or interesting facts about an object such as where and how it was acquired or what its abilities and powers are. However, in addition to this, inscriptions can be used in far more powerful ways. Many comands in Zangband require you to specify an object from your inventory such as which potion you wish to 'q'uaff or which staff you wish to 'u'se. Inscriptions offer a powerful way of managing this and provide a useful labor saving tool. The {@x#} Inscription --------------------- The {@x#} inscription (where 'x' is the key to activate a specfic command and '#' is a number from 0 to 9) is, perhaps the most commonly used inscription. When you select command 'x', followed by a number '#', the game searches for an object with the inscription {@x#} and performs that command upon it. Thus if the player inscribes a Potion of Healing as {@q1}, then pressing 'q1' will quaff it automatically. This feature is useful because as items are added or removed from your inventory, the actual slot that they are in changes. However, the inscription will work regardless of the position. Note that you can inscribe multiple different objects with the same inscription {@x#}. If you do so, the command 'x#' will act on the first appropriately inscribed object in your inventory list and not on each of them. Also note that you can place multiple such inscriptions on a single object. So that a spell book might be inscribed {@m1@b1@G1} since the cast spell, browse book, and gain spell commands each require a spell book reference as an argument. You can also inscribe the same command more than one so that food item might be inscribed {@E@1E2} Example 1: You have a primary weapon and a shovel. You normally use the weapon but want to use the shovel for mining treasure. Inscribe your primary weapon with {@w0} and your shovel with {@w1}. Then, when you want to wield your shovel, you can press 'w1' and to re-wield your sword you can press 'w0' regardless of where in the inventory they are located at the time. Note: you can also inscribe both weapons with {@w0} and just press 'w0' whenever you want to exchange weapon and shovel. Example 2: To cast the first spell from your first spell book, you would normally type 'maa' and to cast the fisrt spell from the second book, you would type 'mba'. However, if your first book is stolen or destroyed, pressing 'mba' would no longer cast the spell you wanted. This often leads to people people to cast spells incorrectly. Instead inscribe your first spell book with {@m1}, your second with {@m2}, etc. Cast spells by typing 'm1a', 'm2a', etc. Even if your other spell books are lost, you will never need to worry about casting the wrong spell. Example 3: You are carrying some Rations of Food and some Elvish Waybread. You wish to use up the Rations first before eating the Waybread, but also want to be able to use the healing/curing properties of the Waybread in an emergency. You would inscribe the Rations with {@E1} and the Waybread with {@E1@E2}. Since the Rations appear before the Waybread on the inventory listing, typing 'E1' will ensure you eat all the Rations first before touching the Waybread but that you only ever have to press E1 to eat. But, pressing 'E2' will bypass the Rations and jump to the Waybread. Some players also do similar things with cure and healing potions. The {@#} Inscription --------------------- The {@#} inscription (where '#' is a number from 0 to 9) is similar to the {@x#} inscription except that it works for any command. Thus, if you engrave both your digging instrument and your primary weapon with {@0}, pressing 'w0' will wield whichever one is not being currently wielded. However, if you had also inscribed a potion with {@0}, you would instead get an error message because the potion appears first in the inventory and you can not wield potions. The {!x} Inscription -------------------- The {!x} inscription (where 'x' is the key to activate a specfic command) will induce "verification" whenever that object is "selected". Thus, inscribing {!v!k!d} on an object will greatly reduce the odds of you "losing" it by accident. This is because any attempt to throw it ('f'), destroy it ('k') or drop it ('d') will prompt Zangband to verify whether or not you really wish to take that action. Note that inscribing {!*} on an object will prompt Zangband to verify any action thus you allowing you to be very paranoid about the object. Also note that "selling" and "dropping" both use the "d" command so that inscribing an item with {!d} will also prevent you from selling it by accident. The {^x} Inscription -------------------- The {^x} inscription (where 'x' is the key to activate a specific command) will induce verification before taking that action. One common example of its usage is to inscribe boots with {^<^>} which will verify the user's command to leave the level via the staircases before actually doing so. The {=g} Inscription -------------------- The {=g} inscription on an object prompts Zangband to automatically pick it up. This instruction overrides any auto-pickup options chosen chosen by the player (if necessary). The primary prupose is to allow easy retrieval of missiles such as arrows and bolts once they have been fired at a target. ***** === The Object Types === Ammunition ('{') Ammunition of various types and quality is a frequent find in the dungeon and is usually found in 'piles'. In order to get the full benefit from ammunition it must be 'f'ired from the appropriate missile launcher but me also be thrown. Slings fire pebbles and shots, bows fire arrows and crossbows fire bolts. Missile Launchers ('}') There are several types of missile launchers from slings to heavy crossbows. Each type of launcher has its own ammunition (see above). Missile launchers must be 'w'ielded before they can be used and will be placed in the 'b' equipment slot. Weapons ('|', '/', '\') There are numerous types of weapons in the dungeons. Each weapon varies in terms of its weight and based damage dice. Weapons must be 'w'ielded before they can be used and will be placed in the 'a' equipment slot. Armor ('[', '(', ')', ']') Armor may be worn on your body (the 'g' equipment slot), cloaks may worn about your body (the 'h' equipment slot), shields may be worn on your arm (the 'i' equipment slot), head gear (caps, helms and crowns) are worn on your head (the 'j' equipment slot), gloves and gauntlets and cesti are worn on your hands (the 'k' equipment slot) and boots are worn on your feet (the 'l' equipment slot). Armor of any kind must be 'w'ielded before it can be used. worn on your Amulets ('"') Amulets are flavored items and are worn around your neck (the 'e' equipment slot) and must be 'w'ielded before they can be used. Rings ('=') Rings are flavored items. You may wear one ring each of your left and your right hand (the 'c' and 'd' equipment slot) and must be 'w'ielded before they can be used. Scrolls ('?') Scrolls are flavored items which must be 'r'ead to benefit from their effect. Most scrolls serve utility purposes. Potions ('!') Potions are flavored items and must be 'q'uaffed before they have an effect on you. Some potions may be thrown at monsters and will have an effect on them when the potion shatters. Similarly, you may occasionally be carrying a potion which shatters during combat and thus spreading its effects on the monsters around you. Food (',') Various food items are scattered throughout the dungeons and must be 'E'aten for the player to gain nutritional value. If you do not eat regularly you will become hungry and then weak. Eventually, you will begin to faint from hunger and will finally die of starvation. Note: undead races gain little or no nutritional value from food and must therefore obtain nourishment in some other manner. Mushrooms (',') Mushrooms are a flavored food item that grant nutritional value and also have a magical effect on the player. As you might expect, not all of these are beneficial. They must be 'E'aten. Note: undead races will get little or no nutritional value from eating mushrooms but will experience any magical effects. Wands ('-') Wands are flavored, magical devices containing a finite number of 'charges'. Each charge represents a single use of the wand and using the wand reduces the number of charges available by one. Once empty, the wand must be 'recharged' before it can be used again. In general, wands will fire a bolt, beam or ball rather than have an area affect. Wands do not normally affect the player directly and are generally useful for attacking purposes. Wands must be 'a'imed and most wands will require a target. Staffs ('_') Staves are a flavored item and are made of various types of wood. Like wands, they contain a limited number of charges and must be recharged when empty. In general, staves have an area affect or act on the player. Unlike wands, few staves may be used directly for offense. Staves must be 'u'sed and do not require a target. Rods ('-') Rods are similar to both wands and staves in that many magical effects available from wands and staves are also available from rods. The main difference is that a rod carries a single charge but over time will recharge itself. Typically, the better the rod, the longer it will take to recharge itself. This means you have an unlimited supply of the magical effect but can only use it infrequently compared to a wand or staff which can be used each turn until it is empty. Rods must be 'z'apped and many rods will require a target. Spellbooks ('?') There are four spell books for each of the magic realms and which are found either in the shops or at varying depths in the dungeons. If you are are a spellcaster, you may 'b'rowse any book belonging to your chosen realm(s) to see what spells it contains. You may not browse books from realms other than those that your character has chosen. You may learn new spells with the 'g' command and cast them with the '*' command. Chests ('&') Chests are complex objects, containing traps, locks, and possibly treasure or other objects inside them once they are opened. Many of the commands that apply to traps or doors also apply to chests and, like traps and doors, these commands do not work if you are carrying the chest. Magical Figurines ('`') A figurine is a small. magical replica of a monster from the dungeon. When a figurine is thrown, a pet of the figurine's monster type will be generated. Statues (''') Statues made of various materials can be found throughout the dungeon. Unlike figurines, they have no magical attributes but may be worth selling depending upon the material from which they are made. Corpses ('~') You may sometimes find the corpses and skeletons of monsters and other adventurers that have died in the dungeons. Various Junk ('~') Like any dungeon, you should expect to find various junk items like broken sticks, empty bottles, etc. lying around. While most of this junk is useless, iron spikes can be used to 'j'am doors to prevent a monster from chasing you. Light Sources ('~') Various light sources and their fuel may also be found in the dungeon. You may refill your light source if you are carrying the appropriate items by using the 'F' command. Lanterns can be filled with flasks of oil and torches combined with other torches. -- Original : (??) Updated : (??) Updated : Zangband DevTeam Last update: May 25, 2001 ***** Begin Hyperlinks ***** [1] option.txt#UserInterface ***** [2] option.txt#Stacking ***** [3] objects.txt#Artifacts ***** [4] objects.txt#Inscriptions ***** [5] objects.txt#Identifying ***** [6] attack.txt#EgoArtifact ***** [7] defend.txt#EgoArtifact zangband/lib/help/option.hlp0000644000000000000000000000257210250356274015065 0ustar rootrootZangband Options. Please choose one of the following online help files: (0) Zangband Options (option.txt) (1) User Interface Options (option.txt#UserInterface) (2) Disturbance Options (option.txt#Disturbance) (3) Game Play Options (option.txt#GamePlay) (4) Efficiency Options (option.txt#Efficiency) (5) Display Options (option.txt#Display) (6) Birth Options (option.txt#StartUp) (7) Arificial Intelligence Options (option.txt#AI) (8) Testing Options (option.txt#Testing) (9) Base Delay Factor (option.txt#BaseDelay) (a) Hitpoint Warning (option.txt#Hitpoint) (b) Autosave Options (option.txt#Autosave) (c) Window Flags (option.txt#Window) (d) Cheating Options (option.txt#Cheating) (?) Help System Commands (helpinfo.txt) ***** [0] option.txt ***** [1] option.txt#UserInterface ***** [2] option.txt#Disturbance ***** [3] option.txt#GamePlay ***** [4] option.txt#Efficiency ***** [5] option.txt#Display ***** [6] option.txt#StartUp ***** [7] option.txt#AI ***** [8] option.txt#Testing ***** [9] option.txt#BaseDelay ***** [a] option.txt#HitPoint ***** [b] option.txt#Autosave ***** [c] option.txt#Window ***** [d] option.txt#Cheating zangband/lib/help/option.txt0000755000000000000000000010171510250356274015123 0ustar rootroot=== Zangband Options === Most of the "options" are accessible through the '=' command, which provides an interface to the various "sets" of options available to the player. In the descriptions below, each option is listed as the textual summary which is shown on the "options" screen, plus the internal name of the option in brackets, followed by a description of the option. Note that the internal name of the option can be used in user pref files to force the option to a given setting, see "pref.txt" for more info. Various concepts are mentioned in the descriptions below, including "disturb", (cancel any running, resting, or repeated commands, which are in progress), "flush" (forget any keypresses waiting in the keypress queue, including any macros in progress), "fresh" (dump any pending output to the screen). ***** === User Interface Options === ***** Rogue-like commands [rogue_like_commands] Selects the "roguelike" command set (see "command.txt" for info). ***** Activate quick messages [quick_messages] Allows the use of any keypress as a response to the "-more-" prompt (useful for monster farming). Allows most keys to mean "no" to any "[y/n]" prompt. ***** Prompt for various information [other_query_flag] Forces the game to ask you before taking various actions, such as using things which might cause your pack to overflow. Forces the game to ask you which hand to place rings on. ***** Prompt before picking things up [carry_query_flag] Forces the game to ask you if you want to pick something up when you do something that would normally cause the item to be picked up. ***** Use old target by default [use_old_target] Forces all commands which normally ask for a "direction" to use the current "target" if there is one. Use of this option can be dangerous if you target locations on the ground, unless you clear them when done. ***** Pick things up by default [always_pickup] Tells the game that walking onto an item should attempt to pick it up. Otherwise, you must use the "g" command, or the "-" command while walking. Combined with "carry_query_flag", allows you to selectively pick up all items which you step on. ***** Repeat obvious commands [always_repeat] Tells the game that when you attempt to "open" a door or chest, "tunnel" through walls, or "disarm" traps or chests, that you wish to "repeat" the command 99 times (see "command.txt"). ***** Merge inscriptions when stacking [stack_force_notes] Force otherwise identical objects to merge, even if one has an empty inscription and the other does not. The resulting stack keeps the non-empty inscription. ***** Merge discounts when stacking [stack_force_costs] Force otherwise identical objects to merge, even if they have different discounts. The resulting stack keeps the largest discount. This option may cause you to lose "value", but will give you optimal pack usage. ***** Reverse experience display [toggle_xp] If this option is on, the amount of experience you need to move up to the next level will be displayed on the main screen. If the option is off, it will show your total experience. ***** Allow weapons and armor to stack [stack_allow_items] Allow identical weapons and armor to be combined into a stack. This also allows unidentified, but identical, ammo to be combined, which may result in the auto-identification of some of the ammo, but which makes it a lot easier to actually use unidentified ammo. ***** Allow wands/staffs/rods to stack [stack_allow_wands] Allow identical wands/staffs/rods to be combined into a stack. This may force the items to be "unstacked" to use them, which may result in "overflow" of the stack. Also, the entire stack can be recharged (and possibly destroyed) at the same time. ***** Expand the power of the list commands [expand_list] Expand the "listing" commands so that they "wrap" at the "edges" of the appropriate list. This allows the "look" and "target" commands to "cycle" through all appropriate grids forever, and the "identify symbol" to browse through all of the monsters of a given type. ***** No query to destroy known worthless items [auto_destroy] It can sometimes be annoying that the Destroy command asks for confirmation when you are attempting to destroy a Broken sword {cursed}. If this option is set, no confirmation will be asked if you attempt to destroy an object which you know to be worthless. Of course, cursed artifacts cannot be destroyed even if this option is set. ***** Confirm to wear/wield known cursed items [confirm_wear] Some players may occasionally, due to a typing mistake, find themselves wearing an item which they knew was cursed. If this option is set, you should be safe from such typing mistakes: you will be prompted if you attempt to wear or wield an item if your character knows it is cursed. ***** Prompt before exiting a dungeon level [confirm_stairs] Some players (such as myself) often accidentally press the '<' key and exit a Special feeling level. If this option is set, the program asks for confirmation before you go up or down the stairs. Others may find the prompt annoying, they should of course not set this option. :-) ***** Automatically open doors [easy_open] Makes it easy for your character to open a door: simply by walking into it! Also the open command will automatically select one direction if only one door is near you. ***** Automatically disarm traps [easy_disarm] Makes it easy for your character disarm a trap: simply by walking into it! Also the disarm command will automatically select one direction if only one known trap is near you. ***** Display floor stacks in lists [easy_floor] Lets you select an item from a stack on the floor by browsing a list, Also floor stack are described as "You see a stack of n items.", when there is more than one item on a floor grid. ***** Allow unified use command [use_command] Unifies the item commands like "zap a rod", "use a staff", "eat food", "aim a wand", ... into a general "use object" command. The command in the original keymap is "u", and "a" in the roguelike mode. The standard commands for eat, quaff, read, zap, aim, ... are still available, but can be used for macros. ***** === Disturbance Options === ***** Audible bell (on errors, etc) [ring_bell] Attempt to make a "bell" noise when various "errors" occur. ***** Run past stairs [find_ignore_stairs] Ignore stairs when running. ***** Run through open doors [find_ignore_doors] Ignore open doors when running. ***** Run past known corners [find_cut] Cut sharply around "known" corners when running. This will result in "faster" running, but may cause you to run into a "lurking" monster. ***** Run into potential corners [find_examine] Fully explore "potential corners" in hallways. ***** Disturb whenever any monster moves [disturb_move] Disturb the player when any monster moves, appears, or disappears. This includes monsters which are only visible due to telepathy, so you should probably turn this option off if you want to "rest" near such monsters. ***** Disturb whenever viewable monster moves [disturb_near] Disturb the player when any viewable monster moves, whenever any monster becomes viewable for the first time, and also whenever any viewable monster becomes no longer viewable. This option ignores the existence of "telepathy" for the purpose of determining whether a monster is "viewable". See also the "view_reduce_view" option. ***** Disturb whenever map panel changes [disturb_panel] This option causes you to be disturbed by the screen "scrolling", as it does when you get close to the "edge" of the screen. ***** Disturb whenever player state changes [disturb_state] This option causes you to be disturbed whenever the player state changes, including changes in hunger, resistance, confusion, etc. ***** Disturb whenever boring things happen [disturb_minor] This option causes you to be disturbed by various bring things, including monsters bashing down doors, inventory feelings, and beginning to run out of fuel. ***** Disturb whenever random things happen [disturb_other] In Zangband, uncursed teleporting items may teleport you around sometimes, asking for your confirmation (and possibly disturbing your rest). If you unset this option, they will stop asking you and teleporting you randomly. Cursed items will neither ask for confirmation nor stop teleporting you even if this option is unset. ***** Disturb when you leave detection radius [disturb_traps] This option causes you to be disturbed when you move out of an area for which you have already cast "detect traps". ***** Automatically clear -more- prompts [auto_more] The game does not wait for a keypress when it comes to a -more- prompt, but carries on going. ***** Alert user to various failures [alert_failure] Produce a "bell" noise, and flush all pending input, when various "failures" occur, as described above. ***** === Game-play Options === ***** Get last words when the character dies [last_words] Display a random line from the "death.txt" file when your character dies. If this option is not selected, the "You die." message is displayed instead. ***** Allow shopkeepers and uniques to speak [speak_unique] If this option is in use, shopkeepers may sometimes whisper rumours to you. Also certain monsters start boasting as they attack you, and, when they die, they say their 'last words'. A speaking monster may also (if the option is selected) be wanted by the law, in which case you get the reward if you kill it. ***** Allow unusually small dungeon levels [small_levels] This option enables the creation of levels of varying sizes. Levels that are as small as the town level (i.e. 1 'screen') are possible, yet they can be dangerous, especially for a low level character. Note that this option has the side effect of enabling / disabling 'destroyed' levels (they are enabled if small levels are). ***** Allow empty 'arena' levels [empty_levels] Normal dungeon levels consist mostly of rock. If this option is in use, levels which have empty floor instead of solid rock may also be created (somewhat remniscent of Nethack's "big-room" levels). These levels can be extremely deadly, especially with breathing monsters (since there are few obstructions to shield). Arena levels may have vaults, nests and pits in them like normal levels. Some arena levels are dark when they are created, but most are lit. ***** Auto-scum for good levels [auto_scum] This is a hack but allows you to force the generation of "good" levels in the dungeon. This option may be extremely slow on some machines, especially deep in the dungeon. The minimum "goodness" of the level is based on the dungeon level, so the deeper you go, the better the level will be. A lot of people consider this option to be cheating. ***** Map remembers all perma-lit grids [view_perma_grids] Memorize all perma-lit floor grids which are seen by the player. This option allows you to keep track of which explored floor grids were perma-lit, but does not distinguish between dark floor grids, unexplored floor grids, and unknown grids. Turning off this option allows the player to always know which lit floor grids are in line of sight, but this is better accomplished by the "view_bright_lite" option. Note that any non-floor grids which is seen by the player are always memorized, and "object" which is seen by the player is memorized independantly from the memorization of the grid itself. ***** Map remembers all torch-lit grids [view_torch_grids] Memorize all (torch-lit) floor grids which are seen by the player. This option not only allows you to keep track of which floor grids have been explored, but also which ones are "dark", because the use of this option activates a special "color scheme" for the display of floor grids, in which "dark" grids are drawn in "dark gray", "lit" grids are drawn in "white", and (if the "view_bright_lite" option is set) "lit" grids which are also in line of sight are drawn in "orange". Note that grids which are currently "torch-lit" are considered to be "lit", and are thus drawn in "white", unless the "view_yellow_lite" option is set, in which case they are drawn in "yellow". ***** Generate dungeons with aligned rooms [dungeon_align] Force all rooms to be "aligned" with the "panel" divisions. This results in a much "prettier" dungeon, but may result in fewer greater vaults. ***** Generate dungeons with connected stairs [dungeon_stair] Always generate a staircase back to the level you came from, if you used a staircase to get to the level. This is more "realistic", and "safer", but less of a "challenge" for some people. ***** Allow notes to be appended to a file [take_notes] Allows you to use the ':' command to take notes. The notes will be appended to a text file baring the same name and in the same directory as your safevile but with the .txt extension. ***** Automatically note important events [auto_notes] With this option enabled the game will automatically create a file which logs important events such as gaining a level, finding an artifact or kiling a Unique. The automatic notes will be appended to a text file baring the same name and in the same directory as your safevile but with the .txt extension. ***** === Efficiency Options === ***** Reduce lite-radius when running [view_reduce_lite] Reduce the "radius" of the player's "lite" to that of a "torch" when the player is "running", which makes running more "efficient", but is extremely annoying. Certain older versions of Angband used this behavior always, so "purists" should turn it on. ***** Reduce view-radius in town [view_reduce_view] Reduce the "radius" of the player's "view" by half when the player is in town. This makes running faster in town, and also allows the player to ignore monsters in town which are more than ten grids away, which is usually safe, since none have distance attacks. ***** Avoid checking for user abort [avoid_abort] Avoid checking to see if the user has pressed a key during resting or running or repeated commands. This not only makes the game much more efficient (on many systems), but also allows the use of certain obscure macro sequences, such as turning this option on, resting until done, turning this option off, and casting a spell. Note that the use of this option may be dangerous on certain "graphic" machines. Resting for long periods of time with this option set is dangerous since the resting may not stop until the user takes damage from starvation. ***** Avoid processing special colors [avoid_other] Avoid processing the "multi-hued" or "clear" attributes of monsters. This will cause all "multi-hued" monsters to appear "violet" and all "clear" monsters to appear "white", and will cause "trappers" and "lurkers" to be visible on some machines, but it may greatly increase efficiency especially when telepathy is active. Certain systems may choose to set this option if they are unable to support the special "color" processing, but if they handle graphics "correctly", by using attr/char pairs with the "high bits" set, then not only will the game correctly avoid using any "dangerous" color processing, but it will allow such processing to occur when it is not dangerous. So if you are using graphics, and you use a "normal" attr/char for the "floor" grids, then you can use the "special lighting effects" for floors. ***** Flush input on various failures [flush_failure] This option forces the game to flush all pending input whenever various "failures" occur, such as failure to cast a spell, failure to use a wand, etc. This is very useful if you use macros which include "directional" components with commands that can fail, since it will prevent you from walking towards monsters when your spells fail. ***** Flush input whenever disturbed [flush_disturb] This option forces the game to flush all pending input whenever the character is "disturbed". This is useful if you use macros which take time, since it will prevent you from continuing your macro while being attacked by a monster. ***** Flush output before every command [fresh_before] This option forces the game to flush all output before every command. This will give you maximal information, but may slow down the game somewhat. Note that this option is only useful when using macros, resting, running, or repeating commands, since the output is always flushed when the game is waiting for a keypress from the user. ***** Flush output after every command [fresh_after] This option forces the game to flush all output after not only every player command, but also after every round of processing monsters and objects, which will give you maximal information, but may slow down the game a lot, especially on slower machines, and on faster machines you normally do not have a chance to see the results anyway. ***** Compress messages in savefiles [compress_savefile] Compress the savefile, by only saving the most recent "messages" that the player has received. This can cut the size of the savefile by a drastic amount, but will result in the loss of message information. ***** === Display Options === ***** Show dungeon level in feet [depth_in_feet] Display the dungeon depth in "feet" instead of as an actual level. This also affects the monster memory display. ***** Show labels in object lists [show_labels] Display the "labels" for objects in the "equipment" list, and in any "special" window which is displaying the "equipment". These labels indicate what the player is "using" the object for, such as "wielding" or "wearing" (in a given location). After you have played for a while, this information is no longer useful, and can be annoying. Note that in Zangband 2.1.0 and later this option no longer controls the "plain flavored object descriptions": a separate option for them has been added under "Zangband Options". ***** Show weights in object lists [show_weights] Display the weights of objects in the "inventory" and "equipment" lists, and in "stores", and in any "special" window which is displaying any of these lists. ***** Use color if possible (slow) [use_color] This option enables the software support for "color". Since this makes the game slower, you should always disable this option if you are using a machine which is not capable of using color. ***** Hilite the player with the cursor [hilite_player] Place the visible cursor on the player. This looks fine on some Unix machines, but horrible on most graphics machines. Note that only some machines are able to *not* show the cursor, but on those machines, hiding the cursor often speeds up the game and looks better. ***** Use special colors for torch-lit grids [view_yellow_lite] This option causes special colors to be used for "torch-lit" grids in certain situations (see the entries for"view_granite_lite" and "view_special_lite"). Turning this option off will slightly improve game speed. ***** Use special colors for 'viewable' grids [view_bright_lite] This option causes special colors to be used for non "viewable" grids in certain situations (see "view_granite_lite" and "view_special_lite"). When this option is set, floor grids which are normally drawn in "white" but which are not currently "viewable" by the player are instead drawn in "dark gray". This makes the "viewable" grids to appear "brighter" than the others, allowing the player to easily determine which floor grids are in "line of sight". Turning this option off will probably increase the speed of the game. ***** Use special colors for wall grids (slow) [view_granite_lite] This option activates a special color scheme for all "wall" grids which are normally drawn in "white" (as walls and rubble normally are). When the player is blind, we use "dark gray", else if the grid is torch-lit, we use "yellow" (or "white") depending on the "view_yellow_lite" option, else if the "view_bright_lite" option is set, and the grid is not in line of sight, or the grid is dark, or the grid is only "partially" lit, then we use "gray", otherwise we use the normal "white". Turning this option off will probably increase the speed of the game. ***** Use special colors for floor grids (slow) [view_special_lite] This option activates a special color scheme for all "floor" grids which are normally drawn in "white" (as they normally are). When the player is blind, we use "dark gray", else if the grid is torch-lit, we use "yellow" (or "white") depending on the "view_yellow_lite" option, else if the grid is "dark", we use "dark gray", else if the "view_bright_lite" option is set, and the grid is not in line of sight, we use "gray", otherwise we use the normal "white". Turning this option off will probably increase the speed of the game. ***** Plain object descriptions [plain_descriptions] In Zangband, this option disables "full" names for identified 'flavored' objects, in other words, if this option is not in use, an identified Potion of Speed could be listed (for example) as a Blue Potion of Speed. If you prefer simpler, less verbose descriptions, set this option. ***** Always center on the player [center_player] Scrolls the map with each move the player makes so as to keep the player at the center of the map. This reduces the chance of the player being hit by an offscreen breath. ***** Avoid centering while running [avoid_center] Turns off the player centering option when running. ***** Allow monsters to carry lights [monster_light] With this option on, monsters that should glow (eg. fire vortexes) and monsters that would normally require light to move around (eg. humanoids) will give off light. ***** === Birth Options === While cheating makes the game easier, the following options can make Zangband harder. So if you think the game is too easy, or if you want to impress your friends, then switch on the following options. The startup-options can only be accessed while creating a new character (press '=' while creating the character). There is no way to turn them off after the creation is finished! ***** Monsters behave stupidly [stupid_monsters] Zangband 2.1.0 incorporates Keldon Jones' improved monster Artificial Intelligence patch. While this patch most certainly makes monsters behave more realistically, they will also be more deadly with the improved AI. If you are a sissy, set this option to get the old, really stupid monster AI. Note that the new AI is a bit processing power expensive. If you have an old computer (386sx) and Zangband is running too slowly, you could try turning stupid_monsters on. Or buying that Pentium II so you can run Zangband. :-) ***** Use 'vanilla' town without quests and wilderness [vanilla_town] Uses the basic town known from the standard Angband and older versions of Zangband. This town is only one screen in size and contains only the 8 Zangband stores, your home, and the stairs to the dungeon. If you use the 'vanilla' town, then there is no wilderness, no special buildings and no set quests (but you can still use the random quests). This also speeds up the game on slower machines, since the wilderness doesn't need to be created. ***** Stores are permanently closed [ironman_shops] This option closes all shops. Try to survive in the deeps of the dungeon without supplies from town. ***** Always create unusually small dungeon levels [ironman_small_levels] If this option is enabled, then every level will be smaller than usual. See the 'Allow unusually small dungeon levels' option above. ***** Don't allow climbing upwards/recalling [ironman_downward] You are not allowed to climb upwards, or recall to town. All stairs are downstairs and every time you teleport level, you'll teleport to a deeper level. This option may be dangerous in combination with the 'small_levels' options, since the creation of quest-monsters may fail on very cramped levels, trapping you on the level with no stairs up or down. ***** Permanently enable the autoscummer [ironman_autoscum] This option switches the "autoscummer" permanently on, so that only "good" levels are created. This can mean, that there are good items on this level, or that the level is protected by especially powerful monsters. In Zangband it often is the later, so use this option with care. ***** Quest monsters get reinforcements [ironman_hard_quests] Using 'hard quests' mode makes the random quests harder, because you have to kill all monsters at the same visit to the quest level. If you leave the level while some quest monsters are still alive, then all killed quest monsters are revived on your next visit. ***** Always create empty 'arena' levels [ironman_empty_levels] If this option is enabled, then every level will be an arena level. See the 'Allow empty 'arena' levels' option above. ***** Create terrain 'streamers' in the dungeon [terrain_streams] Allows the creation of terrain streamers in the dungeon. This allows the creation of 'lakes' and 'rivers' of water, lava and trees. ***** The good old days ... [ironman_moria] This option induces Zangband to mimic certain aspects of the game of Moria. This includes the lack of colour, inability to gain the high resists, no targetting, and several other changes to game play. ***** Ask for saving death [munchkin_death] If your character dies and this option is enabled, you will be asked if you will be given the opportunity to avoid dying. Enabling this option will prevent your character's score from being entered into the high-score table and is considered cheating. ***** Always generate very unusual rooms [ironman_rooms] Forces the game to always generate intersting rooms such as pits, nests and vaults. You will consequently find lots of out-of-depth items and monsters. ***** Maximize stats [maximize_mode] The "maximize" flag, if set when the character was created, causes the "race" and "class" stat bonuses to be applied as "equipment" bonuses. This usually makes the character harder at the beginning of the game, but easier later on, since the stats are no longer limited to a "natural" value of "18/100". ***** Preserve artifacts [preserve_mode] The "preserve" flag, if set when the character was created, cancels all level feelings of the "special" variety, but allows "missed" artifacts to be "saved" by wandering monsters and found again at a later time. This only works for non-identified artifacts. Anything that would have caused a 'special' feeling contributes to the level feeling that your character receives instead. ***** Specify 'minimal' stats [autoroller] Allows you to make use of the autoroller. ***** Generate character using a point system [point_based] Allows you to specify your character's starting statistics using a point-based allocation system. You start with a certain number of points which may be allocted to your different statistics. As your stats increase, it takes more points to increase them further. If you do not use all your points, you will be rewarded with gold coins. ***** Allow silly monsters [silly_monsters] This option controls whether or not some of the 'joke' monsters will be generated. ***** Nightmare mode [ironman_nightmare] It isn't even remotely fair - don't say we didn't warn you! ***** === Artificial Intelligence Options === ***** Pack monsters use new AI [smart_packs] Allows monsters which normally appear in groups to behave "smarter". ***** === Testing Options === ***** Allow objects to stack on the floor [testing_stack] Allows a cave grid to hold more than one object (or one kind of object). ***** === Base Delay Factor === The "delay_factor" value, if non-zero, is used to "slow down" the game, which is useful to allow you to "observe" the temporal effects of bolt, beam, and ball attacks. The actual delay is equal to "delay_factor" cubed, in milliseconds. ***** === Hitpoint Warning === The "hitpoint_warn" value, if non-zero, is the percentage of maximal hitpoints at which the player is warned that he may die. It is also used as the cut-off for using red to display both hitpoints and mana. ***** === Autosave Options === Ideally, the game should be so stable that these options are not needed at all. However, even if the game were 100% reliable (which it, to be frank, probably is not), the user might forget to and his hardware could fail him. For all of these reasons, you may want to use these options: ***** Autosave when entering new levels [autosave_l] If this option is set, the program will attempt to save your character every time before creating a new dungeon level. Useful if you experience hangups in level generation (although these should have been eliminated in 2.1.0). ***** Timed autosave [autosave_t] If this option is set, the program will attempt to save your character every n game turns, where n is the "frequency". To set frequency press n: it will increase the frequency to the next category (and from 25000 to 0), the categories being every 50, 100, 250, 500, 1000, 2500, 5000, 10000 and 25000 turns. Note that the frequency must be higher than 0 and the "Timed autosave" set to "yes" for timed autosaves to take place. ***** === Window Flags === Selects what kind of information is displayed in which window. ***** === Cheating Options === Using the cheating options marks your character as "Cheater" and you won't get into the high-score list. Turning off the cheating options later does NOT allow your character to get a highscore entry, so think twice before using any cheat. ***** Peek into object creation [cheat_peek] Cheaters never win. But they can peek at object creation. ***** Peek into monster creation [cheat_hear] Cheaters never win. But they can peek at monster creation. ***** Peek into dungeon creation [cheat_room] Cheaters never win. But they can peek at room creation. ***** Peek into something else [cheat_xtra] Cheaters never win. But they can see debugging messages. ***** Know complete monster info [cheat_know] Cheaters never win. But they can know all about monsters. ***** Allow player to avoid death [cheat_live] Cheaters never win. But they can cheat death. -- Original : Ben Harrison Updated : Zangband 2.1.* by Topi Ylinen Updated : Zangband 2.2.0 through 2.2.6c by Robert Ruehlmann Updated : Zangband DevTeam Last update: February 27, 2002 zangband/lib/help/pref.hlp0000644000000000000000000000133110250356274014501 0ustar rootrootUser Preference Files. Please choose one of the following online help files: (0) User Preferences (pref.txt) (1) User Preference File Commands (pref.txt#Commands) (2) Macros (pref.txt#Macros) (3) Key Maps (pref.txt#Keymaps) (4) Visuals (pref.txt#Visuals) (5) Colors (pref.txt#Colors) (6) Options (pref.txt#Options) (?) Help System Commands (helpinfo.txt) ***** [0] pref.txt ***** [1] pref.txt#Commands ***** [2] pref.txt#Macros ***** [3] pref.txt#Keymaps ***** [4] pref.txt#Visuals ***** [5] pref.txt#Colors ***** [6] pref.txt#Options zangband/lib/help/pref.txt0000644000000000000000000002250710250356274014545 0ustar rootroot=== User Pref Files === Zangband allows you to change various aspects of the game to suit your tastes. You may define keymaps (changing the way Zangband maps your keypresses to underlying commands), create macros (allowing you to map a single keypress to a series of keypresses), modify the visuals (allowing you to change the appearance of monsters, objects, or terrain features), change the colors (allowing you to make a given color brighter, darker, or even completely different), or set options (turning them off or on). Zangband stores your preferences in files called "user pref files", which contain comments and "user pref commands", which are simple strings describing one aspect of the system about which the user has a preference. There are many ways to load a user pref file, and in fact, some of these files are automatically loaded for you by the game. All of the files are kept in the "lib/user/" directory, though you may have to use one of the command line arguments to redirect this directory, especially on multiuser systems. You may also enter single user pref commands directly, using the special "Enter a user pref command" command, activated by "double quote". You may have to use the "redraw" command (^R) after changing certain of the aspects of the game, to allow Zangband to adapt to your changes. When the game starts up, after you have loaded an old character, or created a new character, some user pref files are loaded automatically. First, the "pref.prf" file is loaded. This file contains some user pref commands which will work on all platforms. Then one of "font-xxx.prf" (for normal usage) or "graf-xxx.prf" (for bitmap usage) is loaded. These files contain attr/char changes to allow the monsters, objects, and/or terrain features to look "better" on your system. Then the "pref-xxx.prf" file is loaded. This file contains pre-defined system specific stuff (macros, color definitions, etc). Then, the "user-xxx.prf" file is loaded. This file contains user-defined system specific stuff. The "user-xxx.prf" file is used as the "default" user pref file in many places. The "xxx" is the "system suffix" for your system, taken from the "main-xxx.c" file which was used to generate your executable. Finally, the "Race.prf", "Class.prf", and "Name.prf" files are loaded, where "Race", "Class", and "Name" are replaced by the actual race, class, and name of the current character. Several commands allow you to both load existing user pref files, create new user pref files, append information to existing user pref files, and/or interact with various of the user preferences in a more intuitive way than the user pref commands allow. The commands include "Interact with macros" (@), "Interact with visuals" (%), and "Interact with colors" (&), described below. ***** --- User pref file commands --- Interact with options (=) Allow you to interact with options. Note that using the "cheat" options may mark your savefile as unsuitable for the high score list. You may change normal options using the "X" and "Y" user pref commands. You must use the "redraw" command (^R) after changing certain options. Interact with macros (@) Allow you to interact with macros. You may load or save macros from user pref files, create macros of various types, or define keymaps. You must define a "current action", shown at the bottom of the screen, before you attempt to use any of the "create macro" commands, which use that "current action" as their action. This is a horrible interface, and will be fixed eventually. Interact with visuals (%) Allow you to interact with visuals. You may load or save visuals from user pref files, or modify the attr/char mappings for the monsters, objects, and terrain features. You must use the "redraw" command (^R) to redraw the map after changing attr/char mappings. Interact with colors (&) Allow the user to interact with colors. This command only works on some systems. Interact with the system (!) Allow the user to interact with the underlying visual system. This command is currently unused. ***** --- User Pref Files (Macros) --- The "Interact with macros" command allows you to define or remove "macros", which are mappings from a single logical keypress to a sequence of keypresses, allowing you to use special keys on the keyboard, such as function keys or keypad keys, possibly in conjunction with modifier keys, to "automate" repetitive multi-keypress commands that you use a lot. Since macros represent keypress sequences, and not all keypresses have a printable representation, macro triggers and actions must often be "encoded" into a human readable form. This is done using several types of encoding, including "\xHH" for character number HH in hexadecimal, "\e" for the "escape" code, "\n" for the "newline" code, "\r" for the "return" code, "\s" for the "space" code, "\\" for backslash, "\^" for caret, and "^X" for the code for any "control" key "ctrl-X". Note that the "action" of a macro will not be checked against other macro triggers (unless the macro action contains a "control-backslash"), so you cannot make infinite loops. You may specify extremely long macros, but you are limited in length by the underlying input mechanisms, which in general limit you to about 1024 keys in both triggers and actions. The special "\" command (which must be encoded in macros as "\\") is very useful in macros, since it bypasses all keymaps and allows the next keystroke to be considered a command in the underlying Zangband command set. For a list of the Zangband command set, see the "command.txt" help file. For example, a macro which maps Shift-KP6 to "\" + "." + "6" will induce the "run east" behavior, regardless of what keyset the user has chosen, and regardless of what keymaps have been defined. Macros can be specified in user pref files as a pair of lines, one of the form "A:", which defines the encoded macro action, and one of the form "P:", which defines the encoded macro trigger. ***** --- User Pref Files (Keymaps) --- The "Interact with macros" command also allows you to define "keymaps", which are vaguely related to macros. A keymap maps a single keypress to a series of keypresses, which bypass both other keymaps and any macros. Zangband uses keymaps to map the original and the roguelike keysets to the underlying command set, and allows the user to modify or add keymaps of their own. Note that all keymap actions must be specified using underlying commands, not keypresses from the original or roguelike keysets. The original keyset is almost identical to the underlying keyset, except that "numbers" are mapped to ";" plus a direction, "5" is mapped to ",", and a few control-keys are mapped to various things. See "command.txt" for the full set of underlying commands. Some uses for keymaps include the ability to "disable" a command by mapping it to "\x00". Keymaps can be specified in user pref files as lines of the form "M: ", where is the keyset (0 for original and 1 for roguelike), is the encoded trigger key, and is the encoded keymap action. ***** --- User Pref Files (Visuals) --- You can use the "Interact with visuals" command to change various visual information, currently including the choice of what attr/char values are used to represent various monsters, objects, or terrain features. Note that in combination appropriate support in "main-xxx.c", and with the use of the "use_graphics" flag, you may be able to specify that "graphic bitmaps" should be used instead of normal "colored characters" for various things. When interactively modifying the attr/char values for monsters, objects, or terrain features, pressing "n" or "N" will change which entry you are changing, pressing "a" or "A" will rotate through the available attr values, and pressing "c" or "C" will rotate though the available char values. Note that attr/char values with the "high bit" set may induce the display of special "graphic" pictures if the "use_graphics" flag is set, and your system supports the "use_graphics" flag. Note that this command can be abused in various ways, and if you must do so, remember that you are only cheating yourself. Keymaps can be specified in user pref files as lines of the form "R::/" or "K::/" or "F::/" or "U::/". ***** --- User Pref Files (Colors) --- The "Interact with colors" command allows you to change the actual internal values used to display various colors. This command may or may not have any effect on your machine. Advanced machines may allow you to change the actual RGB values used to represent each of the 16 colors used by Zangband, and perhaps even allow you to define new colors which are not currently used by Zangband. Colors can be specified in user pref files as lines of the form "V:::::". ***** --- User Pref Files (Options) --- The "Interact with options" command allows you to turn options on or off. You may turn options off or on using the user pref commands of the form "X:

This file is the original description of the Tk port by Tim Baker. It is now fairly out of date.

Using The Tk Port Interface

This file is meant to be a very quick summary of the major features of the interface. Make sure you read "The Mouse" section at least.

Game Windows

Note: Currently the game recognizes two screen sizes: (1) width greater or equal to 800, and (2) anything less than 800. Some of the windows support dynamic resizing and some don't. If you want to move the windows around, be sure to choose the "Save Window Positions" option from the "Window" menu.

Note: In general, clicking the close box on a window has the same effect as typing Escape. Windows that shouldn't be closed in this way just beep at you.

Message Window

The Message Window displays messages to you. You can click the Message Window once to display the previous message. Double-click it to see a list of previous messages. If the "-more-" prompt is displayed then clicking clears the prompt.
Note: The Message Window is used throughout the game. You should ensure that it does not become obscurred or you may get confused.

Main Window

The Main Window displays the cave, status messages, the Monster Health Bar, player depth, and an information line. Click the depth display to toggle the "depth_in_feet" option. The "Map (M)" command displays a small-scale map in the Main Window, with scroll bars. Pointing to a location in the dungeon map shows what is at that location, displaying the surrounding grids in the Micro Map Window. Click the dungeon map to hide it again. Right-Click the dungeon map to bring up a popup menu that allows you to change the map's scale. Click-drag to scroll the map. Click the "C" in the status bar to recenter the display at the character location.

Micro Map Window

The Micro Map Window displays a miniature version of the cave. Pointing to a location displays (in the Main Window) what is at that location, and displays a description of the location, as is done in the Main Window. Click the Micro Map Window to display the full cave map. Right-Click the Micro Map Window to bring up a popup menu of resolutions. Click-drag to scroll the map. Shift-click to fix the Main Window at the location.

Inventory Window

The Inventory Window displays the inventory and equipment your character has. When the game is asking you to choose an item, simply double-click the item to choose it. When an item is highlighted, the item memory is displayed in the Recall Window. Right-click on an item to pop up a menu of commands. To change some options related to the display of the inventory, click the down-arrow button in the toolbar.

Recall Window

The Recall Window displays your character's memory about artifacts, monsters and objects. When the game is asking the user to select something, such as an inventory item or spell, a list of choices is displayed. When the pointer is over a row it is highlighted: click the item or spell to choose it. When the pointer is inside the Recall Window, the window automatically grows to reveal all of its content. When the pointer leaves the Recall Window, the window shrinks back to its regular size. When the Choice Window is visible, then all choices are displayed in the Choice Window instead.

Misc Window

The Misc Window displays a few of the more important attributes for your character. Click the character icon to see the Character Window. The 9 buttons at the top of the Misc Window give you quick access to various functions. Click the "EXP" label to toggle between total experience points and points needed to advance. Click the "AC" label to toggle between total armor class and base,bonus. Right-click to pop up a menu to choose Text Labels versus graphical labels.

Character Window

The Character Window displays information about the character. Menu commands allow the user to change the character icon, character name, and to write a character record to a text file.

Character Window (Flags)

The Flags page of the Character Window displays a table of properties affecting the current character. Along the top of the table is one icon for each equipment item and one icon for the character. Each row in the table displays, under each icon, what properties the item gives the character, or what the character's intrinsic abilities are. Pointing to one of the icons displays a description of the item, (or the character's name, race and class) and the item memory is displayed in the Recall Window.

Book Window

The Book Window shows the spells in a given book. You can double-click a spell to select it when prompted.

Store Window

When the game is waiting for a command, double-click an item to initiate a purchase (or take an item in the Home). Or, when the game is asking you to select an item to purchase double-click one. When an item is selected the memory is displayed in the Recall Window, and the quantity field is reset to 1. To buy a number of items, type in a number type ENTER.

Options Window

The options window allows you to change the multitude of options available to you. There are a couple of slider controls, but most of the options are boolean on/off options that you can toggle by clicking the checkbox beside the description of each option. Options from the non-Tk version of the game can be displayed, but these options have no effect.

Knowledge Window

The "Knowledge (~)" command brings up this window. On the left is a list of groups of related monsters. Click a group to see a list of monsters in that group. Click a monster on the right and the monster memory is displayed. The menu allows you to see groups of artifacts as well. You can search for known artifacts and monsters by name.


Macros Window

The Macros Window displays a list of all currently defined macros. On the left is a string representing the trigger keypress. On the right is the associated action for the macro.

Type 'n' to create a new macro. Point the mouse over the Trigger Entry and then type a new keypress to invoke the selected macro.

Type an encoded action into the Action Entry and hit Enter. Keep in mind the following representations:

    \e -- Escape
    \s -- Space
    \\ -- Backslash
Macros are not the same as in the original Angband. They use X Windows symbols for keypress names, which are human-readable. This means most of your old macros won't work with AngbandTk.

You can read an existing preferences file by choosing "Load Pref File" from the menu. You can append all the currently defined macros to a new or exising file by choosing "Dump Macros" from the menu. Even though the system asks you to confirm replacing an existing file, the macros are actually appended to the file you choose.


Keymap Window

The Keymap Window displays a keyboard and shows you which keys have keymaps assigned to them. A keymap is just like a macro only it applies to "normal" keys. Click the left mouse button in the displayed keyboard to select a key, enter an action in the Action entry, and hit Enter. Entering an empty string deletes the keymap for the selected key.

Progress Window

The Progress Window displays the character's hitpoints (HP), spell points (also called mana) (SP), and food level (FD). The appearance of the window can be changed using a context menu (right-click to access the menu).

Choice Window

The Choice Window displays a list of choices whenever the game is asking the user to select something, such as an inventory item or spell. A single click chooses the item or spell. When the game is not asking the user to choose, the character's inventory or equipment is displayed (type / to toggle). A context menu of actions is available by right-clicking in a row. The default action is invoked by double-clicking an item. The Choice Window can be closed by clicking the close box in the title bar, and can be shown by selecting the menu entry in the Window Menu. When the Choice Window is not visible, then all choices are displayed in the Recall Window instead. Clicking outside any item pops up a menu of options which control the appearance and behaviour of the Choice Window.

Messages Window

The Messages Window displays the most recent messages. The Messages Window can be closed by clicking the close box in the title bar, and can be displayed by selecting the menu entry in the Window Menu.

The Mouse

These notes apply to mouse events in the Main Window.

1. Click the left mouse button (ButtonPress-1) to move the character one step in the corresponding direction. Pressing and holding the left mouse button moves the character continuously, but is not the same as running (see below).

Note: No game turns are consumed by moving the character into a wall or on-the-spot, except for the initial mouse click.
2. Click the right mouse button (ButtonPress-3) to make the character run in the corresponding direction. Alternatively, shift-click the left mouse button (Shift-ButtonPress-1) to make the character run.

3. Control-click the left mouse button (Control-ButtonPress-1) to invoke the "Alter (+)" command. See "commands.txt" for what that does.

4. Simply pointing the cursor at a given cave location provides a description of what is seen, similar to the "Look (l)" command. When the cursor is over a visible monster, the monster health bar is shown and monster memory is displayed.

5. When the game is asking you to choose an inventory item, press the right mouse button (ButtonPress-3) and a popup menu pops up. Select an item from this menu to choose it; otherwise type ESACPE twice. You can also choose spells to cast and study in this way.

6. When the game is asking you to enter a direction (as when firing an arrow or aiming a wand), simply click the mouse in the desired direction. Or right-click to target the monster or location under the mouse.

7. When the "-more-" prompt is displayed, and the "quick_messages" option is set, click the mouse to skip past the message. It also works if the mouse button is held down (as when fighting a monster).

8. When a repeated command is in progress (running, resting, tunneling, etc), simply click the left mouse button (ButtonPress-1) to interrupt the command.

9. Control-right-click (Control-ButtonPress-3) pops up a cascading menu of items in the character's inventory. Seleting an item uses the item.

10. When the game is asking you to target a monster or location, left-click to set the target. If a targettable monster is at the location, it is targetted. Otherwise the location is targetted.

Performance Issues

From a fast, portable, terminal-based game to a single-system multimedia monster, the performance of AngbandTk may leave you wanting more. Here are some tips for a faster gaming experience.
  • Set your monitor to High Color (16-bit) instead of True Color (24 bit).
  • Keep the Main Window and Micro Map Window sizes to a minimum.
  • Play with 32x32 icons instead of 16x16.
  • Turn off the music.
  • Close any programs which are running in the background.
  • Don't assign a sound to the "Walk a step" event.
  • Buy a faster computer. :-)
zangband/lib/help/makefile.zb0000644000000000000000000000155010250356274015155 0ustar rootrootsubdir = ./lib/help/ ## makefile.zb srcfiles += lib/help/makefile.zb files += \ lib/help/attack.hlp lib/help/attack.txt lib/help/birth.hlp \ lib/help/birth.txt lib/help/bldg.txt lib/help/charattr.hlp \ lib/help/charattr.txt lib/help/command.hlp lib/help/command.txt \ lib/help/commdesc.hlp lib/help/commdesc.txt lib/help/defend.hlp \ lib/help/defend.txt lib/help/dungeon.hlp lib/help/dungeon.txt \ lib/help/gambling.txt lib/help/general.hlp lib/help/general.txt \ lib/help/help.hlp lib/help/helpinfo.txt lib/help/magic.hlp \ lib/help/magic.txt lib/help/monster.hlp lib/help/monster.txt \ lib/help/objects.hlp lib/help/objects.txt lib/help/option.hlp \ lib/help/option.txt lib/help/pref.hlp lib/help/pref.txt \ lib/help/readme.txt lib/help/spoiler.hlp lib/help/town.hlp \ lib/help/town.txt lib/help/version.txt lib/help/wizard.txt \ lib/help/interface.html zangband/lib/info/0000755000000000000000000000000010250356274013045 5ustar rootrootzangband/lib/info/makefile.zb0000644000000000000000000000010710250356274015155 0ustar rootrootsubdir = ./lib/info/ ## makefile.zb srcfiles += lib/info/makefile.zb zangband/lib/pref/0000755000000000000000000000000010250356274013046 5ustar rootrootzangband/lib/pref/colors.prf0000644000000000000000000000135510250356274015064 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # Color redefinitions # Color 'White' V:1:0x07:0xFF:0xFF:0xFF # Color 'Slate' V:2:0x03:0x8C:0x8C:0x8C # Color 'Orange' V:3:0x0C:0xFF:0x77:0x00 # Color 'Red' V:4:0x04:0xC9:0x00:0x00 # Color 'Green' V:5:0x02:0x00:0x86:0x45 # Color 'Blue' V:6:0x01:0x00:0x00:0xE3 # Color 'Umber' V:7:0x06:0x8E:0x45:0x00 # Color 'Light Dark' V:8:0x08:0x50:0x50:0x50 # Color 'Light Slate' V:9:0x0B:0xD1:0xD1:0xD1 # Color 'Violet' V:10:0x05:0xC0:0x00:0xAF # Color 'Yellow' V:11:0x0E:0xFF:0xFF:0x00 # Color 'Light Red' V:12:0x0D:0xFF:0x00:0x68 # Color 'Light Green' V:13:0x0A:0x00:0xFF:0x00 # Color 'Light Blue' V:14:0x09:0x51:0xDD:0xFF # Color 'Light Umber' V:15:0x06:0xD7:0x8E:0x45 zangband/lib/pref/font-ami.prf0000644000000000000000000000116410250356274015273 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-ami.prf # # This file contains text remapping data # currently used in the Amiga version. # # Lars Haugseth # # Color palette - Text V:0:0x01:0x00:0x00:0x00 V:1:0x01:0xFF:0xFF:0xFF V:2:0x01:0xC7:0xC7:0xC7 V:3:0x01:0xFF:0x92:0x00 V:4:0x01:0xFF:0x00:0x00 V:5:0x01:0x00:0xCD:0x00 V:6:0x01:0x00:0x00:0xFE V:7:0x01:0xC8:0x64:0x00 V:8:0x01:0x8A:0x8A:0x8A V:9:0x01:0xE0:0xE0:0xE0 V:10:0x01:0xA5:0x00:0xFF V:11:0x01:0xFF:0xFD:0x00 V:12:0x01:0xFF:0x00:0xBC V:13:0x01:0x00:0xFF:0x00 V:14:0x01:0x00:0xC8:0xFF V:15:0x01:0xFF:0xCC:0x80 zangband/lib/pref/font-dos.prf0000644000000000000000000000046210250356274015312 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-dos.prf # # This file is used by Angband (when it was compiled using "main-dos.c") # to specify simple attr/char remappings using a standard font, allowing # the use of special pseudo-graphic pictures for walls and such. # zangband/lib/pref/font-ibm.prf0000644000000000000000000000325110250356274015273 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-ibm.prf # # This file is used by Angband (when it was compiled using "main-ibm.c") # to specify simple attr/char remappings using a standard font, allowing # the use of the IBM's built in pseudo-graphic pictures for walls and such. # ### Terrain Features ### # # Floors (white / centered dot) # F:1:1/-7 # # Invis traps (white / centered dot) # F:2:1/-7 # Sand -> pebbles. F:8:11/-7 F:9:9/-7 F:10:7/-7 F:11:8/-7 F:12:4/-7 F:13:2/-7 F:14:8/-7 # dirt F:88:7/-7 # grass F:89:5/-7 # invisible wall F:91:1/-7 # snow F:135:9/-7 # # Magma (slate / special solid block) # F:50:2/-80 F:52:2/-80 # # Quartz (light slate / special solid block) # F:51:9/-80 F:53:9/-80 # # Secret door (white / solid block) # F:48:1/-79 # # Granite walls (white / solid block) # F:56:1/-79 F:57:1/-79 F:58:1/-79 F:59:1/-79 # # Permanent rock (white / solid block) # F:60:1/-79 F:61:1/-79 F:62:1/-79 F:63:1/-79 # Space monster R:144:0x00:-7 # Lurker R:247:0x01:-7 # Stunwall R:326:0x09:-79 # Landmine R:333:0x01:-7 # Livingstone R:336:0x09:-79 # Vampiric mist R:365:0x08:-79 # It R:393:0x09:-7 # Xiclotlan R:396:0x08:-79 # Roper R:426:0x08:-79 # Lesser wall monster R:448:0x09:-79 # Chaos tile R:458:0x0A:-7 # Mist giant R:552:0x0E:-79 # Trapper R:565:0x01:-7 # Time bomb R:567:0x01:-7 # Weird fume R:625:0x0A:-79 # Dark young of Shub-Niggurath R:677:0x08:-79 # Colour out of space R:678:0x0A:-7 # Greater wall monster R:718:0x09:-79 # Ahtu, Avatar of Nyarlathotep R:761:0x08:-79 # Null the Living Void R:803:0x00:-7 # Behinder R:868:0x00:-7 # Noxious fume R:875:0x0B:-79 # Creeping dust R:880:0x0A:-7 zangband/lib/pref/font-mac.prf0000644000000000000000000000074410250356274015270 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font.prf # # This file defines special attr/char mappings for use in "text" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # ## OPTION: Display "veins" (white "%") as "normal walls" (white "#") ## This replaces the old method of setting "notice_seams" to false, ## which no longer works as of Angband 2.7.9, for various reasons. # #F:50:1/35 #F:51:1/35 #F:52:1/35 #F:53:1/35 zangband/lib/pref/font-mon.prf0000644000000000000000000000054710250356274015322 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-mon.prf # # This file is used by Angband when using monochrome mode # ### Terrain Features ### # pool of deep water F:83:6:37 # stream of shallow water F:84:14:37 # pool of deep lava F:85:4:37 # stream of shallow lava F:86:12:37 # dark pit F:87:8:37 # tree F:96:5:37 zangband/lib/pref/font-win.prf0000644000000000000000000000355510250356274015330 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-win.prf # # This file is used by Angband (when it was compiled using "main-win.c") # to specify simple attr/char remappings using a standard font, allowing # the use of special pseudo-graphic pictures for walls and such. # # Note that this file is extremely similar to the "font-ibm.prf" file, # but it uses different codes, since it uses the special pseudo-graphic # symbols defined in the "lib/xtra/font/*.FON" files, and there is only # one special wall type, so we have to use special "colors". # ### Terrain features ### # Floors (white / centered dot) F:1:1/31 # Invis traps (white / centered dot) F:2:1/31 # Sand -> pebbles. F:8:11/31 F:9:9/31 F:10:7/31 F:11:8/31 F:12:4/31 F:13:2/31 F:14:8/31 # dirt F:88:7/31 # grass F:89:5/31 # invisible wall F:91:1/31 # snow F:135:9/31 # Magma (slate / solid block) F:50:2/127 F:52:2/127 # Quartz (light slate / solid block) F:51:9/127 F:53:9/127 # Secret door (white / solid block) F:48:1/127 # Granite walls & permanent rock (white / solid block) F:56:1/127 F:57:1/127 F:58:1/127 F:59:1/127 F:60:1/127 F:61:1/127 F:62:1/127 F:63:1/127 # Space monster R:144:0x00:31 # Lurker R:247:0x01:31 # Stunwall R:326:0x09:127 # Landmine R:333:0x01:31 # Livingstone R:336:0x09:127 # Vampiric mist R:365:0x08:127 # It R:393:0x09:31 # Xiclotlan R:396:0x08:127 # Roper R:426:0x08:127 # Lesser wall monster R:448:0x09:127 # Chaos tile R:458:0x0A:31 # Mist giant R:552:0x0E:127 # Trapper R:565:0x01:31 # Time bomb R:567:0x01:31 # Weird fume R:625:0x0A:127 # Dark young of Shub-Niggurath R:677:0x08:127 # Colour out of space R:678:0x0A:31 # Greater wall monster R:718:0x09:127 # Ahtu, Avatar of Nyarlathotep R:761:0x08:127 # Null the Living Void R:803:0x00:31 # Behinder R:868:0x00:31 # Noxious fume R:875:0x0B:127 # Creeping dust R:880:0x0A:31 zangband/lib/pref/font-x11.prf0000644000000000000000000000353210250356274015137 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-x11.prf # Color palette - Text V:0:0x01:0x00:0x00:0x00 V:1:0x01:0xFF:0xFF:0xFF V:2:0x01:0xC7:0xC7:0xC7 V:3:0x01:0xFF:0x92:0x00 V:4:0x01:0xC0:0x00:0x00 V:5:0x01:0x00:0xC0:0x00 V:6:0x01:0x00:0x00:0xFE V:7:0x01:0xC8:0x64:0x00 V:8:0x01:0x8A:0x8A:0x8A V:9:0x01:0xE0:0xE0:0xE0 V:10:0x01:0xA5:0x00:0xFF V:11:0x01:0xFF:0xFD:0x00 V:12:0x01:0xFF:0x40:0x40 V:13:0x01:0x00:0xFF:0x00 V:14:0x01:0x00:0xC8:0xFF V:15:0x01:0xFF:0xCC:0x80 # # Floors (white / centered dot) # F:1:1/0xB7 # # Invis traps (white / centered dot) # F:2:1/0xB7 # Sand -> pebbles. F:8:11/0xB7 F:9:9/0xB7 F:10:7/0xB7 F:11:8/0xB7 F:12:4/0xB7 F:13:2/0xB7 F:14:8/0xB7 # Pillar F:33:1/0x01 # dirt F:88:7/0xB7 # grass F:89:5/0xB7 # invisible wall F:91:1/0xB7 # snow F:135:9/0xB7 # # Secret doors # F:48:1/0x02 # # Magma # F:50:2/0x02 F:52:2/0x02 # # Quartz # F:51:9/0x02 F:53:9/0x02 # # Treasure veins # #F:54:3/0x02 #F:55:3/0x02 # # Granite walls # F:56:1/0x02 F:57:1/0x02 F:58:1/0x02 F:59:1/0x02 # # Permanent walls # F:60:1/0x02 F:61:1/0x02 F:62:1/0x02 F:63:1/0x02 # Space monster R:144:0x00:0xB7 # Lurker R:247:0x01:0xB7 # Stunwall R:326:0x09:0x02 # Landmine R:333:0x01:0xB7 # Livingstone R:336:0x09:0x02 # Vampiric mist R:365:0x08:0x02 # It R:393:0x09:0xB7 # Xiclotlan R:396:0x08:0x02 # Roper R:426:0x08:0x02 # Lesser wall monster R:448:0x09:0x02 # Chaos tile R:458:0x0A:0xB7 # Mist giant R:552:0x0E:0x02 # Trapper R:565:0x01:0xB7 # Time bomb R:567:0x01:0xB7 # Weird fume R:625:0x0A:0x02 # Dark young of Shub-Niggurath R:677:0x08:0x02 # Colour out of space R:678:0x0A:0xB7 # Greater wall monster R:718:0x09:0x02 # Ahtu, Avatar of Nyarlathotep R:761:0x08:0x02 # Null the Living Void R:803:0x00:0xB7 # Behinder R:868:0x00:0xB7 # Noxious fume R:875:0x0B:0x02 # Creeping dust R:880:0x0A:0xB7 zangband/lib/pref/font-xxx.prf0000644000000000000000000001341410250356274015355 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: font-xxx.prf # # This file defines special attr/char mappings for use in "text" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Special attr/char values ##### ## # Unused (@) ## S:0x00:0x00/0x40 ## S:0x01:0x01/0x40 ## S:0x02:0x02/0x40 ## S:0x03:0x03/0x40 ## S:0x04:0x04/0x40 ## S:0x05:0x05/0x40 ## S:0x06:0x06/0x40 ## S:0x07:0x07/0x40 ## S:0x08:0x08/0x40 ## S:0x09:0x09/0x40 ## S:0x0A:0x0A/0x40 ## S:0x0B:0x0B/0x40 ## S:0x0C:0x0C/0x40 ## S:0x0D:0x0D/0x40 ## S:0x0E:0x0E/0x40 ## S:0x0F:0x0F/0x40 ## # Unused (@) ## S:0x10:0x00/0x40 ## S:0x11:0x01/0x40 ## S:0x12:0x02/0x40 ## S:0x13:0x03/0x40 ## S:0x14:0x04/0x40 ## S:0x15:0x05/0x40 ## S:0x16:0x06/0x40 ## S:0x17:0x07/0x40 ## S:0x18:0x08/0x40 ## S:0x19:0x09/0x40 ## S:0x1A:0x0A/0x40 ## S:0x1B:0x0B/0x40 ## S:0x1C:0x0C/0x40 ## S:0x1D:0x0D/0x40 ## S:0x1E:0x0E/0x40 ## S:0x1F:0x0F/0x40 ## # Unused (@) ## S:0x20:0x00/0x40 ## S:0x21:0x01/0x40 ## S:0x22:0x02/0x40 ## S:0x23:0x03/0x40 ## S:0x24:0x04/0x40 ## S:0x25:0x05/0x40 ## S:0x26:0x06/0x40 ## S:0x27:0x07/0x40 ## S:0x28:0x08/0x40 ## S:0x29:0x09/0x40 ## S:0x2A:0x0A/0x40 ## S:0x2B:0x0B/0x40 ## S:0x2C:0x0C/0x40 ## S:0x2D:0x0D/0x40 ## S:0x2E:0x0E/0x40 ## S:0x2F:0x0F/0x40 # Spells (*) S:0x30:0x00/0x2A S:0x31:0x01/0x2A S:0x32:0x02/0x2A S:0x33:0x03/0x2A S:0x34:0x04/0x2A S:0x35:0x05/0x2A S:0x36:0x06/0x2A S:0x37:0x07/0x2A S:0x38:0x08/0x2A S:0x39:0x09/0x2A S:0x3A:0x0A/0x2A S:0x3B:0x0B/0x2A S:0x3C:0x0C/0x2A S:0x3D:0x0D/0x2A S:0x3E:0x0E/0x2A S:0x3F:0x0F/0x2A # Spells (|) S:0x40:0x00/0x7C S:0x41:0x01/0x7C S:0x42:0x02/0x7C S:0x43:0x03/0x7C S:0x44:0x04/0x7C S:0x45:0x05/0x7C S:0x46:0x06/0x7C S:0x47:0x07/0x7C S:0x48:0x08/0x7C S:0x49:0x09/0x7C S:0x4A:0x0A/0x7C S:0x4B:0x0B/0x7C S:0x4C:0x0C/0x7C S:0x4D:0x0D/0x7C S:0x4E:0x0E/0x7C S:0x4F:0x0F/0x7C # Spells (-) S:0x50:0x00/0x2D S:0x51:0x01/0x2D S:0x52:0x02/0x2D S:0x53:0x03/0x2D S:0x54:0x04/0x2D S:0x55:0x05/0x2D S:0x56:0x06/0x2D S:0x57:0x07/0x2D S:0x58:0x08/0x2D S:0x59:0x09/0x2D S:0x5A:0x0A/0x2D S:0x5B:0x0B/0x2D S:0x5C:0x0C/0x2D S:0x5D:0x0D/0x2D S:0x5E:0x0E/0x2D S:0x5F:0x0F/0x2D # Spells (/) S:0x60:0x00/0x2F S:0x61:0x01/0x2F S:0x62:0x02/0x2F S:0x63:0x03/0x2F S:0x64:0x04/0x2F S:0x65:0x05/0x2F S:0x66:0x06/0x2F S:0x67:0x07/0x2F S:0x68:0x08/0x2F S:0x69:0x09/0x2F S:0x6A:0x0A/0x2F S:0x6B:0x0B/0x2F S:0x6C:0x0C/0x2F S:0x6D:0x0D/0x2F S:0x6E:0x0E/0x2F S:0x6F:0x0F/0x2F # Spells (\) S:0x70:0x00/0x5C S:0x71:0x01/0x5C S:0x72:0x02/0x5C S:0x73:0x03/0x5C S:0x74:0x04/0x5C S:0x75:0x05/0x5C S:0x76:0x06/0x5C S:0x77:0x07/0x5C S:0x78:0x08/0x5C S:0x79:0x09/0x5C S:0x7A:0x0A/0x5C S:0x7B:0x0B/0x5C S:0x7C:0x0C/0x5C S:0x7D:0x0D/0x5C S:0x7E:0x0E/0x5C S:0x7F:0x0F/0x5C # Amulets (") S:0x80:0x00/0x22 S:0x81:0x01/0x22 S:0x82:0x02/0x22 S:0x83:0x03/0x22 S:0x84:0x04/0x22 S:0x85:0x05/0x22 S:0x86:0x06/0x22 S:0x87:0x07/0x22 S:0x88:0x08/0x22 S:0x89:0x09/0x22 S:0x8A:0x0A/0x22 S:0x8B:0x0B/0x22 S:0x8C:0x0C/0x22 S:0x8D:0x0D/0x22 S:0x8E:0x0E/0x22 S:0x8F:0x0F/0x22 # Rings (=) S:0x90:0x00/0x3D S:0x91:0x01/0x3D S:0x92:0x02/0x3D S:0x93:0x03/0x3D S:0x94:0x04/0x3D S:0x95:0x05/0x3D S:0x96:0x06/0x3D S:0x97:0x07/0x3D S:0x98:0x08/0x3D S:0x99:0x09/0x3D S:0x9A:0x0A/0x3D S:0x9B:0x0B/0x3D S:0x9C:0x0C/0x3D S:0x9D:0x0D/0x3D S:0x9E:0x0E/0x3D S:0x9F:0x0F/0x3D # Staffs (_) S:0xA0:0x00/0x5F S:0xA1:0x01/0x5F S:0xA2:0x02/0x5F S:0xA3:0x03/0x5F S:0xA4:0x04/0x5F S:0xA5:0x05/0x5F S:0xA6:0x06/0x5F S:0xA7:0x07/0x5F S:0xA8:0x08/0x5F S:0xA9:0x09/0x5F S:0xAA:0x0A/0x5F S:0xAB:0x0B/0x5F S:0xAC:0x0C/0x5F S:0xAD:0x0D/0x5F S:0xAE:0x0E/0x5F S:0xAF:0x0F/0x5F # Wands (-) S:0xB0:0x00/0x2D S:0xB1:0x01/0x2D S:0xB2:0x02/0x2D S:0xB3:0x03/0x2D S:0xB4:0x04/0x2D S:0xB5:0x05/0x2D S:0xB6:0x06/0x2D S:0xB7:0x07/0x2D S:0xB8:0x08/0x2D S:0xB9:0x09/0x2D S:0xBA:0x0A/0x2D S:0xBB:0x0B/0x2D S:0xBC:0x0C/0x2D S:0xBD:0x0D/0x2D S:0xBE:0x0E/0x2D S:0xBF:0x0F/0x2D # Rods (-) S:0xC0:0x00/0x2D S:0xC1:0x01/0x2D S:0xC2:0x02/0x2D S:0xC3:0x03/0x2D S:0xC4:0x04/0x2D S:0xC5:0x05/0x2D S:0xC6:0x06/0x2D S:0xC7:0x07/0x2D S:0xC8:0x08/0x2D S:0xC9:0x09/0x2D S:0xCA:0x0A/0x2D S:0xCB:0x0B/0x2D S:0xCC:0x0C/0x2D S:0xCD:0x0D/0x2D S:0xCE:0x0E/0x2D S:0xCF:0x0F/0x2D # Scrolls (?) S:0xD0:0x00/0x3F S:0xD1:0x01/0x3F S:0xD2:0x02/0x3F S:0xD3:0x03/0x3F S:0xD4:0x04/0x3F S:0xD5:0x05/0x3F S:0xD6:0x06/0x3F S:0xD7:0x07/0x3F S:0xD8:0x08/0x3F S:0xD9:0x09/0x3F S:0xDA:0x0A/0x3F S:0xDB:0x0B/0x3F S:0xDC:0x0C/0x3F S:0xDD:0x0D/0x3F S:0xDE:0x0E/0x3F S:0xDF:0x0F/0x3F # Potions (!) S:0xE0:0x00/0x21 S:0xE1:0x01/0x21 S:0xE2:0x02/0x21 S:0xE3:0x03/0x21 S:0xE4:0x04/0x21 S:0xE5:0x05/0x21 S:0xE6:0x06/0x21 S:0xE7:0x07/0x21 S:0xE8:0x08/0x21 S:0xE9:0x09/0x21 S:0xEA:0x0A/0x21 S:0xEB:0x0B/0x21 S:0xEC:0x0C/0x21 S:0xED:0x0D/0x21 S:0xEE:0x0E/0x21 S:0xEF:0x0F/0x21 # Food (,) S:0xF0:0x00/0x2C S:0xF1:0x01/0x2C S:0xF2:0x02/0x2C S:0xF3:0x03/0x2C S:0xF4:0x04/0x2C S:0xF5:0x05/0x2C S:0xF6:0x06/0x2C S:0xF7:0x07/0x2C S:0xF8:0x08/0x2C S:0xF9:0x09/0x2C S:0xFA:0x0A/0x2C S:0xFB:0x0B/0x2C S:0xFC:0x0C/0x2C S:0xFD:0x0D/0x2C S:0xFE:0x0E/0x2C S:0xFF:0x0F/0x2C ##### Default inventory object colors ##### # SKELETON E:1:0x01 # BOTTLE E:2:0x01 # JUNK E:3:0x01 # SPIKE E:5:0x02 # CHEST E:7:0x02 # SHOT E:16:0x0F # ARROW E:17:0x0F # BOLT E:18:0x0F # BOW E:19:0x07 # DIGGING E:20:0x02 # HAFTED E:21:0x01 # POLEARM E:22:0x01 # SWORD E:23:0x01 # BOOTS E:30:0x0F # GLOVES E:31:0x0F # HELM E:32:0x0F # CROWN E:33:0x0F # SHIELD E:34:0x0F # CLOAK E:35:0x0F # SOFT_ARMOR E:36:0x02 # HARD_ARMOR E:37:0x02 # DRAG_ARMOR E:38:0x02 # LITE E:39:0x0B # AMULET E:40:0x03 # RING E:45:0x04 # STAFF E:55:0x0F # WAND E:65:0x05 # ROD E:66:0x0A # SCROLL E:70:0x01 # POTION E:75:0x0E # FLASK E:77:0x0B # FOOD E:80:0x0F # LIFE_BOOK E:90:0x01 # SORCERY_BOOK E:91:0x0E # NATURE_BOOK E:92:0x0D # CHAOS_BOOK E:93:0x0C # DEATH_BOOK E:94:0x08 # TRUMP_BOOK E:95:0x03 # ARCANE_BOOK E:96:0x09 # FIGURINE E:8:0x02 # STATUE E:9:0x02 # CORPSE E:10:0x02 zangband/lib/pref/font.prf0000644000000000000000000000252510250356274014531 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2003/03/08 14:38:44 $ # File: font.prf # # This file defines special attr/char mappings for use in "text" mode # # This file includes, if appropriate, various "sub-files" # # See "lib/help/command.txt" and "src/files.c" for more information. # ## # ## # OPTION: Display "veins" (white "%") as "normal walls" (white "#"). ## # ## F:50:0x01/0x23 ## F:51:0x01/0x23 ## F:52:0x01/0x23 ## F:53:0x01/0x23 ##### Standard font file ##### %:font-xxx.prf ##### We want to make vanilla town look vanilla-like as possible ##### # Vanilla town mode? */ ?:[EQU $TOWN VANILLA] # General Store T:34:0x0F:0x31 # Armoury T:35:0x02/0x32 # Weapon Smiths T:36:0x01:0x33 # Temple T:37:0x05:0x34 # Alchemy Shop T:38:0x06:0x35 # Magic Shop T:39:0x04:0x36 # Black Market T:40:0x08:0x37 # Home T:41:0x0B:0x38 # Bookstore T:42:0x03:0x39 ?:1 ##### System Specific Subfiles ##### ?:[IOR [EQU $SYS xaw] [EQU $SYS x11] [EQU $SYS xpj] [EQU $SYS gtk]] %:font-x11.prf ?:[IOR [EQU $SYS gcu] [EQU $SYS vcs]] %:font-gcu.prf ?:[EQU $SYS ami] %:font-ami.prf ?:[EQU $SYS mac] %:font-mac.prf ?:[EQU $SYS win] %:font-win.prf ?:[EQU $SYS w2k] %:font-w2k.prf ?:[EQU $SYS dos] %:font-dos.prf ?:[EQU $SYS ibm] %:font-ibm.prf ?:[EQU $SYS emx] %:font-emx.prf ?:[EQU $SYS acn] %:font-acn.prf ?:[EQU $MONOCHROME ON] %:font-mon.prf ?:1 zangband/lib/pref/graf-ami.prf0000644000000000000000000000061010250356274015237 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: graf-ami.prf # # This file defines special attr/char mappings for use in "graphics" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # # Old tiles (8x8) ?:[EQU $GRAF old] %:graf-xxx.prf F:0:0x01/0x20 F:1:0x81/0x8E F:2:0x81/0x8E # New tiles (16x16) ?:[EQU $GRAF new] %:graf-new.prf ?:1 zangband/lib/pref/graf-dos.prf0000644000000000000000000000051610250356274015263 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: graf-dos.prf # # This file defines special attr/char mappings for use in "graphics" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # # Standard file ?:[EQU $GRAF old] %:graf-xxx.prf # New tiles ?:[EQU $GRAF new] %:graf-new.prf zangband/lib/pref/graf-gcu.prf0000644000000000000000000000105010250356274015246 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: graf-gcu.prf # # This file is used by Angband (when it was compiled using "main-gcu.c") # to specify simple attr/char remappings using the special Curses "ACS" # chracters. # # # Secret doors # F:48:1/0xA3 # # Magma # F:50:2/0xA5 F:52:2/0xA5 # # Quartz # F:51:9/0xA5 F:53:9/0xA5 # # Treasure veins # #F:54:3/0xA3 #F:55:3/0xA3 # # Granite walls # F:56:1/0xA3 F:57:1/0xA3 F:58:1/0xA3 F:59:1/0xA3 # # Permanent walls # F:60:1/0xA3 F:61:1/0xA3 F:62:1/0xA3 F:63:1/0xA3 zangband/lib/pref/graf-ibm.prf0000644000000000000000000011472210250356274015252 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # Monster attr/char definitions # Player R:0:0x01:0xE0 # Filthy street urchin R:1:0x08:0xE0 # Scrawny cat R:2:0x0F:0xEA # Scruffy little dog R:3:0x0F:0xE8 # Farmer Maggot R:4:0x01:0x90 # Blubbering idiot R:5:0x09:0xE0 # Hobo R:6:0x05:0xE0 # Raving lunatic R:7:0x0D:0xE0 # Pitiful looking beggar R:8:0x0F:0xE0 # Mangy looking leper R:9:0x07:0xE0 # Agent of black market R:10:0x08:0xE0 # Singing, happy drunk R:11:0x0B:0xE0 # Aimless looking merchant R:12:0x03:0xE0 # Mean looking mercenary R:13:0x0C:0xE0 # Battle scarred veteran R:14:0x04:0xE0 # Martti Ihrasaari R:15:0x01:0x91 # Grey mold R:16:0x02:0xEE # Blinking dot R:17:0x02:0x8F # Newt R:18:0x0B:0xE2 # Giant white centipede R:19:0x01:0x9E # White icky thing R:20:0x01:0xD7 # Clear icky thing R:21:0x0E:0xD7 # Giant white mouse R:22:0x01:0xF6 # Large brown snake R:23:0x07:0xB4 # Small kobold R:24:0x0D:0xCC # Kobold R:25:0x05:0xCC # White worm mass R:26:0x01:0xFB # Floating eye R:27:0x06:0xE9 # Rock lizard R:28:0x0F:0xE2 # Grid bug R:29:0x0A:0xFC # Jackal R:30:0x0F:0xE8 # Soldier ant R:31:0x07:0xE5 # Fruit bat R:32:0x0A:0x87 # Greater hell-beast R:33:0x02:0x84 # Shrieker mushroom patch R:34:0x0C:0x8F # Blubbering icky thing R:35:0x05:0xD7 # Metallic green centipede R:36:0x0D:0x9E # Novice warrior R:37:0x0F:0xE0 # Novice rogue R:38:0x08:0xE0 # Novice priest R:39:0x0E:0xF8 # Novice mage R:40:0x0C:0xF8 # Yellow mushroom patch R:41:0x0B:0x8F # White jelly R:42:0x01:0xE6 # Giant black ant R:43:0x08:0xE5 # Salamander R:44:0x03:0xAA # White harpy R:45:0x01:0xDE # Blue yeek R:46:0x06:0xD1 # Grip, Farmer Maggot's dog R:47:0x01:0xE8 # Fang, Farmer Maggot's dog R:48:0x01:0xE8 # Freesia R:49:0x07:0xEA # Green worm mass R:50:0x05:0xFB # Large yellow snake R:51:0x0B:0xB4 # Cave spider R:52:0x08:0xF7 # Wild cat R:53:0x0F:0xEA # Smeagol R:54:0x05:0x90 # Green ooze R:55:0x05:0xA9 # Poltergeist R:56:0x02:0xF1 # Metallic blue centipede R:57:0x0E:0x9E # Giant white louse R:58:0x01:0xF4 # Black naga R:59:0x08:0xA5 # Spotted mushroom patch R:60:0x03:0x8F # Silver jelly R:61:0x09:0xE6 # Scruffy looking hobbit R:62:0x02:0x90 # Giant white ant R:63:0x01:0xE5 # Yellow mold R:64:0x0B:0xEE # Metallic red centipede R:65:0x0C:0x9E # Yellow worm mass R:66:0x0B:0xFB # Clear worm mass R:67:0x0E:0xFB # Radiation eye R:68:0x0C:0xE9 # Cave lizard R:69:0x07:0xE2 # Novice ranger R:70:0x05:0xE0 # Blue jelly R:71:0x06:0xE6 # Creeping copper coins R:72:0x07:0xBA # Giant white rat R:73:0x09:0xF6 # Blue worm mass R:74:0x06:0xFB # Large grey snake R:75:0x02:0xB4 # Ewok R:76:0x0D:0x90 # Novice mage R:77:0x0C:0xF8 # Green naga R:78:0x05:0xA5 # Blue ooze R:79:0x06:0xA9 # Green glutton ghost R:80:0x05:0xF1 # Green jelly R:81:0x05:0xE6 # Large kobold R:82:0x04:0xCC # Grey icky thing R:83:0x02:0xD7 # Disenchanter eye R:84:0x0A:0xE9 # Red worm mass R:85:0x04:0xFB # Copperhead snake R:86:0x03:0xB4 # Death sword R:87:0x09:0x18 # Purple mushroom patch R:88:0x0A:0x8F # Novice priest R:89:0x0E:0xF8 # Novice warrior R:90:0x0F:0xE0 # Nibelung R:91:0x08:0x90 # Disembodied hand that strangled people R:92:0x05:0xE3 # Brown mold R:93:0x07:0xEE # Giant brown bat R:94:0x07:0x87 # Novice archer R:95:0x0D:0xE0 # Creeping silver coins R:96:0x02:0xBA # Snaga R:97:0x0D:0xEF # Rattlesnake R:98:0x04:0xB4 # Crypt Creep R:99:0x08:0xB5 # Rotting corpse R:100:0x0C:0xDD # Cave orc R:101:0x0F:0xF3 # Wood spider R:102:0x0F:0xF7 # Manes R:103:0x0C:0xED # Bloodshot eye R:104:0x04:0xE9 # Red naga R:105:0x0C:0xA5 # Red jelly R:106:0x0C:0xE6 # Green icky thing R:107:0x0D:0xD7 # Lost soul R:108:0x07:0xF1 # Night lizard R:109:0x06:0xE2 # Mughash the Kobold Lord R:110:0x0A:0xCC # Wormtongue, Agent of Saruman R:111:0x08:0xF8 # Robin Hood, the Outlaw R:112:0x0D:0xE0 # Lagduf, the Snaga R:113:0x07:0xEF # Brown yeek R:114:0x07:0xD1 # Novice ranger R:115:0x05:0xE0 # Giant salamander R:116:0x04:0xAA # Space monster R:117:0x00:0x2E # Green mold R:118:0x05:0xEE # Novice paladin R:119:0x09:0xFD # Lemure R:120:0x03:0xED # Hill orc R:121:0x02:0xF3 # Bandit R:122:0x08:0xE0 # Hunting hawk of Julian R:123:0x07:0x92 # Phantom warrior R:124:0x0E:0xE0 # Gremlin R:125:0x07:0xEB # Yeti R:126:0x01:0xBE # Bloodshot icky thing R:127:0x04:0xD7 # Giant grey rat R:128:0x02:0xF6 # Black harpy R:129:0x08:0xDE # Orc shaman R:130:0x0E:0xF3 # Baby blue dragon R:131:0x0E:0xF0 # Baby white dragon R:132:0x09:0xF0 # Baby green dragon R:133:0x0D:0xF0 # Baby black dragon R:134:0x02:0xF0 # Baby red dragon R:135:0x0C:0xF0 # Giant red ant R:136:0x0C:0xE5 # Brodda, the Easterling R:137:0x03:0xE0 # King cobra R:138:0x05:0xB4 # War bear R:139:0x07:0xF5 # Killer bee R:140:0x0B:0xE5 # Giant spider R:141:0x02:0xF7 # Dark elven mage R:142:0x0A:0xDF # Orfax, Son of Boldor R:143:0x0E:0xD1 # Dark elven warrior R:144:0x08:0xDF # Quiver slot R:145:0x0F:0x17 # Grishnakh, the Hill Orc R:146:0x07:0xF3 # Hairy mold R:147:0x0F:0xEE # Disenchanter mold R:148:0x0A:0xEE # Pseudo dragon R:149:0x0A:0xF0 # Tengu R:150:0x06:0xED # Creeping gold coins R:151:0x0B:0xBA # Wolf R:152:0x07:0xE8 # Giant fruit fly R:153:0x0F:0xF4 # Panther R:154:0x07:0xEA # Tax collector R:155:0x08:0xE0 # Hobbes the Tiger R:156:0x0B:0xEA # Shadow Creature of Fiona R:157:0x02:0xEC # Undead mass R:158:0x07:0xE6 # Chaos shapechanger R:159:0x0A:0xEC # Baby multi-hued dragon R:160:0x0A:0xF0 # Hippogriff R:161:0x0F:0x9A # Black mamba R:162:0x08:0xB4 # White wolf R:163:0x01:0xE8 # Grape jelly R:164:0x0A:0xE6 # Nether worm mass R:165:0x08:0xFB # Abyss worm mass R:166:0x08:0xFB # Golfimbul, the Hill Orc Chief R:167:0x07:0xF3 # Hellcat R:168:0x0C:0xEA # Moon beast R:169:0x09:0xF5 # Master yeek R:170:0x05:0xD1 # Priest R:171:0x06:0xF8 # Dark elven priest R:172:0x06:0xDF # Air spirit R:173:0x0E:0x8D # Skeleton human R:174:0x01:0xB5 # Zombified human R:175:0x02:0xDD # Tiger R:176:0x03:0xEA # Moaning spirit R:177:0x0F:0xF1 # Frumious bandersnatch R:178:0x06:0x9E # Spotted jelly R:179:0x02:0xE6 # Drider R:180:0x06:0xF7 # Mongbat R:181:0x0F:0x87 # Killer brown beetle R:182:0x07:0x9B # Boldor, King of the Yeeks R:183:0x0A:0xD1 # Ogre R:184:0x0F:0xA8 # Creeping mithril coins R:185:0x0E:0xBA # Illusionist R:186:0x0A:0xF8 # Druid R:187:0x05:0xF8 # Black orc R:188:0x08:0xF3 # Ochre jelly R:189:0x0B:0xA9 # Software bug R:190:0x04:0xF4 # Giant white dragon fly R:191:0x01:0xF4 # Blue icky thing R:192:0x06:0xD7 # Gibbering mouther R:193:0x03:0xA9 # Irish wolfhound of Flora R:194:0x02:0xE8 # Hill giant R:195:0x0F:0x91 # Flesh golem R:196:0x03:0x06 # Warg R:197:0x08:0xE8 # Cheerful leprechaun R:198:0x0D:0x1F # Giant flea R:199:0x02:0xF4 # Black ogre R:200:0x08:0xA8 # Magic mushroom patch R:201:0x0E:0x8F # Guardian naga R:202:0x0B:0xA5 # Light hound R:203:0x01:0xB3 # Shadow hound R:204:0x08:0xB3 # Flying skull R:205:0x01:0xB6 # Mi-Go R:206:0x0A:0xE5 # Giant tarantula R:207:0x0D:0xF7 # Giant clear centipede R:208:0x0E:0x9E # Mirkwood spider R:209:0x07:0xF7 # Frost giant R:210:0x01:0x91 # Griffon R:211:0x07:0x9A # Homonculous R:212:0x0F:0x8E # Gnome mage R:213:0x0C:0x90 # Clear hound R:214:0x0E:0xB3 # Umber hulk R:215:0x07:0x8A # Orc captain R:216:0x06:0xF3 # Gelatinous cube R:217:0x0E:0xE6 # Giant green dragon fly R:218:0x0D:0xF4 # Fire giant R:219:0x04:0x91 # Ulfast, Son of Ulfang R:220:0x03:0xE0 # Quasit R:221:0x09:0xED # Imp R:222:0x04:0x8E # Forest troll R:223:0x0D:0xB8 # 2-headed hydra R:224:0x07:0xE1 # Water spirit R:225:0x06:0x8D # Giant red scorpion R:226:0x0C:0xB7 # Earth spirit R:227:0x0F:0x8D # Fire spirit R:228:0x0C:0x8D # Fire hound R:229:0x04:0xB3 # Cold hound R:230:0x09:0xB3 # Energy hound R:231:0x0B:0xB3 # Mimic (potion) R:232:0x01:0xAD # Blink dog R:233:0x0E:0xE8 # Uruk R:234:0x01:0xF3 # Shagrat, the Orc Captain R:235:0x07:0xF3 # Gorbag, the Orc Captain R:236:0x07:0xF3 # Shambling mound R:237:0x05:0x8F # Stone giant R:238:0x02:0x91 # Giant black dragon fly R:239:0x08:0xF4 # Stone golem R:240:0x02:0x06 # Red mold R:241:0x04:0xEE # Giant gold dragon fly R:242:0x0B:0xF4 # Bolg, Son of Azog R:243:0x0C:0xF3 # Phase spider R:244:0x0E:0xF7 # Wyvern R:245:0x0D:0x9D # Livingstone R:246:0x02:0xB1 # Earth hound R:247:0x07:0xB3 # Air hound R:248:0x0E:0xB3 # Sabre-tooth tiger R:249:0x0B:0xEA # Water hound R:250:0x06:0xB3 # Chimera R:251:0x0B:0x9A # Quylthulg R:252:0x09:0x9C # Sasquatch R:253:0x09:0xBE # Weir R:254:0x09:0xE8 # Dark elven lord R:255:0x08:0xDF # Cloud giant R:256:0x0E:0x91 # Ugluk, the Uruk R:257:0x07:0xF3 # Blue dragon bat R:258:0x06:0x87 # Fire vortex R:259:0x04:0xFA # Water vortex R:260:0x06:0xFA # Cold vortex R:261:0x09:0xFA # Energy vortex R:262:0x0B:0xFA # Mummified orc R:263:0x05:0xA1 # Killer stag beetle R:264:0x02:0x9B # Iron golem R:265:0x09:0x06 # Auto-roller R:266:0x02:0xCF # Giant yellow scorpion R:267:0x0B:0xB7 # Black ooze R:268:0x08:0xA9 # Hardened warrior R:269:0x07:0xE0 # Azog, King of the Uruk-Hai R:270:0x04:0xF3 # Dark elven warlock R:271:0x0A:0xDF # Master rogue R:272:0x08:0xE0 # Red dragon bat R:273:0x04:0x87 # Giant bronze dragon fly R:274:0x0F:0xF4 # Kouko R:275:0x0D:0xAF # Mime, the Nibelung R:276:0x07:0x90 # Hagen, son of Alberich R:277:0x03:0xEC # Phantom beast R:278:0x0E:0x84 # 4-headed hydra R:279:0x0F:0xE1 # Tyrannosaur R:280:0x05:0x88 # Mummified human R:281:0x0F:0xA1 # Vampire bat R:282:0x08:0x87 # Sangahyando of Umbar R:283:0x03:0xE0 # It R:284:0x09:0x2E # Banshee R:285:0x09:0xF1 # Silent watcher R:286:0x02:0x9A # Pukelman R:287:0x07:0x06 # Dark elven druid R:288:0x05:0xDF # Stone troll R:289:0x08:0xB8 # Troll priest R:290:0x0E:0xB8 # Wereworm R:291:0x07:0xFB # Killer crimson beetle R:292:0x0C:0x9B # Giant grey ant R:293:0x02:0xE5 # Kharis the Powerslave R:294:0x07:0xA1 # Displacer beast R:295:0x06:0xEA # Cave ogre R:296:0x07:0xA8 # White wraith R:297:0x01:0xAF # Angel R:298:0x06:0x86 # Ghoul R:299:0x0F:0xDD # Alberich the Nibelung King R:300:0x08:0x90 # Hellblade R:301:0x0A:0x18 # Killer red beetle R:302:0x04:0x9B # Creeping adamantite coins R:303:0x0A:0xBA # Algroth R:304:0x02:0xB8 # Headless R:305:0x07:0x99 # Vibration hound R:306:0x0F:0xB3 # Nexus hound R:307:0x0A:0xB3 # Ogre mage R:308:0x03:0xA8 # Grendel R:309:0x05:0xA8 # Vampire R:310:0x09:0xD9 # Gorgimera R:311:0x08:0x9A # Shantak R:312:0x08:0x92 # Colbran R:313:0x06:0x06 # Spirit naga R:314:0x0E:0xA5 # stairway to hell R:315:0x01:0x94 # Barney the Dinosaur R:316:0x0A:0x88 # Black knight R:317:0x08:0xFD # Lesser wall monster R:318:0x02:0xB1 # Mage R:319:0x04:0xF8 # Mind flayer R:320:0x0A:0xEC # The Ultimate Dungeon Cleaner R:321:0x08:0xCF # Father Dagon R:322:0x05:0xED # Basilisk R:323:0x08:0xE1 # Ice troll R:324:0x01:0xB8 # Dhole R:325:0x02:0xFB # Archangel R:326:0x0E:0x86 # Mimic (ring) R:327:0x01:0x10 # Chaos tile R:328:0x0A:0x2E # Young blue dragon R:329:0x06:0xF0 # Young white dragon R:330:0x01:0xF0 # Young green dragon R:331:0x05:0xF0 # Young bronze dragon R:332:0x0F:0xF0 # Mithril golem R:333:0x0E:0x06 # Shadow drake R:334:0x08:0x16 # Manticore R:335:0x03:0x9A # Giant army ant R:336:0x03:0xE5 # Killer slicer beetle R:337:0x03:0x9B # Ghost R:338:0x01:0xF1 # Death watch beetle R:339:0x0E:0x9B # Ogre shaman R:340:0x06:0xA8 # Nexus quylthulg R:341:0x0A:0x9C # Shelob, Spider of Darkness R:342:0x08:0xF7 # Ninja R:343:0x12:0xE0 # Memory moss R:344:0x06:0x8F # Storm giant R:345:0x06:0x91 # Spectator R:346:0x0E:0xE9 # Cave troll R:347:0x07:0xB8 # Anti-paladin R:348:0x08:0xFD # Logrus master R:349:0x0A:0xF8 # Barrow wight R:350:0x09:0xAF # Giant skeleton troll R:351:0x06:0xB5 # Chaos drake R:352:0x0A:0x16 # Law drake R:353:0x09:0x16 # Balance drake R:354:0x02:0x16 # Ethereal drake R:355:0x03:0x16 # Groo the Wanderer R:356:0x03:0xE0 # Fasolt the Giant R:357:0x07:0x91 # Logrus ghost R:358:0x08:0xF1 # Spectre R:359:0x0D:0xF1 # Water troll R:360:0x06:0xB8 # Fire elemental R:361:0x04:0x8D # Cherub R:362:0x09:0x86 # Water elemental R:363:0x06:0x8D # Multi-hued hound R:364:0x0A:0xB3 # Night stalker R:365:0x01:0xDF # Carrion crawler R:366:0x05:0x9E # Master thief R:367:0x08:0xE0 # Jurt the Living Trump R:368:0x0C:0xE0 # Lich R:369:0x01:0x9F # Master vampire R:370:0x02:0xD9 # Oriental vampire R:371:0x05:0xD9 # Giant grey scorpion R:372:0x02:0xB7 # Earth elemental R:373:0x07:0x8D # Air elemental R:374:0x0E:0x8D # Doom drake R:375:0x0A:0x16 # Malicious leprechaun R:376:0x0A:0x1F # Eog golem R:377:0x08:0x06 # Olog R:378:0x05:0xB8 # Halfling slinger R:379:0x04:0x90 # Gravity hound R:380:0x02:0xB3 # Acidic cytoplasm R:381:0x0D:0xA9 # Inertia hound R:382:0x09:0xB3 # Impact hound R:383:0x07:0xB3 # Ooze elemental R:384:0x0F:0x8D # Young black dragon R:385:0x08:0xF0 # Mature white dragon R:386:0x01:0x16 # Xorn R:387:0x03:0x85 # Pattern ghost R:388:0x08:0xF1 # Grey wraith R:389:0x02:0xAF # Young multi-hued dragon R:390:0x0A:0xF0 # Raal's Tome of Destruction R:391:0x04:0x15 # Colossus R:392:0x0B:0x06 # Young gold dragon R:393:0x0B:0xF0 # Mature blue dragon R:394:0x06:0x16 # Mature green dragon R:395:0x05:0x16 # Mature bronze dragon R:396:0x0F:0x16 # Young red dragon R:397:0x04:0xF0 # Nightblade R:398:0x08:0xDF # Trapper R:399:0x01:0x2E # Bodak R:400:0x08:0xED # Elder thing R:401:0x0D:0x8F # Ice elemental R:402:0x01:0x8D # Ipsissimus R:403:0x08:0xF8 # Lord Borel of Hendrake R:404:0x0A:0xE0 # Chaos spawn R:405:0x07:0xE9 # Time elemental R:406:0x0D:0x8D # The Queen Ant R:407:0x0A:0xE5 # Will o' the wisp R:408:0x0B:0x8D # Magma elemental R:409:0x03:0x8D # Black pudding R:410:0x08:0xE6 # Killer iridescent beetle R:411:0x0A:0x9B # Nexus vortex R:412:0x0A:0xFA # Plasma vortex R:413:0x0C:0xFA # Mature red dragon R:414:0x04:0x16 # Mature gold dragon R:415:0x0B:0x16 # Crystal drake R:416:0x0E:0x16 # Mature black dragon R:417:0x08:0x16 # Mature multi-hued dragon R:418:0x0A:0x16 # Death knight R:419:0x02:0xFD # Mandor, Master of the Logrus R:420:0x0A:0xF8 # Time vortex R:421:0x0D:0xFA # Shimmering vortex R:422:0x0A:0xFA # Ancient blue dragon R:423:0x06:0x89 # Ancient bronze dragon R:424:0x0F:0x89 # Beholder R:425:0x05:0xE9 # Emperor wight R:426:0x0B:0xAF # Seraph R:427:0x01:0x86 # Loge, Spirit of Fire R:428:0x0C:0x8D # Black wraith R:429:0x08:0xAF # Nightgaunt R:430:0x08:0x84 # Baron of hell R:431:0x0F:0x84 # Nether wraith R:432:0x0C:0xAF # Moire, Queen of Rebma R:433:0x06:0xF2 # Kavlax the Many-Headed R:434:0x0A:0x16 # Ancient white dragon R:435:0x01:0x89 # Ancient green dragon R:436:0x05:0x89 # Chthonian R:437:0x08:0xFB # Night mare R:438:0x0A:0xF5 # Vampire lord R:439:0x06:0xD9 # Ancient black dragon R:440:0x08:0x89 # War troll R:441:0x05:0xB8 # Disenchanter worm mass R:442:0x0A:0xFB # Rotting quylthulg R:443:0x0F:0x9C # Lesser titan R:444:0x0B:0x91 # 9-headed hydra R:445:0x03:0xE1 # Enchantress R:446:0x0B:0xF2 # Archpriest R:447:0x06:0xF8 # Sorcerer R:448:0x03:0xF8 # Xaren R:449:0x0B:0x85 # Jubjub bird R:450:0x07:0x92 # Minotaur R:451:0x0F:0xB2 # Jasra, Brand's Mistress R:452:0x0A:0xF2 # Death drake R:453:0x07:0x89 # Ancient red dragon R:454:0x04:0x89 # Ancient gold dragon R:455:0x0B:0x89 # Great crystal drake R:456:0x0E:0x89 # Clubber demon R:457:0x02:0x84 # Death quasit R:458:0x01:0xED # Strygalldwir R:459:0x09:0x84 # Dark elven sorceror R:460:0x0A:0xDF # Master lich R:461:0x09:0x9F # Byakhee R:462:0x08:0x92 # Shoggoth R:463:0x08:0xA9 # Rinaldo, son of Brand R:464:0x01:0xC7 # Archon R:465:0x0B:0x86 # Formless spawn of Tsathoggua R:466:0x08:0x84 # Hunting horror R:467:0x08:0xB0 # Undead beholder R:468:0x02:0xE9 # Shadow demon R:469:0x0A:0xF1 # Iron lich R:470:0x02:0xB6 # Mumak R:471:0x02:0xF5 # Ancient multi-hued dragon R:472:0x0A:0x89 # Ethereal dragon R:473:0x03:0x89 # Dark young of Shub-Niggurath R:474:0x05:0x84 # Quaker, Master of Earth R:475:0x07:0x8D # Ariel, Queen of Air R:476:0x0E:0x8D # 11-headed hydra R:477:0x0C:0xE1 # High priest R:478:0x01:0xF8 # Dreadmaster R:479:0x03:0xF1 # Drolem R:480:0x05:0x89 # Scatha the Worm R:481:0x01:0x89 # Warrior of the Dawn R:482:0x0C:0xE0 # Nazgul R:483:0x08:0x83 # Smaug the Golden R:484:0x0C:0x89 # The Stormbringer R:485:0x08:0x18 # Ultra-elite paladin R:486:0x01:0xFD # Dracolich R:487:0x0E:0x89 # Greater titan R:488:0x0A:0x91 # Dracolisk R:489:0x04:0xB0 # Spectral tyrannosaur R:490:0x0D:0x88 # Death mold R:491:0x08:0xEE # Fafner the Dragon R:492:0x0D:0x89 # Glaurung, Father of the Dragons R:493:0x04:0x89 # Greater wall monster R:494:0x02:0xB1 # Balrog R:495:0x0C:0x84 # Goat of Mendes R:496:0x08:0xF9 # Nightwing R:497:0x02:0x87 # Maulotaur R:498:0x07:0xB2 # Nether hound R:499:0x08:0xB3 # Time hound R:500:0x0D:0xB3 # Plasma hound R:501:0x0C:0xB3 # Demonic quylthulg R:502:0x0C:0x9C # Great storm wyrm R:503:0x06:0x89 # Baphomet the Minotaur Lord R:504:0x08:0xB2 # Bull Gates R:505:0x08:0x90 # Santa Claus R:506:0x04:0xEC # Lord of Chaos R:507:0x0A:0xFD # Khamul the Easterling R:508:0x06:0x83 # Hound of Tindalos R:509:0x02:0xB3 # Great ice wyrm R:510:0x01:0x89 # The Phoenix R:511:0x04:0x92 # Nightcrawler R:512:0x02:0xFB # Hand druj R:513:0x08:0xE3 # Eye druj R:514:0x08:0xE9 # Skull druj R:515:0x08:0xB6 # Chaos vortex R:516:0x0A:0xFA # Aether vortex R:517:0x0E:0xFA # The Lernean Hydra R:518:0x0D:0xE1 # Thuringwethil R:519:0x08:0xF2 # Great hell wyrm R:520:0x04:0x89 # Hastur the Unspeakable R:521:0x06:0x82 # Draconic quylthulg R:522:0x0D:0x9C # Nyogtha, the Thing that Should not Be R:523:0x08:0xA9 # Dworkin Barimen R:524:0x0A:0xEC # Uriel, Angel of Fire R:525:0x0C:0x86 # Azriel, Angel of Death R:526:0x08:0x86 # Ancalagon the Black R:527:0x08:0x89 # Nightwalker R:528:0x08:0x91 # Raphael, the Messenger R:529:0x0E:0x86 # Saruman of Many Colours R:530:0x0A:0xF8 # Brand, Mad Visionary of Amber R:531:0x0A:0xC7 # Shadowlord R:532:0x06:0xAF # Bast, Goddess of Cats R:533:0x03:0xF2 # Jabberwock R:534:0x0C:0x9D # Chaos hound R:535:0x0A:0xB3 # Great Wyrm of Chaos R:536:0x0A:0x89 # Great Wyrm of Law R:537:0x09:0x89 # Great Wyrm of Balance R:538:0x02:0x89 # Shambler R:539:0x09:0xA0 # Bleys, the Trickster R:540:0x0F:0xC7 # Great Wyrm of Many Colours R:541:0x0A:0x89 # Fiona the Sorceress R:542:0x0B:0xF2 # Tselakus, the Dreadlord R:543:0x0C:0xF1 # Sky Drake R:544:0x0E:0x89 # Julian, Master of Forest Amber R:545:0x05:0xC7 # The Norsa R:546:0x0E:0x9A # Black reaver R:547:0x08:0x9F # Caine, the Conspirator R:548:0x08:0xC7 # Master quylthulg R:549:0x06:0x9C # Greater draconic quylthulg R:550:0x05:0x9C # Greater rotting quylthulg R:551:0x07:0x9C # Vecna, the Emperor Lich R:552:0x0B:0x9F # Omarax the Eye Tyrant R:553:0x0B:0xE9 # Gerard, Strongman of Amber R:554:0x07:0xC7 # Atlach-Nacha, the Spider God R:555:0x08:0xF7 # Aether hound R:556:0x0E:0xB3 # Eric the Usurper R:557:0x0C:0xC7 # Unmaker R:558:0x0A:0x8D # Cyberdemon R:559:0x07:0x84 # Klingsor, Evil Master of Magic R:560:0x0B:0xF8 # Corwin, Lord of Avalon R:561:0x04:0xC7 # The Emperor Quylthulg R:562:0x0B:0x9C # Qlzqqlzuup, the Lord of Flesh R:563:0x0E:0x9C # Benedict, the Ideal Warrior R:564:0x09:0xC7 # The Witch-King of Angmar R:565:0x0A:0x83 # Ithaqua the Windwalker R:566:0x0E:0xBE # Hell hound of Julian R:567:0x04:0xE8 # Cantoras, the Skeletal Lord R:568:0x0E:0xB5 # Mephistopheles, Lord of Hell R:569:0x04:0xB2 # Godzilla R:570:0x05:0x88 # Star-spawn of Cthulhu R:571:0x0D:0xD5 # Kaschei the Immortal R:572:0x0A:0x9F # Yog-Sothoth, the All-in-One R:573:0x0A:0xE6 # Great wyrm of power R:574:0x0B:0x89 # Shub-Niggurath, Black Goat of the Woods R:575:0x08:0x04 # Carcharoth, the Jaws of Thirst R:576:0x08:0xE8 # Nyarlathotep, the Crawling Chaos R:577:0x04:0x96 # Azathoth, Seething Nuclear Chaos R:578:0x0E:0x95 # Gothmog, the High Captain of Balrogs R:579:0x04:0x84 # Great Cthulhu R:580:0x05:0xD5 # Sauron, the Sorcerer R:581:0x03:0xF8 # Oberon, King of Amber R:582:0x0A:0xC7 # The Serpent of Chaos R:583:0x08:0xD2 # Nobody, the Undefined Ghost R:584:0x01:0xF1 # Object attr/char definitions # something K:0:0x01:0x26 # Blindness K:1:0x0F:0x05 # Paranoia K:2:0x0B:0x05 # Confusion K:3:0x05:0x05 # Hallucination K:4:0x07:0x05 # Cure Poison K:5:0x06:0x05 # Cure Blindness K:6:0x06:0x05 # Cure Paranoia K:7:0x01:0x05 # Cure Confusion K:8:0x05:0x05 # Weakness K:9:0x0E:0x05 # Unhealth K:10:0x0A:0x05 # Restore Constitution K:11:0x04:0x05 # Restoring K:12:0x08:0x05 # Stupidity K:13:0x08:0x05 # Naivety K:14:0x04:0x05 # Poison K:15:0x07:0x05 # Sickness K:16:0x09:0x05 # Paralysis K:17:0x0D:0x05 # Restore Strength K:18:0x01:0x05 # Disease K:19:0x02:0x05 # Cure Serious Wounds K:20:0x02:0x05 # & Ration~ of Food K:21:0x0F:0x05 # & Hard Biscuit~ K:22:0x0F:0x05 # & Strip~ of Venison K:23:0x07:0x05 # & Slime Mold~ K:24:0x05:0x05 # & Piece~ of Elvish Waybread K:25:0x01:0x05 # & Pint~ of Fine Ale K:26:0x0F:0xAD # & Pint~ of Fine Wine K:27:0x04:0xAD # & Broken Dagger~ K:30:0x08:0x18 # & Bastard Sword~ K:31:0x09:0x18 # & Scimitar~ K:32:0x09:0x18 # & Tulwar~ K:33:0x09:0x18 # & Broad Sword~ K:34:0x09:0x18 # & Short Sword~ K:35:0x09:0x18 # & Hellblade~ K:36:0x0A:0x18 # & Two-Handed Sword~ K:37:0x09:0x18 # & Main Gauche~ K:38:0x09:0x18 # & Cutlass~ K:39:0x09:0x18 # & Executioner's Sword~ K:40:0x04:0x18 # & Katana~ K:41:0x01:0x18 # & Long Sword~ K:42:0x09:0x18 # & Dagger~ K:43:0x0E:0x18 # & Rapier~ K:44:0x09:0x18 # & Sabre~ K:45:0x09:0x18 # & Small Sword~ K:46:0x09:0x18 # & Broken Sword~ K:47:0x08:0x18 # & Ball-and-Chain~ K:48:0x08:0xC1 # & Whip~ K:49:0x07:0xC1 # & Flail~ K:50:0x08:0xC1 # & Two-Handed Flail~ K:51:0x0B:0xC1 # & Morning Star~ K:52:0x08:0xC1 # & Mace~ K:53:0x08:0xC1 # & Quarterstaff~ K:54:0x0F:0xC9 # & War Hammer~ K:55:0x08:0xC4 # & Lead-Filled Mace~ K:56:0x08:0xC1 # & Mace~ of Disruption K:57:0x04:0xC1 # & Lucerne Hammer~ K:58:0x0E:0xC4 # & Beaked Axe~ K:59:0x02:0xBD # & Glaive~ K:60:0x02:0xBD # & Halberd~ K:61:0x02:0xBD # & Awl-Pike~ K:62:0x02:0xBD # & Pike~ K:63:0x02:0xBD # & Spear~ K:64:0x07:0xD3 # & Trident~ K:65:0x0B:0xD3 # & Lance~ K:66:0x01:0xD3 # & Great Axe~ K:67:0x02:0xBD # & Battle Axe~ K:68:0x02:0xBD # & Lochaber Axe~ K:69:0x0E:0xBD # & Broad Axe~ K:70:0x02:0xBD # & Scythe~ K:71:0x02:0xBD # & Scythe~ of Slicing K:72:0x0C:0xBD # & Short Bow~ K:73:0x0F:0xCE # & Long Bow~ K:74:0x0F:0xCE # & Light Crossbow~ K:75:0x02:0xCE # & Heavy Crossbow~ K:76:0x02:0xCE # & Sling~ K:77:0x07:0xCE # & Arrow~ K:78:0x0F:0xCA # & Seeker Arrow~ K:79:0x0D:0xCA # & Bolt~ K:80:0x02:0xCA # & Seeker Bolt~ K:81:0x0E:0xCA # & Rounded Pebble~ K:82:0x02:0x1E # & Iron Shot~ K:83:0x08:0x1E # & Shovel~ K:84:0x02:0x11 # & Gnomish Shovel~ K:85:0x0A:0x11 # & Dwarven Shovel~ K:86:0x0E:0x11 # & Pick~ K:87:0x02:0x11 # & Orcish Pick~ K:88:0x0C:0x11 # & Dwarven Pick~ K:89:0x01:0x11 # & Elven Cloak~ K:90:0x0D:0xB9 # & Pair~ of Soft Leather Boots K:91:0x0F:0xBC # & Pair~ of Hard Leather Boots K:92:0x07:0xBC # & Pair~ of Metal Shod Boots K:93:0x02:0xBC # & Hard Leather Cap~ K:94:0x07:0xBB # & Metal Cap~ K:95:0x02:0xBB # & Iron Helm~ K:96:0x08:0xBB # & Steel Helm~ K:97:0x01:0xBB # & Iron Crown~ K:98:0x08:0xBB # & Golden Crown~ K:99:0x0B:0xBB # & Jewel Encrusted Crown~ K:100:0x0A:0xBB # & Robe~ K:101:0x01:0xB9 # & Filthy Rag~ K:102:0x08:0xB9 # Soft Leather Armour~ K:103:0x0F:0x14 # Soft Studded Leather~ K:104:0x0F:0x14 # Hard Leather Armour~ K:105:0x07:0x14 # Hard Studded Leather~ K:106:0x07:0x14 # Leather Scale Mail~ K:107:0x03:0x14 # Metal Scale Mail~ K:108:0x02:0x14 # Chain Mail~ K:109:0x09:0x14 # Rusty Chain Mail~ K:110:0x04:0x14 # Augmented Chain Mail~ K:111:0x09:0x14 # Bar Chain Mail~ K:112:0x09:0x14 # Metal Brigandine Armour~ K:113:0x02:0x14 # Partial Plate Armour~ K:114:0x09:0x14 # Metal Lamellar Armour~ K:115:0x09:0x14 # Full Plate Armour~ K:116:0x09:0x14 # Ribbed Plate Armour~ K:117:0x09:0x14 # Adamantite Plate Mail~ K:118:0x0A:0x14 # Mithril Plate Mail~ K:119:0x0E:0x14 # Mithril Chain Mail~ K:120:0x0E:0x14 # Double Chain Mail~ K:121:0x02:0x14 # & Shield~ of Deflection K:122:0x0E:0xC8 # & Cloak~ K:123:0x03:0xB9 # & Shadow Cloak~ K:124:0x08:0xB9 # & Set~ of Leather Gloves K:125:0x0F:0xCD # & Set~ of Gauntlets K:126:0x02:0xCD # & Set~ of Cesti K:127:0x09:0xCD # & Small Leather Shield~ K:128:0x0F:0xC8 # & Large Leather Shield~ K:129:0x07:0xC8 # & Small Metal Shield~ K:130:0x02:0xC8 # & Large Metal Shield~ K:131:0x02:0xC8 # Strength K:132:0x0C:0x10 # Dexterity K:133:0x08:0x10 # Constitution K:134:0x0B:0x10 # Intelligence K:135:0x01:0x10 # Speed K:136:0x04:0x10 # Searching K:137:0x0F:0x10 # Teleportation K:138:0x0E:0x10 # Slow Digestion K:139:0x0E:0x10 # Resist Fire K:140:0x01:0x10 # Resist Cold K:141:0x07:0x10 # Feather Falling K:142:0x01:0x10 # Poison Resistance K:143:0x0A:0x10 # Free Action K:144:0x09:0x10 # Weakness K:145:0x0D:0x10 # Flames K:146:0x0F:0x10 # Acid K:147:0x0B:0x10 # Ice K:148:0x06:0x10 # Woe K:149:0x01:0x10 # Stupidity K:150:0x05:0x10 # Damage K:151:0x0E:0x10 # Accuracy K:152:0x09:0x10 # Protection K:153:0x0D:0x10 # Aggravate Monster K:154:0x0C:0x10 # See Invisible K:155:0x05:0x10 # Sustain Strength K:156:0x08:0x10 # Sustain Intelligence K:157:0x0E:0x10 # Sustain Wisdom K:158:0x04:0x10 # Sustain Constitution K:159:0x01:0x10 # Sustain Dexterity K:160:0x0D:0x10 # Sustain Charisma K:161:0x06:0x10 # Slaying K:162:0x0E:0x10 # Wisdom K:163:0x0F:0x0C # Charisma K:164:0x01:0x0C # Searching K:165:0x0F:0x0C # Teleportation K:166:0x0F:0x0C # Slow Digestion K:167:0x0B:0x0C # Resist Acid K:168:0x0E:0x0C # Adornment K:169:0x07:0x0C # the Magi K:171:0x0A:0x0C # DOOM K:172:0x0C:0x0C # Enchant Weapon To-Hit K:173:0x01:0x0E # Enchant Weapon To-Dam K:174:0x01:0x0E # Enchant Armor K:175:0x01:0x0E # Identify K:176:0x01:0x0E # *Identify* K:177:0x01:0x0E # Rumour K:178:0x01:0x0E # Logrus K:179:0x01:0x0E # Remove Curse K:180:0x01:0x0E # Light K:181:0x01:0x0E # Fire K:182:0x01:0x0E # Ice K:183:0x01:0x0E # Summon Monster K:184:0x01:0x0E # Phase Door K:185:0x01:0x0E # Teleportation K:186:0x01:0x0E # Teleport Level K:187:0x01:0x0E # Monster Confusion K:188:0x01:0x0E # Magic Mapping K:189:0x01:0x0E # Rune of Protection K:190:0x01:0x0E # *Remove Curse* K:191:0x01:0x0E # Treasure Detection K:192:0x01:0x0E # Object Detection K:193:0x01:0x0E # Trap Detection K:194:0x01:0x0E # Door/Stair Location K:197:0x01:0x0E # Acquirement K:198:0x0C:0x0E # *Acquirement* K:199:0x04:0x0E # Mass Genocide K:200:0x02:0x0E # Detect Invisible K:201:0x01:0x0E # Aggravate Monster K:202:0x01:0x0E # Trap Creation K:203:0x01:0x0E # Trap/Door Destruction K:204:0x01:0x0E # Artifact Creation K:205:0x01:0x0E # Recharging K:206:0x01:0x0E # Genocide K:207:0x09:0x0E # Darkness K:208:0x01:0x0E # Protection from Evil K:209:0x01:0x0E # Satisfy Hunger K:210:0x01:0x0E # Dispel Undead K:211:0x01:0x0E # *Enchant Weapon* K:212:0x01:0x0E # Curse Weapon K:213:0x01:0x0E # *Enchant Armor* K:214:0x01:0x0E # Curse Armor K:215:0x01:0x0E # Summon Undead K:216:0x01:0x0E # Blessing K:217:0x01:0x0E # Holy Chant K:218:0x01:0x0E # Holy Prayer K:219:0x01:0x0E # Word of Recall K:220:0x01:0x0E # *Destruction* K:221:0x01:0x0E # Slime Mold Juice K:222:0x05:0xAD # Apple Juice K:223:0x0F:0xAD # Water K:224:0x0E:0xAD # Strength K:225:0x06:0xAD # Weakness K:226:0x0A:0xAD # Restore Strength K:227:0x02:0xAD # Intelligence K:228:0x0B:0xAD # Stupidity K:229:0x03:0xAD # Restore Intelligence K:230:0x05:0xAD # Wisdom K:231:0x09:0xAD # Naivety K:232:0x09:0xAD # Restore Wisdom K:233:0x0B:0xAD # Charisma K:234:0x0C:0xAD # Ugliness K:235:0x01:0xAD # Restore Charisma K:236:0x05:0xAD # Curing K:237:0x04:0xAD # Invulnerability K:238:0x0A:0xAD # New Life K:239:0x08:0xAD # Cure Serious Wounds K:240:0x04:0xAD # Cure Critical Wounds K:241:0x0A:0xAD # Healing K:242:0x0B:0xAD # Constitution K:243:0x07:0xAD # Experience K:244:0x09:0xAD # Sleep K:245:0x04:0xAD # Blindness K:246:0x04:0xAD # Confusion K:247:0x01:0xAD # Poison K:248:0x04:0xAD # Speed K:249:0x0C:0xAD # Slowness K:250:0x0B:0xAD # Dexterity K:251:0x06:0xAD # Restore Dexterity K:252:0x04:0xAD # Restore Constitution K:253:0x06:0xAD # Lose Memories K:254:0x0B:0xAD # Salt Water K:255:0x05:0xAD # Enlightenment K:256:0x07:0xAD # Heroism K:257:0x0A:0xAD # Berserk Strength K:258:0x0C:0xAD # Boldness K:259:0x0A:0xAD # Restore Life Levels K:260:0x0A:0xAD # Resist Heat K:261:0x08:0xAD # Resist Cold K:262:0x06:0xAD # Detect Invisible K:263:0x04:0xAD # Slow Poison K:264:0x05:0xAD # Neutralize Poison K:265:0x0A:0xAD # Restore Mana K:266:0x0D:0xAD # Infra-vision K:267:0x04:0xAD # Resistance K:268:0x0B:0xAD # Light K:269:0x02:0xE7 # Lightning Bolts K:270:0x09:0xE7 # Frost Bolts K:271:0x0B:0xE7 # Fire Bolts K:272:0x02:0xE7 # Stone to Mud K:273:0x0E:0xE7 # Polymorph K:274:0x0F:0xE7 # Heal Monster K:275:0x09:0xE7 # Haste Monster K:276:0x0E:0xE7 # Slow Monster K:277:0x0F:0xE7 # Confuse Monster K:278:0x0F:0xE7 # Sleep Monster K:279:0x09:0xE7 # Drain Life K:280:0x01:0xE7 # Trap/Door Destruction K:281:0x01:0xE7 # Magic Missile K:282:0x09:0xE7 # Clone Monster K:283:0x07:0xE7 # Scare Monster K:284:0x0B:0xE7 # Teleport Other K:285:0x09:0xE7 # Disarming K:286:0x0E:0xE7 # Lightning Balls K:287:0x09:0xE7 # Cold Balls K:288:0x0F:0xE7 # Fire Balls K:289:0x08:0xE7 # Stinking Cloud K:290:0x01:0xE7 # Acid Balls K:291:0x09:0xE7 # Wonder K:292:0x09:0xE7 # Acid Bolts K:294:0x04:0xE7 # Dragon's Flame K:295:0x09:0xE7 # Dragon's Frost K:296:0x01:0xE7 # Dragon's Breath K:297:0x0F:0xE7 # Annihilation K:298:0x09:0xE7 # Rockets K:299:0x0F:0xE7 # Trap Location K:300:0x02:0xC9 # Treasure Location K:301:0x0F:0xC9 # Object Location K:302:0x0F:0xC9 # Teleportation K:303:0x0F:0xC9 # Earthquakes K:304:0x0F:0xC9 # Summoning K:305:0x07:0xC9 # Light K:306:0x07:0xC9 # *Destruction* K:307:0x0C:0xC9 # Starlight K:308:0x0F:0xC9 # Haste Monsters K:309:0x07:0xC9 # Slow Monsters K:310:0x05:0xC9 # Sleep Monsters K:311:0x0F:0xC9 # Cure Light Wounds K:312:0x0F:0xC9 # Detect Invisible K:313:0x04:0xC9 # Speed K:314:0x0F:0xC9 # Slowness K:315:0x0F:0xC9 # Door/Stair Location K:316:0x0F:0xC9 # Remove Curse K:317:0x0F:0xC9 # Detect Evil K:318:0x0F:0xC9 # Curing K:319:0x0B:0xC9 # Dispel Evil K:320:0x0F:0xC9 # Probing K:321:0x0F:0xC9 # Darkness K:322:0x0F:0xC9 # Genocide K:323:0x09:0xC9 # Power K:324:0x04:0xC9 # the Magi K:325:0x0F:0xC9 # Perception K:326:0x0F:0xC9 # Holiness K:327:0x0F:0xC9 # Enlightenment K:328:0x0F:0xC9 # Healing K:329:0x0E:0xC9 # [Common Book of Prayer] K:330:0x09:0x15 # [High Mass] K:331:0x09:0x15 # [Book of the Unicorn] K:332:0x01:0x15 # [Blessings of the Grail] K:333:0x01:0x15 # [Beginner's Handbook] K:334:0x0E:0x15 # [Master Sorcerer's Handbook] K:335:0x0E:0x15 # [Pattern Sorcery] K:336:0x06:0x15 # [Grimoire of Power] K:337:0x06:0x15 # & Small wooden chest~ K:338:0x03:0x0B # & Large wooden chest~ K:339:0x07:0x0B # & Small iron chest~ K:340:0x02:0x0B # & Large iron chest~ K:341:0x08:0x0B # & Small steel chest~ K:342:0x09:0x0B # & Large steel chest~ K:343:0x01:0x0B # & Ruined chest~ K:344:0x02:0x0B # & Iron Spike~ K:345:0x09:0xCA # & Wooden Torch~ K:346:0x0B:0xC0 # & Brass Lantern~ K:347:0x0B:0xC2 # & Flask~ of oil K:348:0x0B:0xAD # & Empty Bottle~ K:349:0x09:0xAD # Havoc K:350:0x09:0xE7 # Door/Stair Location K:351:0x0F:0xE7 # Trap Location K:352:0x09:0xE7 # Probing K:353:0x09:0xE7 # Recall K:354:0x01:0xE7 # Illumination K:355:0x09:0xE7 # Light K:356:0x04:0xE7 # Lightning Bolts K:357:0x0B:0xE7 # Frost Bolts K:358:0x0F:0xE7 # Fire Bolts K:359:0x0E:0xE7 # Polymorph K:360:0x01:0xE7 # Slow Monster K:361:0x01:0xE7 # Sleep Monster K:362:0x08:0xE7 # Drain Life K:363:0x09:0xE7 # Teleport Other K:364:0x0E:0xE7 # Disarming K:365:0x0E:0xE7 # Lightning Balls K:366:0x01:0xE7 # Cold Balls K:367:0x0F:0xE7 # Fire Balls K:368:0x09:0xE7 # Acid Balls K:369:0x02:0xE7 # Acid Bolts K:370:0x0B:0xE7 # Enlightenment K:371:0x02:0xE7 # Perception K:372:0x09:0xE7 # Curing K:373:0x07:0xE7 # Healing K:374:0x06:0xE7 # Detection K:375:0x09:0xE7 # Restoration K:376:0x0F:0xE7 # Speed K:377:0x0F:0xE7 # [Call of the Wild] K:379:0x0D:0x15 # [Nature Mastery] K:380:0x0D:0x15 # [Nature's Gifts] K:381:0x05:0x15 # [Nature's Wrath] K:382:0x05:0x15 # [Sign of Chaos] K:383:0x0C:0x15 # [Chaos Mastery] K:384:0x0C:0x15 # [Chaos Channels] K:385:0x04:0x15 # [Armageddon Tome] K:386:0x04:0x15 # [Black Prayers] K:387:0x08:0x15 # [Black Mass] K:388:0x08:0x15 # & Shard~ of Pottery K:389:0x04:0xC3 # & Broken Stick~ K:390:0x07:0x81 # & Broken Skull~ K:391:0x01:0xB6 # & Broken Bone~ K:392:0x01:0x81 # & Canine Skeleton~ K:393:0x01:0xC3 # & Rodent Skeleton~ K:394:0x01:0xC3 # & Human Skeleton~ K:395:0x01:0xC3 # & Dwarf Skeleton~ K:396:0x01:0xC3 # & Elf Skeleton~ K:397:0x01:0xC3 # & Gnome Skeleton~ K:398:0x01:0xC3 # Black Dragon Scale Mail~ K:400:0x08:0x14 # Blue Dragon Scale Mail~ K:401:0x06:0x14 # White Dragon Scale Mail~ K:402:0x01:0x14 # Red Dragon Scale Mail~ K:403:0x0C:0x14 # Green Dragon Scale Mail~ K:404:0x05:0x14 # Multi-Hued Dragon Scale Mail~ K:405:0x0A:0x14 # Pseudo Dragon Scale Mail~ K:406:0x0A:0x14 # Law Dragon Scale Mail~ K:407:0x09:0x14 # Bronze Dragon Scale Mail~ K:408:0x0F:0x14 # Gold Dragon Scale Mail~ K:409:0x0B:0x14 # Chaos Dragon Scale Mail~ K:410:0x08:0x14 # Balance Dragon Scale Mail~ K:411:0x02:0x14 # Power Dragon Scale Mail~ K:412:0x0B:0x14 # & Dragon Helm~ K:413:0x0D:0xBB # & Dragon Shield~ K:414:0x0D:0xC8 # Death K:415:0x0C:0xAD # Ruination K:416:0x03:0xAD # Detonations K:417:0x02:0xAD # Augmentation K:418:0x0E:0xAD # *Healing* K:419:0x04:0xAD # Life K:420:0x0E:0xAD # Self Knowledge K:421:0x09:0xAD # *Enlightenment* K:422:0x0F:0xAD # [Black Channels] K:423:0x0A:0x15 # [Necronomicon] K:424:0x0A:0x15 # Fear Resistance K:425:0x07:0x10 # Light and Darkness Resistance K:426:0x08:0x10 # Nether Resistance K:427:0x0B:0x10 # Nexus Resistance K:428:0x05:0x10 # Sound Resistance K:429:0x07:0x10 # Confusion Resistance K:430:0x0E:0x10 # Shard Resistance K:431:0x04:0x10 # Disenchantment Resistance K:432:0x0B:0x10 # Chaos Resistance K:433:0x01:0x10 # Blindness Resistance K:434:0x09:0x10 # Lordly Protection K:435:0x04:0x10 # Extra Attacks K:436:0x02:0x10 # Cure Light Wounds K:437:0x0A:0xAD # Clumsiness K:438:0x0E:0xAD # Sickliness K:439:0x06:0xAD # copper K:480:0x07:0xBA # copper K:481:0x07:0xBA # copper K:482:0x07:0xBA # silver K:483:0x09:0xBA # silver K:484:0x09:0xBA # silver K:485:0x09:0xBA # garnets K:486:0x04:0xD0 # garnets K:487:0x04:0xD0 # gold K:488:0x0B:0xBA # gold K:489:0x0B:0xBA # gold K:490:0x0B:0xBA # opals K:491:0x09:0xD0 # sapphires K:492:0x0E:0xD0 # rubies K:493:0x0C:0xD0 # diamonds K:494:0x01:0xD0 # emeralds K:495:0x0D:0xD0 # mithril K:496:0x0E:0xD0 # adamantite K:497:0x0A:0xBA # & Mighty Hammer~ K:498:0x08:0xC4 # & Massive Iron Crown~ K:499:0x08:0xBB # & Phial~ K:500:0x01:0xAD # & Star~ K:501:0x0B:0x0F # & Jewel~ K:502:0x0C:0x0F # & Amulet~ K:503:0x01:0x0C # & Amulet~ K:504:0x09:0x0C # & Necklace~ K:505:0x08:0x0C # & Ring~ K:506:0x02:0x10 # & Ring~ K:507:0x0B:0x10 # & Ring~ K:508:0x0B:0x10 # & Ring~ K:509:0x09:0x10 # & Ring~ K:510:0x04:0x10 # & Ring~ K:511:0x0B:0x10 # [Conjurings & Tricks] K:512:0x0F:0x15 # [Deck of Many Things] K:513:0x0F:0x15 # [Trumps of Doom] K:514:0x03:0x15 # [Five Aces] K:515:0x03:0x15 # [Cantrips for Beginners] K:516:0x02:0x15 # [Minor Arcana] K:517:0x02:0x15 # [Major Arcana] K:518:0x02:0x15 # [Manual of Mastery] K:519:0x02:0x15 # new amulet K:520:0x0A:0x0C # new amulet K:521:0x0C:0x0C # new amulet K:522:0x0A:0x0C # new amulet K:523:0x0C:0x0C # Unknown Amulet U:40:0x87/0x0C # Unknown Ring U:45:0x82/0x10 # Unknown Staff U:55:0x86/0xC9 # Unknown Wand U:65:0x86/0xE7 # Unknown Rod U:66:0x86/0xE7 # Unknown Scroll U:70:0x86/0x0E # Unknown Potion U:75:0x86/0xAD # Unknown Food U:80:0x8A/0x05 # Feature attr/char definitions # nothing F:0:0x01:0x20 # open floor F:1:0x01:0x2E # invisible trap F:2:0x01:0x2E # glyph of warding F:3:0x0E:0xCB # open door F:4:0x0F:0xC6 # broken door F:5:0x02:0xC6 # up staircase F:6:0x01:0x93 # down staircase F:7:0x01:0x94 # General Store F:8:0x05:0xAB # Armoury F:9:0x0F:0xDA # Weapon Smiths F:10:0x02:0x13 # Temple F:11:0x01:0x80 # Alchemy Shop F:12:0x0A:0xA4 # Magic Shop F:13:0x04:0xA6 # Black Market F:14:0x08:0xAE # Home F:15:0x07:0xC5 # trap door F:16:0x01:0x97 # pit F:17:0x07:0x97 # pit F:18:0x07:0x97 # pit F:19:0x07:0x97 # strange rune F:20:0x0B:0x97 # strange rune F:21:0x0B:0x97 # discolored spot F:22:0x02:0x97 # discolored spot F:23:0x02:0x97 # dart trap F:24:0x04:0x97 # dart trap F:25:0x04:0x97 # dart trap F:26:0x04:0x97 # dart trap F:27:0x04:0x97 # gas trap F:28:0x05:0x97 # gas trap F:29:0x05:0x97 # gas trap F:30:0x05:0x97 # gas trap F:31:0x05:0x97 # door F:32:0x0F:0xC5 # locked door F:33:0x0E:0xC5 # locked door F:34:0x0E:0xC5 # locked door F:35:0x0E:0xC5 # locked door F:36:0x0E:0xC5 # locked door F:37:0x0E:0xC5 # locked door F:38:0x0E:0xC5 # locked door F:39:0x0E:0xC5 # jammed door F:40:0x07:0xC5 # jammed door F:41:0x07:0xC5 # jammed door F:42:0x07:0xC5 # jammed door F:43:0x07:0xC5 # jammed door F:44:0x07:0xC5 # jammed door F:45:0x07:0xC5 # jammed door F:46:0x07:0xC5 # jammed door F:47:0x07:0xC5 # secret door F:48:0x02:0xB1 # rubble F:49:0x07:0x98 # magma vein F:50:0x01:0xB1 # quartz vein F:51:0x09:0xB1 # magma vein F:52:0x01:0xB1 # quartz vein F:53:0x09:0xB1 # magma vein with treasure F:54:0x03:0xBA # quartz vein with treasure F:55:0x0B:0xBA # granite wall F:56:0x02:0xB1 # granite wall F:57:0x02:0xB1 # granite wall F:58:0x02:0xB1 # granite wall F:59:0x02:0xB1 # permanent wall F:60:0x02:0xB1 # permanent wall F:61:0x02:0xB1 # permanent wall F:62:0x02:0xB1 # permanent wall F:63:0x02:0xB1 # explosive rune F:64:0x0C:0xCB # Fields are initialised here. (Note that some don't need tiles.) # Invisible Wall # no tile. # Glyph of warding. T:2:0x0E:0xCB # Explosive rune. T:3:0x0C:0xCB # Corpse #T:4: # Skeleton #T:5: # Trapdoor T:6:0x01:0x97 # Pit T:7:0x07:0x97 # Spiked Pit T:8:0x07:0x97 # Poison Pit T:9:0x07:0x97 # Evil Rune T:10:0xA2:0x8B # Strange Rune T:11:0xA2:0x8A # Discoloured Spot T:12:0x02:0x97 # Discoloured Spot T:13:0x02:0x97 # Gas Trap T:14:0x05:0x97 # Compact Rune T:15:0x0B:0x97 # Dart Trap T:16:0x04:0x97 # Dart Trap T:17:0x04:0x97 # Twisted Rune T:18:0x0B:0x97 # Geometric Rune T:19:0x0B:0x97 # Glowing Rune T:20:0x0B:0x97 # Jagged Rune T:21:0x0B:0x97 # Fractured Rune T:22:0x0B:0x97 # Faded Rune T:23:0x0B:0x97 # Flashing Rune T:24:0x0B:0x97 # Weird Rune T:25:0x0B:0x97 # Shimmering Rune T:26:0x0B:0x97 # Smelly Rune T:27:0x0B:0x97 # Intricate Rune T:28:0x0B:0x97 # Bright Rune T:29:0x0B:0x97 # Blurry Rune T:30:0x0B:0x97 # Spiral Rune T:31:0x0B:0x97 # Locked Door T:32:0x0E:0xC5 # Jammed Door T:33:0x07:0xC5 # General Store T:34:0x05:0xAB # Armoury T:35:0x0F:0xDA # Weapon Smiths T:36:0x02:0x13 # Temple T:37:0x01:0x80 # Alchemy Shop T:38:0x0A:0xA4 # Magic Shop T:39:0x04:0xA6 # Black Market T:40:0x08:0xAE # Home T:41:0x07:0xC5 zangband/lib/pref/graf-lsl.prf0000644000000000000000000000026610250356274015272 0ustar rootroot # # This file defines special attr/char mappings for use in "graphics" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # # Standard file %:graf-xxx.prf zangband/lib/pref/graf-mac.prf0000644000000000000000000000052010250356274015231 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: graf-mac.prf # # This file defines special attr/char mappings for use in "graphics" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # # Standard file ?:[EQU $GRAF old] %:graf-xxx.prf # New tiles ?:[EQU $GRAF new] %:graf-new.prf zangband/lib/pref/graf-new.prf0000644000000000000000000017064210250356274015277 0ustar rootroot# CVS: Last edit by $Author: antimatter $ on $Date: 2002/03/12 18:06:13 $ # File: graf-new.prf # # This file defines special attr/char mappings for use in "graphics" mode # with Adam Bolt's 16x16 tiles. # # By Robert Ruehlmann < rr9@angband.org > # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Special attr/char values ##### ## # Unused (@) ## S:0x00:0x00/0x40 ## S:0x01:0x01/0x40 ## S:0x02:0x02/0x40 ## S:0x03:0x03/0x40 ## S:0x04:0x04/0x40 ## S:0x05:0x05/0x40 ## S:0x06:0x06/0x40 ## S:0x07:0x07/0x40 ## S:0x08:0x08/0x40 ## S:0x09:0x09/0x40 ## S:0x0A:0x0A/0x40 ## S:0x0B:0x0B/0x40 ## S:0x0C:0x0C/0x40 ## S:0x0D:0x0D/0x40 ## S:0x0E:0x0E/0x40 ## S:0x0F:0x0F/0x40 ## # Unused (@) ## S:0x10:0x00/0x40 ## S:0x11:0x01/0x40 ## S:0x12:0x02/0x40 ## S:0x13:0x03/0x40 ## S:0x14:0x04/0x40 ## S:0x15:0x05/0x40 ## S:0x16:0x06/0x40 ## S:0x17:0x07/0x40 ## S:0x18:0x08/0x40 ## S:0x19:0x09/0x40 ## S:0x1A:0x0A/0x40 ## S:0x1B:0x0B/0x40 ## S:0x1C:0x0C/0x40 ## S:0x1D:0x0D/0x40 ## S:0x1E:0x0E/0x40 ## S:0x1F:0x0F/0x40 ## # Unused (@) ## S:0x20:0x00/0x40 ## S:0x21:0x01/0x40 ## S:0x22:0x02/0x40 ## S:0x23:0x03/0x40 ## S:0x24:0x04/0x40 ## S:0x25:0x05/0x40 ## S:0x26:0x06/0x40 ## S:0x27:0x07/0x40 ## S:0x28:0x08/0x40 ## S:0x29:0x09/0x40 ## S:0x2A:0x0A/0x40 ## S:0x2B:0x0B/0x40 ## S:0x2C:0x0C/0x40 ## S:0x2D:0x0D/0x40 ## S:0x2E:0x0E/0x40 ## S:0x2F:0x0F/0x40 # Spells (*) S:0x30:0x91/0x88 S:0x31:0x91/0x89 S:0x32:0x91/0x8A S:0x33:0x91/0x8B S:0x34:0x91/0x8C S:0x35:0x91/0x8D S:0x36:0x91/0x8E S:0x37:0x91/0x8F S:0x38:0x91/0x90 S:0x39:0x91/0x91 S:0x3A:0x91/0x92 S:0x3B:0x91/0x93 S:0x3C:0x91/0x94 S:0x3D:0x91/0x95 S:0x3E:0x91/0x96 S:0x3F:0x91/0x97 # Spells (|) S:0x40:0x8F/0x80 S:0x41:0x8F/0x84 S:0x42:0x8F/0x88 S:0x43:0x8F/0x8C S:0x44:0x8F/0x90 S:0x45:0x8F/0x94 S:0x46:0x8F/0x98 S:0x47:0x8F/0x9C S:0x48:0x90/0x80 S:0x49:0x90/0x84 S:0x4A:0x90/0x88 S:0x4B:0x90/0x8C S:0x4C:0x90/0x90 S:0x4D:0x90/0x94 S:0x4E:0x90/0x98 S:0x4F:0x90/0x9C # Spells (-) S:0x50:0x8F/0x81 S:0x51:0x8F/0x85 S:0x52:0x8F/0x89 S:0x53:0x8F/0x8D S:0x54:0x8F/0x91 S:0x55:0x8F/0x95 S:0x56:0x8F/0x99 S:0x57:0x8F/0x9D S:0x58:0x90/0x81 S:0x59:0x90/0x85 S:0x5A:0x90/0x89 S:0x5B:0x90/0x8D S:0x5C:0x90/0x91 S:0x5D:0x90/0x95 S:0x5E:0x90/0x99 S:0x5F:0x90/0x9D # Spells (/) S:0x60:0x8F/0x82 S:0x61:0x8F/0x86 S:0x62:0x8F/0x8A S:0x63:0x8F/0x8E S:0x64:0x8F/0x92 S:0x65:0x8F/0x96 S:0x66:0x8F/0x9A S:0x67:0x8F/0x9E S:0x68:0x90/0x82 S:0x69:0x90/0x86 S:0x6A:0x90/0x8A S:0x6B:0x90/0x8E S:0x6C:0x90/0x92 S:0x6D:0x90/0x96 S:0x6E:0x90/0x9A S:0x6F:0x90/0x9E # Spells (\) S:0x70:0x8F/0x83 S:0x71:0x8F/0x87 S:0x72:0x8F/0x8B S:0x73:0x8F/0x8F S:0x74:0x8F/0x93 S:0x75:0x8F/0x97 S:0x76:0x8F/0x9B S:0x77:0x8F/0x9F S:0x78:0x90/0x83 S:0x79:0x90/0x87 S:0x7A:0x90/0x8B S:0x7B:0x90/0x8F S:0x7C:0x90/0x93 S:0x7D:0x90/0x97 S:0x7E:0x90/0x9B S:0x7F:0x90/0x9F # Amulets (") S:0x80:0x87/0x87 S:0x81:0x87/0x80 S:0x82:0x87/0x88 S:0x83:0x87/0x82 S:0x84:0x87/0x83 S:0x85:0x87/0x84 S:0x86:0x87/0x85 S:0x87:0x87/0x86 S:0x88:0x87/0x81 S:0x89:0x87/0x81 S:0x8A:0x87/0x89 S:0x8B:0x87/0x8A S:0x8C:0x87/0x8B S:0x8D:0x87/0x8C S:0x8E:0x87/0x8D S:0x8F:0x87/0x8E # Rings (=) S:0x90:0x84/0x87 S:0x91:0x84/0x80 S:0x92:0x84/0x88 S:0x93:0x84/0x82 S:0x94:0x84/0x83 S:0x95:0x84/0x84 S:0x96:0x84/0x85 S:0x97:0x84/0x86 S:0x98:0x84/0x81 S:0x99:0x84/0x81 S:0x9A:0x84/0x89 S:0x9B:0x84/0x8A S:0x9C:0x84/0x8B S:0x9D:0x84/0x8C S:0x9E:0x84/0x8D S:0x9F:0x84/0x8E # Staffs (_) S:0xA0:0x87/0x96 S:0xA1:0x87/0x95 S:0xA2:0x87/0x95 S:0xA3:0x87/0x92 S:0xA4:0x87/0x92 S:0xA5:0x87/0x93 S:0xA6:0x87/0x95 S:0xA7:0x87/0x90 S:0xA8:0x87/0x95 S:0xA9:0x87/0x95 S:0xAA:0x87/0x92 S:0xAB:0x87/0x94 S:0xAC:0x87/0x92 S:0xAD:0x87/0x93 S:0xAE:0x87/0x96 S:0xAF:0x87/0x90 # Wands (-) S:0xB0:0x86/0x97 S:0xB1:0x86/0x90 S:0xB2:0x86/0x98 S:0xB3:0x86/0x92 S:0xB4:0x86/0x93 S:0xB5:0x86/0x94 S:0xB6:0x86/0x95 S:0xB7:0x86/0x96 S:0xB8:0x86/0x91 S:0xB9:0x86/0x91 S:0xBA:0x86/0x99 S:0xBB:0x86/0x9A S:0xBC:0x86/0x9B S:0xBD:0x86/0x9C S:0xBE:0x86/0x9D S:0xBF:0x86/0x9E # Rods (-) S:0xC0:0x86/0x87 S:0xC1:0x86/0x80 S:0xC2:0x86/0x88 S:0xC3:0x86/0x82 S:0xC4:0x86/0x83 S:0xC5:0x86/0x84 S:0xC6:0x86/0x85 S:0xC7:0x86/0x86 S:0xC8:0x86/0x81 S:0xC9:0x86/0x81 S:0xCA:0x86/0x89 S:0xCB:0x86/0x8A S:0xCC:0x86/0x8B S:0xCD:0x86/0x8C S:0xCE:0x86/0x8D S:0xCF:0x86/0x8E # Scrolls (?) S:0xD0:0x83/0x9C S:0xD1:0x83/0x9D S:0xD2:0x83/0x9E S:0xD3:0x83/0x9F S:0xD4:0x83/0x9C S:0xD5:0x83/0x9D S:0xD6:0x83/0x9E S:0xD7:0x83/0x9F S:0xD8:0x83/0x9C S:0xD9:0x83/0x9D S:0xDA:0x83/0x9E S:0xDB:0x83/0x9F S:0xDC:0x83/0x9C S:0xDD:0x83/0x9D S:0xDE:0x83/0x9E S:0xDF:0x83/0x9F # Potions (!) S:0xE0:0x85/0x87 S:0xE1:0x85/0x80 S:0xE2:0x85/0x88 S:0xE3:0x85/0x82 S:0xE4:0x85/0x83 S:0xE5:0x85/0x84 S:0xE6:0x85/0x85 S:0xE7:0x85/0x86 S:0xE8:0x85/0x81 S:0xE9:0x85/0x81 S:0xEA:0x85/0x89 S:0xEB:0x85/0x8A S:0xEC:0x85/0x8B S:0xED:0x85/0x8C S:0xEE:0x85/0x8D S:0xEF:0x85/0x8E # Food (,) S:0xF0:0x85/0x97 S:0xF1:0x85/0x90 S:0xF2:0x85/0x98 S:0xF3:0x85/0x92 S:0xF4:0x85/0x93 S:0xF5:0x85/0x94 S:0xF6:0x85/0x95 S:0xF7:0x85/0x96 S:0xF8:0x85/0x91 S:0xF9:0x85/0x91 S:0xFA:0x85/0x99 S:0xFB:0x85/0x9A S:0xFC:0x85/0x9B S:0xFD:0x85/0x9C S:0xFE:0x85/0x9D S:0xFF:0x85/0x9E ##### Feature attr/char definitions ##### # nothing F:0:0x80/0x80 # open floor F:1:0x80/0x81 # invisible trap (drawn as open floor) F:2:0x80/0x81 # glyph of warding F:3:0x8D/0x95 # open door F:4:0x82/0x84 # broken door F:5:0x82/0x85 # up staircase F:6:0x80/0x96 # down staircase F:7:0x80/0x99 # Sand F:8:0xBB/0x9D # salt F:9:0xBC/0x85 # Wet mud F:10:0xBB/0x9A # Dry mud F:11:0xBB/0x97 # Tiled floor F:12:0xBB/0x85 # Wooden floor F:13:0xBB/0x88 # Pebbles F:14:0xBB/0x8B # solidified lava F:15:0xBD/0x94 # visible trap -- trap door F:16:0x81/0x8C # visible trap -- open pit F:17:0x81/0x89 # visible trap -- spiked pit F:18:0x81/0x89 # visible trap -- poison pit F:19:0x81/0x89 # visible trap -- rune -- summon F:20:0x81/0x8F # visible trap -- rune -- teleport F:21:0x81/0x92 # visible trap -- spot -- fire F:22:0x81/0x86 # visible trap -- spot -- acid F:23:0x81/0x86 # visible trap -- dart -- slow F:24:0x81/0x80 # visible trap -- dart -- lose str F:25:0x81/0x80 # visible trap -- dart -- lose dex F:26:0x81/0x80 # visible trap -- dart -- lose con F:27:0x81/0x80 # visible trap -- gas -- blind F:28:0x81/0x83 # visible trap -- gas -- confuse F:29:0x81/0x83 # visible trap -- gas -- poison F:30:0x81/0x83 # visible trap -- gas -- sleep F:31:0x81/0x83 # closed door F:32:0x82/0x83 # Pillar F:33:0xBB/0x82 # secret door (drawn as granite wall) F:48:0x80/0x84 # rubble F:49:0x80/0x9C # magma vein F:50:0x80/0x8D # quartz vein F:51:0x80/0x87 # magma vein + hidden treasure F:52:0x80/0x90 # quartz vein + hidden treasure F:53:0x80/0x87 # magma vein with treasure F:54:0x80/0x90 # quartz vein with treasure F:55:0x80/0x8A # granite wall -- basic F:56:0x80/0x84 # granite wall -- inner F:57:0x80/0x84 # granite wall -- outer F:58:0x80/0x84 # granite wall -- solid F:59:0x80/0x84 # permanent wall -- basic (perm) F:60:0x80/0x93 # permanent wall -- inner (perm) F:61:0x80/0x93 # permanent wall -- outer (perm) F:62:0x80/0x93 # permanent wall -- solid (perm) F:63:0x80/0x93 # explosive rune F:64:0x83/0x9A # section of the Pattern F:65:0x81/0x95 # section of the Pattern F:66:0x81/0x95 # section of the Pattern F:67:0x81/0x95 # section of the Pattern F:68:0x81/0x95 # section of the Pattern F:69:0x81/0x95 # section of the Pattern F:70:0x81/0x95 # section of the Pattern (discharged) F:71:0x81/0x98 # Pattern exit F:72:0x81/0x9B # corrupted section of the Pattern (Is this right? I don't think it is. -SF-) F:73:0x81/0x9E # General Store F:74:0x82/0x87 # Armoury F:75:0x82/0x88 # Weapon Smiths F:76:0x82/0x89 # Temple F:77:0x82/0x8A # Alchemy Shop F:78:0x82/0x8B # Magic Shop F:79:0x82/0x8C # Black Market F:80:0x82/0x8D # Home F:81:0x82/0x8E # Bookstore F:82:0x82/0x8F # Deep Water F:83:0x83/0x80 # Shallow Water F:84:0x83/0x83 # Deep Lava F:85:0xB6/0x9D # Shallow Lava F:86:0xB6/0x9A # Dark pit F:87:0xBD/0x97 # Dirt F:88:0xBB/0x94 # Grass F:89:0xBB/0x8E # compact rune (XXX - tile from teleport rune) F:90:0x81/0x92 # invisible wall F:91:0x80/0x81 # Ocean F:92:0xBC/0x8E # deep acid F:93:0xBC/0x8B # shallow acid F:94:0xBC/0x88 # submerged tree F:95:0xBC/0x91 # Trees F:96:0xBE/0x8C # Rock face F:97:0xBD/0x8E # Snow covered rock face F:98:0xBD/0x91 # boulder F:99:0xBE/0x80 # Pine tree F:100:0xBC/0x94 # Snow covered pine tree F:101:0xBC/0x97 # Obelisk F:102:0xBD/0x82 # Pillar F:103:0xBB/0x82 # stone fence F:112:0xBD/0x8B # Well F:113:0xBD/0x9A # Fountain F:114:0xBE/0x83 # Jungle F:115:0x82/0x9A # Bush F:128:0xBC/0x9D # dead bush F:129:0xBD/0x9D # Long grass F:130:0xBB/0x91 # Rock (on general terrain) F:131:0xBD/0x85 # Rock (on snow) F:132:0xBD/0x88 # Dead tree (on general terrain) F:133:0xBC/0x9A # Dead tree (on snow) F:134:0xBD/0x9D # Snow F:135:0xBC/0x82 # thick swamp F:136:0xBE/0x86 # swamp F:137:0xBE/0x89 ##### Object attr/char definitions ##### # something K:0:0x01/0x20 # Mushroom of Blindness K:1:0x85/0x94 # Mushroom of Paranoia K:2:0x85/0x94 # Mushroom of Confusion K:3:0x85/0x94 # Mushroom of Hallucination K:4:0x85/0x94 # Mushroom of Cure Poison K:5:0x85/0x94 # Mushroom of Cure Blindness K:6:0x85/0x94 # Mushroom of Cure Paranoia K:7:0x85/0x94 # Mushroom of Cure Confusion K:8:0x85/0x94 # Mushroom of Weakness K:9:0x85/0x94 # Mushroom of Unhealth K:10:0x85/0x94 # Mushroom of Restore Constitution K:11:0x85/0x94 # Mushroom of Restoring K:12:0x85/0x94 # Mushroom of Stupidity K:13:0x85/0x94 # Mushroom of Naivety K:14:0x85/0x94 # Mushroom of Poison K:15:0x85/0x94 # Mushroom of Sickness K:16:0x85/0x94 # Mushroom of Paralysis K:17:0x85/0x94 # Mushroom of Restore Strength K:18:0x85/0x94 # Mushroom of Disease K:19:0x85/0x94 # Mushroom of Cure Serious Wounds K:20:0x85/0x94 # & Ration~ of Food K:21:0x8E/0x84 # & Hard Biscuit~ K:22:0x8E/0x82 # & Strip~ of Beef Jerky K:23:0x8E/0x83 # & Slime Mold~ K:24:0x8E/0x85 # & Piece~ of Elvish Waybread K:25:0x8E/0x86 # & Pint~ of Fine Ale K:26:0x8E/0x80 # & Pint~ of Fine Wine K:27:0x8E/0x80 # No-dachi K:29:0xB5/0x89 # & Broken Dagger~ K:30:0x8A/0x8D # & Bastard Sword~ K:31:0x8A/0x8E # & Scimitar~ K:32:0x8A/0x97 # & Tulwar~ K:33:0x8A/0x95 # & Broad Sword~ K:34:0x8A/0x98 # & Short Sword~ K:35:0x8A/0x94 # & Blade~ of Chaos K:36:0x8A/0x9E # & Two-Handed Sword~ K:37:0x8A/0x9C # & Main Gauche~ K:38:0x8A/0x90 # & Cutlass~ K:39:0x8A/0x96 # & Executioner's Sword~ K:40:0x8A/0x9D # & Katana~ K:41:0x8A/0x9B # & Long Sword~ K:42:0x8A/0x99 # & Dagger~ K:43:0x8A/0x8F # & Rapier~ K:44:0x8A/0x91 # & Sabre~ K:45:0x8A/0x93 # & Small Sword~ K:46:0x8A/0x92 # & Broken Sword~ K:47:0x8A/0x8E # & Ball-and-Chain~ K:48:0x8B/0x86 # & Whip~ K:49:0x8A/0x9F # & Flail~ K:50:0x8B/0x83 # & Two-Handed Flail~ K:51:0x8B/0x87 # & Morning Star~ K:52:0x8B/0x84 # & Mace~ K:53:0x8B/0x81 # & Quarterstaff~ K:54:0x8B/0x82 # & War Hammer~ K:55:0x8B/0x80 # & Lead-Filled Mace~ K:56:0x8B/0x85 # & Mace~ of Disruption K:57:0x8B/0x88 # & Lucerne Hammer~ K:58:0x8B/0x8D # & Beaked Axe~ K:59:0x8B/0x90 # & Glaive~ K:60:0x8B/0x92 # & Halberd~ K:61:0x8B/0x93 # & Awl-Pike~ K:62:0x8B/0x8B # & Pike~ K:63:0x8B/0x8F # & Spear~ K:64:0x8B/0x89 # & Trident~ K:65:0x8B/0x8A # & Lance~ K:66:0x8B/0x8C # & Great Axe~ K:67:0x8B/0x95 # & Battle Axe~ K:68:0x8B/0x8E # & Lochaber Axe~ K:69:0x8B/0x94 # & Broad Axe~ K:70:0x8B/0x91 # & Scythe~ K:71:0x8B/0x96 # & Scythe~ of Slicing K:72:0x8B/0x97 # & Short Bow~ K:73:0x8B/0x98 # & Long Bow~ K:74:0x8B/0x99 # & Light Crossbow~ K:75:0x8B/0x9A # & Heavy Crossbow~ K:76:0x8B/0x9B # & Sling~ K:77:0x8B/0x9C # & Arrow~ K:78:0x8C/0x80 # & Seeker Arrow~ K:79:0x8C/0x81 # & Bolt~ K:80:0x8C/0x82 # Seeker Bolt K:81:0x8C/0x83 # Rounded Pebble K:82:0x8C/0x84 # Iron Shot K:83:0x8C/0x85 # Shovel K:84:0x8E/0x8F # Gnomish Shovel K:85:0x8E/0x90 # Dwarven Shovel K:86:0x8E/0x91 # Pick K:87:0x8E/0x8C # Orcish Pick K:88:0x8E/0x8D # Dwarven Pick K:89:0x8E/0x91 # Elven Cloak K:90:0x89/0x89 # Pair of Soft Leather Boots K:91:0x88/0x8E # Pair of Hard Leather Boots K:92:0x88/0x8F # Pair of Metal Shod Boots K:93:0x88/0x90 # Hard Leather Cap K:94:0x87/0x98 # Metal Cap K:95:0x87/0x99 # Iron Helm K:96:0x87/0x9A # Steel Helm K:97:0x87/0x9B # Iron Crown K:98:0x87/0x9C # Golden Crown K:99:0x87/0x9D # Jewel Encrusted Crown K:100:0x87/0x9E # Robe K:101:0x89/0x8C # Filthy Rag K:102:0x89/0x8B # Soft Leather Armour K:103:0x89/0x8D # Soft Studded Leather K:104:0x89/0x8E # Hard Leather Armour K:105:0x89/0x8F # Hard Studded Leather K:106:0x89/0x90 # Leather Scale Mail K:107:0x89/0x91 # Metal Scale Mail K:108:0x89/0x92 # Chain Mail K:109:0x89/0x94 # Rusty Chain Mail K:110:0x89/0x93 # Augmented Chain Mail K:111:0x89/0x96 # Bar Chain Mail K:112:0x89/0x97 # Metal Brigandine Armour K:113:0x89/0x98 # Partial Plate Armour K:114:0x89/0x99 # Metal Lamellar Armour K:115:0x89/0x9A # Full Plate Armour K:116:0x89/0x9B # Ribbed Plate Armour K:117:0x89/0x9C # Adamantite Plate Mail K:118:0x89/0x9F # Mithril Plate Mail K:119:0x89/0x9E # Mithril Chain Mail K:120:0x89/0x9D # Double Chain Mail K:121:0x89/0x95 # Shield of Deflection K:122:0x88/0x98 # Cloak K:123:0x89/0x88 # Shadow Cloak K:124:0x89/0x89 # Set of Leather Gloves K:125:0x88/0x91 # Set of Gauntlets K:126:0x88/0x92 # Set of Cesti K:127:0x88/0x93 # Small Leather Shield K:128:0x88/0x94 # Large Leather Shield K:129:0x88/0x95 # Small Metal Shield K:130:0x88/0x96 # Large Metal Shield K:131:0x88/0x97 # Ring of Strength K:132:0x84/0x81 # Ring of Dexterity K:133:0x84/0x83 # Ring of Constitution K:134:0x84/0x83 # Ring of Intelligence K:135:0x84/0x83 # Ring of Speed K:136:0x84/0x83 # Ring of Searching K:137:0x84/0x83 # Ring of Teleportation K:138:0x84/0x83 # Ring of Slow Digestion K:139:0x84/0x83 # Ring of Resist Fire K:140:0x84/0x83 # Ring of Resist Cold K:141:0x84/0x83 # Ring of Feather Falling K:142:0x84/0x83 # Ring of Poison Resistance K:143:0x84/0x83 # Ring of Free Action K:144:0x84/0x83 # Ring of Weakness K:145:0x84/0x83 # Ring of Flames K:146:0x84/0x83 # Ring of Acid K:147:0x84/0x83 # Ring of Ice K:148:0x84/0x83 # Ring of Woe K:149:0x84/0x83 # Ring of Stupidity K:150:0x84/0x83 # Ring of Damage K:151:0x84/0x83 # Ring of Accuracy K:152:0x84/0x83 # Ring of Protection K:153:0x84/0x83 # Ring of Aggravate Monster K:154:0x84/0x83 # Ring of See Invisible K:155:0x84/0x83 # Ring of Sustain Strength K:156:0x84/0x83 # Ring of Sustain Intelligence K:157:0x84/0x83 # Ring of Sustain Wisdom K:158:0x84/0x83 # Ring of Sustain Constitution K:159:0x84/0x83 # Ring of Sustain Dexterity K:160:0x84/0x83 # Ring of Sustain Charisma K:161:0x84/0x83 # Ring of Slaying K:162:0x84/0x83 # Amulet of Wisdom K:163:0x87/0x83 # Amulet of Charisma K:164:0x87/0x83 # Amulet of Searching K:165:0x87/0x83 # Amulet of Teleportation K:166:0x87/0x83 # Amulet of Slow Digestion K:167:0x87/0x83 # Amulet of Resist Acid K:168:0x87/0x83 # Amulet of Adornment K:169:0x87/0x83 # Amulet of the Magi K:171:0x87/0x83 # Amulet of DOOM K:172:0x87/0x83 # Scroll of Enchant Weapon To-Hit K:173:0x83/0x9C # Scroll of Enchant Weapon To-Dam K:174:0x83/0x9C # Scroll of Enchant Armor K:175:0x83/0x9C # Scroll of Identify K:176:0x83/0x9C # Scroll of *Identify* K:177:0x83/0x9C # Scroll of Rumour K:178:0x83/0x9C # Scroll of Logrus K:179:0x83/0x9C # Scroll of Remove Curse K:180:0x83/0x9C # Scroll of Light K:181:0x83/0x9C # Scroll of Fire K:182:0x83/0x9C # Scroll of Ice K:183:0x83/0x9C # Scroll of Summon Monster K:184:0x83/0x9C # Scroll of Phase Door K:185:0x83/0x9C # Scroll of Teleport K:186:0x83/0x9C # Scroll of Teleport Level K:187:0x83/0x9C # Scroll of Monster Confusion K:188:0x83/0x9C # Scroll of Magic Mapping K:189:0x83/0x9C # Scroll of Rune of Protection K:190:0x83/0x9C # Scroll of *Remove Curse* K:191:0x83/0x9C # Scroll of Treasure Detection K:192:0x83/0x9C # Scroll of Object Detection K:193:0x83/0x9C # Scroll of Trap Detection K:194:0x83/0x9C # Sheaf Arrow K:195:0xB6/0x91 # Mithril Shot K:196:0xB6/0x8F # Scroll of Door/Stair Location K:197:0x83/0x9C # Scroll of Acquirement K:198:0x83/0x9C # Scroll of *Acquirement* K:199:0x83/0x9C # Scroll of Mass Genocide K:200:0x83/0x9C # Scroll of Detect Invisible K:201:0x83/0x9C # Scroll of Aggravate Monster K:202:0x83/0x9C # Scroll of Trap Creation K:203:0x83/0x9C # Scroll of Trap/Door Destruction K:204:0x83/0x9C # Scroll of Artifact Creation K:205:0x83/0x9C # Scroll of Recharging K:206:0x83/0x9C # Scroll of Genocide K:207:0x83/0x9C # Scroll of Darkness K:208:0x83/0x9C # Scroll of Protection from Evil K:209:0x83/0x9C # Scroll of Satisfy Hunger K:210:0x83/0x9C # Scroll of Dispel Undead K:211:0x83/0x9C # Scroll of *Enchant Weapon* K:212:0x83/0x9C # Scroll of Curse Weapon K:213:0x83/0x9C # Scroll of *Enchant Armor* K:214:0x83/0x9C # Scroll of Curse Armor K:215:0x83/0x9C # Scroll of Summon Undead K:216:0x83/0x9C # Scroll of Blessing K:217:0x83/0x9C # Scroll of Holy Chant K:218:0x83/0x9C # Scroll of Holy Prayer K:219:0x83/0x9C # Scroll of Word of Recall K:220:0x83/0x9C # Scroll of *Destruction* K:221:0x83/0x9C # Potion of Slime Mold Juice K:222:0x85/0x85 # Potion of Apple Juice K:223:0x85/0x85 # Potion of Water K:224:0x85/0x85 # Potion of Strength K:225:0x85/0x85 # Potion of Weakness K:226:0x85/0x85 # Potion of Restore Strength K:227:0x85/0x85 # Potion of Intelligence K:228:0x85/0x85 # Potion of Stupidity K:229:0x85/0x85 # Potion of Restore Intelligence K:230:0x85/0x85 # Potion of Wisdom K:231:0x85/0x85 # Potion of Naivety K:232:0x85/0x85 # Potion of Restore Wisdom K:233:0x85/0x85 # Potion of Charisma K:234:0x85/0x85 # Potion of Ugliness K:235:0x85/0x85 # Potion of Restore Charisma K:236:0x85/0x85 # Potion of Curing K:237:0x85/0x85 # Potion of Invulnerability K:238:0x85/0x85 # Potion of New Life K:239:0x85/0x85 # Potion of Cure Serious Wounds K:240:0x85/0x85 # Potion of Cure Critical Wounds K:241:0x85/0x85 # Potion of Healing K:242:0x85/0x85 # Potion of Constitution K:243:0x85/0x85 # Potion of Experience K:244:0x85/0x85 # Potion of Sleep K:245:0x85/0x85 # Potion of Blindness K:246:0x85/0x85 # Potion of Booze K:247:0x85/0x85 # Potion of Poison K:248:0x85/0x85 # Potion of Speed K:249:0x85/0x85 # Potion of Slowness K:250:0x85/0x85 # Potion of Dexterity K:251:0x85/0x85 # Potion of Restore Dexterity K:252:0x85/0x85 # Potion of Restore Constitution K:253:0x85/0x85 # Potion of Lose Memories K:254:0x85/0x85 # Potion of Salt Water K:255:0x85/0x85 # Potion of Enlightenment K:256:0x85/0x85 # Potion of Heroism K:257:0x85/0x85 # Potion of Berserk Strength K:258:0x85/0x85 # Potion of Boldness K:259:0x85/0x85 # Potion of Restore Life Levels K:260:0x85/0x85 # Potion of Resist Heat K:261:0x85/0x85 # Potion of Resist Cold K:262:0x85/0x85 # Potion of Detect Invisible K:263:0x85/0x85 # Potion of Slow Poison K:264:0x85/0x85 # Potion of Neutralize Poison K:265:0x85/0x85 # Potion of Restore Mana K:266:0x85/0x85 # Potion of Infra-vision K:267:0x85/0x85 # Potion of Resistance K:268:0x85/0x85 # Wand of Light K:269:0x86/0x93 # Wand of Lightning Bolts K:270:0x86/0x93 # Wand of Frost Bolts K:271:0x86/0x93 # Wand of Fire Bolts K:272:0x86/0x93 # Wand of Stone to Mud K:273:0x86/0x93 # Wand of Polymorph K:274:0x86/0x93 # Wand of Heal Monster K:275:0x86/0x93 # Wand of Haste Monster K:276:0x86/0x93 # Wand of Slow Monster K:277:0x86/0x93 # Wand of Confuse Monster K:278:0x86/0x93 # Wand of Sleep Monster K:279:0x86/0x93 # Wand of Drain Life K:280:0x86/0x93 # Wand of Trap/Door Destruction K:281:0x86/0x93 # Wand of Magic Missile K:282:0x86/0x93 # Wand of Clone Monster K:283:0x86/0x93 # Wand of Scare Monster K:284:0x86/0x93 # Wand of Teleport Away K:285:0x86/0x93 # Wand of Disarming K:286:0x86/0x93 # Wand of Lightning Balls K:287:0x86/0x93 # Wand of Cold Balls K:288:0x86/0x93 # Wand of Fire Balls K:289:0x86/0x93 # Wand of Stinking Cloud K:290:0x86/0x93 # Wand of Acid Balls K:291:0x86/0x93 # Wand of Wonder K:292:0x86/0x93 # Flight Arrow K:293:0xB6/0x90 # Wand of Acid Bolts K:294:0x86/0x93 # Wand of Dragon's Flame K:295:0x86/0x93 # Wand of Dragon's Frost K:296:0x86/0x93 # Wand of Dragon's Breath K:297:0x86/0x93 # Wand of Annihilation K:298:0x86/0x93 # Wand of Rockets K:299:0x86/0x93 # Staff of Trap Location K:300:0x87/0x92 # Staff of Treasure Location K:301:0x87/0x92 # Staff of Object Location K:302:0x87/0x92 # Staff of Teleportation K:303:0x87/0x92 # Staff of Earthquakes K:304:0x87/0x92 # Staff of Summoning K:305:0x87/0x92 # Staff of Light K:306:0x87/0x92 # Staff of *Destruction* K:307:0x87/0x92 # Staff of Starlight K:308:0x87/0x92 # Staff of Haste Monsters K:309:0x87/0x92 # Staff of Slow Monsters K:310:0x87/0x92 # Staff of Sleep Monsters K:311:0x87/0x92 # Staff of Cure Light Wounds K:312:0x87/0x92 # Staff of Detect Invisible K:313:0x87/0x92 # Staff of Speed K:314:0x87/0x92 # Staff of Slowness K:315:0x87/0x92 # Staff of Door/Stair Location K:316:0x87/0x92 # Staff of Remove Curse K:317:0x87/0x92 # Staff of Detect Evil K:318:0x87/0x92 # Staff of Curing K:319:0x87/0x92 # Staff of Dispel Evil K:320:0x87/0x92 # Staff of Probing K:321:0x87/0x92 # Staff of Darkness K:322:0x87/0x92 # Staff of Genocide K:323:0x87/0x92 # Staff of Power K:324:0x87/0x92 # Staff of the Magi K:325:0x87/0x92 # Staff of Perception K:326:0x87/0x92 # Staff of Holiness K:327:0x87/0x92 # Staff of Enlightenment K:328:0x87/0x92 # Staff of Healing K:329:0x87/0x92 # [Book of Common Prayer] K:330:0x8D/0x84 # [High Mass] K:331:0x8D/0x85 # [Book of the Unicorn] K:332:0x8D/0x86 # [Blessings of the Grail] K:333:0x8D/0x87 # [Beginner's Handbook] K:334:0x8C/0x9C # [Master Sorcerer's Handbook] K:335:0x8C/0x9D # [Pattern Sorcery] K:336:0x8C/0x9E # [Grimoire of Power] K:337:0x8C/0x9F # Small wooden chest K:338:0x84/0x99 # Large wooden chest K:339:0x84/0x9A # Small iron chest K:340:0x84/0x9B # Large iron chest K:341:0x84/0x9C # Small steel chest K:342:0x84/0x9D # Large steel chest K:343:0x84/0x9E # Ruined chest K:344:0x84/0x9F # Iron Spike K:345:0x8E/0x89 # Wooden Torch K:346:0x8E/0x8B # Brass Lantern K:347:0x8E/0x8A # Flask of oil K:348:0x8E/0x88 # Empty Bottle K:349:0x8E/0x87 # Rod of Havoc K:350:0x86/0x83 # Rod of Door/Stair Location K:351:0x86/0x83 # Rod of Trap Location K:352:0x86/0x83 # Rod of Probing K:353:0x86/0x83 # Rod of Recall K:354:0x86/0x83 # Rod of Illumination K:355:0x86/0x83 # Rod of Light K:356:0x86/0x83 # Rod of Lightning Bolts K:357:0x86/0x83 # Rod of Frost Bolts K:358:0x86/0x83 # Rod of Fire Bolts K:359:0x86/0x83 # Rod of Polymorph K:360:0x86/0x83 # Rod of Slow Monster K:361:0x86/0x83 # Rod of Sleep Monster K:362:0x86/0x83 # Rod of Drain Life K:363:0x86/0x83 # Rod of Teleport Other K:364:0x86/0x83 # Rod of Disarming K:365:0x86/0x83 # Rod of Lightning Balls K:366:0x86/0x83 # Rod of Cold Balls K:367:0x86/0x83 # Rod of Fire Balls K:368:0x86/0x83 # Rod of Acid Balls K:369:0x86/0x83 # Rod of Acid Bolts K:370:0x86/0x83 # Rod of Enlightenment K:371:0x86/0x83 # Rod of Perception K:372:0x86/0x83 # Rod of Curing K:373:0x86/0x83 # Rod of Healing K:374:0x86/0x83 # Rod of Detection K:375:0x86/0x83 # Rod of Restoration K:376:0x86/0x83 # Rod of Speed K:377:0x86/0x83 # [Call of the Wild] K:379:0x8D/0x80 # [Nature Mastery] K:380:0x8D/0x81 # [Nature's Gifts] K:381:0x8D/0x82 # [Nature's Wrath] K:382:0x8D/0x83 # [Sign of Chaos] K:383:0x8C/0x98 # [Chaos Mastery] K:384:0x8C/0x99 # [Chaos Channels] K:385:0x8C/0x9A # [Armageddon Tome] K:386:0x8C/0x9B # [Black Prayers] K:387:0x8D/0x88 # [Black Mass] K:388:0x8D/0x89 # Shard of Pottery K:389:0x8E/0x92 # Broken Stick K:390:0x8E/0x93 # Broken Skull K:391:0x8E/0x94 # Broken Bone K:392:0x8E/0x95 # Canine Skeleton K:393:0x8E/0x9A # Rodent Skeleton K:394:0x8E/0x9B # Human Skeleton K:395:0x8E/0x96 # Dwarf Skeleton K:396:0x8E/0x98 # Elf Skeleton K:397:0x8E/0x97 # Gnome Skeleton K:398:0x8E/0x99 # Great Hammer K:399:0xB5/0x8A # Black Dragon Scale Mail K:400:0x8A/0x82 # Blue Dragon Scale Mail K:401:0x8A/0x80 # White Dragon Scale Mail K:402:0x8A/0x81 # Red Dragon Scale Mail K:403:0x8A/0x83 # Green Dragon Scale Mail K:404:0x8A/0x84 # Multi-Hued Dragon Scale Mail K:405:0x8A/0x8B # Pseudo Dragon Scale Mail K:406:0x8A/0x87 # Law Dragon Scale Mail K:407:0x8A/0x89 # Bronze Dragon Scale Mail K:408:0x8A/0x85 # Gold Dragon Scale Mail K:409:0x8A/0x86 # Chaos Dragon Scale Mail K:410:0x8A/0x88 # Balance Dragon Scale Mail K:411:0x8A/0x8A # Power Dragon Scale Mail K:412:0x8A/0x8C # Dragon Helm K:413:0x88/0x82 # Dragon Shield K:414:0x88/0x9C # Potion of Death K:415:0x85/0x85 # Potion of Ruination K:416:0x85/0x85 # Potion of Detonations K:417:0x85/0x85 # Potion of Augmentation K:418:0x85/0x85 # Potion of *Healing* K:419:0x85/0x85 # Potion of Life K:420:0x85/0x85 # Potion of Self Knowledge K:421:0x85/0x85 # Potion of *Enlightenment* K:422:0x85/0x85 # [Black Channels] K:423:0x8D/0x8A # [Necronomicon] K:424:0x8D/0x8B # Ring of Fear Resistance K:425:0x84/0x83 # Ring of Light and Darkness Resistance K:426:0x84/0x83 # Ring of Nether Resistance K:427:0x84/0x83 # Ring of Nexus Resistance K:428:0x84/0x83 # Ring of Sound Resistance K:429:0x84/0x83 # Ring of Confusion Resistance K:430:0x84/0x83 # Ring of Shard Resistance K:431:0x84/0x83 # Ring of Disenchantment Resistance K:432:0x84/0x83 # Ring of Chaos Resistance K:433:0x84/0x83 # Ring of Blindness Resistance K:434:0x84/0x83 # Ring of Lordly Protection K:435:0x84/0x83 # Ring of Extra Attacks K:436:0x84/0x83 # Potion of Cure Light Wounds K:437:0x85/0x85 # Potion of Clumsiness K:438:0x85/0x85 # Potion of Sickliness K:439:0x85/0x85 # copper K:480:0x83/0x91 # copper K:481:0x83/0x91 # copper K:482:0x83/0x91 # silver K:483:0x83/0x92 # silver K:484:0x83/0x92 # silver K:485:0x83/0x92 # garnets K:486:0x83/0x96 # garnets K:487:0x83/0x96 # gold K:488:0x83/0x93 # gold K:489:0x83/0x93 # gold K:490:0x83/0x93 # opals K:491:0x83/0x97 # sapphires K:492:0x83/0x98 # rubies K:493:0x83/0x99 # diamonds K:494:0x83/0x9A # emeralds K:495:0x83/0x9B # mithril K:496:0x83/0x94 # adamantite K:497:0x83/0x95 # Mighty Hammer K:498:0x88/0x9E # Massive Iron Crown (Grond) K:499:0x8B/0x9E # Phial of Galadriel K:500:0x8E/0x9D # Star of Elendil K:501:0x8E/0x9E # Jewel of Judgement K:502:0x8E/0x9F # The Amulet of Carlammas K:503:0x84/0x96 # The Amulet of Ingwe K:504:0x84/0x97 # The Necklace of the Dwarves K:505:0x84/0x98 # The Ring of Barahir K:506:0x84/0x8F # The Ring of the Tulkas K:507:0x84/0x90 # Narya K:508:0x84/0x92 # Nenya K:509:0x84/0x93 # Vilya K:510:0x84/0x94 # The One Ring K:511:0x84/0x95 # [Conjurings & Tricks] K:512:0x8D/0x90 # [Deck of Many Things] K:513:0x8D/0x91 # [Trumps of Doom] K:514:0x8D/0x92 # [Five Aces] K:515:0x8D/0x93 # [Cantrips for Beginners] K:516:0x8D/0x8C # [Minor Arcana] K:517:0x8D/0x8D # [Major Arcana] K:518:0x8D/0x8E # [Manual of Master] K:519:0x8D/0x8F # Amulet of Reflection K:520:0x87/0x83 # Amulet of Anti-Magic K:521:0x87/0x83 # Amulet of Anti-Teleportation K:522:0x87/0x83 # Amulet of Resistance K:523:0x87/0x83 # Zweihander K:524:0xB5/0x8C # Tanto K:525:0xB5/0x81 # Splint Mail K:526:0xB6/0x92 # Do-Maru K:527:0xB6/0x82 # Trifurcate spear K:528:0xB5/0x85 # Three Piece Rod K:529:0xB5/0x80 # O-yoroi K:530:0xB6/0x83 # Fur Cloak K:531:0xB6/0x8C # Lajatang K:532:0xB5/0x86 # Hatchet K:533:0xB5/0x8F # Rhino Hide Armour K:535:0xB6/0x88 # Leather Jacket K:536:0xB6/0x80 # Sickle K:537:0xB5/0x90 # Tetsubo K:538:0xB5/0x87 # Nunchaku K:539:0xB5/0x91 # Bo Staff K:540:0xB5/0x93 # Jo Staff K:541:0xB5/0x92 # Club K:542:0xB5/0x94 # Broad Spear K:543:0xB5/0x84 # Khopesh K:544:0xB5/0x94 # Flamberge K:545:0xB5/0x83 # Claymore K:546:0xB5/0x8D # Espadon K:547:0xB5/0x8E # Great Scimitar K:548:0xB5/0x8B # Wakizashi K:549:0xB5/0x98 # Naginata K:550:0xB5/0x88 # Fauchard K:551:0xB5/0x95 # Guisarme K:552:0xB5/0x96 # Heavy Lance K:553:0xB5/0x82 # Basillard K:554:0xB5/0x99 # Ninjato K:555:0xB5/0x97 # Ring Mail K:556:0xB6/0x93 # Cord Armour K:557:0xB6/0x8B # Paper Armour K:558:0xB6/0x8A # Padded Armour K:559:0xB6/0x89 # Kabuto K:560:0xB6/0x84 # Stone and Hide Armour K:561:0xB6/0x8D # Jingasa K:562:0xB6/0x85 # Haramakido K:563:0xB6/0x81 # Incandescent Light of Edison K:564:0x8D/0x9E # Diamond Edge K:565:0xB5/0x9B # Scroll of Mundanity K:566:0x83/0x9C # Magical Figurine K:567:0xBF/0x86 # Wooden statue K:568:0xBF/0x87 # Clay statue K:569:0xBF/0x88 # Stone statue K:570:0xBF/0x89 # Iron statue K:571:0xBF/0x8A # Copper statue K:572:0xBF/0x8B # Silver statue K:573:0xBF/0x8C # Golden statue K:574:0xBF/0x8D # Ivory statue K:575:0xBF/0x8E # Mithril statue K:576:0xBF/0x8F # Ornate statue K:577:0xBF/0x90 # blank #K:578 # blank #K:579 # T-shirt K:580:0x8B/0x9D # Steel Bolt K:581:0xB6/0x8E # Elfblade K:590:0xB5/0x9C ##### Monster attr/char definitions ##### # Player R:0:0x92/0x80 # Filthy street urchin R:1:0xAA/0x80 # Scrawny cat R:2:0xA7/0x82 # Sparrow R:3:0xB7/0x80 # Chaffinch R:4:0xB7/0x81 # Wild rabbit R:5:0xB7/0x82 # Woodsman R:6:0xB7/0x83 # Scruffy little dog R:7:0x9D/0x9A # Farmer Maggot R:8:0xAA/0x81 # Blubbering idiot R:9:0xAA/0x82 # Hobo R:10:0xAA/0x83 # Raving lunatic R:11:0xAA/0x84 # Pitiful looking beggar R:12:0xAA/0x85 # Mangy looking leper R:13:0xAA/0x86 # Agent of black market R:14:0xAA/0x87 # Singing, happy drunk R:15:0xAA/0x88 # Aimless looking merchant R:16:0xAA/0x89 # Mean looking mercenary R:17:0xAA/0x8A # Battle scarred veteran R:18:0xAA/0x8B # Martti Ihrasaari R:19:0xB0/0x80 # Grey mold R:20:0xA8/0x9F # Large white snake R:21:0xA2/0x85 # Blinking dot R:22:0xB0/0x81 # Newt R:23:0xB0/0x82 # Giant white centipede R:24:0xA5/0x95 # White icky thing R:25:0xA8/0x83 # Clear icky thing R:26:0xA8/0x84 # Giant white mouse R:27:0xAC/0x85 # Large brown snake R:28:0xA2/0x84 # Small kobold R:29:0xA8/0x99 # Kobold R:30:0xA8/0x9A # White worm mass R:31:0xAC/0x9D # Floating eye R:32:0xA6/0x9B # Rock lizard R:33:0xA2/0x86 # Grid Bug R:34:0xB0/0x84 # Jackal R:35:0x9D/0x9B # Soldier ant R:36:0xA5/0x87 # Fruit bat R:37:0xA5/0x8F # Insect swarm R:38:0xB7/0x84 # Greater hell-beast R:39:0xB0/0x83 # Shrieker mushroom patch R:40:0x9D/0x86 # Blubbering icky thing R:41:0xA8/0x85 # Metallic green centipede R:42:0xA5/0x96 # Novice warrior R:43:0xAA/0x8C # Novice rogue R:44:0xAA/0x8D # Novice priest R:45:0xAA/0x8E # Novice mage R:46:0xAA/0x8F # Yellow mushroom patch R:47:0x9D/0x87 # White jelly R:48:0xA8/0x8A # Giant black ant R:49:0xA5/0x88 # Salamander R:50:0xA2/0x88 # White harpy R:51:0xA0/0x88 # Blue yeek R:52:0xAD/0x87 # Grip, Farmer Maggot's dog R:53:0x9D/0x9C # Wolf, Farmer Maggot's dog R:54:0xB7/0x85 # Fang, Farmer Maggot's dog R:55:0x9D/0x9D # Giant green frog R:56:0xA2/0x87 # Freesia R:57:0xB0/0x85 # Green worm mass R:58:0xAC/0x9E # Large yellow snake R:59:0xA2/0x89 # Cave spider R:60:0xA2/0x9D # Crow R:61:0xB7/0x86 # Wild cat R:62:0xA7/0x83 # Smeagol R:63:0xAA/0x90 # Green ooze R:64:0xA8/0x8B # Poltergeist R:65:0x9F/0x99 # Yellow jelly R:66:0xA8/0x8D # Metallic blue centipede R:67:0xA5/0x97 # Raven R:68:0xB7/0x87 # Giant white louse R:69:0xA8/0x9D # Piranha R:70:0xB7/0x88 # Black naga R:71:0xA9/0x88 # Spotted mushroom patch R:72:0x9D/0x88 # Silver jelly R:73:0xA8/0x8C # Scruffy looking hobbit R:74:0xA7/0x93 # Giant white ant R:75:0xA5/0x89 # Yellow mold R:76:0xA9/0x80 # Metallic red centipede R:77:0xA5/0x98 # Yellow worm mass R:78:0xAC/0x9F # Clear worm mass R:79:0xAD/0x80 # Radiation eye R:80:0xA6/0x9C # Yellow light R:81:0xB7/0x89 # Cave lizard R:82:0xA2/0x8A # Novice ranger R:83:0xAA/0x91 # Blue jelly R:84:0xA8/0x8E # Creeping copper coins R:85:0x9D/0x80 # Giant white rat R:86:0xAC/0x86 # Snotling R:87:0xB7/0x8A # Swordfish R:88:0xB7/0x8B # Blue worm mass R:89:0xAD/0x81 # Large grey snake R:90:0xA2/0x8B # Skeleton kobold R:91:0xAC/0x89 # Ewok R:92:0xB0/0x86 # Novice mage R:93:0xAA/0x8F # Green naga R:94:0xA9/0x89 # Giant leech R:95:0xB7/0x8C # Barracuda R:96:0xB7/0x8D # Novice paladin R:97:0xAA/0x92 # Zog R:98:0xB7/0x8E # Blue ooze R:99:0xA8/0x8F # Green glutton ghost R:100:0x9F/0x9A # Green jelly R:101:0xA8/0x90 # Large kobold R:102:0xA8/0x9B # Grey icky thing R:103:0xA8/0x86 # Disenchanter eye R:104:0xA6/0x9D # Red worm mass R:105:0xAD/0x82 # Copperhead snake R:106:0xA2/0x8C # Death sword R:107:0xB0/0x87 # Purple mushroom patch R:108:0x9D/0x89 # Novice priest R:109:0xAA/0x8E # Novice warrior R:110:0xAA/0x8C # Nibelung R:111:0xB0/0x88 # Disembodied hand that strangled people R:112:0xB0/0x89 # Brown mold R:113:0xA9/0x81 # Giant brown bat R:114:0xA5/0x90 # Rat-thing R:115:0xB7/0x8F # Novice archer R:116:0xAA/0x97 # Creeping silver coins R:117:0x9D/0x81 # Snaga R:118:0xA9/0x8E # Rattlesnake R:119:0xA2/0x8D # Giant slug R:120:0xB7/0x90 # Giant pink frog R:121:0xA2/0x8E # Dark elf R:122:0xA7/0x95 # Zombified kobold R:123:0xAD/0x8C # Crypt Creep R:124:0xB0/0x8A # Rotting corpse R:125:0xB0/0x8B # Cave orc R:126:0xA9/0x8F # Wood spider R:127:0xA2/0x9E # Manes R:128:0xA0/0x91 # Bloodshot eye R:129:0xA6/0x9E # Red naga R:130:0xA9/0x8A # Red jelly R:131:0xA8/0x91 # Green icky thing R:132:0xA8/0x87 # Lost soul R:133:0x9F/0x9B # Night lizard R:134:0xA2/0x8F # Mughash the Kobold Lord R:135:0xA8/0x9C # Skeleton orc R:136:0xAC/0x8A # Wormtongue, Agent of Saruman R:137:0xAA/0x98 # Robin Hood, the Outlaw R:138:0xB0/0x8C # Nurgling R:139:0xB7/0x91 # Lagduf, the Snaga R:140:0xA9/0x90 # Brown yeek R:141:0xAD/0x88 # Novice ranger R:142:0xAA/0x91 # Giant salamander R:143:0xA2/0x90 # Space monster R:144:0xB0:0x8D # Carnivorous flying monkey R:145:0xB7/0x92 # Green mold R:146:0xA9/0x82 # Novice paladin R:147:0xAA/0x92 # Lemure R:148:0xA0/0x92 # Hill orc R:149:0xA9/0x91 # Bandit R:150:0xAA/0x9B # Hunting hawk of Julian R:151:0xB0/0x8E # Phantom warrior R:152:0xB0/0x8F # Gremlin R:153:0xB0/0x90 # Yeti R:154:0xA4/0x91 # Bloodshot icky thing R:155:0xA8/0x88 # Giant grey rat R:156:0xAC/0x87 # Black harpy R:157:0xA0/0x89 # Skaven R:158:0xB7/0x93 # The wounded bear R:159:0xB7/0x94 # Portuguese man-o-war R:160:0xB7/0x95 # Rock mole R:161:0xB7/0x96 # Orc shaman R:162:0xA9/0x92 # Baby blue dragon R:163:0xA5/0x9D # Baby white dragon R:164:0xA5/0x9E # Baby green dragon R:165:0xA5/0x9F # Baby black dragon R:166:0xA6/0x80 # Baby red dragon R:167:0xA6/0x81 # Giant red ant R:168:0xA5/0x8D # Brodda, the Easterling R:169:0xAA/0x9C # Bloodfang the Wolf R:170:0xB7/0x97 # King cobra R:171:0xA2/0x91 # Eagle R:172:0xB7/0x98 # War bear R:173:0xB0/0x91 # Killer bee R:174:0xB0/0x92 # Giant spider R:175:0xA2/0x9F # Giant white tick R:176:0xAC/0x92 # The Borshin R:177:0xB7/0x99 # Dark elven mage R:178:0xA7/0x96 # Kamikaze yeek R:179:0xB7/0x9A # Orfax, Son of Boldor R:180:0xAD/0x89 # Servant of Glaaki R:181:0xB7/0x9B # Dark elven warrior R:182:0xA7/0x97 # Sand-dweller R:183:0xB7/0x9D # Clear mushroom patch R:184:0x9D/0x8A # Quiver slot R:185:0xB0/0x93 # Grishnakh, the Hill Orc R:186:0xA9/0x93 # Giant piranha R:187:0xB7/0x9E # Owlbear R:188:0xB7/0x9F # Blue horror R:189:0xB8/0x80 # Hairy mold R:190:0xA9/0x83 # Grizzly bear R:191:0xB8/0x81 # Disenchanter mold R:192:0xA9/0x84 # Pseudo dragon R:193:0xA6/0x82 # Tengu R:194:0xA0/0x93 # Creeping gold coins R:195:0x9D/0x82 # Wolf R:196:0x9D/0x9E # Giant fruit fly R:197:0x9F/0x91 # Panther R:198:0xA7/0x84 # Tax collector R:199:0xB0/0x94 # Hobbes the Tiger R:200:0xB0/0x95 # Shadow Creature of Fiona R:201:0xB0/0x96 # Undead mass R:202:0xB0/0x97 # Chaos shapechanger R:203:0xB0/0x98 # Baby multi-hued dragon R:204:0xA6/0x83 # Vorpal bunny R:205:0xB8/0x82 # Old Man Willow R:206:0xB8/0x83 # Hippocampus R:207:0xB8/0x84 # Zombified orc R:208:0xAD/0x8D # Hippogriff R:209:0xA0/0x8A # Black mamba R:210:0xA2/0x92 # White wolf R:211:0x9D/0x9F # Grape jelly R:212:0xA8/0x92 # Nether worm mass R:213:0xAD/0x83 # Abyss worm mass R:214:0xB0/0x99 # Golfimbul, the Hill Orc Chief R:215:0xA9/0x94 # Swordsman R:216:0xAA/0x9F # Skaven shaman R:217:0xB8/0x85 # Gazer R:218:0xB7/0x9C # Knight archer R:219:0xB8/0x86 # Ixitxachitl R:220:0xB8/0x87 # Mine-dog R:221:0xB8/0x88 # Hellcat R:222:0xB0/0x9A # Moon beast R:223:0xB0/0x9B # Master yeek R:224:0xAD/0x8A # Priest R:225:0xAA/0x9E # Dark elven priest R:226:0xA7/0x99 # Air spirit R:227:0x9E/0x9F # Skeleton human R:228:0xAC/0x8B # Zombified human R:229:0xAD/0x8E # Tiger R:230:0xA7/0x85 # Moaning spirit R:231:0x9F/0x9C # Frumious bandersnatch R:232:0xA5/0x99 # Spotted jelly R:233:0xA8/0x93 # Drider R:234:0xA3/0x80 # Mongbat R:235:0xB0/0x9C # Killer brown beetle R:236:0xA0/0x9B # Boldor, King of the Yeeks R:237:0xAD/0x8B # Ogre R:238:0xA1/0x8B # Creeping mithril coins R:239:0x9D/0x83 # Illusionist R:240:0xAB/0x80 # Druid R:241:0xAB/0x81 # Pink horror R:242:0xB8/0x89 # Cloaker R:243:0xB8/0x8A # Black orc R:244:0xA9/0x95 # Ochre jelly R:245:0xA8/0x94 # Software bug R:246:0xB0/0x9D # Lurker (rr9) R:247:0x80/0x81 # Nixie R:248:0xB8/0x8B # Vlasta R:249:0xB8/0x8C # Giant white dragon fly R:250:0x9F/0x93 # Snaga sapper R:251:0xB8/0x8D # Blue icky thing R:252:0xA8/0x89 # Gibbering mouther R:253:0xB0/0x9E # Irish wolfhound of Flora R:254:0xB0/0x9F # Hill giant R:255:0xA1/0x91 # Flesh golem R:256:0xA7/0x89 # Warg R:257:0x9E/0x80 # Cheerful leprechaun R:258:0xB1/0x80 # Giant flea R:259:0x9F/0x92 # Ufthak of Cirith Ungol R:260:0xA9/0x96 # Clay golem R:261:0xA7/0x8A # Black ogre R:262:0xA1/0x8C # Dweller on the threshold R:263:0xB8/0x8E # Half-orc R:264:0xA9/0x97 # Dark naga R:265:0xB8/0x8F # Giant octopus R:266:0xB8/0x90 # Magic mushroom patch R:267:0x9D/0x8B # Plaguebearer of Nurgle R:268:0xB8/0x91 # Guardian naga R:269:0xA9/0x8B # Wererat R:270:0xAC/0x88 # Light hound R:271:0xA4/0x93 # Shadow hound R:272:0xA4/0x94 # Flying skull R:273:0xB1/0x81 # Mi-Go R:274:0xB1/0x82 # Giant tarantula R:275:0xA3/0x81 # Giant clear centipede R:276:0xA5/0x9A # Mirkwood spider R:277:0xA3/0x82 # Frost giant R:278:0xA1/0x92 # Griffon R:279:0xA0/0x8B # Homonculous R:280:0xA0/0x94 # Gnome mage R:281:0xA7/0x98 # Clear hound R:282:0xA4/0x95 # Umber hulk R:283:0xA3/0x99 # Rust monster R:284:0xB8/0x92 # Orc captain R:285:0xA9/0x98 # Gelatinous cube R:286:0xA8/0x95 # Giant green dragon fly R:287:0x9F/0x94 # Fire giant R:288:0xA1/0x93 # Hummerhorn R:289:0x9F/0x96 # Lizardman R:290:0xB8/0x93 # Ulfast, Son of Ulfang R:291:0xAB/0x82 # Hammerhead R:292:0xB8/0x94 # Berserker R:293:0xB8/0x95 # Quasit R:294:0xA0/0x95 # Sphinx R:295:0xB8/0x96 # Imp R:296:0xA0/0x96 # Forest troll R:297:0xA3/0x89 # Freezing sphere R:298:0xB8/0x97 # Jumping fireball R:299:0xB8/0x98 # Ball lightning R:300:0xB8/0x99 # 2-headed hydra R:301:0xA2/0x93 # Swamp thing R:302:0xB8/0x9A # Water spirit R:303:0x9F/0x80 # Giant red scorpion R:304:0xA3/0x83 # Earth spirit R:305:0x9F/0x81 # Fire spirit R:306:0x9F/0x82 # Fire hound R:307:0xA4/0x96 # Cold hound R:308:0xA4/0x97 # Energy hound R:309:0xA4/0x98 # Mimic (potion) R:310:0x9D/0x8E # Door mimic R:311:0xB8/0x9B # Blink dog R:312:0x9E/0x81 # Uruk R:313:0xA9/0x99 # Shagrat, the Orc Captain R:314:0xA9/0x9A # Gorbag, the Orc Captain R:315:0xA9/0x9B # Shambling mound R:316:0x9D/0x8C # White shark R:317:0xB8/0x9C # Chaos beastman R:318:0xB8/0x9D # Daemonette of Slaanesh R:319:0xB8/0x9E # Giant bronze dragon fly R:320:0x9F/0x98 # Stone giant R:321:0xA1/0x94 # Giant black dragon fly R:322:0x9F/0x96 # Stone golem R:323:0xA7/0x8B # Red mold R:324:0xA9/0x85 # Giant gold dragon fly R:325:0x9F/0x97 # Stunwall R:326:0xB8/0x9F # Ghast R:327:0xB9/0x80 # Ixitxachitl priest R:328:0xB9/0x81 # Huorn R:329:0xB9/0x82 # Bolg, Son of Azog R:330:0xA9/0x9C # Phase spider R:331:0xA3/0x84 # Lizard king R:332:0xB9/0x83 # Landmine R:333:0xB9/0x84 # Wyvern R:334:0xB1/0x83 # Great eagle R:335:0xB9/0x85 # Livingstone R:336:0xB1/0x84 # Earth hound R:337:0xA4/0x99 # Air hound R:338:0xA4/0x9A # Sabre-tooth tiger R:339:0xA7/0x86 # Water hound R:340:0xA4/0x9B # Chimera R:341:0xA0/0x8C # Quylthulg R:342:0xA1/0x9A # Sasquatch R:343:0xA4/0x92 # Weir R:344:0xB1/0x85 # Whale R:345:0xB9/0x86 # Electric eel R:346:0xB9/0x87 # Werewolf R:347:0x9E/0x82 # Dark elven lord R:348:0xA7/0x9C # Cloud giant R:349:0xA1/0x96 # Ugluk, the Uruk R:350:0xA9/0x9D # Blue dragon bat R:351:0xA5/0x91 # Scroll mimic R:352:0x9D/0x8D # Chest mimic R:353:0xB9/0x88 # Fire vortex R:354:0xAC/0x94 # Water vortex R:355:0xAC/0x95 # Lugdush, the Uruk R:356:0xA9/0x9E # Arch-vile R:357:0xB9/0x89 # Cold vortex R:358:0xAC/0x96 # Energy vortex R:359:0xAC/0x97 # Globefish R:360:0xB9/0x8A # Carrion R:361:0xB9/0x8B # Mummified orc R:362:0xA1/0x88 # Killer whale R:363:0xB9/0x8C # Serpent man R:364:0xB9/0x8D # Vampiric mist R:365:0xB9/0x8E # Killer stag beetle R:366:0xA0/0x9D # Iron golem R:367:0xA7/0x8C # Auto-roller R:368:0xB1/0x86 # Giant yellow scorpion R:369:0xA3/0x85 # Jade monk R:370:0xB9/0x8F # Black ooze R:371:0xA8/0x96 # Hardened warrior R:372:0xAB/0x83 # Azog, King of the Uruk-Hai R:373:0xA9/0x9F # Fleshhound of Khorne R:374:0xB9/0x90 # Dark elven warlock R:375:0xB1/0x87 # Master rogue R:376:0xAB/0x84 # Red dragon bat R:377:0xA5/0x92 # Killer white beetle R:378:0xA0/0x9C # Ice skeleton R:379:0xB9/0x91 # Angamaite of Umbar R:380:0xAB/0x86 # Kouko R:381:0xB1/0x88 # Mime, the Nibelung R:382:0xB1/0x89 # Hagen, son of Alberich R:383:0xB1/0x8A # Meneldor the Swift R:384:0xB9/0x92 # Phantom beast R:385:0xB1/0x8B # Great white shark R:386:0xB9/0x93 # 4-headed hydra R:387:0xA2/0x95 # Lesser hell-beast R:388:0xB9/0x94 # Tyrannosaur R:389:0xB1/0x8C # Mummified human R:390:0xA1/0x89 # Vampire bat R:391:0xA5/0x93 # Sangahyando of Umbar R:392:0xAB/0x85 # It R:393:0xB1:0x8D # Banshee R:394:0x9F/0x9D # Carrion crawler R:395:0xA5/0x9B # Xiclotlan R:396:0xB9/0x95 # Silent watcher R:397:0xB1/0x8E # Pukelman R:398:0xA7/0x8D # Disenchanter beast R:399:0xB9/0x96 # Dark elven druid R:400:0xA7/0x9F # Stone troll R:401:0xA3/0x8A # Black R:402:0xB9/0x97 # Troll priest R:403:0xA3/0x8B # Wereworm R:404:0xAD/0x84 # Killer crimson beetle R:405:0xA0/0x9F # Vampiric ixitxachitl R:406:0xB9/0x98 # Gnoph-Keh R:407:0xB9/0x99 # Giant grey ant R:408:0xA5/0x8C # Kharis the Powerslave R:409:0xB1/0x8F # Gwaihir the Windlord R:410:0xB9/0x9A # Giant red tick R:411:0xAC/0x93 # Displacer beast R:412:0xA7/0x87 # Ulwarth, Son of Ulfang R:413:0xAB/0x87 # Agent of Benedict R:414:0xB9/0x9B # Cave ogre R:415:0xA1/0x8D # White wraith R:416:0xA3/0x9F # Angel R:417:0x9D/0x8F # Ghoul R:418:0xB4/0x8F # Alberich the Nibelung King R:419:0xB1/0x90 # Hellblade R:420:0xB1/0x91 # Killer red beetle R:421:0xA1/0x80 # Beast of Nurgle R:422:0xB9/0x9C # Creeping adamantite coins R:423:0x9D/0x84 # Algroth R:424:0xA3/0x8C #N:425:Flamer of Tzeentch # Roper R:426:0xB9/0x9D # Headless R:427:0xB1/0x92 # Vibration hound R:428:0xA4/0x9C # Nexus hound R:429:0xA4/0x9D # Ogre mage R:430:0xA1/0x8E # Grendel R:431:0xA1/0x90 # Vampire R:432:0xA3/0x9A # Gorgimera R:433:0xA0/0x8D # Shantak R:434:0xB1/0x93 # Colbran R:435:0xA7/0x8E # Spirit naga R:436:0xA9/0x8C # Corpser R:437:0xB9/0x9E # Fiend of Slaanesh R:438:0xB9/0x9F # stairway to hell R:439:0xB1/0x94 # 5-headed hydra R:440:0xA2/0x96 # Barney the Dinosaur R:441:0xB1/0x95 # Black knight R:442:0xAB/0x88 # Seahorse R:443:0xBA/0x80 # Cyclops R:444:0xBA/0x81 # Clairvoyant R:445:0xBA/0x82 # Giant purple worm R:446:0xAD/0x85 # Catoblepas R:447:0xAC/0x81 # Lesser wall monster R:448:0xB1/0x96 # Mage R:449:0xAB/0x8A # Mind flayer R:450:0xAB/0x8B # The Ultimate Dungeon Cleaner R:451:0xB1/0x97 # Deep one R:452:0xBA/0x83 # Basilisk R:453:0xA2/0x97 # Ice troll R:454:0xA3/0x8D # Dhole R:455:0xB1/0x99 # Archangel R:456:0x9D/0x90 # Mimic (ring) R:457:0xAD/0x9E # Chaos tile R:458:0xB1/0x9A # Young blue dragon R:459:0xA6/0x84 # Young white dragon R:460:0xA6/0x85 # Young green dragon R:461:0xA6/0x86 # Young bronze dragon R:462:0xA6/0x87 # Aklash R:463:0xBA/0x84 # Mithril golem R:464:0xA7/0x8F # Skeleton troll R:465:0xAC/0x8C # Skeletal tyrannosaur R:466:0xBA/0x85 # Jaws R:467:0xBA/0x86 # Thorondor R:468:0xBA/0x87 # Giant blue ant R:469:0xA5/0x8B # Grave wight R:470:0xA4/0x80 # Shadow drake R:471:0xA6/0x88 # Manticore R:472:0xA0/0x8E # Giant army ant R:473:0xAE/0x81 # Killer slicer beetle R:474:0xA1/0x81 # Gorgon R:475:0xBA/0x88 # Gug R:476:0xBA/0x89 # Ghost R:477:0x9F/0x9E # Death watch beetle R:478:0xA1/0x82 # Ogre shaman R:479:0xA1/0x8F # Nexus quylthulg R:480:0xA1/0x9B # Shelob, Spider of Darkness R:481:0xA3/0x86 # Giant squid R:482:0xBA/0x8A # Ghoulking R:483:0xBA/0x8B # Doombat R:484:0xBA/0x8C # Ninja R:485:0xAB/0x8C # Memory moss R:486:0xA9/0x86 # Storm giant R:487:0xA1/0x95 # Spectator R:488:0xB1/0x9B # Bokrug R:489:0xBA/0x8D # Biclops R:490:0xBA/0x8E # Half-troll R:491:0xA3/0x8F # Ivory monk R:492:0xBA/0x8F # Bert the Stone Troll R:493:0xA3/0x90 # Bill the Stone Troll R:494:0xA3/0x91 # Tom the Stone Troll R:495:0xA3/0x92 # Cave troll R:496:0xA3/0x8E # Anti-paladin R:497:0xB1/0x9C # Logrus master R:498:0xB1/0x9D # Barrow wight R:499:0xA4/0x81 # Giant skeleton troll R:500:0xAC/0x8D # Chaos drake R:501:0xA6/0x89 # Law drake R:502:0xA6/0x8A # Balance drake R:503:0xA6/0x8B # Ethereal drake R:504:0xA6/0x8C # Groo the Wanderer R:505:0xB1/0x9E # Fasolt the Giant R:506:0xB1/0x9F # Logrus ghost R:507:0xB1/0x9D # Spectre R:508:0xA0/0x80 # Water troll R:509:0xA3/0x93 # Fire elemental R:510:0x9F/0x83 # Cherub R:511:0x9D/0x91 # Water elemental R:512:0x9F/0x84 # Multi-hued hound R:513:0xB2/0x81 # Night stalker R:514:0x9F/0x85 # Carrion crawler R:515:0xA5/0x9C # Master thief R:516:0xAB/0x8E # Jurt the Living Trump R:517:0xB2/0x82 # Lich R:518:0xA1/0x83 # Gas spore R:519:0xBA/0x90 # Master vampire R:520:0xA3/0x9B # Oriental vampire R:521:0xB2/0x83 # Greater mummy R:522:0xBA/0x91 # Bloodletter of Khorne R:523:0xBA/0x92 # Giant grey scorpion R:524:0xA3/0x87 # Earth elemental R:525:0x9F/0x86 # Air elemental R:526:0x9F/0x87 # Doom drake R:527:0xB2/0x84 # Gargoyle R:528:0xBA/0x93 # Malicious leprechaun R:529:0xB2/0x85 # Eog golem R:530:0xA7/0x90 # Little Boy R:531:0xBA/0x94 # Dagashi R:532:0xAB/0x90 # Headless ghost R:533:0xBA/0x95 # Dread R:534:0xA0/0x81 # Leng spider R:535:0xBA/0x96 # Star vampire R:536:0xBA/0x97 # Smoke elemental R:537:0x9F/0x89 # Olog R:538:0xA3/0x94 # Halfling slinger R:539:0xB2/0x86 # Gravity hound R:540:0xA4/0x9E # Acidic cytoplasm R:541:0xA8/0x97 # Inertia hound R:542:0xA4/0x9F # Impact hound R:543:0xA5/0x80 # Sea troll R:544:0xBA/0x98 # Ooze elemental R:545:0x9F/0x88 # Young black dragon R:546:0xA6/0x8D # Mumak R:547:0xAC/0x82 # Giant red ant R:548:0xA5/0x8D # Mature white dragon R:549:0xA6/0x8E # Xorn R:550:0xA4/0x8F # Rogrog the Black Troll R:551:0xA3/0x95 # Mist giant R:552:0xBA/0x99 # Pattern ghost R:553:0xB2/0x87 # Grey wraith R:554:0xA4/0x82 # Revenant R:555:0xBA/0x9A # Young multi-hued dragon R:556:0xA6/0x8F # Raal's Tome of Destruction R:557:0xB2/0x88 # Colossus R:558:0xB2/0x89 # Young gold dragon R:559:0xA6/0x90 # Mature blue dragon R:560:0xA6/0x91 # Mature green dragon R:561:0xA6/0x92 # Mature bronze dragon R:562:0xA6/0x93 # Young red dragon R:563:0xA6/0x94 # Nightblade R:564:0xB2/0x8A # Trapper R:565:0xAD/0x9F # Bodak R:566:0xA0/0x98 # Time bomb R:567:0xBA/0x9B # Mezzodaemon R:568:0xBA/0x9C # Elder thing R:569:0xB2/0x8B # Ice elemental R:570:0x9F/0x8A # Ipsissimus R:571:0xB2/0x8C # The Greater hell magic mushroom were-quylthulg R:572:0xBA/0x9D # Lord Borel of Hendrake R:573:0xB2/0x8D # Chaos spawn R:574:0xB2/0x8E # Mummified troll R:575:0xA1/0x8A # Fire angel R:576:0xBA/0x9E #N:577:Crypt thing # Chaos butterfly R:578:0xBA/0x9F # Time elemental R:579:0xB2/0x8F # Flying polyps R:580:0xBB/0x80 # The Queen Ant R:581:0xA5/0x8E # Will o' the wisp R:582:0x9F/0x8B # Shan R:583:0xBB/0x81 # Magma elemental R:584:0x9F/0x8C # Black pudding R:585:0xA8/0x98 # Killer iridescent beetle R:586:0xB4/0x90 # Nexus vortex R:587:0xAE/0x80 # Plasma vortex R:588:0xAC/0x98 # Mature red dragon R:589:0xA6/0x95 # Mature gold dragon R:590:0xA6/0x96 # Crystal drake R:591:0xA6/0x97 # Mature black dragon R:592:0xA6/0x98 # Mature multi-hued dragon R:593:0xA6/0x99 #N:594:Sky whale R:594:0xB4/0x98 # Father Dagon R:595:0xB1/0x98 #N:596:Mother Hydra R:596:0xB4/0x9A # Death knight R:597:0xAB/0x94 # Mandor, Master of the Logrus R:598:0xB2/0x90 # Time vortex R:599:0xAC/0x99 # Shimmering vortex R:600:0xAC/0x9A # Ancient blue dragon R:601:0x9E/0x88 # Ancient bronze dragon R:602:0x9E/0x89 # Beholder R:603:0xA6/0x9F # Emperor wight R:604:0xA4/0x83 # Seraph R:605:0x9D/0x92 # Loge, Spirit of Fire R:606:0x9F/0x8D # Black wraith R:607:0xA4/0x84 # Nightgaunt R:608:0xB2/0x91 # Baron of hell R:609:0xB2/0x92 #N:610:Scylla #N:611:Monastic lich R:611:0xB4/0x99 # Nether wraith R:612:0xA4/0x85 #N:613:Fire vampire # 7-headed hydra R:614:0xA2/0x98 # Moire, Queen of Rebma R:615:0x9F/0x8E # Kavlax the Many-Headed R:616:0xA6/0x9A # Ancient white dragon R:617:0x9E/0x8A # Ancient green dragon R:618:0x9E/0x8B # Chthonian R:619:0xB2/0x93 # Eldrak R:620:0xA3/0x96 # Ettin R:621:0xA3/0x97 # Night mare R:622:0xAC/0x83 # Vampire lord R:623:0xA3/0x9C # Ancient black dragon R:624:0x9E/0x8C #N:625:Weird fume #N:626:Spawn of Ubbo-Sathla #N:627:Fat Man #N:628:Malekith the Accursed #N:629:Morgenstern, Julian's steed # Spirit troll R:630:0xA3/0x98 # War troll R:631:0xB2/0x94 # Disenchanter worm mass R:632:0xAD/0x86 # Rotting quylthulg R:633:0xA1/0x9C # Lesser titan R:634:0xA1/0x97 # 9-headed hydra R:635:0xA2/0x99 # Enchantress R:636:0xAB/0x96 # Archpriest R:637:0xAB/0x97 # Sorcerer R:638:0xAB/0x98 # Xaren R:639:0xA4/0x90 # Jubjub bird R:640:0x9D/0x97 # Minotaur R:641:0xA0/0x8F # Jasra, Brand's Mistress R:642:0xA9/0x8D # Death drake R:643:0x9E/0x8D # Ancient red dragon R:644:0x9E/0x8E # Ancient gold dragon R:645:0x9E/0x8F # Great crystal drake R:646:0x9E/0x90 #N:647:Wyrd sister # Clubber demon R:648:0xB2/0x95 # Death quasit R:649:0xA0/0x99 #N:650:Giganto the Gargantuan # Strygalldwir R:651:0xB2/0x96 #N:652:Fallen angel #N:653:Giant headless #N:654:Judge Fire #N:655:Ubbo-Sathla, the Unbegotten Source #N:656:Judge Mortis # Dark elven sorceror R:657:0xA8/0x81 # Master lich R:658:0xA1/0x84 # Byakhee R:659:0xB2/0x97 # Rinaldo, son of Brand R:660:0xB2/0x99 # Archon R:661:0x9D/0x93 # Formless spawn of Tsathoggua R:662:0xB2/0x9A # Hunting horror R:663:0xB2/0x9B # Undead beholder R:664:0xA7/0x80 # Shadow demon R:665:0xA0/0x81 # Iron lich R:666:0xB2/0x9C # Dread R:667:0xA0/0x81 #N:668:Greater basilisk #N:669:Charybdis #N:670:Jack of Shadows #N:671:Zephyr Lord #N:672:Juggernaut of Khorne # Mumak R:673:0xAC/0x82 #N:674:Judge Fear # Ancient multi-hued dragon R:675:0x9E/0x91 # Ethereal dragon R:676:0x9E/0x92 # Dark young of Shub-Niggurath R:677:0xB2/0x9D #N:678:Colour out of space # Quaker, Master of Earth R:679:0x9F/0x8F #N:680:Death leprechaun #N:681:Chaugnar Faugn, Horror from the Hills #N:682:Lloigor #N:683:Utgard-Loke #N:684:Quachil Uttaus, Treader of the Dust # Shoggoth R:685:0xB2/0x98 #N:686:Judge Death # Ariel, Queen of Air R:687:0x9F/0x91 # 11-headed hydra R:688:0xA2/0x9A # High priest R:689:0xAB/0x9A # Dreadmaster R:690:0xA0/0x85 # Drolem R:691:0xA7/0x92 # Scatha the Worm R:692:0xAD/0x9B # Warrior of the Dawn R:693:0xB2/0x9E #N:694:Lesser black reaver #N:695:Zoth-Ommog # Nazgul R:696:0xB2/0x9F # Smaug the Golden R:697:0x9E/0x93 # The Stormbringer R:698:0xB3/0x80 # Ultra-elite paladin R:699:0xB3/0x81 #N:700:Leprechaun fanatic # Dracolich R:701:0x9E/0x95 # Greater titan R:702:0xA1/0x98 # Dracolisk R:703:0x9E/0x94 #N:704:Fastitocalon # Spectral tyrannosaur R:705:0xB3/0x82 #N:706:Yibb-Tstll the Patient One #N:707:Ghatanothoa #N:708:Ent #N:709:Hru #N:710:Itangast the Fire Drake # Death mold R:711:0xA9/0x87 # Fafner the Dragon R:712:0xB3/0x83 #N:713:Fangorn the Treebeard #N:714:Zhar the Twin Obscenity # Glaurung, Father of the Dragons R:715:0xAD/0x9A #N:716:Behemoth #N:717:Garm, Guardian of Hel # Greater wall monster R:718:0xB3/0x84 #N:719:Nycadaemon # Balrog R:720:0xAD/0x95 # Goat of Mendes R:721:0xB3/0x85 # Nightwing R:722:0xAD/0x9D # Maulotaur R:723:0xB3/0x86 # Nether hound R:724:0xA5/0x81 # Time hound R:725:0xA5/0x82 # Plasma hound R:726:0xA5/0x83 # Demonic quylthulg R:727:0xA1/0x9D # Great storm wyrm R:728:0x9E/0x97 #N:729:Ulik the Troll # Baphomet the Minotaur Lord R:730:0xA0/0x90 #N:731:Hell knight # Bull Gates R:732:0xB3/0x87 # Santa Claus R:733:0xB3/0x88 #N:734:Eihort, the Thing in the Labyrinth #N:735:The King in Yellow #N:736:Great unclean one # Lord of Chaos R:737:0xB3/0x89 # Khamul the Easterling R:738:0xA4/0x8B # Hound of Tindalos R:739:0xB3/0x8A #N:740:Lesser kraken # Great ice wyrm R:741:0x9E/0x98 #N:742:Demilich # The Phoenix R:743:0x9D/0x98 # Nightcrawler R:744:0xA4/0x8C #N:745:Lord of Change #N:746:Keeper of Secrets #N:747:Shudde M'ell # Hand druj R:748:0xAC/0x8E # Eye druj R:749:0xAC/0x8F # Skull druj R:750:0xAC/0x90 # Chaos vortex R:751:0xAC/0x9B # Aether vortex R:752:0xAC/0x9C #N:753:Nidhogg the Hel-Drake # The Lernean Hydra R:754:0xA2/0x9B # Thuringwethil R:755:0xA3/0x9D # Great hell wyrm R:756:0x9E/0x99 # Hastur the Unspeakable R:757:0xB3/0x8B #N:758:Bloodthirster # Draconic quylthulg R:759:0xA1/0x9E # Nyogtha, the Thing that Should not Be R:760:0xB3/0x8C #N:761:Ahtu, Avatar of Nyarlathotep #N:762:Fundin Bluecloak R:762:0xA8/0x82 # Dworkin Barimen R:763:0xB3/0x8D # Uriel, Angel of Fire R:764:0x9D/0x94 # Azriel, Angel of Death R:765:0x9D/0x95 # Ancalagon the Black R:766:0x9E/0x9A #N:767:Daoloth, the Render of the Veils # Nightwalker R:768:0xA4/0x8D # Raphael, the Messenger R:769:0x9D/0x96 #N:770:Artsi the Champion of Chaos # Saruman of Many Colours R:771:0xAB/0x9E #N:772:Gandalf the Grey # Brand, Mad Visionary of Amber R:773:0xB3/0x8E # Shadowlord R:774:0xA0/0x86 #N:775:Greater kraken #N:776:Archlich # Bast, Goddess of Cats R:777:0xB3/0x8F # Jabberwock R:778:0xB3/0x90 # Chaos hound R:779:0xA5/0x85 #N:780:Vlad Dracula, Prince of Darkness #N:781:Ultimate beholder #N:782:Leviathan # Great Wyrm of Chaos R:783:0x9E/0x9B # Great Wyrm of Law R:784:0x9E/0x9C # Great Wyrm of Balance R:785:0x9E/0x9D # Shambler R:786:0xB3/0x91 #N:787:Hypnos, Lord of Sleep #N:788:Glaaki # Bleys, Master of Manipulation R:789:0xB3/0x92 # Great Wyrm of Many Colours R:790:0xB3/0x93 # Fiona the Sorceress R:791:0xB3/0x94 # Tselakus, the Dreadlord R:792:0xA0/0x87 # Sky Drake R:793:0xB3/0x95 # Julian, Master of Forest Amber R:794:0xB3/0x96 # Tiamat, Celestial Dragon of Evil R:795:0x9E/0x9E # The Norsa R:796:0xB3/0x97 #N:797:Rhan-Tegoth # Black reaver R:798:0xA1/0x85 # Caine, the Conspirator R:799:0xB3/0x98 # Master quylthulg R:800:0xA1/0x9F # Greater draconic quylthulg R:801:0xA2/0x80 # Greater rotting quylthulg R:802:0xA2/0x81 #N:803:Null the Living Void # Vecna, the Emperor Lich R:804:0xA1/0x86 # Omarax the Eye Tyrant R:805:0xA7/0x81 #N:806:Tsathoggua, the Sleeper of N'kai # Gerard, Strongman of Amber R:807:0xB3/0x99 # Ungoliant, the Unlight R:808:0xA3/0x88 # Atlach-Nacha, the Spider God R:809:0xB3/0x9A #N:810:Y'golonac # Aether hound R:811:0xA5/0x86 #N:812:Warp demon # Eric the Usurper R:813:0xB3/0x9B #N:814:Yig, Father of Serpents # Unmaker R:815:0xB3/0x9C # Cyberdemon R:816:0xB3/0x9D #N:817:Hela, Queen of the Dead # The Mouth of Sauron R:818:0xAB/0x9F # Klingsor, Evil Master of Magic R:819:0xB3/0x9E # Corwin, Lord of Avalon R:820:0xB3/0x9F # The Emperor Quylthulg R:821:0xA2/0x82 # Qlzqqlzuup, the Lord of Flesh R:822:0xA2/0x83 #N:823:Cthugha, the Living Flame # Benedict, the Ideal Warrior R:824:0xB4/0x80 # The Witch-King of Angmar R:825:0xA4/0x8E #N:826:Cyaegha #N:827:Pazuzu, Lord of Air # Ithaqua the Windwalker R:828:0xb4/0x81 # Hell hound of Julian R:829:0x9E/0x83 # Cantoras, the Skeletal Lord R:830:0xAC/0x91 # Mephistopheles, Lord of Hell R:831:0xB4/0x82 # Godzilla R:832:0xB4/0x83 #N:833:Abhoth, Source of Uncleanness #N:834:Ymir the Ice Giant #N:835:Loki the Trickster # Star-spawn of Cthulhu R:836:0xB4/0x84 #N:837:Surtur the Giant Fire Demon # The Tarrasque R:838:0xA2/0x9C # Lungorthin, the Balrog of White Fire R:839:0xAD/0x97 # Draugluin, Sire of All Werewolves R:840:0x9E/0x85 #N:841:Shuma-Gorath #N:842:Tulzscha, the Green Flame #N:843:Oremorj the Cyberdemon Lord # Kaschei the Immortal R:844:0xB4/0x85 # Yog-Sothoth, the All-in-One R:845:0xB4/0x86 #N:846:Fenris Wolf # Great wyrm of power R:847:0xB4/0x87 # Shub-Niggurath, Black Goat of the Woods R:848:0xB4/0x88 #N:849:Nodens, Lord of the Great Abyss # Carcharoth, the Jaws of Thirst R:850:0x9E/0x86 # Nyarlathotep, the Crawling Chaos R:851:0xB4/0x89 # Azathoth, Seething Nuclear Chaos R:852:0xB4/0x8A # Cerberus, Guardian of Hades R:853:0x9E/0x87 #N:854:Jormungand the Midgard Serpent #N:855:The Destroyer # Gothmog, the High Captain of Balrogs R:856:0xAD/0x98 # Great Cthulhu R:857:0xB4/0x8B # Sauron, the Sorcerer R:858:0xAC/0x80 #N:859:The Unicorn of Order # Oberon, King of Amber R:860:0xB4/0x8C # Morgoth, Lord of Darkness R:861:0xA1/0x99 # The Serpent of Chaos R:862:0xB4/0x8D # Fields are initialised here. (Note that some don't need tiles.) # Invisible Wall # no tile. # Glyph of warding. T:2:0x8D/0x95 # Explosive rune. T:3:0x83/0x9A # Corpse T:4:0xB4/0x91 # Skeleton T:5:0x8E/0x96 # Trapdoor T:6:0x81/0x8C # Pit T:7:0x81/0x89 # Spiked Pit T:8:0x81/0x89 # Poison Pit T:9:0x81/0x89 # Evil Rune T:10:0x81/0x8F # Strange Rune T:11:0x81/0x92 # Discoloured Spot T:12:0x81/0x86 # Discoloured Spot T:13:0x81/0x86 # Gas Trap T:14:0x81/0x83 # Compact Rune T:15:0x81/0x92 # Dart Trap T:16:0x81/0x80 # Dart Trap T:17:0x81/0x80 # Twisted Rune T:18:0x81/0x8F # Geometric Rune T:19:0x81/0x92 # Glowing Rune T:20:0x83/0x8D # Jagged Rune T:21:0x81/0x92 # Fractured Rune T:22:0x81/0x92 # Faded Rune T:23:0x82/0x80 # Flashing Rune T:24:0x83/0x8D # Weird Rune T:25:0x82/0x80 # Shimmering Rune T:26:0x83/0x8D # Smelly Rune T:27:0x82/0x80 # Intricate Rune T:28:0x83/0x8D # Bright Rune T:29:0x83/0x8D # Blurry Rune T:30:0x82/0x80 # Spiral Rune T:31:0x81/0x8F # Locked Door T:32:0x82/0x83 # Jammed Door T:33:0x82/0x86 # General Store T:34:0x82/0x87 # Armoury T:35:0x82/0x88 # Weapon Smiths T:36:0x82/0x89 # Temple T:37:0x82/0x8A # Alchemy Shop T:38:0x82/0x8B # Magic Shop T:39:0x82/0x8C # Black Market T:40:0x82/0x8D # Home T:41:0x82/0x8E # Bookstore T:42:0x82/0x8F # Weaponmaster T:43:0xBE/0x94 # Zymurgist T:44:0xBE/0x96 # Magesmith (weapon) T:45:0xBE/0x95 # Magesmith (armour) T:46:0xBE/0x95 # Mutatalist T:47:0xBE/0x91 # Map Maker T:48:0xBE/0x92 # Weapon Smiths T:49:0xC0/0x86 # Weapon Smiths T:50:0xC0/0x86 # Weapon Smiths T:51:0xC0/0x86 # Weapon Smiths T:52:0xC0/0x86 # Weapon Smiths T:53:0xC0/0x86 # Armoury T:54:0xC0/0x89 # Armoury T:55:0xC0/0x89 # Armoury T:56:0xC0/0x89 # Armoury T:57:0xC0/0x89 # Armoury T:58:0xC0/0x89 # Swordsman T:59:0xC0/0x87 # Swordsman T:60:0xC0/0x87 # Swordsman T:61:0xC0/0x87 # Swordsman T:62:0xC0/0x87 # Swordsman T:63:0xC0/0x87 # Swordsman T:64:0xC0/0x87 # Shieldsman T:65:0xC0/0x8E # Shieldsman T:66:0xC0/0x8E # Shieldsman T:67:0xC0/0x8E # Shieldsman T:68:0xC0/0x8E # Shieldsman T:69:0xC0/0x8E # Shieldsman T:70:0xC0/0x8E # Axeman T:71:0xC0/0x85 # Axeman T:72:0xC0/0x85 # Axeman T:73:0xC0/0x85 # Axeman T:74:0xC0/0x85 # Axeman T:75:0xC0/0x85 # Axeman T:76:0xC0/0x85 # Ammunition T:77:0xC0/0x81 # Ammunition T:78:0xC0/0x81 # Ammunition T:79:0xC0/0x81 # Fletcher T:80:0xC0/0x83 # Fletcher T:81:0xC0/0x83 # Fletcher T:82:0xC0/0x83 # Fletcher T:83:0xC0/0x83 # Warrior Hall T:84:0xC1/0x86 # Warrior Hall T:85:0xC1/0x86 # Warrior Hall T:86:0xC1/0x86 # Warrior Hall T:87:0xC1/0x86 # Warrior Hall T:88:0xC1/0x86 # Warrior Hall T:89:0xC1/0x86 # Clothes Store T:90:0xC0/0x8B # Clothes Store T:91:0xC0/0x8B # Heavy Armoury T:92:0xC0/0x8F # Heavy Armoury T:93:0xC0/0x8F # Heavy Armoury T:94:0xC0/0x8F # Heavy Armoury T:95:0xC0/0x8F # Heavy Armoury T:96:0xC0/0x8F # Heavy Armoury T:97:0xC0/0x8F # Milliner T:98:0xC0/0x88 # Milliner T:99:0xC0/0x88 # Milliner T:100:0xC0/0x88 # Milliner T:101:0xC0/0x88 # Jeweler T:102:0xC0/0x9A # Jeweler T:103:0xC0/0x9A # Jeweler T:104:0xC0/0x9A # Jeweler T:105:0xC0/0x9A # Jeweler T:106:0xC0/0x9A # Statue Store T:107:0xC0/0x99 # Statue Store T:108:0xC0/0x99 # Figurine Store T:109:0xC0/0x9B # Figurine Store T:110:0xC0/0x9B # Potion Store T:111:0xC0/0x9F # Potion Store T:112:0xC0/0x9F # Potion Store T:113:0xC0/0x9F # Potion Store T:114:0xC0/0x9F # Potion Store T:115:0xC0/0x9F # Scroll Store T:116:0xC0/0x9E # Scroll Store T:117:0xC0/0x9E # Scroll Store T:118:0xC0/0x9E # Scroll Store T:119:0xC0/0x9E # Scroll Store T:120:0xC0/0x9E # Magic Store T:121:0xC0/0x92 # Magic Store T:122:0xC0/0x92 # Magic Store T:123:0xC0/0x92 # Magic Store T:124:0xC0/0x92 # Magic Store T:125:0xC0/0x92 # Book Store T:126:0xC0/0x98 # Temple (These tiles suck - and need to be redone) T:127:0xC0/0x93 # Temple T:128:0xC0/0x93 # Temple T:129:0xC0/0x93 # Supplies Store T:130:0xC1/0x89 # Supplies Store T:131:0xC1/0x88 # Black Market T:132:0xC1/0x95 # Black Market T:133:0xC1/0x95 # Alchemy Shop T:134:0xC1/0x8F # Alchemy Shop T:135:0xC1/0x8F # Junk store T:136:0xC1/0x8B # Food store T:137:0xC1/0x91 # Library T:138:0xBE/0x96 # Casino T:139:0xBE/0x90 # Inn T:140:0xC1/0x80 # Restore Stats T:141:0xBE/0x8F # Load the special player pictures %:xtra-new.prf zangband/lib/pref/graf-win.prf0000644000000000000000000000061510250356274015273 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2004/08/23 20:16:57 $ # File: graf-win.prf # # This file defines special attr/char mappings for use in "graphics" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # # Standard file ?:[EQU $GRAF old] %:graf-xxx.prf # New tiles ?:[EQU $GRAF new] %:graf-new.prf # David Gervais tiles ?:[EQU $GRAF david] %:graf-dvg.prf zangband/lib/pref/graf-x11.prf0000644000000000000000000000137110250356274015107 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2004/08/23 20:16:57 $ # File: graf-x11.prf # Font stuff %:font-x11.prf # Color palette - Graphics V:16:0x01:0x00:0x00:0x00 V:17:0x01:0xF0:0xE0:0xD0 V:18:0x01:0x80:0x80:0x80 V:19:0x01:0x50:0x50:0x50 V:20:0x01:0xE0:0xB0:0x00 V:21:0x01:0xC0:0xA0:0x70 V:22:0x01:0x80:0x60:0x40 V:23:0x01:0x50:0x3C:0x28 V:24:0x01:0x00:0xA0:0xF0 V:25:0x01:0x00:0x00:0xF0 V:26:0x01:0x00:0x00:0x70 V:27:0x01:0xF0:0x00:0x00 V:28:0x01:0x80:0x00:0x00 V:29:0x01:0x90:0x00:0xB0 V:30:0x01:0x00:0x60:0x10 V:31:0x01:0x60:0xF0:0x40 # Hack -- initialization ?:[NOT [EQU $SYS xxx]] # Standard file ?:[EQU $GRAF old] %:graf-xxx.prf # New tiles ?:[EQU $GRAF new] %:graf-new.prf # David Gervais tiles ?:[EQU $GRAF david] %:graf-dvg.prf ?:1 zangband/lib/pref/graf-xaw.prf0000644000000000000000000000141210250356274015271 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2004/08/23 20:16:57 $ # File: graf-x11.prf # Font stuff %:font-x11.prf # Color palette - Graphics #V:16:0x01:0x00:0x00:0x00 #V:17:0x01:0xF0:0xE0:0xD0 #V:18:0x01:0x80:0x80:0x80 #V:19:0x01:0x50:0x50:0x50 #V:20:0x01:0xE0:0xB0:0x00 #V:21:0x01:0xC0:0xA0:0x70 #V:22:0x01:0x80:0x60:0x40 #V:23:0x01:0x50:0x3C:0x28 #V:24:0x01:0x00:0xA0:0xF0 #V:25:0x01:0x00:0x00:0xF0 #V:26:0x01:0x00:0x00:0x70 #V:27:0x01:0xF0:0x00:0x00 #V:28:0x01:0x80:0x00:0x00 #V:29:0x01:0x90:0x00:0xB0 #V:30:0x01:0x00:0x60:0x10 #V:31:0x01:0x60:0xF0:0x40 # Hack -- initialization ?:[NOT [EQU $SYS xxx]] # Standard file ?:[EQU $GRAF old] %:graf-xxx.prf # New tiles ?:[EQU $GRAF new] %:graf-new.prf # David Gervais tiles ?:[EQU $GRAF david] %:graf-dvg.prf ?:1 zangband/lib/pref/graf-xpj.prf0000644000000000000000000002116710250356274015304 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2001/05/31 11:57:37 $ # File: graf-new.prf # # This file defines special attr/char mappings for use in "graphics" mode # with Adam Bolt's 16x16 tiles. # # By Robert Ruehlmann < rr9@angband.org > # # See "lib/help/command.txt" and "src/files.c" for more information. # # Font stuff %:font-x11.prf # Color palette - Graphics V:16:0x01:0x00:0x00:0x00 V:17:0x01:0xF0:0xE0:0xD0 V:18:0x01:0x80:0x80:0x80 V:19:0x01:0x50:0x50:0x50 V:20:0x01:0xE0:0xB0:0x00 V:21:0x01:0xC0:0xA0:0x70 V:22:0x01:0x80:0x60:0x40 V:23:0x01:0x50:0x3C:0x28 V:24:0x01:0x00:0xA0:0xF0 V:25:0x01:0x00:0x00:0xF0 V:26:0x01:0x00:0x00:0x70 V:27:0x01:0xF0:0x00:0x00 V:28:0x01:0x80:0x00:0x00 V:29:0x01:0x90:0x00:0xB0 V:30:0x01:0x00:0x60:0x10 V:31:0x01:0x60:0xF0:0x40 # New tiles ?:[EQU $GRAF new] %:graf-new.prf ?:1 ##### Override some things ##### # # # This pref file is unlike most others that set the graphics. # # The demands of a 3d view are such that extra information needs # to be passed to the renderer. This information is encoded as # bits in the 'character' byte, as well as the 'attribute' byte. # # Note that "normally" graphics is used when both bytes have the # high-bit set. This has changed so that only the attr high-bit # needs to be set for this. # # Now - the size of the graphics bitmap allows us two 'spare' bits. # The 0x40 bit in the character byte is used to denote '3d' tiles. # These tiles have a 'roof' and walls. # If the 0x80 bit in the character byte is set - then the illumination # of the roof is controlled by the BRIGHT_WALLS #define in main-xpj.c # Note that this is a major hack - it will only work if the tiles are # aligned correctly modulo three. # # If this bit is not set, then the 'normal' illumination is used. This # is the case for jungle and the various door tiles. # # If this bit is not set, and the 'wall' bit is not set - then the # behavior is undefined. (We may use this case to denote other effects # in the future. ##### Feature attr/char definitions ##### # nothing F:0:0x80/0x80 # open floor F:1:0x80/0x81 # invisible trap (drawn as open floor) F:2:0x80/0x81 # glyph of warding F:3:0x8D/0x95 # open door F:4:0x82/0x44 # broken door F:5:0x82/0x45 # up staircase F:6:0x80/0x96 # down staircase F:7:0x80/0x99 # Sand F:8:0xBB/0x9D # salt F:9:0xBC/0x85 # Wet mud F:10:0xBB/0x9A # Dry mud F:11:0xBB/0x97 # Tiled floor F:12:0xBB/0x85 # Wooden floor F:13:0xBB/0x88 # Pebbles F:14:0xBB/0x8B # solidified lava F:15:0xBD/0x94 # closed door F:32:0x82/0x43 # Pillar F:33:0xBE/0x97 W:33:0x80/0x81 # secret door (drawn as granite wall) F:48:0x80/0xC4 # rubble F:49:0x80/0xDC # magma vein F:50:0x80/0xCD # quartz vein F:51:0x80/0xC7 # magma vein + hidden treasure F:52:0x80/0xD0 # quartz vein + hidden treasure F:53:0x80/0xC7 # magma vein with treasure F:54:0x80/0xD0 # quartz vein with treasure F:55:0x80/0xCA # granite wall -- basic F:56:0x80/0xC4 # granite wall -- inner F:57:0x80/0xC4 # granite wall -- outer F:58:0x80/0xC4 # granite wall -- solid F:59:0x80/0xC4 # permanent wall -- basic (perm) F:60:0x80/0xD3 # permanent wall -- inner (perm) F:61:0x80/0xD3 # permanent wall -- outer (perm) F:62:0x80/0xD3 # permanent wall -- solid (perm) F:63:0x80/0xD3 # Deep Water F:83:0x83/0x80 # Shallow Water F:84:0x83/0x83 # Deep Lava F:85:0xB6/0x9D # Shallow Lava F:86:0xB6/0x9A # Dark pit F:87:0xBD/0x97 # Dirt F:88:0xBB/0x94 # Grass F:89:0xBB/0x8E # compact rune (XXX - tile from teleport rune) F:90:0x81/0x92 # invisible wall F:91:0x80/0x81 # Ocean F:92:0xBC/0x8E # deep acid F:93:0xBC/0x8B # shallow acid F:94:0xBC/0x88 # submerged tree F:95:0xBE/0x9A W:95:0x83/0x83 # Trees F:96:0x82/0x97 W:96:0xBB/0x8E # Rock face F:97:0xBD/0x8E # Snow covered rock face F:98:0xBD/0x91 # boulder F:99:0xBE/0x80 # Pine tree F:100:0xBE/0x9D W:100:0xBB/0x8E # Snow covered pine tree F:101:0xBE/0x9D W:101:0xBC/0x82 # Obelisk F:102:0xBF/0x80 W:102:0xBB/0x8E # Pillar #F:103:0xBB/0x82 # stone fence F:112:0xBD/0x8B # Well F:113:0x83/0x86 W:113:0xBB/0x8E # Fountain F:114:0xBE/0x83 # Jungle F:115:0x82/0x5A # Bush F:128:0x82/0x9D W:128:0xBB/0x8E # dead bush F:129:0xBF/0x83 W:129:0xBB/0x94 # Long grass F:130:0xBB/0x91 # Rock (on general terrain) F:131:0xBD/0x85 # Rock (on snow) F:132:0xBD/0x88 # Dead tree (on general terrain) F:133:0xBE/0x9A W:133:0xBB/0x94 # Dead tree (on snow) F:134:0xBE/0x9A W:134:0xBC/0x82 # Snow F:135:0xBC/0x82 # thick swamp F:136:0xBE/0x86 # swamp F:137:0xBE/0x89 # Fields are initialised here. (Note that some don't need tiles.) # Glyph of warding. T:2:0x8D/0x95 # Explosive rune. T:3:0x83/0x9A # Corpse T:4:0xB4/0x91 # Skeleton T:5:0x8E/0x96 # Trapdoor T:6:0x81/0x8C # Pit T:7:0x81/0x89 # Spiked Pit T:8:0x81/0x89 # Poison Pit T:9:0x81/0x89 # Evil Rune T:10:0x81/0x8F # Strange Rune T:11:0x81/0x92 # Discoloured Spot T:12:0x81/0x86 # Discoloured Spot T:13:0x81/0x86 # Gas Trap T:14:0x81/0x83 # Compact Rune T:15:0x81/0x92 # Dart Trap T:16:0x81/0x80 # Dart Trap T:17:0x81/0x80 # Twisted Rune T:18:0x81/0x8F # Geometric Rune T:19:0x81/0x92 # Glowing Rune T:20:0x83/0x8D # Jagged Rune T:21:0x81/0x92 # Fractured Rune T:22:0x81/0x92 # Faded Rune T:23:0x82/0x80 # Flashing Rune T:24:0x83/0x8D # Weird Rune T:25:0x82/0x80 # Shimmering Rune T:26:0x83/0x8D # Smelly Rune T:27:0x82/0x80 # Intricate Rune T:28:0x83/0x8D # Bright Rune T:29:0x83/0x8D # Blurry Rune T:30:0x82/0x80 # Spiral Rune T:31:0x81/0x8F # Locked Door T:32:0x82/0x43 # Jammed Door T:33:0x82/0x43 # General Store T:34:0x82/0x47 # Armoury T:35:0x82/0x48 # Weapon Smiths T:36:0x82/0x49 # Temple T:37:0x82/0x4A # Alchemy Shop T:38:0x82/0x4B # Magic Shop T:39:0x82/0x4C # Black Market T:40:0x82/0x4D # Home T:41:0x82/0x4E # Bookstore T:42:0x82/0x4F # Weaponmaster T:43:0xBE/0x54 # Zymurgist T:44:0xBE/0x56 # Magesmith (weapon) T:45:0xBE/0x55 # Magesmith (armour) T:46:0xBE/0x55 # Mutatalist T:47:0xBE/0x51 # Map Maker T:48:0xBE/0x52 # Weapon Smiths T:49:0xC0/0x46 # Weapon Smiths T:50:0xC0/0x46 # Weapon Smiths T:51:0xC0/0x46 # Weapon Smiths T:52:0xC0/0x46 # Weapon Smiths T:53:0xC0/0x46 # Armoury T:54:0xC0/0x49 # Armoury T:55:0xC0/0x49 # Armoury T:56:0xC0/0x49 # Armoury T:57:0xC0/0x49 # Armoury T:58:0xC0/0x49 # Swordsman T:59:0xC0/0x47 # Swordsman T:60:0xC0/0x47 # Swordsman T:61:0xC0/0x47 # Swordsman T:62:0xC0/0x47 # Swordsman T:63:0xC0/0x47 # Swordsman T:64:0xC0/0x47 # Shieldsman T:65:0xC0/0x4E # Shieldsman T:66:0xC0/0x4E # Shieldsman T:67:0xC0/0x4E # Shieldsman T:68:0xC0/0x4E # Shieldsman T:69:0xC0/0x4E # Shieldsman T:70:0xC0/0x4E # Axeman T:71:0xC0/0x45 # Axeman T:72:0xC0/0x45 # Axeman T:73:0xC0/0x45 # Axeman T:74:0xC0/0x45 # Axeman T:75:0xC0/0x45 # Axeman T:76:0xC0/0x45 # Ammunition T:77:0xC0/0x41 # Ammunition T:78:0xC0/0x41 # Ammunition T:79:0xC0/0x41 # Fletcher T:80:0xC0/0x43 # Fletcher T:81:0xC0/0x43 # Fletcher T:82:0xC0/0x43 # Fletcher T:83:0xC0/0x43 # Warrior Hall T:84:0xC1/0x46 # Warrior Hall T:85:0xC1/0x46 # Warrior Hall T:86:0xC1/0x46 # Warrior Hall T:87:0xC1/0x46 # Warrior Hall T:88:0xC1/0x46 # Warrior Hall T:89:0xC1/0x46 # Clothes Store T:90:0xC0/0x4B # Clothes Store T:91:0xC0/0x4B # Heavy Armoury T:92:0xC0/0x4F # Heavy Armoury T:93:0xC0/0x4F # Heavy Armoury T:94:0xC0/0x4F # Heavy Armoury T:95:0xC0/0x4F # Heavy Armoury T:96:0xC0/0x4F # Heavy Armoury T:97:0xC0/0x4F # Milliner T:98:0xC0/0x48 # Milliner T:99:0xC0/0x48 # Milliner T:100:0xC0/0x48 # Milliner T:101:0xC0/0x48 # Jeweler T:102:0xC0/0x5A # Jeweler T:103:0xC0/0x5A # Jeweler T:104:0xC0/0x5A # Jeweler T:105:0xC0/0x5A # Jeweler T:106:0xC0/0x5A # Statue Store T:107:0xC0/0x59 # Statue Store T:108:0xC0/0x59 # Figurine Store T:109:0xC0/0x5B # Figurine Store T:110:0xC0/0x5B # Potion Store T:111:0xC0/0x5F # Potion Store T:112:0xC0/0x5F # Potion Store T:113:0xC0/0x5F # Potion Store T:114:0xC0/0x5F # Potion Store T:115:0xC0/0x5F # Scroll Store T:116:0xC0/0x5E # Scroll Store T:117:0xC0/0x5E # Scroll Store T:118:0xC0/0x5E # Scroll Store T:119:0xC0/0x5E # Scroll Store T:120:0xC0/0x5E # Magic Store T:121:0xC0/0x52 # Magic Store T:122:0xC0/0x52 # Magic Store T:123:0xC0/0x52 # Magic Store T:124:0xC0/0x52 # Magic Store T:125:0xC0/0x52 # Book Store T:126:0xC0/0x58 # Temple (These tiles suck - and need to be redone) T:127:0xC0/0x53 # Temple T:128:0xC0/0x53 # Temple T:129:0xC0/0x53 # Supplies Store T:130:0xC1/0x49 # Supplies Store T:131:0xC1/0x48 # Black Market T:132:0xC1/0x55 # Black Market T:133:0xC1/0x55 # Alchemy Shop T:134:0xC1/0x4F # Alchemy Shop T:135:0xC1/0x4F # Junk store T:136:0xC1/0x4B # Food store T:137:0xC1/0x51 # Library T:138:0xBE/0x56 # Casino T:139:0xBE/0x50 # Inn T:140:0xC1/0x40 # Restore Stats T:141:0xBE/0x4F # Load the special player pictures %:xtra-new.prf zangband/lib/pref/graf-xxx.prf0000644000000000000000000020211410250356274015323 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2001/07/09 10:28:25 $ ##### Special attr/char values ##### ## # Unused (@) ## S:0x00:0x00/0x40 ## S:0x01:0x01/0x40 ## S:0x02:0x02/0x40 ## S:0x03:0x03/0x40 ## S:0x04:0x04/0x40 ## S:0x05:0x05/0x40 ## S:0x06:0x06/0x40 ## S:0x07:0x07/0x40 ## S:0x08:0x08/0x40 ## S:0x09:0x09/0x40 ## S:0x0A:0x0A/0x40 ## S:0x0B:0x0B/0x40 ## S:0x0C:0x0C/0x40 ## S:0x0D:0x0D/0x40 ## S:0x0E:0x0E/0x40 ## S:0x0F:0x0F/0x40 ## # Unused (@) ## S:0x10:0x00/0x40 ## S:0x11:0x01/0x40 ## S:0x12:0x02/0x40 ## S:0x13:0x03/0x40 ## S:0x14:0x04/0x40 ## S:0x15:0x05/0x40 ## S:0x16:0x06/0x40 ## S:0x17:0x07/0x40 ## S:0x18:0x08/0x40 ## S:0x19:0x09/0x40 ## S:0x1A:0x0A/0x40 ## S:0x1B:0x0B/0x40 ## S:0x1C:0x0C/0x40 ## S:0x1D:0x0D/0x40 ## S:0x1E:0x0E/0x40 ## S:0x1F:0x0F/0x40 ## # Unused (@) ## S:0x20:0x00/0x40 ## S:0x21:0x01/0x40 ## S:0x22:0x02/0x40 ## S:0x23:0x03/0x40 ## S:0x24:0x04/0x40 ## S:0x25:0x05/0x40 ## S:0x26:0x06/0x40 ## S:0x27:0x07/0x40 ## S:0x28:0x08/0x40 ## S:0x29:0x09/0x40 ## S:0x2A:0x0A/0x40 ## S:0x2B:0x0B/0x40 ## S:0x2C:0x0C/0x40 ## S:0x2D:0x0D/0x40 ## S:0x2E:0x0E/0x40 ## S:0x2F:0x0F/0x40 # Spells (*) S:0x30:0x85/0x93 S:0x31:0x85/0x92 S:0x32:0x85/0x92 S:0x33:0x85/0x8D S:0x34:0x85/0x8C S:0x35:0x85/0x8F S:0x36:0x85/0x90 S:0x37:0x85/0x95 S:0x38:0x85/0x93 S:0x39:0x85/0x92 S:0x3A:0x85/0x91 S:0x3B:0x85/0x8E S:0x3C:0x85/0x8D S:0x3D:0x85/0x8F S:0x3E:0x85/0x90 S:0x3F:0x85/0x95 # Spells (|) S:0x40:0x84/0x98 S:0x41:0x84/0x98 S:0x42:0x84/0x98 S:0x43:0x84/0x98 S:0x44:0x84/0x98 S:0x45:0x84/0x98 S:0x46:0x84/0x98 S:0x47:0x84/0x98 S:0x48:0x84/0x98 S:0x49:0x84/0x98 S:0x4A:0x84/0x98 S:0x4B:0x84/0x98 S:0x4C:0x84/0x98 S:0x4D:0x84/0x98 S:0x4E:0x84/0x98 S:0x4F:0x84/0x98 # Spells (-) S:0x50:0x84/0x99 S:0x51:0x84/0x99 S:0x52:0x84/0x99 S:0x53:0x84/0x99 S:0x54:0x84/0x99 S:0x55:0x84/0x99 S:0x56:0x84/0x99 S:0x57:0x84/0x99 S:0x58:0x84/0x99 S:0x59:0x84/0x99 S:0x5A:0x84/0x99 S:0x5B:0x84/0x99 S:0x5C:0x84/0x99 S:0x5D:0x84/0x99 S:0x5E:0x84/0x99 S:0x5F:0x84/0x99 # Spells (/) S:0x60:0x84/0x9A S:0x61:0x84/0x9A S:0x62:0x84/0x9A S:0x63:0x84/0x9A S:0x64:0x84/0x9A S:0x65:0x84/0x9A S:0x66:0x84/0x9A S:0x67:0x84/0x9A S:0x68:0x84/0x9A S:0x69:0x84/0x9A S:0x6A:0x84/0x9A S:0x6B:0x84/0x9A S:0x6C:0x84/0x9A S:0x6D:0x84/0x9A S:0x6E:0x84/0x9A S:0x6F:0x84/0x9A # Spells (\) S:0x70:0x84/0x9B S:0x71:0x84/0x9B S:0x72:0x84/0x9B S:0x73:0x84/0x9B S:0x74:0x84/0x9B S:0x75:0x84/0x9B S:0x76:0x84/0x9B S:0x77:0x84/0x9B S:0x78:0x84/0x9B S:0x79:0x84/0x9B S:0x7A:0x84/0x9B S:0x7B:0x84/0x9B S:0x7C:0x84/0x9B S:0x7D:0x84/0x9B S:0x7E:0x84/0x9B S:0x7F:0x84/0x9B # Amulets (") S:0x80:0xB6/0x87 S:0x81:0xB6/0x88 S:0x82:0xB6/0x85 S:0x83:0xB6/0x86 S:0x84:0xB6/0x81 S:0x85:0xB6/0x82 S:0x86:0xB6/0x83 S:0x87:0xB6/0x84 S:0x88:0xB6/0x87 S:0x89:0xB6/0x88 S:0x8A:0xB6/0x8E S:0x8B:0xB6/0x86 S:0x8C:0xB6/0x81 S:0x8D:0xB6/0x82 S:0x8E:0xB6/0x8B S:0x8F:0xB6/0x8C # Rings (=) S:0x90:0xB5/0x8B S:0x91:0xB5/0x8C S:0x92:0xB5/0x89 S:0x93:0xB5/0x8A S:0x94:0xB5/0x81 S:0x95:0xB5/0x82 S:0x96:0xB5/0x83 S:0x97:0xB5/0x88 S:0x98:0xB5/0x8B S:0x99:0xB5/0x8C S:0x9A:0xB5/0x80 S:0x9B:0xB5/0x8A S:0x9C:0xB5/0x81 S:0x9D:0xB5/0x82 S:0x9E:0xB5/0x83 S:0x9F:0xB5/0x88 # Staffs (_) S:0xA0:0xB9/0x84 S:0xA1:0xB9/0x85 S:0xA2:0xB9/0x85 S:0xA3:0xB9/0x81 S:0xA4:0xB9/0x81 S:0xA5:0xB9/0x82 S:0xA6:0xB9/0x80 S:0xA7:0xB9/0x87 S:0xA8:0xB9/0x84 S:0xA9:0xB9/0x85 S:0xAA:0xB9/0x83 S:0xAB:0xB9/0x87 S:0xAC:0xB9/0x81 S:0xAD:0xB9/0x82 S:0xAE:0xB9/0x80 S:0xAF:0xB9/0x87 # Wands (-) S:0xB0:0xB7/0x84 S:0xB1:0xB7/0x85 S:0xB2:0xB7/0x85 S:0xB3:0xB7/0x86 S:0xB4:0xB7/0x81 S:0xB5:0xB7/0x82 S:0xB6:0xB7/0x80 S:0xB7:0xB7/0x87 S:0xB8:0xB7/0x84 S:0xB9:0xB7/0x85 S:0xBA:0xB7/0x83 S:0xBB:0xB7/0x86 S:0xBC:0xB7/0x81 S:0xBD:0xB7/0x82 S:0xBE:0xB7/0x80 S:0xBF:0xB7/0x87 # Rods (-) S:0xC0:0xB8/0x84 S:0xC1:0xB8/0x85 S:0xC2:0xB8/0x85 S:0xC3:0xB8/0x86 S:0xC4:0xB8/0x81 S:0xC5:0xB8/0x82 S:0xC6:0xB8/0x80 S:0xC7:0xB8/0x87 S:0xC8:0xB8/0x84 S:0xC9:0xB8/0x85 S:0xCA:0xB8/0x83 S:0xCB:0xB8/0x86 S:0xCC:0xB8/0x81 S:0xCD:0xB8/0x82 S:0xCE:0xB8/0x80 S:0xCF:0xB8/0x87 # Scrolls (?) S:0xD0:0x86/0x82 S:0xD1:0x86/0x82 S:0xD2:0x86/0x82 S:0xD3:0x86/0x82 S:0xD4:0x86/0x82 S:0xD5:0x86/0x82 S:0xD6:0x86/0x82 S:0xD7:0x86/0x82 S:0xD8:0x86/0x82 S:0xD9:0x86/0x82 S:0xDA:0x86/0x82 S:0xDB:0x86/0x82 S:0xDC:0x86/0x82 S:0xDD:0x86/0x82 S:0xDE:0x86/0x82 S:0xDF:0x86/0x82 # Potions (!) S:0xE0:0xBC/0x84 S:0xE1:0xBC/0x83 S:0xE2:0xBC/0x8A S:0xE3:0xBC/0x8B S:0xE4:0xBC/0x87 S:0xE5:0xBC/0x86 S:0xE6:0xBC/0x85 S:0xE7:0xBC/0x89 S:0xE8:0xBC/0x84 S:0xE9:0xBC/0x83 S:0xEA:0xBC/0x8E S:0xEB:0xBC/0x88 S:0xEC:0xBC/0x8B S:0xED:0xBC/0x8C S:0xEE:0xBC/0x8D S:0xEF:0xBC/0x89 # Food (,) S:0xF0:0xBA/0x84 S:0xF1:0xBA/0x85 S:0xF2:0xBA/0x85 S:0xF3:0xBA/0x86 S:0xF4:0xBA/0x81 S:0xF5:0xBA/0x82 S:0xF6:0xBA/0x80 S:0xF7:0xBA/0x87 S:0xF8:0xBA/0x84 S:0xF9:0xBA/0x85 S:0xFA:0xBA/0x83 S:0xFB:0xBA/0x86 S:0xFC:0xBA/0x81 S:0xFD:0xBA/0x82 S:0xFE:0xBA/0x80 S:0xFF:0xBA/0x87 # Monster attr/char definitions # Player R:0:0x8C:0x81 # Filthy street urchin R:1:0x9B:0x8A # Scrawny cat R:2:0x98:0x8B # Sparrow R:3:0xBD:0x87 # Chaffinch R:4:0xBD:0x86 # Wild rabbit R:5:0xBF:0x85 # Woodsman R:6:0xBD:0x88 # Scruffy little dog R:7:0x8E:0x9D # Farmer Maggot R:8:0x9B:0x8B # Blubbering idiot R:9:0x9B:0x8C # Hobo R:10:0x9B:0x8D # Raving lunatic R:11:0x9B:0x8E # Pitiful looking beggar R:12:0xBB:0x82 # Mangy looking leper R:13:0x9B:0x90 # Agent of black market R:14:0xA2:0x94 # Singing, happy drunk R:15:0x9B:0x92 # Aimless looking merchant R:16:0x9B:0x93 # Mean looking mercenary R:17:0x9B:0x94 # Battle scarred veteran R:18:0x9B:0x95 # Martti Ihrasaari R:19:0xA1:0x8D # Grey mold R:20:0x9A:0x88 # Large white snake R:21:0x93/0x8A # Blinking dot R:22:0x8E:0x85 # Newt R:23:0xA0:0x86 # Giant white centipede R:24:0x96:0x9E # White icky thing R:25:0x99:0x8C # Clear icky thing R:26:0x99:0x8D # Giant white mouse R:27:0x9D:0x8F # Large brown snake R:28:0x93:0x89 # Small kobold R:29:0x9A:0x82 # Kobold R:30:0x9A:0x83 # White worm mass R:31:0x9E:0x88 # Floating eye R:32:0x98:0x84 # Rock lizard R:33:0x93:0x8B # Grid bug R:34:0xA2:0x9B # Jackal R:35:0x8E:0x9E # Soldier ant R:36:0x96:0x8F # Fruit bat R:37:0x96:0x98 # Insect swarm R:38:0xBD:0x89 # Greater hell-beast R:39:0xA2:0x82 # Shrieker mushroom patch R:40:0x8E:0x86 # Blubbering icky thing R:41:0x99:0x8E # Metallic green centipede R:42:0x96:0x9F # Novice warrior R:43:0x9B:0x96 # Novice rogue R:44:0x9B:0x97 # Novice priest R:45:0x9B:0x98 # Novice mage R:46:0x9B:0x99 # Yellow mushroom patch R:47:0x8E:0x87 # White jelly R:48:0x99:0x93 # Giant black ant R:49:0x96:0x90 # Salamander R:50:0x93:0x8D # White harpy R:51:0x91:0x8C # Blue yeek R:52:0x9E:0x92 # Grip, Farmer Maggot's dog R:53:0x8E:0x9F # Wolf, Farmer Maggot's dog R:54:0xBD:0x8A # Fang, Farmer Maggot's dog R:55:0x8F:0x80 # Giant green frog R:56:0x93/0x8C # Freesia R:57:0xBC:0x98 # Green worm mass R:58:0x9E:0x89 # Large yellow snake R:59:0x93:0x91 # Cave spider R:60:0x94:0x82 # Crow R:61:0xBD:0x8B # Wild cat R:62:0x98:0x8C # Smeagol R:63:0x9B:0x9A # Green ooze R:64:0x99:0x94 # Poltergeist R:65:0x90:0x9D # Yellow jelly R:66:0x99/0x96 # Metallic blue centipede R:67:0x97:0x80 # Raven R:68:0xBD:0x8C # Giant white louse R:69:0x9A:0x86 # Piranha R:70:0xBE:0x80 # Black naga R:71:0x9A:0x91 # Spotted mushroom patch R:72:0x8E:0x88 # Silver jelly R:73:0x99:0x95 # Scruffy looking hobbit R:74:0x98:0x9C # Giant white ant R:75:0x96:0x91 # Yellow mold R:76:0x9A:0x89 # Metallic red centipede R:77:0x97:0x81 # Yellow worm mass R:78:0x9E:0x8A # Clear worm mass R:79:0x9E:0x8B # Radiation eye R:80:0x98:0x85 # Yellow light R:81:0xBD:0x92 # Cave lizard R:82:0x93:0x8F # Novice ranger R:83:0x9B:0x9B # Blue jelly R:84:0x99:0x97 # Creeping copper coins R:85:0x8E:0x80 # Giant white rat R:86:0x9D:0x90 # Snotling R:87:0xBD:0x8D # Swordfish R:88:0xBE:0x81 # Blue worm mass R:89:0x9E:0x8C # Large grey snake R:90:0x93:0x90 # Skeleton kobold R:91:0x9D:0x93 # Ewok R:92:0xBB:0x90 # Novice mage R:93:0x9B:0x9D # Green naga R:94:0x9A:0x92 # Giant leech R:95:0xBD:0x8E # Barracuda R:96:0xBE:0x82 # Novice paladin R:97:0x9B:0x9C # Zog R:98:0xBD:0x8F # Blue ooze R:99:0x99:0x98 # Green glutton ghost R:100:0x90:0x9E # Green jelly R:101:0x99:0x99 # Large kobold R:102:0x9A:0x84 # Grey icky thing R:103:0x99:0x8F # Disenchanter eye R:104:0x98:0x86 # Red worm mass R:105:0x9E:0x8D # Copperhead snake R:106:0x93:0x91 # Death sword R:107:0x89:0x85 # Purple mushroom patch R:108:0x8E:0x89 # Novice priest R:109:0x9B:0x9E # Novice warrior R:110:0x9B:0x9F # Nibelung R:111:0xBB:0x8E # Disembodied hand that strangled people R:112:0x9F:0x87 # Brown mold R:113:0x9A:0x8A # Giant brown bat R:114:0x96:0x99 # Rat-thing R:115:0xBD:0x90 # Novice archer R:116:0x9C:0x81 # Creeping silver coins R:117:0x8E:0x81 # Snaga R:118:0x9A:0x97 # Rattlesnake R:119:0x93:0x92 # Giant slug R:120:0xBD:0x93 # Giant pink frog R:121:0x93:0x93 # Dark elf R:122:0x98:0x9E # Zombified kobold R:123:0x9E:0x97 # Crypt Creep R:124:0x9F:0x93 # Rotting corpse R:125:0xBB:0x8B # Cave orc R:126:0x9A:0x98 # Wood spider R:127:0x94:0x83 # Manes R:128:0x91:0x96 # Bloodshot eye R:129:0x98:0x87 # Red naga R:130:0x9A:0x93 # Red jelly R:131:0x99:0x9A # Green icky thing R:132:0x99:0x90 # Lost soul R:133:0x90:0x9F # Night lizard R:134:0x93:0x94 # Mughash the Kobold Lord R:135:0x9A:0x85 # Skeleton orc R:136:0x9D:0x94 # Wormtongue, Agent of Saruman R:137:0xBC:0x9A # Robin Hood, the Outlaw R:138:0xBB:0x88 # Nurgling R:139:0xBD:0x94 # Lagduf, the Snaga R:140:0x9A:0x99 # Brown yeek R:141:0x9E:0x93 # Novice ranger R:142:0x9C:0x83 # Giant salamander R:143:0x93:0x95 # Space monster R:144:0x8A:0x9B # Carnivorous flying monkey R:145:0xBD:0x95 # Green mold R:146:0x9A:0x8B # Novice paladin R:147:0x9B:0x9C # Lemure R:148:0x91:0x97 # Hill orc R:149:0x9A:0x9A # Bandit R:150:0x9C:0x85 # Hunting hawk of Julian R:151:0x96:0x99 # Phantom warrior R:152:0xA0:0x83 # Gremlin R:153:0xA1:0x86 # Yeti R:154:0x95:0x99 # Bloodshot icky thing R:155:0x99:0x91 # Giant grey rat R:156:0x9D:0x91 # Black harpy R:157:0x91:0x8D # Skaven R:158:0xBD:0x96 # The wounded bear R:159:0xBD:0x98 # Portuguese man-o-war R:160:0xBE:0x83 # Rock mole R:161:0xBD:0x99 # Orc shaman R:162:0x9A:0x9B # Baby blue dragon R:163:0x97:0x86 # Baby white dragon R:164:0x97:0x87 # Baby green dragon R:165:0x97:0x88 # Baby black dragon R:166:0x97:0x89 # Baby red dragon R:167:0x97:0x8A # Giant red ant R:168:0x96:0x96 # Brodda, the Easterling R:169:0x9C:0x86 # Bloodfang the Wolf R:170:0xBD:0x9A # King cobra R:171:0x93:0x96 # Eagle R:172:0xBD:0x9B # War bear R:173:0x9F:0x9D # Killer bee R:174:0xA0:0x88 # Giant spider R:175:0x94:0x87 # Giant white tick R:176:0x9D:0x9C # The Borshin R:177:0xBD:0x9C # Dark elven mage R:178:0x98:0x9F # Kamikaze yeek R:179:0xBD:0x9D # Orfax, Son of Boldor R:180:0x9E:0x94 # Servant of Glaaki R:181:0xBD:0x9E # Dark elven warrior R:182:0x99:0x80 # Sand-dweller R:183:0xBF:0x80 # Clear mushroom patch R:184:0x8E:0x8A # Quiver slot R:185:0x89:0x9A # Grishnakh, the Hill Orc R:186:0x9A:0x9C # Giant piranha R:187:0xBE:0x84 # Owlbear R:188:0xBF:0x81 # Blue horror R:189:0xBF:0x82 # Hairy mold R:190:0x9A:0x8C # Grizzly bear R:191:0xBF:0x83 # Disenchanter mold R:192:0x9A:0x8D # Pseudo dragon R:193:0xBB:0x9B # Tengu R:194:0x91:0x98 # Creeping gold coins R:195:0x8E:0x82 # Wolf R:196:0x8F:0x81 # Giant fruit fly R:197:0x90:0x95 # Panther R:198:0x98:0x8D # Tax collector R:199:0xBC:0x99 # Hobbes the Tiger R:200:0x98:0x8E # Shadow Creature of Fiona (rr9) R:201:0xBB:0x8F # Undead mass R:202:0xA0:0x89 # Chaos shapechanger R:203:0xA0:0x8E # Baby multi-hued dragon R:204:0x97:0x8C # Vorpal bunny R:205:0xBF:0x84 # Old Man Willow R:206:0xBF:0x86 # Hippocampus R:207:0xBE:0x85 # Zombified orc R:208:0x9E:0x98 # Hippogriff R:209:0x91:0x8E # Black mamba R:210:0x93:0x97 # White wolf R:211:0x8F:0x82 # Grape jelly R:212:0x99:0x9B # Nether worm mass R:213:0x9E:0x8E # Abyss worm mass R:214:0xA0:0x8C # Golfimbul, the Hill Orc Chief R:215:0x9A:0x9D # Swordsman R:216:0x9C:0x89 # Skaven shaman R:217:0xBD:0x97 # Gazer R:218:0xBD:0x9F # Knight archer R:219:0xBF:0x87 # Ixitxachitl R:220:0xBE:0x86 # Mine-dog R:221:0xBF:0x88 # Hellcat R:222:0xBC:0x97 # Moon beast R:223:0xBB:0x9E # Master yeek R:224:0x9E:0x95 # Priest R:225:0x9C:0x88 # Dark elven priest R:226:0x99:0x82 # Air spirit R:227:0x90:0x83 # Skeleton human R:228:0x9D:0x95 # Zombified human R:229:0x9E:0x99 # Tiger R:230:0x98:0x8E # Moaning spirit R:231:0x91:0x80 # Frumious bandersnatch R:232:0xA0:0x8D # Spotted jelly R:233:0x99:0x9C # Drider R:234:0x94:0x85 # Mongbat R:235:0xC4:0x80 # Killer brown beetle R:236:0x92:0x80 # Boldor, King of the Yeeks R:237:0x9E:0x96 # Ogre R:238:0x92:0x90 # Creeping mithril coins R:239:0x8E:0x83 # Illusionist R:240:0x9C:0x8A # Druid R:241:0x9C:0x8B # Pink horror R:242:0xBF:0x89 # Cloaker R:243:0xBF:0x8A # Black orc R:244:0x9A:0x9E # Ochre jelly R:245:0x99:0x9D # Software bug R:246:0xA0:0x92 # Lurker (rr9) R:247:0x80:0x80 # Nixie R:248:0xBF:0x8B # Vlasta R:249:0xBF:0x8C # Giant white dragon fly R:250:0x90:0x97 # Snaga sapper R:251:0xBF:0x8D # Blue icky thing R:252:0x99:0x92 # Gibbering mouther R:253:0xA0:0x8A # Irish wolfhound of Flora R:254:0xA0:0x91 # Hill giant R:255:0x92:0x96 # Flesh golem R:256:0x98:0x92 # Warg R:257:0x8F:0x83 # Cheerful leprechaun R:258:0xA0:0x93 # Giant flea R:259:0x90:0x96 # Ufthak of Cirith Ungol R:260:0x9A:0x9F # Clay golem R:261:0x98:0x93 # Black ogre R:262:0x92:0x91 #N:263:Dweller on the threshold R:263:0xC4:0x83 # Half-orc R:264:0x9B:0x80 # Dark naga R:265:0xBF:0x8E # Giant octopus R:266:0xBE:0x93 # Magic mushroom patch R:267:0x8E:0x8B # Plaguebearer of Nurgle R:268:0xBF:0x8F # Guardian naga R:269:0x9A:0x94 # Wererat R:270:0x9D:0x92 # Light hound R:271:0x95:0x9B # Shadow hound R:272:0x95:0x9C # Flying skull R:273:0xA0:0x95 # Mi-Go R:274:0x9F:0x9F # Giant tarantula R:275:0x94:0x86 # Giant clear centipede R:276:0x97:0x83 # Mirkwood spider R:277:0x94:0x84 # Frost giant R:278:0x92:0x97 # Griffon R:279:0x91:0x8F # Homonculous R:280:0x91:0x99 # Gnome mage R:281:0x99:0x83 # Clear hound R:282:0x95:0x9D # Umber hulk R:283:0x94:0x9E # Rust monster R:284:0xBF:0x90 # Orc captain R:285:0x9B:0x81 # Gelatinous cube R:286:0x99:0x9E # Giant green dragon fly R:287:0x90:0x98 # Fire giant R:288:0x92:0x98 # Hummerhorn R:289:0x90:0x99 # Lizardman R:290:0xBF:0x91 # Ulfast, Son of Ulfang R:291:0x9C:0x8C # Hammerhead R:292:0xBE:0x88 # Berserker R:293:0xBF:0x92 # Quasit R:294:0x91:0x9A # Sphinx R:295:0xBF:0x93 # Imp R:296:0x91:0x9B # Forest troll R:297:0x94:0x8E # Freezing sphere R:298:0xBF:0x94 # Jumping fireball R:299:0xBF:0x95 # Ball lightning R:300:0xBF:0x96 # 2-headed hydra R:301:0x93:0x98 # Swamp thing R:302:0xBF:0x97 # Water spirit R:303:0x90:0x84 # Giant red scorpion R:304:0x94:0x8C # Earth spirit R:305:0x90:0x85 # Fire spirit R:306:0x90:0x86 # Fire hound R:307:0x95:0x9E # Cold hound R:308:0x95:0x9F # Energy hound R:309:0x96:0x80 # Mimic (potion) R:310:0x8E:0x8F # Door mimic R:311:0xBF:0x98 # Blink dog R:312:0x8F:0x84 # Uruk R:313:0x9B:0x82 # Shagrat, the Orc Captain R:314:0x9B:0x83 # Gorbag, the Orc Captain R:315:0x9B:0x84 # Shambling mound R:316:0x8E:0x8C # White shark R:317:0xBE:0x89 # Chaos beastman R:318:0xBF:0x99 # Daemonette of Slaanesh R:319:0xBF:0x9A # Giant bronze dragon fly R:320:0x90:0x9C # Stone giant R:321:0x92:0x99 # Giant black dragon fly R:322:0x90:0x9A # Stone golem R:323:0x98:0x94 # Red mold R:324:0x9A:0x8E # Giant gold dragon fly R:325:0x90:0x9B # Stunwall R:326:0xBF:0x9B # Ghast R:327:0xBF:0x9C # Ixitxachitl priest R:328:0xBE:0x8A # Huorn R:329:0xBF:0x9D # Bolg, Son of Azog R:330:0x9B:0x85 # Phase spider R:331:0x94:0x89 # Lizard king R:332:0xBF:0x9E # Landmine R:333:0xBF:0x9F # Wyvern R:334:0xA0:0x97 # Great eagle R:335:0xC0:0x80 # Livingstone R:336:0x80:0x82 # Earth hound R:337:0x96:0x81 # Air hound R:338:0x96:0x82 # Sabre-tooth tiger R:339:0x98:0x8F # Water hound R:340:0x96:0x83 # Chimera R:341:0x91:0x90 # Quylthulg R:342:0x92:0x9F # Sasquatch R:343:0x95:0x9A # Weir R:344:0xA0:0x96 # Whale R:345:0xBE:0x8B # Electric eel R:346:0xBE:0x8C # Werewolf R:347:0x8F:0x85 # Dark elven lord R:348:0x99:0x85 # Cloud giant R:349:0x92:0x9A # Ugluk, the Uruk R:350:0x9B:0x86 # Blue dragon bat R:351:0x96:0x9A # Scroll mimic R:352:0x86:0x82 # Chest mimic R:353:0xC0:0x83 # Fire vortex R:354:0x9D:0x9E # Water vortex R:355:0x9D:0x9F # Lugdush, the Uruk R:356:0x9B:0x87 # Arch-vile R:357:0xC0:0x84 # Cold vortex R:358:0x9E:0x80 # Energy vortex R:359:0x9E:0x81 # Globefish R:360:0xBE:0x8D # Carrion R:361:0xC3:0x9E # Mummified orc R:362:0x92:0x8D # Killer whale R:363:0xBE:0x8E # Serpent man R:364:0xC0:0x85 # Vampiric mist R:365:0xC0:0x86 # Killer stag beetle R:366:0x92:0x81 # Iron golem R:367:0x98:0x95 # Auto-roller R:368:0xA0:0x98 # Giant yellow scorpion R:369:0x94:0x8A # Jade monk R:370:0xC0:0x87 # Black ooze R:371:0x99:0x9F # Hardened warrior R:372:0x9C:0x8D # Azog, King of the Uruk-Hai R:373:0x9B:0x88 # Fleshhound of Khorne R:374:0xC0:0x89 # Dark elven warlock R:375:0xA0:0x81 # Master rogue R:376:0x9C:0x8E # Red dragon bat R:377:0x96:0x9B # Killer white beetle R:378:0x96:0x91 # Ice skeleton R:379:0xC0:0x8A # Angamaite of Umbar R:380:0x9C:0x90 # Kouko R:381:0xBB:0x8D # Mime, the Nibelung R:382:0xA2:0x98 # Hagen, son of Alberich R:383:0xA2:0x99 # Meneldor the Swift R:384:0xC0:0x8B # Phantom beast R:385:0xA0:0x84 # Great white shark R:386:0xBE:0x8F # 4-headed hydra R:387:0x93:0x9A # Lesser hell-beast R:388:0xC0:0x8C # Tyrannosaur R:389:0x9F:0x94 # Mummified human R:390:0x92:0x8E # Vampire bat R:391:0x96:0x9C # Sangahyando of Umbar R:392:0x9C:0x8F # It R:393:0x80:0x80 # Banshee R:394:0x91:0x81 # Carrion crawler R:395:0x97:0x84 # Xiclotlan R:396:0xC0:0x8D # Silent watcher R:397:0xA0:0x9A # Pukelman R:398:0x98:0x96 # Disenchanter beast R:399:0xC0:0x8E # Dark elven druid R:400:0x99:0x88 # Stone troll R:401:0x94:0x9A # Black R:402:0xC0:0x8F # Troll priest R:403:0x94:0x90 # Wereworm R:404:0x9E:0x8F # Killer crimson beetle R:405:0x92:0x83 # Vampiric ixitxachitl R:406:0xBE:0x90 # Gnoph-Keh R:407:0xC0:0x90 # Giant grey ant R:408:0x96:0x95 # Kharis the Powerslave R:409:0xA0:0x9C # Gwaihir the Windlord R:410:0xC0:0x91 # Giant red tick R:411:0x9D:0x9D # Displacer beast R:412:0x98:0x90 # Ulwarth, Son of Ulfang R:413:0x9C:0x91 #N:414:Agent of Benedict R:414:0xC4:0x82 # Cave ogre R:415:0x92:0x92 # White wraith R:416:0x95:0x84 # Angel R:417:0x8E:0x92 # Ghoul R:418:0xBB:0x8C # Alberich the Nibelung King R:419:0xA2:0x9A # Hellblade R:420:0x89:0x87 # Killer red beetle R:421:0x92:0x84 # Beast of Nurgle R:422:0xC0:0x92 # Creeping adamantite coins R:423:0x8E:0x84 # Algroth R:424:0x94:0x9D # Flamer of Tzeentch R:425:0xC0:0x93 # Roper R:426:0xC0:0x94 # Headless R:427:0x9F:0x86 # Vibration hound R:428:0x96:0x84 # Nexus hound R:429:0x96:0x8A # Ogre mage R:430:0x92:0x93 # Grendel R:431:0xA1:0x80 # Vampire R:432:0x94:0x9F # Gorgimera R:433:0x91:0x91 # Shantak R:434:0xA0:0x9D # Colbran R:435:0x98:0x97 # Spirit naga R:436:0x9A:0x95 # Corpser R:437:0xC0:0x95 # Fiend of Slaanesh R:438:0xC0:0x96 # stairway to hell R:439:0x81:0x9E # 5-headed hydra R:440:0x93:0x9B # Barney the Dinosaur R:441:0x9F:0x96 # Black knight R:442:0x9C:0x92 # Seahorse R:443:0xBE:0x91 # Cyclops R:444:0xC0:0x97 # Clairvoyant R:445:0xC0:0x98 # Giant purple worm R:446:0x9E:0x90 # Catoblepas R:447:0x9D:0x8B # Lesser wall monster R:448:0x80:0x82 # Mage R:449:0x9C:0x94 # Mind flayer R:450:0x9C:0x95 # The Ultimate Dungeon Cleaner R:451:0xA0:0x99 # Deep one R:452:0xC0:0x99 # Basilisk R:453:0xBB:0x9F # Ice troll R:454:0x94:0x92 # Dhole R:455:0xA1:0x82 # Archangel R:456:0x8E:0x93 # Mimic (ring) R:457:0x82:0x81 # Chaos tile R:458:0xA2:0x86 # Young blue dragon R:459:0x97:0x8D # Young white dragon R:460:0x97:0x8E # Young green dragon R:461:0x97:0x8F # Young bronze dragon R:462:0x97:0x90 # Aklash R:463:0xC0:0x9A # Mithril golem R:464:0x98:0x98 # Skeleton troll R:465:0x9D:0x96 # Skeletal tyrannosaur R:466:0xC0:0x9B # Jaws R:467:0xBE:0x92 # Thorondor R:468:0xC0:0x9C # Giant blue ant R:469:0x96:0x94 # Grave wight R:470:0x95:0x85 # Shadow drake R:471:0x97:0x91 # Manticore R:472:0x91:0x92 # Giant army ant R:473:0x96:0x95 # Killer slicer beetle R:474:0x92:0x85 # Gorgon R:475:0xC0:0x9D # Gug R:476:0xC0:0x9E # Ghost R:477:0x91:0x82 # Death watch beetle R:478:0x92:0x86 # Ogre shaman R:479:0x92:0x95 # Nexus quylthulg R:480:0xA2:0x93 # Shelob, Spider of Darkness R:481:0x94:0x8B # Giant squid R:482:0xBE:0x87 # Ghoulking R:483:0xC0:0x9F # Doombat R:484:0xC1:0x80 # Ninja R:485:0x9C:0x96 # Memory moss R:486:0x9A:0x8F # Storm giant R:487:0x92:0x9B # Spectator R:488:0xA0:0x85 # Bokrug R:489:0xC1:0x81 # Biclops R:490:0xC1:0x82 # Half-troll R:491:0x94:0x94 # Ivory monk R:492:0xC0:0x88 # Bert the Stone Troll R:493:0x94:0x95 # Bill the Stone Troll R:494:0x94:0x96 # Tom the Stone Troll R:495:0x94:0x97 # Cave troll R:496:0x94:0x93 # Anti-paladin R:497:0xA1:0x84 # Logrus master R:498:0xBB:0x84 # Barrow wight R:499:0x95:0x86 # Giant skeleton troll R:500:0x9D:0x97 # Chaos drake R:501:0xBB:0x9A # Law drake R:502:0x97:0x93 # Balance drake R:503:0x97:0x94 # Ethereal drake R:504:0x97:0x95 # Groo the Wanderer R:505:0xA1:0x81 # Fasolt the Giant R:506:0xBB:0x83 # Logrus ghost R:507:0x91:0x83 # Spectre R:508:0xA2:0x85 # Water troll R:509:0x94:0x98 # Fire elemental R:510:0x90:0x87 # Cherub R:511:0x8E:0x94 # Water elemental R:512:0x90:0x88 # Multi-hued hound R:513:0xA2:0x83 # Night stalker R:514:0x90:0x89 # Carrion crawler R:515:0x97:0x85 # Master thief R:516:0x9C:0x98 # Jurt the Living Trump R:517:0xBB:0x87 # Lich R:518:0x92:0x88 # Gas spore R:519:0xC1:0x83 # Master vampire R:520:0x95:0x80 # Oriental vampire R:521:0xA1:0x88 # Greater mummy R:522:0xC1:0x84 # Bloodletter of Khorne R:523:0xC1:0x85 # Giant grey scorpion R:524:0xBB:0x9D # Earth elemental R:525:0x90:0x8A # Air elemental R:526:0x90:0x8B # Doom drake R:527:0xA1:0x89 # Gargoyle R:528:0xC1:0x86 # Malicious leprechaun R:529:0xA0:0x94 # Eog golem R:530:0x98:0x99 # Little Boy R:531:0xC0:0x81 # Dagashi R:532:0x9C:0x9A # Headless ghost R:533:0xC1:0x87 # Dread R:534:0x91:0x85 # Leng spider R:535:0xC1:0x88 # Star vampire R:536:0xC1:0x89 # Smoke elemental R:537:0x90:0x8D # Olog R:538:0x94:0x99 # Halfling slinger R:539:0xBB:0x91 # Gravity hound R:540:0x96:0x86 # Acidic cytoplasm R:541:0x9A:0x80 # Inertia hound R:542:0x96:0x87 # Impact hound R:543:0x96:0x88 # Sea troll R:544:0xBE:0x94 # Ooze elemental R:545:0x90:0x8C # Young black dragon R:546:0x97:0x96 # Mumak R:547:0x9D:0x8C # Giant red ant R:548:0x96:0x96 # Mature white dragon R:549:0x97:0x97 # Xorn R:550:0x95:0x97 # Rogrog the Black Troll R:551:0x94:0x9A # Mist giant R:552:0xC1:0x8A # Pattern ghost R:553:0x91:0x86 # Grey wraith R:554:0x95:0x87 # Revenant R:555:0xC1:0x8B # Young multi-hued dragon R:556:0x97:0x98 # Raal's Tome of Destruction R:557:0xA3:0x91 # Colossus R:558:0xA0:0x80 # Young gold dragon R:559:0x97:0x99 # Mature blue dragon R:560:0x97:0x9A # Mature green dragon R:561:0x97:0x9B # Mature bronze dragon R:562:0x97:0x9C # Young red dragon R:563:0x97:0x9D # Nightblade R:564:0xBB:0x92 # Trapper R:565:0x8E:0x8E # Bodak R:566:0x91:0x9D # Time bomb R:567:0xC1:0x8C # Mezzodaemon R:568:0xC1:0x8D # Elder thing R:569:0x9F:0x9E # Ice elemental R:570:0x90:0x8E # Ipsissimus R:571:0xA1:0x87 # The Greater hell magic mushroom were-quylthulg R:572:0xC1:0x8E # Lord Borel of Hendrake R:573:0xBB:0x86 # Chaos spawn R:574:0x9F:0x85 # Mummified troll R:575:0x92:0x8F # Fire angel R:576:0xC1:0x8F # Crypt thing R:577:0xC1:0x90 # Chaos butterfly R:578:0xC1:0x92 # Time elemental R:579:0xA2:0x84 # Flying polyps R:580:0xC1:0x93 # The Queen Ant R:581:0x96:0x97 # Will o' the wisp R:582:0x90:0x8F # Shan R:583:0xC1:0x94 # Magma elemental R:584:0x90:0x90 # Black pudding R:585:0x9A:0x81 # Killer iridescent beetle R:586:0xA3:0x94 # Nexus vortex R:587:0xA1:0x9D # Plasma vortex R:588:0x9E:0x83 # Mature red dragon R:589:0x97:0x9E # Mature gold dragon R:590:0x97:0x9F # Crystal drake R:591:0xBB:0x99 # Mature black dragon R:592:0x98:0x81 # Mature multi-hued dragon R:593:0x98:0x82 # Sky whale R:594:0xC1:0x95 # Father Dagon R:595:0xC1:0x96 # Mother Hydra R:596:0xC1:0x97 # Death knight R:597:0x9C:0x9E # Mandor, Master of the Logrus R:598:0xBB:0x89 # Time vortex R:599:0x9E:0x82 # Shimmering vortex R:600:0x9E:0x85 # Ancient blue dragon R:601:0x8F:0x8B # Ancient bronze dragon R:602:0x8F:0x8C # Beholder R:603:0x98:0x88 # Emperor wight R:604:0x95:0x88 # Seraph R:605:0x8E:0x95 # Loge, Spirit of Fire R:606:0x90:0x87 # Black wraith R:607:0x95:0x89 # Nightgaunt R:608:0xA0:0x9E # Baron of hell R:609:0x9F:0x8F # Scylla R:610:0xC1:0x98 # Monastic lich R:611:0xC1:0x91 # Nether wraith R:612:0x95:0x8A # Fire vampire R:613:0xC1:0x99 # 7-headed hydra R:614:0x93:0x9D # Moire, Queen of Rebma R:615:0xA2:0x9F # Kavlax the Many-Headed R:616:0x98:0x83 # Ancient white dragon R:617:0x8F:0x8D # Ancient green dragon R:618:0x8F:0x8E # Chthonian R:619:0xA1:0x83 # Eldrak R:620:0x94:0x9B # Ettin R:621:0x94:0x9C # Night mare R:622:0x9D:0x8D # Vampire lord R:623:0x95:0x81 # Ancient black dragon R:624:0x8F:0x8F # Weird fume R:625:0xC1:0x9A # Spawn of Ubbo-Sathla R:626:0xC1:0x9B # Fat Man R:627:0xC0:0x82 # Malekith the Accursed R:628:0xBD:0x91 # Morgenstern, Julian's steed R:629:0xC1:0x9C # Spirit troll R:630:0xC4:0x84 # War troll R:631:0xA1:0x8C # Disenchanter worm mass R:632:0x9E:0x91 # Rotting quylthulg R:633:0x93:0x81 # Lesser titan R:634:0x92:0x9C # 9-headed hydra R:635:0x93:0x9E # Enchantress R:636:0xBB:0x81 # Archpriest R:637:0x9D:0x81 # Sorcerer R:638:0x9D:0x82 # Xaren R:639:0x95:0x98 # Jubjub bird R:640:0x96:0x99 # Minotaur R:641:0x91:0x93 # Jasra, Brand's Mistress R:642:0x9A:0x96 # Death drake R:643:0xBB:0x98 # Ancient red dragon R:644:0x8F:0x91 # Ancient gold dragon R:645:0x8F:0x92 # Great crystal drake R:646:0xBB:0x97 # Wyrd sister R:647:0xC1:0x9D # Clubber demon R:648:0x9E:0x9D # Death quasit R:649:0x91:0x9E # Giganto the Gargantuan R:650:0xBE:0x95 # Strygalldwir R:651:0x8E:0x9C # Fallen angel R:652:0xC1:0x9E # Giant headless R:653:0xC1:0x9F # Judge Fire R:654:0xC2:0x80 # Ubbo-Sathla, the Unbegotten Source R:655:0xC2:0x81 # Judge Mortis R:656:0xC2:0x82 # Dark elven sorceror R:657:0x99:0x8A # Master lich R:658:0x92:0x89 # Byakhee R:659:0xA0:0x9F # Shoggoth R:685:0xA1:0x8E # Rinaldo, son of Brand R:660:0xBB:0x8A # Archon R:661:0x8E:0x96 # Formless spawn of Tsathoggua R:662:0x9F:0x9C # Hunting horror R:663:0x9F:0x98 # Undead beholder R:664:0x98:0x89 # Shadow demon R:665:0x9F:0x88 # Iron lich R:666:0xA1:0x8B # Dread R:667:0x91:0x85 # Greater basilisk R:668:0xC2:0x83 # Charybdis R:669:0xBE:0x96 # Jack of Shadows R:670:0xC2:0x84 # Zephyr Lord R:671:0xC2:0x85 # Juggernaut of Khorne R:672:0xC2:0x86 # Mumak R:673:0x9D:0x8E # Judge Fear R:674:0xC2:0x87 # Ancient multi-hued dragon R:675:0x8F:0x94 # Ethereal dragon R:676:0x8F:0x95 # Dark young of Shub-Niggurath R:677:0xA1:0x8F # Colour out of space R:678:0xC2:0x88 # Quaker, Master of Earth R:679:0x90:0x93 # Death leprechaun R:680:0xBD:0x82 # Chaugnar Faugn, Horror from the Hills R:681:0xC2:0x89 # Lloigor R:682:0xC2:0x8A # Utgard-Loke R:683:0xC2:0x8B # Quachil Uttaus, Treader of the Dust R:684:0xC2:0x8C #N:685:Shoggoth # Judge Death R:686:0xC2:0x8D # Ariel, Queen of Air R:687:0x90:0x94 # 11-headed hydra R:688:0x93:0x9F # High priest R:689:0x9D:0x81 # Dreadmaster R:690:0x91:0x89 # Drolem R:691:0x98:0x9B # Scatha the Worm R:692:0x8F:0x93 # Warrior of the Dawn R:693:0xA1:0x8A # Lesser black reaver R:694:0xC2:0x8E # Zoth-Ommog R:695:0xC2:0x8F # Nazgul R:696:0x95:0x8F # Smaug the Golden R:697:0xBB:0x96 # The Stormbringer R:698:0xA0:0x9B # Ultra-elite paladin R:699:0xA0:0x90 # Leprechaun fanatic R:700:0xC2:0x90 # Dracolich R:701:0x8F:0x98 # Greater titan R:702:0x92:0x9D # Dracolisk R:703:0x8F:0x9E # Fastitocalon R:704:0xBE:0x97 # Spectral tyrannosaur R:705:0x9F:0x95 # Yibb-Tstll the Patient One R:706:0xC2:0x91 # Ghatanothoa R:707:0xC2:0x92 # Ent R:708:0xC2:0x93 # Hru R:709:0xC2:0x94 # Itangast the Fire Drake R:710:0x8F:0x99 # Death mold R:711:0x9A:0x90 # Fafner the Dragon R:712:0xA1:0x90 # Fangorn the Treebeard R:713:0xC2:0x95 # Zhar the Twin Obscenity R:714:0xC2:0x96 # Glaurung, Father of the Dragons R:715:0x8F:0x99 # Behemoth R:716:0xC2:0x97 # Garm, Guardian of Hel R:717:0xC2:0x98 # Greater wall monster R:718:0x80:0x82 # Nycadaemon R:719:0xC2:0x99 # Balrog R:720:0x9F:0x80 # Goat of Mendes R:721:0x9F:0x8D # Nightwing R:722:0x95:0x91 # Maulotaur R:723:0xA0:0x8B # Nether hound R:724:0x96:0x89 # Time hound R:725:0x96:0x85 # Plasma hound R:726:0x96:0x8B # Demonic quylthulg R:727:0x93:0x82 # Great storm wyrm R:728:0x8F:0x9B # Ulik the Troll R:729:0xC2:0x9A # Baphomet the Minotaur Lord R:730:0x91:0x95 # Hell knight R:731:0xC2:0x9B # Bull Gates R:732:0xBC:0x9B # Santa Claus R:733:0x9F:0x89 # Eihort, the Thing in the Labyrinth R:734:0xC2:0x9C # The King in Yellow R:735:0xC2:0x9D # Great unclean one R:736:0xC2:0x9E # Lord of Chaos R:737:0xBB:0x85 # Khamul the Easterling R:738:0x95:0x92 # Hound of Tindalos R:739:0x96:0x89 # Lesser kraken R:740:0xBE:0x98 # Great ice wyrm R:741:0x8F:0x9C # Demilich R:742:0xC2:0x9F # The Phoenix R:743:0x8E:0x9B # Nightcrawler R:744:0x95:0x94 # Lord of Change R:745:0xC3:0x80 # Keeper of Secrets R:746:0xC3:0x81 # Shudde M'ell R:747:0xC3:0x82 # Hand druj R:748:0x9D:0x98 # Eye druj R:749:0x9D:0x99 # Skull druj R:750:0x9D:0x9A # Chaos vortex R:751:0xBB:0x93 # Aether vortex R:752:0x9E:0x87 # Nidhogg the Hel-Drake R:753:0xC3:0x83 # The Lernean Hydra R:754:0x94:0x80 # Thuringwethil R:755:0x95:0x82 # Great hell wyrm R:756:0x8F:0x9D # Hastur the Unspeakable R:757:0x9F:0x8C # Bloodthirster R:758:0xC3:0x84 # Draconic quylthulg R:759:0x93:0x83 # Nyogtha, the Thing that Should not Be R:760:0xA1:0x91 # Ahtu, Avatar of Nyarlathotep R:761:0xC3:0x85 # Fundin Bluecloak R:762:0x99:0x8B # Dworkin Barimen R:763:0xA1:0x92 # Uriel, Angel of Fire R:764:0x8E:0x97 # Azriel, Angel of Death R:765:0x8E:0x98 # Ancalagon the Black R:766:0x8F:0x90 # Daoloth, the Render of the Veils R:767:0xC3:0x86 # Nightwalker R:768:0x95:0x95 # Raphael, the Messenger R:769:0x8E:0x99 # Artsi the Champion of Chaos R:770:0xC3:0x87 # Saruman of Many Colours R:771:0x9D:0x88 # Gandalf the Grey R:772:0xC3:0x88 # Brand, Mad Visionary of Amber R:773:0xA1:0x94 # Shadowlord R:774:0xC4:0x81 # Greater kraken R:775:0xBE:0x99 # Archlich R:776:0xC3:0x89 # Bast, Goddess of Cats R:777:0xC3:0x8A # Jabberwock R:778:0x91:0x9F # Chaos hound R:779:0xBB:0x94 # Vlad Dracula, Prince of Darkness R:780:0xC3:0x8B # Ultimate beholder R:781:0xC3:0x8C # Leviathan R:782:0xBE:0x9A # Great Wyrm of Chaos R:783:0xBB:0x95 # Great Wyrm of Law R:784:0x90:0x80 # Great Wyrm of Balance R:785:0x90:0x81 # Shambler R:786:0x9F:0x92 # Hypnos, Lord of Sleep R:787:0xC3:0x8D # Glaaki R:788:0xC3:0x8E # Bleys, Master of Manipulation R:789:0xA1:0x98 # Great Wyrm of Many Colours R:790:0xA2:0x95 # Fiona the Sorceress R:791:0x9D:0x80 # Tselakus, the Dreadlord R:792:0x91:0x8B # Sky Drake R:793:0xA2:0x80 # Julian, Master of Forest Amber R:794:0xA1:0x95 # Tiamat, Celestial Dragon of Evil R:795:0x90:0x82 # The Norsa R:796:0xBB:0x80 # Rhan-Tegoth R:797:0xC3:0x8F # Black reaver R:798:0x92:0x8A # Caine, the Conspirator R:799:0xA1:0x96 # Master quylthulg R:800:0x93:0x84 # Greater draconic quylthulg R:801:0x93:0x85 # Greater rotting quylthulg R:802:0x93:0x86 # Null the Living Void R:803:0xC3:0x90 # Vecna, the Emperor Lich R:804:0x92:0x8B # Omarax the Eye Tyrant R:805:0x98:0x8A # Tsathoggua, the Sleeper of N'kai R:806:0xC3:0x91 # Gerard, Strongman of Amber R:807:0xA1:0x99 # Ungoliant, the Unlight R:808:0x94:0x8D # Atlach-Nacha, the Spider God R:809:0x94:0x8D # Y'golonac R:810:0xC3:0x92 # Aether hound R:811:0x96:0x8E # Warp demon R:812:0xC3:0x93 # Eric the Usurper R:813:0xA1:0x9A # Yig, Father of Serpents R:814:0xC3:0x94 # Unmaker R:815:0xA1:0x9E # Cyberdemon R:816:0x9F:0x91 # Hela, Queen of the Dead R:817:0xC3:0x95 # The Mouth of Sauron R:818:0x9D:0x89 # Klingsor, Evil Master of Magic R:819:0xA1:0x9F # Corwin, Lord of Avalon R:820:0xA1:0x97 # The Emperor Quylthulg R:821:0x93:0x87 # Qlzqqlzuup, the Lord of Flesh R:822:0x93:0x88 # Cthugha, the Living Flame R:823:0xC3:0x96 # Benedict, the Ideal Warrior R:824:0xA1:0x9C # The Witch-King of Angmar R:825:0x95:0x96 # Cyaegha R:826:0xBD:0x81 # Pazuzu, Lord of Air R:827:0x8E:0x9C # Ithaqua the Windwalker R:828:0xA0:0x82 # Hell hound of Julian R:829:0x8F:0x87 # Cantoras, the Skeletal Lord R:830:0x9D:0x9B # Mephistopheles, Lord of Hell R:831:0x9F:0x90 # Godzilla R:832:0x9F:0x97 # Abhoth, Source of Uncleanness R:833:0xC3:0x97 # Ymir the Ice Giant R:834:0xBD:0x84 # Loki the Trickster R:835:0xC3:0x98 # Star-spawn of Cthulhu R:836:0x9F:0x8A # Surtur the Giant Fire Demon R:837:0xBD:0x85 # The Tarrasque R:838:0x94:0x81 # Lungorthin, the Balrog of White Fire R:839:0x9F:0x82 # Draugluin, Sire of All Werewolves R:840:0x8F:0x88 # Shuma-Gorath R:841:0xBD:0x80 # Tulzscha, the Green Flame R:842:0xC3:0x99 # Oremorj the Cyberdemon Lord R:843:0xC3:0x9A # Kaschei the Immortal R:844:0x92:0x8C # Yog-Sothoth, the All-in-One R:845:0x9F:0x8B # Fenris Wolf R:846:0xC3:0x9B # Great wyrm of power R:847:0xA2:0x81 # Shub-Niggurath, Black Goat of the Woods R:848:0x9F:0x99 # Nodens, Lord of the Great Abyss R:849:0xC3:0x9C # Carcharoth, the Jaws of Thirst R:850:0x8F:0x89 # Nyarlathotep, the Crawling Chaos R:851:0x9F:0x9B # Azathoth, Seething Nuclear Chaos R:852:0x9F:0x9A # Cerberus, Guardian of Hades R:853:0x8F:0x8A # Jormungand the Midgard Serpent R:854:0xBE:0x9B # The Destroyer R:855:0xBD:0x83 # Gothmog, the High Captain of Balrogs R:856:0x9F:0x81 # Great Cthulhu R:857:0x9F:0x84 # Sauron, the Sorcerer R:858:0x9D:0x8A # The Unicorn of Order R:859:0xC3:0x9D # Oberon, King of Amber R:860:0xA1:0x93 # Morgoth, Lord of Darkness R:861:0x92:0x9E # The Serpent of Chaos R:862:0x9F:0x8E # Quantum Dot R:863:0xC4:0x97 # 864 Plain Gold Ring R:864:0xB5:0x87 # Lourph R:865:0xC4:0x98 # Caaws R:866:0xC4:0x99 # Culverin R:867:0xC4:0x9A # Behinder R:868:0xC4:0x9B # Boadile R:869:0xC4:0x9C R:870:0xC5:0x80 R:871:0xC5:0x81 R:872:0xC5:0x82 R:873:0xC5:0x83 R:874:0xC5:0x84 R:875:0xC5:0x85 R:876:0xC5:0x86 R:877:0xC5:0x87 R:878:0xC5:0x88 R:879:0xC5:0x89 R:880:0xC5:0x8A R:881:0xC5:0x8B # Anti-magic hound R:882:0xC4:0x9D # Mana hound R:883:0xC4:0x9E # Vore R:884:0xC4:0x9F # Feature attr/char definitions # nothing (Commented out for speedup. Drawing a space is quicker.) #F:0:0x81:0x80 # open floor F:1:0x80:0x80 # invisible trap F:2:0x80:0x80 # glyph of warding F:3:0xA2:0x88 # open door F:4:0x81:0x87 # broken door F:5:0x81:0x87 # up staircase F:6:0x81:0x9C # down staircase F:7:0x81:0x9E #N:8:sand F:8:0xCF:0x8E #N:9:salt #G:.:W #N:10:wet mud F:10:0xCF:0x8C #N:11:dry mud F:11:0xCF:0x8D #N:12:tiled floor #G:.:r #N:13:wooden floor #G:.:s #N:14:pebbles F:14:0xCF:0x90 #N:15:solidified lava #G:::D # trap door F:16:0xA2:0x97 # pit F:17:0xA2:0x96 # pit F:18:0xA2:0x96 # pit F:19:0xA2:0x96 # strange rune F:20:0xA2:0x8B # strange rune F:21:0xA2:0x8A # discolored spot F:22:0x8A:0x9B # discolored spot F:23:0x8A:0x9B # dart trap F:24:0x82:0x9E # dart trap F:25:0xA2:0x89 # dart trap F:26:0xA2:0x8D # dart trap F:27:0xA2:0x92 # gas trap F:28:0xA2:0x8E # gas trap F:29:0xA2:0x8F # gas trap F:30:0xA2:0x90 # gas trap F:31:0xA2:0x91 # closed door F:32:0x81:0x8B #N:33:pillar #G:#:w F:33:0xCF:0x9A # secret door F:48:0x80:0x82 # pile of rubble F:49:0x81:0x9A # magma vein F:50:0x80:0x83 # quartz vein F:51:0x80:0x83 # magma vein F:52:0x80:0x83 # quartz vein F:53:0x80:0x83 # magma vein with treasure F:54:0x80:0x84 # quartz vein with treasure F:55:0x80:0x84 # granite wall F:56:0x80:0x82 # granite wall F:57:0x80:0x82 # granite wall F:58:0x80:0x82 # granite wall F:59:0x80:0x82 # permanent wall F:60:0x80:0x95 # permanent wall F:61:0x80:0x95 # permanent wall F:62:0x80:0x95 # permanent wall F:63:0x80:0x95 # explosive rune F:64:0xA2:0x87 # section of the Pattern F:65:0xA3:0x9D # section of the Pattern F:66:0xA3:0x97 # section of the Pattern F:67:0xA3:0x9C # section of the Pattern F:68:0xA3:0x9B # section of the Pattern F:69:0xA3:0x9A # section of the Pattern F:70:0xA3:0x98 # section of the Pattern F:71:0xA3:0x98 # Pattern exit F:72:0xA3:0x9D # corrupted section of the Pattern F:73:0xA3:0x99 # General Store F:74:0x81:0x91 # Armoury F:75:0x81:0x92 # Weapon Smiths F:76:0x81:0x93 # Temple F:77:0x81:0x94 # Alchemy Shop F:78:0x81:0x95 # Magic Shop F:79:0x81:0x96 # Black Market F:80:0x81:0x97 # Home F:81:0x81:0x98 # Bookstore F:82:0xCB:0x90 # Deep water F:83:0xCB:0x81 # Shallow water F:84:0xCB:0x82 # Deep lava F:85:0xCB:0x89 # Shallow lava F:86:0xCB:0x88 # Dark pit F:87:0xCF:0x80 # Dirt F:88:0xCB:0x84 # Grass F:89:0xCB:0x85 #N:90:compact rune #G:^:D #N:91:invisible wall #G:.:w #N:92:very deep water F:92:0xCB:0x80 #N:93:deep acid #G:~:g #N:94:shallow acid #G:~:G #N:95:submerged tree F:95:0xCF:0x87 # Tree F:96:0xCB:0x86 # Mountain F:97:0xCB:0x87 # N:98:snow covered rock face F:98:0xD0:0x80 # N:99:boulder #G:::y #N:100:pine tree F:100:0xCF:0x82 #N:101:snow covered tree F:101:0xCF:0x86 #N:102:obelisk #G:;:b #N:103:pillar #G:#:w #N:112:stone fence #G:::b #N:113:well #G:~:v #N:114:fountain #G:~:U #N:115:jungle F:115:0xCF:0x84 #N:128:bush F:128:0xCF:0x88 #N:129:dead bush F:129:0xCF:0x89 #N:130:long grass F:130:0xCF:0x8B #rock on general terrain #N:131:rock F:131:0xD1:0x81 #rock on snow F:132:0xD0:0x81 # dead tree on general terrain F:133:0xCF:0x85 # dead tree on snow F:134:0xD0:0x85 #N:135:snow F:135:0xCF:0x81 #N:136:thick swamp #G:;:g F:136:0xCF:0x93 #N:137:swamp F:137:0xCF:0x92 # Object attr/char definitions # something K:0:0x01:0x20 # Blindness K:1:0xBA:0x81 # Paranoia K:2:0xBA:0x81 # Confusion K:3:0xBA:0x81 # Hallucination K:4:0xBA:0x81 # Cure Poison K:5:0xBA:0x81 # Cure Blindness K:6:0xBA:0x81 # Cure Paranoia K:7:0xBA:0x81 # Cure Confusion K:8:0xBA:0x81 # Weakness K:9:0xBA:0x81 # Unhealth K:10:0xBA:0x81 # Restore Constitution K:11:0xBA:0x81 # Restoring K:12:0xBA:0x81 # Stupidity K:13:0xBA:0x81 # Naivety K:14:0xBA:0x81 # Poison K:15:0xBA:0x81 # Sickness K:16:0xBA:0x81 # Paralysis K:17:0xBA:0x81 # Restore Strength K:18:0xBA:0x81 # Disease K:19:0xBA:0x81 # Cure Serious Wounds K:20:0xBA:0x81 # & Ration~ of Food K:21:0x8B:0x82 # & Hard Biscuit~ K:22:0x8B:0x82 # & Strip~ of Venison K:23:0x8B:0x82 # & Slime Mold~ K:24:0x8A:0x9F # & Piece~ of Elvish Waybread K:25:0x8B:0x80 # & Pint~ of Fine Ale K:26:0x8A:0x95 # & Pint~ of Fine Wine K:27:0x8A:0x96 # & Mattock~ K:28:0xCD:0x80 # & No-dachi~ K:29:0xCD:0x81 # & Broken Dagger~ K:30:0x89:0x83 # & Bastard Sword~ K:31:0x89:0x85 # & Scimitar~ K:32:0x89:0x85 # & Tulwar~ K:33:0x89:0x84 # & Broad Sword~ K:34:0x89:0x85 # & Short Sword~ K:35:0x89:0x84 # & Blade~ of Chaos K:36:0x89:0x87 # & Two-Handed Sword~ K:37:0x89:0x85 # & Main Gauche~ K:38:0x89:0x83 # & Cutlass~ K:39:0x89:0x84 # & Executioner's Sword~ K:40:0x89:0x86 # & Katana~ K:41:0x89:0x85 # & Long Sword~ K:42:0x89:0x85 # & Dagger~ K:43:0x89:0x83 # & Rapier~ K:44:0x89:0x84 # & Sabre~ K:45:0x89:0x84 # & Small Sword~ K:46:0x89:0x84 # & Broken Sword~ K:47:0x89:0x83 # & Ball-and-Chain~ K:48:0x89:0x88 # & Whip~ K:49:0x89:0x89 # & Flail~ K:50:0x89:0x8B # & Two-Handed Flail~ K:51:0x89:0x8B # & Morning Star~ K:52:0x89:0x8B # & Mace~ K:53:0x89:0x8C # & Quarterstaff~ K:54:0x89:0x8E # & War Hammer~ K:55:0x89:0x8F # & Lead-Filled Mace~ K:56:0x89:0x8C # & Mace~ of Disruption K:57:0x89:0x8D # & Lucerne Hammer~ K:58:0x89:0x90 # & Beaked Axe~ K:59:0x89:0x90 # & Glaive~ K:60:0x89:0x90 # & Halberd~ K:61:0x89:0x90 # & Awl-Pike~ K:62:0x89:0x91 # & Pike~ K:63:0x89:0x91 # & Spear~ K:64:0x89:0x91 # & Trident~ K:65:0x89:0x92 # & Lance~ K:66:0x89:0x93 # & Great Axe~ K:67:0x89:0x90 # & Battle Axe~ K:68:0x89:0x90 # & Lochaber Axe~ K:69:0x89:0x90 # & Broad Axe~ K:70:0x89:0x90 # & Scythe~ K:71:0x89:0x94 # & Scythe~ of Slicing K:72:0x89:0x94 # & Short Bow~ K:73:0x89:0x95 # & Long Bow~ K:74:0x89:0x96 # & Light Crossbow~ K:75:0x89:0x97 # & Heavy Crossbow~ K:76:0x89:0x98 # & Sling~ K:77:0x89:0x99 # & Arrow~ K:78:0x89:0x9A # & Seeker Arrow~ K:79:0x89:0x9B # & Bolt~ K:80:0x89:0x9C # & Seeker Bolt~ K:81:0x89:0x9D # & Rounded Pebble~ K:82:0x89:0x9E # & Iron Shot~ K:83:0x89:0x9F # & Shovel~ K:84:0x8A:0x98 # & Gnomish Shovel~ K:85:0x8B:0x8F # & Dwarven Shovel~ K:86:0x8B:0x90 # & Pick~ K:87:0x8A:0x97 # & Orcish Pick~ K:88:0x8B:0x8D # & Dwarven Pick~ K:89:0x8B:0x8E # & Elven Cloak~ K:90:0x88:0x81 # & Pair~ of Soft Leather Boots K:91:0x88:0x89 # & Pair~ of Hard Leather Boots K:92:0x88:0x8A # & Pair~ of Metal Shod Boots K:93:0x88:0x8B # & Hard Leather Cap~ K:94:0x88:0x82 # & Metal Cap~ K:95:0x88:0x83 # & Iron Helm~ K:96:0x88:0x84 # & Steel Helm~ K:97:0x88:0x85 # & Iron Crown~ K:98:0x88:0x86 # & Golden Crown~ K:99:0x88:0x87 # & Jewel Encrusted Crown~ K:100:0x88:0x88 # & Robe~ K:101:0x88:0x95 # & Filthy Rag~ K:102:0x88:0x94 # Soft Leather Armour~ K:103:0x88:0x96 # Soft Studded Leather~ K:104:0x88:0x96 # Hard Leather Armour~ K:105:0x88:0x97 # Hard Studded Leather~ K:106:0x88:0x97 # Leather Scale Mail~ K:107:0x88:0x98 # Metal Scale Mail~ K:108:0x88:0x98 # Chain Mail~ K:109:0x88:0x99 # Rusty Chain Mail~ K:110:0x88:0x9A # Augmented Chain Mail~ K:111:0x88:0x99 # Bar Chain Mail~ K:112:0x88:0x99 # Metal Brigandine Armour~ K:113:0x88:0x99 # Partial Plate Armour~ K:114:0x88:0x9B # Metal Lamellar Armour~ K:115:0x88:0x9B # Full Plate Armour~ K:116:0xCD:0x82 # Ribbed Plate Armour~ K:117:0x88:0x9B # Adamantite Plate Mail~ K:118:0xA3:0x96 # Mithril Plate Mail~ K:119:0x88:0x9C # Mithril Chain Mail~ K:120:0x88:0x9C # Double Chain Mail~ K:121:0x88:0x99 # & Shield~ of Deflection K:122:0x88:0x93 # & Cloak~ K:123:0x88:0x80 # & Shadow Cloak~ K:124:0x88:0x81 # & Set~ of Leather Gloves K:125:0x88:0x8C # & Set~ of Gauntlets K:126:0x88:0x8D # & Set~ of Cesti K:127:0x88:0x8E # & Small Leather Shield~ K:128:0x88:0x8F # & Large Leather Shield~ K:129:0x88:0x90 # & Small Metal Shield~ K:130:0x88:0x91 # & Large Metal Shield~ K:131:0x88:0x92 # Strength K:132:0xB5:0x81 # Dexterity K:133:0xB5:0x81 # Constitution K:134:0xB5:0x81 # Intelligence K:135:0xB5:0x81 # Speed K:136:0xB5:0x83 # Searching K:137:0xB5:0x80 # Teleportation K:138:0xB5:0x80 # Slow Digestion K:139:0xB5:0x80 # Resist Fire K:140:0xB5:0x80 # Resist Cold K:141:0xB5:0x80 # Feather Falling K:142:0xB5:0x80 # Poison Resistance K:143:0xB5:0x82 # Free Action K:144:0xB5:0x80 # Weakness K:145:0xB5:0x80 # Flames K:146:0xB5:0x82 # Acid K:147:0xB5:0x82 # Ice K:148:0xB5:0x82 # Woe K:149:0xB5:0x82 # Stupidity K:150:0xB5:0x80 # Damage K:151:0xB5:0x81 # Accuracy K:152:0xB5:0x81 # Protection K:153:0xB5:0x80 # Aggravate Monster K:154:0xB5:0x80 # See Invisible K:155:0xB5:0x81 # Sustain Strength K:156:0xB5:0x81 # Sustain Intelligence K:157:0xB5:0x81 # Sustain Wisdom K:158:0xB5:0x81 # Sustain Constitution K:159:0xB5:0x81 # Sustain Dexterity K:160:0xB5:0x81 # Sustain Charisma K:161:0xB5:0x81 # Slaying K:162:0xB5:0x81 # Wisdom K:163:0xB6:0x9F # Charisma K:164:0xB6:0x9F # Searching K:165:0xB6:0x9E # Teleportation K:166:0xB6:0x9E # Slow Digestion K:167:0xB6:0x9E # Resist Acid K:168:0xB6:0x9E # Adornment K:169:0xB6:0x9E # Double Ring Mail~ K:170:0xCD:0x83 # the Magi K:171:0xB6:0x80 # DOOM K:172:0xB6:0x80 # Enchant Weapon To-Hit K:173:0x86:0x80 # Enchant Weapon To-Dam K:174:0x86:0x80 # Enchant Armor K:175:0x86:0x80 # Identify K:176:0x86:0x80 # *Identify* K:177:0x86:0x82 # Rumour K:178:0x86:0x80 # Logrus K:179:0x86:0x80 # Remove Curse K:180:0x86:0x80 # Light K:181:0x86:0x80 # Fire K:182:0x86:0x80 # Ice K:183:0x86:0x80 # Summon Monster K:184:0x86:0x80 # Phase Door K:185:0x86:0x80 # Teleportation K:186:0x86:0x80 # Teleport Level K:187:0x86:0x80 # Monster Confusion K:188:0x86:0x80 # Magic Mapping K:189:0x86:0x80 # Rune of Protection K:190:0x86:0x82 # *Remove Curse* K:191:0x86:0x82 # Treasure Detection K:192:0x86:0x80 # Object Detection K:193:0x86:0x80 # Trap Detection K:194:0x86:0x80 # & Sheaf Arrow~ K:195:0xCD:0x84 # & Mithril Shot~ K:196:0xCD:0x85 # Door/Stair Location K:197:0x86:0x80 # Acquirement K:198:0x86:0x80 # *Acquirement* K:199:0x86:0x82 # Mass Genocide K:200:0x86:0x82 # Detect Invisible K:201:0x86:0x80 # Aggravate Monster K:202:0x86:0x80 # Trap Creation K:203:0x86:0x80 # Trap/Door Destruction K:204:0x86:0x80 # Artifact Creation K:205:0x86:0x82 # Recharging K:206:0x86:0x81 # Genocide K:207:0x86:0x81 # Darkness K:208:0x86:0x80 # Protection from Evil K:209:0x86:0x81 # Satisfy Hunger K:210:0x86:0x80 # Dispel Undead K:211:0x86:0x81 # *Enchant Weapon* K:212:0x86:0x82 # Curse Weapon K:213:0x86:0x82 # *Enchant Armor* K:214:0x86:0x82 # Curse Armor K:215:0x86:0x82 # Summon Undead K:216:0x86:0x80 # Blessing K:217:0x86:0x80 # Holy Chant K:218:0x86:0x80 # Holy Prayer K:219:0x86:0x81 # Word of Recall K:220:0x86:0x80 # *Destruction* K:221:0x86:0x82 # Slime Mold Juice K:222:0xBC:0x85 # Apple Juice K:223:0xBC:0x85 # Water K:224:0xBC:0x85 # Strength K:225:0xBC:0x86 # Weakness K:226:0xBC:0x85 # Restore Strength K:227:0xBC:0x86 # Intelligence K:228:0xBC:0x86 # Stupidity K:229:0xBC:0x85 # Restore Intelligence K:230:0xBC:0x86 # Wisdom K:231:0xBC:0x86 # Naivety K:232:0xBC:0x85 # Restore Wisdom K:233:0xBC:0x86 # Charisma K:234:0xBC:0x86 # Ugliness K:235:0xBC:0x86 # Restore Charisma K:236:0xBC:0x86 # Curing K:237:0xBC:0x86 # Invulnerability K:238:0xBC:0x86 # New Life K:239:0xBC:0x86 # Cure Serious Wounds K:240:0xBC:0x85 # Cure Critical Wounds K:241:0xBC:0x85 # Healing K:242:0xBC:0x85 # Constitution K:243:0xBC:0x86 # Experience K:244:0xBC:0x87 # Sleep K:245:0xBC:0x85 # Blindness K:246:0xBC:0x85 # Booze K:247:0xBC:0x85 # Poison K:248:0xBC:0x85 # Speed K:249:0xBC:0x85 # Slowness K:250:0xBC:0x85 # Dexterity K:251:0xBC:0x86 # Restore Dexterity K:252:0xBC:0x86 # Restore Constitution K:253:0xBC:0x86 # Lose Memories K:254:0xBC:0x85 # Salt Water K:255:0xBC:0x85 # Enlightenment K:256:0xBC:0x85 # Heroism K:257:0xBC:0x85 # Berserk Strength K:258:0xBC:0x85 # Boldness K:259:0xBC:0x85 # Restore Life Levels K:260:0xBC:0x87 # Resist Heat K:261:0xBC:0x85 # Resist Cold K:262:0xBC:0x85 # Detect Invisible K:263:0xBC:0x85 # Slow Poison K:264:0xBC:0x85 # Neutralize Poison K:265:0xBC:0x85 # Restore Mana K:266:0xBC:0x86 # Infra-vision K:267:0xBC:0x85 # Resistance K:268:0xBC:0x85 # Light K:269:0xB7:0x8F # Lightning Bolts K:270:0xB7:0x8F # Frost Bolts K:271:0xB7:0x8F # Fire Bolts K:272:0xB7:0x90 # Stone to Mud K:273:0xB7:0x8F # Polymorph K:274:0xB7:0x8F # Heal Monster K:275:0xB7:0x8F # Haste Monster K:276:0xB7:0x8F # Slow Monster K:277:0xB7:0x8F # Confuse Monster K:278:0xB7:0x8F # Sleep Monster K:279:0xB7:0x8F # Drain Life K:280:0xB7:0x91 # Trap/Door Destruction K:281:0xB7:0x8F # Magic Missile K:282:0xB7:0x8F # Clone Monster K:283:0xB7:0x90 # Scare Monster K:284:0xB7:0x90 # Teleport Other K:285:0xB7:0x8F # Disarming K:286:0xB7:0x8F # Lightning Balls K:287:0xB7:0x90 # Cold Balls K:288:0xB7:0x90 # Fire Balls K:289:0xB7:0x91 # Stinking Cloud K:290:0xB7:0x8F # Acid Balls K:291:0xB7:0x91 # Wonder K:292:0xB7:0x8F # & Flight Arrow~ K:293:0xCD:0x86 # Acid Bolts K:294:0xB7:0x90 # Dragon's Flame K:295:0xB7:0x91 # Dragon's Frost K:296:0xB7:0x91 # Dragon's Breath K:297:0xB7:0x91 # Annihilation K:298:0xB7:0x91 # Rockets K:299:0xB7:0x91 # Trap Location K:300:0xB9:0x99 # Treasure Location K:301:0xB9:0x99 # Object Location K:302:0xB9:0x99 # Teleportation K:303:0xB9:0x99 # Earthquakes K:304:0xB9:0x9A # Summoning K:305:0xB9:0x99 # Light K:306:0xB9:0x99 # *Destruction* K:307:0xB9:0x9B # Starlight K:308:0xB9:0x99 # Haste Monsters K:309:0xB9:0x99 # Slow Monsters K:310:0xB9:0x99 # Sleep Monsters K:311:0xB9:0x99 # Cure Light Wounds K:312:0xB9:0x99 # Detect Invisible K:313:0xB9:0x99 # Speed K:314:0xB9:0x9A # Slowness K:315:0xB9:0x99 # Door/Stair Location K:316:0xB9:0x99 # Remove Curse K:317:0xB9:0x9A # Detect Evil K:318:0xB9:0x99 # Curing K:319:0xB9:0x9A # Dispel Evil K:320:0xB9:0x9B # Probing K:321:0xB9:0x9A # Darkness K:322:0xB9:0x99 # Genocide K:323:0xB9:0x9B # Power K:324:0xB9:0x9C # the Magi K:325:0xB9:0x9C # Perception K:326:0xB9:0x99 # Holiness K:327:0xB9:0x9C # Enlightenment K:328:0xB9:0x9A # Healing K:329:0xB9:0x9C # [Book of Common Prayer] K:330:0xA3:0x8A # [High Mass] K:331:0xA3:0x8A # [Book of the Unicorn] K:332:0xA3:0x8B # [Blessings of the Grail] K:333:0xA3:0x8B # [Beginner's Handbook] K:334:0xA3:0x8C # [Master Sorcerer's Handbook] K:335:0xA3:0x8C # [Pattern Sorcery] K:336:0xA3:0x8D # [Grimoire of Power] K:337:0xA3:0x8D # & Small wooden chest~ K:338:0x80:0x96 # & Large wooden chest~ K:339:0x80:0x97 # & Small iron chest~ K:340:0x80:0x98 # & Large iron chest~ K:341:0x80:0x99 # & Small steel chest~ K:342:0x80:0x9A # & Large steel chest~ K:343:0x80:0x9B # & Ruined chest~ K:344:0x80:0x9C # & Iron Spike~ K:345:0x8B:0x84 # & Wooden Torch~ K:346:0x8B:0x86 # & Brass Lantern~ K:347:0x8B:0x85 # & Flask~ of oil K:348:0xBC:0x90 # & Empty Bottle~ K:349:0x8A:0x99 # Havoc K:350:0xB8:0x94 # Door/Stair Location K:351:0xB8:0x94 # Trap Location K:352:0xB8:0x94 # Probing K:353:0xB8:0x97 # Recall K:354:0xB8:0x96 # Illumination K:355:0xB8:0x95 # Light K:356:0xB8:0x94 # Lightning Bolts K:357:0xB8:0x94 # Frost Bolts K:358:0xB8:0x95 # Fire Bolts K:359:0xB8:0x95 # Polymorph K:360:0xB8:0x95 # Slow Monster K:361:0xB8:0x95 # Sleep Monster K:362:0xB8:0x95 # Drain Life K:363:0xB8:0x97 # Teleport Other K:364:0xB8:0x96 # Disarming K:365:0xB8:0x95 # Lightning Balls K:366:0xB8:0x96 # Cold Balls K:367:0xB8:0x96 # Fire Balls K:368:0xB8:0x97 # Acid Balls K:369:0xB8:0x97 # Acid Bolts K:370:0xB8:0x95 # Enlightenment K:371:0xB8:0x97 # Perception K:372:0xB8:0x96 # Curing K:373:0xB8:0x97 # Healing K:374:0xB8:0x97 # Detection K:375:0xB8:0x95 # Restoration K:376:0xB8:0x97 # Speed K:377:0xB8:0x97 # [Call of the Wild] K:379:0xA3:0x8E # [Nature Mastery] K:380:0xA3:0x8E # [Nature's Gifts] K:381:0xA3:0x8F # [Nature's Wrath] K:382:0xA3:0x8F # [Sign of Chaos] K:383:0xA3:0x90 # [Chaos Mastery] K:384:0xA3:0x90 # [Chaos Channels] K:385:0xA3:0x91 # [Armageddon Tome] K:386:0xA3:0x91 # [Black Prayers] K:387:0xA3:0x92 # [Black Mass] K:388:0xA3:0x92 # & Shard~ of Pottery K:389:0x8B:0x88 # & Broken Stick~ K:390:0x8B:0x89 # & Broken Skull~ K:391:0x8B:0x8A # & Broken Bone~ K:392:0x8B:0x8B # & Canine Skeleton~ K:393:0x8B:0x87 # & Rodent Skeleton~ K:394:0x8B:0x87 # & Human Skeleton~ K:395:0x8B:0x87 # & Dwarf Skeleton~ K:396:0x8B:0x87 # & Elf Skeleton~ K:397:0x8B:0x87 # & Gnome Skeleton~ K:398:0x8B:0x87 # & Great Hammer~ K:399:0xCD:0x87 # Black Dragon Scale Mail~ K:400:0x88:0x9F # Blue Dragon Scale Mail~ K:401:0x88:0x9D # White Dragon Scale Mail~ K:402:0x88:0x9E # Red Dragon Scale Mail~ K:403:0x89:0x81 # Green Dragon Scale Mail~ K:404:0x89:0x80 # Multi-Hued Dragon Scale Mail~ K:405:0xCE:0x8F # Pseudo Dragon Scale Mail~ K:406:0xBB:0x9C # Law Dragon Scale Mail~ K:407:0x88:0x9F # Bronze Dragon Scale Mail~ K:408:0x88:0x96 # Gold Dragon Scale Mail~ K:409:0x88:0x9C # Chaos Dragon Scale Mail~ K:410:0x89:0x80 # Balance Dragon Scale Mail~ K:411:0x88:0x99 # Power Dragon Scale Mail~ K:412:0xA2:0x9E # & Dragon Helm~ K:413:0xA2:0x9D # & Dragon Shield~ K:414:0xA2:0x9C # Death K:415:0xBC:0x88 # Ruination K:416:0xBC:0x87 # Detonations K:417:0xBC:0x87 # Augmentation K:418:0xBC:0x87 # *Healing* K:419:0xBC:0x87 # Life K:420:0xBC:0x88 # Self Knowledge K:421:0xBC:0x87 # *Enlightenment* K:422:0xBC:0x88 # [Black Channels] K:423:0xA3:0x93 # [Necronomicon] K:424:0xA3:0x93 # Fear Resistance K:425:0xB5:0x81 # Light and Darkness Resistance K:426:0xB5:0x81 # Nether Resistance K:427:0xB5:0x81 # Nexus Resistance K:428:0xB5:0x81 # Sound Resistance K:429:0xB5:0x81 # Confusion Resistance K:430:0xB5:0x81 # Shard Resistance K:431:0xB5:0x81 # Disenchantment Resistance K:432:0xB5:0x81 # Chaos Resistance K:433:0xB5:0x81 # Blindness Resistance K:434:0xB5:0x81 # Lordly Protection K:435:0xB5:0x81 # Extra Attacks K:436:0xB5:0x81 # Cure Light Wounds K:437:0xBC:0x85 # Clumsiness K:438:0xBC:0x85 # Sickliness K:439:0xBC:0x85 # copper K:480:0x80:0x8B # copper K:481:0x80:0x8B # copper K:482:0x80:0x8B # silver K:483:0x80:0x8C # silver K:484:0x80:0x8C # silver K:485:0x80:0x8C # garnets K:486:0x80:0x8F # garnets K:487:0x80:0x8F # gold K:488:0x80:0x8D # gold K:489:0x80:0x8D # gold K:490:0x80:0x8D # opals K:491:0x80:0x90 # sapphires K:492:0x80:0x91 # rubies K:493:0x80:0x92 # diamonds K:494:0x80:0x93 # emeralds K:495:0x80:0x94 # mithril K:496:0x80:0x8E # adamantite K:497:0xA3:0x95 # & Mighty Hammer~ K:498:0x87:0x9A # & Massive Iron Crown~ K:499:0x87:0x9B # & Phial~ K:500:0x87:0x9D # & Star~ K:501:0x87:0x9E # & Jewel~ K:502:0x87:0x9F # & Amulet~ K:503:0xB6:0x82 # & Amulet~ K:504:0xB6:0x83 # & Necklace~ K:505:0xB6:0x84 # & Ring~ K:506:0xB5:0x83 # & Ring~ K:507:0xB5:0x83 # & Ring~ K:508:0xB5:0x84 # & Ring~ K:509:0xB5:0x85 # & Ring~ K:510:0xB5:0x86 # & Ring~ K:511:0xB5:0x87 R:864:0xB5:0x87 # [Conjurings & Tricks] K:512:0xBC:0x91 # [Deck of Many Things] K:513:0xBC:0x92 # [Trumps of Doom] K:514:0xBC:0x93 # [Five Aces] K:515:0xBC:0x94 # [Cantrips for Beginners] K:516:0xBC:0x95 # [Minor Arcana] K:517:0xBC:0x95 # [Major Arcana] K:518:0xBC:0x95 # [Manual of Master] K:519:0xBC:0x95 # new amulet K:520:0xB6:0x80 # new amulet K:521:0xB6:0x80 # new amulet K:522:0xB6:0x80 # new amulet K:523:0xB6:0x80 # & Zweihander~ K:524:0xCD:0x88 # & Tanto~ K:525:0xCD:0x89 # Splint Mail~ K:526:0xCD:0x8A # Do-maru~ K:527:0xCD:0x8B # & Trifurcate Spear~ K:528:0xCD:0x96 # & Three Piece Rod~ K:529:0xCD:0x8C # O-yoroi~ K:530:0xCD:0x8D # & Fur Cloak~ K:531:0xCD:0x8E # & Lajatang~ K:532:0xCD:0x8F # & Hatchet~ K:533:0xCD:0x90 # Rhino Hide Armour~ K:535:0xCD:0x91 # Leather Jacket~ K:536:0xCD:0x92 # & Sickle~ K:537:0xCD:0x93 # & Tetsubo~ K:538:0xCD:0x94 # & Nunchaku~ K:539:0xCD:0x95 # & Bo Staff~ K:540:0xCD:0x97 # & Jo Staff~ K:541:0xCD:0x98 # & Club~ K:542:0xCD:0x99 # & Broad Spear~ K:543:0xCD:0x9A # & Khopesh~ K:544:0xCD:0x9B # & Flamberge~ K:545:0xCD:0x9C # & Claymore~ K:546:0xCD:0x9D # & Espadon~ K:547:0xCD:0x9E # & Great Scimitar~ K:548:0xCD:0x9F # & Wakizashi~ K:549:0xCE:0x80 # & Naginata~ K:550:0xCE:0x81 # & Fauchard~ K:551:0xCE:0x82 # & Guisarme~ K:552:0xCE:0x83 # & Heavy Lance~ K:553:0xCE:0x84 # & Basillard~ K:554:0xCE:0x85 # & Ninjato~ K:555:0xCE:0x86 # Ring Mail~ K:556:0xCE:0x87 # Cord Armour~ K:557:0xCE:0x88 # Paper Armour~ K:558:0xCE:0x89 # Padded Armour~ K:559:0xCE:0x8A # & Kabuto~ K:560:0xCE:0x8B # Stone and Hide Armour~ K:561:0xCE:0x8C # & Jingasa~ K:562:0xCE:0x8D # Haramakido~ K:563:0xCE:0x8E # Diamond Edge~ K:565:0xCE:0x90 # Scroll of Mundanity K:566:0x86:0x80 # Magical Figurine K:567:0xB4:0x80 # Wooden Statue K:568:0xB4:0x81 # Clay Statue K:569:0xB4:0x82 # Stone Statue K:570:0xB4:0x83 # Iron Statue K:571:0xB4:0x84 # Copper Statue K:572:0xB4:0x85 # Silver Statue K:573:0xB4:0x86 # Golden Statue K:574:0xB4:0x87 # Ivory Statue K:575:0xB4:0x88 # Mithril Statue K:576:0xB4:0x89 # Ornate Statue K:577:0xB4:0x8A # Generic Skeleton K:578:0xB4:0x8B # Generic Corpse K:579:0xB4:0x8C # T-shirt K:580:0xB4:0x8D # Fields are initialised here. (Note that some don't need tiles.) # Invisible Wall # no tile. # N:0:Nothing # N:1:blank # Glyph of warding. T:2:0xA2:0x88 # Explosive rune. T:3:0xA2:0x87 # Corpse T:4:0xB4:0x8C # Skeleton T:5:0xB4:0x8B # Trapdoor T:6:0xA2/0x97 # Pit T:7:0xA2:0x96 # Spiked Pit T:8:0xA2:0x96 # Poison Pit T:9:0xA2:0x96 # Evil Rune T:10:0xA2:0x8B # Strange Rune T:11:0xA2:0x8A # Discoloured Spot T:12:0x8A:0x9B # Discoloured Spot T:13:0x8A:0x9B # Gas Trap T:14:0xA2:0x8F # Compact Rune T:15:0xA2/0x88 # Dart Trap T:16:0x82/0x9E # Dart Trap T:17:0x82:0x9E # Twisted Rune T:18:0xA2/0x89 # Geometric Rune T:19:0xA2/0x8C # Glowing Rune T:20:0xA2/0x8D # Jagged Rune T:21:0xA2/0x8E # Fractured Rune T:22:0xA2/0x90 # Faded Rune T:23:0xA2/0x91 # Flashing Rune T:24:0xA2/0x92 # Weird Rune T:25:0xA2/0x91 # Shimmering Rune T:26:0xA2/0x8D # Smelly Rune T:27:0xA2/0x90 # Intricate Rune T:28:0xA2/0x89 # Bright Rune T:29:0xA2/0x8D # Blurry Rune T:30:0xA2/0x91 # Spiral Rune T:31:0xA2/0x89 # Locked Door T:32:0x81:0x8B # Jammed Door T:33:0x81:0x8B # General Store T:34:0x81:0x91 # Armoury T:35:0x81:0x92 # Weapon Smiths T:36:0x81:0x93 # Temple T:37:0x81:0x94 # Alchemy Shop T:38:0x81:0x95 # Magic Shop T:39:0x81:0x96 # Black Market T:40:0x81:0x97 # Home T:41:0x81:0x98 # Bookstore T:42:0xCB:0x90 # Weaponmaster T:43:0xCB/0x97 # Zymurgist T:44:0xCB/0x98 # Magesmith (weapon) T:45:0xCC/0x9D # Magesmith (armour) T:46:0xCC/0x9D # Mutatalist T:47:0xCB/0x9F # Map Maker T:48:0xD4:0x9C # N:49:Advanced Weapon Smiths T:49:0xCC:0x80 # N:50:Expert Weapon Smiths T:50:0xCC:0x81 # N:51:Arcane Weapon Smiths T:51:0xCC:0x82 # N:52:Obscure Weapon Smiths T:52:0xCC:0x83 # N:53:Rare Weapon Smiths T:53:0xCC:0x84 # N:54:Advanced Armoury T:54:0xCC:0x85 # N:55:Expert Armoury T:55:0xCC:0x86 # N:56:Arcane Armoury T:56:0xCC:0x87 # N:57:Obscure Armoury T:57:0xCC:0x88 # N:58:Rare Armoury T:58:0xCC:0x89 # N:59:Swordsman T:59:0xCC:0x8A # N:60:Advanced Swordsman T:60:0xCC:0x8B # N:61:Expert Swordsman T:61:0xCC:0x8C # N:62:Arcane Swordsman T:62:0xCC:0x8D # N:63:Obscure Swordsman T:63:0xCC:0x8E # N:64:Rare Swordsman T:64:0xCC:0x8F # N:65:Shieldsman T:65:0xCC:0x90 # N:66:Advanced Shieldsman T:66:0xCC:0x91 # N:67:Expert Shieldsman T:67:0xCC:0x92 # N:68:Arcane Shieldsman T:68:0xCC:0x93 # N:69:Obscure Shieldsman T:69:0xCC:0x94 # N:70:Rare Shieldsman T:70:0xCC:0x95 # N:71:Axeman T:71:0xCC:0x96 # N:72:Advanced Axeman T:72:0xCC:0x97 # N:73:Expert Axeman T:73:0xCC:0x98 # N:74:Arcane Axeman T:74:0xCC:0x99 # N:75:Obscure Axeman T:75:0xCC:0x9A # N:76:Rare Axeman T:76:0xCC:0x9B # N:77:Ammo Supplies T:77:0xD3:0x80 # N:78:Powerful Ammo Supplies T:78:0xD3:0x81 # N:79:Deadly Ammo Supplies T:79:0xD3:0x82 # N:80:Fletcher T:80:0xD3:0x83 # N:81:Advanced Fletcher T:81:0xD3:0x84 # N:82:Expert Fletcher T:82:0xD3:0x85 # N:83:Rare Fletcher T:83:0xD3:0x86 # N:84:Warrior Hall T:84:0xD3:0x87 # N:85:Advanced Warrior Hall T:85:0xD3:0x88 # N:86:Expert Warrior Hall T:86:0xD3:0x89 # N:87:Arcane Warrior Hall T:87:0xD3:0x8A # N:88:Obscure Warrior Hall T:88:0xD3:0x8B # N:89:Rare Warrior Hall T:89:0xD3:0x8C # N:90:Clothes Store T:90:0xD3:0x8D # N:91:Expensive Clothes Store T:91:0xD3:0x8E # N:92:Heavy Armoury T:92:0x81:0x92 # N:93:Advanced Heavy Armoury T:93:0xCC:0x85 # N:94:Expert Heavy Armoury T:94:0xCC:0x86 # N:95:Arcane Heavy Armoury T:95:0xCC:0x87 # N:96:Obscure Heavy Armoury T:96:0xCC:0x88 # N:97:Rare Heavy Armoury T:97:0xCC:0x89 # N:98:Milliner T:98:0xD3:0x8F # N:99:Advanced Milliner T:99:0xD3:0x90 # N:100:Expert Milliner T:100:0xD3:0x91 # N:101:Rare Milliner T:101:0xD3:0x92 # N:102:Jeweler T:102:0xD3:0x93 # N:103:Copper Jewler T:103:0xD3:0x94 # N:104:Silver Jeweler T:104:0xD3:0x95 # N:105:Gold Jeweler T:105:0xD3:0x96 # N:106:Rare Jeweler T:106:0xD3:0x97 # N:107:Statue Store T:107:0xD3:0x98 # N:108:Expensive Statue Store T:108:0xD3:0x99 # N:109:Figurine Store T:109:0xD3:0x9A # N:110:Expensive Figurine Store T:110:0xD3:0x9B # N:111:Potion Store T:111:0xD4:0x80 # N:112:Cultured Potion Store T:112:0xD4:0x81 # N:113:Expensive Potion Store T:113:0xD4:0x82 # N:114:Obscure Potion Store T:114:0xD4:0x83 # N:115:Rare Potion Store T:115:0xD4:0x84 # N:116:Scroll Store T:116:0xD4:0x85 # N:117:Crafted Scroll Store T:117:0xD4:0x86 # N:118:Expensive Scroll Store T:118:0xD4:0x87 # N:119:Obscure Scroll Store T:119:0xD4:0x88 # N:120:Rare Scroll Store T:120:0xD4:0x89 # N:121:Magic Store T:121:0xD4:0x8A # N:122:Crafted Magic Store T:122:0xD4:0x8B # N:123:Expensive Magic Store T:123:0xD4:0x8C # N:124:Obscure Magic Store T:124:0xD4:0x8D # N:125:Rare Magic Store T:125:0xD4:0x8E # N:126:Rare Book Store T:126:0xD4:0x8F # N:127:Large Temple T:127:0xD4:0x90 # N:128:High Temple T:128:0xD4:0x91 # N:129:Hidden Temple T:129:0xD4:0x92 # N:130:Supplies Store T:130:0xD4:0x93 # N:131:Dungeon Supplies Store T:131:0xD4:0x94 # N:132:Large Black Market T:132:0xD4:0x95 # N:133:Hidden Black Market T:133:0xD4:0x97 # N:134:Advanced Alchemy Shop T:134:0xD4:0x98 # N:135:Rare Alchemy Shop T:135:0xD4:0x99 # N:136:Junk Store T:136:0xD4:0x9A # N:137:Food Store T:137:0xD4:0x9B # N:138:Library T:138:0xCB:0x91 # N:139:Casino T:139:0xCB:0x94 # N:140:Inn T:140:0xCB:0x95 # N:141:Healer T:141:0xCB:0x8F # Load the special player pictures %:xtra-xxx.prf zangband/lib/pref/graf.prf0000644000000000000000000000154110250356274014477 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2003/03/08 14:38:44 $ # File: graf.prf # # This file defines special attr/char mappings for use in "graphics" mode # # This file includes, if appropriate, various "sub-files" # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Standard font file ##### %:font-xxx.prf ##### System Specific Subfiles ##### ?:[IOR [EQU $SYS xaw] [EQU $SYS gtk] [EQU $SYS tnb]] %:graf-xaw.prf ?:[EQU $SYS x11] %:graf-x11.prf ?:[EQU $SYS xpj] %:graf-xpj.prf ?:[EQU $SYS lsl] %:graf-lsl.prf ?:[IOR [EQU $SYS gcu] [EQU $SYS vcs]] %:graf-gcu.prf ?:[EQU $SYS ami] %:graf-ami.prf ?:[EQU $SYS mac] %:graf-mac.prf ?:[EQU $SYS dos] %:graf-dos.prf ?:[IOR [EQU $SYS win] [EQU $SYS w2k]] %:graf-win.prf ?:[EQU $SYS ibm] %:graf-ibm.prf ?:[EQU $SYS emx] %:graf-emx.prf ?:[EQU $SYS acn] %:graf-acn.prf ?:1 zangband/lib/pref/message.prf0000644000000000000000000000232610250356274015206 0ustar rootroot# File: message.prf # # This file defines the default message colors. # # This file includes, if appropriate, various "sub-files". # # See "lib/help/command.txt" and "src/files.c" for more information. # # Format: # M:: # # Possible colors: # d = Black # w = White # s = Gray # o = Orange # r = Red # g = Green # b = Blue # u = Brown # D = Dark Gray # W = Light Gray # v = Violet # y = Yellow # R = Light Red # G = Light Green # B = Light Blue # U = Light Brown # MSG_GENERIC M:0:w # MSG_HIT M:1:w # MSG_MISS M:2:w # MSG_FLEE M:3:w # MSG_DROP M:4:w # MSG_KILL M:5:w # MSG_LEVEL M:6:w # MSG_DEATH M:7:w # MSG_STUDY M:8:w # MSG_TELEPORT M:9:w # MSG_SHOOT M:10:w # MSG_QUAFF M:11:w # MSG_ZAP M:12:w # MSG_WALK M:13:w # MSG_TPOTHER M:14:w # MSG_HITWALL M:15:w # MSG_EAT M:16:w # MSG_STORE1 M:17:w # MSG_STORE2 M:18:w # MSG_STORE3 M:19:w # MSG_STORE4 M:20:w # MSG_DIG M:21:w # MSG_OPENDOOR M:22:w # MSG_SHUTDOOR M:23:w # MSG_TPLEVEL M:24:w # MSG_BELL M:25:o # MSG_NOTHING_TO_OPEN M:26:w # MSG_LOCKPICK_FAIL M:27:w # MSG_STAIRS M:28:w # MSG_HITPOINT_WARN M:29:o ?:1 zangband/lib/pref/pref-acn.prf0000644000000000000000000000043310250356274015252 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: pref-acn.prf # This is a minimal "pref" file for RISC OS # Map cursor keys onto keypad A:4 P:^_18C\r A:6 P:^_18D\r A:2 P:^_18E\r A:8 P:^_18F\r # Map F3 to ^S (to be a bit RISC OS-ey) A:^S P:^_183\r zangband/lib/pref/pref-ami.prf0000644000000000000000000000030110250356274015251 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: pref-ami.prf # # This file contains various default macros for use with # the executable built from "main-ami.c". # zangband/lib/pref/pref-emx.prf0000644000000000000000000000055410250356274015306 0ustar rootroot# File: pref-emx.prf # # Include "pref-ibm.prf" to get the default macros. # # Note that, while most key-to-trigger mappings are the same as DOS/Win, # some few odd keys will be different or not available at all. # # Examples: Ctrl-Escape, Alt-PrintScreen # # What's NOT working: color palette redefinitions. # %:pref-win.prf # shift-5 - rest A:R\r P:^_Sx4C\r zangband/lib/pref/pref-gcu.prf0000644000000000000000000000212510250356274015267 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: pref-gcu.prf # # This file may be included by "pref.prf", when using "main-gcu.prf". # # It contains macro definitions to allow the VT100 cursor keys to be # recognized by Angband. This will also make the "escape" key take a # few seconds to recognize, so you may want to use the "`" key instead. # ### VT100 Keypad ### # Special keypad keys (delete, insert) A:. P:\e[3~ A:0 P:\e[2~ # Numerical keypad keys (map to appropriate number) A:1 P:\e[4~ P:\e[F A:2 P:\e[B A:3 P:\e[6~ A:4 P:\e[D A:5 P:\e[G A:6 P:\e[C A:7 P:\e[1~ P:\e[H A:8 P:\e[A A:9 P:\e[5~ # Ctrl - Numerical keypad A:+0 P:\e[0C~ A:+1 P:\e[1C~ A:+2 P:\e[2C~ A:+3 P:\e[3C~ A:+4 P:\e[4C~ A:+5 P:\e[5C~ A:+6 P:\e[6C~ A:+7 P:\e[7C~ A:+8 P:\e[8C~ A:+9 P:\e[9C~ # Shift - Numerical keypad A:\\.1 P:\e[1S~ A:\\.2 P:\e[2S~ A:\\.3 P:\e[3S~ A:\\.4 P:\e[4S~ A:\\.5 P:\e[5S~ A:\\.6 P:\e[6S~ A:\\.7 P:\e[7S~ A:\\.8 P:\e[8S~ A:\\.9 P:\e[9S~ A:\\.0 P:\e[0S~ # Basic function keys (F1 - F4) A:\e P:\eOP A:\e P:\eOQ A:\e P:\eOR A:\e P:\eOS zangband/lib/pref/pref-gtk.prf0000644000000000000000000000461410250356274015303 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: pref-x11.prf # # This file provides some macros for use with versions of Angband compiled # using the "main-x11.c" (or "main-xaw.c") file. # # Note the use of "\e\e\e\e" (four escapes) to allow the macros to work # even if the game is not yet ready for a command. # # Note the use of "\\." (for "run") and "\\+" (for "alter"), to make sure # that the macros will work regardless of the "keymap" being used. # # Keypad (0-9) A:0 P:^__FFB0\r P:^__FF63\r P:^__????\r P:^__FF9E\r A:1 P:^__FFB1\r P:^__FF57\r P:^__FFDE\r P:^__FF9C\r A:2 P:^__FFB2\r P:^__FF54\r P:^__FFDF\r P:^__FF99\r A:3 P:^__FFB3\r P:^__FF56\r P:^__FFE0\r P:^__FF9B\r A:4 P:^__FFB4\r P:^__FF51\r P:^__FFDB\r P:^__FF96\r A:5 P:^__FFB5\r P:^__FF80\r P:^__FFDC\r P:^__FF9D\r A:6 P:^__FFB6\r P:^__FF53\r P:^__FFDD\r P:^__FF98\r A:7 P:^__FFB7\r P:^__FF50\r P:^__FFD8\r P:^__FF95\r A:8 P:^__FFB8\r P:^__FF52\r P:^__FFD9\r P:^__FF97\r A:9 P:^__FFB9\r P:^__FF55\r P:^__FFDA\r P:^__FF9A\r # Shift-Keypad (0-9) A:\e\e\e\e\\.0 P:^_S_FFB0\r P:^_S_FF63\r P:^_S_????\r P:^_S_FF9E\r A:\e\e\e\e\\.1 P:^_S_FFB1\r P:^_S_FF57\r P:^_S_FFDE\r P:^_S_FF9C\r A:\e\e\e\e\\.2 P:^_S_FFB2\r P:^_S_FF54\r P:^_S_FFDF\r P:^_S_FF99\r A:\e\e\e\e\\.3 P:^_S_FFB3\r P:^_S_FF56\r P:^_S_FFE0\r P:^_S_FF9B\r A:\e\e\e\e\\.4 P:^_S_FFB4\r P:^_S_FF51\r P:^_S_FFDB\r P:^_S_FF96\r A:\e\e\e\e\\.5 P:^_S_FFB5\r P:^_S_FF80\r P:^_S_FFDC\r P:^_S_????\r A:\e\e\e\e\\.6 P:^_S_FFB6\r P:^_S_FF53\r P:^_S_FFDD\r P:^_S_FF98\r A:\e\e\e\e\\.7 P:^_S_FFB7\r P:^_S_FF50\r P:^_S_FFD8\r P:^_S_FF95\r A:\e\e\e\e\\.8 P:^_S_FFB8\r P:^_S_FF52\r P:^_S_FFD9\r P:^_S_FF97\r A:\e\e\e\e\\.9 P:^_S_FFB9\r P:^_S_FF55\r P:^_S_FFDA\r P:^_S_FF9A\r # Control-Keypad (0-9) A:\e\e\e\e\\+0 P:^_N_FFB0\r P:^_N_FF63\r P:^_N_????\r P:^_N_FF9E\r A:\e\e\e\e\\+1 P:^_N_FFB1\r P:^_N_FF57\r P:^_N_FFDE\r P:^_N_FF9C\r A:\e\e\e\e\\+2 P:^_N_FFB2\r P:^_N_FF54\r P:^_N_FFDF\r P:^_N_FF99\r A:\e\e\e\e\\+3 P:^_N_FFB3\r P:^_N_FF56\r P:^_N_FFE0\r P:^_N_FF9B\r A:\e\e\e\e\\+4 P:^_N_FFB4\r P:^_N_FF51\r P:^_N_FFDB\r P:^_N_FF96\r A:\e\e\e\e\\+5 P:^_N_FFB5\r P:^_N_FF80\r P:^_N_FFDC\r P:^_N_????\r A:\e\e\e\e\\+6 P:^_N_FFB6\r P:^_N_FF53\r P:^_N_FFDD\r P:^_N_FF98\r A:\e\e\e\e\\+7 P:^_N_FFB7\r P:^_N_FF50\r P:^_N_FFD8\r P:^_N_FF95\r A:\e\e\e\e\\+8 P:^_N_FFB8\r P:^_N_FF52\r P:^_N_FFD9\r P:^_N_FF97\r A:\e\e\e\e\\+9 P:^_N_FFB9\r P:^_N_FF55\r P:^_N_FFDA\r P:^_N_FF9A\r zangband/lib/pref/pref-key.prf0000644000000000000000000000324110250356274015301 0ustar rootroot# File: pref-key.prf # # This file defines "default" keysets # # This file includes, if appropriate, various "sub-files" # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Original Keyset Mappings ##### # Stay still A:, C:0:5 # Movement A:;1 C:0:1 A:;2 C:0:2 A:;3 C:0:3 A:;4 C:0:4 A:;6 C:0:6 A:;7 C:0:7 A:;8 C:0:8 A:;9 C:0:9 # Hack -- Return A:\r C:0:^J # Hack -- Commit suicide A:Q C:0:^K # Hack -- Commit suicide A:Q C:0:^C # Hack -- swap equipment A:w0 C:0:X ##### Roguelike Keyset Mappings ##### # Run A:, C:1:. # Stay still A:. C:1:, # Stay still A:. C:1:5 # Movement A:;1 C:1:1 A:;2 C:1:2 A:;3 C:1:3 A:;4 C:1:4 A:;6 C:1:6 A:;7 C:1:7 A:;8 C:1:8 A:;9 C:1:9 # Movement (rogue keys) A:;1 C:1:b A:;2 C:1:j A:;3 C:1:n A:;4 C:1:h A:;6 C:1:l A:;7 C:1:y A:;8 C:1:k A:;9 C:1:u # Running (shift + rogue keys) A:.1 C:1:B A:.2 C:1:J A:.3 C:1:N A:.4 C:1:H A:.6 C:1:L A:.7 C:1:Y A:.8 C:1:K A:.9 C:1:U # Altering (control + rogue keys) A:+1 C:1:^B A:+2 C:1:^J A:+3 C:1:^N A:+4 C:1:^H A:+6 C:1:^L A:+7 C:1:^Y A:+8 C:1:^K A:+9 C:1:^U # Allow use of the "tunnel" command A:T C:1:^T # Allow use of the "destroy" command A:k C:1:^D # Locate player on map A:L C:1:W # Browse a book (Peruse) A:b C:1:P # Jam a door (Spike) A:j C:1:S # Toggle search mode A:S C:1:# # Use a staff (Zap) A:u C:1:Z # Take off equipment A:t C:1:T # Fire an item A:f C:1:t # Bash a door (Force) A:B C:1:f # Look around (examine) A:l C:1:x # Aim a wand (Zap) A:a C:1:z # Zap a rod (Activate) A:z C:1:a # Hack -- Commit suicide A:Q C:1:^C # Hack -- swap equipment A:w0 C:1:X # Hack - race/mutation power activation A:U C:1:O # Hack - race/mutation power activation A:^t C:1:' zangband/lib/pref/pref-mac.prf0000644000000000000000000000266510250356274015262 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2004/08/03 21:31:51 $ # File: pref-mac.prf # # This file is included by "pref.prf" when "main-mac.c" is used. # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Simple Macros ##### # # Shift-Keypad -- Directed running # A:\e\e\\.1 P:^_S19\r A:\e\e\\.2 P:^_S20\r A:\e\e\\.3 P:^_S21\r A:\e\e\\.4 P:^_S22\r A:\e\e\\.5 P:^_S23\r A:\e\e\\.6 P:^_S24\r A:\e\e\\.7 P:^_S25\r A:\e\e\\.8 P:^_S27\r A:\e\e\\.9 P:^_S28\r # # Control-Keypad -- Directed tunneling # A:\e\e\\+1 P:^_C19\r A:\e\e\\+2 P:^_C20\r A:\e\e\\+3 P:^_C21\r A:\e\e\\+4 P:^_C22\r A:\e\e\\+5 P:^_C23\r A:\e\e\\+6 P:^_C24\r A:\e\e\\+7 P:^_C25\r A:\e\e\\+8 P:^_C27\r A:\e\e\\+9 P:^_C28\r # # Option-Control-Keypad -- wield {@0} and tunnel # A:\e\ew0\s\s\\+1 P:^_CO19\r A:\e\ew0\s\s\\+2 P:^_CO20\r A:\e\ew0\s\s\\+3 P:^_CO21\r A:\e\ew0\s\s\\+4 P:^_CO22\r A:\e\ew0\s\s\\+5 P:^_CO23\r A:\e\ew0\s\s\\+6 P:^_CO24\r A:\e\ew0\s\s\\+7 P:^_CO25\r A:\e\ew0\s\s\\+8 P:^_CO27\r A:\e\ew0\s\s\\+9 P:^_CO28\r # # Option-Control-Keypad-Zero -- wield {@0} # A:\e\ew0\s P:^_CO18\r # # Hack -- Arrow-Keys # A:4 P:^_59\r A:6 P:^_60\r A:2 P:^_61\r A:8 P:^_62\r # # Keypad -- (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.) # A:/ P:^_K/\r A:* P:^_K*\r A:7 P:^_K7\r A:8 P:^_K8\r A:9 P:^_K9\r A:- P:^_K-\r A:4 P:^_K4\r A:5 P:^_K5\r A:6 P:^_K6\r A:+ P:^_K+\r A:1 P:^_K1\r A:2 P:^_K2\r A:3 P:^_K3\r A:0 P:^_K0\r A:. P:^_K.\r zangband/lib/pref/pref-tnb.prf0000644000000000000000000000203610250356274015275 0ustar rootroot# File: pref-tk-x11.prf # # Control + Keypad # A:\e\e\e\\+1 P:^_Control-KP_End\r A:\e\e\e\\+2 P:^_Control-KP_Down\r A:\e\e\e\\+3 P:^_Control-KP_Next\r A:\e\e\e\\+4 P:^_Control-KP_Left\r A:\e\e\e\\+6 P:^_Control-KP_Right\r A:\e\e\e\\+7 P:^_Control-KP_Home\r A:\e\e\e\\+8 P:^_Control-KP_Up\r A:\e\e\e\\+9 P:^_Control-KP_Prior\r # # Shift + Keypad # A:\e\e\e\\.1 P:^_Shift-KP_1\r A:\e\e\e\\.2 P:^_Shift-KP_2\r A:\e\e\e\\.3 P:^_Shift-KP_3\r A:\e\e\e\\.4 P:^_Shift-KP_4\r A:\e\e\e\\.6 P:^_Shift-KP_6\r A:\e\e\e\\.7 P:^_Shift-KP_7\r A:\e\e\e\\.8 P:^_Shift-KP_8\r A:\e\e\e\\.9 P:^_Shift-KP_9\r A:\e\e\\+1 P:^_Control-1\r A:\e\e\\+2 P:^_Control-2\r A:\e\e\\+3 P:^_Control-3\r A:\e\e\\+4 P:^_Control-4\r A:\e\e\\+6 P:^_Control-6\r A:\e\e\\+7 P:^_Control-7\r A:\e\e\\+8 P:^_Control-8\r A:\e\e\\+9 P:^_Control-9\r # # Shift + Keypad # A:\e\e\e\\.1 P:^_End\r A:\e\e\e\\.2 P:^_Down\r A:\e\e\e\\.3 P:^_Next\r A:\e\e\e\\.4 P:^_Left\r A:\e\e\e\\.6 P:^_Right\r A:\e\e\e\\.7 P:^_Home\r A:\e\e\e\\.8 P:^_Up\r A:\e\e\e\\.9 P:^_Prior\r zangband/lib/pref/pref-win.prf0000644000000000000000000000563110250356274015313 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: pref-ibm.prf # # This file is used by Angband (when it was compiled using "main-ibm.c" # or "main-dos.c" or "main-win.c") to specify various "user preferences", # including "macros". # # This file defines some basic macros, which allow the use of the "keypad", # alone, and with the shift and/or control modifier keys. All "special" # keys are translated by "main-ibm.c" (or "main-win.c") into special "macro # triggers" of the encoded form "^_MMMxSS\r", where the "modifier" flags are # stored in "MMM", and the two digit hexidecimal scan code of the keypress is # stored in "SS". # # The "main-ibm.prf" and "main-dos.prf" files may not be able to recognize # the "/" and "*" keys on the keypad, because it mistakenly classifies the # "0x35" and "0x37" codes as the keycodes of "normal" keys. # # The "main-win.prf" file should not be using the final "control + keypad" # section in this file, it was created for "main-ibm.c" and "main-dos.c". # # The "main-win.prf" file may actually send the "ascii" equivalent of some # keypad keys after the keypad key itself, especially if "numlock" is down, # which may cause problems. Or it may not, it is hard to tell. This is bad. # # See "main-ibm.c" and "main-dos.c" and "main-win.c" for more info. # # # Hack -- Some foreign keyboards have a special key on the keyboard, which # is used to generate the "<", ">", and "|" keys (alone, shifted, alt-ed). # A:< P:^_x56\r A:> P:^_Sx56\r A:| P:^_Ax56\r # # Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.) # A:/ P:^_x35\r A:* P:^_x37\r A:7 P:^_x47\r A:8 P:^_x48\r A:9 P:^_x49\r A:- P:^_x4A\r A:4 P:^_x4B\r A:5 P:^_x4C\r A:6 P:^_x4D\r A:+ P:^_x4E\r A:1 P:^_x4F\r A:2 P:^_x50\r A:3 P:^_x51\r A:0 P:^_x52\r A:. P:^_x53\r # # Shift + Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.) # A:\e\e\e P:^_Sx35\r A:\e\e\e P:^_Sx37\r A:\e\e\\.7 P:^_Sx47\r A:\e\e\\.8 P:^_Sx48\r A:\e\e\\.9 P:^_Sx49\r A:\e\e\e P:^_Sx4A\r A:\e\e\\.4 P:^_Sx4B\r A:\e\e\\.5 P:^_Sx4C\r A:\e\e\\.6 P:^_Sx4D\r A:\e\e\e P:^_Sx4E\r A:\e\e\\.1 P:^_Sx4F\r A:\e\e\\.2 P:^_Sx50\r A:\e\e\\.3 P:^_Sx51\r A:\e\e\e P:^_Sx52\r A:\e\e\e P:^_Sx53\r # # Control + Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.) # A:\e\e\e P:^_Cx35\r A:\e\e\e P:^_Cx37\r A:\e\e\\+7 P:^_Cx47\r A:\e\e\\+8 P:^_Cx48\r A:\e\e\\+9 P:^_Cx49\r A:\e\e\e P:^_Cx4A\r A:\e\e\\+4 P:^_Cx4B\r A:\e\e\\+5 P:^_Cx4C\r A:\e\e\\+6 P:^_Cx4D\r A:\e\e\e P:^_Cx4E\r A:\e\e\\+1 P:^_Cx4F\r A:\e\e\\+2 P:^_Cx50\r A:\e\e\\+3 P:^_Cx51\r A:\e\e\e P:^_Cx52\r A:\e\e\e P:^_Cx53\r # # Control + Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.) # A:\e\e\e P:^_Cx95\r A:\e\e\e P:^_Cx96\r A:\e\e\\+7 P:^_Cx77\r A:\e\e\\+8 P:^_Cx8D\r A:\e\e\\+9 P:^_Cx84\r A:\e\e\e P:^_Cx8E\r A:\e\e\\+4 P:^_Cx73\r A:\e\e\\+5 P:^_Cx8F\r A:\e\e\\+6 P:^_Cx74\r A:\e\e\e P:^_Cx90\r A:\e\e\\+1 P:^_Cx75\r A:\e\e\\+2 P:^_Cx91\r A:\e\e\\+3 P:^_Cx76\r A:\e\e\e P:^_Cx92\r A:\e\e\e P:^_Cx93\r zangband/lib/pref/pref-x11.prf0000644000000000000000000000451210250356274015124 0ustar rootroot# File: pref-x11.prf # # This file provides some macros for use with versions of Angband compiled # using the "main-x11.c" (or "main-xaw.c") file. # # Note the use of "\e\e\e\e" (four escapes) to allow the macros to work # even if the game is not yet ready for a command. # # Note the use of "\\." (for "run") and "\\+" (for "alter"), to make sure # that the macros will work regardless of the "keymap" being used. # # Keypad (0-9) A:0 P:^__FFB0\r P:^__FF63\r P:^__????\r P:^__FF9E\r A:1 P:^__FFB1\r P:^__FF57\r P:^__FFDE\r P:^__FF9C\r A:2 P:^__FFB2\r P:^__FF54\r P:^__FFDF\r P:^__FF99\r A:3 P:^__FFB3\r P:^__FF56\r P:^__FFE0\r P:^__FF9B\r A:4 P:^__FFB4\r P:^__FF51\r P:^__FFDB\r P:^__FF96\r A:5 P:^__FFB5\r P:^__FF80\r P:^__FFDC\r P:^__FF9D\r A:6 P:^__FFB6\r P:^__FF53\r P:^__FFDD\r P:^__FF98\r A:7 P:^__FFB7\r P:^__FF50\r P:^__FFD8\r P:^__FF95\r A:8 P:^__FFB8\r P:^__FF52\r P:^__FFD9\r P:^__FF97\r A:9 P:^__FFB9\r P:^__FF55\r P:^__FFDA\r P:^__FF9A\r # Shift-Keypad (0-9) A:\e\e\e\e\\.0 P:^_S_FFB0\r P:^_S_FF63\r P:^_S_????\r P:^_S_FF9E\r A:\e\e\e\e\\.1 P:^_S_FFB1\r P:^_S_FF57\r P:^_S_FFDE\r P:^_S_FF9C\r A:\e\e\e\e\\.2 P:^_S_FFB2\r P:^_S_FF54\r P:^_S_FFDF\r P:^_S_FF99\r A:\e\e\e\e\\.3 P:^_S_FFB3\r P:^_S_FF56\r P:^_S_FFE0\r P:^_S_FF9B\r A:\e\e\e\e\\.4 P:^_S_FFB4\r P:^_S_FF51\r P:^_S_FFDB\r P:^_S_FF96\r A:\e\e\e\e\\.5 P:^_S_FFB5\r P:^_S_FF80\r P:^_S_FFDC\r P:^_S_????\r A:\e\e\e\e\\.6 P:^_S_FFB6\r P:^_S_FF53\r P:^_S_FFDD\r P:^_S_FF98\r A:\e\e\e\e\\.7 P:^_S_FFB7\r P:^_S_FF50\r P:^_S_FFD8\r P:^_S_FF95\r A:\e\e\e\e\\.8 P:^_S_FFB8\r P:^_S_FF52\r P:^_S_FFD9\r P:^_S_FF97\r A:\e\e\e\e\\.9 P:^_S_FFB9\r P:^_S_FF55\r P:^_S_FFDA\r P:^_S_FF9A\r # Control-Keypad (0-9) A:\e\e\e\e\\+0 P:^_N_FFB0\r P:^_N_FF63\r P:^_N_????\r P:^_N_FF9E\r A:\e\e\e\e\\+1 P:^_N_FFB1\r P:^_N_FF57\r P:^_N_FFDE\r P:^_N_FF9C\r A:\e\e\e\e\\+2 P:^_N_FFB2\r P:^_N_FF54\r P:^_N_FFDF\r P:^_N_FF99\r A:\e\e\e\e\\+3 P:^_N_FFB3\r P:^_N_FF56\r P:^_N_FFE0\r P:^_N_FF9B\r A:\e\e\e\e\\+4 P:^_N_FFB4\r P:^_N_FF51\r P:^_N_FFDB\r P:^_N_FF96\r A:\e\e\e\e\\+5 P:^_N_FFB5\r P:^_N_FF80\r P:^_N_FFDC\r P:^_N_????\r A:\e\e\e\e\\+6 P:^_N_FFB6\r P:^_N_FF53\r P:^_N_FFDD\r P:^_N_FF98\r A:\e\e\e\e\\+7 P:^_N_FFB7\r P:^_N_FF50\r P:^_N_FFD8\r P:^_N_FF95\r A:\e\e\e\e\\+8 P:^_N_FFB8\r P:^_N_FF52\r P:^_N_FFD9\r P:^_N_FF97\r A:\e\e\e\e\\+9 P:^_N_FFB9\r P:^_N_FF55\r P:^_N_FFDA\r P:^_N_FF9A\r zangband/lib/pref/pref.prf0000644000000000000000000000154010250356274014513 0ustar rootroot# File: pref.prf # # This file includes basic pref files. # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Keyset definitions ##### %:pref-key.prf ##### Option definitions ##### %:pref-opt.prf ##### Spell color definitions ##### %:spell-xx.prf ##### Message color definitions ##### %:message.prf ##### System Specific Subfiles ##### ?:[IOR [EQU $SYS xaw] [EQU $SYS x11] [EQU $SYS xpj]] %:pref-x11.prf ?:[EQU $SYS gtk] %:pref-gtk.prf ?:[IOR [EQU $SYS gcu] [EQU $SYS lsl] [EQU $SYS vcs]] %:pref-gcu.prf ?:[EQU $SYS ami] %:pref-ami.prf ?:[EQU $SYS mac] %:pref-mac.prf ?:[IOR [EQU $SYS win] [EQU $SYS dos] [EQU $SYS ibm] [EQU $SYS w2k]] %:pref-win.prf ?:[EQU $SYS emx] %:pref-emx.prf ?:[EQU $SYS acn] %:pref-acn.prf ?:[EQU $SYS tnb] %:pref-tnb.prf ?:1 zangband/lib/pref/spell-xx.prf0000644000000000000000000000277110250356274015342 0ustar rootroot# File: spell-xx.prf # # This file defines "default" colors to be used when drawing spells # # This file includes, if appropriate, various "sub-files" # # See "lib/help/command.txt" and "src/files.c" for more information. # Z:GF_ELEC:wwB Z:GF_POIS:gggG Z:GF_ACID:s Z:GF_COLD:w Z:GF_FIRE:roy Z:GF_MISSILE:DDDs Z:GF_ARROW:U Z:GF_PLASMA:wwwwbbBBv Z:GF_WATER:s Z:GF_LITE:wwwy Z:GF_DARK:D Z:GF_LITE_WEAK:wwwy Z:GF_DARK_WEAK:D Z:GF_SHARDS:DDDs Z:GF_SOUND:y Z:GF_CONFUSION:U Z:GF_FORCE:u Z:GF_INERTIA:W Z:GF_MANA:wwwwgG Z:GF_METEOR:DDDroy Z:GF_ICE:w Z:GF_CHAOS:wsorgbuDWvyRGBU Z:GF_NETHER:G Z:GF_DISENCHANT:wsorgbuDWvyRGBU Z:GF_NEXUS:RRRr Z:GF_TIME:B Z:GF_GRAVITY:ddDs Z:GF_KILL_WALL:W Z:GF_KILL_DOOR:U Z:GF_KILL_TRAP:R Z:GF_MAKE_WALL:W Z:GF_MAKE_DOOR:U Z:GF_MAKE_TRAP:R Z:GF_OLD_CLONE:B Z:GF_OLD_POLY:wsorgbuDWvyRGBU Z:GF_OLD_HEAL:ggGb Z:GF_OLD_SPEED:B Z:GF_OLD_SLOW:u Z:GF_OLD_CONF:U Z:GF_OLD_SLEEP:DDs Z:GF_OLD_DRAIN:G Z:GF_NEW_DRAIN:G Z:GF_AWAY_UNDEAD:uug Z:GF_AWAY_EVIL:WWw Z:GF_AWAY_ALL:wwwwbbBBv Z:GF_TURN_UNDEAD:uug Z:GF_TURN_EVIL:WWw Z:GF_TURN_ALL:wwwwbbBBv Z:GF_DISP_UNDEAD:uug Z:GF_DISP_EVIL:WWw Z:GF_DISP_ALL:wwwwbbBBv Z:GF_DISP_DEMON:RRro Z:GF_DISP_LIVING:GGg Z:GF_ROCKET:DDDroy Z:GF_NUKE:wwwroy Z:GF_MAKE_GLYPH:RRRr Z:GF_STASIS:B Z:GF_STONE_WALL:s Z:GF_DEATH_RAY:DDDDssG Z:GF_STUN:w Z:GF_HOLY_FIRE:DDDs Z:GF_HELL_FIRE:DDDssr Z:GF_DISINTEGRATE:D Z:GF_CHARM:BBb Z:GF_CONTROL_UNDEAD:uug Z:GF_CONTROL_ANIMAL:uugggr Z:GF_PSI:B Z:GF_PSI_DRAIN:B Z:GF_TELEKINESIS:B Z:GF_JAM_DOOR:w Z:GF_DOMINATION:DDDssr Z:GF_DISP_GOOD:DDDssr zangband/lib/pref/user-win.prf0000644000000000000000000000135510250356274015334 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # Color redefinitions # Color 'White' V:1:0x07:0xFF:0xFF:0xFF # Color 'Slate' V:2:0x03:0x90:0x90:0x90 # Color 'Orange' V:3:0x0C:0xFF:0x80:0x00 # Color 'Red' V:4:0x04:0xC0:0x00:0x00 # Color 'Green' V:5:0x02:0x00:0x80:0x40 # Color 'Blue' V:6:0x01:0x00:0x00:0xFF # Color 'Umber' V:7:0x06:0xA0:0x50:0x10 # Color 'Light Dark' V:8:0x08:0x70:0x70:0x70 # Color 'Light Slate' V:9:0x0B:0xD0:0xD0:0xD0 # Color 'Violet' V:10:0x05:0xFF:0x00:0xFF # Color 'Yellow' V:11:0x0E:0xFF:0xFF:0x00 # Color 'Light Red' V:12:0x0D:0xFF:0x00:0x00 # Color 'Light Green' V:13:0x0A:0x00:0xFF:0x00 # Color 'Light Blue' V:14:0x09:0x00:0xFF:0xFF # Color 'Light Umber' V:15:0x06:0xC0:0x80:0x40 zangband/lib/pref/user.prf0000644000000000000000000000146110250356274014537 0ustar rootroot# CVS: Last edit by $Author: sfuerst $ on $Date: 2003/03/08 14:38:44 $ # File: user.prf # # This file defines "override" actions of various kinds # # This file includes, if appropriate, various "sub-files" # # See "lib/help/command.txt" and "src/files.c" for more information. # ## Option -- Force the use of original commands #X:rogue_like_commands ## Option -- Force the use of roguelike commands #Y:rogue_like_commands ##### System Specific Subfiles ##### ?:[IOR [IOR [IOR [EQU $SYS xaw] [EQU $SYS x11]] [EQU $SYS xpj]] [EQU $SYS gtk]] %:user-x11.prf ?:[EQU $SYS gcu] %:user-gcu.prf ?:[EQU $SYS ami] %:user-ami.prf ?:[EQU $SYS mac] %:user-mac.prf ?:[IOR [EQU $SYS win] [EQU $SYS dos] [EQU $SYS ibm] [EQU $SYS w2k]] %:user-win.prf ?:[EQU $SYS emx] %:user-emx.prf ?:[EQU $SYS acn] %:user-acn.prf ?:1 zangband/lib/pref/xtra-gcu.prf0000644000000000000000000000112310250356274015306 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: xtra-gcu.prf # Rename this file to "pref-gcu.prf" to allow the VT100 cursor keys # to be recognized by Angband. This will also make the "escape" key # take a few seconds to recognize, so you may want to use the "`" key # instead, or make a macro from escape+escape to escape. ### VT100 Keypad ### # Special keypad keys (delete, insert) A:. P:\e[3~ A:0 P:\e[2~ # Numerical keypad keys (map to appropriate number) A:1 P:\e[4~ A:2 P:\e[B A:3 P:\e[6~ A:4 P:\e[D A:5 P:\e[G A:6 P:\e[C A:7 P:\e[1~ A:8 P:\e[A A:9 P:\e[5~ zangband/lib/pref/xtra-new.prf0000644000000000000000000005366110250356274015337 0ustar rootroot# CVS: Last edit by $Author: rr9 $ on $Date: 2001/05/23 11:30:34 $ # File: xtra-new.prf # # This file defines special attr/char mappings for use in "graphics" mode # # Edited for use with Adam Bolt's new graphics by Robert Ruehlmann < rr9@angband.org > # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Remap the player icon ##### ?:[AND [EQU $CLASS Warrior] [EQU $RACE Human] ] R:0:0x92/0x80 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Elf] ] R:0:0x92/0x81 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Elf] ] R:0:0x92/0x82 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Hobbit] ] R:0:0x92/0x83 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Gnome] ] R:0:0x92/0x84 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Dwarf] ] R:0:0x92/0x85 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Orc] ] R:0:0x92/0x86 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Troll] ] R:0:0x92/0x87 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Dunadan] ] R:0:0x92/0x88 ?:[AND [EQU $CLASS Warrior] [EQU $RACE High-Elf] ] R:0:0x92/0x89 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Amberite] ] R:0:0x92/0x8A ?:[AND [EQU $CLASS Warrior] [EQU $RACE Barbarian] ] R:0:0x92/0x8B ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Ogre] ] R:0:0x92/0x8C ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Giant] ] R:0:0x92/0x8D ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Titan] ] R:0:0x92/0x8E ?:[AND [EQU $CLASS Warrior] [EQU $RACE Cyclops] ] R:0:0x92/0x8F ?:[AND [EQU $CLASS Warrior] [EQU $RACE Yeek] ] R:0:0x92/0x90 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Klackon] ] R:0:0x92/0x91 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Kobold] ] R:0:0x92/0x92 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Nibelung] ] R:0:0x92/0x93 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Dark-Elf] ] R:0:0x92/0x94 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Draconian] ] R:0:0x92/0x95 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Mindflayer] ] R:0:0x92/0x96 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Imp] ] R:0:0x92/0x97 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Golem] ] R:0:0x92/0x98 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Skeleton] ] R:0:0x92/0x99 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Zombie] ] R:0:0x92/0x9A ?:[AND [EQU $CLASS Warrior] [EQU $RACE Vampire] ] R:0:0x92/0x9B ?:[AND [EQU $CLASS Warrior] [EQU $RACE Spectre] ] R:0:0x92/0x9C ?:[AND [EQU $CLASS Warrior] [EQU $RACE Sprite] ] R:0:0x92/0x9D ?:[AND [EQU $CLASS Warrior] [EQU $RACE Beastman] ] R:0:0x92/0x9E ?:[AND [EQU $CLASS Mage] [EQU $RACE Human] ] R:0:0x93/0x80 ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Elf] ] R:0:0x93/0x81 ?:[AND [EQU $CLASS Mage] [EQU $RACE Elf] ] R:0:0x93/0x82 ?:[AND [EQU $CLASS Mage] [EQU $RACE Hobbit] ] R:0:0x93/0x83 ?:[AND [EQU $CLASS Mage] [EQU $RACE Gnome] ] R:0:0x93/0x84 ?:[AND [EQU $CLASS Mage] [EQU $RACE Dwarf] ] R:0:0x93/0x85 ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Orc] ] R:0:0x93/0x86 ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Troll] ] R:0:0x93/0x87 ?:[AND [EQU $CLASS Mage] [EQU $RACE Dunadan] ] R:0:0x93/0x88 ?:[AND [EQU $CLASS Mage] [EQU $RACE High-Elf] ] R:0:0x93/0x89 ?:[AND [EQU $CLASS Mage] [EQU $RACE Amberite] ] R:0:0x93/0x8A ?:[AND [EQU $CLASS Mage] [EQU $RACE Barbarian] ] R:0:0x93/0x8B ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Ogre] ] R:0:0x93/0x8C ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Giant] ] R:0:0x93/0x8D ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Titan] ] R:0:0x93/0x8E ?:[AND [EQU $CLASS Mage] [EQU $RACE Cyclops] ] R:0:0x93/0x8F ?:[AND [EQU $CLASS Mage] [EQU $RACE Yeek] ] R:0:0x93/0x90 ?:[AND [EQU $CLASS Mage] [EQU $RACE Klackon] ] R:0:0x93/0x91 ?:[AND [EQU $CLASS Mage] [EQU $RACE Kobold] ] R:0:0x93/0x92 ?:[AND [EQU $CLASS Mage] [EQU $RACE Nibelung] ] R:0:0x93/0x93 ?:[AND [EQU $CLASS Mage] [EQU $RACE Dark-Elf] ] R:0:0x93/0x94 ?:[AND [EQU $CLASS Mage] [EQU $RACE Draconian] ] R:0:0x93/0x95 ?:[AND [EQU $CLASS Mage] [EQU $RACE Mindflayer] ] R:0:0x93/0x96 ?:[AND [EQU $CLASS Mage] [EQU $RACE Imp] ] R:0:0x93/0x97 ?:[AND [EQU $CLASS Mage] [EQU $RACE Golem] ] R:0:0x93/0x98 ?:[AND [EQU $CLASS Mage] [EQU $RACE Skeleton] ] R:0:0x93/0x99 ?:[AND [EQU $CLASS Mage] [EQU $RACE Zombie] ] R:0:0x93/0x9A ?:[AND [EQU $CLASS Mage] [EQU $RACE Vampire] ] R:0:0x93/0x9B ?:[AND [EQU $CLASS Mage] [EQU $RACE Spectre] ] R:0:0x93/0x9C ?:[AND [EQU $CLASS Mage] [EQU $RACE Sprite] ] R:0:0x93/0x9D ?:[AND [EQU $CLASS Mage] [EQU $RACE Beastman] ] R:0:0x93/0x9E ?:[AND [EQU $CLASS Priest] [EQU $RACE Human] ] R:0:0x94/0x80 ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Elf] ] R:0:0x94/0x81 ?:[AND [EQU $CLASS Priest] [EQU $RACE Elf] ] R:0:0x94/0x82 ?:[AND [EQU $CLASS Priest] [EQU $RACE Hobbit] ] R:0:0x94/0x83 ?:[AND [EQU $CLASS Priest] [EQU $RACE Gnome] ] R:0:0x94/0x84 ?:[AND [EQU $CLASS Priest] [EQU $RACE Dwarf] ] R:0:0x94/0x85 ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Orc] ] R:0:0x94/0x86 ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Troll] ] R:0:0x94/0x87 ?:[AND [EQU $CLASS Priest] [EQU $RACE Dunadan] ] R:0:0x94/0x88 ?:[AND [EQU $CLASS Priest] [EQU $RACE High-Elf] ] R:0:0x94/0x89 ?:[AND [EQU $CLASS Priest] [EQU $RACE Amberite] ] R:0:0x94/0x8A ?:[AND [EQU $CLASS Priest] [EQU $RACE Barbarian] ] R:0:0x94/0x8B ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Ogre] ] R:0:0x94/0x8C ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Giant] ] R:0:0x94/0x8D ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Titan] ] R:0:0x94/0x8E ?:[AND [EQU $CLASS Priest] [EQU $RACE Cyclops] ] R:0:0x94/0x8F ?:[AND [EQU $CLASS Priest] [EQU $RACE Yeek] ] R:0:0x94/0x90 ?:[AND [EQU $CLASS Priest] [EQU $RACE Klackon] ] R:0:0x94/0x91 ?:[AND [EQU $CLASS Priest] [EQU $RACE Kobold] ] R:0:0x94/0x92 ?:[AND [EQU $CLASS Priest] [EQU $RACE Nibelung] ] R:0:0x94/0x93 ?:[AND [EQU $CLASS Priest] [EQU $RACE Dark-Elf] ] R:0:0x94/0x94 ?:[AND [EQU $CLASS Priest] [EQU $RACE Draconian] ] R:0:0x94/0x95 ?:[AND [EQU $CLASS Priest] [EQU $RACE Mindflayer] ] R:0:0x94/0x96 ?:[AND [EQU $CLASS Priest] [EQU $RACE Imp] ] R:0:0x94/0x97 ?:[AND [EQU $CLASS Priest] [EQU $RACE Golem] ] R:0:0x94/0x98 ?:[AND [EQU $CLASS Priest] [EQU $RACE Skeleton] ] R:0:0x94/0x99 ?:[AND [EQU $CLASS Priest] [EQU $RACE Zombie] ] R:0:0x94/0x9A ?:[AND [EQU $CLASS Priest] [EQU $RACE Vampire] ] R:0:0x94/0x9B ?:[AND [EQU $CLASS Priest] [EQU $RACE Spectre] ] R:0:0x94/0x9C ?:[AND [EQU $CLASS Priest] [EQU $RACE Sprite] ] R:0:0x94/0x9D ?:[AND [EQU $CLASS Priest] [EQU $RACE Beastman] ] R:0:0x94/0x9E ?:[AND [EQU $CLASS Rogue] [EQU $RACE Human] ] R:0:0x95/0x80 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Elf] ] R:0:0x95/0x81 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Elf] ] R:0:0x95/0x82 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Hobbit] ] R:0:0x95/0x83 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Gnome] ] R:0:0x95/0x84 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Dwarf] ] R:0:0x95/0x85 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Orc] ] R:0:0x95/0x86 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Troll] ] R:0:0x95/0x87 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Dunadan] ] R:0:0x95/0x88 ?:[AND [EQU $CLASS Rogue] [EQU $RACE High-Elf] ] R:0:0x95/0x89 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Amberite] ] R:0:0x95/0x8A ?:[AND [EQU $CLASS Rogue] [EQU $RACE Barbarian] ] R:0:0x95/0x8B ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Ogre] ] R:0:0x95/0x8C ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Giant] ] R:0:0x95/0x8D ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Titan] ] R:0:0x95/0x8E ?:[AND [EQU $CLASS Rogue] [EQU $RACE Cyclops] ] R:0:0x95/0x8F ?:[AND [EQU $CLASS Rogue] [EQU $RACE Yeek] ] R:0:0x95/0x90 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Klackon] ] R:0:0x95/0x91 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Kobold] ] R:0:0x95/0x92 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Nibelung] ] R:0:0x95/0x93 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Dark-Elf] ] R:0:0x95/0x94 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Draconian] ] R:0:0x95/0x95 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Mindflayer] ] R:0:0x95/0x96 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Imp] ] R:0:0x95/0x97 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Golem] ] R:0:0x95/0x98 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Skeleton] ] R:0:0x95/0x99 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Zombie] ] R:0:0x95/0x9A ?:[AND [EQU $CLASS Rogue] [EQU $RACE Vampire] ] R:0:0x95/0x9B ?:[AND [EQU $CLASS Rogue] [EQU $RACE Spectre] ] R:0:0x95/0x9C ?:[AND [EQU $CLASS Rogue] [EQU $RACE Sprite] ] R:0:0x95/0x9D ?:[AND [EQU $CLASS Rogue] [EQU $RACE Beastman] ] R:0:0x95/0x9E ?:[AND [EQU $CLASS Ranger] [EQU $RACE Human] ] R:0:0x96/0x80 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Elf] ] R:0:0x96/0x81 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Elf] ] R:0:0x96/0x82 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Hobbit] ] R:0:0x96/0x83 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Gnome] ] R:0:0x96/0x84 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Dwarf] ] R:0:0x96/0x85 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Orc] ] R:0:0x96/0x86 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Troll] ] R:0:0x96/0x87 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Dunadan] ] R:0:0x96/0x88 ?:[AND [EQU $CLASS Ranger] [EQU $RACE High-Elf] ] R:0:0x96/0x89 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Amberite] ] R:0:0x96/0x8A ?:[AND [EQU $CLASS Ranger] [EQU $RACE Barbarian] ] R:0:0x96/0x8B ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Ogre] ] R:0:0x96/0x8C ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Giant] ] R:0:0x96/0x8D ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Titan] ] R:0:0x96/0x8E ?:[AND [EQU $CLASS Ranger] [EQU $RACE Cyclops] ] R:0:0x96/0x8F ?:[AND [EQU $CLASS Ranger] [EQU $RACE Yeek] ] R:0:0x96/0x90 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Klackon] ] R:0:0x96/0x91 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Kobold] ] R:0:0x96/0x92 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Nibelung] ] R:0:0x96/0x93 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Dark-Elf] ] R:0:0x96/0x94 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Draconian] ] R:0:0x96/0x95 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Mindflayer] ] R:0:0x96/0x96 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Imp] ] R:0:0x96/0x97 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Golem] ] R:0:0x96/0x98 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Skeleton] ] R:0:0x96/0x99 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Zombie] ] R:0:0x96/0x9A ?:[AND [EQU $CLASS Ranger] [EQU $RACE Vampire] ] R:0:0x96/0x9B ?:[AND [EQU $CLASS Ranger] [EQU $RACE Spectre] ] R:0:0x96/0x9C ?:[AND [EQU $CLASS Ranger] [EQU $RACE Sprite] ] R:0:0x96/0x9D ?:[AND [EQU $CLASS Ranger] [EQU $RACE Beastman] ] R:0:0x96/0x9E ?:[AND [EQU $CLASS Paladin] [EQU $RACE Human] ] R:0:0x97/0x80 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Elf] ] R:0:0x97/0x81 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Elf] ] R:0:0x97/0x82 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Hobbit] ] R:0:0x97/0x83 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Gnome] ] R:0:0x97/0x84 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Dwarf] ] R:0:0x97/0x85 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Orc] ] R:0:0x97/0x86 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Troll] ] R:0:0x97/0x87 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Dunadan] ] R:0:0x97/0x88 ?:[AND [EQU $CLASS Paladin] [EQU $RACE High-Elf] ] R:0:0x97/0x89 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Amberite] ] R:0:0x97/0x8A ?:[AND [EQU $CLASS Paladin] [EQU $RACE Barbarian] ] R:0:0x97/0x8B ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Ogre] ] R:0:0x97/0x8C ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Giant] ] R:0:0x97/0x8D ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Titan] ] R:0:0x97/0x8E ?:[AND [EQU $CLASS Paladin] [EQU $RACE Cyclops] ] R:0:0x97/0x8F ?:[AND [EQU $CLASS Paladin] [EQU $RACE Yeek] ] R:0:0x97/0x90 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Klackon] ] R:0:0x97/0x91 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Kobold] ] R:0:0x97/0x92 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Nibelung] ] R:0:0x97/0x93 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Dark-Elf] ] R:0:0x97/0x94 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Draconian] ] R:0:0x97/0x95 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Mindflayer] ] R:0:0x97/0x96 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Imp] ] R:0:0x97/0x97 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Golem] ] R:0:0x97/0x98 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Skeleton] ] R:0:0x97/0x99 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Zombie] ] R:0:0x97/0x9A ?:[AND [EQU $CLASS Paladin] [EQU $RACE Vampire] ] R:0:0x97/0x9B ?:[AND [EQU $CLASS Paladin] [EQU $RACE Spectre] ] R:0:0x97/0x9C ?:[AND [EQU $CLASS Paladin] [EQU $RACE Sprite] ] R:0:0x97/0x9D ?:[AND [EQU $CLASS Paladin] [EQU $RACE Beastman] ] R:0:0x97/0x9E ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Human] ] R:0:0x98/0x80 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Elf] ] R:0:0x98/0x81 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Elf] ] R:0:0x98/0x82 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Hobbit] ] R:0:0x98/0x83 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Gnome] ] R:0:0x98/0x84 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dwarf] ] R:0:0x98/0x85 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Orc] ] R:0:0x98/0x86 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Troll] ] R:0:0x98/0x87 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dunadan] ] R:0:0x98/0x88 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE High-Elf] ] R:0:0x98/0x89 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Amberite] ] R:0:0x98/0x8A ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Barbarian] ] R:0:0x98/0x8B ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Ogre] ] R:0:0x98/0x8C ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Giant] ] R:0:0x98/0x8D ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Titan] ] R:0:0x98/0x8E ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Cyclops] ] R:0:0x98/0x8F ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Yeek] ] R:0:0x98/0x90 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Klackon] ] R:0:0x98/0x91 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Kobold] ] R:0:0x98/0x92 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Nibelung] ] R:0:0x98/0x93 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dark-Elf] ] R:0:0x98/0x94 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Draconian] ] R:0:0x98/0x95 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Mindflayer] ] R:0:0x98/0x96 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Imp] ] R:0:0x98/0x97 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Golem] ] R:0:0x98/0x98 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Skeleton] ] R:0:0x98/0x99 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Zombie] ] R:0:0x98/0x9A ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Vampire] ] R:0:0x98/0x9B ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Spectre] ] R:0:0x98/0x9C ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Sprite] ] R:0:0x98/0x9D ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Beastman] ] R:0:0x98/0x9E ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Human] ] R:0:0x99/0x80 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Elf] ] R:0:0x99/0x81 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Elf] ] R:0:0x99/0x82 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Hobbit] ] R:0:0x99/0x83 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Gnome] ] R:0:0x99/0x84 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dwarf] ] R:0:0x99/0x85 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Orc] ] R:0:0x99/0x86 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Troll] ] R:0:0x99/0x87 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dunadan] ] R:0:0x99/0x88 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE High-Elf] ] R:0:0x99/0x89 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Amberite] ] R:0:0x99/0x8A ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Barbarian] ] R:0:0x99/0x8B ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Ogre] ] R:0:0x99/0x8C ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Giant] ] R:0:0x99/0x8D ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Titan] ] R:0:0x99/0x8E ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Cyclops] ] R:0:0x99/0x8F ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Yeek] ] R:0:0x99/0x90 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Klackon] ] R:0:0x99/0x91 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Kobold] ] R:0:0x99/0x92 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Nibelung] ] R:0:0x99/0x93 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dark-Elf] ] R:0:0x99/0x94 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Draconian] ] R:0:0x99/0x95 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Mindflayer] ] R:0:0x99/0x96 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Imp] ] R:0:0x99/0x97 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Golem] ] R:0:0x99/0x98 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Skeleton] ] R:0:0x99/0x99 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Zombie] ] R:0:0x99/0x9A ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Vampire] ] R:0:0x99/0x9B ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Spectre] ] R:0:0x99/0x9C ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Sprite] ] R:0:0x99/0x9D ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Beastman] ] R:0:0x99/0x9E ?:[AND [EQU $CLASS Monk] [EQU $RACE Human] ] R:0:0x9A/0x80 ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Elf] ] R:0:0x9A/0x81 ?:[AND [EQU $CLASS Monk] [EQU $RACE Elf] ] R:0:0x9A/0x82 ?:[AND [EQU $CLASS Monk] [EQU $RACE Hobbit] ] R:0:0x9A/0x83 ?:[AND [EQU $CLASS Monk] [EQU $RACE Gnome] ] R:0:0x9A/0x84 ?:[AND [EQU $CLASS Monk] [EQU $RACE Dwarf] ] R:0:0x9A/0x85 ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Orc] ] R:0:0x9A/0x86 ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Troll] ] R:0:0x9A/0x87 ?:[AND [EQU $CLASS Monk] [EQU $RACE Dunadan] ] R:0:0x9A/0x88 ?:[AND [EQU $CLASS Monk] [EQU $RACE High-Elf] ] R:0:0x9A/0x89 ?:[AND [EQU $CLASS Monk] [EQU $RACE Amberite] ] R:0:0x9A/0x8A ?:[AND [EQU $CLASS Monk] [EQU $RACE Barbarian] ] R:0:0x9A/0x8B ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Ogre] ] R:0:0x9A/0x8C ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Giant] ] R:0:0x9A/0x8D ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Titan] ] R:0:0x9A/0x8E ?:[AND [EQU $CLASS Monk] [EQU $RACE Cyclops] ] R:0:0x9A/0x8F ?:[AND [EQU $CLASS Monk] [EQU $RACE Yeek] ] R:0:0x9A/0x90 ?:[AND [EQU $CLASS Monk] [EQU $RACE Klackon] ] R:0:0x9A/0x91 ?:[AND [EQU $CLASS Monk] [EQU $RACE Kobold] ] R:0:0x9A/0x92 ?:[AND [EQU $CLASS Monk] [EQU $RACE Nibelung] ] R:0:0x9A/0x93 ?:[AND [EQU $CLASS Monk] [EQU $RACE Dark-Elf] ] R:0:0x9A/0x94 ?:[AND [EQU $CLASS Monk] [EQU $RACE Draconian] ] R:0:0x9A/0x95 ?:[AND [EQU $CLASS Monk] [EQU $RACE Mindflayer] ] R:0:0x9A/0x96 ?:[AND [EQU $CLASS Monk] [EQU $RACE Imp] ] R:0:0x9A/0x97 ?:[AND [EQU $CLASS Monk] [EQU $RACE Golem] ] R:0:0x9A/0x98 ?:[AND [EQU $CLASS Monk] [EQU $RACE Skeleton] ] R:0:0x9A/0x99 ?:[AND [EQU $CLASS Monk] [EQU $RACE Zombie] ] R:0:0x9A/0x9A ?:[AND [EQU $CLASS Monk] [EQU $RACE Vampire] ] R:0:0x9A/0x9B ?:[AND [EQU $CLASS Monk] [EQU $RACE Spectre] ] R:0:0x9A/0x9C ?:[AND [EQU $CLASS Monk] [EQU $RACE Sprite] ] R:0:0x9A/0x9D ?:[AND [EQU $CLASS Monk] [EQU $RACE Beastman] ] R:0:0x9A/0x9E ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Human] ] R:0:0x9B/0x80 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Elf] ] R:0:0x9B/0x81 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Elf] ] R:0:0x9B/0x82 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Hobbit] ] R:0:0x9B/0x83 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Gnome] ] R:0:0x9B/0x84 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dwarf] ] R:0:0x9B/0x85 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Orc] ] R:0:0x9B/0x86 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Troll] ] R:0:0x9B/0x87 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dunadan] ] R:0:0x9B/0x88 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE High-Elf] ] R:0:0x9B/0x89 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Amberite] ] R:0:0x9B/0x8A ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Barbarian] ] R:0:0x9B/0x8B ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Ogre] ] R:0:0x9B/0x8C ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Giant] ] R:0:0x9B/0x8D ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Titan] ] R:0:0x9B/0x8E ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Cyclops] ] R:0:0x9B/0x8F ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Yeek] ] R:0:0x9B/0x90 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Klackon] ] R:0:0x9B/0x91 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Kobold] ] R:0:0x9B/0x92 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Nibelung] ] R:0:0x9B/0x93 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dark-Elf] ] R:0:0x9B/0x94 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Draconian] ] R:0:0x9B/0x95 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Mindflayer] ] R:0:0x9B/0x96 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Imp] ] R:0:0x9B/0x97 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Golem] ] R:0:0x9B/0x98 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Skeleton] ] R:0:0x9B/0x99 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Zombie] ] R:0:0x9B/0x9A ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Vampire] ] R:0:0x9B/0x9B ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Spectre] ] R:0:0x9B/0x9C ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Sprite] ] R:0:0x9B/0x9D ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Beastman] ] R:0:0x9B/0x9E ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Human] ] R:0:0x9C/0x80 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Elf] ] R:0:0x9C/0x81 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Elf] ] R:0:0x9C/0x82 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Hobbit] ] R:0:0x9C/0x83 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Gnome] ] R:0:0x9C/0x84 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dwarf] ] R:0:0x9C/0x85 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Orc] ] R:0:0x9C/0x86 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Troll] ] R:0:0x9C/0x87 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dunadan] ] R:0:0x9C/0x88 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE High-Elf] ] R:0:0x9C/0x89 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Amberite] ] R:0:0x9C/0x8A ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Barbarian] ] R:0:0x9C/0x8B ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Ogre] ] R:0:0x9C/0x8C ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Giant] ] R:0:0x9C/0x8D ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Titan] ] R:0:0x9C/0x8E ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Cyclops] ] R:0:0x9C/0x8F ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Yeek] ] R:0:0x9C/0x90 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Klackon] ] R:0:0x9C/0x91 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Kobold] ] R:0:0x9C/0x92 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Nibelung] ] R:0:0x9C/0x93 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dark-Elf] ] R:0:0x9C/0x94 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Draconian] ] R:0:0x9C/0x95 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Mindflayer] ] R:0:0x9C/0x96 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Imp] ] R:0:0x9C/0x97 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Golem] ] R:0:0x9C/0x98 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Skeleton] ] R:0:0x9C/0x99 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Zombie] ] R:0:0x9C/0x9A ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Vampire] ] R:0:0x9C/0x9B ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Spectre] ] R:0:0x9C/0x9C ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Sprite] ] R:0:0x9C/0x9D ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Beastman] ] R:0:0x9C/0x9E zangband/lib/pref/xtra-xxx.prf0000644000000000000000000005211510250356274015366 0ustar rootroot# File: xtra-xxx.prf # # This file defines special attr/char mappings for use in "graphics" mode # # See "lib/help/command.txt" and "src/files.c" for more information. # ##### Remap the player icon ##### ?:[AND [EQU $CLASS Warrior] [EQU $RACE Human] ] R:0:0xA4/0x80 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Elf] ] R:0:0xA4/0x81 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Elf] ] R:0:0xA4/0x82 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Hobbit] ] R:0:0xA4/0x83 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Gnome] ] R:0:0xA4/0x84 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Dwarf] ] R:0:0xA4/0x85 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Orc] ] R:0:0xA4/0x86 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Troll] ] R:0:0xA4/0x87 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Amberite] ] R:0:0xA4/0x88 ?:[AND [EQU $CLASS Warrior] [EQU $RACE High-Elf] ] R:0:0xA4/0x89 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Barbarian] ] R:0:0xA4/0x8A ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Ogre] ] R:0:0xA4/0x8B ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Giant] ] R:0:0xA4/0x8C ?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Titan] ] R:0:0xA4/0x8D ?:[AND [EQU $CLASS Warrior] [EQU $RACE Cyclops] ] R:0:0xA4/0x8E ?:[AND [EQU $CLASS Warrior] [EQU $RACE Yeek] ] R:0:0xA4/0x8F ?:[AND [EQU $CLASS Warrior] [EQU $RACE Klackon] ] R:0:0xA4/0x90 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Kobold] ] R:0:0xA4/0x91 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Nibelung] ] R:0:0xA4/0x92 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Dark-Elf] ] R:0:0xA4/0x93 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Draconian] ] R:0:0xA4/0x94 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Mindflayer] ] R:0:0xA4/0x95 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Imp] ] R:0:0xA4/0x96 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Golem] ] R:0:0xA4/0x97 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Skeleton] ] R:0:0xA4/0x98 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Zombie] ] R:0:0xA4/0x99 ?:[AND [EQU $CLASS Warrior] [EQU $RACE Vampire] ] R:0:0xA4/0x9A ?:[AND [EQU $CLASS Warrior] [EQU $RACE Spectre] ] R:0:0xA4/0x9B ?:[AND [EQU $CLASS Warrior] [EQU $RACE Sprite] ] R:0:0xA4/0x9C ?:[AND [EQU $CLASS Warrior] [EQU $RACE Beastman] ] R:0:0xA4/0x9D ?:[AND [EQU $CLASS Mage] [EQU $RACE Human] ] R:0:0xA5/0x80 ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Elf] ] R:0:0xA5/0x81 ?:[AND [EQU $CLASS Mage] [EQU $RACE Elf] ] R:0:0xA5/0x82 ?:[AND [EQU $CLASS Mage] [EQU $RACE Hobbit] ] R:0:0xA5/0x83 ?:[AND [EQU $CLASS Mage] [EQU $RACE Gnome] ] R:0:0xA5/0x84 ?:[AND [EQU $CLASS Mage] [EQU $RACE Dwarf] ] R:0:0xA5/0x85 ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Orc] ] R:0:0xA5/0x86 ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Troll] ] R:0:0xA5/0x87 ?:[AND [EQU $CLASS Mage] [EQU $RACE Amberite] ] R:0:0xA5/0x88 ?:[AND [EQU $CLASS Mage] [EQU $RACE High-Elf] ] R:0:0xA5/0x89 ?:[AND [EQU $CLASS Mage] [EQU $RACE Barbarian] ] R:0:0xA5/0x8A ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Ogre] ] R:0:0xA5/0x8B ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Giant] ] R:0:0xA5/0x8C ?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Titan] ] R:0:0xA5/0x8D ?:[AND [EQU $CLASS Mage] [EQU $RACE Cyclops] ] R:0:0xA5/0x8E ?:[AND [EQU $CLASS Mage] [EQU $RACE Yeek] ] R:0:0xA5/0x8F ?:[AND [EQU $CLASS Mage] [EQU $RACE Klackon] ] R:0:0xA5/0x90 ?:[AND [EQU $CLASS Mage] [EQU $RACE Kobold] ] R:0:0xA5/0x91 ?:[AND [EQU $CLASS Mage] [EQU $RACE Nibelung] ] R:0:0xA5/0x92 ?:[AND [EQU $CLASS Mage] [EQU $RACE Dark-Elf] ] R:0:0xA5/0x93 ?:[AND [EQU $CLASS Mage] [EQU $RACE Draconian] ] R:0:0xA5/0x94 ?:[AND [EQU $CLASS Mage] [EQU $RACE Mindflayer] ] R:0:0xA5/0x95 ?:[AND [EQU $CLASS Mage] [EQU $RACE Imp] ] R:0:0xA5/0x96 ?:[AND [EQU $CLASS Mage] [EQU $RACE Golem] ] R:0:0xA5/0x97 ?:[AND [EQU $CLASS Mage] [EQU $RACE Skeleton] ] R:0:0xA5/0x98 ?:[AND [EQU $CLASS Mage] [EQU $RACE Zombie] ] R:0:0xA5/0x99 ?:[AND [EQU $CLASS Mage] [EQU $RACE Vampire] ] R:0:0xA5/0x9A ?:[AND [EQU $CLASS Mage] [EQU $RACE Spectre] ] R:0:0xA5/0x9B ?:[AND [EQU $CLASS Mage] [EQU $RACE Sprite] ] R:0:0xA5/0x9C ?:[AND [EQU $CLASS Mage] [EQU $RACE Beastman] ] R:0:0xA5/0x9D ?:[AND [EQU $CLASS Priest] [EQU $RACE Human] ] R:0:0xA6/0x80 ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Elf] ] R:0:0xA6/0x81 ?:[AND [EQU $CLASS Priest] [EQU $RACE Elf] ] R:0:0xA6/0x82 ?:[AND [EQU $CLASS Priest] [EQU $RACE Hobbit] ] R:0:0xA6/0x83 ?:[AND [EQU $CLASS Priest] [EQU $RACE Gnome] ] R:0:0xA6/0x84 ?:[AND [EQU $CLASS Priest] [EQU $RACE Dwarf] ] R:0:0xA6/0x85 ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Orc] ] R:0:0xA6/0x86 ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Troll] ] R:0:0xA6/0x87 ?:[AND [EQU $CLASS Priest] [EQU $RACE Amberite] ] R:0:0xA6/0x88 ?:[AND [EQU $CLASS Priest] [EQU $RACE High-Elf] ] R:0:0xA6/0x89 ?:[AND [EQU $CLASS Priest] [EQU $RACE Barbarian] ] R:0:0xA6/0x8A ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Ogre] ] R:0:0xA6/0x8B ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Giant] ] R:0:0xA6/0x8C ?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Titan] ] R:0:0xA6/0x8D ?:[AND [EQU $CLASS Priest] [EQU $RACE Cyclops] ] R:0:0xA6/0x8E ?:[AND [EQU $CLASS Priest] [EQU $RACE Yeek] ] R:0:0xA6/0x8F ?:[AND [EQU $CLASS Priest] [EQU $RACE Klackon] ] R:0:0xA6/0x90 ?:[AND [EQU $CLASS Priest] [EQU $RACE Kobold] ] R:0:0xA6/0x91 ?:[AND [EQU $CLASS Priest] [EQU $RACE Nibelung] ] R:0:0xA6/0x92 ?:[AND [EQU $CLASS Priest] [EQU $RACE Dark-Elf] ] R:0:0xA6/0x93 ?:[AND [EQU $CLASS Priest] [EQU $RACE Draconian] ] R:0:0xA6/0x94 ?:[AND [EQU $CLASS Priest] [EQU $RACE Mindflayer] ] R:0:0xA6/0x95 ?:[AND [EQU $CLASS Priest] [EQU $RACE Imp] ] R:0:0xA6/0x96 ?:[AND [EQU $CLASS Priest] [EQU $RACE Golem] ] R:0:0xA6/0x97 ?:[AND [EQU $CLASS Priest] [EQU $RACE Skeleton] ] R:0:0xA6/0x98 ?:[AND [EQU $CLASS Priest] [EQU $RACE Zombie] ] R:0:0xA6/0x99 ?:[AND [EQU $CLASS Priest] [EQU $RACE Vampire] ] R:0:0xA6/0x9A ?:[AND [EQU $CLASS Priest] [EQU $RACE Spectre] ] R:0:0xA6/0x9B ?:[AND [EQU $CLASS Priest] [EQU $RACE Sprite] ] R:0:0xA6/0x9C ?:[AND [EQU $CLASS Priest] [EQU $RACE Beastman] ] R:0:0xA6/0x9D ?:[AND [EQU $CLASS Rogue] [EQU $RACE Human] ] R:0:0xA7/0x80 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Elf] ] R:0:0xA7/0x81 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Elf] ] R:0:0xA7/0x82 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Hobbit] ] R:0:0xA7/0x83 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Gnome] ] R:0:0xA7/0x84 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Dwarf] ] R:0:0xA7/0x85 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Orc] ] R:0:0xA7/0x86 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Troll] ] R:0:0xA7/0x87 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Amberite] ] R:0:0xA7/0x88 ?:[AND [EQU $CLASS Rogue] [EQU $RACE High-Elf] ] R:0:0xA7/0x89 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Barbarian] ] R:0:0xA7/0x8A ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Ogre] ] R:0:0xA7/0x8B ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Giant] ] R:0:0xA7/0x8C ?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Titan] ] R:0:0xA7/0x8D ?:[AND [EQU $CLASS Rogue] [EQU $RACE Cyclops] ] R:0:0xA7/0x8E ?:[AND [EQU $CLASS Rogue] [EQU $RACE Yeek] ] R:0:0xA7/0x8F ?:[AND [EQU $CLASS Rogue] [EQU $RACE Klackon] ] R:0:0xA7/0x90 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Kobold] ] R:0:0xA7/0x91 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Nibelung] ] R:0:0xA7/0x92 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Dark-Elf] ] R:0:0xA7/0x93 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Draconian] ] R:0:0xA7/0x94 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Mindflayer] ] R:0:0xA7/0x95 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Imp] ] R:0:0xA7/0x96 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Golem] ] R:0:0xA7/0x97 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Skeleton] ] R:0:0xA7/0x98 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Zombie] ] R:0:0xA7/0x99 ?:[AND [EQU $CLASS Rogue] [EQU $RACE Vampire] ] R:0:0xA7/0x9A ?:[AND [EQU $CLASS Rogue] [EQU $RACE Spectre] ] R:0:0xA7/0x9B ?:[AND [EQU $CLASS Rogue] [EQU $RACE Sprite] ] R:0:0xA7/0x9C ?:[AND [EQU $CLASS Rogue] [EQU $RACE Beastman] ] R:0:0xA7/0x9D ?:[AND [EQU $CLASS Ranger] [EQU $RACE Human] ] R:0:0xA8/0x80 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Elf] ] R:0:0xA8/0x81 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Elf] ] R:0:0xA8/0x82 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Hobbit] ] R:0:0xA8/0x83 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Gnome] ] R:0:0xA8/0x84 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Dwarf] ] R:0:0xA8/0x85 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Orc] ] R:0:0xA8/0x86 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Troll] ] R:0:0xA8/0x87 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Amberite] ] R:0:0xA8/0x88 ?:[AND [EQU $CLASS Ranger] [EQU $RACE High-Elf] ] R:0:0xA8/0x89 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Barbarian] ] R:0:0xA8/0x8A ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Ogre] ] R:0:0xA8/0x8B ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Giant] ] R:0:0xA8/0x8C ?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Titan] ] R:0:0xA8/0x8D ?:[AND [EQU $CLASS Ranger] [EQU $RACE Cyclops] ] R:0:0xA8/0x8E ?:[AND [EQU $CLASS Ranger] [EQU $RACE Yeek] ] R:0:0xA8/0x8F ?:[AND [EQU $CLASS Ranger] [EQU $RACE Klackon] ] R:0:0xA8/0x90 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Kobold] ] R:0:0xA8/0x91 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Nibelung] ] R:0:0xA8/0x92 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Dark-Elf] ] R:0:0xA8/0x93 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Draconian] ] R:0:0xA8/0x94 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Mindflayer] ] R:0:0xA8/0x95 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Imp] ] R:0:0xA8/0x96 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Golem] ] R:0:0xA8/0x97 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Skeleton] ] R:0:0xA8/0x98 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Zombie] ] R:0:0xA8/0x99 ?:[AND [EQU $CLASS Ranger] [EQU $RACE Vampire] ] R:0:0xA8/0x9A ?:[AND [EQU $CLASS Ranger] [EQU $RACE Spectre] ] R:0:0xA8/0x9B ?:[AND [EQU $CLASS Ranger] [EQU $RACE Sprite] ] R:0:0xA8/0x9C ?:[AND [EQU $CLASS Ranger] [EQU $RACE Beastman] ] R:0:0xA8/0x9D ?:[AND [EQU $CLASS Paladin] [EQU $RACE Human] ] R:0:0xA9/0x80 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Elf] ] R:0:0xA9/0x81 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Elf] ] R:0:0xA9/0x82 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Hobbit] ] R:0:0xA9/0x83 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Gnome] ] R:0:0xA9/0x84 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Dwarf] ] R:0:0xA9/0x85 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Orc] ] R:0:0xA9/0x86 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Troll] ] R:0:0xA9/0x87 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Amberite] ] R:0:0xA9/0x88 ?:[AND [EQU $CLASS Paladin] [EQU $RACE High-Elf] ] R:0:0xA9/0x89 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Barbarian] ] R:0:0xA9/0x8A ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Ogre] ] R:0:0xA9/0x8B ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Giant] ] R:0:0xA9/0x8C ?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Titan] ] R:0:0xA9/0x8D ?:[AND [EQU $CLASS Paladin] [EQU $RACE Cyclops] ] R:0:0xA9/0x8E ?:[AND [EQU $CLASS Paladin] [EQU $RACE Yeek] ] R:0:0xA9/0x8F ?:[AND [EQU $CLASS Paladin] [EQU $RACE Klackon] ] R:0:0xA9/0x90 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Kobold] ] R:0:0xA9/0x91 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Nibelung] ] R:0:0xA9/0x92 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Dark-Elf] ] R:0:0xA9/0x93 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Draconian] ] R:0:0xA9/0x94 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Mindflayer] ] R:0:0xA9/0x95 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Imp] ] R:0:0xA9/0x96 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Golem] ] R:0:0xA9/0x97 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Skeleton] ] R:0:0xA9/0x98 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Zombie] ] R:0:0xA9/0x99 ?:[AND [EQU $CLASS Paladin] [EQU $RACE Vampire] ] R:0:0xA9/0x9A ?:[AND [EQU $CLASS Paladin] [EQU $RACE Spectre] ] R:0:0xA9/0x9B ?:[AND [EQU $CLASS Paladin] [EQU $RACE Sprite] ] R:0:0xA9/0x9C ?:[AND [EQU $CLASS Paladin] [EQU $RACE Beastman] ] R:0:0xA9/0x9D ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Human] ] R:0:0xAA/0x80 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Elf] ] R:0:0xAA/0x81 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Elf] ] R:0:0xAA/0x82 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Hobbit] ] R:0:0xAA/0x83 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Gnome] ] R:0:0xAA/0x84 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dwarf] ] R:0:0xAA/0x85 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Orc] ] R:0:0xAA/0x86 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Troll] ] R:0:0xAA/0x87 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Amberite] ] R:0:0xAA/0x88 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE High-Elf] ] R:0:0xAA/0x89 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Barbarian] ] R:0:0xAA/0x8A ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Ogre] ] R:0:0xAA/0x8B ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Giant] ] R:0:0xAA/0x8C ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Titan] ] R:0:0xAA/0x8D ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Cyclops] ] R:0:0xAA/0x8E ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Yeek] ] R:0:0xAA/0x8F ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Klackon] ] R:0:0xAA/0x90 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Kobold] ] R:0:0xAA/0x91 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Nibelung] ] R:0:0xAA/0x92 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dark-Elf] ] R:0:0xAA/0x93 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Draconian] ] R:0:0xAA/0x94 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Mindflayer] ] R:0:0xAA/0x95 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Imp] ] R:0:0xAA/0x96 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Golem] ] R:0:0xAA/0x97 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Skeleton] ] R:0:0xAA/0x98 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Zombie] ] R:0:0xAA/0x99 ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Vampire] ] R:0:0xAA/0x9A ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Spectre] ] R:0:0xAA/0x9B ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Sprite] ] R:0:0xAA/0x9C ?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Beastman] ] R:0:0xAA/0x9D ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Human] ] R:0:0xAB/0x80 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Elf] ] R:0:0xAB/0x81 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Elf] ] R:0:0xAB/0x82 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Hobbit] ] R:0:0xAB/0x83 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Gnome] ] R:0:0xAB/0x84 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dwarf] ] R:0:0xAB/0x85 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Orc] ] R:0:0xAB/0x86 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Troll] ] R:0:0xAB/0x87 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Amberite] ] R:0:0xAB/0x88 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE High-Elf] ] R:0:0xAB/0x89 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Barbarian] ] R:0:0xAB/0x8A ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Ogre] ] R:0:0xAB/0x8B ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Giant] ] R:0:0xAB/0x8C ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Titan] ] R:0:0xAB/0x8D ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Cyclops] ] R:0:0xAB/0x8E ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Yeek] ] R:0:0xAB/0x8F ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Klackon] ] R:0:0xAB/0x90 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Kobold] ] R:0:0xAB/0x91 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Nibelung] ] R:0:0xAB/0x92 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dark-Elf] ] R:0:0xAB/0x93 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Draconian] ] R:0:0xAB/0x94 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Mindflayer] ] R:0:0xAB/0x95 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Imp] ] R:0:0xAB/0x96 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Golem] ] R:0:0xAB/0x97 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Skeleton] ] R:0:0xAB/0x98 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Zombie] ] R:0:0xAB/0x99 ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Vampire] ] R:0:0xAB/0x9A ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Spectre] ] R:0:0xAB/0x9B ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Sprite] ] R:0:0xAB/0x9C ?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Beastman] ] R:0:0xAB/0x9D ?:[AND [EQU $CLASS Monk] [EQU $RACE Human] ] R:0:0xAC/0x80 ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Elf] ] R:0:0xAC/0x81 ?:[AND [EQU $CLASS Monk] [EQU $RACE Elf] ] R:0:0xAC/0x82 ?:[AND [EQU $CLASS Monk] [EQU $RACE Hobbit] ] R:0:0xAC/0x83 ?:[AND [EQU $CLASS Monk] [EQU $RACE Gnome] ] R:0:0xAC/0x84 ?:[AND [EQU $CLASS Monk] [EQU $RACE Dwarf] ] R:0:0xAC/0x85 ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Orc] ] R:0:0xAC/0x86 ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Troll] ] R:0:0xAC/0x87 ?:[AND [EQU $CLASS Monk] [EQU $RACE Amberite] ] R:0:0xAC/0x88 ?:[AND [EQU $CLASS Monk] [EQU $RACE High-Elf] ] R:0:0xAC/0x89 ?:[AND [EQU $CLASS Monk] [EQU $RACE Barbarian] ] R:0:0xAC/0x8A ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Ogre] ] R:0:0xAC/0x8B ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Giant] ] R:0:0xAC/0x8C ?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Titan] ] R:0:0xAC/0x8D ?:[AND [EQU $CLASS Monk] [EQU $RACE Cyclops] ] R:0:0xAC/0x8E ?:[AND [EQU $CLASS Monk] [EQU $RACE Yeek] ] R:0:0xAC/0x8F ?:[AND [EQU $CLASS Monk] [EQU $RACE Klackon] ] R:0:0xAC/0x90 ?:[AND [EQU $CLASS Monk] [EQU $RACE Kobold] ] R:0:0xAC/0x91 ?:[AND [EQU $CLASS Monk] [EQU $RACE Nibelung] ] R:0:0xAC/0x92 ?:[AND [EQU $CLASS Monk] [EQU $RACE Dark-Elf] ] R:0:0xAC/0x93 ?:[AND [EQU $CLASS Monk] [EQU $RACE Draconian] ] R:0:0xAC/0x94 ?:[AND [EQU $CLASS Monk] [EQU $RACE Mindflayer] ] R:0:0xAC/0x95 ?:[AND [EQU $CLASS Monk] [EQU $RACE Imp] ] R:0:0xAC/0x96 ?:[AND [EQU $CLASS Monk] [EQU $RACE Golem] ] R:0:0xAC/0x97 ?:[AND [EQU $CLASS Monk] [EQU $RACE Skeleton] ] R:0:0xAC/0x98 ?:[AND [EQU $CLASS Monk] [EQU $RACE Zombie] ] R:0:0xAC/0x99 ?:[AND [EQU $CLASS Monk] [EQU $RACE Vampire] ] R:0:0xAC/0x9A ?:[AND [EQU $CLASS Monk] [EQU $RACE Spectre] ] R:0:0xAC/0x9B ?:[AND [EQU $CLASS Monk] [EQU $RACE Sprite] ] R:0:0xAC/0x9C ?:[AND [EQU $CLASS Monk] [EQU $RACE Beastman] ] R:0:0xAC/0x9D ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Human] ] R:0:0xAD/0x80 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Elf] ] R:0:0xAD/0x81 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Elf] ] R:0:0xAD/0x82 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Hobbit] ] R:0:0xAD/0x83 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Gnome] ] R:0:0xAD/0x84 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dwarf] ] R:0:0xAD/0x85 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Orc] ] R:0:0xAD/0x86 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Troll] ] R:0:0xAD/0x87 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Amberite] ] R:0:0xAD/0x88 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE High-Elf] ] R:0:0xAD/0x89 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Barbarian] ] R:0:0xAD/0x8A ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Ogre] ] R:0:0xAD/0x8B ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Giant] ] R:0:0xAD/0x8C ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Titan] ] R:0:0xAD/0x8D ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Cyclops] ] R:0:0xAD/0x8E ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Yeek] ] R:0:0xAD/0x8F ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Klackon] ] R:0:0xAD/0x90 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Kobold] ] R:0:0xAD/0x91 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Nibelung] ] R:0:0xAD/0x92 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dark-Elf] ] R:0:0xAD/0x93 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Draconian] ] R:0:0xAD/0x94 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Mindflayer] ] R:0:0xAD/0x95 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Imp] ] R:0:0xAD/0x96 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Golem] ] R:0:0xAD/0x97 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Skeleton] ] R:0:0xAD/0x98 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Zombie] ] R:0:0xAD/0x99 ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Vampire] ] R:0:0xAD/0x9A ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Spectre] ] R:0:0xAD/0x9B ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Sprite] ] R:0:0xAD/0x9C ?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Beastman] ] R:0:0xAD/0x9D ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Human] ] R:0:0xAE/0x80 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Elf] ] R:0:0xAE/0x81 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Elf] ] R:0:0xAE/0x82 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Hobbit] ] R:0:0xAE/0x83 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Gnome] ] R:0:0xAE/0x84 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dwarf] ] R:0:0xAE/0x85 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Orc] ] R:0:0xAE/0x86 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Troll] ] R:0:0xAE/0x87 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Amberite] ] R:0:0xAE/0x88 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE High-Elf] ] R:0:0xAE/0x89 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Barbarian] ] R:0:0xAE/0x8A ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Ogre] ] R:0:0xAE/0x8B ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Giant] ] R:0:0xAE/0x8C ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Titan] ] R:0:0xAE/0x8D ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Cyclops] ] R:0:0xAE/0x8E ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Yeek] ] R:0:0xAE/0x8F ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Klackon] ] R:0:0xAE/0x90 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Kobold] ] R:0:0xAE/0x91 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Nibelung] ] R:0:0xAE/0x92 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dark-Elf] ] R:0:0xAE/0x93 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Draconian] ] R:0:0xAE/0x94 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Mindflayer] ] R:0:0xAE/0x95 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Imp] ] R:0:0xAE/0x96 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Golem] ] R:0:0xAE/0x97 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Skeleton] ] R:0:0xAE/0x98 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Zombie] ] R:0:0xAE/0x99 ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Vampire] ] R:0:0xAE/0x9A ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Spectre] ] R:0:0xAE/0x9B ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Sprite] ] R:0:0xAE/0x9C ?:[AND [EQU $CLASS High-Mage] [EQU $RACE Beastman] ] R:0:0xAE/0x9D zangband/lib/pref/makefile.zb0000644000000000000000000000134210250356274015160 0ustar rootrootsubdir = ./lib/pref/ ## makefile.zb srcfiles += lib/pref/makefile.zb files += $(addprefix lib/pref/,colors.prf \ font-ami.prf \ font-dos.prf \ font-ibm.prf \ font-mac.prf \ font-mon.prf \ font-win.prf \ font-x11.prf \ font-xxx.prf \ font.prf \ graf-ami.prf \ graf-dos.prf \ graf-gcu.prf \ graf-ibm.prf \ graf-lsl.prf \ graf-mac.prf \ graf-new.prf \ graf-win.prf \ graf-x11.prf \ graf-xaw.prf \ graf-xpj.prf \ graf-xxx.prf \ graf.prf \ message.prf \ pref-acn.prf \ pref-ami.prf \ pref-emx.prf \ pref-gcu.prf \ pref-gtk.prf \ pref-key.prf \ pref-mac.prf \ pref-tnb.prf \ pref-win.prf \ pref-x11.prf \ pref.prf \ spell-xx.prf \ user-win.prf \ user.prf \ xtra-gcu.prf \ xtra-new.prf \ xtra-xxx.prf) zangband/lib/save/0000755000000000000000000000000010250356274013050 5ustar rootrootzangband/lib/save/makefile.zb0000644000000000000000000000033410250356274015162 0ustar rootrootsubdir = ./lib/save/ ## makefile.zb srcfiles += lib/save/makefile.zb INSTALL += \ if [ -n "$(GAMEGROUP)" ]; then \ chown -R root:$(GAMEGROUP) "$(DESTDIR)/lib/save";\ chmod -R 070 "$(DESTDIR)/lib/save";\ fi; zangband/lib/script/0000755000000000000000000000000010250356274013416 5ustar rootrootzangband/lib/script/tk/0000755000000000000000000000000010250356274014034 5ustar rootrootzangband/lib/script/tk/config/0000755000000000000000000000000010250356274015301 5ustar rootrootzangband/lib/script/tk/config/adamNN+feat.cfg0000644000000000000000000000114410250356274020033 0ustar rootroot# File: adamNN+feat.cfg # Purpose: icon configuration file # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Extract the icon size from the configuration prefix scan [Global config,prefix] adam%d iconSize # Initialize the icon environment NSConfig::InitIcons $iconSize # Common stuff NSConfig::SourceOne adamNN-common 1 # This file is shared by adam16+feat.cfg and adam32+feat.cfg NSConfig::ShareConfigFile assign adam+feat-assign zangband/lib/script/tk/config/birth0000644000000000000000000000234310250356274016336 0ustar rootroot# Automatically generated. Do not edit. ReadSettingAux char,gender "Male" ReadSettingAux char,race "Human" ReadSettingAux char,class "Warrior" ReadSettingAux stat_limit,strength "" ReadSettingAux stat_limit,intelligence "" ReadSettingAux stat_limit,wisdom "" ReadSettingAux stat_limit,dexterity "" ReadSettingAux stat_limit,constitution "" ReadSettingAux stat_limit,charisma "" ReadSettingAux point_based "0" ReadSettingAux autoroller "1" ReadSettingAux maximize_mode "1" ReadSettingAux preserve_mode "1" ReadSettingAux ironman_shops "0" ReadSettingAux ironman_small_levels "0" ReadSettingAux ironman_downward "0" ReadSettingAux ironman_autoscum "0" ReadSettingAux ironman_hard_quests "0" ReadSettingAux ironman_empty_levels "0" ReadSettingAux ironman_rooms "0" ReadSettingAux ironman_nightmare "0" ReadSettingAux lite_town "0" ReadSettingAux max_quest "20" ReadSettingAux vanilla_town "0" ReadSettingAux char,realm1 "None" ReadSettingAux char,realm2 "None" ReadSettingAux terrain_streams "1" ReadSettingAux munchkin_death "0" ReadSettingAux points,strength "10" ReadSettingAux points,intelligence "10" ReadSettingAux points,wisdom "10" ReadSettingAux points,dexterity "10" ReadSettingAux points,constitution "10" ReadSettingAux points,charisma "10" zangband/lib/script/tk/config/classic-common0000644000000000000000000000157310250356274020141 0ustar rootroot# File: classic-common # Purpose: common icon configuration settings # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # ### MERGE+ start # We choose the largest font for which "font metrics $font -linespace" # is <= the icon height. switch -- [icon size] { 16 { if {[Platform unix]} {set size 12} if {[Platform windows]} {set size 10} } 24 { if {[Platform unix]} {set size 18} if {[Platform windows]} {set size 16} } 32 { if {[Platform unix]} {set size 24} if {[Platform windows]} {set size 21} } } ### MERGE+ stop # Standard 16 colors set color {255 0 250 17 217 196 199 101 129 247 30 5 35 185 180 52} # Attribute characters used by monsters and objects # set attrStr "dwsorgbuDWvyRGBU" zangband/lib/script/tk/config/config0000644000000000000000000000144710250356274016477 0ustar rootroot# File: config # Purpose: list of icon configurations # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # Config: adam16 "Adam Bolt 16x16" Config: adam24 "Adam Bolt 24x24" Config: adam32 "Adam Bolt 32x32" Config: adam16+feat "Adam Bolt + Original Features 16x16" Config: adam32+feat "Adam Bolt + Original Features 32x32" Config: classic16 "Ascii + Adam Bolt Features 16x16" Config: classic24 "Ascii + Adam Bolt Features 24x24" Config: classic32 "Ascii + Adam Bolt Features 32x32" Config: classic16+feat "Ascii + Original Features 16x16" Config: classic32+feat "Ascii + Original Features 32x32" Config: dg32 "David E. Gervais 32x32" zangband/lib/script/tk/config/geometry0000644000000000000000000000005110250356274017053 0ustar rootroot# Automatically generated. Do not edit. zangband/lib/script/tk/config/adam+feat-assign0000644000000000000000000005310110250356274020323 0ustar rootroot# Automatically generated. Do not edit. Assign icon Type none Type blank Type default Type feature Type feature2 Type adam Type pattern Type town Type ascii Group feature One 1 2 3 0 Feat icon 1 One 2 2 3 0 Feat icon 2 One 3 2 5 439 Feat none 1 One 4 0 1 Feat icon 1 One 5 0 1 Feat icon 1 One 6 3 3 Feat none 6 One 7 3 2 Feat none 7 One 8 3 2 Feat none 8 One 9 3 3 Feat none 9 One 10 3 2 Feat none 10 One 11 3 3 Feat none 11 One 12 3 2 Feat none 12 One 13 2 3 49 Feat none 13 One 14 2 3 50 Feat none 14 One 15 2 3 51 Feat none 15 One 16 2 5 46 Feat none 1 One 17 2 5 43 Feat none 1 One 18 2 5 43 Feat none 1 One 19 2 5 43 Feat none 1 One 20 2 5 49 Feat none 1 One 21 2 5 52 Feat none 1 One 22 2 5 40 Feat none 1 One 23 2 5 40 Feat none 1 One 24 2 5 34 Feat none 1 One 25 2 5 34 Feat none 1 One 26 2 5 34 Feat none 1 One 27 2 5 34 Feat none 1 One 28 2 5 37 Feat none 1 One 29 2 5 37 Feat none 1 One 30 2 5 37 Feat none 1 One 31 2 5 37 Feat none 1 One 32 0 0 Feat icon 1 One 33 0 0 Feat icon 1 One 34 0 0 Feat icon 1 One 35 0 0 Feat icon 1 One 36 0 0 Feat icon 1 One 37 0 0 Feat icon 1 One 38 0 0 Feat icon 1 One 39 0 0 Feat icon 1 One 40 0 0 Feat icon 1 One 41 0 0 Feat icon 1 One 42 0 0 Feat icon 1 One 43 0 0 Feat icon 1 One 44 0 0 Feat icon 1 One 45 0 0 Feat icon 1 One 46 0 0 Feat icon 1 One 47 0 0 Feat icon 1 One 48 2 3 4 Feat icon 48 One 49 2 3 16 Feat icon 1 One 50 0 3 Feat icon 50 One 51 0 3 Feat icon 51 One 52 0 3 Feat icon 52 One 53 0 3 Feat icon 53 One 54 3 4 Feat tint 54 One 55 3 4 Feat tint 55 One 56 2 3 4 Feat icon 56 One 57 0 2 Feat icon 57 One 58 2 3 4 Feat icon 58 One 59 2 3 4 Feat icon 59 One 60 2 3 4 Feat icon 60 One 61 2 3 4 Feat icon 61 One 62 2 3 4 Feat icon 62 One 63 2 3 4 Feat icon 63 One 64 2 5 111 Feat none 1 One 65 2 6 6 Feat icon 65 One 66 2 6 0 Feat icon 66 One 67 2 6 3 Feat icon 67 One 68 2 6 0 Feat icon 68 One 69 2 6 3 Feat icon 69 One 70 2 6 0 Feat icon 70 One 71 2 6 9 Feat icon 71 One 72 2 6 6 Feat icon 72 One 73 2 6 9 Feat icon 73 One 74 2 4 24 Feat none 74 One 75 2 4 25 Feat none 75 One 76 2 4 26 Feat none 76 One 77 2 4 27 Feat none 77 One 78 2 4 28 Feat none 78 One 79 2 4 29 Feat none 79 One 80 2 4 30 Feat none 80 One 81 2 4 31 Feat none 81 One 82 2 4 32 Feat none 82 One 83 2 4 51 Feat icon 83 One 84 2 4 48 Feat icon 84 One 85 2 4 62 Feat icon 85 One 86 2 4 59 Feat icon 86 One 87 2 4 65 Feat none 87 One 88 2 4 33 Feat icon 88 One 89 2 4 56 Feat icon 89 One 90 2 5 52 Feat none 1 One 91 2 2 0 Feat none 91 One 92 2 2 0 Feat none 92 One 93 2 2 0 Feat none 93 One 94 2 2 0 Feat none 94 One 95 2 2 0 Feat none 95 One 96 2 4 39 Feat icon 96 One 97 2 4 55 Feat none 97 One 98 2 2 0 Feat none 98 One 99 2 2 0 Feat none 99 One 100 2 2 0 Feat none 100 One 101 2 2 0 Feat none 101 One 102 2 2 0 Feat none 102 One 103 2 2 0 Feat none 103 One 104 2 2 0 Feat none 104 One 105 2 2 0 Feat none 105 One 106 2 2 0 Feat none 106 One 107 2 2 0 Feat none 107 One 108 2 2 0 Feat none 108 One 109 2 2 0 Feat none 109 One 110 2 2 0 Feat none 110 One 111 2 2 0 Feat none 111 One 112 2 2 0 Feat none 112 One 113 2 2 0 Feat none 113 One 114 2 2 0 Feat none 114 One 115 2 2 0 Feat none 115 One 116 2 2 0 Feat none 116 One 117 2 2 0 Feat none 117 One 118 2 2 0 Feat none 118 One 119 2 2 0 Feat none 119 One 120 2 2 0 Feat none 120 One 121 2 2 0 Feat none 121 One 122 2 2 0 Feat none 122 One 123 2 2 0 Feat none 123 One 124 2 2 0 Feat none 124 One 125 2 2 0 Feat none 125 One 126 2 2 0 Feat none 126 One 127 2 2 0 Feat none 127 One 128 2 4 31 Feat none 128 One 129 2 4 31 Feat none 129 One 130 2 4 31 Feat none 130 One 131 2 4 31 Feat none 131 One 132 2 4 31 Feat none 132 One 133 2 4 31 Feat none 133 One 134 2 4 31 Feat none 134 One 135 2 4 31 Feat none 135 One 136 2 4 31 Feat none 136 One 137 2 4 31 Feat none 137 One 138 2 4 31 Feat none 138 One 139 2 4 31 Feat none 139 One 140 2 4 31 Feat none 140 One 141 2 4 31 Feat none 141 One 142 2 4 31 Feat none 142 One 143 2 4 31 Feat none 143 One 144 2 4 31 Feat none 144 One 145 2 4 31 Feat none 145 One 146 2 4 31 Feat none 146 One 147 2 4 31 Feat none 147 One 148 2 4 31 Feat none 148 One 149 2 4 31 Feat none 149 One 150 2 4 31 Feat none 150 One 151 2 4 31 Feat none 151 One 152 2 4 31 Feat none 152 One 153 2 4 31 Feat none 153 One 154 2 4 31 Feat none 154 One 155 2 4 31 Feat none 155 One 156 2 4 31 Feat none 156 One 157 2 4 31 Feat none 157 One 158 2 4 31 Feat none 158 One 159 2 4 31 Feat none 159 Group monster One 1 2 5 1344 One 2 2 5 1250 One 3 2 5 1760 One 4 2 5 1761 One 5 2 5 1762 One 6 2 5 1763 One 7 2 5 954 One 8 2 5 1345 One 9 2 5 1346 One 10 2 5 1347 One 11 2 5 1348 One 12 2 5 1349 One 13 2 5 1350 One 14 2 5 1351 One 15 2 5 1352 One 16 2 5 1353 One 17 2 5 1354 One 18 2 5 1355 One 19 2 5 1536 One 20 2 5 1311 One 21 2 5 1093 One 22 3 5 One 23 2 5 1538 One 24 2 5 1205 One 25 2 5 1283 One 26 2 5 1284 One 27 2 5 1413 One 28 2 5 1092 One 29 2 5 1305 One 30 2 5 1306 One 31 2 5 1437 One 32 2 5 1243 One 33 2 5 1094 One 34 2 5 1540 One 35 2 5 955 One 36 2 5 1191 One 37 2 5 1199 One 38 2 5 1764 One 39 2 5 1539 One 40 2 5 934 One 41 2 5 1285 One 42 2 5 1206 One 43 2 5 1356 One 44 2 5 1357 One 45 2 5 1358 One 46 2 5 1359 One 47 2 5 935 One 48 2 5 1290 One 49 2 5 1192 One 50 2 5 1096 One 51 2 5 1032 One 52 2 5 1447 One 53 2 5 956 One 54 2 5 1765 One 55 2 5 957 One 56 2 5 1095 One 57 2 5 1541 One 58 2 5 1438 One 59 2 5 1097 One 60 2 5 1117 One 61 2 5 1766 One 62 2 5 1251 One 63 2 5 1360 One 64 2 5 1291 One 65 2 5 1017 One 66 2 5 1293 One 67 2 5 1207 One 68 2 5 1767 One 69 2 5 1309 One 70 2 5 1768 One 71 2 5 1320 One 72 2 5 936 One 73 2 5 1292 One 74 2 5 1267 One 75 2 5 1193 One 76 2 5 1312 One 77 2 5 1208 One 78 2 5 1439 One 79 2 5 1440 One 80 2 5 1244 One 81 2 5 1769 One 82 2 5 1098 One 83 2 5 1361 One 84 2 5 1294 One 85 2 5 928 One 86 2 5 1414 One 87 2 5 1770 One 88 2 5 1771 One 89 2 5 1441 One 90 2 5 1099 One 91 2 5 1417 One 92 2 5 1542 One 93 2 5 1363 One 94 2 5 1321 One 95 2 5 1772 One 96 2 5 1773 One 97 2 5 1370 One 98 2 5 1774 One 99 2 5 1295 One 100 2 5 1018 One 101 2 5 1296 One 102 2 5 1307 One 103 2 5 1286 One 104 2 5 1245 One 105 2 5 1442 One 106 2 5 1100 One 107 2 5 1543 One 108 2 5 937 One 109 2 5 1364 One 110 2 5 1365 One 111 2 5 1544 One 112 2 5 1545 One 113 2 5 1313 One 114 2 5 1200 One 115 2 5 1775 One 116 2 5 1367 One 117 2 5 929 One 118 2 5 1326 One 119 2 5 1101 One 120 2 5 1776 One 121 2 5 1102 One 122 2 5 1269 One 123 2 5 1452 One 124 2 5 1546 One 125 2 5 1547 One 126 2 5 1327 One 127 2 5 1118 One 128 2 5 1041 One 129 2 5 1246 One 130 2 5 1322 One 131 2 5 1297 One 132 2 5 1287 One 133 2 5 1019 One 134 2 5 1103 One 135 2 5 1308 One 136 2 5 1418 One 137 2 5 1368 One 138 2 5 1548 One 139 2 5 1777 One 140 2 5 1328 One 141 2 5 1448 One 142 2 5 1369 One 143 2 5 1104 One 144 2 5 1549 One 145 2 5 1778 One 146 2 5 1314 One 147 2 5 1370 One 148 2 5 1042 One 149 2 5 1329 One 150 2 5 1371 One 151 2 5 1550 One 152 2 5 1551 One 153 2 5 1552 One 154 2 5 1169 One 155 2 5 1288 One 156 2 5 1415 One 157 2 5 1033 One 158 2 5 1779 One 159 2 5 1780 One 160 2 5 1781 One 161 2 5 1782 One 162 2 5 1330 One 163 2 5 1213 One 164 2 5 1214 One 165 2 5 1215 One 166 2 5 1216 One 167 2 5 1217 One 168 2 5 1194 One 169 2 5 1372 One 170 2 5 1783 One 171 2 5 1105 One 172 2 5 1784 One 173 2 5 1553 One 174 2 5 1554 One 175 2 5 1119 One 176 2 5 1426 One 177 2 5 1785 One 178 2 5 1270 One 179 2 5 1786 One 180 2 5 1449 One 181 2 5 1787 One 182 2 5 1271 One 183 2 5 1789 One 184 2 5 938 One 185 2 5 1555 One 186 2 5 1331 One 187 2 5 1790 One 188 2 5 1791 One 189 2 5 1792 One 190 2 5 1315 One 191 2 5 1793 One 192 2 5 1316 One 193 2 5 1218 One 194 2 5 1043 One 195 2 5 930 One 196 2 5 958 One 197 2 5 1009 One 198 2 5 1252 One 199 2 5 1556 One 200 2 5 1557 One 201 2 5 1558 One 202 2 5 1559 One 203 2 5 1560 One 204 2 5 1219 One 205 2 5 1794 One 206 2 5 1795 One 207 2 5 1796 One 208 2 5 1453 One 209 2 5 1034 One 210 2 5 1106 One 211 2 5 959 One 212 2 5 1298 One 213 2 5 1443 One 214 2 5 1561 One 215 2 5 1332 One 216 2 5 1375 One 217 2 5 1797 One 218 2 5 1788 One 219 2 5 1798 One 220 2 5 1799 One 221 2 5 1800 One 222 2 5 1562 One 223 2 5 1563 One 224 2 5 1450 One 225 2 5 1374 One 226 2 5 1273 One 227 2 5 991 One 228 2 5 1419 One 229 2 5 1454 One 230 2 5 1253 One 231 2 5 1020 One 232 2 5 1209 One 233 2 5 1299 One 234 2 5 1120 One 235 2 5 1564 One 236 2 5 1051 One 237 2 5 1451 One 238 2 5 1067 One 239 2 5 931 One 240 2 5 1376 One 241 2 5 1377 One 242 2 5 1801 One 243 2 5 1802 One 244 2 5 1333 One 245 2 5 1300 One 246 2 5 1565 One 247 2 0 0 One 248 2 5 1803 One 249 2 5 1804 One 250 2 5 1011 One 251 2 5 1805 One 252 2 5 1289 One 253 2 5 1566 One 254 2 5 1567 One 255 2 5 1073 One 256 2 5 1257 One 257 2 5 960 One 258 2 5 1568 One 259 2 5 1010 One 260 2 5 1334 One 261 2 5 1258 One 262 2 5 1068 One 263 2 5 1806 One 264 2 5 1335 One 265 2 5 1807 One 266 2 5 1808 One 267 2 5 939 One 268 2 5 1809 One 269 2 5 1323 One 270 2 5 1416 One 271 2 5 1171 One 272 2 5 1172 One 273 2 5 1569 One 274 2 5 1570 One 275 2 5 1121 One 276 2 5 1210 One 277 2 5 1122 One 278 2 5 1074 One 279 2 5 1035 One 280 2 5 1044 One 281 3 0 One 282 2 5 1173 One 283 2 5 1145 One 284 2 5 1810 One 285 2 5 1336 One 286 2 5 1301 One 287 2 5 1012 One 288 2 5 1075 One 289 2 5 1013 One 290 2 5 1811 One 291 2 5 1378 One 292 2 5 1812 One 293 2 5 1813 One 294 2 5 1045 One 295 2 5 1814 One 296 2 5 1046 One 297 2 5 1129 One 298 2 5 1815 One 299 2 5 1816 One 300 2 5 1817 One 301 2 5 1107 One 302 2 5 1818 One 303 2 5 992 One 304 2 5 1123 One 305 2 5 993 One 306 2 5 994 One 307 2 5 1174 One 308 2 5 1175 One 309 2 5 1176 One 310 2 5 942 One 311 2 5 1819 One 312 2 5 961 One 313 2 5 1337 One 314 2 5 1338 One 315 2 5 1339 One 316 2 5 940 One 317 2 5 1820 One 318 2 5 1821 One 319 2 5 1822 One 320 2 5 1016 One 321 2 5 1076 One 322 2 5 1014 One 323 2 5 1259 One 324 2 5 1317 One 325 2 5 1015 One 326 2 5 1823 One 327 2 5 1824 One 328 2 5 1825 One 329 2 5 1826 One 330 2 5 1340 One 331 2 5 1124 One 332 2 5 1827 One 333 2 0 0 One 334 2 5 1571 One 335 2 5 1829 One 336 2 5 1572 One 337 2 5 1177 One 338 2 5 1178 One 339 2 5 1254 One 340 2 5 1179 One 341 2 5 1036 One 342 2 5 1082 One 343 2 5 1170 One 344 2 5 1573 One 345 2 5 1830 One 346 2 5 1831 One 347 2 5 962 One 348 2 5 1276 One 349 2 5 1078 One 350 2 5 1341 One 351 2 5 1201 One 352 2 5 941 One 353 2 5 1832 One 354 2 5 1428 One 355 2 5 1429 One 356 2 5 1342 One 357 2 5 1833 One 358 2 5 1430 One 359 2 5 1431 One 360 2 5 1834 One 361 2 5 1835 One 362 2 5 1064 One 363 2 5 1836 One 364 2 5 1837 One 365 2 5 1838 One 366 2 5 1053 One 367 2 5 1260 One 368 2 5 1574 One 369 2 5 1125 One 370 2 5 1839 One 371 2 5 1302 One 372 2 5 1379 One 373 2 5 1343 One 374 2 5 1840 One 375 2 5 1575 One 376 2 5 1380 One 377 2 5 1202 One 378 2 5 1052 One 379 2 5 1841 One 380 2 5 1382 One 381 2 5 1576 One 382 2 5 1577 One 383 2 5 1578 One 384 2 5 1842 One 385 2 5 1579 One 386 2 5 1843 One 387 2 5 1109 One 388 2 5 1844 One 389 2 5 1580 One 390 2 5 1065 One 391 2 5 1203 One 392 2 5 1381 One 393 2 0 0 One 394 2 5 1021 One 395 2 5 1211 One 396 2 5 1845 One 397 2 5 1582 One 398 2 5 1261 One 399 2 5 1846 One 400 2 5 1279 One 401 2 5 1130 One 402 2 5 1847 One 403 2 5 1131 One 404 2 5 1444 One 405 2 5 1055 One 406 2 5 1848 One 407 2 5 1849 One 408 2 5 1196 One 409 2 5 1583 One 410 2 5 1850 One 411 2 5 1427 One 412 2 5 1255 One 413 2 5 1383 One 414 2 5 1851 One 415 2 5 1069 One 416 2 5 1151 One 417 2 5 943 One 418 2 5 1679 One 419 2 5 1584 One 420 2 5 1585 One 421 2 5 1056 One 422 2 5 1852 One 423 2 5 932 One 424 2 5 1132 One 426 2 5 1853 One 427 2 5 1586 One 428 2 5 1180 One 429 2 5 1181 One 430 2 5 1070 One 431 2 5 1072 One 432 2 5 1146 One 433 2 5 1037 One 434 2 5 1587 One 435 2 5 1262 One 436 2 5 1324 One 437 2 5 1854 One 438 2 5 1855 One 439 2 5 1588 One 440 2 5 1110 One 441 2 5 1589 One 442 2 5 1384 One 443 2 5 1856 One 444 2 5 1857 One 445 2 5 1858 One 446 2 5 1445 One 447 2 5 1409 One 448 2 5 1590 One 449 2 5 1386 One 450 2 5 1387 One 451 2 5 1591 One 452 2 5 1859 One 453 2 5 1111 One 454 2 5 1133 One 455 2 5 1593 One 456 2 5 944 One 457 2 5 1470 One 458 2 5 1594 One 459 2 5 1220 One 460 2 5 1221 One 461 2 5 1222 One 462 2 5 1223 One 463 2 5 1860 One 464 2 5 1263 One 465 2 5 1420 One 466 2 5 1861 One 467 2 5 1862 One 468 2 5 1863 One 469 2 5 1195 One 470 2 5 1152 One 471 2 5 1224 One 472 2 5 1038 One 473 2 5 1473 One 474 2 5 1057 One 475 2 5 1864 One 476 2 5 1865 One 477 2 5 1022 One 478 2 5 1058 One 479 2 5 1071 One 480 2 5 1083 One 481 2 5 1126 One 482 2 5 1866 One 483 2 5 1867 One 484 2 5 1868 One 485 2 5 1388 One 486 2 5 1318 One 487 2 5 1077 One 488 2 5 1595 One 489 2 5 1869 One 490 2 5 1870 One 491 2 5 1135 One 492 2 5 1871 One 493 2 5 1136 One 494 2 5 1137 One 495 2 5 1138 One 496 2 5 1134 One 497 2 5 1596 One 498 2 5 1597 One 499 2 5 1153 One 500 2 5 1421 One 501 2 5 1225 One 502 2 5 1226 One 503 2 5 1227 One 504 2 5 1228 One 505 2 5 1598 One 506 2 5 1599 One 507 2 5 1600 One 508 2 5 1024 One 509 2 5 1139 One 510 2 5 995 One 511 2 5 945 One 512 2 5 996 One 513 2 5 1601 One 514 2 5 997 One 515 2 5 1212 One 516 2 5 1390 One 517 2 5 1602 One 518 2 5 1059 One 519 2 5 1872 One 520 2 5 1147 One 521 2 5 1603 One 522 2 5 1873 One 523 2 5 1874 One 524 2 5 1127 One 525 2 5 998 One 526 2 5 999 One 527 2 5 1604 One 528 2 5 1875 One 529 2 5 1605 One 530 2 5 1264 One 531 2 5 1876 One 532 2 5 1392 One 533 2 5 1877 One 534 2 5 1025 One 535 2 5 1878 One 536 2 5 1879 One 537 2 5 1001 One 538 2 5 1140 One 539 2 5 1606 One 540 2 5 1182 One 541 2 5 1303 One 542 2 5 1183 One 543 2 5 1184 One 544 2 5 1880 One 545 2 5 1000 One 546 2 5 1229 One 547 2 5 1410 One 548 2 5 1197 One 549 2 5 1230 One 550 2 5 1167 One 551 2 5 1141 One 552 2 5 1881 One 553 2 5 1607 One 554 2 5 1154 One 555 2 5 1882 One 556 2 5 1231 One 557 2 5 1608 One 558 2 5 1609 One 559 2 5 1232 One 560 2 5 1233 One 561 2 5 1234 One 562 2 5 1235 One 563 2 5 1236 One 564 2 5 1610 One 565 2 0 0 One 566 2 5 1048 One 567 2 0 0 One 568 2 5 1884 One 569 2 5 1611 One 570 2 5 1002 One 571 2 5 1612 One 572 2 5 1885 One 573 2 5 1613 One 574 2 5 1614 One 575 2 5 1066 One 576 2 5 1886 One 578 2 5 1887 One 579 2 5 1615 One 580 2 5 1888 One 581 2 5 1198 One 582 2 5 1003 One 583 2 5 1889 One 584 2 5 1004 One 585 2 5 1304 One 586 2 5 1680 One 587 2 5 1472 One 588 2 5 1432 One 589 2 5 1237 One 590 2 5 1238 One 591 2 5 1239 One 592 2 5 1240 One 593 2 5 1241 One 595 2 5 1592 One 597 2 5 1396 One 598 2 5 1616 One 599 2 5 1433 One 600 2 5 1434 One 601 2 5 968 One 602 2 5 969 One 603 2 5 1247 One 604 2 5 1155 One 605 2 5 946 One 606 2 5 1005 One 607 2 5 1156 One 608 2 5 1617 One 609 2 5 1618 One 612 2 5 1157 One 614 2 5 1112 One 615 2 5 1006 One 616 2 5 1242 One 617 2 5 970 One 618 2 5 971 One 619 2 5 1619 One 620 2 5 1142 One 621 2 5 1143 One 622 2 5 1411 One 623 2 5 1148 One 624 2 5 972 One 630 2 5 1144 One 631 2 5 1620 One 632 2 5 1446 One 633 2 5 1084 One 634 2 5 1079 One 635 2 5 1113 One 636 2 5 1398 One 637 2 5 1399 One 638 2 5 1400 One 639 2 5 1168 One 640 2 5 951 One 641 2 5 1039 One 642 2 5 1325 One 643 2 5 973 One 644 2 5 974 One 645 2 5 975 One 646 2 5 976 One 648 2 5 1621 One 649 2 5 1049 One 651 2 5 1622 One 657 2 5 1281 One 658 2 5 1060 One 659 2 5 1623 One 660 2 5 1625 One 661 2 5 947 One 662 2 5 1626 One 663 2 5 1627 One 664 2 5 1248 One 665 2 5 1026 One 666 2 5 1628 One 667 2 5 1028 One 673 3 1 One 675 2 5 977 One 676 2 5 978 One 677 2 5 1629 One 679 2 5 1007 One 685 2 5 1624 One 687 2 5 1008 One 688 2 5 1114 One 689 2 5 1402 One 690 2 5 1029 One 691 2 5 1266 One 692 2 5 1466 One 693 2 5 1630 One 696 2 5 1631 One 697 2 5 979 One 698 2 5 1632 One 699 2 5 1633 One 701 2 5 981 One 702 2 5 1080 One 703 2 5 980 One 705 2 5 1634 One 710 2 5 982 One 711 2 5 1319 One 712 2 5 1635 One 715 2 5 1465 One 718 2 5 1636 One 720 2 5 1461 One 721 2 5 1637 One 722 2 5 1469 One 723 2 5 1638 One 724 2 5 1185 One 725 2 5 1186 One 726 2 5 1187 One 727 2 5 1085 One 728 2 5 983 One 730 2 5 1040 One 732 2 5 1639 One 733 2 5 1640 One 737 2 5 1641 One 738 2 5 1163 One 739 2 5 1642 One 741 2 5 984 One 743 2 5 952 One 744 2 5 1164 One 748 2 5 1422 One 749 2 5 1423 One 750 2 5 1424 One 751 2 5 1435 One 752 2 5 1436 One 754 2 5 1115 One 755 2 5 1149 One 756 2 5 985 One 757 2 5 1643 One 759 2 5 1086 One 760 2 5 1644 One 762 2 5 1282 One 763 2 5 1645 One 764 2 5 948 One 765 2 5 949 One 766 2 5 986 One 768 2 5 1165 One 769 2 5 950 One 771 2 5 1406 One 773 2 5 1646 One 774 2 5 1030 One 777 2 5 1647 One 778 2 5 1648 One 779 2 5 1189 One 783 2 5 987 One 784 2 5 988 One 785 2 5 989 One 786 2 5 1649 One 789 2 5 1650 One 790 2 5 1651 One 791 2 5 1652 One 792 2 5 1031 One 793 2 5 1653 One 794 2 5 1654 One 795 2 5 990 One 796 2 5 1655 One 798 2 5 1061 One 799 2 5 1656 One 800 2 5 1087 One 801 2 5 1088 One 802 2 5 1089 One 804 2 5 1062 One 805 2 5 1249 One 807 2 5 1657 One 808 2 5 1128 One 809 2 5 1658 One 811 2 5 1190 One 813 2 5 1659 One 815 2 5 1660 One 816 2 5 1661 One 818 2 5 1407 One 819 2 5 1662 One 820 2 5 1663 One 821 2 5 1090 One 822 2 5 1091 One 824 2 5 1664 One 825 2 5 1166 One 827 2 5 953 One 828 2 5 1665 One 829 2 5 963 One 830 2 5 1425 One 831 2 5 1666 One 832 2 5 1667 One 836 2 5 1668 One 838 2 5 1116 One 839 2 5 1463 One 840 2 5 965 One 844 2 5 1669 One 845 2 5 1670 One 847 2 5 1671 One 848 2 5 1672 One 850 2 5 966 One 851 2 5 1673 One 852 2 5 1674 One 853 2 5 967 One 856 2 5 1464 One 857 2 5 1675 One 858 2 5 1408 One 860 2 5 1676 One 861 2 5 1081 One 862 2 5 1677 One 868 2 0 0 Group object One 21 2 5 452 One 22 2 5 450 One 23 2 5 451 One 24 2 5 453 One 25 2 5 454 One 26 2 5 448 One 27 2 5 449 One 29 2 5 1705 One 30 2 5 333 One 31 2 5 346 One 32 2 5 343 One 33 2 5 341 One 34 2 5 344 One 35 2 5 340 One 36 2 5 350 One 37 2 5 348 One 38 2 5 336 One 39 2 5 342 One 40 2 5 349 One 41 2 5 347 One 42 2 5 345 One 43 2 5 335 One 44 2 5 337 One 45 2 5 339 One 46 2 5 338 One 47 2 5 334 One 48 2 5 358 One 49 2 5 351 One 50 2 5 355 One 51 2 5 359 One 52 2 5 356 One 53 2 5 353 One 54 2 5 354 One 55 2 5 352 One 56 2 5 357 One 57 2 5 360 One 58 2 5 365 One 59 2 5 368 One 60 2 5 370 One 61 2 5 371 One 62 2 5 363 One 63 2 5 367 One 64 2 5 361 One 65 2 5 362 One 66 2 5 364 One 67 2 5 373 One 68 2 5 366 One 69 2 5 372 One 70 2 5 369 One 71 2 5 374 One 72 2 5 375 One 73 2 5 376 One 74 2 5 377 One 75 2 5 378 One 76 2 5 379 One 77 2 5 380 One 78 2 5 384 One 79 2 5 385 One 80 2 5 386 One 81 2 5 387 One 82 2 5 388 One 83 2 5 389 One 84 2 5 463 One 85 2 5 464 One 86 2 5 465 One 87 2 5 460 One 88 2 5 461 One 89 2 5 462 One 90 2 5 298 One 91 2 5 270 One 92 2 5 271 One 93 2 5 272 One 94 2 5 248 One 95 2 5 249 One 96 2 5 250 One 97 2 5 251 One 98 2 5 252 One 99 2 5 253 One 100 2 5 254 One 101 2 5 300 One 102 2 5 299 One 103 2 5 301 One 104 2 5 302 One 105 2 5 303 One 106 2 5 304 One 107 2 5 305 One 108 2 5 306 One 109 2 5 308 One 110 2 5 307 One 111 2 5 310 One 112 2 5 311 One 113 2 5 312 One 114 2 5 313 One 115 2 5 314 One 116 2 5 315 One 117 2 5 316 One 118 2 5 319 One 119 2 5 318 One 120 2 5 317 One 121 2 5 309 One 122 2 5 280 One 123 2 5 296 One 124 2 5 297 One 125 2 5 273 One 126 2 5 274 One 127 2 5 275 One 128 2 5 276 One 129 2 5 277 One 130 2 5 278 One 131 2 5 279 One 170 2 5 1735 One 173 2 5 126 One 174 2 5 126 One 175 2 5 126 One 176 2 5 126 One 177 2 5 126 One 178 2 5 126 One 179 2 5 126 One 180 2 5 126 One 181 2 5 126 One 182 2 5 126 One 183 2 5 126 One 184 2 5 126 One 185 2 5 126 One 186 2 5 126 One 187 2 5 126 One 188 2 5 126 One 189 2 5 126 One 190 2 5 126 One 191 2 5 126 One 192 2 5 126 One 193 2 5 126 One 194 2 5 126 One 195 2 5 1745 One 196 2 5 1743 One 197 2 5 126 One 198 2 5 126 One 199 2 5 126 One 200 2 5 126 One 201 2 5 126 One 202 2 5 126 One 203 2 5 126 One 204 2 5 126 One 205 2 5 126 One 206 2 5 126 One 207 2 5 126 One 208 2 5 126 One 209 2 5 126 One 210 2 5 126 One 211 2 5 126 One 212 2 5 126 One 213 2 5 126 One 214 2 5 126 One 215 2 5 126 One 216 2 5 126 One 217 2 5 126 One 218 2 5 126 One 219 2 5 126 One 220 2 5 126 One 221 2 5 126 One 293 2 5 1744 One 330 2 5 420 One 331 2 5 421 One 332 2 5 422 One 333 2 5 423 One 334 2 5 412 One 335 2 5 413 One 336 2 5 414 One 337 2 5 415 One 338 2 5 153 One 339 2 5 154 One 340 2 5 155 One 341 2 5 156 One 342 2 5 157 One 343 2 5 158 One 344 2 5 159 One 345 2 5 457 One 346 2 5 459 One 347 2 5 458 One 348 2 5 456 One 349 2 5 455 One 379 2 5 416 One 380 2 5 417 One 381 2 5 418 One 382 2 5 419 One 383 2 5 408 One 384 2 5 409 One 385 2 5 410 One 386 2 5 411 One 387 2 5 424 One 388 2 5 425 One 389 2 5 466 One 390 2 5 467 One 391 2 5 468 One 392 2 5 469 One 393 2 5 474 One 394 2 5 475 One 395 2 5 470 One 396 2 5 472 One 397 2 5 471 One 398 2 5 473 One 399 2 5 1706 One 400 2 5 322 One 401 2 5 320 One 402 2 5 321 One 403 2 5 323 One 404 2 5 324 One 405 2 5 331 One 406 2 5 327 One 407 2 5 329 One 408 2 5 325 One 409 2 5 326 One 410 2 5 328 One 411 2 5 330 One 412 2 5 332 One 413 2 5 255 One 414 2 5 281 One 423 2 5 426 One 424 2 5 427 One 480 2 5 113 One 481 2 5 113 One 482 2 5 113 One 483 2 5 114 One 484 2 5 114 One 485 2 5 114 One 486 2 5 118 One 487 2 5 118 One 488 2 5 115 One 489 2 5 115 One 490 2 5 115 One 491 2 5 119 One 492 2 5 120 One 493 2 5 121 One 494 2 5 122 One 495 2 5 123 One 496 2 5 116 One 497 2 5 117 One 498 2 5 382 One 499 2 5 383 One 500 2 5 477 One 501 2 5 478 One 502 2 5 479 One 503 2 5 150 One 504 2 5 151 One 505 2 5 152 One 506 2 5 143 One 507 2 5 145 One 508 2 5 146 One 509 2 5 147 One 510 2 5 148 One 511 2 5 149 One 512 2 5 432 One 513 2 5 433 One 514 2 5 434 One 515 2 5 435 One 516 2 5 428 One 517 2 5 429 One 518 2 5 430 One 519 2 5 431 One 524 2 5 1708 One 525 2 5 1697 One 527 2 5 1730 One 528 2 5 1701 One 529 2 5 1696 One 530 2 5 1731 One 531 2 5 1740 One 532 2 5 1702 One 533 2 5 1711 One 535 2 5 1736 One 536 2 5 1728 One 537 2 5 1712 One 538 2 5 1703 One 539 2 5 1713 One 540 2 5 1715 One 541 2 5 1714 One 542 2 5 1716 One 543 2 5 1700 One 544 2 5 1717 One 545 2 5 1699 One 546 2 5 1709 One 547 2 5 1710 One 548 2 5 1707 One 549 2 5 1721 One 550 2 5 1704 One 551 2 5 1718 One 552 2 5 1719 One 553 2 5 1698 One 554 2 5 1722 One 555 2 5 1720 One 556 2 5 1734 One 557 2 5 1739 One 558 2 5 1738 One 559 2 5 1737 One 560 2 5 1732 One 561 2 5 1741 One 562 2 5 1733 One 563 2 5 1729 zangband/lib/script/tk/config/adamNN-common0000644000000000000000000000071210250356274017650 0ustar rootroot# File: adamNN-common # Purpose: common icon configuration settings # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Assign ascii-type icons to everything because not everything has an icon. NSConfig::SourceOne classic-common # Hack NSConfig::NoMoreIcons zangband/lib/script/tk/config/classic+feat-assign0000644000000000000000000001066010250356274021045 0ustar rootroot# Automatically generated. Do not edit. Assign icon Type none Type blank Type default Type feature Type feature2 Type adam Type town Type pattern Type ascii Group feature One 1 2 3 0 Feat icon 1 One 2 2 3 0 Feat icon 2 One 3 2 5 439 Feat none 1 One 4 0 1 Feat icon 1 One 5 0 1 Feat icon 1 One 6 3 3 Feat none 6 One 7 3 2 Feat none 7 One 8 3 2 Feat none 8 One 9 3 3 Feat none 9 One 10 3 2 Feat none 10 One 11 3 3 Feat none 11 One 12 3 2 Feat none 12 One 13 2 3 49 Feat none 13 One 14 2 3 50 Feat none 14 One 15 2 3 51 Feat none 15 One 16 2 5 46 Feat none 1 One 17 2 5 43 Feat none 1 One 18 2 5 43 Feat none 1 One 19 2 5 43 Feat none 1 One 20 2 5 49 Feat none 1 One 21 2 5 52 Feat none 1 One 22 2 5 40 Feat none 1 One 23 2 5 40 Feat none 1 One 24 2 5 34 Feat none 1 One 25 2 5 34 Feat none 1 One 26 2 5 34 Feat none 1 One 27 2 5 34 Feat none 1 One 28 2 5 37 Feat none 1 One 29 2 5 37 Feat none 1 One 30 2 5 37 Feat none 1 One 31 2 5 37 Feat none 1 One 32 0 0 Feat icon 1 One 33 0 0 Feat icon 1 One 34 0 0 Feat icon 1 One 35 0 0 Feat icon 1 One 36 0 0 Feat icon 1 One 37 0 0 Feat icon 1 One 38 0 0 Feat icon 1 One 39 0 0 Feat icon 1 One 40 0 0 Feat icon 1 One 41 0 0 Feat icon 1 One 42 0 0 Feat icon 1 One 43 0 0 Feat icon 1 One 44 0 0 Feat icon 1 One 45 0 0 Feat icon 1 One 46 0 0 Feat icon 1 One 47 0 0 Feat icon 1 One 48 2 3 4 Feat icon 48 One 49 2 3 16 Feat icon 1 One 50 0 3 Feat icon 50 One 51 0 3 Feat icon 51 One 52 0 3 Feat icon 52 One 53 0 3 Feat icon 53 One 54 3 4 Feat tint 54 One 55 3 4 Feat tint 55 One 56 2 3 4 Feat icon 56 One 57 0 2 Feat icon 57 One 58 2 3 4 Feat icon 58 One 59 2 3 4 Feat icon 59 One 60 2 3 4 Feat icon 60 One 61 2 3 4 Feat icon 61 One 62 2 3 4 Feat icon 62 One 63 2 3 4 Feat icon 63 One 64 2 5 111 Feat none 1 One 65 2 7 6 Feat icon 65 One 66 2 7 0 Feat icon 66 One 67 2 7 3 Feat icon 67 One 68 2 7 0 Feat icon 68 One 69 2 7 3 Feat icon 69 One 70 2 7 0 Feat icon 70 One 71 2 7 9 Feat icon 71 One 72 2 7 6 Feat icon 72 One 73 2 7 9 Feat icon 73 One 74 2 4 24 Feat none 74 One 75 2 4 25 Feat none 75 One 76 2 4 26 Feat none 76 One 77 2 4 27 Feat none 77 One 78 2 4 28 Feat none 78 One 79 2 4 29 Feat none 79 One 80 2 4 30 Feat none 80 One 81 2 4 31 Feat none 81 One 82 2 4 32 Feat none 82 One 83 2 4 51 Feat icon 83 One 84 2 4 48 Feat icon 84 One 85 2 4 62 Feat icon 85 One 86 2 4 59 Feat icon 86 One 87 2 4 65 Feat none 87 One 88 2 4 33 Feat icon 88 One 89 2 4 56 Feat icon 89 One 90 2 5 52 Feat none 1 One 91 2 2 0 Feat none 91 One 92 2 2 0 Feat none 92 One 93 2 2 0 Feat none 93 One 94 2 2 0 Feat none 94 One 95 2 2 0 Feat none 95 One 96 2 4 39 Feat icon 96 One 97 2 4 55 Feat none 97 One 98 2 2 0 Feat none 98 One 99 2 2 0 Feat none 99 One 100 2 2 0 Feat none 100 One 101 2 2 0 Feat none 101 One 102 2 2 0 Feat none 102 One 103 2 2 0 Feat none 103 One 104 2 2 0 Feat none 104 One 105 2 2 0 Feat none 105 One 106 2 2 0 Feat none 106 One 107 2 2 0 Feat none 107 One 108 2 2 0 Feat none 108 One 109 2 2 0 Feat none 109 One 110 2 2 0 Feat none 110 One 111 2 2 0 Feat none 111 One 112 2 2 0 Feat none 112 One 113 2 2 0 Feat none 113 One 114 2 2 0 Feat none 114 One 115 2 2 0 Feat none 115 One 116 2 2 0 Feat none 116 One 117 2 2 0 Feat none 117 One 118 2 2 0 Feat none 118 One 119 2 2 0 Feat none 119 One 120 2 2 0 Feat none 120 One 121 2 2 0 Feat none 121 One 122 2 2 0 Feat none 122 One 123 2 2 0 Feat none 123 One 124 2 2 0 Feat none 124 One 125 2 2 0 Feat none 125 One 126 2 2 0 Feat none 126 One 127 2 2 0 Feat none 127 One 128 2 4 31 Feat none 128 One 129 2 4 31 Feat none 129 One 130 2 4 31 Feat none 130 One 131 2 4 31 Feat none 131 One 132 2 4 31 Feat none 132 One 133 2 4 31 Feat none 133 One 134 2 4 31 Feat none 134 One 135 2 4 31 Feat none 135 One 136 2 4 31 Feat none 136 One 137 2 4 31 Feat none 137 One 138 2 4 31 Feat none 138 One 139 2 4 31 Feat none 139 One 140 2 4 31 Feat none 140 One 141 2 4 31 Feat none 141 One 142 2 4 31 Feat none 142 One 143 2 4 31 Feat none 143 One 144 2 4 31 Feat none 144 One 145 2 4 31 Feat none 145 One 146 2 4 31 Feat none 146 One 147 2 4 31 Feat none 147 One 148 2 4 31 Feat none 148 One 149 2 4 31 Feat none 149 One 150 2 4 31 Feat none 150 One 151 2 4 31 Feat none 151 One 152 2 4 31 Feat none 152 One 153 2 4 31 Feat none 153 One 154 2 4 31 Feat none 154 One 155 2 4 31 Feat none 155 One 156 2 4 31 Feat none 156 One 157 2 4 31 Feat none 157 One 158 2 4 31 Feat none 158 One 159 2 4 31 Feat none 159 Group monster One 144 2 1 0 One 247 2 0 0 One 333 2 0 0 One 393 2 0 0 One 565 2 0 0 One 567 2 0 0 One 803 2 0 0 One 868 2 0 0 Group object zangband/lib/script/tk/config/classicNN+feat.cfg0000644000000000000000000000121310250356274020547 0ustar rootroot# File: classicNN+feat.cfg # Purpose: icon configuration file # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Extract the icon size from the configuration prefix scan [Global config,prefix] classic%d iconSize # Initialize the icon environment NSConfig::InitIcons $iconSize # Hack NSConfig::NoMoreIcons # This file is shared by classicNN+feat.cfg NSConfig::ShareConfigFile assign classic+feat-assign # Even more common ascii stuff NSConfig::SourceOne classic-common zangband/lib/script/tk/config/dg32-assign0000644000000000000000000005416610250356274017261 0ustar rootroot# Automatically generated. Do not edit. Assign icon Type none Type blank Type default Type armor Type food Type classm Type humans Type jewls Type magic Type misc Type potions Type wands Type weapons Type people Type dragon Type monster1 Type monster2 Type monster3 Type monster4 Type monster5 Type monster6 Type monster7 Type undead Type uniques Type edging3 Type dungeon Type grounds Type extra1 Type town0 Type town1 Type town2 Type town3 Type town4 Type town5 Type town6 Type town7 Type town8 Type town9 Type edging1 Type edging2 Type adam Type pattern Type town Type ascii Group feature One 1 2 27 0 Feat icon 1 One 2 2 27 0 Feat icon 2 One 3 2 10 41 Feat none 1 One 4 2 30 32 Feat tint 4 One 5 2 30 33 Feat tint 5 One 6 2 30 28 Feat tint 6 One 7 2 30 29 Feat tint 7 One 8 2 30 29 Feat tint 8 One 9 2 30 28 Feat tint 9 One 10 2 30 29 Feat tint 10 One 11 2 30 28 Feat tint 11 One 12 2 26 43 Feat tint 12 One 13 2 2 0 Feat none 13 One 14 2 2 0 Feat none 14 One 15 2 2 0 Feat none 15 One 16 2 10 38 Feat none 1 One 17 2 10 36 Feat none 1 One 18 2 10 37 Feat none 1 One 19 2 10 67 Feat none 1 One 20 2 10 39 Feat none 1 One 21 2 10 40 Feat none 1 One 22 2 10 35 Feat none 1 One 23 2 10 35 Feat none 1 One 24 2 10 33 Feat none 1 One 25 2 10 57 Feat none 1 One 26 2 10 58 Feat none 1 One 27 2 10 60 Feat none 1 One 28 2 10 61 Feat none 1 One 29 2 10 59 Feat none 1 One 30 2 10 56 Feat none 1 One 31 2 10 61 Feat none 1 One 32 2 30 31 Feat tint 32 One 33 2 26 30 Feat tint 33 One 34 2 26 30 Feat tint 34 One 35 2 26 30 Feat tint 35 One 36 2 26 30 Feat tint 36 One 37 2 26 30 Feat tint 37 One 38 2 26 30 Feat tint 38 One 39 2 26 30 Feat tint 39 One 40 2 26 30 Feat tint 40 One 41 2 26 30 Feat tint 41 One 42 2 26 30 Feat tint 42 One 43 2 26 30 Feat tint 43 One 44 2 26 30 Feat tint 44 One 45 2 26 30 Feat tint 45 One 46 2 26 30 Feat tint 46 One 47 2 26 30 Feat tint 47 One 48 2 26 78 Feat icon 48 One 49 2 10 55 Feat none 1 One 50 2 26 51 Feat icon 50 One 51 2 26 57 Feat icon 51 One 52 2 26 51 Feat icon 52 One 53 2 26 57 Feat icon 53 One 54 2 26 54 Feat icon 54 One 55 2 26 60 Feat icon 55 One 56 2 26 0 Feat icon 56 One 57 0 0 Feat icon 57 One 58 2 26 78 Feat icon 58 One 59 2 26 78 Feat icon 59 One 60 2 30 0 Feat icon 60 One 61 2 26 78 Feat icon 61 One 62 2 26 78 Feat icon 62 One 63 2 26 78 Feat icon 63 One 64 2 10 39 Feat none 1 One 65 2 42 6 Feat icon 65 One 66 2 42 0 Feat icon 66 One 67 2 42 3 Feat icon 67 One 68 2 42 0 Feat icon 68 One 69 2 42 3 Feat icon 69 One 70 2 42 0 Feat icon 70 One 71 2 42 9 Feat icon 71 One 72 2 42 6 Feat icon 72 One 73 2 42 9 Feat icon 73 One 74 2 29 3 Feat none 74 One 75 2 29 4 Feat none 75 One 76 2 29 5 Feat none 76 One 77 2 29 6 Feat none 77 One 78 2 29 7 Feat none 78 One 79 2 29 8 Feat none 79 One 80 2 29 27 Feat none 80 One 81 2 29 23 Feat none 81 One 82 2 29 9 Feat none 82 One 83 3 4 Feat none 83 One 84 3 3 Feat none 84 One 85 2 27 36 Feat icon 85 One 86 2 27 33 Feat icon 86 One 87 2 27 52 Feat none 87 One 88 2 27 27 Feat icon 88 One 89 2 27 9 Feat icon 89 One 90 2 10 40 Feat none 1 One 91 2 2 0 Feat none 91 One 92 2 2 0 Feat none 92 One 93 2 2 0 Feat none 93 One 94 2 2 0 Feat none 94 One 95 2 2 0 Feat none 95 One 96 2 27 54 Feat icon 89 One 97 2 27 81 Feat icon 97 One 98 2 2 0 Feat none 98 One 99 2 2 0 Feat none 99 One 100 2 2 0 Feat none 100 One 101 2 2 0 Feat none 101 One 102 2 2 0 Feat none 102 One 103 2 2 0 Feat none 103 One 104 2 2 0 Feat none 104 One 105 2 2 0 Feat none 105 One 106 2 2 0 Feat none 106 One 107 2 2 0 Feat none 107 One 108 2 2 0 Feat none 108 One 109 2 2 0 Feat none 109 One 110 2 2 0 Feat none 110 One 111 2 2 0 Feat none 111 One 112 2 2 0 Feat none 112 One 113 2 2 0 Feat none 113 One 114 2 2 0 Feat none 114 One 115 2 2 0 Feat none 115 One 116 2 2 0 Feat none 116 One 117 2 2 0 Feat none 117 One 118 2 2 0 Feat none 118 One 119 2 2 0 Feat none 119 One 120 2 2 0 Feat none 120 One 121 2 2 0 Feat none 121 One 122 2 2 0 Feat none 122 One 123 2 2 0 Feat none 123 One 124 2 2 0 Feat none 124 One 125 2 2 0 Feat none 125 One 126 2 2 0 Feat none 126 One 127 2 2 0 Feat none 127 One 128 2 30 31 Feat none 128 One 129 2 30 31 Feat none 129 One 130 2 30 31 Feat none 130 One 131 2 30 31 Feat none 131 One 132 2 30 31 Feat none 132 One 133 2 30 31 Feat none 133 One 134 2 30 31 Feat none 134 One 135 2 30 31 Feat none 135 One 136 2 30 31 Feat none 136 One 137 2 30 31 Feat none 137 One 138 2 30 31 Feat none 138 One 139 2 30 31 Feat none 139 One 140 2 30 31 Feat none 140 One 141 2 30 31 Feat none 141 One 142 2 30 31 Feat none 142 One 143 2 30 31 Feat none 143 One 144 2 30 31 Feat none 144 One 145 2 30 31 Feat none 145 One 146 2 30 31 Feat none 146 One 147 2 30 31 Feat none 147 One 148 2 30 31 Feat none 148 One 149 2 30 31 Feat none 149 One 150 2 30 31 Feat none 150 One 151 2 30 31 Feat none 151 One 152 2 30 31 Feat none 152 One 153 2 30 31 Feat none 153 One 154 2 30 31 Feat none 154 One 155 2 30 31 Feat none 155 One 156 2 30 31 Feat none 156 One 157 2 30 31 Feat none 157 One 158 2 30 31 Feat none 158 One 159 2 30 31 Feat none 159 Group monster One 1 2 14 0 One 2 2 19 54 One 3 2 41 1760 One 4 2 41 1761 One 5 2 41 1762 One 6 2 41 1763 One 7 2 18 72 One 8 2 14 1 One 9 2 14 2 One 10 2 14 3 One 11 2 14 4 One 12 2 14 5 One 13 2 14 6 One 14 2 14 7 One 15 2 14 8 One 16 2 14 9 One 17 2 14 10 One 18 2 14 11 One 19 2 41 1536 One 20 2 17 66 One 21 2 19 19 One 22 3 1 One 23 2 41 1538 One 24 2 20 33 One 25 2 17 78 One 26 2 17 79 One 27 2 16 24 One 28 2 19 18 One 29 2 16 11 One 30 2 16 12 One 31 2 20 16 One 32 2 21 65 One 33 2 22 12 One 34 2 18 41 One 35 2 19 49 One 36 2 20 0 One 37 2 19 1 One 38 2 41 1764 One 39 2 41 1539 One 40 2 17 86 One 41 2 17 80 One 42 2 20 34 One 43 2 7 0 One 44 2 7 1 One 45 2 7 2 One 46 2 7 3 One 47 2 17 87 One 48 2 18 19 One 49 2 20 1 One 50 2 22 13 One 51 2 22 10 One 52 2 18 58 One 53 2 18 73 One 54 2 18 74 One 55 2 18 75 One 56 2 22 8 One 57 2 41 1541 One 58 2 20 17 One 59 2 19 20 One 60 2 18 56 One 61 2 41 1766 One 62 2 19 50 One 63 2 18 60 One 64 2 18 0 One 65 2 23 23 One 66 2 18 10 One 67 2 20 35 One 68 2 41 1767 One 69 2 18 42 One 70 2 41 1768 One 71 2 19 13 One 72 2 17 88 One 73 2 18 20 One 74 2 16 0 One 75 2 20 2 One 76 2 17 70 One 77 2 20 36 One 78 2 20 18 One 79 2 20 19 One 80 2 19 7 One 81 2 41 1769 One 82 2 22 14 One 83 2 7 4 One 84 2 18 6 One 85 2 10 7 One 86 2 16 24 One 87 2 41 1770 One 88 2 41 1771 One 89 2 20 20 One 90 2 19 21 One 91 2 23 59 One 92 2 41 1542 One 93 2 7 6 One 94 2 19 14 One 95 2 20 45 One 96 2 41 1773 One 97 2 7 5 One 98 2 41 1774 One 99 2 18 1 One 100 2 23 2 One 101 2 18 17 One 102 2 16 13 One 103 2 17 81 One 104 2 19 6 One 105 2 20 21 One 106 2 19 22 One 107 2 13 15 One 108 2 17 89 One 109 2 7 7 One 110 2 7 8 One 111 2 41 1544 One 112 2 41 1545 One 113 2 17 71 One 114 2 19 2 One 115 2 41 1775 One 116 2 7 10 One 117 2 10 8 One 118 2 16 19 One 119 2 19 23 One 120 2 41 1776 One 121 2 22 9 One 122 2 16 1 One 123 2 23 27 One 124 2 41 1546 One 125 2 41 1547 One 126 2 16 20 One 127 2 18 53 One 128 2 16 36 One 129 2 19 5 One 130 2 41 1322 One 131 2 41 1297 One 132 2 17 82 One 133 2 23 22 One 134 2 22 15 One 135 2 24 1 One 136 2 23 62 One 137 2 24 2 One 138 2 41 1548 One 139 2 41 1777 One 140 2 24 3 One 141 2 18 57 One 142 2 7 11 One 143 2 22 13 One 144 2 41 1549 One 145 2 41 1778 One 146 2 17 73 One 147 2 7 12 One 148 2 16 37 One 149 2 21 24 One 150 2 7 13 One 151 2 41 1550 One 152 2 23 43 One 153 2 41 1552 One 154 2 17 50 One 155 2 17 83 One 156 2 16 25 One 157 2 22 11 One 158 2 41 1779 One 159 2 41 1780 One 160 2 41 1781 One 161 2 41 1782 One 162 2 21 25 One 163 2 15 0 One 164 2 15 2 One 165 2 15 3 One 166 2 15 4 One 167 2 15 5 One 168 2 20 7 One 169 2 24 4 One 170 2 41 1783 One 171 2 19 24 One 172 2 41 1784 One 173 2 41 1553 One 174 2 41 1554 One 175 2 18 52 One 176 2 18 42 One 177 2 41 1785 One 178 2 16 2 One 179 2 18 61 One 180 2 24 5 One 181 2 41 1787 One 182 2 16 3 One 183 2 41 1789 One 184 2 17 90 One 185 2 41 1555 One 186 2 24 6 One 187 2 41 1790 One 188 2 41 1791 One 189 2 41 1792 One 190 2 17 72 One 191 2 41 1793 One 192 2 17 67 One 193 2 15 46 One 194 2 16 38 One 195 2 10 9 One 196 2 19 42 One 197 2 18 49 One 198 2 19 53 One 199 2 41 1556 One 200 2 41 1557 One 201 2 41 1558 One 202 2 41 1559 One 203 2 41 1560 One 204 2 15 7 One 205 2 41 1794 One 206 2 41 1795 One 207 2 41 1796 One 208 2 23 26 One 209 2 20 15 One 210 2 19 25 One 211 2 19 48 One 212 2 18 11 One 213 2 20 22 One 214 2 20 19 One 215 2 24 7 One 216 2 7 16 One 217 2 41 1797 One 218 2 19 9 One 219 2 41 1798 One 220 2 41 1799 One 221 2 18 77 One 222 2 41 1562 One 223 2 41 1563 One 224 2 18 59 One 225 2 7 15 One 226 2 16 5 One 227 2 18 21 One 228 2 23 30 One 229 2 23 25 One 230 2 19 52 One 231 2 23 20 One 232 2 20 37 One 233 2 18 16 One 234 2 22 26 One 235 2 41 1564 One 236 2 22 17 One 237 2 24 8 One 238 2 16 14 One 239 2 10 10 One 240 2 7 17 One 241 2 7 18 One 242 2 41 1801 One 243 2 3 0 One 244 2 21 26 One 245 2 18 8 One 246 2 41 1565 One 247 2 0 0 One 248 2 41 1803 One 249 2 41 1804 One 250 2 20 56 One 251 2 41 1805 One 252 2 17 84 One 253 2 41 1566 One 254 2 41 1567 One 255 2 21 10 One 256 2 21 0 One 257 2 19 46 One 258 2 41 1568 One 259 2 18 43 One 260 2 24 9 One 261 2 21 1 One 262 2 16 15 One 263 2 41 1806 One 264 2 21 27 One 265 2 41 1807 One 266 2 41 1808 One 267 2 17 91 One 268 2 41 1809 One 269 2 19 16 One 270 2 16 27 One 271 2 19 64 One 272 2 19 66 One 273 2 41 1569 One 274 2 41 1570 One 275 2 18 51 One 276 2 20 38 One 277 2 18 54 One 278 2 21 11 One 279 2 20 14 One 280 2 16 39 One 281 2 16 4 One 282 2 19 58 One 283 2 21 33 One 284 2 41 1810 One 285 2 21 28 One 286 2 18 5 One 287 2 20 57 One 288 2 21 12 One 289 2 18 48 One 290 2 18 65 One 291 2 24 10 One 292 2 41 1812 One 293 2 41 1813 One 294 2 16 40 One 295 2 41 1814 One 296 2 16 42 One 297 2 21 46 One 298 2 41 1815 One 299 2 41 1816 One 300 2 41 1817 One 301 2 19 33 One 302 2 21 19 One 303 2 18 22 One 304 2 21 72 One 305 2 18 23 One 306 2 18 24 One 307 2 19 61 One 308 2 19 68 One 309 2 19 75 One 310 2 11 6 One 311 2 41 1819 One 312 2 19 47 One 313 2 21 29 One 314 2 24 12 One 315 2 24 13 One 316 2 17 92 One 317 2 41 1820 One 318 2 41 1821 One 319 2 41 1822 One 320 2 20 60 One 321 2 21 13 One 322 2 20 58 One 323 2 21 2 One 324 2 17 68 One 325 2 20 59 One 326 2 41 1823 One 327 2 41 1824 One 328 2 41 1825 One 329 2 41 1826 One 330 2 24 14 One 331 2 18 50 One 332 2 18 64 One 333 2 0 0 One 334 2 15 47 One 335 2 20 12 One 336 2 41 1572 One 337 2 19 74 One 338 2 19 63 One 339 2 19 55 One 340 2 19 71 One 341 2 22 29 One 342 2 22 0 One 343 2 17 40 One 344 2 41 1573 One 345 2 41 1830 One 346 2 41 1831 One 347 2 19 45 One 348 2 16 7 One 349 2 21 14 One 350 2 24 15 One 351 2 19 3 One 352 2 10 76 One 353 2 41 1832 One 354 2 21 36 One 355 2 21 37 One 356 2 24 16 One 357 2 41 1833 One 358 2 21 38 One 359 2 21 39 One 360 2 41 1834 One 361 2 41 1835 One 362 2 20 48 One 363 2 41 1836 One 364 2 41 1837 One 365 2 41 1838 One 366 2 22 18 One 367 2 21 3 One 368 2 41 1574 One 369 2 21 73 One 370 2 41 1839 One 371 2 18 2 One 372 2 7 19 One 373 2 24 17 One 374 2 41 1840 One 375 2 41 1575 One 376 2 7 20 One 377 2 19 4 One 378 2 22 19 One 379 2 41 1841 One 380 2 24 21 One 381 2 41 1576 One 382 2 41 1577 One 383 2 41 1578 One 384 2 41 1842 One 385 2 41 1579 One 386 2 41 1843 One 387 2 19 35 One 388 2 41 1844 One 389 2 41 1580 One 390 2 20 49 One 391 2 19 0 One 392 2 24 20 One 393 2 0 0 One 394 2 23 1 One 395 2 20 39 One 396 2 41 1845 One 397 2 41 1582 One 398 2 21 4 One 399 2 41 1846 One 400 2 16 8 One 401 2 21 47 One 402 2 41 1847 One 403 2 21 48 One 404 2 20 23 One 405 2 22 20 One 406 2 41 1848 One 407 2 41 1849 One 408 2 20 4 One 409 2 41 1583 One 410 2 41 1850 One 411 2 18 47 One 412 2 19 57 One 413 2 24 22 One 414 2 41 1851 One 415 2 16 16 One 416 2 23 12 One 417 2 17 54 One 418 2 41 1679 One 419 2 41 1584 One 420 2 41 1585 One 421 2 22 21 One 422 2 41 1852 One 423 2 10 11 One 424 2 21 49 One 426 2 41 1853 One 427 2 41 1586 One 428 2 19 59 One 429 2 19 69 One 430 2 16 17 One 431 2 41 1072 One 432 2 23 50 One 433 2 22 30 One 434 2 41 1587 One 435 2 21 5 One 436 2 19 17 One 437 2 41 1854 One 438 2 21 77 One 439 2 41 1588 One 440 2 19 36 One 441 2 41 1589 One 442 2 7 21 One 443 2 41 1856 One 444 2 41 1857 One 445 2 41 1858 One 446 2 20 24 One 447 2 22 27 One 448 2 41 1590 One 449 2 7 22 One 450 2 16 10 One 451 2 41 1591 One 452 2 41 1859 One 453 2 20 8 One 454 2 21 50 One 455 2 20 41 One 456 2 17 55 One 457 2 8 3 One 458 2 41 1594 One 459 2 15 8 One 460 2 15 10 One 461 2 15 11 One 462 2 15 9 One 463 2 41 1860 One 464 2 21 6 One 465 2 23 42 One 466 2 41 1861 One 467 2 41 1862 One 468 2 41 1863 One 469 2 20 5 One 470 2 23 8 One 471 2 15 40 One 472 2 22 31 One 473 2 20 6 One 474 2 22 22 One 475 2 41 1864 One 476 2 41 1865 One 477 2 23 24 One 478 2 22 23 One 479 2 16 18 One 480 2 22 1 One 481 2 24 27 One 482 2 41 1866 One 483 2 41 1867 One 484 2 41 1868 One 485 2 7 23 One 486 2 17 93 One 487 2 21 15 One 488 2 19 12 One 489 2 41 1869 One 490 2 41 1870 One 491 2 21 52 One 492 2 41 1871 One 493 2 24 28 One 494 2 24 29 One 495 2 24 30 One 496 2 21 51 One 497 2 41 1596 One 498 2 41 1597 One 499 2 23 10 One 500 2 23 55 One 501 2 15 41 One 502 2 15 42 One 503 2 15 45 One 504 2 15 39 One 505 2 41 1598 One 506 2 41 1599 One 507 2 41 1600 One 508 2 23 7 One 509 2 21 53 One 510 2 18 27 One 511 2 17 56 One 512 2 18 26 One 513 2 19 77 One 514 2 18 40 One 515 2 20 40 One 516 2 7 25 One 517 2 41 1602 One 518 2 23 16 One 519 2 41 1872 One 520 2 23 51 One 521 2 41 1603 One 522 2 20 52 One 523 2 41 1874 One 524 2 21 74 One 525 2 18 28 One 526 2 18 35 One 527 2 41 1604 One 528 2 41 1875 One 529 2 41 1605 One 530 2 21 7 One 531 2 41 1876 One 532 2 7 26 One 533 2 41 1877 One 534 2 23 0 One 535 2 41 1878 One 536 2 41 1879 One 537 2 18 39 One 538 2 21 54 One 539 2 41 1606 One 540 2 19 73 One 541 2 18 4 One 542 2 19 65 One 543 2 19 60 One 544 2 41 1880 One 545 2 18 31 One 546 2 15 12 One 547 2 20 61 One 548 2 20 7 One 549 2 15 18 One 550 2 21 34 One 551 2 24 32 One 552 2 41 1881 One 553 2 41 1607 One 554 2 23 13 One 555 2 41 1882 One 556 2 15 15 One 557 2 9 8 One 558 2 21 8 One 559 2 15 14 One 560 2 15 16 One 561 2 15 19 One 562 2 15 17 One 563 2 15 13 One 564 2 41 1610 One 565 2 0 0 One 566 2 16 43 One 567 2 0 0 One 568 2 41 1884 One 569 2 41 1611 One 570 2 21 64 One 571 2 41 1612 One 572 2 41 1885 One 573 2 41 1613 One 574 2 41 1614 One 575 2 20 50 One 576 2 41 1886 One 577 2 23 14 One 578 2 41 1887 One 579 2 41 1615 One 580 2 41 1888 One 581 2 24 34 One 582 2 18 25 One 583 2 41 1889 One 584 2 18 33 One 585 2 18 3 One 586 2 22 25 One 587 2 21 40 One 588 2 21 41 One 589 2 15 21 One 590 2 15 22 One 591 2 15 44 One 592 2 15 20 One 593 2 15 23 One 595 2 41 1592 One 597 2 7 29 One 598 2 41 1616 One 599 2 21 42 One 600 2 21 43 One 601 2 15 24 One 602 2 15 25 One 603 2 19 10 One 604 2 23 11 One 605 2 17 57 One 606 2 41 1005 One 607 2 23 19 One 608 2 16 56 One 609 2 41 1618 One 611 2 23 18 One 612 2 23 14 One 613 2 21 41 One 614 2 19 38 One 615 2 41 1006 One 616 2 24 38 One 617 2 15 26 One 618 2 15 27 One 619 2 20 29 One 620 2 21 55 One 621 2 21 56 One 622 2 22 28 One 623 2 23 52 One 624 2 15 28 One 626 2 18 18 One 630 2 21 57 One 631 2 21 61 One 632 2 20 22 One 633 2 22 2 One 634 2 21 16 One 635 2 19 40 One 636 2 7 30 One 637 2 7 31 One 638 2 7 32 One 639 2 21 35 One 640 2 41 951 One 641 2 16 21 One 642 2 24 40 One 643 2 15 43 One 644 2 15 29 One 645 2 15 30 One 646 2 15 44 One 647 2 41 1398 One 648 2 41 1621 One 649 2 16 44 One 651 2 41 1622 One 652 2 17 58 One 653 2 41 1586 One 657 2 16 9 One 658 2 23 15 One 659 2 41 1623 One 660 2 41 1625 One 661 2 17 54 One 662 2 41 1626 One 663 2 41 1627 One 664 2 19 11 One 665 2 41 1026 One 666 2 41 1628 One 667 2 23 5 One 668 2 20 9 One 672 2 21 9 One 673 2 20 63 One 675 2 15 31 One 676 2 15 38 One 677 2 41 1629 One 679 2 24 46 One 680 2 41 1605 One 682 2 21 39 One 685 2 41 1624 One 687 2 24 47 One 688 2 19 41 One 689 2 41 1402 One 690 2 23 3 One 691 2 21 9 One 692 2 24 48 One 693 2 41 1630 One 694 2 23 19 One 696 2 41 1631 One 697 2 24 50 One 698 2 41 1632 One 699 2 41 1633 One 700 2 41 1605 One 701 2 15 48 One 702 2 21 17 One 703 2 15 52 One 704 2 15 36 One 705 2 41 1634 One 708 2 41 1826 One 709 2 21 13 One 710 2 24 51 One 711 2 17 74 One 712 2 41 1635 One 715 2 24 52 One 718 2 41 1636 One 719 2 17 35 One 720 2 16 59 One 721 2 41 1637 One 722 2 23 15 One 723 2 41 1638 One 724 2 19 67 One 725 2 19 76 One 726 2 19 70 One 727 2 22 3 One 728 2 15 33 One 730 2 24 54 One 731 2 41 1397 One 732 2 41 1639 One 733 2 41 1640 One 737 2 41 1641 One 738 2 24 57 One 739 2 19 67 One 741 2 15 34 One 742 2 23 18 One 743 2 24 58 One 744 2 23 16 One 745 2 17 59 One 748 2 21 30 One 749 2 21 31 One 750 2 21 32 One 751 2 21 44 One 752 2 21 45 One 754 2 24 59 One 755 2 24 60 One 756 2 15 32 One 757 2 41 1643 One 759 2 22 4 One 760 2 41 1644 One 762 2 24 61 One 763 2 41 1645 One 764 2 24 62 One 765 2 24 63 One 766 2 24 64 One 768 2 23 17 One 769 2 41 950 One 771 2 24 66 One 773 2 41 1646 One 774 2 41 1030 One 776 2 23 14 One 777 2 41 1647 One 778 2 41 1648 One 779 2 19 62 One 781 2 19 12 One 782 2 21 63 One 783 2 15 35 One 784 2 15 36 One 785 2 15 37 One 786 2 41 1649 One 789 2 41 1650 One 790 2 41 1651 One 791 2 41 1652 One 792 2 24 68 One 793 2 41 1653 One 794 2 41 1654 One 795 2 24 69 One 796 2 41 1655 One 798 2 23 19 One 799 2 41 1656 One 800 2 22 5 One 801 2 22 6 One 802 2 22 7 One 804 2 24 70 One 805 2 24 71 One 807 2 41 1657 One 808 2 24 72 One 809 2 41 1658 One 811 2 19 77 One 813 2 41 1659 One 815 2 41 1660 One 816 2 41 1661 One 818 2 24 73 One 819 2 41 1662 One 820 2 41 1663 One 821 2 24 74 One 822 2 24 75 One 824 2 41 1664 One 825 2 41 1166 One 827 2 24 77 One 828 2 41 1665 One 829 2 19 43 One 830 2 24 78 One 831 2 41 1666 One 832 2 41 1667 One 836 2 41 1668 One 838 2 24 79 One 839 2 24 80 One 840 2 24 81 One 844 2 41 1669 One 845 2 41 1670 One 847 2 41 1671 One 848 2 41 1672 One 850 2 24 83 One 851 2 41 1673 One 852 2 41 1674 One 853 2 24 84 One 856 2 24 85 One 857 2 41 1675 One 858 2 17 95 One 860 2 41 1676 One 861 2 18 71 One 862 2 41 1677 One 863 3 1 One 864 2 8 10 One 865 2 18 53 One 868 2 0 0 One 870 2 41 1597 Group object One 21 2 5 4 One 22 2 5 2 One 23 2 5 3 One 24 2 5 5 One 25 2 5 6 One 26 2 5 0 One 27 2 5 1 One 29 2 41 1705 One 30 2 13 1 One 31 2 13 14 One 32 2 13 11 One 33 2 13 9 One 34 2 13 12 One 35 2 13 8 One 36 2 13 17 One 37 2 13 15 One 38 2 13 4 One 39 2 13 10 One 40 2 13 15 One 41 2 13 14 One 42 2 13 13 One 43 2 13 3 One 44 2 13 5 One 45 2 13 7 One 46 2 13 6 One 47 2 13 2 One 48 2 13 25 One 49 2 13 18 One 50 2 13 22 One 51 2 13 26 One 52 2 13 23 One 53 2 13 20 One 54 2 13 21 One 55 2 13 19 One 56 2 13 24 One 57 2 13 27 One 58 2 13 32 One 59 2 13 35 One 60 2 13 37 One 61 2 13 38 One 62 2 13 30 One 63 2 13 34 One 64 2 13 28 One 65 2 13 29 One 66 2 13 31 One 67 2 13 40 One 68 2 13 33 One 69 2 13 39 One 70 2 13 36 One 71 2 13 41 One 72 2 13 42 One 73 2 13 43 One 74 2 13 44 One 75 2 13 45 One 76 2 13 46 One 77 2 13 47 One 78 2 13 49 One 79 2 13 50 One 80 2 13 51 One 81 2 13 52 One 82 2 13 53 One 83 2 13 54 One 84 2 10 30 One 85 2 10 31 One 86 2 10 32 One 87 2 10 27 One 88 2 10 28 One 89 2 10 29 One 90 2 3 3 One 91 2 3 20 One 92 2 3 21 One 93 2 3 22 One 94 2 3 70 One 95 2 3 11 One 96 2 3 12 One 97 2 3 13 One 98 2 3 15 One 99 2 3 16 One 100 2 3 17 One 101 2 3 34 One 102 2 3 33 One 103 2 3 38 One 104 2 3 39 One 105 2 3 40 One 106 2 3 41 One 107 2 3 42 One 108 2 3 43 One 109 2 3 45 One 110 2 3 44 One 111 2 3 47 One 112 2 3 48 One 113 2 3 49 One 114 2 3 50 One 115 2 3 51 One 116 2 3 52 One 117 2 3 53 One 118 2 3 56 One 119 2 3 54 One 120 2 3 55 One 121 2 3 46 One 122 2 3 32 One 123 2 3 0 One 124 2 3 1 One 125 2 3 23 One 126 2 3 26 One 127 2 3 27 One 128 2 3 90 One 129 2 3 29 One 130 2 3 93 One 131 2 3 89 One 170 2 41 1735 One 173 0 3 One 174 0 4 One 175 0 6 One 176 0 1 One 177 0 2 One 178 0 43 One 179 0 43 One 180 0 36 One 181 0 7 One 182 0 43 One 183 0 43 One 184 0 8 One 185 0 9 One 186 0 10 One 187 0 11 One 188 0 12 One 189 0 13 One 190 0 14 One 191 0 37 One 192 0 35 One 193 0 39 One 194 0 22 One 195 2 13 89 One 196 2 41 1743 One 197 0 28 One 198 0 40 One 199 0 41 One 200 0 29 One 201 0 34 One 202 0 24 One 203 0 21 One 204 0 38 One 205 0 43 One 206 0 15 One 207 0 23 One 208 0 27 One 209 0 33 One 210 0 26 One 211 0 32 One 212 0 31 One 213 0 30 One 214 0 5 One 215 0 25 One 216 0 18 One 217 0 17 One 218 0 16 One 219 0 20 One 220 0 19 One 221 0 42 One 293 2 13 84 One 330 2 9 9 One 331 2 9 10 One 332 2 9 13 One 333 2 9 14 One 334 2 9 18 One 335 2 9 19 One 336 2 9 22 One 337 2 9 23 One 338 2 10 0 One 339 2 10 1 One 340 2 10 2 One 341 2 10 3 One 342 2 10 4 One 343 2 10 5 One 344 2 10 6 One 345 2 10 24 One 346 2 10 26 One 347 2 10 25 One 348 2 10 23 One 349 2 10 22 One 379 2 9 27 One 380 2 9 28 One 381 2 9 31 One 382 2 9 32 One 383 2 9 0 One 384 2 9 1 One 385 2 9 4 One 386 2 9 5 One 387 2 9 36 One 388 2 9 37 One 389 2 10 34 One 390 2 10 42 One 391 2 10 43 One 392 2 10 44 One 393 2 10 49 One 394 2 10 50 One 395 2 10 45 One 396 2 10 47 One 397 2 10 46 One 398 2 10 48 One 399 2 41 1706 One 400 2 3 59 One 401 2 3 57 One 402 2 3 58 One 403 2 3 60 One 404 2 3 61 One 405 2 3 68 One 406 2 41 327 One 407 2 3 66 One 408 2 3 62 One 409 2 3 63 One 410 2 3 65 One 411 2 3 67 One 412 2 3 69 One 413 2 3 14 One 414 2 3 84 One 423 2 9 40 One 424 2 9 41 One 480 2 10 7 One 481 2 10 7 One 482 2 10 7 One 483 2 10 8 One 484 2 10 8 One 485 2 10 8 One 486 2 10 12 One 487 2 10 12 One 488 2 10 9 One 489 2 10 9 One 490 2 10 9 One 491 2 10 13 One 492 2 10 14 One 493 2 10 15 One 494 2 10 16 One 495 2 10 17 One 496 2 10 10 One 497 2 10 11 One 498 2 13 48 One 499 2 3 18 One 500 2 10 51 One 501 2 10 52 One 502 2 10 53 One 503 2 8 38 One 504 2 8 31 One 505 2 8 37 One 506 2 8 21 One 507 2 8 21 One 508 2 8 22 One 509 2 8 19 One 510 2 8 20 One 511 2 8 23 One 512 2 9 45 One 513 2 9 46 One 514 2 9 49 One 515 2 9 50 One 516 2 9 54 One 517 2 9 55 One 518 2 9 58 One 519 2 9 59 One 524 2 41 1708 One 525 2 41 1697 One 527 2 41 1730 One 528 2 41 1701 One 529 2 41 1696 One 530 2 41 1731 One 531 2 3 2 One 532 2 41 1702 One 533 2 41 1711 One 535 2 41 1736 One 536 2 41 1728 One 537 2 41 1712 One 538 2 41 1703 One 539 2 41 1713 One 540 2 41 1715 One 541 2 41 1714 One 542 2 41 1716 One 543 2 41 1700 One 544 2 41 1717 One 545 2 41 1699 One 546 2 41 1709 One 547 2 41 1710 One 548 2 41 1707 One 549 2 41 1721 One 550 2 41 1704 One 551 2 41 1718 One 552 2 41 1719 One 553 2 41 1698 One 554 2 41 1722 One 555 2 41 1720 One 556 2 41 1734 One 557 2 41 1739 One 558 2 41 1738 One 559 2 41 1737 One 560 2 41 1732 One 561 2 41 1741 One 562 2 41 1733 One 563 2 41 1729 One 564 2 10 72 One 565 2 13 16 One 566 0 43 One 578 2 10 45 zangband/lib/script/tk/config/value0000644000000000000000000000570010250356274016342 0ustar rootroot# Automatically generated. Do not edit. Manage TERM_BLUE #0033FF Manage TERM_DARK #000000 Manage TERM_GREEN #00BB00 Manage TERM_L_BLUE #00FFFF Manage TERM_L_DARK #666666 Manage TERM_L_GREEN #00FF00 Manage TERM_L_RED #FF0000 Manage TERM_L_UMBER #CC9933 Manage TERM_L_WHITE #BBBBBB Manage TERM_ORANGE #FF9900 Manage TERM_RED #BB0000 Manage TERM_SLATE #777777 Manage TERM_UMBER #993300 Manage TERM_VIOLET #FF00FF Manage TERM_WHITE #FFFFFF Manage TERM_YELLOW #FFFF00 Manage TV_AMULET #FF9900 Manage TV_ARCANE_BOOK #BBBBBB Manage TV_ARROW #CC9933 Manage TV_BOLT #CC9933 Manage TV_BOOTS #CC9933 Manage TV_BOTTLE #FFFFFF Manage TV_BOW #993300 Manage TV_CHAOS_BOOK #FF0000 Manage TV_CHEST #777777 Manage TV_CLOAK #CC9933 Manage TV_CORPSE #BBBBBB Manage TV_CROWN #CC9933 Manage TV_DEATH_BOOK #666666 Manage TV_DIGGING #777777 Manage TV_DRAG_ARMOR #777777 Manage TV_FIGURINE #BBBBBB Manage TV_FLASK #FFFF00 Manage TV_FOOD #CC9933 Manage TV_GLOVES #CC9933 Manage TV_GOLD #FF9900 Manage TV_HAFTED #FFFFFF Manage TV_HARD_ARMOR #777777 Manage TV_HELM #CC9933 Manage TV_JUNK #FFFFFF Manage TV_LIFE_BOOK #FFFFFF Manage TV_LITE #FFFF00 Manage TV_NATURE_BOOK #00FF00 Manage TV_NONE #FFFFFF Manage TV_POLEARM #FFFFFF Manage TV_POTION #00FFFF Manage TV_RING #BB0000 Manage TV_ROD #FF00FF Manage TV_SCROLL #FFFFFF Manage TV_SHIELD #CC9933 Manage TV_SHOT #CC9933 Manage TV_SKELETON #FFFFFF Manage TV_SOFT_ARMOR #777777 Manage TV_SORCERY_BOOK #00FFFF Manage TV_SPIKE #777777 Manage TV_STAFF #CC9933 Manage TV_STATUE #BBBBBB Manage TV_SWORD #FFFFFF Manage TV_TRUMP_BOOK #FF9900 Manage TV_WAND #00BB00 Manage bigmap,scale 4 Manage choicewindow,autoexpand 1 Manage choicewindow,show 0 Manage choicewindow,showicon 0 Manage config,prefix classic16 Manage font,autobar {Helvetica 12} Manage font,choice {Helvetica 12} Manage font,inventory {Helvetica 12} Manage font,knowledge {Helvetica 12} Manage font,macros {Courier 12} Manage font,magic {Helvetica 12} Manage font,message {Helvetica 12} Manage font,messages {Helvetica 12} Manage font,misc {Helvetica 10} Manage font,miscPopup {Helvetica 12} Manage font,monster {Helvetica 12} Manage font,options {Helvetica 12} Manage font,recall {Helvetica 12} Manage font,status {Helvetica 12} Manage font,statusBar {Helvetica 12} Manage font,store {Helvetica 12} Manage inventory,alwaysOnTop 1 Manage inventory,style new Manage know_unseen_artifacts 0 Manage listBG #222222 Manage listHilite #339999 Manage listInactive #888888 Manage main,statusbar,color White Manage map,detail high Manage message,float 0 Manage message2window,show 0 Manage messages,max 256 Manage misc,float 0 Manage misc,layout tall Manage misc,mode,armor_class 1 Manage misc,mode,exp 1 Manage misc,textLabels 0 Manage progress,showBars 1 Manage progress,showNumbers 1 Manage recall,show 1 Manage recall,showicon 1 Manage record,dump 1 Manage record,message 1 Manage record,photo 1 Manage settings,showUnused 0 Manage store,style new Manage tip,current 0 Manage tip,show 1 Manage warning,setup 0 Manage window,autosave 1 zangband/lib/script/tk/config/adam-assign0000644000000000000000000005321710250356274017420 0ustar rootroot# Automatically generated. Do not edit. Assign icon Type none Type blank Type default Type feature Type adam Type pattern Type town Type ascii Group feature One 1 2 3 0 Feat icon 1 One 2 2 3 0 Feat icon 2 One 3 2 4 439 Feat none 1 One 4 2 3 22 Feat none 1 One 5 2 3 23 Feat none 1 One 6 2 4 24 Feat none 1 One 7 2 4 27 Feat none 1 One 8 2 4 27 Feat none 1 One 9 2 4 24 Feat none 1 One 10 2 4 27 Feat none 1 One 11 2 4 24 Feat none 1 One 12 2 4 27 Feat none 1 One 13 2 3 29 Feat none 13 One 14 2 3 30 Feat none 14 One 15 2 3 31 Feat none 15 One 16 2 4 46 Feat none 1 One 17 2 4 43 Feat none 1 One 18 2 4 43 Feat none 1 One 19 2 4 43 Feat none 1 One 20 2 4 49 Feat none 1 One 21 2 4 52 Feat none 1 One 22 2 4 40 Feat none 1 One 23 2 4 40 Feat none 1 One 24 2 4 34 Feat none 1 One 25 2 4 34 Feat none 1 One 26 2 4 34 Feat none 1 One 27 2 4 34 Feat none 1 One 28 2 4 37 Feat none 1 One 29 2 4 37 Feat none 1 One 30 2 4 37 Feat none 1 One 31 2 4 37 Feat none 1 One 32 2 3 21 Feat none 1 One 33 2 3 21 Feat none 1 One 34 2 3 21 Feat none 1 One 35 2 3 21 Feat none 1 One 36 2 3 21 Feat none 1 One 37 2 3 21 Feat none 1 One 38 2 3 21 Feat none 1 One 39 2 3 21 Feat none 1 One 40 2 3 21 Feat none 1 One 41 2 3 21 Feat none 1 One 42 2 3 21 Feat none 1 One 43 2 3 21 Feat none 1 One 44 2 3 21 Feat none 1 One 45 2 3 21 Feat none 1 One 46 2 3 21 Feat none 1 One 47 2 3 21 Feat none 1 One 48 2 3 3 Feat icon 48 One 49 2 3 36 Feat icon 1 One 50 2 3 12 Feat icon 50 One 51 2 3 6 Feat icon 51 One 52 2 3 12 Feat icon 52 One 53 2 3 6 Feat icon 53 One 54 2 3 15 Feat icon 54 One 55 2 3 9 Feat icon 55 One 56 2 3 3 Feat icon 56 One 57 2 3 3 Feat icon 57 One 58 2 3 3 Feat icon 58 One 59 2 3 3 Feat icon 59 One 60 2 3 18 Feat icon 60 One 61 2 3 18 Feat icon 61 One 62 2 3 18 Feat icon 62 One 63 2 3 18 Feat icon 63 One 64 2 4 111 Feat none 1 One 65 2 5 6 Feat icon 65 One 66 2 5 0 Feat icon 66 One 67 2 5 3 Feat icon 67 One 68 2 5 0 Feat icon 68 One 69 2 5 3 Feat icon 69 One 70 2 5 0 Feat icon 70 One 71 2 5 9 Feat icon 71 One 72 2 5 6 Feat icon 72 One 73 2 5 9 Feat none 73 One 74 2 3 24 Feat none 74 One 75 2 3 25 Feat none 75 One 76 2 3 26 Feat none 76 One 77 2 3 27 Feat none 77 One 78 2 3 28 Feat none 78 One 79 2 3 29 Feat none 79 One 80 2 3 30 Feat none 80 One 81 2 3 31 Feat none 81 One 82 2 3 32 Feat none 82 One 83 2 3 51 Feat icon 83 One 84 2 3 48 Feat icon 84 One 85 2 3 62 Feat icon 85 One 86 2 3 59 Feat icon 86 One 87 2 3 65 Feat none 87 One 88 2 3 33 Feat icon 88 One 89 2 3 56 Feat icon 89 One 90 2 4 52 Feat none 1 One 91 2 2 0 Feat none 91 One 92 2 2 0 Feat none 92 One 93 2 2 0 Feat none 93 One 94 2 2 0 Feat none 94 One 95 2 2 0 Feat none 95 One 96 2 3 39 Feat icon 1 One 97 2 3 55 Feat none 97 One 98 2 2 0 Feat none 98 One 99 2 2 0 Feat none 99 One 100 2 2 0 Feat none 100 One 101 2 2 0 Feat none 101 One 102 2 2 0 Feat none 102 One 103 2 2 0 Feat none 103 One 104 2 2 0 Feat none 104 One 105 2 2 0 Feat none 105 One 106 2 2 0 Feat none 106 One 107 2 2 0 Feat none 107 One 108 2 2 0 Feat none 108 One 109 2 2 0 Feat none 109 One 110 2 2 0 Feat none 110 One 111 2 2 0 Feat none 111 One 112 2 2 0 Feat none 112 One 113 2 2 0 Feat none 113 One 114 2 2 0 Feat none 114 One 115 2 2 0 Feat none 115 One 116 2 2 0 Feat none 116 One 117 2 2 0 Feat none 117 One 118 2 2 0 Feat none 118 One 119 2 2 0 Feat none 119 One 120 2 2 0 Feat none 120 One 121 2 2 0 Feat none 121 One 122 2 2 0 Feat none 122 One 123 2 2 0 Feat none 123 One 124 2 2 0 Feat none 124 One 125 2 2 0 Feat none 125 One 126 2 2 0 Feat none 126 One 127 2 2 0 Feat none 127 One 128 2 3 31 Feat none 128 One 129 2 3 31 Feat none 129 One 130 2 3 31 Feat none 130 One 131 2 3 31 Feat none 131 One 132 2 3 31 Feat none 132 One 133 2 3 31 Feat none 133 One 134 2 3 31 Feat none 134 One 135 2 3 31 Feat none 135 One 136 2 3 31 Feat none 136 One 137 2 3 31 Feat none 137 One 138 2 3 31 Feat none 138 One 139 2 3 31 Feat none 139 One 140 2 3 31 Feat none 140 One 141 2 3 31 Feat none 141 One 142 2 3 31 Feat none 142 One 143 2 3 31 Feat none 143 One 144 2 3 31 Feat none 144 One 145 2 3 31 Feat none 145 One 146 2 3 31 Feat none 146 One 147 2 3 31 Feat none 147 One 148 2 3 31 Feat none 148 One 149 2 3 31 Feat none 149 One 150 2 3 31 Feat none 150 One 151 2 3 31 Feat none 151 One 152 2 3 31 Feat none 152 One 153 2 3 31 Feat none 153 One 154 2 3 31 Feat none 154 One 155 2 3 31 Feat none 155 One 156 2 3 31 Feat none 156 One 157 2 3 31 Feat none 157 One 158 2 3 31 Feat none 158 One 159 2 3 31 Feat none 159 Group monster One 1 2 4 1344 One 2 2 4 1250 One 3 2 4 1760 One 4 2 4 1761 One 5 2 4 1762 One 6 2 4 1763 One 7 2 4 954 One 8 2 4 1345 One 9 2 4 1346 One 10 2 4 1347 One 11 2 4 1348 One 12 2 4 1349 One 13 2 4 1350 One 14 2 4 1351 One 15 2 4 1352 One 16 2 4 1353 One 17 2 4 1354 One 18 2 4 1355 One 19 2 4 1536 One 20 2 4 1311 One 21 2 4 1093 One 22 3 2 One 23 2 4 1538 One 24 2 4 1205 One 25 2 4 1283 One 26 2 4 1284 One 27 2 4 1413 One 28 2 4 1092 One 29 2 4 1305 One 30 2 4 1306 One 31 2 4 1437 One 32 2 4 1243 One 33 2 4 1094 One 34 2 4 1540 One 35 2 4 955 One 36 2 4 1191 One 37 2 4 1199 One 38 2 4 1764 One 39 2 4 1539 One 40 2 4 934 One 41 2 4 1285 One 42 2 4 1206 One 43 2 4 1356 One 44 2 4 1357 One 45 2 4 1358 One 46 2 4 1359 One 47 2 4 935 One 48 2 4 1290 One 49 2 4 1192 One 50 2 4 1096 One 51 2 4 1032 One 52 2 4 1447 One 53 2 4 956 One 54 2 4 1765 One 55 2 4 957 One 56 2 4 1095 One 57 2 4 1541 One 58 2 4 1438 One 59 2 4 1097 One 60 2 4 1117 One 61 2 4 1766 One 62 2 4 1251 One 63 2 4 1360 One 64 2 4 1291 One 65 2 4 1017 One 66 2 4 1293 One 67 2 4 1207 One 68 2 4 1767 One 69 2 4 1309 One 70 2 4 1768 One 71 2 4 1320 One 72 2 4 936 One 73 2 4 1292 One 74 2 4 1267 One 75 2 4 1193 One 76 2 4 1312 One 77 2 4 1208 One 78 2 4 1439 One 79 2 4 1440 One 80 2 4 1244 One 81 2 4 1769 One 82 2 4 1098 One 83 2 4 1361 One 84 2 4 1294 One 85 2 4 928 One 86 2 4 1414 One 87 2 4 1770 One 88 2 4 1771 One 89 2 4 1441 One 90 2 4 1099 One 91 2 4 1417 One 92 2 4 1542 One 93 2 4 1363 One 94 2 4 1321 One 95 2 4 1772 One 96 2 4 1773 One 97 2 4 1370 One 98 2 4 1774 One 99 2 4 1295 One 100 2 4 1018 One 101 2 4 1296 One 102 2 4 1307 One 103 2 4 1286 One 104 2 4 1245 One 105 2 4 1442 One 106 2 4 1100 One 107 2 4 1543 One 108 2 4 937 One 109 2 4 1364 One 110 2 4 1365 One 111 2 4 1544 One 112 2 4 1545 One 113 2 4 1313 One 114 2 4 1200 One 115 2 4 1775 One 116 2 4 1367 One 117 2 4 929 One 118 2 4 1326 One 119 2 4 1101 One 120 2 4 1776 One 121 2 4 1102 One 122 2 4 1269 One 123 2 4 1452 One 124 2 4 1546 One 125 2 4 1547 One 126 2 4 1327 One 127 2 4 1118 One 128 2 4 1041 One 129 2 4 1246 One 130 2 4 1322 One 131 2 4 1297 One 132 2 4 1287 One 133 2 4 1019 One 134 2 4 1103 One 135 2 4 1308 One 136 2 4 1418 One 137 2 4 1368 One 138 2 4 1548 One 139 2 4 1777 One 140 2 4 1328 One 141 2 4 1448 One 142 2 4 1369 One 143 2 4 1104 One 144 2 4 1549 One 145 2 4 1778 One 146 2 4 1314 One 147 2 4 1370 One 148 2 4 1042 One 149 2 4 1329 One 150 2 4 1371 One 151 2 4 1550 One 152 2 4 1551 One 153 2 4 1552 One 154 2 4 1169 One 155 2 4 1288 One 156 2 4 1415 One 157 2 4 1033 One 158 2 4 1779 One 159 2 4 1780 One 160 2 4 1781 One 161 2 4 1782 One 162 2 4 1330 One 163 2 4 1213 One 164 2 4 1214 One 165 2 4 1215 One 166 2 4 1216 One 167 2 4 1217 One 168 2 4 1194 One 169 2 4 1372 One 170 2 4 1783 One 171 2 4 1105 One 172 2 4 1784 One 173 2 4 1553 One 174 2 4 1554 One 175 2 4 1119 One 176 2 4 1426 One 177 2 4 1785 One 178 2 4 1270 One 179 2 4 1786 One 180 2 4 1449 One 181 2 4 1787 One 182 2 4 1271 One 183 2 4 1789 One 184 2 4 938 One 185 2 4 1555 One 186 2 4 1331 One 187 2 4 1790 One 188 2 4 1791 One 189 2 4 1792 One 190 2 4 1315 One 191 2 4 1793 One 192 2 4 1316 One 193 2 4 1218 One 194 2 4 1043 One 195 2 4 930 One 196 2 4 958 One 197 2 4 1009 One 198 2 4 1252 One 199 2 4 1556 One 200 2 4 1557 One 201 2 4 1558 One 202 2 4 1559 One 203 2 4 1560 One 204 2 4 1219 One 205 2 4 1794 One 206 2 4 1795 One 207 2 4 1796 One 208 2 4 1453 One 209 2 4 1034 One 210 2 4 1106 One 211 2 4 959 One 212 2 4 1298 One 213 2 4 1443 One 214 2 4 1561 One 215 2 4 1332 One 216 2 4 1375 One 217 2 4 1797 One 218 2 4 1788 One 219 2 4 1798 One 220 2 4 1799 One 221 2 4 1800 One 222 2 4 1562 One 223 2 4 1563 One 224 2 4 1450 One 225 2 4 1374 One 226 2 4 1273 One 227 2 4 991 One 228 2 4 1419 One 229 2 4 1454 One 230 2 4 1253 One 231 2 4 1020 One 232 2 4 1209 One 233 2 4 1299 One 234 2 4 1120 One 235 2 4 1564 One 236 2 4 1051 One 237 2 4 1451 One 238 2 4 1067 One 239 2 4 931 One 240 2 4 1376 One 241 2 4 1377 One 242 2 4 1801 One 243 2 4 1802 One 244 2 4 1333 One 245 2 4 1300 One 246 2 4 1565 One 247 2 0 0 One 248 2 4 1803 One 249 2 4 1804 One 250 2 4 1011 One 251 2 4 1805 One 252 2 4 1289 One 253 2 4 1566 One 254 2 4 1567 One 255 2 4 1073 One 256 2 4 1257 One 257 2 4 960 One 258 2 4 1568 One 259 2 4 1010 One 260 2 4 1334 One 261 2 4 1258 One 262 2 4 1068 One 263 2 4 1806 One 264 2 4 1335 One 265 2 4 1807 One 266 2 4 1808 One 267 2 4 939 One 268 2 4 1809 One 269 2 4 1323 One 270 2 4 1416 One 271 2 4 1171 One 272 2 4 1172 One 273 2 4 1569 One 274 2 4 1570 One 275 2 4 1121 One 276 2 4 1210 One 277 2 4 1122 One 278 2 4 1074 One 279 2 4 1035 One 280 2 4 1044 One 281 3 0 One 282 2 4 1173 One 283 2 4 1145 One 284 2 4 1810 One 285 2 4 1336 One 286 2 4 1301 One 287 2 4 1012 One 288 2 4 1075 One 289 2 4 1013 One 290 2 4 1811 One 291 2 4 1378 One 292 2 4 1812 One 293 2 4 1813 One 294 2 4 1045 One 295 2 4 1814 One 296 2 4 1046 One 297 2 4 1129 One 298 2 4 1815 One 299 2 4 1816 One 300 2 4 1817 One 301 2 4 1107 One 302 2 4 1818 One 303 2 4 992 One 304 2 4 1123 One 305 2 4 993 One 306 2 4 994 One 307 2 4 1174 One 308 2 4 1175 One 309 2 4 1176 One 310 2 4 942 One 311 2 4 1819 One 312 2 4 961 One 313 2 4 1337 One 314 2 4 1338 One 315 2 4 1339 One 316 2 4 940 One 317 2 4 1820 One 318 2 4 1821 One 319 2 4 1822 One 320 2 4 1016 One 321 2 4 1076 One 322 2 4 1014 One 323 2 4 1259 One 324 2 4 1317 One 325 2 4 1015 One 326 2 4 1823 One 327 2 4 1824 One 328 2 4 1825 One 329 2 4 1826 One 330 2 4 1340 One 331 2 4 1124 One 332 2 4 1827 One 333 2 0 0 One 334 2 4 1571 One 335 2 4 1829 One 336 2 4 1572 One 337 2 4 1177 One 338 2 4 1178 One 339 2 4 1254 One 340 2 4 1179 One 341 2 4 1036 One 342 2 4 1082 One 343 2 4 1170 One 344 2 4 1573 One 345 2 4 1830 One 346 2 4 1831 One 347 2 4 962 One 348 2 4 1276 One 349 2 4 1078 One 350 2 4 1341 One 351 2 4 1201 One 352 2 4 941 One 353 2 4 1832 One 354 2 4 1428 One 355 2 4 1429 One 356 2 4 1342 One 357 2 4 1833 One 358 2 4 1430 One 359 2 4 1431 One 360 2 4 1834 One 361 2 4 1835 One 362 2 4 1064 One 363 2 4 1836 One 364 2 4 1837 One 365 2 4 1838 One 366 2 4 1053 One 367 2 4 1260 One 368 2 4 1574 One 369 2 4 1125 One 370 2 4 1839 One 371 2 4 1302 One 372 2 4 1379 One 373 2 4 1343 One 374 2 4 1840 One 375 2 4 1575 One 376 2 4 1380 One 377 2 4 1202 One 378 2 4 1052 One 379 2 4 1841 One 380 2 4 1382 One 381 2 4 1576 One 382 2 4 1577 One 383 2 4 1578 One 384 2 4 1842 One 385 2 4 1579 One 386 2 4 1843 One 387 2 4 1109 One 388 2 4 1844 One 389 2 4 1580 One 390 2 4 1065 One 391 2 4 1203 One 392 2 4 1381 One 393 2 0 0 One 394 2 4 1021 One 395 2 4 1211 One 396 2 4 1845 One 397 2 4 1582 One 398 2 4 1261 One 399 2 4 1846 One 400 2 4 1279 One 401 2 4 1130 One 402 2 4 1847 One 403 2 4 1131 One 404 2 4 1444 One 405 2 4 1055 One 406 2 4 1848 One 407 2 4 1849 One 408 2 4 1196 One 409 2 4 1583 One 410 2 4 1850 One 411 2 4 1427 One 412 2 4 1255 One 413 2 4 1383 One 414 2 4 1851 One 415 2 4 1069 One 416 2 4 1151 One 417 2 4 943 One 418 2 4 1679 One 419 2 4 1584 One 420 2 4 1585 One 421 2 4 1056 One 422 2 4 1852 One 423 2 4 932 One 424 2 4 1132 One 426 2 4 1853 One 427 2 4 1586 One 428 2 4 1180 One 429 2 4 1181 One 430 2 4 1070 One 431 2 4 1072 One 432 2 4 1146 One 433 2 4 1037 One 434 2 4 1587 One 435 2 4 1262 One 436 2 4 1324 One 437 2 4 1854 One 438 2 4 1855 One 439 2 4 1588 One 440 2 4 1110 One 441 2 4 1589 One 442 2 4 1384 One 443 2 4 1856 One 444 2 4 1857 One 445 2 4 1858 One 446 2 4 1445 One 447 2 4 1409 One 448 2 4 1590 One 449 2 4 1386 One 450 2 4 1387 One 451 2 4 1591 One 452 2 4 1859 One 453 2 4 1111 One 454 2 4 1133 One 455 2 4 1593 One 456 2 4 944 One 457 2 4 1470 One 458 2 4 1594 One 459 2 4 1220 One 460 2 4 1221 One 461 2 4 1222 One 462 2 4 1223 One 463 2 4 1860 One 464 2 4 1263 One 465 2 4 1420 One 466 2 4 1861 One 467 2 4 1862 One 468 2 4 1863 One 469 2 4 1195 One 470 2 4 1152 One 471 2 4 1224 One 472 2 4 1038 One 473 2 4 1473 One 474 2 4 1057 One 475 2 4 1864 One 476 2 4 1865 One 477 2 4 1022 One 478 2 4 1058 One 479 2 4 1071 One 480 2 4 1083 One 481 2 4 1126 One 482 2 4 1866 One 483 2 4 1867 One 484 2 4 1868 One 485 2 4 1388 One 486 2 4 1318 One 487 2 4 1077 One 488 2 4 1595 One 489 2 4 1869 One 490 2 4 1870 One 491 2 4 1135 One 492 2 4 1871 One 493 2 4 1136 One 494 2 4 1137 One 495 2 4 1138 One 496 2 4 1134 One 497 2 4 1596 One 498 2 4 1597 One 499 2 4 1153 One 500 2 4 1421 One 501 2 4 1225 One 502 2 4 1226 One 503 2 4 1227 One 504 2 4 1228 One 505 2 4 1598 One 506 2 4 1599 One 507 2 4 1600 One 508 2 4 1024 One 509 2 4 1139 One 510 2 4 995 One 511 2 4 945 One 512 2 4 996 One 513 2 4 1601 One 514 2 4 997 One 515 2 4 1212 One 516 2 4 1390 One 517 2 4 1602 One 518 2 4 1059 One 519 2 4 1872 One 520 2 4 1147 One 521 2 4 1603 One 522 2 4 1873 One 523 2 4 1874 One 524 2 4 1127 One 525 2 4 998 One 526 2 4 999 One 527 2 4 1604 One 528 2 4 1875 One 529 2 4 1605 One 530 2 4 1264 One 531 2 4 1876 One 532 2 4 1392 One 533 2 4 1877 One 534 2 4 1025 One 535 2 4 1878 One 536 2 4 1879 One 537 2 4 1001 One 538 2 4 1140 One 539 2 4 1606 One 540 2 4 1182 One 541 2 4 1303 One 542 2 4 1183 One 543 2 4 1184 One 544 2 4 1880 One 545 2 4 1000 One 546 2 4 1229 One 547 2 4 1410 One 548 2 4 1197 One 549 2 4 1230 One 550 2 4 1167 One 551 2 4 1141 One 552 2 4 1881 One 553 2 4 1607 One 554 2 4 1154 One 555 2 4 1882 One 556 2 4 1231 One 557 2 4 1608 One 558 2 4 1609 One 559 2 4 1232 One 560 2 4 1233 One 561 2 4 1234 One 562 2 4 1235 One 563 2 4 1236 One 564 2 4 1610 One 565 2 0 0 One 566 2 4 1048 One 567 2 0 0 One 568 2 4 1884 One 569 2 4 1611 One 570 2 4 1002 One 571 2 4 1612 One 572 2 4 1885 One 573 2 4 1613 One 574 2 4 1614 One 575 2 4 1066 One 576 2 4 1886 One 578 2 4 1887 One 579 2 4 1615 One 580 2 4 1888 One 581 2 4 1198 One 582 2 4 1003 One 583 2 4 1889 One 584 2 4 1004 One 585 2 4 1304 One 586 2 4 1680 One 587 2 4 1472 One 588 2 4 1432 One 589 2 4 1237 One 590 2 4 1238 One 591 2 4 1239 One 592 2 4 1240 One 593 2 4 1241 One 595 2 4 1592 One 597 2 4 1396 One 598 2 4 1616 One 599 2 4 1433 One 600 2 4 1434 One 601 2 4 968 One 602 2 4 969 One 603 2 4 1247 One 604 2 4 1155 One 605 2 4 946 One 606 2 4 1005 One 607 2 4 1156 One 608 2 4 1617 One 609 2 4 1618 One 612 2 4 1157 One 614 2 4 1112 One 615 2 4 1006 One 616 2 4 1242 One 617 2 4 970 One 618 2 4 971 One 619 2 4 1619 One 620 2 4 1142 One 621 2 4 1143 One 622 2 4 1411 One 623 2 4 1148 One 624 2 4 972 One 630 2 4 1144 One 631 2 4 1620 One 632 2 4 1446 One 633 2 4 1084 One 634 2 4 1079 One 635 2 4 1113 One 636 2 4 1398 One 637 2 4 1399 One 638 2 4 1400 One 639 2 4 1168 One 640 2 4 951 One 641 2 4 1039 One 642 2 4 1325 One 643 2 4 973 One 644 2 4 974 One 645 2 4 975 One 646 2 4 976 One 648 2 4 1621 One 649 2 4 1049 One 651 2 4 1622 One 657 2 4 1281 One 658 2 4 1060 One 659 2 4 1623 One 660 2 4 1625 One 661 2 4 947 One 662 2 4 1626 One 663 2 4 1627 One 664 2 4 1248 One 665 2 4 1026 One 666 2 4 1628 One 667 2 4 1028 One 673 3 1 One 675 2 4 977 One 676 2 4 978 One 677 2 4 1629 One 679 2 4 1007 One 685 2 4 1624 One 687 2 4 1008 One 688 2 4 1114 One 689 2 4 1402 One 690 2 4 1029 One 691 2 4 1266 One 692 2 4 1466 One 693 2 4 1630 One 696 2 4 1631 One 697 2 4 979 One 698 2 4 1632 One 699 2 4 1633 One 701 2 4 981 One 702 2 4 1080 One 703 2 4 980 One 705 2 4 1634 One 710 2 4 982 One 711 2 4 1319 One 712 2 4 1635 One 715 2 4 1465 One 718 2 4 1636 One 720 2 4 1461 One 721 2 4 1637 One 722 2 4 1469 One 723 2 4 1638 One 724 2 4 1185 One 725 2 4 1186 One 726 2 4 1187 One 727 2 4 1085 One 728 2 4 983 One 730 2 4 1040 One 732 2 4 1639 One 733 2 4 1640 One 737 2 4 1641 One 738 2 4 1163 One 739 2 4 1642 One 741 2 4 984 One 743 2 4 952 One 744 2 4 1164 One 748 2 4 1422 One 749 2 4 1423 One 750 2 4 1424 One 751 2 4 1435 One 752 2 4 1436 One 754 2 4 1115 One 755 2 4 1149 One 756 2 4 985 One 757 2 4 1643 One 759 2 4 1086 One 760 2 4 1644 One 762 2 4 1282 One 763 2 4 1645 One 764 2 4 948 One 765 2 4 949 One 766 2 4 986 One 768 2 4 1165 One 769 2 4 950 One 771 2 4 1406 One 773 2 4 1646 One 774 2 4 1030 One 777 2 4 1647 One 778 2 4 1648 One 779 2 4 1189 One 783 2 4 987 One 784 2 4 988 One 785 2 4 989 One 786 2 4 1649 One 789 2 4 1650 One 790 2 4 1651 One 791 2 4 1652 One 792 2 4 1031 One 793 2 4 1653 One 794 2 4 1654 One 795 2 4 990 One 796 2 4 1655 One 798 2 4 1061 One 799 2 4 1656 One 800 2 4 1087 One 801 2 4 1088 One 802 2 4 1089 One 804 2 4 1062 One 805 2 4 1249 One 807 2 4 1657 One 808 2 4 1128 One 809 2 4 1658 One 811 2 4 1190 One 813 2 4 1659 One 815 2 4 1660 One 816 2 4 1661 One 818 2 4 1407 One 819 2 4 1662 One 820 2 4 1663 One 821 2 4 1090 One 822 2 4 1091 One 824 2 4 1664 One 825 2 4 1166 One 827 2 4 953 One 828 2 4 1665 One 829 2 4 963 One 830 2 4 1425 One 831 2 4 1666 One 832 2 4 1667 One 836 2 4 1668 One 838 2 4 1116 One 839 2 4 1463 One 840 2 4 965 One 844 2 4 1669 One 845 2 4 1670 One 847 2 4 1671 One 848 2 4 1672 One 850 2 4 966 One 851 2 4 1673 One 852 2 4 1674 One 853 2 4 967 One 856 2 4 1464 One 857 2 4 1675 One 858 2 4 1408 One 860 2 4 1676 One 861 2 4 1081 One 862 2 4 1677 One 868 2 0 0 Group object One 21 2 4 452 One 22 2 4 450 One 23 2 4 451 One 24 2 4 453 One 25 2 4 454 One 26 2 4 448 One 27 2 4 449 One 29 2 4 1705 One 30 2 4 333 One 31 2 4 346 One 32 2 4 343 One 33 2 4 341 One 34 2 4 344 One 35 2 4 340 One 36 2 4 350 One 37 2 4 348 One 38 2 4 336 One 39 2 4 342 One 40 2 4 349 One 41 2 4 347 One 42 2 4 345 One 43 2 4 335 One 44 2 4 337 One 45 2 4 339 One 46 2 4 338 One 47 2 4 334 One 48 2 4 358 One 49 2 4 351 One 50 2 4 355 One 51 2 4 359 One 52 2 4 356 One 53 2 4 353 One 54 2 4 354 One 55 2 4 352 One 56 2 4 357 One 57 2 4 360 One 58 2 4 365 One 59 2 4 368 One 60 2 4 370 One 61 2 4 371 One 62 2 4 363 One 63 2 4 367 One 64 2 4 361 One 65 2 4 362 One 66 2 4 364 One 67 2 4 373 One 68 2 4 366 One 69 2 4 372 One 70 2 4 369 One 71 2 4 374 One 72 2 4 375 One 73 2 4 376 One 74 2 4 377 One 75 2 4 378 One 76 2 4 379 One 77 2 4 380 One 78 2 4 384 One 79 2 4 385 One 80 2 4 386 One 81 2 4 387 One 82 2 4 388 One 83 2 4 389 One 84 2 4 463 One 85 2 4 464 One 86 2 4 465 One 87 2 4 460 One 88 2 4 461 One 89 2 4 462 One 90 2 4 298 One 91 2 4 270 One 92 2 4 271 One 93 2 4 272 One 94 2 4 248 One 95 2 4 249 One 96 2 4 250 One 97 2 4 251 One 98 2 4 252 One 99 2 4 253 One 100 2 4 254 One 101 2 4 300 One 102 2 4 299 One 103 2 4 301 One 104 2 4 302 One 105 2 4 303 One 106 2 4 304 One 107 2 4 305 One 108 2 4 306 One 109 2 4 308 One 110 2 4 307 One 111 2 4 310 One 112 2 4 311 One 113 2 4 312 One 114 2 4 313 One 115 2 4 314 One 116 2 4 315 One 117 2 4 316 One 118 2 4 319 One 119 2 4 318 One 120 2 4 317 One 121 2 4 309 One 122 2 4 280 One 123 2 4 296 One 124 2 4 297 One 125 2 4 273 One 126 2 4 274 One 127 2 4 275 One 128 2 4 276 One 129 2 4 277 One 130 2 4 278 One 131 2 4 279 One 170 2 4 1735 One 173 2 4 126 One 174 2 4 126 One 175 2 4 126 One 176 2 4 126 One 177 2 4 126 One 178 2 4 126 One 179 2 4 126 One 180 2 4 126 One 181 2 4 126 One 182 2 4 126 One 183 2 4 126 One 184 2 4 126 One 185 2 4 126 One 186 2 4 126 One 187 2 4 126 One 188 2 4 126 One 189 2 4 126 One 190 2 4 126 One 191 2 4 126 One 192 2 4 126 One 193 2 4 126 One 194 2 4 126 One 195 2 4 1745 One 196 2 4 1743 One 197 2 4 126 One 198 2 4 126 One 199 2 4 126 One 200 2 4 126 One 201 2 4 126 One 202 2 4 126 One 203 2 4 126 One 204 2 4 126 One 205 2 4 126 One 206 2 4 126 One 207 2 4 126 One 208 2 4 126 One 209 2 4 126 One 210 2 4 126 One 211 2 4 126 One 212 2 4 126 One 213 2 4 126 One 214 2 4 126 One 215 2 4 126 One 216 2 4 126 One 217 2 4 126 One 218 2 4 126 One 219 2 4 126 One 220 2 4 126 One 221 2 4 126 One 293 2 4 1744 One 330 2 4 420 One 331 2 4 421 One 332 2 4 422 One 333 2 4 423 One 334 2 4 412 One 335 2 4 413 One 336 2 4 414 One 337 2 4 415 One 338 2 4 153 One 339 2 4 154 One 340 2 4 155 One 341 2 4 156 One 342 2 4 157 One 343 2 4 158 One 344 2 4 159 One 345 2 4 457 One 346 2 4 459 One 347 2 4 458 One 348 2 4 456 One 349 2 4 455 One 379 2 4 416 One 380 2 4 417 One 381 2 4 418 One 382 2 4 419 One 383 2 4 408 One 384 2 4 409 One 385 2 4 410 One 386 2 4 411 One 387 2 4 424 One 388 2 4 425 One 389 2 4 466 One 390 2 4 467 One 391 2 4 468 One 392 2 4 469 One 393 2 4 474 One 394 2 4 475 One 395 2 4 470 One 396 2 4 472 One 397 2 4 471 One 398 2 4 473 One 399 2 4 1706 One 400 2 4 322 One 401 2 4 320 One 402 2 4 321 One 403 2 4 323 One 404 2 4 324 One 405 2 4 331 One 406 2 4 327 One 407 2 4 329 One 408 2 4 325 One 409 2 4 326 One 410 2 4 328 One 411 2 4 330 One 412 2 4 332 One 413 2 4 255 One 414 2 4 281 One 423 2 4 426 One 424 2 4 427 One 480 2 4 113 One 481 2 4 113 One 482 2 4 113 One 483 2 4 114 One 484 2 4 114 One 485 2 4 114 One 486 2 4 118 One 487 2 4 118 One 488 2 4 115 One 489 2 4 115 One 490 2 4 115 One 491 2 4 119 One 492 2 4 120 One 493 2 4 121 One 494 2 4 122 One 495 2 4 123 One 496 2 4 116 One 497 2 4 117 One 498 2 4 382 One 499 2 4 383 One 500 2 4 477 One 501 2 4 478 One 502 2 4 479 One 503 2 4 150 One 504 2 4 151 One 505 2 4 152 One 506 2 4 143 One 507 2 4 145 One 508 2 4 146 One 509 2 4 147 One 510 2 4 148 One 511 2 4 149 One 512 2 4 432 One 513 2 4 433 One 514 2 4 434 One 515 2 4 435 One 516 2 4 428 One 517 2 4 429 One 518 2 4 430 One 519 2 4 431 One 524 2 4 1708 One 525 2 4 1697 One 527 2 4 1730 One 528 2 4 1701 One 529 2 4 1696 One 530 2 4 1731 One 531 2 4 1740 One 532 2 4 1702 One 533 2 4 1711 One 535 2 4 1736 One 536 2 4 1728 One 537 2 4 1712 One 538 2 4 1703 One 539 2 4 1713 One 540 2 4 1715 One 541 2 4 1714 One 542 2 4 1716 One 543 2 4 1700 One 544 2 4 1717 One 545 2 4 1699 One 546 2 4 1709 One 547 2 4 1710 One 548 2 4 1707 One 549 2 4 1721 One 550 2 4 1704 One 551 2 4 1718 One 552 2 4 1719 One 553 2 4 1698 One 554 2 4 1722 One 555 2 4 1720 One 556 2 4 1734 One 557 2 4 1739 One 558 2 4 1738 One 559 2 4 1737 One 560 2 4 1732 One 561 2 4 1741 One 562 2 4 1733 One 563 2 4 1729 zangband/lib/script/tk/config/adamNN.cfg0000644000000000000000000000112010250356274017112 0ustar rootroot# File: adamNN.cfg # Purpose: icon configuration file # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Extract the icon size from the configuration prefix scan [Global config,prefix] adam%d iconSize # Initialize the icon environment NSConfig::InitIcons $iconSize # Common stuff NSConfig::SourceOne adamNN-common 1 # This file is shared by adam16.cfg and adam32.cfg NSConfig::ShareConfigFile assign adam-assign zangband/lib/script/tk/config/classic-assign0000644000000000000000000001100010250356274020117 0ustar rootroot# Automatically generated. Do not edit. Assign icon Type none Type blank Type default Type feature Type adam Type town Type pattern Type ascii Group feature One 1 2 3 0 Feat icon 1 One 2 2 3 0 Feat icon 2 One 3 2 4 439 Feat none 1 One 4 2 3 22 Feat none 1 One 5 2 3 23 Feat none 1 One 6 2 4 24 Feat none 1 One 7 2 4 27 Feat none 1 One 8 2 4 27 Feat none 1 One 9 2 4 24 Feat none 1 One 10 2 4 27 Feat none 1 One 11 2 4 24 Feat none 1 One 12 2 4 27 Feat none 1 One 13 2 3 29 Feat none 13 One 14 2 3 30 Feat none 14 One 15 2 3 31 Feat none 15 One 16 2 4 46 Feat none 1 One 17 2 4 43 Feat none 1 One 18 2 4 43 Feat none 1 One 19 2 4 43 Feat none 1 One 20 2 4 49 Feat none 1 One 21 2 4 52 Feat none 1 One 22 2 4 40 Feat none 1 One 23 2 4 40 Feat none 1 One 24 2 4 34 Feat none 1 One 25 2 4 34 Feat none 1 One 26 2 4 34 Feat none 1 One 27 2 4 34 Feat none 1 One 28 2 4 37 Feat none 1 One 29 2 4 37 Feat none 1 One 30 2 4 37 Feat none 1 One 31 2 4 37 Feat none 1 One 32 2 3 21 Feat none 1 One 33 2 3 21 Feat none 1 One 34 2 3 21 Feat none 1 One 35 2 3 21 Feat none 1 One 36 2 3 21 Feat none 1 One 37 2 3 21 Feat none 1 One 38 2 3 21 Feat none 1 One 39 2 3 21 Feat none 1 One 40 2 3 21 Feat none 1 One 41 2 3 21 Feat none 1 One 42 2 3 21 Feat none 1 One 43 2 3 21 Feat none 1 One 44 2 3 21 Feat none 1 One 45 2 3 21 Feat none 1 One 46 2 3 21 Feat none 1 One 47 2 3 21 Feat none 1 One 48 2 3 3 Feat icon 48 One 49 2 3 36 Feat icon 1 One 50 2 3 12 Feat icon 50 One 51 2 3 6 Feat icon 51 One 52 2 3 12 Feat icon 52 One 53 2 3 6 Feat icon 53 One 54 2 3 15 Feat icon 54 One 55 2 3 9 Feat icon 55 One 56 2 3 3 Feat icon 56 One 57 2 3 3 Feat icon 57 One 58 2 3 3 Feat icon 58 One 59 2 3 3 Feat icon 59 One 60 2 3 18 Feat icon 60 One 61 2 3 18 Feat icon 61 One 62 2 3 18 Feat icon 62 One 63 2 3 18 Feat icon 63 One 64 2 4 111 Feat none 1 One 65 2 6 6 Feat icon 65 One 66 2 6 0 Feat icon 66 One 67 2 6 3 Feat icon 67 One 68 2 6 0 Feat icon 68 One 69 2 6 3 Feat icon 69 One 70 2 6 0 Feat icon 70 One 71 2 6 9 Feat icon 71 One 72 2 6 6 Feat icon 72 One 73 2 6 9 Feat none 73 One 74 2 3 24 Feat none 74 One 75 2 3 25 Feat none 75 One 76 2 3 26 Feat none 76 One 77 2 3 27 Feat none 77 One 78 2 3 28 Feat none 78 One 79 2 3 29 Feat none 79 One 80 2 3 30 Feat none 80 One 81 2 3 31 Feat none 81 One 82 2 3 32 Feat none 82 One 83 2 3 51 Feat icon 83 One 84 2 3 48 Feat icon 84 One 85 2 3 62 Feat icon 85 One 86 2 3 59 Feat icon 86 One 87 2 3 65 Feat none 87 One 88 2 3 33 Feat icon 88 One 89 2 3 56 Feat icon 89 One 90 2 4 52 Feat none 1 One 91 2 2 0 Feat none 91 One 92 2 2 0 Feat none 92 One 93 2 2 0 Feat none 93 One 94 2 2 0 Feat none 94 One 95 2 2 0 Feat none 95 One 96 2 3 39 Feat icon 96 One 97 2 3 55 Feat none 97 One 98 2 2 0 Feat none 98 One 99 2 2 0 Feat none 99 One 100 2 2 0 Feat none 100 One 101 2 2 0 Feat none 101 One 102 2 2 0 Feat none 102 One 103 2 2 0 Feat none 103 One 104 2 2 0 Feat none 104 One 105 2 2 0 Feat none 105 One 106 2 2 0 Feat none 106 One 107 2 2 0 Feat none 107 One 108 2 2 0 Feat none 108 One 109 2 2 0 Feat none 109 One 110 2 2 0 Feat none 110 One 111 2 2 0 Feat none 111 One 112 2 2 0 Feat none 112 One 113 2 2 0 Feat none 113 One 114 2 2 0 Feat none 114 One 115 2 2 0 Feat none 115 One 116 2 2 0 Feat none 116 One 117 2 2 0 Feat none 117 One 118 2 2 0 Feat none 118 One 119 2 2 0 Feat none 119 One 120 2 2 0 Feat none 120 One 121 2 2 0 Feat none 121 One 122 2 2 0 Feat none 122 One 123 2 2 0 Feat none 123 One 124 2 2 0 Feat none 124 One 125 2 2 0 Feat none 125 One 126 2 2 0 Feat none 126 One 127 2 2 0 Feat none 127 One 128 2 3 31 Feat none 128 One 129 2 3 31 Feat none 129 One 130 2 3 31 Feat none 130 One 131 2 3 31 Feat none 131 One 132 2 3 31 Feat none 132 One 133 2 3 31 Feat none 133 One 134 2 3 31 Feat none 134 One 135 2 3 31 Feat none 135 One 136 2 3 31 Feat none 136 One 137 2 3 31 Feat none 137 One 138 2 3 31 Feat none 138 One 139 2 3 31 Feat none 139 One 140 2 3 31 Feat none 140 One 141 2 3 31 Feat none 141 One 142 2 3 31 Feat none 142 One 143 2 3 31 Feat none 143 One 144 2 3 31 Feat none 144 One 145 2 3 31 Feat none 145 One 146 2 3 31 Feat none 146 One 147 2 3 31 Feat none 147 One 148 2 3 31 Feat none 148 One 149 2 3 31 Feat none 149 One 150 2 3 31 Feat none 150 One 151 2 3 31 Feat none 151 One 152 2 3 31 Feat none 152 One 153 2 3 31 Feat none 153 One 154 2 3 31 Feat none 154 One 155 2 3 31 Feat none 155 One 156 2 3 31 Feat none 156 One 157 2 3 31 Feat none 157 One 158 2 3 31 Feat none 158 One 159 2 3 31 Feat none 159 Group monster One 144 2 1 0 One 247 2 0 0 One 333 2 0 0 One 393 2 0 0 One 565 2 0 0 One 567 2 0 0 One 803 2 0 0 One 868 2 0 0 Group object zangband/lib/script/tk/config/classicNN.cfg0000644000000000000000000000120010250356274017630 0ustar rootroot# File: classicNN.cfg # Purpose: icon configuration file # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Extract the icon size from the configuration prefix scan [Global config,prefix] classic%d iconSize # Initialize the icon environment NSConfig::InitIcons $iconSize # Hack NSConfig::NoMoreIcons # This file is shared by classicNN.cfg files NSConfig::ShareConfigFile assign classic-assign # Even more common ascii stuff NSConfig::SourceOne classic-common zangband/lib/script/tk/config/dg32.cfg0000644000000000000000000000077510250356274016532 0ustar rootroot# File: dg32.cfg # Purpose: icon configuration file # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # ### You must call "angband init_icons" to initialize the icon ### environment. The argument is the size of icons to use, ### either 16 or 32. NSConfig::InitIcons 32 NSConfig::SourceOne classic-common # Hack NSConfig::NoMoreIcons zangband/lib/script/tk/config/makefile.zb0000644000000000000000000000054610250356274017420 0ustar rootrootsubdir = ./lib/script/tk/config/ ## makefile.zb srcfiles += lib/script/tk/config/makefile.zb files += $(addprefix lib/script/tk/config/, \ adamNN+feat.cfg birth classic-common config geometry \ adam+feat-assign adamNN-common classic+feat-assign \ classicNN+feat.cfg dg32-assign value adam-assign \ adamNN.cfg classic-assign classicNN.cfg dg32.cfg \ ) zangband/lib/script/tk/image/0000755000000000000000000000000010250356274015116 5ustar rootrootzangband/lib/script/tk/image/ac.gif0000644000000000000000000000217110250356274016171 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,uH°à@*DH€1cDz;f¬1b>¼X¨P¶l3,f‘XÇ!Ëò£KŒ ‹±Ìã²#̃9zÌFRäŸrÒäYŒÀÏ£C‚^:Ñ(R K 5}z”I–RMúpêÖªµ~*v¬Ù€!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/au.gif0000644000000000000000000000214410250356274016213 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,`H° A#,H@C ˜Åk¯Y(Á‘cƃ+^ühpcÇ 2¤h£CA‡Èb²#Ë‘2SÞœu%Í—=w&š²gB£3S&Ì©t¦Ó§P£J!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-activate.gif0000644000000000000000000000223410250356274020717 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,˜ Ø  •‚(Ø€ j&,h„ ¼;ÔîDl0"Œw.Â;h„£Ç‹ÔFÁÑHIŠS¢ÄèÊK—C²TIeÔ(’GŽä„G ¥j-íI”1¢vFià2¦S¤>‰R9RÒˆÐ¢ðˆª„È•*N–aÇ’½¹iJ¤[]ÂŒ‰5¥Í— ©öôiç§M‰y+ÖtÈP`@!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-book.gif0000644000000000000000000000050510250356274020050 0ustar rootrootGIF89a²3f™Ìÿ333!ù  ,ƒ3"""ff33™3™f3ÌfÌ™3ÿ™ÿÌfÿÿÿ?pÉI«½8ëÍ»ÿ`'áP è@nÑ$Ç`lB€1‚ YMØ(……C@¨ÒPÙnå4šž5Äíz;!þ€This animated GIF file was constructed using Ulead GIF Animator Lite, visit us at http://www.ulead.com to find out more.USSPCMT!ÿ PIANYGIF2.0Image;zangband/lib/script/tk/image/button-down.gif0000644000000000000000000000220510250356274020064 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ, D‚A  Áƒ &<Èp¡@‘"J”HÐà@3btHÀPE†?ô’$CC* ©\‰¤ 0cªx ’€Œo‚¼q㦌Ÿ?Cð<ÈSŇHCZ©Ñ‚O 0½aPņ«W R噣`ˆ ^ÁÈÁ³lŽ® Ï–]{ã¬[²k!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-food.gif0000644000000000000000000000215710250356274020052 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,k H° ÁJ رSÉ „ BL8¢=s3.l(°RFtö@‚ÄÈ‘€½öÚˆ2%K‚ ºtH³ Mšnæ4˜RgNœ;gž‰SgËŠQŽ´W¡GŒP™Rìè4¢Ó’«:­É•@@!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-help.gif0000644000000000000000000000017710250356274020053 0ustar rootrootGIF89a"!ù ,‚€€ÿÿ¿¿¿ÿÿÿDxºÀp9!^T“R˲ƒÆ5„~¢$”h»aK) Énå­š°›/PcŽ¡@p¨6ÄesÔØ0 ΋헽`ºŒ;zangband/lib/script/tk/image/button-options.gif0000644000000000000000000000014510250356274020611 0ustar rootrootGIF89a!ù ,ÿÿÿ6œiÀí@˜”B$k½'¿¿L™&†Á¸&*J—wZŒ:ÀsSÖâéX ÎCqL±M„äÃ|Š&¢;zangband/lib/script/tk/image/button-potion.gif0000644000000000000000000000217410250356274020432 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,x H° Á|ùÈ¡[ˆÎ À„èè!̇Ã!tâ`L'Ž@Ç‚è`G zQÄÁ Œ— ØøH0°–Ù`p“Is MœÀ²ñ4ø3hСm*=:“¨Q¥Jã&­*U‹¦VÕŠ•IŽâ*bEG®lO¬h!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-rod.gif0000644000000000000000000000213110250356274017677 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,U m Áƒ±aCX!…µ=tH@›¶m¶iSH1#Æm;œ'r`¹qEž —Ñ"Å•!"„åˆeL™i’pØP Î’~–ZÒÑN H!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-scroll.gif0000644000000000000000000000214410250356274020415 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,` H° Áj*,(Ηǃ!£¢Å‹0 J“i£Ç#U”ƒdIi?nì2¥E“' ðȲ¥K˜$mTÉQçÀ‘%G¢ÔÈÓ#Aq‘’*]I"EŒeJ%!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-staff.gif0000644000000000000000000000217510250356274020226 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,y (°Áƒ $hpà† 4he"… 40ÅHƒ‡ 'F92²bà ³eëˆG†FHF¶m¥­(#_Ühk=¶t¢4ÔÜ æNG@†¸˜.T¤jÕ‹Oúغs¡@†Hx=VìØe!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-up.gif0000644000000000000000000000217410250356274017546 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,x `àÃÁ‚8&8  *´Á€ÁÀ‚ 32ô‘Ð!ŽŒ d”XQ£É†3.X¹ #LJ*Yº¤h‘aÈ+bh£§Ïm0ôùS Ä’YʨӇQˆP! |ù‘@Ô¨<;ò$ZT§Ð‚`Ã:å(U¬X«6!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/button-wand.gif0000644000000000000000000000215310250356274020050 0ustar rootrootGIF89a÷"3"333"""222;;;%t&&t33fDUfw|3fKtCv&Lt&ttDHHM,R,V0Z4^8f{f3b<f33GGHHd@jDnHrL{HzG{HvQzT}X{{{{ff3CCCKKKTTT\\\zGG{HHzzG{{Hdddiiisssyyy3™&&›33™ÿ"Â3ÿ&&Â33Ì33ÿ^‘f™&L›&s›3f™LÂfÿ&JÂ3fÌ3fÿMM›Mt›MsÂffÌ„‰™ª£3™#ª-®-³/¸2»7À3Ì:Á 3ÿf™fÿ+‘‘3™™™ÿ1—ÊMšÂc–Éf™Ìf™ÿMÂÂeËþeþþ™™3™33£¸¡9‚\†`ŠdŽh‘km™f–p›u žx™f3ªC³K»S®{¢|­z™ff­GG®HH­zG®{H®{{ÉÍÕÕÍ#ç éöë%ë/ë7ÿ#ÿ.ÿ6ë> ÿ> Ã[ÌdÍjÍuÓkÜtëC%ÿF(ÿO1ÿT6â|á{ÿfÌffÖppá{Há{{™™™™3¤€­­££=™™f­­G®®H££p®®{͔̙͙̈́ Ì™3Ö£=å…á—ó‹ü”ÿšÿŸÿ™3ᢠá¬á­ÿ©ÿ´ÿ£=ÿº#Ì™fÖ£pÿ™fá®Há®{ÿ£pÿÂ+ÿÉ2ÿË;ÿÖ=ÿÿÌÌfÖÖpÿÌfÿÖpƒƒƒŠŠŠ“““ššš¢¢¢ªªª³³³ºººÌ™™Ö££ÿ™™á®®ÿ££ÌÌ™ÖÖ£ÿÌ™ÿÖ£ÿÿ™ÄÄÄËËËÓÓÓÜÜÜÿÌÌÿÖÖÿÿÌâââìììðððÿÿÿÿÿÿ,g $À€À 9ˆ+'€Ã:D'ÄëU$h!†Šãx0rè4P]9’A†yq¥Dk°fhâÆy’PÈ0'D¡:/"M:p)Ó†H<èôiÔ©Xk!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/cha.gif0000644000000000000000000000216010250356274016337 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,lH° ÁƒtÀÐÀ† :0´¨"Ê‹ E421cEŠ3:ìXÑOÉE&?:ÈãÇŸ%*´æÔ¡Ru^…8”ëT”†‚}!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/dex.gif0000644000000000000000000000214510250356274016367 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,aH° Áƒ(\ˆÐˆBk­90¸0›ƒˆ)b[q"Ål 7ÚÊxб“ÆØòXpÈ!UJtI³æÂ‘Ô´‰ãÂ21úäi«hQˆ8uÒ\x3§Â@ŸBº3 !þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/exp.gif0000644000000000000000000000216410250356274016404 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,pH°à@ DHÐA‹„B”Iò'NŠ@ õh´éGL›Je ”Ô«T­^ºµkÓ€!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/level.gif0000644000000000000000000000214410250356274016715 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,`H°  $¨0áA' :q@ÑĈiÜ(ˆ"Ç=‚ܨh£ÇŠ Kj¬ˆò£ÊŽF’4fÍ™+YŠd¨gO˜2sgËŽjê<Ú‘¦LK™>Ý!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/look.gif0000644000000000000000000000214210250356274016550 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,^H° Áƒ:ppà†ëÆ¡DhØŒT,(±6hF†Û¨Ñ1ŒÙRŠœ±qÈElÙ 4ÆrˆÍ›/³ 12ò¦O—&cºüIT"6D“.LÊhÓ§P!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/ms-back.gif0000644000000000000000000000042410250356274017122 0ustar rootrootGIF89a ²3f™Ìÿ333!ù , ‚/`ÿ/ÿ/Ïÿ¿¿¿ÿÿ&HºÜ ÎW(´^º§[!Ž…@ ¬ìš¡& ÏÖ•\5ø^%!þ€This animated GIF file was constructed using Ulead GIF Animator Lite, visit us at http://www.ulead.com to find out more.USSPCMT!ÿ PIANYGIF2.0Image;zangband/lib/script/tk/image/ms-book-closed.gif0000644000000000000000000000017110250356274020422 0ustar rootrootGIF89a"!ù ,‚€€¿¿¿ÿÿÿ>HºÀp9!^T“R˲Ö\3|d% c bTªš€æ–N,Ó@ÀÞ[Nè»–#0 §®x 56Ɔó”C^$VH;zangband/lib/script/tk/image/ms-book-open.gif0000644000000000000000000000021010250356274020104 0ustar rootrootGIF89a"!ù ,‚€€ÿÿ¿¿¿ÿÿÿMXÚþ°G(Ò…íìZÓ¤x§ Œ×HÃ'¬d¼'3Û­ýÆ­wk  »Z(䵊‹×Îé FSžmƒÃ®x<þ‚Uè4 ;zangband/lib/script/tk/image/ms-help-page.gif0000644000000000000000000000016410250356274020065 0ustar rootrootGIF89a "!ù , ‚ÿÿÿ¿¿¿ÿÿÿ9º\®Œ8Ô“Ô øÂDÐðIçØEJbŽ)jö‰ïÔÎNg5‹öŠp¥LDh:Ÿ›¨Tš;zangband/lib/script/tk/image/ms-hide.gif0000644000000000000000000000051610250356274017135 0ustar rootrootGIF89a²3f™Ìÿ333!ù ,‚ÿÿòÿÿ¿¿¿ÿÿÿ`8 ÜþL ª½HÈÝ.A!FišÚãé¦H°¤Üwa8NÌ5W‰G0¿ZO· ŸÈ™p8F¥¶å’ a´YA‰c‚ǬñtøI«³åÝGÔZŸéïéî}m: !þ€This animated GIF file was constructed using Ulead GIF Animator Lite, visit us at http://www.ulead.com to find out more.USSPCMT!ÿ PIANYGIF2.0Image;zangband/lib/script/tk/image/ms-next.gif0000644000000000000000000000042310250356274017177 0ustar rootrootGIF89a ²3f™Ìÿ333!ù , ‚/`ÿ/ÿ/Ïÿ¿¿¿ÿÿÏÿÿ%Hºü*Ș-èÌmF(†ÅhŠྮ Q0Ó.­`J!þ€This animated GIF file was constructed using Ulead GIF Animator Lite, visit us at http://www.ulead.com to find out more.USSPCMT!ÿ PIANYGIF2.0Image;zangband/lib/script/tk/image/ms-refresh.gif0000644000000000000000000000052210250356274017657 0ustar rootrootGIF89a ²3f™Ìÿ333!ù , ƒ``/`€€€¿¿¿ÿÿÏÏÏðððÿÿÿLÈI‹%(ëLÀ݈‰‡m‚¬À© )rmfÀ¸HcF0 0‘a— „bºZO k¡£ÈHÂ'ñˆ‹U¹ÑçÉÊÜÍÎèkÍ^E!þ€This animated GIF file was constructed using Ulead GIF Animator Lite, visit us at http://www.ulead.com to find out more.USSPCMT!ÿ PIANYGIF2.0Image;zangband/lib/script/tk/image/ms-show.gif0000644000000000000000000000050610250356274017203 0ustar rootrootGIF89a²3f™Ìÿ333!ù ,‚ÿÿòÿÿÿÿÿX8º þ° „½WÎã M!Ž£¦àI®&@¤ 4-jî[Ëk@BáÅ«J9à°àõ ’d°93ÞNÀÈóŠmUmÜ“wk:…˜Vèæü,S¶êu îft" !þ€This animated GIF file was constructed using Ulead GIF Animator Lite, visit us at http://www.ulead.com to find out more.USSPCMT!ÿ PIANYGIF2.0Image;zangband/lib/script/tk/image/open.gif0000644000000000000000000000013210250356274016542 0ustar rootrootGIF89a Ñ€€ÿÿÿÿÿ!ù d, *œo È÷^cKIj…¶ ‚—†ã'rª´n«lï ÀìJ C*‡(;zangband/lib/script/tk/image/pwrdLogo175.gif0000644000000000000000000001743410250356274017650 0ustar rootrootGIF89ao¯÷½½½ç91sÞ9µ½½)s½µµç11{ÿÿÿÆÆ½ç)!k½ÆÆï9­µµ1sç!9{÷÷÷çJ!çBç!µÎÎ1s){)J„!1kÖ19{ÞR1ïsÎ{k÷¥Œ÷„ÖcJÿïÿ÷½1!ÞJ)Æ”Œ!B„÷çç11cœ¥­ÿ÷ïÿÖB{ïZ9œ11ïZÿν½­­ÿ½Ö)÷„kB1ZR1R÷µ¥ï{c9RŒk1Jïµ­ÿçÞ÷œÿçBcŒÖsc÷œ„Æ¥œ„19ÞZBÖkRïcBï”{ÿÞÎÿÎÿÎÆ÷­œïsRJc”ïç÷Î1ïRRk”ckœçÞïZ1RïR)ÿÆ­Œœ­„”¥ÎÆÆ¥11÷­)B„ïï÷s„¥k{œ9J„BR”„„µ1)Z{1Bï)ïkJ1ZŒk„­­¥ÆÞÖçÆ¥¥ÎŒ{{Œ¥ÆÆÖÆ1!Æ)÷”{¥­µZs”)k!kc)1kµµÎ¥)!Æ­¥Zs¥¥¥µ¥91ÎŒ„ç„sBcœkZ1JÞçïJcŒÎÎçÖÆÞ{!1½)÷J)Δ„ÿc9ÎÎÆï÷÷c{œks¥””½ccœŒ))µB1ÖJ1ÎB)ïJ!9Z”¥­ÆÖÖç½½ÖŒŒµc)B„1BÆ­­÷ÎÎçcRï­œÿ{Zÿ¥Œ”­Æ½ÆÖ1Bs­µÖZc”œœ½{{­¥œ½½µÎJ9cïÞÞ­””ïÆÆïB”­µ„¥µ”¥ÎŒ”­cs¥Œs„kBZs1B{)9ŒRZ„BJ¥RR½ZZ½Ö{cÿB÷sJZ1)cÖÎÖR)J½œµ{Zs¥„””k{Þ½ÆÎœ¥œck”BJ”眔¥Þ¥œ½cRÖÎÆÆÎÎ1c”Bc¥9J{ss”kkŒkZ„œŒ­{c„”{”Z!B{Rcµ„”µss¥cc½RJ¥!ÎRBµRB¥)ƽÆÎ½ÎçJJŒB9kcZ„­”¥œs„µ{Œk)”Zc!ù,o¯@ÿH° Áƒ*\Ȱ¡C„ D$ É3jܨ1‚$>pÄ$ “L¦Dɲe€0c^„ù’¦KŒFHháovœâÂ…ÊŒ| )Ƥ J}ŠÑ‚¬NJ•«×­`3R@Ñ ¢@Š{¦x)¨ÐŒ2d ýP‚‰W¡|$H0ÁB P @•`nß¾R–ç@³i°¨˜%‰”PY‘ “„ƒC6â`ãV5j¸¨!·£%€8=à¡A¸$ìy!PæÊ#0©ñ[ã$-61†8(変н œ°Ñqi “ŠÿQ|ÀŠ €£›'Zžoy€ÂŒ5€™ëonæÄŒ3PÀ]‘ BqB9péi@€ïQô=QtÁ Ì'ÔŒ4Ss¬š~ä_¥XÕ¬¤at–l€hpp–ZÔ@ È÷ât„@“y –ÁÂøÀúÀM) øâFà0£´·“{9RÔ@8Rj¸€ €‘BsðÁˆ {|Ö\ ÄÓD;d8eK 0I{4aAuŒæ| xðƒ2 †ŠÐ2K~,'ZÞ$°XÝÉ’&¤R Ÿ’…JЮHišn´Ä V†€RŒÿ0dH:XF ¡C/¥:ÒEdQdý À×à‚ ¨úŠ >ˆÃ ð1J Vððaz°(ñL`ê,G ñžcm©À%:ß0„ðí䀅  LkØ:_$B”ãrDA½·ed ˆA=4[ð7$0ˆ¤|ÂÁ£šø[À8«úX0N 4ÙÉ’IÔ0ht1òMt1‡ Œq ̾|m˜œnŸ TA8p0’¤ìEÁY@0dµÅ{ ùÛX´P§Îo lŽÆDŇDKœ‘d&Є·™@ñ¡X`‘† I|À Nì•…¿>ÿA§Ó\Ó7¨( 1¢s.›ÆQifÀãR˜ÐE1ÑC8d~ÅåœóbÄç wa‚ä¤sÀ¨?®ºãS…ÉxM2UPĹ[’ߤ…ˆû¶¡m;"ï‚å¾»îƒ ?ü~`°Ÿ´›ÕÓc_|ƒ|áoz|aÇT¤ }@¡I@ÁƒhA…<@ð{ TtÐAç ëŸO¼&T Òþõé äa¡ë!‘QKO˜#.$7¹á‚P-lA ‘8„4 ¸/w 4Þ~Ò#€0ði¤BÙA `РnèCP0Ã7ØC@ÂFp„ž€@ Wÿ2s€qh8¢e2r¡!' Ї,°¡VotIA ËW¢ ÀF$¿ÀkHà‚ î Ü[0„ ìda[2ÀŠ .Ŭ¤nÌÈJ˜P‚@VæÈvö2 TÍN#;À !J`"ëšRJPƒ»°d˜“ Àé5ç§zY}6°‹7 €ä|Ê„¹„f­ hJT™È€ Lâ)äI0 HA~!7ôq–"(&G,PŠäâv#l’P€$¡mAŒGp  *Œâ¥#ÈÒYpD(:`Êl*Ê Œ˜Ó¥Üâÿ íE8@ùXà3µP¤BiiÀ2©)t¡H‚9’"^¡†z âÈ‚ °À4g9 é„óp´“ õ¤EàC+3¦E"¢ôVÀ%L ××qtvÎ H@ º‘PA¼è¥e hŒHcVv€8¸IQ…°øUÏÜ!  Ã;=ƒ a€–äRŽ`‹_¬8p¢1˜™.Â*ˆˆcX#;-˜¤ lœ%Gñ¢@²Q ³i kKDa5lÍ54À ˆð,œõN Ák´LP fà@³”ÚÒr¨L’©"N HÙê&pfЂÿd¡W:“$ z.ÂéFÈÁËHHÛ:†6<5xf@Æ .{Xlv"*È5¼Tδ …*Ô€iømI )@*ªQŒ­r­\¢âbð‡0€Z&àˆP5pbd Mp¯7²Û‚„M,ˆipY‚4¥n;Ð 5'` *D·r€=áÁæHÉj@í²¢ËaÐ|•Æ®ìì Jh[ˆ3r·öDÊò×¢eÏûøÇ@¶§@V•mÁ¢†éËËÀ0€`Cò€‡NÂM`&P6ð… †è‚§6ƒx*ª€S¨"ÿ[¿ëÀÀpŠA†„¨@DÆÀ&à&,ªXR úЈNt ^ F7Z~£Íè†ðÎ ²¦±Âýl-Ž‘ˆ¨'rE„ôÓí`Ã:ªQ¯ºÄ¢Áz– n‘ ÚC¬À’xÔÀ>Öó\m@‹e)‹±•½Åtõ¡4˜! Pм8Â@PÉ ؤ7¸Ç-n³„­ &C˜Èn›È§o‰‹^“x`(@¶m(äBLû²°öµµÃ|—8hP¬.R²`œejÀ¬D*¾tALRƒh¶fƇD‚ª‰õ§ŸPW#ÿ ¤lLóšï€¸Ðw–ðð²”h}˜ZðbPªö';Iár(®²ë qðõTWp[R(Ù'À s,ÙÕ(Ð#¾ÒRZãhRö×ÿ ~`JÔ"†U0° "R«j‚Õ²`ˆaxvn“6«Àªç*½mÀ]ƒ]àO@;J`ˆz ’`%Xž÷   ®—*  aNE<p sà7§à>»”à`)'°‡.áF€f¨j'ó<9ÒÐ`•Y‘- ØVŸàz)¦Ä©`^ÈS€PaÀ©†j±¶ À q3» ;Ô™ðQ“‡æ`[¸õ2‹Ð B!½…]«Öe¥S#Žé@<+00€-d`J >`‘t0U¤Byt°…51Dà=`…f°²¢ÿiŒÕ‡b‘.1p•„jhѵ° 932¦wP$)+V`8ƨ7ÐQ7\š•@‚Æ0  @‰ªT Ý0"Õb q%•‚±RP0Ð?>ùI"—Ù\ð°ÁOmˆ ©Ð rà ®%9pz° $F¢˜vÉt @R s. @ V0h³? ®PQD3e0yN¢i—-Á¬t@(ƒ`/1߈ƒ/‘œ ~2U —Ы(= 0oy¡™btÖõ„ĉg0›w’ƒ´0î`_35Su1Á³3@ w Pbç* ÿ ô„1àœð$Ë  W@Kpe ±˜àÃYŸ9—©f,½&÷µù¥3BäÐ4~sOôrÆÑy>ójj1žî3S`¹0i—ïr*($(—VoŠrn0ž6AB04zÉP¦'à¡Qq;ÐN/Ý”tOA5+È 0¢ š/Ò zëÂŒÁeXYA3 @H*v2)úÚ_äRM0]>ªI0 %¦²]ª‡ @Т ™!X@=òQŠ.ŠrNpƒã²“$*^daÀŒ25¦:qúrw³°6Ac€1Ïrÿ ’ÓŸÃ =p9€+°°(°cœÊ©êá*û1-2%SZª¦Ê©¤º©ª a`2ÏC}ð ³2=x‚¸š«Ù”<ËkIæ= B `𠸙=q0Æú Âúg‰À zð†p x \&) ˆÐH® fUÆú­¿#lÐH·pgw¦ Ô¹0yÉh(ˆØ”0{ÉŠ{@šcPÍU<0  ï`°`ðÅóf¶õãdU0‰ðu~J>sVšP<€ `PËZëˆ%Z²BQÏ¥«*»²öÄcð[ù«4³4«ÿ$A·q³ ³;k³6{ ÝGA,pAP òÇhéB·Ú€pô'ˆ@„k{Zl€en¡Ö@ò…j¹ñAõÀZb@$ðA¦¸{2dW«jxkt隨†P‚hµhñm½fj«fR΃ ‘·ÙR‹@È9rTdx‰k2ÉÖ¸ÈÖ[…¹ì´¸{ûk,ÄBýæoÓÖ×öÃ@pÿ€[@|V<ŒXNKƒ<Èa|vCç ­Ý€°¨FQË/Ø*½Ú'XûÎ2\I )d¼P UÓ+¾DÏŽ‰„¡uT q!7lÝ’iµ˜ÀÍ:}$À•a‡MÜ·•Å ¥J™ VØ;¥¹ð€¶•ƒãâP>ûMxZ«Ò÷2Ä 3p’ "$b Ð:ÏoáÇÉṉgzoíQ]PŽ®bž1€ ¸Ù;¾ ZÕëýQe0¯[+ ã°ŒÅ6€'ІrÀ஢ªðã½³­>àJ¾ä}ð¸@@•2w›æ.ÿö#! 5Aq¸(F ±à õ”°À7\ã—8#<Àk ANl˶¼üÅÏà 90ÔÎ! £`1Òã8œ²~¸Ûtëḭ—˜Uê{ U@˜Q5 QPQmU4¼lú2 Xäóä–´@ž¨R‡È_ð¬ŠR VÊ ØŽw€ŸÐ V°a#C rÞ߸Àª ké+ÇwÑÐ0GÐ\[ŽL°ÀÍI7ÁÓxè±ß\âù쎞oa70ž!’ÅTý¢xð °á0]•Òý'DU€ Ýè,°Q¶åãœñ𓂉7W\•T@¸hÿhI `G¥)C׳°SI È)ÌÁÛ0oÙçKnwT׋œW¤›à 7Ä™Ð:@.­56“ÂË J­W&PÔ ZÀÁü µ('êIµ:°¿põ'@9)pW%™éR  3,ˆ—<@¶ £¥) ? ¹‘ÝhÙVf0xõ^' Ä]¢’zúeëj¯TXó#p÷d“زøŠò 0M0¨¿”“ŸjgÞ’ç©™3… (Oú’xž>0ë½æÐvÔÎe   /¹æaxO0FP$nÕXøüê+°ŒÐJ`ôŠ‘Y¿ÿ8X$ÿÊŽ3p‡³‘HWsßVÉPKGµûb„%Ìú©†#ã™ ¾ OO0 80"µ/WÆw€eIs ÀA„ .d¸ð’H Ñ@EŒ(ƒÇDC!ÈbW6ð±E) (ð ËnÀ°!ÒgÇ &^|±"‹™áðÓ)ÂŽ˜š¹¡¦X HI³:3$uÂàÓ§<И8±¢·/PðàÅŠ¦h:–‚+E%¨RàoŽ ¨)ò–`F…³z}8BC¨Ñ¤'*øƒoÂC]ZôiÒ(P8`p3? pÿ–ÀOÌ”ì.Ð!Ž’ª.\£q“ŠA€X8Cf´ B! 4“Iy–` n™) ³#øs‘ªÝ4(z\£ÜŒBš£g¸ ‰Š:Áäzlˆ6 ¼” `@™%X£/=.È,®£Üà2¸bÁ…HCJÀCiFH ‘¸ ¯XT #2 : ÁŒæŠ"¶È®ZLh,&˜„ó  +Ô0±+3Byl4á² ßC®"ìØ Ç&¡:À CÔÈc&3¤ %MŽDÀr–¸)KŸ„𽊊RÀHŒÈ‘̓xÌ"ŽÁ€š(#<<ÂÑAAÿ<z€ˆ2Ã)ŒXè@bH4¡8 ç2êA…8b3 œbî¼S$}kÆÌ€c& b0õTÒ®À+‹ $˜©‡f¥õ ¦4Ê(¨( ƒ LðµÅÖZc²W BH`)~)ضdÔ¬.:“ÅóAte :ˆlOu`ÑÚ„*°¬½7%R@‹IðÖEZ΂Æ A_„,h¢…že!ËÜs#WÿZÉÒ¢m¡%, µpZà¬Ò4Y\`‡`X÷àP0nX÷ "&Ï ¶‰±B4ÏQœPâ†Lf\ít°À´‹“FziŒ›Nú ÜŠ—®ÿ:4pXæ,Ða,",† †0ïã¼níãUâ³Pf„¶~‹.´Àà°†´æºà‘Xb¬6nvG´…vši¥w:!X6J ¸Ê–e³Þ:%H!ûÂ.Ûûž07Ï~ bž"BΣÕàT‡‚³"!‚¼"s`Ðav8}$ûœd‡Ö)|ÐÁ‰ SôÓÕ·ÞñÆA£…ëú"È32ÉpEwÈÙ“OW¸ôO=àŒ‹#»qC |§GkŸ`0½ýK½OJ©’afR)ñ-„ñUŽx§Y9 âÖ³*£ð<„ÿðÁýq€rƒ Lƒœá …Èô°b(2`ÃÝäp7àa{¸ ñ…/Ü€p$"ñ gàEÎàDF1]0¡ 0áœ@‹¸Äzæ)¿„çˆ+À wsÃê%bcÝøF8ÆÑ41L. ”ø ^ÀäøG@RƒW €GÅè8q©xùHHFÒÀà³0=@Ž ^ðÉØˆ’)X…‘Ž”’ÐPЕRyJ |²•2ñ$(O¹JY²1–¯œd%—öh¦"’ºeª0Vx"aR!!°‡ ¸‚ ¬˜€€ŽÿT2­"mH& Añ|Á †(üh˜cNš<0Qo^V@¡* á Dh¥˜ HÁ9#ø´ÇUœ”I(1—t"¸)€¢ÀBä¨hƒÀó…8D! ¯HD"&Pð0@m˜@Z¡J0à˜cafA;MD¬¡rôD"òð…(ìažáá' L¤@()/›äA%‘‡f¨BHD¬€8XíDU€*$1D0œmH|i“À Ÿ?[Ö´‰Ò¢ÏA+yµ‰ÔåÒ¥F•>}!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/town16.gif0000644000000000000000000004366110250356274016755 0ustar rootrootGIF89a°€w!ùdÿ,°€‡  "6%)98""&+((2 1 57)#)$%2 +79$ 8&83 :3'((,229*#0/0<3%677??@GWI LW gsd "F+E(U1E8R?K"(i/q*2k"ðž`XÀCZ€£*Ha õ`f6«7ë£GaHÃøBp†¡d í®P£z3Œh"'DF+<ñƒat#¹Hh7\qˆd`Dó˜ÑEæt“Tc zÚŽ5ùŸke« µÃîðÕ ¸6jDð2H 8€Öаª=•l ÇDÂÈ1èî b(¦~z÷ƒˆa 98¡Ú-†„ -Ö~poxKE+WØFÁ¡QkakýAž~êSýnãLßëÙ9ÈŒh# ¸Ø†<Ä ÿ rPc ¢@‰&ÎA Q,Ó¢†8ÎøŠU,ƒ¶d(‡ ±¸C:`õÕä @Èó+PlMÜ@@JÚí’þ¾½wÎ*ƒ¶"†!då ?J ø Ñ’õ”ÿ.ÕHŸA=ªb>ß“I£9*"êx&Cc€ áÀ]œ-– eðŽ€ o  Ö€ æ· V´ Àu9ëÑ ob l’œ€ =¡ Å µQEÐK@ ¦à ¸ ÃÀb§SDG„D9p3vШÑ|ªQVB('C`ð€D‚‡ TTePA(o׸2?°?Õ<¹RU˜o€…Zh…„Ri@24ò  €  À]Ã0'RA`'sUSZÀ‘oxboð4`@‚l ‚s•p8óqš°$ ÿ…À­ð W€ tT[Uä Wð«0*qà ÛsT¬à ¨òH@ e° ¢yÎM¢{|:˜9G —†ìØD?€+ìqŒ&cæÅDNI¡¾¦IÜÿI¸„ôI×]…,‘© 29@\4m&o€ D úÆÄ…`jÝ•rü\Ðp¡Q{\WiêäjŠ3‚ü#[19\(¡ ÑÑ.ε Z@ö S Ú€ ¦§Ýåg`Db‰šüq§Y¤œs3Û”7“ 2d·aA›À ´@ bÍà ªPhàbÕñ œÐ\«@àa9€x‹ËïdPÐ&Ù¨<ô¡„Ähp¨ùQP¤XbK°fîÁVÜD…¢Wp?H–¨É#!WŒgD¥ü-ðü   &à2@ÿ3p/€ÜßÎþÐnjvbä¶'ÉS;ügÀ9 bŒý!¯NáÂ0 1¸nÀ…LÖà 1.,È ’q#& *dÕÂY+ä„Â1eÊD!“¦Œ?©üŒ1RH‘cö” R$M2EŠùDC™ª¬xQF‰4‹ˆÉñRÃHŸÇø,¢A(Ñ”Fw¤L*TT5@B«V¼~õZµÂ—AeŽ|ZÄ(R X¨óJ®Ÿ2©PùIse›2eÖªmz•k•ŸWÔ¾![rd¥ ËÊ…ãĉ«7 eËvWÃ"… í¸RäFŽ2xÇãGÌCþ¨ U™C©!Ï…\rÈ$†ŠR”P°#ÇÓ”f¯€]äŠÌ˜Å_¿:4sŸÆw\­ + ì`¿°ðTCó"Ö£+S XVm£ÖìUT¤4¡*òªØ£bÉë 1*Ñ„ M\APOr¡f^ć2Ð p44˜` –Ä8cŒ1Šèa 4¸"•TNZi#1Äð°?\+‹ X€á…~+!Ç,À€—Ê"ˆ´„D`9𮍢…¡„L)ˆèŽ:²¬ ƒ¸«®8 ;ï¼*J'¥dò¹+¼ ­T 1d›n¶iÅCî+Ä^Úˆã4@™kƨ$alþP›4zX‚?\1Œ“02Å)Èà‡nèÌ?ôØHÒ+ðJªÑè»ÔC1~øñ‚qÌ‘»,-㩎rI‹—ªB€¬1P ƒ10¨UVDzˆ1rò(²ó«-¿z5Øa‹…ÊW-£™fj%q¸i&•Ö꣜¾”# 3ÌH e¢Q†8´'™4Xq…•+Ä0c’18G›m 1ãH;,bƒõåŠB¶_•+$•2‚Iƒ¢ha«KøM«@ËáÞ"†¤ +8pA…* Ê€&=üa¦ãBcdvÐ -¯\6€ZeNä´^‚è+¨ §›pÞþÐ$œmPAE8·i¨š_e…•d¬QfUÜ@ãŒ>äêÆ1а„•VºaÛ›T΀ô‡Pb8,\+¤EI ±T€. ÓXH¡ðS W¹îЀ,šf’ g+ HAHÀŠ 8HIÈI¦Ç5˜Ù- @>w\ôÈ]òüñ+ ³f›iP §™ˆ¡vem@E”ȬIº°ß ãaäÛx¶ÙilV)à ?ÎÐã &˜Bâ ô8é bc*ƒ”BPê(• Œ8 ¥8? „Ä9®¼ð07`(‘(H½«þ±’ŸåJ œ€c.€’^²?bU¡Ç*]²ªÂ+þ$Ð)è’ ɳk5¸!Žu¬ƒô±*Ò„0ÜÇ/l{Ã&’áŠUã1Ý(–p†dtÃâX^ÑŠ·- èàvКBì¡g(ÃÊ@›& ¡nR´K!°0í@K•UHÆ"I&¥ëÊ*g9 €–S@ê,ºÐað%Œ Ï4 ì`‹A Yê(1Í$#Ó‚¥V‘ ¾yˆPC,¶!ŠiLCopÃÞÀ‰ih#ÈЄ@¦±†?½ÂBcîTˆ%Là=°Ê ‘‚1òC„DMøTÂ’ `¡4áÇ<–,À+XQnVº,­ÿÑ~l´\ ²ó€)ÝK™A«‚ðl®VËÙªY‘›…ì'à'M‚`ˆ=ìa«@D;;BC5£列 U|¢!ÙØÄ@ CT£ÖpƒdXa WäIPKÀP<ƒ…䀛JÇR#†u¨¢²‰ØGʃÂi‰W*RÍžr¬Œq*CÇØˆ¿9~IhN’Q°b X *Nƒ°¢ hΦ*ê8jT#AÍLP3”]îÒ‘fLÍ *àe5LH•(Ö a £¡BÆ„*‘®1Pà?`ÉODŰºÕEb¨¤å½:¤¼œ! »JÏ Ȥþ$@\4Q¥±’4ÃÂ`'Èå`,ÛAUÈBÇ1- a’-,#û²å\–ðDÔN«‰W b ¤HC!¶‘3œÓÈ1܉N#š@š6¾p6M|B ßÅ0Ñ(&,áÈE5À7?¸d ?èGVò#‹&ì e •ˆr ˜€;=À`;ûxEˆ³_ªTõ›ªÌä%dYoJV3È¢V€UÇÑor|"$åD6-û Ý}_ò"¨’–Ci¸ytCøBƒÔ¦a jC ÛÈê&Š|hʨ5°PL º,¥+ÏÔŒaÃùÕjʰ†,DfrþÍÏ–EÁ4ˆdøŠKí‡#îT…+biœ“®Pä ÅqO\ª0e‘ ŒÀLÊrt¨ìå+!Ì!3‰GÞ5V ¢ J-„8’ T$!RÈ+TCŸ+ŒäÄ£%\¡‰/CËÁ\ü „'§¾Tʇ~0ꊈBÒÄ2œh/U€÷âœFíÆÃu,°YšO]bé#áW² ­dkšàzÖ©>Žrnmß*Ü¥܈Å"c!ŸUÀÂ#ÜÈ„Þ` M¬iex7¸A W@(Y(7RA éà Ôð†5Iä@‹HÜTÝòñ­2öÿC²P6 fòLdgb T!A8‡ ! ŧXÉÒW‚Àžtr øÀRjÒ¤>ƒ-KŠÝK¦0¢ äö ÂÈAG¬nCÛP“Ó$ŽzˆC.šH)`´‘3,$/X ÍŒð#ˆÁBi@*9¢TT²B­MÆ6^ ø`õÍ@Å*X‘†Wd5ë´ …Ò^¡ °[ËÙƒ*ÌD ZHuZI!´ ‹°¡ k`ƒBbßZÀB!ì ˆ=ü vŠXöÐ Cèy‡ªꀊ1­d'ylÇ€Áyä¤(M½uÁ¥!íiX-¢Î ^¸âv9ƒþRŸŠQ|#§xE2ZëU„ð«°À|ÿ -|+/…Ð=*fóyac¶>8H¸qô‚R?ЃċTp¢)¶ñ}q´Æ.šHÅ+ díò'ƒ?Œy8œ† šChéG8Ð!x_ÿùgoè?H†ÿÆó†üëCHèq?øSùÛú«9t ¾üÛ?qè?ù¿p`áo‡×k†ç¡…fpyà†3ÀÛ+Œl-Zø†mh† (¤A?ØR°×ò¤AjHƒVx˜ë†zà˜c¬šRX^xônø†Tx|C¸Âò#F*„.çÿ£o@ëmƒ9ni†màpÀét+n ^ˆCl†=A? ü"WH‡uHšU趤…3LB6´6tÃn€C9ô:´C9ÌC3…Â`CÔ6ky…4 „rèt𬫅¤Ù,Å5ÑÁBh³=¨…mpšm‡n(Ãfp€9˜[Äò†\¤KÈ¥ÙW`ùkbŒ@Ki³û¥é†*„Üã…mð$ìÅ%¤…V …Ààn`›n‡^dÃo °«C!q@Zˆ3YGUàÂH†i¬ÆÜlÔFnôF¶ Ç4$GZxnxz ‡z0ȃD‡mð‚2ÿЄü£F9gp†—n¨…‰dÃw‚¤îƒHY´9j@šx¡†Äl‡šûÿ O@7kñÆp\…5Gqk…V`oQÀn@‡y™FKÃé ‡ŸTÁJ£LITˆ…1ü¾pè…Xà@qð…$VøÉëcC¡$ʲ ‡£D¯At¿u8‡pDqp‡ ¤2À1`¹ÁfÈ…"ôhèx¡nÀKxI¿¨Ñ?X¶ºìnÈ…4p…fÁEê ‡$LÂnp§a…\h†vpÈpHÌtšpX6Th3ÙCgôo(j0ɬâù†ox¹tìÅ5 ÇŒÍçÿ¡©„Hm©´‡¨óÔüÚÙÖtÍ­ìGÙŒÄÒTÛäÀû“ Ô¿m0ƒò ¶ø€wÈEyXD:„dXC*„Uà7x˜«‡ œ—º„¹m‰Da„ÂçGu”:j!pl…­Ç®³ UH=û›M߉¾ç“¿5qÍzùû—ã?òSP„ \zÈ¿i0ÈtÀ¿i?€¹oÀÅoÐ磹•¿—{Ð $Ñpì ¥¹Ú@j8…(‚3°„ág`6ƒŒ…T •4ƒVTVju©ÂVmåVl=‘/¼DM/È®3(„ @%Oâ–¼‡'¥í"ÿ:XyèKùËVh‚¥AZC˜{†ŠíG—û>pø>ÙÜØ­ÔLñ{Jó‹^|1èÁs¨9;ϸ‹1“T ×Ó„™ËmPRÈW6”‹v8×t»+XÙ¨iÙ3ñ˜=”AÃY‰Bœ-ÂøìSt È…ŠxjX‡nˆ—ÛQ¬:Ïfàƒ˜ÅÇåXàÀ½ “¸ÁmÉE^»®{H§™Áq†\܆U(™Ü–T¨É›4„œ,¶A‡)â¾¼=h "dG<ÔŽ;15„RÐáUÈ…”tNèD‡ïTZ(… @„n…®á®+öa=åá)^Ó\¾A}É(dÿ*«MƒmÑ`„m…º?n€̼?Ó̪6#O„Ò2,àƒtõŒšìÆãmÕ9V(g£>Ž΄ÅÏ\ÆUͯ,Í*H$Ùc$K¡‹ˆ¡"o¡ /]¤8©äXˆ'®=Ë´\KI‡š£Xø€F¾dH.Iö JŽ˜dc$4iåx’½nQˆ+(ñ!ºSŠ4p†f ¢³M…¬¡Ïz°nˆ»x‘W+ÙƒfH†Y•º¦dfð>]5‘dÈfqè–Tàæxx—sDX…Y‹ôó†^Ã1¸]3D^ÀXo@ÊãUÐ}L˜goÈ‚µçm¨ÓZX˜¥  ƒ7›€!(ƒL,À^,ÂæüZD‡ŽÏ–]Þ-Âp؃"ÔÅŠEÃ+ GŽ C ÔÀŽu9ppM·Ùƒ\€Eè1†gàm¸¼fXÎohÁ—Sм|Ît~ G‡ôÙHÂÄ$ÌÜE„‡Z(Dik0êâLG¥æS5ù¡§~¹o Cn •¼ €º!ˆ0’ÒßK¿'ͰS“d "3‰(D†¬sU R/XðÏù€ ‹ ЃÈ|¹Õ ‡,5Ènø7ÕÀ9 ùè“2X‚šYQ™‚³)Ÿ{ѧyV ÁND…èÿ5,˜:¹ …(À¶i¤…: ò†tå¶yá¶CÛ†¨sÃN†tP™“@ZxºÖV¿ÔCÛ,B=gjH†ôû€*À‚4ÈÛoy…R8ÁUЂçn-Æc@(eƒ€ïø†ïxoù.©;C´XŒ}š"`I+‚” À ‚%p¹ùƒ"˜ÏG18ƒ" i^È…\H†BƒÓ"v:Ái†+ˆfG Çn ߆?HRøp=,Œf@ÈU€q˜FÙLs×ÿ@ÝFþk»f¡(„Ø5¤ä Tšu€Å]`ô†-^‚"hù~oún€ÿ¨oúîÐ0;¥á¿|KI +ð*ß” xŠ!èø ªxðH CH(¤ùƒ»¹ ‰9:L¥¡CTH“ä…:E,ÒƒÜ)„<PpØñù-EfèWðôLšo(2×cL¼u¹6ÜüÓ†uˆXæmjL†š©òú€+o€]çò\ßu•p l¨á3  À‚4(íèJpI™p 1`‚òÀó6ÿ/Ø”øƒýÃÅm@‡H„àžÁÇÄ–WåYh@á†f‡v÷Rˆ¹XÈ‘ì†WgN`3V(RPÀf@õKinÿñ€–C+Ýœ$G´°‚ +¥ðá7ò¬ÃM,©ð"ÎŒ°¼’›X& Å¢öðƒˆôý<¼(‡\)©¨ÒþW!…ˆÃÍ;Ü@Û;´\AßdL‘C8Ô„ƒÎ6ÔpdÒ7ÊqsÓ6Øx+?ØðÃE!HqÅíÐ4Àô·{&¡ãÇdÔÙÃì0D£;ptA©bhrÆAxˆƒ¥ö*ÉœN3Ì ³j2¬²Š_iУ8ÍøA JâÐò 7çœÓL,Ý…Š´Ža–S€fV2Ј£dDâ¤B P×N‡-îôÊ|[AÐÛ$³ ›°…N:õPgRI¬ Ödè@êWäkƒSˆ1$68]ñŠiâh"?øÄ|j –Ãb1‘‘þÑÙ|È2!FŽT„@Eœ¡A3ë`“LÊa®Â†еâGy%5SH,ÊudL8!±Œ 5«¼Q) í)ØAä Á+ÞtÓÌiÝ€s-P@Ž7oz%3#§²9l£äMÞ€Ã+3¯ÜFË*¯PÃË+}ASw_j C§eàÔ;TE4€¡^QÞUI‘ BŸí€XÅ逨;ZœY;rY{+xYEžÌiÛÐÛ*iÄlu3ÍùŒÚ´ÙŒh¥Ó‚M8©ûòã܇…&cìXâË P¬´H uO#ãÈlÿ )¹€EÍNàÜt/­€•K2š€¶Ã}tþîÁb,[DØuEPpA4CK,cäDa•-Fø÷™Èuç00¤Áà`HubKB( p¤C6Ü@G:`³Ÿ´ÂèJO<`A‹o¤´¸ÑŒN—ŽX”á ~À‚„†WèÀ‰7Â!D ‚"¨˜,¸a"q8£ bH®Ð©#vj ¯À]G²s¤gJ¦3„%–°–ôù@  @b˜˜ hbüŒ ‡¹Î?A¸‚BFø°O_ù™Àb h€H²Yô“Ê;Ð@BW„Ð âh-ÄQWhÂ]YE!¸Á uì¤u›„‡7þhÑ n¸«Û¨Ç*:uŸù )?FSÖvB o4(]‰E±Šr6}1a ¾LÈ ;UˆW„C–¥;‡˜4¢°4\a Å1bgr p>C˜&ú ¢¾"è¡àO`(äÇ)C0HªPL?8'S>Ðv„R{tŠS|Àƒ @ A @&Å1µmxC¯P *¤#%r´çlÞ`èN’–’ B9XÈ F‡ÔÀ@â@¬2ÄyÑUø!E¯1ŧ @C¨Óg~ðdðB“¼˹HòŠ20a g@È}¢‰P ¦j 2…+¤a-EØèCªò™Åóùþ ŸX ù[g²º·=Y.R}. ËÒ°;AixlJú0þ‰! e¬zÅF Ü`óÓÀ¾vÑA ( g[f"ÿ™ð¯´XÇ6Pá tÀ#Ô(:v"Ž™dËAP•˜7%FN-sf†“'äÁixƒ&,a ?Wo0®{½I8é ¨:r]lK°#9ÊA6<à‹Èª I‰Sº€€ÇŸ P`ÊÖ TSz@ L€Èri@¦›ë¬ËWh¦úÊ ˜%ôv Åyé*J‹w„£®Û€ÅNàUÌ‹}C®ò®CÌ &x`4/m1 P`&b ÉpK ê3ƒî3 X€C*P—oÂ…?t!ã\êÎp®×âì5°ÍÀººŠ@àä#þ ŠÉL¶µuN%?`€ö¡ê<óÊz…Åäö+v"mˆ!«(°!0·¹-3\ÚÞ¤ù8Ü`NH*Ê@9fd.,‚É_{®6]êœä:ÿšàCvxÁ{]ÝPÜÉ?ó(~ì°i|ºËç\ \w`áœ@¦ÈÊql>[}E=ÂTt´+LFiâ‚P_7€ƒPwpzwTõÈïøA‹…(…Æ`Ü2”0Fø´ Î Ì%×x‚™<QJ—\daüÙX*¬ DÆ,Ä‚Ú4C,YíÕƒՂ­ˆõŒˆañ>㳯R: ÇøÉƒÖòJ:àŸõ›ìðý5tÅ‘jÖÝ#Èlj&`ö}œèOc¨âÊ-Õ¤Ü)ŒÂ¨áß3qâ^œAà7á]ýÀæLÖx9ïB)Ì‚ÌÔÔýl/lÃ7(()š±ÅÛuR+š70u{”Òr8C3̃$i°r¤ø— Áà¬tÅŠuÑ*±I—Ú#ÅÕjöÛ²á€ÌQÆ Ž^6@êíà0H3!ă$>\¶låÿÕY$ÒX *¨M*”.W°)h‚j¥+ *ÄB2LCìDÄmLõ<Àÿr¶gßhÓ‚h;ºa1µöÕNC8œ*Ø1¸W5î Øñ ˜ÁÅa¦ªc gÊY]€HúüK¨0\$ÖËý‰ƒ ñÑ$Êãd¶ÙMá-æ*X‚ò ‚*ŠHtdËÖb°8ôB;8• 0Ó{»7|¿ƒ|7ñ.Š›&…˜<€ˆƒ*ÆA‰•°x††<€oŸ) Êv¬’ÝXÚÀL€g|FÉø6qJ×[„j`#i Ös#õ"6øî±>ý¾±B+‚xÿsEtÌÂ,¼¿²Kg±,K»t)ËrÇ\{“Ÿ¢õI G/¬•AíCï`m®)*8ùT@H¬‰aj~!p³],ØM[äÀg,‹gå§Á,hŽ/.Q¬Á|X@3\ʦpFÓÅ¡žPÂÑÆtô… oC:œF:„˜8œÃN G1…X¡³ ¢+ºW‡qC2Ȉ¢qÃ+¬C¥:¼aX£{A)|Œ&¤Åãe€\œš3X‡åÅY\‡‡ˆ[^Øìä‘}&`°yê©—ÍþVÐ91^ ËNæd^î|®!èÈ+`’9CX`R*tCo­âÑ2z :uÿ\{»€í\m»ˆ‰ƒ<¸ŽÕ–;z ‡±¬+è Hö6`S5Á›% ^ ñu¶csÞc—©&0A$Ò`FSäÇX€€n€š,<;îÜ!7N.²'¨t…@ð¬Bë˜nGÜ´ÅÆ'-ø‚öÐÃÌyÃ<ÈC,åŠÈ“<8˜|: ¼ÊT®HÍI=T1U1uBéÙB@HJ]ÁÌ0rÆW𙡄¡˜ÙE;2i­gSZÔ ¢~€t¡åØÿø›z¢/™­ïÚhÛÓ­bÉÙ*$±±ˆIÍ™ˆÛ[Ãfwög›ŽjÃÃh—6Ý£¶Ý¯¶$©­7¤<ÔC’·ò<þ(‡%THdÁðS%ØØ‘³MÀÀ1h²)½²u]‘a]¸îá!…TÁƒLA’Á,“ZЊÁ\ÁªµûÁNébÎþt=6G OšØÕ7ˆ‰™àHA“-’Ã÷{#‡<з$MÉðS‰<È=ÈC7ØCøáH8@¿ëPü¸·”iS§O¡F•Ê”]¢FŠYMÄ®Q׬_å„­VβgEtP»¶CZ¶j @€\qíÚ}P$ˆF¾EŠ”3¦h 9,˜Ô@Áâ9 d°8 Á” x0sæ .ç•‹Ùòè©§Q§^ÊÕk£¬®!uõšèNífËÊ¡s‡²iݪýС†k $Ç+7nò»q5ÉÁ÷ã•+9ùj iñA†Š2æá÷Ê•h6i23{Ëï3Ç/Yu}ûT]s­*ÛuW®‰Âª @Þp«­69ÔAt«Á´š ƒçðR®€èrŠ#ê® ¬þ¦+2‚€<ïr à±"* ‰¤øÎÓ¬3˜ ³ÑÞ»Œ²Ñ¸OÇÔXÛ¯‘<´ºc+H²°¬;j¸MŽm[rÁµh«ƒÚr+¹ »Ë®äØiÂÓ¢ˆÁƪˆ1,²$‹@Ú§ Û€Ì& ©¤;£l‚Ñ*ËlNÊ@»¬ •ŠEZê¿F¸RäŽ<ŽÌM³4K„µ×&”µ÷ûïªD€<0,…)ÿ ëIãÔb°¾?-À[*ï |.»‚8•£+°b 1‚àÀUT3‚3kÇb­(‡['8½Îä*IlÿŒ9¾ÔU-«¶ †M+Ú„. @:ê¨Ýn'dPSà"®pB‹ŽË¦"2ç±1Ù€€@ #z|``mƒ 4`€¦l`ãU8b„èñ„7pl€dÐÆØ_Ühàí†Ùþhy¬ÀÑ€È !4°õ– .@ q‚Lj|Þ’ í#c²†,P¡\´œ0@À*o‚· l!‚;T[÷bséoÞŒèì-âŠ="¾p!+ÆL+r¡b… ß8@´ÁL¦ÈÌÂa¼qC#A,Øeì„ÚS8^72|ÄI"A\Ѭ“)!…kX!bÃw©4ðõ°a¶ˆ¤J10‚S8E€x@ôÀƒÆ]pƒ¾|à‹,H L hPˆ$ S¾0DHƒ$,J²À,ÀAÀ`É¿ÐÖ#»ð@Â)ŒX‚ PÁ‹OþÀlÚñÌ €€jÓ™¦ “¨™ó ”üa£ -í-ƒr§ØcÁÊìÓ€)ØñP|ú݈)#ÐÓK!ÈÐI1É ¥\=ÂC“ø-I£hª}“œR 0À¦ x‘Doò€o'í‰Ã&„Ek?7üàÂ'…ÕÃÊ…N°=´´j’ä*Ét¥ìRÏ 0TâÂ?.Àòƒ)/0¨BÀùó,PŽ• 5˜ì09ílÀ i/0™h0ðlÆ$±$€€hí‹ ð .øT  rþß°BÁ…¶ÀB(e_? Œ]‡pÒ°räã3ˆÏ å9_ÚËŒRþ ý_?¸ØóBþÈ`Ãn/ôRXá­ïúA¨?o½ðp¾±6ÜobD„ØŠ]PóÔ„Œh3!„O*@¸|Ã=úX€h!— $d„Œœ’ Pc¸EÀ8v˜•ÌQ”à‹‡@€9F/vÐ.!7(Ñ>$‚ÁW\À+ð$"5Y¼Is33ÂIôòt¢A°€Å^Q5Bõ`yQV  "òà3aÖ,Åjùãu•KMº&óÌk#8 À¾¤j±þQÞ4àN#*†üØB R|D!à Cr88Å+¡ ­ÁEHTþ;¬È“ Q‹ ^·ò¼gBØ„ ‚ðDI9ÓÀ¿ê…Ž»hÀ•±ÆlÖ1¬m¦Ý`cQ¸±Ÿ$Û ¡Ö„c"+Pñ&kã7x•ŽY‰Ô •h€}¶ƒMìf7¸€{PMtz¨W¼¹ó^&™ð  ¨{b¬ lIþJG7’C€9n)‰¢Ë la$F(0a CXÜ0P‚eD)ä–”9b ÀBaÁéôen  U ’J0b}fÓ}ˆgÐèg2¨@`$ 4‰p%´†fD7u‰IM aP3³ÔL4øÐ?LPÿŒS„` ˜2È?¾ɰ¢/°G‚P‘‹,„hƒÛ€ª¥?ªŽd‚kýEµDÒ D’YòÄ| ëA ˜ ÄY6¾UÀ§ƒ+\;\qPsÕGdµ‘nœ¯xK\Ô–̞‹j éç와¸E¯ H+KÁE}sY›H)”’Ëeh3Pi0Xõ}Õ 7{ÑÇ®d8Ôüë§Š‰€cŸ$`ÙøWj€”Yi áÍúºâ!(±86Á#ÐWŒõA†+úv¥3Ì@šiÀêxaRSÝIO~òåª]hGéÀ’Âv¸hj&¸É(BÀSd¨ÿ½MIÿ2hûW S"€MA€X Høg€Ò CSÐ ˆs€X ð[Õ6’a8 F~B¡/²÷Ÿ 8¤±pÀE [t!ÿŒƒßòHÏRH7pH4Œ0W„âg*@ =°GÁÞH"ruA T‡_A@´ˆ°‡d0â ö‡µ²!pD²tDmæ ?s#€¢óÁ E20s³à7 @, d%‡°Úv3Žs3C„؆¨†ø/ò\/rˆ0ó\Ѝq9* ÐE~ÇH ʰ6Ȇ#-37“"ˆà#§³  ÓÁ)|ã)ÿGò$žFClV*ÑGCáC#€à†#’d'Ïp%WRõø"ø0274R Œó\1‚u(0ûØù\Î…Œ“OŒsXƒõX’/‚øø"õˆOX#+:Ìh2N"P 6Y¢‚ÛCiA$RàÒ’/ÐRÔ>¾Ó 4É7Imr «²>Ê&¢B°-|·; ÿq×.à],C°ß’Ø4-5óˆ˜3í@Cý¢Ž¤Ñ‡IR!%G‘h0ÏòwB²ÄpPv®$ó‘i+ Küažçyý˜V~)K&ðÿ@8€—{é{É/ƒÓ$0B˜“ ˜²ô/ä R“F]y3yS~´D Øp8‰×Ña>5!&bðQW…šWä›°r2W':®ìV2E@Si³6•à>ÂÎt#‹¢B-0`‰³=|Á)l7G0ÀˆIÒótÇv V‡‰ FpklÆ1!.!0 Ðl˜€í PÂ,å ~ säžCÐCpŸ×!í¹žڞóé] Ф¡fƒ/! EbFpUâ‚=å’¥a  0ípB)Äðtk¿ÀjÕ4H*ê*À+ÿ³") @@@€M°Ù²"Äö;?`’ ­† D# $ùà“N—_„Ð>MܰA°ôAàF9ƒHJÊOP }h*k¸ž˜ò/+°iB³jʦ|ó%5Y8&4éÒ­eàr§Ù/xd 7a<þ1¤Y’mFšöfa…}RE(–F—#RÒUOrHB£Yws)öƒ?æC^²à?qÒ|:Ö?ø# Öð 1¯“@Ø‘0ÙºàçS 5YŽÚ2s¡«š t…J‰Fc‡¦evæðms¶ŽÝ™$øl–(N#0"¦]MB ýñ ­Q­ÿM2~Sn¬¥­áXÞÐ$Ð$ßÐQßЂ*øA¡ìîªïb ú"„R6 vø¤O#ÀO—67ÂôÖ/œ"Bsh¿ ÿ | ‹I´F«¸— !0A¸Wê …ÒU1¤ c ò 4dWT)-U ̶6'ñ2ÜWÔ!Ó!°S=…\Ae¤b–Éš¬ë·´Š—€S 0Ò…ìà 9A sÙW´‚—0r+ª÷§b±`E£#N#K©'® /¹àµëÐh/잇4fÑÖ['q]hÔ™•1xG{'9ɵ0h\£H5ùl‹Hÿ××:"ƒ"¸áä_ûP.0âb½À Ê 5É"/@øZ[<9q¹UOgÔ[j¡À¥ò·`”sP¥âv=Û³Ù™hN¢ÔÅ7Õ$ÆsB"€00í’»š§Q“ P3Ùð¾KqÔEP‚້Ò${ñf7üvP襑âd!8µ(…àI_e¯c8H †%úf°îR/Ay9E'2 w¨eo’9¦U:¬‰7%h¢0€4¥ÐÒrÃ"±õK_Åâ*@fB&)ùĨÁ}3PpLæd/²i/€žÆv­Ë¯Ûpw†—ÿ„e/,ÐÈ`BÃ6ü¥ÔX­ 8 Ä2 ° LÂá#<{·.Ì)PÒÄO¬‚°™¬„0J Ì/¸Um½*vÀ7¡PÙ« Û#!p"p>ÄB„ ‹@48`rÌ¥” ÆÑH„)qP0ðkÕ AðÆmñ`00”°Þ `ðm¨r› [¯#5“mÛ¦r00Ä„¨²)Ü–{* ‡u8­ÔÐÎûz#Cb’cì”ã 0A¼L5¯üÉH,8·€#C»L˪‘7¼$€Õ£Ö±@d6VcØÇAÿ50Ó´áfcñ ±£ B@=À_šr&'‘ÎFÀR+f1%4ÉÐ>k†Vò”ºà á çÌ3Q!';;ôunaÎç  _‡bç´7©N%GJÂÉJ aê3ex%Ò86©î¦N¢­²äÂíçÊ¥ÞjßpÄr¯I/ ӨµÀ4<„!K:ýK²áªCAp¡º½F'%vè7P6#@]$b.bÂX³™*3†I±˜ö!p©Tq ÷füÁ ã `Yâyh-NbÖòBÖj=l-KÌP/ŠŒmy×ðm•c”¹³ °°¤ÿ¦@(ØF#ò /'×S:†" fNõÍ€'†ÒÑØ¥ñ†ŒØ!ùð2汪3C g“r1Q˜ ù°QžMØÌ"€Ø}G°ù†aæ ‘-“(òâ óÐiÚ¤¹­j™*©ÒBP/­ º‚`­‘­ñÀ­ýñ­õñÀF× ÝÛŠÝÞ:qÕ}Ýò %ï*ÛÞ‰B¯gÝñÚúAˉØHAì0Á57ßønjLLíÖ‚/àyc%Ð×^ÒÊæ’Ê--¡Êð#Ø¡Uâ‚7‚Np!çƒRßþ`J£P%(a÷ÆópÕâ-þM,4"à×)Õ$\¤ñÿrÑ׎JÒ=á´ESMËO‹/Õ µ8¾´û‚´ÐãQ‹µeK4b‹d› W5Úà]E@5q*·dJ¿pGš JO-ü å·z—‚óiÙðE-<–F!îF1È8„wÂÔu×Høc=  PHaÂ2ÿÐ .y~ic.xKz[ۥ柼`¨Òæo. ¥ÓQ# ÊhH*H!\ΕÜëÖ…dÒQXL@•5x /«~Œ®NëLë†6ë9¡ÖpŒ›ã½ÎƳdU ùâuCä<©pô±6I-@„rXÃ=ªÁ·ÿ' 0n‡‰ò P’oX½”êqÁ’pØ5B€cÝwœÕRXdÌù àìõ¤„lr(gÖ^ %éz§íÓi4Ôßàη¾ýP ±Ûž£lÂb}$nhLj8oFlà @ÂDÑ ?¼Ã_9¿¸>|ĽÆS ,UÜñÂói2@ŸFE12¡¡„`i]vVŠ¥sX¢H¼ƒ¹bCËŠ*ƒ”¼±BF¼p!¢‘7“n¼¸aÑÔÇ` p5t1XCV+U̧DÒÔDXô+ /L¼°ÄTM€ ðxÄãx™¢Vû´`D QUVq˜e„ÄV ‹ë^Ú /H¿@tÌÿƒ*Òrá%@0àn°´6T`$ €£­ Xøø@€_1E€H“Å¥EˆXÐÀ¢å)1=nrõ^׳ÿ¢´Üg±ÍWX…C…7#AxDºb… Œ7¯èqØW=fR¸@ü3@(E ‚Ì ç•†ˆ)0 ` ¼2‚IºÔv àðB:Ä`CT0Ü@€!ôpŒ aÃÊf¸áVd üø¿  A˜ „éQ\/,àÑG ¬©A l0 h #|P\>hÉ%ÿnÂ)'Ý™'{C‰L XYØiK8ôP €Õ§ !ðÐ7„°OU3ˆ#AÜð’,`ÈB0Ü„É t“r…ðQ?‡ò«ÁÄêi¦!rƒ H£;îlb¥BÜ02 `¦=áÈ!äØC!0€*®j <×`‡°\¨Aþ¼âV¯¼¦r 9™€—ifäpÐïšcrTyæ0¡J@«@¸èE$¼pLk>¼‘Ä/¥T±¡sD$)ŒÝàAÄ<A+$`AM½BŒ4óÕ õ‘DB´¤ÀooêÕ /G*Âÿ˜¥„ðâ‹-d´¯,8xú€ýmvƒ·@`½À%àrÍŽx fÞnV¾-5äaÙÚ°rb”\ÊCœ-r.Y Ïcû¡À0ƒç¸ƒ¥ÔXŒÉ¬°—WtÏ¿2 iÜ5V¬þgé›ú„&ì´i&x/N€ Ò½àq!@¯€Eyp IÈŽ~ …»ŠªBpÄ$ °€V°ÎrÊÊ.1‚°Á(8 ŒP˜cÁI°¦4Íí4AB€ dôÂ'˜@ÞNã­«…â+Ž kLX«Q$döôFlƒÑ ,à V©cÉ—oÚõ ¤k]oB„Þ*â›|mˆCD_À‘Œlà7äÂG¦B– à;k:ÍЖAŒ ×C ¸wÄ䨄Å*[©’W""–Öá*M‚V®€}õ¢$‘|ä˜þXHÐi“2ª0ò‘©m’Vȸdæâô¥ä9@À&c•üF]È$tªµµ­k7øÚ#Næ!®@ @Ö’°2z²meh€*à5nOÁÈÛ Ð BJëElž´I£‘Í„ð@¹Œœ`€¤8¸!W§W‘$'ÌYIK^b `*ÅÉIZʘ¾@¦”ˆ“Pˆb”³Ý Ä#€ÊÁ²"(s °@‹¼q-ÐRAPA€ƒ†/6%!ˆ*¥è¸ + —œ $ž «îöO]í06 ”¸FÀ,JyjòÆŽ gÁ)#pÿŒ%Â5‚Ù‹4l’,–$¡ZôÒœ`´N°ˆ! #iÈzv*X¦$nLÿʈÑŒà;—·ô`ME­jY+±¸ÂV¶´­íR«Õ2%³‹ðb°‚Å0'ˆ¦2p’)ްgr Jxq-­`h0xQN牠Cáô¢ZØÒ½ÙÏ>X{ p‹‰F2r úÁåùl1€”À-*¾!qïš©$ ´.9vzé ßk*4Qj@Aÿ@@œ’ÖÌç¤ékêH¿|C€k®ÉØyš¢5§:ªzß.N§ O,6”‹_ì&FQS>"ØÝ þȹ€Ä Bl#Tޏ‚ºH}7X«)&.ªDv[°RÈSî B ¨EYêÙ<¢x<çÊ ƒÅ€ S2‘1a³43› ˆ}“ºYÖrL|û·ŒzÙ,Üô€ogÖ\¨_#%)LJ1†m,›Èöi9ŒÒ­ý˜T–„m@B `£ð !Á}»Er˜c>=•}4qA%nwkëÌ¥>Wèϵó@‚†V Ën|ÁP…~&°£6†ëÈÆ6n€\åê0+{ŒKÍ¢u´Ã.‡8E#‹AVîbÂór¤.&Ê+²ž·»³ï :þ8J$ŽL˰Å2ÚäèMk` @FðÑ \&°&¹!‚üà _x>ˆ‡Cœ\ ¸0~ì ‡ ‹hÄ#<Õ# Fä)ÌòD’xxf5Ý]ðžs ¨$mÁì«„ý—Dy¥BòÆ Rô£á¨HH*tóã‚vÞoØîÊWЂ³N8I¹èê½Õ=Ö2€WåÖ*‚¶ºM/­(?0”0¸ÀY<¹e¬é6‘ŽZðÁ øý€ï{x Ã¥I7d!&–<$ (*@tJc¤BÉ`E+˜ü`bnÐÔÅ»`G`õZC»`TÀþÇWއÿÐY1Z„ TpÌþôg* ¨Ê™t¯jÿÄ0‡ ¢FàAè˜Ô*B á[…€ú×°x–È M-â[¡§Í¥_åÚ)›ê¦ä´¥(VérQ1þ·™ÿ/HùCpþö§Dýðg-Q„[‹ë?’ÏíçS8 ZPÇÀ§5+b!`$§ l3Aéä4Îwe…!f‡Vb`7G? &2&!  0àMJ5DEÄ#B‚ÜÂ@AðÏe4×–EõæUð"/å´sÂdäE­÷\¢@æâ¦P&ZûFxÒ‘¡p}sŒQ…¼bS˜Xˆ`…€…ÿÛñ l.sB1R’0XñsÜ#"˜8#0*¥P0âñna ÞTh·’+gQ/¾ÂX²bFÉÐ<`œ" _áˆP+Ó2/s00ø(G âðõa=#0Ð`3A8jg4„„EKƒQbl# y(5Tce¬ V0qlFÂ}㈓I6Q!2Ó°0»¢ «uÇxÄhŒÂxÉX!©5jÀ”Ç"‘È2Aà20#3Ÿx6à +P :óL,#)±I‹E4ZÄŠ:èŠP¢N5PD5£± ò‘#bs·¿x ¹qn‚&Ñ'gr†±@ / 1‰(4y6Y ùà' €Må-Xœ0$¼ã;çs5Dz_aU•<Èô1…PF=Ö“‘Ùƒ %¹K’[Uh° ç3jjÈ`? @všÀ `Ær5BÐçPÈ€À#E"F8Ú5SØ3[>ñ–0^11^¤ °€H°*¢vÅr5$BL=`L6° ‡Ñ 3&˜ ‹H†ÓD…Á€S‹2§|ÝTˆßJSädN B}ÿêäMTñÎ¥±ª`Ze¥r§ ÐQ5¤!ˆ,† €/@œÆ¹&È™2qÌÉ*ÏðPÝéà žðåiž€žÝ)°žÞéžï©Z"eaÕ¹œËèœñ%P¢´°p°ñ&m‚3Å3) -CUTL—T ã-ÁW¨Ö;²_n3=n>ãu_µ?aeJQVg•Vhçk«˜Û9öùКúIA0ýJϱ ñ0uQ£;7᣽¤ÉYEÚ/ïžáY`žå‰ž`ì©¥ОïéžÿCê¤GšI ¤Ǥ.À± qåˆÀQ/«/` z)-A_ôñ,¨¶qjÊféœà-K.!ð[Bó1¤:‰†VÎY.ÉàÁ´YáC¤ï`¤Á¤éi¦ÕùÍhœ´Dê›`¨õ9…sª«ª,¦yö 3ñª·A¤ðYêUJžXÊž\ª¥êé¥ýÙ¸jª»J¤.A«¿`«FÑÓ%Ä%# )VA3¤HÐ)rÕuU)($Ìò)PÔ 1àB,c $‚dVAtYƒ*ˆpMæ$eAv}¸`e’¤ñ‘Ð7hu" ¥ñVÂñ©ÿ Pž6Ф¼ h¿À¶"¡ôð€d÷ ÐG¤qJk±d‘±˱û±ý´Yê«êIž ¥Yºž\³ÆÚ«±¦±;±(«»#»±Ó e!p5¹†0мÐ+‰žB—Wmmƒfý¡ì)€_±V2@ks–fâBH“™/ûЮ7,\T"Ŧ‹²(œø‡+±¶ û© [@+†bÁPA…¡“ç!¸~ëÌ*¸(aB©ƒI`ˆ¸€» â¹²Þé²Rúž2;³Þ¹7Ž{¸«¸ †Œ[¸¬†^v´j‰ Å+‚à´O‹ÿÆ ß.ÖðCÚ0šÑDœBñQ*Ìn'‚î ¡ç+ˆt­g(#Ú(&a.|cJò€’£¸w[žÚÛ°ž[ðÀQœ 07П¤0¾ã1¾6Áäû ‰‹¾ï«¾àK66!ž^J¬á™¿ÞÙž2;¬ì ó›c¬`¿âû¾å;ÀcB¿pÚ½r´ÓP™LFo±BtLFGçˆà˜vŽá–ÇA‡ð"ø#Åó/±Y'ZÈW¤Çv=÷}u!½ nÆ)J ½Ü ¬Tê²¼(¦CQ zážÕ' ›0à2G4hÁÄ/PÄO Qü™¹,ÿ•K³L¬@xd‘WüY,ÅAÅkÒ' 8@ÄÕ=Ðt{t‰ØÓO»"'@"VAgªF^á8`ƒœ5õqЕµ:Èþ¡Å3 nC¼¶I2…Ö Ì7!}cT’Wò!ùŠWìÃP¥æy·—+¥×£§Q1¥@ ¿ðž¿ [!Þ Ëyæ…ÊDËjìÊпš+Ì]ì¿1Û¥ÿÛ¥ÁˬáËxò£‚ ËʬIp=8@ WSŒßšñ×$â“R»§,ë:@"8@A=ÒÀ"6óÚƒ&( ^Kn¿eì“ÝG!êƒó’(Ê«@#PŒ¢£%ië·ÿ²lʦ\¥ŸÚ°Xꬑ/ð]² ¸´ê¥Ðš°b¼Ä›ê›¿±ÐÑM¤ÍÅÆ|¬ÃÆÆìÑ# †!m1#ÍÑPR;(Á*Ír-“B ßj°cJ^h­™ W\ñ+Ò>#‚Îg¢2ƒ‡, )':Ó<Ã6¤hŠ¨ØŽ,ÚŠÚ!±Ñ:—šU ›°Û;Ö ]ž.+žu&Y|¶%Jz¬b-ð¥#m7ðpª§Šk½ m=Ò'íž`Ü¥ÆJ¬yM¤{MŸà×ïpׂÔ-‡ýi×)Á° ÙJ —˜G ‡cÆF€±§µ;["° "@¨Ê3P.sS©UÙ ÿ™UV™ÚJ^©« @À$c9’‡ãÃbÊb-Öüë«‚ ËŒ§ qMÜÄÚ˽ØÛY©Ê*vÃáIÒ ØÅÀï¹Ý›º„c‡ÜÚMÞ4Þvƒ0)›8§H`‡ÐÒá)‚à)iÃòLƒÕ  š —šPk\;kyé*p§2ã ~)Чd’Ic A¯0^%:™qa Â}žÄž°²7ëŸÝýžÄý©Ð]µ,Ö?;÷é±þ)smÌ«*ŽÒƒÝ !ã%f˜¤<\ 8­BJãƒ"QA@{ôÍ¢´Ñ‘‘—8Z2  ˆŽÿ @š‰ ™M#ÊTÑ•<Ð\£iWbš¨iÛ´š,Ñšãõš|#›-ŒNÈ€p ÀHjÜÜ;¥Ý žUÚ}|Í­ã}þÜ,þ©ïy yÆ„2­qè(­ÅQ|…ÛèÐZè­ª—>v !Á@´»z8C8„Ò1 äøˆ©{;²Î‡1j:Ð{:²fB <ýafæ6¤ðΊÁžŠ~¥æií t½Ä¬ í´søÞYuQéÝIþëýéððíoþÀ€þåÿƒKÿØ!å¹K //0… 07=F7ŠŒŒAB< "+ . 7<<› /7.6A==7FB§•!. •# 0ƒ‚/   ¾#0…"·º¼Î/ÎÈ/ˆÞààßà é èÞÚÙØóÙÚÛùóÞÞ¹øúþ®àºv¹44 ø/àÀr±s§`.u¥T71áBu¦[€£×‹’‚6:)âÅ-?à€áaÁ…"VÑéÔ6B %¢¤¢ 6x!Ã…ˆEFpÜxtê V PkÃ$Û$A+¦Á¨¦Ä ¯  (tq»`Ê} €/Ÿƒqá ^àLÝ ˆg^=m}H6`À^Cè,@W÷ÃáÄ Ê‚$›yó€¹>7m0æ š9[ôŒxµèº¯c«#(Í—48’}’LĆé‚Ìà$È‹+:qzÁC¨‹6\hd$È' !V¸Ñ¨ÿõN‘ ¿!Á¬2Û5B„påJ9p/ƒø €€À#øæÙ !´G³ù`•H 0K„<ØØs_”u³?ÿಢypQ…ui Á ¬Ð0é ˜ŽŠ±h‘‹0ÊHã‰)®háŽ/Æ8£‰6*•€J!ÀS‚ܰ–/ûyõA!HeDÌÝ Â ¸ >ü„ EÜÀÀ–B!Õ&SñPÁ!¤ Iå€Ã™=0Â(°YoÒ´tÌ^0à4 Là °’¾ˆpƒg…õA}…c€`ä€êA„!>v˜Md„¨áyƒŽÿS×H 0Ð@R{½¨Ù´BêF äºk.ø:k­ÃT¬®¼&ûÁ¯Áæ͵ȬõÂZÁõ"Fà¤*1ذ‚”7pUÁ$PA¬PÓWˆ6$ºBI‡÷ÕT0ÀY40B0 ‚À·z0€~‡x¥ëf΀¢(£û}µ@8t3ÎÇfQ±½CÎ7}Èa=ûPö¬±þÃ@YyUÖ° ‚YRYLóÈ7_”s5H¥³‚Ï*]2l:ÝóÌ*-`J!Qí9S!n\M0‚$pÁ -Ýp®¤¨yC’˜MŠwÐðˆ$+œ«)¯("I;þ $À 2$Hö ´ÄL¯ø½¨ÙÈx¶Û8úâ䨸nN:“sZA€å²WfšÅú€ú¼Úd.ÌMÌÿŒ¹¤3¾ž (D˜f óÂì£ÃîºOK°ïÀ×.üí»Ï;ò ÑÃÕ0Q3T 3€kÄ8xI´*àC›Ùà}$7à°&ÛnrÇe¼ç#ƒiž l`ÀÀFЃ½|ÀPÈ Às샌$ ‹Ê…8#&„a†&@6Ñ)¯Ö80Sž‘åw#‰Héb&™|¨ÌCÜðìfx,¯¨03µsFÐÈ’€ („g ¸!lrÿ1‹Î‡5 "lpH˜"º¦‡? áõ²‡ƒ*F¥ÀéA)Àe #&)IÁ¼2ؼË]‹Æ1‚"¹“\q„LHÀ"¦7©ˆÀÌéP„eÁÛZ„^€Hˆ€0B­ç%HJò;qÙª¸=E2R‹Ø°”Ž¢l ‰ËVï¦ a–jA‘ WÆR?´Üœâ&€ƒ#àL@œB¬Ä„(I Kœ2•ÉXå ZiËÈ’WÒ%,­‰ËlîRHp’ ˜q5äǨP*ä4€NQ™,¥á¦AÈ"*Q1Ä,ðyµþ€82é èY’ãá^|NNâcîñ$F@¸p „™DBvÌâîx½8Ò›É?ÊÅh¢8{Ü('MñƒÔñŽyÌ"¥òµ@²;ª8Ey`„0ÇWlxêSàì Ú£Q±˜%ŠÚ±0iªKíXص7¨i)n S•¨? êN{ʃ,šb¤WÄ z0„£®µ­ E¤)7“…&-äd‘×…2c €  äϺþXìAÒdE‹AXk0‰ÅŽžDWã"'‘AƒN/Qèìô¦7¸þ¤µâJqVë30…L!HÖÿ“Ö›Þ$a „¯±x\„+š‹*îñ³Ýc9‰PŲvz®…il{0[S Á¶¸în{ ÔÜ·jSçôcÅ|V±Dä-Ž»YSÈ ­8/û_ö#ØÖJ^€B¸·­"=p¦rG&4Á¿0—Œ™Ž’(ÑH`Dõ  ƒh/*̰0'`nÝ'ÂR’ "¼ÓzÀ¡âL¤Ô= bïŸ=ÆD…°V´`&2ŽŠNéœv€‚!Õm+S”ø³T›¢öXì½Ç¤­ñacŠžRMÊ1‚Áq™¸.¥tT­I Eìôú‹æÜN/þ¸–?ejæó‚¢²QyA/ªµ=É&0f%6 GèRù³ª¸š0y;½“Ê}$D¡ÁµÜ“\­j{Žäž¶öç@Ï„6  êÌB€ZÔ|Š‘›+½;*B¸w,r#/]ÅLgÓÙò´“`jŒÚ”¦æ5ªXÅt¶»IHŽ0#0!ÂC‚]¥aÜ–¡{~<‰¦«˜ß>n[°§Ìï¶œ‚kWÑKÛêÞ‘7‹bìq”É.žŒq\ÝóJ…È£M¾÷xÊB™68ëQñêKrC0p¼àm5x[ˆíÎD¥'íÄJŸÆ¤q“¢2„!œÇÁbEÉŒRsÀTþMø•ª`f8<¾_„›ðÉÖ©É|âכɱ»Q#4›. –aŸˆA˜@Íc)x‹X$Œ`éLäÓ{¥d8òßÞS:½Ñ‚K¦ÁíAuh{Ý.â{¶`ŽD®ææ A&Ch{iˆâ ¨ª$­è>iÌ •%àÓ%KY‡.t¥›f©ˆë𸳿âÄ.Þ#$9þTL¾ïø»R=xÅW6‹–®Ö<Ñ«y´Uê"([cÚáE0i(Œ:“p \Z+ ðˆ{c.ˆq¹Ç­|b–ªxÜVI (“chñä"?;T[D²;i…ìp²É,b*ÏqÿtD-Ú]$HMÕ{:³|Á%Qã>ÑÕâÖÛo\U,ýŠŒü¡{‚ã.¨ý×çwV•s­E…Swdó7QUÄ€ÕsT,u\èuG¦ÐlÁè`™5n’Y{ô§°+ Eçå„0)cñ9‡XU¨~ì5~î§€(„6=@£‡E;Øx"à&w5$ \Çoì…G×#ÌA±u&5‘ æÅÔ¾•\Àa…$=ð{]”O¿Öñ%p$‡LX N¨E2…SX…W˜…tØ…vTQzæb­&=¨€Hg  TWL`oˆUˆò‡…Ó}¥ˆÿw'XÓ„L†ØL f*Qz$STEs_'R!uE¥X… #GÐ¥¤hŠ78‚àL]ÄÓ#ª` ¢dëWhH×E:pV¡èba(Q½˜V3‘ 4×jàb|]hTµ˜bà•oFÈE‹°‹ºµG¾ÀhÂhŒ\tvA† UhiMð5j†MàH‘¤·…6êXHíx%PB3A n†ŽGE (W`+7-wLç_.„æfE!=`\”‚W’= ÅzX´n—6EïÆ=M&oOöu46=S–cL7Q^g†‚¤"¹ƒôfA–d)v‘-OóFe Å[R†c3IRÿÒsE‡äVlUPË &bi%!cÜqòÅSPei¦€”ø¥_҉ƕ_‰ @JE‘`ÏQzÝr8œ´`¼± r<…p'ÁV P–B·GCGGBXÄ(=³(¾ðTcQÔ³5œVLºÆ í³'@j„°k9˜PÆ–Æ…ÙµE>×s‘T@/€—¨i[¶i~æ—˜¨o‚I˜Áv˜z4^â……õe=¸@!P(-QvŸ%u[´-n"+¦i²nkárÌàš¨÷v+ùuX‚{rHcXÉÐGÍ6†dIW„wt}{tùUU”=åY”ö8Ø’ Ó Å´•ËE”ÿœt á §TUÐe%Yò5ºÅžÎ´ÙùhÜ)Ü¢äÉm´g“åéL ’ Y¶eNâ"4’šP†P@wtZh¦\ ÖjÕ³kå^ÉÐâvW º{­I½¤5}ÔgA€Éài=5=kE-@}%ªV0†“=€#…ZÄqÓÀ#&†=ÐV/‘ø$Q²©Y%Î%Û Ç9Úf¤·é$Ŷs[¢l† ZìN¡Å£M⣳7Mƒ@%Cº-R¢¤H:`ªAMÊsÉN PÄ€QA¦Î0o¸GŽp=MGrkñQ‚DrfT¹w‚‹@{×’_‰‘ñ6oàE†Ï™žÖØ\ÿ—RJZuç §¥Y".„‘4Â%WR) íó¦Ñù”Bª‡c9Š 4! Ì mˆG`=Òv©„òÅ4œª_éáa¡ê £*©¥šHè)’Qáˆá¦°ñ ˆÀ_ƒT¬%L! E0V€ŽtÄ¡EdæhÅá %ñ¬ÓÓ$bGÔžb÷Tµev@5*0w°ˆ•_ì*èŽ6Œö¦ÈN&b&®O‰¼ÐŠ-᥅ ÁG gf°S‚°%šÞCš€ÍF[Ñmþê «ûo K Ë«뱿²þe†G.jjáXûu£R˜$T†aÿp]5;ŽF †àh2ËYõ ›tnè|Î}‘ÀHôk[tGU‰V‹–hŽq#i.xT=UE  -Ùò”1¶&Рݺ§ÚImz†¶ÚÒ|\¤“iõVëùj@U–_ûoÚY)¿ñoe«¶AÀ¶f»¶§•¶n›}Öh*á±çÑDOµGE±”ÒSA`»Øg‰P¹—»ˆ€¶dc2!¹f ƒ8cï·VÈ´õ'b¦Gºúf\£ÕoOépg3—&uWÚB£†ð[Ù»b:‘3nÅ{Xµ“•ŒÆås8ÀD™½kL+aÁ«‡b921%#˽ÿÉë»Õ³“@E¬oª¸W6  s§—²YqçIZãs·HFà}ˆµÊ›[„‘’)L›¨Y®Uz&ȉ±©“%\(p'wtG õqwy§v|ÇÆeu×#Ð$ jQ^æ»Mr}ÝG=!܉¤‡sGs‹p <‡GæeÁU„ÁÀ¡ÁìP‘ôÁÆTÂÖÃ5$Œ5 õ…çõzqLïršˆà² QÓ¡;|E*å¬N|G;+ƒ5¡[‡Z 0 ³°h¨äe"ÐþXz$R'µ xRz‘³}8Âß'€U=H[~¢ŸöÚŠ{K*u—‘”·ûáÇÄ!cH[D`lµÿΦ{u¬Ù‚ÇW¢Ç€<„'q%<%lɃ|h.æÑ4{ 1 ºH€å_§lLÇé=L÷ƒ|ØZ#¾É§'&H `•XéL䑚k1$* c™`ã”*± ñÕe¥š¸ÆÑÊ=„Å”-P„Šk’Yr¬A³+W ÓÒA"€'¾Ðe¼|ã<Êæ\¦PQ'‘Hq3«Ø,Û¼Ý !Î9cé\ΦT ÀDÎÓT¢qWÄz”@_ñàÁÐêðÎ4­d_=aeun H»w@¬8²ØjžÒÓ¦½™[Þ¶5ÍqœgHœT†pÝU=,mœ¡ à" <èÀ )‹ îà(h”,‡1äà,Ò`!/=…ŒD8Pãë AÉ@f2a³Ü .~¢ØçHàâ \^É21PŇæZm•`‰S˜’Æ…à@³°|'µ. d‘3ýäПá⛮ŚYã³°n'f‘ƒzÝ0€*1ä¦Ñê#±¬AC>ä8›1+m1ÄYžUþõíÓ_3¡VWE³‡(oÿ—c½ ë*%(¯L/°ò žþ$µÆHÀµ'Ÿ$ï…0Mµš_Ytm‘öZÎÑH} D]xF•`˵¨ez ÎÕÈ`"í1ã¡ ¼ýé^•¢¤ÅMrÎÿ½ÁòÊs,&òKh„¬D  €àò‚2+Ã>®ñBshŸ³æýgi¨Tj¥Lp^ÀIÍ LXYíóYŒßö‘†5à Y©Aiå‚„ptGIORXØQ® §¯X¸@f´»Ù ^=¶å&˜dT:&O»u±k"¸@ö ¬>î!ãÌÏü„2Ÿÿç™½Ž œ‘¦þÀr%J>&e¡;¿â‡0+,šÛ¿èÒ‰=·)TÙÃòi»Á—h\Rˆ ùEÿ³¼IÏkiï Û €°ñ2‚Sƒ£‘ ÑƒÓ3óƒØèØs³óòò¡òÑ8YxóYêXØÓ£±Ñð"#ƒóó2Óó"ƒ´1”k4rócôŠc„#ÔDjÜ,´º 2°0@€°À0°pÍ=ð "#"Ò‘)ªù²±)¯¡±°ÁÀò]¿@pÏÞ½06,øPÏŸÁh .X5PÃU0@0€a¤Æ 9Ó3Žj$"Ȇ!FNâH¹c*A2j"$¤H’ZÞÂþ"ÈC|Ì‚$£YÈÆY/tÂhÀ ‘¨¨8n "ká²ÇdÑÃHF$FtÌÌȲÇ!\Q…E†@Û­A¶r ¬‰[PA[¸kà*”cð"D&u>¤sõÇuBDc°!ð=Ëõ0|Èà^Åh741ðႈ ìU¶W!¤¨fM9–Ú´I])MÕĤVHš]_ÜàXQdÜ®Dìö£5÷õ +Ö:Ù§#Üé :êÔÇÖÅ×¹*ĽÉ7p̸=»™û£]9Þ\ˆ'±”Œ Ä+^üB°×j|) 8Ý|³@-ðHO&‘‰É%H€ Ðjÿ“mÈ¡­¹¶@­×+.T"D!Ä!­&‹$ˆÔhÈ%YXR#Çà ÇüHÓÃÁÄ!)£ã&‹õ4ÉPÙ“Jx’¨Õ :†DK"Ì„•XD¸¦DP €E"!^ÁNH& bÄ Q^ )–4¨‚WŠw±BÀÚ$0M5 ¦ ¢×$pÉb“íˆád‹  A˜ÈÏ€-’bcÝ 0à CɃf%`WhÑH݃\'kHûܰy óC?ÐbDÎõú«1"ÈXHd07HEà*Ù+»!"€jaþ0*ƸЭ1ÁÐ&@MâBÿÆ„‡Cº‘e”nò€c8ŒN#Ü(ËJÞX/ 7¼BËï!‚4ütà&Ú£Í5¡e“@há„Æ×tÝu@†#´#BiPÁ>”\A Ÿ¬jY2b§0¨¬ò®üØåÚ ü<Ä¡<ÔEsÊ)MP‚¾ˆôBñY©ãÁ}N‹‚áÐŽ 2B‘q4YVI/ýCS™:üÓ2öð'ÓÖ…\•c¹ÅtÌ—ÈuÖKUf„Wªá7XF‹:ÍÖØ˜à„¨ã7Á—h Â2¦jšü¼ÐÙT£`hÞ`~ØXáB!ˆ´™Î˜˜Ž'7„ ª‹`O‡ŠdÙ,þn[&¶k¤Å ]Ì‚× ¥Ì€ ¿pÂÓÔ Ô̓m/}‰Có7îY!Ļ鵓æ.oÄ9xé£Ü#“Q2b qL¡È3B ÑÈ áòð~üóãµDÃÏ+Û‰ƒ! ¡ 0€Ÿ:V¼‚Ôh8a‚1¢ŠMñãtôPŠ|¶j&}A„ôF€|8Ð,&ž-d‚øX ¦C‡DàÐ=¦ã{¨Š¡±Gêa­‚¨iIÐÒ2“´ 7 ËR˜Aƒ‚ÜhQ,`K’8„Ÿ”މN,¡îTñà€ÇHÅ1øQ£L€¥]‘’!£6¾± iÿU4àAˆ–Hb_iÆžPA—ê…$¤ Áþö!ÒÜìPÏ4V³€l¤J0 Xjшcàm('CM4ô·?ÏÄP33l ¢;N T©LÀÎîÌŒ±J§¨"¹¸¤F‘}ÀˆIT†×x#DªëÆrxˆV ‡ Ñ_¸;^pˆ@äQm(åus ¤G’á6GR/q5—`íá¶¡Rv aá E2úÓ2¼^þP8p˜ðGªøDiTz‘qñЙ²"¶¨}ìáÿð÷Sư bqeÁ^º¤?@¦{Cz¢q‹ðvA[hTtQc p {©Ä|õ*`ÑIƒ¥ (z1C%ÂX°‚‘g”4zTµ :ðcï¤&@ &²,?6!'€þX=F„h`4Ñ:ÀvŸ¤?2ý‚ ð3/€bJtIYJô £PéI¦ö]&’"°(CX=“þS7òc’?Õ?Ác’unIX }!Y¦b÷—j0@ ûC/ ±q/"­P´h"è7— õ’0žá €ei–³G‡…]sŠ…·<ÐÓ9±Nÿ¤`M‹QLE-cÙ$a k™ ¯€   _WH)nd¸RrIM… ‰F=0„=€Fè“á`tߤæE‘Ú·MÉ€–¦7ôe"–öØÒ  isg n!]Ÿ©Ð“Q'@2‹ FP˜\S¸=s_þuÁv*Ä!´%.Ó ²Ås+3Aà×PB³Bå€ÌÉœç…Ö9i€‘†±)2WndAëñ›©™i͉-)c^“v'“2I†Á 'B…1ŸíI1¬¶ ‘”2)Sž² Ô€ ž"¨H¬`1ÀX KQcÿ6!š„TKQO¦ñ ó) Oùt›ÑvIÕ3÷[“mÒzôU4°Æ …79,*ðgâ ×Y1†‘ÖP†±ÒÖ²(àО i×à)ƒ P•…‹` RPÕÖ( ÿ@/äA%#.’v5Ÿ¹1 ®´nº$þA/ò Ú*P'³pBz¡ÛÐjATiFDó‘P× á¡ …ƒ81 )õƒ@¢71ÜðH§:ç)„Gç >³Ç]hˆt³§Gw›Ká3ooÀc)“2BW*IȪåð(DÚ‘¦ÆÉžØEŠÿžñÖvC—¹)D$ öp¥kŠôB kê ®ºšx¬ü”£—2íy ‘­ ’*ôžóI^„±˜$‰a;>C ƒŠÔ!ð'µ.pbToä:ªƒª™&1)²ž¤qAç!#"扠4B`xL–Rm³P)™ Vb™&ØrXoV1á]qž¡Sà‘Djãu¬‹¬ K¯"+£Õ°Ø‚ oI±:ŸÖ0iAú°3³6ê—³š”²tœÜ  ù)£¿9{õP}NH{â=ËP'@vUˆJëáb˜bÄ™i§¦*Qºj<»™…Âj€zñ«É ÿA@B©WV˜)e–Fp©¹O’šÄI„A/áÐ q›iá°œv«š*×x ã  ² Ûœ;°üt«€Á YkžûI=:{¶ºAš‘ü„·pꩱzÃjØ2¬$ór†Dñg”*ã Ø1 =M=¶Kt"°‘šnë°c‹,l%‚“ÝpŠ©q‹ C…g’´Ei¤Aiž»Q× ¬p·J· R²Q·7ËOßP¸ÈzK/[¶ #;½þ)¸ï‰œ/‹ž©F™µ ±ð¹Ÿ À­µê¾˜‚·šr2)i{áA’††X„¥êá!´¥]'ãÈgKkÀKå7¶ƒÿ©º Ö‘>¨§Sò]‘Ç"8s-¢"Á€Ï°  ¼A ¼§äÊ!¤™(;·ùÀd©’ØÂÂLF1àМCQ£Ü€àÀÂjç5¸û±4^<¹Ö’_ŒÛeZ֨ƹe|!¶çÅOkœ5›?Òö«¸r¡ÖcÔ;U9–0nþ‹¼BN¡I½2A5Dš V‘4½—ãTPåRµ†¼ Põ•Á «Ð¸QféÐ$MA /j“’3ºÜ  çu€;²JÖz€T1[ ß PMÌeßà½=œ¿wKÅ’ÄjèÅÁ€ã 8<;LÆjà° }aWØØÿÑ9ÀÃ8PíÉjÈ1r–&P÷O <¬÷M«¡I AžEwÑ4ÀùdÄi-Û ‚NÔ2ô,â&¦F¹“³ ØŒ§–±kiRm+R ÍiWÓyÔÉeäÉOPäˆÁð¹¢7ËÒœÄ(Ç ÁÖ@·Ô ÐjKžÙz^Öè5|¬„1ý­…yIó ¤Þp뙬9K QªŒ–'n-%f†À`DËò–\q4×;÷&’ªÖ›?ßPG"²;C;ßœJ.¢ܦ ev~´>ÃB¨©½V Á7KÃã`Ÿ·Š¾L¹çëÄz#·tÅçÛ IØIËÿÍy²ƒH2bü0œë£<û‡IÁ¯œ2·ZlíZÙÎÒíø\Ñm~96ä4<%›ó£â±·®ëÏýö­ÃâìãIè—«¸é¦ý„ür.°ýÊ!”)% çà½Ýð³ai2è¼ôøÀOCLpï   þ0 /!0’ˆ‰”—/0 ž‰/ •§“” °  ´· ¾†µ ¿ ‡ÌÊ¿·´ƒ´ÀÆÊµÔ Ї ¿³Ã²»Þƒƒ éËÞÙÞò ¦»Òú  ø:¨ÛFŽŸ+ iK `€Pá†F0" 1䃆^ˆ á)ä‹ &±úàj–3¶luÛP æ ”`àL” %Kœ!$6¸©Ò yt#ÄII8ŠªLÄà:eú–i]¦oX‚^·•›Ì×Ve¾7 ­¼[»ð.Ô-~ÛÎîÿÚ€Á€P>T@J€²ùþ~ø›m׿ à¤ûr]Ë`"ý°DQD+r~@È)QçÏ.ˆàÈ(âEQSŽôÚÐ g+J‰^èº{Ÿµ`úv³Ú ‚aÊD Ø}f ÙO"y’TŠR%OA>ânàª÷n_üÆòº{›²vÿ`ÁÚ¶@Ú½˜ï˜é«Š¶˜ß^úÒíJê«7±Ç|à—=4áÓž`„ ¶RîéÎ-s}5Lya ã`~ë! JEi‚C&(m¢gŒÐÃjFÀpI0@I{âØÕŸ]  [oÁÕ2—z:öƒbôòm1~×"H2 ©|‚$I¢Bÿ‰ÑÞ`ÀÜÈ£=ïüsXÖè²Û-ÛäC e^eµ:í„Ó¦/Ϙ×W6LÃÕWpZ#H1ƒåS™ŸHµÇŒ‘Í’M<]áƒO(½ƒNySÕÀ&µ¶IQ—`Rð`„H¨ØÚs7‰° "ÝôÂS™4P£zW¥ÃN{}¹*N*|€©Žpbœ"Ÿ59É2ÏY—€l, WÁRåÆãz}ýÇÀe»DSW‚b%á\]³Ï/áÌa8ø“-‡¶h›Ö8h|…Ò"…Ð$ŽèQ¶€ŽæÍ˜üA¨AyZÕ8—%ž-С%"È£«Ã-¯{j9Ì 9¶8óKÄþ+‚!fßa ²Á·r¡…,îòÌ;èHأ΅§½-ƒC8XÀ®²€'"èÐÎ>à¨Ç/Ö Ë9®±•3áì“Ê%äå‘K•N;:ùª4†ƒ`ì…'JÑMpÐOõàAXÍj—‘| #=pÁv`„؇:Ì}˜‘Œ‘ˆ?h¢®hHC)fìS4Ah„€:á %D@€ Tnl A$a€)ü‚ \jq‚  0,ñÆ@°#Yè#r™†wü×Y ‹Aj , !€pl¬ˆAˆ ñ0©L’J,™—z2¸ ¿Ð£ùx—!.C‹Mÿ7™ %&A%5Âà.ÌšƒŒ|ì"H!N4„ÑÐq= ‚/7Œ­æ8BFz™1`s˜=(æ‘©Lgn ™@tÜEšiáJ\•l4 "ÄÆ:1ç jó µ'HñžX„—jýÅaÛ˜D‹€ŠIµìži#™Þå’ >Ï]sÄÊ#Ñšè˜jA1ˆv"¿È¯?üàƒ˜ºùÌ•¨Ä3QBB„h‰ÐXkÔ²Opl*¦1ÅÐàD2bÆ~‰lÖô¦Â0jŽ?þùàGÚ¼ ÀF‚8—œ|à-žÝ:”™ºõ?] ôZ¶èŠÊùœ„!ñë ;Š'Y‡'»)OzØSÈ[ZiI*FP|ư©Ðkt¢LeŠ@cXÅj±XË$â¤À‰$ ‘Z]z1—#°€DQ«Z'Á µ)|- b+‚†€½¼áë8 A)ÂâªÀ(‰ hì P$`J N142"€ 88e˜ B ëý A(h.çcÛ¢è5 †ò+LR,“Ô¶ŒÈá²A#òKÛ°d;°R^½“¿X¨…üÑ:# ª'™0®rÈB@B'&þÀª^p"!t 8¸AS:ƒ¼Ò¡…0b%xÄ"‰M|ƒvÅŸr1k™ÀôDµÄÁ€ÙL6÷®Z`N¡ˆ›˜Â(î-£4ô¬„’1vE‚q(À 0­À äõ ƒBiúNhG±ÙÏÍŸlÏeìb#Xµe†¡KÒÞ˜îΰ‘@É zâOÜ3Ø)W±ªCD3s¦#øÔ G H:#´k&‚€&0!ÒX4¥--édš†›î4^αx5­¾â§|HÃåVι¨Šît?$ r~ :íØÀ¡ø/ghT»¡(Æ‘/ê‹q„ÿÉçâ±¶fFB~)Oƒ³ §HnU!Û C;6ŠL Â]xb˜³â9L³X«Ä¸=Áx …3ì zð à JÄAw È ¸’Þáùj_bŒÈ ïñ£Ã+VñS<桤ƒ|q5sU¹#IJ`é „&Ä0ðÆP’Š ”/¬ Óclô¡ðùÉöaÕþ"> „Je…¹1À啽N'ržˆÄM”$9¥ç`wfТ…–%~À)!¦¼AIšó³8 1Fxà¸P!¸gø ‡€c#ÂT*Æ”YLa>|5lgñÛã®CºÁî4D¦¾K-p–v5BèþBLIðó&(‰=n‚ä:0sy¸ eŒcºVen1÷§èÀÖûÎ]5ëDèN¶ûO»Þ´NÄ¡ÉÜV`¸“¬*(/@ˆzd¡Œÿeè¹:-fq—hÐR—¢Ù¢wºÂ(5cowê d êP\ßTÄH„j¡>±ˆ¶ ÿÙÉÿBóý=0£’ðƒb.“ÇH0# DÐ"A'¡°U—àdJ¡dï3 * —1ŠÀ;ÚÕ i Á Ã<ôB {F.×ôâ3,¢÷$!ÒF=b ÂI³ )SŸÑùÁ Tó5´â#ìÀ.Ií7s±24ñÂ&/Š HÇ@N—Ð #Äsü?ˆU´C;àä Åœµ2d'-ƒ‹e<² è ‘§#Àv+ ¸¤K$Ö)db²CU„xSèpZ¸b:ÔSn·‹ ×c[¥b¾x"Åp>ÆC'P\è‰Ðî;£¬à5ïCÿ bb!Ù¨Âö>¾à¥ã ‹ÑyÿŽ!ƒ,—za;ÊÓNJ²Ð{K·*+°8'‚^A‚ÚpOt²àG¶Àì@ ùÔ@!ª²+¢ÕRÑ "¦+ÆäT†çM—*FPwX4d4ÄT¹‘é‘’Ñ´CC`iGP(M––x ‚\0 4ð!4Q9à“ЫÒ1Öå$+2á=YQg²‡ï}²‡+³G_F+f¢_ü ™òŽ’”Ö`_§ÂOúõ8±N³¡8À2–Xµ@üs–°Bb1|Çð»I²B*¤t#óQÑ1Ïå 7`8%6`c: Ù" 0f¨p*_ò&Ï’39MG**Ѩ• 4k™7¢G™q.ëÑ7þq'h ´±)Ð1IšB?éJEHª!ƒîÙrçc‰E‘Vw=ÅC«Úª± «¯ x=Å…m÷…Aö)9tMU$c†QH!Rŧ×80 ß¡ÑÂ-#h ~²áØnUƒ pš#$îX&ÿú0UE"æ{×± 0œ0|¿0¯Î #X.rTHcBV p ÿꆟà­“1Xƒ¶ÅBºÒ[ãå·D?äD#à)Ó° ö’=±A4±ET¥\ØcȆUt“Xu"«IýØGA$"f™ÐAà'ä0 Ðõô= ö83:Û÷!¯!PoC1 ð$¤§yE ¤@£á "àDS¢$¬PAm9¯xVYA6ÐЩÉ0¡{ô©F12‘0íä17ñ1®ZáÇS ùKÒtLɲáGLÐt·[M±’`ú’‚C ù„»²d}õEC/ðŠÿ“P iø" `£÷.ï¢?Ë€Öƒ©‚•Õ¹ÀpMK)! Q(è"¸s‡¡J!¯XAQÄ"§b»Á?"ù †`6XucíPó! €NâcpC.[L…*Ê{MŸ…„¹ˆ…;Å‹×cwwÅUd;¤Dlw™Ù4¾LÕDC¦T±*¹*+ËŒà LR,*À/F² q„–brÙFТ *'0îúJ6)”Ô8-Sb!ù!ãéR”£+ïÚ1;§•#ðf¿±|Ë7!x@Ó i¼”\0¥1BD*¬ͤL’–I€GPLÿLpC€’"ÉE—fÃ8¬Ã<ìÃ;e½e….°Ÿ'òb(LÇľMÁ*+›‚šB¸Ô&GX‘°‰åVßSOö’–(aA q Ñ5ƒú[yµžðW…Ç…¥×È–ãæq|£H:’;t æT!С fäl€«‘—ÆÃ÷‘8õÃF’šLœ’ÜÄh'òc)¢C&‚ž=2¦ ðKFð¸¿æ „LVíqyÖ=öj=p2›¼£çA›óEŠÿŠX®‚áZÇGÃf[‡œƒœ22ñQg=2·ÐÚÒÍËÆÿå c,—½i»&òC×WZêlŒ˜yc‘Y˜“¹˜€é˜„©b÷¬bù¼aH°ÊÉä)/0ÐŽ#DtÐ=VT|¹ÔѸ›ÁÄÒ¯!b•z¶£e·à­†ROß¡Gñ…n ÀGüÑ0ø‡ 0œp,ƒÀ&İ¢‚P$3ýIVìf# ßL&á¬lãQWÁ°ˆ Gìñø†MÆÅa4pÅe˜z™…Êô’ôiŸî pù¹jèIÕë ¬÷‰Õª¶L`=Œ"bMÖÇJðB<ƒ,;ct£ñ$ü!</àÂ)"`Hï?Ú•%íö.ÿšQ‹Ø"'.· ”µÁ)(Cÿš¸ È{&ÞFná–ãæ¨æöÒwF_ûÖÔü ÔØAäŒÌ"2 1ºò3pC2*3¤2 F8¤¤awwVJE¹¥â$^â >#pº)I;.B°â‹7 @iŠÿ ´Ü1-!Ðã"âÜAã!@L¬®0-u¦¢Ü¡ã62äÔ,º¡%̉ä°`A¯ ¢.€Íô²! p M €ô] ¹ ] P‰#EA.€…`æ*'6f! ¤×"°Â"xZò(`l⊾èŒÞè>,=Ž^tº!pã#ž?> @¬A Ö@æ9Žˆ féIûEæDF å@UA `N*JæÓbA ‹ `t.ëÓP© `Ë[> < ZÝ,a.€ÍÎ@(.’.à i–— mèrG¾ /Xcƒ ÿþ(†AºÄÜ;è` ŽÞîîÞî(žâáéâ•ÞÝ €Š<?•&é8^é,ÎãÍ@¬,Žãœê³ÎÉa#é £në® êuë7‚ãp™åŠÝê´%Ѓµ¡Ä%230µÙIà!ð I±ßc%ÀïÒH îc…Æé.Hà–¤ ôöîJ¿ô#¾’úžé§ã?>ïš^ð1.Šœ´IÛ` â[ïã,~]_ä"0-Ó"å €ãg¿äÉ¡ñ8å‚D Wžå±^êá ɱú„KÒO”à¸ñí«¢@Ã"14èjQþëTÞGþ à1VÐ£ä ¶Ý•å®â“îNL?ú¤?’®ïý¾’Ý=a?.Cã0®È§Þõn€ãÝéZž*N¬Ržª^öéMfïE9åã𠸮ë_‚DæÔ,¨®n ÓKq*”u**œ"H)° Ð+@äÈ7ºAŠ€éÂÐH‰Õå.`çr½#ð(±@ú€à$8HXhxˆ˜(8$22Ĉ"ä8b„ò"i ÒÐB±)ÒÀ°)B"Â@ºÉ@@ KÑJÚ@ÐzÚpÛ p<|ð{ë{ÌÀþp@ð±°°Cð±±± ­½ñ³] ò‚ðq«±ÀmAà¡1a¡Ñðb±qÛà° P±aÀ‚øUxpC‚~iPà‚  @ÀÁ‚3 p €E$Kšk^€gaÁ <(ȆÏB\u5¨k00Á ò[§xq…† 5ÜJ ±BÄÄHÀŇ(K›>mh™.5’é2„K˜C^¶ÿ>0´¨&MGIýœ ”è[ÂFͲe*PQ‹‹JÌ™tªÄÆ*X0am ¶‘z± ˆ Ý©! ð`« 4(xæ!ý Ø ö5À@…  èì·€4R}†ÑEÇhðÑy¢‰æ j^˜’RµR,™õˆR“8bÉnOa5 (ÅÅÂ[l›°"Ì3!Ø2ËX³‡ )­ø2Ö1aÓ 1¶ s õ¨Íuˆ‘·AÜ0@”ˆà|` ¶àÓÀg`-0‘eü$p H U @lþwLmð1…Ô DzN†~þ9ˆJ/ÉÉ#+­$" ÿ ´v P£˜²Ü(ò¦ËrºEe‹,TÍ8#+PVUa53ŒazŒ2Böå6ÐEŸ@ã×€: LEÀAˆFO‘ÐS@˜ T`Ùgqtl¡-$Cÿ%šDùóQH¢åymH€n{!#G™’‰O›‚Bh’”R¦\%Ë*Oµâaް’‰&²teäU_Ý’ø² +¥þ]Y¦ùc/<‚Óä8”³8 €XqÕÃŽîTЀFú8Yà-00¼ðŸhÙ5ÀTC'… bäËC dÎÜþlÚê&Å!VçÚ†‚½">¢´&X1ð)’P 0oÿ±TÍ ÔFöÛ¾GÕ×½øVϪFGŒ5tmã 7àPÓ]@ÕH|Иd 7d A“ ÜS]<ÿ<»Ð@.§ÜØ?hƒàÍíÜà«ÒâIЖ›T[¹JíäÓ¹E!% $’Â)Y1õÔÅa5V‰P+uËY[yÕ/Y! óÓÖ¥ŽýËW?’Ý¿`ñÝu¼”hsÎÄMZ¼Á*Ð7–€ÐóйÓ_‹Í\`¾`ðßAhöçÌæ?T hþXp­EÄP $—ïŸ#ŒA Áh2i„R„V›£ìd"8€l^—"IìD6½ðŠ,d‚ek4ÒÑOþ˜C°a ã+W)[϶R¬ÀxqY ºápd§IÚ³0€Ž˜ŒcWÑ€ä1yôBhöš™ïLî[€'8¹3ã“ý6B€8hBÿ¾XˆÌaB(†ÚÉ*a›×ÀFj’Q#˜BTB9J>Há#PlEºhÎqÓ³fŒ%, ªŠ¤o¸m0@LÛè3¸¸myÛˆCŽÍÂ2`‹Ë ¸Ÿ„øÂpr @®šå²päZç©“E&†§˜YŒ´tBK\³šÖÔÆh‰†à‰ùD&EQJ‹ ˆ €AQ q†SæP ïz…Tôþ5$ =#¾ðÊV¢ñ£·IL#ä¡Ãè£ñDOmÆÓ@L9¦Ït²ø[=-`  2ŠñG³`¦>ʃ PÖ«Œ…?‰ØO‰H-Á(4š ³q#\"]”(ØÀoˆŠ E?JG•cZØ(k¼€Ê3À!ñ¨G@ dx> %¸Ð#-lc€Z¾N#X°Œñòi^|€1ØOáB¢˜õ$1YLb.ò™ÙÌNç¹,s‘‡~Q%¹ôPVšé¡'5¤Š@q–kþ„BpJ(BÍAŽ-*D `Whñ#A©_q 3n‘–Å$  P'Ã6ðÿBòÌm{ È‹õÄñ †>½ÈÀ«ÀÄ ¨! HTcZf,Ä"ˆÈ$ò8 àµ+ÿ„&"ø¼-—m$5èd%pU…$äœýf5ôò /žë¯æ„E9¨ ™Á #UçkÄ8ÞñXÃ`i¬P>ÔéZ)¬y£$:0À€qÃHÜSLAR¶aÏ A“4ÒUt5B¾ +¥U9Û^.O™‰kr’ a\MÑ,ˆ¢ S^ð BeœÝàèv³ …µVå e3°ˆ}ì8œ[ëÏcz÷,ÐðÕS{p“žaټоý­S†ägþñ#K‰Ð1üA¡ÙF$[VRð‚iR"¡ŽiÇTŠsR”ÅjW 1鈕íÈ(sé.ž‘5M<#„ˆ¤é2jj¤o™aÜy]йÜj†‰989D88"‚C‹œ‚’”0 —CŸœ 8’…1 ¦‚9#8””†7=B“19,"09>HÈ—»ºJ>99 JÑ91JÂ>BŸ1$1 ,ä0 è¶è1 1í"0##0 îî0!10Œ©KÇoß;Ùs7â‰m=("Ž ,"ò É ‹„°¸H`äÆŽOÂÁBËm>>x ˜­A¶e1Tù1s d ~ªô’âÈ%q )Ò£¥K> !Ðá=–Ê4|¡U„ p€@‚Høh6» ÿ„h@—ðê%ðˆE  n\rCA Døða1„`˜pp¨$áÉ3ŒèÁ°à\9nL†¡¹)‹ @94BĆ æú‰i0ÀVEµaxÔ¦ ·Â@¬õÁ@ Òº °"D^ ,ðó·a„7{è X€h‡zÚÅÈ‚‡úÔ÷‹Ñý;Œð#xžŒ!>°Ð@> c/—\à l0›BXÅçtÒ†Cšyö¡ö(ÃQ9ÒC4‘U³É t·@ `A G*ó‹.ÈfH3HôPBGp(Ðé`ºÂ]ä$Àž{°ø€€ „Àð¥8‚zë{èüÚÐqì4 ‚"Î%ÂÑ&=„0ƒe…|rwÛ^Ô€ àâð‰",Â!24àÈEÎ}Ð4ȈÀâ$ÕhC÷bÒƒ¾9¬$.¹óöÐ é*Á@, \p”lëCÿT¿´²˜.!a,Œ—Àd‡€y½Öùe\ „/‹©Ö ä²Ñ/ Q‚‹,mB=€†ž'P ƒÏ/øpB†lYИäPKES}t»P¸ôFøž@AÑÐ`r`º`,K ü0„BÈ€„|íOM¨Ô&tC%ppAš1; 1@™ 8(p^Šk]«=jµC€>ÊޏâŒ;n–\l_ÃxÊÌ&ZÁðÂÚ ñðHw3å F´þðIF¬ƒ Lª ßl2>¤ëwçpˆð;øÜO¶'¾I!²³ð2„ŒÂ ˆ‚|¥ Lÿ€>ØÖà ÑDÃ9 p,±/‹nÝæì íÍ¥ÕC$èÿ”œPp(ᤠ%–¡±ajzŠ#€€@U øÃ›òî¡À²ÑÓØŒö%‰ùKÎŽô²ùJªˆ/>À=ïµänùDP¬ ÀX H@å‚î|€>› –µŒe[‡¯ g¹"6‰ùpO{L†2¾ŒbI˜ZÀrFHläf I ÁF/Æ2#K*óÅò,€CÐMŽC0¶r#lFG€3¦qm¬˜Ý¨G€Øæz\@¨èUœç€Ö1zàaD·,Ëÿ{t¡KkZÊøà#0€È¸†bˆÞ‚RxÊS)0müâ  !„p‰îPÊh®dD&‘’h ¢FúÐlLµ&$@7`ÁH¦‰)#N À"&&ͦHp¢>0d@"ŲEãp@MðYÚó–ç<ùI€°àw,'&ñ!sÖ<Ý3<%nr¤E8pƒK,ë= ƒ(àH¸°a<Уš™.G2¨òRõ†yàHtúËTˆÐ ÍC2ph ì<‰&à'óà¥=,ƒ"  uC(ÉÆ5NÁ€PÛÜf„nL†‡Ù™"~Œñ1þtC/ÿkMD0‚—°ê„DÀ¡†Ä—!е=¥èdÕ†Yß ½ˆd†[Ñ ƒdh%`]Muò1Pó$m3ÊøY0N"ÉÀG¢Ä¨0u_p„Èb“„ „Ë;¦bL©Sˆçvè£Ïa«ŸÑ->J+  µéX"³b`¥@ ¯1„Àb²G á·`@4À‘˜|P‚jÀœE¯Þ7¯ s9£_\À·¹à-bWâ¹`IsÛô —ˆTÙ>áL๠,\B†,’ɶ¦õ HÐ2¹h5õÐ_ºC¯%Z @Ø[ÑÿÏ4¸DBåél3X”ºt¡' àíÞ0q2<)zÑà îA°ÙÉ0fÐjv¨€+h Ò6 `”xŠÞVÀ! $@±DW¿ÆqÀ €Â„ è˜â/G¿Å9ÊR¦2´¹Ë‰ ›ØK‘±´X`%›ñA&–•À/@EÑ”D¬ÙL$#PF-¶¿hi/à€v‡f Éƒ¼Jó›áÌ™9ŸÆ@ŠÉsPPgÑÀ™ÍMÒÙ )M–°D$pP«4YSa@”]@Ê–Á¬:~H(]'œMH„1G ¯È¬5ÑèÁ†ÿM—‚)›ÙŽàE@LÉ‘eÓÕ…È‘‘B(Q³“f6 ‚ç˜vMwbÀ‹X$iGjÅ"·±¤»%\i€¤DÐb ‹Æ È<° ° @¼ÔsæšØI§eQàAÇéa|,` ÔcÉTw GHW¿› À‚(¡€}Â9ȱ‹ ay!\pÄœÜãŠÆT28†Ž£“'KHnr”j+×DPt ,ŒU  2%ˆ$x@ :òÈ%‚ƒšÒ&N½Ec!!ÆE„ñíH‚Nq3± „IlçP‹u‘• „_Zâ)ÿ¥¤è•ºbÔ;‚ ¡]B4"Ü’x³¦Î$Ï6þãìwfª•Ã:fƒÌçIµ%Ï Pzú3‹¤ÏjI¶Eu*™&ÉHÕ:"‚ÊâÞø‹JN)¼³ñ²•›Hå'ÌC cø#à@ð{ŒÀ`÷„è}Hþ|ás„˜Å·ŒžÆX„´!6RF ±qܲ)äJYr§š’©\p° ¢OìEw£0ðâQÇE7Ÿa¢±"¥Ñq¡1˜Òntc …O‡.u6¿C’Áx Ͱ$f•8ÃP:~Á>Íä)²6Ý@ŽWMq7 0Wîf[5˜d8øÿÇÒzî°§&TbzÜA[§Å„Ãïñ9ñS,‹p ª£ ©D ¥%cr\‚!7B ÅÐVg•Va#‹ ná 7@+áâ„‚ *2…8P…Oˆ…òhÈ…ä¥<œ—3èvQrŠ nì¶ë1`„hqZ·eîð,ö62™’) Š'5]kÒ0]{"k…B=0—(‘rQR€$×…ÉM@t¹q "@0%7@Ó áxGâx847‰ë—cT@‚ÂoBðöv?wqNQ6e —[LÔ\uD±qee ßC @ÿ»åa9"@H@êØ êHE@!ªðŽêHëøŽñ˜>PD íè츎c4D F  „`ø(c$ñ¨i™€JPØ  Yi‘ú)Y;“ÐHäfn-ñx/èmíÀ—4%$f0 §[áæapZ{Ç3,Pc@“ Ú€3‘Æg>”z—4UÓ4~¦QBiÝ"ê¢3H9ƒã“@ 5EÀ/0DàJÕ6o‘Úp:»!tÂhn €TjÓŠ@¶ à8?I‰±Apdö!à ÝP09 C€`ý@k@`Ñ Õÿe”SfÆ÷áî€8Ýv›¤=ùˆ³8ò}Í¢™ÀÓ™B}&\õ‘Ò™C@PÆAí$áT @Ö€8#›»õ1.™"À;ÄVÆQ†Ù"uU‚ÃN(à’3  +qðñ˜lù`úDÉ• Ð8B\Ž ºó8´BpºT4dP‘ 't-°rr”\àF æI.€CûHë3“4*H$—u!-¸’u²KD Hb ¸á’N2{7`À Ut!-ãÑ'sv¢KDFE)ó_²}úÕ ÿ’8¶a ÿ®¶_2š,°c= ÐûE…Öˆ> 2±âp,àR Y4`4У3…£. Dr A£4`£A*‰y Qh€)?Á ô‹«0SŠ¡/°Zî³O¯-69%; ZÀ*¦àÊ·`µô}…ÐŽ L(VKy¢'Á´ ÄG‘Caf ~j.±§cô >„@%ñ €Ÿ²,Á÷[1  à!Ñän,—ú "Àœyñœ$3Pï?%sÎAÞã?0ã J¸&aÂaR2¶÷]Ö9B09ÆàQ ù =ÆÐ’-îau±_ £hû5% pØjÿ.)%÷#%á3„õpÿ2­U'%çΪ=⺭åš%0Y¯ )Éà f‰hÉK9 Æñ9éˆö„)çA“ÔyI™²#Ó!ÂW‹ñ ÏZV"#²nxÕV&@W›61‡Öc†h jN "}†GEJA ’âLŽÄ=]õB—CÈÈ»ˆ_h¤ ÍÀãú’ +œß ²ÕQK´«`R]Å2íÁ;[E¬T €PÆ„ ÷100§R’š¹¯<;¶àºòp'"W  R²$ò0 °¶"Ð¶Û ·ûECЬr; 30¶ÚaÐjAd;uë¶“±û–ÿ¸Ñ×·e[‘è‚!–5$BGä°¢%»µ¢PTq09%ïBbÜ"¢à./ÅÖ`öVb”Bºôr ‘0ç’.Ÿ0•ÕJ’ºý°@ foJ ð´4g °»‰eWN1dB@U1–2`š1¼]ña1eqi™æØ ö³DP¼Xv¢ä?™%8x­©öOó‡,yy‘Z ©E“æ0¿ø;€ÈR¿y8wq¿ý{¿ ,,ð;“пð[¿å ¿õ Àü¿z‘ÀMˆ+¬Àø» &¹±hˆFXD[Ƭ)¬zæNꄹCÅf'ÓfÿK¹³;½)—à @)ÖFl¾s |˜ *ÝS!FÀª‡ÂÑ Ä”²V¦´ Щâ°o¿ *é3J¹ºY\*Õ¤%Æ@%0ZÒ¹û±,V]J @Ývõà=ÕÇs!^`¯á3¦e[e) P"@,äñU†)ˆÀå¡ãD‰°“tI°jÀ„˜rpÚ˜¬ÉÅâ¿§ðZŒL  Éo'ɦ\ÉŸŒÉ‡Ê °É‹œKQhçH²â)îg%²%üôäh4ÉOÈ’sAG[F®`"t“ŸðwmwÌÌŽGŠÔ*á3‚@VHÿTO±•aŸ0ôd¦D7B”Ë**gÓ}A$1ÃZLB I“›"2"C2\-ZÖÆÎñ«q1bÒU\¥´v1ÇSÒ'æ‘w± ^‡Èr,ؑ؄Ø!:‘˜Ùá>íÑ"}N uëDÒ‘¸*M8P—0ØÁX' ‰‘h]ÒýäÒ)- µÓŠ×ÒŸ¢Þz“–âÏXºù‘H«[’ÂZvFx€™!”¿u…X#±€¹Ö4Ð)õ0FÒ‡$•ßÒ•ÖOÁQ4˜ºÐÕjfó<¶ToCvWCß>‹”xôBøÿì³N®åuòs8‰Ã>à ÇT[ÙÄ Ç-ƒÐú3ÿâ7©Ñä´Éë4êppÏ) ¢C2¹¤Aš9I,øÚµÒÚ«M29è$p®ï¤Û·½0Ϲݶº­À¼±­¾0ÛªÍÛSrtŠÚÛAzËü>Iâ1@¶iÒÄBq÷Bø ·&o&(f’õò&{ÒߥÞlò\î QñÝk§x^dµŠ0°]h&RÓc'Ë"Y 0ñ`_+´ŒcTØÀ7°‚Îr£$wbI˜t2XzDzE0  ЙŒ+f T‹âq¢Ý%³w›¼¾§gÿÛYÛORÆYò$ð+ÀNòNY”“$ã$‘¨ÚAžã9^äSrN#ãÜóG§Nâã<îãF.EP>ä©)InäÆá£H`Ü -xü‘œJQ›è€I㑘 qXOÏ¿ª'‘RKFù4B™ÏJÙi0À.œ!çC©”LÃ[MyJS8˜nↆXÙçsaíÀ8§„DÙTàôLÑ4M,‘Å«’©›ªM“J¦BÖßeOg;™ö›¸ö›1Ê&~ÈÒF Aµ$ú2{—„8ƒD8Fx.²;¨ö›xô›Þ#ì! ºú›ª0˜¿YgCà+ö Œ€ ¬)…Iì[•ŽIìÖ`ÿíÀžìÃN쮞©ìÊÀí.âÆœ– B»3»|Žyw<©†<ÄÓ¾²[`†ÑªåÜÑZ ­°rjÒrâ\›`mNê¹d@!2*€ði"AÐ *0Öss—NAS@PÎî@)>ðº„Ô—°X5u#ðΙFÒB§:§Äà àYy12,†™Ä†`CÐyaⱞJŸÉÄjµÄ ¢Û©w){Œƒü29ZuS3ª=fše⡸ ‘ à’û\ÏXö[Ÿ"áõY/¶Þ“öWÿõZ?\öeÿ¢ÿA@»7êfWƒ¨_-xâÿˆD(ÈA1pqÕÛЬŠ|»4¨¿d¨“"¨>à¨¶Ô fª§¼ôN“R¹wCqùÛ }cd yµ§¾t Eà&m9Œ-C}M–u¾±ÅUd¸IöKæj^&É`æ‹FJ­þê°®e2,#¥Ù2vªO ±É°!Ñ gÜc‘u’é©Á¸±ýÇÑ 0pˆJôY"j²øÃáïãÿÞïþQ‚ð¯ØÍúýõ/þïðæ0$11",#09 8HBC=8#!*+ "   10!1¡¡‰,  ! ‚#H” B37‚/0>>”ÿ00"8990Å$ÇHÀ=,«’27,ÔÈ>98 ½8CÉ0/ƒ73BH9,Ö” C181ÑøÊ>ÌÎÐ0J葃`=|€—c’îrøp‡£‡q#±`ð"D,8ˆ@ 16ÀÀŒC#BFŒ‚€›8s؉“ #Ha(Á†G7”d° ©Ó1B€*(Œ¬ˆÊ!UA,ðõ W1´ÆXÆÖP^«’¸ÚV­Ô¯V±¦uË5.XbÉö:›ŠQÁ±nqÀx‡„@‘1d¨ ŒºìÕkÀ†R‚ÁQfD-„c"I8f„¨ÿÔZ‰ŒÏN@S<[ÜÄŸz ‘”CÆlÈs4ø@Bò!fä xŽ`mÄ£îþ;‡îJ Ñ#R$KÜX°À±^H¶C”‘áCÄ‚&š‰‹`R±(‚©€ „ 4à‘'9E(! ŒB[9¸npVI ÚNnÀ¡ 62‚p–ƒ‰  ¸šMàÃj hÐaœYÀ€‹0ʘŠ+¶H/ž˜bE©äjŽ,`#Ž:’5!¼@dy G1ê±Ç‡½°“Ü’Ë.(İȕ8(ÀË+¢(ÂK‚çYЃÇŒÀ@%> ÇLá<Ógÿ•œ§žxBì¹Â"¶†Ü À\E lõ‚!‘(r„C Ch40*NÇAÈð˜ç,@* °žC7 G̨Šñ†„°á± 0€j©p œ' DK„ÔRxá…"0À!¦(›@$.€YS-r[é*…!°A Ê·Ì„ Qo…0h¯ Ò®Qìºkn¼é®ÛîV$¼{®¼i©ÛoÁÿ¢½QÙ›*¾ DC eÌO=¼fé  ñB³\@r˜j•‚ÀHaÌ&Ú+,ÁàÒ;,ð ¼`É<1„S Ü7Ý ™9£ÎL3]ÄkÆ:%Nÿª àPt#É2@@C (·ä€†·Ìå)0 °àµÐ_ ,âºÁжŒ†°•pÿX$–³ ¥RAÀòì²—°ºÀäœ!aæÁÁ÷€lÂo €È2/‚ E€EÛ€JÜØ ‘¸$qŠV¦ÅÄœ0€T5a@BÒ £årD£JDô™µ D©ÉNv‚îñb'hÙ(>2!lô¤;‘hj*ŠRŒ)ÁDÕµ•atÝhG9 ÒTˆTðÙA¤ãÁnXê¨hT :5N|@,ˆ2+õɉŽÜ²ÀE9€ÓòN¹C‚{~ñ:މÀ–b[ |€´•:XZ‘°‚ ¡À`.8º‰"$HÆPbbÂJny‡ü·®Â¢€ÿ­ pëኔlEn ] Þôª=T£N0`a“É“ê`¢Ò«øb@D©RKªj$…b@“)nYR@@Q¦ž4_‚bqPF ÀD!˜Êr€W¤ˆDÙ øÂìå¹t”nTbá]çBW¡âE(š ðF÷x¢®um‡]CAž¹È#ö²¼Æ²,\¶€¨Šå(Ç[¥cD€£ì¾§¤ihR(„,` ï” ñ%ä}BÂñŽ ³ ÃÉ;%¥„¬¥]BB* ‚%„#.H € ‰LHFg‘W¢"ÿ&qd1¥S¬”¨Ä"¸4ËGHDRåyÅ,€E°à( ¾¤8‚Nh'Móàˆ’P4—dºýÖ†Laб È-4I€[ P¥#À€òg0`|Q™Ë÷Áçz&‹.XZ@ÒGßÒz®Ì¤- hAú·AcFkeP/‡Ø3Â%°‚ …ÀGƒ IL˜oªpT„Ë`# M‹H³Îœ(tôXÐ3cŠZ¡„T6ƒfaXÐ:˜ˆsA€¶AC âÙsÅGÒBm‘\ÚÚvÉ qC€;ÚãÖUµÏím¯ÛÛã|Ò©ŒKÖ¦  vÿ0`´Bÿ Ѐ֕Š]“·ö^V·jgU2§”«qø ðcJ ?öâ ÇÙ(A°À’ɤBÖ’€ \`9[ÐZ4Îx°ä$@y$,p|/¢`fN²·0“·õùˆb’­²Äe fŽ?¤âç+eç=ìÅy]k  æ›ÒqrѸ:^梻teí¹MYªË™¿Ðe/oy»\â>Ø^ÐÝ/y©K_à>¹û}8øÐ:‚¤ö`©îÈ÷ ,Ò¨rb•X7ÌŒV7ôt‹ÿÄ%2©ºK·‚u\Ãvó\¥ÊË’½N†â'3­Qa&yÓþ7åÑy~‘ÿ3 ë³´g*û ûñ vÚNÀ6ÐtΫ>ês1¥Æ½jz¦£¾óÐÇêå80z =Zdì}Ž’ìÜd¸ËB€ÎLI$9)Fì¯ÑT%ö7éHAŠ¿”æ•* )íg$“3"82;¤j ñ®æ!²ÄoÁ²¤B1+87J“Ó`l1ÀVUëcD9Lq ‡JaÆl’YWâIßã¤`&à A –@|²T£Ö( |E Ó$3à; % =r a ]²oIå3gZ$÷„E,]æ,hU¶A}¹&}¨¤,•…' bHH±ÿjE! E!jØuè=f261\:0 #0þ/1›s/.á0Ȉ #ˆCˆÓør0³0㇎Øýõ_/Ô1#ÀjŸðjâcnó >Ä@t,‡JŸ£„JTÇ1 ,½Ö&xJž!3£ fB!% S:ñ «Æ€IȺ—I#&|ƒ4­±‰pã‰4á#ÇHO9€" Z àb'x‹¬¸P3$‚YX‹»¶Š3PJ²Ž#à†2Ñ[ âÆDËÖ!ÈÆ!òB<¿#;¡C;úh<Ͳ:€ úx; ³;½c:‰<,òÃc:< Y<©Ãÿ˜u÷àx'Pj]ã9€/§+ 1Ð ÷”OŸ¡t¦ âs `D¯e}N—tVâã ä·!ø¸@¦"÷WpŒ@€jÙCéaGY=°‘‘ç]™’0G@:”=1`Oø„/Pz®à’²Žé'®@“œg“_%–v"ãEwv-3!4QnX4t>÷AG)EwH{DF€„F´àR}¤H«€Ù˜ƒ$˜xD˜ `˜?‚‰y…„9ùG F‘ ?òKv1"€ró4“'8ÇAé„(0 €EŽ!ÐJ§1R¦´ )1u¡çIÏGÿ›¶¹2#д‡Zs{]’•ð1™ø²•„_³G|Íé)ÊØ_(@'>Ò!ÐW›¦!œ†yŽ£¢k®øiÁâ©2¨ñtëˆ rt-·Q—¾%Æ¥,g:¤+8 8'À(@Ÿö ׂ†½˜~7ADBÞS_ÚÅgñ]Е^¡ÀB_ˆº][±¨æ^ðõ¨äp]f‘]” f–Õ^š© ZÁË,Z‚_e•0Cî¢5!K²æËwŽ5³£‡"§‡s_fŽ­˜«:ꦕBÅÂ=’t1ÕIÈ(Ò9Þ@Éyª©º+!3±Nâ 3 …€|ÊÒQ¸Ú: ã™-ãyºæÊTÑ®e‘D1Q—¼5—KÆ|I%Q4˜HjoÁvö"i•ÁiTaäPhÑ‚h @°Œ¶i~vÿg  °œÑ ~&°‰F{°ËC2èÔ%-ô+GE@Ì:‹!KÐZCÔ¹±DW Q©:Æ œ,³Û# }G'²p'«¡›ú„‹»Åp²Wq•ò‘€—ÐN*¤qNTö àYx¥F‰”ª6¥Uê B4Cg4+´7›øA8D ¤f‰“Ak³üÔZ@kOW’" "ð#htt ò!˖͆ˆ oqEÓ6oÏÆÉÆìÖn†ˆKnŠû*6s5[ÁnÞ& ‡‹òfn‹ î±ìaß“ ËšLRµ¾’´ÁB˜Q•&âž#ų®5G4»ŽC‹³íyÿ› `»2ù³R+ÌÓñR†|ã_¦ú¢ˆŒ²"¡)É»¼‘œ¨Ú–»0»·É,1) ,¡«!¯½Ð»Â ¾=ËU½ë#ß²lDË‚g1gÉ"¸žP+Àu@wx÷0Páv€Ç÷!sç¿jñ=ÿ xAÀ™Òw0àÌ ,À_aOæ6-4í´±¦ê1”ªêÇ»""‚b¦¼à _†‹£ñ®»*¤kòÂÀjJèË2ÏðDˆ@Ý`BLã‘ ’2¢H  µtÓYO¥D<²‘À$¼•Â…:â½ÃQ“g–s; 6L¤[ü»Öÿ>à«‚»µr~of[HA9ÈæÆ#¢5U²™Ì1•œB—\ÿ§ÑÇeM®5 Í"kDËFμâ;ÜjÜÌ k¨Ûb Žñ-»(UØíÛÜr¿ßòF=$G¥CHƒ ˜®ÐF0à=ŠÞÇzÒšiHztÞˆÉàGíMÞ]Ä6MØNÕjtédVƒzƒ þ…ƒ;‡  A\u`rôeâØèÉæYÆþÝ*¨¾·+IbmECµéÔB§‰sÌ0â1K(Kn#R ”&.£B.“|ËgƨäÒÕzGg¶ü„Ã6î ˆ@u­c៽Ú2¦ R9Ü¢,”òzHn6A4àRRµ% O7åk´k•;‘Snzåõ:Åÿå0"_^å'U[T¡Aþ ŠÌ™à¹½¢Ê3”HŒ!@C$#Q'‘ʵLލ„ád¸…fYͪLè>n;©°|†à0à̵ÝIUu¨ðUã@¬½Ûs~ÉË“ÁhéËs T.B'³† è×ìͪœ,ϽÑ]®^ËŠnëµÞ"ÞαÎãrU4~Q4‚v]žª¨¡ê^ŽÚ}m³jŸj©¢Š©ËÅìñI_Ç^©–År.W•2WZãtL”Z$OnÅs‰'”?·þ‘p‚Bþ^ˆ–©§ž ä:þÒj‰k¨ ´ð»ã¬ÄÞu Á!øá’@ðÒ‰KþÿBP(gð­“,˜=²ËQÝÕ0lJPÍÕ\E䫱, °ñÕñ<ä¡§3Jn[;±‡c*ÇÕ­,Ôzj ±ÛgfÀ ›ó»ó;¢Aï2‹C  }NMO†=”nᔵî&î xæ:År,ÍWï(­ò&½ïg)ÚbuÂõñjØ]Ï`Mñé™´Ú­­ËÏ[éIåÀ@BÄìéØ!c»Ý,£ baÅkÑ}Ü¬Ž ¹^È÷ñÜŠÍ2yökÍ4T$hy¸Û]ÝÞ ‰ú™°2÷¹‹ ¹ž[nÖön™‚Pú…{ú2•ú• m­£sÿûœ+¹LøÅl•A˜• Ø3®¦„ØIƒï º]}ƒ‚³‡^¦.…Ü:†pfèȶrâÜÅ «ÒЦ Ñ'¾Y9X¼·%!îô8O”'Â}>ð•@¢U;1ͽ9__ŒòÍBö¢‡ãø10!1 !  !# Œ   ‘  ›¦# 0¬1#0#1! ±³1 0»#$0 $#«­¯ ¸´¶Ëº¼¾ÀÂÄÀÇ®°²´B H8ßHBC=8å,8+H7B+àáæ"+ "!Ýñâä89 X\hÐi®ºlñ!Ɔ]­`;ÿ¨ìP"Œ R¦,á†YÁà ‚Ã,YLŽÀÃaÁ‰ L³Xºô± œO=Ê1·„œ2a|X`S +[¾Ì± G€ ,€ áCˆ&[µ ŒUÒ®2à$mÚpxë«î€u‘è„iS¤»4-ذ!£Ã™CPù(Ê( Ž€ST.áÐÀKÊ ³Æ Í À7š…–ª¬zÒk#Õ5\uÂWºŒ$Þº»ºK€¯¾àŠj} Ü·dH%€0ðä(£VYScåàaJ8ÈbœA9u7Ù¾ùŽ@G_ÌAƯq"ÈJkuVÈ#T2';„Bä@€‚>°àC ˆ€ÁƒNH@? ¡„2|ƒl·ßæ'u îö]bl 8(`È-J¯X0EŸ½_‘ ÕÖ]½ÁŠaƒÞx ‚ÀB/PЀKá\˜Ã€ˆ /(‚nºñÇ ¾ä8ÿßã¸4àÐJÈ€.v€ÕeÊÈøwòêŽõ¨™çÛíÒÝwjk͵BPôVA¬…¸#|Áp`A ‚¼øŒ°/\(`7lÆÀ ÍÚ}µKñŸüò>ýô©rðÕWÄ’=C€sá·B‡ +ݬ¸AQŠÃ ëw(?å0Âîƒß„ÀAâpD°4¶ºø @rD+Jw(P$X ‹]À+‡D<ÁSfóp$ÊhÔÞu°ÃÍb œ’“"!Q»“È Dž±,À¬‘WÙ,  ]XùWd¶/idPˆ aÈÿHÄ6q<Ì`nG;ae0ˆ'Ø R4 B A)Ö85ú¢pË‰ÖØ‘2 jdc±8Ê1!¥ÐŒϸ­Ù‡! D€?C}C?4¸l(•ƒØÍ&µÙTåj­ÂJ3ÐC²ºvpH|é–ºø|‡ƒ“ÕÉ‚`I†ÂàL§II²À9Î)‚€ 4!Ð FHÀ0 Ž Ðe&õ“ÜܲX2€@¸©ulƒ|J Ëâ‚‹ @Î× ¡•¥4'(T”¤¢wL>Ф/"ì‡` r Vp8ËJÿ‹‚¡ eèâÐòЄ ” =hL": Ûp*D@:F:†Ã-ê¤ ¹Ëo|K¤öû+X 9à%‰¾Z%Z”:š–SFJ¥u*½mÅzAnƒô$\Ϫ_RóçŽH™Ã[=@é> aðç'A9Çý|ðBð\M17}WÀNÅG€d³´K¿p /zåŠ]¼ŠÉ¯Ä2W‘‚™àA1s‰ Ö‚‹^`€ˆW­`ÀwNŒÆ>Ö­hÀdkYÈÂ@²ŠÝ cëÙ mF`V¦– Ó…íØ‡ß”¹f.à*GÏ~öŽÝà/ …r`.tÿ-À›/š`°r5E,6p,FE.é–[ ¡0i@ |°ƒ1Ú¶p ’¶´¦¥V>(Άã­H^³?ƒœ@ `W»>0‚XB5ªR©N>CºNvd㺮 !º@r <\H´ÇmÞŒI?÷ßÝaB°0À% æ¤Rœ¢$Þ9‘’L²éÕ–œ}DK.FrâëbÄAk@üÊ«D¬i1 Èá?¾/~ôUy™†ÌÅ(ý™ß³4´OˆB.¦yó%Šip^góÊ]ÖæN‡Í¹W.½F Ç'&轪9DÀ” p•3ÁØá’IõàHˆG§Þ‘Ô¥F¦ÿJ*•3Õ܃ȇ:ƒI`ïrªŽÑ®ë««È Fk&!q N£øVV\†KòĨ £EP£)¶–HB!–Bó$"a@6PU·‚՘ϫëd`rBÁ&-è9ëg«…Î%&¼@— bó"ü£HA gµÞêÊ;­‚­ÄÒ!$Q JÌÆÖ@Ò¾ú©¬Â]«TQº=8Ap|l\ëÞ3ŒwpØk­$ˆb@Üú-þËgJÞèv <¤À°+,€´‘H²X0ëÎwš‡ãéQ7¸W7€v¶m@»Ý&h /²Æ5÷ÿ²À쥋ǰ¦‡  i䵤ֈfç«yH…ž¢û¼ÎH À r3RJù¦û¶µ°r @4Nú3uœÈWϺಮR\F]ä9yÇ?.`÷ˆü.sÛ©ÆÏ“—ÃÎ ÚÄ®T4u/QО;cD R9ãON@¬ûåïï¼ã´‹wŽ"ƒ)·!ÛŒð1/T”b±é¶Z¤ôÀ»ôšX·J¯5W$(3NA£S,aˆðS˜ät">uÑÕ `_Í$ü8 IÆïò•ï >…@KT ÁÐ[Ë×µy>”¢N{ˆ@— ¦?.„øbÁÖð¨Pÿ§ˆ§ (`QÉ£ÈâH‘Ø7!©×i,ú÷zýç·70ð 0è^'“ˆpÝÅ èxÛ‡gÓ´g7C8ã5Ùe^ ò‿ 'C"ç`³&ÿ—ügz~'æ‚îŽF¨%wƒ&´#jƒqa´C3'£1œp*%Ã)qà! VRgb2(³1LX…NÈPØ œÀA%cBpEeCf^ó°æ÷xðp( Âþ@LÁa†RVdqÚ×€2…6Ef–†zær4Hrî4fhó‡;„ET·Õ~6y‚çK±Í”v¡uS!xK•ÿfvFJ‘h³ S4X‹’@Ž&$ ö`r— ÈëƒF¤ˆîaˆs ogù”'h)B€^™yµ1)0Š„1 Á¡o™;À(1†t;@”ã ¯˜*´b ¶B‹#ÑŠ—Ú±(! A'>âsÆQ žŠ7…FD;†p°²$H‘|]d€ñut‹ 6P— kñ—Ð üh7}„Y þ1öhP‘i‘ð°‘ ù‘"P‘`’™’*i‘I÷xú¨YÂÿø‘ÒØ  X²&Q=$ª³ ;i ½sÿ °p¸„x9 xú±->5eåe‡&ØÈ‰ç†ß•A€d[qÆåfŠ p„Ø6Yv/siFd€ƒÈem³w('>.ií˜| “ý ”y“#!X‘ÀÐX‰Á p&#£˜ƒ XéÖ YB˜—q˜!˜çȘágG€!™J‚ •‰˜+©‘Y’ ’« p’° ›p’+©’ç8™¢i˜ˆIQ‹ té™·©$£1£P PØ£ÊÐtª0€­0VÀU‚'ø€;a‘ã8¾uy®¥yuÖ€ Ø€yø ;á7<¤4u;êèhB'ÿ€XBA1ƒ±§ƒÈiS„Pö‰[”©›—IQ šÉ ’;)œ ‰xÒ |qŽ!XѰÒGQJ Zø |q :”K2”Zk*X p‘Y%i‘ùÀ¢™¢£(I›´Y›Ü‘bj1kA¢š7:b£0$2 ±ã¹ &ŽèÁ€%´2JÐþ09Ð]00 p¦" 3a‰”05R˜HoŒ2ÿæu"vX‚-/Q®ð@/íQ¡ÿõJ³ÇJਠgfñ$‹{!¢:ú : ¡y º“Ê J"ßøŒRPÊ&lCÙ '%ŒZŽŠÿvh©ƒ àÁ›:²˜ ©R²œ*XP§¹‘(z‘-Ú¢Ù¢):›³£5Z›;yŽ—ZªŠªê ’ ª•:ªšÚ£`ó±Áˆ9"à³@± ; Y‘*’ [´&ÛÃK—A>L±ª÷ò…P^x&²vÚ¸¡$àáŽg…_a$‰‹¹$zé²)³3[›7+£¹ ˜ž;¹¡‹¸—»¸É'>›kP± º^"ໞ0\ΪœM ¯¬ <ùÂ׺´“§þÀ7êC:ó?ßu}=±Z«E5?óZù3Müà R[Î$!X9S5¥Ž«—ie#:Ôƒ¤ÿzpõ\eSƒ´; ‹ ¸‰¿Y‘Ÿ[”;)!¬¡ €Nš%kÉW‘ô7£%ûò ÀèXÀ pÀž¦:ºÀÀÁ÷À7åŸÌ®º’²Ùª.+›8k³‰ÁAWÀ|ÜÌìLŒÌÂ|àÕšKdL¾¸5ú ù¢ Ê#H4tÉ4´6dÄÖU#ðlŽ4,a:C,À*_ú5áf:„VR6?ôs®dJ߆JyGDu =…Æòôn|S;¨ª ’P³i#£7õ*Šù¹Ê ‰NcÃ,ਜ»»:ÈðQ0²v&ˆ,kžÈAwÿ'*³ٲi­k«) †LÉÄSÀü¸™ÜJtÊ ¥ŒÁ¦‚œÁ´1ðNÃÕ”4À´H µñC0I tEÒ®-åà^ø¦¦uH›´;bB"áZdÇø&=Ò£M„¹0·Dy' ãädq:„€Wþ’SáìÆuš¸—\ð‘ñ|Ç+º‘²úˆ<2UR>Ž{±ð¡’øÈ& 0¨‹\>Y¡ÉÒsSÂúYúÜE‚@>¨›³&Â* Ê6›Ñ°“ í0AÑ”‹ : П•ä¬5`ƒ1°7>°48ãAEbµß€ Ç®ów&CJÅTJõÿ8ë4M&@¹e>ÍgÖÙf5h¥Na32'¡ðk{ò›·q£\úç¯íÈñ\ôœ¿9³«{°Ÿ5”báÑDÛÒÒ·’K2 @Ðb¡³VâÖ©3HA;r­Ö¾àѨi’­;Ø1ZÂ6+£’©×F+™Râ×Ó˜‹× ÔŽ|-%±Κ˵Á´>>PHÆ@60tÖ\¼û¾Dc9¡4;Ðtˆ /ñ•8ó-<ã3þÃLN¶£÷E¶FÎüE*¦B6o¡ ‡˜–^ãe.fPd‹÷b éAäcP‚»°ô¬¿bíšylRr#½ƒ¶° ûè™#¡«ŠY°1ºª)Ùÿ©s£5<çÞÒ8Þ‚¥ž™Ñ üº¢|«7«ß³È$*´ô-ÞYÞï]©¹™à 1ÝÙ]á8~Ä#òÒ Òs¤Ô$ b“Óâ(Ææ¼¸Ï(ƒÂRñpÒ¢BÆ&)XEWS™ TìÆ©²·çf]å|J5>nê;•s= ×½ÝªiÇ éªBK§¤z|¬D©«ð\Çü ”Mª5  ¡äÇZ¡—P¢Pþɇ à(L›Xî ZŽV .%Lþåj}æLÑEœÐt-!3 4Rµ=Ôtr!›B,AM 0u¯¡8ˈµ³Œ•—TÉìKÀä3è…ÿuÍ8q'çp—&7wrãLSmL—s§rPÇF^Çù«¿²ŠšY. ¸ë 7r¡ÙàŽ åª.›’|¸þÇ º£6/ี¾¸Àæ)ùß²yÑÍÃÎHaì³> ûëÁþΚç¼;j˜L¡9 ©ëØ}FœÁz êø¹ñ)¢Ðdò¹Îì‚›SÎBû+oÈj/1ÿò«òÄÃòž LK›p|JÀçà­Óê¬î¬uæ,AÑ ck‡ ˆ•ˆx² «uOcGs†VÙx"Ð ¼†,è ȘŸýÙrÊ]·É-‹ O]`]° Ï`ñ„K’5+kÛà:Bó+iñJòü«êká+hÞCuuø²ìVÒŸ æŠïߎ?Ê®¼’Ï”ï –¯#«ù“ú£ïä y/5]8Sãç|.ËQû Ô9—ËC3¾×glu†UŒ2‰[* €‚éüVâ‚Òâ„¶u ÐuÊ¥8¡gãzZwªò鮈ã=®ÙƒÇ-I?>‚käù«ºƒÊÿšƒ¿°YWºÚÑù)Éø£ö?=ðìê¨Ú   ‚ Š‹Œ‹ŽŠ„††™… ‚‚  œ ƒ…‡¥—™Ÿ›†$1>90H±H99´9¦# 1 0À¯"·  0$3#=B='ÍHCB 7ÍÎÉ0C7B¶Í8>1 ,ÌÎ(ÑÓ7>BÎ1¯…" ú‚ÁF‹1â `ŒøaÀˆW)(„Á!? þ8 8° ‰(X"FCŠ$¨„À’e’^>xÐ’æ"N :1¨÷‰’ÏE*ƒ U ièPJBy"Šg&ŸúI’ÿ€žŽ E%ôOi&N?AEÄVRIq~ ‹…ˆUt ׈¿Æ€A–,`ଠœp8zn.ÈoDü¢Dˆ $;\Ñ@D Cp A":1i¢—<‚Ã׿‘ÇB#9E t9HàP D ¿ îõåë  „ »öí¹w  TÐBRJhÙRѤ—hÎ|ðH ·f¢Jɨû÷A!à°Ó-¨çÍ‚P?A;°{?M‚lð‰ÒÀ|õ‰ zûø_€ rÀ ƒ"Öä° xùƒˆ¦„B níŒ1ĘÃÿm8¼€7/höÁ Œ  &¤C¶1Ôx£9VZ8#,ð#„p¤eId»€  ä³@¿ÄÂC†g&Sb™È)ÇmÕù i~°æZ£ ¤C†œW@Pâg€U/ð€¢ŠJ2‰÷±°_0‡ŒÀ§©PŽ„‚ )œHÚ¥¡lÂRž`êˆYXšª"k-@A!žÒJߢ’êêWªÈJ+Ÿ ‚“9ˆ@0Þ±Æ"»æŒ{Ý"_ÄÜ2Ye3ø`0L°ÀL„ŒBƒ IÀ 7ÀÀÂ`ƒ-Ã3Íø0®$€,ˆÀ, ƒÿ YÎÊAC¡`  0ÊZ…|¦/†À5ŠEýüóA+´°&@b2À•¨ƒªd袇Ɣ¨,§ u‡À`уn-ÀÞ¦ÜA i82Ò"ƒ’23ûw Ð ÿ$²OýÕ ŠYÀ«z?LÍ27}4Ô #²@Õ˜\  üÂÇ ƒ#4Œ ÌÈ:|‰ˆD7‰ÛÃÖÄÓC6/øÀŒ8à°@r|Ì29ÜÃaßð},À‚Ìd‰Ã.$@IätdPù‚’x\¦ÌkÚ“ÐB04Ä‘/‘ŽÐá ¤.Ì›Ä!T‹À¢úröñ:bÿ#›®AÎ5½<#êYôW#’2¢/ü@‰õÞ¯ª½)Ü/µT$=½)ãkÅ¢ù[¥}T@ÃÀ0Åcÿ/,̘/Ÿ…`у°ƒ5¨ñc¾Q¸¦= À0 0à!hÀ8°¹[œ¨A‚j؃Ä$†8à à^š‰o`Œh @‚?Jù&dP± nt3Ccà†9D@™"Vˆ^pàwˆË¼ã¨GùÄ*Ú‘ 2µßñ >xÞójž™…gÓc„!P |M+¦Ø¢ šF‘U±±ТFæH3ЬB*klã¡f9¢šÿ$îX7*ÀmÉË/L$ÕÉ‚{1Ó[’ôÍù`4¥¹Ûc 7ƒ¨)¡*aPÂ# æáDi¤Rê‹<¢Ò,1m`O†Eô‡¿"1 rS™|‘'`¶i˜=ÔÏêV´'ª$Œ"­ÂM•UQެЬ¥("Œh”:³ÉBàŒiTÄ€¿ìÑ ä$ÀR€55.’ž”jÄÓò¹OôÄÑ (!W1Ðrf‚Ÿzœg=ãxIýÈ~Ɖ0Ø™eF>ØQD``õkåÚÜH²Å)c˜S b Gœ@0ãB)ÀD£Ï´tqžA[ÿøs½=Œ8{¡f釺,ñ-…ØØ*:f–i®É~<$fÏ›l³Š*ãæ$È'2ŠXÄ,0 7ÑŶz›cdÉxPft†‘žàV È´˜…[ÑJZ'>í镯‹(k`ÑŠ®í|áë"ÿjVÁ6v--ë^Ñ·HÀFû#ZA;‘.d\!.>°s ãrHÈÜL€.û± 9•i|°RB¸tÁ'q‘š¦‹0HÚ @„œo™®©O•ÃŒê1¤²ÎTK=”1ŒœÆÄ!2ÿ×2~S¬èµJ_Wñ‹o1 9ˆ²Š]bWvÖÄPïD§@Oõÿ õà„>ì=ÄÞÛ¿ÂÅ|¹ž×#OL ˜À¶Ãø;¯¸ípr"<¾OPxÁ›€š,ªj? IDÄ™í(BLr@4x—ß’±.U&†•cWàpD€Pn‰”Ð×C`ÂUBP0&Þ±V /{€Äû°¡x=¦Lâ8DOlJ4‰Ià H9‰Âˆ&I²K€2¸Œè¦X5Á4 ξ¡&µµ úl^4} éûHzŽƒ³œ¡F?Úž4¨ù¡e deÛ ’J¬@ÿYÄÈÒYc † ¸Ì E&!Ì F!.TâÚøo=è5U@€›:ÈÁŒ­-à5ßBF4?ö›àøbˆ»¹Ox“ˆUbú\Á˜Ç$¥ŠšpÓxjn37;·–½Újk–„ç{Í(–w&-ƒ ;™&ˆjÒ“ÂûwjЀîaBA\á€Ãß2?3eâÏ«Æ÷CŠCÜã¹ÅGžq“‡‚Ä0Ÿä^HÀŒ¢u¯¿Œ-´4Jä0àÈx) ¸9ÖD½þÚé,\™)¡À•Зhj`SLDÍ#)‰îB»ÎÙ³.5Wª7®´ÿ™l †!b°€[ExŒøÎð†Gh`€BŸ¤€A7½Ó’;³›{Æ{\òû0‡g¥È/™­T}˜@E>@q±i±’§|BNÕ‰Uh>œwüç#ÿ÷VX¾ô…8}ê=yôòö"à.T„k¼Ä(?ŸA¿s Áà0H € #˜$¥UQêÀÒ °V=e~’lAÜà69èn¿U¿_`‰KK3©6 LÑýúvˆ›¢›7ÒzÀM €w¢wR#V˜VH3+Þä(…gxׄxt¥x@ œ`‚§E XHƒ 3–‡ ü2 qÿqÔ§0*©Eh–0‚X‚è!¨‚!Ø‚Ëõ‚‚·> 12ÒЂÄ’º·,0@´ðöb±.ë²K² ”«ñ/}ã •³/>öPˆRˆ#8À t…î+0 “¼Ó'¯(¯æ?Pw ³;Ë?fÁohUvÇr‚ƒ€E…°MÞ5RC+U¤§Ó !G  â1p‡7FSDE‹§x?‘Z§ƒˆ”r Aƒðy@à `o™h‰HDå‰>Å$ ¤ˆ‰˜€Š›¸Š„ЊO#бhŠƒF Ð#„@„1f "’$ÄH\H0«5%>2ÿ`1 ”cÈFÊH$‡‘ °lEB,U(B«Áp-–lG‚|[’ràB Pn"c?dnUö Îñ†P‡¦hsôˆ1 ãG‹Dy)o>p66c )‹¶"5f3>1áagÙFÇ“¨*”Pqâ5ƒ€Ø·^àSŸ“³$¹#é5ú4Y'i{%±’ ‘/Ya1aE•’6IWŒ9PŒÊB\„´ K¶@ JðI°´€=€,À ¤ñ Ô/"ð/Y¢9¢TBefØ¡§S•xƒ•Üb|Yu °·g"A Ae$À;ùÇÿ6Ñ9^vr¹]ôè5Å‘68ùh¨ &˜ RS“’)ø‰„H ·(`ÐÓV<³(°w™€“éS˜ ;!A "zWMW”9ˆ†š´âS"Ó ­9€£YM²yš£R›«‰›¤)Ôç¿ ¢„Çw 3 4 >°ÐéJp7;XM»Yš³ù›ªy› 2œÙÙ›d‘šRœßYM ö Ó”@ø8CÅ%8H ²¤Ibi2³p?Ÿ4ü‰‚‘#PÀ!JДÂX8z  ±#·€$Hÿ þ) ¶'€M´ "àÿ¡#ÀK4Š8"p# ðGd0’ {…,ðç¢U‚1ú 3Š-ú#pÄ#*RIóŸ{"-°p?€¤1ðú¡õö@¢& 1(ª¢,j£0:£.F£-ú¢8ê¥;Z£>zøé#7à{Júz²¢œ‡£È"ÀÐDA` 4@ @,XE½d ` ÈÈ…j73r¡½$U5b†³¨²Àb•j AÏy"ðP72²¤9¡Jp ¶0ÃhlC.Gù8>>p"3‚# ïI Å„ÿy«>@|ñ UE® «²ª«¯:  žËÙžÀqÿ Ð)Ÿà „öIù)KEä H@sÑ0\Ø*-SÉ­ºj¤Rã #z 9ÀŸÐyÕÒ6ÊÊžÔÙ¬ðiœó)­ð@­¢a­;Èອ¬Ô Þê¯D®¬d¬;XS$¶à.ZDЪ;±Ÿ* Ò)81¢«Kz,1"°:@±ê{„ !»¤$Û·@söã'lq”±ê±1àž;ÈC>0¬A ¡ ð` ´¡;Œº*9  ÐbªÃ¥¤  °¸j¤·±´¸ ¯Z˜Ã! 2µÆxµ£ñbƹƒÅ%Œ²ÀžÍùœ#®Z²T : °p MIÂ8Û:lËžokÿŒÝ0·`ðù8DÀƒ†`ŒöÊž3J ÀµÇùµÊ)¶Î f»ƒhëµH°¶Âh·>·1 ·J( •ë¶—‹·AëƒKiœ{µ²¢ûž¿QBàø .K& 2‚R4 1òš$Ó¢®1¶‹8»Êb"P6#€I²°»Š³ 1¢Bðy¡*„ ³P HkªÑr«·Àª|! B Ÿ­ª ÛJ£¸»€ œZ¾9à6"@-› Þ ¾±*¿É4BI”3b”Íš”ÃÅ”Ÿ´;X MY ‚û¿¥ŠŒ ‚‹,б¿†Y+Œ+BÀ¡+¤ò›ø{bú‹”Ί”ù:ÿº *À’»À>èªK™À&ÜÀwQ8aÅõ}¿„JÐÂ*# :#Õ¤°;cûë7;±Å«,^;>C R<ø1»öÓ²0Ã8À30±Ãø8ð @X.vëO ¿´ë#8à{[3°°·[›#¿À.Pºk¨p,ÇÊ‚©Uº“¤£$2³¤1#¾èÇ»à‹ÐŒW¬¿¾ÀÁÊ8”C€¡Ðò ÄbŒC±9ð°Í»,F¹A1-!ãÇ)¿MËA;çµì¹ †á¢„,´`›ŒÆ˜È¬L\4ÐÈ<8˜,É”lÉ´É· ÉN#nº±Axw1Œ!Ì´Ê›ËIºH2ÿ®8›$˜LÛ¶±ÏÍй †à¾JRÌÌtbð‰ Ì+¿z Eø6W[„Bp„¾p»¥”ÅÍ*K¸€I¿¡, ¸FŒÏE²¾g\&$@s»`Ä@HϾѢ, üƒAˆÉD„5°Îf'ðÃ}q…%@È:*¡1€ÑH²Ñ½ú¼A‡ê³×lÊܰ¥ÌAŸ”ÐÐrÂç,„ ÑìLÑ ­ÑýÌÑ"M"àË5P¾£±"¤Ið¹¬[û·0  ÁÐ?êú§C©®c 3cêšÉ7ËâÃ+ëGÆ’ ¿Ð "À§Xý§1àÃ@ -Œ•›ŒìéÿÊW½”ŒìÈÍ.¤l¤$¯Ð#í¬ÐiÀÁß××È@„!À¦\œD ^K[é{hlÆ™¤«¢D\Ÿô¹A[«D"ÚÂ8Œ“ŒÎÆH1F¿ ÏÀ¡·°=I²T%’}|±PÙ—ÙbÌÙ1âÙhlÚzÛ°´·¥ýI§mÜW;#“lÔ£QÕù¹EP KQM%½à©/­Ü„]¹/Ö„ H@áýÀAë”xq6¡ÅÝ0„;X¹r««ÁÁœ^Ûªpý¿óŠŒ ¥ ”­„}”$-î„ð¾™t"ôžô,/0„Ï;Ü}ñ ¥[ֵػÿQµÊž,à×k”#Þ·ð|1‚õz­NyÒ£M ò‚ q7?‚áx¡ám[½&.âÙâ¦z¶}Û´Æxâ¥\Ê&쬂K¸*¸€ .ã’Ÿ•ѵ€¬À²tÛ™ëÆJð²Ô¨Ê”\îÉRIU>åô ÊJI9ä‹ë¬\ÃÉLÖ©¶YrÕ¿t ¥7Dð ýJðQ}Ž¡ð°;Ñn"ŸŒA\z®®ÞÀúCò©³@øŒÒ@˼û+8²ºá=Ú™þžmûé2ŽÅ¾`äìƒÐY0ÒÚÉ\¬žÁ 铮閾,˜.ã¡ú8DKÄ?êºîë¤NÊ;ØÏ<ûI°Jÿ < ³Ÿ„ RR7'kZ;#40kޤ¿§°¥Š ؾ ›¤%¢?Úämmí#j + ¯êùž'"éq3¯p³Óâ¢[=ì*ŸÝ¿t H{,íªË‚,Ýàª/ÑÐRSÆâ6Ñ}”ÝðÖ œ:8¥Ž­W|à¤ä¨Êíò Är­]žñFùñý  ŸÄ¿×] 1ÀâÏ Ÿ£Ñò¼>ñÏoð"ªHɯD¸®%ïØ?ò>Ÿ¯@ß·  »ƒEз{¹Ä% ØÁ`&%#D {JJÄCø8›óI&ÚlÎÚõ4þ{ÄtØ/&êéL»1ÀôIÊ6¿¿ô:ÏÿñýLòùÁ±ÀE ÓÆºW{·Æ:ñ€bT¿W%à¬òéµ,Ÿºì~µ30#Ç" œ«Æfï40-Cplú^°¹!J˜©ï Ÿ« ³ì;x¨ ¨¬À:;¬¨o¶í ù’7ÉЩ—¯ ™¯ œ¿¿¤O Ï¹‡:ú\"ü¦ÿzkùõ‰×ñÍõKÕ…:¥»øéÕÖ†:¹¯j >2\‡ «'ŸZ„/ º"د,øiœ'2.0 éQ»³—ìA{Åœ®»ì)²ÆKŸº…°B9>1#H90HH/9‚>"1ˆ110$1 >‡9D09‡>›D H>Ž9Ÿ‘0>ÿ©>J>¯=¬ƒ¯HB±Ÿ@H8‡4©°DH1CH$н9¿9Áø¦­Š¦@BŠ­>®Š=C±±Ùƒ1¥§ƒ©Ãˆ­ƒ°²´±J·¹H»ÉËÍÄÆÈ¾À>Â1¸¡1±êS5"!±C €€#‡V1Høp’#ÆE ÒDZV!D9UòdŽ©HP„ ˆËMÚØ1‡Š‘ e Ú*Q4sé†yòhÊ\-%å8䢤ױuÑVä°Šë§ ¥H"ÇÅnHz4í†Ö1pD¢0f®pŸL4 ÉoŸ FÛK!Ô®å¹JÕ Zƒ¡®©º!-ïà”ÿ½;·néåÕ¨ðU¨¿ñ"¼÷°jÅåÞµ¶ëeÆ 7L ÁÁ¤‰ˆÊ=);LòO#â ÙÐu×lÉq,oŽ`Œp¸õqÂ=昱 y$¬Ò)qò$‘T…Dà0”ƒTJ“Ýò5ÔEMàù0RxÍ€y9DÉD"€ — ŒP!B(±‹€f¤‰Ið SÜ\±Á%—2_ù2& %È9CLXC.¤hÉŠ‘ IüEJiJÄÕÊ.ú…Ò#mXVcb:"‚¨2'~4£*S¹£ŒþPi#RH¼èÃc>Äèâ{0ä6‚u1lÀ€}61&“ÿ%ËÄT‘%ðÅÒC^#ŒàIO>Üi o2QB\4Åçg@ÅI!à ‚€0`|•ä—šÚUßóýöSš#X@BŒ V*ÔBD¦ŠâI,«Â  Á9ÁJ1yåèXu01I@üùT ÜHRGBˆtI 'Xª]\Ùp€‚)‚ à´8T{­h°Áú3*2É3ŸQÛ0þ2‘°“8Æì±É¦²¬?ÎÂíXÑy‹-XÛökÖ·‘XsÌ“¦X"ÕgÃ4éO"ØD@ž6ùðK@D  H û"(:qÉÇå´bŸ¤pM4 "hLH(DˆÀàg—ø°Ôÿ°t¦c õ_\ÑðYse2Z3$C°0Lˆâw‰Y |,Yuð@Í`‹°%!„pP5EªmƒpbÝvÛ×@öED÷Ü'×÷É-ÇÒM8…­Ô Tɹ`[b„Çà‚8`×,v$d› Iri['2šnKš·Ütsn÷æŸóÍ).À…Ây7’b*è¦íO54°Àh‚ .¸4öŠF¹K"Ä/§45‰\¿ÔT¤"X2Qr%Çï¹#DX2«JÂC3ÖÈTŠô󯶡ڽ‡>Hša5×4Ã|ç7UþM2•ª=ÝŸ|Tøœ¼ ¡)jÉ$ž°NE@ÿÁÿ¥,‘ “ö:B«ð%ðF"[Ø:ÆUDœC$ø(È;"!’èÏ&ü»„ÿ†UÀ ,t ÎÈÀæDÌ$>ða¥hhÝšRX€ž*Ÿ^ô™QÂK °s‘$I!ë6¹I `‰Aµ ôQ@°€F¥Yáó"³Bðhå •hE‹†£|"U„c‚ò'HègKño$¡¼¶Ñp›È£âÞ°€"“ÓNv(!Dj'R0àu@°Èõ ê'+£D˜7`²RÈQ]*hÐ"k(b HXB~£5ý(a•Yû€!YEVÄ’ŽÜÎnÔFÉ[6Ò“ÿ6å&ñÓŠ|²ŠÃD”j`ÊT¦²G¤°€"¹›aÒœ$-ôs£Aà –â‹BB‰“œ?ùßž„Æøà›çÐ^¤8C"&ÑóO"%ŠÂÕš¥í“5ÕÉËb&Á‚¬ ´g*#+u#S²¡H T@((E¢v¶‚‹ž¥'‹¢Ð;ͳ #sR;™dŠPà‘a hhÃIàÐ=0;„ r± DÚB¦l2"ŒÎs£éhOSfÒŽŽô¢D )JÓÁŒxh#PhH°i“ˆ\bªÓ ¦f¤lÉÃ]Ý3Ø•" p¬çòdí:ò=]ô«]KW`@€„U\záÿÑiÓ~ £Çpg4ìQŒ$ÒÀl5KýÛ” «"É¢ >…ÆY¹¢"h$új”31HPzÂZ-9ÊS¤^‚µ\…G1ÔŠ Ã›¤3êT—ŸMfˆMõÁ l+‚Í Ê¥älhZ‰Ö®6e¨u«N‡zÚÖ®â~»FI¾š&@„ ˆT5spÝòœ‰=p‰H ñŽ„zBÀU$FÀé W½/1¡v,‡òš¤)ãIx»H‘;©Ã³€î;þ¡‹´cÁSK2jA “°¢j1Z‡ÀR¸Ã5&¹Ï$âÄw¨[1° ?é Š ‚ÄÑ“H‰¬¶â'ezb1¤ÿ¦eBIár'¸/ÂLb«²h¦Ð€UÌÞA„ Œ8˜%.1Šk¹bü̘“0¾ñ“_|‰[¡áǸ Ù4@šìrÀ<“Š”K.ÒaΙ%\0€lÒ ¸]›Ï1Å¡T„ŽM3uÐL‚Ð@}É  h5Kø`ìTÂ}„0ƒ!$B# ¨SºqŠÐ0f‡Áþf‘Œ¤ƒƒ •I ³x:¹Æ‘b J¸µm"ÍÔ³J ­™À¥¾Qâ³*º ’˜Y»TW /ILC$Ó'ˆ¤Ÿ…œê­–5¬ õjMVä¶îu® k‘âú×èö­‰Šåe!«*6­óÿAq`Ý*‹‡€øä'¡ú4ø‹îµ!à[ß¹##´X€mŠY‡Þxâ“0¢næBÒ_Ù<Ð"„;Aü”ÆE»j®© ˆë[L-¼‰\BØH].$#a¡"Ïš6ÌSsL–¡4a1OÞ¶œ?kç*V¼CìuÝ&1ÉÖ.‰L¤Æ|zÌk~óÿùüÖ/©g>ž =^¹¢ÊKO®;‚y·oD8¬d‰eÖ5´÷ël™0¹LžŽá[ƒÌ‡€A«hLhаTG"†Å‚\èeöÏA ¸ÄAörEú„Ÿ[šeD¸Æ> ˆFÿ$€Ô‹Ùô4¤Þ±S Í“£'K+[®¯¥íùU€„ 9HBVÓ!%,a1ÀÀé]ÔÕk‡ù…€=Íeÿ,Ú{r×ÅĽt}WO7Ö {Vð“H-à- ÐZÏoÅàa¤xîß9Bä¸iYQ¢côw4Ô gd²èÃ,À3=šRMiIq# HãÒÛSµÀ•yŸ`495±°oÕ³F­÷¸r=°oPá=À© m³:9…bÀ dd†î¦3AP4 #P µ°Mó‚ã `uh©{‚‚Áô(qøD¦0 Ð84ñ(0Û7,Ž˜"õ§3F„&‡¤޵3"tt¸rbGˆÉãXŠ þ€š€40‘’„‹×L’0‹ÚÒ6³0pyš”‹¿(2Æ’D7â @°4±P±1ZFŸàc¯ø:N„‹|c‹ò0ºh½¸‹À@ÂxytB_UtjÃ0M àtžÄàY0ðJÝ0,çDB€iæb#P°QH ÿ>—·Ïrgô JÀŒâ(")f¡zŽÒy1°&p±¡2*È"70,p Ÿ²ÛQ-1¯ö)¥bùyµ0‡‚€9&Ç%à e‘ ( “_#¼"A68¹9sý ÐŽ‘´ìʼn À41;‰”¬ÂH©”#™’]€ +a ^r•ÖðÉ1²=©“fá”>Ù@‰–C©>¬R*P)RÉ”e‰“G —K)’4O§CvÃEžõnè·l˜'ÜÑNäs’ ‰€<`Y)xŠð˜3I5°–'A‡VŠ™ï` 7£<:š1L$€+rEH£ÿ éÀaEs4I“ ®F£šuјŒàv•‚ š€*ž ¢°6“t“G…A’€Há…áXAzx KAû€œwEÌÙl±°["ÅYFñ€ ¸ˉœ8Ðù%X„Ôù%h'ŒhH;ç¨-ë—Ue9€™s¥9ï• ƒïТÁ \1 lô6&(2¦^z‚0«F#Øã‚È ã“GÙâG’Þ3oàó¡ã# €Áµ² da_ j¡F-j 1#‡V‰ƒZ©p£'yäÕœ–¤’×NQJ•Š]a ÂPï D¥"@d 0ÿ£„g£‘ñ£:* {’>Jy‘—b y^”Ø"i²`Ÿúi °a6ñ¦ P˜ÂVsRyóF$Ã` ;º¡@7¿A´:¥§mF8q/`t$Ž*púrFp´F/â€W©V‚©ˆZ\0@‚0 eãVáS$8a4â3—à8Ðp‚•7ƒ gqˆqH!ÐÕqÇ«91JHº#¨¤JIàWNðJ¿ÇŒxw²«š'«¹Šqµ£'­'ˆq»jw£«•¶qádoå7IÚ²uÊKçš®‘r ½óoîúiãÒ @$(Ð*ÀY¯‡0O`1·"ÿ’¯ûÆTùv Ð$õ” ‹O_¡Oƒ¡y†áO[ û%ÿD8ªÒâA‚˜ Žv L)3.Öva4•€±²2Ц ì43` ï4«¿*r8»ê á¯3C5ô„#p-s,A;´ á2-&Kh)«3ŒÖ²4ð²1«8@³97[³€3ìæ÷ihvéÚeæ—®“4"¹sw„0 1³Â5q‹X'‘ûzBˆw“ð7teW„ÑfäÈ{•~u­}Õ€µ #מ!/XXÆVa!“±aÔ#ˆþeì `ð » $€`Æÿ ¬Ñ`2‘`ª«y ¶"8€²µ©3)‘ =@[¹[AE !À¹ýå ë »í` &vº®»`à ­›ºÎ rä€à0âp†qI_[7ûs~yó™p!¢9p!Éqš k Ep1`Å àãD8P›íÛÇ8›ó{44~_ƒˆ`3ˆÀ53Àÿ˸æð\Ñ#{¸¢ûüË ¹a –¤Ð:0;ï¶&`Û:ïÖ¥)gˆ]ïv~­³ÄÙ Œ†h1I ?OaQ ?‘Ós2 IaBP3"2"e½@LOC\37‡Ã—»ˆF2\PKqp5SÿèqŸà¿×ÀULĘ{Ø æ&U”2t‹¨ðQ hè 8B³‹Ù(5)*=ò(7Ð(é‘’fõ‚L)m&¹Qõ5ð¿ÀÌÈ5ƒÈŠÜÈqOãòvxñ {a~A „z¡"›\$07Œ®Ù, ¶bK6 ªÌ¨œÁ¦l• ñh6Ñ@¤ó ¿Ñº%a3ž1+úÿSK¼ú‰èR¬Ð.Ìϼ1%J@Ì"3ÌÎÌ«J`³—öP\24{gr=·OU БâaÆìlCç–= €ˆ@Œ¸B‰“0ÿ‰•X3Åt‰çûšÈ“TøÖqʇ¸ÈŠÜ8‰Œ¼Ð ]3‚»$Ag ë„#°‡p ÒÈ&b‹]ÙeÙå0³3;pš—*,¶Ä¹þª-!§'=Ò"\7³ÃÂV7¾‘^É r§9 @…¥À6ÝT_r1 0ÔŒ!+K? Ôó‘"+àÓ10ÔKQÔG&ðQ rA+õ<” 3+‰Ð`1ÁNJáX +Š0hûjjÍ&׊%y$ ÖÖVMÆ9ž ¸ÀðQ'¥‡iÆFØ¢h¼ ƒ º ÉìБüÈ Ñ€ ýÈ ÀDÃ'F.•%  F—0ddÿ9Sä‘­C7%-;èZ7ð˜~Ü:é§N`Á>´&ö1U ÀÂïÖŽ Ž¥6—‘@jÛ­YMe&YdSxáz#Ú…Ç`Œ9 Èí :ªÌ=šôÜ=S Èr½¤OÛ¬ç‘Þç;IC !O±8gB%æ ôÍ1ëÝ#Ò4ZxBð¥ÔlÜŒW'ÓÐfd¤“÷Ë=ì0œ¬Ù°(› ‘ŒÑèêÈš=‹R§ˆ˜_e)9FK Nw9…”]íH7²†e£Â¿Í `¾}  ¥,¤0Ÿ± àÚüeu:ʤP6ÓØsãinGàA,6›6‘ôš+xM Ì<öò〘1Qx»Ëê.FåÞî*Yîgª5-ÎÃû_¡û£[`¦‹ºa`Ó˺f`QÊdS7£Ü§ @Œé Ñ-é‘<ã 0ñÐØ:E@ÇÕ\U 0.Ò ÿâ!,2¢ÿn!L7)ÜuÃò¼Aœhˆðö!;ßãÙ•]ðÆ%¯v¼ó¤;ÀŸ}t÷‹Wi&ÑÄ •6*a#x'üæ2£2ý®VüWƒ,äP!sæ2;×ê¸àï'[ (ºÈ·0°åã(þìþF3þi*¹ŸÀw%¸›Ü¨1X‡ >$CH$H9ˆ>>9HJ>DH1„#  šš    ¤¦ª¨«£ 0"10011” ¹»0#09 ¿0"  $® šÍÏЙÕÏÏ! Û1!ÎÕå0Õ!1ÛßääÎÙïÍä¾ÿ8¾Œ19½8>üåáF°d#"ªÁË?!…r(ÉÁÐaˆ$ùÈÑ®€Æ48"!ˆ…>@pd°²j „ÄX¤ Hd*ª© ¼pôr(Bç"EŒ„ˆa4Òœ"rÎŒ‘iƒˆmÉòå áQV~\¢1D%#Šbà;ÑGÙE6ù8T ²M ªÁ ‘€À+R«ˆJ JA¬À·DYXÐÃHc¼?Ρ ‡J6„ ‡nAµd™Cl®ÄrÃ;r >Ñ@SëË œÅ^Z›él•DoPc¶gÊú ñ¨²Ë6ŒsQŽˆ.ddtDB8çÿgœpáÈA(Ú<ºgÛ¦=§%±( §‹Ø#*Þ—ø‹BF ½ˆÈf‘Ëû wÖðŶÉ&,\ÄO JÅ‘‚ÆéÒ[ 'èƒ ò QƒN5äàB{ eXCJ™pÀ@k%j2 ¸Ä)¥„€ · ’¦œ Èb rÅôÖ‹x»™ d1 [lÚXÀâ2©Ibž$€Ìl ž&¦M9e%ЇÀ7W¾ˆÉze%mÜõÃfmöBYYÓS0„À@ø'U'BBHôùg 9ŒÀ[trÂé&@tªe'#ì¢g¡3øtÂB°çÞ1W/”zSÿJe§‹ ÷‚MÆ Qi¬¨Òʈ~”irÐ’ T÷ ƒûK\tü‚$1T§_F±9ˆ$ËFâl%äTÒŒ!Àä•É+©ÐèÙÐb˜,ž¹BÊbÁ i/õ–à„ UŸ.6ÂqÚð‹ ¥AÏ-  AŸróŒÕl e;&ÞÆ¶ l¶N<dG?iåà@ S–½Å˃ 8e“~Âù „zÎ3ƒ‹ˆ0rp& ?(Ç 2†‹´b‘¹ame«N%,ð;&Qó.%ŠÆk®ô‰ ”Kiž)Å* ŠU,]£È×@|U©#­¸›R4˜” \xÿF ‹Ç;–w° x cäˆF>ÓÄ!Ã%íø”ç™ÙtiS€ôL$(ã@ânuâH’A)ííT5p`É@ê‹Î¤¢ AE[uÞPBlÛ'^ôÉY¢ÁºÞMc3<”eËÁ ¨&3uu¨nYM«q¦hdb(ZS$Eû1JŒrvr™/œS6™íK{G%˜£™€å²*ñøÔ²ä®[€ù†»°üî’Ì8FîÊ¥Kj±,`^윔ŽÀjÆW´+ž'šÇ—L„€›d9 Çj2²[ìïâan‘c²Õ°€²#Gl´‚pD$ŠÑ‹Ó±à'ÿ&„UçOiù›Ng 8G'dý”,ð@J./^J@løú \E1a,0&yÆhÀ!@ªžÊTÇà2\ñ©qdÖIdÚ†X©a›°„0âp(6ž1„»ð¥':`:;9LÞ‚Ñ‘J´ˆŠ¨×>ñrÐêè4QøB”hÈ ¨ íõx(üxÐŽ·³”†°Fhî ¶0Ö¼pM4XJpÁLŠÜ13ÈŠœÿd '*‰Q mÍ úd]0aoˆ—¼ÜRF«•6!åOéÈ’!è9YŠÈ“œêŽ k0ø@‰ê8%Ñ´KpW§DqãpŽ®©0Ô{·ö:íO\z‡·h¬ÏpyCvu¡©cp¤ÉÛäe‰±NÏÁ¬Æ%-Úù6¦‘.ÆQ¢pÅŽWueFyÊðB@CÁÚe†ùKÕJÊÃßo·ÃleÇYº€´õQÖL`âž`­XtâœþX0oÉ’}ââ”S£‰€.OËGòÅc±ˆ Áø\BŠ|Ðÿ|ƒAÌÝ¥nmªf…$ƒèf³´¨ñ`”7 Jùœìª| aý–°ßVîG7$ ~ïw%A² 0w<ñ@›Ñ˜  dÆÄkAÉp%Ép –RdŒf¿(mRâÐÃ"@ÑÐPj߯õ‚AÕ¡Z;ÐAvñPaÅ%¹ó#wq\‚¡ww8FN~QNk5cœ²{ýÐK Ã>@r•À;Þ GŸa‹Á,Á[ˆ…,`Øb ˜†ºÃ6ÇpÕ@©Ñ ¹Dâ<³qЀláC’é3>åÕ”G&DJ'T‡À J°ÿ¤³lÄ?§?ëó‡k/@ùð:yt0ß“ tHÚ@G55h?£EQƒBð1GÂF3¢è§2®k¢&`µ (a-SazÎñ¦'[ñòoúD'ž! €6'WÀ$µt ˆ pP²ã „9b ¦ Õa‰°V² éñ0N2%§!;ëÈŽçëè3„è8FçÑ)ò"•÷F#Gç¡#'Z]Å”ˆ„BÁP0p,P!áàB0‘>°4på ’‰‘ZsH!3!#ÄP ©pNöð Þ@c´#.y[jqÿ=?Ã;B2/ñ=GQoˆ ŠC"¢f JÃcBN` 29vkà '$!ر JAÉ‘2él+’ !tÏ 9õÄÖP"â0„Nåu[ir3—ƒ#éŒa‘P²4ãƒPsÉ»/yQ—ý„—}Y01—"ðî@Ïp8®qya%Ƙ PŒHŒy²Ô"²!•‚ ó&8ç+zÁž`"­Q Ë  ~ üi0Ç•|šYfç+Ûõz,Â.Óµ…[zÌAž©çŠÒ4L 'š¢~#'ÎD[£ù¡—! B—ÍG0ü³1,ò?˜1‘=Ј @Ú8Ð2$0X3!¾@™¬âȰ8é&¡B²%Ç¥ úð ,:U0PUîÖ»`2¿Å0`nx‘kËé0Z"¡)’0í@(}Çe‚‡ñ`„Q È6_À0p%Ñ\™`L,Ò%ãÿ)&’nƒªqÅ{â ŠÊ%M  Úä6Å–;•é %‡5L¦dFæ ˈˆ0 Ú'p¡t#T~òù!ªŠpd_ÚKGö=tÙ¶ ,@O•rÀªa8a<ˆ?aª!4M!«ß£&)† c…2Î`>0å“^<6¦ö^ÕA2£9_ÚúNšt¾± ¸7 À “Vð ¹S çH5  °„µ@²ÄŠÇ³{€Y%‚ ›õ«UÁäTXpS¨&XVg én—ôAÍç »× 8÷ÛW •à C m«™mÖÆQA$©$6aUHðÿ’Ð,ìfDeÕV¦˜¡Z=UF ¡ÈuIÅÆ-š°íñH!9@» 8÷1‘0ƒý±,ºs â I(•ñ ¿º 4=|ö7sd'hRqžùTÌÀO©–<ô ¦bR÷)„Ñt¦á¶\Y Í`ßÄ{7 "ð×à ´Ó cYc!`W\yX–@W«a‰K"fb;‹[à±!; cÛ0–:Hîb'81ô"viw iˆ@œ"*< +¶Â‚7»2Šâ6v¨» §K• Hl)–]¶eÄ«e£¢*§’*ýñ@ɰ¤c*4¸2+¹k+Ês UA‚ÒWÿl(;¢y2£GA|󅃔¼±Ù1p¦ÐP8ö:ÉÀ8¢á˜‚ak+&”0!ñ 0w’|NR kNž‘.±ã’úbk4—kñÐ0@#µhâA¸Ö¨žÁ– l¹ÑY¬}o%}±.ÀFºíC[g`2ÒQË´€² Ñ(%q#âSs6ah3‘ÃÍu &ƒöÇS-L.a‡5”åV¨è¦O…ê2O5s15Ie5¶ ÿrkc6`c¯ÊU¥»f‹Çè1û1QP¸y3Ñ'5¥• P’ “gn¸cæÀ™KO”º èB¼{e¨UÁ@ À–‚ÿ9 ÌÁ üÁ %©–EìÁÁ°Á küÀ!ll£Q²sÂsìk£v*uˆt×,˜yâÁ@ÔÕÁŒäÍr¯ËwÃŒhÊ[uˆQgõsÚ¬UÌÐ ‘b»€7 !%cBÄ r"z0´Q2SÊ5–p)¤QަBŽ58;@à !@C¤ ``²œôø#–¥ž-çìœaDl¥“&v ‹±V@œS%ýd–=ç<™@#8ç$Dw ]µ<"t]t„[µ•`W›d·•w A÷VÊ3 ŸÉ‘\‘Ù‘IÆŒPT(|³Ä¯ôÿ…ÂҪ£Ò'€y Hs²³Vö`´CM !¤¤GQÔˆB?IQüÀ %S(•@ ÷ú†ºÔ#hr Œë®ÎÒ§¢ {³ÍÂØN²"š'ÇC¯ÉED(¶¸õ›#˜Z;‡sË0rÉ •ü¯S}Îp}ÐgNåiÁ°|#ØØšL}€½ ~-´KÄ– /ÝÇì`kù¥H HúA?ƒ6¦Ù›$éS~6TP£$*[¨½·Ú¥83P G²tã@Ñ-¦eXÚ°òq4t×J (©¹J‰°Nˆ@ý@•ì6E¦°L´‡;p Âÿn£é •" ÛÝ ½1êpÍ€C¦a‚ü`›pÁìk›#%ƃ å}%áà9·€ÈµS†ƒ hˆ-Π/ƒ-uµ#ÿÝ%›.[(”n…ðúáuH‡³1Ñ“K ‚¢¬r«þb“(ŽC[>â“]ùPƒDÚ] !.g»`½GÇ&¶4 (Ygƒk `r* °5±ê d 'ËFÅ£”/LäàÏ;¢ž™6W·ÅS‹NÁ|¬ÁãKã®®QD•%H˜1 ²5¼;iˆiÎ\'%:É0gŠ.P>Ÿ^‚ŽÚK›нrí®×{ váåëÿ‰ žÈŒO XUv ±F¾"›.r.‰Õé2„á¡Ö£0 äê™>&Mˆ~¡4µê¨®4"«êêÕ}(b²M,Bë[REt®À­0Ÿ²4­@Ô0ìZd©¾Eu6Ÿ-—Ç.ìbî ­€ì¡€9¬°#»DÆÎ`¦Ùyº¯0B YZ:Îð:_®9HQK“/8aˆ>ÐæðñIŠÐæÏ5@Æígª œ>ŸÎs%Œ›µuh]뫚ds7/D2Yr}5À˼à’î˱±QŸùÞ±  Šq#´€ àôét…Õ_N-×Ï òÈnyÿ f앜ýe" `ÁAØ }á"#7óù¬ó©¡ëP®{HIWápÞ‘ ,Ç ÅLú!`ÚÈk«%”0~€â’]¸¯‹câ é„‹!€‘ýé¢{c²£N{£þÁÌÑœtâö °“×'8j“ƒ¦fkCªâ¬÷n €¡ìâ©S­Ÿ‘±ØŠ.šÄIeÐø ’?îV£&«œ7C)D$’~ t”..‰„";Ô@W"ûéýúñ®°®P.µÐÏ´°ò³ßúZ߯uVžàÕ°Då@Ì·DçvÀÀæ ëõ¹¯« ².Y.ËÏižá9¨Ð)§ ô)ÿ²ÞÞ Ciì ì/¥àíÑß)š³r¡õ˜# ²Ð)U¦1׬‘ލ•/Ÿ”Ë€Pã4Õ¡cÍ>>C>9…(11"   ” !™Ž1 0 ŽŽš™! 01 Œ¤ ¨Œ¢¥§1+5ªª0¼ 1¼©#¾ž#Ÿ¿0#›Ê! ’Ð Õ × ÔÔ ÞàÕ ÔÖÝÑÐéíÜÙîðõ÷õ õûøöÿøúXbh <Ø`Ạ²Yˆ`k,X¸È`ãÆ æu³Â¾Š ³A:·Z¶4jT©1R´m0»%xÿt)F Dtj´‰…£ -G)CJÆ9ÄQ£F!0ˆ@•JõjT$C¤"Ý$ŠC¤…Ò2h°¨ˆ 8Œ¸Díˆ à4.¸‹iĶy­ñ Wß^pQ Cf ˜\UÄZ–©¸³hhçí\œm7Éå@þ&M×1mµ /#:T‡ZíÙsÛ>·\¹Ö:wNübÂyÕ2jŒi‘ùH圻33%l‰“®±â6i/m»ÁQˆEŒdšE*Ó"¥— ñŠ C9HÐqß‘BHrWg €†Yn €j¦ @N¤$N…Ñ#“´„ÿ8B4À+!—æä` ªüµ@G$YÚŠzÉ”<•ø3[Z8îÖ Zi=4›B6á(Ž=Ò3 mÐ` ’•#ÞAKêvPEê8 B9Û`£pÕD„Í$ i9Q5í”ÙerÌh451zéÎ6@€E `‚L¬GŽf „×Ô¹©mpÉ]-ZRˆ. ¨#>(qŸT Ø'•HHU'öŰU§-Õnj­7Š(‹ÄW& Ôùˆª+r€ž£qiè( Õj|2 äªxÉ ™0zãâ6ÁQsÏDê¨PΖx=‚i7êìX­‘SêO4NÞ¶ÛÿBÜäVQAÔTR‰5e”a$±XæD.n–б÷f´§>&)GNZDÀ«-– Í–iM ÀAŽ^²†>B’¡c±EM|ï1€Wö’Â2ÉB…²!öå_(ŠbÙ3Qêd 3·ÖÕÀ­¢æÍ;"N;3p,®DFR\ŸAuŒ£H”W 3¿4pìN®ú²^ך(™#‘8AÔ< 3`QÃ|‚„¶7“ ™kjND¥·P– @D•Óœ$W ß’PiMÄ)}¤-plvã «.9qæ@(±ª“GMKd™Óœæp°-v¸fL{Gä°aàP±¬Óu1ÿ¬ŽPµc5ŒK&’‘/Ub“€;ŽRF¥É±œ(36À‚½°ª‘“ò‘)$áç¿d¢¡ØD‰}€‡˜€‡#\÷YÕ©b´k£•+D5ŠhåQÀ yN¬‡(Ép•&‚q*f0À—`ÊR™€c5•æ"ÛÚHj 2…— è\6~s ÿ•b"ôÀF޶‘ù mæÀ<Úq v¤UMŠ$t¶¾yG6dó¨‡ 8ºñtN8Rß\‰‹ÎK#ÜÜÞN Ë|fU¬¾:,pT“$riˆC%¸ #ȦL–©·ÆU †Pò@! 3ž A©£%v‚1oòSPÿüjQÒVù3U|´X½y SdB‰&΂ðŒ¯Ä¬IÞ@ºCµ$"Ÿ!Î$<„k°éNÕ±ÎqôM£ÀêEO’­—lK6šq× "BN¦¤]ÅÌ”*™7i¨í\ 0ᔢào!‘°.$pÔ€´ ˜Hˆ¦˜¤ÿKª>\"&è2PGs_©ÒD–îÒ$à>žâ^d_üæ€?ÿE ÆäÈYb&¨[`"NTÈ”¸°å3²*TZб$Êz©Ù5 =B¬難7ݹ¾v¯%Jš“ )¶†q«µ ËǼ€s§€±©LojH5ÙÎ4ƒòhªÚ*I®ÔÊÒ^òåx‚›IŠô©sK!·^'"¹†íîM¸1'ú2!!Ȳý¶ú¡vN'ȱâ«^òÄšŒp0A¬¹€àµ/J "Ìrpç)Â)5f㉫³D•€Ü‚)\ÒĈò¤FÍ‹ÂD›XMQLHŽsvÄÞ— ‘­¢ÿÚt/ÉŠ÷%OQ9ñÍ>¦“Öòi$^öa5J‚/á‹nБŽ>n Ó¯J²G e˜¢¡º‡¬ØK1à,G¹‡mF‘éîàÖ&BÍäfm>ŒNtÀ#Õ­VÒËÄ ï†û¦B„D%è„™‚PDɪ½ÒÛ•†€t®HHBl{ÿwе'VaHCfÂ&ài˜‰=!khèzI!Ì«ØÉ½Ä—Uð=1F@b˜jÆKŒDÅÌáƒ6,~µ6*‘¥•¶c$´¾&AŽ—ÏÚ$±HkMåŽæÅ ¡Ç”¡ËX—µªÃ%gẦ~Tr!JR8É ÷7Õ‘ÿItD'tsàÉAE†H°'“~nƒ¾ci,b!Ë“Çfâ¿>¨A\o ¼j%qkÓç fx1ya•’àº5‰‘‚±`…„ ÈJ&³vÄ#PëBÈ’Šc­h}Òk€"•¬sP ]ÞˆmüƬkHDYQk9Ϧ&õHÌÉ'¿’‘xQëÁÉ‘ÞÑ(¥¾Žû)p†p gòh¶kDˆ(`b$ö&glШ„ÙIðÞÜ‘‘hÃÜ×ËxŘóÂ9ꟸôôÊî /Š V«":• "ü'eB矷±ÍMøQŽ%vpׯ'œzyÄ €7MÕTØä Ú„=Þôÿ*·â 1ðx“! ‡c>ü‡=^Sh¸ß7’€AYò *A×1â ¯’sÄeJÑe+"\+Rd·§BÝpƒ{q·$…à ˆ³6Óð!‚Ûvç…„;HfªÅTóÂ}ó€QÀ×cDæ0_†#}ÒF3i„OŒUG6† €÷= %Ei¨×ðSžzíò,är„Írzp2MGD BDÊÓ‡8DêÕD›ð|¥C€<¡²UÃ|‡"ƒô'xÝ‚C\|R+õBN27EbÕ¹qQo2-_åI=ØsÛ u@’„9 l:Ôs8fdûÀ6 ÿ„û•“:Jq4UDK“+nôxPmÅ$KÐ)[¡W¢Æ¤V§F,©& Ö±|´7„¥*óÿ:Csøô1ÝCGø+±¢¿  q锋P30hË`OÑI¹(AÎTSšÄ8(À%jB@ã0ÃD¦¥p™TâðC‘zÒ•R.¡1J³|?ƒ^ö»˜AÕAÃh…yc¤”˜Ü°˜Lè˜d²0 îÅ81.±BaÞU,Žå=/aönÖ“+L¡4ýõ_ÿ•)0ƒˆ"`ä¡ |…*ï–b½ÆbéÂbU&i€"(­S(‡ÂnÄbPâY P ¼ñV q òÓhF¶]£uZ˜+è‹•+…‚…I59„Åhm!q‚Ùñ ¨„‘òpÿCú²6 =6d¥ÁTÀèŸ!B@$¥88"ÒÐJI@[a1å°µ7ÑEY…¢B{|”!™Ä!"š1"àegÿNP<f,£ÀYž…=›ZqŸî¦…tÓ‡ Hæê± ñ2 (¤v8„Ä \j}E|¥MðWQOsv‚š-i¾ø•°}ÂxeÅ“9¡5øOžä&iÁ‰·÷Šj Bêp†R9À•nj\¢‰À‰ ¢gŠÓ(H" ÷âc[¢%i1ÝQnjaq}Á1¡:<£»ÈõÖ2p•ÿÁœïñœ!Q™@QZZMuÿS¸k›:d$uµÔ?ÑG @ `²’5ƒ”‡3Ï©žqá+{Qð„<ót¦Ðv„°„lšAJY9¡‰ZP'R˜I:d07¤B=in(r"pa²"õ´-Û:ÅD #èžÑSA¯/x5418*I{BZkdRòúb®P ä²Z ° DÁ ¦ã”w9@w˜2ˆ«‚ô>Ÿ¦²ˆ´‹” ޤCE#ä%5Ð0p`*«B£WªÔ‰ê¢Î÷$9±},·kÖ§¾˜C^ËQ:ÿ!/ž´"€ó{)šs»µUçv‚>V‹@ÙЈ9G%Ì¡±ç"³g!A#‘m;bJÀyÜÇyæÕmÚ¦JC뇘`»M 𸠙RDÀîsžëÓ>‘ȱ¡¦ôc?À…?øº?ʃQ ´³ò5 «±²[ œk´à;»1’ÈWÂb*ɳ< Ñ65¤«îòtRçb H¢*D#Àx{4’ƒzp\É»6­v¯·”% ‘—©¸â"ÈAágkµ s8×$®&Àa00A¡»(”`!'Ñq¬¹Xi þ›£Àÿ/P[á#€ÿlY³5QÚ–EbS$‡¦Bg“hëPrAñƒiâ`ª…!àeH"0.BjZ£ž ¦²ç³ à 7”Æ¥H/·šêŒÔ†AÖ6Þ±¤ÛæÝF\åIq¦¨Š À-Ž °t\.QC  Š‡{5Ÿ™è[kÑ}Ï!ºÆ%*T¯T°¡:ØVŒÍA.Ù¹x¤Ã*‡¹ÌPÇ  ËÙ ÃR,V“’Ͳ,\â ÏÂ#ÑÒ-Ó%K¢qa ËbqŒiðx{²ñ±&f\¬I»Éw ÃÂ…L*¥ÑtZÒ“õK%R¦p:§ÆAb–.‚‰¶%˜ÿQ-Wn#él6¸ZbUñ µZ;¨QšÝW›-bcd²&Èr<BYFd:çoò‹¡”WHh¬:SÒ‹ZØt¼5ŒHÆ0ÊRRá 1M}+À ÜxÐ(òë-‰Á JK# 9Ã[œÄH¹LB‘(m¸ C.ÄBrsQéFÔ›ªdðº3.U¢1 â.á$DB `Ú/gk‹`à‘;@«™tn·Å@Ë1õà(;unç…Aü9»ÉBƒžù5Bà”O‘2’r_9pæ ˆRÿ,, E£v­0À&fjŒ+ÐSx*©Bæ[Æ“;hùÉ£! ƒOxpÕšaãM“Q,ÔîG$ÖbK‹#Á9µ”9Æåî96È9êN$àà{ {¯³7Ì”CĦç çßZƒ ¡’ s²$P‚3á6ÛA«äTÜ'Ùµ.“Wß ãŸAfzƒ¹‹Ì!êX«¤u¥³Št>¢-¬&ÏßROØ+ ïB^áò¿1'ϸMlÅVFÝ„³Ï9b!6[¦bZ*6}8Åb×Ù‹Âøž‰âÍïwã)ªê1 0w9-“†-³)Wïÿ)˜B2˜B$†ãYîáÁDÚGJ>IêÃè©jckp¢0Éî¤Tj*w¼ýïáhÛÏ”ï)8 õ&£Nðæp%8p‰`íó ó‚ > À&sZ‘†’rEBH>DB>9†>>‚>E‡9ÿ9D‰D9HJH@‡B‚‘‰8‡Š‹>Ÿˆ‡‚5H ‹H00  11!!°²´¶¸º±³µ·! B Ñ ÖËÍÑÍ Ð  á   ÌÚÆî ßÜßßÕýÐÓ<³–à´1@À±……°ÁDY+ư˜1#Ûô%° ^½j$£Y irÉeÌБP+,XmX·¡£Îu ˆø@±âˆ1Fˆ€Aˆ0r<å°é£¤8ª$ÊE>j8ò#&°bÁÐJé‘Ö¯©Æ–U7l¨µ>„Ì*‹„ëÿ\½O/!D)†ˆB‡œ.ˆç†OÃ1Ž‘ƒÃ È1p,n<Á‚ V6Ó÷€µ Ó”5s&Z ÄÁgC¼iÔÜ1ÛM A¾wËJ2Â5kÏJ?“v ´ãO‚˜+¡SêÖ+ÃP(}ûôŒ î=ó;¶g ”#Àà¸À{AÍ!àÀ ¢ìРІÁÆÆ²IÔ€ÜSeBPfÈUP%èÈUB¶à#Hl"H_f¹5Ä%[¹ÅU%`UR!†*¢!‡m¡B!*!®Ò ‰h¹X"b† ˆ«€PU85° í¸}þéXÑv±ÄÖ<PÚ4%!ð^I5©ÿžÝ³“ÒLH+©ÏKЬTÍHÍ$°$A@C@ϼIe?ôF ’ ˆP i°Ý{ö‰$6ݤ~¼ ÍxRJéæÐ%@ÒÔã[ ,¹-씳Ҧ=mj“m † ÈQ¶AÉ&> j RªªJD_…ZM|`Ø,Å«-”Ù2 "Œƒ$\0 ·úª+Eßáªë«Âk,²Ê¾°•a5’ „!Áí)— !î´ÂµL7‚H)ÄÀ Ò‘ï,ë¶ Â»9Œ4‚=i²»@›ã•cŒL2‰æNEÑ 7€+…¦L4ƨf>*e³7¸Ó@ܸ‰œÿ“¨G2A—ZcÿÅNm¹\ODM5 F¬dzÏ΂¤à ‚òE0â*¡¸(´À´ì’¿².zMÇ^•‘—¾øõ 胤‰éôq±"îf8‹Òl3€ÐÀÆC€^lšÑ 'Y 9ìV2ŽØ\ê‡_\Òr¬¼K5±= h,šá˜c%:úÈ©&eôæ8ÚKÔ4U>îÝ£ Ï1Æ÷è™|äy¸  *»q~²PGE ˆB2•¤›·¶ an³*‹X|0MÖ`¤4‘ÿ\ƒ„•B€A%DyˆRŠè•µT%+]yJMÈ)Ëä1„`(rˆ¨d&2#‚ õ`#8 –‚øå$š YJóþ$MjÆÏ­»âßѱ <Ç4øÐ7–aƒ„ù©9æyg8Ã4ÆÈ†=‡F¨ŽUÃ4úXŽL(Õ(äý“ K¡¡Bò”êp„† …Ê:BŸáÆ4jÚÏ~–2‚5éLw¬Æš°ˆ#6¬È;z› èè È,Èr%”å+@Ä&+”J <@²fà-b‚=¥„ ~ŠÂ¤&NíK&ÁB¯|ò– zêß–8ºõ…«cQµÿò Ħ{²À~ÒfVsÔ`‘!8TΚÖý4Ã6ɘ¢q‰2äxFvʨŒ IÊŒÀN’ íFPz’€Ž]1K=Ù€Mgc:H Ò! Ïq ©&;mÇ:ÓJhR¤é4t´°`EFÖA<ðÃ8ûDŸHžC15¹Ç7MÂ--à×uÄ&ꛤKá6œÍìA^6™žÚR=ÈS)‰Ámb7*ÂNÛ2""dw»=EBuqÝï¢pUâ5/ˆ¼r®^h+™ðWéf.RÚ„-Óqš(3þX€;YÌvô›”÷é#cÚGs#¾ðR%­!£“ œ¦p„ÇQ•2Ó<ÿaŒÿžÜD7ÇyNIädñ0ø4!ØU¨’6À€Gßa > MóHÀÝ^2ºgÛ!þ‹"é¥úQ’Óø¦;aôháÒ$÷Î¥ ¸e⃨L·-Ø… †0$€ LD T¥Sž¾ ¹$¯Î TL`hÌcQ3›É]90¨?­¤-°LÌ © º&” °Œ€l˜(‚¸bð)Ètñ³ŸB\šF7ÃÑ+`G~LÆã@Š#ÁèЖ£ŒÖ•jÒ(õHB´m4‰ÆhÀEï¿l°†+ë(2NÑxM2­Òʨô& éç™¸¸©&r3îÿѶÝG‘ëAÅŽ’D±ž)F  m–vÚ‹¡ ØÀ&W.u© CBeW¸e)ºD|½5 ®è»–‚0×½ûm‰£.ßþ6x}7ä¸ &«JhøQ”À"N¼ ‚Emþ˸X!ŽF=ŽËB~ uú,üŽ81EvírÔzß¼LÅpŽ\žÙxŠä)¯Š=ø1CÜ?õ>AÍÙæ5ÀzEož8ˆÒÛ¨‡j))ld ‘”³c<ÍÅŽÜŒr”d4qˆ>=õ“ûÔlëHS !Ë!•B¯: ½¯ö!³ b.gIE)c–º¤E¦~'<]Ð2x²-ÿw™%0—€T/KþF>}A# #¤VyÀÐ@B˜7×úhfe…hM?zqÔÂz±al{™’2>öÆØùIP3Eúø†tèÝ,Œ±(#³í…â¾¼ KXâkxÒ½jì)•YÞò%rlàŤ5ÖCÞ7‹šÉOÓ–Fƒ­ï™öì¾|Ï(@£EêÙÁÁW¹éÜ è6EÞ¥,0!€Æ,IáÀ²5Ha#S†"ô!9¸"!Rpvg"”Pø!×JÑ¡s“L"h (²8Š`#=EŒö>/f¦V/Ö3eRh×èT} ðEÔ}<ˆÿ[áaAˆ! ó_c²(öäa…;Ñ ïaHPB}A Ö¦¥@‚4ÆÔ9Á90c.¶Z­Hüð ³å‘XÉQA1òó X²£ò>)ç¡…5RR¡Æ¢iýcˆc‰ªp!}q 2+Ep  HEpdA‰$r‰W5H°‰‰ã‰œ TƒH9`ÔÅ ®(6ŠP.ç"6” \Ò±|p(Ý@Z0(14ËõD [4°‡Ä3v²´31P}ÀAÒh F#s>ibë°HÆ Zð#Hn¢…|Åa$a'¸€ÿ'z”ã'‡¢ û( E2IAÁ=ÌÑ“ÖãµU…•â'Ñ(C ÌCt͸v*q“â‘Àâä4,yCCÒ+ 0-9,Å’Ör,ɲ,7Á} ,q#’ÆÒ’%‰-Ëbh9p‰@q“ JGa“J0ÐU"„“9MMC’ qWC- Q” qfeö²£¡~U°¶³(¼¡;•¡c;¢“;ÕÀ:¹0¬ i¹¶ ¸õçXs°2-s3?{Á22ÓX’ÔÅ@µQ{´G¥áG³ö—ăÿ°Q½ƒ„ ÒPò,’° ÿSßGZHA©•ŸP6”qoã*9 =p@qSštÛ€6{a6¢8rsšÆ%8«É*³¢"@àw¹¹›ö6 .¤i¹hƒ6±N ÛQRSAWÈ鬇¤–‚ÛÐ; æCC;+A% \40+q{gGžÐS3lôb+¥> `ã[ð  â°–ñÓ3;ƒ¨ÜI¥Y;êÊ.@ë¡k •Ê™¶à«{ÿˆQ¡7R]D@ÞU@œ«^]1‘šË]Ÿë¹éuŠàe"ÁòI¨`yô†y£° Ʀ }Mæz•ÁRû¢ Uq 1pM·«¹›kDð@d)¥CF_”>01NA;Ç+DL½åmR)áyp ±gnº·é fR b@r¾lõbÞÁœêkZ6öë`µ;ó;}»X[èô=ÙDÿ„¿?4¦–R<‘.¥’h ±bSö*„b,’ȪÄH@g·"8pÁ…0gn6ÁhVg¬Ák†f?ÕJÀ’8*R'V­”šˆ qÖã¤ó urœj¥©P±’#à Bàÿ.QH²f¼š•Óö@ DÏðT©åT|A¼ÄðNó+bË2›²5À&ea¦ö¢°ï³Å–Å ?#ð,`O×]¨ZðQ#¸Êk‰’EµeN®Õ訇ûÇe¥ýÐ’„/z¡Z‘2…ˆÂr‘Á²™Ž9©„o Ò Wš‡›“LL’𠘜o›lq­x‘ "³RÊ*"^“tS9õ~.¥œNY€å·wu(œ’C|Å èÄmREì=…4ÆÅÜÖËÆ<¶ ËÂJŽéÀœÌ.꨿÷=6WM,ðtO‡ Í\áa €ŸÅ`ÿZhŠ„tõšåâŠQ1ÇÍ–’¿_‡ Ôçñ›d¹ÐF(•!ûQÀ¢2EÐyR3P‰xu!‹÷xq¡~÷Ðí­xg];Yx„¦“5I#ÆuƒhJ龡5;޶&  €¢ÁÜ(Wbgâ¯/ñmÔÅFOÔö &!Ó(ï;! Å2Î,UÅ1Îm‰)$qQ€\mü5OC~o·IšM³¨C†[t’ á¨õldE¦Y"¦ Pâ ÓîpÓJð×P}½C‰š9ù ƒ /ÉŠ)†P‚ ‚ÈÿØè]Ù8D@‚»‰JïµÙºéÙš½I°°œ+¶û´‹ÉÀ%Z4!àÚ¹féH…Ú=\hZÕøR$”acÞ±¾ªõGïÁ•?ø÷c—#eFÝS…o‚m±$î ‚`\°R+ÈÅ C€5i†ß¯â!2 }¶J¦8A— ªˆp>Š~à N‚â5[áàH5ášG!­_žÚu˜ð§½½ U~PQ'¸ ¯MÃ(n øaDâä n'¾Ó HºsÿŽüÐãCÄUIH¸v[â\ \j©äWÔ;¾Ñ py'@ûŽ‚’'öˆ„òî €†IÞqªQ\i°`Þ†ôG]§õ kBÐæ÷ ‰2f4ßq®5cf±t,’¶À¢h‰W•‰§È‰Žsá™|#•8 nq¨Ø‰„Þ£0³Š“L+9*˜¢”î|@¼ˆ¢ ’áÖ³éû‘%É1T>²•É“Õû8E¢%áéußvsža ;‡Oº±YîɦL¦®Å‘N*3-#¹3È®—JZ38:qfGÔîEXR>qM%Ú^=ï0W´Wõ“üó ûƒÿ?ÜeîäŽî.â‘ “# ï×r’/’4±’Ô’%)ï&™-Ò‚în— _ _!·JqîVNÂ+=LP'.õ ñs( ï..eê…m¶~¯ž­~®“Ó²¾b÷ ëšÅ5Ãë{ùëðì|™:"±<®'=\T.5=«a Àõð§ [óÄmưƑ&B”æj½,±YKÍ€¡ÈåØ˜Tõ·47WÑ«šäš Y“£™š¦‰š¶I8­ù™~ö²Yš´)ö·Ù‚íelñ“ˆ-÷aTu¿8BÀ^›ÌÉ+ÞÁâdH(ê²s~¿bÃ÷;'A¥IF†ŠO‹ÿŽý`&mBb‹ÿ$ùÅq Îcð£±Çò˜BAòÐFqt§sGN’§¢±§wô=4úÐ?d¸FlÔ°%RÖи¯Ç¸E|gÂÜ9<» ›ôØ=°¢0ð-hšÐ“#šf2J£#¢¢#À¢"ô¢$J«3Š¢‹†£î¢”àLp¼)"¢þìïþÿî_Ö †ò/Zï3*#   ˆ  # ‹ ”Œ‰ž ‘›‹§‰’”–ŠŒ0 ··!¹B  ¾ƒ²¦ "01 ¹0#¹10Í#ÎÿÑÓÕ„… š›Œ¤ ™íç®”êì ”Šøøô øŠêû—׈9fäˆAD$hø€!d†„4HÀHH"ÆD!0”ø‰$‡“%}¸ÈQÃG "&¶|idI!#}¤DiråL˜>dºj3ÎN„ŒÈaÒ$‘H•6õñô¤“!#NR-ÔΡ¯…DâÀâÖ·®ƒÆžÒTÏ,Y:ǃJ´}×% ÜrÀ«·¾r/ÕåÀ€Ð‚ ³å8›%(ãyAaIÍž=“‚šµ¥Gs‹6–Cˆpäæ˜§¨_eŠ#€W{ߥv¥ÿ>µCD›ÝðÛÈ€À!Ó¦@(aZr”GsôÀ‰$’œ'‘ˆ®¤ä÷ðåsœ§žÄy¨â‘7?ýzõíA)ÿ"> "ßéÇŸx0 ` þ‡Äkhb–k¢„PÈ"|3Ø0¹pvèᇠ†ØaB”hâ‰(¦¨¢‰ Œàâ‹0Æ(ãŒ/Â8æ¨ãŽ< ³Ù€°AÃà" 4 ÅlÂ0ÄE€ p(–\ÐÀ PÀ Z6@À ‡5"‹œ’UrIgh 9ý°Œ¼%(£Ì…À„Ùe(]ºÁPD*éÿ¤”Vj饘fªé¦œRJ "„0¨"„*‚$¤:„C0B©C„  Œº*‰"„#k¼2Pk©³Ž*¯$ø*²ºÚ¢›v¹) ¡¯ñjj8¢ˆ@h͆jc£„&f—(„ì%Øv{-€Ã $Bº† Â!_Q)a‚aX2`ä{ /É·ù ËÀù’i§"t›[#ØSŸwÉ¥‰ŠÀȵ[—Œ°9Š& Úí¡t*óÌ4×<3¡Ž ¬)¼ ê«&Š0*ªB§J‚¨#¨ëÄVðZ®¯½Ø€¬¯N ,7¶Ø%ÖÕVÀ@ÿ¸|cØS»é+¯v¡ûîeä` ·â Kè×o?ëq0Ì‹å0ú‚c°“ÞTxŒ²p»¢4ü…ïÍhð¸›|IÌ8–„âÙœö$ €[•D’H]Àµ#€‘q›²æ2˜3 ì¬Ûlûí¸wJÄ«ªJ«Ð«º(´‹¨º:´±ºJb®– j¸Ì'ýޝ†JuņÚ"Ã6ëÜÚw´âLM€µáظË×`Ë•¾·ì¿.¶ På”ã°À88ÄÌW†^\³û)_¢XA°% „ka,˜€&ðM L¾ü„%¹dàž«(^˜À´#ÿ§#@pSÈ¥e»q cWÏ €M"³DîvÈÃ~Š;CÕ¨\Eª !HA‰Z5Dä%mgBû•¬¦U«Z€¨€‹@5Eì…`Yæ{ÍÙ|!d½+‹7ÊÕÙΧ6Q8¬KÏj”Ãäv€òÁ±KåºW( ŽHh…€ÿ&|m cöÆB ÌæH ˜€W€Âm@Û@%cw5NNí°‘`D|ìtŠ@lÑ9AÉîmo‰ÝPö–²Éd(ë¡.w)³Ý¥jh)Þ/SU¼Q‹C˜Õ­ŽX*ªU`j¤šbõ„'«0Ï{ʺb¯¦V°åJ\eÿ›šPÇcyï]ïŠ#Éǰ¹‘n¢_ú0¯Ç% F*œc7ˆ4¹Æ+0(R@ðpdŒ#Ä4€`æJ!ø`0I Pàëx¡4Q›Óq²uaå°¶c‰ÌËb˜2[Š,c~’Ý ^Úô¦•"]…éTg¬RŠRF5*"´êTËdÚ9&*¥ MyÒ3Ñj•͈""‡¨¾˜F®–ÏWUC–¶¾ËramŽé“_³µôAnBV-gÊÇ‚ÇÉÎ@é]„  ×)Nñ¨Yª@˜À‘ÄÄGjÒøâqº:Ï ÿ;þUÀJÀŸa çRK¹` –zQ‡J(Xâôµ7Õi2Ø*!5YG«@ñŒ'À‹…þÕI ,Ü–ð‚6øœYGà–LF³UD* S”l. i눔ڕi /¶ÄâJçΨ@Õi²˜xc ÑyB(Bm­‡*¤)ÅX\ãðª(Ü£þ¸EY½QÒ®¥Fº‰YWeÔõ]­ªu\òs˜ür6¹N4wñ†rb€å¯T‰HaÆÅ”Z9*]°¡ !¾V9 äs¾\º…›Œ“‰HtÉ„’)+ÛQR¬0„¡HÔ"2fCÛ#e*å e²·DF–¸Íˆ EPñTDlÞÁ…¶3ÿgC<¢¬™«(Þ¸™Â啨¼ç[« ­EutSÁù5ó…ͪÌ%ÛÔÔ×­bóQÅf[v׿F(`øFú Ž$áËI­Ò½êE @à Œ‰,˜.É€äw‚ F¸°´P4Ef¥tE~„ˆ —Ô„#ÝG`8j YŽŒP"kíR{CÒfø-{/‡Æ;¬ôLg)Õf‹ªÚ Á{)3“–*$ÊÚkÌûφ®gŠQ›_,•µZ»” zÞ'¡†Ü¥tκ—y2ÚÜi6:&‘ñ"`³9#ˆc÷#̼ 3ãd/¦˜™XÚÀBÁñÐb†}\wg¹5Ywœÿ´K•‹[0ÜöÖ2ߢLIÛo¹[_SŸÚ¢R€7܃ ˆ¿Î~‰«4& š»ºÞr…†ñ¿7sYåû¢7Éè+«ŠóÔA¸ÒgUù¹Ï\1'?v“Nñ¤&úD 7G8|ãp^•=' /&€_Œæ/ % 0Kÿ’(,Ä>Àæ'ø°u~Q {v îÖus" î–9‚R(ý–C¥E}.s}8h)F5+ ·;EÃ*<Å;Í#y*žF"¿3,4f5ÂÂ<º5^ƒU.kûWrWD6»Â=ÜC-HÔ-š÷]vÓdòÃ,Ø2.ël’ñ8I6IBW¶I"H¼°.1Pÿ # {£—• àHpćP S ã³q%s‚–.¤§3h—À.(6$ã-'ãJµÔp—ƒ )»ÓB…„§"4¢R´*{ÇE­¨I?‰Q.'GØò àˆe?WW†p °_P‚/×(…C+p‰U8™¡B ³w$¡`H,”1ã ‡(ÕD‚1–Yc—‚w†‚ª´g °Qò–,Ä|2xKÉωƒ»óSB°c˜X‘LCã*:åp³R+7"q³hŒ§‚5I+T#x@ó˜_´=Iƒ5.#-܃…áp~ú‡-r³]Ï-oS†ŒM&eL8P S”ë…sçµÿ?Ã0gò&Å&“ ¥% µ¡ …@ ¹—AyBRþH¢Ãq:õ ¥d':’¶(ªD}¢õöÐ%±ÓZg÷–7z}ŸRL¶"k%r~çi¯rž=C„¸2MÉ¢U."§«6æ‘©¸,ÑòEÞsYC.¶(\ƒx•g5§™iÃdö'7Ïè6ë%yù"H\†”|³7·&Ä‚ uå&N·¡¡Hë2 WÂ>¢pA´0‚Ë'Ðqzrò(ä2´Äh±S(Î÷2~3Ÿ*wÑ9D@t#É*Ì#Mw>CT?scçq7˜£Â=#PˆwNÓ²\êyÿ6+· ¦² !0•Wr¾Bt³6¸ùkÜ6$€“p¤“–8›ql̆ziqeê¥@m–ó·w$lfHá¢mÞöz£c›• ê&–ìð‚W²¢¡JêÀ¯³# *%ZŒÖœ’¡o°§S@8Z44«2~.ƶ¥çbN¥MhMTã*î·c 3Is*;æE]šFáPGõ—3‡/[V‡™UgG‰A6ÁÈ™[CVÈr¥» ¦™z­@§lüä.ßà†˜¤“ˆ“ ÐtiâmÿâtÐ{ Ã@;'5:u:°B´Y`¸ #}oQaj'Z.Ôÿvo‘‡&¶qª@”1P"<(DW*F\…W:v LS*7&M¿ú½É…«Üä(W6åskZu]Ñ‚-ÄŠE€.nsä…Ÿr$^`VH‡4G’©g ó/õ¦ *$ˆ ¥mWf•´Er ô©´JÅ×´¨´œ(e|6}0a¢•2Y[K·Ä%Y˼cÛ*ªÈT$V?äbn›LBÓ3€9*¦ˆ‹Ye,¼E;¶·„ixL*+°FNhs-ÑÃ+Žg,nä5ÍÂ]leLÆ=V¹ò[.NÆdÝÒO÷P•ƒmhb :{Å­WæmÀ/@P àGpQ•Åÿ$îH‡”`(µ{RB%X—•©z";-Ê%‚ìJ‹r­¥ &pFµÃ â¶x9EGƒ=;“CPMU7R]Ï#L¨‹½¸=õËËXô5Ý”#\ºžßÄrkíÿ-Ì7gj]ÏbÏ–1›‘7N˳Q¾Bä¼`$p´Gk¬µÎmG @òWÐ0ŒÁn¶Bn2rÓgŠðDòB¦È3¨R1¸s­c£¤ [d+T§òC„g·Ãƒ½[4<±2T³X+É=9£]Õ¸ë\YcUóùy\3>½‚²õ‰ÜÅ६ßsÿ@CW”Ûø¦DI,”Ó€r 37Ç%#½0aâ/زp„$hvä9ã!‚ ä9ô6©@(‹0‚-EK‰–ŒpÔsÿ à/R<ÁcpÛy*G”DKtb!ÀÏÔã<{cÉ8…Y´ÿE7yW´¾çDFµ:Œgä4ÏU+0¬ö.ò;7"î“‚K?g”.ƒðz߈eôD8s˜ è/~ýÐføF^36âÐ0ø‚&«E …Æ"…|vâˆ"Jr2J~%VCýöê› ø£ï¼å¼¤S,Db<ßYLBD[¹à#àÉì5DeUÞë½7*ÝT,I³ÊèkFÚ³ áıVSyþÄrt²uÃ>÷×]nƒ-Äü€ ˆ$C/"Ý6’9I‰&g‚Ó=®2€À0 `»0Í_3!D'ïê1’ˆÆÑ-ðÑß°ÞrùÝZ›Za¶ŽS=Rñ¯#ð™añ¿ñï“Åýñ ò"?òÞ"‡~“ vøUòÇàÜuº.®' 2#nÄ+ð/¶wA‹8lñ9r’”8'“†Óa'‰à¯i鉲v"C­“¼µ>ñ=;zangband/lib/script/tk/image/unchecked.gif0000644000000000000000000000007210250356274017535 0ustar rootrootGIF89a , €ÿÿÿŒË öÒ‰Qj¡Î˜oÛŸ’ãdR;zangband/lib/script/tk/image/wis.gif0000644000000000000000000000214410250356274016410 0ustar rootrootGIF89a÷"3"333"33333"""333DUfw3f3f33fDUfwf33f3f3ff3ffDUfwf3f3f33fff3fffff3DDDUUUfffwwwˆ™ª»3™3™33™ÌÝîÿ3Ì3ÿ3Ì33Ì33ÿf™3f™fÌfÿ3fÌf™f3™fÌf3Ìf3ÿff™ff̈™™3ª3™3™3™f3™fÌÝÌ3î3Ì3Ì33ÿ3ÿf3Ìff™f™3f™ffÌfÌ3fÿ3fÌf™™3™™™Ì™ÿ3™Ì3™ÿ3Ì™3ÿ™ÌÌÌÿÿÿ3ÌÌ3Ìÿf™™f™Ìf™ÿfÌ™fÿ™fÌÌfÌÿfÿÌfÿÿˆ™™3™3™33ª»™f™3f™f™f3™ffÌÝÌ3Ì3Ì33îÿÿ3ÿ3ÿ33ÌfÌ3fÿfÿ3fÌfÌf3ÿfÿf3Ìffÿff™™™3™™Ì™ÿ™3Ì™f™™fÌÌ™Ì3™ÿ3™ÌÌÌ3ÿÌf™ÿf™ÌfÌÌfÿ™™™™3™™f™Ì™Ì3™ÿ™ÿ3™Ìf™ÿf̙̙3ÿ™ÿ™3Ì™fÿ™fÌÌÌÌ3ÌÿÌÿ3ÿÌÿÌ3ÿÿÿÿ3ÌÌfÌÿfÿÌfÿÿfˆˆˆ™™™ªªª»»»™™Ì™Ì™™ÿ™™ÌÌ™Ìÿ™ÿÌ™ÿÿÌ™™ÿ™™Ì™Ìÿ™Ìÿ™ÿÌÌ™Ìÿ™ÿÌ™ÿÿ™ÌÌÌÝÝÝÌÌÿÌÿÌÌÿÿÿÌÌÿÿÌîîîÿÿÿÿÿÿ,`H° Áƒ H¨0€C†#F4(±âD#ÉÒhq!Ʋ:>42¤ä ‘%S–œµ¢…DTšäÕ’E€+`Ê2‹—ƒ,XàÔ)sÖ ˜A‡î,©&Ñ¥P£J-!þåThis file was created by Graphic Workshop for Windows 1.1y from Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA This image may have been created by a party other than Alchemy Mindworks Inc. Use no hooks;zangband/lib/script/tk/image/angbandtk.ico0000644000000000000000000000566610250356274017560 0ustar rootroot è& ¨( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ00 <ðÎÀ00 ì ì° 0ÎÀΰ 0ì;³¿û0 ðˆ°0ñ0°‡ð‘ 0ˆp™‡»ð ‘‡p÷Ÿ ‘w° ð™€÷»»¿ ‹w »»ð‘€ð»»¹¿»»»»¹»ðð »»™»¿°»»»w»ðñ™»¹»°w»¸™›»»€pp™™»›»·{¸€™» »wx€€€™»»»·wx€ »»°wwx€›¹›»»wwx€™™™wwwx€™x€€øÿÿðÿÿðyžð0 ü?þ€ÿàÿÿ ÿþü ?ü?þÿ€ÿÿÿÿÿþþþþü?ü?øðàÄ#Œ1œ9ü?ü?ü?üø?üÿÿ?( @€€€€€€€€€ÀÀÀÀÜÀðʦ>]|›ºÙð$$ÿHHÿllÿÿ´´ÿ>](|2›<ºFÙUð$mÿH…ÿlÿµÿ´Íÿ*>?]T|i›~º“Ùªð$¶ÿHÂÿlÎÿÚÿ´æÿ>>]]||››ººÙÙðð$ÿÿHÿÿlÿÿÿÿ´ÿÿ>*]?|T›iº~Ù“ðª$ÿ¶HÿÂlÿÎÿÚ´ÿæ>]|(›2º<ÙFðU$ÿmHÿ…lÿÿµ´ÿÍ>]|›ºÙð$ÿ$HÿHlÿlÿ´ÿ´>](|2›<ºFÙUðmÿ$…ÿHÿlµÿÍÿ´*>?]T|i›~º“Ùªð¶ÿ$ÂÿHÎÿlÚÿæÿ´>>]]||››ººÙÙððÿÿ$ÿÿHÿÿlÿÿÿÿ´>*]?|T›iº~Ù“ðªÿ¶$ÿÂHÿÎlÿÚÿæ´>]|(›2º<ÙFðUÿm$ÿ…HÿlÿµÿÍ´>]|›ºÙðÿ$$ÿHHÿllÿÿ´´>]|(›2º<ÙFðUÿ$mÿH…ÿlÿµÿ´Í>*]?|T›iº~Ù“ðªÿ$¶ÿHÂÿlÎÿÚÿ´æ>>]]||››ººÙÙððÿ$ÿÿHÿÿlÿÿÿÿ´ÿ*>?]T|i›~º“Ùªð¶$ÿÂHÿÎlÿÚÿæ´ÿ>](|2›<ºFÙUðm$ÿ…HÿlÿµÿÍ´ÿ,,,999EEERRR___lllxxx………’’’ŸŸŸ«««¸¸¸ÅÅÅÒÒÒÞÞÞëëëøøøðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿ2222û2‘‘2ûŠ“‘2222‘“ŠŠ“‘2ûû2‘“ŠŠ“ûû“Šûûûÿÿûûÿòòÿû22òÿòÿ222ûðòòÿÿû2îðòîðûûÿÿìîðòÿòÿìîðÿûûÿêÿò3ûûûûÿ êûÿòòûûûûûÿ êÿ3ûûûûûÿ ÿ3ûûûûûûû3ûûÿÿ3ûûûûûûûÿÿûûûûûûû3òòûûìÿô 3û3ûûûòòûûûìêôò ûû3ò3ûìêòð ûûûûûûòò3ûìêðî ûûûû3òòðîìêîì 3ûûûûûûòòðîìêì ûûûûû3òòòðîìê ûû3ûûûûòòòðîìê ùòòòòòðîìê ðîìê êøÿÿðÿÿðyžð0 ü?þ€ÿàÿÿ ÿþü ?ü?þÿ€ÿÿÿÿÿþþþþþü?øðàÄ#Œ1œ9ü?ü?ü?üø?üÿÿ?zangband/lib/script/tk/image/makefile.zb0000644000000000000000000000024210250356274017226 0ustar rootrootsubdir = ./lib/script/tk/image/ ## makefile.zb srcfiles += lib/script/tk/image/makefile.zb files += lib/script/tk/image/*.gif lib/script/tk/image/angbandtk.ico zangband/lib/script/tk/library/0000755000000000000000000000000010250356274015500 5ustar rootrootzangband/lib/script/tk/library/balloon.tcl0000644000000000000000000001276410250356274017644 0ustar rootroot# File: balloon.tcl # Purpose: Balloon help/tool tip # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSBalloon { variable Priv set Priv(first) 0 set Priv(showing) 0 set Priv(win) .balloon set Priv(delay) 0 set Priv(delayId) {} bind BalloonBindTag { set NSBalloon::Priv(showing) 0 set NSBalloon::Priv(first) 1 if {!$NSBalloon::Priv(delay)} { set NSBalloon::Priv(delay) $NSBalloon::Priv(delay,%W) } set NSBalloon::Priv(id) \ [after $NSBalloon::Priv(delay) \ {NSBalloon::Balloon %W}] } bind BalloonBindTag { set NSBalloon::Priv(first) 0 NSBalloon::Kill %W } bind BalloonBindTag { set NSBalloon::Priv(first) 0 NSBalloon::Kill %W } bind BalloonBindTag { if {$NSBalloon::Priv(showing) == 0} { after cancel $NSBalloon::Priv(id) set NSBalloon::Priv(id) \ [after $NSBalloon::Priv(delay) {NSBalloon::Balloon %W}] } } # namespace eval NSBalloon } # NSBalloon::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSBalloon::InitModule {} { variable Priv set Priv(win) .balloon catch {destroy $Priv(win)} toplevel $Priv(win) -background black wm overrideredirect $Priv(win) 1 wm withdraw $Priv(win) label $Priv(win).label \ -relief flat \ -background $::SystemInfoBackground -foreground black \ -padx 2 -pady 0 -anchor w pack $Priv(win).label -side left -padx 1 -pady 1 if {[Platform unix]} { $Priv(win) configure -cursor left_ptr } return } proc NSBalloon::Show {text x y {anchor nw}} { variable Priv $Priv(win).label configure -text $text if 1 { set width [winfo reqwidth $Priv(win).label] incr width 2 } else { wm geometry $Priv(win) +[winfo screenwidth .]+0 update idletasks set width [winfo reqwidth $Priv(win)] } switch -- $anchor { n { set x [expr {$x - $width / 2}] } } # Not too far left if {$x < 0} {set x 0} # Not too far right set screenWidth [winfo screenwidth $Priv(win)] if {$x + $width > $screenWidth } { set x [expr {$screenWidth - $width}] } wm geometry $Priv(win) +$x+$y update idletasks # Display and raise, don't change the focus wm deiconify $Priv(win) if {[Platform unix]} { raise $Priv(win) } # update return } proc NSBalloon::Hide {} { variable Priv wm withdraw $Priv(win) return } proc NSBalloon::Set {window message} { variable Priv if {![info exists Priv($window)]} { bindtags $window [concat [bindtags $window] BalloonBindTag] set Priv(delay,$window) 500 } if {![info exists Priv(delay,$window)]} { set Priv(delay,$window) 500 } set Priv($window) $message return } proc NSBalloon::Set_Canvas {canvas tagOrId message} { variable Priv foreach id [$canvas find withtag $tagOrId] { if {![info exists Priv($canvas,$id)]} { $canvas addtag BalloonBindTag withtag $id } set Priv($canvas,$id) $message } if {![info exists Priv(canvas,$canvas)]} { set Priv(canvas,$canvas) 1 $canvas bind BalloonBindTag { set NSBalloon::Priv(showing) 0 set NSBalloon::Priv(first) 1 if {!$NSBalloon::Priv(delay)} { set NSBalloon::Priv(delay) $NSBalloon::Priv(delay,%W) } set NSBalloon::Priv(id) \ [after $NSBalloon::Priv(delay) \ {NSBalloon::Balloon_Canvas %W}] } $canvas bind BalloonBindTag { set NSBalloon::Priv(first) 0 NSBalloon::Kill %W } $canvas bind BalloonBindTag { set NSBalloon::Priv(first) 0 NSBalloon::Kill %W } $canvas bind BalloonBindTag { if {$NSBalloon::Priv(showing) == 0} { after cancel $NSBalloon::Priv(id) set NSBalloon::Priv(id) \ [after $NSBalloon::Priv(delay) { NSBalloon::Balloon_Canvas %W }] } } } if {![info exists Priv(delay,$canvas)]} { set Priv(delay,$canvas) 500 } return } proc NSBalloon::Delay {window delay} { variable Priv set Priv(delay,$window) $delay return } proc NSBalloon::Kill {window} { variable Priv after cancel $Priv(id) if {$Priv(showing)} { Hide after cancel $Priv(delayId) set afterId [after 1000 { set NSBalloon::Priv(delay) 0 }] set Priv(delayId) $afterId } set Priv(showing) 0 return } proc NSBalloon::Balloon {window} { variable Priv if {![winfo exists $window]} return set message $Priv($window) if {![string length $message]} return if {$Priv(first) == 1} { set Priv(first) 2 set x [expr {[winfo rootx $window] + ([winfo width $window] / 2)}] set y [expr {[winfo rooty $window] + [winfo height $window] + 4}] Show $message $x $y n set Priv(showing) 1 after cancel $Priv(delayId) set Priv(delay) 10 } return } proc NSBalloon::Balloon_Canvas {canvas} { variable Priv if {![winfo exists $canvas]} return set id [$canvas find withtag current] if {![llength $id]} return set message $Priv($canvas,$id) if {![string length $message]} return if {$Priv(first) == 1} { set Priv(first) 2 scan [$canvas bbox $id] "%s %s %s %s" left top right bottom set x [expr {[winfo rootx $canvas] + $left + ($right - $left) / 2}] set y [expr {[winfo rooty $canvas] + $bottom + 2}] Show $message $x $y n set Priv(showing) 1 after cancel $Priv(delayId) set Priv(delay) 10 } return } zangband/lib/script/tk/library/buttonlabel.tcl0000644000000000000000000000376010250356274020525 0ustar rootroot# File: buttonlabel.tcl # Purpose: a label that behaves like a button # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSButtonLabel { variable Priv # namespace eval NSButtonLabel } # NSButtonLabel::ButtonLabel -- # # Create bindings on the given label widget so it behaves "like a button." # Also remember the given command so it can be called when the label # is clicked. # # Arguments: # widget Which label widget to button-ize # command Command plus args to invoke # # Results: # What happened. proc NSButtonLabel::ButtonLabel {widget command} { variable Priv array set Priv [list "$widget,command" $command] bindtags $widget [concat [bindtags $widget] ButtonLabelBindTags] return } # NSButtonLabel::_ButtonLabelRelease -- # # Called when Button-1 is released for the given label widget. # If the pointer is in the label then the remembered command is # evaluated. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSButtonLabel::_ButtonLabelRelease {widget x y} { variable Priv $widget configure -relief raised set x [expr {$x + [winfo rootx $widget]}] set y [expr {$y + [winfo rooty $widget]}] if {[winfo containing $x $y] != "$widget"} return uplevel #0 $Priv($widget,command) return } # # These bindings are applied to the ButtonLabelBindTags bindtag # bind ButtonLabelBindTags { %W configure -relief raised } bind ButtonLabelBindTags { %W configure -relief sunken } bind ButtonLabelBindTags { %W configure -relief sunken } bind ButtonLabelBindTags { %W configure -relief flat } bind ButtonLabelBindTags { %W configure -relief raised } bind ButtonLabelBindTags { NSButtonLabel::_ButtonLabelRelease %W %x %y } bind ButtonLabelBindTags { unset NSButtonLabel::Priv(%W,command) } zangband/lib/script/tk/library/canvist.tcl0000644000000000000000000006144610250356274017666 0ustar rootroot# File: canvist.tcl # Purpose: a 1-dimension list using a canvas # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSCanvist { variable Priv set Priv(scan,afterId) {} set Priv(canvistPrev) -1 # namespace eval NSCanvist } # NSCanvist::NSCanvist -- # # Object constructor called by NSObject::New. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::NSCanvist {oop parent rowHgt wid hgt newRowCmd highlightCmd} { global NSCanvist variable Priv set c $parent.canvist$oop canvas $c \ -scrollregion [list 0 0 $wid 0] -width $wid -height $hgt \ -relief flat -background white -highlightthickness 0 \ -yscrollincrement $rowHgt -takefocus 1 # # Do stuff when the canvas is clicked # bind $c "NSCanvist::Button1 $oop %x %y 0" bind $c "NSCanvist::Motion1 $oop %x %y" bind $c "NSCanvist::Double1 $oop %x %y" bind $c "NSCanvist::Release1 $oop %x %y" bind $c "NSCanvist::Leave1 $oop %x %y" bind $c "NSCanvist::CancelRepeat $oop" # KeyPress bindings bind $c "$c yview moveto 0 ; break" bind $c "$c yview moveto 1 ; break" bind $c "$c yview scroll -1 pages ; break" bind $c "$c yview scroll 1 pages ; break" bind $c "NSCanvist::UpDown $oop -1 ; break" bind $c "NSCanvist::UpDown $oop +1 ; break" # # The Control key toggles selected rows. # bind $c "NSCanvist::Button1 $oop %x %y 1" # Destroy the object along with the canvas (later) NSUtils::DestroyObjectWithWidget NSCanvist $oop $c # Allows client to draw selection depending on focus bindtags $c [concat [bindtags $c] NSCanvistBindTag$oop] bind NSCanvistBindTag$oop \ "NSCanvist::Activate $oop 1" bind NSCanvistBindTag$oop \ "NSCanvist::Activate $oop 0" set Priv(stroke) 0 set NSCanvist($oop,canvas) $c set NSCanvist($oop,rowHgt) $rowHgt set NSCanvist($oop,newRowCmd) $newRowCmd set NSCanvist($oop,highlightCmd) $highlightCmd set NSCanvist($oop,invokeCmd) {} set NSCanvist($oop,selectionCmd) {} set NSCanvist($oop,count) 0 set NSCanvist($oop,nextRowTag) 0 set NSCanvist($oop,rowTags) {} set NSCanvist($oop,selection) {} set NSCanvist($oop,rowsEnabled) 1 set NSCanvist($oop,nearest) 0 set NSCanvist($oop,stroke) 0 set NSCanvist($oop,trackIgnore) 0 set NSCanvist($oop,clickCmd) {} # Total hack -- PDjam module uses Drag & Drop set NSCanvist($oop,dragSpecial) 0 return } # NSCanvist::~NSCanvist -- # # Object destructor called by NSObject::Delete. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::~NSCanvist {oop} { return } # NSCanvist::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Info {oop info args} { global NSCanvist # Verify the object NSObject::CheckObject NSCanvist $oop # Set info if {[llength $args]} { switch -- $info { default { set NSCanvist($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSCanvist($oop,$info) } } } return } # NSCanvist::Insert -- # # Insert a row at the given index. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Insert {oop index args} { global NSCanvist set canvas $NSCanvist($oop,canvas) set count $NSCanvist($oop,count) set rowHgt $NSCanvist($oop,rowHgt) if {$index == "end"} {set index $count} if {$index < 0} {set index 0} if {$index > $count} {set index $count} set y [expr {$rowHgt * $index}] # # Move following rows down by one. # if {$index < $count} { foreach rowTag [lrange $NSCanvist($oop,rowTags) $index end] { $canvas move $rowTag 0 $rowHgt } } # # The newRowCmd returns a list of all items added that are # on the new row. They get tagged with a common "group tag" # of the form ":N" where N is some integer. # set itemIdList [uplevel #0 $NSCanvist($oop,newRowCmd) $oop $y $args] set rowTag ":$NSCanvist($oop,nextRowTag)" foreach itemId $itemIdList { $canvas addtag $rowTag withtag $itemId } # Insert if {$index < $count} { # Remember the tag applied to all items on this row set NSCanvist($oop,rowTags) \ [linsert $NSCanvist($oop,rowTags) $index $rowTag] # This row is not selected set NSCanvist($oop,selection) \ [linsert $NSCanvist($oop,selection) $index 0] # Append } else { lappend NSCanvist($oop,rowTags) $rowTag lappend NSCanvist($oop,selection) 0 } incr NSCanvist($oop,count) incr NSCanvist($oop,nextRowTag) Synch $oop return } # NSCanvist::InsertMany -- # # Insert multiple rows at the given index. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::InsertMany {oop index itemList} { global NSCanvist set canvas $NSCanvist($oop,canvas) set count $NSCanvist($oop,count) set rowHgt $NSCanvist($oop,rowHgt) set itemListCount [llength $itemList] if {!$itemListCount} return if {$index == "end"} {set index $count} if {$index < 0} {set index 0} if {$index > $count} {set index $count} set y [expr {$rowHgt * $index}] # # Move following rows down. # if {$index < $count} { set offset [expr {$itemListCount * $rowHgt}] foreach rowTag [lrange $NSCanvist($oop,rowTags) $index end] { $canvas move $rowTag 0 $offset } } set newRowTag {} set newSelected {} foreach item $itemList { # # The newRowCmd returns a list of all items added that are # on the new row. They get tagged with a common "group tag" # of the form ":N" where N is some integer. # set itemIdList [uplevel #0 $NSCanvist($oop,newRowCmd) $oop $y $item] set rowTag ":$NSCanvist($oop,nextRowTag)" foreach itemId $itemIdList { $canvas addtag $rowTag withtag $itemId } # Remember the tag applied to all items on this row lappend newRowTag $rowTag # This row is not selected lappend newSelected 0 incr NSCanvist($oop,nextRowTag) incr y $rowHgt } if {$index < $count} { set NSCanvist($oop,rowTags) \ [eval linsert [list $NSCanvist($oop,rowTags)] $index $newRowTag] set NSCanvist($oop,selection) \ [eval linsert [list $NSCanvist($oop,selection)] $index $newSelected] } else { eval lappend NSCanvist($oop,rowTags) $newRowTag eval lappend NSCanvist($oop,selection) $newSelected } incr NSCanvist($oop,count) $itemListCount # incr NSCanvist($oop,nextRowTag) $count Synch $oop return } # NSCanvist::Delete -- # # Delete one or more rows from the list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Delete {oop index1 index2} { global NSCanvist set canvas $NSCanvist($oop,canvas) set count $NSCanvist($oop,count) set rowHgt $NSCanvist($oop,rowHgt) # Nothing to delete if {$count == 0} return if {$index1 >= $count} {set index1 [expr {$count - 1}]} if {$index1 < 0} {set index1 0} if {$index2 == "end"} {set index2 $count} if {$index2 >= $count} {set index2 [expr {$count - 1}]} if {$index2 < 0} {set index2 0} set num [expr {$index2 - $index1 + 1}] if {!$num} return # Call client's selectionCmd if given set command $NSCanvist($oop,selectionCmd) if {[string length $command]} { set deselect {} for {set row $index1} {$row <= $index2} {incr row} { if {[IsRowSelected $oop $row]} { set NSCanvist($oop,selection) \ [lreplace $NSCanvist($oop,selection) $row $row 0] lappend deselect $row } } if {[llength $deselect]} { uplevel #0 $command $oop [list {} $deselect] } } # # Delete all canvas items on each deleted row # foreach rowTag [lrange $NSCanvist($oop,rowTags) $index1 $index2] { $canvas delete $rowTag } # # Move following rows up. # incr index2 foreach rowTag [lrange $NSCanvist($oop,rowTags) $index2 end] { $canvas move $rowTag 0 -[expr {$rowHgt * $num}] } # Delete row tags from list of row tags for deleted rows. incr index2 -1 set NSCanvist($oop,rowTags) [lreplace $NSCanvist($oop,rowTags) $index1 $index2] # Delete selection info for deleted rows set NSCanvist($oop,selection) \ [lreplace $NSCanvist($oop,selection) $index1 $index2] # Debug if {$num > $count} { NSUtils::ProgError "NSCanvist::Delete: $num > $count" set $num $count } incr NSCanvist($oop,count) -$num Synch $oop return } # NSCanvist::DeleteAll -- # # Delete all the rows. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::DeleteAll {oop} { global NSCanvist set canvas $NSCanvist($oop,canvas) # Call client's selectionCmd if given set command $NSCanvist($oop,selectionCmd) if {[string length $command]} { set selection [Selection $oop] if {[llength $selection]} { set NSCanvist($oop,selection) {} uplevel #0 $command $oop [list {} $selection] } } # Bye-bye, suckers! $canvas delete all set NSCanvist($oop,count) 0 set NSCanvist($oop,rowTags) {} set NSCanvist($oop,selection) {} Synch $oop return } # NSCanvist::_GetRowTag -- # # Get the tag common to all items on a row containing the given # item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::_GetRowTag {oop tagOrId} { global NSCanvist set canvas $NSCanvist($oop,canvas) # Get list of tags for item set tagList [$canvas gettags $tagOrId] # Items without enabled tag are considered "disabled" if {[lsearch $tagList "enabled"] == -1} {return {}} # Search list of tags for grouping tag (eg ":1", ":2" etc) set idx [lsearch $tagList ":*"] return [lindex $tagList $idx] } # NSCanvist::RemoveSelection -- # # Remove the selection from all rows. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::RemoveSelection {oop} { if 1 { UpdateSelection $oop {} all } else { global NSCanvist set row 0 foreach state $NSCanvist($oop,selection) { if {[IsRowSelected $oop $row]} { DeselectRow $oop $row } incr row } } return } # NSCanvist::SelectRow -- # # Select the given row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::SelectRow {oop row} { global NSCanvist # Get the widget command set canvas $NSCanvist($oop,canvas) set rowTag [lindex $NSCanvist($oop,rowTags) $row] set itemIdList [$canvas find withtag $rowTag] # Call user's command to highlight this row uplevel #0 $NSCanvist($oop,highlightCmd) $oop 1 $itemIdList # Mark the row as selected set NSCanvist($oop,selection) \ [lreplace $NSCanvist($oop,selection) $row $row 1] return } # NSCanvist::DeselectRow -- # # Deselect the given row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::DeselectRow {oop row} { global NSCanvist # Get the widget command set canvas $NSCanvist($oop,canvas) # Get list of items on this row set rowTag [lindex $NSCanvist($oop,rowTags) $row] set itemIdList [$canvas find withtag $rowTag] # Mark the row as un-selected set NSCanvist($oop,selection) \ [lreplace $NSCanvist($oop,selection) $row $row 0] # Call user's command to un-highlight this row uplevel #0 $NSCanvist($oop,highlightCmd) $oop 0 $itemIdList return } # NSCanvist::IsRowSelected -- # # Is a given row selected? # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::IsRowSelected {oop row} { global NSCanvist set count [expr {$NSCanvist($oop,count) - 1}] if {($row < 0) || ($row > $count)} { error "bad row \"$row\": must be from 0 to $count" } return [lindex $NSCanvist($oop,selection) $row] } # NSCanvist::UpdateSelection -- # # Select and deselect some rows. # When the selection changes, call client's routine (if any). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::UpdateSelection {oop selected deselected} { global NSCanvist # "Selected" takes precedence over "deselected" set doneRows {} if {([llength $selected] == 1) && ($selected == "all")} { set selected {} set count [Info $oop count] for {set row 0} {$row < $count} {incr row} { lappend selected $row } } if {([llength $deselected] == 1) && ($deselected == "all")} { set deselected [Selection $oop] } set newlySelected {} foreach row $selected { if {[lsearch -exact $doneRows $row] >= 0} continue lappend doneRows $row if {[IsRowSelected $oop $row]} continue lappend newlySelected $row } set newlyDeselected {} foreach row $deselected { if {[lsearch -exact $doneRows $row] >= 0} continue lappend doneRows $row if {![IsRowSelected $oop $row]} continue lappend newlyDeselected $row } if {[llength $newlySelected] || [llength $newlyDeselected]} { lsort -integer $newlySelected lsort -integer $newlyDeselected # Select rows foreach row $newlySelected { SelectRow $oop $row } # Deselect rows foreach row $newlyDeselected { DeselectRow $oop $row } # Call client's selectionCmd if given set command $NSCanvist($oop,selectionCmd) if {[string length $command]} { uplevel #0 $command $oop [list $newlySelected $newlyDeselected] } } return } # NSCanvist::Selection -- # # Return a list of row indexes of all currently selected rows. # # Arguments: # arg1 about arg1 # # Results: # Returns list of indexes or empty list if no rows are # selected. proc NSCanvist::Selection {oop} { global NSCanvist set selection {} set row 0 foreach state $NSCanvist($oop,selection) { if {$state} { lappend selection $row } incr row } return $selection } # NSCanvist::Button1 -- # # Handle ButtonPress-1 event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Button1 {oop x y extend} { global NSCanvist variable Priv set c $NSCanvist($oop,canvas) # Claim the input focus focus $c # Get the hit row. set row [PointToRow $oop $x $y] # List rows to select/deselect set select {} set deselect {} set callClickCmd 0 # No item was hit if {$row == -1} { # Unselect all rows if not extending selection. if {!$extend} { set deselect all } # Prepare for drag if {[Info $oop stroke]} { itemMark $c $x $y } # Remember no cell was hit set Priv(canvistPrev) -1 # An item was hit } else { # The row is currently selected if {[IsRowSelected $oop $row]} { # Control-click toggles selection if {$extend} { set deselect $row } else { set deselect all set select $row set callClickCmd 1 } # Row was not selected } else { # Unselect all rows if not extending selection. if {!$extend} { set deselect all } # Select the hit row set select $row } # Remember the current row set Priv(canvistPrev) $row } # Update the selection UpdateSelection $oop $select $deselect if {$callClickCmd} { set command [Info $oop clickCmd] if {[string length $command]} { uplevel #0 $command $oop $row } } return } # NSCanvist::Release1 -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Release1 {oop x y} { global NSCanvist variable Priv set canvas $NSCanvist($oop,canvas) itemSelect $oop set Priv(stroke) 0 $canvas delete area CancelRepeat $oop return } # NSCanvist::Motion1 -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Motion1 {oop x y} { variable Priv set canvas [Info $oop canvas] # Don't track while UpdateSelection() is in progress if {[Info $oop trackIgnore]} return # Don't track if initial click was outside any cell if {$Priv(canvistPrev) == -1} return # When mouse tracking (but not the initial click) we find # the cell nearest to the given location, even if the location # is outside any cell, or even the canvas boundary. Info $oop nearest 1 # Get the hit row. set row [PointToRow $oop $x $y] Info $oop nearest 0 # No item was hit if {($row == -1) || $Priv(stroke)} { if {[Info $oop stroke]} { # Drag out selection box itemStroke $canvas $x $y } # An item was hit } else { # Same row as last time if {$row == $Priv(canvistPrev)} return if {![Info $oop dragSpecial]} { Info $oop trackIgnore 1 UpdateSelection $oop $row all Info $oop trackIgnore 0 } set Priv(canvistPrev) $row } return } # NSCanvist::Leave1 -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Leave1 {oop x y} { AutoScan $oop return } # NSCanvist::Double1 -- # # Call client's command when canvas double-clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Double1 {oop x y} { global NSCanvist set command $NSCanvist($oop,invokeCmd) if {[string length $command]} { uplevel #0 $command $oop $x $y } return } # NSCanvist::AutoScan -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::AutoScan {oop} { global NSCanvist variable Priv set canvas $NSCanvist($oop,canvas) if {![winfo exists $canvas]} return # Don't track while UpdateSelection() is in progress if {[Info $oop trackIgnore]} return set pointerx [winfo pointerx $canvas] set pointery [winfo pointery $canvas] if {[winfo containing $pointerx $pointery] == "$canvas"} return set x [expr {$pointerx - [winfo rootx $canvas]}] set y [expr {$pointery - [winfo rooty $canvas]}] set scrollRgn [$canvas cget -scrollregion] set scrollWidth [expr {[lindex $scrollRgn 2] - [lindex $scrollRgn 0]}] set scrollHeight [expr {[lindex $scrollRgn 3] - [lindex $scrollRgn 1]}] if {[winfo width $canvas] < $scrollWidth} { if {$x >= [winfo width $canvas]} { $canvas xview scroll 1 units } elseif {$x < 0} { $canvas xview scroll -1 units } } if {[winfo height $canvas] < $scrollHeight} { if {$y >= [winfo height $canvas]} { $canvas yview scroll 1 units } elseif {$y < 0} { $canvas yview scroll -1 units } } Motion1 $oop $x $y set Priv(scan,afterId) [after 50 NSCanvist::AutoScan $oop] return } # NSCanvist::CancelRepeat -- # # Cancel auto-scrolling "after" command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::CancelRepeat {oop} { variable Priv after cancel $Priv(scan,afterId) set Priv(scan,afterId) {} return } # NSCanvist::Synch -- # # Sets the scroll region of the canvas to the row height # multiplied by the number of items in the list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Synch {oop} { global NSCanvist set c $NSCanvist($oop,canvas) # The canvist height is (num rows) * (row height) set rowHgt $NSCanvist($oop,rowHgt) set height [expr {$rowHgt * $NSCanvist($oop,count)}] # Get the scroll region and change the height set scrollRegion [lreplace [$c cget -scrollregion] 3 3 $height] $c configure -scrollregion $scrollRegion return } # NSCanvist::ItemRow -- # # Return the row index the given item is on # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::ItemRow {oop tagOrId} { global NSCanvist set rowTag [_GetRowTag $oop $tagOrId] if {$rowTag == {}} {return -1} return [lsearch -exact $NSCanvist($oop,rowTags) $rowTag] } # NSCanvist::PointToRow -- # # Finds the row containing the given point. If the rowsEnabled option # is set, returns the row containing the point, or -1 of no row # contains the point. If the nearest option is also set, returns the # row closest to the given point, even if the point is outside any # row. # # If the rowsEnabled option is not set, returns the row for which an # enabled canvas item contains the point, otherwise returns -1. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::PointToRow {oop x y} { global NSCanvist set canvas $NSCanvist($oop,canvas) # Option: Don't check for enabled items, just hit the row if {$NSCanvist($oop,rowsEnabled)} { set rows [Info $oop count] set rowHeight [Info $oop rowHgt] set row [expr {int([$canvas canvasy $y] / $rowHeight)}] # Option: Find nearest hit row (used for mouse tracking) if {[Info $oop nearest]} { if {$row < 0} { set row 0 } elseif {$row >= $rows} { set row [expr {$rows - 1}] } # Restrict to visible rows only set rowTop [expr {int([$canvas canvasy 0 $rowHeight] / $rowHeight)}] set rowBottom [expr {int([$canvas canvasy [winfo height $canvas] $rowHeight] / $rowHeight - 1)}] if {$row < $rowTop} { set row $rowTop } elseif {$row > $rowBottom} { set row $rowBottom } } if {$row < $rows && $row >= 0} { return $row } return -1 } set x [$canvas canvasx $x] set y [$canvas canvasy $y] # Get the item(s) under the point. set itemIdList [$canvas find overlapping $x $y [expr {$x + 1}] [expr {$y + 1}]] # No item is under that point if {![llength $itemIdList]} {return -1} # Get the topmost enabled item foreach itemId $itemIdList { if {[lsearch -exact [$canvas gettags $itemId] enabled] != -1} { return [ItemRow $oop $itemId] } } # No enabled item is overlapping the given location return -1 } # NSCanvist::UpDown -- # # Handle KeyPress-Up and KeyPress-Down. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::UpDown {oop delta} { global NSCanvist set canvas $NSCanvist($oop,canvas) set selection [Selection $oop] set max [expr {$NSCanvist($oop,count) - 1}] if {$max < 0} return if {[llength $selection]} { set row [expr {[lindex $selection 0] + $delta}] if {$row < 0} { set row $max } elseif {$row > $max} { set row 0 } } else { if {$delta > 0} { set row 0 } else { set row $max } } UpdateSelection $oop $row $selection See $oop $row return } # NSCanvist::See -- # # Scroll the given row into view. If it is the row above the currently- # visible top row, then scroll up one row. If it is the row below the # currently-visible bottom row, then scroll down one row. Otherwise # attempt to center the row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::See {oop row} { global NSCanvist set canvas $NSCanvist($oop,canvas) set rowHeight $NSCanvist($oop,rowHgt) set scrollRgn [$canvas cget -scrollregion] set height [lindex $scrollRgn 3] set rowTop [expr {int([$canvas canvasy 0 $rowHeight] / $rowHeight)}] set rowBottom [expr {int($rowTop + [winfo height $canvas] / $rowHeight - 1)}] if {($row >= $rowTop) && ($row <= $rowBottom)} { } elseif {$row == $rowTop - 1} { $canvas yview scroll -1 units } elseif {$row == $rowBottom + 1} { $canvas yview scroll +1 units } else { set top [expr {($row * $rowHeight - [winfo height $canvas] / 2) \ / double($height)}] $canvas yview moveto $top } return } # Utility procedures for stroking out a rectangle # Adopted from Tk "Widget Demo" proc NSCanvist::itemMark {c x y} { variable Priv set Priv(areaX1) [$c canvasx $x] set Priv(areaY1) [$c canvasy $y] set Priv(areaX2) $Priv(areaX1) set Priv(areaY2) $Priv(areaY1) $c delete area set Priv(stroke) 1 return } proc NSCanvist::itemStroke {c x y} { variable Priv if {!$Priv(stroke)} return set x [$c canvasx $x] set y [$c canvasy $y] if {($Priv(areaX1) != $x) && ($Priv(areaY1) != $y)} { $c delete area $c addtag area withtag [$c create rect $Priv(areaX1) \ $Priv(areaY1) $x $y -outline Grey] set Priv(areaX2) $x set Priv(areaY2) $y } return } proc NSCanvist::itemSelect {oop} { global NSCanvist variable Priv if {!$Priv(stroke)} return # Gotta delete it or its included in the list! $NSCanvist($oop,canvas) delete area if {($Priv(areaX1) == $Priv(areaX2)) || \ ($Priv(areaY1) == $Priv(areaY2))} return # Find all items overlapping the selection rectangle set list [$NSCanvist($oop,canvas) find overlapping \ $Priv(areaX1) $Priv(areaY1) \ $Priv(areaX2) $Priv(areaY2)] set doneRows {} set select {} set deselect {} foreach index $list { # Some items are not "enabled" if {[_GetRowTag $oop $index] == {}} continue # Get the row this item is on set row [ItemRow $oop $index] # Already processed this row if {[lsearch -exact $doneRows $row] != -1} continue # Select this row lappend select $row # Remember we did this row lappend doneRows $row } # Update the selection UpdateSelection $oop $select $deselect return } # NSCanvist::Activate -- # # Called when the focus enters or leaves the canvas. Calls the # client highlight routine for each selected row. This is so # the client can highlight differently depending on whether the # canvas has the focus or not. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCanvist::Activate {oop activate} { foreach row [Selection $oop] { SelectRow $oop $row } return } # FindItemByTag -- # # Return a list of canvas itemIds from the given list of item ids # which are tagged with the given tag. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc FindItemByTag {canvas itemIdList tag} { set result {} foreach itemId $itemIdList { set tagList [$canvas gettags $itemId] if {[lsearch -exact $tagList $tag] != -1} { lappend result $itemId } } return $result } zangband/lib/script/tk/library/color-picker.tcl0000644000000000000000000001115110250356274020574 0ustar rootroot# File: color-picker.tcl # Purpose: a color picker # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSColorPicker { # namespace eval NSColorPicker } # NSColorPicker::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::InitModule {} { } # NSColorPicker::NSColorPicker -- # # Object constructor called by NSObject::New(). Creates a new # color picker in the given parent. Afterwards you should call # SetColors() with a list of colors to display, and then set # NSColorPicker(OOP,command) to the command to call whenever # SetCursor() changes the cursor position. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::NSColorPicker {oop parent cols rows size} { set canvas $parent.picker$oop canvas $canvas \ -width [expr {$size * $cols + 1}] \ -height [expr {$size * $rows + 1}] \ -relief flat -borderwidth 0 -highlightthickness 0 bind $canvas " NSColorPicker::SetCursor $oop \[NSColorPicker::GetIndex $oop %x %y] 1 " bind $canvas " NSColorPicker::SetCursor $oop \[NSColorPicker::GetIndex $oop %x %y] 1 " Info $oop cols $cols Info $oop rows $rows Info $oop size $size Info $oop canvas $canvas Info $oop index 0 Info $oop command "" bind $canvas "NSObject::Delete NSColorPicker $oop" return } # NSColorPicker::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::Info {oop info args} { global NSColorPicker # Verify the object NSObject::CheckObject NSColorPicker $oop # Set info if {[llength $args]} { switch -- $info { default { set NSColorPicker($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSColorPicker($oop,$info) } } } return } # NSColorPicker::SetColors -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::SetColors {oop colors} { set canvas [Info $oop canvas] set size [Info $oop size] set cols [Info $oop cols] set rows [Info $oop rows] set count [llength $colors] if {$count % $cols} { set rows [expr {$count / $cols + 1}] set fudgeCol [expr {$count % $cols}] } else { set rows [expr {$count / $cols}] set fudgeCol $cols } $canvas delete all set i 0 foreach color $colors { set y [expr {$i / $cols}] set x [expr {$i % $cols}] set n 1 if {($x == $cols - 1) || ($y == $rows - 1) || (($y == $rows - 2) && ($x >= $fudgeCol))} {set n 0} $canvas create rectangle [expr {$x * $size}] \ [expr {$y * $size}] [expr {($x + 1) * $size + $n}] \ [expr {($y + 1) * $size + $n}] -fill $color \ -tags $i incr i } $canvas create rectangle 0 0 $size \ $size -outline White -tags cursor # Cursor starts out not visible ShowHideCursor $oop 0 return } # NSColorPicker::SetCursor -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::SetCursor {oop index callCmd} { if {![Info $oop active]} return if {$index < 0 || $index > 255} return if {$index == [Info $oop index]} return set canvas [Info $oop canvas] set size [Info $oop size] set cols [Info $oop cols] set bbox [$canvas bbox cursor] set oldX [lindex $bbox 0] set oldY [lindex $bbox 1] set row [expr {$index / $cols}] set col [expr {$index % $cols}] set x [expr {$col * $size - 1}] set y [expr {$row * $size - 1}] $canvas move cursor [expr {$x - $oldX}] [expr {$y - $oldY}] Info $oop index $index if {$callCmd} { set command [Info $oop command] if {[string length $command]} { uplevel #0 $command $oop $index } } return } # NSColorPicker::ShowHideCursor -- # # Makes the cursor visible or not visible, by changing its color. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::ShowHideCursor {oop visible} { set canvas [Info $oop canvas] if {$visible} { $canvas itemconfigure cursor -outline White } else { $canvas itemconfigure cursor -outline Black } Info $oop active $visible return } # NSColorPicker::GetIndex -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSColorPicker::GetIndex {oop x y} { set canvas [Info $oop canvas] set itemId [$canvas find closest $x $y 1.0 cursor] if {$itemId == ""} {return -1} foreach tag [$canvas gettags $itemId] { if {$tag >= 0 && $tag < 256} { return $tag } } return -1 } zangband/lib/script/tk/library/menu.tcl0000644000000000000000000004321310250356274017153 0ustar rootroot# File: menu.tcl # Purpose: menu management # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMenu { variable Priv set Priv(debug) 0 # Option: Use array instead of list for identifiers variable IdentArray 0 if {![llength [info commands menuentrystate]]} { proc ::menuentrystate {menu index args} { if {[llength $args]} { return [$menu entryconfigure $index -state [lindex $args 0]] } else { return [$menu entrycget $index -state] } } } # namespace eval NSMenu } # NSMenu::NSMenu -- # # Object constructor called by NSObject::New. # # Arguments: # oop OOP ID returned by NSObject::New # parent OOP ID of parent menu or toplevel window pathname # menuDef Menu definition proc NSMenu::NSMenu {oop parent args} { global NSMenu variable Priv if {$Priv(debug)} { Debug "Parsing OOP ID #$oop > $args" } set Priv(postcommand) {} set Priv(ident) {} set Priv(entries) {} set Priv(options) {} eval _MenuParseMenu $args # Popup menu if {[string equal [string index $parent 0] "*"]} { set parent [string range $parent 1 end] set m $parent.menu$oop set topOop $oop } elseif {[string compare [string index $parent 0] "."]} { set topOop $NSMenu($parent,topOop) set m $NSMenu($parent,menu).menu$oop # Append OOP ID to menubar's list of OOP ID's lappend NSMenu($topOop,subOop) $oop } else { set m $parent.menubar$oop $parent configure -menu $m set topOop $oop # lappend NSMenu($topOop,subOop) $oop } if {$Priv(debug)} { Debug "Making menu $m $Priv(options)" } eval menu $m $Priv(options) if {$topOop == $oop} { $m configure -postcommand "NSMenu::_MenuPostCommand $topOop" } elseif {$Priv(postcommand) != {}} { $m configure -postcommand $Priv(postcommand) } elseif {[Platform unix]} { # Linux doesn't call the menubar's postcommand before # interacting with menus $m configure -postcommand "NSMenu::_MenuPostCommand $topOop" } set NSMenu($oop,menu) $m set NSMenu($oop,postcommand) $Priv(postcommand) set NSMenu($oop,ident) $Priv(ident) set NSMenu($oop,subIdent) {} set NSMenu($oop,subOop) {} set NSMenu($oop,topOop) $topOop set NSMenu($oop,menuSelectCmd) {} set NSMenu($oop,invokeCmd) {} set NSMenu($oop,setupMode) disabled set NSMenu($oop,setupCmd) {} # Call client command when entries are selected bind $m <> "NSMenu::MenuSelect $oop %W" # Destroy the object along with the widget (later) NSUtils::DestroyObjectWithWidget NSMenu $oop $m return } # NSMenu::~NSMenu -- # # Object destructor called by NSObject::Delete. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::~NSMenu {oop} { global NSMenu if 0 { # When you bind to a menu, the bindings are also applied to # the clone of the menu. So I have to be careful not to # delete the menu object more than once. if {![info exists NSMenu($oop,topOop)]} { return } } set topOop $NSMenu($oop,topOop) set index [lsearch -exact $NSMenu($topOop,subOop) $oop] if {$index == -1} { return } set NSMenu($topOop,subOop) [lreplace $NSMenu($topOop,subOop) $index $index] return } # NSMenu::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::Info {oop info args} { global NSMenu # Verify the object NSObject::CheckObject NSMenu $oop # Set info if {[llength $args]} { switch -- $info { default { set NSMenu($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSMenu($oop,$info) } } } return } # NSMenu::MenuInsertEntry -- # # Search menu tree for given identifier, then parse the menu # entry definition and insert the new entry before the entry # specified by the identifier. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::MenuInsertEntry {top pos ident args} { variable Priv if {$Priv(debug)} { Debug "MenuInsertEntry $top $pos $ident > $args" } set entry [MenuFindEntry $top $ident] if {$entry == {}} return set oop [lindex $entry 0] set index [lindex $entry 1] switch -- $pos { -first {set index 0} -end {set index end} -append {set index 1000} -before {} -after {incr index} } if {$index == "IS_MENU"} { } eval _MenuInsertEntry $oop $index $args return } # NSMenu::_MenuInsertEntry -- # # Parse entry definition and insert into menu widget. # Also insert matching identifier into list of identifiers. # # Arguments: # oop OOP ID of this menu # beforeIdent Entry identifier to insert just before, or end # entryDef Menu entry definition # # Results: # What happened. proc NSMenu::_MenuInsertEntry {oop index args} { global NSMenu variable Priv set menu $NSMenu($oop,menu) set Priv(command) {} set Priv(ident) {} set Priv(menu) {} set command [eval _MenuParseEntry $menu $index $args] if {$Priv(menu) != {}} { set topOop $NSMenu($oop,topOop) foreach subOop $NSMenu($topOop,subOop) { if {[string equal $NSMenu($subOop,ident) $Priv(menu)]} { lappend command -menu $NSMenu($subOop,menu) } } } # Hack -- MenuInvoke stuff if {$Priv(command) != {}} { lappend command -command $Priv(command) } elseif {$Priv(ident) != {}} { if {![string match "*sep*" $Priv(type)]} { lappend command -command "NSMenu::MenuInvoke $oop $Priv(ident)" } } eval $command if {$NSMenu::IdentArray} { if {$index == "end"} { set index [$menu index end] } set NSMenu($oop,subIdent,$Priv(ident)) $index } else { set NSMenu($oop,subIdent) [linsert $NSMenu($oop,subIdent) $index $Priv(ident)] } return } # NSMenu::MenuDeleteEntry -- # # Delete menu entry from menu widget and identifier from list # of identifiers. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::MenuDeleteEntry {top ident} { global NSMenu set entry [MenuFindEntry $top $ident] if {$entry == {}} return set oop [lindex $entry 0] set index [lindex $entry 1] set menu $NSMenu($oop,menu) if {$index == "IS_MENU"} { destroy $menu } else { $menu delete $index if {$NSMenu::IdentArray} { unset NSMenu($oop,subIdent,$ident) } else { set NSMenu($oop,subIdent) \ [lreplace $NSMenu($oop,subIdent) $index $index] } } return } # NSMenu::_MenuSetupOne -- # # Disable all the entries in the given menu. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::_MenuSetupOne {oop} { global NSMenu set menu $NSMenu($oop,menu) set last [$menu index end] if {$last == "none"} return set command $NSMenu($oop,setupCmd) if {[string length $command]} { uplevel #0 $command $oop } set state $NSMenu($oop,setupMode) if {$state == ""} return for {set i 0} {$i <= $last} {incr i} { if {[$menu type $i] == "separator"} continue if {[string compare [$menu entrycget $i -state] $state]} { menuentrystate $menu $i $state } } return } # NSMenu::MenuSetup -- # # Set up each sub menu. # # Arguments: # toop OOP ID of menubar # # Results: # What happened. proc NSMenu::MenuSetup {toop} { global NSMenu set command $NSMenu($toop,setupCmd) if {[string length $command]} { uplevel #0 $command $toop } foreach subOop $NSMenu($toop,subOop) { _MenuSetupOne $subOop } return } # NSMenu::MenuEnable -- # # Search menu tree for given identifier and set state # of entry to normal. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::MenuEnable {oop identList} { global NSMenu variable Priv foreach ident $identList { set entry [MenuFindEntry $oop $ident] if {$entry == ""} continue set menuId [lindex $entry 0] set index [lindex $entry 1] set menu $NSMenu($menuId,menu) if {[string compare [$menu entrycget $index -state] normal]} { menuentrystate $menu $index normal } } return } # NSMenu::MenuMatchEntryOne -- # # Search given menu for identifiers matching pattern. # # Arguments: # arg1 about arg1 # # Results: # Return numerical index into menu or -1. proc NSMenu::MenuMatchEntryOne {oop pattern} { global NSMenu set result {} if {![string match $pattern $NSMenu($oop,ident)]} { lappend result $ident } foreach ident $NSMenu($oop,subIdent) { if {![string match $pattern $ident]} { lappend result $ident } } return $result } # NSMenu::MenuMatchEntry -- # # Search given menu and submenus for identifiers matching pattern. # # Arguments: # oop OOP ID of menubar # pattern Pattern to match identifiers against # # Results: # Return list "oop index" or empty list. proc NSMenu::MenuMatchEntry {oop pattern} { global NSMenu append result [MenuMatchPattern $oop $pattern] foreach subOop $NSMenu($oop,subOop) { append result [MenuMatchEntry $subOop $pattern] } return $result } # NSMenu::MenuFindEntryOne -- # # Search given menu only for identifier. # # Arguments: # arg1 about arg1 # # Results: # Return numerical index into menu or -1. proc NSMenu::MenuFindEntryOne {oop ident} { global NSMenu if {[string equal $NSMenu($oop,ident) $ident]} { return IS_MENU } if {$NSMenu::IdentArray} { if {![info exists NSMenu($oop,subIdent,$ident)]} {return -1} return $NSMenu($oop,subIdent,$ident) } else { return [lsearch -exact $NSMenu($oop,subIdent) $ident] } } # NSMenu::MenuFindEntry -- # # Search given menu and submenus for identifier. # # Arguments: # oop OOP ID of menubar # ident identifier to look for # # Results: # Return list "oop index" or empty list. proc NSMenu::MenuFindEntry {oop ident} { global NSMenu set topId $NSMenu($oop,topOop) if {[info exists NSMenu($topId,identArray,$ident)]} { return [lindex $NSMenu($topId,identArray,$ident) 0] } set index [MenuFindEntryOne $oop $ident] if {$index != -1} { return [list $oop $index] } foreach subOop $NSMenu($oop,subOop) { set index [MenuFindEntryOne $subOop $ident] if {$index != -1} { return [list $subOop $index] } } return "" } # NSMenu::_MenuParseMenu -- # # Given a menu definition, return a string such that "eval $string" # will create a menu widget. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::_MenuParseMenu {args} { variable Priv foreach {option value} $args { switch -- $option { -postcommand {set Priv(postcommand) $value} -identifier {set Priv(ident) $value} -entries {set Priv(entries) $value} -parent {set Priv(parent) $value} default {lappend Priv(options) $option $value} } } return } # NSMenu::_MenuParseEntry # # Given a menu entry definition, return a list such that # "eval $list" will create a menu widget entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::_MenuParseEntry {menu index args} { variable Priv set result [list $menu insert $index] foreach {option value} $args { switch -- $option { -type { lappend result $value set Priv(type) $value } -menu {set Priv(menu) $value} -identifier {set Priv(ident) $value} -command { set Priv(command) $value } default { lappend result $option $value } } } if {$Priv(debug)} { Debug "_MenuParseEntry:\n $entry\n $result" } return $result } # NSMenu::_MenuPostCommand -- # # Called before posting a menu. Calls MenuSetup to configure the # state of each item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::_MenuPostCommand {toop} { global NSMenu variable Priv if {$Priv(debug)} { Debug "_MenuPostCommand $toop" } MenuSetup $toop set command $NSMenu($toop,postcommand) if {$command != {}} { uplevel #0 $command $toop } return } # NSMenu::MenuInsertEntries -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::MenuInsertEntries {top pos ident entries} { global NSMenu set entry [MenuFindEntry $top $ident] if {$entry == {}} return set oop [lindex $entry 0] set index [lindex $entry 1] switch -- $pos { -first {set index 0} -end {set index end} -append {set index 1000} -before {} -after {incr index} } if {$pos == "-end"} { set index [$NSMenu($oop,menu) index end] if {$index == "none"} { set index 0 } else { incr index } } foreach entry $entries { eval _MenuInsertEntry $oop $index $entry incr index } return } # NSMenu::MenuSelect -- # # Called to handle the <> virtual event. This allows # the client to display help messages for menu entries. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::MenuSelect {oop menu} { global NSMenu # The clone menu is actually the one responding to the <> # virtual event, so I cannot use the object's menu to query the # active item! # set menu $NSMenu($oop,menu) # Get the client command set command $NSMenu($oop,menuSelectCmd) # If this menu doesn't have a menuSelectCmd, then use the command # for the topmost menu, if any. if {![string length $command]} { set topOop $NSMenu($oop,topOop) if {$oop == $topOop} { return } set command $NSMenu($topOop,menuSelectCmd) if {![string length $command]} { return } } # Get the index of the active menu entry set index [$menu index active] # No entry is selected if {$index == "none"} { set ident {} # An entry is selected } else { if {$index < [llength $NSMenu($oop,subIdent)]} { set ident [lindex $NSMenu($oop,subIdent) $index] } else { set ident $NSMenu($oop,ident) } } # Call the client command uplevel #0 $command $oop $index [list $ident] return } # NSMenu::MenuInvoke -- # # Called when a menu entry is invoked. Calls the client's invokeCmd # if present. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::MenuInvoke {oop ident} { global NSMenu # The clone menu is actually the one responding to the <> # virtual event, so I cannot use the object's menu to query the # active item! # set menu $NSMenu($oop,menu) # Get the client command set command $NSMenu($oop,invokeCmd) # If this menu doesn't have an invokeCmd, then use the command # for the topmost menu, if any. if {![string length $command]} { set topOop $NSMenu($oop,topOop) if {$oop == $topOop} { return } set command $NSMenu($topOop,invokeCmd) if {![string length $command]} { return } } # Call the client command uplevel #0 $command $oop [list $ident] return } # NSMenu::EntryConfigure -- # # Configure a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::EntryConfigure {top ident args} { set entry [MenuFindEntry $top $ident] if {$entry == {}} return set oop [lindex $entry 0] set index [lindex $entry 1] set menu [Info $oop menu] eval $menu entryconfigure $index $args return } # NSMenu::SetIdentArray -- # # A big hack. In an effort to speed lookup times, you can create a mapping # of identifiers -> menu,index. But if menu entries are added/deleted/moved # you must call this again. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::SetIdentArray {topId} { global NSMenu array unset NSMenu $topId,identArray,* foreach menuId $NSMenu($topId,subOop) { set index 0 foreach ident $NSMenu($menuId,subIdent) { lappend NSMenu($topId,identArray,$ident) [list $menuId $index] incr index } } return } # NSMenu::BindAccels -- # # Extracts the acclerators from a heirarchy of menu widgets # and creates bindings on a list of windows which invoke the # menu item commands. # # Arguments: # oop NSMenu OOP ID. # widgets A list of widget pathnames # menu Menu widget pathname to examine # # Results: # For any "Ctrl-keysym" or "keysym" accelerator options # found, a binding is created for or # for each of the widgets. proc NSMenu::BindAccelsOne {oop widgets} { set menu [Info $oop menu] set lastIdx [$menu index last] if {"$lastIdx" == "none"} return set idx 0 foreach ident [Info $oop subIdent] { # This is a command menu entry if {[$menu type $idx] == "command"} { # Get the acclerator set acc [$menu entrycget $idx -accelerator] if {$acc != ""} { if {$acc == "Esc"} { set acc Escape } elseif {$acc == "Del"} { set acc Delete } elseif {$acc == "`"} { if {[Platform unix]} { set acc grave } } elseif {$acc == "?"} { if {[Platform unix]} { set acc question } } if {[regsub -all {Ctrl\+(.)} $acc {\1} key]} { if {[string match {[A-Z]} $key]} { set key [string tolower $key] } set acc "" } else { set acc "" } if 0 { if {[regsub -all {(.)} $acc {\1} key]} { if {[string match {[A-Z]} $key]} { set key [string tolower $key] } set acc " } } foreach win $widgets { bind $win $acc "NSMenu::TryInvoke $oop $ident ; break" } } } incr idx } return } proc NSMenu::BindAccels {top widgets} { foreach menuId [Info $top subOop] { BindAccelsOne $menuId $widgets } return } # NSMenu::TryInvoke -- # # Invoke a menu item. Has no effect if the menu item is disabled. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMenu::TryInvoke {oop ident} { set entry [MenuFindEntry $oop $ident] if {$entry == {}} return set menuId [lindex $entry 0] set index [lindex $entry 1] set menu [Info $menuId menu] if 1 { set toop [Info $oop topOop] MenuSetup $toop set command [Info $toop postcommand] if {$command != {}} { uplevel #0 $command $toop } } else { set command [$menu cget -postcommand] if {$command != {}} { uplevel #0 $command } } $menu invoke $index # So statusbar is "covered" MenuSelect $toop [Info $toop menu] return } zangband/lib/script/tk/library/module.tcl0000644000000000000000000000507610250356274017501 0ustar rootroot# File: module.tcl # Purpose: record info about modules, load and reload them # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSModule { variable Priv # We remember the file modification time when the source file # of a module is initially read. Later, the module can be automatically # reloaded if the file modification time changes. set Priv(autoReload) 1 # namespace eval NSModule } proc NSModule::AddModule {module source} { variable Priv set Priv(loaded,$module) 0 set Priv(source,$module) $source set Priv(mtime,$module) 0 return } proc NSModule::Exists {module} { variable Priv return [info exists Priv(loaded,$module)] } proc NSModule::AddModuleIfNeeded {module source} { variable Priv if {![Exists $module]} { AddModule $module $source } return } proc NSModule::LoadIfNeeded {module} { variable Priv if {![Exists $module]} { error "no such module \"$module\"" } # Already been loaded if {$Priv(loaded,$module)} { if {$Priv(autoReload)} { if {[file mtime $Priv(source,$module)] > $Priv(mtime,$module)} { # Nothing } else { return 0 } } else { return 0 } if {[llength [info commands ::${module}::CloseModule]]} { ::${module}::CloseModule } } # Clean up previously-/partially-loaded module if {[llength [namespace children :: $module]]} { namespace delete ::$module } # Source the file uplevel #0 source [list $Priv(source,$module)] # Init the module ${module}::InitModule # Okay, it's loaded set Priv(loaded,$module) 1 # Remember the modification time for auto-reloading set Priv(mtime,$module) [file mtime $Priv(source,$module)] return 1 } proc NSModule::RebootModule {module} { variable Priv # Verify existence, Close() and delete CloseModule $module # Load the module LoadIfNeeded $module return } proc NSModule::CloseModule {module} { variable Priv if {![Exists $module]} { error "no such module \"$module\"" } # Already been loaded if {$Priv(loaded,$module)} { if {[llength [info commands ::${module}::CloseModule]]} { ::${module}::CloseModule } namespace delete ::$module set Priv(loaded,$module) 0 } return } proc NSModule::IndexLoad {path} { variable Priv set Priv(dir,index) [file dirname $path] source $path return } proc NSModule::IndexOne {module name} { variable Priv set path [file join $Priv(dir,index) $name] AddModuleIfNeeded $module $path return } zangband/lib/script/tk/library/moduleIndex.tcl0000644000000000000000000000053710250356274020466 0ustar rootrootIndexOne NSBalloon balloon.tcl IndexOne NSColorPicker color-picker.tcl IndexOne NSStatusBar statusbar.tcl IndexOne NSTabbedFrame tabbed-frame.tcl IndexOne NSTitleFrame title-frame.tcl IndexOne NSToolbar toolbar.tcl IndexOne NSUpDownControl updowncontrol.tcl IndexOne NSUpDownEntry updownentry.tcl IndexOne NSWin98ToolbarButton win98ToolbarButton.tcl zangband/lib/script/tk/library/object.tcl0000644000000000000000000000431410250356274017454 0ustar rootroot# File: object.tcl # Purpose: object-oriented commands for Tcl # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # The code in this file was adapted from work by Jean-Luc Fontaine # (jfontain@pluton.experdata.fr) as part of his "textundo 1.0" package. # I changed it to use the namespace facility introduced in Tcl 8. namespace eval NSObject { variable classNewId 0 variable realObjectId # namespace eval NSObject } # NSObject::New -- # # Create a new object of the given class by getting a new # unique OOP ID and calling the class constructor (if present). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSObject::New {className args} { variable classNewId variable realObjectId # Use local variable for id so New() can be called recursively set oop [incr classNewId] # Keep track of existing objects set realObjectId($className,$oop) 1 # Call the constructor, if any if {[llength [namespace eval ::$className info procs $className]] > 0} { # Avoid catch to track errors eval ${className}::$className $oop $args } return $oop } # NSObject::Delete -- # # Delete object of given class by calling destructor and # deleting any global arrays associated with the object. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSObject::Delete {className oop} { global $className variable realObjectId # Verify the object CheckObject $className $oop # Call the destructor, if any if {[llength [namespace eval ::$className info procs ~$className]] > 0} { # Avoid catch to track errors ${className}::~$className $oop } # Unset any array elements array unset $className $oop,* # Forget this object unset realObjectId($className,$oop) return } # NSObject::CheckObject -- # # Return an error if the given info doesn't specify a known object. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSObject::CheckObject {className oop} { variable realObjectId if {![info exists realObjectId($className,$oop)]} { error "no such object $className $oop" } return } zangband/lib/script/tk/library/progress-window.tcl0000644000000000000000000000417010250356274021357 0ustar rootroot# File: progress-window.tcl # Purpose: a window with a label and progress bar # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSProgressWindow { # namespace eval NSProgressWindow } # NSProgressWindow::NSProgressWindow -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgressWindow::NSProgressWindow {oop} { InitWindow $oop return } # NSProgressWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgressWindow::Info {oop info args} { global NSProgressWindow # Set info if {[llength $args]} { switch -- $info { ratio { NSProgress2::SetDoneRatio [Info $oop progId] [lindex $args 0] } default { set NSProgressWindow($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSProgressWindow($oop,$info) } } } return } # NSProgressWindow::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgressWindow::InitWindow {oop} { set win .progress$oop toplevel $win wm title $win Progress # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSProgressWindow::Close $oop" wm resizable $win no no wm withdraw $win Info $oop win $win set frame $win.frameProgress frame $frame \ -borderwidth 0 label $frame.prompt \ -textvariable NSProgressWindow($oop,prompt) -anchor w set progId [NSObject::New NSProgress2 $frame 225 8] Info $oop progId $progId pack $frame.prompt \ -side top -fill x pack $::NSProgress2($progId,frame) \ -side top -anchor nw pack $frame \ -side top -padx 10 -pady 15 return } # NSProgressWindow::Close -- # # Do something when closing the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgressWindow::Close {oop} { destroy [Info $oop win] NSObject::Delete NSProgressWindow $oop return } zangband/lib/script/tk/library/progress1.tcl0000644000000000000000000000322610250356274020134 0ustar rootroot# File: progress.tcl # Purpose: a colorful progress bar using frames # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSProgress { # namespace eval NSProgress } # NSProgress::NSProgress -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress::NSProgress {oop parent width height color1 color2} { set frame $parent.progress$oop frame $frame -width $width -height $height -relief raised -borderwidth 1 frame $frame.todo -relief sunken -borderwidth 1 -background $color2 frame $frame.done -relief raised -borderwidth 1 -background $color1 place $frame.todo -x 0 -y 0 -anchor nw -relwidth 1.0 -relheight 1.0 place $frame.done -x 0 -y 0 -anchor nw -relwidth 0.5 -relheight 1.0 Info $oop frame $frame return } # NSProgress::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress::Info {oop info args} { global NSProgress # Set info if {[llength $args]} { switch -- $info { default { set NSProgress($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSProgress($oop,$info) } } } return } # NSProgress::SetDoneRatio -- # # Set the progress bar to frac% completion. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress::SetDoneRatio {oop frac} { set frame [Info $oop frame] place configure $frame.done -relwidth $frac return } zangband/lib/script/tk/library/progress2.tcl0000644000000000000000000000540710250356274020140 0ustar rootroot# File: progress2.tcl # Purpose: an MS-Windows-like progress bar using a canvas # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSProgress2 { # namespace eval NSProgress2 } # NSProgress2::NSProgress2 -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress2::NSProgress2 {oop parent width height} { set canvas $parent.progress$oop canvas $canvas \ -width $width -height $height -borderwidth 1 -relief sunken \ -highlightthickness 0 Info $oop frame $canvas Info $oop frac 0.0 Configure $oop bind $canvas "NSProgress2::Configure $oop" return } # NSProgress2::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress2::Info {oop info args} { global NSProgress2 # Set info if {[llength $args]} { switch -- $info { default { set NSProgress2($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSProgress2($oop,$info) } } } return } # NSProgress2::SetDoneRatio -- # # Set the progress bar to frac% completion. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress2::SetDoneRatio {oop frac} { set canvas [Info $oop frame] set numDots [Info $oop numDots] set dotNum [expr {int($frac * $numDots)}] for {set i [Info $oop curDot]} {$i < $dotNum} {incr i} { $canvas itemconfigure dot$i -fill $::SystemHighlight -outline $::SystemHighlight } for {set i $dotNum} {$i < [Info $oop curDot]} {incr i} { $canvas itemconfigure dot$i -fill {} -outline {} } Info $oop curDot $dotNum Info $oop frac $frac return } # NSProgress2::Zero -- # # Set the progress bar to 0% completion. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress2::Zero {oop} { set canvas [Info $oop frame] $canvas itemconfigure dot -fill {} -outline {} Info $oop curDot 0 Info $oop frac 0 return } # NSProgress2::Configure -- # # Called when the canvas changes size. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSProgress2::Configure {oop} { set canvas [Info $oop frame] $canvas delete dot set width [winfo width $canvas] set height [winfo height $canvas] # Create a bunch of progress "dots", invisible for now set n [expr {$width / 5}] set x 2 for {set i 0} {$i < $n} {incr i} { $canvas create rectangle $x 2 [expr {$x + 3}] [expr {$height - 1}] \ -fill "" -outline "" -tags "dot dot$i" incr x 5 } Info $oop numDots $n Info $oop curDot 0 SetDoneRatio $oop [Info $oop frac] return } zangband/lib/script/tk/library/status-text.tcl0000644000000000000000000000423710250356274020517 0ustar rootroot# File: status-text.tcl # Purpose: associate a help string with any widget # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Associate a help string with a given widget, and display that # string when the mouse enters the widget. The help text is # displayed in a label. namespace eval NSStatusText { variable Priv # namespace eval NSStatusText } # NSStatusText::StatusText -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusText::StatusText {widget label string} { variable Priv set Priv($widget,label) $label set Priv($widget,text) $string set Priv($widget,command) {} bindtags $widget [concat [bindtags $widget] StatusText_BindTag] return } # NSStatusText::StatusCommand -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusText::StatusCommand {widget label command} { variable Priv set Priv($widget,label) $label set Priv($widget,text) {} set Priv($widget,command) $command bindtags $widget [concat [bindtags $widget] StatusText_BindTag] return } # NSStatusText::SetText -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusText::SetText {widget} { variable Priv set label $Priv($widget,label) set Priv($widget,oldText) [$label cget -text] if {[string length $Priv($widget,text)]} { set helpText $Priv($widget,text) $label configure -text $helpText } else { uplevel #0 $Priv($widget,command) } return } # NSStatusText::ResetText -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusText::ResetText {widget} { variable Priv set label $Priv($widget,label) set oldText $Priv($widget,oldText) $label configure -text $oldText return } bind StatusText_BindTag { NSStatusText::SetText %W } bind StatusText_BindTag { NSStatusText::ResetText %W } bind StatusText_BindTag { unset NSStatusText::Priv(%W,label) unset NSStatusText::Priv(%W,text) } zangband/lib/script/tk/library/statusbar.tcl0000644000000000000000000002227710250356274020226 0ustar rootroot# File: statusbar.tcl # Purpose: A window statusbar # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSStatusBar { variable Priv variable Relief variable optionTable [list \ "ints" "-sizes" "sizes" "1" doSizes \ "ints" "-weights" "weights" "1" doWeights \ "tags" "-tags" "tags" "tag1" doWeights \ ] if {[string equal $::tcl_platform(platform) unix]} { set font "Helvetica 12" set Relief sunken } if {[string equal $::tcl_platform(platform) windows]} { set font "{MS Sans Serif} 8" set Relief groove } variable optionTableItem [list \ "font" "-font" "font" $font doFont \ "string" "-text" "text" "" doText \ ] } # NSStatusBar::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::InitModule {} { return } # NSStatusBar::NSStatusBar -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::NSStatusBar {oop path args} { variable optionTable variable Priv frame $path \ -borderwidth 0 set frameCmd win98Canvas$path rename $path $frameCmd set Priv(oop,$path) $oop frame $path.pad \ -borderwidth 0 -height 3 label $path.cover \ -borderwidth 0 -anchor nw -padx 2 grid rowconfigure $path 0 -weight 0 grid rowconfigure $path 1 -weight 0 grid $path.pad \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $path.cover \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news # This is the command seen by the outside set command $path if 1 { interp alias {} ::$command {} ::NSStatusBar::Command $oop } else { proc ::$command args \ "eval NSStatusBar::Command $oop \$args" } Info $oop command $command NSUtils::DestroyObjectWithWidget NSStatusBar $oop $path Info $oop frame $path Info $oop frameCmd $frameCmd Info $oop count 0 foreach {type arg info default flags} $optionTable { Info $oop $info $default } eval Configure $oop $args return } # NSStatusBar::~NSStatusBar -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::~NSStatusBar {oop} { variable Priv catch { set frame [Info $oop frame] unset Priv(oop,$frame) } return } # NSStatusBar::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::Info {oop info args} { global NSStatusBar # Verify the object NSObject::CheckObject NSStatusBar $oop # Set info if {[llength $args]} { switch -- $info { default { set NSStatusBar($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSStatusBar($oop,$info) } } } return } # NSStatusBar::Command -- # # This is the command called for a toolbar button. The widget pathname # command is a wrapper around this. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::Command {oop command args} { switch -- $command { cget { # The Tk tabbing engine calls this! error "unknown option $args" } configure { return [eval Configure $oop $args] } cover { switch -- [lindex $args 0] { hide { grid remove [Info $oop frame].cover } set { [Info $oop frame].cover configure -text [lindex $args 1] } show { grid [Info $oop frame].cover raise [Info $oop frame].cover } default { error "unknown command \"$command [lindex $args 0]\"" } } } frame { return [eval [Info $oop frameCmd] $args] } info { return [eval Info $oop $args] } itemcget { return [eval CgetItem $oop $args] } itemconfigure { return [eval ConfigureItem $oop $args] } default { error "unknown command \"$command\"" } } return } # NSStatusBar::Configure -- # # Change configuration options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::Configure {oop args} { variable optionTable foreach {type arg info default flags} $optionTable { foreach flag $flags { set doFlag($flag) 0 } } foreach error {0 1} { # First pass: Set options to new values if {!$error} { foreach {option value} $args { set match 0 foreach {type arg info default flags} $optionTable { if {$option == $arg} { set savedOptions($info) [Info $oop $info] Info $oop $info $value foreach flag $flags { set doFlag($flag) 1 } set match 1 break } } if {!$match} { error "unknown option \"$option\"" } } # Second pass: restore options to old values. } else { foreach name [array names savedOptions] { Info $oop $name $savedOptions($name) } } # Success break } WorldChanged $oop if {$error} { error $errorString } return } if {[info tclversion] == 8.0} { proc stringIsInteger {string} { return [regexp {^[0-9]+$} $string] } } # NSStatusBar::ConfigureItem -- # # Change configuration options for a single item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::ConfigureItem {oop tagOrId args} { variable optionTableItem if {[info tclversion] == 8.0} { if {![stringIsInteger $tagOrId]} { set tagOrId [lsearch -exact [Info $oop tags] $tagOrId] } } else { if {![string is integer $tagOrId]} { set tagOrId [lsearch -exact [Info $oop tags] $tagOrId] } } if {($tagOrId < 0) || ($tagOrId >= [Info $oop count])} { error "no such item \"$tagOrId\"" } set item item$tagOrId foreach {type arg info default flags} $optionTableItem { foreach flag $flags { set doFlag($flag) 0 } } foreach error {0 1} { # First pass: Set options to new values if {!$error} { foreach {option value} $args { set match 0 foreach {type arg info default flags} $optionTableItem { if {$option == $arg} { set savedOptions($info) [Info $oop $item,$info] Info $oop $item,$info $value foreach flag $flags { set doFlag($flag) 1 } set match 1 break } } if {!$match} { error "unknown option \"$option\"" } } # Second pass: restore options to old values. } else { foreach name [array names savedOptions] { Info $oop $name $savedOptions($name) } } # Success break } WorldChanged $oop if {$error} { error $errorString } return } # NSStatusBar::CgetItem -- # # Return configuration info for a single item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::CgetItem {oop tagOrId option} { variable optionTableItem if {[info tclversion] == 8.0} { if {![stringIsInteger $tagOrId]} { set tagOrId [lsearch -exact [Info $oop tags] $tagOrId] } } else { if {![string is integer $tagOrId]} { set tagOrId [lsearch -exact [Info $oop tags] $tagOrId] } } if {($tagOrId < 0) || ($tagOrId >= [Info $oop count])} { error "no such item \"$tagOrId\"" } set item item$tagOrId if {[string equal $option -label]} { return [Info $oop frame].label$tagOrId } foreach {type arg info default flags} $optionTableItem { if {[string equal $option $arg]} { return [Info $oop $item,$info] } } error "unknown option \"$option\"" return } # NSStatusBar::WorldChanged -- # # Called when configuration options change. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStatusBar::WorldChanged {oop} { variable optionTableItem variable Relief set frame [Info $oop frame] if {![Info $oop count]} { set labelNum 0 set column 0 set item 0 foreach size [Info $oop sizes] weight [Info $oop weights] tag [Info $oop tags] { if {$column} { grid columnconfigure $frame $column -weight 0 grid [frame $frame.pad$labelNum -borderwidth 0 -width 2] \ -row 1 -column $column -rowspan 1 -columnspan 1 -stick ns incr column } set label $frame.label$labelNum label $label \ -anchor w -relief $Relief -borderwidth 1 -padx 2 -width $size grid columnconfigure $frame $column -weight $weight grid $label \ -row 1 -column $column -rowspan 1 -columnspan 1 -sticky news foreach {type arg info default flags} $optionTableItem { Info $oop item$item,$info $default } incr column incr labelNum incr item } Info $oop count $item grid $frame.pad -columnspan $column grid $frame.cover -columnspan $column grid remove $frame.cover } # BUG -- It seems that when a Label widget is displayed, it clobbers # the display of any child in that label. This screws up my progressbar # hack (ex in the Assign Window). for {set i 0} {$i < [Info $oop count]} {incr i} { set font [Info $oop item$i,font] set text [Info $oop item$i,text] if {[string compare $text [$frame.label$i cget -text]]} { $frame.label$i configure -text $text -font $font } elseif {[string compare $font [$frame.label$i cget -font]]} { $frame.label$i configure -font $font } } return } # statusbar -- # # Call this to create a new statusbar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc statusbar {args} { set oop [eval NSObject::New NSStatusBar $args] return [NSStatusBar::Info $oop command] } zangband/lib/script/tk/library/tabbed-frame.tcl0000644000000000000000000000417710250356274020526 0ustar rootroot# File: tabbed-frame.tcl # Purpose: Tabs + a frame w/ border # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSTabbedFrame { # namespace eval NSTabbedFrame } # NSTabbedFrame::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTabbedFrame::InitModule {} { # NSModule::LoadIfNeeded NSTabs return } # NSTabbedFrame::NSTabbedFrame -- # # Object constructor called by NSObject::New. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTabbedFrame::NSTabbedFrame {oop parent} { set frame $parent.tabbedFrame$oop frame $frame \ -borderwidth 0 frame $frame.pad \ -borderwidth 0 frame $frame.frameOuter \ -borderwidth 2 -relief raised frame $frame.frameOuter.frameInner \ -borderwidth 0 set tabsId [NSObject::New NSTabs $frame] set canvasTabs [NSTabs::Info $tabsId canvas] set height [winfo reqheight $canvasTabs] $frame.pad configure -height $height pack $frame.pad -side top -fill x pack $frame.frameOuter.frameInner -expand yes -fill both -padx 6 -pady 6 pack $frame.frameOuter -expand yes -fill both place $canvasTabs -in $frame.frameOuter -bordermode outside \ -x 0 -y [expr {0 - $height + 2}] raise [NSTabs::Info $tabsId canvas] Info $oop frame $frame Info $oop content $frame.frameOuter.frameInner Info $oop tabsId $tabsId # Destroy the object along with the widget (later) NSUtils::DestroyObjectWithWidget NSTabbedFrame $oop $frame return } # NSTabbedFrame::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTabbedFrame::Info {oop info args} { global NSTabbedFrame # Verify the object NSObject::CheckObject NSTabbedFrame $oop # Set info if {[llength $args]} { switch -- $info { default { set NSTabbedFrame($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSTabbedFrame($oop,$info) } } } return } zangband/lib/script/tk/library/tabs.tcl0000644000000000000000000003401010250356274017133 0ustar rootroot# File: tabs.tcl # Purpose: MS Windows style tabs # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSTabs { variable Priv if {[Platform unix]} { set Priv(font) {Helvetica 12} } if {[Platform windows]} { set Priv(font) {{MS Sans Serif} 8} } # namespace eval NSTabs } proc NSTabs::NSTabs {oop parent} { variable Priv Info $oop parent $parent Info $oop nextId 0 Info $oop id {} Info $oop totalWidth 0 Info $oop tabWidth {} Info $oop current -1 Info $oop invokeCmd {} Info $oop validateCmd {} Info $oop font $Priv(font) Info $oop active 0 InitDisplay $oop set canvas [Info $oop canvas] bindtags $canvas [concat [bindtags $canvas] TabsBindTag($oop)] bind TabsBindTag($oop) " NSObject::Delete NSTabs $oop " return } proc NSTabs::Info {oop info args} { global NSTabs # Verify the object NSObject::CheckObject NSTabs $oop # Set info if {[llength $args]} { switch -- $info { default { set NSTabs($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSTabs($oop,$info) } } } return } proc NSTabs::InitDisplay {oop} { variable Priv set parent [Info $oop parent] set canvas $parent.tabs$oop canvas $canvas \ -height 24 -width 0 -scrollregion {0 0 1000 21} -highlightthickness 0 # These two lines form the divider line along the bottom of the tabs $canvas create line 0 22 1000 22 -fill $::SystemButtonHighlight \ -width 1.0 -tags ridge $canvas create line 0 23 1000 23 -fill $::System3dLight -width 1.0 \ -tags ridge # This line obscures the bottom half of the divider line for the # current tab only $canvas create line 0 23 0 23 -fill [$canvas cget -background] \ -width 1.0 -tags obscure # Remember the canvas Info $oop canvas $canvas return } proc NSTabs::Add {oop text} { variable Priv set xoffset 2 set canvas [Info $oop canvas] set index [Info $oop nextId] set tabId tab$index set x [expr {[Info $oop totalWidth] + $xoffset}] # Find the text width set width [expr {[font measure [Info $oop font] $text] + 8 * 2}] # Hack -- MS Sans Serif has 1-pixel whitespace to right of last char incr width -1 # Keep a list of tab widths set tabWidth [Info $oop tabWidth] lappend tabWidth $width Info $oop tabWidth $tabWidth set bg [$canvas cget -background] # Outer line $canvas create line $x 22 $x 6 [expr {$x + 2}] 4 \ [expr {$x + $width - 2}] 4 -fill $::SystemButtonHighlight \ -width 1.0 -tags $tabId $canvas create line [expr {$x + $width - 2}] 5 \ [expr {$x + $width - 1}] 6 [expr {$x + $width - 1}] 23 \ -fill $::System3dDarkShadow -width 1.0 \ -tags $tabId # Inner line $canvas create line [expr {$x + 1}] 22 [expr {$x + 1}] 6 \ [expr {$x + 2}] 5 [expr {$x + $width - 2}] 5 \ -fill $::System3dLight -width 1.0 -tags $tabId $canvas create line [expr {$x + $width - 2}] 6 \ [expr {$x + $width - 2}] 23 -fill $::SystemButtonShadow -width 1.0 \ -tags $tabId # Rectangle $canvas create rectangle [expr {$x + 2}] 6 [expr {$x + $width - 2}] 22 \ -outline "" -fill $bg -tags $tabId # Text # Hack -- If *text* width is odd number of pixels (tab width is then # even because of the hack above), move it left one pixel. set d [expr {($width & 1) == 0}] $canvas create text [expr {$x + $width / 2 - $d}] 13 -anchor center \ -font [Info $oop font] -text $text -tags [list $tabId text$tabId] $canvas bind $tabId " NSTabs::Invoke $oop $tabId " $canvas bind $tabId " NSTabs::Dump $oop " Info $oop totalWidth [expr {[Info $oop totalWidth] + $width}] $canvas configure -width [expr {[Info $oop totalWidth] + $xoffset + 3}] Info $oop nextId [expr {$index + 1}] set id [Info $oop id] Info $oop id [lappend id $tabId] if {[Info $oop current] != -1} { $canvas raise ridge $canvas raise [Info $oop current] } return $tabId } proc NSTabs::SetText {oop tabId text} { set canvas [Info $oop canvas] set index [lsearch -exact [Info $oop id] $tabId] if {$index == -1} { error "can't find tab with id \"$tabId\"" } set widthOld [lindex [Info $oop tabWidth] $index] # Set the text $canvas itemconfigure text$tabId -text $text # Calculate the new width set widthNew [expr {[font measure [Info $oop font] $text] + 8 * 2}] # Hack -- MS Sans Serif has 1-pixel whitespace to right of last char incr widthNew -1 # Calculate the difference in old/new width set widthDiff [expr {$widthNew - $widthOld}] if {!$widthDiff} return # Move tabs by difference in tab widths foreach tabId2 [lrange [Info $oop id] [expr {$index + 1}] end] { $canvas move $tabId2 $widthDiff 0 } # Resize this tab IncrTabSize $oop $tabId 0 0 $widthDiff 0 # Update width of this tab Info $oop tabWidth [lreplace [Info $oop tabWidth] $index $index $widthNew] # Remember out current width Info $oop totalWidth [expr {[Info $oop totalWidth] + $widthDiff}] # Hack -- Set canvas width set xoffset 2 $canvas configure -width [expr {[Info $oop totalWidth] + $xoffset + 3}] return } proc NSTabs::Remove {oop tabId} { set canvas [Info $oop canvas] set index [lsearch -exact [Info $oop id] $tabId] if {$index == -1} { error "can't find tab with id \"$tabId\"" } set width [lindex [Info $oop tabWidth] $index] $canvas delete $tabId set id [lreplace [Info $oop id] $index $index] Info $oop id $id set tabWidth [lreplace [Info $oop tabWidth] $index $index] Info $oop tabWidth $tabWidth # Move tabs to right left by width of removed tab foreach tabId2 [lrange $id $index end] { $canvas move $tabId2 -$width 0 } # Remember out current width Info $oop totalWidth [expr {[Info $oop totalWidth] - $width}] # If the selected tab is removed, select the first tab if {$tabId == [Info $oop current]} { if {[llength $id]} { Bigger $oop [lindex $id 0] Info $oop current [lindex $id 0] } else { Info $oop current -1 } } set xoffset 2 $canvas configure -width [expr {[Info $oop totalWidth] + $xoffset + 3}] return } proc NSTabs::Invoke {oop tabId} { set index [lsearch -exact [Info $oop id] $tabId] if {$index == -1} { error "can't find tab with id \"$tabId\"" } if {$tabId == [Info $oop current]} return set command [Info $oop validateCmd] if {[string length $command]} { if {[uplevel #0 $command $oop]} { return } } set canvas [Info $oop canvas] if {[Info $oop active]} { Smaller $oop [Info $oop current] Bigger $oop $tabId Info $oop current $tabId } set command [Info $oop invokeCmd] if {[string length $command]} { uplevel #0 $command $oop $tabId } return } proc NSTabs::IncrTabSize {oop tabId left top right bottom} { set canvas [Info $oop canvas] set index [lsearch -exact [Info $oop id] $tabId] if {$index == -1} { error "can't find tab with id \"$tabId\"" } set items [$canvas find withtag $tabId] set index 0 set lineId [lindex $items $index] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + $left}] set y1 [expr {[lindex $coords 1] + $bottom}] set x2 [expr {[lindex $coords 2] + $left}] set y2 [expr {[lindex $coords 3] + $top}] set x3 [expr {[lindex $coords 4] + $left}] set y3 [expr {[lindex $coords 5] + $top}] set x4 [expr {[lindex $coords 6] + $right}] set y4 [expr {[lindex $coords 7] + $top}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4 set lineId [lindex $items [incr index]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + $right}] set y1 [expr {[lindex $coords 1] + $top}] set x2 [expr {[lindex $coords 2] + $right}] set y2 [expr {[lindex $coords 3] + $top}] set x3 [expr {[lindex $coords 4] + $right}] set y3 [expr {[lindex $coords 5] + $bottom}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 set lineId [lindex $items [incr index]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + $left}] set y1 [expr {[lindex $coords 1] + $top}] set x2 [expr {[lindex $coords 2] + $left}] set y2 [expr {[lindex $coords 3] + $top}] set x3 [expr {[lindex $coords 4] + $left}] set y3 [expr {[lindex $coords 5] + $top}] set x4 [expr {[lindex $coords 6] + $right}] set y4 [expr {[lindex $coords 7] + $top}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4 set lineId [lindex $items [incr index]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + $right}] set y1 [expr {[lindex $coords 1] + $top}] set x2 [expr {[lindex $coords 2] + $right}] set y2 [expr {[lindex $coords 3] + $bottom}] $canvas coords $lineId $x1 $y1 $x2 $y2 set rectId [lindex $items [incr index]] set coords [$canvas coords $rectId] set x1 [expr {[lindex $coords 0] + $left}] set y1 [expr {[lindex $coords 1] + $top}] set x2 [expr {[lindex $coords 2] + $right}] set y2 [expr {[lindex $coords 3] + $bottom}] $canvas coords $rectId $x1 $y1 $x2 $y2 set textId [lindex $items [incr index]] set width [expr {int($x2 - $x1)}] set d [expr {($width & 1) == 0}] set coords [$canvas coords $textId] set x [expr {$x1 + $width / 2 - $d}] set y [expr {[lindex $coords 1] + ($top + $bottom) / 2}] $canvas coords $textId $x $y return } proc NSTabs::Bigger {oop tabId} { set canvas [Info $oop canvas] set index [lsearch -exact [Info $oop id] $tabId] if {$index == -1} { error "can't find tab with id \"$tabId\"" } set items [$canvas find withtag $tabId] set index2 0 set lineId [lindex $items $index2] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] - 2}] set y1 [lindex $coords 1] if {$index == 0} {set y1 [expr {$y1 + 1}]} set x2 [expr {[lindex $coords 2] - 2}] set y2 [expr {[lindex $coords 3] - 2}] set x3 [expr {[lindex $coords 4] - 2}] set y3 [expr {[lindex $coords 5] - 2}] set x4 [expr {[lindex $coords 6] + 2}] set y4 [expr {[lindex $coords 7] - 2}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4 set lineId [lindex $items [incr index2]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + 2}] set y1 [expr {[lindex $coords 1] - 2}] if {$index == 0} {set y1 [expr {$y1 + 1}]} set x2 [expr {[lindex $coords 2] + 2}] set y2 [expr {[lindex $coords 3] - 2}] set x3 [expr {[lindex $coords 4] + 2}] set y3 [lindex $coords 5] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 set lineId [lindex $items [incr index2]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] - 2}] set y1 [lindex $coords 1] set x2 [expr {[lindex $coords 2] - 2}] set y2 [expr {[lindex $coords 3] - 2}] set x3 [expr {[lindex $coords 4] - 2}] set y3 [expr {[lindex $coords 5] - 2}] set x4 [expr {[lindex $coords 6] + 2}] set y4 [expr {[lindex $coords 7] - 2}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4 set lineId [lindex $items [incr index2]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + 2}] set y1 [expr {[lindex $coords 1] - 2}] set x2 [expr {[lindex $coords 2] + 2}] set y2 [lindex $coords 3] $canvas coords $lineId $x1 $y1 $x2 $y2 set rectId [lindex $items [incr index2]] set coords [$canvas coords $rectId] set x1 [expr {[lindex $coords 0] - 2}] set y1 [expr {[lindex $coords 1] - 2}] set x2 [expr {[lindex $coords 2] + 2}] set y2 [expr {[lindex $coords 3] + 1}] $canvas coords $rectId $x1 $y1 $x2 $y2 set textId [lindex $items [incr index2]] $canvas move $textId 0 -2 $canvas raise ridge $canvas raise $tabId # Use expr not incr because values are floats if {$index} { set x1 [expr {$x1 - 2}] } set x2 [expr {$x2 + 2}] $canvas coords obscure $x1 23 $x2 23 $canvas raise obscure return } proc NSTabs::Smaller {oop tabId} { set canvas [Info $oop canvas] if {$tabId == -1} return set index [lsearch -exact [Info $oop id] $tabId] if {$index == -1} { error "can't find tab with id \"$tabId\"" } set items [$canvas find withtag $tabId] set index2 0 set lineId [lindex $items $index2] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + 2}] set y1 [lindex $coords 1] if {$index == 0} {set y1 [expr {$y1 - 1}]} set x2 [expr {[lindex $coords 2] + 2}] set y2 [expr {[lindex $coords 3] + 2}] set x3 [expr {[lindex $coords 4] + 2}] set y3 [expr {[lindex $coords 5] + 2}] set x4 [expr {[lindex $coords 6] - 2}] set y4 [expr {[lindex $coords 7] + 2}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4 set lineId [lindex $items [incr index2]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] - 2}] set y1 [expr {[lindex $coords 1] + 2}] if {$index == 0} {set y1 [expr {$y1 - 1}]} set x2 [expr {[lindex $coords 2] - 2}] set y2 [expr {[lindex $coords 3] + 2}] set x3 [expr {[lindex $coords 4] - 2}] set y3 [lindex $coords 5] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 set lineId [lindex $items [incr index2]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] + 2}] set y1 [lindex $coords 1] set x2 [expr {[lindex $coords 2] + 2}] set y2 [expr {[lindex $coords 3] + 2}] set x3 [expr {[lindex $coords 4] + 2}] set y3 [expr {[lindex $coords 5] + 2}] set x4 [expr {[lindex $coords 6] - 2}] set y4 [expr {[lindex $coords 7] + 2}] $canvas coords $lineId $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4 set lineId [lindex $items [incr index2]] set coords [$canvas coords $lineId] set x1 [expr {[lindex $coords 0] - 2}] set y1 [expr {[lindex $coords 1] + 2}] set x2 [expr {[lindex $coords 2] - 2}] set y2 [lindex $coords 3] $canvas coords $lineId $x1 $y1 $x2 $y2 set rectId [lindex $items [incr index2]] set coords [$canvas coords $rectId] set x1 [expr {[lindex $coords 0] + 2}] set y1 [expr {[lindex $coords 1] + 2}] set x2 [expr {[lindex $coords 2] - 2}] set y2 [expr {[lindex $coords 3] - 1}] $canvas coords $rectId $x1 $y1 $x2 $y2 set textId [lindex $items [incr index2]] $canvas move $textId 0 2 return } proc NSTabs::Dump {oop} { set canvas [Info $oop canvas] foreach tabId [Info $oop id] width [Info $oop tabWidth] { append result "\t$tabId '[$canvas coords text$tabId]' width=$width\n" } return } proc NSTabs::GetNthId {oop index} { set id [Info $oop id] if {($index < 0) || ($index > [llength $id])} { error "bad tab index \"$index\": must be between 0 and [expr {[llength $id] - 1}]" } return [lindex $id $index] } zangband/lib/script/tk/library/texist.tcl0000644000000000000000000003251210250356274017527 0ustar rootroot# File: texist.tcl # Purpose: like a NSCanvist, but using a Text widget # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSTexist { variable Priv set Priv(scan,afterId) {} set Priv(canvistPrev) -1 # namespace eval NSTexist } # NSTexist::NSTexist -- # # Object constructor called by NSObject::New. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::NSTexist {oop parent font width height} { global NSTexist variable Priv set tbox $parent.texist$oop text $tbox \ -font $font -width $width -height $height -wrap none \ -borderwidth 0 -selectborderwidth 0 -highlightthickness 0 \ -cursor {} -state disabled -exportselection no bindtags $tbox [list $tbox [winfo toplevel $parent] all] # # Do stuff when the widget is clicked # bind $tbox "NSTexist::Button1 $oop %x %y 0" bind $tbox "NSTexist::Motion1 $oop %x %y" bind $tbox "NSTexist::Double1 $oop %x %y" bind $tbox "NSTexist::Release1 $oop %x %y" bind $tbox "NSTexist::Leave1 $oop %x %y" bind $tbox "NSTexist::CancelRepeat $oop" # KeyPress bindings bind $tbox "$tbox yview moveto 0 ; break" bind $tbox "$tbox yview moveto 1 ; break" bind $tbox "$tbox yview scroll -1 pages ; break" bind $tbox "$tbox yview scroll 1 pages ; break" bind $tbox "NSTexist::UpDown $oop -1 ; break" bind $tbox "NSTexist::UpDown $oop +1 ; break" # # The Control key adds to the current selection. # bind $tbox "NSTexist::Button1 $oop %x %y 1" # # Remember to destroy the object along with the canvas # bind $tbox "+ NSObject::Delete NSTexist $oop " set Priv(stroke) 0 set NSTexist($oop,text) $tbox set NSTexist($oop,invokeCmd) {} set NSTexist($oop,selectionCmd) {} set NSTexist($oop,count) 0 set NSTexist($oop,selection) {} set NSTexist($oop,stroke) 0 set NSTexist($oop,nextId) 0 return } # NSTexist::~NSTexist -- # # Object destructor called by NSObject::Delete. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::~NSTexist {oop} { } # NSTexist::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Info {oop info args} { global NSTexist # Verify the object NSObject::CheckObject NSTexist $oop # Set info if {[llength $args]} { switch -- $info { default { set NSTexist($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSTexist($oop,$info) } } } return } # NSTexist::Insert -- # # Insert a row at the given index. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Insert {oop index text color} { set tbox [Info $oop text] set count [Info $oop count] if {$index == "end"} {set index $count} if {$index < 0} {set index 0} if {$index > $count} {set index $count} # Get unique Id for this row (a tag) set rowId [expr {[Info $oop nextId] + 1}] Info $oop nextId $rowId # Insert the new row $tbox configure -state normal set index2 [expr {$index + 1}].0 $tbox insert $index2 $text\n row$rowId $tbox tag configure row$rowId -foreground $color $tbox configure -state disabled # This row is not selected Info $oop selection \ [linsert [Info $oop selection] $index 0] # One more row added Info $oop count [expr {[Info $oop count] + 1}] return } # NSTexist::SetList -- # # Set this list with text and colors (for speed). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::SetList {oop textList colorList} { set tbox [Info $oop text] Delete $oop 0 end # Get unique Id for this row (a tag) set rowId [expr {[Info $oop nextId] + 1}] $tbox configure -state normal set count 0 set index 1 set selection {} # Insert the new rows foreach text $textList color $colorList { $tbox insert $index.0 $text\n row$rowId $tbox tag configure row$rowId -foreground $color lappend selection 0 incr rowId incr count incr index } # Delete trailing newline $tbox delete "end - 1 chars" $tbox configure -state disabled Info $oop selection $selection Info $oop nextId $rowId Info $oop count $count return } # NSTexist::Delete -- # # Delete one or more rows from the list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Delete {oop index1 index2} { set tbox [Info $oop text] set count [Info $oop count] # Nothing to delete if {$count == 0} return if {$index1 >= $count} {set index1 [expr {$count - 1}]} if {$index1 < 0} {set index1 0} if {$index2 == "end"} {set index2 $count} if {$index2 >= $count} {set index2 [expr {$count - 1}]} if {$index2 < 0} {set index2 0} set num [expr {$index2 - $index1 + 1}] if {!$num} return # Call client's selectionCmd if given set command [Info $oop selectionCmd] if {[string length $command]} { set deselect {} for {set row $index1} {$row <= $index2} {incr row} { if {[IsRowSelected $oop $row]} { lappend deselect $row } } if {[llength $deselect]} { eval $command $oop [list {}] [list $deselect] } } # # Delete each row # $tbox configure -state normal set start [expr {$index1 + 1}].0 set end [expr {$index2 + 2}].0 $tbox delete $start $end $tbox configure -state disabled # Delete selection info for deleted rows Info $oop selection \ [lreplace [Info $oop selection] $index1 $index2] # Debug if {$num > $count} { NSUtils::ProgError "NSTexist::Delete: $num > $count" set $num $count } Info $oop count [expr {[Info $oop count] - $num}] return } # NSTexist::RemoveSelection -- # # Remove the selection from all rows. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::RemoveSelection {oop} { set row 0 foreach state [Info $oop selection] { if {[IsRowSelected $oop $row]} { DeselectRow $oop $row } incr row } return } # NSTexist::SelectRow -- # # Select the given row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::SelectRow {oop row} { set tbox [Info $oop text] set index [expr {$row + 1}].0 $tbox tag add sel $index "$index lineend + 1 chars" # Mark the row as selected Info $oop selection \ [lreplace [Info $oop selection] $row $row 1] return } # NSTexist::DeselectRow -- # # Deselect the given row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::DeselectRow {oop row} { set tbox [Info $oop text] # Mark the row as un-selected Info $oop selection \ [lreplace [Info $oop selection] $row $row 0] # set index [expr {$row + 1}].0 $tbox tag remove sel $index "$index lineend + 1 chars" return } # NSTexist::IsRowSelected -- # # Is a given row selected? # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::IsRowSelected {oop row} { return [lindex [Info $oop selection] $row] } # NSTexist::UpdateSelection -- # # Select and deselect some rows. # When the selection changes, call client's routine (if any). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::UpdateSelection {oop selected deselected} { # "Selected" takes precedence over "deselected" set doneRows {} # Deselect "all" if {([llength $deselected] == 1) && ($deselected == "all")} { set deselected {} foreach row [Selection $oop] { lappend deselected $row } } set newlySelected {} foreach row $selected { if {[lsearch -exact $doneRows $row] >= 0} continue lappend doneRows $row if {[IsRowSelected $oop $row]} continue lappend newlySelected $row } set newlyDeselected {} foreach row $deselected { if {[lsearch -exact $doneRows $row] >= 0} continue lappend doneRows $row if {![IsRowSelected $oop $row]} continue lappend newlyDeselected $row } if {[llength $newlySelected] || [llength $newlyDeselected]} { lsort -integer $newlySelected lsort -integer $newlyDeselected # Select rows foreach row $newlySelected { SelectRow $oop $row } # Deselect rows foreach row $newlyDeselected { DeselectRow $oop $row } # Call client's selectionCmd if given set command [Info $oop selectionCmd] if {[string length $command]} { eval $command $oop [list $newlySelected $newlyDeselected] } } return } # NSTexist::Selection -- # # Return a list of row indexes of all currently selected rows. # # Arguments: # arg1 about arg1 # # Results: # Returns list of indexes or empty list if no rows are # selected. proc NSTexist::Selection {oop} { set selection {} set row 0 foreach state [Info $oop selection] { if {$state} { lappend selection $row } incr row } return $selection } # NSTexist::Button1 -- # # Handle ButtonPress-1 event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Button1 {oop x y extend} { variable Priv set tbox [Info $oop text] # Claim the input focus focus $tbox # Get the hit row. set row [PointToRow $oop $x $y] # List rows to select/deselect set select {} set deselect {} # No item was hit if {$row == -1} { # Unselect all rows if not extending selection. if {!$extend} { set deselect all } # Remember no cell was hit set Priv(canvistPrev) -1 # An item was hit } else { # The row is currently selected if {[IsRowSelected $oop $row]} { # Control-click toggles selection if {$extend} { set deselect $row } else { set deselect all set select $row } # Row was not selected } else { # Unselect all rows if not extending selection. if {!$extend} { set deselect all } # Select the hit row set select $row } # Remember the current row set Priv(canvistPrev) $row } # Update the selection UpdateSelection $oop $select $deselect return } # NSTexist::Release1 -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Release1 {oop x y} { CancelRepeat $oop return } # NSTexist::Motion1 -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Motion1 {oop x y} { variable Priv set tbox [Info $oop text] # Don't track if initial click was outside any cell if {$Priv(canvistPrev) == -1} return # Get the hit row. set row [PointToRow $oop $x $y] # No item was hit if {$row == -1} { # An item was hit } else { # Same row as last time if {$row == $Priv(canvistPrev)} return UpdateSelection $oop $row all set Priv(canvistPrev) $row } return } # NSTexist::Leave1 -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Leave1 {oop x y} { AutoScan $oop return } # NSTexist::Double1 -- # # Call client's command when canvas double-clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::Double1 {oop x y} { set command [Info $oop invokeCmd] if {[string length $command]} { eval $command $oop $x $y } return } # NSTexist::AutoScan -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::AutoScan {oop} { variable Priv set tbox [Info $oop text] if {![winfo exists $tbox]} return set pointerx [winfo pointerx $tbox] set pointery [winfo pointery $tbox] if {[winfo containing $pointerx $pointery] == "$tbox"} return set x [expr {$pointerx - [winfo rootx $tbox]}] set y [expr {$pointery - [winfo rooty $tbox]}] if {$x >= [winfo width $tbox]} { $tbox xview scroll 1 units } elseif {$x < 0} { $tbox xview scroll -1 units } if {$y >= [winfo height $tbox]} { $tbox yview scroll 1 units } elseif {$y < 0} { $tbox yview scroll -1 units } Motion1 $oop $x $y set Priv(scan,afterId) [after 50 NSTexist::AutoScan $oop] return } # NSTexist::CancelRepeat -- # # Cancel auto-scrolling "after" command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::CancelRepeat {oop} { variable Priv after cancel $Priv(scan,afterId) set Priv(scan,afterId) {} return } # NSTexist::PointToRow -- # # Finds the row containing the given point. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::PointToRow {oop x y} { set tbox [Info $oop text] scan [$tbox index @${x},$y] "%d.%d" row ignore incr row -1 if {$row == [Info $oop count]} { return -1 } return $row } # NSTexist::UpDown -- # # Handle KeyPress-Up and KeyPress-Down. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::UpDown {oop delta} { set tbox [Info $oop text] set selection [Selection $oop] set max [expr {[Info $oop count] - 1}] if {[llength $selection]} { set row [expr {[lindex $selection 0] + $delta}] if {$row < 0} { set row $max } elseif {$row > $max} { set row 0 } } else { if {$delta > 0} { set row 0 } else { set row $max } } UpdateSelection $oop $row $selection See $oop $row return } # NSTexist::See -- # # Scroll the given row into view. If it is the row above the currently- # visible top row, then scroll up one row. If it is the row below the # currently-visible bottom row, then scroll down one row. Otherwise # attempt to center the row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTexist::See {oop row} { set tbox [Info $oop text] $tbox see "$row.0 + 1 lines" return } zangband/lib/script/tk/library/text-list.tcl0000644000000000000000000000372710250356274020152 0ustar rootroot# text-list.tcl -- # # Turns a text widget into a simple list. # TO DO: KeyPress bindings # namespace eval NSTextList { variable LineLast "" variable LineNew "" variable Scan set Scan(afterId) "" # namespace eval NSTextList } proc NSTextList::TextList {textBox} { global NSTextList bindtags $textBox [concat [bindtags $textBox] TextList_BindTag] set NSTextList($textBox,selectCmd) "" return } proc NSTextList::SelectLine {textBox index} { global NSTextList $textBox tag add sel $index "$index lineend + 1 chars" set command $NSTextList($textBox,selectCmd) if {[string length $command]} { set row [lindex [split [$textBox index $index] .] 0] uplevel #0 $command $row } return } proc NSTextList::SelectPoint {textBox x y} { variable LineLast variable LineNew set LineNew [$textBox index "@${x},$y linestart"] if {[string compare $LineNew $LineLast]} { $textBox tag remove sel 1.0 end set LineLast $LineNew SelectLine $textBox $LineLast } return } proc NSTextList::AutoScan {w} { variable Scan if {![winfo exists $w]} return if {$Scan(y) >= [winfo height $w]} { $w yview scroll 2 units } elseif {$Scan(y) < 0} { $w yview scroll -2 units } elseif {$Scan(x) >= [winfo width $w]} { $w xview scroll 2 units } elseif {$Scan(x) < 0} { $w xview scroll -2 units } else { return } SelectPoint $w $Scan(x) $Scan(y) set Scan(afterId) [after 50 NSTextList::AutoScan $w] return } bind TextList_BindTag { focus %W set NSTextList::LineLast "" NSTextList::SelectPoint %W %x %y } bind TextList_BindTag { set NSTextList::Scan(x) %x set NSTextList::Scan(y) %y NSTextList::SelectPoint %W %x %y } bind TextList_BindTag { set NSTextList::Scan(x) %x set NSTextList::Scan(y) %y NSTextList::AutoScan %W } bind TextList_BindTag { after cancel $NSTextList::Scan(afterId) } bind TextList_BindTag { after cancel $NSTextList::Scan(afterId) } zangband/lib/script/tk/library/title-frame.tcl0000644000000000000000000000472210250356274020422 0ustar rootroot# File: title-frame.tcl # Purpose: Grooved frame with a label/checkbutton title # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSTitleFrame { # namespace eval NSTitleFrame } # NSTitleFrame::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTitleFrame::InitModule {} { return } # NSTitleFrame::NSTitleFrame -- # # Object constructor called by NSObject::New. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTitleFrame::NSTitleFrame {oop parent} { set frame $parent.titleFrame$oop frame $frame \ -borderwidth 0 frame $frame.pad \ -borderwidth 0 frame $frame.title \ -borderwidth 0 frame $frame.frameGroove \ -borderwidth 2 -relief groove frame $frame.frameGroove.pad \ -borderwidth 0 pack $frame.pad \ -side top -fill x pack $frame.frameGroove.pad \ -side top -fill x pack $frame.frameGroove \ -expand yes -fill both place $frame.title \ -in $frame.frameGroove -x 6 -bordermode outside raise $frame.title bind $frame.title \ "NSTitleFrame::Configure $oop %h" Info $oop content $frame.frameGroove Info $oop frame $frame Info $oop title $frame.title Info $oop padY1 -1 # Destroy the object along with the widget (later) NSUtils::DestroyObjectWithWidget NSTitleFrame $oop $frame return } # NSTitleFrame::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTitleFrame::Info {oop info args} { global NSTitleFrame # Verify the object NSObject::CheckObject NSTitleFrame $oop # Set info if {[llength $args]} { switch -- $info { default { set NSTitleFrame($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSTitleFrame($oop,$info) } } } return } # NSTitleFrame::Configure -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTitleFrame::Configure {oop height} { set frame [Info $oop frame] if {[Info $oop padY1] < 0} { $frame.pad configure -height [expr {$height / 2 - 1}] } else { $frame.pad configure -height [Info $oop padY1] } $frame.frameGroove.pad configure -height [expr {$height / 2 - 1}] place configure $frame.title \ -y [expr {-$height / 2 + 1}] return } zangband/lib/script/tk/library/toolbar.tcl0000644000000000000000000000730610250356274017654 0ustar rootroot# File: toolbar.tcl # Purpose: a row of NSWin98ToolbarButtons # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSToolbar { # namespace eval NSToolbar } # NSToolbar::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToolbar::InitModule {} { NSModule::LoadIfNeeded NSWin98ToolbarButton return } # NSToolbar::NSToolbar -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToolbar::NSToolbar {oop size parent} { global NSToolbar set frame $parent.toolbar$oop Info $oop frame $frame Info $oop size $size Info $oop count 0 if 1 { frame $frame \ -borderwidth 2 -relief groove set canvas $frame.canvas canvas $canvas \ -borderwidth 0 -highlightthickness 0 -width 10 -height 16 set x 2 set y 2 $canvas create line [expr {$x + 1}] $y $x $y $x 20 \ -fill $::SystemButtonHighlight -tags topleft $canvas create line [expr {$x + 2}] $y [expr {$x + 2}] 20 \ [expr {$x - 1}] 20 -fill $::SystemButtonShadow -tags bottomright bind $canvas \ "NSToolbar::Configure $oop %W %h %w" pack $canvas -side left -fill y } else { frame $frame \ -borderwidth 0 -relief flat frame $frame.divider1 \ -borderwidth 1 -height 2 -relief groove pack $frame -expand yes -fill x -side top pack $frame.divider1 \ -expand yes -fill x -side top } NSUtils::DestroyObjectWithWidget NSToolbar $oop $frame return } # NSToolbar::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToolbar::Info {oop info args} { global NSToolbar # Verify the object NSObject::CheckObject NSToolbar $oop # Set info if {[llength $args]} { switch -- $info { default { set NSToolbar($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSToolbar($oop,$info) } } } return } # NSToolbar::AddTool -- # # Add a new "button" to the right of all others. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToolbar::AddTool {oop args} { global NSToolbar set frame [Info $oop frame] set size [Info $oop size] set count [incr NSToolbar($oop,count)] set button $frame.tool$count if 1 { eval win98button $button $args pack $button -side left } else { label $label \ -anchor center -relief flat -borderwidth 2 -image $image -height $size -width $size NSButtonLabel::ButtonLabel $label $command pack $label \ -anchor center -expand no -padx 2 -pady 2 -side left } return $count } # NSToolbar::GetTool -- # # Return the full window pathname of the given "button". # # Arguments: # oop OOP ID. See above. # index 1-based index of tool. # # Results: # What happened. proc NSToolbar::GetTool {oop index} { global NSToolbar set frame $NSToolbar($oop,frame) return $frame.tool$index } # NSToolbar::Configure -- # # Called when the toolbar changes size. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToolbar::Configure {oop canvas height width} { foreach itemId [$canvas find withtag topleft] { scan [$canvas coords $itemId] "%s %s %s %s %s %s" x1 y1 x2 y2 x3 y3 set y3 [expr {$height - 3}] eval $canvas coords $itemId $x1 $y1 $x2 $y2 $x3 $y3 } foreach itemId [$canvas find withtag bottomright] { scan [$canvas coords $itemId] "%s %s %s %s %s %s" x1 y1 x2 y2 x3 y3 set y2 [set y3 [expr {$height - 3}]] $canvas coords $itemId $x1 $y1 $x2 $y2 $x3 $y3 } return } zangband/lib/script/tk/library/toplevel.tcl0000644000000000000000000001616210250356274020044 0ustar rootroot# File: toplevel.tcl # Purpose: toplevel-related geometry commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSToplevel { variable Priv # namespace eval NSToplevel } # NSToplevel::MoveOffscreen -- # # Move a window offscreen. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::MoveOffscreen {toplevel} { variable Priv set Priv(save,focus) [focus] set Priv(save,state) [wm state $toplevel] set Priv(save,x) [winfo x $toplevel] set Priv(save,y) [winfo y $toplevel] wm withdraw $toplevel update idletasks wm geometry $toplevel +[winfo screenwidth $toplevel]+0 update idletasks # wm deiconify $toplevel wm state $toplevel normal update idletasks return } # NSToplevel::Restore -- # # Put a toplevel back to how it was before it was moved offscreen. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::Restore {toplevel} { variable Priv set state $Priv(save,state) if {$state == "withdrawn"} { wm withdraw $toplevel } elseif {$state == "iconic"} { wm iconify $toplevel } update idletasks # wm geometry $toplevel +$Priv(save,x)+$Priv(save,y) # update idletasks focus $Priv(save,focus) return } # NSToplevel::WidthLeft -- # # Return width of frame at left of a window. It is assumed that this is # also the width at the right and bottom of the frame. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::WidthLeft {toplevel} { return [expr {[ContentLeft $toplevel] - [FrameLeft $toplevel]}] } # NSToplevel::WidthTop -- # # Return the width of frame (and menubar) at the top of a window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::WidthTop {toplevel} { return [expr {[ContentTop $toplevel] - [FrameTop $toplevel]}] } # NSToplevel::FrameRight -- # # Return the pixel-coordinate of pixel to the right of the right edge of a # window's frame. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::FrameRight {toplevel} { return [expr {[ContentLeft $toplevel] + [winfo width $toplevel] + \ [WidthLeft $toplevel]}] } # NSToplevel::FrameBottom -- # # Return the pixel-coordinate of pixel below the bottom edge of a # window's frame. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::FrameBottom {toplevel} { return [expr {[ContentTop $toplevel] + [winfo height $toplevel] + \ [WidthLeft $toplevel]}] } # NSToplevel::ContentWidth -- # # Return the content width required so that a toplevel will be # exactly as wide as the given width, from left edge to right # edge. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::ContentWidth {toplevel width} { set frameLeft [WidthLeft $toplevel] return [expr {$width - $frameLeft * 2}] } # NSToplevel::ContentHeight -- # # Return the content height required so that a toplevel will be # exactly as tall as the given height, from top edge to bottom # edge. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::ContentHeight {toplevel height} { set frameTop [WidthTop $toplevel] set frameBottom [WidthLeft $toplevel] return [expr {$height - $frameTop - $frameBottom}] } # NSToplevel::TotalWidth -- # # Return the width of a toplevel from left to right. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::TotalWidth {toplevel} { set frameLeft [WidthLeft $toplevel] set width [winfo width $toplevel] set frameRight [WidthLeft $toplevel] return [expr {$frameLeft + $width + $frameRight}] } # NSToplevel::TotalHeight -- # # Return the height of a toplevel from top to bottom. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::TotalHeight {toplevel} { set frameTop [WidthTop $toplevel] set height [winfo height $toplevel] set frameBottom [WidthLeft $toplevel] return [expr {$frameTop + $height + $frameBottom}] } # NSToplevel::NaturalSize -- # # Withdraws the given toplevel, moves it offscreen, then shows it # again. At this point the geometry info is all correct, and the # caller can then confidently use the geometry info to set lists # etc. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::NaturalSize {toplevel command} { if {[Platform unix]} { wm state $toplevel normal update idletasks wm geometry $toplevel +[winfo screenwidth $toplevel]+0 update idletasks } else { # Withdraw the toplevel wm withdraw $toplevel update idletasks # Move the toplevel offscreen wm geometry $toplevel +[winfo screenwidth $toplevel]+0 update idletasks # Show the toplevel wm state $toplevel normal update idletasks } # Now that the window is displayed, the geometry information # is all correct. Call the client command to fiddle with the # geometry of any widgets. if {[string length $command]} { uplevel #0 $command } # Just to club it over the head... update return } # NSToplevel::SetTotalWidth -- # # Take the current geometry, and replace width with the given width. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::SetTotalWidth {win newWidth} { set geometry [wm geometry $win] scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y wm geometry $win ${newWidth}x$height$xs$x$ys$y return } # NSToplevel::SetTotalHeight -- # # Take the current geometry, and replace height with the given height. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::SetTotalHeight {win newHeight} { set geometry [wm geometry $win] scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y wm geometry $win ${width}x${newHeight}$xs$x$ys$y return } # NSToplevel::FrameLeft -- # # Return the x coordinate of the toplevel's frame. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::FrameLeft {win} { switch -- [Platform] { windows { return [winfo x $win] } unix { set geometry [wm geometry $win] scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y return $x } } } # NSToplevel::FrameTop -- # # Return the y coordinate of the toplevel's frame. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::FrameTop {win} { switch -- [Platform] { windows { return [winfo y $win] } unix { set geometry [wm geometry $win] scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y return $y } } } # NSToplevel::ContentLeft -- # # Return the x coordinate of the toplevel's insides. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::ContentLeft {win} { switch -- [Platform] { windows { return [winfo rootx $win] } unix { return [winfo rootx $win] } } } # NSToplevel::ContentTop -- # # Return the y coordinate of the toplevel's insides. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSToplevel::ContentTop {win} { switch -- [Platform] { windows { return [winfo rooty $win] } unix { return [winfo rooty $win] } } } zangband/lib/script/tk/library/updowncontrol.tcl0000644000000000000000000003457110250356274021133 0ustar rootroot# File: updowncontrol.tcl # Purpose: MS Windows-like up-down control # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSUpDownControl { variable Priv variable Fill variable optionTable [list \ "script" "-command" "command" "" doCommand \ "string" "-state" "state" "normal" doState \ "string" "-reliefup" "relief,up" "raised" doRelief \ "string" "-reliefdown" "relief,down" "raised" doRelief \ "pixels" "-width" "width" "16" doSize \ "pixels" "-height" "height" "24" doSize \ "pixels" "-widtharrow" "width,arrow" "5" doSize \ "pixels" "-heightarrow" "height,arrow" "3" doSize \ "orient" "-orient" "orient" "vertical" doOrient ] } # NSUpDownControl::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::InitModule {} { variable Priv variable Fill set Fill(raised,out,topleft) $::SystemButtonFace set Fill(raised,out,bottomright) $::System3dDarkShadow set Fill(raised,in,topleft) $::SystemButtonHighlight set Fill(raised,in,bottomright) $::SystemButtonShadow set Fill(sunken,out,topleft) $::SystemButtonShadow set Fill(sunken,out,bottomright) $::SystemButtonHighlight set Fill(sunken,in,topleft) $::System3dDarkShadow set Fill(sunken,in,bottomright) $::SystemButtonFace bind NSUpDownControlBindTag \ "NSUpDownControl::ButtonPress %W %x %y" bind NSUpDownControlBindTag \ "NSUpDownControl::ButtonDown %W" bind NSUpDownControlBindTag \ "NSUpDownControl::ButtonUp %W %x %y" bind NSUpDownControlBindTag \ "NSUpDownControl::Resize %W %w %h" return } # NSUpDownControl::NSUpDownControl -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::NSUpDownControl {oop path args} { variable optionTable variable Priv set canvas $path canvas $canvas -borderwidth 0 -highlightthickness 0 -scrollregion {0 0 0 0} set canvasCmd upDownControl$canvas rename $canvas $canvasCmd set Priv(oop,$path) $oop # This is the command seen by the outside set command $path interp alias {} ::$command {} ::NSUpDownControl::Command $oop Info $oop command $command # The border (up) $canvasCmd create line 0 1 0 0 1 0 -fill $::SystemButtonFace -tags out.topleft,up $canvasCmd create line 0 1 1 1 1 0 -fill $::System3dDarkShadow -tags out.bottomright,up $canvasCmd create line 0 1 0 0 1 0 -fill $::SystemButtonHighlight -tags in.topleft,up $canvasCmd create line 0 1 1 1 1 0 -fill $::SystemButtonShadow -tags in.bottomright,up # The border (down) $canvasCmd create line 0 1 0 0 1 0 -fill $::SystemButtonFace -tags out.topleft,down $canvasCmd create line 0 1 1 1 1 0 -fill $::System3dDarkShadow -tags out.bottomright,down $canvasCmd create line 0 1 0 0 1 0 -fill $::SystemButtonHighlight -tags in.topleft,down $canvasCmd create line 0 1 1 1 1 0 -fill $::SystemButtonShadow -tags in.bottomright,down # The arrow (up) $canvasCmd create polygon 0 0 2 0 1 1 -outline black -tags arrowbg,up $canvasCmd create polygon 0 0 2 0 1 1 -outline black -tags arrow,up # The arrow (down) $canvasCmd create polygon 0 0 2 0 1 1 -outline black -tags arrowbg,down $canvasCmd create polygon 0 0 2 0 1 1 -outline black -tags arrow,down NSUtils::DestroyObjectWithWidget NSUpDownControl $oop $canvas bindtags $canvas [concat [bindtags $canvas] NSUpDownControlBindTag] Info $oop canvas $canvas Info $oop canvasCmd $canvasCmd foreach {type arg info default flags} $optionTable { Info $oop $info $default } eval Configure $oop $args return } # NSUpDownControl::~NSUpDownControl -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::~NSUpDownControl {oop} { return } # NSUpDownControl::Command -- # # This is the command called for an up-down control. The widget pathname # command is a wrapper around this. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::Command {oop command args} { switch -- $command { canvas { return [eval [Info $oop canvasCmd] $args] } cget { set option [lindex $args 0] # The Tk tabbing engine calls this! if {[string equal $option -state]} { return [Info $oop state] } else { return [eval [Info $oop canvasCmd] cget $args] } } configure { return [eval Configure $oop $args] } info { return [eval Info $oop $args] } default { error "unknown command \"$command\"" } } return } # NSUpDownControl::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::Info {oop info args} { global NSUpDownControl # Verify the object NSObject::CheckObject NSUpDownControl $oop # Set info if {[llength $args]} { switch -- $info { default { set NSUpDownControl($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSUpDownControl($oop,$info) } } } return } # NSUpDownControl::Configure -- # # Change configuration options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::Configure {oop args} { variable optionTable if {[llength $args] & 1} { error "wrong number of arguments: must be \"[Info $oop command] configure ?option value?...\"" } foreach {type arg info default flags} $optionTable { foreach flag $flags { set doFlag($flag) 0 } } foreach error {0 1} { # First pass: Set options to new values if {!$error} { foreach {option value} $args { set match 0 foreach {type arg info default flags} $optionTable { if {[string equal $option $arg]} { set savedOptions($info) [Info $oop $info] if {[string equal $type boolean]} { set value [NSUtils::GetBoolean $value] } Info $oop $info $value foreach flag $flags { set doFlag($flag) 1 } set match 1 break } } if {!$match} { error "unknown option \"$option\"" } } # Second pass: restore options to old values. } else { foreach name [array names savedOptions] { Info $oop $name $savedOptions($name) } } # Success break } WorldChanged $oop if {$error} { error $errorString } return } # NSUpDownControl::WorldChanged -- # # Called when configuration options change. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::WorldChanged {oop} { set canvas [Info $oop canvas] set canvasCmd [Info $oop canvasCmd] set state [Info $oop state] set isActive [string equal $state active] set isDisabled [string equal $state disabled] # Set the size of the canvas set width [Info $oop width] set height [Info $oop height] $canvasCmd configure -width $width -height $height # Configure the border (up) if 1 { ConfigBorder $oop 0 0 $width [expr {$height / 2}] out up ConfigBorder $oop 1 1 [expr {$width - 2}] [expr {$height / 2 - 2}] in up set y [expr {$height / 2}] # If the control is an odd number of pixels tall, then move # the down button down 1 pixel set fiddle [expr {($height & 1) != 0}] incr y $fiddle ConfigBorder $oop 0 $y $width [expr {$height / 2}] out down ConfigBorder $oop 1 [expr {$y + 1}] [expr {$width - 2}] [expr {$height / 2 - 2}] in down } else { set bumpUp 0 if {[string equal [Info $oop relief,up] raised]} { set fillTopLeftOut $::SystemButtonFace set fillBottomRightOut $::System3dDarkShadow set fillTopLeftIn $::SystemButtonHighlight set fillBottomRightIn $::SystemButtonShadow } else { set fillTopLeftOut $::SystemButtonShadow set fillBottomRightOut $::SystemButtonHighlight set fillTopLeftIn $::System3dDarkShadow set fillBottomRightIn $::SystemButtonFace set bumpUp 1 } set top 0 set left 0 set right [expr {$width - 1}] set bottom [expr {$height / 2 - 1}] $canvasCmd itemconfigure out.topleft,up -fill $fillTopLeftOut $canvasCmd itemconfigure out.bottomright,up -fill $fillBottomRightOut $canvasCmd coords out.topleft,up \ 0 $bottom 0 0 $right 0 $canvasCmd coords out.bottomright,up \ 0 $bottom $right $bottom $right -1 incr top incr left incr right -1 incr bottom -1 $canvasCmd itemconfigure in.topleft,up -fill $fillTopLeftIn $canvasCmd itemconfigure in.bottomright,up -fill $fillBottomRightIn $canvasCmd coords in.topleft,up \ $left $bottom $left $top $right $left $canvasCmd coords in.bottomright,up \ $left $bottom $right $bottom $right [expr {$top - 1}] # If the control is an odd number of pixels tall, then move # the down button down 1 pixel set fiddle [expr {($height & 1) != 0}] # Configure the border (down) set bumpDown 0 if {[string equal [Info $oop relief,down] raised]} { set fillTopLeftOut $::SystemButtonFace set fillBottomRightOut $::System3dDarkShadow set fillTopLeftIn $::SystemButtonHighlight set fillBottomRightIn $::SystemButtonShadow } else { set fillTopLeftOut $::SystemButtonShadow set fillBottomRightOut $::SystemButtonHighlight set fillTopLeftIn $::System3dDarkShadow set fillBottomRightIn $::SystemButtonFace set bumpDown 1 } set left 0 set right [expr {$width - 1}] set top [expr {$fiddle + $height / 2}] set bottom [expr {$height - 1}] $canvasCmd itemconfigure out.topleft,down -fill $fillTopLeftOut $canvasCmd itemconfigure out.bottomright,down -fill $fillBottomRightOut $canvasCmd coords out.topleft,down \ $left $bottom $left $top $right $top $canvasCmd coords out.bottomright,down \ $left $bottom $right $bottom $right [expr {$top - 1}] incr top incr left incr right -1 incr bottom -1 $canvasCmd itemconfigure in.topleft,down -fill $fillTopLeftIn $canvasCmd itemconfigure in.bottomright,down -fill $fillBottomRightIn $canvasCmd coords in.topleft,down \ $left $bottom $left $top $right $top $canvasCmd coords in.bottomright,down \ $left $bottom $right $bottom $right [expr {$top - 1}] } # The arrow (up) set arrowWidth [Info $oop width,arrow] set arrowHeight [Info $oop height,arrow] if {$isDisabled} { set fill $::SystemButtonShadow set fillbg $::SystemButtonHighlight } else { set fill Black set fillbg "" } set bumpUp [string equal [Info $oop relief,up] sunken] set x [expr {$bumpUp + ($width - $arrowWidth) / 2}] set y [expr {$bumpUp + ($height / 2 - $arrowHeight) / 2}] $canvasCmd itemconfigure arrow,up -fill $fill -outline $fill $canvasCmd itemconfigure arrowbg,up -fill $fillbg -outline $fillbg $canvasCmd coords arrow,up $x [expr {$y + $arrowHeight - 1}] \ [expr {$x + $arrowWidth - 1}] [expr {$y + $arrowHeight - 1}] \ [expr {$x + $arrowWidth / 2}] $y incr x incr y $canvasCmd coords arrowbg,up $x [expr {$y + $arrowHeight - 1}] \ [expr {$x + $arrowWidth - 1}] [expr {$y + $arrowHeight - 1}] \ [expr {$x + $arrowWidth / 2}] $y # The arrow (down) set bumpDown [string equal [Info $oop relief,down] sunken] set x [expr {$bumpDown + ($width - $arrowWidth) / 2}] set y [expr {$fiddle + $bumpDown + $height / 2 + ($height / 2 - $arrowHeight) / 2}] $canvasCmd itemconfigure arrow,down -fill $fill -outline $fill $canvasCmd itemconfigure arrowbg,down -fill $fillbg -outline $fillbg $canvasCmd coords arrow,down $x $y [expr {$x + $arrowWidth - 1}] $y \ [expr {$x + $arrowWidth / 2}] [expr {$y + $arrowHeight - 1}] incr x incr y $canvasCmd coords arrowbg,down $x $y [expr {$x + $arrowWidth - 1}] $y \ [expr {$x + $arrowWidth / 2}] [expr {$y + $arrowHeight - 1}] return } proc NSUpDownControl::ConfigBorder {oop x y wid hgt out up} { variable Fill set canvasCmd [Info $oop canvasCmd] set relief [Info $oop relief,$up] $canvasCmd itemconfigure $out.topleft,$up -fill $Fill($relief,$out,topleft) $canvasCmd itemconfigure $out.bottomright,$up -fill $Fill($relief,$out,bottomright) set left $x set top $y set right [expr {$left + $wid - 1}] set bottom [expr {$top + $hgt - 1}] $canvasCmd coords $out.topleft,$up \ $left $bottom $left $top $right $top $canvasCmd coords $out.bottomright,$up \ $left $bottom $right $bottom $right [expr {$top - 1}] return } # NSUpDownControl::ButtonPress -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::ButtonPress {w x y} { variable Priv set oop $Priv(oop,$w) if {[string compare [Info $oop state] disabled]} { if {$y < [winfo height $w] / 2} { set press up set delta +1 } else { set press down set delta -1 } Configure $oop -relief$press sunken Info $oop press $press Invoke $oop $delta Info $oop afterId [after 500 NSUpDownControl::Auto $oop $delta] } return } proc NSUpDownControl::Auto {oop delta} { Invoke $oop $delta Info $oop afterId [after 200 NSUpDownControl::Auto $oop $delta] return } # NSUpDownControl::ButtonUp -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::ButtonUp {w x y} { variable Priv set oop $Priv(oop,$w) if {[string compare [Info $oop state] disabled]} { Configure $oop -state active -relief[Info $oop press] raised after cancel [Info $oop afterId] Info $oop afterId "" return set canvas [Info $oop canvas] set pointerx [winfo pointerx $canvas] set pointery [winfo pointery $canvas] if {[winfo containing $pointerx $pointery] == $canvas} { # nothing } } return } # NSUpDownControl::ButtonDown -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::ButtonDown {w} { return variable Priv set oop $Priv(oop,$w) if {[string compare [Info $oop state] disabled]} { Configure $oop -relief[Info $oop press] sunken } return } # NSUpDownControl::Invoke -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::Invoke {oop delta} { set command [Info $oop command] if {[string length $command]} { uplevel #0 $command $delta } return } # NSUpDownControl::Resize -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownControl::Resize {w width height} { variable Priv set oop $Priv(oop,$w) Configure $oop -width $width -height $height return } # updowncontrol -- # # Call this to create a new up-down control. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc updowncontrol {args} { set oop [eval NSObject::New NSUpDownControl $args] return [NSUpDownControl::Info $oop command] } zangband/lib/script/tk/library/updownentry.tcl0000644000000000000000000001175710250356274020615 0ustar rootroot# File: updownentry.tcl # Purpose: MS Windows-like entry + up-down control # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSUpDownEntry { variable Priv variable optionTable [list \ ] } # NSUpDownEntry::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::InitModule {} { NSModule::LoadIfNeeded NSUpDownControl return } # NSUpDownEntry::NSUpDownEntry -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::NSUpDownEntry {oop path args} { variable optionTable variable Priv set frame $path frame $frame -borderwidth 2 -relief sunken set frameCmd upDownEntry$frame rename $frame $frameCmd set Priv(oop,$path) $oop # This is the command seen by the outside set command $path interp alias {} ::$command {} ::NSUpDownEntry::Command $oop Info $oop command $command # The entry entry $frame.entry -borderwidth 0 # The up-down control updowncontrol $frame.udc -widtharrow 3 -heightarrow 2 \ -width 11 -height [winfo reqheight $frame.entry] pack $frame.entry -side left -fill x pack $frame.udc -side right -fill y NSUtils::DestroyObjectWithWidget NSUpDownEntry $oop $frame Info $oop frame $frame Info $oop frameCmd $frameCmd Info $oop entry $frame.entry Info $oop udc $frame.udc foreach {type arg info default flags} $optionTable { Info $oop $info $default } eval Configure $oop $args return } # NSUpDownEntry::~NSUpDownEntry -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::~NSUpDownEntry {oop} { return } # NSUpDownEntry::Command -- # # This is the command called for an up-down control. The widget pathname # command is a wrapper around this. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::Command {oop command args} { switch -- $command { cget - delete - icursor - insert - selection { return [eval [Info $oop entry] $command $args] } configure { return [eval Configure $oop $args] } entry { return [eval [Info $oop entry] $args] } info { return [eval Info $oop $args] } udc { return [eval [Info $oop udc] $args] } default { error "unknown command \"$command\"" } } return } # NSUpDownEntry::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::Info {oop info args} { global NSUpDownEntry # Verify the object NSObject::CheckObject NSUpDownEntry $oop # Set info if {[llength $args]} { switch -- $info { default { set NSUpDownEntry($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSUpDownEntry($oop,$info) } } } return } # NSUpDownEntry::Configure -- # # Change configuration options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::Configure {oop args} { variable optionTable if {[llength $args] & 1} { error "wrong number of arguments: must be \"[Info $oop command] configure ?option value?...\"" } foreach {type arg info default flags} $optionTable { foreach flag $flags { set doFlag($flag) 0 } } foreach error {0 1} { # First pass: Set options to new values if {!$error} { foreach {option value} $args { set skipIt 0 switch -- $option { -command { [Info $oop udc] configure $option $value set skipIt 1 } -font - -width - -state { [Info $oop entry] configure $option $value set skipIt 1 } } if {!$skipIt} { set match 0 foreach {type arg info default flags} $optionTable { if {[string equal $option $arg]} { set savedOptions($info) [Info $oop $info] if {[string equal $type boolean]} { set value [NSUtils::GetBoolean $value] } Info $oop $info $value foreach flag $flags { set doFlag($flag) 1 } set match 1 break } } if {!$match} { error "unknown option \"$option\"" } } } # Second pass: restore options to old values. } else { foreach name [array names savedOptions] { Info $oop $name $savedOptions($name) } } # Success break } WorldChanged $oop if {$error} { error $errorString } return } # NSUpDownEntry::WorldChanged -- # # Called when configuration options change. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUpDownEntry::WorldChanged {oop} { return } # updownentry -- # # Call this to create a new up-down entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc updownentry {args} { set oop [eval NSObject::New NSUpDownEntry $args] return [NSUpDownEntry::Info $oop command] } zangband/lib/script/tk/library/utils.tcl0000644000000000000000000003433510250356274017354 0ustar rootroot# File: utils.tcl # Purpose: various support routines # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # WindowBringToFront -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc WindowBringToFront {windowPath} { set focus [focus -lastfor $windowPath] if {![winfo ismapped $windowPath]} { wm state $windowPath normal if {[Platform unix]} { # Needed on X11 or big delay update } } raise $windowPath focus $focus return } # WindowPosition -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc WindowPosition {win x y} { # Save the current focus # set oldFocus [focus] # Withdraw the window, then update all the geometry information # so we know how big it wants to be, then center the window in the # display and de-iconify it. wm withdraw $win update idletasks set x2 [expr {([winfo screenwidth $win] - [winfo reqwidth $win]) / $x \ - [winfo vrootx $win]}] set y2 [expr {([winfo screenheight $win] - [winfo reqheight $win]) / $y \ - [winfo vrooty $win]}] wm geometry $win +$x2+$y2 # Seems required on Windows update idletasks wm deiconify $win #wm state $win normal #update idletasks # Restore focus # catch {focus $oldFocus} return } namespace eval NSUtils { variable Priv set Priv(grabDepth) 0 # namespace eval NSUtils } # NSUtils::GrabSave -- # # Sets a grab on the given window, saving the current focus and # grab for a later call to GrabRelease(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::GrabSave {win} { variable Priv set depth [incr Priv(grabDepth)] set Priv(grab,$depth,win) $win set Priv(grab,$depth,oldFocus) [focus] set Priv(grab,$depth,oldGrab) [grab current $win] if {$Priv(grab,$depth,oldGrab) != ""} { set Priv(grab,$depth,grabStatus) \ [grab status $Priv(grab,$depth,oldGrab)] } grab $win return } # NSUtils::GrabRelease -- # # Releases the grab and restores the focus saved in GrabSave(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::GrabRelease {win} { variable Priv set depth $Priv(grabDepth) incr Priv(grabDepth) -1 if {$Priv(grab,$depth,win) != $win} { error "grab release on wrong window" } catch {focus $Priv(grab,$depth,oldFocus)} grab release $win if {$Priv(grab,$depth,oldGrab) != ""} { if {$Priv(grab,$depth,grabStatus) == "global"} { grab -global $Priv(grab,$depth,oldGrab) } else { grab $Priv(grab,$depth,oldGrab) } } array unset Priv grab,$depth,* return } # NSUtils::StringBox -- # # Put up a window to get a string from the user. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::StringBox {args} { variable Priv set win .stringbox set title "Enter a string" set initial "" set entryWidth 30 set message "" set prompt String set buttons [list OK Cancel] set parent [winfo toplevel [focus]] set resultVar "" set maxLen 0 set type "" foreach {option value} $args { switch -- $option { -buttons - -title - -type - -initial - -message - -prompt - -parent { set [string range $option 1 end] $value } -entrywidth { if {![string is integer -strict $value]} { error "expected integer but got \"$value\"" } set entryWidth $value } -result { set resultVar $value } -maxlen { if {![string is integer -strict $value]} { error "expected integer but got \"$value\"" } set maxLen $value } default { error "unknown option \"$option\"" } } } toplevel $win wm title $win $title wm resizable $win no no wm transient $win $parent if {[string length $message]} { set msg $win.message message $msg \ -text $message -width 280 -anchor w } set frame $win.frame frame $frame \ -borderwidth 0 label $frame.label -text "$prompt:" entry $frame.text -width $entryWidth if {[string equal $type integer]} { if {$maxLen} { $frame.text configure \ -validatecommand "expr [string is integer %P] && (\[string length %P] <= $maxLen)" \ -invalidcommand bell -validate key } else { $frame.text configure \ -validatecommand "string is integer %P" \ -invalidcommand bell -validate key } } elseif {$maxLen} { $frame.text configure \ -validatecommand "expr \[string length %P] <= $maxLen" \ -invalidcommand bell -validate key } # Initial text $frame.text insert 0 $initial set frame $win.frameButton frame $frame \ -borderwidth 0 set n 1 foreach text $buttons { button $frame.button$n \ -text $text -command "set NSUtils::Priv(result) $n" \ -width 9 pack $frame.button$n \ -side left -padx 5 -pady 0 incr n } incr n -1 # Set the left-most button to the default button $frame.button1 configure -default active SetDefaultButton $win $frame.button1 # Return key selects default button bind $win \ "NSUtils::InvokeDefaultButton $win" # Escape key selects "cancel" button bind $win.frame.text \ "tkButtonInvoke $frame.button$n" if {[string length $message]} { pack [frame $win.framePad1 -borderwidth 0 -height 10] \ -side top pack $msg \ -side top -padx 5 -pady 0 -fill x } pack $win.frame.label \ -side left -padx 5 -pady 0 pack $win.frame.text \ -side left -padx 5 -pady 0 pack $win.frame \ -side top -padx 5 -pady 10 -anchor w pack $win.frameButton \ -side top -padx 5 -pady 0 -anchor e pack [frame $win.framePad2 -borderwidth 0 -height 10] \ -side top # handler sets Priv(result) bind $win "set NSUtils::Priv(result) 2" # Position window WindowPosition $win 2 3 # Set up a grab and claim focus too GrabSave $win focus $win.frame.text # Select the text $win.frame.text selection range 0 end # Wait for a button press set Priv(result) "" tkwait variable NSUtils::Priv(result) # Release grab and reset focus GrabRelease $win set result "" if {[winfo exists $win]} { switch $Priv(result) { 1 { set result [$win.frame.text get] } } } # Maybe the window is already destroyed catch { bind $win {} destroy $win } if {$resultVar != ""} { upvar $resultVar resultCode set resultCode [expr {$UtilsPriv(result) == 1}] } return $result } # NSUtils::ZapLabel -- # # Clears the given label (if it exists). Usually called as an # "after" command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::ZapLabel {label} { set text [$label cget -text] regsub -all \" $text "\\\"" text after 1200 " if {\[winfo exists $label]} { if {\[string equal \[$label cget -text] \"$text\"]} { $label configure -text {} } } " return } # NSUtils::GetBoolean -- # # Almost the same as Tcl_GetBoolean(). Given a string, return # 0 if the string is 0/false/no/off, return 1 if the string is # 1/true/yes/on. Case insensitive. Generate an error otherwise. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::GetBoolean {string} { if {[string is boolean -strict $string]} { return [string is true $string] } error "expected boolean value but got \"$string\"" } # NSUtils::HasCursor -- # # Return 1 if the cursor is over the given window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::HasCursor {window} { set pointerx [winfo pointerx $window] set pointery [winfo pointery $window] set window2 [winfo containing $pointerx $pointery] if {![string length $window2]} { return 0 } if {[string compare $window2 $window]} { return 0 } return 1 } # NSUtils::HasFocus -- # # Return 1 if the given window has the focus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::HasFocus {window} { if {[string equal [focus -displayof $window] $window]} { return 1 } return 0 } # NSUtils::ToplevelHasFocus -- # # Return 1 if any childof the given toplevel (or itself) has the focus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::ToplevelHasFocus {top} { set focus [focus -displayof $top] if {[string equal $focus $top]} { return 1 } if {[string match $top* $focus]} { return 1 } return 0 } # NSUtils::DestroyObjectWithWidget -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::DestroyObjectWithWidget {class oop widget} { set tag DestroyObjectBindTag$class$oop bindtags $widget [concat [bindtags $widget] $tag] bind $tag " if {\[string equal \$DestroyObjectWithWidget($class,$oop) %W\]} { NSObject::Delete $class $oop unset DestroyObjectWithWidget($class,$oop) } " # Hack -- Because a menu clone may end up with the bind tag we # added to the menu, we will ignore attempts to destroy the object # if the original widget isn't being destroyed (ie, the clone is) global DestroyObjectWithWidget set DestroyObjectWithWidget($class,$oop) $widget return } # NSUtils::TempFileName -- # # Return an unused file name in the given directory. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::TempFileName {dir} { set index 0 set fileName [file join $dir [format tempFile%04d $index]] while {[file exists $fileName]} { incr index set fileName [file join $dir [format tempFile%04d $index]] } return $fileName } # NSUtils::SetDefaultButton -- # # Specifies that a given Button should be the default button. Under # Win32, when a Button receives the input focus it also becomes the # default button; when no Button has the input focus, the toplevel's # default button becomes the active button once again. # # Arguments: # arg1 about arg1 # # Results: # What happened. bind Button {+ if {[%W cget -state] != "disabled"} { focus %W } } bind Radiobutton {+ if {[%W cget -state] != "disabled"} { focus %W } } bind Checkbutton {+ if {[%W cget -state] != "disabled"} { focus %W } } proc NSUtils::FocusIn_DefaultButton {widget} { global DefaultButton set toplevel [winfo toplevel $widget] if {![info exists DefaultButton(default,$toplevel)]} return # A Button has gained the input focus if {[winfo class $widget] == "Button"} { # The previous Button is no longer the default set current $DefaultButton(current,$toplevel) if {($current != $widget) && [winfo exists $current]} { $current configure -default normal } # The Button with the focus is now the default if {[$widget cget -default] != "active"} { $widget configure -default active } # Remember the current default button set DefaultButton(current,$toplevel) $widget # Done return } # At this point, a non-Button received the input focus # Get the default button for the toplevel set button $DefaultButton(default,$toplevel) if {![winfo exists $button]} return # The previous Button is no longer the default set current $DefaultButton(current,$toplevel) if {($current != $button) && [winfo exists $current]} { $current configure -default normal } # The toplevel's default Button is now the default if {[$button cget -default] != "active"} { $button configure -default active set DefaultButton(current,$toplevel) $button } return } proc NSUtils::SetDefaultButton {toplevel button} { global DefaultButton set DefaultButton(default,$toplevel) $button set DefaultButton(current,$toplevel) $button bind $toplevel {+ NSUtils::FocusIn_DefaultButton %W } return } # NSUtils::InvokeDefaultButton -- # # Find a button with "-state active" and invoke it. If there is # no such button then do nothing. Typically you bind this to # a toplevel for the event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::InvokeDefaultButton {widget} { foreach child [winfo children $widget] { if {[winfo class $child] == "Button"} { if {[$child cget -default] == "active"} { tkButtonInvoke $child break } } else { InvokeDefaultButton $child } } return } # NSUtils::SynchScrollBar -- # # There is a bug in the Windows Tk which prevents a scroll bar # from synchronizing when the window is not mapped. So I bind to # the event and synch the scroll bar here. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSUtils::SynchScrollBar {window scrollbar {moveto 0}} { if {![Platform windows]} return set tag SynchScrlBarBindTag$scrollbar bindtags $scrollbar [concat [bindtags $scrollbar] $tag] switch -- [$scrollbar cget -orient] { horizontal { set view xview } vertical { set view yview } } bind $tag " eval $scrollbar set \[$window $view] if {$moveto} { $window $view moveto 0.0 } " bind $tag " bind $tag {} bind $tag \ "NSWin98ToolbarButton::ButtonEnter %W" bind NSWin98ToolbarButtonBindTag \ "NSWin98ToolbarButton::ButtonLeave %W" bind NSWin98ToolbarButtonBindTag \ "NSWin98ToolbarButton::ButtonPress %W %x %y" bind NSWin98ToolbarButtonBindTag \ "NSWin98ToolbarButton::ButtonDown %W" bind NSWin98ToolbarButtonBindTag \ "NSWin98ToolbarButton::ButtonUp %W %x %y" bind NSWin98ToolbarButtonBindTag \ "NSWin98ToolbarButton::FocusOut %W" set Priv(focus) "" set Priv(posted) 0 return } # NSWin98ToolbarButton::NSWin98ToolbarButton -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::NSWin98ToolbarButton {oop path args} { variable optionTable variable Priv set canvas $path canvas $canvas -borderwidth 0 -highlightthickness 0 -scrollregion {0 0 0 0} set canvasCmd win98Canvas$canvas rename $canvas $canvasCmd set Priv(oop,$canvas) $oop # This is the command seen by the outside set command $path if 1 { interp alias {} ::$command {} ::NSWin98ToolbarButton::Command $oop } else { proc ::$command args \ "eval NSWin98ToolbarButton::Command $oop \$args" } Info $oop command $command # The border $canvasCmd create line 0 1 0 0 1 0 -fill $::SystemButtonHighlight -tags topleft $canvasCmd create line 0 1 1 1 1 0 -fill $::SystemButtonShadow -tags bottomright # The menu-button $canvasCmd create line 0 1 0 0 1 0 -fill $::SystemButtonHighlight -tags topleftmenu $canvasCmd create line 0 1 1 1 1 0 -fill $::SystemButtonShadow -tags bottomrightmenu $canvasCmd create polygon 0 0 4 0 2 0 -outline black -tags combobg $canvasCmd create polygon 0 0 4 0 2 0 -outline black -tags combo # The image $canvasCmd create image 0 0 -image Image_Empty -anchor n -tags image # The label $canvasCmd create text 0 0 -text "" -tags labelbg -anchor s $canvasCmd create text 0 0 -text "" -tags label -anchor s NSUtils::DestroyObjectWithWidget NSWin98ToolbarButton $oop $canvas bindtags $canvas [concat [bindtags $canvas] NSWin98ToolbarButtonBindTag] Info $oop canvas $canvas Info $oop canvasCmd $canvasCmd Info $oop menuVisible 0 Info $oop inFocusOut 0 Info $oop oldLabelVar "" foreach {type arg info default flags} $optionTable { Info $oop $info $default } eval Configure $oop $args return } # NSWin98ToolbarButton::~NSWin98ToolbarButton -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::~NSWin98ToolbarButton {oop} { variable Priv catch { set labelVar [Info $oop labelVar] if {[string length $labelVar]} { trace vdelete $labelVar w \ "NSWin98ToolbarButton::LabelVarChanged $oop" } } catch { set canvas [Info $oop canvas] unset Priv(oop,$canvas) if {$Priv(posted) == $oop} { set Priv(posted) 0 } } return } # NSWin98ToolbarButton::Command -- # # This is the command called for a toolbar button. The widget pathname # command is a wrapper around this. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::Command {oop command args} { switch -- $command { canvas { return [eval [Info $oop canvasCmd] $args] } cget { set option [lindex $args 0] # The Tk tabbing engine calls this! if {[string equal $option -state]} { return [Info $oop state] } else { return [eval [Info $oop canvasCmd] cget $args] } } configure { return [eval Configure $oop $args] } hidemenu { return [eval HideMenu $oop $args] } info { return [eval Info $oop $args] } default { error "unknown command \"$command\"" } } return } # NSWin98ToolbarButton::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::Info {oop info args} { global NSWin98ToolbarButton # Verify the object NSObject::CheckObject NSWin98ToolbarButton $oop # Set info if {[llength $args]} { switch -- $info { default { set NSWin98ToolbarButton($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSWin98ToolbarButton($oop,$info) } } } return } # NSWin98ToolbarButton::Configure -- # # Change configuration options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::Configure {oop args} { variable optionTable if {[llength $args] & 1} { error "wrong number of arguments: must be \"[Info $oop command] configure ?option value?...\"" } foreach {type arg info default flags} $optionTable { foreach flag $flags { set doFlag($flag) 0 } } foreach error {0 1} { # First pass: Set options to new values if {!$error} { foreach {option value} $args { set match 0 foreach {type arg info default flags} $optionTable { if {[string equal $option $arg]} { set savedOptions($info) [Info $oop $info] if {[string equal $type boolean]} { set value [NSUtils::GetBoolean $value] } Info $oop $info $value foreach flag $flags { set doFlag($flag) 1 } set match 1 break } } if {!$match} { error "unknown option \"$option\"" } } # Second pass: restore options to old values. } else { foreach name [array names savedOptions] { Info $oop $name $savedOptions($name) } } # if {([Info $oop image] == "") && ([Info $oop label] == "")} { # set errorString "must specify image and/or label" # continue # } set oldLabelVar [Info $oop oldLabelVar] set labelVar [Info $oop labelVar] if {[string compare $oldLabelVar $labelVar]} { if {[string length $oldLabelVar]} { trace vdelete $oldLabelVar w \ "NSWin98ToolbarButton::LabelVarChanged $oop" } # global $labelVar trace variable $labelVar w \ "NSWin98ToolbarButton::LabelVarChanged $oop" Info $oop oldLabelVar $labelVar LabelVarChanged $oop } # Success break } WorldChanged $oop if {$error} { error $errorString } return } proc NSWin98ToolbarButton::Max {a b} { return [expr {$a > $b ? $a : $b}] } # NSWin98ToolbarButton::WorldChanged -- # # Called when configuration options change. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::WorldChanged {oop} { set canvas [Info $oop canvas] set canvasCmd [Info $oop canvasCmd] set hasMenu [Info $oop hasMenu] set onlyMenu [Info $oop onlyMenu] set showImage [Info $oop showImage] set showLabel [Info $oop showLabel] set state [Info $oop state] set isActive [string equal $state active] set isDisabled [string equal $state disabled] set width 0 set height 0 set minWidth 0 set minHeight 0 set padX [Info $oop padX] set padY [Info $oop padY] if {$showLabel} { set width [Max $width [Info $oop widthLabel]] incr height [Info $oop heightLabel] set font [$canvasCmd itemcget label -font] set minWidth [Max $minWidth [font measure $font [Info $oop label]]] set minHeight [Max $minHeight [font metrics $font -linespace]] } if {$showImage} { set width [Max $width [Info $oop widthImage]] incr height [Info $oop heightImage] # if {!$showLabel && $hasMenu && $onlyMenu} { # incr width [expr {$padX + 5}] # } set image [Info $oop imageNormal] if {![string length $image]} { set image [Info $oop image] } if {![string length $image]} { set image Image_Empty } set minWidth [Max $minWidth [image width $image]] set minHeight [Max $minHeight [image height $image]] } if {$showImage && $showLabel} { set width [Max $width [Info $oop width]] set height [Max $height [Info $oop height]] } if {$hasMenu} { if {$onlyMenu} { # set width [Max $width [expr {$width + $padX + 5}]] set minWidth [Max $minWidth [expr {$minWidth + $padX + 5}]] } else { # set width [Max $width [expr {$width + 13}]] # set minWidth [Max $minWidth [expr {$minWidth + 13}]] } } #puts "minWidth $minWidth width $width minHeight $minHeight height $height" incr minWidth [expr {$padX * 2}] incr width [expr {$padX * 2}] if {$width < $minWidth} { set width $minWidth } incr minHeight [expr {$padY * 2}] incr height [expr {$padY * 2}] if {$height < $minHeight} { set height $minHeight } set canvasWidth $width if {$hasMenu && !$onlyMenu} { incr canvasWidth 13 } $canvasCmd configure -width $canvasWidth -height $height # Configure the border set bump 0 if {$isActive} { if {[Info $oop relief] == "raised"} { set fillTopLeft $::SystemButtonHighlight set fillBottomRight $::SystemButtonShadow } else { set fillTopLeft $::SystemButtonShadow set fillBottomRight $::SystemButtonHighlight set bump 1 } } else { set fillTopLeft [$canvasCmd cget -background] set fillBottomRight [$canvasCmd cget -background] } set widLessOne [expr {$width - 1}] set hgtLessOne [expr {$height - 1}] $canvasCmd itemconfigure topleft -fill $fillTopLeft $canvasCmd itemconfigure bottomright -fill $fillBottomRight $canvasCmd coords topleft 0 $hgtLessOne 0 0 $widLessOne 0 $canvasCmd coords bottomright 0 $hgtLessOne $widLessOne $hgtLessOne $widLessOne -1 # The menu-button if {$hasMenu && !$onlyMenu} { set menuLeft [expr {$canvasWidth - 13}] set widLessOne [expr {$canvasWidth - 1}] set hgtLessOne [expr {$height - 1}] set oldBump $bump if {[Info $oop reliefMenu] != [Info $oop relief]} { if {[Info $oop reliefMenu] == "raised"} { set fillTopLeft $::SystemButtonHighlight set fillBottomRight $::SystemButtonShadow } elseif {[Info $oop reliefMenu] == "sunken"} { set fillTopLeft $::SystemButtonShadow set fillBottomRight $::SystemButtonHighlight set bump 1 } else { set fillTopLeft [$canvasCmd cget -background] set fillBottomRight [$canvasCmd cget -background] } } $canvasCmd itemconfigure topleftmenu -fill $fillTopLeft $canvasCmd itemconfigure bottomrightmenu -fill $fillBottomRight $canvasCmd coords topleftmenu $menuLeft $hgtLessOne $menuLeft 0 $widLessOne 0 $canvasCmd coords bottomrightmenu $menuLeft $hgtLessOne $widLessOne \ $hgtLessOne $widLessOne 0 set iWid 5 set iHgt 3 set x [expr {$menuLeft + (13 - $iWid) / 2 + $bump}] set y [expr {($height - $iHgt) / 2 + $bump}] if {[string equal $state disabled]} { set fill $::SystemButtonShadow set fillbg $::SystemButtonHighlight } else { set fill Black set fillbg "" } $canvasCmd itemconfigure combo -fill $fill -outline $fill $canvasCmd itemconfigure combobg -fill $fillbg -outline $fillbg $canvasCmd coords combo $x $y [expr {$x + 4}] $y [expr {$x + 2}] [expr {$y + 2}] incr x incr y $canvasCmd coords combobg $x $y [expr {$x + 4}] $y [expr {$x + 2}] [expr {$y + 2}] set bump $oldBump } elseif {$hasMenu} { set iWid 5 set iHgt 3 if {$showImage} { set state [string totitle $state] set image [Info $oop image$state] if {![string length $image]} { set image [Info $oop image] } if {![string length $image]} { set image Image_Empty } set xWid [image width $image] } else { set xWid [font measure [$canvasCmd itemcget label -font] \ [Info $oop label]] } set x [expr {$width - ($width - ($xWid + $padX + $iWid)) / 2 - $iWid + $bump}] if {$showLabel && $showImage} { set y [expr {$padY + ([Info $oop heightImage] - $iHgt) / 2 + $bump}] } else { set y [expr {($height - $iHgt) / 2 + $bump}] } if {$isDisabled} { set fill $::SystemButtonShadow set fillbg $::SystemButtonHighlight } else { set fill Black set fillbg "" } $canvasCmd itemconfigure combo -fill $fill -outline $fill $canvasCmd itemconfigure combobg -fill $fillbg -outline $fillbg $canvasCmd coords combo $x $y [expr {$x + 4}] $y [expr {$x + 2}] [expr {$y + 2}] incr x incr y $canvasCmd coords combobg $x $y [expr {$x + 4}] $y [expr {$x + 2}] [expr {$y + 2}] $canvasCmd itemconfigure topleftmenu -fill "" $canvasCmd itemconfigure bottomrightmenu -fill "" } else { $canvasCmd itemconfigure combo -fill "" -outline "" $canvasCmd itemconfigure combobg -fill "" -outline "" $canvasCmd itemconfigure topleftmenu -fill "" $canvasCmd itemconfigure bottomrightmenu -fill "" } # Configure the image if {$showImage} { set state [string totitle $state] set image [Info $oop image$state] if {![string length $image]} { set image [Info $oop image] } if {![string length $image]} { set image Image_Empty } set iWid [image width $image] set iHgt [image height $image] if {$hasMenu && $onlyMenu} { set x [expr {$width / 2 - 4 + $bump}] } else { set x [expr {$width / 2 + $bump}] } if {$showLabel} { set y [expr {$padY + [Info $oop heightImage] / 2 + $bump}] set anchor center } else { set y [expr {$height / 2 + $bump}] set anchor center } $canvasCmd itemconfigure image -image $image -anchor $anchor $canvasCmd coords image $x $y } else { $canvasCmd itemconfigure image -image Image_Empty } # Configure the label if {$showLabel} { set text [Info $oop label] if {$isDisabled} { set fill $::SystemButtonShadow set fillbg $::SystemButtonHighlight } else { set fill Black set fillbg "" } if {!$showImage && $hasMenu && $onlyMenu} { set x [expr {($width - ($padX + 5)) / 2 + $bump}] } else { set x [expr {$width / 2 + $bump}] } if {$showImage} { set y [expr {$height - $padY + $bump}] set anchor s } else { set y [expr {$height / 2 + $bump}] set anchor center } $canvasCmd itemconfigure label -text $text -fill $fill -anchor $anchor $canvasCmd itemconfigure labelbg -text $text -fill $fillbg -anchor $anchor $canvasCmd coords label $x $y $canvasCmd coords labelbg [incr x] [incr y] } else { $canvasCmd itemconfigure label -fill "" $canvasCmd itemconfigure labelbg -fill "" } return } # NSWin98ToolbarButton::Invoke -- # # Call the client command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::Invoke {oop} { set command [Info $oop invokeCmd] if {[string length $command]} { uplevel #0 $command } return } # NSWin98ToolbarButton::ShowMenu -- # # Call the client command to show the menu. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::ShowMenu {oop} { variable Priv set canvas [Info $oop canvas] set command [Info $oop menuCmd] if {![string length $command]} { return } # This is like the Tk menubutton code if {$Priv(posted)} { HideMenu $Priv(posted) } set Priv(focus) [focus] if {[string length $Priv(focus)]} { if {[string compare [winfo toplevel $Priv(focus)] \ [winfo toplevel $canvas]]} { set Priv(focus) [focus -lastfor [winfo toplevel $canvas]] } } set Priv(posted) $oop Info $oop menuVisible 1 # focus $canvas uplevel #0 $command [Info $oop command] return } # NSWin98ToolbarButton::HideMenu -- # # Called when the menu is unposted. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::HideMenu {oop} { variable Priv if {[catch {NSObject::CheckObject NSWin98ToolbarButton $oop}]} { return } if {![Info $oop menuVisible]} { return } # catch {focus $Priv(focus)} set Priv(focus) "" set Priv(posted) 0 Info $oop menuVisible 0 set canvas [Info $oop canvas] set pointerx [winfo pointerx $canvas] set pointery [winfo pointery $canvas] if {[string compare [winfo containing $pointerx $pointery] $canvas]} { ButtonLeave $canvas } else { Configure $oop -state active -relief raised -reliefmenu raised } return } # NSWin98ToolbarButton::ButtonEnter -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::ButtonEnter {w} { variable Priv set oop $Priv(oop,$w) if {([Info $oop state] != "disabled") && ![Info $oop menuVisible]} { Configure $oop -state active -relief raised -reliefmenu raised } return } # NSWin98ToolbarButton::ButtonLeave -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::ButtonLeave {w} { variable Priv set oop $Priv(oop,$w) if {[string compare [Info $oop state] disabled] && ![Info $oop menuVisible]} { Configure $oop -state normal -relief flat -reliefmenu flat } return } # NSWin98ToolbarButton::ButtonPress -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::ButtonPress {w x y} { variable Priv set oop $Priv(oop,$w) if {[string compare [Info $oop state] disabled]} { if {[Info $oop menuVisible]} { Configure $oop -state active -reliefmenu raised HideMenu $oop } elseif {[Info $oop hasMenu] && [Info $oop onlyMenu]} { Configure $oop -state active -relief sunken ShowMenu $oop } elseif {[Info $oop hasMenu] && ($x >= ([winfo width $w] - 13))} { Configure $oop -state active -reliefmenu sunken ShowMenu $oop } else { Configure $oop -state active -relief sunken \ -reliefmenu sunken } } return } # NSWin98ToolbarButton::ButtonUp -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::ButtonUp {w x y} { variable Priv set oop $Priv(oop,$w) if {([Info $oop state] != "disabled") && ![Info $oop menuVisible]} { Configure $oop -state active -relief raised -reliefmenu raised set canvas [Info $oop canvas] set pointerx [winfo pointerx $canvas] set pointery [winfo pointery $canvas] if {[winfo containing $pointerx $pointery] == $canvas} { if {![Info $oop hasMenu] || (![Info $oop onlyMenu] && \ ($x < [winfo width $w] - 13))} { Invoke $oop } } } return } # NSWin98ToolbarButton::ButtonDown -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::ButtonDown {w} { variable Priv set oop $Priv(oop,$w) if {[Info $oop state] != "disabled"} { if {[Info $oop menuVisible]} { } else { Configure $oop -state active -relief sunken -reliefmenu sunken } } return } # NSWin98ToolbarButton::LabelVarChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::LabelVarChanged {oop args} { set labelVar [Info $oop labelVar] global $labelVar Configure $oop -label [set $labelVar] return } # NSWin98ToolbarButton::FocusOut -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWin98ToolbarButton::FocusOut {w} { ##### return ##### variable Priv set oop $Priv(oop,$w) if {[Info $oop menuVisible]} { Info $oop inFocusOut 1 HideMenu $oop Info $oop inFocusOut 0 } return } # win98button -- # # Call this to create a new button. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc win98button {args} { set oop [eval NSObject::New NSWin98ToolbarButton $args] return [NSWin98ToolbarButton::Info $oop command] } zangband/lib/script/tk/library/window-manager.tcl0000644000000000000000000001414310250356274021126 0ustar rootroot# File: window-manager.tcl # Purpose: toplevel management # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSWindowManager { variable Priv # namespace eval NSWindowManager } # NSWindowManager::RegisterWindow -- # # Introduce a toplevel to the NSWindowManager. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::RegisterWindow {winName window geomCmd setupCmd displayCmd} { variable Priv # if {[info exists Priv($winName,window)]} return set Priv($winName,win) $window set Priv($winName,geomCmd) $geomCmd set Priv($winName,displayCmd) $displayCmd set Priv($winName,setupCmd) $setupCmd if {![info exists Priv($winName,geomRequest)]} { set Priv($winName,geomRequest) "" } set Priv($winName,first) 1 set Priv($winName,setup) 0 return } # NSWindowManager::UnregisterWindow -- # # Forget about a toplevel registered with the NSWindowManager. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::UnregisterWindow {winName} { variable Priv array unset Priv *,win return } # NSWindowManager::RequestGeometry -- # # Request geometry for a toplevel. The toplevel might not even exist # yet, and in any case nothing interesting happens. Only when # Setup() is called for the first time will the geometry passed to # this command be used. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::RequestGeometry {winName geometry} { variable Priv set screenHeight [winfo screenheight .] set screenWidth [winfo screenwidth .] # Sanity check if {[scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y] != 6} return if {($width < 1) || ($height < 1)} return # Note: # +-N means N pixels to the left of the left side of the screen # -N means N pixels to the left of the right side of the screen if {[string equal $xs +-]} { set x -$x } elseif {[string equal $xs -]} { set x [expr {$screenWidth - $width - $x}] } if {[string equal $ys +-]} { set y -$y } elseif {[string equal $ys -]} { set y [expr {$screenHeight - $height - $y}] } # Insure part of the titlebar is visible if {($x + $width <= 0) || ($x >= $screenWidth)} return if {($y + 20 <= 0) || ($y >= $screenHeight)} return # Remember the requested geometry set Priv($winName,geomRequest) $geometry return } # NSWindowManager::Setup -- # # Display the toplevel offscreen. Then set the toplevel geometry # and withdraw the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::Setup {winName} { variable Priv if {$Priv($winName,setup)} return set win $Priv($winName,win) NSToplevel::NaturalSize $win $Priv($winName,setupCmd) set geometry $Priv($winName,geomRequest) if {![string length $geometry]} { set geometry [uplevel #0 $Priv($winName,geomCmd)] } if {[scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y] != 6} { error "bad geometry \"$geometry\"" } # If this window is not resizeable, then ignore the given # height or width and use the dimension requested for the # window. scan [wm resizable $win] "%s %s" resizeH resizeV if {!$resizeH} {set width [winfo reqwidth $win]} if {!$resizeV} {set height [winfo reqheight $win]} wm geometry $win ${width}x$height update set command $Priv($winName,setupCmd) if {[string length $command]} { uplevel #0 $command } wm geometry $win $xs$x$ys$y if {[Platform unix]} { update idletasks } wm withdraw $win update set Priv($winName,setup) 1 return } # NSWindowManager::Arrange -- # # Set the desired geometry for the toplevel. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::Arrange {winName} { variable Priv # Check for exists, because I want to clear any scheduled # geometry request below. if {[info exists Priv($winName,win)]} { set geometry [uplevel #0 $Priv($winName,geomCmd)] wm geometry $Priv($winName,win) $geometry update idletasks } # Hack -- Clear any geometry request set Priv($winName,geomRequest) "" return } # NSWindowManager::Arrange -- # # Set the desired/default geometry for each toplevel. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::ArrangeAll {} { variable Priv foreach name [array names Priv *,geomRequest] { regexp "(.*),geomRequest" $name ignore winName Arrange $winName } return } # NSWindowManager::Display -- # # Call Setup() to update the toplevel geometry if needed. Call # the displayCmd if provided, show the toplevel, then call the # displayCmd again. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::Display {winName args} { variable Priv Setup $winName set displayCmd $Priv($winName,displayCmd) if {[wm state $Priv($winName,win)] == "normal"} { if {[string length $displayCmd]} { uplevel #0 $displayCmd reDisplay 0 $args } } else { if {[string length $displayCmd]} { uplevel #0 $displayCmd preDisplay $Priv($winName,first) $args } WindowBringToFront $Priv($winName,win) update if {[string length $displayCmd]} { uplevel #0 $displayCmd postDisplay $Priv($winName,first) $args } set Priv($winName,first) 0 } return } # NSWindowManager::Undisplay -- # # Remove a window from the screen. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::Undisplay {winName args} { variable Priv set displayCmd $Priv($winName,displayCmd) if {[string length $displayCmd]} { uplevel #0 $displayCmd preWithdraw 0 $args } wm withdraw $Priv($winName,win) update if {[string length $displayCmd]} { uplevel #0 $displayCmd postWithdraw 0 $args } return } # NSWindowManager::GetWindowList -- # # Set the desired geometry for the toplevel. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWindowManager::GetWindowList {} { variable Priv set result {} foreach name [array names Priv "*,win"] { regexp "(.*),win" $name ignore window lappend result $window } return $result } zangband/lib/script/tk/library/makefile.zb0000644000000000000000000000020610250356274017610 0ustar rootrootsubdir = ./lib/script/tk/library/ ## makefile.zb srcfiles += lib/script/tk/library/makefile.zb files += lib/script/tk/library/*.tcl zangband/lib/script/tk/about.tcl0000644000000000000000000001431710250356274015660 0ustar rootroot# File: about.tcl # Purpose: the About Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSAbout { variable Priv # namespace eval NSAbout } # NSAbout::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAbout::InitModule {} { variable Priv InitWindow # Position the window the first time WindowPosition $Priv(win) 2 3 return } # NSAbout::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAbout::CloseModule {} { variable Priv catch { destroy $Priv(win) } return } # NSAbout::InitWindow -- # # Create the About Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAbout::InitWindow {} { global Angband variable Priv set win .about toplevel $win wm title $win "About ZAngband" wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSAbout::Close" set Priv(win) $win # Click/Escape to hide the window bind $win "NSAbout::Close" bind $win "NSAbout::Close" set width 350 set height 250 set height2 40 set fg Black set bg White set canvas $win.canvas canvas $canvas \ -borderwidth 0 -highlightthickness 0 \ -width $width -height $height -background $bg set font [Global font,sys,normal] set rowHeight [font metrics $font -linespace] for {set row 1} {$row <= 9} {incr row} { set fill gray[expr {$row * 10}] $canvas create text [expr {$width / 2}] \ [expr {$height - $height2 - 6 - $row * $rowHeight}] \ -fill $fill -font $font -anchor n -tags row$row } pack $canvas -padx 1 -pady 1 if {[Platform unix]} { set font {Times 24 bold} set font2 {Times 12} } if {[Platform windows]} { set font {Times 18 bold} set font2 {Times 10} } set anchor nw set x 11 set y 11 set lineSpace [font metrics $font -linespace] # Create a "shadow" for the text below $canvas create text $x $y -font $font -anchor $anchor -fill gray \ -text "ZAngband" $canvas create text $x [expr {$y + $lineSpace}] -font $font2 \ -text "Copyright (c) 1997-2001 Tim Baker" -anchor $anchor -fill gray # Draw text over the shadow created above incr x -1 incr y -1 $canvas create text $x $y -text "ZAangband" \ -font $font -anchor $anchor -fill $fg $canvas create text $x [expr {$y + $lineSpace}] -font $font2 \ -text "Copyright (c) 1997-2001 Tim Baker" -anchor $anchor -fill $fg \ -tags copyright # Scrolling text scan [$canvas bbox copyright] "%s %s %s %s" left top right bottom set height3 [expr {$height - ($height2 + 8) - ($bottom + 8)}] canvas $canvas.canvas \ -borderwidth 0 -highlightthickness 0 \ -width $width -height $height3 -background gray90 place $canvas.canvas -x 0 -y [expr {$bottom + 8}] -anchor nw # Rectangle at bottom $canvas create rectangle 0 [expr {$height - $height2}] $width $height \ -fill $fg set text "ZAngband is running on:" append text "\nTcl $::tcl_patchLevel Tk $::tk_patchLevel" set os $::tcl_platform(os) append text " $os $::tcl_platform(osVersion)" $canvas create text [expr {$width / 2}] [expr {$height - $height2 + 6}] \ -text $text -fill gray80 -justify center -anchor n -tags message # Add some text to the scrolling canvas set canvas $canvas.canvas set data [list \ h1 ZAngbandTk \ txt "Tim Baker" \ br {} \ h1 ZAngband \ txt "ZAngband Dev Team" \ br {} \ h1 Graphics \ txt "David E. Gervais" \ txt "Adam Bolt" \ txt "Aaron Funk" \ ] lappend data \ br {} \ h1 "The AngbandTk Home Page" \ txt "http://persweb.direct.ca/dbaker/angbandtk.html/" \ br {} \ h1 "Mailing List" \ txt "http://www.groups.yahoo.com/group/angbandtk/" \ txt "mailto:angbandtk@yahoogroups.com" \ br {} \ h1 "Thangorodrim - The Angband Page" \ txt "http://thangorodrim.angband.org/" \ br {} \ h1 "Tcl and Tk" \ txt "ActiveState" \ txt "http://www.activestate.com/" if {[Platform windows]} { lappend data \ br {} \ h1 "The Cygwin Project" \ txt "http://sourceware.cygnus.com/cygwin/" \ } set y 0 set x [expr {[winfo reqwidth $canvas] / 2}] unset font if {[Platform unix]} { set font(h1) "Helvetica 14 bold" set font(txt) "Helvetica 12" } if {[Platform windows]} { set font(h1) "{MS Sans Serif} 10 bold" set font(txt) "{MS Sans Serif} 9" } foreach {tag text} $data { switch -- $tag { h1 - txt { set id [$canvas create text $x $y -justify center -anchor n \ -font $font($tag) -text $text] scan [$canvas bbox $id] "%s %s %s %s" left top right bottom incr y [expr {$bottom - $top}] } br { incr y 12 } } } set Priv(canvas) $canvas set Priv(idle) {} set Priv(delay) 40 return } # NSAbout::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAbout::Close {} { variable Priv wm withdraw $Priv(win) after cancel $Priv(idle) return } # NSAbout::Scroll -- # # Called as an "after" command to scroll the text in the About Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAbout::Scroll {} { variable Priv set canvas $Priv(canvas) if {![winfo exists $canvas]} return if {$::DEBUG} { if {![winfo ismapped $Priv(win)]} { tk_messageBox -message "NSAbout::Scroll while window unmapped" return } } scan [$canvas bbox all] "%s %s %s %s" left top right bottom set height [expr {$bottom - $top}] set y $top if {$bottom <= 0} { set y [winfo height $canvas] } else { incr y -1 } $canvas move all 0 [expr {$y - $top}] set Priv(idle) [after $Priv(delay) NSAbout::Scroll] return } # NSAbout::About -- # # Called as an "after" command to scroll the text in the About Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAbout::About {} { variable Priv set canvas $Priv(canvas) scan [$canvas bbox all] "%s %s %s %s" left top right bottom set y [winfo height $canvas] $canvas move all 0 [expr {$y - $top}] WindowBringToFront $Priv(win) set Priv(idle) [after $Priv(delay) NSAbout::Scroll] return } zangband/lib/script/tk/angband.tcl0000644000000000000000000001504310250356274016135 0ustar rootroot# File: angband.tcl # Purpose: script commands called by Angband # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # angband_close_game -- # # Called by Angband when the game is about to exit. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc angband_close_game {} { global Angband if {[catch { if {[Value window,autosave]} { WriteGeometryFile } Config::Alternate::Write NSValueManager::CloseModule if {[info exists ::Global(photoText)]} { set tempFile [Global photoText] if {[string length $tempFile] && [file exists $tempFile]} { file delete $tempFile } } } result]} { set message "An error occured during shutdown:\n $result" tk_messageBox -title "ZAngband Error" -message $message \ -icon error } return } # angband_display -- # # Called by Angband to hide/show a window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc angband_display {window action args} { global Angband global Display global PYPX if {[string equal $action hide]} { switch -- $window { floor - inven - equip { if {[string equal [Value inventory,style] new]} { if {[Inventory2Obj Info browsing]} return set window inventory2 } else { if {[InventoryObj Info browsing]} return set window inventory } #eval RequestDisplay $window $action $args #return } message { set window messages } player { set window character } store { if {[string equal [Value store,style] new]} { set window store2 } else { set window store } } } # Undisplay the window if {[info exists NSWindowManager::Priv($window,win)]} { eval NSWindowManager::Undisplay $window $args } else { wm withdraw [Window $window] } # Restore focus catch {focus $Display($window,oldFocus)} set Display(window) none } else { switch -- $window { equip { if {[string equal [Value inventory,style] new]} { NSModule::LoadIfNeeded NSInventory2 set window inventory2 } else { NSModule::LoadIfNeeded NSInventory set window inventory } set args [concat [list equipment ""] $args] #eval RequestDisplay $window $action $args #return } floor { if {[string equal [Value inventory,style] new]} { NSModule::LoadIfNeeded NSInventory2 set window inventory2 } else { NSModule::LoadIfNeeded NSInventory set window inventory } set args [list floor ""] #eval RequestDisplay $window $action $args #return } info { NSModule::LoadIfNeeded NSInfoWindow NSInfoWindow::SetList [Global info,oop] [lindex $args 0] \ [lindex $args 1] } inven { if {[string equal [Value inventory,style] new]} { NSModule::LoadIfNeeded NSInventory2 set window inventory2 } else { NSModule::LoadIfNeeded NSInventory set window inventory } set args [concat [list inventory ""] $args] #eval RequestDisplay $window $action $args #return } knowledge { NSModule::LoadIfNeeded NSKnowledge } message { NSModule::LoadIfNeeded NSMessageHistory set window messages } pets { NSModule::LoadIfNeeded NSPets } player { NSModule::LoadIfNeeded NSCharacterWindow set window character } playerflags { NSModule::LoadIfNeeded NSPlayerFlags } store { if {[string equal [Value store,style] new]} { NSModule::LoadIfNeeded NSStore2 set window store2 } else { NSModule::LoadIfNeeded NSStore set window store } } default { error "unknown window \"$window\"" } } # The window isn't already displayed if {$Display(window) != $window} { # Save current focus set Display($window,oldFocus) [focus] set Display(window) $window } # Display the window if {[info exists NSWindowManager::Priv($window,win)]} { eval NSWindowManager::Display $window $args } else { WindowBringToFront [Window $window] } } # Update the windows now update return } # angband_prompt -- # # Called by Angband to prompt the user. Rather complex interface to # handle multiple messages per line, and getting a string from the # user. This routine builds the string to be displayed in the # Message Window. It is not actually displayed until a Term-fresh # quasi-event occurs, at which point Fresh_Prompt() is called. # This behaviour prevents unwanted redraws during macro invocations. # # Arguments: # arg1 about arg1 # # Results: # What happened. set AngbandPriv(prompt,new) {} set AngbandPriv(prompt,old) {} set AngbandPriv(prompt,fresh) 0 proc Fresh_Prompt {} { global AngbandPriv if {!$AngbandPriv(prompt,fresh)} return if {[string compare $AngbandPriv(prompt,new) $AngbandPriv(prompt,old)]} { set wText [Global message,message] $wText delete 1.0 end if {[llength $AngbandPriv(prompt,new)]} { eval $wText insert end $AngbandPriv(prompt,new) } set AngbandPriv(prompt,old) $AngbandPriv(prompt,new) } set AngbandPriv(prompt,fresh) 0 return } proc angband_prompt {action args} { global AngbandPriv switch -- $action { open { set AngbandPriv(prompt,new) $args set AngbandPriv(prompt,prefix) $args } update { set AngbandPriv(prompt,new) [concat $AngbandPriv(prompt,prefix) $args] } set { set AngbandPriv(prompt,new) $args } append { set AngbandPriv(prompt,new) [concat $AngbandPriv(prompt,new) $args] } wipe { set AngbandPriv(prompt,new) {} } } set AngbandPriv(prompt,fresh) 1 return } # NSModule::AddModule NSMessageLine [file join $Angband(dirTK) message-line.tcl] # NSModule::LoadIfNeeded NSMessageLine # angband_store -- # # A bit of ugliness to handle the haggle prompts in a store. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc angband_store {action {arg1 ""}} { if {[string equal [Value store,style] new]} { set command Store2Obj set window store2 } else { set command StoreObj set window store } set win [Window $window] switch -- $action { haggle_open - haggle_close { $command HaggleSetup $action } price_character - price_owner { $win.info.$action configure -text $arg1 } } return } proc angband_command {command} { global Display switch -- $command { e - i { return 0 } default { if {[string equal $Display(window) inventory]} { eval NSWindowManager::Undisplay inventory catch {focus $Display($window,oldFocus)} set Display(window) none } return 0 } } } zangband/lib/script/tk/autobar.tcl0000644000000000000000000003775410250356274016215 0ustar rootroot# File: autobar.tcl # Purpose: the Autobar display and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSAutobar { # namespace eval NSAutobar } # NSAutobar::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::InitModule {} { InitImageIfNeeded Image_ButtonActivate button-activate.gif InitImageIfNeeded Image_ButtonFood button-food.gif InitImageIfNeeded Image_ButtonPotion button-potion.gif InitImageIfNeeded Image_ButtonScroll button-scroll.gif InitImageIfNeeded Image_ButtonRod button-rod.gif InitImageIfNeeded Image_ButtonWand button-wand.gif InitImageIfNeeded Image_ButtonStaff button-staff.gif InitImageIfNeeded Image_ButtonDown button-down.gif InitImageIfNeeded Image_ButtonUp button-up.gif InitImageIfNeeded Image_ButtonBook button-book.gif NSObject::New NSAutobar return } # NSAutobar::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::CloseModule {} { catch { destroy [Global autobar,canvas] } return } # NSAutobar::MInfo -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::MInfo {info args} { variable Priv # Set info if {[llength $args]} { set Priv($info) [lindex $args 0] # Get info } else { return $Priv($info) } return } # NSAutobar::NSAutobar -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSAutobar::NSAutobar {oop} { Info $oop busy 0 Info $oop current "" Info $oop request,id "" Info $oop showing 0 Info $oop bar,visible 0 Info $oop win,visible 0 Info $oop whoHasCursor "" Info $oop after "" Info $oop nextButton 0 InitDisplay $oop set canvas [Info $oop canvas] set wText [Info $oop text] # Update ourself when the list highlight color changes Info $oop clientId,listHilite \ [NSValueManager::AddClient listHilite \ "$wText tag configure HOT \ -background \[Value listHilite]"] # Update ourself when the font changes Info $oop clientId,font,autobar \ [NSValueManager::AddClient font,autobar \ "NSAutobar::ValueChanged_font_autobar $oop"] # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSAutobar $oop $canvas Global autobar,canvas $canvas Global autobar,oop $oop return } # NSAutobar::~NSAutobar -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::~NSAutobar {oop} { NSValueManager::RemoveClient font,autobar [Info $oop clientId,font,autobar] NSValueManager::RemoveClient listHilite [Info $oop clientId,listHilite] return } # NSAutobar::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::Info {oop info args} { global NSAutobar # Set info if {[llength $args]} { switch -- $info { default { set NSAutobar($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSAutobar($oop,$info) } } } return } # NSAutobar::InitDisplay -- # # Create the display. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSAutobar::InitDisplay {oop} { set widget [Global main,widget] # # The main canvas, holding the buttons # set canvas $widget.autobar$oop canvas $canvas \ -borderwidth 0 -relief flat -highlightthickness 0 \ -background Black -height 22 -width 200 \ -scrollregion {0 0 0 0} Info $oop canvas $canvas bind $canvas \ "NSAutobar::Event $oop enter-bar" bind $canvas \ "NSAutobar::Event $oop leave-bar" # # Buttons # Some buttons invoke a command when clicked, such as "go down". # Other buttons display a list of item or spell choices. # If a pop-up button has no valid choices, then it isn't displayed # in the bar. # set x 2 NewButton $oop -image Image_ButtonActivate -popup 1 -hook item \ -args [list equipment A -activate yes] \ -message "Activate an equipment item" $canvas configure -width [incr x 20] $canvas create rectangle \ 0 0 [expr {$x - 1}] 21 -outline gray60 -tags border # # Popup window of choices # set win $canvas.popup toplevel $win $win configure -borderwidth 1 -relief flat -background gray60 wm overrideredirect $win yes wm transient $win [Window main] if {[Platform unix]} { $win configure -cursor arrow } # Start out withdrawn (hidden) wm withdraw $win Info $oop win $win set wText $win.text text $wText \ -wrap none -font [Value font,autobar] \ -borderwidth 0 -setgrid no -highlightthickness 0 \ -padx 4 -pady 2 -background Black -foreground White -cursor "" # Bypass default Text bindings bindtags $wText [list $wText $win all] pack $wText \ -expand yes -fill both Info $oop text $wText # Fiddle with the selection for list behaviour $wText tag configure HOT -foreground White \ -background [Value listHilite] $wText tag bind HOT \ "NSAutobar::Invoke $oop" $wText tag bind TEXT \ "NSAutobar::Motion $oop \[$wText index {@%x,%y linestart}]" $wText tag bind HOT \ "NSAutobar::Motion $oop {}" bind $win \ "NSAutobar::Event $oop enter-win" bind $win " NSAutobar::Motion $oop {} NSAutobar::Event $oop leave-win " return } # NSAutobar::NewButton -- # # Add a new button to the Autobar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::NewButton {oop args} { set canvas [Info $oop canvas] # Get the next unique id for this button set num [incr ::NSAutobar($oop,nextButton)] set config(-command) "" array set config $args set image $config(-image) set command $config(-command) set message $config(-message) # The buttons are positioned in ShowBar(). set x 0 set y 0 # Focus rectangle $canvas create rectangle $x $y [expr {$x + 17}] [expr {$y + 17}] \ -tags [list button button$num border$num] # Image $canvas create image [expr {$x + 1}] [expr {$y + 1}] -image $image \ -anchor nw -tags "button button$num img$num" # Show popup on mouse-over if {![string length $command]} { $canvas bind img$num " $canvas itemconfigure border$num -outline gray60 NSMainWindow::StatusText $oop [list $message] NSAutobar::EnterButton $oop $num " Info $oop button,hook,$num $config(-hook) Info $oop button,args,$num $config(-args) # Click to invoke command } else { $canvas bind img$num " $canvas itemconfigure border$num -outline gray60 NSMainWindow::StatusText $oop [list $message] NSAutobar::Event $oop enter-button2 " $canvas bind img$num " $canvas move button$num 1 1 $command " $canvas bind img$num \ "$canvas move button$num -1 -1" Info $oop button,hook,$num "" } $canvas bind img$num " $canvas itemconfigure border$num -outline Black NSAutobar::Event $oop leave-button NSMainWindow::StatusText $oop {} " return } # NSAutobar::SetHook -- # # Set the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::SetHook {oop hook} { if {[string length $hook]} { Info $oop hook $hook CallHook $oop open } elseif {[string length [Info $oop hook]]} { Info $oop hook "" } return } # NSAutobar::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::CallHook {oop message args} { return [uplevel #0 NSAutobar::[Info $oop hook] $oop $message $args] } # NSAutobar::EnterButton -- # # Display popup of choices. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::EnterButton {oop buttonNum} { set win [Info $oop win] set canvas [Info $oop canvas] set wText [Info $oop text] set hook [Info $oop button,hook,$buttonNum] set hookArgs [Info $oop button,args,$buttonNum] if {[lsearch -exact [angband inkey_flags] INKEY_CMD] == -1} return set x [Info $oop button,x,$buttonNum] incr x [expr {[winfo rootx $canvas] + 9}] set y [winfo rooty $canvas] # Set the list SetHook $oop hook_$hook $wText delete 1.0 end eval CallHook $oop set_list $hookArgs set width [Info $oop maxWidth] incr width [expr {[$wText cget -padx] * 2}] set height [Info $oop maxHeight] incr height [expr {[$wText cget -pady] * 2}] incr width [expr {[$win cget -borderwidth] * 2}] incr height [expr {[$win cget -borderwidth] * 2}] # x is middle incr x [expr {0 - $width / 2}] if {$x < [winfo rootx $canvas]} { set x [winfo rootx $canvas] } # Given y is bottom incr y -$height set screenWidth [winfo screenwidth .] if {$x + $width > $screenWidth} { incr x [expr {$screenWidth - ($x + $width)}] } set screenHeight [winfo screenheight .] if {$y + $height > $screenHeight} { incr y [expr {$screenHeight - ($y + $height)}] } wm geometry $win ${width}x${height}+${x}+$y # Perhaps show the window later Event $oop enter-button return } # NSAutobar::Event -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::Event {oop event} { set who [Info $oop whoHasCursor] switch -- $event { enter-status { if {[lsearch -exact [angband inkey_flags] INKEY_CMD] == -1} return set who status } leave-status { set who "" } enter-bar { # The binding for a button is invoked before # the binding for the canvas. So we don't want to # forget we are in a button if {[string compare $who button]} { set who bar } } leave-bar { set who "" } enter-win { set who win } leave-win { set who "" } enter-button { set who button } enter-button2 { set who button2 } leave-button { set who bar } } Info $oop whoHasCursor $who if {[string match enter-* $event]} { set delay 10 } else { set delay 200 } after cancel [Info $oop after] Info $oop after [after $delay NSAutobar::CheckWhoHasCursor $oop] return } # NSAutobar::CheckWhoHasCursor -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::CheckWhoHasCursor {oop} { set who [Info $oop whoHasCursor] switch -- $who { bar { HideWin $oop } button { ShowWin $oop } button2 { HideWin $oop } status { HideWin $oop ShowBar $oop } win { } default { HideWin $oop HideBar $oop } } Info $oop after "" return } # NSAutobar::ShowBar -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::ShowBar {oop} { set canvas [Info $oop canvas] # The bar is already shown if {[Info $oop bar,visible]} return # Hide all the buttons $canvas itemconfigure button -state hidden # Display buttons which have some commands set x 3 set y 3 # Check each button for {set i 1} {$i <= [Info $oop nextButton]} {incr i} { # Get the hook for this button (if any) set hook [Info $oop button,hook,$i] # This button has a hook if {[string length $hook]} { # Set the hook SetHook $oop hook_$hook # Get the extra args to pass to the hook set args [Info $oop button,args,$i] # See if there are any valid choices if {[eval CallHook $oop has_cmd $args]} { # Position the button horizontally scan [$canvas coords img$i] "%s %s" cx cy $canvas move button$i [expr {$x - $cx}] [expr {$y - $cy}] $canvas itemconfigure button$i -state "" # Remember the x coordinate Info $oop button,x,$i $x # Leave space for another button incr x 20 } # Button has no hook, always show it } else { # Position the button horizontally scan [$canvas coords img$i] "%s %s" cx cy $canvas move button$i [expr {$x - $cx}] [expr {$y - $cy}] $canvas itemconfigure button$i -state "" # Remember the x coordinate Info $oop button,x,$i $x # Leave space for another button incr x 20 } } # Resize the bar, and position the border rectangle set width [expr {$x - 1}] $canvas configure -width $width $canvas coords border 0 0 [expr {$width - 1}] 21 # Now slide the bar into view set height [winfo reqheight $canvas] foreach frac [list 0.1 0.2 0.3 0.5 0.7 1] { set dy [expr {0 - $height * $frac}] place $canvas -relx 0.5 -rely 1.0 -y $dy -anchor n update idletasks } Info $oop bar,visible 1 return } # NSAutobar::HideBar -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::HideBar {oop} { if {![Info $oop bar,visible]} return set canvas [Info $oop canvas] # Now slide the bar out of view set height [winfo reqheight $canvas] foreach frac [list 0.7 0.4 0.1] { set dy [expr {0 - $height * $frac}] place $canvas -relx 0.5 -rely 1.0 -y $dy -anchor n # Kind of slow if mouse is whizzing around... update } place forget $canvas Info $oop bar,visible 0 return } # NSAutobar::ShowWin -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::ShowWin {oop} { if {[Info $oop win,visible]} return set win [Info $oop win] wm deiconify $win if {[Platform unix]} { raise $win } Info $oop win,visible 1 return } # NSAutobar::HideWin -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::HideWin {oop} { if {![Info $oop win,visible]} return set win [Info $oop win] wm withdraw $win Info $oop win,visible 0 return } # NSAutobar::Invoke -- # # Called when a list item is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::Invoke {oop} { set textBox [Info $oop text] set index [Info $oop current] set row [expr {[lindex [split $index .] 0] - 1}] HideWin $oop HideBar $oop CallHook $oop invoke $row return } # NSAutobar::Motion -- # # Called when the mouse moves in a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::Motion {oop index} { set textBox [Info $oop text] # If you invoke an item, hold down the mouse, and drag... if {![string length [Info $oop hook]]} return # No tracking while menu is up # if {[Info $oop busy]} return # See if the item has changed if {$index == [Info $oop current]} return # An item is highlighted if {[string length [Info $oop current]]} { # Remove highlighting UnhighlightItem $oop [Info $oop current] } # An item is under the pointer if {[string length $index]} { # Highlight the item HighlightItem $oop $index } # Remember which item is highlighted Info $oop current $index return } # NSAutobar::HighlightItem -- # # Highlights a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::HighlightItem {oop index} { set textBox [Info $oop text] set row [expr {[lindex [split $index .] 0] - 1}] # Highlight the item $textBox tag add HOT $index "$index lineend" $textBox tag raise HOT # Call the hook (to set the icon, for example) CallHook $oop highlight $row return } # NSAutobar::UnhighlightItem -- # # Removes highlighting from a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::UnhighlightItem {oop index} { set win [Info $oop win] set textBox [Info $oop text] # Unhighlight the item $textBox tag remove HOT 1.0 end return } # NSAutobar::HasCursor -- # # See if the cursor is over the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::HasCursor {oop} { set pointerx [winfo pointerx .] set pointery [winfo pointery .] set window [winfo containing $pointerx $pointery] if {![string length $window]} { return 0 } if {[string compare [winfo toplevel $window] [Info $oop win]]} { return 0 } return 1 } # NSAutobar::ValueChanged_font_autobar -- # # Called when the font,autobar value changes. # Updates the Recall Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSAutobar::ValueChanged_font_autobar {oop} { set text [Info $oop text] $text configure -font [Value font,autobar] return } zangband/lib/script/tk/bgerror.tcl0000644000000000000000000000447210250356274016211 0ustar rootroot# File: bgerror.tcl # Purpose: bgerror implementation # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # bgerror -- # # Overrides Tk's bgerror command. Displays an error message, then # appends the stack trace to the errors.txt file. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc bgerror {err} { global Angband global errorInfo global ErrorText set info $errorInfo set parent [focus] if {![winfo exists $parent] || ![winfo ismapped $parent]} { set parent . wm deiconify . } set message "The following error occurred:\n\n$err\n\n" append message "You might be able to continue playing.\n" append message "Please send the errors.txt file to the zangband mailing list" tk_messageBox -title "Error in ZAngband" \ -message $message -icon info -parent $parent if {[catch {open [PathTk errors.txt] a} fileId]} { tk_messageBox -icon error -title Error \ -message "Couldn't open [PathTk errors.txt]" return } catch { puts $fileId "***** ZAngband" puts $fileId $ErrorText puts $fileId "\n***** Last Stack:\n" puts $fileId $info puts $fileId "" } close $fileId return } # HandleError -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc HandleError {err {prompt ""}} { global Angband global errorInfo global ErrorText set stack $errorInfo if {[string length $prompt]} { set message "$prompt:\n\n$err\n\n" } else { set message "The following error occurred:\n\n$err\n\n" } append message "Please send the errors.txt file to zangband mailing list" if {$::DEBUG} { append message "\nQuit now?" set type yesno } else { set type ok } set answer [tk_messageBox -title "Error in ZAngband" \ -message $message -type $type -icon error] if {[catch {open [PathTk errors.txt] a} fileId]} { if {$::DEBUG} { tk_messageBox -icon error -title Error \ -message "Couldn't open [PathTk errors.txt]\n$fileId" } } else { catch { puts $fileId "***** ZAngband" puts $fileId $ErrorText puts $fileId "\n***** Last Stack:\n" puts $fileId $stack puts $fileId "" } close $fileId } if {[string equal $answer no]} return AbortGame return } zangband/lib/script/tk/character-window.tcl0000644000000000000000000003344510250356274020012 0ustar rootroot# File: character-window.tcl # Purpose: the Character Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSCharacterWindow { variable MenuString variable Priv # namespace eval NSCharacterWindow } # NSCharacterWindow::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::InitModule {} { variable Priv set Priv(hook) {} lappend Priv(hook) HookInfo "Info" lappend Priv(hook) HookFlags "Flags" lappend Priv(hook) HookMutations "Mutations" lappend Priv(hook) HookVirtues "Virtues" lappend Priv(hook) HookNotes "Notes" NSObject::New NSCharacterWindow return } # NSCharacterWindow::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::CloseModule {} { catch { destroy [Window character] } return } # NSCharacterWindow::NSCharacterWindow -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::NSCharacterWindow {oop} { variable Priv Info $oop hook "" InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow character $win \ "GetDefaultGeometry $win screen main" \ "NSCharacterWindow::SetupCmd $oop" \ "NSCharacterWindow::DisplayCmd $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSCharacterWindow $oop $win # # Global list of application windows # Global character,oop $oop Window character $win return } # NSCharacterWindow::~NSCharacterWindow -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::~NSCharacterWindow {oop} { return } # NSCharacterWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::Info {oop info args} { global NSCharacterWindow # Verify the object NSObject::CheckObject NSCharacterWindow $oop # Set info if {[llength $args]} { switch -- $info { default { set NSCharacterWindow($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSCharacterWindow($oop,$info) } } } return } # NSCharacterWindow::InitWindow -- # # Create the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::InitWindow {oop} { variable Priv set win .character$oop toplevel $win wm title $win "Character" wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSCharacterWindow::Close $oop" # Start out withdrawn (hidden) wm withdraw $win Info $oop win $win # # Menus # InitMenus $oop # # Divider # MakeDivider $win.divider2 x # # Tabs! # set tabsId [NSObject::New NSTabs $win] foreach {hook label} $Priv(hook) { NSTabs::Add $tabsId $label } NSTabs::Info $tabsId invokeCmd "NSCharacterWindow::InvokeTab $oop" NSTabs::Info $tabsId active 1 Info $oop tabsId $tabsId # # Content area. A black background frame is used to prevent annoying # flicker. # set frame $win.frame frame $frame \ -borderwidth 1 -relief sunken frame $frame.background \ -borderwidth 0 -background Black place $frame.background -x 0 -y 0 -relwidth 1.0 -relheight 1.0 Info $oop frame $frame # A separate frame for each hook's stuff foreach {hook label} $Priv(hook) { Info $oop frame,$hook [$hook $oop init $frame] } # # Statusbar # MakeStatusBar $win.statusBar 20 # # Geometry # grid rowconfig $win 0 -weight 0 -minsize 0 grid rowconfig $win 1 -weight 0 -minsize 0 grid rowconfig $win 2 -weight 1 -minsize 0 grid rowconfig $win 3 -weight 0 -minsize 0 grid columnconfig $win 0 -weight 0 -minsize 0 grid columnconfig $win 1 -weight 1 -minsize 0 grid $win.divider2 \ -row 0 -column 0 -rowspan 1 -columnspan 2 -sticky ew grid [NSTabs::Info $tabsId canvas] \ -row 1 -column 0 -rowspan 1 -columnspan 2 -sticky ew grid $win.frame \ -row 2 -column 1 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar -in $win \ -row 3 -column 0 -rowspan 1 -columnspan 2 -sticky ew # # Feed Term when keys pressed # bind $win "NSCharacterWindow::Close $oop" bind $win "NSCharacterWindow::Close $oop" return } # NSCharacterWindow::InitMenus -- # # Create the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::InitMenus {oop} { variable MenuString variable Priv set win [Info $oop win] set mod "Ctrl" # # Menu bar # set mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSCharacterWindow::SetupMenus $oop" -identifier MENUBAR] Info $oop mbarId $mbar # Context-sensitive help NSMenu::Info $mbar menuSelectCmd "NSCharacterWindow::MenuSelect $oop" # Call our command when an entry is invoked NSMenu::Info $mbar invokeCmd "NSCharacterWindow::MenuInvoke $oop" # # Character Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_CHARACTER NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_CHARACTER -label "Character" -underline 0 \ -identifier M_CHARACTER set entries {} set i 1 foreach {hook label} $Priv(hook) { lappend entries [list -type radiobutton -label $label \ -variable NSCharacterWindow($oop,radio,hook) -value $hook \ -accelerator $i -identifier E_HOOK_$i] bind $win "NSCharacterWindow::SetHook $oop $hook" incr i } lappend entries [list -type separator] lappend entries [list -type separator] lappend entries [list -type command -label "Close" \ -underline 0 -accelerator $mod+W -identifier E_CLOSE] NSMenu::MenuInsertEntries $mbar -end MENU_CHARACTER $entries set MenuString(M_CHARACTER) \ "Contains character-related commands." set MenuString(E_CLOSE) \ "Closes the window." return } # NSCharacterWindow::SetupMenus -- # # Prepare to post the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::SetupMenus {oop mbarID} { variable Priv set i 0 foreach {hook label} $Priv(hook) { lappend identList E_HOOK_[incr i] } lappend identList E_CLOSE NSMenu::MenuEnable $mbarID $identList [Info $oop win].statusBar cover show return } # NSCharacterWindow::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::MenuSelect {oop menuId index ident} { variable MenuString variable Priv switch -glob -- $ident { {} { set desc {} } E_HOOK_* { set desc "Displays this page." } default { if {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop win].statusBar cover set $desc if {![string length $desc]} { if {$menuId == [Info $oop mbarId]} { [Info $oop win].statusBar cover hide } } return } # NSCharacterWindow::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::MenuInvoke {oop menuId ident} { variable Priv switch -glob -- $ident { E_HOOK_* { scan $ident "E_HOOK_%d" hookNum SetHook $oop [lindex $Priv(hook) [expr {($hookNum - 1) * 2}]] } E_CLOSE {Close $oop} } return } # NSCharacterWindow::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::DisplayCmd {oop message first args} { switch -- $message { preDisplay { if {$first} { SetHook $oop HookInfo } else { CallHook $oop display } } postDisplay { } postWithdraw { } } return } # NSCharacterWindow::SetupCmd -- # # Called by NSWindowManager::Setup(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::SetupCmd {oop} { variable Priv set frame [Info $oop frame] foreach {hook label} $Priv(hook) { set frame [Info $oop frame,$hook] pack $frame -expand yes -fill both update idletasks pack forget $frame } return } # NSCharacterWindow::Close -- # # Do something when closing the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::Close {oop} { angband keypress \033 return } # NSCharacterWindow::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::CallHook {oop message args} { return [uplevel #0 NSCharacterWindow::[Info $oop hook] $oop $message $args] } # NSCharacterWindow::SetHook -- # # Set the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::SetHook {oop hook} { variable Priv set oldHook [Info $oop hook] if {[string equal $hook $oldHook]} return if {[string length $oldHook]} { pack forget [Info $oop frame,$oldHook] } # Remember the hook Info $oop hook $hook set parent [Info $oop frame,$hook] pack $parent -expand yes -fill both CallHook $oop display # Radiobutton menu entries Info $oop radio,hook $hook set tabsId [Info $oop tabsId] set current [NSTabs::Info $tabsId current] set tabId [NSTabs::GetNthId $tabsId [expr {[lsearch -exact $Priv(hook) $hook] / 2}]] if {$tabId != $current} { NSTabs::Smaller $tabsId $current NSTabs::Bigger $tabsId $tabId NSTabs::Info $tabsId current $tabId } [Info $oop win].statusBar itemconfigure t2 -text "" return } # NSCharacterWindow::InvokeTab -- # # Called when a tab is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharacterWindow::InvokeTab {oop tabsId tabId} { variable Priv set index [lsearch -exact [NSTabs::Info $tabsId id] $tabId] SetHook $oop [lindex $Priv(hook) [expr {$index * 2}]] return } proc NSCharacterWindow::HookInfo {oop message args} { switch -- $message { init { set frame [lindex $args 0].frameInfo frame $frame \ -borderwidth 0 NSModule::LoadIfNeeded NSCharInfoCanvas set infoId [NSObject::New NSCharInfoCanvas $frame] Info $oop hook,info,oop $infoId return $frame } display { NSCharInfoCanvas::SetInfo [Info $oop hook,info,oop] } } return } proc NSCharacterWindow::HookFlags {oop message args} { switch -- $message { init { set frame [lindex $args 0].frameFlags frame $frame \ -borderwidth 0 NSModule::LoadIfNeeded NSCharFlagsCanvas set flagsId [NSObject::New NSCharFlagsCanvas $frame] NSCharFlagsCanvas::Info $flagsId statusBar \ [Info $oop win].statusBar Info $oop hook,flags,oop $flagsId return $frame } display { NSCharFlagsCanvas::SetInfo [Info $oop hook,flags,oop] } } return } proc NSCharacterWindow::HookMutations {oop message args} { switch -- $message { init { set frame [lindex $args 0].frameMutations frame $frame \ -borderwidth 0 set textId [NSObject::New NSTexist $frame [Value font,knowledge] \ 60 10] set text [NSTexist::Info $textId text] $text configure -background [Value listBG] \ -yscrollcommand "$frame.yscroll set" scrollbar $frame.yscroll \ -orient vertical -command "$text yview" pack $text -side left -expand yes -fill both pack $frame.yscroll -side left -expand no -fill y Info $oop hook,mutations,textId $textId set id [NSValueManager::AddClient font,knowledge \ "NSCharacterWindow::HookMutations $oop font_changed"] bind $text \ "NSValueManager::RemoveClient font,knowledge $id" return $frame } display { set textId [Info $oop hook,mutations,textId] set textList {} set colorList {} foreach desc [angband player mutations] { lappend textList $desc lappend colorList White } NSTexist::SetList $textId $textList $colorList } font_changed { set textId [Info $oop hook,mutations,textId] set text [NSTexist::Info $textId text] $text configure -font [Value font,knowledge] } } return } proc NSCharacterWindow::HookVirtues {oop message args} { switch -- $message { init { return [Info $oop frame,HookMutations] } display { set textId [Info $oop hook,mutations,textId] set textList {} set colorList {} foreach desc [angband player virtues] { lappend textList $desc lappend colorList White } NSTexist::SetList $textId $textList $colorList } } return } proc NSCharacterWindow::HookNotes {oop message args} { switch -- $message { init { set frame [lindex $args 0].frameNotes frame $frame \ -borderwidth 0 set text $frame.text text $text -foreground White -background [Value listBG] \ -font [Global font,sys,normal] \ -xscrollcommand "$frame.xscroll set" \ -yscrollcommand "$frame.yscroll set" scrollbar $frame.xscroll \ -orient horizontal -command "$text xview" scrollbar $frame.yscroll \ -orient vertical -command "$text yview" NSUtils::SynchScrollBar $text $frame.yscroll NSUtils::SynchScrollBar $text $frame.xscroll grid rowconfig $frame 0 -weight 1 grid rowconfig $frame 1 -weight 0 grid columnconfig $frame 0 -weight 1 grid columnconfig $frame 1 -weight 0 grid $text \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $frame.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns grid $frame.xscroll \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew Info $oop hook,notes,text $text return $frame } display { set text [Info $oop hook,notes,text] $text delete 1.0 end set name [string range [angband player base_name] 0 7].txt set path [PathTk lib save $name] if {[file exists $path]} { set id [open $path] $text insert end [read $id] close $id } } } return } zangband/lib/script/tk/charflags-canvas.tcl0000644000000000000000000002442010250356274017745 0ustar rootroot# File: charflags-canvas.tcl # Purpose: the Character Flags canvas and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSCharFlagsCanvas { variable Priv # namespace eval NSCharFlagsCanvas } # NSCharFlagsCanvas::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::InitModule {} { variable Priv NSModule::LoadIfNeeded NSBalloon set Priv(slots) [list \ INVEN_WIELD \ INVEN_BOW \ INVEN_LEFT \ INVEN_RIGHT \ INVEN_NECK \ INVEN_LITE \ INVEN_BODY \ INVEN_OUTER \ INVEN_ARM \ INVEN_HEAD \ INVEN_HANDS \ INVEN_FEET \ ] set Priv(flags) [list \ STR \ INT \ WIS \ DEX \ CON \ CHR \ SUST_STR \ SUST_INT \ SUST_WIS \ SUST_DEX \ SUST_CON \ SUST_CHR \ IM_ACID \ IM_ELEC \ IM_FIRE \ IM_COLD \ RACE_IM_CUT \ RACE_IM_DARK \ RACE_IM_STUN \ RES_ACID \ RES_ELEC \ RES_FIRE \ RES_COLD \ RES_POIS \ RES_FEAR \ RES_LITE \ RES_DARK \ RES_BLIND \ RES_CONF \ RES_SOUND \ RES_SHARDS \ RES_NEXUS \ RES_NETHER \ RES_CHAOS \ RES_DISEN \ RACE_RES_SANITY \ SLOW_DIGEST \ FEATHER \ LITE \ REGEN \ TELEPATHY \ SEE_INVIS \ REFLECT \ FREE_ACT \ HOLD_LIFE \ RACE_EAT_NETHER \ STEALTH \ SEARCH \ INFRA \ TUNNEL \ SPEED \ BLOWS \ XTRA_SHOTS \ XTRA_MIGHT \ SLAY_ANIMAL \ SLAY_EVIL \ SLAY_UNDEAD \ SLAY_DEMON \ SLAY_ORC \ SLAY_TROLL \ SLAY_GIANT \ SLAY_DRAGON \ KILL_DRAGON \ BRAND_POIS \ BRAND_ACID \ BRAND_ELEC \ BRAND_FIRE \ BRAND_COLD \ SH_FIRE \ SH_ELEC \ VORPAL \ IMPACT \ CHAOTIC \ VAMPIRIC \ WRAITH \ NO_MAGIC \ TELEPORT \ NO_TELE \ AGGRAVATE \ DRAIN_EXP \ BLESSED \ CURSED \ HEAVY_CURSE \ PERMA_CURSE \ TY_CURSE \ ] set font [Global font,sys,large] set lineHgt [font metrics $font -linespace] set Priv(font,font) $font set Priv(font,height) $lineHgt return } # NSCharFlagsCanvas::NSCharFlagsCanvas -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::NSCharFlagsCanvas {oop parent} { variable Priv set canvas $parent.header set width 100 set height 40 canvas $canvas \ -scrollregion "0 0 $width $height" -width $width -height $height \ -relief flat -highlightthickness 0 -background gray40 Info $oop header,canvas $canvas set lineHgt $Priv(font,height) set canvas $parent.canvas set width 100 set height [expr {$lineHgt * 22}] canvas $canvas \ -scrollregion "0 0 $width [expr {($lineHgt + 3) * 32}]" \ -width $width -height $height -yscrollincrement [expr {$lineHgt + 3}] \ -highlightthickness 0 -background #000022 \ -yscrollcommand "$parent.yscroll set" \ -xscrollcommand "$parent.xscroll set" scrollbar $parent.yscroll \ -orient vertical -command "$canvas yview" scrollbar $parent.xscroll \ -orient horizontal -command "$canvas xview" bind $canvas \ "+NSCharFlagsCanvas::Configure $oop" bind $canvas \ "+NSCharFlagsCanvas::Configure $oop" # bind $parent.yscroll \ # "eval %W set \[$canvas yview]" NSUtils::SynchScrollBar $canvas $parent.yscroll Info $oop canvas $canvas Info $oop frame $parent # So it is drawn first raise $parent.header grid rowconfigure $parent 0 -weight 0 grid rowconfigure $parent 1 -weight 1 grid rowconfigure $parent 2 -weight 0 grid columnconfigure $parent 0 -weight 1 grid columnconfigure $parent 1 -weight 0 grid $parent.header \ -row 0 -column 0 -rowspan 1 -columnspan 2 -sticky ew grid $parent.canvas \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $parent.yscroll \ -row 1 -column 1 -rowspan 1 -columnspan 1 -sticky ns grid $parent.xscroll \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid remove $parent.yscroll grid remove $parent.xscroll Info $oop scrollbar,vert 0 Info $oop scrollbar,horz 0 NSUtils::DestroyObjectWithWidget NSCharFlagsCanvas $oop $canvas InitLayout $oop return } # NSCharFlagsCanvas::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::Info {oop info args} { global NSCharFlagsCanvas # Verify the object NSObject::CheckObject NSCharFlagsCanvas $oop # Set info if {[llength $args]} { switch -- $info { default { set NSCharFlagsCanvas($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSCharFlagsCanvas($oop,$info) } } } return } # NSCharFlagsCanvas::InitLayout -- # # Create all the canvas items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::InitLayout {oop} { variable Priv set canvas [Info $oop header,canvas] # Calculate the width of the row labels # Width = Text Width + 2 pixels + 2 * 3 set labelWidth 0 set labels {} foreach flag $Priv(flags) { set label $flag set width [font measure $Priv(font,font) $label] if {$width > $labelWidth} { set labelWidth $width } lappend labels $label } incr labelWidth [expr {2 + (2 * 3)}] if {$labelWidth < 100} { set labelWidth 100 } # Calculate the width of the canvas set width [expr {$labelWidth + (32 + 2) * ([llength $Priv(slots)] + 1)}] $canvas configure -width [expr {$width + 16}] if 0 { # One icon per equipment slot set x [expr {$labelWidth + 16}] set y 20 foreach slot $Priv(slots) { $canvas create widget $x $y -anchor center \ -tags $slot $canvas bind $slot \ "NSCharFlagsCanvas::StatusBar_Slot $oop $slot" $canvas bind $slot \ "\[NSCharFlagsCanvas::Info $oop statusBar] itemconfigure t1 -text {}" if 0 { # Focus ring (really only one is needed) set x2 [expr {$x - [icon size] / 2 - 2}] set y2 [expr {$y - [icon size] / 2 - 2}] $canvas create rectangle $x2 $y2 [expr {$x2 + [icon size] + 4}] \ [expr {$y2 + [icon size] + 4}] -outline Gray60 -fill "" \ -tags $slot,focus } incr x [expr {32 + 2}] } # Character icon $canvas create widget $x $y -anchor center -tags py $canvas bind py \ "\[NSCharFlagsCanvas::Info $oop statusBar] itemconfigure t1 -text {}" } NSBalloon::Delay $canvas 10 set canvas [Info $oop canvas] $canvas configure -width $width set x 2 ; set y 0 foreach flag $Priv(flags) label $labels { $canvas create rectangle $x $y [expr {$labelWidth - 2}] \ [expr {$y + $Priv(font,height) + 2}] -fill gray40 -tags $flag $canvas create rectangle $labelWidth $y $width \ [expr {$y + $Priv(font,height) + 2}] -fill "" -outline "" \ -tags "rect $flag $flag,rect" $canvas create text [expr {$labelWidth / 2}] [expr {$y + 1}] -anchor n \ -text $label -font $Priv(font,font) -fill White \ -tags [list $flag $flag,text text] $canvas bind $flag \ "%W itemconfigure $flag,rect -fill #333366 -outline #333366" $canvas bind $flag \ "%W itemconfigure $flag,rect -fill {} -outline {}" # Dot for each slot set x2 [expr {$labelWidth + 16}] foreach slot $Priv(slots) { $canvas create oval [expr {$x2 - 2}] [expr {$y + 9 - 2}] \ [expr {$x2 + 2}] [expr {$y + 9 + 2}] -fill "" -outline "" \ -tags "$flag,$slot $flag $flag,dot dot" incr x2 34 } # Dot for character $canvas create oval [expr {$x2 - 2}] [expr {$y + 9 - 2}] \ [expr {$x2 + 2}] [expr {$y + 9 + 2}] -fill "" -outline "" \ -tags "$flag,py $flag $flag,dot dot" incr y [expr {$Priv(font,height) + 3}] } return } # NSCharFlagsCanvas::SetInfo -- # # Set text of character-specific items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::SetInfo {oop} { variable Priv set header [Info $oop header,canvas] set canvas [Info $oop canvas] set rowHgt [expr {$Priv(font,height) + 3}] $canvas itemconfigure dot -fill "" -outline "" $canvas itemconfigure text -fill White foreach flag $Priv(flags) { set visible($flag) 0 } set y [expr {$rowHgt * [llength $Priv(flags)]}] foreach slot $Priv(slots) { angband equipment info $slot attrib if {$attrib(k_idx)} { NSBalloon::Set_Canvas $header \ $slot "$attrib(char)\) $attrib(name)" } else { set attrib(icon) "icon none 0" NSBalloon::Set_Canvas $header $slot "" } $header itemconfigure $slot -assign $attrib(icon) foreach flag [angband equipment flags $slot] { $canvas itemconfigure $flag,$slot -fill White -outline White set visible($flag) 1 } } foreach flag [angband player flags] { $canvas itemconfigure $flag,py -fill White -outline White set visible($flag) 1 } set desc "name, the race and class" NSBalloon::Set_Canvas $header py $desc foreach flag $Priv(flags) { if {!$visible($flag)} { $canvas itemconfigure $flag,text -fill gray60 } } $canvas configure -scrollregion [list 0 0 [winfo reqwidth $canvas] $y] $canvas yview moveto 0 Configure $oop # Hack -- Fix scroll glitch set frame [Info $oop frame] set scrollHgt $y set winHgt [expr {[winfo height $frame] - [winfo y $canvas]}] if {$winHgt % $rowHgt} { incr scrollHgt [expr {$rowHgt - ($winHgt % $rowHgt) - 1}] $canvas configure -scrollregion [list 0 0 [winfo reqwidth $canvas] $scrollHgt] } return } # NSCharFlagsCanvas::StatusBar_Slot -- # # Put the name of the item in the given slot in the status bar. # Also displays item recall. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::StatusBar_Slot {oop slot} { angband equipment info $slot attrib if {$attrib(k_idx)} { # Show name in statusbar [Info $oop statusBar] itemconfigure t1 -text $attrib(name) } return } # NSCharFlagsCanvas::Configure -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharFlagsCanvas::Configure {oop} { set frame [Info $oop frame] set canvas [Info $oop canvas] set doVert 0 set doHorz 0 if {[string compare [$canvas yview] "0 1"]} { set doVert 1 } if {[string compare [$canvas xview] "0 1"]} { set doHorz 1 } if {$doVert != [Info $oop scrollbar,vert]} { if {$doVert} { grid $frame.yscroll } else { grid remove $frame.yscroll } Info $oop scrollbar,vert $doVert } if {$doHorz != [Info $oop scrollbar,horz]} { if {$doHorz} { grid $frame.xscroll } else { grid remove $frame.xscroll } Info $oop scrollbar,horz $doHorz } return } zangband/lib/script/tk/charinfo-canvas.tcl0000644000000000000000000004426410250356274017614 0ustar rootroot# File: charinfo-canvas.tcl # Purpose: the Character Info canvas and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSCharInfoCanvas { # namespace eval NSCharInfoCanvas } # NSCharInfoCanvas::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::InitModule {} { return } # NSCharInfoCanvas::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::CloseModule {} { return } # NSCharInfoCanvas::NSCharInfoCanvas -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::NSCharInfoCanvas {oop parent} { set font [Global font,fixed,normal] set lineHgt [font metrics $font -linespace] set charWidth [font measure $font 9] set width [expr {$charWidth * 80}] set height [expr {$lineHgt * 25}] set canvas $parent.charinfocanvas$oop canvas $canvas \ -height $height -width $width \ -relief flat -highlightthickness 0 -background #000022 \ -yscrollcommand "$parent.yscroll set" \ -xscrollcommand "$parent.xscroll set" scrollbar $parent.yscroll \ -orient vertical -command "$canvas yview" scrollbar $parent.xscroll \ -orient horizontal -command "$canvas xview" grid rowconfig $parent 0 -weight 1 grid rowconfig $parent 1 -weight 0 grid columnconfig $parent 0 -weight 1 grid columnconfig $parent 1 -weight 0 grid $canvas \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $parent.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns grid $parent.xscroll \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid remove $parent.yscroll grid remove $parent.xscroll Info $oop scrollbar,vert 0 Info $oop scrollbar,horz 0 bind $canvas \ "+NSCharInfoCanvas::Configure $oop" bind $canvas \ "+NSCharInfoCanvas::Configure $oop" NSUtils::DestroyObjectWithWidget NSCharInfoCanvas $oop $canvas Info $oop canvas $canvas Info $oop font,font $font Info $oop font,width $charWidth Info $oop font,height $lineHgt Info $oop frame $parent Info $oop topRow 0 InitLayout $oop return } # NSCharInfoCanvas::~NSCharInfoCanvas -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::~NSCharInfoCanvas {oop} { return } # NSCharInfoCanvas::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::Info {oop info args} { global NSCharInfoCanvas # Verify the object NSObject::CheckObject NSCharInfoCanvas $oop # Set info if {[llength $args]} { switch -- $info { default { set NSCharInfoCanvas($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSCharInfoCanvas($oop,$info) } } } return } # NSCharInfoCanvas::InitLayout -- # # Create all the canvas items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::InitLayoutAux {oop group row anchor labelList} { set canvas [Info $oop canvas] set font [Info $oop font,font] foreach label $labelList { if {[string compare $label -]} { set label $label lappend idList [$canvas create text 0 0 -font $font -fill White \ -anchor $anchor -text $label -tags $group] } else { lappend idList - } } Info $oop id,$group $idList Info $oop anchor,$group $anchor Info $oop row,$group $row Info $oop minx,$group 0 return } proc NSCharInfoCanvas::InitLayoutAux2 {oop group row anchor tagList} { set canvas [Info $oop canvas] set font [Info $oop font,font] foreach tag $tagList { if {[string compare $tag -]} { lappend idList [$canvas create text 0 0 -font $font \ -anchor $anchor -fill White -tags [list $group $tag]] } else { lappend idList - } } if {[info exists ::NSCharInfoCanvas($oop,id,$group)]} { set idList [concat [Info $oop id,$group] $idList] } Info $oop id,$group $idList Info $oop anchor,$group $anchor Info $oop row,$group $row if {![info exists ::NSCharInfoCanvas($oop,minx,$group)]} { Info $oop minx,$group 0 } return } proc NSCharInfoCanvas::InitLayout {oop} { set canvas [Info $oop canvas] set row1 0 set rowX 1 set row2 8 set rowX $row1 incr row2 set labelList { "Name" "Sex" "Race" "Class" "Title" } lappend labelList \ "Realm 1" \ "Realm 2" \ "Patron" InitLayoutAux $oop tag1 0 nw $labelList set labelList { Age Height Weight Status } lappend labelList \ HP \ SP \ Maximize \ Preserve InitLayoutAux $oop tag2 $rowX nw $labelList Info $oop minx,tag2 25 set labelList { STR: INT: WIS: DEX: CON: CHR: } InitLayoutAux $oop tag3 1 nw $labelList Info $oop minx,tag3 41 set labelList { Self RB CB EB Best } foreach tag {max race class equip top} col {46 53 57 61 65} wid {6 3 3 3 6} label $labelList { InitLayoutAux $oop tag$tag 0 ne [list $label] Info $oop minx,tag$tag [expr {$col + $wid}] } set labelList { Level "Cur Exp" "Max Exp" "Adv Exp" - Gold - Burden } set labelList [lreplace $labelList 6 6 - Armor] InitLayoutAux $oop tag4 $row2 nw $labelList set labelList { "Fighting" "+ to Skill" "Deadliness" "Blows/Round" - "Shooting" "+ to Skill" "Deadliness" "Shots/Round" } InitLayoutAux $oop tag5 $row2 nw $labelList Info $oop minx,tag5 25 set labelList { "Saving Throw" "Stealth" "Fighting" "Shooting" "Disarming" "Magic Device" "Perception" "Searching" } lappend labelList Infravision InitLayoutAux $oop tag6 $row2 nw $labelList Info $oop minx,tag6 48 set tagList { name sex race class title } lappend tagList \ realm1 \ realm2 \ patron InitLayoutAux2 $oop tagA 0 nw $tagList Info $oop minx,tagA 9 set tagList { age height weight social_class } lappend tagList \ *hp \ *sp \ maximize \ preserve InitLayoutAux2 $oop tagB $rowX ne $tagList set col 33 Info $oop minx,tagB [expr {$col + 4}] set tagList { strength.max intelligence.max wisdom.max dexterity.max constitution.max charisma.max } InitLayoutAux2 $oop tagmax 0 ne $tagList set tagList { strength.race intelligence.race wisdom.race dexterity.race constitution.race charisma.race } InitLayoutAux2 $oop tagrace 0 ne $tagList set tagList { strength.class intelligence.class wisdom.class dexterity.class constitution.class charisma.class } InitLayoutAux2 $oop tagclass 0 ne $tagList set tagList { strength.equip intelligence.equip wisdom.equip dexterity.equip constitution.equip charisma.equip } InitLayoutAux2 $oop tagequip 0 ne $tagList set tagList { strength.top intelligence.top wisdom.top dexterity.top constitution.top charisma.top } InitLayoutAux2 $oop tagtop 0 ne $tagList set tagList { strength.use intelligence.use wisdom.use dexterity.use constitution.use charisma.use } InitLayoutAux2 $oop taguse 1 ne $tagList set tagList { level exp expmax expadv - gold - *burden } set tagList [lreplace $tagList 6 6 - *armor] InitLayoutAux2 $oop tagC $row2 ne $tagList Info $oop minx,tagC [expr {8 + 10}] set tagList { - *fskill *fdeadliness *blows - - *sskill *sdeadliness *shots } InitLayoutAux2 $oop tagD $row2 ne $tagList Info $oop minx,tagD [expr {30 + 13}] set tagList { saving_throw stealth fighting bows_throw disarming magic_device perception searching } lappend tagList *infra InitLayoutAux2 $oop tagE $row2 ne $tagList Info $oop minx,tagE [expr {62 + 9}] # History $canvas create text 0 0 -fill White -text "" \ -font [Info $oop font,font] -justify left -anchor nw -tags history return } # # Returns a "rating" of x depending on y # proc NSCharInfoCanvas::likert {x y} { variable likert_color # Paranoia if {$y <= 0} { set y 1 } # Negative value if {$x < 0} { set likert_color [Value TERM_L_RED] return "Very Bad" } # Analyze the value switch [expr {$x / $y}] { 0 - 1 { set likert_color [Value TERM_L_RED] return "Bad" } 2 { set likert_color [Value TERM_L_RED] return "Poor" } 3 - 4 { set likert_color [Value TERM_YELLOW] return "Fair" } 5 { set likert_color [Value TERM_YELLOW] return "Good" } 6 { set likert_color [Value TERM_YELLOW] return "Very Good" } 7 - 8 { set likert_color [Value TERM_L_GREEN] return "Excellent" } 9 - 10 - 11 - 12 - 13 { set likert_color [Value TERM_L_GREEN] return "Superb" } 14 - 15 - 16 - 17 { set likert_color [Value TERM_L_BLUE] return "Chaos Rank" } default { set likert_color Violet return [format "Amber \[%d\]" [expr {int((((($x / $y) - 17) * 5) / 2))}]] } } return } # NSCharInfoCanvas::PositionItems -- # # Arrange all the canvas items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::Layout {oop group x pad} { set canvas [Info $oop canvas] set rowHeight [Info $oop font,height] set charWidth [Info $oop font,width] scan [$canvas bbox $group] "%s %s %s %s" left top right bottom set width [expr {$right - $left}] set x2 $x if {[Info $oop anchor,$group] == "ne"} { incr x2 $width } if {$x2 < $charWidth * [Info $oop minx,$group]} { set x2 [expr {$charWidth * [Info $oop minx,$group]}] } set y [expr {4 + ([Info $oop topRow] + [Info $oop row,$group]) * $rowHeight}] foreach id [Info $oop id,$group] { if {[string compare $id -]} { $canvas coords $id $x2 $y } incr y $rowHeight } set pad [expr {$pad * $charWidth}] if {[Info $oop anchor,$group] == "ne"} { return [expr {$x2 + $pad}] } return [expr {$x2 + $width + $pad}] } proc NSCharInfoCanvas::PositionItems {oop} { set canvas [Info $oop canvas] set topRow [Info $oop topRow] set x [Layout $oop tag1 4 1] set x [Layout $oop tagA $x 1] set x [Layout $oop tag2 $x 1] set x [Layout $oop tagB $x 3] set x [Layout $oop tag3 $x 1] foreach tag {max race class equip top use} { set x [Layout $oop tag$tag $x 1] } set x [Layout $oop tag4 4 2] set x [Layout $oop tagC $x 7] set x [Layout $oop tag5 $x 3] set x [Layout $oop tagD $x 5] set x [Layout $oop tag6 $x 1] set x [Layout $oop tagE $x 0] set x [expr {4 + 50}] set row 19 set y [expr {($topRow + $row) * [Info $oop font,height]}] $canvas coords history $x $y return } # NSCharInfoCanvas::SetInfo -- # # Set text of character-specific items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::SetInfo {oop} { variable likert_color set canvas [Info $oop canvas] set attribs {name sex race class title} foreach attrib $attribs { $canvas itemconfigure $attrib -text [angband player $attrib] \ -fill [Value TERM_L_BLUE] } # Hit Points set info [angband player hitpoints] set cur [lindex $info 0] set max [lindex $info 1] if {$cur >= $max} { set fill [Value TERM_L_BLUE] } elseif {$cur > ($max * 5) / 10} { set fill [Value TERM_YELLOW] } else { set fill [Value TERM_L_RED] } $canvas itemconfigure *hp -text "$cur/$max" -fill $fill # Spell Points set info [angband player mana] set cur [lindex $info 0] set max [lindex $info 1] if {$cur >= $max} { set fill [Value TERM_L_BLUE] } elseif {$cur > ($max * 5) / 10} { set fill [Value TERM_YELLOW] } else { set fill [Value TERM_L_RED] } $canvas itemconfigure *sp -text "$cur/$max" -fill $fill set attribs {age height weight social_class patron} foreach attrib $attribs { set value [angband player $attrib] $canvas itemconfigure $attrib -text $value -fill [Value TERM_L_BLUE] } foreach attrib {realm1 realm2} { set text [angband player $attrib] if {[string equal $text "no magic"]} { set text "None" } $canvas itemconfigure $attrib -text $text -fill [Value TERM_L_BLUE] } foreach attrib {maximize preserve} { set text [expr {[angband player $attrib] ? "Y" : "N"}] $canvas itemconfigure $attrib -text $text -fill [Value TERM_L_BLUE] } set attribs {level gold} foreach attrib $attribs { $canvas itemconfigure $attrib -text [angband player $attrib] \ -fill [Value TERM_L_GREEN] } # Burden set weight [angband player total_weight] set units lb set burden [format "%d.%d $units" [expr {$weight / 10}] [expr {$weight % 10}]] $canvas itemconfigure *burden -text $burden -fill [Value TERM_L_GREEN] if 0 { # Stats set attribs [angband info stat_name] foreach attrib $attribs { angband player stat $attrib statInfo $canvas itemconfigure $attrib.max -text [cnv_stat_disp $statInfo(max)] \ -fill [Value TERM_L_GREEN] foreach mod {race class equip} { if {$statInfo($mod) > 99} { $canvas itemconfigure $attrib.$mod \ -text +++ -fill [Value TERM_L_BLUE] } else { $canvas itemconfigure $attrib.$mod \ -text [format "%+d" $statInfo($mod)] -fill [Value TERM_L_BLUE] } } $canvas itemconfigure $attrib.top -text [cnv_stat_disp $statInfo(top)] \ -fill [Value TERM_L_GREEN] if {$statInfo(use) < $statInfo(top)} { $canvas itemconfigure $attrib.use \ -text [cnv_stat_disp $statInfo(use)] -fill [Value TERM_YELLOW] } else { $canvas itemconfigure $attrib.use -text "" } } } # Experience set info [angband player exp] set cur [lindex $info 0] set max [lindex $info 1] set adv [lindex $info 2] if {$cur >= $max} { set fill [Value TERM_L_GREEN] } else { set fill [Value TERM_YELLOW] } if {$adv == 999999999} { set adv ***** } $canvas itemconfigure exp -text $cur -fill $fill $canvas itemconfigure expmax -text $max -fill [Value TERM_L_GREEN] $canvas itemconfigure expadv -text $adv -fill [Value TERM_L_GREEN] # Armor set info [angband player armor_class] set base [lindex $info 0] set plus [format "%+d" [lindex $info 1]] $canvas itemconfigure *armor -text "\[$base,$plus]" -fill [Value TERM_L_BLUE] $canvas itemconfigure *armor -fill [Value TERM_L_GREEN] # Fight set hit [format "%+d" [angband player to_hit]] set dam [format "%+d" [angband player to_dam]] $canvas itemconfigure *fight -text "($hit,$dam)" -fill [Value TERM_L_BLUE] # Melee set hit [angband player to_hit] set dam [angband player to_dam] angband equipinfo INVEN_WIELD dump if {$dump(known)} {incr hit $dump(to_h)} if {$dump(known)} {incr dam $dump(to_d)} set deadliness [angband player deadliness_conversion $dam] if {$dam > 0} { set percentdam [expr {100 + $deadliness}] } elseif {$dam > -31} { set percentdam [expr {100 - $deadliness}] } else { set percentdam 0 } $canvas itemconfigure *fskill -text $hit -fill [Value TERM_L_BLUE] $canvas itemconfigure *fdeadliness -text $percentdam% -fill [Value TERM_L_BLUE] # Shoot set hit [angband player to_hit] set dam 0 angband equipinfo INVEN_BOW dump if {$dump(known)} {incr hit $dump(to_h)} if {$dump(known)} {incr dam $dump(to_d)} incr dam [angband player to_dam] set deadliness [angband player deadliness_conversion $dam] if {$dam > 0} { set percentdam [expr {100 + $deadliness}] } elseif {$dam > -31} { set percentdam [expr {100 - $deadliness}] } else { set percentdam 0 } $canvas itemconfigure *sskill -text $hit -fill [Value TERM_L_BLUE] $canvas itemconfigure *sdeadliness -text $percentdam% -fill [Value TERM_L_BLUE] # Blows set blows [angband player blows_per_round] $canvas itemconfigure *blows -text $blows -fill [Value TERM_L_BLUE] # Shots set shots [angband player shots_per_round] $canvas itemconfigure *shots -text $shots -fill [Value TERM_L_BLUE] # Infravision set infra [angband player infravision] set units feet $canvas itemconfigure *infra -text "$infra $units" -fill [Value TERM_L_BLUE] set attribs {fighting bows_throw saving_throw stealth} append attribs { perception searching disarming magic_device} foreach attrib $attribs { $canvas itemconfigure $attrib \ -text [eval likert [angband player ability $attrib]] \ -fill $likert_color } $canvas itemconfigure history -text [angband player history] # Arrange the items PositionItems $oop scan [$canvas bbox all] "%s %s %s %s" left top right bottom set left 0 set top 0 $canvas configure -scrollregion "$left $top $right $bottom" return } # NSCharInfoCanvas::WipeInfo -- # # Clear text of character-specific items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::WipeInfo {oop} { set canvas [Info $oop canvas] lappend tags \ *fskill *fdeadliness *sskill *sdeadliness \ name sex race class title realm1 realm2 patron \ age height weight social_class *hp *sp maximize preserve \ *armor *fight *melee *shoot *blows *shots *infra \ level exp expmax expadv gold *burden \ fighting bows_throw saving_throw stealth \ perception searching disarming magic_device \ history foreach tag $tags { $canvas itemconfigure $tag -text {} } return } # NSCharInfoCanvas::AddTextItem -- # # Create a new canvas text item with given option values. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::AddTextItem {oop col row width fill text justify tags} { set canvas [Info $oop canvas] if {[string equal $justify right]} { incr col $width set anchor ne } else { set anchor nw } set padleft 4 set padtop 4 set x [expr {$padleft + $col * [Info $oop font,width]}] set y [expr {$padtop + $row * [Info $oop font,height]}] set width [expr {$width * [Info $oop font,width]}] $canvas create text $x $y -fill $fill -text $text \ -font [Info $oop font,font] -justify $justify -anchor $anchor \ -tags $tags return } # NSCharInfoCanvas::Configure -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSCharInfoCanvas::Configure {oop} { set frame [Info $oop frame] set canvas [Info $oop canvas] set doVert 0 set doHorz 0 if {[string compare [$canvas yview] "0 1"]} { set doVert 1 } if {[string compare [$canvas xview] "0 1"]} { set doHorz 1 } if {$doVert != [Info $oop scrollbar,vert]} { if {$doVert} { grid $frame.yscroll } else { grid remove $frame.yscroll } Info $oop scrollbar,vert $doVert } if {$doHorz != [Info $oop scrollbar,horz]} { if {$doHorz} { grid $frame.xscroll } else { grid remove $frame.xscroll } Info $oop scrollbar,horz $doHorz } return } zangband/lib/script/tk/choice-window.tcl0000644000000000000000000006401710250356274017307 0ustar rootroot# File: choice-window.tcl # Purpose: the Choice Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSChoiceWindow { variable Priv # namespace eval NSChoiceWindow } # Comment proc comment {} { Choose-spell 1 SetHook spell Term-fresh -active yes display,what = bookNum Term-fresh SetList Term-fresh -active no Choose-spell 0 SetHook default Term-fresh -active yes Track-inventory SetList (if hook=inventory) # Comment } # NSChoiceWindow::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::InitModule {} { NSObject::New NSChoiceWindow return } # NSChoiceWindow::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::CloseModule {} { catch { set oop [Global choice,oop] set win [Window choice] destroy $win } return } # NSChoiceWindow::NSChoiceWindow -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSChoiceWindow::NSChoiceWindow {oop} { Info $oop expand [Value choicewindow,autoexpand] Info $oop showIcon [Value choicewindow,showicon] InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow choice $win \ "NSChoiceWindow::GeometryCmd $oop" "" "NSChoiceWindow::DisplayCmd $oop" # Update ourself when the list highlight color changes Info $oop clientId,listHilite \ [NSValueManager::AddClient listHilite \ "$win.frame.text tag configure HOT \ -background \[Value listHilite]"] # Update ourself when the font changes Info $oop clientId,font,choice \ [NSValueManager::AddClient font,choice \ "NSChoiceWindow::ValueChanged_font_choice $oop"] Info $oop hook "" Info $oop current "" Info $oop choosing 0 Info $oop choosing,what "" # Info $oop default,hook hook_item Info $oop default,what inventory Info $oop display,what inventory Info $oop inConfigure 0 Info $oop busy 0 Info $oop expanded 0 # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSChoiceWindow $oop $win # # Global list of application windows # Global choice,oop $oop Window choice $win return } # NSChoiceWindow::~NSChoiceWindow -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::~NSChoiceWindow {oop} { NSValueManager::RemoveClient font,choice [Info $oop clientId,font,choice] NSValueManager::RemoveClient listHilite [Info $oop clientId,listHilite] return } # NSChoiceWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Info {oop info args} { global NSChoiceWindow # Set info if {[llength $args]} { switch -- $info { default { set NSChoiceWindow($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSChoiceWindow($oop,$info) } } } return } # NSChoiceWindow::InitWindow -- # # Create the window. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSChoiceWindow::InitWindow {oop} { set win .choice$oop toplevel $win wm title $win "Choice" wm transient $win [Window main] # Feed the Term when keys are pressed Term_KeyPress_Bind $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSChoiceWindow::Close $oop" # Start out withdrawn (hidden) wm withdraw $win # Set instance variables Info $oop win $win set frame $win.frame frame $frame -relief sunken -borderwidth 1 -background Black # Canvas to display icon set iconSize [expr {[icon size] + 8}] set canvas $frame.icon canvas $canvas \ -borderwidth 0 -width $iconSize -height $iconSize -background Black \ -highlightthickness 0 if 0 { $canvas create widget \ 6 6 -tags icon } # Create an arrow which appears when there is content out of site set x [expr {$iconSize / 2}] $canvas create polygon [expr {$x - 3}] 46 [expr {$x + 3}] 46 \ $x 49 -fill Red -outline Red -tags arrow text $frame.text \ -wrap none -width 50 -height 10 -font [Value font,choice] \ -borderwidth 0 -setgrid no -highlightthickness 0 \ -padx 4 -pady 2 -background Black -foreground White -cursor "" \ -yscrollcommand "$frame.yscroll set" \ -xscrollcommand "$frame.xscroll set" scrollbar $frame.yscroll \ -orient vertical -command "$frame.text yview" scrollbar $frame.xscroll \ -orient horizontal -command "$frame.text xview" # Bypass default Text bindings bindtags $frame.text [list $frame.text $win all] grid rowconfig $frame 0 -weight 1 grid rowconfig $frame 1 -weight 0 grid columnconfig $frame 0 -weight 0 grid columnconfig $frame 1 -weight 1 grid columnconfig $frame 2 -weight 0 grid $frame.icon -in $frame \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ns grid $frame.text -in $frame \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news grid $frame.yscroll \ -row 0 -column 2 -rowspan 1 -columnspan 1 -sticky ns grid $frame.xscroll \ -row 1 -column 1 -rowspan 1 -columnspan 1 -sticky ew grid remove $frame.yscroll grid remove $frame.xscroll Info $oop scrollbar,vert 0 Info $oop scrollbar,horz 0 if {![Info $oop showIcon]} { grid remove $frame.icon } pack $frame \ -expand yes -fill both # Set instance variables Info $oop icon $frame.icon Info $oop text $frame.text # Fiddle with the selection for list behaviour $frame.text tag configure HOT -foreground White \ -background [Value listHilite] $frame.text tag bind HOT \ "NSChoiceWindow::Invoke $oop \[$frame.text index {@%x,%y linestart}]" $frame.text tag bind HOT \ "NSChoiceWindow::DoubleClick $oop" $frame.text tag bind TEXT \ "NSChoiceWindow::Motion $oop \[$frame.text index {@%x,%y linestart}]" $frame.text tag bind HOT \ "NSChoiceWindow::Motion $oop {}" bind $win \ "NSChoiceWindow::Motion $oop {}" # bind $win {raise %W} # bind $win {raise [Window main]} # # Context Menu # set menu $win.context menu $menu -tearoff 0 # $frame.text tag bind TEXT \ # "NSChoiceWindow::ContextMenu_Hook $oop $menu %X %Y" bind $frame.icon \ "NSChoiceWindow::ContextMenu $oop $menu %X %Y" bind $frame.text \ "NSChoiceWindow::ContextMenu_Hook $oop $menu %X %Y" bind $win \ "NSChoiceWindow::Toggle $oop" # Window expands and contracts as the mouse enters and leaves it bindtags $win [concat [bindtags $win] ChoiceWindowBindTag] bind ChoiceWindowBindTag "NSChoiceWindow::Expand $oop" bind ChoiceWindowBindTag "NSChoiceWindow::Contract $oop" if {[Platform unix]} { # When the inactive window is clicked, I get a event # followed by an event. The Contract()'s the window # and removes the highlight (if any). bind ChoiceWindowBindTag " if {!\[NSChoiceWindow::HasCursor $oop]} { NSChoiceWindow::Contract $oop } " $frame.text tag bind HOT " if {!\[NSChoiceWindow::CursorHot $oop %x %y]} { NSChoiceWindow::Motion $oop {} } " bind $win " if {!\[NSChoiceWindow::HasCursor $oop]} { NSChoiceWindow::Motion $oop {} } " proc CursorHot {oop x y} { set text [Info $oop text] if {![llength [$text tag ranges HOT]]} {return 0} set index [$text index @$x,$y] if {[$text compare $index < HOT.first] || [$text compare $index > HOT.last]} {return 0} return 1 } } # When the window changes size, reposition the indicator arrow bind $frame.text \ "NSChoiceWindow::Configure $oop" return } # NSChoiceWindow::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::DisplayCmd {oop message first args} { switch -- $message { preDisplay { if {[Info $oop choosing]} { set hook hook_[Info $oop choosing,what] } else { #set hook hook_item } SetHook $oop $hook CallHook $oop fresh } postDisplay { Setting show_choices 0 Value choicewindow,show 1 } postWithdraw { SetHook $oop "" Value choicewindow,show 0 } } return } # NSChoiceWindow::GeometryCmd -- # # Called by NSWindowManager::Setup(). Returns the desired (default) # geometry for the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::GeometryCmd {oop} { set win [Info $oop win] set winMain [Window main] set spacing 0 set left 0 set top 0 set right [winfo screenwidth .] set bottom [winfo screenheight .] set x [expr {[NSToplevel::FrameLeft $winMain] + [NSToplevel::TotalWidth $winMain] / 2}] set width [NSToplevel::ContentWidth $win \ [expr {[NSToplevel::TotalWidth $winMain] / 2}]] set y [expr {[NSToplevel::FrameBottom $winMain] + $spacing}] if {$bottom - $y < 100} { set y [expr {$bottom - 100}] set height [NSToplevel::ContentHeight $win 100] } elseif {($y + [NSToplevel::TotalHeight $win]) < $bottom} { set height [winfo height $win] } else { set height [expr {$bottom - [NSToplevel::FrameBottom $winMain]}] set height [NSToplevel::ContentHeight $win $height] } return ${width}x$height+$x+$y } # NSChoiceWindow::Close -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSChoiceWindow::Close {oop} { NSWindowManager::Undisplay choice return } # NSChoiceWindow::SetHook -- # # Set the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::SetHook {oop hook} { if {[string length $hook]} { Info $oop hook $hook CallHook $oop open } elseif {[string length [Info $oop hook]]} { Info $oop hook "" } return } # NSChoiceWindow::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::CallHook {oop message args} { return [uplevel #0 NSChoiceWindow::[Info $oop hook] $oop $message $args] } # NSChoiceWindow::Fresh_Display -- # # Calls the hook to set the list, if required. Called as a command # on the "Term-fresh" quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Fresh_Display {oop} { CallHook $oop fresh return } # NSChoiceWindow::SetList -- # # Clears the text, sets the icon to "none 0" and calls the # hook to set the text. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::SetList {oop} { set win [Info $oop win] set icon [Info $oop icon] set textBox [Info $oop text] # Clear the text $textBox delete 1.0 end # Clear the icon $icon itemconfigure icon -assign "icon none 0" # Call the hook to set the list CallHook $oop set_list # Something is displayed Info $oop display something # No item is highlighted Info $oop current "" # Synch the scrollbars Configure $oop return } # NSChoiceWindow::Invoke -- # # Called when a list item is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Invoke {oop index} { set textBox [Info $oop text] set index [Info $oop current] set row [expr {[lindex [split $index .] 0] - 1}] CallHook $oop invoke $row return } # NSChoiceWindow::DoubleClick -- # # Called when a list item is double-clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::DoubleClick {oop} { set index [Info $oop current] set row [expr {[lindex [split $index .] 0] - 1}] CallHook $oop doubleclick $row return } # NSChoiceWindow::Motion -- # # Called when the mouse moves in a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Motion {oop index} { set textBox [Info $oop text] # If you invoke an item, hold down the mouse, and drag... if {![string length [Info $oop hook]]} return # No tracking while menu is up if {[Info $oop busy]} return # See if the item has changed if {$index == [Info $oop current]} return # An item is highlighted if {[string length [Info $oop current]]} { # Remove highlighting UnhighlightItem $oop [Info $oop current] } # An item is under the pointer if {[string length $index]} { # Highlight the item HighlightItem $oop $index } # Remember which item is highlighted Info $oop current $index return } # NSChoiceWindow::HighlightItem -- # # Highlights a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::HighlightItem {oop index} { set textBox [Info $oop text] set row [expr {[lindex [split $index .] 0] - 1}] # Highlight the item $textBox tag add HOT $index "$index lineend" $textBox tag raise HOT # Call the hook (to set the icon, for example) CallHook $oop highlight $row return } # NSChoiceWindow::UnhighlightItem -- # # Removes highlighting from a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::UnhighlightItem {oop index} { set win [Info $oop win] set icon [Info $oop icon] set textBox [Info $oop text] # Unhighlight the item $textBox tag remove HOT 1.0 end # Clear the icon $icon itemconfigure icon -assign "icon none 0" return } # NSChoiceWindow::GetItemCommand -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::GetItemCommand {oop index _command _label} { upvar $_command command $_label label set where [Info $oop display,what] set command "" set label "" angband $where info $index attrib set charItem $attrib(char) if {[string equal $where equipment]} { if {$attrib(known) && $attrib(activate)} { set label "Activate" set charCmd A } else { set label "Remove" set charCmd t } set command "DoKeymapCmd {} $charCmd $charItem" return } switch -glob -- $attrib(tval) { *_BOOK { set label "Browse" set charCmd b # Hack -- Browse shows all the books set command "DoKeymapCmd {} $charCmd {}" return } TV_ARROW - TV_BOLT - TV_SHOT { set label "Fire" set charCmd f } TV_FLASK { set label "Refuel" set charCmd F } TV_FOOD { set label "Eat" set charCmd E } TV_POTION { set label "Drink" set charCmd q } TV_SCROLL { set label "Read" set charCmd r } TV_SPIKE { set label "Jam" set charCmd j } TV_STAFF { set label "Use" set charCmd u } TV_ROD { set label "Zap" set charCmd z } TV_WAND { set label "Aim" set charCmd a } TV_BOW - TV_DIGGING - TV_HAFTED - TV_POLEARM - TV_SWORD - TV_BOOTS - TV_GLOVES - TV_HELM - TV_CROWN - TV_SHIELD - TV_CLOAK - TV_SOFT_ARMOR - TV_HARD_ARMOR - TV_DRAG_ARMOR - TV_LITE - TV_AMULET - TV_RING { set label "Wield" set charCmd w } } # If this item can be sold, set the default action to "Sell" if {[angband store shopping] && [string equal $where inventory]} { set match [angband inventory find -store_will_buy yes] if {[lsearch -exact $match $index] != -1} { set charCmd s if {[angband store ishome]} { set label "Drop" } else { set label "Sell" } } } if {[string length $label]} { set command "DoKeymapCmd {} $charCmd $charItem" } return } # NSChoiceWindow::HasCursor -- # # See if the cursor is over the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::HasCursor {oop} { set pointerx [winfo pointerx .] set pointery [winfo pointery .] set window [winfo containing $pointerx $pointery] if {![string length $window]} { return 0 } if {[string compare [winfo toplevel $window] [Info $oop win]]} { return 0 } return 1 } # NSChoiceWindow::Expand -- # # Resizes the Choice Window to display all of the information in it. # Does nothing if the window is already expanded. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Expand {oop} { variable Priv # Option: expand/contract if {![Info $oop expand]} return if {[Info $oop busy]} return if {[Info $oop expanded]} return set win [Info $oop win] set textBox [Info $oop text] set textHeight [winfo height $textBox] set lineHeight [font metrics [Value font,choice] -linespace] scan [$textBox index end] %d.%d numRows char set height [expr {$lineHeight * ($numRows - 1) + 4 + 2}] set winHeight [winfo height $win] set winWidth [winfo width $win] if {$height <= $winHeight} return # If the window is closer to the top of the screen, then # expand downwards, otherwise expand upwards. set top [NSToplevel::FrameTop $win] set topDist $top if {$topDist < 0} {set topDist 0} set bottom [NSToplevel::FrameBottom $win] set bottomDist [expr {[winfo screenheight $win] - $bottom}] if {$bottomDist < 0} {set bottomDist 0} if {$topDist < $bottomDist} { set expandUp 0 } else { set expandUp 1 } # Save the current window geometry Info $oop geometry [wm geometry $win] Info $oop busy 1 Info $oop expanded 1 raise $win set x [NSToplevel::FrameLeft $win] if {$expandUp} { set y [expr {[NSToplevel::FrameTop $win] - ($height - $winHeight)}] } else { set y [NSToplevel::FrameTop $win] } wm geometry $win ${winWidth}x$height+$x+$y update Info $oop busy 0 # If the cursor moved outside the Choice Window, collapse it if {![HasCursor $oop]} { Contract $oop } return } # NSChoiceWindow::Contract -- # # Restores the window geometry to the size it was before it was # expanded. Does nothing if the window is not expanded. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Contract {oop} { if {[Info $oop busy]} return if {![Info $oop expanded]} return Info $oop busy 1 Info $oop expanded 0 set win [Info $oop win] wm geometry $win [Info $oop geometry] update Info $oop busy 0 return } # NSChoiceWindow::ContextMenu_Hook -- # # When a list item is right-clicked, pop up a context # menu of actions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::ContextMenu_Hook {oop menu x y} { set text [Info $oop text] # Find the hit row set x1 [expr {$x - [winfo rootx $text]}] set y1 [expr {$y - [winfo rooty $text]}] set index [$text index "@$x1,$y1"] if {[lsearch -exact [$text tag names $index] TEXT] == -1} { ContextMenu $oop $menu $x $y return } set row [expr {[lindex [split $index .] 0] - 1}] # Clear the menu $menu delete 0 end # Set the menu if {[CallHook $oop context_menu $menu $row]} { # Hack -- Try to prevent collapsing while popup is visible. # It would be nice if "winfo ismapped $menu" worked Info $oop busy 1 # Pop up the menu tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } # after idle NSChoiceWindow::Info $oop busy 0 Info $oop busy 0 set index "" if {[NSUtils::HasCursor $text]} { set x [expr {[winfo pointerx $text] - [winfo rootx $text]}] set y [expr {[winfo pointery $text] - [winfo rooty $text]}] set index2 [$text index @$x,$y] foreach tag [$text tag names $index2] { if {[string equal $tag TEXT]} { set index "$index2 linestart" break } } } Motion $oop $index } return } # NSChoiceWindow::ContextMenu -- # # When the window is right-clicked, pop up a menu of options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::ContextMenu {oop menu x y} { set text [Info $oop text] $menu delete 0 end $menu add command -label "Set Font" \ -command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font choice" $menu add checkbutton -label "Auto Expand" \ -variable NSChoiceWindow($oop,expand) \ -command "NSChoiceWindow::OptionChanged $oop expand autoexpand" $menu add checkbutton -label "Show Icon" \ -variable NSChoiceWindow($oop,showIcon) \ -command "NSChoiceWindow::OptionChanged $oop showIcon showicon" $menu add separator $menu add command -label "Cancel" # Hack -- Try to prevent collapsing while popup is visible. # It would be nice if "winfo ismapped $menu" worked Info $oop busy 1 # Pop up the menu tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } # after idle NSChoiceWindow::Info $oop busy 0 Info $oop busy 0 set index "" if {[NSUtils::HasCursor $text]} { set x [expr {[winfo pointerx $text] - [winfo rootx $text]}] set y [expr {[winfo pointery $text] - [winfo rooty $text]}] set index2 [$text index @$x,$y] foreach tag [$text tag names $index2] { if {[string equal $tag TEXT]} { set index "$index2 linestart" break } } } Motion $oop $index return } proc NSChoiceWindow::hook_cmd_pet {oop message args} { switch -- $message { open { } fresh { SetList $oop } close { } set_list { set textBox [Info $oop text] # Keep a list of invoke chars set match {} # Process each command foreach {char label} [NSRecall::PetCmdInfo mode] { if {[string equal $char $mode]} { set color [Value TERM_L_BLUE] } else { set color White } # Append the character and description $textBox insert end "$char\) " TEXT $label \ [list ITEM_$char TEXT] "\n" $textBox tag configure ITEM_$char -foreground $color # Keep a list of chars and colors lappend match $char lappend colors $color } # Delete trailing newline $textBox delete "end - 1 chars" # Keep a list of chars and colors Info $oop match $match Info $oop color $colors } get_color { set row [lindex $args 0] return [lindex [Info $oop color] $row] } invoke { if {![Info $oop choosing]} return set row [lindex $args 0] set char [lindex [Info $oop match] $row] angband keypress $char } highlight { } } return } # NSChoiceWindow::Choose -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Choose {oop what show args} { if {[lsearch -exact [list cmd_pet ele_attack item] \ $what] == -1} return Info $oop choosing $show Info $oop choosing,what $what if {!$show} { Info $oop display,what [Info $oop default,what] SetHook $oop [Info $oop default,hook] return } switch -- $what { cmd_pet { SetHook $oop hook_cmd_pet } ele_attack { SetHook $oop hook_ele_attack } } return } # NSChoiceWindow::Configure -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Configure {oop} { # Lottsa foolishness here if {[Info $oop inConfigure]} return Info $oop inConfigure 1 set frame [Info $oop win].frame set canvas [Info $oop icon] set text [Info $oop text] # The "update idletasks" call below displays the arrow in the # wrong location if the window resized, so hide it if {[Info $oop showIcon]} { set bg [$canvas cget -background] $canvas itemconfigure arrow -fill $bg -outline $bg } set didVert 0 set didHorz 0 set done 0 while {!$done} { set done 1 set doVert 0 set doHorz 0 if {!$didVert} { if {[string compare [$text yview] "0 1"]} { set doVert 1 } if {[Info $oop expanded] || [Info $oop expand]} { set doVert 0 } if {$doVert != [Info $oop scrollbar,vert]} { if {$doVert} { grid $frame.yscroll set didVert 1 } else { grid remove $frame.yscroll } Info $oop scrollbar,vert $doVert set done 0 } } update idletasks if {!$didHorz} { if {[string compare [$text xview] "0 1"]} { set doHorz 1 } if {[Info $oop expanded] || [Info $oop expand]} { set doHorz 0 } if {$doHorz != [Info $oop scrollbar,horz]} { if {$doHorz} { grid $frame.xscroll set didHorz 1 } else { grid remove $frame.xscroll } Info $oop scrollbar,horz $doHorz set done 0 } } } if {[Info $oop showIcon]} { # Move the arrow into position scan [$canvas bbox arrow] "%s %s %s %s" left top right bottom set height [winfo height $text] $canvas move arrow 0 [expr {$height - $bottom - 4}] # Hide or show the arrow if {[Info $oop expand] && [string compare [$text yview] "0 1"]} { set fill Red $canvas itemconfigure arrow -fill $fill -outline $fill } } Info $oop inConfigure 0 return } # NSChoiceWindow::Toggle -- # # Swaps the default display between inventory and equipment. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::Toggle {oop} { if {[string compare [angband inkey_flags] INKEY_CMD]} return if {[string equal [Info $oop display,what] inventory]} { Info $oop display,what equipment Info $oop default,what equipment Info $oop default,hook hook_item } else { Info $oop display,what inventory Info $oop default,what inventory Info $oop default,hook hook_item } CallHook $oop fresh # Resize the window if needed if {[Info $oop expanded]} { set win [Info $oop win] set textBox [Info $oop text] set textHeight [winfo height $textBox] set lineHeight [font metrics [Value font,choice] -linespace] scan [$textBox index end] %d.%d numRows char set height [expr {$lineHeight * ($numRows - 1) + 4 + 2}] # Don't make it too small scan [Info $oop geometry] {%dx%d%[+-]%d%[+-]%d} width2 height2 xs x ys y if {$height < $height2} { set height $height2 } set winHeight [winfo height $win] set winWidth [winfo width $win] if {$height != $winHeight} { Info $oop busy 1 set y [expr {[winfo y $win] - ($height - $winHeight)}] wm geometry $win ${winWidth}x$height+[winfo x $win]+$y update Info $oop busy 0 } } return -code break } # NSChoiceWindow::OptionChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::OptionChanged {oop info keyword} { set setting [Info $oop $info] Value choicewindow,$keyword $setting switch -- $keyword { autoexpand { Configure $oop } showicon { if {$setting} { grid [Info $oop icon] } else { grid remove [Info $oop icon] } } } return } # NSChoiceWindow::ValueChanged_font_choice -- # # Called when the font,choice value changes. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSChoiceWindow::ValueChanged_font_choice {oop} { set text [Info $oop text] $text configure -font [Value font,choice] return } zangband/lib/script/tk/config.tcl0000644000000000000000000001352710250356274016015 0ustar rootroot# File: config.tcl # Purpose: icon configuration management # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSConfig { variable Priv # namespace eval NSConfig } # NSConfig::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::InitModule {} { # Read tk/config, which contains a list of icon configurations # and the current icon configuration. ReadConfigFile # Set the default set of files to pass to SourceOne. These # can be overridden by scripts to use common configuration # files. See ShareConfigFile() below. SetPrefix [Value config,prefix] return } # NSConfig::ReadConfigFile -- # # Reads the tk/config file, which holds a list of configuration # prefixes, along with descriptive text for each prefix. Each # prefix can be used to read and write certain icon configuration # files. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::ReadConfigFile {} { variable Priv if {[catch {open [PathTk config config]} fileId]} { set msg "The following error occurred while attempting to open " append msg "the \"config\" file for reading:\n\n$fileId" tk_messageBox -title Oops -message $msg return } while {![eof $fileId]} { # Read a line set count [gets $fileId list] if {$count == -1} break # Save the text, so it can be written out later lappend Priv(text) $list if {$count == 0} continue switch -- [lindex $list 0] { Config: { lappend Priv(config) [lindex $list 1] [lindex $list 2] } } } close $fileId return } # NSConfig::Load -- # # Processes the set of files for the "current" configuration set. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::Load {} { # Get the current configuration prefix set prefix [Global config,prefix] angband_load note $prefix # Try "prefix.cfg" SourceOne $prefix.cfg # Try prefixNN.cfg regsub {(16|24|32)} $prefix NN prefix SourceOne $prefix.cfg return } # NSConfig::InitIcons -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::InitIcons {iconSize} { angband init_icons $iconSize [winfo depth .] return } # NSConfig::SetPrefix -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::SetPrefix {prefix} { Value config,prefix $prefix Global config,prefix $prefix Global config,assign $prefix-assign Global config,town $prefix-town Global config,postop $prefix-postop return } # NSConfig::ShareConfigFile -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::ShareConfigFile {which file} { switch -- $which { assign - town { Global config,$which $file } default { error "unknown config file \"$which\"" } } return } # NSConfig::SourceOne -- # # Looks for the given file in the tk/config directory. If it # exists, it is sourced at the global level. This command is # usually called from a icon configuration file, type ".cfg". # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::SourceOne {fileName {required 0}} { set fileName [file tail $fileName] set path [PathTk config $fileName] if {[file exists $path]} { uplevel #0 source $path return } if {$required} { error "can't find file \"$fileName\"" } return } # NSConfig::Source -- # # Looks for the given file in the tk/config directory. If it # exists, it is sourced in the given namespace. This command is # usually called from a icon configuration file, type ".cfg". # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::Source {fileName namespace} { set fileName [file tail $fileName] set fileName [PathTk config $fileName] if {[file exists $fileName]} { ${namespace}::Source $fileName } return } # NSConfig::FileLibData -- # # Takes the "tail" of the given file name, and appends it to the # complete pathname of the image directory. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::FileLibData {file} { set file [file tail $file] return [PathTk image $file] } # NSConfig::FindImage -- # # Find an image file. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::FindImageFile {imageFile} { set path [PathTk image $imageFile] if {[file exists $path]} { return $path } set path [PathTk image dg [file tail $imageFile]] if {[file exists $path]} { return $path } error "icon image file \"$imageFile\" was not found" return } # NSConfig::NoMoreIcons -- # # This is a big silly hack called when all the icon types have # been created. It is used just so I can update the progress bar # during startup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSConfig::NoMoreIcons {} { global AngbandPriv set canvas $AngbandPriv(load,win).canvas $canvas itemconfigure message -text "Assigning icons..." update return } # Config::Assign -- # # A namespace with commands called when the tk/config/assign file # is sourced. # namespace eval Config::Assign { variable Priv #namespace eval Config::Assign } # Evaluate a script proc Config::Assign::Source {path} { source $path return } # Add an assign type proc Config::Assign::Assign {type} { variable Priv lappend Priv(assignType) $type return } # Add an icon type proc Config::Assign::Type {type} { variable Priv lappend Priv(type) $type return } # Start assigning to this group proc Config::Assign::Group {group} { variable Priv set Priv(group) $group return } proc Config::Assign::Feat {light background} { variable Priv feature configure $Priv(member) -light $light -background $background return } zangband/lib/script/tk/debug.tcl0000644000000000000000000004471210250356274015636 0ustar rootroot# File: debug.tcl # Purpose: the Debug Window for debugging # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSDebug { variable Priv set Priv(font) [Global font,fixed,normal] variable auto_lite 0 variable free_moves 0 variable see_monsters 0 # namespace eval NSDebug } # NSDebug::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::InitModule {} { variable Priv # Create the Debug Window NSObject::New NSDebug return } # NSDebug::NSDebug -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::NSDebug {oop} { InitWindow $oop set win [Info $oop win] NSToplevel::NaturalSize $win "" Info $oop display,current -1 Info $oop detail,current -1 if {[Platform windows]} { wm withdraw $win } wm geometry $win +20+20 update idletasks wm iconify $win return } # NSDebug::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::Info {oop info args} { global NSDebug # Set info if {[llength $args]} { switch -- $info { default { set NSDebug($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSDebug($oop,$info) } } } return } # NSDebug::InitWindow -- # # . # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::InitWindow {oop} { global NSDebug variable Priv set win .debug$oop toplevel $win wm title $win Debug # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSDebug::Close $oop" Info $oop win $win InitMenus $oop # Divider MakeDivider $win.divider x # Tabs! set tabsId [NSObject::New NSTabs $win] foreach {hook label} $Priv(hook) { NSTabs::Add $tabsId $label } NSTabs::Info $tabsId invokeCmd "NSDebug::InvokeTab $oop" NSTabs::Info $tabsId active 1 Info $oop tabsId $tabsId # List of monsters, objects, etc set frame [frame $win.frameList -relief sunken -borderwidth 1] set canvistId [NSObject::New NSTexist $frame $Priv(font) 60 10] set canvas [NSTexist::Info $canvistId text] $canvas configure -background [Value listBG] \ -yscrollcommand "$frame.yscroll set" \ -exportselection no scrollbar $frame.yscroll \ -orient vertical -command "$canvas yview" # Selection command NSTexist::Info $canvistId selectionCmd \ "NSDebug::SelectionChanged_Display $oop" Info $oop display,canvistId $canvistId # This call updates the list background color whenever the # global list background color changes Info $oop display,clientId \ [NSValueManager::AddClient listBG "ListBackgroundChanged $canvas"] pack $frame.yscroll -side right -fill y pack $canvas -side left -expand yes -fill both -anchor nw # Type list set frame [frame $win.frameList2 -relief sunken -borderwidth 1] set canvistId [NSObject::New NSTexist $frame $Priv(font) 60 9] set canvas [NSTexist::Info $canvistId text] $canvas configure -background [Value listBG] \ -yscrollcommand "$frame.yscroll set" \ -exportselection no scrollbar $frame.yscroll \ -orient vertical -command "$canvas yview" # Selection command NSTexist::Info $canvistId selectionCmd \ "NSDebug::SelectionChanged_Detail $oop" # Invoke command NSTexist::Info $canvistId invokeCmd \ "NSDebug::Invoke_Detail $oop" Info $oop detail,canvistId $canvistId # This call updates the list background color whenever the # global list background color changes Info $oop detail,clientId \ [NSValueManager::AddClient listBG "ListBackgroundChanged $canvas"] pack $frame.yscroll -side right -fill y pack $canvas -side left -expand yes -fill both -anchor nw # # Edit-value entry # set frame $win.frameValue frame $frame \ -borderwidth 0 label $frame.label \ -text "Value:" entry $frame.entry \ -width 20 -font [Global font,fixed,normal] bind $frame.entry \ "NSDebug::AcceptValue $oop" pack $frame.label -side left pack $frame.entry -side left Info $oop valueEntry $frame.entry # # Statusbar # MakeStatusBar $win.statusBar 20 # # Geometry # grid rowconfig $win 0 -weight 0 -minsize 0 grid rowconfig $win 1 -weight 0 -minsize 0 grid rowconfig $win 2 -weight 1 -minsize 0 grid rowconfig $win 3 -weight 0 -minsize 0 grid rowconfig $win 4 -weight 0 -minsize 0 grid rowconfig $win 5 -weight 0 -minsize 0 grid columnconfig $win 0 -weight 1 -minsize 0 if {[Platform windows]} { grid $win.divider \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew } grid [NSTabs::Info $tabsId canvas] \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.frameList \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.frameList2 \ -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.frameValue \ -row 4 -column 0 -rowspan 1 -columnspan 1 -sticky ew -pady 2 grid $win.statusBar \ -row 5 -column 0 -rowspan 1 -columnspan 1 -sticky ew return } # NSDebug::InitMenus -- # # Create the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::InitMenus {oop} { set win [Info $oop win] # Modifier key set mod Ctrl # # Menu bar # set mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSDebug::SetupMenus $oop" -identifier MENUBAR] Info $oop mbar $mbar # # Debug Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_DEBUG NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_DEBUG -label Debug -underline 0 -identifier M_DEBUG set entries {} lappend entries [list -type command -label "New Window" -command "NSObject::New NSDebug" -accelerator $mod+N -underline 0 -identifier E_NEW] lappend entries [list -type command -label "Debug Mode" -command "DoUnderlyingCommand ^A" -identifier E_DEBUG_MODE] lappend entries [list -type command -label "Reload" -command "InitDebug $oop" -identifier E_INIT_DEBUG] lappend entries [list -type separator] lappend entries [list -type command -label "Close" -command "NSDebug::Close $oop" -accelerator $mod+W -underline 0 -identifier E_CLOSE] NSMenu::MenuInsertEntries $mbar -end MENU_DEBUG $entries # # Character Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_CHARACTER NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_CHARACTER -label Character -underline 0 \ -identifier M_CHARACTER set entries {} lappend entries [list -type separator] lappend entries [list -type command -label "Cure All Maladies" \ -command "DoCommandIfAllowed ^Aa" -identifier E_CURE_ALL \ -accelerator a] lappend entries [list -type checkbutton -label "Free Moves *" \ -command "debug command free_moves" -onvalue 1 -offvalue 0 \ -variable NSDebug::free_moves -identifier E_FREE_MOVES] lappend entries [list -type command -label "Gain Experience" \ -command "DoCommandIfAllowed ^Ax" -identifier E_GAIN_EXPERIENCE \ -accelerator x] lappend entries [list -type command -label "Phase Door" \ -command "DoCommandIfAllowed ^Ap" -identifier E_PHASE_DOOR \ -accelerator p] lappend entries [list -type command -label "Rerate Hitpoints" \ -command "DoCommandIfAllowed ^Ah" -identifier E_RERATE_HITPOINTS \ -accelerator h] lappend entries [list -type command -label "Self Knowledge" \ -command "DoCommandIfAllowed ^Ak" -identifier E_SELF_KNOWLEDGE \ -accelerator k] lappend entries [list -type command -label "Teleport" \ -command "DoCommandIfAllowed ^At" -identifier E_TELEPORT \ -accelerator t] lappend entries [list -type command -label "Teleport To Target" \ -command "DoCommandIfAllowed ^Ab" -identifier E_TELEPORT_TO_TARGET \ -accelerator b] lappend entries [list -type command -label "Teleport To Location" \ -command "debug command teleport_to_location" \ -identifier E_TELEPORT_TO_LOCATION] NSMenu::MenuInsertEntries $mbar -end MENU_CHARACTER $entries # # Dungeon Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_DUNGEON NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_DUNGEON -label Dungeon -underline 0 -identifier M_DUNGEON set entries {} lappend entries [list -type checkbutton -label "Auto-Lite *" \ -command "debug command auto_lite" -onvalue 1 -offvalue 0 \ -variable NSDebug::auto_lite -identifier E_AUTO_LITE] lappend entries [list -type command -label "Detection" \ -command "DoCommandIfAllowed ^Ad" -identifier E_DETECTION \ -accelerator d] lappend entries [list -type command -label "Jump To Level" \ -command "DoCommandIfAllowed ^Aj" -identifier E_GOTO_LEVEL \ -accelerator j] lappend entries [list -type command -label "Magic Mapping" \ -command "DoCommandIfAllowed ^Am" -identifier E_MAGIC_MAPPING \ -accelerator m] lappend entries [list -type command -label "Wizard Dark" -command "" \ -identifier E_WIZARD_DARK] lappend entries [list -type command -label "Wizard Lite" \ -command "DoCommandIfAllowed ^Aw" -identifier E_WIZARD_LITE \ -accelerator w] NSMenu::MenuInsertEntries $mbar -end MENU_DUNGEON $entries # # Monster Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_MONSTER NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_MONSTER -label Monster -underline 0 -identifier M_MONSTER set entries {} lappend entries [list -type command -label "Mass Genocide" -command "DoCommandIfAllowed ^Az" -identifier E_MASS_GENOCIDE -accelerator z] lappend entries [list -type checkbutton -label "See Monsters *" -command "debug command see_monsters" -onvalue 1 -offvalue 0 -variable NSDebug::see_monsters -identifier E_SEE_MONSTERS] lappend entries [list -type command -label "Summon Random" -command "DoCommandIfAllowed ^As" -identifier E_SUMMON_ANY -accelerator s] NSMenu::MenuInsertEntries $mbar -end MENU_MONSTER $entries # # Object Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_OBJECT NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_OBJECT -label Object -underline 0 -identifier M_OBJECT set entries {} lappend entries [list -type command -label "Acquire Good" -command "DoCommandIfAllowed ^Ag" -identifier E_ACQUIRE_GOOD -accelerator g] lappend entries [list -type command -label "Acquire Very Good" -command "DoCommandIfAllowed ^Av" -identifier E_ACQUIRE_VERY_GOOD -accelerator v] lappend entries [list -type command -label "Allocate Artifact" -command "" -identifier E_CREATE_ARTIFACT] lappend entries [list -type command -label "Create Any" -command "" -identifier E_CREATE_OBJECT] lappend entries [list -type command -label "Identify" -command "DoCommandIfAllowed ^Ai" -identifier E_IDENTIFY -accelerator i] lappend entries [list -type command -label "Identify Fully" -command "^AI" -identifier E_IDENTIFY_FULLY -accelerator I] lappend entries [list -type command -label "Identfy Many" -command "" -identifier E_IDENTIFY_MANY] lappend entries [list -type command -label "Identify Pack" -command "" -identifier E_IDENTIFY_PACK] lappend entries [list -type separator] lappend entries [list -type command -label "Object List" -command "NSDebug::SetHook $oop debug_hook_object" -identifier E_OBJECT_LIST] NSMenu::MenuInsertEntries $mbar -end MENU_OBJECT $entries return } # NSDebug::SetupMenus -- # # Prepare to post the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::SetupMenus {oop mbarID} { set isCmd [expr {[string compare [angband inkey_flags] INKEY_CMD] == 0}] # See if "debug" mode was entered angband player cheat cheat # Debug Menu lappend identList E_NEW E_INIT_DEBUG E_CLOSE if {!$cheat(debug)} { lappend identList E_DEBUG_MODE } # Character Menu if {$isCmd && $cheat(debug)} { lappend identList E_CURE_ALL E_GAIN_EXPERIENCE E_RERATE_HITPOINTS \ E_PHASE_DOOR E_SELF_KNOWLEDGE E_TELEPORT E_TELEPORT_TO_TARGET \ E_TELEPORT_TO_LOCATION } lappend identList E_EQUIPMENT E_INVENTORY E_FREE_MOVES E_EDIT_CHARACTER # Dungeon Menu if {$isCmd && $cheat(debug)} { lappend identList E_DETECTION E_GOTO_LEVEL E_MAGIC_MAPPING \ E_WIZARD_DARK E_WIZARD_LITE } lappend identList E_AUTO_LITE # Monster Menu if {$isCmd && $cheat(debug)} { lappend identList E_MASS_GENOCIDE E_SUMMON_ANY } lappend identList E_SEE_MONSTERS E_MONSTER_LIST # Object Menu if {$isCmd && $cheat(debug)} { lappend identList E_ACQUIRE_GOOD E_ACQUIRE_VERY_GOOD \ E_CREATE_ARTIFACT E_CREATE_OBJECT E_IDENTIFY E_IDENTIFY_FULLY \ E_IDENTIFY_MANY E_IDENTIFY_PACK } lappend identList E_OBJECT_LIST NSMenu::MenuEnable $mbarID $identList return } # NSDebug::Close -- # # Do something when closing the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::Close {oop} { NSValueManager::RemoveClient listBG [Info $oop display,clientId] NSValueManager::RemoveClient listBG [Info $oop detail,clientId] destroy [Info $oop win] NSObject::Delete NSDebug $oop return } # NSDebug::SelectionChanged_Display -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::SelectionChanged_Display {oop canvistId select deselect} { # Nothing was selected if {![llength $select]} { # Clear detail list set canvistId [Info $oop detail,canvistId] NSTexist::Delete $canvistId 0 end # Clear statusbar [Info $oop win].statusBar itemconfigure t2 -text "" # No row is selected Info $oop display,current -1 # Done return } # Get the (first) row set row [lindex $select 0] Info $oop display,current $row CallHook $oop select_display $row return } # NSDebug::SelectionChanged_Detail -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::SelectionChanged_Detail {oop canvistId select deselect} { # If nothing was selected, clear the detail list if {![llength $select]} { [Info $oop valueEntry] delete 0 end Info $oop detail,current -1 return } # Get the (first) row set row [lindex $select 0] Info $oop detail,current $row CallHook $oop select_detail $row return } # NSDebug::Invoke_Detail -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::Invoke_Detail {oop canvistId x y} { set row [NSTexist::PointToRow $canvistId $x $y] if {$row == -1} return CallHook $oop edit $row return } # NSDebug::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::CallHook {oop message args} { return [uplevel #0 NSDebug::[Info $oop hook] $oop $message $args] } # NSDebug::SetHook -- # # Set the hook. The hook controls what is displayed. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::SetHook {oop hook} { variable Priv Info $oop hook $hook CallHook $oop open set tabsId [Info $oop tabsId] set current [NSTabs::Info $tabsId current] set tabId [NSTabs::GetNthId $tabsId [expr {[lsearch -exact $Priv(hook) $hook] / 2}]] if {$tabId != $current} { NSTabs::Smaller $tabsId $current NSTabs::Bigger $tabsId $tabId NSTabs::Info $tabsId current $tabId } return } # NSDebug::CalcLineLength -- # # Return the number of characters that will fit on a line.# # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::CalcLineLength {oop} { variable Priv set canvistId [Info $oop display,canvistId] set text [NSTexist::Info $canvistId text] set cw [font measure $Priv(font) "W"] set width [winfo width $text] return [expr {int([expr {($width - 4) / $cw}])}] } # NSDebug::SetList_Display -- # # Clears the display list, then calls the hook to append rows to it. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::SetList_Display {oop} { set canvistId [Info $oop display,canvistId] # Feedback StatusBar $oop "Displaying..." 0 update idletasks # Clear the list NSTexist::Delete $canvistId 0 end # Call hook to set the display list CallHook $oop set_list_display # Number of matches StatusBar $oop "Done." 1 # Hack -- Clear the detail list set canvistId [Info $oop detail,canvistId] NSTexist::Delete $canvistId 0 end return } # NSDebug::SetList_Detail -- # # Clears the detail list, then calls the hook to append rows to it. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::SetList_Detail {oop} { set canvistId [Info $oop detail,canvistId] # Clear the list NSTexist::Delete $canvistId 0 end # Call hook to set the detail list CallHook $oop set_list_detail return } # NSDebug::UpdateList_Detail -- # # Set a row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::UpdateList_Detail {oop row text color} { set canvistId [Info $oop detail,canvistId] # Delete old row NSTexist::Delete $canvistId $row $row # Insert row again NSTexist::Insert $canvistId $row $text $color # Select it NSTexist::UpdateSelection $canvistId $row {} return } # NSDebug::InvokeTab -- # # Called when a tab is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::InvokeTab {oop tabsId tabId} { variable Priv set index [lsearch -exact [NSTabs::Info $tabsId id] $tabId] SetHook $oop [lindex $Priv(hook) [expr {$index * 2}]] return } # NSDebug::StatusBar -- # # Displays text in the status bar, and optionally clears it later. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::StatusBar {oop text zap} { set win [Info $oop win] set label [$win.statusBar itemcget t1 -label] $label configure -text $text if {$zap} { NSUtils::ZapLabel $label } return } # NSDebug::List_Inventory -- # # Display the character's inventory in the first list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSDebug::ListInvOrEquip {oop invOrEquip} { global NSDebug set canvistId $NSDebug($oop,list) # Clear the list NSTexist::Delete $canvistId 0 end # Get a list of items set items [angband $invOrEquip find] # Append each item foreach index $items { angband $invOrEquip info $index attrib set desc $attrib(name) set color [default_tval_to_attr $attrib(tval)] set icon $attrib(icon) NSTexist::Insert $canvistId end $desc $color } return } proc NSDebug::AcceptValue {oop} { if {[Info $oop detail,current] == -1} return set entry [Info $oop valueEntry] set value [$entry get] CallHook $oop accept_value $value return } proc NSDebug::debug_hook_XXX {oop message args} { switch -- $message { open { } close { } select_display { } select_detail { } edit { } } return } proc InitDebug {oop} { catch {NSDebug::Close $oop} NSModule::RebootModule NSDebug return } zangband/lib/script/tk/errorInfo.tcl0000644000000000000000000000626010250356274016511 0ustar rootroot# File: errorInfo.tcl # Purpose: errorInfo display for debugging # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # proc tracecmd {text name1 name2 op} { global traceId global errorInfo global traceInfo global traceText set errorString $errorInfo if {![winfo exists $text]} { trace vdelete errorInfo w "tracecmd $text" return } # Hack - ignore the warnings about not finding the # file tclIndex.tcl They are harmless. if [string match "*tclIndex*" $errorString] { return } set length [string length $traceInfo] set string [string range $errorString 0 [expr {$length - 1}]] set newline 1 if {[string equal $traceInfo $string]} { set errorString [string range $errorString $length end] set newline 0 } if {[string equal $errorString $traceInfo]} return set traceInfo $errorString if {$newline} { append traceText "\n\n$errorString" } else { append traceText $errorString } if {![string length $traceId]} { set traceId [after idle traceflush $text] } return } proc traceflush {text} { global traceId global traceText global ErrorText append ErrorText $traceText $text insert end $traceText scan [$text index end] %d.%d line char if {$line > 1000} { $text delete 1.0 [expr {$line - 1000}].0 } $text see "end linestart" set traceId "" set traceText "" return } proc tracewindow {} { set win .errors toplevel $win wm title $win errorInfo frame $win.textFrame \ -relief sunken -borderwidth 1 scrollbar $win.yscroll \ -orient vertical -command [list $win.text yview] \ -takefocus 1 scrollbar $win.xscroll \ -orient horizontal -command [list $win.text xview] \ -takefocus 1 if {[Platform unix]} { set font {Courier 12} } if {[Platform windows]} { set font {Courier 9} } text $win.text \ -yscrollcommand [list $win.yscroll set] -wrap none \ -xscrollcommand [list $win.xscroll set] \ -width 82 -height 30 -font $font -borderwidth 0 \ -setgrid no -highlightthickness 0 -padx 4 -pady 2 \ -background Black -foreground White pack $win.textFrame \ -expand yes -fill both grid rowconfig $win.textFrame 0 -weight 1 -minsize 0 grid columnconfig $win.textFrame 0 -weight 1 -minsize 0 grid $win.text -in $win.textFrame \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.yscroll -in $win.textFrame \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news grid $win.xscroll -in $win.textFrame \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news return } proc tracesetup {} { global traceId global errorInfo global traceInfo global traceText global ErrorText tracewindow set win .errors set text $win.text trace variable errorInfo w "tracecmd $text" set traceId "" set traceInfo "" set traceText "" set ErrorText "" $text insert end "# Error Messages:\n\n" return } proc Debug {what} { if {![winfo exists .errors]} return if {![string equal [.errors.text get "end - 2 chars"] "\n"]} { .errors.text insert end \n } .errors.text insert end $what\n .errors.text see end return } tracesetup wm iconify .errors zangband/lib/script/tk/font.tcl0000644000000000000000000003125410250356274015513 0ustar rootroot# File: font.tcl # Purpose: the Font Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSFont { variable Priv # namespace eval NSFont } # NSFont::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::InitModule {} { variable Priv set Priv(items) {} lappend Priv(items) autobar lappend Priv(items) choice lappend Priv(items) inventory lappend Priv(items) knowledge lappend Priv(items) magic lappend Priv(items) message lappend Priv(items) messages lappend Priv(items) misc lappend Priv(items) miscPopup lappend Priv(items) monster lappend Priv(items) options lappend Priv(items) recall lappend Priv(items) status lappend Priv(items) statusBar lappend Priv(items) store set oop [NSObject::New NSFont] # Select the first item [Info $oop win].frameList.list selection set 0 ItemSelectionChanged $oop return } # NSFont::NSFont -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::NSFont {oop} { # Create the toplevel InitWindow $oop # Get the toplevel set win [Info $oop win] # Register toplevel with NSWindowManager NSWindowManager::RegisterWindow font $win \ "GetDefaultGeometry $win reqwidth reqheight" "" \ "NSFont::DisplayCmd $oop" # # Global list of application windows # Global font,oop $oop Window font $win return } # NSFont::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::Info {oop info args} { global NSFont # Verify the object NSObject::CheckObject NSFont $oop # Set info if {[llength $args]} { switch -- $info { default { set NSFont($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSFont($oop,$info) } } } return } # NSFont::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::InitWindow {oop} { variable Priv set win .font$oop toplevel $win wm title $win "Font" wm resizable $win no no wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSFont::Close $oop" # Remember the toplevel Info $oop win $win # Create the menus InitMenus $oop # # Divider # MakeDivider $win.divider2 x # # List of items # set frame $win.frameList frame $frame \ -borderwidth 1 -relief sunken listbox $frame.list \ -height 5 -width 35 -background White -borderwidth 0 \ -yscrollcommand "$frame.yscroll set" -highlightthickness 0 \ -exportselection no -selectmode extended scrollbar $frame.yscroll \ -command "$frame.list yview" grid rowconfigure $frame 0 -weight 1 grid columnconfigure $frame 0 -weight 1 grid columnconfigure $frame 1 -weight 0 grid $frame.list \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $frame.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns foreach item $Priv(items) { $frame.list insert end $item } # Configure the display when the item changes bind $frame.list <> \ "NSFont::ItemSelectionChanged $oop" # # Font face, style, and size # frame $win.frameFont \ -borderwidth 0 InitListPlusLabel $oop $win.frameFont.face "Font:" 20 InitListPlusLabel $oop $win.frameFont.size "Size:" 4 pack $win.frameFont.face -side left -padx 4 pack $win.frameFont.size -side left -padx 4 foreach family [lsort -dictionary [font families]] { $win.frameFont.face.frameList.list insert end $family } foreach size [list 8 9 10 11 12 14 16 18 20 22 24 26 28 36 48 72] { $win.frameFont.size.frameList.list insert end $size } # # Style checkbuttons # set frame $win.frameFont.frameStyle frame $frame \ -borderwidth 0 label $frame.label \ -text "Style:" -anchor w frame $frame.frameCheck \ -borderwidth 2 -relief groove checkbutton $frame.frameCheck.weight \ -text "Bold" -variable NSFont($oop,weight) \ -onvalue bold -offvalue normal \ -command "NSFont::SynchFont $oop" checkbutton $frame.frameCheck.slant \ -text "Italic" -variable NSFont($oop,slant) \ -onvalue italic -offvalue roman \ -command "NSFont::SynchFont $oop" pack $frame.label -side top -anchor w pack $frame.frameCheck.weight -side top -anchor w pack $frame.frameCheck.slant -side top -anchor w pack $frame.frameCheck -side top -anchor nw pack $frame -side left -anchor nw -padx 4 # # Sample text # frame $win.frameSample \ -borderwidth 2 -relief sunken -width 200 -height 60 text $win.frameSample.text \ -background [Global SystemButtonFace] -borderwidth 0 -wrap word pack propagate $win.frameSample no pack $win.frameSample.text $win.frameSample.text tag configure FontTag -justify center $win.frameSample.text insert end "The quick brown fox jumped over the lazy dog." FontTag $win.frameSample.text configure -state disabled # # Statusbar # MakeStatusBar $win.statusBar # frame $win.statusBar -relief flat -borderwidth 0 # label $win.statusBar.label -anchor w -relief sunken -padx 2 # pack $win.statusBar.label -side left -expand yes -fill both # # Geometry # grid rowconfig $win 0 -weight 0 grid rowconfig $win 1 -weight 1 grid rowconfig $win 2 -weight 0 grid rowconfig $win 3 -weight 0 grid rowconfig $win 4 -weight 0 grid columnconfig $win 0 -weight 1 if {[Platform windows]} { grid $win.divider2 \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew } grid $win.frameList \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news -padx 2 -pady 2 grid $win.frameFont \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.frameSample \ -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky ew -padx 10 -pady 4 grid $win.statusBar \ -row 4 -column 0 -rowspan 1 -columnspan 1 -sticky ew # # KeyPress bindings # bind $win "NSFont::Close $oop" bind $win "NSFont::Close $oop" return } # NSFont::InitMenus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::InitMenus {oop} { # Get the toplevel set win [Info $oop win] # Default accelerator modifier set mod "Ctrl" # # Menu bar # Info $oop mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSFont::SetupMenus $oop" -identifier MENUBAR] set mbar [Info $oop mbar] # Call our command when an entry is invoked NSMenu::Info $mbar invokeCmd "NSFont::MenuInvoke $oop" # # Font Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_FONT NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_FONT -label "Font" -underline 0 -identifier M_FONT set entries {} lappend entries [list -type command -label "Close" \ -underline 0 -accelerator $mod+W -identifier E_CLOSE] NSMenu::MenuInsertEntries $mbar -end MENU_FONT $entries return } # NSFont::SetupMenus -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::SetupMenus {oop mbarId} { lappend identList E_CLOSE NSMenu::MenuEnable $mbarId $identList return } # NSFont::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::MenuInvoke {oop menuId ident} { switch -glob -- $ident { E_CLOSE {Close $oop} } return } # NSFont::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::DisplayCmd {oop message first args} { variable Priv switch -- $message { preDisplay - reDisplay { if {[llength $args]} { set listbox [Info $oop win].frameList.list set row [lsearch -exact $Priv(items) [lindex $args 0]] $listbox selection clear 0 end $listbox selection set $row $listbox see $row ItemSelectionChanged $oop } } postDisplay { } } return } # NSFont::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::Close {oop} { NSWindowManager::Undisplay font return } # NSFont::InitListPlusLabel -- # # Creates a frame with a label, listbox and scrollbar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::InitListPlusLabel {oop parent title width} { frame $parent \ -borderwidth 0 label $parent.title \ -text $title set frame $parent.frameList frame $frame \ -borderwidth 1 -relief sunken listbox $frame.list \ -height 7 -width $width -background White -borderwidth 0 \ -yscrollcommand "$frame.yscroll set" -highlightthickness 0 \ -exportselection no scrollbar $frame.yscroll \ -command "$frame.list yview" # # List geometry # grid rowconfigure $frame 0 -weight 1 grid columnconfigure $frame 0 -weight 1 grid columnconfigure $frame 1 -weight 0 grid $frame.list \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $frame.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns # # Geometry # grid rowconfigure $parent 0 -weight 0 grid rowconfigure $parent 1 -weight 1 grid columnconfigure $frame 0 -weight 1 grid $parent.title \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky w grid $parent.frameList \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky w # Update the font when the selection changes bind $parent.frameList.list <> \ "NSFont::SynchFont $oop" return } # NSFont::ListSelectValue -- # # Looks for a given value in a listbox, selects it, and scrolls to it. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::ListSelectValue {oop parent value} { set listbox $parent.frameList.list set index 0 foreach item [$listbox get 0 end] { if {[string equal $item $value]} { $parent.frameList.list selection clear 0 end $parent.frameList.list selection set $index $parent.frameList.list see $index break } incr index } return } # NSFont::SynchFont -- # # Called when the font face, size, or style changes. Updates the # sample text with the new font, and sets the "item" font. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::SynchFont {oop} { variable Priv set win [Info $oop win] set selection [$win.frameList.list curselection] set count [llength $selection] if {$count == 0} return set row [$win.frameFont.face.frameList.list curselection] if {![string length $row]} return set family [$win.frameFont.face.frameList.list get $row] set row [$win.frameFont.size.frameList.list curselection] if {![string length $row]} return set size [$win.frameFont.size.frameList.list get $row] set weight [Info $oop weight] set slant [Info $oop slant] # Set the font description set font [list -family $family -size $size -weight $weight -slant $slant] # Update the sample text $win.frameSample.text tag configure FontTag -font $font # Update each "item" font, if asked if {![Info $oop noconfigure]} { foreach row $selection { set item [lindex $Priv(items) $row] Value font,$item $font } } return } # NSFont::ItemSelectionChanged -- # # Sets the font controls and sample text with the item's font. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSFont::ItemSelectionChanged {oop} { variable Priv set win [Info $oop win] set selection [$win.frameList.list curselection] set count [llength $selection] set synchFont 1 $win.frameSample.text configure -state normal $win.frameSample.text delete 1.0 end if {$count >= 1} { set row [lindex $selection 0] set item [lindex $Priv(items) $row] set font [fontdesc [Value font,$item]] foreach row [lrange $selection 1 end] { set item [lindex $Priv(items) $row] set font2 [fontdesc [Value font,$item]] if {[string compare $font2 $font]} { set synchFont 0 break } } } else { set synchFont 0 $win.frameFont.face.frameList.list selection clear 0 end $win.frameFont.size.frameList.list selection clear 0 end } if {$synchFont} { array set attrib [fontdesc $font] ListSelectValue $oop $win.frameFont.size $attrib(-size) ListSelectValue $oop $win.frameFont.face $attrib(-family) Info $oop weight $attrib(-weight) Info $oop slant $attrib(-slant) $win.frameSample.text insert end "The quick brown fox jumped over the lazy dog." FontTag Info $oop noconfigure 1 SynchFont $oop Info $oop noconfigure 0 } else { $win.frameFont.face.frameList.list selection clear 0 end $win.frameFont.size.frameList.list selection clear 0 end } $win.frameSample.text configure -state disabled if {$count == 1} { set string "1 item selected" } else { set string [format "%d items selected" $count] } $win.statusBar itemconfigure t1 -text $string return } zangband/lib/script/tk/info-window.tcl0000644000000000000000000001320110250356274016775 0ustar rootroot# File: info-window.tcl # Purpose: the Info Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSInfoWindow { # namespace eval NSInfoWindow } # NSInfoWindow::InitModule -- # # One-time-only-ever initialization. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSInfoWindow::InitModule {} { NSObject::New NSInfoWindow } # NSInfoWindow::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInfoWindow::CloseModule {} { catch { destroy [Window info] } return } # NSInfoWindow::NSInfoWindow -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. # # Results: # What happened. proc NSInfoWindow::NSInfoWindow {oop} { InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow info $win \ "GetDefaultGeometry $win main2 main" "" "" # Update ourself when the font,knowledge value changes Info $oop clientId,font,knowledge \ [NSValueManager::AddClient font,knowledge \ "NSInfoWindow::ValueChanged_font_knowledge $oop"] # Update ourself when the listBG value changes Info $oop clientId,listBG \ [NSValueManager::AddClient listBG \ "NSInfoWindow::ValueChanged_listBG $oop"] # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSInfoWindow $oop $win # # Global list of application windows # Global info,oop $oop Window info $win return } # NSInfoWindow::~NSInfoWindow -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInfoWindow::~NSInfoWindow {oop} { NSValueManager::RemoveClient listBG [Info $oop clientId,listBG] NSValueManager::RemoveClient font,knowledge [Info $oop clientId,font,knowledge] return } # NSInfoWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInfoWindow::Info {oop info args} { global NSInfoWindow # Verify the object NSObject::CheckObject NSInfoWindow $oop # Set info if {[llength $args]} { switch -- $info { default { set NSInfoWindow($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSInfoWindow($oop,$info) } } } return } # NSInfoWindow::InitWindow -- # # Create the window associated with the object. # # Arguments: # oop OOP ID. See above. # # Results: # . proc NSInfoWindow::InitWindow {oop} { set win .info$oop toplevel $win wm title $win XXXXX wm transient $win [Window main] # Start out withdrawn (hidden) wm withdraw $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSInfoWindow::Close $oop" # Set instance variables Info $oop win $win set frame $win.frameList frame $frame \ -relief sunken -borderwidth 1 set font [Value font,knowledge] set canvistId [NSObject::New NSTexist $frame $font 60 10] set canvas [NSTexist::Info $canvistId text] $canvas configure -background [Value listBG] $canvas configure -yscrollcommand "$frame.yscroll set" $canvas configure -xscrollcommand "$frame.xscroll set" scrollbar $frame.yscroll \ -orient vertical -command "$canvas yview" scrollbar $frame.xscroll \ -orient horizontal -command "$canvas xview" # This call updates the list background color whenever the # global list background color changes Info $oop list,clientId \ [NSValueManager::AddClient listBG "$canvas configure \ -background \[Value listBG]"] Info $oop canvistId $canvistId pack $win.frameList \ -expand yes -fill both # # Geometry # grid rowconfig $win.frameList 0 -weight 1 grid rowconfig $win.frameList 1 -weight 0 grid columnconfig $win.frameList 0 -weight 1 grid columnconfig $win.frameList 1 -weight 0 grid $canvas \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $frame.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns grid $frame.xscroll \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew # # Feed Term when keys pressed # bind $win { angband keypress %A } # Synch the scrollbars when window is shown. NSUtils::SynchScrollBar $canvas $win.frameList.yscroll NSUtils::SynchScrollBar $canvas $win.frameList.xscroll return } # NSInfoWindow::Close -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # . proc NSInfoWindow::Close {oop} { angband keypress \033 return } # NSInfoWindow::SetList -- # # Display a list of items. # # Arguments: # oop OOP ID. See above. # # Results: # . proc NSInfoWindow::SetList {oop title info} { set win [Info $oop win] set canvistId [Info $oop canvistId] wm title $win $title NSTexist::Delete $canvistId 0 end foreach elem $info { NSTexist::Insert $canvistId end $elem White } return } # NSInfoWindow::ValueChanged_font_knowledge -- # # Called when the font,knowledge value changes. # Updates the Info Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInfoWindow::ValueChanged_font_knowledge {oop} { set texistId [Info $oop canvistId] [NSTexist::Info $texistId text] configure -font [Value font,knowledge] return } # NSInfoWindow::ValueChanged_listBG -- # # Called when the listBG value changes. # Updates the Info Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInfoWindow::ValueChanged_listBG {oop} { set color [Value listBG] set texistId [Info $oop canvistId] [NSTexist::Info $texistId text] configure -background $color return } zangband/lib/script/tk/init-other.tcl0000644000000000000000000005531110250356274016627 0ustar rootroot# File: init-other.tcl # Purpose: post-character-generation initialization script # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # Toplevel headaches (Win32, Tk 8.3.3): # If a window is withdrawn, then "wm geometry $w" returns the geometry # from the last time the window was visible, *not* any requested geomtry. # Also, "wm geometry $w $g" doesn't actually change the geometry until # the window is displayed. proc tryGeometry {win geometry} { if {1 || [string compare $geometry [wm geometry $win]]} { wm geometry $win $geometry } return } # Deiconify a window. If the window does not appear at the location # we requested, then move it there. proc wmDeiconify {win} { set geometry [wm geometry $win] wm deiconify $win update tryGeometry $win $geometry } # Show a window. If the window does not appear at the location # we requested, then move it there. proc wmStateNormal {win} { set geometry [wm geometry $win] wm state $win normal update tryGeometry $win $geometry } proc WMSetWindowGeometry {win geometry} { wm geometry $win $geometry return } # ReadGeometryFile -- # # Reads the "geometry" file which contains the desired geometry # of each of the game's windows. If the "geometry" file does not # exist, then the game uses the default window positions, as set # in HarcodeGeometry() below. To create the "geometry" file, the # user can choose "Save Window Positions" from the Other Menu. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc ReadGeometryFile {} { global Angband global Windows if {[catch {open [PathTk config geometry]} fileId]} { set msg "The following error occurred while attempting to open " append msg "the \"geometry\" file for reading:\n\n$fileId" tk_messageBox -title Oops -message $msg return } set buf [read $fileId] close $fileId # Check each line foreach geomInfo [split $buf \n] { # Skip blank lines if {![string length $geomInfo]} continue # Skip comments if {[string equal [string index $geomInfo 0] #]} continue # Split line into window keyword and geometry if {[scan $geomInfo "%s %s" window geometry] != 2} continue # Request geometry. The window may not exist yet. NSWindowManager::RequestGeometry $window $geometry # Sanity: Windows(win) exists? if {![info exists Windows($window)]} continue # Get the toplevel pathname set win [Window $window] if {[scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y] != 6} { continue } # If this window is not resizeable, then ignore the given # height or width and use the dimension requested for the # window. set resize [wm resizable $win] if {![lindex $resize 0]} {set width [winfo reqwidth $win]} if {![lindex $resize 1]} {set height [winfo reqheight $win]} # If this is a gridded window, convert from dimensions in # pixels to grid units. set grid [wm grid $win] if {[llength $grid]} { set width [expr {$width / [lindex $grid 2]}] set height [expr {$height / [lindex $grid 3]}] } # Set the window geometry set geometry ${width}x$height$xs$x$ys$y WMSetWindowGeometry $win $geometry continue # Get the toplevel state set state [wm state $win] # If this toplevel is showing, then hide it first # to make the geometry request work if {[string equal $state normal]} { wm withdraw $win update idletasks } # Set the toplevel geometry tryGeometry $win $geometry # Restore the window if it was hidden by us if {[string equal $state normal]} { update idletasks wmDeiconify $win } } return } # WriteGeometryFile -- # # Writes the "geometry" file with the current geometry of each of the # game's windows. The "geometry" file is created if it does not already # exist. To create the "geometry" file, the user can choose "Save # Window Positions" from the Other Menu. # # Arguments: # arg1 about arg1 # # Results: # It seems a toplevel's geometry is not correct until is has been # displayed at least once. This routine brings any non-"normal" # windows to the front before getting its geometry. This looks bad. # It might be better to move the window offscreen first. # -> FixWindows() fixed this problem...? proc WriteGeometryFile {} { global Angband global Windows set tempName [NSUtils::TempFileName [PathTk config]] if {[catch {openlf $tempName} fileId]} { set msg "The following error occurred while attempting to open " append msg "the \"geometry\" file for writing:\n\n$fileId" tk_messageBox -title Oops -message $msg return } set success 1 if {[catch { puts $fileId "# Automatically generated. Do not edit.\n" # Be sure to keep any requested geometry for windows which may not # have been created. foreach name [array names NSWindowManager::Priv *,geomRequest] { regexp "(.*),geomRequest" $name ignore window set geometry $NSWindowManager::Priv($name) if {[scan $geometry {%dx%d%[+-]%d%[+-]%d} \ width height xs x ys y] != 6} continue set geometryInfo($window) $geometry } # Get the current geometry for existing windows. foreach window [array names Windows] { set win [Window $window] # HighScore destroys itself after character death if {![winfo exists $win]} continue # Get the window geometry if {[scan [wm geometry $win] {%dx%d%[+-]%d%[+-]%d} \ width height xs x ys y] != 6} continue # If this is a gridded window, the geometry information # is returned as {columns rows columnWidth rowWidth}. In # this case I save the total area of the grid in pixels. # This is needed because (1) the user can switch between # 16x16 and 32x32 icons at startup, and (2) the window # may not be gridded in the next version of the game. set grid [wm grid $win] if {[llength $grid]} { set width [expr {$width * [lindex $grid 2]}] set height [expr {$height * [lindex $grid 3]}] } set geometryInfo($window) ${width}x$height$xs$x$ys$y } # Write the geometry for each window foreach window [lsort -dictionary [array names geometryInfo]] { puts $fileId "$window $geometryInfo($window)" } # catch } result]} { set msg "The following error occurred while attempting to write " append msg "the \"geometry\" file:\n\n$result" tk_messageBox -title Oops -message $msg set success 0 } close $fileId if {$success} { set fileName [NSUtils::ReadLink [PathTk config geometry]] if {[file exists $fileName]} { file rename -force -- $fileName $fileName.bak } file rename -- $tempName $fileName } else { file delete $tempName } return } # HardcodeGeometry -- # # Sets the position of all the game's windows to an appropriate # default position. This is done at startup if the "geometry" file does # not exist, and when the user chooses "Arrange Windows" from # the Other Menu. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc HardcodeGeometry {} { global Windows set spacing 0 set offset [winfo screenwidth .] # Tk 8.3.0 bug set win [Window main] set visible(main) [winfo ismapped $win] wm geometry $win +$offset+0 update idletasks wm deiconify $win # Move each window offscreen, and show it. foreach window [array names Windows] { # Tk 8.3.0 bug if {[string equal $window main]} continue set win [Window $window] set visible($window) [winfo ismapped $win] wm geometry $win +$offset+0 update idletasks wm deiconify $win } # Must be update, not "update idletasks", or the Main Window geometry # is silly (too tall). Don't know why. update # Message Window set win1 [Window message] set x 0 ; incr x $offset wm geometry $win1 +$x+0 update idletasks # Main Window set x 0 ; incr x $offset if {[Value message,float]} { set y [expr {[NSToplevel::FrameBottom $win1] + $spacing}] } else { set y 0 } set win1 [Window main] wm geometry $win1 +$x+$y update idletasks # Misc Window set y [expr {[NSToplevel::FrameBottom $win3] + $spacing}] set win1 [Window misc] wm geometry $win1 +$x+$y update idletasks # Progress Window set y [expr {[NSToplevel::FrameBottom $win1] + $spacing}] set win1 [Window progress] wm geometry $win1 +$x+$y update idletasks # Message Window (width) set win2 [Window message] set x 0 ; incr x $offset set width [NSToplevel::ContentWidth $win2 \ [expr {0 - $x}]] wm geometry $win2 ${width}x[winfo height $win2]+$x+0 update idletasks # Tips Window (centered) if {[info exists Windows(tip)]} { set win [Window tip] set x2 [expr {([winfo screenwidth $win] - [winfo reqwidth $win]) / 2 \ - [winfo vrootx $win] + $offset}] set y2 [expr {([winfo screenheight $win] - [winfo reqheight $win]) / 3 \ - [winfo vrooty $win]}] wm geometry $win +$x2+$y2 } NSWindowManager::ArrangeAll # Iterate over each window. Move the mapped window from its offscreen # position onto the screen again. If it wasn't mapped previously, then # hide the window. foreach window [array names Windows] { set win [Window $window] scan [wm geometry $win] {%dx%d%[+-]%d%[+-]%d} width height xs x ys y incr x -$offset wm geometry $win ${width}x$height$xs$x$ys$y if {$visible($window)} continue # Tk 8.3.0 bug if {[string equal $window main]} continue wm withdraw $win } # Tk 8.3.0 bug if {!$visible(main)} { wm withdraw [Window main] } update return } if {[Platform unix]} { proc HardcodeGeometry {} { global Windows set screenWidth [winfo screenwidth .] set spacing 0 # Tk 8.3.0 bug set win [Window main] set visible(main) [winfo ismapped $win] if {!$visible(main)} { wm state $win normal } # Move each window offscreen, and show it. foreach window [array names Windows] { # Tk 8.3.0 bug if {[string equal $window main]} continue set win [Window $window] set visible($window) [winfo ismapped $win] if {!$visible($window)} { wm state $win normal } } # Must be update, not "update idletasks", or the Main Window geometry # is silly (too tall). Don't know why. update # Message Window set win1 [Window message] wm geometry $win1 +0+0 update # Main Window set x 0 if {[Value message,float]} { set y [expr {[NSToplevel::FrameBottom $win1] + $spacing}] } else { set y 0 } set win1 [Window main] wm geometry $win1 +$x+$y update # Misc Window set y [expr {[NSToplevel::FrameBottom $win3] + $spacing}] set win1 [Window misc] wm geometry $win1 +$x+$y update # Progress Window set y [expr {[NSToplevel::FrameBottom $win1] + $spacing}] set win1 [Window progress] wm geometry $win1 +$x+$y update # Message Window (width) set win2 [Window message] set x 0 set width [NSToplevel::ContentWidth $win2 \ [expr {0 - $x}]] wm geometry $win2 ${width}x[winfo height $win2]+$x+0 update # Tips Window (centered) if {[info exists Windows(tip)]} { set win [Window tip] set x2 [expr {([winfo screenwidth $win] - [winfo reqwidth $win]) / 2 \ - [winfo vrootx $win]}] set y2 [expr {([winfo screenheight $win] - [winfo reqheight $win]) / 3 \ - [winfo vrooty $win]}] wm geometry $win +$x2+$y2 } NSWindowManager::ArrangeAll # Iterate over each window. Move the mapped window from its offscreen # position onto the screen again. If it wasn't mapped previously, then # hide the window. foreach window [array names Windows] { set win [Window $window] scan [wm geometry $win] {%dx%d%[+-]%d%[+-]%d} width height xs x ys y if {!$visible($window)} { # Tk 8.3.0 bug if {![string equal $window main]} { wm withdraw $win update idletasks } } } # Tk 8.3.0 bug if {!$visible(main)} { wm withdraw [Window main] } update return } # unix } # MaximizeWindows -- # # Calculates the largest possible size for the Main Window, resizes # the Main Window, then positions all the other game windows accordingly. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc MaximizeWindows {} { set left 0 set top 0 set right [winfo screenwidth .] set bottom [winfo screenheight .] # Start with the Main Window as big as the desktop set width $right set height $bottom set winMessage [Window message] set winMain [Window main] set winMisc [Window misc] set winRecall [Window recall] # Subtract the width of the Misc Window. set width [expr {$width - $widthMisc}] # Sutbtract the height of the Message Window if {[Value message,float]} { set height [expr {$height - [NSToplevel::TotalHeight $winMessage]}] } # Subtract the height of the Recall Window or Choice Window if {[Value recall,show]} { set height [expr {$height - [NSToplevel::TotalHeight $winRecall]}] } elseif {[Value choicewindow,show]} { set height [expr {$height - [NSToplevel::TotalHeight [Window choice]]}] } # Subtract the height of other subwindows of the Main Window. # This is done because I know main,widget is gridded, and # cannot therefore simply set the height of the window itself. set grid [wm grid $winMain] if {[llength $grid]} { set height2 [winfo height [Global main,widget]] set height [expr {$height - ([winfo height $winMain] - $height2)}] } # Calculate the required dimensions of the content area of # the Main Window set width [NSToplevel::ContentWidth $winMain $width] set height [NSToplevel::ContentHeight $winMain $height] # If the Main Window is a gridded window, convert from dimensions in # pixels to dimensions in grid units. set grid [wm grid $winMain] if {[llength $grid]} { set gridWidth [lindex $grid 2] set gridHeight [lindex $grid 3] set width [expr {$width / $gridWidth}] set height [expr {$height / $gridHeight}] } # Set the geometry of the Main Window wm geometry $winMain ${width}x$height update idletasks if {[Value recall,show]} { # Grow the Recall Window vertically set height [expr {$bottom - [NSToplevel::TotalHeight $winMessage] \ - [NSToplevel::TotalHeight $winMain]}] set height [NSToplevel::ContentHeight $winRecall $height] wm geometry $winRecall [winfo width $winRecall]x$height } # Arrange the other windows relative to the Main Window HardcodeGeometry return } # FixWindows -- # # Here's a stinker for you. In "inventory.tcl" the command CalcLineLength() # calls "winfo width $canvas". Well, if the Inventory Window was never # shown, then "winfo width $canvas" returns a useless value. Ditto for the # Store Window. So to avoid this problem (and any other similar ones) # I call this command to move all the windows offscreen, show them, then # hide them again, once, during startup. This isn't a problem when # starting up with HardcodeGeometry(), only when setting window positions # with ReadGeometryFile(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc FixWindows {} { global Windows set offset [winfo screenwidth .] # Can anyone make this simpler? It is completely beyond me. foreach window [array names Windows] { set win [Window $window] # "winfo geometry" returns bad x,y values, as do both # "winfo x" and "winfo"y" at this point... set geomList [split [wm geometry $win] +x] set x [lindex $geomList 2] set y [lindex $geomList 3] wm geometry $win +$offset+0 update idletasks # wm deiconify $win wm state $win normal # "update idletasks" does NOT work here update wm withdraw $win update idletasks wm geometry $win +$x+$y update idletasks } return } # GetDefaultGeometry -- # # Calculates the default geometry for one of the secondary game windows, # such as the Inventory, Character or Knowledge Window. The top edge is # the same as the Main Window, the left edge zero, and the right edge # is 800 pixels, or the width of the screen, whichever is less. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc GetDefaultGeometry {win reqWidth reqHeight} { set winMain [Window main] set screenWidth [winfo screenwidth $winMain] # If this is being called in HardcodeGeometry(), then the window # should be positioned offscreen. if {[winfo x $winMain] >= $screenWidth} { set x $screenWidth } else { set x 0 } # If the Message Window is embedded, then windows should be # below it. if {[Value message,float]} { set y [NSToplevel::FrameTop $winMain] } else { set y [winfo rooty [Window main].message] incr y [winfo height [Window main].message] incr y 2 } # BUG: When a window is minimized (not withdrawn), Win32 tells # us that rootx and rooty are "3000". # Calculate the width switch -- $reqWidth { main { set width [NSToplevel::TotalWidth $winMain] if {$width > 600} { set width 600 } set width [NSToplevel::ContentWidth $win $width] } main2 { if {$screenWidth < 800} { set width [NSToplevel::ContentWidth $win $screenWidth] } else { set width [NSToplevel::TotalWidth $winMain] if {$width > 600} { set width 600 } set width [NSToplevel::ContentWidth $win $width] } } reqwidth { set width [winfo reqwidth $win] } screen { if {$screenWidth > 600} { set width 600 } else { set width $screenWidth } set width [NSToplevel::ContentWidth $win $width] } default { set width [NSToplevel::ContentWidth $win $reqWidth] } } # Calculate the height switch -- $reqHeight { main { set dy [expr {$y - [NSToplevel::FrameTop $winMain]}] set height [expr {[NSToplevel::TotalHeight $winMain] - $dy}] if {$height > 400} { set height 400 } set height [NSToplevel::ContentHeight $win $height] } reqheight { set height [winfo reqheight $win] } default { set height [NSToplevel::ContentHeight $win $reqHeight] } } return ${width}x$height+$x+$y } # StripCommon -- # # Remove matching elements in path2 from path1. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc StripCommon {path1 path2} { if {[file exists $path1]} { set path1 [LongName $path1] } if {[file exists $path2]} { set path2 [LongName $path2] } set list1 [file split $path1] set list2 [file split $path2] set len [llength $list2] return [lrange $list1 $len end] } # IsFileInX -- # # Determine whether path1 is a child of path2. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc IsFileInX {path1 path2 {deep 1}} { if {[file exists $path1]} { set path1 [LongName $path1] } if {[file exists $path2]} { set path2 [LongName $path2] } foreach elem1 [file split $path1] elem2 [file split $path2] { if {[Platform windows]} { set elem1 [string tolower $elem1] set elem2 [string tolower $elem2] } if {![string length $elem1]} { return 0 } if {![string length $elem2]} { if {$deep} { return 1 } if {[llength [file split $path1]] == [llength [file split $path2]] + 1} { return 1 } return 0 } if {[string compare $elem1 $elem2]} { return 0 } } return 1 } # IsUserFile -- # # Determine whether the given file is inside the lib/user directory. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc IsUserFile {path} { return [IsFileInX $path [PathUser] 0] } # IsFileInPath -- # # Determine whether the given file is a child of the [Path] directory. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc IsFileInPath {path} { return [IsFileInX $path [PathTk]] } proc InitModules {} { NSModule::IndexLoad [PathTk moduleIndex.tcl] return } # InitOther -- # # The main initialization command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc InitOther {} { global Angband InitLoadWindow angband_load progress 0.25 angband_load note "Sourcing scripts..." Source keypress.tcl Source angband.tcl angband_load progress 0.45 angband_load note "Initializing icons..." # Load the configuration files from the "current" set NSConfig::Load angband_load progress 0.75 angband_load note "Initializing modules..." InitModules angband_load note "Initializing: Main Window" NSModule::LoadIfNeeded NSMainWindow angband_load progress 0.80 angband_load note "Initializing: Aux Windows" NSModule::LoadIfNeeded NSMiscWindow angband_load progress 0.85 angband_load note "Initializing: Recall" NSModule::LoadIfNeeded NSRecall angband_load note "Initializing: Info" NSObject::New NSInfoWindow # Synch windows with options if {[Value message,float]} { grid remove [Window main].message } else { Global message,message [Window main].message.message } if {[Value misc,float]} { grid remove [Window main].misc } else { Value misc,layout tall ; # Paranoia Global misc,canvas [Window main].misc.misc } if {[Platform unix]} { if {![file exists [PathTk config geometry]]} { wm geometry [Window main] +0+0 wm geometry [Window message] +0+0 wm geometry [Window recall] +0+0 wm geometry [Window misc] +0+0 wm geometry [Window progress] +0+0 update idletasks wm deiconify [Window main] if {[Value message,float]} { wm state [Window message] normal } if {[Value recall,show]} { # wm state [Window recall] normal NSWindowManager::Display recall } if {[Value misc,float]} { wm state [Window misc] normal if {[Value misc,layout] == "wide"} { wm state [Window progress] normal } } } # unix } angband_load progress 0.95 if {[file exists [PathTk config geometry]]} { angband_load note "Reading geometry file..." ReadGeometryFile # FixWindows } else { angband_load note "Setting default window positions..." HardcodeGeometry } update idletasks # if {$::DEBUG} { # NSModule::AddModule NSDebug [PathTk debug.tcl] # NSModule::LoadIfNeeded NSDebug # } # The load window is obscured below angband_load progress 1.0 # Show windows which should be shown if {[Platform unix]} { if {[file exists [PathTk config geometry]]} { wmDeiconify [Window main] if {[Value recall,show]} { # wmStateNormal [Window recall] NSWindowManager::Display recall } if {[Value misc,float]} { wmStateNormal [Window misc] if {[Value misc,layout] == "wide"} { wmStateNormal [Window progress] } } if {[Value message,float]} { wmStateNormal [Window message] } } } if {[Platform windows]} { wm deiconify [Window main] update if {[Value recall,show]} { NSWindowManager::Display recall # wm state [Window recall] normal update } if {[Value misc,float]} { wm state [Window misc] normal update if {[Value misc,layout] == "wide"} { wm state [Window progress] normal update } } if {[Value message,float]} { wm state [Window message] normal update } } if {[Value choicewindow,show]} { NSModule::LoadIfNeeded NSChoiceWindow NSWindowManager::Display choice } if {[Value message2window,show]} { NSModule::LoadIfNeeded NSMessageWindow NSWindowManager::Display message2 } update # Focus on Main Window # It seems important to do this before hiding the Load Window, # otherwise the application swaps into the background focus [Window main] # Done with "load" window angband_load kill # Show the Tips Window if desired if {[Value tip,show]} { NSModule::LoadIfNeeded NSTips } return } # Main initialization command InitOther zangband/lib/script/tk/init-startup.tcl0000644000000000000000000003603710250356274017214 0ustar rootroot# File: init-startup.tcl # Purpose: the Startup Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # This is the first file sourced during program initialization. It is # sourced before init_angband() is called. # Global -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc Global {info args} { global Global # Set info if {[llength $args]} { set Global($info) [lindex $args 0] # Get info } else { return $Global($info) } return } # Platform -- # # Return 1 if we are running on any of the given platforms. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc Platform {args} { global Angband if {![llength $args]} { return $Angband(platform) } foreach name $args { if {[string equal $name $Angband(platform)]} { return 1 } } return 0 } # Window -- # # Global window info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc Window {info args} { global Windows # Set info if {[llength $args]} { set Windows($info) [lindex $args 0] # Get info } else { return $Windows($info) } return } # Source -- # # Source a .tcl file in the tk directory or a subdirectory of it. # Post a warning if the file is not found. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc Source {args} { global Angband set path [eval file join [list $Angband(dirTk)] $args] if {![file exists $path]} { error "file not found:\n $args, $path" } uplevel #0 source $path return } # LongName -- # # Under Windows, return -longname of the given file. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc LongName {name} { if {[string equal $::tcl_platform(platform) windows]} { return [file attributes $name -longname] } return $name } # AboutApplication -- # # Display program information. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc AboutApplication {} { NSModule::LoadIfNeeded NSAbout NSAbout::About return } # QuitNoSave -- # # Quit the game without saving. If the game is not asking for # a command, then call "game abort". Otherwise do a clean exit. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc QuitNoSave {} { global Angband # Check if game is waiting for a command if {[string compare [angband inkey_flags] INKEY_CMD]} { angband game abort return } # Ask the user to confirm quit with save set answer [tk_messageBox -icon question -type yesno \ -title "Quit ZAngband" -message "Do you really want to\ quit without saving?"] if {[string equal $answer no]} return # Quit without saving DoUnderlyingCommand ^Ey return } namespace eval NSInitStartup { } # NSInitStartup::InitStartupScreen -- # # Initialize the startup window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInitStartup::InitStartupScreen {} { global Angband wm title . "ZAngband" wm resizable . no no # Quit application if the user closes the window wm protocol . WM_DELETE_WINDOW "exit" # Load the "Tcl Powered Logo" image create photo Image_PwrdLogo175 \ -file [PathTk image pwrdLogo175.gif] # Program name if {[Platform unix]} { set font {Times 24 bold} } if {[Platform windows]} { set font {Times 18 bold} } label .title \ -text "ZAngband" -font $font # Program info set info "ZAngband\nhttp://www.zangband.org\n" label .info \ -text $info -justify left # Tcl Powered Logo label .logo \ -image Image_PwrdLogo175 # Startup progress listbox .prompt \ -width 15 -height 10 -borderwidth 0 -highlightthickness 0 .prompt insert end "Initializing arrays..." # Geometry pack .title \ -side top -expand no -anchor w pack .info \ -side top -expand no -anchor w pack .logo \ -side left -expand no pack [frame .filler -borderwidth 0 -height 10] -side top -anchor w pack .prompt \ -side top -expand no -pady 00 -padx 20 -anchor w # Position WindowPosition . 2 3 # When the listbox is unpacked, the window may shrink horizontally. # So set the desired window geometry to what it is now (with the list). wm geometry . [wm geometry .] update return } # angband_startup -- # # Called by Angband (and below) to display status messages # during program initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc angband_startup {what} { switch -- $what { init_misc { set prompt " misc" } init_script { set prompt " script" } init_wilderness { set prompt " wilderness" } init_quests { set prompt " quests" } init_plots { set prompt " plots" } init_other { set prompt " other" } init_alloc { set prompt " alloc" } default { set prompt $what } } .prompt insert end $prompt .prompt see end update return } # angband_initialized -- # # Called by Angband when program initialization is complete. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc angband_initialized {} { .prompt delete 0 end .prompt insert end "Sourcing scripts..." # Source library files angband_startup " object.tcl" Source library object.tcl angband_startup " buttonlabel.tcl" Source library buttonlabel.tcl angband_startup " canvist.tcl" Source library canvist.tcl angband_startup " menu.tcl" Source library menu.tcl angband_startup " module.tcl" Source library module.tcl angband_startup " status-text.tcl" Source library status-text.tcl angband_startup " progress.tcl" Source library progress1.tcl Source library progress2.tcl Source library progress-window.tcl angband_startup " tabs.tcl" Source library tabs.tcl angband_startup " texist.tcl" Source library texist.tcl angband_startup " toplevel.tcl" Source library toplevel.tcl angband_startup " window-manager.tcl" Source library window-manager.tcl NSModule::IndexLoad [PathTk library moduleIndex.tcl] destroy .filler destroy .prompt Source config.tcl NSConfig::InitModule return } # Because init-other.tcl isn't called before Angband starts calling # "angband_xxx", I must set a dummy proc's here. proc angband_display {args} { } # InitLoadWindow -- # # Creates and displays the Load Window, which is used to display # progress during savefile loading and subsequent program initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc InitLoadWindow {} { global Angband global AngbandPriv # If there are no front windows, the application is swapped # into the background. if {[winfo exists .load]} return set win .load toplevel $win -borderwidth 4 -relief ridge wm overrideredirect $win yes # Busy $win configure -cursor watch set width 350 set height 250 set height2 40 set fg Black set bg White set canvas $win.canvas canvas $canvas \ -borderwidth 0 -highlightthickness 0 \ -width $width -height $height -background $bg set progId [NSObject::New NSProgress2 $win.canvas 150 8] ### if {[Platform unix]} { set font {Times 24 bold} set font2 {Times 12} } if {[Platform windows]} { set font {Times 18 bold} set font2 {Times 10} } set anchor nw set x 11 set y 11 set lineSpace [font metrics $font -linespace] # Create a "shadow" for the text below $canvas create text $x $y -font $font -anchor $anchor -fill gray \ -text "Zangband" $canvas create text $x [expr {$y + $lineSpace}] -font $font2 \ -text "Copyright (c) 1997-2001 Tim Baker and ZAngband Dev Team" -anchor $anchor -fill gray # Draw text over the shadow created above incr x -1 incr y -1 $canvas create text $x $y -text "ZAngband" \ -font $font -anchor $anchor -fill $fg $canvas create text $x [expr {$y + $lineSpace}] -font $font2 \ -text "Copyright (c) 1997-2001 Tim Baker" -anchor $anchor -fill $fg \ -tags copyright # Scrolling text scan [$canvas bbox copyright] "%s %s %s %s" left top right bottom set height3 [expr {$height - ($height2 + 8) - ($bottom + 8)}] $canvas create rectangle 0 [expr {$bottom + 8}] $width \ [expr {$bottom + 8 + $height3}] -fill gray90 -outline {} # Rectangle at bottom $canvas create rectangle 0 [expr {$height - $height2}] $width $height \ -fill $fg $canvas create text [expr {$width / 2}] [expr {$height - $height2 + 4}] \ -text "" -fill $bg -anchor n -tags message ### set font [Global font,sys,normal] set rowHeight [font metrics $font -linespace] set rowCount [expr {($height3 - 6) / $rowHeight}] set AngbandPriv(load,rowCount) $rowCount set y [expr {$height - $height2 - 8 - ($height3 - ($rowHeight * $rowCount)) / 2}] for {set row 1} {$row <= $rowCount} {incr row} { $canvas create text [expr {$width / 2}] \ [expr {$y - $row * $rowHeight}] \ -fill Black -font $font -anchor n -tags row$row } pack $canvas -padx 1 -pady 1 place [NSProgress2::Info $progId frame] -x [expr {$width / 2}] \ -y [expr {$height - 8}] -anchor s ### # Position the window WindowPosition $win 2 3 # This inocuous call insures an *active* front window exists focus $win # Hide the startup screen wm withdraw . # Cleanup the startup screen foreach window [winfo children .] { if {[string compare [winfo class $window] Toplevel]} { destroy $window } } image delete Image_PwrdLogo175 foreach tag [bind .] { bind . $tag {} } set AngbandPriv(load,win) $win set AngbandPriv(load,prog) $progId set AngbandPriv(load,message) {} return } # LoadNote -- # # Inserts the given message to the head of the message queue, then # displays all the messages following the message in the Load Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc LoadNote {message} { global AngbandPriv set win .load set canvas $win.canvas set AngbandPriv(load,message) \ [linsert $AngbandPriv(load,message) 0 $message] set AngbandPriv(load,message) \ [lrange $AngbandPriv(load,message) 0 $AngbandPriv(load,rowCount)] set row 1 foreach message [lrange $AngbandPriv(load,message) 1 end] { $canvas itemconfigure row$row -text $message incr row } return } # angband_load -- # # Called by Angband to display info during savefile loading. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc angband_load {action {message ""}} { global AngbandPriv switch -- $action { kill { set win $AngbandPriv(load,win) destroy $win } note { set canvas $AngbandPriv(load,win).canvas $canvas itemconfigure message -text $message LoadNote $message } progress { NSProgress2::SetDoneRatio $AngbandPriv(load,prog) $message } } update return } # NSInitStartup::InitStartup -- # # The main initialization command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInitStartup::InitStartup {} { global auto_path global Angband global DEBUG # The tk directory set Angband(dirTk) [angband game tkdir] set Angband(dirTk) [LongName $Angband(dirTk)] # The msgs directory (message catalog) set Angband(dirTk,msgs) [list [file join $Angband(dirTk) msgs]] # Remember the platform switch -- $::tcl_platform(platform) { macintosh - unix - windows { set Angband(platform) $::tcl_platform(platform) } default { error "unknown platform \"$::tcl_platform(platform)\"" } } # This call makes sure Tcl reads in info about all available packages. # Without this call, the "package names" command returns an empty list. # Also, "package require Tk" doesn't scan external packages. catch {package require no-such-package} if {$Angband(platform) == "windows"} { # Set the default window icon wm iconbitmap . -default [file join $Angband(dirTk) image angbandtk.ico] } # Development debug support set DEBUG 1 Source errorInfo.tcl proc ::ASSERT {condition message} { if {![uplevel expr $condition]} { error $message } return } # Error handling Source bgerror.tcl # Global copyright blurb set Angband(copy) "Based on ZAngband (c) the ZAngband Dev Team" # Hack -- Require WindowPosition() command Source library utils.tcl # Various game-related commands Source misc.tcl # Create a universal "empty" image image create photo Image_Empty # Poor-Man's Font Management set screenWidth [winfo screenwidth .] if {[Platform windows]} { set fontSys "{MS Sans Serif}" set fontFixed "Courier" if {$screenWidth > 800} { Global font,sys,small "$fontSys 8" Global font,sys,normal "$fontSys 8" Global font,sys,large "$fontSys 10" Global font,fixed,small "$fontFixed 9" Global font,fixed,normal "$fontFixed 10" } elseif {$screenWidth > 640} { Global font,sys,small "$fontSys 8" Global font,sys,normal "$fontSys 8" Global font,sys,large "$fontSys 8" Global font,fixed,small "$fontFixed 8" Global font,fixed,normal "$fontFixed 9" } else { Global font,sys,small "$fontSys 8" Global font,sys,normal "$fontSys 8" Global font,sys,large "$fontSys 8" Global font,fixed,small "$fontFixed 8" Global font,fixed,normal "$fontFixed 8" } # Platform win } if {[Platform unix]} { Global font,sys,small {Helvetica 10} Global font,sys,normal {Helvetica 12} Global font,sys,large {Helvetica 12} Global font,fixed,small {Courier 10} Global font,fixed,normal {Courier 12} } # The MS Windows version of Tk supports platform-specific color names # all beginning with "System". Each of these colors is set here for # each platform. # Get these from library/utils.tcl Global SystemButtonFace $::SystemButtonFace Global SystemButtonHighlight $::SystemButtonHighlight Global SystemButtonShadow $::SystemButtonShadow Global SystemHighlight $::SystemHighlight Global SystemHighlightText $::SystemHighlightText Global System3dLight $::System3dLight Global System3dDarkShadow $::System3dDarkShadow Global SystemInfoBackground $::SystemInfoBackground if {[Platform unix]} { option add *Button.default normal widgetDefault option add *Button.font {Helvetica 12} widgetDefault option add *Checkbutton.font {Helvetica 12} widgetDefault option add *Dialog.msg.font {Helvetica 12} option add *Entry.background White widgetDefault option add *Label.font {Helvetica 12} widgetDefault option add *Listbox.font {Helvetica 12} widgetDefault option add *Menu.font {Helvetica 12} widgetDefault option add *Message.font {Helvetica 12} widgetDefault option add *Radiobutton.font {Helvetica 12} widgetDefault option add *Scale.font {Helvetica 12} widgetDefault option add *selectForeground [Global SystemHighlightText] 100 option add *selectBackground [Global SystemHighlight] } # Get term colours Global term_attr {"TERM_DARK" \ "TERM_WHITE" \ "TERM_SLATE" \ "TERM_ORANGE" \ "TERM_RED" \ "TERM_GREEN" \ "TERM_BLUE" \ "TERM_UMBER" \ "TERM_L_DARK" \ "TERM_L_WHITE" \ "TERM_VIOLET" \ "TERM_YELLOW" \ "TERM_L_RED" \ "TERM_L_GREEN" \ "TERM_L_BLUE" \ "TERM_L_UMBER"} # If a new character is created, this is set to 1 Global isNewGame 0 # Value Manager (needed for Birth Options Window) Source value-manager.tcl NSValueManager::InitModule InitStartupScreen return } # Begin NSInitStartup::InitStartup zangband/lib/script/tk/inventory.tcl0000644000000000000000000007243310250356274016606 0ustar rootroot# File: inventory.tcl # Purpose: the Inventory Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSInventory { variable Priv # namespace eval NSInventory } # NSInventory::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::InitModule {} { InitImageIfNeeded Image_ButtonOptions button-options.gif InitImageIfNeeded Image_ButtonHelp button-help.gif NSModule::LoadIfNeeded NSStatusBar NSModule::LoadIfNeeded NSToolbar # Create the Inventory Window NSObject::New NSInventory return } # NSInventory::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::CloseModule {} { catch { destroy [Window inventory] } return } # NSInventory::NSInventory -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::NSInventory {oop} { Info $oop alwaysOnTop [Value inventory,alwaysOnTop] Info $oop browsing 0 Info $oop choose,show 0 Info $oop didChoose 0 Info $oop both 0 Info $oop current -1 Info $oop toolbar,match {} Info $oop skipHide 0 InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow inventory $win \ "GetDefaultGeometry $win main2 main" "" \ "NSInventory::DisplayCmd $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSInventory $oop $win bind $win { InventoryObj Close break } # # Global list of application windows # Global inventory,oop $oop Window inventory $win return } # NSInventory::~NSInventory -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::~NSInventory {oop} { NSValueManager::RemoveClient font,inventory [Info $oop font,clientId] NSValueManager::RemoveClient listBG [Info $oop listBG,clientId] return } # NSInventory::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::Info {oop info args} { global NSInventory # Verify the object NSObject::CheckObject NSInventory $oop # Set info if {[llength $args]} { switch -- $info { default { set NSInventory($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSInventory($oop,$info) } } } return } # NSInventory::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::InitWindow {oop} { set win .inventory$oop toplevel $win wm title $win Inventory if {[Info $oop alwaysOnTop]} { wm transient $win [Window main] } # Start out withdrawn (hidden) wm withdraw $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSInventory::Close $oop" Info $oop win $win InitMenus $oop # # Toolbar # set toolId [NSObject::New NSToolbar 20 $win] NSToolbar::AddTool $toolId -image Image_ButtonOptions \ -showlabel no -command "DoCommandIfAllowed =" -hasmenu yes \ -menucommand "NSInventory::Win98MenuCmd_Options $oop" NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -showlabel no -command "DoCommandIfAllowed ?" set menuId [NSObject::New NSMenu *$win \ -tearoff 0 -identifier MENU_TOOLBAR] NSMenu::Info $menuId menuSelectCmd "NSInventory::MenuSelect $oop" set menu [NSMenu::Info $menuId menu] if {[Platform unix]} { $menu configure -cursor arrow } Info $oop toolbar,menu $menu # # List # set cw [font measure [Value font,inventory] "W"] set width [expr {$cw * 81}] frame $win.frame \ -borderwidth 1 -relief sunken set canvistId [NSObject::New NSCanvist $win.frame 40 $width 300 \ "NSInventory::NewItemCmd $oop" "NSInventory::HighlightItemCmd $oop"] set canvas [NSCanvist::Info $canvistId canvas] $canvas configure -background [Value listBG] $canvas configure -yscrollcommand "$win.frame.scroll set" scrollbar $win.frame.scroll \ -command "$canvas yview" -orient vert # When the window resizes, reposition the canvas items bind $canvas \ "NSInventory::PositionItems $oop" Info $oop canvistId $canvistId Info $oop canvas $canvas pack $win.frame.scroll -side right -fill y pack $canvas -side left -expand yes -fill both # Double-click to select item NSCanvist::Info $canvistId invokeCmd \ "NSInventory::Invoke $oop" # Update ourself when the font,inventory value changes Info $oop font,clientId \ [NSValueManager::AddClient font,inventory \ "NSInventory::ValueChanged_font_inventory $oop"] # This call updates the list background color whenever the # global list background color changes Info $oop listBG,clientId \ [NSValueManager::AddClient listBG \ "NSInventory::ValueChanged_listBG $oop"] # # Statusbar # statusbar $win.statusBar -sizes {18 14 18 15 12} -weights {0 0 0 1 0} \ -tags {t1 t2 t3 t4 t5} # # Geometry # grid rowconfig $win 0 -weight 0 -minsize 0 grid rowconfig $win 1 -weight 1 -minsize 0 grid rowconfig $win 2 -weight 0 -minsize 0 # grid rowconfig $win 3 -weight 0 -minsize 0 grid columnconfig $win 0 -weight 1 -minsize 0 pack forget [NSToolbar::Info $toolId frame] grid [NSToolbar::Info $toolId frame] -in $win \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew # grid $win.divider2 -in $win \ # -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.frame -in $win \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar -in $win \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew # # Context Menu # set m $win.context menu $m -tearoff 0 bind $canvas "NSInventory::ContextMenu $oop $m %X %Y" # # Feed Term when keys pressed # Term_KeyPress_Bind $win Term_KeyPress_Bind $canvas # # Synch the scrollbars when window is shown. # NSUtils::SynchScrollBar $canvas $win.frame.scroll 1 bind $win " if {\[string equal %W $win]} { focus $canvas } " return } # NSInventory::InitMenus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::InitMenus {oop} { } # NSInventory::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::MenuSelect {oop menuId index ident} { variable MenuString variable Priv switch -- $ident { {} { set desc {} } MENU_TOOLBAR { set menu [NSMenu::Info $menuId menu] switch -- [Info $oop toolbar,mode] { option { set desc [lindex [Info $oop toolbar,desc] $index]. } } } default { if {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop win].statusBar cover set $desc if {![string length $desc]} { if {0 && $menuId == [Info $oop mbarId]} { [Info $oop win].statusBar cover hide } } return } # NSInventory::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::DisplayCmd {oop message first args} { switch -- $message { preDisplay { eval SetList $oop $args if {([llength $args] == 3) && [lindex $args 2]} { Info $oop browsing 1 } } postDisplay { } reDisplay { if {[Info $oop browsing]} { if {![Info $oop alwaysOnTop]} { if {([llength $args] == 3) && [lindex $args 2]} { set focus [focus] if {[string length $focus] && ![string match [Info $oop win]* $focus]} { Info $oop oldFocus $focus Info $oop skipHide 1 } } } } if {[Info $oop browsing]} { if {[llength $args] == 3} { set both [lindex $args 2] } else { set both 0 } if {[string compare [lindex $args 0] [Info $oop invOrEquip]] || ($both != [Info $oop both])} { eval SetList $oop $args } } else { eval SetList $oop $args } if {![Info $oop alwaysOnTop]} { WindowBringToFront [Info $oop win] } } postWithdraw { Info $oop browsing 0 } } return } # NSInventory::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::Close {oop} { if {[string equal [angband inkey_flags] INKEY_ITEM]} { angband keypress \033 } elseif {[Info $oop skipHide]} { set oldFocus [Info $oop oldFocus] if {[string length $oldFocus]} { catch {focus $oldFocus} } Info $oop skipHide 0 } elseif {[Info $oop browsing]} { NSWindowManager::Undisplay inventory } return } # NSInventory::Win98MenuCmd_Options -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::Win98MenuCmd_Options {oop button} { set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] $menu delete 0 end set keywordList {} set descList {} $menu add separator lappend descList "" $menu add checkbutton -label "Always On Top" \ -command {InventoryObj AlwaysOnTop} \ -variable ::NSInventory($oop,alwaysOnTop) lappend keywordList "" lappend descList "Keep the window on top of the Main Window" $menu add command -label "Graphics Mode" -command "InventoryObj Swap" lappend keywordList "" lappend descList "Use the graphical window" $menu add separator $menu add command -label "Set Font" -command { NSModule::LoadIfNeeded NSFont NSWindowManager::Display font inventory } lappend descList "Set the font" Info $oop toolbar,mode option Info $oop toolbar,match $keywordList Info $oop toolbar,desc $descList [Info $oop win].statusBar cover show tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } after idle "$button hidemenu ; [Info $oop win].statusBar cover hide" return } # NSInventory::SettingChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::SettingChanged {oop keyword value} { # Ignore settings which don't affect the display if {[lsearch -exact [Info $oop toolbar,match] $keyword] == -1} return # Update the button update idletasks SetList $oop {} {} [Info $oop both] return } # NSInventory::GetItemCommand -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::GetItemCommand {oop where index _command _label} { upvar $_command command $_label label set command "" set label "" angband $where info $index attrib set charItem $attrib(char) if {[string equal $where equipment]} { if {$attrib(known) && $attrib(activate)} { set label "Activate" set charCmd A } else { set label "Remove" set charCmd t } set command "DoKeymapCmd {} $charCmd $charItem" return } switch -glob -- $attrib(tval) { *_BOOK { set label "Browse" set charCmd b # Hack -- Browse shows all the books set command "DoKeymapCmd {} $charCmd {}" return } TV_ARROW - TV_BOLT - TV_SHOT { set label "Fire" set charCmd f } TV_FLASK { set label "Refuel" set charCmd F } TV_FOOD { set label "Eat" set charCmd E } TV_POTION { set label "Drink" set charCmd q } TV_SCROLL { set label "Read" set charCmd r } TV_SPIKE { set label "Jam" set charCmd j } TV_STAFF { set label "Use" set charCmd u } TV_ROD { set label "Zap" set charCmd z } TV_WAND { set label "Aim" set charCmd a } TV_BOW - TV_DIGGING - TV_HAFTED - TV_POLEARM - TV_SWORD - TV_BOOTS - TV_GLOVES - TV_HELM - TV_CROWN - TV_SHIELD - TV_CLOAK - TV_SOFT_ARMOR - TV_HARD_ARMOR - TV_DRAG_ARMOR - TV_LITE - TV_AMULET - TV_RING { set label "Wield" set charCmd w } } if {[string length $label]} { set command "DoKeymapCmd {} $charCmd $charItem" } return } # NSInventory::Invoke -- # # When an inventory item is double-clicked, "angband keypress" the # char. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::Invoke {oop canvistId x y} { variable Priv set row [NSCanvist::PointToRow $canvistId $x $y] if {$row == -1} return set invOrEquip [Info $oop invOrEquip] set char [lindex $Priv(char) $row] set index [lindex $Priv(index) $row] if {[string equal [angband inkey_flags] INKEY_CMD]} { GetItemCommand $oop $invOrEquip $index command label if {[string length $command]} { eval $command } return } if {[string equal [angband inkey_flags] INKEY_ITEM]} { angband keypress $char } return } # NSInventory::ContextMenu -- # # When an inventory item is right-clicked, pop up a context # menu of actions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::ContextMenu {oop menu x y} { variable Priv set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] set where [Info $oop invOrEquip] set font [$menu cget -font] # Find the hit row set x1 [expr {$x - [winfo rootx $canvas]}] set y1 [expr {$y - [winfo rooty $canvas]}] set row [NSCanvist::PointToRow $canvistId $x1 $y1] # Clear the menu $menu delete 0 end set askCmd 0 set askItem 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set askCmd 1 } if {[string equal [angband inkey_flags] INKEY_ITEM]} { set askItem 1 } if {$askCmd} { set closeCmd {InventoryObj Close} set cancelCmd {} } elseif {$askItem} { if {[Info $oop browsing]} { set closeCmd { angband keypress \033 NSWindowManager::Undisplay inventory } } else { set closeCmd {angband keypress \033} } set cancelCmd {angband keypress \033} } else { set closeCmd {InventoryObj Close} set cancelCmd {} } # No row is hit if {$row == -1} { $menu add command -label "Close" -command $closeCmd $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y # Done return } # Get the inventory index set index [lindex $Priv(index) $row] # Get information about this item angband $where info $index attrib # Get the item char. We can't use attrib(char) below because # it might be an item in a floor stack, in which case attrib(char) # is always 'a'. set itemKey [lindex $Priv(char) $row] # Get the tval set itemTval $attrib(tval) # Require a real item (ie, in equipment) if {[string equal $attrib(tval) TV_NONE]} { $menu add command -label "Close" -command $closeCmd $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y # Done return } # We are waiting for an item if {$askItem} { # Append a command to select the item set command "angband keypress $itemKey" $menu add command -label "Select This Item" -command $command \ -font [BoldFont $font] $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y # Done return } # We are not waiting for a command if {!$askCmd} return # Hack -- Looking at a floor stack if {[string equal $where floor]} { # No commands are possible return } # Get the default command for this item GetItemCommand $oop $where $index command label if {[string length $command]} { $menu add command -label $label -command $command -font [BoldFont $font] } $menu add command -label "Drop" \ -command "DoKeymapCmd {} d $itemKey" $menu add command -label "Inspect" \ -command "DoKeymapCmd {} I $itemKey" $menu add command -label "Inscribe" \ -command "DoKeymapCmd {} braceleft $itemKey" # We are looking in the inventory if {[string equal $where inventory]} { $menu add separator set command "angband keypress 0$attrib(number)[angband keymap find k]$itemKey" # Skip the y/n prompt if the user is asked to confirm # the destruction of worthless items. $menu add command -label "*Destroy*" -command $command } $menu add separator $menu add command -label "Close" -command $closeCmd $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y return } # NSInventory::SetList -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::SetList {oop invOrEquip tval {both 0}} { variable Priv set win [Info $oop win] set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] if {[string length $invOrEquip]} { Info $oop invOrEquip $invOrEquip Info $oop tval $tval } else { set invOrEquip [Info $oop invOrEquip] set tval [Info $oop tval] } Info $oop both $both if {$both} { set items [angband $invOrEquip find] } else { set items [angband $invOrEquip find -tester yes] } # Clear the list of char's set Priv(char) {} # Clear the list of indexes set Priv(index) {} # Clear the list NSCanvist::DeleteAll $canvistId # Total the weight set weightDisplayed 0 # Calculate the row height. This is done every time here since # the font may change. The row height equals the linespace of # the font plus 8 pixels for the selection rectangle, or the # icon size plus 8, whichever is greater. set rowHgt [font metrics [Value font,inventory] -linespace] if {[icon size] > $rowHgt} { set rowHgt [icon size] } # Leave room for the selection rectangle on each line incr rowHgt 8 # Set the row height NSCanvist::Info $canvistId rowHgt $rowHgt $canvas configure -yscrollincrement $rowHgt # Options: Show weights, Show labels (in equipment) set show_labels [Setting show_labels] set Priv(width,char) 0 set Priv(width,label) 0 set Priv(width,desc) 0 set Priv(width,weight) 0 set itemList {} # Add each item foreach index $items { set attrib(label) "" angband $invOrEquip info $index attrib # Get the (optional) icon set icon $attrib(icon) # Hack -- Set index for floor item if {[string equal $invOrEquip floor]} { set attrib(char) [string index "abcdefghijklmnopqrstuvw" \ [lsearch -exact $items $index]] } # Use the entire description. It is "truncated" with an opaque item set desc $attrib(name) # Get the (optional) label set label $attrib(label) if {!$show_labels} {set label ""} # Get the (optional) weight set weight [expr {$attrib(weight) * $attrib(number)}] lappend itemList [list $attrib(char) \ $attrib(number) $desc $weight $label \ $attrib(tval) $attrib(sval) $icon] # Total the weight incr weightDisplayed [expr {$attrib(weight) * $attrib(number)}] # Remember the char lappend Priv(char) $attrib(char) # Remember the index lappend Priv(index) $index } # Append the descriptions NSCanvist::InsertMany $canvistId end $itemList # Arrange all the items PositionItems $oop # Display weight of inventory items, % of capacity, and weight limit set units lb set weightTotal [angband inventory total_weight] set weightLimit [angband inventory weight_limit] set displayed [format "%d.%d $units" [expr {$weightDisplayed / 10}] [expr {$weightDisplayed % 10}]] set total [format "%d.%d $units" [expr {$weightTotal / 10}] [expr {$weightTotal % 10}]] set capacity [expr {$weightLimit / 2 + $weightLimit / 10}] set capacity [format "%d.%d $units" [expr {$capacity / 10}] [expr {$capacity % 10}]] set limit [format "%d.%d $units" [expr {$weightLimit / 10}] [expr {$weightLimit % 10}]] $win.statusBar itemconfigure t1 -text "Displayed $displayed" $win.statusBar itemconfigure t2 -text "Total $total" $win.statusBar itemconfigure t3 -text "Threshold $capacity" $win.statusBar itemconfigure t4 -text "Limit $limit" set numItems [llength $items] if {$numItems == 1} { $win.statusBar itemconfigure t5 -text [format "%d item" $numItems] } else { $win.statusBar itemconfigure t5 -text [format "%d items" $numItems] } # Set window title set title [string totitle $invOrEquip] wm title $win $title return } # NSInventory::NewItemCmd -- # # Called by NSCanvist::InsertItem() to create a list row. # # Arguments: # arg1 about arg1 # # Results: # What happened. # The icon, char and weight are positioned exactly # The label, : and name are positioned after all items are gathered proc NSInventory::NewItemCmd {oop canvistId y char number text weight label tval sval icon} { variable Priv set canvas [NSCanvist::Info $canvistId canvas] set lineHeight [NSCanvist::Info $canvistId rowHgt] set font [Value font,inventory] set fh [font metrics $font -linespace] set diff [expr {int([expr {($lineHeight - $fh) / 2}])}] set offset [expr {[icon size] + 8}] if 0 { # Image if {[string length $icon]} { set iw [icon size] set ih [icon size] set wid [expr {[icon size] + 8}] set xdiff [expr {int([expr {($wid - $iw) / 2}])}] set ydiff [expr {int([expr {($lineHeight - $ih) / 2}])}] lappend itemIdList [$canvas create widget $xdiff [expr {$y + $ydiff}] \ -assign $icon] } } # Char lappend itemIdList [$canvas create text $offset [expr {$y + $diff}] \ -text $char) -anchor nw -font $font -fill White -tags text] # Label and : (in equipment) if {[string length $label]} { # Label lappend itemIdList [$canvas create text 0 [expr {$y + $diff}] \ -text $label -anchor nw -font $font -fill White -tags {text label}] # The : are all lined up lappend itemIdList [$canvas create text 0 [expr {$y + $diff}] \ -text : -anchor nw -font $font -fill White -tags {text semicolon}] } # Description set fill [default_tval_to_attr $tval] lappend itemIdList [$canvas create text 0 [expr {$y + $diff}] \ -text $text -anchor nw -font $font -fill $fill \ -tags "text enabled hilite fill:$fill description"] # This item "truncates" long descriptions set listBG [Value listBG] lappend itemIdList [$canvas create rectangle 0 $y \ 1 [expr {$y + $lineHeight}] -fill $listBG -outline $listBG \ -tags truncate] # Weight if {[string length $weight]} { set weight [format "%d.%d lb" [expr {$weight / 10}] [expr {$weight % 10}]] lappend itemIdList [$canvas create text 0 [expr {$y + $diff}] \ -text $weight -anchor ne -justify right -font $font -fill White \ -tags weight] } # Selection rectangle inside row lappend itemIdList [$canvas create rectangle 2 [expr {$y + 2}] \ 2 [expr {$y + $lineHeight - 2}] -fill "" -outline "" \ -tags {enabled selrect} -width 2.0] # Maximum width of char set width [font measure $font "$char) "] if {$width > $Priv(width,char)} { set Priv(width,char) $width } # Maximum width of label if {[string length $label]} { set width [font measure $font "${label}A"] if {$width > $Priv(width,label)} { set Priv(width,label) $width } } # Maximum width of description set width [font measure $font $text] if {$width > $Priv(width,desc)} { set Priv(width,desc) $width } # Maximum width of weight if {[string length $weight]} { set width [font measure $font "AB$weight"] if {$width > $Priv(width,weight)} { set Priv(width,weight) $width } } return $itemIdList } # NSInventory::PositionItems -- # # Arranges all the canvas items in the list. This is called after all # the items are added, and when the event indicates the # window has been resized. This is the routine that lets variable-width # fonts work. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::PositionItems {oop} { variable Priv set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] # Nothing to do if {![NSCanvist::Info $canvistId count]} return # Get the width of the canvas set canvasWidth [winfo width $canvas] set offset [expr {[icon size] + 8}] incr offset $Priv(width,char) # Position each label and semicolon if {$Priv(width,label)} { # Position each label set coords [$canvas coords label] $canvas move label [expr {$offset - [lindex $coords 0]}] 0 incr offset $Priv(width,label) # Position each semicolon set coords [$canvas coords semicolon] $canvas move semicolon [expr {$offset - [lindex $coords 0]}] 0 incr offset [font measure [Value font,inventory] ":A"] } # Position each description set coords [$canvas coords description] $canvas move description [expr {$offset - [lindex $coords 0]}] 0 # Truncate each description (by positioning each "truncate" item) set x0 [expr {($canvasWidth - 1) - $Priv(width,weight) - 4}] set x1 $canvasWidth foreach itemId [$canvas find withtag truncate] { scan [$canvas coords $itemId] "%s %s %s %s" c0 c1 c2 c3 $canvas coords $itemId $x0 $c1 $x1 $c3 } # Position each weight if {$Priv(width,weight)} { set offset [expr {($canvasWidth - 1) - 4}] set coords [$canvas coords weight] $canvas move weight [expr {$offset - [lindex $coords 0]}] 0 } # Position each selection rectangle set x1 [expr {($canvasWidth - 1) - 2}] foreach itemId [$canvas find withtag selrect] { scan [$canvas coords $itemId] "%s %s %s %s" c0 c1 c2 c3 $canvas coords $itemId $c0 $c1 $x1 $c3 } # Set the scrollregion scan [$canvas cget -scrollregion] "%s %s %s %s" x1 y1 x2 y2 $canvas configure -scrollregion "$x1 $y1 $canvasWidth $y2" return } # NSInventory::HighlightItemCmd -- # # Called by NSCanvist::Select() to highlight a row. # # Arguments: # oop OOP ID. See above. # canvistId OOP ID of NSCanvist object. # state 1 or 0 highlight state. # args List of canvas item ids # # Results: # What happened. proc NSInventory::HighlightItemCmd {oop canvistId state args} { set canvas [NSCanvist::Info $canvistId canvas] set itemIdList $args set idRect [FindItemByTag $canvas $itemIdList selrect] if {[NSUtils::HasFocus $canvas]} { set fill [Value listHilite] } else { set fill [Value listInactive] } if {$state} { $canvas itemconfigure $idRect -outline $fill } else { $canvas itemconfigure $idRect -outline "" } return } # NSInventory::ValueChanged_font_inventory -- # # Called when the font,inventory value changes. # Updates the Inventory Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::ValueChanged_font_inventory {oop} { if {![winfo ismapped [Info $oop win]]} return SetList $oop "" "" return } # NSInventory::ValueChanged_listBG -- # # Called when the listBG value changes. # Updates the Inventory Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::ValueChanged_listBG {oop} { set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] set color [Value listBG] $canvas configure -background $color $canvas itemconfigure truncate -fill $color -outline $color return } # NSInventory::Track -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::Track {oop what} { switch -- $what { equipment - inventory { if {[string equal [Info $oop invOrEquip] $what]} { SetList $oop "" "" } } } return } # NSInventory::ChooseItem -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::ChooseItem {oop show other} { Info $oop choose,show $show Info $oop choose,other $other if {$show} { Info $oop didChoose 0 } return } # NSInventory::TermInkey -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::TermInkey {oop} { set win [Info $oop win] if {[Info $oop choose,show]} { SetList $oop [Info $oop choose,other] "" if {![Info $oop alwaysOnTop]} { if {![NSUtils::ToplevelHasFocus $win]} { Info $oop oldFocus [focus] WindowBringToFront $win } } Info $oop didChoose 1 } elseif {![Info $oop didChoose]} { # Nothing } else { if {![Info $oop alwaysOnTop]} { set oldFocus [Info $oop oldFocus] if {[string length $oldFocus] && ![string match $win* $oldFocus]} { catch {focus $oldFocus} } update } SetList $oop "" "" 1 } return } # NSInventory::Swap -- # # Display the "new" Inventory Window # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::Swap {oop} { set browsing [Info $oop browsing] set where [Info $oop invOrEquip] set both [Info $oop both] NSWindowManager::Undisplay inventory NSModule::LoadIfNeeded NSInventory2 NSWindowManager::Display inventory2 $where "" $both if {[Info $oop choose,show]} { Inventory2Obj Info browsing $browsing Inventory2Obj Info choose,show 1 Inventory2Obj Info choose,other $where Inventory2Obj Info didChoose 1 if {![string match [Info $oop win]* [Info $oop oldFocus]]} { Inventory2Obj Info oldFocus [Info $oop oldFocus] } else { Inventory2Obj Info oldFocus [Inventory2Obj Info win] } } Value inventory,style new return } # NSInventory::AlwaysOnTop -- # # Toggle the "Always On Top" option. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory::AlwaysOnTop {oop} { set win [Info $oop win] set onTop [Info $oop alwaysOnTop] if {[Platform unix]} { wm state $win withdrawn } if {$onTop} { wm transient $win [Window main] } else { wm transient $win "" raise $win } if {[Platform unix]} { wm state $win normal } after idle focus $win Value inventory,alwaysOnTop $onTop # Synchronize the graphical inventory window if {[info exists ::Windows(inventory2)] && [winfo exists [Window inventory2]]} { if {$onTop} { wm transient [Window inventory2] [Window main] } else { wm transient [Window inventory2] "" } Inventory2Obj Info alwaysOnTop $onTop } return } proc InventoryObj {command args} { return [eval NSInventory::$command [Global inventory,oop] $args] } zangband/lib/script/tk/inventory2.tcl0000644000000000000000000013353710250356274016673 0ustar rootroot# File: inventory2.tcl # Purpose: the (new) Inventory Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSInventory2 { # namespace eval NSInventory2 } # NSInventory2::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::InitModule {} { InitImageIfNeeded Image_ButtonOptions button-options.gif InitImageIfNeeded Image_ButtonHelp button-help.gif InitImageIfNeeded Image_Binding dg_binding.gif InitImageIfNeeded Image_Equip dg_equip.gif NSModule::LoadIfNeeded NSStatusBar NSModule::LoadIfNeeded NSToolbar # Create the Inventory Window NSObject::New NSInventory2 return } # NSInventory2::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::CloseModule {} { catch { destroy [Window inventory2] } return } # NSInventory2::NSInventory2 -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::NSInventory2 {oop} { Info $oop alwaysOnTop [Value inventory,alwaysOnTop] Info $oop browsing 0 Info $oop busy 0 Info $oop choose,show 0 Info $oop didChoose 0 Info $oop dragging 0 Info $oop highlight,where "" Info $oop highlight,index "" Info $oop select,where "" Info $oop select,index "" Info $oop toolbar,match {} Info $oop request {} Info $oop request,id "" Info $oop skipHide 0 Info $oop equipment,weight 0 InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow inventory2 $win \ "GetDefaultGeometry $win reqwidth reqheight" "" \ "NSInventory2::DisplayCmd $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSInventory2 $oop $win bind $win { Inventory2Obj Close break } # Update ourself when the font,statusBar value changes Info $oop clientId,font,statusBar \ [NSValueManager::AddClient font,statusBar \ "NSInventory2::ValueChanged_font_statusBar $oop"] # # Global list of application windows # Global inventory2,oop $oop Window inventory2 $win return } # NSInventory2::~NSInventory2 -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::~NSInventory2 {oop} { NSValueManager::RemoveClient font,statusBar [Info $oop clientId,font,statusBar] return } # NSInventory2::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Info {oop info args} { global NSInventory2 # Verify the object NSObject::CheckObject NSInventory2 $oop # Set info if {[llength $args]} { switch -- $info { default { set NSInventory2($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSInventory2($oop,$info) } } } return } # NSInventory2::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::InitWindow {oop} { set win .inventory2_$oop toplevel $win wm title $win Inventory wm resizable $win no no if {[Info $oop alwaysOnTop]} { wm transient $win [Window main] } # Start out withdrawn (hidden) wm withdraw $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSInventory2::Close $oop" Info $oop win $win # # Toolbar # set toolId [NSObject::New NSToolbar 20 $win] NSToolbar::AddTool $toolId -image Image_ButtonOptions \ -showlabel no -command "DoCommandIfAllowed =" -hasmenu yes \ -menucommand "NSInventory2::Win98MenuCmd_Options $oop" NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -showlabel no -command "DoCommandIfAllowed ?" set menuId [NSObject::New NSMenu *$win \ -tearoff 0 -identifier MENU_TOOLBAR] NSMenu::Info $menuId menuSelectCmd "NSInventory2::MenuSelect $oop" set menu [NSMenu::Info $menuId menu] if {[Platform unix]} { $menu configure -cursor arrow } Info $oop toolbar,menu $menu # # Canvas # set canvas $win.canvas canvas $canvas \ -borderwidth 0 -highlightthickness 0 \ -background [format #%02x%02x%02x 0 0 153] $canvas create image 109 164 -image Image_Equip -anchor center -tags image if {[info tclversion] >= 8.3} { $canvas itemconfigure image -state disabled } bind $canvas \ "NSInventory2::Button1 $oop %x %y" bind $canvas \ "NSInventory2::Motion1 $oop %x %y" bind $canvas \ "NSInventory2::Release1 $oop %x %y" Info $oop canvas $canvas # # Equipment # if {[Platform unix]} { set font {Helvetica 12 bold} } if {[Platform windows]} { set font {Helvetica 10 bold} } set fontHeight [font metrics $font -linespace] set heightHeader [expr {1 + $fontHeight + 1}] set heightBox [expr {[icon size] + 8}] set widthBox [expr {[icon size] + 8}] set widthBoxMax 40 set widthEquip 224 set heightEquip 297 set padRing 2 MakeBorder $oop $canvas 0 0 $widthEquip $heightEquip MakeHeader $oop $canvas 0 0 $widthEquip "Equipment" equipment,title set slots [list \ INVEN_WIELD 111 158 {119 0 0} {204 0 0} \ INVEN_BOW 72 23 {119 0 0} {204 0 0} \ INVEN_LEFT 160 171 {51 102 102} {102 204 204} \ INVEN_RIGHT 164 7 {51 102 102} {102 204 204} \ INVEN_NECK 51 148 {51 102 102} {102 204 204} \ INVEN_LITE 26 31 {102 51 0} {255 153 0} \ INVEN_BODY 95 89 {0 102 0} {51 204 0} \ INVEN_OUTER 208 138 {0 102 0} {51 204 0} \ INVEN_ARM 118 15 {0 102 0} {51 204 0} \ INVEN_HEAD 39 89 {0 102 0} {51 204 0} \ INVEN_HANDS 208 40 {0 102 0} {51 204 0} \ INVEN_FEET 248 89 {0 102 0} {51 204 0} \ ] set lines [list \ INVEN_WIELD {{147 167}} \ INVEN_BOW {{82 90}} \ INVEN_LEFT {{150 182}} \ INVEN_RIGHT {{67 182}} \ INVEN_NECK {{108 90}} \ INVEN_ARM {{73 147}} \ INVEN_HANDS {{70 175} {147 175}} \ ] array set data $lines foreach {slot y x rgb1 rgb2} $slots { set cy [expr {$y + 20}] set cx [expr {$x + 20}] if {[info exists data($slot)]} { set fill [format "#%02x%02x%02x" 153 153 153] foreach loc $data($slot) { scan $loc "%d %d" x2 y2 $canvas create line $cx $cy $x2 $y2 -fill $fill \ -tags "line line,$slot" } } set rgb1 [eval format "#%02x%02x%02x" $rgb1] set rgb2 [eval format "#%02x%02x%02x" $rgb2] MakeBox $oop equipment $slot $rgb1 $rgb2 Info $oop equipment,y,$slot $cy Info $oop equipment,x,$slot $cx MoveBox $oop $cx $cy equipment $slot Info $oop visible,$slot 1 Info $oop assign,$slot "icon none 0" lappend slot_names $slot } Info $oop slot_names $slot_names Info $oop width,equip $widthEquip # # Inventory # set widthInven [expr {$padRing + 3 + ($widthBoxMax + 6) * 5 + 6 + 3}] set heightInven $heightEquip set x [expr {$widthEquip + 1}] MakeBorder $oop $canvas $x 0 $widthInven $heightInven MakeHeader $oop $canvas $x 0 $widthInven "Inventory" inventory,title set left [expr {$x + $padRing + 3 + 6}] set top [expr {3 + ($heightHeader - 1) + 6}] set columns [expr {($widthInven - $padRing - 3 - 6 - 3) / ($widthBox + 6)}] set diff [expr {($widthInven - $padRing - 3 - 6 - 3) - $columns * ($widthBox + 6)}] # 23 items set col 0 set row 0 for {set i 0} {$i <= 23} {incr i} { MakeBox $oop inventory $i set x [expr {$diff / 2 + $left + $col * ($widthBox + 6) + $widthBox / 2}] set y [expr {$top + $row * ($heightBox + 6) + $heightBox / 2}] Info $oop inventory,x,$i $x Info $oop inventory,y,$i $y if {[incr col] == $columns} { set col 0 incr row } if {[info tclversion] >= 8.3} { MoveBox $oop $x $y inventory $i } } # # Statusbar # set y $heightEquip set topStatus $y Info $oop top,status $y # Equipment statusbar MakeStatus $oop $canvas 0 $topStatus $widthEquip equipment # Inventory statusbar set x [expr {$widthEquip + 1}] MakeStatus $oop $canvas $x $topStatus $widthInven inventory set font [Value font,statusBar] set fontHeight [font metrics $font -linespace] set heightStatus [expr {3 + $fontHeight + 3}] set widthTotal [expr {$widthEquip + 1 + $widthInven}] set heightTotal [expr {$heightEquip + $heightStatus}] $canvas configure -width $widthTotal -height $heightTotal # Divider filler set x $widthEquip $canvas create line $x 0 $x $heightEquip -fill #282828 # Hack -- ring bindings down the middle set y 34 for {set i 0} {$i < 8} {incr i} { $canvas create image $x $y -image Image_Binding incr y 34 } # This box is used for drag & drop MakeBox $oop drag 0 HideBox $oop drag 0 # Help-text statusbar (normally hidden) MakeStatus $oop $canvas 0 $topStatus $widthTotal statusBar $canvas itemconfigure statusBar,status -state hidden # Alternate balloon impl $canvas create rectangle 0 0 10 10 -fill White -state hidden \ -tags {balloon balloon,rect} $canvas create text 0 0 -anchor n -state hidden -tags {balloon balloon,text} # # Geometry # grid rowconfig $win 0 -weight 0 grid rowconfig $win 1 -weight 1 grid columnconfig $win 0 -weight 1 pack forget [NSToolbar::Info $toolId frame] grid [NSToolbar::Info $toolId frame] -in $win \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.canvas -in $win \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news # # Context Menu # set m $win.context menu $m -tearoff 0 bind $canvas \ "NSInventory2::ContextMenu $oop $m %X %Y" # # Feed Term when keys pressed # Term_KeyPress_Bind $win Term_KeyPress_Bind $canvas return } # NSInventory2::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::MenuSelect {oop menuId index ident} { variable MenuString switch -- $ident { {} { set desc {} } MENU_TOOLBAR { set menu [NSMenu::Info $menuId menu] switch -- [Info $oop toolbar,mode] { option { set desc [lindex [Info $oop toolbar,desc] $index]. } } } default { if {[angband store ishome]} { set sym $ident,home } else { set sym $ident,store } if {[info exists MenuString($sym)]} { set desc $MenuString($sym) } elseif {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop canvas] itemconfigure statusBar,status,left -text $desc return } # NSInventory2::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::DisplayCmd {oop message first args} { switch -- $message { preDisplay { eval SetList $oop $args if {([llength $args] == 3) && [lindex $args 2]} { Info $oop browsing 1 } } postDisplay { if {0 && ![Value warning,inventory,window]} { tk_messageBox -parent [Info $oop win] -title "Game Change" \ -message "This is the new graphical Inventory Window.\ If you want the list instead, choose \"List Mode\" from\ the options button in the toolbar." Value warning,inventory,window 1 } } reDisplay { if {[Info $oop browsing]} { if {![Info $oop alwaysOnTop]} { if {([llength $args] == 3) && [lindex $args 2]} { set focus [focus] if {[string length $focus] && ![string match [Info $oop win]* $focus]} { Info $oop oldFocus $focus Info $oop skipHide 1 } } } } else { eval SetList $oop $args } if {![Info $oop alwaysOnTop]} { WindowBringToFront [Info $oop win] } } postWithdraw { # Clear the list set canvas [Info $oop canvas] $canvas itemconfigure icon -assign {icon none 0} foreach slot [Info $oop slot_names] { Info $oop assign,$slot "icon none 0" } Info $oop browsing 0 # In case of errors, prevent paralysis Info $oop busy 0 } } return } # NSInventory2::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Close {oop} { if {[string equal [angband inkey_flags] INKEY_ITEM]} { angband keypress \033 } elseif {[Info $oop skipHide]} { set oldFocus [Info $oop oldFocus] if {[string length $oldFocus]} { catch {focus $oldFocus} } Info $oop skipHide 0 } elseif {[Info $oop browsing]} { NSWindowManager::Undisplay inventory2 } return } # NSInventory2::Win98MenuCmd_Options -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Win98MenuCmd_Options {oop button} { set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] $menu delete 0 end set keywordList {} set descList {} $menu add separator lappend descList "" $menu add checkbutton -label "Always On Top" \ -command {Inventory2Obj AlwaysOnTop} \ -variable NSInventory2($oop,alwaysOnTop) lappend keywordList "" lappend descList "Keep the window on top of the Main Window" $menu add command -label "List Mode" -command {Inventory2Obj Swap} lappend keywordList "" lappend descList "Use the list window" Info $oop toolbar,mode option Info $oop toolbar,match $keywordList Info $oop toolbar,desc $descList [Info $oop canvas] itemconfigure statusBar,status -state "" tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } after idle "$button hidemenu ; [Info $oop canvas] itemconfigure statusBar,status -state hidden" return } # NSInventory2::SettingChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::SettingChanged {oop keyword value} { # Ignore settings which don't affect the display if {[lsearch -exact [Info $oop toolbar,match] $keyword] == -1} return # Update the button update idletasks # SetList $oop {} {} [Info $oop both] SetList_Equipment $oop SetList_Inventory $oop return } # NSInventory2::GetItemCommand -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::GetItemCommand {oop where index _command _label} { upvar $_command command $_label label set command "" set label "" angband $where info $index attrib set charItem $attrib(char) if {[string equal $where equipment]} { if {$attrib(known) && $attrib(activate)} { set label "Activate" set charCmd A } else { set label "Remove" set charCmd t } set command "DoKeymapCmd {} $charCmd $charItem" return } switch -glob -- $attrib(tval) { *_BOOK { set label "Browse" set charCmd b # Hack -- Browse shows all the books set command "DoKeymapCmd {} $charCmd {}" return } TV_ARROW - TV_BOLT - TV_SHOT { set label "Fire" set charCmd f } TV_FLASK { set label "Refuel" set charCmd F } TV_FOOD { set label "Eat" set charCmd E } TV_POTION { set label "Drink" set charCmd q } TV_SCROLL { set label "Read" set charCmd r } TV_SPIKE { set label "Jam" set charCmd j } TV_STAFF { set label "Use" set charCmd u } TV_ROD { set label "Zap" set charCmd z } TV_WAND { set label "Aim" set charCmd a } TV_BOW - TV_DIGGING - TV_HAFTED - TV_POLEARM - TV_SWORD - TV_BOOTS - TV_GLOVES - TV_HELM - TV_CROWN - TV_SHIELD - TV_CLOAK - TV_SOFT_ARMOR - TV_HARD_ARMOR - TV_DRAG_ARMOR - TV_LITE - TV_AMULET - TV_RING { set label "Wield" set charCmd w } } if {[string length $label]} { set command "DoKeymapCmd {} $charCmd $charItem" } return } # NSInventory2::Invoke -- # # Called when an item is double-clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Invoke {oop where index} { set char [Info $oop char,$where,$index] set whereItem [Info $oop where,$where,$index] set item [Info $oop item,$where,$index] angband $whereItem info $item attrib # Ignore non-objects in equipment if {[string equal $attrib(tval) TV_NONE]} return if {[string equal [angband inkey_flags] INKEY_CMD]} { GetItemCommand $oop $where $item command label if 0 { # If this item can be sold, set the default action to "Sell" if {[angband store shopping] && [string equal $where inventory]} { set match [angband inventory find -store_will_buy yes] if {[lsearch -exact $match $index] != -1} { set cmdChar s } } } if {[string length $command]} { eval $command } } if {[string equal [angband inkey_flags] INKEY_ITEM]} { angband keypress $char } return } # NSInventory2::Select -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Select {oop where index} { if {[string equal $where [Info $oop select,where]] && [string equal $index [Info $oop select,index]]} { # SelectionChanged $oop "" "" return } SelectionChanged $oop $where $index return } # NSInventory2::SelectionChanged -- # # Called when the list selection changes. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::SelectionChanged {oop where index} { if {[string length [Info $oop select,where]]} { Highlight $oop 0 [Info $oop select,where] [Info $oop select,index] } # Nothing was selected if {![string length $where]} { Info $oop select,where "" Info $oop select,index "" return } # Remember the selected item Info $oop select,where $where Info $oop select,index $index Highlight $oop 1 $where $index BalloonHide $oop return } # NSInventory2::Highlight -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Highlight {oop state where index} { if {[Info $oop busy]} return Info $oop busy 1 set canvas [Info $oop canvas] if {$state} { # Delayed action: box is hidden if {[$canvas itemcget $where,$index -state] == "hidden"} { Info $oop busy 0 return } # Remember the highlighted box Info $oop highlight,where $where Info $oop highlight,index $index $canvas itemconfigure sel,$where,$index \ -outline [Info $oop color,$where,$index] set whereItem [Info $oop where,$where,$index] set item [Info $oop item,$where,$index] # Get info about the item angband $whereItem info $item attrib # Ignore non-objects in equipment if {[string compare $attrib(tval) TV_NONE]} { NSRecall::RecallObject $whereItem $item set weight [expr {$attrib(weight) * $attrib(number)}] set weight [fmt_wgt $weight] set total [fmt_wgt [Info $oop $where,weight] 1] $canvas itemconfigure $where,status,right -text "$weight/$total" scan [$canvas bbox sel,$where,$index] "%s %s %s %s" left top right bottom set x [expr {[winfo rootx $canvas] + $left + 3 + [icon size] / 2}] set y [expr {[winfo rooty $canvas] + ($bottom + 1) + 2}] set char [Info $oop char,$where,$index] set text "$char\) $attrib(name)" BalloonShow $oop $text $x $y } } else { Info $oop highlight,where "" Info $oop highlight,index "" $canvas itemconfigure sel,$where,$index \ -outline [Info $oop color2,$where,$index] $canvas itemconfigure $where,status,right \ -text [fmt_wgt [Info $oop $where,weight] 1] BalloonHide $oop } Info $oop busy 0 return } # NSInventory2::ContextMenu -- # # When an item is right-clicked, pop up a context menu of actions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::ContextMenu {oop menu x y} { set canvas [Info $oop canvas] set font [$menu cget -font] # Clear the menu $menu delete 0 end set itemId [$canvas find withtag current] if {[llength $itemId]} { set tags [$canvas gettags $itemId] set index [lsearch $tags icon,*,*] if {$index != -1} { scan [lindex $tags $index] {icon,%[^,],%s} where index scan [$canvas bbox sel,$where,$index] "%s %s %s %s" left top right bottom set x [expr {[winfo rootx $canvas] + ($right + 1) + 2}] set y [expr {[winfo rooty $canvas] + $top}] BalloonHide $oop } else { set where "" set index "" } } else { set where "" set index "" } set askCmd 0 set askItem 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set askCmd 1 } if {[string equal [angband inkey_flags] INKEY_ITEM]} { set askItem 1 } if {$askCmd} { set closeCmd {Inventory2Obj Close} set cancelCmd {} } elseif {$askItem} { if {[Info $oop browsing]} { set closeCmd { angband keypress \033 NSWindowManager::Undisplay inventory2 } } else { set closeCmd {angband keypress \033} } set cancelCmd {angband keypress \033} } else { set closeCmd {Inventory2Obj Close} set cancelCmd {} } # No row is hit if {![string length $where]} { $menu add command -label "Close" -command $closeCmd $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y # Done return } # Get information about this item angband $where info $index attrib # Get the item char. We can't use attrib(char) below because # it might be an item in a floor stack, in which case attrib(char) # is always 'a'. set itemKey [Info $oop char,$where,$index] # Get the tval set itemTval $attrib(tval) # Require a real item (ie, in equipment) if {[string equal $attrib(tval) TV_NONE]} { $menu add command -label "Close" -command $closeCmd $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y # Done return } # We are waiting for an item if {$askItem} { # Append a command to select the item set command "angband keypress $itemKey" $menu add command -label "Select This Item" -command $command \ -font [BoldFont $font] $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y # Done return } # We are not waiting for a command if {!$askCmd} return # Hack -- Looking at a floor stack if {[string equal $where floor]} { # No commands are possible return } # Originally, we type 'e' to display equipment which sets command_see # to TRUE. Then type 'd' to drop selects from equipment. But now we # don't set command_see because the window can always be open. So the # toggle char must always be entered when acting on an equipment item. set command_see 0 set toggleChar "" if {$command_see} { if {[string compare [Info $oop where] $where]} { switch -- $where { equipment - inventory { set toggleChar / } } } } else { if {[string equal $where equipment]} { if {[angband inventory count]} { set toggleChar / } } } # Get the default command for this item GetItemCommand $oop $where $index command label if 0 { # If this item can be sold, set the default action to "Sell" if {[angband store shopping] && [string equal $where inventory]} { set match [angband inventory find -store_will_buy yes] if {[lsearch -exact $match $index] != -1} { set cmdChar s if {[angband store ishome]} { set usageString "Drop" } else { set usageString "Sell" } } } } if {[string length $command]} { $menu add command -label $label -command $command -font [BoldFont $font] } if {![angband store shopping]} { $menu add command -label "Drop" \ -command "DoKeymapCmd {} d $toggleChar$itemKey" } $menu add command -label "Inspect" \ -command "DoKeymapCmd {} I $toggleChar$itemKey" $menu add command -label "Inscribe" \ -command "DoKeymapCmd {} braceleft $toggleChar$itemKey" # We are looking in the inventory if {[string equal $where inventory]} { ### Don't use toggleChar: destroy only works on inventory $menu add separator set command "angband keypress 0$attrib(number)[angband keymap find k]$itemKey" # Skip the y/n prompt if the user is asked to confirm # the destruction of worthless items. $menu add command -label "*Destroy*" -command $command } $menu add separator $menu add command -label "Close" -command $closeCmd $menu add separator $menu add command -label "Cancel" -command $cancelCmd # Pop up the menu tk_popup $menu $x $y return } # NSInventory2::SetList -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::SetList {oop where tval {both 0}} { set win [Info $oop win] set canvas [Info $oop canvas] if {[string length $where]} { Info $oop where $where Info $oop tval $tval } else { set where [Info $oop where] set tval [Info $oop tval] } Info $oop both $both SetList_Equipment $oop SetList_Inventory $oop # Set window title if {$both} { wm title $win "Items" } else { wm title $win [string totitle $where] } return } # NSInventory2::SetList_Equipment -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::SetList_Equipment {oop} { set canvas [Info $oop canvas] # Clear the selection SelectionChanged $oop "" "" # Default to each slot not being displayed foreach slot [Info $oop slot_names] { set display($slot) 0 } set doIt 1 switch -- [Info $oop where] { floor { set doIt 0 } inventory { if {![Info $oop both]} { set doIt 0 } } } if {$doIt} { if {[Info $oop both]} { set itemList [angband equipment find] } else { set itemList [angband equipment find -tester yes] } set weight 0 foreach item $itemList { # Get info about the item angband equipment info $item attrib set slot [lindex [Info $oop slot_names] $item] set display($slot) 1 # Get the icon set assign $attrib(icon) # The assigment changed if {[string compare $assign [Info $oop assign,$slot]]} { $canvas itemconfigure icon,equipment,$slot -assign $assign Info $oop assign,$slot $assign } # The visibility changed if {![Info $oop visible,$slot]} { if {[info tclversion] >= 8.3} { $canvas itemconfigure equipment,$slot -state normal $canvas itemconfigure line,$slot -state normal } else { $canvas itemconfigure line,$slot -fill #989898 set x [Info $oop equipment,x,$slot] set y [Info $oop equipment,y,$slot] MoveBox $oop $x $y equipment $slot } Info $oop visible,$slot 1 } # Remember the char Info $oop char,equipment,$slot $attrib(char) # Remember the location Info $oop where,equipment,$slot equipment # Remember the item, != $index for floor Info $oop item,equipment,$slot $item # Sum the weights of displayed items incr weight [expr {$attrib(weight) * $attrib(number)}] } Info $oop equipment,weight $weight $canvas itemconfigure equipment,status,right -text [fmt_wgt $weight 1] set weightLimit [angband inventory weight_limit] set capacity [expr {$weightLimit / 2 + $weightLimit / 10}] set string "[fmt_wgt [angband inventory total_weight]]" append string "/[fmt_wgt $capacity]" append string "/[fmt_wgt $weightLimit 1]" $canvas itemconfigure equipment,status,left -text $string } else { $canvas itemconfigure equipment,status,left -text "" $canvas itemconfigure equipment,status,right -text "" } # Hide slots which aren't visible foreach slot [Info $oop slot_names] { if {!$display($slot) && [Info $oop visible,$slot]} { if {[info tclversion] >= 8.3} { $canvas itemconfigure equipment,$slot -state hidden $canvas itemconfigure line,$slot -state hidden } else { MoveBox $oop -20 -20 equipment $slot $canvas itemconfigure line,$slot -fill {} } Info $oop visible,$slot 0 } } return } # NSInventory2::SetList_Inventory -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::SetList_Inventory {oop} { set canvas [Info $oop canvas] set where [Info $oop where] # Clear the selection SelectionChanged $oop "" "" # Clear the list for {set i 0} {$i <= 23} {incr i} { if {[info tclversion] >= 8.3} { $canvas itemconfigure inventory,$i -state hidden } else { MoveBox $oop -20 -20 inventory $i } } $canvas itemconfigure inventory,status,left -text "" $canvas itemconfigure inventory,status,right -text "" $canvas itemconfigure inventory,title -text "Inventory" switch -- $where { equipment { if {![Info $oop both]} return set itemList [angband inventory find] set whereItem inventory } floor { set itemList [angband floor find -tester yes] $canvas itemconfigure inventory,title -text "Floor" set whereItem floor } inventory { if {[Info $oop both]} { set itemList [angband inventory find] } else { set itemList [angband inventory find -tester yes] } set whereItem inventory } } set index 0 set weight 0 foreach item $itemList { set box $item if {[string equal $where floor]} { set box $index } # Get info about the item angband $whereItem info $item attrib # Assign the icon $canvas itemconfigure icon,inventory,$box -assign $attrib(icon) # Display the box if {[info tclversion] >= 8.3} { $canvas itemconfigure inventory,$box -state normal } else { # Move the box into position set x [Info $oop inventory,x,$index] set y [Info $oop inventory,y,$index] MoveBox $oop $x $y inventory $box } # Hack -- Get char for floor item if {[string equal $where floor]} { set attrib(char) [string index "abcdefghijklmnopqrstuvw" $index] } # Remember the char Info $oop char,inventory,$box $attrib(char) # Remember the location Info $oop where,inventory,$box $whereItem # Remember the item, != $index for floor Info $oop item,inventory,$box $item # Sum the weights of displayed items incr weight [expr {$attrib(weight) * $attrib(number)}] incr index } Info $oop inventory,weight $weight $canvas itemconfigure inventory,status,right -text [fmt_wgt $weight 1] set numItems [llength $itemList] if {$numItems == 1} { set string [format "%d item" $numItems] } else { set string [format "%d items" $numItems] } if {0 && [string compare [Info $oop where] floor]} { set weightLimit [angband inventory weight_limit] set capacity [expr {$weightLimit / 2 + $weightLimit / 10}] append string " Threshold [fmt_wgt $capacity 1]" } $canvas itemconfigure inventory,status,left -text $string return } # NSInventory2::MakeBorder -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::MakeBorder {oop canvas x y width height} { set left [expr {$x + 1}] set top [expr {$y + 1}] set right [expr {$x + $width - 2}] set bottom [expr {$y + $height - 2}] # 3-pixel border $canvas create rectangle $left $top $right $bottom \ -outline #282828 -fill {} -width 3.0 # 1-pixel border $canvas create rectangle $left $top $right $bottom \ -outline #0070FF -fill {} return } # NSInventory2::MakeHeader -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::MakeHeader {oop canvas x y width text tags} { if {[Platform unix]} { set font {Helvetica 12 bold} } if {[Platform windows]} { set font {Helvetica 10 bold} } set fontHeight [font metrics $font -linespace] set left [expr {$x + 2}] set top [expr {$y + 2}] set right [expr {$x + $width - 3}] set bottom [expr {$top + 1 + $fontHeight + 1}] $canvas create rectangle $left $top $right $bottom \ -outline #282828 -fill #0000D4 $canvas create text [expr {$x + $width / 2}] [expr {$top + 1}] \ -text $text -font $font -fill White -anchor n -tags $tags return } # NSInventory2::MakeStatus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::MakeStatus {oop canvas x y width where} { set font [Value font,statusBar] set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] # 3-pixel border + background $canvas create rectangle [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] \ -outline #282828 -fill #0000D4 -width 3.0 \ -tags [list statusBar,rect $where,status] # 1-pixel border $canvas create rectangle [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] \ -outline #0070FF -fill {} \ -tags [list statusBar,rect $where,status] # Left text $canvas create text [expr {$x + 4}] \ [expr {$y + 3}] -anchor nw -fill gray -font $font \ -tags [list statusBar,text $where,status $where,status,left] # Right text $canvas create text [expr {$x + $width - 4}] \ [expr {$y + 3}] -anchor ne -fill gray -font $font \ -tags [list statusBar,text $where,status $where,status,right] foreach side {left right} { $canvas bind $where,status,$side \ "NSInventory2::BindStatus $oop enter $where $side" $canvas bind $where,status,$side \ "NSInventory2::BindStatus $oop leave $where $side" } return } # NSInventory2::BindStatus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::BindStatus {oop message where side} { if {$message == "leave"} { NSMainWindow::StatusText [Global main,oop] "" return } set string "" switch -- $where { equipment { switch -- $side { left { set string "Total/Threshold/Limit" } right { set string "Weight of equipment items" } } } inventory { switch -- $side { left { set string "Number of inventory items" } right { set string "Weight of inventory items" } } } } if {[string length $string]} { NSMainWindow::StatusText [Global main,oop] $string } return } # NSInventory2::MakeBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::MakeBox {oop where index {rgb1 ""} {rgb2 ""}} { set canvas [Info $oop canvas] set width [expr {[icon size] + 8}] set height [expr {[icon size] + 8}] if {![string length $rgb1]} { set rgb1 [format #%02x%02x%02x 102 51 0] set rgb2 [format #%02x%02x%02x 255 153 0] } # 3-pixel border set outline $rgb1 set fill [format #%02x%02x%02x 68 68 68] $canvas create rectangle 2 2 \ [expr {$width - 2}] [expr {$height - 2}] \ -fill $fill -outline $outline -width 3.0 \ -tags "$where,$index border,$where,$index" # 1-pixel border $canvas create rectangle 2 2 \ [expr {$width - 2}] [expr {$height - 2}] \ -fill {} -outline {} -tags "$where,$index sel,$where,$index" -width 1.0 Info $oop color,$where,$index $rgb2 Info $oop color2,$where,$index "" if 0 { # Widget $canvas create widget [expr {$width / 2}] \ [expr {$height / 2}] -assign {icon none 0} \ -tags "icon $where,$index icon,$where,$index" -anchor center $canvas bind icon,$where,$index \ "NSInventory2::Request $oop enter $where $index" $canvas bind icon,$where,$index \ "NSInventory2::Request $oop leave $where $index" $canvas bind icon,$where,$index \ "NSInventory2::Select $oop $where $index" $canvas bind icon,$where,$index \ "NSInventory2::Invoke $oop $where $index" } return } # NSInventory2::MoveBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::MoveBox {oop x y where index} { set canvas [Info $oop canvas] scan [$canvas coords icon,$where,$index] "%s %s" cx cy $canvas move $where,$index [expr {$x - $cx}] [expr {$y - $cy}] return } # NSInventory2::ShowBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::ShowBox {oop where index} { set canvas [Info $oop canvas] if {[info tclversion] >= 8.3} { $canvas itemconfigure $where,$index -state "" } return } # NSInventory2::HideBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::HideBox {oop where index} { set canvas [Info $oop canvas] if {[info tclversion] >= 8.3} { $canvas itemconfigure $where,$index -state hidden } else { MoveBox $oop -20 -20 $where $index } return } # NSInventory2::Button1 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Button1 {oop x y} { set canvas [Info $oop canvas] focus $canvas set itemId [$canvas find withtag current] if {![llength $itemId] || [string equal [$canvas type current] image]} { if {[string length [Info $oop select,index]]} { SelectionChanged $oop "" "" } return } Info $oop press,x $x Info $oop press,y $y return } # NSInventory2::Motion1 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Motion1 {oop x y} { set canvas [Info $oop canvas] set dragging [Info $oop dragging] set where [Info $oop select,where] set index [Info $oop select,index] # No item is selected if {![string length $index]} return # Not dragging if {!$dragging} { set x2 [Info $oop press,x] set y2 [Info $oop press,y] if {(abs($x - $x2) < 10) && (abs($y - $y2) < 10)} return # Copy the image from the dragged item set assign [$canvas itemcget icon,$where,$index -assign] $canvas itemconfigure icon,drag,0 -assign $assign # Copy colors from the drag item set rgb1 [$canvas itemcget border,$where,$index -outline] $canvas itemconfigure border,drag,0 -outline $rgb1 set rgb2 [Info $oop color,$where,$index] $canvas itemconfigure sel,drag,0 -outline $rgb2 # Display the drag feedback ShowBox $oop drag 0 # Drag in progress Info $oop dragging 1 } if {$x - 20 < 0} { set x 20 } elseif {$x + 20 > [winfo width $canvas]} { set x [expr {[winfo width $canvas] - 20}] } if {$y - 20 < 0} { set y 20 } elseif {$y + 20 > [winfo height $canvas]} { set y [expr {[winfo height $canvas] - 20}] } MoveBox $oop $x $y drag 0 return } # NSInventory2::Release1 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Release1 {oop x y} { set canvas [Info $oop canvas] set where [Info $oop select,where] set index [Info $oop select,index] # No drag in progress if {![Info $oop dragging]} return HideBox $oop drag 0 Info $oop dragging 0 set charItem [Info $oop char,$where,$index] # Drop onto the floor if {$y > [Info $oop top,status]} { set toggleChar "" set command_see 0 if {$command_see} { if {[string compare [Info $oop where] $where]} { set toggleChar / } } else { if {[string equal $where equipment]} { if {[angband inventory count]} { set toggleChar / } } } DoUnderlyingCommand d$toggleChar$charItem SkipMoreMessages $oop 1 # Done return } # Find out where the drag ended if {$x < [Info $oop width,equip]} { set dest equipment } else { set dest inventory } # Drag onto source does nothing if {[string equal $where $dest]} return switch -- $where { equipment { DoUnderlyingCommand t$charItem SkipMoreMessages $oop 1 } inventory { DoUnderlyingCommand w$charItem SkipMoreMessages $oop 1 } } return } # NSInventory2::Enter -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Enter {oop where index} { if {[string length [Info $oop select,where]]} return Highlight $oop 1 $where $index return } # NSInventory2::Leave -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Leave {oop where index} { if {![string length [Info $oop select,index]]} { Highlight $oop 0 $where $index } return } # NSInventory2::Request -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Request {oop type where index} { Info $oop request [list $type $where $index] if {![string length [Info $oop request,id]]} { Info $oop request,id [after 1 NSInventory2::HandleRequest $oop] } return } # NSInventory2::HandleRequest -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::HandleRequest {oop} { if {[Info $oop busy]} { Info $oop request,id [after 1 NSInventory2::HandleRequest $oop] return } set request [Info $oop request] Info $oop request {} set type [lindex $request 0] set where [lindex $request 1] set index [lindex $request 2] if {[string length [Info $oop highlight,where]]} { Leave $oop [Info $oop highlight,where] [Info $oop highlight,index] } # Double-check that the item exists if {[string equal $where inventory]} { if {$index >= [angband inventory count]} { Info $oop request,id "" return } } if {[string equal $type enter]} { Enter $oop $where $index } # Clear this after above stuff. See Request() Info $oop request,id "" if {[llength [Info $oop request]]} { Info $oop request,id [after 1 NSInventory2::HandleRequest $oop] } return } # NSInventory2::BalloonShow -- # # Show the balloon. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::BalloonShow {oop text x y} { # XXX We used to use NSBalloon, but some users said the # balloon would disappear randomly (in the Store, anyways). if {0} { NSBalloon::Show $text $x $y n return } set canvas [Info $oop canvas] incr x -[winfo rootx $canvas] incr y -[winfo rooty $canvas] set font [Global font,sys,normal] set textWidth [font measure $font $text] set textHeight [font metrics $font -linespace] set padx 4 set pady 2 set rectWidth [expr {$padx + $textWidth + $padx + 1}] set rectHeight [expr {$pady + $textHeight + $pady + 1}] # Not too far left if {$x - $rectWidth / 2 < 0} { incr x [expr {0 - ($x - $rectWidth / 2)}] # Not too far right } elseif {$x + $rectWidth / 2 > [winfo width $canvas]} { incr x [expr {[winfo width $canvas] - ($x + $rectWidth / 2)}] } # If balloon would obscure the statusbar, then put it above if {$y + $rectHeight > [Info $oop top,status]} { incr y [expr {0 - ([icon size] + 8 + $rectHeight + 4)}] } $canvas coords balloon,rect [expr {$x - $rectWidth / 2}] $y \ [expr {$x + $rectWidth / 2}] [expr {$y + $rectHeight}] $canvas coords balloon,text $x [expr {$pady + $y + 1}] $canvas itemconfigure balloon,text -text $text # Show the items $canvas itemconfigure balloon -state normal return } # NSInventory2::BalloonHide -- # # Hide the balloon. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::BalloonHide {oop} { if {0} { NSBalloon::Hide return } set canvas [Info $oop canvas] # Hide the items $canvas itemconfigure balloon -state hidden return } # NSInventory2::Track -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Track {oop what} { switch -- $what { equipment { SetList_Equipment $oop } inventory { SetList_Inventory $oop # Hack -- Do this so the statusbar weight is correct SetList_Equipment $oop } } return } # NSInventory2::ChooseItem -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::ChooseItem {oop show other} { Info $oop choose,show $show Info $oop choose,other $other if {$show} { Info $oop didChoose 0 } return } # NSInventory2::TermInkey -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::TermInkey {oop} { set win [Info $oop win] if {[Info $oop choose,show]} { SetList $oop [Info $oop choose,other] "" if {![Info $oop alwaysOnTop]} { if {![NSUtils::ToplevelHasFocus $win]} { Info $oop oldFocus [focus] WindowBringToFront $win } } Info $oop didChoose 1 } elseif {![Info $oop didChoose]} { # Nothing } else { if {![Info $oop alwaysOnTop]} { set oldFocus [Info $oop oldFocus] if {[string length $oldFocus] && ![string match $win* $oldFocus]} { catch {focus $oldFocus} } update } SetList $oop inventory "" 1 } return } # NSInventory2::Swap -- # # Display the "old" Inventory Window # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::Swap {oop} { set browsing [Info $oop browsing] set where [Info $oop where] set both [Info $oop both] NSWindowManager::Undisplay inventory2 NSModule::LoadIfNeeded NSInventory NSWindowManager::Display inventory $where "" $both if {[Info $oop choose,show]} { InventoryObj Info browsing $browsing InventoryObj Info choose,show 1 InventoryObj Info choose,other $where InventoryObj Info didChoose 1 if {![string match [Info $oop win]* [Info $oop oldFocus]]} { InventoryObj Info oldFocus [Info $oop oldFocus] } else { InventoryObj Info oldFocus [InventoryObj Info win] } } Value inventory,style old return } # NSInventory2::AlwaysOnTop -- # # Toggle the "Always On Top" option. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::AlwaysOnTop {oop} { set win [Info $oop win] set onTop [Info $oop alwaysOnTop] if {[Platform unix]} { wm state $win withdrawn } if {$onTop} { wm transient $win [Window main] } else { wm transient $win "" raise $win } if {[Platform unix]} { wm state $win normal } after idle focus $win Value inventory,alwaysOnTop $onTop # Synchronize the non-graphical inventory window if {[info exists ::Windows(inventory)] && [winfo exists [Window inventory]]} { if {$onTop} { wm transient [Window inventory] [Window main] } else { wm transient [Window inventory] "" } InventoryObj Info alwaysOnTop $onTop } return } # NSInventory2::ValueChanged_font_statusBar -- # # Called when the font,statusBar value changes. # Updates the Main Window statusbar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::ValueChanged_font_statusBar {oop} { set win [Info $oop win] set canvas [Info $oop canvas] # Get the desired font set font [Value font,statusBar] # Update the font. Too bad there isn't a -fontvar font variable $canvas itemconfigure statusBar,text -font $font set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] foreach itemId [$canvas find withtag statusBar,rect] { scan [$canvas coords $itemId] "%s %s %s %s" left top right bottom set bottom [expr {$top - 1 + $height - 2}] $canvas coords $itemId $left $top $right $bottom } scan [$canvas bbox statusBar,rect] "%d %d %d %d" left top right bottom $canvas configure -height $bottom wm geometry $win "" return } # NSInventory2::SkipMoreMessages -- # # Skip -more- prompts during drag & drop, # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSInventory2::SkipMoreMessages {oop skip} { return } proc Inventory2Obj {command args} { return [eval NSInventory2::$command [Global inventory2,oop] $args] } zangband/lib/script/tk/keypress.tcl0000644000000000000000000000401410250356274016404 0ustar rootroot# File: keypress.tcl # Purpose: KeyPress-related event bindings # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # bind Term_KeyPress_BindTag { angband keypress \037Control-Shift-%K\015 } bind Term_KeyPress_BindTag { } bind Term_KeyPress_BindTag { } bind Term_KeyPress_BindTag { # Special Control-KeyPress (ex Control-F1) if {![string length %A] || [string length %K] > 1} { angband keypress \037Control-%K\015 # Ascii Control-KeyPress } else { angband keypress %A } } bind Term_KeyPress_BindTag { } bind Term_KeyPress_BindTag { # Special Shift-KeyPress (ex Shift-F1) if {![string length %A]} { angband keypress \037Shift-%K\015 # Ascii Shift-KeyPress } else { angband keypress %A } } bind Term_KeyPress_BindTag { } bind Term_KeyPress_BindTag { angband keypress \037Alt-%K\015 } bind Term_KeyPress_BindTag { } bind Term_KeyPress_BindTag { # Special KeyPress (ex F1) if {![string length %A]} { angband keypress \037%K\015 # Normal keys with no modifiers } else { angband keypress %A } } bind Term_KeyPress_BindTag { angband keypress \033 } bind Term_KeyPress_BindTag { angband keypress \r } bind Term_KeyPress_BindTag { angband keypress \t } bind Term_KeyPress_BindTag { angband keypress \010 } bind Term_KeyPress_BindTag { angband keypress \010 } # Term_KeyPress_Bind -- # # Adds the Term_KeyPress_BindTag to the end of the list of bindtags # for the given window. This means KeyPress events are fed to the # Term via the "angband keypress" command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc Term_KeyPress_Bind {win} { bindtags $win [concat [bindtags $win] Term_KeyPress_BindTag] return } zangband/lib/script/tk/knowledge.tcl0000644000000000000000000007117010250356274016525 0ustar rootroot# File: knowledge.tcl # Purpose: the Knowledge Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSKnowledge { variable MenuString variable Priv # namespace eval NSKnowledge } # NSKnowledge::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::InitModule {} { variable Priv set Priv(hook) {} # Create the Knowledge Window NSObject::New NSKnowledge return } # NSKnowledge::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::CloseModule {} { catch { destroy [Window knowledge] } return } # NSKnowledge::NSKnowledge -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::NSKnowledge {oop} { variable Priv InitWindow $oop set win [Info $oop win] Info $oop group,current -1 Info $oop member,current -1 # Info about selected group & member for each hook foreach {hook label} $Priv(hook) { Info $oop group,$hook 0 Info $oop member,$hook -1 } set Priv(find,string) "" set Priv(find,fromStart) 1 NSWindowManager::RegisterWindow knowledge $win \ "GetDefaultGeometry $win reqwidth main" "" \ "NSKnowledge::DisplayCmd $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSKnowledge $oop $win # # Global list of application windows # Global knowledge,oop $oop Window knowledge $win return } # NSKnowledge::~NSKnowledge -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::~NSKnowledge {oop} { NSValueManager::RemoveClient listBG [Info $oop listBG,clientId] NSValueManager::RemoveClient font,knowledge [Info $oop font,clientId] return } # NSKnowledge::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::Info {oop info args} { global NSKnowledge # Verify the object NSObject::CheckObject NSKnowledge $oop # Set info if {[llength $args]} { switch -- $info { default { set NSKnowledge($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { groupCanvas { set canvistId [Info $oop group,canvistId] return [NSCanvist::Info $canvistId canvas] } memberCanvas { set canvistId [Info $oop member,canvistId] return [NSCanvist::Info $canvistId canvas] } default { return $NSKnowledge($oop,$info) } } } return } # NSKnowledge::InitWindow -- # # Create the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::InitWindow {oop} { variable Priv set win .knowledge$oop toplevel $win wm title $win "Knowledge" wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSKnowledge::Close $oop" # Start out withdrawn (hidden) wm withdraw $win Info $oop win $win # # Menus # InitMenus $oop # # Divider # MakeDivider $win.divider2 x # # Tabs! # set tabsId [NSObject::New NSTabs $win] foreach {hook label} $Priv(hook) { NSTabs::Add $tabsId $label } NSTabs::Info $tabsId invokeCmd "NSKnowledge::InvokeTab $oop" NSTabs::Info $tabsId active 1 Info $oop tabsId $tabsId # # Group List # set cw [font measure [Value font,knowledge] "W"] # set width [expr {$cw * 26}] set width [CalcGroupListWidth $oop] set iconSize [expr {[icon size] + 8}] frame $win.frameGroup \ -borderwidth 1 -relief sunken set canvistId [NSObject::New NSCanvist $win.frameGroup $iconSize $width 300 \ "NSKnowledge::NewItemCmd $oop" "NSKnowledge::HighlightItemCmd $oop"] set canvas [NSCanvist::Info $canvistId canvas] $canvas configure -background [Value listBG] $canvas configure -yscrollcommand "$win.frameGroup.scroll set" scrollbar $win.frameGroup.scroll \ -command "$canvas yview" -orient vert # Do something when a group is selected NSCanvist::Info $canvistId selectionCmd \ "NSKnowledge::SelectionChanged_Group $oop" # Update ourself when the listBG value changes Info $oop listBG,clientId \ [NSValueManager::AddClient listBG \ "NSKnowledge::ValueChanged_listBG $oop"] # Update ourself when the font,knowledge value changes Info $oop font,clientId \ [NSValueManager::AddClient font,knowledge \ "NSKnowledge::ValueChanged_font_knowledge $oop"] pack $win.frameGroup.scroll -side right -fill y pack $canvas -side left -expand yes -fill both -anchor nw Info $oop group,canvistId $canvistId # # Member List # # set width [expr {$cw * 50}] set width 350 frame $win.frame \ -borderwidth 1 -relief sunken set canvistId [NSObject::New NSCanvist $win.frame $iconSize $width 300 \ "NSKnowledge::NewItemCmd $oop" "NSKnowledge::HighlightItemCmd $oop"] set canvas [NSCanvist::Info $canvistId canvas] $canvas configure -background [Value listBG] $canvas configure -yscrollcommand "$win.frame.scroll set" scrollbar $win.frame.scroll \ -command "$canvas yview" -orient vert # Do something when a member is selected NSCanvist::Info $canvistId selectionCmd \ "NSKnowledge::SelectionChanged_Member $oop" # Do something when a selected member is clicked. NSCanvist::Info $canvistId clickCmd \ "NSKnowledge::Click_Member $oop" bind $canvas " NSKnowledge::Configure $oop $canvas " Info $oop member,canvistId $canvistId pack $win.frame.scroll -side right -fill y pack $canvas -side left -expand yes -fill both -anchor nw # # Statusbar # MakeStatusBar $win.statusBar 20 # # Geometry # grid rowconfig $win 0 -weight 0 -minsize 0 grid rowconfig $win 1 -weight 0 -minsize 0 grid rowconfig $win 2 -weight 1 -minsize 0 grid rowconfig $win 3 -weight 0 -minsize 0 grid columnconfig $win 0 -weight 0 -minsize 0 grid columnconfig $win 1 -weight 1 -minsize 0 if {[Platform windows]} { grid $win.divider2 \ -row 0 -column 0 -rowspan 1 -columnspan 2 -sticky ew } grid [NSTabs::Info $tabsId canvas] \ -row 1 -column 0 -rowspan 1 -columnspan 2 -sticky ew grid $win.frameGroup \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ns grid $win.frame \ -row 2 -column 1 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar -in $win \ -row 3 -column 0 -rowspan 1 -columnspan 2 -sticky ew # # Feed Term when keys pressed # bind $win "NSKnowledge::Close $oop" bind $win "NSKnowledge::Close $oop" bind $win "NSKnowledge::Find $oop 0" bind $win "NSKnowledge::Find $oop 1" # # Synch the scrollbars when window is shown. # Hmmm. This doesn't work if you bind to $win.frame.scroll... # NSUtils::SynchScrollBar [Info $oop groupCanvas] $win.frameGroup.scroll NSUtils::SynchScrollBar [Info $oop memberCanvas] $win.frame.scroll return } # NSKnowledge::InitMenus -- # # Create the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::InitMenus {oop} { # global NSKnowledge # global NSMenu variable MenuString variable Priv set win [Info $oop win] set mod "Ctrl" # # Menu bar # set mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSKnowledge::SetupMenus $oop" -identifier MENUBAR] Info $oop mbarId $mbar # Context-sensitive help NSMenu::Info $mbar menuSelectCmd "NSKnowledge::MenuSelect $oop" # Call our command when an entry is invoked NSMenu::Info $mbar invokeCmd "NSKnowledge::MenuInvoke $oop" # # Knowledge Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_KNOWLEDGE NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_KNOWLEDGE -label "Knowledge" -underline 0 \ -identifier M_KNOWLEDGE set entries {} set i 1 foreach {hook label} $Priv(hook) { lappend entries [list -type radiobutton -label $label \ -variable NSKnowledge($oop,radio,hook) -value $hook \ -accelerator $i -identifier E_HOOK_$i] bind $win "NSKnowledge::SetHook $oop $hook" incr i } lappend entries [list -type separator] lappend entries [list -type command -label "Find..." \ -accelerator f -underline 0 -identifier E_FIND] lappend entries [list -type command -label "Find Again" \ -accelerator g -underline 6 -identifier E_FIND_AGAIN] lappend entries [list -type separator] lappend entries [list -type command -label "Set Font" \ -underline 0 -identifier E_FONT] lappend entries [list -type separator] lappend entries [list -type command -label "Close" \ -underline 0 -accelerator $mod+W -identifier E_CLOSE] NSMenu::MenuInsertEntries $mbar -end MENU_KNOWLEDGE $entries # # Hook Menu # set m [NSMenu::Info $mbar menu].hook menu $m -tearoff 0 Info $oop hookMenu,menu $m Info $oop hookMenu,inserted 0 set MenuString(M_KNOWLEDGE) \ "Contains commands for displaying and searching groups." set MenuString(E_FIND) \ "Searches for a member by name." set MenuString(E_FIND_AGAIN) \ "Repeats the previous search." set MenuString(E_CLOSE) \ "Closes the window." return } # NSKnowledge::SetupMenus -- # # Prepare to post the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::SetupMenus {oop mbarID} { variable Priv set i 0 foreach {hook label} $Priv(hook) { lappend identList E_HOOK_[incr i] } lappend identList E_FIND E_FIND_AGAIN E_FONT E_CLOSE if {[Info $oop hookMenu,inserted]} { set menu [Info $oop hookMenu,menu] set last [$menu index end] for {set i 0} {$i <= $last} {incr i} { if {[string equal [$menu type $i] separator]} continue $menu entryconfigure $i -state disabled } CallHook $oop menu_setup } NSMenu::MenuEnable $mbarID $identList [Info $oop win].statusBar cover show return } # NSKnowledge::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::MenuSelect {oop menuId index ident} { variable MenuString variable Priv switch -glob -- $ident { {} { set desc {} } E_HOOK_* { set desc "Displays this group." } default { if {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop win].statusBar cover set $desc if {![string length $desc]} { if {$menuId == [Info $oop mbarId]} { [Info $oop win].statusBar cover hide } } return } # NSKnowledge::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::MenuInvoke {oop menuId ident} { variable Priv switch -glob -- $ident { E_HOOK_* { scan $ident "E_HOOK_%d" hookNum SetHook $oop [lindex $Priv(hook) [expr {($hookNum - 1) * 2}]] } E_FIND {Find $oop 0} E_FIND_AGAIN {Find $oop 1} E_FONT { NSModule::LoadIfNeeded NSFont NSWindowManager::Display font knowledge } E_CLOSE {Close $oop} } return } # NSKnowledge::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::DisplayCmd {oop message first args} { variable Priv switch -- $message { preDisplay { # Assume angband.exe called us. See Close() Info $oop hideOnEscape 0 # Display a specific group if {[llength $args]} { set hook [lindex $args 0] Info $oop hook hook_$hook if {[llength $args] > 1} { set index [lindex $args 1] DisplayMember $oop hook_$hook $index # Hack -- Escape hides window Info $oop hideOnEscape 1 } } SetHook $oop [Info $oop hook] } postDisplay { } postWithdraw { StateRemember $oop NSCanvist::DeleteAll [Info $oop group,canvistId] NSCanvist::DeleteAll [Info $oop member,canvistId] } } return } # NSKnowledge::Close -- # # Do something when closing the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::Close {oop} { if {[Info $oop hideOnEscape]} { NSWindowManager::Undisplay knowledge } angband keypress \033 return } # NSKnowledge::StateRemember -- # # Remember the selected group and member, then clear the lists. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::StateRemember {oop} { global Angband ### Mega-Hack -- Don't do this during geometry foolishness # if !$NSWindowManager::Priv(knowledge,setup) return set hook [Info $oop hook] # Because we are clearing the lists here, and don't want to # upset the user, I save the selected group/member so it can # be restored in StateRestore() below. # Because the contents of a group change as the character gains # knowledge, I save the proper index, not the row, of the # selected group/member. set row [Info $oop group,current] if {$row != -1} { set index [lindex [Info $oop group,match] $row] } else { set index -1 } Info $oop group,$hook $index set row [Info $oop member,current] if {$row != -1} { set index [lindex [Info $oop member,match] $row] } else { set index -1 } Info $oop member,$hook $index return } # NSKnowledge::StateRestore -- # # Restore the display. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::StateRestore {oop} { global Angband ### Mega-Hack -- Don't do this during geometry foolishness # if !$NSWindowManager::Priv(knowledge,setup) return set hook [Info $oop hook] # Restore the selected group. It might not be there if the # "cheat_know" option was turned off. set current [Info $oop group,$hook] if {$current != -1} { set row [lsearch -exact [Info $oop group,match] $current] if {$row != -1} { set canvistId [Info $oop group,canvistId] NSCanvist::UpdateSelection $canvistId $row {} NSCanvist::See $canvistId $row } else { Info $oop member,$hook -1 } } # Restore the selected member. It might not be there if the # "cheat_know" option was turned off. set current [Info $oop member,$hook] if {$current != -1} { set row [lsearch -exact [Info $oop member,match] $current] if {$row != -1} { set canvistId [Info $oop member,canvistId] NSCanvist::UpdateSelection $canvistId $row {} NSCanvist::See $canvistId $row } } return } # NSKnowledge::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::CallHook {oop message args} { return [uplevel #0 NSKnowledge::[Info $oop hook] $oop $message $args] } # NSKnowledge::SetHook -- # # Set the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::SetHook {oop hook} { global NSMenu variable Priv if {$hook != [Info $oop hook]} { set again 0 } else { set again 1 } if {!$again} { StateRemember $oop } # Remember the hook Info $oop hook $hook # Clear the hook-specific menu set hookMenu [Info $oop hookMenu,menu] $hookMenu delete 0 end eval destroy [winfo children $hookMenu] # Show icons by default ConfigureList_Member $oop 1 # Calculate the row height of the group list set rowHgt [font metrics [Value font,knowledge] -linespace] if {[icon size] > $rowHgt} { set rowHgt [icon size] } incr rowHgt 8 set canvistId [Info $oop group,canvistId] set canvas [NSCanvist::Info $canvistId canvas] NSCanvist::Info $canvistId rowHgt $rowHgt $canvas configure -yscrollincrement $rowHgt # Set the group list SetList_Group $oop # If the hook-menu is empty, remove it, otherwise insert it set mbarId [Info $oop mbarId] if {[string equal [$hookMenu index end] none]} { if {[Info $oop hookMenu,inserted]} { $NSMenu($mbarId,menu) delete 2 Info $oop hookMenu,inserted 0 } } else { set index [lsearch -exact $Priv(hook) $hook] if {![Info $oop hookMenu,inserted]} { $NSMenu($mbarId,menu) add cascade -menu $hookMenu Info $oop hookMenu,inserted 1 } $NSMenu($mbarId,menu) entryconfigure end \ -label [lindex $Priv(hook) [incr index]] } # Radiobutton menu entries Info $oop radio,hook $hook set tabsId [Info $oop tabsId] set current [NSTabs::Info $tabsId current] set tabId [NSTabs::GetNthId $tabsId [expr {[lsearch -exact $Priv(hook) $hook] / 2}]] if {$tabId != $current} { NSTabs::Smaller $tabsId $current NSTabs::Bigger $tabsId $tabId NSTabs::Info $tabsId current $tabId } [Info $oop win].statusBar itemconfigure t2 -text "" StateRestore $oop return } # NSKnowledge::Click_Member -- # # Do something when a selected member is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::Click_Member {oop canvistId row} { CallHook $oop click_member $row return } # NSKnowledge::SelectionChanged_Group -- # # When a "group" list item is selected, display artifacts/monsters # in the "member" list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::SelectionChanged_Group {oop canvistId select deselect} { # If nothing was selected, clear the member list if {![llength $select]} { set canvistId [Info $oop member,canvistId] NSCanvist::DeleteAll $canvistId Info $oop group,current -1 StatusBar $oop "" 0 [Info $oop win].statusBar itemconfigure t2 -text "" return } # Get the (first) row set row [lindex $select 0] Info $oop group,current $row # Add matching artifacts/monsters to the list SetList_Member $oop $row return } # NSKnowledge::SelectionChanged_Member -- # # When a "member" list item is selected, display memory for the # artifact/monster in the Recall Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::SelectionChanged_Member {oop canvistId select deselect} { # Do nothing if no new row was selected if {![llength $select]} { Info $oop member,current -1 StatusBar $oop "" 0 return } # Get the (first) row set row [lindex $select 0] Info $oop member,current $row # Call the hook to do other stuff (recall, etc) CallHook $oop select_member $row return } # NSKnowledge::SetList_Group -- # # Set the group list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::SetList_Group {oop} { set win [Info $oop win] set canvistId [Info $oop group,canvistId] # Feedback StatusBar $oop "Displaying..." 0 update idletasks # Clear the list NSCanvist::DeleteAll $canvistId # Call hook to set the group list CallHook $oop set_list_group Info $oop group,current -1 # Hack -- Clear the "member" list NSCanvist::DeleteAll [Info $oop member,canvistId] # Feedback StatusBar $oop "Done." 1 return } # NSKnowledge::SetList_Member -- # # Set the member list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::SetList_Member {oop group} { set canvistId [Info $oop member,canvistId] # Clear the list NSCanvist::DeleteAll $canvistId Info $oop member,current -1 # Call hook to set the member list CallHook $oop set_list_member $group return } # NSKnowledge::ConfigureList_Member -- # # Set the row height of the member list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::ConfigureList_Member {oop show_icons} { set canvistId [Info $oop member,canvistId] set canvas [NSCanvist::Info $canvistId canvas] set rowHgt [font metrics [Value font,knowledge] -linespace] if {$show_icons} { if {[icon size] > $rowHgt} { set rowHgt [icon size] } } incr rowHgt 8 NSCanvist::Info $canvistId rowHgt $rowHgt $canvas configure -yscrollincrement $rowHgt return } # NSKnowledge::StatusBar -- # # Display text in the status bar, perhaps clearing it later. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::StatusBar {oop text zap} { set win [Info $oop win] set label [$win.statusBar itemcget t1 -label] $label configure -text $text if {$zap} { NSUtils::ZapLabel $label } return } # NSKnowledge::NewItemCmd -- # # Called by NSCanvist::InsertItem() to create a list row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::NewItemCmd {oop canvistId y args} { variable Priv array set data [list -text "" -assign "" -color White -extra ""] array set data $args set c [NSCanvist::Info $canvistId canvas] set lineHeight [NSCanvist::Info $canvistId rowHgt] set font [Value font,knowledge] set fh [font metrics $font -linespace] set diff [expr {int([expr {($lineHeight - $fh) / 2}])}] # Get the width of the canvas set canvasWidth [winfo width $c] if 0 { # Image if {[string length $data(-assign)]} { set iw [icon size] set ih [icon size] set wid [expr {[icon size] + 8}] set xdiff [expr {int([expr {($wid - $iw) / 2}])}] set ydiff [expr {int([expr {($lineHeight - $ih) / 2}])}] lappend itemIdList [$c create widget $xdiff [expr {$y + $ydiff}] \ -assign $data(-assign)] } else { set wid 3 } } # Text lappend itemIdList [$c create text [expr {$wid + 1}] [expr {$y + $diff}] \ -text $data(-text) -anchor nw -font $font -fill $data(-color) \ -tags text] # Selection rectangle around everything lappend itemIdList [$c create rectangle 2 [expr {$y + 2}] \ [expr {([winfo width $c] - 1) - 2}] [expr {$y + $lineHeight - 2}] \ -fill {} -outline {} -tags selrect -width 2.0] if {[string length $data(-extra)]} { lappend itemIdList [$c create text [expr {($canvasWidth - 1) - 4}] \ [expr {$y + $diff}] -text $data(-extra) -anchor ne -font $font \ -fill White -tags extra] } return $itemIdList } # NSKnowledge::HighlightItemCmd -- # # Called by NSCanvist::Select() to highlight a row. # # Arguments: # oop OOP ID. See above. # canvistId OOP ID of NSCanvist object. # state 1 or 0 highlight state. # args List of canvas item ids # # Results: # What happened. proc NSKnowledge::HighlightItemCmd {oop canvistId state args} { set canvas [NSCanvist::Info $canvistId canvas] set itemIdList $args set idRect [FindItemByTag $canvas $itemIdList selrect] if {[NSUtils::HasFocus $canvas]} { set fill [Value listHilite] } else { set fill [Value listInactive] } if {$state} { $canvas itemconfigure $idRect -outline $fill } else { $canvas itemconfigure $idRect -fill {} -outline {} } return } # NSKnowledge::Configure -- # # Called as a event script. Resizes the selection rectangles # so they fit properly. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::Configure {oop canvas} { # Get the width of the canvas set canvasWidth [winfo width $canvas] foreach itemId [$canvas find withtag selrect] { set coords [$canvas coords $itemId] set right [expr {($canvasWidth - 1) - 2}] set coords [lreplace $coords 2 2 $right] eval $canvas coords $itemId $coords } foreach itemId [$canvas find withtag extra] { set coords [$canvas coords $itemId] set right [expr {($canvasWidth - 1) - 4}] set coords [lreplace $coords 0 0 $right] eval $canvas coords $itemId $coords } # Set the scrollregion to prevent horizontal scrolling scan [$canvas cget -scrollregion] "%s %s %s %s" x1 y1 x2 y2 $canvas configure -scrollregion "$x1 $y1 $canvasWidth $y2" return } # NSKnowledge::InvokeTab -- # # Called when a tab is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::InvokeTab {oop tabsId tabId} { variable Priv set index [lsearch -exact [NSTabs::Info $tabsId id] $tabId] SetHook $oop [lindex $Priv(hook) [expr {$index * 2}]] return } # NSKnowledge::DisplayMember -- # # Find the group the given member is in, and display it. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::DisplayMember {oop hook member} { foreach group [CallHook $oop group_list] { set memberList [CallHook $oop member_list $group] set index [lsearch -exact $memberList $member] if {$index != -1} { # Note: real index, not list row Info $oop group,$hook $group Info $oop member,$hook $member break } } return } # NSKnowledge::Find -- # # Simple search routine to look for a member by name. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::Find {oop again} { variable Priv # Repeat the last search if {$again && [string length $Priv(find,string)]} { set string $Priv(find,string) # Enter a string to find, start from the beginning } else { # Ask the user for a name set string [NSUtils::StringBox -title "Find" \ -initial $Priv(find,string) -prompt "find-prompt" \ -buttons [list "Find" "Cancel"] -parent [Info $oop win]] if {![string length $string]} return # Clean up after the dialog, give message StatusBar $oop "Searching..." 1 update # Reset search parameters set Priv(find,string) $string } # Default to searching from the beginning set groupRow 0 set memberRow 0 # Search in selected group, if any if {!$Priv(find,fromStart)} { set groupCurr [Info $oop group,current] set memberCurr [Info $oop member,current] if {$groupCurr != -1} { set groupRow $groupCurr } if {$memberCurr != -1} { set memberRow [expr {$memberCurr + 1}] } } set max [NSCanvist::Info [Info $oop group,canvistId] count] # Compare lowercase set string [string tolower $string] set groupList [CallHook $oop group_list] for {set i $groupRow} {$i < $max} {incr i} { set group [lindex $groupList $i] set memberList [lrange [CallHook $oop member_list $group] $memberRow end] foreach index $memberList { # Get the member name set name2 [CallHook $oop member_name $index] # Compare lowercase set name2 [string tolower $name2] # Found a match if {[string first $string $name2] != -1} { set canvistId [Info $oop group,canvistId] # The new group is not displayed if {![NSCanvist::IsRowSelected $canvistId $i]} { # Clear the current selection NSCanvist::RemoveSelection $canvistId # Select the new group. As a side effect, the # SetList_Member() command is called to display # the monsters in the group. NSCanvist::UpdateSelection $canvistId $i {} NSCanvist::See $canvistId $i # The new group is already selected } else { } set canvistId [Info $oop member,canvistId] # Select the matching member, deselecting all others NSCanvist::UpdateSelection $canvistId $memberRow all NSCanvist::See $canvistId $memberRow # Don't search from start next time set Priv(find,fromStart) 0 # Clear "Searching..." message StatusBar $oop "" 0 # Done return } incr memberRow } set memberRow 0 } # If we didn't search from the start, then wrap around if {!$Priv(find,fromStart)} { set Priv(find,fromStart) 1 Find $oop 1 return } StatusBar $oop "No match for \"$string\"." 1 return } # NSKnowledge::CalcGroupListWidth -- # # Returns the desired width of the group list. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::CalcGroupListWidth {oop} { variable Priv # Get the desired font set font [Value font,knowledge] set maxWidth 100 foreach {hook label} $Priv(hook) { foreach name [$hook $oop group_names] { set width [font measure $font $name] if {$width > $maxWidth} { set maxWidth $width } } } return [expr {[icon size] + 8 + $maxWidth + 20 + 4}] } # NSKnowledge::ValueChanged_font_knowledge -- # # Called when the font,knowledge value changes. # Updates the Knowledge Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::ValueChanged_font_knowledge {oop} { # Get the desired font set font [Value font,knowledge] # Calculate row height set rowHgt [font metrics $font -linespace] if {[icon size] > $rowHgt} { set rowHgt [icon size] } incr rowHgt 8 # Set row height of group list set canvistId [Info $oop group,canvistId] set canvas [NSCanvist::Info $canvistId canvas] NSCanvist::Info $canvistId rowHgt $rowHgt $canvas configure -yscrollincrement $rowHgt # Set the width of the group list set oldWidth [winfo width $canvas] set newWidth [CalcGroupListWidth $oop] $canvas configure -width $newWidth # Set the scrollregion to prevent horizontal scrolling scan [$canvas cget -scrollregion] "%s %s %s %s" x1 y1 x2 y2 $canvas configure -scrollregion "$x1 $y1 $newWidth $y2" # Hack -- Resize the toplevel so the member list is not resized set diff [expr {$newWidth - $oldWidth}] if {$diff} { set win [Info $oop win] set newWidth [expr {[winfo width $win] + $diff}] NSToplevel::SetTotalWidth $win $newWidth } if {[winfo ismapped [Info $oop win]]} { [Info $oop groupCanvas] itemconfigure text -font $font [Info $oop memberCanvas] itemconfigure text -font $font } return } # NSKnowledge::ValueChanged_listBG -- # # Called when the listBG value changes. # Updates the Knowledge Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSKnowledge::ValueChanged_listBG {oop} { set color [Value listBG] [Info $oop groupCanvas] configure -background $color [Info $oop memberCanvas] configure -background $color return } zangband/lib/script/tk/main-window.tcl0000644000000000000000000013770710250356274017010 0ustar rootroot# File: main-window.tcl # Purpose: the Main Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMainWindow { variable Priv variable tracking 0 variable trackId 0 variable trackStepping 0 variable trackX variable trackY # namespace eval NSMainWindow } # NSMainWindow::InitModule -- # # One-time-only-ever initialization. # # Arguments: # # Results: # What happened. proc NSMainWindow::InitModule {} { global Display global PYPX NSModule::LoadIfNeeded NSMap NSModule::LoadIfNeeded NSWidget NSModule::LoadIfNeeded NSTerm # The character's position set PYPX "0 0" # Keep track of active window (inventory, book, etc) set Display(window) none # Create the main window NSObject::New NSMainWindow return } # NSMainWindow::NSMainWindow -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::NSMainWindow {oop} { InitWindow $oop # Window positions Info $oop window,autosave [Value window,autosave] # # Global access # Window main [Info $oop win] Global main,oop $oop InitAutobar $oop return } # NSMainWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMainWindow::Info {oop info args} { global NSMainWindow # Verify the object NSObject::CheckObject NSMainWindow $oop # Set info if {[llength $args]} { switch -- $info { default { set NSMainWindow($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSMainWindow($oop,$info) } } } return } # NSMainWindow::InitWindow -- # # Creates the Main Window. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::InitWindow {oop} { global Angband set win .main$oop toplevel $win wm title $win "Main - ZAngband" # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSMainWindow::Close $oop" # Start out withdrawn (hidden) wm withdraw $win # Remember the window Info $oop win $win # Create the menus InitMenus $oop set frame $win.divider2 MakeDivider $frame x # # Statusbar # There is a level of tomfoolery with the statusbar to prevent # a really long message causing the Main Window to change size. # This is in spite of the fact that many other windows do not # change size with long statusbar labels. I thought gridded # geometry solved the problem, but not in this case... # # The hack involves pack'ing the label in a frame, and turning # off pack propagation for that frame. Oh well. # # Font for all statusbars set font [Value font,statusBar] frame $win.statusBar \ -borderwidth 0 frame $win.statusBar.frameLabel \ -borderwidth 0 label $win.statusBar.frameLabel.label \ -anchor w -text "Hello world!" -relief sunken -padx 2 \ -foreground [Value main,statusbar,color] -background Black -font $font label $win.statusBar.center \ -text "C" -relief sunken -width 2 -padx 0 -foreground White \ -background Black -font $font label $win.statusBar.depth \ -relief sunken -width 12 -padx 2 \ -foreground White -background Black -font $font bind $win.statusBar.frameLabel.label \ "NSMainWindow::ContextMenu_StatusBar $win.context %X %Y" # Used in various places Global main,statusBar $win.statusBar.frameLabel.label # Hack pack $win.statusBar.frameLabel.label -fill x pack propagate $win.statusBar.frameLabel no grid columnconfigure $win.statusBar 0 -weight 1 grid columnconfigure $win.statusBar 1 -weight 0 grid columnconfigure $win.statusBar 2 -weight 0 grid rowconfigure $win.statusBar 0 -weight 0 grid $win.statusBar.frameLabel \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar.center \ -row 0 -column 1 -rowspan 1 -columnspan 1 grid $win.statusBar.depth \ -row 0 -column 2 -rowspan 1 -columnspan 1 bind $win.statusBar.center " %W configure -foreground gray60 NSMainWindow::StatusText $oop {Click to recenter the display.} " bind $win.statusBar.center " %W configure -foreground White NSMainWindow::StatusText $oop {} " # Update ourself when the font,statusBar value changes NSValueManager::AddClient font,statusBar \ "NSMainWindow::ValueChanged_font_statusBar" # # Message line when Message Window is closed # set frame $win.message frame $frame -background black -borderwidth 1 -relief sunken # # Misc info when Misc Window is closed # set frame $win.misc frame $frame -background black -borderwidth 1 -relief sunken # # Main widget # # Black background affects border color frame $win.mainframe \ -borderwidth 1 -relief sunken -background Black # Get the icon dimensions set gsize [icon size] # This is a large monitor if {[winfo screenwidth .] >= 800} { set width [expr {15 * 32}] set height [expr {11 * 32}] # This is a small monitor } else { set width [expr {13 * 32}] set height [expr {9 * 32}] } set widgetId [NSObject::New NSWidget $win.mainframe \ $width $height $gsize $gsize] NSWidget::Info $widgetId leaveCmd NSMainWindow::Leave set widget [NSWidget::Info $widgetId widget] bind $widget "NSMainWindow::TrackPress $oop %x %y" bind $widget "NSMainWindow::TrackMotion $oop %x %y" bind $widget "NSMainWindow::TrackRelease $oop" bind $widget "NSMainWindow::MouseCommand $oop %x %y +" bind $widget "NSMainWindow::MouseCommand $oop %x %y ." bind $widget " NSWidget::Info $widgetId track,x %x NSWidget::Info $widgetId track,y %y NSWidget::Info $widgetId track,mouseMoved 0 " bind $widget \ "NSWidget::TrackOnce $widgetId %x %y" bind $widget \ "NSMainWindow::ButtonPress3 $oop %x %y %X %Y" bind $widget \ "NSRecall::PopupSelect_Use $win.context %X %Y" # When the pointer leaves the Main Window Widget, we clear the # statusbar text, in addition to the behaviour defined by the # NSWidget module. # bind $widget "+NSMainWindow::StatusText $oop {}" # Remember the center of the Main Window Widget. Global main,widget,center [angband player position] variable HT "" # The "big map", the map of the entire cave with scroll bars. # The user can change the scale via a popup menu, so we save # the desired scale. set scale [Value bigmap,scale] set width [expr $width - 16] set height [expr $height - 16] set mapId [NSObject::New NSMap $widget $width $height $scale $scale] set widget2 [NSMap::Info $mapId widget] NSMap::Info $mapId scaleCmd \ "Value bigmap,scale \[NSWidget::Info [NSMap::Info $mapId widgetId] scale]" bind $widget2 {+ [Global mapdetail,widget] center -100 -100 NSMainWindow::StatusText [Global main,oop] {} } # Hide the Big Map when clicked (but not dragged) bind $widget2 { if {![NSWidget::Info [Global bigmap,widgetId] track,mouseMoved]} { angband keypress \033 } } # Each NSMap widget has Left/Right etc bindings. Need this to # hide the map. bind $widget2 { angband keypress \033 } # Global access Global main,widgetId $widgetId Global main,widget $widget Global bigmap,mapId $mapId Global bigmap,widgetId [NSMap::Info $mapId widgetId] Global bigmap,widget [NSMap::Info $mapId widget] # This binding is called whenever the Main Window is resized # by the user. bind $widget \ "NSMainWindow::Configure $oop %w %h" pack $widget -expand yes -fill both # # Geometry # grid rowconfigure $win 0 -weight 0 grid rowconfigure $win 1 -weight 0 grid rowconfigure $win 2 -weight 1 grid rowconfigure $win 3 -weight 0 grid columnconfigure $win 0 -weight 0 grid columnconfigure $win 1 -weight 1 grid $win.divider2 \ -row 0 -column 0 -rowspan 1 -columnspan 2 -sticky ew grid $win.message \ -row 1 -column 0 -columnspan 2 -sticky we grid $win.misc \ -row 2 -column 0 -sticky ns grid $win.mainframe \ -row 2 -column 1 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar \ -row 3 -column 0 -rowspan 1 -columnspan 2 -sticky ew # # Context menu # menu $win.context -tearoff 0 # # Feed Term when keys pressed # Term_KeyPress_Bind $win # Create terms window set width [expr {16 * 80}] set height [expr {16 * 24}] set term .term toplevel $term wm title $term "Terminal" wm geometry $term +$width+$height wm minsize $term $width $height Term_KeyPress_Bind $term # Do stuff when window closes wm protocol $term WM_DELETE_WINDOW "NSTerm::Close $oop" set termId [NSObject::New NSTerm .term $width $height 16 16] update return } proc NSMainWindow::InitAutobar {oop} { set statusBar [Global main,statusBar] bind $statusBar \ "NSMainWindow::ShowAutobar $oop" return } proc NSMainWindow::ShowAutobar {oop} { # Allow easy rebooting of the module if {[NSModule::LoadIfNeeded NSAutobar]} { set autobarId [Global autobar,oop] set statusBar [Global main,statusBar] bind $statusBar \ "NSAutobar::Event $autobarId leave-status" } set autobarId [Global autobar,oop] NSAutobar::Event $autobarId enter-status return } # NSMainWindow::InitMenus -- # # Initialize the menus for the Main Window. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::InitMenus {oop} { global Angband set win [Info $oop win] set mbarId [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSMainWindow::SetupMenus $oop" -identifier MENUBAR] # Call our command when an entry is invoked NSMenu::Info $mbarId invokeCmd "NSMainWindow::MenuInvoke $oop" Info $oop mbarId $mbarId # # File Menu # NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_FILE NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_FILE -label "File" -underline 0 -identifier M_FILE set entries {} lappend entries [list -type command -label "Save" -identifier E_GAME_SAVE] lappend entries [list -type separator] lappend entries [list -type command -label "Quit With Save" -identifier E_GAME_EXIT] lappend entries [list -type command -label "Quit" -identifier E_GAME_ABORT] NSMenu::MenuInsertEntries $mbarId -end MENU_FILE $entries # # Inven Menu # set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_INVEN] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_INVEN -label "Inven" -underline 0 -identifier M_INVEN # Magic Menu set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_MAGIC] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Activate" -identifier E_MAGIC_ACTIVATE] lappend entries [list -type command -label "Aim Wand" -identifier E_MAGIC_WAND] lappend entries [list -type command -label "Drink Potion" -identifier E_MAGIC_POTION] lappend entries [list -type command -label "Read Scroll" -identifier E_MAGIC_SCROLL] lappend entries [list -type command -label "Use Staff" -identifier E_MAGIC_STAFF] lappend entries [list -type command -label "Zap Rod" -identifier E_MAGIC_ROD] lappend entries [list -type separator] lappend entries [list -type command -label "Browse" -identifier E_MAGIC_BROWSE] lappend entries [list -type command -label "Study" -identifier E_MAGIC_STUDY] NSMenu::MenuInsertEntries $mbarId -end MENU_MAGIC $entries set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_USE] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Destroy" -identifier E_USE_DESTROY] lappend entries [list -type command -label "Drop" -identifier E_USE_DROP] lappend entries [list -type command -label "Pick Up" -identifier E_USE_PICKUP] lappend entries [list -type command -label "Take Off" -identifier E_USE_TAKEOFF] lappend entries [list -type command -label "Wear/Wield" -identifier E_USE_WIELD] lappend entries [list -type separator] lappend entries [list -type command -label "Eat Food" -identifier E_USE_FOOD] lappend entries [list -type command -label "Fire Missle" -identifier E_USE_MISSILE] lappend entries [list -type command -label "Fuel Light" -identifier E_USE_FUEL] lappend entries [list -type command -label "Jam Spike" -identifier E_USE_SPIKE] lappend entries [list -type command -label "Throw" -identifier E_USE_THROW] NSMenu::MenuInsertEntries $mbarId -end MENU_USE $entries set entries {} lappend entries [list -type command -label "Equipment" -identifier E_INVEN_EQUIPMENT] lappend entries [list -type command -label "Inventory" -identifier E_INVEN_INVENTORY] lappend entries [list -type separator] lappend entries [list -type cascade -menu MENU_MAGIC -label "Magic" -identifier M_MAGIC] lappend entries [list -type cascade -menu MENU_USE -label "Use" -identifier M_USE] lappend entries [list -type separator] lappend entries [list -type command -label "Inspect" -identifier E_INVEN_INSPECT] lappend entries [list -type separator] lappend entries [list -type command -label "Inscribe" -identifier E_INVEN_INSCRIBE] lappend entries [list -type command -label "Uninscribe" -identifier E_INVEN_UNINSCRIBE] NSMenu::MenuInsertEntries $mbarId -end MENU_INVEN $entries # # Action Menu # set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_ACTION] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_ACTION -label "Action" -underline 0 -identifier M_ACTION set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_ACTION_ALTER] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Alter" -identifier E_ACTION_ALTER] lappend entries [list -type command -label "Bash" -identifier E_ACTION_BASH] lappend entries [list -type command -label "Close" -identifier E_ACTION_CLOSE] lappend entries [list -type command -label "Disarm" -identifier E_ACTION_DISARM] lappend entries [list -type command -label "Open" -identifier E_ACTION_OPEN] lappend entries [list -type command -label "Tunnel" -identifier E_ACTION_TUNNEL] NSMenu::MenuInsertEntries $mbarId -end MENU_ACTION_ALTER $entries set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_ACTION_LOOKING] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Look" -identifier E_ACTION_LOOK] lappend entries [list -type command -label "Map" -identifier E_ACTION_MAP] NSMenu::MenuInsertEntries $mbarId -end MENU_ACTION_LOOKING $entries set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_ACTION_RESTING] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Rest" -identifier E_ACTION_REST] lappend entries [list -type command -label "Stay (With Pickup)" -identifier E_ACTION_STAY] lappend entries [list -type command -label "Stay" -identifier E_ACTION_STAY_TOGGLE] NSMenu::MenuInsertEntries $mbarId -end MENU_ACTION_RESTING $entries set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_ACTION_SEARCHING] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Search" -identifier E_ACTION_SEARCH] lappend entries [list -type command -label "Search Mode" -identifier E_ACTION_SEARCH_MODE] NSMenu::MenuInsertEntries $mbarId -end MENU_ACTION_SEARCHING $entries set menuId [NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_ACTION_MOVEMENT] NSMenu::Info $menuId setupCmd "NSMainWindow::MenuSetupCmd $oop" set entries {} lappend entries [list -type command -label "Go Down" -identifier E_ACTION_DOWN] lappend entries [list -type command -label "Go Up" -identifier E_ACTION_UP] lappend entries [list -type command -label "Run" -identifier E_ACTION_RUN] lappend entries [list -type command -label "Walk (With Pickup)" -identifier E_ACTION_WALK] lappend entries [list -type command -label "Walk" -identifier E_ACTION_WALK_TOGGLE] NSMenu::MenuInsertEntries $mbarId -end MENU_ACTION_MOVEMENT $entries set entries {} lappend entries [list -type cascade -menu MENU_ACTION_ALTER -label "Alter" -identifier M_ACTION_ALTER] lappend entries [list -type cascade -menu MENU_ACTION_LOOKING -label "Looking" -identifier M_ACTION_LOOKING] lappend entries [list -type cascade -menu MENU_ACTION_MOVEMENT -label "Movement" -identifier M_ACTION_MOVEMENT] lappend entries [list -type cascade -menu MENU_ACTION_RESTING -label "Resting" -identifier M_ACTION_RESTING] lappend entries [list -type cascade -menu MENU_ACTION_SEARCHING -label "Searching" -identifier M_ACTION_SEARCHING] lappend entries [list -type separator] lappend entries [list -type command -label "Note" -identifier E_ACTION_NOTE] lappend entries [list -type command -label "Repeat" -identifier E_ACTION_REPEAT] lappend entries [list -type command -label "Target" -identifier E_ACTION_TARGET] lappend entries [list -type separator] lappend entries [list -type command -label "Pets" -identifier E_ACTION_PETS] lappend entries [list -type command -label "Use Power" -identifier E_ACTION_POWER] NSMenu::MenuInsertEntries $mbarId -end MENU_ACTION $entries # # Other Menu # NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_OTHER NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_OTHER -label "Other" -underline 0 -identifier M_OTHER NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_PREFERENCES set entries {} lappend entries [list -type command -label "Font" -identifier E_PREF_FONT] lappend entries [list -type command -label "Options" -identifier E_PREF_OPTIONS] NSMenu::MenuInsertEntries $mbarId -end MENU_PREFERENCES $entries set entries {} lappend entries [list -type command -label "Character Info" -identifier E_OTHER_INFO] lappend entries [list -type command -label "Feeling" -identifier E_OTHER_FEELING] lappend entries [list -type command -label "Knowledge" -identifier E_OTHER_KNOWLEDGE] lappend entries [list -type command -label "Message History" -identifier E_OTHER_MESSAGES] lappend entries [list -type cascade -menu MENU_PREFERENCES -label "Preferences" -identifier M_PREFERENCES] lappend entries [list -type command -label "Quest Status" -identifier E_OTHER_QUEST] lappend entries [list -type command -label "Time Of Day" -identifier E_OTHER_TIME] NSMenu::MenuInsertEntries $mbarId -end MENU_OTHER $entries # # Window Menu # NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_WINDOW NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_WINDOW -label "Window" -underline 0 -identifier M_WINDOW set entries {} lappend entries [list -type command -label "Arrange Windows..." -identifier E_WINDOW_DEFPOS] lappend entries [list -type command -label "Maximize Windows..." -identifier E_WINDOW_MAXIMIZE] lappend entries [list -type separator] lappend entries [list -type command -label "Save Window Positions" -identifier E_WINDOW_SAVEPOS] lappend entries [list -type command -label "Load Window Positions" -identifier E_WINDOW_LOADPOS] lappend entries [list -type checkbutton -label "AutoSave Positions" \ -variable ::NSMainWindow($oop,window,autosave) -identifier E_WINDOW_AUTOSAVE] if {[file exists [PathTk choice-window.tcl]]} { Info $oop choiceWindow [Value choicewindow,show] lappend entries [list -type separator] lappend entries [list -type checkbutton -label "Choice Window" \ -variable ::NSMainWindow($oop,choiceWindow) -identifier E_CHOICEWINDOW] } Info $oop messageWindow [Value message,float] lappend entries [list -type checkbutton -label "Message Window" \ -variable ::NSMainWindow($oop,messageWindow) -identifier E_WINDOW_MESSAGE] Info $oop messagesWindow 0 lappend entries [list -type checkbutton -label "Messages Window" \ -variable ::NSMainWindow($oop,messagesWindow) -identifier E_WINDOW_MESSAGES] Info $oop miscWindow [Value misc,float] lappend entries [list -type checkbutton -label "Misc Window" \ -variable ::NSMainWindow($oop,miscWindow) -identifier E_WINDOW_MISC] if 0 { lappend entries [list -type checkbutton -label "Progress Window" \ -variable ::NSMainWindow($oop,progressWindow) \ -identifier E_WINDOW_PROGRESS] } Info $oop recallWindow [Value recall,show] lappend entries [list -type checkbutton -label "Recall Window" \ -variable ::NSMainWindow($oop,recallWindow) -identifier E_WINDOW_RECALL] NSMenu::MenuInsertEntries $mbarId -end MENU_WINDOW $entries # # Help Menu # NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_HELP NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_HELP -label "Help" -underline 0 -identifier M_HELP set entries {} lappend entries [list -type command -label "Help" -identifier E_HELP] lappend entries [list -type command -label "Tips" -identifier E_TIPS] lappend entries [list -type separator] lappend entries [list -type command \ -label "About ZAngband..." -identifier E_ABOUT] NSMenu::MenuInsertEntries $mbarId -end MENU_HELP $entries NSMenu::SetIdentArray $mbarId return } # NSMainWindow::SetupMenus -- # # Called by NSMenus::_MenuPostCommand() to enable menu items before # posting a menu. # # Arguments: # oop OOP ID of NSMainWindow object. # mbarId OOP ID of NSMenu object (the menubar). # # Results: # What happened. proc NSMainWindow::SetupMenus {oop mbarId} { global Windows lappend identList E_WINDOW_SAVEPOS E_WINDOW_DEFPOS \ E_WINDOW_LOADPOS E_WINDOW_AUTOSAVE E_WINDOW_MAXIMIZE E_ABOUT E_TIPS lappend identList M_PREFERENCES E_PREF_FONT lappend identList E_CHOICEWINDOW E_WINDOW_MESSAGE E_WINDOW_MESSAGES \ E_WINDOW_MISC E_WINDOW_RECALL if {[info exists Windows(choice)]} { Info $oop choiceWindow [winfo ismapped [Window choice]] } Info $oop messageWindow [winfo ismapped [Window message]] if {[info exists Windows(message2)]} { Info $oop messagesWindow [winfo ismapped [Window message2]] } Info $oop miscWindow [winfo ismapped [Window misc]] Info $oop recallWindow [winfo ismapped [Window recall]] if {[string equal [angband inkey_flags] INKEY_CMD]} { lappend identList E_GAME_SAVE E_GAME_EXIT E_OTHER_FEELING \ E_OTHER_INFO E_OTHER_KNOWLEDGE \ E_OTHER_MESSAGES E_PREF_OPTIONS E_HELP \ E_OTHER_QUEST E_OTHER_TIME } lappend identList E_GAME_ABORT NSMenu::MenuEnable $mbarId $identList return } # NSMainWindow::MenuSetupCmd -- # # Called when a menu is about to be posted. We use this to change the # setupMode of a menu so we don't need to pass a huge list of identifiers # to the MenuEnable() command. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMainWindow::MenuSetupCmd {oop menuId} { if {[string compare [angband inkey_flags] INKEY_CMD]} { NSMenu::Info $menuId setupMode disabled } else { NSMenu::Info $menuId setupMode normal } return } # NSMainWindow::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMainWindow::MenuInvoke {oop menuId ident} { switch -glob -- $ident { E_GAME_SAVE {DoUnderlyingCommand ^s} E_GAME_EXIT {DoUnderlyingCommand ^x} E_GAME_ABORT {QuitNoSave} E_MAGIC_ACTIVATE {DoUnderlyingCommand A} E_MAGIC_WAND {DoUnderlyingCommand a} E_MAGIC_POTION {DoUnderlyingCommand q} E_MAGIC_SCROLL {DoUnderlyingCommand r} E_MAGIC_STAFF {DoUnderlyingCommand u} E_MAGIC_ROD {DoUnderlyingCommand z} E_MAGIC_BROWSE {DoUnderlyingCommand b} E_MAGIC_STUDY {DoUnderlyingCommand G} E_USE_DESTROY {DoUnderlyingCommand k} E_USE_DROP {DoUnderlyingCommand d} E_USE_PICKUP {DoUnderlyingCommand g} E_USE_TAKEOFF {DoUnderlyingCommand t} E_USE_WIELD {DoUnderlyingCommand w} E_USE_FOOD {DoUnderlyingCommand E} E_USE_MISSILE {DoUnderlyingCommand f} E_USE_FUEL {DoUnderlyingCommand F} E_USE_SPIKE {DoUnderlyingCommand j} E_USE_THROW {DoUnderlyingCommand v} E_INVEN_EQUIPMENT {DoUnderlyingCommand e} E_INVEN_INVENTORY {DoUnderlyingCommand i} E_INVEN_INSPECT {DoUnderlyingCommand I} E_INVEN_INSCRIBE {DoUnderlyingCommand \{} E_INVEN_UNINSCRIBE {DoUnderlyingCommand \}} E_ACTION_ALTER {DoUnderlyingCommand +} E_ACTION_BASH {DoUnderlyingCommand B} E_ACTION_CLOSE {DoUnderlyingCommand c} E_ACTION_DISARM {DoUnderlyingCommand D} E_ACTION_DOWN {DoUnderlyingCommand >} E_ACTION_OPEN {DoUnderlyingCommand o} E_ACTION_LOOK {DoUnderlyingCommand l} E_ACTION_MAP {DoUnderlyingCommand M} E_ACTION_NOTE {DoUnderlyingCommand :} E_ACTION_SHAPE {DoUnderlyingCommand \]} E_ACTION_PETS {DoUnderlyingCommand p} E_ACTION_POWER {DoUnderlyingCommand U} E_ACTION_REPEAT {DoUnderlyingCommand n} E_ACTION_REST {DoUnderlyingCommand R} E_ACTION_RUN {DoUnderlyingCommand .} E_ACTION_SEARCH {DoUnderlyingCommand s} E_ACTION_SEARCH_MODE {DoUnderlyingCommand S} E_ACTION_STAY {DoUnderlyingCommand ,} E_ACTION_STAY_TOGGLE {DoUnderlyingCommand g} E_ACTION_TARGET {DoUnderlyingCommand *} E_ACTION_TUNNEL {DoUnderlyingCommand T} E_ACTION_UP {DoUnderlyingCommand <} E_ACTION_WALK {DoUnderlyingCommand ";"} E_ACTION_WALK_TOGGLE {DoUnderlyingCommand -} E_PREF_FONT { NSModule::LoadIfNeeded NSFont NSWindowManager::Display font } E_PREF_OPTIONS {DoUnderlyingCommand =} E_OTHER_INFO {DoUnderlyingCommand C} E_OTHER_FEELING {DoUnderlyingCommand ^F} E_OTHER_KNOWLEDGE {DoUnderlyingCommand ~} E_OTHER_MESSAGES {DoUnderlyingCommand ^p} E_OTHER_QUEST {DoUnderlyingCommand ^Q} E_OTHER_TIME {DoUnderlyingCommand ^T} E_WINDOW_DEFPOS { set title "dialog-title-defpos" set message "dialog-msg-defpos" set answer [tk_messageBox -parent [Info $oop win] -type yesno \ -icon question -title $title -message $message] if {[string equal $answer yes]} { HardcodeGeometry } } E_WINDOW_MAXIMIZE { set title "dialog-title-max" set message "dialog-msg-max" set answer [tk_messageBox -parent [Info $oop win] -type yesno \ -icon question -title $title -message $message] if {[string equal $answer yes]} { MaximizeWindows } } E_WINDOW_SAVEPOS {WriteGeometryFile} E_WINDOW_LOADPOS { set title "dialog-title-loadpos" if {![file exists [PathTk config geometry]]} { set message "dialog-msg-loadpos-fail" tk_messageBox -parent [Info $oop win] \ -title $title -message $message return } set message "dialog-msg-loadpos" set answer [tk_messageBox -parent [Info $oop win] -type yesno \ -icon question -title $title -message $message] if {[string equal $answer yes]} { ReadGeometryFile } } E_WINDOW_AUTOSAVE { Value window,autosave [Info $oop window,autosave] } E_CHOICEWINDOW { if {[Info $oop choiceWindow]} { NSModule::LoadIfNeeded NSChoiceWindow NSWindowManager::Display choice } else { NSWindowManager::Undisplay choice } } E_WINDOW_MESSAGES { if {[Info $oop messagesWindow]} { NSModule::LoadIfNeeded NSMessageWindow NSWindowManager::Display message2 } else { NSWindowManager::Undisplay message2 } } E_WINDOW_MESSAGE { if {[Info $oop messageWindow]} { wm deiconify [Window message] grid remove [Window main].message Global message,message [Window message].message } else { wm withdraw [Window message] grid [Window main].message Global message,message [Window main].message.message } Value message,float [Info $oop messageWindow] } E_WINDOW_MISC { if {[Info $oop miscWindow]} { wm deiconify [Window misc] grid remove [Window main].misc Global misc,canvas [Window misc].misc if {[Value misc,layout] == "wide"} { wm deiconify [Window progress] } } else { Value misc,layout tall wm withdraw [Window misc] wm withdraw [Window progress] grid [Window main].misc Global misc,canvas [Window main].misc.misc } Value misc,float [Info $oop miscWindow] if {[Value misc,layout] == "wide"} { NSMiscWindow::MiscArrangeWide } else { NSMiscWindow::MiscArrangeTall } } E_WINDOW_RECALL { if {[Info $oop recallWindow]} { NSWindowManager::Display recall } else { NSWindowManager::Undisplay recall } } E_HELP {DoUnderlyingCommand ?} E_TIPS { NSModule::LoadIfNeeded NSTips WindowBringToFront [Window tip] } E_ABOUT {AboutApplication} default { error "unhandled menu entry \"$ident\"" } } return } # NSMainWindow::Close -- # # Called when the user attempts to close the window. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::Close {oop} { global Angband # Check if game is waiting for a command. If not, it isn't a # good time to quit. if {[string compare [angband inkey_flags] INKEY_CMD]} { bell return } # Ask the user to confirm quit with save set answer [tk_messageBox -icon question -type yesno \ -title [format "dialog-title-quit" "ZAngband"] \ -message "dialog-msg-quit"] if {[string equal $answer no]} return # Save and quit DoCommandIfAllowed ^x return } # NSMainWindow::Configure -- # # Called when the Main Window widget changes size. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::Configure {oop width height} { set widgetId [Global main,widgetId] set widget [Global main,widget] NSWidget::Resize $widgetId $width $height return } # NSMainWindow::ValueChanged_font_statusBar -- # # Called when the font,statusBar value changes. # Updates the Main Window statusbar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMainWindow::ValueChanged_font_statusBar {} { set statusBar [Window main].statusBar # Get the desired font set font [Value font,statusBar] # Update the font. Too bad there isn't a -fontvar font variable $statusBar.frameLabel.label configure -font $font $statusBar.center configure -font $font $statusBar.depth configure -font $font return } # NSMainWindow::SynchMenuAccel -- # # Sets the accelerator option for certain menu entries depending on # the current keymap. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::SynchMenuAccel {oop force} { global NSMenu variable Priv # Since many keymaps may change when a pref file is read in, delay # configuring the menu accelerators until idle time. if {!$force} { if {![string length $Priv(keymap,afterId)]} { set Priv(keymap,afterId) \ [after idle NSMainWindow::SynchMenuAccel $oop 1] } # The idle task was scheduled by a previous call, but this # call isn't from the idle task. return } # Important: clear the after id. set Priv(keymap,afterId) "" set mbarId [Info $oop mbarId] lappend data E_GAME_SAVE ^S lappend data E_GAME_EXIT ^X lappend data E_MAGIC_ACTIVATE A lappend data E_MAGIC_WAND a lappend data E_MAGIC_POTION q lappend data E_MAGIC_SCROLL r lappend data E_MAGIC_STAFF u lappend data E_MAGIC_ROD z lappend data E_MAGIC_BROWSE b lappend data E_MAGIC_STUDY G lappend data E_USE_DESTROY k lappend data E_USE_DROP d lappend data E_USE_PICKUP g lappend data E_USE_TAKEOFF t lappend data E_USE_WIELD w lappend data E_USE_FOOD E lappend data E_USE_MISSILE f lappend data E_USE_FUEL F lappend data E_USE_SPIKE j lappend data E_USE_THROW v lappend data E_INVEN_EQUIPMENT e lappend data E_INVEN_INVENTORY i lappend data E_INVEN_INSPECT I lappend data E_INVEN_INSCRIBE \{ lappend data E_INVEN_UNINSCRIBE \} lappend data E_ACTION_ALTER + lappend data E_ACTION_BASH B lappend data E_ACTION_CLOSE c lappend data E_ACTION_DISARM D lappend data E_ACTION_DOWN > lappend data E_ACTION_LOOK l lappend data E_ACTION_MAP M lappend data E_ACTION_NOTE : lappend data E_ACTION_OPEN o lappend data E_ACTION_REPEAT n lappend data E_ACTION_REST R lappend data E_ACTION_RUN . lappend data E_ACTION_SEARCH s lappend data E_ACTION_SEARCH_MODE S lappend data E_ACTION_STAY , lappend data E_ACTION_STAY_TOGGLE g lappend data E_ACTION_TARGET * lappend data E_ACTION_TUNNEL T lappend data E_ACTION_UP < lappend data E_ACTION_WALK ";" lappend data E_ACTION_WALK_TOGGLE - lappend data E_ACTION_PETS p lappend data E_ACTION_POWER U lappend data E_HELP ? lappend data E_OTHER_FEELING ^F lappend data E_OTHER_INFO C lappend data E_OTHER_KNOWLEDGE ~ lappend data E_OTHER_MESSAGES ^P lappend data E_OTHER_QUEST ^Q lappend data E_OTHER_TIME ^T lappend data E_PREF_OPTIONS = foreach {ident key} $data { set entry [NSMenu::MenuFindEntry $mbarId $ident] if {$::DEBUG && ![llength $entry]} { error "can't find menu identifier \"$ident\"" } set menuId [lindex $entry 0] set index [lindex $entry 1] set menu $NSMenu($menuId,menu) if 0 { set string [angband keymap find $key] regsub {\^} $string Ctrl+ string $menu entryconfigure $index -accelerator $string } } return } # NSMainWindow::MouseCmd -- # # Use to execute commands when a mouse button is pressed. The direction # is determined from the given widget coordinates. # Calls "angband keypress CMD DIR". # # Arguments: # oop OOP ID of NSMainWindow object. # x x coordinate in Widget (as returned by event) # y y coordinate in Widget (as returned by event) # cmd Command to invoke. # # Results: # What happened. proc NSMainWindow::MouseCommand {oop x y cmd} { set widgetId [Global main,widgetId] set coords [NSWidget::PointToCave $widgetId $x $y] scan $coords "%d %d" caveY caveX set dirInfo [CaveToDirection $caveY $caveX] set charDir [lindex $dirInfo 0] if {$charDir != 5} { angband keypress \\$cmd$charDir } return } # NSMainWindow::TrackPress -- # # Set up mouse tracking when occurs. See TrackMotion() # and TrackOnce() as well. # # Arguments: # oop OOP ID of NSMainWindow object. # x x coordinate in Widget (as returned by event) # y y coordinate in Widget (as returned by event) # # Results: # What happened. proc NSMainWindow::TrackPress {oop x y} { variable tracking variable track1st variable trackStepping variable trackX variable trackY set tracking 1 set track1st 1 set trackX $x set trackY $y # Hack -- Allow drag during targetting if {[string equal [angband inkey_flags] INKEY_TARGET]} { NSWidget::TrackPress [Global main,widgetId] $x $y return } scan [angband player hitpoints] "%d %d" curhp maxhp TrackOnce $oop set track1st 0 set trackStepping 1 after 200 set NSMainWindow::trackStepping 0 return } # NSMainWindow::TrackMotion -- # # Called to remember the cursor position when occurs. # See TrackOnce() below as well. # # Arguments: # oop OOP ID of NSMainWindow object. # x x coordinate in Widget (as returned by event) # y y coordinate in Widget (as returned by event) # # Results: # What happened. proc NSMainWindow::TrackMotion {oop x y} { variable trackX variable trackY # Hack -- Allow drag during targetting if {[string equal [angband inkey_flags] INKEY_TARGET]} { NSWidget::TrackOnce [Global main,widgetId] $x $y return } set trackX $x set trackY $y return } # NSMainWindow::TrackOnce -- # # This command examines the result of "angband inkey_flags" and # takes some action depending on the value. During INKEY_MORE and # INKEY_DISTURB it calls "angband keypress" with a single space # character. During INKEY_DIR it calls "angband keypress" with the # corresponding direction character (0-9). # # During INKEY_CMD it calls "angband keypress" with a direction # key (to move the character). # # This command is usually called when the binding is invoked, # but if the character is unable to move it calls itself again as # an "after" command. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::TrackOnce {oop} { variable tracking variable track1st variable trackX variable trackY variable trackId variable trackStepping # If the mouse isn't down, then do nothing. This command gets # called whenever the event is generated. if {!$tracking} return if 0 { # Hack -- Allow drag during targetting if {[string equal [angband inkey_flags] INKEY_TARGET]} { NSWidget::TrackOnce [Global main,widgetId] $trackX $trackY return } } # It is important to delay after taking the first step, otherwise # the character won't be able to navigate cleanly, and -more- # messages may go zipping by. if {$trackStepping} { set trackId [after 1 NSMainWindow::TrackOnce $oop] return } # (1) Walking into a door with always_repeat # (2) Walking through rubble/tree (OAngband) if {!$track1st && [angband player command_rep]} return # Get the inkey_flags set flags [angband inkey_flags] # If the game is displaying the "-more-" message, feed the Term # with a single space character. This only works if the "quick_messages" # option is set. if {[string equal $flags INKEY_MORE]} { angband keypress " " return } # If a repeated command is in progress, a mouse-click will disturb if {[string equal $flags INKEY_DISTURB]} { angband keypress " " return } set widgetId [Global main,widgetId] set widget [Global main,widget] set coords [NSWidget::PointToCave $widgetId $trackX $trackY] if {![string length $coords]} { set trackId [after 1 NSMainWindow::TrackOnce $oop] return } scan $coords "%d %d" caveY caveX set dirInfo [CaveToDirection $caveY $caveX] set dirKey [lindex $dirInfo 0] set y [lindex $dirInfo 1] set x [lindex $dirInfo 2] # If the game is waiting for the user to enter a direction, then # feed the direction key into the Term. if {[string equal $flags INKEY_DIR]} { angband keypress $dirKey return } # If the game is NOT asking for a command, then do nothing if {[string compare $flags INKEY_CMD]} { return } # If the mouse is over the player grid, only move if this is # the initial mouse click. Otherwise the user may accidentally # "run on the spot". if {!$track1st && ($dirKey == 5)} { set trackId [after 10 NSMainWindow::TrackOnce $oop] return } # If the spacebar is down, we may get any number of Inkey # events per turn. To prevent "mouse command overflow" we # never feed the Term with more than one key per turn. if {[angband keycount]} return # Move the character angband keypress $dirKey return } # NSMainWindow::TrackRelease -- # # Cancels mouse tracking when the mouse button is released. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::TrackRelease {oop} { variable trackId variable tracking variable trackStepping variable trackX variable trackY # One time I selected a menu command and received an error after releasing # the mouse-button if {!$tracking} return set tracking 0 set trackStepping 0 after cancel $trackId # If the Widget wasn't dragged, then tell the game to target if {[string equal [angband inkey_flags] INKEY_TARGET]} { set widgetId [Global main,widgetId] if {![NSWidget::Info $widgetId track,mouseMoved]} { set coords [NSWidget::PointToCave $widgetId $trackX $trackY] scan $coords "%d %d" caveY caveX angband keypress @$caveY\n$caveX\n return } } return } # NSMainWindow::Leave -- # # Handle the mouse leaving the Widget. Called as NSWidget(OOP,leaveCmd). # # Arguments: # oop OOP ID NSWidget. # # Results: # What happened. proc NSMainWindow::Leave {oop} { # Unused: PROJECT_HINT if {0 && [string equal [angband inkey_flags] INKEY_TARGET]} { # Show target grids at the cursor set y [Global cursor,y] set x [Global cursor,x] angband keypress &$y\n$x\n return } # Clear the statusbar prompt StatusText $oop "" return } # NSMainWindow::CaveToDirection -- # # Given cave location y,x, determine the direction key relative # to the player location. # # Arguments: # y y cave location. # x x cave location. # # Results: # Return "dir y x", where dir is key to move, y/x is adjacent cave location # character would move to. proc NSMainWindow::CaveToDirection {y x} { global PYPX scan $PYPX "%d %d" py px if {$y < $py} { set yyy 789 incr py -1 } elseif {$y > $py} { set yyy 123 incr py } else { set yyy 456 } if {$x < $px} { set dirKey [string index $yyy 0] incr px -1 } elseif {$x > $px} { set dirKey [string index $yyy 2] incr px } else { set dirKey [string index $yyy 1] } return "$dirKey $py $px" } # NSMainWindow::StatusText -- # # Displays text in the status bar. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMainWindow::StatusText {oop text} { set label [Global main,statusBar] if {[string compare $text [$label cget -text]]} { $label configure -text $text } return } # NSMainWindow::DisplayDepth -- # # Displays the dungeon level in the Main Window's status bar. # # Arguments: # label The label widget to display the depth in. # depth Current depth. # # Results: # What happened. proc NSMainWindow::DisplayDepth {label depth} { if {$depth == 0} { set depthStr [angband cave wild_name] } else { set depthStr [format "Level %d" $depth] } $label configure -text $depthStr return } # NSMainWindow::ButtonPress3 -- # # Do something when Button 3 is pressed in the main widget. # # Arguments: # oop OOP ID of NSMainWindow object. # x y Coords in Widget (as returned by event). # X Y Global coords (as returned by event). # # Results: # What happened. proc NSMainWindow::ButtonPress3 {oop x y X Y} { set win [Info $oop win] set flags [angband inkey_flags] # Run if {[string equal $flags INKEY_CMD]} { MouseCommand $oop $x $y . # Set target } elseif {[string equal $flags INKEY_DIR]} { scan [NSWidget::PointToCave [Global main,widgetId] $x $y] "%d %d" y2 x2 angband keypress *@$y2\n$x2\n } return } # NSMainWindow::SelectWindow -- # # Make a window the frontmost active window. # # Arguments: # window Index into Windows[] (inventory, book, etc) # # Results: # What happened. proc NSMainWindow::SelectWindow {window} { if {[info exists NSWindowManager::Priv($window,win)]} { NSWindowManager::Display $window return } WindowBringToFront [Window $window] return } # NSMainWindow::WithdrawWindow -- # # Withdraw a window. # # Arguments: # window Index into Windows[] (inventory, book, etc) # # Results: # What happened. proc NSMainWindow::WithdrawWindow {window} { wm withdraw [Window $window] return } # NSMainWindow::Display -- # # Remove current window (if any), and select given window. # # Arguments: # window Index into Windows[] (inventory, book, etc) # # Results: # What happened. proc NSMainWindow::Display {window} { global Display if {[string compare $Display(window) none] && [string compare $Display(window) $window]} { WithdrawWindow $Display(window) } SelectWindow $window set Display(window) $window return } # NSMainWindow::PositionChanged -- # # Called as a qebind script. Update the Main Window # when the character's position changes. Handles the "disturb_panel" option. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMainWindow::PositionChanged {widget y x} { global PYPX # Keep character centered in the display $widget center $y $x Global main,widget,center "$y $x" # This global is read in various places set PYPX "$y $x" return } # FlashCanvasText -- # # Configure the fill color of a canvas item, then do it again later. # # Arguments: # canvas Canvas widget the item is in. # tagOrId The canvas item ID to manipulate. # color The fill color. # num Number of times to flash it. # # Results: # What happened. global FlashCanvas proc FlashCanvasTextAux {canvas tagOrId} { global FlashCanvas set num $FlashCanvas($canvas,$tagOrId,num) if {$num & 1} { set fill $FlashCanvas($canvas,$tagOrId,colorOff) } else { set fill $FlashCanvas($canvas,$tagOrId,colorOn) } $canvas itemconfigure $tagOrId -fill $fill incr num -1 set FlashCanvas($canvas,$tagOrId,num) $num if {$num} { set id [after 250 "FlashCanvasTextAux $canvas $tagOrId"] set FlashCanvas($canvas,$tagOrId,afterId) $id } else { unset FlashCanvas($canvas,$tagOrId,afterId) } return } proc FlashCanvasText {canvas tagOrId colorOn colorOff num} { global FlashCanvas # Never set more than one "after" command for an item if {[info exists FlashCanvas($canvas,$tagOrId,afterId)]} { set id $FlashCanvas($canvas,$tagOrId,afterId) after cancel $id } set FlashCanvas($canvas,$tagOrId,colorOn) $colorOn set FlashCanvas($canvas,$tagOrId,colorOff) $colorOff set FlashCanvas($canvas,$tagOrId,num) $num FlashCanvasTextAux $canvas $tagOrId return } # FlashCanvasTextFill -- # # Returns the fill color for an canvas item. This routine should be # called if a canvas item may be "flashing". # # Arguments: # arg1 about arg1 # # Results: # What happened. proc FlashCanvasTextFill {canvas tagOrId} { global FlashCanvas if {[info exists FlashCanvas($canvas,$tagOrId,afterId)]} { return $FlashCanvas($canvas,$tagOrId,colorOff) } else { return [$canvas itemcget $tagOrId -fill] } } # DoCommandIfAllowed -- # # Feeds a string of bytes to the Term, but only if INKEY_CMD is set. # # Arguments: # string String argument to "angband keypress" # # Results: # What happened. proc DoCommandIfAllowed {string} { # Check if game is waiting for a command if {[string compare [angband inkey_flags] INKEY_CMD]} return # Feed the Term angband keypress $string return } # DoUnderlyingCommand -- # # Feeds the string to "angband keypress", but prepends a slash # to bypass keymaps. This only works if request_command() is being # called to handle the \ escape character. INKEY_CMD is actually set # when examining the inventory or equipment, and when browsing a book, # in which case this cannot be used. # # Arguments: # string String argument to "angband keypress" # # Results: # What happened. proc DoUnderlyingCommand {string} { # Check if game is waiting for a command if {[string compare [angband inkey_flags] INKEY_CMD]} return # Feed the Term angband keypress \\$string return } # DoKeymapCmd -- # # Maps the given command char to the underlying command and calls # "angband keypress" with it. Some command chars can be represented # by the X11 keysym. # # Arguments: # prefix Misc characters to prepend to command char # command The underlying command char # suffix Misc characters to append to command char # # Results: # What happened. proc DoKeymapCmd {prefix command suffix} { switch -- $command { backslash {set command \\} braceleft {set command \{} braceright {set command \}} bracketleft {set command \[} bracketright {set command \]} quotedbl {set command \"} } #set command [angband keymap find $command] angband keypress $prefix$command$suffix return } # Note: Setting a delay of 0 results in running after the mouse is # released; setting a delay of 1 or more prevents this proc ConfigureMouse {} { set win .mouse toplevel $win wm title $win "Mouse Settings" set scale $win.speed scale $scale \ -orient horizontal -label "Tracking Delay" \ -width 15 -sliderlength 20 -length 200 -from 0 -to 200 \ -command "set ::trackDelay" $scale set $::trackDelay pack $scale set clicks [clock clicks] set text [time {after 1} 100] set diff [expr {[clock clicks] - $clicks}] Debug $text Debug "1 ms = [expr {$diff / 100}] clicks" return } proc TestRedrawSpeed {} { set widget [Global main,widget] set clicks [clock clicks] set text [time {$widget wipe ; update idletasks} 100] set diff [expr {[clock clicks] - $clicks}] Debug "TestRedrawSpeed: 100 redraws in $diff clicks" return } # NSMainWindow::ContextMenu_StatusBar -- # # Pop up a context menu in the StatusBar to configure it's # appearance. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMainWindow::ContextMenu_StatusBar {menu x y} { $menu delete 0 end $menu add command -label "Set Font" \ -command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font statusBar" $menu add command -label "Set Color" \ -command { set color [tk_chooseColor -parent [Window main] \ -initialcolor [Value main,statusbar,color]] if {$color != ""} { Value main,statusbar,color $color [Global main,statusBar] configure -foreground $color } } $menu add command -label "Set Autobar Font" \ -command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font autobar" $menu add separator $menu add command -label "Cancel" tk_popup $menu $x $y return } zangband/lib/script/tk/map.tcl0000644000000000000000000001371710250356274015326 0ustar rootroot# File: map.tcl # Purpose: a Widget plus two scrollbars # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMap { # namespace eval NSMap } # NSMap::InitModule -- # # One-time-only-ever initialization. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMap::InitModule {} { NSModule::LoadIfNeeded NSWidget return } # NSMap::NSMap -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMap::NSMap {oop parent width height gwidth gheight} { set frame $parent.map$oop # Frame + widget + scrollbars frame $frame \ -borderwidth 0 set widgetId [NSObject::New NSWidget $frame $width $height \ $gwidth $gheight] set widget [NSWidget::Info $widgetId widget] NSWidget::Info $widgetId scaleCmd "NSMap::ScaleCmd $oop" scrollbar $frame.xscroll \ -orient horizontal -command "NSWidget::xview $widgetId" scrollbar $frame.yscroll \ -orient vertical -command "NSWidget::yview $widgetId" # Synch the scrollbars when the Widget scrolls NSWidget::Info $widgetId xviewCmd "NSMap::SynchScrollBars $oop" NSWidget::Info $widgetId yviewCmd "NSMap::SynchScrollBars $oop" grid rowconfig $frame 0 -weight 1 -minsize 0 grid rowconfig $frame 1 -weight 0 -minsize 0 grid columnconfig $frame 0 -weight 1 -minsize 0 grid columnconfig $frame 1 -weight 0 -minsize 0 grid $widget \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $frame.xscroll \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $frame.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns # Resize the widget when it changes size bind $widget \ "NSMap::Configure $oop %w %h" # Set instance variables Info $oop frame $frame Info $oop widgetId $widgetId Info $oop widget $widget Info $oop scaleCmd "" Info $oop viewCmd "" # KeyPress bindings bind $widget \ "NSWidget::xview $widgetId scroll -10 units" bind $widget \ "NSWidget::xview $widgetId scroll 10 units" bind $widget \ "NSWidget::yview $widgetId scroll -10 units" bind $widget \ "NSWidget::yview $widgetId scroll 10 units" bind $widget \ "NSWidget::xview $widgetId moveto 0" bind $widget \ "NSWidget::xview $widgetId moveto 1" bind $widget \ "NSWidget::yview $widgetId moveto 0" bind $widget \ "NSWidget::yview $widgetId moveto 1" # # Synch the scrollbars when window is shown. # bind $frame "NSMap::SynchScrollBars $oop" return } # NSMap::Configure -- # # Called when the frameWidget changes size. # # Arguments: # oop OOP ID of NSMainWindow object. # # Results: # What happened. proc NSMap::Configure {oop width height} { set widgetId [Info $oop widgetId] set widget [Info $oop widget] if {[NSWidget::Resize $widgetId $width $height]} { eval SetView $oop [$widget center] } return } # NSMap::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMap::Info {oop info args} { global NSMap # Verify the object NSObject::CheckObject NSMap $oop # Set info if {[llength $args]} { switch -- $info { default { set NSMap($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSMap($oop,$info) } } } return } # NSMap::ScaleCmd -- # # Called as the scaleCmd of our NSWidget. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMap::ScaleCmd {oop} { set widget [Info $oop widget] eval SetView $oop [$widget center] set command [Info $oop scaleCmd] if {[string length $command]} { uplevel #0 $command } return } # NSMap::SetView -- # # Center the view on the given position. If this means the map # is scrolled "too far", then adjust accordingly. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMap::SetView {oop y x} { set widget [Info $oop widget] scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max set height [expr {$y_max - $y_min + 1}] set width [expr {$x_max - $x_min + 1}] set dunHgt [angband cave height] set dunWid [angband cave width] if {$dunHgt > $height} { incr dunHgt 2 } if {$dunWid > $width} { incr dunWid 2 } set ny [ConstrainCenter $y $dunHgt $height] set nx [ConstrainCenter $x $dunWid $width] if 0 { # Do nothing if position unchanged scan [$widget center] "%d %d" oy ox if {$oy == $ny && $ox == $nx} return } # Center the widget at the given location $widget center $ny $nx # Update the scrollbars SynchScrollBars $oop return } # NSMap::SynchScrollBars -- # # Configures the scrollbars to reflect the current scroll position # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMap::SynchScrollBars {oop} { set frame [Info $oop frame] set widget [Info $oop widget] scan [$widget center] "%d %d" cy cx scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max set height [expr {$y_max - $y_min + 1}] set width [expr {$x_max - $x_min + 1}] set dunHgt [angband cave height] set dunWid [angband cave width] if {$dunHgt > $height} { incr dunHgt 2 incr cy 1 } if {$dunWid > $width} { incr dunWid 2 incr cx 1 } set top [expr {$cy - $height / 2}] if {$top < 0} {set top 0} set bottom [expr {$top + $height}] if {$bottom > $dunHgt} {set bottom $dunHgt} $frame.yscroll set [expr {$top / double($dunHgt)}] \ [expr {$bottom / double($dunHgt)}] set left [expr {$cx - $width / 2}] if {$left < 0} {set left 0} set right [expr {$left + $width}] if {$right > $dunWid} {set right $dunWid} $frame.xscroll set [expr {$left / double($dunWid)}] \ [expr {$right / double($dunWid)}] set command [Info $oop viewCmd] if {[string length $command]} { uplevel #0 $command } return } zangband/lib/script/tk/message-history.tcl0000644000000000000000000003427010250356274017671 0ustar rootroot# File: message-history.tcl # Purpose: the Message History Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMessageHistory { variable MenuString variable Priv # namespace eval NSMessageHistory } # NSMessageHistory::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::InitModule {} { variable Priv set Priv(find,string) "" set Priv(find,index) end # Create the Message History Window NSObject::New NSMessageHistory return } # NSMessageHistory::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::CloseModule {} { catch { destroy [Window messages] } return } # NSMessageHistory::NSMessageHistory -- # # Create a message window. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageHistory::NSMessageHistory {oop} { # Update ourself when the font,messages value changes Info $oop clientId,font,messages \ [NSValueManager::AddClient font,messages \ NSMessageHistory::ValueChanged_Font_Messages] # Option: Combine identical messages Info $oop combine 0 InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow messages $win \ "GetDefaultGeometry $win main2 main" "" \ "NSMessageHistory::DisplayCmd $oop" bind $win "NSMessageHistory::Find $oop 0" bind $win "NSMessageHistory::Find $oop 1" bind $win "NSMessageHistory::Close $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSMessageHistory $oop $win # # Global list of application windows # Global messages,oop $oop Window messages $win return } # NSMessageHistory::~NSMessageHistory -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::~NSMessageHistory {oop} { NSValueManager::RemoveClient listBG [Info $oop clientId,listBG] NSValueManager::RemoveClient font,messages [Info $oop clientId,font,messages] return } # NSMessageHistory::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::Info {oop info args} { global NSMessageHistory # Verify the object NSObject::CheckObject NSMessageHistory $oop # Set info if {[llength $args]} { switch -- $info { default { set NSMessageHistory($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSMessageHistory($oop,$info) } } } return } # NSMessageHistory::InitWindow -- # # Create the window associated with the object. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageHistory::InitWindow {oop} { set win .messages$oop toplevel $win wm title $win "Message History" wm transient $win [Window main] # Start out withdrawn (hidden) wm withdraw $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSMessageHistory::Close $oop" # Set instance variables Info $oop win $win InitMenus $oop # Divider MakeDivider $win.divider1 x set frame $win.frameList frame $frame \ -relief sunken -borderwidth 1 set font [Value font,messages] set width 60 set height 10 set canvistId [NSObject::New NSTexist $frame $font $width $height] set canvas [NSTexist::Info $canvistId text] $canvas configure -background [Value listBG] $canvas configure -yscrollcommand "$frame.yscroll set" $canvas configure -xscrollcommand "$frame.xscroll set" scrollbar $frame.yscroll \ -orient vertical -command "$canvas yview" scrollbar $frame.xscroll \ -orient horizontal -command "$canvas xview" NSTexist::Info $canvistId selectionCmd \ "NSMessageHistory::SelectionChanged $oop" # This call updates the list background color whenever the # global list background color changes Info $oop clientId,listBG \ [NSValueManager::AddClient listBG \ "$canvas configure -background \[Value listBG]"] Info $oop canvistId $canvistId # # Statusbar # MakeStatusBar $win.statusBar 20 # # Geometry # grid rowconfig $win.frameList 0 -weight 1 grid rowconfig $win.frameList 1 -weight 0 grid columnconfig $win.frameList 0 -weight 1 grid columnconfig $win.frameList 1 -weight 0 grid $canvas \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $frame.yscroll \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns grid $frame.xscroll \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid rowconfigure $win 0 -weight 0 grid rowconfigure $win 1 -weight 1 grid rowconfigure $win 2 -weight 0 grid columnconfig $win 0 -weight 1 if {[Platform windows]} { grid $win.divider1 \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew -pady 2 } grid $win.frameList \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew # # Feed Term when keys pressed # bind $win { angband keypress %A } # Synch the scrollbars when window is shown. NSUtils::SynchScrollBar $canvas $win.frameList.yscroll NSUtils::SynchScrollBar $canvas $win.frameList.xscroll return } # NSMessageHistory::InitMenus -- # # Create the menus associated with the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::InitMenus {oop} { variable MenuString set win [Info $oop win] set mod "Ctrl" # # Menu bar # set mbarId [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSMessageHistory::SetupMenus $oop" -identifier MENUBAR] Info $oop mbarId $mbarId # Context-sensitive help NSMenu::Info $mbarId menuSelectCmd "NSMessageHistory::MenuSelect $oop" # Call our command when an entry is invoked NSMenu::Info $mbarId invokeCmd "NSMessageHistory::MenuInvoke $oop" # # Message Menu # NSObject::New NSMenu $mbarId -tearoff 0 -identifier MENU_MESSAGE NSMenu::MenuInsertEntry $mbarId -end MENUBAR -type cascade \ -menu MENU_MESSAGE -label "Message" -underline 0 \ -identifier M_MESSAGE set entries {} lappend entries [list -type command -label "Dump Messages" \ -underline 0 -identifier E_DUMP] lappend entries [list -type separator] lappend entries [list -type command -label "Find..." \ -accelerator f -underline 0 -identifier E_FIND] lappend entries [list -type command -label "Find Again" \ -accelerator g -underline 6 -identifier E_FIND_AGAIN] lappend entries [list -type separator] lappend entries [list -type command -label "Max Messages..." \ -underline 0 -identifier E_MAX] lappend entries [list -type separator] lappend entries [list -type command -label "Close" \ -underline 0 -accelerator $mod+W -identifier E_CLOSE] NSMenu::MenuInsertEntries $mbarId -end MENU_MESSAGE $entries set MenuString(M_MESSAGE) \ "Contains commands for displaying and searching messages." set MenuString(E_DUMP) \ "Writes all the messages to a text file." set MenuString(E_FIND) \ "Searches for a message containing a given string." set MenuString(E_FIND_AGAIN) \ "Repeats the previous search." set MenuString(E_MAX) \ "Changes the number of messages displayed." set MenuString(E_CLOSE) \ "Closes the window." return } # NSMessageHistory::SetupMenus -- # # Prepare to post the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::SetupMenus {oop mbarId} { variable Priv lappend identList E_DUMP E_COMBINE E_MAX E_FIND E_FIND_AGAIN E_CLOSE NSMenu::MenuEnable $mbarId $identList [Info $oop win].statusBar cover show return } # NSMessageHistory::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::MenuSelect {oop menuId index ident} { variable MenuString switch -- $ident { {} { set desc {} } E_COMBINE { if {[Value messages,combine]} { set desc "Displays each message separately." } else { set desc "Combines identical sequential messages into a single line." } } default { if {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop win].statusBar cover set $desc if {[string length $desc]} { } else { if {$menuId == [Info $oop mbarId]} { [Info $oop win].statusBar cover hide } } return } # NSMessageHistory::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::MenuInvoke {oop menuId ident} { set win [Info $oop win] switch -glob -- $ident { E_DUMP {MessageDump $win} E_FIND {Find $oop 0} E_FIND_AGAIN {Find $oop 1} E_COMBINE {CombineMessages $oop} E_MAX {MaxMessages $oop} E_CLOSE {Close $oop} } return } # NSMessageHistory::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::DisplayCmd {oop message first} { global Angband switch -- $message { preDisplay { SetList $oop } postDisplay { } } return } # NSMessageHistory::Close -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageHistory::Close {oop} { angband keypress \033 return } # NSMessageHistory::SetList -- # # Display a list of recent messages in the Message Window. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageHistory::SetList {oop} { set canvistId [Info $oop canvistId] set canvas [NSTexist::Info $canvistId text] set max [angband message count] if {$max > [Value messages,max]} { set max [Value messages,max] } set age {} # Option: Combine identical messages (ex "You hit it. (x3)") if {[Info $oop combine]} { set curMsg "" set count 0 for {set i [expr {$max - 1}]} {$i >= 0} {incr i -1} { set nextMsg [angband message get $i] if {[string compare $curMsg $nextMsg]} { if {[string length $curMsg]} { set curMsg " $curMsg" if {$count > 1} { append curMsg " (x$count)" } lappend textList $curMsg lappend colorList [Value [angband message color $curAge]] lappend age $curAge } set curMsg $nextMsg set curAge $i set count 1 } else { incr count } } if {$count > 1} { append curMsg " (x$count)" } set curMsg " $curMsg" lappend textList $curMsg lappend colorList [Value [angband message color $curAge]] lappend age $curAge # Don't combine identical messages } else { for {set i [expr {$max - 1}]} {$i >= 0} {incr i -1} { set curMsg [angband message get $i] set curMsg " $curMsg" lappend textList $curMsg lappend colorList [Value [angband message color $i]] lappend age $i } } Info $oop age $age set max [llength $textList] NSTexist::SetList $canvistId $textList $colorList set row [expr {$max - 1}] NSTexist::See $canvistId $row NSTexist::UpdateSelection $canvistId $row {} focus $canvas return } # NSMessageHistory::StatusBar -- # # Display text in the status bar, perhaps clearing it later. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::StatusBar {oop text zap} { set win [Info $oop win] set label [$win.statusBar itemcget t1 -label] $label configure -text $text if {$zap} { NSUtils::ZapLabel $label } return } # NSMessageHistory::Find -- # # Simple search routine to look for a message. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::Find {oop again} { variable Priv set canvistId [Info $oop canvistId] set text [NSTexist::Info $canvistId text] # Repeat the last search if {$again && [string length $Priv(find,string)]} { set string $Priv(find,string) # Enter a string to find, start from the beginning } else { # Ask the user for a string set string [NSUtils::StringBox -title "Find" \ -initial $Priv(find,string) -prompt "find-prompt" \ -buttons [list "Find" "Cancel"] -parent [Info $oop win]] # User cancelled if {![string length $string]} return # Search again set Priv(find,string) $string set Priv(find,index) end } # Search for the string set Priv(find,index) [$text search -backwards -nocase \ -- $Priv(find,string) $Priv(find,index)] # The string wasn't found if {![string length $Priv(find,index)]} { # Search from the end set Priv(find,index) end # Done return } scan $Priv(find,index) "%d.%d" line char incr line -1 NSTexist::UpdateSelection $canvistId $line all NSTexist::See $canvistId $line return } # NSMessageHistory::MaxMessages -- # # Lets the user enter the maximum number of messages to be displayed # in the Message History. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::MaxMessages {oop} { # Ask the user for a number set string [NSUtils::StringBox -title "max-title" \ -initial [Value messages,max] -prompt "max-prompt" \ -buttons [list "OK" "Cancel"] -parent [Info $oop win] \ -entrywidth 5 -type integer -message "max-message"] if {![string length $string]} return if {![scan $string "%d" max]} return if {$max < 1 || $max > 2048} return Value messages,max $max # Redisplay StatusBar $oop "Displaying..." 0 update idletasks SetList $oop StatusBar $oop "Done." 1 return } # NSMessageHistory::CombineMessages -- # # Called when the Combine menu entry is toggled. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::CombineMessages {oop} { Value messages,combine [Info $oop combine] # Redisplay StatusBar $oop "Displaying..." 0 update idletasks SetList $oop StatusBar $oop "Done." 1 return } # NSMessageHistory::ValueChanged_Font_Messages -- # # Called when the font,messages value changes. # Updates the Message History Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageHistory::ValueChanged_Font_Messages {} { set texistId [NSMessageHistory::Info [Global messages,oop] canvistId] [NSTexist::Info $texistId text] configure -font [Value font,messages] return } zangband/lib/script/tk/message-window.tcl0000644000000000000000000001445010250356274017475 0ustar rootroot# File: message-window.tcl # Purpose: the Messages Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMessageWindow { variable Priv # namespace eval NSMessageWindow } # NSMessageWindow::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::InitModule {} { NSObject::New NSMessageWindow return } # NSMessageWindow::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::CloseModule {} { catch { destroy [Window message2] } return } # NSMessageWindow::NSMessageWindow -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageWindow::NSMessageWindow {oop} { InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow message2 $win \ "NSMessageWindow::GeometryCmd $oop" "" \ "NSMessageWindow::DisplayCmd $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSMessageWindow $oop $win # Update ourself when the font,messages value changes Info $oop clientId,font,messages \ [NSValueManager::AddClient font,messages \ "NSMessageWindow::ValueChanged_Font_Messages $oop"] Info $oop clientId,listBG \ [NSValueManager::AddClient listBG \ "[Info $oop text] configure -background \[Value listBG]"] # # Global list of application windows # Global message2,oop $oop Window message2 $win return } # NSMessageWindow::~NSMessageWindow -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::~NSMessageWindow {oop} { NSValueManager::RemoveClient listBG [Info $oop clientId,listBG] NSValueManager::RemoveClient font,messages [Info $oop clientId,font,messages] return } # NSMessageWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::Info {oop info args} { global NSMessageWindow # Set info if {[llength $args]} { switch -- $info { default { set NSMessageWindow($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSMessageWindow($oop,$info) } } } return } # NSMessageWindow::InitWindow -- # # Create the window. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageWindow::InitWindow {oop} { set win .message$oop toplevel $win wm title $win "Messages" wm transient $win [Window main] # Feed the Term when keys are pressed Term_KeyPress_Bind $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSMessageWindow::Close $oop" # Start out withdrawn (hidden) wm withdraw $win # Set instance variables Info $oop win $win set frame $win.frame frame $frame \ -borderwidth 0 set font [Value font,messages] set width 60 set height 5 set text $frame.text text $text \ -borderwidth 0 -highlightthickness 0 -font $font -width 50 -height 5 \ -background [Value listBG] -foreground White -wrap none \ -cursor {} bindtags $text [list $text $win all] pack $text -expand yes -fill both pack $frame -expand yes -fill both Info $oop text $text bind $text \ "NSMessageWindow::Configure $oop %h %w" bind $text { DoUnderlyingCommand ^p } # Support for colored messages foreach attr [Global term_attr] { $text tag configure $attr -foreground [Value $attr] } return } # NSMessageWindow::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::DisplayCmd {oop message first args} { switch -- $message { preDisplay { } postDisplay { Value message2window,show 1 } postWithdraw { Value message2window,show 0 } } return } # NSMessageWindow::GeometryCmd -- # # Called by NSWindowManager::Setup(). Returns the desired (default) # geometry for the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::GeometryCmd {oop} { set win [Info $oop win] set left 0 set top 0 set right [winfo screenwidth .] set bottom [winfo screenheight .] set width [winfo width $win] set height [winfo height $win] set x [expr {$right - [NSToplevel::TotalWidth $win]}] if {[winfo x [Window main]] >= [winfo screenwidth .]} { incr x [winfo screenwidth .] } set y $top return ${width}x$height+$x+$y } # NSMessageWindow::Close -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageWindow::Close {oop} { NSWindowManager::Undisplay message2 return } # NSMessageWindow::TrackMessage -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageWindow::TrackMessage {oop} { set text [Info $oop text] $text delete 1.0 end # Get the number of messages set max [angband message count] # Limit to number of displayed messages if {$max > [Info $oop numRows]} { set max [Info $oop numRows] } incr max -1 # Collect each message for {set i $max} {$i >= 0} {incr i -1} { lappend info [angband message get $i] [angband message color $i] \n {} } # Display eval $text insert end $info return } # NSMessageWindow::Configure -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMessageWindow::Configure {oop height width} { set text [Info $oop text] # set height [winfo height $text] incr height [expr {[$text cget -pady] * -2}] set rowHeight [font metrics [Value font,messages] -linespace] set numRows [expr {$height / $rowHeight}] Info $oop numRows $numRows TrackMessage $oop return } # NSMessageWindow::ValueChanged_Font_Messages -- # # Called when the font,messages value changes. # Updates the Message Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMessageWindow::ValueChanged_Font_Messages {oop} { set text [Info $oop text] $text configure -font [Value font,messages] Configure $oop [winfo height $text] [winfo width $text] return } zangband/lib/script/tk/misc-popup.tcl0000644000000000000000000003473510250356274016650 0ustar rootroot# File: misc-popup.tcl # Purpose: the Misc Window popup menu/window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMiscPopup { variable Priv # namespace eval NSMiscPopup } # NSMiscPopup::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::InitModule {} { InitImageIfNeeded Image_ButtonActivate button-activate.gif InitImageIfNeeded Image_ButtonFood button-food.gif InitImageIfNeeded Image_ButtonPotion button-potion.gif InitImageIfNeeded Image_ButtonScroll button-scroll.gif InitImageIfNeeded Image_ButtonRod button-rod.gif InitImageIfNeeded Image_ButtonWand button-wand.gif InitImageIfNeeded Image_ButtonStaff button-staff.gif InitImageIfNeeded Image_ButtonDown button-down.gif InitImageIfNeeded Image_ButtonUp button-up.gif MInfo oop [NSObject::New NSMiscPopup] return } # NSMiscPopup::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::CloseModule {} { catch { set win [MInfo win] destroy $win } return } # NSMiscPopup::MInfo -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::MInfo {info args} { variable Priv # Set info if {[llength $args]} { set Priv($info) [lindex $args 0] # Get info } else { return $Priv($info) } return } # NSMiscPopup::NSMiscPopup -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMiscPopup::NSMiscPopup {oop} { Info $oop hook "" Info $oop current "" Info $oop busy 0 Info $oop win,visible 0 Info $oop whoHasCursor "" Info $oop after "" Info $oop nextButton 0 InitWindow $oop set win [Info $oop win] # See CloseModule() MInfo win $win # Update ourself when the list highlight color changes Info $oop clientId,listHilite \ [NSValueManager::AddClient listHilite \ "$win.frame.text tag configure HOT \ -background \[Value listHilite]"] # Update ourself when the font changes Info $oop clientId,font,miscPopup \ [NSValueManager::AddClient font,miscPopup \ "NSMiscPopup::ValueChanged_font_miscPopup $oop"] # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSMiscPopup $oop $win return } # NSMiscPopup::~NSMiscPopup -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::~NSMiscPopup {oop} { set win [Info $oop win] NSValueManager::RemoveClient font,miscPopup [Info $oop clientId,font,miscPopup] NSValueManager::RemoveClient listHilite [Info $oop clientId,listHilite] return } # NSMiscPopup::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::Info {oop info args} { global NSMiscPopup # Set info if {[llength $args]} { switch -- $info { default { set NSMiscPopup($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSMiscPopup($oop,$info) } } } return } # NSMiscPopup::InitWindow -- # # Create the window. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSMiscPopup::InitWindow {oop} { Info $oop canvas [Global misc,toolbar] # # Popup window of choices # set win [Global misc,canvas].miscpopup$oop toplevel $win -borderwidth 1 -relief flat -background gray60 wm overrideredirect $win yes wm transient $win [Window misc] if {[Platform unix]} { $win configure -cursor arrow } # Start out withdrawn (hidden) wm withdraw $win # Set instance variables Info $oop win $win set wText $win.text text $wText \ -wrap none -font [Value font,miscPopup] \ -borderwidth 0 -setgrid no -highlightthickness 0 \ -padx 4 -pady 2 -background Black -foreground White -cursor "" # Bypass default Text bindings bindtags $wText [list $wText $win all] pack $wText \ -expand yes -fill both Info $oop text $wText # Fiddle with the selection for list behaviour $wText tag configure HOT -foreground White \ -background [Value listHilite] $wText tag bind HOT \ "NSMiscPopup::Invoke $oop" $wText tag bind TEXT \ "NSMiscPopup::Motion $oop \[$wText index {@%x,%y linestart}]" $wText tag bind HOT \ "NSMiscPopup::Motion $oop {}" bind $win \ "NSMiscPopup::Event $oop enter-win" bind $win " NSMiscPopup::Motion $oop {} NSMiscPopup::Event $oop leave-win " return } # NSMiscPopup::NewButton -- # # Add a new button. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::NewButton {oop args} { set canvas [Info $oop canvas] # Get the next unique id for this button set num [incr ::NSMiscPopup($oop,nextButton)] set config(-command) "" array set config $args set image $config(-image) set command $config(-command) set message $config(-message) # The buttons are positioned in ShowBar(). set x [expr {1 + ($num - 1) * 20}] set y 1 Info $oop button,x,$num $x # Focus rectangle $canvas create rectangle $x $y [expr {$x + 17}] [expr {$y + 17}] \ -tags [list button button$num border$num] # Image $canvas create image [expr {$x + 1}] [expr {$y + 1}] -image $image \ -anchor nw -tags "button button$num img$num" # Show popup on mouse-over if {![string length $command]} { $canvas bind img$num " $canvas itemconfigure border$num -outline gray60 NSMainWindow::StatusText $oop [list $message] NSMiscPopup::EnterButton $oop $num " Info $oop button,hook,$num $config(-hook) Info $oop button,args,$num $config(-args) # Click to invoke command } else { $canvas bind img$num " $canvas itemconfigure border$num -outline gray60 NSMainWindow::StatusText $oop [list $message] NSMiscPopup::Event $oop enter-button2 " $canvas bind img$num " $canvas move button$num 1 1 $command " $canvas bind img$num \ "$canvas move button$num -1 -1" Info $oop button,hook,$num "" } $canvas bind img$num " $canvas itemconfigure border$num -outline Black NSMiscPopup::Event $oop leave-button NSMainWindow::StatusText $oop {} " return } # NSMiscPopup::SetHook -- # # Set the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::SetHook {oop hook} { if {[string length $hook]} { Info $oop hook $hook CallHook $oop open } elseif {[string length [Info $oop hook]]} { Info $oop hook "" } return } # NSMiscPopup::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::CallHook {oop message args} { return [uplevel #0 NSMiscPopup::[Info $oop hook] $oop $message $args] } # NSMiscPopup::EnterButton -- # # Display popup of choices. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::EnterButton {oop buttonNum} { set win [Info $oop win] set canvas [Info $oop canvas] set wText [Info $oop text] if {[lsearch -exact [angband inkey_flags] INKEY_CMD] == -1} return # Set the hook for this button set hookArgs [Info $oop button,args,$buttonNum] SetHook $oop hook_[Info $oop button,hook,$buttonNum] # See if there are any valid choices if {![eval CallHook $oop has_cmd $hookArgs]} { HideWin $oop return } # Set the list $wText delete 1.0 end eval CallHook $oop set_list $hookArgs set x [Info $oop button,x,$buttonNum] incr x [expr {[winfo rootx $canvas] + 9}] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set width [Info $oop maxWidth] incr width [expr {[$wText cget -padx] * 2}] set height [Info $oop maxHeight] incr height [expr {[$wText cget -pady] * 2}] incr width [expr {[$win cget -borderwidth] * 2}] incr height [expr {[$win cget -borderwidth] * 2}] # x is middle incr x [expr {0 - $width / 2}] if {$x < [winfo rootx $canvas]} { set x [winfo rootx $canvas] } set screenWidth [winfo screenwidth .] if {$x + $width > $screenWidth} { incr x [expr {$screenWidth - ($x + $width)}] } set screenHeight [winfo screenheight .] if {$y + $height > $screenHeight} { incr y [expr {$screenHeight - ($y + $height)}] } wm geometry $win ${width}x${height}+${x}+$y # Perhaps show the window later Event $oop enter-button return } # NSMiscPopup::Event -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::Event {oop event} { set who [Info $oop whoHasCursor] switch -- $event { enter-win { set who win } leave-win { set who "" } enter-button { set who button } enter-button2 { set who button2 } leave-button { set who "" } } Info $oop whoHasCursor $who if {[string match enter-* $event]} { set delay 10 } else { set delay 200 } after cancel [Info $oop after] Info $oop after [after $delay NSMiscPopup::CheckWhoHasCursor $oop] return } # NSMiscPopup::CheckWhoHasCursor -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::CheckWhoHasCursor {oop} { set who [Info $oop whoHasCursor] switch -- $who { button { ShowWin $oop } button2 { HideWin $oop } win { } default { HideWin $oop } } Info $oop after "" return } # NSMiscPopup::ShowWin -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::ShowWin {oop} { if {[Info $oop win,visible]} return set win [Info $oop win] wm deiconify $win if {[Platform unix]} { raise $win } Info $oop win,visible 1 return } # NSMiscPopup::HideWin -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::HideWin {oop} { if {![Info $oop win,visible]} return set win [Info $oop win] wm withdraw $win Info $oop win,visible 0 return } # NSMiscPopup::SetList -- # # Clears the text and calls the hook to set the text. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::SetList {oop} { set win [Info $oop win] set textBox [Info $oop text] # Clear the text $textBox delete 1.0 end # Call the hook to set the list CallHook $oop set_list # Something is displayed Info $oop display something # No item is highlighted Info $oop current "" return } # NSMiscPopup::Invoke -- # # Called when a list item is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::Invoke {oop} { set textBox [Info $oop text] set index [Info $oop current] set row [expr {[lindex [split $index .] 0] - 1}] HideWin $oop CallHook $oop invoke $row return } # NSMiscPopup::Motion -- # # Called when the mouse moves in a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::Motion {oop index} { set textBox [Info $oop text] # If you invoke an item, hold down the mouse, and drag... if {![string length [Info $oop hook]]} return # No tracking while menu is up if {[Info $oop busy]} return # See if the item has changed if {$index == [Info $oop current]} return # An item is highlighted if {[string length [Info $oop current]]} { # Remove highlighting UnhighlightItem $oop [Info $oop current] } # An item is under the pointer if {[string length $index]} { # Highlight the item HighlightItem $oop $index } # Remember which item is highlighted Info $oop current $index return } # NSMiscPopup::HighlightItem -- # # Highlights a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::HighlightItem {oop index} { set textBox [Info $oop text] set row [expr {[lindex [split $index .] 0] - 1}] # Highlight the item $textBox tag add HOT $index "$index lineend" $textBox tag raise HOT # Call the hook (to set the icon, for example) CallHook $oop highlight $row return } # NSMiscPopup::UnhighlightItem -- # # Removes highlighting from a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::UnhighlightItem {oop index} { set win [Info $oop win] set textBox [Info $oop text] # Unhighlight the item $textBox tag remove HOT 1.0 end return } # NSMiscPopup::HasCursor -- # # See if the cursor is over the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::HasCursor {oop} { set pointerx [winfo pointerx .] set pointery [winfo pointery .] set window [winfo containing $pointerx $pointery] if {![string length $window]} { return 0 } if {[string compare [winfo toplevel $window] [Info $oop win]]} { return 0 } return 1 } proc NSMiscPopup::hook_cmd_pet {oop message args} { switch -- $message { open { } fresh { SetList $oop } close { } set_list { set textBox [Info $oop text] # Keep a list of invoke chars set match {} # Process each command foreach {char label} [NSRecall::PetCmdInfo mode] { if {[string equal $char $mode]} { set color [Value TERM_L_BLUE] } else { set color White } # Append the character and description $textBox insert end "$char\) " TEXT $label \ [list ITEM_$char TEXT] "\n" $textBox tag configure ITEM_$char -foreground $color # Keep a list of chars and colors lappend match $char lappend colors $color } # Delete trailing newline $textBox delete "end - 1 chars" # Keep a list of chars and colors Info $oop match $match Info $oop color $colors } get_color { set row [lindex $args 0] return [lindex [Info $oop color] $row] } invoke { if {![Info $oop choosing]} return set row [lindex $args 0] set char [lindex [Info $oop match] $row] angband keypress $char } highlight { } } return } # NSMiscPopup::OptionChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::OptionChanged {oop info keyword} { set setting [Info $oop $info] Value choicewindow,$keyword $setting switch -- $keyword { showicon { if {$setting} { grid [Info $oop icon] } else { grid remove [Info $oop icon] } } } return } # NSMiscPopup::ValueChanged_font_miscPopup -- # # Called when the font,miscPopup value changes. # Updates the Recall Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscPopup::ValueChanged_font_miscPopup {oop} { set text [Info $oop text] $text configure -font [Value font,miscPopup] return } zangband/lib/script/tk/misc-window.tcl0000644000000000000000000012762310250356274017013 0ustar rootroot# File: misc-window.tcl # Purpose: Message, Misc and Progress Windows # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSMiscWindow { variable Priv # namespace eval NSMiscWindow } # NSMiscWindow::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::InitModule {} { NSModule::LoadIfNeeded NSBalloon InitAuxWindows return } # NSMiscWindow::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::Info {info args} { variable Priv # Set info if {[llength $args]} { switch -- $info { default { set Priv($info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $Priv($info) } } } return } # NSMiscWindow::InitAuxWindows -- # # Initialize other windows. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::InitAuxWindows {} { # Get the Main Window set winMain [Window main] # Big screens get the Tool Window if {0 && [winfo screenwidth .] >= 800} { # # Toolbar # set win .tools toplevel $win wm title $win "Tool" wm transient $win $winMain wm resizable $win no no Term_KeyPress_Bind $win # Start out withdrawn (hidden) wm withdraw $win Window tool $win set child [InitDisplay_Toolbar $win] pack $child \ -side top -expand yes -fill x } # # Message line # set win .message toplevel $win wm title $win "Message" wm transient $win $winMain wm resizable $win yes no wm protocol $win WM_DELETE_WINDOW { NSMainWindow::Info [Global main,oop] messageWindow 0 NSMainWindow::MenuInvoke [Global main,oop] ignore E_WINDOW_MESSAGE } Term_KeyPress_Bind $win # Start out withdrawn (hidden) wm withdraw $win Window message $win set child [InitDisplay_Message [Window main].message] pack $child -side top -expand yes -fill x set child [InitDisplay_Message $win] pack $child \ -side top -expand yes -fill x # # Misc info window # set win .misc toplevel $win wm title $win "Misc" wm transient $win [Window main] wm resizable $win no no wm protocol $win WM_DELETE_WINDOW { NSMainWindow::Info [Global main,oop] miscWindow 0 NSMainWindow::MenuInvoke [Global main,oop] ignore E_WINDOW_MISC } Term_KeyPress_Bind $win # Start out withdrawn (hidden) wm withdraw $win Window misc $win set child [InitDisplay_Misc [Window main].misc] pack $child -expand yes -fill y set child [InitDisplay_Misc $win] pack $child \ -expand no # # Progress window # set win .progress toplevel $win wm title $win "Progress" wm transient $win $winMain wm resizable $win no no wm protocol $win WM_DELETE_WINDOW "NSMiscWindow::HideProgressWindow" Term_KeyPress_Bind $win # Start out withdrawn (hidden) wm withdraw $win Window progress $win set child [InitDisplay_Progress $win] pack $child \ -expand no return } proc NSMiscWindow::InitDisplay_Toolbar {parent} { set canvas $parent.tools canvas $canvas -width [expr {(16 + 4) * 9}] -height 22 \ -scrollregion {0 0 0 0} -highlightthickness 0 -background Black Global misc,toolbar $canvas NSModule::LoadIfNeeded NSMiscPopup return $canvas } proc NSMiscWindow::InitDisplay_Message {parent} { set font [Value font,message] set text $parent.message text $text \ -width 80 -height 1 -font $font -background Black \ -foreground White -cursor {} -borderwidth 0 -highlightthickness 0 bindtags $text [list $text $parent all] # When the Message Window is clicked: # - see the previous message, or # - toggle the display of inventory, spells, etc, or # - bypass the -more- prompt bind $text { switch -- [angband inkey_flags] { INKEY_CMD { DoUnderlyingCommand ^o } INKEY_ITEM - INKEY_SPELL { angband keypress * } INKEY_MORE { angband keypress " " } } switch -- [angband inkey_flags] { INKEY_MINDCRAFT - INKEY_POWER { angband keypress * } } } bind $text { DoUnderlyingCommand ^p } NSStatusText::StatusText $text \ [Global main,statusBar] \ "Click for last message. Double-click for message history." # Update ourself when the font for the Message Window changes NSValueManager::AddClient font,message \ NSMiscWindow::ValueChanged_font_message if {0 && [llength [info commands tk_chooseFont]]} { bind $text { SetMessageFont [tk_chooseFont -parent %W -style] } } pack $text \ -side top -expand yes -fill x Global message,message $text # Support for colored messages foreach attr [Global term_attr] { $text tag configure $attr -foreground [Value $attr] } # Pop up a menu when the window is clicked set menu $parent.popup menu $menu -tearoff 0 bind $text \ "NSMiscWindow::ContextMenu_Message $menu %X %Y" return $text } # NSMiscWindow::ContextMenu_Message -- # # Pop up a context menu in the Message Window to configure it's # appearance. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::ContextMenu_Message {menu x y} { $menu delete 0 end $menu add command -label "Set Font" \ -command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font message" if {[winfo ismapped [Window message]]} { set label "Attach To Main" set value 0 } else { set label Float set value 1 } $menu add command -label $label \ -command " NSMainWindow::Info [Global main,oop] messageWindow $value NSMainWindow::MenuInvoke [Global main,oop] ignore E_WINDOW_MESSAGE " $menu add separator $menu add command -label "Cancel" tk_popup $menu $x $y return } proc NSMiscWindow::InitDisplay_Misc {parent} { # # Font # set font [Value font,misc] set fontHeight [font metrics $font -linespace] # # Pics # InitImageIfNeeded Image_MiscAC ac.gif InitImageIfNeeded Image_MiscAU au.gif InitImageIfNeeded Image_MiscCHR cha.gif InitImageIfNeeded Image_MiscCON const.gif InitImageIfNeeded Image_MiscDEX dex.gif InitImageIfNeeded Image_MiscEXP exp.gif InitImageIfNeeded Image_MiscINT int.gif InitImageIfNeeded Image_MiscLEVEL level.gif InitImageIfNeeded Image_MiscLOOK look.gif InitImageIfNeeded Image_MiscSTR str.gif InitImageIfNeeded Image_MiscWIS wis.gif # # Main canvas # set c $parent.misc canvas $c \ -scrollregion "0 0 100 100" -width 100 -height 100 \ -relief flat -highlightthickness 0 -background Black # Display help string in Main Window status bar CanvasFeedbackInit $c if 0 { # Character image (center at 22,22) $c create widget 0 0 -anchor center -tags icon # Click the image to see character info $c bind icon { %W itemconfigure focus -outline gray60 } $c bind icon { %W itemconfigure focus -outline Black } $c bind icon " $c move icon 1 1 set CanvasButtonDown 1 " $c bind icon " $c move icon 1 1 set CanvasButtonDown 1 " $c bind icon " $c move icon -1 -1 set CanvasButtonDown 0 " $c bind icon " if {\$CanvasButtonDown} { $c move icon -1 -1 update idletasks DoCommandIfAllowed C } " CanvasFeedbackAdd $c icon "NSMiscWindow::CanvasFeedbackCmd_MiscWindow icon" } # Focus ring around character image (since user can click it) $c create rectangle 0 0 1 1 -outline Black -tags focus # Character name $c create text 0 0 -font $font -justify left \ -anchor w -fill [Value TERM_L_BLUE] -tags {font name} CanvasFeedbackAdd $c name "NSMiscWindow::CanvasFeedbackCmd_MiscWindow name" # Click name to change it $c bind name { %W itemconfigure name -fill gray60 } $c bind name { %W itemconfigure name -fill [Value TERM_L_BLUE] } # Title $c create text 0 0 -font $font -justify left \ -anchor w -fill [Value TERM_L_BLUE] -tags {font title} CanvasFeedbackAdd $c title "NSMiscWindow::CanvasFeedbackCmd_MiscWindow title" if 0 { # Stat titles foreach title {STR INT WIS DEX CON CHR} stat [angband info stat_name] { $c create image 0 0 -image Image_Misc$title -anchor nw \ -tags "$title pic,$title" $c create text 0 0 -text $title -font $font \ -justify left -anchor nw -fill White \ -tags "font $title txt,$title" CanvasFeedbackAdd $c $title \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $stat" } # Stats foreach stat [angband info stat_name] { $c create text 0 0 -font $font -justify right \ -anchor ne -fill [Value TERM_L_GREEN] -tags "font $stat" CanvasFeedbackAdd $c $stat \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $stat" } } # Race and Class foreach info {race class} { $c create text 0 0 -font $font -justify left \ -anchor nw -fill [Value TERM_L_BLUE] -tags "font $info" CanvasFeedbackAdd $c $info \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $info" } # Level, Experience, Gold and Armor Class titles foreach title {LEVEL EXP AU AC} { $c create image 0 0 -image Image_Misc$title -anchor nw \ -tags "$title pic,$title" $c create text 0 0 -text $title -font $font \ -justify left -anchor nw -fill White \ -tags "font $title txt,$title" CanvasFeedbackAdd $c $title \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $title" } # Level, Experience, Gold and Armor Class foreach info {level exp gold armor_class} title {LEVEL EXP AU AC} { $c create text 0 0 -font $font -justify right \ -anchor ne -fill [Value TERM_L_GREEN] -tags "font $info" if {[string equal $title EXP]} {set title exp} if {[string equal $title AC]} {set title ac} CanvasFeedbackAdd $c $info \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $title" } foreach title {HP SP FD} option {hitpoints mana food} { $c create text 0 0 -text $title -font $font \ -fill White -anchor nw -tags "font $title" $c create text 0 0 -text 99999 -font $font \ -fill [Value TERM_L_GREEN] -anchor ne -tags "font $option" CanvasFeedbackAdd $c $title \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $option" CanvasFeedbackAdd $c $option \ "NSMiscWindow::CanvasFeedbackCmd_MiscWindow $option" } # Global access Global misc,canvas $c # Set some initial values. MiscSet # When the mouse enters the "EXP" text, it toggles to ADV and shows # the points needed to reach the next level. $c bind EXP { %W itemconfigure txt,EXP -fill gray60 } $c bind EXP { %W itemconfigure txt,EXP -fill White } $c bind EXP { Value misc,mode,exp [expr {![Value misc,mode,exp]}] namespace eval NSMiscWindow { if {[Value misc,mode,exp]} { %W itemconfigure txt,EXP -text "EXP" } else { %W itemconfigure txt,EXP -text "ADV" } eval bind_Py_exp [angband player exp] CanvasFeedbackCmd_MiscWindow EXP enter } } $c bind AC { %W itemconfigure txt,AC -fill gray60 } $c bind AC { %W itemconfigure txt,AC -fill White } $c bind AC { Value misc,mode,armor_class [expr {![Value misc,mode,armor_class]}] namespace eval NSMiscWindow { eval bind_Py_ac [angband player armor_class] CanvasFeedbackCmd_MiscWindow AC enter } } if {$parent == [Window misc]} { $c create line 0 0 1 1 -fill #333366 -tags divider set tools [InitDisplay_Toolbar $c] pack $tools -side top -anchor n pack propagate $c no } # Arrange all the items ValueChanged_font_misc # Update ourself when the font for the Misc Window changes NSValueManager::AddClient font,misc \ NSMiscWindow::ValueChanged_font_misc # Pop up a menu when the window is clicked set menu $parent.popup menu $menu -tearoff 0 bind $c \ "NSMiscWindow::ContextMenu_Misc $menu %X %Y" return $c } # NSMiscWindow::MiscSet -- # # Set text of Misc Window items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::MiscSet {} { set canvas [Global misc,canvas] if {[Value misc,mode,exp]} { $canvas itemconfigure txt,EXP -text "EXP" } else { $canvas itemconfigure txt,EXP -text "ADV" } eval bind_Py_exp [angband player exp] eval bind_Py_ac [angband player armor_class] if 0 { foreach stat [angband info stat_name] { bind_Stat $stat } foreach tag [list gold level name race class title] { bind_Py_value $tag [angband player $tag] } } foreach tag {hitpoints mana food} { scan [angband player $tag] "%d %d" cur max UpdateHP_SP_FD $tag $cur $max } return } # NSMiscWindow::ContextMenu_Misc -- # # Pop up a context menu in the Misc Window to configure it's # appearance. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::ContextMenu_Misc {menu x y} { $menu delete 0 end $menu add command -label "Set Font" \ -command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font misc" if {[winfo ismapped [Window misc]]} { set label "Attach To Main" set value 0 } else { set label Float set value 1 } $menu add command -label $label \ -command " NSMainWindow::Info [Global main,oop] miscWindow $value NSMainWindow::MenuInvoke [Global main,oop] ignore E_WINDOW_MISC " if {[Value misc,textLabels]} { set label "Picture Labels" set value 0 } else { set label "Text Labels" set value 1 } $menu add command -label $label \ -command "Value misc,textLabels $value ; NSMiscWindow::ValueChanged_font_misc" if {[winfo ismapped [Window misc]]} { if {[Value misc,layout] == "wide"} { set label "Tall Layout" set value tall } else { set label "Wide Layout" set value wide } $menu add command -label $label \ -command " Value misc,layout $value NSMiscWindow::ValueChanged_font_misc " } $menu add separator $menu add command -label "Cancel" tk_popup $menu $x $y return } proc NSMiscWindow::UpdateHP_SP_FD {which cur max} { set canvas [Global misc,canvas] if {[string compare $which food]} { if {$cur >= $max} { set fill [Value TERM_L_GREEN] } elseif {$cur > ($max * 5) / 10} { set fill [Value TERM_YELLOW] } else { if {$cur < 0} {set cur 0} set fill [Value TERM_L_RED] } } else { if {$cur < 500} { set fill [Value TERM_L_RED] } elseif {$cur < 1000} { set fill [Value TERM_ORANGE] } elseif {$cur < 2000} { set fill [Value TERM_YELLOW] } elseif {$cur < 15000} { set fill [Value TERM_L_GREEN] } else { set fill [Value TERM_GREEN] } } if {[Value misc,layout] == "tall"} { $canvas itemconfigure $which -fill $fill -text $cur return } # Creation if {![info exists ::Global(progress,canvas)]} return set canvas [Global progress,canvas] if {[Value progress,showNumbers]} { if {[Value progress,showBars]} { $canvas itemconfigure $which -fill $fill -text $cur } else { $canvas itemconfigure $which -fill $fill -text $cur/$max } } if 0 { if {[Value progress,showBars]} { NSProgress::SetDoneRatio [NSMiscWindow::Info prog$which] $frac } } return } proc NSMiscWindow::InitDisplay_Progress {parent} { global NSProgress # I want this window as wide as the Misc Window set width [winfo reqwidth [Window misc].misc] set font [Global font,fixed,small] set fontWidth [font measure $font "W"] # Space at ends of each bar set barSpace 8 # Width of each bar set progWidth [expr {$width - $fontWidth * 8 - $barSpace * 2 - 2}] set barLeft [expr {$width - $barSpace - $progWidth}] set barRight [expr {$width - $barSpace}] set c $parent.progress canvas $c \ -scrollregion "0 0 $width 60" \ -width $width -height 60 \ -relief flat -highlightthickness 0 -background Black # Display help string in Main Window status bar CanvasFeedbackInit $c # Border around first bar set x [expr {$barLeft - 1}] set y1 12 set y2 19 $c create line $x $y2 $x $y1 $barRight $y1 -fill #666699 \ -tags {borderHP1 border} $c create line $barRight $y1 $barRight $y2 $x $y2 -fill #333366 \ -tags {borderHP2 border} # Border around second bar incr y1 15 incr y2 15 $c create line $x $y2 $x $y1 $barRight $y1 -fill #666699 \ -tags {borderSP1 border} $c create line $barRight $y1 $barRight $y2 $x $y2 -fill #333366 \ -tags {borderSP2 border} # Border around third bar incr y1 15 incr y2 15 $c create line $x $y2 $x $y1 $barRight $y1 -fill #666699 \ -tags {borderFD1 border} $c create line $barRight $y1 $barRight $y2 $x $y2 -fill #333366 \ -tags {borderFD2 border} # Bar titles set y 7 foreach title {HP SP FD} option {hitpoints mana food} { $c create text 2 [incr y 15] -text $title -font $font \ -fill White -anchor sw -tags "font $title" CanvasFeedbackAdd $c $title \ "NSMiscWindow::CanvasFeedbackCmd_Progress $option" } # Hit Points set y 13 set progHP [NSObject::New NSProgress $c $progWidth 6 "Red" gray60] place $NSProgress($progHP,frame) \ -x $barLeft -y $y -anchor nw Info proghitpoints $progHP NSStatusText::StatusCommand $NSProgress($progHP,frame) \ [Global main,statusBar] \ {NSMiscWindow::CanvasFeedbackCmd_Progress hitpoints enter} set x [expr {$barLeft - $barSpace}] set y2 7 $c create text $x [incr y2 15] -text 99999 -font $font \ -fill [Value TERM_L_GREEN] -anchor se -tags {font hitpoints} CanvasFeedbackAdd $c hitpoints \ "NSMiscWindow::CanvasFeedbackCmd_Progress hitpoints" # Spell Points set progSP [NSObject::New NSProgress $c $progWidth 6 "Blue" gray60] place $NSProgress($progSP,frame) \ -x $barLeft -y [incr y 15] -anchor nw Info progmana $progSP NSStatusText::StatusCommand $NSProgress($progSP,frame) \ [Global main,statusBar] \ {NSMiscWindow::CanvasFeedbackCmd_Progress mana enter} $c create text $x [incr y2 15] -text 99999 -font $font \ -fill [Value TERM_L_GREEN] -anchor se -tags {font mana} CanvasFeedbackAdd $c mana \ "NSMiscWindow::CanvasFeedbackCmd_Progress mana" # Food set progFD [NSObject::New NSProgress $c $progWidth 6 "Brown" gray60] place $NSProgress($progFD,frame) \ -x $barLeft -y [incr y 15] -anchor nw Info progfood $progFD NSStatusText::StatusCommand $NSProgress($progFD,frame) \ [Global main,statusBar] \ {NSMiscWindow::CanvasFeedbackCmd_Progress food enter} $c create text $x [incr y2 15] -text 99999 -font $font \ -fill [Value TERM_L_GREEN] -anchor se -tags {font food} CanvasFeedbackAdd $c food \ "NSMiscWindow::CanvasFeedbackCmd_Progress food" # # Context Menu # set menu $parent.context menu $menu -tearoff 0 bind $parent \ "NSMiscWindow::ContextMenu_Progress $menu %X %Y" # Global access Global progress,canvas $c # Update ourself when the font for the Misc Window changes # NSValueManager::AddClient font,misc \ # NSMiscWindow::ConfigProgress # Hack -- Synch with options ConfigProgress return $c } proc NSMiscWindow::ShowProgressWindow {} { wm deiconify [Window progress] set canvas [Global progress,canvas] return } proc NSMiscWindow::HideProgressWindow {} { #### bell return #### wm withdraw [Window progress] set canvas [Global progress,canvas] return } # NSMiscWindow::ConfigProgress -- # # Arranges stuff in the Progress Window depending on the current # configuration options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::ConfigProgress {} { global NSProgress # Get the Progress Window canvas set canvas [Global progress,canvas] set showBars [Value progress,showBars] set showNumbers [Value progress,showNumbers] # Get the desired font set font [Value font,misc] # Calculate the height of a line set fontHeight [font metrics $font -linespace] # Set the font of items with the "font" tag $canvas itemconfigure font -font $font # Get the width of the Misc Window canvas set width [winfo reqwidth [Global misc,canvas]] # Keep the Progress Window as wide as the Misc Window $canvas configure -width $width # Set the height of the canvas $canvas configure -height [expr {$fontHeight * 3 + 8}] # Calculate the width of the text set textWidth 0 foreach label [list HP SP FD] { set twidth [font measure $font $label] if {$twidth > $textWidth} { set textWidth $twidth } } if {$showNumbers} { incr textWidth 8 ; # pad incr textWidth [font measure $font 99999] } if {!$showBars} { incr textWidth [font measure $font /99999] } incr textWidth 2 ; # left pad # Space at ends of each bar set barSpace 8 # Get the height of each progress bar set barHeight [winfo reqheight $NSProgress([Info progfood],frame)] # Width of each bar. This is the width of the window minus the space # at each end of a bar, minus the bar title "HP " etc, minus the # numbers "12345" if numbers are showing. set barWidth [expr {$width - $textWidth - $barSpace * 2}] set barLeft [expr {$width - $barSpace - $barWidth}] set barRight [expr {$width - $barSpace}] set y2 4 foreach title {HP SP FD} { $canvas coords $title 2 [incr y2 $fontHeight] } # Option: Display numbers if {$showNumbers} { set x [expr {$barLeft - $barSpace}] set y2 4 $canvas coords hitpoints $x [incr y2 $fontHeight] set x [expr {$barLeft - $barSpace}] $canvas coords mana $x [incr y2 $fontHeight] set x [expr {$barLeft - $barSpace}] $canvas coords food $x [incr y2 $fontHeight] # Don't show numbers } else { $canvas move hitpoints $width 0 $canvas move mana $width 0 $canvas move food $width 0 } # Option: Show bars if {$showBars} { # Border around first bar set x [expr {$barLeft - 1}] set y1 [expr {4 + $fontHeight / 2 - $barHeight / 2}] set y2 [expr {$y1 + $barHeight}] $canvas coords borderHP1 $x $y2 $x $y1 $barRight $y1 $canvas coords borderHP2 $barRight $y1 $barRight $y2 $x $y2 # Border around second bar incr y1 $fontHeight incr y2 $fontHeight $canvas coords borderSP1 $x $y2 $x $y1 $barRight $y1 $canvas coords borderSP2 $barRight $y1 $barRight $y2 $x $y2 # Border around third bar incr y1 $fontHeight incr y2 $fontHeight $canvas coords borderFD1 $x $y2 $x $y1 $barRight $y1 $canvas coords borderFD2 $barRight $y1 $barRight $y2 $x $y2 # Hit Points set y [expr {4 + $fontHeight / 2 - $barHeight / 2 + 1}] set progHP [Info proghitpoints] place $NSProgress($progHP,frame) \ -x $barLeft -y $y -anchor nw $NSProgress($progHP,frame) configure -width $barWidth # Spell Points set progSP [Info progmana] place $NSProgress($progSP,frame) \ -x $barLeft -y [incr y $fontHeight] -anchor nw $NSProgress($progSP,frame) configure -width $barWidth # Food set progFD [Info progfood] place $NSProgress($progFD,frame) \ -x $barLeft -y [incr y $fontHeight] -anchor nw $NSProgress($progFD,frame) configure -width $barWidth # Don't show bars } else { $canvas move border $width 0 foreach bar [winfo children $canvas] { place forget $bar } } # Cancel geometry wm geometry [Window progress] "" return } # NSMiscWindow::ContextMenu_Progress -- # # Pop up a context menu in the Progress Window to configure it's # appearance. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::ContextMenu_Progress {menu x y} { $menu delete 0 end set showBars [Value progress,showBars] set showNumbers [Value progress,showNumbers] set state normal set action Show if {$showBars} { set action Hide if {!$showNumbers} { set state disabled } } $menu add command -label "$action Bars" -state $state \ -command "NSMiscWindow::ToggleProgress showBars" set state normal set action Show if {$showNumbers} { set action Hide if {!$showBars} { set state disabled } } $menu add command -label "$action Numbers" -state $state \ -command "NSMiscWindow::ToggleProgress showNumbers" $menu add separator $menu add command -label "Cancel" tk_popup $menu $x $y return } # NSMiscWindow::ToggleProgress -- # # Hide or show the bars or numbers in the Progress Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::ToggleProgress {which} { set showIt [Value progress,$which] Value progress,$which [expr {!$showIt}] ConfigProgress foreach option {hitpoints mana food} { eval Update_HP_SP_FD hitpoints [angband player $option] } return } # NSMiscWindow::CanvasAddTextItem -- # # Create a new canvas text item at the given "row" and "column". # Item positioning is determined by the size of the given font. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::CanvasAddTextItem {canvas font col row width fill text justify tags} { set fontWidth [font measure $font "W"] set fontHeight [font metrics $font -linespace] if {[string equal $justify right]} { incr col $width set anchor ne } else { set anchor nw } set x [expr {$col * $fontWidth}] set y [expr {$row * $fontHeight}] set width [expr {$width * $fontWidth}] return [$canvas create text $x $y -fill $fill -text $text \ -font $font -justify $justify \ -anchor $anchor -tags $tags] } # NSMiscWindow::bind_Stat -- # # Called in response to a event. Updates the Misc Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::bind_Stat {stat} { if 0 { set canvas [Global misc,canvas] angband player stat $stat statInfo set max $statInfo(use) set top $statInfo(top) if {$max < $top} { set fill [Value TERM_YELLOW] } else { set fill [Value TERM_L_GREEN] } set value [cnv_stat_disp $max] set text [$canvas itemcget $stat -text] set fillCur [FlashCanvasTextFill $canvas $stat] if {[string compare $text $value] || [string compare $fill $fillCur]} { $canvas itemconfigure $stat -text $value FlashCanvasText $canvas $stat White $fill 4 } } return } # NSMiscWindow::bind_Py -- # # Called in response to a event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::bind_Py_ac {dis_ac dis_to_a} { set canvas [Global misc,canvas] if {[Value misc,mode,armor_class]} { set ac [expr {$dis_ac + $dis_to_a}] } else { set ac [format "%d,%+d" $dis_ac $dis_to_a] } set fill [Value TERM_L_GREEN] set text [$canvas itemcget armor_class -text] if {[string compare $text $ac]} { $canvas itemconfigure armor_class -text $ac FlashCanvasText $canvas armor_class White $fill 4 } return } # NSMiscWindow::bind_Py_exp -- # # Called in response to a event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::bind_Py_exp {cur max adv} { set canvas [Global misc,canvas] if {$cur < $max} { set fill [Value TERM_YELLOW] } else { set fill [Value TERM_L_GREEN] } if {![Value misc,mode,exp]} { if {$adv == 999999999} { set cur ***** } else { set cur [expr {$adv - $cur}] } } set text [$canvas itemcget exp -text] set fillCur [FlashCanvasTextFill $canvas exp] if {[string compare $text $cur] || [string compare $fill $fillCur]} { $canvas itemconfigure exp -text $cur FlashCanvasText $canvas exp White $fill 4 } return } # NSMiscWindow::bind_Py_gold -- # # Called in response to a event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::bind_Py_value {tag value} { set canvas [Global misc,canvas] set text [$canvas itemcget $tag -text] if {[string compare $text $value]} { set fill [FlashCanvasTextFill $canvas $tag] $canvas itemconfigure $tag -text $value FlashCanvasText $canvas $tag White $fill 4 } return } # NSMiscWindow::bind_Py_level -- # # Called in response to a event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::bind_Py_level {cur max} { set canvas [Global misc,canvas] if {$cur < $max} { set fill [Value TERM_YELLOW] } else { set fill [Value TERM_L_GREEN] } set text [$canvas itemcget level -text] if {$text != $cur} { $canvas itemconfigure level -text $cur FlashCanvasText $canvas level White $fill 4 } # Hack -- Display experience as well eval bind_Py_exp [angband player exp] return } # NSMiscWindow::CanvasFeedbackInit -- # # Call a command whenever the mouse enters or leaves a canvas item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::CanvasFeedbackInit {canvas} { $canvas bind StatusTextCanvasTag { if {[info exists CanvasFeedback(%W,command,[%W find withtag current])]} { eval $CanvasFeedback(%W,command,[%W find withtag current]) enter } } $canvas bind StatusTextCanvasTag { if {[info exists CanvasFeedback(%W,command,[%W find withtag current])]} { eval $CanvasFeedback(%W,command,[%W find withtag current]) press1 } } $canvas bind StatusTextCanvasTag { if {[info exists CanvasFeedback(%W,command,[%W find withtag current])]} { eval $CanvasFeedback(%W,command,[%W find withtag current]) leave } } return } proc NSMiscWindow::CanvasFeedbackAdd {canvas tagOrId command} { global CanvasFeedback foreach itemId [$canvas find withtag $tagOrId] { $canvas addtag StatusTextCanvasTag withtag $itemId set CanvasFeedback($canvas,command,$itemId) $command set CanvasFeedback($canvas,tag,$itemId) $tagOrId } ##### return ##### set itemId [lindex [$canvas find withtag $tagOrId] 0] set tagList [$canvas itemcget $itemId -tags] lappend tagList StatusTextCanvasTag $canvas itemconfigure $itemId -tags $tagList set CanvasFeedback($canvas,command,$itemId) $command set CanvasFeedback($canvas,tag,$itemId) $tagOrId return } proc NSMiscWindow::CanvasFeedbackCmd_MiscWindow {info action} { if {![string equal $action enter]} { NSMainWindow::StatusText [Global main,oop] {} return } switch -- $info { strength - intelligence - wisdom - dexterity - constitution - charisma { angband player stat $info statInfo set max $statInfo(use) set top $statInfo(top) set message [format "Your character's %s" $info] if {$max < $top} { append message [format " (max %s)" [cnv_stat_disp $top]] } } icon { # set message "Click for a menu of actions." set message "Click to display the Character Window." } class - name - race - title { set message [format "Your character's %s" $info] if {[string equal $info name]} { append message ". Click to change." } } ac - AC { set message "Your character's armor class " if {[Value misc,mode,armor_class]} { append message "(total)" } else { append message "(base,bonus)" } if {[string equal $info AC]} { append message ". Click to toggle." } } AU { set message "Your character's gold" } exp - EXP { scan [angband player exp] "%d %d %d" cur max adv if {[Value misc,mode,exp]} { set message "Current experience points" if {$cur < $max} { append message [format " (max %s)" $max] } } else { set message "Experience points for next level" if {($cur < $max) && ($adv != 999999999)} { if {$max < $adv} { set max [expr {$adv - $max}] append message [format " (min %d)" $max] } } } if {[string equal $info EXP]} { append message ". Click to toggle." } } LEVEL { set message "Your character's experience level" set max [angband player max_lev] if {[angband player lev] < $max} { append message [format " (max %s)" $max] } } food - hitpoints - mana { set desc(food) "food level" set desc(hitpoints) "hit points" set desc(mana) "spell points" set message [format "Your character's %s" $desc($info)] scan [angband player $info] "%d %d" cur max if {$cur < $max} { append message [format " (max %s)" $max] } } } NSMainWindow::StatusText [Global main,oop] $message return } proc NSMiscWindow::CanvasFeedbackCmd_Progress {info action} { if {[string equal $action leave]} { NSMainWindow::StatusText [Global main,oop] {} return } switch -- $info { food - hitpoints - mana { set desc(food) "food level" set desc(hitpoints) "hit points" set desc(mana) "spell points" set message [format "Your character's %s" $desc($info)] set showNumbers [Value progress,showNumbers] set showBars [Value progress,showBars] scan [angband player $info] "%d %d" cur max if {!$showNumbers} { append message " ($cur/$max)" } elseif {$showBars} { if {$cur < $max} { append message [format " (max %s)" $max] } } append message ". Right-Click for options." } } NSMainWindow::StatusText [Global main,oop] $message return } # NSMiscWindow::MenuSelect_InventoryPopup -- # # When a menu entry is highlighted in the Misc Window popup, display # the object memory in the Recall Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::MenuSelect_InventoryPopup {menu} { variable InventoryPopup set invOrEquip $InventoryPopup(invOrEquip) set itemList $InventoryPopup(itemList) # Get the index of the active menu entry set index [$menu index active] # No entry is selected if {[string equal $index none]} { set item -1 # An entry is selected } else { if {$index < [llength $itemList]} { set item [lindex $itemList $index] } else { set item -1 } } return } # NSMiscWindow::InventoryPopup -- # # Pop up a menu of equipment/inventory choices. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::InventoryPopup {menu canvas invOrEquip cmdChar args} { variable InventoryPopup if {[lsearch -exact [angband inkey_flags] INKEY_CMD] == -1} return # When an entry is highlighted, show object memory in the Recall Window set InventoryPopup(invOrEquip) $invOrEquip set InventoryPopup(itemList) {} bind $menu <> "NSMiscWindow::MenuSelect_InventoryPopup %W" $menu delete 0 end # Can't call DoUnderlyingCommand because INKEY_CMD is not set # while looking at inventory! Can't simply prepend a slash to # get the underlying command because request_command() isn't # being called. So I have to find a keymap for the given # underlying command. set cmdChar [angband keymap find $cmdChar] set num 0 foreach item [eval angband $invOrEquip find $args] { angband $invOrEquip info $item attrib $menu add command -label "$attrib(char) $attrib(name)" \ -command "angband keypress $cmdChar$attrib(char)" \ -underline 0 lappend InventoryPopup(itemList) $item incr num } if {$num} { $menu add separator } $menu add command -label "Cancel" set x [winfo rootx [winfo toplevel $canvas]] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] # Calculate the size of the menu. If it will go offscreen, then # move it to the left by some appropriate amount. update idletasks set menuWidth [winfo reqwidth $menu] # It seems that [winfo reqwidth $menu] does not return a large # enoug value. incr menuWidth 35 set screenWidth [winfo screenwidth .] if {$x + $menuWidth > $screenWidth} { incr x [expr {$screenWidth - ($x + $menuWidth)}] } tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) # Because we never get a event variable Priv $canvas move $Priv(button) -1 -1 } # The menu may be used for other things... after idle bind $menu <> {} return } # NSMiscWindow::ValueChanged_font_message -- # # Called when the font,message value changes. # Updates the Message Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::ValueChanged_font_message {} { set win [Window message] set text [Global message,message] $text configure -font [Value font,message] update idletasks wm geometry $win [winfo reqwidth $win]x[winfo reqheight $win] return } # NSMiscWindow::ValueChanged_font_misc -- # # Called when the font,misc value changes. # Updates the Misc Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSMiscWindow::MiscArrangeWide {} { # Get the Misc Window canvas set canvas [Global misc,canvas] # Get the desired font set font [Value font,misc] # Text or pic labels set useText [Value misc,textLabels] # Calculate the height of a line set fontHeight [font metrics $font -linespace] set rowHeight $fontHeight if {!$useText} { set picHeight 18 if {$picHeight > $fontHeight} { set rowHeight $picHeight } } # Set the font of items with "font" tag $canvas itemconfigure font -font $font $canvas itemconfigure icon -state normal $canvas itemconfigure name -anchor w $canvas itemconfigure title -anchor w foreach tag {HP SP FD hitpoints mana food} { $canvas itemconfigure $tag -state hidden } # Position the character image $canvas coords icon 22 22 # Focus ring set inset [expr {22 - [icon size] / 2}] set left [expr {$inset - 2}] set top [expr {$inset - 2}] set right [expr {$left + [icon size] + 2 + 1}] set bottom [expr {$top + [icon size] + 2 + 1}] $canvas coords focus $left $top $right $bottom set topSpace 42 # Character name $canvas coords name 45 [expr {20 - $fontHeight / 2}] # Title $canvas coords title 45 [expr {20 + $fontHeight / 2}] # Stat titles if {$useText} { set maxWidth 0 } else { set maxWidth 18 } set y [expr {$topSpace - $rowHeight}] foreach title {STR INT WIS DEX CON CHR} { if {$useText} { $canvas coords txt,$title 2 [incr y $rowHeight] set width [font measure $font $title] if {$width > $maxWidth} { set maxWidth $width } $canvas itemconfigure txt,$title -state normal $canvas itemconfigure pic,$title -state hidden } else { $canvas coords pic,$title 2 [incr y $rowHeight] $canvas itemconfigure pic,$title -state normal $canvas itemconfigure txt,$title -state hidden } } # Stats set pad 10 set offset [expr {$maxWidth + $pad + [font measure $font 18/999]}] set y [expr {$topSpace - $rowHeight}] if 0 { foreach stat [angband info stat_name] { $canvas coords $stat $offset [incr y $rowHeight] } } set reqWidthLeft $offset # Race and Class incr offset 15 set y [expr {$topSpace - $rowHeight}] set dy $rowHeight foreach info {race class} { $canvas coords $info $offset [incr y $dy] } # Level, Experience, Gold and Armor Class titles foreach title {LEVEL EXP AU AC} { if {$useText} { $canvas coords txt,$title $offset [incr y $rowHeight] $canvas itemconfigure txt,$title -state normal $canvas itemconfigure pic,$title -state hidden } else { $canvas coords pic,$title $offset [incr y $rowHeight] $canvas itemconfigure pic,$title -state normal $canvas itemconfigure txt,$title -state hidden } } # Level, Experience, Gold and Armor Class if {$useText} { set w 0 foreach title {EXP ADV AU} { set w2 [expr {[font measure $font $title] + [font measure $font 99999999]}] if {$w2 > $w} { set w $w2 } } incr offset [expr {$pad + $w}] } else { incr offset [expr {18 + $pad + [font measure $font 999999999]}] } set y [expr {$topSpace + $rowHeight * 2 - $rowHeight}] foreach info {level exp gold armor_class} { $canvas coords $info $offset [incr y $rowHeight] } set reqWidthRight [expr {$offset - $reqWidthLeft}] pack $canvas.tools -side top -anchor n pack propagate $canvas.tools no # Make room for the toolbar $canvas move all 0 20 scan [$canvas bbox armor_class] "%s %s %s %s" x1 y1 x2 y2 if {!$useText} { scan [$canvas bbox pic,AC] "%s %s %s %s" x2 y2 x4 y4 if {$y4 > $y2} { set y2 $y4 } } # 1 or 2 pixel border on right and bottom edges if {$useText} { incr x2 1 incr y2 1 } else { incr x2 2 incr y2 2 } if {$x2 < [winfo reqwidth $canvas.tools]} { set x2 [winfo reqwidth $canvas.tools] } # Resize the canvas, set the scroll region $canvas configure -width $x2 -height $y2 -scrollregion "0 0 $x2 $y2" # Set the width of the divider $canvas itemconfigure divider -state normal $canvas coords divider 2 22 [expr {$x2 - 2}] 22 set d [expr {$x2 - $reqWidthLeft - $reqWidthRight - 2}] if 0 { foreach stat [angband info stat_name] { $canvas move $stat $d 0 } } foreach info {race class} { $canvas move $info $d 0 } foreach title {LEVEL EXP AU AC} { $canvas move $title $d 0 } foreach info {level exp gold armor_class} { $canvas move $info $d 0 } # Update items MiscSet # Cancel geometry wm geometry [Window misc] "" # Update the Progress Window if {[info exists ::Windows(progress)]} { wm deiconify [Window progress] foreach tag {hitpoints mana food} { scan [angband player $tag] "%d %d" cur max UpdateHP_SP_FD $tag $cur $max } ConfigProgress } return } proc NSMiscWindow::MiscArrangeTall {} { # Get the Misc Window canvas set canvas [Global misc,canvas] # Get the desired font set font [Value font,misc] # Text or pic labels set useText [Value misc,textLabels] # Calculate the height of a line set fontHeight [font metrics $font -linespace] set rowHeight $fontHeight if {!$useText} { set picHeight 18 if {$picHeight > $fontHeight} { set rowHeight $picHeight } } # Set the font of items with "font" tag $canvas itemconfigure font -font $font $canvas itemconfigure icon -state hidden $canvas itemconfigure name -anchor nw $canvas itemconfigure title -anchor nw foreach tag {HP SP FD hitpoints mana food} { $canvas itemconfigure $tag -state normal } # Name, Race, Class, Title set y 2 foreach tag {name race class title} { $canvas coords $tag 2 $y incr y $fontHeight } # Level, Experience, Gold titles set top1 [expr {2 + $fontHeight * 4}] set y $top1 foreach title {LEVEL EXP AU} { if {$useText} { $canvas coords txt,$title 2 $y $canvas itemconfigure txt,$title -state normal $canvas itemconfigure pic,$title -state hidden } else { $canvas coords pic,$title 2 $y $canvas itemconfigure pic,$title -state normal $canvas itemconfigure txt,$title -state hidden } incr y $rowHeight } # Stat titles set top2 [expr {$top1 + $rowHeight * 3}] set y $top2 foreach title {STR INT WIS DEX CON CHR} { if {$useText} { $canvas coords txt,$title 2 $y $canvas itemconfigure txt,$title -state normal $canvas itemconfigure pic,$title -state hidden } else { $canvas coords pic,$title 2 $y $canvas itemconfigure pic,$title -state normal $canvas itemconfigure txt,$title -state hidden } incr y $rowHeight } # AC title set top3 [expr {$top2 + $rowHeight * 6 + $fontHeight}] set y $top3 if {$useText} { $canvas coords txt,AC 2 $y $canvas itemconfigure txt,AC -state normal $canvas itemconfigure pic,AC -state hidden } else { $canvas coords pic,AC 2 $y $canvas itemconfigure pic,AC -state normal $canvas itemconfigure txt,AC -state hidden } # HP SP FD set top4 [expr {$top3 + $rowHeight}] set y $top4 foreach title {HP SP FD} { $canvas coords $title 2 $y incr y $fontHeight } # Level, Experience, Gold set pad 10 if {$useText} { set maxWidth 0 foreach title {EXP ADV AU} { set width [font measure $font $title] if {$width > $maxWidth} { set maxWidth $width } } set minRight [expr {2 + $maxWidth + $pad}] } else { set minRight 18 } incr minRight [font measure $font 99999999] # Stat if {$useText} { set maxWidth 0 foreach title {STR INT WIS DEX CON CHR} { set width [font measure $font $title] if {$width > $maxWidth} { set maxWidth $width } } incr maxWidth [expr {2 + $pad}] } else { set maxWidth 18 } set right [expr {$maxWidth + [font measure $font 18/999]}] if {$right > $minRight} { set minRight $right } if {$useText} { set right [expr {2 + [font measure $font "AC"] + $pad}] } else { set right 18 } incr right [font measure $font 999,+999] if {$right > $minRight} { set minRight $right } # Level, Experience, Gold values set y $top1 foreach tag {level exp gold} { $canvas coords $tag $minRight $y incr y $rowHeight } if 0 { # Stat values set y $top2 foreach stat [angband info stat_name] { $canvas coords $stat $minRight $y incr y $rowHeight } } # AC value set y $top3 $canvas coords armor_class $minRight $y # HP SP FD set y $top4 foreach option {hitpoints mana food} { $canvas coords $option $minRight $y incr y $fontHeight } if {[winfo exists $canvas.tools]} { pack forget $canvas.tools $canvas itemconfigure divider -state hidden } scan [$canvas bbox food] "%s %s %s %s" x1 y1 x2 y2 # 1 or 2 pixel border on right and bottom edges if {$useText} { incr x2 1 incr y2 1 } else { incr x2 2 incr y2 2 } # Resize the canvas, set the scroll region $canvas configure -width $x2 -height $y2 -scrollregion "0 0 $x2 $y2" # Set the width of the divider $canvas coords divider 2 22 [expr {$x2 - 2}] 22 # Update items MiscSet # Cancel geometry wm geometry [Window misc] "" if {[info exists ::Windows(progress)]} { wm withdraw [Window progress] } return } proc NSMiscWindow::ValueChanged_font_misc {} { set layout [Value misc,layout] if {![winfo exists [Global misc,canvas].tools]} { set layout tall } switch -- $layout { wide { MiscArrangeWide } tall { MiscArrangeTall } } return } zangband/lib/script/tk/misc.tcl0000644000000000000000000001571510250356274015504 0ustar rootroot# File: misc.tcl # Purpose: various commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # cnv_stat -- # # Converts a raw stat value into a human-readable value. Values from # 3 to 18 are returned unchanged. # ex. "118" becomes "18/100" # # Arguments: # arg1 about arg1 # # Results: # What happened. proc cnv_stat {val} { # Above 18 if {$val > 18} { set bonus [expr {$val - 18}]; if {$bonus >= 10} { return 18/$bonus } return 18/0$bonus # From 3 to 18 } else { return $val } } # cnv_stat_disp -- # # Same as cnv_stat(), but any bonus greater than 220 is displayed as # "***". # # Arguments: # arg1 about arg1 # # Results: # What happened. proc cnv_stat_disp {val} { # Above 18 if {$val > 18} { set bonus [expr {$val - 18}]; if {$bonus >= 220} { return 18/*** } elseif {$bonus >= 10} { return 18/$bonus } return 18/0$bonus # From 3 to 18 } else { return $val } } # ImageExists -- # # Return true if an image with the given name exists. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc ImageExists {imageName} { set imageList [image names] return [expr {[lsearch -exact $imageList $imageName] != -1}] } # InitImageIfNeeded -- # # Creates a new photo image from the given file. If the image # already exists, nothing happens. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc InitImageIfNeeded {imageName fileName args} { global Angband if {[ImageExists $imageName]} { return 0 } # Look in tk/image and subdirectories, then user-supplied tk # subdirectories foreach elem [concat [list {image} {image dg} {image dg misc-win}] $args] { set path [eval PathTk $elem [list $fileName]] if {[file exists $path]} { image create photo $imageName -file $path return 1 } } error "can't find image file \"$fileName\"" return 1 } # openlf -- # # Open a file for writing, and set the translation mode to "lf" as well. # It seems that writing unix-style files is faster as well as smaller. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc openlf {fileName} { set id [open $fileName w] fconfigure $id -translation lf return $id } # SetWindowIcon -- # # When a toplevel is mapped for the first time, set the icon, # if the window is not transient. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc SetWindowIcon {win} { global WindowIcon if {![info exists WindowIcon($win)]} { set WindowIcon($win) 1 } return } # fmt_wgt -- # # Convert 10ths of lb to ib, or kg. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc fmt_wgt {wgt {showUnits 0}} { set units lb set result [format "%d.%d" [expr {$wgt / 10}] [expr {$wgt % 10}]] if {$showUnits} { append result " $units" } return $result } # MessageDumpAux -- # # Dump a list of messages to the given file. Similar messages are # combined into a single line. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc MessageDumpAux {filePath} { set max [angband message count] set curMsg "" set count 0 for {set i [expr {$max - 1}]} {$i >= 0} {incr i -1} { set nextMsg [angband message get $i] if {[string compare $curMsg $nextMsg]} { if {[string length $curMsg]} { if {$count > 1} { append curMsg " (x$count)" } append buffer $curMsg\n } set curMsg $nextMsg set count 1 } else { incr count } } if {$count > 1} { append curMsg " (x$count)" } append buffer $curMsg\n if {[catch {openlf $filePath} fileId]} { set msg "The following error occurred while attempting to open " append msg "the message log file for writing:\n\n$fileId" tk_messageBox -title Oops -message $msg return 1 } puts $fileId "# Message Dump\n" puts $fileId $buffer close $fileId return 0 } # MessageDump -- # # Dump a list of messages to a file. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc MessageDump {parent} { global Angband set fileName [tk_getSaveFile -initialfile [angband player base_name].msg \ -initialdir [PathUser] -parent $parent] if {![string length $fileName]} return MessageDumpAux $fileName return } # AbortGame -- # # Quit without saving. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc AbortGame {} { global Angband set answer [tk_messageBox -type yesno -title "Abort ZAngband" \ -message "Write config files?"] if {[string equal $answer yes]} { angband_close_game } # In any event, delete the temp photo.txt if {[info exists ::Global(photoText)]} { set tempFile [Global photoText] if {[string length $tempFile] && [file exists $tempFile]} { file delete $tempFile } } # Bye! angband game abort -noask return } # MakeStatusBar -- # # Creates a typical status bar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc MakeStatusBar {statusBar args} { NSModule::LoadIfNeeded NSStatusBar lappend sizes 0 lappend weights 1 lappend tags t1 set i 2 foreach size $args { lappend sizes $size lappend weights 0 lappend tags t$i incr i } statusbar $statusBar -sizes $sizes -weights $weights -tags $tags return } # MakeDivider -- # # Creates a typical divider. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc MakeDivider {divider orient} { if {[Platform unix]} { set relief sunken } if {[Platform windows]} { set relief groove } switch -- $orient { x {set option -height} y {set option -width} } return [frame $divider -borderwidth 1 $option 2 -relief $relief] } # PathTk -- # # Create a path relative to Angband(dirTk) # # Arguments: # arg1 about arg1 # # Results: # What happened. proc PathTk {args} { global Angband return [eval file join [list $Angband(dirTk)] $args] } # AddStyle -- # # Add one or more styles to the given font description if needed. # If you just say "$font bold" and $font is "Times 12 {bold italic}" # you get an error. # FIXME: The font family may change (ex from Times to {Times New Roman}). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc AddStyle {font args} { array set desc [fontdesc $font] set result "[list $desc(-family)] $desc(-size)" foreach style $args { switch -- $style { bold { set desc(-weight) bold } italic { set desc(-slant) italic } overstrike { set desc(-overstrike) 1 } underline { set desc(-underline) 1 } } } if {[string equal $desc(-weight) bold]} { append result " bold" } if {[string equal $desc(-slant) italic]} { append result " italic" } if {$desc(-underline)} { append result " underline" } if {$desc(-overstrike)} { append result " overstrike" } return $result } # BoldFont -- # # Add "bold" style to the given font description if needed. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc BoldFont {font} { return [AddStyle $font bold] } zangband/lib/script/tk/moduleIndex.tcl0000644000000000000000000000160610250356274017020 0ustar rootrootIndexOne NSAbout about.tcl IndexOne NSAutobar autobar.tcl IndexOne NSCharacterWindow character-window.tcl IndexOne NSCharFlagsCanvas charflags-canvas.tcl IndexOne NSCharInfoCanvas charinfo-canvas.tcl IndexOne NSFont font.tcl IndexOne NSChoiceWindow choice-window.tcl IndexOne NSInfoWindow info-window.tcl IndexOne NSInventory inventory.tcl IndexOne NSInventory2 inventory2.tcl IndexOne NSKnowledge knowledge.tcl IndexOne NSMap map.tcl IndexOne NSMapEditor map-editor.tcl IndexOne NSMessageHistory message-history.tcl IndexOne NSTerm term.tcl IndexOne NSMainWindow main-window.tcl IndexOne NSMessageWindow message-window.tcl IndexOne NSMiscPopup misc-popup.tcl IndexOne NSMiscWindow misc-window.tcl IndexOne NSPets pets.tcl IndexOne NSPlayerFlags player-flags.tcl IndexOne NSRecall recall.tcl IndexOne NSStore store.tcl IndexOne NSStore2 store2.tcl IndexOne NSTips tips.tcl IndexOne NSWidget widget.tcl zangband/lib/script/tk/pets.tcl0000644000000000000000000005372310250356274015525 0ustar rootroot# File: pets.tcl # Purpose: the Pets Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSPets { variable Priv # namespace eval NSPets } # NSPets::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::InitModule {} { InitImageIfNeeded Image_ButtonOptions button-options.gif InitImageIfNeeded Image_ButtonHelp button-help.gif NSModule::LoadIfNeeded NSToolbar NSObject::New NSPets return } # NSPets::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::CloseModule {} { catch { destroy [Window pets] } return } # NSPets::NSPets -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::NSPets {oop} { variable Priv InitWindow $oop Info $oop current -1 set win [Info $oop win] NSWindowManager::RegisterWindow pets [Info $oop win] \ "GetDefaultGeometry $win reqwidth reqheight" "" \ "NSPets::DisplayCmd $oop" # Destroy the object along with the toplevel (later) NSUtils::DestroyObjectWithWidget NSPets $oop $win # # Global list of application windows # Global pets,oop $oop Window pets [Info $oop win] return } # NSPets::~NSPets -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::~NSPets {oop} { NSValueManager::RemoveClient font,knowledge [Info $oop clientId,font] return } # NSPets::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::Info {oop info args} { global NSPets # Verify the object NSObject::CheckObject NSPets $oop # Set info if {[llength $args]} { switch -- $info { default { set NSPets($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSPets($oop,$info) } } } return } # NSPets::InitWindow -- # # Create the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::InitWindow {oop} { variable Priv set win .pets$oop toplevel $win wm title $win "Pets" wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSPets::Close $oop" # Start out withdrawn (hidden) wm withdraw $win Info $oop win $win # # Menus # InitMenus $oop # # Toolbar # set toolId [NSObject::New NSToolbar 20 $win] NSToolbar::AddTool $toolId -image Image_ButtonOptions \ -command "DoKeymapCmd \033 = {}" -showlabel no NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -command "DoKeymapCmd \033 ? {}" -showlabel no # # List # frame $win.frame \ -borderwidth 1 -relief sunken # Get the desired font set font [Value font,knowledge] # Column titles set canvas2 $win.frame.header canvas $canvas2 \ -borderwidth 0 -highlightthickness 0 MakeStatus $oop $canvas2 0 0 400 foo set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] $canvas2 configure -height $height foreach title {Name Items Status} anchor {nw ne ne nw} { $canvas2 create text 0 3 -text $title -fill gray -anchor $anchor \ -font $font -tags header,$title } Info $oop header,canvas $canvas2 set rowHgt [font metrics $font -linespace] if {[icon size] > $rowHgt} { set rowHgt [icon size] } incr rowHgt 8 set color [format #%02x%02x%02x 0 0 153] set canvistId [NSObject::New NSCanvist $win.frame $rowHgt 400 250 \ "NSPets::NewItemCmd $oop" "NSPets::HighlightItemCmd $oop"] set canvas [NSCanvist::Info $canvistId canvas] $canvas configure -background $color -yscrollcommand "$win.frame.scroll set" scrollbar $win.frame.scroll \ -command "$canvas yview" -orient vertical # Update ourself when the font,knowledge value changes Info $oop clientId,font \ [NSValueManager::AddClient font,knowledge \ "NSPets::ValueChanged_font_knowledge $oop"] # When a pet is selected, recall it NSCanvist::Info $canvistId selectionCmd \ "NSPets::SelectionChanged $oop" bind $canvas \ "NSPets::Configure $oop" Info $oop canvistId $canvistId Info $oop canvas $canvas pack $win.frame.scroll -side right -fill y pack $win.frame.header -side top -expand no -fill x pack $canvas -side left -expand yes -fill both -anchor nw # # Statusbar # MakeStatusBar $win.statusBar 20 # # Geometry # grid rowconfig $win 0 -weight 0 -minsize 0 grid rowconfig $win 1 -weight 1 -minsize 0 grid rowconfig $win 2 -weight 0 -minsize 0 grid columnconfig $win 0 -weight 1 -minsize 0 pack forget [NSToolbar::Info $toolId frame] grid [NSToolbar::Info $toolId frame] -in $win \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew -pady 0 grid $win.frame \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew # # Context Menu # set m $win.context menu $m -tearoff 0 bind $canvas \ "NSPets::ContextMenu $oop $m %X %Y" # # Feed Term when keys pressed # focus $canvas Term_KeyPress_Bind $canvas bind $win "NSPets::Close $oop" # # Synch the scrollbars when window is shown. # NSUtils::SynchScrollBar $canvas $win.frame.scroll return } # NSPets::InitMenus -- # # Create the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::InitMenus {oop} { global NSPets global NSMenu variable Priv set win [Info $oop win] set mod "Ctrl" # # Menu bar # set mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSPets::SetupMenus $oop" -identifier MENUBAR] Info $oop mbarId $mbar # Context-sensitive help NSMenu::Info $mbar menuSelectCmd "NSPets::MenuSelect $oop" # # Pets Menu # set menuId [NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_PETS] NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_PETS -label "Pets" -underline 0 -identifier M_PETS set entries {} lappend entries [list -type command -label "Dismiss Selected" \ -command "NSPets::Dismiss $oop selected" -underline 0 \ -identifier E_DISMISS_SELECTED] lappend entries [list -type command -label "Dismiss All" \ -command "NSPets::Dismiss $oop all" -underline 8 \ -identifier E_DISMISS_ALL] lappend entries [list -type separator] lappend entries [list -type command -label "Close" \ -command "NSPets::Close $oop" -underline 0 \ -accelerator $mod+W -identifier E_CLOSE] NSMenu::MenuInsertEntries $mbar -end MENU_PETS $entries # # Command Menu # set menuId [NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_COMMAND] NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_COMMAND -label "Command" -underline 0 -identifier M_COMMAND set entries {} lappend entries [list -type radiobutton -label "Stay Close" \ -variable NSPets($oop,pet_follow_distance) \ -value [const PET_CLOSE_DIST] -identifier E_CLOSE_DIST] lappend entries [list -type radiobutton -label "Follow Me" \ -variable NSPets($oop,pet_follow_distance) \ -value [const PET_FOLLOW_DIST] -identifier E_FOLLOW] lappend entries [list -type radiobutton -label "Seek And Destroy" \ -variable NSPets($oop,pet_follow_distance) \ -value [const PET_DESTROY_DIST] -identifier E_DESTROY] lappend entries [list -type radiobutton -label "Give Me Space" \ -variable NSPets($oop,pet_follow_distance) \ -value [const PET_SPACE_DIST] -identifier E_SPACE] lappend entries [list -type radiobutton -label "Stay Away" \ -variable NSPets($oop,pet_follow_distance) \ -value [const PET_AWAY_DIST] -identifier E_AWAY] lappend entries [list -type separator] lappend entries [list -type checkbutton -label "Open Doors" \ -underline 0 \ -variable NSPets($oop,pet_open_doors) -identifier E_DOORS] lappend entries [list -type checkbutton -label "Pick Up Items" \ -underline 0 \ -variable NSPets($oop,pet_pickup_items) -identifier E_ITEMS] NSMenu::MenuInsertEntries $mbar -end MENU_COMMAND $entries return } # NSPets::SetupMenus -- # # Prepare to post the menus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::SetupMenus {oop mbarID} { if {[Info $oop current] != -1} { lappend identList E_DISMISS_SELECTED } if {[llength [angband player pets]]} { lappend identList E_DISMISS_ALL set letters bcdefgh } else { set letters abcdefg } lappend identList E_CLOSE set index -1 foreach ident [list E_CLOSE_DIST E_FOLLOW E_DESTROY E_SPACE \ E_AWAY E_DOORS E_ITEMS] { lappend identList $ident set char [string index $letters [incr index]] NSMenu::EntryConfigure $mbarID $ident -accelerator $char \ -command "angband keypress $char" } NSMenu::MenuEnable $mbarID $identList [Info $oop win].statusBar cover show return } # NSPets::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::MenuSelect {oop menuId index ident} { switch -- $ident { {} { set desc {} } M_PETS { set desc "Contains commands for dismissing pets." } E_DISMISS_SELECTED { set desc "Dismisses selected pets." } E_DISMISS_ALL { set desc "Dismisses all pets." } E_CLOSE { set desc "Closes the window." } M_COMMAND { set desc "Contains commands for controlling pets." } E_CLOSE_DIST { set desc "Pets follow closely. (dist [const PET_CLOSE_DIST])" } E_FOLLOW { set desc "Pets follow nearby. (dist [const PET_FOLLOW_DIST])" } E_DESTROY { set desc "Pets hunt nearby monsters. (dist [const PET_DESTROY_DIST])" } E_SPACE { set desc "Pets keep a short distance away." } E_AWAY { set desc "Pets roam freely." } E_DOORS { set desc "Allow pets to open doors." } E_ITEMS { set desc "Allow pets to pick up items." } default { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } [Info $oop win].statusBar cover set $desc if {![string length $desc]} { if {$menuId == [Info $oop mbarId]} { [Info $oop win].statusBar cover hide } } return } # NSPets::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::DisplayCmd {oop message first} { variable Priv switch -- $message { preDisplay { if 0 { # XXX Hack -- Synchronzize the radiobutton menu entries Info $oop pet_follow_distance \ [struct set player_type 0 pet_follow_distance] # XXX Hack -- Synchronzize the checkbutton menu entries Info $oop pet_open_doors \ [struct set player_type 0 pet_open_doors] Info $oop pet_pickup_items \ [struct set player_type 0 pet_pickup_items] SetList $oop } } postDisplay { } postWithdraw { NSCanvist::DeleteAll [Info $oop canvistId] } } return } # NSPets::Close -- # # Do something when closing the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::Close {oop} { angband keypress \033 return } # NSPets::SelectionChanged -- # # When a list item is highlighted, display memory for the highlighted # pet in the Recall Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::SelectionChanged {oop canvistId select deselect} { # Do nothing if no new row was selected if {![llength $select]} { Info $oop current -1 StatusBar $oop "" 0 return } # Get the (first) row set row [lindex $select 0] Info $oop current $row set r_idx [lindex [Info $oop r_match] $row] # Display memory for the selected pet #NSRecall::RecallMonster $r_idx return } # NSPets::StatusBar -- # # Display text in the status bar, perhaps clearing it later. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::StatusBar {oop text zap} { set win [Info $oop win] set label [$win.statusBar itemcget t1 -label] $label configure -text $text if {$zap} { NSUtils::ZapLabel $label } return } # NSPets::NewItemCmd -- # # Called by NSCanvist::InsertItem() to create a list row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::NewItemCmd {oop canvistId y iconSpec name numObjects status {color White}} { variable Priv set c [NSCanvist::Info $canvistId canvas] set lineHeight [NSCanvist::Info $canvistId rowHgt] set font [Value font,knowledge] set fh [font metrics $font -linespace] set diff [expr {int([expr {($lineHeight - $fh) / 2}])}] if 0 { # Image if {[string length $iconSpec]} { set iw [icon size] set ih [icon size] set wid [expr {[icon size] + 8}] set xdiff [expr {int([expr {($wid - $iw) / 2}])}] set ydiff [expr {int([expr {($lineHeight - $ih) / 2}])}] lappend itemIdList [$c create widget $xdiff [expr {$y + $ydiff}] \ -assign $iconSpec] } } # Name lappend itemIdList [$c create text 0 [expr {$y + $diff}] \ -text $name -anchor nw -font $font -fill $color -tags name] # Option: Pets can pick up items if {[Info $oop pet_pickup_items]} { # Number of objects lappend itemIdList [$c create text 0 [expr {$y + $diff}] \ -text $numObjects -anchor ne -font $font -fill $color \ -tags objects] } # Status lappend itemIdList [$c create text 0 [expr {$y + $diff}] \ -text $status -anchor nw -font $font -fill $color \ -tags status] # Selection rectangle around everything lappend itemIdList [$c create rectangle 2 [expr {$y + 2}] \ 2 [expr {$y + $lineHeight - 2}] -fill {} -outline {} \ -tags selrect -width 2.0] # Maximum width of name set width [font measure $font $name] if {$width > $Priv(width,name)} { set Priv(width,name) $width } # Maximum width of objects set width [font measure $font $numObjects] if {$width > $Priv(width,objects)} { set Priv(width,objects) $width } # Maximum width of status set width [font measure $font $status] if {$width > $Priv(width,status)} { set Priv(width,status) $width } return $itemIdList } # NSPets::PositionItems -- # # Arranges all the canvas items in the list. This is called after all # the items are added, and when the event indicates the # window has been resized. This is the routine that lets variable-width # fonts work. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::PositionItems {oop} { variable Priv set win [Info $oop win] set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] # Get the header canvas set header [Info $oop header,canvas] # Get the font set font [Value font,knowledge] set offset [expr {[icon size] + 8}] # The list is empty if {![NSCanvist::Info $canvistId count]} { if {[Info $oop pet_pickup_items]} { set titleList [list Name Items Status] set alignList [list nw ne ne nw] } else { $header coords header,Items 0 3 set titleList [list Name Status] set alignList [list nw ne nw] } foreach title $titleList align $alignList { set label $title if {[string equal $align ne]} { incr offset [font measure $font $label] } $header coords header,$title $offset 3 if {[string equal $align nw]} { incr offset [font measure $font $label] } incr offset 16 } # Done return } # Get the width of the canvas set canvasWidth [winfo width $canvas] # Configure the name header $header coords header,Name $offset 3 # Position each name set coords [$canvas coords name] $canvas move name [expr {$offset - [lindex $coords 0]}] 0 incr offset $Priv(width,name) # Option: Pets can pick up items if {[Info $oop pet_pickup_items]} { # Position each objects incr offset $Priv(width,objects) set coords [$canvas coords objects] $canvas move objects [expr {$offset - [lindex $coords 0]}] 0 # Configure the objects header $header coords header,Items $offset 3 } else { # Configure the objects header $header coords header,Items 0 3 } # Position each status incr offset [font measure $font "AB"] set coords [$canvas coords status] $canvas move status [expr {$offset - [lindex $coords 0]}] 0 # Configure the status header $header coords header,Status $offset 3 # Position each selection rectangle set x1 [expr {($canvasWidth - 1) - 2}] foreach itemId [$canvas find withtag selrect] { scan [$canvas coords $itemId] "%s %s %s %s" c0 c1 c2 c3 $canvas coords $itemId $c0 $c1 $x1 $c3 } # Set the scrollregion to prevent horizontal scrolling scan [$canvas cget -scrollregion] "%s %s %s %s" x1 y1 x2 y2 $canvas configure -scrollregion "$x1 $y1 $canvasWidth $y2" return } # NSPets::HighlightItemCmd -- # # Called by NSCanvist::Select() to highlight a row. # # Arguments: # oop OOP ID. See above. # canvistId OOP ID of NSCanvist object. # state 1 or 0 highlight state. # args List of canvas item ids # # Results: # What happened. proc NSPets::HighlightItemCmd {oop canvistId state args} { set canvas [NSCanvist::Info $canvistId canvas] set itemIdList $args set idRect [FindItemByTag $canvas $itemIdList selrect] if {[NSUtils::HasFocus $canvas]} { set fill [Value listHilite] } else { set fill [Value listInactive] } if {$state} { $canvas itemconfigure $idRect -outline $fill } else { $canvas itemconfigure $idRect -fill {} -outline {} } return } # NSPets::Configure -- # # Respond to the event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::Configure {oop} { # Arrange list items PositionItems $oop # Resize the list header set canvas [Info $oop header,canvas] ConfigureStatus $oop $canvas 0 0 [winfo width $canvas] foo return } # NSPets::MakeStatus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::MakeStatus {oop canvas x y width where} { set font [Value font,magic] set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] # 3-pixel border + background $canvas create rectangle [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] \ -outline #282828 -fill #0000D4 -width 3.0 \ -tags $where,bg # 1-pixel border $canvas create rectangle [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] \ -outline #0070FF -fill {} \ -tags $where,bd return } proc NSPets::ConfigureStatus {oop canvas x y width where} { set font [Value font,knowledge] set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] # 3-pixel border + background $canvas coords $where,bg [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] # 1-pixel border $canvas coords $where,bd [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] return } # NSPets::ValueChanged_font_knowledge -- # # Called when the font,knowledge value changes. # Updates the Pets Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::ValueChanged_font_knowledge {oop} { set win [Info $oop win] # Get the desired font set font [Value font,knowledge] # Get the font height set fontHgt [font metrics $font -linespace] # Calculate row height set rowHgt $fontHgt if {[icon size] > $rowHgt} { set rowHgt [icon size] } incr rowHgt 8 # Set the row height of the list set canvistId [Info $oop canvistId] set canvas [Info $oop canvas] NSCanvist::Info $canvistId rowHgt $rowHgt $canvas configure -yscrollincrement $rowHgt # Set font of list header set canvas [Info $oop header,canvas] foreach title [list Name Items Status] { $canvas itemconfigure header,$title -font $font } # Resize the list header $canvas configure -height [expr {3 + $fontHgt + 3}] ConfigureStatus $oop $canvas 0 0 [winfo width $canvas] foo return } # NSPets::Dismiss -- # # Dismisses pets. This command relies on some C code which accepts # a list of m_list[] indexes of pets which should be dismissed. This # allows us to dimiss a pet on a certain row of the list, all # selected pets, or all pets of a certain race. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::Dismiss {oop action args} { set canvistId [Info $oop canvistId] switch -- $action { all { # Dismiss all pets (confirm) angband keypress a } one { # Dismiss pet on given row (no confirm) set row [lindex $args 0] set m_idx [lindex [Info $oop match] $row] angband keypress ~$m_idx\n\033 } race { # Dismiss pets with race of pet on given row (no confirm) set row [lindex $args 0] set r_idx [lindex [Info $oop r_match] $row] angband keypress ~ foreach m_idx [Info $oop match] r_idx2 [Info $oop r_match] { if {$r_idx2 == $r_idx} { angband keypress $m_idx\n } } angband keypress \033 } selected { # Dismiss selected pets (no confirm) angband keypress ~ foreach row [NSCanvist::Selection $canvistId] { set m_idx [lindex [Info $oop match] $row] angband keypress $m_idx\n } angband keypress \033 } } return } # NSPets::ContextMenu -- # # When an list item is right-clicked, pop up a context menu of actions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPets::ContextMenu {oop menu x y} { set canvistId [Info $oop canvistId] set canvas [Info $oop canvas] # Find the hit row set x1 [expr {$x - [winfo rootx $canvas]}] set y1 [expr {$y - [winfo rooty $canvas]}] set row [NSCanvist::PointToRow $canvistId $x1 $y1] # Clear the menu $menu delete 0 end if {$row != -1} { $menu add command -label "Dismiss This Pet" \ -command "NSPets::Dismiss $oop one $row" $menu add command -label "Dismiss This Type" \ -command "NSPets::Dismiss $oop race $row" } if {[llength [NSCanvist::Selection $canvistId]]} { $menu add command -label "Dismiss Selected" \ -command "NSPets::Dismiss $oop selected" } if {[llength [angband player pets]]} { $menu add command -label "Dismiss All" \ -command "NSPets::Dismiss $oop all" } if {[string compare [$menu index end] none]} { $menu add separator } $menu add command -label "Close" \ -command "NSPets::Close $oop" $menu add separator $menu add command -label "Cancel" # Pop up the menu tk_popup $menu $x $y return } zangband/lib/script/tk/player.tcl0000644000000000000000000002015510250356274016037 0ustar rootroot# File: player.tcl # Purpose: the Character Info Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSPlayer { variable likert_color variable Priv # namespace eval NSPlayer } # NSPlayer::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::InitModule {} { NSModule::AddModuleIfNeeded NSCharInfoCanvas [PathTk charinfo-canvas.tcl] NSModule::LoadIfNeeded NSCharInfoCanvas NSObject::New NSPlayer return } # NSPlayer::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::CloseModule {} { catch { destroy [Window player] NSWindowManager::UnregisterWindow player unset ::Windows(player) } return } # NSPlayer::NSPlayer -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::NSPlayer {oop} { InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow player $win \ "NSPlayer::GeometryCmd $oop" "" "NSPlayer::DisplayCmd $oop" # Destroy the object along with the canvas (later) NSUtils::DestroyObjectWithWidget NSPlayer $oop $win # # Global list of application windows # Global player,oop $oop Window player $win return } # NSPlayer::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::Info {oop info args} { global NSPlayer # Verify the object NSObject::CheckObject NSPlayer $oop # Set info if {[llength $args]} { switch -- $info { default { set NSPlayer($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSPlayer($oop,$info) } } } return } # NSPlayer::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::InitWindow {oop} { global NSPlayer global Windows variable Priv set win .player$oop toplevel $win wm title $win "Character Info" wm resizable $win no no # Character creation if {[info exists Windows(main)]} { wm transient $win [Window main] } # Start out withdrawn (hidden) wm withdraw $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "angband keypress \033" Info $oop win $win # # Menus # InitMenus $oop # # Main display # set canvasId [NSObject::New NSCharInfoCanvas $win] set canvas [NSCharInfoCanvas::Info $canvasId canvas] set font [NSCharInfoCanvas::Info $canvasId font,font] set fontHeight [NSCharInfoCanvas::Info $canvasId font,height] NSCharInfoCanvas::Info $canvasId topRow 2 NSCharInfoCanvas::AddTextItem $canvasId 1 0 -1 White "" left prompt pack $canvas \ -expand no set Priv(font,font) [NSCharInfoCanvas::Info $canvasId font,font] set Priv(font,height) $fontHeight set Priv(font,width) [NSCharInfoCanvas::Info $canvasId font,width] Info $oop canvasId $canvasId Info $oop canvas $canvas # # Feed Term when keys pressed # bind $win { angband keypress %A } bind $win { angband_display playerflags show } return } # NSPlayer::InitMenus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::InitMenus {oop} { } # NSPlayer::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::DisplayCmd {oop message first} { switch -- $message { preDisplay { SetInfo $oop } postDisplay { } } return } # NSPlayer::GeometryCmd -- # # Called by NSWindowManager::Setup(). Returns the desired (default) # geometry for the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::GeometryCmd {oop} { set win [Info $oop win] set winMain [Window main] set x [winfo x $winMain] set y [winfo y $winMain] set width [winfo width $win] set height [winfo height $win] return ${width}x$height+[winfo x $winMain]+[winfo y $winMain] } # NSPlayer::AddTextItem -- # # Create a new canvas text item with given option values. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::AddTextItem {oop col row width fill text justify tags} { NSCharInfoCanvas::AddTextItem [Info $oop canvasId] $col $row $width \ $fill $text $justify $tags return } # NSPlayer::WipeInfo -- # # Set text of character-specific items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::WipeInfo {oop} { NSCharInfoCanvas::WipeInfo [Info $oop canvasId] NSCharInfoCanvas::PositionItems [Info $oop canvasId] return } # NSPlayer::SetInfo -- # # Set text of character-specific items. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::SetInfo {oop} { NSCharInfoCanvas::SetInfo [Info $oop canvasId] return } # NSPlayer::GetName -- # # Allows the user to enter a new character name. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSPlayer::GetName_PutName {} { variable Priv set nameFrame $Priv(frame) set name $Priv(name) set fill White if {$Priv(default)} { set fill [Value TERM_YELLOW] } for {set i 0} {$i < 15} {incr i} { set letter $nameFrame.letter$i.label set char [string index $name $i] if {$char != {}} { $letter configure -text $char -background $fill } else { $letter configure -text {} -background [Global SystemButtonFace] } } return } proc NSPlayer::GetName_KeyPress {ascii} { variable Priv # if {![string match {[a-zA-Z0-9 ]} $ascii]} return # Anything except a control character if {[string is control $ascii]} return if {$Priv(default)} { set Priv(name) "" set Priv(length) 0 set Priv(default) 0 } if {$Priv(length) == 15} return append Priv(name) $ascii incr Priv(length) GetName_PutName return } proc NSPlayer::GetName_Delete {} { variable Priv if {$Priv(default)} { set Priv(name) "" set Priv(length) 0 set Priv(default) 0 GetName_PutName return } if {!$Priv(length)} return incr Priv(length) -1 set end [expr {$Priv(length) - 1}] set Priv(name) [string range $Priv(name) 0 $end] GetName_PutName return } proc NSPlayer::GetName {oop default allowAbort} { global NSPlayer variable Priv # Get the canvas set canvas [Info $oop canvas] # Selection rectangle around canvas item set heightBy2 [expr {$Priv(font,height) / 2}] set width [expr {$Priv(font,width) * 15 + 2}] set bbox [$canvas bbox name] set bbox [lreplace $bbox 2 2 [expr {[lindex $bbox 0] + $width}]] eval $canvas create rectangle $bbox -outline [Value TERM_L_RED] \ -width 2.0 -tags selectBox $canvas create line [lindex $bbox 2] [expr {[lindex $bbox 1] + $heightBy2}] \ [expr {[lindex $bbox 2] + 30}] [expr {[lindex $bbox 1] + $heightBy2}] \ -width 2.0 -fill [Value TERM_L_RED] -tags selectBox # Create a frame with 15 one-character label widgets set frame $canvas.nameFrame frame $frame \ -borderwidth 2 -relief ridge for {set i 0} {$i < 15} {incr i} { set letterBox $frame.letter$i frame $letterBox \ -borderwidth 1 -relief sunken label $letterBox.label \ -font $Priv(font,font) -width 1 pack $letterBox \ -side left -padx 1 -expand no pack $letterBox.label } place $frame \ -x [expr {[lindex $bbox 2] + 30}] \ -y [expr {[lindex $bbox 1] + $heightBy2}] \ -anchor w bindtags $frame $frame bind $frame "NSPlayer::GetName_KeyPress %A" bind $frame NSPlayer::GetName_Delete bind $frame "set NSPlayer::Priv(result) 1" if {$allowAbort} { bind $frame "set NSPlayer::Priv(result) 0" } set Priv(frame) $frame # Set a grab and claim the focus too. NSUtils::GrabSave $frame focus $frame set Priv(result) -1 set Priv(name) $default set Priv(length) [string length $Priv(name)] set Priv(default) $Priv(length) GetName_PutName # Wait for the user to accept or cancel vwait NSPlayer::Priv(result) # Release grab and focus NSUtils::GrabRelease $frame $canvas delete selectBox destroy $frame if {$Priv(result)} { return $Priv(name) } return {} } zangband/lib/script/tk/recall.tcl0000644000000000000000000006331610250356274016013 0ustar rootroot# File: recall.tcl # Purpose: the Recall Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSRecall { variable Priv # namespace eval NSRecall } # NSRecall::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::InitModule {} { variable Priv set Priv(icon,valid) 0 set Priv(icon,known) 0 set oop [NSObject::New NSRecall] # Update ourself when the font for the Recall Window changes NSValueManager::AddClient font,recall \ "NSRecall::ValueChanged_font_recall $oop" return } # NSRecall::NSRecall -- # # Object constructor called by NSObject::New(). # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSRecall::NSRecall {oop} { Info $oop showIcon [Value recall,showicon] InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow recall $win \ "NSRecall::GeometryCmd $oop" \ "" \ "NSRecall::DisplayCmd $oop" # If the Choice Window is displayed, we don't show choices in # the Recall Window. Info $oop clientId,choicewindow \ [NSValueManager::AddClient choicewindow,show { NSRecall::SetHook [Global recall,oop] "" }] if {$::DEBUG} { set ::debug_display 0 } Info $oop hook "" Info $oop busy 0 Info $oop expanded 0 Info $oop current "" Info $oop inConfigure 0 # Kind of information currently displayed Info $oop display "" Info $oop monsterMem "" # # Global list of application windows # Global recall,oop $oop Window recall $win return } # NSRecall::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Info {oop info args} { global NSRecall # Verify the object NSObject::CheckObject NSRecall $oop # Set info if {[llength $args]} { switch -- $info { default { set NSRecall($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSRecall($oop,$info) } } } return } # NSRecall::InitWindow -- # # Create a recall window. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSRecall::InitWindow {oop} { set win .recall$oop toplevel $win wm title $win "Recall" wm transient $win [Window main] # Feed the Term when keys are pressed Term_KeyPress_Bind $win # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSRecall::Close $oop" # Start out withdrawn (hidden) wm withdraw $win # Turn off geometry propagation for the window pack propagate $win no # Set instance variables Info $oop win $win set frame $win.frame frame $frame -relief sunken -borderwidth 1 -background Black # Canvas to display icon set iconSize [expr {[icon size] + 8}] set canvas $frame.icon canvas $canvas \ -borderwidth 0 -width $iconSize -height $iconSize -background Black \ -highlightthickness 0 if 0 { $canvas create widget \ 6 6 -tags icon } $canvas create rectangle \ 4 4 [expr {6 + [icon size] + 1}] [expr {6 + [icon size] + 1}] \ -outline Black -tags focus if 0 { # Problems with highlight when Knowledge window appears, so skip it $canvas bind icon { # %W itemconfigure focus -outline gray60 } $canvas bind icon { %W itemconfigure focus -outline Black } $canvas bind icon " $canvas move icon 1 1 set CanvasButtonDown 1 " $canvas bind icon " $canvas move icon 1 1 set CanvasButtonDown 1 " $canvas bind icon " $canvas move icon -1 -1 set CanvasButtonDown 0 " $canvas bind icon " if {\$CanvasButtonDown} { $canvas move icon -1 -1 update idletasks NSRecall::DisplayKnowledge $oop } " } # Create an arrow which appears when there is content out of site set x [expr {$iconSize / 2}] $canvas create polygon [expr {$x - 3}] 46 [expr {$x + 3}] 46 \ $x 49 -fill Red -outline Red -tags arrow set wrap word text $frame.text \ -wrap $wrap -width 1 -height 1 -font [Value font,recall] \ -borderwidth 0 -setgrid no -highlightthickness 0 \ -padx 4 -pady 2 -background Black -foreground White -cursor "" bindtags $frame.text [list $frame.text $win all] pack $frame \ -expand yes -fill both grid rowconfig $frame 0 -weight 1 grid columnconfig $frame 0 -weight 0 grid columnconfig $frame 1 -weight 1 grid $frame.icon -in $frame \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ns grid $frame.text -in $frame \ -row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news if {![Info $oop showIcon]} { grid remove $frame.icon } # Set instance variables Info $oop icon $frame.icon Info $oop text $frame.text # Window expands and contracts as the mouse enters and leaves it bindtags $win [concat [bindtags $win] RecallBindTag] bind RecallBindTag "NSRecall::Expand $oop" bind RecallBindTag "NSRecall::Contract $oop" # When the window changes size, reposition the indicator arrow bind $frame.text \ "NSRecall::Configure $oop" # Fiddle with the selection for list behaviour $frame.text tag configure HOT -foreground White \ -background [Value listHilite] $frame.text tag bind HOT \ "NSRecall::Invoke $oop \[$frame.text index {@%x,%y linestart}]" $frame.text tag bind TEXT \ "NSRecall::Motion $oop \[$frame.text index {@%x,%y linestart}]" $frame.text tag bind HOT \ "NSRecall::Motion $oop {}" if {[Platform unix]} { # When the inactive window is clicked, I get a event # followed by an event. The Contract()'s the window # and removes the highlight (if any). bind RecallWindowBindTag " if {!\[NSRecall::HasCursor $oop]} { NSRecall::Contract $oop } " $frame.text tag bind HOT " if {!\[NSRecall::CursorHot $oop %x %y]} { NSRecall::Motion $oop {} } " bind $win " if {!\[NSRecall::HasCursor $oop]} { NSRecall::Motion $oop {} } " proc CursorHot {oop x y} { set text [Info $oop text] if {![llength [$text tag ranges HOT]]} {return 0} set index [$text index @$x,$y] if {[$text compare $index < HOT.first] || [$text compare $index > HOT.last]} {return 0} return 1 } } # # Context Menu # set menu $win.context menu $menu -tearoff 0 bind $frame.icon \ "NSRecall::ContextMenu $oop $menu %X %Y" bind $frame.text \ "NSRecall::ContextMenu $oop $menu %X %Y" return } # NSRecall::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::DisplayCmd {oop message first args} { switch -- $message { preDisplay { } postDisplay { Value recall,show 1 } postWithdraw { SetHook $oop "" Value recall,show 0 } } return } # NSRecall::GeometryCmd -- # # Called by NSWindowManager::Setup(). Returns the desired (default) # geometry for the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::GeometryCmd {oop} { set win [Info $oop win] set winMain [Window main] set spacing 0 set left 0 set top 0 set right [winfo screenwidth .] set bottom [winfo screenheight .] set x [NSToplevel::FrameLeft $winMain] if {[Value choicewindow,show]} { set width [NSToplevel::ContentWidth $win \ [expr {[NSToplevel::TotalWidth $winMain] / 2}]] } else { set width [NSToplevel::TotalWidth $winMain] if {$width > 400} { set width 400 } set width [NSToplevel::ContentWidth $win $width] } set y [expr {[NSToplevel::FrameBottom $winMain] + $spacing}] if {$bottom - $y < 100} { set y [expr {$bottom - 100}] set height [NSToplevel::ContentHeight $win 100] } elseif {($y + [NSToplevel::TotalHeight $win]) < $bottom} { set height [winfo height $win] } else { set height [expr {$bottom - [NSToplevel::FrameBottom $winMain]}] set height [NSToplevel::ContentHeight $win $height] } return ${width}x$height+$x+$y } # NSRecall::Close -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSRecall::Close {oop} { NSWindowManager::Undisplay recall return } # NSRecall::RecallSpell -- # # Show info about a spell. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::RecallSpell {bookNum index} { variable Priv if {![Value recall,show]} return # Hack -- Get the object id set oop [Global recall,oop] # If we are in "list mode", don't clobber the text if {[string length [Info $oop hook]]} return # Get information about the spell angband spell info $bookNum $index attrib # Get the book icon set icon [angband k_info info $bookNum icon] # Color switch -- $attrib(info) { unknown { set color gray70 # The character can learn this spell if {[angband player new_spells] && ($attrib(level) <= [angband player level])} { set color [Value TERM_L_GREEN] } } untried { set color [Value TERM_L_BLUE] } default { set color White } } # Get the name set name $attrib(name): # Get the memory set memory [angband spell memory $bookNum $index] # Extra info if {[string length $memory]} { append memory \n } append memory "Level $attrib(level) Mana $attrib(mana) \ Fail $attrib(chance)%" if {[string length $attrib(info)]} { append memory "\n$attrib(info)" } # Set the text SetText $oop $icon $color $name $memory return } # NSRecall::SetText -- # # Description. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSRecall::SetText {oop icon color title text} { global NSRecall variable Priv set win [Info $oop win] set textBox [Info $oop text] # If we are in "list mode", then do not set the text. This may # happen if we are waiting for an object to be chosen, and the # user highlights an object in the Inventory Window, which would # ordinarily display the object memory. if {[string length [Info $oop hook]]} { return } # Display the icon [Info $oop icon] itemconfigure icon -assign $icon # Delete $textBox delete 1.0 end # Insert title if any if {[string length $title]} { # Title (color?) $textBox insert end $title\n $textBox tag add TAG_STYLE 1.0 {end -1 chars} $textBox tag configure TAG_STYLE -foreground $color } # Insert text if any set text [string trim $text] if {[string length $text]} { # Text $textBox insert end $text } set Priv(icon,valid) 0 # Synchronize the indicator arrow ContentChanged $oop return } # NSRecall::IconChanged -- # # The icon of the recalled monster/object is displayed in the # Recall Window. If that monster or object is assigned a different # icon, we want to update the display. This is called as a # qebind command on the "Assign" quasi-event. # # Arguments: # oop OOP ID. See above. # # Results: # What happened. proc NSRecall::IconChanged {oop to toindex assign} { variable Priv if {!$Priv(icon,valid)} return if {[string equal $to $Priv(icon,to)] && ($toindex == $Priv(icon,toindex))} { [Info $oop icon] itemconfigure icon -assign $assign } return } # NSRecall::DisplayKnowledge -- # # Display the Knowledge Window for the displayed monster or object. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::DisplayKnowledge {oop} { variable Priv if {!$Priv(icon,valid)} return # This can't work when an unknown flavored object is displayed if {!$Priv(icon,known)} return if {[string compare $Priv(icon,to) monster] && [string compare $Priv(icon,to) object]} { return } angband_display knowledge show $Priv(icon,to) $Priv(icon,toindex) return } # NSRecall::SetHook -- # # Set the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::SetHook {oop hook} { if {[string length $hook]} { Info $oop hook NSRecall::$hook CallHook $oop open if {$::DEBUG} { set ::debug_display 1 } } elseif {[string length [Info $oop hook]]} { Info $oop hook "" Restore $oop if {$::DEBUG} { set ::debug_display 0 } } return } # NSRecall::CallHook -- # # Call the hook. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::CallHook {oop message args} { return [uplevel #0 [Info $oop hook] $oop $message $args] } # NSRecall::Fresh_Display -- # # Calls the hook to set the list, if required. Called as a command # on the "Term-fresh" quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Fresh_Display {oop} { ASSERT {$::debug_display == 1} \ "Fresh_Display called with debug_display=0!" CallHook $oop fresh # If the cursor is inside the Recall Window, we will attempt to # expand it. set pointerx [winfo pointerx .] set pointery [winfo pointery .] set toplevel [winfo containing $pointerx $pointery] if {[string length $toplevel] && \ [string equal [winfo toplevel $toplevel] [Info $oop win]]} { Expand $oop } return } # NSRecall::SetList -- # # Clears the recall text, sets the icon to "none 0" and calls the # hook to set the text. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::SetList {oop} { set win [Info $oop win] set textBox [Info $oop text] # Clear the text $textBox delete 1.0 end # Clear the icon [Info $oop icon] itemconfigure icon -assign {icon none 0} # Call the hook to set the list CallHook $oop set_list # Something is displayed Info $oop display something # No item is highlighted Info $oop current "" # Synchronize the indicator arrow ContentChanged $oop return } # NSRecall::Invoke -- # # Called when a list item is clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Invoke {oop index} { set textBox [Info $oop text] set index [Info $oop current] set row [expr {[lindex [split $index .] 0] - 1}] CallHook $oop invoke $row return } # NSRecall::Motion -- # # Called when the mouse moves in a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Motion {oop index} { # If you invoke an item, hold down the mouse, and drag... if {![string length [Info $oop hook]]} return # No tracking while menu is up if {[Info $oop busy]} return # See if the item has changed if {$index == [Info $oop current]} return # An item is highlighted if {[string length [Info $oop current]]} { # Remove highlighting UnhighlightItem $oop [Info $oop current] } # An item is under the pointer if {[string length $index]} { # Highlight the item HighlightItem $oop $index } # Remember which item is highlighted Info $oop current $index return } # NSRecall::HighlightItem -- # # Highlights a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::HighlightItem {oop index} { set textBox [Info $oop text] set row [expr {[lindex [split $index .] 0] - 1}] # Highlight the item $textBox tag add HOT $index "$index lineend" $textBox tag raise HOT # Call the hook (to set the icon, for example) CallHook $oop highlight $row return } # NSRecall::UnhighlightItem -- # # Removes highlighting from a list item. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::UnhighlightItem {oop index} { set win [Info $oop win] set textBox [Info $oop text] # Unhighlight the item $textBox tag remove HOT 1.0 end # Clear the icon [Info $oop icon] itemconfigure icon -assign {icon none 0} return } # NSRecall::HasCursor -- # # See if the cursor is over the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::HasCursor {oop} { set pointerx [winfo pointerx .] set pointery [winfo pointery .] set window [winfo containing $pointerx $pointery] if {![string length $window]} { return 0 } if {[string compare [winfo toplevel $window] [Info $oop win]]} { return 0 } return 1 } # NSRecall::Expand -- # # Resizes the Recall Window to display all of the information in it. # Does nothing if the window is already expanded. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Expand {oop} { variable Priv # if {![string length [Info $oop hook]]} return if {[Info $oop busy]} return if {[Info $oop expanded]} return set win [Info $oop win] set textBox [Info $oop text] set textHeight [winfo height $textBox] set lineHeight [font metrics [Value font,recall] -linespace] # Hack -- In order to find out how much space is taken up by the # text in the text widget, I create a canvas text item with the # proper attributes and calculate its size. The width is width-8 # and height-4 because of the internal padding of the text # widget. I added 2 to each adjustment as a hack. set padx [$textBox cget -padx] set pady [$textBox cget -pady] set itemId [[Info $oop icon] create text 1 1 -font [Value font,recall] \ -width [expr {[winfo width $textBox] - $padx * 2 - 1}] -anchor nw \ -text [$textBox get 1.0 end]] set bbox [[Info $oop icon] bbox $itemId] set height [expr {[lindex $bbox 3] - [lindex $bbox 1] + $pady * 2 + 2}] # Hmmm... Is there a trailing newline, or what? incr height -$lineHeight # Delete the temp canvas item [Info $oop icon] delete $itemId set winHeight [winfo height $win] set winWidth [winfo width $win] if {$height <= $winHeight} return # If the window is closer to the top of the screen, then # expand downwards, otherwise expand upwards. set top [NSToplevel::FrameTop $win] set topDist $top if {$topDist < 0} {set topDist 0} set bottom [NSToplevel::FrameBottom $win] set bottomDist [expr {[winfo screenheight $win] - $bottom}] if {$bottomDist < 0} {set bottomDist 0} if {$topDist < $bottomDist} { set expandUp 0 } else { set expandUp 1 } # Save the current window geometry Info $oop geometry [wm geometry $win] Info $oop busy 1 raise $win set x [NSToplevel::FrameLeft $win] if {$expandUp} { set y [expr {[NSToplevel::FrameTop $win] - ($height - $winHeight)}] } else { set y [NSToplevel::FrameTop $win] } wm geometry $win ${winWidth}x$height+$x+$y update Info $oop expanded 1 Info $oop busy 0 # If the cursor moved outside the Recall Window, collapse it if {![HasCursor $oop]} { Contract $oop } return } # NSRecall::Contract -- # # Restores the window geometry to the size it was before it was # expanded. Does nothing if the window is not expanded. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Contract {oop} { if {[Info $oop busy]} return if {![Info $oop expanded]} return Info $oop busy 1 set win [Info $oop win] wm geometry $win [Info $oop geometry] update Info $oop expanded 0 Info $oop busy 0 return } # NSRecall::Restore -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Restore {oop} { if {![string length [Info $oop display]]} return SetText $oop {icon none 0} {} {} {} Contract $oop Info $oop display "" return } # NSRecall::ContextMenu -- # # When the window is right-clicked, pop up a menu of options. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::ContextMenu {oop menu x y} { set text [Info $oop text] $menu delete 0 end $menu add command -label "Set Font" \ -command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font recall" $menu add checkbutton -label "Show Icon" \ -variable ::NSRecall($oop,showIcon) \ -command "NSRecall::OptionChanged $oop showIcon showicon" $menu add separator $menu add command -label "Cancel" # Hack -- Try to prevent collapsing while popup is visible. # It would be nice if "winfo ismapped $menu" worked Info $oop busy 1 # Pop up the menu tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } Info $oop busy 0 set index "" if {[NSUtils::HasCursor $text]} { set x [expr {[winfo pointerx $text] - [winfo rootx $text]}] set y [expr {[winfo pointery $text] - [winfo rooty $text]}] set index2 [$text index @$x,$y] foreach tag [$text tag names $index2] { if {[string equal $tag TEXT]} { set index "$index2 linestart" break } } } Motion $oop $index return } # NSRecall::OptionChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::OptionChanged {oop info keyword} { set setting [Info $oop $info] Value recall,$keyword $setting switch -- $keyword { showicon { if {$setting} { grid [Info $oop icon] } else { grid remove [Info $oop icon] } } } return } # NSRecall::Configure -- # # Called as a event script. Positions the indicator # arrow (the one which tells us if there is more information out # of site) near the bottom of the window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Configure {oop} { set win [Info $oop win] set canvas [Info $oop icon] set text [Info $oop text] scan [$canvas bbox arrow] "%s %s %s %s" left top right bottom set height [winfo height $text] $canvas move arrow 0 [expr {$height - $bottom - 4}] ContentChanged $oop return } # NSRecall::ContentChanged -- # # Called when the information displayed has changed. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::ContentChanged {oop} { set win [Info $oop win] set canvas [Info $oop icon] set text [Info $oop text] scan [$text yview] "%f %f" top bottom if {$bottom < 1} { set fill Red } else { set fill [$canvas cget -background] } $canvas itemconfigure arrow -fill $fill -outline $fill return } # NSRecall::Choose -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::Choose {oop what show args} { if {[lsearch -exact [list cmd_pet ele_attack item] \ $what] == -1} return if {!$show} { SetHook $oop {} return } switch -- $what { cmd_pet { SetHook $oop hook_cmd_pet } ele_attack { SetHook $oop hook_ele_attack } } return } proc NSRecall::MenuSelect {menu hook} { set index [$menu index active] eval $hook [Global recall,oop] menu_select $menu $index return } proc NSRecall::PetCmdInfo {_mode} { upvar $_mode mode set letters abcdefgh set index -1 if {[llength [angband player pets]]} { set char [string index $letters [incr index]] lappend data $char "Dismiss pets" } # set dist [struct set player_type 0 pet_follow_distance] set mode "" set char [string index $letters [incr index]] lappend data $char "Stay close" if {$dist == [const PET_CLOSE_DIST]} { set mode $char } set char [string index $letters [incr index]] lappend data $char "Follow me" if {$dist == [const PET_FOLLOW_DIST]} { set mode $char } set char [string index $letters [incr index]] lappend data $char "Seek and destroy" if {$dist == [const PET_DESTROY_DIST]} { set mode $char } set char [string index $letters [incr index]] lappend data $char "Give me space" if {$dist == [const PET_SPACE_DIST]} { set mode $char } set char [string index $letters [incr index]] lappend data $char "Stay away"] if {$dist == [const PET_AWAY_DIST]} { set mode $char } set char [string index $letters [incr index]] lappend data $char "Allow open doors" set char [string index $letters [incr index]] lappend data $char "Allow pickup items" return $data } proc NSRecall::hook_cmd_pet {oop message args} { switch -- $message { open { } fresh { SetList $oop } close { } set_list { set textBox [Info $oop text] # Keep a list of invoke chars set match {} # Process each command foreach {char label} [PetCmdInfo mode] { if {[string equal $char $mode]} { set color [Value TERM_L_BLUE] } else { set color White } # Append the character and description $textBox insert end "$char\) " TEXT $label \ [list ITEM_$char TEXT] "\n" $textBox tag configure ITEM_$char -foreground $color # Keep a list of chars and colors lappend match $char lappend colors $color } # Delete trailing newline $textBox delete "end - 1 chars" # Keep a list of chars and colors Info $oop match $match Info $oop color $colors } get_color { set row [lindex $args 0] return [lindex [Info $oop color] $row] } invoke { set row [lindex $args 0] set char [lindex [Info $oop match] $row] angband keypress $char } highlight { } } return } # NSRecall::PopupSelect_CmdPet -- # # Show a pop-up menu of pet commands. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::PopupSelect_CmdPet {menu x y} { global PopupResult set PopupResult 0 # Clear the menu $menu delete 0 end set num 0 foreach {char name} [PetCmdInfo mode] { if {[string equal $char $mode]} { set ::PopupCheck 1 $menu add checkbutton -label "$char $name" \ -command "angband keypress $char ; set PopupResult 1" \ -underline 0 -variable ::PopupCheck } else { $menu add command -label "$char $name" \ -command "angband keypress $char ; set PopupResult 1" \ -underline 0 } incr num } $menu add separator $menu add command -label "Cancel" # Pressing and holding Button-3, popping up, then letting go selects # an item, so wait a bit if it was a quick press-release after 100 tk_popup $menu $x $y [expr {$num / 2}] if {[Platform unix]} { tkwait variable ::tkPriv(popup) } # If the user unposts the menu without choosing an entry, then # I want to feed Escape into the Term. I tried binding to the # event but it isn't called on Windows(TM). after idle { if {!$PopupResult} { angband keypress \033 } } return } proc NSRecall::hook_xxx {oop message args} { switch -- $message { set_list { } get_color { } } return } # NSRecall::ValueChanged_font_recall -- # # Called when the font,recall value changes. # Updates the Recall Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSRecall::ValueChanged_font_recall {oop} { [Info $oop text] configure -font [Value font,recall] return } zangband/lib/script/tk/store.tcl0000644000000000000000000011341610250356274015702 0ustar rootroot# File: store.tcl # Purpose: the Store Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSStore { variable MenuString variable Priv # namespace eval NSStore } # NSStore::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::InitModule {} { InitImageIfNeeded Image_ButtonOptions button-options.gif InitImageIfNeeded Image_ButtonHelp button-help.gif NSModule::LoadIfNeeded NSToolbar NSObject::New NSStore return } # NSStore::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::CloseModule {} { catch { destroy [Window store] } return } # NSStore::NSStore -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::NSStore {oop} { Info $oop current -1 Info $oop toolbar,match {} InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow store $win \ "GetDefaultGeometry $win main2 main" "NSStore::SetupCmd $oop" \ "NSStore::DisplayCmd $oop" # # Global list of application windows # Global store,oop $oop Window store $win return } # NSStore::~NSStore -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::~NSStore {oop} { NSValueManager::RemoveClient listBG [Info $oop clientId,listBG] trace vdelete NSStore($oop,quantity) w "::NSStore::QuantityChanged $oop" return } # NSStore::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Info {oop info args} { global NSStore # Verify the object NSObject::CheckObject NSStore $oop # Set info if {[llength $args]} { switch -- $info { default { set NSStore($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSStore($oop,$info) } } } return } # NSStore::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::InitWindow {oop} { global NSToolbar set win .store$oop toplevel $win wm title $win Store wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSStore::Close $oop" # Start out withdrawn (hidden) wm withdraw $win Info $oop win $win InitMenus $oop # # Toolbar # set toolId [NSObject::New NSToolbar 20 $win] NSToolbar::AddTool $toolId -image Image_ButtonOptions \ -showlabel no -command "DoCommandIfAllowed =" -hasmenu yes \ -menucommand "NSStore::Win98MenuCmd_Options $oop" NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -showlabel no -command "DoCommandIfAllowed ?" MakeDivider [NSToolbar::Info $toolId frame].divider1 y pack [NSToolbar::Info $toolId frame].divider1 -side left -fill y -pady 2 set id [NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -command "DoCommandIfAllowed p" -label "Buy" -showlabel yes \ -showimage no -heightlabel 16 -hasmenu yes \ -menucommand "NSStore::Win98MenuCmd_Buy $oop"] set id2 [NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -command "DoCommandIfAllowed s" -label "Sell" -showlabel yes \ -showimage no -heightlabel 16 -hasmenu yes \ -menucommand "NSStore::Win98MenuCmd_Sell $oop"] set menuId [NSObject::New NSMenu [Info $oop mbarId] \ -tearoff 0 -identifier MENU_TOOLBAR -postcommand ";"] set menu [NSMenu::Info $menuId menu] if {[Platform unix]} { $menu configure -cursor arrow } Info $oop toolbar,menu $menu Info $oop toolbarId $toolId Info $oop toolbar,buyId $id Info $oop toolbar,sellId $id2 # # Divider + Store Info # if {[Platform unix]} { set font {Courier 12} } if {[Platform windows]} { set font {Courier 9} } set frame $win.info frame $frame \ -borderwidth 0 # frame $frame.divider1 \ # -borderwidth 1 -height 2 -relief groove label $frame.howMany \ -font $font -text "Quantity:" entry $frame.quantity \ -width 2 -state disabled -textvariable NSStore($oop,quantity) label $frame.howMuch \ -font $font -text "Total Cost:" label $frame.totalCost \ -font $font -text "" -anchor w label $frame.purse \ -font $font label $frame.playerGold \ -font $font -text "Gold Remaining:" label $frame.gold \ -font $font -text [angband player gold] label $frame.price_character \ -font $font -text "" -width 25 -anchor w label $frame.price_owner \ -font $font -text "" -width 25 -anchor w global NSStore trace variable NSStore($oop,quantity) w "::NSStore::QuantityChanged $oop" # This stops keys being fed to the Term # bindtags $frame.quantity "$frame.quantity Entry all" MakeDivider $win.divider2 x # # List # set cw [font measure [Value font,store] "W"] set width [expr {$cw * 81}] set iconSize [expr {[icon size] + 8}] frame $win.frame \ -borderwidth 1 -relief sunken set canvistId [NSObject::New NSCanvist $win.frame $iconSize $width 300 \ "NSStore::NewItemCmd $oop" "NSStore::HighlightItemCmd $oop"] set canvas [NSCanvist::Info $canvistId canvas] $canvas configure -background [Value listBG] $canvas configure -yscrollcommand "$win.frame.scroll set" scrollbar $win.frame.scroll \ -command "$canvas yview" -orient vert Info $oop clientId,listBG \ [NSValueManager::AddClient listBG \ "NSStore::ValueChanged_listBG $oop"] # When the window resizes, reposition the canvas items bind $canvas \ "NSStore::PositionItems $oop" Info $oop canvistId $canvistId pack $win.frame.scroll -side right -fill y pack $canvas -side left -expand yes -fill both # When an item is selected, recall it NSCanvist::Info $canvistId selectionCmd \ "NSStore::SelectionChanged $oop" # Double-click to purchase an item NSCanvist::Info $canvistId invokeCmd \ "NSStore::Invoke $oop" # Typing Enter in the Quantity Entry initiates a purchase bind $frame.quantity \ "NSStore::InvokeByReturn $oop" # # Statusbar # MakeStatusBar $win.statusBar 12 # # Geometry # grid rowconfig $win 0 -weight 0 -minsize 0 grid rowconfig $win 1 -weight 0 -minsize 0 grid rowconfig $win 2 -weight 0 -minsize 0 grid rowconfig $win 3 -weight 1 -minsize 0 grid rowconfig $win 4 -weight 0 -minsize 0 grid columnconfig $win 0 -weight 1 -minsize 0 pack forget $NSToolbar($toolId,frame) grid $NSToolbar($toolId,frame) -in $win \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.info -in $win \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.divider2 -in $win \ -row 2 -column 0 -rowspan 1 -columnspan 2 -sticky ew grid $win.frame -in $win \ -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky news grid $win.statusBar -in $win \ -row 4 -column 0 -rowspan 1 -columnspan 2 -sticky ew # # Context Menu # set m $win.context menu $m -tearoff 0 bind $canvas \ "NSStore::ContextMenu $oop $m %X %Y" # # Feed Term when keys pressed # Term_KeyPress_Bind $win Term_KeyPress_Bind $canvas # XXX Hack -- Don't feed Tab to the Term bind $canvas { focus [tk_focusNext %W] break } # # Synch the scrollbars when window is shown. # # bind $win.frame.scroll "NSStore::SynchScrollBars $oop" NSUtils::SynchScrollBar $canvas $win.frame.scroll bind $win " if {\[string equal %W $win]} { focus $canvas } " # Destroy the object along with the widget (later) NSUtils::DestroyObjectWithWidget NSStore $oop $win return } # NSStore::InitMenus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::InitMenus {oop} { variable MenuString set win [Info $oop win] # # Menu bar # set mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSStore::SetupMenus $oop" -identifier MENUBAR] Info $oop mbarId $mbar # Context-sensitive help NSMenu::Info $mbar menuSelectCmd "NSStore::MenuSelect $oop" # Call our command when an entry is invoked NSMenu::Info $mbar invokeCmd "NSStore::MenuInvoke $oop" # # Store Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_STORE NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_STORE -label "Store" -underline 0 -identifier M_STORE set entries {} lappend entries [list -type command -label "Buy" -identifier E_STORE_BUY] lappend entries [list -type command -label "Sell" -identifier E_STORE_SELL] lappend entries [list -type separator -identifier E_SEP_1] lappend entries [list -type separator] lappend entries [list -type command -label "Leave" \ -identifier E_STORE_EXIT] NSMenu::MenuInsertEntries $mbar -end MENU_STORE $entries set MenuString(M_STORE) \ "Contains store-related commands." set MenuString(E_STORE_BUY,home) \ "Take an item from the Home." set MenuString(E_STORE_BUY,store) \ "Purchase an item from the store." set MenuString(E_STORE_SELL,home) \ "Drop an item in the Home." set MenuString(E_STORE_INSPECT) \ "Examine the properties of an item." set MenuString(E_STORE_SELL,store) \ "Sell an item to the store." set MenuString(E_DESTROY) \ "Destroy an item in the Home." set MenuString(M_SELL,home) \ "Contains a list of inventory items to drop." set MenuString(M_SELL,store) \ "Contains a list of inventory items the store will buy." set MenuString(MENU_SELL,home) \ "Drop this item." set MenuString(MENU_SELL,store) \ "Sell this item." return } # NSStore::Synch -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Synch {oop} { set toolbarId [Info $oop toolbarId] # Take/Buy button set state normal if {[angband store ishome]} { set label Take if {![angband store count]} { set state disabled } } else { set label Buy } set id [Info $oop toolbar,buyId] [NSToolbar::GetTool $toolbarId $id] configure -state $state \ -label $label # Drop/Sell button if {[angband store ishome]} { set label Drop } else { set label Sell } set state disabled set items [angband inventory find -store_will_buy yes] if {[llength $items]} { set state normal } set id [Info $oop toolbar,sellId] [NSToolbar::GetTool $toolbarId $id] configure -state $state \ -label $label return } # NSStore::SetupMenus -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::SetupMenus {oop mbarID} { variable Priv set identList {} if {[string equal [angband inkey_flags] INKEY_CMD]} { lappend identList E_STORE_BUY E_STORE_SELL E_STORE_EXIT if {[angband store ishome]} { lappend identList E_DESTROY } } NSMenu::MenuEnable $mbarID $identList [Info $oop win].statusBar cover show return } # NSStore::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::MenuSelect {oop menuId index ident} { variable MenuString variable Priv switch -- $ident { {} { set desc {} } E_STORE_EXIT { if {[angband store ishome]} { set desc "Leave the Home." } else { set desc "Leave the [angband store storename]." } } MENU_SELL { set menu [NSMenu::Info $menuId menu] if {$index == [$menu index end]} { set desc "Do nothing." } else { if {[angband store ishome]} { set desc $MenuString($ident,home) } else { set desc $MenuString($ident,store) } set index [lindex [Info $oop sellMenu,match] $index] NSRecall::RecallObject inventory $index } } MENU_TOOLBAR { set menu [NSMenu::Info $menuId menu] switch -- [Info $oop toolbar,mode] { buy - sell { if {$index == [$menu index end]} { set desc "Do nothing." } else { if {[angband store ishome]} { set desc $MenuString($ident,home) } else { set desc $MenuString($ident,store) } set index [lindex [Info $oop toolbar,match] $index] NSRecall::RecallObject [Info $oop toolbar,where] $index } } option { set desc [lindex [Info $oop toolbar,desc] $index]. } } } default { if {[angband store ishome]} { set sym $ident,home } else { set sym $ident,store } if {[info exists MenuString($sym)]} { set desc $MenuString($sym) } elseif {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop win].statusBar cover set $desc if {![string length $desc]} { if {$menuId == [Info $oop mbarId]} { [Info $oop win].statusBar cover hide } } return } # NSStore::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::MenuInvoke {oop menuId ident} { variable Priv switch -glob -- $ident { E_STORE_BUY {DoCommandIfAllowed p} E_STORE_SELL {DoCommandIfAllowed s} E_DESTROY {DoCommandIfAllowed K} E_STORE_EXIT {DoCommandIfAllowed \033} } return } # NSStore::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::DisplayCmd {oop message first} { set win [Info $oop win] switch -- $message { preDisplay { ConfigureWindow $oop SetList $oop } postDisplay { } reDisplay { # Preserve the scroll position and selection set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] set rowHeight [$canvas cget -yscrollincrement] set rowTop [expr {int([$canvas canvasy 0 $rowHeight] / $rowHeight)}] set current [Info $oop current] SetList $oop # Restore the scroll position and selection $canvas yview scroll $rowTop units if {$current != -1} { set count [NSCanvist::Info $canvistId count] if {$current >= $count} { set current [expr {$count - 1}] } if {$count} { NSCanvist::UpdateSelection $canvistId $current all } } } postWithdraw { # PositionItems() may be called when the window changes # size (for example, when the windows are arranged). Because # "angband store ishome" cannot be called when the character # isn't inside a store, I clear the list so PositionItems() # does nothing. The list should be cleared anyways, because # it might contain sprites. NSCanvist::DeleteAll [Info $oop canvistId] } } return } # NSStore::SetupCmd -- # # Called by NSWindowManager::Setup(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::SetupCmd {oop} { set win [Info $oop win] set frame $win.info # Like ConfigureWindow, but can't call that yet pack $frame.howMuch \ -side left -expand no -padx 2 pack $frame.totalCost \ -side left -expand no -padx 2 update idletasks $frame configure -height [winfo reqheight $frame] pack propagate $frame no return } # NSStore::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Close {oop} { angband keypress \033 return } # NSStore::Win98MenuCmd_Options -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Win98MenuCmd_Options {oop button} { set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] $menu delete 0 end set keywordList {} set descList {} $menu add separator lappend keywordList "" lappend descList "" $menu add command -label "Graphics Mode" -command {StoreObj Swap} lappend keywordList "" lappend descList "Use the graphical window" Info $oop toolbar,mode option Info $oop toolbar,match $keywordList Info $oop toolbar,desc $descList [Info $oop win].statusBar cover show tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } after idle "$button hidemenu ; [Info $oop win].statusBar cover hide" return } # NSStore::SettingChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::SettingChanged {oop keyword value} { # Ignore settings which don't affect the display if {[lsearch -exact [Info $oop toolbar,match] $keyword] == -1} return # Update the button update idletasks SetList $oop return } # NSStore::Win98MenuCmd_Buy -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Win98MenuCmd_Buy {oop button} { variable MenuString set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] $menu delete 0 end set doCmd 0 set doItem 0 set doMenu 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set charCmd p set doMenu 1 set doCmd 1 } elseif {[string equal [angband inkey_flags] INKEY_ITEM_STORE]} { set charCmd "" set doMenu 1 set doItem 1 } set match {} if {$doMenu} { set count [angband store count] for {set index 0} {$index < $count} {incr index} { angband store info $index attrib set charItem $attrib(char) $menu add command -label "$charItem $attrib(name)" \ -command "angband keypress $charCmd$charItem" \ -underline 0 lappend match $index } } if {[string compare [$menu index end] none]} { $menu add separator } if {$doItem} { $menu add command -label "Cancel" -command "angband keypress \033" } else { $menu add command -label "Cancel" } if {[angband store ishome]} { set MenuString(MENU_TOOLBAR,home) "Take this item." } else { set MenuString(MENU_TOOLBAR,store) "Buy this item." } Info $oop toolbar,mode buy Info $oop toolbar,match $match Info $oop toolbar,where store [Info $oop win].statusBar cover show tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } after idle "$button hidemenu ; [Info $oop win].statusBar cover hide" return } # NSStore::Win98MenuCmd_Sell -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Win98MenuCmd_Sell {oop button} { variable MenuString set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] $menu delete 0 end set doCmd 0 set doItem 0 set doMenu 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set charCmd s set doMenu 1 set doCmd 1 } elseif {[string equal [angband inkey_flags] INKEY_ITEM] && [string equal [angband inkey_other] inventory]} { set charCmd "" set doMenu 1 set doItem 1 } set match {} if {$doMenu} { foreach index [angband inventory find -store_will_buy yes] { angband inventory info $index attrib set charItem $attrib(char) $menu add command -label "$charItem $attrib(name)" \ -command "angband keypress $charCmd$charItem" \ -underline 0 lappend match $index } } if {[string compare [$menu index end] none]} { $menu add separator } if {$doItem} { $menu add command -label "Cancel" -command "angband keypress \033" } else { $menu add command -label "Cancel" } if {[angband store ishome]} { set MenuString(MENU_TOOLBAR,home) "Drop this item." } else { set MenuString(MENU_TOOLBAR,store) "Sell this item." } Info $oop toolbar,mode sell Info $oop toolbar,match $match Info $oop toolbar,where inventory [Info $oop win].statusBar cover show tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } after idle "$button hidemenu ; [Info $oop win].statusBar cover hide" return } # NSStore::ConfigureWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::ConfigureWindow {oop} { set win [Info $oop win] set frame $win.info eval pack forget [winfo children $frame] # pack $frame.divider1 \ # -side top -expand yes -fill x pack $frame.howMany \ -side left -expand no -padx 2 pack $frame.quantity \ -side left -expand no -padx 2 # Not in the Home if {![angband store ishome]} { pack $frame.howMuch \ -side left -expand no -padx 2 pack $frame.totalCost \ -side left -expand no -padx 2 } pack $frame.gold \ -side right -expand no -padx 2 pack $frame.playerGold \ -side right -expand no -padx 2 if {![angband store ishome]} { $frame.purse configure -text "Purse: [angband store purse] " pack $frame.purse \ -side right -expand no -padx 2 } set mbarId [Info $oop mbarId] NSMenu::MenuDeleteEntry $mbarId E_DESTROY if {[angband store ishome]} { NSMenu::EntryConfigure $mbarId M_STORE -label "Home" NSMenu::EntryConfigure $mbarId M_SELL -label "Drop" NSMenu::EntryConfigure $mbarId E_STORE_BUY -label "Take" NSMenu::EntryConfigure $mbarId E_STORE_SELL -label "Drop" NSMenu::MenuInsertEntry $mbarId -before E_SEP_1 \ -type command -label "Destroy" -identifier E_DESTROY } else { NSMenu::EntryConfigure $mbarId M_STORE -label "Store" NSMenu::EntryConfigure $mbarId M_SELL -label "Sell" NSMenu::EntryConfigure $mbarId E_STORE_BUY -label "Buy" NSMenu::EntryConfigure $mbarId E_STORE_SELL -label "Sell" } return } # NSStore::HaggleSetup -- # # Shows or hides display elements during the buy/sell process. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::HaggleSetup {oop action} { set win [Info $oop win] set frame $win.info switch -- $action { haggle_open { pack forget $frame.howMany $frame.quantity $frame.howMuch \ $frame.totalCost pack $frame.price_owner \ -side left -padx 2 pack $frame.price_character \ -side left -padx 2 } haggle_close { pack forget $frame.price_owner $frame.price_character pack $frame.howMany \ -side left -padx 2 pack $frame.quantity \ -side left -padx 2 pack $frame.howMuch \ -side left -padx 2 pack $frame.totalCost \ -side left -padx 2 } } return } # NSStore::SetList -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::SetList {oop} { variable Priv # Get the window and canvas set win [Info $oop win] set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] # Clear and disable the "quantity" entry Info $oop quantity "" $win.info.quantity configure -state disabled # Get the store name set storename [angband store storename] # In the Home if {[angband store ishome]} { # Set the window title wm title $win "Your Home" # Not in the Home } else { # Set the window title wm title $win [format "The %s owned by %s" \ $storename [angband store ownername]] } # Get the number of items set count [angband store count] # Clear the list NSCanvist::DeleteAll $canvistId # Calculate the row height. This is done every time here since # the font may change. The row height equals the linespace of # the font plus 8 pixels for the selection rectangle, or the # icon size plus 8, whichever is greater. set rowHgt [font metrics [Value font,store] -linespace] # Option: Show icons in lists if {[icon size] > $rowHgt} { set rowHgt [icon size] } # Leave room for the selection rectangle on each line incr rowHgt 8 # Set the row height NSCanvist::Info $canvistId rowHgt $rowHgt $canvas configure -yscrollincrement $rowHgt set Priv(width,char) 0 set Priv(width,desc) 0 set Priv(width,weight) 0 set Priv(width,price) 0 # Append each object for {set index 0} {$index < $count} {incr index} { # Get the object info angband store info $index attrib # Get the (optional) icon set icon $attrib(icon) # Get the (optional) weight set weight $attrib(weight) # No price in the Home if {[angband store ishome]} { set price "" set fixed "" # Price in the store } else { # Hack -- See if this is a fixed price set price $attrib(cost) regexp {([0-9]+)(.*)} $price ignore price fixed } # Append to the list NSCanvist::Insert $canvistId end $attrib(char) $attrib(number) \ $attrib(name) $weight $attrib(tval) $icon $price $fixed } # Arrange all the items PositionItems $oop # Display number of objects if {$count == 1} { set string [format "%d item" $count] } else { set string [format "%d items" $count] } $win.statusBar itemconfigure t2 -text $string Synch $oop return } # NSStore::ContextMenu -- # # When the store list is right-clicked, pop up a context # menu of actions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::ContextMenu {oop menu x y} { variable Priv set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] set font [$menu cget -font] # Find the hit row set x1 [expr {$x - [winfo rootx $canvas]}] set y1 [expr {$y - [winfo rooty $canvas]}] set row [NSCanvist::PointToRow $canvistId $x1 $y1] # Clear the menu $menu delete 0 end set charBuy p set charSell s if {[angband store ishome]} { set stringBuy "Take" set stringSell "Drop" } else { set stringBuy "Buy" set stringSell "Sell" } # No row is hit if {$row == -1} { # We are waiting for a command if {[string equal [angband inkey_flags] INKEY_CMD]} { $menu add command -label "$stringBuy An Item" \ -command "angband keypress $charBuy" $menu add command -label "$stringSell An Item" \ -command "angband keypress $charSell" $menu add separator $menu add command -label Leave \ -command "angband keypress \033" $menu add separator $menu add command -label Cancel # Pop up the menu tk_popup $menu $x $y } # Done return } set itemIndex $row # Get information about this item angband store info $itemIndex attrib # Get the item char set itemKey $attrib(char) # Get the tval set itemTval $attrib(tval) # Get the amount set itemAmount $attrib(number) # We are waiting for an item if {[string equal [angband inkey_flags] INKEY_ITEM_STORE]} { # Append a command to select the item set command "angband keypress $itemKey" $menu add command -label "Select This Item" -command $command \ -font [BoldFont $font] $menu add separator $menu add command -label "Cancel" -command "angband keypress \033" # Pop up the menu tk_popup $menu $x $y # Done return } # We are not waiting for a command if {[string compare [angband inkey_flags] INKEY_CMD]} { return } if {$itemAmount == 1} { $menu add command -label "$stringBuy This Item" \ -command "angband keypress $charBuy$itemKey" -font [BoldFont $font] } else { $menu add command -label "$stringBuy One" \ -command "angband keypress 01$charBuy$itemKey" -font [BoldFont $font] if {$itemAmount > 5} { $menu add command -label "$stringBuy Five" \ -command "angband keypress 05$charBuy$itemKey" } $menu add command -label "$stringBuy All" \ -command "angband keypress 0$itemAmount$charBuy$itemKey" } $menu add separator $menu add command -label "$stringSell An Item" \ -command "angband keypress $charSell" if {[angband store ishome]} { # Complex handling of Destroy command. The entire stack is # destroyed, and the user isn't asked to confirm. set command "angband keypress 0${itemAmount}K$itemKey" $menu add separator $menu add command -label "*Destroy*" -command $command } $menu add separator $menu add command -label "Leave" \ -command "angband keypress \033" $menu add separator $menu add command -label "Cancel" # Pop up the menu tk_popup $menu $x $y return } # NSStore::SynchScrollBars -- # # There is a bug (my bug or in Tk?) which prevents the scroll bars # from synchronizing when the window is not mapped. So I bind to # the event and synch the scroll bars here. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::SynchScrollBars {oop} { set win [Info $oop win] set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] eval $win.frame.scroll set [$canvas yview] $canvas yview moveto 0.0 return } # NSStore::Invoke -- # # When a store item is double-clicked, "angband keypress" to # initiate a purchase. This is also called when Return is typed # while the Quantity Entry has the focus. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Invoke {oop canvistId x y} { set canvas [NSCanvist::Info $canvistId canvas] # Get the hit row set row [NSCanvist::PointToRow $canvistId $x $y] # No row was hit if {$row == -1} return # Invoke InvokeRow $oop $row return } # NSStore::InvokeByReturn -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::InvokeByReturn {oop} { set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] # Hack -- Focus on the list focus $canvas # Get the selection set selection [NSCanvist::Selection $canvistId] if {![llength $selection]} return # Get the (first) selected row set row [lindex $selection 0] # Invoke InvokeRow $oop $row return } # NSStore::InvokeRow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::InvokeRow {oop row} { # See if we are waiting for a command set doCmd 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set doCmd 1 } # See if we are waiting for a store item set doItem 0 if {[string equal [angband inkey_flags] INKEY_ITEM_STORE]} { set doItem 1 } # Do nothing if {!$doCmd && !$doItem} return # Get the item info angband store info $row attrib # Append a quantity if waiting for a command if {$doCmd} { set keypress "0[Info $oop quantity]p" } # Append item char append keypress $attrib(char) # Feed the Term angband keypress $keypress return } # NSStore::SelectionChanged -- # # Called when the list selection changes. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::SelectionChanged {oop canvistId select deselect} { set win [Info $oop win] set entry $win.info.quantity # An item was selected if {[llength $select]} { $entry configure -state normal Info $oop quantity "1" # Get the (first) row set row [lindex $select 0] Info $oop current $row # Display memory for this object NSRecall::RecallObject store $row # An item was deselected (but not selected) } else { Info $oop quantity "" $entry configure -state disabled Info $oop current -1 } return } # NSStore::QuantityChanged -- # # Trace variable callback on NSStore($oop,quantity). When the number # of items to buy changes, synch the "total price" field. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::QuantityChanged {oop name1 name2 op} { set win [Info $oop win] set canvistId [Info $oop canvistId] set quantity [Info $oop quantity] set selection [NSCanvist::Selection $canvistId] if {$selection == {} || $quantity == {}} { $win.info.totalCost configure -text {} return } set row [lindex $selection 0] angband store info $row attrib if {$quantity <= 0} { set quantity 1 } set max $attrib(number) if {$quantity > $max} { set quantity $max } # Hack -- Handle "fixed" prices (ex "45 F") scan $attrib(cost) %d price # No prices are displayed in the Home if {![angband store ishome]} { $win.info.totalCost configure -text [expr {$quantity * $price}] } Info $oop quantity $quantity return } # NSStore::NewItemCmd -- # # Called by NSCanvist::InsertItem() to create a list row. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::NewItemCmd {oop canvistId y char number text weight tval icon price fixed} { variable Priv set canvas [NSCanvist::Info $canvistId canvas] set lineHeight [NSCanvist::Info $canvistId rowHgt] set font [Value font,store] set fh [font metrics $font -linespace] set diff [expr {int([expr {($lineHeight - $fh) / 2}])}] set offset [expr {[icon size] + 8}] if 0 { # Image if {[string length $icon]} { set iw [icon size] set ih [icon size] set wid [expr {[icon size] + 8}] set xdiff [expr {int([expr {($wid - $iw) / 2}])}] set ydiff [expr {int([expr {($lineHeight - $ih) / 2}])}] lappend itemIdList [$canvas create widget $xdiff [expr {$y + $ydiff}] \ -assign $icon] } } # Char lappend itemIdList [$canvas create text $offset [expr {$y + $diff}] \ -text $char) -anchor nw -font $font -fill White] # Description set fill [default_tval_to_attr $tval] lappend itemIdList [$canvas create text 0 \ [expr {$y + $diff}] -text $text -anchor nw -font $font -fill $fill \ -tags "enabled fill:$fill hilite description"] # This item "truncates" long descriptions set listBG [Value listBG] lappend itemIdList [$canvas create rectangle 0 $y \ 1 [expr {$y + $lineHeight}] -fill $listBG -outline $listBG \ -tags truncate] # Weight set weight [format "%d.%d lb" [expr {$weight / 10}] [expr {$weight % 10}]] lappend itemIdList [$canvas create text 0 \ [expr {$y + $diff}] -text $weight -anchor ne -justify right \ -font $font -fill White -tags weight] # Price (except in Home) if {![angband store ishome]} { lappend itemIdList [$canvas create text 0 [expr {$y + $diff}] \ -text $price -anchor ne -justify right -font $font -fill White \ -tags price] lappend itemIdList [$canvas create text 0 [expr {$y + $diff}] \ -text $fixed -anchor ne -justify right -font $font -fill White \ -tags fixed] } # Selection rectangle around text lappend itemIdList [$canvas create rectangle 2 [expr {$y + 2}] \ 2 [expr {$y + $lineHeight - 2}] -fill "" -outline "" \ -tags {enabled selrect} -width 2.0] # Maximum width of char set width [font measure $font "$char) "] if {$width > $Priv(width,char)} { set Priv(width,char) $width } # Maximum width of description set width [font measure $font $text] if {$width > $Priv(width,desc)} { set Priv(width,desc) $width } # Maximum width of weight if {[string length $weight]} { set width [font measure $font "ABC$weight"] if {$width > $Priv(width,weight)} { set Priv(width,weight) $width } } # Maximum width of price if {![angband store ishome]} { set width [font measure $font "ABC{$price}"] if {$width > $Priv(width,price)} { set Priv(width,price) $width } } return $itemIdList } # NSStore::PositionItems -- # # Arranges all the canvas items in the list. This is called after all # the items are added, and when the event indicates the # window has been resized. This is the routine that lets variable-width # fonts work. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::PositionItems {oop} { variable Priv set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] # Nothing to do if {![NSCanvist::Info $canvistId count]} return # Get the font set font [Value font,store] # Get the width of the canvas set canvasWidth [winfo width $canvas] set offset [expr {[icon size] + 8}] incr offset $Priv(width,char) # Position each description set coords [$canvas coords description] $canvas move description [expr {$offset - [lindex $coords 0]}] 0 # Truncate each description (by positioning each "truncate" item) set x0 [expr {($canvasWidth - 1) - $Priv(width,weight) - \ $Priv(width,price) - 4}] set x1 $canvasWidth foreach itemId [$canvas find withtag truncate] { scan [$canvas coords $itemId] "%s %s %s %s" c0 c1 c2 c3 $canvas coords $itemId $x0 $c1 $x1 $c3 } if {[angband store ishome]} { set Priv(width,fixed) 0 } else { set Priv(width,fixed) [font measure $font " F"] } # Position each weight if {$Priv(width,weight)} { set offset [expr {($canvasWidth - 1) - $Priv(width,price) \ - $Priv(width,fixed) - 4}] set coords [$canvas coords weight] $canvas move weight [expr {$offset - [lindex $coords 0]}] 0 } if {![angband store ishome]} { # Position each price set offset [expr {($canvasWidth - 1) - $Priv(width,fixed) - 4}] set coords [$canvas coords price] $canvas move price [expr {$offset - [lindex $coords 0]}] 0 # Position each fixed set offset [expr {($canvasWidth - 1) - 4}] set coords [$canvas coords fixed] $canvas move fixed [expr {$offset - [lindex $coords 0]}] 0 } # Position each selection rectangle set x1 [expr {($canvasWidth - 1) - 2}] foreach itemId [$canvas find withtag selrect] { scan [$canvas coords $itemId] "%s %s %s %s" c0 c1 c2 c3 $canvas coords $itemId $c0 $c1 $x1 $c3 } # Set the scrollregion to prevent horizontal scrolling scan [$canvas cget -scrollregion] "%s %s %s %s" x1 y1 x2 y2 $canvas configure -scrollregion "$x1 $y1 $canvasWidth $y2" return } # NSStore::HighlightItemCmd -- # # Called by NSCanvist::Select() to highlight a row. # # Arguments: # oop OOP ID. See above. # canvistId OOP ID of NSCanvist object. # state 1 or 0 highlight state. # args List of canvas item ids # # Results: # What happened. proc NSStore::HighlightItemCmd {oop canvistId state args} { set canvas [NSCanvist::Info $canvistId canvas] set itemIdList $args set idRect [FindItemByTag $canvas $itemIdList selrect] if {[NSUtils::HasFocus $canvas]} { set fill [Value listHilite] } else { set fill [Value listInactive] } if {$state} { $canvas itemconfigure $idRect -outline $fill } else { $canvas itemconfigure $idRect -outline {} } return } # NSStore::StatusBar -- # # Display text in the status bar, perhaps clearing it later. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::StatusBar {oop text zap} { set win [Info $oop win] set label [$win.statusBar itemcget t1 -label] $label configure -text $text if {$zap} { NSUtils::ZapLabel $label } return } # NSStore::ValueChanged_listBG -- # # Called when the listBG value changes. # Updates the Store Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::ValueChanged_listBG {oop} { set canvistId [Info $oop canvistId] set canvas [NSCanvist::Info $canvistId canvas] set color [Value listBG] $canvas configure -background $color $canvas itemconfigure truncate -fill $color -outline $color return } # NSStore::Swap -- # # Display the "old" Store Window # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore::Swap {oop} { NSWindowManager::Undisplay store NSModule::LoadIfNeeded NSStore2 NSWindowManager::Display store2 Value store,style new return } proc StoreObj {command args} { return [eval NSStore::$command [Global store,oop] $args] } zangband/lib/script/tk/store2.tcl0000644000000000000000000015237010250356274015766 0ustar rootroot# File: store2.tcl # Purpose: the (new) Store Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSStore2 { variable MenuString variable Priv # namespace eval NSStore2 } # NSStore2::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::InitModule {} { InitImageIfNeeded Image_ButtonOptions button-options.gif InitImageIfNeeded Image_ButtonHelp button-help.gif InitImageIfNeeded Image_Binding dg_binding.gif InitImageIfNeeded Image_Shelf shelf.gif # NSModule::LoadIfNeeded NSBalloon NSModule::LoadIfNeeded NSToolbar NSObject::New NSStore2 return } # NSStore2::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::CloseModule {} { catch { destroy [Window store2] } return } # NSStore2::NSStore2 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::NSStore2 {oop} { Info $oop busy 0 Info $oop dragging 0 Info $oop highlight,where "" Info $oop highlight,index "" Info $oop select,where "" Info $oop select,index "" Info $oop toolbar,match {} Info $oop request {} Info $oop request,id "" Info $oop skipMore 0 InitWindow $oop set win [Info $oop win] NSWindowManager::RegisterWindow store2 $win \ "GetDefaultGeometry $win reqwidth reqheight" \ "NSStore2::SetupCmd $oop" \ "NSStore2::DisplayCmd $oop" # not true on X11 if {[Platform windows]} { ### Tk 8.3.0 BUG!!! ### "wm resizable" always returns "1 1" if a toplevel has a menu. ### Since the Store Window is not resizable, we must clear any ### requested height/width for the window. See NSWindowManager::Setup(). proc ::NSStore2::SetupCmd {oop} { set geometry $NSWindowManager::Priv(store2,geomRequest) if {[string length $geometry]} { if {[scan $geometry {%dx%d%[+-]%d%[+-]%d} width height xs x ys y] == 6} { set win [Info $oop win] set width [winfo reqwidth $win] set height [winfo reqheight $win] set NSWindowManager::Priv(store2,geomRequest) ${width}x$height$xs$x$ys$y } } return } set NSWindowManager::Priv(store2,setupCmd) "NSStore2::SetupCmd $oop" } # Update ourself when the font,statusBar value changes Info $oop clientId,font,statusBar \ [NSValueManager::AddClient font,statusBar \ "NSStore2::ValueChanged_font_statusBar $oop"] # Destroy the object along with the widget (later) NSUtils::DestroyObjectWithWidget NSStore2 $oop $win # # Global list of application windows # Global store2,oop $oop Window store2 $win return } # NSStore2::~NSStore2 -- # # Object destructor called by NSObject::Delete(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::~NSStore2 {oop} { trace vdelete ::NSStore2($oop,quantity) w "::NSStore2::QuantityChanged $oop" NSValueManager::RemoveClient font,statusBar [Info $oop clientId,font,statusBar] return } # NSStore2::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Info {oop info args} { global NSStore2 # Verify the object NSObject::CheckObject NSStore2 $oop # Set info if {[llength $args]} { switch -- $info { default { set NSStore2($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSStore2($oop,$info) } } } return } # NSStore2::InitWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::InitWindow {oop} { set win .store2_$oop toplevel $win wm title $win Store wm resizable $win no no wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSStore2::Close $oop" # Start out withdrawn (hidden) wm withdraw $win Info $oop win $win InitMenus $oop # # Toolbar # set toolId [NSObject::New NSToolbar 20 $win] NSToolbar::AddTool $toolId -image Image_ButtonOptions \ -showlabel no -command "DoCommandIfAllowed =" -hasmenu yes \ -menucommand "NSStore2::Win98MenuCmd_Options $oop" NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -showlabel no -command "DoCommandIfAllowed ?" MakeDivider [NSToolbar::Info $toolId frame].divider1 y pack [NSToolbar::Info $toolId frame].divider1 -side left -fill y -pady 2 set id [NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -command "DoCommandIfAllowed p" -label "Buy" -showlabel yes \ -showimage no -heightlabel 16 -hasmenu yes \ -menucommand "NSStore2::Win98MenuCmd_Buy $oop"] set id2 [NSToolbar::AddTool $toolId -image Image_ButtonHelp \ -command "DoCommandIfAllowed s" -label "Sell" -showlabel yes \ -showimage no -heightlabel 16 -hasmenu yes \ -menucommand "NSStore2::Win98MenuCmd_Sell $oop"] set menuId [NSObject::New NSMenu [Info $oop mbarId] \ -tearoff 0 -identifier MENU_TOOLBAR -postcommand ";"] set menu [NSMenu::Info $menuId menu] if {[Platform unix]} { $menu configure -cursor arrow } Info $oop toolbar,menu $menu Info $oop toolbarId $toolId Info $oop toolbar,buyId $id Info $oop toolbar,sellId $id2 # # Divider + Store Info # if {[Platform unix]} { set font {Courier 12} } if {[Platform windows]} { set font {Courier 9} } set frame $win.info frame $frame \ -borderwidth 0 label $frame.howMany \ -font $font -text "Quantity:" entry $frame.quantity \ -width 2 -state disabled -textvariable NSStore2($oop,quantity) label $frame.howMuch \ -font $font -text "Total Cost:" label $frame.totalCost \ -font $font -text "" -anchor w label $frame.purse \ -font $font label $frame.playerGold \ -font $font -text "Gold Remaining:" label $frame.gold \ -font $font -text [angband player gold] # Pack these here so the initial geometry is correct. Otherwise # the requested height is wrong pack $frame.howMany \ -side left -expand no -padx 2 pack $frame.quantity \ -side left -expand no -padx 2 # Typing Enter in the Quantity Entry initiates a purchase bind $frame.quantity \ "NSStore2::InvokeSelected $oop" label $frame.price_character \ -font $font -text "" -anchor w label $frame.price_owner \ -font $font -text "" -anchor w trace variable ::NSStore2($oop,quantity) w \ "::NSStore2::QuantityChanged $oop" MakeDivider $win.divider2 x # # Canvas # set canvas $win.canvas canvas $canvas \ -borderwidth 0 -highlightthickness 0 \ -background [format #%02x%02x%02x 0 0 153] Info $oop canvas $canvas if {[Platform unix]} { set font {Helvetica 12 bold} } if {[Platform windows]} { set font {Helvetica 10 bold} } set fontHeight [font metrics $font -linespace] set heightHeader [expr {1 + $fontHeight + 1}] set heightBox [expr {[icon size] + 8}] set widthBox [expr {[icon size] + 8}] set heightBoxMax 40 set widthBoxMax 40 set heightImage [image height Image_Shelf] set heightRow [expr {6 + $heightBox + 2 + $heightImage}] set heightStore [expr {3 + ($heightHeader - 1) + 4 * $heightRow + 6 + 3}] set padRing 2 set widthStore [expr {3 + 6 * (6 + $widthBoxMax) + 6 + 3 + $padRing}] # Store items MakeBorder $oop $canvas 0 0 $widthStore $heightStore MakeHeader $oop $canvas 0 0 $widthStore Store store,title set columns [expr {($widthStore - $padRing - 3 - 6 - 3) / ($widthBox + 6)}] set diff [expr {($widthStore - $padRing - 3 - 6 - 3) - $columns * ($widthBox + 6)}] set index 0 for {set row 0} {$row < 4} {incr row} { for {set col 0} {$col < $columns} {incr col} { MakeBox $oop $canvas store $index set x [expr {$diff / 2 + 3 + 6 + $col * ($widthBox + 6) + $widthBox / 2}] set y [expr {3 + ($heightHeader - 1) + 6 + $row * $heightRow + $heightBox / 2}] Info $oop store,x,$index $x Info $oop store,y,$index $y # Shelf image set x [expr {3 + 6 - 1}] set y [expr {3 + ($heightHeader - 1) + 6 + $row * $heightRow + $widthBox + 2}] $canvas create image $x $y -image Image_Shelf -anchor nw if {[incr index] == 24} break } if {$index == 24} break } # Inventory set widthInven [expr {$padRing + 3 + 5 * ($widthBoxMax + 6) + 6 + 3}] set x [expr {$widthStore + 1}] MakeBorder $oop $canvas $x 0 $widthInven $heightStore MakeHeader $oop $canvas $x 0 $widthInven "Inventory" inventory,title set left [expr {$x + $padRing + 3 + 6}] set top [expr {3 + ($heightHeader - 1) + 6}] set columns [expr {($widthInven - $padRing - 3 - 6 - 3) / ($widthBox + 6)}] set diff [expr {($widthInven - $padRing - 3 - 6 - 3) - $columns * ($widthBox + 6)}] # 23 items set col 0 set row 0 for {set i 0} {$i <= 23} {incr i} { MakeBox $oop $canvas inventory $i set x [expr {$diff / 2 + $left + $col * ($widthBox + 6) + $widthBox / 2}] set y [expr {$top + $row * ($heightBox + 6) + $heightBox / 2}] Info $oop inventory,x,$i $x Info $oop inventory,y,$i $y if {[incr col] == $columns} { set col 0 incr row } } bind $canvas \ "NSStore2::Button1 $oop %x %y" bind $canvas \ "NSStore2::Motion1 $oop %x %y" bind $canvas \ "NSStore2::Release1 $oop %x %y" Info $oop width,store $widthStore # # Statusbar # # Store statusbar MakeStatus $oop $canvas 0 $heightStore $widthStore store # Inventory statusbar set x [expr {$widthStore + 1}] MakeStatus $oop $canvas $x $heightStore $widthInven inventory set font [Value font,statusBar] set fontHeight [font metrics $font -linespace] set heightStatus [expr {3 + $fontHeight + 3}] set widthTotal [expr {$widthStore + 1 + $widthInven}] set heightTotal [expr {$heightStore + $heightStatus}] $canvas configure -width $widthTotal -height $heightTotal # Divider filler set x $widthStore $canvas create line $x 0 $x $heightTotal -fill #282828 # Hack -- ring bindings down the middle set deltaY 34 set y [expr {3 + ($heightHeader - 1) + 6 + 1}] set max [expr {($heightStore - 6 - 6) / 34}] for {set i 0} {$i < $max} {incr i} { set itemId [$canvas create image $x $y -image Image_Binding -anchor n] # $canvas bind $itemId {dbwin "[%W coords current]\n"} $canvas bind $itemId {} incr y $deltaY } # This box is used for drag & drop MakeBox $oop $canvas drag 0 HideBox $oop drag 0 # Help-text statusbar (normally hidden) MakeStatus $oop $canvas 0 $heightStore $widthTotal statusBar $canvas itemconfigure statusBar,status -state hidden # Alternate balloon impl $canvas create rectangle 0 0 10 10 -fill White -state hidden \ -tags {balloon balloon,rect} $canvas create text 0 0 -anchor n -state hidden -tags {balloon balloon,text} # # Geometry # grid rowconfig $win 0 -weight 0 grid rowconfig $win 1 -weight 0 grid rowconfig $win 2 -weight 0 grid rowconfig $win 3 -weight 1 grid columnconfig $win 0 -weight 1 pack forget [NSToolbar::Info $toolId frame] grid [NSToolbar::Info $toolId frame] \ -row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.info \ -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.divider2 \ -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew grid $win.canvas \ -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky news # # Context Menu # set m $win.context menu $m -tearoff 0 bind $canvas \ "NSStore2::ContextMenu $oop $m %X %Y" # # Feed Term when keys pressed # Term_KeyPress_Bind $win Term_KeyPress_Bind $canvas # XXX Hack -- Don't feed Tab to the Term bind $canvas { focus [tk_focusNext %W] break } return } # NSStore2::InitMenus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::InitMenus {oop} { variable MenuString set win [Info $oop win] # # Menu bar # set mbar [NSObject::New NSMenu $win -tearoff 0 \ -postcommand "NSStore2::SetupMenus $oop" -identifier MENUBAR] Info $oop mbarId $mbar # Context-sensitive help NSMenu::Info $mbar menuSelectCmd "NSStore2::MenuSelect $oop" # Call our command when an entry is invoked NSMenu::Info $mbar invokeCmd "NSStore2::MenuInvoke $oop" # # Store Menu # NSObject::New NSMenu $mbar -tearoff 0 -identifier MENU_STORE NSMenu::MenuInsertEntry $mbar -end MENUBAR -type cascade \ -menu MENU_STORE -label "Store" -underline 0 -identifier M_STORE set entries {} lappend entries [list -type command -label "Buy" -identifier E_STORE_BUY] lappend entries [list -type command -label "Sell" -identifier E_STORE_SELL] lappend entries [list -type separator -identifier E_SEP_1] lappend entries [list -type separator] lappend entries [list -type command -label "Leave" \ -identifier E_STORE_EXIT] NSMenu::MenuInsertEntries $mbar -end MENU_STORE $entries set MenuString(M_STORE) \ "Contains store-related commands." set MenuString(E_STORE_BUY,home) \ "Take an item from the Home." set MenuString(E_STORE_BUY,store) \ "Purchase an item from the store." set MenuString(E_STORE_SELL,home) \ "Drop an item in the Home." set MenuString(E_STORE_INSPECT) \ "Examine the properties of an item." set MenuString(E_STORE_SELL,store) \ "Sell an item to the store." set MenuString(E_DESTROY) \ "Destroy an item in the Home." set MenuString(M_SELL,home) \ "Contains a list of inventory items to drop." set MenuString(M_SELL,store) \ "Contains a list of inventory items the store will buy." set MenuString(MENU_SELL,home) \ "Drop this item." set MenuString(MENU_SELL,store) \ "Sell this item." return } # NSStore2::Synch -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Synch {oop} { set toolbarId [Info $oop toolbarId] # # Take/Buy button # set state normal if {[angband store ishome]} { set label Take if {![angband store count]} { set state disabled } } else { set label Buy } set id [Info $oop toolbar,buyId] [NSToolbar::GetTool $toolbarId $id] configure -state $state \ -label $label # # Drop/Sell button # if {[angband store ishome]} { set label Drop } else { set label Sell } set state disabled set items [angband inventory find -store_will_buy yes] if {[llength $items]} { set state normal } set id [Info $oop toolbar,sellId] [NSToolbar::GetTool $toolbarId $id] configure -state $state \ -label $label return } # NSStore2::SetupMenus -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SetupMenus {oop mbarID} { variable Priv set identList {} if {[string equal [angband inkey_flags] INKEY_CMD]} { lappend identList E_STORE_BUY E_STORE_SELL E_STORE_EXIT if {[angband store ishome]} { lappend identList E_DESTROY } } NSMenu::MenuEnable $mbarID $identList [Info $oop canvas] itemconfigure statusBar,status -state "" return } # NSStore2::MenuSelect -- # # Displays a help string associated with a menu entry. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MenuSelect {oop menuId index ident} { variable MenuString variable Priv switch -- $ident { {} { set desc {} } E_STORE_EXIT { if {[angband store ishome]} { set desc "Leave the Home." } else { set desc "Leave the [angband store storename]." } } MENU_SELL { set menu [NSMenu::Info $menuId menu] if {$index == [$menu index end]} { set desc "Do nothing." } else { if {[angband store ishome]} { set desc $MenuString($ident,home) } else { set desc $MenuString($ident,store) } set index [lindex [Info $oop sellMenu,match] $index] NSRecall::RecallObject inventory $index } } MENU_TOOLBAR { set menu [NSMenu::Info $menuId menu] switch -- [Info $oop toolbar,mode] { buy - sell { if {$index == [$menu index end]} { set desc "Do nothing." } else { if {[angband store ishome]} { set desc $MenuString($ident,home) } else { set desc $MenuString($ident,store) } set index [lindex [Info $oop toolbar,match] $index] NSRecall::RecallObject [Info $oop toolbar,where] $index } } option { set desc [lindex [Info $oop toolbar,desc] $index]. } } } default { if {[angband store ishome]} { set sym $ident,home } else { set sym $ident,store } if {[info exists MenuString($sym)]} { set desc $MenuString($sym) } elseif {[info exists MenuString($ident)]} { set desc $MenuString($ident) } else { set menu [NSMenu::Info $menuId menu] set desc [$menu entrycget $index -label] } } } [Info $oop canvas] itemconfigure statusBar,status,left -text $desc if {![string length $desc]} { if {$menuId == [Info $oop mbarId]} { [Info $oop canvas] itemconfigure statusBar,status -state hidden } } return } # NSStore2::MenuInvoke -- # # Called when a menu entry is invoked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MenuInvoke {oop menuId ident} { variable Priv switch -glob -- $ident { E_STORE_BUY {DoCommandIfAllowed p} E_STORE_SELL {DoCommandIfAllowed s} E_DESTROY {DoCommandIfAllowed K} E_STORE_EXIT {DoCommandIfAllowed \033} } return } # NSStore2::DisplayCmd -- # # Called by NSWindowManager::Display(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::DisplayCmd {oop message first} { set win [Info $oop win] switch -- $message { preDisplay { ConfigureWindow $oop SetList $oop SetList_Inventory $oop } reDisplay { SetList $oop } postDisplay { if {0 && ![Value warning,store,window]} { tk_messageBox -parent [Info $oop win] -title "Game Change" \ -message "This is the new graphical Store Window.\ If you want the list instead, choose \"List Mode\" from\ the options button in the toolbar." Value warning,store,window 1 } } postWithdraw { Info $oop busy 0 } } return } # NSStore2::SetupCmd -- # # Called by NSWindowManager::Setup(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SetupCmd {oop} { set win [Info $oop win] set frame $win.info $frame configure -height [winfo reqheight $frame] pack propagate $frame no return } # NSStore2::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Close {oop} { angband keypress \033 return } # NSStore2::Win98MenuCmd_Options -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Win98MenuCmd_Options {oop button} { set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] $menu delete 0 end set keywordList {} set descList {} $menu add separator lappend keywordList "" lappend descList "" $menu add command -label "List Mode" -command {Store2Obj Swap} lappend descList "Use the list window" Info $oop toolbar,mode option Info $oop toolbar,match $keywordList Info $oop toolbar,desc $descList [Info $oop canvas] itemconfigure statusBar,status -state "" tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) } after idle "$button hidemenu ; [Info $oop canvas] itemconfigure statusBar,status -state hidden" return } # NSStore2::SettingChanged -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SettingChanged {oop keyword value} { # Ignore settings which don't affect the display if {[lsearch -exact [Info $oop toolbar,match] $keyword] == -1} return # Update the button update idletasks SetList $oop SetList_Inventory $oop return } # NSStore2::Win98MenuCmd_Buy -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Win98MenuCmd_Buy {oop button} { variable MenuString set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] if {[Platform unix]} { set font [$menu cget -font] $menu configure -font {Helvetica 10} } $menu delete 0 end set doCmd 0 set doItem 0 set doMenu 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set charCmd p set doMenu 1 set doCmd 1 } elseif {[string equal [angband inkey_flags] INKEY_ITEM_STORE]} { set charCmd "" set doMenu 1 set doItem 1 } set match {} if {$doMenu} { set count [angband store count] for {set index 0} {$index < $count} {incr index} { angband store info $index attrib set charItem $attrib(char) $menu add command -label "$charItem $attrib(name)" \ -command "angband keypress $charCmd$charItem" \ -underline 0 lappend match $index } } if {[string compare [$menu index end] none]} { $menu add separator } if {$doItem} { $menu add command -label "Cancel" -command "angband keypress \033" } else { $menu add command -label "Cancel" } if {[angband store ishome]} { set MenuString(MENU_TOOLBAR,home) "Take this item." } else { set MenuString(MENU_TOOLBAR,store) "Buy this item." } Info $oop toolbar,mode buy Info $oop toolbar,match $match Info $oop toolbar,where store [Info $oop canvas] itemconfigure statusBar,status -state "" tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) $menu configure -font $font } after idle "$button hidemenu ; [Info $oop canvas] itemconfigure statusBar,status -state hidden" return } # NSStore2::Win98MenuCmd_Sell -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Win98MenuCmd_Sell {oop button} { variable MenuString set canvas [$button info canvas] set x [winfo rootx $canvas] set y [expr {[winfo rooty $canvas] + [winfo height $canvas]}] set menu [Info $oop toolbar,menu] if {[Platform unix]} { set font [$menu cget -font] $menu configure -font {Helvetica 10} } $menu delete 0 end set doCmd 0 set doItem 0 set doMenu 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set charCmd s set doMenu 1 set doCmd 1 } elseif {[string equal [angband inkey_flags] INKEY_ITEM] && [string equal [angband inkey_other] inventory]} { set charCmd "" set doMenu 1 set doItem 1 } set match {} if {$doMenu} { foreach index [angband inventory find -store_will_buy yes] { angband inventory info $index attrib set charItem $attrib(char) $menu add command -label "$charItem $attrib(name)" \ -command "angband keypress $charCmd$charItem" \ -underline 0 lappend match $index } } if {[string compare [$menu index end] none]} { $menu add separator } if {$doItem} { $menu add command -label "Cancel" -command "angband keypress \033" } else { $menu add command -label "Cancel" } if {[angband store ishome]} { set MenuString(MENU_TOOLBAR,home) "Drop this item." } else { set MenuString(MENU_TOOLBAR,store) "Sell this item." } Info $oop toolbar,mode sell Info $oop toolbar,match $match Info $oop toolbar,where inventory [Info $oop canvas] itemconfigure statusBar,status -state "" tk_popup $menu $x $y if {[Platform unix]} { tkwait variable ::tkPriv(popup) $menu configure -font $font } after idle "$button hidemenu ; [Info $oop canvas] itemconfigure statusBar,status -state hidden" return } # NSStore2::ConfigureWindow -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::ConfigureWindow {oop} { set win [Info $oop win] set frame $win.info eval pack forget [winfo children $frame] pack $frame.howMany \ -side left -expand no -padx 2 pack $frame.quantity \ -side left -expand no -padx 2 # Not in the Home if {![angband store ishome]} { pack $frame.howMuch \ -side left -expand no -padx 2 pack $frame.totalCost \ -side left -expand no -padx 2 } pack $frame.gold \ -side right -expand no -padx 2 pack $frame.playerGold \ -side right -expand no -padx 2 if {![angband store ishome]} { $frame.purse configure -text "Purse: [angband store purse] " pack $frame.purse \ -side right -expand no -padx 2 } set mbarId [Info $oop mbarId] NSMenu::MenuDeleteEntry $mbarId E_DESTROY if {[angband store ishome]} { NSMenu::EntryConfigure $mbarId M_STORE -label "Home" NSMenu::EntryConfigure $mbarId M_SELL -label "Drop" NSMenu::EntryConfigure $mbarId E_STORE_BUY -label "Take" NSMenu::EntryConfigure $mbarId E_STORE_SELL -label "Drop" NSMenu::MenuInsertEntry $mbarId -before E_SEP_1 \ -type command -label "Destroy" -identifier E_DESTROY } else { NSMenu::EntryConfigure $mbarId M_STORE -label "Store" NSMenu::EntryConfigure $mbarId M_SELL -label "Sell" NSMenu::EntryConfigure $mbarId E_STORE_BUY -label "Buy" NSMenu::EntryConfigure $mbarId E_STORE_SELL -label "Sell" } return } # NSStore2::HaggleSetup -- # # Shows or hides display elements during the buy/sell process. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::HaggleSetup {oop action} { set win [Info $oop win] set frame $win.info switch -- $action { haggle_open { $frame.price_character configure -text "" pack forget $frame.howMany $frame.quantity $frame.howMuch \ $frame.totalCost $frame.purse pack $frame.price_owner \ -side left -padx 2 pack $frame.price_character \ -side left -padx 24 } haggle_close { pack forget $frame.price_owner $frame.price_character pack $frame.howMany \ -side left -padx 2 pack $frame.quantity \ -side left -padx 2 pack $frame.howMuch \ -side left -padx 2 pack $frame.totalCost \ -side left -padx 2 pack $frame.purse \ -side right -expand no -padx 2 } } # Stop skipping -more- prompts SkipMoreMessages $oop 0 return } # NSStore2::SetList -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SetList {oop} { variable Priv set win [Info $oop win] set canvas [Info $oop canvas] # Clear and disable the "quantity" entry Info $oop quantity "" $win.info.quantity configure -state disabled # Get the store name set storename [angband store storename] # In the Home if {[angband store ishome]} { # Set the window title wm title $win "Your Home" $canvas itemconfigure store,title -text "Your Home" # Not in the Home } else { # Set the window title wm title $win [format "The %s owned by %s" \ $storename [angband store ownername]] $canvas itemconfigure store,title -text "The $storename" } # Get the number of items set count [angband store count] # Clear the selection SelectionChanged $oop "" "" # Clear the list for {set i 0} {$i < 24} {incr i} { MoveBox $oop -20 -20 store $i } set index 0 for {set item 0} {$item < $count} {incr item} { angband store info $item attrib $canvas itemconfigure icon,store,$item -assign $attrib(icon) set x [Info $oop store,x,$index] set y [Info $oop store,y,$index] MoveBox $oop $x $y store $item incr index Info $oop char,store,$item $attrib(char) } # Display number of objects if {$count == 1} { set string [format "%d item" $count] } else { set string [format "%d items" $count] } $canvas itemconfigure store,status,left -text $string Synch $oop return } # NSStore2::SetList_Inventory -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SetList_Inventory {oop} { set canvas [Info $oop canvas] # Clear the selection SelectionChanged $oop "" "" # Clear the list for {set i 0} {$i <= 23} {incr i} { MoveBox $oop -20 -20 inventory $i } $canvas itemconfigure inventory,status,left -text "" $canvas itemconfigure inventory,status,right -text "" set itemList [angband inventory find -tester yes] # Display items the store will buy in a different color set buyList [angband inventory find -store_will_buy yes] set weight 0 set index 0 foreach item $itemList { angband inventory info $item attrib $canvas itemconfigure icon,inventory,$item -assign $attrib(icon) if {[lsearch -exact $buyList $item] != -1} { set rgb1 [format #%02x%02x%02x 0 124 0] set rgb2 [format #%02x%02x%02x 51 204 0] } else { set rgb1 [format #%02x%02x%02x 102 51 0] set rgb2 [format #%02x%02x%02x 255 153 0] } $canvas itemconfigure border,inventory,$item -outline $rgb1 Info $oop color,inventory,$item $rgb2 set x [Info $oop inventory,x,$index] set y [Info $oop inventory,y,$index] MoveBox $oop $x $y inventory $item incr index Info $oop char,inventory,$item $attrib(char) incr weight [expr {$attrib(weight) * $attrib(number)}] } Info $oop inventory,weight $weight $canvas itemconfigure inventory,status,right -text [fmt_wgt $weight 1] set numItems [llength $itemList] if {$numItems == 1} { set string [format "%d item" $numItems] } else { set string [format "%d items" $numItems] } $canvas itemconfigure inventory,status,left -text $string return } # NSStore2::ContextMenu -- # # When the store list is right-clicked, pop up a context # menu of actions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::ContextMenu {oop menu x y} { set canvas [Info $oop canvas] set font [$menu cget -font] # Clear the menu $menu delete 0 end set itemId [$canvas find withtag current] if {[llength $itemId]} { set tags [$canvas gettags $itemId] set index [lsearch $tags icon,*,*] if {$index != -1} { scan [lindex $tags $index] {icon,%[^,],%s} where index scan [$canvas bbox sel,$where,$index] "%s %s %s %s" left top right bottom set x [expr {[winfo rootx $canvas] + ($right + 1) + 2}] set y [expr {[winfo rooty $canvas] + $top}] BalloonHide $oop } else { set where "" set index "" } } else { set where "" set index "" } set charBuy p set charSell s if {[angband store ishome]} { set stringBuy "Take" set stringSell "Drop" } else { set stringBuy "Buy" set stringSell "Sell" } # No item is hit if {![string length $where]} { # We are waiting for a command if {[string equal [angband inkey_flags] INKEY_CMD]} { $menu add command -label "$stringBuy An Item" \ -command "angband keypress $charBuy" $menu add command -label "$stringSell An Item" \ -command "angband keypress $charSell" $menu add separator $menu add command -label "Leave" \ -command "angband keypress \033" $menu add separator $menu add command -label "Cancel" # Pop up the menu tk_popup $menu $x $y } # Done return } # Get information about this item angband $where info $index attrib # Get the item char set itemKey $attrib(char) # Get the tval set itemTval $attrib(tval) # Get the amount set itemAmount $attrib(number) # An inventory item is hit if {[string compare $where store]} { # We are waiting for an inventory item if {[string equal [angband inkey_flags] INKEY_ITEM]} { # Append a command to select the item set command "angband keypress $itemKey" $menu add command -label "Select This Item" \ -command $command -font [BoldFont $font] $menu add separator $menu add command -label "Cancel" \ -command "angband keypress \033" # Pop up the menu tk_popup $menu $x $y # Done return } # We are not waiting for a command if {[string compare [angband inkey_flags] INKEY_CMD]} { return } # See if the store will accept this item set match [angband inventory find -store_will_buy yes] if {[lsearch -exact $match $index] != -1} { if {$itemAmount > 1} { $menu add command -label "$stringSell One" \ -command "angband keypress 01$charSell$itemKey" \ -font [BoldFont $font] if {$itemAmount > 5} { $menu add command -label "$stringSell Five" \ -command "angband keypress 05$charSell$itemKey" } $menu add command -label "$stringSell All" \ -command "angband keypress 0$itemAmount$charSell$itemKey" } else { $menu add command -label "$stringSell This Item" \ -command "angband keypress $charSell$itemKey" \ -font [BoldFont $font] } $menu add separator $menu add command -label "$stringBuy An Item" \ -command "angband keypress $charBuy" } else { $menu add command -label "$stringBuy An Item" \ -command "angband keypress $charBuy" $menu add command -label "$stringSell An Item" \ -command "angband keypress $charSell" } $menu add separator $menu add command -label "Leave" \ -command "angband keypress \033" $menu add separator $menu add command -label "Cancel" # Pop up the menu tk_popup $menu $x $y # Done return } # We are waiting for a store item if {[string equal [angband inkey_flags] INKEY_ITEM_STORE]} { # Append a command to select the item set command "angband keypress $itemKey" $menu add command -label "Select This Item" -command $command \ -font [BoldFont $font] $menu add separator $menu add command -label "Cancel" -command "angband keypress \033" # Pop up the menu tk_popup $menu $x $y # Done return } # We are not waiting for a command if {[string compare [angband inkey_flags] INKEY_CMD]} { return } if {$itemAmount == 1} { $menu add command -label "$stringBuy This Item" \ -command "angband keypress $charBuy$itemKey" \ -font [BoldFont $font] } else { $menu add command -label "$stringBuy One" \ -command "angband keypress 01$charBuy$itemKey" \ -font [BoldFont $font] if {$itemAmount > 5} { $menu add command -label "$stringBuy Five" \ -command "angband keypress 05$charBuy$itemKey" } $menu add command -label "$stringBuy All" \ -command "angband keypress 0$itemAmount$charBuy$itemKey" } $menu add separator $menu add command -label "$stringSell An Item" \ -command "angband keypress $charSell" if {[angband store ishome]} { # Complex handling of Destroy command. The entire stack is # destroyed, and the user isn't asked to confirm. set command "angband keypress 0${itemAmount}K$itemKey" $menu add separator $menu add command -label "*Destroy*" -command $command } $menu add separator $menu add command -label "Leave" \ -command "angband keypress \033" $menu add separator $menu add command -label "Cancel" # Pop up the menu tk_popup $menu $x $y return } # NSStore2::Invoke_Store -- # # Called when a store item is double-clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Invoke_Store {oop where index} { # See if we are waiting for a command set doCmd 0 if {[string equal [angband inkey_flags] INKEY_CMD]} { set doCmd 1 } # See if we are waiting for a store item set doItem 0 if {[string equal [angband inkey_flags] INKEY_ITEM_STORE]} { set doItem 1 } # Do nothing if {!$doCmd && !$doItem} return # Get the item info angband store info $index attrib # Append a quantity if waiting for a command if {$doCmd} { set keypress "0[Info $oop quantity]p" } # Append item char append keypress $attrib(char) # Feed the Term angband keypress $keypress return } # NSStore2::Invoke_Inventory -- # # Called when an inventory item is double-clicked. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Invoke_Inventory {oop where index} { set char [Info $oop char,$where,$index] # angband $where info $index attrib if {[string equal [angband inkey_flags] INKEY_CMD]} { # If this item can be sold, set the default action to "Sell" set match [angband inventory find -store_will_buy yes] if {[lsearch -exact $match $index] != -1} { set cmdChar s angband keypress $cmdChar$char } } if {[string equal [angband inkey_flags] INKEY_ITEM]} { angband keypress $char } return } # NSStore2::InvokeByReturn -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::InvokeSelected {oop} { # Hack -- Focus on the list focus [Info $oop canvas] # Get the selection set where [Info $oop select,where] set index [Info $oop select,index] if {![string length $where]} return # Invoke Invoke $oop $where $index return } # NSStore2::Invoke -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Invoke {oop where index} { switch -- $where { inventory {Invoke_Inventory $oop $where $index} store {Invoke_Store $oop $where $index} } return } # NSStore2::Select -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Select {oop where index} { if {[string equal $where [Info $oop select,where]] && [string equal $index [Info $oop select,index]]} { # SelectionChanged $oop "" "" return } SelectionChanged $oop $where $index return } # NSStore2::SelectionChanged -- # # Called when the list selection changes. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SelectionChanged {oop where index} { if {[string length [Info $oop select,where]]} { Highlight $oop 0 [Info $oop select,where] [Info $oop select,index] } # Nothing was selected if {![string length $index]} { Info $oop select,where "" Info $oop select,index "" return } # Remember the selected item Info $oop select,where $where Info $oop select,index $index Highlight $oop 1 $where $index BalloonHide $oop return } # NSStore2::QuantityChanged -- # # Trace variable callback on NSStore2($oop,quantity). When the number # of items to buy changes, synch the "total price" field. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::QuantityChanged {oop name1 name2 op} { set win [Info $oop win] set quantity [Info $oop quantity] set where [Info $oop highlight,where] set index [Info $oop highlight,index] if {![string length $where] || [string compare $where store] || \ ![string length $quantity]} { $win.info.totalCost configure -text "" return } angband store info $index attrib if {$quantity <= 0} { set quantity 1 } set max $attrib(number) if {$quantity > $max} { set quantity $max } # Hack -- Handle "fixed" prices (ex "45 F") scan $attrib(cost) %d price # No prices are displayed in the Home if {![angband store ishome]} { $win.info.totalCost configure -text [expr {$quantity * $price}] } Info $oop quantity $quantity return } # NSStore2::Button1 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Button1 {oop x y} { set canvas [Info $oop canvas] focus $canvas set itemId [$canvas find withtag current] if {![llength $itemId] || [string equal [$canvas type current] image]} { if {[string length [Info $oop select,index]]} { SelectionChanged $oop "" "" } return } Info $oop press,x $x Info $oop press,y $y return } # NSStore2::Motion1 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Motion1 {oop x y} { set canvas [Info $oop canvas] set dragging [Info $oop dragging] set where [Info $oop select,where] set index [Info $oop select,index] # No item is selected if {![string length $index]} return # Not dragging if {!$dragging} { set x2 [Info $oop press,x] set y2 [Info $oop press,y] if {(abs($x - $x2) < 10) && (abs($y - $y2) < 10)} return # Copy the image from the dragged item set assign [$canvas itemcget icon,$where,$index -assign] $canvas itemconfigure icon,drag,0 -assign $assign # Copy colors from the drag item set rgb1 [$canvas itemcget border,$where,$index -outline] $canvas itemconfigure border,drag,0 -outline $rgb1 set rgb2 [Info $oop color,$where,$index] $canvas itemconfigure sel,drag,0 -outline $rgb2 # Display the drag feedback ShowBox $oop drag 0 # Drag in progress Info $oop dragging 1 } if {$x - 20 < 0} { set x 20 } elseif {$x + 20 > [winfo width $canvas]} { set x [expr {[winfo width $canvas] - 20}] } if {$y - 20 < 0} { set y 20 } elseif {$y + 20 > [winfo height $canvas]} { set y [expr {[winfo height $canvas] - 20}] } MoveBox $oop $x $y drag 0 return } # NSStore2::Release1 -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Release1 {oop x y} { set canvas [Info $oop canvas] set where [Info $oop select,where] set index [Info $oop select,index] if {![Info $oop dragging]} return HideBox $oop drag 0 Info $oop dragging 0 set charItem [Info $oop char,$where,$index] if {$x < [Info $oop width,store]} { set dest store } else { set dest inventory } if {[string equal $where $dest]} return switch -- $where { inventory { DoUnderlyingCommand s$charItem SkipMoreMessages $oop 1 } store { angband keypress "0[Info $oop quantity]p$charItem" SkipMoreMessages $oop 1 } } return } # NSStore2::Highlight -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Highlight {oop state where index} { if {[Info $oop busy]} return Info $oop busy 1 set canvas [Info $oop canvas] set entry [Info $oop win].info.quantity if {$state} { # Highlight $canvas itemconfigure sel,$where,$index \ -outline [Info $oop color,$where,$index] angband $where info $index attrib # Balloon scan [$canvas bbox sel,$where,$index] "%s %s %s %s" left top right bottom set x [expr {[winfo rootx $canvas] + $left + 3 + [icon size] / 2}] set y [expr {[winfo rooty $canvas] + ($bottom + 1) + 2}] set char [Info $oop char,$where,$index] set text "$char\) $attrib(name)" BalloonShow $oop $text $x $y # This must come before QuantityChanged() Info $oop highlight,where $where Info $oop highlight,index $index if {[string equal $where store]} { $entry configure -state normal Info $oop quantity "1" } if {[string equal $where store]} { set weight [fmt_wgt $attrib(weight) 1] $canvas itemconfigure $where,status,right -text $weight } else { set weight [expr {$attrib(weight) * $attrib(number)}] set weight [fmt_wgt $weight] set total [fmt_wgt [Info $oop $where,weight] 1] $canvas itemconfigure $where,status,right -text "$weight/$total" } NSRecall::RecallObject $where $index } else { # Un-highlight $canvas itemconfigure sel,$where,$index \ -outline [Info $oop color2,$where,$index] BalloonHide $oop # This must come before QuantityChanged() Info $oop highlight,where "" Info $oop highlight,index "" if {[string equal $where store]} { Info $oop quantity "" $entry configure -state disabled } if {[string equal $where store]} { $canvas itemconfigure $where,status,right -text "" } else { $canvas itemconfigure $where,status,right \ -text [fmt_wgt [Info $oop $where,weight] 1] } } Info $oop busy 0 return } # NSStore2::Enter -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Enter {oop where index} { if {[string length [Info $oop select,where]]} return Highlight $oop 1 $where $index return } # NSStore2::Leave -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Leave {oop where index} { if {![string length [Info $oop select,index]]} { Highlight $oop 0 $where $index } return } # NSStore2::Request -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Request {oop type where index} { Info $oop request [list $type $where $index] if {![string length [Info $oop request,id]]} { Info $oop request,id [after 1 NSStore2::HandleRequest $oop] } return } # NSStore2::HandleRequest -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::HandleRequest {oop} { if {[Info $oop busy]} { Info $oop request,id [after 1 NSStore2::HandleRequest $oop] return } set request [Info $oop request] Info $oop request {} set type [lindex $request 0] set where [lindex $request 1] set index [lindex $request 2] if {[string length [Info $oop highlight,where]]} { Leave $oop [Info $oop highlight,where] [Info $oop highlight,index] } # Because this is called as an "after" script, it is possible that: # a) the character has already left the store # b) the item no longer exists # Both of these conditions can give an error, so we check for both. if {![angband store shopping]} { Info $oop request,id "" return } if {$index >= [angband $where count]} { Info $oop request,id "" return } if {[string equal $type enter]} { Enter $oop $where $index } # Clear this after above stuff. See Request() Info $oop request,id "" if {[llength [Info $oop request]]} { Info $oop request,id [after 1 NSStore2::HandleRequest $oop] } return } # NSStore2::BalloonShow -- # # Show the balloon. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::BalloonShow {oop text x y} { # XXX We used to use NSBalloon, but some users said the # balloon would disappear randomly. if {0} { NSBalloon::Show $text $x $y n return } set canvas [Info $oop canvas] incr x -[winfo rootx $canvas] incr y -[winfo rooty $canvas] set font [Global font,sys,normal] set textWidth [font measure $font $text] set textHeight [font metrics $font -linespace] set padx 4 set pady 2 set rectWidth [expr {$padx + $textWidth + $padx + 1}] set rectHeight [expr {$pady + $textHeight + $pady + 1}] # Not too far left if {$x - $rectWidth / 2 < 0} { incr x [expr {0 - ($x - $rectWidth / 2)}] # Not too far right } elseif {$x + $rectWidth / 2 > [winfo width $canvas]} { incr x [expr {[winfo width $canvas] - ($x + $rectWidth / 2)}] } $canvas coords balloon,rect [expr {$x - $rectWidth / 2}] $y \ [expr {$x + $rectWidth / 2}] [expr {$y + $rectHeight}] $canvas coords balloon,text $x [expr {$pady + $y + 1}] $canvas itemconfigure balloon,text -text $text # Show the items $canvas itemconfigure balloon -state normal return } # NSStore2::BalloonHide -- # # Hide the balloon. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::BalloonHide {oop} { if {0} { NSBalloon::Hide return } set canvas [Info $oop canvas] # Hide the items $canvas itemconfigure balloon -state hidden return } # NSStore2::Track -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::TrackInventory {oop} { SetList_Inventory $oop return } # NSStore2::ChooseItem -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::ChooseItem {oop show other} { if {[string compare $other inventory]} return Info $oop choose,show $show Info $oop choose,other $other if {$show} { Info $oop didChoose 0 } return } # NSStore2::TermInkey -- # # Handle quasi-event. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::TermInkey {oop} { if {[Info $oop choose,show]} { # Show the choices SetList_Inventory $oop Info $oop didChoose 1 } elseif {![Info $oop didChoose]} { # Nothing } else { # Show all items SetList_Inventory $oop } return } # NSStore2::MakeBorder -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MakeBorder {oop canvas x y width height} { set left [expr {$x + 1}] set top [expr {$y + 1}] set right [expr {$x + $width - 2}] set bottom [expr {$y + $height - 2}] # 3-pixel border $canvas create rectangle $left $top $right $bottom \ -outline #282828 -fill {} -width 3.0 # 1-pixel border $canvas create rectangle $left $top $right $bottom \ -outline #0070FF -fill {} return } # NSStore2::MakeHeader -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MakeHeader {oop canvas x y width text tags} { if {[Platform unix]} { set font {Helvetica 12 bold} } if {[Platform windows]} { set font {Helvetica 10 bold} } set fontHeight [font metrics $font -linespace] set left [expr {$x + 2}] set top [expr {$y + 2}] set right [expr {$x + $width - 3}] set bottom [expr {$top + 1 + $fontHeight + 1}] $canvas create rectangle $left $top $right $bottom \ -outline #282828 -fill #0000D4 $canvas create text [expr {$x + $width / 2}] [expr {$top + 1}] \ -text $text -font $font -fill White -anchor n -tags $tags return } # NSStore2::MakeStatus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MakeStatus {oop canvas x y width where} { set font [Value font,statusBar] set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] # 3-pixel border + background $canvas create rectangle [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] \ -outline #282828 -fill #0000D4 -width 3.0 \ -tags [list statusBar,rect $where,status] # 1-pixel border $canvas create rectangle [expr {$x + 1}] [expr {$y + 1}] \ [expr {$x + $width - 2}] [expr {$y + $height - 2}] \ -outline #0070FF -fill {} \ -tags [list statusBar,rect $where,status] # Left text $canvas create text [expr {$x + 4}] \ [expr {$y + 3}] -anchor nw -fill gray -font $font \ -tags [list statusBar,text $where,status $where,status,left] # Right text $canvas create text [expr {$x + $width - 4}] \ [expr {$y + 3}] -anchor ne -fill gray -font $font \ -tags [list statusBar,text $where,status $where,status,right] foreach side {left right} { $canvas bind $where,status,$side \ "NSStore2::BindStatus $oop enter $where $side" $canvas bind $where,status,$side \ "NSStore2::BindStatus $oop leave $where $side" } return } # NSStore2::BindStatus -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::BindStatus {oop message where side} { if {$message == "leave"} { NSMainWindow::StatusText [Global main,oop] "" return } set string "" switch -- $where { inventory { switch -- $side { left { set string "Number of inventory items" } right { set string "Weight of inventory items" } } } store { switch -- $side { left { set string "Number of store items" } right { } } } } if {[string length $string]} { NSMainWindow::StatusText [Global main,oop] $string } return } # NSStore2::MakeBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MakeBox {oop canvas where index {rgb1 ""} {rgb2 ""}} { set width [expr {[icon size] + 8}] set height [expr {[icon size] + 8}] if {![string length $rgb1]} { set rgb1 [format #%02x%02x%02x 102 51 0] set rgb2 [format #%02x%02x%02x 255 153 0] } # 3-pixel border set outline $rgb1 set fill [format #%02x%02x%02x 68 68 68] $canvas create rectangle 2 2 \ [expr {$width - 2}] [expr {$height - 2}] \ -fill $fill -outline $outline -width 3.0 \ -tags "$where,$index border,$where,$index" # 1-pixel border $canvas create rectangle 2 2 \ [expr {$width - 2}] [expr {$height - 2}] \ -fill {} -outline {} -tags "$where,$index sel,$where,$index" Info $oop color,$where,$index $rgb2 Info $oop color2,$where,$index "" if 0 { # Widget $canvas create widget [expr {$width / 2}] \ [expr {$height / 2}] -assign {icon none 0} \ -tags "icon $where,$index icon,$where,$index" -anchor center # No bindings on the drag box if {[string equal $where drag]} return $canvas bind icon,$where,$index \ "NSStore2::Request $oop enter $where $index" $canvas bind icon,$where,$index \ "NSStore2::Request $oop leave $where $index" $canvas bind icon,$where,$index \ "NSStore2::Select $oop $where $index" $canvas bind icon,$where,$index \ "NSStore2::Invoke $oop $where $index" } return } # NSStore2::MoveBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::MoveBox {oop x y where index} { set canvas [Info $oop canvas] scan [$canvas coords icon,$where,$index] "%s %s" cx cy $canvas move $where,$index [expr {$x - $cx}] [expr {$y - $cy}] return } # NSStore2::ShowBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::ShowBox {oop where index} { set canvas [Info $oop canvas] if {[info tclversion] >= 8.3} { $canvas itemconfigure $where,$index -state "" } return } # NSStore2::HideBox -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::HideBox {oop where index} { set canvas [Info $oop canvas] if {[info tclversion] >= 8.3} { $canvas itemconfigure $where,$index -state hidden } else { MoveBox $oop -20 -20 $where $index } return } # NSStore2::Swap -- # # Display the "old" Store Window # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::Swap {oop} { NSWindowManager::Undisplay store2 NSModule::LoadIfNeeded NSStore NSWindowManager::Display store Value store,style old return } # NSStore2::ValueChanged_font_statusBar -- # # Called when the font,statusBar value changes. # Updates the Main Window statusbar. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::ValueChanged_font_statusBar {oop} { set win [Info $oop win] set canvas [Info $oop canvas] # Get the desired font set font [Value font,statusBar] # Update the font. Too bad there isn't a -fontvar font variable $canvas itemconfigure statusBar,text -font $font set fontHeight [font metrics $font -linespace] set height [expr {3 + $fontHeight + 3}] foreach itemId [$canvas find withtag statusBar,rect] { scan [$canvas coords $itemId] "%s %s %s %s" left top right bottom set bottom [expr {$top - 1 + $height - 2}] $canvas coords $itemId $left $top $right $bottom } scan [$canvas bbox statusBar,rect] "%d %d %d %d" left top right bottom $canvas configure -height $bottom wm geometry $win "" return } # NSStore2::SkipMoreMessages -- # # Skip -more- prompts during drag & drop, # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSStore2::SkipMoreMessages {oop skip} { if {$skip == [Info $oop skipMore]} { return } Info $oop skipMore $skip return } proc Store2Obj {command args} { return [eval NSStore2::$command [Global store2,oop] $args] } zangband/lib/script/tk/term.tcl0000644000000000000000000000301410250356274015505 0ustar rootroot# File: term.tcl # Purpose: the Main Terminal # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. namespace eval NSTerm { # namespace eval NSTerm } # NSTerm::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTerm::InitModule {} { } # NSTerm::NSTerm -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTerm::NSTerm {oop parent width height gwidth gheight} { variable win set widget $parent.term$oop #update idletasks widget $widget -width $width -height $height \ -gwidth $gwidth -gheight $gheight -term 1 # Shift-drag does nothing bind $widget break bind $widget break bind $widget break # Disable tracking when dragging bind $widget break bind $widget break # This binding is called whenever the window is resized # by the user. bind $widget \ "NSTerm::Resize $oop %w %h" # Feed the Term when keys are pressed Term_KeyPress_Bind $widget pack $widget -expand yes -fill both return } proc NSTerm::Resize {oop width height} { variable win .term.term$oop configure -width $width -height $height return 1 } proc NSTerm::Close {oop} { #wm iconify .term.term$oop } zangband/lib/script/tk/tips.tcl0000644000000000000000000001577110250356274015532 0ustar rootroot# File: tips.tcl # Purpose: the Tips Window and related commands # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSTips { variable Priv # namespace eval NSTips } # NSTips::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::InitModule {} { variable Priv set Priv(current) [Value tip,current] set Priv(showTips) [Value tip,show] # NSValueManager::AddClient showTips \ # "set NSTips::Priv(showTips) [Value showTips]" ReadTipsFile NSObject::New NSTips return } # NSTips::CloseModule -- # # One-time-only-ever cleanup. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::CloseModule {} { } # NSTips::ReadTipsFile -- # # Read the tips.txt file. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::ReadTipsFile {} { global Angband variable Priv set tips_file "tips.txt" if {[catch {open [PathTk $tips_file]} fileId]} { set prompt "The following error occurred while attempting to open " append prompt "the \"$tips_file\" file for reading" HandleError $fileId $prompt return } set Priv(count) 0 while 1 { if {[gets $fileId lineBuf] <= 0} break lappend Priv(tips) $lineBuf incr Priv(count) } close $fileId return } # NSTips::NSTips -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::NSTips {oop} { InitWindow $oop DisplayNextTip $oop # # Global list of application windows # Global tip,oop $oop Window tip [Info $oop win] return } # NSAssign::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::Info {oop info args} { global NSTips # Verify the object NSObject::CheckObject NSTips $oop # Set info if {[llength $args]} { switch -- $info { default { set NSTips($oop,$info) [lindex $args 0] } } # Get info } else { switch -- $info { default { return $NSTips($oop,$info) } } } return } # NSTips::InitWindow -- # # Initialize the Tips Window. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::InitWindow {oop} { global Angband variable Priv set win .tips$oop toplevel $win wm title $win "ZAngband Tips" wm transient $win [Window main] # Do stuff when window closes wm protocol $win WM_DELETE_WINDOW "NSTips::Close $oop" Info $oop win $win # Header if {[Platform unix]} { set font {Times 24 bold} } if {[Platform windows]} { set font {Times 18 bold} } label $win.header \ -text "Welcome to ZAngband" -font $font # Tip display frame $win.tip \ -borderwidth 1 -relief sunken if {[Platform unix]} { set font {Courier 12} } if {[Platform windows]} { set font {Courier 9} } text $win.tip.text \ -font $font -width 46 -height 10 -cursor {} \ -background #FFFFE0 -borderwidth 0 -highlightthickness 0 \ -wrap word -takefocus 0 bindtags $win.tip.text [list $win.tip.text $win all] pack $win.tip.text \ -fill both frame $win.misc \ -borderwidth 0 # Checkbutton checkbutton $win.misc.checkshow \ -text "Show this window every time the game starts" \ -variable NSTips::Priv(showTips) \ -command "Value tip,show \$NSTips::Priv(showTips)" # Tip number label $win.misc.tipnumber \ -anchor e pack $win.misc.checkshow \ -side left -expand no pack $win.misc.tipnumber \ -side right -expand no # Divider MakeDivider $win.divider1 x # Buttons frame $win.buttons \ -borderwidth 0 button $win.buttons.back \ -text "< Back" -command "NSTips::DisplayPreviousTip $oop" -width 11 \ -underline 2 button $win.buttons.next \ -text "Next >" -command "NSTips::DisplayNextTip $oop" -width 11 \ -underline 0 -default active button $win.buttons.close \ -text "Close" -command "NSTips::Close $oop" -width 11 pack $win.buttons.close \ -side right -pady 5 -padx 5 pack $win.buttons.next \ -side right -pady 5 -padx 5 pack $win.buttons.back \ -side right -pady 5 -padx 5 # Geometry grid columnconfigure $win 0 -weight 0 grid rowconfigure $win 0 -weight 0 grid rowconfigure $win 1 -weight 0 grid rowconfigure $win 2 -weight 0 grid rowconfigure $win 3 -weight 0 grid rowconfigure $win 4 -weight 0 grid $win.header \ -row 0 -column 0 -rowspan 1 -columnspan 2 -sticky w grid $win.tip \ -row 1 -column 0 -rowspan 1 -columnspan 1 -padx 5 -pady 5 grid $win.misc \ -row 2 -column 0 -rowspan 1 -columnspan 2 -padx 5 -sticky ew grid $win.divider1 \ -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky ew -padx 5 grid $win.buttons \ -row 4 -column 0 -rowspan 1 -columnspan 1 -sticky e NSUtils::SetDefaultButton $win $win.buttons.next bind $win "tkButtonInvoke $win.buttons.back" bind $win "tkButtonInvoke $win.buttons.back" bind $win "tkButtonInvoke $win.buttons.back" bind $win "tkButtonInvoke $win.buttons.next" bind $win "tkButtonInvoke $win.buttons.next" bind $win "tkButtonInvoke $win.buttons.next" bind $win "NSUtils::InvokeDefaultButton $win" bind $win "tkButtonInvoke $win.buttons.close" WindowPosition $win 2 3 focus $win return } # NSTips::Close -- # # Description. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::Close {oop} { variable Priv wm withdraw [Info $oop win] Value tip,current $Priv(current) return } # NSTips::DisplayTip -- # # Display a tip. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::DisplayTip {oop tip} { variable Priv set win [Info $oop win] set text $win.tip.text $text delete 1.0 end if {[Platform unix]} { set font {Helvetica 12} } if {[Platform windows]} { set font {Helvetica 10} } $text insert end "Did you know...\n" tag1 $text tag configure tag1 -font [BoldFont $font] \ -lmargin1 20 -spacing1 20 -spacing3 15 $text insert end $tip tag2 $text tag configure tag2 -font $font \ -lmargin1 20 -lmargin2 20 -rmargin 20 $win.misc.tipnumber configure -text "[expr {$Priv(current) + 1}]/$Priv(count)" return } # NSTips::DisplayNextTip -- # # Display the next tip, wrapping if required. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::DisplayNextTip {oop} { variable Priv set current $Priv(current) incr current if {$current >= $Priv(count)} { set current 0 } set Priv(current) $current DisplayTip $oop [lindex $Priv(tips) $current] return } # NSTips::DisplayPreviousTip -- # # Display the previous tip, wrapping if required. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSTips::DisplayPreviousTip {oop} { variable Priv set current $Priv(current) incr current -1 if {$current < 0} { set current [expr {$Priv(count) - 1}] } set Priv(current) $current DisplayTip $oop [lindex $Priv(tips) $current] return } zangband/lib/script/tk/value-manager.tcl0000644000000000000000000001673310250356274017276 0ustar rootroot# File: value-manager.tcl # Purpose: the Value Manager # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # # The Value Manager records various values, and informs clients when those # values are modified. namespace eval NSValueManager { variable Command variable Value variable ValuePriv # namespace eval NSValueManager } # NSValueManager::InitModule -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::InitModule {} { global Angband variable Value variable ValuePriv # Each client gets a unique ID set ValuePriv(clientId) 0 # These are the default values. The tk/config/value file is sourced # below to set user-defined values. Manage listBG [palette set 253] Manage listHilite [palette set 158] Manage listInactive [palette set 249] # These are the standard Angband terminal colors mapped to # entries in our 256-color palette. They can be accessed via # "Value TERM_XXX". Manage TERM_DARK [palette set 255] Manage TERM_WHITE [palette set 0] Manage TERM_SLATE [palette set 250] Manage TERM_ORANGE [palette set 17] Manage TERM_RED [palette set 217] Manage TERM_GREEN [palette set 227] Manage TERM_BLUE [palette set 204] Manage TERM_UMBER [palette set 101] Manage TERM_L_DARK [palette set 129] Manage TERM_L_WHITE [palette set 247] Manage TERM_VIOLET [palette set 30] Manage TERM_YELLOW [palette set 5] Manage TERM_L_RED [palette set 35] Manage TERM_L_GREEN [palette set 185] ; # ~LawnGreen Manage TERM_L_BLUE [palette set 180] ; # ~turquoise2 Manage TERM_L_UMBER [palette set 52] Manage tip,current 1000 Manage tip,show 1 Manage progress,showBars 1 Manage progress,showNumbers 1 Manage recall,show 1 Manage recall,showicon 1 Manage record,dump 1 Manage record,message 1 Manage record,photo 1 Manage bigmap,scale 4 Manage map,detail high Manage main,statusbar,color White ; # #80FFFF Manage message,float 0 Manage misc,mode,exp 1 Manage misc,mode,armor_class 1 Manage misc,textLabels [expr {[winfo screenwidth .] < 800}] Manage misc,layout tall Manage misc,float 0 Manage messages,max 256 Manage settings,showUnused 0 # Icon configuration Manage config,prefix dg32 Manage choicewindow,show 0 Manage choicewindow,autoexpand 1 Manage choicewindow,showicon 0 Manage inventory,alwaysOnTop 1 Manage inventory,style new Manage store,style new Manage message2window,show 0 Manage font,autobar [Global font,sys,normal] Manage font,choice [Global font,sys,normal] Manage font,inventory [Global font,sys,normal] Manage font,knowledge [Global font,sys,normal] Manage font,magic [Global font,sys,normal] Manage font,message [Global font,sys,normal] Manage font,messages [Global font,sys,normal] Manage font,misc [Global font,sys,small] Manage font,miscPopup [Global font,sys,normal] Manage font,monster [Global font,sys,normal] Manage font,recall [Global font,sys,large] Manage font,options [Global font,sys,normal] Manage font,status [Global font,sys,normal] Manage font,statusBar [Global font,sys,normal] Manage font,store [Global font,sys,normal] ### One-time warnings to the user. # Show Setup Window first time Manage warning,setup 0 # Manage options,autosave 1 Manage window,autosave 1 LoadValueFile return } # NSValueManager::CloseModule -- # # Called before the game exits. Dumps all values to the tk/config/value # file. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::CloseModule {} { global Angband variable Value variable Write set tempName [NSUtils::TempFileName [PathTk config]] if {[catch {openlf $tempName} fileId]} { set msg "The following error occurred while attempting to open " append msg "the \"value\" file for writing:\n\n$fileId" tk_messageBox -title Oops -message $msg return } puts $fileId "# Automatically generated. Do not edit.\n" foreach name [lsort [array names Value]] { if {[info exists Write($name)]} { set value [eval $Write($name) $name] } else { set value [Get $name] } puts $fileId [list Manage $name $value] } close $fileId set fileName [NSUtils::ReadLink [PathTk config value]] if {[file exists $fileName]} { file rename -force -- $fileName $fileName.bak } file rename -- $tempName $fileName return } # NSValueManager::LoadValueFile -- # # Read tk\config\value. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::LoadValueFile {} { variable Value variable Read set fileName [PathTk config value] if {[file exists $fileName]} { source $fileName } foreach name [array names Read] { if {[info exists Value($name)]} { eval $Read($name) $name } } return } # NSValueManager::AddClient -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::AddClient {name command} { variable Command variable ValuePriv set clientId [incr ValuePriv(clientId)] set Command($name,$clientId) $command return $clientId } # NSValueManager::RemoveClient -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::RemoveClient {name clientId} { variable Command unset Command($name,$clientId) return } # NSValueManager::Manage -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::Manage {name value} { variable Value set Value($name) $value return } # NSValueManager::Set -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::Set {name value} { variable Value set Value($name) $value Changed $name return } # NSValueManager::Get -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::Get {name} { variable Value return $Value($name) } # NSValueManager::Changed -- # # Description # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSValueManager::Changed {name} { variable Command foreach name [array names Command $name,*] { catch {uplevel #0 $Command($name)} err } return } # ColorFromValue -- # # Return the palette RGB color for the value with the given name. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc ColorFromValue {valueName} { set index [NSValueManager::Get $valueName] return [palette set $index] } # Value -- # # Convenience interface to NSValueManager::Get/Set # # Arguments: # arg1 about arg1 # # Results: # What happened. proc Value {name args} { # Set info if {[llength $args]} { NSValueManager::Set $name [lindex $args 0] # Get info } else { return [NSValueManager::Get $name] } return } # ListBackgroundChanged -- # # Configures the background color of the given canvas to the # color of the "listBG" value. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc ListBackgroundChanged {canvas} { $canvas configure -background [Value listBG] return } # default_tval_to_attr -- # # Returns the color associated with the given object tval. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc default_tval_to_attr {tval} { return [Value $tval] } proc DumpValueManager {} { set win .dumpvalue toplevel $win text $win.text pack $win.text foreach name [lsort [array names NSValueManager::Value]] { set value [NSValueManager::Get $name] $win.text insert end "Manage $name $value\n" } return } zangband/lib/script/tk/widget.tcl0000644000000000000000000002746510250356274016041 0ustar rootroot# File: widget.tcl # Purpose: commands for manipulating Widgets # # Copyright (c) 1997-2001 Tim Baker # # This software may be copied and distributed for educational, research, and # not for profit purposes provided that this copyright and statement are # included in all such copies. # namespace eval NSWidget { # namespace eval NSWidget } # NSWidget::InitModule -- # # One-time-only-ever initialization. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::InitModule {} { } # NSWidget::NSWidget -- # # Object constructor called by NSObject::New(). # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::NSWidget {oop parent width height gwidth gheight} { set widget $parent.widget$oop widget $widget -width $width -height $height \ -gwidth $gwidth -gheight $gheight bind $widget "NSWidget::Motion $oop %x %y" bind $widget "NSWidget::Motion $oop %x %y" bind $widget "NSWidget::Leave $oop" # Hack -- When we point to a location, the Recall Window may be # set with information, and we may want to interact with the # Recall Window to see the information. But if the mouse moves # over another grid (on the way to the Recall Window) the # information in the Recall Window may change. So we don't # examine cave locations when the Shift key is down. bind $widget break bind $widget break # Shift-drag does nothing bind $widget break bind $widget "NSWidget::TrackPress $oop %x %y" bind $widget "NSWidget::TrackOnce $oop %x %y" # Disable tracking when dragging bind $widget break bind $widget break bind $widget { %W yview scroll [expr {- (%D / 120) * 4}] units } Info $oop widget $widget Info $oop examined "" Info $oop examineCmd "" Info $oop leaveCmd "" Info $oop scaleCmd "" Info $oop xviewCmd "" Info $oop yviewCmd "" Info $oop track,mouseMoved 0 Info $oop caveyx 0 # Set the checkmark for the current scale Info $oop scale $gwidth return } # NSWidget::Info -- # # Query and modify info. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::Info {oop info args} { global NSWidget # Verify the object NSObject::CheckObject NSWidget $oop # Set info if {[llength $args]} { set NSWidget($oop,$info) [lindex $args 0] # Get info } else { return $NSWidget($oop,$info) } return } # NSWidget::Motion -- # # Call the client's command when the mouse moves over a grid. # # Arguments: # oop OOP ID. # x x location in widget. # y y location in widget. # # Results: # What happened. proc NSWidget::Motion {oop x y} { set pos [PointToCave $oop $x $y] if {![string length $pos]} return if {[string equal [Info $oop examined] $pos]} return Info $oop examined $pos set command [Info $oop examineCmd] if {[string length $command]} { uplevel #0 $command $oop $pos } return } # NSWidget::Leave -- # # Handle the event. # # Arguments: # oop OOP ID. # # Results: # What happened. proc NSWidget::Leave {oop} { Info $oop examined "" set command [Info $oop leaveCmd] if {[string length $command]} { uplevel #0 $command $oop } } # NSWidget::PointToCave -- # # Determine the cave y,x location based on the given # coordinates inside the given widget. # # Arguments: # oop OOP ID. # x x coordinate in Widget. # y y coordinate in Widget. # # Results: # Return "y x". proc NSWidget::PointToCave {oop x y} { set widget [Info $oop widget] # Normally, we want to know which grid the point is over, # and for isometric view this requires accurate hittesting # of the actual icons near the point, instead of just the # floor tile. if {![Info $oop caveyx]} { set str [$widget hittest $x $y] if {[string length $str]} { scan $str "%d %d" cy cx set str "$cy $cx" } return $str } # Vault editor wants floor tile. return [$widget caveyx $x $y] } # NSWidget::SetScale -- # # Sets the resolution of the Widget, but doesn't let the Widget # get any larger than its original dimensions. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::SetScale {oop scale} { set widget [Info $oop widget] if {$scale == [$widget cget -gwidth]} return $widget configure -gwidth $scale -gheight $scale # Context menu Info $oop scale $scale # Hack -- Fully update the widget $widget wipe eval $widget center [$widget center] set command [Info $oop scaleCmd] if {[string length $command]} { uplevel #0 $command } return } # NSWidget::Resize -- # # Change the size of the widget. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::Resize {oop width height} { set widget [Info $oop widget] if 0 { if {($width == [$widget cget -width]) && \ ($height == [$widget cget -height])} { return 0 } } $widget configure -width $width -height $height if 0 { # Hack -- Fully update the widget $widget wipe eval $widget center [$widget center] } return 1 } proc NSWidget::Size {oop _height _width} { upvar $_height height upvar $_width width set widget [Info $oop widget] scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max set height [expr {$y_max - $y_min + 1}] set width [expr {$x_max - $x_min + 1}] return } proc NSWidget::CaveSize {oop _height _width} { upvar $_height height upvar $_width width set widget [Info $oop widget] set h [angband cave height] set w [angband cave width] Size $oop h2 w2 if {$h > $h2} { incr h 2 } if {$w > $w2} { incr w 2 } set height $h set width $w return } # NSWidget::yview -- # # Typical yview command # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::yview {oop cmd args} { set widget [Info $oop widget] scan [$widget center] "%d %d" oy ox scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max set height [expr {$y_max - $y_min + 1}] set caveHgt [angband cave height] if {$caveHgt > $height} { incr caveHgt 2 set fiddle -1 } else { set fiddle 0 } switch $cmd { moveto { set fraction [lindex $args 0] if {$fraction > 1.0} { set fraction 1.0 } elseif {$fraction < 0} { set fraction 0 } set top [expr {int($fraction * double($caveHgt) + 0.5)}] incr top $fiddle set ny [expr {$top + $height / 2}] } scroll { set number [lindex $args 0] set what [lindex $args 1] switch $what { units { set ny [expr {$oy + $number}] } pages { set pageSize [expr {$height - 10}] set ny [expr {$oy + $number * $pageSize}] } } } } set ny [ConstrainCenter $ny $caveHgt $height] # Do nothing if position unchanged if {$oy == $ny} return $widget center $ny $ox set command [Info $oop yviewCmd] if {[string length $command]} { uplevel #0 $command } return } # NSWidget::xview -- # # Typical xview command # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::xview {oop cmd args} { set widget [Info $oop widget] scan [$widget center] "%d %d" oy ox scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max set width [expr {$x_max - $x_min + 1}] set caveWid [angband cave width] if {$caveWid > $width} { incr caveWid 2 set fiddle -1 } else { set fiddle 0 } switch $cmd { moveto { set fraction [lindex $args 0] if {$fraction > 1.0} { set fraction 1.0 } elseif {$fraction < 0} { set fraction 0 } set left [expr {int($fraction * double($caveWid) + 0.5)}] incr left $fiddle set nx [expr {$left + $width / 2}] } scroll { set number [lindex $args 0] set what [lindex $args 1] switch $what { units { set nx [expr {$ox + $number}] } pages { set pageSize [expr {$width - 10}] set nx [expr {$ox + $number * $pageSize}] } } } } set nx [ConstrainCenter $nx $caveWid $width] # Do nothing if position unchanged if {$ox == $nx} return $widget center $oy $nx set command [Info $oop xviewCmd] if {[string length $command]} { uplevel #0 $command } return } # NSWidget::TrackPress -- # # Handles events # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::TrackPress {oop x y} { Info $oop track,x $x Info $oop track,y $y Info $oop track,mouseMoved 0 return } # NSWidget::TrackOnce -- # # Handles events # # Arguments: # arg1 about arg1 # # Results: # What happened. proc NSWidget::TrackOnce {oop x y} { # Get the widget set widget [Info $oop widget] # Get the scale set scale [$widget cget -gwidth] # Calculate the distance the pointer moved set dx [expr {[Info $oop track,x] - $x}] set dy [expr {[Info $oop track,y] - $y}] # Require minimum movement if {abs($dx) < $scale} { set dx 0 } if {abs($dy) < $scale} { set dy 0 } # If the pointer didn't move, do nothing if {!$dx && !$dy} { return } # Remember the pointer moved Info $oop track,mouseMoved 1 # Remember the current center scan [$widget center] "%d %d" oy ox # We should scroll horizontally if {$dx} { # Convert from pixels to grid size set dx [expr {$dx / $scale}] # Scroll the Widget xview $oop scroll $dx units } # We should scroll vertically if {$dy} { # Convert from pixels to grid size set dy [expr {$dy / $scale}] # Scroll the Widget yview $oop scroll $dy units } # Get the new center scan [$widget center] "%d %d" ny nx # Remember the current pointer position if {$nx != $ox} { Info $oop track,x $x } # Remember the current pointer position if {$ny != $oy} { Info $oop track,y $y } return } # WidgetCenter -- # # When the character goes to a new level (or WOR back to a level) this # routine sets the center of the given widget. The widget is centered # on the character position. # # Arguments: # widget Widget to center # # Results: # What happened. proc WidgetCenter {widget} { scan [angband player position] "%d %d" y x $widget center $y $x return "$y $x" } # ClipCenter -- # # Helper command used control scrolling of a widget when updating the # character's position. # # Arguments: # _coord Name of variable holding # center Current widget center. # units Cave height or width. # units2 Widget height or width. # # Results: # What happened. proc ClipCenter {_coord center units units2} { upvar $_coord coord set min [expr {$center - $units2 / 2}] set max [expr {$min + $units2 - 1}] set bord [expr {$units2 / 8}] set pad [expr {$units2 / 4}] if {$coord < $min + $bord} { set coord [expr {($coord + $pad) - $units2 / 2}] if {$units2 % 2 == 0} {incr coord} set scroll 1 } elseif {$coord > $max - $bord} { set coord [expr {($coord - $pad) + $units2 / 2}] set scroll 1 } else { set coord $center set scroll 0 } if {$scroll} { if {$units > $units2} { set centerMin [expr {$units2 / 2 - 1}] set centerMax [expr {$units - $units2 / 2 + 1}] if {$units2 & 1} {incr centerMax -1} if {$coord < $centerMin} { set coord $centerMin } elseif {$coord > $centerMax} { set coord $centerMax } elseif {$coord == $centerMin + 1} { set coord $centerMin } elseif {$coord == $centerMax - 1} { set coord $centerMax } } else { set coord [expr {($units - $units2) / 2 + $units2 / 2}] } } return $scroll } # ConstrainCenter -- # # Call this when you want to set the x/y center of a widget but do # not want the widget to scroll "too far". This calculation adds a # 1-grid border around the edge of the cave. # # Arguments: # arg1 about arg1 # # Results: # What happened. proc ConstrainCenter {coord units units2} { if {$units > $units2} { set centerMin [expr {$units2 / 2 - 1}] set centerMax [expr {$units - $units2 / 2 - 1}] if {$units2 & 1} {incr centerMax -1} if {$coord < $centerMin} { set coord $centerMin } elseif {$coord > $centerMax} { set coord $centerMax } } else { set coord [expr {($units - $units2) / 2 + $units2 / 2}] } return $coord } zangband/lib/script/tk/tips.txt0000644000000000000000000000443210250356274015557 0ustar rootrootYou can view these tips at any time through the Help Menu. You can choose which graphic setup to play with before opening a game through the "Setup" button. You can change the scale of the Micro Map with a popup menu. You can scroll the Micro Map by clicking and dragging it. You can center the Main Window by Shift-clicking the Micro Map. You can bring up the dungeon map by clicking in the Micro Map Window. You can change the scale of the dungeon map with a popup menu. You can scroll the dungeon map by clicking and dragging it. You can hide the dungeon map by clicking in it. You can assign sounds to over 130 events in the Sound Preferences. You can assign multiple sounds to each event in the Sound Preferences. You can assign a sound to any monster in the Assign Preferences. You can customize game colors in the Color Preferences. You can change the appearance of the Progress Window with a popup menu. You can bring up the Character Window by clicking the character image in the Misc Window. You can change the character's name in the Character Window. You can toggle the depth display in the Main Window by clicking in it. You can select an item to use by right-clicking in the Main Window when prompted. You can select an item to use by double-clicking in the Inventory Window when prompted. You can select an item to use by clicking in the Recall Window when prompted. When an item is highlighted in the Inventory Window, its properties are displayed in the Recall Window. Moving the cursor into the Recall Window causes the window to grow to show all of its content. Double-clicking an item in the Store Window initiates a purchase. You can bring up a popup menu of items to use by Control-right-clicking in the Main Window. You can make the character run by Shift-clicking in the Main Window. You can make the character run by right-clicking in the Main Window. You can make the character tunnel by Control-clicking in the Main Window. You can see the previous message by clicking in the Message Window. You can bring up the Message History Window by double-clicking the Message Window. You can repeat the last command by typing 'n'. Most windows can be closed by typing ESCAPE. Inscribing your ammo with =g causes it to automatically be picked up, regardless of any pickup options. zangband/lib/script/tk/makefile.zb0000644000000000000000000000035410250356274016150 0ustar rootrootsubdir = ./lib/script/tk/ ## makefile.zb srcfiles += lib/script/tk/makefile.zb files += lib/script/tk/*.tcl lib/script/tk/tips.txt dirs := config image library dirlist := $(dirlist) $(addprefix $(subdir),$(dirs)) include $(scandir) zangband/lib/script/init.lua0000644000000000000000000000033010250356274015060 0ustar rootroot-- Redirect error messages to Angband's msg_print() _ALERT = function(text) msg_print(text) end dofile(build_script_path("object.lua")) dofile(build_script_path("spell.lua")) dofile(build_script_path("field.lua")) zangband/lib/script/object.lua0000644000000000000000000001221310250356274015366 0ustar rootroot-- Helper functions for complicated object activations function cure_potion(hp, do_unblind, do_unconfuse, do_unpoison) local ident = FALSE if hp_player(hp) then ident = TRUE end if do_unblind and clear_blind() then ident = TRUE end if do_unconfuse and clear_confused() then ident = TRUE end if do_unpoison and clear_poisoned() then ident = TRUE end if do_unpoison and clear_stun() then ident = TRUE end if do_unpoison then if clear_cut() then ident = TRUE end elseif do_unconfuse then if inc_cut(-50) then ident = TRUE end elseif do_unblind then if inc_cut(-10) then ident = TRUE end end return ident end function do_curing() local ident = FALSE if cure_potion(200, TRUE, TRUE, TRUE) then ident = TRUE end if clear_image() then ident = TRUE end return ident end function do_dream() if ironman_nightmare then msgf("A horrible vision enters your mind.") -- Have some nightmares have_nightmare() return TRUE else return FALSE end end function wand_of_wonder(dir) local sval local ident = FALSE sval = randint0(SV_WAND_WONDER) if sval == SV_WAND_HEAL_MONSTER then if heal_monster(dir) then ident = TRUE end elseif sval == SV_WAND_HASTE_MONSTER then if speed_monster(dir) then ident = TRUE end elseif sval == SV_WAND_CLONE_MONSTER then if clone_monster(dir) then ident = TRUE end elseif sval == SV_WAND_TELEPORT_AWAY then if teleport_monster(dir) then ident = TRUE end elseif sval == SV_WAND_DISARMING then if disarm_trap(dir) then ident = TRUE end elseif sval == SV_WAND_TRAP_DOOR_DEST then if destroy_door(dir) then ident = TRUE end elseif sval == SV_WAND_STONE_TO_MUD then if wall_to_mud(dir) then ident = TRUE end elseif sval == SV_WAND_LITE then msgf("A line of blue shimmering light appears.") lite_line(dir, damroll(6, 8)) ident = TRUE elseif sval == SV_WAND_SLEEP_MONSTER then if sleep_monster(dir) then ident = TRUE end elseif sval == SV_WAND_SLOW_MONSTER then if slow_monster(dir) then ident = TRUE end elseif sval == SV_WAND_CONFUSE_MONSTER then if confuse_monster(dir, 20) then ident = TRUE end elseif sval == SV_WAND_FEAR_MONSTER then if fear_monster(dir, 20) then ident = TRUE end elseif sval == SV_WAND_DRAIN_LIFE then if drain_life(dir, 150) then ident = TRUE end elseif sval == SV_WAND_POLYMORPH then if poly_monster(dir) then ident = TRUE end elseif sval == SV_WAND_STINKING_CLOUD then ident = fire_ball(GF_POIS, dir, 15, 2) elseif sval == SV_WAND_MAGIC_MISSILE then ident = fire_bolt_or_beam(20, GF_MISSILE, dir, damroll(2, 6)) elseif sval == SV_WAND_ACID_BOLT then ident = fire_bolt_or_beam(20, GF_ACID, dir, damroll(6, 8)) elseif sval == SV_WAND_CHARM_MONSTER then ident = charm_monster(dir, 45) elseif sval == SV_WAND_FIRE_BOLT then ident = fire_bolt_or_beam(20, GF_FIRE, dir, damroll(10, 8)) elseif sval == SV_WAND_COLD_BOLT then ident = fire_bolt_or_beam(20, GF_COLD, dir, damroll(6, 8)) elseif sval == SV_WAND_ACID_BALL then ident = fire_ball(GF_ACID, dir, 125, 2) elseif sval == SV_WAND_ELEC_BALL then ident = fire_ball(GF_ELEC, dir, 75, 2) elseif sval == SV_WAND_FIRE_BALL then ident = fire_ball(GF_FIRE, dir, 150, 2) elseif sval == SV_WAND_COLD_BALL then ident = fire_ball(GF_COLD, dir, 100, 2) end return ident end function do_life_potion() msgf("You feel life flow through your body!") restore_level() clear_poisoned() clear_blind() clear_confused() clear_image() clear_stun() clear_cut() do_res_stat(A_STR) do_res_stat(A_CON) do_res_stat(A_DEX) do_res_stat(A_WIS) do_res_stat(A_INT) do_res_stat(A_CHR) -- Recalculate max. hitpoints update_stuff() hp_player(5000) end function werewindle() local i = randint1(13) if n <= 5 then teleport_player(10) elseif n <= 10 then teleport_player(222) elseif n <= 12 then stair_creation() else if (get_check("Leave this level? ") ~= 0) then player.state.leaving = TRUE end end end function restore_all_stats() local ident = FALSE if do_res_stat(A_STR) then ident = TRUE end if do_res_stat(A_INT) then ident = TRUE end if do_res_stat(A_WIS) then ident = TRUE end if do_res_stat(A_DEX) then ident = TRUE end if do_res_stat(A_CON) then ident = TRUE end if do_res_stat(A_CHR) then ident = TRUE end return ident end function summon_controlled(specific) summon_specific(-1, player.px, player.py, player.lev, specific, TRUE, TRUE, TRUE) end function summon_unsafe(specific) if one_in_(3) then if summon_specific(0, player.px, player.py, player.lev * 3 / 2, specific, TRUE, FALSE, FALSE) then if specific == SUMMON_UNDEAD or specific == SUMMON_HI_UNDEAD then msgf("The dead arise... to punish you for disturbing them!") elseif specific == SUMMON_DEMON then msgf("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'") else msgf("You fail to control it!") end end else summon_specific(-1, player.px, player.py, player.lev * 3 / 2, specific, TRUE, FALSE, TRUE) end end function rand_range2(i) return rand_range(i, i * 2) end function inc_oppose_all(turns) inc_oppose_acid(turns) inc_oppose_elec(turns) inc_oppose_fire(turns) inc_oppose_cold(turns) inc_oppose_pois(turns) end function set_obj_flag(object, num, flag) object.flags[num] = bOr(object.flags[num], flag) end zangband/lib/script/spell.lua0000644000000000000000000000161310250356274015241 0ustar rootroot-- Helper functions for various things function summon_monsters(num, kind) local ident; ident = FALSE for k = 0, num do if summon_specific(0, player.px, player.py, player.depth, kind, TRUE, FALSE, FALSE) then ident = TRUE end end return ident end function restore_mana() if player.csp < player.msp then player.csp = player.msp player.csp_frac = 0 msgf("You feel your head clear.") player.redraw = bOr(player.redraw, PR_MANA) player.window = bOr(player.window, PW_PLAYER) player.window = bOr(player.window, PW_SPELL) return TRUE else return FALSE end end function cure_all_mutations() if (player.muta1 ~= 0) or (player.muta2 ~= 0) or (player.muta3 ~= 0) then msgf("You are cured of all mutations.") player.muta1 = 0 player.muta2 = 0 player.muta3 = 0 player.update = bOr(player.update, PU_BONUS) handle_stuff() return TRUE else return FALSE end end zangband/lib/script/test.lua0000644000000000000000000000016210250356274015077 0ustar rootroot-- A very simple test-suite for Lua support in Angband -- Hack - this doesn't work now, it needs some replacing. zangband/lib/script/field.lua0000644000000000000000000001211510250356274015204 0ustar rootroot-- Helper functions for complicated field actions -- -- Hack XXX XXX Convert the char of the monster to a corpse type -- -- There are seven sizes of corpses. -- 0 is large, 6 is small -- function corpse_type(c) if c == 'a' then return 6 end if c == 'b' then return 6 end if c == 'c' then return 5 end if c == 'd' then return 0 end if c == 'e' then return 6 end if c == 'f' then return 4 end if c == 'g' then return 1 end if c == 'h' then return 2 end if c == 'i' then return 5 end if c == 'j' then return 3 end if c == 'k' then return 4 end if c == 'l' then return 0 end if c == 'm' then return 6 end if c == 'n' then return 3 end if c == 'o' then return 3 end if c == 'p' then return 2 end if c == 'q' then return 4 end if c == 'r' then return 6 end if c == 's' then return 2 end if c == 't' then return 2 end if c == 'u' then return 3 end if c == 'v' then return 4 end if c == 'w' then return 5 end if c == 'x' then return 5 end if c == 'y' then return 4 end if c == 'z' then return 3 end if c == 'A' then return 2 end if c == 'B' then return 5 end if c == 'C' then return 5 end if c == 'D' then return 0 end if c == 'E' then return 3 end if c == 'F' then return 4 end if c == 'G' then return 3 end if c == 'H' then return 2 end if c == 'I' then return 6 end if c == 'J' then return 5 end if c == 'K' then return 3 end if c == 'L' then return 1 end if c == 'M' then return 1 end if c == 'N' then return 3 end if c == 'O' then return 1 end if c == 'P' then return 0 end if c == 'Q' then return 3 end if c == 'R' then return 5 end if c == 'S' then return 6 end if c == 'T' then return 1 end if c == 'U' then return 0 end if c == 'V' then return 1 end if c == 'W' then return 6 end if c == 'X' then return 1 end if c == 'Y' then return 1 end if c == 'Z' then return 5 end if c == ',' then return 6 end return 3; end -- -- Initialise a corpse field -- function corpse_init(r_idx) local race field.data[1] = r_idx / 256 field.data[2] = mod(r_idx, 256) race = monst_race(r_idx) set_corpse_size(field, corpse_type(race.d_char)) notice_field(field) end -- -- Look at a corpse -- function corpse_look() local race race = monst_race(field.data[1] * 256 + field.data[2]) if (bAnd(race.flags[0], RF0_UNIQUE) ~= 0) then -- Copy name to the output string. name = field_name(field).." of "..mon_race_name(race) else -- Copy name to the output string. name = mon_race_name(race).." "..field_name(field) end end -- -- Corpses Decay -- function corpse_decay() local name local r_idx local race r_idx = field.data[1] * 256 + field.data[2] race = monst_race(r_idx) name = mon_race_name(race) if (ironman_nightmare == TRUE) then -- Summon a monster nearby if possible if (summon_cloned_creature(field.fx, field.fy, race, FALSE)) then if (visible == TRUE) then if (disturb_minor == TRUE) then msgf("The "..name.." rises.") disturb(FALSE) end end else if (visible == TRUE) then -- Let the player know what happened if (disturb_minor == TRUE) then msgf("The "..name.." decays.") disturb(FALSE) end end end else if (visible == TRUE) then -- Let the player know what happened if (disturb_minor == TRUE) then msgf("The "..name.." decays.") disturb(FALSE) end end end -- We must delete ourselves - the is an 'exit' script. deleteme() end -- -- Initialise a field with a counter -- function counter_init(max) local new_value new_value = field.counter + power; -- Bounds checking if new_value > max then field.counter = max elseif new_value < 0 then -- Hack - we'll decrement next turn field.counter = 1 else -- Store in the new value field.counter = new_value end end -- -- Attempt to disarm a trap -- function trap_disarm(level) power = power - level -- Always have a small chance of success if (power < 2) then power = 2 end -- Delete the trap if successful if (randint0(100) < power) then deleteme() gain_exp(level * level) end end -- -- Traps interact with magic -- function trap_gf() local power local j if (type == GF_KILL_TRAP) or (type == GF_KILL_DOOR) then power = field.data[0] j = dam - power -- Always have a small chance of success if j < 2 then j = 2 end if randint0(100) < j then -- Success -- Check for LOS if known == TRUE then notice = TRUE msgf("There is a bright flash of light!") end -- Delete the field deleteme() end end end -- -- Doors interact with magic -- function door_gf() if type == GF_KILL_WALL then -- Delete the field deleteme() elseif type == GF_KILL_DOOR then -- Check for LOS if known == TRUE then msgf("There is a bright flash of light!") notice = TRUE end -- Delete the field deleteme() elseif type == GF_KILL_TRAP then -- Unlock the door if known == TRUE then msgf("Click!") notice = TRUE end -- Delete the field deleteme() end end -- -- Walls interact with magic -- function wall_gf() if type == GF_KILL_WALL then -- Check for LOS if known == TRUE then notice = TRUE end -- Delete the field deleteme() end end zangband/lib/script/makefile.zb0000644000000000000000000000042610250356274015532 0ustar rootrootsubdir = ./lib/script/ ## makefile.zb srcfiles += lib/script/makefile.zb files += lib/script/init.lua lib/script/object.lua lib/script/spell.lua \ lib/script/test.lua lib/script/field.lua dirs := tk dirlist := $(dirlist) $(addprefix $(subdir),$(dirs)) include $(scandir) zangband/lib/user/0000755000000000000000000000000010250356274013070 5ustar rootrootzangband/lib/user/makefile.zb0000644000000000000000000000010710250356274015200 0ustar rootrootsubdir = ./lib/user/ ## makefile.zb srcfiles += lib/user/makefile.zb zangband/lib/xtra/0000755000000000000000000000000010250356274013070 5ustar rootrootzangband/lib/xtra/font/0000755000000000000000000000000010250356274014036 5ustar rootrootzangband/lib/xtra/font/10X20.FON0000644000000000000000000002100010250356274015105 0ustar rootrootMZ} ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NE…ƒ+@H|……†   €P ,€ 0€FONTDIR10X20'FONTRES 100,75,75 : 10x20 (MicroX font)ÿÿÿÿÿÿo2'ƒ2)£2+Ã2-ã2/31#33C35c3ÿ39£3;Ã3=ã3?4A#4CC4ÿo4GóÿÿÿÿÿÏ4ÿÿÿO5Q#5SC5Uc5Wóÿÿÿÿ[Ã5ÿÿÿ_6ÿÿÿcC6ÿÿÿgƒ6i£6kÃ6mã6oóÿq#7sC7ÿo7wƒ7y£7{Ã7}ã78#8ƒC8…c8‡ƒ8‰£8ÿÏ8ÿï89‘#9“C9•óÿÿÿÿ™£9›Ã9ã9Ÿ:¡#:£C:¥c:§ƒ:©£:«Ã:­ã:¯;±#;³C;µc;·ƒ;¹£;»Ã;½ã;¿óÿÁ#<ÃC<Åc<ÿÿÿÉ£<ËÃ<Íóÿÿ=Ñ#=ÓC=Õc=׃=Ù£=ÛÃ=ÿÿÿÿ>ÿ/>ÿO>åóÿÿ>éóÿÿÿÿíóÿÿ?ñ#?óC?õc?÷ƒ?ù£?ûÃ?ýã?ÿ@”Copyright 1989 Network Computing Devices, Inc.KKu 0  Ž10X20EW„EY¤E[ÄE]äE_Fa$FcDFedFg„Fi¤FkÄFmäFoGq$GsDGudGw„Gy¤G{ÄG}äGH$HƒDH…dH‡„H‰¤H‹ÄHôÿI‘$I“ôÿ•dI—„I™¤I›ÄIäIŸôÿ¡ôÿ£DJ¥dJ§„J©ôÿÿÿÿÿÿÿ¯K±$K³DKµdK·„K¹¤Kÿÿÿ½ôÿÿLÁ$LÃDLÅdLÇ„LɤLËÄLÍäLÏMÑ$MÓDMÕdMׄMÙôÿÛôÿÝäMßNá$NãDNådNç„Né¤NëÄNíäNïOñ$OóDOõdO÷„Où¤OûÄOýôÿÿP%PÿOPÿoPÿPÿ¯PÿÏPÿÿÿQ%QõÿeQ…Q¥QÅQåQR!%R#ER%eR'…R)¥R+ÅR-åR/S1%S3ES5õÿ7õÿÿÿÿ;ÅS=åS?TAõÿCETEeTG…TI¥TKÅTMåTÿÿÿÿÿÿÿÿÿÿ¨Copyright 1989 Network Computing Devices, Inc.KKu 0  ¢z z ¢ Ê ò  B j ’ º â  2 Z ‚ ª Ò ú " J r š Â ê  : b Š ² Ú  * R z ¢ Ê ò  B j ’ º â 2 Z ‚ ª Ò ú " J r š  ê  : b Š ² Ú  * R z ¢ Ê ò  B j ’ º â  2 Z ‚ ª Ò ú " J r š Â ê  : b Š ² Ú  * R z ¢ Ê ò  B j ’ º â  2 Z ‚ ª Ò ú " J r š Â ê  : b Š ² Ú  * Rz ?? €€UªUªUªUªUªUª@€@€@€@€@€@€fff~fff€|``x```  €  €333  €€fv~~nf €fff<<€ üü  À À ÿ ÀÿÀÿÀÿÀÿÀÿÀ  À ü ÿÀÿ À 8à8ÿ€€€ppÀÀ?[3€€ 0€€€~|Vs€€À€ 333 ?666€€€À€ ?mlll? m? €€€€€9mm; 663€€€€ÀÀ€666<8lfccv<À€€À  0  03333€  €€ 00``€€ 33aaaaa33 €€€€€ €€€€€€€`````nsaaaasn€€€€1````1€€3aaaa3€€€€€€€€€€€€€3a``1€€€~€€>cccc>`?aaa?€€€€€`````nsaaaaaa€€€€€€ < €aa3€€€€€€€€€€€€`````cflx|ngc€< €[mmmmmm€€€€€€€nsaaaaaa€€€€€€3aaaa3€€€€nsaaaasn````€€€€3aaaa3€€€€€€€€€€€€o9000000€?a`?a?€€€€~€aaaaaa3€€€€€€€€aa33 €€aaammm3€€€€€€€a3 3a€€aaaaaa3a3€€€€€€€€€€ 0`€€ x €€ x  x€9mg€€UªUªUªUªUªUªUªUªUªUª@€@€@€@€@€@€@€@€@€@€@€@€@€@€@€@€@€@€@€@€10X20q$GsDGudGw„Gy¤G{ÄG}äGH$HƒDH…dH‡„H‰¤H‹ÄHôÿI‘$I“ôÿ•dI—„I™¤I›ÄIäIŸôÿ¡ôÿ£DJ¥dJ§„J©ôÿÿÿÿÿÿÿ¯K±$K³DKµdK·„K¹¤Kÿÿÿ½ôÿÿLÁ$LÃDLÅdLÇ„LɤLËÄLÍäLÏMÑ$MÓDMÕdMׄMÙôÿÛôÿÝäMßNá$NãDNådNç„Né¤NëÄNíäNïOñ$OóDOõdO÷„Où¤OûÄOýôÿÿP%PÿOPÿoPÿPÿ¯PÿÏPÿÿÿQ%QõÿeQ…Q¥QÅQåQR!%R#ER%eR'…R)¥R+ÅR-åR/S1%S3ES5õÿ7õÿÿÿÿ;ÅS=åS?TAõÿCETEeTG…TI¥TKÅTMåTÿÿÿÿÿÿÿÿÿÿzangband/lib/xtra/font/16x16.txt0000644000000000000000000011607110250356274015372 0ustar rootroot# This file contains the information for a 16x16 font # This font is used in zangband when the characters need # to be the same size as the graphics. # Other square fonts may need to be created as well. N:0 F:................ F:......*****..... F:.....*******.... F:....**.....**... F:....**.....**... F:....**....**.... F:.........**..... F:........**...... F:........**...... F:................ F:........**...... F:.......***...... F:........**...... F:................ F:................ F:................ N:1 F:................ F:................ F:................ F:.......**....... F:......****...... F:.....******..... F:....********.... F:...**********... F:...**********... F:....********.... F:.....******..... F:......****...... F:.......**....... F:................ F:................ F:................ N:2 F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. F:.*.*.*.*.*.*.*.* F:*.*.*.*.*.*.*.*. # HT N:3 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # FF N:4 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # CR N:5 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # LF N:6 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:7 F:................ F:...***.......... F:..**.**......... F:..*...*......... F:..**.**......... F:...***.......... F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:8 F:................ F:................ F:.......**....... F:.......**....... F:.......**....... F:...**********... F:...**********... F:.......**....... F:.......**....... F:.......**....... F:................ F:...**********... F:...**********... F:................ F:................ F:................ # NL N:9 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # VT N:10 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:11 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:*********....... F:*********....... F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:12 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:*********....... F:*********....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... N:13 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:.......********* F:.......********* F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... N:14 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......********* F:.......********* F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:15 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:**************** F:**************** F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... # Unknown N:16 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # Unknown N:17 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:18 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:**************** F:**************** F:................ F:................ F:................ F:................ F:................ F:................ F:................ # Unknown N:19 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # Unknown N:20 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:21 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......********* F:.......********* F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... N:22 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:*********....... F:*********....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... N:23 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:**************** F:**************** F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:24 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:**************** F:**************** F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... N:25 F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... N:26 F:................ F:................ F:..........***... F:.......****..... F:.....***........ F:...***.......... F:...***.......... F:.....***........ F:.......****..... F:..........***... F:................ F:...**********... F:...**********... F:................ F:................ F:................ N:27 F:................ F:................ F:...***.......... F:.....****....... F:........***..... F:..........***... F:..........***... F:........***..... F:.....****....... F:...***.......... F:................ F:...**********... F:...**********... F:................ F:................ F:................ N:28 F:................ F:................ F:................ F:................ F:................ F:............*... F:...*********.... F:...*********.... F:..*.**...**..... F:....**...**..... F:....**...**..... F:....**...**..... F:....**...**..... F:................ F:................ F:................ N:29 F:................ F:................ F:................ F:..........**.... F:.........**..... F:..************.. F:..************.. F:.......***...... F:......***....... F:..************.. F:..************.. F:.....**......... F:....**.......... F:................ F:................ F:................ N:30 F:................ F:......*****..... F:....****.***.... F:....**.....**... F:....**.....**... F:..*******....... F:..*******....... F:....**.......... F:....**.......... F:..*****......... F:.**.***....**... F:.**.*********... F:..***..****..... F:................ F:................ F:................ N:31 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:......***....... F:......***....... F:......***....... F:................ F:................ F:................ F:................ F:................ F:................ # Space N:32 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:33 F:................ F:.......**....... F:.......**....... F:.......**....... F:......****...... F:......****...... F:......****...... F:......****...... F:.......**....... F:................ F:.......**....... F:......****...... F:.......**....... F:................ F:................ F:................ N:34 F:................ F:.....**..**..... F:.....**..**..... F:.....**..**..... F:.....**..**..... F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:35 F:................ F:................ F:....**....**.... F:....**....**.... F:..************.. F:..************.. F:....**....**.... F:....**....**.... F:....**....**.... F:..************.. F:..************.. F:....**....**.... F:....**....**.... F:................ F:................ F:................ N:36 F:................ F:......**........ F:...********..... F:..***.**.***.... F:..**..**...**... F:..**..**........ F:...*****........ F:.....*****...... F:......**.***.... F:......**...**... F:..*...**...**... F:..***.**.****... F:...*********.... F:......**........ F:................ F:................ N:37 F:................ F:................ F:...***....**.... F:..**.**...**.... F:..*...*..**..... F:..**.**..**..... F:...***..**...... F:.......**....... F:......**..***... F:.....**..**.**.. F:....**...*...*.. F:...**....**.**.. F:...**.....***... F:................ F:................ F:................ N:38 F:................ F:....****........ F:...**..**....... F:...**..**....... F:....*****....... F:....****........ F:....*****...**.. F:...***.***.***.. F:..***...*****... F:..**.....***.... F:..**....*****... F:..****.***.***.. F:....*****...**.. F:................ F:................ F:................ N:39 F:................ F:......***....... F:......***....... F:......***....... F:......**........ F:......**........ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:40 F:................ F:........****.... F:......****...... F:.....***........ F:.....**......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:.....**......... F:.....***........ F:......****...... F:........****.... F:................ F:................ F:................ N:41 F:................ F:....****........ F:......****...... F:........***..... F:.........**..... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:.........**..... F:........***..... F:......****...... F:....****........ F:................ F:................ F:................ N:42 F:................ F:................ F:................ F:................ F:....**...**..... F:....***.***..... F:.....*****...... F:...*********.... F:...*********.... F:.....*****...... F:....***.***..... F:....**...**..... F:................ F:................ F:................ F:................ N:43 F:................ F:................ F:................ F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:...**********... F:...**********... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:................ F:................ F:................ N:44 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:.........**..... F:........****.... F:........****.... F:.........**..... F:........**...... F:................ F:................ F:................ N:45 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:..************.. F:..************.. F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:46 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:......***....... F:......***....... F:......***....... F:................ F:................ F:................ N:47 F:................ F:................ F:..........**.... F:..........**.... F:.........**..... F:........**...... F:........**...... F:.......**....... F:......**........ F:.....**......... F:.....**......... F:....**.......... F:...**........... F:...**........... F:................ F:................ N:48 F:................ F:.......**....... F:.....******..... F:....***..***.... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:....***..***.... F:.....******..... F:.......**....... F:................ F:................ F:................ N:49 F:................ F:.......**....... F:......***....... F:....*****....... F:....**.**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:....********.... F:....********.... F:................ F:................ F:................ N:50 F:................ F:......****...... F:.....******..... F:...***...**..... F:...**....**..... F:.........**..... F:........**...... F:......***....... F:.....**......... F:....**.......... F:...**........... F:...*********.... F:...*********.... F:................ F:................ F:................ N:51 F:................ F:.....*****...... F:...********..... F:...**....**..... F:..........**.... F:.....*******.... F:.....******..... F:.........**..... F:..........**.... F:...**......**... F:...**.....***... F:....********.... F:....*******..... F:................ F:................ F:................ N:52 F:................ F:........**...... F:.......***...... F:......****...... F:.....**.**...... F:....**..**...... F:...**...**...... F:..**********.... F:..**********.... F:........**...... F:........**...... F:........**...... F:........**...... F:................ F:................ F:................ N:53 F:................ F:...*********.... F:...*********.... F:...**........... F:...**........... F:...********..... F:...*********.... F:..........**.... F:..........**.... F:..........**.... F:...**.....**.... F:....********.... F:....*******..... F:................ F:................ F:................ N:54 F:................ F:......*****..... F:.....********... F:....**.....**... F:...**........... F:...**........... F:...**.*****..... F:...*********.... F:...***....***... F:...**......**... F:...***....***... F:....********.... F:.....******..... F:................ F:................ F:................ N:55 F:................ F:...*********.... F:...*********.... F:...*......**.... F:.........**..... F:........**...... F:........**...... F:.......**....... F:......**........ F:.....**......... F:.....**......... F:....**.......... F:...**........... F:................ F:................ F:................ N:56 F:................ F:.....******..... F:....********.... F:....**....**.... F:....**....**.... F:.....******..... F:.....******..... F:....**....**.... F:...**......**... F:...**......**... F:...***....***... F:....********.... F:.....******..... F:................ F:................ F:................ N:57 F:................ F:.....******..... F:....********.... F:...**.....***... F:...**......**... F:...***....***... F:....*********... F:.....*****.**... F:...........**... F:...*.......**... F:...**.....**.... F:....*******..... F:......****...... F:................ F:................ F:................ N:58 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:......***....... F:......***....... F:................ F:................ F:......***....... F:......***....... F:................ F:................ F:................ N:59 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:......***....... F:......***....... F:................ F:................ F:......***....... F:.......**....... F:......**........ F:......*......... F:................ N:60 F:................ F:..........**.... F:.........**..... F:........**...... F:.......**....... F:......**........ F:.....**......... F:.....**......... F:......**........ F:.......**....... F:........**...... F:.........**..... F:..........**.... F:................ F:................ F:................ N:61 F:................ F:................ F:................ F:................ F:................ F:..************.. F:..************.. F:................ F:................ F:..************.. F:..************.. F:................ F:................ F:................ F:................ F:................ N:62 F:................ F:....**.......... F:.....**......... F:......**........ F:.......**....... F:........**...... F:.........**..... F:.........**..... F:........**...... F:.......**....... F:......**........ F:.....**......... F:....**.......... F:................ F:................ F:................ N:63 F:................ F:......*****..... F:.....*******.... F:....**.....**... F:....**.....**... F:....**....**.... F:.........**..... F:........**...... F:........**...... F:................ F:........**...... F:.......***...... F:........**...... F:................ F:................ F:................ N:64 F:................ F:.......****..... F:.....********... F:....**.....***.. F:...**...**..**.. F:..**...****.**.. F:..**..**.**.**.. F:..**..*****.**.. F:..**...******... F:...**........... F:....**......**.. F:.....********... F:.......****..... F:................ F:................ F:................ N:65 F:................ F:.......**....... F:.......**....... F:......****...... F:.....**..**..... F:.....**..**..... F:....**....**.... F:....********.... F:...**********... F:...**......**... F:...**......**... F:..**........**.. F:..**........**.. F:................ F:................ F:................ N:66 F:................ F:....******...... F:....*******..... F:....**....**.... F:....**....**.... F:....**....**.... F:....*******..... F:....********.... F:....**.....**... F:....**.....**... F:....**.....**... F:....********.... F:....*******..... F:................ F:................ F:................ N:67 F:................ F:......****...... F:....********.... F:...***....**.... F:...**........... F:..**............ F:..**............ F:..**............ F:..**............ F:...**........... F:...***....**.... F:....********.... F:......****...... F:................ F:................ F:................ N:68 F:................ F:....*****....... F:....********.... F:....**.....**... F:....**.....**... F:....**......**.. F:....**......**.. F:....**......**.. F:....**......**.. F:....**.....**... F:....**.....**... F:....********.... F:....******...... F:................ F:................ F:................ N:69 F:................ F:....*********... F:....*********... F:....**.......... F:....**.......... F:....**.......... F:....******...... F:....******...... F:....**.......... F:....**.......... F:....**.......... F:....*********... F:....*********... F:................ F:................ F:................ N:70 F:................ F:....*********... F:....*********... F:....**.......... F:....**.......... F:....**.......... F:....******...... F:....******...... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:................ F:................ F:................ N:71 F:................ F:......*****..... F:....*********... F:...***....***... F:...**........... F:..**............ F:..**............ F:..**....*****... F:..**....*****... F:...**......**... F:...***.....**... F:....*********... F:......*******... F:................ F:................ F:................ N:72 F:................ F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**********... F:...**********... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:................ F:................ F:................ N:73 F:................ F:.....******..... F:.....******..... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.....******..... F:.....******..... F:................ F:................ F:................ N:74 F:................ F:...........**... F:...........**... F:...........**... F:...........**... F:...........**... F:...........**... F:...........**... F:...........**... F:...**......**... F:...***....***... F:....********.... F:.....*****...... F:................ F:................ F:................ N:75 F:................ F:...**......**... F:...**.....***... F:...**....**..... F:...**...**...... F:...**.***....... F:...*****........ F:...*****........ F:...**.***....... F:...**...**...... F:...**....**..... F:...**.....***... F:...**......**... F:................ F:................ F:................ N:76 F:................ F:...**........... F:...**........... F:...**........... F:...**........... F:...**........... F:...**........... F:...**........... F:...**........... F:...**........... F:...**........... F:...**********... F:...**********... F:................ F:................ F:................ N:77 F:................ F:..**........**.. F:..**........**.. F:..***......***.. F:..***......***.. F:..****....****.. F:..****....****.. F:..**.**..**.**.. F:..**.**..**.**.. F:..**..****..**.. F:..**..****..**.. F:..**...**...**.. F:..**...**...**.. F:................ F:................ F:................ N:78 F:................ F:...**......**... F:...***.....**... F:...***.....**... F:...****....**... F:...**.**...**... F:...**..**..**... F:...**..**..**... F:...**...**.**... F:...**....****... F:...**.....***... F:...**.....***... F:...**......**... F:................ F:................ F:................ N:79 F:................ F:.....******..... F:....********.... F:...**......**... F:..**........**.. F:..**........**.. F:..**........**.. F:..**........**.. F:..**........**.. F:..**........**.. F:...**......**... F:....********.... F:.....******..... F:................ F:................ F:................ N:80 F:................ F:....********.... F:....*********... F:....**......**.. F:....**......**.. F:....**.....**... F:....********.... F:....*******..... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:................ F:................ F:................ N:81 F:................ F:.....******..... F:....********.... F:...**......**... F:..**........**.. F:..**........**.. F:..**........**.. F:..**........**.. F:..**....**..**.. F:..**.....**.**.. F:...**.....***... F:....*********... F:.....******.**.. F:................ F:................ F:................ N:82 F:................ F:....********.... F:....*********... F:....**......**.. F:....**......**.. F:....**.....**... F:....********.... F:....*******..... F:....**....**.... F:....**....**.... F:....**.....**... F:....**.....**... F:....**......**.. F:................ F:................ F:................ N:83 F:................ F:....******...... F:...********..... F:..***....***.... F:..**......**.... F:..***........... F:...******....... F:......*****..... F:..........**.... F:...........**... F:..**......***... F:..**********.... F:...********..... F:................ F:................ F:................ N:84 F:................ F:...**********... F:...**********... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:................ F:................ F:................ N:85 F:................ F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:...***....***... F:....********.... F:......****...... F:................ F:................ F:................ N:86 F:................ F:...**......**... F:...**......**... F:...**......**... F:....**....**.... F:....**....**.... F:....**....**.... F:.....**..**..... F:.....**..**..... F:......****...... F:......****...... F:.......**....... F:.......**....... F:................ F:................ F:................ N:87 F:................ F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:...**.****.**... F:...**.****.**... F:...**.****.**... F:...****..****... F:....***..***.... F:....***..***.... F:....**....**.... F:....**....**.... F:................ F:................ F:................ N:88 F:................ F:...**......**... F:...**......**... F:....**....**.... F:.....**..**..... F:......****...... F:.......**....... F:.......**....... F:......****...... F:.....**..**..... F:....**....**.... F:...**......**... F:...**......**... F:................ F:................ F:................ N:89 F:................ F:...**......**... F:...**......**... F:....**....**.... F:....**....**.... F:.....**..**..... F:......****...... F:......****...... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:................ F:................ F:................ N:90 F:................ F:...**********... F:...**********... F:..........**.... F:.........**..... F:........**...... F:.......**....... F:.......**....... F:......**........ F:.....**......... F:....**.......... F:...**********... F:...**********... F:................ F:................ F:................ N:91 F:................ F:....********.... F:....********.... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....********.... F:....********.... F:................ F:................ F:................ N:92 F:................ F:................ F:...**........... F:...**........... F:....**.......... F:.....**......... F:.....**......... F:......**........ F:.......**....... F:........**...... F:........**...... F:.........**..... F:..........**.... F:..........**.... F:................ F:................ N:93 F:................ F:....********.... F:....********.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:....********.... F:....********.... F:................ F:................ F:................ N:94 F:................ F:.......**....... F:......****...... F:.....**..**..... F:....**....**.... F:...**......**... F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:95 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:**************** F:**************** F:................ F:................ F:................ N:96 F:................ F:...****......... F:...****......... F:.....***........ F:.......**....... F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ N:97 F:................ F:................ F:................ F:................ F:................ F:......***.**.... F:.....*******.... F:....***...**.... F:....**....**.... F:....**....**.... F:....**....**.... F:.....*******.... F:......******.... F:................ F:................ F:................ N:98 F:................ F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.***...... F:....*******..... F:....***...**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....********.... F:....*******..... F:................ F:................ F:................ N:99 F:................ F:................ F:................ F:................ F:................ F:......*****..... F:.....*******.... F:....***......... F:....**.......... F:....**.......... F:....***......... F:.....*******.... F:......*****..... F:................ F:................ F:................ N:100 F:................ F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:......***.**.... F:....********.... F:....***...**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....********.... F:.....*******.... F:................ F:................ F:................ N:101 F:................ F:................ F:................ F:................ F:................ F:.....*****...... F:....********.... F:...***....**.... F:...*********.... F:...*********.... F:...**........... F:....********.... F:.....******..... F:................ F:................ F:................ N:102 F:................ F:......****...... F:.....******..... F:....***..**..... F:....**.......... F:....**.......... F:...******....... F:...******....... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:................ F:................ F:................ N:103 F:................ F:................ F:................ F:................ F:................ F:......******.... F:.....*******.... F:....***...**.... F:....**....**.... F:....**....**.... F:....***...**.... F:.....*******.... F:......******.... F:....*.....**.... F:....********.... F:.....******..... N:104 F:................ F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....******...... F:....*******..... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:................ F:................ F:................ N:105 F:................ F:................ F:................ F:.......**....... F:.......**....... F:................ F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:................ F:................ F:................ N:106 F:................ F:................ F:................ F:..........**.... F:..........**.... F:................ F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:..........**.... F:....**....**.... F:....**....**.... F:....*******..... F:.....*****...... N:107 F:................ F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**....**.... F:....**..***..... F:....*****....... F:....*****....... F:....**.**....... F:....**..***..... F:....**....**.... F:................ F:................ F:................ N:108 F:................ F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:................ F:................ F:................ N:109 F:................ F:................ F:................ F:................ F:................ F:..*****..***.... F:..***********... F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:................ F:................ F:................ N:110 F:................ F:................ F:................ F:................ F:................ F:....******...... F:....*******..... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:................ F:................ F:................ N:111 F:................ F:................ F:................ F:................ F:................ F:.....******..... F:....********.... F:...**......**... F:...**......**... F:...**......**... F:...**......**... F:....********.... F:.....******..... F:................ F:................ F:................ N:112 F:................ F:................ F:................ F:................ F:................ F:...*******...... F:...********..... F:...**.....**.... F:...**.....**.... F:...**.....**.... F:...**.....**.... F:...********..... F:...*******...... F:...**........... F:...**........... F:...**........... N:113 F:................ F:................ F:................ F:................ F:................ F:.....*******.... F:....********.... F:...**.....**.... F:...**.....**.... F:...**.....**.... F:...**.....**.... F:....********.... F:.....*******.... F:..........**.... F:..........**.... F:..........**.... N:114 F:................ F:................ F:................ F:................ F:................ F:....**.****..... F:....*******..... F:....***......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:....**.......... F:................ F:................ F:................ N:115 F:................ F:................ F:................ F:................ F:................ F:.....******..... F:....********.... F:....**.......... F:....*******..... F:.....*******.... F:..........**.... F:....********.... F:.....******..... F:................ F:................ F:................ N:116 F:................ F:.......**....... F:.......**....... F:.......**....... F:....********.... F:....********.... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:.......**....... F:................ F:................ F:................ N:117 F:................ F:................ F:................ F:................ F:................ F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:.....*******.... F:......******.... F:................ F:................ F:................ N:118 F:................ F:................ F:................ F:................ F:................ F:....**....**.... F:....**....**.... F:....***..***.... F:.....**..**..... F:.....**..**..... F:......****...... F:......****...... F:.......**....... F:................ F:................ F:................ N:119 F:................ F:................ F:................ F:................ F:................ F:..**...**...**.. F:..**...**...**.. F:..**...**...**.. F:...**..**..**... F:...**.****.**... F:....***..***.... F:....***..***.... F:....**....**.... F:................ F:................ F:................ N:120 F:................ F:................ F:................ F:................ F:................ F:....**....**.... F:....**....**.... F:.....**..**..... F:......****...... F:......****...... F:.....**..**..... F:....**....**.... F:....**....**.... F:................ F:................ F:................ N:121 F:................ F:................ F:................ F:................ F:................ F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:....**....**.... F:.....*******.... F:......******.... F:..........**.... F:....********.... F:.....******..... N:122 F:................ F:................ F:................ F:................ F:................ F:....********.... F:....********.... F:.........**..... F:.......***...... F:......***....... F:.....**......... F:....********.... F:....********.... F:................ F:................ F:................ N:123 F:................ F:........****.... F:......****...... F:......**........ F:......**........ F:.....***........ F:...***.......... F:...***.......... F:.....***........ F:......**........ F:......**........ F:......****...... F:........****.... F:................ F:................ F:................ N:124 F:................ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:......**........ F:................ F:................ F:................ N:125 F:................ F:....****........ F:......****...... F:........**...... F:........**...... F:........***..... F:..........***... F:..........***... F:........***..... F:........**...... F:........**...... F:......****...... F:....****........ F:................ F:................ F:................ N:126 F:................ F:....***.....**.. F:...******..***.. F:..***..******... F:..**.....***.... F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ # Unknown N:127 F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ F:................ zangband/lib/xtra/font/6X10.FON0000644000000000000000000001100010250356274015030 0ustar rootrootMZ{ ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NE„ƒ*@H|„„…   €P ,€0€FONTDIR6X10&FONTRES 100,75,75 : 6x10 (MicroX font)LYX›•ANGBAND EXE McXô åNGBAND EXE :cXR¢º ‰Public domain terminal emulator font. Share and enjoy. KK“ 0 „6X10AŽBŽCŽDŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿ‰Public domain terminal emulator font. Share and enjoy. KK“ 0 „zz„Ž˜¢¬¶ÀÊÔÞèòü$.8BLV`jt~ˆ’œ¦°ºÄÎØâìö (2<FPZdnx‚Œ– ª´¾ÈÒÜæðú",6@JT^hr|†š¤®¸ÂÌÖàêôþ&0:DNXblv€Š”ž¨²¼ÆÐÚäîø  *4>HR\fpz88|88T(T(T(THHxHH<p@`@\8@@88$8$$@@@x< 8 8((8||HhhXH <HH0 <pp||||||p|| @ |@  @||((((|| @$ x 8l (((((|(|((8P88$T((TH PP TH4    D(|(D| |8 @@(DDD(0P|8D @||D8(H||@XdD8 @XdD8| 8DD8DD88DL40888  ||@  @8D8DLTX@8(DD|DDx$$8$$x8D@@@D8x$$$$$x|@@x@@||@@x@@@8D@@LD8DDD|DDD88D8DHP`PHD@@@@@@|DDlTDDDDDdTLDD8DDDDD8xDDx@@@8DDDDT8xDDxPHD8D@8D8|DDDDDD8DDD(((DDDTTlDDD((DDDD(| @|8 8@@ 88(D|08>00000000000>>000000þþ00000þþþþþþþþþþ000000>>00000000000ðð00000000000þþþþ000000000000000000 `À` üüÀ` `ÀüüülllìÌ üü0üüÀ€8l``ð``l¸0000000000lll((||(||((0x´°x4´x0ä¬è0`\ÔœpØØØpÔÜØt880`00```00`0000`H0üü0H00üü00880`üü0x0 0``ÀÀ0HÌÌÌÌÌH00p°00000üxÌÌ 8`ÀÀüxÌ 8 Ìx ÌÌÌxx0~~øøøøÿÿÿÿÿÿÿÿÿÿÿÿøøÿÿÿÿ 0`À`0 üü`0  0`~~þþllìÌ þþ8þþ`À<~bðð```ò¾î000000000llllllþþlþþll0|ü°ø|4üø0BæL0 d΄0xÌÌx2vÜüv0` 00```00 `0 0`Æl8þþ8lÆ00üü008880`üü888 0`À€€8|ÆÆÆÆÆÆ|88x~~|þ†|àÀþþüþ<<þü 000>000000000x|   |xfþØUªUªUªUªUªUªU8X13BŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿzangband/lib/xtra/font/9X15B.FON0000644000000000000000000001600010250356274015147 0ustar rootrootMZ‚ ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NE…ƒ0@H|……†   €P ,€ 0€FONTDIR9X15B,FONTRES 100,75,75 : 9x15b Bold (MicroX font)LYX›•ANGBAND EXE McXô åNGBAND EXE :cXR¢º Public domain font. Share and enjoy.KK ’ 0  ‰9X15BAŽBŽCŽDŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿžPublic domain font. Share and enjoy.KK ’ 0  ˜z z ˜ ¶ Ô ò  . L j ˆ ¦ Ä â   < Z x – ´ Ò ð  , J h † ¤  à þ  : X v ” ² Ð î  * H f „ ¢ À Þ ü  8 V t ’ ° Î ì ( F d ‚   ¾ Ü ú  6 T r ® Ì ê  & D b € ž ¼ Ú ø  4 R p Ž ¬ Ê è  $ B ` ~ œ º Ø ö  2 P n Œ ª È æ  " @ ^ | š ¸ Ö ô  0 N l Š ¨ Æ ä   > \z ?? €m3m3m3m€€€€ff~ff€|`x``  €>b`b>€€€````|  €33  €€€€fv~nf €fff<€ üüüü  €€ €€ ÿÿ €€ÿÿ€€ÿÿ€€ÿÿ€€ÿÿ€€ÿÿ€€  €€ üü ÿÿ€€ÿÿ €€ 8`8€€€€pp€€€ss€€ 0`€€€€€?|_p€€ 66666654<5€€€€1{{6 77c€€€w>k  €0€   00`€3aaaaaa3€€€€€€ < ??aa0`€€€€a?€€€€€€3c€``qa?€€€€€€0``qaaa?€€€€ 00€€€?aaa?aaaa?€€€€€€€?aaac?>€€€€€€€    0 00 €€0  0?aa €€€?aaoy{o``?€€€€€€ 3aaaaaa€€€€€€€111?1111€€€€€€€?a``````a?€€11111111€€€€€€€€000>0000€€000>00000€?a```gaaa?€€€€€aaaaaaaaa€€€€€€€€€€? ?c>€acflxxlfca€€`````````€aasmmaaa€€€€€€€€€€aaqymgcaaa€€€€€€€€€€?aaaaaaaa?€€€€€€€€aaa`````€€€?aaaaaamg?€€€€€€€€€aaalfcaa€€€€€?a``?a?€€€€€ €aaaaaaaaa?€€€€€€€€€aaa333 €€€aaaammmm3€€€€€€€€€aa3 3aa€€€€aa3 €€ 0``€€€`00 €>> 3a€ÿ€<??ac?€€€€€€```qaaaq€€€€€?a```a?€€?caaac?€€€€€€€€€€?aa``?€€€~€€?ccc>`?aa?€€€```qaaaaa€€€€€€  ?ccc>```ag|p|ga€€ ?mmmmma€€€€€€qaaaaa€€€€€€?aaaaa?€€€€€qaaaq```€€€€€?caaac?€€€€€€€€€€o910000€€?a`?a?€€€€cccccc?€aa33 €€aammm3€€€€€€a3 3a€€cccccg?c> 0€€  €€ x  x9mg€€UªUªUªUªUªUªUªU€€€€€€€€€€€€€€9X15BЎюҎӎԎՎ֎׎؎َڎێ܎ݎގߎàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿzangband/lib/xtra/font/12X24.FON0000644000000000000000000002300010250356274015115 0ustar rootrootMZ} ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NE…ƒ+@H|……†   €P ,€0€FONTDIR12X24'FONTRES 100,75,75 : 12x24 (MicroX font)i$j$k$l$m$n$o$p$q$r$s$t$ÿÿÿÿÿÿÿÿy$z${$|$}$~$$€$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›$œ$$ž$Ÿ$ÿÿ¡$¢$£$¤$¥$¦$§$ÿÿ´$µ$¶$ÿÿÿÿÃ$Ä$Å$Æ$Ç$È$ÿÿÎ$Ï$Ð$Ñ$Ò$Ó$Ô$Õ$Ö$×$Ø$Ù$Ú$Û$Ü$Ý$Þ$ß$à$á$â$ã$ä$å$æ$ç$è$é$ÿÿÿÿÿÿÿÿõ$ö$÷$ÿÿ˜Copyright (c) 1987, 1988 Sony Corp.dd” 0  ’12X24A&ÿÿÿÿD&ÿÿÿÿG&H&ÿÿÿÿÿÿL&M&N&ÿÿP&ÿÿÿÿÿÿT&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]&^&_&ÿÿa&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&ÿÿs&t&u&v&w&x&y&z&{&|&}&~&&€&&‚&ƒ&„&…&†&‡&ˆ&ÿÿÿÿŒ&&Ž&&ÿÿÿÿÿÿ•&–&—&˜&™&š&›&œ&&ž&Ÿ& &¡&¢&ÿÿÿÿÿÿÿÿÿÿÿÿ¯&°&±&²&³&´&µ&¶&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊ&Ë&Ì&Í&Î&Ï&Ð&ÿÿÒ&Ó&Ô&ÿÿÖ&×&Ø&Ù&Ú&Û&Ü&Ý&Þ&ÿÿÿÿç&è&ÿÿÿÿë&ì&í&î&ï&ð&ñ&ò&ó&ô&õ&ö&÷&ø&ù&ú&û&ü&ý&þ&ÿ&°Copyright (c) 1987, 1988 Sony Corp.dd” 0  »ªz z ª Ú  : j š Ê ú * Z Š º ê  J z ª Ú  : j š Ê ú * Z Š º ê  J z ª Ú : j š Ê ú * Z Š º ê  J z ª Ú : j š Ê ú * Z Š º ê  J z ª Ú  : j š Ê ú * Z Š º ê  J z ª Ú  : j š Ê ú * Z Š º ê  J z ª Ú  : j š Ê ú * Z Š º ê  J z ª Ú  : j š Ê ú * Z Š º ê  Jz??ÿ??€€ÀÀàÀÀ€€ÆÆÆ000ÆÆÆ000ÆÆÆ000ÆÆÆ000ÀÀÀ000ÀÀÀ000ÀÀÀ000ÆÆÆÆþÆÆÆÆ?ÀþÀÀÀüÀÀÀÀÀ€c?ø€€ÀÀÀ€€Àà````À€ !!AAÿ€€€€€€€€€€€€€€à€€€€à@@@@@_q`@|àÀ€À``````ÀÀ€ 0``ÀÀÏðÀÀÀÀÀÀ``1€À``````ÀÀ€ÿÿÀ€€ àà @€€1 ````09#@ÀÀÀÀ@`€€ÀÀÀÀ€ÀÀ````@À1``ÀÀÀÀÀ@a8€ÀÀ``````à```ÀÀ€ 0``0  `À€€À` ÿÿÿÿàààà@`0  0`@€À``À€0``p0À````àÀ€ `@À—³³³³³³·À@` €À@` `À À  ? @@@@à€€€€ÀÀÀÀ````ðþa``````a~a``````aþ€ÀÀÀÀÀ€€À````À€00```````````00 à` Àüca`````````````acü€ÀÀ`````````ÀÀ€ÿ`````aaccaa`````ÿÀÀ@ @ÀÀÿ`````aaccaa`````ðÀÀ@ 1``ÀÀÀÀÀÀÃÀÀÀÀ``1@ÀÀ@àÀÀÀÀÀÀÀ@ñ````````````````ñàÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀà? ?aáÁÁcà€€€€€€€€€€€€€€€€ñ```abbdh||ffccaa`ñà@€€€€Ààð`````````````````ÿ @ÀÀà``ppqYY[NNNDD@@@@àp`ààà`````````````ðà`pppXXLLFFCCCAA@@àà@@@@@@@@@@@@@ÀÀÀÀ@`@ÀÀÀÀÀÀÀÀÀÀÀÀÀ@`À@`````````````@Àþa```````a~```````ð€À`````À€a@ÀÀÀÀÀÀÀÀÀÀÀÌÒbc€€ÀÀÀÀÀÀÀÀÀÀÀÀÀ€€ àÀþa``````a~baaaaaaað€€ÀÀÀÀ€€€€€€  ÀaÀÀÀÀàp>€Àáž@ÀÀ@€Àà````À€ÿÌŒŒŒ ÀÀ@@@ð````````````````0à@@@@@@@@@@@@@@@@€ð````0000 à@@@@€€€€àÀÀÀÀÀÆÆÆÆÆÏËkkqq11` @@@À€€ð``00  !!@@áà@@€€€€ÀÀàð``000à@@€€€€@@ 00``ÿàÀÀ€€ @ÀÀ À€€À€À@` 0 €€À@` `xx`;`À€À`ÿÿàà<€sAaÁÁÁÁc<€€€€€€€€  À`à````nq`````````qN€ÀÀ`````ÀÀ€1``ÀÀÀÀÀ``1€ÀÀ@€1``ÀÀÀÀÀ``1ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀà1``ÀÿÀÀÀ``0€Àà`à À ÿ ?À``À1```10'@ÀÀ``à€ÀÀÀ€Àà``À`à````oq``````````ñ€ÀÀÀÀÀÀÀÀÀÀà< ?ÃÆ|`à````a`abdhxlfca`ñà€€Àà < ?möffffffffffïÀ```````````pfù``````````ñ€ÀÀÀÀÀÀÀÀÀÀà1`@ÀÀÀÀÀ@`1€À@`````@À€nñ```````qn````ð€À`````À€1`ÀÀÀÀÀ`1@ÀÀÀÀÀÀÀÀÀÀÀÀÀÀà1ó6800000000üÀà`aÀÀà|€ÀàŸ@ÀÀ@€Àà``Àÿ € @€`á`````````3ÀÀÀÀÀÀÀÀÀÀÀÀàð``000à@@€€€àÀÀÆÆÆÎËËQq` ` `ÀÀ€ð`0!@áà@€€Ààð``00 ÈÈpà@@€€€`A 00`ÿàÀ€€ @@À@€€€€€€€€€€€€€€€€€@@ 0000000000000 @8|ǃ `À€UªUªUªUªUªUªUªUªUªUªUªUªP P P P P P P P P P P P P P P P P P P P P P P P 12X24oÓÄ/µ™ˆ '‘)€*ÓgnB<*Ó)oÉ*½*Ǭ*Ó *Ç-®Š-ûvk_T*@/5*$/-®-ûkì*Û/Ð*¿/« -Ïi:tJ;¿0)8³ý³ì³â³Ø³zangband/lib/xtra/font/5X8.FON0000644000000000000000000001100010250356274014756 0ustar rootrootMZy ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NEƒƒ)@H|ƒƒ„   €P ,€0€FONTDIR5X8%FONTRES 100,75,75 : 5x8 (MicroX font)LYX›•ANGBAND EXE McXô åNGBAND EXE :cXR¢º †Copyright 1989 by Cognition Corp.KK² ‚5x8AŽBŽCŽDŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿ†Copyright 1989 by Cognition Corp.KK² ‚zz‚Š’š¢ª²ºÂÊÒÚâêòú "*2:BJRZbjrz‚Š’š¢ª²ºÂÊÒÚâêòú "*2:BJRZbjrz‚Š’š¢ª²ºÂÊÒÚâêòú "*2:BJRZbjrz‚Š’š¢ª²ºÂÊÒÚâêòú "*2:BJRZbjrz pøp (P(P(P(PPpPP8à€À¸ 0 `€`0(0(€€€à8 0 pPPp p pа 8   @8 àà 8 8 ø xxxxx 8 à øø  @ p@  @pøPPPPø ø@0Hà@pØ@ PPPPPøPøPP p p(p @P P PP PP(0 @ @ @H0x0H ø 0 @x p  @@ PPPP ` p0H0@x0HH00Pxx@pH00@PhH0x 0H0HH00HX(00000000 @ @@ pp@  @ P 0H˜¨¨@00HHxHHpHpHHp0H@@H0pHHHHpx@p@@xx@p@@@0H@XH0HHxHHHp p8H0HP`PPH@@@@@pHxxHHHHhxXXH0HHHH0pHHp@@0HHhX0pHHpXH0H H0ø HHHHH0HHHH00HHHxxHHH00HHˆˆP x @xp@@@@p@@ pp PPø`@ (XX(@@pHHp0@@0(XX(0x@0( p 0H80@@pHHH ` pP @@HpHH` pP¨¨ˆpHHH0HH0pHp@@8H8Ph@@p`p p (HHH8PPP ˆ¨¨pH00HHH8H0x x `  `  `(PP¨P¨P¨P¨5x8DŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿzangband/lib/xtra/font/6X12.FON0000644000000000000000000001200010250356274015033 0ustar rootrootMZ{ ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NE„ƒ*@H|„„…   €P ,€0€FONTDIR6X12&FONTRES 100,75,75 : 6x12 (MicroX font)LYX›•ANGBAND EXE McXô åNGBAND EXE :cXR¢º {Public domain terminal emulator font. Share and enjoy. KK¨ 0€v6X12AŽBŽCŽDŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿ{Public domain terminal emulator font. Share and enjoy. KK¨ 0€vvv‚Žš¦²¾ÊÖâîú*6BNZfr~Š–¢®ºÆÒÞêö&2>JVbnz†’žª¶ÂÎÚæòþ ".:FR^jv‚Žš¦²¾ÊÖâîú*6BNZfr~Š–¢®ºÆÒÞêö&2>JVbnz†’žª¶ÂÎÚæòþ ".:FR^jøøpxˆxøø€€ðˆˆˆðøøpˆ€€xøøxˆˆˆxøøpˆð€pøø0H@à@@@øøpˆˆˆxpøø€€ðˆˆˆˆøø øøˆpøø€€ˆàˆøø øøÐ¨¨¨¨øø°Èˆˆˆøøpˆˆˆpøøðˆˆˆð€€øøpˆˆˆxøø°È€€€øøx€pðøø ø øøˆˆˆˆpøøˆˆˆP øøˆˆ¨¨PøøˆP PˆøøˆˆˆP @øøøp@ø PˆP øø€@ øøà àøø Pˆ PPPPøPPøP p¨ p(¨p øÈ @˜˜@  @¨h``À €@ @€ ¨p p¨ ø ``Àø`` @€pˆ˜¨Èˆp ` ppˆ @øpˆ0ˆp0Pøø€ðˆp0@€ðˆˆpø pˆˆpˆˆppˆˆx`````````À @ øø@  @pˆ pˆ¸¨¸€ppˆˆøˆˆˆðˆˆðˆˆðpˆ€€€ˆpðHHHHHðø€€ð€€øø€€ð€€€pˆ€€˜ˆpˆˆˆøˆˆˆp pˆpˆ À ˆ€€€€€€øˆØ¨ˆˆˆˆˆˆÈ¨˜ˆˆpˆˆˆˆˆpðˆˆð€€€pˆˆˆ¨hðˆˆð ˆpˆ€pˆpø ˆˆˆˆˆˆpˆˆˆˆPP ˆˆˆˆ¨ØˆˆˆP PˆˆˆˆP øø@€ø8 8€@ à à Pˆø00pxˆx€€ðˆˆˆðpˆ€€xxˆˆˆxpˆð€p0H@à@@@pˆˆˆxp€€ðˆˆˆˆ ˆp€€ˆàˆ Ш¨¨¨°Èˆˆˆpˆˆˆpðˆˆˆð€€pˆˆˆx°È€€€x€pð ø ˆˆˆˆpˆˆˆP ˆˆ¨¨PˆP PˆˆˆˆP @€øp@ø  €@@@ @@@€h°T¨T¨T¨T¨T¨T¨6X12AŽBŽCŽDŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿzangband/lib/xtra/font/6X13B.FON0000644000000000000000000001200010250356274015136 0ustar rootrootMZ‚ ÿÿe@@èSThis program requires Microsoft Windows. $ Z´ Í!¸LÍ!NE…ƒ0@H|……†   €P ,€0€FONTDIR6X13B,FONTRES 100,75,75 : 6x13b Bold (MicroX font)LYX›•ANGBAND EXE McXô åNGBAND EXE :cXR¢º  Public domain font. Share and enjoy. KK À 0  6X13BAŽBŽCŽDŽEŽFŽGŽHŽIŽJŽKŽLŽMŽNŽOŽPŽQŽRŽSŽTŽUŽVŽWŽXŽYŽZŽ[Ž\Ž]Ž^Ž_Ž`ŽaŽbŽcŽdŽeŽfŽgŽhŽiŽjŽkŽlŽmŽnŽoŽpŽqŽrŽsŽtŽuŽvŽwŽxŽyŽzŽ{Ž|Ž}Ž~ŽŽ€ŽŽ‚ŽƒŽ„Ž…Ž†Ž‡ŽˆŽ‰ŽŠŽ‹ŽŒŽŽŽŽŽŽ‘Ž’Ž“Ž”Ž•Ž–Ž—Ž˜Ž™ŽšŽ›ŽœŽŽžŽŸŽ Ž¡Ž¢Ž£Ž¤Ž¥Ž¦Ž§Ž¨Ž©ŽªŽ«Ž¬Ž­Ž®Ž¯Ž°Ž±Ž²Ž³Ž´ŽµŽ¶Ž·Ž¸Ž¹ŽºŽ»Ž¼Ž½Ž¾Ž¿ŽÀŽÁŽÂŽÃŽÄŽÅŽÆŽÇŽÈŽÉŽÊŽËŽÌŽÍŽÎŽÏŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿ Public domain font. Share and enjoy. KK À 0  zz‡”¡®»ÈÕâïü #0=JWdq~‹˜¥²¿ÌÙæó '4AN[hu‚œ©¶ÃÐÝê÷+8ER_ly†“ ­ºÇÔáîû"/<IVcp}Š—¤±¾ËØåòÿ &3@MZgtŽ›¨µÂÏÜéö*7DQ^kx…’Ÿ¬¹ÆÓàíú0xüx0üxüxüxüØØøØØ<ðÀàÀÀ<0800xÈÀÈxxlxhlÀÀÀÀð<0800pØØp00ü00üØøøØØ0000<ØØØp <000000ðððð00000<<00000000000<<000000üü00000üüüüüüüüüü000000<<00000000000ðð00000000000üüüü000000000000000000 0`0 üÀ`00`ÀüüxxxØ ü0üÀ8l`ø`ðxÀ000000000xxxxxüxüxx0xðxÿ/>ÿO>åc>ÿ>é£>ÿÿÿíóÿÿ?ñ#?óC?õc?÷ƒ?ù£?ûÃ?ýã?ÿ@ Public domain font. Share and enjoy. KK g 0  7X13EW„EY¤E[ÄE]äE_Fa$FcDFÿÿÿÿÿÿiôÿÿÏFmäFoGq$GsDGudGw„GÿÿÿÿÿÿÿïGH$HƒDHÿÿÿÿÿÿ‰¤HÿÿÿôÿIÿ/I“Ô€•dI—„I™¤I›ÄIäIŸôÿ¡ôÿ£DJ¥dJ§„J©ôÿÿÿÿÿÿÿ¯K±$K³DKµdK·„K¹¤Kÿÿÿ½äKÿLÁ$LÃDLÅdLÇ„LɤLËÄLÍäLÏMÑ$MÓDMÕdMׄMÙôÿÛôÿÝäMßNá$NãDNådNç„Né¤NëÄNíäNïOñ$OóDOõdO÷„Où¤OûÄOýôÿÿPÿ/PÿOPÿoPÿPÿ¯PÿÏPÿÿÿQ%QõÿeQ…Q¥QÅQåQR!%R#ER%eR'…R)¥R+ÅR-åR/S1%S3ES5õÿ7…Sÿÿÿ;ÅS=åS?TAõÿCETEeTG…TI¥TKÅTMåTÿÿÿÿÿÿÿÿÿÿ Public domain font. Share and enjoy. KK g 0  zz‡”¡®»ÈÕâïü #0=JWdq~‹˜¥²¿ÌÙæó '4AN[hu‚œ©¶ÃÐÝê÷+8ER_ly†“ ­ºÇÔáîû"/<IVcp}Š—¤±¾ËØåòÿ &3@MZgtŽ›¨µÂÏÜéö*7DQ^kx…’Ÿ¬¹ÆÓàíú pøp ´H´H´H´ðð€à€€< 8 p€€€p8$8($€€€€ð< 8 pˆˆp ø øˆÈ¨˜ˆ <ˆˆPP |ððþþþþþþðþþ`€`ü€``€üüHHHˆü ü@€8D@à@@D¸(((((|(|((ð€à€žx€€x<"<""€€€ø> < 8DD8þþˆÈ¨˜ˆ >ˆˆP >ððÿÿÿÿÿÿðÿÿ € þ€  €þþ$$$Dþþ@€" ø x¦@$$$$$~$~$$BF:@@@\bBBb\ \z?€$$$$€€€€"">""€< 8 '€ €€€ >€??€€"2*&"€""€üü€€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€€üÿ€ÿ€ ?€€€  ?€€? €?? €€€>)€€ ??$$€€)) !€€€"""""!#€€ ? €?€ ?€ € €€€€€€    ?€€€€? €€€€€€ !?€? /0 €€€€€€ /0 €€€€?€€€  €€€€€€€ !€€€€€€€ ??€€ €€€ '()& €€€€€€  ? €€€€€€€??€€€€€€€ €€??€€€€€€€€??€€?€ # €€€€€ ? €€€€€€€€€€!€ !"$8($"! €€ ?€ 1**$$ €€€€€€€€€€ 0($"! €€€€€€€€€€ €€€€€€€€? ? €€€ $"€€€€€€€€€? ?$"! €€€€€  €€€€€?€ €€€€€€€€€  €€€ $$$$*€€€€€€€€€    €€€€  €€? ?€€€ €  €€  !€€€€€€ /0 0/€€€€€ €€! !€€€€€€€€€€ ? €€€>€€!!!  €€€ /0 €€€€€€ !!! #,0,# €€ ;$$$$$ €€€€€€/0 €€€€€€ €€€€€/0 0/ €€€€€! !€€€€€€€€€€'€€  €€€?€!!!!!!€  €€ $$$*€€€€€€    €€!!!!!#!??€€ €€88$#€€UªUªUªUªUªUªUªU€€€€€€€€€€€€€€9X15ŽÐŽÑŽÒŽÓŽÔŽÕŽÖŽ×ŽØŽÙŽÚŽÛŽÜŽÝŽÞŽßŽàŽáŽâŽãŽäŽåŽæŽçŽèŽéŽêŽAÿÿÿÿÿÿïŽÿÿñŽÿÿÿÿôŽÿÿÿÿ÷ŽÿÿÿÿÿÿûŽÿÿýŽþŽÿŽÿzangband/lib/xtra/font/xm8x16b.fnt0000755000000000000000000000460710250356274015776 0ustar rootrootYð ~ xm8x16b.fntX_misc_bold$$$~$$$~$$$*(( * PR$%<@@ 0IFF9   0 0k"  @@ 8 pppc``>c>~cccccccc>cccc666ccckkwwccfff<`````>?ccccc?>cc``>>?ccccc?>```~ccccccx~<```cflxlfc8<vkkkkccnsccccc>ccccc>~ccccc~```?ccccc?np`````>``<|~cccccg;ccc66cckkkk6ff<~ 0`~  0 01IF Copyright 1989 Dale Schumacher, dal@syntel.mn.org 399 Beacon Ave. St. Paul, MN 55104-3527 Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Dale Schumacher not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Dale Schumacher makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. FONT -Schumacher-Clean-Bold-R-Normal--16-160-75-75-C-80-ISO8859-1 COPYRIGHT "Copyright 1989 Dale Schumacher." zangband/lib/xtra/font/makefile.zb0000644000000000000000000000043410250356274016151 0ustar rootrootsubdir = ./lib/xtra/font/ ## makefile.zb srcfiles += lib/xtra/font/makefile.zb files += $(addprefix lib/xtra/font/,\ 10X20.FON 16x16.txt 6X10.FON 6X13.FON 6X9.FON 7X13B.FON 8X13B.FON \ 9X15B.FON 12X24.FON 5X8.FON 6X12.FON 6X13B.FON 7X13.FON 8X13.FON \ 9X15.FON xm8x16b.fnt \ ) zangband/lib/xtra/graf/0000755000000000000000000000000010250356274014007 5ustar rootrootzangband/lib/xtra/graf/16x16.bmp0000644000000000000000000206206610250356274015310 0ustar rootrootBM6d6(0`ÄÄ000 DDDuuueeeUUU‰‰‰™™™ºººÎÎÿ™™ÿªªª™™ÎeeÎÎÎÎee™00e,ÞÞÞ0eee™Î0e™îîîe™™™ÎÎ0™Î™ÿÿe™0eÎÿÿeÎÎ000™™0ÎÎ0ÎÿeÎÿe™ÿ0™ÿ00™ÎΙ™ee™Îe™e™ÎÿÎÿe00™00™e0Îe0™™e™ee00Îeeÿÿe0eÎÿÿÎ00ÿ™™e™ÿ0e0eeÿÿ000e0ÿeee0U0™eD ™0ue ‰ÿ0DªeÿÎ0™ÿ0™™eÎÿ™™0Îeeÿe0ÿ00ÿÎeΙ0Ιe0e0ÿ™eee0eΙÿÿ™ÿÿe0Î0ÿÎÿe0ÿeeÎe™Îeee™000ÿe0uÎÎeî™ÿ™™Î™ÎΙ™eÿÿÎUÿ0eÿîee™0ÿ™ÿ™™eÿÎÿ™0Î00ÿ™Îe™eÿÎeÎ0ΙÎ0e0Îe0ÿ0™ÿÎ0™ee0™ 0™Î™™eÿ™e™0e0e™™0ÿÿ™eÎÎeÿ0eÿÎÎÿ0Îÿ0™Îe™eÎeÎÿÿ00e000™ª‰Î™ÎÿÎÿΙ™0™0™‰D™™0™0™0e0Îe0ÎÎ0ºÞΙÿe0ÿ0ΙU0ÿÎueÿÎîÿΙ0ÎΙª™Î0ÞºÎÎ0ÿ™ÿÿeÎ0ÿÿ™0ÿÎ0eÿÞeΙÿe0ÎÎ0eÎe™™™ÎÎÎÎ0™ÿÿÿΙÿ0ÎeΙÎ0ÎΙÎÿ™!! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !O!  !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !..DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDO !!DDOOO ::! OO ''! OO }}! OO ÃÃ! OO ¡¡! OO ±±! OO ! OO <33ªÂ€ "MC- ‚V±±.A!f4ft6± y{:œ9'%$a< <3ec¤¤€€?€€¨ C VJ±XZY± !!5f5y5XˆtˆtŒ y{{ ''@'9:Z ªeabah¢µ¤k?Ü]Üš  C V‚‚I‚JG±XXY[[4f 5c0c4yc5Yttˆtˆthyy y:$/:$%  a he3;¤;¤W@}k?š]äCdG+JHHJQ‚XXY4 f4yffy5fc4tZ6(jt{ Œ ''/'.$Z  a 3ueu3<›4´;4*/kšÜ3ܨ D!!|!n‚QQ‚V‚X±X[4 .4 Ac05cYˆttXZYZˆ6t @$œ'%œœ$ ª<2Ýcªa#/œWkšc;=C d  nQ+‚QZ±.  A50yfccyWYXZX6XW,6t y {y{ Ä::/:sD!¶´]5O©Oµµ°–WOX²³KL[™DOXO9[L³A¨§¨µ§[’fM4  ˜†‰!G~O(((([KLLkLDDDDD00²²0s›s_s!OZ´]5O¤µ°Ø–™[²DDX/, [[[ddP¨[4§§J˜0†˜  (IILDODDD0s>s]5¤µ°ØDD,XXZ/X[LL[†!GGGJJ((CCff11f1^^^y4O[[0    GJJ GKJ(99(69(XDDOOff™12fD(Lq^^ 4yOOOO[PPO²!fA!0   KKGJJGJ KJ(99((XDO² [1f221™f24DCDCILLLL[LLLq^&22y12{{y4OP[OO²OO ™LSSO²!+? K GKKKKGJJGJÆ KKJJG((69jDCXO²² OY D[If2122241f4CDDDLLL[[LI  fOYO›qHH ™21y4244444y{yPP[[I²²²O!OILPO²f?0?0“0K KKKKHKKKJJGÆÆJ JG699jCXOOO²0 }}YOOOOD[LI¨f2™ss44(CDODDDODIL L[[ LYOO(›LHHB&B2y4422444yOSPLL I²²² ²IO0²²f?0?0  GGKHKKKKKKGJÆ GGGO('9'(DjOO²YY OLI™™f21sf411f™CODCCDYˆD(DIL[[ LIAf00f(((YOODYLH:BL&&By2442442PS[LL[[I²²²ODD!²D[LdI[OP0²?00D0  K HHHGH KKGGJJJGGGIIG6D9'9CjO²²² 6YËYI™¨?™ff2ffs441fCYDCDOCDODLIL[L A0fff4((YYY(›q›0HHB´Y&9²BNW{{424 O²PPS[[L[LL²²²O!OOD[P[LIL[POPO+A??0OD“0  K HH HKKJJ GGGIGGGDOOO9DXO YYYË}}6[[¨™¨Kd1fsfff41fff4fCDCDOCDDOL[[Z0K0KfX4Zj^›0€€AB´O9k&²N´0{{4222224²S[LDOI²²ZOZDP[dIL[PPO0²A!²000000“OD“0?W:WKH   KKJÆ JJGJGDDD(9'SS. .OY6ËË}}Ë[™?¨jdddfff4ffffDCCDCDODD[[[[KNfNf•˜KZ/jDO^;°Ô€0AOYkU²´0?yy44224224OPP[[[[LIDODDODP[L[[PPPS00s²f?0¨OD“0?? KH   HHKJÆGJÆJ(JÆGDD9(S  Y}}YË }}}DDD¨™ddIIf4fIf’s™DCDCCOYL99[I[N   f---fNXZ0Z/  fOYDOOYDY^µÔ}·0O}kXB´000?2{y424yy442  PLLLds!DODDOPP™[SPO0ss²&&0“PD“0?0 UWWKHGHKKHGHKKJÆJGGJJGJ}JGD(''9} O} }OYOD™?¨KKIddIIIId’(’ssDC(CDˆ¨™0IIL9LI [9   f˜-•KXjXZf  (fYDOOY(Y ^;°Ô}©OkYPH´0D0{y4222442 LdssODDO&DP²™?™SO0ssf²&&0“0?0? UW:,KGKKHKKHGKJGÆJGGÆJIG(DDOj%9XZ }}¨¨dKI’’ddId’6ds(COD¨™™¨ILIILZXZf fDYOY ;µ°Ô·}YPNL““X044y{4224OLsD(DDP™SPPO10s(“0?0WjW:KHKK HGJÆGIGGJÆG(DODUDXXOOS%%XXZ&&Y DU|K’g’’dd6Od ™YD¨IXI DW/Xf f °µ}YPH´[L0?“?0X2414yy4422y{4O&&D™™?OPOPO011s& 0“00? K KKK KÆJJGJJJJDDSWSSS%Y |t|dKLdg’’dO( ’dODDO9D0fXf0fff fX }YHH[[L0?0j92@4422y4y{{{4O6O DD²OPWX1550s²D0??0 KKKK KKHK KGJJGIGJSO(  }}Y|tKIddd’d/(YCDD(fkfffkf fXj² B[[[LH000?0jj2444yy444{4OO DDO15510s²0D00KKKKK IL(/((f444f²² B[LL0244224O0011110f))))}}DOf]f@@@@@,+!++!++!OfffffCCC99PPPCYCY}DCUXDDDDYPPPPPO™ÏY PPPPKKIIKK]f@@@,++++++*!PPOPPO]ffxfCPCCPPCPSPPOPPPCCCYC}}PPPDDD¨UXOPDDD DDOYDOP66POÙÏ´N ]f]DZ,@,*+QÆOSODPPO!Of]fx]CPPCCPPSSPPOPPPCD|DYC}Y}}OIG*GSSDD¨¨§jUPDD}PDO}YD DOD¨›ÙNK IIKKffuffWD,)Z,)+H+Q*+*+ODDDO!OOXff]]0CPCPPStSPPDDPPPCˆ}C}+GI,,GSSDDDODDD§UPDODOYDDYDD  DY(O(DO¨€[›¤KKgO Ou]f//D,)@))),HN+Q*+DOSDOOOOXfff]ff00CPCCPStPPPPOPD!PP}CˆC+IOG****YPSDDDYOODD²¨jDPOYDY   DODDO6O(PO¨[¤¯;£N´ˆˆ“ˆ““LOGOf]]f]D//D)@),,,f!!H+*Æ*ODOODOC!Offff0K00CCPPSCPYOPDPˆˆ}C}IG*** ,GSSYDYDODDOY§XPDO}  YODD(ODPP(€I¤¯;£]˜´d9Yˆ“ˆˆ“} IKOGGO]f]]fufO}ODWWD,)@@),,f++!HF+Q*Æ++ OtO DOOC! fffKdK0CPCCSPPOPYPDPD!O!}CG*G+PSYDYDYOODDO¨}DY  DODD(6DOPO¨[dYY}ÏÙ¤;¯;£˜˜T—jWf(ˆYOO  Ofu]]{]fffDO}OD,)@)),),Z++!K+*+*F+*9SjWODOO Y Xf]dCCCPOPPYPOP!|DOO}YG+*GPSYDOOD DOYO  DDD6(6(X[™ˆ Ù00 0ffffYWY©DO}O Of]]]]]]ffDOOOD)@)Z*)Z+*++HF!K+F~+**+9DS±DOODC!O } XOff]ffffK49CCCCCPPSSOOPPDDPDDD|O!CC+*,G SDOOP  PYDWYˆ DDYO6X6(PPDD6(OO™™™Ï0P04fˆˆ©YDO!GGGGKOf]]]]uf)@)Z))Z ÆQ+H+~HQÆ.ODUUDDD9DCO! Zff]xfff]xfKddK0CCSPCCCKSOSPPPPPD|DDD|DD|O!DCG*,*G SPDYDODP}YW DDDY  OYDOD6OOPPDP(€¨€¨™0PPS¨00ffW(4ˆˆYOODL,]uu])@),Z,)ZKK66H(UUWOD!XZ]ff]fff]KKC9SPSPCCCPSPPOPSSOD!|DD|OODG*,GGGGG*G SSSDDOYWYOPDDOYPDOj9j PDDY fDO((POOOX ¨¨)(S§9104f4fO©YYYYˆ“©“YOGG ]{u]f,),*Z,,)ZZ,f0Y.UUUWO!XOffK0KPSSPSPCSWOPOOSSPD!O!DG*GG* G,SPYSSPSDDYYYDPDDYOXXDPODDDDDYO ]D((6YO6L9(?OO©Yˆ“Y©O]uffu{u)),*+,@,@Z,,6Y6.O9f0O99SSOOPPWODDD|DGG!G!  SXWSYDYYDO9ODDOOO  ]DDO(XXXD(?“?OOOOuf,)*+)@,6~9 09SSY  !!!!DOYDD9DDO fOYXDZX?,)*Z,@),) 0SO !!DYOOOOfŠ 66(sfsssŠgDDDDDDDDDD+++ ˆ6€€€€6669. KKIPPPPŠgfd"DODODDD!++!+++    ˆODYYDYOYOYYOD€€¨¨€€€LKHHHKHLH((6™999. KGKK((’PSPPDŠgŠ-DODOO H++!     6XODDODDDOD€¨€€BHBBH((6(((66OOSODOOO GK(((’PSDŠ-ff   !HH+  (DOD€¨KK€?KN!BI(((66((66OODDDOO!OX O IIKK((9(d"PSOP’gŠfODZ,,!+  " ˆOZ,X(©(XXDD€KNN€?€€N +!!IK((6(66DOSDO!Z,,ZZZOO999X(979ddd’fPgŠfDDDOD4+!+!HH+ !((¨((DD€KNNK€¨€KCCCCCC+!BLIKLK(((6(6000OOtOOZO//OODOO9LXGK(7’dd’I’ !PSsf|d½ffDD ODOfK++!++""H+++ !((§(((€NNN€€CCCCCPPPCCCCCC-!BIILII((666020 2 DD99OXZ,,X.OIKXGGO(99O(K U ’’P!!!GffDO 4++++!""!H+!O(((¨§©(0D€NK¨€€ C PPP PP CC ---LL(((66™00 ›2OY±DOO.ZZ00,/,O!OXX((9((9(9(’d’’’   !PODf’---fDf+O+!+H!+H (((§§¨"€??€K€€¨€€?€PPSSSSPPSSSSPC!BIBL(((6I020 q2DD±D/00Z :UX(O9O779d’’d’SfPD|!dffg-D4f!!H+!++H+H6"!OXZ,X¨¨¨€€ €€CCCCCCCCCC-Ix-L6((66™|D0202DDO9DDZ.O!D!!GGGGK (((Kdd’d ’SfPSsŠ-d!LPfD  }}f++!K*+*+HHHK6! "6,/¨"0?€€ -(BxIL((66I™A024DODO(OZZDOD %L, ((((d’ ’fsff fŠ--’PDUDf 4+H!!+*+HHKK!!"6(("€€-ˆ--L(600OD99,ZO!OO9%%GG(K’d ’’df0fŠ-OODf4!H++!K:::6}(€€??-x-L(  999ZZOO9O%(((’d’’’’d( f-fDH++!!K ( (€?OOOO(((rf!(  OOCCCYSSµYOttOˆ(OY!O!OOO O[OO(SOSCYOSS=µDDDDDOOYˆttYO!OBD.."O!O[L[[9OYSSYOSPµµ=µ=ODDDOKdIIOBKKBKBOD..YYYO!L[YYOY66XOO('9OYOCCCSPPµµµµ= OOOOIdLKBBKBNBO!OXZXZOO!OYOOL[YSSSOY66XOXOOO 9'SPPPCYSµµµµOLH ((IKKLBB-BLLBOO!O!ZXZOZOOYYO!LKXYOSSYXOODX XODOX 99(9(CP PCCPOS=µµµµµOOAHH DD((LLLL ONL--NO!O!!OOOXZZXOO!!OO!O³L[XYOSOYYOY66O  D*DDXXODDXX (@(P CCPOµµµµLHOD(O/,BKKBL!O!O.ZOX.OOZOY"!YL[OXYYOYXYSY,*XXXOXXX  99'(CPPCSS=µµµµLABL ODO(O((dLLLKKjBLNBKLB!O!O!Z.OZXO6YY(Y""6KYXYXYXXOYSYY  ˆ}X *OXOXX  ((99CPPCOµµµµAHH OD(((ˆO(Kdd}KKdK,OXXj }B}OOO*O!/!OXZOZYYY6YY6"6[K[L[²²YOOY O}ˆ7ˆX& 'CP PPOµµ (((O7(d} ODOBBBKOYZZ!Z.XZX!YLL[²™X OˆO 99   .9CCP PPPµµµµµ ˆ((Oˆ(ˆ(OOOODBB} *O.Z!!(([²?YO .&9&9 &&.9PPPPY==µµ=ˆˆODOOOOZXOOYOW™YO9.W..9.PO}OP=µµ=xddOOO OY!!6™YW&.POPµ(HKdH OOYYOZ/WP(}KOO231"+P!*****<Û¬>>ÂÂààåÙÙÙäÔPPCPCPPCPCCCCCCC^^^^;^;;;;;;;; 5 ––––––––––;Â--f˜˜˜f˜˜B˜fffffff?0?0?????µ?1111^––^11111111ØØØØ=Ø^5^5155Ø=^00-55=555=55 55?;;××Üܰ““((((""Ù¤¯¯›qzz³³³³³³³³zqq›¯¯¤Ùš©©©Y©©©šÙÙÙ¤¤¤¯¯››qqz³‰•‚MMJJJ**XX22fff0020f02+P!********||S³³zzqq¸ÂÂàá¤ÙãäPDDPDPPDPCPCPPCP^^^^;^^;;;^;;; ––––^–^–^0–;;-˜-˜f˜BBBBfffff˜˜00¨???????000?¨00551115^^11^51000ØØ5Ø^5-;5455^Ø00---55=--5-5-5-=5;×××ܰ°““((("""’¤¤¯›qzz‰‰‰••‰‰‰³zqq›¯¤ššYYXXXXXY©©©ššÙ¤¤¯¯›qzz³‰‚MMJJ*XXXXXYY0000ddd˜-ddf ff““2dfA001DSB•B!!CPPPD|O|DDppfzfq£>ªªÂ⯤ÙãDDDDDDSDDDPDPPDP^^55^^11^;;;;;;; ––^–^–^–15;0;˜Bff˜BBBBf˜fff˜˜¨0¨?0????¨¨0?5515^^––^^^55000Ø=5^Ø×4f455]44 00?“=555µ=--=µ-=-¸¸;;××Üܰ““((("""’´!¤¯›qzz³‰••••••‰³zq›¯¤š©YXXXXXYY©ššÙ¤¯¯›qz³‰•‚MJJ*XXXXXXYY0f0?f?ff’--C!!+!W““2d+232pcb›†+!!DSC!!!SS****SËËD|t|DYÇpB³³ffz£qqª›¯¯áSSDDSSSS|DSDSDDD5151^1^^^;^;;;^–^––^–^–^1;;;; Bf˜ff˜BBf˜’’ff˜˜¨0¨A0A?0?????¨¨™0?A005^^––^550000=Ø5^4^4545450 ---55---5£¸¸¸;;×××ܰ°““((("""’’’´¯¯›qz³‰••‚‚‚••‰‰zq¯¤š©YX*****XYY©ššÙ¤¯›qz³••‚MJJ**XXXXX0?2?5?0“-’dd -´-0!!!G!!!+!C"“??“2^peÆSÆ›+!!G+((****::*vˆ|t|DˈvDBB³fffz0zq›››ªà||SS||SS|SSSSSSS5101^11^^^^^; –^^–^^^^;;;;;- B˜˜˜˜fBBBf˜˜fff˜?001A0¨?????¨¨¨¨¨0?0550005^–550000===5554425244505-555µ5--££¸;;4Üܰ“(((""""’’!!¨¯›qz³‰••‚MMM‚••‰z›¯¤š©X**JJJJJJJ*XXY©šÙ¤¯›qz‰‰•‚‚MJJ**0°f“0 x†-!G+(C¨(!C"““““?“05^Æee$Ðd•´(GGG??**:::**}ËÇDtttS}}ABffffffzqq›ªÂOOO||O||S|||||||^05151^^^^^^^^^^^^^1;;0;0˜˜fff˜˜ff˜˜˜f˜˜f?00000¨?00???¨¨¨¨0?0100001550000552554-5-×555xŒ--5-5µµ5££¸¸;;××°““((("""’!!!›qzz³‰•‚MMMMM‚•‰q›¤Ù©YX*JJJJMMMMMJJ*XXY©šÙ¤¤¯qz³‰••‚‚MJJ******01f“-  @ -- !!!¨Z§(!!!!**"““““““?05§0§^ÆÆ$$d†B(((GM((???***)*:"***vYS±tt±|}fdfff³qfq¸ÂtOt|OOt|O||OO|OO5–11151^^1^^^^^^^^^^^^15^;;;;;˜˜f˜f˜ff˜˜˜˜f˜˜˜¨??00¨000??¨0¨¨?0100000000550055==-5554554ØØxŒ“--522??µ=--£¸¸¸;××ܰ““((("""""’’’’¨!!!›qz³‰‰•‚MMMMM‚•‰q¯¤š©X*JJJJMMM‚‚‚MMJ**XY©©š¤¤¯qzz³‰‰•‚‚MMJJJJJ00’"5?f   @$!IC!t!!t+’!**"““““““055="ÉßÙß$d+GM??********:""ʱOOtYYYOÇ}NBf³Ûzfz££t±ttOtttOOOOttOt–500155^^1^^^^^^^^^^150;^0;^;d!  Bf˜Bff˜ff˜˜fff˜˜???00????00¨¨¨¨¨?1100000000==5^5552^Ø=¥Œ=5555=552µ5-¸¸;;×Üܰ°(((("""""""""!qzz³‰•‚MMMMMM‚•‰q¯ÙšYX*JJMMM‚‚•••‚MMJ**XY©©šÙ¤¯qqzz³‰‰••‚‚‚‚M‚0?0’200 @ {@ ´†- ![!!GG!+++*""*““Å“505555="˜5Ùß$(GGGG(??*::::::*::ÊYOOYˆËÇYËËSGNdf‰³ffz¬8YtOttt±tt|O±tt±t^550151110111^^^^^^^^^0^^^0^-  ˜’f˜˜ffff˜ff˜˜˜˜??000A?0??¨¨¨¨D01500000000Ø^^^^424]- ^=Œ-==-55---55--?¸;××Üܰ“““("""""""""""qzz‰‰•‚MMMMMM‚•³›¯Ù©Y*JJMM‚‚••••••‚MJJ**XXY©šÙ¯¯›qqzz³‰‰‰•••••0df5?’--? 44o  ¶†-f!!!¨S+J+++++""""?““““5§§55©555€µ=ÜÙ$G!!!MG(**:::::::**:SSvvttÊ}ÊÊ}ˆÇSGdQdddffff³ÝÞY±t±±tt±ttt±Y±±Y^5511555151111^^^^^^^15;^^^^0^^ f  f˜ff˜˜˜˜fdd˜˜˜˜˜¨?00?00¨??0“1^50005505Ø^ØØØØ4^5ר55Œ¥ x –ŒŒ 555=5-555-?;;Üܰ°“““(((""""((zz³‰••‚MMMMM‚•‰³›¤Ù©Y*JJM‚‚•••‰‰••‚MJJJ**XXY©š¤¤¯¯›qqzzzz³³³³zf05-µf(4oo4 -df0f¨!++!!!,,"?““?“©5§5§§555?5=5ŒØ!GGG*)::::::**S|O}ËË}}}}ˆÇS+MMNddddff‰³ÛÛÇYÇYY±±±±t±±±YYY^05111;51101^^;;^^^^^0^^^^  ffff˜˜˜f˜’dff˜˜˜??000¨?00????????°]10000055055Ø^5Ø45-=Ø^ ŒŒŒx  –Œ Œ-5-555==5554××Üܰ°““““(((((((((zz³‰••MMMMM‚‚•‰³›¤š©Y*JMM‚••‰‰‰‰••‚MJJJ***XXY©šÙ¤¤¯¯¯››qqqqqqq’fµ5?5“?4oo4@010!!!!!!!C*,""(???5©©5“5G5=ØÆŒ"XGG**:::**||vv:}:ÊÇSSSJJMMQBddp‰‰³³ÇËËËËÇY±±±Y±±ÇÇÇ^^05111^0;;;111;^^;^^^^0^^^0^^› ffff˜˜f˜˜’fd˜f˜f¨???0¨?0?¨???????0¨?f00000500004455555-  ŒŒ xx x x5-5=55=5555×ד“““((((((((((((((““°z³‰‰••‚MMMM‚••‰³›¤Ù©Y*JM‚••‰‰‰‰‰‰•‚MJJJ***XXY©šÙÙÙ¤¤¤¯¯¯¯¯¯¯¯d’f050f?f?0?44-221C!!!!!L**“§/55/===ÆÎUZ*||}vvS||S**JMT‚Q••‰pfYˆËˆËˆËËDZÇY±ËËË^›^^5^50;;;011^;;;1^^^^0^^0^^ ffffff˜˜˜f˜˜˜BBB¨??00¨???0001?0¨¨¨?00050000000000;552=^5442µ5ŒxxŒxx Œ^^5-55==555°°“““((((((((((((((““°°°z³‰‰••‚MM‚‚‚•‰³z›¤Ù©YJMM••‰‰‰³³‰‰•‚MJJJ****XXY©©ššššššÙÙÙÙÙÙÙÙd´’050??f05310““555==ÎUÆ"Z??)**))***SS ||SZX,**Mž‚dd••‰ÇËʈʈʈˆËÇËËˈË^^›^0›^;;0;10;;15^^0^^–^^^0› -f-ff˜˜˜˜˜˜˜˜BBB¨??0?0???¨¨¨¨¨°f55000000000500-^^5555^54- ŒŒ   Œ¥¥==55?--“((((((((((((((((“““°°°°°fzz³‰‰•‚‚‚‚‚••‰³z›¯Ù©YJM‚••‰‰³³³‰‰•‚MJJ****XXXY©©©©©©©©©©©©©©©dd00?0??“(001110*§5ŒÆUU"Z??)**))** DDDkXUZ***JJMdQTÚÖÇÊÊvÊÊÊvÊÊˈˆÊˆÊ››››11›;0;;;;0Â;;^5^^0^^0^0^›1› ---ff˜˜˜˜˜˜fB˜BfD???0???¨?¨¨¨¨¨¨“205––5500000000^µ555^^µ^××5  Œx ¥= Œ555=55(((((((((((((((“““°°°°°µµµzz³³‰••‚‚‚‚••‰³z›¯Ù©XJM‚‰‰‰‰³³³‰‰•‚MJ****XXXYYYYYYYYYXXXXXXX012µ(0s005UÆÆU }ÇXWZZX**JžžÓÃÓ}vvv}vvv}ÊÊËÊÊvÊ››››››1›1›;;;Â;Â;;–^––^–^–––›1››--˜fff˜˜f˜˜f˜˜˜B??0¨??¨?¨¨¨¨¨¨?25––55005500550001^µ555µµµ×5 Œx ¥Ø¥¥¥5==5((((((((((((((((°°°°°µµµ1111qzz³‰‰•‚‚‚‚••‰³z›¯Ù©XJ‚•‰‰³³³³³‰‰•‚MJ*XXXXXYYXXXX***JJ*****XXYY©XXÆŠd44444(+***XXYY©*ddd-XXXXXXX‡‡‡‘¶‡44  44 DD|7(X((+***XXXYY""d´´´ddd-///XXXXZZjjX(‡m½x444444444 4|OOS|OtO(7(XO(((+****XXt9("?d´’’dd---Xjjjj,WZZjjjjX‡½†¶d[!!²²444yyyyy44*,,,*OtDOOD||OO(767OOO((((+***XXXO""9 d´ddd’’d XjjjZ,WZXXXjX ‡‡¶´IdILLIGdL4 yyyyyy 4,)WW,,+OtOSOODO7((6XXOOD(OO(**XXXX|""d´’´´d´´d-- XZjXXXXX‡m†ddLALddd!!GI4yyyyyyyy4*,W,**,OOD|±DOO(76((XXXOO!OOODD*XX**Xddddd’’??--XXXjjXXXXdddd½¶’ddd²²!ddddLIL4 yyyyyyyy44,,(ÇÇtOO(((7(jXOXOXXO!((OO**XXX""*,?d’´d´´d??CPCPP--/XXXXjjXXXXXdd’’´­¶’’dddI!!IIA4 yyyyyyyy44,,/,*,*|OS!Ëjˆt||((OOj~jjXO!DO!!O((O!!ODOOXX"ZZ ??GG+´??!XXSXUP--/!XXXXZXXXjXXXd´´d¶x½’dddL!²²dG4yyyyyyyy4W/,,,,,*|OSOÇjjÇ|((((OOO~jO!((DDOOXXXXX*,WZ d?(!GG!d??SXXSC--UXXXXXjXdddgx´¶’ddd[²fG4 yyyyyy 4!W,,/*DOËjÇO(7(D(OO(**XXXXX,,XG““GGPDC-XXXXXXXXXdd‘†MA­xdddAs 4 yyyy !,)!,*+|OS±YtDD(6((76(O((((GJ******""*(!’´d- XXXXX(“XXXX(d ‡‡Ö‡‡m­dd² 44444 ),),*ËËOD||((((((GJJJ****,," ddddd-//XjX(““(XX(XX­‡ÆJÔ>‚mЇž}O}²444,,,,,ˆˆt|S|7(((d+JJJJ*****XX’’’’--/XXjX?““(jjjXJz‡dd’t>ž‡MVž²²44ZYYSD|OddGGJJJ*********Xjj??XjXXM¸MdÁžVV>VƲ²44D•NNMMMJJJJJJJJJJXjJJMMGžVÆž²fY!!!!*D¨0°°PPOO(DDj(jjjUUDDD6(UWUDD(U::kYYYs4YY!!!*PPP¨!°?00?00?°(}(DYDPYDOOODOO(D//((~/W~YUYD6(U( ˆUWˆ(DYkUD(U:k(OU:OYˆYYttPDODfsfsfsfYYY[!!++PPD¨¨¨!+P 0°?000001°O((}(7}ODOPPYYODDD 6((O6(/WUjW/Wj~jYjrr(WkˆˆWˆOYkU:Wr((kU(OSSˆYˆSSODfffsfsY}Y!!²!!PPD¨¨¨?¨ !¨00°01?1?°005OO005(((O(( DODODYO(DDO6(6YY~6(/Uk~~kjjDYD6 O 6k(WˆD(W(kWWWU(OY:OOˆ|OtS  PP!sfffYYYY!!!+!PPD¨¨??¨=¨¨01Ž?1?°0ް000505!O00505O}OODD}(DOODYOOOY DD YDD6(~~O~WDjk~kjDO(  77( D((k((OOOWWU(OO:UkOOYÇYOSDCP45 P!fs4fYYYj}Y[[[I!²!PP¨¨?=¨S?=¨°01°?=°?1°005OO050505OO(O(ODDYY(OY6(YDO YD6~DY~~~YY~((/6DOkjjjjjDj6O  7 DWUOUWrrOUY((SOYY}}ËtD||f 4–54 PD4f4ffYYj(YjYI[I²+²PD¨?=¨SS¨1?°?1°?ް0A™5O!O!A™55A™OD(DYDDDOYDO((OYO(6ODYOODDO~~Y~YY~~~U/66OjOjjDOj6O (UˆD(OYrWrOOUDY(OOYY}YYSDÇO ff®yy  5D4sffffY}jY(YYYI[[² +PD¨“?OOS¨¨¨0°0??°=?°=Ž5A0A5!OO05(5A0ADDD DOOOOOYDOP}D66YOYODO(OOYYYPOD66ODOY~(~(UU(DjOOjDjjYOOrDYY(U(YYYUDDOSYˆYOSSˆ 5{{ S|t!}kfffsfsf4fY}Y(Y(Yj[II[+PPDD¨¨¨D??¨S?0°=?°?500™O!00!50™5(O(O(YOODOYDPYYP(YYOY(6ODDDYPPY(O6DDYY6~D(((ODOOjYYOOD 7DUY((rY(rYDSYPYˆtOˆ}®®  S|SP f4f44sf4Y}jY[I!++PPPP¨?“O¨°011Ž??°=00?5A005A00005(OO((7CDCYDOYDDOPDY((OD}666DY OPY(6~YDO~OY(DD(YY((OOD OO(DD(Y(Y(Y(Y(OOY}YPYYYtO}  {{ PPP f4fs(jYY}YL! + PD¨?=S0°0ް0°°005,00j5550656((DO}(CODCY}YYDDYDDYOD6(ODYDDDO(~YDY~~6(DUODDrODD O((D((Y(DO((OYO}¶PSSYYtYˆf 4®cDOD!Ds4f44fYYjYYY}[[™ PP¨O?“¨°00000?°5k„6((}(CDCCOCODOPDYYjYOO}YDOYOOYD~OYOY~O(DYUYYOOOODDr7OODO 7(DDY(YDDY(DOOS}SOYOˆ· 4{ P!PDOfssfffYYY}?*¨PPPPD¨“?¨001010°°6(CDDYUDDP}YWPPDYWYYDYO6OOYWY OD~YDOOY(DDUkOOY( 7D rDYDOYDOSSS}YYYOˆ}·}f {c  {§!OP4fsffYj[[™?™²²¨PPDDD¨O¨0°0000(CDODYPDDOYPOO}DYOOOOY(6OˆDOYˆ O(~~ODDODDO6(6UkYDOY7(OOO DYOOODYDOSSOˆ}YYYˆˆt}·4 {·§kO 4fsfYPPP¨°°0°0DYDPDYOPYDDOWW(666OYYˆ,O OY~(YOYUOODOO((YUYkYWYY7OrOrO r (DDOWˆWOODOO(SO}ˆYˆ} {{ fY¨PPP¨°0DDOY6~(OO6OO(UYD((r7( (DOODOO(Oˆ}} {X   ˆ f-7fKÆ ’½d,XX,X,K         DO (jˆ  fu(f]]]]fWGKGGKÆW ’DD,K    D6DjUf-fuu-(}f]f XZGGJGKGÄ/WX’’,XOd ((    (6DD, ,~DDjU  ff4xffr}7ffXZKGKJGG/ZZ   ’H’’’KX,DKK!  ((((((   6(6ODDDO6DDDˆ9UWkff  fffff4ff667x ²OKGJGKJÆÄ/WX   ’dHd’ddDOdKO!    ((      (((~DDO(6(DDOO(ˆˆjUUXˆfff  ff]4x fff(6r76]f ™²OKGKGJÆÄ/WZ  ½’’’’dd$KdODKKdKO!    ((  (((}   (6ODO(6 DDOYODDˆkYËkUUk(ˆff  fff-xff(6r77vf ?LKGÆ ÄÄXW   dHd½’’’$-(œKdKdD!  ((((       %O(DD O~(YkUOD(YˆkkWWkUfff- ]]fffxfr7 6r7( f]  ™L(~&  ÄZZXZ  ’H’dd--dXDdKddOODOU  (   ((OY,D6 @O±ˆU±ODD(YÊjWWUkˆff   fffu]f4­f6. 6ˆr7}ff ²d((}}Ïr& lZZ   ’d’Kd’’’$-dDOOd-d!OD!ODO               (((%ODD((O @ (OYkU±ODOˆ((ˆvUWWZÊff--f  4 fff ­­fffr} ... (7fx]  ²L³(“((Ïr& Ì2qX  ’d’d$$d-XDX (9d-dO!DOOO (     (((·  (DDDOD ((YˆYOODOˆˆ(Y((vjWWj(ff-  ffff4fff}r7 7((7 77rff]] ??™™²KÎ?Î(& lÌ2q2q   ’d’d’-{X X(9{-!OO!ODO 6(       §OO%(((OYDY ((±OOYYˆ(Y((ÊjWU6(  ff4r 77(((7 7ffff]xx]?™²²IKd³?ÎÍ?(&lÌ2q2q ½’-d{dK ,d-{y’KO!!O!           %( D(,OD ((ˆˆYY(ÇkUj  ffff]­f(rr 7777 7ffff]f  [LIKd““ÎÍ?“Ì2q  d’’$$gyy-wdx…d’dO!6  6     ( (OD(O (Yˆˆ(YÇkË  f44ffu4ff66rr 7ff [LK³??“Í“Ì22 ’’dd’d’dg-dK--’dKODO     (DD(((Yˆˆˆkˆff-fff-r}}(f [???Î222q2’½KdddKKKddKOD   D(YˆkYfffr??q !!¨! |22242™!§D|X¨!!9!²²DOO OODOO±±OO 9OO9OOOC|D|t|||DD 2212414424CCCCDD²™™€DDDDD9OOXPOK²²tYXOjXSOOOSSOO!!P²CDODSOOPOO±OOO±ˆOO . 999OO99²OOCD|t|tt|22111Õ1ÕDCC™€DOD999DD9999XODOKK²ss!jX·XXj!SSDOO!!OO!!²²|DP|PPPD||D|PPPS|PCC ˆOtˆ±±ˆ  ..OOSODOOO  ²CD|ÇtÇ|  2104242DPDD²™™€DDYDOSODO!OOOOODO!OKXPO10K²›1s!OX|ˆDDD!+!O!ODO SDCCCCPtOPPPD|SPCP9±±tˆ±ˆ  ..OODDDOO!O   ²CDtt|  2014ÕPODO™€™DDOOODDDO!O²DODDD!OK0ZDO0KKK²›ss1!X§XUÇ!OSDO!O!OO ²0²PSPPDSDO|DC|SSPD&Dˆtˆˆ . (DOSDO!  DD|t 00Õ2DDO™€DOYDOSDO²²DODD!KK0ZO01²›s151!XX!DO!!²²0|SO±|DDP|SOPS!9OOtˆ  OOtOO YYCC| B20122DDO™€DOODOOs²²DODOO0XO0KK9²s11sP!S(!!OOt+²0²ZCOt&D!&&D:W( DD99DOYY DCD±| B221121112B²OX9DOtO²DOtO00XZ,(01K²551(O!OOIGGO!!OO&&²²OD|POZSO&DOˆOOˆD( 9±DOO ¯²Xj|DD|t||| ´9421244124Õ9´PDOD²XSjjs²s9ODD9DXO(010²²551!!(Çkj!SO+IDSO!&PC|CDD|±|DDOC!Y&&OYˆ6( ( DD±D ¯²²Yjj||DD|t|tt|  BBB211Õ4B´B9PDOODO²DDS±Ds²²9D±DOO0DZOD0K² ²›s15stj,UjX!SDOIDSDSO!!O²DPSCCD|DOSOPCC|PDY OO6.6±ˆ Ž“Ž  DDO9DD   ¯²¯Yj||||t|   BB1D124B9PPXZD²²™X)ODSDO±DDD²OOD±OK0((XZODOs ²ss1|X(©UYX§!!OGIOD!! 0(²|t|tt|9OCSCPOSO Y±j6(ˆˆˆ. ““ .. DODO(O  ²¯. Yj ((CjtÇ((  Cj2B™DDDD²²j²™€DDjDDO9OODOOOOD²s²9O O!K10(X XOPDOX(ss² s1sX§$ZXO!G+&²OStOY  9±ˆ' “?§9“.“OD99 99 . jX(((  9²S99²9€DS9ODO9Os²9O OKK10 (OO/UX(s ›s1|UZ'+9+  OS D&& Yˆ99O9%“?/9/?“ 999 9. Y (C}( 9(O99(²9D(O}D(O99²9K100900U/Us²9sst6X9 0  DOˆˆY““  ¯ (CC(6· ² (S((((S(((99KK10KKXUj›ss SDDOˆY    ™(((K1KjXsODDOSDOOOODDDDDDOOODDD²ODOOODOOOOOOOOOOOOODOOYODDO D|DDDD!O!¨O¨!O!OOOOSSPOP!!O!OOD D|DDOOO  DOOOOODDOOOOODDD²²O OOOOˆˆOOdOIOOOOOOOOYˆO±±±  DDO!O¨¨§???¨O!!!OOO!SSDPD!P!DD!!O! DDO!!YjOY!(OOO9&&!²² ˆOtˆ±±ˆ!!IIIIKd  ˆOtˆˆ±ˆ± OKdII¨§?§¨²§¨¨OKdII  !!DCPPDPD !D!ddDDYZjYYDDODD DO!ODD9&&DDO²²   ±Otˆ±ˆ!!IKId±Otˆ±ˆ±  &DDOIIdLKK¨?¨¨²²?¨IIdLKK!!CDPPD   &DDD!OOdKdd!jYYZZ!OOOX!OO!!ODO!²² ˆOtˆˆ!!IdLK  ˆOtYˆ O!²KIK dK§¨²¨KIKIKdK  +!|CP DODO D!!O!LKddOYOYYO!ODD,,OOOOO!OO!O²²²9OtˆdH"IIKI  OtˆO!O²²L KdO¨²²¨LIIKd DDP DDD ODOO!OOLKdOjYZOD DODOO DO² W:W±ˆDD±Zd@LLI  DD±ZZWDO²LO§LLL +DO!W:W !ODKKdKZYjÏDODDODO!O  9DOD&&&DOD&²²  ±O±DO±""@L W:W|O±D  DOD²?W!D|!,O! Kdd’ZjYO(DO!OOO!!  D9&&!OD   ±Oˆˆ±OO±d"dKKd ±Yˆˆ±O  |!DODO?¨KdD!SDO!! )  Dd’YYjZO!OOO!O&O!O&²  ˆ±ˆˆ±KIdd  |Y|±ˆˆ± DDO²²OOO?²¨KIdd+DS!! *:OtKdYZ(D(D((²(² O±jˆ±LLLK   ±|±jˆ±ˆ± |D(D0(² OO?§²¨LLLK  !S O  Ot!OOO!,/O& O9O 9ˆ±OO&²O ?¨O % &YY OD/9D&& 9O9±D² OKKOO±ª % OOYjO//& ² DD0O OLIKK ±}²ª DK s!!!d!!!!!d!AAA2qqH’HHXZ!!!!!+!!!!!!!!AAAAAAHH9662qA22244LHN"’HNNHXXXX,,ZG+!!!!!!!!!!!!!!0000!§!¨!S!¨!AfAAAAACPH""dddH66 692qqq2224IHN"HHN’’NH"""""""X!!GKKW""XXXWYXXYXX""""Z,,,,d!!!!!!!!!!!!!Gdd!dd0f0?f?00?0tt±||X§!AAppAAAACCCP"#dHdddH6669 2qq24I’NHNKKHHNO""!!""!!XXG’XDDXW"HXAXssXssXWsXXYXY "",,/5!!!!!!!!!!!++!Gddd!ddG!0?2?5?0“5OOtYXËX±±OSApAAfAApACCCCDD"dHdHdg@666 22qAq2…dHK’"HHNKIHHOOO""O!OXX"O!OX!!dG’DDDDDDDK!KBAAIAABsssXX K!KK!KjYZ/5!!!+!!+G+!!!!!!!+!!!dddddd0?°f““?0SOtYXËX±XOAAAAApAfAAPP¨§0¨!"ddKH­9666 222Asq24dddfHK’"NHKLKKHOOO"O"" OOOOOO! d  DODDDBHAIABs2XYYX Zjj5!!!!!!!!!!!!+X++!Gdddddd!dd01°“f“?!ttXYÇY±tt|DAAfAAfAAPP¨DP??!""dddKKK@9666 -222HKL’""NHLLIKHDDDO9XOOYvY O   OOO KBABWB2 sYXYYKXZ,//,0!!!+G!A!!!tG+!!dddddG00µŽ=“5?fOXjXY±YOOAAAAAAAAAfACDSPD™C!HHKK"#d66Y((22222 -2qfKL"’KLIKK"OOD""" ˆO9XOYˆOODXWAHIIXXHXs2Y KZ,YXZj@Y,5!!!!d!+G!!G!+!!dd´ddd!!0?0°“?2?00S!±jjjX±t±OSAAApAAAAAAPPDSDC¨Hdd"d96Y(2qqq 2qK’.I"O"""½ "!OYX9X!"œXXOYˆ!Od0d’U/DOYXKDDP BAIXXsXY"KK""ZZ,ZZYj,!!!+!G+!!+!!!+X!!!´ddd!0d05?=“=µ?!SXjZjjY±OOSAAAfAAAfAAADS|PH’6Y(((q2ff.OO"O"½ OOˆYO9XX OOOYO’d0qKXXDOYX"K!O!OK BKXX2sUXjK OK"XZ@YZjYj!!!!!!!!!!++!¨dGddG!d0005??=?=µfDÇjjjX§t!¨!AAAAAAApAAC¨PDd­(qAA4fODD"""!OO XYˆOX9XO OYOYX  qO0’XYXOY DKOK" WXXBXXs2s XUYUjY KK K,,j,//,j!!+!+!!!+!+!!ddddddddd!!?0fµ5?5“?DOXXjXOOtO§¨AAAAAAAACPC!H"g6YY(qqA-…42.O"""½" "X!ˆO""X !OY  d0  WYOWO KKK ABWH ssW2ˆUUjKK KK Y/,@/!+!+!!!d!!!!!!!!!!dd!ddddd?0050f?f?0?|tYX±|OOtO!AAAAAAAAC!dHd’96q4422ff..""D"½XX"""XXXXY’  d  YY d d  /ZYY@,!!!!+!!!!G!!!!!!ddGdd0050??f0OtttDS!AAAAfAAACPdd6244ffd.HHHO X"X   " BBssXX " "XX!!!!!!!!!!!!d00?0OSS¨!¨AAAAA9--224ddHOO XUX,@X!!!!!!!!!0D!¨D2224’d’’dXXZZ²+!ODDXXOOsfs²ddddd"+++!!!XXjZXD((ss²DDdddddddddddddd"""ddddddddssss™""!++!++++*+XjjjZZ(f²™²Dd dd- ddddHA"ABd ddddddd’ssss¸¸2sddOOY(((™™€™™""!+!+++!+!+jjjWZs²²€ AHBAB rªsdsssAAIIdKdd’fUªs`s2s››¸q¸¸2IQddddOYYOOY(66(™€€š€™((!! +++*!++**++*ZjZU(²²€ AA"´#B  ªsf AIddKddd’f5UUU$$ªsª¸s¸222s›ª›sqqq¸csIKd´´dQd´´dOYˆYˆ(ˆ((6rr™€™€Ôš€€™™((O(!!+++""" +!+O+++O+O !!ZZXZXj™/:-  BBKHB’"Ar sfss  IKdKdKdd1fU$$$ªsªqq¸q¸22c`¸ssªsªsss¸qq¸{sIAHd´QdQ´¶´ddOYYYˆ(ˆ(66rr6Y™€€€š¼Ôšš€?™™OO(!!!!" $²""OOO+*+(ZZXjZU(fs² BAAB’BHr sªfs4 AKdKdKKKfU$$$$›ª¸¸¸¸`22222css›ªsqqss¸2sIBQ´´Q´´¶¶´dOˆYˆ(((66r76(Y™€š€š¼š€==™?(OO$dO+"²"+D++DDD++OO+*D+*+((ZOZ((6ffss™  O  AB’´B ss4  AKdKdKdKdKd5$$$$ª`2222cc2cs›¸ssqqqs¸2sHKLABQ´´´¶¶—´dYˆ((666r7666(€™€ššÔ€?=?=O((($$.dA ²@ +D++*OO!+++*DD+*OD+++*+ ((&&(((96(fs²²€ O  H#  sª4  AIdKKddKdKdKdd5U$$s`2c2cc{s¸s¸qs¸q¸s¸IKd´´´´¶—¿dˆY66r76 7(€™€??=?($...+dO+A ² @D++O!+OO+O! X(6(²™& &&O   B´ ssf  KKKKdKKKddddd’d1$s`cc{ª¸qs¸q¸q¸2IBN´´¶¶dY(66 ((™™?(d" "@@+*+++++O!O +++ !!(XjZW(sO&f4IAIKIdddd’’d1Uªª¸¸¸qs¸2HQ´´—Y(7(?=$B"A"@ ++*O!!OO+*WZ((6ff SSAId’d›ªdQB" +++++!W(f Bd""""#"#"@+*!d!(Ad""""""### +!!(A"""!fuff2 {O  H++O ffuuuuuuuf®c y4wOODOOO     ffsffffsfffssffssffffsffDDffsffsH+Xfu­uu­ufc {c {ODOODO!   0sffsfssfffs±±±±DD sfDOXDDDOXXXDD!sfL[[[f!+HXXOXj²jXOX  fu­fu]® wc DOODO!   000!fs!sfsfssffsOOO±ˆˆ±OOO±ˆˆ±DD ss sXXXZXXXZZZLLfsL[[fs!+H+(7XXXOOXXXj ²²DOXjOXjf­­c cwO!ODO     0050  fsDDfsffsffsOOO±}ˆOOO±}ˆD XXXXZXZWXLO[[O!HH+!O((XOXPPOZjX  ²DDO!jOXuufu]]® ® OO!ODODO    50 !s!fsODsODO&±Wˆ}±ˆ} XZXXZZWXOODOO!HH+O!!O(XXsOXXOPXXYXXX²™²DDDCOO!Xu­fu]fc® c c {OODCD!D    OO5W!DfDODOˆ±±O±±f  Ofs&ZOWZZXZZLLOODDD!ff+D!! 7XssPOXXOjZjZS!jXjj²™sOkODDOXOfufuf® w® cwcDDDOCDD!XO    O ffOD!sfsfsO!ffO± ±ˆˆ:O± ˆ±sfOXXOZWW&XWZZ![L*X!fs+DO!!O X77(sXOPZOZOZOZSZ$j$s²µ™DCOODOXX  ]u­u]® {4 c {DOCDCDOO!D    OO fsDDDOfssfffsDDPf&& ˆ±&& ˆ±OfODOZX&WW&ffsL+O!sHO!O!O  X7((r( hsXZOPXXjX$$²µµODDODO!Xuuff w5-®wO!ODCO!XO     fOsfsfffsf!O!Of&ˆ&O fsD&&WZ&&&fsfsO+!HOOsH"O!! Z ( hr²X²µµ™DCXZXPO!POXfuufuuf {5{cwO!ODDODCO!    sff!fsODsOf OO±±fOO±±&&O  ssXOXO!f!+D+!!XZ( h1 ²™ODXDXXPDPXX'']ufu­­u]u­®45 {w5{!!DODCCOO!     Os!s²OssfOO ±ˆs±ˆO ssXXZZZ&XXZZO!sH+OO!XOxlhh²ODDDXPO!O! ]u­]u­u­ffuu® 2c{ 4w{!!OO!DDOO!!     OO ff ffOff O±ˆff ±ˆ ff WZWZff + " xh ²²ODXXP uff­u]]uc cc w w{cO!OOODCDO   f0f0ff0f0ZZf0!H" ²D:ZOOPDOfu]]]ufc®w{{cOXOX      f  !H XX f]ff]f     +  ˆˆYYb2bb      ˆˆO¤,b2bb2FKˆY::jjddµµµb2q2bq    KKIFFK  AAL}©SS¯¯:,222b222b2bLLFF,OOˆjˆYˆYW:>ªNHk°jjjd´=bq22   KLF!   ALš©SS¤Z,:b2bAqbbbKL[KKO,!!OOOˆjjOOˆYˆYUWª>NHkkjXOd52b222bbq  IKdFLKœ  ALµ¤S¤¯% :sqsb2sqb22bKL[FKKdœ!OœODˆjˆOYˆjYˆUU 2 NH°Xjjˆj´d=52bqb2   KLLLddLK,!,y  A5µ¯ W/Zqq¸bf3b2bFI[FKKLd,!O,jOOˆˆˆ||YjYˆ,UW#ª>UdTAµµ(AAX!|jO´d=542b2bq2 FKdKd!,!,,yy%% 525¤¯ H/X/:qq£320sb22bLF[IIL’,O,O,,ˆODˆjjUj|S±ˆjjYˆ:,XW:: >2NKKN:AH ޵1((0?0XXXO±j2´dŽ122bbqbq2b%  dKFKKd,œ,œy %% %2¬5µ¤ FTW)::*,qb0sbq22bLKLLddI,œ,,œjYXjUˆYˆSDtËjYˆ(XUXW::y5ª>dIZÊN޵°1q6(}Y000?°ˆXjËj:2ddŽ71µbb2b2b%  KddF’F,Oœœ,Oyf%%% %2½ HÃ,):),qb2qqb2bbdLLd’KIœ,Oˆj±|OUUˆ(OjjˆU:XW:::W 5%2dI[ZIKNT] ÏÏZ°1µ1q((–26(²0 0?jjjjˆ dŽ7bqbbq ddKFdL’FF,,, O²²²–y5­y%%% % ¯;;;¬ ÃT:,:b2qbKKdLLd’dKIOœœ !!jXjUOˆOXjˆX:W:::W5yµ%0dILZËGÓ]u:  ÏZzqp˜6^° ^6 A² ??jjjjŽ&qbbb2qb2 %KÄdLdKF !™W²²²L:5-4 y.ZX  % ¯:;T:W/:£qbKddKILI’dK O!!jˆˆjjUUO±OXUj:::,,: 55yy%0HU}udH1Zzb˜——˜ € – 0²0 “°XŽq{bbb~%dKL’dO?™?™²L5y y–%  FF/U::2q£qbƒILKddK OO!OjjˆYˆ±XUj(::UW:,,j%c#y# OXO}}6?Z}rT]’ddÏk}}‰fzp——n^–76 A0qbbL’FO[Ldf4 f ­–%% ~   TÃ/Wbbb2bLdd’KO!O(ˆˆOYˆYjUj(WU:WW:W5(µc  y}:U}Êr]Tu’:H:k}‰p˜˜A00?0qL’dOO[KKd³f–y–}%% Tq{bbLLKd’K!ˆˆjjj:::W( xycy}}tr7’ ’’1‰000 LdO[KL³f–y––%  FqbLKd(jY:5cy­­yY 222ªÒ   K[[L y––% y­}:22WZjUWZW????}?????“kZWUUUWZZZ??????OOOf]ff]}r77 r}r rgggd????Î?“?ÏÏZ:/ZZOODUW OjBp222WZ}r}ZZ99Ž???Ž?bbbAAddOOOfff]f]r 7 r 7 7rgdggg²²??“=Î?“Ä/WX/WYf(OOODDDk,,UZ WWdd’UqqBÐÑ9}}77}9!C?Ž?Ž???Ž?Ž?Ž2b22bAdAAddODDDOf]}r .}7 dg’g’’gd™²²™??ÎÍ?ÍÏ/Ä/WYY(ffO}DOOCDDDkU,#Z UI’‡’dqqqBÐ9ˆ}}r}O!C-7?Ž?7=7Ž?2b2b  AdAddAdDODf]]]]]f}r7 . 7 7r’gggd?²²??Í?“Ä*/ZXZO66Y(Yff]]ffOODDOCDCDDˆ#@, OI’’F’’qq^bYˆ }99OCC-77ŽŽ?-=77=Ž2b2bb2 %  dAdAdOODDOCDDOf]]f}rr7 ... rdg’’g™²²™?ÍÄÄ*XZXO (67(Y(6YYf…ffOOOYYOCDDCDDCCD2Y#, A’‡qs5f9ˆ}r9!!O%DCCd--Ž??d-=7=bbbub2 dddDODODOODffffffr ... r( ’g’’g’d7(’d™²²[²qllÌlÄÆÄ*XDDO (667(]f]f Yk}ˆODDDDDCDOD2YY# ’ddsAfYr7!%DOD-x--d-x--uuu2bub2%ddd’ddddOOOOOffff(77. 7rrd7’g’(7’gg’²[[ql2ÌlÄÄÆÆÆJGD ]……f YˆUkYDOCDCCDCDODD2Y#Ad’A0b9ˆ} !OOCCdxwxddx--buu2ub Äd’’dD¶I7 7r77r(}(gdd’d²[[[LdL[qÌ]2]ÌJÆG (Yˆ}ˆYDDCCCOCDCDOYDDDb2OOˆˆ€# d¶ss0ˆˆr9!DOOOdxwxdd-x -x2bbuuu ’dd’’D7. 7r7}r7OL³³ddLLL²q2q2q2GÆKG((((DCDOYDCDOYDDODOD2b2222ˆˆˆjˆ}O²™²# HHs0ˆ}79DOODCdnxx´d-x x2{u2uu2~ d’Drr r(}7WO™qq2q2qGKGGK(ODDDOY7ODOOODD2sb2s22bjˆj/ˆjˆ²™€²™X"" H¶BssˆvrCCDDdxwnddnx­ x-dd}7rk²q22q2pKKGKGDDDDDjjDDODOOOD2sss2U2s2bbjˆˆYjˆYjjj™X"Hd3sYËËÇ(CDDDCCCDCdwx-nnxx-d’²q2pKGKGKjjOOOb2ss2b2bsjjjˆYˆjjˆX dsq±Çʈ((C²ODODODDDODd-x-nn--ddd}2qKKGKOOb22ZZjYˆjXXHddÇYˆˆ(9C²CCDDDODDd-´d--KKObZˆXZCCDDDff™™™ +!++SDD&&&&&&&ff0™¨™DD!+++!OSDS DDDffff[A0™™¨™CPDDDPC  D¨¨[[[DDP!!OS  P!DD |OOfufffuuf  ’f4ff++++!++OOOAA²²??™PDDSDCC DPPPC¨™¨[[[L[DD|SP²+!O !9! XXjjjOjXOOXXO 4 fffffuffuuu  = Bf44444++!!!§O§§[A²CP=??DDDDC  DCPDSP¨¨[[L[SSDD!O  &&!!!&XXDO9ˆXDjYj4 fuuuu­uf = d BKHBA44AAff4f+++!!+!!OD(§DOY[A[CDP™CC||P   DP|DC²¨¨[[LLPDCOO!& &O&PPDD&& XXO}(DY}X 44fuuuuf%2%KBAHBAAAfffAffff+++!!+!!+Z+D(©DY§[DDC?||P !DD²¨[[L[CPCO!P!!&&&PC!DO   (}Y9YkX 4fffuu­uff%2%KAKAA!!+++!(((OYDD©( [[DDP?™¨DDDP !C²[LL[CPP:,!O!!DO!    O YˆkUYkUY(44 uuuu­­­­­uff’ = »2ABBAKBAA4ff++++++ZZ+!O§©Yˆˆ}Yˆ}©(  A[AAPC™¨CPDPPDS  ²[L[PC+!O!OOP!O!& ODD&&!O  Y}O}kU}k}Ok}O h f­fu­­u­u2%%=y»yŒyAAHHB’Bf44f++Z++(7§(O©©}YO}YO AACCDPPt|O ²CCCC[,DOOPPD! 9& D&DD/O((OOX9XYfhf 4 uffuuffu ’d’y­­y22 BBddBd’’dAB4f+++!+!++++++(O(O§((©Y  LpffPDPYO ²²²CPPDDC¨PPDP+DDC!&O!!9! XY(XXf h4u h4hffufU mBAAKdddBA++!!!!++((((  CPCYI²CDPCPSD¨¨¨ DDCC!!OP!OO  &O!D!OXXXX 4  h  hffffff%NAAAAAAAA(   DDIOƒICCˆˆCPCO,, ODDD²ª²O+!OOSO!OO!!!&O(h4hhfffAAAAA((   YO}DrJIUYD²(WˆW(OZ GIG }O²D²›OrO&&OO fffAAA( DDIˆD2(ˆ ª²²OO›O &&ffAA(ª²! AA69¨IL[LLI++++++++++ffffff!!!!!PPPŠ O69,%,¨¨IL[[LI!+++**++!!+  !!OffffffffX!ª²|SPCŠg²O((96,,%¨¨§¨I[LI +*!!!+  OOffffOOOXX!!!!Oª²|SCCŠgfd"D²OO66996,,¨¨¨¨ILLG  +!!!!  !!Of4,O,$,XXO9X!!!!ªª²²PP||PCDŠgŠ-DOD ²²D!OO66996%,™¨¨§¨¨GIOIG  +!+!!+ !4 OO!OOf,,O,,,XjX!OXXX!!!!!Oªª²²|P|PCPCDŠ-ff ²²²DOOO696(6(966cc%,,™¨¨¨¨¨¨ILIOIGIIL  ++WW+!+*+WW4f!ODOOOOOOfffff,OD$$DjjXOXOX"!!dO!ªª²ª|S|PCPCCOP’gŠfOD ²²OOO9666(((9cc,%,™¨¨§¨¨IILI!LLI  ++WW!+O*+W !!ff !DDO!D!O!O  Off,,DDD,,DOj9XO!OXX"O44d²5ªªª|P±PCPPCPgŠfDD sssfs O!O—6669'6S96ccyc,,%,¨¨¨¨¨GIILOLIIG!  +++O !!f CDO!DDO.O! OO!ff$,ODO$,ODOjXODOjX"ODO""4OOO²²5 ²ª²±PSPPPC|d½ffD  fsssfO!OO½—(6996O9cyyyc,™§¨§ILO!LI!!  +!OO+ !! DDCCO!ODDOOOD!O4,ODO$$OXXODDCOj9XLODO"ddd!!O!²ªÉªªª||SPC!!!Gff   fssfO!O½—6((69((66y­c,,O¨¨I[[[OLI!WÄ!+++O+4 ff4 CCCC!O!!!!D!fAf,,OD$$!XXODDXXO!4d4!O!² ª² |PCODf’---f  0f²²sf4OOO½((66cc­y,¨¨¨OUOUO¨IL[LILI[I +*!++!!+!O!+44 4 !DCDDCOO!.!f4ff$,,OO,,XX!OOXXX""!O"4dx!!OO²ª5 ª( PP|CCAD|!dffg-  fffssfOOO6((69c™¨­§¨OO¨¨ILLL[LIILLIG+ !*,*+++!!+!**44ff4f  OCDDDOO!fff4,$,O,$$,!G!XjXOO!Xjj"#"!!"#"4dxOO²ª5 É( (±5CdŠ-d!LPf  4ssf4!69%,,™¨¨¨§¨§¨GILIIIGGIILIGG++!+ ! !,+*+!!L!!+**44ff44 OODOOC!!.!OO ffff,$$,,D,$$,GXj9X!XX!Xj9X"#"!!!"#"d44W5²²((|P5ddAfŠ--’PDUDf  ff OOD96996',,™¨¨µ§GGIIILGLIGIGG+*!* ++* +**+L!++*+*+44 !DD.!O ff$$,O$$,6O6j9XODXj9X"#"!"#"4dd4dW: ªrª(P 55dA)fŠ-OODf  f0DD'966'',,¨§¨µ¨GGGIGGJGGGI**+ !+!+ +**U!U+*+4f4fDDDD    f44f4,,,$,,,XXXXXXXL!L""Ä4ÄdW}²²ª²²( r=ddf-fD  O,¨¨¨IGGIII** +**+++ O  f"4ª²5=dfAA(AAXXLLf(.+AAAAAAAADXOXSO[[YYXYX[[L//IIIIIB²BHHHHHYDDDDD4f 4f44KFdKAHdd.(.(.((AAAAAA(!! GDXZYZZX!O6ˆ6ˆ[[LYXXYZ[[[L[KXUXUUW//W//GGGGGƒAƒNMHNMt6OYˆYˆYˆˆ f 4HKFFF!KKKAAd¶d..((AAdA++A GM½G!G((ˆZXW!!ˆˆ6L[K[YXZYXZ[LLKKL[[LKXUWUWW/W/IFGJGJ²AªAHHNMžžHMOYOYDY}ˆY} ff4f4  IFIHFFFL!dKdd++d´....Ad(ddAAO  GG!!MMG!!G*@/*XDZXWS!O!6ˆ67[L[XXOXXXZXLL[[LKX,XXUWUWWW/X/IGGKFJGJ²1²²HNMNMžÄžNMYYOtOY6DˆˆˆYˆkkYY f44ff4 4f IF+LIFFOAAIHd+’J´dA..(A+(dAAO !!G!!MM!G(ˆ((@****@*YYDXSSOˆ7 ~6[LYXOOOYXZZXYX[LLK,,UXUWXXXUW/W/XZ/FJKFGKIIGJ²ªHNMHžžHHNMžžtYOOYYˆˆO6ˆ}YYYUWˆkkY f4f ¶fW+WIIFL[IKFKA!IIHG´J¶’´+dd....AA+*dAA! G!GGMMGG!ˆ(9(/*CCCOˆ~ ˆ6[L[[OYZZYX,,X,UWUUUUUXW/W/ZWW////KJKIKGKKKK²ªNMNMMHHNHMMžÄžOYtYOtYYtY}OOkk}WUOkk  f+F+LLHK[L[KFKO"H!I!Kdd´d’¶+d(dd((.......(Add*! GMMM½!('*CDX!S6ˆ6 ~[[[[XYYXXZLX,,,,WUXXWXXZZUW/ZZWWW/W//KGKKFKJJKGGKJJ²²1NNNMMNMžžHNNžMOtOOYYtOOtOYO}ˆkk}YOkk}k:kf 4 IILKMIHIIIFHOKKI!H+d¶¶½dA(...((A!G!GGMM!½Gˆ(9(**DCOXC~6~~66[[[LXYOYZXX,,,WUXWUXXZUU//WW/W/W//KFFGGJIGKGJIJ²1NNNHNMÄžHFFˆ6OOOˆˆYYˆˆYkkOˆWkYYˆYkk  ILHJFJJMKHIO"A!Kddd´’¶’´dAA..A(AddAOO!O !GMGGMMGGGM½G((*@DOX ~ˆ~6[[[[OOXZXLXXXWWWXWWXWWUZWWXWW///KKJJKKJJGKKJJ²ª²²HMHKMNžžMMFˆD6tYD6OˆD6YOkWUYkk}kkOY4ƒLIHFIFFJILIO"+AH´+d´’´dH(d...AA+(A*AOO! !M½!!MMGˆ'**/DO6ˆˆ~6LYXXLX,XUXWXWWXWUZWXXZW/KKJKKGJGGG²²HHNHMMMMNMMMˆYOOˆ6ˆ6OˆˆOYOkk}YYOUWY}ˆ   „ ILLKKKKHKO+d+d´¶´d....Addd*A(O  GG!!GGMMG(9*DOOˆ6LOOYXL[[XXXUUW/XWWZZ//ZXXWKGGIGGGG²ª²NNHMNNNMMYOOOOOYYD6YOYO}}}ˆ}Ykk}kk ¶  ƒ LKFF AAA´d+dH.A(AAAOOOO  !G!GG(*Sˆ66LL[OYXX[[L[XXXUXUWZXUW/W//W/KGGJGJGGGKG²NžžžžžžMžžžYtYˆttˆ6OˆˆkUkkk}}ˆ}k:k   Mž/AAAAAAA.(+AAA!OOO  !G(ˆ[LL[OY[[K[XUWWUWWWW/,///XW/KGJIJIJGJJ²NMMÄĞĞMžÄž6OYtˆODˆDtˆD6kWkU:kkkkkk¶ zzpMMž*/AAAAA66OYLK,XXWUUWWW////KKJGJG1NMMMžMž6YˆDY6tˆ6ˆ6YˆkkWUWUƒ 44ppdFpMJJ*/A6OYY,X/WK²ªMY6ˆˆkkkk  z[L!LL!!   1311133pp44&.&..&&.&.&&.&Y(2X[LALPDDPD!LLCCPL!LOD}Y     ttDtt!  UWZZ 1311334pppp4&...&..&&..YYOSY(OY22222X6[LA[ALdDDDD!CPD!O}YDO}YD" "  " tttt!t!f-%9% 44 hhUZW 133s1133sps&.&&&..&((ODDOOSSSOI2cac22222O(OX6A[LAdPDDPDOLPODP}YOY      tDtt!tt9%%% hZssssss&&9&&~~ ²²v((SPPDSDDDSLIII222acaac2aa2O(6DX6[ALAddDODO!CPOO}YD   ~~tDDttttf---tSf--jX99%h Z !sssssss&&& ~&& vrr((vrYODCPDDPDOYLILILIKI22acahhcachha2((YˆˆX,,6LLAAdDOOOO!CCO}}D " "  " ~~~t!tDtSXX!999h(ZW!sssszss&   ²%r7rrv(rrv((ODPCCPPCPDOSOLI[IKH+KI2acahhhhh{{hha2OOYYkkˆOXXX,/XDL[!LAPDOOL!PL!O}DYO         D~!!DDf--Stf-Xj!!! 9ZZZ!!13s11szzzss ²²&&&v7777rr77v((((YDCCCCPDDOYLLILIKKHHKIcah{{{{{{hac2(YYˆkD6XXXXODDL!!D!"!"LLOœU}D"    D!!tCCD!!j9X! !1313zssszs &.&& &&%%%rr7 777 rrrvv((ddODCCPSO(L[LIIIIK+F22cah{{{{hhc22((6OYYOYO(XXXXDX,ZDDYL[LAOPDDDDD !!LPL!!YD}  "   CCCOttt!CCCD!!ttjj !XZ(  sssssszzss  &&..&& ²²&&&&%%%&vv777 777rvdd(OPPCCCPDY6(I[LILIKH+FKKI22ahh{{{{hhha(Y6PDOD(OXP!+/[L[Ld[LLDODOLLL!PDCC6}O/O}D"   " ~CCDDDtt!CCt!SSSt9XO 4(W(W(Z(!!zzsss{zss  & ²&&&%%&vr77 77rr(d(YOCPPOY~(ILLILIIKHF+FKKahhhhh{haahhc(6OPOY((PO+,XX[LAdLAAAdDDDPODDDO O CCC!C!!CPP(6YD}}Y " ""~DtOtO~!!tDtCDttttSStt9fO!!WWW(Z(!!!s4ssssz{{p4p &&   ²(vr7777rrr(d((6~YDOY(66([LIIIK+FK+KKI22caahhacaca(O(D|DOZ,/XX[AdA OPOPDDO6(O!!!P!!CO!~/}Y/OO "" ""    OtO~t!tDtDCD!t!!tt!fStSjX- ! jX%%h4 ZWWWZW(W/!444sss{{{444p& &...&²(rrrrv(((6(6(((LIIKH+JKIKKI2c2aha22ca(CCDOZ,/X d OOO6O!!P!///}~D"" " !tOOt!tDCD,tt!tt!,ftS  jjf  %%9!jjX9%%h 4…(WWWU/(!ss4z4zsss4p{4{p44 &.&  }O}²(vv(6((II[I+HHKIIaa2ac2a(Z//XÄPrD6( /O }P/6~/OO  "  O ~~tCD,!t,f-fj-  %9j%%h…((Z((U/sz111zssz4ppzpsp44&%  &&&  ²²(((IIIIahchCXD(6(/!PO6/6}œ   O~tDDttjj!%X4h44(W(WUssss33sssssp44s444ss44& /W:  ²²((caaa6P~}Y ""~~D4Usssss44s²(!+CCDDD,,+!DDDOOSOCD~(W,DO,OKJ+!CDOSSOD   CDD&ŽZWW,OWI,O,K+KJ+!!DDCOO SPSDPODD  CCDDq&(~(7(¨99'',DOUW,,,LL,,O++U+!DCOOOO CPSWYWPPSPPDSPOODDDD   DCDDq(~Ž Ž Ž¨''ZZDODOW,,+G%,,+K!+!DODOOO CSCDPDDSSYPPPPPPSDPPDOODDD    DCCCDCq55››šŽššŽ7¨99,,OU ,,r+r,,,K+K+J&! COO CSSCSSYYPPSPDSSOSYSPPPPPPPSSPPP[DOODDDD    DCCDCCC›4…11qš¼¼¼Ž7(¨ ,,,W ,, ,%%,,%%, W!++&&&" DSO XPYYPDSYPDSYOPSC(PDDS²[LDOODD CDDDODCCq11121›qŽŽ€¼7&7¼™¨ 999 ,,, ,%,,,,, +&&& SSSYSYWDSDPSPPPPPSPPY(DDSY[LKDDODODDCDODOCCCD›qq11221q7޼¼7&¼(ŽŽ(¨ ,W ,,  ,,,   ++&&&& (SOPCSPPIPPSPPYPPSY((O}DDY²[LKdDDDOOODD   CDDCCOCCCCq224…15›¼¼7š&~7ލ¨ 9 9   ,,  r GG+G++&&& (%OWYPPPDSPƒPSSDPPPDYW((((PP²²[[LK³DODOOOD   DCCDDOCCCC›q2115 5qŽ6(r~&~Ž(D¨DODDOYYOLGLG      %% /~LLLLGGKK++J.& O%SYPDIƒPPSSPDDPYWPPDD((((P²™²Ld³DOO  DCCCDCCDOC›554 …4›Ž7Žr(7&7(YDO¨ˆDYDUOUk:W:LƒLL   ,  % +ILLLLKK+JJ&& SOOOS%XSSSPPDDPDDDD(PW™²DDDOODCCDDCD›554 57(6&.~~§ODOUYUL   ,   +LL+++J&. SSSS%UOYWPDDDDD(P™™??DDDDCDDCDq55Ž(6.~((¨Y    ILLL+++JJJ SO( ²DCCDDCOŽ66¨  I+JJJJ  D(66UUWU6U6UU6LL!!!!!!!!(((fffffkUUUWkUWWWUUkk(f4y44ff(696UWWWWWUU6f!L!L+!!!!!!!CStYtSfffffffffkUW///:/WUUUkkff4yy444fy44ff(6U(6U////U66(fOL!I!!C!+!!CStSCff fff fffDD(kW/::/WWUUkk4f4fff444ffff(6UrUW///WU6(54ff![!!!C!!+!!SSC f yffDOO(UW/://WUk((4f4ff44444fy4(ffffffO((UW666UUWW//WU((ff5!!L++D!D!CCy yODDOUWW/::/WU(((((44f44fff4y4ffdf((ff˜˜ffffO996UU66rUWWUWWUrU6f4yf![!SD!+SPP  y…yYODDY(W/::/WUkk((y44ffff4yd´d((ff˜4ff4fffOU66U6U6966UUUUWUr(r6(f55f!![|!+X+!!CPPCCyyy…yDˆYDˆW:::/WU(U(444ffff4yy4d´´df(f˜fW6U6969((6U((WWU6(UU6ff54445fO!!!X!!+!CCCCyOˆODYˆWW://:/WUUUky44yff4fyD*ˆd´´dd(f˜4ffffO96UUUW(((6(r6(UWWUUrrUr544yfffO!++ OYOYˆOU//W/:/WkU/WWkff4fffff4O!DODKXK!!)YAAdf((f˜4fff4ff66UWWUUW(6U((WUU6UU(6(f44f445ffffO!L!+!! ˆˆOˆˆYOkW/WW//WU////W(fy4fff44f!!Dj jJFGK!*O!*!YZWWZdAAAAdffff(((f˜f˜ff((UU66UUUU6rU6U(UUr6ffff4yy445fO[[[!X+!CPSSPPCCy y OkUˆk6((U//W////WWWWW(fyf4y4  K GJKZZ!ZZ) gd´dd´dff((ff˜f4ff44f(U6U69966(Ur6Ur6666U6f44b55b4ffffO[[!!+++CPPCCC4 4yYDO}k}(((U//WWW/WWUkUWk(ffyy4yyyf  GKG*)W-g -dddddddff˜4f4fffWUUWU6(((66U6UU6U(6Ur6f5y4ffy5ffO![!+CCC y …yOkOOˆUDkUW/WWWW((kWUkffy4f4y4f @W-gdd´´d´(fff4ffUU6((rU(6Ur6(6(Uf555fff545fL!!!++yYUYOOU}DkkUWkUUWU((kUkffyy4f4yf -gddddf(f6666U66ffffO++++ YkYˆk}ˆ(kkU(kUWk(kfyyyyfyyf f˜(6((!+ˆUkˆk(U(U(kffyyff22q22 [[LL[dYY}}}}}YOZZ// yy}}ZZ(O(OS((}(}((!–yyyy((222q2   [[[L[dKY}ˆˆYttY}}}OXZ// = »yy¼ ((XZ[[   Okk}Z;ª  (}ˆ((}(!!IIII–yy–y­f.OOO((YY(C}2qbqq2b,  [[LL[[LKdd}ˆYtD}}Z/= %  ’(((X$WXZ[L[[[[[ Ukk}OˆkW;;;² }ˆ(ˆˆ}(LIILIIA–y–f–­–A–9OOOO(ˆY((OOˆˆr(D(6}Yr(6}YY(}}2q2b22quO,   [LLKILKd’d}YODD}ˆˆZXX//:»% ¼}’%%%((kX$WXZWZFL[[LK[d  (k}}}ˆˆkUˆZW²;ª;²²   }ˆ}(Yˆ}IIL™²²IdAA–(y­––f xfx fOD(ˆDOY((ˆ(rrOrk~((DD(6~~YOr~}~YY~}~%qq2b2q,O!   [LLIKLId’dXSˆYYODˆˆjXZ/X//:: y@?ÇŽ¯1?%%XZXW$XW(WZ[FF[LFF[dd (kˆO}kUZWW²²²ªª;²  O}OOk}}OO(Y}LIf??²LIfAf– f­yf–ffxffxf(D(O(D(YOO(ˆ6~~O~k~r(CCD}}}~O~ˆ}~Oˆ}}r(q%b2q,œ %% [KLLKILIK’’dSYˆYYYYjW/XZ/:?­y“G¯?%%%%Z:XXUZ$W((W[[[LLFK[d O(k}ˆ}}kWWˆ²ª²ª;~  }ˆDD$k$DYˆ}LI³WjZIfI²AffAAf–fy9fxx{{x f xf((r~OYDDY~(YO(6ˆ~Oˆkˆ~rDDDCrk/k}ˆ}}OO}U~(2bqqquq,,,!  [KLLLLLId’’dYODYˆˆYYYjUjW/ZW/XXW//WZ, "y°10¯11??%ZXZXkZW((ZW[L[K[L[[Lddd  O(kZWWkˆ²;Fª²ª ª    UYDk}UYDOY}}Id²j™²Ld³[?Aff –y–y(}f xf x{{fOOYYˆˆ}Yˆ}Y((6rYOˆYOkˆ~r(DDCDC66~}}Y}kU}ˆUkY~%%2b2qu2q O,/O % [IKI[[Id’’’KYjOYDYYYYjUj/UWZ/:%W//,,,%j2=4}µ°1µ11›¯199%WX$XUkZZWZ[[[LL[L[[[L dd   Okk}ZWWˆˆ²;ÆF²ªª²$  UYODD}YOYˆ}([IIL[²If²f–Af y­–­–(ff f xf(/O~OkkUkO}YO6ˆˆˆ~ˆˆkˆUYY~r6CCDCDO(rˆYYYˆˆk}ˆk}Yr(%22qqz22,,/,O! ~%[IKL[ddd’dOWYYODDYjUjjDD,/XZ/:W/Z,ZZ =5"¼Åddµ1›¯²¯d9%ZWkZWWWZ([k[[[[LF[[ dd OOkWWWZZˆª;K²ª;² $ XkˆYOO}YYˆ}}!D[[L²dI?fy:–­–fyDY(Offf{f(Or6rYYY~rrY(r/kˆkUUkˆ~OY~rCOCDO(ˆkWˆOY}ˆkk}}~r62qu›qzD, œO! LLL[Idd’’KL(YYYOjUjjDDW:XX%/:ZZ/:,XZ »24Å’A¯²dý1XZXW$ZW$ZZ((F[[L[[LF[[[d  dSOZWWZZWˆª;²²   $  XˆYYOYYˆ}}(k[[[[²²fy–f yAy­– . fffx(r(6~rr~Orˆ~ˆˆˆˆ~r~~Y~(DOCDO6~ˆ~Ykˆ~~~rˆ}~%2q…b2uq2D,,OO III[Idd’’dLKYOYjUUjYD(,UX'/W//:Zyyf#f °HHdA²d½½d1%%ZWXU$WW$W(KFL[[LF[[ddˆkˆZWWZkWˆO²;ª;²²    (}ˆYYYˆYY}}X!O[[L²™²?y­f–yA–...ff(6(r(~r6~~~r6r~~r(O}DDDO(r~YWˆ~rr~6~}ˆrq››bbO!œ O!  LK[Kd’’dLLOYOOUUUjY((U/Z/W/:,W4­y#f HÄQ1d½T½dXWZUkWZ$W[LFF[ d(U/ˆZWZSOZZ(²;ª;²    (X}kk}ˆYˆ}OXX[[L²[™f­ y­  YDDYf(6((66~r66~r(OCDO§¨6Y}ˆ~ˆr6~}ˆ62q›qqqbOO!!!O NKLIId’’dLKVLYYjUjjOY(/WZ/:W:,:=f ­@NÃHdd½ÃTdZ(ZWkkZZWW$ZWZ[LF[ kkZWZSOOWZ(²²;ªª²››²   (XOOOkˆ}YX![[[²­ ––AA . (r(r6(r6(6(6r~66rˆ~bb2q›qbq›DO!O NƒKId’’KOYjUjOYZ/W::/,:5»y%N9½½Td(XZZZWW$WZZ[[[žF[ (kWZWk}(²²²›²  (XXOOOOYXDO![[[™–yyy–y–A  D666(6(r6r~2{b2›qqqbqqOODOO ANLd’’LKjUjO:/,d2d y­%½Td(XW$([[LFF[ WZkkkk}((²²²² (X(OYXX((Dy–y–A  YDDY((6622›qbuqOODO!  KALj,dd2» y TdW$ WW}}²²²  ˆX(DA  f]X,,,,,,,ˆYYˆYˆ&!!  f]f] XX ,//,YYˆYˆ & %6(+!+!* X+++0000AOO xffs]    X,/,/ˆYYk  O((((²OZ!,Z+  +++++( A0™0pp˜˜O xf]s]]    X/,/kYˆˆk   OD(6(²µOOOXOOO!"+""Z"*/X+ 4+f"  o +XL++!  ™05p˜Bl˜!OO  xs]f] ¶ X ,//(kYˆ 6Yk  OOOW(((WU²™OOOOOO  +!XXZ+XZ+ZX,++4 "+h " f+o  XIKHG++!+++ (¨A0Bpp˜pp˜n˜!d1›1› D x s]f]     XX@,/,@~k  6  !UW6(WW²™DODXOOXO !!!!X'+X""++,Z/Z+*++44"h44 " w w f  KF"G!+*$ A0™1500˜lnnll˜ln!!d"dddd 5151 O f]f]  —½  X@,,,@~ 6~%%DD!W/W6WW(U/W²µXDXXO . X+''++""Z"X""Z,*X+4 4h 4"hh"+ff w +" ++G"GX*$+$*+    ™A05A0˜l˜nl˜pl!!!d""dd555–DD f]] —   ,@,,,,~' 66~~%%%DO!//WW/W66W/W²™²™OXXXXXOXX  !+X+X+++""/ZZ,"",+/Z/h" " h y f f"wo o HGK+GGXG+** ( ™00™s˜˜˜ppnp˜!+ddd" 54%OO4 ss  ——  ,%,,@,//YˆYkkkYˆˆ%%%%DO!!!W//UWW/UW//Wµ²µ™XXXX .  XXZX++"Z+,+"/Z,44""hh"f"w wow x  KGGGG+KG$++! ™A0s0¨0ppppll˜!!d 44 f]]sffsff  —— — X ,/,,///@ˆˆ~kY6 kk'Yk% %%O!!O!O(//UUUUUUU//WW™²µO.!+""Z","/+Z+  u +- n"  IHG"GLKG"G!*$( ( A15™™A5l˜˜nll˜d""dO  4  x f sf]f]]]]]f  —— — X  , @/,,,//,œ(6''k k~'~6% %%D!O!(/WU(UW(UW/WS²™  'XX+""Z+ X/X 4+ +no  KG"IKHG""*++*   ( 05¨A0A11pppdp˜˜!O  4x% f]f]]]  .½+  XX X,œœ/,,/,@œ6''~Y~'~% %O!!OWU(WWWWW(}S™² . X+ I"GIKHGG+$ (  (0™5115˜ll˜d"O     ]  ] ½  j,%œœ@//,œœœ(~'6ˆY(~6 &!OUW(WUU(WWU™²™™ !+XZ++ KKH*$  A15˜˜dd x   ]    ½. % ,  %@//œœ~~(6ˆk((~6 %&&&W(WU™$05 %     %,%@@//@6~(kk(~&W/%O(UW(UU(²    %%@@66???=???$????™šŽš??((((6((?fO?42?0??!O?ss?0?=24=?42?0??2O?f20?0?5$ZW$?W$$O?42,0??22??42?0??!ODDD&&AfKK!+5™™™™?™™™OŽšššš?€22?OY(kY6~~kY(XXXXOPDPD?42?0??!ffofOODDD2y44?122??ss44?144=2y42?12 2?2f44?24?0445554?44??Z––5$5– $2y44,122?2y44?122??244?!O&&????KKA?????+*445554?44™?00™000™šš?š??€4452€?€€?((Yk~~~k~6(jXjODOXDDO2y44?122??Dfff˜˜????0??2?? y20 yssy=44=? y?4 ff20425(554544?455445?244? –'5 5?’y2, 2? y20 42y4??24?(????????????224?????0fK2442?54544?455?0™0?5š??4€?4š4yy455?522((6Y66~Y6(((OXXODX? y20(CDff˜n…˜ff?242?12 2?2 4?y20?4y2 =2yf5?24555545??5244?24y4???02 5'5d2½,?y?d& 4?y2(2 y?0 4(??42??555™™™™€šš???42??5y52422?2y4K02K?22?5545??55?0™0ŽŽŽ244?€?4š(4y44?224((6YYY6YYY(OOOXDDOX2 4?}CCCff˜…˜f˜ffffff˜? y?4 2? 4=ys=4y? 4=00?425(00544?4 &?5454? 495ddd? ½?4’d???4?4(2 4?y26442?5452™?==?™€š'9(442?5452?4K- ABf???00??0™0Žš€222??2š(2y4?24((OO(6O(ODOXOOOX? 4?4(DDfff˜n…o˜f˜ff˜xfx˜2 ?2y2d´0&?=2s™y=s24f4d5254f202??4y22??02y4ZZ?2Z½’d244?2($$? 4?4$$(?2402y4222s?™?9''9(?2402y4????2?KAB?42??22?f??™0™Žš?24?š(45?2P|O(DDDDXDX??2(CC(fff˜f˜o…˜˜fffffw˜w? 4?2´0242ss2ff40?242405f4?0f42y2y40242?24(W22’?24696??269$6?42?2222ss22ss=9''?42?222?224?22fK242?24?+X2f4?0??2f™š™((OPXOY|DOOOXO(ODDDO66ffff˜n…{n˜˜fff˜w?22d4420=4ssssy0=y 4s0?2sf4?04y20d2&&2’d44?42f44944y2?242(W45Z?Z422,?4’2220?6&6(6&654??2sss===y5?52??2y44AAKB24+++XX??424?42424?™00™?™š2™2?44(((242™(|±±/±O(±|X/k/XDOPX20?677ODDODO776ff˜4˜…{{…˜˜f˜fww˜24424242024y2s2s22=244y s s220222 s4ff44y0fd&’5d&d455yyf4(224??4250224?426~5–5–5W5–5Z~44224,024½2d?442026?20?(64224?422?'42s2244y54?224?422?4:KBK-AB+*??44455yy445?0?0™000™$42242š2444$(625™44?2(((6±(±(±jXjXjDXOOOXOX20(6(DDOD66(fn4…o…{{…n……˜fffw’˜wf04420?24224 0=4yy&2s24=44yy s4 0?4y fy4&4fd4’dd4&&4402542204245?244(5– 0Z5s–W(22½44 Z?4y2&d2 0?4222?6&242024&6(44245?242'$=42s522455y?245?242y?BA-LAf+?24244242?5??0™05?™f$šš4242šš242šš$ y244Ž2“Ž(5y |±((±XXDXX4 0?42?6DD((f˜n{˜…nn…˜{nffxf˜wfxf0d24242024222?y&22=2y& (& 2=:2(f444.4d’dd’d504424y222?44y 655Zs(6~~62&2’2,Z2&d?2??424?(&4?0?4y&24y222?20449$$ =sss5554yyy222?2yKKBAkB??““?5?Ž“??55™™s$š$š2š’Ž4š$$ 4y“?™“2yy ±(~OXPZ?24422?(?(D(fnn…n˜˜…{˜ffnnnff˜ff?d´s24 ?4y4&s. &('6:&%66fff4fd’ddy42224y44yy ((f(~(&d4,&Zd’X&&4?&2? 44424y4?24y4296' =2ss424y45y4?24BA?“?ŽŽ?y4?““Ž“?ff$$šŽ$ 24™“9§?( k~D XXZ 4677D(f˜f˜f˜…{f˜f˜f“´d?42?D?42&s&(9( :'69'6(fdd242y42(5(6d’dd2//ddd2X?2..242?y5..(96?=4%994245?2?AA??““?????“??0fŽŽ6((?/9/?(( 6~k(/r//2}Of˜f˜ff˜˜fff˜f?“´?02DO24 s (( 69((d444y4– ((dd2,dd4 ?D~D4.4.6(??4%9245?2KK??????????0 š(6((““((((~6/ /XX/ /4 DOffffffff0??02//26(((d2444y4(24224y4AA???ššŽ((6/f0?024 4!.&..&.&5X!!O!!O!ODO!f!!OPPPPPOSSSPSS&.&O++!+D!D“““““    55yOOX!!LLLAD!!XZXf!(OPPOOSPS%9&OO++++*¨§¨¨§§¨!O?“??O ( ([554455OXXLBI0000022((6XZWZX((SOZWZW:WZZ??D-(((5555y{555DDLILIZXZZWW/!DD5544{y4555 DDLLAL[LX:WZ!+¨§¨!DPODO(Y55545{{4545  DDLLBLLI,,XDO,!!DDDOO-D55454y{{45455DD$XALLI(00XOD!!§§!"!!PDO--DO554544{{44455 A[Z[00&&00WZOW,!O++++!§§!!!!!!PPD€D  –Y5y{{4y{{y4{{y5 $A$Z[02((((XZZ!DO++*+¨¨0!!!DPO]DD-{y4y{{{{{4y{5  AABII&DDDD((XX(O(ZZ+O!!++Af0DSDDO (Y544yy{y4555   OOOO99AAB&III.DDDD(Z sZ((O(OO(%..XX]+++fAf!1Ž“ŽD -YY5544{{45455OOOO9&&9&&&/&.f ffO&&Y  (.D&&.+!ffff““DdB–5454{{5455XXX&$& fXXX.D.h!!f sff““5§“Â^^554555jXDjXX$$ YXD~DD.D~D “?h5h“^5555UjUDOD!““5.&..&.&’’’B99f9O&.&OOOO!+ff’’’’½’““““““ZZXZXZ BBB99999&9!O!!O!P!!Of999DDODDD%9&OOOOO++!O!!O!000ff’’??OOOOO??ZXXXZ  OOOˆPPCCDPBBOOSODDOOOOSODOOOOO!+OOXO!OO!!ODOPDOPOOOO!OO!OOSODPPDOOOOSODDOOOOSODOOCDCDODDOODD%DOOO&&DDCDD05O!O!OOO!OO!!OOSODOO’’DXPCCODOPBBB BOPPOOODDDOO!OOODDOO+!O!!++OODOOOXOXjXOOO!O!DDP!O!OOODO+OODDPDO!OOODDDOO!OOODDOCDCODDDOODODOOOO!!DDCCCDOD0DODODODOO!OOO!OOODDOOd’’½d’!!OX (PPCCOBBB-BBBBOOPPDOSDO!ODOSDO+!++OO!!OO!ODOO!!O!+DOSDO!ODOSDO!ODOSDCCDODOOOODDDODOOO!O!O!DODCDCD5DDDOD5OD9ODXZWDOSDO’’dd’d!OPPXXDDPDPBBB -BBB!O!OOOtOOtO+OO!XXOD!OOODO!OOOODODOOOtODOtCCDDODODODODOOO!O!DOCCODODOOOOOOOD!OOOt’dA’Ld!O!OPDO ODODBBBB BBBBO!O!OOOOO+.DO!OOOOXjXO!D!OD!OODO+ODOODCCOOODOOCCDCODODDDODDDDDDDA00O9OO!OOO!D!OD!DDOOddAL’’ODODOPODO DBB-BB- -BBB-BO!PO!DOO!OO+OO!O&&XD!ODDO!DDDPDODODDOZDODC,:DDDOOOO!ODD5DO5OOOO!+++D!ODD!0DPdLd#½½ODODPSOOOOOO!PDDPDBB- B -B -B±DDDODOOD±DDDO!!+OOXjO!ODDOODDODDD.OODDOPDDOODOD±DDDOD±DDDDCCDCDDODDODDO!!!DDDDOCDOO9DOP+**O0fOPP±DDDd’’’"dd!ODO“O?OOPPYDD  OD!!ODO!O!B B   B- B '!OOODDO9DDO9ODDO9DDOO!9+!XODPDD!!OO!OfODPDODODDO9DDDODO9DDODDDCCCDODDDDODDOODOOO!OO!!O&DCCDDODCDOODODODX*+0DOfDDOOd"’A’’A’’""d!O!“9O!DPPPOOOOX  }(OˆOOOB B-  -BB   OO!OODODO(OO9OO(OO9O!(DOO!O.O+OPsOOO(ODO(OODDODCDDOODODD%DDDDDDO.!O!DD!.CDDOOOODOkOXO0OOfd½½Add¨½Ad""!D/9/D“DDPYWPPDDZXOOOXX  }(=B-BB  -BB-BBOOdOOD99Z&&&OO/.O!!+ ffDDYDOD CDODODOO&.!&!O.&CDDDDO fff0d"d½dd"“!“PDDPDDDDXOODOXZZX  5 BBB -BB-O--d99O9O99OD!OO9OjDCDDDOO&&O.& DXOOO0fd"dd"““PDDDDDZODXZXZ=}BB--BdO+OOOOCCOOO&DDddXZ==BBBOODOOODDXddBYY““ADOODDDDDDOPPPODDD&&ODDDODYDYOYYOYŽŽŽ“Ž“?DDDDDOODAODODDDDDODODDYODYDDYODOPOODOYDDODDOYDOOOPODDDYDOOODDODOOOOY(DOHADDDODDDDYODDDOYOYDOODODDYODODYDDODODDO(DDD+OODDDDDDOSODDODOO}DOAAHD(OODDDODD(DO(DDDO.(DOY69DOD(DOOODOYSOYO(?““DO(DOOH6r(k(DDDOODODOO(D(DOYODYO(O(DOODOOD(DO((D/D(DOD++(“DOY“ODOOXDDYDYSXOD(DDODPODDOOˆkA6U6UkDkkODYDDY((rD(&DO&(D((DDO(DYDDDY(DODYO(DOYODOODD/9D((YO(L*POY“6DrDYOOOXD(?DOYODODYDDODOOˆk 9O9AAHO9DU(r(k(DODOk(DODO((YD(((rD(DDOYD(DDO(DOD(DY(DDDODO(DDDODY(DODDDDDDD/DDO(((K[+D!O6rDDO“6“YDOXCCDD“?DOkPODDDDDO(DOYˆ9DOHA9DO(UrDkDDOD((((((ODDO((DODDDDD/&DOO69/9CDODO((K[+++“DO“(OXDXCDˆDDO“(kYDOODO((DD }DOYBDD9 U6D(kDk&&D((((((D&Y(DODOYDD(DDD(&DZPDDODDD...P69(DYDO(DkD((((OODXCSSYXY?D(k(YO kOO6DO(DDDOAA9 U6DD(OODD&(&kDO&&((&DD&DrDDYDrDOO(DO(DDOD(DO&Z&WPDOYDOOPDO&DODPPO6(DDO(O([K™²²DO“Dr(f0DD0f(6YYYDCSDYYXY?D“Ž““?k(OSDDDD(66DDODDO(DOYDA 6DDDk(YDOYDk(DOYDk(DDODDYDYOADOOD sOYDO(DOYDXWZXDYPOOOYDDDDOODD.DO(DO(²W™DY(f(0D0DO(f(~Y~OSCOWˆDXY““D“?k(DDOY(DYDYODDOYDY((X(D/OYDOOODY(9 f99(ZXDYOP  YPDDYO(.PPDDODOD(™“(kD OYk((6~XOYˆOXDD“9DO(k“6(DOD OODYO(kr/.kDDDUAUDO9(/(9(ÁXXrYPPkkOYP(DDDY6PDYO(/YO(0f((6XYˆYX?/9/Ok(DU}}O}DOZZDO(DOODk&OOD((O(OXDODDO6%6(WWD(6%PDDD}}DY(DO(Dr0((YˆYDD(OOYOYOYDYOYDYOYOYOY(6DD(6DDDDYYYO(““ffff!!OOOOO!O!ffOOOOOOOOOPPPPPOOO0fff!+DDDDDŽŽŽ“Ž“?44f  fff!4OO!+O+44OODDOPPO|SO%9f0005˜f++*OD ““Ž?O4ff (}ffff|OO4f4f44f4f4f4SO%%ff4˜f++4ff4f?““4DOffffffffffOff ff4f4f4499fffff4f4?““DDU(4fffffff"OOOf4f444f4f4ZXffyy4f4(499ff4ff4005˜ f+ff44ff4(( DU(4fffffff#"OOf4f4f4f4fff44ff4f4(6(4ff4f4445f04˜ A˜fff!ff4sf??f4(( DDfU(ff4fffffffff##OOf4f4ff4f44fZ,ff4%fff46f4494455000f""Aff++!4f4ff“?(““(DPPf4OO4ff4¨fx4fxffff###fff4&((f444f4fff(9''(C/5,5fA"f++++4??fsff“““(DPDZD4f Y §f44ffff"##"f&(.fffdd4fffff4((ff4f4(f.'''f˜ff +* “?fff4“f4“DDDDff Zf}f§(ffff44ff’f4f&&f4(.f4fDDd4f44f4y4ff6fdffffff('9f˜f˜f"fIf+ ?““??sfs4 sf4²?“Ž“D44Z4( f4(fffff4xf#ff4444f4d4f4 sy(6(f4’dOf94949(f44f˜˜4?fs4²A²²²“f“4D4 4(f44f¨ (ffff4xffnxn˜f  f/4. f6d’fY44  f44(4f f??f ffW²f“94 f } ((ffff4xf˜n˜f Z/.444(Od44..4 f˜0f ²$²²A?/9/ r (}(ffffff˜f˜ff©©444f4 f4f4f44f44f44(f4fff4$f44f4(fffff444 4f4W“  5OOOOOOO!O!OOOOOOPPPPPO  O!+Z:XWZXZWXWZjUWjjUŽŽŽ“Ž“?D  ˆ(554 J G JOOOO!+O+  DDOPPO %9OO++*XXXYYZZYjU““Ž?D //W//W (ˆ   554455GJGGJ       OOO:%% ++XXXYjWjY?““W/WUWW:( 555554555IGGG     fWW:99.Z,/   s  jUjYWWjY?““OOUUZj/ 554454555IG       W:/WW::W     P99  O+jZ/  YUYjjU@jjY DODUZjZjˆ  555554554 G   W/    Z Z         PP         O+ !+ZW    jYYUUjYWj  DkWOjZWW   555554 454455     W W        : OPO    9    + ! +ZWWW  jY/“ YUjWZjUWW/   ( 55555545  W:W,W:W && ZW:Z W::Z  ZW:W W:W   W j:W%W:WC/  +++WZZ:  sjYj WY?“DYjWWZUUWU/: UWXˆ  544y454555U/j  ..  ZZ GfZZ::   W Z%...ZX +*X sXXZfjY WYUY“ OD D/WUjWjjUW  } 4y5 4555    . DD     W:WfZ ZWPDP  W      +Z XYfX:sUjYUW jZ?“Ž“ ODUjW//jU/    554–4 5555    IG          WZ s:WZ:WO jZWW:9.   X XYYsZWZUY Z§@W ““ “ DDUUW:ZU   (  5–y55555GG GJ  /  . fW: Y    W 9 9.  ...   ffUU/W“9 “DOWjW:W 5 555yy555J DDJOZ/.     O O(:   ..   0fWW@?/9/? DWW=r555555UU     W:   ˆD~D    D~D UZ““ W/=5555O W(DOODUj5OOO?BOOOOOODOOOOOOPPPPPOSSSPSS!+DOO,?““??“?OOO(BFBO!!OOO!!OOO!!OODODODOOO!O!!OO!OOOD!OODDOOPPOOSPS%+9[[[!!OOK+*DODOO![OO,d?dIL*O’HOOHˆ(L[FFF˜B!!!!DDdKODKd!!+!+OO!!!+!+!!O!!!!DKdDCDD|ODOL!!LL!SOSS+*!+![L[[K[[LD!DdIdIBL!!!!+LILI’d’LIF*d’d’HL ILIBFN˜B!!!!OKdD!dK+++!OO+!+!+O!!ODdKCCKK|D|OL+L!O!*!L!+*L[[[[[LLD!D[D!0HIIKLI+LK[[IL,d’dLIFd’A’A!d K[[ILBFN˜NNFBBB••!KdDKd!LL!+!+!!+!!!CLKCKdDDODOL!L!+!!++!L!+*LLL[[[LL[[!D[[!dLKKLd5DD/+LLLILKO,d’d’I[LIODH’H’LH(I[[BBN˜˜B-˜BFB†BmdL !!DKIdKKLKdK!Kd!!+I!!!CCdLdDODOL+!L+!!!+!L!+LLL[[[LL[D[!LdHHddOD*LKG+LKdKL[ILO#Oddd’ILIFIODODDdHdALˆL[[BBBN˜n˜FFNNBB†•B’dLLKFL!!!IKdLKL!KdKdKd!KKK!LI+!!K!DCL/L||OL!!!!LL!L[LL[L[[[[LLIdA00O!+BGKKddffILL[GI[[IOODDHdA!(LBBNNNAABNFNN˜B•BmB†•’dOLLKKFLDL/LD/LLKLddKd&&KKIdK!LL!!KKKKKLODKD[PdfD!LLL,[[LL[I05O!OKB+++KIdKf[IKfL[[[IO“?d[IKFSPPIL(L!K[BNNNN˜Bn´NNn˜Bm•†•BOdKKddKKdDdKOdDKIdK!+I!I+KdLLLLK[L!KdLDOKdK!PPDDL!!L!KLLLK..[L[OOX!+*f[LKDf/IGLLI,“dKKF“Fdd?“SSPƒDDOOO[L!L!+HdL[LBBNn—A˜Bn-—NBBBm•OLKKLOKIddKIddKDdDODD!+!+KLILdKdK!LL!KLD!|KdOPPDPK!!KL+!+K%LLHLHLHNIKH[LI[[[[O!D+sLLs,IGGIOCLI“+“d“PSSPOODO!O*!!(!!+KLLBBNnN——˜B—˜NBB•GKLKGHKLHGDLLLKLLLKDDODDODD!+!dKLKLddKKd+LLDDOsDOKDL!!OPOOOd!LKL++!+HNHLN´NNHNIIIKHHLII[²,,O[L“9FIPSSS!OO!D+HHBB˜˜NNANNBBNGHKGOHHGDOjD/DDKddLdddKD fDOOYKd!K  ++KHLLK´HLNIIIKKHH.[D ffOO?/9/OD DNBBn—BBBGHOOGZ/DKLKdKKDXXXLK+KLIKKKHHDDD!sfO(FFGG*D*D}BB——G¿XZj// YXD~DDDLLIIHHD! ,(kYFNNBBXDOODDLIKFB!““ BAXZX!!OO[O!DDDDO!!OPPPPPOOOO!+Dfsf?“““ OOOBBABZXZO[[!ODD!OOPOPOPPOOS|%..OO++*DDssf[[[L€?OOYY!!!BBBHA[[[²¨¨PPPOS.s²sL[[PDO!BBBBAAHBL[[²²¨§CSCPSf²LPPDD!|BBBHBBBBBB[L²¨PPPO++s²LPPPD(BBBBBABBBB""L[²¨PS*+ ²sKLLLLPS?“DD¨BBBBBAAHBBBBB"XL&¨CS.+!fsKLLKKLSP““DPPD|¨BBBHBHAHBBBB#"&L[§fCPC+++sHLL[K|S?“DDkO  O!¨BBHHHAHHAB"L[((¨SC.sHKL[HLL|Ž“ŽDDDD  ˆ (AAAABBB#XZ[.²¨CP.²sKKLLHKL[LLLPS““SD (ˆBHBAAH-BBO&.DfPOO..9XffHK[LLLLMLP“9§?DˆB-BHB-B-B!&&&&&&Y &OsfKHLLKK?/9/?D BBAHBBBBDDDSXXX. LKL““ ¨¨}BBHBŽSYXD~DOOKL¨|BHHBBBDB.&..&.&U        ,/,,/XPPP OOO&&.&"""!+U/WW/  P   5545OO                 , @@Z    &""+*    X  |       554              @X      && 05"    UX   (55555455                  /        "" X        554455555           Z           05L!"L"+  X      555544555    X     5   !/L !W P |P5555544455555 ,         $Z,Z,  L!""  !W!W/PP P|  544454y5555        &&      ,,   CC A02!" +++ + ZW/ “ || ||P 55y444455555                   $  ,/          "L!+*     Z! W “    |U|P  (  55544y4555            & &&      Zf$  /@        &&  ""I+   Af  UZW/!! Ž“Ž “| |   ( 5554–4yy455   X   . . &     f ZXZ        &... ""fA U U}/ “““  ˆ  5555–y5y55ZOZX/.   f fY  %% && &.& "fff AUWWœ“9§?    5555yy5y45ZZ’#X   :. f XXX%      ffWU/W?/9/?    }555554y5XOO     YX D~  UWW““  55545     WW5????????$????™šŽš???f?42?0??!O?42?0??24??42?0??2O?420?0?5?42?D!$ZW$?W$$O?42,0??22??42?0??!ODDD&&1211!+5™™™™?™™™OŽšššš?€22?PPDDPD?42?0??!¨¨ff4fG!G!GG2y44?122??2y44?124?2y42?12 2?2444?24?0445554?44??2y44?120??Z––5$5– $2y44,122?2y44?122??244?!O&&????0121?????+*445554?44™?00™000™šš?š??€4452€?€€?|DDDD2y44?122??¨¨fffffG??0??2?? y20 4? y20 2? y?4 25420425(554544?455445?244? y2004? –'5 5? y2, 2? y20 42y4??24?????????????2240?????00212442?54544?455?0™0?5š??4€?4š4yy455?522|PDDPDX? y20(¨¨ffff4ff?42?12 2?2 4?y20?2y2 ?2y&25?24555545??5244?24y4???02 4?y202 5'52 ,?y?& 4?y22 y?0 4(??42??555™™™™™™™???42??5y52422?2y41021?22?225545??55?0™0ŽŽŽ244?€?4š4y44?224|DODO2 4?}¨O¨¨¨ff4fffff’? y?4 2? 4?4s?42? 4?24y00?425(00&2&544?4 &?5452? 4?4? 495? ,?4???4?42 4?y26442?5452™?==?™™4442?5452?41- 02??2+00??0™0Žš€222??2š(2y4?24DXOX? 4?4(¨DD¨?fffff4xffff½½2 ?2y4??2s™4?24424f45254f202242??4y22??02y4??2ZZ?2Z??244?2&&?? 4?4&(?2402y4222s?™?4?2402y4????2?21?42??24f™0™Žš?24?š45?2P|PPDO??2OOD¨?fffff4f4fffff½½? 4?2&42422y40?242405f4?0240y42y40242?22'99W22?24?? ??266?42?2222ss22ss2y?42?222?224?222-0242?22f4?02f™š™(OPX||PPDˆˆO¨?fffff44xffff½?&420?424y0?44420?22?04y202&&2y44?4244y2?24290?9W45Z?Z422,?42220? 4&54??2sss222y5?52??2y4A004124+++424?42424™00™?™š2™2?44242™|±±±/±±|POPDX (20?§Dˆ ?¨ff4f44x4ffff½½2242420244224yy024442424202424f44y0d&25d&d455yy24??4250224?42240202425–5–5W5–5Z644224,02442?44202242?20?4224?422?'42s2244y54?224?422?040j55-+*?44455yy44?5?0?0™000™$42242š2444$25™44?2(±±±PDOXOX(}20(§OO?f4f444xx444ff½’½4420?224 0?4y2&44y&&4y24s24 0?4y44y4&fd45dd4&&402542204245?24242020?4y45– 0Z5s–W(22444 Z?4y2&d2 0?42 ?242024&44245?242''=242s522455y?245?242y?51-12+?24244242?5??0™05?™f$šš4242šš242šš$54244Ž2“Ž52|±((U±±XXDPXDXX2 0?42?§ODD¨?f44˜f4xff44f½½½242420242422?2&22?22&&422?:4244ffd504424y222?2044422?224455Zs(62&22,Z2&d?2??24224?0?4y&24y222?2044(''=?sss5554yyy222?2y121W212?““5?“?55™™s$š$š2š’Ž4š$$244y“?™“2y42±(UUU((PXPXZ22?(?¨¨˜n4n˜f4xfff4nnfs24 ?4y44&&4&&.:&%y42224y45y4442f(&d4,&Zd’X&&2422? 44424y4?24y42('=22ss424y45y4?2421202?“?y4““?ff$$šŽ$(“224™“9§?“2(((U((UXZ /Z4? ¨˜f˜ff4xf˜f˜f4224422(2:.(242y42"2"5(d’dd2//dd2X4??2242?y5((?244245?2?20012????0fŽŽ“?/9/?“(((PrXPXZ2}?˜fffffffff˜44 4 4 (444y44 – (d2,d4 ??D~D4%4%(??4245?2211010 š((““(((((X4 ¨?ffff2/d2444y4(24444y4110ššŽ((f4 4!“XXB!!O!O!DDO!!DDDD!!PPPPPOOOOFFF!+DDDDDOO““““Ž“?ZZX  DDBBBO!OOO!O!DDO!OOOPPOOOOO%9OOMF++*ODDD OOO““Ž?OX DDBBBBOO&D&&L!!!G!!GD!DODDDDOOD%%MFO++sO?“Ž“DODDBB-B OO&&+!LLLL.+   GDGODDPPOOO99/!MFFO sfsfODOO““DOOODDBBBBBB$ O+!+   ! !PP PDO99DOM/F/++sDODOOD?“DDDPOOODDDDOBBBB-BBB@$!L  !O D !D OPPD.. D F/FM*OO+ODOODDODDD XDODOOODODDDDOBBBBBBB$$+!    4!D|GD DOOPODDD ..DDDI""FFMF+O!ffODOODD““DPPODZXODDPXXOOODOD,DOBB BB-B BBB@$+++&&LL:Z&&fOD !PfOOPCDCCDO%..CDCCD,IF"FF+++ffsfDD““ŽŽ“?PPIXXZX (DDDDBB -B BB $+O&&.L+&.  Gf!OOOPPDDCCDDD..CDDFFFI+*ffsfDDOD“OD“DPOƒDDXOD(}(ODDOODBBB B- BBB $O&&!+.&DD   G!O|GfDOOOPPDPDtOt%%%...DtOtMIMF"FIF+ffsOODO“DD“Ž “DSOOXO D(OO OOODDBBB  -BB OO&!WDD   G!Os!OGODYPOOOOtO%99%%OtOFFkF s :D!“OO“O““DXDDXO(O}(DOB-BB-B-B!+O &&j&&O /&&D. fOYt 9  td ffOOO“9§ DXOXZDD }( BBB BBBBO!++J Z&O:.D O OXXXO~  O ~d0f!?/9/XZX(O}OBB O+ ZZ:/ OYYXD~D D~Ddd ““ZBBBBOO+JDOODBO002000fAAA000?0000(6(r¼¼((((¼(231"BK˜V((((}(042002404ffuAAAAHAHAAAff000000000000% dd((6r(r7 r6(676¼(6¼6¼6(¼(622fff00KBKgVV %’d((((}}}(}}}((5 5 5 ff44u4ffufAAABHAHBBBHAAff0?0?00?0000?0?0’  76rr7 r 7 677(“6¼66Ž666“((˜-ddf ff˜˜B˜V EEEd((((d(((((}}kk}}kk}}(0025{ 4{4f@4u­­u44ufAHBdBAdBBBBAA24ff4200?°?0??0??0?0@%  %4 ’  L(77 .. r7 7(6¼7¼6666¼666¼76(--˜L˜˜˜B  ’@%wEE %Ed((“((((’((((}}}kUUk}kUk}}(404 44yy24254uu­u44­uf4uAAHBBddB´dBHA24f4ff000?µ?0???µ?000 @ Œ Lf77.... . 7 76¼¼66¼6¼676¼ 6¼(d -´-0LKBKB  VžEwƒƒE((((“(((f6’’((((((’((’(}}kkUUUkUUUk}(02 4 yyy4y2y{0u44­­4uu­fuffAABd´B´´d´´BAff444f00?0?µ?µµµ??000% @@  2LL§¨§r ...... . 7(“66¼¼¼66¼¼66¼6( x†-BBK[L[Bg    E%wƒŠƒ Ed(((““(“((f½((((’’½’(((½((}}kkWWWUWUkk}}20444y{yy4y2400f4uuu­­­­­ufABB´´’’’’´dHf44xx400???µµ°µ?000%@2[¨¨Z§§L6r .... . 66(“66¼77¼¼7¼6666¼  @ -- ˜[K[g   ’E%ƒŠŠE d(((““°““°“((ff4(’’(((’½½½’’((’((’’(}}kWW/WUk}}}(0yyyy{{{yy442fuf4­­­u­uf@4fAHdd´’’’’B’AAf4ffx44f00?0?µ°°µ???000%@@%% 2 %4IOPSt¨Ifr ...... 776¼7¼¼¼ 77¼7¼¼66“   @$BBB[KKKLg   ’%ƒwŠƒ%E ’0((°“°°°““(“(4ff44f4f4ff(’½’((((’’6((’½(((½’}}kUUWWWUkk}((4y4y{{{{{yyy450044­­uu4ABB´’’’’Bd’HAf4444f000?µ°°µ?°?°?0%@42{­’ [CCCCP¨I77 ... ..7((66¼¼77¼¼6676¼( @ {@ ´†- ˜BKg[LL˜K   %wwƒŠ ’d((““°°°°“°“(444((½’(((((’½(’((}kkUUUWWUUkk}(04y{y{{{{{44f2{u­u­­­uuAdB´´’’’’dxff4000?µ°µµ°µ??000%% @@%4{   4¨DCC[6 ....... .7((Ž666¼¼7¼6¼666( 44o  ¶†-f˜[[[KK  %E ƒwŠƒ %’((““°“°““((fff(d’(’(’6(}}kkUUWUkk}}y{y{{y{y2y20f4@4u­uu­u­44fAHBd´d’’BAf4f440?00??µ???µ?000%% @@@%» 44f¨C¨L( .. ... r((¼6¼67¼¼6666 ((4oo4 -df0B˜LLB  ’%E Šƒw@E ’V((“(“““(((fd(((6((((}}kkkUUkk}}(4yyyyyyyy50ff44uu­uu4f4fAAddBdBdBf0°0?µ?0?0?0?00@@@%   ¨¨r r77 . r7(66¼6¼6¼666¼74oo4@010KLKB  V @ƒƒEE ’d(((((“(“(((’(((}}}kkkkkkk}}(04yy44y{44u4u­44ffAKBBBABBBBBHAH4f0000?0?000?0%%% %­Œ2[IL6 777 . r6(r7(¼666¼66޼((¼¼44-221KK V %%% EVd((((((0d((}}}}kkk}}}({40y2y225f4fu4u4f4fAAHAHABHAHAAf0000?0000000%@2 d(r7 r777 r( 7(6¼7(“66Ž6“((5310KKBKVV%@VEEVd(((((}}}}}}((02020000200ufœuf4AAAAHAAAAAf2420000000d 2(7(67r6rr 7r(6((¼(((¼((¼(??“(001110KKBBVV’V’d(0((}2000f4ff4fAff000Œr(6(rr(((((6612µ(0s00˜KK˜’’ddd6y(6(6(22(7(6((rr((76(6»2’y%677(((7r(6(67(7((76642y22’²™I( 6(7r77r(6r 7r(((r ((((  III   yy%%y5™OZj?™d(77(((7r7 . 7r7r((67((r77r(7(67(6767(66  GIGIIGI{ { {{’y»»»’»?™™³™7.7(((r77(76(7.7(((7.76(((6(667(GIG  %y?f™(( 7(((((7 r(r 7r((((((44GII»%™Lr7((r7667r6(7766I2y»²Ir((((r6((r(6((y(7(((((000000fAAAA0?000(0000002HAAAAHAA00?00000((((2000000000fuffHAHAAAAHAHAAABABAf0?µ000000?0000?0}((((}(((}(0400000000042000020f4f44fAABAHHBAHABABdHAHAABBd´A-ff?05??00000?µ0µ000µ00000?µ00(k6(66(k6((((6k(0240000200004200040uf4­uH´dAAHAAB´BAAAAHd´´BAHAAHB´´dBf--0?=?00000?µµµµ??0000?5µ?00?µ5?0   (6/6(((}k/Uk}((/6(((6/60{00024{y44200{00002{2004ff42­u4u44ff­f{AB´´HAHAB´¶¶¶´BAHAAAB´¶´BAHAHB´¶BHAff--f0?µyµ?000?µµ5=–y=5µµ?000µ–µ?00?µ–µ0  (U6((66((6U((((6U6(00y0000220002y0002y200f2fff­ufufy2fH´¶BAHdB´dBAHAABABB´dBABHAd´BBA--f0µ–?00000??µµ?µµ000µyµ0000µyµ000(k((((((k((k(024000000042004002ff4fuAB´BAAAHAHAHAAHAB´HAAABdBAHAff-000?=µ?000000000000?=?000?=??0   (}(((}}(0200000200200u224fdBHHHAAAHABABAHABAHAf0?5?000000?µ?0µ?00((((0000000002ff4AHAHHAAAAAAAHAf00µ0?000?00?0000000ffAAAA0?000000000H00000A00004Of4f4O!ff4x4x444fAOXXOOOO!  f4xxyx4xyx4%X//d--dOAdd%   XZDO!OOOO!O      4yyx4xx4x4f%%%XZ//,AOOOOAYOOAB%%  XD!O!O!O(O     """ f4x­444­yxf%%%%XZ/,XA,,AOOBAAABd%%    DO!OO!O9(!     "@@"f4x44­4­x444%%%%ZZ,,d dOYOAAABd’ddd%  OOOOOO!!(99(            ""f44y444xxf%%%@%%ZZd((­OABd’’  X!OO fO   9     "@@" f44xy4­­4x4%@@%% ZX-AA-d  Z,D! 4!  49(!OOO                  @@ fg4x44­444yx%%@@%%%% dd DjXDOf 4   ff4y!O            I "@@@"f4x4x­4yx4%%%%A-    DkXZOy  4ffy        " 44xyx4yx4f%%ddZ4y 44   f44xxxx4%% f   f444x44f4f%ZZfff4ff---(““(““?(“(     dd(dd-dddd-dddd-dd((š( =     dd((-----ddd---‘-ddd-d---ddd?((§?     dddHddd¨XXXXk((9(d----d--dd--‘gd--dd----d--d§?§(==    AdAdAdKHdd ¨¨¨OXOXOX(kU9(d-d--d-dd-d-d-gd-dd-d-d-d-ddd-(?(?(= (      .. AAH%AAH%AAH%QKKH%Hd%%% %¨¨OXX%XOXX%XOjj%X(kUk%U9(OO%OOO%OOO%OOO%Od---d--dd---d--dd--dd--d((“X§X§((   ... AAHG%AAHG%AAHdQd%QKKHHNH%H%% % : %¨¨¨¨OXXX%XOXOZX%XOjjZZj%jkUOkWk%U(OO%OOO%OOO%OOO%Oddd---ddd---dd-d---((( ““(=(      .  "HKGH!H!GHd’’GQdQKHžHžQHH    :// ¨¨§¨§¨¨¨§¨¨§¨·¨¨XˆOˆXXOZXjZUˆˆjjj(kWWWkUOOODOODOOd-d-d-d-dd‘d-d-dd-dd(((=(=“      ..HH!!!HGH!!!HGHGKKH’ddKHƒƒžQH     ::W/ ¨§§¨¨§¨¨¨§©¨§·©§·¨XˆOˆXXXZXjZˆˆOZj((kOkWkODDDODDODDDD-ddd-‘dd‘d‘-ddd-((=§?      fho› HHHQ’’’QdKQQTTQd ::: ¨¨¨¨¨¨¨XZZZXZZZZZjjˆZUUkWUWUUOOO-d---dd-d-dd-d§(“(      h‘h<..d((-dd------ddd-‘dd-‘-‘--ddd--dd---d--ddd-(““?“=(      .<::h.ddd--d-dd--dddd--d“(“XX(     .<‘.-dd-dd-dd-dd-ddddd==(=   .d--d-d--d‘-d‘d-gdd-d-d--“?=?(      ...d------gd---‘-‘gd-d--d-???“     -dd-dgd-d“(((   000000?00000000Y?000B?B000000000000000YODYOOOOOOOODOOO??0DB?00000¸0000>200YO YDOOOOOOOOOˆOˆOˆOˆWD OOOOOˆ“?“O&DBB00000¸000ª–>220jODD%%ODD%%ODD%%OOO%%OOO%%ˆOO%%ˆOO%%ˆOO%%ˆ//D%%%%%%%% %DD%%OOO%%OOO%%ˆ“?OOOBB0s%%0s%%003%‘¸000ªÂyb20jjDDOOO%%OODDOOO%%OODDDOO%%OOOOOOO%%OOOOYˆY%%ˆOOX}ˆY%%ˆOOOYXY%%ˆOOOYˆY%%ˆOD//D%%%% %%% % : %DDOOO%%OOOOOOO%%OOOOYXY%%ˆO“OOODOO??000s%%0s02s%%0s0›¸¸3‘%¸s000ªÂÂ>8–bs00j  DOOXZOODOXWOOODOOXZOOOXOXZOOYˆZWˆYOYˆWZXYOYZWWZYOYˆXZˆYOO}ODWWD           :// DOOXZOOOXOXZOOYZWWZYO?0O§-BDO&&DB0s420s0s442s000›¸b£¸3¸00002ª_h8£8–b00  OZWWZOOOXWOOOWXWXOOWZXZOYˆWZˆYY}XWZYYZXWZYYˆWZˆYDO}OD             ::W/ OZWWZOOWZXZO?YZXWZY?00?L§ZBDO?00›s24ss›2204s0›¸bb¸3000ª_hh88 0OXOOOOOOOXOOOOOOOOOOOOOOYˆˆˆˆYYˆ}}ˆYYˆˆXˆYYˆˆˆˆYDOOOD  ::: OXOOOOOOOOOOYˆˆXˆY000L™ZBDO000›¸¸¸¸s›¸¸¸¸¸›¸¸¸¸300ªÂ>>>80?00000000000 00  OPDDODC!O!ODO   DO!OOD¨! O!!OOO!O    O!OPO¨!OOO!DO!OODD D!D!OD¨!PP!DDOD 5;3;3;D!O PO¨! !D!OO! D|D 5;a3e3OPD!O!OO OYO!D 3;e35OCO O ! Y   !OOD|D  b3e    DOOP¨  O    O DO!DOSD   D CD¨·¨(X O     O OOO!DOSD     P(© ©j O       O!DO|D     CC(§©§          O OOO|    P¨( !   !             0000 [[²(©99© 000002q  %% L!!ˆˆˆ6LL[[[[[XX,/'²²™ž|©99°µ 0000D00 0022 % ""!L±ˆPZZPˆ6ˆLAAI[[[[O,XXXZœX™?² 6N |9¨§§§¨9°    0?O02 02c % L!!!±±ˆPZWP±ˆAIIdd´KFOO,œ,ZZ!Z,œO//²™–ff fy(žN s (©¨§¨²9µ        00? 0ODO02 2c2 %"±±ˆPZˆ±±LId´¶dFO,,Z,,,,Z$$œ™?²ufssy–6(N©9¨²9©       0ª ?0D0c2 !!P±ZZW±IAAAIOœ,/œœXO$œ²™?04c –yf ©'²'°          0?ª?00 "!"!PPZZLK++AO,œ,jXZ²[5]gyf™ 99'@9O   0000D0 2 !!!!PPOZWˆLIK***IO!!!!ZZ,'œ[f[ff(™ AY©d9ddµD     000 22%%%LL!!POZZˆL[Id+—¶II!,,!CX/,'X'[[LLd³[f–ccf6› AY©99'‡9°O    *+* 00002 c2  L±OZZWW6ˆ[[[K+**´KI,CXZXZ@,OZœL[[Lfd[–y­­y–(€™NT|}}´BB9©°5DOD++*00022002c…c2   ˆOOtˆˆˆˆ6ˆL[[IFKKKFFKODD,œ,WXZ,,,@XZLdL³fL–yyyyy(ˆš€™sABNTNN ©´B²9µo°DO O**0c…c00c…c2   ± 6ˆP±OOˆ IIFT FKODODO,œXWXXWœ, Z Z Ldy–y­..š šˆv±OsN ||Y©¨²²B"5°DD+*Dª     OOOœ,WZ,WœW––­y..  ||Y}9"oo°ooOO+Dª  Y5 5 D GIOOOOIGJGGIIIG0?0000    o  O!O!OJGGGFGFGHKG44??00??DO4        cpcc   o244!G!OOOOGJGFGHHHKIG4??? 4DDODDODD!DD!DOO       !!        co c ­{w¤“cW/KGM2424u!G!!!!IGFFHKHKIIG0?4 4DDDDDD,DDDO    f!(Y (   p{4c f­f {w ¯©“¥cjZW/YOY6(KGKMG2;24u¦4X/!G!DY!IGHHKKKIIGD0?4y44OO!O,!DDDO( f  O (((  coucoA w¨ ¯¤?“3¥“YZW/œ/Y(Y((6(FKGMM;0224uuXZ!!!ODOFHKKIILGJDD0?y44OODSDDCD    DOO      c c5 foAf  ¤?¤?¥¥cYUjZ/YY6Y(r6KIFMEJK02;u44uXXZ/G!OOGKILLGGJ™?0?4ZZD!DOP , , j,: 2cc AAo¨  ¯¤?3“c¥YXjZW/œY(Y(ˆ7rIFGMJVG00424u2X/Z!DYFLLGGHJ4400DZ,DDDCDDCDDPO   f         p5cfAff   €¤©“¥“¥YUWZœ/œœOYO(6r7FKFMVEE;2244XZ!!OD!G/LGLKGDD444?4DDDDDDDC!C!DO4   2 p-p{u-{02c Afww˜   ¯?¥“XjZUW/6Y(67ˆ(KFKGJEJ02u4Z/DDOZj/LKGDDDD444? 4!DO!PSPSOOD!                       5c5uc f{{ccfffA˜wo   ©©ZWZW/(Y(6(FGMVJ04ZODDODLHK/440 !!!D||DO          ((    -pcu p-{ Ac{scfAA˜ff A!D!HLKG040444™DD!!DDDOOO       4        Y((Y        f ®4w{cAf­f­­GK[GGH4 yy?!ODDD!ODDDDOO   Y(     c{c c{cAf ff ­­!GGJH!!!!   {c   !  f£ !!fsq!,,!+ f] jWSYq£0D! !D!,!+,   f] XjZDOYO[LL›sq¬qO!XO!  !P+,!+,  fu { jZUWDOO(YOL[LIFKIqs££O!!!O!   D+, !,,   fu { XjZUZDODOYˆ([LLIIFFq›sq£qODO!X!O   !  DD+, !,  f]f {{    jZjZUSDOODY(([LILIIKsqq¬£DO!XXXXss¤“(O6I22X!!ODD    D!+, ++,,,   ]f {{   XjZUWDOSOOˆYˆL[LIFKF›ss££qO!O!XUXUOˆLLILsq£O!?§§cj/YO6rME4¦2,ZO!O  !!,!+,,  f{{    jZjUWUSODY(OYKLLFIFFªqq£q£DO!XXOOUYOOYLIq!DD§¨¨©ZYY/ODD7GIIJ004XOOOO! !+, !!!+,,    {{    ZjZZUWSOYˆY[KLIFKFª£q£!O!XjWUWOYˆj[ILFsq£sO!O "¤?¥“jW/Y6rFEME24¦uZ, ,,, !+,+,    ZjUZLIKIF£qjWOjLFsq!“c/Y7FJ2uX +,     DODt9tZ, (¡¢05+D!,  jUYˆIL3DOŸ W}6MžfyODt, UWY&FIO!  XX67ž‡X,ZODt+:+++(X4XZ,,Xt+*,9Z4ZX,ZD!*Z+XXX…XXX,X, X+t+,,++ZX4XZWW,,OD!+,   U/W}B ++XZ4XZXXZ,OSt!+:!D WWUM?++44XXWXZ,9St*,!D  WW/œi p+XXZXXW X!St+Z ! UU  ZWODt!+: !!  Ot,Zd0,ff XWOA A  €]:˜c ZDdA?,˜cZ(d5 5"yyXWOA€#fxfyXZOA0€5Z,f˜fc ZdA?,ff Wd5 ?: f fXWO(A5 €Z: ˜fXZOAA€5,xy ZdA?,ff W(d5 ?:˜cXWOA5 €]"˜fZ,OA0€]Z,fxfyZd#xfyf ZXd50 ?0"˜c   XZO(A A0 €5"fyfyXAd?fftt|KL›1OOKL™™?€™tvApA  X  K ›55O K ™™?==? 6„„6A q  +AO6O ZjXXj||||ˆKHKH››1›O!O!?€™™€?""6tt|Pt6n˜˜˜2222 Xj!+A! 0“0,ˆ6B02     ,Z,,Zt|t dKd’4›1›14!OO   =€€€™? #"#"vttt|t ˜˜n˜ 424y  jZDD!LIApO 0“,ˆD(•B02 ,,,Z:tˆd’115– ?“““Œ?###@rvvn——n4y@ XODOYI!A˜AD!!DLL™š,DDY6BA00   ,, ˆ ’’45  =“= #„rr nn yy  jZUZjDOOYL!!GApAADDL0?=?0,))Oˆ6rB•o•A2]0   4   == 6  jWUjDYL!JA!0 Ž0,)Or(‘B2o2?? 6 Uj+!AŽ?          =Œ„   ??  6AsA Ap‹s!   +0Yˆ‰ŠsA!+OO ++    !000000(}7AXAO  ++OO! +      !0f000030d000((0000ff(r(XXA ! +OO!+      !! UW}rKTfe0 €:7 ‚ƒe{0r~/m(}„(005]…00ff†‡ff((::Z(ZXX  !O      S!!   XXZ0W0XX 0 ZWZX0XYr]~(( (0 0fff f((/9Wr(XX'!OO        !S!   ZXZWZZX   ZWZZX  ZXY}26 (0 000f f((W,Z(XZ O!OO!O!       |0XZXXOO!!O!!!     XZ' !OO!OO!!   OZXXZOO!O!O  ||OOOOO™q¯s›q›1››››››››™s™ss0µ2µ212151010100100001011000155555151s™s¯™s¯›1›1››s™s™²™²²s™002µ2µ1µ012µ15151000101000000005515c55555!!Yv¯s¯1q11¯q1›››››™s™²00™s0µ2µ15255µ251010000000001000555555   4f 4 !!* qqpvf4ffTOOq™s0›110q¯›1s¯››s™s00s™0s™2µ2515552µ152µ0000010001000555555    XXXZ!,ZZ  qpq]lv4 f4 h4-O›¯›™›1q¯›q²²™²²²™0s™s²001µ1001µ152µ02111000001000000005555155c5     4  f4  XXkjXkXXj!ZZ*/XZ 4ff  o  {p]p]f44 hf-N-OOOO›11sª¯qªq¯›10›¯0s›²™s›s™0™1552;1µ2;2µ1551µ0001100010000051155555 00000 00000  00000 4  f 4  XXXXkXXXX!XXZXZ+ZX,,4  h  fod(qpYvvf4 h-m-T-OO10›™s›ª››1›0›0²™²s›00²5511µ021;0151501000100100000005c555c55022221001fff00022bb10jjjkjj!!!!X'XZ,,Z/Z+*++44 h44  w w f(d {pppp]vrYr f 4 h-mNKfTO.O›0›00›››001²0²0²²00²00150151051015155000000000055555155002a210O0 0122f12S0 0b2a2bb0jkjjUjjjjXX''ZZX,/Z,*X+4 4hh4hhff ww ((d  {ql]qpq]pYvYr4 fffKN-NKff-.›1››01›0›¯›s›0››000™s0151055151µ12151010000000000000555c5c5503f2b20O102ff2f0S0 03f2b201       XkkXjkUXXjjX!XXX/ZZ,Z,+/Z/h  hyf f w  o dd({{  p]pp]]plpvr vr vvvr4h4hKfdfmNf-..OO.000›11ª›0¯s00›0²000²0™s05155155;015µ25510000000000000000055555555f1002p0f001fpS0f1002p0      XXjkXjjUkXjXjkXXXZX/Z+,//Z,44hhf w wo xd(d ]lpvvvvYvrf4y-fm.O11111››10s™00²00000²s™0²0155555115512µ515001000010000000005555c555sAf0sOsAf0ssAf0st 4   f 4  jXjXjX!Z,/,Z  u - n  vrvrv4 hKf-KNfTO.0›››101›00››100²00²0²5101555115510150110000000000051551155      +          XXkjXkXXj'XX/Z X/X  no  pqp]r KTNKN-Nf-O.O››1››000›00›²0²0²²010150115515005510010000000000000155555551 +   4  f   4XjkjXjkUjXXjjjXXd]lf4Kfm-OO..›››1›010²²0²²0²0²²0²1001150115151050000010000000000!555555555   !XZf(g h²ª0›1››››››00›²›0²0²00;505110101151510000000010000055555555d(››0ª››0›0ª›²²²²0›0²0›1101005;015115;00010100051551555500555DDOODDDDDPDCCDCCCDCCDDD   KFFFIKKFKFFIHHHLIIHIHHKFFFIKKFKNF0000000555555D!!O D!ODPP D!D!DO D!PPO !! !! !! !! !! !!  !! !! CCCCCCDD   FFFMFIIIFMFFFHH!!HNHLLLHNHHHFMFMFILIFMNMF0000000555555555 O!O!D OOODDP ODDODPD O!P D  !OOOOO  !OOOOO  !OOOO!  !O!  !OOOOO!  !OOOOO    !O!  !OOOOO DD    !!FFFKKFMFIFMFMFNHHIIHQHLHNTNHFMMKKFMFIFMVMN00000005555555555 D!!OD DO!!ODP D!!DD!PD D!!PD !..O !..O !..O !! !O !DDDDO !O  !!DDO !..OCDCDDD   OOO!OOO!OOOFIFFMFFKFKFEMKHLH!HNHHIHIHNQIMIFFVMFKFKNEMK00000000555555OO!ODDD!O OODOD OODOPCO OO!ODDD O...! O..! O...! O!O!! OP D! !00!    O!OZ,! OXYOOX!       ODDDOD!DD!ODIFFMFFKFIFFMFFKILHHNHHIHLHHNHHILLFFMFMFNIMFMNNKIO00000555 D!!!DOD DO!!OPP D!DCD D!!P!DP  !.UU..O  ! .O  !....O  !!!!O  !8>O  !0O     !!!,O  !O.O CDDDCDDDCCDCCDDDDOSDD  O!OO!O!O!KKMFMFFFFFKFFKIFIINHNHHHHHIHHILHKKMMMFNMNNKFMKMFLLKLLOKIKK0000000555 DO!!O !D OODCC DODD DO!!OP O..U! O. .! O .! O! O! O <8! D0<0O    O!!OO! OOYYYX!CDCCDCDCC       !DO!DO!IFFMFIDIMFKFKIILHHFHLPLNHIHILLIFFMFIDIMFKMKIIKLGKKGIDLGKKGKKGKKG0000555 OD!O!DO!OO D!ODCC OD!OO OD!OP!DPO  D...WUO  D. .O  D.. O  D !  DPD <> DO  DZ0WO    D!O!DO!O  D.OYYXX.O DDDDDDO      OHHI!SIILOHHKKFMMMIIDFFIFDKIHFQFLLP!HHLHP!IKNMVMII!FFIFDKK[KKKGJGI[IIKGGGKIKGKGJJ00000555 DOOD!ODD!D!D OOO!O DODD DOODPODDPPP O...U:W! O..! O...! OOD!DD!D  ODD DDP! OZW!  OOD!ODD!D! OOPPPX!DDDD     ODHII!SPILLODHKKFFFEFFFIIIFFFIHFHQHHH!LLLHHHLNNMEMNNLIIFMFILKKJGKGJGLIIKGKGKGIIKGJGJGJG000000555DDDDDDODDODDD!ODCDDDDDDDPDODD DD...UWU.D D.......D D...D DDODDODD DDPPD DD  DDOD!DODDD DXYYYX.D DD     OGI!SGOGKFMEFFIFFIFIFMFHNNFHL!H!HLHLHNHFMEMNIFFLFIMVFLKLGGJKKLKLGGKIKKGKGGJKI000000005555DDOD!DODOD O!OOPP DCDD DDOD!DO D  ..U. . . DDDODOD( DDP   DDDDODOD ......CCCCDCCCCCDDD     HD!OIIGOGGIP!SLLGSGGHD!OKKGOGJFFFIFFIFEIIFFEMHHHLHHLHNLLHHQNNMFIFMIFVIIFFEMGKGGKGGKG000000000555555 DDDDDODODD DOODOP DCDD DDDDDODDDDDODOODDDDODOODDDDODOODDDDDOOD DDDODOODDDDODOODDDDODOODDDDODOODCDDCCDDCDCDD   IIHGGGJILLIGGGJLKKHGGGJKKFIIFFMMMFIMMFIHLLHHNTNHLNTHKFLINMMEMFIMMFKIK00000000000555555DDDDODOODDDODOPPPCDDDDDDDDODOODDDODODDDODODDDODODDDODODD DODODDDODODDDODODDDODODDDCCDCCSD     GIGGGGIGFFEGGGLGGGGLGKKJGGGKJGJGKGFJEGGFKFIIFMMFMFFFFFHIHLLHNNHNHHHHHFKFIIFMMNMENNFM0000000005555555555DDODODDDDODPPPDCDODDDODODDDDODDODDODDODDODDODDODDODDDDDD       GIGDIFGJIJJGJGGLGPLKGJLJJGLJGGJJDKJGJKJJGKJGKFKFFIFEMFMKFMFFIHINHLHQNHNIHNHHKFMFNIFEMFMKFMNF0000000005555555DODPCCDODDOD                  DDDDD    GFGEFGIGJIFHGGKGJKGLGJLKIGGFGEJGKGJKFJGKKFMFFMMFMFFMFKFIIHQHHNNHNHNNHIHKKFMRNMMFMFNMMKF000005555CDCDD    EFGHIGFFHJKGILGKKIEFGJKGFFHFMFMKFFKFFKKFMHNHNIHHIHHIIHNFMFMKFFKFFKKFM11211212242211211212242200101010101022222222001010101010222222226002010101000121022224322222002010100000121022224522222((((696(&00000200002010022422<322422000001000020100224225522422$(96(96&10010;10021000010100288283232420000010010000001002252=525242$$A!A!B6666$:$010;1;110110211;112122328333322200??0?101100??0?001225255555222@$AAAABB9((9((0101;1;1;;11210101;;;;2;11222838883322201000?0?0011101000?0?001122255===55222$@696(996(&010111;;;;1;;1120101;;>>>1201142223338<883334301010?0??010211201010?0??0101142225555=5=55343$$@$$6(6((9(6((&&$:$1211;;1>;1;101211212;>;>;;;121214423328<8<38<3341211010?0?0101211212210?2?012121442552=5==554334$$@$$(9(669(6&7$$::$$1011;1;1;101111010;1;;12;11242423338832<4510100?0?00011110100?0?0001124242555=552445$$696(6997::010101;1;101101;;>;002222323883324301000?0?01011000?0?002222525==55243@$@(((697$:$01201;101010;0;1002428388<822330120010001000100024255=5552233$(6700110110101101002232323823220011010010110100225252552522$000112101110011202242242322322000112100000112022422423223220010101010010012222332432200101010001001222235245220121000120233433201210001202334332                                      .   .                                                     .     .                         . "   . ,,#"                           %$  "   %%  ,,"",/                           ' . "   .  ,,,*+//                       $$   )#   !",,!#-                     $$$%  #)# %%  *+""                    &$''&  #  &%&   (!                      $ "##  %                               !"!#"                                                                                                                       zangband/lib/xtra/graf/8x13.bmp0000644000000000000000000031156610250356274015226 0ustar rootrootBMv“v(&“sÿcÿ­!1BRRRBÿcŒBcŒŒŒŒ½œs­Î½ïÿÞïÿUYÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUYÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUYÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7wwuUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7wwuUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7wwuUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7wwuUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUSwwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUSwwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUSwwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7uUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7uUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU7uUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ðUw3PUUUUUUUUUUUUUUUUUUUUUUUUUXUUXUeVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ðUw3PUUUUUUUUUUUUUUUUUUUUUUUUUXUUXUeVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿÿªö`ô"÷33?þèˆýÍ™ü™Y_üªjoýíî÷wª¯ôDª¯û»‹Ws30UUUUUUUUUUUUUUUUUUUUUUUU…UU…VUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_ÿúð_fð_B"ð_s3ð_îˆð_ÜÙð_É™ð_ʪð_ÞÞð_wzð_DJð_»¸ðUD! `UUUUUUUUUUUUUUUUUUUUUUUUˆ…XUX…VUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_ÿúð_fð_B"ð_s3ð_îˆð_ÜÙð_É™ð_ʪð_ÞÞð_wzð_DJð_»¸ðUD! `UUUUUUUUUUUUUUUUUUUUUUUUˆ…XUX…VUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿÿUöoUô/U÷?UþïUýÏUüŸUü¯UýïU÷UôOUû¿UUUUP¦UUUUUUUUUUUUUUUUUUUUUUUUUXˆˆØe†hÖUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿÿUöoUô/U÷?UþïUýÏUüŸUü¯UýïU÷UôOUû¿UUUUP¦UUUUUUUUUUUUUUUUUUUUUUUUUXˆˆØe†hÖUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿWUWUU UUUUUUUUUUUUUUUUUUUUUUUUU…îåXeX…VUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šðuTUuU UUUUUUUUUUUUUUUUUUUUUUUUU…UUUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šðuTUuU UUUUUUUUUUUUUUUUUUUUUUUUU…UUUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU™™U™™U™™U™™U™™U™™U™™U™™U™™U™™U™™U™™UTUUP`UUUUUUUUUUUUUUUUUUUUUUUUU…UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÌ•UUUUUUUUUUUUUUUUUUUUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÌ•UUUUUUUUUUUUUUUUUUUUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU¶µ[µXˆŽåU`V\Å\Åè莎YUYUYUYUU°[™Y•U¸¸‹‹è莎U0\\…UÅUUSUU€XÅ Z Y€XYPUÕUUåUÅ^SUååîîîåZUEEfUªfUUZôUUSDUUUUUUUUZjjjjUUj]35¶µ[µXˆŽåU`V\Å\Åè莎YUYUYUYUU°[™Y•U¸¸‹‹è莎U0\\…UÅUUSUU€XÅ Z Y€XYPUÕUUåUÅ^SUååîîîåZUEEfUªfUUZôUUSDUUUUUUUUZjjjjUUj]35VfVeUXåUU`UÅœUŽSUSUQUQUÀ° SS•U»‹‹‹îŽŽŽUÀSUÅ\UUSu5€°  YƒY™`P]õUUuU…QU3>µUÜÜèUª¤õUjVVU_¥UU^…UU[µUU±U¯¯¥UeVUP3UUeffeUXîÅUff É™œ\舎]ÕwUQU»½ S0Y•¸ˆ‹èˆŽ0SÀ ÅÈŒ\U7WU`ˆˆ°ªª •V`€UˆffUUÛõUUw»±'Z»U]ÎÍȤTOU¦ffjUEUUUeUUUµ[U°± ZeUUfV¥jSUPeffeUXîÅUff É™œ\舎]ÕwUQU»½ S0Y•¸ˆ‹èˆŽ0SÀ ÅÈŒ\U7WU`ˆˆ°ªª •V`€UˆffUUÛõUUw»±'Z»U]ÎÍȤTOU¦ffjUEUUUeUUUµ[U°± ZeUUfV¥jSUPVffeU]ÅU™™ ™‰™œèøˆŽÊª¥Uʪ¥UÛ Y UY¸øˆ‹èøˆŽ0\0\Å\È\u7uUVi–`°»» X:£…_ˆ†`UjðUÛïUUW{fuUUUUÜŽEUEU¥¦jZUôU_UªUWUµUU°± ¦_eUjª¦ªS0¥VffeU]ÅU™™ ™‰™œèøˆŽÊª¥Uʪ¥UÛ Y UY¸øˆ‹èøˆŽ0\0\Å\È\u7uUVi–`°»» X:£…_ˆ†`UjðUÛïUUW{fuUUUUÜŽEUEU¥¦jZUôU_UªUWUµUU°± ¦_eUjª¦ªS0¥UUfeÌÎŽÅYš©•˜˜ÉÌ興…\µ»µ\Q½°ˆÈˆ¥¸ˆˆ»èˆˆŽÀ£0SÅ\È\s77WUYU[»»°•X€Uõ\ÀU± õ]¾%U¥UV`%QUUåU]Ž¥ZUUªfj¦UZô¥UQª5U[Uµ°»» ZöUUUfªU3ªUUfªuÍÍÅ\ÀU˜™œÌ^ÿÈð¾îëµ½ÝÛµZ ÀUUàpægvnê­Ú®[»»åÀˆˆ ÝXˆUZff¥Uîîå\Ì…PwuP3…WwõU3333UUUU€ŽèUQ"ÅO_ÿU]ÝUU…XU»îÝÕ73sSWfuUU^UÐà Bÿ"$â-Ò.苸Žë´K¾èØŽà Ðè‡xŽã7s>àpægvnê­Ú®[»»åÀˆˆ ÝXˆUZff¥Uîîå\Ì…PwuP3…WwõU3333UUUU€ŽèUQ"ÅõUõUÕUÕU…X…X[¾ã3sws3WfguUUUUÐà Bÿÿ$â-Ò.苸Žë´K¾èØŽà Ðè‡xŽã7s>àpægvnê­Ú®U^îU©– ÕU…UZ`¥UàåXÌÀSuX5QOïUs733W0SÐ½Û U]ÅõUõUÕUÕU…X…X[¾ã3sws3WfguUUUUÐà Bÿÿ$â-Ò.苸Žë´K¾èØŽà Ðè‡xŽã7s>àpægvnê­Ú®U^îU©– ÕU…UZ`¥UàåXÌÀSuX5QOïUs733W0SÐ½Û U]ÅETUU…XUU…ˆˆX%US7ww73vffgUUUUÐà Bò/ôâ-Ò.苸Žë´K¾èØŽà Ðè‡xŽã7s>àpægvnê­Ú®UUUUVi©`5[UUZ`¥Uà刉™Ì3<À5ˆ™…UõUs7s3W33ÐÝÝ \ÂÅUDTõUˆXÕXˆˆ…UUuu7ws3gvgvUUUUÐà Bÿÿ$íÝÝÞë»»¾äDDNíÝÝÞíÝÝÞçww~çww~çww~çww~íÝÝÞUUUUUPÀUU»TuZff¥Uàåˆ\ÌÅ3P5ˆÝÝ…UUUUW330Us0½Õ]ÛUÍÍÅUDTõUˆXÕXˆˆ…UUuu7ws3gvgvUUUUÐà Bÿÿ$íÝÝÞë»»¾äDDNíÝÝÞíÝÝÞçww~çww~çww~çww~íÝÝÞUUUUUPÀUU»TuZff¥Uàåˆ\ÌÅ3P5ˆÝÝ…UUUUW330Us0½Õ]ÛUÍÍÅUTOUUXUU€UUUUw37wg~nvUUUU]ÕT""E^""å^ˆˆå^»»å^ˆˆå^å^ˆˆå^33å^å^ffå^ªªåUUUUUYUU[GUUªªUUîîåˆUňÌÜ]]ˆUUUUUw3UUUUUUUUUÜÍUUUUUUUUUUX…UUUUUUwuuvwwgUUUUUÝÝUUDDUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]ÕUUUUUUUUUUX…UUUUUUwuuvwwgUUUUUÝÝUUDDUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUîîUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]ÕU\Å\ÅU\ÅUUõUõUõUõUYWUUuU…XUP€U33U»»¾åéîéåUª¥UVUUeX…X…UP3wwuPP^î}Xˆ»ˆ ˆUUUUUU Y`UUVUUYUYUUµUUUµUU€\Å\ÅU\ÅUUõUõUõUõUYWUUuU…XUP€U33U»»¾åéîéåUª¥UVUUeX…X…UP3wwuPP^î}Xˆ»ˆ ˆUUUUUU Y`UUVUUYUYUUµUUUµUU€ÈˆŒ\U\UUfjª¥ »µˆ€¨UpU…PXPàUS5U[»îUænæåZ UU5SUUUUU33s^íÝÝXˆ»ˆ€‰Y3™9" "ª"¢€Z Z Pˆ€Q`[µUU[µUUàˆˆˆœ^îîUVªUZP»U[ʪ ªuSW…PXPîîS3P¾ë»åîffåU Uu5SWÀ\PUP€US733f`e^Ý DXÝØÅˆà™Y“39P" R*ª¢ˆˆU˜˜Pˆ€Qff¸µUUеUUîˆœ^îîUVªUZP»U[ʪ ªuSW…PXPîîS3P¾ë»åîffåU Uu5SWÀ\PUP€US733f`e^Ý DXÝØÅˆà™Y“39P" R*ª¢ˆˆU˜˜Pˆ€Qff¸µUUеUUî‰ÌUU¦eVZ¶PU™Æ¦WS5uX€…P®êW¾U[¾ÊªnîUª¥UwVewP`…UU37Pf]»»´XݻݕîàY\ªª\ªª\ffʪ UUY€U\ªªQÏÿðU¸ëUUÐUUለ‰ÌUU¦eVZ¶PU™Æ¦WS5uX€…P®êW¾U[¾ÊªnîUª¥UwVewP`…UU37Pf]»»´XݻݕîàY\ªª\ªª\ffʪ UUY€U\ªªQÏÿðU¸ëUUÐUUሙœÌ^ÌÌÅfUfU`UP`Us7UU€UPnæS[UUµnfnìj¦ªewVewUP…ˆˆ…uUS7P `[¼ª ]»»½ ž™šS—wTZ*ª°ˆ€UUš©]ˆQ@ª U[޵U]µUáY\Y•^îåõª¦eU»°ePUWuUUøUUUðUU[Uffiž¦êæ¥uffWUUUU€…U7US3PPU¬Ì[¼ª UYÀYS7w3TADZªªª»ˆ‹°ª  ª]؈ÝUUDª¤@U¸ëUUÐUPY\Y•^îåõª¦eU»°ePUWuUUøUUUðUU[Uffiž¦êæ¥uffWUUUU€…U7US3PPU¬Ì[¼ª UYÀYS7w3TADZªªª»ˆ‹°ª  ª]؈ÝUUDª¤@U¸ëUUÐUP_™ŸUUŒÅõZfªw[f`UU^UU÷UUØUU\ÅUðUU»»ˆœiî_ª¯Uf§zfPL@UUUUXUXsU3sUUPUZªU¬ÌUX€™Q–É Q À\U) œÀ© ÀšQ À\QàáœÀ[¸ëµ]еU ÀUUUUUUˆ…õUZªwUUfUPUU×}UUPUUPUPPUUUUéÌîåõUUõUªªUUÝÕUUUUUUUX…WwwSPPUUUUU]ÝUUYUœÀUUœÀUUœÀUYÌUªªUœÀUUUYÌU»Žî»Ý»U\ÅUUUUUUˆ…õUZªwUUfUPUU×}UUPUUPUPPUUUUéÌîåõUUõUªªUUÝÕUUUUUUUX…WwwSPPUUUUU]ÝUUYUœÀUUœÀUUœÀUYÌUªªUœÀUUUYÌU»Žî»Ý»U\ÅUYÕYÕ€$USCTRSST$BEUUUU݈UÕYUYUUUUUUˆˆUUˆˆUY•Y•UUUPQ"UXåSuUUUUUYUUUUUUUUUSUXUUUUUUUUˆîåUUUUUUUUUUUUUUUU _XˆUUUUUUUUUUYÕYÕ€$USCTRSST$BEUUUU݈UÕYUYUUUUUUˆˆUUˆˆUY•Y•UUUPQ"UXåSuUUUUUYUUUUUUUUUSUXUUUUUUUUˆîåUUUUUUUUUUUUUUUU _XˆUUUUUUUUUUYÙÕ€TUTUTTTURR%%UTDUUÝ…U••UUUUUY˜‰…\ÈŒ…U•‰UUP°µRUQ%Xé—uUUUUˆø¨eUUVUUUUUÃ3ÀUȈÀUY©UYUUXŽˆïZªªeTDD%Zff`U€€UUðZˆUPµ[P•\ ÙÙ…E%ETTU"R$UUBT@Õ]ØÕYX…•UPPUœ˜ÈˆÎÈ舘ˆˆYUUUUhé—UUUUʪúªfj¦eUUUUU000U€€€U ªX‰˜…^è€Uªª¦DDBUª`U€€UýÝÝX…PPP•\Ì ÙÙ…E%ETTU"R$UUBT@Õ]ØÕYX…•UPPUœ˜ÈˆÎÈ舘ˆˆYUUUUhé—UUUUʪúªfj¦eUUUUU000U€€€U ªX‰˜…^è€Uªª¦DDBUª`U€€UýÝÝX…PPP•\Ì Ù…%TURTEBRE%RRDDDÝÝÝUU˜‰UU œ™ÈŒÎÌ茈™˜‰UUUUU­ÑU]Uø™Æ¦¦ffeU…å…USÃUXÈYª ©Xˆˆ…è¬Ì¦OÿBU¦fU€€U0SðSU…PPÀ Ù…%TURTEBRE%RRDDDÝÝÝUU˜‰UU œ™ÈŒÎÌ茈™˜‰UUUUU­ÑU]Uø™Æ¦¦ffeU…å…USÃUXÈYª ©Xˆˆ…è¬Ì¦OÿBU¦fU€€U0SðSU…PPÀ ÝÙݰUEREETTDE%UUTDDÿDÕˆÝUUY•UPEUÌÌ™˜îîÌȉ˜™™PUQQ%­ÝÑDXXXXøÿÿojªf¥XˆèˆUWwU[»Z­šY™ˆ•ŽŽè€ZªªeTDD%UZ`UU€€U Zð_UˆP° ™À™™ÙÝ…[°%$E%TU%UEååBTDDE…XØÕU•YUUUTUœÌ™ÈÎîÌè‰ØÙ™UPU°QURZÑEX…Uˆ_ú¯ðVUffU]íUUUpUUU°UUʪ¥XUˆˆèîîUUUUUUUUZUePU€€Uú Ð:€P™ÙÉ™™ÙÝ…[°%$E%TU%UEååBTDDE…XØÕU•YUUUTUœÌ™ÈÎîÌè‰ØÙ™UPU°QURZÑEX…Uˆ_ú¯ðVUffU]íUUUpUUU°UUʪ¥XUˆˆèîîUUUUUUUUZUePU€€Uú Ð:€P™ÙÉ™YžíÕ…Z¥$DDUTUBUTUT%EETTXÝUYUU•UUUUYÌÌ•\îîÅ_ˆUUU Q%%U[µUUUUUU_ðUUUeeUUUUUUpUUU°UUDTEUU……Žå^UUUUUUUUZ®n]UíÞU`X`[µ™À™U]ÕUÕUUµUREUUUUUU$"UUUUUÝ…UÕUUUUUUUUU™™UUÌÌU_U_UUUUPURUUU[µUUUUUU_ðUUUUUUUUUUUUUUUUUTEUDUUUUåUîîUUUUUUUUUUeUU]UU]ÐUVPˆeVUUeYÀU]ÕUÕUUµUREUUUUUU$"UUUUUÝ…UÕUUUUUUUUU™™UUÌÌU_U_UUUUPURUUU[µUUUUUU_ðUUUUUUUUUUUUUUUUUTEUDUUUUåUîîUUUUUUUUUUeUU]UU]ÐUVPˆeVUUeYÀ^UUå^UUå_ýßU^èŽUU5SUUUUUU•YU]]UUU[ UPîîU…XUX…U•UXUU…U]ÝUU``UU^^UZeV¥Uú_¥PW5sUTµKU[…¸UV`UUSUQUUQUÕUU]]]UPU•YUUs7U°°U^UUå^UUå_ýßU^èŽUU5SUUUUUU•YU]]UUU[ UPîîU…XUX…U•UXUU…U]ÝUU``UU^^UZeV¥Uú_¥PW5sUTµKU[…¸UV`UUSUQUUQUÕUU]]]UPU•YUUs7U°°U^UUå^å^åÐõõU€ååUU5SUUXˆUYU•UÕÕ\UU° UPîåU…XUîˆ]™R.. PUUWw^åU€€UUåUåZ¥Z¥Uú_¥UUwwuUDDEU»»µUffeU0¥a]EUU]]]UUeU˜‰…US0UU[UUå^U^å^åÝýõUˆèåUS5PX€U˜UUÜÕ\U[µUPÎîìX…PîèUÕU"UUDEUXX€ˆU^^UUÌÌUUZªUõP_UWuwUTEDU[µ»UVefU0U¦¥ÛEUUUØÕUUUeX™™˜µS0[X»¸Uå^U^å^åÝýõUˆèåUS5PX€U˜UUÜÕ\U[µUPÎîìX…PîèUÕU"UUDEUXX€ˆU^^UUÌÌUUZªUõP_UWuwUTEDU[µ»UVefU0U¦¥ÛEUUUØÕUUUeX™™˜µS0[X»¸å^å^åîî^ÝÝ߈ˆŽs0pˆˆˆU˜UU]ÌÅU[ µPîÿîÈ€À^åUUU%U%PPUQUÀ‰€À^U…^eVe\Uÿ¯¥_õW7sWT´KT[‹¸[V`VUSPªfªeÛäUUÕXUÕPfUˆ‰™™W33pˆ‹ˆ€å^å^åîî^ÝÝ߈ˆŽs0pˆˆˆU˜UU]ÌÅU[ µPîÿîÈ€À^åUUU%U%PPUQUÀ‰€À^U…^eVe\Uÿ¯¥_õW7sWT´KT[‹¸[V`VUSPªfªeÛäUUÕXUÕPfUˆ‰™™W33pˆ‹ˆ€åîî^åîî^ÿÿõUîîåU70ˆˆÿˆY™…UUÜUU° Uÿå Œ€UU"URUURPUUÈ™˜À^èˆî¥œÉ\_ÿÿ¯UðUWsWUTKTU[¸[UV`VRC%Zjê]¾EUÕî…ÕPPePøˆ‰•µWp[ˆ‹ˆ€^îîå^îîå_ÝÝÿ^ˆˆîs3pXˆˆ…•UYUUÜUU ° °PÌUȈÀUTB%RUURUUPP\™œUˆè…YÌÌ•__ê_U ­ÿw5UÿDµUÿ»…UÿfU$CBQꦡUÛäU^îèUU`ˆÕUUU{·U[U^îîå^îîå_ÝÝÿ^ˆˆîs3pXˆˆ…•UYUUÜUU ° °PÌUȈÀUTB%RUURUUPP\™œUˆè…YÌÌ•__ê_U ­ÿw5UÿDµUÿ»…UÿfU$CBQꦡUÛäU^îèUU`ˆÕUUU{·U[UUàU^åUU__UU^^U……XX•UU•UUUU ¾°PàîU UTD%U%U%UU_] V UPàUX…P_UUúUPUwçUUDäUU»ëUUfæUUU70UUZQ]ÛäEU^U^UPUUXUU[½Û°_»¿U^åUUàUUUUUUUUPWuUUUUYUU•UUUUµ»»[UåUP\ÅUUDUURRUUUõUPUUl`UUUP ÀEETTUUUUWUUUTUUU[UUUVUUUSUQUݾîDUUîåUU UUUUUU[°UUUUUU^åUUàUUUUUUUUPWuUUUUYUU•UUUUµ»»[UåUP\ÅUUDUURRUUUõUPUUl`UUUP ÀEETTUUUUWUUUTUUU[UUUVUUUSUQUݾîDUUîåUU UUUUUU[°UUUUUUEUUœü•UuuUuUÕUUUµUUUÕUUUUU™Ù••UUU¥UUU5UUUõUUUÅUUU%UUU…UUU37wuU[»µURUUUETUUÅ\UT "Yœy—ÉT "UÅÅZUUY¥YU\•U]UUÕUWuUZUU¥\UUÅ]wwuUEUUœü•UuuUuUÕUUUµUUUÕUUUUU™Ù••UUU¥UUU5UUUõUUUÅUUU%UUU…UUU37wuU[»µURUUUETUUÅ\UT "Yœy—ÉT "UÅÅZUUY¥YU\•U]UUÕUWuUZUU¥\UUÅ]wwuTõUUŸ©•USuWU]õUU[…UU]õUUPeUUÉYUUÿ¯UUw7UUÝýUU™ÉUUD$UUîŽUUfUSwUW[µU[UUÕåUT%UU\•UUE%U™Ç9œUE%UUSZ\UÀU0 U\ÅU]UUÕUUs5ZUU¥UÅ\U×uUÇOEUUöü•U75UUßõUU¾µUUßÕUU UUÜÙ••_U_UWUWU]U]UYUYUTUTU^U^UVUVUs5SW»UU[_UÕUUTBBU\ÉÉBT%EY—ssBT%UZZÅU˜¨ :ª3 U\Ì•ÕÕRUwssUU¥ZUÿ\Åÿ]w\\OEUUöü•U75UUßõUU¾µUUßÕUU UUÜÙ••_U_UWUWU]U]UYUYUTUTU^U^UVUVUs5SW»UU[_UÕUUTBBU\ÉÉBT%EY—ssBT%UZZÅU˜¨ :ª3 U\Ì•ÕÕRUwssUU¥ZUÿ\Åÿ]w\\ÿôUU¦¯ÉU3wUWßýUUŽ‹UUÿýUUj`UUÌÍ™YU_UUUWUUU]UUUYUUUTUUU^UUUVUU3U3U»UVµU]_UUEEUUÅÅUED"YœyyœED%UU<¬<À™< À™ÌUYÉ•Õ]ÕÝs75U¥Z¥Z\ÌÅÅUÓÀ\ÿôUU¦¯ÉU3wUWßýUUŽ‹UUÿýUUj`UUÌÍ™YU_UUUWUUU]UUUYUUUTUUU^UUUVUU3U3U»UVµU]_UUEEUUÅÅUED"YœyyœED%UU<¬<À™< À™ÌUYÉ•Õ]ÕÝs75U¥Z¥Z\ÌÅÅUÓÀ\_ÿEUÊjü™SwuU]ÿÕUXèµU_ÿÕUV¦UœÌÙ•ÿZU_wSUWÝ_U]™\UYDRUTîXU^fPUVws5U[µUUUÕUWUUTÕUU\åDB%U™É—ùDB%UUS¬Z <ª 0¡¡UÌÅ_]ÝÝUwsW3¥ª™ZUÌÌUÌ\0ÅUDÿEœúªüU3s5UßýåU¸¸µUßÿÕUf™ÜÌÙU¥_¥U5W5Uõ]õUÅYÅU%T%U…^…UVW3wÌU»»µU]ÕUUUUUUUUUUMUYY™œÉUNTUU¥ÅZZœ£ÀZÃUUÏÿUÕU]W35UZ¯™ÿ\^åÅUÌÌUDÿEœúªüU3s5UßýåU¸¸µUßÿÕUf™ÜÌÙU¥_¥U5W5Uõ]õUÅYÅU%T%U…^…UVW3wÌU»»µU]ÕUUUUUUUUUUMUYY™œÉUNTUU¥ÅZZœ£ÀZÃUUÏÿUÕU]W35UZ¯™ÿ\^åÅUÌÌTDôEϪ¯©Swwu^ÝÝå[ˆ»…]ÿýÕP`ÌÍÙU_ÿUUWwUU]ÝUUY™UUTDUU^îUUVfUUWwÌ[µU»WUUUUUUUUUUUUUUUYY™™UUUEUUUUU–0UU\ UU\3ÿ]U¿µWww3YÅY•\\\ÀÅDDODÿÿúÿwwwwíÝíÞ»»¸»ÝÝßÝÝÝÜÝUUU¥UUU5UUUõUUUÅUUU%UUU…UUUUUSS[»»µUÕTUUUUUUUUUUUUUUU••UUUEUUUUUUUUUYUUS5õUÝÛ»UWóUU™•UÅUU\ÅP\DDODÿÿúÿwwwwíÝíÞ»»¸»ÝÝßÝÝÝÜÝUUU¥UUU5UUUõUUUÅUUU%UUU…UUUUUSS[»»µUÕTUUUUUUUUUUUUUUU••UUUEUUUUUUUUUYUUS5õUÝÛ»UWóUU™•UÅUU\ÅP\R!$Ejj¦ úú¯¯ÅUU\$$BAÉÉœœP üüÏÏ7CK¶YY•™UYUÌÅÌUfeVe_UUõfeVeUUUUUUUUUUUUÌÌù™UõõUUð_Uð__UUõ_UUõUUUUUUUUUZúUUð_UUUUUUUUU…UUUEUUR!$Ejj¦ úú¯¯ÅUU\$$BAÉÉœœP üüÏÏ7CK¶YY•™UYUÌÅÌUfeVe_UUõfeVeUUUUUUUUUUUUÌÌù™UõõUUð_Uð__UUõ_UUõUUUUUUUUUZúUUð_UUUUUUUUU…UUUEUUUREUf ¦ ÿ¯¯¯\Å\Å"@BAÌœœœU UÿÏÏÏtÙ”¶Y•ªªèn†èÌUÌUVfVeõUUõVfVeUõ_U_U_UZUZUÅUÿUUÿUUUð_Uð_Uõ_UUõ_UUÿõUUïïUU ð¥Uð__UUõ]UUÕXåUUT%UUURDÅj ª ú¯ª¯U˜˜U$@DAÉŸ™œYüÏÌÏIŽvU™ªªÊª€\ÌÌÅeffe_õ_UeffeU_ú¥Uÿú¥Uª¦e\ÿöÅU¯ÿUð_ðúð_õõ__Uõ_U_ª¯U_÷~åUÿJð_UjªUUŽîU…UUAEUUURDÅj ª ú¯ª¯U˜˜U$@DAÉŸ™œYüÏÌÏIŽvU™ªªÊª€\ÌÌÅeffe_õ_UeffeU_ú¥Uÿú¥Uª¦e\ÿöÅU¯ÿUð_ðúð_õõ__Uõ_U_ª¯U_÷~åUÿJð_UjªUUŽîU…UUAEUUU]-Åj  úúª¯UY…U$AÉù™œ üüÉÏݘ”6Y•jªh™`U\Ì\VffeÿÿÿUVfnåUÅ\U\U\U\U\UUÿoU_^õUð_ð_ð_ð_õ_õ_õÿÿ_Z¯õõ^s7õ_ÿÿ@R TöªÿöØîíèíèUU!$UUU]-Åj  úúª¯UY…U$AÉù™œ üüÉÏݘ”6Y•jªh™`U\Ì\VffeÿÿÿUVfnåUÅ\U\U\U\U\UUÿoU_^õUð_ð_ð_ð_õ_õ_õÿÿ_Z¯õõ^s7õ_ÿÿ@R TöªÿöØîíèíèUU!$UUÌÄ$Åj ª`úªªÿUœÉU$@D!É™™Ì üÌœÿ}GfÌš¦ªh`ÌÅUÌUUfeUUÿ_UUfe\UUU\UUU\UUUU_ÿÿUUUUðª _ð_ð_õ_õ_õ_õ_ZúõU_çåUð@R TZ¯ÿ¥^íÝå^Þ…UTEUuÍÍÅV™Ê`_ÿÊðÌÌÌÌR™Ä \ÿÉð _ÿÌðWÿà_fª¥V`eÌÌÌÅUUfªUU_þUUfªUUUUUUUUUUUUUU\UUUU_ïêðÿ  ÿõZ¥_õZ¥_ZúõUUïþUUÿÿõDDõªú_Õîî]Uˆî…UB"EuÍÍÅV™Ê`_ÿÊðÌÌÌÌR™Ä \ÿÉð _ÿÌðWÿà_fª¥V`eÌÌÌÅUUfªUU_þUUfªUUUUUUUUUUUUUU\UUUU_ïêðÿ  ÿõZ¥_õZ¥_ZúõUUïþUUÿÿõDDõªú_Õîî]Uˆî…UB"EUÜÍUUÌÉUÌÏÉ œUÌÉUÌÏYÌÉ•UÌÏUÌÏ\ü¯UU^UUUUUUeUoUUUUUeUoUUUUUUUUUUUUUUUUUUUUUÿðUUïþU_ïêõõïê_UZUUUUUUU_ÿUUïþUõUU_ÕUU]Xˆè…T"$EU]ÕUU™UUÿðUÌÌU™UUÿðUU UUÿðUUÿðUUÏõUU_ªUUUUUUVf_UUUUUVf_UUUUUUUUUUUUUUUUUUUUUUUUU_ðUU_õU__õõUUUUUUUUUUUUU_ðUUUUUUUUUˆˆŽˆDDBDU]ÕUU™UUÿðUÌÌU™UUÿðUU UUÿðUUÿðUUÏõUU_ªUUUUUUVf_UUUUUVf_UUUUUUUUUUUUUUUUUUUUUUUUU_ðUU_õU__õõUUUUUUUUUUUUU_ðUUUUUUUUUˆˆŽˆDDBDUY™X…UUX…UUYU%RUUYYUYUUYŽŽèàYUYU¦¦jjss77YUYUYUYUUYYUYUYUYUYUYUUYUYUYU"U"D"UV••YYUYYUYUZ¥U™``YUYUè莎UYUBB$$UY™X…UUX…UUYU%RUUYYUYUUYŽŽèàYUYU¦¦jjss77YUYUYUYUUYYUYUYUYUYUYUUYUYUYU"U"D"UV••YYUYYUYUZ¥U™``YUYUè莎UYUBB$$ 0XX•U™UUpU%RU 0QQU 0ˆèèàX…XUªjjjw777QQQUQU¥0¸‹¸»X…XUX…X…ˆh†ˆQUQU!RR"BPU™YYY¥0VeV©UZ¥fXUXUîŽŽŽˆh†ˆD$$$À33 X€Y••Uʪw Å!\À33 UUÀ33 ŽèîàUˆˆU¦ofjs?37QUQU 33»ˆˆµUˆˆUX…ˆUʪ€QU!AU!$UU•\UY 33ÕV`UÊ•ªY` X…ˆU艈ŽÊª€B/"$À33 X€Y••Uʪw Å!\À33 UUÀ33 ŽèîàUˆˆU¦ofjs?37QUQU 33»ˆˆµUˆˆUX…ˆUʪ€QU!AU!$UU•\UY 33ÕV`UÊ•ªY` X…ˆU艈ŽÊª€B/"$©– QUY»¼0™Ì…!V©– ʪU©– ŽˆŽàʪ…U¦öfjsó37ʪ¥Uʪ¥UÀ٠ʪ¥UʪªUʪªUh™`ʪª¥!"µ"!"Pªª¥•ÅUYÀٜʪª¥•Z¥Y` ʪªªè˜ˆŽh™`Bò"$©– QUY»¼0™Ì…!V©– ʪU©– ŽˆŽàʪ…U¦öfjsó37ʪ¥Uʪ¥UÀ٠ʪ¥UʪªUʪªUh™`ʪª¥!"µ"!"Pªª¥•ÅUYÀٜʪª¥•Z¥Y` ʪªªè˜ˆŽh™`Bò"$Vî©`™É™¥QUZ033ehhXVi©`•ˆ…\Vi©`Žèˆ…\¦ffªs33w刅\\©– ›U•\E\h`刅\RÿÁ»+"±UU•UU™©– e\Yªª•`fE\興îh`B""DUPÀUUœUYUÅZSî30V††…UPÎU™ˆ‰•UPÂUX™Î€™ˆ‰•ZÿÆðWÿÃðîÞåVi©`™•™•DÔEV`eåUÌDEQ»"U\ÀYÿÅÀVi©`f ÖeZ\Å¥VÿÀ DE^ÿÈV`eTÿÂðUPÀUUœUYUÅZSî30V††…UPÎU™ˆ‰•UPÂUX™Î€™ˆ‰•ZÿÆðWÿÃðîÞåVi©`™•™•DÔEV`eåUÌDEQ»"U\ÀYÿÅÀVi©`f ÖeZ\Å¥VÿÀ DE^ÿÈV`eTÿÂðUYUUPY•UY•UU\ÀUU\ÅUU^àUˆœ‰UUR UUÌɈœ‰UUÌÏUÌÏUœUUPÀUœµÅœUDœUUVUUTD!UR%ÛÛUPPUÌÏUPÀUfœUU©šUUÌÏDœUUÌÏU] UUÌÏUUUUUU•UUUUUUYUUY•UUUUUYÌUUUUUUU™UYÌUUUÿðUUÿðUPÌUUPÌUUUYUYÌU\YÌUUUÙUUUVU]ÌUUUUD!»U[µUUUPUÿðUUYUP UUUZ¥UUÿðUPÌUUUÿðUUVUUÿðUUUUUUU•UUUUUUYUUY•UUUUUYÌUUUUUUU™UYÌUUUÿðUUÿðUPÌUUPÌUUUYUYÌU\YÌUUUÙUUUVU]ÌUUUUD!»U[µUUUPUÿðUUYUP UUUZ¥UUÿðUPÌUUUÿðUUVUUÿðUUÅ\UUYUYUYUYUYUYUYUYV`UV`UV`V`U`VU`VU`VUQV`UU0SXUU•Y•UYUYUYUY¯¯úö !U™\UUYUY !¯¯úöUYUÅ\UUYUYUYUYUYUYUYUYV`UV`UV`V`U`VU`VU`VUQV`UU0SXUU•Y•UYUYUYUY¯¯úö !U™\UUYUY !¯¯úöUYUÌ\UUÀUÀUÀUÀUÀUÀUÀUÀÅfÅÅfÅVff`U`VUU`VUU`VUUQV`UP0XUUå^UU0SU0SU0 0ªðúö"Y™šUUpˆ¨"ªðúöU0\YYÅšª šª šª šª šª šª šª šª  ª  ª UªªÅff\Åff\Åff\UÅfÅÀ33 XˆUÅîî\ʪ3¥Êª3 ʪ3 À33 ¯ðÿö!Z¦`lʪw ʪª!¯ðÿöʪ3 \YYÅšª šª šª šª šª šª šª šª  ª  ª UªªÅff\Åff\Åff\UÅfÅÀ33 XˆUÅîî\ʪ3¥Êª3 ʪ3 À33 ¯ðÿö!Z¦`lʪw ʪª!¯ðÿöʪ3 \\ÅÅÀff À™Ì €™ˆ 0™3 @™D à™î p™w @îD Zª U^ê U_ªª Q•™™Y•™™YUý߉©– ÎîUEØT™Êª™Ì™Ì©– ¯ö!AUª¥U0™Ì¨™Æ¦!A¯ö™Ì\\ÅÅÀff À™Ì €™ˆ 0™3 @™D à™î p™w @îD Zª U^ê U_ªª Q•™™Y•™™YUý߉©– ÎîUEØT™Êª™Ì™Ì©– ¯ö!AUª¥U0™Ì¨™Æ¦!A¯ö™ÌYœ• ¦f ¬¬  ¨¨ ££ ¤¤ ®® §§ ®äUÌUUÌUõ\À_QY™™•Y™™•_ffðX˜UVi©`îîèÌT!Eªjª‘Vi©`¯ðÿ¦!"UUœÅ033¨ªªj.á"îðÿ¦\ÌZ™–`ZÊÊÀZŠŠ€Z::0ZJJ@ZêêàZzzpZJî@UÝUUÝUÅÜÍ U\ÀUUX€UU\ÀUU\ÀUUÌUU\ÀUUÌ}U¼ËUQª¦ Q™QUPÀUZ™Ï RÿÁ@UUUUS330Zªª RÿÁ@Z™Ï Qî\ÌZ™–`ZÊÊÀZŠŠ€Z::0ZJJ@ZêêàZzzpZJî@UÝUUÝUÅÜÍ U\ÀUUX€UU\ÀUU\ÀUUÌUU\ÀUUÌ}U¼ËUQª¦ Q™QUPÀUZ™Ï RÿÁ@UUUUS330Zªª RÿÁ@Z™Ï QîUÉUU™U™U™U™U™U™U™U™UUUUUUUUUU]ÐUUZ¥UUZ¥UUÝÝUÜÍU™UUYUU™0U[¾åU\ÀUUìÀUU\ÀUUYUUÌÉUÌÏUUUUU\ÀUUZ UUÌÏUÌÉU\ÀUUUUUUUUUPUPUPUPUPUPUPUUUUUUUUUUUUUUUUUUUUUU]ÐUU]ÐUUUUUUUUUUUUUUUUUUZ UU^àUUYUUUUUU™UUÿðUUUUUUYUUZ UUÿðUU™UUYUUUUUUUUUPUPUPUPUPUPUPUUUUUUUUUUUUUUUUUUUUUU]ÐUU]ÐUUUUUUUUUUUUUUUUUUZ UU^àUUYUUUUUU™UUÿðUUUUUUYUUZ UUÿðUU™UUYU興ˆfU55UU5SUU5¥5î(¨(UUUUUUUUjª¦ªÍÝÜÝœÌÉÌ7wswÉ™œ™GwtwŽîèîK»´»`VW335^ˆˆ…]wwuZffe]wwuU5SUV`UU5SUUYÅUU\UYUYUYUY興ˆfU55UU5SUU5¥5î(¨(UUUUUUUUjª¦ªÍÝÜÝœÌÉÌ7wswÉ™œ™GwtwŽîèîK»´»`VW335^ˆˆ…]wwuZffe]wwuU5SUV`UU5SUUYÅUU\UYUYUYUY^興UfUS5UZ£SUUZU5^ŠˆUUõUU™™V¦ªf\ÜÝÌYÉÌ™Ssw3\œ™ÌTtwDXèîˆT´»DVf`UPs5USè…UX×uUW¦eUV×uUWZ£SU–f`•Z£SUUÀ\Å\ÅUÀUÀUUÀUîèˆUUU`U33Uªª5US£SUR¢UUõUUUUUUjjeUÍÍÅUœœ•U775•ÉÉÉUGGEUŽŽ…UKKEU``eVUW33U^ˆˆU]wwUZffU]wU\ªª•U ¬ ªªÕUšª U––Ušª šª ª šªª UîèˆUUU`U33Uªª5US£SUR¢UUõUUUUUUjjeUÍÍÅUœœ•U775•ÉÉÉUGGEUŽŽ…UKKEU``eVUW33U^ˆˆU]wwUZffU]wU\ªª•U ¬ ªªÕUšª U––Ušª šª ª šªª UUXˆPUPUYœUZ£33£S5S¢‚QUUõU™UUUfVUUÌ\UU™YUU3SUU•œYUDT]UˆXUUDTUUfVUUU`UUs5UUè…UU×uUU¦eUÓ5\Z©—wZÊÀUZ­×wÀ™Ì UYeUÀ À"" Ì™À™Ì UUXˆPUPUYœUZ£33£S5S¢‚QUUõU™UUUfVUUÌ\UU™YUU3SUU•œYUDT]UˆXUUDTUUfVUUU`UUs5UUè…UU×uUU¦eUÓ5\Z©—wZÊÀUZ­×wÀ™Ì UYeUÀ À"" Ì™À™Ì UU^…UUVUUUUUY™ZS355R"%UõUõUUUUUUUUUUUUUUUUUUUUYU™Y]UÕUUUUUUUUUUUUUYÅVYÅW9YÅ^€YÅ]wYÅZ`Ì\5ÅUWuZU™UUWuZ ¬¬ UœÉU aa  bb  i©  Ý¬ Uîè…PUVUUUUUÅÅZU33UUâUUUUUUUUUUUUUUUUUUUUUUUUU•UUUUÕUUUUUUUUUUUU\Ì`\Ìs•\Ìè\Ì×\̦UÌÌUUUUZU™UUUUUZZ™šÀ\™œÅV™–V™– V Z™šÀUîè…PUVUUUUUÅÅZU33UUâUUUUUUUUUUUUUUUUUUUUUUUUU•UUUUÕUUUUUUUUUUUU\Ì`\Ìs•\Ìè\Ì×\̦UÌÌUUUUZU™UUUUUZZ™šÀ\™œÅV™–V™– V Z™šÀUˆ…UUUUUUUUUUUUUUY™™UY™™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUÕUUUUUUUUUUUUUUPUY™™UPUPUPUU|Å5UUUUUUUUUUUUU™UÉ™œU™UU™UUUU™UUUUUUUUUUUUUUUUUU\\UU\\UUUUUU™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUSSUUUUUUUUUUUUUUUUUÅUU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\\UU\\UUUUUU™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUSSUUUUUUUUUUUUUUUUUÅUU\UUUUUUUUUUUUUUUU"ò"òw575U»[µUDTES00Uî^åU7»»UõZUU¥VUUõZTU¥VUUuSUUõZUUERUj¯ÿÿeUUUVjªªýÝÝÝ$DDUUUU37wwˆŽîî»»Š®ïÿœÍÝݪªj¦UUUU"ò"òw575U»[µUDTES00Uî^åU7»»UõZUU¥VUUõZTU¥VUUuSUUõZUUERUj¯ÿÿeUUUVjªªýÝÝÝ$DDUUUU37wwˆŽîî»»Š®ïÿœÍÝݪªj¦UUUUP!ÿ%Pîÿ%¦fjPTDDE^îîåU3[»»µPVkºUVZUZjf¥UVJUUPVUUSSUUVZUUQRUjÿÿÿeUUUVª¯úýÝÝÝDDDUUUU3wwwˆîîî!»²+hïÿîœÝÝݦfUfUUUUUU_ÿñU_ÿñ fªUf]ÝDE]ÝîåU3U]Ý»µUPfºUj¯_ZeUjUj¯OUjZU37WUj¯_U$TV¯ÿÿe333Pj¯ú_ÝÝÝQ$DDQ!"%S7wwXŽîîR²+V®ïÿYÍÝݦUUVPUU_ÿñU_ÿñ fªUf]ÝDE]ÝîåU3U]Ý»µUPfºUj¯_ZeUjUj¯OUjZU37WUj¯_U$TV¯ÿÿe333Pj¯ú_ÝÝÝQ$DDQ!"%S7wwXŽîîR²+V®ïÿYÍÝݦUUVPUjðUUÌDEUUÌDE`ÅUjöjRÝÔEXÝÞåUS0UQÝÛµUPk¥ªÿ¥¦UUe¥ªô¤efªe53w5¥ªÿ¥%"D%UoÿõS3w35j¯¥UýÝÕUDE$B"U7wuUŽîåU+²µUÿõUÝÕ¦VU`UjðUUÌDEUUÌDE`ÅUjöjRÝÔEXÝÞåUS0UQÝÛµUPk¥ªÿ¥¦UUe¥ªô¤efªe53w5¥ªÿ¥%"D%UoÿõS3w35j¯¥UýÝÕUDE$B"U7wuUŽîåU+²µUÿõUÝÕ¦VU`! õTD!UUUTD!UÅÌU!¦ª",ÄEˆŒÎå!3õ˵ UUÌfVZßÕZfÖÕVZßÕPVÚÕSS×ÕVZïåQRÔÕUZ¯õUS3uUVj¥U_ýÕUR$EU"$UUS7uUXŽåUQµUZŽõU\ÍÕfUV`UU¥b/UUUD!Q UUD!¥_ªU¥b%ª"^îEˆ[»å¥R/UTDµ¥R U_ªÌUUjUUU¦UUUjUUUUUU3UUUjUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjªª`UUUU¥b/UUUD!Q UUD!¥_ªU¥b%ª"^îEˆ[»å¥R/UTDµ¥R U_ªÌUUjUUU¦UUUjUUUUUU3UUUjUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjªª`UUUU¥_õUUUUUQUUUU¥UUUõ_õUÌUåÌÌUµÌ¥_õUÌUEÌUPUUU¥UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUfffUUUU¥UUUUUUUUPUUUUUUUUUõUUUUU^UUU[U¥UUUUUTUUUU…UUªªUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU¥UUUUUUUUPUUUUUUUUUõUUUUU^UUU[U¥UUUUUTUUUU…UUªªUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUõ_UUeVUUÕWUU\ÅœUUUUUUUUUUUEUUUUUUUUUUUUU^îîåU¥ZU\UU\V`UPÐÐUPÐÐUV`UUUPYÅYÅZ•V•V¥V¥PePeV¥V¥REREVõV¥YÅYÅV¥V¥ÿªo¥V`V`V`PPõ_UUeVUUÕWUU\ÅœUUUUUUUUUUUEUUUUUUUUUUUUU^îîåU¥ZU\UU\V`UPÐÐUPÐÐUV`UUUPYÅYÅZ•V•V¥V¥PePeV¥V¥REREVõV¥YÅYÅV¥V¥ÿªo¥V`V`V`PP_õ¯UVeU]u2UUY™ÅUDDUUwwUUÿÿ^UïïUU[»UUZ:UïÿÿþZUZUÅUUÅeVeUÕ]ÕUÕ]ÕUeVePUYÉœÅV–©•V¦j¥P`e¦¦jjUETUV¦ZõYÉœÅV¦j¥UªfjÆff`Vf`UPUZªõUPfeUS"EÅÅœÅTB$EWs7u_û¿õ_ùžå[°S0ÿs7ÿZ¥ZUÌÅ\UVfU]ÐU]ÐUf`UPPlÉÉœœššii¦¦jj``jj¦jEB%RööÿjIÉœœ¦¦jj_ÿ¦jõÌfUªffUUUUZªõUPfeUS"EÅÅœÅTB$EWs7u_û¿õ_ùžå[°S0ÿs7ÿZ¥ZUÌÅ\UVfU]ÐU]ÐUf`UPPlÉÉœœššii¦¦jj``jj¦jEB%RööÿjIÉœœ¦¦jj_ÿ¦jõÌfUªffUUUõõ¯õefeµå$µÉÌœUT!EW0u° õ^ õ» »»::ªÿ0ÿ¥ªªÚ\ÌÌÌVUUP UUP UUVPfPœœÉœ©i–©jj¦j`jª¦ª$TETjjöoœœÉœjj¦jUUúj_ªª`_ª¦`UPUUõõ¯õefeµå$µÉÌœUT!EW0u° õ^ õ» »»::ªÿ0ÿ¥ªªÚ\ÌÌÌVUUP UUP UUVPfPœœÉœ©i–©jj¦j`jª¦ª$TETjjöoœœÉœjj¦jUUúj_ªª`_ª¦`UPUUúÿ¯U`fU¸î‹UœUÎUTB$EWs7u_û¿ò_éŸå»»ÿ»::ÿ£ÿs7ÿ¥Z¥ZÉ™™ÜU`UUÝÐUUÝðõ`UPUœÌÉÌi™–™jª¦ªf`fV®ê¥TDE$jÿö¯œÌÉÌjª¦ªeU_¦U\ÀUõ\ÀU!õ!U¯UõUUeU+U»UYP¬îUDDUUwwUUÿÿUUïþU[»»µS£:5ïÿÿþZUUUÅU\\Uff`U Uÿýff`\`YÉœÅZ–©•V¦j¥P`eUZ¥URB$EV¦jõYÉœÅf¦jª¦U_¦UYUÅZ U¥R/U¥R%U¯UõUUeU+U»UYP¬îUDDUUwwUUÿÿUUïþU[»»µS£:5ïÿÿþZUUUÅU\\Uff`U Uÿýff`\`YÉœÅZ–©•V¦j¥P`eUZ¥URB$EV¦jõYÉœÅf¦jª¦U_¦UYUÅZ U¥R/U¥R%UZUªúPUfRUˆèU ßôUUUUUUUU[UUUUUUUµµ[[55ZZ^îîåUUUUÅUUUUVÐPU]ýPU]ýPPVÐPV ÅU\ÅUUY•UUZ¥UUVeUUUUTU]ÕUEZõUU\ÅUVnê¥úU¦jUUUU¥UUU¥_õUU_õUUUUUUUUUUUUUUÝÌUUUUUUUUUUÕUUUUUUUUUUUUUUUUUUUUU\UUUUUUÕÕUUÕÕUUV` ÅU\ÅUUY•UUZ¥UUVeUUUUUTTEUUZõUU\ÅUUZ¥U_ªf_UUUU¥UUU¥UUUUUUUUUUUUUUUUUUUUÝÌUUUUUUUUUUÕUUUUUUUUUUUUUUUUUUUUU\UUUUUUÕÕUUÕÕUUV` ÅU\ÅUUY•UUZ¥UUVeUUUUUTTEUUZõUU\ÅUUZ¥U_ªf_UUUU¥UUU¥UUUUUUUR"DUXˆîUPfUU™œUUwuUUwuUDDB%ÿÿú¥wws5ff`îîè…UUUUDDs5T@T_ð_WpW]Ð]UUPUUVªUU_ÝUYÍÿUUYœV`V¥ZUU]ÐT_ð_ETUUuWUUÕ]UU^à^U^UUõ_UUR"DUXˆîUPfUU™œUUwuUUwuUDDB%ÿÿú¥wws5ff`îîè…UUUUDDs5T@T_ð_WpW]Ð]UUPUUVªUU_ÝUYÍÿUUYœV`V¥ZUU]ÐT_ð_ETUUuWUUÕ]UU^à^U^UUõ_UU$UREŽUXåUPeYUU•UwuUUwuUTD"U_ÿªUWw3UVfU^îˆUUUwUWt"UU$@UU¯ðUU7pUUýÐUUPUUZeUU]õUU_ÕUU\œUU`UZ¥úUUÍpUUßðUTE$UWu7U]ÕýUUŽàUUUŽU_õßU%%U$……UŽU•™ÉYUWUUUWUUB$D%ú¯ÿ¥s7w5`fèŽî…UWUus$KµR$BZ¯úS7s_ýßUUUU¥UUUÕUUUõUUUÅÅ\P`U_ÿ¥R7s]ßýUR"EUS3uU_ÿÕXŽèŽXˆåU]Ýõ%%U$……UŽU•™ÉYUWUUUWUUB$D%ú¯ÿ¥s7w5`fèŽî…UWUus$KµR$BZ¯úS7s_ýßUUUU¥UUUÕUUUõUUUÅÅ\P`U_ÿ¥R7s]ßýUR"EUS3uU_ÿÕXŽèŽXˆåU]ÝõUU%$UU…ŽUUÉU\\UwuUUwuUBUTBúU_úsUWs`UV`èU^èUWUUÜU[¸"$Bª¯ú37sÿýßUU^UªUZUÿU_UýU]UÅ\Ì`¥¥ú¥3$BÝßýEE$Euu7uÕÕýÕˆŽèåUŽåõõßõUU%$UU…ŽUUÉU\\UwuUUwuUBUTBúU_úsUWs`UV`èU^èUWUUÜU[¸"$Bª¯ú37sÿýßUU^UªUZUÿU_UýU]UÅ\Ì`¥¥ú¥3$BÝßýEE$Euu7uÕÕýÕˆŽèåUŽåõõßõURREUXXåUPPeUU•YUwuUUîuUTUUE_UUõWUUuVUUe^UUåUUuW]UUå"D@UªÿðU3wpUÿÝÐUUPUVªeU_ÿõU]ÝÕU\U\f`U¯ªúUsK°UÝÿðUBD$Usw7UßÝýUˆîàUU^ŽUýÿßURU"TXUˆ^``VUÉU•[WUµ[WUµUUTUUU_UUUWUUUVUUU^UUuWuUU[UD@$@ÿð¯ðwp7pÝÐýÐPUUZU¦U]UÿU_UýU\UÌÅf``úU¥U×@¸àÿðßð$UEU7UuUýUÕUîàŽàŽUåUßUõURU"TXUˆ^``VUÉU•[WUµ[WUµUUTUUU_UUUWUUUVUUU^UUuWuUU[UD@$@ÿð¯ðwp7pÝÐýÐPUUZU¦U]UÿU_UýU\UÌÅf``úU¥U×@¸àÿðßð$UEU7UuUýUÕUîàŽàŽUåUßUõUR"EEXˆåå`Pee\UYUµçåµµçåµUUDDUUÿÿUUwwUUffUUîîUWuWUU»DUUUUUUUUUUUUUUUUUUU¦UeUßUõUýUÕUÉUÅUUUU_UªúUUUUUUUURU"BSU3s_UÿßUUUUXUˆè]UÝýUTUUU^UUePUUU™ÅU[[[U[[[UUUUUUUUUUUUUUUUUUUUUUUuWUUUUUUUUUUUUUUUUUUUUUPUUVUeU_UõU]UÕU\UÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUU^UUePUUU™ÅU[[[U[[[UUUUUUUUUUUUUUUUUUUUUUUuWUUUUUUUUUUUUUUUUUUUUUPUUVUeU_UõU]UÕU\UÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUùYüŸ¥U¥ZõUõ_EUETÅUÅ\uUuW•U•YuUuWUQUP…U…XÕUÕ]¥U¥ZeUeVÉYÉœPPPUUVeUUV¥UUX…U5WuSUREUUZ¥U…^åXVfª¥UUUUUUUUUUUUUUUUUUUU]ÝÝU_ÿÿUS3wUùYüŸ¥U¥ZõUõ_EUETÅUÅ\uUuW•U•YuUuWUQUP…U…XÕUÕ]¥U¥ZeUeVÉYÉœPPPUUVeUUV¥UUX…U5WuSUREUUZ¥U…^åXVfª¥UUUUUUUUUUUUUUUUUUUU]ÝÝU_ÿÿUS3wU¬œüÏeU¥Z¥Uõ_%UET•UÅ\5UuWÅU•YÕUuWUQeUPµUå^åUõ]õU5ZUeVٙɜP`Põjÿ_刈^Sw35E$ô_e¦PXfª¯úUUUUUUUUUUUUUUUUUUUUÝU]ÕÿU_õ7USuªÿÿüfªª¥ªÿÿõ"DDE™ÌÌÅ3wwuÌ™™•Ýwwuf»Žî…îßÿÕÿª£¥ffeÝÌÌÉ™UPPP`_¯ÿõXˆîåUs3U_OOEP¦`eUèˆUjªÿúUUSUUUUUUUUUUUUUUUUUÕÕUÝõõUÿ55U7ªÿÿüfªª¥ªÿÿõ"DDE™ÌÌÅ3wwuÌ™™•Ýwwuf»Žî…îßÿÕÿª£¥ffeÝÌÌÉ™UPPP`_¯ÿõXˆîåUs3U_OOEP¦`eUèˆUjªÿúUUSUUUUUUUUUUUUUUUUUÕÕUÝõõUÿ55U7ʯúªVj¦fZ¯úªR$B"YœÉ™S7s3\ÉœÌ]×}ÝPV`f[¸è‹^íýÞ_B§þP`ÜÍÝUUUUUVUUZõUUX…U3s33UTõUUVUˆèˆˆj¯ÿúU^åUUUÅUUU%UUUåUUàåUUUÕÝUUõÿUU57ʯúªVj¦fZ¯úªR$B"YœÉ™S7s3\ÉœÌ]×}ÝPV`f[¸è‹^íýÞ_B§þP`ÜÍÝUUUUUVUUZõUUX…U3s33UTõUUVUˆèˆˆj¯ÿúU^åUUUÅUUU%UUUåUUàåUUUÕÝUUõÿUU57üœÊ¯¥UVjõUZ¯EUR$ÅUYœuUS7•U\ÉuU]×UPUV`…UX¸ÕU]í¥U_úeUPÉ™ÜUˆU™fVÿ_õÿî^åîUW5UDTEÿfVeU^…UVª¯¥UÅ\UU\\UURRUU^^UUPUU]]ÕU__õUSSuÉœüüUU¥¥UUõõUUEEUUÅÅUUuuUU••UUuuUUUUUU……UUÕÕUU¥¥UUee•YÉÉUUUUUePUUõ_UU…^UU5SUUE_UUePUU…XUõ_¥_ÌUUÌ\ÅUÌR%U"^åUîUU]UÝ]_Uÿ_SU3WÉœüüUU¥¥UUõõUUEEUUÅÅUUuuUU••UUuuUUUUUU……UUÕÕUU¥¥UUee•YÉÉUUUUUePUUõ_UU…^UU5SUUE_UUePUU…XUõ_¥_ÌUUÌ\ÅUÌR%U"^åUîUU]UÝ]_Uÿ_SU3W•YÉÉUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU••™U™UUPUUõ_UUå^US5S5Uõ_UUPUX…X…¥¥__UUUUUUUUUUUUUUUUUUUU]ÝÕÕ_ÿõõS3uuUU••UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU5SUUUUUUUUUU…XUUõ_UUUUUUUUUUUUUUUUUUUUUU]UUU_UUUWUUUU••UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU5SUUUUUUUUUU…XUUõ_UUUUUUUUUUUUUUUUUUUUUU]UUU_UUUWUUj`PUPe5U5UUUZUUUUUUUVUUUUUPUUUPUPUUV`j¥UUZU\UÅUUUVUUUYUf UUQUZ¥ªZUUUUVeVeUUQUU¥UeUõU¥¥UUõœUYÅõUõ_UP¥U¥ZåUå^EUETj`PUPe5U5UUUZUUUUUUUVUUUUUPUUUPUPUUV`j¥UUZU\UÅUUUVUUUYUf UUQUZ¥ªZUUUUVeVeUUQUU¥UeUõU¥¥UUõœUYÅõUõ_UP¥U¥ZåUå^EUET`UUUUPeU05UUZõ¥U UUV¥eUUPeUPUUUV¥UUZõ¥\ÌUUUV¥eUYÅ•™–`UQ%Zªõ¥UPU ™UQµUZZUU__U_U_U\U\UõUõ_UPõU¥Z…Uå^%UET`U`UPfð`U¥U¯_U`U¥ UÅUjZU`U¥UVUUPUPPUUVªÅU¯_U\ÅUUUjZUUœ\U©™`™URUU¯_UUP`UUZfUU_ªUõ¥õ_Å•Å\ÿÿÿõÿªª¥ˆîÞåBDôE`U`UPfð`U¥U¯_U`U¥ UÅUjZU`U¥UVUUPUPPUUVªÅU¯_U\ÅUUUjZUUœ\U©™`™URUU¯_UUP`UUZfUU_ªUõ¥õ_Å•Å\ÿÿÿõÿªª¥ˆîÞåBDôEUŽ€UZ ðV Å5õ¯õUV ¥eY Å•¥j¥UV ¥eeeUUUUP`Åõ¯õU\UUU¥j¥UÅœÅU %%UU¯ÿUUPµµUUª¦fUÿúª¥_UZ•\UY_ÿú¯P`_ú¯ÿXÝèT/OBUŽ€UZ ðV Å5õ¯õUV ¥eY Å•¥j¥UV ¥eeeUUUUP`Åõ¯õU\UUU¥j¥UÅœÅU %%UU¯ÿUUPµµUUª¦fUÿúª¥_UZ•\UY_ÿú¯P`_ú¯ÿXÝèT/OBÿ`U VõU  Uú¯¯_U  UUÅ\U¦jjZUˆU`VPUPÌÊjÅú¯¯_UÌÅU¦jjZÉœœ\ !RZ¯¯UU`±U¥Z_Uõ_õZõõÅYÅÅõUZÿUV¥U_úåU^ŽEUT$UðððZÊÊUÿÿ¯ÿZªªU\ÌÌUªªjªZªªUfffPPUÀÀÅÿÿ¯ÿ\\UUªªjªÌ̜̩™`™"""ÿÿ¯ÿUf`f»»»U¥¥UUõõU¥¯õ¥•œÅ•UUõõUUUU¥¥UUååUUEEUðððZÊÊUÿÿ¯ÿZªªU\ÌÌUªªjªZªªUfffPPUÀÀÅÿÿ¯ÿ\\UUªªjªÌ̜̩™`™"""ÿÿ¯ÿUf`f»»»U¥¥UUõõU¥¯õ¥•œÅ•UUõõUUUU¥¥UUååUUEEUÿUUÿUUðUUUUõ_õ_UUUUUUUU¥Z¥ZUUUUeVeVPPUUU ÀUõ_õ_Å\UU¥Z¥ZÅ\Å\™–`%R%Rõ_õ_PU™i™µ[µ[UUUUUUUU¯ÿÿUœÌÌUUUUUUUUUUUUUUUUUUUUUUUUUUUPPUUUUU_õUUUUUUUUUUZ¥UUUUUUVeUUPUUUUPUUT$%UUUUU^®UU\ÅUf U^.UU^þUPUPUfeUU^ÞUUUUUUUUUUZõUUYÅUUUUUUUUUUUUUUUUUUUUUUUUUUUPPUUUUU_õUUUUUUUUUUZ¥UUUUUUVeUUPUUUUPUUT$%UUUUU^®UU\ÅUf U^.UU^þUPUPUfeUU^ÞUUUUUUUUUUZõUUYÅUUUUUUUUUUUUUUUUUUUUUuUuWw5SUeVeVÅ\Å\ffP ` PPÅY•\\ ETET]%QR\œœœPPWUUuPUUWUUu]UUu_UUõWUU5U0SPUUPUUPUUTUUuWUUuVUUeWUUuWUUuZUU¥\•Yj`uUuWw5SUeVeVÅ\Å\ffP ` PPÅY•\\ ETET]%QR\œœœPPWUUuPUUWUUu]UUu_UUõWUU5U0SPUUPUUPUUTUUuWUUuVUUeWUUuWUUuZUU¥\•Yj`WuuWWww5effVÅÌÌ\```…XÅ™™\UÏÏÅEFdTUßßÕ%RUÎÎÅPUuWUUPUUuWUUÕWUUõ_UUuSU00UPUUPUUPUUuWUUuWUUeVUUuWUUuWUU¥ZUUÌ™UP`UwwuUwswV`e\É™Åf  PfY˜ˆÅ\UUTeVE]•UUR%\•UUPuuWWPPuªªWÕÕWWõõ__uuSS037PPPPPPuEWWuuWWeeVVuuWWuuWW¥¥ZZœÉ™œP`PUwwuUwswV`e\É™Åf  PfY˜ˆÅ\UUTeVE]•UUR%\•UUPuuWWPPuªªWÕÕWWõõ__uuSS037PPPPPPuEWWuuWWeeVVuuWWuuWW¥¥ZZœÉ™œP`PuswwUwu_UffUUÌÌUV`e¢*U•U™™UÀ_UUFdUÙ_•UU!UÉ^•UUUwWuwPwZ¥w}Wu}ÿ_õÿ7S5703sPPPGWEtwWuwfVefwWuwwWuwªZ¥ªœ œUPuswwUwu_UffUUÌÌUV`e¢*U•U™™UÀ_UUFdUÙ_•UU!UÉ^•UUUwWuwPwZ¥w}Wu}ÿ_õÿ7S5703sPPPGWEtwWuwfVefwWuwwWuwªZ¥ªœ œUPw3uuUw_UVVe\\•ÅPV`bXPi…\Y…Å\ðUUTTeE]ùUURQ%\éUUPPwWuwPwZ¥w}]u}ÿ_õÿ7W57S370X…S5R%GTuww÷wfVefwWuwwWuwªZ¥ª U`W7uwUWwseffVÅÌÌ\`PR@%Å™™\UUUUEF¤TUUUU% RUUUUPus7W`Pu£7Wu×}Wõú¯_5s7SUS0U…€X50S% RuB7Wó?÷ej¦Vus7Wus7W¥¦jZÕ ]UfW7uwUWwseffVÅÌÌ\`PR@%Å™™\UUUUEF¤TUUUU% RUUUUPus7W`Pu£7Wu×}Wõú¯_5s7SUS0U…€X50S% RuB7Wó?÷ej¦Vus7Wus7W¥¦jZÕ ]UfssuwUUþåUeVUUÅ\UUPUT^TU…XUU•YUUUUUUETUUUUUU%RUUUUUUPUw33w`z£3w}wwÝÿªªÿ733wU33ˆˆ33""G#3tw33wf¦jfw33ww33wªj¦ªÜéžUVÿWWWuZ¯¥UVUUe\UUÅPUUT_õTPUU\UUÅUUUUTUUEUUUURUU%UUUUPUUUS5UUPUUS5UUWuUUZ¥UUS5UUS5UUPUUPUUPUUR5UUS5UUVeU_ó?õ]Ó=ÕUZ¥UUÉ™UUUUWWWuZ¯¥UVUUe\UUÅPUUT_õTPUU\UUÅUUUUTUUEUUUURUU%UUUUPUUUS5UUPUUS5UUWuUUZ¥UUS5UUS5UUPUUPUUPUUR5UUS5UUVeU_ó?õ]Ó=ÕUZ¥UUÉ™UUUU`ff88hŽˆ™ˆ™èˆî²¦fªªèˆ‹²ª8hfèˆîî`Ì™™œÿªª¯ZUU¥7UU7îˆUåfÌ™UŪffjÌ™™œÌ™™œŽUUŽfUeîˆUåw337ff`uUuWuUuWuUuWuUuWw3UuuUuWuUuWuUuW`ff88hŽˆ™ˆ™èˆî²¦fªªèˆ‹²ª8hfèˆîî`Ì™™œÿªª¯ZUU¥7UU7îˆUåfÌ™UŪffjÌ™™œÌ™™œŽUUŽfUeîˆUåw337ff`uUuWuUuWuUuWuUuWw3UuuUuWuUuWuUuW¦f În8™ˆˆ˜™ÎîžœúªjoÈŽˆ²®ª™–ÎîžœfU\ÅUU_õUUªUjU5SUUî…ŽUZ¥UUÌ•œUZ¥UU\ÅUYÉÅUU…XUUfUÞ…ŽUWuUUPUWuuWWuuWWuuWWuuWUw57WuuWWuuWWuuWZª¥Z“Ê5Y˜‰ž\™Ì×_ªªõ\™ÌÓZ££5\™ÌÅVff¥UU\UUU_UUZ¦¥3Su7å^èåUUVUÅ\ÉÅUU_UUU\U\U\UˆXåŽeV`eå]ØåUUWUUUPeUwwuUwwuUwwuUwwuuWsuUwwuUwwuUwwuZª¥Z“Ê5Y˜‰ž\™Ì×_ªªõ\™ÌÓZ££5\™ÌÅVff¥UU\UUU_UUZ¦¥3Su7å^èåUUVUÅ\ÉÅUU_UUU\U\U\UˆXåŽeV`eå]ØåUUWUUUPeUwwuUwwuUwwuUwwuuWsuUwwuUwwuUwwuUZªUUS£UåY™^U\ÍuU_ÿUU\Í5US3UœÌPUVªUUUUÅUUUõ¦ªªU7wswèîîUUUU¥ÉÌÌUUUUõUUUÅfUUÅŽîèî`ffUèíÞUUUUuUUUUuwwUuwwUuwwuuwwswwUuuwwuuwwuuwwUZªUUS£UåY™^U\ÍuU_ÿUU\Í5US3UœÌPUVªUUUUÅUUUõ¦ªªU7wswèîîUUUU¥ÉÌÌUUUUõUUUÅfUUÅŽîèî`ffUèíÞUUUUuUUUUuwwUuwwUuwwuuwwswwUuuwwuuwwuuwwUUUUUUUUå^îUÅUÌUUUUUUUUUUUUUUUUUUUÌÌUUÿÿUZUªUU3wUåˆîUUffUÅ™ÌUUúªUUÌÌUeÌÌUUˆîUefUåˆÝU3wwUUeUUuWUUsWUWW5WUuuu3wUwUuuwUuuwUuuUUUUUUUUUUîU\ÌUUUUUUUUUUUUUUUUUU\UUU_UUUUV¦¥WS5u…XèåZUUU•YÉÅ_UUU\UUU\UUU^X…åP`e…XØå73UUPeU…uWUuuWSuuuW5WWUu5SsuWWUuWWUwWWUwUUUUUUUUUUîU\ÌUUUUUUUUUUUUUUUUUU\UUU_UUUUV¦¥WS5u…XèåZUUU•YÉÅ_UUU\UUU\UUU^X…åP`e…XØå73UUPeU…uWUuuWSuuuW5WWUu5SsuWWUuWWUwWWUwUUUUUUUUUUåUUUUUUUUUUUUUUUUUUUUUUÌÅUUÿõUVªUju7wWXîUŽUfeUYÌUœUª¥UUÝÕUUÌÅUåŽî^PfU]îUŽ3wuUUàUWuUuWu5uSs5SuUuuSwU7uUuuuUuuuuuuUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªeU¥uSuWî…UåUUUUÌ•UÅUUUUUUUUUUUUåXå^fUeîÝUåUUUUUUUUUUUWUS5WUWwUUUUUw5UuUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªeU¥uSuWî…UåUUUUÌ•UÅUUUUUUUUUUUUåXå^fUeîÝUåUUUUUUUUUUUWUS5WUWwUUUUUw5UuUUUUUUUUUUUUÅÌÌ\ÅÌÌ\EDDTåîî^ÕÝÝ]¥ªªZeffVuwwW``````````Uð_U _ZUU¥\Å\ÅPPPU•\Å\Å\Å\ÅZ¥š¥\Å\ÅTETE\Å\ÅVeZ¥\Å\Å\Å\ÅY•\ÅY•\Å\Å\ÆèˆîîÅÌÌ\ÅÌÌ\EDDTåîî^ÕÝÝ]¥ªªZeffVuwwW``````````Uð_U _ZUU¥\Å\ÅPPPU•\Å\Å\Å\ÅZ¥š¥\Å\ÅTETE\Å\ÅVeZ¥\Å\Å\Å\ÅY•\ÅY•\Å\Å\Æèˆîî\™™Å\™™ÅT""E^ˆˆå]îîÕZff¥VeW33u```f```f```f```f```fU0_UðZUõ_UÉ™œ\ ™PY™PÀ \Æfl\©™šZUÅœUUE”UUÅœUUešUUÅœUUµ»UU•üUU•üUVfÎ̙YœÌ™YœD"$îˆXŽÝîŽíªfjfw37` ` ` ` `  _ðöð_õõ__™Y™œ™i™™Y™• fffœ™Y™šÉ™œ\I™”TÉ™œ\i™šVÉ™œ\Ë»»\Ïÿü\Ïÿü\Æ` \™ÌÅÌ™YœÌ™YœD"$îˆXŽÝîŽíªfjfw37` ` ` ` `  _ðöð_õõ__™Y™œ™i™™Y™• fffœ™Y™šÉ™œ\I™”TÉ™œ\i™šVÉ™œ\Ë»»\Ïÿü\Ïÿü\Æ` \™ÌÅ\ÉœÅ\ÉœÅTB$E^èŽå]ÞíÕZ¦j¥V`eWs7uV V V VêV º0SðZ0SðSõSõ_••ÉÌ–f ••ÉPÌffiÌ••ɪ™Y™œ™)™”™é™œ™i™š™)™œÆFfœÿ¯ÿÌÿ¯ÿÌff`U\ÌU\ÉœÅ\ÉœÅTB$E^èŽå]ÞíÕZ¦j¥V`eWs7uV V V VêV º0SðZ0SðSõSõ_••ÉÌ–f ••ÉPÌffiÌ••ɪ™Y™œ™)™”™é™œ™i™š™)™œÆFfœÿ¯ÿÌÿ¯ÿÌff`U\ÌUÅÉ™\ÅÉ™\EB"Tåèˆ^ÕÞî]¥¦fZe`Vus3WZ¦`Z¦`Z¦`Z¦`Z¦`ðf`_ Zð_5Zõ_•™œÌ–f`•™•™œÌf™œÌ•™šª••ÉÌ’"IDžîÉÌ–fij’"ÉÌÄDiÌú™œœú™œœf`fUUUUUœ™UUœ™UU$"UUŽˆUUíîUUjfUUUU73U__ðU__ðUUPU_^þU_[ûU_ïæðú`:õVeZY\Y•VeY •Y\Y•Y\Y•YZY••™œÌ’"$DžîìÌ–ª¦ª’",ÌÄDLÅùÌÉÌùÌÉÌ`eUUUUUœ™UUœ™UU$"UUŽˆUUíîUUjfUUUU73U__ðU__ðUUPU_^þU_[ûU_ïæðú`:õVeZY\Y•VeY •Y\Y•Y\Y•YZY••™œÌ’"$DžîìÌ–ª¦ª’",ÌÄDLÅùÌÉÌùÌÉÌ`eUUUUUÅ\U\Å\ÅUETUUå^UUÕ]UZ¥Z¥UeVUUuWUõ_ðUô_ðUUPUõ_ðUõ_ðUU:ðUUïþUZïæõ_™ŸU_foU_™ŸU_™ŸU_™ŸU_™ŸUY\Y•R%^ŒŽåUª¥UR %T EUÌUUÌÕUPààUUUUUUUUÅÅ\\UUUUUUUUUUUUU¥ZUUUUUUUUUõUUU}µUUUUUUõ]ÐUõUUUUUUUUZðUUS¥UUUUUUUUUUUUUUUUUUUUUUUUUU™•UU"%UUîåUUª¥UU"%UUDEUUUUÝÕUVUUUUUUUUUÅÅ\\UUUUUUUUUUUUU¥ZUUUUUUUUUõUUU}µUUUUUUõ]ÐUõUUUUUUUUZðUUS¥UUUUUUUUUUUUUUUUUUUUUUUUUU™•UU"%UUîåUUª¥UU"%UUDEUUUUÝÕUVUUUUUUUUUUUZUZ__ZU¥ZUUUUUUUUUU¥ZUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\UUU]UUU]UÕU]UÕU]UU]\Å\Åw3Uu\Å\Å^U^UÕÝU[U[UWUWU^U^UWUWUWUWUPˆ^U^UUuUWUUUUUUZUZ__ZU¥ZUUUUUUUUUU¥ZUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\UUU]UUU]UÕU]UÕU]UU]\Å\Åw3Uu\Å\Å^U^UÕÝU[U[UWUWU^U^UWUWUWUWUPˆ^U^UUuUWUU¥UUZõ¥ZÿÿúUeVUUU¥UUU¥UUPUUUõUUU¥UUUeUUUUUUUõÕ]_Õ]PÅUUýÕUUýÕUÕÕÕUÕUÕUUÕUÅœUUw57UÅœUUååUÙÝÕUUµµUUuuUUååUUuuUUuuUXUUååUWUUuU_¥UU¯_UVðö¥ffZU_¥UUZ¥U¥ZU_õUUZ¥UUVeUUPUUQUõõ__PP\Å_U]Õ_U]ÕÝÝ]ÕÕS]Õ]UÉ™™\uWsuÉ™™\^X…åÜÝUÝ[X…µWS5u^]ÕåWS5uWS5uX€^\•åUuWUU_¥UU¯_UVðö¥ffZU_¥UUZ¥U¥ZU_õUUZ¥UUVeUUPUUQUõõ__PP\Å_U]Õ_U]ÕÝÝ]ÕÕS]Õ]UÉ™™\uWsuÉ™™\^X…åÜÝUÝ[X…µWS5u^]ÕåWS5uWS5uX€^\•åUuWUUúUUõ¯õUVðöffPUúUUUªUUfªPUÿUUUªUUUfUUUUUUUUõðð_ PÉŸÿUÜÏÿUÜÌüÍÜÌ÷uÜÌÌU™ÌÉœswwU™ÌÉœUèŽU]Ý]UU¸‹UXs7^UíÞUUs7UUs7U€€€UÉÉU5_õWUúUUõ¯õUVðöffPUúUUUªUUfªPUÿUUUªUUUfUUUUUUUUõðð_ PÉŸÿUÜÏÿUÜÌüÍÜÌ÷uÜÌÌU™ÌÉœswwU™ÌÉœUèŽU]Ý]UU¸‹UXs7^UíÞUUs7UUs7U€€€UÉÉU5_õWUÿ¥Uú¯¯_VÿÿöPUÿ¥UUª¥UPUÿõUUª¥UUfeUUUUUÿ Àÿ¬ÊUUÿUUUÿUUUÿýUUÿóÕUÀœ™ÌÌu3wUœ™ÌÌU^åUUÝÕUU[µU^‡xŽU^åUU7uUUçåUààU^åUws_¥ÿÿVðP_ª¯¥Zªª¥P_ÿÿõZVffePQÿ\Åÿ\ÅUUÿÝUUÿÝU_ßÕU_?5]P Åœ™œÌ5SsuœéìÌUååU_ýÿUUµ[UXuuŽUÕÕUWUuUWUuUU€ˆUUÕÕUs7S3_¥ÿÿVðP_ª¯¥Zªª¥P_ÿÿõZVffePQÿ\Åÿ\ÅUUÿÝUUÿÝU_ßÕU_?5]P Åœ™œÌ5SsuœéìÌUååU_ýÿUUµ[UXuuŽUÕÕUWUuUWUuUU€ˆUUÕÕUs7S3¥ðZõð_UðUZ¥U¥_¥Z¥Z¥ZUZ¥Uõ_õ_¥Z¥ZeVeVPPQPÿPÿZ¥UUUUUUUUU]UUU]UUÕU_™ŸU^wU7]™UUå^UUUUUUUUU^UUåUÕ]UUUUUSUSUUUXUUµ[UsUu3¥ïåZUïåUU_ÿUUVeU¥_¥ZeZ¥ZUVeUõ_õ_¥Z¥ZeVeVPP] P_UUõPUUUUUUUUUUUUÝÕUUÝÕUUUU_U_Uw>Uu]U]UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU5^uS¥ïåZUïåUU_ÿUUVeU¥_¥ZeZ¥ZUVeUõ_õ_¥Z¥ZeVeVPP] P_UUõPUUUUUUUUUUUUÝÕUUÝÕUUUU_U_Uw>Uu]U]UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU5^uS_UÕÕ]Uõõ\UÅÅZúUUDô/DZ¥Z¥ßÿþ÷w~Dô/DUUUUZ¥Z¥ZõUUœÌÌÉUU¥_õU_ªUU•UŽíÝèX…X…R%R%Y•Y•Z¥Z¥UUUUUUUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_UÕÕ]Uõõ\UÅÅZúUUDô/DZ¥Z¥ßÿþ÷w~Dô/DUUUUZ¥Z¥ZõUUœÌÌÉUU¥_õU_ªUU•UŽíÝèX…X…R%R%Y•Y•Z¥Z¥UUUUUUUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿýõUÝßÕUÉœÅU¯¥UUTDO¥¬œZîÝýýîßwý!DOUUUU¥¬œZUª¥UY™Ì™UU¦UúUÿ¥YYéYXˆÞˆU…ˆUU%"UU•™UU¥ªUUUUUUUUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUeUý_ÝUß]ÿUœ\ÌUUZúUUõRD©šÊʈíÝÞŽïÿþñDUUUU©šÊÊUZúUUYœÉUZ¦eúÿú¥UYéUUXŽØŽˆèX$"BRœYÉY¯ªúZUUUUUUUeUUUUUUUUUUUUUUUUUUUUUUUUUVUUWw5UZeUý_ÝUß]ÿUœ\ÌUUZúUUõRD©šÊʈíÝÞŽïÿþñDUUUU©šÊÊUZúUUYœÉUZ¦eúÿú¥UYéUUXŽØŽˆèX$"BRœYÉY¯ªúZUUUUUUUeUUUUUUUUUUUUUUUUUUUUUUUUUVUUWw5UZeUÕUÕUõUõUÅUÂUUZÿ¥õUU$ššœ™ˆîîÞŽÝÝÞõU$UUUUššœ™UZÿ¥ÌU™ÉUªfe_fú_YžÞ™ˆUˆèèîèˆBDB"ÉÅÅ™úÿúªUUUUUUVUUUUUUUUUUU]UUUUUUUUUUUUUU`UUU_÷uU¦UUÕUÕUõUõUÅUÂUUZÿ¥õUU$ššœ™ˆîîÞŽÝÝÞõU$UUUUššœ™UZÿ¥ÌU™ÉUªfe_fú_YžÞ™ˆUˆèèîèˆBDB"ÉÅÅ™úÿúªUUUUUUVUUUUUUUUUUU]UUUUUUUUUUUUUU`UUU_÷uU¦UUõ_UUÕ]UU•ç%U¯úªUÿRDUš©UXîŽîŽÞîÞUÿRDUUUUª¬É™UZúªUUœÉU¦f¥öÿöÿžíýîUUèîDD$"Å\œ™ÿÿ¯ªUUÕUU¦UUUUõUUUuUUUPUUeUUUõUUUÕUUfUWpUUªeUUõ_ÕUÕ]õþpSu¯ÿÿÿ_TOõUUUU^Ž^îŽíîî_TOõ^å^åUš©UU_ÿÿUœÉ•VªjUúÿúúYžÞ™UŽè…Žîèˆ$DB"•œYY¯ÿúªU]ÅUUUUUU_¥UUW5UUÝUUVUU_ÕUU]õUVuWuuZfjeUõ_ÕUÕ]õþpSu¯ÿÿÿ_TOõUUUU^Ž^îŽíîî_TOõ^å^åUš©UU_ÿÿUœÉ•VªjUúÿúúYžÞ™UŽè…Žîèˆ$DB"•œYY¯ÿúªU]ÅUUUUUU_¥UUW5UUÝUUVUU_ÕUU]õUVuWuuZfjeU_ýUU]ßUþW7%UUUUUõUUUUUUXˆˆèŽÞîèUÿõõUUUUUUUUZ¯úªUY•UVj¦¥_î®åUYéUUX…UîD"$DÉÌYÅÿª¯ÿUÅ\UUUUUU¥ZUU5SUU\\UUPUUÕ]UUõ_UVPUUUueZeVUUUUUUUU_î%UUUUUUUUUUUUUXX…X興Uÿ_UUUUUUUUUÿÿÿÿÌUUUUf¦jU_ªUYYéYˆUUUUîååUDEEUÌÅÅUÿõõUUUUUUUUUUUUUUUUUÅUÅUUUUUUUUUUUUVPUUUUeZeVUUUUUUUU_î%UUUUUUUUUUUUUXX…X興Uÿ_UUUUUUUUUÿÿÿÿÌUUUUf¦jU_ªUYYéYˆUUUUîååUDEEUÌÅÅUÿõõUUUUUUUUUUUUUUUUUÅUÅUUUUUUUUUUUUVPUUUUeZeVÅUUÅ¥UUZõUU_¥UUZUE\UÌÉÌUåUå^åUå^ÌÉÌUZUU¥åUU^BUUßUUúUUsUU`UU`ffeèUUýUUªÿÿõÜUUÿUªÿ_ÿÿªÝUÿÝõ¥UˆîîåSUuuRUEEZUõõXUååUªPUeeÅUUÅ¥UUZõUU_¥UUZUE\UÌÉÌUåUå^åUå^ÌÉÌUZUU¥åUU^BUUßUUúUUsUU`UU`ffeèUUýUUªÿÿõÜUUÿUªÿ_ÿÿªÝUÿÝõ¥UˆîîåSUuuRUEEZUõõXUååUªPUee\ÉœUª¥Z¥ÿõ_õª¦j¥UÄÄUÀœU…Uå^…Uå^ÀœUªUU¥îå^å$Býß ¯ú7s`UffŽèßýUªªúÍsUú_V¯ªªUUß]__UUˆˆèU37uU"$EUª¯õUˆŽåU PZUe\™œUZªª¥_ÿÿõjªª¥TUULœœ•UˆîíÕˆîíÕœœ•Uª¥Z¥^îîåUD@UUÝÐUUÿðUUwpUUf`UPfUîàUUÿðUZ¯ªªUwpU¯ÿÿúªªú¥ÍÝÝßUÿðUXŽˆˆ7WwU$TDU¯_ÿUŽ^îUff`VfU\™œUZªª¥_ÿÿõjªª¥TUULœœ•UˆîíÕˆîíÕœœ•Uª¥Z¥^îîåUD@UUÝÐUUÿðUUwpUUf`UPfUîàUUÿðUZ¯ªªUwpU¯ÿÿúªªú¥ÍÝÝßUÿðUXŽˆˆ7WwU$TDU¯_ÿUŽ^îUff`VfUÅUÌÌ¥UªUõUÿU¦fªe\U\U™ÉÌXŽßøXŽÞè™É̪ªª¥å^^åR$D _ýÝðZ¯ÿ S7w0PfUUXŽî€]ßÿÐUUª¯S$D ¥¯úZúªUUÅýÜ_U_U UUˆŽuUuUEUEUõUõUåUåU`PeUeUÅUÌÌ¥UªUõUÿU¦fªe\U\U™ÉÌXŽßøXŽÞè™É̪ªª¥å^^åR$D _ýÝðZ¯ÿ S7w0PfUUXŽî€]ßÿÐUUª¯S$D ¥¯úZúªUUÅýÜ_U_U UUˆŽuUuUEUEUõUõUåUåU`PeUeUÅUÅÅZUªª_UÿÿZUªªUUUUÉš¥UåU^ŽåU^ŽÉ˜…Uªªª¥^î^î"$D ÿýÝðª¯ÿ 37w0feUPˆŽî€ÝßÿÐõUZ¯2$» ªÿú¯ú¥U_ÍýÜßUZ_ åUXŽ5SUU%TUU¥ZUU…XUU`VUUUUUUUU¥¥UUõõUU¥¥UUUUZªªªUUååUUååXˆîîªUªUU^^U"DDÿÝݪÿÿ3wwffUPˆîîÝÿÿ¯UZª"»»¯¯ú¯ª¥UúÍýÜßUÿÿŽUXˆU3SuUEREU¥ZõUˆXå`UePeUUUUUU¥¥UUõõUU¥¥UUUUZªªªUUååUUååXˆîîªUªUU^^U"DDÿÝݪÿÿ3wwffUPˆîîÝÿÿ¯UZª"»»¯¯ú¯ª¥UúÍýÜßUÿÿŽUXˆU3SuUEREU¥ZõUˆXå`UePeUUUUUUUUUUUUUUUUUUUUUZ UUUUUUUUU^¥UªªUUUU$Tý]¯_7WV`UjŽ^ß_úUªú»^ª_U¯¯ªU¯Í]Uÿ¯_èUˆèUS7UUR$UUZ¯UUXŽUVUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU¥¥UUUUEUDDÕUÝÝõUÿÿuUwweUffVffZåUîîõUÿÿ_ÿÿZµUîîZ_ÿ¥¥ÿÿõ\]ÝõõU_ÿ^îîXUUUUUUUUUUUUUUUUPVfUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU¥¥UUUUEUDDÕUÝÝõUÿÿuUwweUffVffZåUîîõUÿÿ_ÿÿZµUîîZ_ÿ¥¥ÿÿõ\]ÝõõU_ÿ^îîXUUUUUUUUUUUUUUUUPVfUUUUPfPfPfPfPfUUUUUUUUUUUUUUUUUUUUUUUUUUUU30W3PPPPUUUU_¯¦õUR UUUUU]Õ]Õ]Õ]Õ]Õ]ÕªªªªÝÝÝÝîîŽî ÿÿßÿÌ™™•îÝÝÕU•YUUÅ\UU•YUÅUUÅPfPfPfPfPfUUUUUUUUUUUUUUUUUUUUUUUUUUUU30W3PPPPUUUU_¯¦õUR UUUUU]Õ]Õ]Õ]Õ]Õ]ÕªªªªÝÝÝÝîîŽî ÿÿßÿÌ™™•îÝÝÕU•YUUÅ\UU•YUÅUUÅ`````_jfW33]Ì™]Ùˆ_»±ZUV_DBS30™™UœUœøˆîï_ô$Uj¦_ÿÿõ_ÿÿõZªª¥Zªª¥]ÝÝÕ^îŽåP _ÿßõU\É™U^íÝÅP\UÌÌUÉU\Y\œÉU`````U¦`UU30UUÉUU˜€UU»UUe`UUD UW35PPPPUUUU]Žîõ_DB j¦ ¯ÿÿúßÿÿýÚªª­Úªª­ýÝÝßÞèˆí` ªÿýÝýÕUÉU UíU™ ™\UUÌ\•Å™\ÌÉU`````U¦`UU30UUÉUU˜€UU»UUe`UUD UW35PPPPUUUU]Žîõ_DB j¦ ¯ÿÿúßÿÿýÚªª­Úªª­ýÝÝßÞèˆí` ªÿýÝýÕUÉU UíU™ ™\UUÌ\•Å™\ÌÉUPæPæPæPæPæúªª`×ww0ýÝÝÀÝîíÐû»»eUU`ôDD sS0S™ˆœUŽUUøïU_ôD j¦ ªÿÿªÝÿÿÝݪªÝݪªÝÿÝÝÿÝîŽÝf fÝÿßÝœ™™ÞÝÝÞ YUYUœÉ™•ÅUÉÌPæPæPæPæPæúªª`×ww0ýÝÝÀÝîíÐû»»eUU`ôDD sS0S™ˆœUŽUUøïU_ôD j¦ ªÿÿªÝÿÿÝݪªÝݪªÝÿÝÝÿÝîŽÝf fÝÿßÝœ™™ÞÝÝÞ YUYUœÉ™•ÅUÉÌ\ŒŒ…Z ¥¥] ÖÕ_úõ[‹¸µ_ª¦]ws_ÝÜ^Ýî_»±Vff_DBs5=SPPPPUUUUUßÿU_ô$ ªª ª¯úªÝßýÝÝÚ­ÝÝÚ­ÝÿýßÿÝÞíÝf`fÝßýÝœ™™ÞÝÝÞÉ™UUUUÅU\ÌÅUÅÅUÈÈUU¥ªUUÖÝUUúÿUU¸»UUUUU_U_UUUUUUUUUUUUUUUUUBUUBW73Ј™UŽUœ_©šõ_DBj¥Z¦ªZ¥ªÝ]ÕÝÝ_õÝÝ_õÝÿ_õÿÝ_õÝfZ¥fÝ]ÕÝÕUÉU UíUÉžœUUUUUUÅÅUUUUUÈÈUU¥ªUUÖÝUUúÿUU¸»UUUUU_U_UUUUUUUUUUUUUUUUUBUUBW73Ј™UŽUœ_©šõ_DBj¥Z¦ªZ¥ªÝ]ÕÝÝ_õÝÝ_õÝÿ_õÿÝ_õÝfZ¥fÝ]ÕÝÕUÉU UíUÉžœUUUUUUÅÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUõ_UõUUUUUUUUUUUUUUUUUT%UUs3ÕPPPPUUUUU™™U_ô$UeVU¥]ÕZÕ]Õ]Õ]Õ]Õ]Õ]õ]Õ_Õ]Õ]eVeVÕ]Õ]U\É™U^íÝ\Å\ÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_UUUUUUUUUUUUUUUUUUT%UBUW00™™œUœUUUUUUR UVUUe¥UUZÕUU]ÕUU]ÕUU]õUU_ÕUU]eUUVÕUU]Ì™™•îÝÝÕUÅ\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_UUUUUUUUUUUUUUUUUUT%UBUW00™™œUœUUUUUUR UVUUe¥UUZÕUU]ÕUU]ÕUU]õUU_ÕUU]eUUVÕUU]Ì™™•îÝÝÕUÅ\UUUUUUUUUUUUUU ZU ZU ZU ZU ZU ZU ZU ZUYUYUYUYUYUYUYU€XUYUYUð_Uð_Uð_Uð_Uð_Uð_U@TUpWUð_UÐ]UUUUUUUUUUUUUUUUU ZU ZU ZU ZU ZU ZU ZU ZUYUYUYUYUYUYUYU€XUYUYUð_Uð_Uð_Uð_Uð_Uð_U@TUpWUð_UÐ]UUUUUUUUUUUUUUUUÀ  À  À  À    0  À     À À À À À À 0€À   Àð Àð Àð Àð Àð Àð @ 0pÀð  Ð UUUUUUUUUUUUUUUU`ªª`ªª`ªª`ªª`ªª`ªª`ªª`ªª0™™0™™0™™0330330330™™ˆˆ 0™™0™™ ÿý ÿý ÿý ¯ß ¯ß ¯ß DO0w ÿýÐÝß UUUUUUUUUUUUUUUU`ªª`ªª`ªª`ªª`ªª`ªª`ªª`ªª0™™0™™0™™0330330330™™ˆˆ 0™™0™™ ÿý ÿý ÿý ¯ß ¯ß ¯ß DO0w ÿýÐÝß UUUUUUUUUUUUUUUU`ffVff`Vff`Vi–``ff`ff`ff`ff033033033S330S330S9“0033™™ 033033 ¯ß ¯ß ¯ßZªýõZªýõZ©õ $ô07÷ ¯ßÐÝý UUUUUUUUUUUUUUUU`ffVff`Vff`Vi–``ff`ff`ff`ff033033033S330S330S9“0033™™ 033033 ¯ß ¯ß ¯ßZªýõZªýõZ©õ $ô07÷ ¯ßÐÝý UUUUUUUUUUUUUUUUVff`U\ÀUU\ÀUUYUVff`Vff`Vff`Vff`S330S330S330U\ÀUU\ÀUUYUS330Y™™S330S330ZªýõZªýõZªýõ_ ÀU_ ÀU_ UR"OES3uZªýõ]ÝßÕUUUUUUUUUUUUUUUUU\ÀUUœÉUœÉUœÉUYUUS0UU\ÀUUZ UU\ÀUU\ÀUU\ÀUUœÉUœÉUœÉUYUUS0UU\ÀUUZ U_ ÀU_ ÀU_ ÀUðœÉðœÉðœÉ_ U_0U_ ÀU_  UUUUUUUUUUUUUUUUUU\ÀUUœÉUœÉUœÉUYUUS0UU\ÀUUZ UU\ÀUU\ÀUU\ÀUUœÉUœÉUœÉUYUUS0UU\ÀUUZ U_ ÀU_ ÀU_ ÀUðœÉðœÉðœÉ_ U_0U_ ÀU_  UUUUUUUUUUUUUUUUUUÜÍUYUUYUUYUU U33U¬ÊUú¯UœÉUœÉUÜÍUYUUYUUYUU U33U¬ÊUú¯ðœÉðœÉðÜÍUYUUYUUYUð ð33ð¬Êðú¯UUUUUUUUUUUUUUUUU]ÐUUUUUUUUUUUUUUPUUS0UUZ UU_ðUUYUUYUU]ÐUUUUUUUUUUUUUUPUUS0UUZ UU_ðUUYUUYUU]ÐUUUUUUUUUUUUUUPUUS0UUZ UU_ðUUUUUUUUUUUUUUUUUU]ÐUUUUUUUUUUUUUUPUUS0UUZ UU_ðUUYUUYUU]ÐUUUUUUUUUUUUUUPUUS0UUZ UU_ðUUYUUYUU]ÐUUUUUUUUUUUUUUPUUS0UUZ UU_ðUUUUUUUUUUUUUUUUUUà^Uà^Uà^Uà^Uà^Uà^U RU0SUà^Uà^````````````!!00````o¯úúo¯úúo¯úúo¯úúo¯úúo¯úú$$BB77sso¯úúÙÙU ZU ZUà^Uà^Uà^Uà^Uà^Uà^U RU0SUà^Uà^````````````!!00````o¯úúo¯úúo¯úúo¯úúo¯úúo¯úú$$BB77sso¯úúÙÙU ZU ZÀà Àà Àà Àà Àà Àà  00Àà  à ```f```f```f```f```f```f!!!"0003```f```fo¯ªo¯ªo¯ªo¯ªo¯ªo¯ª$$"773o¯ª ™À  À  €îí€îí€î퀎ހŽÞ€ŽÞ"$037€îí€îí` ` ` ` ` `©!A00` ` oÿúoÿúoÿúoðúoðúoðú$DB7wsoÿúÝ Ù`ªª`ªª€îí€îí€î퀎ހŽÞ€ŽÞ"$037€îí€îí` ` ` ` ` `©!A00` ` oÿúoÿúoÿúoðúoðúoðú$DB7wsoÿúÝ Ù`ªª`ªª€ŽÞ€ŽÞ€ŽÞXˆíàXˆíàX‰àB03s€ŽÞ€ŽÞ` ` ` V V V™!0` ` oðúoðúoðújÿújÿújÿ ú$@B7psoðúÐÙ`ff`ff€ŽÞ€ŽÞ€ŽÞXˆíàXˆíàX‰àB03s€ŽÞ€ŽÞ` ` ` V V V™!0` ` oðúoðúoðújÿújÿújÿ ú$@B7psoðúÐÙ`ff`ffXˆíàXˆíàXˆíàZ ÀUZ ÀUZ UQ$ S370XˆíàXˆíàfffZ ÿ`Z ÿ`Z ™`"3ffjÿújÿújÿúZü™ Zü™ Zü™ "DB3wsjÿú™Ý ÙVff`Vff`Z ÀUZ ÀUZ ÀU œÉ œÉ œÉZ UZ0UZ ÀUZ  UZ ÿ`Z ÿ`Z Ý`UüÌUüÌUœÌT S3Z ª`Z ÿ`Zü™ Zü™ ZüÝ UœÌUœÌUœÌRI Ss30Züª YÚÿU\ÀUU\ÀUZ ÀUZ ÀUZ ÀU œÉ œÉ œÉZ UZ0UZ ÀUZ  UZ ÿ`Z ÿ`Z Ý`UüÌUüÌUœÌT S3Z ª`Z ÿ`Zü™ Zü™ ZüÝ UœÌUœÌUœÌRI Ss30Züª YÚÿU\ÀUU\ÀU œÉ œÉ ÜÍUY•UUY•UUY•U   33 ¬Ê ú¯UüÌUüÌUÜÌU_ÿU_ÿUY™U ™U33U¬ÌUúªUœÌUœÌUÜÌUY™UY™UY™U ™U33U¬ÌUúªUœÉUœÉUY•UUY•UU]ÕUUUUUUUUUUUUUUPUUS5UUZ¥UU_õUU_ÿU_ÿU]ÝUUUUUUUUUUUUUPUS3UZªU_ÿUY™UY™U]ÝUUUUUUUUUUUUUPUS3UZªU_ÿUYUUYUUY•UUY•UU]ÕUUUUUUUUUUUUUUPUUS5UUZ¥UU_õUU_ÿU_ÿU]ÝUUUUUUUUUUUUUPUS3UZªU_ÿUY™UY™U]ÝUUUUUUUUUUUUUPUS3UZªU_ÿUYUUYU\ÌÉÀUUUUY™˜UUUUZUUœœÌÀYUUUUð_U\˜UUUUUUUUU¯¥UUUUUU\UU\UUUUUU]UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\ÌÉÀUUUUY™˜UUUUZUUœœÌÀYUUUUð_U\˜UUUUUUUUU¯¥UUUUUU\UU\UUUUUU]UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]ÌÉÀ\™ˆ\™˜UUUUU UUYÿÜ\™UUUð_UÌ™€UÉUUUYùUVöUUUUUUU\UU\UUZU]ÖÝUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÜÌU˜€UUÉ™UUUUUZUYÿÜUÌ•Uúð_UÌ™€U\•UUÿoeUUUUUUU\UU\UVª¦]mmUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÜÌU˜€UUÉ™UUUUUZUYÿÜUÌ•Uúð_UÌ™€U\•UUÿoeUUUUUUU\UU\UVª¦]mmUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÜÌÌUUUUUUUUUZ UœœÌÀU\ßUð_ð_U\™UUÉUYÿùèŠUUUUUUU\UU\UZªªU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÜÌÌUUUUUUUUUZ UœœÌÀU\ßUð_ð_U\™UUÉUYÿùèŠUUUUUUU\UU\UZªªU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]ÌÉUUUUUUUUUUUUZœÊU]ÿÕð_ð_UÉÉ€UUUU_ðŸ…öUUUUUUZªª_ÿÿU\UU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_ZUUßÕÿ  ÿUUUUUÉUUUÏÿÅ¥o¦UUUUUU\UU\UU\UU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_ZUUßÕÿ  ÿUUUUUÉUUUÏÿÅ¥o¦UUUUUU\UU\UU\UU\UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUú¥UUU]ÕU_ðUUUUU\•UUUUUUUVúUUUUUUUUUUUUUUÌÀUUÌÀUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]UU_ðUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]UU_ðUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\UUUUU_¯¦ð_¯¦ðUUUUUwwUUUUUUÌUS330UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\UUUUU_¯¦ð_¯¦ðUUUUUwwUUUUUUÌUS330UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]Ìœ\ˆ˜ÍÌÀUUs30U\UUZUõUU_üŒŒÏUUUUW33uUUUU\ÌÉW330XÉ™XÉ™XÉ™XÉ™QK»°P¦f`P舀QB" P¨ˆ€Vúª Vúª Vúª Vúª Qû»°RôD@S÷wpYýÝÐXþîàUUUUUÜÀUUÈ€UÍÌÌÉUxƒ0U\UVª¦_UUð_ÈÈðUUsÿ37UUUUW33Us3XÉ™XÉ™XÉ™XÉ™QK»°P¦f`P舀QB" P¨ˆ€Vúª Vúª Vúª Vúª Qû»°RôD@S÷wpYýÝÐXþîàUUUUUÜÀUUÈ€UÍÌÌÉUxƒ0U\UVª¦_UUð_ÈÈðUUsÿ37UUUUW33Us3YÌœYÌœYÌœYÌœ[D´°Vªj`XRD$ XªŠ€Zÿ¯ Zÿ¯ Zÿ¯ Zÿ¯ [ÿ¿°TÿO@Wÿp]ÿßÐ^ÿïàUUUUUUUUUUUUÍÌÀÉUxƒ0U\UZªªUõ_UüPUsÿÿ7UUUUUs0UUUUUYÌœYÌœYÌœYÌœ[D´°Vªj`XRD$ XªŠ€Zÿ¯ Zÿ¯ Zÿ¯ Zÿ¯ [ÿ¿°TÿO@Wÿp]ÿßÐ^ÿïàUUUUUUUUUUUUÍÌÀÉUxƒ0U\UZªªUõ_UüPUsÿÿ7UUUUUs0UUUUUYÉÉÀYÉÉÀYÉÉÀYÉÉÀ[KK@V¦¦ XèèàRBB@X¨¨ ZúúðZúúðZúúðZúúð[ûûðTôôðW÷÷ð]ýýð^þþðUUUUUUUUUUUUÝÝÌÉUx30ZªªUU\UUÿÿUÿÿPUsó?÷UUUUUUUUUUUUXÉ™XÉ™XÉ™XÉ™QK»°P¦f`P舀QB" P¨ˆ€Vúª Vúª Vúª Vúª Qû»°RôD@S÷wpYýÝÐXþîàUUUUUUUUUUUUÿÿÐUUW3U\UUU\U_©šð_¨ŠðUUsÿÿ7UUUUUUUUUUUUXÉ™XÉ™XÉ™XÉ™QK»°P¦f`P舀QB" P¨ˆ€Vúª Vúª Vúª Vúª Qû»°RôD@S÷wpYýÝÐXþîàUUUUUUUUUUUUÿÿÐUUW3U\UUU\U_©šð_¨ŠðUUsÿÿ7UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUW3UUUUUÌÀUU™™UUˆˆUUUUW33uUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUwwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUwwUUUUUUUUUUUUUUUUUUUUUUUUUU_UU_UU_UUUUUUUjÀUUUœUUUUUÀU\UU\UUU\UœUÉUU\UUjUUZUUÉUUU\ÌU\œÀU_UUÏÀUUÌÉU|pUUÞÐUUZUU^UU UUUUUUUUUUUUUUUUUU_UU_UU_UUUUUUUjÀUUUœUUUUUÀU\UU\UUU\UœUÉUU\UUjUUZUUÉUUU\ÌU\œÀU_UUÏÀUUÌÉU|pUUÞÐUUZUU^UU UUUUUU7sUŽèUÙU_UU_UUo`UUo`UUŽ€UV¦`ÀUUUYÉUV¦ÀU\UU\UUUœUœU\UU\UUZ`UUZ`UU\UUUÀðUUÀ_UüðUYüù\\U\UU^UUZUU^UUúUUZUp7sàŽè@$BUª UU_UUo`UUo`UUŽ€Ujª` ÀUUUUœUjª`ÀU\UU\UUUÉUœUUÉUYÀUUV UUV UUUÉU\ðU\__ _œŸYÀYU\UU^UUZUU^UUUUUU¯ Up7sàŽè@$BUª UU_UUo`UUo`UUŽ€Ujª` ÀUUUUœUjª`ÀU\UU\UUUÉUœUUÉUYÀUUV UUV UUUÉU\ðU\__ _œŸYÀYU\UU^UUZUU^UUUUUU¯ Up7sàŽèp7sUZUUª UUo`UUo`UUŽ€UV¦`À  UUYÉV¦ÀU\UU\UUYÀUUœUU\UUÉUU¦UU¦UU\U\ðU\_ð\ððœðU\UU\UU^UUZUU^UUUZUZUp7sàŽèp7sUZUUª UUo`UUo`UUŽ€UV¦`À  UUYÉV¦ÀU\UU\UUYÀUUœUU\UUÉUU¦UU¦UU\U\ðU\_ð\ððœðU\UU\UU^UUZUU^UUUZUZUpwwàîîàîîUUUUUZUZjjUo`UUŽ€UU`ZÀ UUœU`UÀV¦¦XÞØU\UZ¬`UUUÉUœUUjUUjUUUÉ\ðU\_ÌœœÀÀœÀUÉÉU\UU^UUª UUÞÐUUU_ UUUU7uWsŽå^èÕ]ÙUUUUUUUUUZUUo`UUŽ€UZVÀÀ UYÉÉÀZUÀjjj`íí€UœUZªª`UolU\UZªª`UZ`Uú`\UÀðU\_U\UœœœZ`\UZUU_UU_UU_UZUUUUUU7uWsŽå^èÕ]ÙUUUUUUUUUZUUo`UUŽ€UZVÀÀ UYÉÉÀZUÀjjj`íí€UœUZªª`UolU\UZªª`UZ`Uú`\UÀðU\_U\UœœœZ`\UZUU_UU_UU_UZUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUZjj]VU Ì \ÌÌVU V¦¦XÞØUÉUUUUUU¯ÿ¦UZðUVö¦ðUZðU_ú`ÉU\ÌUÀ_UUUUUœUVª`UUUUUUUUUUUUUUUUU_ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUZUU]UU¦¦UUUUYÌÉU¦¦UUUUUUUUYÀUUUUUUUZÿðUUúUVðUUUúU_ÿ¦UUUUU\œÀUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUZUU]UU¦¦UUUUYÌÉU¦¦UUUUUUUUYÀUUUUUUUZÿðUUúUVðUUUúU_ÿ¦UUUUU\œÀUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU3033f`ffUUUUUUUUUUUUUUUUUUUUUUUUUUUUœÌɉ™˜jª¦ÀÀUU  UU ÐUUUUUUUUUUUUUUUUUUUZU‰™˜ "!UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU3033f`ffUUUUUUUUUUUUUUUUUUUUUUUUUUUUœÌɉ™˜jª¦ÀÀUU  UU ÐUUUUUUUUUUUUUUUUUUUZU‰™˜ "!UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU3033f`ffUUUUUUUUUUUUUUUUUUUUUUUUUUUUœUU‰€UUj`UUœÌUjªUÙ UU™™U™™UªªUª¦U¯ U‰™ˆ "UœÉU‰˜U`UUj¦Ui¦UœÉUUÍÜU$BU¯úU`3035f`feUUUUUUUUUUUUUUUUUUUUUUUUUUUUœœÌ‰‰™€jjª`ÌÌÀUªª UÙ UY™™Y™™Zªª Zªª`Zúj‰˜… !ÀœÉ ‰˜  j¦  j¦  j– ÅÍÜ\ÐÍÜ @$Bð¯ú``3035f`feUUUUUUUUUUUUUUUUUUUUUUUUUUUUœœÌ‰‰™€jjª`ÌÌÀUªª UÙ UY™™Y™™Zªª Zªª`Zúj‰˜… !ÀœÉ ‰˜  j¦  j¦  j– ÅÍÜ\ÐÍÜ @$Bð¯ú``S035V`feUUUUUUUUUUUUUUUUUUUUUUUUjÿú`œ™U‰ˆUjfUU\UUZU]ÐUUY™™™™™™_ªª ªªª¦ªúj X™…Q "ÀœÉ ‰˜ `ff j¦ i¦ Õßý]ÐÝÝ @$Bð¯ú``S035V`feUUUUUUUUUUUUUUUUUUUUUUUUjÿú`œ™U‰ˆUjfUU\UUZU]ÐUUY™™™™™™_ªª ªªª¦ªúj X™…Q "ÀœÉ ‰˜ `ff j¦ i¦ Õßý]ÐÝÝ @$Bð¯ú``U05U`eUUUUUUUU_ Z`Z`Vjªª`œÝÜØHxàYÉUX˜UV¦UUU\ UUZ UUÝ\™™Éœ™™_ªª ú¯ªª¯ª¦ U …U ÀÌÌ ™™  j¦  ªª  ªš õÿÿ_ÐÝÝ @DDðÿÿ`ffS3VfX™™€Vªª`_ªª`Zff    ÈØØÀUˆ€UYÉUX˜UV¦UUUÌÉUUª¦U]ÐUÌ™ɜəUúúú¯úª¯ª¦ Y˜R!œÅ\ɉ•Y˜jeV¦j¥Z¦j•Y¦Ÿõ_ùÍÕ]Ü$ETB¯õ_úeV`S3VfX™™€Vªª`_ªª`Zff    ÈØØÀUˆ€UYÉUX˜UV¦UUUÌÉUUª¦U]ÐUÌ™ɜəUúúú¯úª¯ª¦ Y˜R!œÅ\ɉ•Y˜jeV¦j¥Z¦j•Y¦Ÿõ_ùÍÕ]Ü$ETB¯õ_úeV`UUUUUUUUU‰˜Uj¦Uú¦U¦`UUUUÕUUÕU_UUUUUUUUUUUUUU\ÌÌUZªªU]ÐUUUU\™™UUUU_ªª ¯ª¦ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÀUUU UUUÝUUUUUÌ™UUUUUÿªUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÀUUU UUUÝUUUUUÌ™UUUUUÿªUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU»°UUîàUUD@UUÉÀUU" UUUUURW77^ŽŽ][‹‹ZjjUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUUUUUUUUUUßýUUT%UU^…UU»°UUîàUUD@UUÉÀUU" UUUUURW77^ŽŽ][‹‹ZjjUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUUUUUUUUUUßýUUT%UU^…U[$+^úþT«¤\œRUUUU"!! wsspîèèàÝÙÙл¸¸°ª¦¦ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUUUUUUUUU]ÿÿÕUDBUUîèUU»°UUîàUUD@UUÉÀUU" UUUUU/B! ÷spïþèàßýÙпû¸°¯ú¦ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUjjj`UUUUUßýU_BD%_èî…U»°UUîàUUD@UUÉÀUU" UUUUU/B! ÷spïþèàßýÙпû¸°¯ú¦ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUjjj`UUUUUßýU_BD%_èî…UZUU]UU]UUZUU]UUUUURô"Wÿw^ÿî]ÿÝ[ÿ»ZÿªUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUÿ ðUUUUU]õUUôBUUþèUUZUU]UU]UUZUU]UUUUURô"Wÿw^ÿî]ÿÝ[ÿ»ZÿªUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUÿ ðUUUUU]õUUôBUUþèUU  UUÐÐUUððUU  UUÐÐUUUUUU/ UUpUUïàUUßÐUU¿°UU¯ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUUUUUUUUUU]õUUUUUUUUUZZ]]]]ZZ]]UUUUUD UUÿpUUÿàUUÿÐUUÿ°UUÿ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_U_UUUUUUßýUTUTU_U_UZZ]]]]ZZ]]UUUUUD UUÿpUUÿàUUÿÐUUÿ°UUÿ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_U_UUUUUUßýUTUTU_U_U UU ÐUUÐðUUð UU ÐUUÐUUUU_É’_É—_Éž_É_É›_ÉšUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU````õ_UõUUUUÕUU]ETUEõ_Uõjªª`ÍÝÝÀÍýýÀo¯¯`ÍÝÝÀUUUUUÌUUÌUUÌUUÌUUÌUUÌUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUU_UUUUUUUÕ]UUTUUU_UUjªª`ÍÝÝÀÍýýÀo¯¯`ÍÝÝÀUUUUUÌUUÌUUÌUUÌUUÌUUÌUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUjUU_UUUUUUUÕ]UUTUUU_UUUYUUX€UUR UUS0UUUUU_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ðQBUXì€USs0UX»UUUUUUQBUXì€USs0UX»UUUUUUUœUUUœUUUœUUUœUUUUUÝÐUUwpUUYUUX€UUR UUS0UUUUU_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ð_¯¦ðQBUXì€USs0UX»UUUUUUQBUXì€USs0UX»UUUUUUUœUUUœUUUœUUUœUUUUUÝÐUUwpU]ÿÏ`^ÿï€_D$ Wÿ?0UUUUô"÷33?þèˆýÍ™ü™Y_üªjoýíî÷wª¯ôDª¯û»‹QBUXì€USs0UX»UUUUUUQBUXì€USs0UX»UUUUUUYÉUUYÉUUYÉUUYÉUUUU]èíWBG]üÿ`^þÿ€_BD Wóÿ0UUUU_B"ð_s3ð_îˆð_ÜÙð_É™ð_ʪð_ÞÞð_wzð_DJð_»¸ðU!UŽÈU73U‹±UUUUU!UŽÈU73U‹±UUUUUUœUUœUUœUUœUUUUUÝÐUUwpU]üÿ`^þÿ€_BD Wóÿ0UUUU_B"ð_s3ð_îˆð_ÜÙð_É™ð_ʪð_ÞÞð_wzð_DJð_»¸ðU!UŽÈU73U‹±UUUUU!UŽÈU73U‹±UUUUUUœUUœUUœUUœUUUUUÝÐUUwpU]ÿÿ`^ÿÿ€_DD Wÿÿ0UUUUUô/U÷?UþïUýÏUüŸUü¯UýïU÷UôOUû¿U!UŽÈU73U‹±UUUUU!UŽÈU73U‹±UUUUUYÉUYÉUYÉUYÉUUUUU_UU]U]ÿÿ`^ÿÿ€_DD Wÿÿ0UUUUUô/U÷?UþïUýÏUüŸUü¯UýïU÷UôOUû¿U!UŽÈU73U‹±UUUUU!UŽÈU73U‹±UUUUUYÉUYÉUYÉUYÉUUUUU_UU]U]ÿÏ`^ÿï€_D$ Wÿ?0UUUUUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUÿÿUQBUXì€USs0UX»UUUUQBUXì€USs0UX»UUUUUUœUUœUUœUUœUUUUUUððUUÐÐU]üÿ`^þÿ€_BD Wóÿ0UUUU_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šðUYÞUYýUQBUS÷0UUUUYÞUYýUQBUS÷0UUUUUYÉUYÉUYÉUYÉUUUUU__]]]üÿ`^þÿ€_BD Wóÿ0UUUU_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šð_©šðUYÞUYýUQBUS÷0UUUUYÞUYýUQBUS÷0UUUUUYÉUYÉUYÉUYÉUUUUU__]]]ÿÏ`^ÿï€_D$ Wÿ?0UUUUU™™U™™U™™U™™U™™U™™U™™U™™U™™U™™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUýUUBUUèUUsUUUUUUðUUðÐUUÐUYUUX€UUR UUS0UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUßUU$UUŽUU7UUUUUU¯ÿÿ ÍÝÝÀUYUUX€UUR UUS0UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUßUU$UUŽUU7UUUUUU¯ÿÿ ÍÝÝÀUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUöUUoUUPñUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUöUUoUUPñUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURUUUUUURUUUUUU%UYUUUUUUYUUUUUU•U^UUUUUU^UUUUUUå^…Xå]å^Õ]Å\ÕW5SuT%RE[µ[µoöoöPPññ\•YÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUEUUUUTUU\UUUUUUUÅUUUU\UU]UUUUUUUÕUUUU]UUŽèUUØUUÍÜUU7sUU$BUU+²UV¯úeUUQ/òUœÉUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUEUUUUTUU\UUUUUUUÅUUUU\UU]UUUUUUUÕUUUU]UUŽèUUØUUÍÜUU7sUU$BUU+²UV¯úeUUQ/òUœÉUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_UUROBUU_UUUUõUU]UUYÍÉUU]UUUUÕUU_UU^ßÞUU_UUUUõUèèŽŽÞØíÜÙÍss77BA$»²+»ÿöoÿPÿñÿÉÀ œUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_UUROBUU_UUUUõUU]UUYÍÉUU]UUUUÕUU_UU^ßÞUU_UUUUõUèèŽŽÞØíÜÙÍss77BA$»²+»ÿöoÿPÿñÿÉÀ œUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUEUUTUUU\UUUUUUUUÅUU\UUU]UUUUUUUUÕUU]UUUŽèUUØUUÍÜUU7sUU$BUU+²UV¯úeUUQ/òUœÉUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURUUUUUUUURUU%UUUYUUUUUUUUYUU•UUU^UUUUUUUU^UUåUU^…Xå]å^Õ]Å\ÕW5SuT%RE[µ[µoöoöPPññ\•YÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURUUUUUUUURUU%UUUYUUUUUUUUYUU•UUU^UUUUUUUU^UUåUU^…Xå]å^Õ]Å\ÕW5SuT%RE[µ[µoöoöPPññ\•YÅUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUöUUoUUPñUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUXUUUUUUXUUUUUU…UXUUUUUUXUUUUUU…UYUUUUUUYUUUUUU•USUUUUUUSUUUUUU5UQUUUUUUQUUUUUUUQUUUUUUQUUUUUUU_UUUUUU_UUUUUUõUVUUUUUUVUUUUUUeU^UUUUUUUåUUUU^UU^UUUUUUUåUUUU^UU]UUUUUUUÕUUUU]UUWUUUUUUUuUUUUWUURUUUUUUU%UUUURUU[UUUUUUUµUUUU[UU_UUUUUUUõUUUU_UUZUUUUUUU¥UUUUZUU^UUUUUUUåUUUU^UU^UUUUUUUåUUUU^UU]UUUUUUUÕUUUU]UUWUUUUUUUuUUUUWUURUUUUUUU%UUUURUU[UUUUUUUµUUUU[UU_UUUUUUUõUUUU_UUZUUUUUUU¥UUUUZUU^UUXîèUU^UUUUåUU]UUXíèUU]UUUUÕUU]UUYÝÙUU]UUUUÕUUWUUSwsUUWUUUUuUUTUUQ$!UUTUUUUEUU[UUQ»±UU[UUUUµUU_UU_ÿÿUU_UUUUõUUZUUVª¦UUZUUUU¥UU^UUXîèUU^UUUUåUU]UUXíèUU]UUUUÕUU]UUYÝÙUU]UUUUÕUUWUUSwsUUWUUUUuUUTUUQ$!UUTUUUUEUU[UUQ»±UU[UUUUµUU_UU_ÿÿUU_UUUUõUUZUUVª¦UUZUUUU¥UU^UUUUUUUUåUU^UUU^UUUUUUUUåUU^UUU]UUUUUUUUÕUU]UUUWUUUUUUUUuUUWUUURUUUUUUUU%UURUUU[UUUUUUUUµUU[UUU_UUUUUUUUõUU_UUUZUUUUUUUU¥UUZUUUXUUUUUUUUXUU…UUUXUUUUUUUUXUU…UUUYUUUUUUUUYUU•UUUSUUUUUUUUSUU5UUUQUUUUUUUUQUUUUUQUUUUUUUUQUUUUU_UUUUUUUU_UUõUUUVUUUUUUUUVUUeUUUXUUUUUUUUXUU…UUUXUUUUUUUUXUU…UUUYUUUUUUUUYUU•UUUSUUUUUUUUSUU5UUUQUUUUUUUUQUUUUUQUUUUUUUUQUUUUU_UUUUUUUU_UUõUUUVUUUUUUUUVUUeUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÉÉÉÏ™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUîîUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÉÉÉÏ™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUîîUUUUUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^ˆˆåUUUU\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍÏÏÏÉ™™™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUèØŽUUUU\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍ\ÐÍÏÏÏÉ™™™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUèØŽUUUUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUèØŽUUUUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUUÍÐUœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUèØŽUUUUU»°UUŽàUU7pUU$@UU$@UUŽàUU7pUUUUUÉÉÉÏ™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUèØŽUUUUUUUUUUUUUUUUUUUU_U_U_U_U_U_U_U_Uœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUíÝÝÞUUUUUUUUUUUUUUUUUUUU_U_U_U_U_U_U_U_Uœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUíÝÝÞUUUUUUUUUUUUUUUUUUUUõ_Uõõ_Uõõ_Uõõ_UõÏÏÏÉ™™™UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^ˆˆåUUUUUUUUUUUUUUUUUUUUU_UUU_UUU_UUU_UUœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUîîUUUUUUUUUUUUUUUUUUUUUU_UUU_UUU_UUU_UUœüüœ ™™ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUîîUUUUUUUUUUUUUjjjjooooUUUUUUUUUUUUUUUUUUUUUU`333UUUUUUUUUUUUUUUU\™ˆ…jªª¦ÉéÉèj¯ú¦/¯¦ò»»»»ff`7wwsUUUU驈¨UUUUÿªªªUUUUªªªÿUUUUUUUUUUUUjjjjooooUUUUUUUUUUUUUUUUUUUUUU`333UUUUUUUUUUUUUUUU\™ˆ…jªª¦ÉéÉèj¯ú¦/¯¦ò»»»»ff`7wwsUUUU驈¨UUUUÿªªªUUUUªªªÿUUUUUUUUUUUUª¦ª¦úöúöUUUUUUUU`UUUUUUUUUUUUUU`ff333UUUUUVUVUUUUUUUUX\•…jÏü¦Œœˆèjoö¦þ»`733sUUUUŽˆè˜U3U™UÿªªUUUUªªÿUUUUUUUUUUUUjjjjooooUUUUUUUUðUUUUUUUUUUUUUU`oö333UUUUUUUUUUUUUUUUX\ß…úÿÿ¯‰Êèèjoö¦/îîò»Íл  `7?ósUUUUXš˜ŽUUUUUUÿªUUUUªÿUUUUUUUUUUUUUjjjjooooUUUUUUUUðUUUUUUUUUUUUUU`oö333UUUUUUUUUUUUUUUUX\ß…úÿÿ¯‰Êèèjoö¦/îîò»Íл  `7?ósUUUUXš˜ŽUUUUUUÿªUUUUªÿUUUUUUUUUUUUU¦ª¦ªöúöúUUUUUUUU`UUUUUUUUUUUUUU`ffUUU0UUUUVUVUUUUUUUUUX]ÿÕúÿÿ¯œ¨èj¯ú¦$þïB¼ÐÍ `733sUUUUUX騙U3U_UÿUUUUÿUðUUUUUUUUUUUU¦ª¦ªöúöúUUUUUUUU`UUUUUUUUUUUUUU`ffUUU0UUUUVUVUUUUUUUUUX]ÿÕúÿÿ¯œ¨èj¯ú¦$þïB¼ÐÍ `733sUUUUUX騙U3U_UÿUUUUÿUðUUUUUUUUUUUUjjjjooooUUUUUUUUUUUUUUUUUUUUUU`UUUSUUUUUUUUUUUUUUUUXˆßÕÿÿÿÿÎîúèjoö¦$ÿÿB»Íлff`7wwsUUUUUUXŽUUUU_UUUUUUUUZúUUUUUUUUUUUUª¦ª¦úöúöUUUUUUUU`UUUUUUUUUUUUUU`ffUUUUUUUUUVUVUUUUUUUUXU]ÕÏöoüžˆ¨ÿÿÿÿ/©šò»Žà»`733sUUUUUUUUU3U™ÿúUUUUUUU¯ÿUUUUUUUUUUUUª¦ª¦úöúöUUUUUUUU`UUUUUUUUUUUUUU`ffUUUUUUUUUVUVUUUUUUUUXU]ÕÏöoüžˆ¨ÿÿÿÿ/©šò»Žà»`733sUUUUUUUUU3U™ÿúUUUUUUU¯ÿUUUUUUUUUUUUjjjjooooUUUUUUUU`UUUUUUUUUUUUUU`ffUUUUUUUUUUUUUUUUUUUUXU]…jff¦Žˆˆújoö¦$™™B»»`733sUUUUUUUUUUUU¯ UUUUUUUUUðUUUUUUUUUUUU¦ª¦ªöúöúUUUUUUUUPUUUUUUUUUUUUUUffUUUUUUUUVUVUUUUUUUUUˆˆPªª`îî¯ú`DD »»°ffww5UUUUUUUU™U3U_UUUUUUUUUðUUUUUUUUUUUU¦ª¦ªöúöúUUUUUUUUPUUUUUUUUUUUUUUffUUUUUUUUVUVUUUUUUUUUˆˆPªª`îî¯ú`DD »»°ffww5UUUUUUUU™U3U_UUUUUUUUUðUUUUUUUUUUPUffffjjjjjúúj€ˆˆˆÉÉÉÏÏÏÏɈªªŠªª¨PfPfPfPfUUUUVUUQUUXUUZUUSUªªªª™™™™™™™fff`ffffªªª ªªªª™ÉÉœÝÝÝÝ AÐUUUUUUPUffffjjjjjúúj€ˆˆˆÉÉÉÏÏÏÏɈªªŠªª¨PfPfPfPfUUUUVUUQUUXUUZUUSUªªªª™™™™™™™fff`ffffªªª ªªªª™ÉÉœÝÝÝÝ AÐUUUUUUUUlªª¦ÊªÆ¦ÊÚööˆˆˆüüüœüüüù€ˆˆ€¨ŠŠˆˆ¨````U–PUUª`UU"UUˆ€UUÿ UU30U¯ÿÿúÉÉÉ™™™™¦¦¦`ffffúúú ªªªªÉ™œÑÝÝÝÝ qÐUUUUUUPUffffjjjjÿÝßj€ˆˆˆÏÏÏÉŸÏÏŸˆˆˆ¨úŠø¨````TªZÿ¦RD!Xîˆ_PúSw3ªªªª\œ™Ìœ™™Zjfªjff_¯ªÿ¯ªª™œ™ÑwwAq qÐUUUUUUPUffffjjjjÿÝßj€ˆˆˆÏÏÏÉŸÏÏŸˆˆˆ¨úŠø¨````TªZÿ¦RD!Xîˆ_PúSw3ªªªª\œ™Ìœ™™Zjfªjff_¯ªÿ¯ªª™œ™ÑwwAq qÐUUUUPª¦lªª¦Ê¦ªýßÿˆüüüœüœœüˆˆˆ¨ŠŠˆˆ¨PfPfPfPfŸª‘¯ÿú`$DBŽîè€õ¥ 7ws0ÿú¯ÿUUUUY™™UUUUVff`UUUUZªª ÉUÑqAq AÐUUUUPª¦lªª¦Ê¦ªýßÿˆüüüœüœœüˆˆˆ¨ŠŠˆˆ¨PfPfPfPfŸª‘¯ÿú`$DBŽîè€õ¥ 7ws0ÿú¯ÿUUUUY™™UUUUVff`UUUUZªª ÉUÑqAq AÐUUUUUUPUfffflªjjo­Ýj€ˆˆˆÉÉÉÏÏÏÏɈªªŠªª¨\ŒŒ…Z ¥¥] ÖÕ_úõŸJ¡¯ÿú`$DBŽîè€úZ_ 7ws0ªªªªUUUU\œ™UUUUZjf`UUUU_¯ª UU\ÑqqA qÐUUUUUUUUlªª¦¦¦Êª¦öÚúˆˆˆüüüœüüüù€ˆˆ€¨ŠŠˆˆ¨UÈÈUU¥ªUUÖÝUUúÿUZÿFZÿúRD!Xîè_ÿúSws¯ÿÿúUUUUUUUUUUUUUUUUUUUUUUUUUUUUÑwwqA AÐUUUUUUUUlªª¦¦¦Êª¦öÚúˆˆˆüüüœüüüù€ˆˆ€¨ŠŠˆˆ¨UÈÈUU¥ªUUÖÝUUúÿUZÿFZÿúRD!Xîè_ÿúSws¯ÿÿúUUUUUUUUUUUUUUUUUUUUUUUUUUUUÑwwqA AÐUUUUUUPUffffjjjjojúo€ˆˆˆÏÏÏÉŸÏÏŸˆˆˆ¨ŠŠˆˆ¨UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªUUUUUUUUUUUUUUUUUUUUUUUUUUUUÑÝÝÝÝ AÐUUUUPª¦lª¦Ê¦¦¦Êö¦ˆüüüœüœœüˆˆˆ  ªª€UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿú¯ÿUUUUUUUUUUUUUUUUUUUUUUUUUUUUÝÝÝÝ qÐUUUUPª¦lª¦Ê¦¦¦Êö¦ˆüüüœüœœüˆˆˆ  ªª€UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿú¯ÿUUUUUUUUUUUUUUUUUUUUUUUUUUUUÝÝÝÝ qÐzangband/lib/xtra/graf/8x8.bmp0000644000000000000000000061606610250356274015155 0ustar rootrootBM66(ÄÄÿÿÿ000 DDDuuueeeUUU‰‰‰™™™ºººÎÎÿ™™ÿªªª™™ÎeeÎÎÎÎee™00e,ÞÞÞ0eee™Î0e™îîîe™™™ÎÎ0™Î™ÿÿe™0eÎÿÿeÎÎ000™™0ÎÎ0ÎÿeÎÿe™ÿ0™ÿ00™ÎΙ™ee™Îe™e™ÎÿÎÿe00™00™e0Îe0™™e™ee00Îeeÿÿe0eÎÿÿÎ00ÿ™™e™ÿ0e0eeÿÿ000e0ÿeee0U0™eD ™0ue ‰ÿ0DªeÿÎ0™ÿ0™™eÎÿ™™0Îeeÿe0ÿ00ÿÎeΙ0Ιe0e0ÿ™eee0eΙÿÿ™ÿÿe0Î0ÿÎÿe0ÿeeÎe™Îeee™000ÿe0uÎÎeî™ÿ™™Î™ÎΙ™eÿÿÎUÿ0eÿîee™0ÿ™ÿ™™eÿÎÿ™0Î00ÿ™Îe™eÿÎeÎ0ΙÎ0e0Îe0ÿ0™ÿÎ0™ee0™ 0™Î™™eÿ™e™0e0e™™0ÿÿ™eÎÎeÿ0eÿÎÎÿ0Îÿ0™Îe™eÎeÎÿÿ00e000™ª‰Î™ÎÿÎÿΙ™0™0™‰D™™0™0™0e0Îe0ÎÎ0ºÞΙÿe0ÿ0ΙU0ÿÎueÿÎîÿΙ0ÎΙª™Î0ÞºÎÎ0ÿ™ÿÿeÎ0ÿÿ™0ÿÎ0eÿÞeΙÿe0ÎÎ0eÎe™™™ÎÎÎÎ0™ÿÿÿΙÿ0ÎeΙÎ0ÎΙÎÿ™±}}}}}}±°}}}}}}°æ°°°°°°æÀ<<<<<<À<<<<<<±}±±±±}±°}°°°°}°æ°ææææ°æÀ<ÀÀÀÀ<À<<À±}±±±±}±°}°°°°}°æ°ææææ°æÀ<ÀÀÀÀ<À<<À±}±±±±}±°}°°°°}°æ°ææææ°æÀ<ÀÀÀÀ<À<<±}}}}}}±°}}}}}}°æ°°°°°°æÀ<<<<<<À<<<<<<±}±±±±}±°}°°°°}°æ°ææææ°æÀ<ÀÀÀÀ<À<<±}±±±±}±°}°°°°}°æ°ææææ°æÀ<ÀÀÀÀ<À<<æ±}}}}±ææ°}}}}°æææ°°°°æææÀ<<<<Àææ<<<<æææ±±ÀÀ//°°\\°ææ\\ææ°°ææææ°°ææææ°°ææææ°°ææXXææ°±}}}}}}±±±//////\<<<<<<\æ\\\\\\ææ°°°°°°æ±°°//°°±\<<<<\FFFæææææææÀÀæ//±/æXX<<<\<\}}}±}±mmmFmF///±/±°<<\<\°°°°///°°°°}}X}X°±À<\À±}±±FmFF±±X°°À/\ææ\<\æ±}±æ\ææ°°æ±°±//±°±\<\\<\FFæFæææææææææææææ///±/±//XXXXXXmm>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~€€€‚‚‚ƒƒƒ„„„………†††‡‡‡ˆˆˆ‰‰‰ŠŠŠ‹‹‹ŒŒŒŽŽŽ‘‘‘’’’“““”””•••–––———˜˜˜™™™ššš›››œœœžžžŸŸŸ   ¡¡¡¢¢¢£££¤¤¤¥¥¥¦¦¦§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³³³´´´µµµ¶¶¶···¸¸¸¹¹¹ººº»»»¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÁÁÂÂÂÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÈÈÈÉÉÉÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzangband/lib/xtra/graf/makefile.zb0000644000000000000000000000030410250356274016116 0ustar rootrootsubdir = ./lib/xtra/graf/ ## makefile.zb srcfiles += lib/xtra/graf/makefile.zb files += lib/xtra/graf/16x16.bmp \ lib/xtra/graf/8x13.bmp \ lib/xtra/graf/8x8.bmp \ lib/xtra/graf/mask.bmp zangband/lib/xtra/help/0000755000000000000000000000000010250356274014020 5ustar rootrootzangband/lib/xtra/help/makefile.zb0000644000000000000000000000012110250356274016124 0ustar rootrootsubdir = ./lib/xtra/help/ ## makefile.zb srcfiles += lib/xtra/help/makefile.zb zangband/lib/xtra/music/0000755000000000000000000000000010250356274014210 5ustar rootrootzangband/lib/xtra/music/makefile.zb0000644000000000000000000000012310250356274016316 0ustar rootrootsubdir = ./lib/xtra/music/ ## makefile.zb srcfiles += lib/xtra/music/makefile.zb zangband/lib/xtra/sound/0000755000000000000000000000000010250356274014220 5ustar rootrootzangband/lib/xtra/sound/sound.cfg0000755000000000000000000000217410250356274016040 0ustar rootroot# CVS: Last edit by $Author: remco $ on $Date: 1999/09/30 10:08:46 $ # sound.cfg # # Configuration file for the Angband sound events # # The format is: # name of the Angband event = list of the available sample-names seperated by spaces. # # Example: # hit = hit.wav hit1.wav # # Look at the definition of "angband_sound_name" in variable.c for a complete list of # all the available event names. [Sound] hit = hit.wav drop.wav hit1.wav miss = miss.wav miss1.wav flee = flee.wav drop = clunk.wav kill = kill.wav destroy.wav kill1.wav level = level.wav death = death.wav study = teleport = shoot = quaff = zap = walk = tpother = hitwall = eat = eat.wav store1 = money.wav store2 = money.wav store3 = money.wav store4 = money.wav dig = thump.wav opendoor = opendoor.wav shutdoor = shutdoor.wav tplevel = teleport.wav scroll = buy = sell = warn = rocket = n_kill = u_kill = quest = heal = x_heal = bite = claw = m_spell = summon = breath = ball = m_heal = atkspell = evil = touch = sting = crush = slime = wail = winner = fire = acid = elec = cold = illegal = fail = wakeup = invuln = fall = pain = destitem = moan = show = unused = explode = zangband/lib/xtra/sound/makefile.zb0000644000000000000000000000016510250356274016334 0ustar rootrootsubdir = ./lib/xtra/sound/ ## makefile.zb srcfiles += lib/xtra/sound/makefile.zb files += lib/xtra/sound/sound.cfg zangband/lib/xtra/makefile.zb0000644000000000000000000000026410250356274015204 0ustar rootrootsubdir = ./lib/xtra/ ## makefile.zb srcfiles += lib/xtra/makefile.zb dirs := font graf help music sound dirlist := $(dirlist) $(addprefix $(subdir),$(dirs)) include $(scandir) zangband/lib/makefile.zb0000644000000000000000000000031510250356274014223 0ustar rootrootsubdir = ./lib/ ## makefile.zb srcfiles += lib/makefile.zb dirs := apex bone data edit file help info pref save script user xtra dirlist := $(dirlist) $(addprefix $(subdir),$(dirs)) include $(scandir) zangband/angdos.cfg0000755000000000000000000001660010250356274013306 0ustar rootroot# File: angdos.cfg # # This file contains configuration data for Angband when compiled # (and run) with the "main-dos.c" file. # # It also contains configuration data for the Allegro library. # If you leave an Allegro parameter blank, Allegro will autodetect # ########### Graphics settings ########## # graphics driver (there is usually no need to set this) # VGA - Standard VGA # MODX - Mode-X # VBE1 - VESA 1.x # VB2B - VBE 2.0 (banked) # VB2L - VBE 2.0 (linear) # VBE3 - VBE 3.0 # VBAF - VBE/AF # XTND - Xtended mode gfx_card = # where to look for the VBE/AF driver vbeaf_driver = ########### Sound settings ########## [sound] # digital sound output driver # 0 - none # SB - Sound Blaster (autodetect breed) # SB10 - Sound Blaster 1.0 # SB15 - Sound Blaster 1.5 # SB20 - Sound Blaster 2.0 # SBP - Sound Blaster Pro # SB16 - Sound Blaster 16 # ESS - ESS AudioDrive # ESC - Ensoniq Soundscape digi_card = # music output driver # 0 - none # OPL - Adlib (autodetect OPL version) # OPL2 - OPL2 FM synth # OPLX - Dual OPL2 (SB Pro-1) # OPL3 - OPL3 FM synth # SB - raw SB MIDI interface # MPU - MPU-401 MIDI interface # DIGI - DIGMID software wavetable # AWE - AWE32 midi_card = # how many voices to reserve for each driver digi_voices = midi_voices = # how loud? (range 0-255) digi_volume = 255 midi_volume = 255 # sample mixing (0=fastest, 1=use 16 bit inputs, 2=interpolation) quality = # toggling this between 0 and 1 reverses the left/right panning of samples flip_pan = # SB port address (usually 220) sb_port = # SB DMA channel (usually 1) sb_dma = # SB IRQ number (usually 7) sb_irq = # SB sample frequencies are 11906, 16129, 22727 or 45454 # ESS Audiodrive uses 11363, 17046, 22729, or 44194 # Ensoniq Soundscape uses 11025, 16000, 22050, or 48000 sb_freq = # FM synth port address (usually 388) fm_port = # MPU-401 port address (usually 330) mpu_port = # MPU-401 IRQ number (usually the same as sb_irq) mpu_irq = # instrument definitions for the Adlib driver ibk_file = ibk_drum_file = # patch set for the DIGMID driver patches = ########### General Angband settings ########## [Angband] Graphics = 1 Sound = 0 Resolution = 1 ########### Screen Resolution 640 x 480 ########## [Mode-1] screen_wid = 640 screen_hgt = 480 Description = bitmap_wid = 8 bitmap_hgt = 8 bitmap_file = 8x8.bmp num_windows = 3 [Term-1-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 8 tile_hgt = 13 font_wid = 8 font_hgt = 13 font_file = 8x13b.dat [Term-1-1] x = 0 y = 336 cols = 80 rows = 24 tile_wid = 4 tile_hgt = 6 font_wid = 4 font_hgt = 6 font_file = 4x6.dat [Term-1-2] x = 320 y = 336 cols = 80 rows = 24 tile_wid = 4 tile_hgt = 6 font_wid = 4 font_hgt = 6 font_file = 4x6.dat ########### Screen Resolution 640 x 480 ########## [Mode-2] screen_wid = 640 screen_hgt = 480 Description = with new graphics bitmap_wid = 16 bitmap_hgt = 16 bitmap_file = 16x16.bmp graf-mode = new num_windows = 3 [Term-2-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 8 tile_hgt = 13 font_wid = 8 font_hgt = 13 font_file = 8x13b.dat [Term-2-1] x = 0 y = 336 cols = 80 rows = 24 tile_wid = 4 tile_hgt = 6 font_wid = 4 font_hgt = 6 font_file = 4x6.dat [Term-2-2] x = 320 y = 336 cols = 80 rows = 24 tile_wid = 4 tile_hgt = 6 font_wid = 4 font_hgt = 6 font_file = 4x6.dat ########## Screen Resolution 800 x 600 ########## [Mode-3] screen_wid = 800 screen_hgt = 600 Description = bitmap_wid = 8 bitmap_hgt = 8 bitmap_file = 8x8.bmp num_windows = 3 [Term-3-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 10 tile_hgt = 17 font_wid = 10 font_hgt = 17 font_file = 10x17.dat [Term-3-1] x = 0 y = 408 cols = 80 rows = 24 tile_wid = 5 tile_hgt = 8 font_wid = 5 font_hgt = 8 font_file = 5x8.dat [Term-3-2] x = 400 y = 408 cols = 80 rows = 24 tile_wid = 5 tile_hgt = 8 font_wid = 5 font_hgt = 8 font_file = 5x8.dat ########## Screen Resolution 800 x 600 ########## [Mode-4] screen_wid = 800 screen_hgt = 600 Description = with new graphics bitmap_wid = 16 bitmap_hgt = 16 bitmap_file = 16x16.bmp graf-mode = new num_windows = 3 [Term-4-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 10 tile_hgt = 17 font_wid = 10 font_hgt = 17 font_file = 10x17.dat [Term-4-1] x = 0 y = 408 cols = 80 rows = 24 tile_wid = 5 tile_hgt = 8 font_wid = 5 font_hgt = 8 font_file = 5x8.dat [Term-4-2] x = 400 y = 408 cols = 80 rows = 24 tile_wid = 5 tile_hgt = 8 font_wid = 5 font_hgt = 8 font_file = 5x8.dat ########## Screen Resolution 1024 x 768 ########## [Mode-5] screen_wid = 1024 screen_hgt = 768 Description = bitmap_wid = 8 bitmap_hgt = 8 bitmap_file = 8x8.bmp num_windows = 3 [Term-5-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 12 tile_hgt = 20 font_wid = 12 font_hgt = 20 font_file = 12x20.dat [Term-5-1] x = 0 y = 480 cols = 80 rows = 24 tile_wid = 6 tile_hgt = 12 font_wid = 6 font_hgt = 12 font_file = 6x12.dat [Term-5-2] x = 481 y = 480 cols = 80 rows = 24 tile_wid = 6 tile_hgt = 12 font_wid = 6 font_hgt = 12 font_file = 6x12.dat ########## Screen Resolution 1024 x 768 ########## [Mode-6] screen_wid = 1024 screen_hgt = 768 Description = with new graphics bitmap_wid = 16 bitmap_hgt = 16 bitmap_file = 16x16.bmp graf-mode = new num_windows = 3 [Term-6-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 12 tile_hgt = 20 font_wid = 12 font_hgt = 20 font_file = 12x20.dat [Term-6-1] x = 0 y = 480 cols = 80 rows = 24 tile_wid = 6 tile_hgt = 12 font_wid = 6 font_hgt = 12 font_file = 6x12.dat [Term-6-2] x = 481 y = 480 cols = 80 rows = 24 tile_wid = 6 tile_hgt = 12 font_wid = 6 font_hgt = 12 font_file = 6x12.dat ########## Screen Resolution 1280 x 1024 ########## [Mode-7] screen_wid = 1280 screen_hgt = 1024 Description = bitmap_wid = 8 bitmap_hgt = 8 bitmap_file = 8x8.bmp num_windows = 3 [Term-7-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 16 tile_hgt = 25 font_wid = 16 font_hgt = 25 font_file = 16x25.dat [Term-7-1] x = 0 y = 610 cols = 80 rows = 24 tile_wid = 8 tile_hgt = 16 font_wid = 8 font_hgt = 16 font_file = 8x16.dat [Term-7-2] x = 640 y = 610 cols = 80 rows = 24 tile_wid = 8 tile_hgt = 16 font_wid = 8 font_hgt = 16 font_file = 8x16.dat ########## Screen Resolution 1280 x 1024 ########## [Mode-8] screen_wid = 1280 screen_hgt = 1024 Description = with new graphics bitmap_wid = 16 bitmap_hgt = 16 bitmap_file = 16x16.bmp graf-mode = new num_windows = 3 [Term-8-0] x = 0 y = 0 cols = 80 rows = 24 tile_wid = 16 tile_hgt = 25 font_wid = 16 font_hgt = 25 font_file = 16x25.dat [Term-8-1] x = 0 y = 610 cols = 80 rows = 24 tile_wid = 8 tile_hgt = 16 font_wid = 8 font_hgt = 16 font_file = 8x16.dat [Term-8-2] x = 640 y = 610 cols = 80 rows = 24 tile_wid = 8 tile_hgt = 16 font_wid = 8 font_hgt = 16 font_file = 8x16.dat ########### Background settings ########## [Background] # Background-0 : Standard background # Background-1 : inven/equip # Background-2 : equip/inven # Background-3 : player (basic) # Background-4 : player (extra) # Background-5 : XXX # Background-6 : XXX # Background-7 : messages # Background-8 : overhead view # Background-9 : monster recall # Background-10 : object recall # Background-11 : XXX # Background-12 : snap-shot # Background-13 : XXX # Background-14 : XXX # Background-15 : borg messages # Background-16 : borg status Background-0 = backgrnd.bmp zangband/readme0000755000000000000000000003007210250356274012531 0ustar rootrootThis is the README file for Angband 2.8.1 (03/04/97) [Meaning it might not apply for Zangband in all respects, of course... (TY)] Angband 2.8.1 is a semi-stable official release incorporating all of the improvements added since Angband 2.7.8. Any bugs found in Angband 2.8.1 will be fixed in Angband 2.8.2, the next stable official release. See the Official Angband Home Page "http://www.phial.com/angband/" for more information about exactly what has changed recently. === General information === Angband is a "graphical" dungeon adventure game using textual characters to represent the walls and floors of a dungeon and the inhabitants therein, in the vein of "rogue", "hack", "nethack", and "moria". There are extensive ascii "on line help" files in the "lib/help" directory. This version of Angband will run on Macintosh, Windows, Unix (X11/Curses), Linux (X11/Curses), Acorn, Amiga, various IBM machines, and many others... See Makefile, h-config.h, and config.h for details on compiling. See "Makefile.xxx" and "main-xxx.c" for various supported systems. Visit the Angband Home Page ("http://www.phial.com/angband/"), and browse through the Angband newsgroup ("rec.games.roguelike.angband"). Send bug reports, suggestions, etc, to Ben Harrison ("benh@phial.com"). === Quick and dirty compilation instructions (for Unix) === Step 1: Acquire. Ftp to "clockwork.dementia.org:/angband/Source" Try "bin" and "mget angband*.tar.gz" and "y" Step 2: Extract. Try "gunzip *.gz" then "tar -xvf *.tar" Step 3: Prepare. Try "cd angband*/src", then edit "Makefile" You may also edit "h-config.h" and "config.h" Step 4: Compile. Try "make", and then "cd .." if successful Step 5: Execute. Try "angband -uTest" to initialize stuff Step 6: Play.... Read the "online help" via the "?" command. === Special instructions for other systems === The IBM requires extraction with "pkunzip -d" to create subdirectories. The Macintosh requires that the "lib" folder be in the same folder as the executable. Also, note that System 7.5 (and perhaps others) are brain damaged, and may default to the incorrect folder for opening savefiles. Make sure you keep all your savefiles in the proper place, and if you load a savefile from the wrong place, note that the game may decide to re-save your savefile in the proper place when you quit. === Upgrading from older versions or other platforms === If you have been playing Angband on a different platform, or with an older version of Angband, it may be possible to "upgrade" to Angband 2.8.X, keeping your old savefiles, high score list, and other files, instead of starting over. Angband 2.8.X uses a platform independant file format for the binary files that store information about games in progress, known as "savefiles". Also, Angband 2.8.X is able to translate savefiles from any version of Angband from Angband 2.5.1 on. Unfortunately, pre-2.5.1 savefiles are not usable, since they used a system specific savefile format, and did not have an official definition, and sometimes include assumptions that are no longer valid. To use an old savefile, simply copy it into the "lib/save" directory, changing the name of the savefile (if necessary) to satisfy the requirements of the platform you are using. In general, the savefile should be named "UUU.NNN" or "NNN" where "UUU" is the userid of the player (on "multiuser" systems), and "NNN" is the name of the "character" in the savefile. Note that only "multiuser" platforms use the "UUU.NNN" form, and the "period" is required. Angband 2.8.X uses a platform independant (?) file format for the binary file used to store the high score list. This file may be moved between platforms (if needed) or copied from older versions (Angband 2.7.4 or later). Angband 2.8.X uses a high score file called "scores.raw", which is kept in the "lib/apex" directory. Older versions kept the "scores.raw" file in the "lib/data" directory, along with several other version specific files. Angband 2.8.X uses a platform independant file format for the ascii files used to store information about dead players, which is used to help create "player ghosts" in the dungeon. If necessary, these files, which are kept in the "lib/bone" directory, may be copied from older versions, as long as you make sure that they are renamed as "bone.XXX" where XXX is the level on which the dead player died, and that every line in the file is "complete". It is also possible to delete the contents of the "lib/bone" directory at any time, which will prevent the creation of player ghosts. Currently, these files are not actually used by the game. Angband 2.8.X uses a set of ascii "configuration files" which are kept in the "lib/file" directory (see below). Certain of these files may be taken from older versions, though most are optional for the most common configurations. Angband 2.8.X uses a set of ascii "user pref files" which are kept in the "lib/user" directory. Some of these files are usable on multiple platforms, but in general they are restricted to a single platform. In general, "pref" files from older versions may be used with Angband 2.8.X, by moving them to the "lib/user" directory from the old "lib/pref" or "lib/pref-*" directories, but note that some important changes have been made in Angband 2.8.X, and, for example, old "pref" files which change the "attr/char" definitions of "terrain" features such as walls or doors must be changed to use the new methods. Make sure to put comments in your "user pref files" describing the purpose and the proper platform for your files, to facilitate future upgrades. === Directory "src" === The "src" directory contains the complete set of source files. The "main-???.c" and "Makefile.???" allow compilation on various systems. Some of these systems (Macintosh, Windows) require "extra" files, most of which are located elsewhere (except for the "A-mac-h.*" Macintosh files). === Directory "lib" === The "lib" directory contains all of Angband's special sub-directories. === Directory "lib/apex" === The "lib/apex" directory contains the "high score" files. The "scores.raw" file contains the "high score" table, in a "semi-binary" form, that is, all the bytes in the file are normal ascii values, but this includes the special "nul" or "zero" byte, which is used to separate and pad records. You should probably not attempt to modify this file with a normal text editor. This file should be (more or less) portable between different platforms. It must be present (or creatable) for the game to run correctly. === Directory "lib/bone" === The "lib/bone" directory contains special "player ghost" template files. The files in this directory have filenames of the form "bone.NNN" where "NNN" is the level on which the player died. They contain information about the dead player, currently, one line each for the player name, maximum hitpoints, race, and class. These files are probably portable, and are probably compatible with older bone files, if those files are renamed, and a final newline is added for compatibility. Actually, only the "player name" from these files is actually used. === Directory "lib/data" === The "lib/data" directory contains various special binary data files. The files 'f_info.raw', 'k_info.raw', 'a_info.raw', 'e_info.raw', 'r_info.raw', and 'v_info.raw' are binary image files constructed by parsing the ascii template files in "lib/edit", described below. These files are required, but can be created by the game if the "lib/edit" directory contains the proper files, and if the game was compiled to allow this creation. === Directory "lib/edit" === The "lib/edit" directory contains various special ascii data files. The files 'f_info.txt', 'k_info.txt', 'a_info.txt', 'e_info.txt', 'r_info.txt', and 'v_info.txt' are ascii template files used to construct the binary image files in "lib/data", described above. These files describe the "terrain features", "object kinds", "artifacts", "ego-items", "monster races", and "dungeon vaults", respectively. The ascii template files are easier to edit than hard-coded arrays, and also prevent compilation errors on some machines, and also shrink the size of the binary executable, and also provide a user-readible spoiler file of sorts. These files are optional if the game is distributed with pre-created binary raw files in "lib/data". === Directory "lib/file" === The "lib/file" directory contains various special ascii data files. The 'news.txt' file is displayed to the user when the game starts up. It contains basic information such as my name and email address, and the names of some of the people who have been responsible for previous versions of Angband. You may edit this file (slightly) to include local "site specific" information such as who compiled the local executable. You should refer the user to a special "online help" file, if necessary, that describes any local modifications in detail. The first two lines of this file should be blank, and only the next 20 lines should contain information. The 'dead.txt' file is displayed to the user when the player dies. It contains a picture of a tombstone which is filled in with interesting information about the dead player. You should not edit this file. The optional file 'wizards.txt' may be used to specify which users may enter 'wizard' mode. A missing file provides no restrictions, and an empty file prevents everyone from entering 'wizard' mode. This file is only used on multi-user machines, otherwise there are no restrictions. The optional file 'time.txt' may be used to restrict the "times" at which the game may be played, by providing specification of which hours of each day of the week are legal for playing the game. See 'files.c' for more details. A missing file provides no restrictions, and an empty file will, by default, forbid the playing of the game from 8am-5pm on weekdays. This file is only used on multi-user machines, and only if CHECK_TIME is defined, otherwise, there are no restrictions. The optional file 'load.txt' may be used to restrict the "load" which the game may impose on the system. See 'files.c' for more details. A missing file provides no restrictions, and an empty file will, by default, restrict the "current load" to a maximal value of 100*FSCALE. This file is only used on multi-user machines, and only if CHECK_LOAD is defined, otherwise, there are no restrictions. === Directory "lib/help" === The "lib/help" directory contains the "online help" files. This directory is used to search for normal "on line help" files. === Directory "lib/info" === The "lib/info" directory (optional) contains the "spoiler" files. This directory is used to search for any "on line help" file that cannot be found in the "lib/help" directory. The user may "redirect" this directory to point at any available directory. Note that the default "help.hlp" file allows the "9" key to access a help file called "spoiler.hlp", and allows the "0" key to access "user.hlp". These special help files can thus be placed in the user's own "info" directory to allow the on line help to access his files. === Directory "lib/save" === The "lib/save" directory contains "savefiles" for the players. Each savefile is named "NNN" where "NNN" is the name of the savefile, or, on some machines, the name of the character, or, on multi-user machines, "UUU.NNN", where "UUU" is the player uid and "NNN" is the savefile name. The savefiles should be portable between systems, assuming that the appropriate renaming is perfomed, and any file "type" information is specified (for the Macintosh). === Directory "lib/user" === The "lib/user" directory contains the "user pref files", if any. In general, these files are used to "customize" aspects of the game for a given site or a given player. These files can define "macros" (which allow a single keypress to perform a complex action), set options (which affect how the game will handle various situations), and specify visual mappings for various monsters, objects, or terrain features. See "files.c" for more information on the proper "format" of these files. === Directory "lib/xtra" === The "lib/xtra" directory contains special system files, if any. --- Ben --- zangband/z_faq.txt0000755000000000000000000004537610250356274013227 0ustar rootroot ZAngband Official FAQ by Steven Fuerst svf@mssl.ucl.ac.uk April 2nd, 2000 ========================================================================== 1. About this FAQ ========================================================================== -------------------------------------------------------------------------- 1.1 Introduction -------------------------------------------------------------------------- ZAngband is a freeware roguelike computer roleplaying game, based on Angband. This document lists frequently asked questions and answers. -------------------------------------------------------------------------- 1.2 Table of contents -------------------------------------------------------------------------- 1. About this FAQ 1.1 Introduction 1.2 Table of contents 1.3 Where can I get this FAQ? 2. General questions 2.1 What is ZAngband? 2.2 On what computers can I play it? 2.3 Where can I download it? 3. System-specific questions 3.1 DOS 3.1.1 Can I play it in the old ASCII-mode? 3.1.2 Can I use multiple savefiles? 3.1.3 Why uses the DOS version only 2/3 of the screen? 3.2 Windows 95/98/NT/XP 3.2.1 How do I run in the Windows version? 3.2.2 Why do I get an error message when I try to switch on graphics? 3.2.3 The screen isn't refreshing properly when I move around, how can I fix this? 3.3 Unix 3.3.1 How do I change the number of terminals used? 3.3.2 How do I change the tileset used? 4. Game-play questions 4.1 Artifacts 4.1.1 What does the artifact xyz do? 4.1.2 I've found an artifact, but it doesn't show up in the list of known artifacts. Is that a bug? 4.1.3 I've found an artifact in a store, but I thought artifacts can never appear there if not sold to the store. Is that a bug? 4.1.4 Why isn't there a list of 'seen' artifacts any more? 4.2 Monsters 4.2.1 I've found a 'Greater Hell Beast' that never does any damage to me, and that seems to be immune to my attacks. What is it? 4.3 Objects 4.3.1 What does a 'magical figurine' do? 4.3.2 What does a 'statue' do? 4.3.3 Why do I have a 'lantern of everburning' with a light radius of one square? 4.4 Miscellaneous 4.4.1 What is an "anti-magic shell"? 4.4.2 What are virtues, and how can I affect them? 4.4.3 Why isn't there a staircase downwards on dungeon level 15? 4.5 Quests 4.5.1 What happened to the fixed quests after version 2.4.0? 5. Other questions 5.1 Interface 5.1.1 Why can't I see my spells or missiles flying through the air? 5.1.2 What does the "(number / number)" on missiles mean? 5.2 Bugs 5.2.1 A monster "points at me and curses", and the game crashes! What is going on? 5.2.1.1 Wait, I have version 2.1.0b and the game *still* crashes When a monster "points at me and curses"! -------------------------------------------------------------------------- 1.3 Where can I get this FAQ? -------------------------------------------------------------------------- This FAQ is available from: http://thangorodrim.angband.org/zangband-faq.txt ========================================================================== 2. General questions ========================================================================== -------------------------------------------------------------------------- 2.1 What is ZAngband? -------------------------------------------------------------------------- ZAngband is one of the many variants of the freeware roguelike computer roleplaying game Angband. -------------------------------------------------------------------------- 2.2 On what computers can I play it? -------------------------------------------------------------------------- ZAngband is available for many different systems, including MS-DOS, Windows 95/98/NT, Macintosh, Amiga, OS/2, Acorn RISC OS, and the many different Unix-derivates including Linux. -------------------------------------------------------------------------- 2.3 Where can I download it? -------------------------------------------------------------------------- The newest version of ZAngband is always available from "Thangorodrim - The Angband Page" at: http://thangorodrim.angband.org/ and the Angband FTP-Server at: ftp://clockwork.dementia.org/angband/ ========================================================================== 3. System-specific questions ========================================================================== -------------------------------------------------------------------------- 3.1 DOS -------------------------------------------------------------------------- -------------------------------------------------------------------------- 3.1.1 Can I play it in the old ASCII-mode? -------------------------------------------------------------------------- Yes, you can. The old IBM-mode is included in the DOS version and can be activated with the '-mibm' option. Just run ZAngband with 'ZAngband.exe -mibm' to switch it on. -------------------------------------------------------------------------- 3.1.2 Can I use multiple savefiles? -------------------------------------------------------------------------- Yes, you can. Run ZAngband with the -u option. For example: 'ZAngband.exe -uFred' loads the character called Fred. If no savefile called 'Fred' is found then a new one is created. To use another character just run ZAngband with a different name. -------------------------------------------------------------------------- 3.1.3 Why uses the DOS version only 2/3 of the screen? -------------------------------------------------------------------------- The lower part of the screen is used for displaying two extra windows with additional information. To activate this feature, press '=' to access the ZAngband options, then press 'W' to enter the 'Window Flags' section. Now you can assign various informations to the two special windows lower part of the screen. Move the cursor to the 'Mirror' column and switch on one of the options with the 'y' key. A good choice is the 'Display messages' option. Now move to the 'Recall' column and select another option (for example 'Display inven/equip'). Now press the ESCAPE key two times to leave the options menu and press Ctrl-R to redraw the screen. -------------------------------------------------------------------------- 3.2 Windows 95/98/NT/XP -------------------------------------------------------------------------- -------------------------------------------------------------------------- 3.2.1 How do I run with the numeric keypad in the Windows version? -------------------------------------------------------------------------- Press the Num-Lock key to switch off Num-Lock mode. Now you should be able to run when pressing Shift and a key of the numeric keypad. -------------------------------------------------------------------------- 3.2.2 Why do I get an error message when I try to switch on graphics? -------------------------------------------------------------------------- The 'Unusable bitmap palette (255 entries) - Could not initialize graphics' error means that you tried to use graphics with a desktop set to 256 colors. Switch to high-color or true-color mode to fix this problem. -------------------------------------------------------------------------- 3.2.2 The screen isn't refreshing properly when I move around, how can I fix this? -------------------------------------------------------------------------- Try toggling the "bizzare display" option. This seems to affect windows XP systems mostly. -------------------------------------------------------------------------- 3.2 Unix -------------------------------------------------------------------------- -------------------------------------------------------------------------- 3.2.1 How do I change the number of terminals used? -------------------------------------------------------------------------- Start the game with the '-- -n#' command-line option, with the '#' as the number of terminals you want (up to eight). Start the game with '-?' to get a list of all command-line options. -------------------------------------------------------------------------- 3.2.2 How do I change the tileset used? -------------------------------------------------------------------------- Start the game with the '-g' command-line option to get graphics. You can then use the '-b' option to pick the tileset. '-b8' picks the old 8x8 tileset. '-b16' picks the newer Adam Bolt 16x16 tileset. If you use the gtk port, then you can alter the tileset used while the game is running via the Options:graphics menu. You can also toggle the use of graphics or transparency through the same menu. ========================================================================== 4. Game-play questions ========================================================================== -------------------------------------------------------------------------- 4.1 Artifacts -------------------------------------------------------------------------- -------------------------------------------------------------------------- 4.1.1 What does the artifact xyz do? -------------------------------------------------------------------------- Most of the artifacts in ZAngband have some random features, and some are even completely random, so you have to find out what an artifact does by yourself. Try reading a scroll of *Identify* (the stars are important), or use 'Research item' service offered by some buildings in the towns. Many spellcasting classes get the *Identify* spell at higher levels. You can also sell artifacts to a store and buy it back immediatly, but this will cost you a lot of money. The powers of items that have been *Identified* or bought from stores can be displayed at any time with the 'I' command. -------------------------------------------------------------------------- 4.1.2 I've found an artifact, but it doesn't show up in the list of known artifacts. Is that a bug? -------------------------------------------------------------------------- No. You've found a 'random artifact'. These kind of artifacts get random powers and names, and they are not displayed in the list of known artifacts. The random artifacts can be found in addition to the normal ones, and they may even appear in stores from time to time. -------------------------------------------------------------------------- 4.1.3 I've found an artifact in a store, but I thought artifacts can never appear there if not sold to the store. Is that a bug? -------------------------------------------------------------------------- No. The store is selling a 'random artifact'. See question 4.1.2. -------------------------------------------------------------------------- 4.1.4 Why isn't there a list of 'seen' artifacts any more? -------------------------------------------------------------------------- With the preparation for the ability to add external mods to the game, it no longer is possible to track which artifacts the player has seen with any certainty. With the addition of random artifacts, the list becomes even less useful. So rather than have a buggy list that leaves out most artifacts the player sees, it has been removed. -------------------------------------------------------------------------- 4.2 Monsters -------------------------------------------------------------------------- -------------------------------------------------------------------------- 4.2.1 I've found a 'Greater Hell Beast' that never does any damage to me, and that seems to be immune to my attacks. What is it? -------------------------------------------------------------------------- The 'Greater Hell Beast' is a joke to scare all the veterans who know that an "U" is normally a very dangerous greater demon. It appears very early and can't hurt you directly, but it's very annoying. The huge amount of hitpoints and the big armor-class make it nearly invincible and even if you manage to kill it, it's not worth the efforts. -------------------------------------------------------------------------- 4.3 Objects -------------------------------------------------------------------------- -------------------------------------------------------------------------- 4.3.1 What does a 'magical figurine' do? -------------------------------------------------------------------------- A figurine is a small magical replica of a monster from the dungeon. When a figurine is thrown, a pet of the figurine's monster type will be generated. -------------------------------------------------------------------------- 4.3.2 What does a 'statue' do? -------------------------------------------------------------------------- Statues made of various materials can be found throughout the dungeon. Unlike figurines, they have no magical attributes but may be worth selling depending upon the material from which they are made. -------------------------------------------------------------------------- 4.3.3 Why do I have a 'lantern of everburning' with a light radius of one square? -------------------------------------------------------------------------- Your lantern was zapped by a trap of darkness. You can refuel it to make it glow at the normal rate again. -------------------------------------------------------------------------- 4.4 Miscellaneous -------------------------------------------------------------------------- -------------------------------------------------------------------------- 4.4.1 What is an "anti-magic shell"? -------------------------------------------------------------------------- An anti-magic shell around you prevents you from casting any spells and using mindcrafter powers. It also increases your saving throw vs. spells to 95%, prevents the activation of some mutations, and helps against the draining effects of the Jewel of Judgement. -------------------------------------------------------------------------- 4.4.2 What are virtues and how can I affect them? -------------------------------------------------------------------------- The game keeps track of lots of little things you do as a player. These affect a series of 'virtues' that are internally represented as counters that are either increased or decreased depending on those actions. The virtues will be shown to you when you quaff a potion of self-knowledge. They also can be accessed via a not-so-hidden option in the '~' menu. The virtues have no gameplay effect - they are just there to give you a hint at what type of playing style you have. However, don't take them too seriously as they are very unbalanced. It is almost impossible to stay on the good side of nature, for example, because of the huge number of 'natural' creatures on the first few levels of the dungeon. -------------------------------------------------------------------------- 4.4.3 Why isn't there a staircase downwards on dungeon level 15? -------------------------------------------------------------------------- The town dungeons only go down to level 15 (750'). You need to travel to a dungeon in the wilderness to go any deeper. The wilderness dungeons have different types of monsters, rooms, and objects in the them. -------------------------------------------------------------------------- 4.5 Quests -------------------------------------------------------------------------- -------------------------------------------------------------------------- 4.4.1 What happend to the fixed quests after version 2.4.0? -------------------------------------------------------------------------- The fixed quests are slowly being rewritten to fit into the new wilderness. Unfortunately, this turned out to be much harder than we thought, so towards the end of the 2.5.x development branch we realised we wouldn't get them done in time. This meant that the 2.6.x stable branch was released without the old fixed quests, and only with the random dungeon quests. During the 2.7.0 branch, the fixed quests will be replaced with 'randomised fixed quests' to fit in with the random wilderness and dungeon. The 'camp' quests are not an indication of what these will be like other than the fact that they are merged seamlessly with the wilderness. (Those were just for testing purposes.) The next stable branch will have the replacement quest framework. ========================================================================== 5. Other questions ========================================================================== -------------------------------------------------------------------------- 5.1 Interface -------------------------------------------------------------------------- -------------------------------------------------------------------------- 5.1.1 Why can't I see my spells or missiles flying through the air? -------------------------------------------------------------------------- First change the "base delay factor" in the options menu to a higher setting. A value of 4 (64 milli-seconds) displays the graphical effects nicely. Also check that "Flush output before every command" in the Efficience Options menu is turned on. -------------------------------------------------------------------------- 5.1.2 What does the "(number / number)" on missiles mean? -------------------------------------------------------------------------- The first number shows the average damage done per shot, and the second shows the average damage done per round. The two can differ because you can fire weapons faster or slower than one per round. -------------------------------------------------------------------------- 5.2.1 A monster "points at me and curses", and the game crashes! What is going on? -------------------------------------------------------------------------- The version you are playing is 2.1.0. When a monster tries to curse an item you are wearing, the curse attempts to change the inscription on the item to "cursed". However, if the item has no previous inscription at all, the game crashes. Solution #1: Upgrade to 2.1.0b or later version. They fix this bug. The items get cursed as they should. Aren't you happy! (alternative) Solution #2: Give all your equipment some inscription. The items get cursed as they should. Aren't you happy! N.B. This version of the faq document does not come with the version 2.1.0b. Try playing whatever version you got the faq from and you should not encounter the problem! -------------------------------------------------------------------------- 5.2.1.1 Wait, I have version 2.1.0b and the game *still* crashes When a monster "points at me and curses"! -------------------------------------------------------------------------- You are using the misleadingly named Windows port "Zangband 2.1.0b". Windows Zangband "2.1.0b" still has the bug ("real" 2.1.0b ports have the fix). The Windows port that fixes the bug is 2.1.0c. For all other platforms (Unix, DOS, Amiga etc.) version 2.1.0b fixes the bug. zangband/z_update.txt0000755000000000000000000054323010250356274013732 0ustar rootrootThis is a document file listing the changes in Zangband (compared to the standard Angband 2.8.1.) No long explanations, just a list. See code for details. In reversed chronological order (more or less): Changes in Zangband 2.7.5pre1: 2005-06-04 14:26 rr9 * src/zborg2.c: The borg didn't correctly reset his goal when entering a new level. 2005-06-04 14:25 rr9 * src/zbmagic2.c: The borg didn't predict the power from the "sleep nearby monsters" artifact activation correctly. 2005-06-01 14:49 siemelink * src/: zbmagic1.c, zborg6.c, zborg6.h, zborg8.c, zborg9.c: The borg often forgot to revisit shops. 2005-05-30 16:01 siemelink * src/: externs.h, object2.c, store.c: There was a bug in the shopping code that created duplicated items because the sold item was not taken away from the equipment. I fixed this bug by copying the drop_object code that handles droppng an equipped item that was missing in the shopping code. 2005-05-26 12:54 siemelink * src/zborg7.c: The borg can now *identify* an item that has special or terrible as pseudo-id but that has not yet been identified. 2005-05-26 12:51 siemelink * src/zborg2.c: The borg parses pet messages successfully 2005-05-18 12:21 siemelink * src/wizard1.c: I Synchronized the full monster spoilers with the monster knowledge routine. Fixed the object description spoiler that was showing unknown items as unknowns. 2005-05-18 12:17 siemelink * src/object1.c: If you examine a weapon that is to heavy for you you'll get a line saying just that. This comes in handy when you want to buy a new weapon in the shop. 2005-05-16 14:26 rr9 * src/run.c: Improved the running code so that it stops at corridor entrances when running alongside the wall of a room. 2005-05-15 21:28 rr9 * src/makefile.std: - Provided an "osx-dist" Makefile target to build a *.dmg disk image containing ZAngband. - Cleaned up the OS X part of the Makefile a bit. 2005-05-15 16:13 rr9 * lib/: data/makefile.zb, save/makefile.zb: The 'chown' command expects user and group separated by a colon instead of a period. 2005-05-15 15:55 rr9 * src/makefile.std: Added an "osx-install-tiles" Makefile target. An archive with the Mac-tiles can be found at: ftp://ftp.thangorodrim.net/pub/angband/Variant/ZAngband/ext-graf-ma c-275.zip 2005-05-15 15:26 rr9 * src/h-config.h: Don't add the user-id to the savefilename on OS X. 2005-05-15 14:49 rr9 * src/: Data.icns, Edit.icns, Save.icns, ZAngband.icns, ZAngband.r, ZAngband.xml, externs.h, h-config.h, init2.c, main-crb.c, main.c, makefile.std, util.c: Ported the basic Angband OS X code to ZAngband. 2005-05-15 13:15 rr9 * src/h-config.h: Allow the definition of PRIVATE_USER_PATH to be overwritten. 2005-05-15 12:55 rr9 * src/: z-util.c, z-util.h: Added secure versions of strcpy() and strcat() that check the buffer size. 2005-05-15 12:28 rr9 * src/makefile.std: Make is picky about tabulators and spaces. Don't use tabs where spaces should be. 2005-05-14 17:41 siemelink * src/zborg9.c: Have the borg parse the demon summoning message correctly. 2005-05-14 17:32 rr9 * src/dungeon.c: Added a workaround for a possible crash when starting a new character from an old savefile. 2005-05-14 17:31 rr9 * src/maid-grf.c: Second try to fix a crash bug when playing with bigscreen. 2005-05-14 16:26 rr9 * src/maid-grf.c: The last patch broke more things than it fixed, so undo it. 2005-05-14 16:19 rr9 * src/maid-grf.c: Added a workaround for a crash in bigscreen mode. 2005-05-14 15:46 rr9 * src/: cmd3.c, files.c: Fixed two compiler warnings. 2005-05-09 15:32 siemelink * src/object2.c: Fixed a message bug when you used the last item of a pile on the ground. 2005-05-09 14:56 siemelink * src/object1.c: Fix resize during object examination 2005-05-09 14:55 siemelink * src/cmd3.c: Fix resize during monster recall 2005-05-02 10:44 siemelink * src/files.c: Fix resizing when there is a menu on the screen. There was an unused parameter in show_file that I used to flag for the resize. 2005-05-02 09:33 siemelink * src/zborg2.c: Repair the borg recognizing uniques. 2005-05-02 09:32 siemelink * src/zborg8.c: Allow the borg to search twitchingly only in dungeons. Otherwise it burns its phase and teleport scrolls. 2005-04-28 12:11 siemelink * src/zborg6.c: Fix that the borg leaves the vanilla town for the dungeon Made the header of borg_flow_spread easier to use. 2005-04-28 12:08 siemelink * src/zborg7.c: Remove old comment 2005-04-28 11:51 siemelink * src/zborg3.c: Enble borg use of artifact phase door. 2005-04-01 12:52 siemelink * src/zborg7.c: The borg can now use artifact activation and hobbit racial power to eat. 2005-02-12 22:59 siemelink * src/zborg6.c: Oops, !vanilla should have been vanilla 2005-02-12 22:45 siemelink * src/zborg6.c: And avoid the borg trying to wait inside the building. 2005-02-12 22:39 siemelink * src/: zborg6.c, zborg8.c: Have the borg check his money before going to an inn to wait out the night 2005-02-12 22:35 siemelink * src/zborg8.c: Have the borg check his money before using an inn 2005-02-12 22:21 siemelink * src/: zborg2.c, zborg6.c, zborg9.c: tweaks to borg_status window, borg_find_town and that Line of Sight that I left in comments. 2005-02-03 22:36 siemelink * lib/edit/k_info.txt, src/zborg1.c, src/zborg1.h, src/zborg2.c, src/zborg4.c, src/zborg6.c, src/zborg6.h, src/zborg7.c, src/zborg8.c, src/zborg9.c: What a few days without the internet can do: There are some procedures that call borg_power() to swap items. Borg_power has as a side effect that it resets bp_ptr. But these procs would sometimes leave bp_ptr inconsistent with the unswapped state. These procedures now call borg_power at the end to make sure that bp_ptr is consistent with reality. The existing inconsistency was causing an error with borg_restock and a wilderness loop. Tried to fix another wilderness loop. There was a funny calculation to determine the best shop in town. But as the borg visits all the shops anyway I changed the best shop into the closest, unvisited shop. Put in some more GOAL_NONEs The previous entry trying to fix message handling for monks was incorrect as it missed al the uniques if they were hit by some fancy monk move. I fixed it by first testing the message for a suffix. I tried to fix the dungeon loop where the borg would hover close to a non-moving monster just around the corner. I put in another line of sight check to improve on this. There was a wilderness loop where the borg would reach a town but leave it right away for some unexplored wilderness. This won't happen anymore because now the borg will head for a shop when it realizes that it reached the town. 2005-01-26 10:58 siemelink * src/zborg7.c: Allow the borg to light up, detect traps and monsters in the wilderness 2005-01-26 10:02 siemelink * src/: zborg3.c, zborg7.c: Prevent the borg from wearing crap items like an amulet of teleportation 2005-01-20 21:38 siemelink * src/: externs.h, object1.c, object2.c: Add wraparound for examining objects. Now the tail end of your object description won't fall off. 2005-01-20 14:34 siemelink * src/: zborg3.c, zborg6.c: The borg no longer stops when as a Half_giant it wanted to eat rock 2005-01-19 13:05 siemelink * src/: cave.c, externs.h, run.c: If the player is runnig he'll stop when he's is next to a shop entrance. 2005-01-19 12:49 siemelink * src/: zbmagic1.c, zborg6.c, zborg6.h: Prevent the borg from eating a cure poison when the borg is full or gorged (unless the borg has three hp or less) 2005-01-19 11:51 siemelink * src/zborg2.c: Fixed parsing of hit-messages with a suffix Prevent borg_add_town to add towns called Bottom, lev or 50 ft 2005-01-19 09:33 siemelink * src/: zborg1.h, zborg5.c, zborg6.c, zborg6.h, zborg8.c, zborg9.c: Another attempt to prevent lifelock for borgs in the wilderness 2004-11-26 17:46 siemelink * src/: zborg1.h, zborg2.c, zborg2.h, zborg9.c: Fiddle with borg_add_dungeon 2004-11-25 22:43 siemelink * src/: zborg1.c, zborg1.h, zborg2.c, zborg2.h, zborg6.c, zborg6.h, zborg9.c: Prevent the borg from using towns called Bottom and 100 ft. Hack the borg_map_reader a bit further so the borg knwos the correct recall_depth. Make the borg look for more towns in the wilderness depending on its level. Prevent the borg from waiting out the night while on a swamp. If the borg is looking for an inn it might as well remember it with goal_shop. I rewrote the wilderness explorer so that the borg will explore a dark spot on the map by not going there all the way but just far enough. Prevented another bouncing borg, with shops this time. 2004-11-22 10:53 siemelink * src/: zbmagic1.c, zborg2.c, zborg5.c, zborg6.c, zborg7.c, zborg8.c, zborg9.c: Fix a bouncing borg in the wilderness. Borgs that have IM_POISON do not need to carry cure poisons anymore The wilderness map reader now includes wilderness dungeons. I rewrote borg_leave_level 2004-11-20 21:19 siemelink * src/zborg9.c: Aha, I forgot to press save. 2004-11-20 21:16 siemelink * src/: zborg1.c, zborg1.h, zborg2.c, zborg3.c, zborg5.c, zborg6.c, zborg6.h, zborg7.c, zborg9.c: I removed the last of the borg_notes_fmt I made an attempt to get rid of the bouncing borg (with telepathy) problem where it would flow to a monster, accidentally erase it, do something new, see the monster, flow to a it, erase it.... The borg no longer rests close to some mana draining monster I integrated the two routines that made the borg pick a dungeon The borg now realizes that a immunity also represents the relevant resistance. The status window noe also displays immunities to poison, light and dark. 2004-11-19 15:21 siemelink * src/dungeon.c: If you were not hurt by shallow swamp in the wilderness you still got the -The plants poison you- message. No longer. 2004-11-19 09:41 siemelink * src/: zborg1.c, zborg1.h, zborg6.c: Reduce the compile size by changing some static arrays used by the borg to be dynamically allocated. 2004-11-19 09:22 siemelink * src/zborg6.c: Fix the livelock that occurred when the borg was picking up items on painful feats. 2004-11-19 08:59 siemelink * src/: zborg6.c, zborg8.c: Fix some omissions for the borg wilderness handling 2004-11-18 12:55 siemelink * src/run.c: Allow players with levitation to continue running (ahem) over terrains that would hurt them without levitation. Now the player isn't stopped everytime he runs (flies) next to swamp or acid either. 2004-11-18 12:46 siemelink * src/: zbmagic1.c, zborg1.c, zborg1.h, zborg2.c, zborg2.h, zborg3.c, zborg4.c, zborg6.c, zborg6.h, zborg7.c, zborg8.c, zborg9.c: Prevent frequent crash. Further improvements to wilderness handling: The borg goes to an inn when it is dark, to a dungeon of the right depth compared to its equipment and to (all) the known towns to go shopping. 2004-10-30 22:51 siemelink * src/: zbmagic1.c, zborg1.h, zborg2.c, zborg2.h, zborg3.c, zborg4.c, zborg6.c, zborg6.h, zborg8.c, zborg9.c: Tweaks to the flow code for the wilderness changed a light beam radius test from <= to < Prevent the borg from wearing weapons of Morgul, it used to like them because of the see invisible. 2004-10-27 10:18 siemelink * src/: zbmagic1.c, zborg1.h, zborg6.c, zborg6.h, zborg8.c: If the borg is standing on some painful terrain feature it now can get off. 2004-10-26 22:12 siemelink * src/: zbmagic1.c, zborg1.c, zborg1.h, zborg2.c, zborg3.c, zborg3.h, zborg5.c, zborg5.h, zborg6.c, zborg6.h, zborg7.c, zborg8.c, zborg9.c: I have introduced the concept of multiple dungeons to the borg. The borg now keeps track of the location and depth of the dungeons it has seen. When about to Recall into a dungeon it will first pick a dungeon with the correct depth. When it is dark the borg will head for the closest Inn to wait out the night. I added another borg command ^z9: The list of dungeons. 2004-10-20 22:43 siemelink * src/zborg6.c: Enable the borg to explore the wilderness. If the borg is bored at a town or far away from dungeons it will locate the closest unexplored spot on the map and head in that direction. 2004-10-20 22:40 siemelink * src/spells3.c: Aha, here was that boundary fix for the map maker. 2004-10-20 22:38 siemelink * src/zborg9.c: Force the borg to release it flow target when that target is a jungle. 2004-10-20 22:36 siemelink * src/: scores.c, zborg8.c: Fix faulty boundary for the map maker. 2004-10-19 10:49 siemelink * src/: externs.h, object1.c, object2.c, store.c: Yet another fix to the *id* handling. I accidentally left out its use in the shops. Maybe this time it is ready. 2004-10-19 10:47 siemelink * src/zborg4.c: remove unused variable. 2004-10-19 10:46 siemelink * src/: cmd3.c, defines.h, load.c, monster1.c, save.c: Fix some library issues. 1: You couldn't find a researched monster in the monster memory if you had never seen it before. 2: If you research a monster that you have researched before it is now for free. 3: There were differences between what you'd see in the library and the monster memory, if the monster had never been killed. I fixed this by introducing a new monster flag, RF6_LIBRARY. If set then a monster description will include those details. To do this properly r_ptr->r_flags[6] has to be saved, which it wasn't. I found that a previous change of mine (finding out about swimming monsters) also needed this to be saved. I hadn't realize this until now. So I've upped SAVEFILE_VERSION, added a wr32b in save.c and an if (sf_version > 51) rd32b in load.c. I think I did that right, please correct me if I am wrong. Willem. 2004-10-17 22:28 siemelink * src/zborg8.c: Fix the borg use of the healer building. 2004-10-17 22:28 siemelink * src/: object1.c, object2.c: Add the pack letter when you are examining an object. 2004-10-17 22:26 siemelink * lib/edit/t_info.txt: Add a message to the healing building when all your stats are tiptop. 2004-10-17 21:38 siemelink * src/zborg3.c: Try to prevent a crash noticed by mnbv 2004-10-17 21:37 siemelink * src/zborg9.c: Fix omission of "%s", in a prtf 2004-10-15 19:39 siemelink * src/zborg5.c: increase the penalty for aggravate monsters. The borg would wield a weapon of morgul otherwise. 2004-10-15 18:33 siemelink * src/: zborg1.h, zborg2.c, zborg4.c, zborg8.c, zborg9.c: Get the borg to understand buildings that are not shops. The borg can now use the weaponmaster, the two magesmiths, the casino, the zymurgist, the map maker, the inn, the healer, the magetowers and the castles. Just for the library and the mutatalist I can't think of a way for the borg to use them advantageously. 2004-10-15 17:51 siemelink * src/zborg7.c: Yet another rewrite of the enchant weaopn and armour routines. This the aim was to let the borg always read the scroll and cast spells onyl for a while. There were put together and that prevented the borg from reading scrolls when it is in the wilderness for a long time. 2004-10-15 17:48 siemelink * src/zbmagic1.c: prevent the borg from using teleport level in town/wilderness because in the wilderness there usually is no dungeon beneath. 2004-10-14 22:21 siemelink * src/zborg6.c: Undo change to wait for recharging rods. It is necessary for them to be charging in the first place. 2004-10-14 22:20 siemelink * src/zborg3.c: The racial power could be used when the borg does not have enough hitpoints.... 2004-10-14 00:00 siemelink * src/: zbmagic1.c, zborg3.c, zborg3.h, zborg4.c, zborg6.c, zborg7.c: Add some code for the borg to handle the branding of bolts with the Crossbow of Brand. Add a hack for the borg to handle the second racial powers of Amberites and Ghouls. 2004-10-12 11:56 siemelink * src/bldg.c: If you are a monk you can now see your attacks listed at the weaponmasters. 2004-10-12 11:55 siemelink * src/wizard2.c: If you create money in wizard mode the maximum is now a bit less so it won't look odd in the shops on the gold remaining line. But don't worry, 99999999 is still plenty. 2004-10-12 11:53 siemelink * lib/edit/k_info.txt: Give all the armours an article (a/an). They were the only objects without them. 2004-10-11 23:46 siemelink * lib/edit/t_info.txt, src/bldg.c, src/object1.c: Smoothen the use of special buildings. 2004-10-11 14:17 siemelink * lib/edit/k_info.txt, src/cmd6.c, src/externs.h, src/l-spell.pkg, src/object2.c, src/script.c, src/spells3.c: Attempt to have a correct message after identifying an object. The message often contains the wrong letter for the identified object or the id scroll. This happens because of the sorting and combining which is done after the message generation. So I moved the sorting and combining forward. This solution is not perfect, there are some obscure possibilities that still generate an incorrect message. (When by reading a scroll of identify you combine two piles of scrolls of identify) To keep everything consistent I had to hack a bit too. So if my hacks are too monstrous please tell me how to make it better. 2004-10-11 14:05 siemelink * src/object1.c: That tweak of examining was no good. Back to normal. 2004-10-11 12:19 siemelink * src/: defines.h, l-ui.pkg: Complete changes to window options screen. 2004-10-11 12:16 siemelink * src/object1.c: Tweak *identify* 2004-10-11 12:13 siemelink * lib/edit/k_info.txt: Fix a double d: entry that wasn't to be. 2004-10-07 22:24 siemelink * lib/edit/t_info.txt: Correctly fix the compact runes with bOr instead of + 2004-10-07 22:23 siemelink * src/zborg4.c: Tweak borg appreciation of TY_curse 2004-10-06 23:35 siemelink * src/zborg9.c: Make sure that the borg ignores the new options emergency_stop 2004-10-06 23:34 siemelink * src/: cmd4.c, defines.h, effects.c, externs.h, tables.c: Add a #define WINDOW_CHOICE_MAX which contains the number of different widows you can have. Make the window submenu use it. Add a new option emergency_stop. If your hitpoints get low this option will discard all your input until you press a 'c' to continue. I've played with this option and I found that it cut down on the number of careless deaths by at least 50%. 2004-10-06 22:55 siemelink * lib/edit/t_info.txt: compact runes didn't work because this cannot be parsed. PROJECT_HIDE | PROJECT_JUMP. Apparently the | is not in lua. I don't know what is but in this case a + fixes the problem. 2004-10-06 22:53 siemelink * lib/help/spoiler.hlp: Replace reference to web page that no longer exists with a how to generate the spoilers yourself. 2004-10-06 22:52 siemelink * lib/edit/e_info.txt, src/dungeon.c: Attempt to prevent bad ego items from showing up as {good} or {excellent} 2004-10-06 22:36 siemelink * lib/edit/k_info.txt: Fix the script for staff of slowness 2004-10-06 22:35 siemelink * src/: zborg5.c, zborg7.c, zborg8.c: Minor tweaks. 2004-10-04 22:25 siemelink * src/: zbmagic1.c, zborg3.c, zborg3.h, zborg4.c, zborg5.c, zborg7.c: Add the use of BORG_ACT_* to the power calculations. Some items are no longer left at home: Enchant scrolls and resis heat/cold potions. 2004-10-04 11:36 siemelink * src/monster1.c: The monster memory now shows if a monster is flying ("flies" instead of "moves"), if a monster can swim or if a monster lives in water. 2004-10-04 11:35 siemelink * src/melee2.c: If the player sees a non-flying monster on lava or swamp or acid or deep water the appropriate flag (im_fire, im_pois, im_acid or can_swim) will be added to the monster memory. 2004-10-04 09:57 siemelink * src/: zbmagic1.c, zborg3.c, zborg3.h: Fix BORG_ACT_RECALL to use the Jewel of Judgment 2004-10-04 09:54 siemelink * src/object1.c: When you examine an object that has an immunity and vulnerability to fire, the vulnerability is no longer shown. The vulnerability wasn't counted because the immunity sets the damage to 0. Same with resistance and acid, cold, poison, elec, light and dark. 2004-10-04 09:52 siemelink * src/: zborg1.h, zborg4.c, zborg5.c: Have the borg carry wands/rods of teleport away. 2004-10-03 20:38 siemelink * src/main-win.c: This buffer is no longer needed here because it sits inside ingame_score 2004-10-03 20:37 siemelink * src/ui.c: Remove reference to procedure that no longer exists. 2004-10-03 12:04 sfuerst * src/fields.c: Add back missing check for field destruction in compact_fields(). Destroy it by hand if the script doesn't kill it for us. 2004-10-02 22:00 siemelink * src/zborg7.c: Let BORG_ACT_DETECT_MONSTERS to be used. 2004-10-02 21:59 siemelink * src/: zbmagic3.c, zborg1.h, zborg4.c, zborg5.c: Allow the borg to use an activation for the perma protection form evil. No longer collect scrolls of PfE, it is not worth the bother now that many items give prot from evil. 2004-10-02 21:37 siemelink * lib/edit/a_info.txt: Fix The Jewel of Judgment. Whether you said y or n didn't matter, you got the recall. The mistake was that 0 != nil 2004-10-02 21:36 siemelink * lib/script/spell.lua: Fix the summoning bug (no identification of the staff when it wsa used) properly, it was a faulty initialization. 2004-10-02 13:29 siemelink * src/: zbmagic1.c, zbmagic2.c, zbmagic3.c, zborg3.c, zborg3.h, zborg6.c, zborg7.c, zborg8.c: Have the borg use artifact activations that do no damage, like identify, teleport, invulnerabilty, etc. This is based on the activation description so the borg makes no difference between randarts and standarts. Replaced all the references to specific artifacts (for their activations) with their activations. 2004-10-02 13:08 siemelink * src/artifact.c: In activation texts replace & with and some other rewording. 2004-10-02 13:07 siemelink * src/zborg4.c: Correct usage of precognition with trap/door detection 2004-10-02 13:05 siemelink * lib/edit/a_info.txt: Give randarts and standarts the same format for damage and radius. 2004-10-02 13:04 siemelink * lib/edit/k_info.txt: Fix typo 2004-09-30 16:01 siemelink * src/zbmagic2.c: I revamped the borg support for activating artifacts that damage monsters. Instead of multiple passes on the activation description string only one is needed. 2004-09-28 23:51 siemelink * src/zbmagic2.c: Add borg support for artifact actications that can damage monsters. 2004-09-28 22:45 siemelink * src/tk/describe.c: Whoops, I overlooked this one. 2004-09-27 22:53 siemelink * src/artifact.c: Prevent the display of a (+0, +0%) boost to to_hit and to_dam for randart non-weapons. 2004-09-27 22:27 siemelink * lib/script/spell.lua: Fix the bug where you summon a monster and your scroll or staff would not get awareness. 2004-09-27 22:20 siemelink * src/store.c: Oops, I forgot the crucial one with the OB_STOREB stuff. 2004-09-27 22:18 siemelink * src/: artifact.c, birth.c, defines.h, object2.c, scores.c: Restrict the usage of OB_STOREB to inside the shops only. Once you buy an item the flag goes away. Introduce OB_NO_EXP that keeps track of items that shouldn't be scored because they came from a shop. This is to make shopbought items show their flavor. Previously, you'd have a shopbought potion of poison and you could not tell its flavor. This I found weird because the player has the potion right there in his pack so surely he can see if the flavor is polka-dot. 2004-09-27 11:27 siemelink * src/store.c: Tweak the store examine code which had both a msgf and a fput_str describing what object it was. 2004-09-27 11:19 siemelink * src/: flavor.c, wizard2.c: There was a bug where you could find out what a potion was by dropping several of them at home. If you would pick it up the home/store code would reveal what is was. I fixed it by testing for the OB_SHOPB flag. Only with that flag you can see an unid'd item completely in a shop. This warped the wizard mode play with object command so I fixed that by giving such object a temporary shop flag. 2004-09-27 10:49 siemelink * src/: artifact.c, cmd3.c, externs.h, object1.c, spells3.c, store.c: identify_fully_aux returned a bool in case there was nothing special. But the returned bool was always (TRUE). So I replaced the bool with a void and put the nothing special inside identify_fully_aux. I replaced a msgf that stated what you were examining with a fput_str to get that item as a header for the examination info. I added that you only need to be aware of an object to see its description from k_idx.txt. 2004-09-27 10:43 siemelink * src/object2.c: When you merge two stacks the *full* info from the second stack gets carried over. 2004-09-27 10:39 siemelink * src/zborg4.c: Allow the borg to wear exp_draining items when hehas reached lvl 50. 2004-09-27 10:38 siemelink * src/zborg8.c: Allow the borg to sell un*id*'d artifacts when it has lots of cash. 2004-09-27 10:34 siemelink * lib/edit/k_info.txt: I added lots of descriptions to items. Everything except for armour and weapons should have a description now. 2004-09-24 17:06 siemelink * src/zborg5.c: use borg_test_bad_curse to edcrease value on the equipment when the borg wears an item with such a curse inappropriately. 2004-09-24 17:05 siemelink * src/zborg4.c: Add drain_exp to the bad curses 2004-09-24 16:49 siemelink * src/: zborg4.c, zborg4.h, zborg7.c: There is now a test for the nasty curse (ty, no_tele, no_magic, drain_stat). The borg can ignore drain_stat when it has all sustains. 2004-09-18 21:20 siemelink * src/: zborg4.c, zborg8.c: Fix a home loop by changing the definition for home_damage. Further tweak the pick up from home code 2004-09-18 21:19 siemelink * src/zborg5.c: Change the usage for multibonus so that it doesn't give a bonus for resistances that are already covered by your race. 2004-09-17 09:14 siemelink * src/zborg8.c: If the home is full the borg will take the item away that costs the least, instead of just the negative ones. When attempting to add an item to the home when it is almost full the added value must be higher than the item in the home that adds the least. 2004-09-16 22:55 siemelink * src/zborg7.c: Add use of rod of restoration Add use of staff of detect evil tweak destroying items 2004-09-16 22:05 siemelink * src/zbmagic3.c: Add use of staff of earthquakes 2004-09-16 22:05 siemelink * src/zbmagic2.c: Add use of staff of starlight 2004-09-16 22:04 siemelink * src/zbmagic1.c: Add use of staff of cure light wounds 2004-09-16 22:03 siemelink * src/: zborg1.c, zborg1.h, zborg4.c, zborg5.c: Replace borg_note with borg_note_fmt Forbid the borg to drop items with ty_curse and the like at home. Add use of Amulet of Luck and Sustenance. Add use of unknown shadow and elven cloaks. Add use of Spell point bonus 2004-09-13 12:32 siemelink * src/: zborg1.c, zborg3.c, zborg3.h, zborg4.c, zborg5.c, zborg6.c, zborg7.c, zborg8.c, zborg9.c: Collapse borg_item_icky and borg_keep_unidentified into borg_worthless_item Introduce borg_spell_no_reserve for when you know there is no danger and theborg should do a reserve mana check. Use this for identifying and destroying things. Give a value fire/cold/elec/acid sheaths. Prevent a crash when evaluating borg_power_home(). Rewrite identifying objects. Prevent a home loop. Allow the sale of ego items that need *id* to make the shop *id* it. Make the borg remember the settings of 11 options that have to be set FALSE. WHen the borg is done the original value is restored. This is for my daughter who likes to explore caves. She uses easy_open and the borg can not. 2004-09-13 12:31 siemelink * src/dungeon.c: Add the cursed kn_flags to items when the curse is discovered by pseudo id 2004-09-13 12:22 siemelink * lib/edit/k_info.txt: Improve the description of Rings of Flames, Ice and Acid 2004-09-12 16:17 sfuerst * src/: dungeon.c, externs.h, load.c, main.c, save.c, z-config.h: Removed the broken VERIFY_SAVEFILE option because it didn't work. Removed the VERIFY_TIMESTAMP option because it was trivial to hack around by using a modified version of the game source code. Turned VERIFY_CHECKSUMS permanently on because it is good at detecting corrupted savefiles. (Its security aspect is ignorable though, being trivial to hack around - again with a modified copy of the game source code.) 2004-09-11 21:35 sfuerst * src/z-config.h: Remove the ALLOW_CAVERNS_AND_LAKES option. It is no longer used anywhere because the old stack overflow problem was fixed many versions ago. 2004-09-11 21:31 sfuerst * src/: dungeon.c, externs.h, files.c, util.c, variable.c, z-config.h: Remove the CHECK_LOAD option. Multiuser machines are quite a bit more powerful than in the Moria days. 2004-09-11 21:26 sfuerst * src/: dungeon.c, externs.h, files.c, util.c, z-config.h: Remove the old Moria (non-GPLed) CHECK_TIME option. 2004-09-11 21:18 sfuerst * src/: main.c, save.c, util.c, z-config.h: Remove the dead code for the out of date AFS file system. Removed the 'SECURE' option so it can be used for something else. 2004-09-11 21:09 sfuerst * src/: externs.h, main.c, util.c: Move multiuser machine initialization into a seperate function, and then put that in util.c 2004-09-11 18:43 sfuerst * src/: externs.h, files.c, main-crb.c, main-win.c, scores.c, ui.c, variable.c: Move all handling of highscore_fd into scores.c Export an extra function so the windows and mac carbon ports can do their magic. Move the death functions from files.c to scores.c 2004-09-11 18:01 sfuerst * src/: externs.h, files.c, util.c: Remove unused parts of the files API. 2004-09-11 17:45 sfuerst * src/zborg9.c: Remove the need for safe_setuid_grab/drop in the borg code by putting files in the users directory where they have the required permisions already. 2004-09-11 17:15 sfuerst * src/: externs.h, files.c, util.c: Move guid and signals code from files.c to util.c leaving only the pref-file and death-handling stuff in files.c 2004-09-08 11:29 sfuerst * src/files.c: Fix printing of enscriptions on equipment in character dumps. 2004-09-07 10:52 siemelink * src/: zborg1.c, zborg1.h, zborg3.c, zborg3.h, zborg4.c, zborg5.c, zborg7.c, zborg7.h, zborg8.c, zborg9.c: Remove the hack borg_wearing_cursed and replaced it with bp_ptr->status.cursed The borg now counts how many items it has to identify, remove curse or whatever. This way the borg can put items that need *id* at home and pull them out again when it has the *id* scroll. The borg can now wait for a rod to recharge before leaving a level. If the borg has a heavy curse it will show up orange on the borg_status menu. 2004-09-07 10:50 siemelink * src/: cmd2.c, cmd3.c, store.c: Fixed the bug where you'd know an item is cursed but would not get the KN_FLAG for it. 2004-09-02 21:42 siemelink * src/: zborg1.h, zborg3.c, zborg4.c, zborg5.c, zborg7.c, zborg9.c: The borg now judges the fail rate for cursed items correctly. The borg may now read a scroll of mundanity The borg now wield/wear cursed/heavy cursed or teleport items if the other flags on the item make it good enough. The borg ignores vulnerabilities if it has invulnerabilities. A Borg that can't eat shouldn't try it when destroying items. Some of the borg option handling was changed. A borg mage may now wear any pair of gloves, as long as it leaves him 300+ mana. 2004-09-01 23:21 siemelink * src/: zborg1.h, zborg4.c, zborg4.h, zborg5.c, zborg7.c: Add code for the borg to use scrolls of artifact creation. Finding one myself gave me the inspiration to do this. 2004-08-31 22:19 siemelink * src/quest.c: Add some breaks in a switch. 2004-08-31 22:18 siemelink * src/cmd4.c: I forgot. (It did have meaning) 2004-08-31 22:17 siemelink * src/effects.c: Give the warning bell when you are exactly on the HP-percentage 2004-08-31 22:14 siemelink * src/scores.c: fix score display bug when you are in the top 10. 2004-08-31 22:13 siemelink * src/artifact.c: Fix that you can see what the artifact is before you name it instead of after. 2004-08-24 18:58 sfuerst * src/maid-grf.c: Code cleanup in the pick_graphics() function. 2004-08-24 18:57 sfuerst * src/: main-x11.c, main-xaw.c: Fix bigtile mode to work in 32x32 and 8x8 tilesets. 2004-08-23 22:20 sfuerst * src/: angband.rc, fields.c, files.c, maid-grf.c, main-gtk.c, main-win.c, main-x11.c, main-xaw.c, spells1.c: Add support for David Gervais's tileset to several linux + windows ports. Note that it doesn't seem to work well with bigtile in x11 and xaw yet. Use the -b32 option to select it in the port options. Or use the menus in gtk and windows. 2004-08-23 22:16 sfuerst * lib/pref/: graf-dvg.prf, graf-win.prf, graf-x11.prf, graf-xaw.prf, xtra-dvg.prf: Add pref files for David Gervais's tileset. Note that these have been copied from Angband - so are totally wrong for [Z]. If anyone feels like fixing the tile allocations that would be a great help. At the moment barely anything works properly. 2004-08-23 22:12 sfuerst * lib/xtra/graf/: 32x32.bmp, mask32.bmp: Add David Gervais tileset from Angband 2004-08-18 10:47 siemelink * src/maid-grf.c: Add colour to the map home item list. 2004-08-16 19:32 siemelink * lib/edit/r_info.txt: Reduce the XP for Caaws by a factor 10. Getting 200000 XP when you are lvl 50 is ridiculous if the monster doesn't move. 2004-08-16 19:30 siemelink * lib/edit/k_info.txt: Add descriptions to books, foodstuff and diggers. From Otto Martin. 2004-08-15 22:27 sfuerst * src/: angband.rc, main-win.c: Removed the bizzare mode from main-win, and made it permanently on. (Since the broken windows versions are very common now.) Added bigtile support to main-win.c 2004-08-15 18:52 sfuerst * src/: cmd4.c, maid-grf.c, maid-grf.h, main-gtk.c: Add a new checkbox to main-gtk so bigtile mode can be toggled in-game. 2004-08-15 17:31 sfuerst * src/main-x11.c: Minor optimisation. 2004-08-15 17:30 sfuerst * src/main-gtk.c: main-gtk supports bigtile. 2004-08-15 15:55 sfuerst * src/main-xaw.c: Fix resize bug with xaw and bigtile. 2004-08-15 13:02 sfuerst * src/: main-x11.c, main-xaw.c, z-term.c, z-term.h: main-xaw.c now supports bigtile. 2004-08-15 12:17 sfuerst * src/maid-grf.c: Rename the resize_map() in miad-grf.c to resize_big_map() so it doesn't conflict with the resize_map() in cmd4.c 2004-08-15 00:08 siemelink * src/maid-grf.c: Resizing while looking at the big map now adjusts the size of the map. 2004-08-14 18:57 sfuerst * src/z-term.c: Redraw cursor only when it is inside the affected region. 2004-08-14 17:57 sfuerst * src/z-term.c: Redraw cursor in Term_redraw_section() 2004-08-14 17:48 sfuerst * src/main-x11.c: Fix Term_wipe_x11() to work properly. Make the copy/paste box use xor instead of grey. 2004-08-14 00:24 siemelink * src/: externs.h, main-win.c, scores.c: This all started with one bug: If your highscore list has 100 entries and you get killed with a score lower than nr 100, you'd get listed as 100. This way you'd bump off the real nr 100. I fixed that. Then I found at that pressing ESC does not always end the listing of the highscores list. I fixed that too. Then I thought, why not have more than 5 scores on a page? I use bigscreen after all. So I fixed that the whole page is filled with scores. Then I found out that if you resize your bigscreen while in the score display the main map needs updating too. So I fixed that too. 2004-08-14 00:16 siemelink * src/store.c: Add store commands to the display_store procedure 2004-08-14 00:03 siemelink * src/bldg.c: Fix resize to work inside buildings that are not shops. 2004-08-12 21:30 sfuerst * src/: main-cap.c, main-crb.c, main-dos.c, main-gcu.c, main-gtk.c, main-ibm.c, main-mac-carbon.c, main-mac.c, main-ros.c, main-tnb.c, main-vcs.c, main-win.c, main-x11.c, main-xaw.c, main-xpj.c, main-xxx.c: Actually use the Term_wipe_blah hook if we have one. This makes the close_game() screen glitch free in bigtile mode. 2004-08-12 20:42 sfuerst * src/xtra2.c: Make dimension door work properly again. 2004-08-12 20:34 sfuerst * src/z-term.c: Fix glitch where the screen isn't redraw properly when transitioning between non-bigtile and bigtile modes where the screen has an odd width. (So stuff on the far right hand side remained when it shouldn't have.) 2004-08-12 20:26 sfuerst * src/defines.h: Fix panel off-by-one glitch. 2004-08-12 20:08 sfuerst * src/maid-grf.c: Fix large scale map in bigtile mode. 2004-08-11 22:41 siemelink * src/: externs.h, maid-grf.c, quest.c: Rewrote that bit again in order to prevent a temp file to be opened when it is not needed for the map. 2004-08-11 22:39 siemelink * lib/edit/t_info.txt: Changed Copper Jewler into a Copper Jeweler 2004-08-08 22:29 sfuerst * src/: maid-grf.c, main-x11.c, util.c, z-term.c, z-term.h: More updates to the bigtile code. Glitches are much rarer now, and it seems to be almost usable. Fix use before open of fff in do_cmd_view_map_aux. 2004-08-08 16:22 siemelink * src/: bldg.c, externs.h, store.c: I made stores and buildings share the same procedure that provides all the commands that are not related to stores and buildings. These are commands like e, i, b, M, {, {, (, ), @, %, etc... 2004-08-08 16:18 siemelink * src/main-win.c: Remove from main-win.c the declaration for use_bigtile which was there as a hack and now is useless. 2004-08-07 23:21 siemelink * src/: externs.h, maid-grf.c, quest.c: Change the town info command on the wilderness map so that no temp file is opened if it is not needed 2004-08-07 21:54 siemelink * src/artifact.c: The breathe elements random activation was wrongly initialized causing a script erorr. I changed a FALSE with a TRUE to fix this. 2004-08-06 19:12 sfuerst * src/z-term.c: Fix term redraw in text mode. 2004-08-05 22:59 sfuerst * src/object1.c: Minor memory optimisation. 2004-08-05 22:58 sfuerst * src/: dungeon.c, externs.h, xtra2.c, z-term.c: Fix looking. (Bug due to panel code changes.) 2004-08-05 22:26 sfuerst * src/main-x11.c: Only allow bigtile mode if graphics is turned on. 2004-08-05 22:19 sfuerst * src/main-x11.c: Fix cursor size in bigtile mode. 2004-08-05 22:00 sfuerst * src/maid-grf.c: Fix off by one error in map drawing code which was stomping on the lower status line. 2004-08-05 19:26 sfuerst * src/maid-grf.c: Fix typo which stuffed up the cursor. 2004-08-05 19:09 sfuerst * src/z-term.c: Fix menus so that bigtile works a bit better. It still isn't perfect though... 2004-08-05 15:51 sfuerst * src/: cmd4.c, dungeon.c, generate.c, maid-grf.c, main-cap.c, main-gcu.c, main-win.c, wild1.c, wild3.c: Move nearly all calls of map_panel_size() to use PU_MAP instead. This probably breaks something due to delayed updates of panel sizes. However, it removes the last of the panel crud from the main-port.c files. 2004-08-05 15:36 sfuerst * src/: main-ami.c, main-cap.c, main-crb.c, main-dos.c, main-emx.c, main-gcu.c, main-gtk.c, main-ibm.c, main-lsl.c, main-mac-carbon.c, main-mac.c, main-ros.c, main-sla.c, main-tnb.c, main-vcs.c, main-vme.c, main-win.c, main-x11.c, main-xaw.c, main-xpj.c, main-xxx.c, z-term.c, z-term.h: Remove final use of TERM_XTRA_CLEAR. It was reduntantly being used in screen resize/refresh. 2004-08-05 15:06 sfuerst * src/z-term.c: Fix Term_redraw_section to work properly with bigtile. 2004-08-04 21:35 sfuerst * src/: maid-grf.c, xtra2.c: Stomp memory overrun bug when drawing map while resizing the panel. 2004-08-04 17:31 siemelink * lib/help/: command.txt, commdesc.txt: Add the roguelike display time command (') to the help 2004-08-04 17:11 sfuerst * src/: main-x11.c, main-xaw.c, zborg9.c: Speed up resize/redraw in x11 port massively. Fix a few remaining bugs with bigtile mode. The only problem now is the cursor... 2004-08-04 17:09 sfuerst * src/z-term.c: Prevent race condition in map updating. 2004-08-03 23:39 siemelink * src/notes.c: Prevent the Chaos-warrior of chaos in the notes, it is just a chaos-warrior. 2004-08-03 23:35 sfuerst * src/main-win.c: Remove old panel code. 2004-08-03 23:34 sfuerst * src/main-gcu.c: Update to the latest version of main-gcu.c from [V]. 2004-08-03 23:33 sfuerst * src/main-cap.c: Update to the latest version of main-cap.c from [V]. 2004-08-03 23:32 sfuerst * makefile.in, src/makefile.zb, src/lua/makefile.zb, src/tk/makefile.zb: Add a 'make dust' target to do minor cleanup of the backup files my editor drops behind. 2004-08-03 23:31 sfuerst * lib/pref/pref-mac.prf: Add support for numeric keypad on mac. 2004-08-03 23:30 sfuerst * src/: bldg.c, cave.c, cmd3.c, cmd4.c, defines.h, dungeon.c, externs.h, files.c, generate.c, load.c, maid-grf.c, maid-grf.h, main-x11.c, main.c, save.c, store.c, types.h, variable.c, xtra1.c, xtra2.c, z-term.c, z-term.h: Rewrite the panel code. Add a hacked-up version of the 'bigtile' patch to x11. There are two bugs remaining: The cursor seems to be broken. (But I don't think it is due to my changes.) Also there is a memory error when you resize the screen too much, spotted by valgrind. 2004-08-03 16:47 siemelink * src/dungeon.c: When you inscribe a recharging item with '!!' then you get a message when it is recharged. Based upon Andrew Farmers patch I made the recharge message to show how many of the pile are now charged. 2004-08-03 13:26 siemelink * src/zborg2.c: Add the town name to the list of shops 2004-08-03 13:25 siemelink * src/: zborg1.c, zborg1.h, zborg9.c: Get rid of options that were not needed and bools that were not used 2004-08-03 13:23 siemelink * src/: zbmagic1.c, zborg7.c: Repair the use of lightbeams 2004-08-03 13:22 siemelink * lib/help/: command.txt, commdesc.txt: Document the time command ^t 2004-08-03 11:58 siemelink * src/quest.c: Fix the buggy directions of wilderness dungeons. Zangband 2.7.4c - Fixed a bug that could cause the game to exit with an "Cannot find field to delete!" message. Zangband 2.7.4b - Lots of borg updates including: The borg can use ego items which are not *id*'d. The borg is more intelligent with light sources, and with spells that produce light. The borg now discards more 'boring' items without bothering to identify them. The borg uses various detection types in a better order. Major changes to the borg defence code. The borg will *id* rings of lordly protection. The floor can now be displayed with ^zg The borg will use rods/wands of disarming. Stopped the borg from collecting rods of pesticide once it has reached lvl 25. Added mindcrafters and high_mage as classes that want to reach 0% failure rates. Fixed calculation of borg armour class. Added some code that if the borg has no light but still can cast light_area then he should light area anytime he is next to a dark spot. Rewrote the recharging routine to include {empty} wands and staffs. - Fixed the release so that spell.lua isn't left out. - Fixed script for speed, poison and salt water potions. - Fixed loading of savefiles with 'out of bounds' wilderness blocks. - Changed build system to differentiate compile from link in sanitized make output. - Allow post-release versions, like this 'b' version to work with the build system. - Prevent message quest targets being homes. - Added more diagnostics to the 'test fields' debug command. - Fix bug in the fields code which was corrupting squares with multiple traps/corpses on them. - Make the output from doing '/*' shorter, so it wasn't truncated. - Allow browsing of any spellbook, since examining them will tell you what's inside anyway. - Made uniques killed list in colour. - Made known objects list in colour. - Made town list show store/building attr/chars. - Fixed crash in overhead map caused by jumping between levels. - Fixed bug in monster AI found by valgrind. - Fixed glitch in reset recall prompt. - Fixed timeout of esp. - Fixed wipe_o_list() so that objects in stores are deleted instead of slowly filling the objects list with dead objects as you play more and more games. - Fixed the alter-stats debug command to use the new internal stat limits. - Fix create_traps() so that it checks for 'naked' grids as commented. - Greatly tweaked wilderness generation so that the closer dungeons and quests to the starting town tend to be the least dangerous. The game now tries even harder to place the player in an 'easy' spot in the wilderness. - Fix the mindcrafter out of mana prompt. - Fix problem with the recursion C->lua->C->lua in apply_object_trigger() stomping on the 'current' object, and thus causing scripts to fail. - Fixed wands of haste monster to use the correct function. - Allowed randarts to have poison immunity. - Fix shallow swamps so they work properly, and can poison again. - Fix poison counter code so that poison resistance reduces the amount added appropriately. If you have a 'double' resist (timed + normal), then the counter doesn't get incremented. - Pruned out a huge number of functions from the API exported to lua. This should improve compilation time. - Added an extra option 'check_tranaction' so the user can decide if they want to be prompted for buying/selling items in stores. - Added price to item list so you can now shop with even fewer keypresses. - Fixed inventory damage so double resistance prevents it again. - Adjusted pricing of ego items and object flags. - Fixed all uses of get_check() in lua scripts. - Changed the tolua utility to generate smaller wrapper .c files by using macros for common operations. - Fix typo in 'fund a lost relic'. Zangband 2.7.4 - Moved the lua scripts for objects into k_info.txt instead of having them in object.lua Object scripts are saved in the savefile so add-ons will continue to work in new versions of the original game. - Changed object activations to use lua instead of C. - Artifact and Ego item activations are now handled inline as lua scripts in a_info.txt and e_info.txt - Fix the large scale wilderness map so that the monster camps are not examinable like towns are. - The "frost bolt" randart activation was doing acid bolt by mistake. - Display "Bottom" when the player is on the last level in a dungeon. - Display some information to show when lights of everburning are "empty". - Fix the booze potion effect in the wilderness. - Huge number of borg fixes from Willem Siemelink. The borg is now much smarter with spells and objects. The borg no longer gets confused in stores, and many borg 'loops' have been fixed. The borg now uses id / *id* again. The borg can now activate rings, rods, wands etc. The borg enchants items better. Improve the borg ^z commands displays. The borg uses emergency spell casting more intelligently. The borg understands more object flags. The borg stores spell books it can't use yet in its home. - The game now makes wooden bridges over the rivers in the wilderness. - Changed the effects of the 'strange luck' flag to make them more interesting. - Give monsters a fighting chance to resist protection at low levels. - Make having a chaos patron reduce spell mana costs for chaos spells. Increase chaos-warriors' mana costs to compensate. - Chaos-warriors can draw extra power from a chaotic weapon. - A Holy Avenger weapon gives a paladin a saving throw boost. - Change how saving throws are calculated. Wisdom has a larger effect, all classes have a higher maximum saving throw (at least 88%), and saving throw difficulty is modified by monster level in most cases. - Change the mana/int progression to advance slightly faster but top out at a lower value. Add a new flag, TR1_SP, and an item to provide it. Added this flag to the crown of the magi. - Added a new item: amulet of luck. - Added castles in the towns that can give you quests to do. Large castles give more difficult and rewarding quests than small castles. - Removed the old dungeon quests to kill n monsters of a given type. Replacing them are a group of out of depth monsters on the level plus a good item to find. You may also take bounty quests from a castle if you want. - Fixed font selection in the gtk port. - Fix arrows of explosion to only explode if they hit. - Fix various crashes in the monster AI. - Fixed stacking of torches bought from the store. - Correctly set BASE_TK_DIR to the value specified by --with-tkdir=foo. - Updated the tk port so it compiles with version 8.3 again. - Fix the tk port so that if it fails to start up due to X11 not existing, then the game will try some other port. - Added RISC-OS port from Antony Sidwell. - Make gtk the default port, not the tk port, which doesn't work very well yet. (This fixes the screen offset problem.) - Fixed overflow handling of object and monster lists in dungeon building. This stops the game from crashing when it tries to make a new dungeon when the old dungeon has a huge number of objects / monsters in it. - Fix a bug in the store cache that was converting empty stores into general shops. - Changed to a new configure.in build system. This one should be more robust. - Make the build system default to a less verbose method so that warning messages are easier to spot. Use "make V=1" to turn on verbose compilation messages. - New flag disturb_view to disturb when a new monster becomes visible (or nonvisible). This should stop people from running into floating eyes while carrying lanterns. - Change how monster generation depth is calculated. Now we have a target 'rating' and try to generate monsters to match it. - Add a new build target in makefile.std which allows you to cross-compile from linux to windows using mingw. You need wine installed, along with the bin-fmt extensions so tolua.exe can be executed. - Turn on the broken ASCII handling workaround on in windows XP. - Move stormbringer to a_info.txt where it belongs. - Fixed the annoying beeping in the birth menus. - Remove obsolete arena level options. These only appear in "city" dungeon types now. - Fix display of items in store when you learn about one by buying it. - Make sure the quest staircases are not created covered in a corpse. - Fix the character dump due to changes in the 'C' info screens. - Fix display of deadliness values to use the correct numbers in object descriptions. - After detection, overhead maps need to be redrawn. - Fix problems with some dungeons that start at level 2. - Fix monster summoning. Monsters can be summoned to dungeons where they might not normally live. - Changed a few unpopular monsters. - Rewrite of the random artifact making code. - Fix crash when creating random artifacts in town. - Allow descriptions for artifacts. - Change the way slays/brands and critical hits work: they add to deadliness rather than being a seperate multiplier. This makes figuring out how much damage you're doing easier. - Add more object flavors. Add comments to make it easier to tell just how many flavors of each type there are. - Use mmap() to load *_info.raw, if it's available. This makes startup much faster. - Don't use monte carlo for object depth chart if the exact routine is available (in debug mode). - Make some artifacts more flavorful with new good and bad points. - Add per-dungeon recall depths. - Updated the FAQ - Added some magical macros that allow easy access to the object and monster flags. - Change the sense of the dangerous (y/n) questions so that yes is always the safest option. - Use a smarter method of selecting which buildings to use. Rarity now works properly. - Fix some silly messages given when blind. - Fix broken knowledge of doors when you open/close them when blind. - Fix 'icky' status of the pattern so that it isn't broken by stairs. - Fix out of bounds problems with the light area spell. - Fix the message you get when taking off equipment when you have items the object will stack with in your inventory. - Fix creation of unremovable enscription created when destroying *id'ed* artifacts. - Fix placement of "price" and "weight" column headers in stores and make stores work with bigscreen. - Turn off the execute script debug command by default. - Fix poison damage so it works like the other basic resists. Swamps (but not thick swamps) will not poison you if you have resist poison. - Changed a few races so that they have immunity to poison, instead of just poison resistance. Gave those races vulnerabilities to some elements to counteract the advantage. - Fix broken interaction between fields and targetting commands. Corpses won't interfere with shooting now. - Make 'channel' rooms less annoying. - Rubble in the wilderness doesn't yield objects when dug out. - Add 'entrances' to the wilderness dungeons. Now you'll have to fight your way in. - Add the auto-more option from angband. - Make starting in wizard mode be able to prevent death again. (Instead of crashing on an assert.) - Improved the error messages given when lua scripts fail. - Fix bug in message display when printing strings with embedded carriage returns. - Fix magic mapping to work like it used to. - Fix bug causing dropped wands to not remember used charges properly when recombined. - Fix out of bounds problem with monster lights when teleporting in the wilderness. - Prevent crash when displaying large piles of items. - Made scrolls of *remove curse* give a message when successfully used. - Change monsters killed list under '~' menu to display monsters in colour. - Added a new command in the 'show_file()' screens to 'search again'. The highlighted strings now work with the colour escape sequences. - Prevent monster lights leaking through walls. Make the lights update the map when monsters move. - Fix crash when using cheat death in the dungeon whilst you are poisoned, afraid, blind etc. - Make wiz_lite() only light the dungeon for one turn. Zangband 2.7.3: - Added many new object flags to describe new behaviour. - Changed the build system to not use automake. The makefiles are recursively included, instead of recursively executed. This makes intra-directory dependancies work properly. - Added auto-detection of the gtk, tk and vcs ports in the configure script. - Fixed a bug in the lua source, MULT_RET was being used before it was defined. - More cleanup of the tk port source. This still is alpha-level quality though. - Replaced much of the random artifact code. The changes include: Preventing the slow digestion flag from being given to armour. Themed artifact types. More types of items can be random artifacts, including phials etc. The power of randarts depends on the depth you find them. - Fix problems with redrawing the screen when a racial activation kills a monster. - Tweaked the mutations, adding disadvantages to most "good" mutations, and advantages to some of the "bad" ones. Removed the regeneration modifier caused by having too many mutations. - Fixed a crash on loading corrupt savefiles, when o_max was set a the limit. - Changed the r_info.txt to make groups of monsters appear in a logical order in the dungeon. - Prevent multiple traps from being created on the same square. It seems no one likes this "feature". - Added a few new death messages. - Added a few new items. (A ring, two amulets and a sword.) - Removed player histories from the info screen. - Added themed dungeons. These will have differing distributions of monsters, objects, rooms, floors and depths. If you don't like the booty in one type of dungeon, try somewhere else. - Added approximately ten more types of rooms so that the new dungeons will look quite different from each other. - Tweaked item selection so that not quite so many out of depth items are made early in the dungeon. - Shortened the town names. - Added the dragons from Ingeborg Norden. - Minor fixups to the borg - it understands traps now. (Code from Matt T.) - Huge cleanups to the lua interface - instead of using set_blah(), use inc_blah() or clear_blah(). - Added a "return" flags to some items. This causes them to return to your hand when thrown. - Thrown weapons stack in your inventory. - Gave rogues an extra shot with a thrown dagger at level 10. - Only delay on TERM_XTRA_DELAY when the requested delay is >0. This should speed up the Borg a lot on systems that end the timeslice of the process when calling "usleep(0)". - Fixed a problem with resizing the bitmap in the gtk port, when bits per pixel and bytes per pixel don't correspond (for example 24 bit bitmap, but 4 bytes per pixel). - Fixed the pattern vortex abuse. - Fix problem with open/closing doors in the dark/blind. - Fix problem when there are too many objects on a square to list them all. Just list the first 23. - Fix bug with selling cursed items to stores causing them to be displayed for sale at 0 AU. - Fix bug with bows causing the STR and DEX bonuses to always apply. - Prevent the player from buying stacks of weapons / armour. - Optimised the overhead map and dungeon map term types. They should be much more usable on slow machines. - Fix object corruption in shooting code with regards to the fired ammo being paritally overwritten by a dead monsters loot. - Fix many bugs where changing the ego type of an item didn't cause a pack sort/combine. - Minor error messages are now immediately displayed in the message window. (bug reported by "Mogami") - Make sure regions are loaded properly. Before, we generated everything and then copied the loaded savefile over the top. This screws the object, monster and field lists. We now take care of the creation manually - fixing up the refcounts in allocate_block(). This fixes the "corrupted savefile" bug where the game was bombing out on an assert in object2.c 2.7.3 will load these "broken" savefiles with no problems. - Fix a couple of problems with darkness-causing spells. - Fixed a crash on displaying graphics on secondary terms in a few linux ports. - Removed autoscum and ironman_rooms options. The new dungeon-making algorithm makes much more interesting dungeons... - Removed the old "destroy dungeon" code - the new ruined dungeons look much better. - Made multiple dungeons appear in the middle of the wilderness. The number on the wilderness main-map is the starting depth of the dungeon divided by 10. (Just like quest difficulties.) - Add the pet info screen as an option in the pet menu. - Make the enter key function as 'yes' for yes/no prompts. - Add description lines for objects, and display them in the inspect command. (Most objects don't have descriptions yet.) - Make the "save and exit" prompt work better with bigscreen. - Fix autorepeat bug with menu code. (Makes the 'n' command work again.) - Glyph of warding works again. - Fix generation of crossroads in the wilderness. - Add support for broken Win 2k, which has problems displaying the "centered dot" character. This is from "danceswithcrows@usa.net" on rec.games.roguelike.angband. - Updated the FAQ. - The DOS version can now be compiled with current releases of the Allegro library. - Apply Bablos's fixes to the amiga port. - Fix the query-symbol command so that it doesn't leave junk on the screen. - Highlight quest monsters in visible monster list term. - Fix bounds checking so that rooms next to permanent walls on the dungeon boundary get lit properly. - Attempt to fix the problem where the game generates towns with blank spots for stores, when converting from old savefiles. - Display town names on large scale wilderness map when cursor is on the towns. - Add command to display list of stores for the highlighted town on the large scale wilderness map. - Removed the view_perma_grids option - it didn't do much. - Arena levels only appear in "city" dungeons. - Make deep swamp act safer with regards to resist poison. - Make the borg_cheat_death option work again. Zangband 2.7.2: - Made the HAVE_MKSTEMP option on by default for the linux ports. If you do not have a library with that function the option can be removed. - Fixed makefile.std so that the BSD ports will compile properly. - Fixed some dependancy problems in makefile.std in ports that add extra files to compile to the source list. - Added an option to makefile.std to compile without the gtk libraries. - Made the amiga port use the new overhead map interface. Removed the code that changes the graphical tile indecies only in the amiga port. It is trivial to add it in main-ami.c if needed. - Replaced the Amulet of Adornment with Amulet of Berserk Strength. - Fixed some monster pluralization issues. - The game no longer gives away information by displaying the color of unknown items in shops. - Fixed the bug causing wrong prices for items where the base type has a bonus/penalty to hit or to ac. - Fixed some bugs with the quark handling causing object inscriptions to be corrupted. - Acquirement will not give you cursed items. - Make sure 'good' and 'great' monster drops are not junk. - Added 'elemental' nests. - Add missing EASY_KNOW flags to mushroom types. - The chances for monsters summoned by artifact activations being hostile were backwards. - The depth and xp reward for a few monster types were adjusted. - Correctly handle changing and saving attr/char prefs on systems where 'char' is signed. - Updated configure.in and the makefile system - Changed the internal representation of the stats to be linear. The only gameplay change is the effects of stat loss from time effects. - The stats are displayed in decimal. - Don't adjust stats below 3. - Smoothed out the effects of stat potions. - There is some randomness applied to the last digit of starting characters' stats. - Added some borg fixes from Lunal (M. Titus). - Made Rings of See Invisible, Dexterity, Resist Fire and Cold, and Ring of the Cat more common. - Change the nominal depths of many items to match their actual drop depths, or in some cases add or change drop entries to match the nominal depth. (This fixes some old bugs caused by the dungeon rearrangement in 2.5.x) - Fixed the bug that caused arrows that miss to disappear and corrupt the object arrays. - Allow magesmiths (weapons) to enchant deadliness a bit higher, and display the possible enchantment correctly. - Fix browse info for many spells to actually match what they do. - Allow the player to dimension door onto trees. - Fix bug with drawing the player's light right after sunset. - Updated the help files. - Fixed a bug found by Victor Chou where the first item on the floor was used when you pressed '-' at the item prompt, even when that item was of an incorrect type. - Fixed the double-free bug for quark strings found by Randy Heit. - Halve the cost of using the magetowers. - Fixed a bug causing using a magetower with monster lights on to crash the game. - Changed the price of identify pack and restore all stats building options. - The stone wall spell doesn't affect permanent grids now. - Fixed the racial and mutation activation menu. - The *identify* information now uses bigscreen. - Fixed bug that caused heavily cursed items to be mistakenly shown as permanently cursed. - Scrolls of *Identify* give the message "You have nothing to *identify*" if they have nothing to work on. - Made sure the starting town has a Bazaar. - Added a hugely altered version of Tim Bakers Tcl/Tk port. This is alpha-quality and most functionality is currently disabled. It will be renabled over time. Start the game with the -mtnb option to try it out. The code has been changed to be much more portable than the original version. Other angband varients will be able to use it if the maid-grf interface is ported over. This port requires tcl/tk version 8.4.0 or higher to compile. - Changed alot of the string formatting code to be more powerful. It now can include colour codes, and calls to specialized format functions which can take arbitrary inputs. - Changed the message and screen display code to use the more powerful formatted string input. - You can use the escape character '$' followed by a letter from A to P in an inscription or player name to make coloured text. - These escape sequences also work in the help files to allow coloured text there as well. - Moved most of the low-level interface code to a new file: ui.c and created a generic menu system in that source file, instead of using many copies of virtually the same code throughout the source. - Simplified the API of the fields code by using vaargs instead of pointers to arbitrary structs. - Updated the Lua source to the 4.0.1 release. - Changed the terms code so that Term_save() and Term_load() will work with an arbitrary number of saved screens, rather than just one. - Changed the ascii art in the winners crown. - Removed the old code used to output a matlab .m file used to balance the dungeon distribution of monsters. - Removed many unused options from the borg command screen. - Removed all users of sprintf() in the main source, and replaced them with the safer strnfmt(). Changed all uses of strcat() to strnfcat() or strnfmt() to prevent buffer overflows. - Fixed crashes in the wilderness caused by recursion in trying to place monsters on swamp or lava tiles. - Fixed bug when buying wands from stores when you already have a stack with more charges than the number in the store. - Inscribing things with the '!' modifier works again. - Fixed some major bugs with spell effects and objects. The first object in a pile was affecting what happened to the ones below it in strange ways. - Prevented artifacts being created with +0 stealth. - Fixed enormous bug that was corrupting the player inventory when loading some savefiles. - Made spells work on the fake wilderness boundary squares. - Changed phantom beasts from light to dark blue, so they don't look like phantom warriors. - The rarity of a monster is now displayed in the monster memory. - Automatically load 'player.prf' at startup/birth. - Decreased number of messages displayed in character dumps to be 50 by default. (There is an option to restore the old behaviour.) - The deepest kills are displayed in character dumps. - Added a new item "Whip of Entanglement" - Fixed loading savefiles from dead characters. - Put the auto-notes files in the 'user' directory, instead of the savefile directory. This works much better in multi-player machines, where the file will be put in the ~/.angband/ZAngband directory. - Monsters killed in the wilderness now have reasonable drops, instead of having ones calculated as if they are on level zero. - Fixed the vanilla town option so it doesn't crash when the town is made. - Get rid of inconsistancy of colouring between charging and fully charged rods. - Jungle no longer makes running extremely annoying. - Immobile monsters don't say they are fleeing. - Fixed display bug with reading phase door scrolls on the ground. - Fixed the 'paralyzed!' message, which wasn't updated properly. - Improved the description of time attacks. - Fixed the problem with monster AI that was exposed by the day of the dove spell, and mindcrafter domination effect. - Made sure you can always see the price of items you are buying or selling by putting them on another line. - Fixed the silly message when removing an item you are wearing. - Fixed the mindcrafter psychometry spell to work like the mage identify spell, and only list available items. - Only show inscribed objects in the uninscribe command menu. - Fixed bugs with uncursing items, where the players information wasn't been updated fully. - The shatter armour / weapon effects now update the players object feelings. - Fix infinite loop when you try to dimension door to your current location. - Made the mindcrafter power menu work more like the standard spell menu, and display the available options all the time. - Fixed off-by one bug in object allocation code. - Fixed major bug when objects in the players pack combine. - Fixed a bug where non-passable fields weren't working as planned. - Selling items was giving free identify for the rest of the pile. This doesn't make sense for wands. - Prevent towns from having blank names. - Display the starting town name properly when the game begins. Zangband 2.7.1: - Split off the graphics-specific code in util.c and moved it into maid-grf.c - Added an overhead map interface into maid-grf.c This allows access to what the player knows about his surroundings in ports and in the borg. - The wilderness size is no longer editable via misc.txt, changing it caused savefiles to break. - Added terrain feature flags to f_info.txt and simplified most of the enormous if statements dealing with properties of terrain. - Added the missing walls to the overhead map priority list. - Improved the vanilla town so that the stores are distributed more evently. - Added a new option to the query symbol command. Ctrl-K will list the monsters you have killed as this player. - Added missing do_cmd_rerate() to the lua interface. Potions of new life wont crash the game now. - Reformatted the recharge building menu. - Added APW's [Z] borg for 2.4.0 This has been changed an awful lot, with nearly all of the low-level code now different. It uses the interface in maid-graf.c as much as possible, instead of going through the terms interface. This means that graphics is automatically supported. It also simplifies things enormously. It is still extremely buggy with heaps of missing features. eg. It doesn't know about traps, artifacts or the home. - Improved the pack AI. - Fixed loading of 2.6.2 savefiles. - Fixed the chest-inside-a-chest bug. - Torches given at birth will stack with those found/bought later. - Remove curse is more powerful, and will uncurse items in the pack as well as those being worn. - Fixed bug where AC and to_hit were much easier to enchant upwards than intended. - You require a large weapon skill in order to get a large number of attacks. This fixes the bug with mage combat ability being too large. - Changed the monster-nest/pit implementation. - Tweaked white ants a little bit. - Added a couple new monsters and renamed the second type of giant red ant to giant fire ant. - Updated the emx makefile. - The ZAngband Borg screensaver should load it's display preferences from 'zangband.ini' instead of 'angband.ini'. (It is disabled at the moment though.) - Optimised update_view() a little bit more. - Updated the indenter script to more closely match the Angband coding style. Nearly all of the source has passed through this indenter. - Items that give extra blows work again. - Made the Ring of Elemental Mastery more powerful, and fixed it so it can actually be generated. - Made elemental attack stat-loss rarer. - Stores now stock items they buy from the player if there is room. - Renamed a few stores, and added a Bazaar to the starting town. - Make stores only buy appropriate items, since the player can still sell anything to the Bazaar in the starting town. - Fixed the bug that was causing some stores to have no items for sale. - Add farms to the wilderness. - Made the more powerful shops much rarer. - Modify the price formula for enchanted items to be more reasonable for very high enchantments. - Add magetowers which can teleport the player between towns. - Use a more meaningful character for towns on the wilderness map. - Display the difficulty of quests on the overhead map. A '4' means a quest equivalent to depth 31-40. - Fixed the major bug with wilderness quest difficulty. - The large-scale wilderness map no longer shows area outside the world. - The distribution of town sizes should be better. - Scale the "law" map so there is equal area at each difficulty level in the wilderness. - Add a new option, ironman_deep_quests, to make dungeon random quests more like ZCE's. - Correctly notice kills of yellow lights. - Added more coloring of the monster memory. - Fixed the quark code so that objects don't lose their inscriptions or artifact/ego names. - Fixed bug the prevented weapon type effects from taking place. (Found by Zedar) - Cleaned up the projection code somewhat. Now missiles will travel in much 'straighter-looking' lines if possible. - Fixed the bug caused when shooting at monsters hidden behind other monsters causing the shot to hit a wall. - Changed the low-level object code. Moved the scanning of lists of objects to use an iterator. Changed how object compaction works. Changed things so that there are no statically defined objects. This prevents massive memory leaks now that quarks are refcounted. Changed the player and store inventories to use linked lists of items in k_info.txt This yields massive simplification of code that is duplicated everywhere. Equipment is still stored as an array. - Added a couple of weak low-level rings. - Added message-colour modifying to the '&' command. - Removed the old Mac port - now main-mac.new is main-mac.c - Turned the -DUSE_TRANSPARENCY compile-time option permanently on, as in the Angband cvs. - Replace The Cloak of Benedict with The Filthy Rag of Keri the Humble. - Removed haggling. - Removed support for savefiles before version 2.2.x - Improved the "standard" makefile alot, and moved many of the other makefiles inside it as options. Use "make -f makefile.std portname" to compile with this makefile. The default port is linux. This breaks some ports... but having out of date makefiles was even worse. - Removed the makefiles of ports that have been merged into makefile.std - Added a view towns option to the ~ which shows towns you have visited and the shops in them (and whether or not the town has stairs) - If you have toggle_xp on, it displays NEED instead of EXP in the main screen. - Display some obvious object flags on the 'C'haracter screen. - Removed two useless flush options, and the flush function works now. - Lua understands dos path seperators as well as unix ones. This means that cyg-win can now compile the complete game. - The "Wall of stone" spell no longer can destroy permanent rock. - Mindcrafter starting potion changed from Restore Mana to Restore Wisdom - Fixed the stat code to work properly again. The birth process in 2.7.0 could give way too large of a bonus to certain stats. - Make a few artifacts deeper and rarer. - The Oberon quest needs to be level 99, not 98. - Made the object list menues look better in bigscreen mode, and now always show the list of available choices. - Spell menus always show the list of spells now. - Made lanterns, torches and oil slightly more common since the phial is now deeper in the dungeon. - Changed the damage dice on whips and clubs. - Fixed the wrong racial ability description for Half-Trolls. - Terrain feature descriptions are now gramatically correct. - Added a new sword type 'elfblade'. - Moria Mode now invalidates several options that did not exist in Moria: connected stairs, autoroller, and point based generation. - Removed some old and broken options. - Made dragon shields and helms rarer. - Added a function to calculate the distribution of an object in the dungeon mathematically, rather than using a Monte Carlo method. - Disallow floating point in the source. - Added u64b and s64b types. This probably breaks most of the makefiles / ports. We fall back to "long long" if not using a C99 compiler. If your machine definately doesn't have a 64 bit type, you can disable the (currently tiny) code in wizard2.c that uses it. Most machines with enough ram to play the game will have a compiler with a 64bit type though. - Monsters with RAND_50 | RAND_25 will only make the 75% check, not the 50% and 25% checks as well. - Fixed the broken "flow by sound" code. - Fixed the bug with night->day transition in wilderness not resulting in map memorisation. - Wilderness quest object no longer make a sound when created. They can now be enchanted when found. They can be found in piles. - Turned on ironman_los permanently. This probably breaks things. - Quest descriptions now take into account the depth_in_feet option. - Attempted to fix use of roguelike keyset with the buildings. - Added a new option: view_player_colour. This toggles the colouring of the '@' character when you are hurt. - Got rid of a huge number of compile warnings from the lua code. No warnings are generated by the wrapper files now. - Removed a heap of warnings from the gtk and xaw ports. - Fixed saving of message colours in the savefile. - Updated the FAQ with recent common questions. - Removed the lua wrapper files from the archive, they can be created from the makefile. - Fixed a problem with the autoconf bootstrap method. - Rangers are better, not worse at hitting monsters hiding in trees. - Normal stores can generate good or excellent items again. ZAngband 2.7.0: - Removed the python code, and replaced it with scripting using Lua. - Added scripting for food, potions, scrolls, wands and staves. - Fixed the Amberite racial skills. - Climbing stairs no longer takes zero game time. - Replaced the combat system with a simplified version of Oangband combat version 2. Changes include: - Enchant scrolls act more like stat potions, the increase is variable. - The critical hit formula is simpler. - The deadliness calculation is linear. - The intrinsic percentage deadliness is shown on the weapon. - Changed the damage dice of arrows and bolts. - Updated util.c to be more like that in newer versions of Angband. - Updated the init code to the Angband 2.9.3 codebase. - Zangband handles file permissions like Angband. - Updated the mac, windows and dos ports to use the latest files. - Updated the gtk port to use pelpels menu code. - The newgame command line flag now works properly with the gtk port. - Removed many magic numbers from the sourcecode. - The chaos patron big heal reward now restores stats and then heals to ensure full heal like the potion of life. - Removed non-maximise mode, and changed maximise mode to have more variablity at the start of the game. This method takes the best attributes of both old options. - Disintegrate affects the new terrains in a smarter way. - Fixed possible crash in displaying shape-changing monsters. - Fixed the ironman_los option. - The ATTR_MULTI flag now takes into account the breaths if the monster has any. This is similar to the code in Oangband. Several vortices and Wyrms now use this flag. - Changed curse display on the info screen. A permanent curse is displayed like an immunity. - Added the improved identification patch from RML. - Split up the cave data type into player and non-player information. - The memorisation of the map has changed so you remember the terrain features. This prevents you from seeing Umber Hulks approaching as they dig their tunnels. - Added the GRID_SEEN flag from Angband, and optimised the update_view() and map_info() routines to use the new information. - Added a noise-level patch, similar to that in Oangband. The more noisy you are, the further away the monsters will have accurate flow information to your location. - Replaced the underlying wilderness code. The new code allows quests in the wilderness to be made. - Replaced the old cave[][] array with "regions". Regions allow multiple dungeons to be active at once. They also simplify the code for creating towns and quests in the wilderness. - Fixed the rng so it works on 64bit systems. - Prevented the player from gaining extra information when selling items to the rare stores. - Improved the artifact activation messages a bit. - The EAT_LITE mutation decreases o_ptr->timeout instead of the pval of the light. - Replaced the quest code. The new code supports quests in the dungeon wilderness, and general quests. - Fixed the spelling error with Aluminium flavoured items. - Added the missing documentation about the (+x/+y) information on ammo. - The players' colour depends on hitpoint status. - The disturb_traps option now works much better. You will get a message if you move outside a region you have detected for traps. - Major code cleanup - replaced all coordinates in the form (y,x) with the more natural (x,y) form. - The 'use all linux ports' option in makefile.std works now. - Added more lines to mondeath.txt and monspeak.txt - Decreased the xp for the plain gold ring, stopping score overflow. - Fixed problem when trying to destroy closed doors with GF_KILL_WALL. - Made the word of recall scroll more common in the shops and dungeon. - Fixed the road-making code so that it connects to town gates. - Fixed problems with spectres, doors, and the easy_open option. - Fixed the bug with monsters not being able to bash doors. - Fixed the bug with exiting item creation in debug mode early. - Added four new ego types for lites. Lites deliberately have easy_know. - Replaced some artifacts with others from gumband. Other artifacts were renamed to be more in theme. - The phial is deeper in the dungeon, now that there are ego lites. - The inventory and equipment screens now support bigscreen. - The inventory, equipment and floor menu boxes now support bigscreen. - Some of the racial activation names were too long. - Searching is renamed to "sensing", and now has an additional affect on psuedo-id rate. - Fixed display glitch with hitpoint warning menu. - Fixed the rarity inversion of monster pits/nests. - Added the new improved autoroller from Antiband. - Use "player_base".prf for player-specific pref file name. - Cleaned up the AI code somewhat, using some code from Oangband. - Gremlins should have the SILLY flag. - Enabled comments in get_rnd_line() - Added more lines for speaking uniques. - Monsters and objects are not quite so out of depth in vaults. - Passed the code through a hacked-up version of gnu-indent that understands most of the angband coding style. - The svga port now knows about the font location on debian boxes. - Added patch to only darken rods if the whole stack is charging. - Updated the quark compaction code. - Fixed up some bugs in the automake makefiles. - The messages when loading a savefile are now displayed correctly. - Fixed bug with loading vanilla town savefiles with the player standing the lower right hand corner of the map. - Fixed the crash caused by starting the game with the wizard mode option and with a dead character. - Fixed bug trying to display a term with the overhead map, but with too small of a size. - Aquatic monsters cannot be teleported onto dry land. ZAngband 2.6.2: - Made potions of restore life levels more common. - Fixed loading of some 2.4.0 savefiles that weren't working. - Fixed problems with items caused by importing older savefiles. - Added more 8x8 tiles - The wheel of fortune now shows the correct number when it stops. - Fixed out of bounds memory access in dungeon depth display. - Added the coloured monster-memory patch, with a few minor changes. - Added the vcs port mentioned on rgra. - Redid the birth screen with scrolling menus with the help of Matt T. - Added support for autoconf detection of the Athena widget set. - The autoconf stuff can be rebuilt from scratch. - Renamed 8X8.bmp to be 8x8.bmp, it works better on case-sensitive machines. - Fixed the problems with explosive runes. They will only explode if you are standing on them. - Fixed the really weak trap disarm spell. - Added a new race: ghoul. It has two special powers which are: paralyzing touch and eat corpses. - Fixed a bug that prevented skeletons from being resurrected. - Changed the default X11 fonts so that things behave better when people try to redefine them. - 'Normal' artifacts are less common than in 2.6.1, hopefully the rarity now approaches that in 2.4.0 - The correct town names are shown in character dumps. - Slaying weapons now use a different formula for the adjusted damage dice. - Made trump summoning spells more powerful - the formula was based on the old dungeon distribution of monsters. - Monsters now use LOS properly when targetting something. - Secret doors are supposed to look like granite walls. - Scrolls of *remove curse* are more common. - Monsters can open secret doors again. - Objects deep in the dungeon are more varied. - Added an ironman_los option for those who want to play without any line of sight abuses. - Fixed the problem with stacking of wands after loading a savefile. - The monster list term works properly even when the term is too small. - When you punch monsters, you do the damage shown on the information screen. - Graphics now works on 8bit screens in X. (Affects all X windows ports.) - Ghouls gain the 'sense living' ability when they get to level 30. - Increased the price of potions of experience. - The Unicorn of Order can bash down doors. - You can now open chests on the same square as yourself. - Fixed crash when starting with no savefile, and graphics is turned on. - Figurines are no longer found cursed in the dungeon. - Removed two obsolete los options. - The birth screen now shows race and class modifiers when you make your choice. - Added an option to show the 'experience to go until next level' instead of the total experience. - Cheerful leprechauns are affected by the silly monster birth option. - Added a command line option so that you can choose the tileset to the unix ports. - Changed the formatting in the unique killed list to be like [V]2.9.3 - The gtk port is now of at least beta quality. It has been added to the makefile. It supports graphics and bigscreen resizing. - The spell-list term type now adjusts to window size. - Potions of resist heat and cold are more common. - Fixed compilation problems on machines lacking memset(). ZAngband 2.6.1: - The supplies store can sell ammo. - Added an option so that you can set the map to remember monster-lit grids. - Made potions of restore life levels more common. - Items from acquirement() can not be worthless anymore. - Quest rewards are now worth at least (100 * dungeon_level) gold pieces. - The object selection routines used by stores when stocking themselves should be much faster. - Towns now have random names. - Added autoconf support. - Potions of Life will now give back full hitpoints even if CON was drained. - Fixed the building prices. They were off by a factor of four. - The options screen could display bogus options. - Fixed problem with the description given when looking at snow. - Fixed problem with displaying charging artifact lights in the wrong colour. - Fixed an off-by-one error in the object selection function. - The inventory is now reorganized after refueling from a lantern. - The monster list term is now turned off when hallucinating. - Interactions with traps shouldn't cause any display glitches. - Door bashing with '+' command works again. - Fixed an error that was preventing the throwing mutation from working. - Only display the 'gain' message when getting a mutation. - Fixed problem with monsters being able to walk though wall-like squares. - Fixed problem when recalling into the dungeon when the overhead map term was turned on. - Fixed duplicated mutation information for the 'dazzle' mutation. - Updated the message you get when *identifying* artifact lights. - Fixed the problem with starting the game on 64bit machines. - Corrected the color palette for the 16x16 tiles in the DOS version. - The curses interface should now be more compatible with some BSD curses versions. - The '5' key on the numeric keypad should now work in the X11 version. - The X11 and XAW versions default to three windows instead of eight. - Use the mkstemp() function where available instead of the insecure tmpnam(). ZAngband 2.6.0: - The starting town has a fixed list of stores now. - Changed how the sanity-blasting effect of monsters works. It will only happen if you can see the monster. Stats will not be drained. However, you may become afraid, even if you have resist fear. - Made sanity blasting monsters much rarer. - Included 'unseen' monsters as a category in the kill-count list. - The rare book store now stocks more normal books, and less dungeon ones. - Nightmare mode is slightly harder. - Splitted the 'lib/user/' folder into 'lib/pref/' for the default pref files that are distributed with the game, and 'lib/user/' (or ~/.angband/ZAngband/ on multi-user systems) for files created by the user. - Changed object distribution from chests. - Added an alpha-quality gtk port. - Scrolls of protection are now preserved when an object on the floor (including the scroll itself when reading it from the floor) prevents the creation of the rune. - Halved the price of the 'identify pack' service at the Zymurgist. - Small towns can now have special buildings (like an inn). - Updated and added lots of tiles for the 8x8 and 16x16 graphics modes. - Updated the documentation. - Prevented object levels from getting too high and producing lots of potions of new life at deeper levels. - Fixed the problem in stores with examining spell books from realms that you do not belong to. - Fixed several bugs in new projection code. - Fixed a bug in the monster lighting code. - Fixed bug that prevented loading of version 2.4.0 savefiles. - Fixed score abuse when you had zero hard quests. - Fixed a problem with identify not being useable on items whose flavour you have forgotten. - Corrected the prices of some weapons. - The game will now return the standard error-exit code of the system when an error occurs. - Charisma didn't affect building prices in the right way. - Fixed a problem with the xaw port not reading font information from .Xdefaults. - Re-detecting forgotten traps works properly now. - The 'use old target by default' and 'equippy chars' options are now disabled in Moria mode. - The default birth options for new characters can now be set from *.prf files. - The used staircases is now marked as known when entering a new level. - Refulling a latern from another latern in the inventory will now correctly update the inventory and equipment windows. - Corpses are now correctly updated when switching Adam Bolt's tiles off. - Fixed annoying lack of redraw when moving about the wilderness map quickly. - Rangers were *worse* at shooting a monster in a tree than other classes. - Added some extra checking in the bitmap loading code. - Fixed a bug with mimics in the 'visible monsters' window. - Fixed bug with the fake_monochrome flag not being updated when the graphics type changes. - Fixed display of item to be picked up when covered with gold, when several options were set in a certain way. - The race- and class-specific player icons work now with the 8x8 tiles. ZAngband 2.5.6: - Shifted store names over so that the long ones fit along with the maximum price. - Fixed display of the 'curse' line on the info screen so it shows the Permacurse or Evilcurse. - Figurines are green instead of blue, to match the stores. - Tweaks to the object prices and distribution. Restore stat potions should be buyable at the alchemist. The distribution of Ego items in the dungeon has been tweaked. - You can use the '5' key to enter stores while standing on them. - Added a user-editable 16x16 font for the projected view. - Stores will buy more types of items than they sell. - Increased maximum number of spikes you can jam a door with to 2^16 - 1 or so. 3 tonnes of spikes should stop a few monsters... - Fixed combining of {empty} wands with non-empty wands still keeping the {empty} inscription. - Quaffing potions of salt water no longer causes a dangling pointer which can read in garbage values for the change in 'hunger' value. - Made the "normal" stores and home, more common. - Your inventory updates as rods recharge, not just when all have recharged. - Fixed loading of old savefiles. A possible crash has been prevented, and the starting town is correctly drawn. - Fixed bug in number of monk attacks you get depending on encumberance. - Torches and lanterns now use the timeout variable, instead of pval. This will allow ego lights in the future. - Added the LITE flags to the artifact lights. They show up as providing light on the info screen. - Fixed various bugs in combining ammo with other ammo. Fixed bugs in combining ego items. - Fixed bug that was turning "good" quest rewards half the time into "bad" items. - Made the pval of various rings and amulets affected by the values in k_info.txt - Added the y/n/k prompt to the easy_floor option, when there is only one object to pick up. - Removed several options, and moved others around. This includes turning the AI on, and adding a smart_packs option. The monster lighting no longer has 'testing' status. - Fixed the case when a menu has zero available options. - Added the casino, inn and healer back in. - Fixed monsters breathing much less often than normal with the AI on. - Lowered overall monster-level of the wilderness. Made the starting town be placed in the 'easiest' place in the wilderness. - Improved the chance that towns have stairs. - Major cleanup of the unix ports. The maid-x11.c file now behaves properly. - Added proper ascii support to the projected view, using the 16x16 font. It now works properly, and is fast enough to use except when the center on player option is on. The tile size is adjustable to multiples of four, and the look of walls is tweakable. (See comments in header of main-xpj.c) - Cursed arrows hit half as often as normal arrows. - Reverted the overhead map code to work the old way and made it look better in graphics mode. - The 'Yellow light' can now blind you when exploding. - Fixed the bug in the weaponmaster when showing minimum damage. - Improved the distribution of books in the normal bookstore. - Prevented the stack-smash when too many monsters explode at once. - Turned off illumination of walls with the view_torch_grids option on. (This option does not work correctly.) - Fixed messages received when you look at or discover a mimic. - Rewrote los and projection code so that they work the same way as the view code. You can hit anything you see, and nothing you can not. 'Trick shots' are not possible. (Every shot is a 'trick shot'.) - The amnesia attack is more powerful. You will forget the tried status, and flavor of objects. - The formula used for monster fear ignores attacks that do zero damage. - Fixed bug that was preventing the rarity value of stores from having any effect on their distribution in towns. - Moved the platform-specific externs to h-system.h, and out of main.c - Made the cursor a rectangle instead of a filled box, on xwindows ports. - Fixed a crash with some beaming spells. - The projected view port now has an ascii mode (controlled by the '-g' startup option.) - Things are no longer always invisible in doorways in the projected view. - Reduced bonuses to slaying ego-items in some cases where they were too large. - Lights are yellow in your inventory, not grey. - Fixed quest message being on two lines instead of one. - If a message is displayed while the dungeon was generated, you won't see half the dungeon lit. - Cloaks and boots count as armour now. (Not 'tools') - The XAW port is now smart enough to understand the fonts defined in environment variables, and in z-config.h (As well as in .Xdefaults) - Fixed a bug with the monster lighting not lighting other monsters properly. - Stopped the projection routine being too smart, and not hitting monsters you are aiming at. - Reordered some of the shop attr/feats. '0' now stands for 'other'. Warrior halls are now '0', and lots of the old '0' stores have changed. - Made the vanilla town shops look like the vanilla versions used to. ZAngband 2.5.5: - Items of Curing now heal hp as well as removing bad effects. - Disenchantment resist protects against disenchanment traps. - Improved the saving throw for artifacts against being blasted. - Fixed bug in the 'cursed' line on the info screen. - Fixed crash when there are too many 'interesting' squares to look at. - Store entrances no longer block magical effects. - No traps in rivers. - {empty} wands and staves are 'noticed' when you find out, not later on. - Changed distribution of figurines, so they do not depend on the players current depth. (Otherwise figurines in stores aren't very useful.) - Corpse decay messages are only displayed if disturb_minor is on. - Improved quest rewards. - Added about one hundred new store types (Jeweler, Clothes Store, Fletcher, Scroll Store, Ammo Supplies, Potion Store, Food store, ...). - The starting town has a supplies store that carries potions, scrolls, weapons, armor, food, light sources, ... - Added a 'monster list' term type, which displays a list of the monsters you can see. - Added 16x16 tiles for most objects. - Corpses now last longer before they decay. - Random artifacts have a chance to get boosted damage dice. - Balance changes: - Boots and gloves are more common deeper in the dungeon. - Changed some of the better '1dx' weapons to be 1d(2x) or there-abouts. - Most of these weapons are deeper in the dungeon than they used to be. (This makes some of the spears more useful in [O]-based combat.) - Moved the various healing potions in depth. - Changed the damage dice on some artifacts. - Rods should now be deeper than the corresponding staves. - Increased the gold value of hit, damage and AC bonuses. - The identification menus will only show unid'ed items. - The magesmith now enchants items differently. You get more for your gold. - The game tries less hard to make objects of a given depth, and is more efficient at using object themes. - Pressing -ESC- at the -more- prompt skips all messages until the you are hurt, or your turn comes around. - {bad} items are on average less bad. - You are allowed to tweak random artifacts in debug mode. - Changed the weaponmaster so that it looks at one weapon, and also *identifies* it for you. - Your gold is displayed on the screen when you are in a building. - Made mimics harder to spot. - Random quests won't use "easy" monsters. - Cloaks of Aman get high resists like they should. - Optimised the 'projected view'. - Fixed the labels in the chardump so that homes are listed properly. - The 'destroy on pickup' now works alot more like the normal destroy command. - Changed the behavior of traps so that they trigger and disarm more often. - Fixed the message on entering a quest with one remaining monster. - Light sources in your inventory are sorted by fuel level. - Fixed how corpses look in 16x16 tiles. Transparency is used properly. - Generating artifact spoilers now works properly. - The players tile changes when polymorphed. - You can now tunnel into pillars. - The RNG is now re-initialised when loading a dead character. - Do not calculate the probability distribution of objects in debug mode, unless asked. - Prevented the winning artifacts from being generated early. - Prices for services in buildings depend on your charisma and race. - Objects that sustain and increase a stat are shown more clearly on the information screen. - Artifact creation scrolls can no longer be used on ammo. - Fixed bugs in the lighting code. - Fixed a bug with earthquakes and fields. - Unidentified items can no longer be sold for their real price. The 'base' price will be used, as it should have been. - Fixed bug in the berserk mutation. - Oceans look nicer now. - Towns have a minimum seperation. - Removed unused info.txt files for the old fixed quests. - Ego diggers get the correct pval. - Fixed inequity between monster drops and items in vaults. Both use the same method now. - Monsters stop attacking when they die from an aura. - Tweaked main-gcu.c so it worked better on BSD machines. - Prevented stupidly small windows in the main-gcu port, when the screen was slightly larger than 80x24. ZAngband 2.5.4: - Tweaked the initial towns and their surroundings to make them more hospitable to starting characters. - Tweaked item distribution to make weapons more common in the deeper dungeon and bad items less common generally. - Neither vaults nor random artifacts will be generated when in ironman_moria mode. - Changed the pick-up prompt to (y/n/k) where 'k' will destroy the item. - Water in the wilderness is no longer lit at night. - If an invisible monster drops a corpse when killed, the kill counter is incremented. - Added additional shopkeepers for each special building. - Added additional context sensitive help for the birth process and for debugging commands. Minor fixes and updates to the help files. - Added an experimental main-xpj.c file which uses a projected 3d view (pre-alpha, do not use). - Added Debian and OpenBSD (needs testing) options to the standard makefile. - Updated mask.bmp and the graphics pref files. - Fixed an error in the calculation of activation energy for magical devices. - Fixed some loopholes in the imnplementation of the recall code. - Fixed a bug which prevented ego ammo from stacking properly. - Fixed a bug with the graphics tiles for shop and building doors. - Fixed a bug allowing of Resist Acid to be created. - Fixed a bug with the ancestral kill count. - Fixed a problem with roguelike commands in the special buildings. - Fixed the bug which occasionally crashed the game when monsters stole from the player. - Fixed the mutatalist so it shows you the altered prices as you gain and lose mutations. - The Cygwin makefile will now produce executables that work without the "cygwin1.dll". ZAngband 2.5.3: - Added roads between the towns to the wilderness. - Added lakes (of various kinds) to the wilderness. - Enchanted ammo is now cheaper. - Tweaked the enhanced damage dice of weapons. - Fixed a bug that allowed monsters to walk through pillars. - Fixed a bug with random quest rewards. - Fixed a problem with 'Vanila town' mode and the dungeon 'M'ap. - Light carried by monsters is no longer visible if the player is blind. - Toned down some of the nastier traps. - Improved handling of the bigscreen feature in the Windows port. - Updated the bigscreen code to be simpler and less "hack'ish". - Added some new 16x16 monster and terrain tiles. - Reduced DSM breath damage by one-third. - You can now press 'S' while choosing your magic realms to restart the birth process. - *Identifying* a book (or eXamining it in a store) will now print out a list of it's spells. - Amberites can no longer resurrect themselves via their curse. - Chopping down trees no longer creates snow covered terrain. - Caverns and lakes in the dungeon are less common. - The "bright flash of light" message is now only printed if a trap is destroyed. - Improved the message given when the game is started with the '-?' option. - The "fetch" command no longer can get artifacts from afar. - Changed the wand stacking code to be more intelligent. It now keeps track of the total number of used charges in a stack. You sell / recharge the wand with the smallest number of charges. - Large changes to how objects are created and stored within the game: Ego items now use the level / rarity in e_info.txt. The object selection function is much more efficient. Monsters drop "themed" objects - by picking kills, you can select what type of items you get. Changed the distribution of items in the dungeon. There should be much less junk created now. Monsters drop less items, but the average quality of drop will be higher. "good" and "great" have changed meaning - they are now level offsets. Added three new pseudo id flavors. "bad", "dubious" and "tainted" Cursed items in the dungeon can have good properties. The rarity graph in wizard mode is now correct. Disabled the autodestroy worthless items option. This shouldn't be needed now that worthless items are rare. - Added many more lines for the speaking uniques. - Chests use "themed similar" objects like [O]. - *destruction* now destroys fields properly. - Creeping adamantite coins are now green just like the coins and armour. - Fiddled with the probabilities of vaults and unusual rooms. - There are less arrows in a pile. - Turned fields on in the wilderness. - Implemented stores and buildings as fields. - Changed the options code to be more like Angband. - Major cleanup of global variables - most are moved to player_type. - Added Ed Cogburn's Tombstone patch. - Tweaked the stats or descriptions of many monsters. - Removed virtues from the '~' menu. Option 7 still works though. - Added an ironman_moria option. The good old days. - Mindcrafters start with a dagger instead of a short sword. - Swapped first two mindcrafter abilities. - Unidentified wands stack now. - Using an artifact creation scroll is much less risky. - Merged makefile.lsl into makefile.std - Added context-sensitive help for the option screen. - Randomly sized cities are created in the wilderness. - Updated main-mac-carbon.c so that it actually compiles. - Rangers get one more extra shot. - Toned down speed increase of activating items. Staves always use 100 energy. - Added some of the more useful "new" buildings in 2.4.0 - Changed how word of recall works. You can only recall into the dungeon when you are standing in a town with stairs. - Added a new building type - the map maker. - Removed virtues from the chardump. ZAngband 2.5.2b: -The "Good luck" and "Bad luck" mutations no longer affect unweildable items. The "Good luck" mutation can no longer be abused with memory mosses. -Fixed a bug in the dynamic monster lighting. -Increased the price of powerful rods. -The map should no longer "jump about" when the center on player option is on. -You get a maximum of 600gp for left-over points in the point-based character generation. -Glyphs of warding and explosive runes can now be created. -You can't run into traps any more. -Bugfixing and cleanup of the dungeon generation code. -Shooting at monsters adjacent to you is much harder if they are awake. -The death spell "raise dead" now raises corpses. -Added yet another type of random vault. -Pillars now use the pillar tile. This tile is "semi-graphical" in X11 when in ascii mode. -Added three new types of rooms. (Some with multiple sub-types.) -Fixed a couple of problems with mindcrafter spell descriptions. -Closed several possible security holes in the pref-file handling. -Renamed makefile.cyg to match the other makefiles. -Fields now have tiles on the main-ibm platform. ZAngband 2.5.2: -Added monster lighting effects. This is similar to the monster lighting patch from APW, however it is much more efficient. Also, there are three possible light radii instead of one. -Corpses now are dropped by monsters when they die. These decay over time, and cannot be picked up. Some monsters have spells that raise all corpses in LOS. -Removed the bash command. Use the open command to open stuck doors. -Completely rewritten traps code. The types of traps are now level dependent. There are new types, and some old types are nastier. -Added a version of the big screen patch. -Items are *identified* in death chardump. -Fur cloaks are cheaper. -Changed the shots per round display. Changed the formula for extra shots. Large numbers of "extra shots" are less powerful now. -Beastmen going up multiple levels get an increased chance to get a mutation. -Many optimizations to the monster AI. -Speedups to the term code allows quick redrawing of rectangular sections of windows. This speeds up redrawing as other windows move in front of terms. -Major optimisation of functions in cave.c The game may be twice as fast in some situations. -Fixed bug in the anti-double-move patch. -Fixed various bugs in dungeon generation. Toned down the "unusual rooms" option. It now makes unusual rooms, not vaults. -Ammo is found in bigger piles, and is easier to enchant. Mushrooms are found in clumps. -Grid bugs are less common now. -Mushrooms of cure serious wounds now weight as much as every other mushroom. -You can now drown in the ocean. -Iron spikes are lighter now. The stuck door formulae have changed. -Weapons of poisoning are now known as weapons of venom. -Sting now gets +2 attacks. -Fixed a bug in the range of fired ammo. -Fixed problems with the meteor storm spell. -Moved Earth hounds to the intended depth. -Fixed several bugs in the note-taking code. -Fixed "scroll of Logrus" typo. -Changed projection functions to give information only if hit squares are in LOS. This fixes bugs with identification of wands of "stone to mud". -Fixed the confusion activation direction problem. -Fixed some long standing bugs in the monster flow code. -Finally fixed the last of the problems with the fractal caves patch. The game shouldn't crash on dungeon creation any more. -Wilderness panel initialisation fix. This should stop the empty screens appearing in vanilla town mode. -Fixed problems with bounds checking in random vaults. -Cleaned up the "Easy Patch". The code is now integrated much better with the normal behaviour of the game. -Chaos warriors are now initialised properly with the point-based birth system. -Fixed inconsistancy between monster-monster and monster-player spells. -Fixed the weaponmaster code. (The building isn't used though.) -Updated makefiles for CygWin and LCC and removed unused parts of the source code. ZAngband 2.5.1: -Removed some compiler warnings. -Documentation updates. -Weakened the Death Ray spell somewhat. -CHECK_MODIFICATION_TIME should now work correctly for Macs. -Added a SILLY flag to r_info.txt. This will allow people to turn off 'silly' monsters via a birth option. -Sanity blasts have been switched back on but toned down. -The 'wasting disease' permanent stat drain has been removed. -Displaying the character history is now possible by pressing 'h' when rolling the character (not yet in point-based mode). -Closing the Win32 version with minimized main-window no longer causes problems. -The terrain_streams option now does something. It toggles the use of (lava)rivers, (lava)lakes, and trees on level 1. -You can now disarm traps when standing on them. -Rivers should look less silly now. -Changed the drive-letters in the Cygwin makefile from d: to c:. -Fixed some problems with the 8x8 tiles. -Fixed several bugs in dungeon generation routines. -Fixed some missing static keywords. -Fixed a bug with the Amiga map. -Fixed a bug that caused nameless characters to be saved in the wrong directory. -Fixed a bug that messed up objects on the floor when rerolling them in debug mode. -Fixed the vanilla town bug where the stores / home were not saved. -Fixed a problem with the 'Cheat Death' option. -Fixed a buffer-overrun with long notes. -Fixed rounding error with fractional monster experience points shown in monster memory. -Fixed a problem with life ratings and racial intrinsics being mixed up in the self-knowledge scrren. -Fixed a problem with generating full monster spoilers. -Fixed some problems with the monster fleeing code. -Added some monster AI optimizations from standard Angband. -Corrected some silly messages when failing trump summoning spells. -Mindcrafters can no longer get the 'viscous blow' effect when failing to use their powers. -Reduced the multiplier for the throwing mutation it was way too powerful when combined with some items. -Autoscummer cut-offs have been adjusted to reflect the new object and monster distributions. -The monster health bar is now corectly updated when the monster wakes up. -Changed the prices of a few objects. -Mushrooms of Cure Serious Wounds now heal the same amount of hit points as the equivalent potion. -Toned down Arch Viles and made Pink Horrors deeper. -Added a graphical overhead-map of the current level or wilderness area to the Windows version. -The large wilderness map can now be scrolled. -Towns are no longer concentrated in one corner of the wilderness. -Ocean is now very deep water. -The 'l'ook command now skips boring terrains (grass, dirt, very deep water). -You can now clear Jungle with 'ctrl-direction'. -Monster pits and nests are slightly more common and their intrinsic depths have been adjusted for the new monster distribution. -Stopped pre-emptive tunnel completion when the tunnel meets the caved in regions with trees on first level. -Simplified the dungeon fractal generation code somewhat. -Fixed the problem with main-gcu in graphics mode. Use "-g" to get the graphical walls. -Changed ascii dynamic lighting for red and blue - lava and water should look better now. -Transparency now works in XAW mode. -Added gamma correction for -X11. -You can no longer target friendly monsters by accident. ZAngband 2.5.0: -Completely reworked the wilderness. The extended towns and fixed quests are not available at the moment. -Towns now have locked gates so the nasty monsters in the wilderness cannot get in. No experience is given for picking them. -Added a feature to allow your game to be automatically logged. You may append notes to the log file by pressing ':'. -Added point-bases stat allocation at birth (adapted from standard Angband). -Revised monster and object listings to distribute them more evenly throughout the dungeon. -Added the CAN_SWIM and CAN_FLY flags to deep uniques. -Slow and haste spells acting on monsters will decrease in effect if the monster is already slowed/hasted. -Fixed a bug that forced parsing of v_info.txt every time the game started. -Fixed the problem with vampiric attacks, where only if the monster was seen, was the hp bonus given to the player. This was done via the addition of a new GF type: GF_NEW_DRAIN. -Lava rivers are found deeper in the dungeon. -Fixed some out of bounds accesses of the wilderness map. -Added out of bounds checks to the running algorithm. -If you choose more than 20 quests, the chance to get excellent items is decreased. -The number of random quests chosen appears in the character dump. -Fixed problem with some messages that were given when the subject was in "LOS" but not visible due to lighting effects. -Added bugfixes for the amiga overhead map. -Removed excess elemental resistances from a few monsters. Now, only very powerful, or multihued monsters will resist all five basic elements. -Running next to the wilderness boundary no longer should crash the game. -Increased number of affectable squares to 1024 per projection. -Fixed problems with monsters passing through walls, passing through other monsters and passing through the player. -Fixed the "missing monsters" bug. -Items that grant extra blows are now worth slightly more. -Fixed some problems with taking various actions near the wilderness boundary. -Vampires / vampiric mutation can no longer be used if you are afraid. -You can choose a random number of random quests when generating your character. -Removed makefile.org (It was a copy of makfile.std). -Removed the overhead map in vanilla town mode - it isn't needed. -Removed some warnings given by VC. -Score now rewards ironman options rather than penalizing options which can be turned on and off. -Adjusted minimum levels for the various types of rooms. -Adjusted probability to use the new corridor type. -Rescaled the monster breaths for the 30% reduction in monster hp. -Renormalised the shop items to use the changed object depth distribution. -Changed DSM to be more useful. More damage + can be activated more often. -Made the weaker healing potions / staff heal more damage. Before they were completely useless. However, they are slightly more expensive to compensate. -Altered monster saving throws to use the changed dungeon distribution. Some spells are much more useful now. -Changed the failure rates for monster-monster spells. -Recharging spells are more powerful. -Changed the power of many rods/ wands/ staves/ scrolls and artifacts. They are more useful now. -Most wands/ staves/ rods and a few scrolls are cheaper. -Changed the prices of the armor. Changed the prices of the ego items. -Rebalancing the artifacts. A few have had a price cut. A few have had various flags removed. -The energy it takes to zap a rod/ aim a wand / use a staff and activate an artifact now depends on the players device skill. -Changed monster critical hit code so that stormy doesn't get insta-kills quite as often. -Golems lose their "nostun" ability in hard games with the ironman options on. -Added numerous new vaults. -Added new graphics tiles by Marten Woxberg. -Revised and re-ordered the options menus. -Switched off the sanity blasting effect when seeing an eldritch horror. -The Nazguls can no longer be killed by other monsters. -Made cursed items somewhat easier to activate. -Statues now do more damage when thrown. -You can now throw wielded items. -Items inscribed with '=g' will automatically be picked up. -Escort monsters now have the same alignment as the monster they are escorting. -The most powerful monster attacks (breaths and mana and darkness storms) have had their max damage reduced by roughly 30%. -Hobbits and Sprites now have an 8-sided base hit die. -Pits will no longer contain opposite-aligned creatures. -Removed the effect of the virtues on player-summoned monsters. -Killing evil angels no longer has the same effect on virtues as killing the good angels. -Minor changes to some monsters (including making Cyberdemons stupid). -Enhanced the available dialogue for certain speaking monsters. -New monsters, observed melee attacks and graphics to be used when the player is hallucinating. -Pets should no longer cast ball spells or breathe at monsters if the player is likely to get caught in the ball/breath. -Restored the wizard-mode damage message which will now also display the damage for shield bashes. -Changed the way base hitpoints are calculated to reduce the spread caused by extreme life rating values. -Coordinates are now displayed when targeting in wizard mode. -Pressing '|' on the options screen will save them to "pref-opt.prf". -Enhancements and fixes to main-win.c, main-mac.c, main-mac.new and main-mac-carbon.c. -Added makefiles for the Cygwin, LCC-32 and Borland command line compilers. -Graphics under x11 / xaw. -Enabled transparency for x11 systems (this is a hack). -Changed the x11 / xaw versions to use semi-graphical characters in ascii mode. -Changed the pref files so monsters that look like terrain, also get changed when graphical characters are used. -Added some Draconian histories. -Added a patch by Jason Willoughby to remove Y2K warnings when compiling. -Fixed a bug that crashed the game when looking at quest entrances (Note: made obsolete by the wilderness changes). -Minor fixes to the combat code. -Toned down the damage dice of some artifacts. -Tweaked the levels at which monks gain additional attacks. -Tweaked the line-of-sight code. -Tweaked spell-casting AI to reduce the chance of smart monsters healing and teleporting. -Added a new type of bolt (a "steel bolt"). Some missiles are no longer sold in the stores. Missile rarity tweaked. -Fixed graphics in the x11 port. -Fixed the problems with too much or too little speed (patch by Mitsuhiro Itakura). -You can no longer recall into rubble. -Prevented ironman characters from recalling to town under certain circumstances. -Fixed a bug when using a scroll of mundanity on the floor. -Fixed the stack overflow in the dungeon creation routines. -Fixed a problem with partial knowledge of stats when identifying Pattern Weapons. -Fixed a bug that allowed the player to attack monsters in walls without using any energy. -Fixed a problem with item activations when wielding them from the ground. -Prevented very fast monsters from getting double-moves against a character moving at a faster speed than the monster. -Caverns are rarer deeper in the dungeon. -Store memory allocation is now dynamic. -Removed a number of unused variables, initializations and some dead code. -Throwing potions at monsters will now identify the potion if the player observes appropriate effects. -Monsters now receive AC bonuses and penalties on certain types of terrain. -Updated the FAQ. -Fishes are now 'l', trees '%' and watery terrain '~'. ZAngband 2.3.5: -Cleaned up monster fighting messages. -Quest entrances show now the name of the quest. -Added player alignment based on pet alignment and virtues; this determines the alignment of monsters the player can summon. -Monsters may no longer summon their enemies. -Added an "autodestroy worthless objects" option. -Lowered the pet upkeep slightly. -Made pets able to locate the player from a greater distance. -Restored the mana of all classes to pre-2.3.4 levels. -Made 'trump cyberdemon' less difficult to cast successfully. -Added an invisible wall for nightmare mode. -Disintegration balls are now stopped by permanent walls. -Corrected a bug which prevented monsters from casting non-bolt spells at monsters when a friend is between. -Shattered weapons are now 1d1 instead of 0d0. This fixes the 'division by zero' error when getting a critical hit with such a weapon. -The EMPTY_MIND flag of monsters is now remembered by the player when an attack shows that the monster is immune to psi-powers. -Exploding monsters no longer hurt the player twice when exploding. -Fixed a bug that could crash the game when generating fractal caves. -Fixed a typo when a ring disappears in shallow water. -Removed the CAN_SWIM flag from mushrooms, molds, and most jellies. -Fixed a bug with the 'NOT' operator of the *.prf files parser. -Added a new main-x11.c file by Uwe Siems. The improvements include better graphic-scaling on systems with HiColor/TrueColor mode (can be switched off with -s). -Added a new main-mac-carbon.c file by Ron Anderson. It supports PPC, CarbonPPC, and 68k and implements the 16x16 tiles. ZAngband 2.3.4c: -Fixed a bug that could crash the game when lighting/darkening the dungeon. -The mindcrafter 'domination' spell no longer affects quest monsters. -Fixed a bug that caused 'time' melee attacks to also cause the effects of vampiric attack. -Fixed a bug that caused the arcane spell "Elemental Ball" to always produce an acid ball. -Fixed a bug that caused an "invalid command beep" when changing the autosave-frequency. -Fixed a bug that gave imps a much too high saving-throw vs. nightmares. -Fixed a minor bug in the character-dump routine. -Fixed a problem which prevented 'good luck' and 'bad luck' from appearing in the mutations list. ZAngband 2.3.4: -Extended the online helpful system significantly (also added The Angband Newbie Guide by Chris Weisiger and amended it somewhat for Zangband). -Resting at the Inn now restores hit-points and mana. -Added some new monsters and amended others. -Replaced the 'Resist Time' and 'Sustain Stats' mutations with two new ones. -Upgraded the TY_CURSE and nightmare mode somewhat. -Enhanced compatibility with lcc-win32 compiler. -New wizard command to delete all monsters on the current level. -Illumination now works on all open areas and not just rooms. -Increased the ratings of the 'miniature cell' and 'interlock' vaults. -Reduced all monster hit-points and armor class in preparation for the change to Oangband-style combat. -Implemented Oangband-style combat with the following changes: - Instead of [blows*(xdy+z)+extradamage], the new formula is [blows*xdy*fractional_deadliness]. fractional_deadliness is a function of z+extradamage. - The number of blows due to str/dex has been decreased. Also the maximum number of normal blows for each class has been decreased. - The number of extra blows from ego items and random artifacts has been decreased for larger weapons. However, the formula is such that the larger weapons will still do more damage than the smaller, lighter ones. - Downgraded some artifacts and normal weapons. - Most players will be able to get 2 blows from the start. - Wielding a weapon that is too heavy will mean that a player can only get 1 blow. - The information on the info-screen (shift-C) has been changed to reflect changes in damage calculations. Weapon evaluation at the weaponsmith has been similarly changed. - Rangers get 1 extra shot at plev 20 and 40 (total of 2) for bows. They also get an extra shot at plev 20 for crossbows. - Rogues get 1 extra shot at plev 20 and 40 (total of 2) for slings. - Warriors get 1 extra shot at plev 40 for all launcher types. - Normal classes now get 2* 1d1 for unarmed combat (punches) (Except for monks who still start with 1 attack). - Monks do less damage than before (scaled down by 25-30%) per attack to compensate for the lower monster hp but retain their original bonus extra attack structure to max out at 8 attacks. - Shield bashes have made a return, bashing does some damage and may stun and confuse your opponent. - New critical hit structure and messages. - Damage multipliers for slays and brands amended. Slay Evil and Slay Animal do x1.7, Kill Dragon x3 and all others x2. - Added the 'THROW' flag to various objects and artifacts. These items will do more damage when thrown. - Strength no longer affects your combat skill (was to_hit). -Further amendments and refinements to the dungeon generation code including (by Steven Fuerst): - Rooms of all types can be packed closer together. - 5 new room types added. - 2 new corridor types (the pillared tunnel is optional). - 8 Random vault types added. - Added Caverns and water/lava lakes. - Rivers and trees are placed more realistically. - Vaults in v_info.txt can now be included in the dungeon but rotated and reflected. -Increased the damage from wands of rockets and annihilation. -You will no longer receive rewards for killing non-unique monsters. -Added a small tribute to Lev Zakrevski. -Trees will now only block LOS half the time. -Increased the maximum number of pits to 2 per level. -Changed the compiler optimization flags for the DOS version to work- around a bug in DJGPP that could cause Zangband to hang when compacting items. -Artifacts now have a higher activation failure rate when cursed. -Added some new vaults by Eric Bock and tweaked a couple of others. -Minor changes to certain quests and their rewards. -Added a "status bar" that displays certain timed effects under the stats. Timed effects previously already displayed like poison, wounds and stunning remain as they were. -Made artifacts 'tweakable' and their activations can now be modified. [Note: include details of how to do this?] -Players may now walk the pattern incorrectly but should be cautious doing so. -Lice can no longer fly and are no longer denoted by the 'l' character. -'Enlightement' will now detect all walls and monsters. -Rings of Extra Attacks will now normally be +1 and occasionally +2. They are also rarer. -Removed some items from silly.txt and rumors.txt and added some new entries. -Adjusted missile launcher damage multipliers and the energy required to fire them. -Pebbles and shots have been upgraded. Rarity, damage and weight for various missile types have been adjusted. -The scoring function now considers various things such as options chosen at birth, etc. (Modified from GSNBand). -The 'M'ap now scales to the level size. -The auto-roller delay is now optional. -Allowed random artifact rings and amulets - these are very rare. Note that Scrolls of Artifact Creation will not work on non-artifact rings and amulets. -Enhanced pack monster AI. -Level feelings will now be generated based on the length of time you have spent on the current level rather than the previous level. -Monster memory, unique lists, spoilers, and kill counts are now sorted by monster level regardless of the monsters' index in r_info.txt. -Easy-open doors works now normally for Spectres. -Life rating is no longer shown at character generation. -Reworked the 'C'haracter information screen to remove duplicate information and reformatted character dumps to prevent line wrapping. -Fixed the excessive disturbing messages produced by monsters fighting each other. -Fixed some problems with makefile.std and amended pref-emx.prf. -Fixed a problem with makefile.ibm. -Fixed a problem with pet summoners. -Fixed a bug with critical shots. -Fixed a bug that caused thrown/fired objects to look strange with graphics turned on. -Fixed a bug that could mess up character dumps when the player had drained stats. -Fixed a bug that caused thrown/fired objects to look strange with graphics switched on. -Fixed some problems with the 'easy patch' option. -Fixed a bug that prevented Sprites from getting food at the inn. -Fixed the buffer overrun if players became too fast. -Fixed a bug that caused some monsters to cast "cause fear" too often. -Fixed the sources of most of the compiler warnings with gcc. -Wands and staves will now stack properly when emptied. -Made some changes to enhance compatibility with Mac compilers. -Some changes to makefile.lsl and main-lsl and began work on getting the graphics to work including adding graf-lsl.prf (only vanilla monsters at present). -Added a main-mac.new for testing. It adds support for 16x16 tiles with transparency, and asynchronous sound effects by Ron Anderson (untested). ZAngband 2.3.3: -Added magical figurines, statues, and corpses (corpses are turned off since they can't be resurrected yet). -Added a new scroll 'of mundanity'. -New weapon type - the 'Diamond Edge' sword which has the vorpal flag. -Chests now use the '&' symbol and have more varied colors. -Decreased the levels of certain rods. -Lucerne Hammers are now 'polearms'. Turmil is now a 'blessed' blade. -Players should now be cautious when throwing certain potions! -Tweaked the naming of random artifacts. -Changed various monsters to allow them to speak and added relevant entries to monspeak.txt. -Farmor Maggot will now drop an excellent item. -White Icky Things and Kobolds made slightly tougher. Also increased their xp and depth. -Rat-things no longer bite to confuse. -Shoggoths are now eldritch horrors. Elder Things are not. -Ents and Hrus weakened. They are now both male. -Added several monsters. -Descriptions of certain monsters edited and numerous other tweaks made to various monsters. -Monsters with KILL_BODY will now perform an attack on other monsters blocking their path rather than insta-killing them. -Added 8x8 graphics tiles for the new monsters and items. -You can now teleport into forested areas. This will prevent getting trapped in the wilderness. -Various changes to the Mindcrafter: - Dimension Door is back. At level 40. - Psychometry becomes Identify at level 25, not 40. - PSI damage only affects opponents that can see you (affects neural blast, mind wave, as well as a Trump spell, a mutation and a racial power). - Psychic Drain is always a radius-0 ball, not bigger. - Adrenalin Channeling only heals when the player isn't already 'heroed' ('herofied'? 'heroficated'?) and hasted. - Precognition also gives Detect Doors at level 5. -Fixed a bug preventing the loading of 2.2.7 save files. -Fixed a bug with the {cursed} inscription. -Fixed a bug preventing Ravens and Crows from appearing. -Fixed the maximize mode -> nightmare mode bug. -Fixed a bug with tunneling. -Another attempt at fixing the 'more-than-seven-unnamed-Nazguls' bug. -Fixed a bug which could cause crashes when *id-ing* an object. -Fixed a bug that prevented monsters from casting spells correctly. -Fixed a bug in the wand/rod selling code. -Fixed a bug with '.' and ',' in the roguelike keyset. -Fixed an over-correction in the 'distance' function. -Fixed a bug that allowed *huge* vaults to be placed partially outside of the level causing crashes or strange bugs. -Fixed a bug with the 'u' command that allowed you to perform certain actions when blinded or confused. -Numerous spelling and grammar changes in, and additions to, various flavor messages. -Added multiple message tracking (ie. 'You tunnel into the granite wall <30x>'). -Added main-lsl.c from Angband 2.7.9v6. This is untested. -Added code to support monster invulnerability and vampirism. -Removed the 'Unbiased RNG' option. -Separated chaos and confusion resistance and added RES_CONF to several artifacts and other objects. -Changed the colors on some traps and added a new trap. -Split the dungeon generation code into smaller files (generate.c, grid.c, rooms.c and streams.c). -Removed the requirement that destroyed levels be 'boring'. -Added enhanced support for hyperlinks in the help files. Added readme.txt and helpinfo.txt to the /lib/help directory. -Changed the help file command '?' to '<' to open the previous file. '?' will now open helpinfo.txt. -Made amendments to various quests by Shayne Steel and their rewards. -Disabled bones file production. -Updated the names of certain windows. -Made the autoroller a bit friendlier. -Added support for HTML help files to the Windows version. -Reduced the cost and level of the 'sterility' mutation. -Chaos Warriors can no longer get the 'You attract the attention of a chaos deity' mutation. -Added a health display to the pet knowledge, extended the pet commands and tweaked pet behavior somewhat. -Aggravation no longer annoys friendly monsters. -Some changes to prevent the player from avoiding some of the nastier effects of nightmare mode. -Tweaked 'star' spells to allow rays to be fired in all directions. -Added a missing entry to timefun.txt. -Made some changes to the wizard commands. ZAngband 2.3.2 Internal development version. ZAngband 2.3.1: -Added Tom Morton's 'fake artifact names' patch. Inscribing "a Foo" with "#'Bar'" will display the item as "a Foo 'Bar'". -Due to popular demand, multiple rings are supported. Additional rings can be obtained upon reaching an appropriate level. -New ironman option: Nightmare mode. It isn't even remotely fair. -Fixed a bug that prevented pets from casting spells under the AI code. -Fixed a bug that made them cast spells at unreachable targets. -Introduced pref files for options (for new savefiles). -Made amulet, potion, ring mimics slightly more troublesome. -Added an option for monster memory in wizard mode. -Searching in help files is now case insensitive. -Charging items now have a darker colour in inventory. -Lifted many shopkeepers from CthAngband, no more five copies of the same 5k shopkeeper (unless the RNG is really after you). -Monster groups now appear "scattered". -Added conical breath attacks. -Changed the savefile version numbering somewhat, because of problems with the Linux version scheme and savefiles. -Kicked out a lot of old vanilla savefile code. -Changed the way game-created inscriptions work. They're seperate from player inscriptions now. No more problem with priests cursing and scrolls of remove curse that mess up your own inscriptions. -Fixed a bug in patterns in *greater* vaults that made it impossible to walk them. -Added new quests (and changed ones) by Shayne Steele. -Changed the rewards for two quests. -Toned down Jack of Shadows, changed the level of Hrus, Shudde M'Ell. Fixed some monster typos. Added new Revenant monster. -Fixed a bug that crashed the ^Q command now and then. ZAngband 2.3.0 -Maximize mode, preserve mode, and the autoroller are now specified in the startup-options. -The number of spellpoints gained by casting 'Omnicide' is now resticted to twice your maximum spellpoints. -'Teleport level' scrolls and spells won't have any effect on quest-levels if the 'no upstairs' ironman mode is active. -Orc and Troll pits will no longer contain undead trolls or orcs. -The DOS version now uses BMP files instead of the patented GIF format. -Fixed a graphics glitch with beam spells and permanent walls. -Fixed an inaccessible area in one of the vaults. -Added Greg Wooledge's fix for a compilation problem on various Linux systems. -The "laser eye" mutation will now properly anger pets and friendly monsters. -Fixed a bug in the "research monster" building command. -The time-command now displays the turn number. -The level-teleport from the pattern is now restricted to downward in ironman mode, but allows to get down to the bottom of the dungeon if you already finished off the Serpent. -Lit arena levels no longer reveal the whole level and the objects. The following changes are mainly by Prfnoff and Eric Bock: -Added a new Ironman option to always generate "arena" levels. -Added a new startup option that controls terrain streamer generation. -Added two new "munchkin" startup options: one allows Mindcrafters to get Dimension Door, and another turns the death save off (in a way). Both prevent scoring and are displayed on character dumps. -Added a new Ironman option to always generate very unusual rooms. -Fiddled a little with the TY_CURSE code (to prevent paralyze/Cyberdemon as result of certain things). -Added Eric Bock's wizard allocation patch. -Adapted Eric Bock's player centering scroll patch. -The Windows version will now try to create missing directories. (Eric Bock) ZAngband 2.2.6c -*Really* fixed the bug that could cause crashes when finishing a random quest. -Several changes to the 'Old Castle' quest. Replaced the "unknown grids" with normal floor. Fixed a bug that placed Mother Hydra instead of Death Knights. The reward is now correctly placed in front of the inn. You will now fail the quest when leaving without completing it first. -Fixed a bug that caused beam spells to do double damage at the end of the beam. -The casino will throw you out if you don't have money. -Fixed a glitch in the description of the snotlings. -Splitted up the cave generation source code. ZAngband 2.2.6b -Fixed a bug that could cause crashes when finishing a random quest. ZAngband 2.2.6 -Added a new quest and 3 new artifacts by Shayne Steele. -Added a new quest by John I'anson-Holton. -Added Mark Kvale's labyrinth rooms. -The Alter (+) command will close an open door, as the documentation says it does. -The monster memory will now record the lack of attacks of some monsters. -Fixed some small bugs in the documentation. -Fixed some bugs that prevented display of the correct message when failing some of the quests. -The quest stairs will no longer be placed on objects. -Disintegration spells will no longer affect grass and dirt and disintegrated trees will leave behind a patch of grass. -Cleaned up the handling of non-living monsters. -Monsters detected by magic are now correctly marked as seen in the monster memory. -Rewards for killing uniques are no longer depending on the "speaking uniques" option. -Fixed several small bugs in the monster definitions. -Fixed a bug that prevented the proper assignment of the Water Cave quest. -Fixed a bug in the distribution of charges for thrown wands and rods. -Fixed a bug that prevented Eldritch Horrors detected by telepathy or magic from blasting your sanity. -Secret doors the player reveals are sometimes locked or jammed. -Doubled the maximum number of objects and monsters per level. -The spell list now uses colors. -The savefile loading code now always shows the correct version number. -Fixed a compilation problem in the X11 module when compiling with graphics turned off. -Fixed a compilation problem on Solaris and SGI machines. ZAngband 2.2.5e -Recharging wands, staffs, and rods is now easier and more powerful. -Fixed a bug that could crash the game when recharging wands or staves with many charges. -Fixed a bug that created overcharged wands when purchasing wands from stores. -The black market will now often carry stacks of wands, rods, and staves. -Fixed a bug in trump magic's Mind Blast spell that could crash the game. -Fixed a bug in the pricing of the recharge services for staves. -The Windows version will now prevent saving and exiting the game while reading a scroll to stop certain abuses. ZAngband 2.2.5d -Spells that ask for a direction or an item to apply the spell to, can now be canceled without losing a turn or mana. -Fixed a bug in Z 2.2.5c that lowered the hitpoints of many monsters. -Fixed two small bugs in the vaults. -Blue horrors will now appear near the location of the killed Pink horror. ZAngband 2.2.5c -Fixed a bug that prevented locked doors from detection with spells. -Fixed a compilation problem in main-gcu.c for curses versions support for colors. -Fixed a bug that allowed stacked staves to be overcharged in the tower of sorcery. -Fixed some bugs in the new vaults. -Fixed a bug that messed up the order of quests in the town Telmora. -Fixed a bug that allowed Farmer Maggot's dogs to speak when dying and to be wanted for crimes. -Fixed a bug in the definition of the 16x16 tile for angels. -Compiled the DOS version with the up-to-date version of Allegro 3.11. This should improve compatibility with various sound- and graphic-cards. -The Windows version no longer requires the ZAngband.ini file to be present. -Added an experimental terminal window option to show a player-centered overhead view of the dungeon. ZAngband 2.2.5b -Quest monsters that have been teleported away by destruction spells will now be healed. -Fixed a bug that caused the game to hang when entering some of the shops. -Fixed a typo in "melee2.c" that influenced speaking uniques. -The buildings in the "lite" town are not correctly initialized. -Long rumors (more than 80 characters) will no longer cause error messages. -Bug in the price calculation of wands fixed. -Changed the windows resizing in the Windows version a bit. ZAngband 2.2.5 -Added an option to unify the item commands like "zap a rod", "use a staff", "eat food", "aim a wand", ... into a "use object" command. -Bloodletters of Khorne drop Blades of Chaos less often. -Randomized the parameters for dungeon creation to produce more different dungeon layouts. -Character dumps will now include the inventories of all homes. -Empty slots in homes and the player inventory are no longer displayed in the character dumps and spaces are stripped from the line endings. -Added Eric Bock's unbiased RNG as an startup-option. -Added a modified version of Matt Graham's speaking uniques patch. The lines for every speaking unique are now user-defineable for the fearful, friendly and normal monsters and the death of the monster. -Added lines for Farmer Maggots dogs. -Farmer Maggot, Martti Ihrasaari, and Fundin Bluecloak are now friendly, but will fight back if attacked (or aggravated). -The Shadow Cloak of Luthien now provides light- and dark-resistance. -The confusion resistance granted by chaos resistance is now displayed. -Telekinetic powers no longer work on items in vaults. -Several small changes to the "City beneath the sea" quest. -Various quest related bugs have been fixed. -Changed the autoroller to the Angband 2.8.3 layout. -Pets will not try to shoot spells through the player. -Two bugs in the easy_pickup option fixed. The correct message will be displayed when picking up an object from a stack including money. Switching between inventory and equipment lists when selecting an object will now correctly update the display. -Fixed a bug that displayed the quest-completed message again when killing summoned monsters in an already completed fixed quest. -The descriptions for the lightning ball (radius 3) and the balance dragon scale mail activation are now correctly displayed. -Fixed a bug that caused the wilderness to be reinitialized when leaving a building even if not necessary. -Fixed a bug with the monster-tracking of some spells. -Fixed a typo and a wrong terrain feature in the "Logrus Master" quest. -Fixed a small error in the character history of vampires. -Fixed several bugs in the research monster building command. -Monster recall no longer shows the "You feel an intense desire to kill this monster." message if you have not met the creature yet. -Added Keldon Jones' new main-gcu files with support for multiple windows, color redefinition and special character support. Thanks to Topi Ylinen for the following changes: -Many monsters have been tweaked a bit (mainly rebalancing hitpoints). -Potions of Healing and *Healing* are now more common on the deeper levels. -Two new quests added. -New quest-failed texts for several quests. -The 8x8 tiles have been slightly edited and various errors in the tile definitions have been fixed. -The command to dump the "Knowledge menu" lists into a file (with 'f' or 'F') is available again. The following changes are taken from Leon Marick's OAngband: -Added the enhanced wand and rod stacking. -Confused monsters cannot steal items. ZAngband 2.2.4 -Most fixed quests will now be marked as failed if you leave before completing your goal. -Changed the "Druid's Shop" quest to a "Logrus Master" quest. -Added 33 new vaults designed by Chris Weisiger. -Chests produce less gold and more items; all chest items are at least "good" (like in GWAngband). -Increased the randomness of long-range teleport effects to decrease the frequency of consecutive teleports "bouncing" the player between two or three different spots (also from GWAngband). -Golems have now a penalty to dexterity (-2). -Mindcrafters no longer get the 'dimension door' power. -Toned down The Katana of Groo and Stormbringer. -Toned down the number of extra attacks on random artifacts. -The quests from the magic towers are now only available for members of the towers realm. -Added a note about the startup-options to the prompts at birth. -Fixed a bug that could crash the game when making strange offers while haggling. -Corrected the damage calculation for slays and brands in the weaponsmaster. -Fixed a bug in the Amberites 'shadow shifting' ability while in quests. -Fixed a bug that could turn monster hitpoints negative when draining charges from magical items. -Monsters attacked by other monsters will now always wake up. -Fixed a typo in the description of 'Cthugha, the Living Flame'. -Added a patch by Julian Lighton, that changes the way effects like Mind Wave interact with special critters like Pink Horrors and Warriors of the Dawn. -Changed the Windows port to support easier resizing of the windows and display of scores. Also removed the Abort menu item. -Added a modified 'main-x11.c' file by Denis Eropkin. -The wall creation spells like "Wall of Stone" no longer create a wall directly on the player. -Corrected the pluralization of some monsters. -Added an updated short building documentation and a new rumor file with many new and changed rumors (thanks to Juergen Neitzel). The total number of rumors is now 613. ZAngband 2.2.3d -Fixed a bug that caused food to become invisible after switching from the 8x8 tiles to ASCII mode. -Fixed a bug in the loading of magic realm specific pref-files. Macros and other preferences in files with the names "Life.prf", "Sorcery.prf", "Nature.prf", "Chaos.prf", "Death.prf", "Trump.prf", and "Arcane.prf" are now automatically loaded for magic users with these realms. -Fixed a small memory leak in the research monster option. -Fixed a bug in the chaos realm spell "Meteor swarm". -ZAngband will no longer try to place Sea-Trolls in troll pits. -Corrected hitpoints of lesser krakens and Yig, Father of Serpents. -Friendly monsters will no longer be wanted for crimes. -Undead players races will start the game at midnight. ZAngband 2.2.3c -Fixed a bug in the weaponsmaster that could cause the inventory to get corrupted. -Worked around a bug in the optimizer of the Windows MS VC compiler that caused all entries in the list of racial powers and mutations to be labeled with "a)". -Fixed a bug that allowed the player to gain full knowledge about all monsters. -Kamikaze yeeks and Leprechaun fanatics are now fearless. -Fixed a bug in the "polymorph self" routine that could cause crashes. -Restored the 'Hellfire' spell to it's former glory. Evil monsters take extra damage, all others normal damage. -Fixed a bug that allowed the player to fire anything with a bow that is too heavy to wield comfortably. -Fixed a bug that could place the player inside rubble after recalling down into the dungeon. ZAngband 2.2.3b -The algorithm for the selection of random quest monsters is now less likely to choose an easy monster, especially on the deeper levels. -Prevented savefile-scumming of the casino. -Fixed a bug that sometimes caused monsters in quests to share the same position with the player. -Fixed a bug that allowed up-stairs to be created in the 'no climbing up' mode on quest-levels. -Fixed the palette of the 16x16.bmp for the Windows version. -Fixed the 16x16 tiles for 'Wolf, Farmer Maggot's dog' and the 'insect swarm'. -Fixed a bug in the vanilla_town option that could cause the player to be trapped in permanent walls. ZAngband 2.2.3 -Improved the initialization- and game-speed and reduced memory requirements. -Added a menu for startup options (press '=' at character generation to enter it). -Added a 'vanilla town' startup-option, that disables the wilderness, buildings and quests (but you can still use the random quests). -Added several 'ironman' startup-options, like 'all stores closed', 'no climbing upwards', 'always small levels', and 'always autoscum'. -Added a menu for 'pet commands' (press 'p' to access it). You can call them to you, send them out to kill monsters, dismiss them, and allow/disallow opening of door and picking up items. Your pets will drop all picked up items when you disallow them to pick up anything. -Greatly lowered the fail-rates of the trump spells. -Trump summoning spells will now only summon hostile monsters if the spell fails. -Rangers start now with a short-bow, a dagger and some arrows instead of the broad-sword. -High-mages will now start with a wand of magic missile. -Changed the monster AI to allow monster groups to surround the player in arena levels and in the wilderness. -Completely changed the item enchantment and recharge services of the buildings and adjusted the prices. -You can now also enchant bolts and shots in the Rangers guild. -Changed the Weaponsmaster to display the weapon-damage including the players bonus to damage. -Random quest monsters may now appear in groups or with escorts. -Changed the creation of trees in the dungeon to a more reasonable layout. -Added a new room type with four pillars. -Lowered the odds of ammunition breaking when thrown/fired. -Changed the display of racial powers and mutations to show fail rates. -Adjusted the 'shriek' mutation. -Changed the 'Hellfire' spell. Now it hurts Good, Evil resist, and others take normal damage. -Fixed a bug in the mindcrafter 'psychometry' spell that could cause crashes. -Fixed a bug in the rumors code that produced *strange* rumors. -You no longer receive "The sun has risen/fallen" messages when in a quest. -The 'Living Trump' spell now correctly gives 'random teleportation' or the 'teleport control' ability. -Golems, skeletons, zombies, vampires and spectres no longer get food rations at character birth. Vampires no longer get scrolls of light. -Fixed a bug that messed up quest-descriptions with single quotes in the text. -The 'banish evil' mutation no longer affects quest monsters or uniques. -'Swordsman' will now be correctly pluralized. -The 'Eric's Stronghold' quest in the 'lite' town is rewarded correctly. -A bug that caused *strange* problems when the sound of the DOS version was enabled was "fixed" by disabling MOD-file support. -Fixed a bug in the initialization of realm dependent buildings. -Added a bunch of new 16x16 tiles by Adam Bolt. -Updated the ZAngband FAQ and the documentation a bit. -The following bugfixes and patches are taken from Tim Baker's ZAngbandTk: -Added the 'easy floor' option. You can now select an item from a stack on the floor by browsing a list. -Fixed a bug that prevented the player from entering a new wilderness area if the target grid was lava or water. -The monster-health bar is now correctly updated. -The 'Cowardice' mutation is now correctly handled. -Animal pack monsters will now flee correctly if afraid. -Fixed a bug that could cause the mindcrafters spell-points to go below zero if a failed spell caused a mana-storm. -Friendly monsters and pets are now a bit more careful in the selection of targets for distance attacks. -Monsters falling asleep from an psi-attack are now correctly noted. -Escaping from an earthquake works now correctly. -Monsters that are embedded in rock after an earthquake are now killed. -Snaga sappers will now explode when killed. ZAngband 2.2.2d -Having mutations will now slow down hitpoint regeneration and reduce the hitpoints healed from draining monsters with a vampiric weapon. Beastman are affected less by this effect. -The door creation spell no longer creates a door directly on the player. -Added many new rumors by Juergen Neitzel. -Fixed the display of the life rating. -Fixed the wilderness mode prompt. -ZAngband now removes the '.lok' file when quitting at character generation on systems where VERIFY_SAVEFILE is defined. -Removed the space after Thorondor's name in r_info.txt. -Beholders are now worth less experience than the undead beholders. -Really fixed the bug in the 'Eric's Stronghold' quest. -Fixed the articles of the new armors. -The 'Assault on Montsalvat' quest is now only available to Paladins of Death. -Fixed two bugs in the monster polymorph code. -Changed the prompt when quitting (commiting suicide). -Changed the behavior of the automatic trap disarming. Traps that can't affect you are now ignored. -Disintegration spells no longer affect water or lava. -Fixed a bug in the artifact activation code that prevented artifact dragon scale mails from activating correctly. -Scrolls of artifact creation no longer affect dragon scale mails. -Split up the source code into smaller files. ZAngband 2.2.2c -The chaos tower can now remove mutations. -Fixed a bug in the layout of the M$ quest. -Fixed a bug that made pack animal fearless. -Fixed a bug that could hang the game in mountain areas. -Fixed several spelling errors. -Fixed a bug that prevented already killed uniques from being revived for a quest. -Fixed a bug that allowed normal sized levels to be created when the 'always create small dungeon-levels' option is active. -Fixed a bug that caused monsters to cast spells at the player even if they can't hit the player with the spell. -Fixed several bugs in the M$ quest. -Fixed a bug in the 'Eric's Stronghold' quest. -Fixed a bug in the R'Lyeh quest. -Fixed a bug that allowed monsters that can 'destroy weaker monsters' to destroy quest monsters. -Fixed a bug that caused the extra attacks from mutations to sometimes "kill" an already dead monster. -Changed the vampiric mutation to act like the racial activation. -The mindcrafter 'psychometry' spell now can pseudo-id already sensed items. -Added Jesse Jones' updated main-mac.c file (used for the Mac versions) with improved handling of multiple monitors, arg_graphics is saved with the preferences, and a much more attractive alert window. ZAngband 2.2.2 -Fixed a bug that could crash the game when entering any area around the town 'Angwil'. -Fixed the 'wilderness mode' prompt. -Allowed the dropping of items and running in forests. -Pressing ESCAPE at the prompt for your bet in the gambling house works now correctly. -Fixed a graphics glitch with beam spells and permanent walls. -'Word of recall' works now also outside of the towns. -Golems, Zombies, Vampires, ... no longer can get food in the inns. -You have now to pay for rumors in the inn. -Dworkin Barimen has now a chance to drop the Jewel of Judgement. -You can no longer kick aquatic monsters in the ankle. -Removed a duplicated 'You have completed your quest!' when summoning and killing a monster in an already completed quest. -Killing monsters with the explosion of a thrown potion gives now experience. -Fixed the 'You see (nothing).' message that can appear in an obscure constellation involving a skeleton quaffing a potion of detonation. -Set the default state of the first two terminal windows to 'Display messages' and 'Display inven/equip'. -Set the default state for the list and look commands to expanded mode. -'Trap Creation' only affects floor grids now. -Added 4 new quests designed by Topi Ylinen. -Added 5 new quests designed by Jeff Coleburn. -Changed some other quests slightly. -Added the Rod of Pesticide (fast charging, activates for a low damage poison cloud). -Changed the behavior of earthquake/destruction spells in quests. In random quests these spells try to teleport away quest monsters, before the area caves in, in predefined quests these spells do nothing. -Changed the (Mass-)Genocide spells to prevent them from working in predefined quests. -Fixed the pluralization of various monster names. -Bloodletters of Khorne may now drop magical blades of chaos. -Added a draft of the new ZAngband-FAQ. -Various other bugfixes. ZAngband 2.2.1 -Changed the scores file back to the old pre-2.2.0 file-format, Score-files from version 2.2.0 won't work! The installation will overwrite the 'lib/apex/scores.raw' file by default, so if you want to keep your old scores file then make a backup before installing. -Added friendly monsters. -Added a 'no wilderness' mode for slower systems. The mode can be selected when starting a new character and when importing a 2.1.1c or older savefile. A special town with quests and buildings has been added for this mode. -Bloodletters of Khorne drop now a Blade of Chaos when killed. -Pink horrors split into two Blue Horrors when killed. -Allowed easy addition of new monsters to r_info.txt. -Various optimizations to improve game speed. -Added a cold aura to some monsters. -Added an option to switch on 'hard quests' on character birth and when importing old savefiles. -Fixed a bug in the 'Reset Recall' spell. -Fixed problems with the 'Vault' quest. -Added a home to the town Telmora. -Changed the 'look' command to ignore boring terrains. -Changed the damage when walking over shallow lava. -Fixed a bug that allowed creation of townspeople in the dungeon. -Changed the behavior of the 'magic mapping' spell. -Fixed a bug that allowed monsters to ignore 'glyphs of warding' and 'exploding runes'. -Fixed bugs in the 'absorb light' mutation. -Fixed a bug that sometimes prevented the creation of 'connected stairs' and caused lookups when killing the last quest-monster. -Fixed a bug that hangs the game when displaying the contents of your houses after quitting or dying. -Various changes in the monster definitions. -Fixed the k_info.txt entry of the Trifurcate Spear. -Removed some disturbing messages. -'Detect treasure' no longer detects monsters represented with an '*'. -Prevented the use of 'Alter Reality' in quests. -Changed the description of ~ in the 'identify symbol' command. -Changed some of the 8x8 tiles. -Hagen has now a high chance to drop his spear. -Fixed the 'cursed items as quest rewards' bug. ZAngband 2.2.0 -Added 279 new monsters and the corresponding 8x8 sized tiles designed and drawed by Topi Ylinen. -Added 46 new items (weapons, armor, and ammo) designed by John Duffin and Leigh Silas Hanrihan. -Added exploding, aquatic, swimming, and flying monsters. -Added new monster melee attacks (time, explode, and disease). -Added Ken Wigle's KAngband style town, quests and terrains. -Changed quest system to allow more flexibility and multi-level quests. -Added big wilderness outside the towns with a modified fractal terrain generator from KAmband. -New initialization code for quests, towns, buildings and wilderness. -Removed the 64 kByte limits for monsters, items, artifacts, and vaults. -Moved the definition of the limits for the number of monsters, items, vaults, ... to an external data-file. -Added Keldon Jones' patch to allow automatic recreation of the raw-files. -Added "equippy chars" in the inventory, equipment list and in shops. -Fixed the definitions of the new tiles for the Half-Ogres and Dark-Elfes. -Added support for the S-Lang scripting language, based on Scott Bigham's S-Lang patch. -Added Greg Wooledge's patch that adds the "flag grid" to Zangband char dumps and shows immunities in the "flag grid". -The Windows version no longer requires "LibPath" to be set correctly. This allows installation in any directory without changes to the ini-file. -Allowed random selection of race, class, realms, ... at character birth with the * key. -Added the Easy-Patch by Tim Baker and changed it to be an option. -Added 52 new mutations created by Jeff Duprey. -Changed the pet AI to make the pets more useful. -Added new sound-events. -Only uniques will now resist disintegration effects. -Warrior-Mages can now choose Sorcery. -Many other changes and bugfixes. ZAngband 2.1.1c -Fixed the 16x16 tile for hobbit characters. A beer mug is fitting for a hobbit, but it was still a bug. ;-) -Fixed another bug in the creation of quest levels. -Allowed all characters created in pre 2.1.1 versions of ZAngband to choose the number of quests when loading the savegame. You may have to leave and reenter the current level, if it's a quest level. -Only Uniques will now resist disintegration effects. -The last quest monster now drops all carried items, the normal monster drop and one reward item when you finish the quest. -Reward drops are no longer added to the monster knowledge. -Fixed the problem with the transparent color of the 8x8 bitmap. ZAngband 2.1.1b -Quest message pluralization fixed (again). -No more extremely out of depth quests for new characters. -Disintegration spells like "Fist of force" can destroy walls. -Reflection works now. -Corrected the new tiles for spirit naga, angel, and blue jellies. ZAngband 2.1.1 -Paralysis attacks on a paralyzed player always do at least one hp of damage. This should prevent "infinite paralysis" from floating eyes. -Fixed some bugs in the autosaving code. -Fixed a bug in the monster-AI code. -Added circular rooms from Dag Arneson Ying-YAngband. -Fixed a bug that allowed the creation of artifact-arrows, -bolts, and -shots with scrolls of artifact creation. -Adam Bolt's tiles are now finished and every monster/object/spell effect has a tile. -Another bug in the quest code fixed. -Quest messages are now correctly pluralized. -The roguelike commands to show game time (the ' key) and to activate the racial power/mutation (the O key) work now. -Benny S. Hofmann's patch to the windows version allows switching between ASCII, old tiles and Adam Bolt's tiles. -Toned down the TY-curse a bit. ZAngband 2.1.1 beta 2 -Updated Adam Bolt's terrain tiles again. -Implemented the item flavoring code from Angband 2.8.3. -Fixed a bug in the macro-creation. -Fixed a bug creation of the quest levels. ZAngband 2.1.1 beta 1 -Updated Adam Bolt's terrain tiles. -Mindcrafter powers are correctly displayed in the spell-list window and don't crash the game anymore. -Fixed a bug in the random activations of the mutations, the effects appear less often now. -The main-win.c file is now ready for compiling with Adam Bolt's new tiles. Just define "USE_AB_TILES" and compile it. The windows version also supports transparency and the "double-step bug" is fixed. -Added a sound-event for the "Hitpoint Warning". -Pets will no longer pick up/destroy items. -Implemented Greg Harvey's patch that adds the macro code from 2.8.3. -Added Heino Vander Sanden's QAngband quest code with big help from Dean Anderson. Zangband 2.1.0e -Fixed some bugs with the new bookstore and the store maintenance and owner shuffling works for the bookstore. -Fixed the problem with compiling the main.c file on Unix/Linux systems. Zangband 2.1.0d -Silas Dunsmore's cumulative light radius patch added. Any object in your equipment that has the 'glow' flag will extend your lit-area radius by 1, up to a maximum of 5. The normal light-source objects still work the same, but glowing artifacts and helms of light are actually useful. -Added a bookstore to the town to free up space in the magic shop. -Fixed problems with the Summon Demon (Chaos), Raise Death (Death), Phantasmal Servant (Trump), Mindwave, and Adrenaline (Mindcraft) spells on systems without mathematical coprocessor. -Chaos Warriors are no longer hurt (or killed) by the "strain of casting (Mass) Genocide" when the chaos patron casts the Genocide spell ("Let me relieve thee of thine oppressors!"). -Fixed a bug with the sound-effects, the correct effects will be played now. -Added new sound-events for buying and selling in stores. -Added support for Adam Bolt's new tile-graphics, transparency- and lighting-effects, MOD-, and S3M-files to the DOS version. Adam Bolt's tiles for ZAngband are not yet finished, most of the ZAngband specific monsters are displayed with other tiles or with ASCII-characters. The special DOS options can be accessed by pressing ! while playing. Zangband 2.1.0c -Command (in powers menu) to dismiss all pets (costs no mana, takes a turn) -Alberich has lost his "deadly touch" -Hilarious new nonsensical rumors courtesy of altavista translation service -A "pit" variant of the "symbol clone nest" -James Lockwood's store-auto*id* and examine patch -Patches from Silas Dunsmore, lots of little fixes -Teleporting monsters have to pass a skill test to follow you -"Blessed" weapons are likely to resist cursing by monsters -Heavy cursing less likely; scrolls of *Remove curse* for sale in the temple TODO (features/ideas/bugfixes that have not yet been implemented): * Game difficulty levels (point-based / simple to impossible?) * Quests * Your own inscriptions should not be junked if an item becomes cursed * Potential bug: exploding unmakers crash / hang the game? * Maybe weapons of slay xxx should glow if xxx is nearby? (option) * Bug? Eldritch horrors seen first via ESP don't blast your sanity? * Disintegration balls can explode out-of-bounds? * Add oriental melee weapons for monks (nunchaku, bo stick etc.) * Smashed potions should also affect the player (if within range) * Perhaps add Silas Dunsmore's cumulative light radius patch -- Zangband 2.1.0 -Version handling for save files, pre-210 save files loaded as 206 save files * Svga is the default; '-mibm' for no graphics, '-mibm -g' for font graphics -The Dos version can run in 3 modes: SVGA graphics, font graphics, text only -Lots of extra tests added to generate.c to avoid infinite loops -Special feelings caused by other than artifacts should be less common now -New options; Autosaving options; "Testing" options renamed ("Stacking") -Confirm staircases turned into a runtime option -New plain_descriptions option (to split up show_labels) -New auto_destroy option to destroy known worthless items without prompting -New option to confirm wielding/wearing known cursed items -Confirm staircases is a runtime option now -Options menu reorganized. Zangband special options under a separate sub-menu -Activate power command ('U'/'O') uses a menu now -Total kill count option added in the 'knowledge' menu -The display command can dump to file (command 'f') -New texts added (into lib/file/) by jd9072@aol.com, for extra color -Gollum patch (by John May & 'Tim') implemented (slightly toned down version) -Fixed a number of bugs in the object absorption code (also in store.c) -2 new magic realms: Trump (teleport+summoning), Arcane (weak general purpose) -The "Polymorph Self" is a bit less useless now -Added chaos mutations -Potion of New Life gets rid of all mutations (in addition to normal effect) -Added (possible) activation effects for random artifacts -Immunities are a _lot_ less common in random artifacts now -Fixed (?) the bug which was causing the One Ring sometimes not being cursed -Four new types of amulets to make amulets more interesting -Feather Fall turned into Levitation (allows flying over trap doors) -Object flags: NO_TELE (prevents teleporting), NO_MAGIC (no spells, high save) -Added a very powerful new artifact, the Sword of the Dawn (as rare as Ringil) -Two new races: Sprite (very weak & smart), Chaos Beastman (gain mutations) -New class: High Mage (specializes in one realm, learns it really well) -Warrior/Paladin/DeathKn gain xp for destroying deep books (all/non-Life/Life) -Certain races gain no nutrition from food (they must use Satisfy Hunger) -Chaos warriors gain resist chaos at lvl 30, resist fear at lvl 40 -Unencumbered monks gain free action at lvl 25 -Monks are more likely to attack with the highest level attack available now -Added special breaths for Draconian Monks, Chaos Warriors and Warrior-Mages -Monsters which use the same symbol sometimes appear in 'hordes' -Everybody's favourite monster, 'the Unmaker', is far nastier now -Some monsters are 'good' (as opposed to 'evil'), immune to holy fire -Some monsters are sanity-blasting 'eldritch horrors' -For fairness' sake, monsters can also have reflection & auras of fire/elec -Certain monsters may resist teleportation -Adjacent monsters which can teleport may follow you when you teleport -Clones drop treasure nor objects -The 'xxx curses' spells may actually make your equipment 'cursed' -Added a new very powerful spell, Hand of Doom, for high level uniques -Killing Amberites can be dangerous -- ever heard of the Amberite blood curse? -The Serpent of Chaos is a bit tougher now -Various minor fixes & cosmetic enhancements & new icons -Lots of features by Paul Sexton: * Major revision of the spell system (nature+death+chaos upgraded) * Summoning spells for friendly monsters * Charming spells to make new friends * Wand of Lightning Bolts -> Tame Monster * Two new races: Vampire & Spectre * Mindcrafter class * Potion smashing effects * Cosmetic changes (character info screen) -My own changes to the above: * You may accidentally attack friends if blind, confused etc. * No 'friendly uniques' can be summoned * You must pay upkeep (mana) for friendly monsters * Fixed the fatal "displacing crash" bug (caused by my own code) * 'Friends' are also possible as Chaos rewards, artifact activation -- Zangband 2.0.6 -Dos version compiled with Robert Ruehlmann's SVGA graphics+windows code -The option display_spell_flags works slightly better now (on windowed systems) -Free Action _almost_ negates the paralyzing effect of 'ancient curse' -Three new character classes: Warrior-Mage, Chaos Warrior and Monk -Three new (protective) item flags: Reflection, Auras of Fire and Electricity -Pattern Vaults implemented -- now any character can walk the Pattern -The artifacts you create yourself will be *identified* when created -Certain uniques may drop artifacts which 'belong' to them -Patch (by Daniel Nash): Wizard mode command to create specific artifacts -Patch (by Scott Bigham): Rods sort by recharging time left -Fixed a missing break which was crippling the "Summon Kin" code -Invulnerability no longer induces 'blindness' with graphics enabled -Hopefully fixed the char dump so that it no longer requires fp math unit -Certain ego items with digging actually receive + to digging -Empty dark levels are a lot less common early on now -Life magic Holy orb is resisted by non-evils (Hellfire works as before) -Detect Treasure / Objects also detects Treasure / Object 'monsters' -Only 'Assassins' (Rogues with Death magic) get a poisoned weapon when created -Warriors get the extra shots later than rangers -Lots of minor and cosmetic fixes *Bugfix release: 2.0.6b: -Compiled the dos binary so that it asks for no confirmation with stairs -Birth.txt includes a description of monk attack types -Chaos Warriors get 'nasty' rewards much less often now -Fixed a missing break in self_knowledge -Fixed the 'Tunneling bug' -Quick-fixed the infamous 'two-eyed cyclopses bug' (for new characters) -Fixed a minor display bug with equippy chars which did not update correctly -The function calc_spells() should work a bit more logically now -Caine and Hagen had their drops mixed; fixed. -Dworkin may now carry the JoJ. Smeagol may still NOT carry the ... Ring -Fixed the potion icon bug -Also redrew / modified / added icons -- Zangband 2.0.5 -Stuff from 2.8.2, including: * Auto-knowledge of standard ego item flags * The new generic "knowledge" command * Preserve artifacts when creation fails * 'Synchronized' compact objects+monsters when saving * The old z-term.c replaced with the new one * Monsters stay visible as long as they are being detected * (Other bug fixes already present, like the stacking bug fix) -Fixed a bug which could cause teleport_player to hang the game -Fixed the "This race has no bonus power." message bug (zombies, skeletons) -Halflings can cook some food -Klackon powers changed: they _become_ faster, activate to spit acid -Dark Elves gain their power (magic missile) one level earlier now -Most Chaos attack spells are a bit cheaper for mages -Chaos loses Blink, new spell ('Wallbreaker') for chaotic wall destruction -Death Ray (in Necronomicon) works a lot more often now -Healing II replaced w/ 'Bless Weapon', of use to priests (artifacts resist) -Blessings of the Grail is no longer 'heavily blessed' :) -Explosive runes are no longer 'permanent rock' -Self knowledge gives some info about your racial power now -'Amber' skill rank (numeric value) is calculated differently now -Inscribe '.' in a (non-cursed) teleporting item to prevent teleporting -They will also not teleport you if have the disturb_other option set to FALSE -Vampiric weapons can heal a maximum of 100 hp per round -Random bow artifacts no longer get 'useless' (slay xxx) flags -Immunities are somewhat less common in random artifacts -The 'bias' code no longer produces arbitrary resistances for ego items -Better random names for random artifacts -Also, Julian Lighton's patch to fix crashes (w/ random artifacts) implemented -Wand of Rockets identifies itself if used (like other wands) -Scrolls of *identify* are a bit more common now -*Identify* gives correct info about Fear Resistance in an item -The temple buys blessed weapons -A certain artifact which is not in a_info will be cursed when created -Julian Lighton's ingenious new monster spell to summon 'relatives' added -The boring Terminator monster replaced w/ Warriors of the Dawn (from Hawkmoon) -Some of the highest level uniques can now summon Cyberdemons to help them -Some of the Lovecraftian uniques have been boosted up a bit -Some of the other uniques have been upgraded a bit as well -Groo behaves in a more Grooish manner now -Themis and Mnemosyne have the correct gender now -Fixed a display bug (on a PC, w/ a shapechanger + no graphics) -Again, more minor and cosmetic fixes -- Zangband 2.0.4 -Correct version display -No less than 16 new player races (all new, although a few ideas are borrowed) -Recharging True replaced w/ Explosive Rune (explodes only if you stand on it) -Mages can choose Life magic (they are worse than paladins in it) -Death Knights (variant of the Paladin class) added -Warriors and Paladins become immune to fear at high levels -Rogues also get a (much weaker) backstab attack against fleeing foes -New vaults (vaguely based on ancient Egyptian tombs / temples) -Small levels have (initially) less monsters now (related to the level size) -More precautions to avoid (the rare) hangups when a small level is generated -Fixed a bug with spell selection (cmd5.c) -Rings of Flames etc. also give temporary resistance when activated -Rings of Flames etc. show their activation info now when *identified* -Weapons of fire will also work as light now -Slightly better, 'smart' generation of random artifacts -New summoning effects for 'wild magic' -Level based bonus to the racial power activation test -Ball of Radiation & Invoke Logrus (monster attacks) are functional now -Radiation effects changed slightly -A certain artifact which is not in a_info no longer gets RES_DISEN -Minor changes to the PC font (hopefully for the better...) -Slightly saner price calculation for random items -Slightly better prompts ("recite which prayer" vs "cast which prayer") -Other cosmetic and minor fixes -- Zangband 2.0.3 -Rings of Flames, Ice and Acid can now be activated (for an obvious effect) -New vaults (some have their floor plans borrowed from Nethack) -Cosmetic fixes (documentation) -Game balance fixes -Kharis no longer crashes the spoiler generation -Evil jellies (like death molds and shoggoths) no longer show up in jelly pits -Better 'speech' for uniques that are afraid (/lib/file/monfear.txt) -Trump weapons can now also be activated for teleport -Ego weapon damage dice loaded properly -Amberites can now open doors, 'summon Amberites' no longer produces undead -Monster special resistances fully implemented -Grayswandir is a sabre now -Rings of high resistance stack now (Lordly Protection doesn't) -Fixed Rings of Extra Attacks -Curing also cures hallucination -Angels are fearless now -Nature's Wrath (spell) damage upgraded -Call Chaos sometimes produces highly destructive beams now -- Zangband 2.0.2 -Internal version Angband 2.8.1, fake version (Z 2.0.2) displayed for commands -Finally updated (almost all of) the documentation! -Fixed more bugs in v_info -The stacking patch applied (fixes gold deleting all objects below it) -Redraw mana / hp after using racial powers & receiving rewards -Certain stationary monsters are fearless now -Priests can choose Life or Death magic (not both) + 1 from S / N / C -Some paladin prayers are slightly easier now -Sorcery spells are now available earlier (to make the realm more competitive) -Miscast Chaos spells may produce random "wild magic" effects -Miscast Death spells may hurt the player -Nature Awareness downgraded slightly -'Produce food' (Nature spell) renamed to 'Foraging' (no real reason, though) -Other cosmetic fixes+enhancements (Ball of Cold has no longer "Duration" :) -Different colours for the special player symbol (level dependent) -Nightwalker CAN_SPEAK moved to Raphael (where it belongs) -Rotting corpses and malicious leprechauns have physical attacks now -No more freebie attacks on unseen monsters in walls (digging must be used) -Using certain aimed rods no longer randomly crashes the game -Small levels should no longer cause alloc_monster to hang the game -Dragon Helm of Dor-Lomin no longer crashes the game -Arcum Dagsson's code to support separate macro files for different realms -Fixed a bug that prevented random resistances for some artifacts -Fixed a bug which prevented the saving of random flags from certain items -Better name-forming code for Random artifacts (created in Black Market) -Better descriptions for unidentified objects (without show_labels) -Equippy characters restored as an option by popular demand (I missed them too) -Initial life rating display moved to a more logical place -do_cmd_rerate will now be compiled even without wizard mode -Rewards for killing wanted uniques are at least 250 gold (not 0 gold) -Option to dump the 'final' screen (when you die) -- Zangband 2.0.1 -Nature Sense Surroundings upgraded to Nature Awareness -Removed 'marginal' item generation from temple, added 3 * scroll of WoR -Compile time option to display different player symbols (/race, /class) -Fixed take_hit message for Scroll of Logrus (not "of Chaos") -Fixed the major bug with spell remembrance/forgetting in xtra1.c -Fixed the damage display for Frost Bolt -Fixed "You can learn 1 new spells." in cmd5.c -Magic traps are a bit less lethal early on -Silly hallucination monsters (as in Nethack) -Potion of Confusion -> Booze -Alchemy now takes into account the amount of items (max still 30k / spell) -Changes to options -Genocide True replaced with Vampiric Branding -Fixed a bug in v_info -Items stolen by the agents of black market may show up in the Black market -Racial intrinsics are a bit easier to use now -- Zangband 2.0.0 -New spell system (life, sorcery, nature, chaos, death magic) -New monsters list (mostly from the older Zangband) -User can_not_ set the colors of flavored objects (via pref files) -Warriors get extra blows and shots, increased damage -Rangers have a slightly better innate pseudo-id -Last words, speaking uniques, rumors, warrants, error messages -"Negative" level feelings -New ego items and artifacts -New object flags (VORPAL, CHAOTIC, VAMPIRIC, BRAND_POIS) -Rogues start with a poisoned dagger and can backstab nasties -xtra1.c, files.c: DUNADAN -> AMBERITE -Other stuff... zangband/src/0000755000000000000000000000000010250356275012134 5ustar rootrootzangband/src/lua/0000755000000000000000000000000010250356275012715 5ustar rootrootzangband/src/lua/lapi.c0000644000000000000000000002533610250356275014017 0ustar rootroot/* ** $Id: lapi.c,v 1.2 2002/08/29 19:06:48 rr9 Exp $ ** Lua API ** See Copyright Notice in lua.h */ #include #include "lua.h" #include "lapi.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" const char lua_ident[] = "$Lua: " LUA_VERSION " " LUA_COPYRIGHT " $\n" "$Authors: " LUA_AUTHORS " $"; #define Index(L,i) ((i) >= 0 ? (L->Cbase+((i)-1)) : (L->top+(i))) #define api_incr_top(L) incr_top TObject *luaA_index (lua_State *L, int index) { return Index(L, index); } static TObject *luaA_indexAcceptable (lua_State *L, int index) { if (index >= 0) { TObject *o = L->Cbase+(index-1); if (o >= L->top) return NULL; else return o; } else return L->top+index; } void luaA_pushobject (lua_State *L, const TObject *o) { *L->top = *o; incr_top; } LUA_API int lua_stackspace (lua_State *L) { return (L->stack_last - L->top); } /* ** basic stack manipulation */ LUA_API int lua_gettop (lua_State *L) { return (L->top - L->Cbase); } LUA_API void lua_settop (lua_State *L, int index) { if (index >= 0) luaD_adjusttop(L, L->Cbase, index); else L->top = L->top+index+1; /* index is negative */ } LUA_API void lua_remove (lua_State *L, int index) { StkId p = luaA_index(L, index); while (++p < L->top) *(p-1) = *p; L->top--; } LUA_API void lua_insert (lua_State *L, int index) { StkId p = luaA_index(L, index); StkId q; for (q = L->top; q>p; q--) *q = *(q-1); *p = *L->top; } LUA_API void lua_pushvalue (lua_State *L, int index) { *L->top = *luaA_index(L, index); api_incr_top(L); } /* ** access functions (stack -> C) */ LUA_API int lua_type (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL) ? LUA_TNONE : ttype(o); } LUA_API const char *lua_typename (lua_State *L, int t) { UNUSED(L); return (t == LUA_TNONE) ? "no value" : luaO_typenames[t]; } LUA_API int lua_iscfunction (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL) ? 0 : iscfunction(o); } LUA_API int lua_isnumber (lua_State *L, int index) { TObject *o = luaA_indexAcceptable(L, index); return (o == NULL) ? 0 : (tonumber(o) == 0); } LUA_API int lua_isstring (lua_State *L, int index) { int t = lua_type(L, index); return (t == LUA_TSTRING || t == LUA_TNUMBER); } LUA_API int lua_tag (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL) ? LUA_NOTAG : luaT_tag(o); } LUA_API int lua_equal (lua_State *L, int index1, int index2) { StkId o1 = luaA_indexAcceptable(L, index1); StkId o2 = luaA_indexAcceptable(L, index2); if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */ else return luaO_equalObj(o1, o2); } LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { StkId o1 = luaA_indexAcceptable(L, index1); StkId o2 = luaA_indexAcceptable(L, index2); if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */ else return luaV_lessthan(L, o1, o2, L->top); } LUA_API long lua_tonumber (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL || tonumber(o)) ? 0 : nvalue(o); } LUA_API const char *lua_tostring (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL || tostring(L, o)) ? NULL : svalue(o); } LUA_API size_t lua_strlen (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL || tostring(L, o)) ? 0 : tsvalue(o)->len; } LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL || !iscfunction(o)) ? NULL : clvalue(o)->f.c; } LUA_API void *lua_touserdata (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); return (o == NULL || ttype(o) != LUA_TUSERDATA) ? NULL : tsvalue(o)->u.d.value; } LUA_API const void *lua_topointer (lua_State *L, int index) { StkId o = luaA_indexAcceptable(L, index); if (o == NULL) return NULL; switch (ttype(o)) { case LUA_TTABLE: return hvalue(o); case LUA_TFUNCTION: return clvalue(o); default: return NULL; } } /* ** push functions (C -> stack) */ LUA_API void lua_pushnil (lua_State *L) { ttype(L->top) = LUA_TNIL; api_incr_top(L); } LUA_API void lua_pushnumber (lua_State *L, long n) { nvalue(L->top) = n; ttype(L->top) = LUA_TNUMBER; api_incr_top(L); } LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { tsvalue(L->top) = luaS_newlstr(L, s, len); ttype(L->top) = LUA_TSTRING; api_incr_top(L); } LUA_API void lua_pushstring (lua_State *L, const char *s) { if (s == NULL) lua_pushnil(L); else lua_pushlstring(L, s, strlen(s)); } LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { luaV_Cclosure(L, fn, n); } LUA_API void lua_pushusertag (lua_State *L, void *u, int tag) { /* ORDER LUA_T */ if (!(tag == LUA_ANYTAG || tag == LUA_TUSERDATA || validtag(tag))) luaO_verror(L, "invalid tag for a userdata (%d)", tag); tsvalue(L->top) = luaS_createudata(L, u, tag); ttype(L->top) = LUA_TUSERDATA; api_incr_top(L); } /* ** get functions (Lua -> stack) */ LUA_API void lua_getglobal (lua_State *L, const char *name) { StkId top = L->top; *top = *luaV_getglobal(L, luaS_new(L, name)); L->top = top; api_incr_top(L); } LUA_API void lua_gettable (lua_State *L, int index) { StkId t = Index(L, index); StkId top = L->top; *(top-1) = *luaV_gettable(L, t); L->top = top; /* tag method may change top */ } LUA_API void lua_rawget (lua_State *L, int index) { StkId t = Index(L, index); LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected"); *(L->top - 1) = *luaH_get(L, hvalue(t), L->top - 1); } LUA_API void lua_rawgeti (lua_State *L, int index, int n) { StkId o = Index(L, index); LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected"); *L->top = *luaH_getnum(hvalue(o), n); api_incr_top(L); } LUA_API void lua_getglobals (lua_State *L) { hvalue(L->top) = L->gt; ttype(L->top) = LUA_TTABLE; api_incr_top(L); } LUA_API int lua_getref (lua_State *L, int ref) { if (ref == LUA_REFNIL) ttype(L->top) = LUA_TNIL; else if (0 <= ref && ref < L->refSize && (L->refArray[ref].st == LOCK || L->refArray[ref].st == HOLD)) *L->top = L->refArray[ref].o; else return 0; api_incr_top(L); return 1; } LUA_API void lua_newtable (lua_State *L) { hvalue(L->top) = luaH_new(L, 0); ttype(L->top) = LUA_TTABLE; api_incr_top(L); } /* ** set functions (stack -> Lua) */ LUA_API void lua_setglobal (lua_State *L, const char *name) { StkId top = L->top; luaV_setglobal(L, luaS_new(L, name)); L->top = top-1; /* remove element from the top */ } LUA_API void lua_settable (lua_State *L, int index) { StkId t = Index(L, index); StkId top = L->top; luaV_settable(L, t, top-2); L->top = top-2; /* pop index and value */ } LUA_API void lua_rawset (lua_State *L, int index) { StkId t = Index(L, index); LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected"); *luaH_set(L, hvalue(t), L->top-2) = *(L->top-1); L->top -= 2; } LUA_API void lua_rawseti (lua_State *L, int index, int n) { StkId o = Index(L, index); LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected"); *luaH_setint(L, hvalue(o), n) = *(L->top-1); L->top--; } LUA_API void lua_setglobals (lua_State *L) { StkId newtable = --L->top; LUA_ASSERT(ttype(newtable) == LUA_TTABLE, "table expected"); L->gt = hvalue(newtable); } LUA_API int lua_ref (lua_State *L, int lock) { int ref; if (ttype(L->top-1) == LUA_TNIL) ref = LUA_REFNIL; else { if (L->refFree != NONEXT) { /* is there a free place? */ ref = L->refFree; L->refFree = L->refArray[ref].st; } else { /* no more free places */ luaM_growvector(L, L->refArray, L->refSize, 1, struct Ref, "reference table overflow", MAX_INT); L->nblocks += sizeof(struct Ref); ref = L->refSize++; } L->refArray[ref].o = *(L->top-1); L->refArray[ref].st = lock ? LOCK : HOLD; } L->top--; return ref; } /* ** "do" functions (run Lua code) ** (most of them are in ldo.c) */ LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults) { luaD_call(L, L->top-(nargs+1), nresults); } /* ** Garbage-collection functions */ /* GC values are expressed in Kbytes: #bytes/2^10 */ #define GCscale(x) ((int)((x)>>10)) #define GCunscale(x) ((unsigned long)(x)<<10) LUA_API int lua_getgcthreshold (lua_State *L) { return GCscale(L->GCthreshold); } LUA_API int lua_getgccount (lua_State *L) { return GCscale(L->nblocks); } LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold) { if (newthreshold > GCscale(ULONG_MAX)) L->GCthreshold = ULONG_MAX; else L->GCthreshold = GCunscale(newthreshold); luaC_checkGC(L); } /* ** miscellaneous functions */ LUA_API void lua_settag (lua_State *L, int tag) { luaT_realtag(L, tag); switch (ttype(L->top-1)) { case LUA_TTABLE: hvalue(L->top-1)->htag = tag; break; case LUA_TUSERDATA: tsvalue(L->top-1)->u.d.tag = tag; break; default: luaO_verror(L, "cannot change the tag of a %.20s", luaO_typename(L->top-1)); } } LUA_API void lua_unref (lua_State *L, int ref) { if (ref >= 0) { LUA_ASSERT(ref < L->refSize && L->refArray[ref].st < 0, "invalid ref"); L->refArray[ref].st = L->refFree; L->refFree = ref; } } LUA_API int lua_next (lua_State *L, int index) { StkId t = luaA_index(L, index); Node *n; LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected"); n = luaH_next(L, hvalue(t), luaA_index(L, -1)); if (n) { *(L->top-1) = *key(n); *L->top = *val(n); api_incr_top(L); return 1; } else { /* no more elements */ L->top -= 1; /* remove key */ return 0; } } LUA_API int lua_getn (lua_State *L, int index) { Hash *h = hvalue(luaA_index(L, index)); const TObject *value = luaH_getstr(h, luaS_new(L, "n")); /* value = h.n */ if (ttype(value) == LUA_TNUMBER) return (int)nvalue(value); else { Number max = 0; int i = h->size; Node *n = h->node; while (i--) { if (ttype(key(n)) == LUA_TNUMBER && ttype(val(n)) != LUA_TNIL && nvalue(key(n)) > max) max = nvalue(key(n)); n++; } return (int)max; } } LUA_API void lua_concat (lua_State *L, int n) { StkId top = L->top; luaV_strconc(L, n, top); L->top = top-(n-1); luaC_checkGC(L); } LUA_API void *lua_newuserdata (lua_State *L, size_t size) { TString *ts = luaS_newudata(L, (size==0) ? 1 : size, NULL); tsvalue(L->top) = ts; ttype(L->top) = LUA_TUSERDATA; api_incr_top(L); return ts->u.d.value; } zangband/src/lua/lauxlib.c0000644000000000000000000001172410250356275014526 0ustar rootroot/* ** $Id: lauxlib.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #include #include #include /* This file uses only the official API of Lua. ** Any function declared here could be written as an application function. ** With care, these functions can be used by other libraries. */ #include "lua.h" #include "lauxlib.h" #include "luadebug.h" LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { int i; for (i=0; list[i]; i++) if (strcmp(list[i], name) == 0) return i; return -1; /* name not found */ } LUALIB_API void luaL_argerror (lua_State *L, int narg, const char *extramsg) { lua_Debug ar; lua_getstack(L, 0, &ar); lua_getinfo(L, "n", &ar); if (ar.name == NULL) ar.name = "?"; luaL_verror(L, "bad argument #%d to `%.50s' (%.100s)", narg, ar.name, extramsg); } static void type_error (lua_State *L, int narg, int t) { char buff[50]; sprintf(buff, "%.8s expected, got %.8s", lua_typename(L, t), lua_typename(L, lua_type(L, narg))); luaL_argerror(L, narg, buff); } LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { if (space > lua_stackspace(L)) luaL_verror(L, "stack overflow (%.30s)", mes); } LUALIB_API void luaL_checktype(lua_State *L, int narg, int t) { if (lua_type(L, narg) != t) type_error(L, narg, t); } LUALIB_API void luaL_checkany (lua_State *L, int narg) { if (lua_type(L, narg) == LUA_TNONE) luaL_argerror(L, narg, "value expected"); } LUALIB_API const char *luaL_check_lstr (lua_State *L, int narg, size_t *len) { const char *s = lua_tostring(L, narg); if (!s) type_error(L, narg, LUA_TSTRING); if (len) *len = lua_strlen(L, narg); return s; } LUALIB_API const char *luaL_opt_lstr (lua_State *L, int narg, const char *def, size_t *len) { if (lua_isnull(L, narg)) { if (len) *len = (def ? strlen(def) : 0); return def; } else return luaL_check_lstr(L, narg, len); } LUALIB_API long luaL_check_number (lua_State *L, int narg) { long d = lua_tonumber(L, narg); if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ type_error(L, narg, LUA_TNUMBER); return d; } LUALIB_API long luaL_opt_number (lua_State *L, int narg, long def) { if (lua_isnull(L, narg)) return def; else return luaL_check_number(L, narg); } LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n) { int i; for (i=0; ip == (B)->buffer) #define bufflen(B) ((B)->p - (B)->buffer) #define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) #define LIMIT (LUA_MINSTACK/2) static int emptybuffer (luaL_Buffer *B) { size_t l = bufflen(B); if (l == 0) return 0; /* put nothing on stack */ else { lua_pushlstring(B->L, B->buffer, l); B->p = B->buffer; B->level++; return 1; } } static void adjuststack (luaL_Buffer *B) { if (B->level > 1) { lua_State *L = B->L; int toget = 1; /* number of levels to concat */ size_t toplen = lua_strlen(L, -1); do { size_t l = lua_strlen(L, -(toget+1)); if (B->level - toget + 1 >= LIMIT || toplen > l) { toplen += l; toget++; } else break; } while (toget < B->level); if (toget >= 2) { lua_concat(L, toget); B->level = B->level - toget + 1; } } } LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { if (emptybuffer(B)) adjuststack(B); return B->buffer; } LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { while (l--) luaL_putchar(B, *s++); } LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { luaL_addlstring(B, s, strlen(s)); } LUALIB_API void luaL_pushresult (luaL_Buffer *B) { emptybuffer(B); if (B->level == 0) lua_pushlstring(B->L, NULL, 0); else if (B->level > 1) lua_concat(B->L, B->level); B->level = 1; } LUALIB_API void luaL_addvalue (luaL_Buffer *B) { lua_State *L = B->L; size_t vl = lua_strlen(L, -1); if (vl <= bufffree(B)) { /* fit into buffer? */ memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ B->p += vl; lua_pop(L, 1); /* remove from stack */ } else { if (emptybuffer(B)) lua_insert(L, -2); /* put buffer before new value */ B->level++; /* add new value into B stack */ adjuststack(B); } } LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { B->L = L; B->p = B->buffer; B->level = 0; } /* }====================================================== */ zangband/src/lua/lbaselib.c0000644000000000000000000004131610250356275014643 0ustar rootroot/* ** $Id: lbaselib.c,v 1.2 2002/08/29 19:06:49 rr9 Exp $ ** Basic library ** See Copyright Notice in lua.h */ #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "luadebug.h" #include "lualib.h" /* ** If your system does not support `stderr', redefine this function, or ** redefine _ERRORMESSAGE so that it won't need _ALERT. */ static int luaB__ALERT (lua_State *L) { fputs(luaL_check_string(L, 1), stderr); return 0; } /* ** Basic implementation of _ERRORMESSAGE. ** The library `liolib' redefines _ERRORMESSAGE for better error information. */ static int luaB__ERRORMESSAGE (lua_State *L) { luaL_checktype(L, 1, LUA_TSTRING); lua_getglobal(L, LUA_ALERT); if (lua_isfunction(L, -1)) { /* avoid error loop if _ALERT is not defined */ lua_Debug ar; lua_pushstring(L, "error: "); lua_pushvalue(L, 1); if (lua_getstack(L, 1, &ar)) { lua_getinfo(L, "Sl", &ar); if (ar.source && ar.currentline > 0) { char buff[100]; sprintf(buff, "\n <%.70s: line %d>", ar.short_src, ar.currentline); lua_pushstring(L, buff); lua_concat(L, 2); } } lua_pushstring(L, "\n"); lua_concat(L, 3); lua_rawcall(L, 1, 0); } return 0; } /* ** If your system does not support `stdout', you can just remove this function. ** If you need, you can define your own `print' function, following this ** model but changing `fputs' to put the strings at a proper place ** (a console window or a log file, for instance). */ static int luaB_print (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int i; lua_getglobal(L, "tostring"); for (i=1; i<=n; i++) { const char *s; lua_pushvalue(L, -1); /* function to be called */ lua_pushvalue(L, i); /* value to print */ lua_rawcall(L, 1, 1); s = lua_tostring(L, -1); /* get result */ if (s == NULL) lua_error(L, "`tostring' must return a string to `print'"); if (i>1) fputs("\t", stdout); fputs(s, stdout); lua_pop(L, 1); /* pop result */ } fputs("\n", stdout); return 0; } static int luaB_tonumber (lua_State *L) { int base = luaL_opt_int(L, 2, 10); if (base == 10) { /* standard conversion */ luaL_checkany(L, 1); if (lua_isnumber(L, 1)) { lua_pushnumber(L, lua_tonumber(L, 1)); return 1; } } else { const char *s1 = luaL_check_string(L, 1); char *s2; unsigned long n; luaL_arg_check(L, 2 <= base && base <= 36, 2, "base out of range"); n = strtoul(s1, &s2, base); if (s1 != s2) { /* at least one valid digit? */ while (isspace((unsigned char)*s2)) s2++; /* skip trailing spaces */ if (*s2 == '\0') { /* no invalid trailing characters? */ lua_pushnumber(L, n); return 1; } } } lua_pushnil(L); /* else not a number */ return 1; } static int luaB_error (lua_State *L) { lua_error(L, luaL_opt_string(L, 1, NULL)); return 0; /* to avoid warnings */ } static int luaB_setglobal (lua_State *L) { luaL_checkany(L, 2); lua_setglobal(L, luaL_check_string(L, 1)); return 0; } static int luaB_getglobal (lua_State *L) { lua_getglobal(L, luaL_check_string(L, 1)); return 1; } static int luaB_tag (lua_State *L) { luaL_checkany(L, 1); lua_pushnumber(L, lua_tag(L, 1)); return 1; } static int luaB_settag (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_pushvalue(L, 1); /* push table */ lua_settag(L, luaL_check_int(L, 2)); return 1; /* return table */ } static int luaB_newtag (lua_State *L) { lua_pushnumber(L, lua_newtag(L)); return 1; } static int luaB_copytagmethods (lua_State *L) { lua_pushnumber(L, lua_copytagmethods(L, luaL_check_int(L, 1), luaL_check_int(L, 2))); return 1; } static int luaB_globals (lua_State *L) { lua_getglobals(L); /* value to be returned */ if (!lua_isnull(L, 1)) { luaL_checktype(L, 1, LUA_TTABLE); lua_pushvalue(L, 1); /* new table of globals */ lua_setglobals(L); } return 1; } static int luaB_rawget (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); lua_rawget(L, 1); return 1; } static int luaB_rawset (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); luaL_checkany(L, 3); lua_rawset(L, 1); return 1; } static int luaB_settagmethod (lua_State *L) { int tag = luaL_check_int(L, 1); const char *event = luaL_check_string(L, 2); luaL_arg_check(L, lua_isfunction(L, 3) || lua_isnil(L, 3), 3, "function or nil expected"); if (strcmp(event, "gc") == 0) lua_error(L, "deprecated use: cannot set the `gc' tag method from Lua"); lua_gettagmethod(L, tag, event); lua_pushvalue(L, 3); lua_settagmethod(L, tag, event); return 1; } static int luaB_gettagmethod (lua_State *L) { int tag = luaL_check_int(L, 1); const char *event = luaL_check_string(L, 2); if (strcmp(event, "gc") == 0) lua_error(L, "deprecated use: cannot get the `gc' tag method from Lua"); lua_gettagmethod(L, tag, event); return 1; } static int luaB_gcinfo (lua_State *L) { lua_pushnumber(L, lua_getgccount(L)); lua_pushnumber(L, lua_getgcthreshold(L)); return 2; } static int luaB_collectgarbage (lua_State *L) { lua_setgcthreshold(L, luaL_opt_int(L, 1, 0)); return 0; } static int luaB_type (lua_State *L) { luaL_checkany(L, 1); lua_pushstring(L, lua_typename(L, lua_type(L, 1))); return 1; } static int luaB_next (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); /* create a 2nd argument if there isn't one */ if (lua_next(L, 1)) return 2; else { lua_pushnil(L); return 1; } } static int passresults (lua_State *L, int status, int oldtop) { static const char *const errornames[] = {"ok", "run-time error", "file error", "syntax error", "memory error", "error in error handling"}; if (status == 0) { int nresults = lua_gettop(L) - oldtop; if (nresults > 0) return nresults; /* results are already on the stack */ else { lua_pushuserdata(L, NULL); /* at least one result to signal no errors */ return 1; } } else { /* error */ lua_pushnil(L); lua_pushstring(L, errornames[status]); /* error code */ return 2; } } static int luaB_dostring (lua_State *L) { int oldtop = lua_gettop(L); size_t l; const char *s = luaL_check_lstr(L, 1, &l); if (*s == '\33') /* binary files start with ESC... */ lua_error(L, "`dostring' cannot run pre-compiled code"); return passresults(L, lua_dobuffer(L, s, l, luaL_opt_string(L, 2, s)), oldtop); } static int luaB_dofile (lua_State *L) { int oldtop = lua_gettop(L); const char *fname = luaL_opt_string(L, 1, NULL); return passresults(L, lua_dofile(L, fname), oldtop); } static int luaB_call (lua_State *L) { int oldtop; const char *options = luaL_opt_string(L, 3, ""); int err = 0; /* index of old error method */ int i, status; int n; luaL_checktype(L, 2, LUA_TTABLE); n = lua_getn(L, 2); if (!lua_isnull(L, 4)) { /* set new error method */ lua_getglobal(L, LUA_ERRORMESSAGE); err = lua_gettop(L); /* get index */ lua_pushvalue(L, 4); lua_setglobal(L, LUA_ERRORMESSAGE); } oldtop = lua_gettop(L); /* top before function-call preparation */ /* push function */ lua_pushvalue(L, 1); luaL_checkstack(L, n, "too many arguments"); for (i=0; i=pos; n--) { lua_rawgeti(L, 1, n); lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ } lua_pushvalue(L, v); lua_rawseti(L, 1, pos); /* t[pos] = v */ return 0; } static int luaB_tremove (lua_State *L) { int pos, n; luaL_checktype(L, 1, LUA_TTABLE); n = lua_getn(L, 1); pos = luaL_opt_int(L, 2, n); if (n <= 0) return 0; /* table is "empty" */ lua_rawgeti(L, 1, pos); /* result = t[pos] */ for ( ;pos= P */ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { if (i>u) lua_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[i] */ } /* repeat --j until a[j] <= P */ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { if (jt.token); } /* ** Returns the the previous instruction, for optimizations. ** If there is a jump target between this and the current instruction, ** returns a dummy instruction to avoid wrong optimizations. */ static Instruction previous_instruction (FuncState *fs) { if (fs->pc > fs->lasttarget) /* no jumps to current position? */ return fs->f->code[fs->pc-1]; /* returns previous instruction */ else return CREATE_0(OP_END); /* no optimizations after an `END' */ } int luaK_jump (FuncState *fs) { int j = luaK_code1(fs, OP_JMP, NO_JUMP); if (j == fs->lasttarget) { /* possible jumps to this jump? */ luaK_concat(fs, &j, fs->jlt); /* keep them on hold */ fs->jlt = NO_JUMP; } return j; } static void luaK_fixjump (FuncState *fs, int pc, int dest) { Instruction *jmp = &fs->f->code[pc]; if (dest == NO_JUMP) SETARG_S(*jmp, NO_JUMP); /* point to itself to represent end of list */ else { /* jump is relative to position following jump instruction */ int offset = dest-(pc+1); if (abs(offset) > MAXARG_S) luaK_error(fs->ls, "control structure too long"); SETARG_S(*jmp, offset); } } static int luaK_getjump (FuncState *fs, int pc) { int offset = GETARG_S(fs->f->code[pc]); if (offset == NO_JUMP) /* point to itself represents end of list */ return NO_JUMP; /* end of list */ else return (pc+1)+offset; /* turn offset into absolute position */ } /* ** returns current `pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). ** discharge list of jumps to last target. */ int luaK_getlabel (FuncState *fs) { if (fs->pc != fs->lasttarget) { int lasttarget = fs->lasttarget; fs->lasttarget = fs->pc; luaK_patchlist(fs, fs->jlt, lasttarget); /* discharge old list `jlt' */ fs->jlt = NO_JUMP; /* nobody jumps to this new label (yet) */ } return fs->pc; } void luaK_deltastack (FuncState *fs, int delta) { fs->stacklevel += delta; if (fs->stacklevel > fs->f->maxstacksize) { if (fs->stacklevel > MAXSTACK) luaK_error(fs->ls, "function or expression too complex"); fs->f->maxstacksize = fs->stacklevel; } } void luaK_kstr (LexState *ls, int c) { luaK_code1(ls->fs, OP_PUSHSTRING, c); } static int number_constant (FuncState *fs, Number r) { /* check whether `r' has appeared within the last LOOKBACKNUMS entries */ Proto *f = fs->f; int c = f->nknum; int lim = c < LOOKBACKNUMS ? 0 : c-LOOKBACKNUMS; while (--c >= lim) if (f->knum[c] == r) return c; /* not found; create a new entry */ luaM_growvector(fs->L, f->knum, f->nknum, 1, Number, "constant table overflow", MAXARG_U); c = f->nknum++; f->knum[c] = r; return c; } void luaK_number (FuncState *fs, Number f) { if (f <= (Number)MAXARG_S && (Number)(int)f == f) luaK_code1(fs, OP_PUSHINT, (int)f); /* f has a short integer value */ else luaK_code1(fs, OP_PUSHNUM, number_constant(fs, f)); } void luaK_adjuststack (FuncState *fs, int n) { if (n > 0) luaK_code1(fs, OP_POP, n); else luaK_code1(fs, OP_PUSHNIL, -n); } int luaK_lastisopen (FuncState *fs) { /* check whether last instruction is an open function call */ Instruction i = previous_instruction(fs); if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET) return 1; else return 0; } void luaK_setcallreturns (FuncState *fs, int nresults) { if (luaK_lastisopen(fs)) { /* expression is an open function call? */ SETARG_B(fs->f->code[fs->pc-1], nresults); /* set number of results */ luaK_deltastack(fs, nresults); /* push results */ } } static int discharge (FuncState *fs, expdesc *var) { switch (var->k) { case VLOCAL: luaK_code1(fs, OP_GETLOCAL, var->u.index); break; case VGLOBAL: luaK_code1(fs, OP_GETGLOBAL, var->u.index); break; case VINDEXED: luaK_code0(fs, OP_GETTABLE); break; case VEXP: return 0; /* nothing to do */ } var->k = VEXP; var->u.l.t = var->u.l.f = NO_JUMP; return 1; } static void discharge1 (FuncState *fs, expdesc *var) { discharge(fs, var); /* if it has jumps then it is already discharged */ if (var->u.l.t == NO_JUMP && var->u.l.f == NO_JUMP) luaK_setcallreturns(fs, 1); /* call must return 1 value */ } void luaK_storevar (LexState *ls, const expdesc *var) { FuncState *fs = ls->fs; switch (var->k) { case VLOCAL: luaK_code1(fs, OP_SETLOCAL, var->u.index); break; case VGLOBAL: luaK_code1(fs, OP_SETGLOBAL, var->u.index); break; case VINDEXED: /* table is at top-3; pop 3 elements after operation */ luaK_code2(fs, OP_SETTABLE, 3, 3); break; default: LUA_INTERNALERROR("invalid var kind to store"); } } static OpCode invertjump (OpCode op) { switch (op) { case OP_JMPNE: return OP_JMPEQ; case OP_JMPEQ: return OP_JMPNE; case OP_JMPLT: return OP_JMPGE; case OP_JMPLE: return OP_JMPGT; case OP_JMPGT: return OP_JMPLE; case OP_JMPGE: return OP_JMPLT; case OP_JMPT: case OP_JMPONT: return OP_JMPF; case OP_JMPF: case OP_JMPONF: return OP_JMPT; default: LUA_INTERNALERROR("invalid jump instruction"); return OP_END; /* to avoid warnings */ } } static void luaK_patchlistaux (FuncState *fs, int list, int target, OpCode special, int special_target) { Instruction *code = fs->f->code; while (list != NO_JUMP) { int next = luaK_getjump(fs, list); Instruction *i = &code[list]; OpCode op = GET_OPCODE(*i); if (op == special) /* this `op' already has a value */ luaK_fixjump(fs, list, special_target); else { luaK_fixjump(fs, list, target); /* do the patch */ if (op == OP_JMPONT) /* remove eventual values */ SET_OPCODE(*i, OP_JMPT); else if (op == OP_JMPONF) SET_OPCODE(*i, OP_JMPF); } list = next; } } void luaK_patchlist (FuncState *fs, int list, int target) { if (target == fs->lasttarget) /* same target that list `jlt'? */ luaK_concat(fs, &fs->jlt, list); /* delay fixing */ else luaK_patchlistaux(fs, list, target, OP_END, 0); } static int need_value (FuncState *fs, int list, OpCode hasvalue) { /* check whether list has a jump without a value */ for (; list != NO_JUMP; list = luaK_getjump(fs, list)) if (GET_OPCODE(fs->f->code[list]) != hasvalue) return 1; return 0; /* not found */ } void luaK_concat (FuncState *fs, int *l1, int l2) { if (*l1 == NO_JUMP) *l1 = l2; else { int list = *l1; for (;;) { /* traverse `l1' */ int next = luaK_getjump(fs, list); if (next == NO_JUMP) { /* end of list? */ luaK_fixjump(fs, list, l2); return; } list = next; } } } static void luaK_testgo (FuncState *fs, expdesc *v, int invert, OpCode jump) { int prevpos; /* position of last instruction */ Instruction *previous; int *golist, *exitlist; if (!invert) { golist = &v->u.l.f; /* go if false */ exitlist = &v->u.l.t; /* exit if true */ } else { golist = &v->u.l.t; /* go if true */ exitlist = &v->u.l.f; /* exit if false */ } discharge1(fs, v); prevpos = fs->pc-1; previous = &fs->f->code[prevpos]; LUA_ASSERT(*previous==previous_instruction(fs), "no jump allowed here"); if (!ISJUMP(GET_OPCODE(*previous))) prevpos = luaK_code1(fs, jump, NO_JUMP); else { /* last instruction is already a jump */ if (invert) SET_OPCODE(*previous, (OpCode)invertjump(GET_OPCODE(*previous))); } luaK_concat(fs, exitlist, prevpos); /* insert last jump in `exitlist' */ luaK_patchlist(fs, *golist, luaK_getlabel(fs)); *golist = NO_JUMP; } void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue) { luaK_testgo(fs, v, 1, keepvalue ? OP_JMPONF : OP_JMPF); } static void luaK_goiffalse (FuncState *fs, expdesc *v, int keepvalue) { luaK_testgo(fs, v, 0, keepvalue ? OP_JMPONT : OP_JMPT); } static int code_label (FuncState *fs, OpCode op, int arg) { luaK_getlabel(fs); /* those instructions may be jump targets */ return luaK_code1(fs, op, arg); } void luaK_tostack (LexState *ls, expdesc *v, int onlyone) { FuncState *fs = ls->fs; if (!discharge(fs, v)) { /* `v' is an expression? */ OpCode previous = GET_OPCODE(fs->f->code[fs->pc-1]); if (!ISJUMP(previous) && v->u.l.f == NO_JUMP && v->u.l.t == NO_JUMP) { /* expression has no jumps */ if (onlyone) luaK_setcallreturns(fs, 1); /* call must return 1 value */ } else { /* expression has jumps */ int final; /* position after whole expression */ int j = NO_JUMP; /* eventual jump over values */ int p_nil = NO_JUMP; /* position of an eventual PUSHNIL */ int p_1 = NO_JUMP; /* position of an eventual PUSHINT */ if (ISJUMP(previous) || need_value(fs, v->u.l.f, OP_JMPONF) || need_value(fs, v->u.l.t, OP_JMPONT)) { /* expression needs values */ if (ISJUMP(previous)) luaK_concat(fs, &v->u.l.t, fs->pc-1); /* put `previous' in t. list */ else { j = code_label(fs, OP_JMP, NO_JUMP); /* to jump over both pushes */ /* correct stack for compiler and symbolic execution */ luaK_adjuststack(fs, 1); } p_nil = code_label(fs, OP_PUSHNILJMP, 0); p_1 = code_label(fs, OP_PUSHINT, 1); luaK_patchlist(fs, j, luaK_getlabel(fs)); } final = luaK_getlabel(fs); luaK_patchlistaux(fs, v->u.l.f, p_nil, OP_JMPONF, final); luaK_patchlistaux(fs, v->u.l.t, p_1, OP_JMPONT, final); v->u.l.f = v->u.l.t = NO_JUMP; } } } void luaK_prefix (LexState *ls, UnOpr op, expdesc *v) { FuncState *fs = ls->fs; if (op == OPR_MINUS) { luaK_tostack(ls, v, 1); luaK_code0(fs, OP_MINUS); } else { /* op == NOT */ Instruction *previous; discharge1(fs, v); previous = &fs->f->code[fs->pc-1]; if (ISJUMP(GET_OPCODE(*previous))) SET_OPCODE(*previous, (OpCode)invertjump(GET_OPCODE(*previous))); else luaK_code0(fs, OP_NOT); /* interchange true and false lists */ { int temp = v->u.l.f; v->u.l.f = v->u.l.t; v->u.l.t = temp; } } } void luaK_infix (LexState *ls, BinOpr op, expdesc *v) { FuncState *fs = ls->fs; switch (op) { case OPR_AND: luaK_goiftrue(fs, v, 1); break; case OPR_OR: luaK_goiffalse(fs, v, 1); break; default: luaK_tostack(ls, v, 1); /* all other binary operators need a value */ } } static const struct { OpCode opcode; /* opcode for each binary operator */ int arg; /* default argument for the opcode */ } codes[] = { /* ORDER OPR */ {OP_ADD, 0}, {OP_SUB, 0}, {OP_MULT, 0}, {OP_DIV, 0}, {OP_POW, 0}, {OP_CONCAT, 2}, {OP_JMPNE, NO_JUMP}, {OP_JMPEQ, NO_JUMP}, {OP_JMPLT, NO_JUMP}, {OP_JMPLE, NO_JUMP}, {OP_JMPGT, NO_JUMP}, {OP_JMPGE, NO_JUMP} }; void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2) { FuncState *fs = ls->fs; switch (op) { case OPR_AND: { LUA_ASSERT(v1->u.l.t == NO_JUMP, "list must be closed"); discharge1(fs, v2); v1->u.l.t = v2->u.l.t; luaK_concat(fs, &v1->u.l.f, v2->u.l.f); break; } case OPR_OR: { LUA_ASSERT(v1->u.l.f == NO_JUMP, "list must be closed"); discharge1(fs, v2); v1->u.l.f = v2->u.l.f; luaK_concat(fs, &v1->u.l.t, v2->u.l.t); break; } default: { luaK_tostack(ls, v2, 1); /* `v2' must be a value */ luaK_code1(fs, codes[op].opcode, codes[op].arg); } } } static void codelineinfo (FuncState *fs) { Proto *f = fs->f; LexState *ls = fs->ls; if (ls->lastline > fs->lastline) { luaM_growvector(fs->L, f->lineinfo, f->nlineinfo, 2, int, "line info overflow", MAX_INT); if (ls->lastline > fs->lastline+1) f->lineinfo[f->nlineinfo++] = -(ls->lastline - (fs->lastline+1)); f->lineinfo[f->nlineinfo++] = fs->pc; fs->lastline = ls->lastline; } } int luaK_code0 (FuncState *fs, OpCode o) { return luaK_code2(fs, o, 0, 0); } int luaK_code1 (FuncState *fs, OpCode o, int arg1) { return luaK_code2(fs, o, arg1, 0); } int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) { Instruction i = previous_instruction(fs); int delta = luaK_opproperties[o].push - luaK_opproperties[o].pop; int optm = 0; /* 1 when there is an optimization */ switch (o) { case OP_CLOSURE: { delta = -arg2+1; break; } case OP_SETTABLE: { delta = -arg2; break; } case OP_SETLIST: { if (arg2 == 0) return NO_JUMP; /* nothing to do */ delta = -arg2; break; } case OP_SETMAP: { if (arg1 == 0) return NO_JUMP; /* nothing to do */ delta = -2*arg1; break; } case OP_RETURN: { if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET) { SET_OPCODE(i, OP_TAILCALL); SETARG_B(i, arg1); optm = 1; } break; } case OP_PUSHNIL: { if (arg1 == 0) return NO_JUMP; /* nothing to do */ delta = arg1; switch(GET_OPCODE(i)) { case OP_PUSHNIL: SETARG_U(i, GETARG_U(i)+arg1); optm = 1; break; default: break; } break; } case OP_POP: { if (arg1 == 0) return NO_JUMP; /* nothing to do */ delta = -arg1; switch(GET_OPCODE(i)) { case OP_SETTABLE: SETARG_B(i, GETARG_B(i)+arg1); optm = 1; break; default: break; } break; } case OP_GETTABLE: { switch(GET_OPCODE(i)) { case OP_PUSHSTRING: /* `t.x' */ SET_OPCODE(i, OP_GETDOTTED); optm = 1; break; case OP_GETLOCAL: /* `t[i]' */ SET_OPCODE(i, OP_GETINDEXED); optm = 1; break; default: break; } break; } case OP_ADD: { switch(GET_OPCODE(i)) { case OP_PUSHINT: SET_OPCODE(i, OP_ADDI); optm = 1; break; /* `a+k' */ default: break; } break; } case OP_SUB: { switch(GET_OPCODE(i)) { case OP_PUSHINT: /* `a-k' */ i = CREATE_S(OP_ADDI, -GETARG_S(i)); optm = 1; break; default: break; } break; } case OP_CONCAT: { delta = -arg1+1; switch(GET_OPCODE(i)) { case OP_CONCAT: /* `a..b..c' */ SETARG_U(i, GETARG_U(i)+1); optm = 1; break; default: break; } break; } case OP_MINUS: { switch(GET_OPCODE(i)) { case OP_PUSHINT: /* `-k' */ SETARG_S(i, -GETARG_S(i)); optm = 1; break; case OP_PUSHNUM: /* `-k' */ SET_OPCODE(i, OP_PUSHNEGNUM); optm = 1; break; default: break; } break; } case OP_JMPNE: { if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a~=nil' */ i = CREATE_S(OP_JMPT, NO_JUMP); optm = 1; } break; } case OP_JMPEQ: { if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a==nil' */ i = CREATE_0(OP_NOT); delta = -1; /* just undo effect of previous PUSHNIL */ optm = 1; } break; } case OP_JMPT: case OP_JMPONT: { switch (GET_OPCODE(i)) { case OP_NOT: { i = CREATE_S(OP_JMPF, NO_JUMP); optm = 1; break; } case OP_PUSHINT: { if (o == OP_JMPT) { /* JMPONT must keep original integer value */ i = CREATE_S(OP_JMP, NO_JUMP); optm = 1; } break; } case OP_PUSHNIL: { if (GETARG_U(i) == 1) { fs->pc--; /* erase previous instruction */ luaK_deltastack(fs, -1); /* correct stack */ return NO_JUMP; } break; } default: break; } break; } case OP_JMPF: case OP_JMPONF: { switch (GET_OPCODE(i)) { case OP_NOT: { i = CREATE_S(OP_JMPT, NO_JUMP); optm = 1; break; } case OP_PUSHINT: { /* `while 1 do ...' */ fs->pc--; /* erase previous instruction */ luaK_deltastack(fs, -1); /* correct stack */ return NO_JUMP; } case OP_PUSHNIL: { /* `repeat ... until nil' */ if (GETARG_U(i) == 1) { i = CREATE_S(OP_JMP, NO_JUMP); optm = 1; } break; } default: break; } break; } case OP_GETDOTTED: case OP_GETINDEXED: case OP_TAILCALL: case OP_ADDI: { LUA_INTERNALERROR("instruction used only for optimizations"); break; } default: { LUA_ASSERT(delta != VD, "invalid delta"); break; } } luaK_deltastack(fs, delta); if (optm) { /* optimize: put instruction in place of last one */ fs->f->code[fs->pc-1] = i; /* change previous instruction */ return fs->pc-1; /* do not generate new instruction */ } /* else build new instruction */ switch ((enum Mode)luaK_opproperties[o].mode) { case iO: i = CREATE_0(o); break; case iU: i = CREATE_U(o, arg1); break; case iS: i = CREATE_S(o, arg1); break; case iAB: i = CREATE_AB(o, arg1, arg2); break; } codelineinfo(fs); /* put new instruction in code array */ luaM_growvector(fs->L, fs->f->code, fs->pc, 1, Instruction, "code size overflow", MAX_INT); fs->f->code[fs->pc] = i; return fs->pc++; } const struct OpProperties luaK_opproperties[NUM_OPCODES] = { {iO, 0, 0}, /* OP_END */ {iU, 0, 0}, /* OP_RETURN */ {iAB, 0, 0}, /* OP_CALL */ {iAB, 0, 0}, /* OP_TAILCALL */ {iU, VD, 0}, /* OP_PUSHNIL */ {iU, VD, 0}, /* OP_POP */ {iS, 1, 0}, /* OP_PUSHINT */ {iU, 1, 0}, /* OP_PUSHSTRING */ {iU, 1, 0}, /* OP_PUSHNUM */ {iU, 1, 0}, /* OP_PUSHNEGNUM */ {iU, 1, 0}, /* OP_PUSHUPVALUE */ {iU, 1, 0}, /* OP_GETLOCAL */ {iU, 1, 0}, /* OP_GETGLOBAL */ {iO, 1, 2}, /* OP_GETTABLE */ {iU, 1, 1}, /* OP_GETDOTTED */ {iU, 1, 1}, /* OP_GETINDEXED */ {iU, 2, 1}, /* OP_PUSHSELF */ {iU, 1, 0}, /* OP_CREATETABLE */ {iU, 0, 1}, /* OP_SETLOCAL */ {iU, 0, 1}, /* OP_SETGLOBAL */ {iAB, VD, 0}, /* OP_SETTABLE */ {iAB, VD, 0}, /* OP_SETLIST */ {iU, VD, 0}, /* OP_SETMAP */ {iO, 1, 2}, /* OP_ADD */ {iS, 1, 1}, /* OP_ADDI */ {iO, 1, 2}, /* OP_SUB */ {iO, 1, 2}, /* OP_MULT */ {iO, 1, 2}, /* OP_DIV */ {iO, 1, 2}, /* OP_POW */ {iU, VD, 0}, /* OP_CONCAT */ {iO, 1, 1}, /* OP_MINUS */ {iO, 1, 1}, /* OP_NOT */ {iS, 0, 2}, /* OP_JMPNE */ {iS, 0, 2}, /* OP_JMPEQ */ {iS, 0, 2}, /* OP_JMPLT */ {iS, 0, 2}, /* OP_JMPLE */ {iS, 0, 2}, /* OP_JMPGT */ {iS, 0, 2}, /* OP_JMPGE */ {iS, 0, 1}, /* OP_JMPT */ {iS, 0, 1}, /* OP_JMPF */ {iS, 0, 1}, /* OP_JMPONT */ {iS, 0, 1}, /* OP_JMPONF */ {iS, 0, 0}, /* OP_JMP */ {iO, 0, 0}, /* OP_PUSHNILJMP */ {iS, 0, 0}, /* OP_FORPREP */ {iS, 0, 3}, /* OP_FORLOOP */ {iS, 2, 0}, /* OP_LFORPREP */ {iS, 0, 3}, /* OP_LFORLOOP */ {iAB, VD, 0} /* OP_CLOSURE */ }; zangband/src/lua/ldblib.c0000644000000000000000000001021010250356275014303 0ustar rootroot/* ** $Id: ldblib.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ #include #include #include #include "lua.h" #include "lauxlib.h" #include "luadebug.h" #include "lualib.h" static void settabss (lua_State *L, const char *i, const char *v) { lua_pushstring(L, i); lua_pushstring(L, v); lua_settable(L, -3); } static void settabsi (lua_State *L, const char *i, int v) { lua_pushstring(L, i); lua_pushnumber(L, v); lua_settable(L, -3); } static int getinfo (lua_State *L) { lua_Debug ar; const char *options = luaL_opt_string(L, 2, "flnSu"); char buff[20]; if (lua_isnumber(L, 1)) { if (!lua_getstack(L, (int)lua_tonumber(L, 1), &ar)) { lua_pushnil(L); /* level out of range */ return 1; } } else if (lua_isfunction(L, 1)) { lua_pushvalue(L, 1); sprintf(buff, ">%.10s", options); options = buff; } else luaL_argerror(L, 1, "function or level expected"); if (!lua_getinfo(L, options, &ar)) luaL_argerror(L, 2, "invalid option"); lua_newtable(L); for (; *options; options++) { switch (*options) { case 'S': settabss(L, "source", ar.source); if (ar.source) settabss(L, "short_src", ar.short_src); settabsi(L, "linedefined", ar.linedefined); settabss(L, "what", ar.what); break; case 'l': settabsi(L, "currentline", ar.currentline); break; case 'u': settabsi(L, "nups", ar.nups); break; case 'n': settabss(L, "name", ar.name); settabss(L, "namewhat", ar.namewhat); break; case 'f': lua_pushstring(L, "func"); lua_pushvalue(L, -3); lua_settable(L, -3); break; } } return 1; /* return table */ } static int getlocal (lua_State *L) { lua_Debug ar; const char *name; if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */ luaL_argerror(L, 1, "level out of range"); name = lua_getlocal(L, &ar, luaL_check_int(L, 2)); if (name) { lua_pushstring(L, name); lua_pushvalue(L, -2); return 2; } else { lua_pushnil(L); return 1; } } static int setlocal (lua_State *L) { lua_Debug ar; if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */ luaL_argerror(L, 1, "level out of range"); luaL_checkany(L, 3); lua_pushstring(L, lua_setlocal(L, &ar, luaL_check_int(L, 2))); return 1; } /* dummy variables (to define unique addresses) */ static char key1, key2; #define KEY_CALLHOOK (&key1) #define KEY_LINEHOOK (&key2) static void hookf (lua_State *L, void *key) { lua_getregistry(L); lua_pushuserdata(L, key); lua_gettable(L, -2); if (lua_isfunction(L, -1)) { lua_pushvalue(L, 1); lua_rawcall(L, 1, 0); } else lua_pop(L, 1); /* pop result from gettable */ lua_pop(L, 1); /* pop table */ } static void callf (lua_State *L, lua_Debug *ar) { lua_pushstring(L, ar->event); hookf(L, KEY_CALLHOOK); } static void linef (lua_State *L, lua_Debug *ar) { lua_pushnumber(L, ar->currentline); hookf(L, KEY_LINEHOOK); } static void sethook (lua_State *L, void *key, lua_Hook hook, lua_Hook (*sethookf)(lua_State * L, lua_Hook h)) { lua_settop(L, 1); if (lua_isnil(L, 1)) (*sethookf)(L, NULL); else if (lua_isfunction(L, 1)) (*sethookf)(L, hook); else luaL_argerror(L, 1, "function expected"); lua_getregistry(L); lua_pushuserdata(L, key); lua_pushvalue(L, -1); /* dup key */ lua_gettable(L, -3); /* get old value */ lua_pushvalue(L, -2); /* key (again) */ lua_pushvalue(L, 1); lua_settable(L, -5); /* set new value */ } static int setcallhook (lua_State *L) { sethook(L, KEY_CALLHOOK, callf, lua_setcallhook); return 1; } static int setlinehook (lua_State *L) { sethook(L, KEY_LINEHOOK, linef, lua_setlinehook); return 1; } static const struct luaL_reg dblib[] = { {"getlocal", getlocal}, {"getinfo", getinfo}, {"setcallhook", setcallhook}, {"setlinehook", setlinehook}, {"setlocal", setlocal} }; LUALIB_API void lua_dblibopen (lua_State *L) { luaL_openl(L, dblib); } zangband/src/lua/ldebug.c0000644000000000000000000002646210250356275014335 0ustar rootroot/* ** $Id: ldebug.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Debug Interface ** See Copyright Notice in lua.h */ #include #include "lua.h" #include "lapi.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "luadebug.h" static const char *getfuncname (lua_State *L, StkId f, const char **name); static void setnormalized (TObject *d, const TObject *s) { if (ttype(s) == LUA_TMARK) { clvalue(d) = infovalue(s)->func; ttype(d) = LUA_TFUNCTION; } else *d = *s; } static int isLmark (StkId o) { return (o && ttype(o) == LUA_TMARK && !infovalue(o)->func->isC); } LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func) { lua_Hook oldhook = L->callhook; L->callhook = func; return oldhook; } LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func) { lua_Hook oldhook = L->linehook; L->linehook = func; return oldhook; } static StkId aux_stackedfunction (lua_State *L, int level, StkId top) { int i; for (i = (top-1) - L->stack; i>=0; i--) { if (is_T_MARK(L->stack[i].ttype)) { if (level == 0) return L->stack+i; level--; } } return NULL; } LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { StkId f = aux_stackedfunction(L, level, L->top); if (f == NULL) return 0; /* there is no such level */ else { ar->_func = f; return 1; } } static int nups (StkId f) { switch (ttype(f)) { case LUA_TFUNCTION: return clvalue(f)->nupvalues; case LUA_TMARK: return infovalue(f)->func->nupvalues; default: return 0; } } int luaG_getline (int *lineinfo, int pc, int refline, int *prefi) { int refi; if (lineinfo == NULL || pc == -1) return -1; /* no line info or function is not active */ refi = prefi ? *prefi : 0; if (lineinfo[refi] < 0) refline += -lineinfo[refi++]; LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info"); while (lineinfo[refi] > pc) { refline--; refi--; if (lineinfo[refi] < 0) refline -= -lineinfo[refi--]; LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info"); } for (;;) { int nextline = refline + 1; int nextref = refi + 1; if (lineinfo[nextref] < 0) nextline += -lineinfo[nextref++]; LUA_ASSERT(lineinfo[nextref] >= 0, "invalid line info"); if (lineinfo[nextref] > pc) break; refline = nextline; refi = nextref; } if (prefi) *prefi = refi; return refline; } static int currentpc (StkId f) { CallInfo *ci = infovalue(f); LUA_ASSERT(isLmark(f), "function has no pc"); if (ci->pc) return (*ci->pc - ci->func->f.l->code) - 1; else return -1; /* function is not active */ } static int currentline (StkId f) { if (!isLmark(f)) return -1; /* only active lua functions have current-line information */ else { CallInfo *ci = infovalue(f); int *lineinfo = ci->func->f.l->lineinfo; return luaG_getline(lineinfo, currentpc(f), 1, NULL); } } static Proto *getluaproto (StkId f) { return (isLmark(f) ? infovalue(f)->func->f.l : NULL); } LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; StkId f = ar->_func; Proto *fp = getluaproto(f); if (!fp) return NULL; /* `f' is not a Lua function? */ name = luaF_getlocalname(fp, n, currentpc(f)); if (!name) return NULL; luaA_pushobject(L, (f+1)+(n-1)); /* push value */ return name; } LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; StkId f = ar->_func; Proto *fp = getluaproto(f); L->top--; /* pop new value */ if (!fp) return NULL; /* `f' is not a Lua function? */ name = luaF_getlocalname(fp, n, currentpc(f)); if (!name || name[0] == '(') return NULL; /* `(' starts private locals */ *((f+1)+(n-1)) = *L->top; return name; } static void infoLproto (lua_Debug *ar, Proto *f) { ar->source = f->source->str; ar->linedefined = f->lineDefined; ar->what = "Lua"; } static void funcinfo (lua_State *L, lua_Debug *ar, StkId func) { Closure *cl = NULL; switch (ttype(func)) { case LUA_TFUNCTION: cl = clvalue(func); break; case LUA_TMARK: cl = infovalue(func)->func; break; default: lua_error(L, "value for `lua_getinfo' is not a function"); } if (cl->isC) { ar->source = "=C"; ar->linedefined = -1; ar->what = "C"; } else infoLproto(ar, cl->f.l); luaO_chunkid(ar->short_src, ar->source, sizeof(ar->short_src)); if (ar->linedefined == 0) ar->what = "main"; } static const char *travtagmethods (lua_State *L, const TObject *o) { if (ttype(o) == LUA_TFUNCTION) { int e; for (e=0; elast_tag; t++) if (clvalue(o) == luaT_gettm(L, t, e)) return luaT_eventname[e]; } } return NULL; } static const char *travglobals (lua_State *L, const TObject *o) { Hash *g = L->gt; int i; for (i=0; isize; i++) { if (luaO_equalObj(o, val(node(g, i))) && ttype(key(node(g, i))) == LUA_TSTRING) return tsvalue(key(node(g, i)))->str; } return NULL; } static void getname (lua_State *L, StkId f, lua_Debug *ar) { TObject o; setnormalized(&o, f); /* try to find a name for given function */ if ((ar->name = travglobals(L, &o)) != NULL) ar->namewhat = "global"; /* not found: try tag methods */ else if ((ar->name = travtagmethods(L, &o)) != NULL) ar->namewhat = "tag-method"; else ar->namewhat = ""; /* not found at all */ } LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { StkId func; int isactive = (*what != '>'); if (isactive) func = ar->_func; else { what++; /* skip the '>' */ func = L->top - 1; } for (; *what; what++) { switch (*what) { case 'S': { funcinfo(L, ar, func); break; } case 'l': { ar->currentline = currentline(func); break; } case 'u': { ar->nups = nups(func); break; } case 'n': { ar->namewhat = (isactive) ? getfuncname(L, func, &ar->name) : NULL; if (ar->namewhat == NULL) getname(L, func, ar); break; } case 'f': { setnormalized(L->top, func); incr_top; /* push function */ break; } default: return 0; /* invalid option */ } } if (!isactive) L->top--; /* pop function */ return 1; } /* ** {====================================================== ** Symbolic Execution ** ======================================================= */ static int pushpc (int *stack, int pc, int top, int n) { while (n--) stack[top++] = pc-1; return top; } static Instruction luaG_symbexec (const Proto *pt, int lastpc, int stackpos) { int stack[MAXSTACK]; /* stores last instruction that changed a stack entry */ const Instruction *code = pt->code; int top = pt->numparams; int pc = 0; if (pt->is_vararg) /* varargs? */ top++; /* `arg' */ while (pc < lastpc) { const Instruction i = code[pc++]; LUA_ASSERT(0 <= top && top <= pt->maxstacksize, "wrong stack"); switch (GET_OPCODE(i)) { case OP_RETURN: { LUA_ASSERT(top >= GETARG_U(i), "wrong stack"); top = GETARG_U(i); break; } case OP_TAILCALL: { LUA_ASSERT(top >= GETARG_A(i), "wrong stack"); top = GETARG_B(i); break; } case OP_CALL: { int nresults = GETARG_B(i); if (nresults == MULT_RET) nresults = 1; LUA_ASSERT(top >= GETARG_A(i), "wrong stack"); top = pushpc(stack, pc, GETARG_A(i), nresults); break; } case OP_PUSHNIL: { top = pushpc(stack, pc, top, GETARG_U(i)); break; } case OP_POP: { top -= GETARG_U(i); break; } case OP_SETTABLE: case OP_SETLIST: { top -= GETARG_B(i); break; } case OP_SETMAP: { top -= 2*GETARG_U(i); break; } case OP_CONCAT: { top -= GETARG_U(i); stack[top++] = pc-1; break; } case OP_CLOSURE: { top -= GETARG_B(i); stack[top++] = pc-1; break; } case OP_JMPONT: case OP_JMPONF: { int newpc = pc + GETARG_S(i); /* jump is forward and do not skip `lastpc'? */ if (pc < newpc && newpc <= lastpc) { stack[top-1] = pc-1; /* value comes from `and'/`or' */ pc = newpc; /* do the jump */ } else top--; /* do not jump; pop value */ break; } default: { OpCode op = GET_OPCODE(i); LUA_ASSERT(luaK_opproperties[op].push != VD, "invalid opcode for default"); top -= luaK_opproperties[op].pop; LUA_ASSERT(top >= 0, "wrong stack"); top = pushpc(stack, pc, top, luaK_opproperties[op].push); } } } return code[stack[stackpos]]; } static const char *getobjname (lua_State *L, StkId obj, const char **name) { StkId func = aux_stackedfunction(L, 0, obj); if (!isLmark(func)) return NULL; /* not an active Lua function */ else { Proto *p = infovalue(func)->func->f.l; int pc = currentpc(func); int stackpos = obj - (func+1); /* func+1 == function base */ Instruction i = luaG_symbexec(p, pc, stackpos); LUA_ASSERT(pc != -1, "function must be active"); switch (GET_OPCODE(i)) { case OP_GETGLOBAL: { *name = p->kstr[GETARG_U(i)]->str; return "global"; } case OP_GETLOCAL: { *name = luaF_getlocalname(p, GETARG_U(i)+1, pc); LUA_ASSERT(*name, "local must exist"); return "local"; } case OP_PUSHSELF: case OP_GETDOTTED: { *name = p->kstr[GETARG_U(i)]->str; return "field"; } default: return NULL; /* no useful name found */ } } } static const char *getfuncname (lua_State *L, StkId f, const char **name) { StkId func = aux_stackedfunction(L, 0, f); /* calling function */ if (!isLmark(func)) return NULL; /* not an active Lua function */ else { Proto *p = infovalue(func)->func->f.l; int pc = currentpc(func); Instruction i; if (pc == -1) return NULL; /* function is not activated */ i = p->code[pc]; switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: return getobjname(L, (func+1)+GETARG_A(i), name); default: return NULL; /* no useful name found */ } } } /* }====================================================== */ void luaG_typeerror (lua_State *L, StkId o, const char *op) { const char *name; const char *kind = getobjname(L, o, &name); const char *t = luaO_typename(o); if (kind) luaO_verror(L, "attempt to %.30s %.20s `%.40s' (a %.10s value)", op, kind, name, t); else luaO_verror(L, "attempt to %.30s a %.10s value", op, t); } void luaG_binerror (lua_State *L, StkId p1, int t, const char *op) { if (ttype(p1) == t) p1++; LUA_ASSERT(ttype(p1) != t, "must be an error"); luaG_typeerror(L, p1, op); } void luaG_ordererror (lua_State *L, StkId top) { const char *t1 = luaO_typename(top-2); const char *t2 = luaO_typename(top-1); if (t1[2] == t2[2]) luaO_verror(L, "attempt to compare two %.10s values", t1); else luaO_verror(L, "attempt to compare %.10s with %.10s", t1, t2); } zangband/src/lua/ldo.c0000644000000000000000000002424110250356275013642 0ustar rootroot/* ** $Id: ldo.c,v 1.2 2002/08/29 19:06:49 rr9 Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #include #include #include #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lundump.h" #include "lvm.h" #include "lzio.h" /* space to handle stack overflow errors */ #define EXTRA_STACK (2*LUA_MINSTACK) void luaD_init (lua_State *L, int stacksize) { L->stack = luaM_newvector(L, stacksize+EXTRA_STACK, TObject); L->nblocks += stacksize*sizeof(TObject); L->stack_last = L->stack+(stacksize-1); L->stacksize = stacksize; L->Cbase = L->top = L->stack; } void luaD_checkstack (lua_State *L, int n) { if (L->stack_last - L->top <= n) { /* stack overflow? */ if (L->stack_last-L->stack > (L->stacksize-1)) { /* overflow while handling overflow */ luaD_breakrun(L, LUA_ERRERR); /* break run without error message */ } else { L->stack_last += EXTRA_STACK; /* to be used by error message */ lua_error(L, "stack overflow"); } } } static void restore_stack_limit (lua_State *L) { if (L->top - L->stack < L->stacksize - 1) L->stack_last = L->stack + (L->stacksize-1); } /* ** Adjust stack. Set top to base+extra, pushing NILs if needed. ** (we cannot add base+extra unless we are sure it fits in the stack; ** otherwise the result of such operation on pointers is undefined) */ void luaD_adjusttop (lua_State *L, StkId base, int extra) { int diff = extra-(L->top-base); if (diff <= 0) L->top = base+extra; else { luaD_checkstack(L, diff); while (diff--) ttype(L->top++) = LUA_TNIL; } } /* ** Open a hole inside the stack at `pos' */ static void luaD_openstack (lua_State *L, StkId pos) { int i = L->top-pos; while (i--) pos[i+1] = pos[i]; incr_top; } static void dohook (lua_State *L, lua_Debug *ar, lua_Hook hook) { StkId old_Cbase = L->Cbase; StkId old_top = L->Cbase = L->top; luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ L->allowhooks = 0; /* cannot call hooks inside a hook */ (*hook)(L, ar); LUA_ASSERT(L->allowhooks == 0, "invalid allow"); L->allowhooks = 1; L->top = old_top; L->Cbase = old_Cbase; } void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook) { if (L->allowhooks) { lua_Debug ar; ar._func = func; ar.event = "line"; ar.currentline = line; dohook(L, &ar, linehook); } } static void luaD_callHook (lua_State *L, StkId func, lua_Hook callhook, const char *event) { if (L->allowhooks) { lua_Debug ar; ar._func = func; ar.event = event; infovalue(func)->pc = NULL; /* function is not active */ dohook(L, &ar, callhook); } } static StkId callCclosure (lua_State *L, const struct Closure *cl, StkId base) { int nup = cl->nupvalues; /* number of upvalues */ StkId old_Cbase = L->Cbase; int n; L->Cbase = base; /* new base for C function */ luaD_checkstack(L, nup+LUA_MINSTACK); /* ensure minimum stack size */ for (n=0; ntop++) = cl->upvalue[n]; n = (*cl->f.c)(L); /* do the actual call */ L->Cbase = old_Cbase; /* restore old C base */ return L->top - n; /* return index of first result */ } void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults) { StkId base = L->top - nParams; luaD_openstack(L, base); clvalue(base) = f; ttype(base) = LUA_TFUNCTION; luaD_call(L, base, nResults); } /* ** Call a function (C or Lua). The function to be called is at *func. ** The arguments are on the stack, right after the function. ** When returns, the results are on the stack, starting at the original ** function position. ** The number of results is nResults, unless nResults=LUA_MULTRET. */ void luaD_call (lua_State *L, StkId func, int nResults) { lua_Hook callhook; StkId firstResult; CallInfo ci; Closure *cl; if (ttype(func) != LUA_TFUNCTION) { /* `func' is not a function; check the `function' tag method */ Closure *tm = luaT_gettmbyObj(L, func, TM_FUNCTION); if (tm == NULL) luaG_typeerror(L, func, "call"); luaD_openstack(L, func); clvalue(func) = tm; /* tag method is the new function to be called */ ttype(func) = LUA_TFUNCTION; } cl = clvalue(func); ci.func = cl; infovalue(func) = &ci; ttype(func) = LUA_TMARK; callhook = L->callhook; if (callhook) luaD_callHook(L, func, callhook, "call"); firstResult = (cl->isC ? callCclosure(L, cl, func+1) : luaV_execute(L, cl, func+1)); if (callhook) /* same hook that was active at entry */ luaD_callHook(L, func, callhook, "return"); LUA_ASSERT(ttype(func) == LUA_TMARK, "invalid tag"); /* move results to `func' (to erase parameters and function) */ if (nResults == LUA_MULTRET) { while (firstResult < L->top) /* copy all results */ *func++ = *firstResult++; L->top = func; } else { /* copy at most `nResults' */ for (; nResults > 0 && firstResult < L->top; nResults--) *func++ = *firstResult++; L->top = func; for (; nResults > 0; nResults--) { /* if there are not enough results */ ttype(L->top) = LUA_TNIL; /* adjust the stack */ incr_top; /* must check stack space */ } } luaC_checkGC(L); } /* ** Execute a protected call. */ struct CallS { /* data to `f_call' */ StkId func; int nresults; }; static void f_call (lua_State *L, void *ud) { struct CallS *c = (struct CallS *)ud; luaD_call(L, c->func, c->nresults); } LUA_API int lua_call (lua_State *L, int nargs, int nresults) { StkId func = L->top - (nargs+1); /* function to be called */ struct CallS c; int status; c.func = func; c.nresults = nresults; status = luaD_runprotected(L, f_call, &c); if (status != 0) /* an error occurred? */ L->top = func; /* remove parameters from the stack */ return status; } /* ** Execute a protected parser. */ struct ParserS { /* data to `f_parser' */ ZIO *z; int bin; }; static void f_parser (lua_State *L, void *ud) { struct ParserS *p = (struct ParserS *)ud; Proto *tf = p->bin ? luaU_undump(L, p->z) : luaY_parser(L, p->z); luaV_Lclosure(L, tf, 0); } static int protectedparser (lua_State *L, ZIO *z, int bin) { struct ParserS p; unsigned long old_blocks; int status; p.z = z; p.bin = bin; /* before parsing, give a (good) chance to GC */ if (L->nblocks/8 >= L->GCthreshold/10) luaC_collectgarbage(L); old_blocks = L->nblocks; status = luaD_runprotected(L, f_parser, &p); if (status == 0) { /* add new memory to threshold (as it probably will stay) */ L->GCthreshold += (L->nblocks - old_blocks); } else if (status == LUA_ERRRUN) /* an error occurred: correct error code */ status = LUA_ERRSYNTAX; return status; } static int parse_file (lua_State *L, const char *filename) { ZIO z; int status; int bin; /* flag for file mode */ int c; /* look ahead char */ FILE *f = (filename == NULL) ? stdin : fopen(filename, "r"); if (f == NULL) return LUA_ERRFILE; /* unable to open file */ c = fgetc(f); ungetc(c, f); bin = (c == ID_CHUNK); if (bin && f != stdin) { f = freopen(filename, "rb", f); /* set binary mode */ if (f == NULL) return LUA_ERRFILE; /* unable to reopen file */ } lua_pushstring(L, "@"); lua_pushstring(L, (filename == NULL) ? "(stdin)" : filename); lua_concat(L, 2); c = lua_gettop(L); filename = lua_tostring(L, c); /* filename = '@'..filename */ luaZ_Fopen(&z, f, filename); status = protectedparser(L, &z, bin); lua_remove(L, c); /* remove `filename' from the stack */ if (f != stdin) fclose(f); return status; } LUA_API int lua_dofile (lua_State *L, const char *filename) { int status = parse_file(L, filename); if (status == 0) /* parse OK? */ status = lua_call(L, 0, LUA_MULTRET); /* call main */ return status; } static int parse_buffer (lua_State *L, const char *buff, size_t size, const char *name) { ZIO z; if (!name) name = "?"; luaZ_mopen(&z, buff, size, name); return protectedparser(L, &z, buff[0]==ID_CHUNK); } LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name) { int status = parse_buffer(L, buff, size, name); if (status == 0) /* parse OK? */ status = lua_call(L, 0, LUA_MULTRET); /* call main */ return status; } LUA_API int lua_dostring (lua_State *L, const char *str) { return lua_dobuffer(L, str, strlen(str), str); } /* ** {====================================================== ** Error-recover functions (based on long jumps) ** ======================================================= */ /* chain list of long jump buffers */ struct lua_longjmp { jmp_buf b; struct lua_longjmp *previous; volatile int status; /* error code */ }; static void message (lua_State *L, const char *s) { const TObject *em = luaH_getglobal(L, LUA_ERRORMESSAGE); if (ttype(em) == LUA_TFUNCTION) { *L->top = *em; incr_top; lua_pushstring(L, s); luaD_call(L, L->top-2, 0); } } /* ** Reports an error, and jumps up to the available recovery label */ LUA_API void lua_error (lua_State *L, const char *s) { if (s) message(L, s); luaD_breakrun(L, LUA_ERRRUN); } void luaD_breakrun (lua_State *L, int errcode) { if (L->errorJmp) { L->errorJmp->status = errcode; longjmp(L->errorJmp->b, 1); } else { if (errcode != LUA_ERRMEM) message(L, "unable to recover; exiting\n"); exit(EXIT_FAILURE); } } int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud) { StkId oldCbase = L->Cbase; StkId oldtop = L->top; struct lua_longjmp lj; int allowhooks = L->allowhooks; lj.status = 0; lj.previous = L->errorJmp; /* chain new error handler */ L->errorJmp = &lj; if (setjmp(lj.b) == 0) (*f)(L, ud); else { /* an error occurred: restore the state */ L->allowhooks = allowhooks; L->Cbase = oldCbase; L->top = oldtop; restore_stack_limit(L); } L->errorJmp = lj.previous; /* restore old error handler */ return lj.status; } /* }====================================================== */ zangband/src/lua/lfunc.c0000644000000000000000000000456410250356275014201 0ustar rootroot/* ** $Id: lfunc.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #include #include "lua.h" #include "lfunc.h" #include "lmem.h" #include "lstate.h" #define sizeclosure(n) ((int)sizeof(Closure) + (int)sizeof(TObject)*((n)-1)) Closure *luaF_newclosure (lua_State *L, int nelems) { int size = sizeclosure(nelems); Closure *c = (Closure *)luaM_malloc(L, size); c->next = L->rootcl; L->rootcl = c; c->mark = c; c->nupvalues = nelems; L->nblocks += size; return c; } Proto *luaF_newproto (lua_State *L) { Proto *f = luaM_new(L, Proto); f->knum = NULL; f->nknum = 0; f->kstr = NULL; f->nkstr = 0; f->kproto = NULL; f->nkproto = 0; f->code = NULL; f->ncode = 0; f->numparams = 0; f->is_vararg = 0; f->maxstacksize = 0; f->marked = 0; f->lineinfo = NULL; f->nlineinfo = 0; f->nlocvars = 0; f->locvars = NULL; f->lineDefined = 0; f->source = NULL; f->next = L->rootproto; /* chain in list of protos */ L->rootproto = f; return f; } static size_t protosize (Proto *f) { return sizeof(Proto) + f->nknum*sizeof(Number) + f->nkstr*sizeof(TString *) + f->nkproto*sizeof(Proto *) + f->ncode*sizeof(Instruction) + f->nlocvars*sizeof(struct LocVar) + f->nlineinfo*sizeof(int); } void luaF_protook (lua_State *L, Proto *f, int pc) { f->ncode = pc; /* signal that proto was properly created */ L->nblocks += protosize(f); } void luaF_freeproto (lua_State *L, Proto *f) { if (f->ncode > 0) /* function was properly created? */ L->nblocks -= protosize(f); luaM_free(L, f->code); luaM_free(L, f->locvars); luaM_free(L, f->kstr); luaM_free(L, f->knum); luaM_free(L, f->kproto); luaM_free(L, f->lineinfo); luaM_free(L, f); } void luaF_freeclosure (lua_State *L, Closure *c) { L->nblocks -= sizeclosure(c->nupvalues); luaM_free(L, c); } /* ** Look for n-th local variable at line `line' in function `func'. ** Returns NULL if not found. */ const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { int i; for (i = 0; inlocvars && f->locvars[i].startpc <= pc; i++) { if (pc < f->locvars[i].endpc) { /* is variable active? */ local_number--; if (local_number == 0) return f->locvars[i].varname->str; } } return NULL; /* not found */ } zangband/src/lua/lgc.c0000644000000000000000000002053610250356275013634 0ustar rootroot/* ** $Id: lgc.c,v 1.2 2002/08/29 19:06:49 rr9 Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ #include "lua.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" typedef struct GCState { Hash *tmark; /* list of marked tables to be visited */ Closure *cmark; /* list of marked closures to be visited */ } GCState; static void markobject (GCState *st, TObject *o); /* mark a string; marks larger than 1 cannot be changed */ #define strmark(s) {if ((s)->marked == 0) (s)->marked = 1;} static void protomark (Proto *f) { if (!f->marked) { int i; f->marked = 1; strmark(f->source); for (i=0; inkstr; i++) strmark(f->kstr[i]); for (i=0; inkproto; i++) protomark(f->kproto[i]); for (i=0; inlocvars; i++) /* mark local-variable names */ strmark(f->locvars[i].varname); } } static void markstack (lua_State *L, GCState *st) { StkId o; for (o=L->stack; otop; o++) markobject(st, o); } static void marklock (lua_State *L, GCState *st) { int i; for (i=0; irefSize; i++) { if (L->refArray[i].st == LOCK) markobject(st, &L->refArray[i].o); } } static void markclosure (GCState *st, Closure *cl) { if (!ismarked(cl)) { if (!cl->isC) protomark(cl->f.l); cl->mark = st->cmark; /* chain it for later traversal */ st->cmark = cl; } } static void marktagmethods (lua_State *L, GCState *st) { int e; for (e=0; elast_tag; t++) { Closure *cl = luaT_gettm(L, t, e); if (cl) markclosure(st, cl); } } } static void markobject (GCState *st, TObject *o) { switch (ttype(o)) { case LUA_TUSERDATA: case LUA_TSTRING: strmark(tsvalue(o)); break; case LUA_TMARK: markclosure(st, infovalue(o)->func); break; case LUA_TFUNCTION: markclosure(st, clvalue(o)); break; case LUA_TTABLE: { if (!ismarked(hvalue(o))) { hvalue(o)->mark = st->tmark; /* chain it in list of marked */ st->tmark = hvalue(o); } break; } default: break; /* numbers, etc */ } } static void markall (lua_State *L) { GCState st; st.cmark = NULL; st.tmark = L->gt; /* put table of globals in mark list */ L->gt->mark = NULL; marktagmethods(L, &st); /* mark tag methods */ markstack(L, &st); /* mark stack objects */ marklock(L, &st); /* mark locked objects */ for (;;) { /* mark tables and closures */ if (st.cmark) { int i; Closure *f = st.cmark; /* get first closure from list */ st.cmark = f->mark; /* remove it from list */ for (i=0; inupvalues; i++) /* mark its upvalues */ markobject(&st, &f->upvalue[i]); } else if (st.tmark) { int i; Hash *h = st.tmark; /* get first table from list */ st.tmark = h->mark; /* remove it from list */ for (i=0; isize; i++) { Node *n = node(h, i); if (ttype(key(n)) != LUA_TNIL) { if (ttype(val(n)) == LUA_TNIL) luaH_remove(h, key(n)); /* dead element; try to remove it */ markobject(&st, &n->key); markobject(&st, &n->val); } } } else break; /* nothing else to mark */ } } static int hasmark (const TObject *o) { /* valid only for locked objects */ switch (o->ttype) { case LUA_TSTRING: case LUA_TUSERDATA: return tsvalue(o)->marked; case LUA_TTABLE: return ismarked(hvalue(o)); case LUA_TFUNCTION: return ismarked(clvalue(o)); default: /* number */ return 1; } } /* macro for internal debugging; check if a link of free refs is valid */ #define VALIDLINK(L, st,n) (NONEXT <= (st) && (st) < (n)) static void invalidaterefs (lua_State *L) { int n = L->refSize; int i; for (i=0; irefArray[i]; if (r->st == HOLD && !hasmark(&r->o)) r->st = COLLECTED; LUA_ASSERT((r->st == LOCK && hasmark(&r->o)) || (r->st == HOLD && hasmark(&r->o)) || r->st == COLLECTED || r->st == NONEXT || (r->st < n && VALIDLINK(L, L->refArray[r->st].st, n)), "inconsistent ref table"); } LUA_ASSERT(VALIDLINK(L, L->refFree, n), "inconsistent ref table"); } static void collectproto (lua_State *L) { Proto **p = &L->rootproto; Proto *next; while ((next = *p) != NULL) { if (next->marked) { next->marked = 0; p = &next->next; } else { *p = next->next; luaF_freeproto(L, next); } } } static void collectclosure (lua_State *L) { Closure **p = &L->rootcl; Closure *next; while ((next = *p) != NULL) { if (ismarked(next)) { next->mark = next; /* unmark */ p = &next->next; } else { *p = next->next; luaF_freeclosure(L, next); } } } static void collecttable (lua_State *L) { Hash **p = &L->roottable; Hash *next; while ((next = *p) != NULL) { if (ismarked(next)) { next->mark = next; /* unmark */ p = &next->next; } else { *p = next->next; luaH_free(L, next); } } } static void checktab (lua_State *L, stringtable *tb) { if (tb->nuse < (lint32)(tb->size/4) && tb->size > 10) luaS_resize(L, tb, tb->size/2); /* table is too big */ } static void collectstrings (lua_State *L, int all) { int i; for (i=0; istrt.size; i++) { /* for each list */ TString **p = &L->strt.hash[i]; TString *next; while ((next = *p) != NULL) { if (next->marked && !all) { /* preserve? */ if (next->marked < FIXMARK) /* does not change FIXMARKs */ next->marked = 0; p = &next->nexthash; } else { /* collect */ *p = next->nexthash; L->strt.nuse--; L->nblocks -= sizestring(next->len); luaM_free(L, next); } } } checktab(L, &L->strt); } static void collectudata (lua_State *L, int all) { int i; for (i=0; iudt.size; i++) { /* for each list */ TString **p = &L->udt.hash[i]; TString *next; while ((next = *p) != NULL) { LUA_ASSERT(next->marked <= 1, "udata cannot be fixed"); if (next->marked && !all) { /* preserve? */ next->marked = 0; p = &next->nexthash; } else { /* collect */ int tag = next->u.d.tag; *p = next->nexthash; next->nexthash = L->TMtable[tag].collected; /* chain udata */ L->TMtable[tag].collected = next; L->nblocks -= sizestring(next->len); L->udt.nuse--; } } } checktab(L, &L->udt); } #define MINBUFFER 256 static void checkMbuffer (lua_State *L) { if (L->Mbuffsize > MINBUFFER*2) { /* is buffer too big? */ size_t newsize = L->Mbuffsize/2; /* still larger than MINBUFFER */ L->nblocks += (newsize - L->Mbuffsize)*sizeof(char); L->Mbuffsize = newsize; luaM_reallocvector(L, L->Mbuffer, newsize, char); } } static void callgcTM (lua_State *L, const TObject *o) { Closure *tm = luaT_gettmbyObj(L, o, TM_GC); if (tm != NULL) { int oldah = L->allowhooks; L->allowhooks = 0; /* stop debug hooks during GC tag methods */ luaD_checkstack(L, 2); clvalue(L->top) = tm; ttype(L->top) = LUA_TFUNCTION; *(L->top+1) = *o; L->top += 2; luaD_call(L, L->top-2, 0); L->allowhooks = oldah; /* restore hooks */ } } static void callgcTMudata (lua_State *L) { int tag; TObject o; ttype(&o) = LUA_TUSERDATA; L->GCthreshold = 2*L->nblocks; /* avoid GC during tag methods */ for (tag=L->last_tag; tag>=0; tag--) { /* for each tag (in reverse order) */ TString *udata; while ((udata = L->TMtable[tag].collected) != NULL) { L->TMtable[tag].collected = udata->nexthash; /* remove it from list */ tsvalue(&o) = udata; callgcTM(L, &o); luaM_free(L, udata); } } } void luaC_collect (lua_State *L, int all) { collectudata(L, all); callgcTMudata(L); collectstrings(L, all); collecttable(L); collectproto(L); collectclosure(L); } void luaC_collectgarbage (lua_State *L) { markall(L); invalidaterefs(L); /* check unlocked references */ luaC_collect(L, 0); checkMbuffer(L); L->GCthreshold = 2*L->nblocks; /* set new threshold */ callgcTM(L, &luaO_nilobject); } void luaC_checkGC (lua_State *L) { if (L->nblocks >= L->GCthreshold) luaC_collectgarbage(L); } zangband/src/lua/liolib.c0000644000000000000000000004423410250356275014342 0ustar rootroot/* ** $Id: liolib.c,v 1.2 2001/11/26 18:50:59 rr9 Exp $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "luadebug.h" #include "lualib.h" #ifndef OLD_ANSI #include #include #define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s)) #define free(b) if (b) (free)(b) #else /* no support for locale and for strerror: fake them */ #define setlocale(a,b) ((void)a, strcmp((b),"C")==0?"C":NULL) #define LC_ALL 0 #define LC_COLLATE 0 #define LC_CTYPE 0 #define LC_MONETARY 0 #define LC_NUMERIC 0 #define LC_TIME 0 #define strerror(e) "generic I/O error" #define errno (-1) #endif #ifdef POPEN /* FILE *popen(); int pclose(); */ #define CLOSEFILE(L, f) ((pclose(f) == -1) ? fclose(f) : 0) #else /* no support for popen */ #define popen(x,y) NULL /* that is, popen always fails */ #define CLOSEFILE(L, f) (fclose(f)) #endif #define INFILE 0 #define OUTFILE 1 typedef struct IOCtrl { int ref[2]; /* ref for strings _INPUT/_OUTPUT */ int iotag; /* tag for file handles */ int closedtag; /* tag for closed handles */ } IOCtrl; static const char *const filenames[] = {"_INPUT", "_OUTPUT"}; static int pushresult (lua_State *L, int i) { if (i) { lua_pushuserdata(L, NULL); return 1; } else { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushnumber(L, errno); return 3;; } } /* ** {====================================================== ** FILE Operations ** ======================================================= */ static FILE *gethandle (lua_State *L, IOCtrl *ctrl, int f) { void *p = lua_touserdata(L, f); if (p != NULL) { /* is `f' a userdata ? */ int ftag = lua_tag(L, f); if (ftag == ctrl->iotag) /* does it have the correct tag? */ return (FILE *)p; else if (ftag == ctrl->closedtag) lua_error(L, "cannot access a closed file"); /* else go through */ } return NULL; } static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) { FILE *f = gethandle(L, ctrl, arg); luaL_arg_check(L, f, arg, "invalid file handle"); return f; } static FILE *getfilebyref (lua_State *L, IOCtrl *ctrl, int inout) { FILE *f; lua_getglobals(L); lua_getref(L, ctrl->ref[inout]); lua_rawget(L, -2); f = gethandle(L, ctrl, -1); if (f == NULL) luaL_verror(L, "global variable `%.10s' is not a file handle", filenames[inout]); return f; } static void setfilebyname (lua_State *L, IOCtrl *ctrl, FILE *f, const char *name) { lua_pushusertag(L, f, ctrl->iotag); lua_setglobal(L, name); } #define setfile(L,ctrl,f,inout) (setfilebyname(L,ctrl,f,filenames[inout])) static int setreturn (lua_State *L, IOCtrl *ctrl, FILE *f, int inout) { if (f == NULL) return pushresult(L, 0); else { setfile(L, ctrl, f, inout); lua_pushusertag(L, f, ctrl->iotag); return 1; } } static int closefile (lua_State *L, IOCtrl *ctrl, FILE *f) { if (f == stdin || f == stdout || f == stderr) return 1; else { lua_pushusertag(L, f, ctrl->iotag); lua_settag(L, ctrl->closedtag); return (CLOSEFILE(L, f) == 0); } } static int io_close (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); lua_pop(L, 1); /* remove upvalue */ return pushresult(L, closefile(L, ctrl, getnonullfile(L, ctrl, 1))); } static int file_collect (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *f = getnonullfile(L, ctrl, 1); if (f != stdin && f != stdout && f != stderr) CLOSEFILE(L, f); return 0; } static int io_open (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *f; lua_pop(L, 1); /* remove upvalue */ f = fopen(luaL_check_string(L, 1), luaL_check_string(L, 2)); if (f) { lua_pushusertag(L, f, ctrl->iotag); return 1; } else return pushresult(L, 0); } static int io_fromto (lua_State *L, int inout, const char *mode) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *current; lua_pop(L, 1); /* remove upvalue */ if (lua_isnull(L, 1)) { closefile(L, ctrl, getfilebyref(L, ctrl, inout)); current = (inout == 0) ? stdin : stdout; } else if (lua_tag(L, 1) == ctrl->iotag) /* deprecated option */ current = (FILE *)lua_touserdata(L, 1); else { const char *s = luaL_check_string(L, 1); current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode); } return setreturn(L, ctrl, current, inout); } static int io_readfrom (lua_State *L) { return io_fromto(L, INFILE, "r"); } static int io_writeto (lua_State *L) { return io_fromto(L, OUTFILE, "w"); } static int io_appendto (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *current; lua_pop(L, 1); /* remove upvalue */ current = fopen(luaL_check_string(L, 1), "a"); return setreturn(L, ctrl, current, OUTFILE); } /* ** {====================================================== ** READ ** ======================================================= */ #ifdef LUA_COMPAT_READPATTERN /* ** We cannot lookahead without need, because this can lock stdin. ** This flag signals when we need to read a next char. */ #define NEED_OTHER (EOF-1) /* just some flag different from EOF */ static int read_pattern (lua_State *L, FILE *f, const char *p) { int inskip = 0; /* {skip} level */ int c = NEED_OTHER; luaL_Buffer b; luaL_buffinit(L, &b); while (*p != '\0') { switch (*p) { case '{': inskip++; p++; continue; case '}': if (!inskip) lua_error(L, "unbalanced braces in read pattern"); inskip--; p++; continue; default: { const char *ep = luaI_classend(L, p); /* get what is next */ int m; /* match result */ if (c == NEED_OTHER) c = getc(f); m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep); if (m) { if (!inskip) luaL_putchar(&b, c); c = NEED_OTHER; } switch (*ep) { case '+': /* repetition (1 or more) */ if (!m) goto break_while; /* pattern fails? */ /* else go through */ case '*': /* repetition (0 or more) */ while (m) { /* reads the same item until it fails */ c = getc(f); m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep); if (m && !inskip) luaL_putchar(&b, c); } /* go through to continue reading the pattern */ case '?': /* optional */ p = ep+1; /* continues reading the pattern */ continue; default: if (!m) goto break_while; /* pattern fails? */ p = ep; /* else continues reading the pattern */ } } } } break_while: if (c != NEED_OTHER) ungetc(c, f); luaL_pushresult(&b); /* close buffer */ return (*p == '\0'); } #else #define read_pattern(L, f, p) (lua_error(L, "read patterns are deprecated"), 0) #endif static int read_number (lua_State *L, FILE *f) { long d; if (fscanf(f, "%ld", &d) == 1) { lua_pushnumber(L, d); return 1; } else return 0; /* read fails */ } static int read_word (lua_State *L, FILE *f) { int c; luaL_Buffer b; luaL_buffinit(L, &b); do { c = fgetc(f); } while (isspace(c)); /* skip spaces */ while (c != EOF && !isspace(c)) { luaL_putchar(&b, c); c = fgetc(f); } ungetc(c, f); luaL_pushresult(&b); /* close buffer */ return (lua_strlen(L, -1) > 0); } static int read_line (lua_State *L, FILE *f) { int n = 0; luaL_Buffer b; luaL_buffinit(L, &b); for (;;) { char *p = luaL_prepbuffer(&b); if (!fgets(p, LUAL_BUFFERSIZE, f)) /* read fails? */ break; n = strlen(p); if (p[n-1] != '\n') luaL_addsize(&b, n); else { luaL_addsize(&b, n-1); /* do not add the `\n' */ break; } } luaL_pushresult(&b); /* close buffer */ return (n > 0); /* read something? */ } static void read_file (lua_State *L, FILE *f) { size_t len = 0; size_t size = BUFSIZ; char *buffer = NULL; for (;;) { char *newbuffer = (char *)realloc(buffer, size); if (newbuffer == NULL) { free(buffer); lua_error(L, "not enough memory to read a file"); } buffer = newbuffer; len += fread(buffer+len, sizeof(char), size-len, f); if (len < size) break; /* did not read all it could */ size *= 2; } lua_pushlstring(L, buffer, len); free(buffer); } static int read_chars (lua_State *L, FILE *f, size_t n) { char *buffer; size_t n1; char statbuff[BUFSIZ]; if (n <= BUFSIZ) buffer = statbuff; else { buffer = (char *)malloc(n); if (buffer == NULL) lua_error(L, "not enough memory to read a file"); } n1 = fread(buffer, sizeof(char), n, f); lua_pushlstring(L, buffer, n1); if (buffer != statbuff) free(buffer); return (n1 > 0 || n == 0); } static int io_read (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); int lastarg = lua_gettop(L) - 1; int firstarg = 1; FILE *f = gethandle(L, ctrl, firstarg); int n; if (f) firstarg++; else f = getfilebyref(L, ctrl, INFILE); /* get _INPUT */ lua_pop(L, 1); if (firstarg > lastarg) { /* no arguments? */ lua_settop(L, 0); /* erase upvalue and other eventual garbage */ firstarg = lastarg = 1; /* correct indices */ lua_pushstring(L, "*l"); /* push default argument */ } else /* ensure stack space for all results and for auxlib's buffer */ luaL_checkstack(L, lastarg-firstarg+1+LUA_MINSTACK, "too many arguments"); for (n = firstarg; n<=lastarg; n++) { int success; if (lua_isnumber(L, n)) success = read_chars(L, f, (size_t)lua_tonumber(L, n)); else { const char *p = luaL_check_string(L, n); if (p[0] != '*') success = read_pattern(L, f, p); /* deprecated! */ else { switch (p[1]) { case 'n': /* number */ if (!read_number(L, f)) goto endloop; /* read fails */ continue; /* number is already pushed; avoid the "pushstring" */ case 'l': /* line */ success = read_line(L, f); break; case 'a': /* file */ read_file(L, f); success = 1; /* always success */ break; case 'w': /* word */ success = read_word(L, f); break; default: luaL_argerror(L, n, "invalid format"); success = 0; /* to avoid warnings */ } } } if (!success) { lua_pop(L, 1); /* remove last result */ break; /* read fails */ } } endloop: return n - firstarg; } /* }====================================================== */ static int io_write (lua_State *L) { int lastarg = lua_gettop(L) - 1; IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); int arg = 1; int status = 1; FILE *f = gethandle(L, ctrl, arg); if (f) arg++; else f = getfilebyref(L, ctrl, OUTFILE); /* get _OUTPUT */ for (; arg <= lastarg; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* LUA_NUMBER */ /* optimization: could be done exactly as for strings */ status = status && fprintf(f, "%ld", lua_tonumber(L, arg)) > 0; } else { size_t l; const char *s = luaL_check_lstr(L, arg, &l); status = status && (fwrite(s, sizeof(char), l, f) == l); } } pushresult(L, status); return 1; } static int io_seek (lua_State *L) { static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *f; int op; long offset; lua_pop(L, 1); /* remove upvalue */ f = getnonullfile(L, ctrl, 1); op = luaL_findstring(luaL_opt_string(L, 2, "cur"), modenames); offset = luaL_opt_long(L, 3, 0); luaL_arg_check(L, op != -1, 2, "invalid mode"); op = fseek(f, offset, mode[op]); if (op) return pushresult(L, 0); /* error */ else { lua_pushnumber(L, ftell(f)); return 1; } } static int io_flush (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1); FILE *f; lua_pop(L, 1); /* remove upvalue */ f = gethandle(L, ctrl, 1); luaL_arg_check(L, f || lua_isnull(L, 1), 1, "invalid file handle"); return pushresult(L, fflush(f) == 0); } /* }====================================================== */ /* ** {====================================================== ** Other O.S. Operations ** ======================================================= */ static int io_execute (lua_State *L) { lua_pushnumber(L, system(luaL_check_string(L, 1))); return 1; } static int io_remove (lua_State *L) { return pushresult(L, remove(luaL_check_string(L, 1)) == 0); } static int io_rename (lua_State *L) { return pushresult(L, rename(luaL_check_string(L, 1), luaL_check_string(L, 2)) == 0); } static int io_getenv (lua_State *L) { lua_pushstring(L, getenv(luaL_check_string(L, 1))); /* if NULL push nil */ return 1; } static int io_clock (lua_State *L) { lua_pushnumber(L, ((long)clock())/CLOCKS_PER_SEC); return 1; } static int io_date (lua_State *L) { char b[256]; const char *s = luaL_opt_string(L, 1, "%c"); struct tm *stm; time_t t; time(&t); stm = localtime(&t); if (strftime(b, sizeof(b), s, stm)) lua_pushstring(L, b); else lua_error(L, "invalid `date' format"); return 1; } static int setloc (lua_State *L) { static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}; static const char *const catnames[] = {"all", "collate", "ctype", "monetary", "numeric", "time", NULL}; int op = luaL_findstring(luaL_opt_string(L, 2, "all"), catnames); luaL_arg_check(L, op != -1, 2, "invalid option"); lua_pushstring(L, setlocale(cat[op], luaL_check_string(L, 1))); return 1; } static int io_exit (lua_State *L) { exit(luaL_opt_int(L, 1, EXIT_SUCCESS)); return 0; /* to avoid warnings */ } /* }====================================================== */ static int io_debug (lua_State *L) { for (;;) { char buffer[250]; fprintf(stderr, "lua_debug> "); if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0) return 0; lua_dostring(L, buffer); lua_settop(L, 0); /* remove eventual returns */ } } #define LEVELS1 12 /* size of the first part of the stack */ #define LEVELS2 10 /* size of the second part of the stack */ static int errorfb (lua_State *L) { int level = 1; /* skip level 0 (it's this function) */ int firstpart = 1; /* still before eventual `...' */ lua_Debug ar; luaL_Buffer b; luaL_buffinit(L, &b); luaL_addstring(&b, "error: "); luaL_addstring(&b, luaL_check_string(L, 1)); luaL_addstring(&b, "\n"); while (lua_getstack(L, level++, &ar)) { char buff[120]; /* enough to fit following `sprintf's */ if (level == 2) luaL_addstring(&b, "stack traceback:\n"); else if (level > LEVELS1 && firstpart) { /* no more than `LEVELS2' more levels? */ if (!lua_getstack(L, level+LEVELS2, &ar)) level--; /* keep going */ else { luaL_addstring(&b, " ...\n"); /* too many levels */ while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ level++; } firstpart = 0; continue; } sprintf(buff, "%4d: ", level-1); luaL_addstring(&b, buff); lua_getinfo(L, "Snl", &ar); switch (*ar.namewhat) { case 'g': case 'l': /* global, local */ sprintf(buff, "function `%.50s'", ar.name); break; case 'f': /* field */ sprintf(buff, "method `%.50s'", ar.name); break; case 't': /* tag method */ sprintf(buff, "`%.50s' tag method", ar.name); break; default: { if (*ar.what == 'm') /* main? */ sprintf(buff, "main of %.70s", ar.short_src); else if (*ar.what == 'C') /* C function? */ sprintf(buff, "%.70s", ar.short_src); else sprintf(buff, "function <%d:%.70s>", ar.linedefined, ar.short_src); ar.source = NULL; /* do not print source again */ } } luaL_addstring(&b, buff); if (ar.currentline > 0) { sprintf(buff, " at line %d", ar.currentline); luaL_addstring(&b, buff); } if (ar.source) { sprintf(buff, " [%.70s]", ar.short_src); luaL_addstring(&b, buff); } luaL_addstring(&b, "\n"); } luaL_pushresult(&b); lua_getglobal(L, LUA_ALERT); if (lua_isfunction(L, -1)) { /* avoid loop if _ALERT is not defined */ lua_pushvalue(L, -2); /* error message */ lua_rawcall(L, 1, 0); } return 0; } static const struct luaL_reg iolib[] = { {LUA_ERRORMESSAGE, errorfb}, {"clock", io_clock}, {"date", io_date}, {"debug", io_debug}, {"execute", io_execute}, {"exit", io_exit}, {"getenv", io_getenv}, {"remove", io_remove}, {"rename", io_rename}, {"setlocale", setloc}, }; static const struct luaL_reg iolibtag[] = { {"appendto", io_appendto}, {"closefile", io_close}, {"flush", io_flush}, {"openfile", io_open}, {"read", io_read}, {"readfrom", io_readfrom}, {"seek", io_seek}, {"write", io_write}, {"writeto", io_writeto} }; static void openwithcontrol (lua_State *L) { IOCtrl *ctrl = (IOCtrl *)lua_newuserdata(L, sizeof(IOCtrl)); unsigned int i; ctrl->iotag = lua_newtag(L); ctrl->closedtag = lua_newtag(L); for (i=0; iref[INFILE] = lua_ref(L, 1); lua_pushstring(L, filenames[OUTFILE]); ctrl->ref[OUTFILE] = lua_ref(L, 1); /* predefined file handles */ setfile(L, ctrl, stdin, INFILE); setfile(L, ctrl, stdout, OUTFILE); setfilebyname(L, ctrl, stdin, "_STDIN"); setfilebyname(L, ctrl, stdout, "_STDOUT"); setfilebyname(L, ctrl, stderr, "_STDERR"); /* close files when collected */ lua_pushcclosure(L, file_collect, 1); /* pops `ctrl' from stack */ lua_settagmethod(L, ctrl->iotag, "gc"); } LUALIB_API void lua_iolibopen (lua_State *L) { luaL_openl(L, iolib); openwithcontrol(L); } zangband/src/lua/llex.c0000644000000000000000000002276010250356275014034 0ustar rootroot/* ** $Id: llex.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #include #include #include #include "lua.h" #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "luadebug.h" #include "lzio.h" #define next(LS) (LS->current = zgetc(LS->z)) /* ORDER RESERVED */ static const char *const token2string [] = { "and", "break", "do", "else", "elseif", "end", "for", "function", "if", "local", "nil", "not", "or", "repeat", "return", "then", "until", "while", "", "..", "...", "==", ">=", "<=", "~=", "", "", ""}; void luaX_init (lua_State *L) { int i; for (i=0; imarked = (unsigned char)(RESERVEDMARK+i); /* reserved word */ } } #define MAXSRC 80 void luaX_checklimit (LexState *ls, int val, int limit, const char *msg) { if (val > limit) { char buff[100]; sprintf(buff, "too many %.50s (limit=%d)", msg, limit); luaX_error(ls, buff, ls->t.token); } } void luaX_syntaxerror (LexState *ls, const char *s, const char *token) { char buff[MAXSRC]; luaO_chunkid(buff, ls->source->str, sizeof(buff)); luaO_verror(ls->L, "%.99s;\n last token read: `%.30s' at line %d in %.80s", s, token, ls->linenumber, buff); } void luaX_error (LexState *ls, const char *s, int token) { char buff[TOKEN_LEN]; luaX_token2str(token, buff); if (buff[0] == '\0') luaX_syntaxerror(ls, s, ls->L->Mbuffer); else luaX_syntaxerror(ls, s, buff); } void luaX_token2str (int token, char *s) { if (token < 256) { s[0] = (char)token; s[1] = '\0'; } else strcpy(s, token2string[token-FIRST_RESERVED]); } static void luaX_invalidchar (LexState *ls, int c) { char buff[8]; sprintf(buff, "0x%02X", c); luaX_syntaxerror(ls, "invalid control char", buff); } static void inclinenumber (LexState *LS) { next(LS); /* skip '\n' */ ++LS->linenumber; luaX_checklimit(LS, LS->linenumber, MAX_INT, "lines in a chunk"); } void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source) { LS->L = L; LS->lookahead.token = TK_EOS; /* no look-ahead token */ LS->z = z; LS->fs = NULL; LS->linenumber = 1; LS->lastline = 1; LS->source = source; next(LS); /* read first char */ if (LS->current == '#') { do { /* skip first line */ next(LS); } while (LS->current != '\n' && LS->current != EOZ); } } /* ** ======================================================= ** LEXICAL ANALYZER ** ======================================================= */ /* use Mbuffer to store names, literal strings and numbers */ #define EXTRABUFF 128 #define checkbuffer(L, n, len) if ((len)+(n) > L->Mbuffsize) \ luaO_openspace(L, (len)+(n)+EXTRABUFF) #define save(L, c, l) (L->Mbuffer[l++] = (char)c) #define save_and_next(L, LS, l) (save(L, LS->current, l), next(LS)) static const char *readname (LexState *LS) { lua_State *L = LS->L; size_t l = 0; checkbuffer(L, 10, l); do { checkbuffer(L, 10, l); save_and_next(L, LS, l); } while (isalnum(LS->current) || LS->current == '_'); save(L, '\0', l); return L->Mbuffer; } /* LUA_NUMBER */ static void read_number (LexState *LS, int comma, SemInfo *seminfo) { lua_State *L = LS->L; size_t l = 0; checkbuffer(L, 10, l); if (comma) save(L, '.', l); while (isdigit(LS->current)) { checkbuffer(L, 10, l); save_and_next(L, LS, l); } if (LS->current == '.') { save_and_next(L, LS, l); if (LS->current == '.') { save_and_next(L, LS, l); save(L, '\0', l); luaX_error(LS, "ambiguous syntax" " (decimal point x string concatenation)", TK_NUMBER); } } while (isdigit(LS->current)) { checkbuffer(L, 10, l); save_and_next(L, LS, l); } if (LS->current == 'e' || LS->current == 'E') { save_and_next(L, LS, l); /* read 'E' */ if (LS->current == '+' || LS->current == '-') save_and_next(L, LS, l); /* optional exponent sign */ while (isdigit(LS->current)) { checkbuffer(L, 10, l); save_and_next(L, LS, l); } } save(L, '\0', l); if (!luaO_str2d(L->Mbuffer, &seminfo->r)) luaX_error(LS, "malformed number", TK_NUMBER); } static void read_long_string (LexState *LS, SemInfo *seminfo) { lua_State *L = LS->L; int cont = 0; size_t l = 0; checkbuffer(L, 10, l); save(L, '[', l); /* save first '[' */ save_and_next(L, LS, l); /* pass the second '[' */ for (;;) { checkbuffer(L, 10, l); switch (LS->current) { case EOZ: save(L, '\0', l); luaX_error(LS, "unfinished long string", TK_STRING); break; /* to avoid warnings */ case '[': save_and_next(L, LS, l); if (LS->current == '[') { cont++; save_and_next(L, LS, l); } continue; case ']': save_and_next(L, LS, l); if (LS->current == ']') { if (cont == 0) goto endloop; cont--; save_and_next(L, LS, l); } continue; case '\n': save(L, '\n', l); inclinenumber(LS); continue; default: save_and_next(L, LS, l); } } endloop: save_and_next(L, LS, l); /* skip the second ']' */ save(L, '\0', l); seminfo->ts = luaS_newlstr(L, L->Mbuffer+2, l-5); } static void read_string (LexState *LS, int del, SemInfo *seminfo) { lua_State *L = LS->L; size_t l = 0; checkbuffer(L, 10, l); save_and_next(L, LS, l); while (LS->current != del) { checkbuffer(L, 10, l); switch (LS->current) { case EOZ: case '\n': save(L, '\0', l); luaX_error(LS, "unfinished string", TK_STRING); break; /* to avoid warnings */ case '\\': next(LS); /* do not save the '\' */ switch (LS->current) { case 'a': save(L, '\a', l); next(LS); break; case 'b': save(L, '\b', l); next(LS); break; case 'f': save(L, '\f', l); next(LS); break; case 'n': save(L, '\n', l); next(LS); break; case 'r': save(L, '\r', l); next(LS); break; case 't': save(L, '\t', l); next(LS); break; case 'v': save(L, '\v', l); next(LS); break; case '\n': save(L, '\n', l); inclinenumber(LS); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int c = 0; int i = 0; do { c = 10*c + (LS->current-'0'); next(LS); } while (++i<3 && isdigit(LS->current)); if (c != (unsigned char)c) { save(L, '\0', l); luaX_error(LS, "escape sequence too large", TK_STRING); } save(L, c, l); break; } default: /* handles \\, \", \', and \? */ save_and_next(L, LS, l); } break; default: save_and_next(L, LS, l); } } save_and_next(L, LS, l); /* skip delimiter */ save(L, '\0', l); seminfo->ts = luaS_newlstr(L, L->Mbuffer+1, l-3); } int luaX_lex (LexState *LS, SemInfo *seminfo) { for (;;) { switch (LS->current) { case ' ': case '\t': case '\r': /* `\r' to avoid problems with DOS */ next(LS); continue; case '\n': inclinenumber(LS); continue; case '$': luaX_error(LS, "unexpected `$' (pragmas are no longer supported)", '$'); break; case '-': next(LS); if (LS->current != '-') return '-'; do { next(LS); } while (LS->current != '\n' && LS->current != EOZ); continue; case '[': next(LS); if (LS->current != '[') return '['; else { read_long_string(LS, seminfo); return TK_STRING; } case '=': next(LS); if (LS->current != '=') return '='; else { next(LS); return TK_EQ; } case '<': next(LS); if (LS->current != '=') return '<'; else { next(LS); return TK_LE; } case '>': next(LS); if (LS->current != '=') return '>'; else { next(LS); return TK_GE; } case '~': next(LS); if (LS->current != '=') return '~'; else { next(LS); return TK_NE; } case '"': case '\'': read_string(LS, LS->current, seminfo); return TK_STRING; case '.': next(LS); if (LS->current == '.') { next(LS); if (LS->current == '.') { next(LS); return TK_DOTS; /* ... */ } else return TK_CONCAT; /* .. */ } else if (!isdigit(LS->current)) return '.'; else { read_number(LS, 1, seminfo); return TK_NUMBER; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': read_number(LS, 0, seminfo); return TK_NUMBER; case EOZ: return TK_EOS; case '_': goto tname; default: if (!isalpha(LS->current)) { int c = LS->current; if (iscntrl(c)) luaX_invalidchar(LS, c); next(LS); return c; } tname: { /* identifier or reserved word */ TString *ts = luaS_new(LS->L, readname(LS)); if (ts->marked >= RESERVEDMARK) /* reserved word? */ return ts->marked-RESERVEDMARK+FIRST_RESERVED; seminfo->ts = ts; return TK_NAME; } } } } zangband/src/lua/lmem.c0000644000000000000000000000744610250356275014026 0ustar rootroot/* ** $Id: lmem.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #include #include "lua.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #ifdef LUA_DEBUG /* ** {====================================================================== ** Controlled version for realloc. ** ======================================================================= */ #include #include #include #define realloc(b, s) debug_realloc(b, s) #define malloc(b) debug_realloc(NULL, b) #define free(b) debug_realloc(b, 0) /* ensures maximum alignment for HEADER */ #define HEADER (sizeof(union L_Umaxalign)) #define MARKSIZE 16 #define MARK 0x55 /* 01010101 (a nice pattern) */ #define blocksize(b) ((unsigned long *)((char *)(b) - HEADER)) unsigned long memdebug_numblocks = 0; unsigned long memdebug_total = 0; unsigned long memdebug_maxmem = 0; unsigned long memdebug_memlimit = LONG_MAX; static void *checkblock (void *block) { unsigned long *b = blocksize(block); unsigned long size = *b; int i; for (i=0;i memdebug_memlimit) return NULL; /* to test memory allocation errors */ else { size_t realsize = HEADER+size+MARKSIZE; char *newblock = (char *)(malloc)(realsize); /* alloc a new block */ int i; if (realsize < size) return NULL; /* overflow! */ if (newblock == NULL) return NULL; if (block) { size_t oldsize = *blocksize(block); if (oldsize > size) oldsize = size; memcpy(newblock+HEADER, block, oldsize); freeblock(block); /* erase (and check) old copy */ } memdebug_total += size; if (memdebug_total > memdebug_maxmem) memdebug_maxmem = memdebug_total; memdebug_numblocks++; *(unsigned long *)newblock = size; for (i=0;i= limit-inc) lua_error(L, errormsg); if ((newn ^ nelems) <= nelems || /* still the same power-of-2 limit? */ (nelems > 0 && newn < MINPOWER2)) /* or block already is MINPOWER2? */ return block; /* do not need to reallocate */ else /* it crossed a power-of-2 boundary; grow to next power */ return luaM_realloc(L, block, luaO_power2(newn)*size); } /* ** generic allocation routine. */ void *luaM_realloc (lua_State *L, void *block, lint32 size) { if (size == 0) { free(block); /* block may be NULL; that is OK for free */ return NULL; } else if (size >= MAX_SIZET) lua_error(L, "memory allocation error: block too big"); block = realloc(block, size); if (block == NULL) { if (L) luaD_breakrun(L, LUA_ERRMEM); /* break run without error message */ else return NULL; /* error before creating state! */ } return block; } zangband/src/lua/lobject.c0000644000000000000000000000600610250356275014505 0ustar rootroot/* ** $Id: lobject.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ #include #include #include #include #include #include "lua.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" const TObject luaO_nilobject = {LUA_TNIL, {NULL}}; const char *const luaO_typenames[] = { "userdata", "nil", "number", "string", "table", "function" }; /* ** returns smaller power of 2 larger than `n' (minimum is MINPOWER2) */ lint32 luaO_power2 (lint32 n) { lint32 p = MINPOWER2; while (p<=n) p<<=1; return p; } int luaO_equalObj (const TObject *t1, const TObject *t2) { if (ttype(t1) != ttype(t2)) return 0; switch (ttype(t1)) { case LUA_TNUMBER: return nvalue(t1) == nvalue(t2); case LUA_TSTRING: case LUA_TUSERDATA: return tsvalue(t1) == tsvalue(t2); case LUA_TTABLE: return hvalue(t1) == hvalue(t2); case LUA_TFUNCTION: return clvalue(t1) == clvalue(t2); default: LUA_ASSERT(ttype(t1) == LUA_TNIL, "invalid type"); return 1; /* LUA_TNIL */ } } char *luaO_openspace (lua_State *L, size_t n) { if (n > L->Mbuffsize) { luaM_reallocvector(L, L->Mbuffer, n, char); L->nblocks += (n - L->Mbuffsize)*sizeof(char); L->Mbuffsize = n; } return L->Mbuffer; } int luaO_str2d (const char *s, Number *result) { /* LUA_NUMBER */ char *endptr; Number res = lua_str2number(s, &endptr); if (endptr == s) return 0; /* no conversion */ while (isspace((unsigned char)*endptr)) endptr++; if (*endptr != '\0') return 0; /* invalid trailing characters? */ *result = res; return 1; } /* maximum length of a string format for `luaO_verror' */ #define MAX_VERROR 280 /* this function needs to handle only '%d' and '%.XXs' formats */ void luaO_verror (lua_State *L, const char *fmt, ...) { va_list argp; char buff[MAX_VERROR]; /* to hold formatted message */ va_start(argp, fmt); vsprintf(buff, fmt, argp); va_end(argp); lua_error(L, buff); } void luaO_chunkid (char *out, const char *source, int bufflen) { if (*source == '=') { strncpy(out, source+1, bufflen); /* remove first char */ out[bufflen-1] = '\0'; /* ensures null termination */ } else { if (*source == '@') { int l; source++; /* skip the `@' */ bufflen -= sizeof("file `...%s'"); l = strlen(source); if (l>bufflen) { source += (l-bufflen); /* get last part of file name */ sprintf(out, "file `...%.99s'", source); } else sprintf(out, "file `%.99s'", source); } else { int len = strcspn(source, "\n"); /* stop at first newline */ bufflen -= sizeof("string \"%.*s...\""); if (len > bufflen) len = bufflen; if (source[len] != '\0') { /* must truncate? */ strcpy(out, "string \""); out += strlen(out); strncpy(out, source, len); strcpy(out+len, "...\""); } else sprintf(out, "string \"%.99s\"", source); } } } zangband/src/lua/lparser.c0000644000000000000000000007200410250356275014534 0ustar rootroot/* ** $Id: lparser.c,v 1.3 2003/12/28 13:55:52 sfuerst Exp $ ** LL(1) Parser and code generator for Lua ** See Copyright Notice in lua.h */ #include #include #include "lua.h" #include "lcode.h" #include "lfunc.h" #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" /* ** Constructors descriptor: ** `n' indicates number of elements, and `k' signals whether ** it is a list constructor (k = 0) or a record constructor (k = 1) ** or empty (k = ';' or '}') */ typedef struct Constdesc { int n; int k; } Constdesc; typedef struct Breaklabel { struct Breaklabel *previous; /* chain */ int breaklist; int stacklevel; } Breaklabel; /* ** prototypes for recursive non-terminal functions */ static void body (LexState *ls, int needself, int line); static void chunk (LexState *ls); static void constructor (LexState *ls); static void expr (LexState *ls, expdesc *v); static void exp1 (LexState *ls); static void next (LexState *ls) { ls->lastline = ls->linenumber; if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ ls->t = ls->lookahead; /* use this one */ ls->lookahead.token = TK_EOS; /* and discharge it */ } else ls->t.token = luaX_lex(ls, &ls->t.seminfo); /* read next token */ } static void lookahead (LexState *ls) { LUA_ASSERT(ls->lookahead.token == TK_EOS, "two look-aheads"); ls->lookahead.token = luaX_lex(ls, &ls->lookahead.seminfo); } static void error_expected (LexState *ls, int token) { char buff[100], t[TOKEN_LEN]; luaX_token2str(token, t); sprintf(buff, "`%.20s' expected", t); luaK_error(ls, buff); } static void check (LexState *ls, int c) { if (ls->t.token != c) error_expected(ls, c); next(ls); } static void check_condition (LexState *ls, int c, const char *msg) { if (!c) luaK_error(ls, msg); } static int optional (LexState *ls, int c) { if (ls->t.token == c) { next(ls); return 1; } else return 0; } static void check_match (LexState *ls, int what, int who, int where) { if (ls->t.token != what) { if (where == ls->linenumber) error_expected(ls, what); else { char buff[100]; char t_what[TOKEN_LEN], t_who[TOKEN_LEN]; luaX_token2str(what, t_what); luaX_token2str(who, t_who); sprintf(buff, "`%.20s' expected (to close `%.20s' at line %d)", t_what, t_who, where); luaK_error(ls, buff); } } next(ls); } static int string_constant (FuncState *fs, TString *s) { Proto *f = fs->f; int c = s->u.s.constindex; if (c >= f->nkstr || f->kstr[c] != s) { luaM_growvector(fs->L, f->kstr, f->nkstr, 1, TString *, "constant table overflow", MAXARG_U); c = f->nkstr++; f->kstr[c] = s; s->u.s.constindex = c; /* hint for next time */ } return c; } static void code_string (LexState *ls, TString *s) { luaK_kstr(ls, string_constant(ls->fs, s)); } static TString *str_checkname (LexState *ls) { TString *ts; check_condition(ls, (ls->t.token == TK_NAME), " expected"); ts = ls->t.seminfo.ts; next(ls); return ts; } static int checkname (LexState *ls) { return string_constant(ls->fs, str_checkname(ls)); } static int luaI_registerlocalvar (LexState *ls, TString *varname) { Proto *f = ls->fs->f; luaM_growvector(ls->L, f->locvars, f->nlocvars, 1, LocVar, "", MAX_INT); f->locvars[f->nlocvars].varname = varname; return f->nlocvars++; } static void new_localvar (LexState *ls, TString *name, int n) { FuncState *fs = ls->fs; luaX_checklimit(ls, fs->nactloc+n+1, MAXLOCALS, "local variables"); fs->actloc[fs->nactloc+n] = luaI_registerlocalvar(ls, name); } static void adjustlocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; while (nvars--) fs->f->locvars[fs->actloc[fs->nactloc++]].startpc = fs->pc; } static void removelocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; while (nvars--) fs->f->locvars[fs->actloc[--fs->nactloc]].endpc = fs->pc; } static void new_localvarstr (LexState *ls, const char *name, int n) { new_localvar(ls, luaS_newfixed(ls->L, name), n); } static int search_local (LexState *ls, TString *n, expdesc *var) { FuncState *fs; int level = 0; for (fs=ls->fs; fs; fs=fs->prev) { int i; for (i=fs->nactloc-1; i >= 0; i--) { if (n == fs->f->locvars[fs->actloc[i]].varname) { var->k = VLOCAL; var->u.index = i; return level; } } level++; /* `var' not found; check outer level */ } var->k = VGLOBAL; /* not found in any level; must be global */ return -1; } static void singlevar (LexState *ls, TString *n, expdesc *var) { int level = search_local(ls, n, var); if (level >= 1) /* neither local (0) nor global (-1)? */ luaX_syntaxerror(ls, "cannot access a variable in outer scope", n->str); else if (level == -1) /* global? */ var->u.index = string_constant(ls->fs, n); } static int indexupvalue (LexState *ls, expdesc *v) { FuncState *fs = ls->fs; int i; for (i=0; inupvalues; i++) { if (fs->upvalues[i].k == v->k && fs->upvalues[i].u.index == v->u.index) return i; } /* new one */ luaX_checklimit(ls, fs->nupvalues+1, MAXUPVALUES, "upvalues"); fs->upvalues[fs->nupvalues] = *v; return fs->nupvalues++; } static void pushupvalue (LexState *ls, TString *n) { FuncState *fs = ls->fs; expdesc v; int level = search_local(ls, n, &v); if (level == -1) { /* global? */ if (fs->prev == NULL) luaX_syntaxerror(ls, "cannot access upvalue in main", n->str); v.u.index = string_constant(fs->prev, n); } else if (level != 1) luaX_syntaxerror(ls, "upvalue must be global or local to immediately outer scope", n->str); luaK_code1(fs, OP_PUSHUPVALUE, indexupvalue(ls, &v)); } static void adjust_mult_assign (LexState *ls, int nvars, int nexps) { FuncState *fs = ls->fs; int diff = nexps - nvars; if (nexps > 0 && luaK_lastisopen(fs)) { /* list ends in a function call */ diff--; /* do not count function call itself */ if (diff <= 0) { /* more variables than values? */ luaK_setcallreturns(fs, -diff); /* function call provide extra values */ diff = 0; /* no more difference */ } else /* more values than variables */ luaK_setcallreturns(fs, 0); /* call should provide no value */ } /* push or pop eventual difference between list lengths */ luaK_adjuststack(fs, diff); } static void code_params (LexState *ls, int nparams, int dots) { FuncState *fs = ls->fs; adjustlocalvars(ls, nparams); luaX_checklimit(ls, fs->nactloc, MAXPARAMS, "parameters"); fs->f->numparams = fs->nactloc; /* `self' could be there already */ fs->f->is_vararg = dots; if (dots) { new_localvarstr(ls, "arg", 0); adjustlocalvars(ls, 1); } luaK_deltastack(fs, fs->nactloc); /* count parameters in the stack */ } static void enterbreak (FuncState *fs, Breaklabel *bl) { bl->stacklevel = fs->stacklevel; bl->breaklist = NO_JUMP; bl->previous = fs->bl; fs->bl = bl; } static void leavebreak (FuncState *fs, Breaklabel *bl) { fs->bl = bl->previous; LUA_ASSERT(bl->stacklevel == fs->stacklevel, "wrong levels"); luaK_patchlist(fs, bl->breaklist, luaK_getlabel(fs)); } static void pushclosure (LexState *ls, FuncState *func) { FuncState *fs = ls->fs; Proto *f = fs->f; int i; for (i=0; inupvalues; i++) luaK_tostack(ls, &func->upvalues[i], 1); luaM_growvector(ls->L, f->kproto, f->nkproto, 1, Proto *, "constant table overflow", MAXARG_A); f->kproto[f->nkproto++] = func->f; luaK_code2(fs, OP_CLOSURE, f->nkproto-1, func->nupvalues); } static void open_func (LexState *ls, FuncState *fs) { Proto *f = luaF_newproto(ls->L); fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; fs->L = ls->L; ls->fs = fs; fs->stacklevel = 0; fs->nactloc = 0; fs->nupvalues = 0; fs->bl = NULL; fs->f = f; f->source = ls->source; fs->pc = 0; fs->lasttarget = 0; fs->lastline = 0; fs->jlt = NO_JUMP; f->code = NULL; f->maxstacksize = 0; f->numparams = 0; /* default for main chunk */ f->is_vararg = 0; /* default for main chunk */ } static void close_func (LexState *ls) { lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; luaK_code0(fs, OP_END); luaK_getlabel(fs); /* close eventual list of pending jumps */ luaM_reallocvector(L, f->code, fs->pc, Instruction); luaM_reallocvector(L, f->kstr, f->nkstr, TString *); luaM_reallocvector(L, f->knum, f->nknum, Number); luaM_reallocvector(L, f->kproto, f->nkproto, Proto *); removelocalvars(ls, fs->nactloc); luaM_reallocvector(L, f->locvars, f->nlocvars, LocVar); luaM_reallocvector(L, f->lineinfo, f->nlineinfo+1, int); f->lineinfo[f->nlineinfo++] = MAX_INT; /* end flag */ luaF_protook(L, f, fs->pc); /* proto is ok now */ ls->fs = fs->prev; LUA_ASSERT(fs->bl == NULL, "wrong list end"); } Proto *luaY_parser (lua_State *L, ZIO *z) { struct LexState lexstate; struct FuncState funcstate; luaX_setinput(L, &lexstate, z, luaS_new(L, zname(z))); open_func(&lexstate, &funcstate); next(&lexstate); /* read first token */ chunk(&lexstate); check_condition(&lexstate, (lexstate.t.token == TK_EOS), " expected"); close_func(&lexstate); LUA_ASSERT(funcstate.prev == NULL, "wrong list end"); LUA_ASSERT(funcstate.nupvalues == 0, "no upvalues in main"); return funcstate.f; } /*============================================================*/ /* GRAMMAR RULES */ /*============================================================*/ static int explist1 (LexState *ls) { /* explist1 -> expr { ',' expr } */ int n = 1; /* at least one expression */ expdesc v; expr(ls, &v); while (ls->t.token == ',') { luaK_tostack(ls, &v, 1); /* gets only 1 value from previous expression */ next(ls); /* skip comma */ expr(ls, &v); n++; } luaK_tostack(ls, &v, 0); /* keep open number of values of last expression */ return n; } static void funcargs (LexState *ls, int slf) { FuncState *fs = ls->fs; int slevel = fs->stacklevel - slf - 1; /* where is func in the stack */ switch (ls->t.token) { case '(': { /* funcargs -> '(' [ explist1 ] ')' */ int line = ls->linenumber; int nargs = 0; next(ls); if (ls->t.token != ')') /* arg list not empty? */ nargs = explist1(ls); check_match(ls, ')', '(', line); #ifdef LUA_COMPAT_ARGRET if (nargs > 0) /* arg list is not empty? */ luaK_setcallreturns(fs, 1); /* last call returns only 1 value */ #else UNUSED(nargs); /* to avoid warnings */ #endif break; } case '{': { /* funcargs -> constructor */ constructor(ls); break; } case TK_STRING: { /* funcargs -> STRING */ code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */ next(ls); break; } default: { luaK_error(ls, "function arguments expected"); break; } } fs->stacklevel = slevel; /* call will remove function and arguments */ luaK_code2(fs, OP_CALL, slevel, MULT_RET); } static void var_or_func_tail (LexState *ls, expdesc *v) { for (;;) { switch (ls->t.token) { case '.': { /* var_or_func_tail -> '.' NAME */ next(ls); luaK_tostack(ls, v, 1); /* `v' must be on stack */ luaK_kstr(ls, checkname(ls)); v->k = VINDEXED; break; } case '[': { /* var_or_func_tail -> '[' exp1 ']' */ next(ls); luaK_tostack(ls, v, 1); /* `v' must be on stack */ v->k = VINDEXED; exp1(ls); check(ls, ']'); break; } case ':': { /* var_or_func_tail -> ':' NAME funcargs */ int name; next(ls); name = checkname(ls); luaK_tostack(ls, v, 1); /* `v' must be on stack */ luaK_code1(ls->fs, OP_PUSHSELF, name); funcargs(ls, 1); v->k = VEXP; v->u.l.t = v->u.l.f = NO_JUMP; break; } case '(': case TK_STRING: case '{': { /* var_or_func_tail -> funcargs */ luaK_tostack(ls, v, 1); /* `v' must be on stack */ funcargs(ls, 0); v->k = VEXP; v->u.l.t = v->u.l.f = NO_JUMP; break; } default: return; /* should be follow... */ } } } static void var_or_func (LexState *ls, expdesc *v) { /* var_or_func -> ['%'] NAME var_or_func_tail */ if (optional(ls, '%')) { /* upvalue? */ pushupvalue(ls, str_checkname(ls)); v->k = VEXP; v->u.l.t = v->u.l.f = NO_JUMP; } else /* variable name */ singlevar(ls, str_checkname(ls), v); var_or_func_tail(ls, v); } /* ** {====================================================================== ** Rules for Constructors ** ======================================================================= */ static void recfield (LexState *ls) { /* recfield -> (NAME | '['exp1']') = exp1 */ switch (ls->t.token) { case TK_NAME: { luaK_kstr(ls, checkname(ls)); break; } case '[': { next(ls); exp1(ls); check(ls, ']'); break; } default: luaK_error(ls, " or `[' expected"); } check(ls, '='); exp1(ls); } static int recfields (LexState *ls) { /* recfields -> recfield { ',' recfield } [','] */ FuncState *fs = ls->fs; int n = 1; /* at least one element */ recfield(ls); while (ls->t.token == ',') { next(ls); if (ls->t.token == ';' || ls->t.token == '}') break; recfield(ls); n++; if (n%RFIELDS_PER_FLUSH == 0) luaK_code1(fs, OP_SETMAP, RFIELDS_PER_FLUSH); } luaK_code1(fs, OP_SETMAP, n%RFIELDS_PER_FLUSH); return n; } static int listfields (LexState *ls) { /* listfields -> exp1 { ',' exp1 } [','] */ FuncState *fs = ls->fs; int n = 1; /* at least one element */ exp1(ls); while (ls->t.token == ',') { next(ls); if (ls->t.token == ';' || ls->t.token == '}') break; exp1(ls); n++; luaX_checklimit(ls, n/LFIELDS_PER_FLUSH, MAXARG_A, "`item groups' in a list initializer"); if (n%LFIELDS_PER_FLUSH == 0) luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH); } luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH); return n; } static void constructor_part (LexState *ls, Constdesc *cd) { switch (ls->t.token) { case ';': case '}': { /* constructor_part -> empty */ cd->n = 0; cd->k = ls->t.token; break; } case TK_NAME: { /* may be listfields or recfields */ lookahead(ls); if (ls->lookahead.token != '=') /* expression? */ goto case_default; /* else go through to recfields */ } case '[': { /* constructor_part -> recfields */ cd->n = recfields(ls); cd->k = 1; /* record */ break; } default: { /* constructor_part -> listfields */ case_default: cd->n = listfields(ls); cd->k = 0; /* list */ break; } } } static void constructor (LexState *ls) { /* constructor -> '{' constructor_part [';' constructor_part] '}' */ FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_code1(fs, OP_CREATETABLE, 0); int nelems; Constdesc cd; check(ls, '{'); constructor_part(ls, &cd); nelems = cd.n; if (optional(ls, ';')) { Constdesc other_cd; constructor_part(ls, &other_cd); check_condition(ls, (cd.k != other_cd.k), "invalid constructor syntax"); nelems += other_cd.n; } check_match(ls, '}', '{', line); luaX_checklimit(ls, nelems, MAXARG_U, "elements in a table constructor"); SETARG_U(fs->f->code[pc], nelems); /* set initial table size */ } /* }====================================================================== */ /* ** {====================================================================== ** Expression parsing ** ======================================================================= */ static void simpleexp (LexState *ls, expdesc *v) { FuncState *fs = ls->fs; switch (ls->t.token) { case TK_NUMBER: { /* simpleexp -> NUMBER */ Number r = ls->t.seminfo.r; next(ls); luaK_number(fs, r); break; } case TK_STRING: { /* simpleexp -> STRING */ code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */ next(ls); break; } case TK_NIL: { /* simpleexp -> NIL */ luaK_adjuststack(fs, -1); next(ls); break; } case '{': { /* simpleexp -> constructor */ constructor(ls); break; } case TK_FUNCTION: { /* simpleexp -> FUNCTION body */ next(ls); body(ls, 0, ls->linenumber); break; } case '(': { /* simpleexp -> '(' expr ')' */ next(ls); expr(ls, v); check(ls, ')'); return; } case TK_NAME: case '%': { var_or_func(ls, v); return; } default: { luaK_error(ls, " expected"); return; } } v->k = VEXP; v->u.l.t = v->u.l.f = NO_JUMP; } static void exp1 (LexState *ls) { expdesc v; expr(ls, &v); luaK_tostack(ls, &v, 1); } static UnOpr getunopr (int op) { switch (op) { case TK_NOT: return OPR_NOT; case '-': return OPR_MINUS; default: return OPR_NOUNOPR; } } static BinOpr getbinopr (int op) { switch (op) { case '+': return OPR_ADD; case '-': return OPR_SUB; case '*': return OPR_MULT; case '/': return OPR_DIV; case '^': return OPR_POW; case TK_CONCAT: return OPR_CONCAT; case TK_NE: return OPR_NE; case TK_EQ: return OPR_EQ; case '<': return OPR_LT; case TK_LE: return OPR_LE; case '>': return OPR_GT; case TK_GE: return OPR_GE; case TK_AND: return OPR_AND; case TK_OR: return OPR_OR; default: return OPR_NOBINOPR; } } static const struct { char left; /* left priority for each binary operator */ char right; /* right priority */ } priority[] = { /* ORDER OPR */ {5, 5}, {5, 5}, {6, 6}, {6, 6}, /* arithmetic */ {9, 8}, {4, 3}, /* power and concat (right associative) */ {2, 2}, {2, 2}, /* equality */ {2, 2}, {2, 2}, {2, 2}, {2, 2}, /* order */ {1, 1}, {1, 1} /* logical */ }; #define UNARY_PRIORITY 7 /* priority for unary operators */ /* ** subexpr -> (simplexep | unop subexpr) { binop subexpr } ** where `binop' is any binary operator with a priority higher than `limit' */ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { BinOpr op; UnOpr uop = getunopr(ls->t.token); if (uop != OPR_NOUNOPR) { next(ls); subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls, uop, v); } else simpleexp(ls, v); /* expand while operators have priorities higher than `limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; BinOpr nextop; next(ls); luaK_infix(ls, op, v); /* read sub-expression with higher priority */ nextop = subexpr(ls, &v2, priority[op].right); luaK_posfix(ls, op, v, &v2); op = nextop; } return op; /* return first untreated operator */ } static void expr (LexState *ls, expdesc *v) { subexpr(ls, v, -1); } /* }==================================================================== */ /* ** {====================================================================== ** Rules for Statements ** ======================================================================= */ static int block_follow (int token) { switch (token) { case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_UNTIL: case TK_EOS: return 1; default: return 0; } } static void block (LexState *ls) { /* block -> chunk */ FuncState *fs = ls->fs; int nactloc = fs->nactloc; chunk(ls); luaK_adjuststack(fs, fs->nactloc - nactloc); /* remove local variables */ removelocalvars(ls, fs->nactloc - nactloc); } static int assignment (LexState *ls, expdesc *v, int nvars) { int left = 0; /* number of values left in the stack after assignment */ luaX_checklimit(ls, nvars, MAXVARSLH, "variables in a multiple assignment"); if (ls->t.token == ',') { /* assignment -> ',' NAME assignment */ expdesc nv; next(ls); var_or_func(ls, &nv); check_condition(ls, (nv.k != VEXP), "syntax error"); left = assignment(ls, &nv, nvars+1); } else { /* assignment -> '=' explist1 */ int nexps; check(ls, '='); nexps = explist1(ls); adjust_mult_assign(ls, nvars, nexps); } if (v->k != VINDEXED) luaK_storevar(ls, v); else { /* there may be garbage between table-index and value */ luaK_code2(ls->fs, OP_SETTABLE, left+nvars+2, 1); left += 2; } return left; } static void cond (LexState *ls, expdesc *v) { /* cond -> exp */ expr(ls, v); /* read condition */ luaK_goiftrue(ls->fs, v, 0); } static void whilestat (LexState *ls, int line) { /* whilestat -> WHILE cond DO block END */ FuncState *fs = ls->fs; int while_init = luaK_getlabel(fs); expdesc v; Breaklabel bl; enterbreak(fs, &bl); next(ls); cond(ls, &v); check(ls, TK_DO); block(ls); luaK_patchlist(fs, luaK_jump(fs), while_init); luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs)); check_match(ls, TK_END, TK_WHILE, line); leavebreak(fs, &bl); } static void repeatstat (LexState *ls, int line) { /* repeatstat -> REPEAT block UNTIL cond */ FuncState *fs = ls->fs; int repeat_init = luaK_getlabel(fs); expdesc v; Breaklabel bl; enterbreak(fs, &bl); next(ls); block(ls); check_match(ls, TK_UNTIL, TK_REPEAT, line); cond(ls, &v); luaK_patchlist(fs, v.u.l.f, repeat_init); leavebreak(fs, &bl); } static void forbody (LexState *ls, int nvar, OpCode prepfor, OpCode loopfor) { /* forbody -> DO block END */ FuncState *fs = ls->fs; int prep = luaK_code1(fs, prepfor, NO_JUMP); int blockinit = luaK_getlabel(fs); check(ls, TK_DO); adjustlocalvars(ls, nvar); /* scope for control variables */ block(ls); luaK_patchlist(fs, luaK_code1(fs, loopfor, NO_JUMP), blockinit); luaK_patchlist(fs, prep, luaK_getlabel(fs)); removelocalvars(ls, nvar); } static void fornum (LexState *ls, TString *varname) { /* fornum -> NAME = exp1,exp1[,exp1] forbody */ FuncState *fs = ls->fs; check(ls, '='); exp1(ls); /* initial value */ check(ls, ','); exp1(ls); /* limit */ if (optional(ls, ',')) exp1(ls); /* optional step */ else luaK_code1(fs, OP_PUSHINT, 1); /* default step */ new_localvar(ls, varname, 0); new_localvarstr(ls, "(limit)", 1); new_localvarstr(ls, "(step)", 2); forbody(ls, 3, OP_FORPREP, OP_FORLOOP); } static void forlist (LexState *ls, TString *indexname) { /* forlist -> NAME,NAME IN exp1 forbody */ TString *valname; check(ls, ','); valname = str_checkname(ls); /* next test is dirty, but avoids `in' being a reserved word */ check_condition(ls, (ls->t.token == TK_NAME && ls->t.seminfo.ts == luaS_new(ls->L, "in")), "`in' expected"); next(ls); /* skip `in' */ exp1(ls); /* table */ new_localvarstr(ls, "(table)", 0); new_localvar(ls, indexname, 1); new_localvar(ls, valname, 2); forbody(ls, 3, OP_LFORPREP, OP_LFORLOOP); } static void forstat (LexState *ls, int line) { /* forstat -> fornum | forlist */ FuncState *fs = ls->fs; TString *varname; Breaklabel bl; enterbreak(fs, &bl); next(ls); /* skip `for' */ varname = str_checkname(ls); /* first variable name */ switch (ls->t.token) { case '=': fornum(ls, varname); break; case ',': forlist(ls, varname); break; default: luaK_error(ls, "`=' or `,' expected"); } check_match(ls, TK_END, TK_FOR, line); leavebreak(fs, &bl); } static void test_then_block (LexState *ls, expdesc *v) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ next(ls); /* skip IF or ELSEIF */ cond(ls, v); check(ls, TK_THEN); block(ls); /* `then' part */ } static void ifstat (LexState *ls, int line) { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ FuncState *fs = ls->fs; expdesc v; int escapelist = NO_JUMP; test_then_block(ls, &v); /* IF cond THEN block */ while (ls->t.token == TK_ELSEIF) { luaK_concat(fs, &escapelist, luaK_jump(fs)); luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs)); test_then_block(ls, &v); /* ELSEIF cond THEN block */ } if (ls->t.token == TK_ELSE) { luaK_concat(fs, &escapelist, luaK_jump(fs)); luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs)); next(ls); /* skip ELSE */ block(ls); /* `else' part */ } else luaK_concat(fs, &escapelist, v.u.l.f); luaK_patchlist(fs, escapelist, luaK_getlabel(fs)); check_match(ls, TK_END, TK_IF, line); } static void localstat (LexState *ls) { /* stat -> LOCAL NAME {',' NAME} ['=' explist1] */ int nvars = 0; int nexps; do { next(ls); /* skip LOCAL or ',' */ new_localvar(ls, str_checkname(ls), nvars++); } while (ls->t.token == ','); if (optional(ls, '=')) nexps = explist1(ls); else nexps = 0; adjust_mult_assign(ls, nvars, nexps); adjustlocalvars(ls, nvars); } static int funcname (LexState *ls, expdesc *v) { /* funcname -> NAME [':' NAME | '.' NAME] */ int needself = 0; singlevar(ls, str_checkname(ls), v); if (ls->t.token == ':' || ls->t.token == '.') { needself = (ls->t.token == ':'); next(ls); luaK_tostack(ls, v, 1); luaK_kstr(ls, checkname(ls)); v->k = VINDEXED; } return needself; } static void funcstat (LexState *ls, int line) { /* funcstat -> FUNCTION funcname body */ int needself; expdesc v; next(ls); /* skip FUNCTION */ needself = funcname(ls, &v); body(ls, needself, line); luaK_storevar(ls, &v); } static void namestat (LexState *ls) { /* stat -> func | ['%'] NAME assignment */ FuncState *fs = ls->fs; expdesc v; var_or_func(ls, &v); if (v.k == VEXP) { /* stat -> func */ check_condition(ls, luaK_lastisopen(fs), "syntax error"); /* an upvalue? */ luaK_setcallreturns(fs, 0); /* call statement uses no results */ } else { /* stat -> ['%'] NAME assignment */ int left = assignment(ls, &v, 1); luaK_adjuststack(fs, left); /* remove eventual garbage left on stack */ } } static void retstat (LexState *ls) { /* stat -> RETURN explist */ FuncState *fs = ls->fs; next(ls); /* skip RETURN */ if (!block_follow(ls->t.token) && ls->t.token != ';') explist1(ls); /* optional return values */ luaK_code1(fs, OP_RETURN, ls->fs->nactloc); fs->stacklevel = fs->nactloc; /* removes all temp values */ } static void breakstat (LexState *ls) { /* stat -> BREAK [NAME] */ FuncState *fs = ls->fs; int currentlevel = fs->stacklevel; Breaklabel *bl = fs->bl; if (!bl) luaK_error(ls, "no loop to break"); next(ls); /* skip BREAK */ luaK_adjuststack(fs, currentlevel - bl->stacklevel); luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); /* correct stack for compiler and symbolic execution */ luaK_adjuststack(fs, bl->stacklevel - currentlevel); } static int luastat (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ switch (ls->t.token) { case TK_IF: { /* stat -> ifstat */ ifstat(ls, line); return 0; } case TK_WHILE: { /* stat -> whilestat */ whilestat(ls, line); return 0; } case TK_DO: { /* stat -> DO block END */ next(ls); /* skip DO */ block(ls); check_match(ls, TK_END, TK_DO, line); return 0; } case TK_FOR: { /* stat -> forstat */ forstat(ls, line); return 0; } case TK_REPEAT: { /* stat -> repeatstat */ repeatstat(ls, line); return 0; } case TK_FUNCTION: { /* stat -> funcstat */ funcstat(ls, line); return 0; } case TK_LOCAL: { /* stat -> localstat */ localstat(ls); return 0; } case TK_NAME: case '%': { /* stat -> namestat */ namestat(ls); return 0; } case TK_RETURN: { /* stat -> retstat */ retstat(ls); return 1; /* must be last statement */ } case TK_BREAK: { /* stat -> breakstat */ breakstat(ls); return 1; /* must be last statement */ } default: { luaK_error(ls, " expected"); return 0; /* to avoid warnings */ } } } static void parlist (LexState *ls) { /* parlist -> [ param { ',' param } ] */ int nparams = 0; int dots = 0; if (ls->t.token != ')') { /* is `parlist' not empty? */ do { switch (ls->t.token) { case TK_DOTS: next(ls); dots = 1; break; case TK_NAME: new_localvar(ls, str_checkname(ls), nparams++); break; default: luaK_error(ls, " or `...' expected"); } } while (!dots && optional(ls, ',')); } code_params(ls, nparams, dots); } static void body (LexState *ls, int needself, int line) { /* body -> '(' parlist ')' chunk END */ FuncState new_fs; open_func(ls, &new_fs); new_fs.f->lineDefined = line; check(ls, '('); if (needself) { new_localvarstr(ls, "self", 0); adjustlocalvars(ls, 1); } parlist(ls); check(ls, ')'); chunk(ls); check_match(ls, TK_END, TK_FUNCTION, line); close_func(ls); pushclosure(ls, &new_fs); } /* }====================================================================== */ static void chunk (LexState *ls) { /* chunk -> { stat [';'] } */ int islast = 0; while (!islast && !block_follow(ls->t.token)) { islast = luastat(ls); optional(ls, ';'); LUA_ASSERT(ls->fs->stacklevel == ls->fs->nactloc, "stack size != # local vars"); } } zangband/src/lua/lstate.c0000644000000000000000000000621010250356275014354 0ustar rootroot/* ** $Id: lstate.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Global State ** See Copyright Notice in lua.h */ #include #include "lua.h" #include "ldo.h" #include "lgc.h" #include "llex.h" #include "lmem.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #ifdef LUA_DEBUG static lua_State *lua_state = NULL; void luaB_opentests (lua_State *L); #endif /* ** built-in implementation for ERRORMESSAGE. In a "correct" environment ** ERRORMESSAGE should have an external definition, and so this function ** would not be used. */ static int errormessage (lua_State *L) { const char *s = lua_tostring(L, 1); if (s == NULL) s = "(no message)"; fprintf(stderr, "error: %s\n", s); return 0; } /* ** open parts that may cause memory-allocation errors */ static void f_luaopen (lua_State *L, void *ud) { int stacksize = *(int *)ud; if (stacksize == 0) stacksize = DEFAULT_STACK_SIZE; else stacksize += LUA_MINSTACK; L->gt = luaH_new(L, 10); /* table of globals */ luaD_init(L, stacksize); luaS_init(L); luaX_init(L); luaT_init(L); lua_newtable(L); lua_ref(L, 1); /* create registry */ lua_register(L, LUA_ERRORMESSAGE, errormessage); #ifdef LUA_DEBUG luaB_opentests(L); if (lua_state == NULL) lua_state = L; /* keep first state to be opened */ #endif LUA_ASSERT(lua_gettop(L) == 0, "wrong API stack"); } LUA_API lua_State *lua_open (int stacksize) { lua_State *L = luaM_new(NULL, lua_State); if (L == NULL) return NULL; /* memory allocation error */ L->stack = NULL; L->strt.size = L->udt.size = 0; L->strt.nuse = L->udt.nuse = 0; L->strt.hash = NULL; L->udt.hash = NULL; L->Mbuffer = NULL; L->Mbuffsize = 0; L->rootproto = NULL; L->rootcl = NULL; L->roottable = NULL; L->TMtable = NULL; L->last_tag = -1; L->refArray = NULL; L->refSize = 0; L->refFree = NONEXT; L->nblocks = sizeof(lua_State); L->GCthreshold = MAX_INT; /* to avoid GC during pre-definitions */ L->callhook = NULL; L->linehook = NULL; L->allowhooks = 1; L->errorJmp = NULL; if (luaD_runprotected(L, f_luaopen, &stacksize) != 0) { /* memory allocation error: free partial state */ lua_close(L); return NULL; } L->GCthreshold = 2*L->nblocks; return L; } LUA_API void lua_close (lua_State *L) { LUA_ASSERT(L != lua_state || lua_gettop(L) == 0, "garbage in C stack"); luaC_collect(L, 1); /* collect all elements */ LUA_ASSERT(L->rootproto == NULL, "list should be empty"); LUA_ASSERT(L->rootcl == NULL, "list should be empty"); LUA_ASSERT(L->roottable == NULL, "list should be empty"); luaS_freeall(L); if (L->stack) L->nblocks -= (L->stack_last - L->stack + 1)*sizeof(TObject); luaM_free(L, L->stack); L->nblocks -= (L->last_tag+1)*sizeof(struct TM); luaM_free(L, L->TMtable); L->nblocks -= (L->refSize)*sizeof(struct Ref); luaM_free(L, L->refArray); L->nblocks -= (L->Mbuffsize)*sizeof(char); luaM_free(L, L->Mbuffer); LUA_ASSERT(L->nblocks == sizeof(lua_State), "wrong count for nblocks"); luaM_free(L, L); LUA_ASSERT(L != lua_state || memdebug_numblocks == 0, "memory leak!"); LUA_ASSERT(L != lua_state || memdebug_total == 0,"memory leak!"); } zangband/src/lua/lstring.c0000644000000000000000000001011010250356275014534 0ustar rootroot/* ** $Id: lstring.c,v 1.2 2002/08/29 19:06:49 rr9 Exp $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ #include #include "lua.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" /* ** type equivalent to TString, but with maximum alignment requirements */ union L_UTString { TString ts; union L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ }; void luaS_init (lua_State *L) { L->strt.hash = luaM_newvector(L, 1, TString *); L->udt.hash = luaM_newvector(L, 1, TString *); L->nblocks += 2*sizeof(TString *); L->strt.size = L->udt.size = 1; L->strt.nuse = L->udt.nuse = 0; L->strt.hash[0] = L->udt.hash[0] = NULL; } void luaS_freeall (lua_State *L) { LUA_ASSERT(L->strt.nuse==0, "non-empty string table"); L->nblocks -= (L->strt.size + L->udt.size)*sizeof(TString *); luaM_free(L, L->strt.hash); LUA_ASSERT(L->udt.nuse==0, "non-empty udata table"); luaM_free(L, L->udt.hash); } static unsigned long hash_s (const char *s, size_t l) { unsigned long h = l; /* seed */ size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */ for (; l>=step; l-=step) h = h ^ ((h<<5)+(h>>2)+(unsigned char)*(s++)); return h; } void luaS_resize (lua_State *L, stringtable *tb, int newsize) { TString **newhash = luaM_newvector(L, newsize, TString *); int i; for (i=0; isize; i++) { TString *p = tb->hash[i]; while (p) { /* for each node in the list */ TString *next = p->nexthash; /* save next */ unsigned long h = (tb == &L->strt) ? p->u.s.hash : IntPoint(p->u.d.value); int h1 = h&(newsize-1); /* new position */ LUA_ASSERT(h%newsize == (h&(newsize-1)), "a&(x-1) == a%x, for x power of 2"); p->nexthash = newhash[h1]; /* chain it in new position */ newhash[h1] = p; p = next; } } luaM_free(L, tb->hash); L->nblocks += (newsize - tb->size)*sizeof(TString *); tb->size = newsize; tb->hash = newhash; } static void newentry (lua_State *L, stringtable *tb, TString *ts, int h) { ts->nexthash = tb->hash[h]; /* chain new entry */ tb->hash[h] = ts; tb->nuse++; if (tb->nuse > (lint32)tb->size && tb->size < MAX_INT/2) /* too crowded? */ luaS_resize(L, tb, tb->size*2); } TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { unsigned long h = hash_s(str, l); int h1 = h & (L->strt.size-1); TString *ts; for (ts = L->strt.hash[h1]; ts; ts = ts->nexthash) { if (ts->len == l && (memcmp(str, ts->str, l) == 0)) return ts; } /* not found */ ts = (TString *)luaM_malloc(L, sizestring(l)); ts->marked = 0; ts->nexthash = NULL; ts->len = l; ts->u.s.hash = h; ts->u.s.constindex = 0; memcpy(ts->str, str, l); ts->str[l] = 0; /* ending 0 */ L->nblocks += sizestring(l); newentry(L, &L->strt, ts, h1); /* insert it on table */ return ts; } TString *luaS_newudata (lua_State *L, size_t s, void *udata) { union L_UTString *uts = (union L_UTString *)luaM_malloc(L, (lint32)sizeof(union L_UTString)+s); TString *ts = &uts->ts; ts->marked = 0; ts->nexthash = NULL; ts->len = s; ts->u.d.tag = 0; ts->u.d.value = (s > 0) ? uts+1 : udata; L->nblocks += sizestring(s); /* insert it on table */ newentry(L, &L->udt, ts, IntPoint(ts->u.d.value) & (L->udt.size-1)); return ts; } TString *luaS_createudata (lua_State *L, void *udata, int tag) { int h1 = IntPoint(udata) & (L->udt.size-1); TString *ts; for (ts = L->udt.hash[h1]; ts; ts = ts->nexthash) { if (udata == ts->u.d.value && (tag == ts->u.d.tag || tag == LUA_ANYTAG)) return ts; } /* not found */ ts = luaS_newudata(L, 0, udata); if (tag != LUA_ANYTAG) ts->u.d.tag = tag; return ts; } TString *luaS_new (lua_State *L, const char *str) { return luaS_newlstr(L, str, strlen(str)); } TString *luaS_newfixed (lua_State *L, const char *str) { TString *ts = luaS_new(L, str); if (ts->marked == 0) ts->marked = FIXMARK; /* avoid GC */ return ts; } zangband/src/lua/lstrlib.c0000644000000000000000000004200210250356275014532 0ustar rootroot/* ** $Id: lstrlib.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" static int str_len (lua_State *L) { size_t l; luaL_check_lstr(L, 1, &l); lua_pushnumber(L, l); return 1; } static long posrelat (long pos, size_t len) { /* relative string position: negative means back from end */ return (pos>=0) ? pos : (long)len+pos+1; } static int str_sub (lua_State *L) { size_t l; const char *s = luaL_check_lstr(L, 1, &l); long start = posrelat(luaL_check_long(L, 2), l); long end = posrelat(luaL_opt_long(L, 3, -1), l); if (start < 1) start = 1; if (end > (long)l) end = l; if (start <= end) lua_pushlstring(L, s+start-1, end-start+1); else lua_pushstring(L, ""); return 1; } static int str_lower (lua_State *L) { size_t l; size_t i; luaL_Buffer b; const char *s = luaL_check_lstr(L, 1, &l); luaL_buffinit(L, &b); for (i=0; i 0) luaL_addlstring(&b, s, l); luaL_pushresult(&b); return 1; } static int str_byte (lua_State *L) { size_t l; const char *s = luaL_check_lstr(L, 1, &l); long pos = posrelat(luaL_opt_long(L, 2, 1), l); luaL_arg_check(L, 0level && cap->capture[l].len != -1)) lua_error(L, "invalid capture index"); return l; } static int capture_to_close (lua_State *L, struct Capture *cap) { int level = cap->level; for (level--; level>=0; level--) if (cap->capture[level].len == -1) return level; lua_error(L, "invalid pattern capture"); return 0; /* to avoid warnings */ } const char *luaI_classend (lua_State *L, const char *p) { switch (*p++) { case ESC: if (*p == '\0') lua_error(L, "malformed pattern (ends with `%')"); return p+1; case '[': if (*p == '^') p++; do { /* look for a ']' */ if (*p == '\0') lua_error(L, "malformed pattern (missing `]')"); if (*(p++) == ESC && *p != '\0') p++; /* skip escapes (e.g. '%]') */ } while (*p != ']'); return p+1; default: return p; } } static int match_class (int c, int cl) { int res; switch (tolower(cl)) { case 'a' : res = isalpha(c); break; case 'c' : res = iscntrl(c); break; case 'd' : res = isdigit(c); break; case 'l' : res = islower(c); break; case 'p' : res = ispunct(c); break; case 's' : res = isspace(c); break; case 'u' : res = isupper(c); break; case 'w' : res = isalnum(c); break; case 'x' : res = isxdigit(c); break; case 'z' : res = (c == '\0'); break; default: return (cl == c); } return (islower(cl) ? res : !res); } static int matchbracketclass (int c, const char *p, const char *endclass) { int sig = 1; if (*(p+1) == '^') { sig = 0; p++; /* skip the '^' */ } while (++p < endclass) { if (*p == ESC) { p++; if (match_class(c, (unsigned char)*p)) return sig; } else if ((*(p+1) == '-') && (p+2 < endclass)) { p+=2; if ((int)(unsigned char)*(p-2) <= c && c <= (int)(unsigned char)*p) return sig; } else if ((int)(unsigned char)*p == c) return sig; } return !sig; } int luaI_singlematch (int c, const char *p, const char *ep) { switch (*p) { case '.': /* matches any char */ return 1; case ESC: return match_class(c, (unsigned char)*(p+1)); case '[': return matchbracketclass(c, p, ep-1); default: return ((unsigned char)*p == c); } } static const char *match (lua_State *L, const char *s, const char *p, struct Capture *cap); static const char *matchbalance (lua_State *L, const char *s, const char *p, struct Capture *cap) { if (*p == 0 || *(p+1) == 0) lua_error(L, "unbalanced pattern"); if (*s != *p) return NULL; else { int b = *p; int e = *(p+1); int cont = 1; while (++s < cap->src_end) { if (*s == e) { if (--cont == 0) return s+1; } else if (*s == b) cont++; } } return NULL; /* string ends out of balance */ } static const char *max_expand (lua_State *L, const char *s, const char *p, const char *ep, struct Capture *cap) { long i = 0; /* counts maximum expand for item */ while ((s+i)src_end && luaI_singlematch((unsigned char)*(s+i), p, ep)) i++; /* keeps trying to match with the maximum repetitions */ while (i>=0) { const char *res = match(L, (s+i), ep+1, cap); if (res) return res; i--; /* else didn't match; reduce 1 repetition to try again */ } return NULL; } static const char *min_expand (lua_State *L, const char *s, const char *p, const char *ep, struct Capture *cap) { for (;;) { const char *res = match(L, s, ep+1, cap); if (res != NULL) return res; else if (ssrc_end && luaI_singlematch((unsigned char)*s, p, ep)) s++; /* try with one more repetition */ else return NULL; } } static const char *start_capture (lua_State *L, const char *s, const char *p, struct Capture *cap) { const char *res; int level = cap->level; if (level >= MAX_CAPTURES) lua_error(L, "too many captures"); cap->capture[level].init = s; cap->capture[level].len = -1; cap->level = level+1; if ((res=match(L, s, p+1, cap)) == NULL) /* match failed? */ cap->level--; /* undo capture */ return res; } static const char *end_capture (lua_State *L, const char *s, const char *p, struct Capture *cap) { int l = capture_to_close(L, cap); const char *res; cap->capture[l].len = s - cap->capture[l].init; /* close capture */ if ((res = match(L, s, p+1, cap)) == NULL) /* match failed? */ cap->capture[l].len = -1; /* undo capture */ return res; } static const char *match_capture (lua_State *L, const char *s, int level, struct Capture *cap) { int l = check_capture(L, level, cap); size_t len = cap->capture[l].len; if ((size_t)(cap->src_end-s) >= len && memcmp(cap->capture[l].init, s, len) == 0) return s+len; else return NULL; } static const char *match (lua_State *L, const char *s, const char *p, struct Capture *cap) { init: /* using goto's to optimize tail recursion */ switch (*p) { case '(': /* start capture */ return start_capture(L, s, p, cap); case ')': /* end capture */ return end_capture(L, s, p, cap); case ESC: /* may be %[0-9] or %b */ if (isdigit((unsigned char)(*(p+1)))) { /* capture? */ s = match_capture(L, s, *(p+1), cap); if (s == NULL) return NULL; p+=2; goto init; /* else return match(L, s, p+2, cap) */ } else if (*(p+1) == 'b') { /* balanced string? */ s = matchbalance(L, s, p+2, cap); if (s == NULL) return NULL; p+=4; goto init; /* else return match(L, s, p+4, cap); */ } else goto dflt; /* case default */ case '\0': /* end of pattern */ return s; /* match succeeded */ case '$': if (*(p+1) == '\0') /* is the '$' the last char in pattern? */ return (s == cap->src_end) ? s : NULL; /* check end of string */ else goto dflt; default: dflt: { /* it is a pattern item */ const char *ep = luaI_classend(L, p); /* points to what is next */ int m = ssrc_end && luaI_singlematch((unsigned char)*s, p, ep); switch (*ep) { case '?': { /* optional */ const char *res; if (m && ((res=match(L, s+1, ep+1, cap)) != NULL)) return res; p=ep+1; goto init; /* else return match(L, s, ep+1, cap); */ } case '*': /* 0 or more repetitions */ return max_expand(L, s, p, ep, cap); case '+': /* 1 or more repetitions */ return (m ? max_expand(L, s+1, p, ep, cap) : NULL); case '-': /* 0 or more repetitions (minimum) */ return min_expand(L, s, p, ep, cap); default: if (!m) return NULL; s++; p=ep; goto init; /* else return match(L, s+1, ep, cap); */ } } } } static const char *lmemfind (const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1; /* empty strings are everywhere */ else if (l2 > l1) return NULL; /* avoids a negative `l1' */ else { const char *init; /* to search for a `*s2' inside `s1' */ l2--; /* 1st char will be checked by `memchr' */ l1 = l1-l2; /* `s2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++; /* 1st char is already checked */ if (memcmp(init, s2+1, l2) == 0) return init-1; else { /* correct `l1' and `s1' to try again */ l1 -= init-s1; s1 = init; } } return NULL; /* not found */ } } static int push_captures (lua_State *L, struct Capture *cap) { int i; luaL_checkstack(L, cap->level, "too many captures"); for (i=0; ilevel; i++) { int l = cap->capture[i].len; if (l == -1) lua_error(L, "unfinished capture"); lua_pushlstring(L, cap->capture[i].init, l); } return cap->level; /* number of strings pushed */ } static int str_find (lua_State *L) { size_t l1, l2; const char *s = luaL_check_lstr(L, 1, &l1); const char *p = luaL_check_lstr(L, 2, &l2); long init = posrelat(luaL_opt_long(L, 3, 1), l1) - 1; struct Capture cap; luaL_arg_check(L, 0 <= init && (size_t)init <= l1, 3, "out of range"); if (lua_gettop(L) > 3 || /* extra argument? */ strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ const char *s2 = lmemfind(s+init, l1-init, p, l2); if (s2) { lua_pushnumber(L, s2-s+1); lua_pushnumber(L, s2-s+l2); return 2; } } else { int anchor = (*p == '^') ? (p++, 1) : 0; const char *s1=s+init; cap.src_end = s+l1; do { const char *res; cap.level = 0; if ((res=match(L, s1, p, &cap)) != NULL) { lua_pushnumber(L, s1-s+1); /* start */ lua_pushnumber(L, res-s); /* end */ return push_captures(L, &cap) + 2; } } while (s1++capture[level].init, cap->capture[level].len); } } } } else { /* is a function */ int n; lua_pushvalue(L, 3); n = push_captures(L, cap); lua_rawcall(L, n, 1); if (lua_isstring(L, -1)) luaL_addvalue(b); /* add return to accumulated result */ else lua_pop(L, 1); /* function result is not a string: pop it */ } } static int str_gsub (lua_State *L) { size_t srcl; const char *src = luaL_check_lstr(L, 1, &srcl); const char *p = luaL_check_string(L, 2); int max_s = luaL_opt_int(L, 4, srcl+1); int anchor = (*p == '^') ? (p++, 1) : 0; int n = 0; struct Capture cap; luaL_Buffer b; luaL_arg_check(L, lua_gettop(L) >= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), 3, "string or function expected"); luaL_buffinit(L, &b); cap.src_end = src+srcl; while (n < max_s) { const char *e; cap.level = 0; e = match(L, src, p, &cap); if (e) { n++; add_s(L, &b, &cap); } if (e && e>src) /* non empty match? */ src = e; /* skip it */ else if (src < cap.src_end) luaL_putchar(&b, *src++); else break; if (anchor) break; } luaL_addlstring(&b, src, cap.src_end-src); luaL_pushresult(&b); lua_pushnumber(L, n); /* number of substitutions */ return 2; } /* }====================================================== */ static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { size_t l; const char *s = luaL_check_lstr(L, arg, &l); luaL_putchar(b, '"'); while (l--) { switch (*s) { case '"': case '\\': case '\n': luaL_putchar(b, '\\'); luaL_putchar(b, *s); break; case '\0': luaL_addlstring(b, "\\000", 4); break; default: luaL_putchar(b, *s); } s++; } luaL_putchar(b, '"'); } /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ #define MAX_ITEM 512 /* maximum size of each format specification (such as '%-099.99d') */ #define MAX_FORMAT 20 static int str_format (lua_State *L) { int arg = 1; const char *strfrmt = luaL_check_string(L, arg); luaL_Buffer b; luaL_buffinit(L, &b); while (*strfrmt) { if (*strfrmt != '%') luaL_putchar(&b, *strfrmt++); else if (*++strfrmt == '%') luaL_putchar(&b, *strfrmt++); /* %% */ else { /* format item */ struct Capture cap; char form[MAX_FORMAT]; /* to store the format ('%...') */ char buff[MAX_ITEM]; /* to store the formatted item */ const char *initf = strfrmt; form[0] = '%'; if (isdigit((unsigned char)*initf) && *(initf+1) == '$') { arg = *initf - '0'; initf += 2; /* skip the 'n$' */ } arg++; cap.src_end = strfrmt+strlen(strfrmt)+1; cap.level = 0; strfrmt = match(L, initf, "[-+ #0]*(%d*)%.?(%d*)", &cap); if (cap.capture[0].len > 2 || cap.capture[1].len > 2 || /* < 100? */ strfrmt-initf > MAX_FORMAT-2) lua_error(L, "invalid format (width or precision too long)"); strncpy(form+1, initf, strfrmt-initf+1); /* +1 to include conversion */ form[strfrmt-initf+2] = 0; switch (*strfrmt++) { case 'c': case 'd': case 'i': sprintf(buff, form, luaL_check_int(L, arg)); break; case 'o': case 'u': case 'x': case 'X': sprintf(buff, form, (unsigned int)luaL_check_number(L, arg)); break; case 'e': case 'E': case 'f': case 'g': case 'G': sprintf(buff, form, luaL_check_number(L, arg)); break; case 'q': luaI_addquoted(L, &b, arg); continue; /* skip the "addsize" at the end */ case 's': { size_t l; const char *s = luaL_check_lstr(L, arg, &l); if (cap.capture[1].len == 0 && l >= 100) { /* no precision and string is too long to be formatted; keep original string */ lua_pushvalue(L, arg); luaL_addvalue(&b); continue; /* skip the "addsize" at the end */ } else { sprintf(buff, form, s); break; } } default: /* also treat cases 'pnLlh' */ lua_error(L, "invalid option in `format'"); } luaL_addlstring(&b, buff, strlen(buff)); } } luaL_pushresult(&b); return 1; } static const struct luaL_reg strlib[] = { {"strlen", str_len}, {"strsub", str_sub}, {"strlower", str_lower}, {"strupper", str_upper}, {"strchar", str_char}, {"strrep", str_rep}, {"ascii", str_byte}, /* for compatibility with 3.0 and earlier */ {"strbyte", str_byte}, {"format", str_format}, {"strfind", str_find}, {"gsub", str_gsub} }; /* ** Open string library */ LUALIB_API void lua_strlibopen (lua_State *L) { luaL_openl(L, strlib); } zangband/src/lua/ltable.c0000644000000000000000000002067310250356275014334 0ustar rootroot/* ** $Id: ltable.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ /* ** Implementation of tables (aka arrays, objects, or hash tables); ** uses a mix of chained scatter table with Brent's variation. ** A main invariant of these tables is that, if an element is not ** in its main position (i.e. the `original' position that its hash gives ** to it), then the colliding element is in its own main position. ** In other words, there are collisions only when two elements have the ** same main position (i.e. the same hash values for that table size). ** Because of that, the load factor of these tables can be 100% without ** performance penalties. */ #include "lua.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #define gcsize(L, n) (sizeof(Hash)+(n)*sizeof(Node)) #define TagDefault LUA_TTABLE /* ** returns the `main' position of an element in a table (that is, the index ** of its hash value) */ Node *luaH_mainposition (const Hash *t, const TObject *key) { unsigned long h; switch (ttype(key)) { case LUA_TNUMBER: h = (unsigned long)(long)nvalue(key); break; case LUA_TSTRING: h = tsvalue(key)->u.s.hash; break; case LUA_TUSERDATA: h = IntPoint(tsvalue(key)); break; case LUA_TTABLE: h = IntPoint(hvalue(key)); break; case LUA_TFUNCTION: h = IntPoint(clvalue(key)); break; default: return NULL; /* invalid key */ } LUA_ASSERT(h%(unsigned int)t->size == (h&((unsigned int)t->size-1)), "a&(x-1) == a%x, for x power of 2"); return &t->node[h&(t->size-1)]; } static const TObject *luaH_getany (lua_State *L, const Hash *t, const TObject *key) { Node *n = luaH_mainposition(t, key); if (!n) lua_error(L, "table index is nil"); else do { if (luaO_equalObj(key, &n->key)) return &n->val; n = n->next; } while (n); return &luaO_nilobject; /* key not found */ } /* specialized version for numbers */ const TObject *luaH_getnum (const Hash *t, Number key) { Node *n = &t->node[(unsigned long)(long)key&(t->size-1)]; do { if (ttype(&n->key) == LUA_TNUMBER && nvalue(&n->key) == key) return &n->val; n = n->next; } while (n); return &luaO_nilobject; /* key not found */ } /* specialized version for strings */ const TObject *luaH_getstr (const Hash *t, TString *key) { Node *n = &t->node[key->u.s.hash&(t->size-1)]; do { if (ttype(&n->key) == LUA_TSTRING && tsvalue(&n->key) == key) return &n->val; n = n->next; } while (n); return &luaO_nilobject; /* key not found */ } const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key) { switch (ttype(key)) { case LUA_TNUMBER: return luaH_getnum(t, nvalue(key)); case LUA_TSTRING: return luaH_getstr(t, tsvalue(key)); default: return luaH_getany(L, t, key); } } Node *luaH_next (lua_State *L, const Hash *t, const TObject *key) { int i; if (ttype(key) == LUA_TNIL) i = 0; /* first iteration */ else { const TObject *v = luaH_get(L, t, key); if (v == &luaO_nilobject) lua_error(L, "invalid key for `next'"); i = (int)(((const char *)v - (const char *)(&t->node[0].val)) / sizeof(Node)) + 1; } for (; isize; i++) { Node *n = node(t, i); if (ttype(val(n)) != LUA_TNIL) return n; } return NULL; /* no more elements */ } /* ** try to remove a key without value from a table. To avoid problems with ** hash, change `key' for a number with the same hash. */ void luaH_remove (Hash *t, TObject *key) { if (ttype(key) == LUA_TNUMBER || (ttype(key) == LUA_TSTRING && tsvalue(key)->len <= 30)) return; /* do not remove numbers nor small strings */ else { /* try to find a number `n' with the same hash as `key' */ Node *mp = luaH_mainposition(t, key); int n = mp - &t->node[0]; /* make sure `n' is not in `t' */ while (luaH_getnum(t, n) != &luaO_nilobject) { if (n >= MAX_INT - t->size) return; /* give up; (to avoid overflow) */ n += t->size; } ttype(key) = LUA_TNUMBER; nvalue(key) = n; LUA_ASSERT(luaH_mainposition(t, key) == mp, "cannot change hash"); } } static void setnodevector (lua_State *L, Hash *t, lint32 size) { int i; if (size > MAX_INT) lua_error(L, "table overflow"); t->node = luaM_newvector(L, size, Node); for (i=0; i<(int)size; i++) { ttype(&t->node[i].key) = ttype(&t->node[i].val) = LUA_TNIL; t->node[i].next = NULL; } L->nblocks += gcsize(L, size) - gcsize(L, t->size); t->size = size; t->firstfree = &t->node[size-1]; /* first free position to be used */ } Hash *luaH_new (lua_State *L, int size) { Hash *t = luaM_new(L, Hash); t->htag = TagDefault; t->next = L->roottable; L->roottable = t; t->mark = t; t->size = 0; L->nblocks += gcsize(L, 0); t->node = NULL; setnodevector(L, t, luaO_power2(size)); return t; } void luaH_free (lua_State *L, Hash *t) { L->nblocks -= gcsize(L, t->size); luaM_free(L, t->node); luaM_free(L, t); } static int numuse (const Hash *t) { Node *v = t->node; int size = t->size; int realuse = 0; int i; for (i=0; isize; Node *nold = t->node; int nelems = numuse(t); int i; LUA_ASSERT(nelems<=oldsize, "wrong count"); if (nelems >= oldsize-oldsize/4) /* using more than 3/4? */ setnodevector(L, t, (lint32)oldsize*2); else if (nelems <= oldsize/4 && /* less than 1/4? */ oldsize > MINPOWER2) setnodevector(L, t, oldsize/2); else setnodevector(L, t, oldsize); for (i=0; ival) != LUA_TNIL) *luaH_set(L, t, &old->key) = old->val; } luaM_free(L, nold); /* free old array */ } /* ** inserts a key into a hash table; first, check whether key is ** already present; if not, check whether key's main position is free; ** if not, check whether colliding node is in its main position or not; ** if it is not, move colliding node to an empty place and put new key ** in its main position; otherwise (colliding node is in its main position), ** new key goes to an empty position. */ TObject *luaH_set (lua_State *L, Hash *t, const TObject *key) { Node *mp = luaH_mainposition(t, key); Node *n = mp; if (!mp) lua_error(L, "table index is nil"); do { /* check whether `key' is somewhere in the chain */ if (luaO_equalObj(key, &n->key)) return &n->val; /* that's all */ else n = n->next; } while (n); /* `key' not found; must insert it */ if (ttype(&mp->key) != LUA_TNIL) { /* main position is not free? */ Node *othern; /* main position of colliding node */ n = t->firstfree; /* get a free place */ /* is colliding node out of its main position? (can only happens if its position is after "firstfree") */ if (mp > n && (othern=luaH_mainposition(t, &mp->key)) != mp) { /* yes; move colliding node into free position */ while (othern->next != mp) othern = othern->next; /* find previous */ othern->next = n; /* redo the chain with `n' in place of `mp' */ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ mp->next = NULL; /* now `mp' is free */ } else { /* colliding node is in its own main position */ /* new node will go into free position */ n->next = mp->next; /* chain new position */ mp->next = n; mp = n; } } mp->key = *key; for (;;) { /* correct `firstfree' */ if (ttype(&t->firstfree->key) == LUA_TNIL) return &mp->val; /* OK; table still has a free place */ else if (t->firstfree == t->node) break; /* cannot decrement from here */ else (t->firstfree)--; } rehash(L, t); /* no more free places */ return luaH_set(L, t, key); /* `rehash' invalidates this insertion */ } TObject *luaH_setint (lua_State *L, Hash *t, int key) { TObject index; ttype(&index) = LUA_TNUMBER; nvalue(&index) = key; return luaH_set(L, t, &index); } void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val) { TObject *value, index; ttype(&index) = LUA_TSTRING; tsvalue(&index) = key; value = luaH_set(L, t, &index); ttype(value) = LUA_TNUMBER; nvalue(value) = val; } const TObject *luaH_getglobal (lua_State *L, const char *name) { return luaH_getstr(L->gt, luaS_new(L, name)); } zangband/src/lua/ltests.c0000644000000000000000000003125510250356275014405 0ustar rootroot/* ** $Id: ltests.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Internal Module for Debugging of the Lua Implementation ** See Copyright Notice in lua.h */ #include #include #include #include #include "lua.h" #include "lapi.h" #include "lauxlib.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lmem.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "luadebug.h" #include "lualib.h" void luaB_opentests (lua_State *L); /* ** The whole module only makes sense with LUA_DEBUG on */ #ifdef LUA_DEBUG static void setnameval (lua_State *L, const char *name, int val) { lua_pushstring(L, name); lua_pushnumber(L, val); lua_settable(L, -3); } /* ** {====================================================== ** Disassembler ** ======================================================= */ static const char *const instrname[NUM_OPCODES] = { "END", "RETURN", "CALL", "TAILCALL", "PUSHNIL", "POP", "PUSHINT", "PUSHSTRING", "PUSHNUM", "PUSHNEGNUM", "PUSHUPVALUE", "GETLOCAL", "GETGLOBAL", "GETTABLE", "GETDOTTED", "GETINDEXED", "PUSHSELF", "CREATETABLE", "SETLOCAL", "SETGLOBAL", "SETTABLE", "SETLIST", "SETMAP", "ADD", "ADDI", "SUB", "MULT", "DIV", "POW", "CONCAT", "MINUS", "NOT", "JMPNE", "JMPEQ", "JMPLT", "JMPLE", "JMPGT", "JMPGE", "JMPT", "JMPF", "JMPONT", "JMPONF", "JMP", "PUSHNILJMP", "FORPREP", "FORLOOP", "LFORPREP", "LFORLOOP", "CLOSURE" }; static int pushop (lua_State *L, Proto *p, int pc) { char buff[100]; Instruction i = p->code[pc]; OpCode o = GET_OPCODE(i); const char *name = instrname[o]; sprintf(buff, "%5d - ", luaG_getline(p->lineinfo, pc, 1, NULL)); switch ((enum Mode)luaK_opproperties[o].mode) { case iO: sprintf(buff+8, "%-12s", name); break; case iU: sprintf(buff+8, "%-12s%4u", name, GETARG_U(i)); break; case iS: sprintf(buff+8, "%-12s%4d", name, GETARG_S(i)); break; case iAB: sprintf(buff+8, "%-12s%4d %4d", name, GETARG_A(i), GETARG_B(i)); break; } lua_pushstring(L, buff); return (o != OP_END); } static int listcode (lua_State *L) { int pc; Proto *p; int res; luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); p = clvalue(luaA_index(L, 1))->f.l; lua_newtable(L); setnameval(L, "maxstack", p->maxstacksize); setnameval(L, "numparams", p->numparams); pc = 0; do { lua_pushnumber(L, pc+1); res = pushop(L, p, pc++); lua_settable(L, -3); } while (res); return 1; } static int liststrings (lua_State *L) { Proto *p; int i; luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); p = clvalue(luaA_index(L, 1))->f.l; lua_newtable(L); for (i=0; inkstr; i++) { lua_pushnumber(L, i+1); lua_pushstring(L, p->kstr[i]->str); lua_settable(L, -3); } return 1; } static int listlocals (lua_State *L) { Proto *p; int pc = luaL_check_int(L, 2) - 1; int i = 0; const char *name; luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); p = clvalue(luaA_index(L, 1))->f.l; while ((name = luaF_getlocalname(p, ++i, pc)) != NULL) lua_pushstring(L, name); return i-1; } /* }====================================================== */ static int get_limits (lua_State *L) { lua_newtable(L); setnameval(L, "BITS_INT", BITS_INT); setnameval(L, "LFPF", LFIELDS_PER_FLUSH); setnameval(L, "MAXARG_A", MAXARG_A); setnameval(L, "MAXARG_B", MAXARG_B); setnameval(L, "MAXARG_S", MAXARG_S); setnameval(L, "MAXARG_U", MAXARG_U); setnameval(L, "MAXLOCALS", MAXLOCALS); setnameval(L, "MAXPARAMS", MAXPARAMS); setnameval(L, "MAXSTACK", MAXSTACK); setnameval(L, "MAXUPVALUES", MAXUPVALUES); setnameval(L, "MAXVARSLH", MAXVARSLH); setnameval(L, "RFPF", RFIELDS_PER_FLUSH); setnameval(L, "SIZE_A", SIZE_A); setnameval(L, "SIZE_B", SIZE_B); setnameval(L, "SIZE_OP", SIZE_OP); setnameval(L, "SIZE_U", SIZE_U); return 1; } static int mem_query (lua_State *L) { if (lua_isnull(L, 1)) { lua_pushnumber(L, memdebug_total); lua_pushnumber(L, memdebug_numblocks); lua_pushnumber(L, memdebug_maxmem); return 3; } else { memdebug_memlimit = luaL_check_int(L, 1); return 0; } } static int hash_query (lua_State *L) { if (lua_isnull(L, 2)) { luaL_arg_check(L, lua_tag(L, 1) == LUA_TSTRING, 1, "string expected"); lua_pushnumber(L, tsvalue(luaA_index(L, 1))->u.s.hash); } else { Hash *t; luaL_checktype(L, 2, LUA_TTABLE); t = hvalue(luaA_index(L, 2)); lua_pushnumber(L, luaH_mainposition(t, luaA_index(L, 1)) - t->node); } return 1; } static int table_query (lua_State *L) { const Hash *t; int i = luaL_opt_int(L, 2, -1); luaL_checktype(L, 1, LUA_TTABLE); t = hvalue(luaA_index(L, 1)); if (i == -1) { lua_pushnumber(L, t->size); lua_pushnumber(L, t->firstfree - t->node); return 2; } else if (i < t->size) { luaA_pushobject(L, &t->node[i].key); luaA_pushobject(L, &t->node[i].val); if (t->node[i].next) { lua_pushnumber(L, t->node[i].next - t->node); return 3; } else return 2; } return 0; } static int string_query (lua_State *L) { stringtable *tb = (*luaL_check_string(L, 1) == 's') ? &L->strt : &L->udt; int s = luaL_opt_int(L, 2, 0) - 1; if (s==-1) { lua_pushnumber(L ,tb->nuse); lua_pushnumber(L ,tb->size); return 2; } else if (s < tb->size) { TString *ts; int n = 0; for (ts = tb->hash[s]; ts; ts = ts->nexthash) { ttype(L->top) = LUA_TSTRING; tsvalue(L->top) = ts; incr_top; n++; } return n; } return 0; } static int tref (lua_State *L) { luaL_checkany(L, 1); lua_pushvalue(L, 1); lua_pushnumber(L, lua_ref(L, luaL_opt_int(L, 2, 1))); return 1; } static int getref (lua_State *L) { if (lua_getref(L, luaL_check_int(L, 1))) return 1; else return 0; } static int unref (lua_State *L) { lua_unref(L, luaL_check_int(L, 1)); return 0; } static int newuserdata (lua_State *L) { if (lua_isnumber(L, 2)) lua_pushusertag(L, (void *)luaL_check_int(L, 1), luaL_check_int(L, 2)); else lua_newuserdata(L, luaL_check_int(L, 1)); return 1; } static int udataval (lua_State *L) { luaL_checktype(L, 1, LUA_TUSERDATA); lua_pushnumber(L, (int)lua_touserdata(L, 1)); return 1; } static int newstate (lua_State *L) { lua_State *L1 = lua_open(luaL_check_int(L, 1)); if (L1) lua_pushuserdata(L, L1); else lua_pushnil(L); return 1; } static int loadlib (lua_State *L) { lua_State *L1 = (lua_State *)lua_touserdata(L, 1); switch (*luaL_check_string(L, 2)) { case 'm': lua_mathlibopen(L1); break; case 's': lua_strlibopen(L1); break; case 'i': lua_iolibopen(L1); break; case 'd': lua_dblibopen(L1); break; case 'b': lua_baselibopen(L1); break; default: luaL_argerror(L, 2, "invalid option"); } return 0; } static int closestate (lua_State *L) { luaL_checktype(L, 1, LUA_TUSERDATA); lua_close((lua_State *)lua_touserdata(L, 1)); return 0; } static int doremote (lua_State *L) { lua_State *L1; const char *code = luaL_check_string(L, 2); int status; luaL_checktype(L, 1, LUA_TUSERDATA); L1 = (lua_State *)lua_touserdata(L, 1); status = lua_dostring(L1, code); if (status != 0) { lua_pushnil(L); lua_pushnumber(L, status); return 2; } else { int i = 0; while (!lua_isnull(L1, ++i)) lua_pushstring(L, lua_tostring(L1, i)); return i-1; } } static int settagmethod (lua_State *L) { int tag = luaL_check_int(L, 1); const char *event = luaL_check_string(L, 2); luaL_checkany(L, 3); lua_gettagmethod(L, tag, event); lua_pushvalue(L, 3); lua_settagmethod(L, tag, event); return 1; } static int pushbool (lua_State *L, int b) { if (b) lua_pushnumber(L, 1); else lua_pushnil(L); return 1; } static int equal (lua_State *L) { return pushbool(L, lua_equal(L, 1, 2)); } /* ** {====================================================== ** function to test the API with C. It interprets a kind of "assembler" ** language with calls to the API, so the test can be driven by Lua code ** ======================================================= */ static const char *const delimits = " \t\n,;"; static void skip (const char **pc) { while (**pc != '\0' && strchr(delimits, **pc)) (*pc)++; } static int getnum (lua_State *L, const char **pc) { int res = 0; int sig = 1; skip(pc); if (**pc == '.') { res = (int)lua_tonumber(L, -1); lua_pop(L, 1); (*pc)++; return res; } else if (**pc == '-') { sig = -1; (*pc)++; } while (isdigit(**pc)) res = res*10 + (*(*pc)++) - '0'; return sig*res; } static const char *getname (char *buff, const char **pc) { int i = 0; skip(pc); while (**pc != '\0' && !strchr(delimits, **pc)) buff[i++] = *(*pc)++; buff[i] = '\0'; return buff; } #define EQ(s1) (strcmp(s1, inst) == 0) #define getnum ((getnum)(L, &pc)) #define getname ((getname)(buff, &pc)) static int testC (lua_State *L) { char buff[30]; const char *pc = luaL_check_string(L, 1); for (;;) { const char *inst = getname; if EQ("") return 0; else if EQ("isnumber") { lua_pushnumber(L, lua_isnumber(L, getnum)); } else if EQ("isstring") { lua_pushnumber(L, lua_isstring(L, getnum)); } else if EQ("istable") { lua_pushnumber(L, lua_istable(L, getnum)); } else if EQ("iscfunction") { lua_pushnumber(L, lua_iscfunction(L, getnum)); } else if EQ("isfunction") { lua_pushnumber(L, lua_isfunction(L, getnum)); } else if EQ("isuserdata") { lua_pushnumber(L, lua_isuserdata(L, getnum)); } else if EQ("isnil") { lua_pushnumber(L, lua_isnil(L, getnum)); } else if EQ("isnull") { lua_pushnumber(L, lua_isnull(L, getnum)); } else if EQ("tonumber") { lua_pushnumber(L, lua_tonumber(L, getnum)); } else if EQ("tostring") { lua_pushstring(L, lua_tostring(L, getnum)); } else if EQ("tonumber") { lua_pushnumber(L, lua_tonumber(L, getnum)); } else if EQ("strlen") { lua_pushnumber(L, lua_strlen(L, getnum)); } else if EQ("tocfunction") { lua_pushcfunction(L, lua_tocfunction(L, getnum)); } else if EQ("return") { return getnum; } else if EQ("gettop") { lua_pushnumber(L, lua_gettop(L)); } else if EQ("settop") { lua_settop(L, getnum); } else if EQ("pop") { lua_pop(L, getnum); } else if EQ("pushnum") { lua_pushnumber(L, getnum); } else if EQ("pushvalue") { lua_pushvalue(L, getnum); } else if EQ("remove") { lua_remove(L, getnum); } else if EQ("insert") { lua_insert(L, getnum); } else if EQ("gettable") { lua_gettable(L, getnum); } else if EQ("settable") { lua_settable(L, getnum); } else if EQ("next") { lua_next(L, -2); } else if EQ("concat") { lua_concat(L, getnum); } else if EQ("rawcall") { int narg = getnum; int nres = getnum; lua_rawcall(L, narg, nres); } else if EQ("call") { int narg = getnum; int nres = getnum; lua_call(L, narg, nres); } else if EQ("dostring") { lua_dostring(L, luaL_check_string(L, getnum)); } else if EQ("settagmethod") { int tag = getnum; const char *event = getname; lua_settagmethod(L, tag, event); } else if EQ("gettagmethod") { int tag = getnum; const char *event = getname; lua_gettagmethod(L, tag, event); } else if EQ("type") { lua_pushstring(L, lua_typename(L, lua_type(L, getnum))); } else luaL_verror(L, "unknown instruction %.30s", buff); } return 0; } /* }====================================================== */ static const struct luaL_reg tests_funcs[] = { {"hash", hash_query}, {"limits", get_limits}, {"listcode", listcode}, {"liststrings", liststrings}, {"listlocals", listlocals}, {"loadlib", loadlib}, {"querystr", string_query}, {"querytab", table_query}, {"testC", testC}, {"ref", tref}, {"getref", getref}, {"unref", unref}, {"newuserdata", newuserdata}, {"udataval", udataval}, {"newstate", newstate}, {"closestate", closestate}, {"doremote", doremote}, {"settagmethod", settagmethod}, {"equal", equal}, {"totalmem", mem_query} }; void luaB_opentests (lua_State *L) { lua_newtable(L); lua_getglobals(L); lua_pushvalue(L, -2); lua_setglobals(L); luaL_openl(L, tests_funcs); /* open functions inside new table */ lua_setglobals(L); /* restore old table of globals */ lua_setglobal(L, "T"); /* set new table as global T */ } #endif zangband/src/lua/ltm.c0000644000000000000000000001013710250356275013657 0ustar rootroot/* ** $Id: ltm.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Tag methods ** See Copyright Notice in lua.h */ #include #include #include "lua.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "ltm.h" const char *const luaT_eventname[] = { /* ORDER TM */ "gettable", "settable", "index", "getglobal", "setglobal", "add", "sub", "mul", "div", "pow", "unm", "lt", "concat", "gc", "function", "le", "gt", "ge", /* deprecated options!! */ NULL }; static int findevent (const char *name) { int i; for (i=0; luaT_eventname[i]; i++) if (strcmp(luaT_eventname[i], name) == 0) return i; return -1; /* name not found */ } static int luaI_checkevent (lua_State *L, const char *name, int t) { int e = findevent(name); if (e >= TM_N) luaO_verror(L, "event `%.50s' is deprecated", name); if (e == TM_GC && t == LUA_TTABLE) luaO_verror(L, "event `gc' for tables is deprecated"); if (e < 0) luaO_verror(L, "`%.50s' is not a valid event name", name); return e; } /* events in LUA_TNIL are all allowed, since this is used as a * 'placeholder' for "default" fallbacks */ /* ORDER LUA_T, ORDER TM */ static const char luaT_validevents[NUM_TAGS][TM_N] = { {1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TUSERDATA */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* LUA_TNIL */ {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, /* LUA_TNUMBER */ {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, /* LUA_TSTRING */ {0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TTABLE */ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0} /* LUA_TFUNCTION */ }; int luaT_validevent (int t, int e) { /* ORDER LUA_T */ return (t >= NUM_TAGS) ? 1 : luaT_validevents[t][e]; } static void init_entry (lua_State *L, int tag) { int i; for (i=0; iTMtable[tag].collected = NULL; } void luaT_init (lua_State *L) { int t; luaM_growvector(L, L->TMtable, 0, NUM_TAGS, struct TM, "", MAX_INT); L->nblocks += NUM_TAGS*sizeof(struct TM); L->last_tag = NUM_TAGS-1; for (t=0; t<=L->last_tag; t++) init_entry(L, t); } LUA_API int lua_newtag (lua_State *L) { luaM_growvector(L, L->TMtable, L->last_tag, 1, struct TM, "tag table overflow", MAX_INT); L->nblocks += sizeof(struct TM); L->last_tag++; init_entry(L, L->last_tag); return L->last_tag; } static void checktag (lua_State *L, int tag) { if (!(0 <= tag && tag <= L->last_tag)) luaO_verror(L, "%d is not a valid tag", tag); } void luaT_realtag (lua_State *L, int tag) { if (!validtag(tag)) luaO_verror(L, "tag %d was not created by `newtag'", tag); } LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom) { int e; checktag(L, tagto); checktag(L, tagfrom); for (e=0; eu.d.tag; case LUA_TTABLE: return hvalue(o)->htag; default: return t; } } LUA_API void lua_gettagmethod (lua_State *L, int t, const char *event) { int e; e = luaI_checkevent(L, event, t); checktag(L, t); if (luaT_validevent(t, e) && luaT_gettm(L, t, e)) { clvalue(L->top) = luaT_gettm(L, t, e); ttype(L->top) = LUA_TFUNCTION; } else ttype(L->top) = LUA_TNIL; incr_top; } LUA_API void lua_settagmethod (lua_State *L, int t, const char *event) { int e = luaI_checkevent(L, event, t); checktag(L, t); if (!luaT_validevent(t, e)) luaO_verror(L, "cannot change `%.20s' tag method for type `%.20s'%.20s", luaT_eventname[e], luaO_typenames[t], (t == LUA_TTABLE || t == LUA_TUSERDATA) ? " with default tag" : ""); switch (ttype(L->top - 1)) { case LUA_TNIL: luaT_gettm(L, t, e) = NULL; break; case LUA_TFUNCTION: luaT_gettm(L, t, e) = clvalue(L->top - 1); break; default: lua_error(L, "tag method must be a function (or nil)"); } L->top--; } zangband/src/lua/lundump.c0000644000000000000000000001251310250356275014547 0ustar rootroot/* ** $Id: lundump.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** load bytecodes from files ** See Copyright Notice in lua.h */ #include #include #include "lfunc.h" #include "lmem.h" #include "lopcodes.h" #include "lstring.h" #include "lundump.h" #define LoadByte ezgetc static const char* ZNAME (ZIO* Z) { const char* s=zname(Z); return (*s=='@') ? s+1 : s; } static void unexpectedEOZ (lua_State* L, ZIO* Z) { luaO_verror(L,"unexpected end of file in `%.99s'",ZNAME(Z)); } static int ezgetc (lua_State* L, ZIO* Z) { int c=zgetc(Z); if (c==EOZ) unexpectedEOZ(L,Z); return c; } static void ezread (lua_State* L, ZIO* Z, void* b, int n) { int r=zread(Z,b,n); if (r!=0) unexpectedEOZ(L,Z); } static void LoadBlock (lua_State* L, void* b, size_t size, ZIO* Z, int swap) { if (swap) { char *p=(char *) b+size-1; int n=size; while (n--) *p--=(char)ezgetc(L,Z); } else ezread(L,Z,b,size); } static void LoadVector (lua_State* L, void* b, int m, size_t size, ZIO* Z, int swap) { if (swap) { char *q=(char *) b; while (m--) { char *p=q+size-1; int n=size; while (n--) *p--=(char)ezgetc(L,Z); q+=size; } } else ezread(L,Z,b,m*size); } static int LoadInt (lua_State* L, ZIO* Z, int swap) { int x; LoadBlock(L,&x,sizeof(x),Z,swap); return x; } static size_t LoadSize (lua_State* L, ZIO* Z, int swap) { size_t x; LoadBlock(L,&x,sizeof(x),Z,swap); return x; } static Number LoadNumber (lua_State* L, ZIO* Z, int swap) { Number x; LoadBlock(L,&x,sizeof(x),Z,swap); return x; } static TString* LoadString (lua_State* L, ZIO* Z, int swap) { size_t size=LoadSize(L,Z,swap); if (size==0) return NULL; else { char* s=luaO_openspace(L,size); LoadBlock(L,s,size,Z,0); return luaS_newlstr(L,s,size-1); /* remove trailing '\0' */ } } static void LoadCode (lua_State* L, Proto* tf, ZIO* Z, int swap) { int size=LoadInt(L,Z,swap); tf->code=luaM_newvector(L,size,Instruction); LoadVector(L,tf->code,size,sizeof(*tf->code),Z,swap); if (tf->code[size-1]!=OP_END) luaO_verror(L,"bad code in `%.99s'",ZNAME(Z)); luaF_protook(L,tf,size); } static void LoadLocals (lua_State* L, Proto* tf, ZIO* Z, int swap) { int i,n; tf->nlocvars=n=LoadInt(L,Z,swap); tf->locvars=luaM_newvector(L,n,LocVar); for (i=0; ilocvars[i].varname=LoadString(L,Z,swap); tf->locvars[i].startpc=LoadInt(L,Z,swap); tf->locvars[i].endpc=LoadInt(L,Z,swap); } } static void LoadLines (lua_State* L, Proto* tf, ZIO* Z, int swap) { int n; tf->nlineinfo=n=LoadInt(L,Z,swap); tf->lineinfo=luaM_newvector(L,n,int); LoadVector(L,tf->lineinfo,n,sizeof(*tf->lineinfo),Z,swap); } static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap); static void LoadConstants (lua_State* L, Proto* tf, ZIO* Z, int swap) { int i,n; tf->nkstr=n=LoadInt(L,Z,swap); tf->kstr=luaM_newvector(L,n,TString*); for (i=0; ikstr[i]=LoadString(L,Z,swap); tf->nknum=n=LoadInt(L,Z,swap); tf->knum=luaM_newvector(L,n,Number); LoadVector(L,tf->knum,n,sizeof(*tf->knum),Z,swap); tf->nkproto=n=LoadInt(L,Z,swap); tf->kproto=luaM_newvector(L,n,Proto*); for (i=0; ikproto[i]=LoadFunction(L,Z,swap); } static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap) { Proto* tf=luaF_newproto(L); tf->source=LoadString(L,Z,swap); tf->lineDefined=LoadInt(L,Z,swap); tf->numparams=LoadInt(L,Z,swap); tf->is_vararg=LoadByte(L,Z); tf->maxstacksize=LoadInt(L,Z,swap); LoadLocals(L,tf,Z,swap); LoadLines(L,tf,Z,swap); LoadConstants(L,tf,Z,swap); LoadCode(L,tf,Z,swap); return tf; } static void LoadSignature (lua_State* L, ZIO* Z) { const char* s=SIGNATURE; while (*s!=0 && ezgetc(L,Z)==*s) ++s; if (*s!=0) luaO_verror(L,"bad signature in `%.99s'",ZNAME(Z)); } static void TestSize (lua_State* L, int s, const char* what, ZIO* Z) { int r=ezgetc(L,Z); if (r!=s) luaO_verror(L,"virtual machine mismatch in `%.99s':\n" " %.20s is %d but read %d",ZNAME(Z),what,s,r); } #define TESTSIZE(s) TestSize(L,s,#s,Z) #define V(v) v/16,v%16 static int LoadHeader (lua_State* L, ZIO* Z) { int version,swap; Number f=0,tf=TEST_NUMBER; LoadSignature(L,Z); version=ezgetc(L,Z); if (version>VERSION) luaO_verror(L,"`%.99s' too new:\n" " read version %d.%d; expected at most %d.%d", ZNAME(Z),V(version),V(VERSION)); if (version #include #include #include "lua.h" #include "lapi.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" #ifdef OLD_ANSI #define strcoll(a,b) strcmp(a,b) #endif /* ** Extra stack size to run a function: ** TAG_LINE(1), NAME(1), TM calls(3) (plus some extra...) */ #define EXTRA_STACK 8 int luaV_tonumber (TObject *obj) { if (ttype(obj) != LUA_TSTRING) return 1; else { if (!luaO_str2d(svalue(obj), &nvalue(obj))) return 2; ttype(obj) = LUA_TNUMBER; return 0; } } int luaV_tostring (lua_State *L, TObject *obj) { /* LUA_NUMBER */ if (ttype(obj) != LUA_TNUMBER) return 1; else { char s[32]; /* 16 digits, sign, point and \0 (+ some extra...) */ lua_number2str(s, nvalue(obj)); /* convert `s' to number */ tsvalue(obj) = luaS_new(L, s); ttype(obj) = LUA_TSTRING; return 0; } } static void traceexec (lua_State *L, StkId base, StkId top, lua_Hook linehook) { CallInfo *ci = infovalue(base-1); int *lineinfo = ci->func->f.l->lineinfo; int pc = (*ci->pc - ci->func->f.l->code) - 1; int newline; if (pc == 0) { /* may be first time? */ ci->line = 1; ci->refi = 0; ci->lastpc = pc+1; /* make sure it will call linehook */ } newline = luaG_getline(lineinfo, pc, ci->line, &ci->refi); /* calls linehook when enters a new line or jumps back (loop) */ if (newline != ci->line || pc <= ci->lastpc) { ci->line = newline; L->top = top; luaD_lineHook(L, base-1, newline, linehook); } ci->lastpc = pc; } static Closure *luaV_closure (lua_State *L, int nelems) { Closure *c = luaF_newclosure(L, nelems); L->top -= nelems; while (nelems--) c->upvalue[nelems] = *(L->top+nelems); clvalue(L->top) = c; ttype(L->top) = LUA_TFUNCTION; incr_top; return c; } void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems) { Closure *cl = luaV_closure(L, nelems); cl->f.c = c; cl->isC = 1; } void luaV_Lclosure (lua_State *L, Proto *l, int nelems) { Closure *cl = luaV_closure(L, nelems); cl->f.l = l; cl->isC = 0; } /* ** Function to index a table. ** Receives the table at `t' and the key at top. */ const TObject *luaV_gettable (lua_State *L, StkId t) { Closure *tm; int tg; if (ttype(t) == LUA_TTABLE && /* `t' is a table? */ ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */ luaT_gettm(L, tg, TM_GETTABLE) == NULL)) { /* or no TM? */ /* do a primitive get */ const TObject *h = luaH_get(L, hvalue(t), L->top-1); /* result is no nil or there is no `index' tag method? */ if (ttype(h) != LUA_TNIL || ((tm=luaT_gettm(L, tg, TM_INDEX)) == NULL)) return h; /* return result */ /* else call `index' tag method */ } else { /* try a `gettable' tag method */ tm = luaT_gettmbyObj(L, t, TM_GETTABLE); } if (tm != NULL) { /* is there a tag method? */ luaD_checkstack(L, 2); *(L->top+1) = *(L->top-1); /* key */ *L->top = *t; /* table */ clvalue(L->top-1) = tm; /* tag method */ ttype(L->top-1) = LUA_TFUNCTION; L->top += 2; luaD_call(L, L->top - 3, 1); return L->top - 1; /* call result */ } else { /* no tag method */ luaG_typeerror(L, t, "index"); return NULL; /* to avoid warnings */ } } /* ** Receives table at `t', key at `key' and value at top. */ void luaV_settable (lua_State *L, StkId t, StkId key) { int tg; if (ttype(t) == LUA_TTABLE && /* `t' is a table? */ ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */ luaT_gettm(L, tg, TM_SETTABLE) == NULL)) /* or no TM? */ *luaH_set(L, hvalue(t), key) = *(L->top-1); /* do a primitive set */ else { /* try a `settable' tag method */ Closure *tm = luaT_gettmbyObj(L, t, TM_SETTABLE); if (tm != NULL) { luaD_checkstack(L, 3); *(L->top+2) = *(L->top-1); *(L->top+1) = *key; *(L->top) = *t; clvalue(L->top-1) = tm; ttype(L->top-1) = LUA_TFUNCTION; L->top += 3; luaD_call(L, L->top - 4, 0); /* call `settable' tag method */ } else /* no tag method... */ luaG_typeerror(L, t, "index"); } } const TObject *luaV_getglobal (lua_State *L, TString *s) { const TObject *value = luaH_getstr(L->gt, s); Closure *tm = luaT_gettmbyObj(L, value, TM_GETGLOBAL); if (tm == NULL) /* is there a tag method? */ return value; /* default behavior */ else { /* tag method */ luaD_checkstack(L, 3); clvalue(L->top) = tm; ttype(L->top) = LUA_TFUNCTION; tsvalue(L->top+1) = s; /* global name */ ttype(L->top+1) = LUA_TSTRING; *(L->top+2) = *value; L->top += 3; luaD_call(L, L->top - 3, 1); return L->top - 1; } } void luaV_setglobal (lua_State *L, TString *s) { const TObject *oldvalue = luaH_getstr(L->gt, s); Closure *tm = luaT_gettmbyObj(L, oldvalue, TM_SETGLOBAL); if (tm == NULL) { /* is there a tag method? */ if (oldvalue != &luaO_nilobject) { /* cast to remove `const' is OK, because `oldvalue' != luaO_nilobject */ *(TObject *)oldvalue = *(L->top - 1); } else { TObject key; ttype(&key) = LUA_TSTRING; tsvalue(&key) = s; *luaH_set(L, L->gt, &key) = *(L->top - 1); } } else { luaD_checkstack(L, 3); *(L->top+2) = *(L->top-1); /* new value */ *(L->top+1) = *oldvalue; ttype(L->top) = LUA_TSTRING; tsvalue(L->top) = s; clvalue(L->top-1) = tm; ttype(L->top-1) = LUA_TFUNCTION; L->top += 3; luaD_call(L, L->top - 4, 0); } } static int call_binTM (lua_State *L, StkId top, TMS event) { /* try first operand */ Closure *tm = luaT_gettmbyObj(L, top-2, event); L->top = top; if (tm == NULL) { tm = luaT_gettmbyObj(L, top-1, event); /* try second operand */ if (tm == NULL) { tm = luaT_gettm(L, 0, event); /* try a `global' method */ if (tm == NULL) return 0; /* error */ } } lua_pushstring(L, luaT_eventname[event]); luaD_callTM(L, tm, 3, 1); return 1; } static void call_arith (lua_State *L, StkId top, TMS event) { if (!call_binTM(L, top, event)) luaG_binerror(L, top-2, LUA_TNUMBER, "perform arithmetic on"); } static int luaV_strcomp (const TString *ls, const TString *rs) { const char *l = ls->str; size_t ll = ls->len; const char *r = rs->str; size_t lr = rs->len; for (;;) { int temp = strcoll(l, r); if (temp != 0) return temp; else { /* strings are equal up to a '\0' */ size_t len = strlen(l); /* index of first '\0' in both strings */ if (len == ll) /* l is finished? */ return (len == lr) ? 0 : -1; /* l is equal or smaller than r */ else if (len == lr) /* r is finished? */ return 1; /* l is greater than r (because l is not finished) */ /* both strings longer than `len'; go on comparing (after the '\0') */ len++; l += len; ll -= len; r += len; lr -= len; } } } int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top) { if (ttype(l) == LUA_TNUMBER && ttype(r) == LUA_TNUMBER) return (nvalue(l) < nvalue(r)); else if (ttype(l) == LUA_TSTRING && ttype(r) == LUA_TSTRING) return (luaV_strcomp(tsvalue(l), tsvalue(r)) < 0); else { /* call TM */ luaD_checkstack(L, 2); *top++ = *l; *top++ = *r; if (!call_binTM(L, top, TM_LT)) luaG_ordererror(L, top-2); L->top--; return (ttype(L->top) != LUA_TNIL); } } void luaV_strconc (lua_State *L, int total, StkId top) { do { int n = 2; /* number of elements handled in this pass (at least 2) */ if (tostring(L, top-2) || tostring(L, top-1)) { if (!call_binTM(L, top, TM_CONCAT)) luaG_binerror(L, top-2, LUA_TSTRING, "concat"); } else if (tsvalue(top-1)->len > 0) { /* if len=0, do nothing */ /* at least two string values; get as many as possible */ lint32 tl = (lint32)tsvalue(top-1)->len + (lint32)tsvalue(top-2)->len; char *buffer; int i; while (n < total && !tostring(L, top-n-1)) { /* collect total length */ tl += tsvalue(top-n-1)->len; n++; } if (tl > MAX_SIZET) lua_error(L, "string size overflow"); buffer = luaO_openspace(L, tl); tl = 0; for (i=n; i>0; i--) { /* concat all strings */ size_t l = tsvalue(top-i)->len; memcpy(buffer+tl, tsvalue(top-i)->str, l); tl += l; } tsvalue(top-n) = luaS_newlstr(L, buffer, tl); } total -= n-1; /* got `n' strings to create 1 new */ top -= n-1; } while (total > 1); /* repeat until only 1 result left */ } static void luaV_pack (lua_State *L, StkId firstelem) { int i; Hash *htab = luaH_new(L, 0); for (i=0; firstelem+itop; i++) *luaH_setint(L, htab, i+1) = *(firstelem+i); /* store counter in field `n' */ luaH_setstrnum(L, htab, luaS_new(L, "n"), i); L->top = firstelem; /* remove elements from the stack */ ttype(L->top) = LUA_TTABLE; hvalue(L->top) = htab; incr_top; } static void adjust_varargs (lua_State *L, StkId base, int nfixargs) { int nvararg = (L->top-base) - nfixargs; if (nvararg < 0) luaD_adjusttop(L, base, nfixargs); luaV_pack(L, base+nfixargs); } #define dojump(pc, i) { int d = GETARG_S(i); pc += d; } /* ** Executes the given Lua function. Parameters are between [base,top). ** Returns n such that the the results are between [n,top). */ StkId luaV_execute (lua_State *L, const Closure *cl, StkId base) { const Proto *const tf = cl->f.l; StkId top; /* keep top local, for performance */ const Instruction *pc = tf->code; TString **const kstr = tf->kstr; const lua_Hook linehook = L->linehook; infovalue(base-1)->pc = &pc; luaD_checkstack(L, tf->maxstacksize+EXTRA_STACK); if (tf->is_vararg) /* varargs? */ adjust_varargs(L, base, tf->numparams); else luaD_adjusttop(L, base, tf->numparams); top = L->top; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; if (linehook) traceexec(L, base, top, linehook); switch (GET_OPCODE(i)) { case OP_END: { L->top = top; return top; } case OP_RETURN: { L->top = top; return base+GETARG_U(i); } case OP_CALL: { int nres = GETARG_B(i); if (nres == MULT_RET) nres = LUA_MULTRET; L->top = top; luaD_call(L, base+GETARG_A(i), nres); top = L->top; break; } case OP_TAILCALL: { L->top = top; luaD_call(L, base+GETARG_A(i), LUA_MULTRET); return base+GETARG_B(i); } case OP_PUSHNIL: { int n = GETARG_U(i); LUA_ASSERT(n>0, "invalid argument"); do { ttype(top++) = LUA_TNIL; } while (--n > 0); break; } case OP_POP: { top -= GETARG_U(i); break; } case OP_PUSHINT: { ttype(top) = LUA_TNUMBER; nvalue(top) = (Number)GETARG_S(i); top++; break; } case OP_PUSHSTRING: { ttype(top) = LUA_TSTRING; tsvalue(top) = kstr[GETARG_U(i)]; top++; break; } case OP_PUSHNUM: { ttype(top) = LUA_TNUMBER; nvalue(top) = tf->knum[GETARG_U(i)]; top++; break; } case OP_PUSHNEGNUM: { ttype(top) = LUA_TNUMBER; nvalue(top) = -tf->knum[GETARG_U(i)]; top++; break; } case OP_PUSHUPVALUE: { *top++ = cl->upvalue[GETARG_U(i)]; break; } case OP_GETLOCAL: { *top++ = *(base+GETARG_U(i)); break; } case OP_GETGLOBAL: { L->top = top; *top = *luaV_getglobal(L, kstr[GETARG_U(i)]); top++; break; } case OP_GETTABLE: { L->top = top; top--; *(top-1) = *luaV_gettable(L, top-1); break; } case OP_GETDOTTED: { ttype(top) = LUA_TSTRING; tsvalue(top) = kstr[GETARG_U(i)]; L->top = top+1; *(top-1) = *luaV_gettable(L, top-1); break; } case OP_GETINDEXED: { *top = *(base+GETARG_U(i)); L->top = top+1; *(top-1) = *luaV_gettable(L, top-1); break; } case OP_PUSHSELF: { TObject receiver; receiver = *(top-1); ttype(top) = LUA_TSTRING; tsvalue(top++) = kstr[GETARG_U(i)]; L->top = top; *(top-2) = *luaV_gettable(L, top-2); *(top-1) = receiver; break; } case OP_CREATETABLE: { L->top = top; luaC_checkGC(L); hvalue(top) = luaH_new(L, GETARG_U(i)); ttype(top) = LUA_TTABLE; top++; break; } case OP_SETLOCAL: { *(base+GETARG_U(i)) = *(--top); break; } case OP_SETGLOBAL: { L->top = top; luaV_setglobal(L, kstr[GETARG_U(i)]); top--; break; } case OP_SETTABLE: { StkId t = top-GETARG_A(i); L->top = top; luaV_settable(L, t, t+1); top -= GETARG_B(i); /* pop values */ break; } case OP_SETLIST: { int aux = GETARG_A(i) * LFIELDS_PER_FLUSH; int n = GETARG_B(i); Hash *arr = hvalue(top-n-1); L->top = top-n; /* final value of `top' (in case of errors) */ for (; n; n--) *luaH_setint(L, arr, n+aux) = *(--top); break; } case OP_SETMAP: { int n = GETARG_U(i); StkId finaltop = top-2*n; Hash *arr = hvalue(finaltop-1); L->top = finaltop; /* final value of `top' (in case of errors) */ for (; n; n--) { top-=2; *luaH_set(L, arr, top) = *(top+1); } break; } case OP_ADD: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_ADD); else nvalue(top-2) += nvalue(top-1); top--; break; } case OP_ADDI: { if (tonumber(top-1)) { ttype(top) = LUA_TNUMBER; nvalue(top) = (Number)GETARG_S(i); call_arith(L, top+1, TM_ADD); } else nvalue(top-1) += (Number)GETARG_S(i); break; } case OP_SUB: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_SUB); else nvalue(top-2) -= nvalue(top-1); top--; break; } case OP_MULT: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_MUL); else nvalue(top-2) *= nvalue(top-1); top--; break; } case OP_DIV: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_DIV); else nvalue(top-2) /= nvalue(top-1); top--; break; } case OP_POW: { if (!call_binTM(L, top, TM_POW)) lua_error(L, "undefined operation"); top--; break; } case OP_CONCAT: { int n = GETARG_U(i); luaV_strconc(L, n, top); top -= n-1; L->top = top; luaC_checkGC(L); break; } case OP_MINUS: { if (tonumber(top-1)) { ttype(top) = LUA_TNIL; call_arith(L, top+1, TM_UNM); } else nvalue(top-1) = -nvalue(top-1); break; } case OP_NOT: { ttype(top-1) = (ttype(top-1) == LUA_TNIL) ? LUA_TNUMBER : LUA_TNIL; nvalue(top-1) = 1; break; } case OP_JMPNE: { top -= 2; if (!luaO_equalObj(top, top+1)) dojump(pc, i); break; } case OP_JMPEQ: { top -= 2; if (luaO_equalObj(top, top+1)) dojump(pc, i); break; } case OP_JMPLT: { top -= 2; if (luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i); break; } case OP_JMPLE: { /* a <= b === !(b b === (b= b === !(a 0 ? nvalue(top-3) > nvalue(top-2) : nvalue(top-3) < nvalue(top-2)) { /* `empty' loop? */ top -= 3; /* remove control variables */ dojump(pc, i); /* jump to loop end */ } break; } case OP_FORLOOP: { LUA_ASSERT(ttype(top-1) == LUA_TNUMBER, "invalid step"); LUA_ASSERT(ttype(top-2) == LUA_TNUMBER, "invalid limit"); if (ttype(top-3) != LUA_TNUMBER) lua_error(L, "`for' index must be a number"); nvalue(top-3) += nvalue(top-1); /* increment index */ if (nvalue(top-1) > 0 ? nvalue(top-3) > nvalue(top-2) : nvalue(top-3) < nvalue(top-2)) top -= 3; /* end loop: remove control variables */ else dojump(pc, i); /* repeat loop */ break; } case OP_LFORPREP: { Node *node; if (ttype(top-1) != LUA_TTABLE) lua_error(L, "`for' table must be a table"); node = luaH_next(L, hvalue(top-1), &luaO_nilobject); if (node == NULL) { /* `empty' loop? */ top--; /* remove table */ dojump(pc, i); /* jump to loop end */ } else { top += 2; /* index,value */ *(top-2) = *key(node); *(top-1) = *val(node); } break; } case OP_LFORLOOP: { Node *node; LUA_ASSERT(ttype(top-3) == LUA_TTABLE, "invalid table"); node = luaH_next(L, hvalue(top-3), top-2); if (node == NULL) /* end loop? */ top -= 3; /* remove table, key, and value */ else { *(top-2) = *key(node); *(top-1) = *val(node); dojump(pc, i); /* repeat loop */ } break; } case OP_CLOSURE: { L->top = top; luaV_Lclosure(L, tf->kproto[GETARG_A(i)], GETARG_B(i)); top = L->top; luaC_checkGC(L); break; } } } } zangband/src/lua/lzio.c0000644000000000000000000000335110250356275014040 0ustar rootroot/* ** $Id: lzio.c,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** a generic input stream interface ** See Copyright Notice in lua.h */ #include #include #include "lua.h" #include "lzio.h" /* ----------------------------------------------------- memory buffers --- */ static int zmfilbuf (ZIO* z) { (void)z; /* to avoid warnings */ return EOZ; } ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name) { if (b==NULL) return NULL; z->n = size; z->p = (const unsigned char *)b; z->filbuf = zmfilbuf; z->u = NULL; z->name = name; return z; } /* ------------------------------------------------------------ strings --- */ ZIO* zsopen (ZIO* z, const char* s, const char *name) { if (s==NULL) return NULL; return zmopen(z, s, strlen(s), name); } /* -------------------------------------------------------------- FILEs --- */ static int zffilbuf (ZIO* z) { size_t n; if (feof((FILE *)z->u)) return EOZ; n = fread(z->buffer, 1, ZBSIZE, (FILE *)z->u); if (n==0) return EOZ; z->n = n-1; z->p = z->buffer; return *(z->p++); } ZIO* zFopen (ZIO* z, FILE* f, const char *name) { if (f==NULL) return NULL; z->n = 0; z->p = z->buffer; z->filbuf = zffilbuf; z->u = f; z->name = name; return z; } /* --------------------------------------------------------------- read --- */ size_t zread (ZIO *z, void *b, size_t n) { while (n) { size_t m; if (z->n == 0) { if (z->filbuf(z) == EOZ) return n; /* return number of missing bytes */ zungetc(z); /* put result from `filbuf' in the buffer */ } m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ memcpy(b, z->p, m); z->n -= m; z->p += m; b = (char *)b + m; n -= m; } return 0; } zangband/src/lua/tolua.c0000644000000000000000000000664110250356275014214 0ustar rootroot/* tolua ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua.c,v 1.6 2002/04/01 11:54:31 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #include "tolua.h" #include "lua.h" #include "lualib.h" #include #include #include static void help (void) { fprintf(stderr,"\n" "usage: tolua [options] input_file\n" "\n" "Command line options are:\n" " -v : print version information.\n" " -o file : set output file; default is stdout.\n" " -H file : create include file.\n" " -n name : set package name; default is input file root name.\n" " -p : parse only.\n" " -P : parse and print structure information (for debug).\n" " -h : print this message.\n" "Should the input file be omitted, stdin is assumed;\n" "in that case, the package name must be explicitly set.\n\n" ); } static void version (void) { fprintf(stderr, "%s (written by W. Celes)\n",TOLUA_VERSION); } static void setfield (lua_State* L, int table, const char* f, const char* v) { lua_pushstring(L,f); lua_pushstring(L,v); lua_settable(L,table); } static void error (char* o) { fprintf(stderr,"tolua: unknown option '%s'\n",o); help(); exit(1); } int main (int argc, char* argv[]) { lua_State* L = lua_open(0); lua_baselibopen(L); lua_iolibopen(L); lua_strlibopen(L); lua_pushstring(L,TOLUA_VERSION); lua_setglobal(L,"TOLUA_VERSION"); if (argc==1) { help(); return 0; } else { int i, t; lua_newtable(L); lua_pushvalue(L,-1); lua_setglobal(L,"flags"); t = lua_gettop(L); for (i=1; i /* registry fiels used to hold current error info - tolua_err_narg: number of wrong argument - tolua_err_provided: provided type - tolua_err_expected: expected type */ void toluaI_eh_set (lua_State* L, int narg, const char* provided, const char* expected) { lua_pushnumber(L,narg); toluaI_setregistry(L,"tolua_err_narg"); lua_pushstring(L,provided); toluaI_setregistry(L,"tolua_err_provided"); lua_pushstring(L,expected); toluaI_setregistry(L,"tolua_err_expected"); } void tolua_error (lua_State* L, const char* msg) { if (msg[0]=='#') { static char buffer[BUFSIZ]; const char* err_provided; const char* err_expected; toluaI_getregistry(L,"tolua_err_provided"); err_provided = lua_tostring(L,-1); toluaI_getregistry(L,"tolua_err_expected"); err_expected = lua_tostring(L,-1); lua_pop(L,2); if (msg[1]=='f') { int err_narg; toluaI_getregistry(L,"tolua_err_narg"); err_narg = (int)lua_tonumber(L,-1); lua_pop(L,1); sprintf(buffer,"%s\n argument #%d is '%s'; '%s' expected.\n", msg+2,err_narg,err_provided,err_expected); } else if (msg[1]=='v') sprintf(buffer,"%s\n value is '%s'; '%s' expected.\n", msg+2,err_provided,err_expected); lua_error(L,buffer); } else lua_error(L,msg); } zangband/src/lua/tolua_gp.c0000644000000000000000000001004310250356275014671 0ustar rootroot/* tolua: get & push functions. ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua_gp.c,v 1.2 2002/04/02 23:17:42 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #include "tolua.h" #include "tolua_tm.h" #include #include long tolua_getnumber (lua_State* L, int narg, long def) { return lua_gettop(L) #include "tolua.h" #include "tolua_rg.h" #include "tolua_tm.h" #include "tolua_tt.h" void tolua_globalvar (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set) { lua_newtable(L); lua_pushstring(L,".get"); lua_pushcfunction(L,get); lua_settable(L,-3); if (set) { lua_pushstring(L,".set"); lua_pushcfunction(L,set); lua_settable(L,-3); } lua_pushvalue(L,-1); /* duplicate top */ lua_setglobal(L,name); toluaI_tm_global(L,lua_gettop(L)); lua_pop(L,1); } static int toluaI_const_global_array (lua_State* L) { lua_error(L,"value of const array cannot be changed"); return 0; } void tolua_globalarray (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set) { int tag = lua_newtag(L); lua_newtable(L); lua_settag(L,tag); lua_setglobal(L,name); lua_pushcfunction(L,get); lua_settagmethod(L,tag,"gettable"); if (set) lua_pushcfunction(L,set); else lua_pushcfunction(L,toluaI_const_global_array); lua_settagmethod(L,tag,"settable"); } void tolua_tablevar (lua_State* L, const char* table, const char* name, lua_CFunction get, lua_CFunction set) { lua_getglobal(L,table); lua_pushstring(L,".get"); lua_gettable(L,-2); lua_pushstring(L,name); lua_pushcfunction(L,get); lua_settable(L,-3); lua_pop(L,1); if (set) { lua_pushstring(L,".set"); lua_gettable(L,-2); lua_pushstring(L,name); lua_pushcfunction(L,set); lua_settable(L,-3); lua_pop(L,1); } lua_pop(L,1); } static int toluaI_get_array (lua_State* L) { void* self = tolua_getuserdata(L,1,0); const char* field = tolua_getstring(L,2,0); if (!field) tolua_error(L,"invalid 'field' in accessing array"); if (!self) { static char msg[BUFSIZ]; sprintf(msg,"invalid 'self' in accessing array '%s'",field); tolua_error(L,msg); } toluaI_getregistry(L,"tolua_tbl_itype"); lua_pushnumber(L,lua_tag(L,1)); lua_gettable(L,-2); lua_getglobal(L,lua_tostring(L,-1)); lua_pushstring(L,".array"); lua_gettable(L,-2); lua_pushvalue(L,2); /* field */ lua_gettable(L,-2); lua_pushstring(L,".self"); lua_pushvalue(L,1); /* self */ lua_rawset(L,-3); return 1; } static int toluaI_const_array (lua_State* L) { lua_error(L,"value of const field cannot be changed"); return 0; } void tolua_tablearray (lua_State* L, const char* table, const char* name, lua_CFunction get, lua_CFunction set) { int tag = lua_newtag(L); lua_getglobal(L,table); lua_pushstring(L,".array"); lua_rawget(L,-2); lua_pushstring(L,name); lua_newtable(L); lua_settag(L,tag); lua_settable(L,-3); lua_pop(L,2); lua_pushcfunction(L,get); lua_settagmethod(L,tag,"gettable"); if (set) lua_pushcfunction(L,set); else lua_pushcfunction(L,toluaI_const_array); lua_settagmethod(L,tag,"settable"); tolua_tablevar(L,table,name,toluaI_get_array,NULL); } void tolua_module (lua_State* L, const char* name) { lua_getglobal(L,name); if (!lua_istable(L,-1)) { lua_newtable(L); lua_pushstring(L,".get"); lua_newtable(L); lua_settable(L,-3); lua_pushstring(L,".set"); lua_newtable(L); lua_settable(L,-3); lua_pushvalue(L,-1); /* duplicate top */ lua_setglobal(L,name); toluaI_tm_module(L,lua_gettop(L)); lua_pop(L,1); } lua_pop(L,1); } void tolua_cclass (lua_State* L, const char* name, const char* base) { int t; lua_newtable(L); lua_pushstring(L,".get"); lua_newtable(L); lua_settable(L,-3); lua_pushstring(L,".set"); lua_newtable(L); lua_settable(L,-3); lua_pushstring(L,".array"); lua_newtable(L); lua_settable(L,-3); if (*base != 0) { lua_pushstring(L,".base"); lua_getglobal(L,base); lua_rawset(L,-3); } lua_pushvalue(L,-1); /* duplicate top */ lua_setglobal(L,name); t = lua_gettop(L); toluaI_tm_class(L,t,name); toluaI_tt_class(L,t,name,base); lua_pop(L,1); } void tolua_function (lua_State* L, const char* parent, const char* name, lua_CFunction func) { if (parent==NULL) { lua_pushcfunction(L,func); lua_setglobal(L,name); } else { lua_getglobal(L,parent); lua_pushstring(L,name); lua_pushcfunction(L,func); lua_settable(L,-3); lua_pop(L,1); } } void tolua_constant (lua_State* L, const char* parent, const char* name, long value) { if (parent==NULL) { lua_pushnumber(L,value); lua_setglobal(L,name); } else { lua_getglobal(L,parent); lua_pushstring(L,name); lua_pushnumber(L,value); lua_settable(L,-3); lua_pop(L,1); } } void toluaI_setregistry (lua_State* L, const char* field) { lua_getregistry(L); lua_insert(L,-2); lua_pushstring(L,field); lua_insert(L,-2); lua_settable(L,-3); lua_pop(L,1); } void toluaI_getregistry (lua_State* L, const char* field) { lua_getregistry(L); lua_pushstring(L,field); lua_gettable(L,-2); lua_insert(L,-2); lua_pop(L,1); } zangband/src/lua/tolua_tm.c0000644000000000000000000003373710250356275014722 0ustar rootroot/* tolua: tag methods ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua_tm.c,v 1.3 2002/04/02 23:17:42 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #include "tolua.h" #include "tolua_tm.h" #include "tolua_tt.h" #include "tolua_rg.h" #include /* Global tables created in Lua registry: tolua_tbl_class: indexed by instance tags, stores the class tables. tolua_tbl_clone: indexed by memory address, stores the tag indicanting it is a clone. tolua_tbl_mate: indexed by memory address, stores the associate instance table. General tags stored in Lua registry: tolua_tag_global; tolua_tag_module; tolua_tag_class; tolua_tag_instance; tolua_tag_linstance; tolua_tag_indirect; */ /* internal function prototype */ static void setmethods (lua_State* L); static void settag (lua_State* L, int lo, const char* tag_registry_field) { toluaI_getregistry(L,tag_registry_field); lua_pushvalue(L,lo); lua_settag(L,(int)lua_tonumber(L,-2)); lua_pop(L,2); } void toluaI_tm_global (lua_State* L, int lo) { settag(L,lo,"tolua_tag_global"); } void toluaI_tm_module (lua_State* L, int lo) { settag(L,lo,"tolua_tag_module"); } void toluaI_tm_setclass (lua_State* L, int lo) { settag(L,lo,"tolua_tag_class"); } void toluaI_tm_class (lua_State* L, int lo, const char* name) { int tag_class; int tag = lua_newtag(L); char* type = toluaI_tt_concat("class ",name); toluaI_getregistry(L,"tolua_tag_class"); tag_class = (int)lua_tonumber(L,-1); lua_copytagmethods(L,tag,tag_class); toluaI_tt_register(L,tag,type); toluaI_tt_sethierarchy(L,tag,tag_class); lua_pushvalue(L,lo); lua_settag(L,tag); lua_pop(L,2); /* tag_class and lo */ } void toluaI_tm_instance (lua_State* L, int tag, int lo) { toluaI_getregistry(L,"tolua_tbl_class"); lua_pushnumber(L,tag); lua_pushvalue(L,lo); lua_settable(L,-3); toluaI_getregistry(L,"tolua_tag_instance"); lua_copytagmethods(L,tag,(int)lua_tonumber(L,-1)); lua_pop(L,2); /* tbl_class and tag_instance */ } void toluaI_tm_linstance (lua_State* L, int tag, int lo) { toluaI_getregistry(L,"tolua_tbl_class"); lua_pushnumber(L,tag); lua_pushvalue(L,lo); lua_settable(L,-3); toluaI_getregistry(L,"tolua_tag_linstance"); lua_copytagmethods(L,tag,(int)lua_tonumber(L,-1)); lua_pop(L,2); /* tbl_class and tag_linstance */ } void* tolua_doclone (lua_State* L, void* value, int tag) { toluaI_getregistry(L,"tolua_tbl_clone"); lua_pushuserdata(L,value); lua_pushnumber(L,tag); lua_settable(L,-3); lua_pop(L,1); return value; } void* tolua_copy (lua_State* L, void* value, unsigned int size) { void* clone = (void*)malloc(size); if (clone) memcpy(clone,value,size); else tolua_error(L,"insuficient memory"); return clone; } static void toluaI_tm_undoclone (lua_State* L, int tag, void* clone) { toluaI_getregistry(L,"tolua_tbl_clone"); lua_pushuserdata(L,clone); lua_gettable(L,-2); if (lua_isnumber(L,-1) && lua_tonumber(L,-1)==tag) { lua_pushuserdata(L,clone); lua_pushnil(L); lua_settable(L,-4); /* get base class */ toluaI_getregistry(L,"tolua_tbl_class"); lua_pushnumber(L,tag); lua_rawget(L,-2); /* look for destructor */ lua_pushstring(L,"delete"); lua_gettable(L,-2); if (lua_iscfunction(L,-1)) { lua_pushusertag(L,clone,tag); lua_call(L,1,0); } else { free(clone); /* no destructor: use raw free */ lua_pop(L,1); /* the nil function value */ } lua_pop(L,2); /* tbl_class and class method table */ } lua_pop(L,2); /* table and value */ } void toluaI_tm_pushmate (lua_State* L, int lo) { toluaI_getregistry(L,"tolua_tbl_mate"); lua_pushvalue(L,lo); lua_rawget(L,-2); lua_insert(L,-2); lua_pop(L,1); } void toluaI_tm_pushclass (lua_State* L, int lo) { toluaI_getregistry(L,"tolua_tbl_class"); lua_pushnumber(L,lua_tag(L,lo)); lua_rawget(L,-2); lua_insert(L,-2); lua_pop(L,1); } static int toluaI_gettag (lua_State* L, const char* tagname) { int tag; toluaI_getregistry(L,tagname); tag = (int)lua_tonumber(L,-1); lua_pop(L,1); return tag; } void toluaI_tm_init (lua_State* L) { lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_class"); lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_clone"); lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_mate"); lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_global"); lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_module"); lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_class"); lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_instance"); lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_linstance"); lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_indirect"); toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_global"),"generic variable"); toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_module"),"generic module"); toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_class"),"generic class"); toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_indirect"),"generic indirect"); toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_instance"),"generic instance"); toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_linstance"),"generic lua instance"); /* allows modules and classes to be used as ordinary tables */ toluaI_tt_sethierarchy(L,toluaI_gettag(L,"tolua_tag_module"),tolua_tag_table); toluaI_tt_sethierarchy(L,toluaI_gettag(L,"tolua_tag_class"),tolua_tag_table); setmethods(L); } static int map (lua_State* L) { int m = lua_gettop(L); /* do not pass string fields starting with a dot */ if (!lua_isstring(L,1) || *lua_tostring(L,1)!='.') { lua_getglobals(L); lua_pushvalue(L,1); lua_pushvalue(L,m); lua_rawset(L,-3); lua_pop(L,1); } return 0; } void toluaI_tm_using (lua_State* L, int module) { lua_newtable(L); lua_settag(L,toluaI_gettag(L,"tolua_tag_indirect")); lua_pushstring(L,".module"); lua_pushvalue(L,module); lua_settable(L,-3); lua_getglobal(L,"foreach"); lua_pushvalue(L,module); lua_pushvalue(L,-3); lua_pushcclosure(L,map,1); lua_call(L,2,0); lua_getglobal(L,"foreach"); lua_pushvalue(L,module); lua_pushstring(L,".get"); lua_gettable(L,-2); lua_insert(L,-2); lua_pop(L,1); /* module table */ lua_pushvalue(L,-3); lua_pushcclosure(L,map,1); lua_call(L,2,0); lua_pop(L,1); /* indirect table */ } /********************************************************** tag methods */ /* tag methods coded in C */ /* generic gettable */ static void oo_gettable (lua_State* L, int table, int base, int index) { while (lua_istable(L,base)) { lua_pushvalue(L,index); lua_rawget(L,base); if (!lua_isnil(L,-1)) return; /* returned value already on the top */ else if (lua_isnumber(L,index)) { lua_pushstring(L,"operator_get"); lua_rawget(L,base); if (!lua_isnil(L,-1)) { lua_pushvalue(L,table); lua_pushvalue(L,index); lua_call(L,2,1); return; } } else { lua_pushstring(L,".get"); lua_rawget(L,base); if (!lua_isnil(L,-1)) { lua_pushvalue(L,index); lua_rawget(L,-2); if (!lua_isnil(L,-1)) { lua_pushvalue(L,table); lua_pushvalue(L,index); /* need to access array field (?) */ lua_call(L,2,1); return; } } } lua_pushstring(L,".base"); lua_rawget(L,base); base = lua_gettop(L); } lua_pushnil(L); } /* generic settable */ static int oo_settable (lua_State* L, int table, int base, int index, int value) { while (lua_istable(L,base)) { lua_pushstring(L,".set"); lua_rawget(L,base); if (!lua_isnil(L,-1)) { lua_pushvalue(L,index); lua_rawget(L,-2); if (!lua_isnil(L,-1)) { lua_pushvalue(L,table); lua_pushvalue(L,value); lua_call(L,2,0); return 1; } } lua_pushstring(L,".base"); lua_rawget(L,base); base = lua_gettop(L); } return 0; } /* class tag methods */ static int class_index (lua_State* L) { int table = 1; int index = 2; oo_gettable(L,table,table,index); return 1; } static int class_settable (lua_State* L) { int table = 1; int index = 2; int value = 3; if (oo_settable(L,table,table,index,value) == 0) { lua_pushvalue(L,table); lua_pushvalue(L,index); lua_pushvalue(L,value); lua_rawset(L,-3); } return 0; } /* instance tags */ static int instance_gettable (lua_State* L) { int table = 1; int index = 2; toluaI_tm_pushmate(L,table); /* pushes mate */ if (!lua_isnil(L,-1)) /* if there's a mate table */ { lua_pushvalue(L,index); lua_rawget(L,-2); if (!lua_isnil(L,-1)) /* if field in mate table exists */ return 1; } toluaI_tm_pushclass(L,table); /* pushes base */ oo_gettable(L,table,lua_gettop(L),index); return 1; } static int instance_settable (lua_State* L) { int table = 1; int index = 2; int value = 3; toluaI_tm_pushclass(L,table); /* pushes base */ if (lua_isnumber(L,index)) { lua_pushstring(L,"operator_set"); lua_rawget(L,-2); if (!lua_isnil(L,-1)) {/* the stack here is: table,index,value,base,operator */ /* call operator passing table, index, and value */ lua_insert(L,1); lua_pop(L,1); /* base */ lua_call(L,3,0); return 0; } } if (oo_settable(L,table,4,index,value) == 0) { toluaI_tm_pushmate(L,table); /* pushes mate */ if (lua_isnil(L,-1)) { /* creates mate table */ lua_newtable(L); toluaI_getregistry(L,"tolua_tbl_mate"); lua_pushvalue(L,table); /* that is the userdata */ lua_pushvalue(L,-3); lua_rawset(L,-3); lua_pop(L,1); /* tbl_mate */ } /* the mate table is on the top */ lua_pushvalue(L,index); lua_pushvalue(L,value); lua_rawset(L,-3); } return 0; } static int instance_gc (lua_State* L) { toluaI_tm_undoclone(L,lua_tag(L,1),lua_touserdata(L,1)); return 0; } static int gen_operator (lua_State* L) { int op1 = 1; int op2 = 2; int event = 3; char* name = toluaI_tt_concat("operator_",lua_tostring(L,event)); lua_pushstring(L,name); lua_gettable(L,op1); lua_pushvalue(L,op1); lua_pushvalue(L,op2); lua_call(L,2,1); return 1; } static int instance_operator (lua_State* L) { return gen_operator(L); } static int instance_relational (lua_State* L) { gen_operator(L); if ((int)lua_tonumber(L,-1)==0) lua_pushnil(L); return 1; } /* lua instance tags */ static int linstance_index (lua_State* L) { toluaI_tm_pushclass(L,1); oo_gettable(L,1,3,2); /* table,base,index */ return 1; } /* module tag methods */ static int module_index (lua_State* L) { int table = 1; int index = 2; lua_pushstring(L,".get"); lua_rawget(L,table); if (!lua_isnil(L,-1)) { lua_pushvalue(L,index); lua_rawget(L,-2); if (!lua_isnil(L,-1)) { lua_call(L,0,1); return 1; } } lua_pushnil(L); return 1; } static int module_settable (lua_State* L) { int table = 1; int index = 2; int value = 3; lua_pushstring(L,".set"); lua_rawget(L,table); if (!lua_isnil(L,-1)) { lua_pushvalue(L,index); lua_rawget(L,-2); if (!lua_isnil(L,-1)) { lua_pushvalue(L,value); lua_call(L,1,0); return 0; } } lua_pushvalue(L,index); lua_pushvalue(L,value); lua_rawset(L,table); return 0; } /* global variable tag methods */ static int global_getglobal (lua_State* L) { int value = 2; lua_pushstring(L,".get"); lua_rawget(L,value); lua_call(L,0,1); return 1; } static int global_setglobal (lua_State* L) { int value = 2; int newvalue = 3; lua_pushstring(L,".set"); lua_rawget(L,value); if (lua_isnil(L,-1)) lua_error(L,"value of const variable cannot be changed"); else { lua_pushvalue(L,newvalue); lua_call(L,1,0); } return 0; } /* indirect tag methods */ static int indirect_getglobal (lua_State* L) { int varname = 1; int value = 2; lua_pushstring(L,".module"); lua_gettable(L,value); lua_pushvalue(L,varname); lua_gettable(L,-2); return 1; } static int indirect_setglobal (lua_State* L) { int varname = 1; int value = 2; int newvalue = 3; lua_pushstring(L,".module"); lua_gettable(L,value); lua_pushvalue(L,varname); lua_pushvalue(L,newvalue); lua_settable(L,-3); return 0; } static void setmethods (lua_State* L) { /* global variable */ lua_pushcfunction(L,global_getglobal); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_global"),"getglobal"); lua_pushcfunction(L,global_setglobal); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_global"),"setglobal"); /* module */ lua_pushcfunction(L,module_index); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_module"),"index"); lua_pushcfunction(L,module_settable); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_module"),"settable"); /* class */ lua_pushcfunction(L,class_index); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_class"),"index"); lua_pushcfunction(L,class_settable); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_class"),"settable"); /* instance */ lua_pushcfunction(L,instance_gettable); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"gettable"); lua_pushcfunction(L,instance_settable); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"settable"); lua_pushcfunction(L,instance_operator); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"add"); lua_pushcfunction(L,instance_operator); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"sub"); lua_pushcfunction(L,instance_operator); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"mul"); lua_pushcfunction(L,instance_operator); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"div"); lua_pushcfunction(L,instance_relational); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"lt"); lua_pushcfunction(L,instance_gc); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"gc"); /* lua instance */ lua_pushcfunction(L,linstance_index); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_linstance"),"index"); /* indirect */ lua_pushcfunction(L,indirect_getglobal); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_indirect"),"getglobal"); lua_pushcfunction(L,indirect_setglobal); lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_indirect"),"setglobal"); } zangband/src/lua/tolua_tt.c0000644000000000000000000001751210250356275014722 0ustar rootroot/* tolua: type & tag manipulation. ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua_tt.c,v 1.3 2002/04/02 23:17:42 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #include "tolua.h" #include "tolua_tt.h" #include "tolua_tm.h" #include "tolua_eh.h" #include "tolua_rg.h" #include #include #include /* Global tables created in Lua registry: tolua_tbl_itype: indexed by instance tags, stores the instance types. tolua_tbl_itag: indexed by instance types, stores the instance tags. tolua_tbl_const: indexed by constant tags, stores the tags. tolua_tbl_hierarchy: indexed by instance tags, stores the base tags. */ /* exported basic type tags */ int tolua_tag_nil; int tolua_tag_number; int tolua_tag_string; int tolua_tag_userdata; int tolua_tag_table; int tolua_tag_function; static const char* gettype (lua_State* L, int tag) { const char* type; toluaI_getregistry(L,"tolua_tbl_itype"); lua_pushnumber(L,tag); lua_gettable(L,-2); type = lua_tostring(L,-1); if (type==NULL) type = "[undefined]"; lua_pop(L,2); return type; } const char* toluaI_tt_getobjtype (lua_State* L, int lo) { if (lua_gettop(L)=abs(narg)) { toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg), toluaI_tt_getobjtype(L,lua_gettop(L)+1)); return 0; } return 1; } zangband/src/lua/tolualua.c0000644000000000000000000054522410250356275014723 0ustar rootroot/* ** Lua binding: tolualua ** Generated automatically by tolua 4.0a - angband on Sun Nov 11 22:59:08 2001. */ #include "tolua.h" /* Exported function */ int tolua_tolualua_open (lua_State* tolua_S); void tolua_tolualua_close (lua_State* tolua_S); /* function to register type */ static void toluaI_reg_types (lua_State* tolua_S) { (void) tolua_S; } /* error messages */ #define TOLUA_ERR_SELF tolua_error(tolua_S,"invalid 'self'") #define TOLUA_ERR_ASSIGN tolua_error(tolua_S,"#vinvalid type in variable assignment.") /* Open function */ int tolua_tolualua_open (lua_State* tolua_S) { tolua_open(tolua_S); toluaI_reg_types(tolua_S); { /* begin embedded lua code */ static unsigned char B[] = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 95, 98, 97,115,105, 99, 32, 61, 32,123, 10, 91, 39,118, 111,105,100, 39, 93, 32, 61, 32, 39, 39, 44, 10, 91, 39, 99, 104, 97,114, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39,105,110,116, 39, 93, 32, 61, 32, 39,110, 117,109, 98,101,114, 39, 44, 10, 91, 39,115,104,111,114,116, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39,108,111,110,103, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39, 95, 99,115,116,114,105,110, 103, 39, 93, 32, 61, 32, 39,115,116,114,105,110,103, 39, 44, 10, 91, 39, 95,117,115,101,114,100, 97,116, 97, 39, 93, 32, 61, 32, 39,117,115,101,114,100, 97,116, 97, 39, 44, 10, 91, 39, 99,104, 97,114, 42, 39, 93, 32, 61, 32, 39,115,116,114, 105,110,103, 39, 44, 10, 91, 39,118,111,105,100, 42, 39, 93, 32, 61, 32, 39,117,115,101,114,100, 97,116, 97, 39, 44, 10, 91, 39, 98,111,111,108, 39, 93, 32, 61, 32, 39, 98,111,111, 108, 39, 44, 10, 91, 39, 76, 85, 65, 95, 86, 65, 76, 85, 69, 39, 93, 32, 61, 32, 39,118, 97,108,117,101, 39, 44, 10, 91, 39, 98,121,116,101, 39, 93, 32, 61, 32, 39,110,117,109, 98, 101,114, 39, 44, 10, 91, 39,115, 49, 54, 98, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39,117, 49, 54, 98, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39,115, 51, 50, 98, 39, 93, 32, 61, 32, 39,110, 117,109, 98,101,114, 39, 44, 10, 91, 39,117, 51, 50, 98, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10,125, 10, 10, 95, 98, 97,115,105, 99, 95,116, 97,103, 32, 61, 32, 123, 10, 91, 39,118,111,105,100, 39, 93, 32, 61, 32, 39, 39, 44, 10, 91, 39, 99,104, 97,114, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39, 105,110,116, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,115,104,111,114,116, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,108,111,110,103, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39, 95, 99,115,116,114,105,110,103, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 83, 84, 82, 73, 78, 71, 39, 44, 10, 91, 39, 95,117,115,101,114,100, 97,116, 97, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 85, 83, 69, 82, 68, 65, 84, 65, 39, 44, 10, 91, 39, 99,104, 97,114, 42, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 83, 84, 82, 73, 78, 71, 39, 44, 10, 91, 39,118,111,105,100, 42, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 85, 83, 69, 82, 68, 65, 84, 65, 39, 44, 10, 91, 39, 98,111,111,108, 39, 93, 32, 61, 32, 39,116,111,108, 117, 97, 95,116, 97,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 98,111,111,108, 34, 41, 39, 44, 10, 91, 39, 98,121,116, 101, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,115, 49, 54, 98, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,117, 49, 54, 98, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39, 115, 51, 50, 98, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,117, 51, 50, 98, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10,125, 10, 10, 95, 98, 97,115,105, 99, 95, 99,116,121,112,101, 32, 61, 32,123, 10,110,117,109, 98,101, 114, 32, 61, 32, 34,108,111,110,103, 34, 44, 10,115,116,114, 105,110,103, 32, 61, 32, 34, 99,111,110,115,116, 32, 99,104, 97,114, 42, 34, 44, 10,117,115,101,114,100, 97,116, 97, 32, 61, 32, 34,118,111,105,100, 42, 34, 44, 10, 98,111,111,108, 32, 61, 32, 34,105,110,116, 34, 44, 10,125, 10, 10, 10, 10, 95,117,115,101,114,116,121,112,101, 32, 61, 32,123,125, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,116,111,108,117, 97, 95,105,110,100,101,120, 32, 40,116, 44,102, 41, 10,105, 102, 32,102, 32, 61, 61, 32, 39, 95, 98, 97,115,101, 39, 32, 116,104,101,110, 10,114,101,116,117,114,110, 32,116,111,108, 117, 97, 95,111,108,100, 95,105,110,100,101,120, 40,116, 44, 102, 41, 10,101,108,115,101, 10,114,101,116,117,114,110, 32, 116, 46, 95, 98, 97,115,101, 91,102, 93, 10,101,110,100, 10, 101,110,100, 10, 10,116,111,108,117, 97, 95,116, 97,103, 32, 61, 32,110,101,119,116, 97,103, 40, 41, 10,116,111,108,117, 97, 95,111,108,100, 95,105,110,100,101,120, 32, 61, 32,115, 101,116,116, 97,103,109,101,116,104,111,100, 40,116,111,108, 117, 97, 95,116, 97,103, 44, 34,105,110,100,101,120, 34, 44, 116,111,108,117, 97, 95,105,110,100,101,120, 41, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32,116,111,108,117, 97, 95, 101,114,114,111,114, 32, 40,115, 41, 10,108,111, 99, 97,108, 32,111,117,116, 32, 61, 32, 95, 79, 85, 84, 80, 85, 84, 10, 95, 79, 85, 84, 80, 85, 84, 32, 61, 32, 95, 83, 84, 68, 69, 82, 82, 10,105,102, 32,115,116,114,115,117, 98, 40,115, 44, 49, 44, 49, 41, 32, 61, 61, 32, 39, 35, 39, 32,116,104,101, 110, 10,119,114,105,116,101, 40, 34, 92,110, 42, 42, 32,116, 111,108,117, 97, 58, 32, 34, 46, 46,115,116,114,115,117, 98, 40,115, 44, 50, 41, 46, 46, 34, 46, 92,110, 92,110, 34, 41, 10,101,108,115,101, 10,119,114,105,116,101, 40, 34, 92,110, 42, 42, 32,116,111,108,117, 97, 32,105,110,116,101,114,110, 97,108, 32,101,114,114,111,114, 58, 32, 34, 46, 46,115, 46, 46, 34, 46, 92,110, 92,110, 34, 41, 10,114,101,116,117,114, 110, 10,101,110,100, 10, 10,105,102, 32, 95, 99,117,114,114, 95, 99,111,100,101, 32,116,104,101,110, 10,108,111, 99, 97, 108, 32, 95, 44, 95, 44,115, 32, 61, 32,115,116,114,102,105, 110,100, 40, 95, 99,117,114,114, 95, 99,111,100,101, 44, 34, 94, 37,115, 42, 40, 46, 45, 92,110, 41, 34, 41, 10,105,102, 32,115, 61, 61,110,105,108, 32,116,104,101,110, 32,115, 32, 61, 32, 95, 99,117,114,114, 95, 99,111,100,101, 32,101,110, 100, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 95, 117,115,101,114,100, 97,116, 97, 34, 44, 34,118,111,105,100, 42, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 95, 99,115,116,114,105,110,103, 34, 44, 34, 99,104, 97, 114, 42, 34, 41, 10,119,114,105,116,101, 40, 34, 67,111,100, 101, 32, 98,101,105,110,103, 32,112,114,111, 99,101,115,115, 101,100, 58, 92,110, 34, 46, 46,115, 46, 46, 34, 92,110, 34, 41, 10,101,110,100, 10, 95, 79, 85, 84, 80, 85, 84, 32, 61, 32,111,117,116, 10,101,110,100, 10, 10, 10, 95, 69, 82, 82, 79, 82, 77, 69, 83, 83, 65, 71, 69, 32, 61, 32,116,111,108, 117, 97, 95,101,114,114,111,114, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32,114,101,103,116,121,112,101, 32, 40,116, 41, 10,105,102, 32,110,111,116, 32,105,115,116,121,112,101, 40,116, 41, 32,116,104,101,110, 10, 95,117,115,101,114,116, 121,112,101, 91,116, 93, 32, 61, 32,116, 10,101,110,100, 10, 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32,116, 97,103,118, 97,114, 40,116,121,112,101, 44, 99,111,110,115,116, 41, 10,105,102, 32,116,121,112,101, 32, 61, 61, 32, 39, 39, 32,111,114, 32, 116,121,112,101, 32, 61, 61, 32, 39,118,111,105,100, 39, 32, 116,104,101,110, 10,114,101,116,117,114,110, 32,116,121,112, 101, 44, 48, 10,101,108,115,101, 10,108,111, 99, 97,108, 32, 109, 44,116, 32, 61, 32,102,105,110,100,116,121,112,101,100, 101,102, 40,116,121,112,101, 41, 10,105,102, 32,105,115, 98, 97,115,105, 99, 40,116, 41, 32,116,104,101,110, 10,114,101, 116,117,114,110, 32,116, 44, 32, 95, 98, 97,115,105, 99, 95, 116, 97,103, 91,116, 93, 10,101,110,100, 10,105,102, 32,115, 116,114,102,105,110,100, 40,109, 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101,110, 32, 99,111,110,115,116, 32, 61, 32, 39, 99,111,110,115,116, 39, 32,101,110,100, 10,114,101, 103,116,121,112,101, 40,116, 41, 10,105,102, 32, 99,111,110, 115,116, 32, 97,110,100, 32, 99,111,110,115,116, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,116, 32, 61, 32, 39, 99, 111,110,115,116, 32, 39, 46, 46,116, 10,101,110,100, 10,114, 101,116,117,114,110, 32,116, 44, 39,116,111,108,117, 97, 95, 116, 97,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,116, 46, 46, 39, 34, 41, 39, 10,101,110,100, 10,101,110, 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,105,115, 98, 97,115,105, 99, 32, 40,116,121,112,101, 41, 10,108,111, 99, 97,108, 32,109, 44,116, 32, 61, 32,102,105,110,100,116, 121,112,101,100,101,102, 40,116,121,112,101, 41, 10,108,111, 99, 97,108, 32, 98, 32, 61, 32, 95, 98, 97,115,105, 99, 91, 116, 93, 10,105,102, 32, 98, 32,116,104,101,110, 10,114,101, 116,117,114,110, 32, 98, 44, 95, 98, 97,115,105, 99, 95, 99, 116,121,112,101, 91, 98, 93, 10,101,110,100, 10,114,101,116, 117,114,110, 32,110,105,108, 10,101,110,100, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32,105,115,116,121,112,101, 32, 40,116, 41, 10,114,101,116,117,114,110, 32, 95, 98, 97,115, 105, 99, 91,116, 93, 32,111,114, 32, 95,117,115,101,114,116, 121,112,101, 91,116, 93, 32,111,114, 32,105,115,116,121,112, 101,100,101,102, 40,116, 41, 10,101,110,100, 10, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32,115,112,108,105,116, 32, 40,115, 44,116, 41, 10,108,111, 99, 97,108, 32,108, 32, 61, 32,123,110, 61, 48,125, 10,108,111, 99, 97,108, 32,102, 32, 61, 32,102,117,110, 99,116,105,111,110, 32, 40,115, 41, 10, 37,108, 46,110, 32, 61, 32, 37,108, 46,110, 32, 43, 32, 49, 10, 37,108, 91, 37,108, 46,110, 93, 32, 61, 32,115, 10,101, 110,100, 10,108,111, 99, 97,108, 32,112, 32, 61, 32, 34, 37, 115, 42, 40, 46, 45, 41, 37,115, 42, 34, 46, 46,116, 46, 46, 34, 37,115, 42, 34, 10,115, 32, 61, 32,103,115,117, 98, 40, 115, 44, 34, 94, 37,115, 43, 34, 44, 34, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 37,115, 43, 36, 34, 44, 34, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,112, 44,102, 41, 10,108, 46,110, 32, 61, 32,108, 46,110, 32, 43, 32, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32,103, 115,117, 98, 40,115, 44, 34, 40, 37,115, 37,115, 42, 41, 36, 34, 44, 34, 34, 41, 10,114,101,116,117,114,110, 32,108, 10, 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,111,110, 99, 97,116, 32, 40,116, 44,102, 44,108, 41, 10,108,111, 99, 97,108, 32,115, 32, 61, 32, 39, 39, 10,108, 111, 99, 97,108, 32,105, 61,102, 10,119,104,105,108,101, 32, 105, 60, 61,108, 32,100,111, 10,115, 32, 61, 32,115, 46, 46, 116, 91,105, 93, 10,105, 32, 61, 32,105, 43, 49, 10,105,102, 32,105, 32, 60, 61, 32,108, 32,116,104,101,110, 32,115, 32, 61, 32,115, 46, 46, 39, 32, 39, 32,101,110,100, 10,101,110, 100, 10,114,101,116,117,114,110, 32,115, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,111,117,116,112, 117,116, 32, 40, 46, 46, 46, 41, 10,108,111, 99, 97,108, 32, 105, 61, 49, 10,119,104,105,108,101, 32,105, 60, 61, 97,114, 103, 46,110, 32,100,111, 10,105,102, 32, 95, 99,111,110,116, 32, 97,110,100, 32,110,111,116, 32,115,116,114,102,105,110, 100, 40, 95, 99,111,110,116, 44, 39, 91, 37, 40, 44, 34, 93, 39, 41, 32, 97,110,100, 10,115,116,114,102,105,110,100, 40, 97,114,103, 91,105, 93, 44, 34, 94, 91, 37, 97, 95,126, 93, 34, 41, 32,116,104,101,110, 10,119,114,105,116,101, 40, 39, 32, 39, 41, 10,101,110,100, 10,119,114,105,116,101, 40, 97, 114,103, 91,105, 93, 41, 10,105,102, 32, 97,114,103, 91,105, 93, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10, 95, 99, 111,110,116, 32, 61, 32,115,116,114,115,117, 98, 40, 97,114, 103, 91,105, 93, 44, 45, 49, 44, 45, 49, 41, 10,101,110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,105,102, 32,115,116,114,102,105,110,100, 40, 97,114,103, 91, 97,114, 103, 46,110, 93, 44, 34, 91, 37, 47, 37, 41, 37, 59, 37,123, 37,125, 93, 36, 34, 41, 32,116,104,101,110, 10, 95, 99,111, 110,116, 61,110,105,108, 32,119,114,105,116,101, 40, 39, 92, 110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115, 115, 70,101, 97,116,117,114,101, 32, 61, 32,123, 10,125, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115, 115, 70,101, 97,116,117,114,101, 58,115,117,112, 99,111,100, 101, 32, 40, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117, 114,101, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,101, 110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99, 108, 97,115,115, 70,101, 97,116,117,114,101, 58,114,101,103, 105,115,116,101,114, 32, 40, 41, 10,101,110,100, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70, 101, 97,116,117,114,101, 58,117,110,114,101,103,105,115,116, 101,114, 32, 40, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116, 117,114,101, 58,112,114,101, 97,109, 98,108,101, 32, 40, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111, 110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 58, 105,110, 99,108, 97,115,115, 32, 40, 41, 10,105,102, 32,115, 101,108,102, 46,112, 97,114,101,110,116, 32, 97,110,100, 32, 115,101,108,102, 46,112, 97,114,101,110,116, 46,116,121,112, 101, 32, 61, 61, 32, 39, 99,108, 97,115,115, 39, 32,116,104, 101,110, 10,114,101,116,117,114,110, 32,115,101,108,102, 46, 112, 97,114,101,110,116, 46,110, 97,109,101, 10,101,108,115, 101, 10,114,101,116,117,114,110, 32,110,105,108, 10,101,110, 100, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105, 111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 58,105,110,109,111,100,117,108,101, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46,112, 97,114,101,110,116, 32, 97,110, 100, 32,115,101,108,102, 46,112, 97,114,101,110,116, 46,116, 121,112,101, 32, 61, 61, 32, 39,109,111,100,117,108,101, 39, 32,116,104,101,110, 10,114,101,116,117,114,110, 32,115,101, 108,102, 46,112, 97,114,101,110,116, 46,110, 97,109,101, 10, 101,108,115,101, 10,114,101,116,117,114,110, 32,110,105,108, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 58, 99,102,117,110, 99,110, 97,109,101, 32, 40,110, 41, 10,105,102, 32,115,101,108,102, 46,112, 97, 114,101,110,116, 32,116,104,101,110, 10,110, 32, 61, 32,115, 101,108,102, 46,112, 97,114,101,110,116, 58, 99,102,117,110, 99,110, 97,109,101, 40,110, 41, 10,101,110,100, 10,105,102, 32,115,101,108,102, 46,108,110, 97,109,101, 32,116,104,101, 110, 10,114,101,116,117,114,110, 32,110, 46, 46, 39, 95, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 10,101,108, 115,101, 10,114,101,116,117,114,110, 32,110, 46, 46, 39, 95, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,110, 100, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 86, 101,114, 98, 97,116,105,109, 32, 61, 32,123, 10,108,105,110, 101, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10, 125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 86, 101,114, 98, 97,116,105,109, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58,112, 114,101, 97,109, 98,108,101, 32, 40, 41, 10,105,102, 32,110, 111,116, 32,115,101,108,102, 46, 99,111,110,100, 32,116,104, 101,110, 10,119,114,105,116,101, 40,115,101,108,102, 46,108, 105,110,101, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 101,114, 98, 97,116,105,109, 58,115,117,112, 99,111,100,101, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 99,111,110, 100, 32,116,104,101,110, 10,119,114,105,116,101, 40,115,101, 108,102, 46,108,105,110,101, 41, 10,119,114,105,116,101, 40, 39, 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58,114,101,103,105,115,116, 101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 99, 111,110,100, 32,116,104,101,110, 10,119,114,105,116,101, 40, 115,101,108,102, 46,108,105,110,101, 41, 10,101,110,100, 10, 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58, 112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108, 111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34, 86,101,114, 98, 97,116,105,109,123, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,108,105,110,101, 32, 61, 32, 39, 34, 46, 46,115,101,108, 102, 46,108,105,110,101, 46, 46, 34, 39, 44, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 86,101,114, 98, 97,116,105,109, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115, 101, 32, 61, 32, 99,108, 97,115,115, 86,101,114, 98, 97,116, 105,109, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108, 117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,100, 40, 116, 41, 10,114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 86,101, 114, 98, 97,116,105,109, 32, 40,108, 41, 10,108,111, 99, 97, 108, 32, 99, 10,105,102, 32,115,116,114,115,117, 98, 40,108, 44, 49, 44, 49, 41, 32, 61, 61, 32, 39, 36, 39, 32,116,104, 101,110, 10, 99, 32, 61, 32, 49, 10,108, 32, 61, 32,115,116, 114,115,117, 98, 40,108, 44, 50, 41, 10,101,110,100, 10,114, 101,116,117,114,110, 32, 95, 86,101,114, 98, 97,116,105,109, 32,123, 10,108,105,110,101, 32, 61, 32,108, 44, 10, 99,111, 110,100, 32, 61, 32, 99, 10,125, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 67,111,100,101, 32, 61, 32,123, 10,116, 101,120,116, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115, 115, 67,111,100,101, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,100,101, 58,114,101,103,105,115,116,101, 114, 32, 40, 41, 10, 10,108,111, 99, 97,108, 32,115, 32, 61, 32, 99,108,101, 97,110, 40,115,101,108,102, 46,116,101,120, 116, 41, 10,105,102, 32,110,111,116, 32,115, 32,116,104,101, 110, 10,101,114,114,111,114, 40, 34,112, 97,114,115,101,114, 32,101,114,114,111,114, 32,105,110, 32,101,109, 98,101,100, 100,101,100, 32, 99,111,100,101, 34, 41, 10,101,110,100, 10, 10, 10,111,117,116,112,117,116, 40, 39, 92,110, 32,123, 32, 47, 42, 32, 98,101,103,105,110, 32,101,109, 98,101,100,100, 101,100, 32,108,117, 97, 32, 99,111,100,101, 32, 42, 47, 92, 110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,115,116, 97,116,105, 99, 32,117,110,115,105,103,110,101,100, 32, 99, 104, 97,114, 32, 66, 91, 93, 32, 61, 32,123, 92,110, 32, 39, 41, 10,108,111, 99, 97,108, 32,116, 61,123,110, 61, 48,125, 10,108,111, 99, 97,108, 32, 98, 32, 61, 32,103,115,117, 98, 40,115, 44, 39, 40, 46, 41, 39, 44,102,117,110, 99,116,105, 111,110, 32, 40, 99, 41, 10,108,111, 99, 97,108, 32,101, 32, 61, 32, 39, 39, 10, 37,116, 46,110, 61, 37,116, 46,110, 43, 49, 32,105,102, 32, 37,116, 46,110, 61, 61, 49, 53, 32,116, 104,101,110, 32, 37,116, 46,110, 61, 48, 32,101, 61, 39, 92, 110, 32, 39, 32,101,110,100, 10,114,101,116,117,114,110, 32, 102,111,114,109, 97,116, 40, 39, 37, 51,117, 44, 37,115, 39, 44,115,116,114, 98,121,116,101, 40, 99, 41, 44,101, 41, 10, 101,110,100, 10, 41, 10,111,117,116,112,117,116, 40, 98, 46, 46,115,116,114, 98,121,116,101, 40, 34, 32, 34, 41, 41, 10, 111,117,116,112,117,116, 40, 39, 92,110, 32,125, 59, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,100,111, 98,117,102,102,101,114, 40,116,111,108,117, 97, 95, 83, 44, 40, 99,104, 97,114, 42, 41, 66, 44,115,105,122, 101,111,102, 40, 66, 41, 44, 34,116,111,108,117, 97, 58, 32, 101,109, 98,101,100,100,101,100, 32, 76,117, 97, 32, 99,111, 100,101, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,125, 32, 47, 42, 32,101,110,100, 32,111,102, 32,101, 109, 98,101,100,100,101,100, 32,108,117, 97, 32, 99,111,100, 101, 32, 42, 47, 92,110, 92,110, 39, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97, 115,115, 67,111,100,101, 58,112,114,105,110,116, 32, 40,105, 100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,105, 110,116, 40,105,100,101,110,116, 46, 46, 34, 67,111,100,101, 123, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,116,101,120,116, 32, 61, 32, 91, 91, 34, 46, 46,115,101,108,102, 46,116,101,120,116, 46, 46, 34, 93, 93, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101, 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 67,111,100,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97, 115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,100,101, 10, 115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95, 116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10, 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 67,111,100,101, 32, 40,108, 41, 10,114,101,116,117,114,110, 32, 95, 67,111,100, 101, 32,123, 10,116,101,120,116, 32, 61, 32,108, 10,125, 10, 101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97, 115,115, 84,121,112,101,100,101,102, 32, 61, 32,123, 10,117, 116,121,112,101, 32, 61, 32, 39, 39, 44, 10,109,111,100, 32, 61, 32, 39, 39, 44, 10,116,121,112,101, 32, 61, 32, 39, 39, 10,125, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99, 108, 97,115,115, 84,121,112,101,100,101,102, 58,112,114,105, 110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 84,121,112,101,100,101,102,123, 34, 41, 10,112,114,105, 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,117,116,121, 112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,117, 116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105, 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105, 100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101, 110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 84,121,112,101,100,101,102, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 84, 121,112,101,100,101,102, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112, 101,110,100,116,121,112,101,100,101,102, 40,116, 41, 10,114, 101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 84,121,112,101,100,101, 102, 32, 40,115, 41, 10,105,102, 32,115,116,114,102,105,110, 100, 40,115, 44, 39, 91, 37, 42, 38, 93, 39, 41, 32,116,104, 101,110, 10,116,111,108,117, 97, 95,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,105,100, 32,116,121,112,101,100, 101,102, 58, 32,112,111,105,110,116,101,114,115, 32, 40, 97, 110,100, 32,114,101,102,101,114,101,110, 99,101,115, 41, 32, 97,114,101, 32,110,111,116, 32,115,117,112,112,111,114,116, 101,100, 34, 41, 10,101,110,100, 10,108,111, 99, 97,108, 32, 116, 32, 61, 32,115,112,108,105,116, 40,103,115,117, 98, 40, 115, 44, 34, 37,115, 37,115, 42, 34, 44, 34, 32, 34, 41, 44, 34, 32, 34, 41, 10,114,101,116,117,114,110, 32, 95, 84,121, 112,101,100,101,102, 32,123, 10,117,116,121,112,101, 32, 61, 32,116, 91,116, 46,110, 93, 44, 10,116,121,112,101, 32, 61, 32,116, 91,116, 46,110, 45, 49, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,116, 44, 49, 44,116, 46, 110, 45, 50, 41, 10,125, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115, 115, 67,111,110,116, 97,105,110,101,114, 32, 61, 10,123, 10, 99,117,114,114, 32, 61, 32,110,105,108, 44, 10, 95, 98, 97, 115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117, 114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 44,116,111, 108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97, 105,110,101,114, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,112,117,115,104, 40,115,101,108,102, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101, 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,111,112, 40, 41, 10, 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58,115,117,112, 99,111,100,101, 32, 40, 41, 10,112,117,115, 104, 40,115,101,108,102, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,115,117, 112, 99,111,100,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,111,112, 40, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 67,111, 110,116, 97,105,110,101,114, 32, 40,115,101,108,102, 41, 10, 115,101,108,102, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 10,115,101, 116,116, 97,103, 40,115,101,108,102, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,115,101,108,102, 46,110, 32, 61, 32, 48, 10,115,101,108,102, 46,116,121,112,101,100,101,102,115, 32, 61, 32,123,110, 61, 48,125, 10,115,101,108,102, 46,108, 110, 97,109,101,115, 32, 61, 32,123,125, 10,114,101,116,117, 114,110, 32,115,101,108,102, 10,101,110,100, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32,112,117,115,104, 32, 40,116, 41, 10, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101, 114, 46, 99,117,114,114, 32, 61, 32,116, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,112,111,112, 32, 40, 41, 10, 99,108, 97,115,115, 67,111,110,116, 97,105,110, 101,114, 46, 99,117,114,114, 32, 61, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 46, 112, 97,114,101,110,116, 10,101,110,100, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 97,112,112,101,110,100, 32, 40, 116, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 58, 97,112,112,101,110,100, 40,116, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 97,112,112,101,110, 100,116,121,112,101,100,101,102, 32, 40,116, 41, 10,114,101, 116,117,114,110, 32, 99,108, 97,115,115, 67,111,110,116, 97, 105,110,101,114, 46, 99,117,114,114, 58, 97,112,112,101,110, 100,116,121,112,101,100,101,102, 40,116, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,102,105,110, 100,116,121,112,101,100,101,102, 32, 40,116,121,112,101, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115, 67,111, 110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,102,105, 110,100,116,121,112,101,100,101,102, 40,116,121,112,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,105,115,116,121,112,101,100,101,102, 32, 40,116,121,112, 101, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 58, 105,115,116,121,112,101,100,101,102, 40,116,121,112,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58, 97,112,112,101,110,100, 32, 40,116, 41, 10,115,101,108, 102, 46,110, 32, 61, 32,115,101,108,102, 46,110, 32, 43, 32, 49, 10,115,101,108,102, 91,115,101,108,102, 46,110, 93, 32, 61, 32,116, 10,116, 46,112, 97,114,101,110,116, 32, 61, 32, 115,101,108,102, 10,101,110,100, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97, 105,110,101,114, 58, 97,112,112,101,110,100,116,121,112,101, 100,101,102, 32, 40,116, 41, 10,115,101,108,102, 46,116,121, 112,101,100,101,102,115, 46,110, 32, 61, 32,115,101,108,102, 46,116,121,112,101,100,101,102,115, 46,110, 32, 43, 32, 49, 10,115,101,108,102, 46,116,121,112,101,100,101,102,115, 91, 115,101,108,102, 46,116,121,112,101,100,101,102,115, 46,110, 93, 32, 61, 32,116, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58,111,118,101,114,108,111, 97,100, 32, 40,108,110, 97,109,101, 41, 10,105,102, 32,110,111,116, 32, 115,101,108,102, 46,108,110, 97,109,101,115, 91,108,110, 97, 109,101, 93, 32,116,104,101,110, 10,115,101,108,102, 46,108, 110, 97,109,101,115, 91,108,110, 97,109,101, 93, 32, 61, 32, 48, 10,101,108,115,101, 10,115,101,108,102, 46,108,110, 97, 109,101,115, 91,108,110, 97,109,101, 93, 32, 61, 32,115,101, 108,102, 46,108,110, 97,109,101,115, 91,108,110, 97,109,101, 93, 32, 43, 32, 49, 10,101,110,100, 10,114,101,116,117,114, 110, 32,102,111,114,109, 97,116, 40, 34, 37, 48, 50,100, 34, 44,115,101,108,102, 46,108,110, 97,109,101,115, 91,108,110, 97,109,101, 93, 41, 10,101,110,100, 10, 10,102,117,110, 99, 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97, 105,110,101,114, 58,102,105,110,100,116,121,112,101,100,101, 102, 32, 40,116,121,112,101, 41, 10,108,111, 99, 97,108, 32, 101,110,118, 32, 61, 32,115,101,108,102, 10,119,104,105,108, 101, 32,101,110,118, 32,100,111, 10,105,102, 32,101,110,118, 46,116,121,112,101,100,101,102,115, 32,116,104,101,110, 10, 108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,105, 93, 32,100,111, 10,105,102, 32,101,110,118, 46,116,121,112, 101,100,101,102,115, 91,105, 93, 46,117,116,121,112,101, 32, 61, 61, 32,116,121,112,101, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,109,111,100, 49, 44,116,121,112,101, 49, 32, 61, 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91, 105, 93, 46,109,111,100, 44,101,110,118, 46,116,121,112,101, 100,101,102,115, 91,105, 93, 46,116,121,112,101, 10,108,111, 99, 97,108, 32,109,111,100, 50, 44,116,121,112,101, 50, 32, 61, 32,102,105,110,100,116,121,112,101,100,101,102, 40,116, 121,112,101, 49, 41, 10,114,101,116,117,114,110, 32,109,111, 100, 50, 46, 46, 39, 32, 39, 46, 46,109,111,100, 49, 44,116, 121,112,101, 50, 10,101,110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10,101,110,118, 32, 61, 32,101,110,118, 46,112, 97,114,101,110,116, 10,101,110,100, 10,114,101,116,117,114,110, 32, 39, 39, 44,116,121,112,101, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58, 105,115,116,121,112,101,100,101,102, 32, 40,116,121,112,101, 41, 10,108,111, 99, 97,108, 32,101,110,118, 32, 61, 32,115, 101,108,102, 10,119,104,105,108,101, 32,101,110,118, 32,100, 111, 10,105,102, 32,101,110,118, 46,116,121,112,101,100,101, 102,115, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,101,110,118, 46,116,121, 112,101,100,101,102,115, 91,105, 93, 32,100,111, 10,105,102, 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,105, 93, 46,117,116,121,112,101, 32, 61, 61, 32,116,121,112,101, 32,116,104,101,110, 10,114,101,116,117,114,110, 32, 49, 10, 101,110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10,101,110,118, 32, 61, 32,101,110,118, 46, 112, 97,114,101,110,116, 10,101,110,100, 10,114,101,116,117, 114,110, 32,110,105,108, 10,101,110,100, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110, 116, 97,105,110,101,114, 58,100,111,112, 97,114,115,101, 32, 40,115, 41, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,110, 97,109,101, 44, 98,111,100,121, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,109,111,100,117,108,101, 37,115, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32, 116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 77,111,100,117,108,101, 40,110, 97,109,101, 44, 98, 111,100,121, 41, 10,114,101,116,117,114,110, 32,115,116,114, 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10, 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,110, 97,109,101, 32, 61, 32,115,116,114,102, 105,110,100, 40,115, 44, 34, 94, 37,115, 42, 35,100,101,102, 105,110,101, 37,115, 37,115, 42, 40, 91, 94, 37,115, 93, 42, 41, 91, 94, 92,110, 93, 42, 92,110, 37,115, 42, 34, 41, 10, 105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40, 115, 44, 98, 44,101, 41, 10, 68,101,102,105,110,101, 40,110, 97,109,101, 41, 10,114,101,116,117,114,110, 32,115,116,114, 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10, 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44, 98,111,100,121, 32, 61, 32,115,116,114,102, 105,110,100, 40,115, 44, 34, 94, 37,115, 42,101,110,117,109, 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59, 63, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,116,104,101, 110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32, 115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 69, 110,117,109,101,114, 97,116,101, 40, 98,111,100,121, 41, 10, 114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100, 10, 10, 100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44, 98,111, 100,121, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,105, 110,100, 40,115, 44, 34, 94, 37,115, 42,116,121,112,101,100, 101,102, 37,115, 37,115, 42,101,110,117,109, 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,115, 42, 40, 91, 37,119, 95, 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99, 117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115, 117, 98, 40,115, 44, 98, 44,101, 41, 10, 69,110,117,109,101, 114, 97,116,101, 40, 98,111,100,121, 41, 10, 84,121,112,101, 100,101,102, 40, 34,105,110,116, 32, 34, 46, 46,110, 97,109, 101, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110, 100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44, 101, 44,100,101, 99,108, 44,107,105,110,100, 44, 97,114,103, 44, 99,111,110,115,116, 32, 61, 32,115,116,114,102,105,110, 100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 37,115, 37, 42, 38, 93, 42,111,112,101,114, 97,116,111,114, 41, 37,115, 42, 40, 91, 94, 37,115, 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32, 116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 79,112,101,114, 97,116,111,114, 40,100,101, 99,108, 44,107,105,110,100, 44, 97,114,103, 44, 99,111,110,115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,115,116, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95, 64, 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63, 110, 63,115, 63,116, 63, 41, 37,115, 42, 61, 63, 37,115, 42, 48, 63, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 110,111,116, 32, 98, 32,116,104,101,110, 10, 10, 98, 44,101, 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,115,116, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,101, 110,100, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99, 117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115, 117, 98, 40,115, 44, 98, 44,101, 41, 10, 70,117,110, 99,116, 105,111,110, 40,100,101, 99,108, 44, 97,114,103, 44, 99,111, 110,115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114, 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10, 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,100,101, 99,108, 44, 97,114,103, 44, 99,111, 110,115,116, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95, 64, 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 37, 98, 123,125, 37,115, 42, 34, 41, 10,105,102, 32,110,111,116, 32, 98, 32,116,104,101,110, 10, 10, 98, 44,101, 44,100,101, 99, 108, 44, 97,114,103, 44, 99,111,110,115,116, 32, 61, 32,115, 116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 37, 98,123,125, 37,115, 42, 34, 41, 10,101,110, 100, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117, 114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 70,117,110, 99,116,105, 111,110, 40,100,101, 99,108, 44, 97,114,103, 44, 99,111,110, 115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114,115, 117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101, 110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,110, 97,109,101, 44, 98, 97,115,101, 44, 98,111, 100,121, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 99,108, 97,115,115, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32,110,111,116, 32, 98, 32,116,104,101,110, 10, 98, 44,101, 44,110, 97,109,101, 44, 98, 97,115,101, 44, 98,111,100,121, 32, 61, 32,115,116,114, 102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,115,116,114, 117, 99,116, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37, 119, 93, 42, 41, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10, 105,102, 32,110,111,116, 32, 98, 32,116,104,101,110, 10, 98, 97,115,101, 32, 61, 32, 39, 39, 10, 98, 44,101, 44, 98,111, 100,121, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,105, 110,100, 40,115, 44, 34, 94, 37,115, 42,116,121,112,101,100, 101,102, 37,115, 37,115, 42,115,116,114,117, 99,116, 37,115, 37,115, 42, 91, 95, 37,119, 93, 42, 37,115, 42, 40, 37, 98, 123,125, 41, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37, 119, 93, 42, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,101, 110,100, 10,101,110,100, 10,105,102, 32, 98, 32,116,104,101, 110, 10,105,102, 32, 98, 97,115,101, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,108,111, 99, 97,108, 32, 98, 44,101, 10, 98, 44,101, 44, 98, 97,115,101, 32, 61, 32,115,116,114, 102,105,110,100, 40, 98, 97,115,101, 44, 34, 46, 45, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 36, 34, 41, 10, 101,110,100, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 67,108, 97,115,115, 40,110, 97,109,101, 44, 98, 97,115, 101, 44, 98,111,100,121, 41, 10,114,101,116,117,114,110, 32, 115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101, 110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,116,121,112,101,115, 32, 61, 32, 115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 116,121,112,101,100,101,102, 37,115, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100, 101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44, 101, 41, 10, 84,121,112,101,100,101,102, 40,116,121,112,101, 115, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110, 100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44, 101, 44,100,101, 99,108, 32, 61, 32,115,116,114,102,105,110, 100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 64, 37,115, 37,119, 37,100, 37, 42, 38, 93, 42, 91, 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117, 114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 86, 97,114,105, 97, 98, 108,101, 40,100,101, 99,108, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10, 101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,100,101, 99,108, 32, 61, 32, 115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 93, 91, 95, 64, 37,115, 37,119, 37,100, 37, 42, 38, 37, 45, 37, 62, 93, 42, 91, 93, 95, 37, 119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10, 105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40, 115, 44, 98, 44,101, 41, 10, 65,114,114, 97,121, 40,100,101, 99,108, 41, 10,114,101,116,117,114,110, 32,115,116,114,115, 117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101, 110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44, 99,111,100,101, 32, 61, 32,115,116,114,102,105, 110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 37, 98, 92, 49, 92, 50, 41, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 67,111,100,101, 40,115,116,114,115,117, 98, 40, 99,111, 100,101, 44, 50, 44, 45, 50, 41, 41, 10,114,101,116,117,114, 110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108, 111, 99, 97,108, 32, 98, 44,101, 44,108,105,110,101, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 37, 36, 40, 46, 45, 92,110, 41, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 86,101,114, 98, 97,116,105,109, 40,108,105,110,101, 41, 10,114,101,116,117,114,110, 32,115, 116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110, 100, 10,101,110,100, 10, 10, 10,105,102, 32,103,115,117, 98, 40,115, 44, 34, 37,115, 37,115, 42, 34, 44, 34, 34, 41, 32, 126, 61, 32, 34, 34, 32,116,104,101,110, 10, 95, 99,117,114, 114, 95, 99,111,100,101, 32, 61, 32,115, 10,101,114,114,111, 114, 40, 34, 35,112, 97,114,115,101, 32,101,114,114,111,114, 34, 41, 10,101,108,115,101, 10,114,101,116,117,114,110, 32, 34, 34, 10,101,110,100, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58,112, 97,114,115,101, 32, 40,115, 41, 10,119,104,105,108,101, 32,115, 32,126, 61, 32, 39, 39, 32, 100,111, 10,115, 32, 61, 32,115,101,108,102, 58,100,111,112, 97,114,115,101, 40,115, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 80, 97, 99,107, 97, 103,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 44, 10,116,121,112,101, 32, 61, 32, 39,112, 97, 99,107, 97,103, 101, 39, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97, 115,115, 80, 97, 99,107, 97,103,101, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111, 110, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 58, 112,114,105,110,116, 32, 40, 41, 10,112,114,105,110,116, 40, 34, 80, 97, 99,107, 97,103,101, 58, 32, 34, 46, 46,115,101, 108,102, 46,110, 97,109,101, 41, 10,108,111, 99, 97,108, 32, 105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91, 105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,112, 114,105,110,116, 40, 34, 34, 44, 34, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 58,112,114,101,112,114,111, 99,101,115, 115, 32, 40, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32, 34, 92,110, 34, 46, 46,115,101,108,102, 46, 99,111, 100,101, 10, 10,108,111, 99, 97,108, 32, 86, 32, 61, 32,123, 125, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103, 115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 92,110, 40, 37,115, 42, 37, 36, 91, 94, 37, 91, 37, 93, 93, 91, 94, 92,110, 93, 42, 41, 34, 44,102,117,110, 99,116,105, 111,110, 32, 40,118, 41, 10,116,105,110,115,101,114,116, 40, 37, 86, 44,118, 41, 10,114,101,116,117,114,110, 32, 34, 92, 110, 36, 34, 46, 46,103,101,116,110, 40, 37, 86, 41, 46, 46, 34, 36, 34, 10,101,110,100, 41, 10, 10,108,111, 99, 97,108, 32, 67, 32, 61, 32,123,125, 10,115,101,108,102, 46, 99,111, 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 37, 91, 34, 44, 34, 92, 49, 34, 41, 10,115,101,108,102, 46, 99,111, 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 37, 93, 34, 44, 34, 92, 50, 34, 41, 10,115,101,108,102, 46, 99,111, 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 40, 37, 98, 92, 49, 92, 50, 41, 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40, 99, 41, 10, 116,105,110,115,101,114,116, 40, 37, 67, 44, 99, 41, 10,114, 101,116,117,114,110, 32, 34, 92,110, 36, 91, 34, 46, 46,103, 101,116,110, 40, 37, 67, 41, 46, 46, 34, 93, 36, 34, 10,101, 110,100, 41, 10, 10, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111, 100,101, 44, 34, 40, 47, 47, 91, 94, 92,110, 93, 42, 41, 34, 44, 34, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100, 101, 44, 34, 47, 37, 42, 34, 44, 34, 92, 49, 34, 41, 10,115, 101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 37, 42, 47, 34, 44, 34, 92, 50, 34, 41, 10,115,101,108,102, 46, 99,111, 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 37, 98, 92, 49, 92, 50, 34, 44, 34, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32, 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 92, 49, 34, 44, 34, 47, 37, 42, 34, 41, 10,115,101,108, 102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115, 101,108,102, 46, 99,111,100,101, 44, 34, 92, 50, 34, 44, 34, 37, 42, 47, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111, 100,101, 44, 34, 37,115, 42, 64, 37,115, 42, 34, 44, 34, 64, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32, 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 37,115, 63,105,110,108,105,110,101, 40, 37,115, 41, 34, 44, 34, 37, 49, 34, 41, 10,115,101,108,102, 46, 99,111,100, 101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99, 111,100,101, 44, 34, 37,115, 63,101,120,116,101,114,110, 40, 37,115, 41, 34, 44, 34, 37, 49, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101, 108,102, 46, 99,111,100,101, 44, 34, 37,115, 63,118,105,114, 116,117, 97,108, 40, 37,115, 41, 34, 44, 34, 37, 49, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115, 117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,112, 117, 98,108,105, 99, 58, 34, 44, 34, 34, 41, 10,115,101,108, 102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115, 101,108,102, 46, 99,111,100,101, 44, 34, 40, 91, 94, 37,119, 95, 93, 41,118,111,105,100, 37,115, 42, 37, 42, 34, 44, 34, 37, 49, 95,117,115,101,114,100, 97,116, 97, 32, 34, 41, 10, 115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 40, 91, 94, 37,119, 95, 93, 41,118,111,105,100, 37,115, 42, 37, 42, 34, 44, 34, 37, 49, 95,117,115,101,114,100, 97,116, 97, 32, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32, 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 40, 91, 94, 37,119, 95, 93, 41, 99,104, 97,114, 37,115, 42, 37, 42, 34, 44, 34, 37, 49, 95, 99,115,116,114,105,110, 103, 32, 34, 41, 10, 10, 10,115,101,108,102, 46, 99,111,100, 101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99, 111,100,101, 44, 34, 37, 36, 37, 91, 40, 37,100, 43, 41, 37, 93, 37, 36, 34, 44,102,117,110, 99,116,105,111,110, 32, 40, 110, 41, 10,114,101,116,117,114,110, 32, 37, 67, 91,116,111, 110,117,109, 98,101,114, 40,110, 41, 93, 10,101,110,100, 41, 10, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103, 115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 37, 36, 40, 37,100, 43, 41, 37, 36, 34, 44,102,117,110, 99, 116,105,111,110, 32, 40,110, 41, 10,114,101,116,117,114,110, 32, 37, 86, 91,116,111,110,117,109, 98,101,114, 40,110, 41, 93, 10,101,110,100, 41, 10,101,110,100, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99, 107, 97,103,101, 58,112,114,101, 97,109, 98,108,101, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 76,117, 97, 32, 98,105,110,100,105,110,103, 58, 32, 39, 46, 46,115, 101,108,102, 46,110, 97,109,101, 46, 46, 39, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 71,101,110, 101,114, 97,116,101,100, 32, 97,117,116,111,109, 97,116,105, 99, 97,108,108,121, 32, 98,121, 32, 39, 46, 46, 84, 79, 76, 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 46, 46, 39, 32,111, 110, 32, 39, 46, 46,100, 97,116,101, 40, 41, 46, 46, 39, 46, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 47, 92,110, 92,110, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39, 35,105,110, 99,108,117,100,101, 32, 34,108,117, 97, 47, 116,111,108,117, 97, 46,104, 34, 92,110, 92,110, 39, 41, 10, 10,105,102, 32,110,111,116, 32,102,108, 97,103,115, 46,104, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 47, 42, 32, 69,120,112,111,114,116,101,100, 32,102,117,110, 99, 116,105,111,110, 32, 42, 47, 39, 41, 10,111,117,116,112,117, 116, 40, 39,105,110,116, 32,116,111,108,117, 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 95,111, 112,101,110, 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117, 116,112,117,116, 40, 39,118,111,105,100, 32,116,111,108,117, 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83, 116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32, 100,111, 10,115,101,108,102, 91,105, 93, 58,112,114,101, 97, 109, 98,108,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10, 101,110,100, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 32,102,117, 110, 99,116,105,111,110, 32,116,111, 32,114,101,103,105,115, 116,101,114, 32,116,121,112,101, 32, 42, 47, 39, 41, 10,111, 117,116,112,117,116, 40, 39,115,116, 97,116,105, 99, 32,118, 111,105,100, 32,116,111,108,117, 97, 73, 95,114,101,103, 95, 116,121,112,101,115, 32, 40,108,117, 97, 95, 83,116, 97,116, 101, 42, 32,116,111,108,117, 97, 95, 83, 41, 39, 41, 10,111, 117,116,112,117,116, 40, 39,123, 39, 41, 10,102,111,114,101, 97, 99,104, 40, 95,117,115,101,114,116,121,112,101, 44,102, 117,110, 99,116,105,111,110, 40,110, 44,118, 41, 32,111,117, 116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,117,115, 101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 44,118, 44, 39, 34, 41, 59, 39, 41, 32,101,110,100, 41, 10,111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 92,110, 39, 41, 10, 10,111,117, 116,112,117,116, 40, 39, 47, 42, 32,101,114,114,111,114, 32, 109,101,115,115, 97,103,101,115, 32, 42, 47, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 35,100,101,102,105,110,101, 32, 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 32, 116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108, 117, 97, 95, 83, 44, 92, 34,105,110,118, 97,108,105,100, 32, 92, 39,115,101,108,102, 92, 39, 92, 34, 41, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 35,100,101,102,105,110,101, 32, 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 65, 83, 83, 73, 71, 78, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116, 111,108,117, 97, 95, 83, 44, 92, 34, 35,118,105,110,118, 97, 108,105,100, 32,116,121,112,101, 32,105,110, 32,118, 97,114, 105, 97, 98,108,101, 32, 97,115,115,105,103,110,109,101,110, 116, 46, 92, 34, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99, 107, 97,103,101, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32, 79,112, 101,110, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 34, 41, 10,111,117,116,112,117,116, 40, 34,105,110,116, 32,116, 111,108,117, 97, 95, 34, 46, 46,115,101,108,102, 46,110, 97, 109,101, 46, 46, 34, 95,111,112,101,110, 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10,111,117,116,112,117,116, 40, 34, 32,116,111,108,117, 97, 95,111,112,101,110, 40,116,111,108,117, 97, 95, 83, 41, 59, 34, 41, 10,111,117,116,112,117,116, 40, 34, 32,116,111,108, 117, 97, 73, 95,114,101,103, 95,116,121,112,101,115, 40,116, 111,108,117, 97, 95, 83, 41, 59, 34, 41, 10,108,111, 99, 97, 108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108, 102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,114,101,103,105,115,116,101,114, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,111,117,116,112,117,116, 40, 34, 32,114,101,116,117,114,110, 32, 49, 59, 34, 41, 10, 111,117,116,112,117,116, 40, 34,125, 34, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 58,117,110,114,101, 103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117, 116, 40, 34, 47, 42, 32, 67,108,111,115,101, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 34, 41, 10,111,117,116,112, 117,116, 40, 34,118,111,105,100, 32,116,111,108,117, 97, 95, 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83,116, 97, 116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10, 111,117,116,112,117,116, 40, 34,123, 34, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101, 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,117,110,114,101,103,105,115,116,101,114, 40, 41, 10, 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,111,117,116, 112,117,116, 40, 34,125, 34, 41, 10,101,110,100, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 58,104,101, 97,100,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 92,110, 39, 41, 32,111,117,116,112,117,116, 40, 39, 42, 42, 32, 76,117, 97, 32, 98,105,110,100,105,110,103, 58, 32, 39, 46, 46,115, 101,108,102, 46,110, 97,109,101, 46, 46, 39, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 71,101,110, 101,114, 97,116,101,100, 32, 97,117,116,111,109, 97,116,105, 99, 97,108,108,121, 32, 98,121, 32, 39, 46, 46, 84, 79, 76, 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 46, 46, 39, 32,111, 110, 32, 39, 46, 46,100, 97,116,101, 40, 41, 46, 46, 39, 46, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 47, 92,110, 92,110, 39, 41, 10, 10,105,102, 32,110,111,116, 32, 102,108, 97,103,115, 46,104, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 47, 42, 32, 69,120,112,111,114,116, 101,100, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 39, 41, 10,111,117,116,112,117,116, 40, 39,105,110,116, 32,116, 111,108,117, 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97, 109,101, 46, 46, 39, 95,111,112,101,110, 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,118,111, 105,100, 32,116,111,108,117, 97, 95, 39, 46, 46,115,101,108, 102, 46,110, 97,109,101, 46, 46, 39, 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111, 108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117, 116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 80, 97, 99,107, 97,103,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97, 115,101, 32, 61, 32, 99,108, 97,115,115, 80, 97, 99,107, 97, 103,101, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108, 117, 97, 95,116, 97,103, 41, 10,114,101,116,117,114,110, 32, 116, 10,101,110,100, 10, 10, 10, 10, 10,102,117,110, 99,116, 105,111,110, 32, 80, 97, 99,107, 97,103,101, 32, 40,110, 97, 109,101, 41, 10, 10,108,111, 99, 97,108, 32, 99,111,100,101, 32, 61, 32,114,101, 97,100, 40, 34, 42, 97, 34, 41, 10, 99, 111,100,101, 32, 61, 32, 34, 92,110, 34, 32, 46, 46, 32, 99, 111,100,101, 10, 10,108,111, 99, 97,108, 32,110,115,117, 98, 115,116, 10,114,101,112,101, 97,116, 10, 99,111,100,101, 44, 110,115,117, 98,115,116, 32, 61, 32,103,115,117, 98, 40, 99, 111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 60, 40, 46, 45, 41, 62, 37,115, 42, 92,110, 34, 44,102,117,110, 99,116, 105,111,110, 32, 40,102,110, 41, 10,108,111, 99, 97,108, 32, 102,112, 44,109,115,103, 32, 61, 32,111,112,101,110,102,105, 108,101, 40,102,110, 44, 39,114, 39, 41, 10,105,102, 32,110, 111,116, 32,102,112, 32,116,104,101,110, 10,101,114,114,111, 114, 40, 39, 35, 39, 46, 46,109,115,103, 46, 46, 39, 58, 32, 39, 46, 46,102,110, 41, 10,101,110,100, 10,108,111, 99, 97, 108, 32,115, 32, 61, 32,114,101, 97,100, 40,102,112, 44, 39, 42, 97, 39, 41, 10, 99,108,111,115,101,102,105,108,101, 40, 102,112, 41, 10,114,101,116,117,114,110, 32, 34, 92,110, 34, 32, 46, 46, 32,115, 10,101,110,100, 41, 10,117,110,116,105, 108, 32,110,115,117, 98,115,116, 61, 61, 48, 10, 10, 10,108, 111, 99, 97,108, 32,110,115,117, 98,115,116, 10,114,101,112, 101, 97,116, 10, 99,111,100,101, 44,110,115,117, 98,115,116, 32, 61, 10,103,115,117, 98, 40, 99,111,100,101, 44, 34, 92, 110, 37,115, 42, 37, 36,123, 40, 46, 45, 41,125, 37,115, 42, 92,110, 34, 44, 10,102,117,110, 99,116,105,111,110, 32, 40, 102,110, 41, 10,108,111, 99, 97,108, 32,102,112, 44,109,115, 103, 32, 61, 32,111,112,101,110,102,105,108,101, 40,102,110, 44, 39,114, 39, 41, 10,105,102, 32,110,111,116, 32,102,112, 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39, 46, 46,109,115,103, 46, 46, 39, 58, 32, 39, 46, 46,102,110, 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,115, 32, 61, 32,114,101, 97,100, 40,102,112, 44, 39, 42, 97, 39, 41, 10, 99,108,111,115,101,102,105,108,101, 40,102,112, 41, 10, 10, 108,111, 99, 97,108, 32, 84, 32, 61, 32,123, 99,111,100,101, 61, 34, 92,110, 34,125, 10,115, 61, 32, 34, 92,110, 34, 32, 46, 46, 32,115, 32, 46, 46, 32, 34, 92,110, 34, 10, 10,103, 115,117, 98, 40,115, 44, 34, 92,110, 40, 46, 45, 41, 91, 84, 116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85,117, 93, 91, 65, 97, 93, 95, 91, 69,101, 93, 91, 88,120, 93, 91, 80,112, 93, 91, 79,111, 93, 91, 82,114, 93, 91, 84,116, 93, 91, 94, 92,110, 93, 42, 92,110, 34, 44, 10,102,117,110, 99,116,105, 111,110, 32, 40, 99, 41, 32, 37, 84, 46, 99,111,100,101, 32, 61, 32, 37, 84, 46, 99,111,100,101, 32, 46, 46, 32, 99, 32, 46, 46, 32, 34, 92,110, 34, 32,101,110,100, 10, 41, 10, 10, 103,115,117, 98, 40,115, 44, 34, 92,110, 91, 94, 92,110, 93, 42, 91, 84,116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85, 117, 93, 91, 65, 97, 93, 95, 91, 66, 98, 93, 91, 69,101, 93, 91, 71,103, 93, 91, 73,105, 93, 91, 78,110, 93, 91, 94, 92, 110, 93, 42, 34, 46, 46, 10, 34, 40, 46, 45, 41, 34, 32, 46, 46, 10, 34, 92,110, 91, 94, 92,110, 93, 42, 91, 84,116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85,117, 93, 91, 65, 97, 93, 95, 91, 69,101, 93, 91, 78,110, 93, 91, 68,100, 93, 91, 94, 92,110, 93, 42, 92,110, 34, 44, 10,102,117,110, 99,116, 105,111,110, 32, 40, 99, 41, 32, 37, 84, 46, 99,111,100,101, 32, 61, 32, 37, 84, 46, 99,111,100,101, 32, 46, 46, 32, 99, 32, 46, 46, 32, 34, 92,110, 34, 32,101,110,100, 10, 41, 10, 114,101,116,117,114,110, 32, 84, 46, 99,111,100,101, 10,101, 110,100, 41, 10,117,110,116,105,108, 32,110,115,117, 98,115, 116, 61, 61, 48, 10, 10,108,111, 99, 97,108, 32,116, 32, 61, 32, 95, 80, 97, 99,107, 97,103,101, 40, 95, 67,111,110,116, 97,105,110,101,114,123,110, 97,109,101, 61,110, 97,109,101, 44, 32, 99,111,100,101, 61, 99,111,100,101,125, 41, 10,112, 117,115,104, 40,116, 41, 10,116, 58,112,114,101,112,114,111, 99,101,115,115, 40, 41, 10,116, 58,112, 97,114,115,101, 40, 116, 46, 99,111,100,101, 41, 10,112,111,112, 40, 41, 10,114, 101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99, 108, 97,115,115, 77,111,100,117,108,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 67,111, 110,116, 97,105,110,101,114, 44, 10,116,121,112,101, 32, 61, 32, 39,109,111,100,117,108,101, 39, 10,125, 10,115,101,116, 116, 97,103, 40, 99,108, 97,115,115, 77,111,100,117,108,101, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 77,111, 100,117,108,101, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,109,111,100,117,108,101, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,108,111, 99, 97,108, 32, 105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91, 105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,114, 101,103,105,115,116,101,114, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 77,111,100, 117,108,101, 58,117,110,114,101,103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46, 115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105, 111,110, 32, 99,108, 97,115,115, 77,111,100,117,108,101, 58, 112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108, 111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34, 77,111,100,117,108,101,123, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 110, 97,109,101, 46, 46, 34, 39, 59, 34, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101, 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,114,105,110,116, 40,105,100,101, 110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 77,111,100,117,108,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 77,111, 100,117,108,101, 10,115,101,116,116, 97,103, 40,116, 44,116, 111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110, 100, 40,116, 41, 10,114,101,116,117,114,110, 32,116, 10,101, 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 77,111,100,117,108,101, 32, 40,110, 44, 98, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32, 95, 77,111,100,117,108,101, 40, 95, 67,111,110,116, 97,105,110,101,114,123,110, 97,109, 101, 61,110,125, 41, 10,112,117,115,104, 40,116, 41, 10,116, 58,112, 97,114,115,101, 40,115,116,114,115,117, 98, 40, 98, 44, 50, 44,115,116,114,108,101,110, 40, 98, 41, 45, 49, 41, 41, 10,112,111,112, 40, 41, 10,114,101,116,117,114,110, 32, 116, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 68, 101,102,105,110,101, 32, 61, 32,123, 10,110, 97,109,101, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99, 108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10,125, 10, 115,101,116,116, 97,103, 40, 99,108, 97,115,115, 68,101,102, 105,110,101, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115, 115, 68,101,102,105,110,101, 58,114,101,103,105,115,116,101, 114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 32, 61, 32, 115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41, 10,105,102, 32,112, 32,116,104,101,110, 10,111,117,116,112, 117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115, 116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108, 102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46, 115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 78, 85, 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97, 115,115, 68,101,102,105,110,101, 58,117,110,114,101,103,105, 115,116,101,114, 32, 40, 41, 10,105,102, 32,110,111,116, 32, 115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32, 108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,108, 117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,103, 108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115, 115, 68,101,102,105,110,101, 58,112,114,105,110,116, 32, 40, 105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114, 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 68,101,102, 105,110,101,123, 34, 41, 10,112,114,105,110,116, 40,105,100, 101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34, 32,108,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10, 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 68,101,102,105,110,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 68,101, 102,105,110,101, 10,115,101,116,116, 97,103, 40,116, 44,116, 111,108,117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,116, 46,110, 97,109,101, 32, 61, 61, 32, 39, 39, 32,116,104,101, 110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108, 105,100, 32,100,101,102,105,110,101, 34, 41, 10,101,110,100, 10, 10, 97,112,112,101,110,100, 40,116, 41, 10,114,101,116, 117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 68,101,102,105,110,101, 32, 40, 110, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112, 108,105,116, 40,110, 44, 39, 64, 39, 41, 10,114,101,116,117, 114,110, 32, 95, 68,101,102,105,110,101, 32,123, 10,110, 97, 109,101, 32, 61, 32,116, 91, 49, 93, 44, 10,108,110, 97,109, 101, 32, 61, 32,116, 91, 50, 93, 32,111,114, 32,116, 91, 49, 93, 10,125, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 69, 110,117,109,101,114, 97,116,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116, 117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99, 108, 97,115,115, 69,110,117,109,101,114, 97,116,101, 44,116, 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 69,110,117,109, 101,114, 97,116,101, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,115,101, 108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105, 108,101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,105, 102, 32,112, 32,116,104,101,110, 10,105,102, 32,115,101,108, 102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,116,104,101, 110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101,115, 91, 105, 93, 46, 46, 39, 34, 44, 39, 46, 46,112, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97, 110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46, 112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46, 108,110, 97,109,101,115, 91,105, 93, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,108,115,101, 10,111,117,116,112, 117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115, 116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 78, 85, 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97, 109,101,115, 91,105, 93, 46, 46, 39, 34, 44, 39, 46, 46,115, 101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39, 41, 10,101, 110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10, 101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99, 108, 97,115,115, 69,110,117,109,101,114, 97,116,101, 58,117, 110,114,101,103,105,115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105,108, 32, 97,110,100, 32,115,101,108,102, 58, 105,110,109,111,100,117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32, 100,111, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46, 115,101,108,102, 46,108,110, 97,109,101,115, 91,105, 93, 46, 46, 39, 34, 41, 59, 39, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 69, 110,117,109,101,114, 97,116,101, 58,112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 69,110, 117,109,101,114, 97,116,101,123, 34, 41, 10,108,111, 99, 97, 108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108, 102, 91,105, 93, 32,100,111, 10,112,114,105,110,116, 40,105, 100,101,110,116, 46, 46, 34, 32, 39, 34, 46, 46,115,101,108, 102, 91,105, 93, 46, 46, 34, 39, 40, 34, 46, 46,115,101,108, 102, 46,108,110, 97,109,101,115, 91,105, 93, 46, 46, 34, 41, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 69,110,117, 109,101,114, 97,116,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 69,110,117,109, 101,114, 97,116,101, 10,115,101,116,116, 97,103, 40,116, 44, 116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101, 110,100, 40,116, 41, 10,114,101,116,117,114,110, 32,116, 10, 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 69,110,117,109,101,114, 97,116,101, 32, 40, 98, 41, 10, 108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,115,116,114,115,117, 98, 40, 98, 44, 50, 44, 45, 50, 41, 44, 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 32, 61, 32, 49, 10,108,111, 99, 97,108, 32,101, 32, 61, 32,123,110, 61, 48,125, 10,119,104,105,108,101, 32,116, 91,105, 93, 32, 100,111, 10,108,111, 99, 97,108, 32,116,116, 32, 61, 32,115, 112,108,105,116, 40,116, 91,105, 93, 44, 39, 61, 39, 41, 10, 101, 46,110, 32, 61, 32,101, 46,110, 32, 43, 32, 49, 10,101, 91,101, 46,110, 93, 32, 61, 32,116,116, 91, 49, 93, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10, 10,105, 32, 61, 32, 49, 10,101, 46,108,110, 97,109,101,115, 32, 61, 32,123, 125, 10,119,104,105,108,101, 32,101, 91,105, 93, 32,100,111, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105, 116, 40,101, 91,105, 93, 44, 39, 64, 39, 41, 10,101, 91,105, 93, 32, 61, 32,116, 91, 49, 93, 10,101, 46,108,110, 97,109, 101,115, 91,105, 93, 32, 61, 32,116, 91, 50, 93, 32,111,114, 32,116, 91, 49, 93, 10,105, 32, 61, 32,105, 43, 49, 10,101, 110,100, 10,114,101,116,117,114,110, 32, 95, 69,110,117,109, 101,114, 97,116,101, 40,101, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 68,101, 99, 108, 97,114, 97,116,105,111,110, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116, 117,114,101, 44, 10,109,111,100, 32, 61, 32, 39, 39, 44, 10, 116,121,112,101, 32, 61, 32, 39, 39, 44, 10,112,116,114, 32, 61, 32, 39, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39, 44, 10,100,105,109, 32, 61, 32, 39, 39, 44, 10,114,101,116, 32, 61, 32, 39, 39, 44, 10,100,101,102, 32, 61, 32, 39, 39, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 44,116,111,108, 117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116, 105,111,110, 32, 99,114,101, 97,116,101, 95,118, 97,114,110, 97,109,101, 32, 40, 41, 10,105,102, 32,110,111,116, 32, 95, 118, 97,114,110,117,109, 98,101,114, 32,116,104,101,110, 32, 95,118, 97,114,110,117,109, 98,101,114, 32, 61, 32, 48, 32, 101,110,100, 10, 95,118, 97,114,110,117,109, 98,101,114, 32, 61, 32, 95,118, 97,114,110,117,109, 98,101,114, 32, 43, 32, 49, 10,114,101,116,117,114,110, 32, 34,116,111,108,117, 97, 95,118, 97,114, 95, 34, 46, 46, 95,118, 97,114,110,117,109, 98,101,114, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97, 114, 97,116,105,111,110, 58, 99,104,101, 99,107,110, 97,109, 101, 32, 40, 41, 10, 10,105,102, 32,115,116,114,115,117, 98, 40,115,101,108,102, 46,110, 97,109,101, 44, 49, 44, 49, 41, 32, 61, 61, 32, 39, 91, 39, 32, 97,110,100, 32,110,111,116, 32,105,115,116,121,112,101, 40,115,101,108,102, 46,116,121, 112,101, 41, 32,116,104,101,110, 10,115,101,108,102, 46,110, 97,109,101, 32, 61, 32,115,101,108,102, 46,116,121,112,101, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115,112,108,105,116, 40,115,101, 108,102, 46,109,111,100, 44, 39, 37,115, 37,115, 42, 39, 41, 10,115,101,108,102, 46,116,121,112,101, 32, 61, 32,109, 91, 109, 46,110, 93, 10,115,101,108,102, 46,109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49, 41, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32, 116, 32, 61, 32,115,112,108,105,116, 40,115,101,108,102, 46, 110, 97,109,101, 44, 39, 61, 39, 41, 10,105,102, 32,116, 46, 110, 61, 61, 50, 32,116,104,101,110, 10,115,101,108,102, 46, 110, 97,109,101, 32, 61, 32,116, 91, 49, 93, 10,115,101,108, 102, 46,100,101,102, 32, 61, 32,116, 91,116, 46,110, 93, 10, 101,110,100, 10, 10,108,111, 99, 97,108, 32, 98, 44,101, 44, 100, 32, 61, 32,115,116,114,102,105,110,100, 40,115,101,108, 102, 46,110, 97,109,101, 44, 34, 37, 91, 40, 46, 45, 41, 37, 93, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10,115, 101,108,102, 46,110, 97,109,101, 32, 61, 32,115,116,114,115, 117, 98, 40,115,101,108,102, 46,110, 97,109,101, 44, 49, 44, 98, 45, 49, 41, 10,115,101,108,102, 46,100,105,109, 32, 61, 32,100, 10,101,110,100, 10, 10, 10,105,102, 32,115,101,108, 102, 46,116,121,112,101, 32,126, 61, 32, 39, 39, 32, 97,110, 100, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32, 97,110,100, 32,115,101,108,102, 46,110, 97,109,101, 32, 61, 61, 32, 39, 39, 32,116,104,101, 110, 10,115,101,108,102, 46,110, 97,109,101, 32, 61, 32, 99, 114,101, 97,116,101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,108,115,101,105,102, 32,115,101,108,102, 46,107,105, 110,100, 61, 61, 39,118, 97,114, 39, 32,116,104,101,110, 10, 105,102, 32,115,101,108,102, 46,116,121,112,101, 61, 61, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,110, 97,109,101, 126, 61, 39, 39, 32,116,104,101,110, 10,115,101,108,102, 46, 116,121,112,101, 32, 61, 32,115,101,108,102, 46,116,121,112, 101, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,115,101, 108,102, 46,110, 97,109,101, 32, 61, 32, 99,114,101, 97,116, 101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,108,115, 101,105,102, 32,105,115,116,121,112,101, 40,115,101,108,102, 46,110, 97,109,101, 41, 32,116,104,101,110, 10,105,102, 32, 115,101,108,102, 46,116,121,112,101, 61, 61, 39, 39, 32,116, 104,101,110, 32,115,101,108,102, 46,116,121,112,101, 32, 61, 32,115,101,108,102, 46,110, 97,109,101, 10,101,108,115,101, 32,115,101,108,102, 46,116,121,112,101, 32, 61, 32,115,101, 108,102, 46,116,121,112,101, 46, 46, 39, 32, 39, 46, 46,115, 101,108,102, 46,110, 97,109,101, 32,101,110,100, 10,115,101, 108,102, 46,110, 97,109,101, 32, 61, 32, 99,114,101, 97,116, 101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,110,100, 10,101,110,100, 10, 10,101,110,100, 10, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99, 108, 97,114, 97,116,105,111,110, 58, 99,104,101, 99,107,116, 121,112,101, 32, 40, 41, 10, 10, 10,105,102, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 32, 97,110,100, 32,115,101,108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101,110, 10,115,101,108,102, 46,114,101, 116, 32, 61, 32,115,101,108,102, 46,112,116,114, 10,115,101, 108,102, 46,112,116,114, 32, 61, 32,110,105,108, 10,101,110, 100, 10, 10, 10,105,102, 32,115,101,108,102, 46,100,105,109, 126, 61, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,114, 101,116,126, 61, 39, 39, 32,116,104,101,110, 10,101,114,114, 111,114, 40, 39, 35,105,110,118, 97,108,105,100, 32,112, 97, 114, 97,109,101,116,101,114, 58, 32, 99, 97,110,110,111,116, 32,114,101,116,117,114,110, 32, 97,110, 32, 97,114,114, 97, 121, 32,111,102, 32,118, 97,108,117,101,115, 39, 41, 10,101, 110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46,116,121, 112,101,126, 61, 39, 39, 32,116,104,101,110, 10,114,101,103, 116,121,112,101, 40,115,101,108,102, 46,116,121,112,101, 41, 10,101,110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46, 116,121,112,101, 32, 61, 61, 32, 39, 95,117,115,101,114,100, 97,116, 97, 39, 32,116,104,101,110, 32,115,101,108,102, 46, 116,121,112,101, 32, 61, 32, 39,118,111,105,100, 42, 39, 10, 101,108,115,101,105,102, 32,115,101,108,102, 46,116,121,112, 101, 32, 61, 61, 32, 39, 95, 99,115,116,114,105,110,103, 39, 32,116,104,101,110, 32,115,101,108,102, 46,116,121,112,101, 32, 61, 32, 39, 99,104, 97,114, 42, 39, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10,101,110,100, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10, 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 68, 101, 99,108, 97,114, 97,116,105,111,110,123, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109, 111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109, 111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105, 100,101,110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46, 115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,100,105,109, 32, 61, 32, 39, 34, 46, 46,115,101,108, 102, 46,100,105,109, 46, 46, 34, 39, 44, 34, 41, 10,112,114, 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,100,101, 102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,101, 102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40, 105,100,101,110,116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10, 101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111, 110, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115,101, 108,102, 46,105,116,121,112,101, 44, 32,115,101,108,102, 46, 116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101, 108,102, 46,116,121,112,101, 44,115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110,115, 116, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,111,117,116, 99,104,101, 99, 107,116,121,112,101, 32, 40,110, 97,114,103, 41, 10,108,111, 99, 97,108, 32,116, 97,103, 44, 32,100,101,102, 10,105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,116, 97,103, 32, 61, 32, 39, 76, 85, 65, 95, 84, 84, 65, 66, 76, 69, 39, 10,100,101,102, 32, 61, 32, 48, 10,101,108,115,101, 10,116, 97,103, 32, 61, 32,115, 101,108,102, 46,116, 97,103, 10,100,101,102, 32, 61, 32,115, 101,108,102, 46,100,101,102,126, 61, 39, 39, 32,111,114, 32, 48, 10,101,110,100, 10,114,101,116,117,114,110, 32, 39,116, 111,108,117, 97, 95,105,115,116,121,112,101, 40,116,111,108, 117, 97, 95, 83, 44, 39, 46, 46,110, 97,114,103, 46, 46, 39, 44, 39, 46, 46,116, 97,103, 46, 46, 39, 44, 39, 46, 46,100, 101,102, 46, 46, 39, 41, 39, 10,101,110,100, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,100,101, 99,108, 97, 114,101, 32, 40,110, 97,114,103, 41, 10,108,111, 99, 97,108, 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101, 108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101,110, 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10, 111,117,116,112,117,116, 40, 34, 32, 34, 44,115,101,108,102, 46,109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44, 112,116,114, 41, 10,105,102, 32,115,101,108,102, 46,100,105, 109, 32,126, 61, 32, 39, 39, 32, 97,110,100, 32,116,111,110, 117,109, 98,101,114, 40,115,101,108,102, 46,100,105,109, 41, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112, 117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116, 112,117,116, 40,115,101,108,102, 46,110, 97,109,101, 41, 10, 105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,105,102, 32,116,111,110,117, 109, 98,101,114, 40,115,101,108,102, 46,100,105,109, 41,126, 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,117, 116, 40, 39, 91, 39, 44,115,101,108,102, 46,100,105,109, 44, 39, 93, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112, 117,116, 40, 39, 32, 61, 32, 40, 39, 44,115,101,108,102, 46, 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,112, 116,114, 44, 39, 42, 41, 39, 44, 10, 39,109, 97,108,108,111, 99, 40, 39, 44,115,101,108,102, 46,100,105,109, 44, 39, 42, 115,105,122,101,111,102, 40, 39, 44,115,101,108,102, 46,116, 121,112,101, 44,112,116,114, 44, 39, 41, 41, 59, 39, 41, 10, 101,110,100, 10,101,108,115,101, 10,108,111, 99, 97,108, 32, 116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108, 102, 46,116,121,112,101, 41, 10,111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41, 10,105,102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112,116,114, 61, 61, 39, 39, 32,116,104, 101,110, 32,111,117,116,112,117,116, 40, 39, 42, 39, 41, 32, 101,110,100, 10,111,117,116,112,117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,109,111,100, 44,115,101,108,102, 46, 116,121,112,101, 41, 10,105,102, 32,110,111,116, 32,116, 32, 116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32,115,101,108,102, 46,100,101,102, 32, 126, 61, 32, 39, 39, 32,116,104,101,110, 32,100,101,102, 32, 61, 32,115,101,108,102, 46,100,101,102, 32,101,110,100, 10, 105,102, 32,116, 32,116,104,101,110, 10,111,117,116,112,117, 116, 40, 39,116,111,108,117, 97, 95,103,101,116, 39, 46, 46, 116, 44, 39, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 39, 44, 110, 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110, 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58, 103,101,116, 97,114,114, 97,121, 32, 40,110, 97,114,103, 41, 10,105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,123, 39, 41, 10,108,111, 99, 97,108, 32,100,101, 102, 32, 61, 32,115,101,108,102, 46,100,101,102,126, 61, 39, 39, 32,111,114, 32, 48, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95, 97,114,114, 97,121,105,115,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44, 39, 44,115,101, 108,102, 46,116, 97,103, 44, 39, 44, 39, 44,115,101,108,102, 46,100,105,109, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,103,111, 116,111, 32,116,111,108,117, 97, 95,108,101,114,114,111,114, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,101,108, 115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,105, 110,116, 32,105, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,102,111,114, 40,105, 61, 48, 59, 32,105, 60, 39, 46, 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 59,105, 43, 43, 41, 39, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32, 105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121, 112,101, 41, 10,108,111, 99, 97,108, 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101,108,102, 46,112,116,114, 126, 61, 39, 39, 32,116,104,101,110, 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,105, 93, 32, 61, 32, 39, 41, 10,105,102, 32,110, 111,116, 32,116, 32, 97,110,100, 32,112,116,114, 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116,112,117,116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117,116,112,117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,109,111,100, 44,115, 101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,110,111, 116, 32,116, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116,112,117, 116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,108, 32,100, 101,102, 32, 61, 32, 48, 10,105,102, 32,115,101,108,102, 46, 100,101,102, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 32, 100,101,102, 32, 61, 32,115,101,108,102, 46,100,101,102, 32, 101,110,100, 10,105,102, 32,116, 32,116,104,101,110, 10,111, 117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101, 116,102,105,101,108,100, 39, 46, 46,116, 46, 46, 39, 40,116, 111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100, 117,115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110, 100, 10,111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10, 111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,101,110, 100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111, 110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116, 105,111,110, 58,115,101,116, 97,114,114, 97,121, 32, 40,110, 97,114,103, 41, 10,105,102, 32,115,101,108,102, 46,100,105, 109, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112, 117,116, 40, 39, 32,105,110,116, 32,105, 59, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 32,102,111,114, 40,105, 61, 48, 59, 32,105, 60, 39, 46, 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 59,105, 43, 43, 41, 39, 41, 10,108,111, 99, 97, 108, 32,116, 44, 99,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101, 108,100, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 44,115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93, 41, 59, 39, 41, 10, 101,108,115,101, 10,105,102, 32,115,101,108,102, 46,112,116, 114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112, 117,116, 40, 39, 35,105,102,100,101,102, 32, 95, 95, 99,112, 108,117,115,112,108,117,115, 92,110, 39, 41, 10,111,117,116, 112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108, 117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119, 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 40, 39, 44,115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,101, 108,115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99, 111,112,121, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111, 105,100, 42, 41, 38, 39, 44,115,101,108,102, 46,110, 97,109, 101, 44, 39, 91,105, 93, 44,115,105,122,101,111,102, 40, 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 41, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,101,110,100, 105,102, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,108, 100,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40, 116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 73, 95, 99,108,111,110,101, 44, 39, 44,115,101,108,102, 46,116, 97, 103, 44, 39, 41, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10, 10, 10,101,108,115,101, 10,111,117,116, 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115, 104,102,105,101,108,100,117,115,101,114,116,121,112,101, 40, 116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44, 40,118,111,105,100, 42, 41, 39, 44, 115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,111,117,116,112,117, 116, 40, 39, 32,125, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97, 115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,102, 114,101,101, 97,114,114, 97,121, 32, 40, 41, 10,105,102, 32, 115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39, 32, 97,110,100, 32,116,111,110,117,109, 98,101,114, 40,115,101, 108,102, 46,100,105,109, 41, 61, 61,110,105,108, 32,116,104, 101,110, 10,111,117,116,112,117,116, 40, 39, 32,102,114,101, 101, 40, 39, 44,115,101,108,102, 46,110, 97,109,101, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68, 101, 99,108, 97,114, 97,116,105,111,110, 58,112, 97,115,115, 112, 97,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 112,116,114, 61, 61, 39, 38, 39, 32,116,104,101,110, 10,111, 117,116,112,117,116, 40, 39, 42, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 41, 10,101,108,115,101,105,102, 32,115, 101,108,102, 46,114,101,116, 61, 61, 39, 42, 39, 32,116,104, 101,110, 10,111,117,116,112,117,116, 40, 39, 38, 39, 46, 46, 115,101,108,102, 46,110, 97,109,101, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40,115,101,108,102, 46,110, 97, 109,101, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,114,101,116,118, 97, 108,117,101, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 114,101,116, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10, 108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111,117,116, 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115, 104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46,115,101, 108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10, 101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116, 111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121, 112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105, 100, 42, 41, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,114,101,116,117,114, 110, 32, 49, 10,101,110,100, 10,114,101,116,117,114,110, 32, 48, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111, 110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 32, 40,116, 41, 10,105,102, 32,116, 46,110, 97,109,101, 32, 97, 110,100, 32,116, 46,110, 97,109,101,126, 61, 39, 39, 32,116, 104,101,110, 10,108,111, 99, 97,108, 32,110, 32, 61, 32,115, 112,108,105,116, 40,116, 46,110, 97,109,101, 44, 39, 64, 39, 41, 10,116, 46,110, 97,109,101, 32, 61, 32,110, 91, 49, 93, 10,116, 46,108,110, 97,109,101, 32, 61, 32,103,115,117, 98, 40,110, 91, 50, 93, 32,111,114, 32,110, 91, 49, 93, 44, 34, 37, 91, 46, 45, 37, 93, 34, 44, 34, 34, 41, 10,101,110,100, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115, 115, 68,101, 99,108, 97,114, 97,116,105,111,110, 10,115,101, 116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,116, 97, 103, 41, 10,116, 58, 99,104,101, 99,107,110, 97,109,101, 40, 41, 10,116, 58, 99,104,101, 99,107,116,121,112,101, 40, 41, 10,114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 68,101, 99, 108, 97,114, 97,116,105,111,110, 32, 40,115, 44,107,105,110, 100, 41, 10, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 37,115, 42, 61, 37,115, 42, 34, 44, 34, 61, 34, 41, 10, 10,105,102, 32,107,105,110,100, 32, 61, 61, 32, 34,118, 97, 114, 34, 32,116,104,101,110, 10, 10,105,102, 32,115, 32, 61, 61, 32, 39, 39, 32,111,114, 32,115, 32, 61, 61, 32, 39,118, 111,105,100, 39, 32,116,104,101,110, 10,114,101,116,117,114, 110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 116,121,112,101, 32, 61, 32, 39,118,111,105,100, 39, 44, 32, 107,105,110,100, 32, 61, 32,107,105,110,100,125, 10,101,110, 100, 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 42, 37, 115, 42, 38, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61, 32, 50, 32,116,104,101,110, 10,105,102, 32,107,105,110,100, 32, 61, 61, 32, 39,102,117,110, 99, 39, 32,116,104,101,110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,105, 100, 32,102,117,110, 99,116,105,111,110, 32,114,101,116,117, 114,110, 32,116,121,112,101, 58, 32, 34, 46, 46,115, 41, 10, 101,110,100, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115, 112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115, 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,101, 99, 108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61, 32, 39, 42, 39, 44, 10,114,101,116, 32, 61, 32, 39, 38, 39, 44, 10, 116,121,112,101, 32, 61, 32,109, 91,109, 46,110, 93, 44, 10, 109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49, 41, 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10, 10, 116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 42, 37,115, 42, 37, 42, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61, 32, 50, 32,116,104,101,110, 10,105,102, 32,107,105, 110,100, 32, 61, 61, 32, 39,102,117,110, 99, 39, 32,116,104, 101,110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97, 108,105,100, 32,102,117,110, 99,116,105,111,110, 32,114,101, 116,117,114,110, 32,116,121,112,101, 58, 32, 34, 46, 46,115, 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115, 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68, 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109, 101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61, 32, 39, 42, 39, 44, 10,114,101,116, 32, 61, 32, 39, 42, 39, 44, 10,116,121,112,101, 32, 61, 32,109, 91,109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40, 109, 44, 49, 44,109, 46,110, 45, 49, 41, 44, 10,107,105,110, 100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10, 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 38, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61, 32, 50, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115, 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68, 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109, 101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61, 32, 39, 38, 39, 44, 10,116,121,112,101, 32, 61, 32,109, 91, 109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49, 41, 32, 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,115, 49, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 40, 37, 98, 92, 91, 92, 93, 41, 34, 44,102,117,110, 99,116,105,111,110, 32, 40,110, 41, 32,114,101,116,117,114,110, 32,103,115,117, 98, 40,110, 44, 39, 37, 42, 39, 44, 39, 92, 49, 39, 41, 32,101, 110,100, 41, 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 49, 44, 39, 37, 42, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61, 32, 50, 32,116,104,101,110, 10,116, 91, 50, 93, 32, 61, 32,103,115,117, 98, 40,116, 91, 50, 93, 44, 39, 92, 49, 39, 44, 39, 37, 42, 39, 41, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115, 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61, 32, 39, 42, 39, 44, 10,116,121,112,101, 32, 61, 32, 109, 91,109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99, 111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49, 41, 32, 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10,105,102, 32,107,105,110,100, 32, 61, 61, 32, 39,118, 97,114, 39, 32,116,104,101,110, 10, 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 115, 37,115, 42, 39, 41, 10,108,111, 99, 97,108, 32,118, 10, 105,102, 32,105,115,116,121,112,101, 40,116, 91,116, 46,110, 93, 41, 32,116,104,101,110, 32,118, 32, 61, 32, 39, 39, 32, 101,108,115,101, 32,118, 32, 61, 32,116, 91,116, 46,110, 93, 59, 32,116, 46,110, 32, 61, 32,116, 46,110, 45, 49, 32,101, 110,100, 10,114,101,116,117,114,110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32, 61, 32,118, 44, 10,116,121,112,101, 32, 61, 32,116, 91,116, 46, 110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97, 116, 40,116, 44, 49, 44,116, 46,110, 45, 49, 41, 44, 10,107, 105,110,100, 32, 61, 32,107,105,110,100, 10,125, 10, 10,101, 108,115,101, 10, 10, 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37,115, 37,115, 42, 39, 41, 10,108,111, 99, 97,108, 32,118, 32, 61, 32,116, 91,116, 46,110, 93, 10,108, 111, 99, 97,108, 32,116,112, 44,109,100, 10,105,102, 32,116, 46,110, 62, 49, 32,116,104,101,110, 10,116,112, 32, 61, 32, 116, 91,116, 46,110, 45, 49, 93, 10,109,100, 32, 61, 32, 99, 111,110, 99, 97,116, 40,116, 44, 49, 44,116, 46,110, 45, 50, 41, 10,101,110,100, 10,114,101,116,117,114,110, 32, 95, 68, 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109, 101, 32, 61, 32,118, 44, 10,116,121,112,101, 32, 61, 32,116, 112, 44, 10,109,111,100, 32, 61, 32,109,100, 44, 10,107,105, 110,100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116, 105,111,110, 44, 10,125, 10, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 44,116, 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 58,112,114,105,110,116, 32, 40,105,100,101, 110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 86, 97,114,105, 97, 98, 108,101,123, 34, 41, 10,112,114,105,110,116, 40,105,100,101, 110,116, 46, 46, 34, 32,109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101, 108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10, 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 112,116,114, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110, 116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,110, 97,109, 101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40, 105,100,101,110,116, 46, 46, 34, 32,100,101,102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,101,102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34, 46, 46, 115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115, 115, 86, 97,114,105, 97, 98,108,101, 58,103,101,116,118, 97, 108,117,101, 32, 40, 99,108, 97,115,115, 44,115,116, 97,116, 105, 99, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110, 100, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,114, 101,116,117,114,110, 32, 99,108, 97,115,115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101, 108,115,101,105,102, 32, 99,108, 97,115,115, 32,116,104,101, 110, 10,114,101,116,117,114,110, 32, 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101, 108,115,101, 10,114,101,116,117,114,110, 32,115,101,108,102, 46,110, 97,109,101, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 58,115,117,112, 99,111,100, 101, 32, 40, 41, 10,108,111, 99, 97,108, 32, 99,108, 97,115, 115, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115, 115, 40, 41, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 116,104,101,110, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,101, 10,111,117,116, 112,117,116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97, 109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100, 10,115, 101,108,102, 46, 99,103,101,116,110, 97,109,101, 32, 61, 32, 115,101,108,102, 58, 99,102,117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,103,101,116, 34, 41, 10,111, 117,116,112,117,116, 40, 34,115,116, 97,116,105, 99, 32,105, 110,116, 34, 44,115,101,108,102, 46, 99,103,101,116,110, 97, 109,101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116, 112,117,116, 40, 34,123, 34, 41, 10, 10, 10,108,111, 99, 97, 108, 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32, 115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111, 100, 44, 39, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 39, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104, 101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44, 99, 108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32, 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,115,115, 44, 39, 42, 41, 32, 39, 41, 10,111, 117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101, 116,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 48, 41, 59, 39, 41, 10,101,108,115,101, 105,102, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115,101,108,102, 46,109,111,100, 32, 61, 32, 115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111, 100, 44, 39, 94, 37,115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,101,110,100, 10, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32, 115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104,101, 110, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32, 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 59, 39, 41, 59, 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32, 105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121, 112,101, 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111, 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112, 117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46, 115,101,108,102, 58,103,101,116,118, 97,108,117,101, 40, 99, 108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,105,102, 32,115,101, 108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,111, 114, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118, 111,105,100, 42, 41, 38, 39, 46, 46,115,101,108,102, 58,103, 101,116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115, 116, 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108, 102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,108,115, 101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40, 116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41, 39, 46, 46,115,101,108,102, 58,103,101,116,118, 97,108,117, 101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,111, 117,116,112,117,116, 40, 39, 32,114,101,116,117,114,110, 32, 49, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10, 10, 10,105,102, 32,110,111,116, 32,115,116,114,102,105,110, 100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110, 115,116, 39, 41, 32,116,104,101,110, 10,105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,105, 111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,108, 102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101, 110,100, 10,115,101,108,102, 46, 99,115,101,116,110, 97,109, 101, 32, 61, 32,115,101,108,102, 58, 99,102,117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,115,101,116, 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97,116, 105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99,115, 101,116,110, 97,109,101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10, 10, 108,111, 99, 97,108, 32,110, 97,114,103, 61, 49, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116, 105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32, 61, 32, 39, 41, 10, 111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,115, 115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 48, 41, 59, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32, 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 59, 39, 41, 59, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10, 101,108,115,101,105,102, 32,115,116, 97,116,105, 99, 32,116, 104,101,110, 10, 95, 44, 95, 44,115,101,108,102, 46,109,111, 100, 32, 61, 32,115,116,114,102,105,110,100, 40,115,101,108, 102, 46,109,111,100, 44, 39, 94, 37,115, 42,115,116, 97,116, 105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,101,110, 100, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 33, 39, 46, 46,115,101,108,102, 58,111,117,116, 99, 104,101, 99,107,116,121,112,101, 40,110, 97,114,103, 41, 46, 46, 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32, 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 65, 83, 83, 73, 71, 78, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,112,116, 114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101,108,102, 46, 112,116,114,126, 61, 39, 39, 32,116,104,101,110, 32,112,116, 114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,111,117,116, 112,117,116, 40, 39, 32, 39, 41, 10,105,102, 32, 99,108, 97, 115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116, 104,101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115, 115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 41, 10,101,108,115,101,105,102, 32, 99,108, 97, 115,115, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,108,102, 46, 110, 97,109,101, 41, 10,101,108,115,101, 10,111,117,116,112, 117,116, 40,115,101,108,102, 46,110, 97,109,101, 41, 10,101, 110,100, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41, 10,105,102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112, 116,114, 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116, 112,117,116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117, 116,112,117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46, 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 41, 10, 105,102, 32,110,111,116, 32,116, 32,116,104,101,110, 10,111, 117,116,112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10, 111,117,116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32, 115,101,108,102, 46,100,101,102, 32,126, 61, 32, 39, 39, 32, 116,104,101,110, 32,100,101,102, 32, 61, 32,115,101,108,102, 46,100,101,102, 32,101,110,100, 10,105,102, 32,116, 32,116, 104,101,110, 10,111,117,116,112,117,116, 40, 39,116,111,108, 117, 97, 95,103,101,116, 39, 46, 46,116, 44, 39, 40,116,111, 108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,108, 115,101, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,116,121,112,101, 40,116, 111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101, 110,100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,116, 117,114,110, 32, 48, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92, 110, 39, 41, 10,101,110,100, 10, 10,101,110,100, 10, 10,102, 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97, 114,105, 97, 98,108,101, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 97,114,101,110, 116, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115, 115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105,110,109, 111,100,117,108,101, 40, 41, 10,105,102, 32,112, 97,114,101, 110,116, 32,116,104,101,110, 10,105,102, 32,115,101,108,102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110, 10, 111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95, 116, 97, 98,108,101,118, 97,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109, 101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99, 103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115, 101,108,102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117, 116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,101, 118, 97,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97, 109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10, 101,110,100, 10,101,108,115,101, 10,105,102, 32,115,101,108, 102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108,118, 97,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,101,108,102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116, 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108,118, 97,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101, 116,110, 97,109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 58,117,110,114,101,103,105, 115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105,108, 32, 97,110,100, 32,115,101,108,102, 58,105,110,109,111,100, 117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,103, 101,116,103,108,111, 98, 97,108,115, 40,116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,115,116,114,105,110,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 44,115,101,108, 102, 46,108,110, 97,109,101, 44, 39, 34, 41, 59, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,114, 97,119,115,101,116, 40,116,111,108,117, 97, 95, 83, 44, 45, 51, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112, 111,112, 40,116,111,108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 95, 86, 97,114,105, 97, 98,108, 101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 10, 115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95, 116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10, 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 86, 97,114,105, 97, 98,108,101, 32, 40,115, 41, 10,114,101,116,117,114,110, 32, 95, 86, 97,114,105, 97, 98,108,101, 32, 40, 68,101, 99,108, 97,114, 97,116,105,111,110, 40,115, 44, 39,118, 97,114, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 65, 114,114, 97,121, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116, 105,111,110, 44, 10,125, 10, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 65,114,114, 97,121, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105, 111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,112, 114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111, 115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 65,114,114, 97,121,123, 34, 41, 10,112,114,105, 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105, 100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101, 110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101, 108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10, 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 100,101,102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 100,101,102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110, 116, 40,105,100,101,110,116, 46, 46, 34, 32,100,105,109, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,105,109, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100, 101,110,116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110, 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,103,101,116,118, 97,108, 117,101, 32, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,114,101, 116,117,114,110, 32, 99,108, 97,115,115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 10,101,108,115,101,105,102, 32, 99,108, 97,115,115, 32,116, 104,101,110, 10,114,101,116,117,114,110, 32, 39,115,101,108, 102, 45, 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117, 97, 73, 95,105,110,100,101, 120, 93, 39, 10,101,108,115,101, 10,114,101,116,117,114,110, 32,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,116, 111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 10,101, 110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105, 111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,115, 117,112, 99,111,100,101, 32, 40, 41, 10,108,111, 99, 97,108, 32, 99,108, 97,115,115, 32, 61, 32,115,101,108,102, 58,105, 110, 99,108, 97,115,115, 40, 41, 10, 10, 10,105,102, 32, 99, 108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112,117, 116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110, 99,116, 105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99, 108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115, 101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,103,101, 116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101, 108,102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10, 101,110,100, 10,115,101,108,102, 46, 99,103,101,116,110, 97, 109,101, 32, 61, 32,115,101,108,102, 58, 99,102,117,110, 99, 110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,103,101, 116, 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97, 116,105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99, 103,101,116,110, 97,109,101, 44, 34, 40,108,117, 97, 95, 83, 116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,110,116, 32,116, 111,108,117, 97, 73, 95,105,110,100,101,120, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32, 95, 44, 95, 44,115,116, 97, 116,105, 99, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42, 40,115, 116, 97,116,105, 99, 41, 39, 41, 10,105,102, 32, 99,108, 97, 115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61, 110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,115,116,114,105, 110,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 46,115,101, 108,102, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,114, 97,119,103,101,116, 40,116,111, 108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41, 10,111,117,116, 112,117,116, 40, 39, 32,115,101,108,102, 32, 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97, 115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117, 116, 40, 39,108,117, 97, 95,116,111,117,115,101,114,100, 97, 116, 97, 40,116,111,108,117, 97, 95, 83, 44, 45, 49, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115,101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102,105,110,100, 40, 115,101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42,115, 116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,101,110,100, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95,105,115, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 50, 44, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 44, 48, 41, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108, 117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44, 34,105,110,118, 97,108,105,100, 32,116,121,112,101, 32,105,110, 32, 97,114,114, 97,121, 32,105,110,100,101,120, 105,110,103, 46, 34, 41, 59, 39, 41, 10,111,117,116,112,117, 116, 40, 39, 32,116,111,108,117, 97, 73, 95,105,110,100,101, 120, 32, 61, 32, 40,105,110,116, 41,116,111,108,117, 97, 95, 103,101,116,110,117,109, 98,101,114, 40,116,111,108,117, 97, 95, 83, 44, 50, 44, 48, 41, 45, 49, 59, 39, 41, 10,111,117, 116,112,117,116, 40, 39, 32,105,102, 32, 40,116,111,108,117, 97, 73, 95,105,110,100,101,120, 60, 48, 32,124,124, 32,116, 111,108,117, 97, 73, 95,105,110,100,101,120, 62, 61, 39, 46, 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 97,114,114, 97,121, 32,105,110,100,101,120,105,110,103, 32,111,117,116, 32,111,102, 32,114, 97,110,103,101, 46, 34, 41, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101, 108,102, 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116, 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111, 108,117, 97, 95,112,117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46,115,101,108,102, 58,103,101,116,118, 97, 108,117,101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10, 105,102, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,111,114, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,116, 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115, 104,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41, 38, 39, 46, 46,115, 101,108,102, 58,103,101,116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118, 111,105,100, 42, 41, 39, 46, 46,115,101,108,102, 58,103,101, 116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10, 101,110,100, 10,111,117,116,112,117,116, 40, 39, 32,114,101, 116,117,114,110, 32, 49, 59, 39, 41, 10,111,117,116,112,117, 116, 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10, 10, 10,105,102, 32,110,111,116, 32,115, 116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101,110, 10, 105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,111, 117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116, 32,102, 117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46, 110, 97,109,101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100, 10,115,101,108,102, 46, 99,115, 101,116,110, 97,109,101, 32, 61, 32,115,101,108,102, 58, 99, 102,117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,115,101,116, 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97,116,105, 99, 32,105,110,116, 34, 44,115,101, 108,102, 46, 99,115,101,116,110, 97,109,101, 44, 34, 40,108, 117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105, 110,116, 32,116,111,108,117, 97, 73, 95,105,110,100,101,120, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32,115,116,114,102,105, 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37, 115, 42, 40,115,116, 97,116,105, 99, 41, 39, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116, 105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 59, 39, 41, 10,111,117, 116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104, 115,116,114,105,110,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 46,115,101,108,102, 34, 41, 59, 39, 41, 10,111,117,116, 112,117,116, 40, 39, 32,108,117, 97, 95,114, 97,119,103,101, 116, 40,116,111,108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,115,101,108,102, 32, 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,115,115, 44, 39, 42, 41, 32, 39, 41, 10,111, 117,116,112,117,116, 40, 39,108,117, 97, 95,116,111,117,115, 101,114,100, 97,116, 97, 40,116,111,108,117, 97, 95, 83, 44, 45, 49, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115, 116, 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44, 115,101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102, 105,110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,101,110,100, 10, 10, 10,111,117,116, 112,117,116, 40, 39, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 50, 44, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 44, 48, 41, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111, 108,117, 97, 95, 83, 44, 34,105,110,118, 97,108,105,100, 32, 116,121,112,101, 32,105,110, 32, 97,114,114, 97,121, 32,105, 110,100,101,120,105,110,103, 46, 34, 41, 59, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 73, 95, 105,110,100,101,120, 32, 61, 32, 40,105,110,116, 41,116,111, 108,117, 97, 95,103,101,116,110,117,109, 98,101,114, 40,116, 111,108,117, 97, 95, 83, 44, 50, 44, 48, 41, 45, 49, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 116,111,108,117, 97, 73, 95,105,110,100,101,120, 60, 48, 32, 124,124, 32,116,111,108,117, 97, 73, 95,105,110,100,101,120, 62, 61, 39, 46, 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116, 111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 97,114,114, 97,121, 32,105,110,100,101, 120,105,110,103, 32,111,117,116, 32,111,102, 32,114, 97,110, 103,101, 46, 34, 41, 59, 39, 41, 10, 10, 10,108,111, 99, 97, 108, 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115, 101,108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101, 110, 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,111,117,116,112,117,116, 40, 39, 32, 39, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116, 105, 99, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115,115, 46, 46, 39, 58, 58, 39, 46, 46,115,101, 108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,108,115, 101,105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10, 111,117,116,112,117,116, 40, 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91, 116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40,115,101, 108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,105,115, 98, 97, 115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10, 111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41, 10,105, 102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112,116,114, 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116,112,117, 116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117,116,112, 117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,109,111, 100, 44,115,101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,110,111,116, 32,116, 32,116,104,101,110, 10,111,117,116, 112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117, 116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97, 108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32,115,101, 108,102, 46,100,101,102, 32,126, 61, 32, 39, 39, 32,116,104, 101,110, 32,100,101,102, 32, 61, 32,115,101,108,102, 46,100, 101,102, 32,101,110,100, 10,105,102, 32,116, 32,116,104,101, 110, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116, 39, 46, 46,116, 44, 39, 40,116,111,108,117, 97, 95, 83, 44, 51, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 51, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110, 100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,116,117, 114,110, 32, 48, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10, 10,101,110,100, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10, 108,111, 99, 97,108, 32,112, 97,114,101,110,116, 32, 61, 32, 115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32, 111,114, 32,115,101,108,102, 58,105,110,109,111,100,117,108, 101, 40, 41, 10,105,102, 32,112, 97,114,101,110,116, 32,116, 104,101,110, 10,105,102, 32,115,101,108,102, 46, 99,115,101, 116,110, 97,109,101, 32,116,104,101,110, 10,111,117,116,112, 117,116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108, 101, 97,114,114, 97,121, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101, 116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,101,108, 102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,101, 97,114, 114, 97,121, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97, 109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10, 101,110,100, 10,101,108,115,101, 10,105,102, 32,115,101,108, 102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108, 97,114,114, 97,121, 40,116,111, 108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46, 108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101, 108,102, 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,101,108,102, 46, 99,115,101,116,110, 97,109, 101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111, 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103, 108,111, 98, 97,108, 97,114,114, 97,121, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99, 108, 97,115,115, 65,114,114, 97,121, 58,117,110,114,101,103, 105,115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108, 102, 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105, 108, 32, 97,110,100, 32,115,101,108,102, 58,105,110,109,111, 100,117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101, 110, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95, 112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97, 108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115, 101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 95, 65,114,114, 97,121, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99, 108, 97,115,115, 65,114,114, 97,121, 10,115,101,116,116, 97, 103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,117,114, 110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32, 65,114,114, 97,121, 32, 40,115, 41, 10, 114,101,116,117,114,110, 32, 95, 65,114,114, 97,121, 32, 40, 68,101, 99,108, 97,114, 97,116,105,111,110, 40,115, 44, 39, 118, 97,114, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 70,117,110, 99,116,105,111, 110, 32, 61, 32,123, 10,109,111,100, 32, 61, 32, 39, 39, 44, 10,116,121,112,101, 32, 61, 32, 39, 39, 44, 10,112,116,114, 32, 61, 32, 39, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39, 44, 10, 97,114,103,115, 32, 61, 32,123,110, 61, 48,125, 44, 10, 99,111,110,115,116, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97, 116,117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 44,116, 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,117,110, 99, 116,105,111,110, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115,101,108,102, 46,105,116,121,112,101, 44,115,101,108, 102, 46,116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40, 115,101,108,102, 46,116,121,112,101, 44,115,116,114,102,105, 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111, 110,115,116, 39, 41, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 46, 97,114, 103,115, 91,105, 93, 32,100,111, 10,115,101,108,102, 46, 97, 114,103,115, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101, 110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58, 115,117,112, 99,111,100,101, 32, 40, 41, 10,108,111, 99, 97, 108, 32,110,114,101,116, 32, 61, 32, 48, 10,108,111, 99, 97, 108, 32, 99,108, 97,115,115, 32, 61, 32,115,101,108,102, 58, 105,110, 99,108, 97,115,115, 40, 41, 10,108,111, 99, 97,108, 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32,115, 116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 39, 41, 10, 10,105,102, 32, 99,108, 97,115,115, 32,116,104,101, 110, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,109,101, 116,104,111,100, 58, 34, 44,115,101,108,102, 46,110, 97,109, 101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108, 115,101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,102, 117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46, 110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100, 10,111,117,116,112,117,116, 40, 34,115,116, 97,116,105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99,110, 97,109, 101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32, 116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112, 117,116, 40, 34,123, 34, 41, 10, 10, 10,111,117,116,112,117, 116, 40, 39, 32,105,102, 32, 40, 92,110, 39, 41, 10, 10,108, 111, 99, 97,108, 32,110, 97,114,103, 10,105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110, 100, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32, 115,101,108,102, 46,110, 97,109,101,126, 61, 39,110,101,119, 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110, 105,108, 32,116,104,101,110, 10,105,102, 32,115,101,108,102, 46, 99,111,110,115,116, 32, 61, 61, 32, 39, 99,111,110,115, 116, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32, 33,116,111,108,117, 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 39, 44,115,101, 108,102, 46,112, 97,114,101,110,116, 46, 99,116, 97,103, 44, 39, 44, 48, 41, 32,124,124, 92,110, 39, 41, 10,101,108,115, 101, 10,111,117,116,112,117,116, 40, 39, 32, 33,116,111,108, 117, 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 39, 44,115,101,108,102, 46,112, 97,114, 101,110,116, 46,116, 97,103, 44, 39, 44, 48, 41, 32,124,124, 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10,105, 102, 32,115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46, 116,121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32, 116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10, 119,104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,105,102, 32,105,115, 98, 97,115, 105, 99, 40,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 46,116,121,112,101, 41, 32,126, 61, 32, 39,118, 97,108,117, 101, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32, 33, 39, 46, 46,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,111,117,116, 99,104,101, 99,107,116,121,112, 101, 40,110, 97,114,103, 41, 46, 46, 39, 32,124,124, 92,110, 39, 41, 10,101,110,100, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101, 110,100, 10,101,110,100, 10, 10,111,117,116,112,117,116, 40, 39, 32, 33,116,111,108,117, 97, 95,105,115,110,111,111, 98, 106, 40,116,111,108,117, 97, 95, 83, 44, 39, 46, 46,110, 97, 114,103, 46, 46, 39, 41, 92,110, 32, 41, 92,110, 32,103,111, 116,111, 32,116,111,108,117, 97, 95,108,101,114,114,111,114, 59, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39, 32,101, 108,115,101, 92,110, 32,123, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,110, 97,114,103, 10,105,102, 32, 99,108, 97,115, 115, 32,116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101, 108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10, 105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,101, 108,102, 46,110, 97,109,101,126, 61, 39,110,101,119, 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44,115,101,108,102, 46, 99,111,110,115,116, 44, 99,108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32, 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39, 44, 115,101,108,102, 46, 99,111,110,115,116, 44, 99,108, 97,115, 115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114, 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 48, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115, 101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102,105, 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37, 115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,101,110,100, 10, 10,105,102, 32,115,101, 108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108, 101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32, 100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,100,101, 99,108, 97,114,101, 40,110, 97,114,103, 41, 10, 110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32, 115,101,108,102, 46,110, 97,109,101,126, 61, 39,110,101,119, 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110, 105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111, 108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44, 34,105,110,118, 97,108,105,100, 32, 92, 39,115, 101,108,102, 92, 39, 32,105,110, 32,102,117,110, 99,116,105, 111,110, 32, 92, 39, 39, 46, 46,115,101,108,102, 46,110, 97, 109,101, 46, 46, 39, 92, 39, 34, 41, 59, 39, 41, 59, 10,101, 110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,116, 104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,105,102, 32, 115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,121, 112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104, 101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104, 105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115, 91, 105, 93, 58,103,101,116, 97,114,114, 97,121, 40,110, 97,114, 103, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101, 110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97, 110,100, 32,115,101,108,102, 46,110, 97,109,101, 61, 61, 39, 100,101,108,101,116,101, 39, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 32,100,101,108,101,116,101, 32,115, 101,108,102, 59, 39, 41, 10,101,108,115,101,105,102, 32, 99, 108, 97,115,115, 32, 97,110,100, 32,115,101,108,102, 46,110, 97,109,101, 32, 61, 61, 32, 39,111,112,101,114, 97,116,111, 114, 38, 91, 93, 39, 32,116,104,101,110, 10,111,117,116,112, 117,116, 40, 39, 32,115,101,108,102, 45, 62,111,112,101,114, 97,116,111,114, 91, 93, 40, 39, 44,115,101,108,102, 46, 97, 114,103,115, 91, 49, 93, 46,110, 97,109,101, 44, 39, 41, 32, 61, 32, 39, 44,115,101,108,102, 46, 97,114,103,115, 91, 50, 93, 46,110, 97,109,101, 44, 39, 59, 39, 41, 10,101,108,115, 101, 10,111,117,116,112,117,116, 40, 39, 32,123, 39, 41, 10, 105,102, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61, 32, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,116,121, 112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104, 101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44,115, 101,108,102, 46,109,111,100, 44,115,101,108,102, 46,116,121, 112,101, 44,115,101,108,102, 46,112,116,114, 44, 39,116,111, 108,117, 97, 73, 95,114,101,116, 32, 61, 32, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 40, 39, 44,115,101,108,102, 46, 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,115, 101,108,102, 46,112,116,114, 44, 39, 41, 32, 39, 41, 10,101, 108,115,101, 10,111,117,116,112,117,116, 40, 39, 32, 39, 41, 10,101,110,100, 10,105,102, 32, 99,108, 97,115,115, 32, 97, 110,100, 32,115,101,108,102, 46,110, 97,109,101, 61, 61, 39, 110,101,119, 39, 32,116,104,101,110, 10,111,117,116,112,117, 116, 40, 39,110,101,119, 39, 44, 99,108, 97,115,115, 44, 39, 40, 39, 41, 10,101,108,115,101,105,102, 32, 99,108, 97,115, 115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116,104, 101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115,115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110, 97, 109,101, 44, 39, 40, 39, 41, 10,101,108,115,101,105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112, 117,116, 40, 39,115,101,108,102, 45, 62, 39, 46, 46,115,101, 108,102, 46,110, 97,109,101, 44, 39, 40, 39, 41, 10,101,108, 115,101, 10,111,117,116,112,117,116, 40,115,101,108,102, 46, 110, 97,109,101, 44, 39, 40, 39, 41, 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108, 101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32, 100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,112, 97,115,115,112, 97,114, 40, 41, 10,105, 32, 61, 32, 105, 43, 49, 10,105,102, 32,115,101,108,102, 46, 97,114,103, 115, 91,105, 93, 32,116,104,101,110, 10,111,117,116,112,117, 116, 40, 39, 44, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10,111,117,116,112,117,116, 40, 39, 41, 59, 39, 41, 10, 10, 10,105,102, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61, 32, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,116, 121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116, 104,101,110, 10,110,114,101,116, 32, 61, 32,110,114,101,116, 32, 43, 32, 49, 10,108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,104,101, 110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116, 111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 116,111,108,117, 97, 73, 95,114,101,116, 41, 59, 39, 41, 10, 101,108,115,101, 10,105,102, 32,115,101,108,102, 46,112,116, 114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117, 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112, 117,116, 40, 39, 35,105,102,100,101,102, 32, 95, 95, 99,112, 108,117,115,112,108,117,115, 92,110, 39, 41, 10,111,117,116, 112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108, 117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119, 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 40,116, 111,108,117, 97, 73, 95,114,101,116, 41, 59, 39, 41, 10,111, 117,116,112,117,116, 40, 39, 35,101,108,115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,111,112,121, 40,116,111, 108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41, 38,116, 111,108,117, 97, 73, 95,114,101,116, 44,115,105,122,101,111, 102, 40, 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 41, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35, 101,110,100,105,102, 92,110, 39, 41, 10,111,117,116,112,117, 116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,117, 115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40, 116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 73, 95, 99,108,111,110,101, 44, 39, 44,115,101,108,102, 46,116, 97, 103, 44, 39, 41, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10, 10,101,108,115,101,105,102, 32,115,101, 108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,116, 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111, 108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112, 101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,116, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32, 116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116, 121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111, 105,100, 42, 41,116,111,108,117, 97, 73, 95,114,101,116, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10,108, 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32, 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,110,114,101,116, 32, 61, 32,110,114,101,116, 32, 43, 32, 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,114,101, 116,118, 97,108,117,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,111,117,116,112,117,116, 40, 39, 32, 125, 39, 41, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115, 101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,105,102, 32,115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,116, 121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116, 104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119, 104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91, 105, 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,115,101,116, 97,114,114, 97,121, 40,110, 97, 114,103, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10, 101,110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46, 97, 114,103,115, 91, 49, 93, 46,116,121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101, 108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115, 101,108,102, 46, 97,114,103,115, 91,105, 93, 58,102,114,101, 101, 97,114,114, 97,121, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10, 111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,111,117, 116,112,117,116, 40, 39, 32,114,101,116,117,114,110, 32, 39, 46, 46,110,114,101,116, 46, 46, 39, 59, 39, 41, 10, 10, 10, 111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,108, 101,114,114,111,114, 58, 92,110, 39, 41, 10,108,111, 99, 97, 108, 32,111,118,101,114,108,111, 97,100, 32, 61, 32,115,116, 114,115,117, 98, 40,115,101,108,102, 46, 99,110, 97,109,101, 44, 45, 50, 44, 45, 49, 41, 32, 45, 32, 49, 10,105,102, 32, 111,118,101,114,108,111, 97,100, 32, 62, 61, 32, 48, 32,116, 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,114,101, 116,117,114,110, 32, 39, 46, 46,115,116,114,115,117, 98, 40, 115,101,108,102, 46, 99,110, 97,109,101, 44, 49, 44, 45, 51, 41, 46, 46,102,111,114,109, 97,116, 40, 34, 37, 48, 50,100, 34, 44,111,118,101,114,108,111, 97,100, 41, 46, 46, 39, 40, 116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,101,108,115, 101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 35,102,101,114,114,111,114, 32,105,110, 32,102,117, 110, 99,116,105,111,110, 32, 92, 39, 39, 46, 46,115,101,108, 102, 46,108,110, 97,109,101, 46, 46, 39, 92, 39, 46, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,114,101, 116,117,114,110, 32, 48, 59, 39, 41, 10,101,110,100, 10, 10, 111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,117,116, 112,117,116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,114,101,103,105,115,116, 101,114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 97,114, 101,110,116, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105, 110,109,111,100,117,108,101, 40, 41, 10,105,102, 32,112, 97, 114,101,110,116, 32,116,104,101,110, 10,111,117,116,112,117, 116, 40, 39, 32,116,111,108,117, 97, 95,102,117,110, 99,116, 105,111,110, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116, 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,102,117,110, 99,116,105,111,110, 40,116,111,108,117, 97, 95, 83, 44, 78, 85, 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101, 110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105, 111,110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111, 110, 58,117,110,114,101,103,105,115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 58,105,110, 99,108, 97,115, 115, 40, 41, 61, 61,110,105,108, 32, 97,110,100, 32,115,101, 108,102, 58,105,110,109,111,100,117,108,101, 40, 41, 61, 61, 110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40, 116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115, 101,116,103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109, 101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101, 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,112, 114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111, 115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 70,117,110, 99,116,105,111,110,123, 34, 41, 10, 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110, 116, 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112, 101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40, 105,100,101,110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99,111,110,115,116, 32, 61, 32, 39, 34, 46, 46, 115,101,108,102, 46, 99,111,110,115,116, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46, 115,101,108,102, 46, 99,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,108,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46, 115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 97,114,103,115, 32, 61, 32,123, 34, 41, 10,108, 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32, 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101, 110,100, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,125, 34, 41, 10,112,114,105,110,116, 40,105,100, 101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111, 110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,111,118,101,114,108,111, 97,100, 32, 40, 41, 10,114,101, 116,117,114,110, 32,115,101,108,102, 46,112, 97,114,101,110, 116, 58,111,118,101,114,108,111, 97,100, 40,115,101,108,102, 46,108,110, 97,109,101, 41, 10,101,110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 70,117,110, 99, 116,105,111,110, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115, 101, 32, 61, 32, 99,108, 97,115,115, 70,117,110, 99,116,105, 111,110, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108, 117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,116, 46, 99, 111,110,115,116, 32,126, 61, 32, 39, 99,111,110,115,116, 39, 32, 97,110,100, 32,116, 46, 99,111,110,115,116, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,105,100, 32, 39, 99,111,110,115, 116, 39, 32,115,112,101, 99,105,102,105, 99, 97,116,105,111, 110, 34, 41, 10,101,110,100, 10, 10, 97,112,112,101,110,100, 40,116, 41, 10,105,102, 32,116, 58,105,110, 99,108, 97,115, 115, 40, 41, 32,116,104,101,110, 10,105,102, 32,116, 46,110, 97,109,101, 32, 61, 61, 32,116, 46,112, 97,114,101,110,116, 46,110, 97,109,101, 32,116,104,101,110, 10,116, 46,110, 97, 109,101, 32, 61, 32, 39,110,101,119, 39, 10,116, 46,108,110, 97,109,101, 32, 61, 32, 39,110,101,119, 39, 10,116, 46,116, 121,112,101, 32, 61, 32,116, 46,112, 97,114,101,110,116, 46, 110, 97,109,101, 10,116, 46,112,116,114, 32, 61, 32, 39, 42, 39, 10,101,108,115,101,105,102, 32,116, 46,110, 97,109,101, 32, 61, 61, 32, 39,126, 39, 46, 46,116, 46,112, 97,114,101, 110,116, 46,110, 97,109,101, 32,116,104,101,110, 10,116, 46, 110, 97,109,101, 32, 61, 32, 39,100,101,108,101,116,101, 39, 10,116, 46,108,110, 97,109,101, 32, 61, 32, 39,100,101,108, 101,116,101, 39, 10,101,110,100, 10,101,110,100, 10,116, 46, 99,110, 97,109,101, 32, 61, 32,116, 58, 99,102,117,110, 99, 110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 34, 41, 46, 46,116, 58,111,118,101,114,108,111, 97,100, 40,116, 41, 10, 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 70,117,110, 99,116,105,111,110, 32, 40,100, 44, 97, 44, 99, 41, 10,108, 111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40, 115,116,114,115,117, 98, 40, 97, 44, 50, 44, 45, 50, 41, 44, 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10, 108,111, 99, 97,108, 32,108, 32, 61, 32,123,110, 61, 48,125, 10,119,104,105,108,101, 32,116, 91,105, 93, 32,100,111, 10, 108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,105, 111,110, 40,116, 91,105, 93, 44, 39,118, 97,114, 39, 41, 10, 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,108,111, 99, 97,108, 32,102, 32, 61, 32, 68,101, 99,108, 97,114, 97,116, 105,111,110, 40,100, 44, 39,102,117,110, 99, 39, 41, 10,102, 46, 97,114,103,115, 32, 61, 32,108, 10,102, 46, 99,111,110, 115,116, 32, 61, 32, 99, 10,114,101,116,117,114,110, 32, 95, 70,117,110, 99,116,105,111,110, 40,102, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 79,112,101,114, 97,116, 111,114, 32, 61, 32,123, 10,107,105,110,100, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115, 115, 70,117,110, 99,116,105,111,110, 44, 10,125, 10,115,101, 116,116, 97,103, 40, 99,108, 97,115,115, 79,112,101,114, 97, 116,111,114, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10, 95, 84, 77, 32, 61, 32,123, 91, 39, 43, 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,111,114, 95, 97,100,100, 39, 44, 10, 91, 39, 45, 39, 93, 32, 61, 32, 39,111,112,101, 114, 97,116,111,114, 95,115,117, 98, 39, 44, 10, 91, 39, 42, 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,111,114, 95, 109,117,108, 39, 44, 10, 91, 39, 47, 39, 93, 32, 61, 32, 39, 111,112,101,114, 97,116,111,114, 95,100,105,118, 39, 44, 10, 91, 39, 60, 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116, 111,114, 95,108,116, 39, 44, 10, 91, 39, 91, 93, 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,111,114, 95,103,101,116, 39, 44, 10, 91, 39, 38, 91, 93, 39, 93, 32, 61, 32, 39,111, 112,101,114, 97,116,111,114, 95,115,101,116, 39, 44, 10,125, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 79,112,101,114, 97,116,111,114, 58,112,114,105, 110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 79,112,101,114, 97,116,111,114,123, 34, 41, 10,112,114, 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,107,105, 110,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,107, 105,110,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110, 116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100, 101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110, 116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46, 46, 115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108, 102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99, 111,110,115,116, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 99,111,110,115,116, 46, 46, 34, 39, 44, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99, 110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 99,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,108, 110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 97, 114,103,115, 32, 61, 32,123, 34, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,101,108, 102, 46, 97,114,103,115, 91,105, 93, 58,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112, 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,125, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110, 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 79, 112,101,114, 97,116,111,114, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 79,112,101, 114, 97,116,111,114, 10,115,101,116,116, 97,103, 40,116, 44, 116,111,108,117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32, 116, 46, 99,111,110,115,116, 32,126, 61, 32, 39, 99,111,110, 115,116, 39, 32, 97,110,100, 32,116, 46, 99,111,110,115,116, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,101,114,114, 111,114, 40, 34, 35,105,110,118, 97,108,105,100, 32, 39, 99, 111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97, 116,105,111,110, 34, 41, 10,101,110,100, 10, 10, 97,112,112, 101,110,100, 40,116, 41, 10,105,102, 32,110,111,116, 32,116, 58,105,110, 99,108, 97,115,115, 40, 41, 32,116,104,101,110, 10,101,114,114,111,114, 40, 34, 35,111,112,101,114, 97,116, 111,114, 32, 99, 97,110, 32,111,110,108,121, 32, 98,101, 32, 100,101,102,105,110,101,100, 32, 97,115, 32, 99,108, 97,115, 115, 32,109,101,109, 98,101,114, 34, 41, 10,101,110,100, 10, 10,116, 46, 99,110, 97,109,101, 32, 61, 32,116, 58, 99,102, 117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 34, 41, 46, 46,116, 58,111,118,101,114,108,111, 97,100, 40, 116, 41, 10,116, 46,110, 97,109,101, 32, 61, 32,116, 46,110, 97,109,101, 46, 46,116, 46,107,105,110,100, 10,114,101,116, 117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 10, 10, 102,117,110, 99,116,105,111,110, 32, 79,112,101,114, 97,116, 111,114, 32, 40,100, 44,107, 44, 97, 44, 99, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,115, 116,114,115,117, 98, 40, 97, 44, 50, 44,115,116,114,108,101, 110, 40, 97, 41, 45, 49, 41, 44, 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,108,111, 99, 97,108, 32,108, 32, 61, 32,123,110, 61, 48,125, 10,119,104,105,108,101, 32, 116, 91,105, 93, 32,100,111, 10,108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32, 68, 101, 99,108, 97,114, 97,116,105,111,110, 40,116, 91,105, 93, 44, 39,118, 97,114, 39, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,105,102, 32,107, 32, 61, 61, 32, 39, 91, 93, 39, 32,116,104,101,110, 10,100, 32, 61, 32,103,115,117, 98, 40,100, 44, 39, 38, 39, 44, 39, 39, 41, 10,101,108,115, 101,105,102, 32,107, 61, 61, 39, 38, 91, 93, 39, 32,116,104, 101,110, 10,108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10, 108, 91,108, 46,110, 93, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,105,111,110, 40,100, 44, 39,118, 97,114, 39, 41, 10, 108, 91,108, 46,110, 93, 46,110, 97,109,101, 32, 61, 32, 39, 116,111,108,117, 97, 73, 95,118, 97,108,117,101, 39, 10,101, 110,100, 10,108,111, 99, 97,108, 32,102, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,105,111,110, 40,100, 44, 39,102,117, 110, 99, 39, 41, 10,105,102, 32,107, 32, 61, 61, 32, 39, 91, 93, 39, 32, 97,110,100, 32, 40,108, 91, 49, 93, 61, 61,110, 105,108, 32,111,114, 32,105,115, 98, 97,115,105, 99, 40,108, 91, 49, 93, 46,116,121,112,101, 41,126, 61, 39,110,117,109, 98,101,114, 39, 41, 32,116,104,101,110, 10,101,114,114,111, 114, 40, 39,111,112,101,114, 97,116,111,114, 91, 93, 32, 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105, 110,101,100, 32,102,111,114, 32,110,117,109,101,114,105, 99, 32,105,110,100,101,120, 46, 39, 41, 10,101,110,100, 10,102, 46, 97,114,103,115, 32, 61, 32,108, 10,102, 46, 99,111,110, 115,116, 32, 61, 32, 99, 10,102, 46,107,105,110,100, 32, 61, 32,103,115,117, 98, 40,107, 44, 34, 37,115, 34, 44, 34, 34, 41, 10,102, 46,108,110, 97,109,101, 32, 61, 32, 95, 84, 77, 91,102, 46,107,105,110,100, 93, 10,105,102, 32,110,111,116, 32,102, 46,108,110, 97,109,101, 32,116,104,101,110, 10,101, 114,114,111,114, 40, 34,116,111,108,117, 97, 58, 32,110,111, 32,115,117,112,112,111,114,116, 32,102,111,114, 32,111,112, 101,114, 97,116,111,114, 34, 32, 46, 46, 32,102, 46,107,105, 110,100, 41, 10,101,110,100, 10,105,102, 32,102, 46,107,105, 110,100, 32, 61, 61, 32, 39, 91, 93, 39, 32, 97,110,100, 32, 110,111,116, 32,115,116,114,102,105,110,100, 40,102, 46,109, 111,100, 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101, 110, 10, 79,112,101,114, 97,116,111,114, 40,100, 44, 39, 38, 39, 46, 46,107, 44, 97, 44, 99, 41, 10,101,110,100, 10,114, 101,116,117,114,110, 32, 95, 79,112,101,114, 97,116,111,114, 40,102, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115, 115, 67,108, 97,115,115, 32, 61, 32,123, 10, 95, 98, 97,115, 101, 32, 61, 32, 99,108, 97,115,115, 67,111,110,116, 97,105, 110,101,114, 44, 10,116,121,112,101, 32, 61, 32, 39, 99,108, 97,115,115, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39, 44, 10, 98, 97,115,101, 32, 61, 32, 39, 39, 44, 10,125, 10, 115,101,116,116, 97,103, 40, 99,108, 97,115,115, 67,108, 97, 115,115, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115, 115, 67,108, 97,115,115, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111, 108,117, 97, 95, 99, 99,108, 97,115,115, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97, 109,101, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46, 98, 97,115,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,108, 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32, 115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,114,101,103,105,115,116,101,114, 40, 41, 10, 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97, 115,115, 67,108, 97,115,115, 58,117,110,114,101,103,105,115, 116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111, 108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116, 103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10, 10, 10,102,117, 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,108, 97, 115,115, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115, 101,108,102, 46,105,116,121,112,101, 44,115,101,108,102, 46, 116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101, 108,102, 46,110, 97,109,101, 41, 59, 10,115,101,108,102, 46, 99,105,116,121,112,101, 44,115,101,108,102, 46, 99,116, 97, 103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,108,102, 46,110, 97,109,101, 44, 39, 99,111,110,115,116, 39, 41, 59, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108, 101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,115,101, 108,102, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110, 100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99, 108, 97,115,115, 67,108, 97,115,115, 58,112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10, 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 67, 108, 97,115,115,123, 34, 41, 10,112,114,105,110,116, 40,105, 100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101, 110,116, 46, 46, 34, 32, 98, 97,115,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46, 98, 97,115,101, 46, 46, 34, 39, 59, 34, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119, 104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,112,114,105,110,116, 40, 105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,114, 105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102, 117,110, 99,116,105,111,110, 32, 95, 67,108, 97,115,115, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99, 108, 97,115,115, 67,108, 97,115,115, 10,115,101,116,116, 97, 103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,117,114, 110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99, 116,105,111,110, 32, 67,108, 97,115,115, 32, 40,110, 44,112, 44, 98, 41, 10,108,111, 99, 97,108, 32, 99, 32, 61, 32, 95, 67,108, 97,115,115, 40, 95, 67,111,110,116, 97,105,110,101, 114,123,110, 97,109,101, 61,110, 44, 32, 98, 97,115,101, 61, 112,125, 41, 10,112,117,115,104, 40, 99, 41, 10, 99, 58,112, 97,114,115,101, 40,115,116,114,115,117, 98, 40, 98, 44, 50, 44,115,116,114,108,101,110, 40, 98, 41, 45, 49, 41, 41, 10, 112,111,112, 40, 41, 10,101,110,100, 10, 10, 10, 10, 83, 84, 82, 49, 32, 61, 32, 34, 92, 48, 48, 49, 34, 10, 83, 84, 82, 50, 32, 61, 32, 34, 92, 48, 48, 50, 34, 10, 83, 84, 82, 51, 32, 61, 32, 34, 92, 48, 48, 51, 34, 10, 83, 84, 82, 52, 32, 61, 32, 34, 92, 48, 48, 52, 34, 10, 82, 69, 77, 32, 61, 32, 34, 92, 48, 48, 53, 34, 10, 65, 78, 89, 32, 61, 32, 34, 40, 91, 92, 48, 48, 49, 45, 92, 48, 48, 53, 93, 41, 34, 10, 69, 83, 67, 49, 32, 61, 32, 34, 92, 48, 48, 54, 34, 10, 69, 83, 67, 50, 32, 61, 32, 34, 92, 48, 48, 55, 34, 10, 10, 77, 65, 83, 75, 32, 61, 32,123, 10,123, 69, 83, 67, 49, 44, 32, 34, 92, 92, 39, 34,125, 44, 10,123, 69, 83, 67, 50, 44, 32, 39, 92, 92, 34, 39,125, 44, 10,123, 83, 84, 82, 49, 44, 32, 34, 39, 34,125, 44, 10,123, 83, 84, 82, 50, 44, 32, 39, 34, 39, 125, 44, 10,123, 83, 84, 82, 51, 44, 32, 34, 37, 91, 37, 91, 34,125, 44, 10,123, 83, 84, 82, 52, 44, 32, 34, 37, 93, 37, 93, 34,125, 44, 10,123, 82, 69, 77, 32, 44, 32, 34, 37, 45, 37, 45, 34,125, 44, 10,125, 10, 10,102,117,110, 99,116,105, 111,110, 32,109, 97,115,107, 32, 40,115, 41, 10,102,111,114, 32,105, 32, 61, 32, 49, 44,103,101,116,110, 40, 77, 65, 83, 75, 41, 32,100,111, 10,115, 32, 61, 32,103,115,117, 98, 40, 115, 44, 77, 65, 83, 75, 91,105, 93, 91, 50, 93, 44, 77, 65, 83, 75, 91,105, 93, 91, 49, 93, 41, 10,101,110,100, 10,114, 101,116,117,114,110, 32,115, 10,101,110,100, 10, 10,102,117, 110, 99,116,105,111,110, 32,117,110,109, 97,115,107, 32, 40, 115, 41, 10,102,111,114, 32,105, 32, 61, 32, 49, 44,103,101, 116,110, 40, 77, 65, 83, 75, 41, 32,100,111, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 77, 65, 83, 75, 91,105, 93, 91, 49, 93, 44, 77, 65, 83, 75, 91,105, 93, 91, 50, 93, 41, 10,101,110,100, 10,114,101,116,117,114,110, 32,115, 10,101, 110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 101, 97,110, 32, 40,115, 41, 10, 10,108,111, 99, 97,108, 32, 99,111,100,101, 32, 61, 32, 34,114,101,116,117,114,110, 32, 102,117,110, 99,116,105,111,110, 32, 40, 41, 32, 34, 32, 46, 46, 32,115, 32, 46, 46, 32, 34, 32,101,110,100, 34, 10,105, 102, 32,110,111,116, 32,100,111,115,116,114,105,110,103, 40, 99,111,100,101, 41, 32,116,104,101,110, 10,114,101,116,117, 114,110, 32,110,105,108, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32, 83, 32, 61, 32, 34, 34, 10, 10,115, 32, 61, 32, 109, 97,115,107, 40,115, 41, 10, 10, 10,119,104,105,108,101, 32, 49, 32,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,100, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 65, 78, 89, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 83, 32, 61, 32, 83, 46, 46,115,116,114,115,117, 98, 40,115, 44, 49, 44, 98, 45, 49, 41, 10,115, 32, 61, 32,115,116,114, 115,117, 98, 40,115, 44, 98, 43, 49, 41, 10,105,102, 32,100, 61, 61, 83, 84, 82, 49, 32,111,114, 32,100, 61, 61, 83, 84, 82, 50, 32,116,104,101,110, 10,101, 32, 61, 32,115,116,114, 102,105,110,100, 40,115, 44,100, 41, 10, 83, 32, 61, 32, 83, 32, 46, 46,100, 46, 46,115,116,114,115,117, 98, 40,115, 44, 49, 44,101, 41, 10,115, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,108,115,101,105,102, 32, 100, 61, 61, 83, 84, 82, 51, 32,116,104,101,110, 10,101, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 83, 84, 82, 52, 41, 10, 83, 32, 61, 32, 83, 46, 46,100, 46, 46,115,116, 114,115,117, 98, 40,115, 44, 49, 44,101, 41, 10,115, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10, 101,108,115,101,105,102, 32,100, 61, 61, 82, 69, 77, 32,116, 104,101,110, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 91, 94, 92,110, 93, 42, 40, 92,110, 63, 41, 34, 44, 34, 37, 49, 34, 44, 49, 41, 10,101,110,100, 10,101,108,115,101, 10, 83, 32, 61, 32, 83, 46, 46,115, 10, 98,114,101, 97,107, 10,101,110,100, 10,101,110,100, 10, 10, 83, 32, 61, 32,103, 115,117, 98, 40, 83, 44, 34, 91, 32, 92,116, 93, 43, 34, 44, 34, 32, 34, 41, 10, 83, 32, 61, 32,103,115,117, 98, 40, 83, 44, 34, 91, 32, 92,116, 93, 42, 92,110, 91, 32, 92,116, 93, 42, 34, 44, 34, 92,110, 34, 41, 10, 83, 32, 61, 32,117,110, 109, 97,115,107, 40, 83, 41, 10,114,101,116,117,114,110, 32, 83, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,105,102, 32,102,108, 97,103,115, 46,102, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44, 32,109,115,103, 32, 61, 32,114,101, 97,100,102,114,111,109, 40,102,108, 97,103,115, 46,102, 41, 10,105,102, 32,110,111, 116, 32,115,116, 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39, 46, 46,109,115,103, 41, 10,101,110,100, 10, 101,110,100, 10, 10, 10,105,102, 32,110,111,116, 32,102,108, 97,103,115, 46,110, 32,116,104,101,110, 10,105,102, 32,102, 108, 97,103,115, 46,102, 32,116,104,101,110, 10,102,108, 97, 103,115, 46,110, 32, 61, 32,103,115,117, 98, 40,102,108, 97, 103,115, 46,102, 44, 34, 37, 46, 46, 42, 34, 44, 34, 34, 41, 10,101,108,115,101, 10,101,114,114,111,114, 40, 34, 35,110, 111, 32,112, 97, 99,107, 97,103,101, 32,110, 97,109,101, 32, 110,111,114, 32,105,110,112,117,116, 32,102,105,108,101, 32, 112,114,111,118,105,100,101,100, 34, 41, 10,101,110,100, 10, 101,110,100, 10, 10,108,111, 99, 97,108, 32,112, 32, 61, 32, 80, 97, 99,107, 97,103,101, 40,102,108, 97,103,115, 46,110, 41, 10, 10,105,102, 32,102,108, 97,103,115, 46,102, 32,116, 104,101,110, 10,114,101, 97,100,102,114,111,109, 40, 41, 10, 101,110,100, 10, 10,105,102, 32,102,108, 97,103,115, 46,112, 32,116,104,101,110, 10,114,101,116,117,114,110, 10,101,110, 100, 10, 10,105,102, 32,102,108, 97,103,115, 46,111, 32,116, 104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,109,115, 103, 32, 61, 32,119,114,105,116,101,116,111, 40,102,108, 97, 103,115, 46,111, 41, 10,105,102, 32,110,111,116, 32,115,116, 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39, 46, 46,109,115,103, 41, 10,101,110,100, 10,101,110,100, 10, 10,105,102, 32,102,108, 97,103,115, 46, 80, 32,116,104,101, 110, 10,112, 58,112,114,105,110,116, 40, 41, 10,101,108,115, 101, 10,112, 58,100,101, 99,108,116, 97,103, 40, 41, 10,112, 58,112,114,101, 97,109, 98,108,101, 40, 41, 10,112, 58,115, 117,112, 99,111,100,101, 40, 41, 10,112, 58,114,101,103,105, 115,116,101,114, 40, 41, 10,112, 58,117,110,114,101,103,105, 115,116,101,114, 40, 41, 10,101,110,100, 10, 10,105,102, 32, 102,108, 97,103,115, 46,111, 32,116,104,101,110, 10,119,114, 105,116,101,116,111, 40, 41, 10,101,110,100, 10, 10, 10,105, 102, 32,110,111,116, 32,102,108, 97,103,115, 46, 80, 32,116, 104,101,110, 10,105,102, 32,102,108, 97,103,115, 46, 72, 32, 116,104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,109, 115,103, 32, 61, 32,119,114,105,116,101,116,111, 40,102,108, 97,103,115, 46, 72, 41, 10,105,102, 32,110,111,116, 32,115, 116, 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39, 46, 46,109,115,103, 41, 10,101,110,100, 10,112, 58,104, 101, 97,100,101,114, 40, 41, 10,119,114,105,116,101,116,111, 40, 41, 10,101,110,100, 10,101,110,100,32 }; lua_dobuffer(tolua_S,(char*)B,sizeof(B),"tolua: embedded Lua code"); } /* end of embedded lua code */ return 1; } /* Close function */ void tolua_tolualua_close (lua_State* tolua_S) { (void) tolua_S; } zangband/src/lua/lapi.h0000644000000000000000000000044410250356275014015 0ustar rootroot/* ** $Id: lapi.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ #ifndef lapi_h #define lapi_h #include "lobject.h" TObject *luaA_index (lua_State *L, int index); void luaA_pushobject (lua_State *L, const TObject *o); #endif zangband/src/lua/lauxlib.h0000644000000000000000000000551510250356275014534 0ustar rootroot/* ** $Id: lauxlib.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #ifndef lauxlib_h #define lauxlib_h #include #include #include "lua.h" #ifndef LUALIB_API #define LUALIB_API extern #endif struct luaL_reg { const char *name; lua_CFunction func; }; LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n); LUALIB_API void luaL_argerror (lua_State *L, int numarg, const char *extramsg); LUALIB_API const char *luaL_check_lstr (lua_State *L, int numArg, size_t *len); LUALIB_API const char *luaL_opt_lstr (lua_State *L, int numArg, const char *def, size_t *len); LUALIB_API long luaL_check_number (lua_State *L, int numArg); LUALIB_API long luaL_opt_number (lua_State *L, int numArg, long def); LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg); LUALIB_API void luaL_checktype (lua_State *L, int narg, int t); LUALIB_API void luaL_checkany (lua_State *L, int narg); LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...); LUALIB_API int luaL_findstring (const char *name, const char *const list[]); /* ** =============================================================== ** some useful macros ** =============================================================== */ #define luaL_arg_check(L, cond,numarg,extramsg) if (!(cond)) \ luaL_argerror(L, numarg,extramsg) #define luaL_check_string(L,n) (luaL_check_lstr(L, (n), NULL)) #define luaL_opt_string(L,n,d) (luaL_opt_lstr(L, (n), (d), NULL)) #define luaL_check_int(L,n) ((int)luaL_check_number(L, n)) #define luaL_check_long(L,n) ((long)luaL_check_number(L, n)) #define luaL_opt_int(L,n,d) ((int)luaL_opt_number(L, n,d)) #define luaL_opt_long(L,n,d) ((long)luaL_opt_number(L, n,d)) #define luaL_openl(L,a) luaL_openlib(L, a, (sizeof(a)/sizeof(a[0]))) /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ #ifndef LUAL_BUFFERSIZE #define LUAL_BUFFERSIZE BUFSIZ #endif typedef struct luaL_Buffer { char *p; /* current position in buffer */ int level; lua_State *L; char buffer[LUAL_BUFFERSIZE]; } luaL_Buffer; #define luaL_putchar(B,c) \ ((void)((B)->p < &(B)->buffer[LUAL_BUFFERSIZE] || luaL_prepbuffer(B)), \ (*(B)->p++ = (char)(c))) #define luaL_addsize(B,n) ((B)->p += (n)) LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B); LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B); LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l); LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s); LUALIB_API void luaL_addvalue (luaL_Buffer *B); LUALIB_API void luaL_pushresult (luaL_Buffer *B); /* }====================================================== */ #endif zangband/src/lua/lcode.h0000644000000000000000000000363410250356275014162 0ustar rootroot/* ** $Id: lcode.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #ifndef lcode_h #define lcode_h #include "llex.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" /* ** Marks the end of a patch list. It is an invalid value both as an absolute ** address, and as a list link (would link an element to itself). */ #define NO_JUMP (-1) /* ** grep "ORDER OPR" if you change these enums */ typedef enum BinOpr { OPR_ADD, OPR_SUB, OPR_MULT, OPR_DIV, OPR_POW, OPR_CONCAT, OPR_NE, OPR_EQ, OPR_LT, OPR_LE, OPR_GT, OPR_GE, OPR_AND, OPR_OR, OPR_NOBINOPR } BinOpr; typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_NOUNOPR } UnOpr; enum Mode {iO, iU, iS, iAB}; /* instruction format */ #define VD 100 /* flag for variable delta */ extern const struct OpProperties { char mode; unsigned char push; unsigned char pop; } luaK_opproperties[NUM_OPCODES]; void luaK_error (LexState *ls, const char *msg); int luaK_code0 (FuncState *fs, OpCode o); int luaK_code1 (FuncState *fs, OpCode o, int arg1); int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2); int luaK_jump (FuncState *fs); void luaK_patchlist (FuncState *fs, int list, int target); void luaK_concat (FuncState *fs, int *l1, int l2); void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue); int luaK_getlabel (FuncState *fs); void luaK_deltastack (FuncState *fs, int delta); void luaK_kstr (LexState *ls, int c); void luaK_number (FuncState *fs, Number f); void luaK_adjuststack (FuncState *fs, int n); int luaK_lastisopen (FuncState *fs); void luaK_setcallreturns (FuncState *fs, int nresults); void luaK_tostack (LexState *ls, expdesc *v, int onlyone); void luaK_storevar (LexState *ls, const expdesc *var); void luaK_prefix (LexState *ls, UnOpr op, expdesc *v); void luaK_infix (LexState *ls, BinOpr op, expdesc *v); void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2); #endif zangband/src/lua/ldebug.h0000644000000000000000000000073410250356275014334 0ustar rootroot/* ** $Id: ldebug.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Auxiliary functions from Debug Interface module ** See Copyright Notice in lua.h */ #ifndef ldebug_h #define ldebug_h #include "lstate.h" #include "luadebug.h" void luaG_typeerror (lua_State *L, StkId o, const char *op); void luaG_binerror (lua_State *L, StkId p1, int t, const char *op); int luaG_getline (int *lineinfo, int pc, int refline, int *refi); void luaG_ordererror (lua_State *L, StkId top); #endif zangband/src/lua/ldo.h0000644000000000000000000000154610250356275013652 0ustar rootroot/* ** $Id: ldo.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #ifndef ldo_h #define ldo_h #include "lobject.h" #include "lstate.h" /* ** macro to increment stack top. ** There must be always an empty slot at the L->stack.top */ #define incr_top {if (L->top == L->stack_last) luaD_checkstack(L, 1); L->top++;} void luaD_init (lua_State *L, int stacksize); void luaD_adjusttop (lua_State *L, StkId base, int extra); void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook); void luaD_call (lua_State *L, StkId func, int nResults); void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults); void luaD_checkstack (lua_State *L, int n); void luaD_breakrun (lua_State *L, int errcode); int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud); #endif zangband/src/lua/lfunc.h0000644000000000000000000000103010250356275014167 0ustar rootroot/* ** $Id: lfunc.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #ifndef lfunc_h #define lfunc_h #include "lobject.h" Proto *luaF_newproto (lua_State *L); void luaF_protook (lua_State *L, Proto *f, int pc); Closure *luaF_newclosure (lua_State *L, int nelems); void luaF_freeproto (lua_State *L, Proto *f); void luaF_freeclosure (lua_State *L, Closure *c); const char *luaF_getlocalname (const Proto *func, int local_number, int pc); #endif zangband/src/lua/lgc.h0000644000000000000000000000044310250356275013634 0ustar rootroot/* ** $Id: lgc.h,v 1.2 2002/08/29 19:06:49 rr9 Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ #ifndef lgc_h #define lgc_h #include "lobject.h" void luaC_collect (lua_State *L, int all); void luaC_collectgarbage (lua_State *L); void luaC_checkGC (lua_State *L); #endif zangband/src/lua/llex.h0000644000000000000000000000344610250356275014041 0ustar rootroot/* ** $Id: llex.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #ifndef llex_h #define llex_h #include "lobject.h" #include "lzio.h" #define FIRST_RESERVED 257 /* maximum length of a reserved word (+1 for final 0) */ #define TOKEN_LEN 15 /* * WARNING: if you change the order of this enumeration, * grep "ORDER RESERVED" */ enum RESERVED { /* terminal symbols denoted by reserved words */ TK_AND = FIRST_RESERVED, TK_BREAK, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FOR, TK_FUNCTION, TK_IF, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_NAME, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, TK_STRING, TK_EOS }; /* number of reserved words */ #define NUM_RESERVED ((int)(TK_WHILE-FIRST_RESERVED+1)) typedef union { Number r; TString *ts; } SemInfo; /* semantics information */ typedef struct Token { int token; SemInfo seminfo; } Token; typedef struct LexState { int current; /* current character */ Token t; /* current token */ Token lookahead; /* look ahead token */ struct FuncState *fs; /* `FuncState' is private to the parser */ struct lua_State *L; struct zio *z; /* input stream */ int linenumber; /* input line counter */ int lastline; /* line of last token `consumed' */ TString *source; /* current source name */ } LexState; void luaX_init (lua_State *L); void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source); int luaX_lex (LexState *LS, SemInfo *seminfo); void luaX_checklimit (LexState *ls, int val, int limit, const char *msg); void luaX_syntaxerror (LexState *ls, const char *s, const char *token); void luaX_error (LexState *ls, const char *s, int token); void luaX_token2str (int token, char *s); #endif zangband/src/lua/llimits.h0000644000000000000000000001113310250356275014542 0ustar rootroot/* ** $Id: llimits.h,v 1.2 2002/12/29 19:28:39 sfuerst Exp $ ** Limits, basic types, and some other "installation-dependent" definitions ** See Copyright Notice in lua.h */ #ifndef llimits_h #define llimits_h #include #include /* ** try to find number of bits in an integer */ #ifndef BITS_INT /* avoid overflows in comparison */ #if INT_MAX-20 < 32760 #define BITS_INT 16 #else #if INT_MAX > 2147483640L /* machine has at least 32 bits */ #define BITS_INT 32 #else #error "you must define BITS_INT with number of bits in an integer" #endif #endif #endif /* ** Define the type `number' of Lua ** GREP LUA_NUMBER to change that */ #ifndef LUA_NUM_TYPE #define LUA_NUM_TYPE long #endif typedef LUA_NUM_TYPE Number; /* function to convert a Number to a string */ #define NUMBER_FMT "%ld" /* LUA_NUMBER */ #define lua_number2str(s,n) sprintf((s), NUMBER_FMT, (n)) /* function to convert a string to a Number */ #define lua_str2number(s,p) strtol((s), (p), 10) typedef unsigned long lint32; /* unsigned int with at least 32 bits */ #define MAX_SIZET ((size_t)(~(size_t)0)-2) #define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ /* ** conversion of pointer to int (for hashing only) ** (the shift removes bits that are usually 0 because of alignment) */ #define IntPoint(p) (((unsigned long)(p)) >> 3) #define MINPOWER2 4 /* minimum size for "growing" vectors */ #ifndef DEFAULT_STACK_SIZE #define DEFAULT_STACK_SIZE 1024 #endif /* type to ensure maximum alignment */ union L_Umaxalign { long d; char *s; long l; }; /* ** type for virtual-machine instructions ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) ** For a very small machine, you may change that to 2 bytes (and adjust ** the following limits accordingly) */ typedef unsigned long Instruction; /* ** size and position of opcode arguments. ** For an instruction with 2 bytes, size is 16, and size_b can be 5 ** (accordingly, size_u will be 10, and size_a will be 5) */ #define SIZE_INSTRUCTION 32 #define SIZE_B 9 #define SIZE_OP 6 #define SIZE_U (SIZE_INSTRUCTION-SIZE_OP) #define POS_U SIZE_OP #define POS_B SIZE_OP #define SIZE_A (SIZE_INSTRUCTION-(SIZE_OP+SIZE_B)) #define POS_A (SIZE_OP+SIZE_B) /* ** limits for opcode arguments. ** we use (signed) int to manipulate most arguments, ** so they must fit in BITS_INT-1 bits (-1 for sign) */ #if SIZE_U < BITS_INT-1 #define MAXARG_U ((1<>1) /* `S' is signed */ #else #define MAXARG_U MAX_INT #define MAXARG_S MAX_INT #endif #if SIZE_A < BITS_INT-1 #define MAXARG_A ((1< MAXARG_B #undef MAXSTACK #define MAXSTACK MAXARG_B #endif /* maximum number of local variables */ #ifndef MAXLOCALS #define MAXLOCALS 200 /* arbitrary limit (=MAXSTACK #undef MAXLOCALS #define MAXLOCALS (MAXSTACK-1) #endif /* maximum number of upvalues */ #ifndef MAXUPVALUES #define MAXUPVALUES 32 /* arbitrary limit (<=MAXARG_B) */ #endif #if MAXUPVALUES>MAXARG_B #undef MAXUPVALUES #define MAXUPVALUES MAXARG_B #endif /* special code to fit a LUA_MULTRET inside an argB */ #define MULT_RET 255 /* (<=MAXARG_B) */ #if MULT_RET>MAXARG_B #undef MULT_RET #define MULT_RET MAXARG_B #endif /* maximum number of variables in the left side of an assignment */ #ifndef MAXVARSLH #define MAXVARSLH 100 /* arbitrary limit (=MULT_RET #undef MAXVARSLH #define MAXVARSLH (MULT_RET-1) #endif /* maximum number of parameters in a function */ #ifndef MAXPARAMS #define MAXPARAMS 100 /* arbitrary limit (=MAXLOCALS #undef MAXPARAMS #define MAXPARAMS (MAXLOCALS-1) #endif /* number of list items to accumulate before a SETLIST instruction */ #define LFIELDS_PER_FLUSH 64 #if LFIELDS_PER_FLUSH>(MAXSTACK/4) #undef LFIELDS_PER_FLUSH #define LFIELDS_PER_FLUSH (MAXSTACK/4) #endif /* number of record items to accumulate before a SETMAP instruction */ /* (each item counts 2 elements on the stack: an index and a value) */ #define RFIELDS_PER_FLUSH (LFIELDS_PER_FLUSH/2) /* maximum lookback to find a real constant (for code generation) */ #ifndef LOOKBACKNUMS #define LOOKBACKNUMS 20 /* arbitrary constant */ #endif #endif zangband/src/lua/lmem.h0000644000000000000000000000207410250356275014023 0ustar rootroot/* ** $Id: lmem.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #ifndef lmem_h #define lmem_h #include #include "llimits.h" #include "lua.h" void *luaM_realloc (lua_State *L, void *oldblock, lint32 size); void *luaM_growaux (lua_State *L, void *block, size_t nelems, int inc, size_t size, const char *errormsg, size_t limit); #define luaM_free(L, b) luaM_realloc(L, (b), 0) #define luaM_malloc(L, t) luaM_realloc(L, NULL, (t)) #define luaM_new(L, t) ((t *)luaM_malloc(L, sizeof(t))) #define luaM_newvector(L, n,t) ((t *)luaM_malloc(L, (n)*(lint32)sizeof(t))) #define luaM_growvector(L, v,nelems,inc,t,e,l) \ ((v)=(t *)luaM_growaux(L, v,nelems,inc,sizeof(t),e,l)) #define luaM_reallocvector(L, v,n,t) \ ((v)=(t *)luaM_realloc(L, v,(n)*(lint32)sizeof(t))) #ifdef LUA_DEBUG extern unsigned long memdebug_numblocks; extern unsigned long memdebug_total; extern unsigned long memdebug_maxmem; extern unsigned long memdebug_memlimit; #endif #endif zangband/src/lua/lobject.h0000644000000000000000000001124610250356275014514 0ustar rootroot/* ** $Id: lobject.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ #ifndef lobject_h #define lobject_h #include "llimits.h" #include "lua.h" #ifdef LUA_DEBUG #undef NDEBUG #include #define LUA_INTERNALERROR(s) assert(((void)s,0)) #define LUA_ASSERT(c,s) assert(((void)s,(c))) #else #define LUA_INTERNALERROR(s) /* empty */ #define LUA_ASSERT(c,s) /* empty */ #endif #ifdef LUA_DEBUG /* to avoid warnings, and make sure value is really unused */ #define UNUSED(x) (x=0, (void)(x)) #else #define UNUSED(x) ((void)(x)) /* to avoid warnings */ #endif /* mark for closures active in the stack */ #define LUA_TMARK 6 /* tags for values visible from Lua == first user-created tag */ #define NUM_TAGS 6 /* check whether `t' is a mark */ #define is_T_MARK(t) ((t) == LUA_TMARK) typedef union { struct TString *ts; /* LUA_TSTRING, LUA_TUSERDATA */ struct Closure *cl; /* LUA_TFUNCTION */ struct Hash *a; /* LUA_TTABLE */ struct CallInfo *i; /* LUA_TLMARK */ Number n; /* LUA_TNUMBER */ } Value; /* Macros to access values */ #define ttype(o) ((o)->ttype) #define nvalue(o) ((o)->value.n) #define tsvalue(o) ((o)->value.ts) #define clvalue(o) ((o)->value.cl) #define hvalue(o) ((o)->value.a) #define infovalue(o) ((o)->value.i) #define svalue(o) (tsvalue(o)->str) typedef struct lua_TObject { int ttype; Value value; } TObject; /* ** String headers for string table */ /* ** most `malloc' libraries allocate memory in blocks of 8 bytes. TSPACK ** tries to make sizeof(TString) a multiple of this granularity, to reduce ** waste of space. */ #define TSPACK ((int)sizeof(int)) typedef struct TString { union { struct { /* for strings */ unsigned long hash; int constindex; /* hint to reuse constants */ } s; struct { /* for userdata */ int tag; void *value; } d; } u; size_t len; struct TString *nexthash; /* chain for hash table */ int marked; char str[TSPACK]; /* variable length string!! must be the last field! */ } TString; /* ** Function Prototypes */ typedef struct Proto { Number *knum; /* Number numbers used by the function */ int nknum; /* size of `knum' */ struct TString **kstr; /* strings used by the function */ int nkstr; /* size of `kstr' */ struct Proto **kproto; /* functions defined inside the function */ int nkproto; /* size of `kproto' */ Instruction *code; int ncode; /* size of `code'; when 0 means an incomplete `Proto' */ short numparams; short is_vararg; short maxstacksize; short marked; struct Proto *next; /* debug information */ int *lineinfo; /* map from opcodes to source lines */ int nlineinfo; /* size of `lineinfo' */ int nlocvars; struct LocVar *locvars; /* information about local variables */ int lineDefined; TString *source; } Proto; typedef struct LocVar { TString *varname; int startpc; /* first point where variable is active */ int endpc; /* first point where variable is dead */ } LocVar; /* ** Closures */ typedef struct Closure { union { lua_CFunction c; /* C functions */ struct Proto *l; /* Lua functions */ } f; struct Closure *next; struct Closure *mark; /* marked closures (point to itself when not marked) */ short isC; /* 0 for Lua functions, 1 for C functions */ short nupvalues; TObject upvalue[1]; } Closure; #define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->isC) typedef struct Node { TObject key; TObject val; struct Node *next; /* for chaining */ } Node; typedef struct Hash { Node *node; int htag; int size; Node *firstfree; /* this position is free; all positions after it are full */ struct Hash *next; struct Hash *mark; /* marked tables (point to itself when not marked) */ } Hash; /* unmarked tables and closures are represented by pointing `mark' to ** themselves */ #define ismarked(x) ((x)->mark != (x)) /* ** informations about a call (for debugging) */ typedef struct CallInfo { struct Closure *func; /* function being called */ const Instruction **pc; /* current pc of called function */ int lastpc; /* last pc traced */ int line; /* current line */ int refi; /* current index in `lineinfo' */ } CallInfo; extern const TObject luaO_nilobject; extern const char *const luaO_typenames[]; #define luaO_typename(o) (luaO_typenames[ttype(o)]) lint32 luaO_power2 (lint32 n); char *luaO_openspace (lua_State *L, size_t n); int luaO_equalObj (const TObject *t1, const TObject *t2); int luaO_str2d (const char *s, Number *result); void luaO_verror (lua_State *L, const char *fmt, ...); void luaO_chunkid (char *out, const char *source, int len); #endif zangband/src/lua/lopcodes.h0000644000000000000000000001204310250356275014676 0ustar rootroot/* ** $Id: lopcodes.h,v 1.2 2002/12/29 19:28:39 sfuerst Exp $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lopcodes_h #define lopcodes_h #include "llimits.h" /*=========================================================================== We assume that instructions are unsigned numbers. All instructions have an opcode in the first 6 bits. Moreover, an instruction can have 0, 1, or 2 arguments. Instructions can have the following types: type 0: no arguments type 1: 1 unsigned argument in the higher bits (called `U') type 2: 1 signed argument in the higher bits (`S') type 3: 1st unsigned argument in the higher bits (`A') 2nd unsigned argument in the middle bits (`B') A signed argument is represented in excess K; that is, the number value is the unsigned value minus K. K is exactly the maximum value for that argument (so that -max is represented by 0, and +max is represented by 2*max), which is half the maximum for the corresponding unsigned argument. The size of each argument is defined in `llimits.h'. The usual is an instruction with 32 bits, U arguments with 26 bits (32-6), B arguments with 9 bits, and A arguments with 17 bits (32-6-9). For small installations, the instruction size can be 16, so U has 10 bits, and A and B have 5 bits each. ===========================================================================*/ /* creates a mask with `n' 1 bits at position `p' */ #define MASK1(n,p) ((~((~(Instruction)0)<>POS_U)) #define SETARG_U(i,u) ((i) = (((i)&MASK0(SIZE_U,POS_U)) | \ ((Instruction)(u)<>POS_A)) #define SETARG_A(i,a) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ ((Instruction)(a)<>POS_B) & MASK1(SIZE_B,0))) #define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ ((Instruction)(b)<y)? PC+=s */ OP_JMPGE,/* J y x - (x>=y)? PC+=s */ OP_JMPT,/* J x - (x~=nil)? PC+=s */ OP_JMPF,/* J x - (x==nil)? PC+=s */ OP_JMPONT,/* J x (x~=nil)? x : - (x~=nil)? PC+=s */ OP_JMPONF,/* J x (x==nil)? x : - (x==nil)? PC+=s */ OP_JMP,/* J - - PC+=s */ OP_PUSHNILJMP,/* - - nil PC++; */ OP_FORPREP,/* J */ OP_FORLOOP,/* J */ OP_LFORPREP,/* J */ OP_LFORLOOP,/* J */ OP_CLOSURE/* A B v_b-v_1 closure(KPROTO[a], v_1-v_b) */ } OpCode; #define NUM_OPCODES ((int)OP_CLOSURE+1) #define ISJUMP(o) (OP_JMPNE <= (o) && (o) <= OP_JMP) #endif zangband/src/lua/lparser.h0000644000000000000000000000271410250356275014542 0ustar rootroot/* ** $Id: lparser.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** LL(1) Parser and code generator for Lua ** See Copyright Notice in lua.h */ #ifndef lparser_h #define lparser_h #include "lobject.h" #include "lzio.h" /* ** Expression descriptor */ typedef enum { VGLOBAL, VLOCAL, VINDEXED, VEXP } expkind; typedef struct expdesc { expkind k; union { int index; /* VGLOBAL: `kstr' index of global name; VLOCAL: stack index */ struct { int t; /* patch list of `exit when true' */ int f; /* patch list of `exit when false' */ } l; } u; } expdesc; /* state needed to generate code for a given function */ typedef struct FuncState { Proto *f; /* current function header */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct lua_State *L; /* copy of the Lua state */ int pc; /* next position to code */ int lasttarget; /* `pc' of last `jump target' */ int jlt; /* list of jumps to `lasttarget' */ short stacklevel; /* number of values on activation register */ short nactloc; /* number of active local variables */ short nupvalues; /* number of upvalues */ int lastline; /* line where last `lineinfo' was generated */ struct Breaklabel *bl; /* chain of breakable blocks */ expdesc upvalues[MAXUPVALUES]; /* upvalues */ int actloc[MAXLOCALS]; /* local-variable stack (indices to locvars) */ } FuncState; Proto *luaY_parser (lua_State *L, ZIO *z); #endif zangband/src/lua/lstate.h0000644000000000000000000000347610250356275014374 0ustar rootroot/* ** $Id: lstate.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Global State ** See Copyright Notice in lua.h */ #ifndef lstate_h #define lstate_h #include "lobject.h" #include "lua.h" #include "luadebug.h" typedef TObject *StkId; /* index to stack elements */ /* ** marks for Reference array */ #define NONEXT -1 /* to end the free list */ #define HOLD -2 #define COLLECTED -3 #define LOCK -4 struct Ref { TObject o; int st; /* can be LOCK, HOLD, COLLECTED, or next (for free list) */ }; struct lua_longjmp; /* defined in ldo.c */ struct TM; /* defined in ltm.h */ typedef struct stringtable { int size; lint32 nuse; /* number of elements */ TString **hash; } stringtable; struct lua_State { /* thread-specific state */ StkId top; /* first free slot in the stack */ StkId stack; /* stack base */ StkId stack_last; /* last free slot in the stack */ int stacksize; StkId Cbase; /* base for current C function */ struct lua_longjmp *errorJmp; /* current error recover point */ char *Mbuffer; /* global buffer */ size_t Mbuffsize; /* size of Mbuffer */ /* global state */ Proto *rootproto; /* list of all prototypes */ Closure *rootcl; /* list of all closures */ Hash *roottable; /* list of all tables */ stringtable strt; /* hash table for strings */ stringtable udt; /* hash table for udata */ Hash *gt; /* table for globals */ struct TM *TMtable; /* table for tag methods */ int last_tag; /* last used tag in TMtable */ struct Ref *refArray; /* locked objects */ int refSize; /* size of refArray */ int refFree; /* list of free positions in refArray */ unsigned long GCthreshold; unsigned long nblocks; /* number of `bytes' currently allocated */ lua_Hook callhook; lua_Hook linehook; int allowhooks; }; #endif zangband/src/lua/lstring.h0000644000000000000000000000165510250356275014557 0ustar rootroot/* ** $Id: lstring.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ #ifndef lstring_h #define lstring_h #include "lobject.h" #include "lstate.h" /* ** any TString with mark>=FIXMARK is never collected. ** Marks>=RESERVEDMARK are used to identify reserved words. */ #define FIXMARK 2 #define RESERVEDMARK 3 #define sizestring(l) ((long)sizeof(TString) + \ ((long)(l+1)-TSPACK)*(long)sizeof(char)) void luaS_init (lua_State *L); void luaS_resize (lua_State *L, stringtable *tb, int newsize); TString *luaS_newudata (lua_State *L, size_t s, void *udata); TString *luaS_createudata (lua_State *L, void *udata, int tag); void luaS_freeall (lua_State *L); TString *luaS_newlstr (lua_State *L, const char *str, size_t l); TString *luaS_new (lua_State *L, const char *str); TString *luaS_newfixed (lua_State *L, const char *str); #endif zangband/src/lua/ltable.h0000644000000000000000000000204410250356275014331 0ustar rootroot/* ** $Id: ltable.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ #ifndef ltable_h #define ltable_h #include "lobject.h" #define node(t,i) (&(t)->node[i]) #define key(n) (&(n)->key) #define val(n) (&(n)->val) Hash *luaH_new (lua_State *L, int nhash); void luaH_free (lua_State *L, Hash *t); const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key); const TObject *luaH_getnum (const Hash *t, Number key); const TObject *luaH_getstr (const Hash *t, TString *key); void luaH_remove (Hash *t, TObject *key); TObject *luaH_set (lua_State *L, Hash *t, const TObject *key); Node * luaH_next (lua_State *L, const Hash *t, const TObject *r); TObject *luaH_setint (lua_State *L, Hash *t, int key); void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val); unsigned long luaH_hash (lua_State *L, const TObject *key); const TObject *luaH_getglobal (lua_State *L, const char *name); /* exported only for debugging */ Node *luaH_mainposition (const Hash *t, const TObject *key); #endif zangband/src/lua/ltm.h0000644000000000000000000000203510250356275013662 0ustar rootroot/* ** $Id: ltm.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Tag methods ** See Copyright Notice in lua.h */ #ifndef ltm_h #define ltm_h #include "lobject.h" #include "lstate.h" /* * WARNING: if you change the order of this enumeration, * grep "ORDER TM" */ typedef enum { TM_GETTABLE = 0, TM_SETTABLE, TM_INDEX, TM_GETGLOBAL, TM_SETGLOBAL, TM_ADD, TM_SUB, TM_MUL, TM_DIV, TM_POW, TM_UNM, TM_LT, TM_CONCAT, TM_GC, TM_FUNCTION, TM_N /* number of elements in the enum */ } TMS; struct TM { Closure *method[TM_N]; TString *collected; /* list of garbage-collected udata with this tag */ }; #define luaT_gettm(L,tag,event) (L->TMtable[tag].method[event]) #define luaT_gettmbyObj(L,o,e) (luaT_gettm((L),luaT_tag(o),(e))) #define validtag(t) (NUM_TAGS <= (t) && (t) <= L->last_tag) extern const char *const luaT_eventname[]; void luaT_init (lua_State *L); void luaT_realtag (lua_State *L, int tag); int luaT_tag (const TObject *o); int luaT_validevent (int t, int e); /* used by compatibility module */ #endif zangband/src/lua/lua.h0000644000000000000000000001737610250356275013665 0ustar rootroot/* ** $Id: lua.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lua - An Extensible Extension Language ** TeCGraf: Grupo de Tecnologia em Computacao Grafica, PUC-Rio, Brazil ** e-mail: lua@tecgraf.puc-rio.br ** www: http://www.tecgraf.puc-rio.br/lua/ ** See Copyright Notice at the end of this file */ #ifndef lua_h #define lua_h /* definition of `size_t' */ #include /* mark for all API functions */ #ifndef LUA_API #define LUA_API extern #endif #define LUA_VERSION "Lua 4.0" #define LUA_COPYRIGHT "Copyright (C) 1994-2000 TeCGraf, PUC-Rio" #define LUA_AUTHORS "W. Celes, R. Ierusalimschy & L. H. de Figueiredo" /* name of global variable with error handler */ #define LUA_ERRORMESSAGE "_ERRORMESSAGE" /* pre-defined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) #define LUA_REFREGISTRY 0 /* pre-defined tags */ #define LUA_ANYTAG (-1) #define LUA_NOTAG (-2) /* option for multiple returns in lua_call */ #define LUA_MULTRET (-1) /* minimum stack available for a C function */ #define LUA_MINSTACK 20 /* error codes for lua_do* */ #define LUA_ERRRUN 1 #define LUA_ERRFILE 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRERR 5 typedef struct lua_State lua_State; typedef int (*lua_CFunction) (lua_State *L); /* ** types returned by `lua_type' */ #define LUA_TNONE (-1) #define LUA_TUSERDATA 0 #define LUA_TNIL 1 #define LUA_TNUMBER 2 #define LUA_TSTRING 3 #define LUA_TTABLE 4 #define LUA_TFUNCTION 5 /* ** state manipulation */ LUA_API lua_State *lua_open (int stacksize); LUA_API void lua_close (lua_State *L); /* ** basic stack manipulation */ LUA_API int lua_gettop (lua_State *L); LUA_API void lua_settop (lua_State *L, int index); LUA_API void lua_pushvalue (lua_State *L, int index); LUA_API void lua_remove (lua_State *L, int index); LUA_API void lua_insert (lua_State *L, int index); LUA_API int lua_stackspace (lua_State *L); /* ** access functions (stack -> C) */ LUA_API int lua_type (lua_State *L, int index); LUA_API const char *lua_typename (lua_State *L, int t); LUA_API int lua_isnumber (lua_State *L, int index); LUA_API int lua_isstring (lua_State *L, int index); LUA_API int lua_iscfunction (lua_State *L, int index); LUA_API int lua_tag (lua_State *L, int index); LUA_API int lua_equal (lua_State *L, int index1, int index2); LUA_API int lua_lessthan (lua_State *L, int index1, int index2); LUA_API long lua_tonumber (lua_State *L, int index); LUA_API const char *lua_tostring (lua_State *L, int index); LUA_API size_t lua_strlen (lua_State *L, int index); LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index); LUA_API void *lua_touserdata (lua_State *L, int index); LUA_API const void *lua_topointer (lua_State *L, int index); /* ** push functions (C -> stack) */ LUA_API void lua_pushnil (lua_State *L); LUA_API void lua_pushnumber (lua_State *L, long n); LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len); LUA_API void lua_pushstring (lua_State *L, const char *s); LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n); LUA_API void lua_pushusertag (lua_State *L, void *u, int tag); /* ** get functions (Lua -> stack) */ LUA_API void lua_getglobal (lua_State *L, const char *name); LUA_API void lua_gettable (lua_State *L, int index); LUA_API void lua_rawget (lua_State *L, int index); LUA_API void lua_rawgeti (lua_State *L, int index, int n); LUA_API void lua_getglobals (lua_State *L); LUA_API void lua_gettagmethod (lua_State *L, int tag, const char *event); LUA_API int lua_getref (lua_State *L, int ref); LUA_API void lua_newtable (lua_State *L); /* ** set functions (stack -> Lua) */ LUA_API void lua_setglobal (lua_State *L, const char *name); LUA_API void lua_settable (lua_State *L, int index); LUA_API void lua_rawset (lua_State *L, int index); LUA_API void lua_rawseti (lua_State *L, int index, int n); LUA_API void lua_setglobals (lua_State *L); LUA_API void lua_settagmethod (lua_State *L, int tag, const char *event); LUA_API int lua_ref (lua_State *L, int lock); /* ** "do" functions (run Lua code) */ LUA_API int lua_call (lua_State *L, int nargs, int nresults); LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults); LUA_API int lua_dofile (lua_State *L, const char *filename); LUA_API int lua_dostring (lua_State *L, const char *str); LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name); /* ** Garbage-collection functions */ LUA_API int lua_getgcthreshold (lua_State *L); LUA_API int lua_getgccount (lua_State *L); LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold); /* ** miscellaneous functions */ LUA_API int lua_newtag (lua_State *L); LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom); LUA_API void lua_settag (lua_State *L, int tag); LUA_API void lua_error (lua_State *L, const char *s); LUA_API void lua_unref (lua_State *L, int ref); LUA_API int lua_next (lua_State *L, int index); LUA_API int lua_getn (lua_State *L, int index); LUA_API void lua_concat (lua_State *L, int n); LUA_API void *lua_newuserdata (lua_State *L, size_t size); /* ** =============================================================== ** some useful macros ** =============================================================== */ #define lua_pop(L,n) lua_settop(L, -(n)-1) #define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n)) #define lua_pushuserdata(L,u) lua_pushusertag(L, u, 0) #define lua_pushcfunction(L,f) lua_pushcclosure(L, f, 0) #define lua_clonetag(L,t) lua_copytagmethods(L, lua_newtag(L), (t)) #define lua_isfunction(L,n) (lua_type(L,n) == LUA_TFUNCTION) #define lua_istable(L,n) (lua_type(L,n) == LUA_TTABLE) #define lua_isuserdata(L,n) (lua_type(L,n) == LUA_TUSERDATA) #define lua_isnil(L,n) (lua_type(L,n) == LUA_TNIL) #define lua_isnull(L,n) (lua_type(L,n) == LUA_TNONE) #define lua_getregistry(L) lua_getref(L, LUA_REFREGISTRY) #endif /****************************************************************************** * Copyright (C) 1994-2000 TeCGraf, PUC-Rio. All rights reserved. * * Permission is hereby granted, without written agreement and without license * or royalty fees, to use, copy, modify, and distribute this software and its * documentation for any purpose, including commercial applications, subject to * the following conditions: * * - The above copyright notice and this permission notice shall appear in all * copies or substantial portions of this software. * * - The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be greatly * appreciated (but it is not required). * * - Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * The authors specifically disclaim any warranties, including, but not limited * to, the implied warranties of merchantability and fitness for a particular * purpose. The software provided hereunder is on an "as is" basis, and the * authors have no obligation to provide maintenance, support, updates, * enhancements, or modifications. In no event shall TeCGraf, PUC-Rio, or the * authors be held liable to any party for direct, indirect, special, * incidental, or consequential damages arising out of the use of this software * and its documentation. * * The Lua language and this implementation have been entirely designed and * written by Waldemar Celes Filho, Roberto Ierusalimschy and * Luiz Henrique de Figueiredo at TeCGraf, PUC-Rio. * * This implementation contains no third-party code. ******************************************************************************/ zangband/src/lua/luac.h0000644000000000000000000000114710250356275014015 0ustar rootroot/* ** $Id: luac.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** definitions for luac ** See Copyright Notice in lua.h */ #include "ldebug.h" #include "lfunc.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lstring.h" #include "ltable.h" #include "lundump.h" extern lua_State *lua_state; #define L lua_state /* lazy! */ /* from dump.c */ void luaU_dumpchunk(const Proto* Main, FILE* D); /* from opt.c */ void luaU_optchunk(Proto* Main); /* from print.c */ void luaU_printchunk(const Proto* Main); /* from test.c */ void luaU_testchunk(const Proto* Main); #define Sizeof(x) ((int)sizeof(x)) zangband/src/lua/luadebug.h0000644000000000000000000000244710250356275014665 0ustar rootroot/* ** $Id: luadebug.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Debugging API ** See Copyright Notice in lua.h */ #ifndef luadebug_h #define luadebug_h #include "lua.h" typedef struct lua_Debug lua_Debug; /* activation record */ typedef struct lua_Localvar lua_Localvar; typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func); LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func); #define LUA_IDSIZE 60 struct lua_Debug { const char *event; /* `call', `return' */ int currentline; /* (l) */ const char *name; /* (n) */ const char *namewhat; /* (n) `global', `tag method', `local', `field' */ int nups; /* (u) number of upvalues */ int linedefined; /* (S) */ const char *what; /* (S) `Lua' function, `C' function, Lua `main' */ const char *source; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct lua_TObject *_func; /* active function */ }; #endif zangband/src/lua/lualib.h0000644000000000000000000000123710250356275014341 0ustar rootroot/* ** $Id: lualib.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lua standard libraries ** See Copyright Notice in lua.h */ #ifndef lualib_h #define lualib_h #include "lua.h" #ifndef LUALIB_API #define LUALIB_API extern #endif #define LUA_ALERT "_ALERT" LUALIB_API void lua_baselibopen (lua_State *L); LUALIB_API void lua_iolibopen (lua_State *L); LUALIB_API void lua_strlibopen (lua_State *L); LUALIB_API void lua_mathlibopen (lua_State *L); LUALIB_API void lua_dblibopen (lua_State *L); /* Auxiliary functions (private) */ const char *luaI_classend (lua_State *L, const char *p); int luaI_singlematch (int c, const char *p, const char *ep); #endif zangband/src/lua/lundump.h0000644000000000000000000000162010250356275014551 0ustar rootroot/* ** $Id: lundump.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** load pre-compiled Lua chunks ** See Copyright Notice in lua.h */ #ifndef lundump_h #define lundump_h #include "lobject.h" #include "lzio.h" /* load one chunk */ Proto* luaU_undump (lua_State* L, ZIO* Z); /* find byte order */ int luaU_endianess (void); /* definitions for headers of binary files */ #define VERSION 0x40 /* last format change was in 4.0 */ #define VERSION0 0x40 /* last major change was in 4.0 */ #define ID_CHUNK 27 /* binary files start with ESC... */ #define SIGNATURE "Lua" /* ...followed by this signature */ /* formats for error messages */ #define SOURCE_FMT "<%d:%.99s>" #define SOURCE tf->lineDefined,tf->source->str #define IN_FMT " in %p " SOURCE_FMT #define IN tf,SOURCE /* a multiple of PI for testing native format */ /* multiplying by 1E8 gives non-trivial integer values */ #define TEST_NUMBER 3 #endif zangband/src/lua/lvm.h0000644000000000000000000000173410250356275013671 0ustar rootroot/* ** $Id: lvm.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lvm_h #define lvm_h #include "ldo.h" #include "lobject.h" #include "ltm.h" #define tonumber(o) ((ttype(o) != LUA_TNUMBER) && (luaV_tonumber(o) != 0)) #define tostring(L,o) ((ttype(o) != LUA_TSTRING) && (luaV_tostring(L, o) != 0)) int luaV_tonumber (TObject *obj); int luaV_tostring (lua_State *L, TObject *obj); const TObject *luaV_gettable (lua_State *L, StkId t); void luaV_settable (lua_State *L, StkId t, StkId key); const TObject *luaV_getglobal (lua_State *L, TString *s); void luaV_setglobal (lua_State *L, TString *s); StkId luaV_execute (lua_State *L, const Closure *cl, StkId base); void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems); void luaV_Lclosure (lua_State *L, Proto *l, int nelems); int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top); void luaV_strconc (lua_State *L, int total, StkId top); #endif zangband/src/lua/lzio.h0000644000000000000000000000216010250356275014042 0ustar rootroot/* ** $Id: lzio.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ #ifndef lzio_h #define lzio_h #include /* For Lua only */ #define zFopen luaZ_Fopen #define zsopen luaZ_sopen #define zmopen luaZ_mopen #define zread luaZ_read #define EOZ (-1) /* end of stream */ typedef struct zio ZIO; ZIO* zFopen (ZIO* z, FILE* f, const char *name); /* open FILEs */ ZIO* zsopen (ZIO* z, const char* s, const char *name); /* string */ ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name); /* memory */ size_t zread (ZIO* z, void* b, size_t n); /* read next n bytes */ #define zgetc(z) (((z)->n--)>0 ? ((int)*(z)->p++): (z)->filbuf(z)) #define zungetc(z) (++(z)->n,--(z)->p) #define zname(z) ((z)->name) /* --------- Private Part ------------------ */ #ifndef ZBSIZE #define ZBSIZE 256 /* buffer size */ #endif struct zio { size_t n; /* bytes still unread */ const unsigned char* p; /* current position in buffer */ int (*filbuf)(ZIO* z); void* u; /* additional data */ const char *name; unsigned char buffer[ZBSIZE]; /* buffer */ }; #endif zangband/src/lua/print.h0000644000000000000000000000466710250356275014237 0ustar rootroot/* ** $Id: print.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ ** extracted automatically from lopcodes.h by mkprint.lua -- DO NOT EDIT ** See Copyright Notice in lua.h */ case OP_END: P_OP("END"); P_NONE; break; case OP_RETURN: P_OP("RETURN"); P_U; break; case OP_CALL: P_OP("CALL"); P_AB; break; case OP_TAILCALL: P_OP("TAILCALL"); P_AB; break; case OP_PUSHNIL: P_OP("PUSHNIL"); P_U; break; case OP_POP: P_OP("POP"); P_U; break; case OP_PUSHINT: P_OP("PUSHINT"); P_S; break; case OP_PUSHSTRING: P_OP("PUSHSTRING"); P_Q; break; case OP_PUSHNUM: P_OP("PUSHNUM"); P_N; break; case OP_PUSHNEGNUM: P_OP("PUSHNEGNUM"); P_N; break; case OP_PUSHUPVALUE: P_OP("PUSHUPVALUE"); P_U; break; case OP_GETLOCAL: P_OP("GETLOCAL"); P_L; break; case OP_GETGLOBAL: P_OP("GETGLOBAL"); P_K; break; case OP_GETTABLE: P_OP("GETTABLE"); P_NONE; break; case OP_GETDOTTED: P_OP("GETDOTTED"); P_K; break; case OP_GETINDEXED: P_OP("GETINDEXED"); P_L; break; case OP_PUSHSELF: P_OP("PUSHSELF"); P_K; break; case OP_CREATETABLE: P_OP("CREATETABLE"); P_U; break; case OP_SETLOCAL: P_OP("SETLOCAL"); P_L; break; case OP_SETGLOBAL: P_OP("SETGLOBAL"); P_K; break; case OP_SETTABLE: P_OP("SETTABLE"); P_AB; break; case OP_SETLIST: P_OP("SETLIST"); P_AB; break; case OP_SETMAP: P_OP("SETMAP"); P_U; break; case OP_ADD: P_OP("ADD"); P_NONE; break; case OP_ADDI: P_OP("ADDI"); P_S; break; case OP_SUB: P_OP("SUB"); P_NONE; break; case OP_MULT: P_OP("MULT"); P_NONE; break; case OP_DIV: P_OP("DIV"); P_NONE; break; case OP_POW: P_OP("POW"); P_NONE; break; case OP_CONCAT: P_OP("CONCAT"); P_U; break; case OP_MINUS: P_OP("MINUS"); P_NONE; break; case OP_NOT: P_OP("NOT"); P_NONE; break; case OP_JMPNE: P_OP("JMPNE"); P_J; break; case OP_JMPEQ: P_OP("JMPEQ"); P_J; break; case OP_JMPLT: P_OP("JMPLT"); P_J; break; case OP_JMPLE: P_OP("JMPLE"); P_J; break; case OP_JMPGT: P_OP("JMPGT"); P_J; break; case OP_JMPGE: P_OP("JMPGE"); P_J; break; case OP_JMPT: P_OP("JMPT"); P_J; break; case OP_JMPF: P_OP("JMPF"); P_J; break; case OP_JMPONT: P_OP("JMPONT"); P_J; break; case OP_JMPONF: P_OP("JMPONF"); P_J; break; case OP_JMP: P_OP("JMP"); P_J; break; case OP_PUSHNILJMP: P_OP("PUSHNILJMP"); P_NONE; break; case OP_FORPREP: P_OP("FORPREP"); P_J; break; case OP_FORLOOP: P_OP("FORLOOP"); P_J; break; case OP_LFORPREP: P_OP("LFORPREP"); P_J; break; case OP_LFORLOOP: P_OP("LFORLOOP"); P_J; break; case OP_CLOSURE: P_OP("CLOSURE"); P_F; break; zangband/src/lua/tolua.h0000644000000000000000000001257610250356275014225 0ustar rootroot/* tolua - Support code for Lua bindings. ** Written by Waldemar Celes (celes@tecgraf.puc-rio.br) ** TeCGraf/PUC-Rio ** http://www.tecgraf.puc-rio.br/~celes/tolua ** Jul 1998 ** $Id: tolua.h,v 1.6 2003/12/21 11:05:40 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #ifndef tolua_h #define tolua_h #define TOLUA_VERSION "tolua 4.0a - angband" #include /* NULL, malloc, free */ #ifdef __cplusplus extern "C" { #endif #include "lua.h" /*************************************** Exported functions */ int tolua_open (lua_State* L); void tolua_using (lua_State* L, int module); void tolua_class (lua_State* L, int derived, int base); void tolua_instance (lua_State* L, int instance, int classobj); void tolua_foreach (lua_State* L, int lo, int f); int tolua_tag (lua_State* L, const char* type); const char* tolua_type (lua_State* L, int lo); int tolua_base (lua_State* L, int lo); int tolua_cast (lua_State* L, int lo, const char* type); void tolua_takeownership (lua_State* L, int lo); /*************************************** Support functions for binding code */ #define LUA_VALUE int #define LUA_NIL 0 /* TODO */ /*#define TOLUA_NIL (lua_pushnil(),lua_pop())*/ /* Register functions */ void tolua_globalvar (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set); void tolua_globalarray (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set); void tolua_module (lua_State* L, const char* name); void tolua_cclass (lua_State* L, const char* name, const char* base); void tolua_function (lua_State* L, const char* parent, const char* name, lua_CFunction func); void tolua_constant (lua_State* L, const char* parent, const char* name, long value); void tolua_tablevar (lua_State* L, const char* table, const char* name, lua_CFunction get, lua_CFunction set); void tolua_tablearray (lua_State* L, const char* table, const char* name, lua_CFunction get, lua_CFunction set); /* Get and push functions */ long tolua_getnumber (lua_State* L, int narg, long def); const char* tolua_getstring (lua_State* L, int narg, const char* def); void* tolua_getuserdata (lua_State* L, int narg, void* def); void* tolua_getusertype (lua_State* L, int narg, void* def); int tolua_getvalue (lua_State* L, int narg, int def); int tolua_getbool (lua_State* L, int narg, int def); long tolua_getfieldnumber (lua_State* L, int lo, int index, long def); const char* tolua_getfieldstring (lua_State* L, int lo, int index, const char* def); void* tolua_getfielduserdata (lua_State* L, int lo, int index, void* def); void* tolua_getfieldusertype (lua_State* L, int lo, int index, void* def); int tolua_getfieldvalue (lua_State* L, int lo, int index, int def); int tolua_getfieldbool (lua_State* L, int lo, int index, int def); void tolua_pushnumber (lua_State* L, long value); void tolua_pushstring (lua_State* L, const char* value); void tolua_pushuserdata (lua_State* L, void* value); void tolua_pushusertype (lua_State* L, void* value, int tag); void tolua_pushvalue (lua_State* L, int lo); void tolua_pushbool (lua_State* L, int value); void tolua_pushfieldnumber (lua_State* L, int lo, int index, long v); void tolua_pushfieldstring (lua_State* L, int lo, int index, char* v); void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v); void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, int tag); void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v); void tolua_pushfieldbool (lua_State* L, int lo, int index, int v); /* Type & tag manipulation */ void tolua_usertype (lua_State* L, const char* type); #if 0 void tolua_settag (lua_State* L, char* type, int* tag); #endif int tolua_istype (lua_State* L, int narg, int tag, int dim); int tolua_arrayistype (lua_State* L, int narg, int tag, int dim, int def); int tolua_isnoobj (lua_State* L, int narg); /* Tag method manipulation */ void* tolua_doclone (lua_State* L, void* value, int tag); void* tolua_copy (lua_State* L, void* value, unsigned int size); /* Error handling */ void tolua_error (lua_State* L, const char* msg); /* Exported variables */ extern int tolua_tag_nil; extern int tolua_tag_number; extern int tolua_tag_string; extern int tolua_tag_userdata; extern int tolua_tag_table; extern int tolua_tag_function; /* Extra defines */ #define TOLUA_GET_SELF(C) \ C* self = (C *) tolua_getusertype(tolua_S,1,0); \ if (!self) tolua_error(tolua_S,"invalid 'self'") #define TOLUA_ARRAY_SELF(C) \ C* self; \ lua_pushstring(tolua_S,".self"); \ lua_rawget(tolua_S,1); \ self = (C *) lua_touserdata(tolua_S,-1) #define TOLUA_ARRAY_INDEX(S,D) \ if (!tolua_istype(tolua_S,2,LUA_TNUMBER,0)) \ tolua_error(tolua_S,"invalid type in array indexing."); \ toluaI_index = (int)tolua_getnumber(tolua_S,2,0); \ if (toluaI_index<0 || toluaI_index>=D) \ tolua_error(tolua_S,"array:" S " indexing out of range."); #define TOLUA_ERR_ASSIGN tolua_error(tolua_S,"#vinvalid type in variable assignment.") #define TOLUA_ERR_FN(F) tolua_error(tolua_S,"#ferror in function" #F); return 0; #define TOLUA_DEF(N) tolua_constant(tolua_S,NULL, #N, N) #define TOLUA_FUN(L,N) tolua_function(tolua_S,NULL, #L, N) #define TOLUA_UNDEF(L) lua_pushnil(tolua_S); lua_setglobal(tolua_S,#L) #ifdef __cplusplus } #endif #endif zangband/src/lua/tolua_eh.h0000644000000000000000000000106710250356275014672 0ustar rootroot/* tolua: error handling ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua_eh.h,v 1.1 2001/10/29 17:49:53 rr9 Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #ifndef tolua_eh_h #define tolua_eh_h void toluaI_eh_set (lua_State* L, int narg, const char* provided, const char* expected); #endif zangband/src/lua/tolua_rg.h0000644000000000000000000000113010250356275014675 0ustar rootroot/* tolua: register functions ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Nov 200 ** $Id: tolua_rg.h,v 1.2 2002/04/01 11:54:31 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #ifndef tolua_rg_h #define tolua_rg_h void toluaI_setregistry (lua_State* L, const char* field); void toluaI_getregistry (lua_State* L, const char* field); #endif zangband/src/lua/tolua_tm.h0000644000000000000000000000172210250356275014714 0ustar rootroot/* tolua: tag methods ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua_tm.h,v 1.2 2002/04/02 23:17:42 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #ifndef tolua_tm_h #define tolua_tm_h void toluaI_tm_init (lua_State* L); void toluaI_tm_global (lua_State* L, int lo); void toluaI_tm_module (lua_State* L, int lo); void toluaI_tm_class (lua_State* L, int lo, const char* name); void toluaI_tm_instance (lua_State* L, int tag, int lo); void toluaI_tm_linstance (lua_State* L, int tag, int lo); void toluaI_tm_using (lua_State* L, int module); void toluaI_tm_setclass (lua_State* L, int lo); void toluaI_tm_pushmate (lua_State* L, int lo); void toluaI_tm_pushclass (lua_State* L, int lo); #endif zangband/src/lua/tolua_tt.h0000644000000000000000000000171010250356275014720 0ustar rootroot/* tolua: type & tag manipulation. ** Support code for Lua bindings. ** Written by Waldemar Celes ** TeCGraf/PUC-Rio ** Jul 1998 ** $Id: tolua_tt.h,v 1.3 2002/04/02 23:17:42 sfuerst Exp $ */ /* This code is free software; you can redistribute it and/or modify it. ** The software provided hereunder is on an "as is" basis, and ** the author has no obligation to provide maintenance, support, updates, ** enhancements, or modifications. */ #ifndef tolua_tt_h #define tolua_tt_h void toluaI_tt_init (lua_State* L); void toluaI_tt_register (lua_State* L, int tag, const char* type); void toluaI_tt_class (lua_State* L, int lo, const char* derived, const char* base); void toluaI_tt_sethierarchy (lua_State* L, int tag, int btag); int toluaI_tt_isusertype (lua_State* L, int lo); int toluaI_tt_gettag (lua_State* L, const char* type); const char* toluaI_tt_getobjtype (lua_State* L, int lo); char* toluaI_tt_concat (const char* s1, const char* s2); #endif zangband/src/lua/array.lua0000644000000000000000000001211010250356275014531 0ustar rootroot-- tolua: array class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1999 -- $Id: array.lua,v 1.4 2003/12/04 17:23:54 sfuerst Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Array class -- Represents a extern array variable or a public member of a class. -- Stores all fields present in a declaration. classArray = { _base = classDeclaration, } settag(classArray,tolua_tag) -- Print method function classArray:print (ident,close) print(ident.."Array{") print(ident.." mod = '"..self.mod.."',") print(ident.." type = '"..self.type.."',") print(ident.." ptr = '"..self.ptr.."',") print(ident.." name = '"..self.name.."',") print(ident.." def = '"..self.def.."',") print(ident.." dim = '"..self.dim.."',") print(ident.." ret = '"..self.ret.."',") print(ident.."}"..close) end -- get variable value function classArray:getvalue (class,static) if class and static then return class..'::'..self.name..'[toluaI_index]' elseif class then return 'self->'..self.name..'[toluaI_index]' else return self.name..'[toluaI_index]' end end -- Write binding functions function classArray:supcode () local class = self:inclass() -- get function ------------------------------------------------ if class then output("/* get function:",self.name," of class ",class," */") else output("/* get function:",self.name," */") end self.cgetname = self:cfuncname("toluaI_get") output("static int",self.cgetname,"(lua_State* tolua_S)") output("{") -- declare index output(' int toluaI_index;') -- declare self, if the case local _,_,static = strfind(self.mod,'^%s*(static)') if class and static==nil then output(' TOLUA_ARRAY_SELF(',class,');') elseif static then _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)') end -- check index if class then output(' TOLUA_ARRAY_INDEX("',class,':',self.name,'",',self.dim,');') else output(' TOLUA_ARRAY_INDEX("',self.name,'",',self.dim,');') end -- return value local t,ct = isbasic(self.type) if t then output(' tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static)..');') else if self.ptr == '&' or self.ptr == '' then output(' tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static)..',',self.tag,');') else output(' tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static)..',',self.tag,');') end end output(' return 1;') output('}') output('\n') -- set function ------------------------------------------------ if not strfind(self.mod,'const') then if class then output("/* set function:",self.name," of class ",class," */") else output("/* set function:",self.name," */") end self.csetname = self:cfuncname("toluaI_set") output("static int",self.csetname,"(lua_State* tolua_S)") output("{") -- declare index output(' int toluaI_index;') -- declare self, if the case local _,_,static = strfind(self.mod,'^%s*(static)') if class and static==nil then output(' TOLUA_ARRAY_SELF(',class,');') elseif static then _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)') end -- check index if class then output(' TOLUA_ARRAY_INDEX("',class,':',self.name,'",',self.dim,');') else output(' TOLUA_ARRAY_INDEX("',self.name,'",',self.dim,');') end -- assign value local ptr = '' if self.ptr~='' then ptr = '*' end output(' ') if class and static then output(class..'::'..self.name..'[toluaI_index]') elseif class then output('self->'..self.name..'[toluaI_index]') else output(self.name..'[toluaI_index]') end local t = isbasic(self.type) output(' = ') if not t and ptr=='' then output('*') end output('((',self.mod,self.type) if not t then output('*') end output(') ') local def = 0 if self.def ~= '' then def = self.def end if t then output('tolua_get'..t,'(tolua_S,3,',def,'));') else output('tolua_getusertype(tolua_S,3,',def,'));') end output(' return 0;') output('}') output('\n') end end function classArray:register () local parent = self:inclass() or self:inmodule() if parent then if self.csetname then output(' tolua_tablearray(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..','..self.csetname..');') else output(' tolua_tablearray(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..',NULL);') end else if self.csetname then output(' tolua_globalarray(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');') else output(' tolua_globalarray(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);') end end end function classArray:unregister () if self:inclass()==nil and self:inmodule()==nil then output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");') end end -- Internal constructor function _Array (t) t._base = classArray settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects a string representing the variable declaration. function Array (s) return _Array (Declaration(s,'var')) end zangband/src/lua/basic.lua0000644000000000000000000000761310250356275014510 0ustar rootroot-- tolua: basic utility functions -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: basic.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Basic C types and their corresponding Lua types -- All occurrences of "char*" will be replaced by "_cstring", -- and all occurrences of "void*" will be replaced by "_userdata" _basic = { ['void'] = '', ['char'] = 'number', ['int'] = 'number', ['short'] = 'number', ['long'] = 'number', ['_cstring'] = 'string', ['_userdata'] = 'userdata', ['char*'] = 'string', ['void*'] = 'userdata', ['bool'] = 'bool', ['LUA_VALUE'] = 'value', ['byte'] = 'number', ['s16b'] = 'number', ['u16b'] = 'number', ['s32b'] = 'number', ['u32b'] = 'number', } _basic_tag = { ['void'] = '', ['char'] = 'LUA_TNUMBER', ['int'] = 'LUA_TNUMBER', ['short'] = 'LUA_TNUMBER', ['long'] = 'LUA_TNUMBER', ['_cstring'] = 'LUA_TSTRING', ['_userdata'] = 'LUA_TUSERDATA', ['char*'] = 'LUA_TSTRING', ['void*'] = 'LUA_TUSERDATA', ['bool'] = 'tolua_tag(tolua_S,"bool")', ['byte'] = 'LUA_TNUMBER', ['s16b'] = 'LUA_TNUMBER', ['u16b'] = 'LUA_TNUMBER', ['s32b'] = 'LUA_TNUMBER', ['u32b'] = 'LUA_TNUMBER', } _basic_ctype = { number = "long", string = "const char*", userdata = "void*", bool = "int", } -- List of user defined types -- Each type corresponds to a variable name that stores its tag value. _usertype = {} -- Tag method to provide inheritance function tolua_index (t,f) if f == '_base' then -- to avoid loop return tolua_old_index(t,f) else return t._base[f] end end tolua_tag = newtag() tolua_old_index = settagmethod(tolua_tag,"index",tolua_index) -- Error handler function tolua_error (s) local out = _OUTPUT _OUTPUT = _STDERR if strsub(s,1,1) == '#' then write("\n** tolua: "..strsub(s,2)..".\n\n") else write("\n** tolua internal error: "..s..".\n\n") return end if _curr_code then local _,_,s = strfind(_curr_code,"^%s*(.-\n)") -- extract first line if s==nil then s = _curr_code end s = gsub(s,"_userdata","void*") -- return with 'void*' s = gsub(s,"_cstring","char*") -- return with 'char*' write("Code being processed:\n"..s.."\n") end _OUTPUT = out end _ERRORMESSAGE = tolua_error -- register an user defined type function regtype (t) if not istype(t) then _usertype[t] = t end return t end -- return tag name function tagvar(type,const) if type == '' or type == 'void' then return type,0 else local m,t = findtypedef(type) if isbasic(t) then return t, _basic_tag[t] end if strfind(m,'const') then const = 'const' end regtype(t) if const and const ~= '' then t = 'const '..t end return t,'tolua_tag(tolua_S,"'..t..'")' end end -- check if basic type function isbasic (type) local m,t = findtypedef(type) local b = _basic[t] if b then return b,_basic_ctype[b] end return nil end -- check if type function istype (t) return _basic[t] or _usertype[t] or istypedef(t) end -- split string using a token function split (s,t) local l = {n=0} local f = function (s) %l.n = %l.n + 1 %l[%l.n] = s end local p = "%s*(.-)%s*"..t.."%s*" s = gsub(s,"^%s+","") s = gsub(s,"%s+$","") s = gsub(s,p,f) l.n = l.n + 1 l[l.n] = gsub(s,"(%s%s*)$","") return l end -- concatenate strings of a table function concat (t,f,l) local s = '' local i=f while i<=l do s = s..t[i] i = i+1 if i <= l then s = s..' ' end end return s end -- output line function output (...) local i=1 while i<=arg.n do if _cont and not strfind(_cont,'[%(,"]') and strfind(arg[i],"^[%a_~]") then write(' ') end write(arg[i]) if arg[i] ~= '' then _cont = strsub(arg[i],-1,-1) end i = i+1 end if strfind(arg[arg.n],"[%/%)%;%{%}]$") then _cont=nil write('\n') end end zangband/src/lua/class.lua0000644000000000000000000000342610250356275014532 0ustar rootroot-- tolua: class class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: class.lua,v 1.2 2003/12/04 17:46:14 sfuerst Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Class class -- Represents a class definition. -- Stores the following fields: -- name = class name -- base = class base, if any (only single inheritance is supported) -- {i} = list of members classClass = { _base = classContainer, type = 'class', name = '', base = '', } settag(classClass,tolua_tag) -- register class function classClass:register () output(' tolua_cclass(tolua_S,"'..self.name..'","'..self.base..'");') local i=1 while self[i] do self[i]:register() i = i+1 end end -- unregister class function classClass:unregister () output(' TOLUA_UNDEF('..self.name..');') end -- output tags function classClass:decltag () self.itype,self.tag = tagvar(self.name); self.citype,self.ctag = tagvar(self.name,'const'); local i=1 while self[i] do self[i]:decltag() i = i+1 end end -- Print method function classClass:print (ident,close) print(ident.."Class{") print(ident.." name = '"..self.name.."',") print(ident.." base = '"..self.base.."';") local i=1 while self[i] do self[i]:print(ident.." ",",") i = i+1 end print(ident.."}"..close) end -- Internal constructor function _Class (t) t._base = classClass settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects the name, the base and the body of the class. function Class (n,p,b) local c = _Class(_Container{name=n, base=p}) push(c) c:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces pop() end zangband/src/lua/clean.lua0000644000000000000000000000237310250356275014507 0ustar rootroot-- mark up comments and strings STR1 = "\001" STR2 = "\002" STR3 = "\003" STR4 = "\004" REM = "\005" ANY = "([\001-\005])" ESC1 = "\006" ESC2 = "\007" MASK = { -- the substitution order is important {ESC1, "\\'"}, {ESC2, '\\"'}, {STR1, "'"}, {STR2, '"'}, {STR3, "%[%["}, {STR4, "%]%]"}, {REM , "%-%-"}, } function mask (s) for i = 1,getn(MASK) do s = gsub(s,MASK[i][2],MASK[i][1]) end return s end function unmask (s) for i = 1,getn(MASK) do s = gsub(s,MASK[i][1],MASK[i][2]) end return s end function clean (s) -- check for compilation error local code = "return function () " .. s .. " end" if not dostring(code) then return nil end local S = "" -- saved string s = mask(s) -- remove blanks and comments while 1 do local b,e,d = strfind(s,ANY) if b then S = S..strsub(s,1,b-1) s = strsub(s,b+1) if d==STR1 or d==STR2 then e = strfind(s,d) S = S ..d..strsub(s,1,e) s = strsub(s,e+1) elseif d==STR3 then e = strfind(s,STR4) S = S..d..strsub(s,1,e) s = strsub(s,e+1) elseif d==REM then s = gsub(s,"[^\n]*(\n?)","%1",1) end else S = S..s break end end -- eliminate unecessary spaces S = gsub(S,"[ \t]+"," ") S = gsub(S,"[ \t]*\n[ \t]*","\n") S = unmask(S) return S end zangband/src/lua/code.lua0000644000000000000000000000330710250356275014335 0ustar rootroot-- tolua: code class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1999 -- $Id: code.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Code class -- Represents Lua code to be compiled and included -- in the initialization function. -- The following fields are stored: -- text = text code classCode = { text = '', _base = classFeature, } settag(classCode,tolua_tag) -- register code function classCode:register () -- clean Lua code local s = clean(self.text) if not s then error("parser error in embedded code") end -- convert to C output('\n { /* begin embedded lua code */\n') output(' static unsigned char B[] = {\n ') local t={n=0} local b = gsub(s,'(.)',function (c) local e = '' %t.n=%t.n+1 if %t.n==15 then %t.n=0 e='\n ' end return format('%3u,%s',strbyte(c),e) end ) output(b..strbyte(" ")) output('\n };\n') output(' lua_dobuffer(tolua_S,(char*)B,sizeof(B),"tolua: embedded Lua code");') output(' } /* end of embedded lua code */\n\n') end -- Print method function classCode:print (ident,close) print(ident.."Code{") print(ident.." text = [["..self.text.."]],") print(ident.."}"..close) end -- Internal constructor function _Code (t) t._base = classCode settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects a string representing the code text function Code (l) return _Code { text = l } end zangband/src/lua/container.lua0000644000000000000000000001413210250356275015403 0ustar rootroot-- tolua: container abstract class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: container.lua,v 1.2 2001/11/08 18:41:02 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Container class -- Represents a container of features to be bound -- to lua. classContainer = { curr = nil, _base = classFeature, } settag(classContainer,tolua_tag) -- output tags function classContainer:decltag () push(self) local i=1 while self[i] do self[i]:decltag() i = i+1 end pop() end -- write support code function classContainer:supcode () push(self) local i=1 while self[i] do self[i]:supcode() i = i+1 end pop() end -- Internal container constructor function _Container (self) self._base = classContainer settag(self,tolua_tag) self.n = 0 self.typedefs = {n=0} self.lnames = {} return self end -- push container function push (t) classContainer.curr = t end -- pop container function pop () classContainer.curr = classContainer.curr.parent end -- append to current container function append (t) return classContainer.curr:append(t) end -- append typedef to current container function appendtypedef (t) return classContainer.curr:appendtypedef(t) end -- substitute typedef function findtypedef (type) return classContainer.curr:findtypedef(type) end -- check if is typedef function istypedef (type) return classContainer.curr:istypedef(type) end -- append feature to container function classContainer:append (t) self.n = self.n + 1 self[self.n] = t t.parent = self end -- append typedef function classContainer:appendtypedef (t) self.typedefs.n = self.typedefs.n + 1 self.typedefs[self.typedefs.n] = t end -- determine lua function name overload function classContainer:overload (lname) if not self.lnames[lname] then self.lnames[lname] = 0 else self.lnames[lname] = self.lnames[lname] + 1 end return format("%02d",self.lnames[lname]) end function classContainer:findtypedef (type) local env = self while env do if env.typedefs then local i=1 while env.typedefs[i] do if env.typedefs[i].utype == type then local mod1,type1 = env.typedefs[i].mod,env.typedefs[i].type local mod2,type2 = findtypedef(type1) return mod2..' '..mod1,type2 end i = i+1 end end env = env.parent end return '',type end function classContainer:istypedef (type) local env = self while env do if env.typedefs then local i=1 while env.typedefs[i] do if env.typedefs[i].utype == type then return 1 end i = i+1 end end env = env.parent end return nil end -- parse chunk function classContainer:doparse (s) -- try module do local b,e,name,body = strfind(s,"^%s*module%s%s*([_%w][_%w]*)%s*(%b{})%s*") if b then _curr_code = strsub(s,b,e) Module(name,body) return strsub(s,e+1) end end -- try define do local b,e,name = strfind(s,"^%s*#define%s%s*([^%s]*)[^\n]*\n%s*") if b then _curr_code = strsub(s,b,e) Define(name) return strsub(s,e+1) end end -- try enumerates do local b,e,body = strfind(s,"^%s*enum[^{]*(%b{})%s*;?%s*") if b then _curr_code = strsub(s,b,e) Enumerate(body) return strsub(s,e+1) end end do local b,e,body,name = strfind(s,"^%s*typedef%s%s*enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*") if b then _curr_code = strsub(s,b,e) Enumerate(body) Typedef("int "..name) return strsub(s,e+1) end end -- try operator do local b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&]*operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)%s*;%s*") if b then _curr_code = strsub(s,b,e) Operator(decl,kind,arg,const) return strsub(s,e+1) end end -- try function do local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*") if not b then -- try a single letter function name b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*") end if b then _curr_code = strsub(s,b,e) Function(decl,arg,const) return strsub(s,e+1) end end -- try inline function do local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*%b{}%s*") if not b then -- try a single letter function name b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*%b{}%s*") end if b then _curr_code = strsub(s,b,e) Function(decl,arg,const) return strsub(s,e+1) end end -- try class do local b,e,name,base,body = strfind(s,"^%s*class%s*([_%w][_%w]*)%s*(.-)%s*(%b{})%s*;%s*") if not b then b,e,name,base,body = strfind(s,"^%s*struct%s*([_%w][_%w]*)%s*(.-)%s*(%b{})%s*;%s*") if not b then base = '' b,e,body,name = strfind(s,"^%s*typedef%s%s*struct%s%s*[_%w]*%s*(%b{})%s*([_%w][_%w]*)%s*;%s*") end end if b then if base ~= '' then local b,e b,e,base = strfind(base,".-([_%w][_%w]*)$") end _curr_code = strsub(s,b,e) Class(name,base,body) return strsub(s,e+1) end end -- try typedef do local b,e,types = strfind(s,"^%s*typedef%s%s*(.-)%s*;%s*") if b then _curr_code = strsub(s,b,e) Typedef(types) return strsub(s,e+1) end end -- try variable do local b,e,decl = strfind(s,"^%s*([_%w][_@%s%w%d%*&]*[_%w%d])%s*;%s*") if b then _curr_code = strsub(s,b,e) Variable(decl) return strsub(s,e+1) end end -- try array do local b,e,decl = strfind(s,"^%s*([_%w][][_@%s%w%d%*&%-%>]*[]_%w%d])%s*;%s*") if b then _curr_code = strsub(s,b,e) Array(decl) return strsub(s,e+1) end end -- try code do local b,e,code = strfind(s,"^%s*(%b\1\2)") if b then Code(strsub(code,2,-2)) return strsub(s,e+1) end end -- try verbatim do local b,e,line = strfind(s,"^%s*%$(.-\n)") if b then Verbatim(line) return strsub(s,e+1) end end -- no matching if gsub(s,"%s%s*","") ~= "" then _curr_code = s error("#parse error") else return "" end end function classContainer:parse (s) while s ~= '' do s = self:doparse(s) end end zangband/src/lua/declaration.lua0000644000000000000000000002246010250356275015711 0ustar rootroot-- tolua: declaration class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: declaration.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Declaration class -- Represents variable, function, or argument declaration. -- Stores the following fields: -- mod = type modifiers -- type = type -- ptr = "*" or "&", if representing a pointer or a reference -- name = name -- dim = dimension, if a vector -- def = default value, if any (only for arguments) -- ret = "*" or "&", if value is to be returned (only for arguments) classDeclaration = { _base = classFeature, mod = '', type = '', ptr = '', name = '', dim = '', ret = '', def = '' } settag(classDeclaration,tolua_tag) -- Create an unique variable name function create_varname () if not _varnumber then _varnumber = 0 end _varnumber = _varnumber + 1 return "tolua_var_".._varnumber end -- Check declaration name -- It also identifies default values function classDeclaration:checkname () if strsub(self.name,1,1) == '[' and not istype(self.type) then self.name = self.type..self.name local m = split(self.mod,'%s%s*') self.type = m[m.n] self.mod = concat(m,1,m.n-1) end local t = split(self.name,'=') if t.n==2 then self.name = t[1] self.def = t[t.n] end local b,e,d = strfind(self.name,"%[(.-)%]") if b then self.name = strsub(self.name,1,b-1) self.dim = d end if self.type ~= '' and self.type ~= 'void' and self.name == '' then self.name = create_varname() elseif self.kind=='var' then if self.type=='' and self.name~='' then self.type = self.type..self.name self.name = create_varname() elseif istype(self.name) then if self.type=='' then self.type = self.name else self.type = self.type..' '..self.name end self.name = create_varname() end end end -- Check declaration type -- Substitutes typedef's. function classDeclaration:checktype () -- check if there is a pointer to basic type if isbasic(self.type) and self.ptr~='' then self.ret = self.ptr self.ptr = nil end -- check if there is array to be returned if self.dim~='' and self.ret~='' then error('#invalid parameter: cannot return an array of values') end -- register type if self.type~='' then regtype(self.type) end -- restore 'void*' and 'string*' if self.type == '_userdata' then self.type = 'void*' elseif self.type == '_cstring' then self.type = 'char*' end -- -- -- if returning value, automatically set default value -- if self.ret ~= '' and self.def == '' then -- self.def = '0' -- end -- end -- Print method function classDeclaration:print (ident,close) print(ident.."Declaration{") print(ident.." mod = '"..self.mod.."',") print(ident.." type = '"..self.type.."',") print(ident.." ptr = '"..self.ptr.."',") print(ident.." name = '"..self.name.."',") print(ident.." dim = '"..self.dim.."',") print(ident.." def = '"..self.def.."',") print(ident.." ret = '"..self.ret.."',") print(ident.."}"..close) end -- declare tag function classDeclaration:decltag () self.itype, self.tag = tagvar(self.type,strfind(self.mod,'const')) end -- output type checking function classDeclaration:outchecktype (narg) local tag, def if self.dim ~= '' then tag = 'LUA_TTABLE' def = 0 else tag = self.tag def = self.def~='' or 0 end return 'tolua_istype(tolua_S,'..narg..','..tag..','..def..')' end -- Declare variable function classDeclaration:declare (narg) local ptr = '' if self.ptr~='' then ptr = '*' end output(" ",self.mod,self.type,ptr) if self.dim ~= '' and tonumber(self.dim)==nil then output('*') end output(self.name) if self.dim ~= '' then if tonumber(self.dim)~=nil then output('[',self.dim,'];') else output(' = (',self.mod,self.type,ptr,'*)', 'malloc(',self.dim,'*sizeof(',self.type,ptr,'));') end else local t = isbasic(self.type) output(' = ') if not t and ptr=='' then output('*') end output('((',self.mod,self.type) if not t then output('*') end output(') ') local def = 0 if self.def ~= '' then def = self.def end if t then output('tolua_get'..t,'(tolua_S,',narg,',',def,'));') else output('tolua_getusertype(tolua_S,',narg,',',def,'));') end end end -- Get parameter value function classDeclaration:getarray (narg) if self.dim ~= '' then output(' {') local def = self.def~='' or 0 output(' if (!tolua_arrayistype(tolua_S,',narg,',',self.tag,',',self.dim,',',def,'))') output(' goto tolua_lerror;') output(' else\n') output(' {') output(' int i;') output(' for(i=0; i<'..self.dim..';i++)') local t = isbasic(self.type) local ptr = '' if self.ptr~='' then ptr = '*' end output(' ',self.name..'[i] = ') if not t and ptr=='' then output('*') end output('((',self.mod,self.type) if not t then output('*') end output(') ') local def = 0 if self.def ~= '' then def = self.def end if t then output('tolua_getfield'..t..'(tolua_S,',narg,',i+1,',def,'));') else output('tolua_getfieldusertype(tolua_S,',narg,',i+1,',def,'));') end output(' }') output(' }') end end -- Get parameter value function classDeclaration:setarray (narg) if self.dim ~= '' then output(' {') output(' int i;') output(' for(i=0; i<'..self.dim..';i++)') local t,ct = isbasic(self.type) if t then output(' tolua_pushfield'..t..'(tolua_S,',narg,',i+1,(',ct,')',self.name,'[i]);') else if self.ptr == '' then output(' {') output('#ifdef __cplusplus\n') output(' void* toluaI_clone = new',self.type,'(',self.name,'[i]);') output('#else\n') output(' void* toluaI_clone = tolua_copy(tolua_S,(void*)&',self.name,'[i],sizeof(',self.type,'));') output('#endif\n') output(' tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_doclone(tolua_S,toluaI_clone,',self.tag,'),',self.tag,');') output(' }') --output(' tolua_pushfieldclone(tolua_S,',narg,',i+1,(void*)&',self.name,'[i],sizeof(',self.type,'),',self.tag,');') else output(' tolua_pushfieldusertype(tolua_S,',narg,',i+1,(void*)',self.name,'[i],',self.tag,');') end end output(' }') end end -- Free dynamically allocated array function classDeclaration:freearray () if self.dim ~= '' and tonumber(self.dim)==nil then output(' free(',self.name,');') end end -- Pass parameter function classDeclaration:passpar () if self.ptr=='&' then output('*'..self.name) elseif self.ret=='*' then output('&'..self.name) else output(self.name) end end -- Return parameter value function classDeclaration:retvalue () if self.ret ~= '' then local t,ct = isbasic(self.type) if t then output(' tolua_push'..t..'(tolua_S,(',ct,')'..self.name..');') else output(' tolua_pushusertype(tolua_S,(void*)'..self.name..',',self.tag,');') end return 1 end return 0 end -- Internal constructor function _Declaration (t) if t.name and t.name~='' then local n = split(t.name,'@') t.name = n[1] t.lname = gsub(n[2] or n[1],"%[.-%]","") end t._base = classDeclaration settag(t,tolua_tag) t:checkname() t:checktype() return t end -- Constructor -- Expects the string declaration. -- The kind of declaration can be "var" or "func". function Declaration (s,kind) -- eliminate spaces if default value is provided s = gsub(s,"%s*=%s*","=") if kind == "var" then -- check the form: void if s == '' or s == 'void' then return _Declaration{type = 'void', kind = kind} end end -- check the form: mod type*& name local t = split(s,'%*%s*&') if t.n == 2 then if kind == 'func' then error("#invalid function return type: "..s) end local m = split(t[1],'%s%s*') return _Declaration{ name = t[2], ptr = '*', ret = '&', type = m[m.n], mod = concat(m,1,m.n-1), kind = kind } end -- check the form: mod type** name t = split(s,'%*%s*%*') if t.n == 2 then if kind == 'func' then error("#invalid function return type: "..s) end local m = split(t[1],'%s%s*') return _Declaration{ name = t[2], ptr = '*', ret = '*', type = m[m.n], mod = concat(m,1,m.n-1), kind = kind } end -- check the form: mod type& name t = split(s,'&') if t.n == 2 then local m = split(t[1],'%s%s*') return _Declaration{ name = t[2], ptr = '&', type = m[m.n], mod = concat(m,1,m.n-1) , kind = kind } end -- check the form: mod type* name local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end) t = split(s1,'%*') if t.n == 2 then t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression local m = split(t[1],'%s%s*') return _Declaration{ name = t[2], ptr = '*', type = m[m.n], mod = concat(m,1,m.n-1) , kind = kind } end if kind == 'var' then -- check the form: mod type name t = split(s,'%s%s*') local v if istype(t[t.n]) then v = '' else v = t[t.n]; t.n = t.n-1 end return _Declaration{ name = v, type = t[t.n], mod = concat(t,1,t.n-1), kind = kind } else -- kind == "func" -- check the form: mod type name t = split(s,'%s%s*') local v = t[t.n] -- last word is the function name local tp,md if t.n>1 then tp = t[t.n-1] md = concat(t,1,t.n-2) end return _Declaration{ name = v, type = tp, mod = md, kind = kind } end end zangband/src/lua/define.lua0000644000000000000000000000275310250356275014661 0ustar rootroot-- tolua: define class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: define.lua,v 1.3 2003/12/04 17:49:53 sfuerst Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Define class -- Represents a numeric const definition -- The following filds are stored: -- name = constant name classDefine = { name = '', _base = classFeature, } settag(classDefine,tolua_tag) -- register define function classDefine:register () local p = self:inmodule() if p then output(' tolua_constant(tolua_S,"'..p..'","'..self.lname..'",'..self.name..');') else output(' TOLUA_DEF('..self.name..');') end end -- unregister define function classDefine:unregister () if not self:inmodule() then output(' TOLUA_UNDEF('..self.lname..');') end end -- Print method function classDefine:print (ident,close) print(ident.."Define{") print(ident.." name = '"..self.name.."',") print(ident.." lname = '"..self.lname.."',") print(ident.."}"..close) end -- Internal constructor function _Define (t) t._base = classDefine settag(t,tolua_tag) if t.name == '' then error("#invalid define") end append(t) return t end -- Constructor -- Expects a string representing the constant name function Define (n) local t = split(n,'@') return _Define { name = t[1], lname = t[2] or t[1] } end zangband/src/lua/doit.lua0000644000000000000000000000226410250356275014363 0ustar rootroot-- Generate binding code -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: doit.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- open input file, if any if flags.f then local st, msg = readfrom(flags.f) if not st then error('#'..msg) end end -- define package name, if not provided if not flags.n then if flags.f then flags.n = gsub(flags.f,"%..*","") else error("#no package name nor input file provided") end end local p = Package(flags.n) if flags.f then readfrom() end if flags.p then return -- only parse end if flags.o then local st,msg = writeto(flags.o) if not st then error('#'..msg) end end if flags.P then p:print() else p:decltag() p:preamble() p:supcode() p:register() p:unregister() end if flags.o then writeto() end -- write header file if not flags.P then if flags.H then local st,msg = writeto(flags.H) if not st then error('#'..msg) end p:header() writeto() end end zangband/src/lua/enumerate.lua0000644000000000000000000000412710250356275015411 0ustar rootroot-- tolua: enumerate class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: enumerate.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Enumerate class -- Represents enumeration -- The following fields are stored: -- {i} = list of constant names classEnumerate = { _base = classFeature, } settag(classEnumerate,tolua_tag) -- register enumeration function classEnumerate:register () local p = self:inclass() or self:inmodule() local i=1 while self[i] do if p then if self:inclass() then output(' tolua_constant(tolua_S,"'..p..'","'..self.lnames[i]..'",'..p..'::'..self[i]..');') else output(' tolua_constant(tolua_S,"'..p..'","'..self.lnames[i]..'",'..self[i]..');') end else output(' tolua_constant(tolua_S,NULL,"'..self.lnames[i]..'",'..self[i]..');') end i = i+1 end end -- register enumeration function classEnumerate:unregister () if self:inclass()==nil and self:inmodule()==nil then local i=1 while self[i] do output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lnames[i]..'");') i = i+1 end end end -- Print method function classEnumerate:print (ident,close) print(ident.."Enumerate{") local i=1 while self[i] do print(ident.." '"..self[i].."'("..self.lnames[i].."),") i = i+1 end print(ident.."}"..close) end -- Internal constructor function _Enumerate (t) t._base = classEnumerate settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects a string representing the enumerate body function Enumerate (b) local t = split(strsub(b,2,-2),',') -- eliminate braces local i = 1 local e = {n=0} while t[i] do local tt = split(t[i],'=') -- discard initial value e.n = e.n + 1 e[e.n] = tt[1] i = i+1 end -- set lua names i = 1 e.lnames = {} while e[i] do local t = split(e[i],'@') e[i] = t[1] e.lnames[i] = t[2] or t[1] i = i+1 end return _Enumerate(e) end zangband/src/lua/feature.lua0000644000000000000000000000305110250356275015052 0ustar rootroot-- tolua: abstract feature class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: feature.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Feature class -- Represents the base class of all mapped feature. classFeature = { } -- write support code function classFeature:supcode () end -- output tag function classFeature:decltag () end -- register feature function classFeature:register () end -- unregister feature function classFeature:unregister () end -- translate verbatim function classFeature:preamble () end -- check if feature is inside a class definition -- it returns the feature class name or nil. function classFeature:inclass () if self.parent and self.parent.type == 'class' then return self.parent.name else return nil end end -- check if feature is inside a module -- it returns the feature module name or nil. function classFeature:inmodule () if self.parent and self.parent.type == 'module' then return self.parent.name else return nil end end -- return C binding function name based on name -- the client specifies a prefix -- return C binding function name -- the client specifies a prefix function classFeature:cfuncname (n) if self.parent then n = self.parent:cfuncname(n) end if self.lname then return n..'_'..self.lname else return n..'_'..self.name end end zangband/src/lua/function.lua0000644000000000000000000001744710250356275015262 0ustar rootroot-- tolua: function class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: function.lua,v 1.5 2003/12/04 17:40:58 sfuerst Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Function class -- Represents a function or a class method. -- The following fields are stored: -- mod = type modifiers -- type = type -- ptr = "*" or "&", if representing a pointer or a reference -- name = name -- args = list of argument declarations -- const = if it is a method receiving a const "this". classFunction = { mod = '', type = '', ptr = '', name = '', args = {n=0}, const = '', _base = classFeature, } settag(classFunction,tolua_tag) -- declare tags function classFunction:decltag () self.itype,self.tag = tagvar(self.type,strfind(self.mod,'const')) local i=1 while self.args[i] do self.args[i]:decltag() i = i+1 end end -- Write binding function -- Outputs C/C++ binding function. function classFunction:supcode () local nret = 0 -- number of returned values local class = self:inclass() local _,_,static = strfind(self.mod,'^%s*(static)') if class then output("/* method:",self.name," of class ",class," */") else output("/* function:",self.name," */") end output("static int",self.cname,"(lua_State* tolua_S)") output("{") -- check types output(' if (') -- check self local narg local count = 0 if class then narg=2 else narg=1 end if class and self.name~='new' and static==nil then if self.const == 'const' then output('!tolua_istype(tolua_S,1,',self.parent.ctag,',0) ||\n') else output('!tolua_istype(tolua_S,1,',self.parent.tag,',0) ||\n') end count = 1 end -- check args if self.args[1].type ~= 'void' then local i=1 while self.args[i] do if isbasic(self.args[i].type) ~= 'value' then if count == 0 then output('!'..self.args[i]:outchecktype(narg)..' ||\n') count=1 else output(' !'..self.args[i]:outchecktype(narg)..' ||\n') end end narg = narg+1 i = i+1 end end -- check end of list if count == 0 then output('!tolua_isnoobj(tolua_S,'..narg..'))\n') count=1 else output(' !tolua_isnoobj(tolua_S,'..narg..'))\n') end output(' {') -- call overloaded function or generate error local overload = strsub(self.cname,-2,-1) - 1 if overload >= 0 then output(' return '..strsub(self.cname,1,-3)..format("%02d",overload)..'(tolua_S);') else output(' TOLUA_ERR_FN('..self.lname..');') end output(' } else') output(' {') -- declare self, if the case local narg if class then narg=2 else narg=1 end if class and self.name~='new' and static==nil then output(' ',self.const,class,'*','self = ') output('(',self.const,class,'*) ') output('tolua_getusertype(tolua_S,1,0);') elseif static then _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)') end -- declare parameters if self.args[1].type ~= 'void' then local i=1 while self.args[i] do self.args[i]:declare(narg) narg = narg+1 i = i+1 end end -- check self if class and self.name~='new' and static==nil then output(' if (!self) tolua_error(tolua_S,"invalid \'self\' in function \''..self.name..'\'");'); end -- get array element values if class then narg=2 else narg=1 end if self.args[1].type ~= 'void' then local i=1 while self.args[i] do self.args[i]:getarray(narg) narg = narg+1 i = i+1 end end -- call function if class and self.name=='delete' then output(' delete self;') elseif class and self.name == 'operator&[]' then output(' self->operator[](',self.args[1].name,') = ',self.args[2].name,';') else if self.type ~= '' and self.type ~= 'void' then output(' ',self.mod,self.type,self.ptr,'toluaI_ret = ') output('(',self.mod,self.type,self.ptr,') ') else output(' ') end if class and self.name=='new' then output('new',class,'(') elseif class and static then output(class..'::'..self.name,'(') elseif class then output('self->'..self.name,'(') else output(self.name,'(') end -- write parameters local i=1 while self.args[i] do self.args[i]:passpar() i = i+1 if self.args[i] then output(',') end end output(');') -- return values if self.type ~= '' and self.type ~= 'void' then nret = nret + 1 local t,ct = isbasic(self.type) if t then output(' tolua_push'..t..'(tolua_S,(',ct,')toluaI_ret);') else if self.ptr == '' then output(' {') output('#ifdef __cplusplus\n') output(' void* toluaI_clone = new',self.type,'(toluaI_ret);') output('#else\n') output(' void* toluaI_clone = tolua_copy(tolua_S,(void*)&toluaI_ret,sizeof(',self.type,'));') output('#endif\n') output(' tolua_pushusertype(tolua_S,tolua_doclone(tolua_S,toluaI_clone,',self.tag,'),',self.tag,');') output(' }') --output(' tolua_pushclone((void*)&toluaI_ret,sizeof(',self.type,'),',self.tag,');') elseif self.ptr == '&' then output(' tolua_pushusertype(tolua_S,(void*)&toluaI_ret,',self.tag,');') else output(' tolua_pushusertype(tolua_S,(void*)toluaI_ret,',self.tag,');') end end end local i=1 while self.args[i] do nret = nret + self.args[i]:retvalue() i = i+1 end -- set array element values if class then narg=2 else narg=1 end if self.args[1].type ~= 'void' then local i=1 while self.args[i] do self.args[i]:setarray(narg) narg = narg+1 i = i+1 end end -- free dynamically allocated array if self.args[1].type ~= 'void' then local i=1 while self.args[i] do self.args[i]:freearray() i = i+1 end end end output(' }') output(' return '..nret..';') output('}') output('\n') end -- register function function classFunction:register () local parent = self:inclass() or self:inmodule() if parent then output(' tolua_function(tolua_S,"'..parent..'","'..self.lname..'",'..self.cname..');') else output(' TOLUA_FUN('..self.lname..','..self.cname..');') end end -- unregister function function classFunction:unregister () if self:inclass()==nil and self:inmodule()==nil then output(' TOLUA_UNDEF('..self.lname..');') end end -- Print method function classFunction:print (ident,close) print(ident.."Function{") print(ident.." mod = '"..self.mod.."',") print(ident.." type = '"..self.type.."',") print(ident.." ptr = '"..self.ptr.."',") print(ident.." name = '"..self.name.."',") print(ident.." const = '"..self.const.."',") print(ident.." cname = '"..self.cname.."',") print(ident.." lname = '"..self.lname.."',") print(ident.." args = {") local i=1 while self.args[i] do self.args[i]:print(ident.." ",",") i = i+1 end print(ident.." }") print(ident.."}"..close) end -- determine lua function name overload function classFunction:overload () return self.parent:overload(self.lname) end -- Internal constructor function _Function (t) t._base = classFunction settag(t,tolua_tag) if t.const ~= 'const' and t.const ~= '' then error("#invalid 'const' specification") end append(t) if t:inclass() then if t.name == t.parent.name then t.name = 'new' t.lname = 'new' t.type = t.parent.name t.ptr = '*' elseif t.name == '~'..t.parent.name then t.name = 'delete' t.lname = 'delete' end end t.cname = t:cfuncname("toluaI")..t:overload(t) return t end -- Constructor -- Expects three strings: one representing the function declaration, -- another representing the argument list, and the third representing -- the "const" or empty string. function Function (d,a,c) local t = split(strsub(a,2,-2),',') -- eliminate braces local i=1 local l = {n=0} while t[i] do l.n = l.n+1 l[l.n] = Declaration(t[i],'var') i = i+1 end local f = Declaration(d,'func') f.args = l f.const = c return _Function(f) end zangband/src/lua/lua2c.lua0000644000000000000000000000142210250356275014425 0ustar rootroot-- lua2c.lua -- embed lua code into C source -- celetecgraf.puc-rio.br -- dez 2000 function embed (code) -- clean Lua code local s = clean(code) if not s then error("parser error in embedded code") end -- convert to C output('\n { /* begin embedded lua code */\n') output(' static unsigned char B[] = {\n ') local t={n=0} local b = gsub(s,'(.)',function (c) local e = '' %t.n=%t.n+1 if %t.n==15 then %t.n=0 e='\n ' end return format('%3u,%s',strbyte(c),e) end ) output(b..strbyte(" ")) output('\n };\n') output(' lua_dobuffer(tolua_S,(char*)B,sizeof(B),"'..fn..': embedded Lua code");') output(' } /* end of embedded lua code */\n\n') end zangband/src/lua/module.lua0000644000000000000000000000271010250356275014705 0ustar rootroot-- tolua: module class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: module.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Module class -- Represents module. -- The following fields are stored: -- {i} = list of objects in the module. classModule = { _base = classContainer, type = 'module' } settag(classModule,tolua_tag) -- register module function classModule:register () output(' tolua_module(tolua_S,"'..self.name..'");') local i=1 while self[i] do self[i]:register() i = i+1 end end -- unregister module function classModule:unregister () output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.name..'");') end -- Print method function classModule:print (ident,close) print(ident.."Module{") print(ident.." name = '"..self.name.."';") local i=1 while self[i] do self[i]:print(ident.." ",",") i = i+1 end print(ident.."}"..close) end -- Internal constructor function _Module (t) t._base = classModule settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects two string representing the module name and body. function Module (n,b) local t = _Module(_Container{name=n}) push(t) t:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces pop() return t end zangband/src/lua/operator.lua0000644000000000000000000000564710250356275015267 0ustar rootroot-- tolua: operator class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: operator.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Operator class -- Represents an operator function or a class operator method. -- It stores the same fields as functions do plus: -- kind = set of character representing the operator (as it appers in C++ code) classOperator = { kind = '', _base = classFunction, } settag(classOperator,tolua_tag) -- table to transform operator kind into the appropriate tag method name _TM = {['+'] = 'operator_add', ['-'] = 'operator_sub', ['*'] = 'operator_mul', ['/'] = 'operator_div', ['<'] = 'operator_lt', ['[]'] = 'operator_get', ['&[]'] = 'operator_set', } -- Print method function classOperator:print (ident,close) print(ident.."Operator{") print(ident.." kind = '"..self.kind.."',") print(ident.." mod = '"..self.mod.."',") print(ident.." type = '"..self.type.."',") print(ident.." ptr = '"..self.ptr.."',") print(ident.." name = '"..self.name.."',") print(ident.." const = '"..self.const.."',") print(ident.." cname = '"..self.cname.."',") print(ident.." lname = '"..self.lname.."',") print(ident.." args = {") local i=1 while self.args[i] do self.args[i]:print(ident.." ",",") i = i+1 end print(ident.." }") print(ident.."}"..close) end -- Internal constructor function _Operator (t) t._base = classOperator settag(t,tolua_tag) if t.const ~= 'const' and t.const ~= '' then error("#invalid 'const' specification") end append(t) if not t:inclass() then error("#operator can only be defined as class member") end t.cname = t:cfuncname("toluaI")..t:overload(t) t.name = t.name..t.kind return t end -- Constructor -- Expects three strings: one representing the function declaration, -- another representing the argument list, and the third representing -- the "const" or empty string. function Operator (d,k,a,c) local t = split(strsub(a,2,strlen(a)-1),',') -- eliminate braces local i=1 local l = {n=0} while t[i] do l.n = l.n+1 l[l.n] = Declaration(t[i],'var') i = i+1 end if k == '[]' then d = gsub(d,'&','') elseif k=='&[]' then l.n = l.n+1 l[l.n] = Declaration(d,'var') l[l.n].name = 'toluaI_value' end local f = Declaration(d,'func') if k == '[]' and (l[1]==nil or isbasic(l[1].type)~='number') then error('operator[] can only be defined for numeric index.') end f.args = l f.const = c f.kind = gsub(k,"%s","") f.lname = _TM[f.kind] if not f.lname then error("tolua: no support for operator" .. f.kind) end if f.kind == '[]' and not strfind(f.mod,'const') then Operator(d,'&'..k,a,c) -- create correspoding set operator end return _Operator(f) end zangband/src/lua/package.lua0000644000000000000000000001546410250356275015025 0ustar rootroot-- tolua: package class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: package.lua,v 1.8 2003/12/04 14:06:42 sfuerst Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Package class -- Represents the whole package being bound. -- The following fields are stored: -- {i} = list of objects in the package. classPackage = { _base = classContainer, type = 'package' } settag(classPackage,tolua_tag) -- Print method function classPackage:print () print("Package: "..self.name) local i=1 while self[i] do self[i]:print("","") i = i+1 end end function classPackage:preprocess () self.code = "\n"..self.code -- add a blank sentinel line -- avoid preprocessing verbatim lines local V = {} self.code = gsub(self.code,"\n(%s*%$[^%[%]][^\n]*)",function (v) tinsert(%V,v) return "\n$"..getn(%V).."$" end) -- avoid preprocessing embedded lua code local C = {} self.code = gsub(self.code,"\n%s*%$%[","\1") -- deal with embedded Lua code self.code = gsub(self.code,"\n%s*%$%]","\2") self.code = gsub(self.code,"(%b\1\2)", function (c) tinsert(%C,c) return "\n$["..getn(%C).."]$" end) -- perform global substitution self.code = gsub(self.code,"(//[^\n]*)","") -- eliminate C++ comments self.code = gsub(self.code,"/%*","\1") self.code = gsub(self.code,"%*/","\2") self.code = gsub(self.code,"%b\1\2","") self.code = gsub(self.code,"\1","/%*") self.code = gsub(self.code,"\2","%*/") self.code = gsub(self.code,"%s*@%s*","@") -- eliminate spaces beside @ self.code = gsub(self.code,"%s?inline(%s)","%1") -- eliminate 'inline' keyword self.code = gsub(self.code,"%s?extern(%s)","%1") -- eliminate 'extern' keyword self.code = gsub(self.code,"%s?virtual(%s)","%1") -- eliminate 'virtual' keyword self.code = gsub(self.code,"public:","") -- eliminate 'public:' keyword self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' self.code = gsub(self.code,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' -- restore embedded code self.code = gsub(self.code,"%$%[(%d+)%]%$",function (n) return %C[tonumber(n)] end) -- restore verbatim lines self.code = gsub(self.code,"%$(%d+)%$",function (n) return %V[tonumber(n)] end) end -- translate verbatim function classPackage:preamble () output('/*\n') output('** Lua binding: '..self.name..'\n') output('** Generated automatically by '..TOLUA_VERSION..'.\n') output('*/\n\n') output('#include "lua/tolua.h"\n\n') if not flags.h then output('/* Exported function */') output('int tolua_'..self.name..'_open (lua_State* tolua_S);') output('void tolua_'..self.name..'_close (lua_State* tolua_S);') output('\n') end local i=1 while self[i] do self[i]:preamble() i = i+1 end output('\n') output('/* function to register type */') output('static void toluaI_reg_types (lua_State* tolua_S)') output('{') -- Hack - prevent compiler warnings when no types. output('(void) tolua_S; /* Hack - prevent compiler warnings */') foreach(_usertype,function(n,v) output(' tolua_usertype(tolua_S,"',v,'");') end) output('}') output('\n') end -- register package -- write package open function function classPackage:register () output("/* Open function */") output("int tolua_"..self.name.."_open (lua_State* tolua_S)") output("{") output(" tolua_open(tolua_S);") output(" toluaI_reg_types(tolua_S);") local i=1 while self[i] do self[i]:register() i = i+1 end output(" return 1;") output("}") end -- unregister package -- write package close function function classPackage:unregister () output("/* Close function */") output("void tolua_"..self.name.."_close (lua_State* tolua_S)") output("{") local i=1 while self[i] do self[i]:unregister() i = i+1 end output("}") end -- write header file function classPackage:header () output('/*\n') output('** Lua binding: '..self.name..'\n') output('** Generated automatically by '..TOLUA_VERSION..'.\n') output('*/\n\n') if not flags.h then output('/* Exported function */') output('int tolua_'..self.name..'_open (lua_State* tolua_S);') output('void tolua_'..self.name..'_close (lua_State* tolua_S);') output('\n') end end -- Internal constructor function _Package (t) t._base = classPackage settag(t,tolua_tag) return t end -- Constructor -- Expects the base file name. -- It assumes the file has extension ".pkg". function Package (name) -- read file local code = read("*a") code = "\n" .. code -- add sentinel -- deal with include directive local nsubst repeat code,nsubst = gsub(code,"\n%s*%$<(.-)>%s*\n",function (fn) local fp,msg = openfile(fn,'r') if not fp then error('#'..msg..': '..fn) end local s = read(fp,'*a') closefile(fp) return "\n" .. s end) until nsubst==0 -- deal with include directive for C/C++ header files local nsubst repeat code,nsubst = gsub(code,"\n%s*%${(.-)}%s*\n", function (fn) local fp,msg = openfile(fn,'r') if not fp then error('#'..msg..': '..fn) end local s = read(fp,'*a') closefile(fp) -- extract marked code local T = {code="\n"} s= "\n" .. s .. "\n" -- add blank lines as sentinels -- extract one-line statments gsub(s,"\n(.-)[Tt][Oo][Ll][Uu][Aa]_[Ee][Xx][Pp][Oo][Rr][Tt][^\n]*\n", function (c) %T.code = %T.code .. c .. "\n" end ) -- extract multiline statments gsub(s,"\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Bb][Ee][Gg][Ii][Nn][^\n]*".. "(.-)" .. "\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Ee][Nn][Dd][^\n]*\n", function (c) %T.code = %T.code .. c .. "\n" end ) return T.code end) until nsubst==0 local t = _Package(_Container{name=name, code=code}) push(t) t:preprocess() t:parse(t.code) pop() return t end zangband/src/lua/typedef.lua0000644000000000000000000000261210250356275015061 0ustar rootroot-- tolua: typedef class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: typedef.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Typedef class -- Represents a type synonym. -- The 'de facto' type replaces the typedef before the -- remaining code is parsed. -- The following fields are stored: -- utype = typedef name -- type = 'de facto' type -- mod = modifiers to the 'de facto' type classTypedef = { utype = '', mod = '', type = '' } -- Print method function classTypedef:print (ident,close) print(ident.."Typedef{") print(ident.." utype = '"..self.utype.."',") print(ident.." mod = '"..self.mod.."',") print(ident.." type = '"..self.type.."',") print(ident.."}"..close) end -- Internal constructor function _Typedef (t) t._base = classTypedef settag(t,tolua_tag) appendtypedef(t) return t end -- Constructor -- Expects one string representing the type definition. function Typedef (s) if strfind(s,'[%*&]') then tolua_error("#invalid typedef: pointers (and references) are not supported") end local t = split(gsub(s,"%s%s*"," ")," ") return _Typedef { utype = t[t.n], type = t[t.n-1], mod = concat(t,1,t.n-2) } end zangband/src/lua/variable.lua0000644000000000000000000001140710250356275015210 0ustar rootroot-- tolua: variable class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: variable.lua,v 1.3 2003/12/04 12:16:03 sfuerst Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Variable class -- Represents a extern variable or a public member of a class. -- Stores all fields present in a declaration. classVariable = { _base = classDeclaration, } settag(classVariable,tolua_tag) -- Print method function classVariable:print (ident,close) print(ident.."Variable{") print(ident.." mod = '"..self.mod.."',") print(ident.." type = '"..self.type.."',") print(ident.." ptr = '"..self.ptr.."',") print(ident.." name = '"..self.name.."',") print(ident.." def = '"..self.def.."',") print(ident.." ret = '"..self.ret.."',") print(ident.."}"..close) end -- get variable value function classVariable:getvalue (class,static) if class and static then return class..'::'..self.name elseif class then return 'self->'..self.name else return self.name end end -- Write binding functions function classVariable:supcode () local class = self:inclass() -- get function ------------------------------------------------ if class then output("/* get function:",self.name," of class ",class," */") else output("/* get function:",self.name," */") end self.cgetname = self:cfuncname("toluaI_get") output("static int",self.cgetname,"(lua_State* tolua_S)") output("{") -- declare self, if the case local _,_,static = strfind(self.mod,'^%s*(static)') if class and static==nil then -- get and check self value output(' TOLUA_GET_SELF(',class,');'); elseif static then _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)') end -- return value local t,ct = isbasic(self.type) if t then output(' tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static)..');') else if self.ptr == '&' or self.ptr == '' then output(' tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static)..',',self.tag,');') else output(' tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static)..',',self.tag,');') end end output(' return 1;') output('}') output('\n') -- set function ------------------------------------------------ if not strfind(self.mod,'const') then if class then output("/* set function:",self.name," of class ",class," */") else output("/* set function:",self.name," */") end self.csetname = self:cfuncname("toluaI_set") output("static int",self.csetname,"(lua_State* tolua_S)") output("{") -- declare self, if the case local narg=1 if class and static==nil then -- set and check self value output(' TOLUA_GET_SELF(',class,');'); narg = narg+1 elseif static then _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)') narg = narg+1 end -- check type output(' if (!'..self:outchecktype(narg)..')') output(' TOLUA_ERR_ASSIGN;') -- assign value local ptr = '' if self.ptr~='' then ptr = '*' end output(' ') if class and static then output(class..'::'..self.name) elseif class then output('self->'..self.name) else output(self.name) end local t = isbasic(self.type) output(' = ') if not t and ptr=='' then output('*') end output('((',self.mod,self.type) if not t then output('*') end output(') ') local def = 0 if self.def ~= '' then def = self.def end if t then output('tolua_get'..t,'(tolua_S,',narg,',',def,'));') else output('tolua_getusertype(tolua_S,',narg,',',def,'));') end output(' return 0;') output('}') output('\n') end end function classVariable:register () local parent = self:inclass() or self:inmodule() if parent then if self.csetname then output(' tolua_tablevar(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..','..self.csetname..');') else output(' tolua_tablevar(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..',NULL);') end else if self.csetname then output(' tolua_globalvar(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');') else output(' tolua_globalvar(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);') end end end function classVariable:unregister () if self:inclass()==nil and self:inmodule()==nil then output(' lua_getglobals(tolua_S);') output(' lua_pushstring(tolua_S,"',self.lname,'"); lua_pushnil(tolua_S); lua_rawset(tolua_S,-3);') output(' lua_pop(tolua_S,1);') end end -- Internal constructor function _Variable (t) t._base = classVariable settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects a string representing the variable declaration. function Variable (s) return _Variable (Declaration(s,'var')) end zangband/src/lua/verbatim.lua0000644000000000000000000000262210250356275015233 0ustar rootroot-- tolua: verbatim class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: verbatim.lua,v 1.1 2001/10/29 17:49:53 rr9 Exp $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- Verbatim class -- Represents a line translated directed to the binding file. -- The following filds are stored: -- line = line text classVerbatim = { line = '', _base = classFeature, } settag(classVerbatim,tolua_tag) -- preamble verbatim function classVerbatim:preamble () if not self.cond then write(self.line) end end -- support code function classVerbatim:supcode () if self.cond then write(self.line) write('\n') end end -- register code function classVerbatim:register () if self.cond then write(self.line) end end -- Print method function classVerbatim:print (ident,close) print(ident.."Verbatim{") print(ident.." line = '"..self.line.."',") print(ident.."}"..close) end -- Internal constructor function _Verbatim (t) t._base = classVerbatim settag(t,tolua_tag) append(t) return t end -- Constructor -- Expects a string representing the text line function Verbatim (l) local c if strsub(l,1,1) == '$' then c = 1 l = strsub(l,2) end return _Verbatim { line = l, cond = c } end zangband/src/lua/tolualua.pkg0000644000000000000000000000042110250356275015243 0ustar rootroot$[ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $] zangband/src/lua/makefile.zb0000644000000000000000000000204510250356275015030 0ustar rootrootsubdir = ./src/lua ## ## Lua ## ## ## Lua Object Files ## LUAOBJS := $(addprefix src/lua/,\ lapi.o ldebug.o lmem.o lstrlib.o lvm.o \ tolua_lb.o lauxlib.o ldo.o lobject.o ltable.o \ lzio.o tolua_rg.o lbaselib.o lfunc.o lparser.o \ ltests.o tolua_bd.o tolua_tm.o lcode.o lgc.o \ lstate.o ltm.o tolua_eh.o tolua_tt.o ldblib.o \ llex.o lstring.o lundump.o tolua_gp.o) objs-y += $(LUAOBJS) ## ## tolua ## TOLUAOBJS := $(addprefix src/lua/,tolua.o tolualua.o liolib.o) $(LUAOBJS) src/lua/tolua: $(TOLUAOBJS) $(LINK) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) # Wrappers also depend on these TOLUALUAOBJS := $(addprefix src/lua/,basic.lua feature.lua verbatim.lua code.lua \ typedef.lua container.lua package.lua module.lua define.lua \ enumerate.lua declaration.lua variable.lua array.lua function.lua \ operator.lua class.lua clean.lua doit.lua) srcdirlist += src/lua srcfiles += src/lua/*.c src/lua/*.h src/lua/*.lua src/lua/*.pkg src/lua/makefile.zb dust-files += src/lua/*~ clean-files += src/lua/*.bak src/lua/*.o src/lua/tolua zangband/src/tk/0000755000000000000000000000000010250356275012552 5ustar rootrootzangband/src/tk/cmdinfo.c0000644000000000000000000001077310250356275014345 0ustar rootroot/* File: cmdinfo.c */ /* Purpose: command stuff */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" int CommandInfo_ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; int subCmdIndex; if (infoCmd->subCmd.count) { if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[1], infoCmd->subCmd.name, "option", 0, &subCmdIndex) != TCL_OK) { return TCL_ERROR; } infoCmd = infoCmd->subCmd.info[subCmdIndex]; return CommandInfo_ObjCmd((ClientData) infoCmd, interp, objc, objv); } if ((infoCmd->minArgs && (objC < infoCmd->minArgs)) || (infoCmd->maxArgs && (objC > infoCmd->maxArgs))) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, infoCmd->errorMsg); return TCL_ERROR; } return (*infoCmd->proc)(clientData, interp, objc, objv); } void CommandInfo_Add(CommandInfo *infoCmd, CommandInfo *infoSubCmd) { int i; int alloc = infoCmd->subCmd.alloc; int count = infoCmd->subCmd.count; if ((count + 1) >= alloc) { CommandInfo **info; cptr *name; C_MAKE(info, alloc + 5, CommandInfo*); C_MAKE(name, alloc + 5 + 1, cptr); if (infoCmd->subCmd.count) { for (i = 0; i < count; i++) { name[i] = infoCmd->subCmd.name[i]; info[i] = infoCmd->subCmd.info[i]; } FREE(infoCmd->subCmd.name); FREE(infoCmd->subCmd.info); } infoCmd->subCmd.name = name; infoCmd->subCmd.info = info; infoCmd->subCmd.alloc += 5; } infoCmd->subCmd.name[count] = infoSubCmd->name; infoCmd->subCmd.name[count + 1] = NULL; infoCmd->subCmd.info[count] = infoSubCmd; ++infoCmd->subCmd.count; } CommandInfo *CommandInfo_New(CommandInit *init) { CommandInfo *infoCmd; MAKE(infoCmd, CommandInfo); infoCmd->name = init->name; infoCmd->minArgs = init->minArgs; infoCmd->maxArgs = init->maxArgs; infoCmd->errorMsg = init->errorMsg; infoCmd->proc = init->proc; infoCmd->clientData = init->clientData; infoCmd->depth = init->depth; infoCmd->subCmd.name = NULL; infoCmd->subCmd.info = NULL; infoCmd->subCmd.count = 0; infoCmd->subCmd.alloc = 0; return infoCmd; } /* * Get the CommandInfo for a command or subcommand. */ static CommandInfo *CommandInfo_GetInfoAux(Tcl_Interp *interp, cptr names[], CommandInfo *infoCmd) { int subCmdIndex; #if 0 if (strcmp(infoCmd->name, names[0])) { /* REPORT */ return NULL; } #endif /* This is the command we're looking for */ if (names[1] == NULL) { return infoCmd; } /* Check subcommands */ if (infoCmd->subCmd.count) { Tcl_Obj *nameObjPtr = Tcl_NewStringObj(names[1], -1); if (Tcl_GetIndexFromObj(interp, nameObjPtr, infoCmd->subCmd.name, "option", 0, &subCmdIndex) != TCL_OK) { Tcl_DecrRefCount(nameObjPtr); return NULL; } Tcl_DecrRefCount(nameObjPtr); infoCmd = infoCmd->subCmd.info[subCmdIndex]; return CommandInfo_GetInfoAux(interp, names + 1, infoCmd); } /* REPORT */ return NULL; } CommandInfo *CommandInfo_GetInfo(Tcl_Interp *interp, cptr names[]) { Tcl_CmdInfo cmdInfo; if (Tcl_GetCommandInfo(interp, names[0], &cmdInfo) == 0) { return NULL; } return CommandInfo_GetInfoAux(interp, names, (CommandInfo *) cmdInfo.objClientData); } int CommandInfo_InitAux(Tcl_Interp *interp, CommandInit *init, int index, CommandInfo *parent) { int i; CommandInfo *infoCmd = NULL; /* Done */ if (init[index].name == NULL) return -1; if (parent == NULL) { cptr names[2]; names[0] = init[index].name; names[1] = NULL; infoCmd = CommandInfo_GetInfo(interp, names); } if (infoCmd == NULL) { /* Create a new command */ infoCmd = CommandInfo_New(&init[index]); if (parent == NULL) { Tcl_CreateObjCommand(interp, infoCmd->name, CommandInfo_ObjCmd, (ClientData) infoCmd, NULL); } else { CommandInfo_Add(parent, infoCmd); } } i = index++; while (init[index].depth > init[i].depth) { index = CommandInfo_InitAux(interp, init, index, infoCmd); if (index == -1) break; /* ? */ } return index; } int CommandInfo_Init(Tcl_Interp *interp, CommandInit *init, CommandInfo *parent) { int i; for (i = 0; (i >= 0) && init[i].name; ) { i = CommandInfo_InitAux(interp, init, i, parent); } return TCL_OK; } zangband/src/tk/describe.c0000644000000000000000000003560410250356275014506 0ustar rootroot/* File: describe.c */ /* Purpose: object recall */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" /* * Like strcpy() but returns the length of the string copied */ int strcpy_len(char *s1, const char *s2) { int len = 0; while ((*s1++ = *s2++)) ++len; return len; } /* Global pointer into output buffer */ static char *s_buffer; /* * Write a string to the output buffer, plus optional padding */ static void roll_off(cptr str, bool pad) { if (pad) s_buffer += strcpy_len(s_buffer, " "); s_buffer += strcpy_len(s_buffer, str); } /* * Remove characters from the output buffer */ static void roll_back(int count) { s_buffer -= count; } /* * Write array of strings to output buffer */ static void roll_em(cptr *vp, int vn, cptr joiner, cptr finish) { int i; for (i = 0; i < vn; ++i) { roll_off(vp[i], FALSE); if (i < vn - 2) roll_off(", ", FALSE); else if (i == vn - 2) roll_off(joiner ? joiner : " and ", FALSE); else roll_off(finish ? finish : ".", FALSE); } } #define TR0_STAT_MASK \ (TR0_STR | TR0_INT | TR0_WIS | TR0_DEX | \ TR0_CON | TR0_CHR) #define TR1_SUST_MASK \ (TR1_SUST_STR | TR1_SUST_INT | TR1_SUST_WIS | TR1_SUST_DEX | \ TR1_SUST_CON | TR1_SUST_CHR) #define TR_STAT_MASK 0, TR0_STAT_MASK #define TR_SUST_MASK 1, TR1_SUST_MASK /* * Write object memory to a buffer */ long angtk_describe_object(object_type *o_ptr, char *buf, bool in_store) { cptr vp[128]; int vn; bool known = FALSE; bool k = FALSE; bool hack; /* Set the global. See roll_off(). */ s_buffer = buf; /* Null-terminate it anyhow */ s_buffer[0] = '\0'; /* See if the object is "known" */ if (object_known_p(o_ptr)) known = TRUE; /* Mega-Hack -- describe activation */ if (FLAG(o_ptr, TR_ACTIVATE)) { roll_off("It can be activated for ", k); roll_off(item_activation(o_ptr), FALSE); roll_off(" if it is being worn.", FALSE); k = TRUE; } /* Figurines, a hack */ if (o_ptr->tval == TV_FIGURINE) { roll_off("It will transform into a pet when thrown.", k); k = TRUE; } /* Bad things */ vn = 0; hack = FALSE; if (FLAG(o_ptr, TR_TY_CURSE)) vp[vn++] = "carries an ancient foul curse"; if (cursed_p(o_ptr)) { if (FLAG(o_ptr, TR_PERMA_CURSE)) vp[vn++] = "is permanently cursed"; else if (FLAG(o_ptr, TR_HEAVY_CURSE)) vp[vn++] = "is heavily cursed"; else if (known || (o_ptr->info & OB_SENSE)) vp[vn++] = "is cursed"; } if (FLAG(o_ptr, TR_DRAIN_EXP)) vp[vn++] = "drains experience"; if (FLAG(o_ptr, TR_TELEPORT)) vp[vn++] = "teleports randomly"; if (FLAG(o_ptr, TR_AGGRAVATE)) vp[vn++] = "aggravates monsters"; if (FLAG(o_ptr, TR_NO_MAGIC)) vp[vn++] = "disrupts magic"; if (FLAG(o_ptr, TR_NO_TELE)) vp[vn++] = "inhibits teleportation"; if (vn) { roll_off("Unfortunately it ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; hack = TRUE; } if ((FLAG(o_ptr, TR_STAT_MASK)) && o_ptr->pval) { if (hack && (o_ptr->pval < 0)) { roll_back(1); roll_off(",", FALSE); } else { if (k) roll_off(" ", FALSE); k = TRUE; } roll_off((o_ptr->pval > 0) ? "Increases " : (hack ? " and it decreases " : "Decreases "), FALSE); vn = 0; if (FLAG(o_ptr, TR_STR)) vp[vn++] = "Strength"; if (FLAG(o_ptr, TR_INT)) vp[vn++] = "Intelligence"; if (FLAG(o_ptr, TR_WIS)) vp[vn++] = "Wisdom"; if (FLAG(o_ptr, TR_DEX)) vp[vn++] = "Dexterity"; if (FLAG(o_ptr, TR_CON)) vp[vn++] = "Constitution"; if (FLAG(o_ptr, TR_CHR)) vp[vn++] = "Charisma"; if (vn == 6) { roll_off("all stats.", FALSE); } else { roll_em(vp, vn, NULL, NULL); } } /* Effects on skills */ if (o_ptr->pval) { vn = 0; if (FLAG(o_ptr, TR_STEALTH)) vp[vn++] = "Stealth"; if (FLAG(o_ptr, TR_SEARCH)) vp[vn++] = "Searching"; if (FLAG(o_ptr, TR_INFRA)) vp[vn++] = "Infra-vision"; if (FLAG(o_ptr, TR_BLOWS)) vp[vn++] = "number of attacks"; if (FLAG(o_ptr, TR_SPEED)) vp[vn++] = "speed"; if (vn) { roll_off(o_ptr->pval > 0 ? "Improves " : "Hinders ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; } } /* Criticals */ vn = 0; hack = FALSE; if (FLAG(o_ptr, TR_BRAND_ACID)) vp[vn++] = "acid"; if (FLAG(o_ptr, TR_BRAND_ELEC)) vp[vn++] = "electricity"; if (FLAG(o_ptr, TR_BRAND_FIRE)) vp[vn++] = "fire"; if (FLAG(o_ptr, TR_BRAND_COLD)) vp[vn++] = "frost"; if (FLAG(o_ptr, TR_BRAND_POIS)) vp[vn++] = "poison"; if (vn) { roll_off("Does extra damage from ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; hack = TRUE; } if (FLAG(o_ptr, TR_THROW)) { if (k) roll_off(" ", FALSE); roll_off("It is perfectly balanced for throwing.", FALSE); k = TRUE; hack = TRUE; } /* Misc nastiness */ vn = 0; if (FLAG(o_ptr, TR_CHAOTIC)) vp[vn++] = "produces chaotic effects"; if (FLAG(o_ptr, TR_VAMPIRIC)) vp[vn++] = "drains life from your foes"; if (FLAG(o_ptr, TR_IMPACT)) vp[vn++] = "causes earthquakes"; if (FLAG(o_ptr, TR_VORPAL)) vp[vn++] = "cuts with supernatural sharpness"; if (vn) { roll_off("It ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; hack = TRUE; } /* What does it slay? */ vn = 0; if (FLAG(o_ptr, TR_KILL_DRAGON) || FLAG(o_ptr, TR_SLAY_DRAGON)) vp[vn++] = "dragons"; if (FLAG(o_ptr, TR_SLAY_ORC)) vp[vn++] = "orcs"; if (FLAG(o_ptr, TR_SLAY_TROLL)) vp[vn++] = "trolls"; if (FLAG(o_ptr, TR_SLAY_GIANT)) vp[vn++] = "giants"; if (FLAG(o_ptr, TR_SLAY_DEMON)) vp[vn++] = "demons"; if (FLAG(o_ptr, TR_SLAY_UNDEAD)) vp[vn++] = "undead"; if (FLAG(o_ptr, TR_SLAY_ANIMAL)) vp[vn++] = "animals"; if (FLAG(o_ptr, TR_SLAY_EVIL)) vp[vn++] = "evil"; if (vn) { roll_off("Especially deadly against ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; } /* Sustain stats? */ if (FLAG(o_ptr, TR_SUST_MASK)) { roll_off("Sustains ", k); vn = 0; if (FLAG(o_ptr, TR_SUST_STR)) vp[vn++] = "Strength"; if (FLAG(o_ptr, TR_SUST_INT)) vp[vn++] = "Intelligence"; if (FLAG(o_ptr, TR_SUST_WIS)) vp[vn++] = "Wisdom"; if (FLAG(o_ptr, TR_SUST_DEX)) vp[vn++] = "Dexterity"; if (FLAG(o_ptr, TR_SUST_CON)) vp[vn++] = "Constitution"; if (FLAG(o_ptr, TR_SUST_CHR)) vp[vn++] = "Charisma"; if (vn == 6) { roll_off("all stats.", FALSE); } else { roll_em(vp, vn, NULL, NULL); } k = TRUE; } /* Immunities */ vn = 0; if (FLAG(o_ptr, TR_IM_ACID)) vp[vn++] = "acid"; if (FLAG(o_ptr, TR_IM_ELEC)) vp[vn++] = "electricity"; if (FLAG(o_ptr, TR_IM_FIRE)) vp[vn++] = "fire"; if (FLAG(o_ptr, TR_IM_COLD)) vp[vn++] = "cold"; if (FLAG(o_ptr, TR_RES_BLIND)) vp[vn++] = "blindness"; if (FLAG(o_ptr, TR_FREE_ACT)) vp[vn++] = "paralysis"; if (FLAG(o_ptr, TR_RES_FEAR)) vp[vn++] = "fear"; if (vn) { roll_off("Provides immunity to ", k); k = TRUE; roll_em(vp, vn, NULL, NULL); } /* Resistances */ vn = 0; if (FLAG(o_ptr, TR_RES_ACID)) vp[vn++] = "acid"; if (FLAG(o_ptr, TR_RES_ELEC)) vp[vn++] = "electricity"; if (FLAG(o_ptr, TR_RES_FIRE)) vp[vn++] = "fire"; if (FLAG(o_ptr, TR_RES_COLD)) vp[vn++] = "cold"; if (FLAG(o_ptr, TR_RES_POIS)) vp[vn++] = "poison"; if (FLAG(o_ptr, TR_RES_LITE)) vp[vn++] = "light"; if (FLAG(o_ptr, TR_RES_DARK)) vp[vn++] = "dark"; if (FLAG(o_ptr, TR_RES_CONF)) vp[vn++] = "confusion"; if (FLAG(o_ptr, TR_RES_SOUND)) vp[vn++] = "sound"; if (FLAG(o_ptr, TR_RES_SHARDS)) vp[vn++] = "shards"; if (FLAG(o_ptr, TR_RES_NETHER)) vp[vn++] = "nether"; if (FLAG(o_ptr, TR_RES_NEXUS)) vp[vn++] = "nexus"; if (FLAG(o_ptr, TR_RES_CHAOS)) vp[vn++] = "chaos"; if (FLAG(o_ptr, TR_RES_DISEN)) vp[vn++] = "disenchantment"; if (FLAG(o_ptr, TR_HOLD_LIFE)) vp[vn++] = "life draining"; if (vn) { roll_off("Provides resistance to ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; } /* What's cool? */ vn = 0; if (FLAG(o_ptr, TR_FEATHER)) vp[vn++] = "feather falling"; if (FLAG(o_ptr, TR_SEE_INVIS)) vp[vn++] = "see invisible"; if (FLAG(o_ptr, TR_TELEPATHY)) vp[vn++] = "telepathy"; if (FLAG(o_ptr, TR_SLOW_DIGEST)) vp[vn++] = "slow digestion"; if (FLAG(o_ptr, TR_REGEN)) vp[vn++] = "regeneration"; if (vn) { roll_off("Enables ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; } /* Cool too */ vn = 0; if (FLAG(o_ptr, TR_REFLECT)) vp[vn++] = "reflects missiles"; if (FLAG(o_ptr, TR_SH_FIRE)) vp[vn++] = "radiates fire"; if (FLAG(o_ptr, TR_SH_ELEC)) vp[vn++] = "radiates electricity"; if (vn) { roll_off("It ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; } /* Powerful bows */ vn = 0; if (FLAG(o_ptr, TR_XTRA_MIGHT)) vp[vn++] = "with extra might"; if (FLAG(o_ptr, TR_XTRA_SHOTS)) vp[vn++] = "excessively fast"; if (vn) { roll_off("Fires missiles ", k); roll_em(vp, vn, NULL, NULL); k = TRUE; } /* Misc */ if (FLAG(o_ptr, TR_BLESSED)) { roll_off("It has been blessed by the gods.", k); k = TRUE; } /* Hack -- describe lite's */ if (o_ptr->tval == TV_LITE) { if (FLAG(o_ptr, TR_INSTA_ART)) { roll_off("It provides light (radius 3) forever.", k); } else if (o_ptr->sval == SV_LITE_LANTERN) { roll_off("It provides light (radius 2) when fueled.", k); } else { roll_off("It provides light (radius 1) when fueled.", k); } k = TRUE; } /* Permanent light source */ if (FLAG(o_ptr, TR_LITE)) { roll_off("It provides light (radius 1) forever.", k); k = TRUE; } if (FLAG(o_ptr, TR_TUNNEL)) { roll_off("It is ", k); roll_off((o_ptr->pval >= 0) ? "an effective" : "a useless", FALSE); roll_off(" digging tool.", FALSE); k = TRUE; } /* What does the object resist? */ vn = 0; if (FLAG(o_ptr, TR_IGNORE_ACID)) vp[vn++] = "acid"; if (FLAG(o_ptr, TR_IGNORE_ELEC)) vp[vn++] = "electricity"; if (FLAG(o_ptr, TR_IGNORE_FIRE)) vp[vn++] = "fire"; if (FLAG(o_ptr, TR_IGNORE_COLD)) vp[vn++] = "cold"; if (vn) { roll_off("It cannot be harmed by ", k); roll_em(vp, vn, " or ", NULL); k = TRUE; } /* Permanently identified? */ if (o_ptr->info & OB_MENTAL) { roll_off("It is permanently identified.", k); k = TRUE; } /* Store bought */ if (!in_store && (o_ptr->info & OB_NO_EXP)) { roll_off("It was purchased.", k); k = TRUE; } return s_buffer - buf; } /* * Set a field of a Tcl array variable */ int SetArrayValueChar(cptr varName, cptr field, char value) { char string[20]; strnfmt(string, 20, "%c", value); if (Tcl_SetVar2(g_interp, varName, field, string, TCL_LEAVE_ERR_MSG) == NULL) { return TCL_ERROR; } return TCL_OK; } /* * Set a field of a Tcl array variable */ int SetArrayValueLong(cptr varName, cptr field, long value) { char string[20]; strnfmt(string, 20, "%ld", value); if (Tcl_SetVar2(g_interp, varName, field, string, TCL_LEAVE_ERR_MSG) == NULL) { return TCL_ERROR; } return TCL_OK; } /* * Set a field of a Tcl array variable */ int SetArrayValueString(cptr varName, cptr field, cptr value) { if (Tcl_SetVar2(g_interp, varName, field, value, TCL_LEAVE_ERR_MSG) == NULL) { return TCL_ERROR; } return TCL_OK; } /* * Dump object info into a Tcl array variable */ static int DumpObjectInfo(object_type *o_ptr, char *varName) { int known; cptr note; /* Known? */ known = object_known_p(o_ptr) ? 1 : 0; if (SetArrayValueLong(varName, "known", known) != TCL_OK) { return TCL_ERROR; } /* Some fields are returned only if the object is "known" */ if (known) { if (SetArrayValueLong(varName, "activate", (FLAG(o_ptr, TR_ACTIVATE)) != 0) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "artifact", FLAG(o_ptr, TR_INSTA_ART)) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "pval", o_ptr->pval) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "to_a", o_ptr->to_a) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "to_d", o_ptr->to_d) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "to_h", o_ptr->to_h) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "timeout", o_ptr->timeout) != TCL_OK) { return TCL_ERROR; } } if (SetArrayValueLong(varName, "ac", o_ptr->ac) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "dd", o_ptr->dd) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "ds", o_ptr->ds) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "k_idx", o_ptr->k_idx) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "number", o_ptr->number) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "sval", o_ptr->sval) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "tval", o_ptr->tval) != TCL_OK) { return TCL_ERROR; } if (SetArrayValueLong(varName, "weight", o_ptr->weight) != TCL_OK) { return TCL_ERROR; } note = ""; if (o_ptr->inscription) { note = quark_str(o_ptr->inscription); } if (ExtToUtf_SetArrayValueString(varName, "note", note) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* * Get verbose info for an inventory item. This should be combined * with the "angband inventory" command. The main difference is it * returns more info. */ int objcmd_inveninfo(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; /* int objC = objc - infoCmd->depth; */ Tcl_Obj *CONST *objV = objv + infoCmd->depth; int i_idx; char *varName; object_type *o_ptr; /* Hack - ignore parameter */ (void) objc; if (Tcl_GetIntFromObj(interp, objV[1], &i_idx) != TCL_OK) { return TCL_ERROR; } /*** Verify index, existing object ***/ /* Get the array variable name to dump results in */ varName = Tcl_GetStringFromObj(objV[2], NULL); /* Grab the item */ o_ptr = get_list_item(p_ptr->inventory, i_idx); return DumpObjectInfo(o_ptr, varName); } /* Textual name of each equipment slot */ cptr keyword_slot[] = { "INVEN_WIELD", "INVEN_BOW", "INVEN_LEFT", "INVEN_RIGHT", "INVEN_NECK", "INVEN_LITE", "INVEN_BODY", "INVEN_OUTER", "INVEN_ARM", "INVEN_HEAD", "INVEN_HANDS", "INVEN_FEET", NULL }; /* * Get verbose info for an equipment item. This should be combined * with the "angband equipment" command. The main difference is it * returns more info. */ int objcmd_equipinfo(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; /* int objC = objc - infoCmd->depth; */ Tcl_Obj *CONST *objV = objv + infoCmd->depth; int i_idx; char *varName; object_type *o_ptr; /* Hack - ignore parameter */ (void) objc; /* Get a numerical index or slot name */ if (Tcl_GetIntFromObj(interp, objV[1], &i_idx) != TCL_OK) { Tcl_ResetResult(interp); if (Tcl_GetIndexFromObj(interp, objV[1], keyword_slot, "slot", 0, &i_idx) != TCL_OK) { return TCL_ERROR; } } /*** Verify index, existing object ***/ /* Get the array variable name to dump results in */ varName = Tcl_GetStringFromObj(objV[2], NULL); /* Grab the item */ o_ptr = &p_ptr->equipment[i_idx]; return DumpObjectInfo(o_ptr, varName); } zangband/src/tk/icon.c0000644000000000000000000003032710250356275013653 0ustar rootroot/* File: icon.c */ /* Purpose: icon stuff */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" #include "icon.h" unsigned char *g_palette_rgb; t_icon_data *g_icon_data; /* Array of icon types */ int g_icon_data_count = 0; /* Number of icon types */ Tcl_HashTable g_icon_table; /* Hash table for icon types */ long g_icon_length = 0; /* Length in bytes of one icon */ int g_icon_size = 0; /* Icon dimensions (16, 24 or 32) */ int g_icon_depth = 0; /* Icon depth (8, 16 or 24 bpp) */ int g_pixel_size; /* Num bytes per pixel (1, 2, 3 or 4) */ int g_icon_pixels; /* Num pixels per icon (16x16, 24x24, 32x32) */ int *g_background = NULL; bool g_icon_map_changed = FALSE; void init_palette(void) { if (Palette_Init(g_interp) != TCL_OK) quit(Tcl_GetStringResult(g_interp)); g_palette_rgb = Palette_GetRGB(); } /* * */ static void Icon_AddType(t_icon_data *data) { int new; t_icon_data iconData, *icon_data_ptr = &iconData; Tcl_HashEntry *hPtr; memset(icon_data_ptr, 0, sizeof(t_icon_data)); icon_data_ptr->desc = string_make(data->desc); icon_data_ptr->icon_count = data->icon_count; icon_data_ptr->icon_data = data->icon_data; icon_data_ptr->depth = data->depth; icon_data_ptr->bypp = data->bypp; icon_data_ptr->width = data->width; icon_data_ptr->height = data->height; icon_data_ptr->pitch = data->pitch; icon_data_ptr->length = data->length; icon_data_ptr->pixels = data->pixels; g_icon_data = Array_Append(g_icon_data, &g_icon_data_count, sizeof(t_icon_data), icon_data_ptr); hPtr = Tcl_CreateHashEntry(&g_icon_table, data->desc, &new); Tcl_SetHashValue(hPtr, (ClientData) (g_icon_data_count - 1)); } /* * Initialize the icon environment. This should be called once with * the desired dimensions of the icons to use (16x16, 24x24 or 32x32). */ void init_icons(int size, int depth) { int i, n, y, x, y2, x2; t_assign_icon assign; t_icon_data icon_data, *icon_data_ptr = &icon_data; unsigned char *rgb = Colormap_GetRGB(); /* Initialize the Icon library */ if (Icon_Init(g_interp, size, depth) != TCL_OK) { quit(Tcl_GetStringFromObj(Tcl_GetObjResult(g_interp), NULL)); } /* * The TYPE_NONE/"none" type icon is a single masked icon with an empty * mask. It is suitable for equipment displays when no item is present * in a slot. */ icon_data_ptr->desc = "none"; icon_data_ptr->icon_count = 1; C_MAKE(icon_data_ptr->icon_data, g_icon_length, byte); for (i = 0; i < g_icon_length; i++) { icon_data_ptr->icon_data[i] = 0x00; } icon_data_ptr->depth = g_icon_depth; icon_data_ptr->bypp = g_pixel_size; icon_data_ptr->width = g_icon_size; icon_data_ptr->height = g_icon_size; icon_data_ptr->pitch = g_icon_size * g_pixel_size; icon_data_ptr->length = g_icon_size * g_icon_size * g_pixel_size; icon_data_ptr->pixels = g_icon_size * g_icon_size; Icon_AddType(icon_data_ptr); for (i = 0; i < g_icon_length; i++) { if (g_icon_depth != 8) icon_data_ptr->icon_data[i] = 0; /* Black (RGB 0,0,0) */ else icon_data_ptr->icon_data[i] = COLORMAP_BLACK; } /* * The TYPE_BLANK/"blank" icon type is a single black unmasked icon */ icon_data_ptr->desc = "blank"; icon_data_ptr->icon_count = 1; C_MAKE(icon_data_ptr->icon_data, g_icon_length, byte); for (i = 0; i < g_icon_length; i++) { if (g_icon_depth != 8) icon_data_ptr->icon_data[i] = 0; /* Black (RGB 0,0,0) */ else icon_data_ptr->icon_data[i] = COLORMAP_BLACK; } icon_data_ptr->depth = g_icon_depth; icon_data_ptr->bypp = g_pixel_size; icon_data_ptr->width = g_icon_size; icon_data_ptr->height = g_icon_size; icon_data_ptr->pitch = g_icon_size * g_pixel_size; icon_data_ptr->length = g_icon_size * g_icon_size * g_pixel_size; icon_data_ptr->pixels = g_icon_size * g_icon_size; Icon_AddType(icon_data_ptr); /* * The TYPE_DEFAULT/"default" icon type is a single multicolored * unmasked icon. If we see it, it probably means we forgot to * assign an icon to something. */ icon_data_ptr->desc = "default"; icon_data_ptr->icon_count = 1; C_MAKE(icon_data_ptr->icon_data, g_icon_length, byte); n = 0, y2 = 0; for (y = 0; y < 16; y++) { int dy = 0; if (g_icon_size == 24) { if (!(y & 1)) dy++; } if (g_icon_size == 32) dy++; x2 = 0; for (x = 0; x < 16; x++) { int dx = 0; if (g_icon_size == 24) { if (!(x & 1)) dx++; } if (g_icon_size == 32) dx++; PixelSet_RGB(icon_data_ptr->icon_data + (x2 * g_pixel_size) + (y2 * g_icon_size * g_pixel_size), rgb[0], rgb[1], rgb[2], g_pixel_size); PixelSet_RGB(icon_data_ptr->icon_data + ((x2 + dx) * g_pixel_size) + (y2 * g_icon_size * g_pixel_size), rgb[0], rgb[1], rgb[2], g_pixel_size); PixelSet_RGB(icon_data_ptr->icon_data + (x2 * g_pixel_size) + ((y2 + dy) * g_icon_size * g_pixel_size), rgb[0], rgb[1], rgb[2], g_pixel_size); PixelSet_RGB(icon_data_ptr->icon_data + ((x2 + dx) * g_pixel_size) + ((y2 + dy) * g_icon_size * g_pixel_size), rgb[0], rgb[1], rgb[2], g_pixel_size); rgb += 3; n++; x2 += dx ? 2 : 1; } y2 += dy ? 2 : 1; } icon_data_ptr->depth = g_icon_depth; icon_data_ptr->bypp = g_pixel_size; icon_data_ptr->width = g_icon_size; icon_data_ptr->height = g_icon_size; icon_data_ptr->pitch = g_icon_size * g_pixel_size; icon_data_ptr->length = g_icon_size * g_icon_size * g_pixel_size; icon_data_ptr->pixels = g_icon_size * g_icon_size; Icon_AddType(icon_data_ptr); assign.type = ICON_TYPE_DEFAULT; assign.index = 0; /* * When a feature is masked, or a masked icon is drawn on * a feature, we may use the icon assigned to a different feature * as the background. */ C_MAKE(g_background, z_info->f_max, int); /* Clear the color hash table */ Palette_ResetHash(); if (init_widget(g_interp, g_icon_depth) != TCL_OK) quit(Tcl_GetStringFromObj(Tcl_GetObjResult(g_interp), NULL)); /* if (CanvasWidget_Init(g_interp) != TCL_OK) quit(Tcl_GetStringFromObj(Tcl_GetObjResult(g_interp), NULL)); */ } #include #ifndef USHRT_MAX #define USHRT_MAX 65535 #endif int PixelPtrToLong(IconPtr p, int bypp); void PixelLongToPtr(IconPtr dst, int pixel, int bypp); /* Hack -- Standard 16 "term" colors. User should be able to change */ int g_term_palette[16] = {255, 0, 250, 17, 217, 196, 199, 101, 129, 247, 30, 5, 35, 185, 180, 52}; /* Actual 8/16/24 pixel values for above */ unsigned long g_term_colormap[16]; static int InitPixelSize(Tcl_Interp *interp) { BitmapType bitmap; bitmap.width = bitmap.height = 10; bitmap.depth = g_icon_depth; Bitmap_New(interp, &bitmap); g_pixel_size = bitmap.pixelSize; Bitmap_Delete(&bitmap); return TCL_OK; } RGBInfo g_rgbi; static int CountBits(unsigned long mask) { int n; for (n = 0; mask != 0; mask &= mask - 1) n++; return n; } static void InitRGBInfo(Tcl_Interp *interp) { Tk_Window tkwin = Tk_MainWindow(interp); Visual *visual = Tk_Visual(tkwin); g_rgbi.red_mask = visual->red_mask; g_rgbi.green_mask = visual->green_mask; g_rgbi.blue_mask = visual->blue_mask; #ifdef PLATFORM_WIN /* XXX Always 5-5-5 */ g_rgbi.red_mask = 0x7c00; g_rgbi.green_mask = 0x03e0; g_rgbi.blue_mask = 0x001f; #endif g_rgbi.red_count = CountBits(g_rgbi.red_mask); g_rgbi.green_count = CountBits(g_rgbi.green_mask); g_rgbi.blue_count = CountBits(g_rgbi.blue_mask); g_rgbi.red_shift = g_rgbi.red_count + g_rgbi.green_count + g_rgbi.blue_count - 8; g_rgbi.green_shift = g_rgbi.green_count + g_rgbi.blue_count - 8; g_rgbi.blue_shift = -(g_rgbi.blue_count - 8); g_rgbi.extra = ~(g_rgbi.red_mask | g_rgbi.green_mask | g_rgbi.blue_mask); } static void SetPix8(unsigned char *p, int r, int g, int b) { /* NOTE: Not Colormap */ *p = Palette_RGB2Index(r, g, b); } void SetPix16(unsigned char *p, int r, int g, int b) { int r2 = (r << g_rgbi.red_shift) & g_rgbi.red_mask; int g2 = (g << g_rgbi.green_shift) & g_rgbi.green_mask; int b2 = (b >> g_rgbi.blue_shift) & g_rgbi.blue_mask; *((unsigned short *) p) = g_rgbi.extra | r2 | g2 | b2; } static void SetPix24(unsigned char *p, int r, int g, int b) { *p++ = b; *p++ = g; *p++ = r; if (g_pixel_size == 4) *p++ = 0xFF; } void PixelSet_RGB(IconPtr dst, int r, int g, int b, int bypp) { switch (bypp) { case 1: SetPix8(dst, r, g, b); break; case 2: SetPix16(dst, r, g, b); break; case 3: case 4: SetPix24(dst, r, g, b); break; } } void PixelLongToPtr(IconPtr p, int pixel, int bypp) { switch (bypp) { case 1: *p = (unsigned char) pixel; break; case 2: *((unsigned short *) p) = pixel & 0xFFFF; break; case 3: { unsigned char *p2 = (unsigned char *) &pixel; *p++ = *p2++; *p++ = *p2++; *p++ = *p2++; break; } case 4: *((unsigned long *) p) = pixel; break; } } int PixelPtrToLong(IconPtr p, int bypp) { switch (bypp) { case 1: return *p; case 2: return *(unsigned short *) p; case 3: return p[0] + (p[1] << 8) + (p[2] << 16); /* ??? */ case 4: return *(int *) p; } return 0; } /* * objcmd_icon -- */ static int objcmd_icon(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { static cptr cmdOption[] = {"size", NULL}; enum {IDX_SIZE}; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); /* Hack - ignore parameter */ (void) dummy; /* Required number of arguments */ if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } /* Get requested option */ if (Tcl_GetIndexFromObj(interp, objv[1], cmdOption, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } switch (option) { case IDX_SIZE: /* size */ Tcl_SetIntObj(resultPtr, g_icon_size); break; } /* Success */ return TCL_OK; } /* * Initialization. */ int Icon_Init(Tcl_Interp *interp, int size, int depth) { /* Only initialize once */ if (g_icon_size != 0) return TCL_OK; /* Require a known icon size */ if ((size != 16) && (size != 24) && (size != 32)) { Tcl_SetStringObj(Tcl_GetObjResult(interp), format("invalid icon size \"%d\": must be 16, 24 or 32", size), -1); return TCL_ERROR; } /* Require a known icon depth */ if ((depth != 8) && (depth != 16) && (depth != 24)) { Tcl_SetStringObj(Tcl_GetObjResult(interp), format("invalid icon depth \"%d\": must be 8, 16 or 24", depth), -1); return TCL_ERROR; } /* Remember the requested icon size */ g_icon_size = size; /* Remember the requested icon depth */ g_icon_depth = depth; if (InitPixelSize(interp) != TCL_OK) { return TCL_ERROR; } /* Remember number of pixels in an icon */ g_icon_pixels = size * size; /* Remember the number of bytes in an icon */ g_icon_length = g_icon_pixels * g_pixel_size; /***** RETURN ERROR IF PALETTE NOT INITIALIZED *****/ g_palette_rgb = Palette_GetRGB(); if (g_icon_depth == 16) InitRGBInfo(interp); /* New block here */ { int i, paletteIndex; for (i = 0; i < 16; i++) { paletteIndex = g_term_palette[i]; switch (g_icon_depth) { case 8: g_term_colormap[i] = g_palette2colormap[paletteIndex]; break; case 16: { unsigned short pix16; unsigned char *rgb = &g_palette_rgb[paletteIndex * 3]; SetPix16((unsigned char *) &pix16, rgb[0], rgb[1], rgb[2]); g_term_colormap[i] = pix16; break; } case 24: { unsigned char *rgb = &g_palette_rgb[paletteIndex * 3]; unsigned char *pix24 = (unsigned char *) &g_term_colormap[i]; SetPix24(pix24 + 1, rgb[0], rgb[1], rgb[2]); break; } } } } /* * This is an array of t_icon_data types, which specify * the icon data and optional mask data for each type of * icon. Icon types are defined through the "icon createtype" * command. */ MAKE(g_icon_data, t_icon_data); g_icon_data_count = 0; /* * This hash table maps symbolic names of icon types (as defined * through the "icon createtype" command) to indexes into * the g_icon_data[] array above. */ Tcl_InitHashTable(&g_icon_table, TCL_STRING_KEYS); Tcl_CreateObjCommand(interp, "icon", objcmd_icon, NULL, NULL); return TCL_OK; } void Icon_Exit(void) { int i; if (g_icon_size == 0) return; /* Check each icon type */ for (i = 0; i < g_icon_data_count; i++) { t_icon_data *iconDataPtr = &g_icon_data[i]; /* Help the memory debugger */ if (iconDataPtr->icon_data) FREE(iconDataPtr->icon_data); } } zangband/src/tk/interp.c0000644000000000000000000010237010250356275014222 0ustar rootroot/* File: interp.c */ /* Purpose: general script commands */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" #include "icon.h" /* TRUE if current command is repeated */ bool command_repeating = FALSE; int inkey_flags = 0; int inkey_book; int exit_skip_save = FALSE; /* * Return a Tcl list of m_list[] indexes of pets */ static Tcl_Obj *DumpPets(void) { int i; Tcl_Obj *listObjPtr; /* Create a new Tcl list object */ listObjPtr = Tcl_NewListObj(0, NULL); /* Process the monsters */ for (i = 1; i < m_max; i++) { /* Access the monster */ monster_type *m_ptr = &m_list[i]; /* Ignore "dead" monsters */ if (!m_ptr->r_idx) continue; /* Append m_list[] index of friendly monster */ if (is_pet(m_ptr)) { Tcl_ListObjAppendElement(g_interp, listObjPtr, Tcl_NewIntObj(i)); } } return listObjPtr; } static int s_status_value; /* * Prints status of hunger */ static cptr state_hunger(void) { /* Fainting / Starving */ if (p_ptr->food < PY_FOOD_FAINT) { return "Weak"; } /* Weak */ else if (p_ptr->food < PY_FOOD_WEAK) { return "Weak"; } /* Hungry */ else if (p_ptr->food < PY_FOOD_ALERT) { return "Hungry"; } /* Normal */ else if (p_ptr->food < PY_FOOD_FULL) { return ""; } /* Full */ else if (p_ptr->food < PY_FOOD_MAX) { return "Full"; } /* Gorged */ else { return "Gorged"; } } /* * Prints Blind status */ static cptr state_blind(void) { if (p_ptr->tim.blind) { return "Blind"; } else { return ""; } } /* * Prints Confusion status */ static cptr state_confused(void) { if (p_ptr->tim.confused) { return "Confused"; } else { return ""; } } /* * Prints Fear status */ static cptr state_afraid(void) { if (p_ptr->tim.afraid) { return "Afraid"; } else { return ""; } } /* * Prints Poisoned status */ static cptr state_poisoned(void) { if (p_ptr->tim.poisoned) { return "Poisoned"; } else { return ""; } } static int trunc_num(int n) { /* Only 4 digits are allowed */ if (n > 9999) n = 9999; /* Extensive */ if (n >= 1000) { return (n / 100) * 100; } /* Long */ else if (n >= 100) { return (n / 10) * 10; } /* Medium */ else if (n >= 10) { return (n / 5) * 5; } /* Short */ return n; } /* * Prints Searching, Resting, Paralysis, or 'count' status * Display is always exactly 10 characters wide (see below) * * This function was a major bottleneck when resting, so a lot of * the text formatting code was optimized in place below. */ static cptr state_state(void) { /* Paralysis */ if (p_ptr->tim.paralyzed) { return "Paralyzed!"; } /* Resting */ else if (p_ptr->state.resting) { int n = p_ptr->state.resting; /* Rest until healed */ if (n == -1) { return "Rest *****"; } /* Rest until done */ else if (n == -2) { return "Rest &&&&&"; } else { s_status_value = trunc_num(n); return "Rest %d"; } } /* Repeating */ else if (p_ptr->cmd.rep) { int n = p_ptr->cmd.rep; s_status_value = trunc_num(n); if (n > 999) { return "Rep. %d"; } else { return "Repeat %d"; } } /* Searching */ else if (p_ptr->state.searching) { return "Searching"; } /* Nothing interesting */ else { return ""; } } /* * Prints the speed of a character. -CJS- */ static cptr state_speed(void) { int n = p_ptr->pspeed; /* Hack -- Visually "undo" the Search Mode Slowdown */ if (p_ptr->state.searching) n += 10; /* Fast */ if (n > 110) { s_status_value = n - 110; return "Fast (%+d)"; } /* Slow */ else if (n < 110) { s_status_value = -(110 - n); return "Slow (%+d)"; } /* Normal */ return ""; } static cptr state_study(void) { if (p_ptr->new_spells) { return "Study"; } else { return ""; } } static cptr state_cut(void) { int c = p_ptr->tim.cut; if (c > 1000) { return "Mortal wound"; } else if (c > 200) { return "Deep gash"; } else if (c > 100) { return "Severe cut"; } else if (c > 50) { return "Nasty cut"; } else if (c > 25) { return "Bad cut"; } else if (c > 10) { return "Light cut"; } else if (c) { return "Graze"; } else { return ""; } } static cptr state_stun(void) { int s = p_ptr->tim.stun; if (s > 100) { return "Knocked out"; } else if (s > 50) { return "Heavy stun"; } else if (s) { return "Stun"; } else { return ""; } } static cptr state_winner(void) { /* Wizard */ if (p_ptr->state.wizard) { return "Wizard"; } /* Winner */ else if (p_ptr->state.total_winner || (p_ptr->lev > PY_MAX_LEVEL)) { return "Winner"; } /* Normal */ else { return ""; } } cptr player_status(int status, int *value) { cptr format; typedef cptr (status_proc)(void); static status_proc *status_info[] = { state_cut, state_stun, state_hunger, state_blind, state_confused, state_afraid, state_poisoned, state_state, state_speed, state_study, state_winner }; s_status_value = 0; format = (*status_info[status])(); (*value) = s_status_value; return format; } static void blows_per_round(int *_blows, int *_muta_att) { int muta_att = 0; if (p_ptr->muta2 & MUT2_HORNS) muta_att++; if (p_ptr->muta2 & MUT2_SCOR_TAIL) muta_att++; if (p_ptr->muta2 & MUT2_BEAK) muta_att++; if (p_ptr->muta2 & MUT2_TRUNK) muta_att++; if (p_ptr->muta2 & MUT2_TENTACLES) muta_att++; (*_blows) = p_ptr->num_blow; (*_muta_att) = muta_att; } static void shots_per_round(int *_shots, int *_shots_frac) { int energy_fire = 100; int shots, shots_frac; object_type *o_ptr = &p_ptr->equipment[EQUIP_BOW]; if (o_ptr->k_idx) { switch (o_ptr->sval) { case SV_SLING: { energy_fire = 50; break; } case SV_SHORT_BOW: { energy_fire = 100; break; } case SV_LONG_BOW: { energy_fire = 100; break; } case SV_LIGHT_XBOW: { energy_fire = 120; break; } case SV_HEAVY_XBOW: { if (p_ptr->stat[A_DEX].use >= 16) { energy_fire = 150; } else { /* players with low dex will take longer to load */ energy_fire = 200; } } break; } } shots = p_ptr->num_fire * 100; shots_frac = (shots * 100 / energy_fire) % 100; shots = shots / energy_fire; (*_shots) = shots; (*_shots_frac) = shots_frac; } /* *-------------------------------------------------------------- * * objcmd_player -- * * Implements the "player" script command. * *-------------------------------------------------------------- */ int objcmd_player(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; static cptr cmdOptions[] = {"ability", "age", "armor_class", "blows_per_round", "died_from", "exp", "food", "gold", "height", "hitpoints", "infravision", "level", "mana", "position", "sex", "shots_per_round", "social_class", "title", "to_dam", "to_hit", "weight", "total_weight", "preserve", "base_name", "is_dead", "turn", "max_level", "disturb", "new_spells", "command_rep", "running", "prayer_or_spell", "health_who", "monster_race_idx", "life_rating", "pets", "realm1", "realm2", "patron", NULL}; enum {IDX_ABILITY, IDX_AGE, IDX_ARMOR_CLASS, IDX_BLOWS_PER_ROUND, IDX_DIED_FROM, IDX_EXP, IDX_FOOD, IDX_GOLD, IDX_HEIGHT, IDX_HITPOINTS, IDX_INFRAVISION, IDX_LEVEL, IDX_MANA, IDX_POSITION, IDX_SEX, IDX_SHOTS_PER_ROUND, IDX_SOCIAL_CLASS, IDX_TITLE, IDX_TO_DAM, IDX_TO_HIT, IDX_WEIGHT, IDX_TOTAL_WEIGHT, IDX_PRESERVE, IDX_BASE_NAME, IDX_IS_DEAD, IDX_TURN, IDX_MAX_LEVEL, IDX_DISTURB, IDX_NEW_SPELLS, IDX_COMMAND_REP, IDX_RUNNING, IDX_PRAYER_OR_SPELL, IDX_HEALTH_WHO, IDX_MONSTER_RACE_IDX, IDX_LIFE_RATING, IDX_PETS, IDX_REALM1, IDX_REALM2, IDX_PATRON }; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); int index; object_type *o_ptr; int i, tmp; long expadv; cptr t; static cptr abilityOptions[] = {"fighting", "bows_throw", "saving_throw", "stealth", "perception", "searching", "disarming", "magic_device", NULL}; static struct {int rating; int max;} ability[] = { {0, 10}, /* fighting */ {0, 10}, /* bows_throw */ {0, 6}, /* saving_throw */ {0, 1}, /* stealth */ {0, 6}, /* perception */ {0, 6}, /* searching */ {0, 8}, /* disarming */ {0, 6} /* magic_device */ }; /* Required number of arguments */ if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } /* Get requested option */ if (Tcl_GetIndexFromObj(interp, objV[1], cmdOptions, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } switch (option) { case IDX_ABILITY: /* ability */ if (objC != 3) { Tcl_WrongNumArgs(interp, infoCmd->depth + 2, objv, "ability"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[2], abilityOptions, "ability", 0, &index) != TCL_OK) { return TCL_ERROR; } /* Fighting Skill (with current weapon) */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; tmp = p_ptr->to_h + o_ptr->to_h; ability[0].rating = p_ptr->skills[SKILL_THN] + (tmp * BTH_PLUS_ADJ); /* Shooting Skill (with current bow and normal missile) */ o_ptr = &p_ptr->equipment[EQUIP_BOW]; tmp = p_ptr->to_h + o_ptr->to_h; ability[1].rating = p_ptr->skills[SKILL_THB] + (tmp * BTH_PLUS_ADJ); ability[2].rating = p_ptr->skills[SKILL_SAV]; ability[3].rating = p_ptr->skills[SKILL_STL]; ability[4].rating = p_ptr->skills[SKILL_FOS]; ability[5].rating = p_ptr->skills[SKILL_SNS]; ability[6].rating = p_ptr->skills[SKILL_DIS]; ability[7].rating = p_ptr->skills[SKILL_DEV]; Tcl_SetStringObj(resultPtr, format("%d %d", ability[index].rating, ability[index].max), -1); break; case IDX_AGE: /* age */ Tcl_SetIntObj(resultPtr, p_ptr->rp.age); break; case IDX_ARMOR_CLASS: /* armor_class */ Tcl_SetStringObj(resultPtr, format("%d %d", p_ptr->dis_ac, p_ptr->dis_to_a), -1); break; case IDX_BLOWS_PER_ROUND: /* blows_per_round */ { int blows, muta_att; blows_per_round(&blows, &muta_att); Tcl_SetStringObj(resultPtr, format(muta_att ? "%d+%d" : "%d", blows, muta_att), -1); break; } case IDX_DIED_FROM: /* died_from */ if (!p_ptr->state.is_dead) { Tcl_SetStringObj(resultPtr, "character is not dead", -1); return TCL_ERROR; } ExtToUtf_SetResult(interp, p_ptr->state.died_from); break; case IDX_EXP: /* exp */ if (p_ptr->lev >= PY_MAX_LEVEL) expadv = 999999999; else expadv = (s32b)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L); Tcl_SetStringObj(resultPtr, format("%ld %ld %ld", p_ptr->exp, p_ptr->max_exp, expadv), -1); break; case IDX_FOOD: /* food */ Tcl_SetStringObj(resultPtr, format("%d %d", p_ptr->food, PY_FOOD_MAX), -1); break; case IDX_GOLD: /* gold */ Tcl_SetStringObj(resultPtr, format("%ld", p_ptr->au), -1); break; case IDX_HEIGHT: /* height */ Tcl_SetIntObj(resultPtr, p_ptr->rp.ht); break; case IDX_HITPOINTS: /* hitpoints */ Tcl_SetStringObj(resultPtr, format("%d %d", p_ptr->chp, p_ptr->mhp), -1); break; case IDX_INFRAVISION: /* infravision */ Tcl_SetIntObj(resultPtr, p_ptr->see_infra * 10); break; case IDX_LEVEL: /* level */ Tcl_SetIntObj(resultPtr, p_ptr->lev); break; case IDX_MANA: /* mana */ Tcl_SetStringObj(resultPtr, format("%d %d", p_ptr->csp, p_ptr->msp), -1); break; case IDX_POSITION: /* position */ Tcl_SetStringObj(resultPtr, format("%d %d", p_ptr->py, p_ptr->px), -1); break; case IDX_SEX: /* sex */ Tcl_SetStringObj(resultPtr, sp_ptr->title, -1); break; case IDX_SHOTS_PER_ROUND: /* shots_per_round */ { int shots, shots_frac; shots_per_round(&shots, &shots_frac); Tcl_SetStringObj(resultPtr, format("%d.%d", shots, shots_frac), -1); break; } case IDX_SOCIAL_CLASS: /* social_class */ Tcl_SetIntObj(resultPtr, p_ptr->rp.sc); break; case IDX_TITLE: /* title */ ExtToUtf_SetResult(interp, player_title[p_ptr->rp.pclass][(p_ptr->lev-1)/5]); break; case IDX_TO_DAM: /* to_dam */ Tcl_SetIntObj(resultPtr, p_ptr->dis_to_d); break; case IDX_TO_HIT: /* to_hit */ Tcl_SetIntObj(resultPtr, p_ptr->dis_to_h); break; case IDX_WEIGHT: /* weight */ Tcl_SetIntObj(resultPtr, p_ptr->rp.wt); break; case IDX_TOTAL_WEIGHT: /* total_weight */ Tcl_SetIntObj(resultPtr, p_ptr->total_weight); break; case IDX_PRESERVE: /* preserve */ Tcl_SetIntObj(resultPtr, preserve_mode); break; case IDX_BASE_NAME: /* base_name */ ExtToUtf_SetResult(interp, player_base); break; case IDX_IS_DEAD: /* is_dead */ Tcl_SetBooleanObj(resultPtr, p_ptr->state.is_dead); break; case IDX_TURN: /* turn */ Tcl_SetLongObj(resultPtr, turn); break; case IDX_MAX_LEVEL: /* max_level */ Tcl_SetIntObj(resultPtr, p_ptr->max_lev); break; case IDX_DISTURB: /* disturb */ /* When is this allowed? */ if (inkey_flags == 0) { disturb(FALSE); } break; case IDX_NEW_SPELLS: /* new_spells */ if (!p_ptr->spell.r[0].realm) { Tcl_SetStringObj(resultPtr, "character cannot read books", -1); return TCL_ERROR; } Tcl_SetIntObj(resultPtr, p_ptr->new_spells); break; case IDX_COMMAND_REP: /* command_rep */ Tcl_SetIntObj(resultPtr, p_ptr->cmd.rep); break; case IDX_RUNNING: /* running */ Tcl_SetIntObj(resultPtr, p_ptr->state.running); break; case IDX_PRAYER_OR_SPELL: /* prayer_or_spell */ if (!p_ptr->spell.r[0].realm) { Tcl_SetStringObj(resultPtr, "character cannot read books", -1); return TCL_ERROR; } switch (mp_ptr->spell_book) { case TV_LIFE_BOOK: t = "prayer"; break; default: t = "spell"; break; } if (t == NULL) { quit_fmt("unhandled mp_ptr->spell_book %d", mp_ptr->spell_book); } Tcl_SetStringObj(resultPtr, t, -1); break; case IDX_HEALTH_WHO: /* health_who */ /* * Should I call health_track() to set PW_HEALTH? * Should I call handle_stuff() to update the display? */ if (objC == 3) { int m_idx; if (Tcl_GetIntFromObj(interp, objV[2], &m_idx) != TCL_OK) { return TCL_ERROR; } if ((m_idx < 0) || (m_idx >= m_max)) { Tcl_SetStringObj(resultPtr, format("bad m_list index \"%d\": must be between 0 and %d", m_idx, (int) m_max - 1), -1); } p_ptr->health_who = m_idx; break; } Tcl_SetIntObj(resultPtr, p_ptr->health_who); break; case IDX_MONSTER_RACE_IDX: /* monster_race_idx */ /* * Should I call monster_race_track() to set PW_MONSTER? * Should I call handle_stuff() to update the display? */ if (objC == 3) { int r_idx; if (Tcl_GetIntFromObj(interp, objV[2], &r_idx) != TCL_OK) { return TCL_ERROR; } if (!((r_idx >= 0) && (r_idx < z_info->r_max))) { Tcl_SetStringObj(resultPtr, format("bad r_info index \"%d\": must be between 0 and %d", r_idx, (int) z_info->r_max - 1), -1); return TCL_ERROR; } p_ptr->monster_race_idx = r_idx; break; } Tcl_SetIntObj(resultPtr, p_ptr->monster_race_idx); break; case IDX_LIFE_RATING: /* life_rating */ i = (int) (((long) p_ptr->player_hp[PY_MAX_LEVEL - 1] * 200L) / (2 * p_ptr->rp.hitdie + ((PY_MAX_LEVEL - 1) * (p_ptr->rp.hitdie + 1)))); Tcl_SetIntObj(resultPtr, i); break; case IDX_PETS: /* pets */ Tcl_SetObjResult(interp, DumpPets()); break; case IDX_REALM1: /* realm1 */ Tcl_SetStringObj(resultPtr, realm_names[p_ptr->spell.r[0].realm], -1); break; case IDX_REALM2: /* realm2 */ Tcl_SetStringObj(resultPtr, realm_names[p_ptr->spell.r[1].realm], -1); break; case IDX_PATRON: /* patron */ ExtToUtf_SetResult(interp, chaos_patrons[p_ptr->chaos_patron]); break; } return TCL_OK; } /* *-------------------------------------------------------------- * * angtk_eval -- * * Eval() a command with arguments. * *-------------------------------------------------------------- */ void angtk_eval(cptr command, ...) { cptr s = command; va_list vp; int objc = 0; Tcl_Obj *objv[40]; int i, result; /* Start processing variable argument list */ va_start(vp, command); /* Process each string argument */ while (s) { /* Append a new string object to the command object */ /* XXX Some args are already ASCII, safe to translate? */ objv[objc++] = ExtToUtf_NewStringObj(s, -1); Tcl_IncrRefCount(objv[objc - 1]); /* Get the next string argument */ s = va_arg(vp, cptr); } /* Finish processing variable argument list */ va_end(vp); result = Tcl_EvalObjv(g_interp, objc, objv, TCL_EVAL_GLOBAL); for (i = 0; i < objc; i++) { Tcl_DecrRefCount(objv[i]); } if (result == TCL_ERROR) { /* Report the error */ Tcl_AddErrorInfo(g_interp, "\n (inside angtk_eval)"); Tcl_BackgroundError(g_interp); } } static void HandleError(void) { char path[1024]; cptr errorInfo; FILE *fp; /* Dump the stack to errors.txt */ path_make(path, ANGBAND_DIR_TK, "errors.txt"); fp = fopen(path, "a"); if (fp != NULL) { errorInfo = Tcl_GetVar(g_interp, "errorInfo", TCL_GLOBAL_ONLY); fprintf(fp, "***** (inside HandleError)\n\n%s\n\n", errorInfo); fclose(fp); } /* Display a message and quit */ quit_fmt("The following error occurred:\n\n%s\n\n" "Please examine the errors.txt file to see what happened.", Tcl_GetStringResult(g_interp)); } static CommandInit commandInit[] = { {0, "angband", 0, 0, NULL, NULL, (ClientData) 0}, {1, "cave", 0, 0, NULL, objcmd_cave, (ClientData) 0}, {1, "game", 0, 0, NULL, objcmd_game, (ClientData) 0}, {1, "inkey_flags", 1, 1, NULL, objcmd_inkey_flags, (ClientData) 0}, {1, "inventory", 0, 0, NULL, objcmd_inventory, (ClientData) 0}, {1, "keypress", 2, 2, "string", objcmd_keypress, (ClientData) 0}, {1, "message", 0, 0, NULL, objcmd_message, (ClientData) 0}, {1, "player", 0, 0, NULL, objcmd_player, (ClientData) 0}, {1, "equipinfo", 3, 3, "slot arrayName", objcmd_equipinfo, (ClientData) 0}, {1, "inveninfo", 3, 3, "slot arrayName", objcmd_inveninfo, (ClientData) 0}, {1, "init_icons", 3, 3, "size depth", objcmd_init_icons, (ClientData) 0}, {1, "floor", 0, 0, NULL, objcmd_floor, (ClientData) 0}, {1, "keycount", 0, 0, NULL, objcmd_keycount, (ClientData) 0}, {0, "fontdesc", 2, 2, "font", objcmd_fontdesc, (ClientData) 0}, {0, NULL, 0, 0, NULL, NULL, (ClientData) 0} }; /* * Initialize stuff after Tcl/Tk but before a game is started. */ void angtk_init(void) { char path[1024]; /* Tcl commands */ CommandInfo_Init(g_interp, commandInit, NULL); /* Standard color palette */ init_palette(); /* Source the "startup script" */ path_make(path, ANGBAND_DIR_TK, "init-startup.tcl"); if (angtk_eval_file(path) == TCL_ERROR) { HandleError(); } } /* * Initialize stuff after init_angband(). */ void angtk_angband_initialized(void) { char path[1024]; /* Program is intialized */ if (Tcl_EvalEx(g_interp, "angband_initialized", -1, TCL_EVAL_GLOBAL) != TCL_OK) { HandleError(); } /* Source a file to create the interface */ path_make(path, ANGBAND_DIR_TK, "init-other.tcl"); if (angtk_eval_file(path) == TCL_ERROR) { HandleError(); } /* The icon environment must be initialized by a script. */ if (g_icon_size == 0) { quit_fmt("Fatal error:\nIcons were not initialized.\n" "You must call \"angband init_icons\""); } } /* * Tcl_Eval() a file, assuming the given filename is not UTF-8. */ int angtk_eval_file(cptr extFileName) { cptr utfFileName; Tcl_DString dString; int result; utfFileName = Tcl_ExternalToUtfDString(NULL, extFileName, -1, &dString); result = Tcl_EvalFile(g_interp, utfFileName); Tcl_DStringFree(&dString); return result; } /* *-------------------------------------------------------------- * * objcmd_cave -- * * Implements the "cave" script command. * Syntax: * cave blocked y x -- can player move there * cave examine y x -- describe what's there * cave height -- height of cave * cave width -- width of cave * cave info -- get info about a grid * cave wild_name -- get name of wilderness area * *-------------------------------------------------------------- */ int objcmd_cave(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; static cptr cmdOptions[] = {"wild_name", NULL}; enum {IDX_WILD_NAME}; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[1], cmdOptions, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } switch (option) { case IDX_WILD_NAME: /* wild_name */ if (!character_dungeon) { /* Set the error */ Tcl_SetStringObj(resultPtr, "dungeon has not been generated yet", -1); goto error; } if (!p_ptr->depth) { if (p_ptr->place_num) { ExtToUtf_SetResult(interp, place[p_ptr->place_num].name); } else { Tcl_SetStringObj(resultPtr, "Wilderness", -1); } } break; } /* Success */ return TCL_OK; error: /* Failure */ return TCL_ERROR; } /* *-------------------------------------------------------------- * * objcmd_floor -- * * Implements the "floor" script command. * Syntax: * * floor find SEARCHCOMMAND ?arg arg ...? * Return list of indexes of matching objects * floor info INDEX arrayName * floor memory INDEX * *-------------------------------------------------------------- */ int objcmd_floor(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; static cptr cmdOptions[] = {"find", "memory", NULL}; enum {IDX_FIND, IDX_MEMORY}; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); int index; Tcl_Obj *listObjPtr; char *buffer; int i; long length; object_type *o_ptr; int fy, fx; /* Default to finding all matches */ int request_limit = 0, match_limit = 0, cnt = 0; /* Default to ignoring item_tester_okay() hook */ int request_tester = 0, match_tester = 0; /* Default to no restriction on tval */ int request_tval = 0, match_tval[10], tval_cnt = 0; if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[1], cmdOptions, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } /* XXX Hack -- Determine the location to display */ fy = p_ptr->py; fx = p_ptr->px; switch (option) { case IDX_FIND: /* find */ { bool (*old_tester_hook)(const object_type *) = item_tester_hook; bool (*temp_tester_hook)(const object_type *) = NULL; /* Scan arguments for options */ for (i = 2; i < objC; ) { static cptr cmdOptions[] = {"-limit", "-tester", NULL}; /* Get the sub-option */ if (Tcl_GetIndexFromObj(interp, objV[i], cmdOptions, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch (index) { case 0: /* Limit */ { if (Tcl_GetIntFromObj(interp, objV[i+1], &match_limit) != TCL_OK) { return TCL_ERROR; } request_limit = 1; i += 2; break; } case 1: /* Tester */ { if (Tcl_GetBooleanFromObj(interp, objV[i+1], &match_tester) != TCL_OK) { return TCL_ERROR; } request_tester = 1; i += 2; break; } } } if (temp_tester_hook) item_tester_hook = temp_tester_hook; /* Return a list of o_list[] indexes */ listObjPtr = Tcl_NewListObj(0, NULL); /* Scan all objects in the grid */ OBJ_ITT_START (area(fx, fy)->o_idx, o_ptr) { if (request_tester && match_tester) { /* Accept TV_GOLD if no tester */ if ((o_ptr->tval == TV_GOLD) && !item_tester_hook && !item_tester_tval) { /* Nothing */ } else if (!item_tester_okay(o_ptr)) { continue; } } if (request_tval) { for (i = 0; i < tval_cnt; i++) { if (match_tval[0] == o_ptr->tval) break; } if (i == tval_cnt) continue; } /* Found a match */ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(_this_o_idx)); /* Return x matches */ if (request_limit && (++cnt >= match_limit)) break; } OBJ_ITT_END; /* XXX Hack -- Restore the hook */ item_tester_hook = old_tester_hook; /* Return a list of o_list[] indexes */ Tcl_SetObjResult(interp, listObjPtr); break; } case IDX_MEMORY: /* memory */ if (Tcl_GetIntFromObj(interp, objV[2], &i) != TCL_OK) { return TCL_ERROR; } if (i <= 0 || i > o_max) goto bad_index; /* Get item info */ o_ptr = &o_list[i]; /* Illegal */ if (!o_ptr->k_idx || (o_ptr->iy != fy) || (o_ptr->ix != fx)) { goto bad_index; } C_MAKE(buffer, 5 * 1024L, char); length = angtk_describe_object(o_ptr, buffer, FALSE); Tcl_SetObjResult(interp, ExtToUtf_NewStringObj(buffer, length)); FREE(buffer); break; } return TCL_OK; bad_index: Tcl_SetStringObj(resultPtr, format("bad floor index \"%d\"", i), -1); return TCL_ERROR; } /* *-------------------------------------------------------------- * * objcmd_game -- * * Implements the "game" script command. * Syntax: * game abort ?confirm? -- Quit without saving * game directory -- Get a directory pathname * game keymap_dump -- Dump a keymap file * game new -- Start a new game * game open -- Open a save file * game process_pref_file -- Process a preference file * game quit -- Quit with save * *-------------------------------------------------------------- */ /* List of directory keywords */ cptr keyword_path[] = { "ANGBAND_DIR_ROOT", "ANGBAND_DIR_USER", "ANGBAND_DIR_TK", NULL }; int objcmd_game(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; static cptr cmdOptions[] = {"abort", "tkdir" "version", NULL}; enum {IDX_ABORT, IDX_TKDIR, IDX_VERSION}; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); int index; if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[1], cmdOptions, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } switch (option) { case IDX_ABORT: /* abort */ { int confirm = 1; if (objC == 3) { static cptr abortSwitch[] = {"-noask", NULL}; if (Tcl_GetIndexFromObj(interp, objV[2], abortSwitch, "switch", 0, &index) != TCL_OK) { return TCL_ERROR; } confirm = 0; } if (confirm && game_in_progress && character_generated) { int result; result = Tcl_EvalEx(g_interp, "tk_messageBox -icon warning -type okcancel -message \"Your character will not be saved!\" -title \"Quit Without Saving\"", -1, TCL_EVAL_GLOBAL); if (result == TCL_OK) { cptr s = Tcl_GetStringResult(g_interp); if (!strcmp(s, "cancel")) break; } } quit(NULL); break; } case IDX_TKDIR: /* Tk directory for game .tcl files */ { /* Return the current directory path */ ExtToUtf_SetResult(interp, ANGBAND_DIR_TK); break; } case IDX_VERSION: /* version */ Tcl_SetStringObj(resultPtr, format("%d.%d.%d", VER_MAJOR, VER_MINOR, VER_PATCH), -1); break; } return TCL_OK; } /* init_icons $size $depth */ int objcmd_init_icons(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; /* int objC = objc - infoCmd->depth; */ Tcl_Obj *CONST *objV = objv + infoCmd->depth; int size, depth; /* Hack - ignore parameter */ (void) objc; if (g_icon_size) { Tcl_SetResult(interp, (char *) "icons were already initialized", TCL_VOLATILE); return TCL_ERROR; } /* Get the size */ if (Tcl_GetIntFromObj(interp, objV[1], &size) != TCL_OK) { return TCL_ERROR; } /* Get the depth */ if (Tcl_GetIntFromObj(interp, objV[2], &depth) != TCL_OK) { return TCL_ERROR; } /* Initialize (quit on failure) */ init_icons(size, depth); return TCL_OK; } /* Strings returned by "inkey_flags" command, indexed by INKEY_XXX defines. */ cptr inkey_to_str[] = {"", "INKEY_CMD", "INKEY_DIR", "INKEY_DISTURB", "INKEY_ITEM", "INKEY_ITEM_STORE", "INKEY_MORE", "INKEY_SPELL", "INKEY_TARGET", "INKEY_POWER", "INKEY_CMD_PET", NULL}; /* (inkey) flags */ int objcmd_inkey_flags(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { /* Hack - ignore parameters */ (void) objc; (void) objv; (void) clientData; Tcl_SetResult(interp, (char *) inkey_to_str[inkey_flags], TCL_VOLATILE); return TCL_OK; } /* *-------------------------------------------------------------- * * objcmd_inventory -- * * Implements the "inventory" script command. * Syntax: * * inventory count * Return number of inventory items carried * * inventory find SEARCHCOMMAND ?arg arg ...? * Return list of indexes of matching objects * * inventory info INDEX VARNAME * Return info about specific object * * inventory memory INDEX * Return memory about about specific object * * inventory total_weight * Return total weight carried * * inventory weight_limit * Return carrying capacity in 10ths of pounds * *-------------------------------------------------------------- */ int objcmd_inventory(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; static cptr cmdOptions[] = { "total_weight", "weight_limit", NULL}; enum {IDX_TOTAL_WEIGHT, IDX_WEIGHT_LIMIT}; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); int i; if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[1], cmdOptions, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } switch (option) { case IDX_TOTAL_WEIGHT: /* total_weight */ Tcl_SetIntObj(resultPtr, p_ptr->total_weight); break; case IDX_WEIGHT_LIMIT: /* weight_limit */ /* Max carrying capacity in 10ths of pounds */ i = adj_str_wgt[p_ptr->stat[A_STR].ind] * 100; Tcl_SetIntObj(resultPtr, i); break; } return TCL_OK; } /* keycount */ int objcmd_keycount(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { /* Hack - ignore parameters */ (void) objc; (void) objv; (void) clientData; Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Term->key_head != Term->key_tail)); return TCL_OK; } /* keypress $string */ int objcmd_keypress(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; /* int objC = objc - infoCmd->depth; */ Tcl_Obj *CONST *objV = objv + infoCmd->depth; char *t; int i; /* Hack - ignore parameters */ (void) objc; (void) interp; t = Tcl_GetStringFromObj(objV[1], NULL); for (i = 0; t[i]; i++) { Term_keypress(t[i]); } return TCL_OK; } /* *-------------------------------------------------------------- * * objcmd_message -- * * Implements the "message" script command. * Syntax: * message color -- Return color for message $index * message count -- Return number of saved messages * message get $index -- Return most-recent number of messages * message sound $index -- Return sound for message $index * *-------------------------------------------------------------- */ int objcmd_message(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; static cptr cmdOption[] = {"color", "count", "get", NULL}; enum {IDX_COLOR, IDX_COUNT, IDX_GET}; int option; Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); int i, k; byte attr; if (objC < 2) { Tcl_WrongNumArgs(interp, infoCmd->depth + 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objV[1], cmdOption, "option", 0, &option) != TCL_OK) { return TCL_ERROR; } if (!character_generated) { Tcl_AppendStringsToObj(resultPtr, "character has not been generated yet", NULL); return TCL_ERROR; } switch (option) { case IDX_COLOR: /* color */ if (Tcl_GetIntFromObj(interp, objV[2], &i) != TCL_OK) { return TCL_ERROR; } k = message_num(); if (i < 0 || i >= k) { Tcl_SetStringObj(resultPtr, format("invalid message index \"%d\": " "must be from 0 to %d", i, k - 1), -1); return TCL_ERROR; } attr = TERM_WHITE; Tcl_SetStringObj(resultPtr, keyword_term_color[attr], -1); break; case IDX_COUNT: /* count */ Tcl_SetIntObj(resultPtr, message_num()); break; case IDX_GET: /* get */ if (Tcl_GetIntFromObj(interp, objV[2], &i) != TCL_OK) { return TCL_ERROR; } k = message_num(); if (i < 0 || i >= k) { Tcl_SetStringObj(resultPtr, format("invalid message index \"%d\": " "must be from 0 to %d", i, k - 1), -1); return TCL_ERROR; } ExtToUtf_SetResult(interp, message_str(i)); break; } return TCL_OK; } zangband/src/tk/plat.c0000644000000000000000000004231010250356275013656 0ustar rootroot/* File: plat.c */ /* Purpose: platform-specific stuff */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" #include #ifndef USHRT_MAX #define USHRT_MAX 65535 #endif #ifdef PLATFORM_WIN /* #include */ /* HPALETTE in windows headers */ extern void *Palette_GetHPal(void); struct PlatBitmap { HBITMAP hbm; /* Bitmap */ TkWinDrawable twd; /* Pixmap */ TkWinColormap twc; /* Colormap */ }; void Bitmap_New(Tcl_Interp *interp, BitmapPtr bitmapPtr) { int depth = bitmapPtr->depth; struct PlatBitmap *platData; BITMAPINFO *infoPtr; unsigned char *rgb; int i; MAKE(platData, PlatBitmap); if (depth == 8) { /* Allocate temp storage */ C_MAKE(infoPtr, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD), byte); /* Set header fields */ infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); infoPtr->bmiHeader.biWidth = bitmapPtr->width; infoPtr->bmiHeader.biHeight = -bitmapPtr->height; /* Top-down */ infoPtr->bmiHeader.biPlanes = 1; infoPtr->bmiHeader.biBitCount = 8; infoPtr->bmiHeader.biCompression = BI_RGB; infoPtr->bmiHeader.biSizeImage = 0; infoPtr->bmiHeader.biXPelsPerMeter = 0; infoPtr->bmiHeader.biYPelsPerMeter = 0; infoPtr->bmiHeader.biClrUsed = 256; infoPtr->bmiHeader.biClrImportant = 0; rgb = Palette_GetRGB(); /* Colormap_ ? */ /* Copy the colors to the bitmap color table */ for (i = 0; i < 256; i++) { infoPtr->bmiColors[i].rgbRed = *rgb++; infoPtr->bmiColors[i].rgbGreen = *rgb++; infoPtr->bmiColors[i].rgbBlue = *rgb++; infoPtr->bmiColors[i].rgbReserved = 0; } /* Create the bitmap, and get the address of the bits */ platData->hbm = CreateDIBSection(NULL, infoPtr, DIB_RGB_COLORS, (LPVOID) &bitmapPtr->pixelPtr, NULL, 0); /* Create a Colormap */ platData->twc.palette = Palette_GetHPal(); /* Colormap */ platData->twd.bitmap.colormap = (Colormap) &platData->twc; } else { /* Allocate temp storage */ MAKE(infoPtr, BITMAPINFO); /* Set header fields */ infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); infoPtr->bmiHeader.biWidth = bitmapPtr->width; infoPtr->bmiHeader.biHeight = -bitmapPtr->height; /* Top-down */ infoPtr->bmiHeader.biPlanes = 1; infoPtr->bmiHeader.biBitCount = depth; /* 16 or 24 */ infoPtr->bmiHeader.biCompression = BI_RGB; infoPtr->bmiHeader.biSizeImage = 0; infoPtr->bmiHeader.biXPelsPerMeter = 0; infoPtr->bmiHeader.biYPelsPerMeter = 0; infoPtr->bmiHeader.biClrUsed = 0; infoPtr->bmiHeader.biClrImportant = 0; /* Create the bitmap, and get the address of the bits */ platData->hbm = CreateDIBSection(NULL, infoPtr, DIB_RGB_COLORS, (LPVOID) &bitmapPtr->pixelPtr, NULL, 0); platData->twd.bitmap.colormap = Tk_Colormap(Tk_MainWindow(interp)); } /* Free temp storage */ FREE(infoPtr); bitmapPtr->pixelSize = bitmapPtr->depth / 8; bitmapPtr->pitch = bitmapPtr->width * bitmapPtr->pixelSize; if (bitmapPtr->pitch % 4) bitmapPtr->pitch += 4 - bitmapPtr->pitch % 4; /* Now create a Pixmap */ platData->twd.type = TWD_BITMAP; platData->twd.bitmap.depth = depth; platData->twd.bitmap.handle = platData->hbm; bitmapPtr->pixmap = (Pixmap) &platData->twd; bitmapPtr->platData = platData; } void Bitmap_Delete(BitmapPtr bitmapPtr) { struct PlatBitmap *platData = bitmapPtr->platData; DeleteObject(platData->hbm); FREE(platData); bitmapPtr->platData = NULL; } typedef struct _PALETTE { WORD Version; WORD NumberOfEntries; PALETTEENTRY aEntries[256]; } _PALETTE; void *Plat_PaletteInit(unsigned char *rgb) { int i; /* The 'logical' palette we will use */ /* "0x300" = Windows 3.0 or later */ /* "256" = Number of colors */ _PALETTE LogicalPalette = {0x300, 256}; for (i = 0; i < 256; i++) { LogicalPalette.aEntries[i].peRed = *rgb++; LogicalPalette.aEntries[i].peGreen = *rgb++; LogicalPalette.aEntries[i].peBlue = *rgb++; LogicalPalette.aEntries[i].peFlags = PC_NOCOLLAPSE; } return CreatePalette((LPLOGPALETTE) &LogicalPalette); } #endif /* PLATFORM_WIN */ #ifdef PLATFORM_X11 #include #include #ifdef HAVE_SYS_IPC_H #include #endif #ifdef HAVE_SYS_SHM_H #include #endif /* #include */ struct PlatBitmap { Display *display; XShmSegmentInfo shminfo; XImage *ximage; }; static int ErrorHandler(ClientData clientData, XErrorEvent *errEventPtr) { int *anyError = (int *) clientData; /* Hack - ignore parameter */ (void) errEventPtr; *anyError = 1; return 0; } void Bitmap_New(Tcl_Interp *interp, BitmapPtr bitmapPtr) { int depth = bitmapPtr->depth; struct PlatBitmap *platData; Display *display = Tk_Display(Tk_MainWindow(interp)); int screenNum = XDefaultScreen(display); Tk_ErrorHandler handler; Window root = RootWindow(display, screenNum); Visual *visual = DefaultVisual(display, screenNum); int ret, anyError; MAKE(platData, struct PlatBitmap); #ifdef HAVE_SHMGET /* Verify shared-memory pixmaps are available */ { int major, minor; Bool pixmaps; XShmQueryVersion(display, &major, &minor, &pixmaps); if (pixmaps != True) { Tcl_Panic("no shared pixmaps"); } } #endif /* HAVE_SHMGET */ if (bitmapPtr->width < 1) bitmapPtr->width = 1; if (bitmapPtr->height < 1) bitmapPtr->height = 1; #ifdef HAVE_SHMGET /* Create shared-memory image. */ platData->ximage = XShmCreateImage(display, visual, depth, ZPixmap, NULL, &platData->shminfo, bitmapPtr->width, bitmapPtr->height); /* Allocate shared memory */ ret = platData->shminfo.shmid = shmget(IPC_PRIVATE, platData->ximage->bytes_per_line * platData->ximage->height, IPC_CREAT | 0777); if (ret < 0) { Tcl_Panic("shmget() failed"); } platData->shminfo.shmaddr = shmat(platData->shminfo.shmid, 0, 0); ret = (int) platData->shminfo.shmaddr; if(ret == -1) { shmctl(platData->shminfo.shmid, IPC_RMID, 0); Tcl_Panic("shmat() failed"); } /* Allow the server to write into our pixmap */ platData->shminfo.readOnly = False; anyError = 0; handler = Tk_CreateErrorHandler(display, -1, -1, -1, ErrorHandler, (ClientData) &anyError); ret = XShmAttach(display, &platData->shminfo); if(ret != True) { shmdt(platData->shminfo.shmaddr); shmctl(platData->shminfo.shmid, IPC_RMID, 0); Tcl_Panic("XShmAttach() failed"); } XSync(display, False); Tk_DeleteErrorHandler(handler); if (anyError) { shmdt(platData->shminfo.shmaddr); shmctl(platData->shminfo.shmid, IPC_RMID, 0); Tcl_Panic("XShmAttach() etc gave errors"); } ret = shmctl(platData->shminfo.shmid, IPC_RMID, 0); if(ret < 0) { XShmDetach(display, &platData->shminfo); shmdt(platData->shminfo.shmaddr); shmctl(platData->shminfo.shmid, IPC_RMID, 0); Tcl_Panic("shmctl() failed"); } /* Image uses shared memory we allocated */ platData->ximage->data = platData->shminfo.shmaddr; /* Create shared-memory Pixmap */ bitmapPtr->pixmap = XShmCreatePixmap(display, root, platData->shminfo.shmaddr, &platData->shminfo, bitmapPtr->width, bitmapPtr->height, depth); if (bitmapPtr->pixmap == None) { Tcl_Panic("XShmCreatePixmap() failed"); } #else /* HAVE_SHMGET */ platData->ximage = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, bitmapPtr->width, bitmapPtr->height, 32, 0); platData->ximage->data = malloc(platData->ximage->bytes_per_line * platData->ximage->height); bitmapPtr->pixmap = XCreatePixmap(display, root, bitmapPtr->width, bitmapPtr->height, depth); #endif /* !HAVE_SHMGET */ /* Set pitch, pixelSize, and pixelPtr */ bitmapPtr->pitch = platData->ximage->bytes_per_line; bitmapPtr->pixelSize = platData->ximage->bits_per_pixel / 8; bitmapPtr->pixelPtr = (unsigned char *) platData->shminfo.shmaddr; platData->display = display; bitmapPtr->platData = platData; } void Bitmap_Delete(BitmapPtr bitmapPtr) { struct PlatBitmap *platData = bitmapPtr->platData; Display *display = platData->display; #ifdef HAVE_SHMGET XShmDetach(display, &platData->shminfo); XDestroyImage(platData->ximage); XFreePixmap(display, bitmapPtr->pixmap); shmdt(platData->shminfo.shmaddr); shmctl(platData->shminfo.shmid, IPC_RMID, 0); #else XDestroyImage(platData->ximage); XFreePixmap(display, bitmapPtr->pixmap); #endif FREE(platData); bitmapPtr->platData = NULL; } void *Plat_PaletteInit(unsigned char *rgb) { /* Hack - just return NULL */ (void) rgb; return NULL; } #endif /* PLATFORM_X11 */ int Plat_XColor2Pixel(XColor *xColorPtr) { if (xColorPtr == NULL) return 0; #ifdef PLATFORM_X11 return xColorPtr->pixel; #endif #ifdef PLATFORM_WIN switch (g_icon_depth) { case 8: return Colormap_RGB2Index( ((double) xColorPtr->red / USHRT_MAX) * 255, ((double) xColorPtr->green / USHRT_MAX) * 255, ((double) xColorPtr->blue / USHRT_MAX) * 255); case 16: { unsigned short pix16; SetPix16((unsigned char *) &pix16, GetRValue(xColorPtr->pixel), GetGValue(xColorPtr->pixel), GetBValue(xColorPtr->pixel)); return pix16; } case 24: { return (GetBValue(xColorPtr->pixel) << 16) | (GetGValue(xColorPtr->pixel) << 8) | GetRValue(xColorPtr->pixel); } } #endif /* PLATFORM_WIN */ return 0; } #ifdef HAVE_TKPSYNC extern void TkpSync (Display * display); #endif void Plat_SyncDisplay(Display *display) { #ifdef HAVE_TKPSYNC TkpSync(display); #endif } /* * The Win32 "BITMAPFILEHEADER" type. */ typedef struct BITMAPFILEHEADER { u16b bfType; u32b bfSize; u16b bfReserved1; u16b bfReserved2; u32b bfOffBits; } BITMAPFILEHEADER; /* * The Win32 "BITMAPINFOHEADER" type. */ typedef struct BITMAPINFOHEADER { u32b biSize; u32b biWidth; u32b biHeight; u16b biPlanes; u16b biBitCount; u32b biCompresion; u32b biSizeImage; u32b biXPelsPerMeter; u32b biYPelsPerMeter; u32b biClrUsed; u32b biClrImportand; } BITMAPINFOHEADER; /* * The Win32 "RGBQUAD" type. */ typedef struct RGBQUAD { unsigned char b, g, r; unsigned char filler; } RGBQUAD; /*** Helper functions for system independent file loading. ***/ static byte get_byte(FILE *fff) { /* Get a character, and return it */ return (getc(fff) & 0xFF); } static void rd_byte(FILE *fff, byte *ip) { *ip = get_byte(fff); } static void rd_u16b(FILE *fff, u16b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u16b)(get_byte(fff)) << 8); } static void rd_u32b(FILE *fff, u32b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u32b)(get_byte(fff)) << 8); (*ip) |= ((u32b)(get_byte(fff)) << 16); (*ip) |= ((u32b)(get_byte(fff)) << 24); } /* * Read a Win32 BMP file. * * Assumes that the bitmap has a size such that no padding is needed in * various places. */ static u32b *ReadBMP(cptr Name, int *bw, int *bh) { FILE *f; BITMAPFILEHEADER fileheader; BITMAPINFOHEADER infoheader; u32b *Data32; u32b *p; u32b pixel; /* Palette of bitmap */ byte *pal = NULL; int ncol; int total; int i; int x, y; /* Open the BMP file */ f = fopen(Name, "r"); /* No such file */ if (!f) { quit_fmt("No bitmap to load! (%s)", Name); } /* Read the "BITMAPFILEHEADER" */ rd_u16b(f, &(fileheader.bfType)); rd_u32b(f, &(fileheader.bfSize)); rd_u16b(f, &(fileheader.bfReserved1)); rd_u16b(f, &(fileheader.bfReserved2)); rd_u32b(f, &(fileheader.bfOffBits)); /* Read the "BITMAPINFOHEADER" */ rd_u32b(f, &(infoheader.biSize)); rd_u32b(f, &(infoheader.biWidth)); rd_u32b(f, &(infoheader.biHeight)); rd_u16b(f, &(infoheader.biPlanes)); rd_u16b(f, &(infoheader.biBitCount)); rd_u32b(f, &(infoheader.biCompresion)); rd_u32b(f, &(infoheader.biSizeImage)); rd_u32b(f, &(infoheader.biXPelsPerMeter)); rd_u32b(f, &(infoheader.biYPelsPerMeter)); rd_u32b(f, &(infoheader.biClrUsed)); rd_u32b(f, &(infoheader.biClrImportand)); /* Verify the header */ if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) { quit_fmt("Incorrect BMP file format %s", Name); } /* The two headers above occupy 54 bytes total */ /* The "bfOffBits" field says where the data starts */ /* The "biClrUsed" field does not seem to be reliable */ /* Compute number of colors recorded */ ncol = (fileheader.bfOffBits - 54) / 4; if (ncol) { /* Create palette */ C_MAKE(pal, ncol * 3, byte); } for (i = 0; i < ncol; i++) { RGBQUAD clrg; /* Read an "RGBQUAD" */ rd_byte(f, &(clrg.b)); rd_byte(f, &(clrg.g)); rd_byte(f, &(clrg.r)); rd_byte(f, &(clrg.filler)); /* Analyze the color */ pal[i * 3] = clrg.b; pal[i * 3 + 1] = clrg.g; pal[i * 3 + 2] = clrg.r; } /* Look for illegal bitdepths. */ if ((infoheader.biBitCount == 1) || (infoheader.biBitCount == 4)) { quit_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name); } /* Determine total bytes needed for image */ total = infoheader.biWidth * (infoheader.biHeight + 2); /* * Allocate image memory * (plus a tiny bit extra so we can use aligned 32bit accesses) */ C_MAKE(Data32, total + 4, u32b); p = Data32; for (y = 0; y < (int) infoheader.biHeight; y++) { for (x = 0; x < (int) infoheader.biWidth; x++) { int ch = getc(f); p = Data32 + x + (infoheader.biHeight - 1 - y) * infoheader.biWidth; /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); if (infoheader.biBitCount == 8) { pixel = pal[ch * 3]; pixel = pixel * 256 + pal[ch * 3 + 1]; pixel = pixel * 256 + pal[ch * 3 + 2]; *p = pixel; } else if (infoheader.biBitCount == 24) { pixel = ch; ch = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); pixel = pixel * 256 + ch; ch = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); pixel = pixel * 256 + ch; *p = pixel; } } } fclose(f); /* Save the size for later */ *bw = infoheader.biWidth; *bh = infoheader.biHeight; /* Free palette */ FREE(pal); /* * Return data */ return (Data32); } /* * Actually load a bitmap */ BitmapPtr Bitmap_Load(Tcl_Interp *interp, cptr name) { BitmapPtr bitmapPtr; u32b *data; int i, j; MAKE(bitmapPtr, BitmapType); /* Want a 24bit bitmap */ bitmapPtr->depth = 24; data = ReadBMP(name, &bitmapPtr->width, &bitmapPtr->height); Bitmap_New(interp, bitmapPtr); /* Copy in the data */ if (bitmapPtr->pixelSize == 3) { byte *d8 = (byte *) data; byte *p = (byte *) bitmapPtr->pixelPtr; /* We need to copy each part seperately */ for (i = 0; i < bitmapPtr->height; i++) { for (j = 0; j < bitmapPtr->width; j++) { /* I bet this is wrong... */ *p++ = *d8++; *p++ = *d8++; *p++ = *d8++; d8++; } } } else if (bitmapPtr->pixelSize == 4) { C_COPY(bitmapPtr->pixelPtr, data, bitmapPtr->pitch * bitmapPtr->height, byte); } FREE(data); return (bitmapPtr); } /* * Load a font */ BitmapPtr Font_Load(Tcl_Interp *interp, cptr name, int size) { BitmapPtr bitmapPtr; FILE *fp; int i, j, k, m; int num = 0, error_idx = -1; char buf[1024]; char line[16]; byte *pixel; /* Open the BMP file */ fp = fopen(name, "r"); /* No such file */ if (fp == NULL) { return (NULL); } /* Create bitmap for font */ MAKE(bitmapPtr, BitmapType); /* Want a 8bit bitmap */ bitmapPtr->depth = 8; /* 128 characters */ bitmapPtr->width = 128 * size; bitmapPtr->height = size; /* Actually allocate the bitmap */ Bitmap_New(interp, bitmapPtr); /* Paraonia */ if (!bitmapPtr) { fclose(fp); return (NULL); } /* Reset the counters for use in parsing the font data */ i = 0; j = 16; /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { /* Count lines */ num++; /* Skip "empty" lines */ if (!buf[0]) continue; /* Skip "blank" lines */ if (isspace(buf[0])) continue; /* Skip comments */ if (buf[0] == '#') continue; /* Look at the line */ /* Verify correct "colon" format */ if (buf[1] != ':') { quit_fmt("Incorrect font file format on line %d", num); } /* Get number */ if (buf[0] == 'N') { /* Get the index */ i = atoi(buf+2); /* Verify information */ if (i <= error_idx) { quit_fmt("Incorrect font file numbering on line %d", num); } error_idx = i; /* Verify information */ if (i >= 128) { quit_fmt("Incorrect font file numbering on line %d", num); } /* Verify information */ if (j != size) { quit_fmt("Incorrect font size on line %d", num); } /* Start from the top */ j = 0; } /* Get font data */ if (buf[0] == 'F') { /* Verify information */ if (j >= size) { quit_fmt("Incorrect font size length on line %d", num); } if (((int) strlen(buf)) != size + 2) { quit_fmt("Incorrect font size width on line %d", num); } /* Create the line */ for (k = 0; k < size; k++) { line[k] = buf[k + 2]; } /* Copy it to the bitmap */ for (m = 0; m < size; m++) { pixel = get_icon_ptr(bitmapPtr, i * size + m, j); if (line[m] == '*') { /* Coloured pixel */ *pixel = 255; } else { /* Blank pixel */ *pixel = 0; } } /* Next line of the character */ j++; } } /* Close the file */ my_fclose(fp); /* Paranoia - does the file end early? */ if ((i != 127) || (j != size)) return (NULL); return (bitmapPtr); } zangband/src/tk/tcltk.c0000644000000000000000000002171010250356275014040 0ustar rootroot/* File: TclTk.c */ /* Purpose: embedded Tcl */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" #ifdef PLATFORM_WIN #include static void WishPanic TCL_VARARGS_DEF(char *,arg1) { va_list argList; char buf[1024]; char *format; format = TCL_VARARGS_START(char *, arg1, argList); vstrnfmt(buf, 1024, format, &argList); MessageBeep(MB_ICONEXCLAMATION); MessageBox(NULL, buf, "Fatal Error in Angband", MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); #ifdef _MSC_VER _asm { int 3 } #endif ExitProcess(1); } static void CloseStdHandle(DWORD nStdHandle) { HANDLE handle; handle = GetStdHandle(nStdHandle); if ((handle == INVALID_HANDLE_VALUE) || (handle == 0) || (GetFileType(handle) == FILE_TYPE_UNKNOWN)) { return; } CloseHandle(handle); } Tcl_Interp *TclTk_Init(char **argv) { char *p; char buffer[MAX_PATH], *t; size_t length; Tcl_Interp *interp; /*** From tk80/win/winMain.c ***/ Tcl_SetPanicProc(WishPanic); /* This call does nothing on Win32 systems */ SetMessageQueue(64); GetModuleFileName(NULL, buffer, sizeof(buffer)); for (p = buffer; *p != '\0'; p++) { if (*p == '\\') { *p = '/'; } } /*** From tk80/generic/TkMain.c ***/ Tcl_FindExecutable(buffer); /* According to Hobbs, this should come after Tcl_FindExecutable() */ interp = Tcl_CreateInterp(); /* XXX Hack -- When run from a BAT file, the input/output doesn't * go to the Tk Console. */ CloseStdHandle(STD_INPUT_HANDLE); CloseStdHandle(STD_OUTPUT_HANDLE); CloseStdHandle(STD_ERROR_HANDLE); Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); /*** from tk80/win/winMain.c (Tcl_AppInit) ***/ if (Tcl_Init(interp) != TCL_OK) { goto error; } if (Tk_Init(interp) != TCL_OK) { goto error; } Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit); /* Require the same Tcl version */ t = Tcl_GetVar(interp, "tcl_patchLevel", TCL_GLOBAL_ONLY); if (!t || strcmp(t, TCL_PATCH_LEVEL)) { WishPanic("This version of Angband was compiled for use\nwith " "Tcl version %s, not %s.", TCL_PATCH_LEVEL, t); } /* Require the same Tk version */ t = Tcl_GetVar(interp, "tk_patchLevel", TCL_GLOBAL_ONLY); if (!t || strcmp(t, TK_PATCH_LEVEL)) { WishPanic("This version of Angband was compiled for use\nwith " "Tk version %s, not %s.", TK_PATCH_LEVEL, t); } Tcl_ResetResult(interp); return interp; error: WishPanic(interp->result); return NULL; } #endif /* PLATFORM_WIN */ #ifdef PLATFORM_X11 #include typedef struct ThreadSpecificData { Tcl_Interp *interp; /* Interpreter for this thread. */ Tcl_DString command; /* Used to assemble lines of terminal input * into Tcl commands. */ Tcl_DString line; /* Used to read the next line from the * terminal input. */ int tty; /* Non-zero means standard input is a * terminal-like device. Zero means it's * a file. */ } ThreadSpecificData; Tcl_ThreadDataKey dataKey; /* *---------------------------------------------------------------------- * * Prompt -- * * Issue a prompt on standard output, or invoke a script * to issue the prompt. * * Results: * None. * * Side effects: * A prompt gets output, and a Tcl script may be evaluated * in interp. * *---------------------------------------------------------------------- */ /* * Interpreter to use for prompting. * Non-zero 'partial' means there already * exists a partial command, so use * the secondary prompt. */ static void Prompt(Tcl_Interp *interp, int partial) { cptr promptCmd; int code; Tcl_Channel outChannel, errChannel; promptCmd = Tcl_GetVar(interp, (partial ? "tcl_prompt2" : "tcl_prompt1"), TCL_GLOBAL_ONLY); if (promptCmd == NULL) { defaultPrompt: if (!partial) { /* * We must check that outChannel is a real channel - it * is possible that someone has transferred stdout out of * this interpreter with "interp transfer". */ outChannel = Tcl_GetChannel(interp, "stdout", NULL); if (outChannel != (Tcl_Channel) NULL) { Tcl_WriteChars(outChannel, "% ", 2); } } } else { code = Tcl_Eval(interp, promptCmd); if (code != TCL_OK) { Tcl_AddErrorInfo(interp, "\n (script that generates prompt)"); /* * We must check that errChannel is a real channel - it * is possible that someone has transferred stderr out of * this interpreter with "interp transfer". */ errChannel = Tcl_GetChannel(interp, "stderr", NULL); if (errChannel != (Tcl_Channel) NULL) { Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); Tcl_WriteChars(errChannel, "\n", 1); } goto defaultPrompt; } } outChannel = Tcl_GetChannel(interp, "stdout", NULL); if (outChannel != (Tcl_Channel) NULL) { Tcl_Flush(outChannel); } } /* *---------------------------------------------------------------------- * * StdinProc -- * * This procedure is invoked by the event dispatcher whenever * standard input becomes readable. It grabs the next line of * input characters, adds them to a command being assembled, and * executes the command if it's complete. * * Results: * None. * * Side effects: * Could be almost arbitrary, depending on the command that's * typed. * *---------------------------------------------------------------------- */ static void StdinProc(ClientData clientData, int mask) { static int gotPartial = 0; char *cmd; int code, count; Tcl_Channel chan = (Tcl_Channel) clientData; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_Interp *interp = tsdPtr->interp; /* Hack - ignore parameter */ (void) mask; count = Tcl_Gets(chan, &tsdPtr->line); if (count < 0) { if (!gotPartial) { if (tsdPtr->tty) { Tcl_Exit(0); } else { Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan); } return; } } (void) Tcl_DStringAppend(&tsdPtr->command, Tcl_DStringValue( &tsdPtr->line), -1); cmd = Tcl_DStringAppend(&tsdPtr->command, "\n", -1); Tcl_DStringFree(&tsdPtr->line); if (!Tcl_CommandComplete(cmd)) { gotPartial = 1; goto prompt; } gotPartial = 0; /* * Disable the stdin channel handler while evaluating the command; * otherwise if the command re-enters the event loop we might * process commands from stdin before the current command is * finished. Among other things, this will trash the text of the * command being evaluated. */ Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan); code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL); chan = Tcl_GetStdChannel(TCL_STDIN); if (chan) { Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, (ClientData) chan); } Tcl_DStringFree(&tsdPtr->command); if (Tcl_GetStringResult(interp)[0] != '\0') { if ((code != TCL_OK) || (tsdPtr->tty)) { chan = Tcl_GetStdChannel(TCL_STDOUT); if (chan) { Tcl_WriteObj(chan, Tcl_GetObjResult(interp)); Tcl_WriteChars(chan, "\n", 1); } } } /* * Output a prompt. */ prompt: if (tsdPtr->tty) { Prompt(interp, gotPartial); } Tcl_ResetResult(interp); } extern void TkpDisplayWarning(const char * msg, const char * title); Tcl_Interp *TclTk_Init(cptr *argv) { Tcl_Interp *interp; Tcl_Channel inChannel, outChannel; ThreadSpecificData *tsdPtr; interp = Tcl_CreateInterp(); /*** From tk83/generic/TkMain.c ***/ tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); Tcl_FindExecutable(argv[0]); tsdPtr->interp = interp; tsdPtr->tty = isatty(0); Tcl_SetVar(interp, "tcl_interactive", (tsdPtr->tty ? "1" : "0"), TCL_GLOBAL_ONLY); /*** from tk83/unix/tkAppInit.c (Tcl_AppInit) ***/ if (Tcl_Init(interp) != TCL_OK) { goto error; } if (Tk_Init(interp) != TCL_OK) { goto error; } Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit); /*** From tk83/generic/TkMain.c (again) ***/ /* * Establish a channel handler for stdin. */ inChannel = Tcl_GetStdChannel(TCL_STDIN); if (inChannel) { Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc, (ClientData) inChannel); } if (tsdPtr->tty) { Prompt(interp, 0); } outChannel = Tcl_GetStdChannel(TCL_STDOUT); if (outChannel) { Tcl_Flush(outChannel); } Tcl_DStringInit(&tsdPtr->command); Tcl_DStringInit(&tsdPtr->line); Tcl_ResetResult(interp); return interp; error: TkpDisplayWarning(Tcl_GetStringResult(interp), "TclTk_Init Failed!"); Tcl_DeleteInterp(interp); Tcl_Exit(1); return NULL; } #endif /* PLATFORM_X11 */ zangband/src/tk/tk-util.c0000644000000000000000000003760410250356275014321 0ustar rootroot/* File: util-dll.c */ /* Purpose: misc stuff */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" #include #ifndef USHRT_MAX #define USHRT_MAX 65535 #endif #define MAX_DELTA (255 * 6) #define MAX_COLOR_ENTRY 1021 #ifdef HAVE_TKFONT_H #include #else /* HAVE_TKFONT_H */ typedef struct TkFontAttributes { Tk_Uid family; /* Font family, or NULL to represent * plaform-specific default system font. */ int size; /* Pointsize of font, 0 for default size, or * negative number meaning pixel size. */ int weight; /* Weight flag; see below for def'n. */ int slant; /* Slant flag; see below for def'n. */ int underline; /* Non-zero for underline font. */ int overstrike; /* Non-zero for overstrike font. */ } TkFontAttributes; #define TK_FW_NORMAL 0 #define TK_FW_BOLD 1 #define TK_FS_ROMAN 0 #define TK_FS_ITALIC 1 typedef struct TkFontMetrics { int ascent; /* From baseline to top of font. */ int descent; /* From baseline to bottom of font. */ int maxWidth; /* Width of widest character in font. */ int fixed; /* Non-zero if this is a fixed-width font, * 0 otherwise. */ } TkFontMetrics; typedef struct TkFont { /* * Fields used and maintained exclusively by generic code. */ int resourceRefCount; /* Number of active uses of this font (each * active use corresponds to a call to * Tk_AllocFontFromTable or Tk_GetFont). * If this count is 0, then this TkFont * structure is no longer valid and it isn't * present in a hash table: it is being * kept around only because there are objects * referring to it. The structure is freed * when resourceRefCount and objRefCount * are both 0. */ int objRefCount; /* The number of Tcl objects that reference * this structure. */ Tcl_HashEntry *cacheHashPtr;/* Entry in font cache for this structure, * used when deleting it. */ Tcl_HashEntry *namedHashPtr;/* Pointer to hash table entry that * corresponds to the named font that the * tkfont was based on, or NULL if the tkfont * was not based on a named font. */ Screen *screen; /* The screen where this font is valid. */ int tabWidth; /* Width of tabs in this font (pixels). */ int underlinePos; /* Offset from baseline to origin of * underline bar (used for drawing underlines * on a non-underlined font). */ int underlineHeight; /* Height of underline bar (used for drawing * underlines on a non-underlined font). */ /* * Fields used in the generic code that are filled in by * platform-specific code. */ Font fid; /* For backwards compatibility with XGCValues * structures. Remove when TkGCValues is * implemented. */ TkFontAttributes fa; /* Actual font attributes obtained when the * the font was created, as opposed to the * desired attributes passed in to * TkpGetFontFromAttributes(). The desired * metrics can be determined from the string * that was used to create this font. */ TkFontMetrics fm; /* Font metrics determined when font was * created. */ struct TkFont *nextPtr; /* Points to the next TkFont structure with * the same name. All fonts with the * same name (but different displays) are * chained together off a single entry in * a hash table. */ } TkFont; #endif /* !HAVE_TKFONT_H */ /* Structure for a hash table used by rgb2index() */ typedef struct { int valid; /* The hash-table entry is valid */ u32b pixel; /* an RGB pixel value */ int index; /* closest matching palette index for 'pixel' */ } t_color_entry; typedef struct IndexedColor IndexedColor; struct IndexedColor { byte rgb[256 * 3]; t_color_entry hash[MAX_COLOR_ENTRY]; void *platData; }; static IndexedColor g_palette; static bool Palette_Initialized = 0; int g_palette_black = 255; int g_palette_white = 0; int g_colormap_black; int g_colormap_white; byte g_palette2colormap[256]; /* * Append an element to an array */ void *Array_Append(void *array_ptr, int *count, int elem_size, void *elem_ptr) { char *ap = array_ptr; int n = (*count) + 1; ap = Tcl_Realloc(ap, n * elem_size); (void) memcpy(ap + (n - 1) * elem_size, elem_ptr, elem_size); (*count) = n; return ap; } /* * Insert an element in an array */ void *Array_Insert(void *array_ptr, int *count, int elem_size, int index) { char *ap = array_ptr; int n = (*count) + 1; if (index < 0) index = 0; if (index >= n) index = n - 1; ap = Tcl_Realloc(ap, n * elem_size); if (index != n - 1) { (void) memcpy(ap + (index + 1) * elem_size, ap + index * elem_size, (n - index - 1) * elem_size); } else { memset(ap + index * elem_size, 0, elem_size); } (*count) = n; return ap; } /* * Delete an element from an array */ void *Array_Delete(void *array_ptr, int *count, int elem_size, int index) { char *ap = array_ptr; int i, n = (*count) - 1; if (index < 0) index = 0; if (index > n) index = n; if (index != n) { (void) memcpy(ap + index * elem_size, ap + (index + 1) * elem_size, (n - index) * elem_size); } /* Zero the trailing slot to catch errors */ for (i = 0; i < elem_size; i++) { ap[n * elem_size + i] = 0; } (*count) = n; return (void *) Tcl_Realloc(ap, n * elem_size); } static void IndexedColor_ResetHash(IndexedColor *idc) { int i; for (i = 0; i < 256; i++) { idc->hash[i].valid = 0; } } /* * Returns the nearest matching palette index for the given * RGB values. */ static int IndexedColor_RGB2Index(IndexedColor *idc, unsigned char r, unsigned char g, unsigned char b) { int i, diff, index, max; unsigned char *col; unsigned long pixel; t_color_entry *entry; /* Calculate the pixel value */ pixel = (r << 16) | (g << 8) | b; /* Hash the pixel value */ entry = &idc->hash[pixel % MAX_COLOR_ENTRY]; /* We already calculated the palette index */ if (entry->valid && (entry->pixel == pixel)) { /* Return the palette index */ return entry->index; } index = 0; max = MAX_DELTA; col = idc->rgb; /* Check each palette entry */ for (i = 0; i < 256; i++) { /* Work out the 'difference' between the colours */ diff = abs(r - col[0]); diff += abs(g - col[1]); diff += abs(b - col[2]); /* Multiply by the 'colour factor' */ diff *= 3; /* Add in the effects of brightness */ diff += abs(b + r + g - col[0] - col[1] - col[2]); col += 3; /* This palette entry is a better match than any other so far */ if (diff < max) { /* Remember the palette index */ index = i; /* Remember the minimum difference */ max = diff; } } /* Remember the hash table entry info */ entry->pixel = pixel; entry->index = index; entry->valid = 1; /* Return the palette index */ return index; } /* set $index ?$color? */ static int objcmd_palette_set(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { CommandInfo *infoCmd = (CommandInfo *) clientData; int objC = objc - infoCmd->depth; Tcl_Obj *CONST *objV = objv + infoCmd->depth; char buf[20]; int i, r, g, b; if (Tcl_GetIntFromObj(interp, objV[1], &i) != TCL_OK) { return TCL_ERROR; } if ((i < 0) || (i >= 256)) { return TCL_ERROR; } if (objC == 3) { XColor *xColorPtr = Tk_AllocColorFromObj(interp, Tk_MainWindow(interp), objV[2]); if (xColorPtr == NULL) { return TCL_ERROR; } g_palette.rgb[i * 3] = xColorPtr->red / 255; g_palette.rgb[i * 3 + 1] = xColorPtr->green / 255; g_palette.rgb[i * 3 + 2] = xColorPtr->blue / 255; Tk_FreeColor(xColorPtr); return TCL_OK; } r = g_palette.rgb[i * 3]; g = g_palette.rgb[i * 3 + 1]; b = g_palette.rgb[i * 3 + 2]; strnfmt(buf, 20, "#%02X%02X%02X", r, g, b); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_OK; } static CommandInit commandInit[] = { {0, "palette", 0, 0, NULL, NULL, (ClientData) 0}, {1, "set", 2, 3, "?color?", objcmd_palette_set, (ClientData) 0}, {0, NULL, 0, 0, NULL, NULL, 0} }; /* Uniform colour table */ static byte def_pal1[6] = {255, 204, 153, 102, 51, 0}; static byte def_pal2[10] = {238, 221, 187, 170, 136, 119, 85, 68, 34, 17}; int Palette_Init(Tcl_Interp *interp) { int i, j, k; unsigned char *rgb; if (Palette_Initialized) return TCL_OK; rgb = g_palette.rgb; g_palette.platData = NULL; /* Create colour cube */ for (i = 0; i < 6; i++) { for (j = 0; j < 6; j++) { for (k = 0; k < 6; k++) { /* Write this color to the array */ *rgb++ = def_pal1[i]; *rgb++ = def_pal1[j]; *rgb++ = def_pal1[k]; } } } /* Create primary colours */ for (i = 0; i < 10; i++) { *rgb++ = def_pal2[i]; *rgb++ = 0; *rgb++ = 0; } for (i = 0; i < 10; i++) { *rgb++ = 0; *rgb++ = def_pal2[i]; *rgb++ = 0; } for (i = 0; i < 10; i++) { *rgb++ = 0; *rgb++ = 0; *rgb++ = def_pal2[i]; } /* Create greys */ for (i = 0; i < 10; i++) { *rgb++ = def_pal2[i]; *rgb++ = def_pal2[i]; *rgb++ = def_pal2[i]; } g_palette.platData = Plat_PaletteInit(g_palette.rgb); (void) CommandInfo_Init(interp, commandInit, NULL); Palette_ResetHash(); Palette_Initialized = TRUE; Colormap_Init(interp); return TCL_OK; } void Palette_ResetHash(void) { IndexedColor_ResetHash(&g_palette); } /* * Returns the nearest matching palette index for the given * RGB values. */ int Palette_RGB2Index(unsigned char r, unsigned char g, unsigned char b) { return IndexedColor_RGB2Index(&g_palette, r, g, b); } #ifdef PLATFORM_WIN void *Palette_GetHPal(void) { return g_palette.platData; /* HPALETTE */ } #endif /* PLATFORM_WIN */ unsigned char *Palette_GetRGB(void) { return g_palette.rgb; } Tcl_Obj *ExtToUtf_NewStringObj(CONST char *bytes, int length) { char *utfString; Tcl_DString utfDString; Tcl_Obj *objResult; utfString = Tcl_ExternalToUtfDString(NULL, bytes, length, &utfDString); objResult = Tcl_NewStringObj(utfString, Tcl_DStringLength(&utfDString)); Tcl_DStringFree(&utfDString); return objResult; } void ExtToUtf_SetResult(Tcl_Interp *interp, cptr string) { char *utfString; Tcl_DString utfDString; utfString = Tcl_ExternalToUtfDString(NULL, string, -1, &utfDString); Tcl_SetResult(interp, utfString, TCL_VOLATILE); Tcl_DStringFree(&utfDString); } char *UtfToExt_TranslateFileName(Tcl_Interp *interp, char *utfPath, Tcl_DString *extDStringPtr) { char *extString, *utfString; Tcl_DString utfDString; int utfLen; utfString = Tcl_TranslateFileName(interp, utfPath, &utfDString); if (utfString == NULL) return NULL; utfLen = Tcl_DStringLength(&utfDString); extString = Tcl_UtfToExternalDString(NULL, utfString, utfLen, extDStringPtr); Tcl_DStringFree(&utfDString); return extString; } static IndexedColor g_colormap; unsigned char *g_colormap_rgb; int Colormap_Init(Tcl_Interp *interp) { #ifdef PLATFORM_X11 Tk_Window tkwin = Tk_MainWindow(interp); Display *display = Tk_Display(tkwin); Colormap colormap = Tk_Colormap(tkwin); /* DefaultColormap() */ XColor xColor; #endif int i, k, r, g, b; IndexedColor_ResetHash(&g_colormap); #ifdef PLATFORM_X11 if (Tk_Depth(tkwin) == 8) { /* * Allocate each color in the colormap, to prevent the colormap * entries from changing. */ for (i = 0; i < 256; i++) { xColor.pixel = i; XQueryColor(display, colormap, &xColor); (void) Tk_GetColorByValue(tkwin, &xColor); } } #endif /* PLATFORM_X11 */ for (i = 0; i < 256; i++) { r = g_palette.rgb[i * 3 + 0]; g = g_palette.rgb[i * 3 + 1]; b = g_palette.rgb[i * 3 + 2]; #ifdef PLATFORM_X11 if (Tk_Depth(tkwin) == 8) { /* Get the XColor at this location in the colormap */ xColor.pixel = i; XQueryColor(display, colormap, &xColor); /* Convert RGB values to 0-255 */ r = xColor.red / 255; g = xColor.green / 255; b = xColor.blue / 255; } #endif /* PLATFORM_X11 */ /* Remember RGB values at this colormap index */ g_colormap.rgb[i * 3 + 0] = r; g_colormap.rgb[i * 3 + 1] = g; g_colormap.rgb[i * 3 + 2] = b; } for (i = 0; i < 256; i++) { /* Get the RGB values at this palette index */ r = g_palette.rgb[i * 3 + 0]; g = g_palette.rgb[i * 3 + 1]; b = g_palette.rgb[i * 3 + 2]; /* Find the closest color in the colormap */ k = Colormap_RGB2Index(r, g, b); /* Map palette index -> colormap index */ g_palette2colormap[i] = k; } /* Get the black and white pixels */ g_colormap_black = PALETTE_BLACK; g_colormap_white = PALETTE_WHITE; #ifdef PLATFORM_X11 if (Tk_Depth(tkwin) == 8) { g_colormap_black = BlackPixelOfScreen(Tk_Screen(tkwin)); g_colormap_white = WhitePixelOfScreen(Tk_Screen(tkwin)); } #endif /* PLATFORM_X11 */ g_colormap_rgb = g_colormap.rgb; return TCL_OK; } unsigned char *Colormap_GetRGB(void) { return g_colormap.rgb; } int Colormap_RGB2Index(unsigned char r, unsigned char g, unsigned char b) { return IndexedColor_RGB2Index(&g_colormap, r, g, b); } /* * Return a "standardized" string describing a font. */ int objcmd_fontdesc(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tk_Font tkfont; TkFont *fontPtr; char buf[1024]; /* Hack - ignore unused parameter */ (void) dummy; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "font"); return TCL_ERROR; } tkfont = Tk_AllocFontFromObj(interp, Tk_MainWindow(interp), objv[1]); if (tkfont == NULL) { return TCL_ERROR; } fontPtr = (TkFont *) tkfont; strnfmt(buf, 1024, "-family {%s} -size %d -weight %s -slant %s " "-underline %d -overstrike %d", fontPtr->fa.family, fontPtr->fa.size, (fontPtr->fa.weight == TK_FW_BOLD) ? "bold" : "normal", (fontPtr->fa.slant == TK_FS_ITALIC) ? "italic" : "roman", fontPtr->fa.underline, fontPtr->fa.overstrike); Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1); Tk_FreeFontFromObj(Tk_MainWindow(interp), objv[1]); return TCL_OK; } cptr keyword_term_color[16] = { "TERM_DARK", "TERM_WHITE", "TERM_SLATE", "TERM_ORANGE", "TERM_RED", "TERM_GREEN", "TERM_BLUE", "TERM_UMBER", "TERM_L_DARK", "TERM_L_WHITE", "TERM_VIOLET", "TERM_YELLOW", "TERM_L_RED", "TERM_L_GREEN", "TERM_L_BLUE", "TERM_L_UMBER" }; byte g_prompt_attr = TERM_WHITE; /* * Display a prompt in the "message line", but don't save it. */ void prompt_print(cptr str) { cptr attr = keyword_term_color[g_prompt_attr]; angtk_eval("angband_prompt", "set", str, attr, NULL); } /* * Erase the "message line". */ void prompt_erase(void) { angtk_eval("angband_prompt", "wipe", NULL); } /* * Display a formatted prompt, using "vstrnfmt()" and "prompt_print()". */ void prompt_format(cptr fmt, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Display */ prompt_print(buf); } void prompt_append(cptr str) { cptr attr = keyword_term_color[g_prompt_attr]; angtk_eval("angband_prompt", "append", str, attr, NULL); } void prompt_open(cptr str) { cptr attr = keyword_term_color[g_prompt_attr]; angtk_eval("angband_prompt", "open", str, attr, NULL); } void prompt_update(cptr str) { cptr attr = keyword_term_color[g_prompt_attr]; angtk_eval("angband_prompt", "update", str, attr, NULL); } /* * Display a prompt, wait for a keypress. */ void any_more(cptr prompt) { bool old_quick = quick_messages; /* Set the prompt */ if (!prompt) prompt = "Hit any key to continue"; /* Set quick_messages so any key is accepted */ quick_messages = TRUE; /* Display the message, wait for a response */ msgf(prompt); message_flush(); /* Restore quick_messages */ quick_messages = old_quick; } int ExtToUtf_SetArrayValueString(cptr varName, cptr field, cptr value) { Tcl_DString utfDString; cptr utfString; Tcl_ExternalToUtfDString(NULL, value, -1, &utfDString); utfString = Tcl_DStringValue(&utfDString); if (Tcl_SetVar2(g_interp, varName, field, utfString, TCL_LEAVE_ERR_MSG) == NULL) { Tcl_DStringFree(&utfDString); return TCL_ERROR; } Tcl_DStringFree(&utfDString); return TCL_OK; } zangband/src/tk/widget.c0000644000000000000000000010426510250356275014211 0ustar rootroot/* File: widget.c */ /* Purpose: Widget stuff */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "tnb.h" #include "../maid-grf.h" typedef struct Widget Widget; struct Widget { Tk_Window tkwin; Display *display; Tcl_Interp *interp; Tcl_Command widgetCmd; GC gc; /* pixmap context */ BitmapPtr bitmap; /* Offscreen bitmap */ int width; /* # of columns */ int height; /* # of rows */ BitmapPtr tiles; /* The graphical tiles */ BitmapPtr font; /* A dodgy 16x16 font */ int oldWidth, oldHeight; /* To notice changes */ int y, x; /* Cave location (center of widget) */ int rc, cc; /* Rows, columns */ int bh, bw; /* Bitmap width & height */ int dx1, dx2, dy1, dy2; /* Dirty rect for copying */ int y_min, y_max; /* Limits of displayed info */ int x_min, x_max; /* Limits of displayed info */ int gwidth; /* Source column width */ int gheight; /* Source row height */ int oldGWidth, oldGHeight; /* To notice changes */ int term; /* Boolean: Widget is a term, or not */ #define WIDGET_REDRAW 0x01 #define WIDGET_DELETED 0x02 #define WIDGET_EXPOSE 0x04 #define WIDGET_WIPE 0x08 byte flags; /* Misc flags */ }; /* Colour depth */ static int widget_icon_depth; /* Hack - have one widget for the current term */ static Widget *tnb_term; /* Predeclare */ static void widget_draw_all(Widget *widgetPtr); /* * Actually draw stuff into the Widget's display. This routine is * usually passed to Tcl_DoWhenIdle(). */ static void Widget_Display(ClientData clientData) { Widget *widgetPtr = (Widget *) clientData; Tk_Window tkwin = widgetPtr->tkwin; /* We want to draw all grids */ if (widgetPtr->flags & WIDGET_WIPE) { /* Forget that a wipe (and redraw) is needed */ widgetPtr->flags &= ~(WIDGET_WIPE); /* Draw all grids */ widget_draw_all(widgetPtr); } /* Forget that a redraw is scheduled */ widgetPtr->flags &= ~WIDGET_REDRAW; /* The window doesn't exist, or it is not mapped */ if ((tkwin == NULL) || (!Tk_IsMapped(tkwin))) { /* Done */ return; } if (widgetPtr->flags & WIDGET_EXPOSE) { /* Reset dirty bounds to entire window */ widgetPtr->dx1 = 0; widgetPtr->dy1 = 0; widgetPtr->dx2 = widgetPtr->width; widgetPtr->dy2 = widgetPtr->height; /* Forget expose flag */ widgetPtr->flags &= ~WIDGET_EXPOSE; } #if 0 /* Use Tk_SetWindowBackgroundPixmap() for more speed */ XClearWindow(widgetPtr->display, Tk_WindowId(tkwin)); #endif XCopyArea(widgetPtr->display, widgetPtr->bitmap->pixmap, /* source drawable */ Tk_WindowId(tkwin), /* dest drawable */ widgetPtr->gc, /* graphics context */ widgetPtr->dx1, widgetPtr->dy1, /* source top-left */ widgetPtr->dx2 - widgetPtr->dx1, /* width */ widgetPtr->dy2 - widgetPtr->dy1, /* height */ widgetPtr->dx1, widgetPtr->dy1 /* dest top-left */ ); Plat_SyncDisplay(widgetPtr->display); /* Clear dirty rectangle */ widgetPtr->dx1 = widgetPtr->width; widgetPtr->dy1 = widgetPtr->height; widgetPtr->dx2 = 0; widgetPtr->dy2 = 0; } static void Widget_EventuallyRedraw(Widget *widgetPtr) { if (widgetPtr->tkwin == NULL) return; /* A redraw is already scheduled */ if (widgetPtr->flags & WIDGET_REDRAW) return; /* Schedule a redraw */ Tcl_DoWhenIdle(Widget_Display, (ClientData) widgetPtr); /* Remember a redraw is scheduled */ widgetPtr->flags |= WIDGET_REDRAW; } /* Resize the dirty rectangle */ static void dirty_rectangle(int x1, int y1, int x2, int y2, Widget *widgetPtr) { if (x1 < widgetPtr->dx1) widgetPtr->dx1 = x1; if (y1 < widgetPtr->dy1) widgetPtr->dy1 = y1; if (x2 > widgetPtr->dx2) widgetPtr->dx2 = x2; if (y2 > widgetPtr->dy2) widgetPtr->dy2 = y2; /* Redraw later */ Widget_EventuallyRedraw(widgetPtr); } /* Dirty a string of changed tiles */ static void dirty_line(int x, int y, int n, Widget *widgetPtr) { int x1, x2, y1, y2; /* Set dirty region */ x1 = (x - widgetPtr->x_min) * widgetPtr->gwidth; x2 = (x + n - widgetPtr->x_min) * widgetPtr->gwidth; y1 = (y - widgetPtr->y_min) * widgetPtr->gheight; y2 = (y + 1 - widgetPtr->y_min) * widgetPtr->gheight; /* Dirty it */ dirty_rectangle(x1, y1, x2, y2, widgetPtr); } /* * Draw a blank square at this 'unkown' location */ static void DrawBlank(int x, int y, Widget *widgetPtr) { BitmapPtr bitmapPtr = widgetPtr->bitmap; byte *dstPtr; int y2; int length = widgetPtr->gwidth * bitmapPtr->pixelSize; /* Get the address of where to write the data in the bitmap */ dstPtr = bitmapPtr->pixelPtr + x * bitmapPtr->pixelSize + y * bitmapPtr->pitch; /* Clear the area */ for (y2 = 0; y2 < widgetPtr->gheight; y2++) { (void) C_WIPE(dstPtr, length, byte); dstPtr += bitmapPtr->pitch; } } static u16b r16mask = 0, r16shift = 0; static u16b g16mask = 0, g16shift = 0; static u16b b16mask = 0, b16shift = 0; static int count_ones(u16b mask) { int n; for (n = 0; mask != 0; mask &= mask - 1) { n++; } return (n); } /* Initialise the 16bit colour masks and shifts */ static void init_masks(Tcl_Interp *interp) { Tk_Window tkwin = Tk_MainWindow(interp); Visual *visual = Tk_Visual(tkwin); int redcount, greencount, bluecount; r16mask = visual->red_mask; g16mask = visual->green_mask; b16mask = visual->blue_mask; #ifdef PLATFORM_WIN /* XXX Always 5-5-5 */ r16mask = 0x7C00; g16mask = 0x03E0; b16mask = 0x001F; #endif /* Get red mask and shift */ redcount = count_ones(r16mask); greencount = count_ones(g16mask); bluecount = count_ones(b16mask); r16shift = redcount + greencount + bluecount - 8; g16shift = greencount + bluecount - 8; b16shift = 8 - bluecount; } /* * Find a location on the bitmaps */ byte *get_icon_ptr(BitmapPtr bitmap_ptr, int x, int y) { return (bitmap_ptr->pixelPtr + x * bitmap_ptr->pixelSize + y * bitmap_ptr->pitch); } errr Term_xtra_tnb_react(void) { /* Paranoia */ if (!tnb_term) return (1); /* Redraw later */ Widget_EventuallyRedraw(tnb_term); return 0; } /* * Draw stuff at this location */ static void draw_char(int x, int y, byte a, char c, Widget *widgetPtr) { int i, j; int depth = widgetPtr->bitmap->depth; byte *src, *dest; u32b r, g, b; /* Draw a blank square first */ DrawBlank(x, y, widgetPtr); /* Loop over bitmap size */ for (i = 0; i < widgetPtr->gwidth; i++) { for (j = 0; j < widgetPtr->gheight; j++) { /* Get address of icon data in tiles bitmap */ src = get_icon_ptr(widgetPtr->font, c * tnb_font_size + i, j); /* Get destination */ dest = get_icon_ptr(widgetPtr->bitmap, x + i, y + j); /* Get pixel */ if (*src) { r = angband_color_table[a][1]; g = angband_color_table[a][2]; b = angband_color_table[a][3]; } else { r = 0; g = 0; b = 0; } /* Convert to bitdepth of screen bitmap and display */ switch (depth) { case 8: { *dest = Palette_RGB2Index(r, g, b); break; } case 16: { u16b p; /* Convert to 16bit colour */ p = (r << r16shift) & r16mask; p += (g << g16shift) & g16mask; p += (b >> b16shift) & b16mask; dest[1] = (byte) ((p & 0xFF00) >> 8); dest[0] = (byte) (p & 0x00FF); break; } case 24: { dest[0] = r; dest[1] = g; dest[2] = b; break; } } } } } /* * Draw pict */ static void draw_pict(int x, int y, byte a, char c, byte ta, char tc, Widget *widgetPtr) { u32b *src1, *src2; byte *dest; u16b r, g, b; u32b pixel; int i, j; int depth = widgetPtr->bitmap->depth; int s1x = 0, s2x = 0, s1y = 0, s2y = 0; if (a & 0x80) { s1x = (c & 0x7F) * widgetPtr->gwidth; s1y = (a & 0x7F) * widgetPtr->gheight; } if (ta & 0x80) { s2x = (tc & 0x7F) * widgetPtr->gwidth; s2y = (ta & 0x7F) * widgetPtr->gheight; } /* Loop over bitmap size */ for (i = 0; i < widgetPtr->gwidth; i++) { for (j = 0; j < widgetPtr->gheight; j++) { /* Get address of icon data in tiles bitmap */ src1 = (u32b *) get_icon_ptr(widgetPtr->tiles, s1x + i, s1y + j); src2 = (u32b *) get_icon_ptr(widgetPtr->tiles, s2x + i, s2y + j); /* Get destination */ dest = get_icon_ptr(widgetPtr->bitmap, x + i, y + j); /* Get tile pixel (using transparency) */ pixel = *src1 & 0x00FFFFFF; /* Hack - overlay */ if (!pixel) pixel = *src2 & 0x00FFFFFF; b = (pixel & 0x00FF0000) >> 16; g = (pixel & 0x0000FF00) >> 8; r = pixel & 0xFF; /* Convert to bitdepth of screen bitmap and display */ switch (depth) { case 8: { *dest = Palette_RGB2Index(r, g, b); break; } case 16: { u16b p; /* Convert to 16bit colour */ p = (r << r16shift) & r16mask; p += (g << g16shift) & g16mask; p += (b >> b16shift) & b16mask; dest[1] = (byte) ((p & 0xFF00) >> 8); dest[0] = (byte) (p & 0x00FF); break; } case 24: { dest[0] = r; dest[1] = g; dest[2] = b; break; } } } } } static void draw_square(int x, int y, byte a, char c, byte ta, char tc, Widget *widgetPtr) { if ((a & 0x80) || (ta & 0x80)) { /* Graphical tiles */ draw_pict(x, y, a, c, ta, tc, widgetPtr); } else { /* Characters */ draw_char(x, y, a, c, widgetPtr); } } /* * Wipe n characters at position x, y */ errr Term_wipe_tnb(int x, int y, int n) { int i, xp, yp; /* Paranoia */ if (!tnb_term) return (1); for (i = 0; (i < n) && (x + i < 80); i++) { xp = (x + i - tnb_term->x_min) * tnb_term->gwidth; yp = (y - tnb_term->y_min) * tnb_term->gheight; DrawBlank(xp, yp, tnb_term); } /* Set dirty region */ dirty_line(x, y, n, tnb_term); return (0); } /* * Draw a string of tiles */ errr Term_pict_tnb(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i, xp, yp; /* Paranoia */ if (!tnb_term) return (1); for (i = 0; (i < n) && (x + i < 80); i++) { xp = (x + i - tnb_term->x_min) * tnb_term->gwidth; yp = (y - tnb_term->y_min) * tnb_term->gheight; draw_pict(xp, yp, ap[i], cp[i], tap[i], tcp[i], tnb_term); } /* Set dirty region */ dirty_line(x, y, n, tnb_term); return 0; } /* * Draw a string of characters * * (This could be made more efficient by drawing * more than one character at a time.) */ errr Term_text_tnb(int x, int y, int n, byte a, const char *s) { int i, xp, yp; /* Paranoia */ if (!tnb_term) return (1); for (i = 0; (i < n) && (x + i < 80); i++) { xp = (x + i - tnb_term->x_min) * tnb_term->gwidth; yp = (y - tnb_term->y_min) * tnb_term->gheight; draw_char(xp, yp, a, s[i], tnb_term); } /* Set dirty region */ dirty_line(x, y, n, tnb_term); return 0; } /* * Draw the cursor as a rectangle. */ errr Term_curs_tnb(int x, int y) { /* Paranoia */ if (!tnb_term) return (1); /* Draw the rectangle */ XDrawRectangle(tnb_term->display, tnb_term->bitmap->pixmap, tnb_term->gc, (x - tnb_term->x_min) * tnb_term->gwidth, (y - tnb_term->y_min) * tnb_term->gheight, tnb_term->gwidth - 1, tnb_term->gheight - 1); /* Success */ return (0); } /* * Redraw everything. */ static void widget_draw_all(Widget *widgetPtr) { int y, x, yp, xp; byte a, ta; char c, tc; map_block *mb_ptr; /* Paranoia: make sure the bitmap exists */ if (!widgetPtr->bitmap) return; for (x = widgetPtr->x_min; x < widgetPtr->x_max; x++) { for (y = widgetPtr->y_min; y < widgetPtr->y_max; y++) { xp = (x - widgetPtr->x_min) * widgetPtr->gwidth; yp = (y - widgetPtr->y_min) * widgetPtr->gheight; if (widgetPtr->term) { if ((x < 0) || (x >= 80) || (y < 0) || (y >= 24)) { /* Just "erase" this spot */ DrawBlank(xp, yp, widgetPtr); } else { /* Hack - Get attr/char of this location */ tnb_get_term(x, y, &a, &c, &ta, &tc); /* Draw stuff at that location */ draw_square(xp, yp, a, c, ta, tc, widgetPtr); } } else { /* Are we on the map? */ if (!map_in_bounds(x, y)) { /* Are we on the screen? */ if ((x >= widgetPtr->x_min) && (x < widgetPtr->x_max) && (y >= widgetPtr->y_min) && (y < widgetPtr->y_max)) { /* Just "erase" this spot */ DrawBlank(xp, yp, widgetPtr); } continue; } /* Get the map location */ mb_ptr = map_loc(x, y); /* Draw stuff at that location */ draw_square(xp, yp, mb_ptr->a, mb_ptr->c, mb_ptr->ta, mb_ptr->tc, widgetPtr); } } } /* Dirty the screen */ dirty_rectangle(0, 0, widgetPtr->width, widgetPtr->height, widgetPtr); } /* * Create a bitmap as big as the given Widget. We get the address of * the bits so we can write directly into the bitmap. The bitmap is * 8-bit depth with a 256-color palette. */ static void Widget_CreateBitmap(Widget *widgetPtr) { /* Create the bitmap structure */ MAKE(widgetPtr->bitmap, BitmapType); /* Calculate the bitmap dimensions in pixels */ widgetPtr->bitmap->width = widgetPtr->bw; widgetPtr->bitmap->height = widgetPtr->bh; widgetPtr->bitmap->depth = widget_icon_depth; /* Create the bitmap */ Bitmap_New(widgetPtr->interp, widgetPtr->bitmap); } /* Free the bitmap for this Widget */ static void Widget_DeleteBitmap(Widget *widgetPtr) { Bitmap_Delete(widgetPtr->bitmap); /* Free the bitmap structure */ FREE(widgetPtr->bitmap); } static void Widget_Calc(Widget *widgetPtr) { int rc, cc; int dLeft, cLeft, dRight, cRight; int dTop, rTop, dBottom, rBottom; dLeft = (widgetPtr->width - widgetPtr->gwidth) / 2; cLeft = dLeft / widgetPtr->gwidth; if (dLeft % widgetPtr->gwidth) ++cLeft; dRight = widgetPtr->width - dLeft - widgetPtr->gwidth; cRight = dRight / widgetPtr->gwidth; if (dRight % widgetPtr->gwidth) ++cRight; dTop = (widgetPtr->height - widgetPtr->gheight) / 2; rTop = dTop / widgetPtr->gheight; if (dTop % widgetPtr->gheight) ++rTop; dBottom = widgetPtr->height - dTop - widgetPtr->gheight; rBottom = dBottom / widgetPtr->gheight; if (dBottom % widgetPtr->gheight) ++rBottom; cc = cLeft + 1 + cRight; rc = rTop + 1 + rBottom; if (widgetPtr->term) { /* Calculate the limits of visibility (only for term) */ widgetPtr->y_min = 0; widgetPtr->y_max = widgetPtr->y_min + widgetPtr->height / widgetPtr->gheight; widgetPtr->x_min = 0; widgetPtr->x_max = widgetPtr->x_min + widgetPtr->width / widgetPtr->gwidth; } widgetPtr->rc = rc; widgetPtr->cc = cc; widgetPtr->bw = cc * widgetPtr->gwidth; widgetPtr->bh = rc * widgetPtr->gheight; } static void Widget_Wipe(Widget *widgetPtr) { /* Remember to redraw all grids later */ widgetPtr->flags |= WIDGET_WIPE; /* Redraw later */ Widget_EventuallyRedraw(widgetPtr); } /* * Wipe the term */ errr Term_xtra_tnb_clear(void) { /* Paranoia */ if (!tnb_term) return (1); Widget_Wipe(tnb_term); return 0; } static void Widget_Center(Widget *widgetPtr, int cx, int cy) { /* Remember new center */ widgetPtr->y = cy, widgetPtr->x = cx; /* Calculate the limits of visibility */ widgetPtr->y_min = cy - widgetPtr->rc / 2; widgetPtr->y_max = widgetPtr->y_min + widgetPtr->rc; widgetPtr->x_min = cx - widgetPtr->cc / 2; widgetPtr->x_max = widgetPtr->x_min + widgetPtr->cc; Widget_Wipe(widgetPtr); } /* * This procedure is called when the world has changed in some * way and the widget needs to recompute all its graphics contexts * and determine its new geometry. */ static void Widget_WorldChanged(ClientData instanceData) { Widget *widgetPtr = (Widget *) instanceData; Tk_Window tkwin = widgetPtr->tkwin; XGCValues gcValues; /* Allocate GC */ if (widgetPtr->gc == None) { gcValues.foreground = 0xFFFFFF; gcValues.function = GXcopy; gcValues.graphics_exposures = False; widgetPtr->gc = Tk_GetGC(tkwin, GCForeground | GCFunction | GCGraphicsExposures, &gcValues); } /* Size changed */ if ((widgetPtr->width != widgetPtr->oldWidth) || (widgetPtr->height != widgetPtr->oldHeight) || (widgetPtr->gwidth != widgetPtr->oldGWidth) || (widgetPtr->gheight != widgetPtr->oldGHeight)) { Widget_Calc(widgetPtr); } /* The bitmap is not the right size */ if ((widgetPtr->bitmap) && ((widgetPtr->bw != widgetPtr->bitmap->width) || (widgetPtr->bh != widgetPtr->bitmap->height))) { /* Delete the bitmap */ Widget_DeleteBitmap(widgetPtr); /* Forget the bitmap */ widgetPtr->bitmap = NULL; } /* No bitmap yet */ if (!widgetPtr->bitmap) { /* Allocate bitmap */ Widget_CreateBitmap(widgetPtr); } /* The widget is of non-zero size */ if ((widgetPtr->width > 0) && (widgetPtr->height > 0)) { /* Request geometry */ Tk_GeometryRequest(tkwin, widgetPtr->width, widgetPtr->height); } /* Remember the current info */ widgetPtr->oldWidth = widgetPtr->width; widgetPtr->oldHeight = widgetPtr->height; widgetPtr->oldGWidth = widgetPtr->gwidth; widgetPtr->oldGHeight = widgetPtr->gheight; /* Redraw the window (later) */ Widget_Wipe(widgetPtr); } /* * This procedure is invoked when a Widget command is deleted. If * the Widget isn't already in the process of being destroyed, * this command destroys it. */ static void Widget_CmdDeletedProc(ClientData clientData) { Widget *widgetPtr = (Widget *) clientData; /* * This procedure could be invoked either because the window was * destroyed and the command was then deleted or because the command * was deleted, and then this procedure destroys the widget. The * WIDGET_DELETED flag distinguishes these cases. */ if (!(widgetPtr->flags & WIDGET_DELETED)) { Tk_DestroyWindow(widgetPtr->tkwin); } } static Tk_OptionTable optionTable = None; /* * Fiddle with configuration options for a Widget */ static int Widget_Configure(Tcl_Interp *interp, Widget *widgetPtr, int objc, Tcl_Obj *CONST objv[]) { Tk_SavedOptions savedOptions; Tcl_Obj *errorResult = NULL; int error; /* * The following loop is potentially executed twice. During the * first pass configuration options get set to their new values. * If there is an error in this pass, we execute a second pass * to restore all the options to their previous values. */ for (error = 0; error <= 1; error++) { if (!error) { /* * First pass: set options to new values. */ if (Tk_SetOptions(interp, (char *) widgetPtr, optionTable, objc, objv, widgetPtr->tkwin, &savedOptions, NULL) != TCL_OK) { continue; } } else { /* * Second pass: restore options to old values. */ errorResult = Tcl_GetObjResult(interp); Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); } /* Require gwidth == gheight. Why not have just one option? */ if (widgetPtr->gwidth != widgetPtr->gheight) { /* Set the error */ Tcl_AppendResult(interp, "expected gwidth equal to gheight", NULL); /* Failure */ continue; } break; } if (!error) { Tk_FreeSavedOptions(&savedOptions); } /* Recompute geometry, etc */ Widget_WorldChanged((ClientData) widgetPtr); if (error) { Tcl_SetObjResult(interp, errorResult); Tcl_DecrRefCount(errorResult); /* Failure */ return TCL_ERROR; } /* Success */ return TCL_OK; } static int Widget_CaveToView(Widget *widgetPtr, int y, int x, int *rowPtr, int *colPtr) { if ((y < widgetPtr->y_min) || (y >= widgetPtr->y_max)) return FALSE; if ((x < widgetPtr->x_min) || (x >= widgetPtr->x_max)) return FALSE; *rowPtr = y - widgetPtr->y_min; *colPtr = x - widgetPtr->x_min; return TRUE; } /* * Map hooks for the widget */ /* Want to redraw this square */ static void Widget_map_info(map_block *mb_ptr, const term_map *map, vptr data) { Widget *widgetPtr = (Widget *) data; int xp, yp; int x = map->x, y = map->y; if (widgetPtr->flags & WIDGET_WIPE) return; /* Needs to be on the screen */ if ((x < widgetPtr->x_min) || (x >= widgetPtr->x_max)) return; if ((y < widgetPtr->y_min) || (y >= widgetPtr->y_max)) return; /* Bitmap coords */ xp = (x - widgetPtr->x_min) * widgetPtr->gwidth; yp = (y - widgetPtr->y_min) * widgetPtr->gheight; /* Draw stuff at this location */ draw_square(xp, yp, mb_ptr->a, mb_ptr->c, mb_ptr->ta, mb_ptr->tc, widgetPtr); /* Dirty the square */ dirty_rectangle(xp, yp, xp + widgetPtr->gwidth - 1, yp + widgetPtr->gheight - 1, widgetPtr); } static void Widget_map_erase(vptr data) { /* Wipe the screen */ Widget_Wipe((Widget *) data); } static void Widget_player_move(int x, int y, vptr data) { /* Recenter the widget and redraw */ Widget_Center((Widget *) data, x, y); } /* * This is the window-specific command created for each new Widget. */ static int Widget_WidgetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { static cptr commandNames[] = {"caveyx", "center", "configure", "wipe", "bounds", "visible", "hittest", NULL}; enum {IDX_CAVEYX, IDX_CENTER, IDX_CONFIGURE, IDX_WIPE, IDX_BOUNDS, IDX_VISIBLE, IDX_HITTEST}; int option; Widget *widgetPtr = (Widget *) clientData; int result; Tcl_Obj *objPtr; /* Required number of arguments */ if (objc < 2) { /* Set the error */ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); /* Failure */ return TCL_ERROR; } result = Tcl_GetIndexFromObj(interp, objv[1], commandNames, "option", 0, &option); if (result != TCL_OK) { return result; } /* * Since this command could possibly destroy the window, and * the window may be in use somewhere up the calling stack, * we increase a reference count to prevent the memory being * freed too soon. */ Tcl_Preserve((ClientData) widgetPtr); switch (option) { case IDX_CAVEYX: /* caveyx */ { int y, x, yc, xc; int row, col; char buffer[20]; /* Get x coord */ if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) { goto error; } /* Get y coord */ if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) { goto error; } if (x < 0 || x >= widgetPtr->width) break; if (y < 0 || y >= widgetPtr->height) break; row = y / widgetPtr->gheight; col = x / widgetPtr->gwidth; yc = widgetPtr->y_min + row; xc = widgetPtr->x_min + col; strnfmt(buffer, 20, "%d %d", yc, xc); Tcl_SetStringObj(Tcl_GetObjResult(interp), buffer, -1); break; } case IDX_CENTER: /* center */ { char buffer[20]; /* New coordinates were given */ if (objc == 4) { int y, x; /* Get y location */ if (Tcl_GetIntFromObj(interp, objv[2], &y) != TCL_OK) { goto error; } /* Get x location */ if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { goto error; } /* Center the widget */ Widget_Center(widgetPtr, x, y); } else { /* Print the current center */ strnfmt(buffer, 20, "%d %d", widgetPtr->y, widgetPtr->x); /* Return the center */ Tcl_SetStringObj(Tcl_GetObjResult(interp), buffer, -1); } break; } case IDX_CONFIGURE: /* configure */ { if (objc <= 3) { objPtr = Tk_GetOptionInfo(interp, (char *) widgetPtr, optionTable, (objc == 3) ? objv[2] : NULL, widgetPtr->tkwin); if (objPtr == NULL) { goto error; } else { Tcl_SetObjResult(interp, objPtr); } } else { result = Widget_Configure(interp, widgetPtr, objc - 2, objv + 2); } break; } case IDX_WIPE: /* wipe */ { /* Schedule a complete redraw */ Widget_Wipe(widgetPtr); break; } case IDX_BOUNDS: /* bounds */ { char buf[32]; strnfmt(buf, 32, "%d %d %d %d", widgetPtr->y_min, widgetPtr->x_min, widgetPtr->y_max - 1, widgetPtr->x_max - 1); Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1); break; } case IDX_VISIBLE: /* visible */ { int y, x, r, c, vis; /* Required number of arguments */ if (objc != 4) { /* Set the error */ Tcl_WrongNumArgs(interp, 2, objv, "y x"); /* Failure */ goto error; } /* Get y location */ if (Tcl_GetIntFromObj(interp, objv[2], &y) != TCL_OK) { goto error; } /* Get x location */ if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { goto error; } vis = Widget_CaveToView(widgetPtr, y, x, &r, &c); if (vis) { int yp, xp, h, w; yp = r * widgetPtr->gheight; xp = c * widgetPtr->gwidth; h = widgetPtr->gheight; w = widgetPtr->gwidth; if (yp < 0 || yp + h > widgetPtr->height || xp < 0 || xp + w > widgetPtr->width) { vis = FALSE; } } Tcl_SetBooleanObj(Tcl_GetObjResult(interp), vis); break; } case IDX_HITTEST: /* hittest */ { int y, x, row, col, yc, xc; int layer = -1; char buffer[20]; /* Get x coord */ if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) { goto error; } /* Get y coord */ if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) { goto error; } if (x < 0 || x >= widgetPtr->width) break; if (y < 0 || y >= widgetPtr->height) break; row = y / widgetPtr->gheight; col = x / widgetPtr->gwidth; yc = widgetPtr->y_min + row; xc = widgetPtr->x_min + col; strnfmt(buffer, 20, "%d %d %d", yc, xc, layer); Tcl_SetStringObj(Tcl_GetObjResult(interp), buffer, -1); break; } } /* Decrease the reference count */ Tcl_Release((ClientData) widgetPtr); /* Result */ return result; error: /* Decrease the reference count */ Tcl_Release((ClientData) widgetPtr); /* Failure */ return TCL_ERROR; } /* * This procedure is invoked by Tcl_EventuallyFree() or Tcl_Release() * to clean up the internal structure of a Widget at a safe time * (when no-one is using it anymore). */ static void Widget_Destroy(Widget *widgetPtr) { widgetPtr->flags |= WIDGET_DELETED; if (widgetPtr->flags & WIDGET_REDRAW) { Tcl_CancelIdleCall(Widget_Display, (ClientData) widgetPtr); } /* * Free up all the stuff that requires special handling, then * let Tk_FreeOptions handle all the standard option-related * stuff. */ Tcl_DeleteCommandFromToken(widgetPtr->interp, widgetPtr->widgetCmd); /* Free a GC */ if (widgetPtr->gc != None) { Tk_FreeGC(widgetPtr->display, widgetPtr->gc); } /* Free the bitmap */ if (widgetPtr->bitmap) { Widget_DeleteBitmap(widgetPtr); } /* Free the options table */ Tk_FreeConfigOptions((char *) widgetPtr, optionTable, widgetPtr->tkwin); widgetPtr->tkwin = NULL; if (widgetPtr->term) { /* We no longer have a term */ tnb_term = NULL; } else { /* Free the callbacks */ del_callback(CALL_MAP_INFO, widgetPtr); del_callback(CALL_MAP_ERASE, widgetPtr); del_callback(CALL_PLAYER_MOVE, widgetPtr); } /* Free the font, if any */ if (widgetPtr->font) Bitmap_Delete(widgetPtr->font); /* Free the tiles */ if (widgetPtr->tiles) Bitmap_Delete(widgetPtr->tiles); /* Free the Widget struct */ Tcl_EventuallyFree((ClientData) widgetPtr, Tcl_Free); } /* * This procedure is invoked by the Tk dispatcher for various * events on a Widget. */ static void Widget_EventProc(ClientData clientData, XEvent *eventPtr) { Widget *widgetPtr = (Widget *) clientData; /* A region of the window became newly visible */ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { /* Redraw the window */ goto redraw; } /* The window was resized */ else if (eventPtr->type == ConfigureNotify) { /* Redraw the window */ goto redraw; } /* The window is about to be destroyed */ else if (eventPtr->type == DestroyNotify) { /* Destroy the window */ Widget_Destroy(widgetPtr); } /* Done */ return; redraw: /* Redraw later */ widgetPtr->flags |= WIDGET_EXPOSE; Widget_EventuallyRedraw(widgetPtr); } /* Hack - Tk versions before 8.4 used an internal interface for this */ #ifdef HAVE_TK_SETCLASSPROCS /* Table of procedures for the "Widget" class */ static Tk_ClassProcs widgetProcs = { sizeof(Tk_ClassProcs), Widget_WorldChanged, /* geometryProc. */ NULL, /* createProc. */ NULL /* modalProc. */ }; #else /* HAVE_TK_SETCLASSPROCS */ typedef Window (TkClassCreateProc) _ANSI_ARGS_((Tk_Window tkwin, Window parent, ClientData instanceData)); typedef void (TkClassGeometryProc) _ANSI_ARGS_((ClientData instanceData)); typedef void (TkClassModalProc) _ANSI_ARGS_((Tk_Window tkwin, XEvent *eventPtr)); typedef struct TkClassProcs { TkClassCreateProc *createProc; /* Procedure to invoke when the platform-dependent window needs to be created. */ TkClassGeometryProc *geometryProc; /* Procedure to invoke when the geometry of a window needs to be recalculated as a result of some change in the system. */ TkClassModalProc *modalProc; /* Procedure to invoke after all bindings on a widget have been triggered in order to handle a modal loop. */ } TkClassProcs; struct TkClassProcs widgetProcs = { NULL, Widget_WorldChanged, NULL }; extern void TkSetClassProcs(Tk_Window, TkClassProcs *, ClientData); #define Tk_SetClassProcs TkSetClassProcs #endif /* !HAVE_TK_SETCLASSPROCS */ /* * Table specifying legal configuration options for a Widget. */ static Tk_OptionSpec optionSpecs[20] = { {TK_OPTION_INT, (char *) "-height", (char *) "height", (char *) "Height", (char *) "100", -1, Tk_Offset(Widget, height), 0, 0, 0}, {TK_OPTION_INT, (char *) "-width", (char *) "width", (char *) "Width", (char *) "100", -1, Tk_Offset(Widget, width), 0, 0, 0}, {TK_OPTION_INT, (char *) "-gheight", (char *) "gheight", (char *) "Height", (char *) "32", -1, Tk_Offset(Widget, gheight), 0, 0, 0}, {TK_OPTION_INT, (char *) "-gwidth", (char *) "gwidth", (char *) "Width", (char *) "32", -1, Tk_Offset(Widget, gwidth), 0, 0, 0}, {TK_OPTION_BOOLEAN, (char *) "-term", (char *) "term", (char *) "Term", (char *) "0", -1, Tk_Offset(Widget, term), 0, 0, 0}, {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} }; /* *-------------------------------------------------------------- * * Widget_ObjCmd -- * * Called by Tcl to implement the "widget" command. This is * the procedure passed to Tcl_CreateCommand() inside * Widget_Init(). * *-------------------------------------------------------------- */ static int Widget_ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Widget *widgetPtr; Tk_Window tkwin; optionTable = (Tk_OptionTable) clientData; if (optionTable == NULL) { Tcl_CmdInfo info; char *name; /* * We haven't created the option table for this widget class * yet. Do it now and save the table as the clientData for * the command, so we'll have access to it in future * invocations of the command. */ optionTable = Tk_CreateOptionTable(interp, optionSpecs); name = Tcl_GetString(objv[0]); Tcl_GetCommandInfo(interp, name, &info); info.objClientData = (ClientData) optionTable; Tcl_SetCommandInfo(interp, name, &info); } /* Required number of arguments */ if (objc < 2) { /* Set the error */ Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); /* Failure */ return(TCL_ERROR); } /* Create a new Tk window with the given name */ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), NULL); /* The window could not be created */ if (tkwin == NULL) { return TCL_ERROR; } /* * Set the window class. By convention all class names start with * a capital letter, and there exists a Tcl command with the same * name as each class. */ Tk_SetClass(tkwin, "Widget"); /* Create the pointer */ MAKE(widgetPtr, Widget); /* Set the class callbacks for the new Widget */ Tk_SetClassProcs(tkwin, &widgetProcs, (ClientData) widgetPtr); /* Set some fields */ widgetPtr->tkwin = tkwin; widgetPtr->display = Tk_Display(tkwin); widgetPtr->interp = interp; /* * Note here we are creating a new Tcl command that has the same * name as the full pathname of the new Tk window (this Widget). */ widgetPtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(tkwin), Widget_WidgetObjCmd, (ClientData) widgetPtr, Widget_CmdDeletedProc); /* Set more fields */ widgetPtr->gc = None; widgetPtr->width = 0; widgetPtr->height = 0; widgetPtr->gwidth = tnb_tile_x; widgetPtr->gheight = tnb_tile_y; widgetPtr->oldWidth = widgetPtr->oldHeight = 0; widgetPtr->oldGWidth = widgetPtr->oldGHeight = 0; widgetPtr->flags = 0; widgetPtr->y = widgetPtr->x = 0; widgetPtr->y_min = widgetPtr->y_max = 0; widgetPtr->x_min = widgetPtr->x_max = 0; widgetPtr->dx1 = widgetPtr->dy1 = 0; widgetPtr->dx2 = widgetPtr->dy2 = 0; widgetPtr->term = 0; /* * Arrange for our routine to be called when any of the specified * events occur to our new Widget. */ Tk_CreateEventHandler(widgetPtr->tkwin, ExposureMask | StructureNotifyMask | FocusChangeMask, Widget_EventProc, (ClientData) widgetPtr); /* Set the default options for the new widget */ if (Tk_InitOptions(interp, (char *) widgetPtr, optionTable, tkwin) != TCL_OK) { /* Destroy the Tk window */ Tk_DestroyWindow(widgetPtr->tkwin); /* Failure */ return TCL_ERROR; } /* Parse the rest of the arguments for option/value pairs */ if (Widget_Configure(interp, widgetPtr, objc - 2, objv + 2) != TCL_OK) { /* Destroy the Tk window */ Tk_DestroyWindow(widgetPtr->tkwin); /* Failure */ return TCL_ERROR; } if (widgetPtr->term) { /* We now have a term */ tnb_term = widgetPtr; } else { /* Hack - initialise the hooks into the overhead map code */ /* Save the tk hooks into the overhead map */ set_callback((callback_type) Widget_map_info, CALL_MAP_INFO, widgetPtr); set_callback((callback_type) Widget_map_erase, CALL_MAP_ERASE, widgetPtr); /* Save old player movement hook */ set_callback((callback_type) Widget_player_move, CALL_PLAYER_MOVE, widgetPtr); } /* Load the tiles */ widgetPtr->tiles = Bitmap_Load(g_interp, tnb_tile_file); /* Load the font */ widgetPtr->font = Font_Load(g_interp, tnb_font_file, tnb_font_size); /* Return the window pathname */ Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(widgetPtr->tkwin), -1); /* Success */ return TCL_OK; } /* * Initialize the Widget package */ int init_widget(Tcl_Interp *interp, int g_icon_depth) { /* Create the "widget" interpreter command */ Tcl_CreateObjCommand(interp, "widget", Widget_ObjCmd, NULL, NULL); /* Save colour depth for later */ widget_icon_depth = g_icon_depth; /* Initialise palette stuff */ if (g_icon_depth == 16) init_masks(interp); /* Success */ return TCL_OK; } zangband/src/tk/icon.h0000644000000000000000000000374310250356275013662 0ustar rootroot/* File: icon.h */ /* Purpose: icon environment definitions */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #ifndef _INCLUDE_ICON_H_ #define _INCLUDE_ICON_H_ typedef struct IconSpec { int type; int index; } IconSpec; extern int g_icon_depth; /* 8, 16 or 24 */ extern long g_icon_length; /* 32x32 x 32bits = 32x32x4 */ #define ICON_LENGTH_MAX (4096L * 4) typedef byte IconData[ICON_LENGTH_MAX]; typedef byte *IconPtr; typedef union PixelPtr { byte *pix8; u16b *pix16; u32b *pix24; } PixelPtr; typedef struct t_icon_data { cptr desc; /* type name */ IconPtr icon_data; /* Address of icon data */ int icon_count; /* Number of icons */ int depth; /* Bits per pixel (8, 16, 24) */ int bypp; /* Bytes per pixel (1, 2, 3 or 4) */ int width; /* Pixels per column */ int height; /* Pixels per row */ int pitch; /* Convenience: width * bypp */ int length; /* Convenience: width * height * bypp */ int pixels; /* Convenience: width * height */ } t_icon_data; extern t_icon_data *g_icon_data; /* Array of icon types */ extern int g_icon_data_count; /* Number of icon types */ extern void PixelSet_RGB(IconPtr dst, int r, int g, int b, int bypp); extern int Icon_Init(Tcl_Interp *interp, int size, int depth); /* * Constants for g_icon_data[] index. * icon_type.type constants. */ /* Special type "none". It's a transparent icon. */ #define ICON_TYPE_NONE 0 #define ICON_TYPE_BLANK 1 #define ICON_TYPE_DEFAULT 2 /* One assigned icon */ typedef struct t_assign_icon { int type; int index; } t_assign_icon; /* * Icon assignment for each member of each group (monster, * object, features, etc) */ typedef struct t_assign_group { int count; /* Number of elements in array */ t_assign_icon *assign; /* Array of iassignments */ } t_assign_group; extern byte *g_palette_rgb; #endif /* _INCLUDE_ICON_H_ */ zangband/src/tk/tnb.h0000644000000000000000000001673010250356275013515 0ustar rootroot/* File: tnb.h */ /* Purpose: AngbandTk header */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #ifndef _INCLUDE_TNB_H_ #define _INCLUDE_TNB_H_ #include #include #include "../angband.h" #if !defined(PLATFORM_WIN) && !defined(PLATFORM_X11) #error "You must define one of PLATFORM_WIN or PLATFORM_X11" #endif /* */ /* Hack - prevent warnings from tk headers */ #ifdef errno # undef errno # define errno errno_hack #endif /* errno */ #ifdef PLATFORM_WIN # include # include #endif /* PLATFORM_WIN */ #ifdef PLATFORM_X11 /* Hack for makefile.std */ # ifndef HAVE_LIMITS_H # define HAVE_LIMITS_H # define HAVE_UNISTD_H # endif /* * Hack - prevent a huge number of compiler warnings when * is included indirectly below. */ /* # define _TCLINTDECLS */ /* # include */ #endif /* PLATFORM_X11 */ /* main-tnb.c */ extern bool game_in_progress; extern char ANGBAND_DIR_TK[1024]; extern Tcl_Interp *g_interp; extern int tnb_tile_x; extern int tnb_tile_y; extern char tnb_tile_file[1024]; extern char tnb_font_file[1024]; extern int tnb_font_size; extern void tnb_get_term(int x, int y, byte *a, char *c, byte *ta, char *tc); /* plat.c */ typedef struct BitmapType { unsigned char *pixelPtr; /* Address of top-left pixel */ int width; /* Width in pixels */ int height; /* Height in pixels */ int depth; /* 8, 16 or 24 */ int pitch; /* Address difference between vertically adjacent pixels */ int pixelSize; /* Address difference between horizontally adjacent pixels */ Pixmap pixmap; void *platData; /* Platform-specific info */ } BitmapType, *BitmapPtr; extern void *Plat_PaletteInit(unsigned char *rgb); extern int Plat_XColor2Pixel(XColor *xColorPtr); extern void Plat_SyncDisplay(Display *display); extern BitmapPtr Font_Load(Tcl_Interp *interp, cptr name, int size); extern void Bitmap_New(Tcl_Interp *interp, BitmapPtr bitmapPtr); extern void Bitmap_Delete(BitmapPtr bitmapPtr); extern BitmapPtr Bitmap_Load(Tcl_Interp *interp, cptr name); /* Widget.c */ extern errr Term_xtra_tnb_react(void); extern errr Term_wipe_tnb(int x, int y, int n); extern errr Term_text_tnb(int x, int y, int n, byte a, const char *s); extern errr Term_pict_tnb(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp); extern errr Term_curs_tnb(int x, int y); extern errr Term_xtra_tnb_clear(void); /* canv-widget.c */ extern byte *get_icon_ptr(BitmapPtr bitmap_ptr, int x, int y); extern int CanvasWidget_Init(Tcl_Interp *interp); /* cmdinfo.c */ typedef struct CommandInfo CommandInfo; typedef struct CommandInit CommandInit; typedef struct SubCommandInfo SubCommandInfo; struct SubCommandInfo { int alloc; int count; cptr *name; CommandInfo **info; }; struct CommandInfo { int depth; cptr name; int minArgs, maxArgs; cptr errorMsg; Tcl_ObjCmdProc *proc; ClientData clientData; SubCommandInfo subCmd; }; struct CommandInit { int depth; cptr name; int minArgs, maxArgs; cptr errorMsg; Tcl_ObjCmdProc *proc; ClientData clientData; }; extern void CommandInfo_Add(CommandInfo *infoCmd, CommandInfo *infoSubCmd); extern CommandInfo *CommandInfo_GetInfo(Tcl_Interp *interp, cptr names[]); extern int CommandInfo_InitAux(Tcl_Interp *interp, CommandInit *init, int index, CommandInfo *parent); extern int CommandInfo_Init(Tcl_Interp *interp, CommandInit *init, CommandInfo *parent); extern CommandInfo *CommandInfo_New(CommandInit *init); extern int CommandInfo_ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); /* icon1.c */ extern int g_icon_size; /* 16, 24 or 32 */ extern void init_icons(int size, int depth); extern void Icon_Exit(void); extern void init_palette(void); /* interp.c */ extern int exit_skip_save; extern bool command_repeating; extern void angtk_angband_initialized(void); extern void angtk_eval(cptr command, ...); extern int angtk_eval_file(cptr extFileName); extern void angtk_health(char *buf); extern void angtk_init(void); extern cptr player_status(int status, int *value); /* * XXXXX Important! * If you add INKEY_XXX flags here, you must update the inkey_to_str[] * array in interp2.c */ #define INKEY_CMD 1 #define INKEY_DIR 2 #define INKEY_DISTURB 3 #define INKEY_ITEM 4 #define INKEY_ITEM_STORE 5 #define INKEY_MORE 6 #define INKEY_SPELL 7 #define INKEY_TARGET 8 #define INKEY_POWER 9 #define INKEY_CMD_PET 10 extern int inkey_book; extern int inkey_flags; /* describe.c */ extern cptr keyword_slot[]; extern int strcpy_len(char *s1, const char *s2); extern long angtk_describe_object(object_type *o_ptr, char *buf, bool in_store); extern int SetArrayValueChar(cptr varName, cptr field, char value); extern int SetArrayValueLong(cptr varName, cptr field, long value); extern int SetArrayValueString(cptr varName, cptr field, cptr value); /* tcltk.c */ extern Tcl_Interp *TclTk_Init(cptr *argv); /* util-tnb.c */ extern cptr keyword_term_color[]; extern byte g_prompt_attr; extern void prompt_print(cptr str); extern void prompt_erase(void); extern void prompt_format(cptr fmt, ...); extern void prompt_append(cptr str); extern void prompt_open(cptr str); extern void prompt_update(cptr str); extern void any_more(cptr prompt); extern int ExtToUtf_SetArrayValueString(cptr varName, cptr field, cptr value); /* util-dll.c */ extern int g_palette_white, g_palette_black; #define PALETTE_WHITE g_palette_white #define PALETTE_BLACK g_palette_black extern int g_colormap_white, g_colormap_black; #define COLORMAP_WHITE g_colormap_white #define COLORMAP_BLACK g_colormap_black extern unsigned char g_palette2colormap[256]; extern int Palette_Init(Tcl_Interp *interp); extern unsigned char *Palette_GetRGB(void); extern void Palette_ResetHash(void); extern int Palette_RGB2Index(unsigned char r, unsigned char g, unsigned char b); extern int Colormap_Init(Tcl_Interp *interp); extern unsigned char *Colormap_GetRGB(void); extern int Colormap_RGB2Index(unsigned char r, unsigned char g, unsigned char b); typedef struct RGBInfo RGBInfo; struct RGBInfo { int red_count, green_count, blue_count; int red_mask, green_mask, blue_mask; int red_shift, green_shift, blue_shift; int extra; }; extern RGBInfo g_rgbi; extern void SetPix16(unsigned char *p, int r, int g, int b); extern void *Array_Append(void *array_ptr, int *count, int elem_size, void *elem_ptr); extern void *Array_Insert(void *array_ptr, int *count, int elem_size, int index); extern void *Array_Delete(void *array_ptr, int *count, int elem_size, int index); extern Tcl_Obj *ExtToUtf_NewStringObj(CONST char *bytes, int length); extern void ExtToUtf_SetResult(Tcl_Interp *interp, cptr string); extern char *UtfToExt_TranslateFileName(Tcl_Interp *interp, char *utfPath, Tcl_DString *extDStringPtr); /* Make a tk hook function called 'objcmd_name' */ #define DECLARE_TK_HOOK(N) \ extern int objcmd_##N (ClientData clientData, \ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) /* DECLARE_TK_HOOK(angband); */ DECLARE_TK_HOOK(player); DECLARE_TK_HOOK(cave); DECLARE_TK_HOOK(floor); DECLARE_TK_HOOK(game); DECLARE_TK_HOOK(init_icons); DECLARE_TK_HOOK(inkey_flags); DECLARE_TK_HOOK(inventory); DECLARE_TK_HOOK(keycount); DECLARE_TK_HOOK(keypress); DECLARE_TK_HOOK(message); DECLARE_TK_HOOK(fontdesc); DECLARE_TK_HOOK(equipinfo); DECLARE_TK_HOOK(inveninfo); #endif /* _INCLUDE_TNB_H_ */ extern int init_widget(Tcl_Interp *interp, int g_icon_depth); zangband/src/tk/makefile.zb0000644000000000000000000000073610250356275014672 0ustar rootrootsubdir = ./src/tk ## ## The tk port ## TKOBJS = $(addprefix src/tk/,\ cmdinfo.o icon.o \ describe.o interp.o plat.o \ widget.o tk-util.o tcltk.o) objs-$(TK_PORT) += $(TKOBJS) ## ## Extra dependancies for the tk port ## $(TKOBJS): $(INCS) src/tk/tnb.h src/main-tnb.o: src/tk/tnb.h src/tk/icon.o src/tk/interp.o: src/tk/icon.h srcdirlist += src/tk srcfiles += src/tk/*.c src/tk/*.h src/tk/makefile.zb dust-files += src/tk/*~ clean-files += src/tk/*.bak src/tk/*.o zangband/src/artifact.c0000755000000000000000000014525010250356274014106 0ustar rootroot/* File: artifact.c */ /* Purpose: Artifact code */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* Chance of using syllables to form the name instead of the "template" files */ #define TABLE_NAME 45 /* Chance of a random artifact being cursed (1 in 13) */ #define A_CURSED 13 /* Chance of getting a 'basic 4' immunity rather than resist (1 in 48) */ #define LOW_IM_LUCK 48 /* Chance of getting a 'high' immunity rather than resist (1 in 12) */ #define HI_IM_LUCK 12 #define ACTIVATION_CHANCE 3 static void random_plus(object_type *o_ptr) { bad_type: switch (randint1(o_ptr->tval < TV_BOOTS ? 24 : 20)) { case 1: case 2: SET_FLAG(o_ptr, TR_STR); break; case 3: case 4: SET_FLAG(o_ptr, TR_INT); break; case 5: case 6: SET_FLAG(o_ptr, TR_WIS); break; case 7: case 8: SET_FLAG(o_ptr, TR_DEX); break; case 9: case 10: SET_FLAG(o_ptr, TR_CON); break; case 11: case 12: SET_FLAG(o_ptr, TR_CHR); break; case 13: case 14: SET_FLAG(o_ptr, TR_STEALTH); break; case 15: case 16: SET_FLAG(o_ptr, TR_SEARCH); break; case 17: case 18: SET_FLAG(o_ptr, TR_INFRA); break; case 19: SET_FLAG(o_ptr, TR_SPEED); break; case 20: SET_FLAG(o_ptr, TR_SP); break; case 21: case 22: SET_FLAG(o_ptr, TR_TUNNEL); break; case 23: case 24: if (o_ptr->tval == TV_BOW) goto bad_type; SET_FLAG(o_ptr, TR_BLOWS); break; } } static void random_resistance(object_type *o_ptr, int specific) { bad_type: switch (specific ? specific : randint1(42)) { case 1: case 5: case 6: case 13: if (!one_in_(LOW_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_ACID); else SET_FLAG(o_ptr, TR_IM_ACID); break; case 2: case 7: case 8: case 14: if (!one_in_(LOW_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_ELEC); else SET_FLAG(o_ptr, TR_IM_ELEC); break; case 3: case 11: case 12: case 16: if (!one_in_(LOW_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_COLD); else SET_FLAG(o_ptr, TR_IM_COLD); break; case 4: case 9: case 10: case 15: if (!one_in_(LOW_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_FIRE); else SET_FLAG(o_ptr, TR_IM_FIRE); break; case 17: case 18: if (!one_in_(LOW_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_POIS); else SET_FLAG(o_ptr, TR_IM_POIS); break; case 19: case 20: SET_FLAG(o_ptr, TR_RES_FEAR); break; case 21: if (!one_in_(HI_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_LITE); else SET_FLAG(o_ptr, TR_IM_LITE); break; case 22: if (!one_in_(HI_IM_LUCK)) SET_FLAG(o_ptr, TR_RES_DARK); else SET_FLAG(o_ptr, TR_IM_DARK); break; case 23: case 24: SET_FLAG(o_ptr, TR_RES_BLIND); break; case 25: case 26: SET_FLAG(o_ptr, TR_RES_CONF); break; case 27: case 28: SET_FLAG(o_ptr, TR_RES_SOUND); break; case 29: case 30: SET_FLAG(o_ptr, TR_RES_SHARDS); break; case 31: case 32: SET_FLAG(o_ptr, TR_RES_NETHER); break; case 33: case 34: SET_FLAG(o_ptr, TR_RES_NEXUS); break; case 35: case 36: SET_FLAG(o_ptr, TR_RES_CHAOS); break; case 37: case 38: SET_FLAG(o_ptr, TR_RES_DISEN); break; case 39: if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR) SET_FLAG(o_ptr, TR_SH_ELEC); else SET_FLAG(o_ptr, TR_RES_ELEC); break; case 40: if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR) SET_FLAG(o_ptr, TR_SH_FIRE); else SET_FLAG(o_ptr, TR_RES_FIRE); break; case 41: if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR) SET_FLAG(o_ptr, TR_SH_COLD); else SET_FLAG(o_ptr, TR_RES_COLD); break; /* Note: SH_ACID is deliberately omitted here */ case 42: if (o_ptr->tval != TV_SHIELD && o_ptr->tval != TV_CLOAK && o_ptr->tval != TV_HELM && o_ptr->tval != TV_HARD_ARMOR) { goto bad_type; } SET_FLAG(o_ptr, TR_REFLECT); break; } } static void random_misc(object_type *o_ptr) { bad_type: switch (randint1(39)) { case 1: SET_FLAG(o_ptr, TR_SUST_STR); break; case 2: SET_FLAG(o_ptr, TR_SUST_INT); break; case 3: SET_FLAG(o_ptr, TR_SUST_WIS); break; case 4: SET_FLAG(o_ptr, TR_SUST_DEX); break; case 5: SET_FLAG(o_ptr, TR_SUST_CON); break; case 6: SET_FLAG(o_ptr, TR_SUST_CHR); break; case 7: case 8: case 14: SET_FLAG(o_ptr, TR_FREE_ACT); break; case 9: SET_FLAG(o_ptr, TR_HOLD_LIFE); break; case 10: case 11: SET_FLAG(o_ptr, TR_LITE); break; case 12: case 13: SET_FLAG(o_ptr, TR_FEATHER); break; case 15: if (o_ptr->tval != TV_GLOVES) goto bad_type; SET_FLAG(o_ptr, TR_GHOUL_TOUCH); break; case 16: case 17: SET_FLAG(o_ptr, TR_SEE_INVIS); break; case 18: SET_FLAG(o_ptr, TR_TELEPATHY); break; case 19: case 20: SET_FLAG(o_ptr, TR_SLOW_DIGEST); break; case 21: case 22: SET_FLAG(o_ptr, TR_REGEN); break; case 23: SET_FLAG(o_ptr, TR_TELEPORT); break; case 24: case 25: case 26: SET_FLAG(o_ptr, TR_SHOW_MODS); o_ptr->to_a += (s16b)rand_range(5, 15); break; case 27: case 28: case 29: SET_FLAG(o_ptr, TR_SHOW_MODS); o_ptr->to_h += (s16b)rand_range(5, 15); o_ptr->to_d += (s16b)rand_range(5, 15); break; case 30: SET_FLAG(o_ptr, TR_NO_MAGIC); break; case 31: SET_FLAG(o_ptr, TR_NO_TELE); break; case 32: case 33: case 34: /* A slay on a non-weapon gives protection */ switch (randint1(8)) { case 1: SET_FLAG(o_ptr, TR_SLAY_ANIMAL); break; case 2: SET_FLAG(o_ptr, TR_SLAY_EVIL); break; case 3: SET_FLAG(o_ptr, TR_SLAY_UNDEAD); break; case 4: SET_FLAG(o_ptr, TR_SLAY_DEMON); break; case 5: SET_FLAG(o_ptr, TR_SLAY_ORC); break; case 6: SET_FLAG(o_ptr, TR_SLAY_TROLL); break; case 7: SET_FLAG(o_ptr, TR_SLAY_GIANT); break; case 8: SET_FLAG(o_ptr, TR_SLAY_DRAGON); break; } break; case 35: SET_FLAG(o_ptr, TR_MUTATE); break; case 36: SET_FLAG(o_ptr, TR_PATRON); break; case 37: SET_FLAG(o_ptr, TR_STRANGE_LUCK); break; case 38: SET_FLAG(o_ptr, TR_LUCK_10); break; case 39: if (o_ptr->tval != TV_BOOTS) goto bad_type; SET_FLAG(o_ptr, TR_WILD_WALK); break; } } static void random_curse(object_type *o_ptr, bool evil) { switch (randint1(evil ? 32 : 18)) { case 1: case 19: SET_FLAG(o_ptr, TR_HURT_ACID); break; case 2: case 20: SET_FLAG(o_ptr, TR_HURT_ELEC); break; case 3: case 21: SET_FLAG(o_ptr, TR_HURT_FIRE); break; case 4: case 22: SET_FLAG(o_ptr, TR_HURT_COLD); break; case 5: SET_FLAG(o_ptr, TR_HURT_LITE); break; case 6: SET_FLAG(o_ptr, TR_HURT_DARK); break; case 7: case 8: SET_FLAG(o_ptr, TR_AGGRAVATE); break; case 9: SET_FLAG(o_ptr, TR_SLOW_HEAL); break; case 10: case 23: SET_FLAG(o_ptr, TR_DRAIN_STATS); break; case 11: case 12: SET_FLAG(o_ptr, TR_AUTO_CURSE); break; case 13: case 14: SET_FLAG(o_ptr, TR_CANT_EAT); break; case 15: case 16: SET_FLAG(o_ptr, TR_CURSED); break; case 17: o_ptr->to_a -= (s16b) rand_range(5, 15); break; case 18: o_ptr->to_h -= (s16b) rand_range(5, 10); o_ptr->to_d -= (s16b) rand_range(5, 10); break; case 24: case 25: SET_FLAG(o_ptr, TR_TELEPORT); break; case 26: SET_FLAG(o_ptr, TR_DRAIN_EXP); break; case 27: case 28: SET_FLAG(o_ptr, TR_TY_CURSE); break; case 29: case 30: case 31: SET_FLAG(o_ptr, TR_CURSED); SET_FLAG(o_ptr, TR_HEAVY_CURSE); break; case 32: SET_FLAG(o_ptr, TR_NO_MAGIC); break; } } static void random_slay(object_type *o_ptr) { /* Bows get special treatment */ if (o_ptr->tval == TV_BOW) { switch (randint1(12)) { case 1: case 2: case 3: case 4: case 5: SET_FLAG(o_ptr, TR_XTRA_MIGHT); break; case 6: SET_FLAG(o_ptr, TR_WILD_SHOT); break; default: SET_FLAG(o_ptr, TR_XTRA_SHOTS); break; } } switch (randint1(36)) { case 1: case 2: SET_FLAG(o_ptr, TR_SLAY_ANIMAL); break; case 3: case 4: SET_FLAG(o_ptr, TR_SLAY_EVIL); break; case 5: case 6: SET_FLAG(o_ptr, TR_SLAY_UNDEAD); break; case 7: case 8: SET_FLAG(o_ptr, TR_SLAY_DEMON); break; case 9: case 10: SET_FLAG(o_ptr, TR_SLAY_ORC); break; case 11: case 12: SET_FLAG(o_ptr, TR_SLAY_TROLL); break; case 13: case 14: SET_FLAG(o_ptr, TR_SLAY_GIANT); break; case 15: case 16: SET_FLAG(o_ptr, TR_SLAY_DRAGON); break; case 17: SET_FLAG(o_ptr, TR_KILL_DRAGON); break; case 18: case 19: if (o_ptr->tval == TV_SWORD) { SET_FLAG(o_ptr, TR_VORPAL); } else { SET_FLAG(o_ptr, TR_IMPACT); } break; case 20: case 21: case 22: SET_FLAG(o_ptr, TR_BRAND_FIRE); break; case 23: case 24: SET_FLAG(o_ptr, TR_BRAND_COLD); break; case 25: case 26: SET_FLAG(o_ptr, TR_BRAND_ELEC); break; case 27: case 28: SET_FLAG(o_ptr, TR_BRAND_ACID); break; case 29: case 30: SET_FLAG(o_ptr, TR_BRAND_POIS); break; case 31: case 32: SET_FLAG(o_ptr, TR_VAMPIRIC); break; case 33: case 34: SET_FLAG(o_ptr, TR_PSI_CRIT); break; default: SET_FLAG(o_ptr, TR_CHAOTIC); break; } } static cptr activation_text[] = { "The %v glows extremely brightly...", "The %v throbs deep green...", "The %v glows an intense red...", "The %v glows black...", "The %v glows an intense blue...", "The %v throbs red...", "The %v glows deep red...", "The %v glows bright white...", "The %v glows deep blue...", "The %v glows in scintillating colours...", "The %v vibrates...", "The %v glows violet...", "The %v lets out a long, shrill note...", "The %v twists in your hands...", "The %v shudders...", "The %v fades in and out...", "The %v hums softly...", "The %v blinks in and out...", "The %v radiates light blue...", "The %v radiates deep purple...", "The %v glows deep green...", "The %v lets out a shrill wail...", "The %v glows brightly...", "The %v shines brightly...", "The %v glows yellow...", "The %v glows light blue...", "The %v glows brown...", "The %v pulsates...", "The %v hums...", "The %v glows bright yellow..." }; static cptr element_list[] = { "GF_ACID", "GF_ELEC", "GF_FIRE", "GF_COLD", "GF_POIS", "GF_PLASMA", "GF_WATER", "GF_LITE", "GF_DARK", "GF_SHARDS", "GF_SOUND", "GF_CONFUSION", "GF_FORCE", "GF_INERTIA", "GF_MANA", "GF_ICE", "GF_CHAOS", "GF_NETHER", "GF_NEXUS", "GF_TIME", "GF_GRAVITY", "GF_NUKE", "GF_HOLY_FIRE", "GF_HELL_FIRE", "GF_MISSILE", }; static cptr element_names[] = { "acid", "lightning", "fire", "cold", "poison", "plasma", "water", "light", "darkness", "shards", "sound", "confusion", "force", "inertia", "mana", "ice", "chaos", "nether", "nexus", "time", "gravity", "toxic waste", "holy power", "unholy power", "elemental force", }; static cptr element_colors[] = { "dark grey", "light blue", "bright red", "pale white", "dark green", "bright orange", "dark blue", "bright white", "black", "dark red", "amber", "dark purple", "light grey", "silver", "many colors", "pale white", "many colors", "black", "yellow", "silver", "dark grey", "sickly yellow", "pure white", "dark red", "many colors", }; static cptr glow_desc[] = { "glows", "shines", "pulses", "throbs", "radiates", "sparks" }; /* * An activation for random artifacts */ typedef struct randart_activation randart_activation; struct randart_activation { cptr text; cptr desc; cptr effect; int freq; bool aimed; int pp; int dice; int bonus; }; #define SPDICE_MM -1 /* * { * text, * desc, * effect, * freq, aimed, pp, dice, bonus * } */ static const struct randart_activation randart_activations[] = { { NULL, "remove fear and cure poison", "clear_afraid(); clear_poisoned()", 20, FALSE, 100, 0, 0 }, { NULL, "stone to mud", "wall_to_mud(dir)", 100, TRUE, 1000, 0, 0 }, { NULL, "destroy doors", "destroy_doors_touch()", 20, FALSE, 1000, 0, 0 }, { NULL, "confuse monster", "confuse_monster(dir, 50)", 50, TRUE, 1000, 0, 0 }, { NULL, "sleep monster", "sleep_monster(dir)", 50, TRUE, 1000, 0, 0 }, { NULL, "slow monster", "slow_monster(dir)", 50, TRUE, 1000, 0, 0 }, { NULL, "identify", "if not ident_spell() then return end", 100, FALSE, 1000, 0, 0 }, { NULL, "recharging", "recharge(130)", 25, FALSE, 1000, 0, 0 }, { NULL, "sleep nearby monsters", "sleep_monsters_touch()", 25, FALSE, 1000, 0, 0 }, { NULL, "sleep monsters", "sleep_monsters()", 10, FALSE, 5000, 0, 0 }, { "You open a dimensional gate. Choose a destination.", "dimension door", "if not dimension_door() return end", 25, FALSE, 1000, 0, 0 }, { "The %v twists space around you...", "teleport (100)", "teleport_player(100)", 100, FALSE, 1000, 0, 0 }, { "An image forms in your mind...", "detection", "detect_all()", 50, FALSE, 1000, 0, 0 }, { "The %v glows bright red...", "explosive rune", "explosive_rune()", 5, FALSE, 5000, 0, 0 }, { NULL, "word of recall", "word_of_recall()", 100, FALSE, 5000, 0, 0 }, { NULL, "restore life levels", "restore_level()", 10, FALSE, 25000, 0, 0 }, { NULL, "teleport away", "fire_beam(GF_AWAY_ALL, dir, player.lev)", 50, TRUE, 5000, 0, 0 }, { NULL, "charm animal", "charm_animal(dir, player.lev)", 10, TRUE, 5000, 0, 0 }, { NULL, "charm animal (level %s)", "charm_animal(dir, %s)", 10, TRUE, 200, 100, 0 /* lev */ }, { NULL, "enslave undead", "control_one_undead(dir, player.lev)", 10, TRUE, 7500, 0, 0 }, { NULL, "enslave undead (level %s)", "control_one_undead(dir, %s)", 10, TRUE, 300, 100, 0 /* lev */ }, { NULL, "charm monster", "charm_monster(dir, player.lev)", 10, TRUE, 10000, 0, 0 }, { NULL, "charm monster (level %s)", "charm_monster(dir, %s)", 10, TRUE, 400, 100, 0 /* lev */ }, { NULL, "alchemy", "alchemy()", 5, FALSE, 10000, 0, 0 }, { NULL, "rune of protection", "warding_glyph()", 10, FALSE, 10000, 0, 0 }, { NULL, "animal friendship", "charm_animals(player.lev * 2)", 5, FALSE, 10000, 0, 0 }, { NULL, "animal friendship (level %s)", "charm_animals(%s * 2)", 5, FALSE, 750, 100, 0 /* lev */ }, { "The power of the %v banishes evil!", "banish evil", "banish_evil(200)", 25, FALSE, 10000, 0, 0 }, { NULL, "genocide", "genocide(TRUE)", 10, FALSE, 10000, 0, 0 }, { NULL, "satisfy hunger", "set_food(PY_FOOD_MAX - 1)", 100, FALSE, 10000, 0, 0 }, { "The %v emits a blast of air...", "whirlwind attack", "whirlwind_attack()", 10, FALSE, 20000, 0, 0 }, { "The %v glows in scintillating colours...", "call chaos", "call_chaos()", 10, FALSE, 25000, 0, 0 }, { NULL, "mass genocide", "mass_genocide(TRUE)", 5, FALSE, 25000, 0, 0 }, { NULL, "mass charm", "charm_monsters(player.lev * 2)", 5, FALSE, 25000, 0, 0 }, { NULL, "mass charm (level %s)", "charm_monsters(%s * 2)", 5, FALSE, 2000, 100, 0 /* lev */ }, { NULL, "restore stats", "restore_all_stats()", 10, FALSE, 25000, 0, 0 }, { NULL, "restore stats and life levels", "restore_all_stats(); restore_level()", 5, FALSE, 50000, 0, 0 }, { NULL, "identify true", "identify_fully()", 25, FALSE, 25000, 0, 0 }, { NULL, "detection, probing and identify true", "detect_all(); probing(); identify_fully()", 5, FALSE, 50000, 0, 0 }, { "A line of sunlight appears.", "beam of sunlight (%sd8)", "lite_line(dir, damroll(%s, 8))", 100, TRUE, 100, 10, 4 /* lev / 10 + 4 */ }, { "The %v glow extremely brightly...", "magic missile (%sd6)", "fire_bolt(GF_MISSILE, dir, damroll(%s, 6))", 100, TRUE, 12, 10, 2 /* lev / 10 + 2 */ }, { "The %v throbs deep green...", "stinking cloud (%s)", "fire_ball(GF_POIS, dir, %s, 2)", 50, TRUE, 5, 100, 10 /* lev + 10 */ }, { "The %v glows black...", "drain life (%s)", "drain_life(dir, %s)", 25, TRUE, 10, 500, 0 /* lev * 5 */ }, { "The %v throbs red...", "vampiric drain (%s)", "drain_gain_life(dir, %s)", 25, TRUE, 15, 500, 0 /* lev * 5 */ }, { "The %v grows magical spikes...", "arrows (%s)", "fire_bolt(GF_ARROW, dir, %s)", 10, TRUE, 10, 500, 0 /* lev * 5 */ }, { "The %v grows magical spikes...", "arrows (%sd25)", "fire_bolt(GF_ARROW, dir, damroll(%s, 25))", 5, TRUE, 125, 33, 1 /* lev / 3 + 1 */ }, { "The %v grows magical spikes...", "arrows (%sd50)", "fire_bolt(GF_ARROW, dir, damroll(%s, 50))", 5, TRUE, 250, 33, 1 /* lev / 3 + 1 */ }, { "You launch a rocket!", "launch rocket (%s)", "fire_ball(GF_ROCKET, dir, %s, 2)", 5, TRUE, 20, 500, 100 /* rlev * 5 + 100 */ }, { "The %v floods the area with goodness...", "dispel evil (%s)", "dispel_evil(%s)", 50, FALSE, 100, 500, 0 /* rlev * 5 */ }, { "The %v floods the area with evil...", "dispel good (%s)", "dispel_good(%s)", 25, FALSE, 100, 500, 0 /* rlev * 5 */ }, { /* Note that this is more powerful than a normal breath activation */ "You breathe the elements.", "breathe the elements (%s, rad. 4)", "fire_ball(GF_MISSILE, dir, %s, 4)", 5, TRUE, 40, 1000, 0 /* rlev * 10 */ }, { "The %v vibrates...", "earthquake (rad. %s)", "earthquake(px, py, %s)", 10, FALSE, 250, 25, 5 /* lev / 4 + 5 */ }, { "The %v emits a loud blast...", "terror", "turn_monsters(40 + player.lev)", 10, FALSE, 2500, 0, 0 }, { "You summon a beast.", "summon animal", "summon_controlled(SUMMON_ANIMAL_RANGER)", 25, FALSE, 10000, 0, 0 }, { "You summon a phantasmal servant.", "summon phantasmal servant", "summon_controlled(SUMMON_PHANTOM)", 25, FALSE, 10000, 0, 0 }, { "You summon an elemental.", "summon elemental", "summon_unsafe(SUMMON_ELEMENTAL)", 25, FALSE, 25000, 0, 0 }, { "Ancient, long-dead forms rise from the ground.", "summon undead", "summon_unsafe(SUMMON_UNDEAD)", 25, FALSE, 25000, 0, 0 }, { "Ancient, long-dead forms rise from the ground.", "summon greater undead", "summon_unsafe(SUMMON_HI_UNDEAD)", 10, FALSE, 50000, 0, 0 }, { "The area fills with the stench of sulphur and brimstone.", "summon demon", "summon_unsafe(SUMMON_DEMON)", 25, FALSE, 25000, 0, 0 }, { "The area fills with brilliant white light.", "summon angel", "summon_controlled(SUMMON_ANGEL)", 10, FALSE, 50000, 0, 0 }, { NULL, "remove fear and heal (%sd10)", "clear_afraid(); hp_player(damroll(%s, 10))", 25, FALSE, 100, 33, 1 /* lev / 3 + 1 */ }, { NULL, "cure wounds and heal (%sd10)", "hp_player(damroll(%s, 10)); clear_cut(); clear_stun()", 100, FALSE, 100, 100, 0 /* lev */ }, { NULL, "cure wounds and heal (%s)", "hp_player(%s); clear_cut(); clear_stun()", 100, FALSE, 20, 1000, 0 /* lev * 10 */ }, { "The %v enters your thoughts...", "telepathy (%s+ turns)", "inc_tim_esp(rand_range2(%s))", 25, FALSE, 200, 100, 0 /* lev */ }, { NULL, "heroism (%s+ turns)", "inc_hero(rand_range2(%s))", 25, FALSE, 150, 100, 0 /* lev */ }, { NULL, "berserk (%s+ turns)", "inc_shero(rand_range2(%s))", 25, FALSE, 150, 100, 0 /* lev */ }, { NULL, "bless (%s+ turns)", "inc_blessed(rand_range2(%s))", 25, FALSE, 100, 100, 0 /* lev */ }, { NULL, "protection from evil (%s+ turns)", "inc_protevil(rand_range2(%s))", 25, FALSE, 100, 200, 0 /* lev * 2 */ }, { "The %v glows many colours...", "resist elements (%s+ turns)", "inc_oppose_all(rand_range2(%s))", 25, FALSE, 250, 100, 0 /* lev */ }, { NULL, "resist acid (%s+ turns)", "inc_oppose_acid(rand_range2(%s))", 10, FALSE, 100, 100, 0 /* lev */ }, { NULL, "resist lightning (%s+ turns)", "inc_oppose_elec(rand_range2(%s))", 10, FALSE, 100, 100, 0 /* lev */ }, { NULL, "resist fire (%s+ turns)", "inc_oppose_fire(rand_range2(%s))", 10, FALSE, 100, 100, 0 /* lev */ }, { NULL, "resist cold (%s+ turns)", "inc_oppose_cold(rand_range2(%s))", 10, FALSE, 100, 100, 0 /* lev */ }, { NULL, "resist poison (%s+ turns)", "inc_oppose_poison(rand_range2(%s))", 5, FALSE, 100, 100, 0 /* lev */ }, { NULL, "speed (%s+ turns)", "inc_fast(rand_range2(%s))", 25, FALSE, 200, 100, 0 /* lev */ }, { "The %v fades out...", "wraith form (%s+ turns)", "inc_wraith_form(rand_range2(%s))", 5, FALSE, 2000, 33, 1 /* lev / 3 + 1 */ }, { "The %v fires a beam of bright light at you...", "invulnerability (%s+ turns)", "inc_invuln(rand_range2(%s))", 5, FALSE, 2000, 10, 3 /* lev / 10 + 3 */ }, { "The %v wells with clear light...", "light area (2d20)", "lite_area(damroll(2, 20), 3)", 100, FALSE, 100, 0, 0 }, { "The %v shines brightly...", "magic mapping and light area (5d20)", "map_area(); lite_area(damroll(5, 20), 3)", 50, FALSE, 400, 0, 0 }, { NULL, "detect evil", "detect_monsters_evil()", 25, FALSE, 1000, 0, 0 }, { NULL, "detect monsters", "detect_monsters()", 50, FALSE, 1000, 0, 0 }, { NULL, "detect traps and doors", "detect_traps(TRUE); detect_doors(); detect_stairs()", 50, FALSE, 1000, 0, 0 }, { NULL, "remove curse", "remove_curse()", 10, FALSE, 25000, 0, 0 }, { NULL, "dispel curse", "remove_all_curse()", 5, FALSE, 50000, 0, 0 }, { NULL, "dispel undead (%s)", "dispel_undead(%s)", 25, FALSE, 50, 500, 0 /* rlev * 5 */ }, { NULL, "dispel demons (%s)", "dispel_demons(%s)", 25, FALSE, 50, 500, 0 /* rlev * 5 */ }, { NULL, "dispel living (%s)", "dispel_living (%s)", 5, FALSE, 200, 400, 0 /* lev * 4 */ }, { NULL, "dispel monsters (%s)", "dispel_monsters(%s)", 5, FALSE, 250, 400, 0 /* lev * 4 */ }, { NULL, "slow monsters", "slow_monsters()", 10, FALSE, 25000, 0, 0 }, { NULL, "detect objects", "detect_objects_normal()", 25, FALSE, 1000, 0, 0 }, { NULL, "detect treasure", "detect_treasure(); detect_objects_gold()", 10, FALSE, 1000, 0, 0 }, { NULL, "detect enchantment", "detect_objects_magic()", 10, FALSE, 2500, 0, 0 }, { NULL, "self knowledge", "self_knowledge()", 10, FALSE, 10000, 0, 0 }, { NULL, "teleport level", "teleport_player_level()", 5, FALSE, 10000, 0, 0 }, { NULL, "create doors", "door_creation()", 5, FALSE, 2500, 0, 0 }, { NULL, "create stairs", "stair_creation()", 5, FALSE, 10000, 0, 0 }, { NULL, "alter reality", "alter_reality()", 5, FALSE, 10000, 0, 0 }, { NULL, "polymorph self", "polymorph_self()", 10, FALSE, 5000, 0, 0 }, { NULL, "phase door", "teleport_player(10)", 100, FALSE, 100, 0, 0 }, { NULL, "banishment", "banish_monsters(200)", 5, FALSE, 25000, 0, 0 }, /* XXX stun, confuse, turn, stasis */ }; static void apply_activation_power(object_type *o_ptr, cptr text, cptr desc, cptr effect, bool aimed, int pp, int level) { char buf[1024]; char text_buf[256]; int len; int charge_min; /* Don't add a power if there already is one */ if (FLAG(o_ptr, TR_ACTIVATE)) return; /* Calculate charge time */ if (level > 0) charge_min = pp / level; else charge_min = pp; /* Round to nice numbers */ if (charge_min >= 1000) charge_min -= charge_min % 100; else if (charge_min >= 250) charge_min -= charge_min % 50; else if (charge_min >= 100) charge_min -= charge_min % 10; else if (charge_min >= 25) charge_min -= charge_min % 5; /* Enforce minimum & maximum charge time */ if (charge_min < 1) charge_min = 1; if (charge_min > 5000) charge_min = 5000; /* Get a description if needed */ if (text == NULL) text = activation_text[randint0(NUM_ELEMENTS(activation_text))]; /* Get the basic name of the object in the description */ strnfmt(text_buf, 256, text, OBJECT_FMT(o_ptr, FALSE, 0)); /* Construct the usage script */ len = strnfmt(buf, 1024, "msgf(\"%s\"); ", text_buf); if (aimed) strnfcat(buf, 1024, &len, "local success; local dir; " "success, dir = get_aim_dir(); " "if not success then return; end; "); strnfcat(buf, 1024, &len, "%s; object.timeout = rand_range(%i, %i)", effect, charge_min, charge_min * 2); o_ptr->trigger[TRIGGER_USE] = quark_add(buf); /* Description script */ len = strnfmt(buf, 1024, "return \"%s every %i-%i turns\"", desc, charge_min, charge_min * 2); o_ptr->trigger[TRIGGER_DESC] = quark_fmt(buf); SET_FLAG(o_ptr, TR_ACTIVATE); o_ptr->timeout = 0; } static void attack_activation_power(object_type *o_ptr, int level, cptr fix_element) { static char text[256]; char effect[256] = ""; char desc[256] = ""; int element; int dice = 0; int sides = 1; int radius = 0; int pp = 0; /* Charge time * level */ bool aimed = TRUE; int rlev = level * rand_range(50, 150) / 100; if (rlev < 1) rlev = 1; if (fix_element == NULL) { /* Pick a low or high element */ if (level <= randint1(60)) element = randint0(5); else element = randint0(NUM_ELEMENTS(element_list)); } else { for (element = 0; strcmp(fix_element, element_list[element]) != 0; element++) ; } /* Describe the visual */ strnfmt(text, 256, "The %%v %s %s...", glow_desc[randint0(NUM_ELEMENTS(glow_desc))], element_colors[element]); switch (randint1(10)) { /* Breathe */ case 1: strnfmt(text, 256, "You breathe %s.", element_names[element]); /* Dice, radius, and charge time */ dice = rlev * 5; radius = 2 + dice / rand_range(100, 200); pp = dice * 10 * radius; /* Create the lua */ strnfmt(desc, 256, "breathe %s (%i, rad. %i)", element_names[element], dice, radius); strnfmt(effect, 256, "fire_ball(%s, dir, %i, %i)", element_list[element], dice, radius); break; /* Emit a blast */ case 2: strnfmt(text, 256, "The %%v emits a blast of %s...", element_names[element]); /* Dice, radius, and charge time */ dice = rlev * 10; radius = 2 + dice / rand_range(50, 100); pp = dice * 2 * radius; aimed = FALSE; /* Create the lua */ strnfmt(desc, 256, "%s blast (%i, rad. %i)", element_names[element], dice, radius); strnfmt(effect, 256, "fire_ball(%s, 0, %i, %i)", element_list[element], dice, radius); break; /* Fire a ball */ case 3: case 4: case 5: /* Dice, radius, and charge time */ dice = 5 * (1 + rlev / 2); radius = 2 + dice / rand_range(100, 200); pp = dice * 10 * radius; /* Create the lua */ strnfmt(desc, 256, "%s%s ball (%i)", radius > 2 ? "large " : "", element_names[element], dice); strnfmt(effect, 256, "fire_ball(%s, dir, %i, %i)", element_list[element], dice, radius); break; /* Fire a beam */ case 6: /* Dice and charge time */ dice = 1 + rlev / 3; sides = rand_range(5, 8); pp = dice * sides * 10; /* Create the lua */ strnfmt(desc, 256, "%s beam (%id%i)", element_names[element], dice, sides); strnfmt(effect, 256, "fire_beam(%s, dir, damroll(%i, %i))", element_list[element], dice, sides); break; /* Fire a bolt */ default: /* Dice and charge time */ dice = 1 + rlev / 2; sides = rand_range(5, 8); pp = dice * sides * 5; /* Create the lua */ strnfmt(desc, 256, "%s bolt (%id%i)", element_names[element], dice, sides); strnfmt(effect, 256, "fire_bolt(%s, dir, damroll(%i, %i))", element_list[element], dice, sides); break; } apply_activation_power(o_ptr, text, desc, effect, aimed, pp, level); } static void misc_activation_power(object_type *o_ptr, int level, cptr fix_power) { const struct randart_activation *act = NULL; char dice[32]; char dice_desc[32]; char effect[256] = ""; char desc[256] = ""; int pp; int rlev = level * rand_range(50, 150) / 100; if (rlev < 1) rlev = 1; if (!fix_power) { do { act = &randart_activations[randint0(NUM_ELEMENTS(randart_activations))]; } while (act->freq < randint1(100)); } else { int i; for (i = 0; strcmp(randart_activations[i].desc, fix_power) != 0; i++) ; act = &randart_activations[i]; } /* Sometimes make the activation dependent on the player's level */ if (one_in_(10) && act->dice >= 100 && act->bonus == 0) { /* Deeper items may give better multipliers */ int mult = act->dice * rand_range(50, 100 + 2 * rlev) / 10000; if (mult < 1) mult = 1; if (mult > 1) { strnfmt(dice, 32, "player.lev * %i", mult); strnfmt(dice_desc, 32, "plev * %i", mult); } else { strcpy(dice, "player.lev"); strcpy(dice_desc, "plev"); } /* Fill in the dice */ strnfmt(desc, 256, act->desc, dice_desc); strnfmt(effect, 256, act->effect, dice); /* Assume plev 30 for power calculation */ pp = act->pp * mult * 30; } else { int d = rlev * act->dice / 100 + act->bonus; if (d < 1) d = 1; /* Fill in the dice */ strnfmt(dice, 32, "%i", d); strnfmt(desc, 256, act->desc, dice); strnfmt(effect, 256, act->effect, dice); pp = act->pp * d; } apply_activation_power(o_ptr, act->text, desc, effect, act->aimed, pp, level); } static void random_activation_power(object_type *o_ptr, int level) { int rlev = level * rand_range(50, 150) / 100; if (rlev < 1) rlev = 1; /* 60%/20% chance of a random attack */ if (randint0(100) < (o_ptr->tval < TV_BOOTS ? 60 : 20)) attack_activation_power(o_ptr, level, NULL); else misc_activation_power(o_ptr, level, NULL); } static void get_random_name(char *return_name, byte tval, int power) { if ((randint1(100) <= TABLE_NAME) || (tval == TV_AMULET) || (tval == TV_RING)) { get_table_name(return_name, TRUE); } else { cptr filename; /* Armour or a Weapon? */ if (tval >= TV_BOOTS) { switch (power) { case 0: filename = "a_cursed.txt"; break; case 1: filename = "a_low.txt"; break; case 2: filename = "a_med.txt"; break; default: filename = "a_high.txt"; } } else { switch (power) { case 0: filename = "w_cursed.txt"; break; case 1: filename = "w_low.txt"; break; case 2: filename = "w_med.txt"; break; default: filename = "w_high.txt"; } } (void)get_rnd_line(filename, 0, return_name); } } static int random_minor_theme_weapon(object_type *o_ptr, int level) { int activate = 0; switch (randint1(39)) { case 1: case 2: case 3: SET_FLAG(o_ptr, TR_WIS); SET_FLAG(o_ptr, TR_BLESSED); break; case 4: case 5: SET_FLAG(o_ptr, TR_BRAND_ACID); SET_FLAG(o_ptr, TR_RES_ACID); if (o_ptr->tval == TV_SWORD && one_in_(3)) SET_FLAG(o_ptr, TR_TUNNEL); if (one_in_(ACTIVATION_CHANCE)) attack_activation_power(o_ptr, level, "GF_ACID"); break; case 6: case 7: SET_FLAG(o_ptr, TR_BRAND_ELEC); SET_FLAG(o_ptr, TR_RES_ELEC); if (one_in_(ACTIVATION_CHANCE)) attack_activation_power(o_ptr, level, "GF_ELEC"); break; case 8: case 9: case 10: SET_FLAG(o_ptr, TR_BRAND_FIRE); SET_FLAG(o_ptr, TR_RES_FIRE); SET_FLAG(o_ptr, TR_LITE); if (one_in_(ACTIVATION_CHANCE)) attack_activation_power(o_ptr, level, "GF_FIRE"); break; case 11: case 12: case 13: SET_FLAG(o_ptr, TR_BRAND_COLD); SET_FLAG(o_ptr, TR_RES_COLD); if (one_in_(ACTIVATION_CHANCE)) attack_activation_power(o_ptr, level, "GF_COLD"); break; case 14: case 15: SET_FLAG(o_ptr, TR_BRAND_POIS); SET_FLAG(o_ptr, TR_RES_POIS); if (one_in_(ACTIVATION_CHANCE)) attack_activation_power(o_ptr, level, "GF_POIS"); break; case 16: case 17: SET_FLAG(o_ptr, TR_CHAOTIC); SET_FLAG(o_ptr, TR_RES_CHAOS); if (one_in_(3)) SET_FLAG(o_ptr, TR_PATRON); if (one_in_(ACTIVATION_CHANCE)) attack_activation_power(o_ptr, level, "GF_CHAOS"); break; case 18: if (o_ptr->tval == TV_SWORD) { SET_FLAG(o_ptr, TR_VORPAL); SET_FLAG(o_ptr, TR_TUNNEL); } else { SET_FLAG(o_ptr, TR_BLOWS); } break; case 19: case 20: SET_FLAG(o_ptr, TR_SLAY_ANIMAL); if (one_in_(2)) SET_FLAG(o_ptr, TR_INT); if (one_in_(2)) SET_FLAG(o_ptr, TR_REGEN); break; case 21: case 22: case 23: SET_FLAG(o_ptr, TR_SLAY_EVIL); SET_FLAG(o_ptr, TR_BLESSED); if (one_in_(2)) SET_FLAG(o_ptr, TR_WIS); if (one_in_(2)) SET_FLAG(o_ptr, TR_RES_FEAR); break; case 24: case 25: SET_FLAG(o_ptr, TR_SLAY_UNDEAD); if (one_in_(2)) SET_FLAG(o_ptr, TR_INT); if (one_in_(2)) SET_FLAG(o_ptr, TR_HOLD_LIFE); if (one_in_(2)) SET_FLAG(o_ptr, TR_SEE_INVIS); break; case 26: case 27: SET_FLAG(o_ptr, TR_SLAY_DEMON); SET_FLAG(o_ptr, TR_INT); break; case 28: case 29: SET_FLAG(o_ptr, TR_SLAY_ORC); SET_FLAG(o_ptr, TR_DEX); break; case 30: case 31: SET_FLAG(o_ptr, TR_SLAY_GIANT); SET_FLAG(o_ptr, TR_STR); break; case 32: case 33: SET_FLAG(o_ptr, TR_SLAY_DRAGON); if (one_in_(3)) SET_FLAG(o_ptr, TR_KILL_DRAGON); SET_FLAG(o_ptr, TR_CON); break; case 34: case 35: SET_FLAG(o_ptr, TR_VAMPIRIC); SET_FLAG(o_ptr, TR_HOLD_LIFE); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "vampiric drain (%s)"); break; case 36: SET_FLAG(o_ptr, TR_HOLD_LIFE); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "drain life (%s)"); break; case 37: o_ptr->to_h += (s16b) rand_range(5, 15); o_ptr->to_d += (s16b) rand_range(5, 15); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "whirlwind attack"); break; case 38: SET_FLAG(o_ptr, TR_SLAY_ANIMAL); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "charm animal"); break; case 39: SET_FLAG(o_ptr, TR_SLAY_UNDEAD); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "enslave undead"); break; case 40: if (o_ptr->tval == TV_SWORD) SET_FLAG(o_ptr, TR_TUNNEL); #if 0 activate = ACT_STONE_MUD; #endif break; } return activate; } static int random_major_theme_weapon(object_type *o_ptr, int level) { int activate = 0; switch (randint1(7)) { case 1: /* Holy Avenger */ SET_FLAG(o_ptr, TR_SLAY_EVIL); SET_FLAG(o_ptr, TR_SLAY_UNDEAD); SET_FLAG(o_ptr, TR_SLAY_DEMON); SET_FLAG(o_ptr, TR_SEE_INVIS); SET_FLAG(o_ptr, TR_BLESSED); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "dispel evil (%s)"); break; case 2: /* Defender */ SET_FLAG(o_ptr, TR_RES_ACID); SET_FLAG(o_ptr, TR_RES_ELEC); SET_FLAG(o_ptr, TR_RES_FIRE); SET_FLAG(o_ptr, TR_RES_COLD); if (one_in_(2)) SET_FLAG(o_ptr, TR_FREE_ACT); if (one_in_(2)) SET_FLAG(o_ptr, TR_SEE_INVIS); if (one_in_(2)) SET_FLAG(o_ptr, TR_FEATHER); if (one_in_(2)) SET_FLAG(o_ptr, TR_REGEN); if (one_in_(2)) o_ptr->to_a += randint1(5); #if 0 if (one_in_(8)) activate = ACT_RESIST_ALL; #endif break; case 3: /* Westernesse */ SET_FLAG(o_ptr, TR_STR); SET_FLAG(o_ptr, TR_DEX); SET_FLAG(o_ptr, TR_CON); SET_FLAG(o_ptr, TR_SLAY_ORC); SET_FLAG(o_ptr, TR_SLAY_TROLL); SET_FLAG(o_ptr, TR_SLAY_GIANT); break; case 4: /* Trump Weapon */ SET_FLAG(o_ptr, TR_SLAY_EVIL); SET_FLAG(o_ptr, TR_TELEPORT); SET_FLAG(o_ptr, TR_FREE_ACT); if (one_in_(2)) SET_FLAG(o_ptr, TR_SEARCH); if (one_in_(2)) SET_FLAG(o_ptr, TR_REGEN); if (one_in_(2)) SET_FLAG(o_ptr, TR_SLOW_DIGEST); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "teleport (100)"); break; case 5: /* Pattern Weapon */ SET_FLAG(o_ptr, TR_STR); SET_FLAG(o_ptr, TR_CON); SET_FLAG(o_ptr, TR_FREE_ACT); SET_FLAG(o_ptr, TR_SEE_INVIS); if (one_in_(2)) SET_FLAG(o_ptr, TR_SLAY_EVIL); if (one_in_(2)) SET_FLAG(o_ptr, TR_SLAY_DEMON); if (one_in_(2)) SET_FLAG(o_ptr, TR_SLAY_UNDEAD); break; case 6: /* Mixed slays */ if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_ANIMAL); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_EVIL); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_UNDEAD); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_DEMON); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_ORC); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_TROLL); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_GIANT); if (one_in_(3)) SET_FLAG(o_ptr, TR_SLAY_DRAGON); break; case 7: /* Assassin blade */ SET_FLAG(o_ptr, TR_STEALTH); SET_FLAG(o_ptr, TR_BLOWS); SET_FLAG(o_ptr, TR_FREE_ACT); if (one_in_(2)) SET_FLAG(o_ptr, TR_BRAND_POIS); else SET_FLAG(o_ptr, TR_VAMPIRIC); if (o_ptr->tval == TV_SWORD) SET_FLAG(o_ptr, TR_THROW); break; } return activate; } static int random_minor_theme_armor(object_type *o_ptr, int level) { int activate = 0; switch (randint1(33)) { case 1: case 2: case 3: SET_FLAG(o_ptr, TR_SEE_INVIS); SET_FLAG(o_ptr, TR_SEARCH); break; case 4: case 5: SET_FLAG(o_ptr, TR_STR); SET_FLAG(o_ptr, TR_SUST_STR); if (one_in_(3)) SET_FLAG(o_ptr, TR_RES_FEAR); break; case 6: case 7: SET_FLAG(o_ptr, TR_INT); SET_FLAG(o_ptr, TR_SUST_INT); if (one_in_(3)) SET_FLAG(o_ptr, TR_FEATHER); break; case 8: case 9: SET_FLAG(o_ptr, TR_WIS); SET_FLAG(o_ptr, TR_SUST_WIS); if (one_in_(3)) SET_FLAG(o_ptr, TR_SEE_INVIS); break; case 10: case 11: SET_FLAG(o_ptr, TR_DEX); SET_FLAG(o_ptr, TR_SUST_DEX); if (one_in_(3)) SET_FLAG(o_ptr, TR_FREE_ACT); break; case 12: case 13: SET_FLAG(o_ptr, TR_CON); SET_FLAG(o_ptr, TR_SUST_CON); if (one_in_(3)) SET_FLAG(o_ptr, TR_REGEN); break; case 14: case 15: SET_FLAG(o_ptr, TR_CHR); SET_FLAG(o_ptr, TR_SUST_CHR); if (one_in_(3)) SET_FLAG(o_ptr, TR_LITE); break; case 16: SET_FLAG(o_ptr, TR_LITE); SET_FLAG(o_ptr, TR_RES_LITE); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "light area (2d20)"); break; case 17: case 18: SET_FLAG(o_ptr, TR_RES_FIRE); SET_FLAG(o_ptr, TR_SH_FIRE); break; case 19: SET_FLAG(o_ptr, TR_RES_ELEC); SET_FLAG(o_ptr, TR_SH_ELEC); break; case 20: SET_FLAG(o_ptr, TR_RES_COLD); SET_FLAG(o_ptr, TR_SH_COLD); break; case 21: SET_FLAG(o_ptr, TR_INT); SET_FLAG(o_ptr, TR_WIS); break; case 22: SET_FLAG(o_ptr, TR_RES_LITE); SET_FLAG(o_ptr, TR_RES_DARK); if (one_in_(2)) SET_FLAG(o_ptr, TR_LITE); break; case 23: SET_FLAG(o_ptr, TR_SLAY_EVIL); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "banish evil"); else if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "dispel evil (%s)"); break; case 24: SET_FLAG(o_ptr, TR_STEALTH); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "sleep nearby monsters"); break; case 25: SET_FLAG(o_ptr, TR_WIS); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "telepathy (%s+ turns)"); break; case 26: SET_FLAG(o_ptr, TR_RES_DARK); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "beam of sunlight (%sd8)"); else if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "light area (2d20)"); break; case 27: SET_FLAG(o_ptr, TR_RES_CHAOS); SET_FLAG(o_ptr, TR_RES_CONF); break; case 28: SET_FLAG(o_ptr, TR_RES_NETHER); SET_FLAG(o_ptr, TR_HOLD_LIFE); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "restore life levels"); break; case 29: SET_FLAG(o_ptr, TR_RES_SOUND); SET_FLAG(o_ptr, TR_RES_SHARDS); break; case 30: SET_FLAG(o_ptr, TR_RES_FEAR); if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "terror"); else if (one_in_(ACTIVATION_CHANCE)) misc_activation_power(o_ptr, level, "heroism (%s+ turns)"); break; case 31: SET_FLAG(o_ptr, TR_SLAY_ANIMAL); #if 0 if (one_in_(3)) activate = ACT_CHARM_ANIMAL; else if (one_in_(2)) activate = ACT_CHARM_ANIMALS; else activate = ACT_SUMMON_ANIMAL; #endif break; case 32: SET_FLAG(o_ptr, TR_SLAY_UNDEAD); #if 0 if (one_in_(2)) activate = ACT_CHARM_UNDEAD; else activate = ACT_SUMMON_UNDEAD; #endif break; case 33: SET_FLAG(o_ptr, TR_SLAY_DEMON); #if 0 activate = ACT_SUMMON_DEMON; #endif break; } return activate; } static int random_major_theme_armor(object_type *o_ptr, int level) { int activate = 0; int i; /* Hack - ignore unused parameter */ (void) level; switch (randint1(10)) { case 1: SET_FLAG(o_ptr, TR_RES_ACID); SET_FLAG(o_ptr, TR_RES_ELEC); SET_FLAG(o_ptr, TR_RES_FIRE); SET_FLAG(o_ptr, TR_RES_COLD); if (one_in_(3)) SET_FLAG(o_ptr, TR_RES_POIS); break; case 2: SET_FLAG(o_ptr, TR_SUST_STR); SET_FLAG(o_ptr, TR_SUST_INT); SET_FLAG(o_ptr, TR_SUST_WIS); SET_FLAG(o_ptr, TR_SUST_DEX); SET_FLAG(o_ptr, TR_SUST_CON); SET_FLAG(o_ptr, TR_SUST_CHR); break; case 3: /* Might */ SET_FLAG(o_ptr, TR_STR); SET_FLAG(o_ptr, TR_SUST_STR); SET_FLAG(o_ptr, TR_DEX); SET_FLAG(o_ptr, TR_SUST_DEX); SET_FLAG(o_ptr, TR_CON); SET_FLAG(o_ptr, TR_SUST_CON); break; case 4: /* Mental */ SET_FLAG(o_ptr, TR_INT); SET_FLAG(o_ptr, TR_SUST_INT); SET_FLAG(o_ptr, TR_WIS); SET_FLAG(o_ptr, TR_SUST_WIS); if (one_in_(3)) SET_FLAG(o_ptr, TR_SP); break; case 5: /* Lohengrin */ SET_FLAG(o_ptr, TR_STEALTH); SET_FLAG(o_ptr, TR_INT); SET_FLAG(o_ptr, TR_WIS); SET_FLAG(o_ptr, TR_SEE_INVIS); break; case 6: case 7: case 8: /* Several high resists */ for (i = randint1(3) + 1; i > 0; --i) random_resistance(o_ptr, rand_range(17, 38)); break; case 9: /* Mixed stat boosts */ for (i = 3; i > 0; --i) { switch (randint1(6)) { case 1: SET_FLAG(o_ptr, TR_STR); break; case 2: SET_FLAG(o_ptr, TR_INT); break; case 3: SET_FLAG(o_ptr, TR_WIS); break; case 4: SET_FLAG(o_ptr, TR_DEX); break; case 5: SET_FLAG(o_ptr, TR_CON); break; case 6: SET_FLAG(o_ptr, TR_CHR); break; } } break; case 10: /* Thranduil */ SET_FLAG(o_ptr, TR_INT); SET_FLAG(o_ptr, TR_WIS); SET_FLAG(o_ptr, TR_RES_BLIND); if (o_ptr->tval == TV_HELM || o_ptr->tval == TV_CROWN) SET_FLAG(o_ptr, TR_TELEPATHY); break; } return activate; } static void curse_artifact(object_type *o_ptr) { int i; if (o_ptr->pval > 0) o_ptr->pval = 0 - (o_ptr->pval + randint1(4)); if (o_ptr->to_a > 0) o_ptr->to_a = 0 - (o_ptr->to_a + randint1(4)); if (o_ptr->to_h > 0) o_ptr->to_h = 0 - (o_ptr->to_h + randint1(4)); if (o_ptr->to_d > 0) o_ptr->to_d = 0 - (o_ptr->to_d + randint1(4)); SET_FLAG(o_ptr, TR_CURSED); for (i = rand_range(2, 6); i > 0; --i) random_curse(o_ptr, TRUE); } bool create_artifact(object_type *o_ptr, int level, bool a_scroll) { char new_name[1024]; int powers = rand_range(2, 6); int power_level; s32b total_flags, target_flags; bool a_cursed = FALSE; int i; int given = 0; /* No activation yet */ o_ptr->a_idx = 0; new_name[0] = 0; if (!a_scroll && one_in_(A_CURSED)) a_cursed = TRUE; while (one_in_(powers + 1)) powers++; #if 0 if (!a_cursed && one_in_(12)) powers *= 2; #endif if (a_cursed) powers /= 2; target_flags = 0; for (i = 0; i < powers; i++) target_flags += rand_range(10, 50) * (level + 5); /* Sometimes select a major theme - or two */ while (o_ptr->tval < TV_LITE && randint1(powers) > 3) { int act; if (o_ptr->tval == TV_BOW) { /* Hack - bows don't have major themes yet */ random_slay(o_ptr); } else if (o_ptr->tval < TV_BOOTS) { act = random_major_theme_weapon(o_ptr, level); o_ptr->to_h += (s16b) rand_range(5, 15); o_ptr->to_d += (s16b) rand_range(5, 15); } else { act = random_major_theme_armor(o_ptr, level); o_ptr->to_a += (s16b) rand_range(5, 15); } powers -= 3; } /* Possibly apply a power typical for the item's slot */ if (one_in_(2)) { switch (o_ptr->tval) { case TV_BOOTS: if (one_in_(12)) SET_FLAG(o_ptr, TR_SPEED); else if (one_in_(2)) SET_FLAG(o_ptr, TR_FREE_ACT); else SET_FLAG(o_ptr, TR_FEATHER); given++; break; case TV_GLOVES: switch (randint1(3)) { case 1: SET_FLAG(o_ptr, TR_FREE_ACT); break; case 2: SET_FLAG(o_ptr, TR_DEX); break; case 3: SET_FLAG(o_ptr, TR_STR); break; } given++; break; case TV_HELM: case TV_CROWN: switch (randint1(5)) { case 1: SET_FLAG(o_ptr, TR_TELEPATHY); break; case 2: SET_FLAG(o_ptr, TR_SEE_INVIS); break; case 3: SET_FLAG(o_ptr, TR_INFRA); break; case 4: SET_FLAG(o_ptr, TR_INT); break; case 5: SET_FLAG(o_ptr, TR_WIS); break; } given++; break; case TV_CLOAK: if (one_in_(2)) SET_FLAG(o_ptr, TR_LUCK_10); else SET_FLAG(o_ptr, TR_STEALTH); given++; break; case TV_SOFT_ARMOR: switch (randint1(3)) { case 1: SET_FLAG(o_ptr, TR_DEX); break; case 2: SET_FLAG(o_ptr, TR_CON); break; case 3: SET_FLAG(o_ptr, TR_STEALTH); break; } given++; break; case TV_HARD_ARMOR: switch (randint1(3)) { case 1: SET_FLAG(o_ptr, TR_HOLD_LIFE); break; case 2: SET_FLAG(o_ptr, TR_CON); break; case 3: SET_FLAG(o_ptr, TR_REGEN); break; } given++; break; case TV_SHIELD: switch (randint1(3)) { case 1: SET_FLAG(o_ptr, TR_REFLECT); break; case 2: SET_FLAG(o_ptr, TR_LITE); break; case 3: SET_FLAG(o_ptr, TR_FREE_ACT); break; } given++; break; } } /* Lights already have permanent light */ if (o_ptr->tval == TV_LITE) given++; total_flags = flag_cost(o_ptr, 1); /* Main loop */ while (total_flags < target_flags || given < 2) { int act = 0; switch (randint1(o_ptr->tval < TV_BOOTS ? 11 : 7)) { case 1: case 2: random_plus(o_ptr); break; case 3: case 4: random_resistance(o_ptr, 0); break; case 5: random_misc(o_ptr); break; case 6: case 7: act = random_minor_theme_armor(o_ptr, level); break; case 8: case 9: random_slay(o_ptr); break; case 10: case 11: act = random_minor_theme_weapon(o_ptr, level); break; } given++; total_flags = flag_cost(o_ptr, 1); } if (FLAG(o_ptr, TR_PVAL_MASK)) { if (FLAG(o_ptr, TR_BLOWS)) { if (one_in_(100)) { o_ptr->pval = 2; } else { o_ptr->pval = 1; } } else { i = randint1(100); if (i <= 35) o_ptr->pval = 1; else if (i <= 65) o_ptr->pval = 2; else if (i <= 85) o_ptr->pval = 3; else if (i <= 99) o_ptr->pval = 4; else o_ptr->pval = 5; } } else o_ptr->pval = 0; /* give it some plusses... */ if (o_ptr->tval >= TV_BOOTS && o_ptr->tval < TV_LITE) o_ptr->to_a += randint1(o_ptr->to_a > 19 ? 1 : 20 - o_ptr->to_a); else if (o_ptr->tval < TV_BOOTS) { o_ptr->to_h += randint1(o_ptr->to_h > 19 ? 1 : 20 - o_ptr->to_h); o_ptr->to_d += randint1(o_ptr->to_d > 19 ? 1 : 20 - o_ptr->to_d); } /* Just to be sure */ o_ptr->flags[2] |= (TR2_IGNORE_ACID | TR2_IGNORE_ELEC | TR2_IGNORE_FIRE | TR2_IGNORE_COLD); /* Possibly add some curses ... */ total_flags = flag_cost(o_ptr, o_ptr->pval); if (one_in_(13)) { random_curse(o_ptr, FALSE); total_flags = flag_cost(o_ptr, o_ptr->pval); } /* Penalize too-good artifacts */ if (!a_scroll) { if (total_flags >= target_flags * 2 && total_flags >= 5000 && one_in_(2)) { random_curse(o_ptr, FALSE); total_flags = flag_cost(o_ptr, o_ptr->pval); } if (total_flags >= target_flags * 3 && total_flags >= 10000 && !one_in_(12)) { random_curse(o_ptr, TRUE); total_flags = flag_cost(o_ptr, o_ptr->pval); } } if (cheat_peek) msgf("%ld", total_flags); if (a_cursed) curse_artifact(o_ptr); /* Check if it has an activation */ if (!a_cursed && one_in_((o_ptr->tval >= TV_BOOTS) ? ACTIVATION_CHANCE * 2 : ACTIVATION_CHANCE)) { int activation_level = level + randint0(10); if (one_in_(5)) activation_level = activation_level * 3 / 2; if (activation_level > 100) activation_level = 100; /* * Get a random activation */ random_activation_power(o_ptr, activation_level); } if (o_ptr->dd && o_ptr->ds) { if (one_in_(10L * o_ptr->dd * o_ptr->ds)) { o_ptr->ds += (o_ptr->ds * randint1(5)) / 5; } } if (o_ptr->tval >= TV_BOOTS) { if (a_cursed) power_level = 0; else if (total_flags < 10000) power_level = 1; else if (total_flags < 20000) power_level = 2; else power_level = 3; } else { if (a_cursed) power_level = 0; else if (total_flags < 15000) power_level = 1; else if (total_flags < 30000) power_level = 2; else power_level = 3; } /* If this non-weapon has the show_mod flag */ if (o_ptr->tval >= TV_BOOTS && o_ptr->tval <= TV_RING && FLAG(o_ptr, TR_SHOW_MODS)) { /* Check if it is not accidentally zero */ if (!o_ptr->to_h && !o_ptr->to_d) { /* Turn of that flag, no silly (+0, +0%) display */ o_ptr->flags[2] &= ~(TR2_SHOW_MODS); } } if (a_scroll) { char dummy_name[80]; dummy_name[0] = 0; /* Identify it fully */ object_aware(o_ptr); object_known(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; identify_fully_aux(o_ptr); if (!(get_string(dummy_name, 80, "What do you want to call the artifact? "))) { get_random_name(new_name, o_ptr->tval, power_level); } else { strnfmt(new_name, 1024, "'%s'", dummy_name); } } else { get_random_name(new_name, o_ptr->tval, power_level); } chg_virtue(V_INDIVIDUALISM, 2); chg_virtue(V_ENCHANT, 5); /* Save the inscription */ o_ptr->xtra_name = quark_add(new_name); /* Make the object an artifact */ SET_FLAG(o_ptr, TR_INSTA_ART); /* Set the cost */ o_ptr->cost = k_info[o_ptr->k_idx].cost + flag_cost(o_ptr, o_ptr->pval); /* Notice changes */ notice_item(); return TRUE; } /* * Create the artifact of the specified number */ void create_named_art(int a_idx, int x, int y) { object_type *q_ptr; int i; artifact_type *a_ptr = &a_info[a_idx]; /* Ignore "empty" artifacts */ if (!a_ptr->name) return; /* Acquire the "kind" index */ i = lookup_kind(a_ptr->tval, a_ptr->sval); /* Oops */ if (!i) return; /* Create the artifact */ q_ptr = object_prep(i); /* Save the artifact number */ q_ptr->a_idx = a_idx; /* Add any special scripts */ for (i = 0; i < MAX_TRIGGER; i++) { if (a_ptr->trigger[i]) q_ptr->trigger[i] = quark_add(a_text + a_ptr->trigger[i]); } /* Do not make another one */ a_ptr->cur_num = 1; /* Save the artifact flags */ q_ptr->flags[0] |= a_ptr->flags[0]; q_ptr->flags[1] |= a_ptr->flags[1]; q_ptr->flags[2] |= a_ptr->flags[2]; q_ptr->flags[3] |= a_ptr->flags[3]; /* Extract the fields */ q_ptr->pval = a_ptr->pval; q_ptr->ac = a_ptr->ac; q_ptr->dd = a_ptr->dd; q_ptr->ds = a_ptr->ds; q_ptr->to_a = a_ptr->to_a; q_ptr->to_h = a_ptr->to_h; q_ptr->to_d = a_ptr->to_d; q_ptr->weight = a_ptr->weight; /* Save the inscription */ q_ptr->xtra_name = quark_add(a_name + a_ptr->name); /* Apply special scripts */ apply_object_trigger(TRIGGER_MAKE, q_ptr, "i", "lev", a_ptr->level); if (!a_ptr->cost) { /* Hack -- "worthless" artifacts */ q_ptr->cost = 0L; } else { /* Hack - use the artifact price */ q_ptr->cost = k_info[q_ptr->k_idx].cost + a_ptr->cost; } /* Drop the artifact from heaven */ drop_near(q_ptr, -1, x, y); } zangband/src/avatar.c0000644000000000000000000002177110250356274013565 0ustar rootroot/* File: avatar.c */ /* * Purpose: Enable an Ultima IV style "avatar" game where you try to * achieve perfection in various virtues. * * Topi Ylinen 1998 * */ /* * Copyright (c) 1989 James E. Wilson, Christopher J. Stuart * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* The names of the virtues */ cptr virtue[MAX_VIRTUE] = { "Compassion", "Honour", "Justice", "Sacrifice", "Knowledge", "Faith", "Enlightenment", "Mysticism", "Chance", "Nature", "Harmony", "Vitality", "Unlife", "Patience", "Temperance", "Diligence", "Valour", "Individualism", }; /* Aux function */ static bool exist_virtue(int type) { int i; /* Search */ for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { if (p_ptr->vir_types[i] == type) return TRUE; } /* No match */ return FALSE; } /* Aux function */ static void get_random_virtue(int which) { int type = 0; /* Randomly choose a type */ while (!type || exist_virtue(type)) { switch (randint1(29)) { case 1: case 2: case 3: type = V_SACRIFICE; break; case 4: case 5: case 6: type = V_COMPASSION; break; case 7: case 8: case 9: case 10: case 11: case 12: type = V_VALOUR; break; case 13: case 14: case 15: case 16: case 17: type = V_HONOUR; break; case 18: case 19: case 20: case 21: type = V_JUSTICE; break; case 22: case 23: type = V_TEMPERANCE; break; case 24: case 25: type = V_HARMONY; break; case 26: case 27: case 28: type = V_PATIENCE; break; default: type = V_DILIGENCE; } } /* Chosen */ p_ptr->vir_types[which] = type; } /* * Select virtues & reset values for a new character */ void get_virtues(void) { int i, j; /* Reset */ for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { p_ptr->virtues[i] = 0; p_ptr->vir_types[i] = 0; } i = 0; /* Get pre-defined types */ /* One or more virtues based on class */ switch (p_ptr->rp.pclass) { case CLASS_WARRIOR: p_ptr->vir_types[i++] = V_VALOUR; p_ptr->vir_types[i++] = V_HONOUR; break; case CLASS_MAGE: p_ptr->vir_types[i++] = V_KNOWLEDGE; p_ptr->vir_types[i++] = V_ENCHANT; break; case CLASS_PRIEST: p_ptr->vir_types[i++] = V_FAITH; p_ptr->vir_types[i++] = V_TEMPERANCE; break; case CLASS_ROGUE: p_ptr->vir_types[i++] = V_HONOUR; break; case CLASS_RANGER: p_ptr->vir_types[i++] = V_NATURE; p_ptr->vir_types[i++] = V_TEMPERANCE; break; case CLASS_PALADIN: p_ptr->vir_types[i++] = V_JUSTICE; p_ptr->vir_types[i++] = V_VALOUR; p_ptr->vir_types[i++] = V_HONOUR; p_ptr->vir_types[i++] = V_FAITH; break; case CLASS_WARRIOR_MAGE: p_ptr->vir_types[i++] = V_ENCHANT; p_ptr->vir_types[i++] = V_VALOUR; break; case CLASS_CHAOS_WARRIOR: p_ptr->vir_types[i++] = V_CHANCE; p_ptr->vir_types[i++] = V_INDIVIDUALISM; break; case CLASS_MONK: p_ptr->vir_types[i++] = V_FAITH; p_ptr->vir_types[i++] = V_HARMONY; p_ptr->vir_types[i++] = V_TEMPERANCE; p_ptr->vir_types[i++] = V_PATIENCE; break; case CLASS_MINDCRAFTER: p_ptr->vir_types[i++] = V_HARMONY; p_ptr->vir_types[i++] = V_ENLIGHTEN; p_ptr->vir_types[i++] = V_PATIENCE; break; case CLASS_HIGH_MAGE: p_ptr->vir_types[i++] = V_ENLIGHTEN; p_ptr->vir_types[i++] = V_ENCHANT; p_ptr->vir_types[i++] = V_KNOWLEDGE; break; }; /* Get one virtue based on race */ switch (p_ptr->rp.prace) { case RACE_HUMAN: case RACE_HALF_ELF: p_ptr->vir_types[i++] = V_INDIVIDUALISM; break; case RACE_ELF: case RACE_SPRITE: p_ptr->vir_types[i++] = V_NATURE; break; case RACE_HOBBIT: case RACE_HALF_OGRE: p_ptr->vir_types[i++] = V_TEMPERANCE; break; case RACE_DWARF: case RACE_KLACKON: p_ptr->vir_types[i++] = V_DILIGENCE; break; case RACE_GNOME: case RACE_CYCLOPS: p_ptr->vir_types[i++] = V_KNOWLEDGE; break; case RACE_HALF_ORC: case RACE_AMBERITE: case RACE_KOBOLD: p_ptr->vir_types[i++] = V_HONOUR; break; case RACE_HALF_TROLL: case RACE_BARBARIAN: p_ptr->vir_types[i++] = V_VALOUR; break; case RACE_HIGH_ELF: p_ptr->vir_types[i++] = V_VITALITY; break; case RACE_HALF_GIANT: case RACE_GOLEM: p_ptr->vir_types[i++] = V_JUSTICE; break; case RACE_HALF_TITAN: p_ptr->vir_types[i++] = V_HARMONY; break; case RACE_YEEK: p_ptr->vir_types[i++] = V_SACRIFICE; break; case RACE_MIND_FLAYER: p_ptr->vir_types[i++] = V_ENLIGHTEN; break; case RACE_DARK_ELF: case RACE_DRACONIAN: p_ptr->vir_types[i++] = V_ENCHANT; break; case RACE_NIBELUNG: p_ptr->vir_types[i++] = V_PATIENCE; break; case RACE_IMP: p_ptr->vir_types[i++] = V_FAITH; break; case RACE_ZOMBIE: case RACE_SKELETON: case RACE_VAMPIRE: case RACE_SPECTRE: case RACE_GHOUL: p_ptr->vir_types[i++] = V_UNLIFE; break; case RACE_BEASTMAN: p_ptr->vir_types[i++] = V_CHANCE; break; } /* Get a virtue for r[0].realm */ if (p_ptr->spell.r[0].realm) { switch (p_ptr->spell.r[0].realm) { case 1: if (exist_virtue(V_VITALITY)) p_ptr->vir_types[i++] = V_COMPASSION; else p_ptr->vir_types[i++] = V_VITALITY; break; case 2: if (exist_virtue(V_ENCHANT)) p_ptr->vir_types[i++] = V_KNOWLEDGE; else p_ptr->vir_types[i++] = V_ENCHANT; break; case 3: if (exist_virtue(V_NATURE)) p_ptr->vir_types[i++] = V_HARMONY; else p_ptr->vir_types[i++] = V_NATURE; break; case 4: if (exist_virtue(V_CHANCE)) p_ptr->vir_types[i++] = V_INDIVIDUALISM; else p_ptr->vir_types[i++] = V_CHANCE; break; case 5: p_ptr->vir_types[i++] = V_UNLIFE; break; case 6: p_ptr->vir_types[i++] = V_KNOWLEDGE; break; case 7: break; }; } /* Get a virtue for r[1].realm */ if (p_ptr->spell.r[1].realm) { switch (p_ptr->spell.r[1].realm) { case 1: if (exist_virtue(V_VITALITY)) p_ptr->vir_types[i++] = V_COMPASSION; else p_ptr->vir_types[i++] = V_VITALITY; break; case 2: if (exist_virtue(V_ENCHANT)) p_ptr->vir_types[i++] = V_KNOWLEDGE; else p_ptr->vir_types[i++] = V_ENCHANT; break; case 3: if (exist_virtue(V_NATURE)) p_ptr->vir_types[i++] = V_HARMONY; else p_ptr->vir_types[i++] = V_NATURE; break; case 4: if (exist_virtue(V_CHANCE)) p_ptr->vir_types[i++] = V_INDIVIDUALISM; else p_ptr->vir_types[i++] = V_CHANCE; break; case 5: p_ptr->vir_types[i++] = V_UNLIFE; break; case 6: p_ptr->vir_types[i++] = V_KNOWLEDGE; break; case 7: break; }; } /* Eliminate doubles */ for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { for (j = i + 1; j < MAX_PLAYER_VIRTUES; j++) { if ((p_ptr->vir_types[j] != 0) && (p_ptr->vir_types[j] == p_ptr->vir_types[i])) p_ptr->vir_types[j] = 0; } } /* Fill in the blanks */ for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { if (p_ptr->vir_types[i] == 0) get_random_virtue(i); } } void chg_virtue(int virtue, int amount) { int i; for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { if (p_ptr->vir_types[i] == virtue) { if (amount > 0) { if (amount + p_ptr->virtues[i] > 125) p_ptr->virtues[i] = 125; else p_ptr->virtues[i] = p_ptr->virtues[i] + amount; } else { if (amount + p_ptr->virtues[i] < -125) p_ptr->virtues[i] = -125; else p_ptr->virtues[i] = p_ptr->virtues[i] + amount; } return; } } } void dump_virtues(FILE *OutFile) { int v_nr; if (!OutFile) return; for (v_nr = 0; v_nr < MAX_PLAYER_VIRTUES; v_nr++) { char virt_name[20]; int tester = p_ptr->virtues[v_nr]; strcpy(virt_name, virtue[(p_ptr->vir_types[v_nr]) - 1]); if ((p_ptr->vir_types[v_nr] == 0) || (p_ptr->vir_types[v_nr] > MAX_VIRTUE)) froff(OutFile, "Oops. No info about %s.", virt_name); else if (tester < -100) froff(OutFile, "You are the polar opposite of %s.", virt_name); else if (tester < -80) froff(OutFile, "You are an arch-enemy of %s.", virt_name); else if (tester < -60) froff(OutFile, "You are a bitter enemy of %s.", virt_name); else if (tester < -40) froff(OutFile, "You are an enemy of %s.", virt_name); else if (tester < -20) froff(OutFile, "You have sinned against %s.", virt_name); else if (tester < 0) froff(OutFile, "You have strayed from the path of %s.", virt_name); else if (tester == 0) froff(OutFile, "You are neutral to %s.", virt_name); else if (tester < 20) froff(OutFile, "You are somewhat virtuous in %s.", virt_name); else if (tester < 40) froff(OutFile, "You are virtuous in %s.", virt_name); else if (tester < 60) froff(OutFile, "You are very virtuous in %s.", virt_name); else if (tester < 80) froff(OutFile, "You are a champion of %s.", virt_name); else if (tester < 100) froff(OutFile, "You are a great champion of %s.", virt_name); else froff(OutFile, "You are the living embodiment of %s.", virt_name); froff(OutFile, "\n"); } if (p_ptr->state.wizard) froff(OutFile, "Your overall alignment is %ld.\n", p_ptr->align); } zangband/src/birth.c0000755000000000000000000010357410250356274013424 0ustar rootroot/* File: birth.c */ /* Purpose: create a player character */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * How often the autoroller will update the display and pause * to check for user interuptions. * Bigger values will make the autoroller faster, but slower * system may have problems because the user can't stop the * autoroller for this number of rolls. */ #define AUTOROLLER_STEP 25L /* * Maximum number of tries for selection of a proper quest monster */ #define MAX_TRIES 100 /* * Forward declare */ typedef struct birther birther; /* * A structure to hold "rolled" information */ struct birther { s16b age; s16b wt; s16b ht; s16b sc; s32b au; s16b stat[A_MAX]; s16b patron; s16b hp[PY_MAX_LEVEL]; }; /* * The last character displayed */ static birther prev; /* * Current stats */ static s16b stat_use[A_MAX]; /* * Save the current data for later */ static void save_prev_data(void) { int i; /*** Save the current data ***/ /* Save the data */ prev.age = p_ptr->rp.age; prev.wt = p_ptr->rp.wt; prev.ht = p_ptr->rp.ht; prev.sc = p_ptr->rp.sc; prev.au = p_ptr->au; /* Save the stats */ for (i = 0; i < A_MAX; i++) { prev.stat[i] = p_ptr->stat[i].max; } /* Save the patron */ prev.patron = p_ptr->chaos_patron; /* Save the hitpoints */ for (i = 0; i < PY_MAX_LEVEL; i++) { prev.hp[i] = p_ptr->player_hp[i]; } } /* * Load the previous data */ static void load_prev_data(void) { int i; birther temp; /*** Save the current data ***/ /* Save the data */ temp.age = p_ptr->rp.age; temp.wt = p_ptr->rp.wt; temp.ht = p_ptr->rp.ht; temp.sc = p_ptr->rp.sc; temp.au = p_ptr->au; /* Save the stats */ for (i = 0; i < A_MAX; i++) { temp.stat[i] = p_ptr->stat[i].max; } /* Save the patron */ temp.patron = p_ptr->chaos_patron; /* Save the hitpoints */ for (i = 0; i < PY_MAX_LEVEL; i++) { temp.hp[i] = p_ptr->player_hp[i]; } /*** Load the previous data ***/ /* Load the data */ p_ptr->rp.age = prev.age; p_ptr->rp.wt = prev.wt; p_ptr->rp.ht = prev.ht; p_ptr->rp.sc = prev.sc; p_ptr->au = prev.au; /* Load the stats */ for (i = 0; i < A_MAX; i++) { p_ptr->stat[i].max = prev.stat[i]; p_ptr->stat[i].cur = prev.stat[i]; } /* Load the patron */ p_ptr->chaos_patron = prev.patron; /* Load the hitpoints */ for (i = 0; i < PY_MAX_LEVEL; i++) { p_ptr->player_hp[i] = prev.hp[i]; } /*** Save the current data ***/ /* Save the data */ prev.age = temp.age; prev.wt = temp.wt; prev.ht = temp.ht; prev.sc = temp.sc; prev.au = temp.au; /* Save the stats */ for (i = 0; i < A_MAX; i++) { prev.stat[i] = temp.stat[i]; } /* Save the patron */ prev.patron = temp.patron; /* Save the hitpoints */ for (i = 0; i < PY_MAX_LEVEL; i++) { prev.hp[i] = temp.hp[i]; } } /* * Roll for a characters stats * * For efficiency, we include a chunk of "calc_bonuses()". */ static void get_stats(void) { int i, j; int bonus; int dice[18]; /* Roll and verify some stats */ while (TRUE) { /* Roll some dice */ for (j = i = 0; i < 18; i++) { /* Roll the dice */ dice[i] = randint1(3 + i % 3); /* Collect the maximum */ j += dice[i]; } /* Verify totals */ if ((j > 42) && (j < 57)) break; /* 57 was 54... I hate 'magic numbers' :< TY */ } /* Acquire the stats */ for (i = 0; i < A_MAX; i++) { /* Extract 5 + 1d3 + 1d4 + 1d5 */ j = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2]; /* Obtain a "bonus" for "race" and "class" */ bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i]; /* Apply the bonus to the stat (somewhat randomly) */ stat_use[i] = adjust_stat(i, j * 10, bonus); /* Start fully healed */ p_ptr->stat[i].cur = p_ptr->stat[i].max = stat_use[i]; } } /* * Roll for some info that the auto-roller ignores */ static void get_extra(void) { int i, j, min_value, max_value; #ifdef SHOW_LIFE_RATE int percent; #endif /* SHOW_LIFE_RATE */ /* Level one */ p_ptr->max_lev = p_ptr->lev = 1; /* Experience factor */ p_ptr->expfact = rp_ptr->r_exp + cp_ptr->c_exp; /* Hitdice */ p_ptr->rp.hitdie = rp_ptr->r_mhp + cp_ptr->c_mhp; /* Initial hitpoints */ p_ptr->mhp = p_ptr->rp.hitdie; /* Minimum hitpoints at highest level */ min_value = (PY_MAX_LEVEL * (p_ptr->rp.hitdie - 1) * 3) / 8; min_value += PY_MAX_LEVEL; /* Maximum hitpoints at highest level */ max_value = (PY_MAX_LEVEL * (p_ptr->rp.hitdie - 1) * 5) / 8; max_value += PY_MAX_LEVEL; /* Pre-calculate level 1 hitdice */ p_ptr->player_hp[0] = p_ptr->rp.hitdie; /* Roll out the hitpoints */ while (TRUE) { /* Roll the hitpoint values */ for (i = 1; i < PY_MAX_LEVEL; i++) { /* Add in racial hit dice */ j = randint1(rp_ptr->r_mhp); p_ptr->player_hp[i] = p_ptr->player_hp[i - 1] + j; /* If class hit dice is non zero - add it on */ if (cp_ptr->c_mhp) { p_ptr->player_hp[i] += randint1(cp_ptr->c_mhp); } } /* XXX Could also require acceptable "mid-level" hitpoints */ /* Require "valid" hitpoints at highest level */ if (p_ptr->player_hp[PY_MAX_LEVEL - 1] < min_value) continue; if (p_ptr->player_hp[PY_MAX_LEVEL - 1] > max_value) continue; /* Acceptable */ break; } #ifdef SHOW_LIFE_RATE percent = (int)(((long)p_ptr->player_hp[PY_MAX_LEVEL - 1] * 200L) / (2 * p_ptr->rp.hitdie + ((PY_MAX_LEVEL - 1) * (p_ptr->rp.hitdie + 1)))); msgf("Current Life Rating is %d/100.", percent); message_flush(); #endif /* SHOW_LIFE_RATE */ } /* * Computes character's age, height, and weight */ static void get_ahw(void) { int h_percent; /* Calculate the age */ p_ptr->rp.age = rp_ptr->b_age + randint1(rp_ptr->m_age); /* Calculate the height/weight for males */ if (p_ptr->rp.psex == SEX_MALE) { p_ptr->rp.ht = Rand_normal(rp_ptr->m_b_ht, rp_ptr->m_m_ht); h_percent = (int)(p_ptr->rp.ht) * 100 / (int)(rp_ptr->m_b_ht); p_ptr->rp.wt = Rand_normal((int)(rp_ptr->m_b_wt) * h_percent / 100, (int)(rp_ptr->m_m_wt) * h_percent / 300); } /* Calculate the height/weight for females */ else if (p_ptr->rp.psex == SEX_FEMALE) { p_ptr->rp.ht = Rand_normal(rp_ptr->f_b_ht, rp_ptr->f_m_ht); h_percent = (int)(p_ptr->rp.ht) * 100 / (int)(rp_ptr->f_b_ht); p_ptr->rp.wt = Rand_normal((int)(rp_ptr->f_b_wt) * h_percent / 100, (int)(rp_ptr->f_m_wt) * h_percent / 300); } } /* * Get the player's starting money */ static void get_money(void) { int i, gold; /* Social Class determines starting gold */ gold = (p_ptr->rp.sc * 6) + rand_range(300, 400); /* Process the stats */ for (i = 0; i < A_MAX; i++) { /* Mega-Hack -- reduce gold for high stats */ if (stat_use[i] >= 180 + 50) gold -= 300; else if (stat_use[i] >= 180 + 20) gold -= 200; else if (stat_use[i] > 180) gold -= 150; else gold -= (stat_use[i] - 80); } /* Minimum 100 gold */ if (gold < 100) gold = 100; /* Save the gold */ p_ptr->au = gold; } /* * Clear all the global "character" data */ static void player_wipe(void) { int i; bool options[OPT_PLAYER]; bool birth[OPT_BIRTH]; pcave_type *pcave[MAX_HGT]; pblk_ptr **pwild; /* Hack -- save these allocated arrays */ C_COPY(options, p_ptr->options, OPT_PLAYER, bool); C_COPY(birth, p_ptr->birth, OPT_BIRTH, bool); /* Hack -- save the cave and wilderness arrays */ C_COPY(pcave, p_ptr->pcave, MAX_HGT, pcave_type *); pwild = p_ptr->pwild; /* * Delete the carried objects */ if (p_ptr->inventory) delete_object_list(&p_ptr->inventory); /* Hack -- zero the struct */ (void)WIPE(p_ptr, player_type); /* Hack -- Restore the cave and wilderness arrays */ C_COPY(p_ptr->pcave, pcave, MAX_HGT, pcave_type *); p_ptr->pwild = pwild; /* Hack -- Restore the option arrays */ C_COPY(p_ptr->options, options, OPT_PLAYER, bool); C_COPY(p_ptr->birth, birth, OPT_BIRTH, bool); /* Start with no artifacts made yet */ for (i = 0; i < z_info->a_max; i++) { artifact_type *a_ptr = &a_info[i]; a_ptr->cur_num = 0; } /* Reset the objects */ k_info_reset(); /* Reset the "monsters" */ for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Hack -- Reset the counter */ r_ptr->cur_num = 0; /* Hack -- Reset the max counter */ r_ptr->max_num = 100; /* Hack -- Reset the max counter */ if (FLAG(r_ptr, RF_UNIQUE)) r_ptr->max_num = 1; if (FLAG(r_ptr, RF_UNIQUE_7)) r_ptr->max_num = 7; /* Clear player kills */ r_ptr->r_pkills = 0; } /* Hack -- Well fed player */ p_ptr->food = PY_FOOD_FULL - 1; /* None of the spells have been learned yet */ for (i = 0; i < PY_MAX_SPELLS; i++) p_ptr->spell.order[i] = 99; /* Clear "cheat" options */ cheat_peek = FALSE; cheat_hear = FALSE; cheat_room = FALSE; cheat_xtra = FALSE; cheat_know = FALSE; cheat_live = FALSE; /* Default pet command settings */ p_ptr->pet_follow_distance = PET_FOLLOW_DIST; } /* * Each player starts out with a few items, given as tval/sval pairs. * In addition, he always has some food and a few torches. */ static const byte player_init[MAX_CLASS][3][2] = { { /* Warrior */ {TV_RING, SV_RING_RES_FEAR}, /* Warriors need it! */ {TV_SWORD, SV_BROAD_SWORD}, {TV_HARD_ARMOR, SV_CHAIN_MAIL} }, { /* Mage */ {TV_SORCERY_BOOK, 0}, /* Hack: for r[0].realm book */ {TV_SWORD, SV_DAGGER}, {TV_DEATH_BOOK, 0} /* Hack: for r[1].realm book */ }, { /* Priest */ {TV_SORCERY_BOOK, 0}, /* Hack: for Life / Death book */ {TV_HAFTED, SV_MACE}, {TV_DEATH_BOOK, 0} /* Hack: for r[1].realm book */ }, { /* Rogue */ {TV_SORCERY_BOOK, 0}, /* Hack: for r[0].realm book */ {TV_SWORD, SV_DAGGER}, {TV_SOFT_ARMOR, SV_SOFT_LEATHER_ARMOR} }, { /* Ranger */ {TV_NATURE_BOOK, 0}, {TV_SWORD, SV_DAGGER}, {TV_DEATH_BOOK, 0} /* Hack: for r[1].realm book */ }, { /* Paladin */ {TV_SORCERY_BOOK, 0}, {TV_SWORD, SV_BROAD_SWORD}, {TV_SCROLL, SV_SCROLL_PROTECTION_FROM_EVIL} }, { /* Warrior-Mage */ {TV_SORCERY_BOOK, 0}, /* Hack: for r[0].realm book */ {TV_SWORD, SV_SHORT_SWORD}, {TV_DEATH_BOOK, 0} /* Hack: for r[1].realm book */ }, { /* Chaos Warrior */ {TV_SORCERY_BOOK, 0}, /* Hack: For r[0].realm book */ {TV_SWORD, SV_BROAD_SWORD}, {TV_HARD_ARMOR, SV_METAL_SCALE_MAIL} }, { /* Monk */ {TV_SORCERY_BOOK, 0}, {TV_POTION, SV_POTION_HEALING}, {TV_SOFT_ARMOR, SV_SOFT_LEATHER_ARMOR}, }, { /* Mindcrafter */ {TV_SWORD, SV_DAGGER}, {TV_POTION, SV_POTION_RES_WIS}, {TV_SOFT_ARMOR, SV_SOFT_LEATHER_ARMOR}, }, { /* High Mage */ {TV_SORCERY_BOOK, 0}, /* Hack: for r[0].realm book */ {TV_SWORD, SV_DAGGER}, {TV_RING, SV_RING_SUSTAIN_INT} }, }; /* * Init players with some belongings * * Having an item makes the player "aware" of its purpose. */ static void player_outfit(void) { int i, tv, sv; object_type *q_ptr; /* Give the player some food */ switch (p_ptr->rp.prace) { case RACE_GOLEM: case RACE_SKELETON: case RACE_ZOMBIE: case RACE_VAMPIRE: case RACE_SPECTRE: case RACE_GHOUL: { /* Scrolls of satisfy hunger */ q_ptr = object_prep(lookup_kind(TV_SCROLL, SV_SCROLL_SATISFY_HUNGER)); q_ptr->number = (byte)rand_range(2, 5); object_aware(q_ptr); object_known(q_ptr); /* These objects give no score */ q_ptr->info |= OB_NO_EXP; (void)inven_carry(q_ptr); break; } default: { /* Food rations */ q_ptr = object_prep(lookup_kind(TV_FOOD, SV_FOOD_RATION)); q_ptr->number = (byte)rand_range(3, 7); object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr); } } if (p_ptr->rp.prace == RACE_VAMPIRE) { /* Hack -- Give the player scrolls of DARKNESS! */ q_ptr = object_prep(lookup_kind(TV_SCROLL, SV_SCROLL_DARKNESS)); q_ptr->number = (byte)rand_range(2, 5); object_aware(q_ptr); object_known(q_ptr); /* These objects give no score */ q_ptr->info |= OB_NO_EXP; (void)inven_carry(q_ptr); } else { /* Hack -- Give the player some torches */ q_ptr = object_prep(lookup_kind(TV_LITE, SV_LITE_TORCH)); q_ptr->number = (byte)rand_range(3, 7); q_ptr->timeout = rand_range(3, 7) * 500; q_ptr->pval = 0; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr); } if (p_ptr->rp.pclass == CLASS_RANGER) { /* Hack -- Give the player some arrows */ q_ptr = object_prep(lookup_kind(TV_ARROW, SV_AMMO_NORMAL)); q_ptr->number = (byte)rand_range(15, 20); /* These objects give no score */ q_ptr->info |= OB_NO_EXP; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr); /* Hack -- Give the player a bow */ q_ptr = object_prep(lookup_kind(TV_BOW, SV_SHORT_BOW)); /* These objects give no score */ q_ptr->info |= OB_NO_EXP; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr); } else if (p_ptr->rp.pclass == CLASS_HIGH_MAGE) { /* Hack -- Give the player a wand of magic missile */ q_ptr = object_prep(lookup_kind(TV_WAND, SV_WAND_MAGIC_MISSILE)); q_ptr->number = 1; q_ptr->pval = (byte)rand_range(25, 30); /* These objects give no score */ q_ptr->info |= OB_NO_EXP; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr); } /* Hack -- Give the player three useful objects */ for (i = 0; i < 3; i++) { /* Look up standard equipment */ tv = player_init[p_ptr->rp.pclass][i][0]; sv = player_init[p_ptr->rp.pclass][i][1]; /* Hack to initialize spellbooks */ if (tv == TV_SORCERY_BOOK) tv = TV_LIFE_BOOK + p_ptr->spell.r[0].realm - 1; else if (tv == TV_DEATH_BOOK) tv = TV_LIFE_BOOK + p_ptr->spell.r[1].realm - 1; else if (tv == TV_RING && sv == SV_RING_RES_FEAR && p_ptr->rp.prace == RACE_BARBARIAN) { /* Barbarians do not need a ring of resist fear */ sv = SV_RING_SUSTAIN_STR; } /* Hack -- Give the player an object */ q_ptr = object_prep(lookup_kind(tv, sv)); /* Assassins begin the game with a poisoned dagger */ if (tv == TV_SWORD && p_ptr->rp.pclass == CLASS_ROGUE && p_ptr->spell.r[0].realm == REALM_DEATH) { add_ego_flags(q_ptr, EGO_BRAND_POIS); } /* These objects give no score */ q_ptr->info |= OB_NO_EXP; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr); } } /* Locations of the tables on the screen */ #define QUESTION_COL 3 #define SEX_COL 0 #define RACE_COL 12 #define RACE_AUX_COL 27 #define CLASS_COL 27 #define CLASS_AUX_COL 48 #define REALM1_COL 48 #define REALM2_COL 60 /* * Player sex */ static bool get_player_sex(void) { int i; cptr genders[MAX_SEXES]; /* Extra info */ put_fstr(QUESTION_COL, QUESTION_ROW, "Your 'sex' does not have any significant gameplay effects."); /* Tabulate genders */ for (i = 0; i < MAX_SEXES; i++) { genders[i] = sex_info[i].title; } p_ptr->rp.psex = get_player_choice(genders, MAX_SEXES, SEX_COL, 15, "charattr.txt#TheSexes", NULL); /* No selection? */ if (p_ptr->rp.psex == INVALID_CHOICE) { p_ptr->rp.psex = 0; return (FALSE); } /* Save the sex pointer */ sp_ptr = &sex_info[p_ptr->rp.psex]; return (TRUE); } /* * Display additional information about each race during the selection. */ static void race_aux_hook(cptr r_str) { int race; /* Extract the proper race index from the string. */ for (race = 0; race < MAX_RACES; race++) { if (streq(r_str, race_info[race].title)) break; } if (race == MAX_RACES) return; /* Display relevant details. */ put_fstr(RACE_AUX_COL, TABLE_ROW, "%s%+d\n" "%s%+d\n" "%s%+d\n" "%s%+d\n" "%s%+d\n" "%s%+d\n" "Hit die: %d \n" "Experience: %2d%% \n" "Infravision: %d ft", stat_names_reduced[0], race_info[race].r_adj[0], stat_names_reduced[1], race_info[race].r_adj[1], stat_names_reduced[2], race_info[race].r_adj[2], stat_names_reduced[3], race_info[race].r_adj[3], stat_names_reduced[4], race_info[race].r_adj[4], stat_names_reduced[5], race_info[race].r_adj[5], race_info[race].r_mhp, race_info[race].r_exp, race_info[race].infra * 10); } /* * Player race */ static bool get_player_race(void) { int i; cptr races[MAX_RACES]; /* Extra info */ put_fstr(QUESTION_COL, QUESTION_ROW, "Your 'race' determines various intrinsic factors and bonuses."); /* Tabulate races */ for (i = 0; i < MAX_RACES; i++) { races[i] = race_info[i].title; } p_ptr->rp.prace = get_player_sort_choice(races, MAX_RACES, RACE_COL, 15, "charattr.txt#TheRaces", race_aux_hook); /* No selection? */ if (p_ptr->rp.prace == INVALID_CHOICE) { p_ptr->rp.prace = 0; return (FALSE); } /* Give beastman a mutation at character birth */ if (p_ptr->rp.prace == RACE_BEASTMAN) { p_ptr->change |= (PC_MUTATE); } /* Save the race pointer */ rp_ptr = &race_info[p_ptr->rp.prace]; /* Success */ return (TRUE); } /* * Display additional information about each class during the selection. */ static void class_aux_hook(cptr c_str) { int class_idx; char s[128]; /* Extract the proper class index from the string. */ for (class_idx = 0; class_idx < MAX_CLASS; class_idx++) { if (streq(c_str, class_info[class_idx].title)) break; /* Also test for titles in parentheses */ strnfmt(s, 128, "(%s)", class_info[class_idx].title); if (streq(c_str, s)) break; } if (class_idx == MAX_CLASS) return; /* Display relevant details. */ put_fstr(CLASS_AUX_COL, TABLE_ROW, "%s%+d\n" "%s%+d\n" "%s%+d\n" "%s%+d\n" "%s%+d\n" "%s%+d\n" "Hit die: %d \n" "Experience: %2d%%", stat_names_reduced[0], class_info[class_idx].c_adj[0], stat_names_reduced[1], class_info[class_idx].c_adj[1], stat_names_reduced[2], class_info[class_idx].c_adj[2], stat_names_reduced[3], class_info[class_idx].c_adj[3], stat_names_reduced[4], class_info[class_idx].c_adj[4], stat_names_reduced[5], class_info[class_idx].c_adj[5], class_info[class_idx].c_mhp, class_info[class_idx].c_exp); } /* * Player class */ static bool get_player_class(void) { int i; cptr classes[MAX_CLASS]; /* Extra info */ put_fstr(QUESTION_COL, QUESTION_ROW, "Your 'class' determines various intrinsic abilities and bonuses.\n" "Any entries in parentheses should only be used by advanced players."); /* Tabulate classes */ for (i = 0; i < MAX_CLASS; i++) { classes[i] = class_info[i].title; } p_ptr->rp.pclass = get_player_choice(classes, MAX_CLASS, CLASS_COL, 20, "charattr.txt#TheClasses", class_aux_hook); /* No selection? */ if (p_ptr->rp.pclass == INVALID_CHOICE) { p_ptr->rp.pclass = 0; return (FALSE); } /* Set class */ cp_ptr = &class_info[p_ptr->rp.pclass]; mp_ptr = &magic_info[p_ptr->rp.pclass]; return (TRUE); } /* * Choose the magical realms */ static bool get_player_realms(void) { int i; int count = 0; cptr realms[MAX_REALM]; int select[MAX_REALM]; int choose; /* No realms at all? */ select[0] = REALM_NONE; /* Get choices */ for (i = 1; i < MAX_REALM; i++) { /* Can we use this realm? */ if (realm_choices1[p_ptr->rp.pclass] & (1 << (i - 1))) { /* Save the information */ select[count] = i; realms[count] = realm_names[i]; /* Count them */ count++; } } /* No magic? */ if (!count) return (TRUE); /* Extra info */ put_fstr(QUESTION_COL, QUESTION_ROW, "Life and Sorcery are protective, Chaos and Death are destructive.\n" "Nature has both defensive and offensive spells."); choose = get_player_choice(realms, count, REALM1_COL, 10, "magic.txt#MagicRealms", NULL); /* No selection? */ if (choose == INVALID_CHOICE) return (FALSE); /* Save the choice */ p_ptr->spell.r[0].realm = select[choose]; /* Paranoia - No realms at all? */ select[0] = REALM_NONE; /* Reset counter */ count = 0; /* Get choices */ for (i = 1; i < MAX_REALM; i++) { /* Can we use this realm? */ if ((realm_choices2[p_ptr->rp.pclass] & (1 << (i - 1))) && (i != p_ptr->spell.r[0].realm)) { /* Save the information */ select[count] = i; realms[count] = realm_names[i]; /* Increment counter */ count++; } } /* No second realm? */ if (!count) return (TRUE); choose = get_player_choice(realms, count, REALM2_COL, 10, "magic.txt#MagicRealms", NULL); /* No selection? */ if (choose == INVALID_CHOICE) return (FALSE); /* Save the choice */ p_ptr->spell.r[1].realm = select[choose]; /* Done */ return (TRUE); } /* * Helper function for 'player_birth()'. * * This function allows the player to select a sex, race, and class, and * modify options (including the birth options). * * Taken from V 2.9.0 */ static bool player_birth_aux_1(void) { /*** Instructions ***/ /* Clear screen */ Term_clear(); /* Display some helpful information */ put_fstr(QUESTION_COL, HEADER_ROW, "Please select your character from the menu below.\n" "Use the movement keys to scroll the menu, 'enter' to select the current\n" "menu item, '*' for a random menu item, 'ESC' to restart the character\n" "selection, '=' for the birth options, '?' for help, or 'Ctrl-X' to quit."); if (!get_player_sex()) return (FALSE); /* Clean up */ clear_region(0, QUESTION_ROW, TABLE_ROW - 1); /* Choose the players race */ if (!get_player_race()) return (FALSE); /* Clean up */ clear_region(0, QUESTION_ROW, TABLE_ROW - 1); /* Choose the players class */ if (!get_player_class()) return (FALSE); /* Clean up */ clear_region(0, QUESTION_ROW, TABLE_ROW - 1); /* Choose the magic realms */ if (!get_player_realms()) return (FALSE); /* Clear */ Term_clear(); /* Display the information so far. */ /* Name, Sex, Race, Class */ put_fstr(0, 2, "Name : " CLR_L_BLUE "%s\n" CLR_WHITE "Sex : " CLR_L_BLUE "%s\n" CLR_WHITE "Race : " CLR_L_BLUE "%s\n" CLR_WHITE "Class : " CLR_L_BLUE "%s\n", player_name, sp_ptr->title, rp_ptr->title, cp_ptr->title); if (p_ptr->spell.r[0].realm || p_ptr->spell.r[1].realm) { put_fstr(0, 6, "Magic : " CLR_L_BLUE "%s", realm_names[p_ptr->spell.r[0].realm]); } if (p_ptr->spell.r[1].realm) { put_fstr(11, 7, CLR_L_BLUE "%s", realm_names[p_ptr->spell.r[1].realm]); } /* And finally, initialize the starting quests */ init_player_quests(); /* Clear */ clear_from(15); /* Done */ return (TRUE); } /* * Initial stat costs (initial stats always range from 10 to 18 inclusive). */ static const int birth_stat_costs[(18 - 10) + 1] = { 0, 1, 2, 4, 7, 11, 16, 22, 30 }; /* * Helper function for 'player_birth()'. * * This function handles "point-based" character creation. * * The player selects, for each stat, a value from 10 to 18 (inclusive), * each costing a certain amount of points (as above), from a pool of 48 * available points, to which race/class modifiers are then applied. * * Each unused point is converted into 100 gold pieces, with a maximum of * 600 gp at birth. * * Taken from V 2.9.0 */ static bool player_birth_aux_2(void) { int i; int row = 3; int col = 42; int stat = 0; int stats[A_MAX]; int cost; char ch; int mode = DISPLAY_PLAYER_STANDARD; /* Initialize stats */ for (i = 0; i < A_MAX; i++) { /* Initial stats */ stats[i] = 10; } /* Roll for base hitpoints */ get_extra(); /* Roll for age/height/weight */ get_ahw(); /* Dodgy "social class" */ p_ptr->rp.sc = randint1(100); /* Hack -- get a chaos patron even if you are not a chaos warrior */ p_ptr->chaos_patron = (s16b)randint0(MAX_PATRON); p_ptr->muta1 = 0; p_ptr->muta2 = 0; p_ptr->muta3 = 0; /* Interact */ while (1) { /* Reset cost */ cost = 0; /* Process stats */ for (i = 0; i < A_MAX; i++) { int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i]; /* Reset stats */ p_ptr->stat[i].cur = adjust_stat(i, stats[i] * 10, bonus); p_ptr->stat[i].max = p_ptr->stat[i].cur; /* Total cost */ cost += birth_stat_costs[stats[i] - 10]; } /* Restrict cost */ if (cost > 48) { /* Warning */ bell("Excessive stats!"); /* Reduce stat */ stats[stat]--; /* Recompute costs */ continue; } /* Gold is inversely proportional to cost */ p_ptr->au = (100 * (48 - cost)) + 100; /* Maximum of 600 gold */ if (p_ptr->au > 600) p_ptr->au = 600; /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP); /* Update stuff */ update_stuff(); /* Fully healed */ p_ptr->chp = p_ptr->mhp; /* Fully rested */ p_ptr->csp = p_ptr->msp; /* Display the player */ display_player(mode); /* Display the costs header */ put_fstr(col + 32, row - 2, "Cost"); /* Display the costs */ for (i = 0; i < A_MAX; i++) { /* Display cost */ put_fstr(col + 32, row + (i - 1), "%4d", birth_stat_costs[stats[i] - 10]); } /* Prompt XXX XXX XXX */ prtf(0, 0, "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, Enter to accept.", cost); /* Place cursor just after cost of current stat */ Term_gotoxy(col + 36, row + stat - 1); /* Get key */ ch = inkey(); /* Quit */ if (ch == KTRL('X')) quit(NULL); /* Start over */ if (ch == ESCAPE) return (FALSE); /* Done */ if ((ch == '\r') || (ch == '\n')) break; /* Prev stat */ if (ch == '8') { stat = (stat + A_MAX - 1) % A_MAX; } /* Next stat */ if (ch == '2') { stat = (stat + 1) % A_MAX; } /* Decrease stat */ if ((ch == '4') && (stats[stat] > 10)) { stats[stat]--; } /* Increase stat */ if ((ch == '6') && (stats[stat] < 18)) { stats[stat]++; } } /* Process stats */ for (i = 0; i < A_MAX; i++) { int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i]; /* Apply some randomness */ p_ptr->stat[i].cur = adjust_stat(i, stats[i] * 10, bonus); p_ptr->stat[i].max = p_ptr->stat[i].cur; } /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP); /* Update stuff */ update_stuff(); /* Display the player */ display_player(mode); /* Done */ return (TRUE); } /* * Helper function for 'player_birth()'. * * This function handles "auto-rolling" and "random-rolling". * * The delay may be reduced, but is recommended to keep players * from continuously rolling up characters, which can be VERY * expensive CPU wise. And it cuts down on player stupidity. */ static bool player_birth_aux_3(void) { int i, v; bool flag; bool previous = FALSE; char ch; int mode = DISPLAY_PLAYER_STANDARD; #ifdef ALLOW_AUTOROLLER s16b stat_weight[A_MAX]; s16b stat_save[A_MAX]; s32b stat_match[A_MAX]; s32b auto_round = 0L; s32b last_round; /*** Autoroll ***/ /* Initialize */ if (autoroller) { char inp[80]; /* Clean up */ clear_from(10); /* Extra info */ put_fstr(5, 10, "The auto-roller will generate 500 characters and try to pick\n" "the one with the best stats, according to the weightings you\n" "choose below. Enter a value from 1-100 for each stat."); /* Prompt for the stat weights */ put_fstr(2, 15, "Enter weight for: "); /* Output the prompts */ for (i = 0; i < A_MAX; i++) { /* Reset the "success" counter */ stat_match[i] = 0; /* Dump the prompt */ put_fstr(5, 16 + i, "%-5s", stat_names[i]); } /* Input the minimum stats */ for (i = 0; i < A_MAX; i++) { /* In the Antiband version this is dependent on class & stat */ int def_weight = 50; /* Get a minimum stat */ while (TRUE) { /* Move the cursor */ Term_gotoxy(10, 16 + i); /* Default */ strnfmt(inp, 80, "%i", def_weight); /* Get a response (or escape) */ if (!askfor_aux(inp, 9)) inp[0] = '\0'; /* Extract an input */ v = atoi(inp); /* Break on valid input */ if (v <= 100) break; } /* Save the weight */ stat_weight[i] = (v > 0) ? v : def_weight; } } #endif /* ALLOW_AUTOROLLER */ /* Clean up */ clear_from(10); /*** Generate ***/ /* Roll */ while (TRUE) { int col = 42; /* Feedback */ if (autoroller) { s32b best_score; s32b cur_score; Term_clear(); /* Label */ put_fstr(col + 5, 2, "Weight"); /* Label */ put_fstr(col + 13, 2, " Roll"); /* Put the stat weights */ for (i = 0; i < A_MAX; i++) { /* Label stats */ put_fstr(col, i + 3, stat_names[i]); /* Put the weight */ put_fstr(col + 5, i + 3, CLR_L_BLUE "%6i", stat_weight[i]); } /* Note when we started */ last_round = auto_round; /* Label count */ put_fstr(col + 13, 10, "Round:"); /* Indicate the state */ put_fstr(col + 13, 12, "(Hit ESC to stop)"); best_score = -1; for (i = 0; i < A_MAX; i++) { stat_save[i] = 3; } /* Auto-roll */ while (TRUE) { /* Get a new character */ get_stats(); /* Advance the round */ auto_round++; /* Hack -- Prevent overflow */ if (auto_round >= 1000000L) break; /* Calculate a score for the rolled stats */ cur_score = 0; for (i = 0; i < A_MAX; i++) { cur_score += (p_ptr->stat[i].cur) * stat_weight[i]; } /* Compare current score against saved stats */ if (cur_score > best_score) { best_score = cur_score; for (i = 0; i < A_MAX; i++) { stat_save[i] = p_ptr->stat[i].cur; } } /* Break after 500 rolls */ if (auto_round >= last_round + 500) break; /* Take note every x rolls */ flag = (!(auto_round % AUTOROLLER_STEP)); /* Update display occasionally */ if (flag || (auto_round < last_round + 100)) { /* Put the stats (and percents) */ for (i = 0; i < A_MAX; i++) { /* Put the stat */ put_fstr(col + 13, 3 + i, CLR_L_GREEN "%v", stat_format, stat_use[i]); } /* Dump round */ put_fstr(col + 20, 10, "%10ld", auto_round); /* Make sure they see everything */ Term_fresh(); /* Delay 1/10 second */ if (flag) Term_xtra(TERM_XTRA_DELAY, 100); /* Do not wait for a key */ p_ptr->cmd.inkey_scan = TRUE; /* Check for a keypress */ if (inkey()) break; } } /* Load best stat set rolled */ for (i = 0; i < A_MAX; i++) { p_ptr->stat[i].cur = p_ptr->stat[i].max = stat_save[i]; } } /* Otherwise just get a character */ else { /* Get a new character */ get_stats(); } /* Flush input */ flush(); /*** Display ***/ /* Roll for base hitpoints */ get_extra(); /* Roll for age/height/weight */ get_ahw(); /* Dodgy "social class" */ p_ptr->rp.sc = randint1(100); /* Roll for gold */ get_money(); /* Hack -- get a chaos patron even if you are not a chaos warrior */ p_ptr->chaos_patron = (s16b)randint0(MAX_PATRON); p_ptr->muta1 = 0; p_ptr->muta2 = 0; p_ptr->muta3 = 0; /* Input loop */ while (TRUE) { /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP); /* Update stuff */ update_stuff(); /* Fully healed */ p_ptr->chp = p_ptr->mhp; /* Fully rested */ p_ptr->csp = p_ptr->msp; /* Display the player */ display_player(mode); /* Prepare a prompt (must squeeze everything in) */ prtf(2, 23, "['r' to reroll%s, 'n' for next screen, or Enter to accept]", previous ? ", 'p' for prev": ""); /* Prompt and get a command */ ch = inkey(); /* Quit */ if (ch == KTRL('X')) quit(NULL); /* Start over */ if (ch == ESCAPE) return (FALSE); /* Enter accepts the roll */ if ((ch == '\r') || (ch == '\n')) break; /* Reroll this character */ if ((ch == ' ') || (ch == 'r')) break; /* Previous character */ if (previous && (ch == 'p')) { load_prev_data(); continue; } /* Increase mode */ if (ch == 'n') { mode = (mode + 1) % DISPLAY_PLAYER_MAX; } /* Help */ if (ch == '?') { (void)show_file("birth.txt#CharDisplay", NULL, 0, 0); continue; } else if (ch == '=') { do_cmd_options(OPT_FLAG_BIRTH | OPT_FLAG_SERVER | OPT_FLAG_PLAYER); continue; } /* Warning */ bell("Illegal auto-roller command!"); } /* Are we done? */ if ((ch == '\r') || (ch == '\n')) break; /* Save this for the "previous" character */ save_prev_data(); /* Note that a previous roll exists */ previous = TRUE; } /* Clear prompt */ clear_from(23); /* Done */ return (TRUE); } static bool player_birth_aux(void) { char ch; int i; /* Ask questions */ if (!player_birth_aux_1()) return FALSE; /* Point based */ if (point_based) { if (!player_birth_aux_2()) return FALSE; } /* Auto-roll */ else { if (!player_birth_aux_3()) return FALSE; } /* Apply some randomness */ for (i = 0; i < A_MAX; i++) { p_ptr->stat[i].cur += (s16b) randint0(10); p_ptr->stat[i].max = p_ptr->stat[i].cur; } /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP); /* Update stuff */ update_stuff(); /* Get a name, prepare savefile */ get_character_name(); /* Initialize the virtues */ get_virtues(); /* Display the player */ display_player(DISPLAY_PLAYER_STANDARD); /* Prompt for it */ prtf(10, 23, "['Ctrl-X' to suicide, 'Del' to start over, or Enter to continue]"); /* Get a key */ ch = inkey(); /* Quit */ if (ch == KTRL('X')) quit(NULL); /* Start over */ if ((ch == 0x7F) || (ch == '.') || (ch == KTRL('H'))) return (FALSE); /* Accepted */ /* Done */ return (TRUE); } /* * Create a new character. * * Note that we may be called with "junk" leftover in the various * fields, so we must be sure to clear them first. */ void player_birth(void) { /* Create a new character */ while (1) { /* Wipe the player */ player_wipe(); /* Roll up a new character */ if (player_birth_aux()) break; } /* Create a note file if that option is set */ if (take_notes) { add_note_type(NOTE_BIRTH); } /* Note player birth in the message recall */ message_add(" ", MSG_GENERIC); message_add(" ", MSG_GENERIC); message_add("====================", MSG_GENERIC); message_add(" ", MSG_GENERIC); message_add(" ", MSG_GENERIC); /* Hack -- outfit the player */ player_outfit(); /* Set the message window flag as default */ if (!window_flag[1]) window_flag[1] |= PW_MESSAGE; /* Set the inv/equip window flag as default */ if (!window_flag[2]) window_flag[2] |= PW_INVEN; } zangband/src/bldg.c0000755000000000000000000011702710250356274013222 0ustar rootroot/* File: bldg.c */ /* * Purpose: Building commands * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3 * -KMW- * * Rewritten for Kangband 2.8.3i using Kamband's version of * bldg.c as written by Ivan Tkatchev * * Changed for ZAngband by Robert Ruehlmann * Rewritten yet again by Steven Fuerst to use the fields code. */ #include "angband.h" /* Hack - force exit from building */ static bool force_build_exit = FALSE; static void have_nightmare_aux(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; char m_name[80]; bool happened = FALSE; int power = r_ptr->hdice * 2 + 10; cptr desc = mon_race_name(r_ptr); int i; if (!FLAG(r_ptr, RF_UNIQUE)) { /* Describe it */ strnfmt(m_name, 80, "%s %s", (is_a_vowel(desc[0]) ? "an" : "a"), desc); if (FLAG(r_ptr, RF_FRIENDS)) { power -= 50; } } else { /* Describe it */ strnfmt(m_name, 80, "%s", desc); power += 50; } if (player_save(power)) { msgf("%^s chases you through your dreams.", m_name); /* Safe */ return; } if (p_ptr->tim.image) { /* Something silly happens... */ msgf("You behold the %s visage of %s!", funny_desc[randint0(MAX_SAN_FUNNY)], m_name); if (one_in_(3)) { msgf(funny_comments[randint0(MAX_SAN_COMMENT)]); (void) inc_image(randint1(r_ptr->hdice * 2)); } /* Never mind; we can't see it clearly enough */ return; } /* Something frightening happens... */ msgf("You behold the %s visage of %s!", horror_desc[randint0(MAX_SAN_HORROR)], desc); r_ptr->r_flags[3] |= RF3_ELDRITCH_HORROR; switch (p_ptr->rp.prace) { case RACE_IMP: { /* Imps may make a saving throw */ if (saving_throw(20 + p_ptr->lev)) return; break; } case RACE_SKELETON: case RACE_ZOMBIE: case RACE_SPECTRE: case RACE_VAMPIRE: case RACE_GHOUL: { /* Undead may make a saving throw */ if (saving_throw(10 + p_ptr->lev)) return; break; } } /* Mind blast */ if (!player_save(power)) { if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(4, 8)); } if (!(FLAG(p_ptr, TR_RES_CHAOS)) && one_in_(3)) { (void)inc_image(rand_range(250, 400)); } return; } /* Lose int & wis */ if (!player_save(power)) { (void)do_dec_stat(A_INT); (void)do_dec_stat(A_WIS); return; } /* Brain smash */ if (!player_save(power)) { if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(4, 8)); } if (!(FLAG(p_ptr, TR_FREE_ACT))) { (void)inc_paralyzed(rand_range(4, 8)); } for (i = 0; !player_save(power - i); i += 10) { (void)do_dec_stat(A_INT); (void)do_dec_stat(A_WIS); } if (!(FLAG(p_ptr, TR_RES_CHAOS))) { (void)inc_image(rand_range(250, 400)); } return; } /* Permanent lose int & wis */ if (!player_save(power)) { if (dec_stat(A_INT, 10, TRUE)) happened = TRUE; if (dec_stat(A_WIS, 10, TRUE)) happened = TRUE; if (happened) { msgf("You feel much less sane than before."); } return; } /* Amnesia */ if (!player_save(power)) { if (lose_all_info()) { msgf("You forget everything in your utmost terror!"); } return; } /* Else gain permanent insanity */ if ((p_ptr->muta3 & MUT3_MORONIC) && (p_ptr->muta2 & MUT2_BERS_RAGE) && ((p_ptr->muta2 & MUT2_COWARDICE) || (FLAG(p_ptr, TR_RES_FEAR))) && ((p_ptr->muta2 & MUT2_HALLU) || (FLAG(p_ptr, TR_RES_CHAOS)))) { /* The poor bastard already has all possible insanities! */ return; } while (!happened) { switch (randint1(4)) { case 1: { if (!(p_ptr->muta3 & MUT3_MORONIC)) { msgf("You turn into an utter moron!"); if (p_ptr->muta3 & MUT3_HYPER_INT) { msgf("Your brain is no longer a living computer."); p_ptr->muta3 &= ~(MUT3_HYPER_INT); } p_ptr->muta3 |= MUT3_MORONIC; happened = TRUE; } break; } case 2: { if (!(p_ptr->muta2 & MUT2_COWARDICE) && !(FLAG(p_ptr, TR_RES_FEAR))) { msgf("You become paranoid!"); /* Duh, the following should never happen, but anyway... */ if (p_ptr->muta3 & MUT3_FEARLESS) { msgf("You are no longer fearless."); p_ptr->muta3 &= ~(MUT3_FEARLESS); } p_ptr->muta2 |= MUT2_COWARDICE; happened = TRUE; } break; } case 3: { if (!(p_ptr->muta2 & MUT2_HALLU) && !(FLAG(p_ptr, TR_RES_CHAOS))) { msgf("You are afflicted by a hallucinatory insanity!"); p_ptr->muta2 |= MUT2_HALLU; happened = TRUE; } break; } default: { if (!(p_ptr->muta2 & MUT2_BERS_RAGE)) { msgf("You become subject to fits of berserk rage!"); p_ptr->muta2 |= MUT2_BERS_RAGE; happened = TRUE; } break; } } } p_ptr->update |= PU_BONUS; handle_stuff(); } /* * Wrapper function around the nightmare-making routine * so we make sure the monster summon list is restored. */ void have_nightmare(void) { /* Get a monster */ int r_idx = get_filter_mon_num(MAX_DEPTH, get_nightmare); /* Have some nightmares */ have_nightmare_aux(r_idx); } bool get_nightmare(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Require eldritch horrors */ if (!FLAG(r_ptr, RF_ELDRITCH_HORROR)) return (FALSE); /* Require high level */ if (r_ptr->hdice * 2 <= p_ptr->lev) return (FALSE); /* Accept this monster */ return (TRUE); } static void building_prt_gold(void) { prtf(40, 23, "Gold Remaining: %9ld", (long)p_ptr->au); } /* Does the player have enough gold for this action? */ bool test_gold(s32b cost) { if (p_ptr->au < cost) { /* Player does not have enough gold */ msgf("You need %ld gold to do this!", (long)cost); message_flush(); return (FALSE); } /* Player has enough gold */ return (TRUE); } static const store_type *build_ptr; /* Does this building have an outstanding quest */ bool build_has_quest(void) { if (lookup_quest_building(build_ptr)) return (TRUE); return (FALSE); } void build_cmd_quest(int level) { quest_type *q_ptr = lookup_quest_building(build_ptr); /* Do we already have a quest? */ if (q_ptr) { /* Give reward? */ reward_quest(q_ptr); } else { /* Make a new quest */ request_quest(build_ptr, level); } /* Display messages */ message_flush(); } /* Global that contains which building the player is in */ static field_type resize_f_ptr; /* * Display a building. */ void display_build(const field_type *f_ptr) { int factor; cptr build_name = field_name(f_ptr); cptr owner_name = quark_str(build_ptr->owner_name); /* remember for the resize */ resize_f_ptr = *f_ptr; /* The charisma factor */ factor = adj_chr_gold[p_ptr->stat[A_CHR].ind]; factor = ((factor + 200) * build_ptr->greed) / 400; Term_clear(); prtf(1, 2, "%s %s", owner_name, build_name); prtf(0, 19, "You may:"); /* Display building-specific information */ (void) field_script_const(f_ptr, FIELD_ACT_BUILD_ACT1, "i", LUA_VAR(factor)); prtf(0, 23, " ESC) Exit building"); /* Show your gold */ building_prt_gold(); } /* Display the building without argument, relies on display_build going first */ static void resize_build(void) { /* Get the argument from the global */ display_build(&resize_f_ptr); } /* * display fruit for dice slots */ static void display_fruit(int col, int row, int fruit) { switch (fruit) { case 0: { /* lemon */ put_fstr(col, row, CLR_YELLOW " ####.\n" " # #\n" " # #\n" "# #\n" "# #\n" "# # \n" "# # \n" ".#### \n" CLR_WHITE " Lemon "); break; } case 1: { /* orange */ put_fstr(col, row, CLR_ORANGE " ## \n" " #..# \n" " #....# \n" "#......#\n" "#......#\n" " #....# \n" " #..# \n" " ## \n" CLR_WHITE " Orange "); break; } case 2: { /* sword */ put_fstr(col, row, CLR_SLATE " /\\ \n" " ## \n" " ## \n" " ## \n" " ## \n" " ## \n" CLR_UMBER " ###### \n" CLR_UMBER " ## \n" CLR_WHITE " Sword "); break; } case 3: { /* shield */ put_fstr(col, row, CLR_SLATE " ###### \n" "# #\n" "# ++++ #\n" "# +==+ #\n" "# ++ #\n" " # # \n" " # # \n" " ## \n" CLR_WHITE " Shield "); break; } case 4: { /* plum */ put_fstr(col, row, CLR_VIOLET " ## \n" " ###### \n" "########\n" "########\n" "########\n" " ###### \n" " #### \n" " ## \n" CLR_WHITE " Plum "); break; } case 5: { /* cherry */ put_fstr(col, row, CLR_GREEN " ##\n" CLR_RED " ##" CLR_GREEN "# \n" CLR_RED " #..# \n" " #..# \n" " ###### \n" "#..##..#\n" "#..##..#\n" " ## ## \n" CLR_WHITE " Cherry "); break; } } } /* The amount of gold you have before gambling */ static s32b gamble_oldgold; /* * Initialize gambling by getting bet. * Return a wager of zero, if something goes wrong. * * Hack - we return with screen still saved if wager is non-zero */ static s32b gamble_init(void) { char out_val[160]; cptr p; s32b wager; s32b maxbet; /* Save gold before gambling */ gamble_oldgold = p_ptr->au; screen_save(); /* No money */ if (p_ptr->au < 1) { msgf("Hey! You don't have gold - get out of here!"); screen_load(); return (0); } clear_region(0, 5,23); /* Set maximum bet */ if (p_ptr->lev < 10) maxbet = p_ptr->lev * 100; else maxbet = p_ptr->lev * 1000; /* We can't bet more than we have */ maxbet = MIN(maxbet, p_ptr->au); /* Get the wager */ out_val[0] = 0; /* * Use get_string() because we may need more than * the s16b value returned by get_quantity(). */ if (!get_string(out_val, 33, "Your wager (1-%ld) ? ", maxbet)) { screen_load(); return (0); } /* Strip spaces */ for (p = out_val; *p == ' '; p++) ; /* Get the wager */ wager = atol(p); if (wager > p_ptr->au) { msgf("Hey! You don't have the gold - get out of here!"); screen_load(); return (0); } else if (wager > maxbet) { msgf("I'll take %ld gold of that. Keep the rest.", maxbet); wager = maxbet; } else if (wager < 1) { msgf("Ok, we'll start with 1 gold."); wager = 1; } message_flush(); prtf(2, 20, "Gold before game: %9ld\n" "Current Wager: %9ld", p_ptr->au, wager); /* Prevent savefile-scumming of the casino */ Rand_quick = TRUE; Rand_value = time(NULL); /* Return the amount of gold bet */ return (wager); } static bool gamble_again(bool win, int odds, s32b wager) { char again; if (win) { prtf(37, 16, "YOU WON\n" "Payoff: %ld\n" "Again(y/n)?", odds * wager); p_ptr->au += odds * wager; } else { prtf(37, 16, "You Lost\n" "\n" "Again(y/n)?"); p_ptr->au -= wager; } prtf(2, 22, "Current Gold: %9ld", p_ptr->au); Term_gotoxy(49, 18); again = inkey(); if ((again != 'y') && (again != 'Y')) return (FALSE); if (wager > p_ptr->au) { msgf("Hey! You don't have the gold - get out of here!"); message_flush(); /* Get out here */ return (FALSE); } /* The player wants another go */ return (TRUE); } /* * Finished gambling */ static void gamble_done(void) { /* Switch back to complex RNG */ Rand_quick = FALSE; prtf(37, 18, ""); if (p_ptr->au >= gamble_oldgold) msgf("You came out a winner! We'll win next time, I'm sure."); else msgf("You lost gold! Haha, better head home."); message_flush(); screen_load(); } void gamble_help(void) { /* Peruse the gambling help file */ (void)show_file("gambling.txt", NULL, 0, 0); } void gamble_in_between(void) { s32b wager = gamble_init(); int roll1, roll2, choice; bool win; /* Exit if the player is out of gold */ if (!wager) return; while (TRUE) { win = FALSE; put_fstr(2, 5, CLR_GREEN "In Between"); roll1 = randint1(10); roll2 = randint1(10); choice = randint1(10); put_fstr(3, 8, "Black die: %d Black Die: %d", roll1, roll2); put_fstr(14, 11, "Red die: %d", choice); if (((choice > roll1) && (choice < roll2)) || ((choice < roll1) && (choice > roll2))) win = TRUE; if (!gamble_again(win, 3, wager)) break; } gamble_done(); } void gamble_craps(void) { s32b wager = gamble_init(); int roll1, roll2, roll3, choice; bool win; /* Exit if the player is out of gold */ if (!wager) return; while (TRUE) { put_fstr(2, 5, CLR_GREEN "Craps"); /* Roll the dice */ roll1 = randint1(6); roll2 = randint1(6); roll3 = roll1 + roll2; choice = roll3; put_fstr(5, 7, "First roll: %d %d Total: %d", roll1, roll2, roll3); /* Is it is result straight away? */ if ((roll3 == 7) || (roll3 == 11)) win = TRUE; else if ((roll3 == 2) || (roll3 == 3) || (roll3 == 12)) win = FALSE; else while (TRUE) { /* Ok - we need to roll a few more times */ msgf("Hit any key to roll again"); message_flush(); roll1 = randint1(6); roll2 = randint1(6); roll3 = roll1 + roll2; prtf(5, 8, "Roll result: %d %d Total: %d", roll1, roll2, roll3); if (roll3 == choice) win = TRUE; else if (roll3 == 7) win = FALSE; else continue; /* We have a result */ break; } if (!gamble_again(win, 1, wager)) break; } gamble_done(); } void gamble_spin_wheel(void) { s32b wager = gamble_init(); int roll1, choice; bool win; /* Exit if the player is out of gold */ if (!wager) return; while (TRUE) { win = FALSE; put_fstr(2, 5, CLR_GREEN "Wheel"); prtf(5, 7, "1 2 3 4 5 6 7 8 9 10"); prtf(3, 8, "--------------------------------"); choice = get_quantity("Pick a number (1-10): ", 10); message_flush(); roll1 = randint1(10); prtf(3, 13, "The wheel spins to a stop and the winner is %d", roll1); clear_row(9); prtf((3 * roll1 + 2), 9, "*"); if (roll1 == choice) win = TRUE; if (!gamble_again(win, 8, wager)) break; } gamble_done(); } void gamble_dice_slots(void) { static const char *fruit[6] = { "Lemon", "Orange", "Sword", "Shield", "Plum", "Cherry" }; s32b wager = gamble_init(); int roll1, roll2, choice; int odds; bool win; /* Exit if the player is out of gold */ if (!wager) return; while (TRUE) { put_fstr(2, 5, CLR_GREEN "Dice Slots"); win = FALSE; odds = 0; /* Roll the dice */ roll1 = randint1(6); roll2 = randint1(6); choice = randint1(6); /* Show the result */ prtf(37, 15, "%s %s %s", fruit[roll1 - 1], fruit[roll2 - 1], fruit[choice - 1]); prtf(2, 7, "/--------------------------\\"); prtf(2, 17, "\\--------------------------/"); display_fruit(3, 8, roll1 - 1); display_fruit(12, 8, roll2 - 1); display_fruit(21, 8, choice - 1); /* What did we win? */ if ((roll1 == roll2) && (roll2 == choice)) { win = TRUE; if (roll1 == 1) odds = 4; else if (roll1 == 2) odds = 6; else odds = roll1 * roll1; } else if ((roll1 == 6) && (roll2 == 6)) { win = TRUE; odds = choice + 1; } if (!gamble_again(win, odds, wager)) break; } gamble_done(); } /* * The Inn * Note that resting for the night was a perfect way to avoid player * ghosts in the town *if* you could only make it to the inn in time (-: * Now that the ghosts are temporarily disabled in 2.8.X, this function * will not be that useful. * * (Although food is fairly hard to find elsewhere - so the Inn is quite * useful when you are hungry after crossing the wilderness.) * * Resting at night is also a quick way to restock stores -KMW- */ bool inn_rest(void) { /* Only at night time */ if ((turn % (10L * TOWN_DAWN)) < 50000) { msgf("The rooms are available only at night."); message_flush(); return (FALSE); } /* Hurt? */ if ((p_ptr->tim.poisoned) || (p_ptr->tim.cut)) { msgf("You need a healer, not a room."); message_flush(); msgf("Sorry, but don't want anyone dying in here."); message_flush(); return (FALSE); } /* Rest all night */ turn = ((turn / 50000) + 1) * 50000; p_ptr->chp = p_ptr->mhp; /* * Nightmare mode has a TY_CURSE at midnight... * and the player may want to avoid that. */ if (ironman_nightmare) { msgf("Horrible visions flit through your mind as you sleep."); /* Have some nightmares */ while (TRUE) { have_nightmare(); if (!one_in_(3)) break; } msgf("You awake screaming."); message_flush(); return (TRUE); } /* Normally heal the player */ (void)clear_blind(); (void)clear_confused(); p_ptr->tim.stun = 0; p_ptr->csp = p_ptr->msp; msgf("You awake refreshed for the new day."); message_flush(); return (TRUE); } /* * Calculate the probability of successful hit for a weapon and certain AC * * Only accurate for the current weapon, because it includes * player's +to_hit. */ static int hit_prob(int to_h, int ac) { int chance = p_ptr->skills[SKILL_THN] + (p_ptr->to_h + to_h) * BTH_PLUS_ADJ; int prob = 0; if (chance > 0 && ac < chance) prob = (100 * (chance - ac) / chance); return (5 + 95 * prob / 100); } #define WEP_MAST_COL1 2 #define WEP_MAST_COL2 45 /* Show how much damage a player does without a weapon */ static bool compare_weaponless(void) { int i; int intmaxdam = 0, intmindam = 10; if (p_ptr->rp.pclass != CLASS_MONK) { if (p_ptr->rp.prace == RACE_GHOUL) { /* Claw */ msgf("Maybe you will paralyze your foes with your claws."); } else { /* Just punching */ msgf("You can do little damage with your fists."); } /* Free of charge */ return (FALSE); } /* State the obvious */ msgf("Monks without a weapon use martial arts."); /* Need more room on the screen so clear a part of it */ if (p_ptr->lev >= 45) clear_region(0, 15, 21); /* Show the list of attacks */ put_fstr(WEP_MAST_COL1, 4, CLR_L_BLUE "Possible Attacks:"); for (i = 0; i < MAX_MA && ma_blows[i].min_level < p_ptr->lev; i++) { const martial_arts *ma_ptr = &ma_blows[i]; intmaxdam = ma_ptr->dd * ma_ptr->ds; intmindam = MIN(intmindam, ma_ptr->dd); /* Show a line from the table */ put_fstr(WEP_MAST_COL1, i + 5, "%s (%dd%d)", format(ma_ptr->desc, "a monster"), ma_ptr->dd, ma_ptr->ds); } put_fstr(WEP_MAST_COL2, 4, CLR_L_BLUE "Possible Damage:"); /* Damage for one blow (if it hits) */ put_fstr(WEP_MAST_COL2, 5, "One Strike: %d-%d damage", intmindam, intmaxdam); /* The whole attack */ intmindam *= p_ptr->num_blow; intmaxdam *= p_ptr->num_blow; /* Damage for the complete attack (if all blows hit) */ put_fstr(WEP_MAST_COL2, 6, "One Attack: %d-%d damage", intmindam, intmaxdam); /* Print hit probabilities */ put_fstr(WEP_MAST_COL2, 8, "Enemy AC: Low Medium High \n" "Hit Prob: %2d%% %2d%% %2d%% %2d%% %2d%%", hit_prob(0, 25), hit_prob(0, 50), hit_prob(0, 75), hit_prob(0, 100), hit_prob(0, 200)); /* This info costs money */ return (TRUE); } /* * Display the damage figure of an object * (used by compare_weapon_aux1) * * Only accurate for the current weapon, because it includes * the current +dam of the player. */ static void compare_weapon_aux2(const object_type *o_ptr, int numblows, int r, cptr attr, byte slay) { long maxdam, mindam; int dambonus; int intmaxdam, intmindam; dambonus = o_ptr->to_d + p_ptr->to_d; /* Include effects of slaying bonus */ mindam = o_ptr->dd * slay * 10; maxdam = (o_ptr->dd * o_ptr->ds * deadliness_calc(dambonus) * slay) / 10; /* Include effects of slaying bonus */ mindam += (slay - 10) * 100; maxdam += (slay - 10) * 100; /* Number of blows */ maxdam *= numblows; mindam *= numblows; /* rescale */ intmaxdam = maxdam / 100; intmindam = mindam / 100; /* Print the intro text */ put_fstr(WEP_MAST_COL2, r, attr); /* Print the damage */ put_fstr(WEP_MAST_COL2 + 8, r, " %d-%d damage", intmindam, intmaxdam); } /* * Show the damage figures for the various monster types * * Only accurate for the current weapon, because it includes * the current number of blows for the player. */ static void compare_weapon_aux1(const object_type *o_ptr) { int r = 10; /* Print the relevant lines */ if (FLAG(o_ptr, TR_SLAY_ANIMAL)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Animals:", 17); } if (FLAG(o_ptr, TR_SLAY_EVIL)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Evil:", 15); } if (FLAG(o_ptr, TR_SLAY_UNDEAD)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Undead:", 20); } if (FLAG(o_ptr, TR_SLAY_DEMON)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Demons:", 20); } if (FLAG(o_ptr, TR_SLAY_ORC)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Orcs:", 20); } if (FLAG(o_ptr, TR_SLAY_TROLL)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Trolls:", 20); } if (FLAG(o_ptr, TR_SLAY_GIANT)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Giants:", 20); } if (FLAG(o_ptr, TR_SLAY_DRAGON)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Dragons:", 20); } if (FLAG(o_ptr, TR_KILL_DRAGON)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_YELLOW "Dragons:", 30); } if (FLAG(o_ptr, TR_BRAND_ACID)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_RED "Acid:", 20); } if (FLAG(o_ptr, TR_BRAND_ELEC)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_RED "Elec:", 20); } if (FLAG(o_ptr, TR_BRAND_FIRE)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_RED "Fire:", 20); } if (FLAG(o_ptr, TR_BRAND_COLD)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_RED "Cold:", 20); } if (FLAG(o_ptr, TR_BRAND_POIS)) { compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, CLR_RED "Poison:", 20); } } /* * Calculate the probability of critical hit for a weapon * * Only accurate for the current weapon, because it includes * player's +to_hit. */ static int critical_prob(int to_h, int number) { int chance = p_ptr->skills[SKILL_THN] + (p_ptr->to_h + to_h) * BTH_PLUS_ADJ; if (chance <= 0) return (0); /* Chance we make a critical */ chance = (chance * 100) / (chance + 240); /* Which critical do we want? */ switch (number) { case 0: { /* No critical */ return (100 - chance); } case 1: { /* x 1.5 */ chance = ((chance * 89) / 90); chance = ((chance * 39) / 40); chance = ((chance * 11) / 12); return (2 * chance / 3); } case 2: { /* x 2.0 */ chance = ((chance * 89) / 90); chance = ((chance * 39) / 40); chance = ((chance * 11) / 12); return (chance / 3); } case 3: { /* x 2.7 */ chance = ((chance * 89) / 90); chance = ((chance * 39) / 40); return (chance / 12); } case 4: { /* x 3.6 */ chance = ((chance * 89) / 90); return (chance / 40); } case 5: { /* x 5.0 */ return (chance / 90); } } /* Paranoia */ return (0); } /* * Displays all info about a weapon * * Only accurate for the current weapon, because it includes * various info about the player's +to_dam and number of blows. */ static void list_weapon(const object_type *o_ptr) { int dambonus; int intmaxdam, intmindam; /* Print the weapon name */ put_fstr(WEP_MAST_COL1, 6, CLR_L_BLUE "%v", OBJECT_FMT(o_ptr, TRUE, 0)); /* Print to_hit and to_dam of the weapon */ put_fstr(WEP_MAST_COL1, 8, "To Hit: %d Deadliness: %d", o_ptr->to_h, o_ptr->to_d); /* Print the weapons base damage dice and blows */ put_fstr(WEP_MAST_COL1, 10, "Dice: %dd%d Number of Blows: %d", (int)o_ptr->dd, (int)o_ptr->ds, p_ptr->num_blow); /* Print hit probabilities */ put_fstr(WEP_MAST_COL1, 12, "Enemy AC: Low Medium High \n" "Hit Prob: %2d%% %2d%% %2d%% %2d%% %2d%%", hit_prob(o_ptr->to_h, 25), hit_prob(o_ptr->to_h, 50), hit_prob(o_ptr->to_h, 75), hit_prob(o_ptr->to_h, 100), hit_prob(o_ptr->to_h, 200)); /* Print critical hit probabilities */ put_fstr(WEP_MAST_COL1, 15, CLR_RED "Critical:" CLR_WHITE " 1.0 1.5 2.0 2.7 3.6 5.0\n" " %2d%% %2d%% %2d%% %2d%% %2d%% %2d%%", critical_prob(o_ptr->to_h, 0), critical_prob(o_ptr->to_h, 1), critical_prob(o_ptr->to_h, 2), critical_prob(o_ptr->to_h, 3), critical_prob(o_ptr->to_h, 4), critical_prob(o_ptr->to_h, 5)); put_fstr(WEP_MAST_COL2, 6, CLR_L_BLUE "Possible Damage:"); dambonus = o_ptr->to_d + p_ptr->to_d; /* Calculate max and min damage */ intmindam = o_ptr->dd; intmaxdam = (o_ptr->dd * o_ptr->ds * deadliness_calc(dambonus)) / 100; /* Damage for one blow (if it hits) */ put_fstr(WEP_MAST_COL2, 7, "One Strike: %d-%d damage", intmindam, intmaxdam); /* Rescale */ intmindam *= p_ptr->num_blow; intmaxdam *= p_ptr->num_blow; /* Damage for the complete attack (if all blows hit) */ put_fstr(WEP_MAST_COL2, 8, "One Attack: %d-%d damage", intmindam, intmaxdam); } /* * Compare weapons * * Copies the weapons to compare into the weapon-slot and * compares the values for both weapons. */ bool compare_weapons(void) { object_type *o_ptr; bool result = TRUE;; /* Clear the screen */ clear_region(0, 6, 18); /* Point to wielded weapon */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Check to see if we have one */ if (o_ptr->k_idx) { /* Identify the weapon */ identify_item(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; /* Erase the "feeling" */ o_ptr->feeling = FEEL_NONE; /* List the new values */ list_weapon(o_ptr); compare_weapon_aux1(o_ptr); put_fstr(0, 19, "(Only highest damage applies per monster. Special damage not cumulative.)"); msgf("Based on your current abilities, here is what your weapon will do:"); } else { /* Bare hands */ result = compare_weaponless(); } /* Give the player a chance to see it all */ if (auto_more) (void)inkey(); /* Done */ return (result); } /* * Enchant item */ bool enchant_item(s32b cost, bool to_hit, bool to_dam, bool to_ac, bool weap) { bool okay = FALSE; object_type *o_ptr; cptr q, s; int maxenchant = (p_ptr->lev / 5); int maxenchant_d = (p_ptr->lev / 3); if (weap) { /* Select weapons */ item_tester_hook = item_tester_hook_melee_weapon; } else { /* Select armour */ item_tester_hook = item_tester_hook_armour; } /* Get an item */ q = "Improve which item? "; s = "You have nothing to improve."; /* Get the item */ o_ptr = get_item(q, s, (USE_INVEN | USE_EQUIP)); /* No valid items */ if (!o_ptr) return (FALSE); /* Check if the player has enough money */ if (p_ptr->au < (cost * o_ptr->number)) { msgf("You do not have the gold to improve %v!", OBJECT_FMT(o_ptr, TRUE, 0)); message_flush(); return (FALSE); } /* Note that enchanting something a negative number of times will fail */ /* Enchant to hit */ if ((to_hit) && (enchant(o_ptr, maxenchant - o_ptr->to_h, (ENCH_TOHIT | ENCH_FORCE)))) { okay = TRUE; } /* Enchant to damage */ if ((to_dam) && (enchant(o_ptr, maxenchant_d - o_ptr->to_d, (ENCH_TODAM | ENCH_FORCE)))) { okay = TRUE; } /* Enchant to AC */ if ((to_ac) && (enchant(o_ptr, maxenchant - o_ptr->to_a, (ENCH_TOAC | ENCH_FORCE)))) { okay = TRUE; } /* Failure */ if (!okay) { /* Flush */ if (flush_failure) flush(); /* Message */ msgf("The improvement failed."); return (FALSE); } else { msgf("Improved %v for %d gold.", OBJECT_FMT(o_ptr, TRUE, 1), cost * o_ptr->number); message_flush(); /* Charge the money */ p_ptr->au -= (cost * o_ptr->number); /* Something happened */ return (TRUE); } } /* * Recharge rods, wands and staves * * The player can select the number of charges to add * (up to a limit), and the recharge never fails. * * The cost for rods depends on the level of the rod. The prices * for recharging wands and staves are dependent on the cost of * the base-item. */ void building_recharge(s32b cost) { int lev; object_type *o_ptr; object_kind *k_ptr; cptr q, s; int price; int charges; int max_charges; /* Only accept legal items */ item_tester_hook = item_tester_hook_recharge; /* Get an item */ q = "Recharge which item? "; s = "You have nothing to recharge."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* No valid item */ if (!o_ptr) return; k_ptr = &k_info[o_ptr->k_idx]; /* * We don't want to give the player free info about * the level of the item or the number of charges. */ /* The item must be "known" */ if (!object_known_p(o_ptr)) { msgf("The item must be identified first!"); message_flush(); if ((p_ptr->au >= 50) && get_check("Identify for 50 gold? ")) { /* Pay the price */ p_ptr->au -= 50; /* Identify it */ identify_item(o_ptr); /* Description */ msgf("You have: %v.", OBJECT_FMT(o_ptr, TRUE, 3)); } else { return; } } /* Extract the object "level" */ lev = get_object_level(o_ptr); /* Price for a rod */ if (o_ptr->tval == TV_ROD) { if (o_ptr->timeout > 0) { /* Fully recharge */ price = (lev * o_ptr->timeout) / k_ptr->pval; } else { /* No recharge necessary */ msgf("That doesn't need to be recharged."); message_flush(); return; } } else if (o_ptr->tval == TV_STAFF) { /* * Price per charge ( = double the price paid * by shopkeepers for the charge) */ price = (o_ptr->cost / 10) * o_ptr->number; /* Pay at least 10 gold per charge */ price = MAX(10, price); } else { /* * Price per charge ( = double the price paid * by shopkeepers for the charge) */ price = (o_ptr->cost / 10); /* Pay at least 10 gold per charge */ price = MAX(10, price); } /* Limit the number of charges for wands and staves */ if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)) && (o_ptr->pval / o_ptr->number >= k_ptr->pval)) { if ((o_ptr->tval == TV_WAND) && (o_ptr->number == 1)) msgf("This wand is already fully charged."); else if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1)) msgf("These wands are already fully charged."); else if ((o_ptr->tval == TV_STAFF) && (o_ptr->number == 1)) msgf("This staff is already fully charged."); else if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) msgf("These staffs are already fully charged."); message_flush(); return; } /* Factor in shopkeeper greed */ price = (price * cost / 100); /* Check if the player has enough money */ if (p_ptr->au < price) { msgf("You need %d gold to recharge %v!", price, OBJECT_FMT(o_ptr, TRUE, 0)); message_flush(); return; } if (o_ptr->tval == TV_ROD) { if (get_check("Recharge the %s for %d gold? ", ((o_ptr->number > 1) ? "rods" : "rod"), price)) { /* Recharge fully */ o_ptr->timeout = 0; } else { return; } } else { char buf[160]; if (o_ptr->tval == TV_STAFF) max_charges = k_ptr->pval - o_ptr->pval; else max_charges = o_ptr->number * k_ptr->pval - o_ptr->pval; /* Get prompt */ strnfmt(buf, 160, "Add how many charges for %d gold? ", price); /* Get the quantity for staves and wands */ charges = get_quantity(buf, MIN(p_ptr->au / price, max_charges)); /* Do nothing */ if (charges < 1) return; /* Get the new price */ price *= charges; /* Recharge */ o_ptr->pval += charges; /* Hack - no "used" charges */ o_ptr->ac = 0; /* We no longer think the item is empty */ o_ptr->info &= ~(OB_EMPTY); } /* Give feedback */ msgf("%^v %s recharged for %d gold.", OBJECT_FMT(o_ptr, TRUE, 3), ((o_ptr->number > 1) ? "were" : "was"), price); message_flush(); /* Notice changes */ notice_inven(); /* Pay the price */ p_ptr->au -= price; /* Finished */ return; } bool building_healer(void) { bool paid = FALSE; if (do_res_stat(A_STR)) paid = TRUE; if (do_res_stat(A_INT)) paid = TRUE; if (do_res_stat(A_WIS)) paid = TRUE; if (do_res_stat(A_DEX)) paid = TRUE; if (do_res_stat(A_CON)) paid = TRUE; if (do_res_stat(A_CHR)) paid = TRUE; if (paid) { msgf("You are infused with magic, and your ailments disappear."); message_flush(); } return (paid); } static int collect_magetower_links(int n, int *link_p, int *link_w, s32b *cost, int factor) { place_type *pl_ptr = &place[p_ptr->place_num]; int i, j; int max_link = 0; /* Get current town location */ int x = pl_ptr->x, y = pl_ptr->y; /* Find the magetowers we're linked to */ for (i = 0; i < place_count; i++) { place_type *pl_ptr = &place[i]; /* Skip current town */ if (i == p_ptr->place_num) continue; for (j = 0; j < pl_ptr->numstores; j++) { store_type *st_ptr = &pl_ptr->store[j]; if (max_link >= n) return (max_link); /* Hack - only allow teleportation to known magetowers */ if (!st_ptr->data) continue; /* Is it a mage tower? */ if ((st_ptr->type == BUILD_MAGETOWER0) || (st_ptr->type == BUILD_MAGETOWER1)) { link_p[max_link] = i; link_w[max_link] = j; cost[max_link] = distance(x, y, pl_ptr->x, pl_ptr->y) * factor; max_link++; /* Only collect 1 link per city */ break; } } } return max_link; } /* * Record that the player has visited a magetower * and paid for the privelidge of being able to * teleport there from another magetower */ void record_aura(void) { store_type *b_ptr = get_current_store(); if (!b_ptr->data) { /* * Hack XXX - save the fact we have "noticed" this tower * in this variable, which later will have to be removed * from store_type anyway. */ b_ptr->data = 1; msgf("The portal keeper notes your aura."); } } bool building_magetower(int factor, bool display) { int link_p[24], link_w[24]; int max_link = 0; int i; s32b cost[24]; char out_val[160]; /* Collect links */ max_link = collect_magetower_links(24, link_p, link_w, cost, factor); if (display) { /* Haven't been here before? */ if (!build_ptr->data) { put_fstr(35, 18, CLR_YELLOW " R) Record aura (%dgp)", factor * 5); } put_fstr(35, 19, CLR_YELLOW " T) Teleport"); for (i = 0; i < max_link; i++) { int row = i % 12 + 4; int col = (i / 12) * 40; /* Label it, clear the line --(-- */ prtf(col, row, "%c) ", I2A(i)); /* Print place name */ prtf(col + 3, row, place[link_p[i]].name); /* Print cost */ prtf(col + 30, row, "%ld au", (long)cost[i]); } } else { char command; if (max_link == 0) { msgf("You do not know any other towns to teleport to."); return (FALSE); } /* Build the prompt */ strnfmt(out_val, 160, "(Towns %c-%c, ESC to exit)", I2A(0), I2A(max_link - 1)); while (TRUE) { int k; /* Escape */ if (!get_com(out_val, &command)) break; k = (islower(command) ? A2I(command) : -1); if ((k >= 0) && (k < max_link) && test_gold(cost[k])) { place_type *pl_ptr2 = &place[link_p[k]]; store_type *st_ptr2 = &pl_ptr2->store[link_w[k]]; /* Subtract off cost */ p_ptr->au -= cost[k]; /* Move the player */ p_ptr->px = pl_ptr2->x * 16 + st_ptr2->x; p_ptr->py = pl_ptr2->y * 16 + st_ptr2->y; p_ptr->wilderness_x = p_ptr->px; p_ptr->wilderness_y = p_ptr->py; /* Notice player location */ Term_move_player(); /* Remove all monster lights */ lite_n = 0; /* Notice the move */ move_wild(); /* Check for new panel (redraw map) */ verify_panel(); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); /* Update the monsters */ p_ptr->update |= (PU_DISTANCE); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); force_build_exit = TRUE; return (TRUE); } /* Oops */ bell("Illegal choice!"); } } return (FALSE); } static bool process_build_hook(const field_type *f_ptr) { int factor; char command[2]; bool done = FALSE; /* The charisma factor */ factor = adj_chr_gold[p_ptr->stat[A_CHR].ind]; factor = ((factor + 200) * build_ptr->greed) / 400; /* Hack - lua expects a string instead of a character */ command[0] = (byte) p_ptr->cmd.cmd; command[1] = '\0'; field_script_const(f_ptr, FIELD_ACT_BUILD_ACT2, "is:b", LUA_VAR(factor), LUA_VAR(command), LUA_RETURN(done)); /* Redraw screen */ display_build(f_ptr); /* Did we do anything? */ return (done); } /* * Process a command in a building * * Note that we must disable some commands which are allowed * in the dungeon / stores but not in the buildings, to prevent chaos, * and also to give more free keys in order to have building * specific commands. * * Hack - we buypass macros / keymaps to prevent silliness when * people use the roguelike keyset and press a 'direction' key * which also corresponds to a building command. */ static bool build_process_command(const field_type *f_ptr) { /* Hack - Get a command */ p_ptr->cmd.cmd = inkey(); /* Handle repeating the last command */ repeat_check(); /* Process the building-specific commands */ if (process_build_hook(f_ptr)) return (FALSE); /* Parse the command */ switch (p_ptr->cmd.cmd) { case ESCAPE: { /* Leave */ return (TRUE); } case KTRL('R'): { /* Redraw */ do_cmd_redraw(); display_build(f_ptr); break; } case ' ': case '\r': { /* Ignore return */ break; } default: { /* Is it a standard command? */ if (!do_standard_command(p_ptr->cmd.cmd)) { /* Hack -- Unknown command */ msgf("That command does not work in buildings."); } else { /* Why this is neeed so often I don't know */ message_flush(); } break; } } return (FALSE); } /* * Do building commands */ void do_cmd_bldg(const field_type *f_ptr) { store_type *b_ptr; bool leave_build = FALSE; /* Disturb */ disturb(FALSE); b_ptr = get_current_store(); /* Paranoia */ if (!b_ptr) return; /* Save building pointer for lua hook */ build_ptr = b_ptr; /* Init building if required */ field_script_const(f_ptr, FIELD_ACT_SB_INIT, ""); /* Some quests are finished by finding a building */ trigger_quest_complete(QX_FIND_SHOP, (vptr)b_ptr); /* Forget the view */ forget_view(); /* Hack -- Increase "icky" depth */ screen_save(); /* No command argument */ p_ptr->cmd.arg = 0; /* No repeated command */ p_ptr->cmd.rep = 0; /* No automatic command */ p_ptr->cmd.new = 0; /* Display the building */ display_build(f_ptr); /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = resize_build; /* Interact with player */ while (!leave_build) { /* Clear */ clear_from(21); /* Basic commands */ prtf(0, 23, " ESC) Exit building"); /* Show your gold */ building_prt_gold(); /* Process the command */ leave_build = build_process_command(f_ptr); if (force_build_exit) { force_build_exit = FALSE; break; } /* Notice stuff */ notice_stuff(); /* Handle stuff */ handle_stuff(); message_flush(); } /* Free turn XXX XXX XXX */ p_ptr->state.energy_use = 0; /* Hack -- Character is no longer in "icky" mode */ screen_load(); /* Hack -- Cancel automatic command */ p_ptr->cmd.new = 0; /* Flush messages XXX XXX XXX */ message_flush(); /* Hack - reset the redraw hook */ angband_term[0]->resize_hook = resize_map; /* Clear the screen */ Term_clear(); /* Update for the changed screen size */ resize_map(); /* Update everything */ p_ptr->update |= (PU_VIEW); p_ptr->update |= (PU_MONSTERS); /* Redraw entire screen */ p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_EQUIPPY); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* * Initialize a building */ void build_init(int town_num, int build_num, byte build_type) { cptr own_name = owner_names[randint0(owner_names_max)]; cptr own_suffix = owner_suffix[randint0(owner_suffix_max)]; /* Activate that building */ store_type *st_ptr = &place[town_num].store[build_num]; /* Pick an owner */ st_ptr->owner_name = quark_fmt("%s %s", own_name, own_suffix); /* These are set in place_sb() via lua hooks */ st_ptr->greed = 0; st_ptr->max_cost = 0; /* Set the type */ st_ptr->type = build_type; /* Initialize */ st_ptr->data = 0; st_ptr->last_visit = 0; } zangband/src/cave.c0000755000000000000000000022624710250356274013235 0ustar rootroot/* File: cave.c */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* Purpose: low level dungeon routines -BEN- */ #include "angband.h" /* * Support for Adam Bolt's tileset, lighting and transparency effects * by Robert Ruehlmann (rr9@angband.org) */ /* * Maximum number of slopes in a single octant */ #define VINFO_MAX_SLOPES 135 /* * Table of data used to calculate projections / los / shots. */ static project_type *project_data[VINFO_MAX_SLOPES]; /* Number of squares per slope */ static int slope_count[VINFO_MAX_SLOPES]; /* The min and max slopes for each square in sight */ static int p_slope_min[MAX_SIGHT + 1][MAX_SIGHT + 1]; static int p_slope_max[MAX_SIGHT + 1][MAX_SIGHT + 1]; /* * Distance between two points via Newton-Raphson technique */ int distance(int x1, int y1, int x2, int y2) { int dy = ABS(y2 - y1); int dx = ABS(x2 - x1); /* Simple case */ if (!dy) return dx; if (!dx) return dy; /* Group */ { /* Squared distance */ int target = (dy * dy) + (dx * dx); /* Approximate distance: hypot(dy,dx) ~= max(dy,dx) + min(dy,dx) / 2 */ int d = (dy > dx) ? (dy + dx / 2) : (dx + dy / 2); while (1) { /* Approximate error */ int err = (target - d * d) / (2 * d); /* No error - we are done */ if (!err) break; /* Adjust distance */ d += err; } /* Return the distance */ return d; } } /* * Return TRUE if the given square contains a building */ bool is_build(const cave_type *c_ptr) { /* We assume field_is_type does not alter the data in c_ptr */ return (field_is_type(c_ptr, FTYPE_BUILD) != 0); } /* * Return TRUE if the given square contains a trap */ bool is_trap(const cave_type *c_ptr) { /* We assume field_is_type does not alter the data in c_ptr */ return (field_is_type(c_ptr, FTYPE_TRAP) != 0); } /* * Return TRUE if the given square contains a known trap */ bool is_visible_trap(const cave_type *c_ptr) { /* We assume field_first_known does not alter the data in c_ptr */ return (field_first_known(c_ptr, FTYPE_TRAP) != 0); } /* * This is a version of the los function that uses a * tester function to determine whether or not to stop. * * Use this function instead of cut+pasting los() everywhere * with only tiny changes made to it. * * This works by following a "ray" that is one of those used * in the update_view() routine. * * We pick the minimal sloped ray that passes through the * required square. We then follow that ray, looking at each * grid along it. If the grid passes c_hook() then we keep * going. If the grid does not, then we go to the minimal * slope that does not pass through this blocking grid. * We go to the first unchecked square along that ray - and * then continue following it. * * If the new ray does not pass through the target square, then * its slope will be greater than the maximal slope of the * target. * * This routine will over-check some squares in a worst-case * scenario - but it is fairly efficient. Most of the required * information has been pre-calculated in the code that also * works out the data used by update_view() * * Unlike the old los() routine, this will give exactly the * same results as testing cave_view_grid() after using * update_view(). */ static bool los_general(int x1, int y1, int x2, int y2, cave_hook_type c_hook) { int i, j, temp, dist; int x, y; int dx, dy, ax, ay, sx, sy; cave_type *c_ptr; dist = distance(x1, y1, x2, y2); /* If (x1,y1) == (x2, y2) we know we can see ourselves */ if (dist == 0) return (TRUE); /* We only work for points that are less than MAX_SIGHT appart. */ if (dist > MAX_SIGHT) return (FALSE); /* Extract the offset */ dy = y2 - y1; dx = x2 - x1; /* Extract the absolute offset */ ay = ABS(dy); ax = ABS(dx); /* * Start at the first square in the list. * This is a square adjacent to (x1,y1) */ j = 0; /* Extract some signs */ sx = (dx < 0) ? -1 : 1; sy = (dy < 0) ? -1 : 1; /* Hack - we need to stick to one octant */ if (ay < ax) { /* Look up the slope to use */ i = p_slope_min[ax][ay]; while (i <= p_slope_max[ax][ay]) { x = x1 + sx * project_data[i][j].x; y = y1 + sy * project_data[i][j].y; /* Done? */ if ((x == x2) && (y == y2)) return (TRUE); /* Stop if out of bounds */ if (!in_bounds2(x, y)) return (FALSE); c_ptr = area(x, y); if ((*c_hook) (c_ptr)) { /* Blocked: go to the best position we have not looked at yet */ temp = project_data[i][j].slope; j = project_data[i][j].square; i = temp; } else { /* Advance along ray */ j++; } } } else { /* Look up the slope to use */ i = p_slope_min[ay][ax]; while (i <= p_slope_max[ay][ax]) { /* Note that the data offsets have x,y swapped */ x = x1 + sx * project_data[i][j].y; y = y1 + sy * project_data[i][j].x; /* Done? */ if ((x == x2) && (y == y2)) return (TRUE); /* Stop if out of bounds */ if (!in_bounds2(x, y)) return (FALSE); c_ptr = area(x, y); if ((*c_hook) (c_ptr)) { /* Blocked: go to the best position we have not looked at yet */ temp = project_data[i][j].slope; j = project_data[i][j].square; i = temp; } else { /* Advance along ray */ j++; } } } /* No path */ return (FALSE); } /* * Hack - a function to pass to los_general() used * to simulate the old los() */ static bool cave_stop_wall(const cave_type *c_ptr) { /* Is it passable? */ if (cave_los_grid(c_ptr)) return (FALSE); /* Seems ok */ return (TRUE); } /* * Slow, but simple LOS routine. This works in the same way as * the view code, so that if something is in view, los() behaves * as expected. * * The old routine was fast, but did not behave in the right way. * This new routine does not need to be fast, because it isn't * called in time-critical code. * * * It works by trying all slopes that connect (x1,y1) with (x2,y2) * If a wall is found, then it back-tracks to the 'best' square * to check next. There may be cases where it checks the same * square multiple times, but a simple algorithm is much cleaner. * * Note that "line of sight" is not "reflexive" in all cases. * * Use the "projectable()" routine to test "spell/missile line of sight". * * Use the "update_view()" function to determine player line-of-sight. */ bool los(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, cave_stop_wall)); } /* Slope and square used by mmove2 */ static int mmove_slope; static int mmove_sq; /* Direction to move in */ static int mmove_dx; static int mmove_dy; /* * This is a (slow) function that can be used * to test if there is a direct projection * between two squares. * * "Direct" projections tend to look much straighter * on the screen than normal ones. */ static bool is_direct_projectable(int x1, int y1) { int xx, yy; int ax, ay, sx, sy; int slope = 0, sq = 0; cave_type *c_ptr; /* Extract the absolute offset */ ay = ABS(mmove_dy); ax = ABS(mmove_dx); /* Extract some signs */ sx = (mmove_dx < 0) ? -1 : 1; sy = (mmove_dy < 0) ? -1 : 1; /* * Start at the first square in the list. * This is a square adjacent to (x1,y1) */ /* Hack - we need to stick to one octant */ if (ay < ax) { /* Look up the slope to use */ slope = (p_slope_min[ax][ay] + p_slope_max[ax][ay]) / 2; while (TRUE) { xx = x1 + sx * project_data[slope][sq].x; yy = y1 + sy * project_data[slope][sq].y; /* Done? */ if ((xx == x1 + mmove_dx) && (yy == y1 + mmove_dy)) return (TRUE); c_ptr = area(xx, yy); /* Is the square not occupied by a monster, and passable? */ if (!cave_los_grid(c_ptr) || c_ptr->m_idx) { return (FALSE); } else { /* Advance along ray */ sq++; } } } else { /* Look up the slope to use */ slope = (p_slope_min[ay][ax] + p_slope_max[ay][ax]) / 2; while (TRUE) { /* Note that the data offsets have x,y swapped */ xx = x1 + sx * project_data[slope][sq].y; yy = y1 + sy * project_data[slope][sq].x; /* Done? */ if ((xx == x1 + mmove_dx) && (yy == y1 + mmove_dy)) return (TRUE); c_ptr = area(xx, yy); /* Is the square not occupied by a monster, and passable? */ if (!cave_los_grid(c_ptr) || c_ptr->m_idx) { return (FALSE); } else { /* Advance along ray */ sq++; } } } } /* * Calculate the slope and square information used by * a following mmove2 */ void mmove_init(int x1, int y1, int x2, int y2) { int temp; int xx, yy; int dx, dy, ax, ay, sx, sy, dist; cave_type *c_ptr; bool is_projectable; /* Clear slope and square */ mmove_slope = 0; mmove_sq = 0; /* Clear direction */ mmove_dx = 0; mmove_dy = 0; /* Paranoia - degenerate case */ if ((x1 == x2) && (y1 == y2)) return; /* Extract the offset */ dy = y2 - y1; dx = x2 - x1; /* * We only work for points that are less than MAX_SIGHT appart. * Note that MAX_RANGE < MAX_SIGHT */ dist = distance(x1, y1, x2, y2); if (dist > MAX_SIGHT) { /* Rescale */ dx = (dx * MAX_SIGHT) / dist; dy = (dy * MAX_SIGHT) / dist; } /* Save direction */ mmove_dx = dx; mmove_dy = dy; /* Extract the absolute offset */ ay = ABS(dy); ax = ABS(dx); /* Extract some signs */ sx = (dx < 0) ? -1 : 1; sy = (dy < 0) ? -1 : 1; /* Is the square projectable from here? */ is_projectable = projectable(x1, y1, x2, y2); /* * Start at the first square in the list. * This is a square adjacent to (x1,y1) */ /* Hack - we need to stick to one octant */ if (ay < ax) { /* Is there a direct line to the target? */ if (is_direct_projectable(x1, y1)) { /* Set the direct route */ mmove_slope = (p_slope_min[ax][ay] + p_slope_max[ax][ay]) / 2; mmove_sq = 0; /* Done */ return; } /* Look up the slope to use */ mmove_slope = p_slope_min[ax][ay]; while (mmove_slope <= p_slope_max[ax][ay]) { xx = x1 + sx * project_data[mmove_slope][mmove_sq].x; yy = y1 + sy * project_data[mmove_slope][mmove_sq].y; /* Done? */ if ((xx == x1 + dx) && (yy == y1 + dy)) break; c_ptr = area(xx, yy); /* Do we want to stop early? */ if (!is_projectable && c_ptr->m_idx) break; /* Is the square not occupied by a monster, and passable? */ if (!cave_los_grid(c_ptr) || c_ptr->m_idx) { /* Advance to the best position we have not looked at yet */ temp = project_data[mmove_slope][mmove_sq].slope; mmove_sq = project_data[mmove_slope][mmove_sq].square; mmove_slope = temp; } else { /* Advance along ray */ mmove_sq++; } } /* No match? */ if (mmove_slope > p_slope_max[ax][ay]) { mmove_slope = (p_slope_min[ax][ay] + p_slope_max[ax][ay]) / 2; } } else { /* Is there a direct line to the target? */ if (is_direct_projectable(x1, y1)) { /* Set the direct route */ mmove_slope = (p_slope_min[ay][ax] + p_slope_max[ay][ax]) / 2; mmove_sq = 0; /* Done */ return; } /* Look up the slope to use */ mmove_slope = p_slope_min[ay][ax]; while (mmove_slope <= p_slope_max[ay][ax]) { /* Note that the data offsets have x,y swapped */ xx = x1 + sx * project_data[mmove_slope][mmove_sq].y; yy = y1 + sy * project_data[mmove_slope][mmove_sq].x; /* Done? */ if ((xx == x1 + dx) && (yy == y1 + dy)) break; c_ptr = area(xx, yy); /* Do we want to stop early? */ if (!is_projectable && c_ptr->m_idx) break; /* Is the square not occupied by a monster, and passable? */ if (!cave_los_grid(c_ptr) || c_ptr->m_idx) { /* Advance to the best position we have not looked at yet */ temp = project_data[mmove_slope][mmove_sq].slope; mmove_sq = project_data[mmove_slope][mmove_sq].square; mmove_slope = temp; } else { /* Advance along ray */ mmove_sq++; } } /* No match? */ if (mmove_slope > p_slope_max[ay][ax]) { mmove_slope = (p_slope_min[ay][ax] + p_slope_max[ay][ax]) / 2; } } /* * Reset to start. * * Square zero is the the first square along the path. * It is not the starting square */ mmove_sq = 0; } /* * Calculate incremental motion * * The current position is updated. * * (x,y) encodes the current location. * * This routine is very similar to los() except that we can use it * to return partial results. */ void mmove(int *x, int *y, int x1, int y1) { int ax, ay, sx, sy; /* Extract the absolute offset */ ay = ABS(mmove_dy); ax = ABS(mmove_dx); /* Extract some signs */ sx = (mmove_dx < 0) ? -1 : 1; sy = (mmove_dy < 0) ? -1 : 1; /* Paranoia - square number is too large */ if (mmove_sq >= slope_count[mmove_slope]) { mmove_sq = slope_count[mmove_slope] - 1; } if (ay < ax) { /* Work out square to return */ *x = x1 + sx * project_data[mmove_slope][mmove_sq].x; *y = y1 + sy * project_data[mmove_slope][mmove_sq].y; } else { /* Work out square to return */ *x = x1 + sx * project_data[mmove_slope][mmove_sq].y; *y = y1 + sy * project_data[mmove_slope][mmove_sq].x; } /* Next square, next time. */ mmove_sq++; } /* Does this square stop the projection? */ static bool project_stop(const cave_type *c_ptr, u16b flg) { if (cave_los_grid(c_ptr)) { /* Require fields do not block magic */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MAGIC)) { return (TRUE); } /* Is the square occupied by a monster? */ if (c_ptr->m_idx != 0) { if (flg & (PROJECT_STOP)) { /* Bolt spell is stopped by a monster */ return (TRUE); } if ((flg & (PROJECT_FRND)) && is_pet(&m_list[c_ptr->m_idx])) { /* Try not to affect friendly monsters */ return (TRUE); } } /* Seems ok */ return (FALSE); } /* Blocked */ return (TRUE); } /* * Hack - a function to pass to los_general() used * to do projectable(). Assume everything blocks projections. */ static bool cave_stop_project(const cave_type *c_ptr) { /* Is it passable? */ return (project_stop(c_ptr, 0)); } /* * Determine if a bolt spell cast from (x1,y1) to (x2,y2) will arrive * at the final destination, assuming no monster gets in the way. * * This needs to be as fast as possible - so do not use the los_general() * function. * * XXX XXX Should we use a version of los(), but choose the average slope? * * XXX XXX Should we use los_general() anyway? * * XXX XXX Should there be two slightly different versions of this function * a 'smart' one, and a 'dumb' but fast one? */ bool projectable(int x1, int y1, int x2, int y2) { /* Are we projectable? */ return (los_general(x1, y1, x2, y2, cave_stop_project)); } /* * Determine the path taken by a projection. * * The path grids are saved into the grid array pointed to by "gp", and * there should be room for at least "range" grids in "gp". Note that * due to the way in which distance is calculated, this function normally * uses fewer than "range" grids for the projection path, so the result * of this function should never be compared directly to "range". Note * that the initial grid (x1,y1) is never saved into the grid array, not * even if the initial grid is also the final grid. XXX XXX XXX * * The "flg" flags can be used to modify the behavior of this function. * * In particular, the "PROJECT_STOP" and "PROJECT_THRU" flags have the same * semantics as they do for the "project" function, namely, that the path * will stop as soon as it hits a monster, or that the path will continue * through the destination grid, respectively. * * This function returns the number of grids (if any) in the path. This * function will return zero if and only if (x1,y1) and (x2,y2) are equal. * * This algorithm is the same as that used by "los_general()", however * the grids are saved along the path. * * This means that mmove2() will now follow exactly the same path as used * by los() and update_view(). You can only hit things you can see - and * everything you can see, you can hit. The paths from this function * tend to look slightly "curved" - but that is a small price to pay for * simplicity. * * XXX XXX XXX You could make the paths straighter by using the median * ray connecting the two grids, and then going left and right to try and * find a free shot. That would require more information to be saved * though. It also probably would be slower than the current code. * * XXX XXX XXX This routine must be fairly fast - we use it in * projectable(), which is called alot in the AI code. */ sint project_path(coord *gp, int x1, int y1, int x2, int y2, u16b flg) { int y, x, sx, sy, dx, dy; int sq, sl; /* Absolute */ int ay, ax; int dist, temp; cave_type *c_ptr; /* No path necessary (or allowed) */ if ((x1 == x2) && (y1 == y2)) return (0); /* Extract the offset */ dy = y2 - y1; dx = x2 - x1; /* * We only work for points that are less than MAX_SIGHT appart. * Note that MAX_RANGE < MAX_SIGHT */ dist = distance(x1, y1, x2, y2); if (dist > MAX_SIGHT) { /* Rescale */ dx = (dx * MAX_SIGHT) / dist; dy = (dy * MAX_SIGHT) / dist; } /* Extract the absolute offset */ ay = ABS(dy); ax = ABS(dx); /* Extract some signs */ sx = (dx < 0) ? -1 : 1; sy = (dy < 0) ? -1 : 1; /* * Start at the first square in the list. * This is a square adjacent to (x1,y1) */ sq = 0; /* Hack - we need to stick to one octant */ if (ay < ax) { /* Look up the slope to use */ sl = p_slope_min[ax][ay]; while (sl <= p_slope_max[ax][ay]) { x = x1 + sx * project_data[sl][sq].x; y = y1 + sy * project_data[sl][sq].y; /* Done? */ if ((x == x1 + dx) && (y == y1 + dy)) break; /* Stop if out of bounds */ if (!in_bounds2(x, y)) break; c_ptr = area(x, y); if (project_stop(c_ptr, flg)) { /* Advance to the best position we have not looked at yet */ temp = project_data[sl][sq].slope; sq = project_data[sl][sq].square; sl = temp; } else { /* Advance along ray */ sq++; } } /* No match? */ if (sl > p_slope_max[ax][ay]) { sl = (p_slope_min[ax][ay] + p_slope_max[ax][ay]) / 2; } } else { /* Look up the slope to use */ sl = p_slope_min[ay][ax]; while (sl <= p_slope_max[ay][ax]) { /* Note that the data offsets have x,y swapped */ x = x1 + sx * project_data[sl][sq].y; y = y1 + sy * project_data[sl][sq].x; /* Done? */ if ((x == x1 + dx) && (y == y1 + dy)) break; /* Stop if out of bounds */ if (!in_bounds2(x, y)) break; c_ptr = area(x, y); if (project_stop(c_ptr, flg)) { /* Advance to the best position we have not looked at yet */ temp = project_data[sl][sq].slope; sq = project_data[sl][sq].square; sl = temp; } else { /* Advance along ray */ sq++; } } /* No match? */ if (sl > p_slope_max[ay][ax]) { sl = (p_slope_min[ay][ax] + p_slope_max[ay][ax]) / 2; } } /* Scan over squares along path */ for (sq = 0; (sq < MAX_RANGE) && (sq < slope_count[sl]); sq++) { if (ay < ax) { /* Work out square to use */ x = x1 + sx * project_data[sl][sq].x; y = y1 + sy * project_data[sl][sq].y; } else { /* Work out square to use */ x = x1 + sx * project_data[sl][sq].y; y = y1 + sy * project_data[sl][sq].x; } /* Stop if out of bounds */ if (!in_bounds2(x, y)) { sq--; break; } /* Save the square */ gp[sq].x = x; gp[sq].y = y; /* Sometimes stop at destination grid */ if (!(flg & (PROJECT_THRU))) { if ((x == x2) && (y == y2)) break; } c_ptr = area(x, y); /* Does the grid stop projection? */ if (project_stop(c_ptr, flg)) break; } /* Include the last square */ sq++; /* Paranoia */ if (sq > MAX_RANGE) sq = MAX_RANGE; if (sq >= slope_count[sl]) sq = slope_count[sl] - 1; /* Length */ return (sq); } /* Will this square stop a ball spell? */ static bool cave_stop_ball(const cave_type *c_ptr) { /* Walls block spells */ if (!cave_los_grid(c_ptr)) return (TRUE); /* Fields can block magic */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MAGIC)) return (TRUE); /* Seems ok */ return (FALSE); } /* * Use Modified version of los() for calculation of balls. * Balls are stopped by walls, and by fields. */ bool in_ball_range(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, cave_stop_ball)); } /* * Does the grid stop disintegration? */ static bool cave_stop_disintegration(const cave_type *c_ptr) { /* Some terrain types block disintegration */ if (cave_wall_grid(c_ptr) && cave_perma_grid(c_ptr)) { return (TRUE); } /* Fields can block disintegration to */ if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) return (TRUE); /* Seems ok */ return (FALSE); } /* * Use modified version of los() for calculation of disintegration balls. * Disintegration effects are stopped by permanent walls and fields. */ bool in_disintegration_range(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, cave_stop_disintegration)); } /* * Standard "find me a location" function * * Obtains a legal location within the given distance of the initial * location, and with "los()" from the source to destination location. * * This function is often called from inside a loop which searches for * locations while increasing the "d" distance. */ void scatter(int *xp, int *yp, int x, int y, int d) { int nx = 0, ny = 0; int c = 0; /* Pick a location */ while (c++ < 1000) { /* Pick a new location */ ny = rand_spread(y, d); nx = rand_spread(x, d); /* Ignore annoying locations */ if (!in_bounds2(nx, ny)) continue; /* Ignore excessively distant locations */ if ((d > 1) && (distance(x, y, nx, ny) > d)) continue; /* Require line of sight */ if (los(x, y, nx, ny)) break; } if (c > 999) { ny = y; nx = x; } /* Save the location */ (*yp) = ny; (*xp) = nx; } /* * Can the player "see" the given grid in detail? * * He must have vision, illumination, and line of sight. * * Note -- "GRID_LITE" is only set if the "torch" has "los()". * So, given "GRID_LITE", we know that the grid is "fully visible". * * Note that "CAVE_GLOW" makes little sense for a wall, since it would mean * that a wall is visible from any direction. That would be odd. Except * under wizard light, which might make sense. Thus, for walls, we require * not only that they be "CAVE_GLOW", but also, that they be adjacent to a * grid which is not only "CAVE_GLOW", but which is a non-wall, and which is * in line of sight of the player. * * This extra check is expensive, but it provides a more "correct" semantics. * * Note that "glowing walls" are only considered to be "illuminated" if the * grid which is next to the wall in the direction of the player is also a * "glowing" grid. This prevents the player from being able to "see" the * walls of illuminated rooms from a corridor outside the room. */ bool player_can_see_bold(int x, int y) { pcave_type *pc_ptr; /* Needs to be in bounds */ if (!in_boundsp(x, y)) return (FALSE); /* Access the cave grid */ pc_ptr = parea(x, y); /* Require line of sight to the grid + lit grid */ return (player_can_see_grid(pc_ptr)); } /* * Returns true if the player's grid is dark */ bool no_lite(void) { return (!player_can_see_bold(p_ptr->px, p_ptr->py)); } /* * Determine if a given location may be "destroyed" * * Used by destruction spells, and for placing stairs, etc. */ bool cave_valid_grid(const cave_type *c_ptr) { object_type *o_ptr; /* Forbid perma-grids */ if (cave_perma_grid(c_ptr)) return (FALSE); /* Check objects */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Forbid artifact grids */ if (FLAG(o_ptr, TR_INSTA_ART)) return (FALSE); } OBJ_ITT_END; /* Accept */ return (TRUE); } /* * Memorize interesting viewable object/features in the given grid * * This function should only be called on "legal" grids. * * This function will memorize the object and/or feature in the given * grid, if they are (1) viewable and (2) interesting. Note that all * objects are interesting, all terrain features except floors (and * invisible traps) are interesting, and floors (and invisible traps) * are interesting sometimes (depending on various options involving * the illumination of floor grids). * * Note that the memorization of objects is completely separate from * the memorization of terrain features, preventing annoying floor * memorization when a detected object is picked up from a dark floor, * and object memorization when an object is dropped into a floor grid * which is memorized but out-of-sight. * * This function should be called every time the "memorization" of * a grid (or the object in a grid) is called into question, such * as when an object is created in a grid, when a terrain feature * changes, and when any grid becomes "illuminated" or "viewable". * * Note the relatively efficient use of this function by the various * "update_view()" and "update_lite()" calls, to allow objects and * terrain features to be memorized (and drawn) whenever they become * viewable or illuminated in any way, but not when they "maintain" * or "lose" their previous viewability or illumination. * * Note the butchered "internal" version of the old "player_can_see_bold()" * that is used internal to this function. (Using the GRID_SEEN * flag then allows us to quickly test visibility by the player.) */ void note_spot(int x, int y) { int px = p_ptr->px; int py = p_ptr->py; object_type *o_ptr; field_type *f_ptr; cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Is it lit + in view + player is not blind? */ if (((c_ptr->info & (CAVE_GLOW | CAVE_MNLT)) || (pc_ptr->player & (GRID_LITE))) && player_has_los_grid(pc_ptr) && !p_ptr->tim.blind) { /* Memorize certain non-torch-lit wall grids */ if (!cave_floor_grid(c_ptr) && !(pc_ptr->player & (GRID_LITE))) { int yy, xx; /* Hack -- move one grid towards player */ yy = (y < py) ? (y + 1) : (y > py) ? (y - 1) : y; xx = (x < px) ? (x + 1) : (x > px) ? (x - 1) : x; /* Check for "local" illumination */ if (!(area(xx, yy)->info & (CAVE_GLOW))) { /* Hack - lite the spot any way */ lite_spot(x, y); /* Done */ return; } /* We can't see the square */ pc_ptr->player &= ~(GRID_SEEN); } /* We can see the square */ pc_ptr->player |= (GRID_SEEN); /* Memorize feature */ remember_grid(c_ptr, pc_ptr); /* Hack -- memorize objects */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Memorize objects */ o_ptr->info |= OB_SEEN; } OBJ_ITT_END; /* Hack -- memorize fields */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Memorize fields */ f_ptr->info |= FIELD_INFO_MARK; } FLD_ITT_END; } /* Light the spot, now that we have noticed the changes. */ lite_spot(x, y); } /* * Some comments on the cave grid flags. -BEN- * * * One of the major bottlenecks in previous versions of Angband was in * the calculation of "line of sight" from the player to various grids, * such as monsters. This was such a nasty bottleneck that a lot of * silly things were done to reduce the dependancy on "line of sight", * for example, you could not "see" any grids in a lit room until you * actually entered the room, and there were all kinds of bizarre grid * flags to enable this behavior. This is also why the "call light" * spells always lit an entire room. * * The code below provides functions to calculate the "field of view" * for the player, which, once calculated, provides extremely fast * calculation of "line of sight from the player", and to calculate * the "field of torch lite", which, again, once calculated, provides * extremely fast calculation of "which grids are lit by the player's * lite source". In addition to marking grids as "GRID_VIEW" and/or * "GRID_LITE", as appropriate, these functions maintain an array * containing the locations of all of the grids marked with the * GRID_VIEW flag, which can be used to very quickly scan through all * of those grids. * * To allow more "semantically valid" field of view semantics, whenever * the field of view (or the set of torch lit grids) changes, all of the * grids in the field of view (or the set of torch lit grids) are "drawn" * so that changes in the world will become apparent as soon as possible. * This has been optimized so that only grids which actually "change" are * redrawn, using the "temp" array and the "CAVE_TEMP" flag to keep track * of the grids which are entering or leaving the relevent set of grids. * * These new methods are so efficient that the old nasty code was removed. * * Note that there is no reason to "update" the "viewable space" unless * the player "moves", or walls/doors are created/destroyed, and there * is no reason to "update" the "torch lit grids" unless the field of * view changes, or the "light radius" changes, or HACK: if the player * becomes blind. This means that when the player is resting, or digging, * or doing anything that does not involve movement or changing the state * of the dungeon, there is no need to update the "view" or the "lite" * regions, which is nice. * * Note that the calls to the nasty "los()" function have been removed with * regards to the view/lite code. This results in a huge speed increase. * * * Note that every "GRID_LITE" grid is also a "GRID_VIEW" grid, and in * fact, the player can always "see" all grids which are marked as * "GRID_LITE", unless they are "off screen". * * Every lit grid that is "GRID_VIEW" and lit in some way is "GRID_SEEN" * allowing a fast way to tell if the player can see a grid or not. * * The "update_view()" function maintains the "GRID_VIEW" flag for each * grid and maintains an array of all "GRID_VIEW" grids. It also looks * after the "GRID_LITE" flag, and the memorization of new map sqaures. * The "GRID_SEEN" status is also affected - however, the monster lighting * routine also affects this flag. * * * The current "update_view()" algorithm uses the "CAVE_XTRA" flag as a * temporary internal flag to mark those grids which have been previously * memorized. This is to prevent blind players from gaining information * about their surroundings. This flag is always cleared when we are done. * * * The current "update_view()" algorithm uses the "CAVE_TEMP" flag, and the * array of grids which are marked as "CAVE_TEMP", to keep track of which * grids were previously marked as "GRID_VIEW", which allows us to optimize * the "screen updates". We only draw the squares that change on the screen. * * The "CAVE_TEMP" flag, and the array of "CAVE_TEMP" grids, is also used * for various other purposes, such as spreading lite or darkness during * "lite_room()" / "unlite_room()", for calculating monster flow, and filling * the fractal caves. * * * Any grid can be marked as "CAVE_GLOW" which means that the grid itself is * in some way permanently lit. However, for the player to "see" anything * in the grid, as determined by "player_can_see()", the player must not be * blind, the grid must be marked as "GRID_VIEW", and, in addition, "wall" * grids, even if marked as "perma lit", are only illuminated if they touch * a grid which is not a wall and is marked both "CAVE_GLOW" and "GRID_VIEW". * * * The old "CAVE_MARK" flag has been removed - replaced by the terrain type * of the memorised grid, in the player information: pcave_type. This is * FEAT_NONE when the player doesn't know anything about the square. * * Objects are "memorized" in a different way, using a special "OB_SEEN" flag * on the object itself, which is set when an object is observed or detected. * * * A grid may be marked as "CAVE_ROOM" which means that it is part of a "room". * This is used only in dungeon generation. Perhaps this flag can be used in * other code if required. This was an alias for "CAVE_MNLT" which is set if * the square is lit by a monsters light source. * * * A grid may be marked as "CAVE_ICKY" which means it is part of a "vault", * and should be unavailable for "teleportation" destinations. * * * The "view_torch_grids" allows the player to memorize every torch-lit grid. * The player will always memorize important walls, doors, stairs, and other * terrain features, as well as any "detected" grids. * * * Note that the new "update_view()" method allows, among other things, a room * to be "partially" seen as the player approaches it, with a growing cone of * floor appearing as the player gets closer to the door. * * And my favorite "plus" is that you can now use a special option to draw the * "floors" in the "viewable region" brightly (actually, to draw the *other* * grids dimly), providing a "pretty" effect as the player runs around, and * to efficiently display the "torch lite" in a special color. * * Here are some pictures of the legal "light source" radius values, in * which the numbers indicate the "order" in which the grids could have * been calculated, if desired. Larger radii are possible... * * * Rad=0 Rad=1 Rad=2 Rad=3 * No-Lite Torch,etc Lantern Artifacts * * 333 * 333 43334 * 212 32123 3321233 * @ 1@1 31@13 331@133 * 212 32123 3321233 * 333 43334 * 333 * * * Here is an illustration of the two different "update_view()" algorithms, * in which the grids marked "%" are pillars, and the grids marked "?" are * not in line of sight of the player. * * * Sample situation * * ##################### * ############.%.%.%.%# * #...@..#####........# * #............%.%.%.%# * #......#####........# * ############........# * ##################### * * * New Algorithm Old Algorithm * * ########????????????? ########????????????? * #...@..#????????????? #...@..#????????????? * #...........????????? #.........??????????? * #......#####.....???? #......####?????????? * ########?????????...# ########????????????? * * ########????????????? ########????????????? * #.@....#????????????? #.@....#????????????? * #............%??????? #...........????????? * #......#####........? #......#####????????? * ########??????????..# ########????????????? * * ########????????????? ########?????%??????? * #......#####........# #......#####..??????? * #.@..........%??????? #.@..........%??????? * #......#####........# #......#####..??????? * ########????????????? ########????????????? * * ########??????????..# ########????????????? * #......#####........? #......#####????????? * #............%??????? #...........????????? * #.@....#????????????? #.@....#????????????? * ########????????????? ########????????????? * * ########?????????%??? ########????????????? * #......#####.....???? #......####?????????? * #...........????????? #.........??????????? * #...@..#????????????? #...@..#????????????? * ########????????????? ########????????????? */ /* * Clear the viewable space */ void forget_view(void) { int i, x, y; pcave_type *pc_ptr; /* None to forget */ if (!view_n) return; /* Clear them all */ for (i = 0; i < view_n; i++) { y = view_y[i]; x = view_x[i]; /* Access the grid */ pc_ptr = parea(x, y); /* Forget that the grid is viewable or lit */ pc_ptr->player &= ~(GRID_LITE | GRID_VIEW | GRID_SEEN); /* Only lite the spot if is on the panel (can change due to resizing */ if (!panel_contains(x, y)) continue; /* Update the screen */ lite_spot(x, y); } /* None left */ view_n = 0; } /* * Maximum number of grids in a single octant */ #define VINFO_MAX_GRIDS 175 /* * Mask of bits used in a single octant */ #define VINFO_BITS_4 0x0000007FL #define VINFO_BITS_3 0xFFFFFFFFL #define VINFO_BITS_2 0xFFFFFFFFL #define VINFO_BITS_1 0xFFFFFFFFL #define VINFO_BITS_0 0xFFFFFFFFL /* * Forward declare */ typedef struct vinfo_type vinfo_type; /* * The 'vinfo_type' structure */ struct vinfo_type { s16b grid_x[8]; s16b grid_y[8]; u32b bits[5]; vinfo_type *next_0; vinfo_type *next_1; byte y; byte x; byte d; byte r; }; /* * The array of "vinfo" objects, initialized by "vinfo_init()" */ static vinfo_type vinfo[VINFO_MAX_GRIDS]; /* * Slope scale factor */ #define SCALE 100000L /* * Forward declare */ typedef struct vinfo_hack vinfo_hack; /* * Temporary data used by "vinfo_init()" * * - Number of slopes * * - Slope values * * - Slope min and max for each square */ struct vinfo_hack { int num_slopes; s32b slopes[VINFO_MAX_SLOPES]; s32b slopes_min[MAX_SIGHT + 1][MAX_SIGHT + 1]; s32b slopes_max[MAX_SIGHT + 1][MAX_SIGHT + 1]; }; /* * Sorting hook -- comp function -- array of s32b (see below) * * We use "u" to point to an array of s32b. */ static bool ang_sort_comp_hook_s32b(const vptr u, const vptr v, int a, int b) { s32b *x = (s32b *)(u); /* Hack - ignore v */ (void)v; return (x[a] <= x[b]); } /* * Sorting hook -- swap function -- array of s32b (see below) * * We use "u" to point to an array of s32b. */ static void ang_sort_swap_hook_s32b(const vptr u, const vptr v, int a, int b) { s32b *x = (s32b *)(u); s32b temp; /* Hack - ignore v */ (void)v; /* Swap */ temp = x[a]; x[a] = x[b]; x[b] = temp; } /* * Save a slope */ static void vinfo_init_aux(vinfo_hack *hack, int x, int y, s32b m) { int i; /* Handle "legal" slopes */ if ((m > 0) && (m <= SCALE)) { /* Look for that slope */ for (i = 0; i < hack->num_slopes; i++) { if (hack->slopes[i] == m) break; } /* New slope */ if (i == hack->num_slopes) { /* Paranoia */ if (hack->num_slopes >= VINFO_MAX_SLOPES) { quit_fmt("Too many slopes (%d)!", VINFO_MAX_SLOPES); } /* Save the slope, and advance */ hack->slopes[hack->num_slopes++] = m; } } /* Track slope range */ if (hack->slopes_min[y][x] > m) hack->slopes_min[y][x] = m; if (hack->slopes_max[y][x] < m) hack->slopes_max[y][x] = m; } /* * Initialize the "vinfo" and "project_data" arrays * * Full Octagon (radius 20), Grids=1149 * * Quadrant (south east), Grids=308, Slopes=251 * * Octant (east then south), Grids=161, Slopes=126 * * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES * have the correct values, which can be derived by setting them to * a number which is too high, running this function, and using the * error messages to obtain the correct values. */ errr vinfo_init(void) { int i, j; int y, x; s32b m; vinfo_hack *hack; int num_grids = 0; int queue_head = 0; int queue_tail = 0; vinfo_type *queue[VINFO_MAX_GRIDS * 2]; /* Make hack */ MAKE(hack, vinfo_hack); /* Analyze grids */ for (y = 0; y <= MAX_SIGHT; ++y) { for (x = y; x <= MAX_SIGHT; ++x) { /* Skip grids which are out of sight range */ if (distance(0, 0, x, y) > MAX_SIGHT) continue; /* Default slope range */ hack->slopes_min[y][x] = 999999999; hack->slopes_max[y][x] = 0; /* Clear the p_slope_min and max arrays */ p_slope_min[x][y] = VINFO_MAX_SLOPES; p_slope_max[x][y] = 0; p_slope_min[y][x] = VINFO_MAX_SLOPES; p_slope_max[y][x] = 0; /* Paranoia */ if (num_grids >= VINFO_MAX_GRIDS) { quit_fmt("Too many grids (%d >= %d)!", num_grids, VINFO_MAX_GRIDS); } /* Count grids */ num_grids++; /* Slope to the top right corner */ m = SCALE * (1000L * y - 500) / (1000L * x + 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); /* Slope to top left corner */ m = SCALE * (1000L * y - 500) / (1000L * x - 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); /* Slope to bottom right corner */ m = SCALE * (1000L * y + 500) / (1000L * x + 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); /* Slope to bottom left corner */ m = SCALE * (1000L * y + 500) / (1000L * x - 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); } } /* Enforce maximal efficiency */ if (num_grids < VINFO_MAX_GRIDS) { quit_fmt("Too few grids (%d < %d)!", num_grids, VINFO_MAX_GRIDS); } /* Enforce maximal efficiency */ if (hack->num_slopes < VINFO_MAX_SLOPES) { quit_fmt("Too few slopes (%d < %d)!", hack->num_slopes, VINFO_MAX_SLOPES); } /* Sort slopes numerically */ ang_sort_comp = ang_sort_comp_hook_s32b; /* Sort slopes numerically */ ang_sort_swap = ang_sort_swap_hook_s32b; /* Sort the (unique) slopes */ ang_sort(hack->slopes, NULL, hack->num_slopes); /* Clear the counters for each slope */ (void)C_WIPE(slope_count, VINFO_MAX_SLOPES, int); /* Enqueue player grid */ queue[queue_tail++] = &vinfo[0]; /* Process queue */ while (queue_head < queue_tail) { int e; vinfo_type *p; /* Index */ e = queue_head; /* Dequeue next grid */ p = queue[queue_head++]; /* Location */ y = vinfo[e].grid_y[0]; x = vinfo[e].grid_x[0]; /* Compute grid offsets */ vinfo[e].grid_x[0] = x; vinfo[e].grid_x[1] = y; vinfo[e].grid_x[2] = -y; vinfo[e].grid_x[3] = -x; vinfo[e].grid_x[4] = -x; vinfo[e].grid_x[5] = -y; vinfo[e].grid_x[6] = y; vinfo[e].grid_x[7] = x; vinfo[e].grid_y[0] = y; vinfo[e].grid_y[1] = x; vinfo[e].grid_y[2] = x; vinfo[e].grid_y[3] = y; vinfo[e].grid_y[4] = -y; vinfo[e].grid_y[5] = -x; vinfo[e].grid_y[6] = -x; vinfo[e].grid_y[7] = -y; /* Analyze slopes */ for (i = 0; i < hack->num_slopes; ++i) { m = hack->slopes[i]; /* Memorize intersection slopes (for non-player-grids) */ if ((e > 0) && (hack->slopes_min[y][x] < m) && (m < hack->slopes_max[y][x])) { /* We use this slope */ slope_count[i]++; /* Save the bit that stands for this slope */ vinfo[e].bits[i / 32] |= (1L << (i % 32)); } } /* Default */ vinfo[e].next_0 = &vinfo[0]; /* Grid next child */ if (distance(0, 0, x + 1, y) <= MAX_SIGHT) { if (!((queue[queue_tail - 1]->grid_x[0] == x + 1) && (queue[queue_tail - 1]->grid_y[0] == y))) { vinfo[queue_tail].grid_x[0] = x + 1; vinfo[queue_tail].grid_y[0] = y; queue[queue_tail] = &vinfo[queue_tail]; queue_tail++; } vinfo[e].next_0 = &vinfo[queue_tail - 1]; } /* Default */ vinfo[e].next_1 = &vinfo[0]; /* Grid diag child */ if (distance(0, 0, x + 1, y + 1) <= MAX_SIGHT) { if (!((queue[queue_tail - 1]->grid_x[0] == x + 1) && (queue[queue_tail - 1]->grid_y[0] == y + 1))) { vinfo[queue_tail].grid_x[0] = x + 1; vinfo[queue_tail].grid_y[0] = y + 1; queue[queue_tail] = &vinfo[queue_tail]; queue_tail++; } vinfo[e].next_1 = &vinfo[queue_tail - 1]; } /* Hack -- main diagonal has special children */ if (y == x) vinfo[e].next_0 = vinfo[e].next_1; /* Extra values */ vinfo[e].y = y; vinfo[e].x = x; vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2)); vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0); } /* Verify maximal bits XXX XXX XXX */ if (((vinfo[1].bits[4] | vinfo[2].bits[4]) != VINFO_BITS_4) || ((vinfo[1].bits[3] | vinfo[2].bits[3]) != VINFO_BITS_3) || ((vinfo[1].bits[2] | vinfo[2].bits[2]) != VINFO_BITS_2) || ((vinfo[1].bits[1] | vinfo[2].bits[1]) != VINFO_BITS_1) || ((vinfo[1].bits[0] | vinfo[2].bits[0]) != VINFO_BITS_0)) { quit("Incorrect bit masks!"); } /* Create the project_data array */ for (i = 0; i < VINFO_MAX_SLOPES; i++) { /* Create the list of squares intersected by this slope */ C_MAKE(project_data[i], slope_count[i], project_type); j = 0; for (y = 0; y <= MAX_SIGHT; ++y) { for (x = y; x <= MAX_SIGHT; ++x) { /* Only if in range */ if (distance(0, 0, x, y) > MAX_SIGHT) continue; /* Hack - ignore the origin */ if (!x && !y) continue; m = hack->slopes[i]; /* Does this square intersect the line? */ if ((hack->slopes_min[y][x] < m) && (m < hack->slopes_max[y][x])) { /* Save the square */ project_data[i][j].x = x; project_data[i][j].y = y; /* Add in the slopes information */ if (p_slope_min[x][y] > i) p_slope_min[x][y] = i; if (p_slope_max[x][y] < i) p_slope_max[x][y] = i; /* Next square... */ j++; } } } } /* * Add in the final information in the projection table. * * We need to know where to go to if the current square * is blocked. * * This is calculated in the following way: * * First, we need to find the first slope that does not * include the current square. * * This will be the first slope that does * not contain this square. The position along that slope * will be the first square that is not already scanned * by the current slope. * * This means that we may end up scanning squares twice, * but the simplification of the algorithm is worth it. */ for (i = 0; i < VINFO_MAX_SLOPES; i++) { for (j = 0; j < slope_count[i]; j++) { /* Set default case. */ project_data[i][j].slope = VINFO_MAX_SLOPES; project_data[i][j].square = 0; /* Find first slope without this square */ for (x = i + 1; x < VINFO_MAX_SLOPES; x++) { bool found = FALSE; for (y = 0; y < slope_count[x]; y++) { if ((project_data[x][y].x == project_data[i][j].x) && (project_data[x][y].y == project_data[i][j].y)) { found = TRUE; break; } } /* Did we find the blocking square? */ if (found) continue; /* Do we already have an answer? */ if (project_data[i][j].slope != VINFO_MAX_SLOPES) break; /* We did not find the square - save the row */ project_data[i][j].slope = x; /* Paranoia */ project_data[i][j].square = 0; /* Find the first non-matching square */ for (y = 0; y < slope_count[x]; y++) { if ((project_data[x][y].x != project_data[i][y].x) || (project_data[x][y].y != project_data[i][y].y)) { /* Not a match */ project_data[i][j].square = y; break; } } } } } /* Kill hack */ FREE(hack); /* Success */ return (0); } /* * Calculate the complete field of view using a new algorithm * * * Normally, vision along the major axes is more likely than vision * along the diagonal axes, so we check the bits corresponding to * the lines of sight near the major axes first. * * We use the "temp_x/y" arrays (and the "CAVE_TEMP" flag) to keep track of * which grids were previously marked "GRID_VIEW", since only those grids * whose "GRID_VIEW" value changes during this routine must be redrawn. * * This function is now responsible for maintaining the "GRID_LITE" * flags as well as the "GRID_VIEW" flags, which is good, because * the only grids which normally need to be memorized and/or redrawn * are the ones whose "GRID_VIEW" flag changes during this routine. * * Basically, this function divides the "octagon of view" into octants of * grids (where grids on the main axes and diagonal axes are "shared" by * two octants), and processes each octant one at a time, processing each * octant one grid at a time, processing only those grids which "might" be * viewable, and setting the "GRID_VIEW" flag for each grid for which there * is an (unobstructed) line of sight from the center of the player grid to * any internal point in the grid (and collecting these "GRID_VIEW" grids * into the "view_g" array), and setting the "GRID_LITE" flag for the grid * if, in addition, the grid is "illuminated" in some way (by a torch). * * This function relies on a theorem (suggested and proven by Mat Hostetter) * which states that in each octant of a field of view, a given grid will * be "intersected" by one or more unobstructed "lines of sight" from the * center of the player grid if and only if it is "intersected" by at least * one such unobstructed "line of sight" which passes directly through some * corner of some grid in the octant which is not shared by any other octant. * The proof is based on the fact that there are at least three significant * lines of sight involving any non-shared grid in any octant, one which * intersects the grid and passes though the corner of the grid closest to * the player, and two which "brush" the grid, passing through the "outer" * corners of the grid, and that any line of sight which intersects a grid * without passing through the corner of a grid in the octant can be "slid" * slowly towards the corner of the grid closest to the player, until it * either reaches it or until it brushes the corner of another grid which * is closer to the player, and in either case, the existance of a suitable * line of sight is thus demonstrated. * * It turns out that in each octant of the radius 20 "octagon of view", * there are 161 grids (with 128 not shared by any other octant), and there * are exactly 126 distinct "lines of sight" passing from the center of the * player grid through any corner of any non-shared grid in the octant. To * determine if a grid is "viewable" by the player, therefore, you need to * simply show that one of these 126 lines of sight intersects the grid but * does not intersect any wall grid closer to the player. So we simply use * a bit vector with 126 bits to represent the set of interesting lines of * sight which have not yet been obstructed by wall grids, and then we scan * all the grids in the octant, moving outwards from the player grid. For * each grid, if any of the lines of sight which intersect that grid have not * yet been obstructed, then the grid is viewable. Furthermore, if the grid * is a wall grid, then all of the lines of sight which intersect the grid * should be marked as obstructed for future reference. Also, we only need * to check those grids for whom at least one of the "parents" was a viewable * non-wall grid, where the parents include the two grids touching the grid * but closer to the player grid (one adjacent, and one diagonal). For the * bit vector, we simply use 4 32-bit integers. All of the static values * which are needed by this function are stored in the large "vinfo" array * (above), which is initialised at startup. * * This has been changed to allow a more circular view, due to the more * advanced distance() function in Zangband. There are now 135 lines of * sight and one more 32 bit flag to hold the data. * * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids * because the grids at the edge of the field of view use "grid zero" as * their children, and the queue must be able to hold several of these * special grids. Because the actual number of required grids is bizarre, * we simply allocate twice as many as we would normally need. XXX XXX XXX */ void update_view(void) { int py = p_ptr->py; int px = p_ptr->px; object_type *o_ptr; field_type *f_ptr; cave_type *c_ptr; pcave_type *pc_ptr; byte info, player; int x, y, i, o2; /* Light radius */ s16b radius = p_ptr->cur_lite; /*** Save the old "lite" grids for later ***/ /* Save the old "view" grids for later */ for (i = 0; i < view_n; i++) { y = view_y[i]; x = view_x[i]; if (!in_boundsp(x, y)) continue; c_ptr = area(x, y); pc_ptr = parea(x, y); /* Save "GRID_VIEW" grids */ if (player_has_los_grid(pc_ptr)) { /* Set "CAVE_TEMP" flag */ c_ptr->info |= (CAVE_TEMP); /* Save grid for later */ temp_x[temp_n] = x; temp_y[temp_n] = y; temp_n++; } /* Clear "GRID_VIEW" and "GRID_SEEN" flags */ pc_ptr->player &= ~(GRID_VIEW | GRID_SEEN); } /* empty the viewable list */ view_n = 0; /*** Step 1 -- player grid ***/ /* Player grid */ /* Get grid info */ c_ptr = area(px, py); pc_ptr = parea(px, py); info = c_ptr->info; player = pc_ptr->player; /* Assume viewable */ player |= (GRID_VIEW); /* Remember square under player */ remember_grid(c_ptr, pc_ptr); /* Torch-lit grid */ if (0 < radius) { /* Mark as "GRID_LITE" */ player |= (GRID_LITE); } /* Save the fact that we used to know this square */ if (pc_ptr->feat) { info |= CAVE_XTRA; } /* Save cave info */ c_ptr->info = info; pc_ptr->player = player; /* Redraw player */ /*lite_spot(px, py); */ /* Save in array */ view_y[view_n] = py; view_x[view_n] = px; view_n++; /*** Step 2 -- octants ***/ /* Scan each octant */ for (o2 = 0; o2 < 8; o2 += 1) { vinfo_type *p; /* Last added */ vinfo_type *last = &vinfo[0]; /* Grid queue */ int queue_head = 0; int queue_tail = 0; vinfo_type *queue[VINFO_MAX_GRIDS * 2]; /* Slope bit vector */ u32b bits0 = VINFO_BITS_0; u32b bits1 = VINFO_BITS_1; u32b bits2 = VINFO_BITS_2; u32b bits3 = VINFO_BITS_3; u32b bits4 = VINFO_BITS_4; /* Reset queue */ queue_head = queue_tail = 0; /* Initial grids */ queue[queue_tail++] = &vinfo[1]; queue[queue_tail++] = &vinfo[2]; /* Process queue */ while (queue_head < queue_tail) { /* Dequeue next grid */ p = queue[queue_head++]; /* Check bits */ if ((bits0 & (p->bits[0])) || (bits1 & (p->bits[1])) || (bits2 & (p->bits[2])) || (bits3 & (p->bits[3])) || (bits4 & (p->bits[4]))) { /* Get location */ x = p->grid_x[o2] + px; y = p->grid_y[o2] + py; /* Is it in bounds? */ if (!in_boundsp(x, y)) { /* Clear bits */ bits0 &= ~(p->bits[0]); bits1 &= ~(p->bits[1]); bits2 &= ~(p->bits[2]); bits3 &= ~(p->bits[3]); bits4 &= ~(p->bits[4]); continue; } /* Point to the location on the map */ c_ptr = area(x, y); pc_ptr = parea(x, y); /* Get current info flags for the square */ info = c_ptr->info; player = pc_ptr->player; /* Save the fact that we used to know this square */ if (pc_ptr->feat) { info |= CAVE_XTRA; } if (cave_los_grid(c_ptr)) { /* Floor or semi-blocking terrain like trees */ /* Enqueue child */ if (last != p->next_0) { queue[queue_tail++] = last = p->next_0; } /* Enqueue child */ if (last != p->next_1) { queue[queue_tail++] = last = p->next_1; } } /* Handle wall */ else { /* Clear bits */ bits0 &= ~(p->bits[0]); bits1 &= ~(p->bits[1]); bits2 &= ~(p->bits[2]); bits3 &= ~(p->bits[3]); bits4 &= ~(p->bits[4]); } /* All ready seen. Next... */ if (player & GRID_VIEW) continue; /* Mark as viewable */ player |= (GRID_VIEW); /* Torch-lit grids */ if (p->d <= radius) { if (!(player & GRID_LITE)) { /* Mark as "GRID_LITE" */ player |= (GRID_LITE); /* Clear the 'do not update flag' */ info &= ~(CAVE_TEMP); } } else { if (player & GRID_LITE) { /* Clear the flag, and then redraw */ info &= ~(CAVE_TEMP); player &= ~(GRID_LITE); } } /* Save cave info */ c_ptr->info = info; pc_ptr->player = player; /* Save in array */ view_y[view_n] = y; view_x[view_n] = x; view_n++; } } } /*** Step 3 -- Complete the algorithm ***/ /* Process "new" grids */ for (i = 0; i < view_n; i++) { /* Grid */ x = view_x[i]; y = view_y[i]; c_ptr = area(x, y); pc_ptr = parea(x, y); /* Get grid info */ info = c_ptr->info; /* Handle blindness */ if ((p_ptr->tim.blind) && !(info & CAVE_XTRA)) { /* Grid cannot be memorised (wasn't before) */ forget_grid(pc_ptr); /* Don't do anything else */ continue; } /* * We know we have LOS, but is it visible? */ if ((info & (CAVE_GLOW | CAVE_MNLT)) || (pc_ptr->player & (GRID_LITE))) { /* Walls are special */ if (!cave_floor_grid(c_ptr) && !(pc_ptr->player & (GRID_LITE))) { /* This is part of note_spot() */ int yy, xx; /* Hack -- move towards player */ yy = (y < py) ? (y + 1) : (y > py) ? (y - 1) : y; xx = (x < px) ? (x + 1) : (x > px) ? (x - 1) : x; /* Check for "local" illumination */ if (!(area(xx, yy)->info & (CAVE_GLOW | CAVE_MNLT))) { /* Assume the wall isn't illuminated */ continue; } } /* We can see it... */ pc_ptr->player |= GRID_SEEN; /* Show the objects */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Memorize objects */ o_ptr->info |= OB_SEEN; } OBJ_ITT_END; /* Show the fields */ FLD_ITT_START(c_ptr->fld_idx, f_ptr) { /* Memorize fields */ f_ptr->info |= FIELD_INFO_MARK; } FLD_ITT_END; /* Memorise grid */ remember_grid(c_ptr, pc_ptr); /* Must note the new information on the screen */ if (!(info & CAVE_TEMP)) { /* Redraw */ lite_spot(x, y); } } } /* Process "old" grids */ for (i = 0; i < temp_n; i++) { /* Grid */ x = temp_x[i]; y = temp_y[i]; c_ptr = area(x, y); pc_ptr = parea(x, y); /* Get grid info */ info = c_ptr->info; player = pc_ptr->player; /* Clear "CAVE_TEMP" and "CAVE_XTRA" flags */ info &= ~(CAVE_TEMP | CAVE_XTRA); /* Was "GRID_SEEN", is now not in view */ if (!(player & (GRID_SEEN))) { /* Forget memorized floor grids from view_torch_grids */ if (!(info & (CAVE_GLOW)) && !view_torch_grids && !cave_mem_grid(c_ptr)) { forget_grid(pc_ptr); } /* Clear the GRID_LITE flag */ player &= ~(GRID_LITE); /* Save cave info */ c_ptr->info = info; pc_ptr->player = player; /* Redraw */ lite_spot(x, y); } else { /* Save cave info */ c_ptr->info = info; } } /* None left */ temp_n = 0; } /* Monster location */ static int mon_lite_mx, mon_lite_my; /* * Add a square to the changes array */ static void mon_lite_hack(int x, int y) { cave_type *c_ptr; pcave_type *pc_ptr; int dx1, dy1, dx2, dy2; int tx, ty; int rx, ry; /* Out of bounds */ if (!in_boundsp(x, y)) return; c_ptr = area(x, y); pc_ptr = parea(x, y); /* Want an unlit square */ if (c_ptr->info & (CAVE_MNLT)) return; /* Get vectors */ dx1 = p_ptr->px - x; dy1 = p_ptr->py - y; dx2 = mon_lite_mx - x; dy2 = mon_lite_my - y; /* * Use a dot product to determine angle of illumination * * Only illuminate the correct sides of walls. (We don't * need to worry about floor - we won't illuminate that * if we cannot see it.) */ if (!cave_los_grid(c_ptr)) { if ((dx1 * dx2 + dy1 * dy2) < 0) return; /* * Look for the case where the bounce doesn't work * correctly due to the half-block offset: * * ####1 * ...d2 * ....# * ....# @ * ....# * * A solid '1' should not be illuminated if '2' is solid. * If '2' is not solid, then '1' should be illuminated. * * To find this case, find the reflection normal, and * from that work out where '2' is. */ rx = dx1 + dx2; ry = dy1 + dy2; /* Get the bounce block */ if (ABS(rx) > ABS(ry)) { tx = x + SGN(rx); ty = y; } else { tx = x; ty = y + SGN(ry); } /* Hack Bounce block is not in bounds - assume is solid */ if (!in_bounds(tx, ty)) return; /* Make sure that the light path doesn't pass through a wall. */ if (!cave_los_grid(area(tx, ty))) return; } /* Save the square */ if (temp_n < TEMP_MAX) { temp_x[temp_n] = x; temp_y[temp_n] = y; temp_n++; } /* Light it */ c_ptr->info |= CAVE_MNLT; /* We can see it? */ if (player_has_los_grid(pc_ptr)) { pc_ptr->player |= GRID_SEEN; /* Remember it if view_monster_grids is set. */ if (view_monster_grids) { remember_grid(c_ptr, pc_ptr); /* Show on the screen */ lite_spot(x, y); } } } /* * Update squares illuminated by monsters. * * Use the CAVE_MNLT flag to denote squares illuminated by monsters. * * The CAVE_TEMP flag is used to store the state during the * updating. Only squares in view of the player, whose state * changes are drawn via lite_spot(). */ void update_mon_lite(void) { int i, rad; cave_type *c_ptr; pcave_type *pc_ptr; s16b fx, fy; /* Blindness check */ if (p_ptr->tim.blind) { /* Clear all the monster lit squares */ for (i = 0; i < lite_n; i++) { fx = lite_x[i]; fy = lite_y[i]; /* Point to grid */ c_ptr = area(fx, fy); pc_ptr = parea(fx, fy); /* Clear monster illumination flag */ c_ptr->info &= ~(CAVE_MNLT); /* XXX XXX Square is invisible */ pc_ptr->player &= ~(GRID_SEEN); /* It is now unlit */ note_spot(fx, fy); } /* Clear the lit list */ lite_n = 0; /* Done */ return; } /* Clear all monster lit squares */ for (i = 0; i < lite_n; i++) { /* Paranoia */ if (!in_boundsp(lite_x[i], lite_y[i])) continue; /* Point to grid */ c_ptr = area(lite_x[i], lite_y[i]); pc_ptr = parea(lite_x[i], lite_y[i]); /* Set temp flag */ c_ptr->info |= (CAVE_TEMP); /* Clear monster illumination flag */ c_ptr->info &= ~(CAVE_MNLT); /* See if the square is still lit */ if (!((c_ptr->info & (CAVE_GLOW)) || pc_ptr->player & (GRID_LITE))) { /* Not lit any more */ pc_ptr->player &= ~(GRID_SEEN); } } /* Empty temp list of new squares to lite up */ temp_n = 0; /* Loop through monsters, adding newly lit squares to changes list */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Is it too far away? */ if (m_ptr->cdis > MAX_SIGHT + 3) continue; /* Get lite radius */ rad = 0; /* Note the radii are cumulative */ if (FLAG(r_ptr, RF_LITE_1)) rad++; if (FLAG(r_ptr, RF_LITE_2)) rad += 2; /* Exit if has no light */ if (!rad) continue; /* Access the location */ fx = m_ptr->fx; fy = m_ptr->fy; /* Save information */ mon_lite_mx = fx; mon_lite_my = fy; /* The square it is on */ mon_lite_hack(fx, fy); /* Adjacent squares */ mon_lite_hack(fx + 1, fy); mon_lite_hack(fx - 1, fy); mon_lite_hack(fx, fy + 1); mon_lite_hack(fx, fy - 1); mon_lite_hack(fx + 1, fy + 1); mon_lite_hack(fx + 1, fy - 1); mon_lite_hack(fx - 1, fy + 1); mon_lite_hack(fx - 1, fy - 1); /* Radius 2 */ if (rad >= 2) { /* South of the monster */ if (in_boundsp(fx, fy + 1) && cave_floor_grid(area(fx, fy + 1))) { mon_lite_hack(fx + 1, fy + 2); mon_lite_hack(fx, fy + 2); mon_lite_hack(fx - 1, fy + 2); if (in_boundsp(fx, fy + 2)) { c_ptr = area(fx, fy + 2); /* Radius 3 */ if ((rad == 3) && cave_floor_grid(c_ptr)) { mon_lite_hack(fx + 1, fy + 3); mon_lite_hack(fx, fy + 3); mon_lite_hack(fx - 1, fy + 3); } } } /* North of the monster */ if (in_boundsp(fx, fy - 1) && cave_floor_grid(area(fx, fy - 1))) { mon_lite_hack(fx + 1, fy - 2); mon_lite_hack(fx, fy - 2); mon_lite_hack(fx - 1, fy - 2); if (in_boundsp(fx, fy - 2)) { c_ptr = area(fx, fy - 2); /* Radius 3 */ if ((rad == 3) && cave_floor_grid(c_ptr)) { mon_lite_hack(fx + 1, fy - 3); mon_lite_hack(fx, fy - 3); mon_lite_hack(fx - 1, fy - 3); } } } /* East of the monster */ if (in_boundsp(fx + 1, fy) && cave_floor_grid(area(fx + 1, fy))) { mon_lite_hack(fx + 2, fy + 1); mon_lite_hack(fx + 2, fy); mon_lite_hack(fx + 2, fy - 1); if (in_boundsp(fx + 2, fy)) { c_ptr = area(fx + 2, fy); /* Radius 3 */ if ((rad == 3) && cave_floor_grid(c_ptr)) { mon_lite_hack(fx + 3, fy + 1); mon_lite_hack(fx + 3, fy); mon_lite_hack(fx + 3, fy - 1); } } } /* West of the monster */ if (in_boundsp(fx - 1, fy) && cave_floor_grid(area(fx - 1, fy))) { mon_lite_hack(fx - 2, fy + 1); mon_lite_hack(fx - 2, fy); mon_lite_hack(fx - 2, fy - 1); if (in_boundsp(fx - 2, fy)) { c_ptr = area(fx - 2, fy); /* Radius 3 */ if ((rad == 3) && cave_floor_grid(c_ptr)) { mon_lite_hack(fx - 3, fy + 1); mon_lite_hack(fx - 3, fy); mon_lite_hack(fx - 3, fy - 1); } } } } /* Radius 3 */ if (rad == 3) { /* South-East of the monster */ if (in_boundsp(fx + 1, fy + 1) && cave_floor_grid(area(fx + 1, fy + 1))) { mon_lite_hack(fx + 2, fy + 2); } /* South-West of the monster */ if (in_boundsp(fx - 1, fy + 1) && cave_floor_grid(area(fx - 1, fy + 1))) { mon_lite_hack(fx - 2, fy + 2); } /* North-East of the monster */ if (in_boundsp(fx + 1, fy - 1) && cave_floor_grid(area(fx + 1, fy - 1))) { mon_lite_hack(fx + 2, fy - 2); } /* North-West of the monster */ if (in_boundsp(fx - 1, fy - 1) && cave_floor_grid(area(fx - 1, fy - 1))) { mon_lite_hack(fx - 2, fy - 2); } } } /* * Look at old set flags to see if there are any changes. */ for (i = 0; i < lite_n; i++) { fx = lite_x[i]; fy = lite_y[i]; if (!in_boundsp(fx, fy)) continue; /* Point to grid */ c_ptr = area(fx, fy); /* It it no longer lit? */ if (!(c_ptr->info & CAVE_MNLT)) { /* Clear the temp flag for the old lit grids */ c_ptr->info &= ~(CAVE_TEMP); if (player_has_los_grid(parea(fx, fy))) { /* Do we have a monster on this square? */ if (c_ptr->m_idx) { /* Update the monster */ update_mon(c_ptr->m_idx, FALSE); } /* It is now unlit */ note_spot(fx, fy); } } } /* Clear the lite array */ lite_n = 0; /* Copy the temp array into the lit array lighting the new squares. */ for (i = 0; i < temp_n; i++) { fx = temp_x[i]; fy = temp_y[i]; if (!in_boundsp(fx, fy)) continue; /* Point to grid */ c_ptr = area(fx, fy); if (c_ptr->info & CAVE_TEMP) { /* Clear the temp flag for the old lit grids that are still lit */ c_ptr->info &= ~(CAVE_TEMP); } /* Is the square newly lit and visible? */ else if (player_has_los_grid(parea(fx, fy))) { /* Do we have a monster on this square? */ if (c_ptr->m_idx) { /* Update the monster */ update_mon(c_ptr->m_idx, FALSE); } /* It is now lit */ note_spot(fx, fy); } /* Save in the monster lit array */ lite_x[lite_n] = fx; lite_y[lite_n] = fy; lite_n++; } /* Finished with temp_n */ temp_n = 0; } void clear_mon_lite(void) { int i; cave_type *c_ptr; /* Clear all monster lit squares */ for (i = 0; i < lite_n; i++) { /* Point to grid */ c_ptr = area(lite_x[i], lite_y[i]); /* Clear monster illumination flag */ c_ptr->info &= ~(CAVE_MNLT); } /* Empty the array */ lite_n = 0; } /* * Hack -- provide some "speed" for the "flow" code * This entry is the "current index" for the "when" field * Note that a "when" value of "zero" means "not used". * * Note that the "cost" indexes from 1 to 127 are for * "old" data, and from 128 to 255 are for "new" data. * * This means that as long as the player does not "teleport", * then any monster up to 128 + MONSTER_FLOW_DEPTH will be * able to track down the player, and in general, will be * able to track down either the player or a position recently * occupied by the player. */ static int flow_n = 0; /* * Hack -- forget the "flow" information */ void forget_flow(void) { int x, y; /* Nothing to forget */ if (!flow_n) return; /* Check the entire dungeon */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { /* Forget the old data */ area(x, y)->cost = 0; area(x, y)->when = 0; } } /* Start over */ flow_n = 0; } /* * Hack - speed up the update_flow algorithm by only doing * it everytime the player moves out of LOS of the last * "way-point". */ static u16b flow_x = 0; static u16b flow_y = 0; /* * Hack -- fill in the "cost" field of every grid that the player * can "reach" with the number of steps needed to reach that grid. * This also yields the "distance" of the player from every grid. * * In addition, mark the "when" of the grids that can reach * the player with the incremented value of "flow_n". * * Hack -- use the "seen" array as a "circular queue". * * We do not need a priority queue because the cost from grid * to grid is always "one" and we process them in order. */ void update_flow(void) { int py = p_ptr->py; int px = p_ptr->px; int x, y, d, w, n; int ty, tx; int flow_tail = 1; int flow_head = 0; cave_type *c_ptr; byte feat; /* Paranoia -- make sure the array is empty */ if (temp_n) return; /* The last way-point is on the map */ if (in_boundsp(flow_x, flow_y)) { /* Check to see if the player is too close and in los */ if ((distance(px, py, flow_x, flow_y) < FLOW_DIST_MAX) && player_has_los_grid(parea(flow_x, flow_y)) && (p_ptr->state.noise_level < MONSTER_FLOW_DEPTH / 4)) return; } /* Save player position */ flow_y = py; flow_x = px; /* Cycle the old entries (once per 128 updates) */ if (flow_n++ == 255) { /* Rotate the time-stamps */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { c_ptr = area(x, y); w = c_ptr->when; c_ptr->when = (w > 128) ? (w - 128) : 0; } } /* Restart */ flow_n = 128; } /*** Player Grid ***/ c_ptr = area(px, py); /* Save the time-stamp */ c_ptr->when = flow_n; /* Save the flow cost */ c_ptr->cost = p_ptr->state.noise_level; p_ptr->state.noise_level = 0; /* Paranoia - not too much noise */ if (c_ptr->cost > MONSTER_FLOW_DEPTH) { c_ptr->cost = MONSTER_FLOW_DEPTH; } /* Enqueue that entry */ temp_y[0] = py; temp_x[0] = px; /* Now process the queue */ while (flow_head != flow_tail) { /* Extract the next entry */ ty = temp_y[flow_head]; tx = temp_x[flow_head]; /* Forget that entry */ if (++flow_head == TEMP_MAX) flow_head = 0; /* Child cost */ n = area(tx, ty)->cost - 1; /* Hack -- Limit flow depth to noise level */ if (n == 0) continue; /* Add the "children" */ for (d = 0; d < 8; d++) { int old_head = flow_tail; /* Child location */ y = ty + ddy_ddd[d]; x = tx + ddx_ddd[d]; if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); feat = c_ptr->feat; /* Ignore "pre-stamped" entries */ if (c_ptr->when == flow_n) continue; /* Ignore all "walls" except doors + terrain */ if (cave_wall_grid(c_ptr) && (feat != FEAT_CLOSED)) { continue; } #if 0 /* * Hack - do not overwrite loud sounds with quiet ones, * unless some time has passed */ if (c_ptr->cost + c_ptr->when > n + flow_n) continue; #endif /* Save the time-stamp */ c_ptr->when = flow_n; /* Save the flow cost */ c_ptr->cost = n; /* Enqueue that entry */ temp_y[flow_tail] = y; temp_x[flow_tail] = x; /* Advance the queue */ if (++flow_tail == TEMP_MAX) flow_tail = 0; /* Hack -- Overflow by forgetting new entry */ if (flow_tail == flow_head) flow_tail = old_head; } } } /* * Hack -- map a region ala "magic mapping" */ void map_area(void) { int py = p_ptr->py; int px = p_ptr->px; int i, x, y; int y1, y2, x1, x2; int xx, yy; cave_type *c_ptr; pcave_type *pc_ptr; /* Pick an area to map */ y1 = py - MAX_DETECT - randint1(10); y2 = py + MAX_DETECT + randint1(10); x1 = px - MAX_DETECT - randint1(20); x2 = px + MAX_DETECT + randint1(20); /* Speed -- shrink to fit legal bounds */ if (y1 < p_ptr->min_hgt) y1 = p_ptr->min_hgt; if (y2 > p_ptr->max_hgt - 1) y2 = p_ptr->max_hgt - 1; if (x1 < p_ptr->min_wid) x1 = p_ptr->min_wid; if (x2 > p_ptr->max_wid - 1) x2 = p_ptr->max_wid - 1; /* Scan that area */ for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { c_ptr = area(x, y); pc_ptr = parea(x, y); /* All non-walls are "checked" */ if (cave_floor_grid(c_ptr)) { /* Memorize known walls */ for (i = 0; i < 8; i++) { xx = x + ddx_ddd[i]; yy = y + ddy_ddd[i]; if (!in_boundsp(xx, yy)) continue; c_ptr = area(xx, yy); pc_ptr = parea(xx, yy); /* Memorize walls */ if (cave_wall_grid(c_ptr)) { /* Memorize the walls */ remember_grid(c_ptr, pc_ptr); /* Notice the change */ lite_spot(xx, yy); } } } } } /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* * Light up the dungeon using "clairvoyance" * * This function "illuminates" every grid in the dungeon, memorizes all * "objects", memorizes all grids as with magic mapping, and, under the * standard option settings (not view_torch_grids) memorizes all floor * grids too. */ void wiz_lite(void) { int i, y, x; object_type *o_ptr; chg_virtue(V_KNOWLEDGE, 1); chg_virtue(V_ENLIGHTEN, 1); /* Detect monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Repair visibility later */ p_ptr->change |= (PC_REPAIR); /* Hack -- Detect monster */ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW); /* Update the monster */ update_mon(i, FALSE); } /* Scan all normal grids */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Memorize the grid */ remember_grid(c_ptr, pc_ptr); /* Remember items on the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Memorize */ o_ptr->info |= OB_SEEN; } OBJ_ITT_END; /* Notice the change */ lite_spot(x, y); } } /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Remember to fix up the viewability */ p_ptr->change |= (PC_WIZ_LITE); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* * Fix up the dungeon after illumination. */ void change_wiz_lite(void) { int x, y; cave_type *c_ptr; /* Scan all normal grids */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { c_ptr = area(x, y); /* Forget memorized floor grids from view_torch_grids */ if (!(c_ptr->info & (CAVE_GLOW)) && !view_torch_grids && !cave_mem_grid(c_ptr)) { forget_grid(parea(x, y)); } /* Notice the change */ note_spot(x, y); } } /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* * Forget the dungeon map (ala "Thinking of Maud..."). */ void wiz_dark(void) { int i, y, x; /* Forget every grid */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); object_type *o_ptr; /* Process the grid */ forget_grid(pc_ptr); /* Forget items on the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Forget the object */ o_ptr->info &= ~(OB_SEEN); } OBJ_ITT_END; } } /* Forget all fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr = &fld_list[i]; /* Skip dead fields */ if (!f_ptr->t_idx) continue; /* Forget the object */ f_ptr->info &= (~FIELD_INFO_MARK); } /* Update the view */ p_ptr->update |= (PU_VIEW); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* * Change the "feat" flag for a grid, and notice/redraw the grid */ void cave_set_feat(int x, int y, int feat) { cave_type *c_ptr = area(x, y); /* Does los change? */ if (cave_floor_grid(c_ptr)) { /* Is new eat a wall grid? */ if (f_info[feat].flags & FF_BLOCK) { /* Update some things */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE); } } else { /* Is new feat a floor grid? */ if (!(f_info[feat].flags & FF_BLOCK)) { /* Update some things */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE); } } /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Change the feature */ c_ptr->feat = feat; /* Notice + Redraw */ if (character_dungeon) note_spot(x, y); /* Hack - if under player, notice change */ if ((x == p_ptr->px) && (y == p_ptr->py)) { parea(x, y)->feat = feat; /* Draw change */ lite_spot(x, y); } } zangband/src/cmd1.c0000755000000000000000000015204210250356274013132 0ustar rootroot/* File: cmd1.c */ /* Purpose: Movement commands (part 1) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" #define MAX_VAMPIRIC_DRAIN 100 /* * Calculate the deadliness based on the attack power. * This function takes into account bounds checking, * and then just looks at the deadliness_conversion * table in tables.c * * This number is 'inflated' by 100 from the eventual * multiplier to the damage dealt. */ int deadliness_calc(int attack_power) { /* Calculate effect of deadliness - linearly */ int result = (attack_power * 5) + 100; /* Really powerful minus yields zero damage */ if (result < 0) result = 0; return (result); } /* Helper function to calculate the average damage a weapon will do */ long avg_dam(int attack_power, int dice_num, int dice_sides) { /* Calculate damage per dice x 100 */ long temp = dice_sides * deadliness_calc(attack_power); /* Add one to take into account dice formula, and return avg*200 */ return (dice_num * (temp + 100)); } /* * Determine if the player "hits" a monster (normal combat). * Note -- Always miss 5%, always hit 5%, otherwise random. */ static bool test_hit_combat(int chance, int ac, int vis) { int k; /* Percentile dice */ k = randint0(100); /* Hack -- Instant hit. Chance to miss removed in Oangband because * of the way monster ACs now work (fewer truly easy targets). */ if (k < 5) return (TRUE); /* Invisible monsters are harder to hit */ if (!vis) chance = chance / 2; /* Power competes against armor. */ if ((chance > 0) && (randint0(chance) >= ac)) return (TRUE); /* Assume miss */ return (FALSE); } /* At the moment this function is exactly the same as the melee function */ bool test_hit_fire(int chance, int ac, int vis) { int k; /* Percentile dice */ k = randint0(102); /* Hack -- Instant hit. Chance to miss removed in Oangband because * of the way monster ACs now work (fewer truly easy targets). */ if (k < 5) return (TRUE); /* Invisible monsters are harder to hit */ if (!vis) chance = chance / 2; /* Power competes against armor. */ if ((chance > 0) && (randint0(chance) >= ac)) return (TRUE); /* Assume miss */ return (FALSE); } /* * Calculation of critical hits by the player in hand-to-hand combat. -LM- */ static int critical_melee(int chance, int sleeping_bonus, cptr m_name, object_type *o_ptr) { int power = (chance + sleeping_bonus); int bonus = 0; int psi_hit = FALSE; if ((FLAG(p_ptr, TR_PSI_CRIT)) && (p_ptr->csp >= PSI_COST) && (randint(100) < 80)) { psi_hit = TRUE; } if (FLAG(p_ptr, TR_STRANGE_LUCK)) power = power * 3 / 2; /* Test for critical hit. */ if (randint1(power + 240) <= power || (psi_hit && randint(100) < 20)) { /* * Encourage the player to make sneak attacks on * sleeping monsters. -LM- */ if ((sleeping_bonus) && (p_ptr->rp.pclass == CLASS_ROGUE)) msgf("You ruthlessly sneak attack!"); /* Determine level of critical hit. */ if (randint0(90) == 0) bonus = 800; if (randint0(40) == 0) bonus = 500; else if (randint0(12) == 0) bonus = 300; else if (randint0(3) == 0) bonus = 200; else bonus = 100; /* Criticals with PSI_HIT weapons do about 47% more damage */ if (psi_hit) { int psi_bonus; if (one_in_(12) && p_ptr->csp >= PSI_COST * 3) psi_bonus = 3; else if (one_in_(3) && p_ptr->csp >= PSI_COST * 2) psi_bonus = 2; else psi_bonus = 1; bonus *= psi_bonus; p_ptr->csp -= PSI_COST * psi_bonus; p_ptr->redraw |= (PR_MANA); p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); } if ((bonus <= 100) || ((o_ptr->tval == TV_HAFTED) && (o_ptr->sval == SV_WHIP))) { msgf(MSGT_HIT, "You strike %s.", m_name); } else if (bonus <= 200) { if ((o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM)) msgf(MSGT_HIT, "You hack at %s.", m_name); else msgf(MSGT_HIT, "You bash %s.", m_name); } else if (bonus <= 300) { if ((o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM)) msgf(MSGT_HIT, "You slash %s.", m_name); else msgf(MSGT_HIT, "You pound %s.", m_name); } else if (bonus <= 500) { if ((o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM)) msgf(MSGT_HIT, "You gouge %s!", m_name); else msgf(MSGT_HIT, "You bludgeon %s!", m_name); } else { msgf(MSGT_HIT, "You *smite* %s!", m_name); } } /* * If the blow is not a critical hit, display the default attack * message and apply the standard multiplier. */ else { msgf(MSGT_HIT, "You hit %s.", m_name); } return (bonus); } /* * Critical hits (by player) * * Factor in weapon weight, total plusses, player level. * This is used by the old Monk attack routine * mutuations, + a few of the ego weapons. */ static s16b critical_norm(int weight, int plus, int dam) { int power, k; /* Extract "blow" power */ power = (weight + ((p_ptr->to_h + plus) * 5) + (p_ptr->lev * 3)); if (FLAG(p_ptr, TR_STRANGE_LUCK)) power = power * 3 / 2; /* Chance */ if (randint1(5000) <= power) { k = weight + randint1(650); if (k < 400) { msgf("It was a good hit!"); dam = 2 * dam + 5; } else if (k < 700) { msgf("It was a great hit!"); dam = 2 * dam + 10; } else if (k < 900) { msgf("It was a superb hit!"); dam = 3 * dam + 15; } else if (k < 1300) { msgf("It was a *GREAT* hit!"); dam = 3 * dam + 20; } else { msgf("It was a *SUPERB* hit!"); dam = ((7 * dam) / 2) + 25; } } return (dam); } /* * Extract the deadliness bonus from slays and brands. * * Note that most brands and slays are +200%, except Slay Animal (+100%), * Slay Evil (+100%), and Kill dragon (+400%). * * Currently brands add +200%, but it might be more interesting to make * them +100% that is cumulative with other slays and brands. */ int tot_dam_aux(const object_type *o_ptr, const monster_type *m_ptr) { /* * mult is based at 100 = +0 deadliness. */ int mult = 100; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Extract the flags */ u32b f1 = o_ptr->flags[0]; /* Some "weapons" and "ammo" do extra damage */ switch (o_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_DIGGING: { /* Slay Animal */ if ((f1 & TR0_SLAY_ANIMAL) && (FLAG(r_ptr, RF_ANIMAL))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_ANIMAL; } if (mult < 200) mult = 200; } /* Slay Evil */ if ((f1 & TR0_SLAY_EVIL) && (FLAG(r_ptr, RF_EVIL))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_EVIL; } if (mult < 200) mult = 200; } /* Slay Undead */ if ((f1 & TR0_SLAY_UNDEAD) && (FLAG(r_ptr, RF_UNDEAD))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_UNDEAD; } if (mult < 300) mult = 300; } /* Slay Demon */ if ((f1 & TR0_SLAY_DEMON) && (FLAG(r_ptr, RF_DEMON))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_DEMON; } if (mult < 300) mult = 300; } /* Slay Orc */ if ((f1 & TR0_SLAY_ORC) && (FLAG(r_ptr, RF_ORC))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_ORC; } if (mult < 300) mult = 300; } /* Slay Troll */ if ((f1 & TR0_SLAY_TROLL) && (FLAG(r_ptr, RF_TROLL))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_TROLL; } if (mult < 300) mult = 300; } /* Slay Giant */ if ((f1 & TR0_SLAY_GIANT) && (FLAG(r_ptr, RF_GIANT))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_GIANT; } if (mult < 300) mult = 300; } /* Slay Dragon */ if ((f1 & TR0_SLAY_DRAGON) && (FLAG(r_ptr, RF_DRAGON))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_DRAGON; } if (mult < 300) mult = 300; } /* Execute Dragon */ if ((f1 & TR0_KILL_DRAGON) && (FLAG(r_ptr, RF_DRAGON))) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_DRAGON; } if (mult < 500) mult = 500; } /* Brand (Acid) */ if (f1 & TR0_BRAND_ACID) { /* Notice immunity */ if (FLAG(r_ptr, RF_IM_ACID)) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_IM_ACID; } } /* Otherwise, take the damage */ else { if (mult < 300) mult = 300; } } /* Brand (Elec) */ if (f1 & TR0_BRAND_ELEC) { /* Notice immunity */ if (FLAG(r_ptr, RF_IM_ELEC)) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_IM_ELEC; } } /* Otherwise, take the damage */ else { if (mult < 300) mult = 300; } } /* Brand (Fire) */ if (f1 & TR0_BRAND_FIRE) { /* Notice immunity */ if (FLAG(r_ptr, RF_IM_FIRE)) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_IM_FIRE; } } /* Otherwise, take the damage */ else { if (mult < 300) mult = 300; } } /* Brand (Cold) */ if (f1 & TR0_BRAND_COLD) { /* Notice immunity */ if (FLAG(r_ptr, RF_IM_COLD)) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_IM_COLD; } } /* Otherwise, take the damage */ else { if (mult < 300) mult = 300; } } /* Brand (Poison) */ if (f1 & TR0_BRAND_POIS) { /* Notice immunity */ if (FLAG(r_ptr, RF_IM_POIS)) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_IM_POIS; } } /* Otherwise, take the damage */ else { if (mult < 300) mult = 300; } } break; } } /* Return the total damage */ return (mult); } /* * Search for hidden things */ void search(void) { int py = p_ptr->py; int px = p_ptr->px; int y, x, chance; int old_count; /* * Temp variables to store x and y because the * count_traps() function stomps on its inputs. */ int tx, ty; cave_type *c_ptr; object_type *o_ptr; /* Start with base search ability */ chance = p_ptr->skills[SKILL_SNS]; /* Penalize various conditions */ if (p_ptr->tim.blind || no_lite()) chance = chance / 10; if (p_ptr->tim.confused || p_ptr->tim.image) chance = chance / 10; /* Search the nearby grids, which are always in bounds */ for (y = (py - 1); y <= (py + 1); y++) { for (x = (px - 1); x <= (px + 1); x++) { /* Sometimes, notice things */ if (randint0(100) < chance) { /* do not search outside the wilderness */ if (!in_bounds2(x, y)) continue; /* Access the grid */ c_ptr = area(x, y); /* Save x and y into temp variables */ tx = x; ty = y; /* Count number of visible traps next to player */ old_count = count_traps(&tx, &ty, TRUE); /* Look for invisible traps */ if (field_detect_type(c_ptr, FTYPE_TRAP)) { /* Save x and y into temp variables */ tx = x; ty = y; /* See if the number of known traps has changed */ if (old_count != count_traps(&tx, &ty, TRUE)) { /* Message */ msgf("You have found a trap."); /* Disturb */ disturb(FALSE); } } /* Secret door */ if (c_ptr->feat == FEAT_SECRET) { /* Message */ msgf("You have found a secret door."); /* Pick a door */ create_closed_door(x, y); /* Disturb */ disturb(FALSE); } /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Skip non-chests */ if (o_ptr->tval != TV_CHEST) continue; /* Skip non-trapped chests */ if (!chest_traps[o_ptr->pval]) continue; /* Identify once */ if (!object_known_p(o_ptr)) { /* Message */ msgf("You have discovered a trap on the chest!"); /* Know the trap */ object_known(o_ptr); /* Notice it */ disturb(FALSE); } } OBJ_ITT_END; } } } } /* * Determine if the object can be picked up, and has "=g" in its inscription. */ bool auto_pickup_okay(const object_type *o_ptr) { cptr s; /* It can't be carried */ if (!inven_carry_okay(o_ptr)) return (FALSE); /* No inscription */ if (!o_ptr->inscription) return (FALSE); /* Find a '=' */ s = strchr(quark_str(o_ptr->inscription), '='); /* Process inscription */ while (s) { /* Auto-pickup on "=g" */ if (s[1] == 'g') return (TRUE); /* Find another '=' */ s = strchr(s + 1, '='); } /* Don't auto pickup */ return (FALSE); } /* * Helper routine for py_pickup(). * * Add the given dungeon object to the character's inventory. * * Delete the object afterwards. */ void py_pickup_aux(object_type *o_ptr) { object_type *j_ptr; int slot; /* Duplicate the object */ j_ptr = object_dup(o_ptr); /* Delete the old object */ delete_dungeon_object(o_ptr); /* Carry the object */ j_ptr = inven_carry(j_ptr); /* * Note: we have just made an empty slot in o_list * so j_ptr should never be NULL after inven_carry() */ /* Get slot number */ slot = get_item_position(p_ptr->inventory, j_ptr); /* Message */ msgf("You have %v (%c).", OBJECT_FMT(j_ptr, TRUE, 3), I2A(slot)); } /* * Player "wants" to pick up an object or gold. * Note that we ONLY handle things that can be picked up. * See "move_player()" for handling of other things. * * If the easy floor option is true: * Make the player carry everything in a grid * * If "pickup" is FALSE then only gold will be picked up */ void carry(int pickup) { int py = p_ptr->py; int px = p_ptr->px; char o_name[256]; object_type *o_ptr, *fo_ptr = NULL; int floor_num = 0; int can_pickup = 0; /* Recenter the map around the player */ verify_panel(); /* Scan the pile of objects */ OBJ_ITT_START (area(px, py)->o_idx, o_ptr) { /* Describe the object */ object_desc(o_name, o_ptr, TRUE, 3, 256); /* Hack -- disturb */ disturb(FALSE); /* Pick up gold */ if (o_ptr->tval == TV_GOLD) { /* Message */ msgf("You have found %ld gold pieces worth of %s.", (long)o_ptr->pval, o_name); sound(SOUND_SELL); /* Collect the gold */ p_ptr->au += o_ptr->pval; /* Redraw gold */ p_ptr->redraw |= (PR_GOLD); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Delete the gold */ delete_dungeon_object(o_ptr); /* Check the next object */ continue; } /* Test for auto-pickup */ if (auto_pickup_okay(o_ptr)) { /* Hack - Pick up the object */ py_pickup_aux(o_ptr); /* Check the next object */ continue; } /* The old pick-up routine is here */ if (!easy_floor) { /* Describe the object */ if (!pickup) { msgf("You find %s.", o_name); } /* Note that the pack is too full */ else if (!inven_carry_okay(o_ptr)) { msgf("You have no room for %s.", o_name); } /* Pick up the item (if requested and allowed) */ else { /* Hack -- query every item */ if (carry_query_flag) { int i; /* Paranoia XXX XXX XXX */ message_flush(); /* Prompt for it */ prtf(0, 0, "Pick up %s? [y/n/k] ", o_name); /* Get an acceptable answer */ while (TRUE) { i = inkey(); if (quick_messages) break; if (i == ESCAPE) break; if (strchr("YyNnKk", i)) break; bell("Illegal pick-up command!"); } /* Erase the prompt */ clear_msg(); if ((i == 'Y') || (i == 'y')) { /* Hack - Pick up the object */ py_pickup_aux(o_ptr); } if ((i == 'K') || (i == 'k')) { /* Physically try to destroy the item */ if (destroy_item_aux(o_ptr, o_ptr->number)) { delete_dungeon_object(o_ptr); } } } /* Attempt to pick up an object. */ else { /* Hack - Pick up the object */ py_pickup_aux(o_ptr); } } /* Get the next object */ continue; } /* Count non-gold objects that can be picked up. */ if (inven_carry_okay(o_ptr)) { can_pickup++; } /* Count non-gold objects */ if (floor_num != 22) floor_num++; /* Hack - Remember this object */ fo_ptr = o_ptr; } OBJ_ITT_END; /* There are no non-gold objects left */ if (!floor_num) return; /* Mention the number of objects */ if (!pickup) { /* One object */ if (floor_num == 1) { /* Message */ msgf("You find %v.", OBJECT_FMT(fo_ptr, TRUE, 3)); } /* Multiple objects */ else { /* Message */ msgf("You find a pile of %d items.", floor_num); } /* Done */ return; } /* The player has no room for anything on the floor. */ if (!can_pickup) { /* One object */ if (floor_num == 1) { /* Message */ msgf("You have no room for %v.", OBJECT_FMT(fo_ptr, TRUE, 3)); } /* Multiple objects */ else { /* Message */ msgf("You have no room for any of the objects on the floor."); } /* Done */ return; } /* One object */ if (floor_num == 1) { /* Hack -- query every object */ if (carry_query_flag) { int i; /* Paranoia XXX XXX XXX */ message_flush(); /* Prompt for it */ prtf(0, 0,"Pick up %v? [y/n/k] ", OBJECT_FMT(fo_ptr, TRUE, 3)); /* Get an acceptable answer */ while (TRUE) { i = inkey(); if (quick_messages) break; if (i == ESCAPE) break; if (strchr("YyNnKk", i)) break; bell("Illegal pick-up command!"); } /* Erase the prompt */ clear_msg(); if ((i == 'Y') || (i == 'y')) { /* Pick up the object */ py_pickup_aux(fo_ptr); } if ((i == 'K') || (i == 'k')) { /* Physically try to destroy the item */ if (destroy_item_aux(fo_ptr, fo_ptr->number)) { delete_dungeon_object(fo_ptr); } } /* Done */ return; } /* Pick up the object */ py_pickup_aux(fo_ptr); /* Done */ return; } /* Restrict the choices */ item_tester_hook = inven_carry_okay; /* Get an object */ o_ptr = get_item("Get which item? ", "You see nothing there.", (USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Pick up the object */ py_pickup_aux(o_ptr); } static void touch_zap_player(const monster_type *m_ptr) { int aura_damage; monster_race *r_ptr = &r_info[m_ptr->r_idx]; if (FLAG(r_ptr, RF_AURA_FIRE)) { if (!(FLAG(p_ptr, TR_IM_FIRE))) { char aura_dam[80]; aura_damage = damroll(1 + (r_ptr->hdice * 2 / 26), 1 + (r_ptr->level / 17)); /* Hack -- Get the "died from" name */ monster_desc(aura_dam, m_ptr, 0x88, 80); msgf("You are suddenly very hot!"); take_hit(resist(aura_damage, res_fire_lvl), aura_dam); r_ptr->r_flags[1] |= RF1_AURA_FIRE; handle_stuff(); } } if (FLAG(r_ptr, RF_AURA_COLD)) { if (!(FLAG(p_ptr, TR_IM_COLD))) { char aura_dam[80]; aura_damage = damroll(1 + (r_ptr->hdice * 2 / 26), 1 + (r_ptr->level / 17)); /* Hack -- Get the "died from" name */ monster_desc(aura_dam, m_ptr, 0x88, 80); msgf("You are suddenly very cold!"); take_hit(resist(aura_damage, res_cold_lvl), aura_dam); r_ptr->r_flags[2] |= RF2_AURA_COLD; handle_stuff(); } } if (FLAG(r_ptr, RF_AURA_ELEC)) { if (!(FLAG(p_ptr, TR_IM_ELEC))) { char aura_dam[80]; aura_damage = damroll(1 + (r_ptr->hdice * 2 / 26), 1 + (r_ptr->level / 17)); /* Hack -- Get the "died from" name */ monster_desc(aura_dam, m_ptr, 0x88, 80); msgf("You get zapped!"); take_hit(resist(aura_damage, res_elec_lvl), aura_dam); r_ptr->r_flags[1] |= RF1_AURA_ELEC; handle_stuff(); } } } static void natural_attack(s16b m_idx, int attack, bool *fear, bool *mdeath) { int k, bonus, chance; int n_weight; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80]; int dss, ddd; cptr atk_desc; switch (attack) { case MUT2_SCOR_TAIL: { dss = 3; ddd = 7; n_weight = 5; atk_desc = "tail"; break; } case MUT2_HORNS: { dss = 2; ddd = 6; n_weight = 15; atk_desc = "horns"; break; } case MUT2_BEAK: { dss = 2; ddd = 4; n_weight = 5; atk_desc = "beak"; break; } case MUT2_TRUNK: { dss = 1; ddd = 4; n_weight = 35; atk_desc = "trunk"; break; } case MUT2_TENTACLES: { dss = 2; ddd = 5; n_weight = 5; atk_desc = "tentacles"; break; } default: { dss = ddd = n_weight = 1; atk_desc = "undefined body part"; } } /* Extract monster name (or "it") */ monster_desc(m_name, m_ptr, 0, 80); /* Calculate the "attack quality" */ bonus = p_ptr->to_h; chance = (p_ptr->skills[SKILL_THN] + (bonus * BTH_PLUS_ADJ)); /* Test for hit */ if ((!(FLAG(r_ptr, RF_QUANTUM)) || one_in_(2)) && test_hit_combat(chance, r_ptr->ac, m_ptr->ml)) { /* Sound */ sound(SOUND_HIT); msgf(MSGT_HIT, "You hit %s with your %s.", m_name, atk_desc); msg_effect(MSG_HIT, m_ptr->r_idx); k = damroll(ddd, dss); k = critical_norm(n_weight, p_ptr->to_h, k); /* Apply the player damage bonuses */ k += p_ptr->to_d; /* No negative damage */ if (k < 0) k = 0; /* Modify the damage */ k = mon_damage_mod(m_ptr, k, 0); /* Complex message */ if (p_ptr->state.wizard) { msgf("You do %d (out of %d) damage.", k, m_ptr->hp); } /* Anger the monster */ if (k > 0) anger_monster(m_ptr); /* Damage, check for fear and mdeath */ switch (attack) { case MUT2_SCOR_TAIL: { project(0, 0, m_ptr->fx, m_ptr->fy, k, GF_POIS, PROJECT_KILL); *mdeath = (m_ptr->r_idx == 0); break; } case MUT2_HORNS: { *mdeath = mon_take_hit(m_idx, k, fear, NULL); break; } case MUT2_BEAK: { *mdeath = mon_take_hit(m_idx, k, fear, NULL); break; } case MUT2_TRUNK: { *mdeath = mon_take_hit(m_idx, k, fear, NULL); break; } case MUT2_TENTACLES: { *mdeath = mon_take_hit(m_idx, k, fear, NULL); break; } default: { *mdeath = mon_take_hit(m_idx, k, fear, NULL); } } make_noise(4); touch_zap_player(m_ptr); } /* Player misses */ else { /* Sound */ sound(SOUND_MISS); /* Message */ msgf(MSGT_MISS, "You miss %s.", m_name); msg_effect(MSG_MISS, m_ptr->r_idx); } } static bool monster_bash(int *blows, int sleeping_bonus, const cave_type *c_ptr, bool *fear, cptr m_name) { int bash_chance, bash_quality, bash_dam; monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; object_type *o_ptr; /* No shield on arm, no bash. */ if (!p_ptr->equipment[EQUIP_ARM].k_idx) { bash_chance = 0; } /* * Players do not bash if they could otherwise take advantage of special * bonuses against sleeping monsters, or if the monster is low-level. */ else if ((sleeping_bonus) || (r_ptr->hdice * 2 < p_ptr->lev / 2)) { bash_chance = 0; } /* Bashing chance depends on melee Skill, Dex, and a class level bonus. */ else bash_chance = p_ptr->skills[SKILL_THN] + (adj_dex_th[p_ptr->stat[A_DEX].ind]) - 128 + (((p_ptr->rp.pclass == CLASS_WARRIOR) || (p_ptr->rp.pclass == CLASS_PALADIN) || (p_ptr->rp.pclass == CLASS_WARRIOR_MAGE) || (p_ptr->rp.pclass == CLASS_CHAOS_WARRIOR)) ? p_ptr->lev : 0); /* Players bash more often when they see a real need. */ if (bash_chance) { o_ptr = &p_ptr->equipment[EQUIP_WIELD]; if ((!o_ptr->k_idx) && (p_ptr->rp.pclass != CLASS_MONK)) bash_chance *= 3; else if ((o_ptr->dd * o_ptr->ds * (*blows)) < (p_ptr->equipment[EQUIP_ARM].dd * p_ptr->equipment[EQUIP_ARM].ds * 3)) bash_chance *= 2; } /* Try to get in a shield bash. */ if (bash_chance > randint0(240 + r_ptr->hdice * 18)) { o_ptr = &p_ptr->equipment[EQUIP_ARM]; msgf("You get in a shield bash!"); /* Calculate attack quality, a mix of momentum and accuracy. */ bash_quality = p_ptr->skills[SKILL_THN] + (p_ptr->rp.wt / 8) + (p_ptr->total_weight / 80) + (o_ptr->weight / 3); /* Calculate damage. Big shields are deadly. */ bash_dam = damroll(o_ptr->dd, o_ptr->ds); /* Multiply by quality and experience factors */ bash_dam *= bash_quality / 20 + p_ptr->lev / 7; /* Strength bonus. */ bash_dam += (adj_str_td[p_ptr->stat[A_STR].ind] - 128); /* Paranoia. */ if (bash_dam > 125) bash_dam = 125; /* Encourage the player to keep wearing that heavy shield. */ if (randint1(bash_dam) > 30 + randint1(bash_dam / 2)) msgf("WHAMM!"); /* Complex message */ if (p_ptr->state.wizard) { msgf("You do %d (out of %d) damage.", bash_dam, m_ptr->hp); } /* Damage, check for fear and death. */ if (mon_take_hit(c_ptr->m_idx, bash_dam, fear, NULL)) { /* Fight's over. */ return (TRUE); } /* Stunning. */ if (bash_quality + p_ptr->lev > randint1(200 + r_ptr->hdice * 16)) { msgf("%^s is stunned.", m_name); m_ptr->stunned += randint0(p_ptr->lev / 5) + 4; if (m_ptr->stunned > 24) m_ptr->stunned = 24; } /* Confusion. */ if (bash_quality + p_ptr->lev > randint1(300 + r_ptr->hdice * 12) && !(FLAG(r_ptr, RF_NO_CONF))) { msgf("%^s appears confused.", m_name); m_ptr->confused += randint0(p_ptr->lev / 5) + 4; } /* The player will sometimes stumble. */ if ((30 + adj_dex_th[p_ptr->stat[A_DEX].ind] - 128) < randint1(60)) *blows -= randint1(*blows); } make_noise(4); /* Monster is not dead */ return (FALSE); } /* * The monk special attacks and effects. */ static void monk_attack(monster_type *m_ptr, long *k, cptr m_name) { int special_effect = 0, stun_effect = 0, times = 0; const martial_arts *ma_ptr = &ma_blows[0], *old_ptr = &ma_blows[0]; int resist_stun = 0; monster_race *r_ptr = &r_info[m_ptr->r_idx]; if (FLAG(r_ptr, RF_UNIQUE)) resist_stun += 88; if (FLAG(r_ptr, RF_NO_CONF)) resist_stun += 44; if (FLAG(r_ptr, RF_NO_SLEEP)) resist_stun += 44; if ((FLAG(r_ptr, RF_UNDEAD)) || (FLAG(r_ptr, RF_NONLIVING))) resist_stun += 88; /* Attempt 'times' */ for (times = 0; times < (p_ptr->lev < 7 ? 1 : p_ptr->lev / 7); times++) { do { ma_ptr = &ma_blows[randint0(MAX_MA)]; } while ((ma_ptr->min_level > p_ptr->lev) || (randint1(p_ptr->lev) < ma_ptr->chance)); /* keep the highest level attack available we found */ if ((ma_ptr->min_level > old_ptr->min_level) && !p_ptr->tim.stun && !p_ptr->tim.confused) { old_ptr = ma_ptr; if (p_ptr->state.wizard && cheat_xtra) { msgf("Attack re-selected."); } } else { ma_ptr = old_ptr; } } *k = damroll(ma_ptr->dd, ma_ptr->ds); if (ma_ptr->effect == MA_KNEE) { if (FLAG(r_ptr, RF_MALE)) { msgf(MSGT_HIT, "You hit %s in the groin with your knee!", m_name); msg_effect(MSG_HIT, m_ptr->r_idx); sound(SOUND_PAIN); special_effect = MA_KNEE; } else { msgf(MSGT_HIT, ma_ptr->desc, m_name); msg_effect(MSG_HIT, m_ptr->r_idx); } } else if (ma_ptr->effect == MA_SLOW) { if (!((FLAG(r_ptr, RF_NEVER_MOVE)) || strchr("~#{}.UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char))) { msgf(MSGT_HIT, "You kick %s in the ankle.", m_name); msg_effect(MSG_HIT, m_ptr->r_idx); special_effect = MA_SLOW; } else { msgf(MSGT_HIT, ma_ptr->desc, m_name); msg_effect(MSG_HIT, m_ptr->r_idx); } } else { if (ma_ptr->effect) { stun_effect = rand_range(ma_ptr->effect / 2, ma_ptr->effect); } msgf(MSGT_HIT, ma_ptr->desc, m_name); msg_effect(MSG_HIT, m_ptr->r_idx); } *k = critical_norm(p_ptr->lev * randint1(10), ma_ptr->min_level, *k); if ((special_effect == MA_KNEE) && (*k < m_ptr->hp)) { msgf("%^s moans in agony!", m_name); stun_effect = rand_range(8, 20); resist_stun /= 3; } else if ((special_effect == MA_SLOW) && (*k < m_ptr->hp)) { if (!(FLAG(r_ptr, RF_UNIQUE)) && (randint1(p_ptr->lev) > r_ptr->hdice * 2) && m_ptr->mspeed > 60) { msgf("%^s starts limping slower.", m_name); m_ptr->mspeed -= 10; } } if (stun_effect && (*k < m_ptr->hp)) { if (p_ptr->lev > randint1(r_ptr->hdice * 2 + resist_stun + 10)) { if (m_ptr->stunned) msgf("%^s is more stunned.", m_name); else msgf("%^s is stunned.", m_name); m_ptr->stunned += stun_effect; } } } /* * Player attacks a (poor, defenseless) creature -RAK- * * If no "weapon" is available, then "punch" the monster one time. */ void py_attack(int x, int y) { /* Sides dice, also total damage. */ long k; /* The whole and fractional damage dice and their resulting damage. */ int slay; /* blow count */ int num = 0; /* Bonus to attack if monster is sleeping, for certain classes. */ int sleeping_bonus = 0; /* Bonus to effective monster ac if it can take cover in terrain. */ int terrain_bonus = 0; int bonus, chance, total_deadliness; int blows; cave_type *c_ptr = area(x, y); monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; object_type *o_ptr; char m_name[80]; bool fear = FALSE; bool mdeath = FALSE; bool do_quake = FALSE; bool drain_msg = TRUE; int drain_result = 0, drain_heal = 0; int drain_total = 0; s16b ghoul_paral = -1; bool ghoul_hack = FALSE; bool no_extra = FALSE; /* Access the weapon */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Disturb the player */ disturb(FALSE); /* Initial blows available. */ blows = p_ptr->num_blow; /* Prepare for ghoul paralysis? */ if (!(o_ptr->k_idx) && (FLAG(p_ptr, TR_GHOUL_TOUCH))) { ghoul_paral = 0; /* Prevent bogus falls asleep messages */ if (!(m_ptr->csleep)) ghoul_hack = TRUE; } /* It is not honorable etc to attack helpless victims -- in most cases */ if ((m_ptr->csleep) && (ghoul_paral > -1)) { chg_virtue(V_COMPASSION, -1); if (!(p_ptr->rp.pclass == CLASS_ROGUE)) chg_virtue(V_HONOUR, -1); } if (p_ptr->rp.pclass == CLASS_ROGUE) { if (m_ptr->csleep && m_ptr->ml) { /* Can't backstab creatures that we can't see, right? */ sleeping_bonus = 10 + 2 * p_ptr->lev / 5; } else if (m_ptr->monfear && m_ptr->ml) { sleeping_bonus = 5 + p_ptr->lev / 5; } } /* Disturb the monster */ m_ptr->csleep = 0; /* Extract monster name (or "it") */ monster_desc(m_name, m_ptr, 0, 80); /* Auto-Recall if possible and visible */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Track a new monster */ if (m_ptr->ml) health_track(c_ptr->m_idx); /* Look to see if we've spotted a mimic */ if (m_ptr->smart & SM_MIMIC) { /* We've spotted it */ msgf("You've found %v!", MONSTER_FMT(m_ptr, 0x88)); /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now if visible */ if (m_ptr->ml) update_mon_vis(m_ptr->r_idx, 1); } /* Stop if friendly and visible */ if (!is_hostile(m_ptr) && !p_ptr->tim.stun && !p_ptr->tim.confused && !p_ptr->tim.image && !((p_ptr->muta2 & MUT2_BERS_RAGE) && p_ptr->tim.shero) && m_ptr->ml) { if (!o_ptr->xtra_name) { msgf("You stop to avoid hitting %s.", m_name); return; } /* Mega-hack */ if (!(streq(quark_str(o_ptr->xtra_name), "'Stormbringer'"))) { msgf("You stop to avoid hitting %s.", m_name); return; } msgf("Your black blade greedily attacks %s!", m_name); chg_virtue(V_INDIVIDUALISM, 1); chg_virtue(V_HONOUR, -1); chg_virtue(V_JUSTICE, -1); chg_virtue(V_COMPASSION, -1); } /* Handle player fear */ if (p_ptr->tim.afraid) { /* Message */ if (m_ptr->ml) msgf("You are too afraid to attack %s!", m_name); else msgf("There is something scary in your way!"); /* Done */ return; } /* Using a weapon can cause it to become cursed */ if ((FLAG(o_ptr, TR_AUTO_CURSE)) && !(FLAG(o_ptr, TR_CURSED)) && (randint(100) < 10)) { msgf("Your weapon glows black."); SET_FLAG(o_ptr, TR_CURSED); o_ptr->feeling = FEEL_NONE; } /* Monsters in rubble can take advantage of cover. -LM- */ if (c_ptr->feat == FEAT_RUBBLE) { terrain_bonus = r_ptr->ac / 7 + 5; } /* Monsters in water are vulnerable. -LM- */ else if (c_ptr->feat == FEAT_DEEP_WATER) { terrain_bonus -= r_ptr->ac / 5; } /* Attempt to shield bash the monster */ if (monster_bash(&blows, sleeping_bonus, c_ptr, &fear, m_name)) return; /* Initialize. */ total_deadliness = p_ptr->to_d + o_ptr->to_d; /* Calculate the "attack quality". As BTH_PLUS_ADJ has been reduced * to 1, base skill and modifiers to skill are given equal weight. -LM- */ bonus = p_ptr->to_h + o_ptr->to_h; chance = (p_ptr->skills[SKILL_THN] + (bonus * BTH_PLUS_ADJ)); apply_object_trigger(TRIGGER_ATTACK, o_ptr, "iiii", LUA_RETURN(chance), LUA_RETURN(terrain_bonus), LUA_RETURN(total_deadliness), LUA_RETURN(sleeping_bonus)); /* Attack once for each legal blow */ while (num++ < blows) { int progressive = (num - 1) * -15; /* Test for hit */ if (test_hit_combat(chance + sleeping_bonus + progressive, r_ptr->ac + terrain_bonus, m_ptr->ml)) { int drain_power = 0; bool do_poly = FALSE; bool do_tele = FALSE; bool do_conf = FALSE; /* Sound */ sound(SOUND_HIT); /* Hack -- bare hands do one damage */ k = 1; /* Select a chaotic effect (50% chance) */ if ((FLAG(o_ptr, TR_CHAOTIC)) && (one_in_(2))) { if (one_in_(10)) chg_virtue(V_CHANCE, 1); if (randint1(5) < 3) { /* Vampiric (20%) */ if (monster_living(r_ptr)) drain_power = 4; else drain_power = 0; } else if (one_in_(250)) { /* Quake (0.12%) */ do_quake = TRUE; } else if (!one_in_(10)) { /* Confusion (26.892%) */ do_conf = TRUE; } else if (one_in_(2)) { /* Teleport away (1.494%) */ do_tele = TRUE; } else { /* Polymorph (1.494%) */ do_poly = TRUE; } } /* Vampiric drain */ if (FLAG(o_ptr, TR_VAMPIRIC)) { /* Only drain "living" monsters */ if (monster_living(r_ptr)) drain_power = 4; else drain_power = 0; } /* Save current hp */ drain_result = m_ptr->hp; /* Ghoul paralysis */ if ((ghoul_paral > -1) && !(FLAG(r_ptr, RF_NO_SLEEP)) && (r_ptr->hdice * 2 < randint0(1 + ((p_ptr->lev) * 2)))) { ghoul_paral += 25 + randint1(p_ptr->lev / 2); } /* Monk attack? */ if ((p_ptr->rp.pclass == CLASS_MONK) && (!o_ptr->k_idx)) { /* Make a special monk attack */ monk_attack(m_ptr, &k, m_name); } /* Handle normal weapon */ else if (o_ptr->k_idx) { int vorpal_chance = (o_ptr->flags[0] & TR0_VORPAL) ? 2 : 0; int multiplier = deadliness_calc(total_deadliness); /* base damage dice. */ k = o_ptr->ds; /* Add deadliness bonus from slays/brands */ slay = (tot_dam_aux(o_ptr, m_ptr) - 100); multiplier += slay; /* Add deadliness bonus from critical hits */ multiplier += critical_melee(chance, sleeping_bonus, m_name, o_ptr); /* * Convert total Deadliness into a percentage, and apply * it as a bonus or penalty. (100x inflation) */ k *= multiplier; /* * Get the whole number of dice sides by deflating, * and then get total dice damage. */ k = damroll(o_ptr->dd, k / 100 + (randint0(100) < (k % 100) ? 1 : 0)); /* Add in extra effect due to slays */ k += slay / 20; /* Apply scripted effects */ apply_object_trigger(TRIGGER_HIT, o_ptr, ":iiibbbbi", LUA_RETURN(ghoul_paral), LUA_RETURN(drain_power), LUA_RETURN(vorpal_chance), LUA_RETURN(do_quake), LUA_RETURN(do_conf), LUA_RETURN(do_tele), LUA_RETURN(do_poly), LUA_RETURN_NAMED(k, "dam")); /* hack -- check for earthquake. */ if ((FLAG(p_ptr, TR_IMPACT)) && ((k > 50) || one_in_(7))) { do_quake = TRUE; } /* * All of these artifact-specific effects * should be pythonized. */ if (vorpal_chance && one_in_(3 * vorpal_chance)) { /* * The vorpal blade does average: * (e+2)/3 x normal damage. * A normal weapon with the vorpal flag does average: * e-3/2 x normal damage. * Note: this has changed from before - the vorpal blade * has been toned down because of the oangband based * combat. */ int mult = 2; int inc_chance = 2 * vorpal_chance; if (vorpal_chance < 2) { msgf("Your Vorpal Blade goes snicker-snack!"); } else { msgf("Your weapon cuts deep into %s!", m_name); } /* Try to increase the damage */ while (one_in_(inc_chance)) { mult++; inc_chance++; } k *= mult; /* Ouch! */ if (k > m_ptr->hp) { msgf("You cut %s in half!", m_name); } else { switch (mult) { case 2: { msgf("You gouge %s!", m_name); break; } case 3: { msgf("You maim %s!", m_name); break; } case 4: { msgf("You carve %s!", m_name); break; } case 5: { msgf("You cleave %s!", m_name); break; } case 6: { msgf("You smite %s!", m_name); break; } case 7: { msgf("You eviscerate %s!", m_name); break; } default: { msgf("You shred %s!", m_name); } } } } } /* Bare hands and not a monk */ else { msgf("You %s %s.", ((p_ptr->rp.prace == RACE_GHOUL) ? "claw" : "punch"), m_name); } /* No negative damage */ if (k < 0) k = 0; /* Modify the damage */ k = mon_damage_mod(m_ptr, k, 0); /* Complex message */ if (p_ptr->state.wizard) { msgf("You do %d (out of %d) damage.", k, m_ptr->hp); } /* Damage, check for fear and death */ if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL)) { /* Hack -- High-level warriors can spread their attacks out * among weaker foes. -LM- */ if (((p_ptr->rp.pclass == CLASS_WARRIOR) || (p_ptr->rp.pclass == CLASS_CHAOS_WARRIOR)) && (p_ptr->lev > 39) && (num < p_ptr->num_blow) && (p_ptr->state.energy_use)) { p_ptr->state.energy_use = p_ptr->state.energy_use * num / p_ptr->num_blow; } mdeath = TRUE; break; } /* Anger the monster */ if (k > 0) anger_monster(m_ptr); touch_zap_player(m_ptr); /* * Are we draining it? A little note: If the monster is * dead, the drain does not work... */ if (drain_power) { int max_drain = drain_power * (MAX_VAMPIRIC_DRAIN / 4); int drain_left; /* Calculate the difference */ drain_result -= m_ptr->hp; /* Did we really hurt it? */ if (drain_result > 0) { drain_heal = damroll(drain_power, drain_result / 6); drain_left = max_drain - drain_total; if (cheat_xtra) { msgf("Draining left: %d", drain_left); } if (drain_left > 0) { /* Cap life gain */ if (drain_heal > drain_left) drain_heal = drain_left; /* Add to total */ drain_total += drain_heal; if (drain_msg) { msgf("Your weapon drains life from %s!", m_name); drain_msg = FALSE; } /* We get to keep some of it! */ hp_player(drain_heal); } } } /* Confusion attack */ if (p_ptr->state.confusing || do_conf) { /* Cancel glowing hands */ if (p_ptr->state.confusing) { p_ptr->state.confusing = FALSE; msgf("Your hands stop glowing."); p_ptr->redraw |= (PR_STATUS); } /* Confuse the monster */ if (FLAG(r_ptr, RF_NO_CONF)) { if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_NO_CONF; } msgf("%^s is unaffected.", m_name); } else if (randint0(100) < r_ptr->hdice * 2) { msgf("%^s is unaffected.", m_name); } else { msgf("%^s appears confused.", m_name); m_ptr->confused += 10 + randint0(p_ptr->lev) / 5; } } else if (do_tele) { bool resists_tele = FALSE; if (FLAG(r_ptr, RF_RES_TELE)) { if (FLAG(r_ptr, RF_UNIQUE)) { if (m_ptr->ml) r_ptr->r_flags[2] |= RF2_RES_TELE; msgf("%^s is unaffected!", m_name); resists_tele = TRUE; } else if (r_ptr->hdice * 2 > randint1(100)) { if (m_ptr->ml) r_ptr->r_flags[2] |= RF2_RES_TELE; msgf("%^s resists!", m_name); resists_tele = TRUE; } } if (!resists_tele) { msgf("%^s disappears!", m_name); teleport_away(c_ptr->m_idx, 50); num = p_ptr->num_blow + 1; /* Can't hit it anymore! */ no_extra = TRUE; } } else if (do_poly && cave_floor_grid(c_ptr) && (randint1(90) > r_ptr->hdice * 2)) { if (!(FLAG(r_ptr, RF_UNIQUE)) && !(FLAG(r_ptr, RF_BR_CHAO)) && !(FLAG(r_ptr, RF_QUESTOR))) { if (polymorph_monster(x, y)) { msgf("%^s changes!", m_name); /* Hack -- Get new monster */ m_ptr = &m_list[c_ptr->m_idx]; /* Oops, we need a different name... */ monster_desc(m_name, m_ptr, 0, 80); /* Hack -- Get new race */ r_ptr = &r_info[m_ptr->r_idx]; fear = FALSE; } else { msgf("%^s is unaffected.", m_name); } } } make_noise(4); } /* Player misses */ else { /* Sound */ sound(SOUND_MISS); /* Message */ msgf(MSGT_MISS, "You miss %s.", m_name); msg_effect(MSG_MISS, m_ptr->r_idx); } } /* Mutations which yield extra 'natural' attacks */ if (!no_extra) { if ((p_ptr->muta2 & MUT2_HORNS) && !mdeath) natural_attack(c_ptr->m_idx, MUT2_HORNS, &fear, &mdeath); if ((p_ptr->muta2 & MUT2_BEAK) && !mdeath) natural_attack(c_ptr->m_idx, MUT2_BEAK, &fear, &mdeath); if ((p_ptr->muta2 & MUT2_SCOR_TAIL) && !mdeath) natural_attack(c_ptr->m_idx, MUT2_SCOR_TAIL, &fear, &mdeath); if ((p_ptr->muta2 & MUT2_TRUNK) && !mdeath) natural_attack(c_ptr->m_idx, MUT2_TRUNK, &fear, &mdeath); if ((p_ptr->muta2 & MUT2_TENTACLES) && !mdeath) natural_attack(c_ptr->m_idx, MUT2_TENTACLES, &fear, &mdeath); } /* * Hack -- delay paralysis (otherwise, consecutive attacks would make * it counterproductive */ if (ghoul_paral > 0) { /* Message */ if (ghoul_hack && (m_ptr->ml)) { msgf("%^s falls asleep!", m_name); } /* Sleep */ m_ptr->csleep += 5 * ghoul_paral; ghoul_paral = 0; } /* Hack -- delay fear messages */ else if (fear && m_ptr->ml) { flee_message(m_name, m_ptr->r_idx); } if (drain_total > 0) { if (one_in_(4)) chg_virtue(V_VITALITY, 1); } /* Mega-Hack -- apply earthquake brand */ if (do_quake) { int px = p_ptr->px; int py = p_ptr->py; (void)earthquake(px, py, 10); } } static void summon_pattern_vortex(int x, int y) { int i; /* Find the pattern vortex */ for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Summon it */ if (mon_name_cont(r_ptr, "Pattern") && (r_ptr->d_char == 'v')) { if (summon_named_creature(x, y, i, FALSE, FALSE, FALSE)) { msgf("You hear a bell chime."); } } } } static bool pattern_seq(int c_x, int c_y, int n_x, int n_y) { cave_type *c1_ptr, *c2_ptr; c1_ptr = area(c_x, c_y); c2_ptr = area(n_x, n_y); if (!cave_pattern_grid(c1_ptr) && !cave_pattern_grid(c2_ptr)) return TRUE; if (c2_ptr->feat == FEAT_PATTERN_START) { if (!cave_pattern_grid(c1_ptr) && !p_ptr->tim.confused && !p_ptr->tim.stun && !p_ptr->tim.image) { if (get_check ("If you start walking the Pattern, you must walk the whole way. Ok? ")) return TRUE; else return FALSE; } else return TRUE; } else if ((c2_ptr->feat == FEAT_PATTERN_OLD) || (c2_ptr->feat == FEAT_PATTERN_END) || (c2_ptr->feat == FEAT_PATTERN_XTRA2)) { if (cave_pattern_grid(c1_ptr)) { return TRUE; } else { if (get_check("Really step onto the Pattern here? ")) { take_hit(100, "Stepping onto the Pattern"); if (one_in_(3)) summon_pattern_vortex(n_x, n_y); return TRUE; } else { return FALSE; } } } else if ((c2_ptr->feat == FEAT_PATTERN_XTRA1) || (c1_ptr->feat == FEAT_PATTERN_XTRA1)) { return TRUE; } else if (c1_ptr->feat == FEAT_PATTERN_START) { if (cave_pattern_grid(c2_ptr)) return TRUE; else { if (get_check("Really step off of the Pattern? ")) { take_hit(10, "Stepping off of the Pattern"); if (one_in_(6)) summon_pattern_vortex(n_x, n_y); return TRUE; } return FALSE; } } else if ((c1_ptr->feat == FEAT_PATTERN_OLD) || (c1_ptr->feat == FEAT_PATTERN_END) || (c1_ptr->feat == FEAT_PATTERN_XTRA2)) { if (!cave_pattern_grid(c2_ptr)) { if (get_check("Really step off of the Pattern? ")) { take_hit(100, "Stepping off of the Pattern"); if (one_in_(2)) summon_pattern_vortex(n_x, n_y); return TRUE; } else { return FALSE; } } else { return TRUE; } } else { if (!cave_pattern_grid(c1_ptr)) { if (get_check("Really step onto the Pattern here? ")) { take_hit(25, "Stepping onto the Pattern"); if (one_in_(6)) summon_pattern_vortex(n_x, n_y); return TRUE; } else { return FALSE; } } else { byte ok_move = FEAT_PATTERN_START; switch (c1_ptr->feat) { case FEAT_PATTERN_1: { ok_move = FEAT_PATTERN_2; break; } case FEAT_PATTERN_2: { ok_move = FEAT_PATTERN_3; break; } case FEAT_PATTERN_3: { ok_move = FEAT_PATTERN_4; break; } case FEAT_PATTERN_4: { ok_move = FEAT_PATTERN_1; break; } default: { if (p_ptr->state.wizard) msgf("Funny Pattern walking, %d.", *area(c_x, c_y)); /* Goof-up */ return TRUE; } } if ((c2_ptr->feat == ok_move) || (c2_ptr->feat == c1_ptr->feat)) return TRUE; else { if (!cave_pattern_grid(c2_ptr) && get_check("Really step off of the Pattern? ")) { take_hit(50, "Stepping off of the Pattern"); if (one_in_(3)) summon_pattern_vortex(n_x, n_y); return TRUE; } else if (cave_pattern_grid(c2_ptr) && get_check("Really stray from the proper path? ")) { take_hit(25, "Walking backwards along the Pattern"); if (one_in_(5)) summon_pattern_vortex(n_x, n_y); return TRUE; } return FALSE; } } } } /* * Move player in the given direction, with the given "pickup" flag. * * This routine should (probably) always induce energy expenditure. * * Note that moving will *always* take a turn, and will *always* hit * any monster which might be in the destination grid. Previously, * moving into walls was "free" and did NOT hit invisible monsters. */ void move_player(int dir, int do_pickup) { int py = p_ptr->py; int px = p_ptr->px; int y, x; cave_type *c_ptr; pcave_type *pc_ptr; monster_type *m_ptr; object_type *o_ptr; char m_name[80]; bool p_can_pass_walls = FALSE; bool stormbringer = FALSE; bool p_cant_pass_fields = FALSE; bool oktomove = TRUE; /* Find the result of moving */ y = py + ddy[dir]; x = px + ddx[dir]; /* Do not exit the wilderness-area */ if (!p_ptr->depth) { if (!in_bounds2(x, y)) { /* Do not leave the wilderness */ msgf("You can not leave the wilderness."); p_ptr->state.energy_use = 0; return; } } /* Examine the destination */ c_ptr = area(x, y); pc_ptr = parea(x, y); /* Get the monster */ m_ptr = &m_list[c_ptr->m_idx]; /* Access weapon */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Mega-hack */ if (o_ptr->xtra_name) { if (streq(quark_str(o_ptr->xtra_name), "'Stormbringer'")) { stormbringer = TRUE; } } /* Player can not walk through "walls"... */ /* unless in Shadow Form */ if (p_ptr->tim.wraith_form || (FLAG(p_ptr, TR_PASS_WALL))) p_can_pass_walls = TRUE; /* Never walk through permanent features */ if (cave_perma_grid(c_ptr) && cave_wall_grid(c_ptr)) { p_can_pass_walls = FALSE; } /* Get passability of field(s) if there */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) p_cant_pass_fields = TRUE; /* Hack -- attack monsters */ if (c_ptr->m_idx && (m_ptr->ml || cave_floor_grid(c_ptr) || p_can_pass_walls)) { /* Attack -- only if we can see it OR it is not in a wall */ if (!is_hostile(m_ptr) && !(p_ptr->tim.confused || p_ptr->tim.image || !m_ptr->ml || p_ptr->tim.stun || ((p_ptr->muta2 & MUT2_BERS_RAGE) && p_ptr->tim.shero)) && (pattern_seq(px, py, x, y)) && ((cave_floor_grid(c_ptr)) || p_can_pass_walls)) { m_ptr->csleep = 0; /* Extract monster name (or "it") */ monster_desc(m_name, m_ptr, 0, 80); /* Auto-Recall if possible and visible */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Track a new monster */ if (m_ptr->ml) health_track(c_ptr->m_idx); /* displace? */ if (stormbringer && (randint1(1000) > 666)) { py_attack(x, y); } else if (cave_floor_grid(area(px, py)) || FLAG(&r_info[m_ptr->r_idx], RF_PASS_WALL)) { msgf("You push past %s.", m_name); m_ptr->fy = py; m_ptr->fx = px; area(px, py)->m_idx = c_ptr->m_idx; c_ptr->m_idx = 0; update_mon(area(px, py)->m_idx, TRUE); } else { msgf("%^s is in your way!", m_name); p_ptr->state.energy_use = 0; oktomove = FALSE; } /* now continue on to 'movement' */ } else { py_attack(x, y); oktomove = FALSE; } } /* Fields can block movement */ else if (p_cant_pass_fields) { msgf("You can't cross that!"); p_ptr->state.running = 0; oktomove = FALSE; } /* * Player can move through trees and * has effective -10 speed * Rangers can move without penality */ else if ((c_ptr->feat == FEAT_TREES) || (c_ptr->feat == FEAT_PINE_TREE) || (c_ptr->feat == FEAT_SNOW_TREE) || (c_ptr->feat == FEAT_MOUNTAIN) || (c_ptr->feat == FEAT_SNOW_MOUNTAIN) || (c_ptr->feat == FEAT_OBELISK) || (c_ptr->feat == FEAT_BOULDER)) { oktomove = TRUE; if (!FLAG(p_ptr, TR_WILD_WALK)) p_ptr->state.energy_use += 10; } /* Disarm a visible trap */ else if ((do_pickup != easy_disarm) && is_visible_trap(c_ptr)) { (void)do_cmd_disarm_aux(c_ptr, dir); return; } else if (cave_floor_grid(c_ptr)) { oktomove = TRUE; } /* Closed door */ else if (c_ptr->feat == FEAT_CLOSED) { /* Pass through the door? */ if (p_can_pass_walls) { /* Automatically open the door? */ if (easy_open && !do_cmd_open_aux(x, y)) { oktomove = FALSE; /* Disturb the player */ disturb(FALSE); } } else { oktomove = FALSE; /* Disturb the player */ disturb(FALSE); /* Notice things in the dark */ if ((pc_ptr->feat != c_ptr->feat) && !player_can_see_grid(pc_ptr)) { msgf("You feel a closed door blocking your way."); remember_grid(c_ptr, pc_ptr); lite_spot(x, y); } /* Notice things */ else { /* Try to open a door if is there */ if (easy_open) { (void)do_cmd_open_aux(x, y); return; } msgf("There is a closed door blocking your way."); if (!(p_ptr->tim.confused || p_ptr->tim.stun || p_ptr->tim.image)) p_ptr->state.energy_use = 0; } /* Sound */ sound(SOUND_HITWALL); } } /* Player can not walk through "walls" unless in wraith form... */ else if (!p_can_pass_walls) { oktomove = FALSE; /* Disturb the player */ disturb(FALSE); /* Notice things in the dark */ if ((pc_ptr->feat != c_ptr->feat) && !player_can_see_grid(pc_ptr)) { msgf(MSGT_HITWALL, "You feel something blocking your way."); remember_grid(c_ptr, pc_ptr); lite_spot(x, y); } /* Notice things */ else { /* Rubble */ if (c_ptr->feat == FEAT_RUBBLE) { msgf(MSGT_HITWALL, "There is rubble blocking your way."); if (!(p_ptr->tim.confused || p_ptr->tim.stun || p_ptr->tim.image)) p_ptr->state.energy_use = 0; /* * Well, it makes sense that you lose time bumping into * a wall _if_ you are confused, stunned or blind; but * typing mistakes should not cost you a turn... */ } /* Jungle */ else if (c_ptr->feat == FEAT_JUNGLE) { msgf(MSGT_HITWALL, "The jungle is impassable."); if (!(p_ptr->tim.confused || p_ptr->tim.stun || p_ptr->tim.image)) p_ptr->state.energy_use = 0; } /* Pillar */ else if (c_ptr->feat == FEAT_PILLAR) { msgf(MSGT_HITWALL, "There is a pillar blocking your way."); if (!(p_ptr->tim.confused || p_ptr->tim.stun || p_ptr->tim.image)) p_ptr->state.energy_use = 0; } /* Wall (or secret door) */ else { msgf(MSGT_HITWALL, "There is a wall blocking your way."); if (!(p_ptr->tim.confused || p_ptr->tim.stun || p_ptr->tim.image)) p_ptr->state.energy_use = 0; } } /* Sound */ sound(SOUND_HITWALL); } /* Normal movement */ if (!pattern_seq(px, py, x, y)) { if (!(p_ptr->tim.confused || p_ptr->tim.stun || p_ptr->tim.image)) { p_ptr->state.energy_use = 0; } /* To avoid a loop with running */ disturb(FALSE); oktomove = FALSE; } /* Normal movement */ if (oktomove) { int oy, ox; /* Save old location */ oy = py; ox = px; /* Move the player */ p_ptr->py = y; p_ptr->px = x; /* Notice movement */ Term_move_player(); if (!p_ptr->depth) { /* Scroll wilderness */ p_ptr->wilderness_x = x; p_ptr->wilderness_y = y; move_wild(); } /* Redraw new spot */ lite_spot(x, y); /* Redraw old spot */ lite_spot(ox, oy); /* Process fields under the player. */ field_script(area(x, y), FIELD_ACT_PLAYER_ENTER, ""); /* Sound */ /* sound(SOUND_WALK); */ /* Check for new panel (redraw map) */ verify_panel(); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); /* Update the monsters */ p_ptr->update |= (PU_DISTANCE); /* Warn about traps */ /* * Is the disturb_traps option set and out of detection range? */ if (disturb_traps && !(pc_ptr->player & GRID_DTCT) && p_ptr->state.detected) { /* We are out of range */ msgf("Out of trap detection range."); /* Reset the detection flag */ p_ptr->state.detected = FALSE; /* Disturb the player */ disturb(FALSE); } /* Spontaneous Searching */ if ((p_ptr->skills[SKILL_FOS] >= 50) || one_in_(50 - p_ptr->skills[SKILL_FOS])) { search(); } /* Continuous Searching */ if (p_ptr->state.searching) { search(); } /* Handle "objects" */ carry(do_pickup != always_pickup); make_noise(2); } } zangband/src/cmd2.c0000755000000000000000000014657410250356274013150 0ustar rootroot/* File: cmd2.c */ /* Purpose: Movement commands (part 2) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Go up one level */ void do_cmd_go_up(void) { cave_type *c_ptr; /* Player grid */ c_ptr = area(p_ptr->px, p_ptr->py); if (c_ptr->feat == FEAT_LESS) { /* * I'm experimenting without this... otherwise the monsters get to * act first when we go up stairs, theoretically resulting in a * possible insta-death. */ p_ptr->state.energy_use = 0; /* Success */ msgf(MSGT_STAIRS, "You enter a maze of up staircases."); /* Create a way back */ p_ptr->state.create_down_stair = TRUE; /* Go up */ move_dun_level(-1); /* * Hack XXX XXX Take some time * * This will need to be rethought in multiplayer */ turn += 100; } else { msgf("I see no up staircase here."); return; } } /* * Go down one level */ void do_cmd_go_down(void) { cave_type *c_ptr; /* Player grid */ c_ptr = area(p_ptr->px, p_ptr->py); if (c_ptr->feat != FEAT_MORE) { msgf("I see no down staircase here."); return; } else { p_ptr->state.energy_use = 0; /* Success */ msgf(MSGT_STAIRS, "You enter a maze of down staircases."); /* Create a way back */ p_ptr->state.create_up_stair = TRUE; /* Go down */ move_dun_level(1); /* * Hack XXX XXX Take some time * * This will need to be rethought in multiplayer */ turn += 100; } } /* * Simple command to "search" for one turn */ void do_cmd_search(void) { /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Take a turn */ p_ptr->state.energy_use = 100; /* Search */ search(); } /* * Hack -- toggle search mode */ void do_cmd_toggle_search(void) { /* Stop searching */ if (p_ptr->state.searching) { /* Clear the searching flag */ p_ptr->state.searching = FALSE; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the state */ p_ptr->redraw |= (PR_STATE); } /* Start searching */ else { /* Set the searching flag */ p_ptr->state.searching = TRUE; /* Update stuff */ p_ptr->update |= (PU_BONUS); /* Redraw stuff */ p_ptr->redraw |= (PR_STATE | PR_SPEED); } } /* * Determine if a grid contains a chest */ static object_type *chest_check(int x, int y) { cave_type *c_ptr = area(x, y); object_type *o_ptr; /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Check for chest */ if (o_ptr->tval == TV_CHEST) return (o_ptr); } OBJ_ITT_END; /* No chest */ return (0); } /* * Allocates objects upon opening a chest -BEN- * * Disperse treasures from the given chest, centered at (x,y). * * Small chests often contain "gold", while Large chests always contain * items. Wooden chests contain 2 items, Iron chests contain 4 items, * and Steel chests contain 6 items. The "value" of the items in a * chest is based on the "power" of the chest, which is in turn based * on the level on which the chest is generated. */ static void chest_death(int x, int y, object_type *o_ptr) { int number; byte sval, tval; bool small_chest; object_type *q_ptr; int level; /* * Pick type of item to find in the chest. * * Hack - chests are not on this list... * this prevents chests from nesting. */ switch (randint1(8)) { case 1: { /* Swords */ tval = TV_SWORD; sval = SV_ANY; break; } case 2: { /* Boots */ tval = TV_BOOTS; sval = SV_ANY; break; } case 3: { /* Rings */ tval = TV_RING; sval = SV_ANY; break; } case 4: { /* Staves */ tval = TV_STAFF; sval = SV_ANY; break; } case 5: { /* Potions */ tval = TV_POTION; sval = SV_ANY; break; } case 6: { /* Cloaks */ tval = TV_CLOAK; sval = SV_ANY; break; } case 7: { /* Rods */ tval = TV_ROD; sval = SV_ANY; break; } default: { /* Match anything */ tval = TV_GLOVES; sval = SV_ANY; } } /* Select only those types of object */ init_match_hook(tval, sval); /* Prepare allocation table */ get_obj_num_prep(kind_is_match); /* Small chests often hold "gold" */ small_chest = (o_ptr->sval < SV_CHEST_MIN_LARGE); /* Determine how much to drop (see above) */ number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2; /* Zero pval means empty chest */ if (!o_ptr->pval) number = 0; /* Determine the "value" of the items */ level = ABS(o_ptr->pval); /* Drop some objects (non-chests) */ for (; number > 0; --number) { /* Small chests often drop gold */ if (small_chest && one_in_(4)) { /* Make some gold */ q_ptr = make_gold(level, 0); } /* Otherwise drop an item */ else { /* Make a good themed object */ q_ptr = make_object(level, 15, NULL); if (!q_ptr) continue; } /* Drop it in the dungeon */ drop_near(q_ptr, -1, x, y); } /* Empty */ o_ptr->pval = 0; /* Known */ object_known(o_ptr); make_noise(2); } /* * Chests have traps too. * * Exploding chest destroys contents (and traps). * Note that the chest itself is never destroyed. */ static void chest_trap(int x, int y, object_type *o_ptr) { int i, trap; /* Ignore disarmed chests */ if (o_ptr->pval <= 0) return; /* Obtain the traps */ trap = chest_traps[o_ptr->pval]; /* Lose strength */ if (trap & (CHEST_LOSE_STR)) { msgf("A small needle has pricked you!"); take_hit(damroll(1, 4), "a poison needle"); (void)do_dec_stat(A_STR); } /* Lose constitution */ if (trap & (CHEST_LOSE_CON)) { msgf("A small needle has pricked you!"); take_hit(damroll(1, 4), "a poison needle"); (void)do_dec_stat(A_CON); } /* Poison */ if (trap & (CHEST_POISON)) { msgf("A puff of green gas surrounds you!"); (void)pois_dam(10, "poison", rand_range(10, 30)); } /* Paralyze */ if (trap & (CHEST_PARALYZE)) { msgf("A puff of yellow gas surrounds you!"); if (!(FLAG(p_ptr, TR_FREE_ACT))) { (void)inc_paralyzed(rand_range(10, 30)); } } /* Summon monsters */ if (trap & (CHEST_SUMMON)) { int num = rand_range(3, 5); msgf("You are enveloped in a cloud of smoke!"); for (i = 0; i < num; i++) { if (randint1(100) < p_ptr->depth) (void)activate_hi_summon(); else (void)summon_specific(0, x, y, p_ptr->depth, 0, TRUE, FALSE, FALSE); } } /* Explode */ if (trap & (CHEST_EXPLODE)) { msgf("There is a sudden explosion!"); msgf("Everything inside the chest is destroyed!"); o_ptr->pval = 0; sound(SOUND_EXPLODE); take_hit(damroll(5, 8), "an exploding chest"); } make_noise(2); } /* * Attempt to open the given chest at the given location * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_open_chest(int x, int y, object_type *o_ptr) { int i, j; bool flag = TRUE; bool more = FALSE; /* Take a turn */ p_ptr->state.energy_use = 100; /* Attempt to unlock it */ if (o_ptr->pval > 0) { /* Assume locked, and thus not open */ flag = FALSE; /* Get the "disarm" factor */ i = p_ptr->skills[SKILL_DIS]; /* Penalize some conditions */ if (p_ptr->tim.blind || no_lite()) i = i / 10; if (p_ptr->tim.confused || p_ptr->tim.image) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success -- May still have traps */ if (randint0(100) < j) { msgf(MSGT_OPENDOOR, "You have picked the lock."); gain_exp(1); flag = TRUE; } /* Failure -- Keep trying */ else { /* We may continue repeating */ more = TRUE; if (flush_failure) flush(); msgf(MSGT_LOCKPICK_FAIL, "You failed to pick the lock."); } } /* Allowed to open */ if (flag) { /* Apply chest traps, if any */ chest_trap(x, y, o_ptr); /* Let the Chest drop items */ chest_death(x, y, o_ptr); } /* Result */ return (more); } /* * Return TRUE if the given feature is an open door */ static bool is_open(int feat) { return (feat == FEAT_OPEN); } /* * Return TRUE if the given feature is a closed door */ static bool is_closed(int feat) { return (feat == FEAT_CLOSED); } /* * Return the number of traps around (or under) the character. */ int count_traps(int *x, int *y, bool under) { int d; int xx, yy; int count = 0; /* Count how many matches */ /* Check around (and under) the character */ for (d = 0; d < 9; d++) { /* if not searching under player continue */ if ((d == 8) && !under) continue; /* Extract adjacent (legal) location */ yy = p_ptr->py + ddy_ddd[d]; xx = p_ptr->px + ddx_ddd[d]; /* paranoia */ if (!in_bounds2(xx, yy)) continue; /* Not looking for this feature */ if (!is_visible_trap(area(xx, yy))) continue; /* OK */ ++count; /* Remember the location. Only useful if only one match */ *y = yy; *x = xx; } /* All done */ return count; } /* * Return the number of doors around (or under) the character. */ static int count_doors(int *x, int *y, bool (*test) (int feat), bool under) { int d; int xx, yy; int count = 0; /* Count how many matches */ /* Check around (and under) the character */ for (d = 0; d < 9; d++) { /* if not searching under player continue */ if ((d == 8) && !under) continue; /* Extract adjacent (legal) location */ yy = p_ptr->py + ddy_ddd[d]; xx = p_ptr->px + ddx_ddd[d]; /* paranoia */ if (!in_boundsp(xx, yy)) continue; /* Must have knowledge */ if (!(parea(xx, yy)->feat)) continue; /* Not looking for this feature */ if (!((*test) (area(xx, yy)->feat))) continue; /* OK */ ++count; /* Remember the location. Only useful if only one match */ *y = yy; *x = xx; } /* All done */ return count; } /* * Return the number of chests around (or under) the character. * If requested, count only trapped chests. */ static int count_chests(int *x, int *y, bool trapped) { int d, count; object_type *o_ptr; /* Count how many matches */ count = 0; /* Check around (and under) the character */ for (d = 0; d < 9; d++) { /* Extract adjacent (legal) location */ int yy = p_ptr->py + ddy_ddd[d]; int xx = p_ptr->px + ddx_ddd[d]; o_ptr = chest_check(xx, yy); /* No (visible) chest is there */ if (!o_ptr) continue; /* Already open */ if (o_ptr->pval == 0) continue; /* No (known) traps here */ if (trapped && (!object_known_p(o_ptr) || !chest_traps[o_ptr->pval])) continue; /* OK */ ++count; /* Remember the location. Only useful if only one match */ *y = yy; *x = xx; } /* All done */ return count; } /* * Convert an adjacent location to a direction. */ static int coords_to_dir(int x, int y) { int d[3][3] = { {7, 4, 1}, {8, 5, 2}, {9, 6, 3} }; int dy = y - p_ptr->py; int dx = x - p_ptr->px; /* Paranoia */ if (ABS(dx) > 1 || ABS(dy) > 1) return (0); return d[dx + 1][dy + 1]; } /* * Perform the basic "open" command on doors * * Assume destination is a closed/locked/jammed door * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ bool do_cmd_open_aux(int x, int y) { int i; cave_type *c_ptr; field_type *f_ptr; /* Take a turn */ p_ptr->state.energy_use = 100; /* Get requested grid */ c_ptr = area(x, y); /* Must be a closed door */ if (c_ptr->feat != FEAT_CLOSED) { /* Nope */ return (FALSE); } /* Get fields */ f_ptr = field_is_type(c_ptr, FTYPE_DOOR); /* If the door is locked / jammed */ if (f_ptr) { /* Get the "disarm" factor */ i = p_ptr->skills[SKILL_DIS]; /* Penalize some conditions */ if (p_ptr->tim.blind || no_lite()) i = i / 10; if (p_ptr->tim.confused || p_ptr->tim.image) i = i / 10; /* Success? */ if (!field_script_single(f_ptr, FIELD_ACT_INTERACT, "i", LUA_VAR_NAMED(i, "power"))) { /* Sound */ sound(SOUND_OPENDOOR); /* Gain experience, but not for the locked doors in town */ if (p_ptr->depth) gain_exp(1); } /* Failure */ else { /* Failure */ if (flush_failure) flush(); /* We may keep trying */ return (TRUE); } } /* Closed door */ else { /* Open the door */ cave_set_feat(x, y, FEAT_OPEN); /* Sound */ sound(SOUND_OPENDOOR); make_noise(3); } /* We know about the change */ note_spot(x, y); /* Done - no more to try. */ return (FALSE); } /* * Open a closed/locked/jammed door or a closed/locked chest. * * Unlocking a locked door/chest is worth one experience point. */ void do_cmd_open(void) { int y, x, dir; object_type *o_ptr; cave_type *c_ptr; bool more = FALSE; /* Option: Pick a direction */ if (easy_open) { int num_doors, num_chests; /* Count closed doors */ num_doors = count_doors(&x, &y, is_closed, TRUE); /* Count chests (locked) */ num_chests = count_chests(&x, &y, FALSE); /* See if only one target */ if ((num_doors + num_chests) == 1) { p_ptr->cmd.dir = coords_to_dir(x, y); } } /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Get requested location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; /* Get requested grid */ c_ptr = area(x, y); /* Check for chest */ o_ptr = chest_check(x, y); /* Nothing useful */ if (!((c_ptr->feat == FEAT_CLOSED) || o_ptr)) { /* Message */ msgf(MSGT_NOTHING_TO_OPEN, "You see nothing there to open."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Take a turn */ p_ptr->state.energy_use = 100; /* Message */ msgf("There is a monster in the way!"); /* Attack */ py_attack(x, y); } /* Handle chests */ else if (o_ptr) { /* Open the chest */ more = do_cmd_open_chest(x, y, o_ptr); } /* Handle doors */ else { /* Open the door */ more = do_cmd_open_aux(x, y); } } /* Cancel repeat unless we may continue */ if (!more) disturb(FALSE); } /* * Perform the basic "close" command * * Assume destination is an open/broken door * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_close_aux(int x, int y) { cave_type *c_ptr; bool more = FALSE; if ((x == p_ptr->px) && (y == p_ptr->py)) { /* You cannot close a door beneith yourself */ msgf("You cannot close it now."); /* No more */ return (more); } /* Take a turn */ p_ptr->state.energy_use = 100; /* Get grid and contents */ c_ptr = area(x, y); /* Broken door */ if (c_ptr->feat == FEAT_BROKEN) { /* Message */ msgf("The door appears to be broken."); } /* Open door */ else { /* Close the door */ cave_set_feat(x, y, FEAT_CLOSED); /* Sound */ sound(SOUND_SHUTDOOR); make_noise(3); } /* We know about the change */ note_spot(x, y); /* Result */ return (more); } /* * Close an open door. */ void do_cmd_close(void) { int y, x, dir; cave_type *c_ptr; bool more = FALSE; /* Option: Pick a direction */ if (easy_open) { /* Count open doors */ if (count_doors(&x, &y, is_open, FALSE) == 1) { p_ptr->cmd.dir = coords_to_dir(x, y); } } /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Get requested location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) { /* Message */ msgf("You see nothing there to close."); disturb(FALSE); return; } /* Get grid and contents */ c_ptr = area(x, y); /* Require open/broken door */ if ((c_ptr->feat != FEAT_OPEN) && (c_ptr->feat != FEAT_BROKEN)) { /* Message */ msgf("You see nothing there to close."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Take a turn */ p_ptr->state.energy_use = 100; /* Message */ msgf("There is a monster in the way!"); /* Attack */ py_attack(x, y); } /* Close the door */ else { /* Close the door */ more = do_cmd_close_aux(x, y); } } /* Cancel repeat unless we may continue */ if (!more) disturb(FALSE); } /* * Tunnel through wall. Assumes valid location. */ static bool twall(int x, int y, byte feat) { cave_type *c_ptr = area(x, y); /* Paranoia -- Require a wall or door or some such */ if (cave_floor_grid(c_ptr)) return (FALSE); /* Remove the feature */ cave_set_feat(x, y, feat); /* Result */ return (TRUE); } /* * Perform the basic "tunnel" command * * Assumes that the destination is a wall, a vein, a secret * door, or rubble. * * Assumes that no monster is blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_tunnel_aux(int x, int y) { bool more = FALSE; cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); int action; int dig = p_ptr->skills[SKILL_DIG]; field_type *f_ptr = field_script_find(c_ptr, FIELD_ACT_INTERACT_TEST, ":i", LUA_RETURN(action)); /* Take a turn */ p_ptr->state.energy_use = 100; /* Sound */ sound(SOUND_DIG); /* Must have knowledge */ if (!(pc_ptr->feat)) { /* Message */ msgf("You see nothing there."); /* Nope */ return (FALSE); } if (f_ptr && (action == ACT_TUNNEL)) { if (!field_script_single(f_ptr, FIELD_ACT_INTERACT, "i", LUA_VAR_NAMED(dig, "power"))) { /* Finished tunneling */ return (FALSE); } /* Keep on tunneling */ return (TRUE); } /* Must be a wall/door/etc */ if (cave_floor_grid(c_ptr) && !(cave_semi_grid(c_ptr))) { /* Message */ msgf("You see nothing there to tunnel."); /* Nope */ return (FALSE); } /* Titanium */ if (cave_perma_grid(c_ptr) && cave_wall_grid(c_ptr)) { msgf("This seems to be permanent rock."); } else if ((c_ptr->feat == FEAT_TREES) || (c_ptr->feat == FEAT_PINE_TREE)) { /* Chop Down */ if ((p_ptr->skills[SKILL_DIG] > 10 + randint0(400)) && twall(x, y, FEAT_GRASS)) { msgf("You have cleared away the trees."); chg_virtue(V_DILIGENCE, 1); chg_virtue(V_NATURE, -1); } /* Keep trying */ else { /* We may continue chopping */ msgf("You chop away at the tree."); more = TRUE; /* Occasional Search XXX XXX */ if (one_in_(4)) search(); } } else if (c_ptr->feat == FEAT_SNOW_TREE) { /* Chop Down */ if ((p_ptr->skills[SKILL_DIG] > 10 + randint0(400)) && twall(x, y, FEAT_SNOW)) { msgf("You have cleared away the trees."); chg_virtue(V_DILIGENCE, 1); chg_virtue(V_NATURE, -1); } /* Keep trying */ else { /* We may continue chopping */ msgf("You chop away at the tree."); more = TRUE; /* Occasional Search XXX XXX */ if (one_in_(4)) search(); } } /* Jungle */ else if (c_ptr->feat == FEAT_JUNGLE) { /* Chop Down */ if ((p_ptr->skills[SKILL_DIG] > 10 + randint0(800)) && twall(x, y, FEAT_BUSH)) { msgf("You have cleared away the jungle."); chg_virtue(V_DILIGENCE, 1); chg_virtue(V_NATURE, -2); } /* Keep trying */ else { /* We may continue chopping */ msgf("You chop away at the undergrowth."); more = TRUE; /* Occasional Search XXX XXX */ if (one_in_(4)) search(); } } /* Granite + mountain side */ else if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) || (c_ptr->feat == FEAT_MOUNTAIN) || (c_ptr->feat == FEAT_SNOW_MOUNTAIN) || (c_ptr->feat == FEAT_PILLAR)) { /* Tunnel */ if ((p_ptr->skills[SKILL_DIG] > 40 + randint0(1600)) && twall(x, y, the_floor())) { msgf("You have finished the tunnel."); chg_virtue(V_DILIGENCE, 1); chg_virtue(V_NATURE, -1); } /* Keep trying */ else { /* We may continue tunelling */ msgf("You tunnel into the granite wall."); more = TRUE; } } /* Quartz / Magma */ else if ((c_ptr->feat >= FEAT_MAGMA) && (c_ptr->feat <= FEAT_QUARTZ_K)) { bool okay; bool gold = FALSE; bool hard = FALSE; /* Found gold */ if (c_ptr->feat >= FEAT_MAGMA_K) gold = TRUE; /* Extract "quartz" flag XXX XXX XXX */ if ((c_ptr->feat - FEAT_MAGMA) & 0x01) hard = TRUE; /* Quartz */ if (hard) { okay = (p_ptr->skills[SKILL_DIG] > 20 + randint0(800)); } /* Magma */ else { okay = (p_ptr->skills[SKILL_DIG] > 10 + randint0(400)); } /* Success */ if (okay && twall(x, y, the_floor())) { /* Found treasure */ if (gold) { /* Place some gold */ place_gold(x, y); /* Message */ msgf("You have found something!"); } /* Found nothing */ else { /* Message */ msgf("You have finished the tunnel."); chg_virtue(V_DILIGENCE, 1); chg_virtue(V_NATURE, -1); } } /* Failure (quartz) */ else if (hard) { /* Message, continue digging */ msgf("You tunnel into the quartz vein."); more = TRUE; } /* Failure (magma) */ else { /* Message, continue digging */ msgf("You tunnel into the magma vein."); more = TRUE; } } /* Rubble */ else if (c_ptr->feat == FEAT_RUBBLE) { /* Remove the rubble */ if ((p_ptr->skills[SKILL_DIG] > randint0(200)) && twall(x, y, the_floor())) { /* Message */ msgf("You have removed the rubble."); /* Hack -- place an object */ if (p_ptr->depth && one_in_(10)) { /* Create a simple object */ place_object(x, y, FALSE, FALSE, 0); /* Observe new object */ if (player_can_see_grid(pc_ptr)) { msgf("You have found something!"); } } } else { /* Message, keep digging */ msgf("You dig in the rubble."); more = TRUE; } } /* Secret doors */ else if (c_ptr->feat >= FEAT_SECRET) { /* Tunnel */ if ((p_ptr->skills[SKILL_DIG] > 30 + randint0(1200)) && twall(x, y, the_floor())) { msgf("You have finished the tunnel."); } /* Keep trying */ else { /* We may continue tunelling */ msgf("You tunnel into the granite wall."); more = TRUE; /* Occasional Search XXX XXX */ if (one_in_(4)) search(); } } make_noise(4); /* Result */ return (more); } /* * Tunnels through "walls" (including rubble and closed doors) * * Note that you must tunnel in order to hit invisible monsters * in walls, though moving into walls still takes a turn anyway. * * Digging is very difficult without a "digger" weapon, but can be * accomplished by strong players using heavy weapons. */ void do_cmd_tunnel(void) { int y, x, dir; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Get a direction to tunnel, or Abort */ if (get_rep_dir(&dir)) { /* Get location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* Cannot escape the wilderness by tunneling */ if (!in_bounds2(x, y)) { /* Message */ msgf("You cannot tunnel outside the wilderness."); /* Do not repeat */ disturb(FALSE); /* exit */ return; } /* Get grid */ c_ptr = area(x, y); /* No tunnelling through doors */ if (c_ptr->feat == FEAT_CLOSED) { /* Message */ msgf("You cannot tunnel through doors."); } /* No tunnelling through air */ else if (cave_floor_grid(c_ptr) && !cave_semi_grid(c_ptr)) { /* Message */ msgf("You cannot tunnel through air."); } /* No tunneling through obelisks */ else if (c_ptr->feat == FEAT_OBELISK) { /* Message */ msgf("You cannot tunnel through that."); } /* A monster is in the way */ else if (c_ptr->m_idx) { /* Take a turn */ p_ptr->state.energy_use = 100; /* Message */ msgf("There is a monster in the way!"); /* Attack */ py_attack(x, y); } /* Try digging */ else { /* Tunnel through walls */ more = do_cmd_tunnel_aux(x, y); } } /* Cancel repetition unless we can continue */ if (!more) disturb(FALSE); } /* * Perform the basic "disarm" command * * Assume destination is a visible trap * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_disarm_chest(int x, int y, object_type *o_ptr) { int i, j; bool more = FALSE; /* Take a turn */ p_ptr->state.energy_use = 100; /* Get the "disarm" factor */ i = p_ptr->skills[SKILL_DIS]; /* Penalize some conditions */ if (p_ptr->tim.blind || no_lite()) i = i / 10; if (p_ptr->tim.confused || p_ptr->tim.image) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval; /* Always have a small chance of success */ if (j < 2) j = 2; /* Must find the trap first. */ if (!object_known_p(o_ptr)) { msgf("I don't see any traps."); } /* Already disarmed/unlocked */ else if (o_ptr->pval <= 0) { msgf("The chest is not trapped."); } /* No traps to find. */ else if (!chest_traps[o_ptr->pval]) { msgf("The chest is not trapped."); } /* Success (get a lot of experience) */ else if (randint0(100) < j) { msgf("You have disarmed the chest."); gain_exp(o_ptr->pval); o_ptr->pval = (0 - o_ptr->pval); } /* Failure -- Keep trying */ else if ((i > 5) && (randint1(i) > 5)) { /* We may keep trying */ more = TRUE; if (flush_failure) flush(); msgf("You failed to disarm the chest."); } /* Failure -- Set off the trap */ else { msgf("You set off a trap!"); sound(SOUND_FAIL); chest_trap(x, y, o_ptr); } /* Result */ return (more); } /* * Perform the basic "disarm" command * * Assume destination is a visible trap * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ bool do_cmd_disarm_aux(cave_type *c_ptr, int dir) { int i; field_type *f_ptr; field_thaum *t_ptr; bool more = FALSE; /* Get trap */ f_ptr = field_first_known(c_ptr, FTYPE_TRAP); /* This should never happen - no trap here to disarm */ if (!f_ptr) { msgf("Error condition: Trying to disarm a non-existant trap."); return (FALSE); } /* Take a turn */ p_ptr->state.energy_use = 100; /* Get type of trap */ t_ptr = &t_info[f_ptr->t_idx]; /* Get the "disarm" factor */ i = p_ptr->skills[SKILL_DIS]; /* Penalize some conditions */ if (p_ptr->tim.blind || no_lite()) i = i / 10; if (p_ptr->tim.confused || p_ptr->tim.image) i = i / 10; /* Success */ if (!field_script_single(f_ptr, FIELD_ACT_INTERACT, "i", LUA_VAR_NAMED(i, "power"))) { /* Message */ msgf("You have disarmed the %s.", t_ptr->name); } /* Failure -- Keep trying */ else if ((i > 5) && (randint1(i) > 5)) { /* Failure */ if (flush_failure) flush(); /* Message */ msgf("You failed to disarm the %s.", t_ptr->name); /* We may keep trying */ more = TRUE; } /* Failure -- Set off the trap */ else { /* Message */ msgf("You set off the %s!", t_ptr->name); /* Move the player onto the trap */ move_player(dir, easy_disarm); } /* Result */ return (more); } /* * Disarms a trap, or chest */ void do_cmd_disarm(void) { int y, x, dir; object_type *o_ptr; cave_type *c_ptr; bool more = FALSE; /* Option: Pick a direction */ if (easy_disarm) { int num_traps, num_chests; /* Count visible traps */ num_traps = count_traps(&x, &y, TRUE); /* Count chests (trapped) */ num_chests = count_chests(&x, &y, TRUE); /* See if only one target */ if (num_traps || num_chests) { bool too_many = (num_traps + num_chests > 1); if (!too_many) p_ptr->cmd.dir = coords_to_dir(x, y); } } /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Get a direction (or abort) */ if (get_rep_dir(&dir)) { /* Get location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) { /* Message */ msgf("You see nothing there to disarm."); disturb(FALSE); return; } /* Get grid and contents */ c_ptr = area(x, y); /* Check for chests */ o_ptr = chest_check(x, y); /* Disarm a trap */ if (!is_visible_trap(c_ptr) && !o_ptr) { /* Message */ msgf("You see nothing there to disarm."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Message */ msgf("There is a monster in the way!"); /* Attack */ py_attack(x, y); } /* Disarm chest */ else if (o_ptr) { /* Disarm the chest */ more = do_cmd_disarm_chest(x, y, o_ptr); } /* Disarm trap */ else { /* Disarm the trap */ more = do_cmd_disarm_aux(c_ptr, dir); } } /* Cancel repeat unless told not to */ if (!more) disturb(FALSE); } /* * Manipulate an adjacent grid in some way * * Attack monsters, tunnel through walls, disarm traps, open doors. * * Consider confusion XXX XXX XXX * * This command must always take a turn, to prevent free detection * of invisible monsters. */ void do_cmd_alter(void) { int y, x, dir; int action; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Get a direction */ if (get_rep_dir(&dir)) { /* Get location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) { /* Oops */ msgf("You attack the empty air."); disturb(FALSE); return; } /* Get grid */ c_ptr = area(x, y); /* Take a turn */ p_ptr->state.energy_use = 100; /* Attack monsters */ if (c_ptr->m_idx) { /* Attack */ py_attack(x, y); } else if (field_script_find(c_ptr, FIELD_ACT_INTERACT_TEST, ":i", LUA_RETURN(action))) { switch (action) { case ACT_TUNNEL: { /* Tunnel */ more = do_cmd_tunnel_aux(x, y); break; } case ACT_DISARM: { /* Disarm */ more = do_cmd_disarm_aux(c_ptr, dir); break; } case ACT_OPEN: { /* Unlock / open */ more = do_cmd_open_aux(x, y); break; } } } /* Open closed doors */ else if (c_ptr->feat == FEAT_CLOSED) { /* open */ more = do_cmd_open_aux(x, y); } /* Tunnel through walls */ else if (cave_wall_grid(c_ptr)) { /* Tunnel */ more = do_cmd_tunnel_aux(x, y); } /* Close open doors */ else if ((c_ptr->feat == FEAT_OPEN) || (c_ptr->feat == FEAT_BROKEN)) { /* close */ more = do_cmd_close_aux(x, y); } /* Oops */ else { /* Oops */ msgf("You attack the empty air."); } } /* Cancel repetition unless we can continue */ if (!more) disturb(FALSE); } /* * Find the index of some "spikes", if possible. * * XXX XXX XXX Let user choose a pile of spikes, perhaps? */ static object_type *get_spike(void) { object_type *o_ptr; /* Check every item in the pack */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Check the "tval" code */ if (o_ptr->tval == TV_SPIKE) { /* Save the spike index */ return (o_ptr); } } OBJ_ITT_END; /* Oops */ return (NULL); } /* * Jam a closed door with a spike * * This command may NOT be repeated */ void do_cmd_spike(void) { int dir; s16b y, x; object_type *o_ptr; cave_type *c_ptr; /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Get location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) { /* Message */ msgf("You see nothing there to spike."); disturb(FALSE); return; } /* Get grid and contents */ c_ptr = area(x, y); /* Require closed door */ if (c_ptr->feat != FEAT_CLOSED) { /* Message */ msgf("You see nothing there to spike."); disturb(FALSE); return; } /* Get a spike */ o_ptr = get_spike(); if (!o_ptr) { /* Message */ msgf("You have no spikes!"); } /* Is a monster in the way? */ else if (c_ptr->m_idx) { /* Take a turn */ p_ptr->state.energy_use = 100; /* Message */ msgf("There is a monster in the way!"); /* Attack */ py_attack(x, y); } /* Go for it */ else { /* Take a turn */ p_ptr->state.energy_use = 100; /* Successful jamming */ msgf("You jam the door with a spike."); /* Make a jammed door on the square */ make_lockjam_door(x, y, 1, TRUE); /* Use up, and describe, a single spike, from the bottom */ item_increase(o_ptr, -1); } } make_noise(4); } /* * Support code for the "Walk" command */ void do_cmd_walk(int pickup) { int dir; bool more = FALSE; /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Take a turn */ p_ptr->state.energy_use = 100; /* Actually move the character */ move_player(dir, pickup); /* Allow more walking */ more = TRUE; } /* Cancel repeat unless we may continue */ if (!more) disturb(FALSE); } /* * Start running. */ void do_cmd_run(void) { int dir; /* Hack -- no running when confused */ if (p_ptr->tim.confused) { msgf("You are too confused!"); return; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Hack -- Set the run counter */ p_ptr->state.running = (p_ptr->cmd.arg ? p_ptr->cmd.arg : 1000); /* First step */ run_step(dir); } } /* * Stay still. Search. Enter stores. * Pick up treasure if "pickup" is true. */ void do_cmd_stay(int pickup) { /* Allow repeated command */ if (p_ptr->cmd.arg) { /* Set repeat count */ p_ptr->cmd.rep = p_ptr->cmd.arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ p_ptr->cmd.arg = 0; } /* Take a turn */ p_ptr->state.energy_use = 100; /* Spontaneous Searching */ if ((p_ptr->skills[SKILL_FOS] >= 50) || one_in_(50 - p_ptr->skills[SKILL_FOS])) { search(); } /* Continuous Searching */ if (p_ptr->state.searching) { search(); } /* Handle "objects" */ carry(pickup); /* * Fields you are standing on may do something. */ field_script(area(p_ptr->px, p_ptr->py), FIELD_ACT_PLAYER_ENTER, ""); } /* * Resting allows a player to safely restore his hp -RAK- */ void do_cmd_rest(void) { /* Prompt for time if needed */ if (p_ptr->cmd.arg <= 0) { char out_val[80]; /* Default */ strcpy(out_val, "&"); /* Ask for duration */ if (!get_string(out_val, 5, "Rest (0-9999, '*' for HP/SP, '&' as needed): ")) { return; } /* Rest until done */ if (out_val[0] == '&') { p_ptr->cmd.arg = (-2); } /* Rest a lot */ else if (out_val[0] == '*') { p_ptr->cmd.arg = (-1); } /* Rest some */ else { p_ptr->cmd.arg = atoi(out_val); if (p_ptr->cmd.arg <= 0) return; } } /* Paranoia */ if (p_ptr->cmd.arg > 9999) p_ptr->cmd.arg = 9999; /* The sin of sloth */ if (p_ptr->cmd.arg > 100) chg_virtue(V_DILIGENCE, -1); /* Why are you sleeping when there's no need? WAKE UP! */ if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp == p_ptr->msp) && !p_ptr->tim.blind && !p_ptr->tim.confused && !p_ptr->tim.poisoned && !p_ptr->tim.afraid && !p_ptr->tim.stun && !p_ptr->tim.cut && !p_ptr->tim.slow && !p_ptr->tim.paralyzed && !p_ptr->tim.image && !p_ptr->tim.word_recall) chg_virtue(V_DILIGENCE, -1); /* Take a turn XXX XXX XXX (?) */ p_ptr->state.energy_use = 100; /* Save the rest code */ p_ptr->state.resting = p_ptr->cmd.arg; /* Cancel searching */ p_ptr->state.searching = FALSE; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Handle stuff */ handle_stuff(); /* Refresh */ Term_fresh(); } /* * Determines the odds of an object breaking when thrown at a monster * * Note that artifacts never break, see the "drop_near()" function. */ static int breakage_chance(const object_type *o_ptr) { /* Examine the item type */ switch (o_ptr->tval) { /* Always break */ case TV_FLASK: case TV_POTION: case TV_BOTTLE: case TV_FOOD: case TV_JUNK: return (100); /* Often break */ case TV_LITE: case TV_SCROLL: case TV_SKELETON: return (50); /* Sometimes break */ case TV_WAND: case TV_SPIKE: case TV_ARROW: return (25); /* Rarely break */ case TV_SHOT: case TV_BOLT: default: return (10); } } /* * Calculation of critical hits for objects fired or thrown by the player. -LM- */ static int critical_shot(int chance, int sleeping_bonus, cptr o_name, cptr m_name, int visible) { int power = (chance + sleeping_bonus); int bonus = 0; if (!visible) { msgf("The %s finds a mark.", o_name); } /* Test for critical hit. */ if (randint1(power + 240) <= power) { /* * Encourage the player to throw weapons at sleeping * monsters. -LM- */ if (sleeping_bonus && visible) { msgf("You rudely awaken the monster!"); } /* Determine deadliness bonus from critical hit. */ if (randint0(100) == 0) bonus = 800; if (randint0(40) == 0) bonus = 500; else if (randint0(12) == 0) bonus = 300; else if (randint0(3) == 0) bonus = 200; else bonus = 100; /* Only give a message if we see it hit. */ if (visible) { if (bonus <= 100) { msgf("The %s strikes %s.", o_name, m_name); } else if (bonus <= 200) { msgf("The %s penetrates %s.", o_name, m_name); } else if (bonus <= 300) { msgf("The %s drives into %s!", o_name, m_name); } else if (bonus <= 500) { msgf("The %s transpierces %s!", o_name, m_name); } else { msgf("The %s *smites* %s!", o_name, m_name); } } } /* * If the blow is not a critical hit, display the default attack * message and apply the standard multiplier. */ else { if (visible) { msgf("The %s hits %s.", o_name, m_name); } } return (bonus); } /* * Process the effect of hitting something with a * thrown item. */ static void throw_item_effect(object_type *o_ptr, bool hit_body, bool hit_wall, bool hit_success, int x, int y) { /* Chance of breakage (during attacks) */ int breakage = (hit_body ? breakage_chance(o_ptr) : 0); /* Figurines transform */ if (o_ptr->tval == TV_FIGURINE) { /* Always break */ breakage = 100; if (!(summon_named_creature(x, y, o_ptr->pval, FALSE, FALSE, TRUE))) { msgf("The Figurine writhes and then shatters."); } } if ((FLAG(o_ptr, TR_RETURN)) && randint0(100) < 95) { msgf("The %v returns to your hand.", OBJECT_FMT(o_ptr, FALSE, 3)); inven_carry(o_ptr); return; } /* Exploding arrows */ if ((FLAG(o_ptr, TR_EXPLODE)) && hit_body && hit_success) { project(0, 2, x, y, 100, GF_FIRE, (PROJECT_JUMP | PROJECT_ITEM | PROJECT_KILL)); return; } /* Potions smash open */ if (object_is_potion(o_ptr)) { if (hit_body || hit_wall || (randint1(100) < breakage)) { /* Message */ msgf("The %v shatters!", OBJECT_FMT(o_ptr, FALSE, 3)); if (potion_smash_effect(0, x, y, o_ptr)) { monster_type *m_ptr = &m_list[area(x, y)->m_idx]; /* ToDo (Robert): fix the invulnerability */ if (area(x, y)->m_idx && !is_hostile(&m_list[area(x, y)->m_idx]) && !(m_ptr->invulner)) { monster_type *m2_ptr = &m_list[area(x, y)->m_idx]; msgf("%^v gets angry!", MONSTER_FMT(m2_ptr, 0)); set_hostile(m2_ptr); } } return; } else { breakage = 0; } } /* Drop (or break) near that location */ drop_near(o_ptr, breakage, x, y); p_ptr->redraw |= (PR_EQUIPPY); make_noise(3); } /* * Fire an object from the pack or floor. * * You may only fire items that "match" your missile launcher. * * You must use slings + pebbles/shots, bows + arrows, xbows + bolts. * * See "calc_bonuses()" for more calculations and such. * * Note that "firing" a missile is MUCH better than "throwing" it. * * Note: "unseen" monsters are very hard to hit. * * Objects are more likely to break if they "attempt" to hit a monster. * * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots. * * The "extra shot" code works by decreasing the amount of energy * required to make each shot, spreading the shots out over time. * * Note that when firing missiles, the launcher multiplier is applied * after all the bonuses are added in, making multipliers very useful. * * Note that Bows of "Extra Might" get extra range and an extra bonus * for the damage multiplier. * * Note that Bows of "Extra Shots" give an extra shot. */ void do_cmd_fire_aux(int mult, object_type *o_ptr, const object_type *j_ptr) { int py = p_ptr->py; int px = p_ptr->px; int dir; int x, y, nx, ny, tx, ty; int armour, bonus, chance, total_deadliness; int sleeping_bonus = 0; int terrain_bonus = 0; long tdam; int slay; #if 0 /* Assume no weapon of velocity or accuracy bonus. */ int special_dam = 0; int special_hit = 0; #endif /* 0 */ int tdis, thits, tmul; int cur_dis; int mul, div; int chance2; object_type *i_ptr; char o_name[256]; char m_name[80]; int msec = delay_factor * delay_factor * delay_factor; bool hit_wall = FALSE; cave_type *c_ptr; /* This "exception" will have to be added via python. */ #if 0 /* Missile launchers of Velocity and Accuracy sometimes "supercharge" */ if ((j_ptr->name2 == EGO_VELOCITY) || (j_ptr->name2 == EGO_ACCURACY)) { /* Occasional boost to shot. */ if (one_in_(16)) { if (j_ptr->name2 == EGO_VELOCITY) special_dam = TRUE; else if (j_ptr->name2 == EGO_ACCURACY) special_hit = TRUE; /* Let player know that weapon is activated. */ msgf("You feel your %v tremble in your hand.", OBJECT_FMT(j_ptr, FALSE, 0)); } } #endif /* 0 */ /* Get a direction (or cancel) */ if (!get_aim_dir(&dir)) return; /* Duplicate the object */ i_ptr = object_dup(o_ptr); /* Single object */ i_ptr->number = 1; /* Reduce and describe inventory */ item_increase(o_ptr, -1); /* Sound */ if (j_ptr) { sound(SOUND_SHOOT); } /* Describe the object */ object_desc(o_name, i_ptr, FALSE, 0, 256); /* Use the proper number of shots */ if (j_ptr) { thits = p_ptr->num_fire; } else { thits = 1; if (p_ptr->rp.pclass == CLASS_ROGUE && i_ptr->tval == TV_SWORD && i_ptr->sval == SV_DAGGER) { if (p_ptr->lev >= 10) thits++; if (p_ptr->lev >= 30) thits++; } } /* Actually "fire" the object. */ if (j_ptr) { total_deadliness = p_ptr->to_d + i_ptr->to_d + j_ptr->to_d; bonus = (p_ptr->to_h + i_ptr->to_h + j_ptr->to_h); chance = (p_ptr->skills[SKILL_THB] + (bonus * BTH_PLUS_ADJ)); } else { total_deadliness = p_ptr->to_d + i_ptr->to_d; if (FLAG(i_ptr, TR_THROW)) bonus = p_ptr->to_h + i_ptr->to_h; else bonus = i_ptr->to_h; chance = p_ptr->skills[SKILL_THT] + (bonus * BTH_PLUS_ADJ); } /* Cursed arrows tend not to hit anything */ if (cursed_p(i_ptr)) chance = chance / 2; /* Shooter properties */ if (j_ptr) { p_ptr->state.energy_use = p_ptr->bow_energy; tmul = p_ptr->ammo_mult; /* Get extra "power" from "extra might" */ if ((FLAG(p_ptr, TR_XTRA_MIGHT))) tmul++; } else { p_ptr->state.energy_use = 100; if (FLAG(i_ptr, TR_THROW)) { tmul = 5; } else { tmul = 1; } } /* Extract a "distance multiplier" */ mul = 5 + 5 * tmul + 2 * (mult - 1); /* Enforce a minimum "weight" of one pound */ div = ((i_ptr->weight > 10) ? i_ptr->weight : 10); /* Distance -- Reward strength, penalize weight */ tdis = (adj_str_blow[p_ptr->stat[A_STR].ind] + 10) * mul / div; /* Maximum distance */ if (tdis > mul) tdis = mul; /* Paranoia */ if (tdis > MAX_RANGE) tdis = MAX_RANGE; /* Take a (partial) turn - note strange formula. */ /* The real number of shots per round is (1 + n)/2 */ p_ptr->state.energy_use = (2 * p_ptr->state.energy_use / (1 + thits)); /* Another thing to do in python */ #if 0 /* Fire ammo of backbiting, and it will turn on you. -LM- */ if (i_ptr->name2 == EGO_BACKBITING) { /* Message. */ msgf("Your missile turns in midair and strikes you!"); /* Calculate damage. */ tdam = damroll(tmul * 4, i_ptr->ds); /* Inflict both normal and wound damage. */ take_hit(tdam, "ammo of backbiting."); inc_cut(randint1(tdam * 3)); /* That ends that shot! */ return; } #endif /* 0 */ /* Start at the player */ y = py; x = px; /* Predict the "target" location */ ty = py + 99 * ddy[dir]; tx = px + 99 * ddx[dir]; /* Check for "target request" */ if ((dir == 5) && target_okay()) { tx = p_ptr->target_col; ty = p_ptr->target_row; } /* Hack -- Handle stuff */ handle_stuff(); /* Initialise the multi-move */ mmove_init(px, py, tx, ty); /* Travel until stopped */ for (cur_dis = 0; cur_dis <= tdis;) { /* Hack -- Stop at the target */ if ((y == ty) && (x == tx)) break; /* Calculate the new location (see "project()") */ ny = y; nx = x; mmove(&nx, &ny, px, py); /* Stopped by wilderness boundary */ if (!in_bounds2(nx, ny)) { hit_wall = TRUE; break; } /* Stopped by walls/doors */ c_ptr = area(nx, ny); if (cave_wall_grid(c_ptr)) { hit_wall = TRUE; break; } /* Advance the distance */ cur_dis++; /* The player can see the (on screen) missile */ if (panel_contains(nx, ny) && player_can_see_bold(nx, ny)) { char c = object_char(i_ptr); byte a = object_attr(i_ptr); /* Draw, Hilite, Fresh, Pause, Erase */ print_rel(c, a, nx, ny); move_cursor_relative(nx, ny); Term_fresh(); Term_xtra(TERM_XTRA_DELAY, msec); lite_spot(nx, ny); Term_fresh(); } /* The player cannot see the missile */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Save the new location */ x = nx; y = ny; /* Monster here, Try to hit it */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; chance2 = chance - cur_dis; /* Sleeping, visible monsters are easier to hit. -LM- */ if ((m_ptr->csleep) && (m_ptr->ml)) sleeping_bonus = 5 + p_ptr->lev / 5; /* Monsters in rubble can take advantage of cover. -LM- */ if (c_ptr->feat == FEAT_RUBBLE) { terrain_bonus = r_ptr->ac / 5 + 5; } /* * Monsters in trees can take advantage of cover, * except from rangers. */ else if ((c_ptr->feat == FEAT_TREES) && !FLAG(p_ptr, TR_WILD_SHOT)) { terrain_bonus = r_ptr->ac / 5 + 5; } /* Monsters in water are vulnerable. -LM- */ else if (c_ptr->feat == FEAT_DEEP_WATER) { terrain_bonus -= r_ptr->ac / 4; } /* Get effective armour class of monster. */ armour = r_ptr->ac + terrain_bonus; /* Adjacent monsters are harder to hit if awake */ if ((cur_dis == 1) && (!sleeping_bonus)) armour += armour; #if 0 /* Weapons of velocity sometimes almost negate monster armour. */ if (special_hit) armour /= 3; #endif /* 0 */ /* Look to see if we've spotted a mimic */ if ((m_ptr->smart & SM_MIMIC) && m_ptr->ml) { /* We've spotted it */ msgf("You've found %v!", MONSTER_FMT(m_ptr, 0x88)); /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now */ update_mon_vis(m_ptr->r_idx, 1); } /* Did we hit it (penalize range) */ if (test_hit_fire(chance2 + sleeping_bonus, armour, m_ptr->ml)) { bool fear = FALSE; int multiplier = deadliness_calc(total_deadliness); /* Assume a default death */ cptr note_dies = " dies."; /* Some monsters get "destroyed" */ if (!monster_living(r_ptr)) { /* Special note at death */ note_dies = " is destroyed."; } /* Get "the monster" or "it" */ monster_desc(m_name, m_ptr, 0, 80); /* Hack -- Track this monster race */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Hack -- Track this monster */ if (m_ptr->ml) health_track(c_ptr->m_idx); /* * The basic damage-determination formula is the same in * archery as it is in melee (apart from the launcher mul- * tiplier). See formula "py_attack" in "cmd1.c" for more * details. -LM- */ /* Base damage dice. */ tdam = i_ptr->ds; /* Multiply by the missile weapon multiplier. */ tdam *= tmul; /* Add deadliness bonus from slays/brands */ slay = (tot_dam_aux(i_ptr, m_ptr) - 100); multiplier += slay; /* * Add deadliness bonus from critical shot */ multiplier += critical_shot(tmul > 1 ? chance2 : 0, sleeping_bonus, o_name, m_name, m_ptr->ml); /* * Convert total Deadliness into a percentage, and apply * it as a bonus or penalty. (100x inflation) */ tdam *= multiplier; /* * Get the whole number of dice sides by deflating, * and then get total dice damage. */ tdam = damroll(i_ptr->dd, tdam / 100 + (randint0(100) < (tdam % 100) ? 1 : 0)); /* Add in extra effect due to slays */ tdam += slay / 20; #if 0 /* If a weapon of velocity activates, increase damage. */ if (special_dam) { tdam += 15; } #endif /* 0 */ /* No negative damage */ if (tdam < 0) tdam = 0; /* Modify the damage */ tdam = mon_damage_mod(m_ptr, tdam, 0); /* Drop (or break) near that location (i_ptr is now invalid) */ throw_item_effect(i_ptr, TRUE, FALSE, TRUE, x, y); /* Complex message */ if (p_ptr->state.wizard) { msgf("You do %d (out of %d) damage.", tdam, m_ptr->hp); } /* Hit the monster, check for death */ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(c_ptr->m_idx, tdam); /* Anger the monster */ if (tdam > 0) anger_monster(m_ptr); /* Take note */ if (fear && m_ptr->ml) { flee_message(m_name, m_ptr->r_idx); } } } else { /* Drop (or break) near that location (i_ptr is now invalid) */ throw_item_effect(i_ptr, TRUE, FALSE, FALSE, x, y); } /* Stop looking */ return; } } /* Drop (or break) near that location (i_ptr is now invalid) */ throw_item_effect(i_ptr, FALSE, hit_wall, FALSE, x, y); } void do_cmd_fire(void) { object_type *j_ptr, *o_ptr; cptr q, s; /* Get the "bow" (if any) */ j_ptr = &p_ptr->equipment[EQUIP_BOW]; /* Require a launcher */ if (!j_ptr->tval) { msgf("You have nothing to fire with."); return; } /* Require proper missile */ item_tester_tval = p_ptr->ammo_tval; /* Get an item */ q = "Fire which item? "; s = "You have nothing to fire."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Fire the item */ do_cmd_fire_aux(1, o_ptr, j_ptr); } /* * Throw an object from the pack or floor. * * Note: "unseen" monsters are very hard to hit. * * Should throwing a weapon do full damage? Should it allow the magic * to hit bonus of the weapon to have an effect? Should it ever cause * the item to be destroyed? Should it do any damage at all? */ void do_cmd_throw_aux(int mult) { cptr q, s; object_type *o_ptr; /* Get an item */ q = "Throw which item? "; s = "You have nothing to throw."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Hack -- Cannot remove cursed items */ if ((!o_ptr->allocated) && cursed_p(o_ptr)) { /* Oops */ msgf("Hmmm, it seems to be cursed."); /* Set the knowledge flag for the player */ o_ptr->kn_flags[2] |= TR2_CURSED; /* Nope */ return; } do_cmd_fire_aux(mult, o_ptr, NULL); } /* * Throw an object from the pack or floor. */ void do_cmd_throw(void) { do_cmd_throw_aux(1); } zangband/src/cmd3.c0000755000000000000000000007035110250356274013136 0ustar rootroot/* File: cmd3.c */ /* Purpose: Inventory commands */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Display inventory */ void do_cmd_inven(void) { /* Save screen */ screen_save(); /* Hack -- show empty slots */ item_tester_full = TRUE; /* Display the inventory */ show_list(p_ptr->inventory, FALSE); /* Hack -- hide empty slots */ item_tester_full = FALSE; /* Get a command */ prtf(0, 0, "Inventory: carrying %d.%d pounds (%d%% of capacity). Command: ", p_ptr->total_weight / 10, p_ptr->total_weight % 10, (p_ptr->total_weight * 100) / ((adj_str_wgt[p_ptr->stat[A_STR].ind] * 100) / 2)); /* Get a new command */ p_ptr->cmd.new = inkey(); /* Load screen */ screen_load(); /* Process "Escape" */ if (p_ptr->cmd.new == ESCAPE) { /* Reset stuff */ p_ptr->cmd.new = 0; } } /* * Display equipment */ void do_cmd_equip(void) { /* Save the screen */ screen_save(); /* Hack -- show empty slots */ item_tester_full = TRUE; /* Display the equipment */ show_equip(FALSE); /* Hack -- undo the hack above */ item_tester_full = FALSE; /* Get a command */ prtf(0, 0, "Equipment: carrying %d.%d pounds (%d%% of capacity). Command: ", p_ptr->total_weight / 10, p_ptr->total_weight % 10, (p_ptr->total_weight * 100) / ((adj_str_wgt[p_ptr->stat[A_STR].ind] * 100) / 2)); /* Get a new command */ p_ptr->cmd.new = inkey(); /* Restore the screen */ screen_load(); /* Process "Escape" */ if (p_ptr->cmd.new == ESCAPE) { /* Reset stuff */ p_ptr->cmd.new = 0; } } /* * Wield or wear a single item from the pack or floor */ void do_cmd_wield(void) { int slot; object_type *q_ptr; object_type *o_ptr; cptr act; cptr q, s; /* Restrict the choices */ item_tester_hook = item_tester_hook_wear; /* Get an item */ q = "Wear/Wield which item? "; s = "You have nothing you can wear or wield."; q_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!q_ptr) return; /* Check the slot */ slot = wield_slot(q_ptr); /* Hack - rings are special */ if (slot == EQUIP_LEFT) { /* Check to see if have two rings */ if ((p_ptr->equipment[EQUIP_LEFT].k_idx) && (p_ptr->equipment[EQUIP_RIGHT].k_idx)) { if (get_check("Use right hand? ")) slot = EQUIP_RIGHT; } } /* Access the wield slot */ o_ptr = &p_ptr->equipment[slot]; /* Prevent wielding into a cursed slot */ if (cursed_p(o_ptr)) { /* Message */ msgf("The %v you are %s appears to be cursed.", OBJECT_FMT(o_ptr, FALSE, 0), describe_use(slot)); /* Set the knowledge flag for the player */ o_ptr->kn_flags[2] |= TR2_CURSED; /* Cancel the command */ return; } if (cursed_p(q_ptr) && confirm_wear && (object_known_p(q_ptr) || (q_ptr->info & OB_SENSE))) { if (!get_check("Really use the %v {cursed}? ", OBJECT_FMT(q_ptr, FALSE, 0))) return; } /* Take a turn */ p_ptr->state.energy_use = 100; /* Split object */ q_ptr = item_split(q_ptr, 1); /* Take off existing item */ if (o_ptr->k_idx) { /* Take off existing item */ (void)inven_takeoff(o_ptr); } /* Wear the new stuff */ swap_objects(o_ptr, q_ptr); /* Forget stack */ o_ptr->next_o_idx = 0; /* Forget location */ o_ptr->iy = o_ptr->ix = 0; /* Forget Region */ o_ptr->region = 0; /* Now no longer allocated in o_list[] */ o_ptr->allocated = FALSE; /* Where is the item now */ if (slot == EQUIP_WIELD) { act = "You are wielding"; } else if (slot == EQUIP_BOW) { act = "You are shooting with"; } else if (slot == EQUIP_LITE) { act = "Your light source is"; } else { act = "You are wearing"; } /* Message */ msgf("%s %v (%c).", act, OBJECT_FMT(o_ptr, TRUE, 3), I2A(slot)); /* Cursed! */ if (cursed_p(o_ptr)) { /* Warn the player */ msgf("Oops! It feels deathly cold!"); chg_virtue(V_HARMONY, -1); /* Note the curse */ o_ptr->info |= (OB_SENSE); } /* Learn some "obvious" things about the item */ o_ptr->kn_flags[0] |= (o_ptr->flags[0] & TR0_EASY_MASK); /* Recalculate bonuses and weight */ p_ptr->update |= (PU_BONUS | PU_WEIGHT); /* Recalculate torch */ p_ptr->update |= (PU_TORCH); /* Recalculate mana */ p_ptr->update |= (PU_MANA); p_ptr->redraw |= (PR_EQUIPPY); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); make_noise(1); } /* * Take off an item */ void do_cmd_takeoff(void) { object_type *o_ptr; cptr q, s; /* Get an item */ q = "Take off which item? "; s = "You are not wearing anything to take off."; o_ptr = get_item(q, s, (USE_EQUIP)); /* Not a valid item */ if (!o_ptr) return; /* Item is cursed */ if (cursed_p(o_ptr)) { /* Oops */ msgf("Hmmm, it seems to be cursed."); /* Set the knowledge flag for the player */ o_ptr->kn_flags[2] |= TR2_CURSED; /* Nope */ return; } /* Take a partial turn */ p_ptr->state.energy_use = 50; /* Take off the item */ (void)inven_takeoff(o_ptr); make_noise(1); } /* * Drop an item */ void do_cmd_drop(void) { int amt = 1; object_type *o_ptr; cptr q, s; /* Get an item */ q = "Drop which item? "; s = "You have nothing to drop."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN)); /* Not a valid item */ if (!o_ptr) return; /* Hack -- Cannot remove cursed items */ if ((!o_ptr->allocated) && cursed_p(o_ptr)) { /* Oops */ msgf("Hmmm, it seems to be cursed."); /* Set the knowledge flag for the player */ o_ptr->kn_flags[2] |= TR2_CURSED; /* Nope */ return; } /* See how many items */ if (o_ptr->number > 1) { /* Get a quantity */ amt = get_quantity(NULL, o_ptr->number); /* Allow user abort */ if (amt <= 0) return; } /* Take a partial turn */ p_ptr->state.energy_use = 50; /* Drop (some of) the item */ inven_drop(o_ptr, amt); p_ptr->redraw |= (PR_EQUIPPY); make_noise(1); } static bool high_level_book(const object_type *o_ptr) { if ((o_ptr->tval == TV_LIFE_BOOK) || (o_ptr->tval == TV_SORCERY_BOOK) || (o_ptr->tval == TV_NATURE_BOOK) || (o_ptr->tval == TV_CHAOS_BOOK) || (o_ptr->tval == TV_DEATH_BOOK) || (o_ptr->tval == TV_TRUMP_BOOK)) { if (o_ptr->sval > 1) return TRUE; else return FALSE; } return FALSE; } bool destroy_item_aux(object_type *o_ptr, int amt) { bool gain_expr = FALSE; /* Can the player destroy the object? */ if (!can_player_destroy_object(o_ptr)) { /* Message */ msgf("You cannot destroy %v.", OBJECT_FMT(o_ptr, TRUE, 3)); /* Done */ return (FALSE); } /* Take a turn */ p_ptr->state.energy_use += 100; /* Message */ msgf("You destroy %v.", OBJECT_FMT(o_ptr, TRUE, 3)); sound(SOUND_DESTITEM); if (high_level_book(o_ptr)) { if (p_ptr->rp.pclass == CLASS_WARRIOR) { gain_expr = TRUE; } else if (p_ptr->rp.pclass == CLASS_PALADIN) { if (p_ptr->spell.r[0].realm == REALM_LIFE) { if (o_ptr->tval != TV_LIFE_BOOK) gain_expr = TRUE; } else { if (o_ptr->tval == TV_LIFE_BOOK) gain_expr = TRUE; } } if (gain_expr && (p_ptr->exp < PY_MAX_EXP)) { s32b tester_exp = p_ptr->max_exp / 20; if (tester_exp > 10000) tester_exp = 10000; if (o_ptr->sval < 3) tester_exp /= 4; if (tester_exp < 1) tester_exp = 1; msgf("You feel more experienced."); gain_exp(tester_exp * amt); } if (high_level_book(o_ptr) && o_ptr->tval == TV_LIFE_BOOK) { chg_virtue(V_UNLIFE, 1); chg_virtue(V_VITALITY, -1); } else if (high_level_book(o_ptr) && o_ptr->tval == TV_DEATH_BOOK) { chg_virtue(V_UNLIFE, -1); chg_virtue(V_VITALITY, 1); } if (o_ptr->to_a || o_ptr->to_h || o_ptr->to_d) chg_virtue(V_ENCHANT, -1); if (object_value_real(o_ptr) > 30000) chg_virtue(V_SACRIFICE, 2); else if (object_value_real(o_ptr) > 10000) chg_virtue(V_SACRIFICE, 1); } if (o_ptr->to_a != 0 || o_ptr->to_d != 0 || o_ptr->to_h != 0) chg_virtue(V_HARMONY, 1); make_noise(1); /* We destroyed the item(s) */ return (TRUE); } /* * Destroy an item */ void do_cmd_destroy(void) { int amt = 1; int old_number; bool force = FALSE; object_type *o_ptr; cptr q, s; /* Hack -- force destruction */ if (p_ptr->cmd.arg > 0) force = TRUE; /* Get an item */ q = "Destroy which item? "; s = "You have nothing to destroy."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* See how many items */ if (o_ptr->number > 1) { /* Get a quantity */ amt = get_quantity(NULL, o_ptr->number); /* Allow user abort */ if (amt <= 0) return; } /* Describe the objects to delete */ old_number = o_ptr->number; o_ptr->number = amt; /* Verify unless quantity given */ if (!force) { if (!(auto_destroy && (object_value(o_ptr) < 1))) { /* Make a verification */ if (!get_check("Really destroy %v? ", OBJECT_FMT(o_ptr, TRUE, 3))) { o_ptr->number = old_number; return; } } } o_ptr->number = old_number; /* No energy used yet */ p_ptr->state.energy_use = 0; /* Physically try to destroy the item(s) */ if (!destroy_item_aux(o_ptr, amt)) return; /* Reduce the charges of rods/wands */ reduce_charges(o_ptr, amt); /* Eliminate the item */ item_increase(o_ptr, -amt); } /* * Observe an item which has been *identify*-ed */ void do_cmd_observe(void) { object_type *o_ptr; cptr q, s; /* Get an item */ q = "Examine which item? "; s = "You have nothing to examine."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Describe it fully */ identify_fully_aux(o_ptr); } static bool item_tester_inscribed(const object_type *o_ptr) { /* Nothing to remove */ if (!o_ptr->inscription) return (FALSE); return (TRUE); } /* * Remove the inscription from an object * XXX Mention item (when done)? */ void do_cmd_uninscribe(void) { object_type *o_ptr; cptr q, s; /* Only inscribed items */ item_tester_hook = item_tester_inscribed; /* Get an item */ q = "Un-inscribe which item? "; s = "You have nothing to un-inscribe."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Message */ msgf("Inscription removed."); /* Remove the incription */ quark_remove(&o_ptr->inscription); /* Notice changes */ notice_item(); make_noise(2); } /* * Inscribe an object with a comment */ void do_cmd_inscribe(void) { object_type *o_ptr; char out_val[80]; cptr q, s; /* Get an item */ q = "Inscribe which item? "; s = "You have nothing to inscribe."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Message */ msgf("Inscribing %v.", OBJECT_FMT(o_ptr, TRUE, 3)); message_flush(); /* Start with nothing */ out_val[0] = 0; /* Use old inscription */ if (o_ptr->inscription) { /* Start with the old inscription */ strcpy(out_val, quark_str(o_ptr->inscription)); } /* Get a new inscription (possibly empty) */ if (get_string(out_val, 80, "Inscription: ")) { /* Save the inscription */ quark_remove(&o_ptr->inscription); o_ptr->inscription = quark_add(out_val); /* Notice changes */ notice_item(); make_noise(2); } } /* * An "item_tester_hook" for refilling lanterns */ static bool item_tester_refill_lantern(const object_type *o_ptr) { /* Flasks of oil are okay */ if (o_ptr->tval == TV_FLASK) return (TRUE); /* Laterns are okay */ if ((o_ptr->tval == TV_LITE) && (o_ptr->sval == SV_LITE_LANTERN) && (o_ptr->timeout > 0)) return (TRUE); /* Assume not okay */ return (FALSE); } /* * Refill the players lamp (from the pack or floor) */ static void do_cmd_refill_lamp(void) { object_type *o_ptr; object_type *j_ptr; cptr q, s; /* Restrict the choices */ item_tester_hook = item_tester_refill_lantern; /* Get an item */ q = "Refill with which source of oil? "; s = "You have no sources of oil."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Take a partial turn */ p_ptr->state.energy_use = 50; /* Access the lantern */ j_ptr = &p_ptr->equipment[EQUIP_LITE]; /* Refuel */ if (o_ptr->tval == TV_FLASK) { /* Flasks use the pval to store the fuel */ j_ptr->timeout += o_ptr->pval; } else { /* Lanterns use the timeout to store the fuel */ j_ptr->timeout += o_ptr->timeout; } /* Message */ msgf("You fuel your lamp."); /* Comment */ if (j_ptr->timeout >= FUEL_LAMP) { j_ptr->timeout = FUEL_LAMP; msgf("Your lamp is full."); } /* Decrease the item */ if (o_ptr->tval == TV_FLASK) { item_increase(o_ptr, -1); } else { /* The lantern is empty */ o_ptr->timeout = 0; } /* Recalculate torch */ p_ptr->update |= (PU_TORCH); /* Notice changes */ notice_item(); } /* * An "item_tester_hook" for refilling torches */ static bool item_tester_refill_torch(const object_type *o_ptr) { /* Torches are okay */ if ((o_ptr->tval == TV_LITE) && (o_ptr->sval == SV_LITE_TORCH)) return (TRUE); /* Assume not okay */ return (FALSE); } /* * Refuel the players torch (from the pack or floor) */ static void do_cmd_refill_torch(void) { object_type *o_ptr; object_type *j_ptr; cptr q, s; /* Restrict the choices */ item_tester_hook = item_tester_refill_torch; /* Get an item */ q = "Refuel with which torch? "; s = "You have no extra torches."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Take a partial turn */ p_ptr->state.energy_use = 50; /* Access the primary torch */ j_ptr = &p_ptr->equipment[EQUIP_LITE]; /* Refuel */ j_ptr->timeout += o_ptr->timeout + 5; /* Message */ msgf("You combine the torches."); /* Over-fuel message */ if (j_ptr->timeout >= FUEL_TORCH) { j_ptr->timeout = FUEL_TORCH; msgf("Your torch is fully fueled."); } /* Refuel message */ else { msgf("Your torch glows more brightly."); } /* Decrease the item */ item_increase(o_ptr, -1); /* Recalculate torch */ p_ptr->update |= (PU_TORCH); } /* * Refill the players lamp, or restock his torches */ void do_cmd_refill(void) { object_type *o_ptr; /* Get the light */ o_ptr = &p_ptr->equipment[EQUIP_LITE]; /* It is nothing */ if (o_ptr->tval != TV_LITE) { msgf("You are not wielding a light."); } /* It's a lamp */ else if (o_ptr->sval == SV_LITE_LANTERN) { do_cmd_refill_lamp(); } /* It's a torch */ else if (o_ptr->sval == SV_LITE_TORCH) { do_cmd_refill_torch(); } /* No torch to refill */ else { msgf("Your light cannot be refilled."); } } /* * Target command */ void do_cmd_target(void) { /* Target set */ if (target_set(TARGET_KILL)) { msgf("Target Selected."); } /* Target aborted */ else { msgf("Target Aborted."); } } /* * Look command */ void do_cmd_look(void) { /* Look around */ if (target_set(TARGET_LOOK)) { msgf("Target Selected."); } } /* * Allow the player to examine other sectors on the map */ void do_cmd_locate(void) { int dir, y1, x1, y2, x2; int wid, hgt; char tmp_val[80]; char out_val[160]; /* Get size */ get_map_size(&wid, &hgt); /* Start at current panel */ x2 = x1 = p_ptr->panel_x1; y2 = y1 = p_ptr->panel_y1; /* Show panels until done */ while (1) { /* Describe the location */ if ((y2 == y1) && (x2 == x1)) { tmp_val[0] = '\0'; } else { strnfmt(tmp_val, 80, "%s%s of", ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""), ((x2 < x1) ? " West" : (x2 > x1) ? " East" : "")); } /* Prepare to ask which way to look */ strnfmt(out_val, 160, "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?", y2 / (hgt / 2), y2 % (hgt / 2), x2 / (wid / 2), x2 % (wid / 2), tmp_val); /* Assume no direction */ dir = 0; /* Get a direction */ while (!dir) { char command; /* Get a command (or Cancel) */ if (!get_com(out_val, &command)) break; /* Extract the action (if any) */ dir = get_keymap_dir(command); /* Error */ if (!dir) bell("Illegal direction for locate!"); } /* No direction */ if (!dir) break; /* Apply the motion */ if (change_panel(ddx[dir], ddy[dir])) { x2 = p_ptr->panel_x1; y2 = p_ptr->panel_y1; } } /* Recenter the map around the player */ verify_panel(); } /* * The table of "symbol info" -- each entry is a string of the form * "X:desc" where "X" is the trigger, and "desc" is the "info". */ static cptr ident_info[] = { " :A dark grid", "!:A potion (or oil)", "\":An amulet (or necklace)", "#:A wall (or secret door)", "$:Treasure (gold or gems)", "%:Trees", "&:A chest", "':An open door", "(:Soft armor", "):A shield", "*:A treasure or a ball monster", "+:A closed door", ",:Food (or mushroom patch)", "-:A wand (or rod)", ".:Floor", "/:A polearm (Axe/Pike/etc)", /* "0:unused", XXX XXX XXX out of date */ "1:Entrance to General Store", "2:Entrance to Armory", "3:Entrance to Weaponsmith", "4:Entrance to Temple", "5:Entrance to Alchemy shop", "6:Entrance to Magic store", "7:Entrance to Black Market", "8:Entrance to your home", "9:Entrance to the bookstore", "::Rubble / Rock", ";:Swamp / Rune", "<:An up staircase", "=:A ring", ">:A down staircase", "?:A scroll", "@:You", "A:Angel", "B:Bird", "C:Canine", "D:Ancient Dragon/Wyrm", "E:Elemental", "F:Dragon Fly", "G:Ghost", "H:Hybrid", "I:Insect", "J:Snake", "K:Killer Beetle", "L:Lich", "M:Multi-Headed Reptile", /* "N:unused", */ "O:Ogre", "P:Giant Humanoid", "Q:Quylthulg (Pulsing Flesh Mound)", "R:Reptile/Amphibian", "S:Spider/Scorpion/Tick", "T:Troll", "U:Major Demon", "V:Vampire", "W:Wight/Wraith/etc", "X:Xorn/Xaren/etc", "Y:Yeti", "Z:Zephyr Hound", "[:Hard armor", "\\:A hafted weapon (mace/whip/etc)", "]:Misc. armor", "^:A trap", "_:A staff", "`:A figurine or statue", "a:Ant", "b:Bat", "c:Centipede", "d:Dragon", "e:Floating Eye", "f:Feline", "g:Golem", "h:Hobbit/Elf/Dwarf", "i:Icky Thing", "j:Jelly", "k:Kobold", "l:Aquatic monster", "m:Mold", "n:Naga", "o:Orc", "p:Person/Human", "q:Quadruped", "r:Rodent", "s:Skeleton", "t:Townsperson", "u:Minor Demon", "v:Vortex", "w:Worm/Worm-Mass", /* "x:unused", */ "y:Yeek", "z:Zombie/Mummy", "{:A missile (arrow/bolt/shot)", "|:An edged weapon (sword/dagger/etc)", "}:A launcher (bow/crossbow/sling)", "~:Fluid terrain (or miscellaneous item)", NULL }; /* * Sorting hook -- Comp function -- see below * * We use "u" to point to array of monster indexes, * and "v" to select the type of sorting to perform on "u". */ bool ang_sort_comp_hook(const vptr u, const vptr v, int a, int b) { u16b *who = (u16b *)(u); u16b *why = (u16b *)(v); int w1 = who[a]; int w2 = who[b]; int z1, z2; /* Sort by player kills */ if (*why >= 4) { /* Extract player kills */ z1 = r_info[w1].r_pkills; z2 = r_info[w2].r_pkills; /* Compare player kills */ if (z1 < z2) return (TRUE); if (z1 > z2) return (FALSE); } /* Sort by total kills */ if (*why >= 3) { /* Extract total kills */ z1 = r_info[w1].r_tkills; z2 = r_info[w2].r_tkills; /* Compare total kills */ if (z1 < z2) return (TRUE); if (z1 > z2) return (FALSE); } /* Sort by monster level */ if (*why >= 2) { /* Extract levels */ z1 = r_info[w1].level; z2 = r_info[w2].level; /* Compare levels */ if (z1 < z2) return (TRUE); if (z1 > z2) return (FALSE); } /* Sort by monster experience */ if (*why >= 1) { /* Extract experience */ z1 = r_info[w1].mexp; z2 = r_info[w2].mexp; /* Compare experience */ if (z1 < z2) return (TRUE); if (z1 > z2) return (FALSE); } /* Compare indexes */ return (w1 <= w2); } /* * Sorting hook -- Swap function -- see below * * We use "u" to point to array of monster indexes, * and "v" to select the type of sorting to perform. */ void ang_sort_swap_hook(const vptr u, const vptr v, int a, int b) { u16b *who = (u16b *)(u); u16b holder; /* Hack - ignore v */ (void)v; /* Swap */ holder = who[a]; who[a] = who[b]; who[b] = holder; } static int resize_monster; void (*resize_old_hook) (void); static void resize_monster_recall(void) { /* Put the monster description on the newly-sized screen.*/ screen_roff_mon(resize_monster, 0); } /* * Identify a character, allow recall of monsters * * Several "special" responses recall "multiple" monsters: * ^A (all monsters) * ^U (all unique monsters) * ^N (all non-unique monsters) * ^M (case insensitive name search) * * The responses may be sorted in several ways, see below. * * Note that the player ghosts are ignored. XXX XXX XXX */ void do_cmd_query_symbol(void) { int i, n, r_idx; char sym, query; bool all = FALSE; bool uniq = FALSE; bool norm = FALSE; bool killed = FALSE; bool recall = FALSE; u16b why = 0; u16b *who; char temp1[80] = "\0"; char temp2[80] = "\0"; /* Get a character, or abort */ if (!get_com ("Enter character to be identified, or Ctrl (A, U, N, M, K):", &sym)) return; /* Find that character info, and describe it */ for (i = 0; ident_info[i]; ++i) { if (sym == ident_info[i][0]) break; } /* Describe */ if (sym == KTRL('A')) { all = TRUE; prtf(0, 0, "Full monster list."); } else if (sym == KTRL('U')) { all = uniq = TRUE; prtf(0, 0, "Unique monster list."); } else if (sym == KTRL('N')) { all = norm = TRUE; prtf(0, 0, "Non-unique monster list."); } else if (sym == KTRL('M')) { all = TRUE; if (!get_string(temp1, 70, "Name:")) { clear_msg(); } else { prtf(0, 0, "Monsters with a name \"%s\"", temp1); } } else if (sym == KTRL('K')) { all = killed = TRUE; prtf(0, 0, "Killed monster list."); } else if (ident_info[i]) { if (ident_info[i][0] == '$') { /* * Hack - we need two dollar signs since it * is an escape code. */ prtf(0, 0, "$$ - %s.", ident_info[i] + 2); } else { prtf(0, 0, "%c - %s.", sym, ident_info[i] + 2); } } else { prtf(0, 0, "%c - %s.", sym, "Unknown Symbol"); } /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, u16b); /* Collect matching monsters */ for (n = 0, i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Nothing to recall */ if (!cheat_know && !r_ptr->r_sights) continue; /* Require non-unique monsters if needed */ if (norm && FLAG(r_ptr, RF_UNIQUE)) continue; /* Require unique monsters if needed */ if (uniq && !FLAG(r_ptr, RF_UNIQUE)) continue; /* Require killed monsters if needed */ if (killed && !r_ptr->r_pkills) continue; /* Collect monsters with a name temp1 */ if (temp1[0]) { int xx; for (xx = 0; temp1[xx] && (xx < 80); xx++) { if (isupper(temp1[xx])) temp1[xx] = tolower(temp1[xx]); } strcpy(temp2, mon_race_name(r_ptr)); for (xx = 0; temp2[xx] && (xx < 80); xx++) { if (isupper(temp2[xx])) temp2[xx] = tolower(temp2[xx]); } if (strstr(temp2, temp1)) who[n++] = i; } /* Collect "appropriate" monsters */ else if (all || (r_ptr->d_char == sym)) who[n++] = i; } /* Nothing to recall */ if (!n) { /* XXX XXX Free the "who" array */ KILL(who); return; } /* Prompt XXX XXX XXX */ put_fstr(40, 0, "Recall details? (k/y/n): "); /* Query */ query = inkey(); /* Clear top line */ clear_msg(); why = 2; /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array */ ang_sort(who, &why, n); /* Sort by kills (and level) */ if (query == 'k') { why = 4; query = 'y'; } /* Catch "escape" */ if (query != 'y') { /* XXX XXX Free the "who" array */ KILL(who); return; } /* Sort if needed */ if (why == 4) { /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array */ ang_sort(who, &why, n); } /* Start at the end */ i = n - 1; /* Scan the monster memory */ while (1) { /* Extract a race */ r_idx = who[i]; /* Hack -- Auto-recall */ monster_race_track(r_idx); /* Hack -- Handle stuff */ handle_stuff(); /* Hack -- Begin the prompt */ roff_mon_top(r_idx); /* Hack -- Complete the prompt */ roff(" [(r)ecall, ESC]"); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save the screen */ screen_save(); /* Recall on screen */ screen_roff_mon(who[i], 0); /* Hack -- Complete the prompt (again) */ roff(" [(r)ecall, ESC]"); } /* Remember what the resize hook was */ resize_old_hook = angband_term[0]->resize_hook; /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = resize_monster_recall; /* Remember the monster for resizing */ resize_monster = who[i]; /* Command */ query = inkey(); /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = resize_old_hook; /* The size may have changed during the monster recall */ angband_term[0]->resize_hook(); /* Hack - Flush it */ Term_fresh(); /* Unrecall */ if (recall) { /* Restore */ screen_load(); } /* Normal commands */ if (query != 'r') break; /* Toggle recall */ recall = !recall; } /* Stop scanning */ if (query == ESCAPE) break; /* Move to "prev" monster */ if (query == '-') { if (++i == n) { i = 0; if (!expand_list) break; } } /* Move to "next" monster */ else { if (i-- == 0) { i = n - 1; if (!expand_list) break; } } } /* Free the "who" array */ KILL(who); /* Clear top line */ clear_msg(); } /* * research_mon * -KMW- */ bool research_mon(void) { int i, n, r_idx; char sym, query; s16b oldkills; byte oldwake; bool oldcheat; bool picked = FALSE; bool cost_gold = TRUE; bool recall = FALSE; u16b why = 0; monster_race *r2_ptr; u16b *who; oldcheat = cheat_know; /* Save the screen */ screen_save(); /* Get a character, or abort */ if (!get_com("Enter character of monster: ", &sym)) { /* Restore */ screen_load(); return (FALSE); } /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, u16b); /* Find that character info, and describe it */ for (i = 0; ident_info[i]; ++i) { if (sym == ident_info[i][0]) break; } if (ident_info[i]) { prtf(10, 16, "%c - %s.", sym, ident_info[i] + 2); } else { prtf(10, 16, "%c - %s.", sym, "Unknown Symbol"); } /* Collect matching monsters */ for (n = 0, i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; cheat_know = TRUE; /* Collect "appropriate" monsters */ if (r_ptr->d_char == sym) who[n++] = i; } /* Back to what it was */ cheat_know = oldcheat; /* Nothing to recall */ if (!n) { /* Free the "who" array */ KILL(who); /* Restore */ screen_load(); return (FALSE); } /* Sort by level */ why = 2; query = 'y'; /* Sort if needed */ if (why) { /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array */ ang_sort(who, &why, n); } /* Start at the end */ i = n - 1; /* Scan the monster memory */ while (!picked) { /* Extract a race */ r_idx = who[i]; /* Save this monster ID */ p_ptr->monster_race_idx = r_idx; /* Hack -- Handle stuff */ handle_stuff(); /* Hack -- Begin the prompt */ roff_mon_top(r_idx); /* Hack -- Complete the prompt */ roff(" [(r)ecall, ESC, space to continue]"); /* Interact */ while (1) { /* Recall */ if (recall) { /* Recall on screen */ r2_ptr = &r_info[r_idx]; /* Have you researched this monster before? */ if (r2_ptr->r_flags[6] & RF6_LIBRARY) { /* Looking up a monster the second time is for free */ cost_gold = FALSE; } else { /* This monster has now been researched */ r2_ptr->r_flags[6] |= RF6_LIBRARY; /* You've seen this monster now (in a book) */ if (!r2_ptr->r_sights) r2_ptr->r_sights = 1; } oldkills = r2_ptr->r_tkills; oldwake = r2_ptr->r_wake; /* Show the monster */ screen_roff_mon(who[i], 1); r2_ptr->r_tkills = oldkills; r2_ptr->r_wake = oldwake; picked = TRUE; } /* Command */ query = inkey(); /* Normal commands */ if (query != 'r') break; /* Toggle recall */ recall = !recall; } /* Stop scanning */ if (query == ESCAPE) break; /* Move to "prev" monster */ if (query == '-') { if (++i == n) { i = 0; if (!expand_list) break; } } /* Move to "next" monster */ else { if (i-- == 0) { i = n - 1; if (!expand_list) break; } } } /* Free the "who" array */ KILL(who); /* Restore */ screen_load(); return (picked && cost_gold); } zangband/src/cmd4.c0000755000000000000000000023727210250356274013146 0ustar rootroot/* File: cmd4.c */ /* Purpose: Interface commands */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Hack -- redraw the screen * * This command performs various low level updates, clears all the "extra" * windows, does a total redraw of the main window, and requests all of the * interesting updates and redraws that I can think of. * * This command is also used to "instantiate" the results of the user * selecting various things, such as graphics mode, so it must call * the "TERM_XTRA_REACT" hook before redrawing the windows. */ void do_cmd_redraw(void) { int j; term *old = Term; /* Hack -- react to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Update torch */ p_ptr->update |= (PU_TORCH); /* Update stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Update view */ p_ptr->update |= (PU_VIEW | PU_MON_LITE); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw everything */ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY); /* Window stuff */ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_SPELL | PW_PLAYER); /* Window stuff */ p_ptr->window |= (PW_MESSAGE | PW_OVERHEAD | PW_DUNGEON | PW_MONSTER | PW_VISIBLE | PW_OBJECT); /* Hack -- update */ handle_stuff(); /* Redraw every window */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Dead window */ if (!angband_term[j]) continue; /* Activate */ Term_activate(angband_term[j]); /* Redraw */ Term_redraw(); /* Refresh */ Term_fresh(); } /* Restore */ Term_activate(old); } /* * Map resizing whenever the main term changes size */ void resize_map(void) { /* Only if the dungeon exists */ if (!character_dungeon) return; /* Reset the panels */ p_ptr->update |= (PU_MAP); /* Update torch */ p_ptr->update |= (PU_TORCH); /* Update stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Update view */ p_ptr->update |= (PU_VIEW | PU_MON_LITE); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw everything */ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY); /* Hack -- update */ handle_stuff(); /* Place the cursor on the player */ if (!character_icky) move_cursor_relative(p_ptr->px, p_ptr->py); /* Refresh */ Term_fresh(); } /* * Redraw a term when it is resized */ void redraw_window(void) { /* Only if the dungeon exists */ if (!character_dungeon) return; /* Hack - Activate term zero for the redraw */ Term_activate(&term_screen[0]); /* Hack -- react to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Window stuff */ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_SPELL | PW_PLAYER); /* Window stuff */ p_ptr->window |= (PW_MESSAGE | PW_OVERHEAD | PW_DUNGEON | PW_MONSTER | PW_VISIBLE | PW_OBJECT); /* Hack -- update */ handle_stuff(); /* Refresh */ Term_fresh(); } /* * Show previous messages to the user -BEN- * * The screen format uses line 0 and 23 for headers and prompts, * skips line 1 and 22, and uses line 2 thru 21 for old messages. * * This command shows you which commands you are viewing, and allows * you to "search" for strings in the recall. * * Note that messages may be longer than 80 characters, but they are * displayed using "infinite" length, with a special sub-command to * "slide" the virtual display to the left or right. * * Attempt to only hilite the matching portions of the string. */ void do_cmd_messages(void) { int i, j, k, n; int q; char shower[80]; char finder[80]; int wid, hgt; /* Wipe finder */ finder[0] = 0; /* Wipe shower */ shower[0] = 0; /* Total messages */ n = message_num(); /* Start on first message */ i = 0; /* Start at leftmost edge */ q = 0; /* Save the screen */ screen_save(); /* Get size */ Term_get_size(&wid, &hgt); /* Process requests until done */ while (1) { /* Clear screen */ Term_clear(); /* Dump messages */ for (j = 0; (j < hgt - 4) && (i + j < n); j++) { cptr msg = message_str((s16b)(i + j)); cptr attr = color_seq[message_color((s16b)(i + j))]; /* Apply horizontal scroll */ msg = ((int)strlen(msg) >= q) ? (msg + q) : ""; /* Dump the messages, bottom to top */ put_fstr(0, hgt - 3 - j, "%s%s", attr, msg); /* Hilite "shower" */ if (shower[0]) { cptr str = msg; /* Display matches */ while ((str = strstr(str, shower)) != NULL) { int len = strlen(shower); /* Display the match */ put_fstr(str - msg, hgt - 3 - j, CLR_YELLOW "%s", shower); /* Advance */ str += len; } } } /* Display header XXX XXX XXX */ prtf(0, 0, "Message Recall (%d-%d of %d), Offset %d", i, i + j - 1, n, q); /* Display prompt (not very informative) */ prtf(0, hgt - 1, "[Press 'p' for older, 'n' for newer, ..., or ESCAPE]"); /* Get a command */ k = inkey(); /* Exit on Escape */ if (k == ESCAPE) break; /* Hack -- Save the old index */ j = i; /* Horizontal scroll */ if (k == '4') { /* Scroll left */ q = (q >= wid / 2) ? (q - wid / 2) : 0; /* Success */ continue; } /* Horizontal scroll */ if (k == '6') { /* Scroll right */ q = q + wid / 2; /* Success */ continue; } /* Hack -- handle show */ if (k == '=') { /* Prompt */ prtf(0, hgt - 1, "Show: "); /* Get a "shower" string, or continue */ if (!askfor_aux(shower, 80)) continue; /* Okay */ continue; } /* Hack -- handle find */ if (k == '/') { s16b z; /* Prompt */ prtf(0, hgt - 1, "Find: "); /* Get a "finder" string, or continue */ if (!askfor_aux(finder, 80)) continue; /* Show it */ strcpy(shower, finder); /* Scan messages */ for (z = i + 1; z < n; z++) { cptr msg = message_str(z); /* Search for it */ if (strstr(msg, finder)) { /* New location */ i = z; /* Done */ break; } } } /* Recall 1 older message */ if ((k == '8') || (k == '\n') || (k == '\r')) { /* Go newer if legal */ if (i + 1 < n) i += 1; } /* Recall 10 older messages */ if (k == '+') { /* Go older if legal */ if (i + 10 < n) i += 10; } /* Recall 20 older messages */ if ((k == 'p') || (k == KTRL('P')) || (k == ' ')) { /* Go older if legal */ if (i + 20 < n) i += 20; } /* Recall 20 newer messages */ if ((k == 'n') || (k == KTRL('N'))) { /* Go newer (if able) */ i = (i >= 20) ? (i - 20) : 0; } /* Recall 10 newer messages */ if (k == '-') { /* Go newer (if able) */ i = (i >= 10) ? (i - 10) : 0; } /* Recall 1 newer messages */ if (k == '2') { /* Go newer (if able) */ i = (i >= 1) ? (i - 1) : 0; } /* Hack -- Error of some kind */ if (i == j) bell(NULL); } /* Restore the screen */ screen_load(); } /* * Copy the indicated options out from the * option_info[] data structure into the * player, birth and server data structures as indicated. * * Options that should be unchanged are restored to the * current state. (This stops the player changing the * birth options in the middle of the game.) */ void init_options(byte flags) { int birth_counter = 0, server_counter = 0, player_counter = 0; int i; for (i = 0; i < OPT_MAX; i++) { /* A birth option? */ if (i == birth_options[birth_counter]) { /* Are we allowed to copy the data? */ if (flags & OPT_FLAG_BIRTH) { /* Copy the state of the flag into p_ptr->birth[] */ p_ptr->birth[birth_counter] = option_info[i].o_val; } else { /* Restore the option to its original value */ option_info[i].o_val = p_ptr->birth[birth_counter]; } /* Increment birth option counter */ birth_counter++; } /* A server option? */ else if (i == server_options[server_counter]) { /* Are we allowed to copy the data? */ if (flags & OPT_FLAG_SERVER) { /* Copy the state of the flag into svr_ptr->options[] */ svr_ptr->options[server_counter] = option_info[i].o_val; } else { /* Restore the option to its original value */ option_info[i].o_val = svr_ptr->options[server_counter]; } /* Increment server option counter */ server_counter++; } /* A player option */ else { /* Are we allowed to copy the data? */ if (flags & OPT_FLAG_PLAYER) { /* Copy the state of the flag into p_ptr->options[] */ p_ptr->options[player_counter] = option_info[i].o_val; } else { /* Restore the option to its original value */ option_info[i].o_val = p_ptr->options[player_counter]; } /* Increment player option counter */ player_counter++; } } /* * XXX XXX XXX This is an absolutely evil hack. * If ironman_downward is set - set vanilla town as well */ if (ironman_downward) { vanilla_town = TRUE; /* And here is the bit that shouldn't see the light of day. */ option_info[192].o_val = TRUE; } } /* * Number of cheating options */ #define CHEAT_MAX 6 typedef struct cheat_option_type cheat_option_type; struct cheat_option_type { bool *o_var; u16b o_word; cptr o_text; cptr o_desc; }; /* * Cheating options */ static const cheat_option_type cheat_info[CHEAT_MAX] = { {&cheat_peek, 0x0001, "cheat_peek", "Peek into object creation"}, {&cheat_hear, 0x0002, "cheat_hear", "Peek into monster creation"}, {&cheat_room, 0x0004, "cheat_room", "Peek into dungeon creation"}, {&cheat_xtra, 0x0008, "cheat_xtra", "Peek into something else"}, {&cheat_know, 0x0010, "cheat_know", "Know complete monster info"}, {&cheat_live, 0x0020, "cheat_live", "Allow player to avoid death"} }; /* Forward declare */ extern menu_type cheat_menu[CHEAT_MAX + 1]; static bool do_cmd_options_cheat_aux(int option) { char buf[1024]; /* Toggle the option */ (*cheat_info[option].o_var) = !(*cheat_info[option].o_var); if (*cheat_info[option].o_var) { /* Turn on the cheating flag */ p_ptr->state.noscore |= cheat_info[option].o_word; } /* Change the option text */ strnfmt(buf, 1024, "%-48s: %s (%s)", cheat_info[option].o_desc, (*cheat_info[option].o_var ? "yes" : "no "), cheat_info[option].o_text); /* Delete old string */ string_free(cheat_menu[option].text); /* Save new string */ cheat_menu[option].text = string_make(buf); return (FALSE); } menu_type cheat_menu[CHEAT_MAX + 1] = { {NULL, NULL, do_cmd_options_cheat_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_cheat_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_cheat_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_cheat_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_cheat_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_cheat_aux, MN_ACTIVE | MN_SELECT}, MENU_END }; /* * Interact with some options for cheating */ static bool do_cmd_options_cheat(int dummy) { char buf[1024]; int i; /* Hack - ignore unused parameter */ (void) dummy; /* Create the list of options */ for (i = 0; i < CHEAT_MAX; i++) { /* Change the option text */ strnfmt(buf, 1024, "%-48s: %s (%s)", cheat_info[i].o_desc, (*cheat_info[i].o_var ? "yes" : "no "), cheat_info[i].o_text); /* Delete old string */ string_free(cheat_menu[i].text); /* Save new string */ cheat_menu[i].text = string_make(buf); } display_menu(cheat_menu, 0, TRUE, NULL, "Cheaters never win"); return (FALSE); } static const cheat_option_type autosave_info[2] = { {(bool *)(&autosave_l), 0x0001, "autosave_l", "Autosave when entering new levels"}, {(bool *)(&autosave_t), 0x0002, "autosave_t", "Timed autosave"}, }; /* Forward declare */ extern menu_type autosave_menu[4]; static bool do_cmd_options_toggle_frequency(int option) { s16b current = autosave_freq; char buf[1024]; if (current == 0) autosave_freq = 50; if (current == 50) autosave_freq = 100; if (current == 100) autosave_freq = 250; if (current == 250) autosave_freq = 500; if (current == 500) autosave_freq = 1000; if (current == 1000) autosave_freq = 2500; if (current == 2500) autosave_freq = 5000; if (current == 5000) autosave_freq = 10000; if (current == 10000) autosave_freq = 25000; if (current == 25000) autosave_freq = 0; strnfmt(buf, 1024, "Timed autosave frequency: every %d turns", autosave_freq); /* Delete old string */ string_free(autosave_menu[option].text); /* Save new string */ autosave_menu[option].text = string_make(buf); return (FALSE); } static bool do_cmd_options_autosave_aux(int option) { char buf[1024]; /* Toggle the option */ (*autosave_info[option].o_var) = !(*autosave_info[option].o_var); /* Change the option text */ strnfmt(buf, 1024, "%-48s: %s (%s)", autosave_info[option].o_desc, (*autosave_info[option].o_var ? "yes" : "no "), autosave_info[option].o_text); /* Delete old string */ string_free(autosave_menu[option].text); /* Save new string */ autosave_menu[option].text = string_make(buf); return (FALSE); } menu_type autosave_menu[4] = { {NULL, NULL, do_cmd_options_autosave_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_autosave_aux, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_toggle_frequency, MN_ACTIVE}, MENU_END }; /* * Interact with some options for cheating */ static bool do_cmd_options_autosave(int dummy) { char buf[1024]; int i; /* Hack - ignore unused parameter */ (void) dummy; for (i = 0; i < 2; i++) { /* Change the option text */ strnfmt(buf, 1024, "%-48s: %s (%s)", autosave_info[i].o_desc, (*autosave_info[i].o_var ? "yes" : "no "), autosave_info[i].o_text); /* Delete old string */ string_free(autosave_menu[i].text); /* Save new string */ autosave_menu[i].text = string_make(buf); } /* Get string for autosave frequency */ strnfmt(buf, 1024, "Timed autosave frequency: every %d turns", autosave_freq); /* Delete old string */ string_free(autosave_menu[2].text); /* Save new string */ autosave_menu[2].text = string_make(buf); display_menu(autosave_menu, 0, TRUE, NULL, "Autosave"); return (FALSE); } /* Current option flags */ static byte option_flags; static int option_page; /* Forward declare */ extern menu_type options_aux_menu[25]; /* * Toggle the selected option */ static bool do_cmd_options_aux2(int option) { int i, j = option; char buf[1024]; /* Find the option to change */ for (i = 0; option_info[i].o_desc; i++) { /* Notice options on this "page" */ if ((option_info[i].o_page == option_page) && (option_info[i].o_text)) { if (!j) { option_info[i].o_val = !option_info[i].o_val; strnfmt(buf, 1024, "%-48s: %s (%.23s)", option_info[i].o_desc, (option_info[i].o_val ? "yes" : "no "), option_info[i].o_text); /* Update the description */ string_free(options_aux_menu[option].text); options_aux_menu[option].text = string_make(buf); /* Update the help */ strnfmt(buf, 1024, "option.txt#%s", option_info[i].o_text); string_free(options_aux_menu[option].help); options_aux_menu[option].help = string_make(buf); /* Done */ break; } else { /* Count down until we get to the required option */ j--; } } } return (FALSE); } menu_type options_aux_menu[25] = { {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, {NULL, NULL, do_cmd_options_aux2, MN_ACTIVE | MN_SELECT}, MENU_END }; /* * Screen titles for each option sub-window */ static cptr option_window_title[8] = { "User Interface Options", "Disturbance Options", "Game-Play Options", "Efficiency Options", "Display Options", "Birth Options", "Artificial Intelligence Options", "Testing Options" }; /* * Interact with some options */ static bool do_cmd_options_aux(int page) { int i, n = 0; char buf[1024]; /* Save the current page */ option_page = page + 1; /* Clear the options (24 options max + MENU_END) */ for (i = 0; i < 25; i++) { string_free(options_aux_menu[i].text); options_aux_menu[i].text = NULL; string_free(options_aux_menu[i].help); options_aux_menu[i].help = NULL; } /* Scan the options */ for (i = 0; option_info[i].o_desc; i++) { /* Notice options on this "page" */ if (option_info[i].o_page == option_page) { /* Update the description */ strnfmt(buf, 1024, "%-48s: %s (%.23s)", option_info[i].o_desc, (option_info[i].o_val ? "yes" : "no "), option_info[i].o_text); options_aux_menu[n].text = string_make(buf); /* Update the help */ strnfmt(buf, 1024, "option.txt#%s", option_info[i].o_text); options_aux_menu[n].help = string_make(buf); n++; } } /* Paranoia */ if (n == 0) { /* There are no options */ msgf("There are no available options there at the moment."); message_flush(); /* Bail out */ return (FALSE); } display_menu(options_aux_menu, 0, TRUE, NULL, option_window_title[page]); /* Save the changes */ init_options(option_flags); return (FALSE); } /* * Modify the "window" options */ static bool do_cmd_options_win(int dummy) { int i, j, d; int y = 0; int x = 0; char ch; bool go = TRUE; u32b old_flag[ANGBAND_TERM_MAX]; /* Hack - ignore parameter */ (void) dummy; /* Memorize old flags */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Acquire current flags */ old_flag[j] = window_flag[j]; } screen_save(); /* Clear screen */ Term_clear(); /* Interact */ while (go) { /* Prompt XXX XXX XXX */ prtf(0, 0, "Window Flags (, t, y, n, ESC) "); /* Display the windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Window name, staggered, centered */ put_fstr(35 + j * 5 - strlen(angband_term_name[j]) / 2, 2 + j % 2, CLR_L_BLUE "%s", angband_term_name[j]); } /* Display the options */ for (i = 0; i < WINDOW_CHOICE_MAX; i++) { cptr str = window_flag_desc[i]; /* Unused option */ if (!str) continue; /* Flag name */ put_fstr(0, i + 5, CLR_L_BLUE "%s", str); /* Display the windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { char c = '.'; byte a; a = TERM_WHITE; /* Use color */ if (use_color && (i == y) && (j == x)) { a = TERM_L_BLUE; } /* Active flag */ if (window_flag[j] & (1L << i)) c = 'X'; /* Flag value */ Term_putch(35 + j * 5, i + 5, a, c); } } /* Place Cursor */ Term_gotoxy(35 + x * 5, y + 5); /* Get key */ ch = inkey(); /* Analyze */ switch (ch) { case ESCAPE: { go = FALSE; break; } case 'T': case 't': { /* Clear windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { window_flag[j] &= ~(1L << y); } /* Clear flags */ for (i = 0; i < WINDOW_CHOICE_MAX; i++) { window_flag[x] &= ~(1L << i); } /* Fall through */ } case 'y': case 'Y': { /* Ignore screen */ if (x == 0) break; /* Set flag */ window_flag[x] |= (1L << y); break; } case 'n': case 'N': { /* Clear flag */ window_flag[x] &= ~(1L << y); break; } default: { d = get_keymap_dir(ch); x = (x + ddx[d] + 8) % 8; y = (y + ddy[d] + WINDOW_CHOICE_MAX) % WINDOW_CHOICE_MAX; if (!d) bell("Illegal command for window options!"); } } } /* Hack - assume all windows will change */ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_SPELL | PW_PLAYER | PW_MESSAGE | PW_OVERHEAD | PW_MONSTER | PW_OBJECT | PW_SNAPSHOT | PW_BORG_1 | PW_BORG_2 | PW_DUNGEON); /* Notice changes */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* Dead window */ if (!angband_term[j]) continue; /* Ignore non-changes */ if (window_flag[j] == old_flag[j]) continue; /* Activate */ Term_activate(angband_term[j]); /* Erase */ Term_clear(); /* Refresh */ Term_fresh(); /* Restore */ Term_activate(old); } screen_load(); return (FALSE); } /* * Modify the base delay factor */ static bool do_cmd_options_delay(int dummy) { char k; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Clear screen */ Term_clear(); /* Prompt */ prtf(0, 18, "Command: Base Delay Factor"); /* Get a new value */ while (1) { int msec = delay_factor * delay_factor * delay_factor; prtf(0, 22, "Current base delay factor: %d (%d msec)", delay_factor, msec); prtf(0, 20, "Delay Factor (0-9 or ESC to accept): "); k = inkey(); if (k == ESCAPE) break; if (isdigit(k)) delay_factor = D2I(k); else bell("Illegal delay factor!"); } screen_load(); return (FALSE); } /* * Modify the hitpoint warning threshold */ static bool do_cmd_options_hitpoint(int dummy) { char k; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Clear screen */ Term_clear(); /* Prompt */ prtf(0, 18, "Command: Hitpoint Warning"); /* Get a new value */ while (1) { prtf(0, 22, "Current hitpoint warning: %d0%%", hitpoint_warn); prtf(0, 20, "Hitpoint Warning (0-9 or ESC to accept): "); k = inkey(); if (k == ESCAPE) break; if (isdigit(k)) hitpoint_warn = D2I(k); else bell("Illegal hitpoint warning!"); } screen_load(); return (FALSE); } static bool do_cmd_options_dump(int dummy) { int i; FILE *fff; char buf[1024]; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, "pref-opt.prf"); /* Open the file */ fff = my_fopen(buf, "w"); /* Failed */ if (!fff) return (FALSE); /* Header */ froff(fff, "# File: pref-opt.prf\n\n"); froff(fff, "# Allow user specification of various options\n\n"); /* Scan the options */ for (i = 0; i < OPT_MAX; i++) { if ((option_info[i].o_text) && (option_info[i].o_page != OPT_BIRTH_PAGE)) { /* Dump the option */ froff(fff, "%c:%s\n", (option_info[i].o_val ? 'Y' : 'X'), option_info[i].o_text); } } /* Close the file */ my_fclose(fff); /* Clear top row */ clear_msg(); /* Success message */ msgf("Saved default options."); screen_load(); return (FALSE); } /* Number of things in the main options menu */ #define OPTION_MENU_MAX 18 /* The main options menu */ static menu_type options_menu[OPTION_MENU_MAX] = { {"User Interface Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Disturbance Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Game-Play Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Efficiency Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Display Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Birth Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Artificial Intelligence Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Testing Options", NULL, do_cmd_options_aux, MN_ACTIVE | MN_SELECT | MN_CLEAR}, MENU_SEPERATOR, {"Cheating Options", NULL, do_cmd_options_cheat, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Base Delay Factor", NULL, do_cmd_options_delay, MN_ACTIVE | MN_SELECT}, {"Hitpoint Warning", NULL, do_cmd_options_hitpoint, MN_ACTIVE | MN_SELECT}, MENU_SEPERATOR, {"Autosave Options", NULL, do_cmd_options_autosave, MN_ACTIVE | MN_SELECT | MN_CLEAR}, {"Window Flags", NULL, do_cmd_options_win, MN_ACTIVE | MN_SELECT}, MENU_SEPERATOR, {"Dump Options to a Pref File", NULL, do_cmd_options_dump, MN_ACTIVE | MN_SELECT}, MENU_END }; /* * Set or unset various options. * * The user must use the "Ctrl-R" command to "adapt" to changes * in any options which control "visual" aspects of the game. */ void do_cmd_options(byte flags) { /* Save option flags so menu functions can access them */ option_flags = flags; display_menu(options_menu, 0, TRUE, NULL, VERSION_NAME " options"); /* Hack - Redraw equippy chars */ p_ptr->redraw |= (PR_EQUIPPY); } /* * Ask for a "user pref line" and process it * * XXX XXX XXX Allow absolute file names? */ void do_cmd_pref(void) { char buf[80]; /* Default */ buf[0] = 0; /* Ask for a "user pref command" */ if (!get_string(buf, 80, "Pref: ")) return; /* Process that pref command */ (void)process_pref_file_command(buf); } #ifdef ALLOW_MACROS /* * Hack -- append all current macros to the given file */ errr macro_dump(cptr fname) { int i; FILE *fff; char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) return (-1); /* Skip space */ froff(fff, "\n\n"); /* Start dumping */ froff(fff, "# Automatic macro dump\n\n"); /* Dump them */ for (i = 0; i < macro__num; i++) { /* Start the macro */ froff(fff, "# Macro '%d'\n\n", i); /* Extract the action */ ascii_to_text(buf, macro__act[i]); /* Dump the macro */ froff(fff, "A:%s\n", buf); /* Extract the action */ ascii_to_text(buf, macro__pat[i]); /* Dump normal macros */ froff(fff, "P:%s\n", buf); /* End the macro */ froff(fff, "\n\n"); } /* Start dumping */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Success */ return (0); } /* * Hack -- ask for a "trigger" (see below) * * Note the complex use of the "inkey()" function from "util.c". * * Note that both "flush()" calls are extremely important. */ static void do_cmd_macro_aux(char *buf) { int i, n = 0; char tmp[1024]; /* Flush */ flush(); /* Do not process macros */ p_ptr->cmd.inkey_base = TRUE; /* First key */ i = inkey(); /* Read the pattern */ while (i) { /* Save the key */ buf[n++] = i; /* Do not process macros */ p_ptr->cmd.inkey_base = TRUE; /* Do not wait for keys */ p_ptr->cmd.inkey_scan = TRUE; /* Attempt to read a key */ i = inkey(); } /* Terminate */ buf[n] = '\0'; /* Flush */ flush(); /* Convert the trigger */ ascii_to_text(tmp, buf); /* Hack -- display the trigger */ roff("%s", tmp); } #endif /* * Hack -- ask for a keymap "trigger" (see below) * * Note that both "flush()" calls are extremely important. This may * no longer be true, since "util.c" is much simpler now. XXX XXX XXX */ static void do_cmd_macro_aux_keymap(char *buf) { char tmp[1024]; /* Flush */ flush(); /* Get a key */ buf[0] = inkey(); buf[1] = '\0'; /* Convert to ascii */ ascii_to_text(tmp, buf); /* Hack -- display the trigger */ roff("%s", tmp); /* Flush */ flush(); } /* * Hack -- append all keymaps to the given file */ errr keymap_dump(cptr fname) { int i; FILE *fff; char key[1024]; char buf[1024]; int mode; /* Roguelike */ if (rogue_like_commands) { mode = KEYMAP_MODE_ROGUE; } /* Original */ else { mode = KEYMAP_MODE_ORIG; } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) return (-1); /* Skip space */ froff(fff, "\n\n"); /* Start dumping */ froff(fff, "# Automatic keymap dump\n\n"); /* Dump them */ for (i = 0; i < 256; i++) { cptr act; /* Loop up the keymap */ act = keymap_act[mode][i]; /* Skip empty keymaps */ if (!act) continue; /* Encode the key */ buf[0] = i; buf[1] = '\0'; ascii_to_text(key, buf); /* Encode the action */ ascii_to_text(buf, act); /* Dump the macro */ froff(fff, "A:%s\n", buf); froff(fff, "C:%d:%s\n", mode, key); } /* Start dumping */ froff(fff, "\n\n\n"); /* Close */ my_fclose(fff); /* Success */ return (0); } /* * Load a user pref file */ static bool do_cmd_pref_key_load(int dummy) { char tmp[1024]; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Load a user pref file\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "%s.prf", player_base); /* Ask for a file */ if (!askfor_aux(tmp, 80)) { return (FALSE); } /* Process the given filename */ if (0 != process_pref_file("%s", tmp)) { /* Prompt */ msgf("Could not load file!"); } return (FALSE); } #ifdef ALLOW_MACROS static int display_cur_action(int dummy) { char buf[1024]; /* Hack - ignore parameter */ (void) dummy; Term_clear(); /* Analyze the current action */ ascii_to_text(buf, macro__buf); /* Describe + display that action */ prtf(0, 20, "Current action (if any) shown below:\n\n%s", buf); /* No offset */ return (0); } /* * Append macros to a file */ static bool do_cmd_macro_append(int dummy) { char tmp[1024]; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Append macros to a file\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "%s.prf", player_base); /* Ask for a file */ if (!askfor_aux(tmp, 80)) { return (FALSE); } /* Dump the macros */ (void)macro_dump(tmp); /* Prompt */ msgf("Appended macros."); return (FALSE); } /* * Query a macro */ static bool do_cmd_macro_query(int dummy) { char tmp[1024]; int k; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Query a macro\n\n" "Trigger: "); /* Get a macro trigger */ do_cmd_macro_aux(tmp); /* Acquire action */ k = macro_find_exact(tmp); /* Nothing found */ if (k < 0) { /* Prompt */ msgf("Found no macro."); } /* Found one */ else { /* Obtain the action */ strcpy(macro__buf, macro__act[k]); /* Analyze the current action */ ascii_to_text(tmp, macro__buf); /* Display the current action */ prtf(0, 22, tmp); /* Prompt */ msgf("Found a macro."); } return (FALSE); } /* * Create a macro */ static bool do_cmd_macro_create(int dummy) { char tmp[1024]; char buf[1024]; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Create a macro\n\n" "Trigger: "); /* Get a macro trigger */ do_cmd_macro_aux(tmp); /* Clear */ clear_from(20); /* Prompt */ prtf(0, 20, "Action: "); /* Convert to text */ ascii_to_text(buf, macro__buf); /* Get an encoded action */ if (askfor_aux(buf, 80)) { /* Convert to ascii */ text_to_ascii(macro__buf, buf); /* Link the macro */ macro_add(tmp, macro__buf); /* Prompt */ msgf("Added a macro."); } return (FALSE); } /* * Remove a macro */ static bool do_cmd_macro_remove(int dummy) { char tmp[1024]; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Remove a macro\n\n" "Trigger: "); /* Get a macro trigger */ do_cmd_macro_aux(tmp); /* Link the macro */ macro_add(tmp, tmp); /* Prompt */ msgf("Removed a macro."); return (FALSE); } /* * Append the keymaps to a file */ static bool do_cmd_keymap_append(int dummy) { char tmp[1024]; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Append keymaps to a file\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "%s.prf", player_base); /* Ask for a file */ if (!askfor_aux(tmp, 80)) { return (FALSE); } /* Dump the macros */ (void)keymap_dump(tmp); /* Prompt */ msgf("Appended keymaps."); return (FALSE); } /* * Query a keymap */ static bool do_cmd_keymap_query(int dummy) { char buf[1024]; cptr act; int mode; /* Hack - ignore parameter */ (void) dummy; /* Roguelike */ if (rogue_like_commands) { mode = KEYMAP_MODE_ROGUE; } /* Original */ else { mode = KEYMAP_MODE_ORIG; } /* Prompt */ prtf(0, 16, "Command: Query a keymap\n\n" "Keypress: "); /* Get a keymap trigger */ do_cmd_macro_aux_keymap(buf); /* Look up the keymap */ act = keymap_act[mode][(byte)(buf[0])]; /* Nothing found */ if (!act) { /* Prompt */ msgf("Found no keymap."); } /* Found one */ else { /* Obtain the action */ strcpy(macro__buf, act); /* Analyze the current action */ ascii_to_text(buf, macro__buf); /* Display the current action */ prtf(0, 22, buf); /* Prompt */ msgf("Found a keymap."); } return (FALSE); } /* * Create a keymap */ static bool do_cmd_keymap_create(int dummy) { char tmp[1024]; char buf[1024]; int mode; /* Hack - ignore parameter */ (void) dummy; /* Roguelike */ if (rogue_like_commands) { mode = KEYMAP_MODE_ROGUE; } /* Original */ else { mode = KEYMAP_MODE_ORIG; } /* Prompt */ prtf(0, 16, "Command: Create a keymap\n\n" "Keypress: "); /* Get a keymap trigger */ do_cmd_macro_aux_keymap(buf); /* Clear */ clear_from(20); /* Prompt */ prtf(0, 20, "Action: "); /* Convert to text */ ascii_to_text(tmp, macro__buf); /* Get an encoded action */ if (askfor_aux(tmp, 80)) { /* Convert to ascii */ text_to_ascii(macro__buf, tmp); /* Free old keymap */ string_free(keymap_act[mode][(byte)(buf[0])]); /* Make new keymap */ keymap_act[mode][(byte)(buf[0])] = string_make(macro__buf); /* Prompt */ msgf("Added a keymap."); } return (FALSE); } /* * Remove a keymap */ static bool do_cmd_keymap_remove(int dummy) { char buf[1024]; int mode; /* Hack - ignore parameter */ (void) dummy; /* Roguelike */ if (rogue_like_commands) { mode = KEYMAP_MODE_ROGUE; } /* Original */ else { mode = KEYMAP_MODE_ORIG; } /* Prompt */ prtf(0, 16, "Command: Remove a keymap\n\n" "Keypress: "); /* Get a keymap trigger */ do_cmd_macro_aux_keymap(buf); /* Free old keymap */ string_free(keymap_act[mode][(byte)(buf[0])]); /* Make new keymap */ keymap_act[mode][(byte)(buf[0])] = NULL; /* Prompt */ msgf("Removed a keymap."); return (FALSE); } /* * Create a new action */ static bool do_cmd_action_create(int dummy) { char buf[1024]; /* Hack - ignore parameter */ (void) dummy; /* Prompt */ prtf(0, 16, "Command: Enter a new action"); /* Go to the correct location */ Term_gotoxy(0, 22); /* Get the current default action */ ascii_to_text(buf, macro__buf); /* Hack -- limit the value */ buf[80] = '\0'; /* Get an encoded action */ if (!askfor_aux(buf, 80)) { return (FALSE); } /* Extract an action */ text_to_ascii(macro__buf, buf); return (FALSE); } #endif /* ALLOW_MACROS */ #ifdef ALLOW_MACROS #define MACRO_MENU_MAX 11 #else #define MACRO_MENU_MAX 2 #endif /* ALLOW_MACROS */ /* The macro / keymap menu */ static menu_type macro_menu[MACRO_MENU_MAX] = { {"Load a user pref file", NULL, do_cmd_pref_key_load, MN_ACTIVE}, #ifdef ALLOW_MACROS {"Append macros to a file", NULL, do_cmd_macro_append, MN_ACTIVE}, {"Query a macro", NULL, do_cmd_macro_query, MN_ACTIVE}, {"Create a macro", NULL, do_cmd_macro_create, MN_ACTIVE}, {"Remove a macro", NULL, do_cmd_macro_remove, MN_ACTIVE}, {"Append keymaps to a file", NULL, do_cmd_keymap_append, MN_ACTIVE}, {"Query a keymap", NULL, do_cmd_keymap_query, MN_ACTIVE}, {"Create a keymap", NULL, do_cmd_keymap_create, MN_ACTIVE}, {"Remove a keymap", NULL, do_cmd_keymap_remove, MN_ACTIVE}, {"Enter a new action", NULL, do_cmd_action_create, MN_ACTIVE}, #endif /* ALLOW_MACROS */ MENU_END }; /* * Interact with "macros" * * Note that the macro "action" must be defined before the trigger. * * Could use some helpful instructions on this page. XXX XXX XXX */ void do_cmd_macros(void) { /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); display_menu(macro_menu, -1, FALSE, display_cur_action, "Interact with Macros"); } /* * Load a user pref file */ static bool do_cmd_pref_vis_load(int dummy) { char tmp[1024]; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 13, "Command: Load a user pref file\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Query */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Process the given filename */ if (0 != process_pref_file("%s", tmp)) { /* Prompt */ msgf("Could not load file!"); } screen_load(); return (FALSE); } #ifdef ALLOW_VISUALS /* Dump monster attr/chars to pref file */ static bool do_cmd_dump_monster(int dummy) { char tmp[1024], buf[1024]; FILE *fff; int i; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 12, "Command: Dump monster attr/chars\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Get a filename */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, tmp); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) { screen_load(); return (FALSE); } /* Start dumping */ froff(fff, "\n\n"); froff(fff, "# Monster attr/char definitions\n\n"); /* Dump monsters */ for (i = 0; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-entries */ if (!r_ptr->name) continue; /* Dump a comment */ froff(fff, "# %s\n", mon_race_name(r_ptr)); /* Dump the monster attr/char info */ froff(fff, "R:%d:0x%02X:0x%02X\n\n", i, (byte)(r_ptr->x_attr), (byte)(r_ptr->x_char)); } /* All done */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Message */ msgf("Dumped monster attr/chars."); screen_load(); return (FALSE); } /* Dump object attr/chars to a pref file */ static bool do_cmd_dump_object(int dummy) { char tmp[1024], buf[1024]; FILE *fff; int i; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 12, "Command: Dump object attr/chars\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Get a filename */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, tmp); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) { screen_load(); return (FALSE); } /* Start dumping */ froff(fff, "\n\n"); froff(fff, "# Object attr/char definitions\n\n"); /* Dump objects */ for (i = 0; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; /* Skip non-entries */ if (!k_ptr->name) continue; /* Dump a comment */ froff(fff, "# %s\n", (k_name + k_ptr->name)); /* Dump the object attr/char info */ froff(fff, "K:%d:0x%02X:0x%02X\n\n", i, (byte)(k_ptr->x_attr), (byte)(k_ptr->x_char)); } /* All done */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Message */ msgf("Dumped object attr/chars."); screen_load(); return (FALSE); } /* Dump terrain feature attr/chars to a pref file */ static bool do_cmd_dump_feature(int dummy) { char tmp[1024], buf[1024]; FILE *fff; int i; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 12, "Command: Dump feature attr/chars\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Get a filename */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, tmp); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) { screen_load(); return (FALSE); } /* Start dumping */ froff(fff, "\n\n"); froff(fff, "# Feature attr/char definitions\n\n"); /* Dump features */ for (i = 0; i < z_info->f_max; i++) { feature_type *f_ptr = &f_info[i]; /* Skip non-entries */ if (!f_ptr->name) continue; /* Dump a comment */ froff(fff, "# %s\n", (f_name + f_ptr->name)); /* Dump the feature attr/char info */ froff(fff, "F:%d:0x%02X:0x%02X\n\n", i, (byte)(f_ptr->x_attr), (byte)(f_ptr->x_char)); } /* All done */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Message */ msgf("Dumped feature attr/chars."); screen_load(); return (FALSE); } /* Dump field attr/chars to a pref file */ static bool do_cmd_dump_field(int dummy) { char tmp[1024], buf[1024]; FILE *fff; int i; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 12, "Command: Dump field attr/chars\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Get a filename */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, tmp); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) { screen_load(); return (FALSE); } /* Start dumping */ froff(fff, "\n\n"); froff(fff, "# Field attr/char definitions\n\n"); /* Dump features */ for (i = 0; i < z_info->t_max; i++) { field_thaum *t_ptr = &t_info[i]; /* Skip non-entries */ if (!t_ptr->name) continue; /* Dump a comment */ froff(fff, "# %s\n", t_ptr->name); /* Dump the field attr/char info */ froff(fff, "F:%d:0x%02X:0x%02X\n\n", i, (byte)(t_ptr->f_attr), (byte)(t_ptr->f_char)); } /* All done */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Message */ msgf("Dumped field attr/chars."); screen_load(); return (FALSE); } /* Modify monster attr/chars */ static bool do_cmd_change_monster(int dummy) { static int r = 0; char i; /* Hack - ignore parameter */ (void) dummy; screen_save(); Term_clear(); /* Prompt */ prtf(0, 5, "Command: Change monster attr/chars"); /* Hack -- query until done */ while (1) { monster_race *r_ptr = &r_info[r]; byte da = (r_ptr->d_attr); byte dc = (r_ptr->d_char); byte ca = (r_ptr->x_attr); byte cc = (r_ptr->x_char); /* Label the object */ prtf(5, 7, "Monster = %d, Name = %-40.40s", r, mon_race_name(r_ptr)); /* Label the Default values */ prtf(10, 9, "Default attr/char = %3u / %3u", da, dc); put_fstr(40, 9, "<< ? >>"); Term_putch(43, 9, da, dc); /* Label the Current values */ prtf(10, 10, "Current attr/char = %3u / %3u", ca, cc); put_fstr(40, 10, "<< ? >>"); Term_putch(43, 10, ca, cc); /* Prompt */ prtf(0, 12, "Command (n/N/a/A/c/C): "); /* Get a command */ i = inkey(); /* All done */ if (i == ESCAPE) break; /* Analyze */ if (i == 'n') r = (r + z_info->r_max + 1) % z_info->r_max; if (i == 'N') r = (r + z_info->r_max - 1) % z_info->r_max; if (i == 'a') r_ptr->x_attr = (byte)(ca + 1); if (i == 'A') r_ptr->x_attr = (byte)(ca - 1); if (i == 'c') r_ptr->x_char = (byte)(cc + 1); if (i == 'C') r_ptr->x_char = (byte)(cc - 1); } screen_load(); return (FALSE); } /* Modify object attr/chars */ static bool do_cmd_change_object(int dummy) { static int k = 0; char i; /* Hack - ignore parameters */ (void) dummy; screen_save(); Term_clear(); /* Prompt */ prtf(0, 5, "Command: Change object attr/chars"); /* Hack -- query until done */ while (1) { object_kind *k_ptr = &k_info[k]; byte da = (byte)k_ptr->d_attr; byte dc = (byte)k_ptr->d_char; byte ca = (byte)k_ptr->x_attr; byte cc = (byte)k_ptr->x_char; /* Label the object */ prtf(5, 7, "Object = %d, Name = %-40.40s", k, (k_name + k_ptr->name)); /* Label the Default values */ prtf(10, 9, "Default attr/char = %3d / %3d", da, dc); put_fstr(40, 9, "<< ? >>"); Term_putch(43, 9, da, dc); /* Label the Current values */ prtf(10, 10, "Current attr/char = %3d / %3d", ca, cc); put_fstr(40, 10, "<< ? >>"); Term_putch(43, 10, ca, cc); /* Prompt */ prtf(0, 12, "Command (n/N/a/A/c/C): "); /* Get a command */ i = inkey(); /* All done */ if (i == ESCAPE) break; /* Analyze */ if (i == 'n') k = (k + z_info->k_max + 1) % z_info->k_max; if (i == 'N') k = (k + z_info->k_max - 1) % z_info->k_max; if (i == 'a') k_info[k].x_attr = (byte)(ca + 1); if (i == 'A') k_info[k].x_attr = (byte)(ca - 1); if (i == 'c') k_info[k].x_char = (byte)(cc + 1); if (i == 'C') k_info[k].x_char = (byte)(cc - 1); } screen_load(); return (FALSE); } /* Modify feature attr/chars */ static bool do_cmd_change_feature(int dummy) { static int f = 0; char i; /* Hack - ignore parameter */ (void) dummy; screen_save(); Term_clear(); /* Prompt */ prtf(0, 5, "Command: Change feature attr/chars"); /* Hack -- query until done */ while (1) { feature_type *f_ptr = &f_info[f]; byte da = (byte)f_ptr->d_attr; byte dc = (byte)f_ptr->d_char; byte ca = (byte)f_ptr->x_attr; byte cc = (byte)f_ptr->x_char; /* Label the object */ prtf(5, 7, "Terrain = %d, Name = %-40.40s", f, (f_name + f_ptr->name)); /* Label the Default values */ prtf(10, 9, "Default attr/char = %3d / %3d", da, dc); put_fstr(40, 9, "<< ? >>"); Term_putch(43, 9, da, dc); /* Label the Current values */ prtf(10, 10, "Current attr/char = %3d / %3d", ca, cc); put_fstr(40, 10, "<< ? >>"); Term_putch(43, 10, ca, cc); /* Prompt */ prtf(0, 12, "Command (n/N/a/A/c/C): "); /* Get a command */ i = inkey(); /* All done */ if (i == ESCAPE) break; /* Analyze */ if (i == 'n') f = (f + z_info->f_max + 1) % z_info->f_max; if (i == 'N') f = (f + z_info->f_max - 1) % z_info->f_max; if (i == 'a') f_info[f].x_attr = (byte)(ca + 1); if (i == 'A') f_info[f].x_attr = (byte)(ca - 1); if (i == 'c') f_info[f].x_char = (byte)(cc + 1); if (i == 'C') f_info[f].x_char = (byte)(cc - 1); } screen_load(); return (FALSE); } /* Modify field attr/chars */ static bool do_cmd_change_field(int dummy) { static int f = 0; char i; /* Hack - ignore parameter */ (void) dummy; screen_save(); Term_clear(); /* Prompt */ prtf(0, 5, "Command: Change field attr/chars"); /* Hack -- query until done */ while (1) { field_thaum *t_ptr = &t_info[f]; byte da = (byte)t_ptr->d_attr; byte dc = (byte)t_ptr->d_char; byte ca = (byte)t_ptr->f_attr; byte cc = (byte)t_ptr->f_char; /* Label the object */ prtf(5, 7, "Field = %d, Name = %-40.40s", f, t_ptr->name); /* Label the Default values */ prtf(10, 9, "Default attr/char = %3d / %3d", da, dc); put_fstr(40, 9, "<< ? >>"); Term_putch(43, 9, da, dc); /* Label the Current values */ prtf(10, 10, "Current attr/char = %3d / %3d", ca, cc); put_fstr(40, 10, "<< ? >>"); Term_putch(43, 10, ca, cc); /* Prompt */ prtf(0, 12, "Command (n/N/a/A/c/C): "); /* Get a command */ i = inkey(); /* All done */ if (i == ESCAPE) break; /* Analyze */ if (i == 'n') f = (f + z_info->t_max + 1) % z_info->t_max; if (i == 'N') f = (f + z_info->t_max - 1) % z_info->t_max; if (i == 'a') t_info[f].f_attr = (byte)(ca + 1); if (i == 'A') t_info[f].f_attr = (byte)(ca - 1); if (i == 'c') t_info[f].f_char = (byte)(cc + 1); if (i == 'C') t_info[f].f_char = (byte)(cc - 1); } screen_load(); return (FALSE); } #endif /* ALLOW_VISUALS */ static bool do_cmd_reset_visuals(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Reset */ reset_visuals(); /* Message */ msgf("Visual attr/char tables reset."); return (FALSE); } #ifdef ALLOW_VISUALS #define VISUAL_MENU_MAX 11 #else #define VISUAL_MENU MAX 3 #endif /* ALLOW_VISUALS */ /* The visuals menu */ static menu_type visuals_menu[VISUAL_MENU_MAX] = { {"Load a user pref file", NULL, do_cmd_pref_vis_load, MN_ACTIVE}, #ifdef ALLOW_VISUALS {"Dump monster attr/chars", NULL, do_cmd_dump_monster, MN_ACTIVE}, {"Dump object attr/chars", NULL, do_cmd_dump_object, MN_ACTIVE}, {"Dump feature attr/chars", NULL, do_cmd_dump_feature, MN_ACTIVE}, {"Dump field attr/chars", NULL, do_cmd_dump_field, MN_ACTIVE}, {"Change monster attr/chars", NULL, do_cmd_change_monster, MN_ACTIVE | MN_CLEAR}, {"Change object attr/chars", NULL, do_cmd_change_object, MN_ACTIVE | MN_CLEAR}, {"Change feature attr/chars", NULL, do_cmd_change_feature, MN_ACTIVE | MN_CLEAR}, {"Change field attr/chars", NULL, do_cmd_change_field, MN_ACTIVE | MN_CLEAR}, #endif /* ALLOW_VISUALS */ {"Reset Visuals", NULL, do_cmd_reset_visuals, MN_ACTIVE}, MENU_END }; /* * Interact with "visuals" */ void do_cmd_visuals(void) { /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); display_menu(visuals_menu, -1, FALSE, NULL, "Interact with Visuals"); } /* * Load a user pref file */ static bool do_cmd_pref_col_load(int dummy) { char tmp[1024]; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 8, "Command: Load a user pref file\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Query */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Process the given filename */ if (0 != process_pref_file("%s", tmp)) { /* Prompt */ msgf("Could not load file!"); } /* Mega-Hack -- react to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Mega-Hack -- redraw */ Term_redraw(); screen_load(); return (FALSE); } #ifdef ALLOW_COLORS static bool do_cmd_dump_colour(int dummy) { int i; FILE *fff; char tmp[1024], buf[1024]; /* Hack - ignore parameters */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 10, "Command: Dump colors\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Get a filename */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, tmp); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) { screen_load(); return (FALSE); } /* Start dumping */ froff(fff, "\n\n"); froff(fff, "# Color redefinitions\n\n"); /* Dump colors */ for (i = 0; i < 256; i++) { int kv = angband_color_table[i][0]; int rv = angband_color_table[i][1]; int gv = angband_color_table[i][2]; int bv = angband_color_table[i][3]; cptr name = "unknown"; /* Skip non-entries */ if (!kv && !rv && !gv && !bv) continue; /* Extract the color name */ if (i < 16) name = color_names[i]; /* Dump a comment */ froff(fff, "# Color '%s'\n", name); /* Dump the monster attr/char info */ froff(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n", i, (uint)kv, (uint)rv, (uint)gv, (uint)bv); } /* All done */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Message */ msgf("Dumped color redefinitions."); screen_load(); return (FALSE); } /* * Dump the message colours to a pref file */ static bool do_cmd_dump_message(int dummy) { byte i; FILE *fff; char tmp[1024], buf[1024]; /* Hack - ignore parameter */ (void) dummy; screen_save(); /* Prompt */ prtf(0, 10, "Command: Dump message colors\n\n" "File: "); /* Default filename */ strnfmt(tmp, 1024, "user-%s.prf", ANGBAND_SYS); /* Get a filename */ if (!askfor_aux(tmp, 80)) { screen_load(); return (FALSE); } /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, tmp); /* Append to the file */ fff = my_fopen(buf, "a"); /* Failure */ if (!fff) { screen_load(); return (FALSE); } /* Start dumping */ froff(fff, "\n\n"); froff(fff, "# Message color definitions\n\n"); /* Dump message colors */ for (i = 0; i < MSG_MAX; i++) { byte color = get_msg_type_color(i); cptr name = "unknown"; /* Extract the message type name */ name = msg_names[i]; /* Dump a comment */ froff(fff, "# Message type: %s\n", name); /* Dump the message color */ froff(fff, "M:%d:%c\n\n", i, color_char[color]); } /* All done */ froff(fff, "\n\n\n\n"); /* Close */ my_fclose(fff); /* Message */ msgf("Dumped message color definitions."); screen_load(); return (FALSE); } static bool do_cmd_modify_colour(int dummy) { static byte a = 0; int i; /* Hack - ignore parameter */ (void) dummy; screen_save(); clear_region(0, 9, 21); /* Prompt */ prtf(5, 10, "Command: Modify colors"); /* Hack -- query until done */ while (1) { cptr name; byte j; /* Exhibit the normal colors */ for (j = 0; j < 16; j++) { /* Exhibit this color */ put_fstr(j * 4, 18, "%s###", color_seq[j]); /* Exhibit all colors */ put_fstr(j * 4, 20, "%3d", j); } /* Describe the color */ name = ((a < 16) ? color_names[a] : "undefined"); /* Describe the color */ prtf(5, 12, "Color = %d, Name = %s", a, name); /* Label the Current values */ prtf(5, 14, "K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x", angband_color_table[a][0], angband_color_table[a][1], angband_color_table[a][2], angband_color_table[a][3]); /* Prompt */ prtf(5, 16, "Command (n/N/k/K/r/R/g/G/b/B): "); /* Get a command */ i = inkey(); /* All done */ if (i == ESCAPE) break; /* Analyze */ if (i == 'n') a = (byte)(a + 1); if (i == 'N') a = (byte)(a - 1); if (i == 'k') angband_color_table[a][0] = (byte)(angband_color_table[a][0] + 1); if (i == 'K') angband_color_table[a][0] = (byte)(angband_color_table[a][0] - 1); if (i == 'r') angband_color_table[a][1] = (byte)(angband_color_table[a][1] + 1); if (i == 'R') angband_color_table[a][1] = (byte)(angband_color_table[a][1] - 1); if (i == 'g') angband_color_table[a][2] = (byte)(angband_color_table[a][2] + 1); if (i == 'G') angband_color_table[a][2] = (byte)(angband_color_table[a][2] - 1); if (i == 'b') angband_color_table[a][3] = (byte)(angband_color_table[a][3] + 1); if (i == 'B') angband_color_table[a][3] = (byte)(angband_color_table[a][3] - 1); /* Hack -- react to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Hack -- redraw */ Term_redraw(); } screen_load(); return (FALSE); } /* * Modify message colours */ static bool do_cmd_modify_message(int dummy) { static byte a = 0; byte color; int i; /* Hack - ignore parameter */ (void) dummy; screen_save(); clear_region(0, 9, 17); /* Prompt */ prtf(5, 10, "Command: Modify message colors"); /* Hack -- query until done */ while (1) { /* Describe the message */ prtf(5, 12, "Message = %d, Type = %s", a, msg_names[a]); /* Show current color */ color = get_msg_type_color(a); /* Paranoia */ if (color >= 16) color = 0; prtf(5, 14, "Current color: %c / %s%s", color_char[color], color_seq[color], color_names[color]); /* Prompt */ prtf(5, 16, "Command (n/N/c/C): "); /* Get a command */ i = inkey(); /* All done */ if (i == ESCAPE) break; /* Analyze */ if (i == 'n') a = (a + MSG_MAX + 1) % MSG_MAX; if (i == 'N') a = (a + MSG_MAX - 1) % MSG_MAX; if (i == 'c') message_color_define(a, (byte)(color + 1)); if (i == 'C') message_color_define(a, (byte)(color - 1)); } screen_load(); return (FALSE); } #endif /* ALLOW_COLORS */ #ifdef ALLOW_COLORS #define COLOR_MENU_MAX 6 #else #define COLOR_MENU_MAX 2 #endif /* ALLOW_COLORS */ static menu_type color_menu[COLOR_MENU_MAX] = { {"Load a user pref file", NULL, do_cmd_pref_col_load, MN_ACTIVE}, #ifdef ALLOW_COLORS {"Dump colours", NULL, do_cmd_dump_colour, MN_ACTIVE}, {"Dump message colours", NULL, do_cmd_dump_message, MN_ACTIVE}, {"Modify colours", NULL, do_cmd_modify_colour, MN_ACTIVE | MN_CLEAR}, {"Modify message colours", NULL, do_cmd_modify_message, MN_ACTIVE | MN_CLEAR}, #endif /* ALLOW_COLORS */ MENU_END }; /* * Interact with "colors" */ void do_cmd_colors(void) { /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); display_menu(color_menu, -1, FALSE, NULL, "Interact with Colours"); } /* * Take notes. */ void do_cmd_note(void) { char buf[80]; /* Default */ buf[0] = 0; if (!get_string(buf, 60, "Note: ")) return; /* Ignore empty notes */ if (!buf[0] || (buf[0] == ' ')) return; if (take_notes) { /* Add note to file */ add_note(' ', buf); } else { /* Add note to message recall */ msgf("Note: %s", buf); } } /* * Mention the current version */ void do_cmd_version(void) { /* Silly message */ msgf("You are playing " VERSION_NAME " " VERSION_STRING "."); } /* * Array of feeling strings */ static cptr do_cmd_feeling_text[11] = { "Looks like any other level.", "You feel there is something special about this level.", "You nearly faint as horrible visions of death fill your mind!", "This level looks very dangerous.", "You have a very bad feeling...", "You have a bad feeling...", "You feel nervous.", "You feel your luck is turning...", "You don't like the look of this place.", "This level looks reasonably safe.", "What a boring place..." }; /* * Note that "feeling" is set to zero unless some time has passed. * Note that this is done when the level is GENERATED, not entered. */ void do_cmd_feeling(void) { /* Verify the feeling */ if (p_ptr->state.feeling > 10) p_ptr->state.feeling = 10; if (p_ptr->place_num && !p_ptr->depth) { if (place[p_ptr->place_num].quest_num) { /* No useful feeling in a wilderness quest */ msgf("Looks like a typical quest."); } else { /* No useful feeling in town */ msgf("Looks like a typical town."); } return; } /* No useful feeling in the wilderness */ if (!p_ptr->depth) { msgf("Looks like a typical wilderness."); return; } /* Display the feeling */ if (turn - old_turn >= 1000) { msgf(do_cmd_feeling_text[p_ptr->state.feeling]); } else { msgf(do_cmd_feeling_text[0]); } } /* * Hack -- load a screen dump from a file */ void do_cmd_load_screen(void) { int i, y, x; byte a = 0; char c = ' '; bool okay = TRUE; FILE *fff; char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, "dump.txt"); /* Append to the file */ fff = my_fopen(buf, "r"); /* Oops */ if (!fff) return; /* Save the screen */ screen_save(); /* Clear the screen */ Term_clear(); /* Load the screen */ for (y = 0; okay && (y < 24); y++) { /* Get a line of data */ if (my_fgets(fff, buf, 1024)) okay = FALSE; /* Show each row */ for (x = 0; x < 79; x++) { /* Put the attr/char */ Term_draw(x, y, TERM_WHITE, buf[x]); } } /* Get the blank line */ if (my_fgets(fff, buf, 1024)) okay = FALSE; /* Dump the screen */ for (y = 0; okay && (y < 24); y++) { /* Get a line of data */ if (my_fgets(fff, buf, 1024)) okay = FALSE; /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Look up the attr */ for (i = 0; i < 16; i++) { /* Use attr matches */ if (color_char[i] == buf[x]) a = i; } /* Hack -- fake monochrome */ if (!use_color) a = TERM_WHITE; /* Put the attr/char */ Term_draw(x, y, a, c); } } /* Get the blank line */ if (my_fgets(fff, buf, 1024)) okay = FALSE; /* Close it */ my_fclose(fff); /* Message */ msgf("Screen dump loaded."); /* Restore the screen */ screen_load(); } /* * Redefinable "save_screen" action */ void (*screendump_aux) (void) = NULL; /* * Hack -- save a screen dump to a file */ void do_cmd_save_screen(void) { /* Do we use a special screendump function ? */ if (screendump_aux) { /* Dump the screen to a graphics file */ (*screendump_aux) (); } else /* Dump the screen as text */ { int y, x; byte a = 0; char c = ' '; FILE *fff; char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, "dump.txt"); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Append to the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) return; /* Save the screen */ screen_save(); /* Dump the screen */ for (y = 0; y < 24; y++) { /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ buf[x] = c; } /* Terminate */ buf[x] = '\0'; /* End the row */ froff(fff, "%s\n", buf); } /* Skip a line */ froff(fff, "\n"); /* Dump the screen */ for (y = 0; y < 24; y++) { /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ buf[x] = color_char[a & 0x0F]; } /* Terminate */ buf[x] = '\0'; /* End the row */ froff(fff, "%s\n", buf); } /* Skip a line */ froff(fff, "\n"); /* Close it */ my_fclose(fff); /* Message */ msgf("Screen dump saved."); /* Restore the screen */ screen_load(); } } /* * Print a monster string, taking into account the strange * formatting of the '$' and '#' characters, as well as * doing colour correctly. */ static void print_monster_string(FILE *fff, byte a, char c, cptr name, int num) { if (c == '$') { /* Hack - no unique coins */ froff(fff, " %s$$" CLR_WHITE " %d pile of %s\n", color_seq[a], num, name); } else { if (num) { froff(fff, " %s%c" CLR_WHITE " %d %s\n", color_seq[a], c, num, name); } else { froff(fff, " %s%c" CLR_WHITE " %s\n", color_seq[a], c, name); } } } /* * Display known uniques */ static bool do_cmd_knowledge_uniques(int dummy) { FILE *fff; char file_name[1024]; int i, n, count_dead = 0; u16b why = 2; u16b *who; /* Hack - ignore parameter */ (void) dummy; /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, u16b); /* Collect matching monsters */ for (n = 0, i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Nothing to recall */ if (!cheat_know && !r_ptr->r_sights) continue; /* Require unique monsters if needed */ if (!FLAG(r_ptr, RF_UNIQUE)) continue; /* Collect "appropriate" monsters */ who[n++] = i; } /* Nothing to recall */ if (!n) { /* No monsters to recall */ msgf("No known uniques."); /* XXX XXX Free the "who" array */ KILL(who); return (FALSE); } /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array by dungeon depth of monsters */ ang_sort(who, &why, n); /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) { /* XXX XXX Free the "who" array */ KILL(who); return (FALSE); } /* Scan the monster races */ for (i = 0; i < n; i++) { monster_race *r_ptr = &r_info[who[i]]; if (r_ptr->max_num == 0) { /* Count the dead ones */ count_dead++; /* Dead */ print_monster_string(fff, r_ptr->d_attr, r_ptr->d_char, format(CLR_L_DARK "%s is dead.", mon_race_name(r_ptr)), 0); } else { /* Alive */ print_monster_string(fff, r_ptr->d_attr, r_ptr->d_char, format(CLR_L_BLUE "%s is alive.", mon_race_name(r_ptr)), 0); } } /* Free the "who" array */ KILL(who); /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, format("Known uniques, killed %d", count_dead), 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } static const cptr plural_table[] = { "ex", "ices", "ey", "eys", "y", "ies", "human", "humans", "shaman", "shamans", "man", "men", "ouse", "ice", "ff", "ffs", "f", "ves", "mage", "magi", "eraph", "eraphim", "umak", "umakil", "saurus", "saurs", "neko", "neko", "engu", "engu", "us", "i", "s", "ses", "x", "xes", "sh", "shes", "ch", "ches", /* This last entry must match everything */ "", "s" }; /* * Pluralize a monster name * * (Assume name[] is at least 80 chars long) */ void plural_aux(char *name) { char *p; char buf[80]; char tail[80]; int len = strlen(name); int i; /* Don't overflow the buffer */ if (len > 70) return; strcpy(buf, name); /* Total hack - handle Creeping coins */ if (len >= 6 && streq(buf + len - 6, " coins")) { strnfmt(buf, 80, "piles of %s", name); strcpy(name, buf); return; } tail[0] = '\0'; /* Find the trailing part we should ignore, if any */ p = strstr(buf, " out "); if (!p) p = strstr(buf, " of "); if (!p) p = strstr(buf, " that "); if (!p) p = strstr(buf, " on "); if (!p) p = strstr(buf, " to "); if (p) { strcpy(tail, p); *p = '\0'; len = strlen(buf); } /* Find the appropriate plural */ for (i = 0;; i += 2) { if ((len >= (int)strlen(plural_table[i])) && streq(buf + len - strlen(plural_table[i]), plural_table[i])) { /* Preterminate string */ buf[len - strlen(plural_table[i])] = '\0'; /* Pluralise */ strnfmt(name, 80, "%s%s%s", buf, plural_table[i + 1], tail); return; } } /* Paranoia */ quit("Failed to find matching plural in plural_aux()"); } /* * Display current pets */ bool do_cmd_knowledge_pets(int dummy) { int i; FILE *fff; monster_type *m_ptr; int t_friends = 0; int t_levels = 0; int show_upkeep = 0; char file_name[1024]; /* Hack - ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); /* Process the monsters (backwards) */ for (i = m_max - 1; i >= 1; i--) { /* Access the monster */ m_ptr = &m_list[i]; /* Ignore "dead" monsters */ if (!m_ptr->r_idx) continue; /* Calculate "upkeep" for pets */ if (is_pet(m_ptr)) { t_friends++; t_levels += r_info[m_ptr->r_idx].level; froff(fff, "%v (%s)\n", MONSTER_FMT(m_ptr, 0x88), look_mon_desc(i)); } } if (t_friends > 1 + (p_ptr->lev / (cp_ptr->pet_upkeep_div))) { show_upkeep = t_levels; if (show_upkeep > 95) show_upkeep = 95; else if (show_upkeep < 5) show_upkeep = 5; } froff(fff, "----------------------------------------------\n"); froff(fff, " Total: %d pet%s.\n", t_friends, (t_friends == 1 ? "" : "s")); froff(fff, " Upkeep: %d%% mana.\n", show_upkeep); /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Current Pets", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* * Total kill count */ static bool do_cmd_knowledge_kill_count(int dummy) { FILE *fff; char file_name[1024]; u32b total = 0; u32b temp; int i, n; u16b why = 2; u16b *who; int kk; /* Hack - ignore parameter */ (void) dummy; /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, u16b); /* Collect matching monsters */ for (n = 0, i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Nothing to recall */ if (!cheat_know && !r_ptr->r_sights) continue; /* Collect "appropriate" monsters */ who[n++] = i; } /* Nothing to recall */ if (!n) { /* No monsters to recall */ msgf("No known monsters!"); /* XXX XXX Free the "who" array */ KILL(who); return (FALSE); } /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array by dungeon depth of monsters */ ang_sort(who, &why, n); /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) { /* XXX XXX Free the "who" array */ KILL(who); return (FALSE); } /* Monsters slain */ for (kk = 1; kk < z_info->r_max; kk++) { monster_race *r_ptr = &r_info[kk]; if (FLAG(r_ptr, RF_UNIQUE)) { bool dead = (r_ptr->max_num == 0); if (dead) { total++; } } else { s16b this = r_ptr->r_pkills; if (this > 0) { total += this; } } } if (total < 1) froff(fff, "You have defeated no enemies yet.\n\n"); else if (total == 1) froff(fff, "You have defeated one enemy.\n\n"); else froff(fff, "You have defeated %lu enemies.\n\n", total); /* Save total kills for later */ temp = total; /* Zero out total so we can calculate kills of known monsters */ total = 0; /* Scan the monster races */ for (i = 0; i < n; i++) { monster_race *r_ptr = &r_info[who[i]]; if (FLAG(r_ptr, RF_UNIQUE)) { bool dead = (r_ptr->max_num == 0); if (dead) { print_monster_string(fff, r_ptr->d_attr, r_ptr->d_char, mon_race_name(r_ptr), 0); total++; } } else { s16b this = r_ptr->r_pkills; if (this > 0) { if (this < 2) { print_monster_string(fff, r_ptr->d_attr, r_ptr->d_char, mon_race_name(r_ptr), 1); } else { char ToPlural[80]; strcpy(ToPlural, mon_race_name(r_ptr)); plural_aux(ToPlural); print_monster_string(fff, r_ptr->d_attr, r_ptr->d_char, ToPlural, this); } total += this; } } } froff(fff, "----------------------------------------------\n"); froff(fff, " Total: %lu creature%s killed.\n", total, (total == 1 ? "" : "s")); /* Subtract off monsters you know you have killed */ temp -= total; /* Have we killed any monsters we did not see? */ if (temp) { froff(fff, "\n"); froff(fff, " Unseen: %lu creature%s killed.\n", temp, (temp == 1 ? "" : "s")); } /* Free the "who" array */ KILL(who); /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Kill Count", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* * Display known objects */ static bool do_cmd_knowledge_objects(int dummy) { int k; FILE *fff; char file_name[1024]; byte a; char c; cptr attr; /* Hack - ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); /* Scan the object kinds */ for (k = 1; k < z_info->k_max; k++) { object_kind *k_ptr = &k_info[k]; /* Hack -- skip artifacts */ if (FLAG(k_ptr, TR_INSTA_ART)) continue; /* List known flavored objects */ if (k_ptr->flavor && k_ptr->aware) { object_type *o_ptr; /* Create fake object */ o_ptr = object_prep(k); attr = color_seq[tval_to_attr[o_ptr->tval % 128]]; a = object_attr(o_ptr); c = object_char(o_ptr); /* Only add equippys if in ascii mode */ if (!(a & 0x80) && !(c & 0x80)) { if (c == '$') { /* Print a message ('$' needs to be escaped) */ froff(fff, " %s$$%s %v\n", color_seq[a], attr, OBJECT_STORE_FMT(o_ptr, FALSE, 0)); } else { /* Print a message */ froff(fff, " %s%c%s %v\n", color_seq[a], c, attr, OBJECT_STORE_FMT(o_ptr, FALSE, 0)); } } else { /* Print a message */ froff(fff, " %s %v\n", attr, OBJECT_STORE_FMT(o_ptr, FALSE, 0)); } } } /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Known Objects", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* * List virtues & status */ static bool do_cmd_knowledge_virtues(int dummy) { FILE *fff; char file_name[1024]; /* Hack -ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); dump_virtues(fff); /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Virtues", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* * Print notes file */ static bool do_cmd_knowledge_notes(int dummy) { char fname[1024]; /* Hack - ignore parameter */ (void) dummy; strncpy(fname, notes_file(), 1024); (void)show_file(fname, "Notes", 0, 0); return (FALSE); } /* * Dump info about a town to the given file */ void dump_town_info(FILE *fff, int town, bool ignore) { int j; cptr build_name; char c; byte a; bool visited = FALSE; place_type *pl_ptr = &place[town]; /* Is it a town? */ if (!pl_ptr->quest_num) { /* Hack-- determine the town has been visited */ visited = FALSE; for (j = 0; j < pl_ptr->numstores; j++) { /* Stores are not given coordinates until you visit a town */ if ((pl_ptr->store[j].x != 0) && (pl_ptr->store[j].y != 0)) { visited = TRUE; break; } } /* Build a buffer with the information (If visited, and if it is a town) */ if (visited) { /* write stairs information to file */ if (pl_ptr->dungeon) { froff(fff, "%s -- Stairs\n", pl_ptr->name); } else { froff(fff, "%s\n", pl_ptr->name); } /* Built stores information */ for (j = 0; j < pl_ptr->numstores; j++) { build_name = building_name(pl_ptr->store[j].type); /* Make a string, but only if this is a real building */ if (!streq(build_name, "Nothing")) { /* Get attr/char */ building_char(pl_ptr->store[j].type, &a, &c); /* Only draw symbols in ascii mode */ if (!(a & 0x80) && !(c & 0x80)) { /* Append information about store */ froff(fff, " %s%c" CLR_WHITE " %s\n", color_seq[a], c, build_name); } else { /* Append information about store */ froff(fff, " %s\n", build_name); } } } /* Seperator */ froff(fff, "\n"); } /* Never been near the place */ else { /* Give an empty message or no message */ if (!ignore) froff(fff, "\nThis town has not been visited yet.\n"); } } } /* * Dump info about a place if is has a dungeon to the given file */ static void dump_dungeon_info(FILE *fff, int town) { int i; bool visited = FALSE; cptr place_name, place_dir; int depth; int x, y, count = 0; place_type *p2_ptr, *pl_ptr = &place[town]; dun_type *d_ptr = pl_ptr->dungeon; wild_done_type *w_ptr; /* A place without a dungeon */ if (!d_ptr) return; /* Get a shorthand */ depth = d_ptr->recall_depth; /* Is it a town? */ if (pl_ptr->numstores) { /* Hack-- determine the town has been visited */ visited = FALSE; for (i = 0; i < pl_ptr->numstores; i++) { /* Stores are not given coordinates until you visit a town */ if ((pl_ptr->store[i].x != 0) && (pl_ptr->store[i].y != 0)) { visited = TRUE; break; } } /* Build a buffer with the information (If visited, and if it is a town) */ if (visited) { /* Give the dungeon name and location*/ froff(fff, "%s dungeon under %s", dungeon_type_name(d_ptr->habitat), pl_ptr->name); } else { /* Don't show this */ return; } } /* So it is a dungeon */ else { /* Fetch closest known town and direction */ place_name = describe_quest_location(&place_dir, pl_ptr->x, pl_ptr->y, TRUE); /* Check a piece of the map */ for (x = 0; x < 3; x++) { for (y = 0; y < 5; y++) { /* Pick up a spot on the map */ w_ptr = &wild[pl_ptr->y + y][pl_ptr->x + x].done; /* Pick up the place associated with this spot */ p2_ptr = (w_ptr->place) ? &place[w_ptr->place] : NULL; /* Does this spot contain a place? */ if (!p2_ptr) continue; /* Has this spot been seen? */ if (!(w_ptr->info & WILD_INFO_SEEN)) continue; /* Is this place the same as the one that we started with? */ if (p2_ptr == pl_ptr) count++; } } /* Skip if the dungeon is unknown */ if (!count) return; /* Give the dungeon name and location*/ froff(fff, "%s dungeon %s of %s", dungeon_type_name(d_ptr->habitat), place_dir, place_name); /* Did the player go into the dungeon? */ if (!depth) { /* It is still guarded by monsters */ froff(fff, ", guarded"); } } /* If the dungeon was attempted, show the depth */ if (depth) { /* Show the depth reached */ if (depth_in_feet) { if (depth == d_ptr->min_level) froff(fff, ", %d feet", depth * 50); else froff(fff, ", %d - %d feet", d_ptr->min_level * 50, depth * 50); } else { if (depth == d_ptr->min_level) froff(fff, ", level %d", depth); else froff(fff, ", level %d - %d", d_ptr->min_level, depth); } /* All the way down? */ if (depth == d_ptr->max_level) { froff(fff, " (bottom)"); } } /* Is the player in this dungeon? */ if (p_ptr->place_num == town && p_ptr->depth) { froff(fff, ", current.\n\n\n"); } else { froff(fff, ".\n\n"); } } /* * Display information about wilderness areas */ static bool do_cmd_knowledge_wild(int dummy) { int k; FILE *fff; char file_name[1024]; /* Hack - ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); /* Cycle through the places */ for (k = 1; k < place_count; k++) { dump_town_info(fff, k, TRUE); } /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Towns", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* * Display information about wilderness areas */ static bool do_cmd_knowledge_dungeon(int dummy) { int k; FILE *fff; char file_name[1024]; /* Hack - ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); /* Cycle through the places */ for (k = 1; k < place_count; k++) { dump_dungeon_info(fff, k); } /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Dungeons", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* Some gaps for options that should not show up always */ static menu_type knowledge_menu[15] = { {"Display known uniques", NULL, do_cmd_knowledge_uniques, MN_ACTIVE | MN_CLEAR}, {"Display known objects", NULL, do_cmd_knowledge_objects, MN_ACTIVE | MN_CLEAR}, {"Display kill count", NULL, do_cmd_knowledge_kill_count, MN_ACTIVE | MN_CLEAR}, {"Display mutations", NULL, do_cmd_knowledge_mutations, MN_ACTIVE | MN_CLEAR}, {"Display current pets", NULL, do_cmd_knowledge_pets, MN_ACTIVE | MN_CLEAR}, {"Display current quests", NULL, do_cmd_knowledge_quests, MN_ACTIVE | MN_CLEAR}, MENU_END, MENU_END, MENU_END, MENU_END, {"Display virtues", NULL, do_cmd_knowledge_virtues, MN_ACTIVE | MN_CLEAR}, {"Display notes", NULL, do_cmd_knowledge_notes, MN_ACTIVE | MN_CLEAR}, {"Display towns", NULL, do_cmd_knowledge_wild, MN_ACTIVE | MN_CLEAR}, {"Display dungeons", NULL, do_cmd_knowledge_dungeon, MN_ACTIVE | MN_CLEAR}, MENU_END }; /* * Interact with "knowledge" */ void do_cmd_knowledge(void) { int nr, last_option = 6; /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* start at the first free spot */ nr = last_option; /* * Display virtues option is always left out * if (use_virtues) knowledge_menu[nr++] = knowledge_menu[10]; */ /* Copy in the display notes */ if (take_notes) knowledge_menu[nr++] = knowledge_menu[11]; /* Copy in the wilderness displays */ if (!vanilla_town) { knowledge_menu[nr++] = knowledge_menu[12]; knowledge_menu[nr++] = knowledge_menu[13]; } /* Display the menu */ display_menu(knowledge_menu, -1, FALSE, NULL, "Display current knowledge"); /* Clear these options again */ for (; nr >= last_option; nr--) { /* menu item 14 contains a MENU_END */ knowledge_menu[nr] = knowledge_menu[14]; } } /* * Check on the status of an active quest */ void do_cmd_checkquest(void) { /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Quest info */ (void) do_cmd_knowledge_quests(0); } /* * Display the time and date */ void do_cmd_time(void) { s32b len = 10L * TOWN_DAWN; s32b tick = turn % len + len / 4; int day = turn / len + 1; int hour = (24 * tick / len) % 24; int min = (1440 * tick / len) % 60; int full = hour * 100 + min; int start = 9999; int end = -9999; int num = 0; char desc[1024]; char buf[1024]; FILE *fff; strcpy(desc, "It is a strange time."); /* Message */ msgf("This is day %d. The time is %d:%02d %s.", day, (hour % 12 == 0) ? 12 : (hour % 12), min, (hour < 12) ? "AM" : "PM"); /* Find the path */ if (one_in_(10) || p_ptr->tim.image) { path_make(buf, ANGBAND_DIR_FILE, "timefun.txt"); } else { path_make(buf, ANGBAND_DIR_FILE, "timenorm.txt"); } /* Open this file */ fff = my_fopen(buf, "rt"); /* Oops */ if (!fff) return; /* Find this time */ while (!my_fgets(fff, buf, 1024)) { /* Ignore comments */ if (!buf[0] || (buf[0] == '#')) continue; /* Ignore invalid lines */ if (buf[1] != ':') continue; /* Process 'Start' */ if (buf[0] == 'S') { /* Extract the starting time */ start = atoi(buf + 2); /* Assume valid for an hour */ end = start + 59; /* Next... */ continue; } /* Process 'End' */ if (buf[0] == 'E') { /* Extract the ending time */ end = atoi(buf + 2); /* Next... */ continue; } /* Ignore incorrect range */ if ((start > full) || (full > end)) continue; /* Process 'Description' */ if (buf[0] == 'D') { num++; /* Apply the randomizer */ if (one_in_(num)) strcpy(desc, buf + 2); /* Next... */ continue; } } /* Message */ msgf(desc); /* Close the file */ my_fclose(fff); } zangband/src/cmd5.c0000755000000000000000000021037310250356274013140 0ustar rootroot/* File: cmd5.c */ /* Purpose: Spell/Prayer commands */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Allow user to choose a spell/prayer from the given book. * * If a valid spell is chosen, saves it in '*sn' and returns TRUE * If the user hits escape, returns FALSE, and set '*sn' to -1 * If there are no legal choices, returns FALSE, and sets '*sn' to -2 * * The "prompt" should be "cast", "recite", or "study" * The "known" should be TRUE for cast/pray, FALSE for study */ static int get_spell(int *sn, cptr prompt, int sval, bool known, bool realm_2) { int i; int spell; int num = 0; int ask; byte spells[PY_MAX_SPELLS]; bool flag, okay; char choice; const magic_type *s_ptr; char out_val[160]; int use_realm = p_ptr->spell.r[realm_2 ? 1 : 0].realm; cptr p = ((mp_ptr->spell_book == TV_LIFE_BOOK) ? "prayer" : "spell"); /* Get the spell, if available */ if (repeat_pull(sn)) { /* Verify the spell */ if (spell_okay(*sn, known, use_realm - 1)) { /* Success */ return (TRUE); } else { /* Invalid repeat - reset it */ repeat_clear(); } } /* Extract spells */ for (spell = 0; spell < 32; spell++) { /* Check for this spell */ if ((fake_spell_flags[sval] & (1L << spell))) { /* Collect this spell */ spells[num++] = spell; } } /* Assume no usable spells */ okay = FALSE; /* Assume no spells available */ (*sn) = -2; /* Check for "okay" spells */ for (i = 0; i < num; i++) { /* Look for "okay" spells */ if (spell_okay(spells[i], known, use_realm - 1)) okay = TRUE; } /* No "okay" spells */ if (!okay) return (FALSE); /* Assume cancelled */ *sn = (-1); /* Nothing chosen yet */ flag = FALSE; /* Save the screen */ screen_save(); /* Display a list of spells */ print_spells(spells, num, 20, 1, use_realm - 1); /* Show choices */ /* Update */ p_ptr->window |= (PW_SPELL); /* Window stuff */ window_stuff(); /* Build a prompt (accept all spells) */ (void)strnfmt(out_val, 78, "(%^ss, ESC=exit) %^s which %s? ", p, prompt, p); /* Get a spell from the user */ while (get_com(out_val, &choice)) { /* Note verify */ ask = (isupper(choice)); /* Lowercase */ if (ask) choice = tolower(choice); /* Extract request */ i = (islower(choice) ? A2I(choice) : -1); /* Totally Illegal */ if ((i < 0) || (i >= num)) { bell("Illegal spell choice!"); continue; } /* Save the spell index */ spell = spells[i]; /* Require "okay" spells */ if (!spell_okay(spell, known, use_realm - 1)) { bell("Illegal spell choice!"); msgf("You may not %s that %s.", prompt, p); continue; } /* Verify it */ if (ask) { /* Access the spell */ s_ptr = &mp_ptr->info[use_realm - 1][spell % 32]; /* Belay that order */ if (!get_check("%^s %s (%d mana, %d%% fail)? ", prompt, spell_names[use_realm - 1][spell % 32], spell_mana(spell, use_realm - 1), spell_chance(spell, use_realm - 1))) continue; } /* Stop the loop */ flag = TRUE; break; } /* Restore the screen */ screen_load(); /* Show choices */ /* Update */ p_ptr->window |= (PW_SPELL); /* Window stuff */ window_stuff(); /* Abort if needed */ if (!flag) return (FALSE); /* Save the choice */ (*sn) = spell; repeat_push(*sn); /* Success */ return (TRUE); } /* * Peruse the spells/prayers in a given book. Note that we may * bypass do_cmd_browse by calling this function directly (as we * do from identify_fully_aux() which has the effect of allowing * any book to be browsed regardless of the player's realm choice. * * Note that *all* spells in the book are listed */ void do_cmd_browse_aux(const object_type *o_ptr) { int sval; int spell; int num = 0; byte spells[PY_MAX_SPELLS]; /* Access the item's sval */ sval = o_ptr->sval; /* Track the object kind */ object_kind_track(o_ptr->k_idx); /* Hack -- Handle stuff */ handle_stuff(); /* Extract spells */ for (spell = 0; spell < 32; spell++) { /* Check for this spell */ if ((fake_spell_flags[sval] & (1L << spell))) { /* Collect this spell */ spells[num++] = spell; } } /* Save the screen */ screen_save(); /* Display the spells */ print_spells(spells, num, 20, 1, (o_ptr->tval - TV_BOOKS_MIN)); /* Clear the top line */ clear_msg(); /* Prompt user */ pause_line(0); /* Restore the screen */ screen_load(); } /* * Peruse the spells/prayers in a book * * Note that browsing is allowed while confused or blind, * and in the dark, primarily to allow browsing in stores. */ void do_cmd_browse(void) { object_type *o_ptr; cptr q, s; /* Warriors are illiterate */ if (!(p_ptr->spell.r[0].realm || p_ptr->spell.r[1].realm)) { msgf("You cannot read books!"); return; } /* Restrict choices to books */ item_tester_hook = item_tester_hook_is_book; /* Get an item */ q = "Browse which book? "; s = "You have no books that you can read."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Print out the spells */ do_cmd_browse_aux(o_ptr); } /* * Study a book to gain a new spell/prayer */ void do_cmd_study(void) { int i, sval; int increment = 0; /* Spells of r[1].realm will have an increment of +32 */ int spell = -1; cptr p = ((mp_ptr->spell_book == TV_SORCERY_BOOK) ? "spell" : "prayer"); object_type *o_ptr; cptr q, s; if (!p_ptr->spell.r[0].realm) { msgf("You cannot read books!"); return; } if (p_ptr->tim.blind || no_lite()) { msgf("You cannot see!"); return; } if (p_ptr->tim.confused) { msgf("You are too confused!"); return; } if (!(p_ptr->new_spells)) { msgf("You cannot learn any new %ss!", p); return; } msgf("You can learn %d new %s%s.", p_ptr->new_spells, p, (p_ptr->new_spells == 1 ? "" : "s")); message_flush(); /* Restrict choices to "useful" books */ item_tester_tval = mp_ptr->spell_book; /* Get an item */ q = "Study which book? "; s = "You have no books that you can read."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Access the item's sval */ sval = o_ptr->sval; if (o_ptr->tval == REALM2_BOOK) increment = 32; /* Track the object kind */ object_kind_track(o_ptr->k_idx); /* Hack -- Handle stuff */ handle_stuff(); /* Mage -- Learn a selected spell */ if (mp_ptr->spell_book != TV_LIFE_BOOK) { /* Ask for a spell, allow cancel */ if (!get_spell(&spell, "study", sval, FALSE, (bool)(increment ? TRUE : FALSE)) && (spell == -1)) return; } /* Priest -- Learn a random prayer */ else { int k = 0; int gift = -1; /* Extract spells */ for (spell = 0; spell < 32; spell++) { /* Check spells in the book */ if ((fake_spell_flags[sval] & (1L << spell))) { /* Skip non "okay" prayers */ if (!spell_okay(spell, FALSE, p_ptr->spell.r[increment / 32].realm - 1)) continue; /* Hack -- Prepare the randomizer */ k++; /* Hack -- Apply the randomizer */ if (one_in_(k)) gift = spell; } } /* Accept gift */ spell = gift; } /* Nothing to study */ if (spell < 0) { /* Message */ msgf("You cannot learn any %ss in that book.", p); /* Abort */ return; } /* Take a turn */ p_ptr->state.energy_use = 100; if (increment) spell += increment; /* Learn the spell */ p_ptr->spell.r[spell / 32].learned |= (1L << (spell % 32)); /* Find the next open entry in "spell.order[]" */ for (i = 0; i < PY_MAX_SPELLS; i++) { /* Stop at the first empty space */ if (p_ptr->spell.order[i] == 99) break; } /* Add the spell to the known list */ p_ptr->spell.order[i++] = spell; /* Mention the result */ msgf(MSGT_STUDY, "You have learned the %s of %s.", p, spell_names [p_ptr->spell.r[increment / 32].realm - 1][spell % 32]); if (mp_ptr->spell_book == TV_LIFE_BOOK) chg_virtue(V_FAITH, 1); else chg_virtue(V_KNOWLEDGE, 1); /* Sound */ sound(SOUND_STUDY); /* One less spell available */ p_ptr->new_spells--; /* Message if needed */ if (p_ptr->new_spells) { /* Message */ msgf("You can learn %d more %s%s.", p_ptr->new_spells, p, (p_ptr->new_spells != 1) ? "s" : ""); } /* Redraw Study Status */ p_ptr->redraw |= (PR_STUDY); /* Talking to yourself... */ make_noise(1); } #define MAX_BIZARRE 6 static const int bizarre_num[MAX_BIZARRE] = { SUMMON_BIZARRE1, SUMMON_BIZARRE2, SUMMON_BIZARRE3, SUMMON_BIZARRE4, SUMMON_BIZARRE5, SUMMON_BIZARRE6, }; static void wild_magic(int spell) { int px = p_ptr->px; int py = p_ptr->py; switch (randint0(spell) + randint0(9)) { case 1: case 2: case 3: { teleport_player(10); break; } case 4: case 5: case 6: { teleport_player(100); break; } case 7: case 8: { teleport_player(200); break; } case 9: case 10: case 11: { (void)unlite_area(10, 3); break; } case 12: case 13: case 14: { (void)lite_area(damroll(2, 3), 2); break; } case 15: { (void)destroy_doors_touch(); break; } case 16: case 17: { wall_breaker(); break; } case 18: { (void)sleep_monsters_touch(); break; } case 19: case 20: { (void)trap_creation(); break; } case 21: case 22: { (void)door_creation(); break; } case 23: case 24: case 25: { aggravate_monsters(0); break; } case 26: { (void)earthquake(px, py, 5); break; } case 27: case 28: { (void)gain_mutation(0); break; } case 29: case 30: { (void)apply_disenchant(); break; } case 31: { (void)lose_all_info(); break; } case 32: { (void)fire_ball(GF_CHAOS, 0, spell + 5, 1 + (spell / 10)); break; } case 33: { (void)wall_stone(); break; } case 34: case 35: { int i; int type = bizarre_num[randint0(6)]; for (i = 0; i < 8; i++) { (void)summon_specific(0, px, py, (p_ptr->depth * 3) / 2, type, TRUE, FALSE, FALSE); } break; } case 36: case 37: { (void)activate_hi_summon(); break; } case 38: { (void)summon_cyber(-1, px, py); break; } default: { int count = 0; (void)activate_ty_curse(FALSE, &count); break; } } return; } static bool cast_life_spell(int spell) { int px = p_ptr->px; int py = p_ptr->py; int dir; int plev = p_ptr->lev; switch (spell) { case 0: /* Detect Evil */ (void)detect_monsters_evil(); break; case 1: /* Cure Light Wounds */ (void)hp_player(damroll(2, 10)); (void)inc_cut(-10); break; case 2: /* Bless */ (void)inc_blessed(rand_range(12, 24)); break; case 3: /* Remove Fear */ (void)clear_afraid(); break; case 4: /* Call Light */ (void)lite_area(damroll(2, (plev / 2)), (plev / 10) + 1); break; case 5: /* Detect Traps + Secret Doors */ (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); break; case 6: /* Cure Medium Wounds */ (void)hp_player(damroll(4, 10)); (void)inc_cut(-40); break; case 7: /* Satisfy Hunger */ (void)set_food(PY_FOOD_MAX - 1); break; case 8: /* Remove Curse */ (void)remove_curse(); break; case 9: /* Cure Poison */ (void)clear_poisoned(); break; case 10: /* Cure Critical Wounds */ (void)hp_player(damroll(8, 10)); (void)clear_stun(); (void)clear_cut(); break; case 11: /* Sense Unseen */ (void)inc_tim_invis(rand_range(24, 48)); break; case 12: /* Orb or Draining */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_HOLY_FIRE, dir, (damroll(3, 6) + plev + (plev / ((p_ptr->rp.pclass == CLASS_PRIEST || p_ptr->rp.pclass == CLASS_HIGH_MAGE) ? 2 : 4))), ((plev < 30) ? 2 : 3)); break; case 13: /* Protection from Evil */ (void)inc_protevil(randint1(25) + 3 * p_ptr->lev); break; case 14: /* Healing */ (void)hp_player(300); (void)clear_stun(); (void)clear_cut(); break; case 15: /* Glyph of Warding */ (void)warding_glyph(); break; case 16: /* Exorcism */ (void)dispel_undead(plev); (void)dispel_demons(plev); (void)turn_evil(plev); break; case 17: /* Dispel Curse */ (void)remove_all_curse(); break; case 18: /* Dispel Undead + Demons */ (void)dispel_undead(plev * 3); (void)dispel_demons(plev * 3); break; case 19: /* 'Day of the Dove' */ (void)charm_monsters(plev * 2); break; case 20: /* Dispel Evil */ (void)dispel_evil(plev * 4); break; case 21: /* Banishment */ if (banish_evil(100)) { msgf("The power of your god banishes evil!"); } break; case 22: /* Holy Word */ (void)dispel_evil(plev * 4); (void)hp_player(1000); (void)clear_afraid(); (void)clear_poisoned(); (void)clear_stun(); (void)clear_cut(); break; case 23: /* Warding True */ (void)warding_glyph(); (void)glyph_creation(); break; case 24: /* Heroism */ (void)inc_hero(rand_range(25, 50)); (void)hp_player(10); (void)clear_afraid(); break; case 25: /* Prayer */ (void)inc_blessed(rand_range(50, 100)); break; case 26: return bless_weapon(); case 27: /* Restoration */ (void)do_res_stat(A_STR); (void)do_res_stat(A_INT); (void)do_res_stat(A_WIS); (void)do_res_stat(A_DEX); (void)do_res_stat(A_CON); (void)do_res_stat(A_CHR); (void)restore_level(); break; case 28: /* Healing True */ (void)hp_player(2000); (void)clear_stun(); (void)clear_cut(); break; case 29: /* Holy Vision */ return identify_fully(); case 30: /* Divine Intervention */ (void)project(0, 1, px, py, 777, GF_HOLY_FIRE, PROJECT_KILL); (void)dispel_monsters(plev * 4); (void)slow_monsters(); (void)stun_monsters(plev * 4); (void)confuse_monsters(plev * 4); (void)turn_monsters(plev * 4); (void)stasis_monsters(plev * 4); (void)summon_specific(-1, px, py, plev, SUMMON_ANGEL, TRUE, TRUE, TRUE); (void)inc_shero(rand_range(25, 50)); (void)hp_player(300); /* Haste */ (void)inc_fast(randint1(20 + plev) + plev); (void)clear_afraid(); break; case 31: /* Holy Invulnerability */ (void)inc_invuln(rand_range(7, 14)); break; default: msgf("You cast an unknown Life spell: %d.", spell); message_flush(); } make_noise(2); return TRUE; } static bool cast_sorcery_spell(int spell) { int dir; int plev = p_ptr->lev; switch (spell) { case 0: /* Detect Monsters */ (void)detect_monsters_normal(); break; case 1: /* Phase Door */ teleport_player(10); break; case 2: /* Detect Doors and Traps */ (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); break; case 3: /* Light Area */ (void)lite_area(damroll(2, (plev / 2)), (plev / 10) + 1); break; case 4: /* Confuse Monster */ if (!get_aim_dir(&dir)) return FALSE; (void)confuse_monster(dir, (plev * 3) / 2); break; case 5: /* Teleport Self */ teleport_player(plev * 5); break; case 6: /* Sleep Monster */ if (!get_aim_dir(&dir)) return FALSE; (void)sleep_monster(dir); break; case 7: /* Recharging */ return recharge(plev * 4); case 8: /* Magic Mapping */ map_area(); break; case 9: /* Identify */ return ident_spell(); case 10: /* Slow Monster */ if (!get_aim_dir(&dir)) return FALSE; (void)slow_monster(dir); break; case 11: /* Mass Sleep */ (void)sleep_monsters(); break; case 12: /* Teleport Away */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_beam(GF_AWAY_ALL, dir, plev); break; case 13: /* Haste Self */ (void)inc_fast(randint1(20 + plev) + plev); break; case 14: /* Detection True */ (void)detect_all(); break; case 15: /* Identify True */ return identify_fully(); case 16: /* Detect Objects and Treasure */ (void)detect_objects_normal(); (void)detect_treasure(); (void)detect_objects_gold(); break; case 17: /* Detect Enchantment */ (void)detect_objects_magic(); break; case 18: /* Charm Monster */ if (!get_aim_dir(&dir)) return FALSE; (void)charm_monster(dir, plev); break; case 19: /* Dimension Door */ msgf("You open a dimensional gate. Choose a destination."); return dimension_door(); case 20: /* Sense Minds */ (void)inc_tim_esp(rand_range(25, 55)); break; case 21: /* Self knowledge */ (void)self_knowledge(); break; case 22: /* Teleport Level */ (void)teleport_player_level(); break; case 23: /* Word of Recall */ word_of_recall(); break; case 24: /* Stasis */ if (!get_aim_dir(&dir)) return FALSE; (void)stasis_monster(dir); break; case 25: /* Telekinesis */ if (!get_aim_dir(&dir)) return FALSE; fetch(dir, plev * 15, FALSE); break; case 26: /* Explosive Rune */ (void)explosive_rune(); break; case 27: /* Clairvoyance */ wiz_lite(); if (!(FLAG(p_ptr, TR_TELEPATHY))) { (void)inc_tim_esp(rand_range(25, 55)); } break; case 28: /* Enchant Weapon */ return enchant_spell(randint1(4), randint1(4), 0); case 29: /* Enchant Armour */ return enchant_spell(0, 0, rand_range(2, 5)); case 30: /* Alchemy */ return alchemy(); case 31: /* Globe of Invulnerability */ (void)inc_invuln(rand_range(8, 16)); break; default: msgf("You cast an unknown Sorcery spell: %d.", spell); message_flush(); } make_noise(2); return TRUE; } static bool cast_nature_spell(int spell) { int px = p_ptr->px; int py = p_ptr->py; int dir; int beam; int plev = p_ptr->lev; bool no_trump = FALSE; if (p_ptr->rp.pclass == CLASS_MAGE) beam = plev; else if (p_ptr->rp.pclass == CLASS_HIGH_MAGE) beam = plev + 10; else beam = plev / 2; switch (spell) { case 0: /* Detect Creatures */ (void)detect_monsters_normal(); break; case 1: /* First Aid */ (void)hp_player(damroll(2, 8)); (void)inc_cut(-15); break; case 2: /* Detect Doors + Traps */ (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); break; case 3: /* Produce Food */ (void)set_food(PY_FOOD_MAX - 1); break; case 4: /* Daylight */ (void)lite_area(damroll(2, (plev / 2)), (plev / 10) + 1); if ((FLAG(p_ptr, TR_HURT_LITE)) && !(FLAG(p_ptr, TR_RES_LITE)) && !(FLAG(p_ptr, TR_IM_LITE))) { msgf("The daylight scorches your flesh!"); take_hit(damroll(2, 2), "daylight"); } break; case 5: /* Animal Taming */ if (!get_aim_dir(&dir)) return FALSE; (void)charm_animal(dir, plev); break; case 6: /* Resist Environment */ (void)inc_oppose_cold(rand_range(20, 40)); (void)inc_oppose_fire(rand_range(20, 40)); (void)inc_oppose_elec(rand_range(20, 40)); break; case 7: /* Cure Wounds + Poison */ (void)clear_cut(); (void)clear_poisoned(); break; case 8: /* Stone to Mud */ if (!get_aim_dir(&dir)) return FALSE; (void)wall_to_mud(dir); break; case 9: /* Lightning Bolt */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam - 10, GF_ELEC, dir, damroll(3 + ((plev - 5) / 4), 8)); break; case 10: /* Nature Awareness -- downgraded */ map_area(); (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); (void)detect_monsters_normal(); break; case 11: /* Frost Bolt */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam - 10, GF_COLD, dir, damroll(5 + ((plev - 5) / 4), 8)); break; case 12: /* Ray of Sunlight */ if (!get_aim_dir(&dir)) return FALSE; msgf("A line of sunlight appears."); (void)lite_line(dir, damroll(6, 8)); break; case 13: /* Entangle */ (void)slow_monsters(); break; case 14: /* Summon Animals */ if (! (summon_specific (-1, px, py, plev, SUMMON_ANIMAL_RANGER, TRUE, TRUE, TRUE))) no_trump = TRUE; break; case 15: /* Herbal Healing */ (void)hp_player(1000); (void)clear_stun(); (void)clear_cut(); (void)clear_poisoned(); break; case 16: /* Door Building */ (void)door_creation(); break; case 17: /* Stair Building */ (void)stair_creation(); break; case 18: /* Stone Skin */ (void)inc_shield(rand_range(30, 50)); break; case 19: /* Resistance True */ (void)inc_oppose_acid(rand_range(20, 40)); (void)inc_oppose_elec(rand_range(20, 40)); (void)inc_oppose_fire(rand_range(20, 40)); (void)inc_oppose_cold(rand_range(20, 40)); (void)inc_oppose_pois(rand_range(20, 40)); break; case 20: /* Animal Friendship */ (void)charm_animals(plev * 2); break; case 21: /* Stone Tell */ return identify_fully(); case 22: /* Wall of Stone */ (void)wall_stone(); break; case 23: /* Protection from Corrosion */ return rustproof(); case 24: /* Earthquake */ (void)earthquake(px, py, 10); break; case 25: /* Whirlwind Attack */ whirlwind_attack(); break; case 26: /* Blizzard */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_COLD, dir, 70 + plev, (plev / 12) + 1); break; case 27: /* Lightning Storm */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_ELEC, dir, 90 + plev, (plev / 12) + 1); break; case 28: /* Whirlpool */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_WATER, dir, 100 + plev, (plev / 12) + 1); break; case 29: /* Call Sunlight */ (void)fire_ball(GF_LITE, 0, 150, 8); wiz_lite(); if ((FLAG(p_ptr, TR_HURT_LITE)) && !(FLAG(p_ptr, TR_RES_LITE)) && !(FLAG(p_ptr, TR_IM_LITE))) { msgf("The sunlight scorches your flesh!"); take_hit(50, "sunlight"); } break; case 30: /* Elemental Brand */ brand_weapon(0); break; case 31: /* Nature's Wrath */ (void)dispel_monsters(plev * 4); (void)earthquake(px, py, 20 + (plev / 2)); (void)project(0, 1 + plev / 12, px, py, 100 + plev, GF_DISINTEGRATE, PROJECT_KILL | PROJECT_ITEM); break; default: msgf("You cast an unknown Nature spell: %d.", spell); message_flush(); } if (no_trump) msgf("No animals arrive."); make_noise(2); return TRUE; } static bool cast_chaos_spell(int spell) { int px = p_ptr->px; int py = p_ptr->py; int dir, i, beam; int plev = p_ptr->lev; if (p_ptr->rp.pclass == CLASS_MAGE) beam = plev; else if (p_ptr->rp.pclass == CLASS_HIGH_MAGE) beam = plev + 10; else beam = plev / 2; switch (spell) { case 0: /* Magic Missile */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam - 10, GF_MISSILE, dir, damroll(3 + ((plev - 1) / 5), 4)); break; case 1: /* Trap / Door destruction */ (void)destroy_doors_touch(); break; case 2: /* Flash of Light == Light Area */ (void)lite_area(damroll(2, (plev / 2)), (plev / 10) + 1); break; case 3: /* Touch of Confusion */ if (!p_ptr->state.confusing) { msgf("Your hands start glowing."); p_ptr->state.confusing = TRUE; p_ptr->redraw |= (PR_STATUS); } break; case 4: /* Manaburst */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_MISSILE, dir, (damroll(3, 5) + plev + (plev / (((p_ptr->rp.pclass == CLASS_MAGE) || (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) ? 2 : 4))), ((plev < 30) ? 2 : 3)); /* Shouldn't actually use GF_MANA, as it will destroy all * items on the floor */ break; case 5: /* Fire Bolt */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam, GF_FIRE, dir, damroll(8 + ((plev - 5) / 4), 8)); break; case 6: /* Fist of Force ("Fist of Fun") */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_DISINTEGRATE, dir, damroll(8 + ((plev - 5) / 4), 8), 0); break; case 7: /* Teleport Self */ teleport_player(plev * 5); break; case 8: /* Wonder */ { /* * This spell should become more useful (more * controlled) as the player gains experience levels. * Thus, add 1/5 of the player's level to the die roll. * This eliminates the worst effects later on, while * keeping the results quite random. It also allows * some potent effects only at high level. */ int die = randint1(100) + plev / 5; if (die < 26) chg_virtue(V_CHANCE, 1); if (!get_aim_dir(&dir)) return FALSE; if (die > 100) msgf("You feel a surge of power!"); if (die < 8) (void)clone_monster(dir); else if (die < 14) (void)speed_monster(dir); else if (die < 26) (void)heal_monster(dir); else if (die < 31) (void)poly_monster(dir); else if (die < 36) (void)fire_bolt_or_beam(beam - 10, GF_MISSILE, dir, damroll(3 + ((plev - 1) / 5), 4)); else if (die < 41) (void)confuse_monster(dir, plev); else if (die < 46) (void)fire_ball(GF_POIS, dir, 20 + (plev / 2), 3); else if (die < 51) (void)lite_line(dir, damroll(6, 8)); else if (die < 56) (void)fire_bolt_or_beam(beam - 10, GF_ELEC, dir, damroll(3 + ((plev - 5) / 4), 8)); else if (die < 61) (void)fire_bolt_or_beam(beam - 10, GF_COLD, dir, damroll(5 + ((plev - 5) / 4), 8)); else if (die < 66) (void)fire_bolt_or_beam(beam, GF_ACID, dir, damroll(6 + ((plev - 5) / 4), 8)); else if (die < 71) (void)fire_bolt_or_beam(beam, GF_FIRE, dir, damroll(8 + ((plev - 5) / 4), 8)); else if (die < 76) (void)drain_life(dir, 75); else if (die < 81) (void)fire_ball(GF_ELEC, dir, 30 + plev / 2, 2); else if (die < 86) (void)fire_ball(GF_ACID, dir, 40 + plev, 2); else if (die < 91) (void)fire_ball(GF_ICE, dir, 70 + plev, 3); else if (die < 96) (void)fire_ball(GF_FIRE, dir, 80 + plev, 3); else if (die < 101) (void)drain_life(dir, 100 + plev); else if (die < 104) { (void)earthquake(px, py, 12); } else if (die < 106) { (void)destroy_area(px, py, 15); } else if (die < 108) { (void)genocide(TRUE); } else if (die < 110) (void)dispel_monsters(120); else /* RARE */ { (void)dispel_monsters(150); (void)slow_monsters(); (void)sleep_monsters(); (void)hp_player(300); } break; } case 9: /* Chaos Bolt */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam, GF_CHAOS, dir, damroll(10 + ((plev - 5) / 4), 8)); break; case 10: /* Sonic Boom */ msgf("BOOM! Shake the room!"); (void)project(0, plev / 10 + 2, px, py, 45 + plev, GF_SOUND, PROJECT_KILL | PROJECT_ITEM); break; case 11: /* Doom Bolt -- always beam in 2.0.7 or later */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_beam(GF_MANA, dir, damroll(11 + ((plev - 5) / 4), 8)); break; case 12: /* Fire Ball */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_FIRE, dir, plev + 55, 2); break; case 13: /* Teleport Other */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_beam(GF_AWAY_ALL, dir, plev); break; case 14: /* Word of Destruction */ (void)destroy_area(px, py, 15); break; case 15: /* Invoke Logrus */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_CHAOS, dir, plev + 66, plev / 5); break; case 16: /* Polymorph Other */ if (!get_aim_dir(&dir)) return FALSE; (void)poly_monster(dir); break; case 17: /* Chain Lightning */ for (dir = 0; dir <= 9; dir++) (void)fire_beam(GF_ELEC, dir, damroll(5 + (plev / 10), 8)); break; case 18: /* Arcane Binding == Charging */ return recharge(90); case 19: /* Disintegration */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_DISINTEGRATE, dir, plev + 80, 3 + plev / 40); break; case 20: /* Alter Reality */ alter_reality(); break; case 21: /* Polymorph Self */ do_poly_self(); break; case 22: /* Chaos Branding */ brand_weapon(1); break; case 23: /* Summon monster, demon */ { bool pet = (one_in_(3)); bool group = !(pet && (plev < 50)); if (summon_specific ((pet ? -1 : 0), px, py, (plev * 3) / 2, SUMMON_DEMON, group, FALSE, pet)) { msgf ("The area fills with a stench of sulphur and brimstone."); if (pet) msgf("'What is thy bidding... Master?'"); else msgf ("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'"); } break; } case 24: /* Beam of Gravity */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_beam(GF_GRAVITY, dir, damroll(9 + ((plev - 5) / 4), 8)); break; case 25: /* Meteor Swarm */ { int x, y; int b = rand_range(10, 20); for (i = 0; i < b; i++) { int count = 0; while (count < 1000) { count++; x = px - 5 + randint1(10); y = py - 5 + randint1(10); /* paranoia */ if (!in_boundsp(x, y)) continue; /* keep going if not in LOS */ if (!player_has_los_grid(parea(x, y))) continue; /* if close enough - exit */ if (distance(px, py, x, y) < 6) break; } if (count >= 1000) break; (void)project(0, 2, x, y, (plev * 3) / 2, GF_METEOR, PROJECT_KILL | PROJECT_JUMP | PROJECT_ITEM); } } break; case 26: /* Flame Strike */ (void)fire_ball(GF_FIRE, 0, 150 + (2 * plev), 8); break; case 27: /* Call Chaos */ call_chaos(); break; case 28: /* Magic Rocket */ if (!get_aim_dir(&dir)) return FALSE; msgf("You launch a rocket!"); (void)fire_ball(GF_ROCKET, dir, 120 + plev, 2); break; case 29: /* Mana Storm */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_MANA, dir, 300 + (plev * 2), 4); break; case 30: /* Breathe Logrus */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_CHAOS, dir, p_ptr->chp, 2); break; case 31: /* Call the Void */ call_the_(); break; default: msgf("You cast an unknown Chaos spell: %d.", spell); message_flush(); } make_noise(2); return TRUE; } static bool cast_death_spell(int spell) { int px = p_ptr->px; int py = p_ptr->py; int dir; int beam; int plev = p_ptr->lev; int dummy = 0; int i; if (p_ptr->rp.pclass == CLASS_MAGE) beam = plev; else if (p_ptr->rp.pclass == CLASS_HIGH_MAGE) beam = plev + 10; else beam = plev / 2; switch (spell) { case 0: /* Detect Undead & Demons -> Unlife */ (void)detect_monsters_nonliving(); break; case 1: /* Malediction */ if (!get_aim_dir(&dir)) return FALSE; /* A radius-0 ball may (1) be aimed at objects etc., * and will affect them; (2) may be aimed at ANY * visible monster, unlike a 'bolt' which must travel * to the monster. */ (void)fire_ball(GF_HELL_FIRE, dir, damroll(3 + ((plev - 1) / 5), 3), 0); if (one_in_(5)) { /* Special effect first */ dummy = randint1(1000); if (dummy == 666) (void)fire_bolt(GF_DEATH_RAY, dir, plev * 50); else if (dummy < 500) (void)fire_bolt(GF_TURN_ALL, dir, plev); else if (dummy < 800) (void)fire_bolt(GF_OLD_CONF, dir, plev); else (void)fire_bolt(GF_STUN, dir, plev); } break; case 2: /* Detect Evil */ (void)detect_monsters_evil(); break; case 3: /* Stinking Cloud */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_POIS, dir, 10 + (plev / 2), 2); break; case 4: /* Black Sleep */ if (!get_aim_dir(&dir)) return FALSE; (void)sleep_monster(dir); break; case 5: /* Resist Poison */ (void)inc_oppose_pois(rand_range(20, 40)); break; case 6: /* Horrify */ if (!get_aim_dir(&dir)) return FALSE; (void)fear_monster(dir, plev); (void)stun_monster(dir, plev); break; case 7: /* Enslave the Undead */ if (!get_aim_dir(&dir)) return FALSE; (void)control_one_undead(dir, plev); break; case 8: /* Orb of Entropy */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_OLD_DRAIN, dir, (damroll(3, 6) + plev + (plev / (((p_ptr->rp.pclass == CLASS_MAGE) || (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) ? 2 : 4))), ((plev < 30) ? 2 : 3)); break; case 9: /* Nether Bolt */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam, GF_NETHER, dir, damroll(6 + ((plev - 5) / 4), 8)); break; case 10: /* Terror */ (void)turn_monsters(30 + plev); break; case 11: /* Vampiric Drain */ if (!get_aim_dir(&dir)) return FALSE; dummy = plev + randint1(plev) * MAX(1, plev / 10); /* Dmg */ if (drain_gain_life(dir, dummy)) { /* * Hack - this only happens when monster is seen to * be hit. */ chg_virtue(V_SACRIFICE, -1); chg_virtue(V_VITALITY, -1); /* Gain nutritional sustenance: 150/hp drained */ /* A Food ration gives 5000 food points (by contrast) */ /* Don't ever get more than "Full" this way */ /* But if we ARE Gorged, it won't cure us */ dummy = p_ptr->food + MIN(5000, 100 * dummy); if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy); } break; case 12: /* Poison Branding */ brand_weapon(2); break; case 13: /* Dispel Good */ (void)dispel_good(plev * 4); break; case 14: /* Genocide */ (void)genocide(TRUE); break; case 15: /* Restore Life */ (void)restore_level(); break; case 16: /* Berserk */ (void)inc_shero(rand_range(25, 50)); (void)hp_player(30); (void)clear_afraid(); break; case 17: /* Invoke Spirits */ { int die = randint1(100) + plev / 5; if (!get_aim_dir(&dir)) return FALSE; msgf("You call on the power of the dead..."); if (die < 26) chg_virtue(V_CHANCE, 1); if (die > 100) msgf("You feel a surge of eldritch force!"); if (die < 8) { msgf ("Oh no! Mouldering forms rise from the earth around you!"); (void)summon_specific(0, px, py, p_ptr->depth, SUMMON_UNDEAD, TRUE, FALSE, FALSE); chg_virtue(V_UNLIFE, 1); } else if (die < 14) { msgf("An unnamable evil brushes against your mind..."); (void)inc_afraid(rand_range(4, 8)); } else if (die < 26) { msgf ("Your head is invaded by a horde of gibbering spectral voices..."); (void)inc_confused(rand_range(4, 8)); } else if (die < 31) { (void)poly_monster(dir); } else if (die < 36) { (void)fire_bolt_or_beam(beam - 10, GF_MISSILE, dir, damroll(3 + ((plev - 1) / 5), 4)); } else if (die < 41) { (void)confuse_monster(dir, plev); } else if (die < 46) { (void)fire_ball(GF_POIS, dir, 20 + (plev / 2), 3); } else if (die < 51) { (void)lite_line(dir, damroll(6, 8)); } else if (die < 56) { (void)fire_bolt_or_beam(beam - 10, GF_ELEC, dir, damroll(3 + ((plev - 5) / 4), 8)); } else if (die < 61) { (void)fire_bolt_or_beam(beam - 10, GF_COLD, dir, damroll(5 + ((plev - 5) / 4), 8)); } else if (die < 66) { (void)fire_bolt_or_beam(beam, GF_ACID, dir, damroll(6 + ((plev - 5) / 4), 8)); } else if (die < 71) { (void)fire_bolt_or_beam(beam, GF_FIRE, dir, damroll(8 + ((plev - 5) / 4), 8)); } else if (die < 76) { (void)drain_life(dir, 75); } else if (die < 81) { (void)fire_ball(GF_ELEC, dir, 30 + plev / 2, 2); } else if (die < 86) { (void)fire_ball(GF_ACID, dir, 40 + plev, 2); } else if (die < 91) { (void)fire_ball(GF_ICE, dir, 70 + plev, 3); } else if (die < 96) { (void)fire_ball(GF_FIRE, dir, 80 + plev, 3); } else if (die < 101) { (void)drain_life(dir, 100 + plev); } else if (die < 104) { (void)earthquake(px, py, 12); } else if (die < 106) { (void)destroy_area(px, py, 15); } else if (die < 108) { (void)genocide(TRUE); } else if (die < 110) { (void)dispel_monsters(120); } else { /* RARE */ (void)dispel_monsters(150); (void)slow_monsters(); (void)sleep_monsters(); (void)hp_player(300); } if (die < 31) msgf ("Sepulchral voices chuckle. 'Soon you will join us, mortal.'"); break; } case 18: /* Dark Bolt */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam, GF_DARK, dir, damroll(4 + ((plev - 5) / 4), 8)); break; case 19: /* Battle Frenzy */ (void)inc_shero(rand_range(25, 50)); (void)hp_player(30); (void)clear_afraid(); (void)inc_fast(rand_range(plev / 2, 20 + plev)); break; case 20: /* Vampirism True */ if (!get_aim_dir(&dir)) return FALSE; chg_virtue(V_SACRIFICE, -1); chg_virtue(V_VITALITY, -1); for (dummy = 0; dummy < 3; dummy++) { (void)drain_gain_life(dir, 100); } break; case 21: /* Vampiric Branding */ brand_weapon(3); break; case 22: /* Darkness Storm */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_DARK, dir, 120, 4); break; case 23: /* Mass Genocide */ (void)mass_genocide(TRUE); break; case 24: /* Death Ray */ if (!get_aim_dir(&dir)) return FALSE; (void)death_ray(dir, plev); break; case 25: /* Raise the Dead */ { if (raise_dead(px, py, (bool)(!one_in_(3)))) { msgf ("Cold winds begin to blow around you, carrying with them the stench of decay..."); chg_virtue(V_UNLIFE, 1); } else { msgf("Nothing happens."); } break; } case 26: /* Esoteria */ if (randint1(50) > plev) return ident_spell(); else return identify_fully(); break; case 27: /* Word of Death */ (void)dispel_living(plev * 3); break; case 28: /* Evocation */ (void)dispel_monsters(plev * 4); (void)turn_monsters(plev * 4); (void)banish_monsters(plev * 4); break; case 29: /* Hellfire */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_HELL_FIRE, dir, 666, 3); take_hit(rand_range(50, 100), "the strain of casting Hellfire"); break; case 30: /* Omnicide */ p_ptr->csp -= 100; /* Display doesn't show mana cost (100) * as deleted until the spell has finished. This gives a * false impression of how high your mana is climbing. * Therefore, 'deduct' the cost temporarily before entering the * loop, then add it back at the end so that the rest of the * program can deduct it properly */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Hack -- Skip Unique Monsters */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Hack -- Skip Quest Monsters */ if (FLAG(r_ptr, RF_QUESTOR)) continue; /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Delete the monster */ delete_monster_idx(i); /* Take damage */ take_hit(randint1(4), "the strain of casting Omnicide"); /* Absorb power of dead soul - up to twice max. mana */ if (p_ptr->csp < (p_ptr->msp * 2)) p_ptr->csp++; /* Visual feedback */ move_cursor_relative(px, py); /* Redraw */ p_ptr->redraw |= (PR_HP | PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); /* Handle */ handle_stuff(); /* Fresh */ Term_fresh(); /* Delay */ Term_xtra(TERM_XTRA_DELAY, delay_factor * delay_factor * delay_factor); } /* Restore, ready to be deducted properly */ p_ptr->csp += 100; break; case 31: /* Wraithform */ (void)inc_wraith_form(rand_range(plev / 2, plev)); break; default: msgf("You cast an unknown Death spell: %d.", spell); message_flush(); } make_noise(2); return TRUE; } static bool cast_trump_spell(int spell, bool success) { int px = p_ptr->px; int py = p_ptr->py; int dir; int beam; int plev = p_ptr->lev; int dummy = 0; bool no_trump = FALSE; char tmp_val[160]; if (p_ptr->rp.pclass == CLASS_MAGE) beam = plev; else if (p_ptr->rp.pclass == CLASS_HIGH_MAGE) beam = plev + 10; else beam = plev / 2; switch (spell) { case 0: /* Phase Door */ if (success) { teleport_player(10); } break; case 1: /* Mind Blast */ if (success) { if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam - 10, GF_PSI, dir, damroll(3 + ((plev - 1) / 5), 3)); } break; case 2: /* Shuffle */ if (success) { /* A limited power 'wonder' spell */ int die = randint1(120); if ((p_ptr->rp.pclass == CLASS_ROGUE) || (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) die = (randint1(110)) + plev / 5; /* Card sharks and high mages get a level bonus */ msgf("You shuffle the deck and draw a card..."); if (die < 30) chg_virtue(V_CHANCE, 1); if (die < 7) { msgf("Oh no! It's Death!"); for (dummy = 0; dummy < randint1(3); dummy++) (void)activate_hi_summon(); } else if (die < 14) { msgf("Oh no! It's the Devil!"); (void)summon_specific(0, px, py, p_ptr->depth, SUMMON_DEMON, TRUE, FALSE, FALSE); } else if (die < 18) { int count = 0; msgf("Oh no! It's the Hanged Man."); (void)activate_ty_curse(FALSE, &count); } else if (die < 22) { msgf("It's the swords of discord."); aggravate_monsters(0); } else if (die < 26) { msgf("It's the Fool."); (void)do_dec_stat(A_INT); (void)do_dec_stat(A_WIS); } else if (die < 30) { msgf("It's the picture of a strange monster."); if (! (summon_specific (0, px, py, (p_ptr->depth * 3) / 2, rand_range(33, 38), TRUE, FALSE, FALSE))) no_trump = TRUE; } else if (die < 33) { msgf("It's the Moon."); (void)unlite_area(10, 3); } else if (die < 38) { msgf("It's the Wheel of Fortune."); wild_magic(randint0(32)); } else if (die < 40) { msgf("It's a teleport trump card."); teleport_player(10); } else if (die < 42) { msgf("It's Justice."); (void)inc_blessed(p_ptr->lev); } else if (die < 47) { msgf("It's a teleport trump card."); teleport_player(100); } else if (die < 52) { msgf("It's a teleport trump card."); teleport_player(200); } else if (die < 60) { msgf("It's the Tower."); wall_breaker(); } else if (die < 72) { msgf("It's Temperance."); (void)sleep_monsters_touch(); } else if (die < 80) { msgf("It's the Tower."); (void)earthquake(px, py, 5); } else if (die < 82) { msgf("It's the picture of a friendly monster."); if (! (summon_specific (-1, px, py, (p_ptr->depth * 3) / 2, SUMMON_BIZARRE1, FALSE, TRUE, TRUE))) no_trump = TRUE; } else if (die < 84) { msgf("It's the picture of a friendly monster."); if (! (summon_specific (-1, px, py, (p_ptr->depth * 3) / 2, SUMMON_BIZARRE2, FALSE, TRUE, TRUE))) no_trump = TRUE; } else if (die < 86) { msgf("It's the picture of a friendly monster."); if (! (summon_specific (-1, px, py, (p_ptr->depth * 3) / 2, SUMMON_BIZARRE4, FALSE, TRUE, TRUE))) no_trump = TRUE; } else if (die < 88) { msgf("It's the picture of a friendly monster."); if (! (summon_specific (-1, px, py, (p_ptr->depth * 3) / 2, SUMMON_BIZARRE5, FALSE, TRUE, TRUE))) no_trump = TRUE; } else if (die < 96) { msgf("It's the Lovers."); if (get_aim_dir(&dir)) (void)charm_monster(dir, MIN(p_ptr->lev, 20)); } else if (die < 101) { msgf("It's the Hermit."); (void)wall_stone(); } else if (die < 111) { msgf("It's the Judgement."); do_cmd_rerate(); if (p_ptr->muta1 || p_ptr->muta2 || p_ptr->muta3) { msgf("You are cured of all mutations."); p_ptr->muta1 = p_ptr->muta2 = p_ptr->muta3 = 0; p_ptr->update |= PU_BONUS; handle_stuff(); } } else if (die < 120) { msgf("It's the Sun."); wiz_lite(); } else { msgf("It's the World."); if (p_ptr->exp < PY_MAX_EXP) { s32b ee = (p_ptr->exp / 25) + 1; if (ee > 5000) ee = 5000; msgf("You feel more experienced."); gain_exp(ee); } } } break; case 3: /* Reset Recall */ if (p_ptr->depth && success) { dun_type *d_ptr = dungeon(); s16b max_depth = MAX(p_ptr->depth, d_ptr->recall_depth); s16b min_depth = d_ptr->min_level; /* Default */ strnfmt(tmp_val, 160, "%d", MAX(max_depth, min_depth)); /* Ask for a level */ if (get_string(tmp_val, 11, "Reset to which level (%d-%d): ", min_depth, max_depth)) { /* Extract request */ dummy = atoi(tmp_val); /* Paranoia */ if (dummy < min_depth) dummy = min_depth; /* Paranoia */ if (dummy > max_depth) dummy = max_depth; d_ptr->recall_depth = dummy; /* Accept request */ msgf("Recall depth set to level %d (%d').", dummy, dummy * 50); } else { return FALSE; } } break; case 4: /* Teleport Self */ if (success) { teleport_player(plev * 4); } break; case 5: /* Dimension Door */ if (success) { msgf("You open a dimensional gate. Choose a destination."); return dimension_door(); } break; case 6: /* Trump Spying */ if (success) { (void)inc_tim_esp(rand_range(25, 55)); } break; case 7: /* Teleport Away */ if (success) { if (!get_aim_dir(&dir)) return FALSE; (void)fire_beam(GF_AWAY_ALL, dir, plev); } break; case 8: /* Trump Object */ if (success) { if (!get_aim_dir(&dir)) return FALSE; fetch(dir, plev * 15, TRUE); } break; case 9: /* Trump Animal */ { bool pet = success; /* was (randint1(5) > 2) */ int type = (pet ? SUMMON_ANIMAL_RANGER : SUMMON_ANIMAL); bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of an animal..."); if (summon_specific ((pet ? -1 : 0), px, py, plev, type, group, FALSE, pet)) { if (!pet) msgf("An angry animal appears!"); } else { no_trump = TRUE; } break; } case 10: /* Phantasmal Servant */ if (success) { if (summon_specific (-1, px, py, (plev * 3) / 2, SUMMON_PHANTOM, FALSE, TRUE, TRUE)) { msgf("'Your wish, master?'"); } else { no_trump = TRUE; } } break; case 11: /* Trump Monster */ { bool pet = success; /* was (randint1(5) > 2) */ int type = (pet ? SUMMON_NO_UNIQUES : 0); bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of a monster..."); if (summon_specific ((pet ? -1 : 0), px, py, (plev * 3) / 2, type, group, FALSE, pet)) { if (!pet) msgf("An angry monster appears!"); } else { no_trump = TRUE; } break; } case 12: /* Conjure Elemental */ { bool pet = success; /* was (randint1(6) > 3) */ bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of a monster..."); if (summon_specific ((pet ? -1 : 0), px, py, (plev * 3) / 2, SUMMON_ELEMENTAL, group, FALSE, pet)) { if (!pet) msgf("An angry elemental appears!"); } else { no_trump = TRUE; } break; } case 13: /* Teleport Level */ if (success) { (void)teleport_player_level(); } break; case 14: /* Word of Recall */ if (success) { word_of_recall(); } break; case 15: /* Banish */ if (success) { (void)banish_monsters(plev * 4); } break; case 16: /* Joker Card */ { bool pet = success; /* was one_in_(2) */ bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on a joker card..."); switch (randint1(4)) { case 1: dummy = SUMMON_BIZARRE1; break; case 2: dummy = SUMMON_BIZARRE2; break; case 3: dummy = SUMMON_BIZARRE4; break; case 4: dummy = SUMMON_BIZARRE5; break; } if (summon_specific ((pet ? -1 : 0), px, py, (plev * 3) / 2, dummy, group, FALSE, pet)) { if (!pet) msgf("An angry creature appears!"); } else { no_trump = TRUE; } break; } case 17: /* Trump Spiders */ { bool pet = success; /* (randint1(5) > 2) */ bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of a spider..."); if (summon_specific ((pet ? -1 : 0), px, py, plev, SUMMON_SPIDER, group, FALSE, pet)) { if (!pet) msgf("An angry spiders appears!"); } else { no_trump = TRUE; } break; } case 18: /* Trump Reptiles */ { bool pet = success; /* was (randint1(5) > 2) */ bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of a reptile..."); if (summon_specific ((pet ? -1 : 0), px, py, (plev * 3) / 2, SUMMON_HYDRA, group, FALSE, pet)) { if (!pet) msgf("An angry reptile appears!"); } else { no_trump = TRUE; } break; } case 19: /* Trump Hounds */ { bool pet = success; /* was (randint1(5) > 2) */ if (success) msgf("You concentrate on the trump of a hound..."); if (summon_specific ((pet ? -1 : 0), px, py, plev, SUMMON_HOUND, TRUE, FALSE, pet)) { if (!pet) msgf("Angry barking surrounds you!"); } else { no_trump = TRUE; } break; } case 20: /* Trump Branding */ if (success) { brand_weapon(4); } break; case 21: /* Living Trump */ if (success) { if (one_in_(8)) /* Teleport control */ dummy = 12; else /* Random teleportation (uncontrolled) */ dummy = 77; /* Gain the mutation */ if (gain_mutation(dummy)) msgf("You have turned into a Living Trump."); } break; case 22: /* Death Dealing */ if (success) { (void)dispel_living(plev * 3); } break; case 23: /* Trump Cyberdemon */ { bool pet = success; /* was (randint1(10) > 3) */ if (success) msgf("You concentrate on the trump of a Cyberdemon..."); if (summon_specific ((pet ? -1 : 0), px, py, plev * 2, SUMMON_CYBER, FALSE, FALSE, pet)) { if (!pet) msgf("An angry Cyberdemon appears!"); } else { no_trump = TRUE; } break; } case 24: /* Trump Divination */ if (success) { (void)detect_all(); } break; case 25: /* Trump Lore */ if (success) { return identify_fully(); } break; case 26: /* Trump Undead */ { bool pet = success; /* (randint1(10) > 3) */ bool group = (pet ? FALSE : TRUE); if (success) msgf ("You concentrate on the trump of an undead creature..."); if (summon_specific ((pet ? -1 : 0), px, py, plev * 2, SUMMON_UNDEAD, group, FALSE, pet)) { if (!pet) msgf("An angry undead creature appears!"); } else { no_trump = TRUE; } break; } case 27: /* Trump Dragon */ { bool pet = success; /* was (randint1(10) > 3) */ bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of a dragon..."); if (summon_specific ((pet ? -1 : 0), px, py, plev * 2, SUMMON_DRAGON, group, FALSE, pet)) { if (!pet) msgf("An angry dragon appears!"); } else { no_trump = TRUE; } break; } case 28: /* Mass Trump */ { no_trump = TRUE; if (success) msgf("You concentrate on several trumps at once..."); for (dummy = 0; dummy < 3 + (plev / 10); dummy++) { bool pet = success; /* was (randint1(10) > 3) */ bool group = (pet ? FALSE : TRUE); int type = (pet ? SUMMON_NO_UNIQUES : 0); if (summon_specific ((pet ? -1 : 0), px, py, (plev * 3) / 2, type, group, FALSE, pet)) { if (!pet) msgf("An angry creature appears!"); no_trump = FALSE; } } break; } case 29: /* Trump Demon */ { bool pet = success; /* was (randint1(10) > 3) */ bool group = (pet ? FALSE : TRUE); if (success) msgf("You concentrate on the trump of a demon..."); if (summon_specific ((pet ? -1 : 0), px, py, plev * 2, SUMMON_DEMON, group, FALSE, pet)) { if (!pet) msgf("An angry demon appears!"); } else { no_trump = TRUE; } break; } case 30: /* Trump Ancient Dragon */ { bool pet = success; /* was (randint1(10) > 3) */ bool group = (pet ? FALSE : TRUE); int type = (pet ? SUMMON_HI_DRAGON_NO_UNIQUES : SUMMON_HI_DRAGON); if (success) msgf ("You concentrate on the trump of an ancient dragon..."); if (summon_specific ((pet ? -1 : 0), px, py, plev * 2, type, group, FALSE, pet)) { if (!pet) msgf("An angry ancient dragon appears!"); } else { no_trump = TRUE; } break; } case 31: /* Trump Greater Undead */ { bool pet = success; /* was (randint1(10) > 3) */ bool group = (pet ? FALSE : TRUE); int type = (pet ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_HI_UNDEAD); if (success) msgf ("You concentrate on the trump of a greater undead being..."); if (summon_specific ((pet ? -1 : 0), px, py, plev * 2, type, group, FALSE, pet)) { if (!pet) msgf("An angry greater undead creature appears!"); } else { no_trump = TRUE; } break; } default: msgf("You cast an unknown Trump spell: %d.", spell); message_flush(); } if (no_trump) { msgf("Nobody answers to your Trump call."); } make_noise(2); return TRUE; } static bool cast_arcane_spell(int spell) { int dir; int beam; int plev = p_ptr->lev; int dummy = 0; if (p_ptr->rp.pclass == CLASS_MAGE) beam = plev; else if (p_ptr->rp.pclass == CLASS_HIGH_MAGE) beam = plev + 10; else beam = plev / 2; switch (spell) { case 0: /* Zap */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_bolt_or_beam(beam - 10, GF_ELEC, dir, damroll(3 + ((plev - 1) / 5), 3)); break; case 1: /* Wizard Lock */ if (!get_aim_dir(&dir)) return FALSE; (void)wizard_lock(dir); break; case 2: /* Detect Invisibility */ (void)detect_monsters_invis(); break; case 3: /* Detect Monsters */ (void)detect_monsters_normal(); break; case 4: /* Blink */ teleport_player(10); break; case 5: /* Light Area */ (void)lite_area(damroll(2, (plev / 2)), (plev / 10) + 1); break; case 6: /* Trap & Door Destruction */ if (!get_aim_dir(&dir)) return FALSE; (void)destroy_door(dir); break; case 7: /* Cure Light Wounds */ (void)hp_player(damroll(2, 8)); (void)inc_cut(-10); break; case 8: /* Detect Doors & Traps */ (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); break; case 9: /* Phlogiston */ phlogiston(); break; case 10: /* Detect Treasure */ (void)detect_treasure(); (void)detect_objects_gold(); break; case 11: /* Detect Enchantment */ (void)detect_objects_magic(); break; case 12: /* Detect Object */ (void)detect_objects_normal(); break; case 13: /* Cure Poison */ (void)clear_poisoned(); break; case 14: /* Resist Cold */ (void)inc_oppose_cold(rand_range(20, 40)); break; case 15: /* Resist Fire */ (void)inc_oppose_fire(rand_range(20, 40)); break; case 16: /* Resist Lightning */ (void)inc_oppose_elec(rand_range(20, 40)); break; case 17: /* Resist Acid */ (void)inc_oppose_acid(rand_range(20, 40)); break; case 18: /* Cure Medium Wounds */ (void)hp_player(damroll(4, 8)); (void)inc_cut(-50); break; case 19: /* Teleport */ teleport_player(plev * 5); break; case 20: /* Stone to Mud */ if (!get_aim_dir(&dir)) return FALSE; (void)wall_to_mud(dir); break; case 21: /* Ray of Light */ if (!get_aim_dir(&dir)) return FALSE; msgf("A line of light appears."); (void)lite_line(dir, damroll(6, 8)); break; case 22: /* Satisfy Hunger */ (void)set_food(PY_FOOD_MAX - 1); break; case 23: /* See Invisible */ (void)inc_tim_invis(rand_range(24, 48)); break; case 24: /* Recharging */ return recharge(plev * 4); case 25: /* Teleport Level */ (void)teleport_player_level(); break; case 26: /* Identify */ return ident_spell(); case 27: /* Teleport Away */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_beam(GF_AWAY_ALL, dir, plev); break; case 28: /* Elemental Ball */ if (!get_aim_dir(&dir)) return FALSE; switch (randint1(4)) { case 1: dummy = GF_FIRE; break; case 2: dummy = GF_ELEC; break; case 3: dummy = GF_COLD; break; default: dummy = GF_ACID; break; } (void)fire_ball(dummy, dir, 75 + (plev), 2); break; case 29: /* Detection */ (void)detect_all(); break; case 30: /* Word of Recall */ word_of_recall(); break; case 31: /* Clairvoyance */ wiz_lite(); if (!(FLAG(p_ptr, TR_TELEPATHY))) { (void)inc_tim_esp(rand_range(25, 55)); } break; default: msgf("You cast an unknown Arcane spell: %d.", spell); message_flush(); } make_noise(2); return TRUE; } /* * Cast a spell */ void do_cmd_cast(void) { int sval, spell, realm; int chance, smana; int increment = 0; int use_realm; bool cast; const cptr prayer = ((mp_ptr->spell_book == TV_LIFE_BOOK) ? "prayer" : "spell"); object_type *o_ptr; const magic_type *s_ptr; cptr q, s; /* Require spell ability */ if (!p_ptr->spell.r[0].realm) { msgf("You cannot cast spells!"); return; } /* Require lite */ if (p_ptr->tim.blind || no_lite()) { msgf("You cannot see!"); return; } /* Not when confused */ if (p_ptr->tim.confused) { msgf("You are too confused!"); return; } /* Restrict choices to spell books */ item_tester_tval = mp_ptr->spell_book; /* Get an item */ q = "Use which book? "; s = "You have no spell books!"; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Access the item's sval */ sval = o_ptr->sval; if (o_ptr->tval == REALM2_BOOK) increment = 32; /* Track the object kind */ object_kind_track(o_ptr->k_idx); /* Hack -- Handle stuff */ handle_stuff(); realm = p_ptr->spell.r[increment / 32].realm; /* Ask for a spell */ if (!get_spell (&spell, ((mp_ptr->spell_book == TV_LIFE_BOOK) ? "recite" : "cast"), sval, TRUE, (bool)(increment ? TRUE : FALSE))) { if (spell == -2) msgf("You don't know any %ss in that book.", prayer); return; } /* Access the spell */ use_realm = p_ptr->spell.r[increment / 32].realm; s_ptr = &mp_ptr->info[use_realm - 1][spell]; /* Get mana cost */ smana = spell_mana(spell, use_realm - 1); /* Verify "dangerous" spells */ if (smana > p_ptr->csp) { /* Warning */ msgf("You do not have enough mana to %s this %s.", ((mp_ptr->spell_book == TV_LIFE_BOOK) ? "recite" : "cast"), prayer); /* Verify */ if (!get_check("Attempt it anyway? ")) return; } /* Spell failure chance */ chance = spell_chance(spell, use_realm - 1); /* Failed spell */ if (randint0(100) < chance) { if (flush_failure) flush(); msgf("You failed to get the %s off!", prayer); sound(SOUND_FAIL); if ((randint1(100) < chance) && (mp_ptr->spell_book == TV_LIFE_BOOK)) chg_virtue(V_FAITH, -1); else if (randint1(100) < chance) chg_virtue(V_KNOWLEDGE, -1); if (realm == REALM_TRUMP) { (void)cast_trump_spell(spell, FALSE); } else if ((o_ptr->tval == TV_CHAOS_BOOK) && (randint1(100) < spell)) { msgf("You produce a chaotic effect!"); wild_magic(spell); } else if ((o_ptr->tval == TV_DEATH_BOOK) && (randint1(100) < spell)) { if ((sval == 3) && one_in_(2)) { msgf("Your sanity is shaken by reading the Necronomicon!"); /* Mind blast */ if (!player_save(100)) { if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(4, 8)); } if (!(FLAG(p_ptr, TR_RES_CHAOS)) && one_in_(3)) { (void)inc_image(rand_range(150, 400)); } } /* Lose int & wis */ else if (!player_save(100)) { (void)do_dec_stat(A_INT); (void)do_dec_stat(A_WIS); } } else { msgf("It hurts!"); take_hit(damroll(o_ptr->sval + 1, 6), "a miscast Death spell"); if ((spell > 15) && one_in_(6) && !(FLAG(p_ptr, TR_HOLD_LIFE))) lose_exp(spell * 250); } } } /* Process spell */ else { if ((randint1(100) < chance) && (chance < 50)) { if (mp_ptr->spell_book == TV_LIFE_BOOK) chg_virtue(V_FAITH, 1); else chg_virtue(V_KNOWLEDGE, 1); } /* Spells. */ switch (realm) { case REALM_LIFE: /* * LIFE * */ cast = cast_life_spell(spell); break; case REALM_SORCERY: /* * SORCERY * */ cast = cast_sorcery_spell(spell); break; case REALM_NATURE: /* * NATURE * */ cast = cast_nature_spell(spell); break; case REALM_CHAOS: /* * CHAOS * */ cast = cast_chaos_spell(spell); break; case REALM_DEATH: /* * DEATH * */ cast = cast_death_spell(spell); break; case REALM_TRUMP: /* TRUMP */ cast = cast_trump_spell(spell, TRUE); break; case REALM_ARCANE: /* ARCANE */ cast = cast_arcane_spell(spell); break; default: cast = FALSE; msgf("You cast a spell from an unknown realm: realm %d, spell %d.", realm, spell); message_flush(); } /* Canceled spells cost neither a turn nor mana */ if (!cast) return; /* A spell was cast */ if (!(p_ptr->spell.r[increment / 32].worked & (1L << spell))) { /* Experience: 5, 20, 45, or 80 * spell level */ int book = 1 + (spell / 8); int exp = 5 * book * book * s_ptr->slevel; /* The spell worked */ if (realm == p_ptr->spell.r[0].realm) { p_ptr->spell.r[0].worked |= (1L << spell); } else { p_ptr->spell.r[1].worked |= (1L << spell); } /* Gain experience */ gain_exp(exp); if (mp_ptr->spell_book == TV_LIFE_BOOK) chg_virtue(V_FAITH, 1); else chg_virtue(V_KNOWLEDGE, 1); } } /* Take a turn */ p_ptr->state.energy_use = 100; /* Sufficient mana */ if (smana <= p_ptr->csp) { /* Use some mana */ p_ptr->csp -= smana; } /* Over-exert the player */ else { int oops = smana - p_ptr->csp; /* No mana left */ p_ptr->csp = 0; p_ptr->csp_frac = 0; /* Message */ msgf("You faint from the effort!"); /* Hack -- Bypass free action */ (void)inc_paralyzed(randint1(5 * oops + 1)); if (mp_ptr->spell_book == TV_LIFE_BOOK) chg_virtue(V_FAITH, -10); else chg_virtue(V_KNOWLEDGE, -10); /* Damage CON (possibly permanently) */ if (one_in_(2)) { bool perm = one_in_(4); /* Message */ msgf("You have damaged your health!"); /* Reduce constitution */ (void)dec_stat(A_CON, rand_range(15, 25), perm); } } /* Redraw mana */ p_ptr->redraw |= (PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); } /* * Pray a prayer -- Unused in Zangband */ void do_cmd_pray(void) { msgf("Praying is not used in %s. Use magic spell casting instead.", VERSION_NAME); } /* Forward declare */ extern menu_type pet_menu[PET_CHOICE_MAX + 1]; /* * Dismiss some pets */ static bool cmd_pets_dismiss(int dummy) { int dismissed = 0; monster_type *m_ptr; int pet_ctr; bool pets = FALSE, all_pets = FALSE; /* Ignore parameter */ (void) dummy; if (get_check("Dismiss all pets? ")) all_pets = TRUE; /* Process the monsters (backwards) */ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) { /* Access the monster */ m_ptr = &m_list[pet_ctr]; if (is_pet(m_ptr)) { bool delete_this = FALSE; if (all_pets) delete_this = TRUE; else { if (get_check("Dismiss %v? ", MONSTER_FMT(m_ptr, 0x80))) delete_this = TRUE; } if (delete_this) { /* Notice changes in view */ if (r_info[m_ptr->r_idx].flags[6] & (RF6_LITE_1 | RF6_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } delete_monster_idx(pet_ctr); dismissed++; } } } msgf("You have dismissed %d pet%s.", dismissed, (dismissed == 1 ? "" : "s")); /* Calculate pets */ /* Process the monsters (backwards) */ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) { /* Access the monster */ m_ptr = &m_list[pet_ctr]; if (is_pet(m_ptr)) { /* Is it a pet? */ pets = TRUE; break; } } /* Can only dismiss pets if actually have some */ if (pets) { pet_menu[PET_DISMISS].flags |= MN_ACTIVE; } else { pet_menu[PET_DISMISS].flags &= ~(MN_ACTIVE); } /* Stay at menu */ return (FALSE); } static bool cmd_pets_close(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Change follow distance */ p_ptr->pet_follow_distance = PET_CLOSE_DIST; /* Stay at menu */ return (FALSE); } static bool cmd_pets_follow(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Change follow distance */ p_ptr->pet_follow_distance = PET_FOLLOW_DIST; /* Stay at menu */ return (FALSE); } static bool cmd_pets_destroy(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Change follow distance */ p_ptr->pet_follow_distance = PET_DESTROY_DIST; /* Stay at menu */ return (FALSE); } static bool cmd_pets_space(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Change follow distance */ p_ptr->pet_follow_distance = PET_SPACE_DIST; /* Stay at menu */ return (FALSE); } static bool cmd_pets_away(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Change follow distance */ p_ptr->pet_follow_distance = PET_AWAY_DIST; /* Stay at menu */ return (FALSE); } static bool cmd_pets_doors(int dummy) { /* Hack - ignore parameter */ (void) dummy; /* Toggle the open doors flag */ p_ptr->pet_open_doors = !p_ptr->pet_open_doors; if (p_ptr->pet_open_doors) { pet_menu[PET_OPEN_DOORS].text = "pets may open doors"; } else { pet_menu[PET_OPEN_DOORS].text = "pets may not open doors"; } /* Stay at menu */ return (FALSE); } static bool cmd_pets_items(int dummy) { int pet_ctr; monster_type *m_ptr; /* Hack - ignore parameter */ (void) dummy; /* Toggle pet pickup flag */ p_ptr->pet_pickup_items = !p_ptr->pet_pickup_items; /* Drop objects being carried by pets */ if (!p_ptr->pet_pickup_items) { for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) { /* Access the monster */ m_ptr = &m_list[pet_ctr]; if (is_pet(m_ptr)) { drop_object_list(&m_ptr->hold_o_idx, m_ptr->fx, m_ptr->fy); } } } if (p_ptr->pet_pickup_items) { pet_menu[PET_TAKE_ITEMS].text = "pets may pick up items"; } else { pet_menu[PET_TAKE_ITEMS].text = "pets may not pick up items"; } /* Stay at menu */ return (FALSE); } /* The menu used to interact with pets */ menu_type pet_menu[PET_CHOICE_MAX + 1] = { {"Stay close", NULL, cmd_pets_close, MN_ACTIVE | MN_SELECT}, {"Follow me", NULL, cmd_pets_follow, MN_ACTIVE | MN_SELECT}, {"Seek and destroy", NULL, cmd_pets_destroy, MN_ACTIVE | MN_SELECT}, {"Give me space", NULL, cmd_pets_space, MN_ACTIVE | MN_SELECT}, {"Stay away", NULL, cmd_pets_away, MN_ACTIVE | MN_SELECT}, {NULL, NULL, cmd_pets_doors, MN_ACTIVE}, {NULL, NULL, cmd_pets_items, MN_ACTIVE}, {"Display current pets", NULL, do_cmd_knowledge_pets, MN_ACTIVE | MN_CLEAR}, {"Dismiss pets", NULL, cmd_pets_dismiss, MN_ACTIVE | MN_CLEAR}, MENU_END }; /* * Issue a pet command */ void do_cmd_pet(void) { bool pets = FALSE; int pet_ctr; monster_type *m_ptr; int pet_select = -1; /* Calculate pets */ /* Process the monsters (backwards) */ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) { /* Access the monster */ m_ptr = &m_list[pet_ctr]; if (is_pet(m_ptr)) { /* Is it a pet? */ pets = TRUE; break; } } /* Can only dismiss pets if actually have some */ if (pets) { pet_menu[PET_DISMISS].flags |= MN_ACTIVE; } else { pet_menu[PET_DISMISS].flags &= ~(MN_ACTIVE); } /* Get current option */ if (p_ptr->pet_follow_distance == PET_CLOSE_DIST) { pet_select = PET_STAY_CLOSE; } if (p_ptr->pet_follow_distance == PET_FOLLOW_DIST) { pet_select = PET_FOLLOW_ME; } if (p_ptr->pet_follow_distance == PET_DESTROY_DIST) { pet_select = PET_SEEK_AND_DESTROY; } if (p_ptr->pet_follow_distance == PET_SPACE_DIST) { pet_select = PET_ALLOW_SPACE; } if (p_ptr->pet_follow_distance == PET_AWAY_DIST) { pet_select = PET_STAY_AWAY; } /* Change option text depending on flag */ if (p_ptr->pet_open_doors) { pet_menu[PET_OPEN_DOORS].text = "pets may open doors"; } else { pet_menu[PET_OPEN_DOORS].text = "pets may not open doors"; } /* Change option text depending on flag */ if (p_ptr->pet_pickup_items) { pet_menu[PET_TAKE_ITEMS].text = "pets may pick up items"; } else { pet_menu[PET_TAKE_ITEMS].text = "pets may not pick up items"; } /* Interact with menu */ display_menu(pet_menu, pet_select, FALSE, NULL, NULL); } zangband/src/cmd6.c0000755000000000000000000005251510250356274013143 0ustar rootroot/* File: cmd6.c */ /* Purpose: Object commands */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* * This file includes code for eating food, drinking potions, * reading scrolls, aiming wands, using staffs, zapping rods, * and activating artifacts. * * In all cases, if the player becomes "aware" of the item's use * by testing it, mark it as "aware" and reward some experience * based on the object's level, always rounding up. If the player * remains "unaware", mark that object "kind" as "tried". * * This code now correctly handles the unstacking of wands, staffs, * and rods. Note the overly paranoid warning about potential pack * overflow, which allows the player to use and drop a stacked item. * * In all "unstacking" scenarios, the "used" object is "carried" as if * the player had just picked it up. In particular, this means that if * the use of an item induces pack overflow, that item will be dropped. * * For simplicity, these routines induce a full "pack reorganization" * which not only combines similar items, but also reorganizes various * items to obey the current "sorting" method. This may require about * 400 item comparisons, but only occasionally. * * There may be a BIG problem with any "effect" that can cause "changes" * to the inventory. For example, a "scroll of recharging" can cause * a wand/staff to "disappear", moving the inventory up. Luckily, the * scrolls all appear BEFORE the staffs/wands, so this is not a problem. * But, for example, a "staff of recharging" could cause MAJOR problems. * In such a case, it will be best to either (1) "postpone" the effect * until the end of the function, or (2) "change" the effect, say, into * giving a staff "negative" charges, or "turning a staff into a stick". * It seems as though a "rod of recharging" might in fact cause problems. * The basic problem is that the act of recharging (and destroying) an * item causes the inducer of that action to "move", causing "o_ptr" to * no longer point at the correct item, with horrifying results. * * Note that food/potions/scrolls no longer use bit-flags for effects, * but instead use the "sval" (which is also used to sort the objects). */ static void do_cmd_eat_food_aux(object_type *o_ptr) { bool ident; /* Sound */ sound(SOUND_EAT); /* Take a turn */ p_ptr->state.energy_use = 100; /* Is Identity known? */ ident = object_aware_p(o_ptr); /* Eat the food */ (void)use_object(o_ptr, &ident, FALSE); if (!(object_aware_p(o_ptr))) { chg_virtue(V_PATIENCE, -1); chg_virtue(V_CHANCE, 1); } /* We have tried it */ object_tried(o_ptr); /* The player is now aware of the object */ if (ident && !object_aware_p(o_ptr)) { /* Object level */ int lev = get_object_level(o_ptr); object_aware(o_ptr); gain_exp((lev + p_ptr->lev / 2) / p_ptr->lev); } /* Notice changes */ notice_item(); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Food can feed the player */ if (p_ptr->rp.prace == RACE_VAMPIRE) { /* Reduced nutritional benefit */ (void)set_food(p_ptr->food + (o_ptr->pval / 10)); msgf ("Mere victuals hold scant sustenance for a being such as yourself."); if (p_ptr->food < PY_FOOD_ALERT) /* Hungry */ msgf("Your hunger can only be satisfied with fresh blood!"); } else if (FLAG(p_ptr, TR_CANT_EAT)) { if (p_ptr->rp.prace == RACE_SKELETON) { if (!((o_ptr->sval == SV_FOOD_WAYBREAD) || (o_ptr->sval < SV_FOOD_BISCUIT))) { object_type *q_ptr; msgf("The food falls through your jaws!"); /* Create the item */ q_ptr = object_prep(lookup_kind(o_ptr->tval, o_ptr->sval)); /* Drop the object from heaven */ drop_near(q_ptr, -1, p_ptr->px, p_ptr->py); } else { msgf("The food falls through your jaws and vanishes!"); } } else if ((p_ptr->rp.prace == RACE_GOLEM) || (p_ptr->rp.prace == RACE_ZOMBIE) || (p_ptr->rp.prace == RACE_SPECTRE) || (p_ptr->rp.prace == RACE_GHOUL)) { msgf("The food of mortals is poor sustenance for you."); (void)set_food(p_ptr->food + ((o_ptr->pval) / 20)); } else { msgf("This food is poor sustenance for you."); set_food(p_ptr->food + ((o_ptr->pval) / 20)); } } else { (void)set_food(p_ptr->food + o_ptr->pval); } /* Destroy a food item */ item_increase(o_ptr, -1); make_noise(1); } /* * Eat some food (from the pack or floor) */ void do_cmd_eat_food(void) { object_type *o_ptr; cptr q, s; /* Restrict choices to food */ item_tester_tval = TV_FOOD; /* Get an item */ q = "Eat which item? "; s = "You have nothing to eat."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Eat the object */ do_cmd_eat_food_aux(o_ptr); } /* * Quaff a potion (from the pack or the floor) */ static void do_cmd_quaff_potion_aux(object_type *o_ptr) { bool ident; /* Sound */ sound(SOUND_QUAFF); /* Take a turn */ p_ptr->state.energy_use = 100; /* Is Identity known? */ ident = object_aware_p(o_ptr); /* Quaff the potion */ (void)use_object(o_ptr, &ident, FALSE); if (p_ptr->rp.prace == RACE_SKELETON) { msgf("Some of the fluid falls through your jaws!"); (void)potion_smash_effect(0, p_ptr->px, p_ptr->py, o_ptr); } if (!(object_aware_p(o_ptr))) { chg_virtue(V_PATIENCE, -1); chg_virtue(V_CHANCE, 1); } /* The item has been tried */ object_tried(o_ptr); /* An identification was made */ if (ident && !object_aware_p(o_ptr)) { /* Object level */ int lev = get_object_level(o_ptr); object_aware(o_ptr); gain_exp((lev + p_ptr->lev / 2) / p_ptr->lev); } /* Notice changes */ notice_item(); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Potions can feed the player */ switch (p_ptr->rp.prace) { case RACE_VAMPIRE: (void)set_food(p_ptr->food + (o_ptr->pval / 10)); break; case RACE_SKELETON: /* Do nothing */ break; case RACE_GOLEM: case RACE_ZOMBIE: case RACE_SPECTRE: case RACE_GHOUL: (void)set_food(p_ptr->food + ((o_ptr->pval) / 20)); break; default: (void)set_food(p_ptr->food + o_ptr->pval); } /* Reduce and describe items */ item_increase(o_ptr, -1); make_noise(1); } void do_cmd_quaff_potion(void) { object_type *o_ptr; cptr q, s; /* Restrict choices to potions */ item_tester_tval = TV_POTION; /* Get an item */ q = "Quaff which potion? "; s = "You have no potions to quaff."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Quaff the potion */ do_cmd_quaff_potion_aux(o_ptr); } /* * Read a scroll (from the pack or floor). * * Certain scrolls can be "aborted" without losing the scroll. These * include scrolls with no effects but recharge or identify, which are * cancelled before use. XXX Reading them still takes a turn, though. */ static void do_cmd_read_scroll_aux(object_type *o_ptr) { object_type temp, *j_ptr; bool ident, used_up; /* Take a turn */ p_ptr->state.energy_use = 100; /* Is Identity known? */ ident = object_aware_p(o_ptr); /* Copy item into temp */ COPY(&temp, o_ptr, object_type); /* Read the scroll */ used_up = use_object(o_ptr, &ident, FALSE); /* * Counter the side effect of use_object on an identify scroll * by finding the original item */ OBJ_ITT_START (p_ptr->inventory, j_ptr) { /* retrieve the pointer of the original scroll */ if (object_equal(&temp, j_ptr)) o_ptr = j_ptr; } OBJ_ITT_END; /* Hack - the scroll may already be destroyed by its effect */ if (o_ptr->k_idx) { if (!(object_aware_p(o_ptr))) { chg_virtue(V_PATIENCE, -1); chg_virtue(V_CHANCE, 1); } /* The item was tried */ object_tried(o_ptr); /* An identification was made */ if (ident && !object_aware_p(o_ptr)) { /* Object level */ int lev = get_object_level(o_ptr); object_aware(o_ptr); gain_exp((lev + p_ptr->lev / 2) / p_ptr->lev); } /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Hack. Do the sorting now */ o_ptr = reorder_pack_watch(o_ptr); /* Hack. Do the combining now */ o_ptr = combine_pack_watch(o_ptr); /* Hack -- allow certain scrolls to be "preserved" */ if (!used_up) return; sound(SOUND_SCROLL); /* Destroy a scroll */ item_increase(o_ptr, -1); } make_noise(1); } void do_cmd_read_scroll(void) { object_type *o_ptr; cptr q, s; /* Check some conditions */ if (p_ptr->tim.blind) { msgf("You can't see anything."); return; } if (no_lite()) { msgf("You have no light to read by."); return; } if (p_ptr->tim.confused) { msgf("You are too confused!"); return; } /* Restrict choices to scrolls */ item_tester_tval = TV_SCROLL; /* Get an item */ q = "Read which scroll? "; s = "You have no scrolls to read."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Read the scroll */ do_cmd_read_scroll_aux(o_ptr); } /* * Use a staff. * * One charge of one staff disappears. * * Hack -- staffs of identify can be "cancelled". */ static void do_cmd_use_staff_aux(object_type *o_ptr) { int chance, lev; bool ident, use_charge; /* Mega-Hack -- refuse to use a pile from the ground */ if (floor_item(o_ptr) && (o_ptr->number > 1)) { msgf("You must first pick up the staffs."); return; } /* Take a turn */ p_ptr->state.energy_use = 100; /* Is Identity known? */ ident = object_aware_p(o_ptr); /* Extract the item level */ lev = get_object_level(o_ptr); /* Base chance of success */ chance = p_ptr->skills[SKILL_DEV]; /* Confusion hurts skill */ if (p_ptr->tim.confused) chance = chance / 2; /* Hight level objects are harder */ chance = chance - lev / 2; /* Give everyone a (slight) chance */ if ((chance < USE_DEVICE) && one_in_(USE_DEVICE - chance + 1)) { chance = USE_DEVICE; } /* Roll for usage */ if ((chance < USE_DEVICE) || (randint1(chance) < USE_DEVICE)) { if (flush_failure) flush(); msgf("You failed to use the staff properly."); sound(SOUND_FAIL); return; } /* Notice empty staffs */ if (o_ptr->pval <= 0) { if (flush_failure) flush(); msgf("The staff has no charges left."); o_ptr->info |= (OB_EMPTY); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); return; } /* Sound */ sound(SOUND_ZAP); /* Use the staff */ use_charge = use_object(o_ptr, &ident, FALSE); /* Hack - the staff may destroy itself when activated on the ground */ if (o_ptr->k_idx) { if (!(object_aware_p(o_ptr))) { chg_virtue(V_PATIENCE, -1); chg_virtue(V_CHANCE, 1); } /* Tried the item */ object_tried(o_ptr); /* An identification was made */ if (ident && !object_aware_p(o_ptr)) { object_aware(o_ptr); gain_exp((lev + p_ptr->lev / 2) / p_ptr->lev); } /* Notice changes */ notice_item(); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Hack -- some uses are "free" */ if (!use_charge) return; /* XXX Hack -- unstack if necessary */ if (o_ptr->number > 1) { /* Split object */ o_ptr = item_split(o_ptr, 1); /* Use a single charge */ o_ptr->pval--; /* Unstack the used item */ o_ptr = inven_carry(o_ptr); /* Notice weight changes */ p_ptr->update |= PU_WEIGHT; /* Paranoia */ if (!o_ptr) { msgf("Too many dungeon objects - staff lost!"); make_noise(1); /* Exit */ return; } /* Message */ msgf("You unstack your staff."); } else { /* Use a single charge */ o_ptr->pval--; } /* Describe charges in the pack */ if (o_ptr) item_charges(o_ptr); } make_noise(1); } void do_cmd_use_staff(void) { object_type *o_ptr; cptr q, s; /* Restrict choices to wands */ item_tester_tval = TV_STAFF; /* Get an item */ q = "Use which staff? "; s = "You have no staff to use."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; do_cmd_use_staff_aux(o_ptr); } /* * Aim a wand (from the pack or floor). * * Use a single charge from a single item. * Handle "unstacking" in a logical manner. * * For simplicity, you cannot use a stack of items from the * ground. This would require too much nasty code. * * There are no wands which can "destroy" themselves, in the inventory * or on the ground, so we can ignore this possibility. Note that this * required giving "wand of wonder" the ability to ignore destruction * by electric balls. * * All wands can be "cancelled" at the "Direction?" prompt for free. * * Note that the basic "bolt" wands do slightly less damage than the * basic "bolt" rods, but the basic "ball" wands do the same damage * as the basic "ball" rods. */ static void do_cmd_aim_wand_aux(object_type *o_ptr) { bool use_charge; int chance, dir, lev; bool ident; /* Mega-Hack -- refuse to use a pile from the ground */ if (floor_item(o_ptr) && (o_ptr->number > 1)) { msgf("You must first pick up the wands."); return; } /* Notice empty wandss */ if (o_ptr->pval <= 0) { if (flush_failure) flush(); msgf("The wand has no charges left."); o_ptr->info |= (OB_EMPTY); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); return; } /* Allow direction to be cancelled for free */ if (!get_aim_dir(&dir)) return; /* Is Identity known? */ ident = object_aware_p(o_ptr); /* Take a turn */ p_ptr->state.energy_use = MIN(75, 200 - 5 * p_ptr->skills[SKILL_DEV] / 8); /* Get the object level */ lev = k_info[o_ptr->k_idx].level; /* Base chance of success */ chance = p_ptr->skills[SKILL_DEV]; /* Confusion hurts skill */ if (p_ptr->tim.confused) chance /= 2; /* Hight level objects are harder */ chance = chance - lev / 2; /* Give everyone a (slight) chance */ if ((chance < USE_DEVICE) && one_in_(USE_DEVICE - chance + 1)) { chance = USE_DEVICE; } /* Roll for usage */ if ((chance < USE_DEVICE) || (randint1(chance) < USE_DEVICE)) { if (flush_failure) flush(); msgf("You failed to use the wand properly."); sound(SOUND_FAIL); return; } /* Sound */ sound(SOUND_ZAP); /* Aim the wand */ use_charge = use_object(o_ptr, &ident, dir); /* Hack - wands may destroy themselves if activated on the ground */ if (o_ptr->k_idx) { /* Mark it as tried */ object_tried(o_ptr); /* Apply identification */ if (ident && !object_aware_p(o_ptr)) { int lev = get_object_level(o_ptr); object_aware(o_ptr); gain_exp((lev + p_ptr->lev / 2) / p_ptr->lev); } /* Notice changes */ notice_item(); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Hack -- some uses are "free" */ if (!use_charge) return; /* Use a single charge */ o_ptr->pval--; o_ptr->ac++; /* Describe the charges */ item_charges(o_ptr); } make_noise(1); } void do_cmd_aim_wand(void) { object_type *o_ptr; cptr q, s; /* Restrict choices to wands */ item_tester_tval = TV_WAND; /* Get an item */ q = "Aim which wand? "; s = "You have no wand to aim."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Aim the wand */ do_cmd_aim_wand_aux(o_ptr); } /* * Activate (zap) a Rod * * Unstack fully charged rods as needed. * * Hack -- rods of perception/genocide can be "cancelled" * All rods can be cancelled at the "Direction?" prompt * * pvals are defined for each rod in k_info. -LM- */ static void do_cmd_zap_rod_aux(object_type *o_ptr) { int chance, dir, lev; bool ident; /* Hack -- let perception get aborted */ bool use_charge = TRUE; object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* Mega-Hack -- refuse to use a pile from the ground */ if (floor_item(o_ptr) && (o_ptr->number > 1)) { msgf("You must first pick up the rods."); return; } /* A single rod is still charging */ if ((o_ptr->number == 1) && (o_ptr->timeout)) { if (flush_failure) flush(); msgf("The rod is still charging."); return; } /* A stack of rods lacks enough energy. */ else if ((o_ptr->number > 1) && (o_ptr->timeout > (o_ptr->number - 1) * k_ptr->pval)) { if (flush_failure) flush(); msgf("The rods are all still charging."); return; } /* Get a direction (unless KNOWN not to need it) */ if (((o_ptr->sval >= SV_ROD_MIN_DIRECTION) && (o_ptr->sval != SV_ROD_HAVOC)) || !object_aware_p(o_ptr)) { /* Get a direction, allow cancel */ if (!get_aim_dir(&dir)) return; } /* Take a turn */ p_ptr->state.energy_use = MIN(75, 200 - 5 * p_ptr->skills[SKILL_DEV] / 8); /* Not identified yet */ ident = FALSE; /* Is Identity known? */ ident = object_aware_p(o_ptr); /* Extract the item level */ lev = get_object_level(o_ptr); /* Base chance of success */ chance = p_ptr->skills[SKILL_DEV]; /* Confusion hurts skill */ if (p_ptr->tim.confused) chance = chance / 2; /* Hight level objects are harder */ chance = chance - lev / 2; /* Give everyone a (slight) chance */ if ((chance < USE_DEVICE) && one_in_(USE_DEVICE - chance + 1)) { chance = USE_DEVICE; } /* Roll for usage */ if ((chance < USE_DEVICE) || (randint1(chance) < USE_DEVICE)) { if (flush_failure) flush(); msgf("You failed to use the rod properly."); sound(SOUND_FAIL); return; } /* Sound */ sound(SOUND_ZAP); /* Increase the timeout by the rod kind's pval. -LM- */ o_ptr->timeout += k_ptr->pval; /* Zap the rod */ use_charge = use_object(o_ptr, &ident, dir); if (!(object_aware_p(o_ptr))) { chg_virtue(V_PATIENCE, -1); chg_virtue(V_CHANCE, 1); } /* Tried the object */ object_tried(o_ptr); /* Successfully determined the object function */ if (ident && !object_aware_p(o_ptr)) { object_aware(o_ptr); gain_exp((lev + p_ptr->lev / 2) / p_ptr->lev); } /* Notice changes */ notice_item(); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Hack -- deal with cancelled zap */ if (!use_charge) { o_ptr->timeout -= k_ptr->pval; return; } make_noise(1); } void do_cmd_zap_rod(void) { object_type *o_ptr; cptr q, s; /* Restrict choices to rods */ item_tester_tval = TV_ROD; /* Get an item */ q = "Zap which rod? "; s = "You have no rod to zap."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Zap the rod */ do_cmd_zap_rod_aux(o_ptr); } /* * Hook to determine if an object is activatable */ static bool item_tester_hook_activate(const object_type *o_ptr) { /* Check statues */ if (o_ptr->tval == TV_STATUE) return (TRUE); /* Ignore dungeon objects */ if (o_ptr->iy || o_ptr->ix) return (FALSE); /* Not known */ if (!object_known_p(o_ptr)) return (FALSE); /* Check activation flag */ if (FLAG(o_ptr, TR_ACTIVATE)) return (TRUE); /* Assume not */ return (FALSE); } /* * Hack -- activate the ring of power */ void ring_of_power(int dir) { /* Pick a random effect */ switch (randint1(10)) { case 1: case 2: { /* Message */ msgf("You are surrounded by a malignant aura."); sound(SOUND_EVIL); /* Decrease all stats (permanently) */ (void)dec_stat(A_STR, 50, TRUE); (void)dec_stat(A_INT, 50, TRUE); (void)dec_stat(A_WIS, 50, TRUE); (void)dec_stat(A_DEX, 50, TRUE); (void)dec_stat(A_CON, 50, TRUE); (void)dec_stat(A_CHR, 50, TRUE); /* Lose some experience (permanently) */ p_ptr->exp -= (p_ptr->exp / 4); p_ptr->max_exp -= (p_ptr->exp / 4); check_experience(); break; } case 3: { /* Message */ msgf("You are surrounded by a powerful aura."); /* Dispel monsters */ (void)dispel_monsters(1000); break; } case 4: case 5: case 6: { /* Mana Ball */ (void)fire_ball(GF_MANA, dir, 300, 3); break; } case 7: case 8: case 9: case 10: { /* Mana Bolt */ (void)fire_bolt(GF_MANA, dir, 250); break; } } } /* * Activate a wielded object. Wielded objects never stack. * And even if they did, activatable objects never stack. * * Note that it always takes a turn to activate an artifact, even if * the user hits "escape" at the "direction" prompt. */ static void do_cmd_activate_aux(object_type *o_ptr) { int lev, chance; /* Take a turn */ p_ptr->state.energy_use = MIN(75, 200 - 5 * p_ptr->skills[SKILL_DEV] / 8); /* Extract the item level */ lev = get_object_level(o_ptr); /* Base chance of success */ chance = p_ptr->skills[SKILL_DEV]; /* Confusion hurts skill */ if (p_ptr->tim.confused) chance /= 2; /* Cursed items are difficult to activate */ if (cursed_p(o_ptr)) chance /= 3; /* High level objects are harder */ chance = chance - lev / 2; /* Give everyone a (slight) chance */ if ((chance < USE_DEVICE) && one_in_(USE_DEVICE - chance + 1)) { chance = USE_DEVICE; } /* Roll for usage */ if ((chance < USE_DEVICE) || (randint1(chance) < USE_DEVICE)) { if (flush_failure) flush(); msgf("You failed to activate it properly."); sound(SOUND_FAIL); return; } /* Check the recharge */ if (o_ptr->timeout) { msgf("It whines, glows and fades..."); return; } /* Activate the artifact */ msgf(MSGT_ZAP, "You activate it..."); /* Sound */ sound(SOUND_ZAP); /* Activate the object */ apply_object_trigger(TRIGGER_USE, o_ptr, ""); /* Notice changes */ notice_item(); make_noise(3); return; } void do_cmd_activate(void) { object_type *o_ptr; cptr q, s; /* Prepare the hook */ item_tester_hook = item_tester_hook_activate; /* Get an item */ q = "Activate which item? "; s = "You have nothing to activate."; o_ptr = get_item(q, s, (USE_EQUIP | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; /* Activate the item */ do_cmd_activate_aux(o_ptr); } zangband/src/dungeon.c0000755000000000000000000017562610250356274013762 0ustar rootroot/* File: dungeon.c */ /* Purpose: Angband game engine */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" #define TY_CURSE_CHANCE 100 /* * Return a "feeling" (or NULL) about an item. Method 1 (Heavy). */ static byte value_check_aux1(const object_type *o_ptr) { /* Artifacts */ if (FLAG(o_ptr, TR_INSTA_ART)) { /* Cursed / Worthless */ if (cursed_p(o_ptr) || !o_ptr->cost) return FEEL_TERRIBLE; /* Normal */ return FEEL_SPECIAL; } /* Ego-Items */ if (ego_item_p(o_ptr)) { /* Ego items with negative pvals or flags like aggravate or teleport */ if (!o_ptr->cost) { return FEEL_WORTHLESS; } else if (cursed_p(o_ptr)) { return FEEL_TAINTED; } /* Normal */ return FEEL_EXCELLENT; } /* Broken items */ if (!o_ptr->cost) return FEEL_BROKEN; /* Good bonus */ if ((o_ptr->to_a > 0) || (o_ptr->to_h + o_ptr->to_d > 0)) { /* Cursed good item? */ if (cursed_p(o_ptr)) return FEEL_DUBIOUS; /* Normal good item */ return FEEL_GOOD; } /* Cursed items */ if (cursed_p(o_ptr)) return FEEL_CURSED; /* Worthless is "bad" */ if (!object_value(o_ptr)) return FEEL_BAD; /* Default to "average" */ return FEEL_AVERAGE; } /* * Return a "feeling" (or NULL) about an item. Method 2 (Light). */ static byte value_check_aux2(const object_type *o_ptr) { /* Cursed items (all of them) */ if (cursed_p(o_ptr)) return FEEL_CURSED; /* Broken items (all of them) */ if (o_ptr->cost <= 0) return FEEL_BROKEN; /* Artifacts -- except cursed/broken ones */ if (FLAG(o_ptr, TR_INSTA_ART)) return FEEL_GOOD; /* Ego-Items -- except cursed/broken ones */ if (ego_item_p(o_ptr)) return FEEL_GOOD; /* Good armor bonus */ if (o_ptr->to_a > 0) return FEEL_GOOD; /* Good weapon bonuses */ if (o_ptr->to_h + o_ptr->to_d > 0) return FEEL_GOOD; /* Worthless is "bad" */ if (!object_value(o_ptr)) return FEEL_BAD; /* No feeling */ return FEEL_NONE; } /* * Psuedo-id the item */ void sense_item(object_type *o_ptr, bool heavy, bool wield, bool msg) { byte feel; int slot; bool okay = FALSE; /* Valid "tval" codes */ switch (o_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { okay = TRUE; break; } case TV_FIGURINE: { if (!heavy) okay = TRUE; break; } default: { /* Skip */ return; } } /* We know about it already, do not tell us again */ if (o_ptr->info & (OB_SENSE)) return; /* It is fully known, no information needed */ if (object_known_p(o_ptr)) return; /* Occasional failure on inventory items */ if (!wield && !one_in_(5)) return; /* Good luck */ if ((p_ptr->muta3 & MUT3_GOOD_LUCK) && !one_in_(13)) { heavy = TRUE; } /* Check for a feeling */ feel = (heavy ? value_check_aux1(o_ptr) : value_check_aux2(o_ptr)); /* Skip non-changes */ if (feel == o_ptr->feeling) return; /* hack the knowledge flag */ if (cursed_p(o_ptr)) o_ptr->kn_flags[2] |= TR2_CURSED; /* Bad luck */ if ((p_ptr->muta3 & MUT3_BAD_LUCK) && !one_in_(13)) { switch (feel) { case FEEL_TERRIBLE: { feel = FEEL_SPECIAL; break; } case FEEL_WORTHLESS: { feel = FEEL_EXCELLENT; break; } case FEEL_CURSED: { feel = one_in_(3) ? FEEL_AVERAGE : FEEL_GOOD; break; } case FEEL_AVERAGE: { feel = one_in_(2) ? FEEL_BAD : FEEL_GOOD; break; } case FEEL_GOOD: case FEEL_BAD: { feel = one_in_(3) ? FEEL_AVERAGE : FEEL_CURSED; break; } case FEEL_EXCELLENT: { feel = FEEL_WORTHLESS; break; } case FEEL_SPECIAL: { feel = FEEL_TERRIBLE; break; } } } /* Stop everything */ if (disturb_minor) disturb(FALSE); /* Message */ if (msg) { /* Message (equipment) */ if (wield) { slot = GET_ARRAY_INDEX(p_ptr->equipment, o_ptr); msgf("You feel the %v (%c) you are %s %s %s...", OBJECT_FMT(o_ptr, FALSE, 0), I2A(slot), describe_use(slot), ((o_ptr->number == 1) ? "is" : "are"), game_inscriptions[feel]); } /* Message (inventory) */ else { slot = get_item_position(p_ptr->inventory, o_ptr); msgf("You feel the %v (%c) in your pack %s %s...", OBJECT_FMT(o_ptr, FALSE, 0), I2A(slot), ((o_ptr->number == 1) ? "is" : "are"), game_inscriptions[feel]); } } /* We have "felt" it */ o_ptr->info |= (OB_SENSE); /* Set the "inscription" */ o_ptr->feeling = feel; /* Notice changes */ notice_item(); } /* * Sense the inventory * * Class 0 = Warrior --> fast and heavy * Class 1 = Mage --> slow and light * Class 2 = Priest --> fast but light * Class 3 = Rogue --> okay and heavy * Class 4 = Ranger --> slow but heavy (changed!) * Class 5 = Paladin --> slow but heavy */ static void sense_inventory(void) { int i; bool heavy; object_type *o_ptr; long difficulty; /*** Check for "sensing" ***/ /* No sensing when confused */ if (p_ptr->tim.confused) return; /* Analyze the class */ switch (p_ptr->rp.pclass) { case CLASS_WARRIOR: { /* Good (heavy) sensing */ difficulty = 9000L; /* Done */ break; } case CLASS_MAGE: case CLASS_HIGH_MAGE: { /* Very bad (light) sensing */ difficulty = 240000L; /* Done */ break; } case CLASS_PRIEST: { /* Good (light) sensing */ difficulty = 10000L; /* Done */ break; } case CLASS_ROGUE: { /* Okay sensing */ difficulty = 20000L; /* Done */ break; } case CLASS_RANGER: { /* Bad (heavy) sensing */ difficulty = 95000L; /* Done */ break; } case CLASS_PALADIN: { /* Bad (heavy) sensing */ difficulty = 77777L; /* Done */ break; } case CLASS_WARRIOR_MAGE: { /* Bad sensing */ difficulty = 75000L; /* Done */ break; } case CLASS_MINDCRAFTER: { /* Bad sensing */ difficulty = 55000L; /* Done */ break; } case CLASS_CHAOS_WARRIOR: { /* Bad (heavy) sensing */ difficulty = 80000L; /* Done */ break; } case CLASS_MONK: { /* Okay sensing */ difficulty = 20000L; heavy = FALSE; /* Done */ break; } default: { /* Paranoia */ difficulty = 0; } } /* * Scale difficulty depending on sensing ability * This can be affected by objects. */ difficulty /= (p_ptr->skills[SKILL_SNS] > 0 ? p_ptr->skills[SKILL_SNS] : 1); /* Rescale larger by a facter of 25 */ difficulty *= 25; /* Sensing gets better as you get more experienced */ difficulty /= p_ptr->lev * p_ptr->lev + 40; /* Does it work? */ if (!(one_in_(difficulty))) return; /* Heavy sensing? */ heavy = class_info[p_ptr->rp.pclass].heavy_sense; /*** Sense everything ***/ /* Scan Equipment */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Skip empty slots */ if (!o_ptr->k_idx) continue; sense_item(o_ptr, heavy, TRUE, TRUE); } /* Scan inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { sense_item(o_ptr, heavy, FALSE, TRUE); } OBJ_ITT_END; } /* * Go to any level (ripped off from wiz_jump) */ static void pattern_teleport(void) { int min_level = 0; int max_level = 99; /* Ask for level */ if (get_check("Teleport level? ")) { char tmp_val[160]; /* Only downward in ironman mode */ if (ironman_downward) min_level = p_ptr->depth; /* Maximum level */ if (p_ptr->depth > 100) max_level = dungeon()->max_level; else if (p_ptr->depth == 100) max_level = 100; /* Default */ strnfmt(tmp_val, 160, "%d", p_ptr->depth); /* Ask for a level */ if (!get_string(tmp_val, 11, "Teleport to level (%d-%d): ", min_level, max_level)) return; /* Extract request */ p_ptr->cmd.arg = atoi(tmp_val); } else if (get_check("Normal teleport? ")) { teleport_player(200); return; } else { return; } /* Paranoia */ if (p_ptr->cmd.arg < min_level) p_ptr->cmd.arg = min_level; /* Paranoia */ if (p_ptr->cmd.arg > max_level) p_ptr->cmd.arg = max_level; /* Accept request */ msgf("You teleport to dungeon level %d.", p_ptr->cmd.arg); /* Change level */ p_ptr->depth = p_ptr->cmd.arg; /* Leaving */ p_ptr->state.leaving = TRUE; } static void wreck_the_pattern(void) { int px = p_ptr->px; int py = p_ptr->py; int to_ruin, r_y, r_x; if (area(px, py)->feat == FEAT_PATTERN_XTRA2) { /* Ruined already */ return; } msgf("You bleed on the Pattern!"); msgf("Something terrible happens!"); if (!p_ptr->tim.invuln) take_hit(damroll(10, 8), "corrupting the Pattern"); to_ruin = rand_range(35, 80); while (to_ruin--) { scatter(&r_x, &r_y, px, py, 4); if ((area(r_x, r_y)->feat >= FEAT_PATTERN_START) && (area(r_x, r_y)->feat < FEAT_PATTERN_XTRA2)) { cave_set_feat(r_x, r_y, FEAT_PATTERN_XTRA2); } } cave_set_feat(px, py, FEAT_PATTERN_XTRA2); } /* * Returns TRUE if we are on the Pattern... */ static bool pattern_effect(void) { cave_type *c_ptr = area(p_ptr->px, p_ptr->py); if ((c_ptr->feat < FEAT_PATTERN_START) || (c_ptr->feat > FEAT_PATTERN_XTRA2)) return FALSE; if ((p_ptr->rp.prace == RACE_AMBERITE) && (p_ptr->tim.cut > 0) && one_in_(10)) { wreck_the_pattern(); } if (c_ptr->feat == FEAT_PATTERN_END) { (void)clear_poisoned(); (void)clear_image(); (void)clear_stun(); (void)clear_cut(); (void)clear_blind(); (void)clear_afraid(); (void)do_res_stat(A_STR); (void)do_res_stat(A_INT); (void)do_res_stat(A_WIS); (void)do_res_stat(A_DEX); (void)do_res_stat(A_CON); (void)do_res_stat(A_CHR); (void)restore_level(); (void)hp_player(1000); cave_set_feat(p_ptr->px, p_ptr->py, FEAT_PATTERN_OLD); msgf("This section of the Pattern looks less powerful."); } /* * We could make the healing effect of the * Pattern center one-time only to avoid various kinds * of abuse, like luring the win monster into fighting you * in the middle of the pattern... */ else if (c_ptr->feat == FEAT_PATTERN_OLD) { /* No effect */ } else if (c_ptr->feat == FEAT_PATTERN_XTRA1) { pattern_teleport(); } else if (c_ptr->feat == FEAT_PATTERN_XTRA2) { if (!p_ptr->tim.invuln) take_hit(200, "walking the corrupted Pattern"); } else { if ((p_ptr->rp.prace == RACE_AMBERITE) && one_in_(2)) return TRUE; else if (!p_ptr->tim.invuln) take_hit(damroll(1, 3), "walking the Pattern"); } return TRUE; } /* * Regenerate hit points -RAK- */ static void regenhp(int percent) { u32b new_chp, new_chp_frac; int old_chp; /* Save the old hitpoints */ old_chp = p_ptr->chp; /* Extract the new hitpoints */ new_chp = ((u32b)p_ptr->mhp) * percent + PY_REGEN_HPBASE; p_ptr->chp += (s16b)(new_chp >> 16); /* div 65536 */ /* check for overflow */ if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT; new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac; /* mod 65536 */ if (new_chp_frac >= 0x10000L) { p_ptr->chp_frac = (u16b)(new_chp_frac - 0x10000L); p_ptr->chp++; } else { p_ptr->chp_frac = (u16b)new_chp_frac; } /* Fully healed */ if (p_ptr->chp >= p_ptr->mhp) { p_ptr->chp = p_ptr->mhp; p_ptr->chp_frac = 0; } /* Notice changes */ if (old_chp != p_ptr->chp) { /* Redraw */ p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); } } /* * Regenerate mana points -RAK- */ static void regenmana(int percent) { u32b new_mana, new_mana_frac; int old_csp; old_csp = p_ptr->csp; new_mana = ((u32b)p_ptr->msp) * percent + PY_REGEN_MNBASE; p_ptr->csp += (s16b)(new_mana >> 16); /* div 65536 */ /* check for overflow */ if ((p_ptr->csp < 0) && (old_csp > 0)) { p_ptr->csp = MAX_SHORT; } new_mana_frac = (new_mana & 0xFFFF) + p_ptr->csp_frac; /* mod 65536 */ if (new_mana_frac >= 0x10000L) { p_ptr->csp_frac = (u16b)(new_mana_frac - 0x10000L); p_ptr->csp++; } else { p_ptr->csp_frac = (u16b)(new_mana_frac); } /* Must set frac to zero even if equal */ if (p_ptr->csp >= p_ptr->msp) { p_ptr->csp = p_ptr->msp; p_ptr->csp_frac = 0; } /* Redraw mana */ if (old_csp != p_ptr->csp) { /* Redraw */ p_ptr->redraw |= (PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); } } /* * Regenerate the monsters (once per 100 game turns) * * XXX XXX XXX Should probably be done during monster turns. */ static void regen_monsters(void) { int i, frac; /* Regenerate everyone */ for (i = 1; i < m_max; i++) { /* Check the i'th monster */ monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Allow regeneration (if needed) */ if (m_ptr->hp < m_ptr->maxhp) { /* Hack -- Base regeneration */ frac = m_ptr->maxhp / 100; /* Hack -- Minimal regeneration rate */ if (!frac) frac = 1; /* Hack -- Some monsters regenerate quickly */ if (FLAG(r_ptr, RF_REGENERATE)) frac *= 2; /* Hack -- Regenerate */ m_ptr->hp += frac; /* Do not over-regenerate */ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; /* Redraw (later) if needed */ if (p_ptr->health_who == i) p_ptr->redraw |= (PR_HEALTH); } } } void notice_lite_change(object_type *o_ptr) { /* Hack -- notice interesting fuel steps */ if ((o_ptr->timeout < 100) || (!(o_ptr->timeout % 100))) { /* Notice changes */ notice_equip(); } /* Hack -- Special treatment when blind */ if (p_ptr->tim.blind) { /* Hack -- save some light for later */ if (o_ptr->timeout == 0) o_ptr->timeout++; } /* The light is now out */ else if (o_ptr->timeout == 0) { disturb(FALSE); msgf("Your light has gone out!"); /* Calculate torch radius */ p_ptr->update |= (PU_TORCH); } /* The light is getting dim */ else if ((o_ptr->timeout < 100) && (!(o_ptr->timeout % 10))) { if (disturb_minor) disturb(FALSE); msgf("Your light is growing faint."); } } static bool item_tester_unsensed(const object_type *o_ptr) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* Check to see if we have identified the item */ if (object_known_p(o_ptr)) return (FALSE); /* Cannot sense flavoured items */ if (k_ptr->flavor) return (FALSE); return (TRUE); } /* * Forcibly pseudo-identify an object in the inventory * (or on the floor) */ bool psychometry(void) { object_type *o_ptr; byte feel; cptr q, s; /* Only un-id'ed items */ item_tester_hook = item_tester_unsensed; /* Get an item */ q = "Meditate on which item? "; s = "You have nothing appropriate."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* Check for a feeling */ feel = value_check_aux1(o_ptr); /* Skip non-feelings */ if (!feel) { msgf("You do not perceive anything unusual about the %v.", OBJECT_FMT(o_ptr, FALSE, 0)); return TRUE; } msgf("You feel that the %v %s %s...", OBJECT_FMT(o_ptr, FALSE, 0), ((o_ptr->number == 1) ? "is" : "are"), game_inscriptions[feel]); /* We have "felt" it */ o_ptr->info |= (OB_SENSE); /* hack the knowledge flag */ if (cursed_p(o_ptr)) o_ptr->kn_flags[2] |= TR2_CURSED; /* "Inscribe" it */ o_ptr->feeling = feel; /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); /* Something happened */ return (TRUE); } /* * If player has inscribed the object with "!!", let him know when it's * recharged. -LM- */ static void recharged_notice(const object_type *o_ptr) { cptr s; /* No inscription */ if (!o_ptr->inscription) return; /* Find a '!' */ s = strchr(quark_str(o_ptr->inscription), '!'); /* Process notification request. */ while (s) { /* Find another '!' */ if (s[1] == '!') { /* Count the item */ if (o_ptr->number == 1) { /* One item has recharged */ msgf("Your %v has recharged.", OBJECT_FMT(o_ptr, FALSE, 0)); } else { object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* Find out how many are recharging still */ int power = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval; /* Watch the top */ if (power > o_ptr->number) power = o_ptr->number; /* All are charged now */ if (power == 0) { msgf("All %d of your %v are charged.", o_ptr->number, OBJECT_FMT(o_ptr, FALSE, 0)); } /* One is charged */ else if (o_ptr->number - power == 1) { msgf("One of your %v is charged.", OBJECT_FMT(o_ptr, FALSE, 0)); } /* Some are charged, some are recharging */ else { msgf("%d of your %v are charged.", o_ptr->number - power, OBJECT_FMT(o_ptr, FALSE, 0)); } } /* Done. */ return; } /* Keep looking for '!'s */ s = strchr(s + 1, '!'); } } /* * Handle certain things once every 10 game turns */ static void process_world(void) { int i; s32b regen_amount; bool cave_no_regen = FALSE; int upkeep_factor = 0; u16b x, y; object_type *o_ptr; int temp; object_kind *k_ptr; cave_type *c_ptr = area(p_ptr->px, p_ptr->py); const mutation_type *mut_ptr; int depth = base_level(); /* Announce the level feeling */ if ((turn - old_turn == 1000) && (p_ptr->depth)) do_cmd_feeling(); /* Every 10 game turns */ if (turn % 10) return; /*** Attempt timed autosave ***/ if (autosave_t && autosave_freq) { if (!(turn % ((s32b)autosave_freq * 10))) do_cmd_save_game(TRUE); } if (p_ptr->state.mon_fight) { msgf("You hear noise."); } /*** Handle the wilderness/town (sunshine) ***/ /* While in town/wilderness */ if (!p_ptr->depth) { /* Hack -- Daybreak/Nighfall in town */ if (!(turn % ((10L * TOWN_DAWN) / 2))) { bool dawn; /* Check for dawn */ dawn = (!(turn % (10L * TOWN_DAWN))); /* Day breaks */ if (dawn) { /* Message */ msgf("The sun has risen."); } else { /* Message */ msgf("The sun has fallen."); } /* Light up or darken the area */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { light_dark_square(x, y, dawn); } } /* Update the monsters */ p_ptr->update |= (PU_MONSTERS | PU_VIEW); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } } /*** Process the monsters ***/ /* Check for creature generation. */ if (one_in_(MAX_M_ALLOC_CHANCE)) { /* Make a new monster */ (void)alloc_monster(MAX_SIGHT + 5, FALSE, 0); } /* Hack -- Check for creature regeneration */ if (!(turn % 100)) regen_monsters(); /*** Damage over Time ***/ /* Take damage from poison */ if (p_ptr->tim.poisoned && !p_ptr->tim.invuln) { /* Take damage */ take_hit(1, "poison"); } /* (Vampires) Take damage from sunlight */ if (FLAG(p_ptr, TR_HURT_LITE)) { if (!p_ptr->depth && !(FLAG(p_ptr, TR_RES_LITE)) && !(FLAG(p_ptr, TR_IM_LITE)) && !p_ptr->tim.invuln && (!((turn / ((10L * TOWN_DAWN) / 2)) % 2))) { if (c_ptr->info & CAVE_GLOW) { /* Take damage */ msgf("The sun's rays scorch your flesh!"); take_hit(1, "sunlight"); cave_no_regen = TRUE; } } o_ptr = &p_ptr->equipment[EQUIP_LITE]; if (o_ptr->tval && (o_ptr->sval >= SV_LITE_GALADRIEL) && (o_ptr->sval < SV_LITE_THRAIN) && !(FLAG(p_ptr, TR_RES_LITE)) && !(FLAG(p_ptr, TR_IM_LITE))) { char ouch[280]; msgf("The %v scorches your undead flesh!", OBJECT_FMT(o_ptr, FALSE, 0)); cave_no_regen = TRUE; strnfmt(ouch, 280, "wielding %v", OBJECT_FMT(o_ptr, TRUE, 0)); if (!p_ptr->tim.invuln) take_hit(1, ouch); } } if ((c_ptr->feat == FEAT_SHAL_LAVA) && !(FLAG(p_ptr, TR_FEATHER))) { int damage = resist(depth / 2 + 1, res_fire_lvl); if (damage) { /* Take damage */ msgf("The lava burns you!"); take_hit(damage, "shallow lava"); cave_no_regen = TRUE; } } else if (c_ptr->feat == FEAT_DEEP_LAVA) { int damage = resist(depth, res_fire_lvl); cptr message; cptr hit_from; if (FLAG(p_ptr, TR_FEATHER)) { damage = damage / 5; message = "The heat burns you!"; hit_from = "flying over deep lava"; } else { message = "The lava burns you!"; hit_from = "deep lava"; } if (damage) { /* Take damage */ msgf(message); take_hit(damage, hit_from); cave_no_regen = TRUE; } } if ((c_ptr->feat == FEAT_SHAL_ACID) && !(FLAG(p_ptr, TR_FEATHER))) { int damage = resist(depth / 2 + 1, res_acid_lvl); if (damage) { /* Take damage */ msgf("The acid burns you!"); take_hit(damage, "shallow acid"); cave_no_regen = TRUE; } } else if (c_ptr->feat == FEAT_DEEP_ACID) { int damage = resist(depth, res_acid_lvl); cptr message; cptr hit_from; if (FLAG(p_ptr, TR_FEATHER)) { damage = damage / 5; message = "The fumes burn you!"; hit_from = "flying over deep acid"; } else { message = "The acid burns you!"; hit_from = "deep acid"; } if (damage) { /* Take damage */ msgf(message); take_hit(damage, hit_from); cave_no_regen = TRUE; } } if ((c_ptr->feat == FEAT_SHAL_SWAMP) && !(FLAG(p_ptr, TR_FEATHER)) && !FLAG(p_ptr, TR_WILD_WALK)) { int damage = resist(depth / 4 + 1, res_pois_lvl); /* Hack - some resistance will save you */ if (damage && damage >= p_ptr->depth / 4) { /* Take damage */ msgf("The plants poison you!"); take_hit(damage, "swamp"); cave_no_regen = TRUE; } } else if ((c_ptr->feat == FEAT_DEEP_SWAMP) && !p_ptr->tim.invuln && !FLAG(p_ptr, TR_WILD_WALK)) { int damage = resist(depth / 2, res_pois_lvl); cptr message; cptr hit_from; if (FLAG(p_ptr, TR_FEATHER)) { damage = damage / 5; message = "The fumes poison you!"; hit_from = "flying over thick swamp"; } else { message = "The fumes poison you!"; hit_from = "thick swamp"; } if (damage) { /* Take damage */ msgf(message); take_hit(damage, hit_from); cave_no_regen = TRUE; } } else if (((c_ptr->feat == FEAT_DEEP_WATER) || (c_ptr->feat == FEAT_OCEAN_WATER)) && !(FLAG(p_ptr, TR_FEATHER))) { if (p_ptr->total_weight > ((adj_str_wgt[p_ptr->stat[A_STR].ind] * 100) / 2)) { /* Take damage */ msgf("You are drowning!"); take_hit(randint1(depth + 1), "drowning"); cave_no_regen = TRUE; } } /* Spectres -- take damage when moving through walls */ /* * Added: ANYBODY takes damage if inside through walls * without wraith form -- NOTE: Spectres will never be * reduced below 0 hp by being inside a stone wall; others * WILL BE! */ if (cave_wall_grid(c_ptr)) { if (!p_ptr->tim.invuln && !p_ptr->tim.wraith_form && ((p_ptr->chp > (depth / 10)) || !(FLAG(p_ptr, TR_PASS_WALL)))) { cptr dam_desc; cave_no_regen = TRUE; if (FLAG(p_ptr, TR_PASS_WALL)) { msgf("Your molecules feel disrupted!"); dam_desc = "density"; } else { msgf("You are being crushed!"); dam_desc = "solid rock"; } take_hit(1 + (depth / 10), dam_desc); } } /* * Fields you are standing on may do something. */ field_script(c_ptr, FIELD_ACT_PLAYER_ON, ""); /* Nightmare mode activates the TY_CURSE at midnight */ if (ironman_nightmare) { s32b len = 10L * TOWN_DAWN; s32b tick = turn % len + len / 4; int hour = (24 * tick / len) % 24; int min = (1440 * tick / len) % 60; int prev_min = (1440 * (tick - 10) / len) % 60; /* Require exact minute */ if (min != prev_min) { /* Every 15 minutes after 11:00 pm */ if ((hour == 23) && !(min % 15)) { /* Disturbing */ disturb(FALSE); switch (min / 15) { case 0: { msgf("You hear a distant bell toll ominously."); break; } case 1: { msgf("A distant bell sounds twice."); break; } case 2: { msgf("A distant bell sounds three times."); break; } case 3: { msgf("A distant bell tolls four times."); break; } } } /* TY_CURSE activates at mignight! */ if (!hour && !min) { int count = 0; disturb(TRUE); msgf ("A distant bell tolls many times, fading into an deathly silence."); (void)activate_ty_curse(FALSE, &count); } } } /* Take damage from cuts */ if (p_ptr->tim.cut && !p_ptr->tim.invuln) { /* Mortal wound or Deep Gash */ if (p_ptr->tim.cut > 200) { i = 3; } /* Severe cut */ else if (p_ptr->tim.cut > 100) { i = 2; } /* Other cuts */ else { i = 1; } /* Take damage */ take_hit(i, "a fatal wound"); } /*** Check the Food, and Regenerate ***/ /* Digest normally */ if (p_ptr->food < PY_FOOD_MAX) { /* Every 100 game turns */ if (!(turn % 100)) { /* Basic digestion rate based on speed */ if (p_ptr->pspeed > 199) i = 49; else if (p_ptr->pspeed < 0) i = 1; else i = extract_energy[p_ptr->pspeed]; i *= 2; /* Regeneration takes more food */ if (FLAG(p_ptr, TR_REGEN)) i += 30; /* Some specific mutations increase food requirement */ if (p_ptr->muta3 & (MUT3_RESILIENT)) i += 20; if (p_ptr->muta1 & (MUT1_EAT_ROCK)) i += 20; /* Slow digestion takes less food */ if (FLAG(p_ptr, TR_SLOW_DIGEST)) i -= 10; /* Slow healing gives some benefit... */ if (FLAG(p_ptr, TR_SLOW_HEAL)) i -= 5; /* Wasting disease - almost no digestion */ if (p_ptr->muta2 & MUT2_WASTING) i -= 50; /* Minimal digestion */ if (i < 1) i = 1; /* Digest some food */ (void)set_food(p_ptr->food - i); } } /* Digest quickly when gorged */ else { /* Digest a lot of food */ (void)set_food(p_ptr->food - 100); } /* Starve to death (slowly) */ if (p_ptr->food < PY_FOOD_STARVE) { /* Calculate damage */ i = (PY_FOOD_STARVE - p_ptr->food) / 10; /* Take damage */ if (!p_ptr->tim.invuln) take_hit(i, "starvation"); } /* Default regeneration */ regen_amount = PY_REGEN_NORMAL; /* Getting Weak */ if (p_ptr->food < PY_FOOD_WEAK) { /* Lower regeneration */ if (p_ptr->food < PY_FOOD_STARVE) { regen_amount = 0; } else if (p_ptr->food < PY_FOOD_FAINT) { regen_amount = PY_REGEN_FAINT; } else { regen_amount = PY_REGEN_WEAK; } /* Getting Faint */ if (p_ptr->food < PY_FOOD_FAINT) { /* Faint occasionally */ if (!p_ptr->tim.paralyzed && (randint0(100) < 10)) { /* Message */ msgf("You faint from the lack of food."); disturb(TRUE); /* Hack -- faint (bypass free action) */ (void)inc_paralyzed(randint1(5)); } } } /* Are we walking the pattern? */ if (pattern_effect()) { cave_no_regen = TRUE; } else { /* Regeneration ability */ if (FLAG(p_ptr, TR_REGEN)) { regen_amount = regen_amount * 2; } if (FLAG(p_ptr, TR_SLOW_HEAL)) { regen_amount = regen_amount / 4; } } /* Searching or Resting */ if (p_ptr->state.searching || p_ptr->state.resting) { regen_amount = regen_amount * 2; } if (total_friends) { if (total_friends > 1 + (p_ptr->lev / cp_ptr->pet_upkeep_div)) { upkeep_factor = total_friend_levels; /* Bounds checking */ if (upkeep_factor > 95) upkeep_factor = 95; if (upkeep_factor < 5) upkeep_factor = 5; } } /* Regenerate the mana */ if (p_ptr->csp < p_ptr->msp) { if (upkeep_factor) { s16b upkeep_regen = (((100 - upkeep_factor) * regen_amount) / 100); regenmana(upkeep_regen); } else { regenmana(regen_amount); } } /* Poisoned or cut yields no healing */ if (p_ptr->tim.poisoned) regen_amount = 0; if (p_ptr->tim.cut) regen_amount = 0; /* Special floor -- Pattern, in a wall -- yields no healing */ if (cave_no_regen) regen_amount = 0; /* Regenerate Hit Points */ if (p_ptr->chp < p_ptr->mhp) { regenhp(regen_amount); } /*** Timeout Various Things ***/ if (p_ptr->tim.image) (void)inc_image(-1); if (p_ptr->tim.blind) (void)inc_blind(-1); if (p_ptr->tim.invis) (void)inc_tim_invis(-1); if (p_ptr->tim.esp) (void)inc_tim_esp(-1); if (p_ptr->tim.infra) (void)inc_tim_infra(-1); if (p_ptr->tim.paralyzed) (void)inc_paralyzed(-1); if (p_ptr->tim.confused) (void)inc_confused(-1); if (p_ptr->tim.afraid) (void)inc_afraid(-1); if (p_ptr->tim.fast) (void)inc_fast(-1); if (p_ptr->tim.slow) (void)inc_slow(-1); if (p_ptr->tim.protevil) (void)inc_protevil(-1); if (p_ptr->tim.invuln) (void)inc_invuln(-1); if (p_ptr->tim.wraith_form) (void)inc_wraith_form(-1); if (p_ptr->tim.hero) (void)inc_hero(-1); if (p_ptr->tim.shero) (void)inc_shero(-1); if (p_ptr->tim.blessed) (void)inc_blessed(-1); if (p_ptr->tim.shield) (void)inc_shield(-1); if (p_ptr->tim.oppose_acid) (void)inc_oppose_acid(-1); if (p_ptr->tim.oppose_elec) (void)inc_oppose_elec(-1); if (p_ptr->tim.oppose_fire) (void)inc_oppose_fire(-1); if (p_ptr->tim.oppose_cold) (void)inc_oppose_cold(-1); if (p_ptr->tim.oppose_pois) (void)inc_oppose_pois(-1); /*** Poison and Stun and Cut ***/ /* Poison */ if (p_ptr->tim.poisoned) { int adjust = adj_con_fix[p_ptr->stat[A_CON].ind] + 1; /* Apply some healing */ (void)inc_poisoned(-adjust); } /* Stun */ if (p_ptr->tim.stun) { int adjust = adj_con_fix[p_ptr->stat[A_CON].ind] + 1; /* Apply some healing */ (void)inc_stun(-adjust); } /* Cut */ if (p_ptr->tim.cut) { int adjust = adj_con_fix[p_ptr->stat[A_CON].ind] + 1; if (FLAG(p_ptr, TR_SLOW_HEAL)) adjust /= 2; /* Hack -- Truly "mortal" wound */ if (p_ptr->tim.cut > 1000) adjust = 0; /* Apply some healing */ (void)inc_cut(-adjust); } /*** Process mutation effects ***/ for (i = MUT_PER_SET; i < MUT_PER_SET * 2; i++) { mut_ptr = &mutations[i]; /* * Do we have this mutation and * is it truly a randomly activating one? */ if (player_has_mut(i) && (mut_ptr->chance > 0)) { mutation_random_aux(mut_ptr); } } /*** Process Inventory ***/ /* Handle experience draining */ if (FLAG(p_ptr, TR_DRAIN_EXP)) { if ((randint0(100) < 10) && (p_ptr->exp > 0)) { p_ptr->exp--; p_ptr->max_exp--; check_experience(); } } /* Handle stat draining */ if (FLAG(p_ptr, TR_DRAIN_STATS)) { if (one_in_(250)) { int stat = randint0(6); if (p_ptr->stat[stat].cur > 30) { p_ptr->stat[stat].cur--; p_ptr->update |= (PU_BONUS); } } } /* Process equipment */ for (i = 0; i < EQUIP_MAX; i++) { /* Get the object */ o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Apply extra scripts */ apply_object_trigger(TRIGGER_TIMED, o_ptr, ""); /* TY Curse */ if ((FLAG(o_ptr, TR_TY_CURSE)) && one_in_(TY_CURSE_CHANCE)) { int count = 0; (void)activate_ty_curse(FALSE, &count); } /* Auto-curse */ if ((FLAG(o_ptr, TR_AUTO_CURSE)) && !(FLAG(o_ptr, TR_CURSED)) && one_in_(1000)) { msgf("There is a malignant black aura surrounding you..."); SET_FLAG(o_ptr, TR_CURSED); o_ptr->feeling = FEEL_NONE; } /* * Hack: Uncursed teleporting items (e.g. Trump Weapons) * can actually be useful! */ if ((FLAG(o_ptr, TR_TELEPORT)) && one_in_(100)) { if (cursed_p(o_ptr) && !(FLAG(p_ptr, TR_NO_TELE))) { disturb(FALSE); /* Teleport player */ teleport_player(40); } else { if (!disturb_other || (o_ptr->inscription && (strchr (quark_str(o_ptr->inscription), '.')))) { /* Do nothing */ /* msgf("Teleport aborted.") */ ; } else if (get_check("Teleport? ")) { disturb(FALSE); teleport_player(50); } } } /* Recharge activatable objects */ if (o_ptr->timeout > 0) { /* Lights are special */ if (o_ptr->tval == TV_LITE) { /* Artifact lights decrease timeout */ if (FLAG(o_ptr, TR_INSTA_ART)) { /* Recharge */ o_ptr->timeout--; if (!o_ptr->timeout) { recharged_notice(o_ptr); /* Notice changes */ notice_equip(); } } else if (!(FLAG(o_ptr, TR_LITE))) { /* Normal lights that are not everburning */ o_ptr->timeout--; /* Notice interesting fuel steps */ notice_lite_change(o_ptr); } } /* Notice changes */ else { /* Recharge */ o_ptr->timeout--; if (!o_ptr->timeout) { recharged_notice(o_ptr); /* Notice changes */ notice_equip(); } } } } /* * Recharge rods. Rods now use timeout to control charging status, * and each charging rod in a stack decreases the stack's timeout by * one per turn. -LM- */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { k_ptr = &k_info[o_ptr->k_idx]; /* Must have a timeout */ if (!o_ptr->timeout) continue; /* Examine all charging rods or stacks of charging rods. */ if (o_ptr->tval == TV_ROD) { /* Determine how many rods are charging. */ temp = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval; if (temp > o_ptr->number) temp = o_ptr->number; /* Decrease timeout by that number. */ o_ptr->timeout -= temp; /* Boundary control. */ if (o_ptr->timeout < 0) o_ptr->timeout = 0; /* Notice changes, provide message if object is inscribed. */ if (temp > (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval) { recharged_notice(o_ptr); /* Notice changes */ notice_inven(); } } } OBJ_ITT_END; /* Feel the inventory */ sense_inventory(); /*** Process Objects ***/ /* Process objects */ for (i = 1; i < o_max; i++) { /* Access object */ o_ptr = &o_list[i]; /* Skip dead objects */ if (!o_ptr->k_idx) continue; /* Exit if not in dungeon */ if (!(o_ptr->ix || o_ptr->iy)) continue; field_script(area(o_ptr->ix, o_ptr->iy), FIELD_ACT_OBJECT_ON, "p", LUA_OBJECT(o_ptr)); if (!o_ptr->timeout) continue; /* Recharge rods on the ground. No messages. */ if (o_ptr->tval == TV_ROD) { /* Charge it */ o_ptr->timeout -= o_ptr->number; /* Boundary control. */ if (o_ptr->timeout < 0) o_ptr->timeout = 0; } } /* * Cycle ultra-quick R"bool"G to prevent periodic patterns * in the illumination in a forest after dark. */ quick_rand_add(); /*** Involuntary Movement ***/ /* Delayed Word-of-Recall */ if (p_ptr->tim.word_recall) { /* * HACK: Autosave BEFORE resetting the recall counter (rr9) * The player is yanked up/down as soon as * he loads the autosaved game. */ if (autosave_l && (p_ptr->tim.word_recall == 1)) do_cmd_save_game(TRUE); /* Count down towards recall */ p_ptr->tim.word_recall--; p_ptr->redraw |= (PR_STATUS); /* Hack - no recalling in the middle of the wilderness */ if ((!p_ptr->depth) && (!p_ptr->place_num)) return; /* Activate the recall */ if (!p_ptr->tim.word_recall) { /* Disturbing! */ disturb(FALSE); /* Leaving */ p_ptr->state.leaving = TRUE; /* Determine the level */ if (p_ptr->depth) { msgf("You feel yourself yanked upwards!"); p_ptr->depth = 0; } else { dun_type *d_ptr = dungeon(); msgf("You feel yourself yanked downwards!"); /* Not lower than bottom of dungeon */ p_ptr->depth = d_ptr->recall_depth; /* Go down at least to the start of the dungeon */ if (p_ptr->depth < d_ptr->min_level) { p_ptr->depth = d_ptr->min_level; } /* Nightmare mode makes recall more dangerous */ if (ironman_nightmare && one_in_(666)) { if (p_ptr->depth < 50) { p_ptr->depth *= 2; } else if (p_ptr->depth < 99) { p_ptr->depth = (p_ptr->depth + 99) / 2; } else if (p_ptr->depth > 100) { p_ptr->depth = MAX_DEPTH - 1; } if (p_ptr->depth > d_ptr->max_level) { p_ptr->depth = d_ptr->max_level; } } } /* Sound */ sound(SOUND_TPLEVEL); } } } /* * Verify use of "wizard" mode */ static bool enter_wizard_mode(void) { /* Ask first time */ #if 0 if (!(p_ptr->state.noscore & 0x0002)) #else if (!p_ptr->state.noscore) #endif { /* Mention effects */ msgf("Wizard mode is for debugging and experimenting."); msgf("The game will not be scored if you enter wizard mode."); message_flush(); /* Verify request */ if (!get_check("Are you sure you want to enter wizard mode? ")) { return (FALSE); } /* Mark savefile */ p_ptr->state.noscore |= 0x0002; } /* Success */ return (TRUE); } #ifdef ALLOW_WIZARD /* * Verify use of "debug" commands */ static bool enter_debug_mode(void) { /* Ask first time */ #if 0 if (!(p_ptr->state.noscore & 0x0008)) #else if (!p_ptr->state.noscore) #endif { /* Mention effects */ msgf("The debug commands are for debugging and experimenting."); msgf("The game will not be scored if you use debug commands."); message_flush(); /* Verify request */ if (!get_check("Are you sure you want to use debug commands? ")) { return (FALSE); } /* Mark savefile */ p_ptr->state.noscore |= 0x0008; } /* Success */ return (TRUE); } /* * Hack -- Declare the Debug Routines */ extern void do_cmd_debug(void); #endif /* ALLOW_WIZARD */ #ifdef ALLOW_BORG /* * Verify use of "borg" commands */ static bool enter_borg_mode(void) { /* Ask first time */ if (!(p_ptr->state.noscore & 0x0040)) { /* Mention effects */ msgf("The borg commands are for debugging and experimenting."); msgf("The game will not be scored if you use borg commands."); message_flush(); /* Verify request */ if (!get_check("Are you sure you want to use borg commands? ")) { return (FALSE); } /* Mark savefile */ p_ptr->state.noscore |= 0x0040; } /* Success */ return (TRUE); } #endif /* ALLOW_BORG */ /* * Parse and execute the current command * Give "Warning" on illegal commands. * * XXX XXX XXX Make some "blocks" */ static void process_command(void) { /* Handle repeating the last command */ repeat_check(); /* Parse the command */ switch (p_ptr->cmd.cmd) { case ESCAPE: case ' ': { /* Ignore */ break; } case '\r': { /* Ignore return */ break; } /*** Wizard Commands ***/ case KTRL('W'): { /* Toggle Wizard Mode */ if (p_ptr->state.wizard) { p_ptr->state.wizard = FALSE; msgf("Wizard mode off."); } else if (enter_wizard_mode()) { p_ptr->state.wizard = TRUE; msgf("Wizard mode on."); } /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw "title" */ p_ptr->redraw |= (PR_TITLE); break; } #ifdef ALLOW_WIZARD case KTRL('A'): { /* Enter debug mode */ if (enter_debug_mode()) { do_cmd_debug(); } break; } #endif /* ALLOW_WIZARD */ #ifdef ALLOW_BORG case KTRL('Z'): { /* Enter borg mode */ if (enter_borg_mode()) { do_cmd_borg(); } break; } #endif /* ALLOW_BORG */ /*** Inventory Commands ***/ case 'w': { /* Wear/wield equipment */ do_cmd_wield(); break; } case 't': { /* Take off equipment */ do_cmd_takeoff(); break; } case 'd': { /* Drop an item */ do_cmd_drop(); break; } case 'k': { /* Destroy an item */ do_cmd_destroy(); break; } case 'e': { /* Equipment list */ do_cmd_equip(); break; } case 'i': { /* Inventory list */ do_cmd_inven(); break; } /*** Various commands ***/ case 'I': { /* Identify an object */ do_cmd_observe(); break; } case KTRL('I'): { /* Hack -- toggle windows */ toggle_inven_equip(); break; } /*** Standard "Movement" Commands ***/ case '+': { /* Alter a grid */ do_cmd_alter(); break; } case 'T': { /* Dig a tunnel */ do_cmd_tunnel(); break; } case ';': { /* Move (usually pick up things) */ do_cmd_walk(FALSE); break; } case '-': { /* Move (usually do not pick up) */ do_cmd_walk(TRUE); break; } /*** Running, Resting, Searching, Staying */ case '.': { /* Begin Running -- Arg is Max Distance */ do_cmd_run(); break; } case ',': { /* Stay still (usually pick things up) */ do_cmd_stay(always_pickup); break; } case 'g': { /* Stay still (usually do not pick up) */ do_cmd_stay(!always_pickup); break; } case 'R': { /* Rest -- Arg is time */ do_cmd_rest(); break; } case 's': { /* Search for traps/doors */ do_cmd_search(); break; } case 'S': { /* Toggle search mode */ do_cmd_toggle_search(); break; } /*** Stairs and Doors and Chests and Traps ***/ case '<': { /* Go up staircase */ do_cmd_go_up(); break; } case '>': { /* Go down staircase */ do_cmd_go_down(); break; } case 'o': { /* Open a door or chest */ do_cmd_open(); break; } case 'c': { /* Close a door */ do_cmd_close(); break; } case 'j': { /* Jam a door with spikes */ do_cmd_spike(); break; } case 'D': { /* Disarm a trap or chest */ do_cmd_disarm(); break; } /*** Magic and Prayers ***/ case 'G': { /* Gain new spells/prayers */ do_cmd_study(); break; } case 'b': { /* Browse a book */ do_cmd_browse(); break; } case 'm': { /* Cast a spell */ if (FLAG(p_ptr, TR_NO_MAGIC)) { cptr which_power = "magic"; if (p_ptr->rp.pclass == CLASS_MINDCRAFTER) which_power = "psionic powers"; else if (mp_ptr->spell_book == TV_LIFE_BOOK) which_power = "prayer"; msgf("An anti-magic shell disrupts your %s!", which_power); p_ptr->state.energy_use = 0; } else { if (p_ptr->rp.pclass == CLASS_MINDCRAFTER) do_cmd_mindcraft(); else do_cmd_cast(); } break; } case 'p': { /* Issue a pet command */ do_cmd_pet(); break; } /*** Use various objects ***/ case '{': { /* Inscribe an object */ do_cmd_inscribe(); break; } case '}': { /* Uninscribe an object */ do_cmd_uninscribe(); break; } case 'A': { /* Activate an artifact */ do_cmd_activate(); break; } case 'E': { /* Eat some food */ do_cmd_eat_food(); break; } case 'F': { /* Fuel your lantern/torch */ do_cmd_refill(); break; } case 'f': { /* Fire an item */ do_cmd_fire(); break; } case 'v': { /* Throw an item */ do_cmd_throw(); break; } case 'a': { /* Aim a wand */ do_cmd_aim_wand(); break; } case 'z': { /* Zap a rod */ do_cmd_zap_rod(); break; } case 'q': { /* Quaff a potion */ do_cmd_quaff_potion(); break; } case 'r': { /* Read a scroll */ do_cmd_read_scroll(); break; } case 'u': { /* Use a staff */ do_cmd_use_staff(); break; } case 'U': { /* Use racial power */ do_cmd_racial_power(); break; } /*** Looking at Things (nearby or on map) ***/ case 'M': { /* Full dungeon map */ do_cmd_view_map(); break; } case 'L': { /* Locate player on map */ do_cmd_locate(); break; } case 'l': { /* Look around */ do_cmd_look(); break; } case '*': { /* Target monster or location */ do_cmd_target(); break; } /*** Help and Such ***/ case '?': { /* Help */ do_cmd_help(); break; } case '/': { /* Identify symbol */ do_cmd_query_symbol(); break; } case 'C': { /* Character description */ do_cmd_character(); break; } /*** System Commands ***/ case '!': { /* Hack -- User interface */ (void)Term_user(0); break; } case '"': { /* Single line from a pref file */ do_cmd_pref(); break; } case '@': { /* Interact with macros */ do_cmd_macros(); break; } case '%': { /* Interact with visuals */ do_cmd_visuals(); break; } case '&': { /* Interact with colors */ do_cmd_colors(); break; } case '=': { /* Interact with options */ do_cmd_options(OPT_FLAG_SERVER | OPT_FLAG_PLAYER); do_cmd_redraw(); break; } /*** Misc Commands ***/ case ':': { /* Take notes */ do_cmd_note(); break; } case 'V': { /* Version info */ do_cmd_version(); break; } case KTRL('F'): { /* Repeat level feeling */ do_cmd_feeling(); break; } case KTRL('P'): { /* Show previous messages */ do_cmd_messages(); break; } case KTRL('Q'): { /* Show quest status -KMW- */ do_cmd_checkquest(); break; } case KTRL('R'): { /* Redraw the screen */ do_cmd_redraw(); break; } case KTRL('S'): { /* Hack -- Save and don't quit */ do_cmd_save_game(FALSE); break; } case KTRL('T'): { /* Get the time of day */ do_cmd_time(); break; } case KTRL('X'): { /* Save and quit */ do_cmd_save_and_exit(); break; } case 'Q': { /* Quit (commit suicide) */ do_cmd_suicide(); break; } case '~': case '|': { /* Check artifacts, uniques, objects, quests etc. */ do_cmd_knowledge(); break; } case '(': { /* Load "screen dump" */ do_cmd_load_screen(); break; } case ')': { /* Save "screen dump" */ do_cmd_save_screen(); break; } default: { /* Hack -- Unknown command */ if (one_in_(2)) { char error_m[1024]; sound(SOUND_ILLEGAL); if (!get_rnd_line("error.txt", 0, error_m)) msgf(error_m); } else prtf(0, 0, "Type '?' for help."); break; } } } /* * Process the player * * Notice the annoying code to handle "pack overflow", which * must come first just in case somebody manages to corrupt * the savefiles by clever use of menu commands or something. */ static void process_player(void) { /*** Check for interupts ***/ /* Complete resting */ if (p_ptr->state.resting < 0) { /* Basic resting */ if (p_ptr->state.resting == -1) { /* Stop resting */ if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp >= p_ptr->msp)) { disturb(FALSE); } } /* Complete resting */ else if (p_ptr->state.resting == -2) { /* Stop resting */ if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp == p_ptr->msp) && !p_ptr->tim.blind && !p_ptr->tim.confused && !p_ptr->tim.poisoned && !p_ptr->tim.afraid && !p_ptr->tim.stun && !p_ptr->tim.cut && !p_ptr->tim.slow && !p_ptr->tim.paralyzed && !p_ptr->tim.image && !p_ptr->tim.word_recall) { disturb(FALSE); } } } /*** Handle "abort" ***/ /* Check for "player abort" */ if (p_ptr->state.running || p_ptr->cmd.rep || p_ptr->state.resting) { /* Do not wait */ p_ptr->cmd.inkey_scan = TRUE; /* Check for a key */ if (inkey()) { /* Flush input */ flush(); /* Disturb */ disturb(FALSE); /* Hack -- Show a Message */ msgf("Cancelled."); } } /*** Handle actual user input ***/ /* Repeat until energy is reduced */ while (TRUE) { /* Notice stuff */ notice_stuff(); /* Update */ handle_stuff(); /* Place the cursor on the player */ move_cursor_relative(p_ptr->px, p_ptr->py); /* Refresh (optional) */ if (fresh_before) Term_fresh(); /* Hack -- Pack Overflow */ if (get_list_length(p_ptr->inventory) > INVEN_PACK) { int i = 0; object_type *o_ptr; /* Scan pack */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Count items */ i++; /* Does item need to be dropped? */ if (i > INVEN_PACK) { /* Disturbing */ disturb(FALSE); /* Warning */ msgf("Your pack overflows!"); /* Drop the excess item(s) */ inven_drop(o_ptr, o_ptr->number); } } OBJ_ITT_END; /* Notice stuff */ notice_stuff(); /* Update */ handle_stuff(); } /* Assume free turn */ p_ptr->state.energy_use = 0; /* Paralyzed or Knocked Out */ if (p_ptr->tim.paralyzed || (p_ptr->tim.stun >= 100)) { /* Take a turn */ p_ptr->state.energy_use = 100; } /* Resting */ else if (p_ptr->state.resting) { /* Timed rest */ if (p_ptr->state.resting > 0) { /* Reduce rest count */ p_ptr->state.resting--; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); } /* Take a turn */ p_ptr->state.energy_use = 100; } /* Running */ else if (p_ptr->state.running) { /* Take a step */ run_step(0); } /* Repeated command */ else if (p_ptr->cmd.rep) { /* Count this execution */ p_ptr->cmd.rep--; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Redraw stuff */ redraw_stuff(); /* Hack -- Assume messages were seen */ msg_flag = FALSE; /* Clear the top line */ clear_msg(); /* Process the command */ process_command(); } /* Normal command */ else { /* Place the cursor on the player */ move_cursor_relative(p_ptr->px, p_ptr->py); /* Get a command (normal) */ request_command(FALSE); /* Process the command */ process_command(); } /*** Clean up ***/ /* Significant */ if (p_ptr->state.energy_use) { /* Use some energy */ p_ptr->energy -= p_ptr->state.energy_use; /* Change stuff */ change_stuff(); /* Hack -- constant hallucination */ if (p_ptr->tim.image) p_ptr->redraw |= (PR_MAP); } /* Hack -- notice death */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Handle "leaving" */ if (p_ptr->state.leaving) { /* Hack - save game if asked */ if (autosave_l) do_cmd_save_game(TRUE); break; } /* Used up energy for this turn */ if (p_ptr->state.energy_use) break; } } /* * Add energy to player and monsters. * Those with the most energy move first. * (This prevents monsters like Morgoth getting double moves * when he is at a lower speed than the player.) */ static void process_energy(void) { int i, speed, e; monster_type *m_ptr; /*** Apply energy to player ***/ if (p_ptr->pspeed > 199) i = 49; else if (p_ptr->pspeed < 0) i = 1; else i = extract_energy[p_ptr->pspeed]; p_ptr->energy += i; /* Give energy to all monsters */ for (i = m_max - 1; i >= 1; i--) { /* Access the monster */ m_ptr = &m_list[i]; /* Ignore "dead" monsters */ if (!m_ptr->r_idx) continue; speed = m_ptr->mspeed; /* Monsters move quickly in Nightmare mode */ if (ironman_nightmare) { speed = MIN(199, m_ptr->mspeed + 5); } e = extract_energy[speed]; /* Give this monster some energy */ m_ptr->energy += e; } /* Can the player move? */ while (p_ptr->energy >= 100 && !p_ptr->state.leaving) { /* process monster with even more energy first */ process_monsters(p_ptr->energy + 1); /* Process the player while still alive */ if (!p_ptr->state.leaving) { process_player(); } } /* Process the fields */ process_fields(); } /* * Interact with the current dungeon level. * * This function will not exit until the level is completed, * the user dies, or the game is terminated. */ static void evolve_dungeon(void) { cave_type *c_ptr; /* Not leaving */ p_ptr->state.leaving = FALSE; /* Reset the "command" vars */ p_ptr->cmd.cmd = 0; p_ptr->cmd.new = 0; p_ptr->cmd.rep = 0; p_ptr->cmd.arg = 0; p_ptr->cmd.dir = 0; /* Cancel the target */ p_ptr->target_who = 0; /* Cancel the health bar */ health_track(0); /* Check visual effects. Should this be here? */ p_ptr->change |= (PC_SHIMMER | PC_REPAIR); /* Disturb */ disturb(TRUE); /* Track maximum player level */ if (p_ptr->max_lev < p_ptr->lev) { p_ptr->max_lev = p_ptr->lev; } /* No stairs down from final quests */ if (is_special_level(p_ptr->depth)) { p_ptr->state.create_down_stair = FALSE; } /* Paranoia -- no stairs from town or wilderness */ if (!p_ptr->depth) p_ptr->state.create_down_stair = p_ptr->state.create_up_stair = FALSE; /* Option -- no connected stairs */ if (!dungeon_stair) p_ptr->state.create_down_stair = p_ptr->state.create_up_stair = FALSE; /* Nightmare mode is no fun... */ if (ironman_nightmare) p_ptr->state.create_down_stair = p_ptr->state.create_up_stair = FALSE; /* Option -- no up stairs */ if (ironman_downward) p_ptr->state.create_down_stair = p_ptr->state.create_up_stair = FALSE; /* Make a stairway. */ if (p_ptr->state.create_up_stair || p_ptr->state.create_down_stair) { /* Place a stairway */ c_ptr = area(p_ptr->px, p_ptr->py); if (cave_valid_grid(c_ptr)) { /* XXX XXX XXX */ delete_object(p_ptr->px, p_ptr->py); /* Make stairs */ if (p_ptr->state.create_down_stair) { cave_set_feat(p_ptr->px, p_ptr->py, FEAT_MORE); } else { cave_set_feat(p_ptr->px, p_ptr->py, FEAT_LESS); } } /* Cancel the stair request */ p_ptr->state.create_down_stair = p_ptr->state.create_up_stair = FALSE; } /* Center the panel on the player */ panel_center(p_ptr->px, p_ptr->py); /* Flush messages */ message_flush(); /* Enter "xtra" mode */ character_xtra = TRUE; /* Window stuff */ p_ptr->window |= (PW_SPELL | PW_PLAYER); /* Window stuff */ p_ptr->window |= (PW_MONSTER | PW_MESSAGE | PW_VISIBLE); /* Redraw dungeon */ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_EQUIPPY); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Update stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Calculate torch radius */ p_ptr->update |= (PU_TORCH); /* Update */ handle_stuff(); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE); /* Update */ handle_stuff(); /* Leave "xtra" mode */ character_xtra = FALSE; /* Update stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Notice changes */ notice_item(); /* Notice stuff */ notice_stuff(); /* Update */ handle_stuff(); /* Refresh */ Term_fresh(); /* Hack -- notice death or departure */ if (!p_ptr->state.playing || p_ptr->state.is_dead) return; /* Print quest message if appropriate */ quest_discovery(); /*** Process this dungeon level ***/ /* Main loop */ while (TRUE) { /* Hack -- Compact the monster list occasionally */ if (m_cnt + 32 > z_info->m_max) compact_monsters(64); /* Hack -- Compress the monster list occasionally */ if (m_cnt + 32 < m_max) compact_monsters(0); /* Hack -- Compact the object list occasionally */ if (o_cnt + 32 > z_info->o_max) compact_objects(64); /* Hack -- Compress the object list occasionally */ if (o_cnt + 32 < o_max) compact_objects(0); /* Hack -- Compact the field list occasionally */ if (fld_cnt + 32 > z_info->fld_max) compact_fields(64); /* Hack -- Compress the field list occasionally */ if (fld_cnt + 32 < fld_max) compact_fields(0); /* * Add energy to player and monsters. * Those with the most energy move first. */ process_energy(); /* Notice */ notice_stuff(); /* Update */ handle_stuff(); /* Hack -- Hilite the player */ move_cursor_relative(p_ptr->px, p_ptr->py); /* Optional fresh */ if (fresh_after) Term_fresh(); /* Hack -- Notice death or departure */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Process all of the monsters */ process_monsters(100); /* Reset monsters */ reset_monsters(); /* Notice */ notice_stuff(); /* Update */ handle_stuff(); /* Hack -- Hilite the player */ move_cursor_relative(p_ptr->px, p_ptr->py); /* Optional fresh */ if (fresh_after) Term_fresh(); /* Hack -- Notice death or departure */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Handle "leaving" */ if (p_ptr->state.leaving) break; /* Process the world */ process_world(); /* Hack -- Notice death or departure */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Handle "leaving" */ if (p_ptr->state.leaving) break; /* Notice */ notice_stuff(); /* Update */ handle_stuff(); /* Hack -- Hilite the player */ move_cursor_relative(p_ptr->px, p_ptr->py); /* Optional fresh */ if (fresh_after) Term_fresh(); /* Hack -- Notice death or departure */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Handle "leaving" */ if (p_ptr->state.leaving) break; /* Count game turns */ turn++; } /* The dungeon is not ready */ character_dungeon = FALSE; } /* * Load some "user pref files" * * Modified by Arcum Dagsson to support * separate macro files for different realms. */ static void load_all_pref_files(void) { /* Process global pref file */ (void)process_pref_file("player.prf"); /* Process race pref file */ (void)process_pref_file("%s.prf", rp_ptr->title); /* Process class pref file */ (void)process_pref_file("%s.prf", cp_ptr->title); /* Process character file */ (void)process_pref_file("%s.prf", player_base); /* Access the "realm 1" pref file */ if (p_ptr->spell.r[0].realm != REALM_NONE) { /* Process that file */ (void)process_pref_file("%s.prf", realm_names[p_ptr->spell.r[0].realm]); } /* Access the "realm 2" pref file */ if (p_ptr->spell.r[1].realm != REALM_NONE) { /* Process that file */ (void)process_pref_file("%s.prf", realm_names[p_ptr->spell.r[1].realm]); } } /* * Actually play a game * * If the "new_game" parameter is true, then, after loading the * savefile, we will commit suicide, if necessary, to allow the * player to start a new game. */ void play_game(bool new_game) { int i; /* Verify main term */ if (!angband_term[0]) { quit("main window does not exist"); } /* Make sure main term is active */ Term_activate(angband_term[0]); if (!angband_term[0]) quit("Main term does not exist!"); /* Hack -- Character is "icky" */ screen_save(); /* Initialise the resize hooks */ angband_term[0]->resize_hook = resize_map; for (i = 1; i < 8; i++) { /* Does the term exist? */ if (angband_term[i]) { /* Add the redraw on resize hook */ angband_term[i]->resize_hook = redraw_window; } } /* Verify minimum size */ if ((Term->hgt < 24) || (Term->wid < 80)) { quit("main window is too small"); } /* Hack -- turn off the cursor */ (void)Term_set_cursor(0); /* * Initialize wilderness info * This needs to be done before old savefiles are loaded. */ if (init_w_info()) quit("Cannot initialize wilderness"); /* Initialize field info */ if (init_t_info()) quit("Cannot initialize fields"); /* Attempt to load */ if (!load_player()) { /* Oops */ quit("broken savefile"); } /* Nothing loaded */ if (!character_loaded) { /* Make new player */ new_game = TRUE; /* The dungeon is not ready */ character_dungeon = FALSE; } /* Hack -- Default base_name */ if (!player_base[0]) { strcpy(player_base, "PLAYER"); } /* Init the RNG */ if (Rand_quick || new_game) { u32b seed; /* Basic seed */ seed = (time(NULL)); #ifdef SET_UID /* Mutate the seed on Unix machines */ seed = ((seed >> 3) * (getpid() * 2)); #endif /* Use the complex RNG */ Rand_quick = FALSE; /* Seed the "complex" RNG */ Rand_state_init(seed); } /* Set or clear "rogue_like_commands" if requested */ if (arg_force_original) rogue_like_commands = FALSE; if (arg_force_roguelike) rogue_like_commands = TRUE; /* Roll new character */ if (new_game) { /* Initialize the panel bounds to prevent a crash (rr9) */ verify_panel(); /* Wipe everything */ wipe_all_list(); /* Roll up a new character */ player_birth(); /* Hack -- enter the world */ if ((p_ptr->rp.prace == RACE_VAMPIRE) || (p_ptr->rp.prace == RACE_SKELETON) || (p_ptr->rp.prace == RACE_ZOMBIE) || (p_ptr->rp.prace == RACE_SPECTRE) || (p_ptr->rp.prace == RACE_GHOUL)) { /* Undead start just after midnight */ turn = (30L * TOWN_DAWN) / 4 + 1; } else { turn = 1; } /* Create a new wilderness for the player */ create_wilderness(); /* The dungeon is ready */ character_dungeon = TRUE; /* Hack -- seed for flavors */ seed_flavor = randint0(0x10000000); } /* Reset the visual mappings */ reset_visuals(); /* Normal machine (process player name) */ if (savefile[0]) { process_player_name(FALSE); } /* Weird machine (process player name, pick savefile name) */ else { process_player_name(TRUE); } /* Hack - if note file exists, load it */ if (!new_game && take_notes) { add_note_type(NOTE_ENTER_DUNGEON); } /* Flash a message */ prtf(0, 0, "Please wait..."); /* Flush the message */ Term_fresh(); /* Hack -- Enter wizard mode */ if (arg_wizard && enter_wizard_mode()) p_ptr->state.wizard = TRUE; /* Flavor the objects */ flavor_init(); /* Load the "pref" files */ load_all_pref_files(); /* * Set or clear "rogue_like_commands" if requested * (Do it again, because of loading the pref files * can stomp on the options.) */ if (arg_force_original) rogue_like_commands = FALSE; if (arg_force_roguelike) rogue_like_commands = TRUE; /* Generate a dungeon level if needed */ if (!character_dungeon) generate_cave(); /* Character is now "complete" */ character_generated = TRUE; /* Hack -- Character is no longer "icky" */ screen_load(); /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Start game */ p_ptr->state.playing = TRUE; /* Hack -- Enforce "delayed death" */ if (p_ptr->chp < 0) p_ptr->state.is_dead = TRUE; /* Enter "xtra" mode */ character_xtra = TRUE; /* Resize / init the map */ p_ptr->update |= (PU_MAP); /* Need to recalculate some transient things */ p_ptr->update |= (PU_BONUS | PU_SPELLS | PU_WEIGHT); /* Update some stuff not stored in the savefile any more */ p_ptr->update |= (PU_VIEW | PU_MON_LITE); /* Update stuff */ update_stuff(); /* Leave "xtra" mode */ character_xtra = FALSE; /* Window stuff */ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_SPELL | PW_PLAYER); /* Window stuff */ p_ptr->window |= (PW_MONSTER); /* Window stuff */ window_stuff(); /* Initialise inventory and equipment info for ports */ Term_write_list(p_ptr->inventory, LIST_INVEN); Term_write_equipment(); /* Process */ while (TRUE) { /* Process the level */ evolve_dungeon(); /* Notice */ notice_stuff(); /* Update */ handle_stuff(); /* Cancel the target */ p_ptr->target_who = 0; /* Cancel the health bar */ health_track(0); /* Forget the view */ forget_view(); /* Handle "quit and save" */ if (!p_ptr->state.playing && !p_ptr->state.is_dead) break; /* Go to the new level */ change_level(p_ptr->depth); /* XXX XXX XXX */ message_flush(); /* Accidental Death */ if (p_ptr->state.playing && p_ptr->state.is_dead) { /* Mega-Hack -- Allow player to cheat death */ if ((p_ptr->state.wizard || cheat_live) && !get_check("Die? ")) { /* Mark social class, reset age, if needed */ if (p_ptr->rp.sc) p_ptr->rp.sc = p_ptr->rp.age = 0; /* Increase age */ p_ptr->rp.age++; /* Mark savefile */ p_ptr->state.noscore |= 0x0001; /* Message */ msgf("You invoke wizard mode and cheat death."); message_flush(); /* Restore hit points */ p_ptr->chp = p_ptr->mhp; p_ptr->chp_frac = 0; /* Restore spell points */ p_ptr->csp = p_ptr->msp; p_ptr->csp_frac = 0; /* Hack -- Healing */ (void)clear_blind(); (void)clear_confused(); (void)clear_poisoned(); (void)clear_afraid(); (void)clear_paralyzed(); (void)clear_image(); (void)clear_stun(); (void)clear_cut(); /* Hack"-- Prevent starvation */ (void)set_food(PY_FOOD_MAX - 1); /* Hack -- cancel recall */ if (p_ptr->tim.word_recall) { /* Message */ msgf("A tension leaves the air around you..."); message_flush(); /* Hack -- Prevent recall */ p_ptr->tim.word_recall = 0; p_ptr->redraw |= (PR_STATUS); } /* Note cause of death XXX XXX XXX */ (void)strcpy(p_ptr->state.died_from, "Cheating death"); /* Do not die */ p_ptr->state.is_dead = FALSE; p_ptr->depth = 0; change_level(p_ptr->depth); /* Leaving */ p_ptr->state.leaving = TRUE; } } /* Handle "death" */ if (p_ptr->state.is_dead) break; /* Make a new level */ generate_cave(); /* Update panels */ p_ptr->update |= (PU_MAP); update_stuff(); } /* Close stuff */ close_game(); /* Quit */ quit(NULL); } zangband/src/effects.c0000755000000000000000000015765610250356274013745 0ustar rootroot/* File: effects.c */ /* Purpose: effects of various "objects" */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Set "p_ptr->blind", notice observable changes */ static bool set_blind(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.blind) { msgf("You are blind!"); notice = TRUE; chg_virtue(V_ENLIGHTEN, -1); } } /* Shut */ else { if (p_ptr->tim.blind) { msgf("You can see again."); notice = TRUE; } } /* Use the value */ p_ptr->tim.blind = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Fully update the visuals - hack set torch to be radius 0 */ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_TORCH); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Redraw the "blind" */ p_ptr->redraw |= (PR_BLIND); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the blind counter */ bool inc_blind(int v) { return(set_blind(p_ptr->tim.blind + v)); } /* * No more blindness */ bool clear_blind(void) { return(set_blind(0)); } /* * Set "p_ptr->confused", notice observable changes */ static bool set_confused(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.confused) { msgf("You are confused!"); notice = TRUE; chg_virtue(V_HARMONY, -1); } } /* Shut */ else { if (p_ptr->tim.confused) { msgf("You feel less confused now."); notice = TRUE; } } /* Use the value */ p_ptr->tim.confused = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Redraw the "confused" */ p_ptr->redraw |= (PR_CONFUSED); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the confusion counter */ bool inc_confused(int v) { return(set_confused(p_ptr->tim.confused + v)); } /* * No more confusion */ bool clear_confused(void) { return(set_confused(0)); } /* * Set "p_ptr->poisoned", notice observable changes */ static bool set_poisoned(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.poisoned) { msgf("You are poisoned!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.poisoned) { msgf("You are no longer poisoned."); notice = TRUE; } } /* Use the value */ p_ptr->tim.poisoned = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Redraw the "poisoned" */ p_ptr->redraw |= (PR_POISONED); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the poison counter */ bool inc_poisoned(int v) { return(set_poisoned(p_ptr->tim.poisoned + v)); } /* * No more poison */ bool clear_poisoned(void) { return(set_poisoned(0)); } /* * Set "p_ptr->afraid", notice observable changes */ static bool set_afraid(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.afraid) { msgf("You are terrified!"); notice = TRUE; chg_virtue(V_VALOUR, -1); } } /* Shut */ else { if (p_ptr->tim.afraid) { msgf("You feel bolder now."); notice = TRUE; } } /* Use the value */ p_ptr->tim.afraid = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Redraw the "afraid" */ p_ptr->redraw |= (PR_AFRAID); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the afraid counter */ bool inc_afraid(int v) { return(set_afraid(p_ptr->tim.afraid + v)); } /* * No more fear */ bool clear_afraid(void) { return(set_afraid(0)); } /* * Set "p_ptr->paralyzed", notice observable changes */ static bool set_paralyzed(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.paralyzed) { msgf("You are paralyzed!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.paralyzed) { msgf("You can move again."); notice = TRUE; } } /* Use the value */ p_ptr->tim.paralyzed = v; /* Redraw status bar + message*/ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Redraw the state */ p_ptr->redraw |= (PR_STATE | PR_SPEED); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the paralyzed counter */ bool inc_paralyzed(int v) { return(set_paralyzed(p_ptr->tim.paralyzed + v)); } /* * No more paralyzation */ bool clear_paralyzed(void) { return(set_paralyzed(0)); } /* * Set "p_ptr->image", notice observable changes * * Note that we must redraw the map when hallucination changes. */ static bool set_image(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.image) { msgf("Oh, wow! Everything looks so cosmic now!"); notice = TRUE; /* Update the monster vis window */ p_ptr->window |= PW_VISIBLE; } } /* Shut */ else { if (p_ptr->tim.image) { msgf("You can see clearly again."); notice = TRUE; /* Update the monster vis window */ p_ptr->window |= PW_VISIBLE; } } /* Use the value */ p_ptr->tim.image = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the image counter */ bool inc_image(int v) { return(set_image(p_ptr->tim.image + v)); } /* * No more hallucination */ bool clear_image(void) { return(set_image(0)); } /* * Set "p_ptr->fast", notice observable changes */ static bool set_fast(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.fast) { msgf("You feel yourself moving faster!"); notice = TRUE; chg_virtue(V_PATIENCE, -1); chg_virtue(V_DILIGENCE, 1); } } /* Shut */ else { if (p_ptr->tim.fast) { msgf("You feel yourself slow down."); notice = TRUE; } } /* Use the value */ p_ptr->tim.fast = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the "fast" counter. * * Hack - only increase speed a little bit if already hasted. */ bool inc_fast(int v) { /* Haste */ if ((!p_ptr->tim.fast) || (v < 0)) { return (set_fast(p_ptr->tim.fast + v)); } else { return (set_fast(p_ptr->tim.fast + randint1(5))); } } /* * No more increased speed */ bool clear_fast(void) { return(set_fast(0)); } /* * Set "p_ptr->slow", notice observable changes */ static bool set_slow(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.slow) { msgf("You feel yourself moving slower!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.slow) { msgf("You feel yourself speed up."); notice = TRUE; } } /* Use the value */ p_ptr->tim.slow = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the "slow" counter */ bool inc_slow(int v) { return(set_slow(p_ptr->tim.slow + v)); } /* * No more "slowness" */ bool clear_slow(void) { return(set_slow(0)); } /* * Increment "p_ptr->shield", notice observable changes */ bool inc_shield(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.shield; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.shield) { msgf("Your skin turns to stone."); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.shield) { msgf("Your skin returns to normal."); notice = TRUE; } } /* Use the value */ p_ptr->tim.shield = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->blessed", notice observable changes */ bool inc_blessed(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.blessed; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.blessed) { msgf("You feel righteous!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.blessed) { msgf("The prayer has expired."); notice = TRUE; } } /* Use the value */ p_ptr->tim.blessed = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->hero", notice observable changes */ bool inc_hero(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.hero; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.hero) { msgf("You feel like a hero!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.hero) { msgf("The heroism wears off."); notice = TRUE; } } /* Use the value */ p_ptr->tim.hero = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate hitpoints */ p_ptr->update |= (PU_HP); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->shero", notice observable changes */ bool inc_shero(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.shero; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.shero) { msgf("You feel like a killing machine!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.shero) { msgf("You feel less Berserk."); notice = TRUE; } } /* Use the value */ p_ptr->tim.shero = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate hitpoints */ p_ptr->update |= (PU_HP); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->protevil", notice observable changes */ bool inc_protevil(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.protevil; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.protevil) { msgf("You feel safe from evil!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.protevil) { msgf("You no longer feel safe from evil."); notice = TRUE; } } /* Use the value */ p_ptr->tim.protevil = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->wraith_form", notice observable changes */ bool inc_wraith_form(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.wraith_form; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.wraith_form) { msgf ("You leave the physical world and turn into a wraith-being!"); notice = TRUE; /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } } /* Shut */ else { if (p_ptr->tim.wraith_form) { msgf("You feel opaque."); notice = TRUE; /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } } /* Use the value */ p_ptr->tim.wraith_form = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->invuln", notice observable changes */ bool inc_invuln(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.invuln; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.invuln) { msgf("Invulnerability!"); notice = TRUE; chg_virtue(V_TEMPERANCE, -5); chg_virtue(V_HONOUR, -5); chg_virtue(V_SACRIFICE, -5); chg_virtue(V_VALOUR, -10); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } } /* Shut */ else { if (p_ptr->tim.invuln) { msgf("The invulnerability wears off."); notice = TRUE; /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } } /* Use the value */ p_ptr->tim.invuln = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->tim.esp", notice observable changes */ static bool set_tim_esp(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.esp) { msgf("You feel your consciousness expand!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.esp) { msgf("Your consciousness contracts again."); notice = TRUE; } } /* Use the value */ p_ptr->tim.esp = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the "esp" counter */ bool inc_tim_esp(int v) { return(set_tim_esp(p_ptr->tim.esp + v)); } /* * No more "esp" */ bool clear_tim_esp(void) { return(set_tim_esp(0)); } /* * Increment "p_ptr->tim_invis", notice observable changes */ bool inc_tim_invis(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.invis; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.invis) { msgf("Your eyes feel very sensitive!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.invis) { msgf("Your eyes feel less sensitive."); notice = TRUE; } } /* Use the value */ p_ptr->tim.invis = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->tim_infra", notice observable changes */ bool inc_tim_infra(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.invuln; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.infra) { msgf("Your eyes begin to tingle!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.infra) { msgf("Your eyes stop tingling."); notice = TRUE; } } /* Use the value */ p_ptr->tim.infra = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->oppose_acid", notice observable changes */ bool inc_oppose_acid(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.oppose_acid; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.oppose_acid) { msgf("You feel resistant to acid!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.oppose_acid) { msgf("You feel less resistant to acid."); notice = TRUE; } } /* Use the value */ p_ptr->tim.oppose_acid = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->oppose_elec", notice observable changes */ bool inc_oppose_elec(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.oppose_elec; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.oppose_elec) { msgf("You feel resistant to electricity!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.oppose_elec) { msgf("You feel less resistant to electricity."); notice = TRUE; } } /* Use the value */ p_ptr->tim.oppose_elec = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->oppose_fire", notice observable changes */ bool inc_oppose_fire(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.oppose_fire; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.oppose_fire) { msgf("You feel resistant to fire!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.oppose_fire) { msgf("You feel less resistant to fire."); notice = TRUE; } } /* Use the value */ p_ptr->tim.oppose_fire = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->oppose_cold", notice observable changes */ bool inc_oppose_cold(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.oppose_cold; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.oppose_cold) { msgf("You feel resistant to cold!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.oppose_cold) { msgf("You feel less resistant to cold."); notice = TRUE; } } /* Use the value */ p_ptr->tim.oppose_cold = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increment "p_ptr->oppose_pois", notice observable changes */ bool inc_oppose_pois(int v) { bool notice = FALSE; /* What will the new value be? */ v = v + p_ptr->tim.oppose_pois; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim.oppose_pois) { msgf("You feel resistant to poison!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim.oppose_pois) { msgf("You feel less resistant to poison."); notice = TRUE; } } /* Use the value */ p_ptr->tim.oppose_pois = v; /* Redraw status bar */ p_ptr->redraw |= (PR_STATUS); /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Helper functions to test resistance status for the various elements * * These return a value from 0 to 200 indicating how much damage you * take from an element, as a percentage of normal damage. */ static int resist_table[12] = {3, 5, 7, 11, 16, 22, 33, 50, 66, 100, 150, 200}; /* * Acid resist level */ int res_acid_lvl(void) { int level = 9; if (FLAG(p_ptr, TR_IM_ACID)) return (0); if (FLAG(p_ptr, TR_RES_ACID)) level -= 3; if (p_ptr->tim.oppose_acid) level -= 3; if (FLAG(p_ptr, TR_HURT_ACID)) level += 2; if (level < 0) level = 0; if (level > 11) level = 11; return resist_table[level];; } /* * Electricity resist level */ int res_elec_lvl(void) { int level = 9; if (FLAG(p_ptr, TR_IM_ELEC)) return (0); if (FLAG(p_ptr, TR_RES_ELEC)) level -= 3; if (p_ptr->tim.oppose_elec) level -= 3; if (FLAG(p_ptr, TR_HURT_ELEC)) level += 2; if (level < 0) level = 0; if (level > 11) level = 11; return resist_table[level];; } /* * Fire resist level */ int res_fire_lvl(void) { int level = 9; if (FLAG(p_ptr, TR_IM_FIRE)) return (0); if (FLAG(p_ptr, TR_RES_FIRE)) level -= 3; if (p_ptr->tim.oppose_fire) level -= 3; if (FLAG(p_ptr, TR_HURT_FIRE)) level += 2; if (level < 0) level = 0; if (level > 11) level = 11; return resist_table[level];; } /* * Cold resist level */ int res_cold_lvl(void) { int level = 9; if (FLAG(p_ptr, TR_IM_COLD)) return (0); if (FLAG(p_ptr, TR_RES_COLD)) level -= 3; if (p_ptr->tim.oppose_cold) level -= 3; if (FLAG(p_ptr, TR_HURT_COLD)) level += 2; if (level < 0) level = 0; if (level > 11) level = 11; return resist_table[level];; } /* * Poison resist level */ int res_pois_lvl(void) { int level = 9; if (FLAG(p_ptr, TR_IM_POIS)) return (0); if (FLAG(p_ptr, TR_RES_POIS)) level -= 3; if (p_ptr->tim.oppose_pois) level -= 3; if (level < 0) level = 0; if (level > 11) level = 11; return resist_table[level];; } /* * Apply resistance to damage */ int resist(int dam, int (*f_func) (void)) { /* Invulnerability */ if (p_ptr->tim.invuln) return (0); /* Use the function we were passed, and round up the damage */ return ((dam * f_func() + 99) / 100); } /* * Acid has hit the player, attempt to affect some armor. * * Note that the "base armor" of an object never changes. * * If any armor is damaged (or resists), the player takes less damage. */ static int minus_ac(void) { object_type *o_ptr = NULL; /* Pick a (possibly empty) inventory slot */ switch (randint1(6)) { case 1: { o_ptr = &p_ptr->equipment[EQUIP_BODY]; break; } case 2: { o_ptr = &p_ptr->equipment[EQUIP_ARM]; break; } case 3: { o_ptr = &p_ptr->equipment[EQUIP_OUTER]; break; } case 4: { o_ptr = &p_ptr->equipment[EQUIP_HANDS]; break; } case 5: { o_ptr = &p_ptr->equipment[EQUIP_HEAD]; break; } case 6: { o_ptr = &p_ptr->equipment[EQUIP_FEET]; break; } } /* Nothing to damage */ if (!o_ptr->k_idx) return (FALSE); /* No damage left to be done */ if (o_ptr->ac + o_ptr->to_a <= 0) return (FALSE); /* Object resists */ if (FLAG(o_ptr, TR_IGNORE_ACID)) { msgf("Your %v is unaffected!", OBJECT_FMT(o_ptr, FALSE, 0)); return (TRUE); } /* Message */ msgf("Your %v is damaged!", OBJECT_FMT(o_ptr, FALSE, 0)); /* Damage the item */ o_ptr->to_a--; /* Calculate bonuses */ p_ptr->update |= (PU_BONUS); /* Window stuff */ p_ptr->window |= (PW_EQUIP | PW_PLAYER); /* Item was damaged */ return (TRUE); } /* * Hurt the player with Acid */ bool acid_dam(int dam, cptr kb_str) { int inv; dam = resist(dam, res_acid_lvl); /* Total Immunity? */ if (dam <= 0) return (FALSE); inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3; if ((res_acid_lvl() > 50) && one_in_(HURT_CHANCE)) (void)do_dec_stat(A_CHR); /* If any armor gets hit, defend the player */ if (minus_ac()) dam = (dam + 1) / 2; /* Take damage */ take_hit(dam, kb_str); /* Inventory damage */ if (res_acid_lvl() > 25) (void)inven_damage(set_acid_destroy, inv); /* Obvious */ return (TRUE); } /* * Hurt the player with electricity */ bool elec_dam(int dam, cptr kb_str) { int inv; dam = resist(dam, res_elec_lvl); /* Total immunity */ if (dam <= 0) return (FALSE); inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3; if ((res_elec_lvl() > 50) && one_in_(HURT_CHANCE)) (void)do_dec_stat(A_DEX); /* Take damage */ take_hit(dam, kb_str); /* Inventory damage */ if (res_acid_lvl() > 25) (void)inven_damage(set_elec_destroy, inv); /* Obvious */ return (TRUE); } /* * Hurt the player with Fire */ bool fire_dam(int dam, cptr kb_str) { int inv; dam = resist(dam, res_fire_lvl); /* Totally immune? */ if (dam <= 0) return (FALSE); inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3; if ((res_fire_lvl() > 50) && one_in_(HURT_CHANCE)) (void)do_dec_stat(A_STR); /* Take damage */ take_hit(dam, kb_str); /* Inventory damage */ if (res_fire_lvl() > 25) (void)inven_damage(set_fire_destroy, inv); /* Obvious */ return (TRUE); } /* * Hurt the player with Cold */ bool cold_dam(int dam, cptr kb_str) { int inv; dam = resist(dam, res_cold_lvl); /* Total immunity? */ if (dam <= 0) return (FALSE); inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3; if ((res_cold_lvl() > 50) && one_in_(HURT_CHANCE)) (void)do_dec_stat(A_STR); /* Take damage */ take_hit(dam, kb_str); /* Inventory damage */ if (res_cold_lvl() > 25) (void)inven_damage(set_cold_destroy, inv); /* Obvious */ return (TRUE); } /* * Hurt the player with Poison * * Hack - this should probably take a second argument * to add to the poison counter */ bool pois_dam(int dam, cptr kb_str, int pois) { dam = resist(dam, res_pois_lvl); /* Totally immune? */ if (dam <= 0) return (FALSE); if ((res_pois_lvl() > 50) && one_in_(HURT_CHANCE)) (void)do_dec_stat(A_CON); /* Take damage */ take_hit(dam, kb_str); /* Add poison to counter */ if (res_pois_lvl() > 25) { pois = resist(pois, res_pois_lvl); inc_poisoned(pois); } /* Obvious */ return (TRUE); } /* * Set "p_ptr->stun", notice observable changes * * Note the special code to only notice "range" changes. */ static bool set_stun(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* * Golems cannot be stunned when they are being used as a * "training" class. However, when they are being used in * a hard game - they lose this advantage. (Golems are * designed for newbies - not scummers.) */ if ((p_ptr->rp.prace == RACE_GOLEM) && !(ironman_shops || ironman_downward || ironman_nightmare)) { v = 0; } /* Knocked out */ if (p_ptr->tim.stun > 100) { old_aux = 3; } /* Heavy stun */ else if (p_ptr->tim.stun > 50) { old_aux = 2; } /* Stun */ else if (p_ptr->tim.stun > 0) { old_aux = 1; } /* None */ else { old_aux = 0; } /* Knocked out */ if (v > 100) { new_aux = 3; } /* Heavy stun */ else if (v > 50) { new_aux = 2; } /* Stun */ else if (v > 0) { new_aux = 1; } /* None */ else { new_aux = 0; } /* Increase cut */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { case 1: { /* Stun */ msgf("You have been stunned."); break; } case 2: { /* Heavy stun */ msgf("You have been heavily stunned."); break; } case 3: { /* Knocked out */ msgf("You have been knocked out."); break; } } /* * XXX XXX Hack - * Mindcrafters cannot get this effect when * casting a spell. It really doesn't make sense. * Unfortunately, there is no way to know if this is * the case... so it is disabled in all circumstances * if you are a Mindcrafter. (Perhaps it can be * explained away by their "superior mental skills" or * something... */ if ((randint1(1000) < v || one_in_(16)) && (!(p_ptr->rp.pclass == CLASS_MINDCRAFTER))) { msgf("A vicious blow hits your head."); if (one_in_(3)) { if (!(FLAG(p_ptr, TR_SUST_INT))) (void)do_dec_stat(A_INT); if (!(FLAG(p_ptr, TR_SUST_WIS))) (void)do_dec_stat(A_WIS); } else if (one_in_(2)) { if (!(FLAG(p_ptr, TR_SUST_INT))) (void)do_dec_stat(A_INT); } else { if (!(FLAG(p_ptr, TR_SUST_WIS))) (void)do_dec_stat(A_WIS); } } /* Notice */ notice = TRUE; } /* Decrease cut */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { case 0: { /* None */ msgf("You are no longer stunned."); if (disturb_state) disturb(FALSE); break; } } /* Notice */ notice = TRUE; } /* Use the value */ p_ptr->tim.stun = v; /* No change */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the "stun" */ p_ptr->redraw |= (PR_STUN); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the "stun" counter */ bool inc_stun(int v) { return(set_stun(p_ptr->tim.stun + v)); } /* * No more "stun" */ bool clear_stun(void) { return(set_stun(0)); } /* * Set "p_ptr->cut", notice observable changes * * Note the special code to only notice "range" changes. */ static bool set_cut(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; if (p_ptr->rp.prace == RACE_GOLEM || p_ptr->rp.prace == RACE_SKELETON || p_ptr->rp.prace == RACE_SPECTRE || (p_ptr->rp.prace == RACE_ZOMBIE && p_ptr->lev > 11)) v = 0; /* Mortal wound */ if (p_ptr->tim.cut > 1000) { old_aux = 7; } /* Deep gash */ else if (p_ptr->tim.cut > 200) { old_aux = 6; } /* Severe cut */ else if (p_ptr->tim.cut > 100) { old_aux = 5; } /* Nasty cut */ else if (p_ptr->tim.cut > 50) { old_aux = 4; } /* Bad cut */ else if (p_ptr->tim.cut > 25) { old_aux = 3; } /* Light cut */ else if (p_ptr->tim.cut > 10) { old_aux = 2; } /* Graze */ else if (p_ptr->tim.cut > 0) { old_aux = 1; } /* None */ else { old_aux = 0; } /* Mortal wound */ if (v > 1000) { new_aux = 7; } /* Deep gash */ else if (v > 200) { new_aux = 6; } /* Severe cut */ else if (v > 100) { new_aux = 5; } /* Nasty cut */ else if (v > 50) { new_aux = 4; } /* Bad cut */ else if (v > 25) { new_aux = 3; } /* Light cut */ else if (v > 10) { new_aux = 2; } /* Graze */ else if (v > 0) { new_aux = 1; } /* None */ else { new_aux = 0; } /* Increase cut */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { case 1: { /* Graze */ msgf("You have been given a graze."); break; } case 2: { /* Light cut */ msgf("You have been given a light cut."); break; } case 3: { /* Bad cut */ msgf("You have been given a bad cut."); break; } case 4: { /* Nasty cut */ msgf("You have been given a nasty cut."); break; } case 5: { /* Severe cut */ msgf("You have been given a severe cut."); break; } case 6: { /* Deep gash */ msgf("You have been given a deep gash."); break; } case 7: { /* Mortal wound */ msgf("You have been given a mortal wound."); break; } } /* Notice */ notice = TRUE; if (randint1(1000) < v || one_in_(16)) { if (!(FLAG(p_ptr, TR_SUST_CHR))) { msgf("You have been horribly scarred."); (void)do_dec_stat(A_CHR); } } } /* Decrease cut */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { case 0: { /* None */ msgf("You are no longer bleeding."); if (disturb_state) disturb(FALSE); break; } } /* Notice */ notice = TRUE; } /* Use the value */ p_ptr->tim.cut = v; /* No change */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the "cut" */ p_ptr->redraw |= (PR_CUT); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increase the "cut" counter */ bool inc_cut(int v) { return(set_cut(p_ptr->tim.cut + v)); } /* * No more "cuts" */ bool clear_cut(void) { return(set_cut(0)); } /* * Set "p_ptr->food", notice observable changes * * The "p_ptr->food" variable can get as large as 20000, allowing the * addition of the most "filling" item, Elvish Waybread, which adds * 7500 food units, without overflowing the 32767 maximum limit. * * Perhaps we should disturb the player with various messages, * especially messages about hunger status changes. XXX XXX XXX * * Digestion of food is handled in "dungeon.c", in which, normally, * the player digests about 20 food units per 100 game turns, more * when "fast", more when "regenerating", less with "slow digestion", * but when the player is "gorged", he digests 100 food units per 10 * game turns, or a full 1000 food units per 100 game turns. * * Note that the player's speed is reduced by 10 units while gorged, * so if the player eats a single food ration (5000 food units) when * full (15000 food units), he will be gorged for (5000/100)*10 = 500 * game turns, or 500/(100/5) = 25 player turns (if nothing else is * affecting the player speed). */ bool set_food(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = (v > 20000) ? 20000 : (v < 0) ? 0 : v; /* Fainting / Starving */ if (p_ptr->food < PY_FOOD_FAINT) { old_aux = 0; } /* Weak */ else if (p_ptr->food < PY_FOOD_WEAK) { old_aux = 1; } /* Hungry */ else if (p_ptr->food < PY_FOOD_ALERT) { old_aux = 2; } /* Normal */ else if (p_ptr->food < PY_FOOD_FULL) { old_aux = 3; } /* Full */ else if (p_ptr->food < PY_FOOD_MAX) { old_aux = 4; } /* Gorged */ else { old_aux = 5; } /* Fainting / Starving */ if (v < PY_FOOD_FAINT) { new_aux = 0; } /* Weak */ else if (v < PY_FOOD_WEAK) { new_aux = 1; } /* Hungry */ else if (v < PY_FOOD_ALERT) { new_aux = 2; } /* Normal */ else if (v < PY_FOOD_FULL) { new_aux = 3; } /* Full */ else if (v < PY_FOOD_MAX) { new_aux = 4; } /* Gorged */ else { new_aux = 5; } if (old_aux < 1 && new_aux > 0) chg_virtue(V_PATIENCE, 2); else if (old_aux < 3 && (old_aux != new_aux)) chg_virtue(V_PATIENCE, 1); if (old_aux == 2) chg_virtue(V_TEMPERANCE, 1); if (old_aux == 0) chg_virtue(V_TEMPERANCE, -1); /* Food increase */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { case 1: { /* Weak */ msgf("You are still weak."); break; } case 2: { /* Hungry */ msgf("You are still hungry."); break; } case 3: { /* Normal */ msgf("You are no longer hungry."); break; } case 4: { /* Full */ msgf("You are full!"); break; } case 5: { /* Bloated */ msgf("You have gorged yourself!"); chg_virtue(V_HARMONY, -1); chg_virtue(V_PATIENCE, -1); chg_virtue(V_TEMPERANCE, -2); break; } } /* Change */ notice = TRUE; } /* Food decrease */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { case 0: { /* Fainting / Starving */ msgf("You are getting faint from hunger!"); break; } case 1: { /* Weak */ msgf("You are getting weak from hunger!"); break; } case 2: { /* Hungry */ msgf("You are getting hungry."); break; } case 3: { /* Normal */ msgf("You are no longer full."); break; } case 4: { /* Full */ msgf("You are no longer gorged."); break; } } /* Change */ notice = TRUE; } /* Use the value */ p_ptr->food = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(FALSE); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw hunger */ p_ptr->redraw |= (PR_HUNGER); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Increases a stat by one randomized level -RAK- * * Note that this function (used by stat potions) now restores * the stat BEFORE increasing it. */ bool inc_stat(int stat) { int value, gain; int min_gain, max_gain; int cap = stat_cap(stat); /* Then augment the current/max stat */ value = p_ptr->stat[stat].cur; /* Cannot go above limit */ if (value < cap) { min_gain = (cap - value) / 6; max_gain = (cap - value) / 3; if (min_gain > 5) min_gain = 5; if (min_gain < 1) min_gain = 1; if (max_gain > 20) max_gain = 20; if (max_gain < 1) max_gain = 1; gain = rand_range(min_gain, max_gain); value += gain; /* Save the new value */ p_ptr->stat[stat].cur = value; /* Bring up the maximum too */ if (value > p_ptr->stat[stat].max) { p_ptr->stat[stat].max = value; } /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Success */ return (TRUE); } /* Nothing to gain */ return (FALSE); } /* * Decreases a stat by an amount indended to vary from 0 to 100 percent. * * Amount could be a little higher in extreme cases to mangle very high * stats from massive assaults. -CWS * * Note that "permanent" means that the *given* amount is permanent, * not that the new value becomes permanent. This may not work exactly * as expected, due to "weirdness" in the algorithm, but in general, * if your stat is already drained, the "max" value will not drop all * the way down to the "cur" value. */ bool dec_stat(int stat, int amount, int permanent) { int cur, max, same, res = FALSE; /* Acquire current value */ cur = p_ptr->stat[stat].cur; max = p_ptr->stat[stat].max; /* Note when the values are identical */ same = (cur == max); /* Damage "current" value */ if (cur > 30) { if (cur > 30 && amount > 90) cur -= rand_range(3, cur / 10); if (cur > 30 && amount > 50) cur -= rand_range(3, cur / 10); if (cur > 30 && amount > 20) cur -= rand_range(3, cur / 10); if (cur > 30) cur -= rand_range(3, cur / 10); /* Prevent illegal values */ if (cur < 30) cur = 30; /* Something happened */ if (cur != p_ptr->stat[stat].cur) res = TRUE; } /* Damage "max" value */ if (permanent && (max > 30)) { chg_virtue(V_SACRIFICE, 1); if (stat == A_WIS || stat == A_INT) chg_virtue(V_ENLIGHTEN, -2); if (max > 30 && amount > 90) max -= rand_range(3, max / 10); if (max > 30 && amount > 50) max -= rand_range(3, max / 10); if (max > 30 && amount > 20) max -= rand_range(3, max / 10); if (max > 30) max -= rand_range(3, max / 10); /* Hack -- keep it clean */ if (same || (max < cur)) max = cur; /* Something happened */ if (max != p_ptr->stat[stat].max) res = TRUE; } /* Apply changes */ if (res) { /* Actually set the stat to its new value. */ p_ptr->stat[stat].cur = cur; p_ptr->stat[stat].max = max; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); } /* Done */ return (res); } /* * Restore a stat. Return TRUE only if this actually makes a difference. */ bool res_stat(int stat) { /* Restore if needed */ if (p_ptr->stat[stat].cur != p_ptr->stat[stat].max) { /* Restore */ p_ptr->stat[stat].cur = p_ptr->stat[stat].max; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Success */ return (TRUE); } /* Nothing to restore */ return (FALSE); } /* * Increase players hit points, notice effects */ bool hp_player(int num) { /* Healing needed */ if (p_ptr->chp < p_ptr->mhp) { chg_virtue(V_CHANCE, -1); if ((num > 0) && (p_ptr->chp < (p_ptr->mhp / 3))) chg_virtue(V_TEMPERANCE, 1); /* Gain hitpoints */ p_ptr->chp += num; /* Enforce maximum */ if (p_ptr->chp >= p_ptr->mhp) { p_ptr->chp = p_ptr->mhp; p_ptr->chp_frac = 0; } /* Redraw */ p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Heal 0-4 */ if (num < 5) { msgf("You feel a little better."); } /* Heal 5-14 */ else if (num < 15) { msgf("You feel better."); } /* Heal 15-34 */ else if (num < 35) { msgf("You feel much better."); } /* Heal 35+ */ else { msgf("You feel very good."); } /* Notice */ return (TRUE); } /* Ignore */ return (FALSE); } /* * Array of stat "descriptions" */ static cptr desc_stat_pos[] = { "strong", "smart", "wise", "dextrous", "healthy", "cute" }; /* * Array of stat "descriptions" */ static cptr desc_stat_neg[] = { "weak", "stupid", "naive", "clumsy", "sickly", "ugly" }; /* * Lose a "point" */ bool do_dec_stat(int stat) { bool sust = FALSE; /* Access the "sustain" */ switch (stat) { case A_STR: { if (FLAG(p_ptr, TR_SUST_STR)) sust = TRUE; break; } case A_INT: { if (FLAG(p_ptr, TR_SUST_INT)) sust = TRUE; break; } case A_WIS: { if (FLAG(p_ptr, TR_SUST_WIS)) sust = TRUE; break; } case A_DEX: { if (FLAG(p_ptr, TR_SUST_DEX)) sust = TRUE; break; } case A_CON: { if (FLAG(p_ptr, TR_SUST_CON)) sust = TRUE; break; } case A_CHR: { if (FLAG(p_ptr, TR_SUST_CHR)) sust = TRUE; break; } } /* Sustain */ if (sust && !(ironman_nightmare && one_in_(13))) { /* Message */ msgf("You feel %s for a moment, but the feeling passes.", desc_stat_neg[stat]); /* Notice effect */ return (TRUE); } /* Attempt to reduce the stat */ if (dec_stat(stat, 10, (ironman_nightmare && !one_in_(13)))) { /* Message */ msgf("You feel very %s.", desc_stat_neg[stat]); /* Notice effect */ return (TRUE); } /* Nothing obvious */ return (FALSE); } /* * Restore lost "points" in a stat */ bool do_res_stat(int stat) { /* Attempt to increase */ if (res_stat(stat)) { /* Message */ msgf("You feel less %s.", desc_stat_neg[stat]); /* Notice */ return (TRUE); } /* Nothing obvious */ return (FALSE); } /* * Gain a "point" in a stat */ bool do_inc_stat(int stat) { bool res; /* Restore strength */ res = res_stat(stat); /* Attempt to increase */ if (inc_stat(stat)) { if (stat == A_WIS) { chg_virtue(V_ENLIGHTEN, 1); chg_virtue(V_FAITH, 1); } else if (stat == A_INT) { chg_virtue(V_KNOWLEDGE, 1); chg_virtue(V_ENLIGHTEN, 1); } else if (stat == A_CON) chg_virtue(V_VITALITY, 1); /* Message */ msgf("Wow! You feel very %s!", desc_stat_pos[stat]); /* Notice */ return (TRUE); } /* Restoration worked */ if (res) { /* Message */ msgf("You feel less %s.", desc_stat_neg[stat]); /* Notice */ return (TRUE); } /* Nothing obvious */ return (FALSE); } /* * Restores any drained experience */ bool restore_level(void) { /* Restore experience */ if (p_ptr->exp < p_ptr->max_exp) { /* Message */ msgf("You feel your life energies returning."); /* Restore the experience */ p_ptr->exp = p_ptr->max_exp; /* Check the experience */ check_experience(); /* Did something */ return (TRUE); } /* No effect */ return (FALSE); } /* * Forget everything */ bool lose_all_info(void) { int i, k; object_type *o_ptr; chg_virtue(V_KNOWLEDGE, -5); chg_virtue(V_ENLIGHTEN, -5); /* Forget info about equipment */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Allow "protection" if know all the flags... */ if (object_known_full(o_ptr)) continue; /* Remove "default inscriptions" */ o_ptr->feeling = FEEL_NONE; /* Hack -- Clear the "empty" flag */ o_ptr->info &= ~(OB_EMPTY); /* Hack -- Clear the "known" flag */ o_ptr->info &= ~(OB_KNOWN); /* Hack -- Clear the "felt" flag */ o_ptr->info &= ~(OB_SENSE); } /* Forget info about objects */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Allow "protection" if know all the flags... */ if (object_known_full(o_ptr)) continue; /* Remove "default inscriptions" */ o_ptr->feeling = FEEL_NONE; /* Hack -- Clear the "empty" flag */ o_ptr->info &= ~(OB_EMPTY); /* Hack -- Clear the "known" flag */ o_ptr->info &= ~(OB_KNOWN); /* Hack -- Clear the "felt" flag */ o_ptr->info &= ~(OB_SENSE); } OBJ_ITT_END; /* Hack - Remove all knowledge about objects */ /* Scan the object kinds */ for (k = 1; k < z_info->k_max; k++) { object_kind *k_ptr = &k_info[k]; /* Forget flavored items, with saving throw */ if (k_ptr->flavor && !player_save(k_ptr->level - 50)) { /* Forget knowledge */ k_ptr->aware = FALSE; k_ptr->tried = FALSE; } } /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); /* Mega-Hack -- Forget the map */ wiz_dark(); /* It worked */ return (TRUE); } void do_poly_wounds(void) { /* Changed to always provide at least _some_ healing */ s16b wounds = p_ptr->tim.cut; s16b hit_p = (p_ptr->mhp - p_ptr->chp); s16b change = damroll(p_ptr->lev, 5); bool Nasty_effect = (one_in_(5)); if (!(wounds || hit_p || Nasty_effect)) return; msgf("Your wounds are polymorphed into less serious ones."); (void)hp_player(change); if (Nasty_effect) { msgf("A new wound was created!"); take_hit(change / 2, "a polymorphed wound"); (void)set_cut(change); } else { (void)set_cut(p_ptr->tim.cut - (change / 2)); } } void do_poly_self(void) { int i; int power = p_ptr->lev; msgf("You feel a change coming over you..."); chg_virtue(V_CHANCE, 1); if ((power > randint0(20)) && one_in_(3)) { char effect_msg[80] = ""; int old_race, new_race, expfact, goalexpfact; /* Some form of racial polymorph... */ power -= 10; if ((power > randint0(5)) && one_in_(4)) { /* sex change */ power -= 2; if (p_ptr->rp.psex == SEX_MALE) { p_ptr->rp.psex = SEX_FEMALE; sp_ptr = &sex_info[p_ptr->rp.psex]; strnfmt(effect_msg, 80, "female "); } else { p_ptr->rp.psex = SEX_MALE; sp_ptr = &sex_info[p_ptr->rp.psex]; strnfmt(effect_msg, 80, "male "); } } if ((power > randint0(30)) && one_in_(5)) { int tmp = 0; /* Harmful deformity */ power -= 15; while (tmp < A_MAX) { if (one_in_(2)) { (void)dec_stat(tmp, rand_range(6, 12), one_in_(3)); power -= 1; } tmp++; } /* Deformities are discriminated against! */ (void)dec_stat(A_CHR, randint1(6), TRUE); if (effect_msg[0]) { strnfmt(effect_msg, 80, "deformed %s ", effect_msg); } else { strnfmt(effect_msg, 80, "deformed "); } } while ((power > randint0(20)) && one_in_(10)) { /* Polymorph into a less mutated form */ power -= 10; if (!lose_mutation(0)) msgf("You feel oddly normal."); } /* * Restrict the race choices by exp penalty so * weak polymorph always means weak race */ if (power < 0) goalexpfact = 100; else goalexpfact = 100 + 3 * randint0(power); do { new_race = randint0(MAX_RACES); expfact = race_info[new_race].r_exp; } while ((new_race == p_ptr->rp.prace) && (expfact > goalexpfact)); if (!effect_msg[0]) { msgf("You turn into a%s %s!", (((new_race == RACE_AMBERITE) || (new_race == RACE_ELF) || (new_race == RACE_IMP)) ? "n" : ""), race_info[new_race].title); } else { msgf("You turn into a %s%s!", effect_msg, race_info[new_race].title); } chg_virtue(V_CHANCE, 2); old_race = p_ptr->rp.prace; p_ptr->rp.prace = new_race; rp_ptr = &race_info[p_ptr->rp.prace]; /* Adjust the stats */ for (i = 0; i < A_MAX; i++) { int change; /* Calculate the difference between the races */ change = rp_ptr->r_adj[i] - race_info[old_race].r_adj[i]; /* Adjust current stat */ p_ptr->stat[i].cur = adjust_stat(i, p_ptr->stat[i].cur, change); /* Adjust maximum stat */ p_ptr->stat[i].max = adjust_stat(i, p_ptr->stat[i].max, change); } /* Experience factor */ p_ptr->expfact = rp_ptr->r_exp + cp_ptr->c_exp; /* Calculate the height/weight for males */ if (p_ptr->rp.psex == SEX_MALE) { p_ptr->rp.ht = Rand_normal(rp_ptr->m_b_ht, rp_ptr->m_m_ht); p_ptr->rp.wt = Rand_normal(rp_ptr->m_b_wt, rp_ptr->m_m_wt); } /* Calculate the height/weight for females */ else if (p_ptr->rp.psex == SEX_FEMALE) { p_ptr->rp.ht = Rand_normal(rp_ptr->f_b_ht, rp_ptr->f_m_ht); p_ptr->rp.wt = Rand_normal(rp_ptr->f_b_wt, rp_ptr->f_m_wt); } check_experience(); p_ptr->max_lev = p_ptr->lev; p_ptr->redraw |= (PR_BASIC); p_ptr->update |= (PU_BONUS); handle_stuff(); lite_spot(p_ptr->px, p_ptr->py); } if ((power > randint0(30)) && one_in_(6)) { int tmp = 0; /* Abomination! */ power -= 20; msgf("Your internal organs are rearranged!"); while (tmp < A_MAX) { (void)dec_stat(tmp, rand_range(6, 12), one_in_(3)); tmp++; } if (one_in_(6)) { msgf("You find living difficult in your present form!"); take_hit(damroll(randint1(10), p_ptr->lev), "a lethal mutation"); power -= 10; } } if ((power > randint0(20)) && one_in_(4)) { power -= 10; do_cmd_rerate(); } while ((power > randint0(15)) && one_in_(3)) { power -= 7; (void)gain_mutation(0); } if (power > randint0(5)) { power -= 5; do_poly_wounds(); } /* Note: earlier deductions may have left power < 0 already. */ while (power > 0) { mutate_player(); power--; } /* Hack - reset visuals so the player's tile can change */ reset_visuals(); } /* * Decreases players hit points and sets death flag if necessary * * XXX XXX XXX Invulnerability needs to be changed into a "shield" * * XXX XXX XXX Hack -- this function allows the user to save (or quit) * the game when he dies, since the "You die." message is shown before * setting the player to "dead". */ void take_hit(int damage, cptr hit_from) { int old_chp = p_ptr->chp; bool pen_invuln = FALSE; char death_message[1024]; int warning = (p_ptr->mhp * hitpoint_warn / 10); /* Paranoia */ if (p_ptr->state.is_dead) return; /* Disturb */ disturb(TRUE); /* Mega-Hack -- Apply "invulnerability" */ if (p_ptr->tim.invuln && (damage < 9000)) { if (one_in_(PENETRATE_INVULNERABILITY)) { pen_invuln = TRUE; } else { return; } } if (p_ptr->tim.wraith_form) { damage /= 10; if ((damage == 0) && one_in_(10)) damage = 1; } /* Hurt the player */ p_ptr->chp -= damage; /* Display the hitpoints */ p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Do not skip the message */ p_ptr->state.skip_more = FALSE; if (pen_invuln) msgf("The attack penetrates your shield of invulnerability!"); if (!(p_ptr->tim.invuln) || (pen_invuln)) { if (p_ptr->chp == 0) { chg_virtue(V_SACRIFICE, 1); chg_virtue(V_CHANCE, 2); } } /* Dead player */ if (p_ptr->chp < 0) { int len; /* Sound */ sound(SOUND_DEATH); chg_virtue(V_SACRIFICE, 10); /* Hack -- Note death */ if (!last_words) { msgf(MSGT_DEATH, "You die."); message_flush(); } else { if (!get_rnd_line("death.txt", 0, death_message)) msgf(death_message); } /* Note cause of death */ len = strnfmt(p_ptr->state.died_from, 80, hit_from); if (p_ptr->tim.image) strnfcat(p_ptr->state.died_from, 80, &len, "(?)"); /* No longer a winner */ p_ptr->state.total_winner = FALSE; /* Leaving */ p_ptr->state.leaving = TRUE; /* Note death */ p_ptr->state.is_dead = TRUE; if (get_check("Dump the screen? ")) { do_cmd_save_screen(); } /* Dead */ return; } /* Hitpoint warning */ if (p_ptr->chp <= warning) { /* Hack -- bell on first notice */ if (old_chp > warning) { /* Alert */ bell("Low hitpoint warning!"); if (emergency_stop) { /* Show all the messages */ message_flush(); /* Alert the user to the problem */ put_fstr(0, 0, "Emergency stop. Press 'c' to continue."); /* Wait for acknowledgement */ while (inkey() != 'c') ; disturb(TRUE); } } sound(SOUND_WARN); /* Message */ msgf(MSGT_HITPOINT_WARN, "*** LOW HITPOINT WARNING! ***"); message_flush(); } } /* * Gain experience */ void gain_exp(s32b amount) { /* Gain some experience */ p_ptr->exp += amount; /* Slowly recover from experience drainage */ if (p_ptr->exp < p_ptr->max_exp) { /* Gain max experience (20%) (was 10%) */ p_ptr->max_exp += amount / 5; } /* Check Experience */ check_experience(); } /* * Lose experience */ void lose_exp(s32b amount) { /* Never drop below zero experience */ if (amount > p_ptr->exp) amount = p_ptr->exp; /* Lose some experience */ p_ptr->exp -= amount; /* Check Experience */ check_experience(); } /* * Make some noise */ void make_noise(byte amount) { int total = amount + p_ptr->state.noise_level; /* Paranoia (watching for overflow) */ if (total > MONSTER_FLOW_DEPTH) { total = MONSTER_FLOW_DEPTH; } /* Update the flow if this gets too high */ if (total >= 3 * MONSTER_FLOW_DEPTH / 4) { p_ptr->update |= PU_FLOW; } /* Save the new noise level */ p_ptr->state.noise_level = (byte)total; } /* * Various notice 'blah' functions */ /* * Change an item in the inventory */ void notice_inven(void) { /* Combine / Reorder the pack (later) */ p_ptr->notice |= (PN_COMBINE | PN_REORDER); /* Window stuff */ p_ptr->window |= (PW_INVEN); } /* * Change an item in the equipment */ void notice_equip(void) { /* Window stuff */ p_ptr->window |= (PW_EQUIP); } /* * Change an item somewhere */ void notice_item(void) { notice_inven(); notice_equip(); } /* * Something has happened to disturb the player. * * The arg indicates a major disturbance, which affects search. * * All disturbance cancels repeated commands, resting, and running. */ void disturb(bool stop_search) { /* Cancel repeated commands */ if (p_ptr->cmd.rep) { /* Cancel */ p_ptr->cmd.rep = 0; /* Redraw the state (later) */ p_ptr->redraw |= (PR_STATE); } /* Cancel Resting */ if (p_ptr->state.resting) { /* Cancel */ p_ptr->state.resting = 0; /* Redraw the state (later) */ p_ptr->redraw |= (PR_STATE); } /* Cancel running */ if (p_ptr->state.running) { /* Cancel */ p_ptr->state.running = 0; /* Check for new panel if appropriate */ if (center_player && avoid_center) verify_panel(); /* Calculate torch radius */ p_ptr->update |= (PU_TORCH); } /* Cancel searching if requested */ if (stop_search && p_ptr->state.searching) { /* Cancel */ p_ptr->state.searching = FALSE; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the state */ p_ptr->redraw |= (PR_STATE); } /* Flush the input if requested */ if (flush_disturb) flush(); } zangband/src/fields.c0000644000000000000000000010042110250356274013543 0ustar rootroot/* File: fields.c */ /* Purpose: Field code */ /* * Copyright (c) 2000 Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "grid.h" #include "script.h" /* * Is the field visible to the player? */ static bool field_visible(field_type *f_ptr) { int x = f_ptr->fx; int y = f_ptr->fy; /* Refuse "illegal" locations */ if (in_boundsp(x, y)) { /* Can the player see the square? */ if (player_has_los_grid(parea(x, y))) { return (TRUE); } } return (FALSE); } /* * Notice changes to a field */ void notice_field(field_type *f_ptr) { if (field_visible(f_ptr)) { /* Note + Lite the spot */ note_spot(f_ptr->fx, f_ptr->fy); } } /* * The name of a field */ cptr field_name(const field_type *f_ptr) { return (t_info[f_ptr->t_idx].name); } /* * Excise a field from a stack */ void excise_field_idx(int fld_idx) { /* Field */ field_type *f_ptr = &fld_list[fld_idx]; field_type *j_ptr = NULL; field_type *q_ptr; int y = j_ptr->fy; int x = j_ptr->fx; s16b *f_idx_ptr = &area(x, y)->fld_idx; /* Scan all fields in the list */ FLD_ITT_START (*f_idx_ptr, q_ptr) { /* Hack - Done? */ if (q_ptr == f_ptr) { /* No previous */ if (!j_ptr) { /* Remove from list */ *f_idx_ptr = q_ptr->next_f_idx; } /* Real previous */ else { /* Remove from list */ j_ptr->next_f_idx = q_ptr->next_f_idx; } /* Forget next pointer */ q_ptr->next_f_idx = 0; /* Done */ break; } /* Save previous object */ j_ptr = q_ptr; } FLD_ITT_END; } /* * Delete a field * * Handle "lists" of fields correctly. */ void delete_field_ptr(field_type *f_ptr) { int x = f_ptr->fx; int y = f_ptr->fy; cave_type *c_ptr = area(x, y); field_type *j_ptr; field_type *q_ptr = NULL; /* Paranoia */ if (f_ptr->region != cur_region) quit("Trying to find unregioned field"); /* Find field to delete */ FLD_ITT_START (c_ptr->fld_idx, j_ptr); { /* Found it? */ if (j_ptr == f_ptr) { /* Delete it */ if (q_ptr) { q_ptr->next_f_idx = f_ptr->next_f_idx; } else { c_ptr->fld_idx = f_ptr->next_f_idx; } /* Notice the change */ notice_field(f_ptr); /* Wipe the field */ field_wipe(f_ptr); /* Count fields */ fld_cnt--; return; } /* Remember previous field */ q_ptr = j_ptr; } FLD_ITT_END; /* We shouldn't get here! */ quit("Cannot find field to delete!"); return; } /* * Deletes all fields at given location * * Note it does not display the changes on screen */ void delete_field_location(cave_type *c_ptr) { field_type *f_ptr; /* Scan all fields in the grid */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Wipe the field */ field_wipe(f_ptr); /* Count fields */ fld_cnt--; } FLD_ITT_END; /* Nothing left */ c_ptr->fld_idx = 0; } /* * Deletes all fields at given location */ void delete_field(int x, int y) { cave_type *c_ptr; /* Refuse "illegal" locations */ if (!in_bounds2(x, y)) return; /* Grid */ c_ptr = area(x, y); delete_field_location(c_ptr); /* Paranoia */ if (!in_boundsp(x, y)) return; /* Note + Lite the spot */ if (character_dungeon) note_spot(x, y); } /* * Move a field from index i1 to index i2 in the field list */ static void compact_fields_aux(int i1, int i2) { int i; cave_type *c_ptr; field_type *f_ptr; int y, x; /* Do nothing */ if (i1 == i2) return; /* Repair fields */ for (i = 1; i < fld_max; i++) { /* Acquire field */ f_ptr = &fld_list[i]; /* Skip "dead" fields */ if (!f_ptr->t_idx) continue; /* Repair "next" pointers */ if (f_ptr->next_f_idx == i1) { /* Repair */ f_ptr->next_f_idx = i2; } } /* Acquire object */ f_ptr = &fld_list[i1]; /* Acquire location */ y = f_ptr->fy; x = f_ptr->fx; /* If the square exists */ if ((y) && (x)) { /* Acquire grid */ c_ptr = area(x, y); /* Repair grid */ if (c_ptr->fld_idx == i1) { /* Repair */ c_ptr->fld_idx = i2; } } /* Structure copy */ fld_list[i2] = fld_list[i1]; /* Wipe the hole */ field_wipe(f_ptr); } /* * Compact and Reorder the field list * * This function can be very dangerous, use with caution! * * When actually "compacting" field, we base the saving throw on a * combination of field level, distance from player, and current * "desperation". * * After "compacting" (if needed), we "reorder" the fields into a more * compact order, and we reset the allocation info, and the "live" array. */ void compact_fields(int size) { int px = p_ptr->px; int py = p_ptr->py; int y, x, num, cnt; int cur_lev, cur_dis, chance; s16b i; field_thaum *t_ptr; /* Compact */ if (size) { /* Message */ msgf("Compacting fields..."); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* Compact at least 'size' fields */ for (num = 0, cnt = 1; num < size; cnt++) { /* Get more vicious each iteration */ cur_lev = 5 * cnt; /* Get closer each iteration */ cur_dis = 5 * (20 - cnt); /* Examine the fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr = &fld_list[i]; byte fld_level = 100; /* Skip dead fields */ if (!f_ptr->t_idx) continue; /* Point to the field type */ t_ptr = &t_info[f_ptr->t_idx]; switch (t_ptr->type) { case FTYPE_NOTHING: { /* This field is dead - get rid of it */ fld_level = 0; break; } case FTYPE_TRAP: { /* Traps can be gotten rid of safely */ fld_level = 100; break; } case FTYPE_DOOR: { /* Spikes / locked doors can be turned off if required */ fld_level = 80; break; } case FTYPE_BUILD: { /* Buildings should not be removed */ fld_level = 255; break; } case FTYPE_FEAT: { /* General features are important */ fld_level = 250; break; } case FTYPE_QUEST: { /* Quest events can be important */ fld_level = 150; break; } case FTYPE_FIELD: { /* Does the field have a counter? */ if (t_ptr->count_init) { /* Compact fields that are nearly done */ fld_level = 20 + 40 * f_ptr->counter / t_ptr->count_init; } else { /* Permanent magical fields can be gotten rid of */ fld_level = 50; } break; } case FTYPE_CORPSE: { /* Corpses have no real value */ fld_level = f_ptr->counter / 10; break; } } /* Hack -- High level fields start out "immune" */ if (fld_level > cur_lev) continue; /* Get the location */ y = f_ptr->fy; x = f_ptr->fx; /* Nearby fields start out "immune" */ if ((cur_dis > 0) && (distance(px, py, x, y) < cur_dis)) continue; /* Saving throw */ chance = 90; /* Apply the saving throw */ if (randint0(100) < chance) continue; /* Call completion routine */ if (field_script_single(f_ptr, FIELD_ACT_EXIT, "b", LUA_VAR_NAMED(field_visible(f_ptr), "visible"))) { /* Handler failed to delete field - delete it by hand. */ delete_field_ptr(f_ptr); } /* Count it */ num++; } } /* Excise dead fields (backwards!) */ for (i = fld_max - 1; i >= 1; i--) { field_type *f_ptr = &fld_list[i]; /* Skip real fields */ if (f_ptr->t_idx) continue; /* Move last object into open hole */ compact_fields_aux(fld_max - 1, i); /* Compress "fld_max" */ fld_max--; } } /* * Delete all the fields when player leaves the level * * Note -- we do NOT visually reflect these (irrelevant) changes * * Hack -- we clear the "c_ptr->fld_idx" field for every grid * since we know we are clearing every field. Technically, * we only clear those grids containing fields, and we clear * it once for every such field. */ void wipe_f_list(void) { int i; cave_type *c_ptr; int y, x; /* Delete the existing fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr = &fld_list[i]; /* Skip dead fields */ if (!f_ptr->t_idx) continue; /* Access location */ y = f_ptr->fy; x = f_ptr->fx; /* Access grid */ c_ptr = area(x, y); /* Hack -- see above */ c_ptr->fld_idx = 0; /* Wipe the field */ field_wipe(f_ptr); } /* Reset "fld_max" */ fld_max = 1; /* Reset "fld_cnt" */ fld_cnt = 0; } /* * Wipe fields in region */ void wipe_fields(int rg_idx) { int i; /* Delete the existing fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr = &fld_list[i]; /* Skip dead fields */ if (!f_ptr->t_idx) continue; /* Enforce region */ if (f_ptr->region != rg_idx) continue; /* Hack - delete all fields on this square */ delete_field(f_ptr->fx, f_ptr->fy); } /* Compress the fields list */ compact_fields(0); } /* * Acquires and returns the index of a "free" field. * * This routine should almost never fail, but in case it does, * we must be sure to handle "failure" of this routine. */ s16b f_pop(void) { int i; /* Initial allocation */ if (fld_max < z_info->fld_max) { /* Get next space */ i = fld_max; /* Expand field array */ fld_max++; /* Count fields */ fld_cnt++; /* Use this field */ return (i); } /* Recycle dead fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr; /* Acquire field */ f_ptr = &fld_list[i]; /* Skip live fields */ if (f_ptr->t_idx) continue; /* Count fields */ fld_cnt++; /* Use this field */ return (i); } /* Warn the player (except during dungeon creation) */ if (character_dungeon) msgf("Too many fields!"); /* Oops */ return (0); } /* * Wipe an field clean. */ void field_wipe(field_type *f_ptr) { /* Wipe the structure */ (void)WIPE(f_ptr, field_type); } /* * Prepare a field based on an existing one */ void field_copy(field_type *f_ptr, field_type *j_ptr) { /* Copy the structure */ COPY(f_ptr, j_ptr, field_type); } /* * Add a field to a list of fields. * The list is sorted so that the field with the highest * priority is on top. * f_ptr is the field to add. */ s16b field_add(field_type *f_ptr, cave_type *c_ptr) { s16b new_idx; field_type *j_ptr; field_type *q_ptr = NULL; FLD_ITT_START (c_ptr->fld_idx, j_ptr) { /* Look for fields that can be merged */ if ((f_ptr->info & FIELD_INFO_MERGE) && (f_ptr->t_idx == j_ptr->t_idx)) { s32b counter; /* Merge the two together */ counter = j_ptr->counter + f_ptr->counter; /* Bounds checking */ if (counter > MAX_SHORT) counter = MAX_SHORT; /* Store in new counter */ j_ptr->counter = (s16b)counter; /* Return index */ if (q_ptr) { return (q_ptr->next_f_idx); } else { return (c_ptr->fld_idx); } } /* Sort in priority order */ if (j_ptr->priority < f_ptr->priority) break; /* Save previous object */ q_ptr = j_ptr; } FLD_ITT_END; /* Add the field to the list */ /* Get new node in list */ new_idx = f_pop(); if (!new_idx) return (0); /* Move field to location */ field_copy(&fld_list[new_idx], f_ptr); /* If a previous node exists */ if (q_ptr) { fld_list[new_idx].next_f_idx = q_ptr->next_f_idx; q_ptr->next_f_idx = new_idx; } else { /* No old node - just link directly */ fld_list[new_idx].next_f_idx = c_ptr->fld_idx; c_ptr->fld_idx = new_idx; } return (new_idx); } #ifdef UNUSED_FUNC /* * Sort a list of fields so that they are in priority order. * * Since the linked list is one-way. A simple bubble sort is * used. If this is too slow, perhaps hooks to the quicksort * routine should be made. * * This routine is so slow - it should really never be used. * If at all possible - use a merge sort based on the above code. */ static void field_sort_priority(s16b *fld_idx_ptr) { s16b *i_ptr, *j_ptr; s16b temp; bool swapped = TRUE; /* Paranoia - exit if list is empty */ if (!(*fld_idx_ptr)) return; /* Keep scanning until no more changes */ while (swapped) { /* Initialize */ i_ptr = fld_idx_ptr; j_ptr = &(fld_list[*i_ptr].next_f_idx); swapped = FALSE; /* Scan the list until the end */ while (*j_ptr) { /* Check to see if in order */ if (fld_list[*i_ptr].priority < fld_list[*j_ptr].priority) { /* swap the links */ temp = *i_ptr; *i_ptr = *j_ptr; *j_ptr = temp; swapped = TRUE; } /* Advance the pointers */ i_ptr = j_ptr; j_ptr = &(fld_list[*j_ptr].next_f_idx); } } } #endif /* UNUSED_FUNC */ /* * Prepare a field based on kind. */ void field_prep(field_type *f_ptr, s16b t_idx) { field_thaum *t_ptr; int i; /* Clear the record */ field_wipe(f_ptr); /* Save the kind index */ f_ptr->t_idx = t_idx; /* Get pointer to thaum type. */ t_ptr = &t_info[t_idx]; /* What it looks like */ f_ptr->f_attr = t_ptr->f_attr; f_ptr->f_char = t_ptr->f_char; f_ptr->priority = t_ptr->priority; f_ptr->counter = t_ptr->count_init; f_ptr->info = t_ptr->info; for (i = 0; i < 8; i++) { /* Store the value */ f_ptr->data[i] = t_ptr->data_init[i]; } } /* * Initialise all fields after the game has been loaded. */ void init_fields(void) { s16b fld_idx; field_type *f_ptr; field_thaum *t_ptr; for (fld_idx = 0; fld_idx < fld_max; fld_idx++) { /* Point to field */ f_ptr = &fld_list[fld_idx]; /* No dead fields */ if (!f_ptr->t_idx) continue; /* Get pointer to thaum type. */ t_ptr = &t_info[f_ptr->t_idx]; /* What it looks like */ f_ptr->f_attr = t_ptr->f_attr; f_ptr->f_char = t_ptr->f_char; /* Call loading routine */ (void) field_script_single(f_ptr, FIELD_ACT_LOAD, ""); } } /* * See if a field of a particular type is on a square. * (eg. call with (c_ptr, FTYPE_TRAP) to get traps) */ field_type *field_is_type(const cave_type *c_ptr, byte typ) { field_type *f_ptr; /* While the field exists */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Is it the correct type? */ if (t_info[f_ptr->t_idx].type == typ) return (f_ptr); } FLD_ITT_END; /* Nothing found */ return (NULL); } /* * Return the first known field of the requested type * in the list. */ field_type *field_first_known(const cave_type *c_ptr, byte typ) { field_type *f_ptr; /* While the field exists */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Is it known to be the correct type? */ if ((t_info[f_ptr->t_idx].type == typ) && ((f_ptr->info & (FIELD_INFO_MARK | FIELD_INFO_VIS)) == (FIELD_INFO_MARK | FIELD_INFO_VIS))) return (f_ptr); } FLD_ITT_END; /* Nothing found */ return (NULL); } /* * Set all fields of the given type to have INFO_VIS set. * Return TRUE if a field is "found" that was previously * unknown. */ bool field_detect_type(const cave_type *c_ptr, byte typ) { field_type *f_ptr; bool flag = FALSE; /* Scan the list */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Is it the correct type? */ if (t_info[f_ptr->t_idx].type == typ) { /* Is it invisible? */ if (!(f_ptr->info & FIELD_INFO_VIS)) { /* Now is visible + known */ f_ptr->info |= (FIELD_INFO_VIS | FIELD_INFO_MARK); /* Lookable */ f_ptr->info &= ~(FIELD_INFO_NO_LOOK); } else { /* We know it now */ f_ptr->info |= (FIELD_INFO_MARK); } /* We found something */ flag = TRUE; /* Note + Lite the spot */ note_spot(f_ptr->fx, f_ptr->fy); } } FLD_ITT_END; /* Return whether we found something or not */ return (flag); } /* * Destroy all fields of a given type in the list. */ void field_destroy_type(cave_type *c_ptr, byte typ) { field_type *f_ptr; /* While the field exists */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Is it the correct type? */ if (t_info[f_ptr->t_idx].type == typ) { /* Call completion routine */ field_script_single(f_ptr, FIELD_ACT_EXIT, "b", LUA_VAR_NAMED(field_visible(f_ptr), "visible")); } } FLD_ITT_END; } /* * See if flags are set in a list of fields * * This is used to see of a grid blocks movement or magic */ u16b fields_have_flags(const cave_type *c_ptr, u16b info) { field_type *f_ptr; u16b flags = 0; /* Scan the fields */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Or the flags together */ flags |= f_ptr->info; } FLD_ITT_END; return (flags & info); } /* * Place a field of a given type on a square */ field_type *place_field(int x, int y, s16b t_idx) { field_type *f_ptr; s16b fld_idx; region_info *ri_ptr = &ri_list[cur_region]; field_type temp_field; field_type *ft_ptr = &temp_field; /* Paranoia */ if ((t_idx <= 0) || (t_idx >= z_info->t_max)) return (NULL); /* Overlay regions are easy - just store the field type for later */ if (ri_ptr->flags & REGION_OVER) { cave_data[y][x].fld_idx = t_idx; /* Hack - we didn't actually place a real field */ return (NULL); } /* Make the field */ field_prep(ft_ptr, t_idx); /* Place it */ fld_idx = field_add(ft_ptr, area(x, y)); /* Paranoia */ if (!fld_idx) return (NULL); /* Get new field */ f_ptr = &fld_list[fld_idx]; /* Connect to ground */ f_ptr->fy = y; f_ptr->fx = x; /* Region */ f_ptr->region = cur_region; return (f_ptr); } /* * Call the script for the field *f_ptr. * * This function does not do a list of fields like field_script(). * * It returns FALSE if the field deleted itself, TRUE otherwise. */ bool field_script_single(field_type *f_ptr, int action, cptr format, ...) { va_list vp; cptr script; /* Point to the field */ field_thaum *t_ptr = &t_info[f_ptr->t_idx]; /* Paranoia - Is there a function to call? */ if (t_ptr->action[action]) { bool exists = TRUE; /* Begin the Varargs Stuff */ va_start(vp, format); /* Get script to use */ script = quark_str(t_ptr->action[action]); /* Call the action script */ if (apply_field_trigger(script, f_ptr, format, vp)) { /* The field wants to be deleted */ delete_field_ptr(f_ptr); /* The field no longer exists */ exists = FALSE; } /* End the Varargs Stuff */ va_end(vp); /* Does the field exist still? */ return (exists); } /* * XXX XXX Is this logic correct? * What should we do if the field doesn't have the function? */ /* Check for deletion */ if (f_ptr->t_idx) { /* The field is still there */ return TRUE; } else { /* The field has deleted itself */ return FALSE; } } /* * This is the const version of the above function. * The field cannot delete itself, which simplifies things. */ void field_script_const(const field_type *f_ptr, int action, cptr format, ...) { va_list vp; cptr script; /* Point to the field */ field_thaum *t_ptr = &t_info[f_ptr->t_idx]; /* Paranoia - Is there a function to call? */ if (t_ptr->action[action]) { /* Begin the Varargs Stuff */ va_start(vp, format); /* Get script to use */ script = quark_str(t_ptr->action[action]); /* Call the action script */ const_field_trigger(script, f_ptr, format, vp); /* End the Varargs Stuff */ va_end(vp); } } /* * Call the specified action script for each field * in the list at the square c_ptr * * Note the code must take into account fields deleting * themselves. */ void field_script(cave_type *c_ptr, int action, cptr format, ...) { field_type *f_ptr; field_thaum *t_ptr; cptr script; FLD_ITT_START (c_ptr->fld_idx, f_ptr); { /* Point to the field */ t_ptr = &t_info[f_ptr->t_idx]; /* Get script to use */ script = quark_str(t_ptr->action[action]); /* Paranoia - Is there a function to call? */ if (script) { va_list vp; /* Begin the Varargs Stuff */ va_start(vp, format); /* Call the action script */ if (apply_field_trigger(script, f_ptr, format, vp)) { /* The field wants to be deleted */ delete_field_ptr(f_ptr); } /* End the Varargs Stuff */ va_end(vp); } } FLD_ITT_END; } /* * Call the "special" action script for fields * in the specified list which match the required * field type. */ bool field_script_special(cave_type *c_ptr, u16b ftype, cptr format, ...) { field_type *f_ptr; field_thaum *t_ptr; bool deleted = FALSE; cptr script; FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Point to the field */ t_ptr = &t_info[f_ptr->t_idx]; /* Get script to use */ script = quark_str(t_ptr->action[FIELD_ACT_SPECIAL]); /* Check for the right field + existance of a function to call */ if ((t_ptr->type == ftype) && script) { va_list vp; /* Begin the Varargs Stuff */ va_start(vp, format); /* Call the action script */ if (apply_field_trigger(script, f_ptr, format, vp)) { /* The field wants to be deleted */ delete_field_ptr(f_ptr); deleted = TRUE; } /* End the Varargs Stuff */ va_end(vp); } } FLD_ITT_END; /* Was a field deleted? */ return (deleted); } /* * Call the required action function for the first field * in the specified list with that function. */ field_type *field_script_find(cave_type *c_ptr, int action, cptr format, ...) { field_type *f_ptr; field_thaum *t_ptr; va_list vp; cptr script; FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Point to the field */ t_ptr = &t_info[f_ptr->t_idx]; /* Get script to use */ script = quark_str(t_ptr->action[action]); if (script) { /* Begin the Varargs Stuff */ va_start(vp, format); /* Call the action script */ if (apply_field_trigger(script, f_ptr, format, vp)) { /* The field wants to be deleted */ delete_field_ptr(f_ptr); } /* End the Varargs Stuff */ va_end(vp); /* Done */ return (f_ptr); } } FLD_ITT_END; /* Found nothing */ return (NULL); } void process_fields(void) { s16b fld_idx; field_type *f_ptr; for (fld_idx = 0; fld_idx < fld_max; fld_idx++) { /* Point to field */ f_ptr = &fld_list[fld_idx]; /* No dead fields */ if (!f_ptr->t_idx) continue; /* If it is a temporary field, count down every 10 turns */ if ((f_ptr->info & FIELD_INFO_TEMP) && !(turn % 10)) { /* Decrement counter */ f_ptr->counter--; /* If at bottom */ if (!f_ptr->counter) { /* Call completion routine */ field_script_single(f_ptr, FIELD_ACT_EXIT, "b", LUA_VAR_NAMED(field_visible(f_ptr), "visible")); /* Nothing else to do now */ continue; } } } } /* * "Testing" function, used to find bugs. * It will test cave and field data structures. * (A similar function was used to detect the * "invisible monster" bug in the wilderness). */ void test_field_data_integrity(void) { int i, j; cave_type *c_ptr; field_type *f_ptr, *j_ptr; bool found; /* Test cave data structure */ for (i = p_ptr->min_wid; i < p_ptr->max_wid; i++) { for (j = p_ptr->min_hgt; j < p_ptr->max_hgt; j++) { /* Point to location */ c_ptr = area(i, j); /* Want a field */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Dead field? */ if (!f_ptr->t_idx) { msgf("Dead Field"); msgf("Field %d", _this_f_idx); } if (_this_f_idx > fld_max) { msgf("Field index inconsistancy."); } if ((f_ptr->fy != j) || (f_ptr->fx != i)) { msgf("Field location inconsistancy."); msgf("Field x, cave x,%d,%d", f_ptr->fx, i); msgf("Field y, cave y,%d,%d", f_ptr->fy, j); } } FLD_ITT_END; } } /* Test linkage to cave data structures */ for (i = 0; i < fld_max; i++) { f_ptr = &fld_list[i]; if (!f_ptr->t_idx) continue; /* Needs to be in bounds */ if (!in_bounds2(f_ptr->fx, f_ptr->fy)) { msgf("Field out of bounds: %d", i); continue; } c_ptr = area(f_ptr->fx, f_ptr->fy); found = FALSE; /* We need to be in the linked list at the location */ FLD_ITT_START (c_ptr->fld_idx, j_ptr) { if (f_ptr == j_ptr) { found = TRUE; break; } } FLD_ITT_END; if (!found) { msgf("Field not linked correctly (%d,%d): %d", f_ptr->fx, f_ptr->fy, i); } } } /* * Helper functions for fields lua scripts. */ /* * Set the size of a corpse - and change the fields picture. */ void set_corpse_size(field_type *f_ptr, int size) { /* Initialise the graphic */ if ((use_graphics == GRAPHICS_ADAM_BOLT) || (use_graphics == GRAPHICS_DAVID_GERVAIS)) { /* Paranoia */ if ((size > 0) && (size < 7)) { /* Hack - get new tile via offset table */ f_ptr->f_char += size; } } } /* * Traps code. * * data[0] Trap power (used to be always 5). * data[1] Check_hit (used to be always 125). * * data[3] Random number used to define effect in some cases. */ /* * Determine if a trap affects the player. * Always miss 5% of the time, Always hit 5% of the time. * Otherwise, match trap power against player armor. */ bool check_trap_hit(int power) { int k, ac; /* Percentile dice */ k = randint0(100); /* Hack -- 5% hit, 5% miss */ if (k < 10) return (k < 5); /* Paranoia -- No power */ if (power <= 0) return (FALSE); /* Total armor */ ac = p_ptr->ac + p_ptr->to_a; /* Power competes against Armor */ if (randint1(power) > ((ac * 3) / 4)) return (TRUE); /* Assume miss */ return (FALSE); } typedef struct field_trap_type field_trap_type; struct field_trap_type { /* Field type */ u16b t_idx; /* Average level found on */ byte level; }; /* Array used to work out which trap to place */ static field_trap_type trap_num[] = { {FT_TRAP_DOOR, 5}, {FT_TRAP_PIT, 5}, {FT_TRAP_SPIKE_PIT, 15}, {FT_TRAP_POISON_PIT, 30}, {FT_TRAP_CURSE, 20}, {FT_TRAP_TELEPORT, 40}, {FT_TRAP_ELEMENT, 10}, {FT_TRAP_BA_ELEMENT, 50}, {FT_TRAP_GAS, 5}, {FT_TRAP_TRAPS, 60}, {FT_TRAP_TEMP_STAT, 25}, {FT_TRAP_PERM_STAT, 70}, {FT_TRAP_LOSE_XP, 80}, {FT_TRAP_DISENCHANT, 35}, {FT_TRAP_DROP_ITEM, 55}, {FT_TRAP_MUTATE, 90}, {FT_TRAP_NEW_LIFE, 100}, {FT_TRAP_NO_LITE, 0}, {FT_TRAP_HUNGER, 0}, {FT_TRAP_NO_GOLD, 25}, {FT_TRAP_HASTE_MON, 15}, {FT_TRAP_RAISE_MON, 40}, {FT_TRAP_DRAIN_MAGIC, 65}, {FT_TRAP_AGGRAVATE, 15}, {FT_TRAP_SUMMON, 20}, {FT_TRAP_LOSE_MEMORY, 30}, {0, 0} }; /* * Places a random trap at the given location. * * The location must be a legal, naked, floor grid. */ void place_trap(int x, int y) { u16b t_idx; int tmp, total, i; field_trap_type *n_ptr = trap_num; field_type *f_ptr; /* Paranoia -- verify location */ if (!in_bounds2(x, y)) return; /* Calculate the total possibilities */ for (total = 0; TRUE; n_ptr++) { /* Note end */ if (!n_ptr->t_idx) break; /* Ignore excessive depth */ if (n_ptr->level > p_ptr->depth) continue; /* Count this possibility */ total += MAX_DEPTH / (p_ptr->depth - n_ptr->level + 15); } /* Pick a trap */ while (1) { /* Pick a random type */ tmp = randint0(total); /* Find this type */ for (n_ptr = trap_num, i = 0; TRUE; n_ptr++) { /* Note end - this should never happen */ if (!n_ptr->t_idx) { /* Go back one */ n_ptr--; /* exit */ break; } /* Ignore excessive depth */ if (n_ptr->level > p_ptr->depth) continue; /* Count this possibility */ i += MAX_DEPTH / (p_ptr->depth - n_ptr->level + 15); /* Found the type */ if (tmp < i) break; } t_idx = n_ptr->t_idx; /* Accept non-trapdoors */ if (t_idx != FT_TRAP_DOOR) break; /* Hack -- no trap doors on special levels */ if (is_special_level(p_ptr->depth)) continue; /* Probably should prevent trap doors in the wilderness */ if (!p_ptr->depth) continue; /* Hack -- no trap doors on the deepest level */ if (p_ptr->depth >= dungeon()->max_level) continue; break; } f_ptr = place_field(x, y, t_idx); /* Activate the trap */ if (f_ptr) { /* Initialise it */ (void)field_script_single(f_ptr, FIELD_ACT_INIT, ""); } } /* * Common stuff that happens whenever the player * hits a trap. * * The trap is noticed, and the player is disturbed */ void hit_trap(field_type *f_ptr) { /* Look for invisible traps and detect them. */ if (!(f_ptr->info & FIELD_INFO_VIS)) { /* Detect it. */ f_ptr->info |= FIELD_INFO_VIS; /* Trap is "lookable" */ f_ptr->info &= ~(FIELD_INFO_NO_LOOK); /* Message */ msgf("You found a trap!"); /* Notice the changes */ notice_field(f_ptr); } /* Disturb the player */ disturb(FALSE); } /* * Trap interaction functions. * What horrible fate awaits the player after stepping * on this particular trap? */ /* * Nasty cursing of equipment and player */ void evil_trap(void) { /* Curse the equipment */ curse_equipment(p_ptr->depth, p_ptr->depth / 10); /* TY Curse */ if (p_ptr->depth > randint1(100)) /* No nasty effect for low levels */ { bool stop_ty = FALSE; int count = 0; do { stop_ty = activate_ty_curse(stop_ty, &count); } while (one_in_(6)); } /* Blast weapon */ else if (p_ptr->depth > randint1(500)) /* No nasty effect for low levels */ { (void)curse_weapon(); } /* Blast armour */ else if (p_ptr->depth > randint1(500)) /* No nasty effect for low levels */ { (void)curse_armor(); } } /* Drop a random item from the players inventory */ void drop_random_item(void) { int item; object_type *o_ptr; /* Get the item to drop */ item = randint1(get_list_length(p_ptr->inventory)); o_ptr = get_list_item(p_ptr->inventory, item); /* Paranoia */ if (!o_ptr) return; /* Only if not cursed */ if (!cursed_p(o_ptr)) { /* Drop it */ inven_drop(o_ptr, o_ptr->number); } } void drain_lite(void) { object_type *o_ptr; /* Access the lite */ o_ptr = &p_ptr->equipment[EQUIP_LITE]; if ((o_ptr->k_idx) && ((o_ptr->sval == SV_LITE_LANTERN) || (o_ptr->sval == SV_LITE_TORCH))) { /* Drain all light. */ o_ptr->timeout = 0; } /* Recalculate torch */ p_ptr->update |= (PU_TORCH); /* Notice changes */ notice_equip(); /* Darkeness */ unlite_room(p_ptr->px, p_ptr->py); } void drain_food(void) { /* Only effect non-starving people */ if (p_ptr->food > PY_FOOD_WEAK) { /* You are very hungry */ (void)set_food(PY_FOOD_WEAK); } } void drain_magic(void) { object_type *o_ptr; /* Find an item */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Only work some of the time */ if (one_in_(2)) continue; /* Drain charged wands/staffs */ if (((o_ptr->tval == TV_STAFF) || (o_ptr->tval == TV_WAND)) && (o_ptr->pval)) { /* Uncharge */ if (o_ptr->tval == TV_WAND) o_ptr->ac += o_ptr->pval; o_ptr->pval = 0; /* Notice changes */ notice_inven(); } } OBJ_ITT_END; } /* * Make a locked or jammed door on a square */ void make_lockjam_door(int x, int y, int power, bool jam) { cave_type *c_ptr; field_type *f_ptr; int old_power = 0; /* Overlays are simpler */ if (ri_list[cur_region].flags & REGION_OVER) { /* Make a closed door on the square */ set_feat_bold(x, y, FEAT_CLOSED); /* Make a new field */ if (jam) { /* Add a jammed door field */ (void) place_field(x, y, FT_JAM_DOOR); } else { /* Add a locked door field */ (void) place_field(x, y, FT_LOCK_DOOR); } return; } /* Make a closed door on the square */ cave_set_feat(x, y, FEAT_CLOSED); c_ptr = area(x, y); f_ptr = field_is_type(c_ptr, FTYPE_DOOR); /* look for a door field on the square */ if (f_ptr) { /* There already is a door field here... */ /* HACK - Look at type */ if (!((f_ptr->t_idx == FT_JAM_DOOR) || (f_ptr->t_idx == FT_LOCK_DOOR))) { /* * Not a locked door or a jammed door. * * Probably a store or building... exit. */ msgf("Cannot make door! Already one there!"); /* Exit */ return; } /* Save old power */ old_power = f_ptr->counter; /* Get rid of old field */ delete_field_ptr(f_ptr); } /* Make a new field */ if (jam) { /* Add a jammed door field */ f_ptr = place_field(x, y, FT_JAM_DOOR); } else { /* Add a locked door field */ f_ptr = place_field(x, y, FT_LOCK_DOOR); } /* It didn't work for some reason? */ if (!f_ptr) { msgf("Cannot make door! Too many fields."); return; } power = power + old_power; /* * Initialise it. */ (void)field_script_single(f_ptr, FIELD_ACT_INIT, "i:", LUA_VAR(power)); } /* * Can the given monster race open the locked/jammed door. * * Used to be (randint0(m_ptr->hp) > power * power) * Now just uses average hp (from full to wounded) so * we don't have to export the monster internals to lua. */ bool monster_can_open(monster_race *r_ptr, int power) { return (randint0(r_ptr->hdice * r_ptr->hside / 4) > power * power); } /* * Take num strings in an array, and then print them on the screen. * * The strings are centered on the term. */ void print_building_options(cptr strings[], int num) { int i; int max = 0, len; int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Get size of longest string */ for (i = 0; i < num; i++) { len = strlen(strings[i]); if (len > max) max = len; } /* Print them out */ for (i = 0; i < num; i++) { put_fstr(40 - len / 2, 20 - num + i, CLR_YELLOW "%s", strings[i]); } } zangband/src/files.c0000755000000000000000000024522710250356274013420 0ustar rootroot/* File: files.c */ /* Purpose: code dealing with pref files (and death) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* * Extract the first few "tokens" from a buffer * * This function uses "colon" and "slash" as the delimeter characters. * * We never extract more than "num" tokens. The "last" token may include * "delimeter" characters, allowing the buffer to include a "string" token. * * We save pointers to the tokens in "tokens", and return the number found. * * Hack -- Attempt to handle the 'c' character formalism * * Hack -- An empty buffer, or a final delimeter, yields an "empty" token. * * Hack -- We will always extract at least one token */ s16b tokenize(char *buf, s16b num, char **tokens, int mode) { int i = 0; char *s = buf; /* Process */ while (i < num - 1) { char *t; /* Scan the string */ for (t = s; *t; t++) { /* Found a delimiter */ if ((*t == ':') || (*t == '/')) break; /* Handle single quotes */ if ((mode & TOKENIZE_CHECKQUOTE) && (*t == '\'')) { /* Advance */ t++; /* Handle backslash */ if (*t == '\\') t++; /* Require a character */ if (!*t) break; /* Advance */ t++; /* Hack -- Require a close quote */ if (*t != '\'') *t = '\''; } /* Handle back-slash */ if (*t == '\\') t++; } /* Nothing left */ if (!*t) break; /* Nuke and advance */ *t++ = '\0'; /* Save the token */ tokens[i++] = s; /* Advance */ s = t; } /* Save the token */ tokens[i++] = s; /* Number found */ return (i); } /* A number with a name */ typedef struct named_num named_num; struct named_num { cptr name; /* The name of this thing */ int num; /* A number associated with it */ }; /* Index of spell type names */ static const named_num gf_desc[] = { {"GF_ELEC", GF_ELEC}, {"GF_POIS", GF_POIS}, {"GF_ACID", GF_ACID}, {"GF_COLD", GF_COLD}, {"GF_FIRE", GF_FIRE}, {"GF_MISSILE", GF_MISSILE}, {"GF_ARROW", GF_ARROW}, {"GF_PLASMA", GF_PLASMA}, {"GF_WATER", GF_WATER}, {"GF_LITE", GF_LITE}, {"GF_DARK", GF_DARK}, {"GF_LITE_WEAK", GF_LITE_WEAK}, {"GF_DARK_WEAK", GF_DARK_WEAK}, {"GF_SHARDS", GF_SHARDS}, {"GF_SOUND", GF_SOUND}, {"GF_CONFUSION", GF_CONFUSION}, {"GF_FORCE", GF_FORCE}, {"GF_INERTIA", GF_INERTIA}, {"GF_MANA", GF_MANA}, {"GF_METEOR", GF_METEOR}, {"GF_ICE", GF_ICE}, {"GF_CHAOS", GF_CHAOS}, {"GF_NETHER", GF_NETHER}, {"GF_DISENCHANT", GF_DISENCHANT}, {"GF_NEXUS", GF_NEXUS}, {"GF_TIME", GF_TIME}, {"GF_GRAVITY", GF_GRAVITY}, {"GF_KILL_WALL", GF_KILL_WALL}, {"GF_KILL_DOOR", GF_KILL_DOOR}, {"GF_KILL_TRAP", GF_KILL_TRAP}, {"GF_MAKE_WALL", GF_MAKE_WALL}, {"GF_MAKE_DOOR", GF_MAKE_DOOR}, {"GF_MAKE_TRAP", GF_MAKE_TRAP}, {"GF_OLD_CLONE", GF_OLD_CLONE}, {"GF_OLD_POLY", GF_OLD_POLY}, {"GF_OLD_HEAL", GF_OLD_HEAL}, {"GF_OLD_SPEED", GF_OLD_SPEED}, {"GF_OLD_SLOW", GF_OLD_SLOW}, {"GF_OLD_CONF", GF_OLD_CONF}, {"GF_OLD_SLEEP", GF_OLD_SLEEP}, {"GF_OLD_DRAIN", GF_OLD_DRAIN}, {"GF_NEW_DRAIN", GF_NEW_DRAIN}, {"GF_AWAY_UNDEAD", GF_AWAY_UNDEAD}, {"GF_AWAY_EVIL", GF_AWAY_EVIL}, {"GF_AWAY_ALL", GF_AWAY_ALL}, {"GF_TURN_UNDEAD", GF_TURN_UNDEAD}, {"GF_TURN_EVIL", GF_TURN_EVIL}, {"GF_TURN_ALL", GF_TURN_ALL}, {"GF_DISP_UNDEAD", GF_DISP_UNDEAD}, {"GF_DISP_EVIL", GF_DISP_EVIL}, {"GF_DISP_ALL", GF_DISP_ALL}, {"GF_DISP_DEMON", GF_DISP_DEMON}, {"GF_DISP_LIVING", GF_DISP_LIVING}, {"GF_ROCKET", GF_ROCKET}, {"GF_NUKE", GF_NUKE}, {"GF_MAKE_GLYPH", GF_MAKE_GLYPH}, {"GF_STASIS", GF_STASIS}, {"GF_STONE_WALL", GF_STONE_WALL}, {"GF_DEATH_RAY", GF_DEATH_RAY}, {"GF_STUN", GF_STUN}, {"GF_HOLY_FIRE", GF_HOLY_FIRE}, {"GF_HELL_FIRE", GF_HELL_FIRE}, {"GF_DISINTEGRATE", GF_DISINTEGRATE}, {"GF_CHARM", GF_CHARM}, {"GF_CONTROL_UNDEAD", GF_CONTROL_UNDEAD}, {"GF_CONTROL_ANIMAL", GF_CONTROL_ANIMAL}, {"GF_PSI", GF_PSI}, {"GF_PSI_DRAIN", GF_PSI_DRAIN}, {"GF_TELEKINESIS", GF_TELEKINESIS}, {"GF_JAM_DOOR", GF_JAM_DOOR}, {"GF_DOMINATION", GF_DOMINATION}, {"GF_DISP_GOOD", GF_DISP_GOOD}, {NULL, 0} }; /* * Parse a sub-file of the "extra info" (format shown below) * * Each "action" line has an "action symbol" in the first column, * followed by a colon, followed by some command specific info, * usually in the form of "tokens" separated by colons or slashes. * * Blank lines, lines starting with white space, and lines starting * with pound signs ("#") are ignored (as comments). * * Note the use of "tokenize()" to allow the use of both colons and * slashes as delimeters, while still allowing final tokens which * may contain any characters including "delimiters". * * Note the use of "strtol()" to allow all "integers" to be encoded * in decimal, hexidecimal, or octal form. * * Note that "monster zero" is used for the "player" attr/char, "object * zero" will be used for the "stack" attr/char, and "feature zero" is * used for the "nothing" attr/char. * * Parse another file recursively, see below for details. * %: * * Specify the attr/char values for "monsters" by race index. * R::: * * Specify the attr/char values for "objects" by kind index. * K::: * * Specify the attr/char values for "features" by feature index. * F::: * * Specify the attr/char values for "fields" by field index. * T::: * * Specify the attr/char values for unaware "objects" by kind tval. * U::: * * Specify the attr/char values for inventory "objects" by kind tval. * E::: * * Define a macro action, given an encoded macro action. * A: * * Create a normal macro, given an encoded macro trigger. * P: * * Create a command macro, given an encoded macro trigger. * C: * * Create a keyset mapping. * S::: * * Turn an option off, given its name. * X: * * Turn an option on, given its name. * Y: * * Specify visual information, given an index, and some data. * V::::: * * Specify colors for message-types. * M:: * * Specify the set of colors to use when drawing a zapped spell. * Z:: */ errr process_pref_file_command(char *buf) { int i, j, n1, n2; char *zz[16]; /* Skip "empty" lines */ if (!buf[0]) return (0); /* Skip "blank" lines */ if (isspace(buf[0])) return (0); /* Skip comments */ if (buf[0] == '#') return (0); /* Require "?:*" format */ if (buf[1] != ':') return (1); /* Process "%:" */ if (buf[0] == '%') { /* Attempt to Process the given file */ return (process_pref_file("%s", buf + 2)); } /* Process "R::/" -- attr/char for monster races */ if (buf[0] == 'R') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { monster_race *r_ptr; i = (huge) strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if ((i < 0) || (i >= z_info->r_max)) return (1); r_ptr = &r_info[i]; if (n1) r_ptr->x_attr = n1; if (n2) r_ptr->x_char = n2; return (0); } } /* Process "K::/" -- attr/char for object kinds */ else if (buf[0] == 'K') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { object_kind *k_ptr; i = (huge) strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if ((i < 0) || (i >= z_info->k_max)) return (1); k_ptr = &k_info[i]; if (n1) k_ptr->x_attr = n1; if (n2) k_ptr->x_char = n2; return (0); } } /* Process "T::/" -- attr/char for fields */ else if (buf[0] == 'T') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { field_thaum *t_ptr; i = (huge) strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if ((i < 0) || (i >= z_info->t_max)) return (1); t_ptr = &t_info[i]; if (n1) t_ptr->f_attr = n1; if (n2) t_ptr->f_char = n2; return (0); } } /* Process "F::/" -- attr/char for terrain features */ else if (buf[0] == 'F') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { feature_type *f_ptr; i = (huge) strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if ((i < 0) || (i >= z_info->f_max)) return (1); f_ptr = &f_info[i]; if (n1) f_ptr->x_attr = n1; if (n2) f_ptr->x_char = n2; return (0); } } /* Process "W::/" -- xtra attr/char for terrain features */ else if (buf[0] == 'W') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { feature_type *f_ptr; i = (huge) strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if ((i < 0) || (i >= z_info->f_max)) return (1); f_ptr = &f_info[i]; if (n1) f_ptr->w_attr = n1; if (n2) f_ptr->w_char = n2; return (0); } } /* Process "S::/" -- attr/char for special things */ else if (buf[0] == 'S') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { j = (byte)strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if ((j < 0) || (j >= 256)) return (1); misc_to_attr[j] = n1; misc_to_char[j] = n2; return (0); } } /* Process "U::/" -- attr/char for unaware items */ else if (buf[0] == 'U') { if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) == 3) { j = (huge) strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); for (i = 1; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; if (k_ptr->tval == j) { if (n1) k_ptr->d_attr = n1; if (n2) k_ptr->d_char = n2; } } return (0); } } /* Process "E::" -- attribute for inventory objects */ else if (buf[0] == 'E') { if (tokenize(buf + 2, 2, zz, TOKENIZE_CHECKQUOTE) == 2) { j = (byte)strtol(zz[0], NULL, 0) % 128; n1 = strtol(zz[1], NULL, 0); if ((j < 0) || (j >= 128)) return (1); if (n1) tval_to_attr[j] = n1; return (0); } } /* Process "A:" -- save an "action" for later */ else if (buf[0] == 'A') { text_to_ascii(macro__buf, buf + 2); return (0); } /* Process "P:" -- normal macro */ else if (buf[0] == 'P') { char tmp[1024]; text_to_ascii(tmp, buf + 2); macro_add(tmp, macro__buf); return (0); } /* Process "C:" -- create keymap */ else if (buf[0] == 'C') { int mode; char tmp[1024]; if (tokenize(buf + 2, 2, zz, TOKENIZE_CHECKQUOTE) != 2) return (1); mode = strtol(zz[0], NULL, 0); if ((mode < 0) || (mode >= KEYMAP_MODES)) return (1); text_to_ascii(tmp, zz[1]); if (!tmp[0] || tmp[1]) return (1); i = (byte)(tmp[0]); string_free(keymap_act[mode][i]); keymap_act[mode][i] = string_make(macro__buf); return (0); } /* Process "V:::::" -- visual info */ else if (buf[0] == 'V') { if (tokenize(buf + 2, 5, zz, TOKENIZE_CHECKQUOTE) == 5) { i = (byte)strtol(zz[0], NULL, 0); if ((i < 0) || (i >= 256)) return (1); angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0); angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0); angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0); angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0); return (0); } } /* Process "X:" -- turn option off */ else if (buf[0] == 'X') { for (i = 0; i < OPT_MAX; i++) { if (option_info[i].o_desc && (option_info[i].o_page != OPT_BIRTH_PAGE) && option_info[i].o_text && streq(option_info[i].o_text, buf + 2)) { /* Clear */ option_info[i].o_val = FALSE; /* Save the change */ if (character_generated) { init_options(OPT_FLAG_SERVER | OPT_FLAG_PLAYER); } else { init_options(OPT_FLAG_BIRTH | OPT_FLAG_SERVER | OPT_FLAG_PLAYER); } return (0); } } /* XXX XXX XXX - ignore unknown or birth options */ return (0); } /* Process "Y:" -- turn option on */ else if (buf[0] == 'Y') { for (i = 0; i < OPT_MAX; i++) { if (option_info[i].o_desc && (option_info[i].o_page != OPT_BIRTH_PAGE) && option_info[i].o_text && streq(option_info[i].o_text, buf + 2)) { /* Set */ option_info[i].o_val = TRUE; /* Save the change */ if (character_generated) { init_options(OPT_FLAG_SERVER | OPT_FLAG_PLAYER); } else { init_options(OPT_FLAG_BIRTH | OPT_FLAG_SERVER | OPT_FLAG_PLAYER); } return (0); } } /* XXX XXX XXX - ignore unknown or birth options */ return (0); } /* Process "Z::" -- set spell color */ else if (buf[0] == 'Z') { /* Find the colon */ char *t = strchr(buf + 2, ':'); /* Oops */ if (!t) return (1); /* Nuke the colon */ *(t++) = '\0'; for (i = 0; gf_desc[i].name; i++) { /* Match this type */ if (streq(gf_desc[i].name, buf + 2)) { /* Wipe old strings */ if (gf_color[gf_desc[i].num]) { string_free(gf_color[gf_desc[i].num]); } /* Remember this color set */ gf_color[gf_desc[i].num] = string_make(t); /* Success */ return (0); } } } /* Process "M::" -- colors for message-types */ else if (buf[0] == 'M') { if (tokenize(buf + 2, 2, zz, TOKENIZE_CHECKQUOTE) == 2) { u16b type = (u16b)strtol(zz[0], NULL, 0); int color = color_char_to_attr(zz[1][0]); /* Ignore illegal color */ if (color < 0) return (1); /* Store the color */ return (message_color_define(type, (byte)color)); } } /* Failure */ return (1); } /* * Helper function for "process_pref_file()" * * Input: * v: output buffer array * f: final character * * Output: * result */ static cptr process_pref_file_expr(char **sp, char *fp) { cptr v; char *b; char *s; char b1 = '['; char b2 = ']'; char f = ' '; /* Initial */ s = (*sp); /* Skip spaces */ while (isspace(*s)) s++; /* Save start */ b = s; /* Default */ v = "?o?o?"; /* Analyze */ if (*s == b1) { const char *p; const char *t; /* Skip b1 */ s++; /* First */ t = process_pref_file_expr(&s, &f); /* Oops */ if (!*t) { /* Nothing */ } /* Function: IOR */ else if (streq(t, "IOR")) { v = "0"; while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); if (*t && !streq(t, "0")) v = "1"; } } /* Function: AND */ else if (streq(t, "AND")) { v = "1"; while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); if (*t && streq(t, "0")) v = "0"; } } /* Function: NOT */ else if (streq(t, "NOT")) { v = "1"; while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); if (*t && !streq(t, "0")) v = "0"; } } /* Function: EQU */ else if (streq(t, "EQU")) { v = "1"; if (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } while (*s && (f != b2)) { p = t; t = process_pref_file_expr(&s, &f); if (*t && !streq(p, t)) v = "0"; } } /* Function: LEQ */ else if (streq(t, "LEQ")) { v = "1"; if (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } while (*s && (f != b2)) { p = t; t = process_pref_file_expr(&s, &f); if (*t && (strcmp(p, t) >= 0)) v = "0"; } } /* Function: GEQ */ else if (streq(t, "GEQ")) { v = "1"; if (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } while (*s && (f != b2)) { p = t; t = process_pref_file_expr(&s, &f); if (*t && (strcmp(p, t) <= 0)) v = "0"; } } /* Oops */ else { while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } } /* Verify ending */ if (f != b2) v = "?x?x?"; /* Extract final and Terminate */ if ((f = *s) != '\0') *s++ = '\0'; } /* Other */ else { /* Accept all printables except spaces and brackets */ while (isprint(*s) && !strchr(" []", *s)) ++s; /* Extract final and Terminate */ if ((f = *s) != '\0') *s++ = '\0'; /* Variable */ if (*b == '$') { /* System */ if (streq(b + 1, "SYS")) { v = ANGBAND_SYS; } /* Graphics */ else if (streq(b + 1, "GRAF")) { switch (use_graphics) { case GRAPHICS_NONE: v = "none"; break; case GRAPHICS_ORIGINAL: v = "old"; break; case GRAPHICS_ADAM_BOLT: v = "new"; break; case GRAPHICS_DAVID_GERVAIS: v = "david"; break; case GRAPHICS_ANY: v = "error"; break; case GRAPHICS_HALF_3D: v = "none"; break; } } /* Monochrome mode */ else if (streq(b + 1, "MONOCHROME")) { if (arg_monochrome) v = "ON"; else v = "OFF"; } /* Race */ else if (streq(b + 1, "RACE")) { v = rp_ptr->title; } /* Class */ else if (streq(b + 1, "CLASS")) { v = cp_ptr->title; } /* Player */ else if (streq(b + 1, "PLAYER")) { v = player_base; } /* Town */ else if (streq(b + 1, "TOWN")) { v = vanilla_town ? "VANILLA" : "WILDERNESS"; } } /* Constant */ else { v = b; } } /* Save */ (*fp) = f; /* Save */ (*sp) = s; /* Result */ return (v); } /* * Open the "user pref file" and parse it. */ static errr process_pref_file_aux(cptr name) { FILE *fp; char buf[1024]; char old[1024]; int line = -1; errr err = 0; bool bypass = FALSE; /* Open the file */ fp = my_fopen(name, "r"); /* No such file */ if (!fp) return (-1); /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { /* Count lines */ line++; /* Skip "empty" lines */ if (!buf[0]) continue; /* Skip "blank" lines */ if (isspace(buf[0])) continue; /* Skip comments */ if (buf[0] == '#') continue; /* Save a copy */ strcpy(old, buf); /* Process "?:" */ if ((buf[0] == '?') && (buf[1] == ':')) { char f; cptr v; char *s; /* Start */ s = buf + 2; /* Parse the expr */ v = process_pref_file_expr(&s, &f); /* Set flag */ bypass = (streq(v, "0") ? TRUE : FALSE); /* Continue */ continue; } /* Apply conditionals */ if (bypass) continue; /* Process "%:" */ if (buf[0] == '%') { /* Process that file if allowed */ (void)process_pref_file("%s", buf + 2); /* Continue */ continue; } /* Process the line */ err = process_pref_file_command(buf); /* Oops */ if (err) break; } /* Error */ if (err) { /* Print error message */ /* ToDo: Add better error messages */ msgf("Error %d in line %d of file '%s'.", err, line, name); msgf("Parsing '%s'", old); message_flush(); } /* Close the file */ my_fclose(fp); /* Result */ return (err); } /* * Process the "user pref file" with the given name * * See the functions above for a list of legal "commands". * * We also accept the special "?" and "%" directives, which * allow conditional evaluation and filename inclusion. */ errr process_pref_file(cptr fmt, ...) { char buf[1024], name[1024]; errr err = 0; va_list vp; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(name, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Build the filename */ path_make(buf, ANGBAND_DIR_PREF, name); /* Process the pref file */ err = process_pref_file_aux(buf); /* Stop at parser errors, but not at non-existing file */ if (err < 1) { /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, name); /* Process the pref file */ err = process_pref_file_aux(buf); } /* Result */ return (err); } /* * Print number with header at given row, column */ static void prt_num(int col, int row, cptr header, long num, int wid) { put_fstr(col, row, "%s %*ld", header, wid, num); } /* * Returns a "rating" of x depending on y * * Where x is the first parameter in the list, * and y is the second. */ void likert(char *buf, uint max, cptr fmt, va_list *vp) { cptr desc; int x, y; /* Unused parameter */ (void)fmt; /* Get the first argument */ x = va_arg(*vp, int); /* Get the second argument */ y = va_arg(*vp, int); /* Paranoia */ if (y <= 0) y = 1; /* Negative value */ if (x < 0) { desc = CLR_L_DARK "Very Bad"; } else { /* Analyze the value */ switch (x / y) { case 0: case 1: { desc = CLR_RED "Bad"; break; } case 2: { desc = CLR_L_RED "Poor"; break; } case 3: case 4: { desc = CLR_ORANGE "Fair"; break; } case 5: { desc = CLR_YELLOW "Good"; break; } case 6: { desc = CLR_YELLOW "Very Good"; break; } case 7: case 8: { desc = CLR_YELLOW "Excellent"; break; } case 9: case 10: case 11: case 12: case 13: { desc = CLR_GREEN "Superb"; break; } case 14: case 15: case 16: case 17: { desc = CLR_BLUE "Chaos Rank"; break; } default: { strnfmt(buf, max, CLR_VIOLET "Amber [%d]", (int)((((x / y) - 17) * 5) / 2)); return; } } } /* Copy into the buffer */ strncpy(buf, desc, max - 1); } /* Monk average attack damage - only used here, so not in tables.c */ static int monk_avg_damage[PY_MAX_LEVEL + 1] = { 0, 250, 275, 299, 299, 306, 309, 321, 325, 328, 332, 347, 353, 375, 450, 463, 507, 523, 537, 551, 575, 680, 704, 723, 738, 768, 792, 812, 925, 1008, 1032, 1061, 1074, 1160, 1178, 1303, 1326, 1400, 1435, 1476, 1500, 1669, 1809, 1836, 1875, 2155, 2190, 2227, 2587, 2769, 2811 }; #define COL_SKILLS1 0 #define COL_SKILLS2 29 #define COL_SKILLS3 58 /* * Prints ratings on certain abilities * * This code is "imitated" elsewhere to "dump" a character sheet. */ static void display_player_abilities(void) { int tmp, damdice, damsides, dambonus, blows; int xthn, xthb; int muta_att = 0; long avgdam; int energy_fire; int shots, shot_frac; object_type *o_ptr; if (p_ptr->muta2 & MUT2_HORNS) muta_att++; if (p_ptr->muta2 & MUT2_SCOR_TAIL) muta_att++; if (p_ptr->muta2 & MUT2_BEAK) muta_att++; if (p_ptr->muta2 & MUT2_TRUNK) muta_att++; if (p_ptr->muta2 & MUT2_TENTACLES) muta_att++; /* Fighting Skill (with current weapon) */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; tmp = p_ptr->to_h + o_ptr->to_h; xthn = p_ptr->skills[SKILL_THN] + (tmp * BTH_PLUS_ADJ); /* Shooting Skill (with current bow and normal missile) */ o_ptr = &p_ptr->equipment[EQUIP_BOW]; tmp = p_ptr->to_h + o_ptr->to_h; xthb = p_ptr->skills[SKILL_THB] + (tmp * BTH_PLUS_ADJ); /* Is the player is wielding a shooter? */ if (o_ptr->k_idx) { energy_fire = p_ptr->bow_energy; } else { energy_fire = 100; } /* Calculate shots per round - note "strange" formula. */ /* The real number of shots per round is (1 + n)/2 */ shots = (1 + p_ptr->num_fire) * 50; shot_frac = (shots * 100 / energy_fire) % 100; shots = shots / energy_fire; /* Average damage per round */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; dambonus = p_ptr->dis_to_d; if (object_known_p(o_ptr)) dambonus += o_ptr->to_d; damdice = o_ptr->dd; damsides = o_ptr->ds; blows = p_ptr->num_blow; /* Basic abilities */ put_fstr(COL_SKILLS1, 16, CLR_WHITE "Fighting : %v\n" CLR_WHITE "Bows/Throw : %v\n" CLR_WHITE "Saving Throw: %v\n" CLR_WHITE "Stealth : %v", likert, xthn, 10, likert, xthb, 10, likert, p_ptr->skills[SKILL_SAV], 10, likert, p_ptr->skills[SKILL_STL], 1); put_fstr(COL_SKILLS2, 16, CLR_WHITE "Perception : %v\n" CLR_WHITE "Sensing : %v\n" CLR_WHITE "Disarming : %v\n" CLR_WHITE "Magic Device: %v", likert, p_ptr->skills[SKILL_FOS], 6, likert, p_ptr->skills[SKILL_SNS], 6, likert, p_ptr->skills[SKILL_DIS], 8, likert, p_ptr->skills[SKILL_DEV], 6); if (!muta_att) put_fstr(COL_SKILLS3, 16, "Blows/Round : %d", p_ptr->num_blow); else put_fstr(COL_SKILLS3, 16, "Blows/Round : %d+%d", p_ptr->num_blow, muta_att); put_fstr(COL_SKILLS3, 17, "Shots/Round : %d.%d", shots, shot_frac); /* Effect of damage dice x2 */ avgdam = avg_dam(dambonus, damdice, damsides); /* number of blows */ avgdam *= blows; /* Rescale */ avgdam /= 200; /* See if have a weapon with extra power */ if (o_ptr->k_idx) { int dam = avgdam / blows; int vorpal_chance = (o_ptr->flags[0] & TR0_VORPAL) ? 2 : 0; int ghoul_paral = 0; int drain_power = 0; bool do_quake = FALSE; bool do_conf = FALSE; bool do_tele = FALSE; bool do_poly = FALSE; /* Apply special scripts */ apply_object_trigger(TRIGGER_HIT, o_ptr, ":iiibbbbi", LUA_RETURN(ghoul_paral), LUA_RETURN(drain_power), LUA_RETURN(vorpal_chance), LUA_RETURN(do_quake), LUA_RETURN(do_conf), LUA_RETURN(do_tele), LUA_RETURN(do_poly), LUA_RETURN(dam)); /* Apply special damage boost */ avgdam = avgdam * dam / (avgdam / blows); /* Is there a vorpal effect we know about? */ if (vorpal_chance == 1) { /* vorpal blade */ avgdam *= 786; avgdam /= 500; } else if (vorpal_chance == 2) { /* vorpal flag only */ avgdam *= 609; avgdam /= 500; } /* Estimate the effect of increased criticals */ /* The average critical does 1.8345 * normal damage... */ if (object_known_p(o_ptr) && (p_ptr->msp >= PSI_COST) && (FLAG(o_ptr, TR_PSI_CRIT))) { avgdam *= 640; avgdam /= 500; } } /* normal players get two 1d1 punches */ if (!o_ptr->k_idx && (p_ptr->rp.pclass != CLASS_MONK)) avgdam = 2; if (avgdam == 0) { if ((p_ptr->rp.pclass == CLASS_MONK) && (!o_ptr->k_idx)) put_fstr(COL_SKILLS3, 18, "Avg.Dam./Rnd: %d", monk_avg_damage[p_ptr->lev] * blows / 100); else put_fstr(COL_SKILLS3, 18, "Avg.Dam./Rnd: nil!"); } else { put_fstr(COL_SKILLS3, 18, "Avg.Dam./Rnd: %d", (int)avgdam); } put_fstr(COL_SKILLS3, 19, "Infra-Vision: %d'", p_ptr->see_infra * 10); } /* * Obtain the "flags" for the player as if he was an item */ void player_flags(object_flags *of_ptr) { /* Clear */ of_ptr->flags[0] = 0L; of_ptr->flags[1] = 0L; of_ptr->flags[2] = 0L; of_ptr->flags[3] = 0L; /* Classes */ switch (p_ptr->rp.pclass) { case CLASS_WARRIOR: if (p_ptr->lev > 29) SET_FLAG(of_ptr, TR_RES_FEAR); break; case CLASS_PALADIN: if (p_ptr->lev > 39) SET_FLAG(of_ptr, TR_RES_FEAR); break; case CLASS_RANGER: SET_FLAG(of_ptr, TR_WILD_SHOT); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_WILD_WALK); break; case CLASS_CHAOS_WARRIOR: SET_FLAG(of_ptr, TR_PATRON); if (p_ptr->lev > 29) SET_FLAG(of_ptr, TR_RES_CHAOS); if (p_ptr->lev > 39) SET_FLAG(of_ptr, TR_RES_FEAR); break; case CLASS_MONK: /* Monks get extra abilities if unencumbered */ if (!p_ptr->state.monk_armour_stat) { if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_SPEED); if (p_ptr->lev > 24) SET_FLAG(of_ptr, TR_FREE_ACT); } break; case CLASS_MINDCRAFTER: if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_RES_FEAR); if (p_ptr->lev > 19) SET_FLAG(of_ptr, TR_SUST_WIS); if (p_ptr->lev > 29) SET_FLAG(of_ptr, TR_RES_CONF); if (p_ptr->lev > 39) SET_FLAG(of_ptr, TR_TELEPATHY); break; default: ; /* Do nothing */ } /* Races */ switch (p_ptr->rp.prace) { case RACE_ELF: SET_FLAG(of_ptr, TR_RES_LITE); break; case RACE_HOBBIT: SET_FLAG(of_ptr, TR_SUST_DEX); break; case RACE_GNOME: SET_FLAG(of_ptr, TR_FREE_ACT); break; case RACE_DWARF: SET_FLAG(of_ptr, TR_RES_BLIND); break; case RACE_HALF_ORC: SET_FLAG(of_ptr, TR_RES_DARK); break; case RACE_HALF_TROLL: SET_FLAG(of_ptr, TR_SUST_STR); if (p_ptr->lev > 14) { SET_FLAG(of_ptr, TR_REGEN); if (p_ptr->rp.pclass == CLASS_WARRIOR) { SET_FLAG(of_ptr, TR_SLOW_DIGEST); /* * Let's not make Regeneration a disadvantage * for the poor warriors who can never learn * a spell that satisfies hunger (actually * neither can rogues, but half-trolls are not * supposed to play rogues) */ } } break; case RACE_AMBERITE: SET_FLAG(of_ptr, TR_SUST_CON); SET_FLAG(of_ptr, TR_REGEN) /* Amberites heal fast */; break; case RACE_HIGH_ELF: SET_FLAG(of_ptr, TR_RES_LITE); SET_FLAG(of_ptr, TR_SEE_INVIS); break; case RACE_BARBARIAN: SET_FLAG(of_ptr, TR_RES_FEAR); break; case RACE_HALF_OGRE: SET_FLAG(of_ptr, TR_SUST_STR); SET_FLAG(of_ptr, TR_RES_DARK); break; case RACE_HALF_GIANT: SET_FLAG(of_ptr, TR_RES_SHARDS); SET_FLAG(of_ptr, TR_SUST_STR); break; case RACE_HALF_TITAN: SET_FLAG(of_ptr, TR_RES_CHAOS); break; case RACE_CYCLOPS: SET_FLAG(of_ptr, TR_RES_SOUND); break; case RACE_YEEK: SET_FLAG(of_ptr, TR_RES_ACID); if (p_ptr->lev > 19) SET_FLAG(of_ptr, TR_IM_ACID); break; case RACE_KLACKON: SET_FLAG(of_ptr, TR_RES_CONF); SET_FLAG(of_ptr, TR_RES_ACID); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_SPEED); break; case RACE_KOBOLD: SET_FLAG(of_ptr, TR_RES_POIS); break; case RACE_NIBELUNG: SET_FLAG(of_ptr, TR_RES_DISEN); SET_FLAG(of_ptr, TR_RES_DARK); break; case RACE_DARK_ELF: SET_FLAG(of_ptr, TR_RES_DARK); if (p_ptr->lev > 19) SET_FLAG(of_ptr, TR_SEE_INVIS); break; case RACE_DRACONIAN: SET_FLAG(of_ptr, TR_FEATHER); if (p_ptr->lev > 4) SET_FLAG(of_ptr, TR_RES_FIRE); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_RES_COLD); if (p_ptr->lev > 14) SET_FLAG(of_ptr, TR_RES_ACID); if (p_ptr->lev > 19) SET_FLAG(of_ptr, TR_RES_ELEC); if (p_ptr->lev > 34) SET_FLAG(of_ptr, TR_RES_POIS); break; case RACE_MIND_FLAYER: SET_FLAG(of_ptr, TR_SUST_INT); SET_FLAG(of_ptr, TR_SUST_WIS); if (p_ptr->lev > 14) SET_FLAG(of_ptr, TR_SEE_INVIS); if (p_ptr->lev > 29) SET_FLAG(of_ptr, TR_TELEPATHY); break; case RACE_IMP: SET_FLAG(of_ptr, TR_RES_FIRE); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_SEE_INVIS); break; case RACE_GOLEM: SET_FLAG(of_ptr, TR_SEE_INVIS); SET_FLAG(of_ptr, TR_FREE_ACT); SET_FLAG(of_ptr, TR_IM_POIS); SET_FLAG(of_ptr, TR_SLOW_DIGEST); SET_FLAG(of_ptr, TR_CANT_EAT); SET_FLAG(of_ptr, TR_HURT_COLD); if (p_ptr->lev > 34) SET_FLAG(of_ptr, TR_HOLD_LIFE); break; case RACE_SKELETON: SET_FLAG(of_ptr, TR_SEE_INVIS); SET_FLAG(of_ptr, TR_RES_SHARDS); SET_FLAG(of_ptr, TR_HOLD_LIFE); SET_FLAG(of_ptr, TR_IM_POIS); SET_FLAG(of_ptr, TR_CANT_EAT); SET_FLAG(of_ptr, TR_HURT_ACID); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_RES_COLD); break; case RACE_ZOMBIE: SET_FLAG(of_ptr, TR_SEE_INVIS); SET_FLAG(of_ptr, TR_HOLD_LIFE); SET_FLAG(of_ptr, TR_RES_NETHER); SET_FLAG(of_ptr, TR_IM_POIS); SET_FLAG(of_ptr, TR_SLOW_DIGEST); SET_FLAG(of_ptr, TR_CANT_EAT); SET_FLAG(of_ptr, TR_HURT_FIRE); if (p_ptr->lev > 4) SET_FLAG(of_ptr, TR_RES_COLD); break; case RACE_VAMPIRE: SET_FLAG(of_ptr, TR_HOLD_LIFE); SET_FLAG(of_ptr, TR_RES_DARK); SET_FLAG(of_ptr, TR_RES_NETHER); SET_FLAG(of_ptr, TR_LITE); SET_FLAG(of_ptr, TR_RES_POIS); SET_FLAG(of_ptr, TR_RES_COLD); SET_FLAG(of_ptr, TR_IM_DARK); SET_FLAG(of_ptr, TR_HURT_LITE); break; case RACE_SPECTRE: SET_FLAG(of_ptr, TR_RES_COLD); SET_FLAG(of_ptr, TR_SEE_INVIS); SET_FLAG(of_ptr, TR_HOLD_LIFE); SET_FLAG(of_ptr, TR_RES_NETHER); SET_FLAG(of_ptr, TR_IM_POIS); SET_FLAG(of_ptr, TR_SLOW_DIGEST); SET_FLAG(of_ptr, TR_CANT_EAT); SET_FLAG(of_ptr, TR_PASS_WALL); SET_FLAG(of_ptr, TR_HURT_ELEC); if (p_ptr->lev > 34) SET_FLAG(of_ptr, TR_TELEPATHY); break; case RACE_SPRITE: SET_FLAG(of_ptr, TR_RES_LITE); SET_FLAG(of_ptr, TR_FEATHER); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_SPEED); break; case RACE_BEASTMAN: SET_FLAG(of_ptr, TR_RES_SOUND); SET_FLAG(of_ptr, TR_RES_CONF); SET_FLAG(of_ptr, TR_MUTATE); break; case RACE_GHOUL: SET_FLAG(of_ptr, TR_HOLD_LIFE); SET_FLAG(of_ptr, TR_CANT_EAT); if (p_ptr->lev > 9) SET_FLAG(of_ptr, TR_RES_DARK); if (p_ptr->lev > 19) SET_FLAG(of_ptr, TR_RES_NETHER); SET_FLAG(of_ptr, TR_IM_POIS); SET_FLAG(of_ptr, TR_RES_COLD); SET_FLAG(of_ptr, TR_GHOUL_TOUCH); SET_FLAG(of_ptr, TR_HURT_FIRE); break; default: ; /* Do nothing */ } /* Hack - chaos patron */ if (p_ptr->muta2 & MUT2_CHAOS_GIFT) { SET_FLAG(of_ptr, TR_PATRON); } /* Mutations */ if (p_ptr->muta1) { if (p_ptr->muta1 & MUT1_VAMPIRISM) { /* HURT_LITE would be too cruel */ SET_FLAG(of_ptr, TR_CANT_EAT); } if (p_ptr->muta1 & MUT1_POLYMORPH) { SET_FLAG(of_ptr, TR_MUTATE); } if (p_ptr->muta1 & MUT1_PANIC_HIT) { of_ptr->flags[1] &= ~(TR1_RES_FEAR); } if (p_ptr->muta1 & MUT1_EAT_ROCK) { SET_FLAG(of_ptr, TR_CANT_EAT); } } if (p_ptr->muta2) { if (p_ptr->muta2 & MUT2_BEAK) { SET_FLAG(of_ptr, TR_CANT_EAT); } if (p_ptr->muta2 & MUT2_WARNING) { of_ptr->flags[1] &= ~(TR1_RES_FEAR); } } if (p_ptr->muta3) { if (p_ptr->muta3 & MUT3_HYPER_INT) { SET_FLAG(of_ptr, TR_HURT_ELEC); } if (p_ptr->muta3 & MUT3_MORONIC) { SET_FLAG(of_ptr, TR_RES_FEAR); SET_FLAG(of_ptr, TR_RES_CONF); } if (p_ptr->muta3 & MUT3_ALBINO) { SET_FLAG(of_ptr, TR_RES_DARK); } if (p_ptr->muta3 & MUT3_FLESH_ROT) { of_ptr->flags[2] &= ~(TR2_REGEN); SET_FLAG(of_ptr, TR_HOLD_LIFE); } if (p_ptr->muta3 & MUT3_BLANK_FAC) { SET_FLAG(of_ptr, TR_SEE_INVIS); } #if 0 if ((p_ptr->muta3 & MUT3_XTRA_FAT) || (p_ptr->muta3 & MUT3_XTRA_LEGS) || (p_ptr->muta3 & MUT3_SHORT_LEG)) { SET_FLAG(of_ptr, TR_SPEED); } #endif if (p_ptr->muta3 & MUT3_ELEC_TOUC) { SET_FLAG(of_ptr, TR_SH_ELEC); } if (p_ptr->muta3 & MUT3_FIRE_BODY) { SET_FLAG(of_ptr, TR_SH_FIRE); SET_FLAG(of_ptr, TR_LITE); } if (p_ptr->muta3 & MUT3_WINGS) { SET_FLAG(of_ptr, TR_FEATHER); } if (p_ptr->muta3 & MUT3_FEARLESS) { SET_FLAG(of_ptr, TR_RES_FEAR); } if (p_ptr->muta3 & MUT3_REGEN) { SET_FLAG(of_ptr, TR_REGEN); } if (p_ptr->muta3 & MUT3_ESP) { SET_FLAG(of_ptr, TR_TELEPATHY); } if (p_ptr->muta3 & MUT3_MOTION) { SET_FLAG(of_ptr, TR_FREE_ACT); } if (p_ptr->muta3 & MUT3_VULN_ELEM) { SET_FLAG(of_ptr, TR_HURT_ACID); SET_FLAG(of_ptr, TR_HURT_ELEC); SET_FLAG(of_ptr, TR_HURT_FIRE); SET_FLAG(of_ptr, TR_HURT_COLD); } } } /* * Equippy chars */ static void display_player_equippy(int x, int y) { int i; byte a; char c; object_type *o_ptr; /* Dump equippy chars */ for (i = 0; i < EQUIP_MAX; i++) { /* Object */ o_ptr = &p_ptr->equipment[i]; a = object_attr(o_ptr); c = object_char(o_ptr); /* No color */ if (!use_color) a = TERM_WHITE; /* Clear the part of the screen */ if (!o_ptr->k_idx) { c = ' '; a = TERM_DARK; } /* Dump */ Term_putch(x + i, y, a, c); } } void print_equippy(void) { display_player_equippy(COL_EQUIPPY, ROW_EQUIPPY); } /* * Helper function, see below */ static void display_player_flag_aux3(int col, int row, cptr header, int n1, u32b flag1, int n2, u32b flag2, int n3, u32b flag3) { int i; object_flags oflags; /* Header */ put_fstr(col, row, header); /* Advance */ col += strlen(header) + 1; /* Check equipment */ for (i = 0; i < EQUIP_MAX; i++) { object_type *o_ptr; /* Object */ o_ptr = &p_ptr->equipment[i]; /* Known flags */ object_flags_known(o_ptr, &oflags); /* Default */ put_fstr(col, row, CLR_SLATE "."); /* Check flags */ if (oflags.flags[n3] & flag3) { put_fstr(col, row, CLR_RED "v"); if (oflags.flags[n1] & flag1) put_fstr(col, row, CLR_RED "+"); if (oflags.flags[n2] & flag2) put_fstr(col, row, CLR_RED "*"); } else { if (oflags.flags[n1] & flag1) put_fstr(col, row, "+"); if (oflags.flags[n2] & flag2) put_fstr(col, row, "*"); } /* Advance */ col++; } /* Player flags */ player_flags(&oflags); /* Default */ put_fstr(col, row, CLR_SLATE "."); /* Check flags */ if (oflags.flags[n3] & flag3) { put_fstr(col, row, CLR_RED "v"); if (oflags.flags[n1] & flag1) put_fstr(col, row, CLR_RED "+"); if (oflags.flags[n2] & flag2) put_fstr(col, row, CLR_RED "*"); } else { if (oflags.flags[n1] & flag1) put_fstr(col, row, "+"); if (oflags.flags[n2] & flag2) put_fstr(col, row, "*"); } } static void display_player_flag_aux2(int col, int row, cptr header, int n1, u32b flag1, int n2, u32b flag2) { display_player_flag_aux3(col, row, header, n1, flag1, n2, flag2, 1, 0); } static void display_player_flag_aux(int col, int row, cptr header, int n1, u32b flag1) { display_player_flag_aux3(col, row, header, n1, flag1, 1, 0, 1, 0); } /* * Special display, part 1 */ static void display_player_flag_info(void) { int row; int col; /*** Set 1 ***/ row = 1; col = 0; display_player_equippy(col + 8, row++); put_fstr(col + 8, row++, "abcdefghijkl@"); display_player_flag_aux3(col, row++, "Acid :", TR_RES_ACID, TR_IM_ACID, TR_HURT_ACID); display_player_flag_aux3(col, row++, "Elec :", TR_RES_ELEC, TR_IM_ELEC, TR_HURT_ELEC); display_player_flag_aux3(col, row++, "Fire :", TR_RES_FIRE, TR_IM_FIRE, TR_HURT_FIRE); display_player_flag_aux3(col, row++, "Cold :", TR_RES_COLD, TR_IM_COLD, TR_HURT_COLD); display_player_flag_aux2(col, row++, "Poison:", TR_RES_POIS, TR_IM_POIS); display_player_flag_aux(col, row++, "Fear :", TR_RES_FEAR); display_player_flag_aux3(col, row++, "Light :", TR_RES_LITE, TR_IM_LITE, TR_HURT_LITE); display_player_flag_aux3(col, row++, "Dark :", TR_RES_DARK, TR_IM_DARK, TR_HURT_DARK); display_player_flag_aux(col, row++, "Shard :", TR_RES_SHARDS); display_player_flag_aux(col, row++, "Blind :", TR_RES_BLIND); display_player_flag_aux(col, row++, "Conf :", TR_RES_CONF); display_player_flag_aux(col, row++, "Sound :", TR_RES_SOUND); display_player_flag_aux(col, row++, "Nether:", TR_RES_NETHER); display_player_flag_aux(col, row++, "Nexus :", TR_RES_NEXUS); display_player_flag_aux(col, row++, "Chaos :", TR_RES_CHAOS); display_player_flag_aux(col, row++, "Disnch:", TR_RES_DISEN); /*** Set 2 ***/ row = 1; col = 24; display_player_equippy(col + 11, row++); put_fstr(col + 11, row++, "abcdefghijkl@"); display_player_flag_aux(col, row++, "Reflect :", TR_REFLECT); display_player_flag_aux(col, row++, "Aura Acid:", TR_SH_ACID); display_player_flag_aux(col, row++, "Aura Fire:", TR_SH_FIRE); display_player_flag_aux(col, row++, "Aura Elec:", TR_SH_ELEC); display_player_flag_aux(col, row++, "Aura Cold:", TR_SH_COLD); display_player_flag_aux(col, row++, "No Magic :", TR_NO_MAGIC); display_player_flag_aux(col, row++, "Free Actn:", TR_FREE_ACT); display_player_flag_aux(col, row++, "SeeInvis.:", TR_SEE_INVIS); display_player_flag_aux(col, row++, "Hold Life:", TR_HOLD_LIFE); display_player_flag_aux(col, row++, "Telepathy:", TR_TELEPATHY); display_player_flag_aux(col, row++, "SlwDigstn:", TR_SLOW_DIGEST); display_player_flag_aux(col, row++, "Regen. :", TR_REGEN); display_player_flag_aux(col, row++, "Levitate :", TR_FEATHER); display_player_flag_aux(col, row++, "PermLite :", TR_LITE); display_player_flag_aux(col, row++, "Mutate :", TR_MUTATE); display_player_flag_aux(col, row++, "Patron :", TR_PATRON); display_player_flag_aux(col, row++, "Good Luck:", TR_LUCK_10); display_player_flag_aux(col, row++, "WeirdLuck:", TR_STRANGE_LUCK); display_player_flag_aux(col, row++, "Pass Wall:", TR_PASS_WALL); display_player_flag_aux(col, row++, "GhulTouch:", TR_GHOUL_TOUCH); /*** Set 3 ***/ row = 1; col = 52; display_player_equippy(col + 11, row++); put_fstr(col + 11, row++, "abcdefghijkl@"); display_player_flag_aux(col, row++, "Pr Animal:", TR_SLAY_ANIMAL); display_player_flag_aux(col, row++, "Pr Evil :", TR_SLAY_EVIL); display_player_flag_aux(col, row++, "Pr Undead:", TR_SLAY_UNDEAD); display_player_flag_aux(col, row++, "Pr Demon :", TR_SLAY_DEMON); display_player_flag_aux(col, row++, "Pr Orc :", TR_SLAY_ORC); display_player_flag_aux(col, row++, "Pr Troll :", TR_SLAY_TROLL); display_player_flag_aux(col, row++, "Pr Giant :", TR_SLAY_GIANT); display_player_flag_aux(col, row++, "Pr Dragon:", TR_SLAY_DRAGON); display_player_flag_aux2(col, row++, "Cursed :", TR_CURSED, 2, TR2_HEAVY_CURSE | TR2_PERMA_CURSE); display_player_flag_aux(col, row++, "AutoCurse:", TR_AUTO_CURSE); display_player_flag_aux(col, row++, "Teleport :", TR_TELEPORT); display_player_flag_aux(col, row++, "NoTeleprt:", TR_NO_TELE); display_player_flag_aux(col, row++, "Aggravate:", TR_AGGRAVATE); display_player_flag_aux(col, row++, "DrainStat:", TR_DRAIN_STATS); display_player_flag_aux(col, row++, "Drain Exp:", TR_DRAIN_EXP); display_player_flag_aux(col, row++, "Slow Heal:", TR_SLOW_HEAL); display_player_flag_aux(col, row++, "Can't Eat:", TR_CANT_EAT); display_player_flag_aux(col, row++, "EvilCurse:", TR_TY_CURSE); } /* * Special display, part 2b * * How to print out the modifications and sustains. * Positive mods with no sustain will be light green. * Positive mods with a sustain will be dark green. * Sustains (with no modification) will be a dark green 's'. * Negative mods (from a curse) will be red. * Huge mods (>9), like from MICoMorgoth, will be a '*' * No mod, no sustain, will be a slate '.' */ static void display_player_stat_info(void) { int i, e_adj, r_adj, c_adj; int stat_col, stat; int row, col; object_type *o_ptr; object_flags oflags; s16b k_idx; byte a; char c; /* Column */ stat_col = 16; /* Row */ row = 3; /* Print out the labels for the columns */ put_fstr(stat_col, row - 1, "Stat"); put_fstr(stat_col + 5, row - 1, CLR_BLUE "Intrnl"); put_fstr(stat_col + 12, row - 1, CLR_L_BLUE "Rce Cls Mod"); put_fstr(stat_col + 24, row - 1, CLR_L_GREEN "Actual"); put_fstr(stat_col + 31, row - 1, CLR_YELLOW "Currnt"); /* Display the stats */ for (i = 0; i < A_MAX; i++) { /* Calculate equipment adjustment */ e_adj = p_ptr->stat[i].top - p_ptr->stat[i].max; /* Get race and class adjustments */ r_adj = rp_ptr->r_adj[i] * 10; c_adj = cp_ptr->c_adj[i] * 10; /* Reduced name of stat */ put_fstr(stat_col, row + i, stat_names_reduced[i]); /* Internal "natural" max value. Maxes at 40 */ /* This is useful to see if you are maxed out */ /* We actually fake this by subtracting out the race/class bonuses... */ put_fstr(stat_col + 5, row + i, CLR_BLUE "%v", stat_format, p_ptr->stat[i].max - r_adj - c_adj); /* Race, class, and equipment modifiers */ put_fstr(stat_col + 12, row + i, CLR_L_BLUE "%3d", (int)(r_adj / 10)); put_fstr(stat_col + 16, row + i, CLR_L_BLUE "%3d", (int)(c_adj / 10)); put_fstr(stat_col + 20, row + i, CLR_L_BLUE "%3d", (int)(e_adj / 10)); /* Actual maximal modified value */ put_fstr(stat_col + 24, row + i, CLR_L_GREEN "%v", stat_format, p_ptr->stat[i].top); /* Only display stat_use if not maximal */ if (p_ptr->stat[i].use < p_ptr->stat[i].top) { put_fstr(stat_col + 31, row + i, CLR_YELLOW "%v", stat_format, p_ptr->stat[i].use); } } /* Column */ col = stat_col + 39; /* Header and Footer */ display_player_equippy(col, row - 2); put_fstr(col, row - 1, "abcdefghijkl@"); put_fstr(col, row + 6, CLR_L_GREEN "Modifications"); /* Process equipment */ for (i = 0; i < EQUIP_MAX; i++) { /* Access object */ o_ptr = &p_ptr->equipment[i]; /* Object kind */ k_idx = o_ptr->k_idx; /* Acquire "known" flags */ object_flags_known(o_ptr, &oflags); /* Initialize color based of sign of pval. */ for (stat = 0; stat < A_MAX; stat++) { /* Default */ a = TERM_SLATE; c = '.'; /* Boost */ if (oflags.flags[0] & (1 << stat)) { /* Default */ c = '*'; /* Good */ if (o_ptr->pval > 0) { /* Good */ a = TERM_L_GREEN; /* Label boost */ if (o_ptr->pval < 10) c = '0' + o_ptr->pval; } if (oflags.flags[1] & (1 << stat)) { /* Dark green for sustained stats. */ a = TERM_GREEN; } /* Bad */ if (o_ptr->pval < 0) { /* Bad */ a = TERM_RED; /* Label boost */ if (-o_ptr->pval < 10) c = '0' - o_ptr->pval; } } /* Sustain */ else if (oflags.flags[1] & (1 << stat)) { /* Dark green "s" */ a = TERM_GREEN; c = 's'; } /* Handle monochrome */ if (!use_color) a = TERM_WHITE; /* Dump proper character */ Term_putch(col, row + stat, a, c); } /* Advance */ col++; } /* Player flags */ player_flags(&oflags); /* Check stats */ for (stat = 0; stat < A_MAX; stat++) { /* Default */ a = TERM_SLATE; c = '.'; /* Sustain */ if (oflags.flags[1] & 1 << stat) { /* Dark green "s" */ a = TERM_GREEN; c = 's'; } /* Mutations ... */ if (p_ptr->muta1 || p_ptr->muta2 || p_ptr->muta3) { int dummy = 0; if (stat == A_STR) { if (p_ptr->muta3 & MUT3_HYPER_STR) dummy += 4; if (p_ptr->muta3 & MUT3_PUNY) dummy -= 4; if (p_ptr->muta1 & MUT1_MIND_BLST) dummy -= 1; if (p_ptr->muta3 & MUT3_LIMBER) dummy -= 1; } else if (stat == A_INT) { if (p_ptr->muta3 & MUT3_HYPER_INT) dummy += 4; if (p_ptr->muta3 & MUT3_MORONIC) dummy -= 4; if (p_ptr->muta1 & MUT1_STERILITY) dummy -= 1; if (p_ptr->muta3 & MUT3_HYPER_STR) dummy -= 1; } else if (stat == A_WIS) { if (p_ptr->muta3 & MUT3_HYPER_INT) dummy += 4; if (p_ptr->muta3 & MUT3_MORONIC) dummy -= 4; if (p_ptr->muta1 & MUT1_HYPN_GAZE) dummy -= 1; if (p_ptr->muta1 & MUT1_BERSERK) dummy -= 1; if (p_ptr->muta1 & MUT1_MIDAS_TCH) dummy -= 1; if (p_ptr->muta1 & MUT1_EARTHQUAKE) dummy -= 1; if (p_ptr->muta2 & MUT2_INVULN) dummy -= 2; if (p_ptr->muta3 & MUT3_HYPER_STR) dummy -= 1; } else if (stat == A_DEX) { if (p_ptr->muta3 & MUT3_IRON_SKIN) dummy -= 1; if (p_ptr->muta3 & MUT3_LIMBER) dummy += 3; if (p_ptr->muta3 & MUT3_ARTHRITIS) dummy -= 3; if (p_ptr->muta1 & MUT1_COLD_TOUCH) dummy -= 1; if (p_ptr->muta1 & MUT1_LAUNCHER) dummy -= 1; if (p_ptr->muta2 & MUT2_TENTACLES) dummy += 1; if (p_ptr->muta3 & MUT3_PUNY) dummy += 2; if (p_ptr->muta3 & MUT3_XTRA_LEGS) dummy -= 1; } else if (stat == A_CON) { if (p_ptr->muta3 & MUT3_RESILIENT) dummy += 4; if (p_ptr->muta3 & MUT3_XTRA_FAT) dummy += 2; if (p_ptr->muta3 & MUT3_ALBINO) dummy -= 4; if (p_ptr->muta3 & MUT3_FLESH_ROT) dummy -= 2; if (p_ptr->muta1 & MUT1_HYPN_GAZE) dummy -= 1; if (p_ptr->muta1 & MUT1_RADIATION) dummy -= 1; if (p_ptr->muta3 & MUT3_SHORT_LEG) dummy += 1; if (p_ptr->muta2 & MUT2_WRAITH) dummy -= 2; } else if (stat == A_CHR) { if (p_ptr->muta3 & MUT3_SILLY_VOI) dummy -= 4; if (p_ptr->muta3 & MUT3_BLANK_FAC) dummy -= 1; if (p_ptr->muta3 & MUT3_FLESH_ROT) dummy -= 1; if (p_ptr->muta3 & MUT3_SCALES) dummy -= 1; if (p_ptr->muta3 & MUT3_WART_SKIN) dummy -= 2; if (p_ptr->muta1 & MUT1_SHRIEK) dummy -= 1; if (p_ptr->muta1 & MUT1_MIDAS_TCH) dummy -= 1; if (p_ptr->muta2 & MUT2_SCOR_TAIL) dummy -= 2; if (p_ptr->muta2 & MUT2_TRUNK) dummy -= 1; if (p_ptr->muta2 & MUT2_TENTACLES) dummy -= 1; if (p_ptr->muta3 & MUT3_XTRA_EYES) dummy -= 1; if (p_ptr->muta3 & MUT3_ILL_NORM) dummy = 0; } /* Boost */ if (dummy) { /* Default */ c = '*'; /* Good */ if (dummy > 0) { /* Good */ a = TERM_L_GREEN; /* Label boost */ if (dummy < 10) c = '0' + dummy; } /* Sustains */ if (oflags.flags[1] & (1 << stat)) { a = TERM_GREEN; } /* Bad */ if (dummy < 0) { /* Bad */ a = TERM_RED; /* Label boost */ if (-dummy < 10) c = '0' - dummy; } } } /* No color */ if (!use_color) a = TERM_WHITE; /* Dump */ Term_putch(col, row + stat, a, c); } } static void display_player_skill_info(void) { int row, col, skill_col; int i; int skill; object_type *o_ptr; object_flags oflags; s16b k_idx; byte a; char c; row = 13; skill_col = 45; put_fstr(skill_col, row - 1, "Skill"); put_fstr(skill_col, row, "BonusSP:"); put_fstr(skill_col, row + 1, "Stealth:"); put_fstr(skill_col, row + 2, "Search :"); put_fstr(skill_col, row + 3, "Infra :"); put_fstr(skill_col, row + 4, "Tunnel :"); put_fstr(skill_col, row + 5, "Speed :"); put_fstr(skill_col, row + 6, "Blows :"); /* Column */ col = skill_col + 10; /* Header and Footer */ display_player_equippy(col, row - 2); put_fstr(col, row - 1, "abcdefghijkl@"); put_fstr(col, row + 6, CLR_L_GREEN "Modifications"); /* Process equipment */ for (i = 0; i < EQUIP_MAX; i++) { /* Access object */ o_ptr = &p_ptr->equipment[i]; /* Object kind */ k_idx = o_ptr->k_idx; /* Acquire "known" flags */ object_flags_known(o_ptr, &oflags); /* Initialize color based of sign of pval. */ for (skill = 0; skill < 7; skill++) { /* Default */ a = TERM_SLATE; c = '.'; /* Boost */ if (oflags.flags[0] & (TR0_SP << skill)) { /* Default */ c = '*'; /* Good */ if (o_ptr->pval > 0) { /* Good */ a = TERM_L_GREEN; /* Label boost */ if (o_ptr->pval < 10) c = '0' + o_ptr->pval; } /* Bad */ if (o_ptr->pval < 0) { /* Bad */ a = TERM_RED; /* Label boost */ if (-o_ptr->pval < 10) c = '0' - o_ptr->pval; } } /* Handle monochrome */ if (!use_color) a = TERM_WHITE; /* Dump proper character */ Term_putch(col, row + skill, a, c); } /* Advance */ col++; } /* Player flags */ player_flags(&oflags); for (skill = 0; skill < 7; skill++) { int dummy = 0; /* Default */ a = TERM_SLATE; c = '.'; if (p_ptr->muta1 || p_ptr->muta2 || p_ptr->muta3) { /* Stealth */ if (skill == 1) { if (p_ptr->muta1 & MUT1_ILLUMINE) dummy -= 1; if (p_ptr->muta1 & MUT1_DAZZLE) dummy -= 1; if (p_ptr->muta3 & MUT3_XTRA_NOIS) dummy -= 3; if (p_ptr->muta3 & MUT3_MOTION) dummy += 1; } /* Search */ else if (skill == 2) { if (p_ptr->muta1 & MUT1_LASER_EYE) dummy -= 2; if (p_ptr->muta3 & MUT3_XTRA_EYES) dummy += 3; } /* Infra */ else if (skill == 3) { if (p_ptr->muta3 & MUT3_INFRAVIS) dummy += 3; } /* Speed */ else if (skill == 5) { if (p_ptr->muta3 & MUT3_XTRA_LEGS) dummy += 3; if (p_ptr->muta3 & MUT3_SHORT_LEG) dummy -= 3; } } if (skill == 5 && (FLAG(&oflags, TR_SPEED))) { dummy += p_ptr->lev / 10; } /* Boost */ if (dummy) { /* Default */ c = '*'; /* Good */ if (dummy > 0) { /* Good */ a = TERM_L_GREEN; /* Label boost */ if (dummy < 10) c = '0' + dummy; } /* Bad */ if (dummy < 0) { /* Bad */ a = TERM_RED; /* Label boost */ if (-dummy < 10) c = '0' - dummy; } } /* No color */ if (!use_color) a = TERM_WHITE; /* Dump */ Term_putch(col, row + skill, a, c); } } #define COL_NAME 0 #define WID_NAME 11 #define COL_AGE 32 #define COL_STATS 55 /* * Display the standard player information */ static void display_player_top(void) { int i; /* Name, Sex, Race, Class */ put_fstr(COL_NAME, 2, "Name : " CLR_L_BLUE "%s\n" CLR_WHITE "Sex : " CLR_L_BLUE "%s\n" CLR_WHITE "Race : " CLR_L_BLUE "%s\n" CLR_WHITE "Class : " CLR_L_BLUE "%s", player_name, sp_ptr->title, rp_ptr->title, cp_ptr->title); if (p_ptr->spell.r[0].realm || p_ptr->spell.r[1].realm) { put_fstr(COL_NAME, 6, "Magic : " CLR_L_BLUE "%s", realm_names[p_ptr->spell.r[0].realm]); } if (p_ptr->spell.r[1].realm) { put_fstr(COL_NAME + WID_NAME, 7, CLR_L_BLUE "%s", realm_names[p_ptr->spell.r[1].realm]); } else if (FLAG(p_ptr, TR_PATRON)) { put_fstr(COL_NAME, 7, "Patron : " CLR_L_BLUE "%s", chaos_patrons[p_ptr->chaos_patron]); } /* Age, Height, Weight, Social */ prt_num(COL_AGE, 2, "Age " CLR_L_BLUE, (int)p_ptr->rp.age, 3); prt_num(COL_AGE, 3, "Height " CLR_L_BLUE, (int)p_ptr->rp.ht, 3); prt_num(COL_AGE, 4, "Weight " CLR_L_BLUE, (int)p_ptr->rp.wt, 3); prt_num(COL_AGE, 5, "Social Class" CLR_L_BLUE, (int)p_ptr->rp.sc, 3); /* Display the stats */ for (i = 0; i < A_MAX; i++) { /* Special treatment of "injured" stats */ if (p_ptr->stat[i].cur < p_ptr->stat[i].max) { /* Use lowercase stat name */ put_fstr(COL_STATS, i + 2, stat_names_reduced[i]); /* Display the current stat (modified) */ put_fstr(COL_STATS + 5, i + 2, CLR_YELLOW "%v", stat_format, p_ptr->stat[i].use); /* Display the maximum stat (modified) */ put_fstr(COL_STATS + 5 + 7, i + 2, CLR_L_GREEN "%v", stat_format, p_ptr->stat[i].top); } /* Normal treatment of "normal" stats */ else { /* Assume uppercase stat name */ put_fstr(COL_STATS, i + 2, stat_names[i]); /* Display the current stat (modified) */ put_fstr(COL_STATS + 5, i + 2, CLR_L_GREEN "%v", stat_format, p_ptr->stat[i].use); } } } #define COL_BONUS 0 #define COL_VALUE 23 #define COL_LIFE 51 /* * Display the player attributes. */ static void display_player_middle(void) { int percentdam; int show_tohit = p_ptr->dis_to_h; int show_todam = p_ptr->dis_to_d; object_type *o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Hack -- add in weapon info if known */ if (object_known_p(o_ptr)) show_tohit += o_ptr->to_h; if (object_known_p(o_ptr)) show_todam += o_ptr->to_d; /* convert to oangband "deadliness" */ percentdam = deadliness_calc(show_todam); /*** Bonuses ***/ prt_num(COL_BONUS, 9, "+ Skill " CLR_L_BLUE, show_tohit, 3); prt_num(COL_BONUS, 10, "% Deadliness" CLR_L_BLUE, percentdam, 3); prt_num(COL_BONUS, 11, "+ To AC " CLR_L_BLUE, p_ptr->dis_to_a, 3); prt_num(COL_BONUS, 12, " Base AC " CLR_L_BLUE, p_ptr->dis_ac, 3); /*** Level, experience, gold ***/ prt_num(COL_VALUE, 9, "Level " CLR_L_GREEN, (int)p_ptr->lev, 9); if (p_ptr->exp >= p_ptr->max_exp) { prt_num(COL_VALUE, 10, "Experience " CLR_L_GREEN, p_ptr->exp, 9); } else { prt_num(COL_VALUE, 10, "Experience " CLR_YELLOW, p_ptr->exp, 9); } prt_num(COL_VALUE, 11, "Max Exp " CLR_L_GREEN, p_ptr->max_exp, 9); if (p_ptr->lev >= PY_MAX_LEVEL) { put_fstr(COL_VALUE, 12, "Exp to Adv." CLR_L_GREEN " *****"); } else if (toggle_xp) { /* Print the amount of xp until next level */ prt_num(COL_VALUE, 12, "Exp to Adv." CLR_L_GREEN, (long)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L - (long)p_ptr->exp), 9); } else { /* Print the total xp required for next level */ prt_num(COL_VALUE, 12, "Exp to Adv." CLR_L_GREEN, (long)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L), 9); } prt_num(COL_VALUE, 13, "Gold " CLR_L_GREEN, p_ptr->au, 9); /*** Hitpoints/spellpoints ***/ prt_num(COL_LIFE, 9, "Max Hit Points" CLR_L_GREEN, p_ptr->mhp, 5); if (p_ptr->chp >= p_ptr->mhp) { prt_num(COL_LIFE, 10, "Cur Hit Points" CLR_L_GREEN, p_ptr->chp, 5); } else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10) { prt_num(COL_LIFE, 10, "Cur Hit Points" CLR_YELLOW, p_ptr->chp, 5); } else { prt_num(COL_LIFE, 10, "Cur Hit Points" CLR_RED, p_ptr->chp, 5); } prt_num(COL_LIFE, 11, "Max SP (Mana) " CLR_L_GREEN, p_ptr->msp, 5); if (p_ptr->csp >= p_ptr->msp) { prt_num(COL_LIFE, 12, "Cur SP (Mana) " CLR_L_GREEN, p_ptr->csp, 5); } else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10) { prt_num(COL_LIFE, 12, "Cur SP (Mana) " CLR_YELLOW, p_ptr->csp, 5); } else { prt_num(COL_LIFE, 12, "Cur SP (Mana) " CLR_RED, p_ptr->csp, 5); } } /* * Display the standard player information and abilities */ static void display_player_standard(void) { /* Basic info */ display_player_top(); /* Extra info */ display_player_middle(); put_fstr(25, 15, "(Miscellaneous Abilities)"); /* Display the abilities */ display_player_abilities(); } /* * Display a summary of the player's attributes * * See "http://www.cs.berkeley.edu/~davidb/angband.html" */ static void display_player_flag(void) { /* Dump the info */ display_player_flag_info(); } /* * Display a summary of the player's bonuses */ static void display_player_stat(void) { /* Dump the info */ display_player_stat_info(); display_player_skill_info(); } typedef void (*display_func) (void); static display_func displays[DISPLAY_PLAYER_MAX] = { /* Standard display with skills */ display_player_standard, /* Summary of various things */ display_player_flag, /* Summary of stat & skill boosts */ display_player_stat, }; /* * Display the character on the screen (various modes) * * The top two and bottom two lines are left blank. */ void display_player(int mode) { mode %= DISPLAY_PLAYER_MAX; /* Erase screen */ clear_from(0); /* Display it */ (*(displays[mode])) (); } /* * Hack -- change name */ void do_cmd_character(void) { char c; int mode = DISPLAY_PLAYER_STANDARD; char tmp[160]; /* Save the screen */ screen_save(); /* Forever */ while (1) { /* Display the player */ display_player(mode); /* Prompt */ put_fstr(2, 23, "['c' to change name, 'f' to file, 'p' for previous, 'n' for next, or ESC]"); /* Query */ c = inkey(); /* Exit */ if (c == ESCAPE) break; /* Change name */ if (c == 'c') { change_player_name(); } /* File dump */ else if (c == 'f') { strnfmt(tmp, 160, "%s.txt", player_base); if (get_string(tmp, 80, "File name: ")) { if (tmp[0] && (tmp[0] != ' ')) { (void)file_character(tmp, TRUE); } } } /* Decrease mode */ else if (c == 'p') { mode = (mode + DISPLAY_PLAYER_MAX - 1) % DISPLAY_PLAYER_MAX; } /* Increase mode */ else if (c == 'n') { mode = (mode + 1) % DISPLAY_PLAYER_MAX; } /* Oops */ else { bell("Illegal option!"); } /* Flush messages */ message_flush(); } /* Restore the screen */ screen_load(); /* Redraw everything */ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY); handle_stuff(); } /* * Hack -- Dump a character description file * * XXX XXX XXX Allow the "full" flag to dump additional info, * and trigger its usage from various places in the code. */ errr file_character(cptr name, bool full) { int i, j, k, x, y; byte a; char c; cptr paren = ")"; int fd = -1; FILE *fff = NULL; store_type *st_ptr; char buf[1024]; int msg_max = message_num(); object_type *o_ptr; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, name); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Check for existing file */ fd = fd_open(buf, O_RDONLY); /* Existing file */ if (fd >= 0) { /* Close the file */ (void)fd_close(fd); /* Ask */ if (get_check("Replace existing file %s? ", buf)) fd = -1; } /* Open the non-existing file */ if (fd < 0) fff = my_fopen(buf, "w"); /* Invalid file */ if (!fff) { /* Message */ msgf("Character dump failed!"); message_flush(); /* Error */ return (-1); } froff(fff, " [%s %s Character Dump]\n\n", VERSION_NAME, VERSION_STRING); /* Display player */ display_player(DISPLAY_PLAYER_STANDARD); /* Dump part of the screen */ for (y = 2; y < 22; y++) { /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ buf[x] = c; } /* End the string */ buf[x] = '\0'; /* Kill trailing spaces */ while ((x > 0) && (buf[x - 1] == ' ')) buf[--x] = '\0'; /* End the row */ froff(fff, "%s\n", buf); } froff(fff, "\n\n [Miscellaneous information]\n"); if (preserve_mode) froff(fff, "\n Preserve Mode: ON"); else froff(fff, "\n Preserve Mode: OFF"); if (ironman_small_levels) froff(fff, "\n Small Levels: ALWAYS"); else if (small_levels) froff(fff, "\n Small Levels: ON"); else froff(fff, "\n Small Levels: OFF"); if (vanilla_town) froff(fff, "\n Vanilla Town: ON"); if (ironman_shops) froff(fff, "\n No Shops: ON"); if (ironman_downward) froff(fff, "\n Diving only: ON"); if (ironman_nightmare) froff(fff, "\n Nightmare Mode: ON"); froff(fff, "\n Recall Depth: Level %d (%d')\n", max_dun_level_reached(), 50 * max_dun_level_reached()); if (p_ptr->state.noscore) froff(fff, "\n You have done something illegal."); if (stupid_monsters) froff(fff, "\n Your opponents are behaving stupidly."); if (munchkin_death) froff(fff, "\n You possess munchkinish power over death."); /* Show (known) flags grid */ if (full) { /* New line */ froff(fff, "\n\n"); display_player(DISPLAY_PLAYER_FLAG); /* Dump part of the screen */ for (y = 2; y < 9; y++) { /* Dump each row */ for (x = 0; x < 63; x++) { /* Get the attr/char */ (void)(Term_what(x + 16, y, &a, &c)); /* Dump it */ buf[x] = c; } /* End the string */ buf[x] = '\0'; /* Kill trailing spaces */ while ((x > 0) && (buf[x - 1] == ' ')) buf[--x] = '\0'; /* End the row */ froff(fff, "%s\n", buf); } /* New line */ froff(fff, "\n"); /* Dump column */ for (y = 12; y < 20; y++) { for (x = 0; x < 24; x++) { (void)(Term_what(x + 45, y, &a, &c)); buf[x] = c; } buf[x] = '\0'; froff(fff, "%s\n", buf); } froff(fff, "\n"); display_player(DISPLAY_PLAYER_SUMMARY); /* Dump first column */ for (y = 2; y < 19; y++) { for (x = 0; x < 21; x++) { (void)(Term_what(x, y, &a, &c)); buf[x] = c; } buf[x] = '\0'; froff(fff, "%s\n", buf); } /* New line */ froff(fff, "\n"); /* Dump second column */ for (y = 2; y < 23; y++) { for (x = 0; x < 24; x++) { (void)(Term_what(x + 24, y, &a, &c)); buf[x] = c; } buf[x] = '\0'; froff(fff, "%s\n", buf); } /* New line */ froff(fff, "\n"); /* Dump third column */ for (y = 2; y < 22; y++) { for (x = 0; x < 24; x++) { (void)(Term_what(x + 52, y, &a, &c)); buf[x] = c; } buf[x] = '\0'; froff(fff, "%s\n", buf); } } /* Monsters slain */ { u32b Total = 0; for (k = 1; k < z_info->r_max; k++) { monster_race *r_ptr = &r_info[k]; if (FLAG(r_ptr, RF_UNIQUE)) { if (r_ptr->max_num == 0) Total++; } else { Total += r_ptr->r_pkills; } } if (Total < 1) froff(fff, "\n You have defeated no enemies yet.\n"); else if (Total == 1) froff(fff, "\n You have defeated one enemy.\n"); else froff(fff, "\n You have defeated %lu enemies.\n", Total); } /* Top kills */ { u16b *who; int n; u16b why = 0; /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, u16b); /* Collect matching monsters */ for (n = 0, i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Require killed monsters */ if (!r_ptr->r_pkills) continue; /* Collect monsters */ who[n++] = i; } if (n) { froff(fff, "\n\n [Top %i deepest kills]\n\n", n >= 10 ? 10 : n); why = 2; /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array */ ang_sort(who, &why, n); for (i = n - 1; i >= 0 && i >= n - 10; i--) { monster_race *r_ptr = &r_info[who[i]]; froff(fff, "%2i %-36s Level %i (%i')\n", r_ptr->r_pkills, mon_race_name(r_ptr), r_ptr->level, r_ptr->level * 50); } } /* Free the "who" array */ KILL(who); } #if 0 froff(fff, "\n\n [Virtues]\n\n"); dump_virtues(fff); #endif /* 0 */ if (p_ptr->muta1 || p_ptr->muta2 || p_ptr->muta3) { froff(fff, "\n\n [Mutations]\n\n"); dump_mutations(fff); } /* Skip some lines */ froff(fff, "\n\n"); /* Dump the equipment */ froff(fff, " [Character Equipment]\n\n"); for (i = 0; i < EQUIP_MAX; i++) { char o_name[256]; /* Describe object */ object_desc(o_name, &p_ptr->equipment[i], TRUE, 3, 256); /* Clean formatting escape sequences */ fmt_clean(o_name); /* Dump the equipment slot */ froff(fff, "%c%s %s\n", I2A(i), paren, o_name); } froff(fff, "\n\n"); /* Dump the inventory */ froff(fff, " [Character Inventory]\n\n"); i = 0; OBJ_ITT_START (p_ptr->inventory, o_ptr) { char o_name[256]; /* Describe object */ object_desc(o_name, o_ptr, TRUE, 3, 256); /* Clean formatting escape sequences */ fmt_clean(o_name); /* Dump the inventory slots */ froff(fff, "%c%s %s\n", I2A(i), paren, o_name); /* Count slots */ i++; } OBJ_ITT_END; /* Add an empty line */ froff(fff, "\n\n"); /* Print all homes in the different towns */ for (i = 1; i < z_info->wp_max; i++) { for (j = 0; j < place[i].numstores; j++) { st_ptr = &place[i].store[j]; if (st_ptr->type == BUILD_STORE_HOME) { /* Home -- if anything there */ if (st_ptr->stock) { char o_name[256]; /* Header with name of the town */ froff(fff, " [Home Inventory - %s]\n\n", place[i].name); /* Initialise counter */ k = 0; /* Dump all available items */ OBJ_ITT_START (st_ptr->stock, o_ptr) { /* Describe object */ object_desc(o_name, o_ptr, TRUE, 3, 256); /* Clean formatting escape sequences */ fmt_clean(o_name); froff(fff, "%c%s %s\n", I2A(k), paren, o_name); /* Increment counter */ k++; } OBJ_ITT_END; /* Add an empty line */ froff(fff, "\n\n"); } } } } if (limit_messages && msg_max > 50) msg_max = 50; froff(fff, " [Message Log (last %d messages)]\n\n", msg_max); for (i = msg_max - 1; i >= 0; i--) { froff(fff, "%s\n", message_str((s16b)i)); } froff(fff, "\n\n"); /* Close it */ my_fclose(fff); /* Message */ msgf("Character dump successful."); message_flush(); /* Success */ return (0); } #define RESIZE_SHOW_FILE -2 static cptr resize_name = NULL; static cptr resize_what = NULL; static int resize_line = 0; static int resize_hgt = 0; /* * Resizing can happen while show_file is waiting for input. The resize can * be done by show_file, except that then there should be no input */ static void resize_show_file(void) { int dummy; (void)show_file(resize_name, resize_what, resize_line, RESIZE_SHOW_FILE); /* Get size */ Term_get_size(&dummy, &resize_hgt); } /* * Recursive file perusal. * * Return FALSE on "ESCAPE", otherwise TRUE. * * Process various special text in the input file, including * the "menu" structures used by the "help file" system. * * XXX XXX XXX Consider using a temporary file. * * XXX XXX XXX Allow the user to "save" the current file. */ bool show_file(cptr name, cptr what, int line, int mode) { int i, n, k; int wid, hgt; /* Number of "real" lines passed by */ int next = 0; /* Number of "real" lines in the file */ int size = 0; /* Backup value for "line" */ int back = 0; /* Loop counter */ int cnt; /* This screen has sub-screens */ bool menu = FALSE; /* Current help file */ FILE *fff = NULL; /* Find this string (if any) */ cptr find = NULL; /* Jump to this tag */ cptr tag = NULL; /* Hold a string to find */ char finder[81]; /* Hold a string to show */ char shower[81]; /* Filename */ char filename[1024]; /* Describe this thing */ char caption[128]; /* Path buffer */ char path[1024]; /* General buffer */ char buf[1024]; /* Lower case version of the buffer, for searching */ char lc_buf[1024]; /* Aux pointer for making lc_buf (and find!) lowercase */ cptr lc_buf_ptr; /* Sub-menu information */ char hook[62][32]; void (*old_hook) (void); /* Get size */ Term_get_size(&wid, &hgt); /* Wipe finder */ finder[0] = 0; /* Wipe shower */ shower[0] = 0; /* Wipe caption */ caption[0] = 0; /* Wipe the hooks */ for (i = 0; i < 62; i++) { hook[i][0] = '\0'; } /* Copy the filename */ strcpy(filename, name); n = strlen(filename); /* Extract the tag from the filename */ for (i = 0; i < n; i++) { if (filename[i] == '#') { filename[i] = '\0'; tag = filename + i + 1; break; } } /* Redirect the name */ name = filename; /* Hack XXX XXX XXX */ if (what) { /* Caption */ strcpy(caption, what); /* Access the "file" */ strcpy(path, name); /* Open */ fff = my_fopen(path, "r"); } /* Look in "help" */ if (!fff) { /* Caption */ strnfmt(caption, 128, "Help file '%s'", name); /* Build the filename */ path_make(path, ANGBAND_DIR_HELP, name); /* Open the file */ fff = my_fopen(path, "r"); } /* Look in "info" */ if (!fff) { /* Caption */ strnfmt(caption, 128, "Info file '%s'", name); /* Build the filename */ path_make(path, ANGBAND_DIR_INFO, name); /* Open the file */ fff = my_fopen(path, "r"); } /* Oops */ if (!fff) { /* Message */ msgf("Cannot open '%s'.", name); message_flush(); /* Oops */ return (TRUE); } /* Remember what the resize hook was */ old_hook = angband_term[0]->resize_hook; /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = resize_show_file; /* Remember essentials for resizing */ resize_name = name; resize_what = what; resize_line = line; resize_hgt = hgt; /* Pre-Parse the file */ while (TRUE) { /* Read a line or stop */ if (my_raw_fgets(fff, buf, 1024)) break; /* XXX Parse "menu" items */ if (prefix(buf, "***** ")) { /* Notice "menu" requests */ if ((buf[6] == '[') && (isdigit(buf[7]) || isalpha(buf[7]))) { /* This is a menu file */ menu = TRUE; /* Extract the menu item */ k = isdigit(buf[7]) ? D2I(buf[7]) : buf[7] - 'A' + 10; if ((buf[8] == ']') && (buf[9] == ' ')) { /* Extract the menu item */ strncpy(hook[k], buf + 10, 31); /* Make sure it's null-terminated */ hook[k][31] = '\0'; } } /* Notice "tag" requests */ else if (buf[6] == '<') { if (tag) { /* Remove the closing '>' of the tag */ buf[strlen(buf) - 1] = '\0'; /* Compare with the requested tag */ if (streq(buf + 7, tag)) { /* Remember the tagged line */ line = next; } } } /* Skip this */ continue; } /* Count the "real" lines */ next++; } if (mode != RESIZE_SHOW_FILE) screen_save(); /* Save the number of "real" lines */ size = next; /* Display the file */ while (TRUE) { /* Clear screen */ Term_clear(); /* Restart when necessary */ if (line >= size) line = 0; /* Re-open the file if needed */ if (next > line) { /* Close it */ my_fclose(fff); /* Hack -- Re-Open the file */ fff = my_fopen(path, "r"); /* Oops */ if (!fff) { screen_load(); return (FALSE); } /* File has been restarted */ next = 0; } /* Goto the selected line */ while (next < line) { /* Get a line */ if (my_raw_fgets(fff, buf, 1024)) break; /* Skip tags/links */ if (prefix(buf, "***** ")) continue; /* Count the lines */ next++; } /* Dump the next hgt - 4 lines of the file */ for (i = 0; i < hgt - 4;) { /* Hack -- track the "first" line */ if (!i) line = next; /* Get a line of the file or stop */ if (my_raw_fgets(fff, buf, 1024)) break; /* Hack -- skip "special" lines */ if (prefix(buf, "***** ")) continue; /* Count the "real" lines */ next++; /* Make a lower case version of buf for searching */ strcpy(lc_buf, buf); for (lc_buf_ptr = lc_buf; *lc_buf_ptr != 0; lc_buf_ptr++) { lc_buf[lc_buf_ptr - lc_buf] = tolower(*lc_buf_ptr); } /* Hack -- keep searching */ if (find && !i && !strstr(lc_buf, find)) continue; /* Hack -- stop searching */ find = NULL; /* Dump the line */ put_fstr(0, i + 2, "%s", buf); /* Hilite "shower" */ if (shower[0]) { cptr str = lc_buf; /* Display matches */ while ((str = strstr(str, shower)) != NULL) { int offset = fmt_offset(lc_buf, str); /* Display the match */ put_fstr(offset, i + 2, CLR_YELLOW "%s", shower); /* Advance */ str += strlen(shower); } } /* Count the printed lines */ i++; } /* Hack -- failed search */ if (find) { bell("Search string not found!"); line = back; find = NULL; continue; } /* Show a general "title" */ prtf(0, 0, "[%s %s, %s, Line %d/%d]", VERSION_NAME, VERSION_STRING, caption, line, size); /* Prompt -- with menu */ if (menu) { /* Prompt -- small files */ if (size <= hgt - 4) { /* Wait for it */ prtf(0, hgt - 1, "[Press a Number, or ESC to exit.]"); } /* Prompt -- large files */ else { /* Wait for it */ prtf(0, hgt - 1, "[Press a Number, Return, Space, -, =, /, \\, or ESC to exit.]"); } } else { /* Prompt -- small files */ if (size <= hgt - 4) { /* Wait for it */ prtf(0, hgt - 1, "[Press ESC to exit.]"); } /* Prompt -- large files */ else { /* Wait for it */ prtf(0, hgt - 1, "[Press Return, Space, -, =, /, \\, or ESC to exit.]"); } } /* Leave in case of resize */ if (mode == RESIZE_SHOW_FILE) { /* Hack: the file will be closed by the other instance of show_file */ return (TRUE); } else { /* Get a keypress */ k = inkey(); } /* Correct hgt after a resize */ if (hgt != resize_hgt) hgt = resize_hgt; /* Hack -- return to last screen */ if (k == '<') break; /* Show the help for the help */ if (k == '?') { /* Hack - prevent silly recursion */ if (!streq(name, "helpinfo.txt")) show_file("helpinfo.txt", NULL, 0, mode); } /* Hack -- try showing */ if (k == '=') { /* Get "shower" */ prtf(0, hgt - 1, "Show: "); (void)askfor_aux(shower, 80); } /* Hack -- try finding */ if (k == '/') { /* Get "finder" */ prtf(0, hgt - 1, "Find: "); if (askfor_aux(finder, 80)) { /* Find it */ find = finder; back = line; line = line + 1; /* Make finder lowercase */ for (cnt = 0; finder[cnt] != 0; cnt++) { finder[cnt] = tolower(finder[cnt]); } /* Show it */ strcpy(shower, finder); } } /* Hack -- try finding again */ if (k == '\\') { if (finder) { /* Find it */ find = finder; back = 0; line = line + 1; /* Show it */ strcpy(shower, finder); } } /* Go to a specific line */ if (k == '#') { char tmp[81]; prtf(0, hgt - 1, "Goto Line: "); strcpy(tmp, "0"); if (askfor_aux(tmp, 80)) { line = atoi(tmp); } } /* Go to a specific file */ if (k == '%') { char tmp[81]; prtf(0, hgt - 1, "Goto File: "); strcpy(tmp, "help.hlp"); if (askfor_aux(tmp, 80)) { if (!show_file(tmp, NULL, 0, mode)) k = ESCAPE; } } /* Go back half a page */ if (k == '-') { line = line - (hgt - 4) / 2; if (line < 0) line = 0; } /* Advance half a page */ if (k == '+') { line = line + (hgt - 4) / 2; } /* Advance a single line */ if ((k == '\n') || (k == '\r')) { line = line + 1; } /* Advance one page */ if (k == ' ') { line = line + hgt - 4; } /* Recurse on numbers */ if (menu) { int key = -1; if (isdigit(k)) key = D2I(k); else if (isalpha(k)) key = k - 'A' + 10; if ((key > -1) && hook[key][0]) { /* Recurse on that file */ if (!show_file(hook[key], NULL, 0, mode)) k = ESCAPE; } } /* Dump to file */ if (k == '|') { FILE *ffp; char outfile[1024]; char xtmp[82]; /* Start with an empty filename */ xtmp[0] = '\0'; /* Get a filename */ if (!get_string(xtmp, 80, "File name: ")) continue; /* Check for a "valid" name */ if (!(xtmp[0] && (xtmp[0] != ' '))) continue; /* Build the filename */ path_make(outfile, ANGBAND_DIR_USER, xtmp); /* Close the input file */ my_fclose(fff); /* Hack -- Re-Open the input file */ fff = my_fopen(path, "r"); /* Open the output file */ ffp = my_fopen(outfile, "w"); /* Oops */ if (!(fff && ffp)) { msgf("Failed to open file."); k = ESCAPE; break; } /* Write the file line by line */ while (!my_raw_fgets(fff, xtmp, 80)) { froff(ffp, "%s\n", xtmp); } /* Close the files */ my_fclose(fff); my_fclose(ffp); /* Hack -- Re-Open the input file */ fff = my_fopen(path, "r"); } /* Exit on escape */ if (k == ESCAPE) break; } /* Restore the screen */ screen_load(); /* Close the file */ my_fclose(fff); /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = old_hook; /* The size may have changed during the menu display */ angband_term[0]->resize_hook(); /* Hack - Flush it */ Term_fresh(); /* Escape */ if (k == ESCAPE) return (FALSE); /* Normal return */ return (TRUE); } /* * Peruse the On-Line-Help */ void do_cmd_help(void) { /* Peruse the main help file */ (void)show_file("help.hlp", NULL, 0, 0); } /* * Process the player name. * Extract a clean "base name". * Build the savefile name if needed. */ void process_player_name(bool sf) { int i, k = 0; /* Cannot be too long */ if (strlen(player_name) > 15) { /* Name too long */ quit_fmt("The name '%s' is too long!", player_name); } /* Cannot contain "icky" characters */ for (i = 0; player_name[i]; i++) { /* No control characters */ if (iscntrl(player_name[i])) { /* Illegal characters */ quit_fmt("The name '%s' contains control chars!", player_name); } } #ifdef MACINTOSH /* Extract "useful" letters */ for (i = 0; player_name[i]; i++) { char c = player_name[i]; /* Convert "dot" to "underscore" */ if (c == '.') c = '_'; /* Accept all the letters */ player_base[k++] = c; } #else /* Extract "useful" letters */ for (i = 0; player_name[i]; i++) { char c = player_name[i]; /* Accept some letters */ if (isalpha(c) || isdigit(c)) player_base[k++] = c; /* Convert space, dot, and underscore to underscore */ else if (strchr(". _", c)) player_base[k++] = '_'; } #endif #if defined(WINDOWS) || defined(MSDOS) /* Hack -- max length */ if (k > 8) k = 8; #endif /* Terminate */ player_base[k] = '\0'; /* Require a "base" name */ if (!player_base[0]) strcpy(player_base, "PLAYER"); #ifdef SAVEFILE_MUTABLE /* Accept */ sf = TRUE; #endif /* Change the savefile name */ if (sf) { char temp[128]; #ifdef SAVEFILE_USE_UID /* Rename the savefile, using the player_uid and player_base */ strnfmt(temp, 128, "%d.%s", player_uid, player_base); #else /* Rename the savefile, using the player_base */ strnfmt(temp, 128, "%s", player_base); #endif #ifdef VM /* Hack -- support "flat directory" usage on VM/ESA */ strnfmt(temp, 128, "%s.sv", player_base); #endif /* VM */ /* Build the filename */ path_make(savefile, ANGBAND_DIR_SAVE, temp); } } /* * Gets a name for the character, reacting to name changes. * * Assumes that "display_player(0)" has just been called * * Perhaps we should NOT ask for a name (at "birth()") on * Unix machines? XXX XXX */ void change_player_name(void) { char tmp[32]; /* Clear last line */ clear_from(22); /* Prompt and ask */ prtf(2, 23, "[Enter your player's name above, or hit ESCAPE]"); /* Ask until happy */ while (1) { /* Go to the "name" field */ Term_gotoxy(COL_NAME + WID_NAME, 2); /* Save the player name */ strcpy(tmp, player_name); /* Get an input, ignore "Escape" */ if (askfor_aux(tmp, 16)) strcpy(player_name, tmp); /* Process the player name */ process_player_name(FALSE); /* All done */ break; } /* Re-Draw the name (in light blue) */ put_fstr(COL_NAME + WID_NAME, 2, CLR_L_BLUE "%-15.15s", player_name); /* Erase the prompt, etc */ clear_from(22); } /* Gets a name for the character, reacting to name changes. * Taken from V 2.9.0. */ void get_character_name(void) { char tmp[16]; /* Save the player name */ strcpy(tmp, player_name); /* Prompt for a new name */ if (get_string(tmp, sizeof(tmp), "Enter a name for your character: ")) { /* Use the name */ strcpy(player_name, tmp); /* Process the player name */ process_player_name(FALSE); } } /* * Hack -- commit suicide */ void do_cmd_suicide(void) { int i; /* Flush input */ flush(); /* Verify Retirement */ if (p_ptr->state.total_winner) { /* Verify */ if (!get_check("Do you want to retire? ")) return; } /* Verify Suicide */ else { /* Verify */ if (!get_check("Do you really want to commit suicide? ")) return; if (!p_ptr->state.noscore) { /* Special Verification for suicide */ prtf(0, 0, "Please verify SUICIDE by typing the '@' sign: "); flush(); i = inkey(); clear_msg(); if (i != '@') return; } } /* Stop playing */ p_ptr->state.playing = FALSE; /* Kill the player */ p_ptr->state.is_dead = TRUE; /* Leaving */ p_ptr->state.leaving = TRUE; /* Cause of death */ (void)strcpy(p_ptr->state.died_from, "Quitting"); } /* * Save the game */ void do_cmd_save_game(int is_autosave) { /* Autosaves do not disturb */ if (is_autosave) { msgf("Autosaving the game..."); } else { /* Disturb the player */ disturb(TRUE); } /* Clear messages */ message_flush(); /* Handle stuff */ handle_stuff(); /* Message */ prtf(0, 0, "Saving game..."); /* Refresh */ Term_fresh(); /* The player is not dead */ (void)strcpy(p_ptr->state.died_from, "(saved)"); /* Forbid suspend */ signals_ignore_tstp(); /* Save the player */ if (save_player()) { prtf(0, 0, "Saving game... done."); } /* Save failed (oops) */ else { prtf(0, 0, "Saving game... failed!"); } /* Allow suspend again */ signals_handle_tstp(); /* Refresh */ Term_fresh(); /* Clear messages. */ message_flush(); /* Hack -- erase the message line. */ clear_msg(); /* Note that the player is not dead */ (void)strcpy(p_ptr->state.died_from, "(alive and well)"); } /* * Save the game and exit */ void do_cmd_save_and_exit(void) { p_ptr->state.playing = FALSE; /* Leaving */ p_ptr->state.leaving = TRUE; } /* * Get a random line from a file * Based on the monster speech patch by Matt Graham, */ errr get_rnd_line(cptr file_name, int entry, char *output) { FILE *fp; char buf[1024]; int line, counter, test, numentries; /* Build the filename */ path_make(buf, ANGBAND_DIR_FILE, file_name); /* Open the file */ fp = my_fopen(buf, "r"); /* Failed */ if (!fp) return (-1); /* Find the entry of the monster */ while (TRUE) { /* Get a line from the file */ if (my_fgets(fp, buf, 1024) == 0) { /* Look for lines starting with 'N:' */ if ((buf[0] == 'N') && (buf[1] == ':')) { /* Allow default lines */ if (buf[2] == '*') break; /* Get the monster number */ else if (sscanf(&(buf[2]), "%d", &test) != EOF) { /* Is it the right monster? */ if (test == entry) break; } else { /* Error while converting the monster number */ msgf("Error - end of file."); my_fclose(fp); return (-1); } } } else { /* Reached end of file */ my_fclose(fp); return (-1); } } /* Get the number of entries */ while (TRUE) { /* Get the line */ if (my_fgets(fp, buf, 1024) == 0) { /* Look for the number of entries */ if (isdigit(buf[0])) { /* Get the number of entries */ numentries = atoi(buf); break; } } else { /* Reached end of file without finding the number */ msgf("Error - end of file."); my_fclose(fp); return (-1); } } if (numentries > 0) { /* Grab an appropriate line number */ line = randint1(numentries); /* Get the random line */ for (counter = 0; counter < line; counter++) { /* Try to read the line, skipping comments */ while (TRUE) { test = my_fgets(fp, buf, 1024); if (test != 0 || buf[0] != '#') break; } if (test != 0) { /* Error - End of file */ msgf("Error - end of file."); my_fclose(fp); return (-1); } } /* Copy the line */ strcpy(output, buf); } else { /* Close the file */ my_fclose(fp); return (-1); } /* Close the file */ my_fclose(fp); /* Success */ return (0); } zangband/src/flavor.c0000755000000000000000000011760210250356274013602 0ustar rootroot/* File: flavor.c */ /* Purpose: Object flavor code */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Max sizes of the following arrays */ #define MAX_ROCKS 68 /* Used with rings (min 55) */ #define MAX_AMULETS 30 /* Used with amulets (min 20) */ #define MAX_WOODS 40 /* Used with staffs (min 30) */ #define MAX_METALS 39 /* Used with wands/rods (min 30/29) */ #define MAX_COLORS 76 /* Used with potions (min 64) */ #define MAX_SHROOM 25 /* Used with mushrooms (min 20) */ #define MAX_TITLES 54 /* Used with scrolls (min 48) */ #define MAX_SYLLABLES 164 /* Used with scrolls (see below) */ /* * Rings (adjectives and colors) */ static cptr ring_adj[MAX_ROCKS] = { /* 0-9 */ "Alexandrite", "Amethyst", "Aquamarine", "Azurite", "Beryl", "Bloodstone", "Calcite", "Carnelian", "Corundum", "Diamond", /* 10-19 */ "Emerald", "Fluorite", "Garnet", "Granite", "Jade", "Jasper", "Lapis Lazuli", "Malachite", "Marble", "Moonstone", /* 20-29 */ "Onyx", "Opal", "Pearl", "Quartz", "Quartzite", "Rhodonite", "Ruby", "Sapphire", "Tiger Eye", "Topaz", /* 30-39 */ "Turquoise", "Zircon", "Platinum", "Bronze", "Gold", "Obsidian", "Silver", "Tortoise Shell", "Mithril", "Jet", /* 40-49 */ "Engagement", "Adamantite", "Wire", "Dilithium", "Bone", "Wooden", "Iron", "Serpent", "Wedding", "Double", /* 50-59 */ "Plain", "Brass", "Scarab", "Shining", "Rusty", "Transparent", "Cat's-Eye", "Chrysoberyl", "Serpentine", "Spinel", /* 60-67 */ "Topaz", "Morganite", "Heliodor", "Tourmaline", "Chalcedony", "Peridot", "Hematite", "Coral" }; static byte ring_col[MAX_ROCKS] = { /* 0-9 */ TERM_GREEN, TERM_VIOLET, TERM_L_BLUE, TERM_L_BLUE, TERM_L_GREEN, TERM_RED, TERM_WHITE, TERM_RED, TERM_SLATE, TERM_WHITE, /* 10-19 */ TERM_GREEN, TERM_L_GREEN, TERM_RED, TERM_L_DARK, TERM_L_GREEN, TERM_UMBER, TERM_BLUE, TERM_GREEN, TERM_WHITE, TERM_L_WHITE, /* 20-29 */ TERM_L_RED, TERM_L_WHITE, TERM_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_RED, TERM_RED, TERM_BLUE, TERM_YELLOW, TERM_YELLOW, /* 30-39 */ TERM_L_BLUE, TERM_L_UMBER, TERM_WHITE, TERM_L_UMBER, TERM_YELLOW, TERM_L_DARK, TERM_L_WHITE, TERM_GREEN, TERM_L_BLUE, TERM_L_DARK, /* 40-49 */ TERM_YELLOW, TERM_VIOLET, TERM_UMBER, TERM_L_WHITE, TERM_WHITE, TERM_UMBER, TERM_BLUE, TERM_GREEN, TERM_YELLOW, TERM_ORANGE, /* 50-59 */ TERM_YELLOW, TERM_ORANGE, TERM_L_GREEN, TERM_YELLOW, TERM_RED, TERM_WHITE, TERM_YELLOW, TERM_YELLOW, TERM_L_GREEN, TERM_RED, /* 60-67 */ TERM_YELLOW, TERM_L_RED, TERM_YELLOW, TERM_GREEN, TERM_L_DARK, TERM_L_GREEN, TERM_L_DARK, TERM_L_RED }; /* * Amulets (adjectives and colors) */ static cptr amulet_adj[MAX_AMULETS] = { /* 0-9 */ "Amber", "Driftwood", "Coral", "Agate", "Ivory", "Obsidian", "Bone", "Brass", "Bronze", "Pewter", /* 10-19 */ "Tortoise Shell", "Golden", "Azure", "Crystal", "Silver", "Copper", "Rosetted", "Spiral", "Star", "Square", /* 20-29 */ "Hexagonal", "Steel", "Skull", "Platinum", "Electrum", "Glass", "Cracked", "Heartwood", "Bark", "Mirrored" }; static byte amulet_col[MAX_AMULETS] = { /* 0-9 */ TERM_YELLOW, TERM_L_UMBER, TERM_WHITE, TERM_L_WHITE, TERM_WHITE, TERM_L_DARK, TERM_WHITE, TERM_L_UMBER, TERM_L_UMBER, TERM_SLATE, /* 10-19 */ TERM_GREEN, TERM_YELLOW, TERM_L_BLUE, TERM_L_BLUE, TERM_L_WHITE, TERM_L_UMBER, TERM_VIOLET, TERM_VIOLET, TERM_YELLOW, TERM_L_UMBER, /* 20-29 */ TERM_L_DARK, TERM_WHITE, TERM_L_DARK, TERM_L_WHITE, TERM_WHITE, TERM_L_RED, TERM_RED, TERM_UMBER, TERM_UMBER, TERM_WHITE }; /* * Staffs (adjectives and colors) */ static cptr staff_adj[MAX_WOODS] = { /* 0-9 */ "Aspen", "Balsa", "Banyan", "Birch", "Cedar", "Cottonwood", "Cypress", "Dogwood", "Elm", "Willow", /* 10-19 */ "Hemlock", "Hickory", "Ironwood", "Locust", "Mahogany", "Maple", "Mulberry", "Oak", "Pine", "Redwood", /* 20-29 */ "Rosewood", "Spruce", "Sycamore", "Teak", "Walnut", "Mistletoe", "Hawthorn", "Bamboo", "Silver", "Runed", /* 30-39 */ "Golden", "Ashen", "Gnarled", "Ivory", "Sandalwood", "Yew", "Thorned", "Charred", "Darkwood", "Flowering" }; static byte staff_col[MAX_WOODS] = { /* 0-9 */ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, /* 10-19 */ TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER, TERM_L_UMBER, TERM_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_RED, /* 20-29 */ TERM_RED, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER, TERM_GREEN, TERM_L_UMBER, TERM_L_UMBER, TERM_L_WHITE, TERM_UMBER, /* 30-39 */ TERM_YELLOW, TERM_SLATE, TERM_UMBER, TERM_L_WHITE, TERM_YELLOW, TERM_L_UMBER, TERM_L_UMBER, TERM_L_DARK, TERM_L_DARK, TERM_L_GREEN }; /* * Wands (adjectives and colors) */ static cptr wand_adj[MAX_METALS] = { /* 0-9 */ "Aluminium", "Cast Iron", "Chromium", "Copper", "Gold", "Iron", "Magnesium", "Molybdenum", "Nickel", "Rusty", /* 10-19 */ "Silver", "Steel", "Tin", "Titanium", "Tungsten", "Zirconium", "Zinc", "Aluminium-Plated", "Copper-Plated", "Gold-Plated", /* 20-29 */ "Nickel-Plated", "Silver-Plated", "Steel-Plated", "Tin-Plated", "Zinc-Plated", "Mithril-Plated", "Mithril", "Runed", "Bronze", "Brass", /* 30-38 */ "Platinum", "Lead", "Lead-Plated", "Ivory", "Adamantite", "Uridium", "Long", "Short", "Hexagonal" }; static byte wand_col[MAX_METALS] = { /* 0-9 */ TERM_L_BLUE, TERM_L_DARK, TERM_WHITE, TERM_L_UMBER, TERM_YELLOW, TERM_SLATE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_UMBER, TERM_RED, /* 10-19 */ TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_WHITE, TERM_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_BLUE, TERM_L_UMBER, TERM_YELLOW, /* 20-29 */ TERM_L_UMBER, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_BLUE, TERM_L_BLUE, TERM_UMBER, TERM_L_UMBER, TERM_L_UMBER, /* 30-38 */ TERM_WHITE, TERM_SLATE, TERM_SLATE, TERM_WHITE, TERM_VIOLET, TERM_L_RED, TERM_L_BLUE, TERM_BLUE, TERM_RED }; /* * Rods (adjectives and colors). * Efficiency -- copied from wand arrays */ static cptr rod_adj[MAX_METALS]; static byte rod_col[MAX_METALS]; /* * Mushrooms (adjectives and colors) */ static cptr food_adj[MAX_SHROOM] = { /* 0-9 */ "Blue", "Black", "Black Spotted", "Brown", "Dark Blue", "Dark Green", "Dark Red", "Yellow", "Furry", "Green", /* 10-19 */ "Grey", "Light Blue", "Light Green", "Violet", "Red", "Slimy", "Tan", "White", "White Spotted", "Wrinkled", /* 20-24 */ "Strange", "Silver", "Glowing", "Orange", "Dried", }; static byte food_col[MAX_SHROOM] = { /* 0-9 */ TERM_BLUE, TERM_L_DARK, TERM_L_DARK, TERM_UMBER, TERM_BLUE, TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_L_WHITE, TERM_GREEN, /* 10-19 */ TERM_SLATE, TERM_L_BLUE, TERM_L_GREEN, TERM_VIOLET, TERM_RED, TERM_SLATE, TERM_L_UMBER, TERM_WHITE, TERM_WHITE, TERM_UMBER, /* 20-24 */ TERM_L_BLUE, TERM_WHITE, TERM_YELLOW, TERM_ORANGE, TERM_UMBER, }; /* * Color adjectives and colors, for potions. * Hack -- The first four entries are hard-coded. * (water, apple juice, slime mold juice, something) */ static cptr potion_adj[MAX_COLORS] = { /* Fixed colors */ "Clear", "Light Brown", "Icky Green", "xxx", /* 4-9 */ "Azure", "Blue", "Blue Speckled", "Black", "Brown", "Brown Speckled", /* 10-19 */ "Bubbling", "Chartreuse", "Cloudy", "Copper Speckled", "Crimson", "Cyan", "Dark Blue", "Dark Green", "Dark Red", "Gold Speckled", /* 20-29 */ "Green", "Green Speckled", "Grey", "Grey Speckled", "Hazy", "Indigo", "Light Blue", "Light Green", "Magenta", "Metallic Blue", /* 30-39 */ "Metallic Red", "Metallic Green", "Metallic Purple", "Misty", "Orange", "Orange Speckled", "Pink", "Pink Speckled", "Puce", "Purple", /* 40-49 */ "Purple Speckled", "Red", "Red Speckled", "Silver Speckled", "Smoky", "Tangerine", "Violet", "Vermilion", "White", "Yellow", /* 50-59 */ "Violet Speckled", "Pungent", "Clotted Red", "Viscous Pink", "Oily Yellow", "Gloopy Green", "Shimmering", "Coagulated Crimson", "Yellow Speckled", /* 60-69 */ "Gold", "Manly", "Stinking", "Oily Black", "Ichor", "Ivory White", "Sky Blue", "Bloody", "Inky Black", "Silver Flecked", "Red Flecked", /* 70-75 */ "Green Flecked", "Sea Green", "Umber", "Layered", "Fizzy Yellow", "Fizzy Green", }; static byte potion_col[MAX_COLORS] = { /* Fixed colors */ TERM_WHITE, TERM_L_UMBER, TERM_GREEN, 0, /* 4-9 */ TERM_L_BLUE, TERM_BLUE, TERM_BLUE, TERM_L_DARK, TERM_UMBER, TERM_UMBER, /* 10-19 */ TERM_L_WHITE, TERM_L_GREEN, TERM_WHITE, TERM_L_UMBER, TERM_RED, TERM_L_BLUE, TERM_BLUE, TERM_GREEN, TERM_RED, TERM_YELLOW, /* 20-29 */ TERM_GREEN, TERM_GREEN, TERM_SLATE, TERM_SLATE, TERM_L_WHITE, TERM_VIOLET, TERM_L_BLUE, TERM_L_GREEN, TERM_RED, TERM_BLUE, /* 30-39 */ TERM_RED, TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_ORANGE, TERM_ORANGE, TERM_L_RED, TERM_L_RED, TERM_VIOLET, TERM_VIOLET, /* 40-49 */ TERM_VIOLET, TERM_RED, TERM_RED, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE, TERM_VIOLET, TERM_RED, TERM_WHITE, TERM_YELLOW, /* 50-59 */ TERM_VIOLET, TERM_L_RED, TERM_RED, TERM_L_RED, TERM_YELLOW, TERM_GREEN, TERM_VIOLET, TERM_RED, TERM_YELLOW, TERM_YELLOW, /* 60-69 */ TERM_L_UMBER, TERM_UMBER, TERM_L_DARK, TERM_RED, TERM_WHITE, TERM_L_BLUE, TERM_RED, TERM_L_DARK, TERM_WHITE, TERM_RED, /* 70-75 */ TERM_GREEN, TERM_L_GREEN, TERM_UMBER, TERM_L_BLUE, TERM_YELLOW, TERM_L_GREEN, }; /* * Syllables for scrolls (must be 1-4 letters each) */ static cptr syllables[MAX_SYLLABLES] = { "a", "ab", "ag", "aks", "ala", "an", "ankh", "app", "arg", "arze", "ash", "aus", "ban", "bar", "bat", "bek", "bie", "bin", "bit", "bjor", "blu", "bot", "bu", "byt", "comp", "con", "cos", "cre", "dalf", "dan", "den", "der", "doe", "dok", "eep", "el", "eng", "er", "ere", "erk", "esh", "evs", "fa", "fid", "flit", "for", "fri", "fu", "gan", "gar", "glen", "gop", "gre", "ha", "he", "hyd", "i", "ing", "ion", "ip", "ish", "it", "ite", "iv", "jo", "kho", "kli", "klis", "la", "lech", "man", "mar", "me", "mi", "mic", "mik", "mon", "mung", "mur", "nag", "nej", "nelg", "nep", "ner", "nes", "nis", "nih", "nin", "o", "od", "ood", "org", "orn", "ox", "oxy", "pay", "pet", "ple", "plu", "po", "pot", "prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", "sa", "san", "sat", "see", "sef", "seh", "shu", "ski", "sna", "sne", "snik", "sno", "so", "sol", "sri", "sta", "sun", "ta", "tab", "tem", "ther", "ti", "tox", "trol", "tue", "turs", "u", "ulk", "um", "un", "uni", "ur", "val", "viv", "vly", "vom", "wah", "wed", "werg", "wex", "whon", "wun", "xi", "yerg", "yp", "zun", "tri", "blaa", "jah", "bul", "on", "foo", "ju", "xuxu" }; /* * Hold the titles of scrolls, 6 to 14 characters each * Also keep an array of scroll colors (always WHITE for now) */ static char scroll_adj[MAX_TITLES][16]; static byte scroll_col[MAX_TITLES]; /* * Certain items, if aware, are known instantly * This function is used only by "flavor_init()" */ static bool object_easy_know(int i) { object_kind *k_ptr = &k_info[i]; if (FLAG(k_ptr, TR_EASY_KNOW)) return (TRUE); /* Nope */ return (FALSE); } /* * Certain items have a flavor * This function is used only by "flavor_init()" */ static bool object_flavor(int k_idx) { object_kind *k_ptr = &k_info[k_idx]; /* Analyze the item */ switch (k_ptr->tval) { case TV_AMULET: { return (0x80 + amulet_col[k_ptr->sval]); } case TV_RING: { return (0x90 + ring_col[k_ptr->sval]); } case TV_STAFF: { return (0xA0 + staff_col[k_ptr->sval]); } case TV_WAND: { return (0xB0 + wand_col[k_ptr->sval]); } case TV_ROD: { return (0xC0 + rod_col[k_ptr->sval]); } case TV_SCROLL: { return (0xD0 + scroll_col[k_ptr->sval]); } case TV_POTION: { return (0xE0 + potion_col[k_ptr->sval]); } case TV_FOOD: { if (k_ptr->sval < SV_FOOD_MIN_FOOD) { return (0xF0 + food_col[k_ptr->sval]); } break; } } /* No flavor */ return (0); } void get_table_name(char *out_string, bool quotes) { int testcounter = 2; int len = 0; /* Empty string */ out_string[0] = 0; if (quotes) { strnfcat(out_string, 18, &len, "'"); } if (one_in_(3)) { while (testcounter--) strnfcat(out_string, 18, &len, syllables[randint0(MAX_SYLLABLES)]); } else { char Syllable[80]; while (testcounter--) { (void)get_rnd_line("elvish.txt", 0, Syllable); strnfcat(out_string, 18, &len, "%s", Syllable); } } if (quotes) { out_string[1] = toupper(out_string[1]); strnfcat(out_string, 18, &len, "'"); } else { out_string[0] = toupper(out_string[0]); } } /* * Prepare the "variable" part of the "k_info" array. * * The "color"/"metal"/"type" of an item is its "flavor". * For the most part, flavors are assigned randomly each game. * * Initialize descriptions for the "colored" objects, including: * Rings, Amulets, Staffs, Wands, Rods, Food, Potions, Scrolls. * * The first 4 entries for potions are fixed (Water, Apple Juice, * Slime Mold Juice, Unused Potion). * * Scroll titles are always between 6 and 14 letters long. This is * ensured because every title is composed of whole words, where every * word is from 1 to 8 letters long (one or two syllables of 1 to 4 * letters each), and that no scroll is finished until it attempts to * grow beyond 15 letters. The first time this can happen is when the * current title has 6 letters and the new word has 8 letters, which * would result in a 6 letter scroll title. * * Duplicate titles are avoided by requiring that no two scrolls share * the same first four letters (not the most efficient method, and not * the least efficient method, but it will always work). * * Hack -- make sure everything stays the same for each saved game * This is accomplished by the use of a saved "random seed", as in * "town_gen()". Since no other functions are called while the special * seed is in effect, so this function is pretty "safe". * * Note that the "hacked seed" may provide an RNG with alternating parity! */ void flavor_init(void) { int i, j; byte temp_col; cptr temp_adj; /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant flavors */ Rand_value = seed_flavor; /* Efficiency -- Rods/Wands share initial array */ for (i = 0; i < MAX_METALS; i++) { rod_adj[i] = wand_adj[i]; rod_col[i] = wand_col[i]; } /* Rings have "ring colors" */ for (i = 0; i < MAX_ROCKS; i++) { j = randint0(MAX_ROCKS); temp_adj = ring_adj[i]; ring_adj[i] = ring_adj[j]; ring_adj[j] = temp_adj; temp_col = ring_col[i]; ring_col[i] = ring_col[j]; ring_col[j] = temp_col; } /* Amulets have "amulet colors" */ for (i = 0; i < MAX_AMULETS; i++) { j = randint0(MAX_AMULETS); temp_adj = amulet_adj[i]; amulet_adj[i] = amulet_adj[j]; amulet_adj[j] = temp_adj; temp_col = amulet_col[i]; amulet_col[i] = amulet_col[j]; amulet_col[j] = temp_col; } /* Staffs */ for (i = 0; i < MAX_WOODS; i++) { j = randint0(MAX_WOODS); temp_adj = staff_adj[i]; staff_adj[i] = staff_adj[j]; staff_adj[j] = temp_adj; temp_col = staff_col[i]; staff_col[i] = staff_col[j]; staff_col[j] = temp_col; } /* Wands */ for (i = 0; i < MAX_METALS; i++) { j = randint0(MAX_METALS); temp_adj = wand_adj[i]; wand_adj[i] = wand_adj[j]; wand_adj[j] = temp_adj; temp_col = wand_col[i]; wand_col[i] = wand_col[j]; wand_col[j] = temp_col; } /* Rods */ for (i = 0; i < MAX_METALS; i++) { j = randint0(MAX_METALS); temp_adj = rod_adj[i]; rod_adj[i] = rod_adj[j]; rod_adj[j] = temp_adj; temp_col = rod_col[i]; rod_col[i] = rod_col[j]; rod_col[j] = temp_col; } /* Foods (Mushrooms) */ for (i = 0; i < MAX_SHROOM; i++) { j = randint0(MAX_SHROOM); temp_adj = food_adj[i]; food_adj[i] = food_adj[j]; food_adj[j] = temp_adj; temp_col = food_col[i]; food_col[i] = food_col[j]; food_col[j] = temp_col; } /* Potions */ for (i = 4; i < MAX_COLORS; i++) { j = rand_range(4, MAX_COLORS - 1); temp_adj = potion_adj[i]; potion_adj[i] = potion_adj[j]; potion_adj[j] = temp_adj; temp_col = potion_col[i]; potion_col[i] = potion_col[j]; potion_col[j] = temp_col; } /* Scrolls (random titles, always white) */ for (i = 0; i < MAX_TITLES; i++) { /* Get a new title */ while (TRUE) { char buf[80]; int buf_len = 0; bool okay; /* Start a new title */ buf[0] = '\0'; /* Collect words until done */ while (1) { int q, s; char tmp[80]; int len = 0; /* Start a new word */ tmp[0] = '\0'; /* Choose one or two syllables */ s = ((randint0(100) < 30) ? 1 : 2); /* Add a one or two syllable word */ for (q = 0; q < s; q++) { /* Add the syllable */ strnfcat(tmp, 80, &len, syllables[randint0(MAX_SYLLABLES)]); } /* Stop before getting too long */ if (strlen(buf) + 1 + strlen(tmp) > 15) break; /* Add a space + word */ strnfcat(buf, 80, &buf_len, " %s", tmp); } /* Save the title */ strcpy(scroll_adj[i], buf + 1); /* Assume okay */ okay = TRUE; /* Check for "duplicate" scroll titles */ for (j = 0; j < i; j++) { cptr hack1 = scroll_adj[j]; cptr hack2 = scroll_adj[i]; /* Compare first four characters */ if (*hack1++ != *hack2++) continue; if (*hack1++ != *hack2++) continue; if (*hack1++ != *hack2++) continue; if (*hack1++ != *hack2++) continue; /* Not okay */ okay = FALSE; /* Stop looking */ break; } /* Break when done */ if (okay) break; } /* All scrolls are white */ scroll_col[i] = TERM_WHITE; } /* Hack -- Use the "complex" RNG */ Rand_quick = FALSE; /* Analyze every object */ for (i = 1; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; /* Skip "empty" objects */ if (!k_ptr->name) continue; /* Extract "flavor" (if any) */ k_ptr->flavor = object_flavor(i); /* No flavor yields aware */ if (!k_ptr->flavor) k_ptr->aware = TRUE; /* Check for "easily known" */ k_ptr->easy_know = object_easy_know(i); } } /* * Creates a description of the item "o_ptr", and stores it in "out_val". * * One can choose the "verbosity" of the description, including whether * or not the "number" of items should be described, and how much detail * should be used when describing the item. * * The given "buf" must be 80 chars long to hold the longest possible * description, which can get pretty long, including incriptions, such as: * "no more Maces of Disruption (Defender) (+10,+10) [+5] (+3 to stealth)". * Note that the inscription will be clipped to keep the total description * under size - 1 chars (plus a terminator). * * Note the use of "object_desc_num()" and "object_desc_int()" as * hyper-efficient, portable, versions of some common "sprintf()" commands. * * Note that all ego-items (when known) append an "Ego-Item Name", unless * the item is also an artifact, which should NEVER happen. * * Note that all artifacts (when known) append an "Artifact Name", so we * have special processing for "Specials" (artifact Lites, Rings, Amulets). * The "Specials" never use "modifiers" if they are "known", since they * have special "descriptions", such as "The Necklace of the Dwarves". * * Special Lite's use the "k_info" base-name (Phial, Star, or Arkenstone), * plus the artifact name, just like any other artifact, if known. * * Special Ring's and Amulet's, if not "aware", use the same code as normal * rings and amulets, and if "aware", use the "k_info" base-name (Ring or * Amulet or Necklace). They will NEVER "append" the "k_info" name. But, * they will append the artifact name, just like any artifact, if known. * * None of the Special Rings/Amulets are "EASY_KNOW", though they could be, * at least, those which have no "pluses", such as the three artifact lites. * * Hack -- Display "The One Ring" as "a Plain Gold Ring" until aware. * * If "pref" then a "numeric" prefix will be pre-pended. * * Mode: * 0 -- The Cloak of Death * 1 -- The Cloak of Death [1,+3] * 2 -- The Cloak of Death [1,+3] (+2 to Stealth) * 3 -- The Cloak of Death [1,+3] (+2 to Stealth) {nifty} */ void object_desc(char *buf, const object_type *o_ptr, int pref, int mode, int max) { cptr basenm, modstr; int power; bool aware = FALSE; bool known = FALSE; bool append_name = FALSE; bool show_weapon = FALSE; bool show_armour = FALSE; cptr s; object_type *bow_ptr; /* damage dice, damage sides, damage bonus, energy */ int dd, ds, db, energy_use; int tmul; long avgdam; int len = 0; object_kind *k_ptr = &k_info[o_ptr->k_idx]; monster_race *r_ptr = &r_info[o_ptr->pval]; /* See if the object is "aware" */ if (object_aware_p(o_ptr)) aware = TRUE; /* See if the object is "known" */ if (object_known_p(o_ptr)) known = TRUE; /* Artifacts are not "aware' unless "known" */ if ((FLAG(o_ptr, TR_INSTA_ART)) && !known) aware = FALSE; /* Extract default "base" string */ basenm = get_object_name(o_ptr); /* Assume no "modifier" string */ modstr = ""; /* Empty description */ buf[0] = '\0'; /* Analyze the object */ switch (o_ptr->tval) { case TV_SKELETON: case TV_BOTTLE: case TV_JUNK: case TV_SPIKE: case TV_FLASK: case TV_CHEST: { /* Some objects are easy to describe */ break; } case TV_FIGURINE: case TV_STATUE: { /* Figurines/Statues */ cptr tmp = mon_race_name(r_ptr); char idol_name[512]; if (!FLAG(r_ptr, RF_UNIQUE)) { strnfmt(idol_name, 512, "%s%s", (is_a_vowel(*tmp) ? "an " : "a "), tmp); modstr = idol_name; } else { modstr = tmp; } break; } case TV_SHOT: case TV_BOLT: case TV_ARROW: case TV_BOW: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_DIGGING: { /* Missiles/ Bows/ Weapons */ show_weapon = TRUE; break; } case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_CROWN: case TV_HELM: case TV_SHIELD: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Armour */ show_armour = TRUE; break; } case TV_LITE: { /* Lites (including a few "Specials") */ break; } case TV_AMULET: { /* Amulets (including a few "Specials") */ /* Known artifacts */ if ((FLAG(k_ptr, TR_INSTA_ART)) && aware) break; /* Color the object */ modstr = amulet_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Amulet~"; else basenm = "& # Amulet~"; break; } case TV_RING: { /* Rings (including a few "Specials") */ /* Known artifacts */ if ((FLAG(k_ptr, TR_INSTA_ART)) && aware) break; /* Color the object */ modstr = ring_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Ring~"; else basenm = "& # Ring~"; /* Hack -- The One Ring */ if (!aware && (o_ptr->sval == SV_RING_POWER)) modstr = "Plain Gold"; break; } case TV_STAFF: { /* Color the object */ modstr = staff_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Staff~"; else basenm = "& # Staff~"; break; } case TV_WAND: { /* Color the object */ modstr = wand_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Wand~"; else basenm = "& # Wand~"; break; } case TV_ROD: { /* Color the object */ modstr = rod_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Rod~"; else basenm = "& # Rod~"; break; } case TV_SCROLL: { /* Color the object */ modstr = scroll_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Scroll~"; else basenm = "& Scroll~ titled \"#\""; break; } case TV_POTION: { /* Color the object */ modstr = potion_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Potion~"; else basenm = "& # Potion~"; break; } case TV_FOOD: { /* Ordinary food is "boring" */ if (o_ptr->sval >= SV_FOOD_MIN_FOOD) break; /* Color the object */ modstr = food_adj[o_ptr->sval]; if (aware) append_name = TRUE; if (((plain_descriptions) && (aware)) || (o_ptr->info & OB_STOREB)) basenm = "& Mushroom~"; else basenm = "& # Mushroom~"; break; } /*** Magic Books ***/ case TV_LIFE_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Life Magic #"; else basenm = "& Life Spellbook~ #"; break; } case TV_SORCERY_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Sorcery #"; else basenm = "& Sorcery Spellbook~ #"; break; } case TV_NATURE_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Nature Magic #"; else basenm = "& Nature Spellbook~ #"; break; } case TV_CHAOS_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Chaos Magic #"; else basenm = "& Chaos Spellbook~ #"; break; } case TV_DEATH_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Death Magic #"; else basenm = "& Death Spellbook~ #"; break; } case TV_TRUMP_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Trump Magic #"; else basenm = "& Trump Spellbook~ #"; break; } case TV_ARCANE_BOOK: { modstr = basenm; if (mp_ptr->spell_book == TV_LIFE_BOOK) basenm = "& Book~ of Arcane Magic #"; else basenm = "& Arcane Spellbook~ #"; break; } case TV_GOLD: { /* Hack -- Gold/Gems */ strcpy(buf, basenm); return; } default: { /* Used in the "inventory" routine */ strcpy(buf, "(nothing)"); return; } } /* The object "expects" a "number" */ if (basenm[0] == '&') { /* Skip the ampersand (and space) */ s = basenm + 2; /* No prefix */ if (!pref) { /* Nothing */ } /* Hack -- None left */ else if (o_ptr->number <= 0) { strnfcat(buf, max, &len, "no more "); } /* Extract the number */ else if (o_ptr->number > 1) { strnfcat(buf, max, &len, "%d ", o_ptr->number); } /* Hack -- The only one of its kind */ else if (known && (FLAG(o_ptr, TR_INSTA_ART))) { strnfcat(buf, max, &len, "The "); } /* A single one, with a vowel in the modifier */ else if ((*s == '#') && (is_a_vowel(modstr[0]))) { strnfcat(buf, max, &len, "an "); } /* A single one, with a vowel */ else if (is_a_vowel(*s)) { strnfcat(buf, max, &len, "an "); } /* A single one, without a vowel */ else { strnfcat(buf, max, &len, "a "); } } /* Hack -- objects that "never" take an article */ else { /* No ampersand */ s = basenm; /* No pref */ if (!pref) { /* Nothing */ } /* Hack -- all gone */ else if (o_ptr->number <= 0) { strnfcat(buf, max, &len, "no more "); } /* Prefix a number if required */ else if (o_ptr->number > 1) { strnfcat(buf, max, &len, "%d ", o_ptr->number); } /* Hack -- The only one of its kind */ else if (known && (FLAG(o_ptr, TR_INSTA_ART))) { strnfcat(buf, max, &len, "The "); } /* Hack -- single items get no prefix */ else { /* Nothing */ } } /* Copy the string */ while (*s) { /* Pluralizer */ if (*s == '~') { /* Add a plural if needed */ if (o_ptr->number != 1) { /* Get previous character */ char k = s[-1]; /* XXX XXX XXX Mega-Hack */ /* Hack -- "Cutlass-es" and "Torch-es" */ if ((k == 's') || (k == 'h')) { strnfcat(buf, max, &len, "es"); } else { /* Add an 's' */ strnfcat(buf, max, &len, "s"); } } } /* Modifier */ else if (*s == '#') { /* Insert the modifier */ strnfcat(buf, max, &len, "%s", modstr); } /* Normal */ else { /* Copy character */ strnfcat(buf, max, &len, "%c", *s); } s++; } /* Append the "kind name" to the "base name" */ if (append_name) { strnfcat(buf, max, &len, " of %s", get_object_name(o_ptr)); } /* Hack -- Append "Artifact" or "Special" names */ if (known) { if (o_ptr->inscription && strchr(quark_str(o_ptr->inscription), '#')) { /* Find the '#' */ cptr str = strchr(quark_str(o_ptr->inscription), '#'); /* Add the false name */ strnfcat(buf, max, &len, " %s" CLR_DEFAULT, &str[1]); } /* Is it a new artifact or ego item? */ else if (o_ptr->xtra_name) { strnfcat(buf, max, &len, " %s", quark_str(o_ptr->xtra_name)); } } /* No more details wanted */ if (mode < 1) return; /* Hack -- Chests must be described in detail */ if (o_ptr->tval == TV_CHEST) { /* Not searched yet */ if (!known) { /* Nothing */ } /* May be "empty" */ else if (!o_ptr->pval) { strnfcat(buf, max, &len, " (empty)"); } /* May be "disarmed" */ else if (o_ptr->pval < 0) { if (chest_traps[0 - o_ptr->pval]) { strnfcat(buf, max, &len, " (disarmed)"); } else { strnfcat(buf, max, &len, " (unlocked)"); } } /* Describe the traps, if any */ else { /* Describe the traps */ switch (chest_traps[o_ptr->pval]) { case 0: { strnfcat(buf, max, &len, " (Locked)"); break; } case CHEST_LOSE_STR: { strnfcat(buf, max, &len, " (Poison Needle)"); break; } case CHEST_LOSE_CON: { strnfcat(buf, max, &len, " (Poison Needle)"); break; } case CHEST_POISON: { strnfcat(buf, max, &len, " (Gas Trap)"); break; } case CHEST_PARALYZE: { strnfcat(buf, max, &len, " (Gas Trap)"); break; } case CHEST_EXPLODE: { strnfcat(buf, max, &len, " (Explosion Device)"); break; } case CHEST_SUMMON: { strnfcat(buf, max, &len, " (Summoning Runes)"); break; } default: { strnfcat(buf, max, &len, " (Multiple Traps)"); break; } } } } /* Display the item like a weapon */ if (FLAG(o_ptr, TR_SHOW_MODS)) show_weapon = TRUE; /* Display the item like a weapon */ if (o_ptr->to_h && o_ptr->to_d) show_weapon = TRUE; /* Display the item like armour */ if ((o_ptr->ac) && (o_ptr->tval != TV_WAND)) show_armour = TRUE; /* Dump base weapon info */ switch (o_ptr->tval) { case TV_SHOT: case TV_BOLT: case TV_ARROW: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_DIGGING: { /* Missiles and Weapons */ /* Append a "damage" string */ strnfcat(buf, max, &len, " (%dd%d)", o_ptr->dd, o_ptr->ds); /* All done */ break; } case TV_BOW: { /* Bows get a special "damage string" */ /* Extract the "base power" */ switch (o_ptr->sval) { case SV_SLING: { power = 2; break; } case SV_SHORT_BOW: { power = 2; break; } case SV_LONG_BOW: { if (p_ptr->stat[A_STR].use >= 160) { power = 3; } else { /* hack- weak players cannot use a longbow well */ power = 2; } break; } case SV_LIGHT_XBOW: { power = 4; break; } case SV_HEAVY_XBOW: { power = 5; break; } default: { msgf("Unknown firing multiplier."); power = 0; } } /* Apply the "Extra Might" flag */ if (FLAG(o_ptr, TR_XTRA_MIGHT)) power++; /* Append a special "damage" string */ strnfcat(buf, max, &len, " (x%d)", power); /* All done */ break; } } /* Add the weapon bonuses */ if (known) { /* Show the tohit/todam on request */ if (show_weapon) { strnfcat(buf, max, &len, " (%+d,%+d%%)", o_ptr->to_h, deadliness_calc(o_ptr->to_d) - 100); } /* Show the tohit if needed */ else if (o_ptr->to_h) { strnfcat(buf, max, &len, " (%+d)", o_ptr->to_h); } /* Show the todam if needed */ else if (o_ptr->to_d) { strnfcat(buf, max, &len, " (%+d%%)", o_ptr->to_d * 5); } } bow_ptr = &p_ptr->equipment[EQUIP_BOW]; /* if have a firing weapon + ammo matches bow */ if (bow_ptr->k_idx && (p_ptr->ammo_tval == o_ptr->tval)) { /* See if the bow is "known" - then set damage bonus */ if (object_known_p(bow_ptr)) { db = bow_ptr->to_d; } else { db = 0; } /* effect of player */ db += p_ptr->dis_to_d; /* effect of ammo */ if (known) db += o_ptr->to_d; dd = o_ptr->dd; ds = o_ptr->ds; /* effect of damage dice x2 */ avgdam = avg_dam(db, dd, ds); /* Bow properties */ energy_use = p_ptr->bow_energy; tmul = p_ptr->ammo_mult; /* Get extra "power" from "extra might" */ if (FLAG(p_ptr, TR_XTRA_MIGHT)) tmul++; /* launcher multiplier */ avgdam *= tmul; /* display (shot damage/ avg damage) */ strnfcat(buf, max, &len, " (%d/", avgdam / 200); tmul = p_ptr->num_fire; if (tmul == 0) { strnfcat(buf, max, &len, "0)"); } else { /* calc effects of energy x2 */ avgdam *= (1 + p_ptr->num_fire); /* rescale */ avgdam /= 4 * energy_use; strnfcat(buf, max, &len, "%d)", avgdam); } } /* Add the armor bonuses */ if (known) { /* Show the armor class info */ if (show_armour) { strnfcat(buf, max, &len, " [%d,%+d]", o_ptr->ac, o_ptr->to_a); } /* No base armor, but does increase armor */ else if (o_ptr->to_a) { strnfcat(buf, max, &len, " [%+d]", o_ptr->to_a); } } /* Hack -- always show base armor */ else if (show_armour) { strnfcat(buf, max, &len, " [%d]", o_ptr->ac); } /* No more details wanted */ if (mode < 2) return; /* * Hack -- Wands and Staffs have charges. Make certain how many charges * a stack of staffs really has is clear. -LM- */ if (known && ((o_ptr->tval == TV_STAFF) || (o_ptr->tval == TV_WAND))) { /* Dump " (N charges)" */ strnfcat(buf, max, &len, " ("); /* Clear explaination for staffs. */ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) { strnfcat(buf, max, &len, "%dx ", o_ptr->number); } if (o_ptr->pval == 1) { strnfcat(buf, max, &len, "%d charge)", o_ptr->pval); } else { strnfcat(buf, max, &len, "%d charges)", o_ptr->pval); } } /* Hack -- Rods have a "charging" indicator. Now that stacks of rods may * be in any state of charge or discharge, this now includes a number. -LM- */ else if (o_ptr->tval == TV_ROD) { /* Hack -- Dump " (# charging)" if relevant */ if (o_ptr->timeout) { /* Stacks of rods display an exact count of charging rods. */ if (o_ptr->number > 1) { /* Paranoia. */ if (k_ptr->pval == 0) k_ptr->pval = 1; /* * Find out how many rods are charging, by dividing * current timeout by each rod's maximum timeout. * Ensure that any remainder is rounded up. Display * very discharged stacks as merely fully discharged. */ power = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval; if (power > o_ptr->number) power = o_ptr->number; /* Display prettily. */ strnfcat(buf, max, &len, " (%d charging)", power); } /* "one Rod of Perception (1 charging)" would look tacky. */ else { strnfcat(buf, max, &len, " (charging)"); } } } /* Hack -- Process Lanterns/Torches */ else if (o_ptr->tval == TV_LITE) { if (FLAG(o_ptr, TR_LITE)) { /* Hack - tell us when lites of everburning are "empty" */ if ((o_ptr->sval <= SV_LITE_LANTERN) && !o_ptr->timeout) { strnfcat(buf, max, &len, " (empty)"); } } else { /* Hack -- Turns of light for normal lites */ strnfcat(buf, max, &len, " (with %d turns of light)", o_ptr->timeout); } } /* Dump "pval" flags for wearable items */ if (known && (FLAG(o_ptr, TR_PVAL_MASK))) { /* Start the display */ strnfcat(buf, max, &len, " (%+d", o_ptr->pval); /* Do not display the "pval" flags */ if (FLAG(o_ptr, TR_HIDE_TYPE)) { /* Nothing */ } /* Speed */ else if (FLAG(o_ptr, TR_SPEED)) { /* Dump " to speed" */ strnfcat(buf, max, &len, " to speed"); } /* Attack speed */ else if (FLAG(o_ptr, TR_BLOWS)) { if (ABS(o_ptr->pval) == 1) { /* Add " attack" */ strnfcat(buf, max, &len, " attack"); } else { /* Add "attacks" */ strnfcat(buf, max, &len, " attacks"); } } /* Finish the display */ strnfcat(buf, max, &len, ")"); } /* Indicate charging objects, but not rods. */ if (known && o_ptr->timeout && (o_ptr->tval != TV_ROD) && (o_ptr->tval != TV_LITE)) { /* Hack -- Dump " (charging)" if relevant */ strnfcat(buf, max, &len, " (charging)"); } /* No more details wanted */ if (mode < 3) return; /* Use the standard inscription if available */ if (o_ptr->inscription) { cptr tmp = quark_str(o_ptr->inscription); /* Append the inscription */ strnfcat(buf, max, &len, " {"); /* Scan for the '#' character which marks a fake name. */ while(*tmp && (*tmp != '#')) { strnfcat(buf, max, &len, "%c", *tmp); tmp++; } /* Finish the inscription */ strnfcat(buf, max, &len, CLR_DEFAULT "}"); } /* Use the game-generated "feeling" otherwise, if available */ else if (o_ptr->feeling) { /* Append the inscription */ strnfcat(buf, max, &len, " {%s" CLR_DEFAULT "}", game_inscriptions[o_ptr->feeling]); } /* Note "cursed" if the item is known to be cursed */ else if (cursed_p(o_ptr) && (known || (o_ptr->info & (OB_SENSE)))) { /* Append the inscription */ strnfcat(buf, max, &len, " {cursed}"); } /* Mega-Hack -- note empty wands/staffs */ else if (!known && (o_ptr->info & (OB_EMPTY))) { /* Append the inscription */ strnfcat(buf, max, &len, " {empty}"); } /* Note "tried" if the object has been tested unsuccessfully */ else if (!aware && object_tried_p(o_ptr)) { /* Append the inscription */ strnfcat(buf, max, &len, " {tried}"); } /* Note the discount, if any */ else if (o_ptr->discount) { /* Append the inscription */ strnfcat(buf, max, &len, " {%d%% off}", o_ptr->discount); } } /* * Wrapper around object_desc() for the '%v' * format option. This allows object_desc() to be * called in a format string. * * The parameters are object_type (o_ptr), pref (int), mode(int). */ void object_fmt(char *buf, uint max, cptr fmt, va_list *vp) { const object_type *o_ptr; int pref; int mode; /* Unused parameter */ (void)fmt; /* Get the object */ o_ptr = va_arg(*vp, const object_type*); /* Get the pref */ pref = va_arg(*vp, int); /* Get the mode */ mode = va_arg(*vp, int); object_desc(buf, o_ptr, pref, mode, max); } /* * Hack -- describe an item currently in a store's inventory * This allows an item to *look* like the player is "aware" of it */ void object_desc_store(char *buf, const object_type *o_ptr, int pref, int mode, int size) { byte hack_flavor; bool hack_aware; byte info; /* Hack - we will reset the object to exactly like it was */ object_type *q_ptr = (object_type *)o_ptr; /* Save the "flavor" */ hack_flavor = k_info[o_ptr->k_idx].flavor; /* Save the "aware" flag */ hack_aware = k_info[o_ptr->k_idx].aware; /* Save the "info" */ info = o_ptr->info; /* Clear the flavor */ k_info[o_ptr->k_idx].flavor = FALSE; /* If this is a shop item or in you are in wizard mode play with objects */ if (q_ptr->info & OB_STOREB) { /* Make it known */ q_ptr->info |= (OB_KNOWN); /* Force "aware" for description */ k_info[o_ptr->k_idx].aware = TRUE; } /* Describe the object */ object_desc(buf, q_ptr, pref, mode, size); /* Restore "flavor" value */ k_info[o_ptr->k_idx].flavor = hack_flavor; /* Restore "aware" flag */ k_info[o_ptr->k_idx].aware = hack_aware; /* Restore the "info" */ q_ptr->info = info; } /* * Wrapper around object_desc_store() for the '%v' * format option. This allows object_desc() to be * called in a format string. * * The parameters are object_type (o_ptr), pref (int), mode(int). */ void object_store_fmt(char *buf, uint max, cptr fmt, va_list *vp) { const object_type *o_ptr; int pref; int mode; /* Unused parameter */ (void)fmt; /* Get the object */ o_ptr = va_arg(*vp, const object_type*); /* Get the pref */ pref = va_arg(*vp, int); /* Get the mode */ mode = va_arg(*vp, int); object_desc_store(buf, o_ptr, pref, mode, max); } zangband/src/generate.c0000755000000000000000000007534010250356274014105 0ustar rootroot/* File: generate.c */ /* Purpose: Dungeon generation */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* * Note that Level generation is *not* an important bottleneck, * though it can be annoyingly slow on older machines... Thus * we emphasize "simplicity" and "correctness" over "speed". * * This entire file is only needed for generating levels. * This may allow smart compilers to only load it when needed. * * Consider the "v_info.txt" file for vault generation. * * In this file, we use the "special" granite and perma-wall sub-types, * where "basic" is normal, "inner" is inside a room, "outer" is the * outer wall of a room, and "solid" is the outer wall of the dungeon * or any walls that may not be pierced by corridors. Thus the only * wall type that may be pierced by a corridor is the "outer granite" * type. The "basic granite" type yields the "actual" corridors. * * Note that we use the special "solid" granite wall type to prevent * multiple corridors from piercing a wall in two adjacent locations, * which would be messy, and we use the special "outer" granite wall * to indicate which walls "surround" rooms, and may thus be "pierced" * by corridors entering or leaving the room. * * Note that a tunnel which attempts to leave a room near the "edge" * of the dungeon in a direction toward that edge will cause "silly" * wall piercings, but will have no permanently incorrect effects, * as long as the tunnel can *eventually* exit from another side. * And note that the wall may not come back into the room by the * hole it left through, so it must bend to the left or right and * then optionally re-enter the room (at least 2 grids away). This * is not a problem since every room that is large enough to block * the passage of tunnels is also large enough to allow the tunnel * to pierce the room itself several times. * * Note that no two corridors may enter a room through adjacent grids, * they must either share an entryway or else use entryways at least * two grids apart. This prevents "large" (or "silly") doorways. * * To create rooms in the dungeon, we first divide the dungeon up * into "blocks" of 11x11 grids each, and require that all rooms * occupy a rectangular group of blocks. As long as each room type * reserves a sufficient number of blocks, the room building routines * will not need to check bounds. Note that most of the normal rooms * actually only use 23x11 grids, and so reserve 33x11 grids. * * Note that the use of 11x11 blocks (instead of the old 33x11 blocks) * allows more variability in the horizontal placement of rooms, and * at the same time has the disadvantage that some rooms (two thirds * of the normal rooms) may be "split" by panel boundaries. This can * induce a situation where a player is in a room and part of the room * is off the screen. It may be annoying enough to go back to 33x11 * blocks to prevent this visual situation. * * Note that the dungeon generation routines are much different (2.7.5) * and perhaps "DUN_ROOMS" should be less than 50. * * XXX XXX XXX Note that it is possible to create a room which is only * connected to itself, because the "tunnel generation" code allows a * tunnel to leave a room, wander around, and then re-enter the room. * * XXX XXX XXX Note that it is possible to create a set of rooms which * are only connected to other rooms in that set, since there is nothing * explicit in the code to prevent this from happening. But this is less * likely than the "isolated room" problem, because each room attempts to * connect to another room, in a giant cycle, thus requiring at least two * bizarre occurances to create an isolated section of the dungeon. * * Note that (2.7.9) monster pits have been split into monster "nests" * and monster "pits". The "nests" have a collection of monsters of a * given type strewn randomly around the room (jelly, animal, or undead), * while the "pits" have a collection of monsters of a given type placed * around the room in an organized manner (orc, troll, giant, dragon, or * demon). Note that both "nests" and "pits" are now "level dependant", * and both make 16 "expensive" calls to the "get_mon_num()" function. * * Note that the cave grid flags changed in a rather drastic manner * for Angband 2.8.0 (and 2.7.9+), in particular, dungeon terrain * features, such as doors and stairs and traps and rubble and walls, * are all handled as a set of 64 possible "terrain features", and * not as "fake" objects (440-479) as in pre-2.8.0 versions. * * The 64 new "dungeon features" will also be used for "visual display" * but we must be careful not to allow, for example, the user to display * hidden traps in a different way from floors, or secret doors in a way * different from granite walls, or even permanent granite in a different * way from granite. XXX XXX XXX */ #include "angband.h" #include "generate.h" #include "grid.h" #include "rooms.h" #include "streams.h" static int dun_rooms; int dun_tun_rnd; int dun_tun_chg; int dun_tun_con; int dun_tun_pen; int dun_tun_jct; /* * Dungeon generation data -- see "cave_gen()" */ dun_data *dun; static int dun_rating; static int is_special; /* Control the rating */ void inc_rating(int delta_rating) { dun_rating += delta_rating; } /* Set the special feeling */ void set_special(void) { is_special = TRUE; } static byte extract_feeling(void) { /* Hack -- no feeling in the town */ if (!p_ptr->depth) return 0; /* Hack -- Have a special feeling sometimes */ if (is_special && !preserve_mode) return 1; if (dun_rating > 100) return 2; if (dun_rating > 80) return 3; if (dun_rating > 60) return 4; if (dun_rating > 40) return 5; if (dun_rating > 30) return 6; if (dun_rating > 20) return 7; if (dun_rating > 10) return 8; if (dun_rating > 0) return 9; if ((turn - old_turn) > 50000L) chg_virtue(V_PATIENCE, 1); return 10; } /* * Places some staircases near walls */ static bool alloc_stairs(int feat, int num, int walls) { int y, x, i, j, flag; cave_type *c_ptr; if (feat == FEAT_LESS) { /* No up stairs in town or in ironman mode */ if (ironman_downward || !p_ptr->depth) return TRUE; } else if (feat == FEAT_MORE) { /* No downstairs on quest levels */ if (is_special_level(p_ptr->depth)) return TRUE; /* No downstairs at the bottom */ if (p_ptr->depth >= dungeon()->max_level) return TRUE; } /* Place "num" stairs */ for (i = 0; i < num; i++) { /* Place some stairs */ for (flag = FALSE; !flag;) { /* Try several times, then decrease "walls" */ for (j = 0; !flag && j <= 10000; j++) { /* Pick a random grid */ y = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); x = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); /* Access the grid */ c_ptr = cave_p(x, y); /* Require "naked" floor grid */ if (!cave_naked_grid(c_ptr)) continue; /* Require a certain number of adjacent walls */ if (next_to_walls(x, y) < walls) continue; /* Clear previous contents, add stairs */ set_feat_grid(c_ptr, feat); /* All done */ flag = TRUE; } /* If cannot find a blank spot - exit */ if (!walls) { /* Placed at least one. */ if (i > 0) return TRUE; /* Couldn't place any stairs */ return FALSE; } /* Require fewer walls */ walls--; } } /* Done */ return TRUE; } /* * Allocates some objects (using "place" and "type") */ static void alloc_object(int set, int typ, int num) { int x = 0, y = 0, k; int dummy = 0; cave_type *c_ptr = NULL; /* Place some objects */ for (k = 0; k < num; k++) { /* Pick a "legal" spot */ while (dummy < SAFE_MAX_ATTEMPTS) { bool room; dummy++; /* Location */ y = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); x = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); c_ptr = cave_p(x, y); /* Require "naked" floor grid */ if (!cave_naked_grid(c_ptr)) continue; /* Check for "room" */ room = (c_ptr->info & CAVE_ROOM) ? TRUE : FALSE; /* Require corridor? */ if ((set == ALLOC_SET_CORR) && room) continue; /* Require room? */ if ((set == ALLOC_SET_ROOM) && !room) continue; /* Traps cannot be placed on 'icky' grids (rivers/lakes) */ if ((typ == ALLOC_TYP_TRAP) && (c_ptr->info & CAVE_ICKY)) continue; /* Accept it */ break; } if (dummy >= SAFE_MAX_ATTEMPTS) { if (cheat_room) { msgf("Warning! Could not place object!"); } return; } /* Place something */ switch (typ) { case ALLOC_TYP_RUBBLE: { set_feat_grid(c_ptr, FEAT_RUBBLE); break; } case ALLOC_TYP_TRAP: { place_trap(x, y); break; } case ALLOC_TYP_GOLD: { place_gold(x, y); break; } case ALLOC_TYP_OBJECT: { place_object(x, y, FALSE, FALSE, 0); break; } case ALLOC_TYP_INVIS: { /* Create invisible wall */ set_feat_grid(c_ptr, dun->feat_floor); (void)place_field(x, y, FT_WALL_INVIS); break; } } } } /* * Count the number of "corridor" grids adjacent to the given grid. * * Note -- Assumes "in_bounds(x1, y1)" * * XXX XXX This routine currently only counts actual "empty floor" * grids which are not in rooms. We might want to also count stairs, * open doors, closed doors, etc. */ static int next_to_corr(int x1, int y1) { int i, y, x, k = 0; cave_type *c_ptr; /* Scan adjacent grids */ for (i = 0; i < 4; i++) { /* Extract the location */ y = y1 + ddy_ddd[i]; x = x1 + ddx_ddd[i]; /* Access the grid */ c_ptr = cave_p(x, y); /* Skip non clean floors */ if (!cave_clean_grid(c_ptr)) continue; /* Skip grids inside rooms */ if (c_ptr->info & (CAVE_ROOM)) continue; /* Count these grids */ k++; } /* Return the number of corridors */ return (k); } /* * Determine if the given location is "between" two walls, * and "next to" two corridor spaces. XXX XXX XXX * * Assumes "in_bounds(x, y)" */ static bool possible_doorway(int x, int y) { /* Count the adjacent corridors */ if (next_to_corr(x, y) >= 2) { /* Check Vertical */ if (cave_wall_grid(cave_p(x, y - 1)) && cave_wall_grid(cave_p(x, y + 1))) { return (TRUE); } /* Check Horizontal */ if (cave_wall_grid(cave_p(x - 1, y)) && cave_wall_grid(cave_p(x + 1, y))) { return (TRUE); } } /* No doorway */ return (FALSE); } /* * Places door at y, x position if at least 2 walls found */ static void try_door(int x, int y) { cave_type *c_ptr; /* Paranoia */ if (!in_bounds(x, y)) return; c_ptr = cave_p(x, y); /* Ignore walls */ if (cave_wall_grid(c_ptr)) return; /* Ignore room grids */ if (c_ptr->info & (CAVE_ROOM)) return; /* Occasional door (if allowed) */ if ((randint0(100) < dun_tun_jct) && possible_doorway(x, y)) { /* Place a door */ place_random_door(x, y); } } static const byte liquid_types[LQ_MAX][2] = { {FEAT_SHAL_WATER, FEAT_DEEP_WATER}, {FEAT_SHAL_LAVA, FEAT_DEEP_LAVA}, {FEAT_SHAL_ACID, FEAT_DEEP_ACID}, {FEAT_SHAL_SWAMP, FEAT_DEEP_SWAMP} }; static void add_monsters(int count) { int i, j; int delta_level, level, best_level; u16b best_r_idx; int min_depth; u16b r_idx; monster_race *r_ptr; int num; bool group; int x = 0, y = 0; cave_type *c_ptr; int target_rating = randint1(10) + p_ptr->depth * 2 / 3; /* Put some monsters in the dungeon */ for (i = 0; i < count; i++) { /* * Calculate the total levels of monster ood'ness to get * an appropriate level feeling. * * The more boring the dungeon is right now, * the more out of depth to pick monsters. * * Each monster gets a fraction of the total levels we want * that depends on the number of monsters left to generate. */ delta_level = (target_rating - dun_rating) / (count - i); if (delta_level < 0) delta_level = 0; if (delta_level > 10) delta_level = 10; (void)alloc_monster(0, TRUE, delta_level); } /* Sometimes have lots of monster of a given type */ if (one_in_(10)) { level = p_ptr->depth + 6; best_r_idx = 1; best_level = 1; /* Get monster */ for (j = 0; j < 100; j++) { min_depth = level + (level / 20) + 1; /* * Random monster out of depth */ r_idx = get_mon_num(level); r_ptr = &r_info[r_idx]; /* Save the index if the monster is deeper than current monster */ if (!best_r_idx || (r_info[r_idx].level > best_level)) { best_r_idx = r_idx; best_level = r_info[r_idx].level; } /* Accept monsters that are a few levels out of depth */ if (best_level > min_depth) break; } r_ptr = &r_info[best_r_idx]; /* Get the number of monsters */ if (FLAG(r_ptr, RF_UNIQUE)) { num = 1; } else if (FLAG(r_ptr, RF_UNIQUE_7)) { num = randint1(r_ptr->max_num); } else { num = 5 + (s16b)randint0(level / 3 + 5) / r_ptr->rarity; } for (i = 0; i < num; i++) { /* Find an empty grid */ while (TRUE) { y = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); x = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); /* Access the grid */ c_ptr = area(x, y); if (!cave_naked_grid(c_ptr)) continue; if (distance(x, y, p_ptr->px, p_ptr->py) < 10) continue; else break; } if (FLAG(r_ptr, RF_FRIENDS)) group = FALSE; else group = TRUE; /* Try to place the monster */ place_monster_aux(x, y, best_r_idx, FALSE, group, FALSE, FALSE, TRUE); } /* * Make a great object somewhere in the dungeon to compensate * (Hack - use location of last monster as target) */ place_object(x, y, TRUE, TRUE, best_level - p_ptr->depth); } } /* * Generate a new dungeon level * * Note that "dun_body" adds about 4000 bytes of memory to the stack. */ static bool cave_gen(dun_type *d_ptr) { int i, j, k, y, x, y1, x1; int max_vault_ok = 2; cave_type *c_ptr; bool destroyed = FALSE; bool empty_level = FALSE; bool cavern = FALSE; int lq_count; dun_data dun_body; /* Global data */ dun = &dun_body; if (p_ptr->max_hgt - p_ptr->min_hgt < 23) max_vault_ok--; if (p_ptr->max_wid - p_ptr->min_wid < 34) max_vault_ok--; /* Randomize the dungeon creation values */ dun_rooms = rand_range(DUN_ROOMS_MIN, DUN_ROOMS_MAX); dun_tun_rnd = rand_range(DUN_TUN_RND_MIN, DUN_TUN_RND_MAX); dun_tun_chg = rand_range(DUN_TUN_CHG_MIN, DUN_TUN_CHG_MAX); dun_tun_con = rand_range(DUN_TUN_CON_MIN, DUN_TUN_CON_MAX); dun_tun_pen = rand_range(DUN_TUN_PEN_MIN, DUN_TUN_PEN_MAX); dun_tun_jct = rand_range(DUN_TUN_JCT_MIN, DUN_TUN_JCT_MAX); /*** Store in the terrain types ***/ /* Get floor type */ dun->feat_floor = d_ptr->floor; /* Get room types */ dun->room_types = d_ptr->rooms; /* Paranoia */ if (d_ptr->liquid == LQ_NONE) { quit("Undefined liquid type in dungeon."); } /* Count applicable liquid types */ lq_count = count_bits(d_ptr->liquid); /* Pick one */ lq_count = randint0(lq_count); /* Find which choice we have made */ for (i = 0; i < LQ_MAX; i++) { /* Is this flag set? */ if (d_ptr->liquid & (1 << i)) { if (!lq_count) { /* Our choice */ dun->feat_shal_liquid = liquid_types[i][0]; dun->feat_deep_liquid = liquid_types[i][1]; break; } /* Count down bits until we get to the one we want */ lq_count--; } } /* Empty arena levels only ever in "city" dungeons */ if ((d_ptr->habitat & RF7_DUN_CITY) && one_in_(EMPTY_LEVEL)) { empty_level = TRUE; if (cheat_room) msgf("City level."); } /* Hack -- Start with basic granite */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { if (empty_level) { set_feat_bold(x, y, dun->feat_floor); } else { /* Create granite wall */ set_feat_bold(x, y, FEAT_WALL_EXTRA); } } } /* Possible "destroyed" level */ if ((p_ptr->depth > 15) && one_in_(DUN_DEST) && (small_levels)) { destroyed = TRUE; /* extra rubble around the place looks cool */ build_lake(dun->feat_floor, dun->feat_floor, FEAT_RUBBLE); } /* Make a lake some of the time */ if (one_in_(LAKE_LEVEL) && !empty_level && !destroyed) { if (cheat_room) msgf("Lake on the level."); build_lake(dun->feat_deep_liquid, dun->feat_shal_liquid, dun->feat_floor); } else if (one_in_(DUN_CAV1 / (p_ptr->depth + DUN_CAV2)) && !empty_level && !destroyed && (p_ptr->depth >= MIN_CAVERN)) { cavern = TRUE; /* make a large fractal cave in the middle of the dungeon */ if (cheat_room) msgf("Cavern on level."); build_cavern(); } /* Actual maximum number of rooms on this level */ dun->row_rooms = (p_ptr->max_hgt - p_ptr->min_hgt) / BLOCK_HGT; dun->col_rooms = (p_ptr->max_wid - p_ptr->min_hgt) / BLOCK_WID; /* Initialize the room table */ for (y = 0; y < dun->row_rooms; y++) { for (x = 0; x < dun->col_rooms; x++) { dun->room_map[y][x] = FALSE; } } /* No "crowded" rooms yet */ dun->crowded = 0; /* No rooms yet */ dun->cent_n = 0; /* Build some rooms */ for (i = 0; i < dun_rooms; i++) { int count = 0; while (!room_build() && (count++ < 20)) /* Loop */; } /* Make a hole in the dungeon roof sometimes at level 1 */ if (p_ptr->depth == 1) { while (one_in_(DUN_MOS_DEN)) { place_trees(rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2), rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2)); } } /* Hack -- Add some rivers */ if (one_in_(3) && (randint1(p_ptr->depth) > 5)) { add_river(dun->feat_deep_liquid, dun->feat_shal_liquid); } /* Special boundary walls -- Top */ for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { /* Clear previous contents, add "solid" perma-wall */ set_feat_bold(x, p_ptr->min_hgt, FEAT_PERM_SOLID); } /* Special boundary walls -- Bottom */ for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { /* Clear previous contents, add "solid" perma-wall */ set_feat_bold(x, p_ptr->max_hgt - 1, FEAT_PERM_SOLID); } /* Special boundary walls -- Left */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { /* Clear previous contents, add "solid" perma-wall */ set_feat_bold(p_ptr->min_wid, y, FEAT_PERM_SOLID); } /* Special boundary walls -- Right */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { /* Clear previous contents, add "solid" perma-wall */ set_feat_bold(p_ptr->max_wid - 1, y, FEAT_PERM_SOLID); } /* Hack -- Scramble the room order */ for (i = 0; i < dun->cent_n; i++) { int pick1 = randint0(dun->cent_n); int pick2 = randint0(dun->cent_n); y1 = dun->cent[pick1].y; x1 = dun->cent[pick1].x; dun->cent[pick1].y = dun->cent[pick2].y; dun->cent[pick1].x = dun->cent[pick2].x; dun->cent[pick2].y = y1; dun->cent[pick2].x = x1; } /* Start with no tunnel doors */ dun->door_n = 0; /* Hack -- connect the first room to the last room */ y = dun->cent[dun->cent_n - 1].y; x = dun->cent[dun->cent_n - 1].x; /* Connect all the rooms together */ for (i = 0; i < dun->cent_n; i++) { /* Reset the arrays */ dun->tunn_n = 0; dun->wall_n = 0; /* Connect the room to the previous room */ #ifdef PILLAR_TUNNELS if ((randint1(20) > p_ptr->depth) && one_in_(4)) { /* make catacomb-like tunnel */ (void)build_tunnel2(dun->cent[i].x, dun->cent[i].y, x, y, 3, 30); } else if (randint1(p_ptr->depth) > 50) #else if (randint1(p_ptr->depth) > 50) #endif /* PILLAR_TUNNELS */ { /* make cave-like tunnel */ (void)build_tunnel2(dun->cent[i].x, dun->cent[i].y, x, y, 2, 2); } else { /* make normal tunnel */ build_tunnel(dun->cent[i].x, dun->cent[i].y, x, y); } /* Turn the tunnel into corridor */ for (j = 0; j < dun->tunn_n; j++) { /* Access the grid */ y = dun->tunn[j].y; x = dun->tunn[j].x; /* Access the grid */ c_ptr = cave_p(x, y); /* Deleting a locked or jammed door is problematical */ delete_field_location(c_ptr); /* Clear previous contents if wall, add a floor */ if (cave_wall_grid(c_ptr)) { set_feat_grid(c_ptr, dun->feat_floor); } } /* Apply the piercings that we found */ for (j = 0; j < dun->wall_n; j++) { /* Access the grid */ y = dun->wall[j].y; x = dun->wall[j].x; /* Access the grid */ c_ptr = cave_p(x, y); /* Deleting a locked or jammed door is problematical */ delete_field_location(c_ptr); /* Clear previous contents, add up floor */ set_feat_grid(c_ptr, dun->feat_floor); /* Occasional doorway */ if (randint0(100) < dun_tun_pen) { /* Place a random door */ place_random_door(x, y); } } /* Remember the "previous" room */ y = dun->cent[i].y; x = dun->cent[i].x; } /* Place intersection doors */ for (i = 0; i < dun->door_n; i++) { /* Extract junction location */ y = dun->door[i].y; x = dun->door[i].x; /* Try placing doors */ try_door(x, y - 1); try_door(x, y + 1); try_door(x - 1, y); try_door(x + 1, y); } /* Hack -- Add some magma streamers */ for (i = 0; i < DUN_STR_MAG; i++) { build_streamer(FEAT_MAGMA, DUN_STR_MC); } /* Hack -- Add some quartz streamers */ for (i = 0; i < DUN_STR_QUA; i++) { build_streamer(FEAT_QUARTZ, DUN_STR_QC); } /* Place 3 or 4 down stairs near some walls */ if (!alloc_stairs(FEAT_MORE, rand_range(3, 4), 3)) return FALSE; /* Place 1 or 2 up stairs near some walls */ if (!alloc_stairs(FEAT_LESS, rand_range(1, 2), 3)) return FALSE; /* Place quest monsters in the dungeon */ trigger_quest_create(QC_DUN_MONST, NULL); /* Place quest artifacts in the dungeon */ trigger_quest_create(QC_DUN_ARTIFACT, NULL); /* Pick a base number of monsters */ i = MIN_M_ALLOC_LEVEL; /* To make small levels a bit more playable */ if (p_ptr->max_hgt < MAX_HGT || p_ptr->max_wid < MAX_WID) { int small_tester = i; i = (i * p_ptr->max_hgt) / MAX_HGT; i = (i * p_ptr->max_wid) / MAX_WID; i += 1; if (i > small_tester) i = small_tester; else if (cheat_hear) { msgf("Reduced monsters base from %d to %d", small_tester, i); } } i += randint1(8); /* Basic "amount" */ k = (p_ptr->depth / 3); if (k > 10) k = 10; if (k < 2) k = 2; /* Add some monsters to the dungeon */ add_monsters(i + k); /* Place some traps in the dungeon */ alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_TRAP, randint1(k)); /* Put some rubble in corridors */ alloc_object(ALLOC_SET_CORR, ALLOC_TYP_RUBBLE, randint1(k)); /* Put some objects in rooms */ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_OBJECT, Rand_normal(DUN_AMT_ROOM, 3)); /* Put some objects/gold in the dungeon */ alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_OBJECT, Rand_normal(DUN_AMT_ITEM, 3)); alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_GOLD, Rand_normal(DUN_AMT_GOLD, 3)); /* Put some invisible walls in the dungeon for nightmare mode */ if (ironman_nightmare) { alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_INVIS, Rand_normal(DUN_AMT_INVIS, 3)); } if (empty_level && (!one_in_(DARK_EMPTY) || (randint1(100) > p_ptr->depth))) { /* Lite the cave */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_hgt; x < p_ptr->max_wid; x++) { cave_p(x, y)->info |= (CAVE_GLOW); } } } /* Determine the character location */ if (!new_player_spot()) return FALSE; return TRUE; } /* Make a real level */ static bool level_gen(cptr *why, dun_type *d_ptr) { int level_height, level_width; if (ironman_small_levels || (one_in_(SMALL_LEVEL) && small_levels)) { if (cheat_room) msgf("A 'small' dungeon level."); while (TRUE) { level_height = randint1(MAX_HGT / BLOCK_HGT); level_width = randint1(MAX_WID / BLOCK_WID); /* Exit if larger than one screen, but less than normal dungeon */ if ((level_height < (MAX_HGT / BLOCK_HGT)) && (level_height >= (22 / BLOCK_HGT)) && (level_width < (MAX_WID / BLOCK_WID)) && (level_width >= (66 / BLOCK_WID))) break; } /* Get bounds of dungeon */ p_ptr->min_hgt = 0; p_ptr->max_hgt = level_height * BLOCK_HGT; p_ptr->min_wid = 0; p_ptr->max_wid = level_width * BLOCK_WID; if (cheat_room) msgf("X:%d, Y:%d.", p_ptr->max_wid, p_ptr->max_hgt); } else { /* Big dungeon */ p_ptr->min_hgt = 0; p_ptr->max_hgt = MAX_HGT; p_ptr->min_wid = 0; p_ptr->max_wid = MAX_WID; } /* Get the new region */ create_region(d_ptr, p_ptr->max_wid, p_ptr->max_hgt, REGION_CAVE); /* Grab the reference to it */ incref_region(cur_region); /* Make a dungeon */ if (!cave_gen(d_ptr)) { *why = "could not place player"; return FALSE; } return TRUE; } /* * Delete a region from the region list * * Only call this when cleaning up the game during * exit - use unref_region() below normally. */ void del_region(int rg_idx) { int i, j; pcave_type *pc_ptr; /* Acquire region info */ region_info *ri_ptr = &ri_list[rg_idx]; /* * Deallocate everything if region uses * literal meanings of cave_type structure values. * * Note - quests have this flag unset. * m_idx refers to race of monster. * o_idx refers to type of object. * fld_idx refers to type of field. * * (Rather than index of monster, object or fields.) */ if (ri_ptr->flags & REGION_CAVE) { /* Delete everything in the region */ wipe_monsters(rg_idx); /* * Objects are deleted after the monsters, * because monsters carry them. */ wipe_objects(rg_idx); wipe_fields(rg_idx); /* Hack - delete player knowledge */ for (i = 0; i < MAX_WID; i++) { for (j = 0; j < MAX_HGT; j++) { pc_ptr = &p_ptr->pcave[j][i]; /* Clear the player dungeon flags */ pc_ptr->player = 0x00; /* Clear the player dungeon memory */ forget_grid(pc_ptr); } } } /* Deallocate the cave information */ /* Free the cave */ for (i = 0; i < ri_ptr->ysize; i++) { /* Deallocate one row of the cave */ FREE(rg_list[rg_idx][i]); } /* Free the region + info */ KILL(rg_list[rg_idx]); (void)WIPE(&ri_list[rg_idx], region_info); /* Decrement counter */ rg_cnt--; } /* * Decrease refcount on region - deallocate if empty */ int unref_region(int rg_idx) { /* Acquire region info */ region_info *ri_ptr = &ri_list[rg_idx]; /* Paranoia */ if (!ri_ptr->refcount) quit("Region refcount missmatch"); /* Decrease refcount */ ri_ptr->refcount--; /* Delete if just lost final reference */ if (!ri_ptr->refcount) { /* Paranoia */ if (!rg_list[rg_idx]) quit("Deleting unallocated region"); del_region(rg_idx); /* Region no longer exists */ return (0); } /* No change */ return (rg_idx); } /* * Increase refcount on region */ void incref_region(int rg_idx) { /* Acquire region info */ region_info *ri_ptr = &ri_list[rg_idx]; /* Paranoia */ if (!rg_list[rg_idx]) quit("Incrementing unallocated region"); /* Increase refcount */ ri_ptr->refcount++; } /* * Set the global region */ void set_region(int rg_idx) { /* Paranoia */ if (rg_idx >= rg_max) quit("Setting invalid region"); /* Set the region */ cur_region = rg_idx; /* Set region pointer */ cave_data = rg_list[cur_region]; } /* * Delete all regions - and everything inside them */ void wipe_rg_list(void) { int i; /* Wipe each active region */ for (i = 1; i < rg_max; i++) { /* * Hack - use del_region rather than unref_region. * * This function will not clean up all outstanding * references to the regions. Only call this when you * know no such references exist. */ if (rg_list[i]) del_region(i); } /* Wipe the remaining objects, monsters and fields (in wilderness) */ wipe_m_list(); wipe_o_list(); wipe_f_list(); } /* * Do the actual work of allocating a region */ static void allocate_region(int rg_idx, int x, int y) { int i; /* Acquire region info */ region_info *ri_ptr = &ri_list[rg_idx]; /* Save size */ ri_ptr->xsize = x; ri_ptr->ysize = y; /* Hack set the refcount to zero - assume caller increments refcount */ ri_ptr->refcount = 0; /* Make the array of pointers to the cave */ C_MAKE(rg_list[rg_idx], y, cave_type *); /* Allocate and wipe each line of the region */ for (i = 0; i < y; i++) { /* Allocate one row of the cave */ C_MAKE(rg_list[rg_idx][i], x, cave_type); } /* Hack - set this region to be the currently used one */ set_region(rg_idx); } /* * Allocate a region. * * (Usually used to store the dungeon, * However, can be used in the wilderness to store * town info.) * * This rountine should never fail - but be prepared * for when it does. */ void create_region_aux(s16b *region, int x, int y, byte flags) { int rg_idx; int i; if (rg_max < z_info->rg_max) { /* Get next space */ rg_idx = rg_max; /* Expand region array */ rg_max++; /* Count regions */ rg_cnt++; /* Allocate the region */ allocate_region(rg_idx, x, y); /* Save the flags */ ri_list[rg_idx].flags = flags; /* Save the region number */ *region = rg_idx; /* Done */ return; } /* Recycle dead regions */ for (i = 1; i < rg_max; i++) { /* Skip used regions */ if (rg_list[i]) continue; /* Count regions */ rg_cnt++; /* Allocate the region */ allocate_region(i, x, y); /* Save the flags */ ri_list[i].flags = flags; /* Use this region */ *region = i; return; } /* Warn the player */ msgf("Too many regions!"); /* Paranoia */ *region = 0; /* Oops */ return; } /* * Generates a random dungeon level -RAK- * * Hack -- regenerate any "overflow" levels * * Hack -- allow auto-scumming via a gameplay option. */ void generate_cave(void) { int num; dun_type *dundata = place[p_ptr->place_num].dungeon; /* Build the wilderness */ if (!p_ptr->depth) { /* The "dungeon" is ready */ character_dungeon = TRUE; return; } /* Get random dungeon */ if (vanilla_town) { const dun_gen_type *d_ptr = pick_dungeon_type(); /* Get floor type */ dundata->floor = d_ptr->floor; /* Liquid type */ dundata->liquid = d_ptr->liquid; /* Get room types */ dundata->rooms = d_ptr->rooms; /* Set the object theme (structure copy) */ dundata->theme = d_ptr->theme; /* Hack - Reset the dungeon habitat to be everything */ dundata->habitat = d_ptr->habitat; } /* Generate */ for (num = 0; TRUE; num++) { bool okay = TRUE; cptr why = NULL; /* Nothing special here yet */ dundata->good_item_flag = FALSE; /* Nothing good here yet */ dun_rating = 0; is_special = FALSE; okay = level_gen(&why, dundata); /* Save rating for later */ dundata->rating = dun_rating; dundata->good_item_flag = is_special; /* Extract the feeling */ p_ptr->state.feeling = extract_feeling(); /* Prevent object over-flow */ if (o_cnt + 1 >= z_info->o_max) { /* Message */ why = "too many objects"; /* Message */ okay = FALSE; } /* Prevent monster over-flow */ else if (m_cnt + 1 >= z_info->m_max) { /* Message */ why = "too many monsters"; /* Message */ okay = FALSE; } /* Accept */ if (okay) break; /* Message */ if (why) msgf("Generation restarted (%s)", why); /* Delete the level - not good enough */ dundata->region = unref_region(dundata->region); } /* The dungeon is ready */ character_dungeon = TRUE; /* Remember when this level was "created" */ old_turn = turn; } zangband/src/grid.c0000644000000000000000000012622410250356274013233 0ustar rootroot/* * File: grid.c * Purpose: low-level dungeon creation primitives */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "generate.h" #include "grid.h" /* * Returns random co-ordinates for player/monster/object */ bool new_player_spot(void) { int x, y; int max_attempts = 5000; cave_type *c_ptr; /* Place the player */ while (max_attempts--) { /* Pick a legal spot */ y = rand_range(p_ptr->min_hgt, p_ptr->max_hgt - 1); x = rand_range(p_ptr->min_wid, p_ptr->max_wid - 1); c_ptr = cave_p(x, y); /* Must be a "naked" floor grid */ if (!cave_naked_grid(c_ptr)) continue; /* Refuse to start on anti-teleport grids */ if (c_ptr->info & (CAVE_ICKY)) continue; /* Done */ break; } if (max_attempts < 1) /* Should be -1, actually if we failed... */ return FALSE; /* Save the new player grid */ p_ptr->py = y; p_ptr->px = x; /* Notice player location */ Term_move_player(); return TRUE; } /* * Place an up/down staircase at given location */ void place_random_stairs(int x, int y) { bool up_stairs = TRUE; bool down_stairs = TRUE; cave_type *c_ptr; /* Paranoia */ c_ptr = cave_p(x, y); if (!cave_clean_grid(c_ptr)) return; /* Town */ if (!p_ptr->depth) up_stairs = FALSE; /* Ironman */ if (ironman_downward) up_stairs = FALSE; /* Bottom */ if (p_ptr->depth >= dungeon()->max_level) down_stairs = FALSE; /* Final quest */ if (is_special_level(p_ptr->depth)) down_stairs = FALSE; /* We can't place both */ if (down_stairs && up_stairs) { /* Choose a staircase randomly */ if (one_in_(2)) up_stairs = FALSE; else down_stairs = FALSE; } /* Place the stairs */ if (up_stairs) { set_feat_grid(c_ptr, FEAT_LESS); } else if (down_stairs) { set_feat_grid(c_ptr, FEAT_MORE); } } /* * Place a random type of door at the given location */ void place_random_door(int x, int y) { int tmp; cave_type *c_ptr = cave_p(x, y); /* Making a door on top of fields is problematical */ delete_field(y, x); /* Invisible wall */ if (ironman_nightmare && one_in_(666)) { /* Create invisible wall */ set_feat_grid(c_ptr, dun->feat_floor); (void)place_field(x, y, FT_WALL_INVIS); return; } /* Choose an object */ tmp = randint0(1000); /* Open doors (300/1000) */ if (tmp < 300) { /* Create open door */ set_feat_grid(c_ptr, FEAT_OPEN); } /* Broken doors (100/1000) */ else if (tmp < 400) { /* Create broken door */ set_feat_grid(c_ptr, FEAT_BROKEN); } /* Secret doors (200/1000) */ else if (tmp < 600) { /* Create secret door */ set_feat_grid(c_ptr, FEAT_SECRET); } /* Closed, locked, or stuck doors (400/1000) */ else place_closed_door(x, y); } /* * Place a random type of normal door at the given location. * (Use this during dungeon creation) */ void place_closed_door(int x, int y) { int tmp; /* Invisible wall */ if (ironman_nightmare && one_in_(666)) { /* Create invisible wall */ set_feat_bold(x, y, dun->feat_floor); (void)place_field(x, y, FT_WALL_INVIS); return; } /* Choose an object */ tmp = randint0(400); /* Closed doors (300/400) */ if (tmp < 300) { /* Create closed door */ set_feat_bold(x, y, FEAT_CLOSED); } /* Locked doors (99/400) */ else if (tmp < 399) { /* Create locked door */ make_lockjam_door(x, y, randint1(10) + p_ptr->depth / 10, FALSE); } /* Stuck doors (1/400) */ else { /* Create jammed door */ make_lockjam_door(x, y, randint1(5) + p_ptr->depth / 10, TRUE); } } /* * Create up to "num" objects near the given coordinates * Only really called by some of the "vault" routines. */ void vault_objects(int x, int y, int num) { int dummy = 0; int i = 0, j = y, k = x; cave_type *c_ptr; /* Attempt to place 'num' objects */ for (; num > 0; --num) { /* Try up to 11 spots looking for empty space */ for (i = 0; i < 11; ++i) { /* Pick a random location */ while (dummy < SAFE_MAX_ATTEMPTS) { j = rand_spread(y, 2); k = rand_spread(x, 3); dummy++; if (!in_bounds(k, j)) continue; break; } if (dummy >= SAFE_MAX_ATTEMPTS) { if (cheat_room) { msgf("Warning! Could not place vault object!"); } } /* Require "clean" floor space */ c_ptr = cave_p(k, j); if (!cave_clean_grid(c_ptr)) continue; /* Place an item */ if (randint0(100) < 75) { place_object(k, j, FALSE, FALSE, 0); } /* Place gold */ else { place_gold(k, j); } /* Placement accomplished */ break; } } } /* * Place a trap with a given displacement of point */ static void vault_trap_aux(int x, int y, int xd, int yd) { int count; int x1 = x, y1 = y; int dummy = 0; cave_type *c_ptr; /* Place traps */ for (count = 0; count <= 5; count++) { /* Get a location */ while (dummy < SAFE_MAX_ATTEMPTS) { y1 = rand_spread(y, yd); x1 = rand_spread(x, xd); dummy++; if (!in_bounds(x1, y1)) continue; break; } if (dummy >= SAFE_MAX_ATTEMPTS) { if (cheat_room) { msgf("Warning! Could not place vault trap!"); } return; } /* Require "naked" floor grids */ c_ptr = cave_p(x1, y1); if (cave_naked_grid(c_ptr)) { /* Place the trap */ place_trap(x1, y1); /* Done ('return' seems to give a warning.) */ count = 6; } } } /* * Place some traps with a given displacement of given location */ void vault_traps(int x, int y, int xd, int yd, int num) { int i; for (i = 0; i < num; i++) { vault_trap_aux(x, y, xd, yd); } } /* * Hack -- Place some sleeping monsters near the given location */ void vault_monsters(int x1, int y1, int num) { int k, i, y, x; cave_type *c_ptr; /* Try to summon "num" monsters "near" the given location */ for (k = 0; k < num; k++) { /* Try nine locations */ for (i = 0; i < 9; i++) { int d = 1; /* Pick a nearby location */ scatter(&x, &y, x1, y1, d); /* Require "empty" floor grids */ c_ptr = cave_p(x, y); if (!cave_empty_grid(c_ptr)) continue; /* Place the monster (allow groups) */ (void)place_monster(x, y, TRUE, TRUE, 2); /* Have placed a monster */ break; } } } /* * Count the number of walls adjacent to the given grid. * * Note -- Assumes "in_bounds(x, y)" * * We count only granite walls and permanent walls. */ int next_to_walls(int x, int y) { int k = 0; if (cave_floor_grid(cave_p(x, y + 1))) k++; if (cave_floor_grid(cave_p(x, y - 1))) k++; if (cave_floor_grid(cave_p(x + 1, y))) k++; if (cave_floor_grid(cave_p(x - 1, y))) k++; return (k); } /* * Generate helper -- create a new room with optional light */ void generate_room(int x1, int y1, int x2, int y2, int light) { int y, x; cave_type *c_ptr; for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { /* Point to grid */ c_ptr = cave_p(x, y); c_ptr->info |= (CAVE_ROOM); if (light) c_ptr->info |= (CAVE_GLOW); } } } /* * Generate helper -- set flags for random vault. */ void generate_vault(int x1, int y1, int x2, int y2) { int y, x; for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { cave_p(x, y)->info |= (CAVE_ROOM | CAVE_ICKY); } } } /* * Generate helper -- unset the CAVE_ICKY flag in a region. */ void clear_vault(int x1, int y1, int x2, int y2) { int y, x; for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { cave_p(x, y)->info &= ~(CAVE_ICKY); } } } /* * Generate helper -- fill a rectangle with a feature */ void generate_fill(int x1, int y1, int x2, int y2, int feat) { int y, x; for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { /* Draw feature on every square */ set_feat_bold(x, y, feat); } } } /* * Generate helper -- draw a rectangle with a feature */ void generate_draw(int x1, int y1, int x2, int y2, int feat) { int y, x; for (y = y1; y <= y2; y++) { set_feat_bold(x1, y, feat); set_feat_bold(x2, y, feat); } for (x = x1; x <= x2; x++) { set_feat_bold(x, y1, feat); set_feat_bold(x, y2, feat); } } /* * Generate helper -- draw a line with a feature in a room */ void generate_line(int x1, int y1, int x2, int y2, int feat) { int i; if (x1 == x2) { for (i = y1; i <= y2; i++) { set_feat_bold(x1, i, feat); } } else if (y1 == y2) { for (i = x1; i <= x2; i++) { set_feat_bold(i, y1, feat); } } else { quit("Not a horizontal or vertical line in generate_line()"); } } /* * Generate helper -- split a rectangle with a feature */ void generate_plus(int x1, int y1, int x2, int y2, int feat) { int y, x; int y0, x0; /* Center */ y0 = (y1 + y2) / 2; x0 = (x1 + x2) / 2; for (y = y1; y <= y2; y++) { set_feat_bold(x0, y, feat); } for (x = x1; x <= x2; x++) { set_feat_bold(x, y0, feat); } } #ifdef UNUSED_FUNCTION /* * Generate helper -- open all sides of a rectangle with a feature */ void generate_open(int x1, int y1, int x2, int y2, int feat) { int y0, x0; /* Center */ y0 = (y1 + y2) / 2; x0 = (x1 + x2) / 2; /* Open all sides */ set_feat_bold(x0, y1, feat); set_feat_bold(x1, y0, feat); set_feat_bold(x0, y2, feat); set_feat_bold(x2, y0, feat); } /* * Generate helper -- open one side of a rectangle with a feature */ void generate_hole(int x1, int y1, int x2, int y2, int feat) { int y0, x0; /* Center */ y0 = (y1 + y2) / 2; x0 = (x1 + x2) / 2; /* Open random side */ switch (randint0(4)) { case 0: { set_feat_bold(x0, y1, feat); break; } case 1: { set_feat_bold(x1, y0, feat); break; } case 2: { set_feat_bold(x0, y2, feat); break; } case 3: { set_feat_bold(x2, y0, feat); break; } } } #endif /* UNUSED_FUNCTION */ /* * Generate helper -- open one side of a rectangle with a door */ void generate_door(int x1, int y1, int x2, int y2, bool secret) { int y0, x0; /* Center */ y0 = (y1 + y2) / 2; x0 = (x1 + x2) / 2; /* Open random side */ switch (randint0(4)) { case 0: { y0 = y1; break; } case 1: { x0 = x1; break; } case 2: { y0 = y2; break; } case 3: { x0 = x2; break; } } /* Add the door */ if (secret) { place_secret_door(x0, y0); } else { place_closed_door(x0, y0); } } /* * Always picks a correct direction */ static void correct_dir(int *cdir, int *rdir, int x1, int y1, int x2, int y2) { /* Extract vertical and horizontal directions */ *rdir = (y1 == y2) ? 0 : (y1 < y2) ? 1 : -1; *cdir = (x1 == x2) ? 0 : (x1 < x2) ? 1 : -1; /* Never move diagonally */ if (*rdir && *cdir) { if (one_in_(2)) *rdir = 0; else *cdir = 0; } } /* * Pick a random direction */ static void rand_dir(int *cdir, int *rdir) { /* Pick a random direction */ int i = randint0(4); /* Extract the dy/dx components */ *rdir = ddy_ddd[i]; *cdir = ddx_ddd[i]; } /* Function that sees if a square is a floor. (Includes range checking.) */ bool get_is_floor(int x, int y) { cave_type *c_ptr; /* Paranoia */ if (!in_bounds(x, y)) return (FALSE); c_ptr = cave_p(x, y); /* Do not count floors internal to other rooms */ if (c_ptr->info & CAVE_ROOM) return (FALSE); /* Do the real check */ if (cave_floor_grid(c_ptr)) return (TRUE); /* Not a floor */ return (FALSE); } /* Set a square to be floor. (Includes range checking.) */ void set_floor(int x, int y) { cave_type *c_ptr; if (!in_bounds(x, y)) { /* Out of bounds */ return; } c_ptr = cave_p(x, y); if (c_ptr->info & CAVE_ROOM) { /* A room border don't touch. */ return; } /* Set to be floor if is a wall (don't touch lakes). */ if (c_ptr->feat == FEAT_WALL_EXTRA) { set_feat_grid(c_ptr, dun->feat_floor); } } /* * Constructs a tunnel between two points * * This function must be called BEFORE any streamers are created, * since we use the special "granite wall" sub-types to keep track * of legal places for corridors to pierce rooms. * * We use "door_flag" to prevent excessive construction of doors * along overlapping corridors. * * We queue the tunnel grids to prevent door creation along a corridor * which intersects itself. * * We queue the wall piercing grids to prevent a corridor from leaving * a room and then coming back in through the same entrance. * * We "pierce" grids which are "outer" walls of rooms, and when we * do so, we change all adjacent "outer" walls of rooms into "solid" * walls so that no two corridors may use adjacent grids for exits. * * The "solid" wall check prevents corridors from "chopping" the * corners of rooms off, as well as "silly" door placement, and * "excessively wide" room entrances. * * Useful "feat" values: * FEAT_WALL_EXTRA -- granite walls * FEAT_WALL_INNER -- inner room walls * FEAT_WALL_OUTER -- outer room walls * FEAT_WALL_SOLID -- solid room walls * FEAT_PERM_EXTRA -- shop walls (perma) * FEAT_PERM_INNER -- inner room walls (perma) * FEAT_PERM_OUTER -- outer room walls (perma) * FEAT_PERM_SOLID -- dungeon border (perma) */ void build_tunnel(int col1, int row1, int col2, int row2) { int y, x; int tmp_row, tmp_col; int row_dir, col_dir; int start_row, start_col; int main_loop_count = 0; bool door_flag = FALSE; cave_type *c_ptr; /* Save the starting location */ start_row = row1; start_col = col1; /* Start out in the correct direction */ correct_dir(&col_dir, &row_dir, col1, row1, col2, row2); /* Keep going until done (or bored) */ while ((row1 != row2) || (col1 != col2)) { /* Mega-Hack -- Paranoia -- prevent infinite loops */ if (main_loop_count++ > 2000) break; /* Allow bends in the tunnel */ if (randint0(100) < dun_tun_chg) { /* Acquire the correct direction */ correct_dir(&col_dir, &row_dir, col1, row1, col2, row2); /* Random direction */ if (randint0(100) < dun_tun_rnd) { rand_dir(&col_dir, &row_dir); } } /* Get the next location */ tmp_row = row1 + row_dir; tmp_col = col1 + col_dir; /* Extremely Important -- do not leave the dungeon */ while (!in_bounds(tmp_col, tmp_row)) { /* Acquire the correct direction */ correct_dir(&col_dir, &row_dir, col1, row1, col2, row2); /* Random direction */ if (randint0(100) < dun_tun_rnd) { rand_dir(&col_dir, &row_dir); } /* Get the next location */ tmp_row = row1 + row_dir; tmp_col = col1 + col_dir; } /* Access the location */ c_ptr = cave_p(tmp_col, tmp_row); /* Avoid the permanent walls */ if (cave_perma_grid(c_ptr) && cave_wall_grid(c_ptr)) continue; /* Avoid "solid" granite walls */ if (c_ptr->feat == FEAT_WALL_SOLID) continue; /* Pierce "outer" walls of rooms */ if (c_ptr->feat == FEAT_WALL_OUTER) { cave_type *tmp_c_ptr; /* Acquire the "next" location */ y = tmp_row + row_dir; x = tmp_col + col_dir; tmp_c_ptr = cave_p(x, y); /* Hack -- Avoid permanent walls */ if (cave_perma_grid(tmp_c_ptr) && cave_wall_grid(tmp_c_ptr)) { continue; } /* Hack -- Avoid outer/solid granite walls */ if (tmp_c_ptr->feat == FEAT_WALL_OUTER) continue; if (tmp_c_ptr->feat == FEAT_WALL_SOLID) continue; /* Accept this location */ row1 = tmp_row; col1 = tmp_col; /* Save the wall location */ if (dun->wall_n < WALL_MAX) { dun->wall[dun->wall_n].y = row1; dun->wall[dun->wall_n].x = col1; dun->wall_n++; } /* Forbid re-entry near this piercing */ for (y = row1 - 1; y <= row1 + 1; y++) { for (x = col1 - 1; x <= col1 + 1; x++) { /* Convert adjacent "outer" walls as "solid" walls */ if (cave_p(x, y)->feat == FEAT_WALL_OUTER) { /* Change the wall to a "solid" wall */ set_feat_bold(x, y, FEAT_WALL_SOLID); } } } } /* Travel quickly through rooms */ else if (c_ptr->info & (CAVE_ROOM)) { /* Accept the location */ row1 = tmp_row; col1 = tmp_col; } /* Tunnel through all other walls */ else if (cave_wall_grid(c_ptr)) { /* Accept this location */ row1 = tmp_row; col1 = tmp_col; /* Save the tunnel location */ if (dun->tunn_n < TUNN_MAX) { dun->tunn[dun->tunn_n].y = row1; dun->tunn[dun->tunn_n].x = col1; dun->tunn_n++; } /* Allow door in next grid */ door_flag = FALSE; } /* Handle corridor intersections or overlaps */ else { /* Accept the location */ row1 = tmp_row; col1 = tmp_col; /* Collect legal door locations */ if (!door_flag) { /* Save the door location */ if (dun->door_n < DOOR_MAX) { dun->door[dun->door_n].y = row1; dun->door[dun->door_n].x = col1; dun->door_n++; } /* No door in next grid */ door_flag = TRUE; } /* Hack -- allow pre-emptive tunnel termination */ if (randint0(100) >= dun_tun_con) { /* Distance between row1 and start_row */ tmp_row = row1 - start_row; if (tmp_row < 0) tmp_row = (-tmp_row); /* Distance between col1 and start_col */ tmp_col = col1 - start_col; if (tmp_col < 0) tmp_col = (-tmp_col); /* Terminate the tunnel */ if ((tmp_row > 10) || (tmp_col > 10)) break; } } } } /* * This routine adds the square to the tunnel * It also checks for SOLID walls - and returns a nearby * non-SOLID square in (x,y) so that a simple avoiding * routine can be used. The returned boolean value reflects * whether or not this routine hit a SOLID wall. * * "affectwall" toggles whether or not this new square affects * the boundaries of rooms. - This is used by the catacomb * routine. */ static bool set_tunnel(int *x, int *y, bool affectwall) { int feat, i, j, dx, dy; if (!in_bounds(*x, *y)) return TRUE; feat = cave_p(*x, *y)->feat; if ((cave_perma_grid(cave_p(*x, *y)) && cave_wall_grid(cave_p(*x, *y))) || (feat == FEAT_WALL_INNER)) { /* * Ignore permanent walls - sometimes cannot tunnel around them anyway * so don't try - it just complicates things unnecessarily. */ return TRUE; } if (feat == FEAT_WALL_EXTRA) { /* Save the tunnel location */ if (dun->tunn_n < TUNN_MAX) { dun->tunn[dun->tunn_n].y = *y; dun->tunn[dun->tunn_n].x = *x; dun->tunn_n++; } return TRUE; } if (feat == dun->feat_floor) { /* Don't do anything */ return TRUE; } if ((feat == FEAT_WALL_OUTER) && affectwall) { /* Save the wall location */ if (dun->wall_n < WALL_MAX) { dun->wall[dun->wall_n].y = *y; dun->wall[dun->wall_n].x = *x; dun->wall_n++; } /* Forbid re-entry near this piercing */ for (j = *y - 1; j <= *y + 1; j++) { for (i = *x - 1; i <= *x + 1; i++) { /* Convert adjacent "outer" walls as "solid" walls */ if (cave_p(i, j)->feat == FEAT_WALL_OUTER) { /* Change the wall to a "solid" wall */ set_feat_bold(i, j, FEAT_WALL_SOLID); } } } set_feat_bold(*x, *y, dun->feat_floor); return TRUE; } if ((feat == FEAT_WALL_SOLID) && affectwall) { /* cannot place tunnel here - use a square to the side */ /* find usable square and return value in (x,y) */ i = 50; dy = 0; dx = 0; while ((i > 0) && (cave_p(*x + dx, *y + dy)->feat == FEAT_WALL_SOLID)) { dy = randint0(3) - 1; dx = randint0(3) - 1; if (!in_bounds(*x + dx, *y + dy)) { dx = 0; dy = 0; } i--; } if (i == 0) { /* Failed for some reason: hack - ignore the solidness */ cave_p(*x, *y)->feat = FEAT_WALL_OUTER; dx = 0; dy = 0; } /* Give new, acceptable coordinate. */ *x = *x + dx; *y = *y + dy; return FALSE; } return TRUE; } /* * This routine creates the catacomb-like tunnels by removing extra rock. * Note that this routine is only called on "even" squares - so it gives * a natural checkerboard pattern. */ static void create_cata_tunnel(int x, int y) { int x1, y1; /* Build tunnel */ x1 = x - 1; y1 = y; (void)set_tunnel(&x1, &y1, FALSE); x1 = x + 1; y1 = y; (void)set_tunnel(&x1, &y1, FALSE); x1 = x; y1 = y - 1; (void)set_tunnel(&x1, &y1, FALSE); x1 = x; y1 = y + 1; (void)set_tunnel(&x1, &y1, FALSE); } /* * This routine does the bulk of the work in creating the new types of tunnels. * It is designed to use very simple algorithms to go from (x1,y1) to (x2,y2) * It doesn't need to add any complexity - straight lines are fine. * The SOLID walls are avoided by a recursive algorithm which tries random ways * around the obstical until it works. The number of iterations is counted, and it * this gets too large the routine exits. This should stop any crashes - but may leave * small gaps in the tunnel where there are too many SOLID walls. * * Type 1 tunnels are extremely simple - straight line from A to B. This is only used * as a part of the dodge SOLID walls algorithm. * * Type 2 tunnels are made of two straight lines at right angles. When this is used with * short line segments it gives the "cavelike" tunnels seen deeper in the dungeon. * * Type 3 tunnels are made of two straight lines like type 2, but with extra rock removed. * This, when used with longer line segments gives the "catacomb-like" tunnels seen near * the surface. */ static void short_seg_hack(int x1, int y1, int x2, int y2, int type, int count, bool *fail) { int i, x, y; int length; /* Check for early exit */ if (!(*fail)) return; length = distance(x1, y1, x2, y2); count++; if ((type == 1) && (length != 0)) { for (i = 0; i <= length; i++) { x = x1 + i * (x2 - x1) / length; y = y1 + i * (y2 - y1) / length; if (!set_tunnel(&x, &y, TRUE)) { if (count > 50) { /* This isn't working - probably have an infinite loop */ *fail = FALSE; return; } /* solid wall - so try to go around */ short_seg_hack(x, y, x1 + (i - 1) * (x2 - x1) / length, y1 + (i - 1) * (y2 - y1) / length, 1, count, fail); short_seg_hack(x, y, x1 + (i + 1) * (x2 - x1) / length, y1 + (i + 1) * (y2 - y1) / length, 1, count, fail); } } } else if ((type == 2) || (type == 3)) { if (x1 < x2) { for (i = x1; i <= x2; i++) { x = i; y = y1; if (!set_tunnel(&x, &y, TRUE)) { /* solid wall - so try to go around */ short_seg_hack(x, y, i - 1, y1, 1, count, fail); short_seg_hack(x, y, i + 1, y1, 1, count, fail); } if ((type == 3) && ((x + y) % 2)) { create_cata_tunnel(i, y1); } } } else { for (i = x2; i <= x1; i++) { x = i; y = y1; if (!set_tunnel(&x, &y, TRUE)) { /* solid wall - so try to go around */ short_seg_hack(x, y, i - 1, y1, 1, count, fail); short_seg_hack(x, y, i + 1, y1, 1, count, fail); } if ((type == 3) && ((x + y) % 2)) { create_cata_tunnel(i, y1); } } } if (y1 < y2) { for (i = y1; i <= y2; i++) { x = x2; y = i; if (!set_tunnel(&x, &y, TRUE)) { /* solid wall - so try to go around */ short_seg_hack(x, y, x2, i - 1, 1, count, fail); short_seg_hack(x, y, x2, i + 1, 1, count, fail); } if ((type == 3) && ((x + y) % 2)) { create_cata_tunnel(x2, i); } } } else { for (i = y2; i <= y1; i++) { x = x2; y = i; if (!set_tunnel(&x, &y, TRUE)) { /* solid wall - so try to go around */ short_seg_hack(x, y, x2, i - 1, 1, count, fail); short_seg_hack(x, y, x2, i + 1, 1, count, fail); } if ((type == 3) && ((x + y) % 2)) { create_cata_tunnel(x2, i); } } } } } /* * This routine maps a path from (x1, y1) to (x2, y2) avoiding SOLID walls. * Permanent rock is ignored in this path finding- sometimes there is no * path around anyway -so there will be a crash if we try to find one. * This routine is much like the river creation routine in Zangband. * It works by dividing a line segment into two. The segments are divided * until they are less than "cutoff" - when the corresponding routine from * "short_seg_hack" is called. * Note it is VERY important that the "stop if hit another passage" logic * stays as is. Without this the dungeon turns into Swiss Cheese... */ bool build_tunnel2(int x1, int y1, int x2, int y2, int type, int cutoff) { int x3, y3, dx, dy; int changex, changey; int midval; int length; int i; bool retval, firstsuccede; length = distance(x1, y1, x2, y2); if (length > cutoff) { /* * Divide path in half and call routine twice. */ dx = (x2 - x1) / 2; dy = (y2 - y1) / 2; /* perturbation perpendicular to path */ changex = (randint0(ABS(dy) + 2) * 2 - ABS(dy) - 1) / 2; /* perturbation perpendicular to path */ changey = (randint0(ABS(dx) + 2) * 2 - ABS(dx) - 1) / 2; /* Work out "mid" ponit */ x3 = x1 + dx + changex; y3 = y1 + dy + changey; /* See if in bounds - if not - do not perturb point */ if (!in_bounds(x3, y3)) { x3 = (x1 + x2) / 2; y3 = (y1 + y2) / 2; } /* cache midvalue */ midval = cave_p(x3, y3)->feat; if (midval == FEAT_WALL_SOLID) { /* move midpoint a bit to avoid problem. */ i = 50; dy = 0; dx = 0; while ((i > 0) && (cave_p(x3 + dx, y3 + dy)->feat == FEAT_WALL_SOLID)) { dy = randint0(3) - 1; dx = randint0(3) - 1; if (!in_bounds(x3 + dx, y3 + dy)) { dx = 0; dy = 0; } i--; } if (i == 0) { /* Failed for some reason: hack - ignore the solidness */ cave_p(x3, y3)->feat = FEAT_WALL_OUTER; dx = 0; dy = 0; } y3 += dy; x3 += dx; midval = cave_p(x3, y3)->feat; } if (midval == dun->feat_floor) { if (build_tunnel2(x1, y1, x3, y3, type, cutoff)) { if ((cave_p(x3, y3)->info & CAVE_ROOM) || (randint1(100) > 95)) { /* do second half only if works + if have hit a room */ retval = build_tunnel2(x3, y3, x2, y2, type, cutoff); } else { /* have hit another tunnel - make a set of doors here */ retval = FALSE; /* Save the door location */ if (dun->door_n < DOOR_MAX) { dun->door[dun->door_n].y = y3; dun->door[dun->door_n].x = x3; dun->door_n++; } } firstsuccede = TRUE; } else { /* false- didn't work all the way */ retval = FALSE; firstsuccede = FALSE; } } else { /* tunnel through walls */ if (build_tunnel2(x1, y1, x3, y3, type, cutoff)) { retval = build_tunnel2(x3, y3, x2, y2, type, cutoff); firstsuccede = TRUE; } else { /* false- didn't work all the way */ retval = FALSE; firstsuccede = FALSE; } } if (firstsuccede) { /* only do this if the first half has worked */ (void)set_tunnel(&x3, &y3, TRUE); } /* return value calculated above */ return retval; } else { /* Do a short segment */ retval = TRUE; short_seg_hack(x1, y1, x2, y2, type, 0, &retval); /* Hack - ignore return value so avoid infinite loops */ return TRUE; } } /* * Structure to hold all "fill" data */ typedef struct fill_data_type fill_data_type; struct fill_data_type { /* area size */ int xmin; int ymin; int xmax; int ymax; /* cutoffs */ int c1; int c2; int c3; /* features to fill with */ int feat1; int feat2; int feat3; /* number of filled squares */ int amount; }; static fill_data_type fill_data; /* Store routine for the fractal cave generator */ /* this routine probably should be an inline function or a macro. */ static void store_height(int x, int y, int val) { /* if on boundary set val > cutoff so walls are not as square */ if (((x == fill_data.xmin) || (y == fill_data.ymin) || (x == fill_data.xmax) || (y == fill_data.ymax)) && (val <= fill_data.c1)) val = fill_data.c1 + 1; /* store the value in height-map format */ cave_p(x, y)->feat = val; return; } /* * Explanation of the plasma fractal algorithm: * * A grid of points is created with the properties of a 'height-map' * This is done by making the corners of the grid have a random value. * The grid is then subdivided into one with twice the resolution. * The new points midway between two 'known' points can be calculated * by taking the average value of the 'known' ones and randomly adding * or subtracting an amount proportional to the distance between those * points. The final 'middle' points of the grid are then calculated * by averaging all four of the originally 'known' corner points. An * random amount is added or subtracted from this to get a value of the * height at that point. The scaling factor here is adjusted to the * slightly larger distance diagonally as compared to orthogonally. * * This is then repeated recursively to fill an entire 'height-map' * A rectangular map is done the same way, except there are different * scaling factors along the x and y directions. * * A hack to change the amount of correlation between points is done using * the grd variable. If the current step size is greater than grd then * the point will be random, otherwise it will be calculated by the * above algorithm. This makes a maximum distance at which two points on * the height map can affect each other. * * How fractal caves are made: * * When the map is complete, a cut-off value is used to create a cave. * Heights below this value are "floor", and heights above are "wall". * This also can be used to create lakes, by adding more height levels * representing shallow and deep water/ lava etc. * * The grd variable affects the width of passages. * The roug variable affects the roughness of those passages * * The tricky part is making sure the created cave is connected. This * is done by 'filling' from the inside and only keeping the 'filled' * floor. Walls bounding the 'filled' floor are also kept. Everything * else is converted to the normal granite FEAT_WALL_EXTRA. */ /* * Note that this uses the cave.feat array in a very hackish way * the values are first set to zero, and then each array location * is used as a "heightmap" * The heightmap then needs to be converted back into the "feat" format. * * grd=level at which fractal turns on. smaller gives more mazelike caves * roug=roughness level. 16=normal. higher values make things more convoluted * small values are good for smooth walls. * size=length of the side of the square cave system. */ void generate_hmap(int x0, int y0, int xsiz, int ysiz, int grd, int roug, int cutoff) { int xhsize, yhsize, xsize, ysize, maxsize; /* * fixed point variables- these are stored as 256 x normal value * this gives 8 binary places of fractional part + 8 places of normal part */ u16b xstep, xhstep, ystep, yhstep; u16b xstep2, xhstep2, ystep2, yhstep2; u16b i, j, ii, jj, diagsize, xxsize, yysize; /* Cache for speed */ u16b xm, xp, ym, yp; cave_type *c_ptr; /* redefine size so can change the value if out of range */ xsize = xsiz; ysize = ysiz; /* Paranoia about size of the system of caves */ if (xsize > 254) xsize = 254; if (xsize < 4) xsize = 4; if (ysize > 254) ysize = 254; if (ysize < 4) ysize = 4; /* get offsets to middle of array */ xhsize = xsize / 2; yhsize = ysize / 2; /* fix rounding problem */ xsize = xhsize * 2; ysize = yhsize * 2; /* get limits of region */ fill_data.xmin = x0 - xhsize; fill_data.ymin = y0 - yhsize; fill_data.xmax = x0 + xhsize; fill_data.ymax = y0 + yhsize; /* Store cutoff in global for quick access */ fill_data.c1 = cutoff; /* * Scale factor for middle points: * About sqrt(2) * 256 - correct for a square lattice * approximately correct for everything else. */ diagsize = 362; /* maximum of xsize and ysize */ maxsize = (xsize > ysize) ? xsize : ysize; /* Clear the section */ for (i = 0; i <= xsize; i++) { for (j = 0; j <= ysize; j++) { c_ptr = cave_p((int)fill_data.xmin + i, (int)fill_data.ymin + j); /* 255 is a flag for "not done yet" */ c_ptr->feat = 255; /* Clear icky flag because may be redoing the cave */ c_ptr->info &= ~(CAVE_ICKY); } } /* Boundaries are walls */ cave_p(fill_data.xmin, fill_data.ymin)->feat = maxsize; cave_p(fill_data.xmin, fill_data.ymax)->feat = maxsize; cave_p(fill_data.xmax, fill_data.ymin)->feat = maxsize; cave_p(fill_data.xmax, fill_data.ymax)->feat = maxsize; /* Set the middle square to be an open area. */ cave_p(x0, y0)->feat = 0; /* Initialize the step sizes */ xstep = xhstep = xsize * 256; ystep = yhstep = ysize * 256; xxsize = xsize * 256; yysize = ysize * 256; /* * Fill in the rectangle with fractal height data - * like the 'plasma fractal' in fractint. */ while ((xhstep > 256) || (yhstep > 256)) { /* Halve the step sizes */ xstep = xhstep; xhstep /= 2; ystep = yhstep; yhstep /= 2; /* cache well used values */ xstep2 = xstep / 256; ystep2 = ystep / 256; xhstep2 = xhstep / 256; yhstep2 = yhstep / 256; /* middle top to bottom. */ for (i = xhstep; i <= xxsize - xhstep; i += xstep) { for (j = 0; j <= yysize; j += ystep) { /* cache often used values */ ii = i / 256 + fill_data.xmin; jj = j / 256 + fill_data.ymin; /* Test square */ if (cave_p(ii, jj)->feat == 255) { if (xhstep2 > grd) { /* If greater than 'grid' level then is random */ store_height(ii, jj, randint1(maxsize)); } else { /* Average of left and right points +random bit */ store_height(ii, jj, (cave_p(fill_data.xmin + (i - xhstep) / 256, jj)->feat + cave_p(fill_data.xmin + (i + xhstep) / 256, jj)->feat) / 2 + (randint1(xstep2) - xhstep2) * roug / 16); } } } } /* middle left to right. */ for (j = yhstep; j <= yysize - yhstep; j += ystep) { for (i = 0; i <= xxsize; i += xstep) { /* cache often used values */ ii = i / 256 + fill_data.xmin; jj = j / 256 + fill_data.ymin; /* Test square */ if (cave_p(ii, jj)->feat == 255) { if (xhstep2 > grd) { /* If greater than 'grid' level then is random */ store_height(ii, jj, randint1(maxsize)); } else { /* Average of up and down points +random bit */ store_height(ii, jj, (cave_p(ii, fill_data.ymin + (j - yhstep) / 256)->feat + cave_p(ii, fill_data.ymin + (j + yhstep) / 256)->feat) / 2 + (randint1(ystep2) - yhstep2) * roug / 16); } } } } /* center. */ for (i = xhstep; i <= xxsize - xhstep; i += xstep) { for (j = yhstep; j <= yysize - yhstep; j += ystep) { /* cache often used values */ ii = i / 256 + fill_data.xmin; jj = j / 256 + fill_data.ymin; /* Test square */ if (cave_p(ii, jj)->feat == 255) { if (xhstep2 > grd) { /* If greater than 'grid' level then is random */ store_height(ii, jj, randint1(maxsize)); } else { /* Cache reused values. */ xm = fill_data.xmin + (i - xhstep) / 256; xp = fill_data.xmin + (i + xhstep) / 256; ym = fill_data.ymin + (j - yhstep) / 256; yp = fill_data.ymin + (j + yhstep) / 256; /* * Average over all four corners + scale by diagsize to * reduce the effect of the square grid on the shape of the fractal */ store_height(ii, jj, (cave_p(xm, ym)->feat + cave_p(xm, yp)->feat + cave_p(xp, ym)->feat + cave_p(xp, yp)->feat) / 4 + (randint1(xstep2) - xhstep2) * (diagsize / 16) / 256 * roug); } } } } } } static bool hack_isnt_wall(int x, int y, int c1, int c2, int c3, int feat1, int feat2, int feat3) { cave_type *c_ptr = cave_p(x, y); /* * function used to convert from height-map back to the * normal angband cave format */ if (c_ptr->info & CAVE_ICKY) { /* already done */ return FALSE; } else { /* Show that have looked at this square */ c_ptr->info |= (CAVE_ICKY); /* Use cutoffs c1-c3 to allocate regions of floor /water/ lava etc. */ if (c_ptr->feat <= c1) { /* 25% of the time use the other tile : it looks better this way */ if (randint1(100) < 75) { c_ptr->feat = feat1; return TRUE; } else { c_ptr->feat = feat2; return TRUE; } } else if (c_ptr->feat <= c2) { /* 25% of the time use the other tile : it looks better this way */ if (randint1(100) < 75) { c_ptr->feat = feat2; return TRUE; } else { c_ptr->feat = feat1; return TRUE; } } else if (c_ptr->feat <= c3) { c_ptr->feat = feat3; return TRUE; } /* if greater than cutoff then is a wall */ else { c_ptr->feat = FEAT_WALL_OUTER; return FALSE; } } } /* * Fill the fractal height-map using the features specified in in fill_data. * * This routine is similar to the method used to update the monster flow * information. It uses the temp grids as a circular queue. */ static void cave_fill(int x, int y) { int i, j, d; int ty, tx; int flow_tail = 1; int flow_head = 0; /*** Start Grid ***/ /* Enqueue that entry */ temp_y[0] = y; temp_x[0] = x; /* Now process the queue */ while (flow_head != flow_tail) { /* Extract the next entry */ ty = temp_y[flow_head]; tx = temp_x[flow_head]; /* Forget that entry */ if (++flow_head == TEMP_MAX) flow_head = 0; /* Add the "children" */ for (d = 0; d < 8; d++) { int old_head = flow_tail; /* Child location */ j = ty + ddy_ddd[d]; i = tx + ddx_ddd[d]; /* If within bounds */ if ((i > fill_data.xmin) && (i < fill_data.xmax) && (j > fill_data.ymin) && (j < fill_data.ymax)) { /* If not a wall or floor done before */ if (hack_isnt_wall(i, j, fill_data.c1, fill_data.c2, fill_data.c3, fill_data.feat1, fill_data.feat2, fill_data.feat3)) { /* Enqueue that entry */ temp_y[flow_tail] = j; temp_x[flow_tail] = i; /* Advance the queue */ if (++flow_tail == TEMP_MAX) flow_tail = 0; /* Hack -- Overflow by forgetting new entry */ if (flow_tail == flow_head) { flow_tail = old_head; } else { /* keep tally of size of cave system */ (fill_data.amount)++; } } } else { /* affect boundary */ cave_p(i, j)->info |= CAVE_ICKY; } } } } bool generate_fracave(int x0, int y0, int xsize, int ysize, int cutoff, bool light) { int x, y, i, xhsize, yhsize; cave_type *c_ptr; byte info; /* offsets to middle from corner */ xhsize = xsize / 2; yhsize = ysize / 2; /* * select region connected to center of cave system * this gets rid of alot of isolated one-sqaures that * can make teleport traps instadeaths... */ /* cutoffs */ fill_data.c1 = cutoff; fill_data.c2 = 0; fill_data.c3 = 0; /* features to fill with */ fill_data.feat1 = dun->feat_floor; fill_data.feat2 = dun->feat_floor; fill_data.feat3 = dun->feat_floor; /* number of filled squares */ fill_data.amount = 0; cave_fill(x0, y0); /* if tally too small, try again */ if (fill_data.amount < 10) { /* too small - clear area and try again later */ /* Clear the height map */ generate_fill(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, FEAT_WALL_EXTRA); /* Clear the icky flag */ clear_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1); /* Try again */ return FALSE; } /* Get the cave info value to logical OR to the grids */ info = CAVE_ROOM; if (light) info |= CAVE_GLOW; /* * Do boundarys-check to see if they are next to a filled region * If not then they are set to normal granite * If so then they are marked as room walls. */ for (i = 0; i <= xsize; ++i) { /* top boundary */ c_ptr = cave_p(i + x0 - xhsize, 0 + y0 - yhsize); if (c_ptr->info & CAVE_ICKY) { /* Next to a 'filled' region? - set to be room walls */ c_ptr->feat = FEAT_WALL_OUTER; c_ptr->info |= info; } else { /* set to be normal granite */ c_ptr->feat = FEAT_WALL_EXTRA; } /* bottom boundary */ c_ptr = cave_p(i + x0 - xhsize, ysize + y0 - yhsize); if (c_ptr->info & CAVE_ICKY) { /* Next to a 'filled' region? - set to be room walls */ c_ptr->feat = FEAT_WALL_OUTER; c_ptr->info |= info; } else { /* set to be normal granite */ c_ptr->feat = FEAT_WALL_EXTRA; } } /* Do the left and right boundaries minus the corners (done above) */ for (i = 1; i < ysize; ++i) { /* left boundary */ c_ptr = cave_p(0 + x0 - xhsize, i + y0 - yhsize); if (c_ptr->info & CAVE_ICKY) { /* room boundary */ c_ptr->feat = FEAT_WALL_OUTER; c_ptr->info |= info; } else { /* outside room */ c_ptr->feat = FEAT_WALL_EXTRA; } /* right boundary */ c_ptr = cave_p(xsize + x0 - xhsize, i + y0 - yhsize); if (c_ptr->info & CAVE_ICKY) { /* room boundary */ c_ptr->feat = FEAT_WALL_OUTER; c_ptr->info |= info; } else { /* outside room */ c_ptr->feat = FEAT_WALL_EXTRA; } } /* Do the rest: convert back to the normal format */ for (x = 1; x < xsize; ++x) { for (y = 1; y < ysize; ++y) { c_ptr = cave_p(x0 + x - xhsize, y0 + y - yhsize); if (!(c_ptr->info & CAVE_ICKY)) { /* Clear the unconnected regions */ c_ptr->feat = FEAT_WALL_EXTRA; } else { /* Is part of the cave */ c_ptr->info |= info; } } } /* Clear the icky flag */ clear_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1); /* * XXX XXX XXX There is a slight problem when tunnels pierce the caves: * Extra doors appear inside the system. (Its not very noticeable * though.) This can be removed by "filling" from the outside in. * This allows a separation from FEAT_WALL_OUTER with FEAT_WALL_INNER. * (Internal walls are F.W.OUTER instead.) The extra effort for what * seems to be only a minor thing (even non-existant if you think of the * caves not as normal rooms, but as holes in the dungeon), doesn't seem * worth it. */ return TRUE; } bool generate_lake(int x0, int y0, int xsize, int ysize, int c1, int c2, int c3, byte f1, byte f2, byte f3) { int x, y, xhsize, yhsize; cave_type *c_ptr; /* offsets to middle from corner */ xhsize = xsize / 2; yhsize = ysize / 2; /* cutoffs */ fill_data.c1 = c1; fill_data.c2 = c2; fill_data.c3 = c3; /* features to fill with */ fill_data.feat1 = f1; fill_data.feat2 = f2; fill_data.feat3 = f3; /* number of filled squares */ fill_data.amount = 0; /* * Select region connected to center of cave system * this gets rid of alot of isolated one-sqaures that * can make teleport traps instadeaths... */ cave_fill(x0, y0); /* if tally too small, try again */ if (fill_data.amount < 3) { /* too small -clear area and try again later */ /* Clear the height map */ generate_fill(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, FEAT_WALL_EXTRA); /* Clear the icky flag */ clear_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1); /* Try again */ return FALSE; } /* Do boundarys- set to normal granite */ generate_draw(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize, y0 - yhsize + ysize, FEAT_WALL_EXTRA); /* Do the rest: convert back to the normal format */ for (x = 1; x < xsize; ++x) { for (y = 1; y < ysize; ++y) { c_ptr = cave_p(x0 + x - xhsize, y0 + y - yhsize); /* Fill unconnected regions with granite */ if ((!(c_ptr->info & CAVE_ICKY)) || (c_ptr->feat == FEAT_WALL_OUTER)) { c_ptr->feat = FEAT_WALL_EXTRA; } /* Light lava and trees */ if ((c_ptr->feat == FEAT_DEEP_LAVA) || (c_ptr->feat == FEAT_SHAL_LAVA) || (c_ptr->feat == FEAT_TREES)) { c_ptr->info |= CAVE_GLOW; } } } /* Clear the icky flag */ clear_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1); /* Done */ return TRUE; } zangband/src/init1.c0000755000000000000000000015230110250356274013330 0ustar rootroot/* File: init1.c */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ #include "angband.h" /* * This file is used to initialize various variables and arrays for the * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass * the common limitation of "read()" and "write()" to only 32767 bytes * at a time. * * Several of the arrays for Angband are built from "template" files in * the "lib/file" directory, from which quick-load binary "image" files * are constructed whenever they are not present in the "lib/data" * directory, or if those files become obsolete, if we are allowed. * * Warning -- the "ascii" file parsers use a minor hack to collect the * name and text information in a single pass. Thus, the game will not * be able to load any template file with more than 20K of names or 60K * of text, even though technically, up to 64K should be legal. * * Note that if "ALLOW_TEMPLATES" is not defined, then a lot of the code * in this file is compiled out, and the game will not run unless valid * "binary template files" already exist in "lib/data". Thus, one can * compile Angband with ALLOW_TEMPLATES defined, run once to create the * "*.raw" files in "lib/data", and then quit, and recompile without * defining ALLOW_TEMPLATES, which will both save 20K and prevent people * from changing the ascii template files in potentially dangerous ways. * * The code could actually be removed and placed into a "stand-alone" * program, but that feels a little silly, especially considering some * of the platforms that we currently support. */ #ifdef ALLOW_TEMPLATES #include "init.h" /*** Helper arrays for parsing ascii template files ***/ /* * Feature flag types */ static cptr f_info_flags[] = { "BLOCK", "HALF_LOS", "USE_TRANS", "ICKY", "PERM", "OBJECT", "PATTERN", "MARK" }; /* * Object script triggers */ static cptr k_info_triggers[] = { "USE", "MAKE", "BONUS", "SMASH", "DESC", "TIMED", "HIT", "ATTACK", "ALTER", "SPOIL", NULL }; /* * Monster Blow Methods */ static cptr r_info_blow_method[] = { "", "HIT", "TOUCH", "PUNCH", "KICK", "CLAW", "BITE", "STING", "XXX1", "BUTT", "CRUSH", "ENGULF", "CHARGE", "CRAWL", "DROOL", "SPIT", "EXPLODE", "GAZE", "WAIL", "SPORE", "XXX4", "BEG", "INSULT", "MOAN", "SHOW", NULL }; /* * Monster Blow Effects */ static cptr r_info_blow_effect[] = { "", "HURT", "POISON", "UN_BONUS", "UN_POWER", "EAT_GOLD", "EAT_ITEM", "EAT_FOOD", "EAT_LITE", "ACID", "ELEC", "FIRE", "COLD", "BLIND", "CONFUSE", "TERRIFY", "PARALYZE", "LOSE_STR", "LOSE_INT", "LOSE_WIS", "LOSE_DEX", "LOSE_CON", "LOSE_CHR", "LOSE_ALL", "SHATTER", "EXP_10", "EXP_20", "EXP_40", "EXP_80", "DISEASE", "TIME", "EXP_VAMP", NULL }; /* * Monster race flags */ static cptr r_info_flags1[] = { "UNIQUE", "QUESTOR", "MALE", "FEMALE", "CHAR_CLEAR", "CHAR_MIMIC", "ATTR_CLEAR", "ATTR_MULTI", "FORCE_DEPTH", "FORCE_MAXHP", "FORCE_SLEEP", "FORCE_EXTRA", "XXX1_1", "FRIENDS", "ESCORT", "ESCORTS", "NEVER_BLOW", "NEVER_MOVE", "RAND_25", "RAND_50", "ONLY_GOLD", "ONLY_ITEM", "DROP_60", "DROP_90", "DROP_1D2", "DROP_2D2", "DROP_3D2", "DROP_4D2", "DROP_GOOD", "DROP_GREAT", "DROP_USEFUL", "DROP_CHOSEN" }; /* * Monster race flags */ static cptr r_info_flags2[] = { "STUPID", "SMART", "CAN_SPEAK", "REFLECTING", "INVISIBLE", "COLD_BLOOD", "EMPTY_MIND", "WEIRD_MIND", "MULTIPLY", "REGENERATE", "SHAPECHANGER", "ATTR_ANY", "POWERFUL", "XXX2X1", "AURA_FIRE", "AURA_ELEC", "OPEN_DOOR", "BASH_DOOR", "PASS_WALL", "KILL_WALL", "MOVE_BODY", "KILL_BODY", "TAKE_ITEM", "KILL_ITEM", "BRAIN_1", "BRAIN_2", "BRAIN_3", "BRAIN_4", "BRAIN_5", "BRAIN_6", "BRAIN_7", "QUANTUM" }; /* * Monster race flags */ static cptr r_info_flags3[] = { "ORC", "TROLL", "GIANT", "DRAGON", "DEMON", "UNDEAD", "EVIL", "ANIMAL", "AMBERITE", "GOOD", "AURA_COLD", "NONLIVING", "HURT_LITE", "HURT_ROCK", "HURT_FIRE", "HURT_COLD", "IM_ACID", "IM_ELEC", "IM_FIRE", "IM_COLD", "IM_POIS", "RES_TELE", "RES_NETH", "RES_WATE", "RES_PLAS", "RES_NEXU", "RES_DISE", "UNIQUE_7", "NO_FEAR", "NO_STUN", "NO_CONF", "NO_SLEEP" }; /* * Monster race flags */ static cptr r_info_flags4[] = { "SHRIEK", "ELDRITCH_HORROR", "XXX3X4", "ROCKET", "ARROW", "XXX6X4", "XXX7X4", "XXX8X4", "BR_ACID", "BR_ELEC", "BR_FIRE", "BR_COLD", "BR_POIS", "BR_NETH", "BR_LITE", "BR_DARK", "BR_CONF", "BR_SOUN", "BR_CHAO", "BR_DISE", "BR_NEXU", "BR_TIME", "BR_INER", "BR_GRAV", "BR_SHAR", "BR_PLAS", "BR_WALL", "BR_MANA", "BA_NUKE", "BR_NUKE", "BA_CHAO", "BR_DISI", }; /* * Monster race flags */ static cptr r_info_flags5[] = { "BA_ACID", "BA_ELEC", "BA_FIRE", "BA_COLD", "BA_POIS", "BA_NETH", "BA_WATE", "BA_MANA", "BA_DARK", "DRAIN_MANA", "MIND_BLAST", "BRAIN_SMASH", "CAUSE_1", "CAUSE_2", "CAUSE_3", "CAUSE_4", "BO_ACID", "BO_ELEC", "BO_FIRE", "BO_COLD", "BO_POIS", "BO_NETH", "BO_WATE", "BO_MANA", "BO_PLAS", "BO_ICEE", "MISSILE", "SCARE", "BLIND", "CONF", "SLOW", "HOLD" }; /* * Monster race flags */ static cptr r_info_flags6[] = { "HASTE", "HAND_DOOM", "HEAL", "INVULNER", "BLINK", "TPORT", "XXX3X6", "XXX4X6", "TELE_TO", "TELE_AWAY", "TELE_LEVEL", "XXX5", "DARKNESS", "TRAPS", "FORGET", "ANIM_DEAD", /* ToDo: Implement ANIM_DEAD */ "S_KIN", "S_CYBER", "S_MONSTER", "S_MONSTERS", "S_ANT", "S_SPIDER", "S_HOUND", "S_HYDRA", "S_ANGEL", "S_DEMON", "S_UNDEAD", "S_DRAGON", "S_HI_UNDEAD", "S_HI_DRAGON", "S_AMBERITES", "S_UNIQUE" }; /* * Monster race flags */ static cptr r_info_flags7[] = { "AQUATIC", "CAN_SWIM", "CAN_FLY", "FRIENDLY", "SILLY", "LITE_1", "LITE_2", "XXX7X7", "XXX7X8", "XXX7X9", "XXX7X10", "XXX7X11", "XXX7X12", "XXX7X13", "XXX7X14", "XXX7X15", "XXX7X16", "XXX7X17", "XXX7X18", "XXX7X19", "XXX7X20", "XXX7X21", "XXX7X22", "XXX7X23", "XXX7X24", "XXX7X25", "XXX7X26", "XXX7X27", "XXX7X28", "XXX7X29", "XXX7X30", "XXX7X31", }; /* * Monster race flags */ static cptr r_info_flags8[] = { "WILD_FOREST1", "WILD_FOREST2", "WILD_MOUNT1", "WILD_MOUNT2", "WILD_WASTE1", "WILD_WASTE2", "WILD_SWAMP1", "WILD_SWAMP2", "WILD_SHORE", "WILD_OCEAN", "WILD_GRASS", "WILD_TOWN", "DUN_DARKWATER", "DUN_LAIR", "DUN_TEMPLE", "DUN_TOWER", "DUN_RUIN", "DUN_GRAVE", "DUN_CAVERN", "DUN_PLANAR", "DUN_HELL", "DUN_HORROR", "DUN_MINE", "DUN_CITY", "DUN_XTRA1", "DUN_XTRA2", "DUN_XTRA3", "DUN_XTRA4", "DUN_XTRA5", "DUN_XTRA6", "DUN_XTRA7", "DUN_XTRA8" }; /* * Monster race flags - Drops */ static cptr r_info_flags9[] = { "DROP_CORPSE", "DROP_SKELETON", "XXX9X2", "XXX9X3", "XXX9X4", "XXX9X5", "XXX9X6", "XXX9X7", "XXX9X8", "XXX9X9", "XXX9X10", "XXX9X11", "XXX9X12", "XXX9X13", "XXX9X14", "XXX9X15", "XXX9X16", "XXX9X17", "XXX9X18", "XXX9X19", "XXX9X20", "XXX9X21", "XXX9X22", "XXX9X23", "XXX9X24", "XXX9X25", "XXX9X26", "XXX9X27", "XXX9X28", "XXX9X29", "XXX9X30", "XXX9X31", }; /* * Object flags */ static cptr k_info_flags1[] = { "STR", "INT", "WIS", "DEX", "CON", "CHR", "XXX1", "SP", "STEALTH", "SENSE", "INFRA", "TUNNEL", "SPEED", "BLOWS", "CHAOTIC", "VAMPIRIC", "SLAY_ANIMAL", "SLAY_EVIL", "SLAY_UNDEAD", "SLAY_DEMON", "SLAY_ORC", "SLAY_TROLL", "SLAY_GIANT", "SLAY_DRAGON", "KILL_DRAGON", "VORPAL", "IMPACT", "BRAND_POIS", "BRAND_ACID", "BRAND_ELEC", "BRAND_FIRE", "BRAND_COLD" }; /* * Object flags */ static cptr k_info_flags2[] = { "SUST_STR", "SUST_INT", "SUST_WIS", "SUST_DEX", "SUST_CON", "SUST_CHR", "XXX1", "IM_POIS", "IM_ACID", "IM_ELEC", "IM_FIRE", "IM_COLD", "THROW", "REFLECT", "FREE_ACT", "HOLD_LIFE", "RES_ACID", "RES_ELEC", "RES_FIRE", "RES_COLD", "RES_POIS", "RES_FEAR", "RES_LITE", "RES_DARK", "RES_BLIND", "RES_CONF", "RES_SOUND", "RES_SHARDS", "RES_NETHER", "RES_NEXUS", "RES_CHAOS", "RES_DISEN" }; /* * Object flags */ static cptr k_info_flags3[] = { "SH_FIRE", "SH_ELEC", "QUESTITEM", "XXX4", "NO_TELE", "NO_MAGIC", "XXX7", "TY_CURSE", "EASY_KNOW", "HIDE_TYPE", "SHOW_MODS", "INSTA_ART", "FEATHER", "LITE", "SEE_INVIS", "TELEPATHY", "SLOW_DIGEST", "REGEN", "XTRA_MIGHT", "XTRA_SHOTS", "IGNORE_ACID", "IGNORE_ELEC", "IGNORE_FIRE", "IGNORE_COLD", "ACTIVATE", "DRAIN_EXP", "TELEPORT", "AGGRAVATE", "BLESSED", "CURSED", "HEAVY_CURSE", "PERMA_CURSE" }; static cptr k_info_flags4[] = { "LUCK_10", "WILD_SHOT", "WILD_WALK", "EASY_ENCHANT", "XXX5", "XXX6", "XXX7", "XXX8", "IM_LITE", "IM_DARK", "SH_ACID", "SH_COLD", "MUTATE", "PATRON", "STRANGE_LUCK", "PASS_WALL", "GHOUL_TOUCH", "PSI_CRIT", "RETURN", "EXPLODE", "HURT_ACID", "HURT_ELEC", "HURT_FIRE", "HURT_COLD", "HURT_LITE", "HURT_DARK", "XXX27", "XXX28", "AUTO_CURSE", "DRAIN_STATS", "CANT_EAT", "SLOW_HEAL" }; /* * Wilderness Flags */ static cptr w_info_flags[] = { "FOREST1", "FOREST2", "MOUNT1", "MOUNT2", "WASTE1", "WASTE2", "SWAMP1", "SWAMP2" }; /* * Field info-flags */ static cptr t_info_flags[] = { "TEMP", "FEAT", "VIS", "MARK", "TRANS", "NO_LOOK", "NFT_LOOK", "MERGE", "NO_ENTER", "NO_MAGIC", "NO_OBJECT", "PERM", "IGNORE", "NO_MPLACE", "XXX13", "XXX14" }; /* * Object script triggers */ static cptr t_info_triggers[] = { "INIT", "LOAD", "PENTER", "PON", "MENTER", "MON", "OBDROP", "OBON", "INTER", "TARGET", "LOOK", "EXIT", "AI", "SPEC", "INTERT", "MENTT", "BUILD1", "BUILD2", "STORE1", "STORE2", "SBINIT", NULL }; /*** Initialize from ascii template files ***/ /* * Initialize an "*_info" array, by parsing an ascii "template" file */ errr init_info_txt(FILE *fp, char *buf, header *head, parse_info_txt_func parse_info_txt_line) { errr err; /* Not ready yet */ bool okay = FALSE; /* Just before the first record */ error_idx = -1; /* Just before the first line */ error_line = 0; /* Prepare the "fake" stuff */ head->name_size = 0; head->text_size = 0; /* Parse */ while (0 == my_fgets(fp, buf, 1024)) { /* Advance the line number */ error_line++; /* Skip comments and blank lines */ if (!buf[0] || (buf[0] == '#')) continue; /* Verify correct "colon" format */ if (buf[1] != ':') return (PARSE_ERROR_GENERIC); /* Hack -- Process 'V' for "Version" */ if (buf[0] == 'V') { int v1, v2, v3; /* Scan for the values */ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) || (v1 != head->v_major) || (v2 != head->v_minor) || (v3 != head->v_patch)) { return (PARSE_ERROR_OBSOLETE_FILE); } /* Okay to proceed */ okay = TRUE; /* Continue */ continue; } /* No version yet */ if (!okay) return (PARSE_ERROR_OBSOLETE_FILE); /* Parse the line */ if ((err = (*parse_info_txt_line) (buf, head)) != 0) return (err); } /* Complete the "name" and "text" sizes */ if (head->name_size) head->name_size++; if (head->text_size) head->text_size++; /* No version yet */ if (!okay) return (PARSE_ERROR_OBSOLETE_FILE); /* Success */ return (0); } /* * Add a text to the text-storage and store offset to it. * * Returns FALSE when there isn't enough space available to store * the text. */ static bool add_text(u32b *offset, header *head, cptr buf) { /* Hack -- Verify space */ if (head->text_size + strlen(buf) + 8 > z_info->fake_text_size) return (FALSE); /* New text? */ if (*offset == 0) { /* Advance and save the text index */ *offset = ++head->text_size; } /* Append chars to the text */ strcpy(head->text_ptr + head->text_size, buf); /* Advance the index */ head->text_size += strlen(buf); /* Success */ return (TRUE); } /* * Add a name to the name-storage and return an offset to it. * * Returns 0 when there isn't enough space available to store * the name. */ static u32b add_name(header *head, cptr buf) { u32b index; /* Hack -- Verify space */ if (head->name_size + strlen(buf) + 8 > z_info->fake_name_size) return (0); /* Advance and save the name index */ index = ++head->name_size; /* Append chars to the names */ strcpy(head->name_ptr + head->name_size, buf); /* Advance the index */ head->name_size += strlen(buf); /* Return the name index */ return (index); } /* * Initialize the "z_info" structure, by parsing an ascii "template" file */ errr parse_z_info(char *buf, header *head) { /* Unused parameter */ (void)head; /* Hack - Verify 'M:x:' format */ if (buf[0] != 'M') return (PARSE_ERROR_UNDEFINED_DIRECTIVE); if (!buf[2]) return (PARSE_ERROR_UNDEFINED_DIRECTIVE); if (buf[3] != ':') return (PARSE_ERROR_UNDEFINED_DIRECTIVE); /* Process 'F' for "Maximum f_info[] index" */ if (buf[2] == 'F') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->f_max = max; } /* Process 'K' for "Maximum k_info[] index" */ else if (buf[2] == 'K') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->k_max = max; } /* Process 'A' for "Maximum a_info[] index" */ else if (buf[2] == 'A') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->a_max = max; } /* Process 'E' for "Maximum e_info[] index" */ else if (buf[2] == 'E') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->e_max = max; } /* Process 'R' for "Maximum r_info[] index" */ else if (buf[2] == 'R') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->r_max = max; } /* Process 'V' for "Maximum v_info[] index" */ else if (buf[2] == 'V') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->v_max = max; } /* Process 'O' for "Maximum o_list[] index" */ else if (buf[2] == 'O') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->o_max = max; } /* Process 'M' for "Maximum m_list[] index" */ else if (buf[2] == 'M') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->m_max = max; } /* Process 'N' for "Fake name size" */ else if (buf[2] == 'N') { long max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%ld", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->fake_name_size = max; } /* Process 'T' for "Fake text size" */ else if (buf[2] == 'T') { long max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%ld", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->fake_text_size = max; } /* Process 'Q' for "Maximum number of quests" */ else if (buf[2] == 'Q') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->q_max = max; } /* Process 'U' for "Maximum number of field types" */ else if (buf[2] == 'U') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->t_max = max; } /* Process 'D' for "Maximum field list" */ else if (buf[2] == 'D') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->fld_max = max; } /* Process 'G' for "Maximum region list" */ else if (buf[2] == 'G') { int max; /* Scan for the value */ if (1 != sscanf(buf + 4, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->rg_max = max; } /* Process 'W' for "Maximum wilderness values" */ else if (buf[2] == 'W') { /* Hack - Verify 'M:W:x:' format */ if (buf[5] != ':') return (PARSE_ERROR_UNDEFINED_DIRECTIVE); /* Process 'N' for "Maximum wilderness tree nodes" */ if (buf[4] == 'N') { int max; /* Scan for the value */ if (1 != sscanf(buf + 6, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->wn_max = max; } /* Process 'T' for "Maximum wilderness gen types" */ else if (buf[4] == 'T') { int max; /* Scan for the value */ if (1 != sscanf(buf + 6, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->wt_max = max; } /* Process 'P' for "Maximum towns" */ else if (buf[4] == 'P') { int max; /* Scan for the value */ if (1 != sscanf(buf + 6, "%d", &max)) return (PARSE_ERROR_GENERIC); /* Save the value */ z_info->wp_max = max; } } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Initialize the "v_info" array, by parsing an ascii "template" file */ errr parse_v_info(char *buf, header *head) { int i; char *s; /* Current entry */ static vault_type *v_ptr = NULL; /* Process 'N' for "New/Number/Name" */ if (buf[0] == 'N') { /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Verify information */ if (i >= head->info_num) return (PARSE_ERROR_TOO_MANY_ENTRIES); /* Save the index */ error_idx = i; /* Point at the "info" */ v_ptr = &v_info[i]; /* Store the name */ if (!(v_ptr->name = add_name(head, s))) return (PARSE_ERROR_OUT_OF_MEMORY); } /* Process 'D' for "Description" */ else if (buf[0] == 'D') { /* There better be a current v_ptr */ if (!v_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Get the text */ s = buf + 2; /* Store the text */ if (!add_text(&v_ptr->text, head, s)) return (PARSE_ERROR_OUT_OF_MEMORY); } /* Process 'X' for "Extra info" (one line only) */ else if (buf[0] == 'X') { int typ, rat, hgt, wid; /* There better be a current v_ptr */ if (!v_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (4 != sscanf(buf + 2, "%d:%d:%d:%d", &typ, &rat, &hgt, &wid)) return (PARSE_ERROR_GENERIC); /* Save the values */ v_ptr->typ = typ; v_ptr->rat = rat; v_ptr->hgt = hgt; v_ptr->wid = wid; } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one flag from a textual string */ static errr grab_one_feat_flag(byte *flags, cptr names[], cptr what) { int i; /* Check flags */ for (i = 0; i < 8; i++) { if (streq(what, names[i])) { *flags |= (1L << i); return (0); } } return (-1); } /* * Initialize the "f_info" array, by parsing an ascii "template" file */ errr parse_f_info(char *buf, header *head) { int i; char *s, *t; /* Current entry */ static feature_type *f_ptr = NULL; /* Process 'N' for "New/Number/Name" */ if (buf[0] == 'N') { /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Verify information */ if (i >= head->info_num) return (PARSE_ERROR_TOO_MANY_ENTRIES); /* Save the index */ error_idx = i; /* Point at the "info" */ f_ptr = &f_info[i]; /* Store the name */ if (!(f_ptr->name = add_name(head, s))) return (PARSE_ERROR_OUT_OF_MEMORY); } /* Process 'G' for "Graphics" (one line only) */ else if (buf[0] == 'G') { int tmp; /* There better be a current f_ptr */ if (!f_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Paranoia */ if (!buf[2]) return (PARSE_ERROR_GENERIC); if (!buf[3]) return (PARSE_ERROR_GENERIC); if (!buf[4]) return (PARSE_ERROR_GENERIC); /* Extract the attr */ tmp = color_char_to_attr(buf[4]); /* Paranoia */ if (tmp < 0) return (PARSE_ERROR_GENERIC); /* Save the values */ f_ptr->d_attr = tmp; f_ptr->d_char = buf[2]; } else if (buf[0] == 'F') { /* There better be a current f_ptr */ if (!f_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Parse every entry textually */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while (*t == ' ' || *t == '|') t++; } /* Parse this entry */ if (0 != grab_one_feat_flag(&f_ptr->flags, f_info_flags, s)) { return (PARSE_ERROR_INVALID_FLAG); } /* Start the next entry */ s = t; } } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one flag from a textual string */ static errr grab_one_flag(u32b *flags, cptr names[], cptr what) { int i; /* Check flags */ for (i = 0; i < 32; i++) { if (streq(what, names[i])) { *flags |= (1L << i); return (0); } } return (-1); } /* * Grab one flag in an object_kind from a textual string */ static errr grab_one_kind_flag(object_kind *k_ptr, cptr what) { if (grab_one_flag(&k_ptr->flags[0], k_info_flags1, what) == 0) return (0); if (grab_one_flag(&k_ptr->flags[1], k_info_flags2, what) == 0) return (0); if (grab_one_flag(&k_ptr->flags[2], k_info_flags3, what) == 0) return (0); if (grab_one_flag(&k_ptr->flags[3], k_info_flags4, what) == 0) return (0); /* Oops */ msgf("Unknown object flag '%s'.", what); /* Error */ return (PARSE_ERROR_GENERIC); } /* * Initialize the "k_info" array, by parsing an ascii "template" file */ errr parse_k_info(char *buf, header *head) { int i; char *s, *t; /* Current entry */ static object_kind *k_ptr = NULL; /* Process 'N' for "New/Number/Name" */ if (buf[0] == 'N') { /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Verify information */ if (i >= head->info_num) return (PARSE_ERROR_TOO_MANY_ENTRIES); /* Save the index */ error_idx = i; /* Point at the "info" */ k_ptr = &k_info[i]; /* Store the name */ if (!(k_ptr->name = add_name(head, s))) return (PARSE_ERROR_OUT_OF_MEMORY); } /* Process 'G' for "Graphics" (one line only) */ else if (buf[0] == 'G') { char sym; int tmp; /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Paranoia */ if (!buf[2]) return (PARSE_ERROR_GENERIC); if (!buf[3]) return (PARSE_ERROR_GENERIC); if (!buf[4]) return (PARSE_ERROR_GENERIC); /* Extract the char */ sym = buf[2]; /* Extract the attr */ tmp = color_char_to_attr(buf[4]); /* Paranoia */ if (tmp < 0) return (PARSE_ERROR_GENERIC); /* Save the values */ k_ptr->d_attr = tmp; k_ptr->d_char = sym; } /* Process 'I' for "Info" (one line only) */ else if (buf[0] == 'I') { int tval, sval, pval; /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (3 != sscanf(buf + 2, "%d:%d:%d", &tval, &sval, &pval)) return (PARSE_ERROR_GENERIC); /* Save the values */ k_ptr->tval = tval; k_ptr->sval = sval; k_ptr->pval = pval; } /* Process 'W' for "More Info" (one line only) */ else if (buf[0] == 'W') { int level, extra, wgt; long cost; /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld", &level, &extra, &wgt, &cost)) return (PARSE_ERROR_GENERIC); /* Save the values */ k_ptr->level = level; k_ptr->extra = extra; k_ptr->weight = wgt; k_ptr->cost = cost; } /* Process 'A' for "Allocation" (one line only) */ else if (buf[0] == 'A') { int i; /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* XXX Simply read each number following a colon */ for (i = 0, s = buf + 1; s && (s[0] == ':') && s[1]; ++i) { /* Sanity check */ if (i > 3) return (PARSE_ERROR_TOO_MANY_ALLOCATIONS); /* Default chance */ k_ptr->chance[i] = 1; /* Store the attack damage index */ k_ptr->locale[i] = atoi(s + 1); /* Find the slash */ t = strchr(s + 1, '/'); /* Find the next colon */ s = strchr(s + 1, ':'); /* If the slash is "nearby", use it */ if (t && (!s || t < s)) { int chance = atoi(t + 1); if (chance > 0) k_ptr->chance[i] = chance; } } } /* Hack -- Process 'P' for "power" and such */ else if (buf[0] == 'P') { int ac, hd1, hd2, th, td, ta; /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d", &ac, &hd1, &hd2, &th, &td, &ta)) return (PARSE_ERROR_GENERIC); k_ptr->ac = ac; k_ptr->dd = hd1; k_ptr->ds = hd2; k_ptr->to_h = th; k_ptr->to_d = td; k_ptr->to_a = ta; } /* Hack -- Process 'F' for flags */ else if (buf[0] == 'F') { /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Parse every entry textually */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while (*t == ' ' || *t == '|') t++; } /* Parse this entry */ if (0 != grab_one_kind_flag(k_ptr, s)) { return (PARSE_ERROR_INVALID_FLAG); } /* Start the next entry */ s = t; } } /* Process 'D' for "Description" */ else if (buf[0] == 'D') { /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Get the text */ s = buf + 2; /* Store the text */ if (!add_text(&(k_ptr->text), head, s)) { msgf("Icky Description!!"); message_flush(); return (PARSE_ERROR_OUT_OF_MEMORY); } } /* Process 'L' for "Lua script" */ else if (buf[0] == 'L') { int n; /* There better be a current k_ptr */ if (!k_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Analyze the first field */ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == ':') *t++ = '\0'; /* Analyze the trigger */ for (n = 0; k_info_triggers[n]; n++) { if (streq(s, k_info_triggers[n])) break; } /* Invalid trigger */ if (!k_info_triggers[n]) return (PARSE_ERROR_GENERIC); /* Get the text */ s = t; /* Store the text */ if (!add_text(&(k_ptr->trigger[n]), head, s)) { msgf("Icky Trigger!!"); message_flush(); return (PARSE_ERROR_OUT_OF_MEMORY); } } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one flag in an artifact_type from a textual string */ static errr grab_one_artifact_flag(artifact_type *a_ptr, cptr what) { if (grab_one_flag(&a_ptr->flags[0], k_info_flags1, what) == 0) return (0); if (grab_one_flag(&a_ptr->flags[1], k_info_flags2, what) == 0) return (0); if (grab_one_flag(&a_ptr->flags[2], k_info_flags3, what) == 0) return (0); if (grab_one_flag(&a_ptr->flags[3], k_info_flags4, what) == 0) return (0); /* Oops */ msgf("Unknown artifact flag '%s'.", what); /* Error */ return (PARSE_ERROR_GENERIC); } /* * Initialize the "a_info" array, by parsing an ascii "template" file */ errr parse_a_info(char *buf, header *head) { int i; char *s, *t; /* Current entry */ static artifact_type *a_ptr = NULL; /* Process 'N' for "New/Number/Name" */ if (buf[0] == 'N') { /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Verify information */ if (i >= head->info_num) return (PARSE_ERROR_TOO_MANY_ENTRIES); /* Save the index */ error_idx = i; /* Point at the "info" */ a_ptr = &a_info[i]; /* Store the name */ if (!(a_ptr->name = add_name(head, s))) return (PARSE_ERROR_OUT_OF_MEMORY); /* Ignore everything */ SET_FLAG(a_ptr, TR_IGNORE_MASK); } /* Process 'I' for "Info" (one line only) */ else if (buf[0] == 'I') { int tval, sval, pval; /* There better be a current a_ptr */ if (!a_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (3 != sscanf(buf + 2, "%d:%d:%d", &tval, &sval, &pval)) return (PARSE_ERROR_GENERIC); /* Save the values */ a_ptr->tval = tval; a_ptr->sval = sval; a_ptr->pval = pval; } /* Process 'W' for "More Info" (one line only) */ else if (buf[0] == 'W') { int level, rarity, wgt; long cost; /* There better be a current a_ptr */ if (!a_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld", &level, &rarity, &wgt, &cost)) return (PARSE_ERROR_GENERIC); /* Save the values */ a_ptr->level = level; a_ptr->rarity = rarity; a_ptr->weight = wgt; a_ptr->cost = cost; } /* Process 'P' for "power" and such */ else if (buf[0] == 'P') { int ac, hd1, hd2, th, td, ta; /* There better be a current a_ptr */ if (!a_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d", &ac, &hd1, &hd2, &th, &td, &ta)) return (PARSE_ERROR_GENERIC); a_ptr->ac = ac; a_ptr->dd = hd1; a_ptr->ds = hd2; a_ptr->to_h = th; a_ptr->to_d = td; a_ptr->to_a = ta; } /* Process 'F' for flags */ else if (buf[0] == 'F') { /* There better be a current a_ptr */ if (!a_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Parse every entry textually */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while ((*t == ' ') || (*t == '|')) t++; } /* Parse this entry */ if (0 != grab_one_artifact_flag(a_ptr, s)) return (PARSE_ERROR_INVALID_FLAG); /* Start the next entry */ s = t; } } /* Process 'D' for "Description" */ else if (buf[0] == 'D') { /* There better be a current k_ptr */ if (!a_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Get the text */ s = buf + 2; /* Store the text */ if (!add_text(&(a_ptr->text), head, s)) { msgf("Icky Description!!"); message_flush(); return (PARSE_ERROR_OUT_OF_MEMORY); } } /* Process 'L' for "Lua script" */ else if (buf[0] == 'L') { int n; /* There better be a current a_ptr */ if (!a_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Analyze the first field */ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == ':') *t++ = '\0'; /* Analyze the trigger */ for (n = 0; k_info_triggers[n]; n++) { if (streq(s, k_info_triggers[n])) break; } /* Invalid trigger */ if (!k_info_triggers[n]) return (PARSE_ERROR_GENERIC); /* Get the text */ s = t; /* Store the text */ if (!add_text(&(a_ptr->trigger[n]), head, s)) { msgf("Icky Trigger!!"); message_flush(); return (PARSE_ERROR_OUT_OF_MEMORY); } } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one flag in a ego-item_type from a textual string */ static bool grab_one_ego_item_flag(ego_item_type *e_ptr, cptr what) { if (grab_one_flag(&e_ptr->flags[0], k_info_flags1, what) == 0) return (0); if (grab_one_flag(&e_ptr->flags[1], k_info_flags2, what) == 0) return (0); if (grab_one_flag(&e_ptr->flags[2], k_info_flags3, what) == 0) return (0); if (grab_one_flag(&e_ptr->flags[3], k_info_flags4, what) == 0) return (0); /* Oops */ msgf("Unknown ego-item flag '%s'.", what); /* Error */ return (PARSE_ERROR_GENERIC); } /* * Initialize the "e_info" array, by parsing an ascii "template" file */ errr parse_e_info(char *buf, header *head) { int i; char *s, *t; /* Current entry */ static ego_item_type *e_ptr = NULL; static int cur_t = 0; /* Process 'N' for "New/Number/Name" */ if (buf[0] == 'N') { /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Verify information */ if (i >= head->info_num) return (PARSE_ERROR_TOO_MANY_ENTRIES); /* Save the index */ error_idx = i; /* Point at the "info" */ e_ptr = &e_info[i]; /* Store the name */ if (!(e_ptr->name = add_name(head, s))) return (PARSE_ERROR_OUT_OF_MEMORY); /* Start with the first of the tval indices */ cur_t = 0; } /* Process 'W' for "More Info" (one line only) */ else if (buf[0] == 'W') { int level, rarity, pad2; long cost; /* There better be a current e_ptr */ if (!e_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld", &level, &rarity, &pad2, &cost)) return (PARSE_ERROR_GENERIC); /* Save the values */ e_ptr->level = level; e_ptr->rarity = rarity; /* e_ptr->weight = wgt; */ e_ptr->cost = cost; } /* Process 'X' for "Xtra" (one line only) */ else if (buf[0] == 'X') { int slot, rating; /* There better be a current e_ptr */ if (!e_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (2 != sscanf(buf + 2, "%d:%d", &slot, &rating)) return (PARSE_ERROR_GENERIC); /* Save the values */ e_ptr->slot = slot; e_ptr->rating = rating; } /* Hack -- Process 'C' for "creation" */ else if (buf[0] == 'C') { int th, td, ta, pv; /* There better be a current e_ptr */ if (!e_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (4 != sscanf(buf + 2, "%d:%d:%d:%d", &th, &td, &ta, &pv)) return (PARSE_ERROR_GENERIC); e_ptr->max_to_h = th; e_ptr->max_to_d = td; e_ptr->max_to_a = ta; e_ptr->max_pval = pv; } /* Hack -- Process 'F' for flags */ else if (buf[0] == 'F') { /* There better be a current e_ptr */ if (!e_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Parse every entry textually */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while ((*t == ' ') || (*t == '|')) t++; } /* Parse this entry */ if (0 != grab_one_ego_item_flag(e_ptr, s)) return (PARSE_ERROR_INVALID_FLAG); /* Start the next entry */ s = t; } } /* Process 'L' for "Lua script" */ else if (buf[0] == 'L') { int n; /* There better be a current e_ptr */ if (!e_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Analyze the first field */ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == ':') *t++ = '\0'; /* Analyze the trigger */ for (n = 0; k_info_triggers[n]; n++) { if (streq(s, k_info_triggers[n])) break; } /* Invalid trigger */ if (!k_info_triggers[n]) return (PARSE_ERROR_GENERIC); /* Get the text */ s = t; /* Store the text */ if (!add_text(&(e_ptr->trigger[n]), head, s)) { msgf("Icky Trigger!!"); message_flush(); return (PARSE_ERROR_OUT_OF_MEMORY); } } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one (basic) flag in a monster_race from a textual string */ static errr grab_one_basic_flag(monster_race *r_ptr, cptr what) { if (grab_one_flag(&r_ptr->flags[0], r_info_flags1, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[1], r_info_flags2, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[2], r_info_flags3, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[6], r_info_flags7, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[7], r_info_flags8, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[8], r_info_flags9, what) == 0) return (0); /* Oops */ msgf("Unknown monster flag '%s'.", what); /* Failure */ return (PARSE_ERROR_GENERIC); } /* * Grab one (spell) flag in a monster_race from a textual string */ static errr grab_one_spell_flag(monster_race *r_ptr, cptr what) { if (grab_one_flag(&r_ptr->flags[3], r_info_flags4, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[4], r_info_flags5, what) == 0) return (0); if (grab_one_flag(&r_ptr->flags[5], r_info_flags6, what) == 0) return (0); /* Oops */ msgf("Unknown monster flag '%s'.", what); /* Failure */ return (PARSE_ERROR_GENERIC); } /* * Initialize the "r_info" array, by parsing an ascii "template" file */ errr parse_r_info(char *buf, header *head) { int i; char *s, *t; /* Current entry */ static monster_race *r_ptr = NULL; /* Process 'N' for "New/Number/Name" */ if (buf[0] == 'N') { /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Verify information */ if (i >= head->info_num) return (PARSE_ERROR_TOO_MANY_ENTRIES); /* Save the index */ error_idx = i; /* Point at the "info" */ r_ptr = &r_info[i]; /* Store the name */ if (!(r_ptr->name = add_name(head, s))) return (PARSE_ERROR_OUT_OF_MEMORY); } /* Process 'D' for "Description" */ else if (buf[0] == 'D') { /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Get the text */ s = buf + 2; /* Store the text */ if (!add_text(&(r_ptr->text), head, s)) { msgf("Icky Description!!"); message_flush(); return (PARSE_ERROR_OUT_OF_MEMORY); } } /* Process 'G' for "Graphics" (one line only) */ else if (buf[0] == 'G') { char sym; int tmp; /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Paranoia */ if (!buf[2]) return (PARSE_ERROR_GENERIC); if (!buf[3]) return (PARSE_ERROR_GENERIC); if (!buf[4]) return (PARSE_ERROR_GENERIC); /* Extract the char */ sym = buf[2]; /* Extract the attr */ tmp = color_char_to_attr(buf[4]); /* Paranoia */ if (tmp < 0) return (PARSE_ERROR_GENERIC); /* Save the values */ r_ptr->d_attr = tmp; r_ptr->d_char = sym; } /* Process 'I' for "Info" (one line only) */ else if (buf[0] == 'I') { int spd, hp1, hp2, aaf, ac, slp; /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the other values */ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d", &spd, &hp1, &hp2, &aaf, &ac, &slp)) return (PARSE_ERROR_GENERIC); /* Save the values */ r_ptr->speed = spd; r_ptr->hdice = hp1; r_ptr->hside = hp2; r_ptr->aaf = aaf; r_ptr->ac = ac; r_ptr->sleep = slp; } /* Process 'W' for "More Info" (one line only) */ else if (buf[0] == 'W') { int lev, rar, pad; long exp; /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld", &lev, &rar, &pad, &exp)) return (PARSE_ERROR_GENERIC); /* Save the values */ r_ptr->level = lev; r_ptr->rarity = rar; r_ptr->extra = pad; r_ptr->mexp = exp; } /* Process 'O' for "Object theme" (one line only) */ else if (buf[0] == 'O') { int treasure, combat, magic; /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Scan for the values */ if (3 != sscanf(buf + 2, "%d:%d:%d", &treasure, &combat, &magic)) return (PARSE_ERROR_GENERIC); /* Save the values */ r_ptr->obj_drop.treasure = (byte)treasure; r_ptr->obj_drop.combat = (byte)combat; r_ptr->obj_drop.magic = (byte)magic; /* * Since monsters do not drop junk, * this value is defined in terms of the others */ r_ptr->obj_drop.tools = 100 - (treasure + combat + magic); } /* Process 'B' for "Blows" (up to four lines) */ else if (buf[0] == 'B') { int n1, n2; /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Find the next empty blow slot (if any) */ for (i = 0; i < 4; i++) if (!r_ptr->blow[i].method) break; /* Oops, no more slots */ if (i == 4) return (PARSE_ERROR_GENERIC); /* Analyze the first field */ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == ':') *t++ = '\0'; /* Analyze the method */ for (n1 = 0; r_info_blow_method[n1]; n1++) { if (streq(s, r_info_blow_method[n1])) break; } /* Invalid method */ if (!r_info_blow_method[n1]) return (PARSE_ERROR_GENERIC); /* Analyze the second field */ for (s = t; *t && (*t != ':'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == ':') *t++ = '\0'; /* Analyze effect */ for (n2 = 0; r_info_blow_effect[n2]; n2++) { if (streq(s, r_info_blow_effect[n2])) break; } /* Invalid effect */ if (!r_info_blow_effect[n2]) return (PARSE_ERROR_GENERIC); /* Analyze the third field */ for (s = t; *t && (*t != 'd'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == 'd') *t++ = '\0'; /* Save the method */ r_ptr->blow[i].method = n1; /* Save the effect */ r_ptr->blow[i].effect = n2; /* Extract the damage dice and sides */ r_ptr->blow[i].d_dice = atoi(s); r_ptr->blow[i].d_side = atoi(t); } /* Process 'F' for "Basic Flags" (multiple lines) */ else if (buf[0] == 'F') { /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Parse every entry */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while (*t == ' ' || *t == '|') t++; } /* Parse this entry */ if (0 != grab_one_basic_flag(r_ptr, s)) return (PARSE_ERROR_INVALID_FLAG); /* Start the next entry */ s = t; } } /* Process 'S' for "Spell Flags" (multiple lines) */ else if (buf[0] == 'S') { /* There better be a current r_ptr */ if (!r_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Parse every entry */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while ((*t == ' ') || (*t == '|')) t++; } /* XXX Hack -- Read spell frequency */ if (1 == sscanf(s, "1_IN_%d", &i)) { /* Sanity check */ if ((i < 1) || (i > 100)) return (PARSE_ERROR_INVALID_SPELL_FREQ); /* Extract a "frequency" */ r_ptr->freq_spell = r_ptr->freq_inate = 100 / i; /* Start at next entry */ s = t; /* Continue */ continue; } /* Parse this entry */ if (0 != grab_one_spell_flag(r_ptr, s)) return (PARSE_ERROR_INVALID_FLAG); /* Start the next entry */ s = t; } } else { /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one flag in wild_type from a textual string */ static errr grab_one_wild_flag(wild_gen_data_type *w_ptr, cptr what) { int i; /* Check flags */ for (i = 0; i < 8; i++) { if (streq(what, w_info_flags[i])) { w_ptr->rough_type |= (1 << i); return (0); } } /* Oops */ msgf("Unknown wilderness flag '%s'.", what); /* Error */ return (PARSE_ERROR_GENERIC); } /* * Initialize the "wild_choice_tree" and "wild_gen_data" arrays, * by parsing an ascii "template" file */ errr init_w_info_txt(FILE *fp, char *buf) { char *s, *t; u16b i = 0; /* Bounding box of entry */ wild_bound_box_type bound; /* Current entry */ wild_gen_data_type *w_ptr = NULL; /* Just before the first line */ error_line = -1; /* The last index used */ error_idx = -1; /* Parse */ while (0 == my_fgets(fp, buf, 1024)) { /* Advance the line number */ error_line++; /* Skip comments and blank lines */ if (!buf[0] || (buf[0] == '#')) continue; /* Verify correct "colon" format */ if (buf[1] != ':') return (PARSE_ERROR_GENERIC); /* Process 'N' for "Number" (one line only) */ if (buf[0] == 'N') { /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Check to see if there is room in array */ if (i > z_info->wt_max - 1) return (PARSE_ERROR_OUT_OF_MEMORY); /* Save the index */ error_idx = i; /* point to new position in array */ w_ptr = &wild_gen_data[i]; continue; } /* There better be a current w_ptr */ if (!w_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Process 'M' for "Map" (one line only) */ if (buf[0] == 'M') { int tmp; if (1 != sscanf(buf + 2, "%d", &tmp)) return (PARSE_ERROR_GENERIC); /* Paranoia */ if ((tmp >= z_info->f_max) || (tmp < 0)) return (PARSE_ERROR_GENERIC); /* Save the value */ w_ptr->feat = (byte)tmp; /* Next... */ continue; } /* Process 'W' for "Wilderness Info" (one line only) */ if (buf[0] == 'W') { int hgtmin, hgtmax, popmin, popmax, lawmin, lawmax; /* Scan for the values */ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d", &hgtmin, &hgtmax, &popmin, &popmax, &lawmin, &lawmax)) return (PARSE_ERROR_GENERIC); /* Save the values into bounds */ bound.hgtmin = hgtmin; bound.hgtmax = hgtmax; bound.popmin = popmin; bound.popmax = popmax; bound.lawmin = lawmin; bound.lawmax = lawmax; /* Next... */ continue; } /* Process 'T' for "Type" (one line only) */ if (buf[0] == 'T') { int routine, chance; /* Scan for the values */ if (2 != sscanf(buf + 2, "%d:%d", &routine, &chance)) return (PARSE_ERROR_GENERIC); /* Save the values */ w_ptr->gen_routine = routine; w_ptr->chance = chance; /* Next... */ continue; } /* Process 'F' for "Basic Flags" (multiple lines) */ if (buf[0] == 'F') { /* Parse every entry */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while (*t == ' ' || *t == '|') t++; } /* Parse this entry */ if (0 != grab_one_wild_flag(w_ptr, s)) return (PARSE_ERROR_INVALID_FLAG); /* Start the next entry */ s = t; } /* Next... */ continue; } /* Process 'E' for "Extra Information" (one line only) */ if (buf[0] == 'E') { int d0, d1, d2, d3, d4, d5, d6, d7; /* Scan for the values */ if (8 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d", &d0, &d1, &d2, &d3, &d4, &d5, &d6, &d7)) return (PARSE_ERROR_GENERIC); /* Save the values */ w_ptr->data[0] = d0; w_ptr->data[1] = d1; w_ptr->data[2] = d2; w_ptr->data[3] = d3; w_ptr->data[4] = d4; w_ptr->data[5] = d5; w_ptr->data[6] = d6; w_ptr->data[7] = d7; /* Initialise if tree is empty */ if (i == 1) { /* Initialise */ (void)init_choice_tree(&bound, i); } else { /* Add type to decision tree */ if (add_node_tree_root(&bound, i) == 0) return (PARSE_ERROR_OUT_OF_MEMORY); } /* Next... */ continue; } /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } /* * Grab one info-flag from a textual string */ static errr grab_one_info_flag(field_thaum *t_ptr, cptr what) { int i; /* Check flags */ for (i = 0; i < 16; i++) { if (streq(what, t_info_flags[i])) { t_ptr->info |= (1 << i); return (0); } } /* Oops */ msgf("Unknown field info-flag '%s'.", what); /* Error */ return (PARSE_ERROR_GENERIC); } /* * Initialize the field "thaumatergical" arrays, * by parsing an ascii "template" file */ errr init_t_info_txt(FILE *fp, char *buf) { char *s, *t; u16b i = 0; /* Current entry */ field_thaum *t_ptr = NULL; /* Just before the first line */ error_line = -1; /* The last index used */ error_idx = -1; /* Parse */ while (0 == my_fgets(fp, buf, 1024)) { /* Advance the line number */ error_line++; /* Skip comments and blank lines */ if (!buf[0] || (buf[0] == '#')) continue; /* Verify correct "colon" format */ if (buf[1] != ':') return (PARSE_ERROR_GENERIC); /* Process 'N' for "Number" (one line only) */ if (buf[0] == 'N') { /* Length of name string */ u16b length; /* Get the index */ i = atoi(buf + 2); /* Verify information */ if (i <= error_idx) return (PARSE_ERROR_NON_SEQUENTIAL_RECORDS); /* Check to see if there is room in array */ if (i > z_info->t_max - 1) return (PARSE_ERROR_OUT_OF_MEMORY); /* Save the index */ error_idx = i; /* point to new position in array */ t_ptr = &t_info[i]; /* Find the colon before the name */ s = strchr(buf + 2, ':'); /* Verify that colon */ if (!s) return (PARSE_ERROR_GENERIC); /* Nuke the colon, advance to the name */ *s++ = '\0'; /* Paranoia -- require a name */ if (!*s) return (PARSE_ERROR_GENERIC); /* Name + /0 on the end */ length = strlen(s) + 1; /* Make some room */ C_MAKE(t, length, char); if (!t) return (PARSE_ERROR_OUT_OF_MEMORY); /* Out of memory */ /* Add the name */ t_ptr->name = strcpy(t, s); continue; } /* Process 'G' for "Graphics" (one line only) */ if (buf[0] == 'G') { int tmp; /* Paranoia */ if (!buf[2]) return (PARSE_ERROR_GENERIC); if (!buf[3]) return (PARSE_ERROR_GENERIC); if (!buf[4]) return (PARSE_ERROR_GENERIC); /* Extract the color */ tmp = color_char_to_attr(buf[4]); /* Paranoia */ if (tmp < 0) return (PARSE_ERROR_GENERIC); /* Save the default values */ t_ptr->d_attr = tmp; t_ptr->d_char = buf[2]; /* Next... */ continue; } /* Process 'W' for extra information (one line only) */ if (buf[0] == 'W') { int priority, type, counter; /* Scan for the values */ if (3 != sscanf(buf + 2, "%d:%d:%d", &priority, &type, &counter)) return (PARSE_ERROR_GENERIC); /* Save the values */ t_ptr->priority = (byte)priority; t_ptr->type = (byte)type; t_ptr->count_init = (s16b)counter; /* Next... */ continue; } /* Process 'I' for "Info Flags" (multiple lines) */ if (buf[0] == 'I') { /* Parse every entry */ for (s = buf + 2; *s;) { /* Find the end of this entry */ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */ ; /* Nuke and skip any dividers */ if (*t) { *t++ = '\0'; while (*t == ' ' || *t == '|') t++; } /* Parse this entry */ if (0 != grab_one_info_flag(t_ptr, s)) return (PARSE_ERROR_INVALID_FLAG); /* Start the next entry */ s = t; } /* Next... */ continue; } /* Process 'D' for "Data" (one line only) */ if (buf[0] == 'D') { int d0, d1, d2, d3, d4, d5, d6, d7; /* Scan for the values */ if (8 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d", &d0, &d1, &d2, &d3, &d4, &d5, &d6, &d7)) return (PARSE_ERROR_GENERIC); /* Save the values */ t_ptr->data_init[0] = d0; t_ptr->data_init[1] = d1; t_ptr->data_init[2] = d2; t_ptr->data_init[3] = d3; t_ptr->data_init[4] = d4; t_ptr->data_init[5] = d5; t_ptr->data_init[6] = d6; t_ptr->data_init[7] = d7; /* Next... */ continue; } /* Process 'L' for "Lua script" */ if (buf[0] == 'L') { int n; /* There better be a current t_ptr */ if (!t_ptr) return (PARSE_ERROR_MISSING_RECORD_HEADER); /* Analyze the first field */ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */ ; /* Terminate the field (if necessary) */ if (*t == ':') *t++ = '\0'; /* Analyze the trigger */ for (n = 0; t_info_triggers[n]; n++) { if (streq(s, t_info_triggers[n])) break; } /* Invalid trigger */ if (!t_info_triggers[n]) return (PARSE_ERROR_GENERIC); /* Get the text */ s = t; /* Store the text */ if (t_ptr->action[n]) { s16b old = t_ptr->action[n]; t_ptr->action[n] = quark_fmt("%s\n%s", quark_str(old), s); quark_remove(&old); } else { t_ptr->action[n] = quark_add(s); } /* Next... */ continue; } /* Oops */ return (PARSE_ERROR_UNDEFINED_DIRECTIVE); } /* Success */ return (0); } #endif /* ALLOW_TEMPLATES */ zangband/src/init2.c0000755000000000000000000010531310250356274013332 0ustar rootroot/* File: init2.c */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ #include "angband.h" #include "script.h" #include "init.h" #ifdef CHECK_MODIFICATION_TIME #ifndef RISCOS #ifdef MACINTOSH #include #else #include #include #endif /* MACINTOSH */ #endif /* !RISCOS */ #endif /* CHECK_MODIFICATION_TIME */ #ifdef HAVE_MMAP #include #endif /* * This file is used to initialize various variables and arrays for the * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass * the common limitation of "read()" and "write()" to only 32767 bytes * at a time. * * Several of the arrays for Angband are built from "template" files in * the "lib/file" directory, from which quick-load binary "image" files * are constructed whenever they are not present in the "lib/data" * directory, or if those files become obsolete, if we are allowed. * * Warning -- the "ascii" file parsers use a minor hack to collect the * name and text information in a single pass. Thus, the game will not * be able to load any template file with more than 20K of names or 60K * of text, even though technically, up to 64K should be legal. * * The "init1.c" file is used only to parse the ascii template files, * to create the binary image files. If you include the binary image * files instead of the ascii template files, then you can undefine * "ALLOW_TEMPLATES", saving about 20K by removing "init1.c". Note * that the binary image files are extremely system dependant. */ /* * Find the default paths to all of our important sub-directories. * * The purpose of each sub-directory is described in "variable.c". * * All of the sub-directories should, by default, be located inside * the main "lib" directory, whose location is very system dependant. * * This function takes a writable buffer, initially containing the * "path" to the "lib" directory, for example, "/pkg/lib/angband/", * or a system dependant string, for example, ":lib:". The buffer * must be large enough to contain at least 32 more characters. * * Various command line options may allow some of the important * directories to be changed to user-specified directories, most * importantly, the "info" and "user" and "save" directories, * but this is done after this function, see "main.c". * * In general, the initial path should end in the appropriate "PATH_SEP" * string. All of the "sub-directory" paths (created below or supplied * by the user) will NOT end in the "PATH_SEP" string, see the special * "path_build()" function in "util.c" for more information. (Note that * we call this via the path_make() macro in defines.h) * * Mega-Hack -- support fat raw files under NEXTSTEP, using special * "suffixed" directories for the "ANGBAND_DIR_DATA" directory, but * requiring the directories to be created by hand by the user. * * Hack -- first we free all the strings, since this is known * to succeed even if the strings have not been allocated yet, * as long as the variables start out as "NULL". This allows * this function to be called multiple times, for example, to * try several base "path" values until a good one is found. */ void init_file_paths(char *path) { char *tail; #ifdef PRIVATE_USER_PATH char buf[1024]; #endif /* PRIVATE_USER_PATH */ /*** Free everything ***/ /* Free the main path */ string_free(ANGBAND_DIR); /* Free the sub-paths */ string_free(ANGBAND_DIR_APEX); string_free(ANGBAND_DIR_BONE); string_free(ANGBAND_DIR_DATA); string_free(ANGBAND_DIR_EDIT); string_free(ANGBAND_DIR_SCRIPT); string_free(ANGBAND_DIR_FILE); string_free(ANGBAND_DIR_HELP); string_free(ANGBAND_DIR_INFO); string_free(ANGBAND_DIR_SAVE); string_free(ANGBAND_DIR_PREF); string_free(ANGBAND_DIR_USER); string_free(ANGBAND_DIR_XTRA); /*** Prepare the "path" ***/ /* Hack -- save the main directory */ ANGBAND_DIR = string_make(path); /* Prepare to append to the Base Path */ tail = path + strlen(path); #ifdef VM /*** Use "flat" paths with VM/ESA ***/ /* Use "blank" path names */ ANGBAND_DIR_APEX = string_make(""); ANGBAND_DIR_BONE = string_make(""); ANGBAND_DIR_DATA = string_make(""); ANGBAND_DIR_EDIT = string_make(""); ANGBAND_DIR_SCRIPT = string_make(""); ANGBAND_DIR_FILE = string_make(""); ANGBAND_DIR_HELP = string_make(""); ANGBAND_DIR_INFO = string_make(""); ANGBAND_DIR_SAVE = string_make(""); ANGBAND_DIR_PREF = string_make(""); ANGBAND_DIR_USER = string_make(""); ANGBAND_DIR_XTRA = string_make(""); #else /* VM */ /*** Build the sub-directory names ***/ /* Build a path name */ strcpy(tail, "edit"); ANGBAND_DIR_EDIT = string_make(path); /* Build a path name */ strcpy(tail, "script"); ANGBAND_DIR_SCRIPT = string_make(path); /* Build a path name */ strcpy(tail, "file"); ANGBAND_DIR_FILE = string_make(path); /* Build a path name */ strcpy(tail, "help"); ANGBAND_DIR_HELP = string_make(path); /* Build a path name */ strcpy(tail, "info"); ANGBAND_DIR_INFO = string_make(path); /* Build a path name */ strcpy(tail, "pref"); ANGBAND_DIR_PREF = string_make(path); #ifdef PRIVATE_USER_PATH /* Build the path to the user specific directory */ path_make(buf, PRIVATE_USER_PATH, VERSION_NAME); /* Build a relative path name */ ANGBAND_DIR_USER = string_make(buf); #else /* PRIVATE_USER_PATH */ /* Build a path name */ strcpy(tail, "user"); ANGBAND_DIR_USER = string_make(path); #endif /* PRIVATE_USER_PATH */ #ifdef USE_PRIVATE_PATHS /* Build a path name */ path_make(buf, ANGBAND_DIR_USER, "scores"); ANGBAND_DIR_APEX = string_make(buf); /* Build a path name */ path_make(buf, ANGBAND_DIR_USER, "bone"); ANGBAND_DIR_BONE = string_make(buf); /* Build a path name */ path_make(buf, ANGBAND_DIR_USER, "data"); ANGBAND_DIR_DATA = string_make(buf); /* Build a path name */ path_make(buf, ANGBAND_DIR_USER, "save"); ANGBAND_DIR_SAVE = string_make(buf); #else /* USE_PRIVATE_PATHS */ /* Build a path name */ strcpy(tail, "apex"); ANGBAND_DIR_APEX = string_make(path); /* Build a path name */ strcpy(tail, "bone"); ANGBAND_DIR_BONE = string_make(path); /* Build a path name */ strcpy(tail, "data"); ANGBAND_DIR_DATA = string_make(path); /* Build a path name */ strcpy(tail, "save"); ANGBAND_DIR_SAVE = string_make(path); #endif /* USE_PRIVATE_PATHS */ /* Build a path name */ strcpy(tail, "xtra"); ANGBAND_DIR_XTRA = string_make(path); #endif /* VM */ } #ifdef ALLOW_TEMPLATES /* * Hack -- help give useful error messages */ int error_idx; int error_line; /* * Standard error message text */ cptr err_str[PARSE_ERROR_MAX] = { NULL, "parse error", "obsolete file", "missing record header", "non-sequential records", "invalid flag specification", "undefined directive", "out of memory", "value out of bounds", "too few arguments", "too many arguments", "too many allocation entries", "invalid spell frequency", "invalid number of items (0-99)", "too many entries", }; #endif /* ALLOW_TEMPLATES */ #ifndef RISCOS #ifdef CHECK_MODIFICATION_TIME extern errr check_modification_date(int fd, cptr template_file) { char buf[1024]; struct stat txt_stat, raw_stat; /* Build the filename */ path_make(buf, ANGBAND_DIR_EDIT, template_file); /* Access stats on text file */ if (stat(buf, &txt_stat)) { /* No text file - continue */ } /* Access stats on raw file */ else if (fstat(fd, &raw_stat)) { /* Error */ return (-1); } /* Ensure text file is not newer than raw file */ else if (txt_stat.st_mtime > raw_stat.st_mtime) { /* Reprocess text file */ return (-1); } return (0); } #endif /* CHECK_MODIFICATION_TIME */ #endif /* !RISCOS */ /* * File headers */ header z_head; header v_head; header f_head; header k_head; header a_head; header e_head; header r_head; /*** Initialize from binary image files ***/ /* * Initialize a "*_info" array, by parsing a binary "image" file * * If possible, just mmap() the image file directory into memory. * This is faster than reading it from disk, because it delays * the loading until it's actually accessed. It may also save memory. */ static errr init_info_raw(int fd, header *head) { header test; #ifdef HAVE_MMAP char *data; #endif /* HAVE_MMAP */ /* Read and verify the header */ if (fd_read(fd, (char *)(&test), sizeof(header)) || (test.v_major != head->v_major) || (test.v_minor != head->v_minor) || (test.v_patch != head->v_patch) || (test.v_extra != head->v_extra) || (test.info_num != head->info_num) || (test.info_len != head->info_len) || (test.head_size != head->head_size) || (test.info_size != head->info_size)) { /* Error */ return (-1); } /* Accept the header */ COPY(head, &test, header); #ifdef HAVE_MMAP data = mmap(NULL, sizeof(header) + head->info_size + head->name_size + head->text_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (data != MAP_FAILED) { head->mmap_base = data; /* Skip the header */ data += sizeof(header); /* Save a pointer to the info */ head->info_ptr = data; data += head->info_size; /* Save a pointer to the names */ head->name_ptr = data; data += head->name_size; /* Save a pointer to the text */ head->text_ptr = data; } else { #endif /* HAVE_MMAP */ head->mmap_base = NULL; /* Allocate the "*_info" array */ C_MAKE(head->info_ptr, head->info_size, char); /* Read the "*_info" array */ fd_read(fd, head->info_ptr, head->info_size); if (head->name_size) { /* Allocate the "*_name" array */ C_MAKE(head->name_ptr, head->name_size, char); /* Read the "*_name" array */ fd_read(fd, head->name_ptr, head->name_size); } if (head->text_size) { /* Allocate the "*_text" array */ C_MAKE(head->text_ptr, head->text_size, char); /* Read the "*_text" array */ fd_read(fd, head->text_ptr, head->text_size); } #ifdef HAVE_MMAP } #endif /* HAVE_MMAP */ /* Success */ return (0); } /* * Initialize the header of an *_info.raw file. */ static void init_header(header *head, int num, int len) { /* Save the "version" */ head->v_major = VER_MAJOR; head->v_minor = VER_MINOR; head->v_patch = VER_PATCH; head->v_extra = VER_EXTRA; /* Save the "record" information */ head->info_num = num; head->info_len = len; /* Save the size of "*_head" and "*_info" */ head->head_size = sizeof(header); head->info_size = head->info_num * head->info_len; } #ifdef ALLOW_TEMPLATES /* * Display a parser error message. */ static void display_parse_error(cptr filename, errr err, cptr buf) { cptr oops; /* Error string */ oops = (((err > 0) && (err < PARSE_ERROR_MAX)) ? err_str[err] : "unknown"); /* Oops */ msgf("Error at line %d of '%s'.", error_line, filename); msgf("Record %d contains a '%s' error.", error_idx, oops); msgf("Parsing '%s'.", buf); message_flush(); /* Quit */ quit_fmt("Error in '%s.txt' file.", filename); } #endif /* ALLOW_TEMPLATES */ /* * Initialize a "*_info" array * * Note that we let each entry have a unique "name" and "text" string, * even if the string happens to be empty (everyone has a unique '\0'). */ static errr init_info(cptr filename, header *head, void **info, char **name, char **text) { int fd; errr err = 1; FILE *fp; /* General buffer */ char buf[1024]; #ifdef ALLOW_TEMPLATES /*** Load the binary image file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_DATA, format("%s.raw", filename)); /* Attempt to open the "raw" file */ fd = fd_open(buf, O_RDONLY); /* Process existing "raw" file */ if (fd >= 0) { #ifdef CHECK_MODIFICATION_TIME err = check_modification_date(fd, format("%s.txt", filename)); #endif /* CHECK_MODIFICATION_TIME */ /* Attempt to parse the "raw" file */ if (!err) err = init_info_raw(fd, head); /* Close it */ fd_close(fd); } /* Do we have to parse the *.txt file? */ if (err) { /*** Make the fake arrays ***/ /* Allocate the "*_info" array */ C_MAKE(head->info_ptr, head->info_size, char); /* Hack -- make "fake" arrays */ if (name) C_MAKE(head->name_ptr, z_info->fake_name_size, char); if (text) C_MAKE(head->text_ptr, z_info->fake_text_size, char); if (info) (*info) = head->info_ptr; if (name) (*name) = head->name_ptr; if (text) (*text) = head->text_ptr; /*** Load the ascii template file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_EDIT, format("%s.txt", filename)); /* Open the file */ fp = my_fopen(buf, "r"); /* Parse it */ if (!fp) quit_fmt("Cannot open '%s.txt' file.", filename); /* Parse the file */ err = init_info_txt(fp, buf, head, head->parse_info_txt); /* Close it */ my_fclose(fp); /* Errors */ if (err) display_parse_error(filename, err, buf); /*** Dump the binary image file ***/ /* File type is "DATA" */ FILE_TYPE(FILE_TYPE_DATA); /* Build the filename */ path_make(buf, ANGBAND_DIR_DATA, format("%s.raw", filename)); /* Attempt to open the file */ fd = fd_open(buf, O_RDONLY); /* Failure */ if (fd < 0) { int mode = 0644; /* Grab permissions */ safe_setuid_grab(); /* Create a new file */ fd = fd_make(buf, mode); /* Drop permissions */ safe_setuid_drop(); /* Failure */ if (fd < 0) { /* Crash and burn */ quit_fmt("Cannot create the '%s' file!", buf); } } /* Close it */ fd_close(fd); /* Grab permissions */ safe_setuid_grab(); /* Attempt to create the raw file */ fd = fd_open(buf, O_WRONLY); /* Drop permissions */ safe_setuid_drop(); /* Dump to the file */ if (fd >= 0) { /* Dump it */ fd_write(fd, (cptr)head, head->head_size); /* Dump the "*_info" array */ fd_write(fd, head->info_ptr, head->info_size); /* Dump the "*_name" array */ fd_write(fd, head->name_ptr, head->name_size); /* Dump the "*_text" array */ fd_write(fd, head->text_ptr, head->text_size); /* Close */ fd_close(fd); } /*** Kill the fake arrays ***/ /* Free the "*_info" array */ KILL(head->info_ptr); /* Hack -- Free the "fake" arrays */ if (name) KILL(head->name_ptr); if (text) KILL(head->text_ptr); #endif /* ALLOW_TEMPLATES */ /*** Load the binary image file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_DATA, format("%s.raw", filename)); /* Attempt to open the "raw" file */ fd = fd_open(buf, O_RDONLY); /* Process existing "raw" file */ if (fd < 0) quit_fmt("Cannot load '%s.raw' file.", filename); /* Attempt to parse the "raw" file */ err = init_info_raw(fd, head); /* Close it */ fd_close(fd); /* Error */ if (err) quit_fmt("Cannot parse '%s.raw' file.", filename); #ifdef ALLOW_TEMPLATES } #endif /* ALLOW_TEMPLATES */ if (info) (*info) = head->info_ptr; if (name) (*name) = head->name_ptr; if (text) (*text) = head->text_ptr; /* Success */ return (0); } /* * Free the allocated memory for the info-, name-, and text- arrays. */ static errr free_info(header *head) { #ifdef HAVE_MMAP if (head->mmap_base) { munmap(head->mmap_base, sizeof(header) + head->info_size + head->name_size + head->text_size); /* Success */ return (0); } #endif /* HAVE_MMAP */ if (head->info_size) FREE(head->info_ptr); if (head->name_size) FREE(head->name_ptr); if (head->text_size) FREE(head->text_ptr); /* Success */ return (0); } /* * Initialize the "z_info" array */ static errr init_z_info(void) { /* Init the header */ init_header(&z_head, 1, sizeof(maxima)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ z_head.parse_info_txt = parse_z_info; #endif /* ALLOW_TEMPLATES */ return init_info("misc", &z_head, (void *)&z_info, NULL, NULL); } /* * Initialize the "f_info" array */ static errr init_f_info(void) { /* Init the header */ init_header(&f_head, z_info->f_max, sizeof(feature_type)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ f_head.parse_info_txt = parse_f_info; #endif /* ALLOW_TEMPLATES */ return init_info("f_info", &f_head, (void *)&f_info, (void *)&f_name, (void *)&f_text); } /* * Initialize the "k_info" array */ static errr init_k_info(void) { /* Init the header */ init_header(&k_head, z_info->k_max, sizeof(object_kind)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ k_head.parse_info_txt = parse_k_info; #endif /* ALLOW_TEMPLATES */ return init_info("k_info", &k_head, (void *)&k_info, (void *)&k_name, (void *)&k_text); } /* * Initialize the "a_info" array */ static errr init_a_info(void) { /* Init the header */ init_header(&a_head, z_info->a_max, sizeof(artifact_type)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ a_head.parse_info_txt = parse_a_info; #endif /* ALLOW_TEMPLATES */ return init_info("a_info", &a_head, (void *)&a_info, (void *)&a_name, (void *)&a_text); } /* * Initialize the "e_info" array */ static errr init_e_info(void) { /* Init the header */ init_header(&e_head, z_info->e_max, sizeof(ego_item_type)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ e_head.parse_info_txt = parse_e_info; #endif /* ALLOW_TEMPLATES */ return init_info("e_info", &e_head, (void *)&e_info, (void *)&e_name, (void *)&e_text); } /* * Initialize the "r_info" array */ static errr init_r_info(void) { /* Init the header */ init_header(&r_head, z_info->r_max, sizeof(monster_race)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ r_head.parse_info_txt = parse_r_info; #endif /* ALLOW_TEMPLATES */ return init_info("r_info", &r_head, (void *)&r_info, (void *)&r_name, (void *)&r_text); } /* * Initialize the "v_info" array */ static errr init_v_info(void) { /* Init the header */ init_header(&v_head, z_info->v_max, sizeof(vault_type)); #ifdef ALLOW_TEMPLATES /* Save a pointer to the parsing function */ v_head.parse_info_txt = parse_v_info; #endif /* ALLOW_TEMPLATES */ return init_info("v_info", &v_head, (void *)&v_info, (void *)&v_name, (void *)&v_text); } /*** Initialize others ***/ /* * Initialize the "wild_choice_tree" array and the * "wild_gen_data" array. * */ errr init_w_info(void) { errr err; FILE *fp; /* General buffer */ char buf[1024]; /* Later must add in raw file support later. */ C_MAKE(wild_choice_tree, z_info->wn_max, wild_choice_tree_type); C_MAKE(wild_gen_data, z_info->wt_max, wild_gen_data_type); /*** Load the ascii template file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_EDIT, "w_info.txt"); /* Open the file */ fp = my_fopen(buf, "r"); /* Parse it */ if (!fp) quit("Cannot open 'w_info.txt' file."); /* Parse the file */ err = init_w_info_txt(fp, buf); /* Close it */ my_fclose(fp); /* Errors */ if (err) { cptr oops; /* Error string */ oops = (((err > 0) && (err < PARSE_ERROR_MAX)) ? err_str[err] : "unknown"); /* Oops */ msgf("Error %d at line %d of 'w_info.txt'.", err, error_line); msgf("Record %d contains a '%s' error.", error_idx, oops); msgf("Parsing '%s'.", buf); message_flush(); /* Quit */ quit("Error in 'w_info.txt' file."); } /* Success */ return (0); } /* * Initialize the field data structures */ errr init_t_info(void) { errr err; FILE *fp; /* General buffer */ char buf[1024]; /* Later must add in python support. */ C_MAKE(t_info, z_info->t_max, field_thaum); C_MAKE(fld_list, z_info->fld_max, field_type); /*** Load the ascii template file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_EDIT, "t_info.txt"); /* Open the file */ fp = my_fopen(buf, "r"); /* Parse it */ if (!fp) quit("Cannot open 't_info.txt' file."); /* Parse the file */ err = init_t_info_txt(fp, buf); /* Close it */ my_fclose(fp); /* Errors */ if (err) { cptr oops; /* Error string */ oops = (((err > 0) && (err < PARSE_ERROR_MAX)) ? err_str[err] : "unknown"); /* Oops */ msgf("Error %d at line %d of 't_info.txt'.", err, error_line); msgf("Record %d contains a '%s' error.", error_idx, oops); msgf("Parsing '%s'.", buf); message_flush(); /* Quit */ quit("Error in 't_info.txt' file."); } /* Success */ return (0); } /* * The list of available format functions * * (They should be in order of most-called * through to least-called.) */ static vstrnfmt_aux_func my_format_functions[9] = { set_message_type, object_fmt, object_store_fmt, monster_fmt, stat_format, center_string, likert, binary_fmt, NULL }; /* * Initialize some other arrays */ static errr init_other(void) { int i, j, k, n; /*** Pre-allocate space for the "format()" buffer ***/ /* Hack -- Just call the "format()" function */ (void)format("%s (%s).", "Steven Fuerst", MAINTAINER); /* Initialise the "%v" user-defined format function list */ register_format_funcs(my_format_functions); /*** Prepare the various "bizarre" arrays ***/ /* Macro variables */ C_MAKE(macro__pat, MACRO_MAX, cptr); C_MAKE(macro__act, MACRO_MAX, cptr); C_MAKE(macro__cmd, MACRO_MAX, bool); /* Macro action buffer */ C_MAKE(macro__buf, 1024, char); /* Clear the spell colour strings */ (void)C_WIPE(gf_color, MAX_GF, cptr); /* Initialize the "quark" package */ (void)quarks_init(); /* Initialize the "message" package */ (void)messages_init(); /*** Prepare region list ***/ C_MAKE(rg_list, z_info->rg_max, region_type); C_MAKE(ri_list, z_info->rg_max, region_info); /*** Hack - Allocate the player information for each grid ***/ for (i = 0; i < MAX_HGT; i++) { /* Allocate one row of the cave */ C_MAKE(p_ptr->pcave[i], MAX_WID, pcave_type); } /*** Prepare wilderness stuff ***/ /* Allocate temporary wilderness block */ for (i = 0; i < WILD_BLOCK_SIZE + 1; i++) { /* Allocate one row of the temp_block */ C_MAKE(temp_block[i], WILD_BLOCK_SIZE + 1, u16b); } /* Make the list of pointers to blocks */ C_MAKE(wild_cache, WILD_CACHE, blk_ptr); /* Allocate each block */ for (i = 0; i < WILD_CACHE; i++) { /* Allocate block */ C_MAKE(wild_cache[i], WILD_BLOCK_SIZE, cave_type *); /* Allocate rows of a block */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { C_MAKE(wild_cache[i][j], WILD_BLOCK_SIZE, cave_type); } } /* Allocate the player information for each grid (wilderness) */ /* Allocate WILD_VIEW by WILD_VIEW blocks */ C_MAKE(p_ptr->pwild, WILD_VIEW, pblk_ptr *); for (i = 0; i < WILD_VIEW; i++) { C_MAKE(p_ptr->pwild[i], WILD_VIEW, pblk_ptr); /* Allocate each block */ for (j = 0; j < WILD_VIEW; j++) { C_MAKE(p_ptr->pwild[i][j], WILD_BLOCK_SIZE, pcave_type *); for (k = 0; k < WILD_BLOCK_SIZE; k++) { C_MAKE(p_ptr->pwild[i][j][k], WILD_BLOCK_SIZE, pcave_type); } } } /* Allocate the wilderness itself */ C_MAKE(wild, WILD_SIZE, wild_type *); C_MAKE(wild_grid, WILD_SIZE, blk_ptr *); C_MAKE(wild_refcount, WILD_SIZE, int *); for (i = 0; i < WILD_SIZE; i++) { /* Allocate one row of the wilderness */ C_MAKE(wild[i], WILD_SIZE, wild_type); C_MAKE(wild_grid[i], WILD_SIZE, blk_ptr); C_MAKE(wild_refcount[i], WILD_SIZE, int); } /*** Prepare "vinfo" array ***/ /* Used by "update_view()" */ (void)vinfo_init(); /*** Prepare entity arrays ***/ /* Objects */ C_MAKE(o_list, z_info->o_max, object_type); /* Monsters */ C_MAKE(m_list, z_info->m_max, monster_type); /*** Prepare the options ***/ init_options(OPT_FLAG_BIRTH | OPT_FLAG_SERVER | OPT_FLAG_PLAYER); /* Initialize the options */ for (i = 0; i < OPT_MAX; i++) { if (option_info[i].o_text) { /* Accept */ option_mask[i / 32] |= (1L << (i % 32)); } } /* Initialize the window flags */ for (n = 0; n < ANGBAND_TERM_MAX; n++) { /* Analyze the options */ for (i = 0; i < 32; i++) { /* Accept */ if (window_flag_desc[i]) { /* Accept */ window_mask[n] |= (1L << i); } } } /*** Make store stock cache ***/ C_MAKE(store_cache, STORE_CACHE_AMNT, store_type *); /* Allocate the towns */ C_MAKE(place, z_info->wp_max, place_type); /* Get size of shop owner name arrays */ for (i = 0; owner_names[i]; i++) { /* Do nothing */ } owner_names_max = i; for (i = 0; owner_suffix[i]; i++) { /* Do nothing */ } owner_suffix_max = i; /* Success */ return (0); } /* * Initialize some other arrays */ static errr init_alloc(void) { int i; monster_race *r_ptr; alloc_entry *table; s16b num[MAX_DEPTH]; s16b aux[MAX_DEPTH]; /*** Analyze monster allocation info ***/ /* Clear the "aux" array */ (void)C_WIPE(&aux, MAX_DEPTH, s16b); /* Clear the "num" array */ (void)C_WIPE(&num, MAX_DEPTH, s16b); /* Size of "alloc_race_table" */ alloc_race_size = 0; /* Scan the monsters (not the ghost) */ for (i = 1; i < z_info->r_max - 1; i++) { /* Get the i'th race */ r_ptr = &r_info[i]; /* Legal monsters */ if (r_ptr->rarity) { /* Count the entries */ alloc_race_size++; /* Group by level */ num[r_ptr->level]++; } } /* Collect the level indexes */ for (i = 1; i < MAX_DEPTH; i++) { /* Group by level */ num[i] += num[i - 1]; } /* Paranoia */ if (!num[0]) quit("No town monsters!"); /*** Initialize monster allocation info ***/ /* Allocate the alloc_race_table */ C_MAKE(alloc_race_table, alloc_race_size, alloc_entry); /* Get the table entry */ table = alloc_race_table; /* Scan the monsters */ for (i = 1; i < z_info->r_max; i++) { /* Get the i'th race */ r_ptr = &r_info[i]; /* Count valid pairs */ if (r_ptr->rarity) { int p, x, y, z; /* Extract the base level */ x = r_ptr->level; /* Extract the base probability */ p = (100 / r_ptr->rarity); /* Skip entries preceding our locale */ y = (x > 0) ? num[x - 1] : 0; /* Skip previous entries at this locale */ z = y + aux[x]; /* Load the entry */ table[z].index = i; table[z].level = x; table[z].prob1 = p; table[z].prob2 = p; table[z].prob3 = p; /* Another entry complete for this locale */ aux[x]++; } } /* Init "alloc_kind_table" and "alloc_ego_table" */ (void)init_object_alloc(); /* Success */ return (0); } /* * Hack -- take notes on line 23 */ static void note(cptr str) { clear_row(23); put_fstr(20, 23, str); Term_fresh(); } /* * Hack -- Explain a broken "lib" folder and quit (see below). * * XXX XXX XXX This function is "messy" because various things * may or may not be initialized, but the "plog()" and "quit()" * functions are "supposed" to work under any conditions. */ static void init_angband_fail(void) { /* Explain */ plog("The 'lib' directory is probably missing or broken."); /* More details */ plog("Perhaps the archive was not extracted correctly."); /* Explain */ plog("See the 'README' file for more information."); /* Quit with error */ quit("Fatal Error."); } /* * Hack -- main Angband initialization entry point * * Verify some files, display the "news.txt" file, create * the high score file, initialize all internal arrays, and * load the basic "user pref files". * * Be very careful to keep track of the order in which things * are initialized, in particular, the only thing *known* to * be available when this function is called is the "z-term.c" * package, and that may not be fully initialized until the * end of this function, when the default "user pref files" * are loaded and "Term_xtra(TERM_XTRA_REACT,0)" is called. * * Note that this function attempts to verify the "news" file, * and the game aborts (cleanly) on failure, since without the * "news" file, it is likely that the "lib" folder has not been * correctly located. Otherwise, the news file is displayed for * the user. * * Note that this function attempts to verify (or create) the * "high score" file, and the game aborts (cleanly) on failure, * since one of the most common "extraction" failures involves * failing to extract all sub-directories (even empty ones), such * as by failing to use the "-d" option of "pkunzip", or failing * to use the "save empty directories" option with "Compact Pro". * This error will often be caught by the "high score" creation * code below, since the "lib/apex" directory, being empty in the * standard distributions, is most likely to be "lost", making it * impossible to create the high score file. * * Note that various things are initialized by this function, * including everything that was once done by "init_some_arrays". * * This initialization involves the parsing of special files * in the "lib/data" and sometimes the "lib/edit" directories. * * Note that the "template" files are initialized first, since they * often contain errors. This means that macros and message recall * and things like that are not available until after they are done. * * We load the default "user pref files" here in case any "color" * changes are needed before character creation. * * Note that the "graf-xxx.prf" file must be loaded separately, * if needed, in the first (?) pass through "TERM_XTRA_REACT". */ void init_angband(void) { int fd = -1; int mode = 0644; FILE *fp; char buf[1024]; /*** Verify the "news" file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_FILE, "news.txt"); /* Attempt to open the file */ fd = fd_open(buf, O_RDONLY); /* Failure */ if (fd < 0) { /* Message */ plog_fmt("Cannot access the '%s' file!", buf); /* Crash and burn */ init_angband_fail(); } /* Close it */ (void)fd_close(fd); /*** Display the "news" file ***/ /* Clear screen */ Term_clear(); /* Build the filename */ path_make(buf, ANGBAND_DIR_FILE, "news.txt"); /* Open the News file */ fp = my_fopen(buf, "r"); /* Dump */ if (fp) { int i = 0; /* Dump the file to the screen */ while (0 == my_fgets(fp, buf, 1024)) { /* Display and advance */ put_fstr(0, i++, buf); } /* Close */ my_fclose(fp); } /* Display version number */ put_fstr(42, 3, "%d.%d.%d", VER_MAJOR, VER_MINOR, VER_PATCH); /* Flush it */ Term_fresh(); /*** Verify (or create) the "high score" file ***/ /* Build the filename */ path_make(buf, ANGBAND_DIR_APEX, "scores.raw"); /* Attempt to open the high score file */ fd = fd_open(buf, O_RDONLY); /* Failure */ if (fd < 0) { /* File type is "DATA" */ FILE_TYPE(FILE_TYPE_DATA); /* Grab permissions */ safe_setuid_grab(); /* Create a new high score file */ fd = fd_make(buf, mode); /* Drop permissions */ safe_setuid_drop(); /* Failure */ if (fd < 0) { /* Message */ plog_fmt("Cannot create the '%s' file!", buf); /* Crash and burn */ init_angband_fail(); } } /* Close it */ (void)fd_close(fd); /*** Initialize some arrays ***/ /* Init the interface callbacks */ init_term_callbacks(); /* Initialize size info */ note("[Initializing array sizes...]"); if (init_z_info()) quit("Cannot initialize sizes"); /* Initialize scripting */ note("[Initializing scripts... (scripts)]"); if (script_init()) quit("Cannot initialize scripts"); /* Initialize feature info */ note("[Initializing arrays... (features)]"); if (init_f_info()) quit("Cannot initialize features"); /* Initialize object info */ note("[Initializing arrays... (objects)]"); if (init_k_info()) quit("Cannot initialize objects"); /* Initialize artifact info */ note("[Initializing arrays... (artifacts)]"); if (init_a_info()) quit("Cannot initialize artifacts"); /* Initialize ego-item info */ note("[Initializing arrays... (ego-items)]"); if (init_e_info()) quit("Cannot initialize ego-items"); /* Initialize monster info */ note("[Initializing arrays... (monsters)]"); if (init_r_info()) quit("Cannot initialize monsters"); /* Initialize feature info */ note("[Initializing arrays... (vaults)]"); if (init_v_info()) quit("Cannot initialize vaults"); /* Initialize quest array */ note("[Initializing arrays... (quests)]"); if (init_quests()) quit("Cannot initialize quests"); /* Initialize some other arrays */ note("[Initializing arrays... (other)]"); if (init_other()) quit("Cannot initialize other stuff"); /* Initialize some other arrays */ note("[Initializing arrays... (alloc)]"); if (init_alloc()) quit("Cannot initialize alloc stuff"); /*** Load default user pref files ***/ /* Initialize feature info */ note("[Initializing user pref files...]"); /* Access the "basic" pref file */ (void)process_pref_file("pref.prf"); /* Access the "user" pref file */ (void)process_pref_file("user.prf"); /* Initialise the fake monochrome flag */ fake_monochrome = (!use_graphics || streq(ANGBAND_SYS, "ibm")) ? TRUE : FALSE; /* Initialise the overhead map */ init_overhead_map(); /* Done */ note("[Initialization complete]"); } void cleanup_angband(void) { int i, j; /* Free the macros */ for (i = 0; i < macro__num; ++i) { string_free(macro__pat[i]); string_free(macro__act[i]); } FREE((void *)macro__pat); FREE((void *)macro__act); /* Free the keymaps */ for (i = 0; i < KEYMAP_MODES; ++i) { for (j = 0; j < 256; ++j) { string_free(keymap_act[i][j]); } } /* Free the allocation tables */ FREE(alloc_ego_table); FREE(alloc_race_table); FREE(alloc_kind_table); /* Free the towns */ FREE(place); /* Free the stores */ FREE(store_cache); /* Free the quest list */ FREE(quest); /* Free the lore, monster, and object lists */ FREE(m_list); FREE(o_list); #ifdef MONSTER_FLOW /* Flow arrays */ FREE(cave_when); FREE(cave_cost); #endif /* MONSTER_FLOW */ /* Delete the overhead map */ del_overhead_map(); /* * Note that this causes problems if Zangband exits due to an error * parsing the info files since at that point the wilderness is not * initiated. It works fine thereafter. */ #if 0 This code is wrong - the wilderness works differently now. - SF - /* Free the wilderness */ for (i = 0; i < WILD_SIZE; i++) { /* Free one row of the wilderness */ FREE(wild[i]); } /* Free the wilderness itself */ FREE(wild); /* Free cache of wilderness blocks */ for (i = 0; i < WILD_BLOCKS; i++) { /* Free rows of a block */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { FREE(wild_cache[i][j]); } /* Free block */ FREE(wild_cache[i]); } /* Free temporary wilderness block */ for (i = 0; i < WILD_BLOCK_SIZE + 1; i++) { /* Allocate one row of the temp_block */ FREE(temp_block[i]); } /* Free the cave */ for (i = 0; i < MAX_HGT; i++) { /* Allocate one row of the cave */ FREE(cave[i]); } #endif /* 0 */ /* Free the messages */ messages_free(); /* Free the "quarks" */ quarks_free(); /* Free the info, name, and text arrays */ free_info(&v_head); free_info(&r_head); free_info(&e_head); free_info(&a_head); free_info(&k_head); free_info(&f_head); free_info(&z_head); /* Free the interface callbacks */ free_term_callbacks(); /* Free the directories */ string_free(ANGBAND_DIR); string_free(ANGBAND_DIR_APEX); string_free(ANGBAND_DIR_BONE); string_free(ANGBAND_DIR_DATA); string_free(ANGBAND_DIR_EDIT); string_free(ANGBAND_DIR_SCRIPT); string_free(ANGBAND_DIR_FILE); string_free(ANGBAND_DIR_HELP); string_free(ANGBAND_DIR_INFO); string_free(ANGBAND_DIR_SAVE); string_free(ANGBAND_DIR_PREF); string_free(ANGBAND_DIR_USER); string_free(ANGBAND_DIR_XTRA); } zangband/src/load.c0000644000000000000000000017262010250356274013226 0ustar rootroot/* File: load.c */ /* Purpose: support for loading savefiles -BEN- */ #include "angband.h" /* * This file loads savefiles from Angband 2.8.X * * Ancient savefiles (pre-2.8.0) are loaded by another file. * * Note that Angband 2.7.0 through 2.7.8 are now officially obsolete, * and savefiles from those versions may not be successfully converted. * * We attempt to prevent corrupt savefiles from inducing memory errors. * * Note that this file should not use the random number generator, the * object flavors, the visual attr/char mappings, or anything else which * is initialized *after* or *during* the "load character" function. * * This file assumes that the monster/object records are initialized * to zero, and the race/kind tables have been loaded correctly. The * order of object stacks is currently not saved in the savefiles, but * the "next" pointers are saved, so all necessary knowledge is present. * * We should implement simple "savefile extenders" using some form of * "sized" chunks of bytes, with a {size,type,data} format, so everyone * can know the size, interested people can know the type, and the actual * data is available to the parsing routines that acknowledge the type. * * Consider changing the "globe of invulnerability" code so that it * takes some form of "maximum damage to protect from" in addition to * the existing "number of turns to protect for", and where each hit * by a monster will reduce the shield by that amount. * * XXX XXX XXX */ /* * Maximum number of tries for selection of a proper quest monster */ #define MAX_TRIES 100 /* * Local "savefile" pointer */ static FILE *fff; /* * Hack -- old "encryption" byte */ static byte xor_byte; /* * Hack -- simple "checksum" on the actual values */ static u32b v_check = 0L; /* * Hack -- simple "checksum" on the encoded bytes */ static u32b x_check = 0L; /* * The above function, adapted for Zangband */ static bool z_older_than(byte x, byte y, byte z) { /* Much older, or much more recent */ if (z_major < x) return (TRUE); if (z_major > x) return (FALSE); /* Distinctly older, or distinctly more recent */ if (z_minor < y) return (TRUE); if (z_minor > y) return (FALSE); /* Barely older, or barely more recent */ if (z_patch < z) return (TRUE); if (z_patch > z) return (FALSE); /* Identical versions */ return (FALSE); } /* * Hack -- Show information on the screen, one line at a time. * * Avoid the top two lines, to avoid interference with "msgf()". */ static void note(cptr fmt, ...) { static int y = 2; va_list vp; char msg[1024]; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(msg, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Draw the message */ prtf(0, y, msg); /* Advance one line (wrap if needed) */ if (++y >= 24) y = 2; /* Flush it */ Term_fresh(); /* End the Varargs Stuff */ va_end(vp); } /* * Hack -- determine if an item is "wearable" (or a missile) */ static bool wearable_p(const object_type *o_ptr) { /* Valid "tval" codes */ switch (o_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: case TV_LITE: case TV_AMULET: case TV_RING: { return (TRUE); } } /* Nope */ return (FALSE); } /* * Hack -- determine if an item is a "weapon" (or a missile) */ static bool is_weapon(const object_type *o_ptr) { /* Valid "tval" codes */ switch (o_ptr->tval) { case TV_SHOT: case TV_BOLT: case TV_ARROW: case TV_BOW: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_DIGGING: { return (TRUE); } } /* Nope */ return (FALSE); } /* * The following functions are used to load the basic building blocks * of savefiles. They also maintain the "checksum" info for 2.7.0+ */ static byte sf_get(void) { byte c, v; /* Get a character, decode the value */ c = getc(fff) & 0xFF; v = c ^ xor_byte; xor_byte = c; /* Maintain the checksum info */ v_check += v; x_check += xor_byte; /* Return the value */ return (v); } static void rd_byte(byte *ip) { *ip = sf_get(); } static void rd_u16b(u16b *ip) { (*ip) = sf_get(); (*ip) |= ((u16b)(sf_get()) << 8); } static void rd_s16b(s16b *ip) { rd_u16b((u16b *)ip); } static void rd_u32b(u32b *ip) { (*ip) = sf_get(); (*ip) |= ((u32b)(sf_get()) << 8); (*ip) |= ((u32b)(sf_get()) << 16); (*ip) |= ((u32b)(sf_get()) << 24); } static void rd_s32b(s32b *ip) { rd_u32b((u32b *)ip); } /* * Hack -- read a string */ static void rd_string(char *str, int max) { int i; /* Read the string */ for (i = 0; TRUE; i++) { byte tmp8u; /* Read a byte */ rd_byte(&tmp8u); /* Collect string while legal */ if (i < max) str[i] = tmp8u; /* End of string */ if (!tmp8u) break; } /* Terminate */ str[max - 1] = '\0'; } /* * Hack -- strip some bytes */ static void strip_bytes(int n) { byte tmp8u; /* Strip the bytes */ while (n--) rd_byte(&tmp8u); } #ifdef UNUSED_FUNC /* * Hack -- strip a string */ static void strip_string(void) { byte tmp8u; /* Read the string */ do { /* Read a byte */ rd_byte(&tmp8u); } /* End of string */ while (tmp8u); } #endif /* UNUSED_FUNC */ /* * Read an object * * This function attempts to "repair" old savefiles, and to extract * the most up to date values for various object fields. * * Note that Angband 2.7.9 introduced a new method for object "flags" * in which the "flags" on an object are actually extracted when they * are needed from the object kind, artifact index, ego-item index, * and two special "xtra" fields which are used to encode any "extra" * power of certain ego-items. This had the side effect that items * imported from pre-2.7.9 savefiles will lose any "extra" powers they * may have had, and also, all "uncursed" items will become "cursed" * again, including Calris, even if it is being worn at the time. As * a complete hack, items which are inscribed with "uncursed" will be * "uncursed" when imported from pre-2.7.9 savefiles. * * Zangband 2.5.3 changed the ego and artifact interface from the object * data type. The new system makes each treatable like the random * artifacts. This increases memory usage, but simplifies large amounts * of code. It also makes some effects possible that would otherwise * require slow script hooks. */ static void rd_item(object_type *o_ptr) { byte old_dd; byte old_ds; byte tmpbyte; s16b tmps16b; object_kind *k_ptr; char buf[1024]; int i; /* Old flags from pre [Z] 2.5.3 */ byte name1, name2, xtra1, xtra2; /* Number of object flags */ byte n_flags; /* Kind */ rd_s16b(&o_ptr->k_idx); if (sf_version < 6) { /* Location */ rd_byte(&tmpbyte); o_ptr->iy = tmpbyte; rd_byte(&tmpbyte); o_ptr->ix = tmpbyte; } else { /* Location */ rd_s16b(&o_ptr->iy); rd_s16b(&o_ptr->ix); } /* Type/Subtype */ rd_byte(&o_ptr->tval); rd_byte(&o_ptr->sval); /* Special pval */ rd_s16b(&o_ptr->pval); /* New method - old method removed. */ rd_byte(&o_ptr->discount); rd_byte(&o_ptr->number); rd_s16b(&o_ptr->weight); if (sf_version < 19) { /* Old ego and artifact number */ rd_byte(&name1); rd_byte(&name2); } rd_s16b(&o_ptr->timeout); rd_s16b(&o_ptr->to_h); rd_s16b(&o_ptr->to_d); rd_s16b(&o_ptr->to_a); rd_s16b(&o_ptr->ac); rd_byte(&old_dd); rd_byte(&old_ds); rd_byte(&o_ptr->info); if (sf_version < 35) { /* Old "marked" flag */ rd_byte(&tmpbyte); if (tmpbyte) o_ptr->info |= OB_SEEN; } /* Number of object flags */ if (sf_version < 41) n_flags = 3; else if (sf_version < 50) n_flags = 4; else { rd_byte(&n_flags); if (n_flags > NUM_TR_SETS) abort(); } /* Object flags */ for (i = 0; i < n_flags; i++) rd_u32b(&o_ptr->flags[i]); for (i = n_flags; i < NUM_TR_SETS; i++) o_ptr->flags[i] = 0; /* Lites changed in [Z] 2.6.0 */ if ((sf_version < 25) && (o_ptr->tval == TV_LITE)) { /* Torches and lanterns use timeout now */ if ((o_ptr->sval == SV_LITE_TORCH) || (o_ptr->sval == SV_LITE_LANTERN)) { o_ptr->timeout = o_ptr->pval; o_ptr->pval = 0; } else { /* Other lites are everburning. */ SET_FLAG(o_ptr, TR_LITE); } } if (sf_version > 30) { /* Link to next object in the list */ rd_s16b(&o_ptr->next_o_idx); } if (sf_version < 36) { /* Monster holding object */ rd_s16b(&tmps16b); if (tmps16b) { o_ptr->allocated = TRUE; } else { o_ptr->allocated = FALSE; } } else { rd_byte((byte *)(&o_ptr->allocated)); } if (sf_version < 19) { /* Special powers */ rd_byte(&xtra1); rd_byte(&xtra2); } /* Feeling - from 2.3.1, "savefile version 1" */ if (sf_version >= 1) { rd_byte(&o_ptr->feeling); } /* Inscription */ rd_string(buf, 1024); /* If this savefile is old, maybe we need to translate the feeling */ if (sf_version < 1) { byte i; for (i = 0; i <= 9; i++) { if (game_inscriptions[i] == NULL) { continue; } if (streq(buf, game_inscriptions[i])) { o_ptr->feeling = i; buf[0] = 0; break; } } } /* Save the inscription */ if (buf[0]) o_ptr->inscription = quark_add(buf); rd_string(buf, 1024); if (buf[0]) o_ptr->xtra_name = quark_add(buf); /* Attached scripts */ if (sf_version >= 45) { rd_byte(&tmpbyte); while (tmpbyte != 255) { rd_string(buf, 1024); if (tmpbyte < MAX_TRIGGER) { o_ptr->trigger[tmpbyte] = quark_add(buf); } else { /* XXX Error */ } rd_byte(&tmpbyte); } } /* The Python object */ if (!z_older_than(2, 2, 4)) { s32b tmp32s; rd_s32b(&tmp32s); strip_bytes(tmp32s); } /* Obtain the "kind" template */ k_ptr = &k_info[o_ptr->k_idx]; /* For rod-stacking */ if (z_older_than(2, 2, 5) && (o_ptr->tval == TV_ROD)) { o_ptr->timeout = o_ptr->pval * o_ptr->number; o_ptr->pval = k_ptr->pval * o_ptr->number; } /* Mega-Hack... Corpses became fields */ if ((o_ptr->tval == 10) && (sf_version < 15)) { /* Hack - get rid of it. */ o_ptr->k_idx = 0; return; } /* The new flags as of [Z] 2.5.3 */ if (sf_version > 18) { /* The new flags */ rd_s32b(&o_ptr->cost); rd_byte(&o_ptr->a_idx); for (i = 0; i < n_flags; i++) rd_u32b(&o_ptr->kn_flags[i]); for (i = n_flags; i < NUM_TR_SETS; i++) o_ptr->kn_flags[i] = 0; if (o_ptr->a_idx && sf_version < 46) { if (o_ptr->a_idx < 128) { /* Remove old randart activations */ o_ptr->a_idx = 0; o_ptr->flags[2] &= ~TR2_ACTIVATE; } else { /* Now just use a_idx */ o_ptr->a_idx -= 128; } } /* * Add appropriate scripts to artifacts that are missing them, * for older savefiles */ if (o_ptr->a_idx) { int i; for (i = 0; i < MAX_TRIGGER; i++) { if (a_info[o_ptr->a_idx].trigger[i] && !o_ptr->trigger[i]) { o_ptr->trigger[i] = quark_add( a_text + a_info[o_ptr->a_idx].trigger[i]); } } } /* * XXX Some older buggy versions set TR2_PERMA_CURSE * on items where it shouldn't have been set. */ o_ptr->kn_flags[2] &= o_ptr->flags[2] | ~(TR2_HEAVY_CURSE | TR2_PERMA_CURSE); } else { /* Set the cost to something reasonable */ o_ptr->cost = k_ptr->cost; } /* Repair non "wearable" items */ if (!wearable_p(o_ptr)) { /* Acquire correct fields */ o_ptr->to_h = k_ptr->to_h; o_ptr->to_d = k_ptr->to_d; o_ptr->to_a = k_ptr->to_a; /* Acquire correct fields */ if (o_ptr->tval != TV_WAND) { /* Hack - Wands count "used" charges */ o_ptr->ac = k_ptr->ac; } o_ptr->dd = k_ptr->dd; o_ptr->ds = k_ptr->ds; /* Acquire correct weight */ o_ptr->weight = k_ptr->weight; /* Paranoia */ o_ptr->a_idx = 0; /* Reset flags */ for (i = 0; i < NUM_TR_SETS; i++) o_ptr->flags[i] = k_ptr->flags[i]; /* All done */ return; } /* Hack -- extract the "broken" flag */ if (o_ptr->pval < 0) o_ptr->cost = 0; if (sf_version < 19) { /* Convert old ego items to current format */ if (name2) { /* Obtain the ego-item info */ ego_item_type *e_ptr = &e_info[name2]; /* Use that ego-item */ if (e_ptr->name) { /* Save the flags */ add_ego_flags(o_ptr, name2); /* Keep the damage dice */ o_ptr->dd = old_dd; o_ptr->ds = old_ds; /* Change the price */ if (!e_ptr->cost) { o_ptr->cost = 0L; } else { o_ptr->cost += e_ptr->cost; } /* Note: the xtra1 value is ignored here. */ } } /* Convert old artifacts to current format */ else if (name1) { artifact_type *a_ptr; /* Obtain the artifact info */ a_ptr = &a_info[name1]; /* Acquire new artifact "pval" */ o_ptr->pval = a_ptr->pval; /* Acquire new artifact fields */ o_ptr->ac = a_ptr->ac; o_ptr->dd = a_ptr->dd; o_ptr->ds = a_ptr->ds; /* Acquire new artifact weight */ o_ptr->weight = a_ptr->weight; /* Save the artifact flags */ for (i = 0; i < NUM_TR_SETS; i++) o_ptr->flags[i] |= a_ptr->flags[i]; /* Mega-Hack -- set activation */ o_ptr->a_idx = name1; /* Save the inscription */ o_ptr->xtra_name = quark_add(a_name + a_ptr->name); /* Set the cost */ if (!a_ptr->cost) { /* Hack -- "worthless" artifacts */ o_ptr->cost = 0L; } else { /* Hack - use the artifact price */ o_ptr->cost = a_ptr->cost; } } /* Convert Random artifacts */ else if (o_ptr->xtra_name) { /* Strip activation */ if (xtra2) { o_ptr->flags[2] &= ~TR2_ACTIVATE; } /* Make the object an artifact */ SET_FLAG(o_ptr, TR_INSTA_ART); /* Set the cost */ o_ptr->cost = k_info[o_ptr->k_idx].cost + flag_cost(o_ptr, o_ptr->pval); } /* Convert normal items */ else { /* Set cost for normal items */ o_ptr->cost = k_info[o_ptr->k_idx].cost; } /* Identification status */ if (o_ptr->info & (OB_MENTAL)) { for (i = 0; i < NUM_TR_SETS; i++) o_ptr->kn_flags[i] = o_ptr->flags[i]; } } if (o_ptr->xtra_name) /* Artifacts and ego items */ { o_ptr->dd = old_dd; o_ptr->ds = old_ds; } else { /* Acquire standard fields */ o_ptr->ac = k_ptr->ac; o_ptr->dd = k_ptr->dd; o_ptr->ds = k_ptr->ds; /* Acquire standard weight */ o_ptr->weight = k_ptr->weight; /* Obtain tval/sval from k_info */ o_ptr->tval = k_ptr->tval; o_ptr->sval = k_ptr->sval; } /* Change shattered weapons from 0d0 to 1d1 */ if (is_weapon(o_ptr)) { if (o_ptr->dd == 0) o_ptr->dd = 1; if (o_ptr->ds == 0) o_ptr->ds = 1; } } /* * Read a monster */ static void rd_monster(monster_type *m_ptr) { byte tmp8u; /* Read the monster race */ rd_s16b(&m_ptr->r_idx); /* Read the other information */ if (sf_version < 6) { /* Location */ rd_byte(&tmp8u); m_ptr->fy = tmp8u; rd_byte(&tmp8u); m_ptr->fx = tmp8u; } else { /* Location */ rd_s16b(&m_ptr->fy); rd_s16b(&m_ptr->fx); } rd_s16b(&m_ptr->hp); rd_s16b(&m_ptr->maxhp); rd_s16b(&m_ptr->csleep); rd_byte(&m_ptr->mspeed); rd_byte(&m_ptr->energy); rd_byte(&m_ptr->stunned); rd_byte(&m_ptr->confused); rd_byte(&m_ptr->monfear); /* Monster invulnerability introduced from 2.3.2+ */ if (sf_version < 2) m_ptr->invulner = 0; else rd_byte(&m_ptr->invulner); if (!(z_major == 2 && z_minor == 0 && z_patch == 6)) rd_u32b(&m_ptr->smart); else m_ptr->smart = 0; if (sf_version < 31) { rd_byte(&tmp8u); } else { rd_s16b(&m_ptr->hold_o_idx); } } static void rd_field(field_type *f_ptr) { s32b tmp32s; int i; s16b t_idx; /* Type */ rd_s16b(&t_idx); /* Prepare the field */ field_prep(f_ptr, t_idx); /* Location */ rd_s16b(&f_ptr->fy); rd_s16b(&f_ptr->fx); /* Info flags */ rd_u16b(&f_ptr->info); /* Counter */ rd_s16b(&f_ptr->counter); /* Data */ for (i = 0; i < 8; i++) { rd_byte(&f_ptr->data[i]); } rd_s32b(&tmp32s); strip_bytes(tmp32s); } /* * Read the monster lore */ static void rd_lore(int r_idx) { byte tmp8u; monster_race *r_ptr = &r_info[r_idx]; /* Pre-2.2.0 (old r_info.txt) */ if (z_older_than(2, 2, 0)) { /* Throw away old info */ strip_bytes(48); } /* Current */ else { /* Count sights/deaths/kills */ rd_s16b(&r_ptr->r_sights); rd_s16b(&r_ptr->r_deaths); rd_s16b(&r_ptr->r_pkills); rd_s16b(&r_ptr->r_tkills); /* Count wakes and ignores */ rd_byte(&r_ptr->r_wake); rd_byte(&r_ptr->r_ignore); /* Extra stuff */ rd_byte(&r_ptr->r_xtra1); rd_byte(&r_ptr->r_xtra2); /* Count drops */ rd_byte(&r_ptr->r_drop_gold); rd_byte(&r_ptr->r_drop_item); /* Count spells */ rd_byte(&r_ptr->r_cast_inate); rd_byte(&r_ptr->r_cast_spell); /* Count blows of each type */ rd_byte(&r_ptr->r_blows[0]); rd_byte(&r_ptr->r_blows[1]); rd_byte(&r_ptr->r_blows[2]); rd_byte(&r_ptr->r_blows[3]); /* Memorize flags */ rd_u32b(&r_ptr->r_flags[0]); rd_u32b(&r_ptr->r_flags[1]); rd_u32b(&r_ptr->r_flags[2]); rd_u32b(&r_ptr->r_flags[3]); rd_u32b(&r_ptr->r_flags[4]); rd_u32b(&r_ptr->r_flags[5]); if (sf_version > 51) rd_u32b(&r_ptr->r_flags[6]); /* Read the "Racial" monster limit per level */ rd_byte(&r_ptr->max_num); /* Later (?) */ rd_byte(&tmp8u); rd_byte(&tmp8u); rd_byte(&tmp8u); } /* Repair the lore flags */ r_ptr->r_flags[0] &= r_ptr->flags[0]; r_ptr->r_flags[1] &= r_ptr->flags[1]; r_ptr->r_flags[2] &= r_ptr->flags[2]; r_ptr->r_flags[3] &= r_ptr->flags[3]; r_ptr->r_flags[4] &= r_ptr->flags[4]; r_ptr->r_flags[5] &= r_ptr->flags[5]; } /* * Read a store */ static void rd_store(int town_num, int store_num) { store_type *st_ptr = &place[town_num].store[store_num]; int j; s16b data; byte allocated, type = 0, owner = 0; s16b max_cost; byte greed; char buf[256]; /* Read the basic info */ if (sf_version < 34) { strip_bytes(4); } rd_s16b(&data); if (sf_version < 49) { rd_byte(&owner); } else { rd_string(buf, 256); st_ptr->owner_name = quark_add(buf); rd_s16b(&max_cost); rd_byte(&greed); } rd_byte(&allocated); if (sf_version < 34) { strip_bytes(4); } if (sf_version > 20) { rd_u16b(&st_ptr->x); rd_u16b(&st_ptr->y); /* Hack - only listen to 'type' in recent savefiles */ rd_byte(&type); } /* Hack - Initialise the store (even if not really a store) */ store_init(town_num, store_num, type); /* Finish initialisation */ if (sf_version >= 49) { st_ptr->owner_name = quark_add(buf); st_ptr->max_cost = max_cost; st_ptr->greed = greed; } /* Restore the saved parameters */ st_ptr->data = data; /* Read last visit */ rd_s32b(&st_ptr->last_visit); /* * Hack - allocate store if it has stock * Note that this will change the order that * stores are removed from the cache. * The resulting list can be sorted... but it * doesn't really matter. */ if (allocated) { (void)allocate_store(st_ptr); } if (sf_version < 38) { /* Read the items */ for (j = 0; j < allocated; j++) { object_type forge; object_type *q_ptr; /* Get local object */ q_ptr = &forge; /* Read the item */ rd_item(q_ptr); /* Wipe the object */ object_wipe(q_ptr); /* Ignore the item */ } } else { /* Get pointer to item list in o_list[] */ rd_s16b(&st_ptr->stock); } } /* * Read RNG state */ static void rd_randomizer(void) { int i; u16b tmp16u; /* Tmp */ rd_u16b(&tmp16u); /* Place */ rd_u16b(&Rand_place); /* State */ for (i = 0; i < RAND_DEG; i++) { rd_u32b(&Rand_state[i]); } /* Accept */ Rand_quick = FALSE; } /* * Read options (ignore most pre-2.8.0 options) * * Note that the normal options are now stored as a set of 256 bit flags, * plus a set of 256 bit masks to indicate which bit flags were defined * at the time the savefile was created. This will allow new options * to be added, and old options to be removed, at any time, without * hurting old savefiles. * * The window options are stored in the same way, but note that each * window gets 32 options, and their order is fixed by certain defines. */ static void rd_options(void) { int i, n; byte b; u16b c; u32b flag[8], mask[8]; /*** Oops ***/ /* Ignore old options */ strip_bytes(16); /*** Special info */ /* Read "delay_factor" */ rd_byte(&b); delay_factor = b; /* Read "hitpoint_warn" */ rd_byte(&b); hitpoint_warn = b; /*** Cheating options ***/ rd_u16b(&c); if (c & 0x0002) p_ptr->state.wizard = TRUE; cheat_peek = (c & 0x0100) ? TRUE : FALSE; cheat_hear = (c & 0x0200) ? TRUE : FALSE; cheat_room = (c & 0x0400) ? TRUE : FALSE; cheat_xtra = (c & 0x0800) ? TRUE : FALSE; cheat_know = (c & 0x1000) ? TRUE : FALSE; cheat_live = (c & 0x2000) ? TRUE : FALSE; /* Autosave options */ rd_byte(&autosave_l); rd_byte(&autosave_t); rd_s16b(&autosave_freq); /*** Normal Options ***/ /* Read the option flags */ for (n = 0; n < 8; n++) rd_u32b(&flag[n]); /* Read the option masks */ for (n = 0; n < 8; n++) rd_u32b(&mask[n]); /* Analyze the options */ for (n = 0; n < 8; n++) { /* Analyze the options */ for (i = 0; i < 32; i++) { if ((mask[n] & (1L << i)) && (option_mask[n] & (1L << i))) { /* Set */ if (flag[n] & (1L << i)) { /* Set */ option_info[n * 32 + i].o_val = TRUE; } /* Clear */ else { /* Clear */ option_info[n * 32 + i].o_val = FALSE; } } } } /* Set the options */ init_options(OPT_FLAG_BIRTH | OPT_FLAG_SERVER | OPT_FLAG_PLAYER); /*** Window Options ***/ /* Read the window flags */ for (n = 0; n < ANGBAND_TERM_MAX; n++) rd_u32b(&flag[n]); /* Read the window masks */ for (n = 0; n < ANGBAND_TERM_MAX; n++) rd_u32b(&mask[n]); /* Analyze the options */ for (n = 0; n < 8; n++) { /* Analyze the options */ for (i = 0; i < 32; i++) { /* Process valid flags */ if (mask[n] & (1L << i)) { /* Process valid flags */ if (window_mask[n] & (1L << i)) { /* Set */ if (flag[n] & (1L << i)) { /* Set */ window_flag[n] |= (1L << i); } /* Clear */ else { /* Clear */ window_flag[n] &= ~(1L << i); } } } } } } /* * Hack -- strip the "ghost" info * * XXX XXX XXX This is such a nasty hack it hurts. */ static void rd_ghost(void) { char buf[64]; /* Strip name */ rd_string(buf, 64); /* ghosts */ /* Strip old data */ strip_bytes(60); } static bool player_detected = FALSE; /* * Read the "extra" information */ static void rd_extra(void) { int i; byte tmp8u; s16b tmp16s; s16b dummy; char old_history[60]; rd_string(player_name, 32); rd_string(p_ptr->state.died_from, 80); /* Read and ignore old history data */ for (i = 0; i < 4; i++) { rd_string(old_history, 60); } /* Class/Race/Gender/Spells */ rd_byte(&p_ptr->rp.prace); rd_byte(&p_ptr->rp.pclass); rd_byte(&p_ptr->rp.psex); rd_byte(&p_ptr->spell.r[0].realm); rd_byte(&p_ptr->spell.r[1].realm); rd_byte(&tmp8u); /* oops */ /* Special Race/Class info */ rd_byte(&p_ptr->rp.hitdie); rd_u16b(&p_ptr->expfact); /* Age/Height/Weight */ rd_s16b(&p_ptr->rp.age); rd_s16b(&p_ptr->rp.ht); rd_s16b(&p_ptr->rp.wt); /* Read the stat info */ for (i = 0; i < 6; i++) rd_s16b(&p_ptr->stat[i].max); for (i = 0; i < 6; i++) rd_s16b(&p_ptr->stat[i].cur); /* Fix up stats for old savefiles */ if (sf_version < 39) { /* This will be initialized again later, but we need it now for adjust_stat to work */ rp_ptr = &race_info[p_ptr->rp.prace]; cp_ptr = &class_info[p_ptr->rp.pclass]; for (i = 0; i < 6; i++) { int bonus = race_info[p_ptr->rp.prace].r_adj[i] + class_info[p_ptr->rp.pclass].c_adj[i]; p_ptr->stat[i].max = adjust_stat(i, p_ptr->stat[i].max, bonus); /* Hack - Restore all stats... */ p_ptr->stat[i].cur = p_ptr->stat[i].max; } } if (sf_version < 40) { for (i = 0; i < 6; i++) { if (p_ptr->stat[i].max < 18) p_ptr->stat[i].max *= 10; else p_ptr->stat[i].max += 180-18; if (p_ptr->stat[i].cur < 18) p_ptr->stat[i].cur *= 10; else p_ptr->stat[i].cur += 180-18; } } strip_bytes(24); /* oops */ rd_s32b(&p_ptr->au); rd_s32b(&p_ptr->max_exp); rd_s32b(&p_ptr->exp); rd_u16b(&p_ptr->exp_frac); rd_s16b(&p_ptr->lev); rd_s16b(&p_ptr->place_num); /* Read arena and rewards information */ strip_bytes(12); rd_s16b(&tmp16s); for (i = 0; i < tmp16s; i++) rd_s16b(&dummy); rd_s16b(&p_ptr->mhp); rd_s16b(&p_ptr->chp); rd_u16b(&p_ptr->chp_frac); rd_s16b(&p_ptr->msp); rd_s16b(&p_ptr->csp); rd_u16b(&p_ptr->csp_frac); rd_s16b(&p_ptr->max_lev); strip_bytes(2); /* Old "max_depth" */ /* Repair maximum player level XXX XXX XXX */ if (p_ptr->max_lev < p_ptr->lev) p_ptr->max_lev = p_ptr->lev; /* More info */ strip_bytes(8); rd_s16b(&p_ptr->rp.sc); strip_bytes(2); /* Read the flags */ strip_bytes(2); /* Old "rest" */ rd_s16b(&p_ptr->tim.blind); rd_s16b(&p_ptr->tim.paralyzed); rd_s16b(&p_ptr->tim.confused); rd_s16b(&p_ptr->food); strip_bytes(4); /* Old "food_digested" / "protection" */ rd_s16b(&p_ptr->energy); rd_s16b(&p_ptr->tim.fast); rd_s16b(&p_ptr->tim.slow); rd_s16b(&p_ptr->tim.afraid); rd_s16b(&p_ptr->tim.cut); rd_s16b(&p_ptr->tim.stun); rd_s16b(&p_ptr->tim.poisoned); rd_s16b(&p_ptr->tim.image); rd_s16b(&p_ptr->tim.protevil); rd_s16b(&p_ptr->tim.invuln); rd_s16b(&p_ptr->tim.hero); rd_s16b(&p_ptr->tim.shero); rd_s16b(&p_ptr->tim.shield); rd_s16b(&p_ptr->tim.blessed); rd_s16b(&p_ptr->tim.invis); rd_s16b(&p_ptr->tim.word_recall); rd_s16b(&p_ptr->see_infra); rd_s16b(&p_ptr->tim.infra); rd_s16b(&p_ptr->tim.oppose_fire); rd_s16b(&p_ptr->tim.oppose_cold); rd_s16b(&p_ptr->tim.oppose_acid); rd_s16b(&p_ptr->tim.oppose_elec); rd_s16b(&p_ptr->tim.oppose_pois); /* Old savefiles do not have the following fields... */ if ((z_major == 2) && (z_minor == 0) && (z_patch == 6)) { p_ptr->tim.esp = 0; p_ptr->tim.wraith_form = 0; p_ptr->tim.resist_magic = 0; p_ptr->chaos_patron = get_chaos_patron(); p_ptr->muta1 = 0; p_ptr->muta2 = 0; p_ptr->muta3 = 0; get_virtues(); } else { rd_s16b(&p_ptr->tim.esp); rd_s16b(&p_ptr->tim.wraith_form); rd_s16b(&p_ptr->tim.resist_magic); if (sf_version < 32) { /* Ignore unused counters */ strip_bytes(16); } rd_s16b(&p_ptr->chaos_patron); rd_u32b(&p_ptr->muta1); rd_u32b(&p_ptr->muta2); rd_u32b(&p_ptr->muta3); if (sf_version < 5) { get_virtues(); } else { for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { rd_s16b(&p_ptr->virtues[i]); } for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { rd_s16b(&p_ptr->vir_types[i]); } } } rd_byte(&p_ptr->state.confusing); rd_byte(&tmp8u); /* oops */ rd_byte(&tmp8u); /* oops */ rd_byte(&tmp8u); /* oops */ rd_byte((byte *)&p_ptr->state.searching); rd_byte(&tmp8u); rd_byte(&tmp8u); rd_byte(&tmp8u); /* Future use */ for (i = 0; i < 48; i++) rd_byte(&tmp8u); /* Skip the flags */ strip_bytes(12); /* Hack -- the two "special seeds" */ rd_u32b(&seed_flavor); if (sf_version < 24) { /* No more seed_town */ strip_bytes(4); } /* Special stuff */ rd_u16b(&p_ptr->state.panic_save); rd_u16b(&p_ptr->state.total_winner); rd_u16b(&p_ptr->state.noscore); /* Read "death" */ rd_byte(&tmp8u); p_ptr->state.is_dead = tmp8u; /* Read "feeling" */ rd_byte(&p_ptr->state.feeling); /* Turn of last "feeling" */ rd_s32b(&old_turn); /* Current turn */ rd_s32b(&turn); if (sf_version > 17) { /* Get trap detection status */ rd_byte((byte *)&player_detected); if (sf_version < 33) { /* oops */ strip_bytes(4); } else { rd_s16b(&p_ptr->inventory); } } } /* * Mega-Hack , in order to load old savefiles, we * need to save the player's inventory temporarily. */ static object_type old_inventory[24]; /* * Read the player inventory * * Note that the inventory is "re-sorted" later by "dungeon()". */ static errr rd_inventory(void) { object_type forge; object_type *q_ptr = &forge; /* Wipe the structure */ (void)WIPE(q_ptr, object_type); /* Read until done */ while (1) { u16b n; /* Get the next item index */ rd_u16b(&n); /* Nope, we reached the end */ if (n == 0xFFFF) break; /* Get local object */ q_ptr = &forge; /* Wipe the object */ object_wipe(q_ptr); /* Read the item */ rd_item(q_ptr); /* Hack - assume not allocated in o_list[] */ q_ptr->allocated = FALSE; /* Hack -- verify item */ if (!q_ptr->k_idx) return (53); if (sf_version > 36) { /* Wield equipment */ if (n < EQUIP_MAX) { /* Copy object */ swap_objects(&p_ptr->equipment[n], q_ptr); } } else { /* Wield equipment */ if (n >= 24) { /* Copy object */ swap_objects(&p_ptr->equipment[n - 24], q_ptr); } /* Carry inventory */ else { /* Copy object into temp structure */ swap_objects(&old_inventory[n], q_ptr); } } } /* Success */ return (0); } /* * Read the saved messages */ static void rd_messages(void) { int i; char buf[1024]; byte tmp8u; s16b num; /* Total */ rd_s16b(&num); /* Read the messages */ for (i = 0; i < num; i++) { /* Read the message */ rd_string(buf, 1024); /* Read the color */ if (sf_version > 10) rd_byte(&tmp8u); else tmp8u = MSG_GENERIC; /* Save the message */ message_add(buf, tmp8u); } } static void fix_tile(cave_type *c_ptr) { /* Get rid of pre-fields terrain */ if (sf_version < 17) { /* Invisible wall */ if (c_ptr->feat == 0x5B) { /* Get rid of it */ c_ptr->feat = FEAT_FLOOR; } /* Glyph of warding */ if (c_ptr->feat == 0x03) { /* Get rid of it */ c_ptr->feat = FEAT_FLOOR; } /* Explosive Rune */ if (c_ptr->feat == 0x40) { /* Get rid of it */ c_ptr->feat = FEAT_FLOOR; } /* Traps */ if ((c_ptr->feat == 0x02) || (c_ptr->feat >= 0x10 && c_ptr->feat <= 0x1F) || (c_ptr->feat == 0x5A)) { /* Get rid of it */ c_ptr->feat = FEAT_FLOOR; } /* Doors */ if ((c_ptr->feat > 0x20) && (c_ptr->feat < 0x28)) { /* Locked door -> closed door */ c_ptr->feat = FEAT_CLOSED; } if ((c_ptr->feat >= 0x28) && (c_ptr->feat <= 0x2F)) { /* Stuck door -> closed door */ c_ptr->feat = FEAT_CLOSED; } } } /* * Load dungeon or wilderness map */ static void load_map(int xmin, int ymin, int xmax, int ymax) { int i, y, x; byte count; byte tmp8u; s16b tmp16s; cave_type *c_ptr; pcave_type *pc_ptr; /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Access the cave */ c_ptr = area(x, y); pc_ptr = parea(x, y); /* Extract "info" (without the CAVE_ROOM flag set) */ c_ptr->info = (tmp8u & (CAVE_GLOW | CAVE_ICKY)); /* Extract the player data */ if (sf_version < 27) { /* * Set old CAVE_MARK and flag (used below) * (Ignore the CAVE_VIEW flag) */ pc_ptr->player = tmp8u & (0x01); } /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } if (sf_version > 26) { /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Access the cave */ pc_ptr = parea(x, y); /* Extract "player info" (only use detect grid data) */ pc_ptr->player = tmp8u & (GRID_DTCT); /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } if (sf_version > 28) { /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Access the cave */ pc_ptr = parea(x, y); /* Extract "feat" */ pc_ptr->feat = tmp8u; /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Access the cave */ c_ptr = area(x, y); /* Extract "feat" */ c_ptr->feat = tmp8u; /* Quick hack to fix various removed features */ fix_tile(c_ptr); /* Fix player memory for old savefiles */ if (sf_version < 28) { pc_ptr = parea(x, y); /* Old CAVE_MARK flag set? */ if (pc_ptr->player & 0x01) { /* Remember square */ pc_ptr->feat = c_ptr->feat; } } /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } /*** Run length decoding ***/ if (sf_version < 28) { /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Ignore this (The mimic field has been removed) */ /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } /*** Run length decoding ***/ /* This isn't stored in later versions. */ if (sf_version < 15) { /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_s16b(&tmp16s); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Access the cave */ c_ptr = area(x, y); /* Extract field */ c_ptr->fld_idx = 0; /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } } /* * Strip old dungeon or wilderness map from the savefile */ static void strip_map(int xmin, int ymin, int xmax, int ymax) { int i, y, x; byte count; byte tmp8u; s16b tmp16s; /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } if (sf_version > 26) { /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } if (sf_version > 28) { /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } if (sf_version < 29) { /*** Run length decoding ***/ /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_byte(&tmp8u); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } /*** Run length decoding ***/ /* This isn't stored in later versions. */ if (sf_version < 15) { /* Load the dungeon data */ for (x = xmin, y = ymin; y < ymax;) { /* Grab RLE info */ rd_byte(&count); rd_s16b(&tmp16s); /* Apply the RLE info */ for (i = count; i > 0; i--) { /* Advance/Wrap */ if (++x >= xmax) { /* Wrap */ x = xmin; /* Advance/Wrap */ if (++y >= ymax) break; } } } } } /* * Size of the wilderness to load */ static s32b wild_x_size; static s32b wild_y_size; /* * Load wilderness data */ static void load_wild_data(void) { int i, j; u16b tmp_u16b; byte tmp_byte; if (sf_version < 28) { /* Load bounds */ rd_u16b(&p_ptr->max_hgt); rd_u16b(&p_ptr->max_wid); rd_u16b(&p_ptr->min_hgt); rd_u16b(&p_ptr->min_wid); rd_byte(&tmp_byte); rd_byte(&tmp_byte); /* Load cache status */ rd_byte(&tmp_byte); } /* Load wilderness seed */ rd_u32b(&wild_seed); /* Load wilderness map */ for (i = 0; i < wild_x_size; i++) { for (j = 0; j < wild_y_size; j++) { if (sf_version < 8) { /* Terrain */ rd_u16b(&wild[j][i].done.wild); /* Places */ rd_u16b(&tmp_u16b); wild[j][i].done.place = (byte)tmp_u16b; /* Info flag */ rd_byte(&wild[j][i].done.info); /* Monster Gen type */ rd_byte(&tmp_byte); wild[j][i].done.mon_gen = tmp_byte; } else { /* Changed size of data types. */ /* Terrain */ rd_u16b(&wild[j][i].done.wild); /* Places */ rd_byte(&wild[j][i].done.place); /* Info flag */ rd_byte(&wild[j][i].done.info); /* Monster Gen type */ rd_byte(&wild[j][i].done.mon_gen); /* Monster Probability */ rd_byte(&wild[j][i].done.mon_prob); } } } } /* The version when the format of the wilderness last changed */ #define VERSION_CHANGE_WILD 48 /* * Read the dungeon * * The monsters/objects must be loaded in the same order * that they were stored, since the actual indexes matter. */ static errr rd_dungeon(void) { int i; s16b py, px; int wid, hgt; u16b limit; cave_type *c_ptr; u16b dun_level_backup, px_back, py_back; bool ignore_stuff = FALSE; dun_type *dundata = place[p_ptr->place_num].dungeon; s16b cur_wid, cur_hgt; /* Get size */ Term_get_size(&wid, &hgt); /*** Basic info ***/ /* Header info */ rd_s16b(&p_ptr->depth); /* Read the base level */ if (!z_older_than(2, 2, 2)) { strip_bytes(2); } rd_s16b(&num_repro); rd_s16b(&py); rd_s16b(&px); rd_s16b(&cur_hgt); rd_s16b(&cur_wid); /* Old panel rows and columns */ strip_bytes(4); /* New panel bounds */ if (sf_version > 50) { rd_s16b(&p_ptr->panel_x1); rd_s16b(&p_ptr->panel_y1); rd_s16b(&p_ptr->panel_x2); rd_s16b(&p_ptr->panel_y2); } /* The player may not be in the dungeon */ character_dungeon = FALSE; /* Assume we are in the dungeon */ p_ptr->max_hgt = cur_hgt; p_ptr->min_hgt = 0; p_ptr->max_wid = cur_wid; p_ptr->min_wid = 0; if (sf_version < 7) { /* Make the wilderness */ dun_level_backup = p_ptr->depth; p_ptr->depth = 0; /* Save player location */ px_back = px; py_back = py; create_wilderness(); p_ptr->depth = dun_level_backup; /* if in the dungeon - restore the player location */ if (p_ptr->depth) { px = px_back; py = py_back; } /* Hack - do not load data into wilderness */ change_level(1); /* Get the new region */ create_region(dundata, cur_wid, cur_hgt, REGION_CAVE); incref_region(cur_region); /* Load dungeon map */ load_map(0, 0, cur_wid, cur_hgt); /* Restore the bounds */ p_ptr->max_hgt = cur_hgt; p_ptr->min_hgt = 0; p_ptr->max_wid = cur_wid; p_ptr->min_wid = 0; } /* The wilderness + dungeon format changed here */ else if (sf_version < 28) { /* Load wilderness data */ load_wild_data(); if (p_ptr->depth) { dun_level_backup = p_ptr->depth; change_level(p_ptr->depth); /* Save player location */ px_back = px; py_back = py; create_wilderness(); p_ptr->depth = dun_level_backup; change_level(p_ptr->depth); /* Get the new region */ create_region(dundata, cur_wid, cur_hgt, REGION_CAVE); incref_region(cur_region); /* Load dungeon map */ load_map(0, 0, cur_wid, cur_hgt); /* * Strip the wilderness map * A square WILD_BLOCK_SIZE * WILD_VIEW in width. */ strip_map(0, 0, 9 * 16, 9 * 16); px = px_back; py = py_back; /* Restore the bounds */ p_ptr->max_hgt = cur_hgt; p_ptr->min_hgt = 0; p_ptr->max_wid = cur_wid; p_ptr->min_wid = 0; } else { /* Strip the wilderness map */ strip_map(p_ptr->min_wid, p_ptr->min_hgt, p_ptr->max_wid, p_ptr->max_hgt); /* Make a new wilderness */ create_wilderness(); /* Save location */ px = p_ptr->px; py = p_ptr->py; } } /* This doesn't do anything at the moment - but will in the future */ else if (sf_version < VERSION_CHANGE_WILD) { /* Load wilderness data */ load_wild_data(); if (p_ptr->depth) { dun_level_backup = p_ptr->depth; change_level(p_ptr->depth); /* Save player location */ px_back = px; py_back = py; create_wilderness(); p_ptr->depth = dun_level_backup; change_level(p_ptr->depth); /* Get the new region */ create_region(dundata, cur_wid, cur_hgt, REGION_CAVE); incref_region(cur_region); /* Load dungeon map */ load_map(0, 0, cur_wid, cur_hgt); px = px_back; py = py_back; /* Restore the bounds */ p_ptr->max_hgt = cur_hgt; p_ptr->min_hgt = 0; p_ptr->max_wid = cur_wid; p_ptr->min_wid = 0; } else { /* * Strip the wilderness map * A square WILD_BLOCK_SIZE * WILD_VIEW in width. */ strip_map(0, 0, 9 * 16, 9 * 16); /* Make a new wilderness */ create_wilderness(); /* Save location */ px = p_ptr->px; py = p_ptr->py; } } else { /* Load wilderness data */ load_wild_data(); change_level(p_ptr->depth); if (p_ptr->depth) { /* Get the new region */ create_region(dundata, cur_wid, cur_hgt, REGION_CAVE); incref_region(cur_region); /* Load dungeon map */ load_map(0, 0, cur_wid, cur_hgt); /* Restore the bounds, overwritten in change_level */ p_ptr->max_hgt = cur_hgt; p_ptr->min_hgt = 0; p_ptr->max_wid = cur_wid; p_ptr->min_wid = 0; } else { /* Load the wilderness */ load_map(p_ptr->min_wid, p_ptr->min_hgt, p_ptr->max_wid, p_ptr->max_hgt); } } /* Ignore stuff if loading old savefiles */ if (sf_version < VERSION_CHANGE_WILD) ignore_stuff = TRUE; /* Hack - restore player position */ p_ptr->px = px; p_ptr->py = py; /* Notice position */ Term_move_player(); /* Hack - wipe the stuff on this level... */ wipe_monsters(cur_region); /* * Objects are deleted after the monsters, * because monsters carry them. */ wipe_objects(cur_region); /* * The following line wrecks stores made in create_wilderness() * above. (Do we need this line at all?) -SF- 2.7.3 */ /* wipe_fields(cur_region); */ /*** Objects ***/ /* Read the item count */ rd_u16b(&limit); /* Verify maximum */ if (limit > z_info->o_max) { note("Too many (%d) object entries!", limit); return (151); } /* * No objects yet */ o_cnt = 0; /* Read the dungeon items */ for (o_max = 1; o_max < limit; o_max++) { object_type *o_ptr; /* Acquire place */ o_ptr = &o_list[o_max]; /* Read the item */ rd_item(o_ptr); /* Hack - import player inventory properly */ o_ptr->allocated = TRUE; /* Real item? */ if (o_ptr->k_idx) { /* Hack - ignore items */ if (ignore_stuff && (o_ptr->ix || o_ptr->iy) && !in_bounds2(o_ptr->ix, o_ptr->iy)) { object_wipe(o_ptr); continue; } /* Count objects */ o_cnt++; /* Dungeon items */ if (o_ptr->ix || o_ptr->iy) { /* Hack - set region of object if is in the dungeon */ o_ptr->region = cur_region; /* Access the item location */ c_ptr = area(o_ptr->ix, o_ptr->iy); /* * This is so much of a hack it hurts. We really need * to have a loop... or something. */ /* XXX XXX Mega-hack - build a stack */ o_ptr->next_o_idx = c_ptr->o_idx; /* Place the object */ c_ptr->o_idx = o_max; } } } /* Repair inventory information */ if (sf_version < 37) { object_type *o_ptr; for (i = 0; i < 24; i++) { o_ptr = &old_inventory[i]; /* Do we have a real object? */ if (o_ptr->k_idx) { /* Carry it */ (void)inven_carry(o_ptr); } } } /*** Monsters ***/ /* Read the monster count */ rd_u16b(&limit); /* Hack -- verify */ if (limit > z_info->m_max) { note("Too many (%d) monster entries!", limit); return (161); } /* Read the monsters */ for (i = 1; i < limit; i++) { int m_idx; monster_type *m_ptr; monster_race *r_ptr; /* Get a new record */ m_idx = m_pop(); /* Acquire monster */ m_ptr = &m_list[m_idx]; /* Read the monster */ rd_monster(m_ptr); /* Hack - set region of monster */ m_ptr->region = cur_region; if (!ignore_stuff && in_bounds2(m_ptr->fx, m_ptr->fy)) { /* Oops */ if (i != m_idx) { note("Monster allocation error (%d <> %d)", i, m_idx); return (162); } /* Access grid */ c_ptr = area(m_ptr->fx, m_ptr->fy); /* Mark the location */ c_ptr->m_idx = m_idx; /* Access race */ r_ptr = &r_info[m_ptr->r_idx]; /* Count XXX XXX XXX */ r_ptr->cur_num++; } else { /* Delete objects */ delete_object_list(&m_ptr->hold_o_idx); /* Hack - just delete the monster */ (void)WIPE(m_ptr, monster_type); /* Count monsters */ m_cnt--; } } if (sf_version > 11) { /*** Fields ***/ /* Read the field count */ rd_u16b(&limit); /* Verify maximum */ if (limit > z_info->fld_max) { note("Too many (%d) field entries!", limit); return (151); } /* Read the fields */ for (i = 1; i < limit; i++) { field_type temp_field; field_type *f_ptr = &temp_field; /* Read the field */ rd_field(f_ptr); /* Hack - set region of field */ f_ptr->region = cur_region; if (!ignore_stuff && in_bounds2(f_ptr->fx, f_ptr->fy)) { /* Access the fields location */ c_ptr = area(f_ptr->fx, f_ptr->fy); /* Build a stack */ field_add(f_ptr, c_ptr); } } } /*** Success ***/ /* Regenerate the dungeon for old savefiles and corrupted panic-saves */ if ((py == 0) || (px == 0)) { character_dungeon = FALSE; } else { /* The dungeon is ready */ character_dungeon = TRUE; } /* Hack - make new level only after objects + monsters are loaded */ if (sf_version < 7) { /* enter the level */ change_level(p_ptr->depth); if (p_ptr->depth) { /* Restore the bounds */ p_ptr->max_hgt = cur_hgt; p_ptr->min_hgt = 0; p_ptr->max_wid = cur_wid; p_ptr->min_wid = 0; } else { character_dungeon = FALSE; } } /* * Set the trap detected flag. * * This is done here because it needs to be below all calls * to "change_level()" */ p_ptr->state.detected = player_detected; /* Success */ return (0); } /* * Strip old (Pre 2.7.0) quest info from the savefile */ static void strip_quests(u16b num) { int i; s16b status; for (i = 0; i < num; i++) { if (i < z_info->q_max) { rd_s16b(&status); if (!z_older_than(2, 2, 0)) { strip_bytes(2); } /* Load quest status if quest is running */ if (status == 1) { strip_bytes(6); if (z_older_than(2, 2, 0)) { strip_bytes(2); } strip_bytes(2); /* Load quest item index */ if (!z_older_than(2, 2, 1)) { strip_bytes(2); } /* Load quest flags */ if (!z_older_than(2, 2, 3)) { strip_bytes(1); } if (z_older_than(2, 2, 0)) { strip_bytes(40); } } } /* Ignore the empty quests from old versions */ else { /* Ignore quest status */ strip_bytes(2); /* Ignore quest level */ if (!z_older_than(2, 2, 0)) { strip_bytes(2); } /* * We don't have to care about the other info, * since status should be 0 for these quests anyway */ } } } /* * Load the quests */ static void rd_quests(int max_quests) { int i, q_idx; quest_type *q_ptr; for (i = 1; i < max_quests; i++) { q_idx = q_pop(); /* Paranoia (We've already tested < q_max) */ if (!q_idx) quit("Trying to load too many quests!"); q_ptr = &quest[q_idx]; /* Generic information */ rd_byte(&q_ptr->status); rd_byte(&q_ptr->flags); rd_byte(&q_ptr->type); rd_byte(&q_ptr->item); rd_u16b(&q_ptr->place); rd_u16b(&q_ptr->shop); rd_u16b(&q_ptr->reward); rd_byte(&q_ptr->c_type); rd_byte(&q_ptr->x_type); rd_u32b(&q_ptr->timeout); rd_string(q_ptr->name, 128); /* Data - quest-type specific */ switch (q_ptr->type) { case QUEST_TYPE_NONE: { /* Un-initialised quests */ break; } case QUEST_TYPE_BOUNTY: { /* Bounty quests */ rd_u16b(&q_ptr->data.bnt.r_idx); rd_u16b(&q_ptr->data.bnt.cur_num); rd_u16b(&q_ptr->data.bnt.max_num); break; } case QUEST_TYPE_DUNGEON: { /* Dungeon quests */ rd_u16b(&q_ptr->data.dun.r_idx); rd_u16b(&q_ptr->data.dun.level); rd_s16b(&q_ptr->data.dun.cur_num); rd_s16b(&q_ptr->data.dun.max_num); rd_s16b(&q_ptr->data.dun.num_mon); break; } case QUEST_TYPE_WILD: { /* Wilderness quests */ rd_u16b(&q_ptr->data.wld.place); rd_u16b(&q_ptr->data.wld.data); rd_byte(&q_ptr->data.wld.depth); break; } case QUEST_TYPE_MESSAGE: { /* Message quests */ rd_u16b(&q_ptr->data.msg.place); rd_u16b(&q_ptr->data.msg.shop); break; } case QUEST_TYPE_FIND_ITEM: { /* Find item quests */ rd_u16b(&q_ptr->data.fit.a_idx); rd_u16b(&q_ptr->data.fit.place); /* The artifact is a quest item */ SET_FLAG(&a_info[q_ptr->data.fit.a_idx], TR_QUESTITEM); break; } case QUEST_TYPE_FIND_PLACE: { /* Find place quests */ rd_u16b(&q_ptr->data.fpl.place); break; } default: { /* Unknown quest type... panic */ quit("Loading unknown quest type."); } } } } /* * Actually read the savefile */ static errr rd_savefile_new_aux(void) { int i, j; int tempx, tempy; byte tmp8u; u16b tmp16u; u32b tmp32u; u32b n_x_check, n_v_check; u32b o_x_check, o_v_check; u16b max_towns_load; u16b max_quests_load; /* Mention the savefile version */ note("Loading a %d.%d.%d savefile...", z_major, z_minor, z_patch); /* Strip the version bytes */ strip_bytes(4); /* Hack -- decrypt */ xor_byte = sf_extra; /* Clear the checksums */ v_check = 0L; x_check = 0L; #if SAVEFILE_VERSION /* Read the version number of the savefile */ if (!z_older_than(2, 2, 8) && !(z_major == 2 && z_minor == 3 && z_patch == 0)) rd_u32b(&sf_version); #endif /* SAVEFILE_VERSION */ /* Operating system info */ rd_u32b(&sf_xtra); /* Time of savefile creation */ rd_u32b(&sf_when); /* Number of resurrections */ rd_u16b(&sf_lives); /* Number of times played */ rd_u16b(&sf_saves); /* Later use (always zero) */ rd_u32b(&tmp32u); /* Later use (always zero) */ rd_u32b(&tmp32u); /* Read RNG state */ rd_randomizer(); if (arg_fiddle) note("Loaded Randomizer Info"); /* Then the options */ rd_options(); if (arg_fiddle) note("Loaded Option Flags"); /* * Munchkin players are marked * * XXX - should be replaced with a better method, * after the new scorefile-handling is implemented. */ if (munchkin_death) { /* Mark savefile */ p_ptr->state.noscore |= 0x0001; } /* Then the "messages" */ rd_messages(); if (arg_fiddle) note("Loaded Messages"); /* Monster Memory */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > z_info->r_max) { note("Too many (%u) monster races!", tmp16u); return (21); } /* Read the available records */ for (i = 0; i < tmp16u; i++) { /* Read the lore */ rd_lore(i); } /* Pre 2.2.0 version (old r_info.txt) */ if (z_older_than(2, 2, 0)) { monster_race *r_ptr; for (i = 0; i < z_info->r_max; i++) { /* Access that monster */ r_ptr = &r_info[i]; /* Hack -- Reset the death counter */ r_ptr->max_num = 100; if (FLAG(r_ptr, RF_UNIQUE)) r_ptr->max_num = 1; if (FLAG(r_ptr, RF_UNIQUE_7)) r_ptr->max_num = 7; } } if (arg_fiddle) note("Loaded Monster Memory"); /* Object Memory */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > z_info->k_max) { note("Too many (%u) object kinds!", tmp16u); return (22); } /* Read the object memory */ for (i = 0; i < tmp16u; i++) { object_kind *k_ptr = &k_info[i]; rd_byte(&tmp8u); k_ptr->aware = (tmp8u & 0x01) ? TRUE : FALSE; k_ptr->tried = (tmp8u & 0x02) ? TRUE : FALSE; } if (arg_fiddle) note("Loaded Object Memory"); /* Number of towns */ rd_u16b(&max_towns_load); /* 2.2.2 or older version */ if (z_older_than(2, 2, 3)) { /* Ignore higher numbers of towns */ if (max_towns_load > z_info->wp_max) max_towns_load = z_info->wp_max; } /* Incompatible save files */ if (max_towns_load > z_info->wp_max) { note("Too many (%u) towns!", max_towns_load); return (23); } /* Number of quests */ rd_u16b(&max_quests_load); /* Ignore old quests */ if (sf_version < 30) { strip_quests(max_quests_load); /* Reinitialise the quests when loading an old version */ init_player_quests(); } /* Newer versions */ else { /* Incompatible save files */ if (max_quests_load > z_info->q_max) { note("Too many (%u) quests!", max_quests_load); return (23); } rd_quests(max_quests_load); } if (arg_fiddle) note("Loaded Quests"); /* Only in 2.2.1 and 2.2.2 */ if (!z_older_than(2, 2, 1) && z_older_than(2, 2, 3)) { /* "Hard quests" flag */ rd_byte((byte *)&tmp8u); /* Inverted "Wilderness" flag */ rd_byte((byte *)&vanilla_town); vanilla_town = !vanilla_town; } /* Position in the wilderness */ rd_s32b(&p_ptr->wilderness_x); rd_s32b(&p_ptr->wilderness_y); /* Size of the wilderness */ rd_s32b(&wild_x_size); rd_s32b(&wild_y_size); /* Incompatible save files */ if ((wild_x_size > WILD_SIZE) || (wild_y_size > WILD_SIZE)) { note("Wilderness is too big (%u/%u)!", wild_x_size, wild_y_size); return (23); } /* Hack - if size is zero - set to WILD_SIZE */ if ((wild_x_size == 0) && (wild_y_size == 0)) { wild_x_size = WILD_SIZE; wild_y_size = WILD_SIZE; } /* Hack - set size of wilderness to x size only */ max_wild = wild_x_size; tempx = (int)p_ptr->wilderness_x / 16; tempy = (int)p_ptr->wilderness_y / 16; /* Get corner of visible region */ shift_in_bounds(&tempx, &tempy); /* Set corner of visible region */ p_ptr->old_wild_x = tempx; p_ptr->old_wild_y = tempy; /* Ignore the seeds from old versions */ if (sf_version < 9) { /* Load the wilderness seeds */ for (i = 0; i < wild_x_size; i++) { for (j = 0; j < wild_y_size; j++) { /* Ignore seeds */ rd_u32b(&tmp32u); } } } /* Load the Artifacts */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > z_info->a_max) { note("Too many (%u) artifacts!", tmp16u); return (24); } /* Read the artifact flags */ for (i = 0; i < tmp16u; i++) { rd_byte(&tmp8u); a_info[i].cur_num = tmp8u; rd_byte(&tmp8u); rd_byte(&tmp8u); rd_byte(&tmp8u); } if (arg_fiddle) note("Loaded Artifacts"); /* Read the extra stuff */ rd_extra(); if (arg_fiddle) note("Loaded extra information"); /* Read the player_hp array */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > PY_MAX_LEVEL) { note("Too many (%u) hitpoint entries!", tmp16u); return (25); } /* Read the player_hp array */ for (i = 0; i < tmp16u; i++) { rd_s16b(&p_ptr->player_hp[i]); } /* Important -- Initialize the sex */ sp_ptr = &sex_info[p_ptr->rp.psex]; /* Important -- Initialize the race/class */ rp_ptr = &race_info[p_ptr->rp.prace]; cp_ptr = &class_info[p_ptr->rp.pclass]; /* Important -- Initialize the magic */ mp_ptr = &magic_info[p_ptr->rp.pclass]; /* Read spell info */ rd_u32b(&p_ptr->spell.r[0].learned); rd_u32b(&p_ptr->spell.r[1].learned); rd_u32b(&p_ptr->spell.r[0].worked); rd_u32b(&p_ptr->spell.r[1].worked); rd_u32b(&p_ptr->spell.r[0].forgotten); rd_u32b(&p_ptr->spell.r[1].forgotten); for (i = 0; i < PY_MAX_SPELLS; i++) { rd_byte(&p_ptr->spell.order[i]); } /* Read the inventory */ if (rd_inventory()) { note("Unable to read inventory"); return (21); } /* Read number of towns */ rd_u16b(&tmp16u); place_count = tmp16u; /* Paranoia */ if (place_count > z_info->wp_max) { note("Error - increase number of towns in misc.txt"); return (33); } /* Empty the store stock cache */ store_cache_num = 0; if (sf_version < 8) { /* Read the stores */ rd_u16b(&tmp16u); for (i = 1; i < place_count; i++) { place[i].numstores = (byte) tmp16u; /* Allocate the stores */ C_MAKE(place[i].store, place[i].numstores, store_type); /* HACK - ignore the empty towns */ if (z_older_than(2, 2, 3) && (i >= 6)) { for (j = 0; j < tmp16u; j++) { /* Read the info into the empty town 5 (R'Lyeh) */ rd_store(5, j); } } else { for (j = 0; j < tmp16u; j++) { rd_store(i, j); } } /* Assume we have a dungeon here */ MAKE(place[i].dungeon, dun_type); } } else { /* Get the town data */ for (i = 1; i < place_count; i++) { place_type *pl_ptr = &place[i]; /* RNG seed */ rd_u32b(&pl_ptr->seed); /* Number of stores */ rd_byte(&pl_ptr->numstores); /* Type */ rd_u16b(&tmp16u); /* Hack.... used to be a u16b, but only ever used a bytes worth */ pl_ptr->type = (byte)tmp16u; if (sf_version > 21) { rd_byte(&pl_ptr->data); } /* Gates */ if (sf_version > 22) { rd_byte(&pl_ptr->gates_x[0]); rd_byte(&pl_ptr->gates_x[1]); rd_byte(&pl_ptr->gates_x[2]); rd_byte(&pl_ptr->gates_x[3]); rd_byte(&pl_ptr->gates_y[0]); rd_byte(&pl_ptr->gates_y[1]); rd_byte(&pl_ptr->gates_y[2]); rd_byte(&pl_ptr->gates_y[3]); } /* Locatation */ rd_byte(&pl_ptr->x); rd_byte(&pl_ptr->y); /* Size */ if (sf_version > 29) { rd_byte(&pl_ptr->xsize); rd_byte(&pl_ptr->ysize); rd_u16b(&pl_ptr->quest_num); rd_byte(&pl_ptr->monst_type); } else { /* Need to create town size as default */ pl_ptr->xsize = 8; pl_ptr->ysize = 8; } /* Name */ if (sf_version < 26) { char temp_buffer[32]; rd_string(temp_buffer, 32); /* Get a new name */ if (vanilla_town) { strcpy(pl_ptr->name, "Town"); } else { select_town_name(pl_ptr->name, pl_ptr->data); } } else { rd_string(pl_ptr->name, T_NAME_LEN); } if (sf_version < 42) { /* Assume we have a dungeon here */ MAKE(place[i].dungeon, dun_type); } else { byte dungeon; rd_byte(&dungeon); if (dungeon) { dun_type *dun_ptr; /* Create a dungeon here */ MAKE(place[i].dungeon, dun_type); dun_ptr = place[i].dungeon; /* Object theme */ rd_byte(&dun_ptr->theme.treasure); rd_byte(&dun_ptr->theme.combat); rd_byte(&dun_ptr->theme.magic); rd_byte(&dun_ptr->theme.tools); /* Habitat */ rd_u32b(&dun_ptr->habitat); /* Levels in dungeon */ rd_byte(&dun_ptr->min_level); rd_byte(&dun_ptr->max_level); if (vanilla_town) { dun_ptr->min_level = 1; dun_ptr->max_level = MAX_DEPTH - 1; } /* Rating */ rd_s16b(&dun_ptr->rating); /* Extra dungeon info */ if (sf_version > 43) { rd_u16b(&dun_ptr->rooms); rd_byte(&dun_ptr->floor); rd_byte(&dun_ptr->liquid); rd_byte(&dun_ptr->flags); /* Recall depth */ if (sf_version > 46) { rd_byte(&dun_ptr->recall_depth); } else { /* Hack - use old one-dungeon depth */ dun_ptr->recall_depth = (byte) p_ptr->depth; /* Make sure the value is in bounds */ if (dun_ptr->recall_depth < dun_ptr->min_level) { dun_ptr->recall_depth = dun_ptr->min_level; } if (dun_ptr->recall_depth > dun_ptr->max_level) { dun_ptr->recall_depth = dun_ptr->max_level; } } } } } /* Allocate the stores */ C_MAKE(pl_ptr->store, pl_ptr->numstores, store_type); /* Get the stores of all towns */ for (j = 0; j < pl_ptr->numstores; j++) { rd_store(i, j); } } } /* Read the pet command settings */ if (sf_version > 2) { rd_s16b(&p_ptr->pet_follow_distance); rd_byte(&p_ptr->pet_open_doors); rd_byte(&p_ptr->pet_pickup_items); } else if (!z_older_than(2, 2, 3)) { rd_byte(&tmp8u); p_ptr->pet_follow_distance = tmp8u; rd_byte(&p_ptr->pet_open_doors); rd_byte(&p_ptr->pet_pickup_items); } else { /* Default pet command settings */ p_ptr->pet_follow_distance = PET_FOLLOW_DIST; p_ptr->pet_open_doors = FALSE; p_ptr->pet_pickup_items = FALSE; } /* I'm not dead yet... */ if (!p_ptr->state.is_dead) { /* Dead players have no dungeon */ note("Restoring Dungeon..."); if (rd_dungeon()) { note("Error reading dungeon data"); return (34); } /* Read the ghost info */ rd_ghost(); if (!z_older_than(2, 2, 4)) { s32b tmp32s; rd_s32b(&tmp32s); strip_bytes(tmp32s); } } /* Save the checksum */ n_v_check = v_check; /* Read the old checksum */ rd_u32b(&o_v_check); /* Verify */ if (o_v_check != n_v_check) { note("Invalid checksum"); return (11); } /* Save the encoded checksum */ n_x_check = x_check; /* Read the checksum */ rd_u32b(&o_x_check); /* Verify */ if (o_x_check != n_x_check) { note("Invalid encoded checksum"); return (11); } /* Success */ return (0); } /* * Actually read the savefile */ errr rd_savefile_new(void) { errr err; /* Grab permissions */ safe_setuid_grab(); /* The savefile is a binary file */ fff = my_fopen(savefile, "rb"); /* Drop permissions */ safe_setuid_drop(); /* Paranoia */ if (!fff) return (-1); /* Call the sub-function */ err = rd_savefile_new_aux(); /* Check for errors */ if (ferror(fff)) err = -1; /* Close the file */ my_fclose(fff); /* Result */ return (err); } zangband/src/maid-grf.c0000644000000000000000000020755610250356274014004 0ustar rootroot /* File: maid-grf.c */ /* Purpose: Interface for graphical ports */ /* * Copyright (c) 2002 S. Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "maid-grf.h" #ifdef SUPPORT_GAMMA /* Table of gamma values */ byte gamma_table[256]; /* Table of ln(x/256) * 256 for x going from 0 -> 255 */ static const s16b gamma_helper[256] = { 0, -1420, -1242, -1138, -1065, -1007, -961, -921, -887, -857, -830, -806, -783, -762, -744, -726, -710, -694, -679, -666, -652, -640, -628, -617, -606, -596, -586, -576, -567, -577, -549, -541, -532, -525, -517, -509, -502, -495, -488, -482, -475, -469, -463, -457, -451, -455, -439, -434, -429, -423, -418, -413, -408, -403, -398, -394, -389, -385, -380, -376, -371, -367, -363, -359, -355, -351, -347, -343, -339, -336, -332, -328, -325, -321, -318, -314, -311, -308, -304, -301, -298, -295, -291, -288, -285, -282, -279, -276, -273, -271, -268, -265, -262, -259, -257, -254, -251, -248, -246, -243, -241, -238, -236, -233, -231, -228, -226, -223, -221, -219, -216, -214, -212, -209, -207, -205, -203, -200, -198, -196, -194, -192, -190, -188, -186, -184, -182, -180, -178, -176, -174, -172, -170, -168, -166, -164, -162, -160, -158, -156, -155, -153, -151, -149, -147, -146, -144, -142, -140, -139, -137, -135, -134, -132, -130, -128, -127, -125, -124, -122, -120, -119, -117, -116, -114, -112, -111, -109, -108, -106, -105, -103, -102, -100, -99, -97, -96, -95, -93, -92, -90, -89, -87, -86, -85, -83, -82, -80, -79, -78, -76, -75, -74, -72, -71, -70, -68, -67, -66, -65, -63, -62, -61, -59, -58, -57, -56, -54, -53, -52, -51, -50, -48, -47, -46, -45, -44, -42, -41, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30, -29, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1 }; /* * Build the gamma table so that floating point isn't needed. * * ANGBAND_X11_GAMMA is * 256 * (1 / gamma), rounded to integer. A recommended value * is 183, which is an approximation of the Macintosh hardware * gamma of 1.4. * * gamma ANGBAND_X11_GAMMA * ----- ----------------- * 1.2 213 * 1.25 205 * 1.3 197 * 1.35 190 * 1.4 183 * 1.45 177 * 1.5 171 * 1.6 160 * 1.7 151 * ... * * XXX XXX The environment variable, or better, * the interact with colours command should allow users * to specify gamma values (or gamma value * 100). */ void build_gamma_table(int gamma) { int i, n; /* * value is the current sum. * diff is the new term to add to the series. */ long value, diff; /* Paranoia */ if (gamma < 0) gamma = 0; if (gamma > 255) gamma = 255; /* Hack - convergence is bad in these cases. */ gamma_table[0] = 0; gamma_table[255] = 255; for (i = 1; i < 255; i++) { /* * Initialise the Taylor series * * value and diff have been scaled by 256 */ n = 1; value = 256 * 256; diff = ((long)gamma_helper[i]) * (gamma - 256); while (diff) { value += diff; n++; /* * Use the following identiy to calculate the gamma table. * exp(x) = 1 + x + x^2/2 + x^3/(2*3) + x^4/(2*3*4) +... * * n is the current term number. * * The gamma_helper array contains a table of * ln(x/256) * 256 * This is used because a^b = exp(b*ln(a)) * * In this case: * a is i / 256 * b is gamma. * * Note that everything is scaled by 256 for accuracy, * plus another factor of 256 for the final result to * be from 0-255. Thus gamma_helper[] * gamma must be * divided by 256*256 each iteration, to get back to * the original power series. */ diff = (((diff / 256) * gamma_helper[i]) * (gamma - 256)) / (256 * n); } /* * Store the value in the table so that the * floating point pow function isn't needed. */ gamma_table[i] = ((long)(value / 256) * i) / 256; } } #endif /* SUPPORT_GAMMA */ /* * Get the name of the default font to use for the term. */ cptr get_default_font(int term_num) { cptr font; char buf[80]; /* Window specific font name */ strnfmt(buf, 80, "ANGBAND_X11_FONT_%d", term_num); /* Check environment for that font */ font = getenv(buf); /* Check environment for "base" font */ if (!font) font = getenv("ANGBAND_X11_FONT"); /* No environment variables, use default font */ if (!font) { switch (term_num) { case 0: { font = DEFAULT_X11_FONT_0; break; } case 1: { font = DEFAULT_X11_FONT_1; break; } case 2: { font = DEFAULT_X11_FONT_2; break; } case 3: { font = DEFAULT_X11_FONT_3; break; } case 4: { font = DEFAULT_X11_FONT_4; break; } case 5: { font = DEFAULT_X11_FONT_5; break; } case 6: { font = DEFAULT_X11_FONT_6; break; } case 7: { font = DEFAULT_X11_FONT_7; break; } default: { font = DEFAULT_X11_FONT; } } } return (font); } #ifdef USE_GRAPHICS /* * Make sure the graphical tiles we want are available. * * Note - we _must_ be passed an array 1024 in size for the filename. */ bool pick_graphics(int graphics, int *xsize, int *ysize, char *filename) { int old_graphics = use_graphics; use_graphics = GRAPHICS_NONE; use_transparency = FALSE; if ((graphics == GRAPHICS_ANY) || (graphics == GRAPHICS_DAVID_GERVAIS)) { /* Try the "32x32.bmp" file */ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/32x32.bmp"); /* Use the "32x32.bmp" file if it exists */ if (0 == fd_close(fd_open(filename, O_RDONLY))) { use_transparency = TRUE; *xsize = 32; *ysize = 32; } use_graphics = GRAPHICS_DAVID_GERVAIS; /* Did we change the graphics? */ return (old_graphics != use_graphics); } /* We failed, or we want 16x16 graphics */ if ((graphics == GRAPHICS_ANY) || (graphics == GRAPHICS_ADAM_BOLT) || (graphics == GRAPHICS_HALF_3D)) { /* Try the "16x16.bmp" file */ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp"); /* Use the "16x16.bmp" file if it exists */ if (0 == fd_close(fd_open(filename, O_RDONLY))) { use_transparency = TRUE; *xsize = 16; *ysize = 16; /* Use graphics */ if (graphics == GRAPHICS_HALF_3D) { use_graphics = GRAPHICS_HALF_3D; } else { use_graphics = GRAPHICS_ADAM_BOLT; } /* Did we change the graphics? */ return (old_graphics != use_graphics); } } /* We failed, or we want 8x8 graphics */ if ((graphics == GRAPHICS_ANY) || (graphics == GRAPHICS_ORIGINAL)) { /* Try the "8x8.bmp" file */ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp"); /* Use the "8x8.bmp" file if it exists */ if (0 == fd_close(fd_open(filename, O_RDONLY))) { /* Use graphics */ use_graphics = GRAPHICS_ORIGINAL; *xsize = 8; *ysize = 8; } } /* Did we change the graphics? */ return (old_graphics != use_graphics); } /* * Is a square in a bigtiled region? */ bool is_bigtiled(int x, int y) { if ((use_bigtile) && (y >= Term->scr->big_y1) && (y <= Term->scr->big_y2) && (x >= Term->scr->big_x1)) { return (TRUE); } return (FALSE); } void toggle_bigtile(void) { if (use_bigtile) { /* Hack - disable bigtile mode */ Term_bigregion(-1, -1, -1); use_bigtile = FALSE; } else { use_bigtile = TRUE; } /* Hack - redraw everything + recalc bigtile regions */ angband_term[0]->resize_hook(); } #endif /* USE_GRAPHICS */ /* * The callbacks */ static callback_list *callbacks[CALL_MAX]; /* * Initialise the callbacks */ void init_term_callbacks(void) { /* Wipe the array */ (void) C_WIPE(callbacks, CALL_MAX, callback_list *); } /* * Free the callbacks */ void free_term_callbacks(void) { int i; callback_list *p, *p_next; for (i = 0; i < CALL_MAX; i++) { p = callbacks[i]; while (p) { p_next = p->next; FREE(p); p = p_next; } } } /* * Register a callback */ void set_callback(callback_type call_func, int number, vptr data) { /* Create a new callback */ callback_list *node; MAKE(node, callback_list); /* Save information into node */ node->next = callbacks[number]; node->data = data; node->func = call_func; /* Insert at the head of the list */ callbacks[number] = node; } void del_callback(int number, vptr data) { callback_list **p; callback_list *temp; p = &callbacks[number]; /* Scan the list */ while (*p) { /* A match? */ if ((*p)->data == data) { /* Delete this node */ temp = *p; *p = (*p)->next; FREE(temp); return; } /* Point to next node */ p = &((*p)->next); } quit("Callback does not exist"); } /* * Code for the overhead mini-map * * This is used by the borg to store the map information. * It is used by some ports to display a mini-map. It * is used by the tk port to display nearly everything. * * It is also used by the "overhead map" term type. */ /* List of 16x16 blocks for the overhead map */ map_blk_ptr_ptr *map_cache; /* Refcount for map cache */ s16b *map_cache_refcount; /* Location of cache blocks */ int *map_cache_x; int *map_cache_y; /* The map itself - grid of 16x16 blocks*/ int **map_grid; /* Player location */ static int player_x = 0; static int player_y = 0; /* * Access the player location */ void map_get_player(int *x, int *y) { *x = player_x; *y = player_y; } /* * Clear the map when changing a level. */ static void clear_map(void) { int i, j; /* Erase the map */ for (i = 0; i < WILD_SIZE; i++) { for (j = 0; j < WILD_SIZE; j++) { /* Set unused */ map_grid[i][j] = -1; } } /* Erase the cache */ for (i = 0; i < MAP_CACHE; i++) { map_cache_refcount[i] = 0; /* Flag that the block isn't used */ map_cache_x[i] = -1; } } /* * Create the map information */ void init_overhead_map(void) { int i, j; /* Make the list of pointers to blocks */ C_MAKE(map_cache, MAP_CACHE, map_blk_ptr_ptr); /* Refcount for cache blocks */ C_MAKE(map_cache_refcount, MAP_CACHE, s16b); /* Cache block locations */ C_MAKE(map_cache_x, MAP_CACHE, int); C_MAKE(map_cache_y, MAP_CACHE, int); /* Allocate each block */ for (i = 0; i < MAP_CACHE; i++) { /* Allocate block */ C_MAKE(map_cache[i], WILD_BLOCK_SIZE, map_blk_ptr); /* Allocate rows of a block */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { C_MAKE(map_cache[i][j], WILD_BLOCK_SIZE, map_block); } } /* Allocate the overhead map itself */ C_MAKE(map_grid, WILD_SIZE, int *); for (i = 0; i < WILD_SIZE; i++) { /* Allocate one row of the wilderness */ C_MAKE(map_grid[i], WILD_SIZE, int); } /* Initialize */ clear_map(); } /* * Delete the overhead map */ void del_overhead_map(void) { int i, j; /* Free refcount for cache blocks */ FREE(map_cache_refcount); /* Cache block locations */ FREE(map_cache_x); FREE(map_cache_y); /* Delete each block */ for (i = 0; i < MAP_CACHE; i++) { /* Deallocate rows of a block */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { FREE(map_cache[i][j]); } /* Free block */ FREE(map_cache[i]); } /* Free the list of pointers to blocks */ FREE(map_cache); for (i = 0; i < WILD_SIZE; i++) { /* Free one row of the wilderness */ FREE(map_grid[i]); } /* Free the overhead map itself */ FREE(map_grid); } /* * Erase a block */ static void clear_block(int block) { int i, j; map_block *mb_ptr; /* Wipe each square */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { mb_ptr = &map_cache[block][i][j]; (void)WIPE(mb_ptr, map_block); } } /* Was this used? */ if (map_cache_x[block] != -1) { /* Mark map block as unused */ map_grid[map_cache_y[block]][map_cache_x[block]] = -1; /* Set "unused block" flag */ map_cache_x[block] = -1; } } /* * Find an empty block to use */ static int get_empty_block(void) { int i; int dist, best_dist = 0; int best_block = 0; int px, py; /* Get player block location */ px = player_x / 16; py = player_y / 16; /* Scan for a used but out of los block */ for (i = 0; i < MAP_CACHE; i++) { /* Get block out of los */ if (map_cache_refcount[i]) continue; /* Check to see if unused */ if (map_cache_x[i] < 0) { best_block = i; break; } /* Get rough dist from player */ dist = ABS(map_cache_x[i] - px) + ABS(map_cache_y[i] - py); /* Save furthest block */ if (dist > best_dist) { best_dist = dist; best_block = i; } } /* Erase the block */ clear_block(best_block); /* Return the furthest unused block from the player */ return (best_block); } /* * Is the location in bounds on the map? */ bool map_in_bounds(int x, int y) { if (x < 0 || x >= WILD_BLOCK_SIZE * WILD_SIZE) return (FALSE); if (y < 0 || y >= WILD_BLOCK_SIZE * WILD_SIZE) return (FALSE); return (map_grid[y >> 4][x >> 4] != -1); } /* * Save information into a block location */ static void save_map_location(int x, int y, const term_map *map) { map_blk_ptr_ptr mbp_ptr; map_block *mb_ptr; int x1 = x / WILD_BLOCK_SIZE; int y1 = y / WILD_BLOCK_SIZE; int block_num; callback_list *callback; /* Does the location exist? */ if (!map_in_bounds(x, y)) { /* Create a new block there */ block_num = get_empty_block(); /* Set this block up */ mbp_ptr = map_cache[block_num]; /* Link to the map */ map_grid[y1][x1] = block_num; /* Save block coordinates */ map_cache_x[block_num] = x1; map_cache_y[block_num] = y1; } else { block_num = map_grid[y1][x1]; mbp_ptr = map_cache[block_num]; } mb_ptr = &mbp_ptr[y & 15][x & 15]; /* Increment refcount depending on visibility */ if (map->flags & MAP_ONCE) { /* Wasn't seen, and now is */ if (!(mb_ptr->flags & (MAP_ONCE))) { map_cache_refcount[block_num]++; } } else { /* Was seen, and now is not */ if (mb_ptr->flags & MAP_ONCE) { /* Paranoia */ if (!map_cache_refcount[block_num]) { quit("Decrementing invalid overhead map loc"); } map_cache_refcount[block_num]--; } } /* Save the tile data */ mb_ptr->a = map->a; mb_ptr->c = map->c; mb_ptr->ta = map->ta; mb_ptr->tc = map->tc; for (callback = callbacks[CALL_MAP_INFO]; callback; callback = callback->next) { /* Execute the callback */ ((map_info_hook_type)callback->func) (mb_ptr, map, callback->data); } /* Save the flags */ mb_ptr->flags = map->flags; /* Save the priority */ mb_ptr->priority = map->priority; #ifdef TERM_CAVE_MAP /* Save the information */ mb_ptr->terrain = map->terrain; mb_ptr->field = map->field; mb_ptr->object = map->object; mb_ptr->unknown = map->unknown; mb_ptr->monster = map->monster; mb_ptr->m_flags = map->m_flags; mb_ptr->m_hp = map->m_hp; #endif /* TERM_CAVE_MAP */ } /* * Save the player location */ static void set_player_location(int x, int y) { callback_list *callback; player_x = x; player_y = y; /* Tell the port that the player has moved */ for (callback = callbacks[CALL_PLAYER_MOVE]; callback; callback = callback->next) { /* Execute the callback */ ((player_move_hook_type)callback->func) (x, y, callback->data); } } /* * Get the information in the map */ map_block *map_loc(int x, int y) { return (&map_cache[map_grid[y / WILD_BLOCK_SIZE][x / WILD_BLOCK_SIZE]] [y & 15][x & 15]); } /* put the banners on the screen */ static void display_banner(wild_done_type *w_ptr) { int wid, hgt; place_type *pl_ptr; /* Get size */ Term_get_size(&wid, &hgt); /* Do we have a place here? */ pl_ptr = (w_ptr->place ? &place[w_ptr->place] : NULL); /* Show the place name, if it is on the map */ if (pl_ptr && (w_ptr->info & WILD_INFO_SEEN)) { cptr banner; cptr place_dir; int i; bool visited_town = FALSE; bool home_in_town = FALSE; bool castle_in_town = FALSE; /* Is it a town */ if (pl_ptr->numstores) { /* Upper banner */ banner = pl_ptr->name; /* Display town name */ put_fstr(1 + (wid - strlen(banner)) / 2, 0, banner); /* Find out if there are homes or castles here */ for (i = 0; i < pl_ptr->numstores; i++) { store_type *st_ptr = &pl_ptr->store[i]; /* Is there a home? */ if (st_ptr->type == BUILD_STORE_HOME) home_in_town = TRUE; /* Is there a castle? */ if (st_ptr->type == BUILD_CASTLE0 || st_ptr->type == BUILD_CASTLE1) castle_in_town = TRUE; /* Stores are not given coordinates until you visit a town */ if (st_ptr->x != 0 && st_ptr->y != 0) visited_town = TRUE; } /* Prevent knowledge from leaking out */ home_in_town &= visited_town; castle_in_town &= visited_town; /* Find out the lower banner */ if (home_in_town) { if (castle_in_town) { /* Town with home and castle */ banner = "Move around, press * for town, h for home, c for castle or any key to exit."; } else { /* Town with home and no castle */ banner = "Move around, press * for town, h for home or any key to exit."; } } /* Town with no home */ else { if (castle_in_town) { /* Town with castle and no home */ banner = "Move around, press * for town, c for castle or any key to exit."; } else { /* Town with no castle and no home */ banner = "Move around, press * for town or any key to exit."; } } /* Display lower banner */ put_fstr(1 + (wid - strlen(banner)) / 2, hgt - 1, banner); } /* So it is in the wilderness */ else { /* Display standard bottom line */ put_fstr(wid / 2 - 23, hgt - 1, "Move around or hit any other key to continue."); /* It is a wilderness dungeon */ if (pl_ptr->dungeon) { /* Fetch closest known town and direction */ banner = describe_quest_location(&place_dir, pl_ptr->x, pl_ptr->y, TRUE); /* Did the player go into the dungeon? */ if (pl_ptr->dungeon->recall_depth == 0) { /* It is still guarded by monsters */ banner = format("Guarded dungeon %s of %s.", place_dir, banner); } else { /* No monsters to guard it */ banner = format("Unguarded dungeon %s of %s.", place_dir, banner); } } /* It is a wilderness quest */ else { /* Fetch wilderness quest name */ banner = quest[pl_ptr->quest_num].name; } /* Display wilderness place name */ put_fstr((wid - strlen(banner)) / 2, 0, banner); } } else { /* Display standard bottom line */ put_fstr(wid / 2 - 23, hgt - 1, "Move around or hit any other key to continue."); } } /* Display info about the home in one town */ static bool dump_home_info(FILE *fff, int town) { int i, k; bool visited_town = FALSE; store_type *st_ptr; object_type *o_ptr; for (i = 0; i < place[town].numstores; i++) { st_ptr = &place[town].store[i]; /* Stores are not given coordinates until you visit a town */ if (st_ptr->x != 0 && st_ptr->y != 0) visited_town = TRUE; /* The only interest is homes */ if (st_ptr->type == BUILD_STORE_HOME) { /* Header with name of the town */ froff(fff, " [Home Inventory - %s]\n\n", place[i].name); /* Home -- if anything there */ if (st_ptr->stock) { char o_name[256]; /* Initialise counter */ k = 0; /* Dump all available items */ OBJ_ITT_START (st_ptr->stock, o_ptr) { /* Describe object */ object_desc(o_name, o_ptr, TRUE, 3, 256); /* Clean formatting escape sequences */ fmt_clean(o_name); /* List the item, inlcuding its colour */ froff(fff, " %s" CLR_SET_DEFAULT " %s\n", color_seq[tval_to_attr[o_ptr->tval]], o_name); /* Increment counter */ k++; } OBJ_ITT_END; /* Add an empty line */ froff(fff, "\n\n"); } /* The home is empty */ else { froff(fff, " [Empty]\n\n"); } } } /* No home, no show */ return (visited_town); } /* This function predicts whether keystroke c on a town has any effect */ static bool dump_info_test(char c, int town) { int i; bool visited_town = FALSE; bool build_found = FALSE; store_type *st_ptr; /* Paranoia */ if (place[town].numstores == 0) return (FALSE); /* Find out if this command makes sense */ switch (c) { case '*': { /* Display the list of shops always works */ return (TRUE); } case 'h': { /* Display the items in the home needs a home */ for (i = 0; i < place[town].numstores; i++) { st_ptr = &place[town].store[i]; /* Stores are not given coordinates until you visit a town */ if (st_ptr->x != 0 && st_ptr->y != 0) visited_town = TRUE; /* The only interest is homes */ if (st_ptr->type == BUILD_STORE_HOME) build_found = TRUE; } /* Return success */ return (build_found && visited_town); } case 'c': { /* Display the items in the home needs a home */ for (i = 0; i < place[town].numstores; i++) { st_ptr = &place[town].store[i]; /* Stores are not given coordinates until you visit a town */ if (st_ptr->x != 0 && st_ptr->y != 0) visited_town = TRUE; /* The only interest is homes */ if (st_ptr->type == BUILD_CASTLE0 || st_ptr->type == BUILD_CASTLE1) build_found = TRUE; } /* Return success */ return (build_found && visited_town); } } return (FALSE); } /* Show the knowledge the player has about a town */ static bool do_cmd_view_map_aux(char c, int town) { FILE *fff; char file_name[1024]; cptr title = NULL; /* Call this proc with a place that is a town */ if (place[town].numstores == 0) return (FALSE); /* go away if nothing will happen */ if (!dump_info_test(c, town)) return (FALSE); /* Open temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); /* Show what? */ switch (c) { case '*': { /* Display the list of shops */ dump_town_info(fff, town, FALSE); title = "Town info"; break; } case 'h': { /* Display the items in the home */ (void)dump_home_info(fff, town); title = "Home info"; break; } case 'c': { /* Display the quests taken */ dump_castle_info(fff, town); title = "Castle info"; break; } } /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, title, 0, 0); /* Remove the file */ (void)fd_kill(file_name); /* And curtain */ return (TRUE); } /* Keep the offset for the resize */ static int map_cx = 0; static int map_cy = 0; static void resize_big_map(void) { int cx, cy; wild_done_type *w_ptr; cx = map_cx; cy = map_cy; /* Make a new map */ display_map(&cx, &cy); /* Get wilderness square */ w_ptr = &wild[map_cy + p_ptr->py / WILD_BLOCK_SIZE] [map_cx + p_ptr->px / WILD_BLOCK_SIZE].done; /* print the banners */ display_banner(w_ptr); /* Show the cursor */ Term_gotoxy(cx, cy); } /* * Display a "small-scale" map of the dungeon for the player * * Currently, the "player" is displayed on the map. XXX XXX XXX */ void do_cmd_view_map(void) { int py = p_ptr->py; int px = p_ptr->px; int cy, cx; int wid, hgt; void (*hook) (void); /* No overhead map in vanilla town mode. */ if (!p_ptr->depth && vanilla_town) return; /* Remember what the resize hook was */ hook = angband_term[0]->resize_hook; /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = resize_big_map; /* Note */ prtf(0, 0, "Please wait..."); /* Flush */ Term_fresh(); /* Clear the screen */ Term_clear(); if (p_ptr->depth) { /* In the dungeon - All we have to do is display the map */ /* Get size */ Term_get_size(&wid, &hgt); /* No offset from player */ cx = 0; cy = 0; /* Match offset for the resize */ map_cx = cx; map_cy = cy; /* Display the map */ display_map(&cx, &cy); /* Wait for it */ put_fstr((wid - COL_MAP) / 2, hgt - 1, "Hit any key to continue"); /* Hilite the player */ Term_gotoxy(cx, cy); /* Get any key */ (void)inkey(); } else { /* Offset from player */ int x, y; /* Direction */ int d; /* Input character */ char c; wild_done_type *w_ptr; /* No offset yet */ x = 0; y = 0; /* In the wilderness - Display the map + move it around */ while (TRUE) { /* Reset offset of map */ cx = x; cy = y; /* Match offset for the resize */ map_cx = cx; map_cy = cy; display_map(&cx, &cy); /* Get wilderness square */ w_ptr = &wild[y + py / WILD_BLOCK_SIZE][x + px / WILD_BLOCK_SIZE].done; /* Get the banners on the screen */ display_banner(w_ptr); /* Show the cursor */ Term_gotoxy(cx, cy); /* Draw it */ Term_fresh(); /* Get a response */ c = inkey(); /* Allow a redraw */ if (c == KTRL('R')) { /* Do the redraw */ do_cmd_redraw(); continue; } /* On a town? -- MT */ if (w_ptr->place) { /* Check if this is an info command */ if (do_cmd_view_map_aux(c, w_ptr->place)) continue; } /* Done if not a direction */ d = get_keymap_dir(c); if (!d) break; x += ddx[d]; y += ddy[d]; /* Bounds checking */ if (x + px / WILD_BLOCK_SIZE < 0) { x = -px / WILD_BLOCK_SIZE; } if (y + py / WILD_BLOCK_SIZE < 0) { y = -py / WILD_BLOCK_SIZE; } if (x + px / WILD_BLOCK_SIZE > max_wild - 2) { x = max_wild - px / WILD_BLOCK_SIZE - 2; } if (y + py / WILD_BLOCK_SIZE > max_wild - 2) { y = max_wild - py / WILD_BLOCK_SIZE - 2; } } } /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = hook; /* The size may have changed during the scores display */ angband_term[0]->resize_hook(); /* Hack - Flush it */ Term_fresh(); } /* * Two arrays listing the effects of "brightness" * and "darkness" on various "base" colours. * * This is used to do dynamic lighting effects in ascii :-) */ static const byte lighting_colours[16] = { /* TERM_DARK */ TERM_L_DARK, /* TERM_WHITE */ TERM_YELLOW, /* TERM_SLATE */ TERM_WHITE, /* TERM_ORANGE */ TERM_YELLOW, /* TERM_RED */ TERM_RED, /* TERM_GREEN */ TERM_L_GREEN, /* TERM_BLUE */ TERM_BLUE, /* TERM_UMBER */ TERM_L_UMBER, /* TERM_L_DARK */ TERM_SLATE, /* TERM_L_WHITE */ TERM_WHITE, /* TERM_VIOLET */ TERM_L_RED, /* TERM_YELLOW */ TERM_YELLOW, /* TERM_L_RED */ TERM_L_RED, /* TERM_L_GREEN */ TERM_YELLOW, /* TERM_L_BLUE */ TERM_L_BLUE, /* TERM_L_UMBER */ TERM_L_UMBER, }; static const byte darking_colours[16] = { /* TERM_DARK */ TERM_DARK, /* TERM_WHITE */ TERM_SLATE, /* TERM_SLATE */ TERM_L_DARK, /* TERM_ORANGE */ TERM_UMBER, /* TERM_RED */ TERM_RED, /* TERM_GREEN */ TERM_GREEN, /* TERM_BLUE */ TERM_BLUE, /* TERM_UMBER */ TERM_RED, /* TERM_L_DARK */ TERM_L_DARK, /* TERM_L_WHITE */ TERM_SLATE, /* TERM_VIOLET */ TERM_BLUE, /* TERM_YELLOW */ TERM_ORANGE, /* TERM_L_RED */ TERM_L_RED, /* TERM_L_GREEN */ TERM_GREEN, /* TERM_L_BLUE */ TERM_L_BLUE, /* TERM_L_UMBER */ TERM_UMBER }; #ifdef VARIABLE_PLAYER_GRAPH /* Magic numbers */ #define BMP_FIRST_PC_CLASS 164 #define BMP_FIRST_PC_RACE 128 static void variable_player_graph(byte *a, char *c) { if (use_graphics != GRAPHICS_ADAM_BOLT) { if (!streq(ANGBAND_SYS, "ibm")) { if (use_graphics) { *a = BMP_FIRST_PC_CLASS + p_ptr->pclass; *c = BMP_FIRST_PC_RACE + p_ptr->prace; } } else { if (use_graphics) { if (p_ptr->psex == SEX_FEMALE) *c = (char)242; switch (p_ptr->pclass) { case CLASS_PALADIN: { if (p_ptr->lev < 20) *a = TERM_L_WHITE; else *a = TERM_WHITE; *c = 253; break; } case CLASS_WARRIOR_MAGE: { if (p_ptr->lev < 20) *a = TERM_L_RED; else *a = TERM_VIOLET; break; } case CLASS_CHAOS_WARRIOR: { *a = randint1(14); break; } case CLASS_MAGE: case CLASS_HIGH_MAGE: { if (p_ptr->lev < 20) *a = TERM_L_RED; else *a = TERM_RED; *c = 248; break; } case CLASS_PRIEST: { if (p_ptr->lev < 20) *a = TERM_L_BLUE; else *a = TERM_BLUE; *c = 248; break; } case CLASS_RANGER: { if (p_ptr->lev < 20) *a = TERM_L_GREEN; else *a = TERM_GREEN; break; } case CLASS_ROGUE: { if (p_ptr->lev < 20) *a = TERM_SLATE; else *a = TERM_L_DARK; break; } case CLASS_WARRIOR: { if (p_ptr->lev < 20) *a = TERM_L_UMBER; else *a = TERM_UMBER; break; } case CLASS_MONK: case CLASS_MINDCRAFTER: { if (p_ptr->lev < 20) *a = TERM_L_UMBER; else *a = TERM_UMBER; *c = 248; break; } default: { /* Unknown */ *a = TERM_WHITE; } } switch (p_ptr->prace) { case RACE_GNOME: case RACE_HOBBIT: { *c = 144; break; } case RACE_DWARF: { *c = 236; break; } case RACE_HALF_ORC: { *c = 243; break; } case RACE_HALF_TROLL: { *c = 184; break; } case RACE_ELF: case RACE_HALF_ELF: case RACE_HIGH_ELF: { *c = 223; break; } case RACE_HALF_OGRE: { *c = 168; break; } case RACE_HALF_GIANT: case RACE_HALF_TITAN: case RACE_CYCLOPS: { *c = 145; break; } case RACE_YEEK: { *c = 209; break; } case RACE_KLACKON: { *c = 229; break; } case RACE_KOBOLD: { *c = 204; break; } case RACE_NIBELUNG: { *c = 144; break; } case RACE_DARK_ELF: { *c = 223; break; } case RACE_DRACONIAN: { if (p_ptr->lev < 20) *c = 240; else if (p_ptr->lev < 40) *c = 22; else *c = 137; break; } case RACE_MIND_FLAYER: { *c = 236; break; } case RACE_IMP: { *c = 142; break; } case RACE_GOLEM: { *c = 6; break; } case RACE_SKELETON: { if (p_ptr->pclass == CLASS_MAGE || p_ptr->pclass == CLASS_PRIEST || p_ptr->pclass == CLASS_HIGH_MAGE || p_ptr->pclass == CLASS_MONK || p_ptr->pclass == CLASS_MINDCRAFTER) *c = 159; else *c = 181; break; } case RACE_ZOMBIE: case RACE_GHOUL: { *c = 221; break; } case RACE_VAMPIRE: { *c = 217; break; } case RACE_SPECTRE: { *c = 241; break; } case RACE_SPRITE: { *c = 244; break; } case RACE_BEASTMAN: { *c = 154; break; } } } } } } #endif /* VARIABLE_PLAYER_GRAPH */ /* * Hack -- Legal monster codes */ static cptr image_monster_hack = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* * Mega-Hack -- Hallucinatory monster */ static void image_monster(byte *ap, char *cp) { int n = strlen(image_monster_hack); /* Random symbol from set above */ if (use_graphics) { (*cp) = r_info[randint1(z_info->r_max - 1)].x_char; (*ap) = r_info[randint1(z_info->r_max - 1)].x_attr; } else /* Text mode */ { (*cp) = (image_monster_hack[randint0(n)]); /* Random color */ (*ap) = randint1(15); } } /* * Hack -- Legal object codes */ static cptr image_object_hack = "?/|\\\"!$()_-=[]{},~"; /* * Mega-Hack -- Hallucinatory object */ static void image_object(byte *ap, char *cp) { int n = strlen(image_object_hack); if (use_graphics) { (*cp) = k_info[randint1(z_info->k_max - 1)].x_char; (*ap) = k_info[randint1(z_info->k_max - 1)].x_attr; } else { (*cp) = (image_object_hack[randint0(n)]); /* Random color */ (*ap) = randint1(15); } } /* * Hack -- Random hallucination */ static void image_random(byte *ap, char *cp) { /* Normally, assume monsters */ if (randint0(100) < 75) { image_monster(ap, cp); } /* Otherwise, assume objects */ else { image_object(ap, cp); } } /* * Table of the GF type for each breath */ static int breath_gf[32] = { GF_NONE, /* RF3_SHRIEK */ GF_NONE, /* RF3_ELDRITCH_HORROR */ GF_NONE, /* RF3_XXX3 */ GF_NONE, /* RF3_ROCKET */ GF_NONE, /* RF3_ARROW */ GF_NONE, /* RF3_XXX6 */ GF_NONE, /* RF3_XXX7 */ GF_NONE, /* RF3_XXX8 */ GF_ACID, /* RF3_BR_ACID */ GF_ELEC, /* RF3_BR_ELEC */ GF_FIRE, /* RF3_BR_FIRE */ GF_COLD, /* RF3_BR_COLD */ GF_POIS, /* RF3_BR_POIS */ GF_NETHER, /* RF3_BR_NETH */ GF_LITE, /* RF3_BR_LITE */ GF_DARK, /* RF3_BR_DARK */ GF_CONFUSION, /* RF3_BR_CONF */ GF_SOUND, /* RF3_BR_SOUN */ GF_CHAOS, /* RF3_BR_CHAO */ GF_DISENCHANT, /* RF3_BR_DISE */ GF_NEXUS, /* RF3_BR_NEXU */ GF_TIME, /* RF3_BR_TIME */ GF_INERTIA, /* RF3_BR_INER */ GF_GRAVITY, /* RF3_BR_GRAV */ GF_SHARDS, /* RF3_BR_SHAR */ GF_PLASMA, /* RF3_BR_PLAS */ GF_FORCE, /* RF3_BR_WALL */ GF_MANA, /* RF3_BR_MANA */ GF_NONE, /* RF3_BA_NUKE */ GF_NUKE, /* RF3_BR_NUKE */ GF_NONE, /* RF3_BA_CHAO */ GF_DISINTEGRATE /* RF3_BR_DISI */ }; /* * Hack -- Get colour based on breaths of monster * * (This may be a little slow.... */ static byte breath_attr(const monster_race *r_ptr) { /* Mask out the breath flags */ u32b flags = r_ptr->flags[3] & RF3_BREATHS; u32b mask; /* See if we breathe anything at all */ if (flags) { byte a; char c; cptr s; int i; int prob = 1; int choice = 0; /* Pick breath */ for (i = 8, mask = 256; i < 32; i++, mask += mask) { if (flags & mask) { /* See if we choose this spell */ if (one_in_(prob)) choice = i; /* Decrease probability of picking next 'spell' */ prob++; } } /* Paranoia */ if (choice) { /* Lookup the default colors for this type */ s = gf_color[breath_gf[choice]]; /* Oops */ if (!s) return (TERM_WHITE); /* Pick a random color */ c = s[randint0(strlen(s))]; /* Lookup this color */ a = strchr(color_char, c) - color_char; /* * Invalid color (note check for < 0 removed, gave a silly * warning because bytes are always >= 0 -- RG) */ if (a > 15) return (TERM_WHITE); /* Use this color */ return (a); } } /* Just do any of 7 colours */ switch (randint1(7)) { case 1: return (TERM_RED); case 2: return (TERM_L_RED); case 3: return (TERM_WHITE); case 4: return (TERM_L_GREEN); case 5: return (TERM_BLUE); case 6: return (TERM_L_DARK); case 7: return (TERM_GREEN); } /* For the compilers... */ return (TERM_WHITE); } /* * Extract tile info for monster. * * Note how we manually alter the tile type to simulate effects * in ascii mode. We must make sure not to break the images * when in (semi) graphics mode, where either the monster * or the floor it is standing on is non-ascii. */ static void map_mon_info(monster_type *m_ptr, monster_race *r_ptr, byte *a, char *c, term_map *map) { byte feat_not_ascii; byte ma = *a; char mc = *c; /* Visible monster */ if (m_ptr->ml) { /* Visible monster */ map->monster = m_ptr->r_idx; /* Keep this grid */ map->flags |= MAP_ONCE; /* Get monster information */ if (m_ptr->csleep) map->m_flags |= MONST_ASLEEP; if (is_friendly(m_ptr)) map->m_flags |= MONST_FRIEND; if (is_pet(m_ptr)) map->m_flags |= MONST_PET; if (m_ptr->confused) map->m_flags |= MONST_CONFUSED; if (m_ptr->monfear) map->m_flags |= MONST_FEAR; if (m_ptr->stunned) map->m_flags |= MONST_STUN; if (m_ptr->invulner) map->m_flags |= MONST_INVULN; /* Get scaled monster hp */ map->m_hp = m_ptr->hp * 10 / m_ptr->maxhp; /* Hack -- hallucination */ if (p_ptr->tim.image) { /* Hallucinatory monster */ image_monster(a, c); return; } else { feat_not_ascii = ((*a) & 0x80); /* Desired attr */ if (!FLAG(r_ptr, RF_ATTR_CLEAR) || feat_not_ascii) { ma = r_ptr->x_attr; } /* Desired char */ if (!FLAG(r_ptr, RF_CHAR_CLEAR) || feat_not_ascii) { mc = r_ptr->x_char; } /* Ignore weird codes + graphics */ if (!(ma & 0x80)) { /* Multi-hued monster */ if (FLAG(r_ptr, RF_ATTR_MULTI)) { /* Is it a shapechanger? */ if (FLAG(r_ptr, RF_SHAPECHANGER)) { if (use_graphics) { mc = r_info[randint1(z_info->r_max - 1)].x_char; ma = r_info[randint1(z_info->r_max - 1)].x_attr; } else { mc = (one_in_(25) ? image_object_hack[randint0 (strlen(image_object_hack))] : image_monster_hack[randint0 (strlen(image_monster_hack))]); } } /* Multi-hued attr */ if (FLAG(r_ptr, RF_ATTR_ANY)) ma = randint1(15); else { /* Pick colour based on breaths */ ma = breath_attr(r_ptr); } } /* Mimics' colors vary */ else if (((mc == '\"') || (mc == '!') || (mc == '=')) && !FLAG(r_ptr, RF_UNIQUE)) { /* Use char */ ; /* Use semi-random attr */ ma = GET_ARRAY_INDEX(m_list, m_ptr) % 15 + 1; } } } } /* Save results */ *a = ma; *c = mc; } /* * Extract the attr/char to display at the given (legal) map location * * Basically, we "paint" the chosen attr/char in several passes, starting * with any known "terrain features" (defaulting to darkness), then adding * any known "objects", and finally, adding any known "monsters". This * is not the fastest method but since most of the calls to this function * are made for grids with no monsters or objects, it is fast enough. * * Note that the "zero" entry in the feature/object/monster arrays are * used to provide "special" attr/char codes, with "monster zero" being * used for the player attr/char, "object zero" being used for the "stack" * attr/char, and "feature zero" being used for the "nothing" attr/char, * though this function makes use of only "feature zero". * * Note that monsters can have some "special" flags, including "ATTR_MULTI", * which means their color changes, and "ATTR_CLEAR", which means they take * the color of whatever is under them, and "CHAR_CLEAR", which means that * they take the symbol of whatever is under them. Technically, the flag * "CHAR_MIMIC" is supposed to indicate that a monster looks strange when * examined, but this flag is currently ignored. * * Note the effects of hallucination. Objects always appear as random * "objects", monsters as random "monsters", and normal grids occasionally * appear as random "monsters" or "objects", but note that these random * "monsters" and "objects" are really just "colored ascii symbols". * * Note the use of the new "terrain feature" information. Note that the * assumption that all interesting "objects" and "terrain features" are * memorized allows extremely optimized processing below. Note the use * of separate flags on objects to mark them as memorized allows a grid * to have memorized "terrain" without granting knowledge of any object * which may appear in that grid. * * We use the players memorised information to pick the terrain feature * to use. This allows massive simplification - getting rid of all the * checks to tha old CAVE_MARK flag, and allowing the player is blind case * to be merged into the main line code. The section picking the terrain * attr/feat is now less than a page long - which is important because * this routine is a major bottleneck. * * Note the "special lighting effects" which can be activated for floor * grids using the "view_special_lite" option causing certain grids to be * displayed using special colors. * * Note the "special lighting effects" which can be activated for wall * grids using the "view_granite_lite" option causing certain grids to be * displayed using special colors. * * The lighting and darkening of colours is handled by two arrays. We can * also lighten and darken some terrains in the 16x16 tileset. * * Note that bizarre things must be done when the "attr" and/or "char" * codes have the "high-bit" set, since these values are used to encode * various "special" pictures in some versions, and certain situations, * such as "multi-hued" or "clear" monsters, cause the attr/char codes * to be "scrambled" in various ways. * * * Save data into the term_map struct so we can pass it to the ports * hooking the overhead map code. The status of the square (x, y) has * probably changed. This allows the main-???.c files to not access * internal game data, which may or may not be accessable. */ static void map_info(int x, int y, byte *ap, char *cp, byte *tap, char *tcp) { feature_type *f_ptr; object_type *o_ptr; object_kind *k_ptr; monster_type *m_ptr; monster_race *r_ptr; field_type *fld_ptr; /* Get location */ cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Get the memorized feature */ byte feat = pc_ptr->feat; /* Info flags */ byte player = pc_ptr->player; byte a; char c; s16b halluc = p_ptr->tim.image; bool visible = player & GRID_SEEN; bool glow = c_ptr->info & CAVE_GLOW; bool lite = (c_ptr->info & CAVE_MNLT) || (player & GRID_LITE); bool float_field = FALSE; term_map map; /* Clear map info */ (void)WIPE(&map, term_map); /* Save known data */ map.terrain = feat; /* Save location */ map.x = x; map.y = y; /* Default priority */ map.priority = 0; /* Pointer to the feature */ f_ptr = &f_info[feat]; /* Hack -- rare random hallucination, except on outer dungeon walls */ if (halluc && !p_ptr->tim.blind && (feat != FEAT_PERM_SOLID) && one_in_(256)) { /* Hallucinate */ image_random(&a, &c); if (glow) { map.flags = MAP_GLOW; } (*tap) = a; (*tcp) = c; } else { /* Visible */ if (visible) { map.flags = MAP_SEEN | MAP_ONCE; if (glow) map.flags |= MAP_GLOW; if (lite) map.flags |= MAP_LITE; } /* The feats attr */ a = f_ptr->x_attr; /* The feats char */ c = f_ptr->x_char; /* * Look for lighting effects. * * Need to have lighting on and the player is not blind. * We then need to have a grid that is allowed to be lit. */ if (view_bright_lite && !p_ptr->tim.blind && (!(f_ptr->flags & FF_BLOCK) || (view_granite_lite && !view_torch_grids))) { /* It's not in view or no lighting effects? */ if (((!(player & (GRID_VIEW))) && view_special_lite) || !visible) { /* If is ascii graphics */ if (a < 16) { /* Use darkened colour */ a = darking_colours[a]; } else if ((use_graphics == GRAPHICS_ADAM_BOLT) && (f_ptr->flags & FF_USE_TRANS)) { /* Use a dark tile */ c++; } } else if (lite && view_yellow_lite) { /* Use the torch effect */ if (a < 16) { /* Use bright colour */ a = lighting_colours[a]; } else if ((use_graphics == GRAPHICS_ADAM_BOLT) && (f_ptr->flags & FF_USE_TRANS)) { /* Use a light tile */ c += 2; } } } /* Save the terrain info for the transparency effects */ /* Does the feature have "extended terrain" information? */ if (f_ptr->w_attr) { /* * Store extended terrain information. * Note hack to get lighting right. */ (*tap) = f_ptr->w_attr + a - f_ptr->x_attr; (*tcp) = f_ptr->w_char + c - f_ptr->x_char; } else { (*tap) = a; (*tcp) = c; } } /* Fields */ FLD_ITT_START (c_ptr->fld_idx, fld_ptr) { /* Memorized, visible fields */ if ((fld_ptr->info & (FIELD_INFO_MARK | FIELD_INFO_VIS)) == (FIELD_INFO_MARK | FIELD_INFO_VIS)) { /* Remember field type */ map.field = fld_ptr->t_idx; /* Keep this grid */ map.flags |= MAP_ONCE; /* High priority tile */ map.priority = 20; /* Which display level to use? */ if (fld_ptr->info & FIELD_INFO_FEAT) { /* Terrain level */ if ((use_graphics == GRAPHICS_ADAM_BOLT) && (fld_ptr->info & (FIELD_INFO_TRANS))) { /* Take into account dynamic lighting. */ c += fld_ptr->f_char - f_ptr->x_char; } else { /* Normal char */ c = fld_ptr->f_char; } /* Normal attr */ a = fld_ptr->f_attr; /* Save the terrain info for the transparency effects */ (*tap) = a; (*tcp) = c; } else { /* Tile */ c = fld_ptr->f_char; a = fld_ptr->f_attr; /* Do we need to look at objects? */ if (!(fld_ptr->info & (FIELD_INFO_IGNORE))) { /* Above objects */ float_field = TRUE; } } /* Done */ break; } } FLD_ITT_END; /* Objects */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Memorized objects */ if (o_ptr->info & (OB_SEEN)) { k_ptr = &k_info[o_ptr->k_idx]; /* Flavoured object */ if (k_ptr->flavor) { /* Save flavor character */ map.unknown = k_ptr->d_char; } else { /* Save object */ map.object = o_ptr->k_idx; } /* Keep this grid */ map.flags |= MAP_ONCE; /* High priority tile */ map.priority = 20; /* A field is obscuring the view to the object */ if (float_field) break; /* Hack -- hallucination */ if (halluc) { image_object(&a, &c); } else { /* Normal char */ c = object_char(o_ptr); /* Normal attr */ a = object_attr(o_ptr); } /* Done */ break; } } OBJ_ITT_END; /* Handle monsters */ if (c_ptr->m_idx) { m_ptr = &m_list[c_ptr->m_idx]; r_ptr = &r_info[m_ptr->r_idx]; /* Get monster tile info */ map_mon_info(m_ptr, r_ptr, &a, &c, &map); /* Not hallucinating and Mimic in los? */ if (!halluc && visible && !m_ptr->ml && FLAG(r_ptr, RF_CHAR_MIMIC)) { /* Keep this grid */ map.flags |= MAP_ONCE; /* Save mimic character */ map.unknown = r_ptr->d_char; } /* High priority tile */ map.priority = 24; } /* Hack -- fake monochrome */ if (fake_monochrome) { if (p_ptr->tim.invuln || !use_color) a = TERM_WHITE; else if (p_ptr->tim.wraith_form) a = TERM_L_DARK; } /* Handle "player" */ if ((x == p_ptr->px) && (y == p_ptr->py)) { monster_race *r_ptr = &r_info[0]; /* Get the "player" attr */ a = r_ptr->x_attr; /* Get the "player" char */ c = r_ptr->x_char; #ifdef VARIABLE_PLAYER_GRAPH variable_player_graph(&a, &c) #endif /* VARIABLE_PLAYER_GRAPH */ /* High priority tile */ map.priority = 50; } /* Save the info */ (*ap) = a; (*cp) = c; /* Save tile information */ map.a = a; map.c = c; map.ta = (*tap); map.tc = (*tcp); /* Save information in map */ save_map_location(x, y, &map); } /* * Update the overhead map (used when the visuals change) */ void update_overhead_map(void) { map_block *mb_ptr; byte a, ta; char c, tc; int x, y; MAP_ITT_START (mb_ptr) { MAP_GET_LOC(x, y); if (in_boundsp(x, y)) { /* Update the known tile at the location */ map_info(x, y, &a, &c, &ta, &tc); } } MAP_ITT_END; } /* * Erase the map */ void Term_erase_map(void) { callback_list *callback; /* Notify erasure of the map */ for (callback = callbacks[CALL_MAP_ERASE]; callback; callback = callback->next) { /* Execute the callback */ ((map_erase_hook_type)callback->func) (callback->data); } /* Actually clear the map */ clear_map(); } /* * Prints the map of the dungeon * * Note that, for efficiency, we contain an "optimized" version * of both "lite_spot()" and "print_rel()", and that we use the * "lite_spot()" function to display the player grid, if needed. * * This function may be called when the cache is wrong. */ void prt_map(void) { int x, y; int v; /* map bounds */ s16b xmin, xmax, ymin, ymax; int wid, hgt; byte *pa; char *pc; byte *pta; char *ptc; /* Get size */ get_map_size(&wid, &hgt); /* Access the cursor state */ (void)Term_get_cursor(&v); /* Hide the cursor */ (void)Term_set_cursor(0); /* Get bounds */ xmin = p_ptr->panel_x1; xmax = p_ptr->panel_x2 - 1; ymin = p_ptr->panel_y1; ymax = p_ptr->panel_y2 - 1; /* Pointers to current position in the string */ pa = mp_a; pc = mp_c; pta = mp_ta; ptc = mp_tc; /* Dump the map */ for (y = ymin; y <= ymax; y++) { /* Clear the arrays in case panel doesn't fill screen */ for (x = 0; x < wid; x++) { mp_a[x] = 0; mp_c[x] = 0; mp_ta[x] = 0; mp_tc[x] = 0; } /* Scan the columns of row "y" */ for (x = xmin; x <= xmax; x++) { if (in_bounds2(x, y)) { /* Get map info */ map_info(x, y, pa, pc, pta, ptc); } /* Advance */ pa++; pc++; pta++; ptc++; } /* Point to start of line */ pa = mp_a; pc = mp_c; pta = mp_ta; ptc = mp_tc; /* Efficiency -- Redraw that row of the map */ Term_queue_line(COL_MAP, y - p_ptr->panel_y1 + ROW_MAP, wid, pa, pc, pta, ptc); } /* Restore the cursor */ (void)Term_set_cursor(v); } void display_dungeon(void) { int px = p_ptr->px; int py = p_ptr->py; int x, y; byte a; char c; int wid = Term->wid / 2, hgt = Term->hgt / 2; byte ta; char tc; for (x = px - wid + 1; x <= px + wid; x++) { for (y = py - hgt + 1; y <= py + hgt; y++) { if (in_boundsp(x, y)) { /* Update this square */ map_info(x, y, &a, &c, &ta, &tc); /* Hack -- Queue it */ Term_queue_char(x - px + wid - 1, y - py + hgt - 1, a, c, ta, tc); } else { /* Clear out-of-bound tiles */ /* Access darkness */ feature_type *f_ptr = &f_info[FEAT_NONE]; /* Normal attr */ a = f_ptr->x_attr; /* Normal char */ c = f_ptr->x_char; /* Hack -- Queue it */ Term_queue_char(x - px + wid - 1, y - py + hgt - 1, a, c, a, c); } } } } /* * Hack -- priority array (see below) * * Note that all "walls" always look like "secret doors" (see "map_info()"). * * This really needs to be done a better way. */ static const byte priority_table[][2] = { /* Dark */ {FEAT_NONE, 2}, /* Floors */ {FEAT_FLOOR, 5}, /* Walls */ {FEAT_SECRET, 10}, {FEAT_WALL_EXTRA, 10}, {FEAT_WALL_INNER, 10}, {FEAT_WALL_OUTER, 10}, {FEAT_WALL_SOLID, 10}, /* Perm Walls */ {FEAT_PERM_EXTRA, 10}, {FEAT_PERM_INNER, 10}, {FEAT_PERM_OUTER, 10}, {FEAT_PERM_SOLID, 10}, /* Quartz */ {FEAT_QUARTZ, 11}, /* Magma */ {FEAT_MAGMA, 12}, /* Rubble */ {FEAT_RUBBLE, 13}, /* Open doors */ {FEAT_OPEN, 15}, {FEAT_BROKEN, 15}, /* Closed doors */ {FEAT_CLOSED, 17}, /* Hidden gold */ {FEAT_QUARTZ_K, 19}, {FEAT_MAGMA_K, 19}, /* water, lava, & trees */ {FEAT_DEEP_WATER, 20}, {FEAT_SHAL_WATER, 20}, {FEAT_DEEP_LAVA, 20}, {FEAT_SHAL_LAVA, 20}, {FEAT_DIRT, 6}, {FEAT_GRASS, 6}, {FEAT_TREES, 6}, {FEAT_MOUNTAIN, 20}, /* Stairs */ {FEAT_LESS, 25}, {FEAT_MORE, 25}, /* End */ {0, 0} }; /* * Hack -- a priority function (see below) */ static byte priority(byte feat) { int i = 0; /* Scan the table */ while (priority_table[i][1]) { /* Does the feature match? */ if (priority_table[i][0] == feat) { return (priority_table[i][1]); } /* Next entry */ i++; } /* Default - assume floor */ return (5); } /* * Equivalent function to map_info, but for displaying * the reduced-size dungeon map. * * We need to calculate priority as well as the symbols to display. * * We cheat by getting the symbol recorded previously. */ static int display_map_info(int x, int y, char *c, byte *a, char *tc, byte *ta) { int tp; byte feat; map_block *mb_ptr; if (!map_in_bounds(x, y)) { /* * Out of bounds * XXX Hack try anyway. * * map_info() should bring the square in bounds. */ map_info(x, y, a, c, ta, tc); } /* Get overhead map square */ mb_ptr = map_loc(x, y); /* Default to precalculated priority */ tp = mb_ptr->priority; if (!tp) { /* Get terrain feature */ feat = parea(x, y)->feat; /* Extract the priority of that attr/char */ tp = priority(feat); } /* Get tile */ if (mb_ptr->a) { /* Get attributes from overhead map */ *a = mb_ptr->a; *c = mb_ptr->c; *ta = mb_ptr->ta; *tc = mb_ptr->tc; } else { map_info(x, y, a, c, ta, tc); } /* Return priority */ return (tp); } /* * Display a "small-scale" map of the dungeon in the active Term * * Note that the "map_info()" function must return fully colorized * data or this function will not work correctly. * * Note that this function must "disable" the special lighting * effects so that the "priority" function will work. * * Note the use of a specialized "priority" function to allow this * function to work with any graphic attr/char mappings, and the * attempts to optimize this function where possible. * * cx and cy are offsets from the position of the player. This * allows the map to be shifted around - but only works in the * wilderness. cx and cy return the position of the player on the * possibly shifted map. */ void display_map(int *cx, int *cy) { int py = p_ptr->py; int px = p_ptr->px; int i, j, x, y; byte feat; byte ta; char tc; byte tta; char ttc; byte tp; bool road; u16b w_type, w_info, twn; byte **ma; char **mc; byte **mp; byte **mta; char **mtc; int hgt, wid, yrat, xrat, xfactor, yfactor; place_type *pl_ptr; /* Hack - disable bigtile mode */ if (use_bigtile) { Term_bigregion(-1, -1, -1); } /* Get size */ Term_get_size(&wid, &hgt); hgt -= 2; wid -= 2; /* Paranoia */ if ((hgt < 3) || (wid < 3)) { /* * Need to place the player... * This is wrong, but the map is too small anyway. */ (*cy) = ROW_MAP; (*cx) = COL_MAP; return; } /* Allocate the maps */ C_MAKE(ma, (hgt + 2), byte *); C_MAKE(mc, (hgt + 2), char *); C_MAKE(mp, (hgt + 2), byte *); C_MAKE(mta, (hgt + 2), byte *); C_MAKE(mtc, (hgt + 2), char *); /* Allocate and wipe each line map */ for (i = 0; i < (hgt + 2); i++) { /* Allocate one row each array */ C_MAKE(ma[i], (wid + 2), byte); C_MAKE(mc[i], (wid + 2), char); C_MAKE(mp[i], (wid + 2), byte); C_MAKE(mta[i], (wid + 2), byte); C_MAKE(mtc[i], (wid + 2), char); } /* Clear the chars and attributes */ for (y = 0; y < hgt + 2; ++y) { for (x = 0; x < wid + 2; ++x) { /* Nothing here */ ma[y][x] = TERM_WHITE; mc[y][x] = ' '; mta[y][x] = TERM_WHITE; mtc[y][x] = ' '; /* No priority */ mp[y][x] = 0; } } if (!p_ptr->depth) { /* Plot wilderness */ /* work out coords of player in wilderness */ x = px / 16 + *cx; y = py / 16 + *cy; /* recenter */ x = x - wid / 2; if (x + wid >= max_wild) x = max_wild - wid - 1; if (x < 0) x = 0; y = y - hgt / 2; if (y + hgt >= max_wild) y = max_wild - hgt - 1; if (y < 0) y = 0; /* Player location in wilderness */ (*cy) += py / 16 - y + ROW_MAP; (*cx) += px / 16 - x + 1; /* Fill in the map */ for (i = 0; i < wid; ++i) { for (j = 0; j < hgt; ++j) { /* Only draw blocks inside map */ if (((x + i + 1) >= max_wild) || ((y + j + 1) >= max_wild)) continue; /* Only draw blocks that have been seen */ if (!(wild[j + y][i + x].done.info & WILD_INFO_SEEN)) continue; w_type = wild[j + y][i + x].done.wild; w_info = wild[j + y][i + x].done.info; if (w_type < WILD_SEA) { /* Normal terrain */ feat = wild_gen_data[w_type].feat; /* Allow roads to be drawn */ road = TRUE; } else { feat = FEAT_DEEP_WATER; /* No roads please */ road = FALSE; } /* Add in effect of other specials */ if (w_info & (WILD_INFO_WATER)) { feat = FEAT_DEEP_WATER; } else if (w_info & (WILD_INFO_ACID)) { feat = FEAT_DEEP_ACID; } else if (w_info & (WILD_INFO_LAVA)) { feat = FEAT_DEEP_LAVA; } /* This is a nasty hack */ /* Add in effects of roads */ if ((w_info & (WILD_INFO_ROAD)) && road) { ma[j + 1][i + 1] = TERM_UMBER; mc[j + 1][i + 1] = '+'; feat = FEAT_NONE; } else if ((w_info & (WILD_INFO_TRACK)) && road) { ma[j + 1][i + 1] = TERM_L_UMBER; mc[j + 1][i + 1] = '+'; feat = FEAT_NONE; } /* Hack - draw places */ /* Eventually will get attr,char from place data structure. */ twn = wild[j + y][i + x].done.place; /* If there is a place... */ if (twn) { pl_ptr = &place[twn]; switch (place[twn].type) { case TOWN_QUEST: { /* Hack make a char / attr from depth */ wild_type *w_ptr = &wild[pl_ptr->y][pl_ptr->x]; int depth = (w_ptr->done.mon_gen + 9) / 10; if (depth > 9) depth = 9; /* Quests are red */ ma[j + 1][i + 1] = TERM_RED; mc[j + 1][i + 1] = '0' + depth; feat = FEAT_NONE; break; } case TOWN_DUNGEON: { /* Hack make a char / attr from depth */ int depth = (pl_ptr->dungeon->min_level + 9) / 10; if (depth > 9) depth = 9; /* Dungeons are blue */ ma[j + 1][i + 1] = TERM_L_BLUE; mc[j + 1][i + 1] = '0' + depth; feat = FEAT_NONE; break; } default: { /* Towns are white */ ma[j + 1][i + 1] = TERM_WHITE; mc[j + 1][i + 1] = pl_ptr->name[0]; feat = FEAT_NONE; break; } } } /* Finally show position of player */ if ((i + x == px / 16) && (j + y == py / 16)) { ma[j + 1][i + 1] = TERM_WHITE; mc[j + 1][i + 1] = '@'; feat = FEAT_NONE; } if (feat) { /* Get attr / char pair for wilderness block type */ ma[j + 1][i + 1] = f_info[feat].x_attr; mc[j + 1][i + 1] = f_info[feat].x_char; if (f_info[feat].w_attr) { mta[j + 1][i + 1] = f_info[feat].w_attr; mtc[j + 1][i + 1] = f_info[feat].w_char; } else { mta[j + 1][i + 1] = ma[j + 1][i + 1]; mtc[j + 1][i + 1] = mc[j + 1][i + 1]; } } } } } else { yrat = p_ptr->max_hgt - p_ptr->min_hgt; xrat = p_ptr->max_wid - p_ptr->min_wid; /* Get scaling factors */ yfactor = ((yrat / hgt < 4) && (yrat > hgt)) ? 10 : 1; xfactor = ((xrat / wid < 4) && (xrat > wid)) ? 10 : 1; yrat = (yrat * yfactor + hgt - 1) / hgt; xrat = (xrat * xfactor + wid - 1) / wid; /* Player location in dungeon */ (*cy) = py * yfactor / yrat + ROW_MAP; (*cx) = px * xfactor / xrat + 1; /* Fill in the map of dungeon */ for (i = p_ptr->min_wid; i < p_ptr->max_wid; ++i) { for (j = p_ptr->min_hgt; j < p_ptr->max_hgt; ++j) { /* Location */ x = i * xfactor / xrat + 1; y = j * yfactor / yrat + 1; /* Get priority and symbol */ tp = display_map_info(i, j, &tc, &ta, &ttc, &tta); /* Save "best" */ if (mp[y][x] < tp) { /* Save the char */ mc[y][x] = tc; /* Save the attr */ ma[y][x] = ta; /* Save the transparency graphic */ mtc[y][x] = ttc; mta[y][x] = tta; /* Save priority */ mp[y][x] = tp; } } } } /* Corners */ i = wid + 1; j = hgt + 1; /* Draw the corners */ mc[0][0] = '+'; mc[0][i] = '+'; mc[j][0] = '+'; mc[j][i] = '+'; /* Draw the horizontal edges */ for (i = 1; i <= wid; i++) { mc[0][i] = '-'; mc[j][i] = '-'; } /* Draw the vertical edges */ for (j = 1; j <= hgt; j++) { mc[j][0] = '|'; mc[j][i] = '|'; } /* Display each map line in order */ for (j = 0; j < hgt + 2; ++j) { /* Display the line */ for (i = 0; i < wid + 2; ++i) { ta = ma[j][i]; tc = mc[j][i]; tta = mta[j][i]; ttc = mtc[j][i]; /* Hack -- Queue it */ Term_queue_char(i, j, ta, tc, tta, ttc); } } /* Free each line map */ for (i = 0; i < (hgt + 2); i++) { /* Free one row each array */ FREE(ma[i]); FREE(mc[i]); FREE(mta[i]); FREE(mtc[i]); FREE(mp[i]); } /* Free the maps */ FREE(ma); FREE(mc); FREE(mta); FREE(mtc); FREE(mp); } /* * Redraw (on the screen) a given MAP location * * This function should only be called on "legal" grids */ void lite_spot(int x, int y) { byte a; char c; byte ta; char tc; /* Paranoia */ if (!character_dungeon) return; if (in_boundsp(x, y)) { /* Update this square */ map_info(x, y, &a, &c, &ta, &tc); /* Redraw if on screen */ if (panel_contains(x, y)) { /* Real coordinates convert to screen positions */ x -= p_ptr->panel_x1 - COL_MAP; y -= p_ptr->panel_y1 - ROW_MAP; /* Hack -- Queue it */ Term_queue_char(x, y, a, c, ta, tc); } } } /* * Moves the cursor to a given MAP (x, y) location */ void move_cursor_relative(int x, int y) { /* Real coordinates convert to screen positions */ x -= p_ptr->panel_x1 - COL_MAP; y -= p_ptr->panel_y1 - ROW_MAP; /* Go there */ Term_gotoxy(x, y); } /* * Place an attr/char pair at the given map coordinate, if legal. */ void print_rel(char c, byte a, int x, int y) { /* Only do "legal" locations */ if (panel_contains(x, y)) { /* Hack -- fake monochrome */ if (fake_monochrome) { if (p_ptr->tim.invuln || !use_color) a = TERM_WHITE; else if (p_ptr->tim.wraith_form) a = TERM_L_DARK; } /* Real coordinates convert to screen positions */ x -= p_ptr->panel_x1 - COL_MAP; y -= p_ptr->panel_y1 - ROW_MAP; /* Draw the char using the attr */ Term_queue_char(x, y, a, c, a, c); } } /* * The player has moved */ void Term_move_player(void) { set_player_location(p_ptr->px, p_ptr->py); } #ifdef TERM_USE_LIST /* Lists of objects on the player */ list_item *equipment; int equip_num; list_item *inventory; int inven_num; /* Current list (Usually used for stores) */ list_item *cur_list; int cur_num; /* * Delete the object list */ static void delete_list(list_item **l_ptr_ptr, int *num) { int i; list_item *l_ptr; for (i = 0; i < *num; i++) { /* Get item */ l_ptr = &((*l_ptr_ptr)[i]); /* Delete strings */ string_free(l_ptr->o_name); string_free(l_ptr->xtra_name); } /* No more items */ *num = 0; /* Kill list */ KILL(*l_ptr_ptr); } /* * Copy a list from t_ptr to l_ptr_ptr * * The first list comes from the game, the second is * stored here for later use by the ports / borg. * * We assume l_ptr_ptr points to a NULL pointer. */ static void copy_list(term_list *t_ptr, int num1, list_item **l_ptr_ptr, int *num2) { list_item *l_ptr; term_list *tl_ptr; int i; /* Paranoia */ if (*l_ptr_ptr) quit("Trying to copy over an allocated list."); /* We don't need to make an empty list */ if (!num1) return; /* Save number of items in list */ *num2 = num1; /* Create the list */ C_MAKE(*l_ptr_ptr, num1, list_item); for (i = 0; i < num1; i++) { l_ptr = &((*l_ptr_ptr)[i]); tl_ptr = &t_ptr[i]; /* Duplicate flags */ l_ptr->kn_flags[0] = tl_ptr->kn_flags[0]; l_ptr->kn_flags[1] = tl_ptr->kn_flags[1]; l_ptr->kn_flags[2] = tl_ptr->kn_flags[2]; l_ptr->kn_flags[3] = tl_ptr->kn_flags[3]; /* Duplicate cost */ l_ptr->cost = tl_ptr->cost; /* Duplicate item type and weight */ l_ptr->k_idx = tl_ptr->k_idx; l_ptr->weight = tl_ptr->weight; l_ptr->number = tl_ptr->number; /* Duplicate info */ l_ptr->info = tl_ptr->info; /* Duplicate pval /tval */ l_ptr->pval = tl_ptr->pval; l_ptr->tval = tl_ptr->tval; /* Duplicate timeout */ l_ptr->timeout = tl_ptr->timeout; /* Duplicate bonuses */ l_ptr->to_h = tl_ptr->to_h; l_ptr->to_d = tl_ptr->to_d; l_ptr->to_a = tl_ptr->to_a; /* Duplicate AC */ l_ptr->ac = tl_ptr->ac; /* Duplicate Dice */ l_ptr->dd = tl_ptr->dd; l_ptr->ds = tl_ptr->ds; /* Duplicate strings */ l_ptr->o_name = string_make(tl_ptr->o_name); l_ptr->xtra_name = string_make(tl_ptr->xtra_name); } } /* * Save the object list so that the port can access it. */ static void save_object_list(term_list *l_ptr, int num, byte list_type) { callback_list *callback; if (list_type == LIST_INVEN) { /* Delete old inventory list */ delete_list(&inventory, &inven_num); /* Copy over with the new list */ copy_list(l_ptr, num, &inventory, &inven_num); } if (list_type == LIST_EQUIP) { /* Delete old equipment list */ delete_list(&equipment, &equip_num); /* Copy over with the new list */ copy_list(l_ptr, num, &equipment, &equip_num); } if ((list_type == LIST_STORE) || (list_type == LIST_HOME)) { /* Delete the old current list */ delete_list(&cur_list, &cur_num); /* Copy over with the new list */ copy_list(l_ptr, num, &cur_list, &cur_num); } /* Notify port */ for (callback = callbacks[CALL_OBJECT_LIST]; callback; callback = callback->next) { /* Execute the callback */ ((list_notice_hook_type)callback->func) (list_type, callback->data); } } /* * Set the basic object flags to send to the port */ static void set_basic_flags(term_list *l_ptr, object_type *o_ptr, bool in_store) { object_flags oflags; /* Known flags */ object_flags_known(o_ptr, &oflags); l_ptr->kn_flags[0] = oflags.flags[0]; l_ptr->kn_flags[1] = oflags.flags[1]; l_ptr->kn_flags[2] = oflags.flags[2]; l_ptr->kn_flags[3] = oflags.flags[3]; /* Type of object */ if (object_aware_p(o_ptr) || in_store) { l_ptr->k_idx = o_ptr->k_idx; } /* Weight and number */ l_ptr->weight = o_ptr->weight; l_ptr->number = o_ptr->number; /* Save information */ l_ptr->info = o_ptr->info; /* Hack - Save cost (If not in a store, this will be inaccurate) */ l_ptr->cost = o_ptr->temp_cost; /* Do we have extra name information? */ if (object_known_p(o_ptr) && (o_ptr->xtra_name)) { l_ptr->xtra_name = string_make(quark_str(o_ptr->xtra_name)); } else { l_ptr->xtra_name = NULL; } /* Damage dice */ l_ptr->dd = o_ptr->dd; l_ptr->ds = o_ptr->ds; /* Hack - only send AC information if isn't a wand */ if (o_ptr->tval != TV_WAND) { l_ptr->ac = o_ptr->ac; } /* Identified items yield extra information */ if (object_known_p(o_ptr)) { /* Bonuses */ l_ptr->to_h = o_ptr->to_h; l_ptr->to_d = o_ptr->to_d; l_ptr->to_a = o_ptr->to_a; /* Pval */ if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)) { /* Wand and staff charges */ l_ptr->pval = o_ptr->pval; } } /* Pval */ if (KN_FLAG(o_ptr, TR_PVAL_MASK)) { /* Normal items with noticable pval */ l_ptr->pval = o_ptr->pval; } /* Tval */ l_ptr->tval = o_ptr->tval; /* Timeout */ if (o_ptr->tval == TV_LITE) { /* Lights have "obvious" timeouts */ l_ptr->timeout = o_ptr->timeout; } else { /* Rods can charge in piles */ if ((o_ptr->number > 1) && (o_ptr->tval == TV_ROD)) { int power; object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* * Find out how many rods are charging. */ power = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval; if (power > o_ptr->number) power = o_ptr->number; /* Hack - Set timeout to number of charging items */ l_ptr->timeout = power; } else { /* Are we charging? */ l_ptr->timeout = o_ptr->timeout ? 1 : 0; } } } /* * Write out the equipment so that the ports can access it. * * This is equivalent to Term_write_list() below, except with * a static array of objects rather than a list. */ void Term_write_equipment(void) { term_list *list, *l_ptr; int i; object_type *o_ptr; char o_name[256]; /* Create the list */ C_MAKE(list, EQUIP_MAX, term_list); /* Fill with information */ for (i = 0; i < EQUIP_MAX; i++) { /* Get object */ o_ptr = &p_ptr->equipment[i]; if (!o_ptr->k_idx) continue; /* Get object list element */ l_ptr = &list[i]; /* Set object flags */ set_basic_flags(l_ptr, o_ptr, FALSE); /* Describe the object */ object_desc(o_name, o_ptr, TRUE, 3, 256); l_ptr->o_name = string_make(o_name); } /* Save for later */ save_object_list(list, EQUIP_MAX, LIST_EQUIP); for (i = 0; i < EQUIP_MAX; i++) { l_ptr = &list[i]; /* Free the strings */ string_free(l_ptr->o_name); string_free(l_ptr->xtra_name); } /* Free the list */ FREE(list); } /* * Angband-specific code used to send a list of objects to the port. * This allows in-game data to be read. * * Hack - we assume every object in the list is 'visible' */ void Term_write_list(s16b o_idx, byte list_type) { term_list *list, *l_ptr; int i = 0; object_type *o_ptr; char o_name[256]; /* Get list length */ int num = get_list_length(o_idx); /* Empty? */ if (!num) { /* We have an empty list */ save_object_list(NULL, 0, list_type); return; } /* Create the list */ C_MAKE(list, num, term_list); /* Fill with information */ OBJ_ITT_START (o_idx, o_ptr) { /* Get object list element */ l_ptr = &list[i]; /* Stores are special */ if (list_type == LIST_STORE) { /* Set object flags */ set_basic_flags(l_ptr, o_ptr, TRUE); /* Describe the object */ object_desc_store(o_name, o_ptr, TRUE, 3, 256); } else { /* Set object flags */ set_basic_flags(l_ptr, o_ptr, FALSE); /* Describe the object */ object_desc(o_name, o_ptr, TRUE, 3, 256); } l_ptr->o_name = string_make(o_name); /* Increment counter */ i++; } OBJ_ITT_END; /* Save for later */ save_object_list(list, num, list_type); for (i = 0; i < num; i++) { l_ptr = &list[i]; /* Free the strings */ string_free(l_ptr->o_name); string_free(l_ptr->xtra_name); } /* Free the list */ FREE(list); } #else /* TERM_USE_LIST */ /*** Make generic do-nothing functions ***/ void Term_write_equipment(void) { /* Do nothing */ } void Term_write_list(s16b o_idx, byte list_type) { /* Ignore parameters */ (void)o_idx; (void)list_type; /* Do nothing */ } #endif /* TERM_USE_LIST */ zangband/src/maid-x11.c0000644000000000000000000005036610250356274013632 0ustar rootroot/* File: maid-x11.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file defines some "XImage" manipulation functions for X11. * * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr). * * BMP format support by Denis Eropkin (denis@dream.homepage.ru). * * Major fixes and cleanup by Ben Harrison (benh@phial.com). * * This file is designed to be "included" by "main-x11.c" or "main-xaw.c", * which will have already "included" several relevant header files. */ #include "angband.h" #ifdef USE_XMAID #ifndef __MAKEDEPEND__ #include #include #include #include #endif /* __MAKEDEPEND__ */ /* Include our headers */ #include "maid-x11.h" #ifdef SUPPORT_GAMMA static bool gamma_table_ready = FALSE; static int gamma_val = 0; #endif /* SUPPORT_GAMMA */ /* * Hack -- Convert an RGB value to an X11 Pixel, or die. */ u32b create_pixel(Display *dpy, byte red, byte green, byte blue) { Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)); XColor xcolour; #ifdef SUPPORT_GAMMA if (!gamma_table_ready) { cptr str = getenv("ANGBAND_X11_GAMMA"); if (str != NULL) gamma_val = atoi(str); gamma_table_ready = TRUE; /* Only need to build the table if gamma exists */ if (gamma_val) build_gamma_table(gamma_val); } /* Hack -- Gamma Correction */ if (gamma_val > 0) { red = gamma_table[red]; green = gamma_table[green]; blue = gamma_table[blue]; } #endif /* SUPPORT_GAMMA */ /* Build the color */ xcolour.red = red * 255; xcolour.green = green * 255; xcolour.blue = blue * 255; xcolour.flags = DoRed | DoGreen | DoBlue; /* Attempt to Allocate the Parsed color */ if (!(XAllocColor(dpy, cmap, &xcolour))) { u32b i, mincolour = 0; u32b mindiff, diff; XColor *colours; u32b numcolours = 1 << DefaultDepth(dpy, DefaultScreen(dpy)); /* Allocate the place where we can store the colourmap */ C_MAKE(colours, numcolours, XColor); /* Initialize */ for (i = 0; i < numcolours; i++) { colours[i].pixel = i; } /* Load the colourmap */ XQueryColors(dpy, cmap, colours, numcolours); while (TRUE) { mindiff = 0xFFFFFFFF; /* Find the closest colour */ for (i = 0; i < numcolours; i++) { /* Work out the 'difference' between the colours */ diff = (u32b) abs((long) xcolour.blue - (long) colours[i].blue); diff += (u32b) abs((long) xcolour.red - (long) colours[i].red); diff += (u32b) abs((long) xcolour.green - (long) colours[i].green); /* Multiply by the 'colour factor' */ diff *= 3; /* Add in the effects of brightness */ diff += (u32b) abs((long) xcolour.blue + (long) xcolour.red + (long) xcolour.green - (long) colours[i].blue - (long) colours[i].red - (long) colours[i].green); /* Is it a better match? */ if (diff < mindiff) { mindiff = diff; mincolour = i; } } /* Change to the new colour */ xcolour.blue = colours[mincolour].blue; xcolour.red = colours[mincolour].red; xcolour.green = colours[mincolour].green; /* Delete the old colour, so do not loop if it is read/write */ colours[mincolour].blue = 0; colours[mincolour].red = 0; colours[mincolour].green = 0; /* Keep on looping if we still cannot get the colour. */ if (XAllocColor(dpy, cmap, &xcolour)) break; } /* free the colour map */ FREE(colours); } return (xcolour.pixel); } #ifdef USE_GRAPHICS /* * The Win32 "BITMAPFILEHEADER" type. */ typedef struct BITMAPFILEHEADER { u16b bfType; u32b bfSize; u16b bfReserved1; u16b bfReserved2; u32b bfOffBits; } BITMAPFILEHEADER; /* * The Win32 "BITMAPINFOHEADER" type. */ typedef struct BITMAPINFOHEADER { u32b biSize; u32b biWidth; u32b biHeight; u16b biPlanes; u16b biBitCount; u32b biCompresion; u32b biSizeImage; u32b biXPelsPerMeter; u32b biYPelsPerMeter; u32b biClrUsed; u32b biClrImportand; } BITMAPINFOHEADER; /* * The Win32 "RGBQUAD" type. */ typedef struct RGBQUAD { unsigned char b, g, r; unsigned char filler; } RGBQUAD; /*** Helper functions for system independent file loading. ***/ static byte get_byte(FILE *fff) { /* Get a character, and return it */ return (getc(fff) & 0xFF); } static void rd_byte(FILE *fff, byte *ip) { *ip = get_byte(fff); } static void rd_u16b(FILE *fff, u16b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u16b) (get_byte(fff)) << 8); } static void rd_u32b(FILE *fff, u32b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u32b) (get_byte(fff)) << 8); (*ip) |= ((u32b) (get_byte(fff)) << 16); (*ip) |= ((u32b) (get_byte(fff)) << 24); } /* * Read a Win32 BMP file. * * This function replaces the old ReadRaw and RemapColors functions. * * Assumes that the bitmap has a size such that no padding is needed in * various places. Currently only handles bitmaps with 3 to 256 colors. */ XImage *ReadBMP(Display *dpy, char *Name) { Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int depth = DefaultDepth(dpy, DefaultScreen(dpy)); FILE *f; BITMAPFILEHEADER fileheader; BITMAPINFOHEADER infoheader; XImage *Res = NULL; char *Data; int ncol; int total; int i, j; u32b x, y; unsigned long clr_pixels[256]; /* Open the BMP file */ f = fopen(Name, "r"); /* No such file */ if (f == NULL) { return (NULL); } /* Read the "BITMAPFILEHEADER" */ rd_u16b(f, &(fileheader.bfType)); rd_u32b(f, &(fileheader.bfSize)); rd_u16b(f, &(fileheader.bfReserved1)); rd_u16b(f, &(fileheader.bfReserved2)); rd_u32b(f, &(fileheader.bfOffBits)); /* Read the "BITMAPINFOHEADER" */ rd_u32b(f, &(infoheader.biSize)); rd_u32b(f, &(infoheader.biWidth)); rd_u32b(f, &(infoheader.biHeight)); rd_u16b(f, &(infoheader.biPlanes)); rd_u16b(f, &(infoheader.biBitCount)); rd_u32b(f, &(infoheader.biCompresion)); rd_u32b(f, &(infoheader.biSizeImage)); rd_u32b(f, &(infoheader.biXPelsPerMeter)); rd_u32b(f, &(infoheader.biYPelsPerMeter)); rd_u32b(f, &(infoheader.biClrUsed)); rd_u32b(f, &(infoheader.biClrImportand)); /* Verify the header */ if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) { quit_fmt("Incorrect BMP file format %s", Name); } /* The two headers above occupy 54 bytes total */ /* The "bfOffBits" field says where the data starts */ /* The "biClrUsed" field does not seem to be reliable */ /* Compute number of colors recorded */ ncol = (fileheader.bfOffBits - 54) / 4; for (i = 0; i < ncol; i++) { RGBQUAD clrg; /* Read an "RGBQUAD" */ rd_byte(f, &(clrg.b)); rd_byte(f, &(clrg.g)); rd_byte(f, &(clrg.r)); rd_byte(f, &(clrg.filler)); /* Analyze the color */ clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b); } /* Determine total bytes needed for image */ i = 1; j = (depth - 1) >> 2; while (j >>= 1) i <<= 1; total = infoheader.biWidth * infoheader.biHeight * i; /* Allocate image memory */ C_MAKE(Data, total, char); Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset */ , Data, infoheader.biWidth, infoheader.biHeight, 32 /*bitmap_pad */ , 0 /*bytes_per_line */ ); /* Failure */ if (Res == NULL) { KILL(Data); fclose(f); return (NULL); } for (y = 0; y < infoheader.biHeight; y++) { u32b y2 = infoheader.biHeight - y - 1; for (x = 0; x < infoheader.biWidth; x++) { int ch = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); if (infoheader.biBitCount == 24) { int c3, c2 = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); c3 = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3)); } else if (infoheader.biBitCount == 8) { XPutPixel(Res, x, y2, clr_pixels[ch]); } else if (infoheader.biBitCount == 4) { XPutPixel(Res, x, y2, clr_pixels[ch / 16]); x++; XPutPixel(Res, x, y2, clr_pixels[ch % 16]); } else { /* Technically 1 bit is legal too */ quit_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name); } } } fclose(f); return Res; } /* ========================================================*/ /* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */ /* ========================================================*/ /* * to save ourselves some labour, define a maximum expected icon width here: */ #define MAX_ICON_WIDTH 32 /* some static variables for composing and decomposing pixel values into * red, green and blue values */ static unsigned long redMask, greenMask, blueMask; static int redShift, greenShift, blueShift; /* * Use smooth rescaling? */ bool smoothRescaling = TRUE; /* * GetScaledRow reads a scan from the given XImage, scales it smoothly * and returns the red, green and blue values in arrays. * The values in this arrays must be divided by a certain value that is * calculated in ScaleIcon. * x, y is the position, iw is the input width and ow the output width * redScan, greenScan and blueScan must be sufficiently sized */ static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan) { int xi, si, sifrac, ci, cifrac, addWhole, addFrac; unsigned long pix; int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue; bool getNextPix; if (iw == ow) { /* unscaled */ for (xi = 0; xi < ow; xi++) { pix = XGetPixel(Im, x + xi, y); redScan[xi] = (pix >> redShift) & redMask; greenScan[xi] = (pix >> greenShift) & greenMask; blueScan[xi] = (pix >> blueShift) & blueMask; } } else if (iw < ow) { /* scaling by subsampling (grow) */ iw--; ow--; /* read first pixel: */ pix = XGetPixel(Im, x, y); nextRed = (pix >> redShift) & redMask; nextGreen = (pix >> greenShift) & greenMask; nextBlue = (pix >> blueShift) & blueMask; prevRed = nextRed; prevGreen = nextGreen; prevBlue = nextBlue; /* si and sifrac give the subsampling position: */ si = x; sifrac = 0; /* getNextPix tells us, that we need the next pixel */ getNextPix = TRUE; for (xi = 0; xi <= ow; xi++) { if (getNextPix) { prevRed = nextRed; prevGreen = nextGreen; prevBlue = nextBlue; if (xi < ow) { /* only get next pixel if in same icon */ pix = XGetPixel(Im, si + 1, y); nextRed = (pix >> redShift) & redMask; nextGreen = (pix >> greenShift) & greenMask; nextBlue = (pix >> blueShift) & blueMask; } } /* calculate subsampled color values: */ /* division by ow occurs in ScaleIcon */ redScan[xi] = prevRed * (ow - sifrac) + nextRed * sifrac; greenScan[xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac; blueScan[xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac; /* advance sampling position: */ sifrac += iw; if (sifrac >= ow) { si++; sifrac -= ow; getNextPix = TRUE; } else { getNextPix = FALSE; } } } else { /* scaling by averaging (shrink) */ /* width of an output pixel in input pixels: */ addWhole = iw / ow; addFrac = iw % ow; /* start position of the first output pixel: */ si = x; sifrac = 0; /* get first input pixel: */ pix = XGetPixel(Im, x, y); nextRed = (pix >> redShift) & redMask; nextGreen = (pix >> greenShift) & greenMask; nextBlue = (pix >> blueShift) & blueMask; for (xi = 0; xi < ow; xi++) { /* find endpoint of the current output pixel: */ ci = si + addWhole; cifrac = sifrac + addFrac; if (cifrac >= ow) { ci++; cifrac -= ow; } /* take fraction of current input pixel (starting segment): */ redScan[xi] = nextRed * (ow - sifrac); greenScan[xi] = nextGreen * (ow - sifrac); blueScan[xi] = nextBlue * (ow - sifrac); si++; /* add values for whole pixels: */ while (si < ci) { pix = XGetPixel(Im, si, y); redScan[xi] += ((pix >> redShift) & redMask) * ow; greenScan[xi] += ((pix >> greenShift) & greenMask) * ow; blueScan[xi] += ((pix >> blueShift) & blueMask) * ow; si++; } /* add fraction of current input pixel (ending segment): */ if (xi < ow - 1) { /* only get next pixel if still in icon: */ pix = XGetPixel(Im, si, y); nextRed = (pix >> redShift) & redMask; nextGreen = (pix >> greenShift) & greenMask; nextBlue = (pix >> blueShift) & blueMask; } sifrac = cifrac; if (sifrac > 0) { redScan[xi] += nextRed * sifrac; greenScan[xi] += nextGreen * sifrac; blueScan[xi] += nextBlue * sifrac; } } } } /* * PutRGBScan takes arrays for red, green and blue and writes pixel values * according to this values in the XImage-structure. w is the number of * pixels to write and div is the value by which all red/green/blue values * are divided first. */ static void PutRGBScan(XImage *Im, int x, int y, int w, int div, unsigned long *redScan, unsigned long *greenScan, unsigned long *blueScan) { int xi; unsigned long pix; unsigned long adj = div / 2; for (xi = 0; xi < w; xi++) { pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) + ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) + ((((blueScan[xi] + adj) / div) & blueMask) << blueShift)); XPutPixel(Im, x + xi, y, pix); } } /* * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut, * locate (x2, y2). * Source size is (ix, iy) and destination size is (ox, oy). * It does this by getting icon scan line from GetScaledScan and handling * them the same way as pixels are handled in GetScaledScan. * This even allows icons to be scaled differently in horizontal and * vertical directions (eg. shrink horizontal, grow vertical). */ static void ScaleIcon(XImage *ImIn, XImage *ImOut, int x1, int y1, int x2, int y2, int ix, int iy, int ox, int oy) { int div; int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac; /* buffers for pixel rows: */ unsigned long prevRed[MAX_ICON_WIDTH]; unsigned long prevGreen[MAX_ICON_WIDTH]; unsigned long prevBlue[MAX_ICON_WIDTH]; unsigned long nextRed[MAX_ICON_WIDTH]; unsigned long nextGreen[MAX_ICON_WIDTH]; unsigned long nextBlue[MAX_ICON_WIDTH]; unsigned long tempRed[MAX_ICON_WIDTH]; unsigned long tempGreen[MAX_ICON_WIDTH]; unsigned long tempBlue[MAX_ICON_WIDTH]; bool getNextRow; /* get divider value for the horizontal scaling: */ if (ix == ox) div = 1; else if (ix < ox) div = ox - 1; else div = ix; if (iy == oy) { /* no scaling needed vertically: */ for (yi = 0; yi < oy; yi++) { GetScaledRow(ImIn, x1, y1 + yi, ix, ox, tempRed, tempGreen, tempBlue); PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue); } } else if (iy < oy) { /* scaling by subsampling (grow): */ iy--; oy--; div *= oy; /* get first row: */ GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue); /* si and sifrac give the subsampling position: */ si = y1; sifrac = 0; /* getNextRow tells us, that we need the next row */ getNextRow = TRUE; for (yi = 0; yi <= oy; yi++) { if (getNextRow) { for (xi = 0; xi < ox; xi++) { prevRed[xi] = nextRed[xi]; prevGreen[xi] = nextGreen[xi]; prevBlue[xi] = nextBlue[xi]; } if (yi < oy) { /* only get next row if in same icon */ GetScaledRow(ImIn, x1, si + 1, ix, ox, nextRed, nextGreen, nextBlue); } } /* calculate subsampled color values: */ /* division by oy occurs in PutRGBScan */ for (xi = 0; xi < ox; xi++) { tempRed[xi] = (prevRed[xi] * (oy - sifrac) + nextRed[xi] * sifrac); tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) + nextGreen[xi] * sifrac); tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) + nextBlue[xi] * sifrac); } /* write row to output image: */ PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue); /* advance sampling position: */ sifrac += iy; if (sifrac >= oy) { si++; sifrac -= oy; getNextRow = TRUE; } else { getNextRow = FALSE; } } } else { /* scaling by averaging (shrink) */ div *= iy; /* height of a output row in input rows: */ addWhole = iy / oy; addFrac = iy % oy; /* start position of the first output row: */ si = y1; sifrac = 0; /* get first input row: */ GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue); for (yi = 0; yi < oy; yi++) { /* find endpoint of the current output row: */ ci = si + addWhole; cifrac = sifrac + addFrac; if (cifrac >= oy) { ci++; cifrac -= oy; } /* take fraction of current input row (starting segment): */ for (xi = 0; xi < ox; xi++) { tempRed[xi] = nextRed[xi] * (oy - sifrac); tempGreen[xi] = nextGreen[xi] * (oy - sifrac); tempBlue[xi] = nextBlue[xi] * (oy - sifrac); } si++; /* add values for whole pixels: */ while (si < ci) { GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue); for (xi = 0; xi < ox; xi++) { tempRed[xi] += nextRed[xi] * oy; tempGreen[xi] += nextGreen[xi] * oy; tempBlue[xi] += nextBlue[xi] * oy; } si++; } /* add fraction of current input row (ending segment): */ if (yi < oy - 1) { /* only get next row if still in icon: */ GetScaledRow(ImIn, x1, si, ix, ox, nextRed, nextGreen, nextBlue); } sifrac = cifrac; for (xi = 0; xi < ox; xi++) { tempRed[xi] += nextRed[xi] * sifrac; tempGreen[xi] += nextGreen[xi] * sifrac; tempBlue[xi] += nextBlue[xi] * sifrac; } /* write row to output image: */ PutRGBScan(ImOut, x2, y2 + yi, ox, div, tempRed, tempGreen, tempBlue); } } } static XImage *ResizeImageSmooth(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy) { Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int width1, height1, width2, height2; int x1, x2, y1, y2; XImage *Tmp; char *Data; width1 = Im->width; height1 = Im->height; width2 = ox * width1 / ix; height2 = oy * height1 / iy; Data = (char *) malloc(width2 * height2 * Im->bits_per_pixel / 8); Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0); /* compute values for decomposing pixel into color values: */ redMask = Im->red_mask; redShift = 0; while ((redMask & 1) == 0) { redShift++; redMask >>= 1; } greenMask = Im->green_mask; greenShift = 0; while ((greenMask & 1) == 0) { greenShift++; greenMask >>= 1; } blueMask = Im->blue_mask; blueShift = 0; while ((blueMask & 1) == 0) { blueShift++; blueMask >>= 1; } /* scale each icon: */ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy) { for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox) { ScaleIcon(Im, Tmp, x1, y1, x2, y2, ix, iy, ox, oy); } } return Tmp; } /* * Resize an image. */ XImage *ResizeImage(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy) { Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int width1, height1, width2, height2; int x1, x2, y1, y2, Tx, Ty; int *px1, *px2, *dx1, *dx2; int *py1, *py2, *dy1, *dy2; XImage *Tmp; char *Data; if (smoothRescaling && (ix != ox || iy != oy) && visual->class == TrueColor) { return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy); } width1 = Im->width; height1 = Im->height; width2 = ox * width1 / ix; height2 = oy * height1 / iy; Data = (char *) malloc(width2 * height2 * Im->bits_per_pixel / 8); Tmp = XCreateImage(dpy, visual, Im->depth, ZPixmap, 0, Data, width2, height2, 32, 0); if (ix > ox) { px1 = &x1; px2 = &x2; dx1 = &ix; dx2 = &ox; } else { px1 = &x2; px2 = &x1; dx1 = &ox; dx2 = &ix; } if (iy > oy) { py1 = &y1; py2 = &y2; dy1 = &iy; dy2 = &oy; } else { py1 = &y2; py2 = &y1; dy1 = &oy; dy2 = &iy; } Ty = *dy1 / 2; for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2);) { Tx = *dx1 / 2; for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2);) { XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1)); (*px1)++; Tx -= *dx2; if (Tx < 0) { Tx += *dx1; (*px2)++; } } (*py1)++; Ty -= *dy2; if (Ty < 0) { Ty += *dy1; (*py2)++; } } return Tmp; } #endif /* USE_GRAPHICS */ #endif /* USE_XMAID */ zangband/src/main-ami.c0000755000000000000000000037777710250356274014026 0ustar rootroot/* :ts=3 File : main-ami.c DEVELOPER VERSION! Version : 1.006 (25 Dec 1998) Angband : 2.8.3/2.8.2/2.8.1 Purpose : Amiga module for Angband with graphics and sound Author : Mark Howson Email : markh@angband.org Original Author : Lars Haugseth Email : larshau@ifi.uio.no WWW : http://www.ifi.uio.no/~larshau Tab size : 3 Todo: Finish window backgrounds; allow proper tiling. Sort of started this. Remove window scroller code, as it's ugly and stupid? Does anyone actually /use/ this? Allow menu definitions for term windows too. Easy enough. I can't think of anything else that needs doing. Great! :) Changes: Graphics should load faster in most cases now. Looks in AngSound: for sounds as well as xtra/sound Added 'Toggle Borders' option in window menus, as well as corresponding 'noborders' option in settings.prf. Fixed broken options that turned Graphics on/off/etc. Added some extra code that /might/ improve stability when opening/closing term windows on certain systems? Menu configuration in xtra/cfg/menu.cfg */ /* What variant is this? Used in the highscore dump */ #define VARIANT "Zangband 2.7.2" /* Main 'assign' needed. Kick2.0+ systems usually don't need it anyway */ #define VERPATH "Zangband:" #define CGXSUPPORT /* Define for RTG support. Leave on */ #define ZANGBAND /* Define if this is Zangband. Zangband now has extra gfx */ /*#define GFXFUNCS */ /* Define if we allow gfx debugging functions. */ /* #define ANG283 */ /*#define ANG282 */ /* Based upon Angband 2.8.2 ? */ #include "angband.h" #ifdef USE_AMI cptr help_ami[] = { "Amiga module with graphics and sound", NULL }; #ifndef __CEXTRACT__ #include "vers.h" #ifndef __GNUC__ #undef byte /* Prevents conflicts with dos.h */ #endif #include "sound-ami.h" #include #ifndef __GNUC__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include /* To prevent warnings... */ #undef ID_BMHD #undef ID_BODY #undef ID_CAMG #undef ID_CMAP #undef ID_CRNG #undef ID_ILBM #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CGXSUPPORT #include #ifdef __GNUC__ # include #else # include # include #endif #endif #ifdef __GNUC__ # define __near #endif #endif #include "maid-grf.h" #define MAX_TERM_DATA 8 /* Maximum length a filename (including a path) can reach. Somewhat arbitary */ #define MAX_PATH_LENGTH 160 /* How much memory to allocate for the blanked out mousepointer */ #define BLANKPOINTER_SIZE 128 #define KICK30 ((kick_ver) >= 39) /* True if K3.0 or better */ #define KICK21 ((kick_ver) >= 38) /* True if K2.1 or better */ #define KICK20 ((kick_ver) >= 36) /* True if K2.0 or better */ #define KICK13 ((kick_ver) < 36) /* True if K1.3 or worse */ #define PEN( p ) ( penconv[ p ] ) /* Pen number conversion */ #define GPEN( p ) ( use_pub ? pubpens[ p ] : p ) /* Graphics pen number conversion */ #define FAIL( str ) return ( amiga_fail( str )) /* Failure */ #define MSG( x, y, txt ) amiga_text( x, y, strlen( txt ), 1, txt ); /* Char and attr under cursor */ #define CUR_A ( td->t.scr->a[ td->cursor_ypos ][ td->cursor_xpos ] ) #define CUR_C ( td->t.scr->c[ td->cursor_ypos ][ td->cursor_xpos ] ) #define CURSOR_PEN 4 /* Colour to use for cursor */ #define MAX_TERM_VERT 24 /* Max num of lines in a term (y) */ #define MAX_TERM_HORIZ 80 /* Max num of chars in a term (x) */ #define AB_GFXW 640 #define AB_GFXH 960 /* Size of 16x16 tile image */ #define AB_GFXB 8 #define DF_GFXW 256 #define DF_GFXH 792 #define DF_GFXB 5 /* Size of current bitmap...initialise by load_gfx() */ int GFXW, GFXH, GFXB; #define TOMW 512 #define TOMH 168 /* Size of tombstone image */ #define TOMB 4 #define DE_MGFX "gfx/tiles.raw" /* 8x8 tile image */ #define AB_MGFX "gfx/tiles256.raw" /* 16x16 tile image */ #define DE_MGFX_CMAP "gfx/tiles.cmap" /* Colour map for normal tiles */ #define AB_MGFX_CMAP "gfx/tiles256.cmap" /* Colour map for AB tiles */ #define MTOM "gfx/tomb.raw" /* Filename of tombstone image */ #define WPRF "settings.prf" /* Preferences file */ /* DisplayID specified with option -m */ char modestr[ 256 ] = ""; static byte palette256[1024]; /* 2.0 and better libraries - may not be available */ struct Library *GadToolsBase = NULL; struct Library *AslBase = NULL; #ifdef __GNUC__ struct ReqToolsBase *ReqToolsBase; #endif /* Need these libraries */ struct Library *DiskfontBase = NULL; struct IntuitionBase *IntuitionBase = NULL; struct GfxBase *GfxBase = NULL; struct Library *IFFBase = NULL; struct Library *CyberGfxBase = NULL; struct Library *DataTypesBase = NULL; struct Device *ConsoleDev = NULL; struct Device *ConsoleDevice = NULL; /* Data shared between terms */ typedef struct term_global { byte *chunky_tile; byte *chunky_mask; struct BitMap *gfxbm; struct BitMap *mapbm; struct BitMap *mskbm; int colours_free; byte resources_freed; } term_global; /* Term data structure. *Really* needs sorting out */ typedef struct term_data { term t; /* Term structure */ cptr name; /* Name string, eg. title */ short backw, backh; struct BitMap *background; /* Bitmap of special background */ struct Menu *menu; /* Ptr to menu strip, or NULL */ byte *bkgname; char fontname[64]; /* Name of font, ie. 'topaz/8'. Used by Save Windows */ struct Gadget ygad; /* Used for window scrollbars */ struct Gadget xgad; struct PropInfo ygadinfo,xgadinfo; struct Image ygadimage,xgadimage; BYTE use; /* Use this window */ BYTE iconified; /* Window is iconified ? */ BYTE xpos; /* Position of data in window. Think of this as an x offset */ BYTE ypos; /* Position of data (y) */ BYTE scroll; /* Put scrollers in borders? */ BYTE backdrop; /* TRUE for no window borders */ BYTE autoscale; BYTE cols; /* Number of columns */ BYTE rows; /* Number of rows */ short wx; /* Window x-pos, in pixels */ short wy; /* Window y-pos, in pixels */ short ww; /* Window width */ short wh; /* Window height */ BYTE fix_w; BYTE fix_h; BYTE fw; /* Font width */ BYTE fh; /* Font height */ BYTE fb; /* Font baseline */ struct TextFont *font; /* Font pointers */ BYTE ownf; /* Font is owned by this term */ struct Window *win; /* Window pointer */ struct RastPort *wrp; /* RastPort of window */ struct RastPort *rp; /* RastPort of screen or window */ int gfx_w,gfx_h; int map_w, map_h; int map_x, map_y; int mpt_w, mpt_h; int cursor_xpos, cursor_ypos; bool cursor_visible; bool cursor_lit; int cursor_frame; int cursor_map; BYTE notitle; BYTE avoidbar; } term_data; /* Term data for all windows */ static term_data data[ MAX_TERM_DATA ]; static term_global tglob; bool use_mask = FALSE; bool use_bkg = FALSE; static char do_after; /* Window names */ static char *term_name[] = { "Main", "Mirror", "Recall", "Choice", "5th Window", "6th Window", "7th Window", "8th Window", "Main", "Term-1","Term-2","Term-3", "Term-4","Term-5","Term-6","Term-7", NULL }; static char *term_short[] = { "MAIN","MIRROR","RECALL","CHOICE", "WIN5TH","WIN6TH","WIN7TH","WIN8TH", "MAIN","TERM-1","TERM-2","TERM-3", "TERM-4","TERM-5","TERM-6","TERM-7", NULL }; /* Nasty Optimise hack : Writes directly to memory to speed things up. */ bool nasty_optimise_gfx = FALSE; /* Can we display a palette requester? (ie. is screen type ok, reqtools etc. */ bool amiga_palette = FALSE; /* We don't want to use this hack for the cursor, so we use the next variable to prevent that. If you have 'nasty_optimise_gfx' on when the menu's are being displayed, the flashing cursor will overwrite the menu on screen :( */ bool block_nasty_gfx = FALSE; /* Screen pointers */ static struct Screen *amiscr = NULL; static struct Screen *pubscr = NULL; static struct IOStdReq io_req; /* Visual info for gadtools menus */ static APTR *visinfo; /* TextAttr for screen font */ static struct TextAttr ScrAttr, *scrattr = &ScrAttr; /* Screen characteristics */ static ULONG screen_width = 0; static ULONG screen_height = 0; static LONG screen_overscan = -1; static ULONG screen_depth = 4; static ULONG screen_cols = 16; static ULONG scr_m = 0; static BOOL use_aga = FALSE; static BOOL use_cyber = FALSE; static BOOL screen_enhanced = FALSE; static term_data *term_curs = NULL; /* Last term for cursor */ static char tmpstr[ 256 ]; /* Temp string for general usage */ static BOOL iconified = FALSE; /* Iconify status of windows */ static BOOL use_menus = TRUE; /* Use intuition menus? */ static struct InputEvent ie; /* Window input event */ /* Version of KickStart available. Typical values are: 34 Kickstart 1.3 only 36 Kickstart 2.0 38 Kickstart 2.1 39 Kickstart 3.0 or better */ static int kick_ver = 0; static int use_pub = FALSE; /* Use public screen? */ static int publock = FALSE; /* TRUE if public screen Locked() */ static int backdrop = FALSE; /* Use a backdrop main window */ static BOOL blankmouse = FALSE; /* Use Mouse Blanking ?? */ static BOOL pointer_visible = TRUE; /* Pointer visibility status */ static void *blankpointer; /* Points to memory for blank mouse sprite. Must be CHIP MEM! */ static ULONG sigmask = 0L; /* Holds mask for Wait() - see amiga_event() */ /* Convert textual pens to screen pens */ static UWORD penconv[ 16 ] = { 0,1,2,4,11,15,9,6,3,1,13,4,11,15,8,5 }; static byte custpens[ 512 ]; static ULONG custpalette[ 512 * 3 + 4]; static LONG gfxpens[ 512 ]; static byte obtain_mask[ 512 ]; /* Default colour palette; 16 for text. Depends upon values of TERM_ in defines.h */ static ULONG default_colours[ 16 ] = { 0x000000, 0xffffff, 0xc7c7c7, 0xff9200, 0xff0000, 0x00cd00, 0x172cff, 0xc86400, 0x8a8a8a, 0xe0e0e0, 0xa500ff, 0xfffd00, 0xff00bc, 0x00ff00, 0x00c8ff, 0xffcc80 }; /* Palette, 32 bits per gun */ static ULONG palette32[ 32 * 3 + 2 ]; /* Palette, 4 bits per gun */ static UWORD palette4[ 32 ]; /* Version string */ static char ver[] = "\0$VER: "VARIANT" "__BABLOSDATE__; struct AmiSound { char *Name; int Volume; int Channel; int Rate; int Repeats; int Memory; struct SoundInfo *Address; }; static char *sound_name_desc = NULL; static struct AmiSound *sound_data = NULL; static int sounds_needed = 0; /* Ouch - hack */ static struct AmiSound *sound_ref[SOUND_MAX][8]; static int channel_last[ 4 ] = { -1, -1, -1, -1 }; static int channel_num[ 4 ] = { 1, 1, 1, 1 }; static int has_sound = FALSE; #define MENUMAX 300 /* Menu userdata indexes */ #define MNU_SCALEDMAP 1001 #define MNU_PALETTE 1002 #define MNU_SAVE_PALETTE 1003 #define MNU_LOAD_PALETTE 1004 #define MNU_EXPORT_HS 1005 #define MNU_GRAPHICS_OFF 1006 #define MNU_GFXMAP 1007 #define MNU_SAVE_WINDOWS 1008 #define MNU_GRAPHICS_8 1009 #define MNU_GRAPHICS_16 1010 #define MNU_WINDOW_FONT 1014 #define MNU_WINDOW_REDRAW 1015 #define MNU_WINDOW_TOGGLEBORDERS 1016 /* Special offset indexes */ #define MNU_KEYCOM 2001 #define MNU_CKEYCOM 3001 #define MNU_OPTION 4001 #define MNU_HELP 5001 #define MNU_WINDOW 6001 /* Macro for menu userdata keycodes and help */ #define MKC( c ) (void *)( MNU_KEYCOM + c ) #define MCC( c ) (void *)( MNU_CKEYCOM + c ) #define MHL( c ) (void *)( MNU_HELP + c ) #define MWI( c ) (void *)( MNU_WINDOW + c ) static struct Menu *menu = NULL; /* Option code gone. It was rather impractical and was taking a fair bit of work to update, so I dumped it. Ought to reinstore this. */ /* Menu items that comes after the options */ struct NewMenu post_item[] = { { NM_TITLE, "Windows", 0, 0, 0, 0 }, { NM_ITEM, "Term 1", 0, CHECKIT | MENUTOGGLE, 0, MWI( 1 ) }, { NM_ITEM, "Term 2", 0, CHECKIT | MENUTOGGLE, 0, MWI( 2 ) }, { NM_ITEM, "Term 3", 0, CHECKIT | MENUTOGGLE, 0, MWI( 3 ) }, { NM_ITEM, "Term 4", 0, CHECKIT | MENUTOGGLE, 0, MWI( 4 ) }, { NM_ITEM, "Term 5", 0, CHECKIT | MENUTOGGLE, 0, MWI( 5 ) }, { NM_ITEM, "Term 6", 0, CHECKIT | MENUTOGGLE, 0, MWI( 6 ) }, { NM_ITEM, "Term 7", 0, CHECKIT | MENUTOGGLE, 0, MWI( 7 ) }, { NM_ITEM, NM_BARLABEL, 0, 0, 0, 0 }, { NM_ITEM, "Save Windows", "w",0,0,(void *)MNU_SAVE_WINDOWS }, { NM_END, NULL, 0, 0, 0, 0 }, { 255, 0, 0, 0, 0, 0 } }; struct NewMenu menu_ptr[MENUMAX]; /* Menu for each of the term windows */ struct NewMenu window_menu[] = { { NM_TITLE, "Window", 0, 0, 0, 0 }, { NM_ITEM, "Font", "f", 0, 0, (void *)MNU_WINDOW_FONT }, { NM_ITEM, "Redraw", "r", 0, 0, (void *)MNU_WINDOW_REDRAW }, { NM_ITEM, "Toggle Borders", "b", 0, 0, (void *)MNU_WINDOW_TOGGLEBORDERS }, { NM_END, NULL, 0, 0, 0, 0 }, { 255, 0, 0, 0, 0, 0 } }; /* Menu array */ static struct NewMenu newmenu[ MENUMAX ]; extern void amiga_gfxmap(void); static errr amiga_pict( int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp ); errr init_ami ( void ); static int load_backpic ( term_data *t, char *name ); static BOOL get_screenmode ( char *modestr ); void amiga_open_libs ( void ); void open_term ( int n, bool doall ); void close_term ( int n ); static void init_term ( term_data *td ); static void link_term ( int i ); static void free_term ( term_data *td ); static BOOL strreq ( char *s, char *d ); static void request_font ( char *str ); static void request_mode ( char *str ); static char *token_line(char *s, char *p); int read_prefs( void ); int read_menus( void ); static BOOL process_bool ( char *param ); static void process_gfx ( char *param ); static errr amiga_user ( int n ); static void amiga_nuke ( term *t ); static void amiga_open ( term *t ); static errr amiga_curs ( int x, int y ); static errr amiga_wipe ( int x, int y, int n ); static errr amiga_clear ( void ); static errr amiga_text ( int x, int y, int n, byte a, cptr s ); static errr amiga_xtra ( int n, int v ); static errr amiga_flush ( int v ); static void process_msg ( int i, ULONG iclass, UWORD icode, UWORD iqual, APTR iaddr ); static void calc_sigmask ( void ); errr amiga_event ( int v ); static errr amiga_react ( int v ); int amiga_tomb ( void ); void tomb_str ( int y, char *str ); void handle_rawkey ( UWORD code, UWORD qual, APTR addr ); void handle_menupick ( int mnum, int term ); static void amiga_save_file ( void ); static char get_bool ( BOOL opt ); static void cursor_on ( term_data *td ); static void cursor_off ( term_data *td ); static void cursor_anim ( void ); static int load_gfx ( void ); static int conv_gfx ( void ); static int size_gfx ( term_data *td ); static void put_gfx ( struct RastPort *rp, int x, int y, int chr, int col ); static int breakfunc(void); static int amiga_fail ( char *msg ); static void amiga_map ( void ); static void free_pen(int pen); static int abandon_pen(void); static int alloc_pen(ULONG r, ULONG g, ULONG b); void load_palette ( void ); static int read_enhanced_palette ( void ); static int read_normal_palette(void); static char *handle_font(struct term_data *td, char *fontname); ULONG trans ( byte g ); int create_menus ( void ); void update_menus ( void ); int init_sound ( void ); void free_sound ( void ); static void play_sound ( int v ); void put_gfx_map ( term_data *td, int x, int y, int c, int a ); struct BitMap *alloc_bitmap ( int width, int height, int depth, ULONG flags, struct BitMap *friend ); void free_bitmap ( struct BitMap *bitmap ); void scale_bitmap ( struct BitMap *srcbm, int srcw, int srch, struct BitMap *dstbm, int dstw, int dsth ); void remap_bitmap ( struct BitMap *srcbm, struct BitMap *dstbm, long *pens, int width, int height ); int depth_of_bitmap ( struct BitMap *bm ); void amiga_show ( char *str ); void amiga_redefine_colours ( void ); void amiga_makepath ( char *name ); void amiga_save_palette ( void ); void amiga_load_palette ( void ); void amiga_hs_to_ascii ( void ); void amiga_user_name ( char *buf ); void amiga_write_user_name ( char *name ); static int get_p_attr ( void ); static int get_p_char ( void ); static void init_default_palette(void); static void amiga_gfx(int type); static int find_menuitem(int *rmenu, int *item, void *ud); static void quick_BltBitMapRastPort( struct BitMap *src, int x, int y, struct RastPort *rp, int dx, int dy, int dw, int dh, int mode); static void quick_Text(struct RastPort *rp, int col, char *s, int n, int dx, int dy); static void ami_map_info(map_block *mb_ptr, term_map *map, vptr dummy); static void ami_player_move(int x, int y, vptr dummy); errr init_ami( void ) { int i; struct NewScreen new_scr; struct NewWindow new_win; struct DimensionInfo diminfo; int pw,ph,px,py,maxw,maxh,th,barh; int fsize; BOOL changed; term_data *ts = &data[ 0 ]; term_data *tt = NULL; /* Open Amiga libraries */ amiga_open_libs(); #ifndef __GNUC__ onbreak(breakfunc); #endif /* Can't have palette requester if we don't have reqtools */ if (ReqToolsBase) amiga_palette = TRUE; /* Ought to check the result of this, but if we don't have 128 bytes of chip memory left we're in all sorts of trouble... */ blankpointer = AllocMem(BLANKPOINTER_SIZE,MEMF_CLEAR | MEMF_CHIP); /* See if we specified graphics | sound via command line */ use_sound = arg_sound; if (arg_graphics) use_graphics = GRAPHICS_ORIGINAL; else use_graphics = GRAPHICS_NONE; /* Initialise global data */ tglob.resources_freed = FALSE; tglob.chunky_tile = NULL; tglob.chunky_mask = NULL; /* Ugh. XXX XXX XXX Clean up! */ for (i = 0 ; i < 512 ; i++) custpens[i] = 0; for (i = 0 ; i < 256 ; i++) gfxpens[i] = -1; for (i = 0 ; i < 512 ; i++) obtain_mask[i] = 0; /* Initialise all terms */ for ( i = 0; i < MAX_TERM_DATA; i++ ) init_term( &data[ i ] ); /* Always use the main term */ ts->use = TRUE; ts->iconified = FALSE; /* We *must* have kickstart 34 or later, which should be no problem */ if ( IntuitionBase->LibNode.lib_Version < 34 ) FAIL( "Sorry, this program requires Kickstart 1.3 or later." ); /* Read preferences file */ read_prefs(); /* Read menus */ read_menus(); /* XXX XXX XXX Command line options have priority */ if (arg_graphics) use_graphics = GRAPHICS_ORIGINAL; else use_graphics = GRAPHICS_NONE; if (arg_sound) use_sound = 1; arg_sound = use_sound; init_default_palette(); if (screen_enhanced) { if (!read_enhanced_palette()) FAIL("Can't read 256 colour palette! Need file tiles256.cmap, 1024 bytes"); } else if (use_graphics == GRAPHICS_ORIGINAL) { if (!read_normal_palette()) FAIL("Can't read normal colour palette! Need file tiles.cmap, 128 bytes"); } /* Initialize keyboard stuff */ ie.ie_NextEvent = NULL; ie.ie_Class = IECLASS_RAWKEY; ie.ie_SubClass = 0; /* Need gadtools.library to use menus */ if ( !GadToolsBase ) use_menus = FALSE; /* Search for prefered screenmode or public screen */ if (KICK20 && strlen( modestr ) > 0 ) { if (!get_screenmode( modestr )) FAIL("Display error."); } /* No extra term windows under K1.3 ever, unless someone really *wants* them */ if (KICK13) { bool bad = FALSE; for (i = 1 ; i < MAX_TERM_DATA; i++) { if (!bad && data[ i ].use) { puts("Sorry... Extra windows not currently supported under KS1.3 (upgrade!)"); bad = TRUE; } data[i].use = FALSE; } } /* Handle 'fix' attribute */ for ( i = 0 ; i < MAX_TERM_DATA; i++) { if (data[i].fix_w) data[i].fw = data[i].fix_w; if (data[i].fix_h) data[i].fh = data[i].fix_h; } /* Calculate window dimensions */ for ( i = 0 ; i < MAX_TERM_DATA; i++) { data[ i ].ww = data[ i ].fw * data[ i ].cols; data[ i ].wh = data[ i ].fh * data[ i ].rows; } /* Find a nice screenmode */ if ( (scr_m == 0) && (KICK30) ) { if (CyberGfxBase) { scr_m = BestCModeIDTags( CYBRBIDTG_NominalWidth, ts->ww, CYBRBIDTG_NominalHeight, ts->wh, TAG_END ); } else scr_m = BestModeID( BIDTAG_NominalWidth, ts->ww, BIDTAG_NominalHeight, ts->wh, BIDTAG_Depth, 4, TAG_END ); } /* Use default screenmode if we don't have any */ if ( scr_m == 0 || scr_m == INVALID_ID ) scr_m = ( DEFAULT_MONITOR_ID | HIRES_KEY ); /* Open custom screen */ if ( !use_pub ) { /* Need minimum screen depth of 4 */ if (screen_depth < 4) screen_depth = 4; /* If trying to use Adam Bolt tiles with < 256 col screen, silently push to 256 colours */ if (screen_enhanced && screen_depth < 8) screen_depth = 8; /* We want to use 32+ colours with graphics */ if (use_graphics == GRAPHICS_ORIGINAL) { if (KICK20 && screen_depth < (GFXB + 1)) { /* Get dimension data for screenmode */ if ( GetDisplayInfoData( NULL, (UBYTE *) &diminfo, sizeof( struct DimensionInfo ), DTAG_DIMS, scr_m )) { if ( diminfo.MaxDepth < GFXB + 1) screen_depth = GFXB + 1; } } } screen_cols = 1 << screen_depth; if ( KICK20 ) { amiscr = OpenScreenTags( NULL, screen_width ? SA_Width : TAG_IGNORE, screen_width ? screen_width : TAG_IGNORE, screen_height ? SA_Height : TAG_IGNORE, screen_height ? screen_height : TAG_IGNORE, (screen_overscan > 0) ? SA_Overscan : TAG_IGNORE, (screen_overscan > 0) ? screen_overscan : TAG_IGNORE, SA_Depth, screen_depth, SA_DisplayID, scr_m, /* SA_Font, scrattr, */ SA_Type, CUSTOMSCREEN, SA_Title, "Angband Screen", SA_ShowTitle, FALSE, SA_Quiet, TRUE, SA_Behind, TRUE, SA_AutoScroll, TRUE, SA_Interleaved, (KICK30) ? TRUE : FALSE, TAG_END ); } else { new_scr.LeftEdge = 0; new_scr.TopEdge = 0; new_scr.Width = screen_width; new_scr.Height = screen_height; new_scr.Depth = screen_depth; new_scr.DetailPen = 0; new_scr.BlockPen = 1; new_scr.ViewModes = HIRES; /* XXX XXX XXX */ new_scr.Type = CUSTOMSCREEN; /* new_scr.Font = scrattr; */ new_scr.DefaultTitle = "Angband Screen"; new_scr.Gadgets = NULL; new_scr.CustomBitMap = NULL; amiscr = OpenScreen( &new_scr ); } if (!amiscr) FAIL( "Unable to open Amiga screen." ); #ifdef CGXSUPPORT if (use_cyber) { if (!IsCyberModeID( scr_m )) use_cyber = FALSE; } #endif /* Initialize screen rastport */ ts->rp = &amiscr->RastPort; SetRast( ts->rp, PEN( 0 )); SetAPen( ts->rp, 1 ); SetBPen( ts->rp, 0 ); SetDrMd( ts->rp, JAM2 ); SetFont( ts->rp, ts->font ); if (KICK20 && GetDisplayInfoData( NULL, (UBYTE *) &diminfo, sizeof( struct DimensionInfo ), DTAG_DIMS, scr_m )) backdrop = TRUE; px = amiscr->LeftEdge; py = amiscr->TopEdge; pw = amiscr->Width; ph = amiscr->Height; screen_width = pw; screen_height = ph; } /* We are using a public screen */ else { /* Get depth */ screen_depth = depth_of_bitmap(pubscr->RastPort.BitMap); if (screen_depth > 8) screen_depth = 8; screen_cols = 1 << screen_depth; use_aga = FALSE; /* Size of public screen */ px = pubscr->LeftEdge; py = pubscr->TopEdge; pw = pubscr->Width; ph = pubscr->Height; /* Height difference between a window with or without a title bar */ th = pubscr->Font->ta_YSize + 1; maxw = 0; /* Find width of widest window */ for ( i = 0; i < MAX_TERM_DATA; i++ ) maxw = MAX( maxw, data[ i ].ww ); /* Find height of tallest window */ maxh = ts->wh + ts->notitle ? 0 : th; for ( i = 0; i < MAX_TERM_DATA; i++ ) { int tmp; tmp = data[ i ].wh + ( data[ i ].notitle ? 0 : th); if (data[i].use && tmp > maxh) maxh = tmp; } /* maxh += pubscr->WBorTop + pubscr->WBorBottom; */ /* Check if the public screen is large enough */ if ( pw < maxw || ph < maxh ) { strnfmt( tmpstr, 256, "Public screen is too small for window (%d x %d).", maxw, maxh ); FAIL( tmpstr ); } /* Use backdrop window if pubscreen is quiet */ backdrop = ( pubscr->Flags & SCREENQUIET ) ? TRUE : FALSE; /* Calculate screen bar height */ barh = pubscr->BarHeight + 1; /* Check for special window positions */ for ( i = 0; i < MAX_TERM_DATA; i++ ) { /* Position window at the left side of the screen */ if ( data[ i ].wx == -1 ) data[ i ].wx = pw - 1; /* Position window at the bottom of the screen */ if ( data[ i ].wy == -1 ) data[ i ].wy = ph - 1; /* Position window below screen bar */ if ( data[ i ].wy == -2 ) data[ i ].wy = barh; } } /* Now we need to load the palette for the screen (text pens!) */ init_default_palette(); load_palette(); /* Font autoscaling, which ought to be in a function... not enough time... */ if (ts->autoscale) { struct TextAttr attr; int maxsize = 0; int our_max = 24; fsize = 3; while (fsize < our_max) { /* Make sure the font name ends with .font */ if ( !strstr( ts->fontname, ".font" )) strcat( ts->fontname, ".font" ); /* Set font attributes */ attr.ta_Name = ts->fontname; attr.ta_YSize = fsize; attr.ta_Style = FS_NORMAL; attr.ta_Flags = (streq( ts->fontname, "topaz.font" ) && ( fsize == 8 || fsize == 9 )) ? FPF_ROMFONT : FPF_DISKFONT; /* Open font from disk */ ts->font = OpenDiskFont( &attr ); if (ts->font) { if (ts->font->tf_XSize * MAX_TERM_HORIZ <= pw && ts->font->tf_YSize * MAX_TERM_VERT <= ph) maxsize = fsize; CloseFont(ts->font); } fsize++; } if (maxsize) { /* printf("Accepted size %d\n",maxsize); */ attr.ta_YSize = maxsize; ts->font = OpenDiskFont( &attr ); if (!ts->font) maxsize = 0; else { ts->ownf = TRUE; /* Set font dimensions */ ts->fw = ts->font->tf_XSize; ts->fh = ts->font->tf_YSize; ts->fb = ts->font->tf_Baseline; /* printf("Auto x %d y %d b %d\n", ts->fw, ts->fh, ts->fb); */ SetFont(ts->rp, ts->font); } /* Recalc window widths */ for ( i = 0 ; i < MAX_TERM_DATA; i++) { data[ i ].ww = data[ i ].fw * data[ i ].cols; data[ i ].wh = data[ i ].fh * data[ i ].rows; } } if (!maxsize) FAIL("Autoscale failed!"); } /* Check window bounds, else Intuition might a) sulk b) crash */ for (i = 0 ; i < MAX_TERM_DATA; i++) { changed = FALSE; if (data[i].wx < px) { data[i].wx = px; changed = 1; } if (data[i].wy < py) { data[i].wy = py; changed = 1; } if (data[i].wy > (py + ph)) data[i].wy = 0; if (data[i].wx > (px + pw)) data[i].wx = 0; if ((data[i].ww + data[i].wx) > (px + pw)) { data[i].ww = (px + pw - data[i].wx); changed = 1; } if ((data[i].wh + data[i].wy) > (py + ph)) { data[i].wh = (py + ph - data[i].wy); changed = 1; } if (changed && data[i].use) printf("Window %d resized...Please change the window dimensions\n",i); } /* Get visual info for GadTools */ if ( use_menus ) { if (( visinfo = GetVisualInfo( use_pub ? pubscr : amiscr, TAG_END )) == NULL ) use_menus = FALSE; } if ( KICK20 ) { ts->win = OpenWindowTags( NULL, WA_Left, ts->wx, WA_Top, ts->wy, WA_InnerWidth, (backdrop) ? screen_width : ts->ww, WA_InnerHeight, (backdrop) ? screen_height : ts->wh, /* WA_InnerWidth, ts->ww, */ /* WA_InnerHeight, ts->wh, */ use_pub ? WA_PubScreen : WA_CustomScreen, use_pub ? pubscr : amiscr, WA_Backdrop, backdrop, WA_Borderless, backdrop, WA_GimmeZeroZero, !backdrop, WA_DragBar, !backdrop && !ts->notitle, WA_DepthGadget, !backdrop && !ts->notitle, WA_NewLookMenus, TRUE, backdrop ? TAG_IGNORE : WA_ScreenTitle, VARIANT, ( backdrop || ts->notitle ) ? TAG_IGNORE : WA_Title, ts->name, WA_Activate, TRUE, WA_RMBTrap, !use_menus, WA_ReportMouse, TRUE, WA_IDCMP, IDCMP_RAWKEY | IDCMP_INTUITICKS | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_MENUPICK | IDCMP_MENUVERIFY | IDCMP_INACTIVEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_CLOSEWINDOW, TAG_END ); } else { new_win.LeftEdge = ts->wx; new_win.TopEdge = ts->wy; new_win.Width = ts->ww; new_win.Height = ts->wh; new_win.DetailPen = 255; new_win.BlockPen = 255; new_win.IDCMPFlags = IDCMP_RAWKEY | IDCMP_INTUITICKS | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_MENUPICK | IDCMP_MENUVERIFY; new_win.Flags = WFLG_BACKDROP | WFLG_BORDERLESS | WFLG_REPORTMOUSE | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP; if ( backdrop ) new_win.Flags |= ( WFLG_BORDERLESS | WFLG_BACKDROP ); new_win.FirstGadget = NULL; new_win.CheckMark = NULL; new_win.Title = NULL; new_win.Screen = amiscr; new_win.BitMap = NULL; new_win.MinWidth = new_win.MinHeight = new_win.MaxWidth = new_win.MaxHeight = 0; new_win.Type = CUSTOMSCREEN; ts->win = OpenWindow( &new_win ); } if (!ts->win) FAIL( "Cannot open Amiga window."); /* Unlock public screen */ if ( publock ) { UnlockPubScreen( NULL, pubscr ); publock = FALSE; } /* Initialize main rastport */ ts->wrp = ts->win->RPort; SetRast( ts->wrp, PEN( 0 )); SetAPen( ts->wrp, 1 ); SetBPen( ts->wrp, 0 ); SetDrMd( ts->wrp, JAM2 ); SetFont( ts->wrp, ts->font ); /* Never use screen's rastport on public screen */ /* if ( use_pub ) */ ts->rp = ts->wrp; if ((IFFBase || DataTypesBase) && ts->bkgname) load_backpic(ts,ts->bkgname); /* Handle other terms */ for ( i = 1; i < MAX_TERM_DATA; i++ ) { /* Term pointer */ tt = &data[ i ]; /* Skip this term if iconified */ if (!tt->use) continue; if ((IFFBase || DataTypesBase) && tt->bkgname) load_backpic(tt,tt->bkgname); if (tt->iconified) continue; if (KICK13) FAIL("Extra term windows not supported under 1.3"); /* Load background picture, if possible */ open_term(i,FALSE); } for ( i = MAX_TERM_DATA - 1; i >= 0 ; i--) { if ( data[ i ].use ) link_term( i ); } /* Bring main window to front */ if ( !backdrop ) WindowToFront( ts->win ); /* Bring screen to front */ ScreenToFront( use_pub ? pubscr : amiscr ); amiga_clear(); /* Load and convert graphics */ if (use_graphics == GRAPHICS_ORIGINAL) { MSG( 0, 0, "Loading graphics" ); if ( !load_gfx() ) FAIL( NULL ); /* Scale the graphics to fit font sizes. AGA /may/ run out of memory here, but we'll ignore it. */ if (use_graphics == GRAPHICS_ORIGINAL) size_gfx( &data[ 0 ] ); MSG( 0, 1, "Remapping graphics" ); if ( !conv_gfx() ) FAIL( "Not enough memory to remap graphics." ); } /* Load sound effects */ if ( use_sound ) { MSG( 0, 2, "Loading sound effects" ); sound_name_desc = malloc(8000); /* Ugh :( */ init_sound(); } /* Save the ami hooks into the overhead map */ set_callback((callback_type) ami_map_info, CALL_MAP_INFO, NULL); /* Save old player movement hook */ set_callback((callback_type) ami_player_move, CALL_PLAYER_MOVE, NULL); /* Success */ return ( 0 ); } static int load_backpic(term_data *t, char *name) { IFFL_HANDLE iff; long pens[64]; struct IFFL_BMHD *bmhd; ULONG cols; struct BitMap *bkg; UBYTE *colour_table; if (DataTypesBase) { Object *o; ULONG *cregs = NULL; struct dtFrameBox dtf = {NULL}; struct FrameInfo fri = {NULL}; struct gpLayout gpl; if (o = NewDTObject (name, DTA_SourceType, DTST_FILE, DTA_GroupID, GID_PICTURE, PDTA_Remap, FALSE, TAG_DONE)) { dtf.MethodID = DTM_FRAMEBOX; dtf.dtf_FrameInfo = &fri; dtf.dtf_ContentsInfo = &fri; dtf.dtf_SizeFrameInfo = sizeof (struct FrameInfo); if (DoMethodA (o, (Msg)&dtf) && fri.fri_Dimensions.Depth) { int z = 0; gpl.MethodID = DTM_PROCLAYOUT; gpl.gpl_GInfo = NULL; gpl.gpl_Initial = 1; if (DoMethodA (o, (Msg)&gpl)) { /* Get the object information */ GetDTAttrs (o, PDTA_CRegs, &cregs, PDTA_NumColors, &cols, PDTA_BitMap, &bkg, TAG_DONE); t->backw = fri.fri_Dimensions.Width; t->backh = fri.fri_Dimensions.Height; /* Calculate how many colours we need */ while (z < cols) { pens[z] = alloc_pen( cregs[z * 3] >> 24, cregs[z * 3 + 1] >> 24, cregs[z * 3 + 2] >> 24); z++; } t->background = alloc_bitmap( fri.fri_Dimensions.Width, fri.fri_Dimensions.Height, screen_depth, BMF_STANDARD, NULL ); remap_bitmap( bkg, t->background, pens, fri.fri_Dimensions.Width, fri.fri_Dimensions.Height); DisposeDTObject(o); } } } return 1; } if (!IFFBase) return 0; if (iff = IFFL_OpenIFF(name, IFFL_MODE_READ) ) { if (colour_table = IFFL_FindChunk(iff, ID_CMAP) ) { ULONG *l = (ULONG *)(colour_table + 4); int r,g,b; int z = 0; /* Calculate how many colours we need */ cols = *l / 3; colour_table += 8; while (z < cols) { /* Calculate colour... */ r = *colour_table++; g = *colour_table ++ ; b = *colour_table++; pens[z] = alloc_pen( r, g, b); z++; } } if (bmhd = IFFL_GetBMHD( iff ) ) { struct BitMap *bkg; bkg = alloc_bitmap( bmhd->w, bmhd->h, screen_depth, 0, NULL ); t->background = alloc_bitmap( bmhd->w, bmhd->h, bmhd->nPlanes, BMF_STANDARD, NULL ); if (!t->background || !bkg) { puts("Not enough memory for bitmaps!"); if (t->background) { free_bitmap(t->background); t->background = NULL; } if (bkg) free_bitmap(bkg); IFFL_CloseIFF( iff ); return 1; } IFFL_DecodePic(iff, t->background); remap_bitmap(t->background, bkg, pens, bmhd->w, bmhd->h); free_bitmap( t->background ); t->background = bkg; t->backw = bmhd->w; t->backh = bmhd->h; } IFFL_CloseIFF( iff ); return 0; } else { printf("Can't open file %s\n",name); return 1; } } static void free_pen(int pen) { if (use_pub && obtain_mask[pen]) { while (obtain_mask[pen]) { ReleasePen( pubscr->ViewPort.ColorMap, pen ); obtain_mask[pen]--; } } else custpens[pen] = 0; } static int abandon_pen(void) { int i; int sc; if (screen_cols > 512) sc = 512; else sc = screen_cols; if (use_pub) { int val = obtain_mask[sc - 1]; int pval = 0; /* Find lowest value, and release first */ for (i = sc - 1; i >= 0 ; i--) { if (obtain_mask[i] < val) { val = obtain_mask[i]; pval = i; } } return pval; } else { int val = custpens[sc - 1]; int pval = 0; for (i = sc - 1; i >= 0 ; i--) { if (custpens[i] < val) { val = custpens[i]; pval = i; } } return pval; } } /* r, g, b in range 0..0xFF */ static int alloc_pen(ULONG r, ULONG g, ULONG b) { ULONG mr, mg, mb; ULONG d, maxd; int pen, i; int sc; if (screen_cols > 512) sc = 512; else sc = screen_cols; if (use_pub) { pen = ObtainBestPen( pubscr->ViewPort.ColorMap, (r << 24) | 0xFFFFFF, (g << 24) | 0xFFFFFF, (b << 24) | 0xFFFFFF, OBP_Precision, PRECISION_EXACT ); if (pen != -1) obtain_mask[pen]++; return pen; } else { /* If we can allocate a new pen, do so */ for (i = 0 ; i < sc ; i++) { if (!custpens[i]) { if (KICK30) { SetRGB32( &amiscr->ViewPort, i, (r << 24) | 0xFFFFFF, (g << 24) | 0xFFFFFF, (b << 24) | 0xFFFFFF); } else { SetRGB4( &amiscr->ViewPort, i, r >> 4, g >> 4, b >> 4); } custpalette[i * 3] = r; custpalette[i * 3 + 1] = g; custpalette[i * 3 + 2] = b; custpens[i]++; return i; } } /* No free pens, so search for pen with a near colour */ for (i = pen = 0 ; i < sc ; i++) { mr = (custpalette[ i * 3 ] - r); mg = (custpalette[ i * 3 + 1] - g); mb = (custpalette[ i * 3 + 2] - b); d = mr*mr + mg*mg + mb*mb; if (!i) maxd = d; else { if (d < maxd) { pen = i; maxd = d; } } } return pen; } } /* Setup palette for text pens */ static void init_default_palette(void) { int i; /* Initialize colour palette */ for ( i = 0; i < 16; i++ ) { /* If undefined, use default palette */ if ( angband_color_table[ i ][ 0 ] == 0 ) { angband_color_table[ i ][ 0 ] = 1; angband_color_table[ i ][ 1 ] = ( default_colours[ i ] & 0xff0000 ) >> 16; angband_color_table[ i ][ 2 ] = ( default_colours[ i ] & 0x00ff00 ) >> 8; angband_color_table[ i ][ 3 ] = ( default_colours[ i ] & 0x0000ff ); } } } /* TRUE if ok */ static BOOL get_screenmode( char *modestr ) { scr_m = strtol( modestr, NULL, 0 ); /* It was not a number, so treat it as a public screen name */ if ( !scr_m) { /* We need kickstart 3.0+ to use a public screen */ if ( !(KICK20) ) { puts( "Public screen can only be used on Kickstart 2.0 or later." ); return FALSE; } /* Try to lock the named public screen if it isn't already */ if ( !pubscr ) pubscr = LockPubScreen( modestr ); /* Failed? */ if ( !pubscr ) { printf( "Unable to get a lock on screen '%s'\n", modestr ); return FALSE; } /* We got a lock now */ publock = TRUE; scr_m = -1; use_pub = TRUE; /* Don't blank mouse on public screen */ blankmouse = FALSE; } /* Use specified screenmode if available */ else { /* Check if requested mode is available */ if ( ModeNotAvailable( scr_m )) scr_m = 0; } return TRUE; } /* -------------------------------------------------------------------- */ /* amiga_open_libs( void ) */ /* */ /* Opens Amiga libraries; including Intuition, Graphics etc. */ /* -------------------------------------------------------------------- */ void amiga_open_libs( void ) { IntuitionBase = (struct IntuitionBase *)OpenLibrary( "intuition.library", 0L); DiskfontBase = OpenLibrary( "diskfont.library", 0L); GfxBase = (struct GfxBase *)OpenLibrary( "graphics.library", 0L); IFFBase = OpenLibrary("iff.library", 0L); DataTypesBase = OpenLibrary("datatypes.library", 0L); if (!DiskfontBase) amiga_fail( "Sorry, this program needs diskfont.library" ); /* Decide which version of the system we have. Ought to use version.library */ kick_ver = IntuitionBase->LibNode.lib_Version; AslBase = OpenLibrary( "asl.library", 36L); GadToolsBase = OpenLibrary( "gadtools.library", 36L); /* Initialise console (only using RawKeyConvert) */ ConsoleDev = (struct Device *)OpenDevice("console.device",CONU_LIBRARY,(struct IORequest *)&io_req,0L); ConsoleDevice = (struct Device *)io_req.io_Device; CyberGfxBase = (struct Library *)OpenLibrary( "cybergraphics.library", 41L); /* Is this evil?? */ if (CyberGfxBase) use_cyber = TRUE; ReqToolsBase = (struct ReqToolsBase *)OpenLibrary( "reqtools.library",0L); } /* Open a term window! (n = window number) */ void open_term( int n, bool doall ) { term_data *tt = &data[ n ]; int i; int notitle; /* Skip this term if not in use */ if ( doall && !tt->use ) return; /* If already open, don't reopen */ if ( tt->win ) return; /* Initialise vertical prop gadget */ if (tt->scroll) { i = tt->rows; if (i > MAX_TERM_VERT) i = MAX_TERM_VERT; memset(&tt->ygadinfo,0,sizeof(struct PropInfo)); memset(&tt->ygad,0,sizeof(struct Gadget)); memset(&tt->ygadimage,0,sizeof(struct Image)); tt->ygadinfo.Flags = AUTOKNOB | FREEVERT | PROPNEWLOOK; tt->ygadinfo.HorizPot = tt->ygadinfo.VertPot = 0; tt->ygadinfo.HorizBody = MAXBODY; tt->ygadinfo.VertBody = MAXBODY / (MAX_TERM_VERT / i); tt->xgadinfo.Flags = AUTOKNOB | FREEVERT | PROPNEWLOOK; tt->xgadinfo.HorizPot = tt->xgadinfo.VertPot = 0; tt->xgadinfo.HorizBody = MAXBODY / (MAX_TERM_HORIZ / i); tt->xgadinfo.VertBody = MAXBODY; tt->ygad.LeftEdge = -14; tt->ygad.TopEdge = amiscr->WBorTop + amiscr->Font->ta_YSize + 2; tt->ygad.Width = 12; tt->ygad.Height = -tt->ygad.TopEdge - 11; tt->xgad.LeftEdge = -3; tt->xgad.TopEdge = -7; tt->xgad.Width = -23; tt->xgad.Height = 6; tt->ygad.Flags = GFLG_RELRIGHT | GFLG_RELHEIGHT; tt->ygad.Activation = GACT_RELVERIFY | GACT_IMMEDIATE | GACT_RIGHTBORDER; tt->ygad.GadgetType = GTYP_PROPGADGET | GTYP_GZZGADGET; tt->ygad.GadgetRender = (APTR)&(tt->ygadimage); tt->ygad.SpecialInfo = (APTR)&(tt->ygadinfo); tt->ygad.GadgetID = 0; tt->ygad.NextGadget = NULL; tt->xgad.Flags = GFLG_RELBOTTOM | GFLG_RELWIDTH; tt->xgad.Activation = GACT_RELVERIFY | GACT_IMMEDIATE | GACT_BOTTOMBORDER; tt->xgad.GadgetType = GTYP_PROPGADGET | GTYP_GZZGADGET; tt->xgad.GadgetRender = (APTR)&(tt->xgadimage); tt->xgad.SpecialInfo = (APTR)&(tt->xgadinfo); tt->xgad.GadgetID = 1; tt->xgad.NextGadget = NULL; } notitle = tt->notitle; if (tt->backdrop) notitle = TRUE; if (( tt->win = OpenWindowTags( NULL, WA_Left, tt->wx, WA_Top, tt->wy, WA_Width, tt->ww, WA_Height, tt->wh, WA_MinWidth,-1, WA_MaxWidth,-1, WA_MinHeight,-1, WA_MaxHeight,-1, WA_DetailPen,4, WA_BlockPen,2, /* makes no difference, as newlook specified */ use_pub ? WA_PubScreen : WA_CustomScreen, use_pub ? pubscr : amiscr, WA_GimmeZeroZero, TRUE, WA_DragBar, !tt->backdrop, WA_Borderless, tt->backdrop, WA_SizeGadget, !tt->backdrop, WA_CloseGadget, !tt->backdrop, WA_DepthGadget, !tt->backdrop, WA_NewLookMenus, TRUE, WA_ScreenTitle, VARIANT, WA_IDCMP, IDCMP_NEWSIZE | IDCMP_CLOSEWINDOW | IDCMP_GADGETUP | IDCMP_GADGETDOWN | IDCMP_MENUPICK | IDCMP_MENUVERIFY, notitle ? TAG_IGNORE : WA_Title, tt->name, WA_ReportMouse, TRUE, tt->scroll ? WA_Gadgets : TAG_IGNORE, tt->scroll ? &(tt->ygad) : NULL, TAG_END )) == NULL ) { amiga_fail("Unable to open term window."); } /* Initialize rastport */ tt->rp = tt->wrp = tt->win->RPort; SetRast( tt->rp, PEN( 0 )); SetAPen( tt->rp, 1 ); SetBPen( tt->rp, 0 ); SetDrMd( tt->rp, JAM2 ); SetFont( tt->rp, tt->font ); if (tt->background) BltBitMapRastPort( tt->background, 0, 0, tt->rp, 0, 0, tt->ww, tt->wh, 0xC0); tt->menu = CreateMenus( window_menu, GTMN_FrontPen, (long)penconv[ TERM_WHITE ], NULL ); if ( tt->menu ) { /* Layout menus */ if ( LayoutMenus( tt->menu, visinfo, KICK30 ? GTMN_NewLookMenus : TAG_IGNORE, TRUE, TAG_END )) SetMenuStrip( tt->win , tt->menu ); else { FreeMenus( tt->menu ); tt->menu = NULL; } } calc_sigmask(); if (doall) { /* Term is no longer iconified */ tt->iconified = FALSE; /* Refresh term */ Term_activate( angband_term[ n ] ); Term_redraw(); Term_activate( angband_term[ 0 ] ); } } /* Close a window (n = term number) */ void close_term( int n ) { term_data *tt = &data[ n ]; /* Skip this term if not in use, or already closed */ if ( !tt->use || tt->iconified) return; amiga_flush(1); /* Remove menustrip */ if (tt->menu && tt->win) ClearMenuStrip( tt->win ); /* Close window */ if ( tt->win ) CloseWindow( tt->win ); tt->menu = NULL; tt->win = NULL; tt->wrp = tt->rp = NULL; /* Recalculate signal mask, as window has now disappeared */ calc_sigmask(); /* Term is now iconified */ tt->iconified = TRUE; } /* Prepare a term */ static void init_term( term_data *td ) { td->name = "term_unknown"; td->use = FALSE; td->iconified = FALSE; td->scroll = FALSE; td->autoscale = FALSE; td->backdrop = FALSE; /* Term size */ td->xpos = td->ypos = 0; td->cols = 80; td->rows = 24; td->fix_w = td->fix_h = 0; /* Term dimension */ td->wx = 0; td->wy = 0; td->ww = 0; td->wh = 0; /* System default font */ td->font = GfxBase->DefaultFont; td->ownf = FALSE; td->fw = td->font->tf_XSize; td->fh = td->font->tf_YSize; td->fb = td->font->tf_Baseline; /* Background bitmap data and path of IFF picture */ td->background = NULL; td->bkgname = NULL; /* No window or rastports */ td->win = NULL; td->wrp = NULL; td->rp = NULL; /* Cursor status */ td->cursor_xpos = 0; td->cursor_ypos = 0; td->cursor_visible = FALSE; td->cursor_lit = FALSE; td->cursor_frame = 0; td->cursor_map = FALSE; /* Use window title */ td->notitle = FALSE; } static void link_term( int i ) { term_data *td = &data[ i ]; term *t; /* Term pointer */ t = &td->t; /* Initialize the term */ term_init( t, td->cols, td->rows, 256 ); /* Hooks */ t->init_hook = amiga_open; t->nuke_hook = amiga_nuke; t->text_hook = amiga_text; t->pict_hook = amiga_pict; t->wipe_hook = amiga_wipe; t->curs_hook = amiga_curs; t->xtra_hook = amiga_xtra; t->user_hook = amiga_user; /* We are emulating a hardware cursor */ t->soft_cursor = FALSE; /* Draw graphical tiles one by one */ t->higher_pict = TRUE; /* Misc. efficiency flags */ t->never_bored = TRUE; t->never_frosh = TRUE; /* Erase with "white space" */ t->attr_blank = TERM_WHITE; t->char_blank = ' '; /* Remember where we come from */ t->data = (vptr) td; /* Activate it */ Term_activate( t ); /* Global pointer */ angband_term[ i ] = t; } static void free_term( term_data *td ) { /* Remove menus, if any */ if ( td->menu ) { ClearMenuStrip( td->win ); FreeMenus( td->menu ); } if ( td->background ) { free_bitmap( td->background ); td->background = NULL; } /* Do we own the font? */ if ( td->ownf && td->font ) { CloseFont( td->font ); td->font = NULL; } if ( td->win ) { CloseWindow( td->win ); td->win = NULL; } } static BOOL strreq( char *s, char *d) { return((BOOL)!stricmp(s,d)); } /* Get font name from user, return in str as 'topaz.font/8' or '' if cancel */ static void request_font( char *str ) { struct FontRequester *req = NULL; /* Blank string as default */ *str = 0; /* Open ASL screenmode requester, if possible */ if (AslBase) { if ( req = AllocAslRequestTags( ASL_FontRequest, TAG_DONE )) { if (amiscr || pubscr) { if ( AslRequestTags( req, ASLFO_FixedWidthOnly, TRUE, ASLFO_Screen, (use_pub ? pubscr : amiscr), TAG_DONE, TAG_DONE )) strnfmt( str, 128, "%s/%d", req->fo_Attr.ta_Name, req->fo_Attr.ta_YSize ); } else if ( AslRequestTags( req, ASLFO_FixedWidthOnly, TRUE, TAG_DONE, TAG_DONE )) strnfmt( str, 128, "%s/%d", req->fo_Attr.ta_Name, req->fo_Attr.ta_YSize ); FreeAslRequest( req ); } } } static void request_mode( char *str ) { struct ScreenModeRequester *req; /* Blank string as default */ *str = 0; if (AslBase) { /* Allocate screenmode requester */ if ( req = AllocAslRequestTags( ASL_ScreenModeRequest, TAG_DONE )) { /* Open screenmode requester */ if( AslRequestTags( req, TAG_DONE )) { /* Store font name and size */ strnfmt( str, 200, "0x%X", req->sm_DisplayID ); } /* Free requester */ FreeAslRequest( req ); } } } /* Build menus */ int read_menus( void ) { static char fname[ MAX_PATH_LENGTH ]; static char line[ 512 ]; static char buf[ 512 ]; struct NewMenu nm; int mn = 0; FILE *file; char *s; path_make(fname, ANGBAND_DIR_XTRA, "cfg/menu.cfg"); file = fopen( fname , "r" ); if (!file) { printf("\nUnable to open menu file 'xtra/cfg/menu.cfg'\n"); return( FALSE ); } while ( fgets( line, 511, file )) { s = line; line[strlen(line) - 1] = 0; if (*s == '#' || *s == ';') continue; s = token_line(s, buf); if (buf[0] == 'T') { s = token_line(s, buf); nm.nm_Type = NM_TITLE; nm.nm_Label = strdup(buf); nm.nm_CommKey = 0; nm.nm_Flags = 0; nm.nm_MutualExclude = 0; nm.nm_UserData = 0; memmove(&menu_ptr[mn++], &nm, sizeof(struct NewMenu)); } else if (buf[0] == 'I') { char keycode[2]; bool ctrl_key = FALSE; bool help_key = FALSE; bool ramiga_key = FALSE; int internal = 0; keycode[1] = 0; s = token_line(s, buf); nm.nm_Type = NM_ITEM; nm.nm_Label = strdup(buf); /* Parse key code */ while (*s) { s = token_line(s, buf); if (!stricmp(buf, "dungeon_map")) internal = MNU_SCALEDMAP; else if (!stricmp(buf, "palette_requester")) internal = MNU_PALETTE; else if (!stricmp(buf, "load_palette")) internal = MNU_LOAD_PALETTE; else if (!stricmp(buf, "graphics_off")) internal = MNU_GRAPHICS_OFF; else if (!stricmp(buf, "graphics_8x8")) internal = MNU_GRAPHICS_8; else if (!stricmp(buf, "graphics_16x16")) internal = MNU_GRAPHICS_16; else if (!stricmp(buf, "save_palette")) internal = MNU_SAVE_PALETTE; else if (!stricmp(buf, "export_hs")) internal = MNU_EXPORT_HS; else if (!stricmp(buf, "CTRL")) ctrl_key = TRUE; else if (!stricmp(buf, "HELP")) help_key = TRUE; else if (!stricmp(buf, "RAMIGA")) ramiga_key = TRUE; else keycode[0] = buf[0]; } nm.nm_Flags = 0; nm.nm_MutualExclude = 0; nm.nm_UserData = NULL; if (ramiga_key) nm.nm_CommKey = strdup(keycode); if (ctrl_key) nm.nm_UserData = (APTR) (MNU_CKEYCOM + keycode[0]); else if (help_key) nm.nm_UserData = (APTR) (MNU_HELP + keycode[0]); else nm.nm_UserData = (APTR) (MNU_KEYCOM + keycode[0]); if (internal) nm.nm_UserData = (APTR) internal; memmove(&menu_ptr[mn++], &nm, sizeof(struct NewMenu)); } else if (buf[0] == 'B') { nm.nm_Type = NM_ITEM; nm.nm_Label = NM_BARLABEL; nm.nm_CommKey = 0; nm.nm_Flags = 0; nm.nm_MutualExclude = 0; nm.nm_UserData = 0; memmove(&menu_ptr[mn++], &nm, sizeof(struct NewMenu)); } } nm.nm_Type = NM_END; nm.nm_Label = NULL; nm.nm_CommKey = 0; nm.nm_Flags = 0; nm.nm_MutualExclude = 0; nm.nm_UserData = 0; memmove(&menu_ptr[mn], &nm, sizeof(struct NewMenu)); fclose(file); } static char *token_line(char *s, char *p) { while (*s == 9 || *s == 32) s++; if (*s == '\"') { do *p++ = *++s; while (*s && *s != '\"'); *(p - 1) = 0; s++; return s; } while (*s && *s != 32 && *s != 9) *p++ = *s++; *p = 0; return s; } /* Parse `settings.prf` file, if found. */ int read_prefs( void ) { static char errorstr[] = "PREFS:Unrecognised option"; FILE *file; static char line[ 256 ]; static char fname[ MAX_PATH_LENGTH ]; char fontname[ 256 ]; char custom_str[200]; char public_str[200]; char *first,*type,*param; int i,k; term_data *td; enum { S_CUSTOM, S_PUBLIC, S_NONE }; int force_mode = S_NONE; public_str[0] = custom_str[0] = 0; path_make( fname , ANGBAND_DIR_USER , WPRF); file = fopen( fname , "r" ); if (!file) { printf("\nUnable to open file '%s'.\n", fname ); return( FALSE ); } /* Read next line from file */ while ( fgets( line, 256, file )) { for (i = 0; line[i] && line[i] <= 32 ; i++) ; if (line[i] == '#' || line[i] == ';' || !line[i]) continue; k = i; while (line[i] && line[i] != '.') i++; if (!line[i]) { printf("PREFS:Error in line '%s' in settings.prf\n",line); continue; } line[i++] = 0; first = (char *)(line + k); while (line[i] == 32 || line[i] == 9) i++; type = (char *)(line + i); while (line[i] > 32) i++; line[i++] = 0; param = (char *)(line + i); while (line[i] == 32 || line[i] == 9) i++; while (line[i] > 32) i++; line[i] = 0; if (strreq(first,"ANGMAN")) continue; if (strreq(first,"ANGBAND")) { if (strreq(type,"gfx")) process_gfx(param); else if (strreq(type,"sound")) use_sound = process_bool(param); else if (strreq(type,"version")) ; else if (strreq(type,"backup")) ; else printf("%s %s.%s\n",errorstr,first,type); continue; } if (strreq(first,"SCREEN")) { if (strreq(type,"menus")) use_menus = process_bool(param); else if (strreq(type,"blankmouse")) blankmouse = process_bool(param); else if (strreq(type,"quick")) { /* Only allow quick graphics on non-RTG systems! */ use_aga = process_bool(param); } else if (strreq(type,"aga")) { /* Only allow quick graphics on non-RTG systems! */ use_aga = process_bool(param); } else if (strreq(type,"use")) { if (param[0] == 'p' || param[0] == 'P') force_mode = S_PUBLIC; else force_mode = S_CUSTOM; } else if (strreq(type,"width")) screen_width = atoi(param); else if (strreq(type,"height")) screen_height = atoi(param); else if (strreq(type,"depth")) { screen_depth = atoi(param); if (screen_depth > 8) screen_depth = 8; screen_cols = 1 << screen_depth; } else if (strreq(type,"overscan")) screen_overscan = atoi(param); else if (strreq(type,"rtg")) { } else if (strreq(type,"name")) { if ( KICK13 ) { puts("Sorry - you need KS2.0 or better for SCREEN.name\n"); continue; } strcpy(public_str, param); } else if (strreq(type,"mode")) { if ( KICK13 ) { puts("Sorry - you need KS2.0 or better for SCREEN.mode\n"); continue; } strcpy( custom_str,param ); if (modestr[0] == '?') request_mode( custom_str ); } else printf("%s %s.%s\n",errorstr,first,type); continue; } k = -1; for (i = 0 ; term_short[i] ; i++) { if (strreq( first, term_short[i] )) { k = i & 7; break; } } if (k != -1) td = &data[ k ]; else { /* printf( "PREFS: Error in line '%s'\n", line ); */ continue; } /* Option 'use' - Use this term */ if ( strreq( type, "use" )) td->use = process_bool( param ); else if ( strreq( type, "scroll" )) td->scroll = process_bool( param ); /* Option 'show' - Don't iconify */ else if ( strreq( type, "show" )) td->iconified = !process_bool( param ); /* 'noborders' - set to TRUE for no window borders */ else if ( strreq( type, "noborders")) td->backdrop = process_bool( param ); /* Option 'cols' - Set number of columns for term */ else if ( strreq( type, "cols" )) td->cols = atoi(param); /* Option 'rows' - Set number of rows for term */ else if ( strreq( type, "rows" )) td->rows = atoi(param); /* Option 'xpos' - Set horizontal position for window in pixels */ else if ( strreq( type, "xpos")) td->wx = atoi(param); /* Option 'ypos' - Set vertical position for window in pixels */ else if ( strreq( type, "ypos" )) td->wy = atoi(param); else if ( strreq( type, "background" )) { td->bkgname = strdup( param ); nasty_optimise_gfx = FALSE; } else if ( strreq( type, "fix" )) { td->fix_h = td->fix_w = atoi(param); } /* Option 'name' - Set window title */ else if ( strreq( type, "title" )) { if (param) td->name = strdup( param ); else td->notitle = TRUE; } /* Option 'font' - Set font for this window */ else if ( strreq( type, "font" )) { char *s; /* Get value */ strcpy(td->fontname,param); if (param[0] == '?') request_font(fontname); else strcpy(fontname,param); s = handle_font(td, fontname); if (s) puts(s); } /* Unknown option */ else { /* Output error message */ printf ( "\nPREFS: Unknown option '%s'.%s\n", first,type ); } } if ((custom_str[0] && !public_str[0]) || (force_mode == S_CUSTOM) ) strcpy(modestr,custom_str); else if ((public_str[0] && !custom_str[0]) || (force_mode == S_PUBLIC) ) { strcpy(modestr,public_str); } else modestr[0] = 0; fclose( file ); } static char *handle_font(struct term_data *td, char *fontname) { static char error[128]; struct TextAttr attr; char *s; int fsize; /* No font specification given, so use system font, or inherit 'main' font */ if ( !fontname[0] ) { if ( td == &data[ 0 ] ) { td->font = GfxBase->DefaultFont; /* Use default font as screen font */ scrattr = NULL; } else td->font = data[ 0 ].font; /* Set font dimensions */ td->fw = td->font->tf_XSize; td->fh = td->font->tf_YSize; td->fb = td->font->tf_Baseline; /* This font is not opened by us */ td->ownf = FALSE; return NULL; } else { /* Find font name/size delimiter */ if (( s = strchr( fontname, '/' )) == NULL ) { strnfmt(error, 128, "PREFS: Illegal font specification: '%s'.\n", fontname ); return error; } /* Now get size of font */ *s++ = 0; /* Check for autoscaling */ if (*s == '?') { strcpy(td->fontname, fontname); td->autoscale = TRUE; return NULL; } else fsize = atoi( s ); /* Make sure the font name ends with .font */ if ( !strstr( fontname, ".font" )) strcat( fontname, ".font" ); /* Set font attributes */ attr.ta_Name = fontname; attr.ta_YSize = fsize; attr.ta_Style = FS_NORMAL; attr.ta_Flags = (streq( fontname, "topaz.font" ) && ( fsize == 8 || fsize == 9 )) ? FPF_ROMFONT : FPF_DISKFONT; /* Open font from disk */ if ( td->font = OpenDiskFont( &attr )) { /* We own this font */ td->ownf = TRUE; /* Set font dimensions */ td->fw = td->font->tf_XSize; td->fh = td->font->tf_YSize; td->fb = td->font->tf_Baseline; /* printf("Font x %d y %d b %d\n",td->fw, td->fh, td->fb); */ /* Copy font attr to screen font */ if ( td == &data[ 0 ] ) { scrattr->ta_Name = strdup( fontname ); scrattr->ta_YSize = fsize; scrattr->ta_Style = FS_NORMAL; scrattr->ta_Flags = attr.ta_Flags; } return NULL; } else { /* Couldn't open, so use default font instead */ td->font = GfxBase->DefaultFont; /* Use default font as screen font */ scrattr = NULL; /* Output error message */ strnfmt(error, 128, "PREFS:Unable to open font '%s/%d'.\n", fontname, fsize ); return error; } } return NULL; } static BOOL process_bool(char *param) { if (*param == 'Y' || *param == 'y' || *param == '1' || *param == 'T' || *param == 't') return TRUE; else return FALSE; } static void process_gfx(char *param) { use_graphics = GRAPHICS_NONE; if (*param == 'Y' || *param == 'y' || *param == '1' || *param == 'T' || *param == 't') { use_graphics = GRAPHICS_ORIGINAL; arg_graphics = use_graphics; } if (*param == 'E' || *param == 'e') { use_graphics = GRAPHICS_ORIGINAL; arg_graphics = use_graphics; screen_enhanced = TRUE; } } static errr amiga_user( int n ) { return( 1 ); } static void amiga_nuke( term *t ) { term_data *td = (term_data *)( t->data ); if ( td == &data[ 0 ] ) { amiga_fail( NULL ); /* Flush the output */ fflush( stdout ); } } static void amiga_open( term *t ) { /* Nothing to do here */ } static errr amiga_curs( int x, int y ) { term_data *td = term_curs; if ( td->cursor_visible ) cursor_off( td ); td->cursor_xpos = x; td->cursor_ypos = y; td->cursor_frame = 0; if ( td->cursor_visible ) cursor_on( td ); return ( 0 ); } static errr amiga_wipe( int x, int y, int n ) { term_data *td = (term_data *)(Term->data); y -= td->ypos; x -= td->xpos; if ( y >= td->rows ) return( 0 ); if ( x >= td->cols ) return( 0 ); if ( (n > 0 ) && !td->iconified ) { /* Erase rectangular area on screen */ if (!td->background) { SetAPen( td->rp, PEN( 0 ) ); RectFill( td->rp, x * td->fw, y * td->fh, ( x + n ) * td->fw - 1, ( y + 1 ) * td->fh - 1 ); } else BltBitMapRastPort( td->background, (x * td->fw) % td->backw, (y * td->fh) % td->backh, td->rp, x * td->fw, y * td->fh, n * td->fw, td->fh, 0xC0); } return ( 0 ); } static errr amiga_clear( void ) { term_data *td = (term_data*)(Term->data); if (td->iconified) return 1; /* Fill window with background colour, or background XXX XXX XXX Tile background */ if ( !td->background ) SetRast( td->rp, PEN( 0 )); else { if (use_pub) BltBitMapRastPort( td->background, 0, 0, td->rp, 0, 0, td->ww, td->wh, 0xC0); else { int x, y; int mx, my; int sw = td->ww; int sh = td->wh; struct RastPort *rp = td->rp; if (td == &data[0]) { sw = screen_width; sh = screen_height; rp = &amiscr->RastPort; } for (y = 0 ; y < sh ; y += td->backh) { for (x = 0 ; x < sw ; x += td->backw) { mx = min(td->backw, sw - x); my = min(td->backh, sh - y); BltBitMapRastPort( td->background, x % td->backw, y % td->backh, rp, x, y, mx, my, 0xC0); } } } } return ( 0 ); } static errr amiga_pict( int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp ) { term_data *td = (term_data *)(Term->data); int i; byte a; char c; if ( td->iconified ) return ( 0 ); y -= td->ypos; x -= td->xpos; if ( y >= td->rows ) return( 0 ); if ( x >= td->cols ) return( 0 ); for ( i = 0; i < n; i++ ) { a = ap[i]; c = cp[i]; if (screen_enhanced) { put_gfx( td->rp, x, y, tcp[i], tap[i] ); use_mask = 1; } put_gfx( td->rp, x, y, c, a ); use_mask = 0; x++; } return ( 0 ); } static errr amiga_text( int x, int y, int n, byte a, cptr s ) { term_data *td = (term_data*)(Term->data); if (td->iconified) return( 1 ); y -= td->ypos; x -= td->xpos; if ( y >= td->rows ) return( 0 ); if ( x >= td->cols ) return( 0 ); if ( x >= 0 && y >= 0 && n > 0 ) { if (td->background) { BltBitMapRastPort( td->background, (x * td->fw) % td->backw, (y * td->fh) % td->backh, td->rp, x * td->fw, y * td->fh, n * td->fw, td->fh, 0xC0); if (KICK30) SetABPenDrMd( td->rp, PEN( a & 0x0F ), PEN( 0 ), JAM1); else { SetDrMd( td->rp, JAM1 ); SetAPen( td->rp, PEN( a & 0x0f )); SetBPen( td->rp, PEN( 0 )); } Move( td->rp, x * td->fw, y * td->fh + td->fb ); Text( td->rp, (char *) s, n ); } else { /* if (use_aga) */ /* quick_Text( td->rp, PEN( a & 0xF ), (char *) s, n, x * td->fw, y * td->fh + td->fb ); */ /* else */ /* { */ if (KICK30) SetABPenDrMd( td->rp, PEN( a & 0x0F ), PEN( 0 ), JAM2); else { SetDrMd( td->rp, JAM2 ); SetAPen( td->rp, PEN( a & 0x0f )); SetBPen( td->rp, PEN( 0 )); } Move( td->rp, x * td->fw, y * td->fh + td->fb ); Text( td->rp, (char *) s, n ); /* } */ } } return ( 0 ); } /* Various 'xtra' events */ static errr amiga_xtra( int n, int v ) { term_data *td = (term_data *)(Term->data); long mytime; unsigned int clock[2], clock2[2]; /* Analyze the request */ switch ( n ) { /* Wait for event */ case TERM_XTRA_EVENT: return ( amiga_event( v )); /* Flush input */ case TERM_XTRA_FLUSH: return ( amiga_flush( v )); /* Change cursor visibility */ case TERM_XTRA_SHAPE: /* Cursor on */ if ( v ) { cursor_on( td ); td->cursor_visible = TRUE; } /* Cursor off */ else { cursor_off( td ); td->cursor_visible = FALSE; } return ( 0 ); /* Flash screen */ case TERM_XTRA_NOISE: DisplayBeep( use_pub ? pubscr : amiscr ); return ( 0 ); /* Play a sound */ case TERM_XTRA_SOUND: if ( has_sound ) play_sound( v ); return ( 0 ); /* React on global changes */ case TERM_XTRA_REACT: return ( amiga_react( v )); case TERM_XTRA_LEVEL: term_curs = td; return( 0 ); case TERM_XTRA_DELAY: if (v <= 0) return (0); v *= 1000; /* SAS/C code. I have GNU code for this, but not included it yet */ timer(clock); do { timer(clock2); mytime = (clock2[0] - clock[0]) * 1000*1000 + (clock2[1] - clock[1]); if (clock2[0] < clock[0]) break; } while (mytime < v); return (0); /* Unknown request type */ default: return ( 1 ); } /* Can't get here */ return ( 1 ); } static errr amiga_flush( int v ) { struct IntuiMessage *imsg; /* Ignore all messages at the port XXX */ while ( imsg = (struct IntuiMessage *) GetMsg( data[ 0 ].win->UserPort )) ReplyMsg(( struct Message *) imsg ); return ( 1 ); } static void process_msg(int i,ULONG iclass, UWORD icode, UWORD iqual, APTR iaddr) { struct Window *win; struct IntuiMessage *imsg; int nw,nh; double ud,tmpa,tmpb; term *old_term = Term; switch( iclass ) { case IDCMP_GADGETUP: if (((struct Gadget *)iaddr)->GadgetID == 0) { /* maxbody * nh ) / max_term_vert */ /* vb * max term / maxbody */ ud = (double)data[ i ].ygadinfo.VertPot / (double)0xFFFF; ud *= (MAX_TERM_VERT - data[ i ].rows); tmpa = modf(ud,&tmpb); if (tmpa >= 0.5) ud = ceil(ud); else ud = floor(ud); data[ i ].ypos = ud; Term_activate(angband_term[i]); Term_redraw(); Term_fresh(); Term_activate(old_term); } break; case IDCMP_INACTIVEWINDOW: case IDCMP_ACTIVEWINDOW: break; case IDCMP_CLOSEWINDOW: /* Respond to any messages before closing */ win = data[i].win; while (imsg = (struct IntuiMessage *)GetMsg( win->UserPort )) ReplyMsg(( struct Message *) imsg ); close_term(i); calc_sigmask(); break; case IDCMP_RAWKEY: handle_rawkey( icode, iqual, iaddr ); break; case IDCMP_MOUSEMOVE: case IDCMP_MOUSEBUTTONS: case IDCMP_MENUVERIFY: if ( blankmouse && !pointer_visible ) { ClearPointer( data[ i ].win ); pointer_visible = TRUE; } break; case IDCMP_INTUITICKS: cursor_anim(); break; case IDCMP_NEWSIZE: /* Calculate new rows & cols. */ win = data[ i ].win; data[ i ].ww = (nw = win->Width / data[ i ].fw) * data[ i ].fw; data[ i ].wh = (nh = win->Height / data[ i ].fh) * data[ i ].fh; data[ i ].wx = win->LeftEdge; data[ i ].wy = win->TopEdge; nw = (data[i].ww - win->BorderRight - win->BorderLeft) / data[i].fw; nh = (data[i].wh - win->BorderTop - win->BorderBottom) / data[i].fh; /* Don`t let user have huge windows */ if (nh > MAX_TERM_VERT) nh = MAX_TERM_VERT; if (nw > MAX_TERM_HORIZ) nw = MAX_TERM_HORIZ; data[ i ].cols = nw; data[ i ].rows = nh; if (KICK20) { ULONG f; UWORD temp; f = (ULONG)MAXBODY * nh; f /= MAX_TERM_VERT; temp = (UWORD)f; ChangeWindowBox(win, data[ i ].wx, data[ i ].wy, data[ i ].ww, data[ i ].wh); /* Update any gadgets inside window */ if (data[ i ].scroll) { NewModifyProp(&data[ i ].ygad, win, NULL,AUTOKNOB | FREEVERT | PROPNEWLOOK, data[i].ygadinfo.HorizPot, 0, data[i].ygadinfo.HorizBody, temp, 1); } data[ i ].ypos = 0; data[ i ].xpos = 0; } Term_activate(angband_term[ i ]); /* * If window scrolling is on, then we always want `band to draw * everything, and we do our own clipping. Otherwise, set the window * to the correct size and let `band handle it. */ if (data[ i ].scroll) Term_resize(80,24); else Term_resize(nw,nh); Term_redraw(); Term_activate(angband_term[ 0 ]); /* Eat the IDCMP_NEWSIZE event coming from ChangeWindowBox(). Icky hack. */ while (imsg = (struct IntuiMessage *)GetMsg( win->UserPort )) ReplyMsg(( struct Message *) imsg ); break; case IDCMP_MENUPICK: handle_menupick( icode, i ); break; } } /* Work out 'signal' mask for all our windows */ static void calc_sigmask(void) { struct Window *win; int i; for (sigmask = i = 0 ; i < MAX_TERM_DATA ; i++) { if (win = data[i].win) sigmask |= (1 << win->UserPort->mp_SigBit); } } errr amiga_event( int v ) { BOOL messages; struct IntuiMessage *imsg; ULONG iclass; UWORD icode; UWORD iqual; APTR iaddr; int i; #ifndef __GNUC__ chkabort(); #endif /* Create mask for Wait(), as we want to watch all of our windows */ if (!sigmask) calc_sigmask(); do { messages = FALSE; for (i = 0 ; i < MAX_TERM_DATA ; i++) { if (!data[i].win) continue; imsg = (struct IntuiMessage *)GetMsg( data[i].win->UserPort ); if (imsg) { iclass = imsg->Class; icode = imsg->Code; iqual = imsg->Qualifier; iaddr = imsg->IAddress; if ( iclass == IDCMP_MENUVERIFY && icode == MENUHOT && use_menus ) update_menus(); ReplyMsg(( struct Message *) imsg ); process_msg(i,iclass,icode,iqual,iaddr); messages = TRUE; } } } while (messages); /* Wait for an event if caller wants us to */ if (v) { Wait(sigmask); /* Ought to handle messages here */ return 0; } else return 1; } static errr amiga_react( int v ) { /* Apply color palette, in case it has changed */ load_palette(); /* Create menus if we don't have any */ if ( use_menus && !menu ) create_menus(); return ( 0 ); } /* Display graphical tombstone. Note this changes the palette so a load_palette after termination is a *must*! */ int amiga_tomb( void ) { /* This stinks :( */ static ULONG tomb_col[ 16 ] = { 0x000000, 0xf0e0d0, 0x808080, 0x505050, 0xe0b000, 0xc0a070, 0x806040, 0x403020, 0x00a0f0, 0x0000f0, 0x000070, 0xf00000, 0x800000, 0x9000b0, 0x006010, 0x60f040, }; cptr p; char tmp[160]; time_t ct = time((time_t)0); FILE *file; char *pp; int plane, row, error = FALSE; struct BitMap *filebm, *scalbm, *convbm; long stdpens[256]; int depth,i; int tw = data[ 0 ].fw * 64; int th = data[ 0 ].fh * 21; /* Release old pens. This might have bad side effects, not sure yet */ for (i = 0 ; i < 16 ; i++) free_pen(abandon_pen()); /* This is naughty :) */ for (i = 0 ; i < 16 ; i++) { stdpens[i] = alloc_pen( (tomb_col[i] >> 16) & 0xFF, (tomb_col[i] >> 8) & 0xFF, tomb_col[i] & 0xFF); } /* Allocate bitmap for tomb graphics file */ filebm = alloc_bitmap( TOMW, TOMH, TOMB, BMF_CLEAR | BMF_STANDARD, data[ 0 ].rp->BitMap ); if (!filebm) return( FALSE ); /* Open tomb file */ path_make( tmp , ANGBAND_DIR_XTRA , MTOM); file = fopen( tmp, "r" ); if (!file) { free_bitmap( filebm ); return( FALSE ); } /* Read file into bitmap */ for ( plane = 0; plane < TOMB && !error; plane++ ) { pp = filebm->Planes[ plane ]; for ( row = 0; row < TOMH && !error; row++ ) { error = ( fread( pp, 1, TOMW >> 3, file) != (TOMW >> 3)); pp += filebm->BytesPerRow; } } fclose( file); /* Get depth of display */ depth = depth_of_bitmap( data[ 0 ].rp->BitMap ); /* Allocate bitmap for remapped image */ convbm = alloc_bitmap( TOMW, TOMH, depth, BMF_CLEAR, data[ 0 ].rp->BitMap ); if (!convbm) { free_bitmap( filebm ); return( FALSE ); } /* Remap old bitmap into new bitmap */ remap_bitmap( filebm, convbm, stdpens, TOMW, TOMH ); free_bitmap( filebm ); /* Allocate bitmap for scaled graphics */ if (KICK20) { scalbm = alloc_bitmap( tw, th, screen_depth, BMF_CLEAR | BMF_STANDARD, data[ 0 ].rp->BitMap ); if (!scalbm) { free_bitmap( convbm ); return ( FALSE ); } /* Scale the tomb bitmap */ scale_bitmap( convbm, TOMW, TOMH, scalbm, tw, th ); /* Free old bitmap */ free_bitmap( convbm ); /* Copy the tomb graphics to the screen, centered */ BltBitMapRastPort( scalbm, 0, 0, data[ 0 ].rp, ( data[ 0 ].ww - tw ) / 2, 0, tw, th, 0xc0 ); /* Free bitmap */ free_bitmap( scalbm ); } else scalbm = convbm; /* King or Queen */ if (p_ptr->total_winner || (p_ptr->lev > PY_MAX_LEVEL)) { p = "Magnificent"; } /* Normal */ else { p = player_title[p_ptr->pclass][(p_ptr->lev-1)/5]; } tomb_str( 3, " R.I.P." ); #ifdef ANG282 tomb_str( 5, op_ptr->full_name ); #else tomb_str( 5, player_name ); #endif tomb_str( 6, "the" ); tomb_str( 7, (char *)p ); tomb_str( 9, (char *)cp_ptr->title ); strnfmt( tmp, 160, "Level: %d", (int)p_ptr->lev ); tomb_str( 10, tmp ); strnfmt( tmp, 160, "Exp: %ld", (long)p_ptr->exp ); tomb_str( 11, tmp ); strnfmt( tmp, 160, "AU: %ld", (long)p_ptr->au ); tomb_str( 12, tmp ); strnfmt( tmp, 160, "Killed on Level %d", p_ptr->depth ); tomb_str( 13, tmp ); strnfmt( tmp, 160, "by %s", p_ptr->died_from ); tomb_str( 14, tmp ); strnfmt( tmp, 160, "%-.24s", ctime(&ct)); tomb_str( 16, tmp ); return( TRUE ); } void tomb_str( int y, char *str ) { term_data *td = &data[ 0 ]; int l = strlen( str ); int xp = ( 39 * td->fw ) - (( l + 1 ) * td->fw ) / 2; SetDrMd( td->rp, JAM1 ); SetAPen( td->rp, PEN( 1 )); Move( td->rp, xp + 1, y * td->fh + td->fb + 1 ); Text( td->rp, str, l ); SetAPen( td->rp, PEN( 0 )); Move( td->rp, xp, y * td->fh + td->fb ); Text( td->rp, str, l ); SetDrMd( td->rp, JAM2 ); } void handle_rawkey( UWORD code, UWORD qual, APTR addr ) { BOOL shift; char buf[ 80 ]; int i; int len; UWORD q; do_after = 0; /* Use a blank mouse-pointer on this window */ if ( blankmouse && pointer_visible ) { SetPointer( data[ 0 ].win, blankpointer, 2, 16, 0, 0 ); pointer_visible = FALSE; } /* Handle cursor keys */ if ( code > 0x4B && code < 0x50 ) { shift = qual & ( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT ); /* Cursor Up */ if ( code == 0x4C ) Term_keypress( shift ? '8' : '8' ); /* Cursor Right */ else if ( code == 0x4E ) Term_keypress( shift ? '6' : '6' ); /* Cursor Down */ else if ( code == 0x4D ) Term_keypress( shift ? '2' : '2' ); /* Cursor Left */ else Term_keypress( shift ? '4' : '4' ); return; } /* Numeric keypad pressed with qualifier? */ if (( qual & IEQUALIFIER_NUMERICPAD ) && ( qual & 0xff )) { /* Direction key? (1,2,3,4,6,7,8,9) */ if (( code >= 0x1d && code <= 0x1f ) || ( code == 0x2d || code == 0x2f ) || ( code >= 0x3d && code <= 0x3f )) { /* Shift/Ctrl/Alt/Amiga keys */ q = qual & 0xff; /* Shift + Direction */ if ( q == IEQUALIFIER_LSHIFT || q == IEQUALIFIER_RSHIFT ) { /* Fake a keypress 'run' */ Term_keypress( '.' ); /* Remove shift key from event */ qual &= ~( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT ); } /* Alt + Direction */ else if ( q == IEQUALIFIER_LALT || q == IEQUALIFIER_RALT ) { /* Fake a keypress 'tunnel' */ Term_keypress( 'T' ); /* Remove alt key from event */ qual &= ~( IEQUALIFIER_LALT | IEQUALIFIER_RALT ); } /* Ctrl + Direction */ else if ( q == IEQUALIFIER_CONTROL ) { /* Fake a keypress 'open' */ Term_keypress( 'o' ); /* Remove ctrl key from event */ qual &= ~IEQUALIFIER_CONTROL; } } /* Numeric '5' key */ else if ( code == 0x2E ) { /* Fake some 'search' commands */ Term_keypress( 's' ); Term_keypress( 's' ); Term_keypress( 's' ); Term_keypress( 's' ); } } /* Convert raw keycode to ANSI sequence */ ie.ie_Class = IECLASS_RAWKEY; ie.ie_Code = code; ie.ie_Qualifier = qual; ie.ie_EventAddress = (APTR *) *((ULONG *) addr ); /* Removed 2.0+ .library method - no need for it now */ len = RawKeyConvert( &ie, buf, 80, NULL ); /* Send ANSI sequence to meta-terminal */ for ( i = 0; i < len; i++ ) Term_keypress( (unsigned char) buf[ i ]); } void handle_menupick( int mnum, int term ) { struct MenuItem *item; ULONG ud; int i; /* Be sure to handle all selections */ while ( mnum != MENUNULL ) { /* Find address of menuitem */ if (term) item = ItemAddress( data[term].menu, mnum); else item = ItemAddress( menu, mnum ); /* Find userdata of menuitem */ ud = (ULONG) GTMENUITEM_USERDATA( item ); /* Is this a window item? */ if ( ud >= MNU_WINDOW ) { i = ud - MNU_WINDOW; if ( item->Flags & CHECKED ) open_term( i, TRUE ); else close_term( i ); } /* Is this a help item? */ else if ( ud >= MNU_HELP ) { /* Send keypresses */ Term_keypress( '\\' ); Term_keypress( '?' ); Term_keypress( ud - MNU_HELP ); } /* Control key shortcuts */ else if ( ud >= MNU_CKEYCOM ) { /* Send keycode */ Term_keypress( '\\' ); Term_keypress( '^' ); Term_keypress( ud - MNU_CKEYCOM ); } /* Key shortcuts */ else if ( ud >= MNU_KEYCOM ) { /* Key code */ i = ud - MNU_KEYCOM; /* Some functions need underlying keymap */ if ( i != 't' && i != 'w' && i != 'T' ) Term_keypress( '\\' ); /* Send keycode */ Term_keypress( i ); } /* Amiga palette requester */ else { switch( ud ) { case MNU_WINDOW_FONT: { char fontname[128]; term_data *td = &data[term]; if ( td->ownf && td->font ) { CloseFont( td->font ); td->font = NULL; } request_font(fontname); handle_font(td, fontname); SetFont(td->rp, td->font); Term_activate( angband_term[ term ] ); Term_redraw(); Term_fresh(); break; } case MNU_WINDOW_REDRAW: Term_redraw(); Term_fresh(); break; case MNU_WINDOW_TOGGLEBORDERS: data[term].wx = data[term].win->LeftEdge; data[term].wy = data[term].win->TopEdge; close_term(term); data[term].backdrop = ~(data[term].backdrop); open_term(term, TRUE); amiga_flush(1); Term_redraw(); Term_fresh(); break; case MNU_SAVE_WINDOWS: amiga_save_file(); break; case MNU_PALETTE: amiga_redefine_colours(); break; case MNU_SAVE_PALETTE: amiga_save_palette(); break; case MNU_LOAD_PALETTE: amiga_load_palette(); break; case MNU_SCALEDMAP: amiga_map(); break; case MNU_GRAPHICS_OFF: amiga_gfx(0); break; case MNU_GRAPHICS_8: amiga_gfx(1); break; case MNU_GRAPHICS_16: amiga_gfx(2); break; case MNU_EXPORT_HS: amiga_hs_to_ascii(); break; #ifdef GFXFUNCS case MNU_GFXMAP: amiga_gfxmap(); break; #endif default: printf("handle_menupick() : bad menu item %d\n",ud); break; } } /* Find next menunumber */ mnum = item->NextSelect; } } /* Write settings back to settings.prf. This is another hack, but I can`t think of a really nice way to do this. */ static void amiga_save_file( void ) { char fname[ MAX_PATH_LENGTH ]; char buf[ 256 ]; ULONG fsrc,fdst; int i,k; FILE *f,*fh; fh = fopen("ram:temp","w"); if (!fh) return; path_make( fname , ANGBAND_DIR_USER , WPRF); f = fopen( fname , "r" ); if (!f) { amiga_show("Can't open settings.prf"); fclose(fh); return; } while (1) { BOOL matched = 1; if (!fgets(buf,200,f)) break; i = 0; while (buf[i] == 32 || buf[i] == 9) i++; if (buf[i] == '\n') matched = 0; for (k = 0 ; term_short[k] ; k++) { if (!strnicmp(buf + i,term_short[k],strlen(term_short[k]))) matched = 0; } if (matched) fprintf(fh,"%s",buf); } for (i = 0 ; i < MAX_TERM_DATA ; i++) { if (data[i].win) { data[i].wx = data[i].win->LeftEdge; data[i].wy = data[i].win->TopEdge; } fprintf(fh,"%s.use %c\n",term_short[i],get_bool(data[i].use)); fprintf(fh,"%s.show %c\n",term_short[i],get_bool(!data[i].iconified)); fprintf(fh,"%s.scroll %c\n",term_short[i],get_bool(data[i].scroll)); fprintf(fh,"%s.title %s\n",term_short[i],data[i].name); fprintf(fh,"%s.font %s\n",term_short[i],data[i].fontname); fprintf(fh,"%s.xpos %d\n",term_short[i],data[i].wx); fprintf(fh,"%s.ypos %d\n",term_short[i],data[i].wy); fprintf(fh,"%s.cols %d\n",term_short[i],data[i].cols); fprintf(fh,"%s.rows %d\n",term_short[i],data[i].rows); fprintf(fh,"\n"); } fclose(f); fclose(fh); /* Copy temp file to other one. */ fsrc = (ULONG)Open("ram:temp",1005); if (!fsrc) { amiga_show("Can't open temporary file"); return; } fdst = (ULONG)Open(fname,1006); if (!fdst) { amiga_show("Can't write to settings.prf"); Close(fsrc); return; } i = 128; while (i == 128) { i = Read(fsrc,buf,128); if (i) Write(fdst,buf,i); } Close(fsrc); Close(fdst); remove("ram:temp"); } static char get_bool( BOOL opt ) { if (opt) return 'Y'; else return 'N'; } static void cursor_on( term_data *td ) { int x0, y0, x1, y1; if ( !td->cursor_lit && !td->iconified ) { td->cursor_frame = 0; /* Hack - Don't draw cursor at (0,0) */ if ( !td->cursor_xpos && !td->cursor_ypos ) return; /* Draw an outlined cursor */ if( CUR_A & 0xf0 && (use_graphics == GRAPHICS_ORIGINAL)) { x0 = td->cursor_xpos * td->fw; y0 = td->cursor_ypos * td->fh; x1 = x0 + td->fw - 1; y1 = y0 + td->fh - 1; SetAPen( td->wrp, PEN( CURSOR_PEN )); Move( td->wrp, x0, y0 ); Draw( td->wrp, x1, y0 ); Draw( td->wrp, x1, y1 ); Draw( td->wrp, x0, y1 ); Draw( td->wrp, x0, y0 ); } /* Draw a filled cursor */ else { SetBPen( td->wrp, PEN( CURSOR_PEN )); SetAPen( td->wrp, PEN( CURSOR_PEN )); RectFill(td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos, td->fw * (td->cursor_xpos + 1) - 1, td->fh * (td->cursor_ypos + 1) - 1); SetAPen( td->wrp, PEN( CUR_A & 0x0f )); Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb ); Text( td->wrp, &CUR_C, 1 ); } td->cursor_lit = TRUE; } } static void cursor_off( term_data *td ) { if ( td->cursor_lit && !td->iconified ) { /* Restore graphics under cursor */ if ( CUR_A & 0xf0 && (use_graphics == GRAPHICS_ORIGINAL)) { put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, Term->scr->tc[ td->cursor_ypos ][ td->cursor_xpos ], Term->scr->ta[ td->cursor_ypos ][ td->cursor_xpos ]); use_mask = 1; put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, Term->scr->c[ td->cursor_ypos ][ td->cursor_xpos ], Term->scr->a[ td->cursor_ypos ][ td->cursor_xpos ]); use_mask = 0; } /* Restore char/attr under cursor */ else { if (td->background) { BltBitMapRastPort( td->background, td->cursor_xpos * td->fw, td->cursor_ypos * td->fh, td->wrp, td->cursor_xpos * td->fw, td->cursor_ypos * td->fh, td->fw,td->fh, 0xC0); SetAPen( td->wrp, PEN( CUR_A & 0x0f )); SetBPen( td->wrp, PEN( 0 )); Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb ); Text( td->wrp, &CUR_C, 1 ); } else { SetAPen( td->wrp, PEN( CUR_A & 0x0f )); SetBPen( td->wrp, PEN( 0 )); Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb ); Text( td->wrp, &CUR_C, 1 ); } } td->cursor_lit = FALSE; } } static void cursor_anim( void ) { term_data *td = term_curs; int x0, y0, x1, y1; int i = p_ptr->px,j = p_ptr->py; if ( !term_curs || td->iconified || !td->wrp) return; /* Don't want cursor blit optimised */ block_nasty_gfx = TRUE; td->cursor_frame = ++(td->cursor_frame) % 8; /* Small cursor on map */ if ( td->cursor_map ) { if ( td->cursor_frame & 2 ) { SetAPen( td->wrp, PEN( CURSOR_PEN )); RectFill( td->wrp, td->map_x + i * td->mpt_w, td->map_y + j * td->mpt_h, td->map_x + ( i + 1) * td->mpt_w - 1, td->map_y + ( j + 1 ) * td->mpt_h - 1 ); } else put_gfx_map( td, i, j, get_p_char(),get_p_attr() ); } else if ( td->cursor_visible && !iconified ) { /* Hack - Don't draw cursor at (0,0) */ if ( td->cursor_xpos == 0 && td->cursor_ypos == 0 ) { block_nasty_gfx = FALSE; return; } /* Draw an outlined cursor */ if ( CUR_A & 0x80 && (use_graphics == GRAPHICS_ORIGINAL)) { /* First draw the tile under cursor */ put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, Term->scr->tc[ td->cursor_ypos ][ td->cursor_xpos ], Term->scr->ta[ td->cursor_ypos ][ td->cursor_xpos ]); use_mask = 1; put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, Term->scr->c[ td->cursor_ypos ][ td->cursor_xpos ], Term->scr->a[ td->cursor_ypos ][ td->cursor_xpos ]); use_mask = 0; if ( td->cursor_frame < 4 ) { x0 = td->cursor_xpos * td->fw; y0 = td->cursor_ypos * td->fh; x1 = x0 + td->fw - 1; y1 = y0 + td->fh - 1; SetAPen( td->wrp, PEN( CURSOR_PEN )); Move( td->wrp, x0, y0 ); Draw( td->wrp, x1, y0 ); Draw( td->wrp, x1, y1 ); Draw( td->wrp, x0, y1 ); Draw( td->wrp, x0, y0 ); } } /* Draw a filled cursor */ else { SetBPen( td->wrp, PEN( CURSOR_PEN )); SetAPen( td->wrp, PEN( CURSOR_PEN )); RectFill(td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos, td->fw * (td->cursor_xpos + 1) - 1, td->fh * (td->cursor_ypos + 1) - 1); SetAPen( td->wrp, PEN( CUR_A & 0x0f )); Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb ); Text( td->wrp, &CUR_C, 1 ); } } block_nasty_gfx = FALSE; } static void free_gfx(void) { if (tglob.mskbm) free_bitmap(tglob.mskbm); if (tglob.gfxbm) free_bitmap(tglob.gfxbm); if (tglob.mapbm) free_bitmap(tglob.mapbm); if (tglob.chunky_mask) free(tglob.chunky_mask); if (tglob.chunky_tile) free(tglob.chunky_tile); tglob.mapbm = tglob.mskbm = tglob.gfxbm = NULL; tglob.chunky_tile = tglob.chunky_mask = NULL; } #define PROGRESS(num, offset) \ RectFill(ts->rp, ts->fw * 22 + 1, ts->fh * offset + 1, ts->fw * (22 + num) - 1, ts->fh * (offset + 1) - 1); #define CLEAR_PROGRESS(offset) \ RectFill(ts->rp, ts->fw * 22, ts->fh * offset, ts->fw * 29, ts->fh * (offset + 1)); /* Load the tile graphics and mask into gfxbm/mskbm */ static int load_gfx( void ) { char tmp[MAX_PATH_LENGTH]; term_data *ts = &data[ 0 ]; FILE *file; char *p; int plane, row, error = FALSE; if (screen_enhanced) { GFXW = AB_GFXW; GFXH = AB_GFXH; GFXB = AB_GFXB; } else { GFXW = DF_GFXW; GFXH = DF_GFXH; GFXB = DF_GFXB; } /* Allocate bitmaps */ tglob.gfxbm = alloc_bitmap( GFXW, GFXH, GFXB, BMF_CLEAR | BMF_STANDARD, ts->rp->BitMap ); if ( !tglob.gfxbm ) return( FALSE ); tglob.mskbm = alloc_bitmap( GFXW, GFXH, 1, BMF_CLEAR | BMF_STANDARD, ts->rp->BitMap ); if ( !tglob.mskbm ) return( FALSE); /* Open file */ path_make( tmp , ANGBAND_DIR_XTRA , screen_enhanced ? AB_MGFX : DE_MGFX ); file = fopen( tmp, "r" ); if (!file) { MSG( 0, 0, "Unable to open graphics file" ); Delay( 100 ); return ( FALSE ); } MSG( 0, 0, "Loading graphics ." ); /* Read file into bitmap */ if ( tglob.gfxbm->BytesPerRow == (GFXW >> 3) ) { for ( plane = 0; plane < GFXB && !error; plane++ ) { p = tglob.gfxbm->Planes[ plane ]; error = fread(p, 1, (GFXW >> 3) * GFXH, file) != ((GFXW >> 3) * GFXH); } } else { for ( plane = 0; plane < GFXB && !error; plane++ ) { p = tglob.gfxbm->Planes[ plane ]; for ( row = 0; row < GFXH && !error; row++ ) { error = (fread(p, 1, GFXW >> 3, file) != (GFXW >> 3)); p += tglob.gfxbm->BytesPerRow; } } } MSG( 0, 0, "Loading graphics .." ); /* Read mask data into bitmap */ if ( tglob.mskbm->BytesPerRow == (GFXW >> 3) ) fread(tglob.mskbm->Planes[0], 1, (GFXW >> 3) * GFXH, file); else { p = tglob.mskbm->Planes[ 0 ]; for ( row = 0; row < GFXH && !error; row++ ) { error = (fread(p, 1, GFXW >> 3, file) != (GFXW >> 3)); p += tglob.mskbm->BytesPerRow; } } MSG( 0, 0, "Loading graphics ..." ); fclose( file ); /* Did we get any errors while reading? */ if ( error ) { MSG( 0, 0, "Error while reading graphics file" ); Delay( 100 ); return ( FALSE ); } /* Success */ return ( TRUE ); } static int conv_gfx( void ) { term_data *ts = &data[ 0 ]; struct BitMap *tmpbm; struct BitMap *sbm = ts->rp->BitMap; static long stdpens[256]; long mskpens[] = { 0, 1 }; long *pen_ptr; int depth; int proposed_depth; for (depth = 0 ; depth < 256 ; depth++) stdpens[depth] = depth; /* Get depth of display */ depth = proposed_depth = depth_of_bitmap( sbm ); SetAPen(ts->rp,PEN( TERM_WHITE )); Move(ts->rp, ts->fw * 22, ts->fh * 1); Draw(ts->rp, ts->fw * 22, ts->fh * 2); Draw(ts->rp, ts->fw * 29, ts->fh * 2); SetAPen(ts->rp,PEN( TERM_L_DARK )); Draw(ts->rp, ts->fw * 29, ts->fh * 1); Draw(ts->rp, ts->fw * 22, ts->fh * 1); SetAPen(ts->rp, PEN( TERM_BLUE )); pen_ptr = stdpens; if (use_graphics == GRAPHICS_ORIGINAL) pen_ptr = gfxpens; /* Remap graphics and mask bitmaps to correct colours: a) AGA/ECS : Write/read from memory directly b) CGX : Use Cybergraphics library c) Other : Just use normal intuition calls and hope for the best Note all this only applies to graphics, not text. All text is handled as normal in the 68k versions. */ /* With CGX we only need a maximum of 8 bits for gfx */ if (use_cyber && proposed_depth > 8) proposed_depth = 8; tmpbm = alloc_bitmap( ts->map_w, ts->map_h, proposed_depth, BMF_CLEAR | BMF_STANDARD, sbm ); if (!tmpbm) { MSG( 0, 1, "Can`t allocate a temporary bitmap (tiles)" ); Delay( 100 ); return FALSE; } /* Remap mini map tiles */ remap_bitmap( tglob.mapbm, tmpbm, pen_ptr, ts->map_w, ts->map_h ); free_bitmap( tglob.mapbm); tglob.mapbm = tmpbm; PROGRESS(1,1); tmpbm = alloc_bitmap( ts->gfx_w, ts->gfx_h, proposed_depth, BMF_CLEAR, sbm); if (!tmpbm) { MSG( 0, 1, "Can`t allocate a temporary bitmap (tiles)" ); Delay( 100 ); return FALSE; } PROGRESS(2,1); remap_bitmap( tglob.gfxbm, tmpbm, pen_ptr, ts->gfx_w, ts->gfx_h ); free_bitmap( tglob.gfxbm ); PROGRESS(3,1); tglob.gfxbm = tmpbm; PROGRESS(4,1); if (use_cyber) { byte *cm; byte *cd; int lx, ly; tglob.chunky_mask = cd = malloc( ts->gfx_w * ts->gfx_h ); cm = (byte *)tglob.mskbm->Planes[0]; cd = tglob.chunky_mask; for (ly = 0 ; ly < ts->gfx_h ; ly++) { for (lx = 0 ; lx < (ts->gfx_w >> 3) ; lx++) *cd++ = *cm++; if ((ts->gfx_w / 8) & 1) cd++; if (ly == (ts->gfx_h >> 1)) PROGRESS(5,1); cm = cm - (ts->gfx_w >> 3); cm += tglob.mskbm->BytesPerRow; } PROGRESS(7,1); SetAPen(ts->rp, PEN(TERM_DARK) ); CLEAR_PROGRESS(1); return TRUE; } /* Try to remap mask bitmap */ tmpbm = alloc_bitmap( ts->gfx_w, ts->gfx_h, depth, BMF_CLEAR, sbm ); if (!tmpbm) { MSG( 0, 1, "Can`t allocate a temporary bitmap (mask)" ); Delay( 100 ); return FALSE; } PROGRESS(6,1); /* XXX XXX XXX Replace with copy_bitmap. Oops, I deleted that :) */ remap_bitmap( tglob.mskbm, tmpbm, mskpens, ts->gfx_w, ts->gfx_h ); free_bitmap( tglob.mskbm ); tglob.mskbm = tmpbm; PROGRESS(7,1); SetAPen(ts->rp, PEN(TERM_DARK) ); CLEAR_PROGRESS(1); /* Done */ return TRUE; } /* Scale graphics to correct size */ static int size_gfx( term_data *td ) { int depth; struct BitMap *sbm = td->rp->BitMap; struct BitMap *tmpbm; int tilew = 8, tileh = 8; int tilenumw = (DF_GFXW / tilew); int tilenumh = (DF_GFXH / tileh); if (KICK13) return( TRUE ); if (screen_enhanced) { tilew = tileh = 16; tilenumw = (AB_GFXW / tilew); tilenumh = (AB_GFXH / tileh); } /* Calculate tile bitmap dimensions */ td->gfx_w = (GFXW / tilew) * td->fw; td->gfx_h = (GFXH / tileh) * td->fh; /* Calculate map bitmap dimensions */ #ifdef ANG282 td->mpt_w = td->ww / DUNGEON_WID; td->mpt_h = td->wh / DUNGEON_HGT; #else td->mpt_w = td->ww / MAX_WID; td->mpt_h = td->wh / MAX_HGT; #endif td->map_w = td->mpt_w * tilenumw; td->map_h = td->mpt_h * tilenumh; /* Friend bitmap */ if ( td->rp ) sbm = td->rp->BitMap; /* Scale tile graphics into map size */ depth = depth_of_bitmap( tglob.gfxbm ); if (( tglob.mapbm = alloc_bitmap( td->map_w, td->map_h, depth, BMF_CLEAR | BMF_STANDARD, sbm )) == NULL ) return( FALSE ); scale_bitmap( tglob.gfxbm, GFXW, GFXH, tglob.mapbm, td->map_w, td->map_h ); /* Scale tile graphics */ depth = depth_of_bitmap( tglob.gfxbm ); if (( tmpbm = alloc_bitmap( td->gfx_w, td->gfx_h, depth, BMF_CLEAR | BMF_STANDARD, sbm )) == NULL ) return( FALSE ); scale_bitmap( tglob.gfxbm, GFXW, GFXH, tmpbm, td->gfx_w, td->gfx_h ); if ( tglob.gfxbm ) free_bitmap( tglob.gfxbm ); tglob.gfxbm = tmpbm; /* Scale tile mask */ depth = depth_of_bitmap( tglob.mskbm ); if (( tmpbm = alloc_bitmap( td->gfx_w, td->gfx_h, depth, BMF_CLEAR | BMF_STANDARD, sbm )) == NULL ) return( FALSE ); scale_bitmap( tglob.mskbm, GFXW, GFXH, tmpbm, td->gfx_w, td->gfx_h ); if ( tglob.mskbm ) free_bitmap( tglob.mskbm ); tglob.mskbm = tmpbm; /* Success */ return( TRUE ); } static void put_gfx( struct RastPort *rp, int x, int y, int chr, int col ) { term_data *td = (term_data *)(Term->data); int fw = td->fw; int fh = td->fh; int x0 = x * fw; int y0 = y * fh; int x1 = x0 + fw - 1; int y1 = y0 + fh - 1; int a = col & 0x7F; int c = chr & 0x7F; if (( td->iconified ) || ( !rp )) return; /* Just a black tile */ if ( a == 0 && c == 0 ) { if ( use_bkg ) BltBitMapRastPort( td->background, c * fw, a * fh, rp, x0, y0, fw, fh, 0xC0); else { SetAPen( rp, PEN( 0 )); RectFill( rp, x0, y0, x1, y1 ); } return; } /* Remap player for race and class */ if ( a == 12 && c == 0 ) { a = get_p_attr(); c = get_p_char(); } /* Draw tile through mask */ if ( use_mask ) { if ( use_bkg ) BltBitMapRastPort( td->background, c * fw, a * fh, rp, x0, y0, fw, fh, 0xC0); if (use_cyber) BltMaskBitMapRastPort( tglob.gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, (ABC|ANBC|ABNC), tglob.chunky_mask ); else BltMaskBitMapRastPort( tglob.gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, (ABC|ANBC|ABNC), tglob.mskbm->Planes[ 0 ] ); } /* Draw full tile */ else { if (use_aga) quick_BltBitMapRastPort( tglob.gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, 0xc0 ); else BltBitMapRastPort( tglob.gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, 0xc0 ); } } #ifndef __GNUC__ static int breakfunc(void) { puts("break!"); amiga_fail("Ctrl-C received....quitting"); return 1; } #endif /* Be prepared for this to be called multiple times! */ static int amiga_fail( char *msg ) { int i; /* Print error message */ if ( msg ) puts( msg ); if (tglob.resources_freed) return(-1); /* Free shared term data */ if (tglob.chunky_tile) free(tglob.chunky_tile); if (tglob.chunky_mask) free(tglob.chunky_mask); if (tglob.gfxbm) free_bitmap(tglob.gfxbm); if (tglob.mskbm) free_bitmap(tglob.mskbm); if (tglob.mapbm) free_bitmap(tglob.mapbm); /* Free sound memory */ free_sound(); /* Unlock public screen */ if ( publock ) { UnlockPubScreen( NULL, pubscr ); publock = FALSE; } /* Remove menu from window */ if ( menu && data[ 0 ].win ) ClearMenuStrip( data[ 0 ].win ); /* Free menus */ if ( menu ) FreeMenus( menu ); FreeMem( blankpointer, BLANKPOINTER_SIZE ); if (sound_name_desc) free(sound_name_desc); if (sound_data) free(sound_data); /* Free term resources */ for ( i = MAX_TERM_DATA - 1; i >= 0; i--) free_term( &data[ i ] ); /* Free obtained pens */ for ( i = 0; i < 512; i++) { while ( obtain_mask[ i ] ) { ReleasePen( pubscr->ViewPort.ColorMap, i ); obtain_mask[i]--; } } /* Free visual info . It may seem like paranoia here, but it simplifies other code to do this */ if ( visinfo && KICK30) { FreeVisualInfo( visinfo ); visinfo = NULL; } /* Close intuition screen */ if ( amiscr ) CloseScreen( amiscr ); /* Close console.device */ if ( ConsoleDev ) CloseDevice( (struct IORequest *)ConsoleDev ); /* Close various libraries */ if ( CyberGfxBase ) CloseLibrary( CyberGfxBase ); if ( DataTypesBase ) CloseLibrary( DataTypesBase ); if ( DiskfontBase ) CloseLibrary( DiskfontBase ); if ( GadToolsBase ) CloseLibrary( GadToolsBase ); if ( ReqToolsBase ) CloseLibrary( (struct Library *)ReqToolsBase ); if ( AslBase ) CloseLibrary( AslBase ); if ( IFFBase ) CloseLibrary( IFFBase ); tglob.resources_freed = TRUE; return( -1 ); } static int player_x; static int player_y; /* * Notice that the player has moved */ static void ami_player_move(int x, int y, vptr dummy) { /* Hack - ignore parameter */ (void) dummy; /* Save for later */ player_x = x; player_y = y; } /* * Save the information so we can access it later */ static void ami_map_info(map_block *mb_ptr, const term_map *map, vptr dummy) { /* Hack -- ignore parameter */ (void) dummy; /* Store feature code for later */ if (map->terrain) { mb_ptr->a = f_info[map->terrain].x_attr; mb_ptr->c = f_info[map->terrain].x_char; } else { mb_ptr->a = 0; mb_ptr->c = 0; } } static void amiga_map( void ) { term_data *td = &data[ 0 ]; int i,j; byte ta,tc; map_block *mb_ptr; /* Only in graphics mode, and not on Kickstart1.3 */ if ((use_graphics == GRAPHICS_NONE) || KICK13 || !tglob.mapbm) return; /* Turn off cursor */ if ( td->cursor_visible ) cursor_off( td ); /* Save screen */ Term_save(); /* Clear screen */ Term_clear(); Term_fresh(); /* In the dungeon */ if (p_ptr->depth) { /* Calculate offset values */ td->map_x = (( td->fw * 80 ) - ( td->mpt_w * MAX_WID )) / 2; td->map_y = (( td->fh * 24 ) - ( td->mpt_h * MAX_HGT )) / 2; } if (td->map_x < 0) td->map_x = 0; if (td->map_y < 0) td->map_y = 0; /* Draw all "interesting" features */ for ( i = 0; i < MAX_WID; i++ ) { for ( j = 0; j < MAX_HGT; j++ ) { /* Get frame tile */ if ( (i == 0) || (i == MAX_WID - 1) || (j == 0) || (j == MAX_HGT - 1) ) { ta = f_info[63].x_attr; tc = f_info[63].x_char; /* Put the graphics to the screen */ put_gfx_map( td, i, j, tc, ta ); } /* Get tile from cave table */ else { int x = player_x + i - MAX_WID / 2; int y = player_y + j - MAX_HGT / 2; if (!map_in_bounds(x, y)) continue; if ((x == player_x) && (y == player_y)) { ta = get_p_attr(); tc = get_p_char(); /* Put the graphics to the screen */ put_gfx_map( td, i, j, tc, ta ); } else { mb_ptr = map_loc(x, y); ta = mb_ptr->a; tc = mb_ptr->c; /* Ignore non-graphics */ if ( ta & 0x80 ) { ta = ta & ((GFXH >> 3) - 1); tc = tc & ((GFXW >> 3) - 1); /* Put the graphics to the screen */ put_gfx_map( td, i, j, tc, ta ); } } } } } /* Draw a small cursor now */ td->cursor_map = TRUE; /* Wait for a keypress, flush key buffer */ Term_inkey( &tc, TRUE, TRUE ); Term_flush(); /* Normal cursor again */ td->cursor_map = FALSE; /* Restore screen */ Term_clear(); Term_fresh(); Term_load(); Term_fresh(); /* Turn cursor back on */ if ( td->cursor_visible ) cursor_on( td ); } void load_palette( void ) { int i; /* Release all the pens we have */ for (i = 0 ; i < 512 ; i++) free_pen(i); /* Now create the text colours */ for (i = 0 ; i < 16 ; i++) { penconv[i] = alloc_pen( angband_color_table[i][1], angband_color_table[i][2], angband_color_table[i][3]); /* This error message should never happen. */ if (penconv[i] == -1) FAIL("Can't allocate any pens for text colours!"); } /* Now do the rest */ if (use_graphics == GRAPHICS_ORIGINAL) { /* We may run out of colours here; in that case we'll attempt to pick sensible ones via alloc_pen. I reckon text colour is more important than gfx colour. */ for (i = 0 ; i < (screen_enhanced ? 256 : 32 ); i++) { gfxpens[i] = alloc_pen( palette256[(i << 2) + 1], palette256[(i << 2) + 2], palette256[(i << 2) + 3]); } } } /* Returns TRUE if can read AB palette, FALSE if not */ static int read_enhanced_palette(void) { static char buffer[1024]; FILE *f; path_make( buffer, ANGBAND_DIR_XTRA, AB_MGFX_CMAP ); f = fopen(buffer, "r"); if (f) { int success = fread(palette256,1024,1,f); fclose(f); if (success == 1) return TRUE; } return FALSE; } /* Returns TRUE if can read normal 8x8 palette, FALSE if not */ static int read_normal_palette(void) { static char buffer[1024]; FILE *f; path_make( buffer, ANGBAND_DIR_XTRA, DE_MGFX_CMAP ); f = fopen(buffer, "r"); if (f) { int success = fread(palette256,128,1,f); fclose(f); if (success == 1) return TRUE; } return FALSE; } ULONG trans( byte g ) { ULONG h; h = (g << 8) | (g << 16) | (g << 24) | g; return h; } int create_menus( void ) { struct NewMenu *item = newmenu; int nmsize = sizeof ( struct NewMenu ); int i; /* Option code deleted. It's just such a pain to do :( */ for ( i = 0; menu_ptr[ i ].nm_Type != NM_END; i++ ) memcpy( item++, &menu_ptr[ i ], nmsize ); /* Copy all post-items into array */ for ( i = 0; post_item[ i ].nm_Type != NM_END; i++ ) memcpy( item++, &post_item[ i ], nmsize ); /* Actually create the menu structures */ menu = CreateMenus( newmenu, GTMN_FrontPen, (long)penconv[ TERM_WHITE ], NULL ); if ( menu ) { /* Layout menus */ if ( LayoutMenus( menu, visinfo, KICK30 ? GTMN_NewLookMenus : TAG_IGNORE, TRUE, TAG_END )) { /* Attach menu to window */ SetMenuStrip( data[ 0 ].win , menu ); } /* Free menus */ else { FreeMenus( menu ); menu = NULL; } } /* Success */ return ( menu != NULL ); } static int find_menuitem(int *rmenu, int *item, void *ud) { int i = -1; int a = -1, b = -1; do { i++; if (newmenu[i].nm_Type == NM_TITLE) { a++; b = 0; } else if (newmenu[i].nm_Type == NM_ITEM) b++; if (newmenu[i].nm_UserData == ud) { *rmenu = a; *item = b; return 1; } } while (newmenu[i].nm_Type != NM_END); return 0; } void update_menus( void ) { struct MenuItem *item; int i, a, b; /* Require a window and a menu */ if ( !data[ 0].win || !menu ) return; /* Detach the menu from the window */ /* Enable/Disable the amiga map according to use_graphics */ if (find_menuitem(&a, &b, (void *)MNU_SCALEDMAP)) { if ( item = ItemAddress( menu, FULLMENUNUM(a, b, 0))) { if (use_graphics == GRAPHICS_NONE) { item->Flags = item->Flags & ~ITEMENABLED; } else { item->Flags = item->Flags | ITEMENABLED; } } } /* Enable/Disable the palette requester */ if (find_menuitem(&a, &b, (void *)MNU_PALETTE)) { if ( item = ItemAddress( menu, FULLMENUNUM(a, b, 0))) item->Flags = amiga_palette ? item->Flags | ITEMENABLED : item->Flags & ~ITEMENABLED; } /* Enable/Disable and check window menu items according to use and iconified status */ for ( i = 1; i < MAX_TERM_DATA; i++ ) { if (find_menuitem(&a, &b, MWI( i ))) { if ( item = ItemAddress( menu, FULLMENUNUM( a, b, 0 ))) { item->Flags = data[ i ].use ? item->Flags | ITEMENABLED : item->Flags & ~ITEMENABLED; item->Flags = ( data[ i ].use && !data[ i ].iconified ) ? item->Flags | CHECKED : item->Flags & ~CHECKED; } } } /* Attach menu to window again */ } int init_sound( void ) { static char tmp[MAX_PATH_LENGTH]; static char buf[MAX_PATH_LENGTH]; static char line[256]; struct AmiSound *snd; static int use_angsound = FALSE; FILE *f; char *s = sound_name_desc; int i,j,k,slev; BOOL memory; path_make(buf, ANGBAND_DIR_XTRA, "cfg/sound.cfg"); /* Look for .cfg file */ f = fopen(buf,"r"); if (!f) { puts("Can't find xtra/cfg/sound.cfg - sound support disabled"); return (has_sound = use_sound = FALSE); } while (fgets( line, 200, f )) { for (i = strlen(line) - 1; i >= 0 ; i--) { if (line[i] == 10 || line[i] == 13) line[i] = 32; } for (i = 0; line[i] && line[i] <= 32 ; i++) ; if (line[i] == '#' || line[i] == ';' || !line[i]) continue; k = i; while (line[i] && line[i] != ':') i++; if (!line[i]) continue; line[i] = 0; for (j = k ; j < i ; j++) { if (line[j] == 32 || line[j] == 9) { line[j] = 0; break; } } slev = -1; for (j = 1 ; j < SOUND_MAX ; j++) { if (strreq(angband_sound_name[j] , line + k)) { slev = j; break; } } if (slev == -1) continue; i++; while (line[i] && (line[i] == 32 || line[i] == 9)) i++; if (!line[i]) continue; /* Remember sample name, if necessary */ do { memory = TRUE; if (line[i] == '!') { memory = FALSE; i++; } /* Frankly, this whole thing is a hack. But I won't tell if you don't. */ while (line[i] > 32) *s++ = line[i++]; *s++ = 0; *s++ = slev; *s++ = memory; while (line[i] && (line[i] == 32 || line[i] == 9)) i++; sounds_needed++; } while (line[i]); } fclose(f); if (getasn("AngSound")) use_angsound = TRUE; path_make(buf, ANGBAND_DIR_XTRA, "sound"); sound_data = snd = malloc( sizeof(struct AmiSound) * sounds_needed ); if (!sound_data) return( has_sound = use_sound = FALSE ); for (i = 0 ; i < SOUND_MAX ; i++) sound_ref[i][0] = (struct AmiSound *)0; s = sound_name_desc; for (i = 0 ; i < sounds_needed ; i++) { snd->Name = s; snd->Volume = 64; snd->Channel = 1; snd->Rate = 0; snd->Repeats = 1; snd->Memory = 1; snd->Address = NULL; while (*s++) ; j = *s++; if (!*s++) snd->Memory = 0; sound_ref[j][0] = (struct AmiSound *)((int)sound_ref[j][0] + 1); sound_ref[j][ (int)sound_ref[j][0] ] = snd; if ((int)sound_ref[j][0] == 8) { puts("Too many sounds for one sound_event (8 is max)"); sound_ref[j][0] = (struct AmiSound *)7; } if ( snd->Memory ) { FILE *f; /* Construct filename */ path_make(tmp, buf, snd->Name ); if (!(f = fopen(tmp, "r"))) { if (use_angsound) strnfmt(tmp, MAX_PATH_LENGTH, "AngSound:%s",snd->Name); } else fclose(f); /* Load the sample into memory */ snd->Address = (struct SoundInfo *) PrepareSound( tmp ); } snd++; } has_sound = use_sound = TRUE; return ( TRUE ); } void free_sound( void ) { int i; struct AmiSound *snd; /* Stop all channels */ StopSound( LEFT0 ); StopSound( LEFT1 ); StopSound( RIGHT0 ); StopSound( RIGHT1 ); snd = sound_data; /* Remove all sounds from memory */ for ( i = 0; i < sounds_needed; i++ ) { if ( snd->Address ) { RemoveSound( snd->Address ); snd->Address = NULL; } snd++; } has_sound = use_sound = FALSE; } static void play_sound( int v ) { struct AmiSound *snd; struct AmiSound *old_snd; int rate; int channel; int old,vnum; char buf[MAX_PATH_LENGTH]; char tmp[MAX_PATH_LENGTH]; static int try_angsound = FALSE; static int use_angsound = FALSE; if (use_sound && !try_angsound) { try_angsound = TRUE; use_angsound = (int)(getasn("AngSound")); } if ( has_sound ) { /* If no sounds are available for chosen event, skip */ if ((int)sound_ref[v][0] == 0) return; if (v > SOUND_MAX) return; /* Just pick 1st sound available at the moment */ snd = sound_ref[v][vnum = 1 + rand_int( (int)sound_ref[v][0] )]; /* Channel number */ channel = snd->Channel; /* Last sample played on channel */ old = channel_last[ channel ]; /* Sample Rate */ rate = snd->Rate; /* Random rate on some sounds */ if ( v == SOUND_HIT || v == SOUND_MISS ) rate = rate - 50 + rand_int( 150 ); /* Pointer to old sound data */ old_snd = (old >= 0) ? (sound_ref[old][channel_num[channel]]) : NULL; if (old_snd) { /* Stop sound currently playing on this channel */ StopSound( channel ); /* Free old sample if required */ if ( !old_snd->Memory && old_snd->Address && old != v ) { /* Remove it from memory */ RemoveSound( old_snd->Address ); /* Clear address field */ old_snd->Address = NULL; } } /* Load new sample into memory if required */ if ( !snd->Memory && snd->Address == NULL ) { FILE *f; /* Construct filename */ path_make(buf, ANGBAND_DIR_XTRA, "sound"); path_make(tmp, buf, snd->Name ); if (!(f = fopen(tmp, "r"))) { if (use_angsound) strnfmt(tmp, MAX_PATH_LENGTH, "AngSound:%s",snd->Name); } else fclose(f); /* Load the sample into memory */ snd->Address = (struct SoundInfo *) PrepareSound( tmp ); } /* Make sure the sample is loaded into memory */ if ( snd->Address ) { /* Start playing the sound */ PlaySound( snd->Address, snd->Volume, channel, rate, snd->Repeats ); } /* Store sample number */ channel_last[ channel ] = v; channel_num[ channel ] = vnum; } } void put_gfx_map( term_data *td, int x, int y, int c, int a ) { if ( td->iconified || !td->wrp) return; if (use_aga) quick_BltBitMapRastPort( tglob.mapbm, c * td->mpt_w, a * td->mpt_h, td->wrp, td->map_x + x * td->mpt_w, td->map_y + y * td->mpt_h, td->mpt_w, td->mpt_h, 0xC0); else BltBitMapRastPort( tglob.mapbm, c * td->mpt_w, a * td->mpt_h, td->wrp, td->map_x + x * td->mpt_w, td->map_y + y * td->mpt_h, td->mpt_w, td->mpt_h, 0xC0); } struct BitMap *alloc_bitmap( int width, int height, int depth, ULONG flags, struct BitMap *friend ) { int p; struct BitMap *bitmap; unsigned char *bp; if ( KICK30 ) { /* Should this work for 8-bit too? */ if (use_cyber && ((flags & BMF_STANDARD) == 0) ) return ( AllocBitMap( width, height, depth, flags | BMF_MINPLANES, friend )); else { return(AllocBitMap( width, height, depth, flags, friend )); } } else { /* Allocate bitmap structure */ if (( bitmap = AllocMem( sizeof( struct BitMap ), MEMF_PUBLIC | MEMF_CLEAR ))) { InitBitMap( bitmap, depth, width, height ); /* Allocate bitplanes */ for ( p = 0; p < depth; p++ ) { bp = AllocRaster( width, height ); if ( !bp ) break; bitmap->Planes[ p ] = bp; } /* Out of memory */ if ( p != depth ) { /* Free bitplanes */ while ( --p >= 0 ) FreeRaster( bitmap->Planes[ p ], width, height ); /* Free bitmap structure */ FreeMem( bitmap, sizeof( struct BitMap )); bitmap = NULL; } } return ( bitmap ); } } void free_bitmap( struct BitMap *bitmap ) { int p; WaitBlit(); if ( KICK30 ) FreeBitMap( bitmap ); else { /* Free bitplanes */ for ( p = 0; p < bitmap->Depth; p++ ) FreeRaster( bitmap->Planes[ p ], bitmap->BytesPerRow * 8, bitmap->Rows ); /* Free bitmap structure */ FreeMem( bitmap, sizeof( struct BitMap )); } } void scale_bitmap( struct BitMap *srcbm, int srcw, int srch, struct BitMap *dstbm, int dstw, int dsth ) { struct BitScaleArgs bsa; bsa.bsa_SrcBitMap = srcbm; bsa.bsa_DestBitMap = dstbm; bsa.bsa_SrcX = bsa.bsa_SrcY = 0; bsa.bsa_SrcWidth = srcw; bsa.bsa_SrcHeight = srch; bsa.bsa_DestX = bsa.bsa_DestY = 0; bsa.bsa_XSrcFactor = srcw; bsa.bsa_YSrcFactor = srch; bsa.bsa_XDestFactor = dstw; bsa.bsa_YDestFactor = dsth; bsa.bsa_Flags = 0; BitMapScale( &bsa ); } void remap_bitmap( struct BitMap *srcbm, struct BitMap *dstbm, long *pens, int width, int height ) { static struct RastPort tmprp; int x,y,p,c,ox,lpr,sd,dd; int bm[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; UBYTE *sp[ 32 ]; UBYTE *dp[ 32 ]; ULONG ls[ 32 ]; ULONG ld[ 32 ]; ULONG mask; int enforce_aga = TRUE; /* Handle gfx cards. Use the AGA/ECS/OCS code if at all possible! */ if (use_cyber) { if (GetCyberMapAttr(srcbm, CYBRMATTR_ISCYBERGFX) || GetCyberMapAttr(dstbm, CYBRMATTR_ISCYBERGFX)) enforce_aga = 0; } if (!enforce_aga) { struct RastPort mainrast,newrast; struct BitMap *tmpbm; LONG colour; LONG twidth; byte *s; twidth = ((width + 15) >> 4) << 1; s = malloc(width * 2); InitRastPort(&mainrast); InitRastPort(&newrast); mainrast.BitMap = srcbm; newrast.BitMap = dstbm; tmpbm = alloc_bitmap( width, 1, depth_of_bitmap(dstbm), NULL, NULL ); /* tmpbm->BytesPerRow = (((width + 15)>>4)<<1); */ /* tmprp = mainrast; */ tmprp.Layer = NULL; tmprp.BitMap = tmpbm; /* V40 uses ChunkyPixel commands */ if ( GfxBase->LibNode.lib_Version >= 40 ) { for (y = 0 ; y < height ; y++) { /* ReadPixelLine8(&mainrast, 0, y, width, s, &tmprp); */ for (x = 0 ; x < width ; x++) s[x] = pens[ ReadPixel(&mainrast,x,y) ]; WriteChunkyPixels(&newrast, 0, y, width, y, s, width); } } else { for (y = 0 ; y < height ; y++) { for (x = 0 ; x < width ; x++) { colour = pens[ ReadPixel(&mainrast,x,y) ]; SetAPen(&newrast,colour); WritePixel(&newrast,x,y); } } } free_bitmap(tmpbm); free(s); return; } /* Source bitplanes */ sd = depth_of_bitmap( srcbm ); for ( p = 0; p < sd; p++ ) sp[ p ] = srcbm->Planes[ p ]; /* Destination bitplanes */ dd = depth_of_bitmap( dstbm ); for ( p = 0; p < dd; p++ ) dp[ p ] = dstbm->Planes[ p ]; /* Number of longwords per row */ lpr = width / 32; /* Convert graphics */ for ( y = 0; y < height; y++ ) { ox = 0; for ( x = 0 ; x < lpr; x++ ) { /* Read source longwords */ for ( p = 0; p < sd; p++ ) ls[ p ] = *(ULONG *)( sp[ p ] + ox); /* Clear destination longwords */ for ( p = 0; p < dd; ld[ p++ ] = 0 ); /* Remap */ for ( mask = 0x80000000; mask != 0; mask >>= 1) { /* Find color index */ for ( p = c = 0; p < sd; p++ ) { if ( ls[ p ] & mask ) c |= bm[ p ]; } /* Remap */ c = pens[ c ]; /* Update destination longwords */ for ( p = 0; p < dd; p++ ) { if ( c & bm[ p ] ) ld[ p ] |= mask; } } /* Write destination longwords */ for ( p = 0; p < dd; p++ ) *(ULONG *)( dp[ p ] + ox ) = ld[ p ]; /* Update offset */ ox += 4; } /* Update pointers to get to next line */ for ( p = 0; p < sd; sp[ p++ ] += srcbm->BytesPerRow ); for ( p = 0; p < dd; dp[ p++ ] += dstbm->BytesPerRow ); } } int depth_of_bitmap( struct BitMap *bm ) { if ( KICK30 ) return ( (int)GetBitMapAttr( bm, BMA_DEPTH ) ); else return (bm->Depth); } /* Put message on screen, allow time for it to be read, then erase it */ void amiga_show( char *str ) { char *spaces = " "; amiga_text( 0, 0, strlen( str ), 1, str ); Delay(80); amiga_text( 0, 0, strlen( spaces ), 1, spaces ); } /* Brings up Reqtools palette requester, extracts new colours and places in angband_color_table. */ void amiga_redefine_colours( void ) { term_data *ts = &data[ 0 ]; struct BitMap *sbm = ts->rp->BitMap; int cols; int i; /* Paranoia */ if (!amiga_palette || pubscr || !ReqToolsBase) return; /* Get number of colours on screen */ cols = 1 << depth_of_bitmap(sbm); if (cols > 256) cols = 256; i = rtPaletteRequest("Redefine the colours",NULL,RT_Screen,amiscr,TAG_END); /* If colours not changed, say bye bye */ if (i == -1) return; /* Hack values into angband_color_table */ if (KICK30) { unsigned long *ctable,*c; c = ctable = (long *)AllocMem(cols << 4,0); if (!ctable) return; GetRGB32(amiscr->ViewPort.ColorMap,0,cols,ctable); if (use_graphics == GRAPHICS_NONE) { for (i = 0 ; i < cols ; i++) { angband_color_table[i + 16][0] = 1; angband_color_table[i + 16][1] = *c++ >> 24; angband_color_table[i + 16][2] = *c++ >> 24; angband_color_table[i + 16][3] = *c++ >> 24; } } else { for (i = 0 ; i < cols ; i++) { angband_color_table[i][0] = 1; angband_color_table[i][1] = *c++ >> 24; angband_color_table[i][2] = *c++ >> 24; angband_color_table[i][3] = *c++ >> 24; } } FreeMem(ctable,cols << 4); } else { unsigned long w; if (use_graphics == GRAPHICS_NONE) { for (i = 0 ; i < cols ; i++) { w = GetRGB4(amiscr->ViewPort.ColorMap,i); angband_color_table[i + 16][0] = 1; angband_color_table[i + 16][1] = (w & 0xF00) >> 4; angband_color_table[i + 16][2] = (w & 0xF0); angband_color_table[i + 16][3] = (w & 0xF) << 4; } } else { for (i = 0 ; i < cols ; i++) { w = GetRGB4(amiscr->ViewPort.ColorMap,i); angband_color_table[i][0] = 1; angband_color_table[i][1] = (w & 0xF00) >> 4; angband_color_table[i][2] = (w & 0xF0); angband_color_table[i][3] = (w & 0xF) << 4; } } } } /* -------------------------------------------------------------------- */ /* amiga_makepath( path ) */ /* */ /* Attempt to pick a sensible path for Angband to load from. If we */ /* have OS2.0 or better, then we can use 'PROGDIR:' - we don't need an */ /* explicit assign. */ /* */ /* Under OS1.3 (or if PROGDIR: is not available for some bizarre */ /* reason), we'll use a game-specific assign (ie. 'Zangband:') */ /* */ /* This is patched into the 'main.c' file at the moment. */ /* -------------------------------------------------------------------- */ void amiga_makepath( char *name ) { FILE *f; static char temp[512]; static char temp2[512]; int pass = 0; struct IntuitionBase *l; /* XXX XXX XXX Ugh. Nasty */ l = (struct IntuitionBase *)OpenLibrary( "intuition.library", 0L); kick_ver = l->LibNode.lib_Version; CloseLibrary((struct Library *)l); /* If KS1.3, user will have to have assigned 'angband' manually */ if (KICK13) { strcpy(name,VERPATH); return; } /* Use PROGDIR if available; check if progdir points to correct path */ /* This is ugly code, but keeps the PPC version happy. */ do { if (!pass) NameFromLock(GetProgramDir(),temp,400); else NameFromLock(ParentDir(GetProgramDir()),temp,400); strcpy(temp2, temp); AddPart(temp, "edit/r_info.txt", 500); if (f = fopen(temp,"r")) break; strcpy(temp, temp2); AddPart(temp, "data/r_info.raw", 500); if (f = fopen(temp,"r")) break; pass++; } while (pass < 2); if (f) { char c; fclose(f); c = temp2[strlen(temp2) - 1]; if (c != '/' && c != ':') strcat(temp2,"/"); strcpy(name, temp2); return; } strcpy(name,VERPATH); return; } /* Dump palette in same format as Angband; this is not ideal */ void amiga_save_palette( void ) { FILE *fff; int i; char buf[MAX_PATH_LENGTH]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, "colours.prf"); /* Append to the file */ fff = fopen(buf, "w"); /* Failure */ if (!fff) { amiga_show("Can't save palette!"); return; } /* Start dumping */ fprintf(fff, "\n\n# Color redefinitions\n\n"); /* Dump colours */ for (i = 0; i < 256; i++) { int kv = angband_color_table[i][0]; int rv = angband_color_table[i][1]; int gv = angband_color_table[i][2]; int bv = angband_color_table[i][3]; cptr name = "unknown"; /* Skip non-entries */ if (!kv && !rv && !gv && !bv) continue; /* Extract the color name */ if (i < 16) name = color_names[i]; /* Dump a comment */ fprintf(fff, "# Color '%s'\n", name); /* Dump the palette info */ fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n", i, kv, rv, gv, bv); } /* All done */ fprintf(fff, "\n\n\n\n"); /* Close */ fclose(fff); } /* Loads the current palette */ void amiga_load_palette( void ) { process_pref_file("colours.prf"); Term_xtra(TERM_XTRA_REACT, 0); Term_redraw(); } /* -------------------------------------------------------------------- */ /* amiga_hs_to_ascii( void ) */ /* */ /* Dumps the highscore table to the 'apex/scores.txt' file. This is */ /* just *too* hacky to document right now. */ /* -------------------------------------------------------------------- */ void amiga_hs_to_ascii(void) { char filename[MAX_PATH_LENGTH]; char destfile[MAX_PATH_LENGTH]; char temp[200]; char date_temp[15]; struct high_score h; int i; FILE *f,*d; int pr, pc, clev, mlev, cdun, mdun; cptr gold, when, aged; path_make(filename,ANGBAND_DIR_APEX,"scores.raw"); f = fopen(filename,"r"); if (!f) { amiga_show("Can't open highscore file!"); return; } path_make(destfile,ANGBAND_DIR_APEX,"scores.txt"); d = fopen(destfile,"w"); if (!d) { amiga_show("Can't open destination file!"); fclose(f); return; } /* Print header, and underline it*/ strnfmt(temp, 200, "Highscore file for %s",VARIANT); fprintf(d,"%s\n",temp); temp[ i = strlen(temp) ] = 0; while (i) temp[--i] = '-'; fprintf(d,"%s\n\n",temp); for (i = 0 ; i < MAX_HISCORES; i++) { if (!fread(&h,sizeof(h),1,f)) break; /* Extract the race/class */ pr = atoi(h.p_r); pc = atoi(h.p_c); /* Extract the level info */ clev = atoi(h.cur_lev); mlev = atoi(h.max_lev); cdun = atoi(h.cur_dun); mdun = atoi(h.max_dun); /* Hack -- extract the gold and such */ for (when = h.day; isspace(*when); when++) /* loop */; for (gold = h.gold; isspace(*gold); gold++) /* loop */; for (aged = h.turns; isspace(*aged); aged++) /* loop */; /* Reconfigure Date */ if ((*when == '@') && strlen(when) == 9) { strnfmt(date_temp, 15, "%.2s-%.2s-%.4s", when+7, when+5, when+1); } else { strnfmt(date_temp, 15, "%.2s-%.2s-20%.2s", when+3, when, when+6); } when = date_temp; /* Dump some info */ strnfmt(temp, 200, "%3d.%9s %s the %s %s, Level %d", i + 1, h.pts, h.who, race_info[pr].title, class_info[pc].title, clev); /* Dump the first line */ fprintf(d, "%s\n",temp); strnfmt(temp, 200, " Killed by %s on %s %d", h.how, "Dungeon Level", cdun); if (!cdun) /* -KMW- */ strnfmt(temp, 200, " Killed by %s in the Town", h.how); /* Append a "maximum level" */ if (mdun > cdun) strcat(temp, format(" (Max %d)", mdun)); /* Dump the info */ fprintf(d, "%s\n",temp); /* And still another line of info */ strnfmt(temp, 200, " (Date %s, Gold %s, Turn %s).", when, gold, aged); fprintf(d, "%s\n\n",temp); } fclose(d); fclose(f); } /* Provides name of the last used save file in 'buf', by reading 'user/data-ami.prf'. This is a hack, but works for most player names. Insert in main.c */ void amiga_user_name( char *buf ) { char temp[MAX_PATH_LENGTH]; char name[24]; int i; FILE *f; /* Check if our data file exists; if not, return 'PLAYER' */ path_make(temp,ANGBAND_DIR_USER,"data-ami.prf"); f = fopen(temp,"r"); if (!f) { strcpy(buf,"PLAYER"); return; } /* Paranoia - if line not valid, return "PLAYER" */ if (!fgets(name,24,f)) { fclose(f); strcpy(buf,"PLAYER"); return; } /* Kill white space */ for (i = strlen(name) - 1; i && (name[i] <= 32) ; i--) name[i] = 0; strcpy(buf,name); fclose(f); } /* -------------------------------------------------------------------- */ /* amiga_write_user_name( char *name ) */ /* */ /* Writes the 'name' argument to the file 'user/data-ami.prf'. */ /* Generally, the 'name' argument will be 'player_name' i.e. 'Gandalf' */ /* This is used in the 'save_player()' routine (see save.c) to */ /* automagically load the last used player... */ /* */ /* Note that this has to be explicitly added to save.c */ /* -------------------------------------------------------------------- */ void amiga_write_user_name( char *name ) { char temp[MAX_PATH_LENGTH]; FILE *f; path_make(temp,ANGBAND_DIR_USER,"data-ami.prf"); f = fopen(temp,"w"); if (!f) return; fprintf(f,"%s\n",name); fclose(f); } /* -------------------------------------------------------------------- */ /* get_p_attr( void ) */ /* */ /* Returns an integer representing the 'attr' attribute of the current */ /* player graphic. This will usually vary depending upon player's */ /* chosen class. */ /* -------------------------------------------------------------------- */ static int get_p_attr( void ) { return p_ptr->pclass + 36; } /* -------------------------------------------------------------------- */ /* get_p_char( void ) */ /* */ /* Returns an integer representing the 'char' attribute of the current */ /* player graphic. This will usually vary depending upon player's */ /* chosen class. */ /* -------------------------------------------------------------------- */ static int get_p_char( void ) { return p_ptr->prace; } static void amiga_gfx(int type) { if (!type) { use_graphics = GRAPHICS_NONE; screen_enhanced = 0; reset_visuals(); free_gfx(); do_cmd_redraw(); } if (type) { use_graphics = GRAPHICS_ORIGINAL; screen_enhanced = (type > 1); free_gfx(); init_default_palette(); if (screen_enhanced) read_enhanced_palette(); else read_normal_palette(); load_palette(); Term_save(); SetRast( data[0].rp, PEN( 0 )); MSG( 0, 0, "Loading graphics" ); if ( !load_gfx() ) { Term_load(); use_graphics = GRAPHICS_NONE; return; } size_gfx( &data[ 0 ] ); MSG( 0, 1, "Remapping graphics" ); if ( !conv_gfx() ) { Term_load(); use_graphics = GRAPHICS_NONE; return; } Term_load(); /* XXX XXX XXX */ if (screen_enhanced) use_graphics = GRAPHICS_ADAM_BOLT; else use_graphics = GRAPHICS_ORIGINAL; reset_visuals(); do_cmd_redraw(); } } /* NOTE : The font for this had better not be NULL else all hell will break loose. You Have Been Warned */ static void quick_Text(struct RastPort *rp, int col, char *s, int n, int dx, int dy) { ULONG *dest_plane_addr; ULONG dest_row = rp->BitMap->BytesPerRow; short depth; int x; struct TextFont *font = rp->Font; ULONG source_row = font->tf_Modulo; static ULONG left_mask[] = { 0, 0x80000000L, 0xC0000000L, 0xE0000000L, 0xF0000000L, 0xF8000000L, 0xFC000000L, 0xFE000000L, 0xFF000000L, 0xFF800000L, 0xFFC00000L, 0xFFE00000L, 0xFFF00000L, 0xFFF80000L, 0xFFFC0000L, 0xFFFE0000L }; byte *source; byte *dest; ULONG mask; UWORD *floc = (UWORD *)font->tf_CharLoc; short t; ULONG sourceoffset, destoffset, sval; static byte *p; static byte *m; int dbit; /* This is very possibly wrong, because it's a complete guess */ dx += rp->Layer->bounds.MinX; dy += rp->Layer->bounds.MinY; dy -= font->tf_Baseline; /* AGA only, or at least AGA *bitmaps* only */ startme: if (!n) return; dbit = 1; dest_plane_addr = (ULONG *)&(rp->BitMap->Planes[0]); depth = rp->BitMap->Depth; /* Find point in dest bitmap */ destoffset = (dy * dest_row) + dx / 8; sourceoffset = floc[ (*s - font->tf_LoChar) << 1 ]; x = sourceoffset & 7; sourceoffset >>= 3; mask = left_mask[ font->tf_XSize ]; mask >>= (dx & 7); while (depth) { source = (byte *)font->tf_CharData + sourceoffset; dest = (byte *)((*dest_plane_addr++) + destoffset); for (t = font->tf_YSize ; t ; t--) { p = (byte *)&sval; m = (byte *)&mask; sval = (*((ULONG *)source) << (ULONG)(x & 7)); sval >>= (dx & 7); if (!(col & dbit)) sval = 0; *dest = (~(*m) & *dest) | (*m & *p++); m++; dest[1] = (~(*m) & dest[1]) | (*m & *p); dest += dest_row; source += source_row; } depth--; dbit += dbit; } n--; dx += font->tf_XSize; s++; goto startme; } static void quick_BltBitMapRastPort( struct BitMap *src, int x, int y, struct RastPort *rp, int dx, int dy, int dw, int dh, int mode) { ULONG *dest_plane_addr = (ULONG *)&(rp->BitMap->Planes[0]); ULONG *source_plane_addr = (ULONG *)&(src->Planes[0]); ULONG dest_row = rp->BitMap->BytesPerRow; ULONG source_row = src->BytesPerRow; short depth = src->Depth; static ULONG left_mask[] = { 0, 0x80000000L, 0xC0000000L, 0xE0000000L, 0xF0000000L, 0xF8000000L, 0xFC000000L, 0xFE000000L, 0xFF000000L, 0xFF800000L, 0xFFC00000L, 0xFFE00000L, 0xFFF00000L, 0xFFF80000L, 0xFFFC0000L, 0xFFFE0000L }; byte *source; byte *dest; ULONG mask; short t; ULONG sourceoffset, destoffset, sval; static byte *p; static byte *m; /* This is very possibly wrong, because it's a complete guess */ dx += rp->Layer->bounds.MinX; dy += rp->Layer->bounds.MinY; /* AGA only, or at least AGA *bitmaps* only */ /* Find point in dest bitmap */ destoffset = (dy * dest_row) + dx / 8; sourceoffset = (y * source_row) + x / 8; mask = left_mask[ dw ]; mask >>= (dx & 7); while (depth) { source = (byte *)((*source_plane_addr++) + sourceoffset); dest = (byte *)((*dest_plane_addr++) + destoffset); for (t = dh ; t ; t--) { p = (byte *)&sval; m = (byte *)&mask; sval = (*((ULONG *)source) << (ULONG)(x & 7)); sval >>= (dx & 7); *dest = (~(*m) & *dest) | (*m & *p++); m++; dest[1] = (~(*m) & dest[1]) | (*m & *p); dest += dest_row; source += source_row; } depth--; } } #if 0 static void quick_clearBltBitMapRastPort( struct BitMap *src, int x, int y, struct RastPort *rp, int dx, int dy, int dw, int dh, int mode) { ULONG *dest_plane_addr = (ULONG *)&(rp->BitMap->Planes[0]); ULONG *source_plane_addr = (ULONG *)&(src->Planes[0]); ULONG dest_row = rp->BitMap->BytesPerRow; ULONG source_row = src->BytesPerRow; short depth = src->Depth; static ULONG left_mask[] = { 0, 0x80000000L, 0xC0000000L, 0xE0000000L, 0xF0000000L, 0xF8000000L, 0xFC000000L, 0xFE000000L, 0xFF000000L, 0xFF800000L, 0xFFC00000L, 0xFFE00000L, 0xFFF00000L, 0xFFF80000L, 0xFFFC0000L, 0xFFFE0000L }; byte *source; byte *dest; ULONG mask; short t; ULONG sourceoffset, destoffset, sval; static byte *p; static byte *m; /* AGA only, or at least AGA *bitmaps* only */ /* Find point in dest bitmap */ destoffset = (dy * dest_row) + dx / 8; sourceoffset = (y * source_row) + x / 8; mask = left_mask[ dw ]; mask >>= (dx & 7); while (depth) { source = (byte *)((*source_plane_addr++) + sourceoffset); dest = (byte *)((*dest_plane_addr++) + destoffset); for (t = dh ; t ; t--) { p = &sval; m = &mask; sval = 0; sval = (*((ULONG *)source) << (ULONG)(x & 7)); sval >>= (dx & 7); *dest = (~(*m) & *dest) | (*m & *p++); m++; dest[1] = (~(*m) & dest[1]) | (*m & *p); dest += dest_row; source += source_row; } depth--; } } static void quick_BltMaskBitMapRastPort( struct BitMap *src, int x, int y, struct RastPort *rp, int dx, int dy, int dw, int dh, int mode, byte *mask) { /* ULONG *dest_plane_addr = (ULONG *)&(rp->BitMap->Planes[0]); */ ULONG *dest_plane_addr = (ULONG *)&(rp->Layer->RastPort->BitMap->Planes[0]); ULONG *source_plane_addr = (ULONG *)&(src->Planes[0]); ULONG dest_row = rp->BitMap->BytesPerRow; ULONG source_row = src->BytesPerRow; short depth = src->Depth; static ULONG left_mask[] = { 0, 0x80000000L, 0xC0000000L, 0xE0000000L, 0xF0000000L, 0xF8000000L, 0xFC000000L, 0xFE000000L, 0xFF000000L, 0xFF800000L, 0xFFC00000L, 0xFFE00000L, 0xFFF00000L, 0xFFF80000L, 0xFFFC0000L, 0xFFFE0000L }; byte *source; byte *dest; ULONG mask; short t; ULONG sourceoffset, destoffset, sval; static byte *p; static byte *m; /* NOT FINISHED!! */ /* AGA only, or at least AGA *bitmaps* only */ /* Find point in dest bitmap */ destoffset = (dy * dest_row) + dx / 8; sourceoffset = (y * source_row) + x / 8; mask = left_mask[ dw ]; mask >>= (dx & 7); while (depth) { source = (byte *)((*source_plane_addr++) + sourceoffset); dest = (byte *)((*dest_plane_addr++) + destoffset); for (t = dh ; t ; t--) { p = &sval; m = &mask; sval = (*((ULONG *)source) << (ULONG)(x & 7)); sval >>= (dx & 7); *dest = (~(*m) & (*dest) | (*m & *p++); m++; dest[1] = (~(*m) & dest[1]) | (*m & *p); dest += dest_row; source += source_row; } depth--; } } #endif #endif /* USE_AMI */ zangband/src/main-cap.c0000755000000000000000000004506610250356274014002 0ustar rootroot/* File: main-cap.c */ /* Purpose: Support for "term.c" using "termcap" calls */ #include "angband.h" #ifdef USE_CAP cptr help_cap[] = { "To use CAP (\"Termcap\" calls)", NULL }; /* * This file is a total hack, but is often very helpful. :-) * * This file allows use of the terminal without requiring the * "curses" routines. In fact, if "USE_HARDCODE" is defined, * this file will attempt to use various hard-coded "vt100" * escape sequences to also avoid the use of the "termcap" * routines. I do not know if this will work on System V. * * This file is intended for use only on those machines which are * unable, for whatever reason, to compile the "main-gcu.c" file, * but which seem to be able to support the "termcap" library, or * which at least seem able to support "vt100" terminals. * * Large portions of this file were stolen from "main-gcu.c" * * This file incorrectly handles output to column 80, I think. */ /* * Require a "system" */ #if !defined(USE_TERMCAP) && !defined(USE_HARDCODE) # define USE_TERMCAP #endif /* * Hack -- try to guess which systems use what commands * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set. * Mega-Hack -- try to guess when "POSIX" is available. * If the user defines two of these, we will probably crash. */ #if !defined(USE_TPOSIX) # if !defined(USE_TERMIO) && !defined(USE_TCHARS) # if defined(_POSIX_VERSION) # define USE_TPOSIX # else # if defined(USG) || defined(linux) || defined(SOLARIS) || defined(WINDOWS) # define USE_TERMIO # else # define USE_TCHARS # endif # endif # endif #endif /* * POSIX stuff */ #ifdef USE_TPOSIX # include # include #endif /* * One version needs these files */ #ifdef USE_TERMIO # include # include #endif /* * The other needs these files */ #ifdef USE_TCHARS # include # include # include # include # include #endif /* * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY" * * They should both work due to the "(i != 1)" test in the code * which checks for the result of the "read()" command. */ #ifndef O_NDELAY # define O_NDELAY O_NONBLOCK #endif #ifdef USE_TERMCAP /* * Termcap string information */ /* The "termcap" entry */ static char blob[1024]; /* The string extraction buffer */ static char termcap_area[1024]; /* The current "index" into "area" */ static char *next = termcap_area; /* The terminal name */ static char *desc; #endif /* * Pointers into the "area" */ static char *cm; /* Move cursor */ static char *ch; /* Move cursor to horizontal location */ static char *cv; /* Move cursor to vertical location */ static char *ho; /* Move cursor to top left */ static char *ll; /* Move cursor to bottom left */ static char *cs; /* Set scroll area */ static char *cl; /* Clear screen */ static char *cd; /* Clear to end of display */ static char *ce; /* Clear to end of line */ static char *cr; /* Move to start of line */ static char *so; /* Turn on standout */ static char *se; /* Turn off standout */ static char *md; /* Turn on bold */ static char *me; /* Turn off bold */ static char *vi; /* Cursor - invisible */ static char *ve; /* Cursor - normal */ static char *vs; /* Cursor - bright */ /* * State variables */ static int rows; /* Screen size (Y) */ static int cols; /* Screen size (X) */ static int curx; /* Cursor location (X) */ static int cury; /* Cursor location (Y) */ static int curv; /* Cursor visibility */ /* * Extern functions */ extern char *getenv(); extern char *tgoto(); extern char *tgetstr(); /* * Write some chars to the terminal */ static void ewrite(char *str) { int numtowrite, numwritten; /* See how much work we have */ numtowrite = strlen(str); /* Write until done */ while (numtowrite > 0) { /* Try to write the chars */ numwritten = write(1, str, numtowrite); /* Handle FIFOs and EINTR */ if (numwritten < 0) numwritten = 0; /* See what we completed */ numtowrite -= numwritten; str += numwritten; /* Hack -- sleep if not done */ if (numtowrite > 0) sleep(1); } } #ifdef USE_TERMCAP static char write_buffer[128]; static char *write_buffer_ptr; static void output_one(char c) { *write_buffer_ptr++ = c; } static void tp(char *s) { /* Dump the string into us */ write_buffer_ptr = write_buffer; /* Write the string with padding */ tputs (s, 1, output_one); /* Finish the string */ *write_buffer_ptr = '\0'; /* Dump the recorded buffer */ ewrite (write_buffer); } #endif #ifdef USE_HARDCODE static void tp(char *s) { ewrite(s); } #endif /* * Clear the screen */ static void do_cl(void) { if (cl) tp (cl); } /* * Clear to the end of the line */ static void do_ce(void) { if (ce) tp(ce); } /* * Set the cursor visibility (0 = invis, 1 = normal, 2 = bright) */ static void curs_set(int vis) { char *v = NULL; if (!vis) { v = vi; } else if (vis > 1) { v = vs ? vs : ve; } else { v = ve ? ve : vs; } if (v) tp(v); } /* * Restrict scrolling to within these rows */ static void do_cs(int y1, int y2) { #ifdef USE_TERMCAP if (cs) tp(tgoto(cs, y2, y1)); #endif #ifdef USE_HARDCODE char temp[64]; strnfmt(temp, sizeof(temp), cs, y1, y2); tp (temp); #endif } /* * Go to the given screen location directly */ static void do_cm(int x, int y) { #ifdef USE_TERMCAP if (cm) tp(tgoto(cm, x, y)); #endif #ifdef USE_HARDCODE char temp[64]; strnfmt(temp, sizeof(temp), cm, y+1, x+1); tp(temp); #endif } /* * Go to the given screen location in a "clever" manner * * XXX XXX XXX This function could use some work! */ static void do_move(int x1, int y1, int x2, int y2) { /* Hack -- unknown start location */ if ((x1 == x2) && (y1 == y2)) do_cm(x2, y2); /* Left edge */ else if (x2 == 0) { if ((y2 <= 0) && ho) tp(ho); else if ((y2 >= rows-1) && ll) tp(ll); else if ((y2 == y1) && cr) tp(cr); #if 0 else if ((y2 == y1+1) && cr && dn) { tp(cr); tp(dn); } else if ((y2 == y1-1) && cr && up) { tp(cr); tp(up); } #endif else do_cm(x2, y2); } #if 0 /* Up/Down one line */ else if ((x2 == x1) && (y2 == y1+1) && dn) tp(dn); else if ((x2 == x1) && (y2 == y1-1) && up) tp(up); #endif /* Default -- go directly there */ else do_cm(x2, y2); } /* * Help initialize this file (see below) */ errr init_cap_aux(void) { #ifdef USE_TERMCAP /* Get the terminal name (if possible) */ desc = getenv("TERM"); if (!desc) return (1); /* Get the terminal info */ if (tgetent(blob, desc) != 1) return (2); /* Get the (initial) columns and rows, or default */ if ((cols = tgetnum("co")) == -1) cols = 80; if ((rows = tgetnum("li")) == -1) rows = 24; /* Find out how to move the cursor to a given location */ cm = tgetstr("cm", &next); if (!cm) return (10); /* Find out how to move the cursor to a given position */ ch = tgetstr("ch", &next); cv = tgetstr("cv", &next); /* Find out how to "home" the screen */ ho = tgetstr("ho", &next); /* Find out how to "last-line" the screen */ ll = tgetstr("ll", &next); /* Find out how to do a "carriage return" */ cr = tgetstr("cr", &next); if (!cr) cr = "\r"; /* Find out how to clear the screen */ cl = tgetstr("cl", &next); if (!cl) return (11); /* Find out how to clear to the end of display */ cd = tgetstr("cd", &next); /* Find out how to clear to the end of the line */ ce = tgetstr("ce", &next); /* Find out how to scroll (set the scroll region) */ cs = tgetstr("cs", &next); /* Find out how to hilite */ so = tgetstr("so", &next); se = tgetstr("se", &next); if (!so || !se) so = se = NULL; /* Find out how to bold */ md = tgetstr("md", &next); me = tgetstr("me", &next); if (!md || !me) md = me = NULL; /* Check the cursor visibility stuff */ vi = tgetstr("vi", &next); vs = tgetstr("vs", &next); ve = tgetstr("ve", &next); #endif #ifdef USE_HARDCODE /* Assume some defualt information */ rows = 24; cols = 80; /* Clear screen */ cl = "\033[2J\033[H"; /* --]--]-- */ /* Clear to end of line */ ce = "\033[K"; /* --]-- */ /* Hilite on/off */ so = "\033[7m"; /* --]-- */ se = "\033[m"; /* --]-- */ /* Scroll region */ cs = "\033[%d;%dr"; /* --]-- */ /* Move cursor */ cm = "\033[%d;%dH"; /* --]-- */ #endif /* Success */ return (0); } /* * Save the "normal" and "angband" terminal settings */ #ifdef USE_TPOSIX static struct termios norm_termios; static struct termios game_termios; #endif #ifdef USE_TERMIO static struct termio norm_termio; static struct termio game_termio; #endif #ifdef USE_TCHARS static struct sgttyb norm_ttyb; static struct tchars norm_tchars; static struct ltchars norm_ltchars; static int norm_local_chars; static struct sgttyb game_ttyb; static struct tchars game_tchars; static struct ltchars game_ltchars; static int game_local_chars; #endif /* * Are we active? Not really needed. */ static int active = FALSE; /* * The main screen (no sub-screens) */ static term term_screen_body; /* * Place the "keymap" into its "normal" state */ static void keymap_norm(void) { #ifdef USE_TPOSIX /* restore the saved values of the special chars */ (void)tcsetattr(0, TCSAFLUSH, &norm_termios); #endif #ifdef USE_TERMIO /* restore the saved values of the special chars */ (void)ioctl(0, TCSETA, (char *)&norm_termio); #endif #ifdef USE_TCHARS /* restore the saved values of the special chars */ (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb); (void)ioctl(0, TIOCSETC, (char *)&norm_tchars); (void)ioctl(0, TIOCSLTC, (char *)&norm_ltchars); (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars); #endif } /* * Place the "keymap" into the "game" state */ static void keymap_game(void) { #ifdef USE_TPOSIX /* restore the saved values of the special chars */ (void)tcsetattr(0, TCSAFLUSH, &game_termios); #endif #ifdef USE_TERMIO /* restore the saved values of the special chars */ (void)ioctl(0, TCSETA, (char *)&game_termio); #endif #ifdef USE_TCHARS /* restore the saved values of the special chars */ (void)ioctl(0, TIOCSETP, (char *)&game_ttyb); (void)ioctl(0, TIOCSETC, (char *)&game_tchars); (void)ioctl(0, TIOCSLTC, (char *)&game_ltchars); (void)ioctl(0, TIOCLSET, (char *)&game_local_chars); #endif } /* * Save the normal keymap */ static void keymap_norm_prepare(void) { #ifdef USE_TPOSIX /* Get the normal keymap */ tcgetattr(0, &norm_termios); #endif #ifdef USE_TERMIO /* Get the normal keymap */ (void)ioctl(0, TCGETA, (char *)&norm_termio); #endif #ifdef USE_TCHARS /* Get the normal keymap */ (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb); (void)ioctl(0, TIOCGETC, (char *)&norm_tchars); (void)ioctl(0, TIOCGLTC, (char *)&norm_ltchars); (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars); #endif } /* * Save the keymaps (normal and game) */ static void keymap_game_prepare(void) { #ifdef USE_TPOSIX /* Acquire the current mapping */ tcgetattr(0, &game_termios); /* Force "Ctrl-C" to interupt */ game_termios.c_cc[VINTR] = (char)3; /* Force "Ctrl-Z" to suspend */ game_termios.c_cc[VSUSP] = (char)26; /* Hack -- Leave "VSTART/VSTOP" alone */ /* Disable the standard control characters */ game_termios.c_cc[VQUIT] = (char)-1; game_termios.c_cc[VERASE] = (char)-1; game_termios.c_cc[VKILL] = (char)-1; game_termios.c_cc[VEOF] = (char)-1; game_termios.c_cc[VEOL] = (char)-1; /* Normally, block until a character is read */ game_termios.c_cc[VMIN] = 1; game_termios.c_cc[VTIME] = 0; /* Hack -- Turn off "echo" and "canonical" mode */ game_termios.c_lflag &= ~(ECHO | ICANON); #endif #ifdef USE_TERMIO /* Acquire the current mapping */ (void)ioctl(0, TCGETA, (char *)&game_termio); /* Force "Ctrl-C" to interupt */ game_termio.c_cc[VINTR] = (char)3; /* Force "Ctrl-Z" to suspend */ game_termio.c_cc[VSUSP] = (char)26; /* Hack -- Leave "VSTART/VSTOP" alone */ /* Disable the standard control characters */ game_termio.c_cc[VQUIT] = (char)-1; game_termio.c_cc[VERASE] = (char)-1; game_termio.c_cc[VKILL] = (char)-1; game_termio.c_cc[VEOF] = (char)-1; game_termio.c_cc[VEOL] = (char)-1; #if 0 /* Disable the non-posix control characters */ game_termio.c_cc[VEOL2] = (char)-1; game_termio.c_cc[VSWTCH] = (char)-1; game_termio.c_cc[VDSUSP] = (char)-1; game_termio.c_cc[VREPRINT] = (char)-1; game_termio.c_cc[VDISCARD] = (char)-1; game_termio.c_cc[VWERASE] = (char)-1; game_termio.c_cc[VLNEXT] = (char)-1; game_termio.c_cc[VSTATUS] = (char)-1; #endif /* Normally, block until a character is read */ game_termio.c_cc[VMIN] = 1; game_termio.c_cc[VTIME] = 0; /* Hack -- Turn off "echo" and "canonical" mode */ game_termio.c_lflag &= ~(ECHO | ICANON); #endif #ifdef USE_TCHARS /* Get the default game characters */ (void)ioctl(0, TIOCGETP, (char *)&game_ttyb); (void)ioctl(0, TIOCGETC, (char *)&game_tchars); (void)ioctl(0, TIOCGLTC, (char *)&game_ltchars); (void)ioctl(0, TIOCLGET, (char *)&game_local_chars); /* Force interupt (^C) */ game_tchars.t_intrc = (char)3; /* Force start/stop (^Q, ^S) */ game_tchars.t_startc = (char)17; game_tchars.t_stopc = (char)19; /* Cancel some things */ game_tchars.t_quitc = (char)-1; game_tchars.t_eofc = (char)-1; game_tchars.t_brkc = (char)-1; /* Force suspend (^Z) */ game_ltchars.t_suspc = (char)26; /* Cancel some things */ game_ltchars.t_dsuspc = (char)-1; game_ltchars.t_rprntc = (char)-1; game_ltchars.t_flushc = (char)-1; game_ltchars.t_werasc = (char)-1; game_ltchars.t_lnextc = (char)-1; /* XXX XXX XXX XXX Verify this before use */ /* Hack -- Turn off "echo" and "canonical" mode */ /* game_termios.c_lflag &= ~(ECHO | ICANON); */ game_ttyb.flag &= ~(ECHO | ICANON); #endif } /* * Suspend/Resume */ static errr Term_xtra_cap_alive(int v) { /* Suspend */ if (!v) { if (!active) return (1); /* Hack -- make sure the cursor is visible */ curs_set(1); /* Move to bottom right */ do_move(0, rows - 1, 0, rows - 1); /* Go to normal keymap mode */ keymap_norm(); /* No longer active */ active = FALSE; } /* Resume */ else { if (active) return (1); /* Hack -- restore the cursor location */ do_move(curx, cury, curx, cury); /* Hack -- restore the cursor visibility */ curs_set(curv); /* Go to angband keymap mode */ keymap_game(); /* Now we are active */ active = TRUE; } /* Success */ return (0); } /* * Process an event */ static errr Term_xtra_cap_event(int v) { int i, arg; char buf[2]; /* Wait */ if (v) { /* Wait for one byte */ i = read(0, buf, 1); /* Hack -- Handle "errors" */ if ((i <= 0) && (errno != EINTR)) exit_game_panic(); } /* Do not wait */ else { /* Get the current flags for stdin */ if ((arg = fcntl(0, F_GETFL, 0)) < 1) return (1); /* Tell stdin not to block */ if (fcntl(0, F_SETFL, arg | O_NDELAY) < 0) return (1); /* Read one byte, if possible */ i = read(0, buf, 1); /* Replace the flags for stdin */ if (fcntl(0, F_SETFL, arg)) return (1); } /* No keys ready */ if ((i != 1) || (!buf[0])) return (1); /* Enqueue the keypress */ Term_keypress(buf[0]); /* Success */ return (0); } /* * Actually move the hardware cursor */ static errr Term_curs_cap(int x, int y) { /* Literally move the cursor */ do_move(curx, cury, x, y); /* Save the cursor location */ curx = x; cury = y; /* Success */ return (0); } /* * Erase a grid of space * * XXX XXX XXX Note that we will never be asked to clear the * bottom line all the way to the bottom right edge, since we * have set the "avoid the bottom right corner" flag. */ static errr Term_wipe_cap(int x, int y, int n) { int dx; /* Place the cursor */ Term_curs_cap(x, y); /* Wipe to end of line */ if (x + n >= 80) { do_ce(); } /* Wipe region */ else { for (dx = 0; dx < n; ++dx) { putc(' ', stdout); curx++; } } /* Success */ return (0); } /* * Place some text on the screen using an attribute */ static errr Term_text_cap(int x, int y, int n, byte a, cptr s) { int i; /* Move the cursor */ Term_curs_cap(x, y); /* Dump the text, advance the cursor */ for (i = 0; s[i]; i++) { /* Dump the char */ putc(s[i], stdout); /* Advance cursor 'X', and wrap */ if (++curx >= cols) { /* Reset cursor 'X' */ curx = 0; /* Hack -- Advance cursor 'Y', and wrap */ if (++cury == rows) cury = 0; } } /* Success */ return (0); } /* * Handle a "special request" */ static errr Term_xtra_cap(int n, int v) { /* Analyze the request */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: (void)write(1, "\007", 1); return (0); /* Change the cursor visibility */ case TERM_XTRA_SHAPE: curv = v; curs_set(v); return (0); /* Suspend/Resume */ case TERM_XTRA_ALIVE: return (Term_xtra_cap_alive(v)); /* Process events */ case TERM_XTRA_EVENT: return (Term_xtra_cap_event(v)); /* Flush events */ case TERM_XTRA_FLUSH: while (!Term_xtra_cap_event(FALSE)); return (0); /* Delay */ case TERM_XTRA_DELAY: if (v > 0) usleep(1000 * v); return (0); } /* Not parsed */ return (1); } /* * Init a "term" for this file */ static void Term_init_cap(term *t) { if (active) return; /* Assume cursor at top left */ curx = 0; cury = 0; /* Assume visible cursor */ curv = 1; /* Clear the screen */ do_cl(); /* Hack -- visible cursor */ curs_set(1); /* Assume active */ active = TRUE; } /* * Nuke a "term" for this file */ static void Term_nuke_cap(term *t) { if (!active) return; /* Hack -- make sure the cursor is visible */ curs_set(1); /* Move to bottom right */ do_move(0, rows - 1, 0, rows - 1); /* Normal keymap */ keymap_norm(); /* No longer active */ active = FALSE; } /* * Prepare this file for Angband usage */ errr init_cap(void) { term *t = &term_screen_body; /*** Initialize ***/ /* Initialize the screen */ if (init_cap_aux()) return (-1); /* Hack -- Require large screen, or Quit with message */ if ((rows < 24) || (cols < 80)) quit("Screen too small!"); /*** Prepare to play ***/ /* Extract the normal keymap */ keymap_norm_prepare(); /* Extract the game keymap */ keymap_game_prepare(); /* Hack -- activate the game keymap */ keymap_game(); /* Hack -- Do NOT buffer stdout */ setbuf(stdout, NULL); /*** Now prepare the term ***/ /* Initialize the term */ term_init(t, cols, rows, 256); /* Avoid the bottom right corner */ t->icky_corner = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Set some hooks */ t->init_hook = Term_init_cap; t->nuke_hook = Term_nuke_cap; /* Set some more hooks */ t->text_hook = Term_text_cap; t->wipe_hook = Term_wipe_cap; t->curs_hook = Term_curs_cap; t->xtra_hook = Term_xtra_cap; /* Save the term */ term_screen = t; /* Activate it */ Term_activate(term_screen); /* Success */ return (0); } #endif /* USE_CAP */ zangband/src/main-crb.c0000644000000000000000000042226010250356274013775 0ustar rootroot/* File: main-crb.c */ /* * Copyright (c) 1997 Ben Harrison, Keith Randall, Peter Ammon, Ron Anderson * and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with Macintosh computers running OS X, * or OS 8/9 with CarbonLib system extention. * * To use this file, use an appropriate "Makefile" or "Project File", which * should define "MACINTOSH" in case of PEF Carbon compilation on CodeWarrior * or MPW, and "MACH_O_CARBON" for OS X Developer CD gcc. Please note that * defining "MACINTOSH" for the latter will result in completely broken * binary, mostly because of different pathname conventions. * * The official compilation uses the CodeWarrior Pro compiler. * * If you are never going to use "graphics" (especially if you are not * compiling support for graphics anyway) then you can delete the "pict" * resources with id "1001", "1002", "1003" and "1004" with no dangerous * side effects. * * * This file assumes that you will be using a PPC Mac running OS X * or OS 8/9 (8.6 or greater) with CarbonLib system extention enabled. * In fact, the game will refuse to run unless these features are available. * * MACH_O_CARBON code pushes the system requirement a bit further, and * I don't think it works on System 8, even with CarbonLib, because it uses * the Bundle services, but I may be wrong. * * Note that the "preference" file is now a simple XML text file * called ".plist" in case of PEF Carbon, and ".plist" for Mach-O Carbon, which contains * key-value paris, so it no longer has to check version stamp to validate * its contents. * * * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c" * should probably be "unloaded" as soon as they are no longer needed, * to save space, but I do not know how to do this. XXX XXX XXX * * Stange bug -- The first "ClipRect()" call crashes if the user closes * all the windows, switches to another application, switches back, and * re-opens the main window, for example, using "command-a". XXX XXX XXX * * * Initial framework (and most code) by Ben Harrison (benh@phial.com). * * Some code adapted from "MacAngband 2.6.1" by Keith Randall * * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl). * * Most Apple Event code provided by Steve Linberg (slinberg@crocker.com). * * Most of the graphics code is adapted from an extremely minimal subset of * the "Sprite World II" package, an amazing (and free) animation package. * * Carbon code adapted from works by Peter Ammon and Ron Anderson. * * (List of changes made by "pelpel" follow) * Some API calls are updated to OS 8.x-- ones. * * Pixmap locking code in Term_pict_mac() follows Carbon Porting Guide * by Apple. * * The idle loop in TERM_XTRA_DELAY is rewritten to sleep on WaitNextEvent * for a couple of reasons. * * CheckEvent now really blocks whenever asked to wait. * * The unused buffer GWorld is completely removed. It has long been pure waste * of memory. * * The default font-size combination was changed because the old one, Monaco * at 12 points causes the redraw artefact problem on OS X. * * Characters in the ASCII mode are clipped by their bounding rects to reduce * redraw artefacts that were quite annoying in certain font-point combos. * * Transparency effect now avoids double bitblts whenever possible. * * Old tiles were drawn in a wrong fashion by the USE_TRANSPARENCY code. * * ASCII and the two graphics modes are now controlled by single graf_mode * variable. arg_* and use_* variables are set when requested mode is * successfully initialised. * * Most of the menus are now loaded from resources. * * Moved TileWidth and TileHeight menus into Special. There were too many menus. * * Added support for 32x32 tiles, now for [V] only. * * Related to the above, globe_init no longer loads tile images twice if * a tileset doesn't have corresponding masks. * * Added support for POSIX-style pathnames, for Mach-O Carbon (gcc, CW >= 7). * We can finally live without Pascal strings to handle files this way. * * (Mach-O Carbon) Graphics tiles are moved out of the resource fork into * bundle-based data fork files. * * Changed size-related menu code, because they no longer function because * some APIs have been changed to return Unicode in some cases. * * Changed the transparency code again, this time using Ron Anderson's code, * which makes more sound assumption about background colour and is more * efficient. * * The old asynchronous sound player could try to lock the same handle more * than once, load same sound resource already in use, or unlock and release * currently playing sound. * * hook_quit() now releases memory-related resources dynamically allocated by * the graphics and sound code. * * Added support for yet another required Apple Event, reopen application. * * Changed the way the main game code is called from the file. It's now * entered from a single point in main(). This makes do_menu_file_new, * do_menu_file_open and handle_open_when_ready much less hackish, but * slightly complicates main(). * * Introduced some "common sense" practices of Macintosh programs: * - the update event handler now respects update regions at last; * - removed validating/invalidating that became superfluous; and * - the event handlers makes use of Window's refcon to find term_data. * * Important Resources in the resource file: * * FREF 130 = ANGBAND_CREATOR / 'APPL' (application) * FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file) * FREF 130 = ANGBAND_CREATOR / 'TEXT' (bone file, generic text file) * FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file) * * DLOG 128 = "About Angband..." * * ALRT 128 = unused (?) * ALRT 129 = "Warning..." * * DITL 128 = body for DLOG 128 * DITL 129 = body for ALRT 129 * DITL 130 = body for ALRT 130 * * ICON 128 = "warning" icon * * MBAR 128 = array of MENU id's (128, 129, 130, 131, 132, 133, 134) * MENU 128 = apple (about, -, ...) * MENU 129 = File (new, open, close, save, -, score, quit) * MENU 130 = Edit (undo, -, cut, copy, paste, clear) * MENU 131 = Font (bold, wide, -) * MENU 132 = Size () * MENU 133 = Windows () * MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle, * Wizard) * Graphics have following submenu attached: * MENU 144 = Graphics (None, 8x8, 16x16, 32x32, enlarge tiles) * TileWidth and TileHeight submenus are filled in by this program. * MENU 145 = TileWidth () * MENU 146 = TileHeight () * * On CFM(PEF) Carbon only: * PICT 1001 = Graphics tile set (8x8) * PICT 1002 = Graphics tile set (16x16 images) * PICT 1004 = Graphics tile set (32x32) * * Mach-O Carbon now uses data fork resources: * 8x8.png = Graphics tile set (8x8) * 16x16.png = Graphics tile set (16x16 images) * 32x32.png = Graphics tile set (32x32) * These files should go into the Resources subdirectory of an application * bundle. * * STR# 128 = "Please select the "lib" folder" * * plst 0 can be empty, but required for single binary Carbon apps on OS X * Isn't necessary for Mach-O Carbon. * * * File name patterns: * all 'APEX' files have a filename of the form "*:apex:*" (?) * all 'BONE' files have a filename of the form "*:bone:*" (?) * all 'DATA' files have a filename of the form "*:data:*" * all 'SAVE' files have a filename of the form "*:save:*" * all 'USER' files have a filename of the form "*:user:*" (?) * * Perhaps we should attempt to set the "_ftype" flag inside this file, * to avoid nasty file type information being spread all through the * rest of the code. (?) This might require adding hooks into the * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX * * * Reasons for each header file: * * angband.h = Angband header file * * Types.h = (included anyway) * Gestalt.h = gestalt code * QuickDraw.h = (included anyway) * OSUtils.h = (included anyway) * Files.h = file code * Fonts.h = font code * Menus.h = menu code * Dialogs.h = dialog code * Windows.h = (included anyway) * Palettes.h = palette code * ToolUtils.h = HiWord() / LoWord() * Events.h = event code * Resources.h = resource code * Controls.h = button code * Processes.h = GetProcessInformation(), ExitToShell() * Memory.h = NewPtr(), etc * QDOffscreen.h = GWorld code * Sound.h = Sound code * Navigation.h = save file / lib locating dialogues * CFPreferences.h = Preferences * CFNumber.h = read/write short values from/to preferences */ /* * Yet another main-xxx.c for Carbon (pelpel) - revision 12 * * Since I'm using CodeWarrior, the traditional header files are * #include'd below. * * I also compiled Angband 3.0.2 successfully with OS X's gcc. * Please follow these instructions if you are interested. * * ---(developer CD gcc + makefile porting notes, for Angband 3.0.2)------- * 1. Compiling the binary * * If you try this on OS X + gcc, please use makefile.std, replacing * main.c and main.o with main-crb.c and main-crb.o, removing all main-xxx.c * and main-xxx.o from SRCS and OBJS, and, and use these settings: * * COPTS = -Wall -O1 -g -fpascal-strings * INCLUDES = * DEFINES = -DMACH_O_CARBON -DANGBAND30X * LIBS = -framework CoreFoundation -framework QuickTime -framework Carbon * * -DANGBAND30X only affects main-crb.c. This is because I'm also compiling * a couple of variants, and this arrangement makes my life easier. * * Never, ever #define MACINTOSH. It'll wreck havoc in system interface * (mostly because of totally different pathname convention). * * You might wish to disable some SET_UID features for various reasons: * to have user folder within the lib folder, savefile names etc. * * For the best compatibility with the Classic ports and my PEF Carbon * ports, my_fopen, fd_make and fd_open [in util.c] should call * (void)fsetfileinfo(buf, _fcreator, _ftype); * when a file is successfully opened. Or you'll see odd icons for some files * in the lib folder. In order to do so, extern.h should contain these lines, * within #ifdef MACH_O_CARBON: * extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype); * extern u32b _fcreator; * extern u32b _ftype; * And enable the four FILE_TYPE macros in h-config.h for defined(MACH_O_CARBON) * in addition to defined(MACINTOSH) && !defined(applec), i.e. * #if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON) * * This is a very good way to spot bugs in use of these macros, btw. * * 2. Installation * * The "angband" binary must be arranged this way for it to work: * * lib/ <- the lib folder * Angband (OS X).app/ * Contents/ * MacOS/ * angband <- the binary you've just compiled * Info.plist <- to be explained below * Resources/ * Angband.icns * Data.icns * Edit.icns * Save.icns * 8x8.png <- 8x8 tiles * 16x16.png <- 16x16 tiles * angband.rsrc <- see below * * 3. Preparing Info.plist * * Info.plist is an XML file describing some attributes of an application, * and this is appropriate for Angband: * * * * * CFBundleNameAngband * CFBundleDisplayNameAngband (OS X) * CFBundleExecutableangband * CFBundlePackageTypeAPPL * CFBundleSignatureA271 * CFBundleVersion3.0.2 * CFBundleShortVersionString3.0.2 * CFBundleIconFileAngband * CFBundleIdentifiernet.thangorodrim.Angband * CFBundleInfoDictionaryVersion6.0 * CFBundleDocumentTypes * * * CFBundleTypeExtentions* * CFBundleTypeIconFileSave * CFBundleTypeNameAngband saved game * CFBundleTypeOSTypesSAVE * CFBundleTypeRoleEditor * * * CFBundleTypeExtentions* * CFBundleTypeIconFileEdit * CFBundleTypeNameAngband game data * CFBundleTypeOSTypesTEXT * CFBundleTypeRoleEditor * * * CFBundleTypeExtentionsraw * CFBundleTypeIconFileData * CFBundleTypeNameAngband game data * CFBundleTypeOSTypesDATA * CFBundleTypeRoleEditor * * * * * * 4. Menu, diaglogue and gfx resources * * The binary assumes angband.rsrc should be in the traditional resource * mangager format. Please run this command to create it from its textual * description: * * Rez -i /Developer/Headers/FlatCarbon -d MACH_O -o angband.rsrc Angband.r * * The command is in /Developer/Tools. You might wish to include it in your * PATH. * * It's better to comment out the definitions of BNDL and plst resources * before you do that. I think you can DeRez the resulting angband.rsrc and * feed it to the Interface Builder to produce a set of compatible .nib files, * but this file also needs to be updated to understand .nib... On the other * hand, I really don't like to hardcode UI definitions in C. * * Graphics resources are moved out of the resource fork and become ordinary * PNG files. Make sure to set its resolution to 72 dpi (<- VERY important) * while keeping vertical and horizontal scaling factor to 100% (<- VERY * important), when you convert tiles in any formats to PNG. This means * that the real size of an image must shrink or grow when you change it's dpi. * * Sound resources are a bit more complicated. * The easiest way is: * 1) Grab recent Mac Angband binary. * 2) Run this command: * DeRez -only 'snd ' (Angband binary) > sound.r * 3) And specify sound.r files in addition to Angband.r when you run Rez. * * ---(end of OS X + gcc porting note)-------------------------------------- * * Code adapted from Peter Ammon's work on 2.8.3 and some modifications * are made when Apple's Carbon Porting Guide says they are absolutely * necessary. Other arbirary changes are mostly because of my hatred * of deep nestings and indentations. The code for controlling graphics modes * have been thoroughly revised simply because I didn't like it (^ ^;). * A bonus of this is that graphics settings can be loaded from Preferences * quite easily. * * I also took Ron Anderson's (minimising the use of local-global coordinate * conversions). Some might say his QuickTime multimedia is the most * significant achievement... Play your favourite CD instead, if you really * miss that (^ ^;) I might consider incorporating it if it makes use of * event notification. * * I replaced some old API calls with new (OS 8.x--) ones, especially * when I felt Apple is strongly against their continued usage. * * Similarly, USE_SFL_CODE (AppleEvent code by Steve Linberg) should be * always active, so I removed ifdef's just to prevent accidents, as well as * to make the code a bit cleaner. * * On the contrary, I deliberately left traditional resource interfaces. * Whatever Apple might say, I abhor file name extentions. And keeping two * different sets of resources for Classic and Carbon is just too much for * a personal project XXX * * Because Carbon forbids the use of 68K code, ANGBAND_LITE_MAC sections * are removed. * * Because the default font-size combination causes redraw artefact problem * (some characters, even in monospace fonts, have negative left bearings), * I introduced rather crude hack to clip all character drawings within * their bounding rects. If you don't like this, please comment out the line * #define CLIP_HACK * below. The alternative, #define OVERWRITE_HACK, is based on Julian Lighton's * brilliant suggestion, but it doesn't work as expected. This is because * DrawText can render the same character with _different_ pixel width, * depending on relative position of a character to the pen. Fonts do look * very nice on the Mac, but too nice I'd say, in case of Angband. * * The check for return values of AEProcessAppleEvent is removed, * because it results in annoying dialogues on OS X, also because * Apple says it isn't usually necessary. * * Because the always_pict code is *so* slow, I changed the graphics * mode selection a bit to use higher_pict when a user chooses a fixed * width font and doesn't changes tile width / height. * * Added support for David Gervais' 32x32 tiles. * * Replaced transparency effect code by Ron Anderson's. * * Added support for gcc & make compilation. They come free with OS X * (on the developer CD). This means that it can be compiled as a bundle. * * For Mach-O Carbon binary, moved graphics tiles out of the * resource fork and made them plain PNG files, to be stored in the application * bundle's "Resources" subdirectory. * * For Mach-O Carbon binary, provided a compile-time option (USE_QT_SOUND) to * move sound effect samples out of the resource fork and use *.wav files in * the bundle's "Resources" subdirectory. The "*" part must match the names in * angband_sound_name (variable.c) exactly. This doesn't hurt performance * a lot in [V] (it's got ~25 sound events), but problematic in [Z]-based * ones (they have somewhere around 70 sound events). * * You still can use the resource file that comes with the ext-mac archive * on the Angband FTP server, with these additions: * - MENUs 131--134 and 144--146, as described above * - MBAR 128: just a array of 128 through 134 * - plst 0 : can be empty, although Apple recommends us to fill it in. * - STR# 128 : something like "Please select your lib folder" * * Since this involves considerable amount of work, I attached * a plain text resource definition (= Rez format) . * I heavily commented on the file, hoping it could be easily adapted * to future versions of Angband as well as variants. * I omitted sound effects and graphic tiles to make it reasonably small. * Please copy them from any recent Mac binaries - you can use, say ZAngband * ones for Vanilla or [V]-based variants quite safely. T.o.M.E. uses fairly * extended 16x16 tileset and it is maintained actively. IIRC Thangorodrim * compile page has an intruction explaining how to convert tiles for * use on the Mac... It can be tricky, depending on your choice of * graphics utility. Remember setting resolution to 72 pixels per inch, * while keeping vertical/horizontal scale factor to 100% and dump the * result as a PICT from resource. * * To build Carbonised Angband with CodeWarrior, copy your PPC project * and * - replace main-mac.c in the project with this file (in the link order tab) * - remove InterfaceLib and MathLib * - add CarbonLib (found in Carbon SDK or CW's UniversalInterfaces) -- * if you have compiler/linker errors, you'll need Carbon SDK 1.1 or greater * - replace MSL C.PPC.Lib with MSL C.Carbon.Lib (both found in * MSL:MSL_C:MSL_MacOS:Lib:PPC) * - leave MSL RuntimePPC.Lib as it is * - don't forget to update resource file, as described above * - as in Classic targets, you may have to include and * . The most convinient place for them is the first * #ifdef MACINTOSH in h-system.h * - check variant dependent ifdef's explained below, and add * appropriate one(s) in your A-mac-h.pch. */ /* * Force Carbon-compatible APIs */ #ifndef MACH_O_CARBON # ifndef TARGET_API_MAC_CARBON /* Can be CodeWarrior or MPW */ # define TARGET_API_MAC_CARBON 1 # endif #else /* * Must be Mach-O Carbon target with OS X gcc. * No need to set TARGET_API_MAC_CARBON to 1 here, but I assume it should * be able to make efficient use of BSD functions, hence: */ # define USE_MALLOC /* Not yet */ /* # define USE_NIB */ #endif /* !MACH_O_CARBON */ #include "angband.h" #if defined(MACINTOSH) || defined(MACH_O_CARBON) /* * Variant-dependent features: * * #define ALLOW_BIG_SCREEN (V, Ey, O, and Z. Dr's big screen needs * more work. New S one is too idiosyncratic...) * #define ANG281_RESET_VISUALS (Cth, Gum, Z) * #define ZANG_AUTO_SAVE (O and Z) * #define HAS_SCORE_MENU (V and maybe more) * #define ANGBAND_CREATOR four letter code for your variant, if any. * or use the default one. * * For [Z], you also have to say -- #define inkey_flag (p_ptr->inkey_flag) * but before that, please, please consider using main-mac-carbon.c in [Z], * that has some interesting features. */ /* ZAngband 2.7.x characteristics */ #define ALLOW_BIG_SCREEN #define NEW_ZVIRT_HOOKS #define ANG281_RESET_VISUALS #define ZANG_AUTO_SAVE /* I can't ditch these, yet, because there are many variants */ #define USE_TRANSPARENCY #undef DEBUG /* Default creator signature */ #ifndef ANGBAND_CREATOR # define ANGBAND_CREATOR 'A271' #endif /* * Use rewritten asynchronous sound player */ #define USE_ASYNC_SOUND /* * A rather crude fix to reduce amount of redraw artefacts. * Some fixed width fonts (i.e. Monaco) has characters with negative * left bearings, so Term_wipe_mac or overwriting cannot completely * erase them. This could be introduced to Classic Mac OS ports too, * but since I've never heard any complaints and I don't like to * make 68K ports even slower, I won't do so there. */ #define CLIP_HACK /* */ /* * Another redraw artifact killer, based on suggestion by Julian Lighton */ /* #define OVERWRITE_HACK */ /* These hacks can co-exist, but I don't like overkill */ #ifdef OVERWRITE_HACK # ifdef CLIP_HACK # undef CLIP_HACK # endif #endif /* * In OS X + gcc, use , and * for ALL of these, including the Apple * Event ones. is used by the tile loading code. */ #ifdef MACH_O_CARBON #include #include #include #include #else /* MACH_O_CARBON */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include # ifdef MAC_MPW # include # endif #endif /* MACH_O_CARBON */ /* * Use "malloc()" instead of "NewPtr()" */ /* #define USE_MALLOC */ /* * Information about each of the 256 available colors */ static RGBColor color_info[256]; #if defined(MACH_O_CARBON) || defined(MAC_MPW) /* * Creator signature and file type - Didn't I say that I abhor file name * extentions? Names and metadata are entirely different set of notions. */ OSType _fcreator; OSType _ftype; #endif /* MACH_O_CARBON || MAC_MPW */ /* * Forward declare */ typedef struct term_data term_data; /* * Extra "term" data */ struct term_data { term *t; Rect r; WindowPtr w; short padding; short pixelDepth; /* GWorldPtr theGWorld; */ GDHandle theGDH; /* GDHandle mainSWGDH; */ Str15 title; s16b oops; s16b keys; s16b last; s16b mapped; s16b rows; s16b cols; s16b font_id; s16b font_size; s16b font_face; s16b font_mono; s16b font_o_x; s16b font_o_y; s16b font_wid; s16b font_hgt; s16b tile_o_x; s16b tile_o_y; s16b tile_wid; s16b tile_hgt; s16b size_wid; s16b size_hgt; s16b size_ow1; s16b size_oh1; s16b size_ow2; s16b size_oh2; }; /* * Forward declare -- see below */ static bool CheckEvents(int wait); /* * Available values for 'wait' */ #define CHECK_EVENTS_DRAIN -1 #define CHECK_EVENTS_NO_WAIT 0 #define CHECK_EVENTS_WAIT 1 #ifndef MACH_O_CARBON /* * Hack -- location of the main directory */ static short app_vol; static long app_dir; #endif /* !MACH_O_CARBON */ /* * Delay handling of double-clicked savefiles */ Boolean open_when_ready = FALSE; /* * Delay handling of pre-emptive "quit" event */ Boolean quit_when_ready = FALSE; /* * Aqua automatically supplies the Quit menu. */ static Boolean is_aqua = FALSE; /* * Version of Mac OS - for version specific bug workarounds (; ;) */ static long mac_os_version; /* * Hack -- game in progress */ static Boolean game_in_progress = FALSE; /* * Indicate if the user chooses "new" to start a game */ static Boolean new_game = FALSE; /* * Only do "SetPort()" when needed */ static WindowPtr active = NULL; /* * Maximum number of terms */ #define MAX_TERM_DATA 8 /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Note when "open"/"new" become valid */ static bool initialized = FALSE; #ifdef MACH_O_CARBON /* Carbon File Manager utilities by pelpel */ /* * (Carbon) * Convert a pathname to a corresponding FSSpec. * Returns noErr on success. */ static OSErr path_to_spec(const char *path, FSSpec *spec) { OSErr err; FSRef ref; /* Convert pathname to FSRef ... */ err = FSPathMakeRef(path, &ref, NULL); if (err != noErr) return (err); /* ... then FSRef to FSSpec */ err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL); /* Inform caller of success or failure */ return (err); } /* * (Carbon) * Convert a FSSpec to a corresponding pathname. * Returns noErr on success. */ static OSErr spec_to_path(const FSSpec *spec, char *buf, size_t size) { OSErr err; FSRef ref; /* Convert FSSpec to FSRef ... */ err = FSpMakeFSRef(spec, &ref); if (err != noErr) return (err); /* ... then FSRef to pathname */ err = FSRefMakePath(&ref, buf, size); /* Inform caller of success or failure */ return (err); } /* * (Carbon) [via path_to_spec] * Set creator and filetype of a file specified by POSIX-style pathname. * Returns 0 on success, -1 in case of errors. */ void fsetfileinfo(cptr pathname, OSType fcreator, OSType ftype) { OSErr err; FSSpec spec; FInfo info; /* Convert pathname to FSSpec */ if (path_to_spec(pathname, &spec) != noErr) return; /* Obtain current finder info of the file */ if (FSpGetFInfo(&spec, &info) != noErr) return; /* Overwrite creator and type */ info.fdCreator = fcreator; info.fdType = ftype; err = FSpSetFInfo(&spec, &info); /* Done */ return; } #else /* MACH_O_CARBON */ /* * Convert a pascal string in place * * This function may be defined elsewhere, but since it is so * small, it is not worth finding the proper function name for * all the different platforms. */ static void ptocstr(StringPtr src) { int i; /* Hack -- pointer */ char *s = (char*)(src); /* Hack -- convert the string */ for (i = s[0]; i; i--, s++) s[0] = s[1]; /* Hack -- terminate the string */ s[0] = '\0'; } /* * Utility routines by Steve Linberg * * The following three routines (pstrcat, pstrinsert, and PathNameFromDirID) * were taken from the Think Reference section called "Getting a Full Pathname" * (under the File Manager section). We need PathNameFromDirID to get the * full pathname of the opened savefile, making no assumptions about where it * is. * * I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully * nice. */ static void pstrcat(StringPtr dst, StringPtr src) { /* copy string in */ BlockMove(src + 1, dst + *dst + 1, *src); /* adjust length byte */ *dst += *src; } /* * pstrinsert - insert string 'src' at beginning of string 'dst' */ static void pstrinsert(StringPtr dst, StringPtr src) { /* make room for new string */ BlockMove(dst + 1, dst + *src + 1, *dst); /* copy new string in */ BlockMove(src + 1, dst + 1, *src); /* adjust length byte */ *dst += *src; } static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName) { CInfoPBRec block; Str255 directoryName; OSErr err; fullPathName[0] = '\0'; block.dirInfo.ioDrParID = dirID; block.dirInfo.ioNamePtr = directoryName; while (1) { block.dirInfo.ioVRefNum = vRefNum; block.dirInfo.ioFDirIndex = -1; block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID; err = PBGetCatInfoSync(&block); pstrcat(directoryName, (StringPtr)"\p:"); pstrinsert(fullPathName, directoryName); if (block.dirInfo.ioDrDirID == 2) break; } } /* * Rewritten to use PathNameFromDirID -- pelpel * * Convert refnum+vrefnum+fname into a full file name * Store this filename in 'buf' (make sure it is long enough) * Note that 'fname' looks to be a "pascal" string */ static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname) { /* Convert directory & volume reference numbers to an absolute path */ PathNameFromDirID(refnum, vrefnum, (unsigned char *)buf); /* Append file name to the path */ pstrcat((unsigned char *)buf, (unsigned char *)fname); /* Convert the result into a C string */ ptocstr((unsigned char *)buf); } #endif /* MACH_O_CARBON */ #ifdef MAC_MPW /* * Convert pathname to an appropriate format, because MPW's * CarbonStdCLib chose to use system's native path format, * making our lives harder to create binaries that run on * OS 8/9 and OS X :( -- pelpel */ void convert_pathname(char* path) { char buf[1024]; /* Nothing has to be done for CarbonLib on Classic */ if (mac_os_version >= 0x1000) { /* Convert to POSIX style */ ConvertHFSPathToUnixPath(path, buf); /* Copy the result back */ strcpy(path, buf); } /* Done. */ return; } # ifdef CHECK_MODIFICATION_TIME /* * Although there is no easy way to emulate fstat in the old interface, * we still can do stat-like things, because Mac OS is an OS. */ static int get_modification_time(cptr path, u32b *mod_time) { CInfoPBRec pb; Str255 pathname; int i; /* Paranoia - make sure the pathname fits in Str255 */ i = strlen(path); if (i > 255) return (-1); /* Convert pathname to a Pascal string */ strncpy((char *)pathname + 1, path, 255); pathname[0] = i; /* Set up parameter block */ pb.hFileInfo.ioNamePtr = pathname; pb.hFileInfo.ioFDirIndex = 0; pb.hFileInfo.ioVRefNum = app_vol; pb.hFileInfo.ioDirID = 0; /* Get catalog information of the file */ if (PBGetCatInfoSync(&pb) != noErr) return (-1); /* Set modification date and time */ *mod_time = pb.hFileInfo.ioFlMdDat; /* Success */ return (0); } /* * A (non-Mach-O) Mac OS version of check_modification_time, for those * compilers without good enough POSIX-compatibility libraries XXX XXX */ errr check_modification_date(int fd, cptr template_file) { #pragma unused(fd) u32b txt_stat, raw_stat; char *p; char fname[32]; char buf[1024]; /* Build the file name */ path_build(buf, sizeof(buf), ANGBAND_DIR_EDIT, template_file); /* XXX XXX XXX */ convert_pathname(buf); /* Obtain modification time */ if (get_modification_time(buf, &txt_stat)) return (-1); /* XXX Build filename of the corresponding *.raw file */ strnfmt(fname, sizeof(fname), "%s", template_file); /* Find last '.' */ p = strrchr(fname, '.'); /* Can't happen */ if (p == NULL) return (-1); /* Substitute ".raw" for ".txt" */ strcpy(p, ".raw"); /* Build the file name of the raw file */ path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, fname); /* XXX XXX XXX */ convert_pathname(buf); /* Obtain modification time */ if (get_modification_time(buf, &raw_stat)) return (-1); /* Ensure the text file is not newer than the raw file */ if (txt_stat > raw_stat) return (-1); /* Keep using the current .raw file */ return (0); } # endif /* CHECK_MODIFICATION_TIME */ #endif /* MAC_MPW */ /* * Center a rectangle inside another rectangle * * Consider using RepositionWindow() whenever possible */ static void center_rect(Rect *r, Rect *s) { int centerx = (s->left + s->right)/2; int centery = (2*s->top + s->bottom)/3; int dx = centerx - (r->right - r->left)/2 - r->left; int dy = centery - (r->bottom - r->top)/2 - r->top; r->left += dx; r->right += dx; r->top += dy; r->bottom += dy; } /* * Activate a given window, if necessary */ static void activate(WindowPtr w) { /* Activate */ if (active != w) { /* Activate */ if (w) SetPort(GetWindowPort(w)); /* Remember */ active = w; } } /* * Display a warning message */ static void mac_warning(cptr warning) { Str255 text; int len, i; /* Limit of 250 chars */ len = strlen(warning); if (len > 250) len = 250; /* Make a "Pascal" string */ text[0] = len; for (i=0; ilast != a) { /* Activate the color */ RGBForeColor(&color_info[a]); /* Memorize color */ td->last = a; } } /* * Hack -- Apply and Verify the "font" info * * This should usually be followed by "term_data_check_size()" * * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt * you have to reset them to zero before this function is called. * XXX XXX This is automatic when the program starts because the term_data * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e. * font, font style and size changes. */ static void term_data_check_font(term_data *td) { int i; FontInfo info; WindowPtr old = active; /* Activate */ activate(td->w); /* Instantiate font */ TextFont(td->font_id); TextSize(td->font_size); TextFace(td->font_face); /* Extract the font info */ GetFontInfo(&info); /* Assume monospaced */ td->font_mono = TRUE; /* Extract the font sizing values XXX XXX XXX */ td->font_wid = CharWidth('@'); /* info.widMax; */ td->font_hgt = info.ascent + info.descent; td->font_o_x = 0; td->font_o_y = info.ascent; /* Check important characters */ for (i = 33; i < 127; i++) { /* Hack -- notice non-mono-space */ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE; /* Hack -- collect largest width */ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i); } /* Set default offsets */ td->tile_o_x = td->font_o_x; td->tile_o_y = td->font_o_y; /* Set default tile size */ if (td->tile_wid == 0) td->tile_wid = td->font_wid; if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt; /* Re-activate the old window */ activate(old); } /* * Hack -- Apply and Verify the "size" info */ static void term_data_check_size(term_data *td) { if (td == &data[0]) { #ifndef ALLOW_BIG_SCREEN /* Forbid resizing of the Angband window */ td->cols = 80; td->rows = 24; #else /* Enforce minimal size */ if (td->cols < 80) td->cols = 80; if (td->rows < 24) td->rows = 24; #endif /* !ALLOW_BIG_SCREEN */ } /* Information windows can be much smaller */ else { if (td->cols < 1) td->cols = 1; if (td->rows < 1) td->rows = 1; } /* Enforce maximal sizes */ if (td->cols > 255) td->cols = 255; if (td->rows > 255) td->rows = 255; /* Minimal tile size */ if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid; if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt; /* Default tile offsets */ td->tile_o_x = (td->tile_wid - td->font_wid) / 2; td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2; /* Minimal tile offsets */ if (td->tile_o_x < 0) td->tile_o_x = 0; if (td->tile_o_y < 0) td->tile_o_y = 0; /* Apply font offsets */ td->tile_o_x += td->font_o_x; td->tile_o_y += td->font_o_y; /* Calculate full window size */ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2; td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2; { BitMap tScreen; /* Get current screen */ (void)GetQDGlobalsScreenBits(&tScreen); /* Verify the bottom */ if (td->r.top > tScreen.bounds.bottom - td->size_hgt) { td->r.top = tScreen.bounds.bottom - td->size_hgt; } /* Verify the top */ if (td->r.top < tScreen.bounds.top + GetMBarHeight()) { td->r.top = tScreen.bounds.top + GetMBarHeight(); } /* Verify the right */ if (td->r.left > tScreen.bounds.right - td->size_wid) { td->r.left = tScreen.bounds.right - td->size_wid; } /* Verify the left */ if (td->r.left < tScreen.bounds.left) { td->r.left = tScreen.bounds.left; } } /* Calculate bottom right corner */ td->r.right = td->r.left + td->size_wid; td->r.bottom = td->r.top + td->size_hgt; /* Assume no graphics */ td->t->higher_pict = FALSE; td->t->always_pict = FALSE; /* Handle graphics */ if (use_graphics) { /* Use higher pict whenever possible */ if (td->font_mono) td->t->higher_pict = TRUE; /* Use always_pict only when necessary */ else td->t->always_pict = TRUE; } /* Fake mono-space */ if (!td->font_mono || (td->font_wid != td->tile_wid) || (td->font_hgt != td->tile_hgt)) { /* * Handle fake monospace * * pelpel: This is SLOW. Couldn't we use CharExtra * and SpaceExtra for monospaced fonts? */ if (td->t->higher_pict) td->t->higher_pict = FALSE; td->t->always_pict = TRUE; } } /* * Hack -- resize a term_data * * This should normally be followed by "term_data_redraw()" */ static void term_data_resize(term_data *td) { /* * Actually resize the window * * ResizeWindow is the preferred API call, but it cannot * be used here. */ SizeWindow(td->w, td->size_wid, td->size_hgt, 0); } /* * Hack -- redraw a term_data * * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR" */ static void term_data_redraw(term_data *td) { term *old = Term; /* Activate the term */ Term_activate(td->t); /* Redraw the contents */ Term_redraw(); /* Flush the output */ Term_fresh(); /* Restore the old term */ Term_activate(old); } /* * Graphics support */ /* * PICT id / file name of image tiles */ #ifdef MACH_O_CARBON static CFStringRef pict_id = NULL; #else static int pict_id = 0; #endif /* MACH_O_CARBON */ /* * Width and height of a tile in pixels */ static int graf_width = 0; static int graf_height = 0; /* * Numbers of rows and columns in a tileset, * calculated by the PICT/PNG loading code */ static int pict_cols = 0; static int pict_rows = 0; /* * Available graphics modes */ #define GRAF_MODE_NONE 0 /* plain ASCII */ #define GRAF_MODE_8X8 1 /* 8x8 tiles */ #define GRAF_MODE_16X16 2 /* 16x16 tiles */ #define GRAF_MODE_32X32 3 /* 32x32 tiles */ /* * Current and requested graphics modes */ static int graf_mode = GRAF_MODE_NONE; static int graf_mode_req = GRAF_MODE_NONE; /* * Available transparency effect modes: * TR_NONE - no transparency effects * TR_OVER - Overwriting with transparent black pixels * TR_OVER only works with 256 colour (8-bit) images if this file * is compiled to produce a PEF Carbon binary, while on Mach-O Carbon * it can handle much deeper pixels (verified with 16-bit and * 24-bit ones). */ #define TR_NONE 0 #define TR_OVER 1 /* * Current transparency effect mode */ static int transparency_mode = TR_NONE; /* * Forward Declare */ typedef struct FrameRec FrameRec; /* * Frame * * - GWorld for the frame image * - Handle to pix map (saved for unlocking/locking) * - Pointer to color pix map (valid only while locked) */ struct FrameRec { GWorldPtr framePort; PixMapHandle framePixHndl; PixMapPtr framePix; }; /* * The global picture data */ static FrameRec *frameP = NULL; /* * Lock a frame */ static void BenSWLockFrame(FrameRec *srcFrameP) { PixMapHandle pixMapH; pixMapH = GetGWorldPixMap(srcFrameP->framePort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->framePixHndl = pixMapH; srcFrameP->framePix = (PixMapPtr)(*(Handle)pixMapH); } /* * Unlock a frame */ static void BenSWUnlockFrame(FrameRec *srcFrameP) { if (srcFrameP->framePort != NULL) { HUnlock((Handle)srcFrameP->framePixHndl); UnlockPixels(srcFrameP->framePixHndl); } srcFrameP->framePix = NULL; } #ifdef MACH_O_CARBON /* * Moving graphics resources into data fork -- pelpel * * (Carbon, Bundle) * Given base and type names of a resource, find a file in the * current application bundle and return its FSSpec in the third argument. * Returns true on success, false otherwise. * e.g. get_resource_spec(CFSTR("8x8"), CFSTR("png"), &spec); */ static Boolean get_resource_spec( CFStringRef base_name, CFStringRef type_name, FSSpec *spec) { CFURLRef res_url; FSRef ref; /* Find resource (=file) in the current bundle */ res_url = CFBundleCopyResourceURL( CFBundleGetMainBundle(), base_name, type_name, NULL); /* Oops */ if (res_url == NULL) return (false); /* Convert CFURL to FSRef */ (void)CFURLGetFSRef(res_url, &ref); /* Convert FSRef to FSSpec */ (void)FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL); /* Free allocated CF data */ CFRelease(res_url); /* Success */ return (true); } /* * (QuickTime) * Create an off-screen GWorld from contents of a file specified by a FSSpec. * Based on BenSWCreateGWorldFromPict. * * Globals referenced: data[0], graf_height, graf_width * Globals updated: pict_rows, pict_cols. */ static OSErr create_gworld_from_spec( GWorldPtr *tile_gw, FSSpec *tile_spec) { OSErr err; GraphicsImportComponent gi; GWorldPtr gw, tmp_gw; GDHandle gdh, tmp_gdh; Rect r; SInt16 depth; /* See if QuickTime understands the file format */ err = GetGraphicsImporterForFile(tile_spec, &gi); /* Oops */ if (err != noErr) return (err); /* Get depth */ depth = data[0].pixelDepth; /* Get GDH */ gdh = data[0].theGDH; /* Retrieve the rect of the image */ err = GraphicsImportGetNaturalBounds(gi, &r); /* Adjust it, so that the upper left corner becomes (0, 0) */ OffsetRect(&r, -r.left, -r.top); /* Calculate and set numbers of rows and columns */ pict_rows = r.bottom / graf_height; pict_cols = r.right / graf_width; /* Create a GWorld */ err = NewGWorld(&gw, depth, &r, NULL, gdh, noNewDevice); /* Oops */ if (err != noErr) return (err); /* Save the pointer to the GWorld */ *tile_gw = gw; /* Save the current GWorld */ GetGWorld(&tmp_gw, &tmp_gdh); /* Activate the newly created GWorld */ (void)GraphicsImportSetGWorld(gi, gw, NULL); /* Prevent pixmap from moving while drawing */ (void)LockPixels(GetGWorldPixMap(gw)); /* Clear the pixels */ EraseRect(&r); /* Draw the image into it */ (void)GraphicsImportDraw(gi); /* Release the lock*/ UnlockPixels(GetGWorldPixMap(gw)); /* Restore GWorld */ SetGWorld(tmp_gw, tmp_gdh); /* Close the image importer */ CloseComponent(gi); /* Success */ return (noErr); } #else /* MACH_O_CARBON */ static OSErr BenSWCreateGWorldFromPict( GWorldPtr *pictGWorld, PicHandle pictH) { OSErr err; GWorldPtr saveGWorld; GDHandle saveGDevice; GWorldPtr tempGWorld; Rect pictRect; short depth; GDHandle theGDH; tempGWorld = NULL; /* Reset */ *pictGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect = (**pictH).picFrame; OffsetRect(&pictRect, -pictRect.left, -pictRect.top); /* Calculate and set numbers of rows and columns */ pict_rows = pictRect.bottom / graf_height; pict_cols = pictRect.right / graf_width; /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Oops */ if (err != noErr) return (err); /* Save pointer */ *pictGWorld = tempGWorld; /* Save GWorld */ GetGWorld(&saveGWorld, &saveGDevice); /* Activate */ SetGWorld(tempGWorld, nil); /* Dump the pict into the GWorld */ (void)LockPixels(GetGWorldPixMap(tempGWorld)); EraseRect(&pictRect); DrawPicture(pictH, &pictRect); UnlockPixels(GetGWorldPixMap(tempGWorld)); /* Restore GWorld */ SetGWorld(saveGWorld, saveGDevice); /* Success */ return (0); } #endif /* MACH_O_CARBON */ /* * Init the global "frameP" */ static errr globe_init(void) { OSErr err; GWorldPtr tempPictGWorldP; #ifdef MACH_O_CARBON FSSpec pict_spec; #else PicHandle newPictH; #endif /* MACH_O_CARBON */ /* Use window XXX XXX XXX */ SetPort(GetWindowPort(data[0].w)); #ifdef MACH_O_CARBON /* Get the tile resources */ if (!get_resource_spec(pict_id, CFSTR("png"), &pict_spec)) return (-1); /* Create GWorld */ err = create_gworld_from_spec(&tempPictGWorldP, &pict_spec); #else /* MACH_O_CARBON */ /* Get the pict resource */ if ((newPictH = GetPicture(pict_id)) == 0) return (-1); /* Create GWorld */ err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH); /* Release resource */ ReleaseResource((Handle)newPictH); #endif /* MACH_O_CARBON */ /* Error */ if (err != noErr) return (err); /* Create the frame */ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec)); /* Analyze result */ if (frameP == NULL) { /* Dispose of image GWorld */ DisposeGWorld(tempPictGWorldP); /* Fake error code */ return (-1); } /* Save GWorld */ frameP->framePort = tempPictGWorldP; /* Lock it */ BenSWLockFrame(frameP); /* Success */ return (noErr); } /* * Nuke the global "frameP" */ static errr globe_nuke(void) { /* Dispose */ if (frameP) { /* Unlock */ BenSWUnlockFrame(frameP); /* Dispose of the GWorld */ DisposeGWorld(frameP->framePort); /* Dispose of the memory */ DisposePtr((Ptr)frameP); /* Forget */ frameP = NULL; } /* Flush events */ FlushEvents(everyEvent, 0); /* Success */ return (0); } #ifdef USE_ASYNC_SOUND /* * Asynchronous sound player revised */ #if defined(USE_QT_SOUND) && !defined(MACH_O_CARBON) # undef USE_QT_SOUND #endif /* USE_QT_SOUND && !MACH_O_CARBON */ /* * How many sound channels will be pooled * * Was: 20, but I don't think we need 20 sound effects playing * simultaneously :) -- pelpel */ #define MAX_CHANNELS 8 /* * A pool of sound channels */ static SndChannelPtr channels[MAX_CHANNELS]; /* * Status of the channel pool */ static Boolean channel_initialised = FALSE; /* * Data handles containing sound samples */ static SndListHandle samples[MSG_MAX]; /* * Reference counts of sound samples */ static SInt16 sample_refs[MSG_MAX]; #define SOUND_VOLUME_MIN 0 /* Default minimum sound volume */ #define SOUND_VOLUME_MAX 255 /* Default maximum sound volume */ #define VOLUME_MIN 0 /* Minimum sound volume in % */ #define VOLUME_MAX 100 /* Maximum sound volume in % */ #define VOLUME_INC 5 /* Increment sound volume in % */ /* * I'm just too lazy to write a panel for this XXX XXX */ static SInt16 sound_volume = SOUND_VOLUME_MAX; #ifdef USE_QT_SOUND /* * QuickTime sound, by Ron Anderson * * I didn't choose to use Windows-style .ini files (Ron wrote a parser * for it, but...), nor did I use lib/xtra directory, hoping someone * would code plist-based configuration code in the future -- pelpel */ /* * (QuickTime) * Load sound effects from data-fork resources. They are wav files * with the same names as angband_sound_name[] (variable.c) * * Globals referenced: angband_sound_name[] * Globals updated: samples[] (they can be *huge*) */ static void load_sounds(void) { OSErr err; int i; /* Start QuickTime */ err = EnterMovies(); /* Error */ if (err != noErr) return; /* * This loop may take a while depending on the count and size of samples * to load. * * We should use a progress dialog for this. */ for (i = 1; i < MSG_MAX; i++) { /* Apple APIs always give me headacke :( */ CFStringRef name; FSSpec spec; SInt16 file_id; SInt16 res_id; Str255 movie_name; Movie movie; Track track; Handle h; Boolean res; /* Allocate CFString with the name of sound event to be processed */ name = CFStringCreateWithCString(NULL, angband_sound_name[i], kTextEncodingUS_ASCII); /* Error */ if (name == NULL) continue; /* Find sound sample resource with the same name */ res = get_resource_spec(name, CFSTR("wav"), &spec); /* Free the reference to CFString */ CFRelease(name); /* Error */ if (!res) continue; /* Open the sound file */ err = OpenMovieFile(&spec, &file_id, fsRdPerm); /* Error */ if (err != noErr) continue; /* Create Movie from the file */ err = NewMovieFromFile(&movie, file_id, &res_id, movie_name, newMovieActive, NULL); /* Error */ if (err != noErr) goto close_file; /* Get the first track of the movie */ track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly ); /* Error */ if (track == NULL) goto close_movie; /* Allocate a handle to store sample */ h = NewHandle(0); /* Error */ if (h == NULL) goto close_track; /* Dump the sample into the handle */ err = PutMovieIntoTypedHandle(movie, track, soundListRsrc, h, 0, GetTrackDuration(track), 0L, NULL); /* Success */ if (err == noErr) { /* Store the handle in the sample list */ samples[i] = (SndListHandle)h; } /* Failure */ else { /* Free unused handle */ DisposeHandle(h); } /* Free the track */ close_track: DisposeMovieTrack(track); /* Free the movie */ close_movie: DisposeMovie(movie); /* Close the movie file */ close_file: CloseMovieFile(file_id); } /* Stop QuickTime */ ExitMovies(); } #else /* USE_QT_SOUND */ /* * Return a handle of 'snd ' resource given Angband sound event number, * or NULL if it isn't found. * * Globals referenced: angband_sound_name[] (variable.c) */ static SndListHandle find_sound(int num) { Str255 sound; /* Get the proper sound name */ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]); sound[0] = strlen((char*)sound + 1); /* Obtain resource XXX XXX XXX */ return ((SndListHandle)GetNamedResource('snd ', sound)); } #endif /* USE_QT_SOUND */ /* * Clean up sound support - to be called when the game exits. * * Globals referenced: channels[], samples[], sample_refs[]. */ static void cleanup_sound(void) { int i; /* No need to clean it up */ if (!channel_initialised) return; /* Dispose channels */ for (i = 0; i < MAX_CHANNELS; i++) { /* Drain sound commands and free the channel */ SndDisposeChannel(channels[i], TRUE); } /* Free sound data */ for (i = 1; i < MSG_MAX; i++) { /* Still locked */ if ((sample_refs[i] > 0) && (samples[i] != NULL)) { /* Unlock it */ HUnlock((Handle)samples[i]); } #ifndef USE_QT_SOUND /* Release it */ if (samples[i]) ReleaseResource((Handle)samples[i]); #else /* Free handle */ if (samples[i]) DisposeHandle((Handle)samples[i]); #endif /* !USE_QT_SOUND */ } } /* * Play sound effects asynchronously -- pelpel * * I don't believe those who first started using the previous implementations * imagined this is *much* more complicated as it may seem. Anyway, * introduced round-robin scheduling of channels and made it much more * paranoid about HLock/HUnlock. * * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done * using channel's callback procedures, which set global flags, and * a procedure hooked into CheckEvents does housekeeping. On the other * hand, this lazy reclaiming strategy keeps things simple (no interrupt * time code) and provides a sort of cache for sound data. * * Globals referenced: channel_initialised, channels[], samples[], * sample_refs[]. * Globals updated: channel_initialised, channels[], sample_refs[]. * Only in !USE_QT_SOUND, samples[]. */ static void play_sound(int num, SInt16 vol) { OSErr err; int i; int prev_num; SndListHandle h; SndChannelPtr chan; SCStatus status; static int next_chan; static SInt16 channel_occupants[MAX_CHANNELS]; static SndCommand volume_cmd, quiet_cmd; /* Initialise sound channels */ if (!channel_initialised) { for (i = 0; i < MAX_CHANNELS; i++) { /* Paranoia - Clear occupant table */ /* channel_occupants[i] = 0; */ /* Create sound channel for all sounds to play from */ err = SndNewChannel(&channels[i], sampledSynth, initMono, NULL); /* Error */ if (err != noErr) { /* Free channels */ while (--i >= 0) { SndDisposeChannel(channels[i], TRUE); } /* Notify error */ plog("Cannot initialise sound channels!"); /* Cancel request */ use_sound = arg_sound = FALSE; /* Failure */ return; } } /* First channel to use */ next_chan = 0; /* Prepare volume command */ volume_cmd.cmd = volumeCmd; volume_cmd.param1 = 0; volume_cmd.param2 = 0; /* Prepare quiet command */ quiet_cmd.cmd = quietCmd; quiet_cmd.param1 = 0; quiet_cmd.param2 = 0; /* Initialisation complete */ channel_initialised = TRUE; } /* Paranoia */ if ((num <= 0) || (num >= MSG_MAX)) return; /* Prepare volume command */ volume_cmd.param2 = ((SInt32)vol << 16) | vol; /* Channel to use (round robin) */ chan = channels[next_chan]; /* See if the resource is already in use */ if (sample_refs[num] > 0) { /* Resource in use */ h = samples[num]; /* Increase the refcount */ sample_refs[num]++; } /* Sound is not currently in use */ else { /* Get handle for the sound */ #ifdef USE_QT_SOUND h = samples[num]; #else h = find_sound(num); #endif /* USE_QT_SOUND */ /* Sample not available */ if (h == NULL) return; #ifndef USE_QT_SOUND /* Load resource */ LoadResource((Handle)h); /* Remember it */ samples[num] = h; #endif /* !USE_QT_SOUND */ /* Lock the handle */ HLockHi((Handle)h); /* Initialise refcount */ sample_refs[num] = 1; } /* Poll the channel */ err = SndChannelStatus(chan, sizeof(SCStatus), &status); /* It isn't available */ if ((err != noErr) || status.scChannelBusy) { /* Shut it down */ SndDoImmediate(chan, &quiet_cmd); } /* Previously played sound on this channel */ prev_num = channel_occupants[next_chan]; /* Process previously played sound */ if (prev_num != 0) { /* Decrease refcount */ sample_refs[prev_num]--; /* We can free it now */ if (sample_refs[prev_num] <= 0) { /* Unlock */ HUnlock((Handle)samples[prev_num]); #ifndef USE_QT_SOUND /* Release */ ReleaseResource((Handle)samples[prev_num]); /* Forget handle */ samples[prev_num] = NULL; #endif /* !USE_QT_SOUND */ /* Paranoia */ sample_refs[prev_num] = 0; } } /* Remember this sound as the current occupant of the channel */ channel_occupants[next_chan] = num; /* Set up volume for channel */ SndDoImmediate(chan, &volume_cmd); /* Play new sound asynchronously */ SndPlay(chan, h, TRUE); /* Schedule next channel (round robin) */ next_chan++; if (next_chan >= MAX_CHANNELS) next_chan = 0; } #else /* USE_ASYNC_SOUND */ /* * Synchronous sound effect player * * This may not be your choice, but much safer and much less * resource hungry. */ static void play_sound(int num, SInt16 vol) { #pragma unused(vol) Handle handle; Str255 sound; /* Get the proper sound name */ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]); sound[0] = strlen((char*)sound + 1); /* Obtain resource XXX XXX XXX */ handle = GetNamedResource('snd ', sound); /* Oops -- it is a failure, but we return 0 anyway */ if (handle == NULL) return; /* Load and Lock */ LoadResource(handle); HLockHi(handle); /* Play sound (wait for completion) */ SndPlay(NULL, (SndListHandle)handle, FALSE); /* Unlock and release */ HUnlock(handle); ReleaseResource(handle); } #endif /* USE_ASYNC_SOUND */ /*** Support for the "z-term.c" package ***/ /* * Initialize a new Term * * Note also the "window type" called "noGrowDocProc", which might be more * appropriate for the main "screen" window. * * Note the use of "srcCopy" mode for optimized screen writes. */ static void Term_init_mac(term *t) { term_data *td = (term_data*)(t->data); WindowAttributes wattrs; OSStatus err; static RGBColor black = {0x0000,0x0000,0x0000}; static RGBColor white = {0xFFFF,0xFFFF,0xFFFF}; #ifndef ALLOW_BIG_SCREEN /* Every window has close and collapse boxes */ wattrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute; /* Information windows are resizable */ if (td != &data[0]) wattrs |= kWindowResizableAttribute; #else /* Big screen - every window has close, collapse and resize boxes */ wattrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowResizableAttribute; #endif /* !ALLOW_BIG_SCREEN */ /* Make the window */ err = CreateNewWindow( kDocumentWindowClass, wattrs, &td->r, &td->w); /* Fatal error */ if (err != noErr) ExitToShell(); /* Set refcon */ SetWRefCon(td->w, (long)td); /* Set window title */ SetWTitle(td->w, td->title); /* Activate the window */ activate(td->w); /* Erase behind words */ TextMode(srcCopy); /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize the window */ term_data_resize(td); /* Prepare the colors (real colors) */ RGBBackColor(&black); RGBForeColor(&white); /* Block */ { Rect globalRect; GDHandle mainGDH; GDHandle currentGDH; GWorldPtr windowGWorld; PixMapHandle basePixMap; /* Obtain the global rect */ GetWindowBounds((WindowRef)td->w, kWindowContentRgn, &globalRect); /* Obtain the proper GDH */ mainGDH = GetMaxDevice(&globalRect); /* Extract GWorld and GDH */ GetGWorld(&windowGWorld, ¤tGDH); /* Obtain base pixmap */ basePixMap = (**mainGDH).gdPMap; /* Save pixel depth */ td->pixelDepth = (**basePixMap).pixelSize; /* Save Window GWorld - unused */ /* td->theGWorld = windowGWorld; */ /* Save Window GDH */ td->theGDH = currentGDH; /* Save main GDH - unused */ /* td->mainSWGDH = mainGDH; */ } { Rect portRect; /* Get current Rect */ GetPortBounds(GetWindowPort(td->w), &portRect); /* Clip to the window */ ClipRect(&portRect); /* Erase the window */ EraseRect(&portRect); } /* * A certain release of OS X fails to display windows at proper * locations (_ _#) */ if ((mac_os_version >= 0x1000) && (mac_os_version < 0x1010)) { /* Hack - Make sure the window is displayed at (r.left,r.top) */ MoveWindow(td->w, td->r.left, td->r.top, 1); } /* Display the window if needed */ if (td->mapped) { TransitionWindow(td->w, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL); } /* Hack -- set "mapped" flag */ t->mapped_flag = td->mapped; /* Forget color */ td->last = -1; } /* * Nuke an old Term */ static void Term_nuke_mac(term *t) { #pragma unused(t) /* XXX */ } /* * Unused */ static errr Term_user_mac(int n) { #pragma unused(n) /* Success */ return (0); } /* * React to changes */ static errr Term_xtra_mac_react(void) { term_data *td = (term_data*)(Term->data); /* Reset color */ td->last = -1; /* Update colors */ update_colour_info(); /* Handle sound */ if (use_sound != arg_sound) { /* Apply request */ use_sound = arg_sound; } /* Don't actually switch graphics until the game is running */ if (!initialized || !game_in_progress) return (-1); /* Handle graphics */ if (graf_mode_req != graf_mode) { /* dispose old GWorld's if present */ globe_nuke(); /* * Setup parameters according to request */ switch (graf_mode_req) { /* ASCII - no graphics whatsoever */ case GRAF_MODE_NONE: { use_graphics = arg_graphics = GRAPHICS_NONE; transparency_mode = TR_NONE; break; } /* * 8x8 tiles (PICT id 1001) * no transparency effect * "old" graphics definitions */ case GRAF_MODE_8X8: { use_graphics = arg_graphics = GRAPHICS_ORIGINAL; transparency_mode = TR_NONE; #ifdef MACH_O_CARBON pict_id = CFSTR("8x8"); #else pict_id = 1001; #endif /* MACH_O_CARBON */ graf_width = graf_height = 8; break; } /* * 16x16 tiles (images: PICT id 1002, masks: PICT id 1003) * with transparency effect * "new" graphics definitions */ case GRAF_MODE_16X16: { use_graphics = arg_graphics = GRAPHICS_ADAM_BOLT; transparency_mode = TR_OVER; #ifdef MACH_O_CARBON pict_id = CFSTR("16x16"); #else pict_id = 1002; #endif /* MACH_O_CARBON */ graf_width = graf_height = 16; break; } /* * 32x32 tiles (images: PICT id 1004) * with transparency effect * "david" graphics definitions * Vanilla-specific */ case GRAF_MODE_32X32: { use_graphics = arg_graphics = GRAPHICS_DAVID_GERVAIS; transparency_mode = TR_OVER; #ifdef MACH_O_CARBON pict_id = CFSTR("32x32"); #else pict_id = 1004; #endif /* MACH_O_CARBON */ graf_width = graf_height = 32; break; } } /* load tiles and setup GWorlds if tiles are requested */ if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0)) { /* Oops */ plog("Cannot initialize graphics!"); /* reject request */ graf_mode_req = GRAF_MODE_NONE; /* reset graphics flags */ use_graphics = arg_graphics = GRAPHICS_NONE; /* reset transparency mode */ transparency_mode = TR_NONE; } /* update current graphics mode */ graf_mode = graf_mode_req; /* Apply and Verify */ term_data_check_size(td); /* Resize the window */ term_data_resize(td); /* Reset visuals */ if (initialized && game_in_progress) { #ifndef ANG281_RESET_VISUALS reset_visuals(TRUE); #else reset_visuals(); #endif /* !ANG281_RESET_VISUALS */ } } /* Success */ return (0); } /* * Do a "special thing" */ static errr Term_xtra_mac(int n, int v) { term_data *td = (term_data*)(Term->data); Rect r; /* Analyze */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: { /* Make a noise */ SysBeep(1); /* Success */ return (0); } /* Make a sound */ case TERM_XTRA_SOUND: { /* Play sound */ play_sound(v, sound_volume); /* Success */ return (0); } /* Process random events */ case TERM_XTRA_BORED: { /* Process an event */ (void)CheckEvents(CHECK_EVENTS_NO_WAIT); /* Success */ return (0); } /* Process pending events */ case TERM_XTRA_EVENT: { /* Process an event */ (void)CheckEvents(v); /* Success */ return (0); } /* Flush all pending events (if any) */ case TERM_XTRA_FLUSH: { /* Hack -- flush all events */ while (CheckEvents(CHECK_EVENTS_DRAIN)) /* loop */; /* Success */ return (0); } /* Hack -- Change the "soft level" */ case TERM_XTRA_LEVEL: { /* Activate if requested */ if (v) activate(td->w); /* Success */ return (0); } #if 0 /* Clear the screen */ case TERM_XTRA_CLEAR: { Rect portRect; /* Get current Rect */ GetPortBounds(GetWindowPort(td->w), &portRect); /* No clipping XXX XXX XXX */ ClipRect(&portRect); /* Erase the window */ EraseRect(&portRect); /* Set the color */ term_data_color(td, TERM_WHITE); /* Frame the window in white */ MoveTo(0, 0); LineTo(0, td->size_hgt-1); LineTo(td->size_wid-1, td->size_hgt-1); LineTo(td->size_wid-1, 0); /* Clip to the new size */ r.left = portRect.left + td->size_ow1; r.top = portRect.top + td->size_oh1; r.right = portRect.right - td->size_ow2; r.bottom = portRect.bottom - td->size_oh2; ClipRect(&r); /* Success */ return (0); } #endif /* 0 */ /* React to changes */ case TERM_XTRA_REACT: { /* React to changes */ return (Term_xtra_mac_react()); } /* Delay (milliseconds) */ case TERM_XTRA_DELAY: { /* * WaitNextEvent relinquishes CPU as well as * induces a screen refresh on OS X */ /* If needed */ if (v > 0) { EventRecord tmp; UInt32 ticks; /* Convert millisecs to ticks */ ticks = (v * 60L) / 1000; /* * Hack? - Put the programme into sleep. * No events match ~everyEvent, so nothing * should be lost in Angband's event queue. * Even if ticks are 0, it's worth calling for * the above mentioned reasons. */ WaitNextEvent((EventMask)~everyEvent, &tmp, ticks, nil); } /* Success */ return (0); } } /* Oops */ return (1); } /* * Low level graphics (Assumes valid input). * Draw a "cursor" at (x,y), using a "yellow box". * We are allowed to use "Term_what()" to determine * the current screen contents (for inverting, etc). */ static errr Term_curs_mac(int x, int y) { Rect r; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, TERM_YELLOW); /* Frame the grid */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; FrameRect(&r); /* Success */ return (0); } #ifdef USE_DOUBLE_TILES /* * Low level graphics (Assumes valid input). * Draw a "cursor" at (x,y), using a "yellow box", twice the width of * the current font. * We are allowed to use "Term_what()" to determine * the current screen contents (for inverting, etc). */ static errr Term_bigcurs_mac(int x, int y) { Rect r; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, TERM_YELLOW); /* Frame the grid */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + 2 * td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; FrameRect(&r); /* Success */ return (0); } #endif /* USE_DOUBLE_TILES */ #ifdef OVERWRITE_HACK /* * Low level graphics helper (Assumes valid input) * * Based on suggestion by Julian Lighton * * Overwrite "n" old characters starting at (x,y) * with the same ones in the background colour */ static void Term_wipe_mac_aux(int x, int y, int n) { term_data *td = (term_data*)(Term->data); int xp, yp; const char *cp; static RGBColor black = {0x0000,0x0000,0x0000}; /* Hack - Black, blacker, blackest-- */ if (td->last != -2) RGBForeColor(&black); /* Hack - force later RGBForeColor switching */ td->last = -2; /* Mega-Hack - use old screen image kept inside the term package */ cp = &(Term->old->c[y][x]); /* Starting pixel */ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1; yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ if (n == 1) DrawChar(*cp); /* Draw the string */ else DrawText(cp, 0, n); } #endif /* OVERWRITE_HACK */ /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ static errr Term_wipe_mac(int x, int y, int n) { Rect r; term_data *td = (term_data*)(Term->data); #ifdef OVERWRITE_HACK /* * Hack - overstrike the leftmost character with * the background colour. This doesn't interfere with * the graphics modes, because they set always_pict. */ Term_wipe_mac_aux(x, y, 1); #endif /* OVERWRITE_HACK */ /* Erase the block of characters */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + n * td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; EraseRect(&r); /* Success */ return (0); } /* * Low level graphics. Assumes valid input. * * Draw several ("n") chars, with an attr, at a given location. */ static errr Term_text_mac(int x, int y, int n, byte a, const char *cp) { int xp, yp; #ifdef CLIP_HACK Rect r; #endif /* CLIP_HACK */ term_data *td = (term_data*)(Term->data); #ifdef OVERWRITE_HACK /* Hack - overstrike with background colour. Is 1 enough? */ Term_wipe_mac_aux(x, y, n); #endif /* OVERWRITE_HACK */ /* Set the color */ term_data_color(td, a); #ifdef CLIP_HACK /* Hack - only draw within the bounding rect */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + n * td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; ClipRect(&r); /* Hack - clear the content of the bounding rect */ EraseRect(&r); #endif /* CLIP_HACK */ /* Starting pixel */ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1; yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ if (n == 1) DrawChar(*cp); /* Draw the string */ else DrawText(cp, 0, n); #ifdef CLIP_HACK /* Obtain current window's rect */ GetPortBounds(GetWindowPort(td->w), &r); /* Clip to the window again */ ClipRect(&r); #endif /* CLIP_HACK */ /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ #ifdef USE_TRANSPARENCY static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) #else /* USE_TRANSPARENCY */ static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp) #endif /* USE_TRANSPARENCY */ { int i; Rect dst_r; GrafPtr port; PixMapHandle pixmap_h; #ifdef CLIP_HACK Rect portRect; #endif /* CLIP_HACK */ term_data *td = (term_data*)(Term->data); static RGBColor black = {0x0000,0x0000,0x0000}; static RGBColor white = {0xFFFF,0xFFFF,0xFFFF}; #ifdef CLIP_HACK /* Remember current window's rect */ GetPortBounds(GetWindowPort(td->w), &portRect); #endif /* CLIP_HACK */ /* Destination rectangle */ dst_r.left = x * td->tile_wid + td->size_ow1; #ifndef USE_DOUBLE_TILES dst_r.right = dst_r.left + td->tile_wid; #endif /* !USE_DOUBLE_TILES */ dst_r.top = y * td->tile_hgt + td->size_oh1; dst_r.bottom = dst_r.top + td->tile_hgt; /* Scan the input */ for (i = 0; i < n; i++) { byte a = *ap++; char c = *cp++; #ifdef USE_TRANSPARENCY byte ta = *tap++; char tc = *tcp++; #endif #ifdef USE_DOUBLE_TILES /* Hack -- a filler for double-width tile */ if (use_bigtile && (a == 255)) { /* Advance */ dst_r.left += td->tile_wid; /* Ignore */ continue; } /* Prepare right side of rectagle now */ dst_r.right = dst_r.left + td->tile_wid; #endif /* USE_DOUBLE_TILES */ /* Graphics -- if Available and Needed */ if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80)) { int col, row; Rect src_r; #ifdef USE_TRANSPARENCY int t_col, t_row; Rect terrain_r; #endif /* USE_TRANSPARENCY */ /* Row and Col */ row = ((byte)a & 0x7F) % pict_rows; col = ((byte)c & 0x7F) % pict_cols; /* Source rectangle */ src_r.left = col * graf_width; src_r.top = row * graf_height; src_r.right = src_r.left + graf_width; src_r.bottom = src_r.top + graf_height; #ifdef USE_TRANSPARENCY /* Row and Col */ t_row = ((byte)ta & 0x7F) % pict_rows; t_col = ((byte)tc & 0x7F) % pict_cols; /* Source rectangle */ terrain_r.left = t_col * graf_width; terrain_r.top = t_row * graf_height; terrain_r.right = terrain_r.left + graf_width; terrain_r.bottom = terrain_r.top + graf_height; #endif /* USE_TRANSPARENCY */ /* Hardwire CopyBits */ RGBBackColor(&white); RGBForeColor(&black); #ifdef USE_DOUBLE_TILES /* Double width tiles */ if (use_bigtile) dst_r.right += td->tile_wid; #endif /* USE_DOUBLE_TILES */ /* * OS X requires locking and unlocking of window port * when we draw directly to its pixmap. * The Lock/Unlock protocol is described in the Carbon * Porting Guide. */ /* Obtain current window's graphic port */ port = GetWindowPort(td->w); /* Lock pixels, so we can use handle safely */ LockPortBits(port); /* Get Pixmap handle */ pixmap_h = GetPortPixMap(port); #ifdef USE_TRANSPARENCY /* Transparency effect */ switch (transparency_mode) { /* No transparency effects */ case TR_NONE: default: { /* Draw the picture */ CopyBits((BitMap*)frameP->framePix, (BitMap*)*pixmap_h, &src_r, &dst_r, srcCopy, NULL); break; } /* Overwriting with transparent black pixels */ case TR_OVER: { /* Draw the terrain */ CopyBits((BitMap*)frameP->framePix, (BitMap*)*pixmap_h, &terrain_r, &dst_r, srcCopy, NULL); /* There's something on the terrain */ if ((row != t_row) || (col != t_col)) { /* Make black pixels transparent */ RGBBackColor(&black); /* Draw monster/object/player */ CopyBits((BitMap*)frameP->framePix, (BitMap*)*pixmap_h, &src_r, &dst_r, transparent, NULL); } break; } } #else /* USE_TRANSPARENCY */ /* Draw the picture */ CopyBits((BitMap*)frameP->framePix, (BitMap*)*pixmap_h, &src_r, &dst_r, srcCopy, NULL); #endif /* USE_TRANSPARENCY */ /* Release the lock and dispose the PixMap handle */ UnlockPortBits(port); /* Restore colors */ RGBBackColor(&black); RGBForeColor(&white); /* Forget color */ td->last = -1; } /* * Deal with these cases: * (1) the player changed tile width / height, or * (2) fake fixed-width for proportional font */ else { int xp, yp; #ifdef OVERWRITE_HACK /* * Hack - overstrike with the background colour * Utterly useless in the graphics mode, but it only affects * performance slightly... */ Term_wipe_mac_aux(x+i, y, 1); #endif /* OVERWRITE_HACK */ #ifdef CLIP_HACK /* Hack - avoid writing outside of dst_r */ ClipRect(&dst_r); /* Some characters do not match dst_r, therefore we have to... */ #endif /* CLIP_HACK */ /* Erase */ EraseRect(&dst_r); /* Set the color */ term_data_color(td, a); /* Starting pixel */ xp = dst_r.left + td->tile_o_x; yp = dst_r.top + td->tile_o_y; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ DrawChar(c); #ifdef CLIP_HACK /* Clip to the window - inefficient (; ;) XXX XXX */ ClipRect(&portRect); #endif /* CLIP_HACK */ } /* Advance */ dst_r.left += td->tile_wid; #ifndef USE_DOUBLE_TILES dst_r.right += td->tile_wid; #endif /* !USE_DOUBLE_TILES */ } /* Success */ return (0); } /* * Create and initialize window number "i" */ static void term_data_link(int i) { term *old = Term; term_data *td = &data[i]; /* Only once */ if (td->t) return; /* Require mapped */ if (!td->mapped) return; /* Allocate */ MAKE(td->t, term); /* Initialize the term */ term_init(td->t, td->cols, td->rows, td->keys); /* Use a "software" cursor */ td->t->soft_cursor = TRUE; /* * HACK - We have an "icky" lower right corner, since * the window resize control is placed there */ td->t->icky_corner = TRUE; /* Erase with "white space" */ td->t->attr_blank = TERM_WHITE; td->t->char_blank = ' '; /* Prepare the init/nuke hooks */ td->t->init_hook = Term_init_mac; td->t->nuke_hook = Term_nuke_mac; /* Prepare the function hooks */ td->t->user_hook = Term_user_mac; td->t->xtra_hook = Term_xtra_mac; td->t->wipe_hook = Term_wipe_mac; td->t->curs_hook = Term_curs_mac; #ifdef USE_DOUBLE_TILES td->t->bigcurs_hook = Term_bigcurs_mac; #endif /* USE_DOUBLE_TILES */ td->t->text_hook = Term_text_mac; td->t->pict_hook = Term_pict_mac; #if 0 /* Doesn't make big difference? */ td->t->never_bored = TRUE; #endif /* Link the local structure */ td->t->data = (void *)(td); /* Activate it */ Term_activate(td->t); /* Global pointer */ angband_term[i] = td->t; /* Activate old */ Term_activate(old); } #ifdef MACH_O_CARBON /* * (Carbon, Bundle) * Return a POSIX pathname of the lib directory, or NULL if it can't be * located. Caller must supply a buffer along with its size in bytes, * where returned pathname will be stored. */ static char *locate_lib(char *buf, size_t size) { CFBundleRef main_bundle; CFURLRef main_url; bool success; /* Get the application bundle (Angband.app) */ main_bundle = CFBundleGetMainBundle(); /* Oops */ if (!main_bundle) return (NULL); /* Obtain the URL of the main bundle */ main_url = CFBundleCopyBundleURL(main_bundle); /* Oops */ if (!main_url) return (NULL); /* Get the URL in the file system's native string representation */ success = CFURLGetFileSystemRepresentation(main_url, TRUE, buf, size); /* Free the url */ CFRelease(main_url); /* Oops */ if (!success) return (NULL); /* Append "/Contents/Resources/lib/" */ my_strcat(buf, "/Contents/Resources/lib/", size); return (buf); } #else /* MACH_O_CARBON */ /* * Set the "current working directory" (also known as the "default" * volume/directory) to the location of the current application. * * Original code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl) * * Completely rewritten to use Carbon Process Manager. It retrieves the * volume and directory of the current application and simply stores it * in the (static) global variables app_vol and app_dir, but doesn't * mess with the "current working directory", because it has long been * an obsolete (and arcane!) feature. */ static void SetupAppDir(void) { OSErr err; ProcessSerialNumber curPSN; ProcessInfoRec procInfo; FSSpec cwdSpec; /* Initialise PSN info for the current process */ curPSN.highLongOfPSN = 0; curPSN.lowLongOfPSN = kCurrentProcess; /* Fill in mandatory fields */ procInfo.processInfoLength = sizeof(ProcessInfoRec); procInfo.processName = nil; procInfo.processAppSpec = &cwdSpec; /* Obtain current process information */ err = GetProcessInformation(&curPSN, &procInfo); /* Oops */ if (err != noErr) { mac_warning("Unable to get process information"); /* Quit without writing anything */ ExitToShell(); } /* Extract and save the Vol and Dir */ app_vol = cwdSpec.vRefNum; app_dir = cwdSpec.parID; } #endif /* MACH_O_CARBON */ /* * Using Core Foundation's Preferences services -- pelpel * * Requires OS 8.6 or greater with CarbonLib 1.1 or greater. Or OS X, * of course. * * Without this, we can support older versions of OS 8 as well * (with CarbonLib 1.0.4). * * Frequent allocation/deallocation of small chunks of data is * far from my liking, but since this is only called at the * beginning and the end of a session, I hope this hardly matters. */ /* * Store "value" as the value for preferences item name * pointed by key */ static void save_pref_short(const char *key, short value) { CFStringRef cf_key; CFNumberRef cf_value; /* allocate and initialise the key */ cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII); /* allocate and initialise the value */ cf_value = CFNumberCreate(NULL, kCFNumberShortType, &value); if ((cf_key != NULL) && (cf_value != NULL)) { /* Store the key-value pair in the applications preferences */ CFPreferencesSetAppValue( cf_key, cf_value, kCFPreferencesCurrentApplication); } /* * Free CF data - the reverse order is a vain attempt to * minimise memory fragmentation. */ if (cf_value) CFRelease(cf_value); if (cf_key) CFRelease(cf_key); } /* * Load preference value for key, returns TRUE if it succeeds with * vptr updated appropriately, FALSE otherwise. */ static bool query_load_pref_short(const char *key, short *vptr) { CFStringRef cf_key; CFNumberRef cf_value; /* allocate and initialise the key */ cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII); /* Oops */ if (cf_key == NULL) return (FALSE); /* Retrieve value for the key */ cf_value = CFPreferencesCopyAppValue( cf_key, kCFPreferencesCurrentApplication); /* Value not found */ if (cf_value == NULL) { CFRelease(cf_key); return (FALSE); } /* Convert the value to short */ CFNumberGetValue( cf_value, kCFNumberShortType, vptr); /* Free CF data */ CFRelease(cf_value); CFRelease(cf_key); /* Success */ return (TRUE); } /* * Update short data pointed by vptr only if preferences * value for key is located. */ static void load_pref_short(const char *key, short *vptr) { short tmp; if (query_load_pref_short(key, &tmp)) *vptr = tmp; } /* * Save preferences to preferences file for current host+current user+ * current application. */ static void cf_save_prefs() { int i; /* Version stamp */ save_pref_short("version.major", VER_MAJOR); save_pref_short("version.minor", VER_MINOR); save_pref_short("version.patch", VER_PATCH); save_pref_short("version.extra", VER_EXTRA); /* Gfx settings */ save_pref_short("arg.arg_sound", arg_sound); save_pref_short("arg.graf_mode", graf_mode); #ifdef USE_DOUBLE_TILES save_pref_short("arg.big_tile", use_bigtile); #endif /* USE_DOUBLE_TILES */ /* Windows */ for (i = 0; i < MAX_TERM_DATA; i++) { term_data *td = &data[i]; save_pref_short(format("term%d.mapped", i), td->mapped); save_pref_short(format("term%d.font_id", i), td->font_id); save_pref_short(format("term%d.font_size", i), td->font_size); save_pref_short(format("term%d.font_face", i), td->font_face); save_pref_short(format("term%d.tile_wid", i), td->tile_wid); save_pref_short(format("term%d.tile_hgt", i), td->tile_hgt); save_pref_short(format("term%d.cols", i), td->cols); save_pref_short(format("term%d.rows", i), td->rows); save_pref_short(format("term%d.left", i), td->r.left); save_pref_short(format("term%d.top", i), td->r.top); } /* * Make sure preferences are persistent */ CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); } /* * Load preferences from preferences file for current host+current user+ * current application. */ static void cf_load_prefs() { bool ok; short pref_major, pref_minor, pref_patch, pref_extra; short valid; int i; /* Assume nothing is wrong, yet */ ok = TRUE; /* Load version information */ ok &= query_load_pref_short("version.major", &pref_major); ok &= query_load_pref_short("version.minor", &pref_minor); ok &= query_load_pref_short("version.patch", &pref_patch); ok &= query_load_pref_short("version.extra", &pref_extra); /* Any of the above failed */ if (!ok) { #if 0 /* This may be the first run */ mac_warning("Preferences are not found."); #endif /* 0 */ /* Ignore the rest */ return; } #if 0 /* Check version */ if ((pref_major != VERSION_MAJOR) || (pref_minor != VERSION_MINOR) || (pref_patch != VERSION_PATCH) || (pref_extra != VERSION_EXTRA)) { /* Message */ mac_warning( format("Ignoring %d.%d.%d.%d preferences.", pref_major, pref_minor, pref_patch, pref_extra)); /* Ignore */ return; } #endif /* HACK - Check for broken preferences */ load_pref_short("term0.mapped", &valid); /* Ignore broken preferences */ if (!valid) { mac_warning("Ignoring broken preferences."); /* Ignore */ return; } /* Gfx settings */ { short pref_tmp; /* sound */ if (query_load_pref_short("arg.arg_sound", &pref_tmp)) arg_sound = pref_tmp; /* graphics */ if (query_load_pref_short("arg.graf_mode", &pref_tmp)) graf_mode_req = pref_tmp; #ifdef USE_DOUBLE_TILES /* double-width tiles */ if (query_load_pref_short("arg.big_tile", &pref_tmp)) { use_bigtile = pref_tmp; } #endif /* USE_DOUBLE_TILES */ } /* Windows */ for (i = 0; i < MAX_TERM_DATA; i++) { term_data *td = &data[i]; load_pref_short(format("term%d.mapped", i), &td->mapped); load_pref_short(format("term%d.font_id", i), &td->font_id); load_pref_short(format("term%d.font_size", i), &td->font_size); load_pref_short(format("term%d.font_face", i), &td->font_face); load_pref_short(format("term%d.tile_wid", i), &td->tile_wid); load_pref_short(format("term%d.tile_hgt", i), &td->tile_hgt); load_pref_short(format("term%d.cols", i), &td->cols); load_pref_short(format("term%d.rows", i), &td->rows); load_pref_short(format("term%d.left", i), &td->r.left); load_pref_short(format("term%d.top", i), &td->r.top); } } /* * Hack -- default data for a window */ static void term_data_hack(term_data *td) { short fid; /* Default to Monaco font */ GetFNum("\pmonaco", &fid); /* Wipe it */ WIPE(td, term_data); /* No color */ td->last = -1; /* Default borders */ td->size_ow1 = 2; td->size_ow2 = 2; td->size_oh2 = 2; /* Start hidden */ td->mapped = FALSE; /* Default font */ td->font_id = fid; /* Default font size - was 12 */ td->font_size = 14; /* Default font face */ td->font_face = 0; /* Default size */ td->rows = 24; td->cols = 80; /* Default position */ td->r.left = 10; td->r.top = 40; /* Minimal keys */ td->keys = 16; } /* * Read the preference file, Create the windows. * * We attempt to use "FindFolder()" to track down the preference file. */ static void init_windows(void) { int i, b = 0; term_data *td; /*** Default values ***/ /* Initialize (backwards) */ for (i = MAX_TERM_DATA; i-- > 0; ) { int n; cptr s; /* Obtain */ td = &data[i]; /* Defaults */ term_data_hack(td); /* Obtain title */ s = angband_term_name[i]; /* Get length */ n = strlen(s); /* Maximal length */ if (n > 15) n = 15; /* Copy the title */ strncpy((char*)(td->title) + 1, s, n); /* Save the length */ td->title[0] = n; /* Tile the windows */ td->r.left += (b * 30); td->r.top += (b * 30); /* Tile */ b++; } /*** Load preferences ***/ cf_load_prefs(); /*** Instantiate ***/ /* Main window */ td = &data[0]; /* Many keys */ td->keys = 1024; /* Start visible */ td->mapped = TRUE; /* Link (backwards, for stacking order) */ for (i = MAX_TERM_DATA; i-- > 0; ) { term_data_link(i); } /* Main window */ td = &data[0]; /* Main window */ Term_activate(td->t); } /* * Save preferences */ static void save_pref_file(void) { cf_save_prefs(); } /* * Prepare savefile dialogue and set the variable * savefile accordingly. Returns true if it succeeds, false (or * aborts) otherwise. If all is false, only allow files whose type * is 'SAVE'. * Originally written by Peter Ammon */ static bool select_savefile(bool all) { OSErr err; FSSpec theFolderSpec; FSSpec savedGameSpec; NavDialogOptions dialogOptions; NavReplyRecord reply; /* Used only when 'all' is true */ NavTypeList types = {ANGBAND_CREATOR, 1, 1, {'SAVE'}}; NavTypeListHandle myTypeList; AEDesc defaultLocation; #ifdef MACH_O_CARBON short foundVRefNum; long foundDirID; /* Find the preferences folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &foundVRefNum, &foundDirID); if (err != noErr) quit("Couldn't find the preferences folder!"); /* Look for the "ZAngband/save/" sub-folder */ err = FSMakeFSSpec(foundVRefNum, foundDirID, "\p:ZAngband:save:", &theFolderSpec); /* Oops */ if (err != noErr) quit_fmt("Unable to find the savefile folder! (Error %d)", err); #else /* Find :lib:save: folder */ err = FSMakeFSSpec(app_vol, app_dir, "\p:lib:save:", &theFolderSpec); /* Oops */ if (err != noErr) quit("Unable to find the folder :lib:save:"); #endif /* Get default Navigator dialog options */ err = NavGetDefaultDialogOptions(&dialogOptions); /* Clear preview option */ dialogOptions.dialogOptionFlags &= ~kNavAllowPreviews; /* Disable multiple file selection */ dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles; /* Make descriptor for default location */ err = AECreateDesc(typeFSS, &theFolderSpec, sizeof(FSSpec), &defaultLocation); /* Oops */ if (err != noErr) quit("Unable to allocate descriptor"); /* We are indifferent to signature and file types */ if (all) { myTypeList = (NavTypeListHandle)nil; } /* Set up type handle */ else { err = PtrToHand(&types, (Handle *)&myTypeList, sizeof(NavTypeList)); /* Oops */ if (err != noErr) quit("Error in PtrToHand. Try enlarging heap"); } /* Call NavGetFile() with the types list */ err = NavChooseFile(&defaultLocation, &reply, &dialogOptions, NULL, NULL, NULL, myTypeList, NULL); /* Free type list */ if (!all) DisposeHandle((Handle)myTypeList); /* Error */ if (err != noErr) { /* Nothing */ } /* Invalid response -- allow the user to cancel */ else if (!reply.validRecord) { /* Hack -- Fake error */ err = -1; } /* Retrieve FSSpec from the reply */ else { AEKeyword theKeyword; DescType actualType; Size actualSize; /* Get a pointer to selected file */ (void)AEGetNthPtr(&reply.selection, 1, typeFSS, &theKeyword, &actualType, &savedGameSpec, sizeof(FSSpec), &actualSize); /* Dispose NavReplyRecord, resources and descriptors */ (void)NavDisposeReply(&reply); } /* Dispose location info */ AEDisposeDesc(&defaultLocation); /* Error */ if (err != noErr) return (FALSE); #ifdef MACH_O_CARBON /* Convert FSSpec to pathname and store it in variable savefile */ (void)spec_to_path(&savedGameSpec, savefile, sizeof(savefile)); #else /* Convert FSSpec to pathname and store it in variable savefile */ refnum_to_name( savefile, savedGameSpec.parID, savedGameSpec.vRefNum, (char *)savedGameSpec.name); #endif /* Success */ return (TRUE); } /* * Handle menu: "File" + "New" */ static void do_menu_file_new(void) { /* Game is in progress */ game_in_progress = TRUE; /* Start a new game */ new_game = TRUE; } /* * Handle menu: "File" + "Open" / "Import" */ static void do_menu_file_open(bool all) { /* Let the player to choose savefile */ if (!select_savefile(all)) return; /* Game is in progress */ game_in_progress = TRUE; /* Use an existing savefile */ new_game = FALSE; } /* * Handle the "open_when_ready" flag */ static void handle_open_when_ready(void) { /* Check the flag XXX XXX XXX make a function for this */ if (open_when_ready && initialized && !game_in_progress) { /* Forget */ open_when_ready = FALSE; /* Game is in progress */ game_in_progress = TRUE; /* Use an existing savefile */ new_game = FALSE; /* Wait for a keypress */ pause_line(Term->hgt - 1); } } /* * Menus * * The standard menus are: * * Apple (128) = { About, -, ... } * File (129) = { New,Open,Import,Close,Save,-,Score,Quit } * Edit (130) = { Cut, Copy, Paste, Clear } (?) * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... } * Size (132) = { ... } * Window (133) = { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice, * Term-4, Term-5, Term-6, Term-7 } * Special (134) = { Sound, Graphics, TileWidth, TileHeight, -, * Fiddle, Wizard } */ /* Apple menu */ #define MENU_APPLE 128 #define ITEM_ABOUT 1 /* File menu */ #define MENU_FILE 129 # define ITEM_NEW 1 # define ITEM_OPEN 2 # define ITEM_IMPORT 3 # define ITEM_CLOSE 4 # define ITEM_SAVE 5 # ifdef HAS_SCORE_MENU # define ITEM_SCORE 7 # define ITEM_QUIT 8 # else # define ITEM_QUIT 7 # endif /* HAS_SCORE_MENU */ /* Edit menu */ #define MENU_EDIT 130 # define ITEM_UNDO 1 # define ITEM_CUT 3 # define ITEM_COPY 4 # define ITEM_PASTE 5 # define ITEM_CLEAR 6 /* Font menu */ #define MENU_FONT 131 # define ITEM_BOLD 1 # define ITEM_WIDE 2 /* Size menu */ #define MENU_SIZE 132 /* Windows menu */ #define MENU_WINDOWS 133 /* Special menu */ #define MENU_SPECIAL 134 # define ITEM_SOUND 1 # define ITEM_GRAPH 2 # define SUBMENU_GRAPH 144 # define ITEM_NONE 1 # define ITEM_8X8 2 # define ITEM_16X16 3 # define ITEM_32X32 4 # define ITEM_BIGTILE 6 # define ITEM_TILEWIDTH 3 # define SUBMENU_TILEWIDTH 145 # define ITEM_TILEHEIGHT 4 # define SUBMENU_TILEHEIGHT 146 # define ITEM_FIDDLE 6 # define ITEM_WIZARD 7 /* * I HATE UNICODE! We've never wanted it. Some multi-national companies * made it up as their internationalisation "solution". So I won't use * any such API's -- pelpel */ #define NSIZES 32 static byte menu_size_values[NSIZES]; static byte menu_tilewidth_values[NSIZES]; static byte menu_tileheight_values[NSIZES]; /* * Initialize the menus * * Fixed top level menus are now loaded all at once by GetNewMBar(). * Although this simplifies the function a bit, we have to make sure * that resources have all the expected entries defined XXX XXX */ static void init_menubar(void) { int i, n; Rect r; WindowPtr tmpw; MenuRef m; #ifdef USE_NIB /* The new way - loading main menu using Interface Builder services */ { IBNibRef nib; OSStatus err; /* Create a nib reference to the main nib file */ err = CreateNibReference(CFSTR("main"), &nib); /* Fatal error - missing Main.nib */ if (err != noErr) quit("Cannot find Main.nib in the bundle!"); /* Unarchive the menu bar and make it ready to use */ err = SetMenuBarFromNib(nib, CFSTR("MainMenu")); /* Fatal error - couldn't insert menu bar */ if (err != noErr) quit("Cannot prepare menu bar!"); /* Dispose of the nib reference because we don't need it any longer */ DisposeNibReference(nib); } #else /* USE_NIB */ /* The old way - loading main menu from Resource Manager resource */ { Handle mbar; /* Load menubar from resources */ mbar = GetNewMBar(128); /* Whoops! */ if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!"); /* Insert them into the current menu list */ SetMenuBar(mbar); /* Free handle */ DisposeHandle(mbar); } #endif /* USE_NIB */ /* Apple menu (id 128) - we don't have to do anything */ #ifndef USE_NIB /* File menu (id 129) - Aqua provides Quit menu for us */ if (is_aqua) { /* Get a handle to the file menu */ m = GetMenuHandle(MENU_FILE); /* Nuke the quit menu since Aqua does that for us */ DeleteMenuItem(m, ITEM_QUIT); #ifndef HAS_SCORE_MENU /* Hack - because the above leaves a separator as the last item */ DeleteMenuItem(m, ITEM_QUIT - 1); #endif /* !HAS_SCORE_MENU */ } #endif /* !USE_NIB */ /* Edit menu (id 130) - we don't have to do anything */ /* * Font menu (id 131) - append names of mono-spaced fonts * followed by all available ones */ m = GetMenuHandle(MENU_FONT); /* Fake window */ r.left = r.right = r.top = r.bottom = 0; /* Make the fake window so that we can retrieve font info */ (void)CreateNewWindow( kDocumentWindowClass, kWindowNoAttributes, &r, &tmpw); /* Activate the "fake" window */ SetPort(GetWindowPort(tmpw)); /* Default mode */ TextMode(0); /* Default size */ TextSize(12); /* Add the fonts to the menu */ AppendResMenu(m, 'FONT'); /* Size of menu */ n = CountMenuItems(m); /* Scan the menu */ for (i = n; i >= 4; i--) { Str255 tmpName; short fontNum; /* Acquire the font name */ GetMenuItemText(m, i, tmpName); /* Acquire the font index */ GetFNum(tmpName, &fontNum); /* Apply the font index */ TextFont(fontNum); /* Remove non-mono-spaced fonts */ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0)) { /* Delete the menu item */ DeleteMenuItem(m, i); } } /* Destroy the fake window */ DisposeWindow(tmpw); /* Add a separator */ AppendMenu(m, "\p-"); /* Add the fonts to the menu */ AppendResMenu(m, 'FONT'); #ifndef USE_NIB /* Size menu (id 132) */ m = GetMenuHandle(MENU_SIZE); /* Add some sizes (stagger choices) */ for (i = 8, n = 1; i <= 32; i += ((i / 16) + 1), n++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(m, buf); /* Remember its value, for we can't be sure it's in ASCII */ menu_size_values[n] = i; } #endif /* !USE_NIB */ /* Windows menu (id 133) */ m = GetMenuHandle(MENU_WINDOWS); /* Default choices */ for (i = 0; i < MAX_TERM_DATA; i++) { Str15 buf; /* Describe the item */ strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(m, buf); /* Command-Key shortcuts */ if (i < 8) SetItemCmd(m, i + 1, I2D(i)); } #ifndef USE_NIB # ifndef MAC_MPW /* CW or gcc -- Use recommended interface for hierarchical menus */ /* Special menu (id 134) */ m = GetMenuHandle(MENU_SPECIAL); /* Insert Graphics submenu (id 144) */ { MenuHandle submenu; /* Get the submenu */ submenu = GetMenu(SUBMENU_GRAPH); /* Insert it */ SetMenuItemHierarchicalMenu(m, ITEM_GRAPH, submenu); } /* Insert TileWidth submenu (id 145) */ { MenuHandle submenu; /* Get the submenu */ submenu = GetMenu(SUBMENU_TILEWIDTH); /* Add some sizes */ for (i = 4, n = 1; i <= 32; i++, n++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(submenu, buf); /* Remember its value, for we can't be sure it's in ASCII */ menu_tilewidth_values[n] = i; } /* Insert it */ SetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, submenu); } /* Insert TileHeight submenu (id 146) */ { MenuHandle submenu; /* Get the submenu */ submenu = GetMenu(SUBMENU_TILEHEIGHT); /* Add some sizes */ for (i = 4, n = 1; i <= 32; i++, n++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(submenu, buf); /* Remember its value, for we can't be sure it's in ASCII */ menu_tileheight_values[n] = i; } /* Insert it */ SetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, submenu); } # else /* !MAC_MPW */ /* XXX XXX */ /* MPW's Universal Interface doesn't understand some newer Carbon APIs */ /* Special menu (id 134) */ /* Get graphics (sub)menu (id 144) */ m = GetMenu(SUBMENU_GRAPH); /* Insert it as a submenu */ InsertMenu(m, hierMenu); /* Get TileWidth (sub)menu (id 145) */ m = GetMenu(SUBMENU_TILEWIDTH); /* Add some sizes */ for (i = 4, n = 1; i <= 32; i++, n++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(m, buf); /* Remember its value, for we can't be sure it's in ASCII */ menu_tilewidth_values[n] = i; } /* Insert it as a submenu */ InsertMenu(m, hierMenu); /* Get TileHeight (sub)menu (id 146) */ m = GetMenu(SUBMENU_TILEHEIGHT); /* Add some sizes */ for (i = 4, n = 1; i <= 32; i++, n++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(m, buf); /* Remember its value, for we can't be sure it's in ASCII */ menu_tileheight_values[n] = i; } /* Insert it as a submenu */ InsertMenu(m, hierMenu); # endif /* MAC_MPW */ #endif /* !USE_NIB */ /* Update the menu bar */ DrawMenuBar(); } /* * Prepare the menus * * It is very important that the player not be allowed to "save" the game * unless the "inkey_flag" variable is set, indicating that the game is * waiting for a new command. XXX XXX XXX */ static void setup_menus(void) { int i, n; short value; Str255 s; MenuHandle m; WindowRef w; term_data *td = NULL; /* Find window */ w = FrontWindow(); /* Relevant "term_data" */ if (w != NULL) td = (term_data *)GetWRefCon(w); /* File menu */ m = GetMenuHandle(MENU_FILE); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Enable "new"/"open..."/"import..." */ if (initialized && !game_in_progress) { EnableMenuItem(m, ITEM_NEW); EnableMenuItem(m, ITEM_OPEN); EnableMenuItem(m, ITEM_IMPORT); } /* Enable "close" */ if (initialized) { EnableMenuItem(m, ITEM_CLOSE); } /* Enable "save" */ if (initialized && character_generated && p_ptr->cmd.inkey_flag) { EnableMenuItem(m, ITEM_SAVE); } #ifdef HAS_SCORE_MENU /* Enable "score" */ if (initialized && character_generated && !character_icky) { EnableMenuItem(m, ITEM_SCORE); } #endif /* HAS_SCORE_MENU */ /* Enable "quit" */ if (!is_aqua) { if (!initialized || !character_generated || p_ptr->cmd.inkey_flag) { EnableMenuItem(m, ITEM_QUIT); } } /* Edit menu */ m = GetMenuHandle(MENU_EDIT); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Enable "edit" options if "needed" */ if (!td) { EnableMenuItem(m, ITEM_UNDO); EnableMenuItem(m, ITEM_CUT); EnableMenuItem(m, ITEM_COPY); EnableMenuItem(m, ITEM_PASTE); EnableMenuItem(m, ITEM_CLEAR); } /* Font menu */ m = GetMenuHandle(MENU_FONT); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, ITEM_BOLD, bold); */ /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, ITEM_WIDE, extend); */ /* Active window */ if (initialized && td) { /* Enable "bold" */ EnableMenuItem(m, ITEM_BOLD); /* Enable "extend" */ EnableMenuItem(m, ITEM_WIDE); /* Check the appropriate "bold-ness" */ if (td->font_face & bold) CheckMenuItem(m, ITEM_BOLD, TRUE); /* Check the appropriate "wide-ness" */ if (td->font_face & extend) CheckMenuItem(m, ITEM_WIDE, TRUE); /* Analyze fonts */ for (i = 4; i <= n; i++) { /* Enable it */ EnableMenuItem(m, i); /* Analyze font */ GetMenuItemText(m, i, s); GetFNum(s, &value); /* Check active font */ if (td->font_id == value) CheckMenuItem(m, i, TRUE); } } /* Size menu */ m = GetMenuHandle(MENU_SIZE); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Active window */ if (initialized && td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ value = menu_size_values[i]; /* Enable the "real" sizes */ if (RealFont(td->font_id, value)) EnableMenuItem(m, i); /* Check the current size */ if (td->font_size == value) CheckMenuItem(m, i, TRUE); } } /* Windows menu */ m = GetMenuHandle(MENU_WINDOWS); /* Get menu size */ n = CountMenuItems(m); /* Check active windows */ for (i = 1; i <= n; i++) { /* Check if needed */ CheckMenuItem(m, i, data[i-1].mapped); } /* Special menu */ m = GetMenuHandle(MENU_SPECIAL); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); #ifdef MAC_MPW /* MPW's Universal Interface is a bit out of date */ /* XXX Oh no, this removes submenu... */ if ((i != ITEM_GRAPH) && (i != ITEM_TILEWIDTH) && (i != ITEM_TILEHEIGHT)) CheckMenuItem(m, i, FALSE); #else CheckMenuItem(m, i, FALSE); #endif } /* Item "arg_sound" */ EnableMenuItem(m, ITEM_SOUND); CheckMenuItem(m, ITEM_SOUND, arg_sound); /* Item "Graphics" */ EnableMenuItem(m, ITEM_GRAPH); { MenuRef submenu; #ifdef MAC_MPW /* MPW's Universal Interface is a bit out of date */ /* Graphics submenu */ submenu = GetMenuHandle(SUBMENU_GRAPH); #else /* Graphics submenu */ (void)GetMenuItemHierarchicalMenu(m, ITEM_GRAPH, &submenu); #endif /* Get menu size */ n = CountMenuItems(submenu); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(submenu, i); CheckMenuItem(submenu, i, FALSE); } /* Item "None" */ EnableMenuItem(submenu, ITEM_NONE); CheckMenuItem(submenu, ITEM_NONE, (graf_mode_req == GRAF_MODE_NONE)); /* Item "8x8" */ EnableMenuItem(submenu, ITEM_8X8); CheckMenuItem(submenu, ITEM_8X8, (graf_mode_req == GRAF_MODE_8X8)); /* Item "16x16" */ EnableMenuItem(submenu, ITEM_16X16); CheckMenuItem(submenu, ITEM_16X16, (graf_mode_req == GRAF_MODE_16X16)); /* Item "32x32" */ EnableMenuItem(submenu, ITEM_32X32); CheckMenuItem(submenu, ITEM_32X32, (graf_mode_req == GRAF_MODE_32X32)); #ifdef USE_DOUBLE_TILES /* Item "Big tiles" */ if (inkey_flag) EnableMenuItem(submenu, ITEM_BIGTILE); CheckMenuItem(submenu, ITEM_BIGTILE, use_bigtile); #endif /* USE_DOUBLE_TILES */ } /* Item "TileWidth" */ EnableMenuItem(m, ITEM_TILEWIDTH); { MenuRef submenu; #ifdef MAC_MPW /* MPW's Universal Interface is a bit out of date */ /* TIleWidth submenu */ submenu = GetMenuHandle(SUBMENU_TILEWIDTH); #else /* TileWidth submenu */ (void)GetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, &submenu); #endif /* Get menu size */ n = CountMenuItems(submenu); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(submenu, i); CheckMenuItem(submenu, i, FALSE); } /* Active window */ if (initialized && td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ value = menu_tilewidth_values[i]; /* Enable */ if (value >= td->font_wid) EnableMenuItem(submenu, i); /* Check the current size */ if (td->tile_wid == value) CheckMenuItem(submenu, i, TRUE); } } } /* Item "TileHeight" */ EnableMenuItem(m, ITEM_TILEHEIGHT); { MenuRef submenu; #ifdef MAC_MPW /* MPW's Universal Interface is a bit out of date */ /* TileHeight submenu */ submenu = GetMenuHandle(SUBMENU_TILEHEIGHT); #else /* TileWidth submenu */ (void)GetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, &submenu); #endif /* Get menu size */ n = CountMenuItems(submenu); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(submenu, i); CheckMenuItem(submenu, i, FALSE); } /* Active window */ if (initialized && td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ value = menu_tileheight_values[i]; /* Enable */ if (value >= td->font_hgt) EnableMenuItem(submenu, i); /* Check the current size */ if (td->tile_hgt == value) CheckMenuItem(submenu, i, TRUE); } } } /* Item "arg_fiddle" */ EnableMenuItem(m, ITEM_FIDDLE); CheckMenuItem(m, ITEM_FIDDLE, arg_fiddle); /* Item "arg_wizard" */ EnableMenuItem(m, ITEM_WIZARD); CheckMenuItem(m, ITEM_WIZARD, arg_wizard); } /* * Process a menu selection (see above) * * Hack -- assume that invalid menu selections are disabled above, * which I have been informed may not be reliable. XXX XXX XXX */ static void menu(long mc) { int i; int menuid, selection; static unsigned char s[1000]; short fid; term_data *td = NULL; WindowPtr old_win; /* Analyze the menu command */ menuid = HiWord(mc); selection = LoWord(mc); /* Find the window XXX XXX Declare another variable */ old_win = FrontWindow(); /* Relevant "term_data" */ if (old_win) td = (term_data *)GetWRefCon(old_win); /* Branch on the menu */ switch (menuid) { /* Apple Menu */ case MENU_APPLE: { /* About Angband... */ if (selection == ITEM_ABOUT) { DialogPtr dialog; short item_hit; /* Get the about dialogue */ dialog = GetNewDialog(128, 0, (WindowPtr)-1); /* Move it to the middle of the screen */ RepositionWindow( GetDialogWindow(dialog), NULL, kWindowCenterOnMainScreen); /* Show the dialog */ TransitionWindow(GetDialogWindow(dialog), kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL); /* Wait for user to click on it */ ModalDialog(0, &item_hit); /* Free the dialogue */ DisposeDialog(dialog); break; } break; } /* File Menu */ case MENU_FILE: { switch (selection) { /* New */ case ITEM_NEW: { do_menu_file_new(); break; } /* Open... */ case ITEM_OPEN: { do_menu_file_open(FALSE); break; } /* Import... */ case ITEM_IMPORT: { do_menu_file_open(TRUE); break; } /* Close */ case ITEM_CLOSE: { /* No window */ if (!td) break; /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ TransitionWindow(td->w, kWindowZoomTransitionEffect, kWindowHideTransitionAction, NULL); break; } /* Save */ case ITEM_SAVE: { /* Hack -- Forget messages */ msg_flag = FALSE; /* Hack -- Save the game */ #ifndef ZANG_AUTO_SAVE do_cmd_save_game(); #else do_cmd_save_game(FALSE); #endif /* !ZANG_AUTO_SAVE */ break; } #ifdef HAS_SCORE_MENU /* Show score */ case ITEM_SCORE: { char buf[1024]; /* Paranoia */ if (!initialized || character_icky || !game_in_progress || !character_generated) { /* Can't happen but just in case */ plog("You may not do that right now."); break; } /* Build the pathname of the score file */ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw"); /* Hack - open the score file for reading */ highscore_fd = fd_open(buf, O_RDONLY); /* Paranoia - No score file */ if (highscore_fd < 0) { msg_print("Score file is not available."); break; } /* Mega-Hack - prevent various functions XXX XXX XXX */ initialized = FALSE; /* Save screen */ screen_save(); /* Clear screen */ Term_clear(); /* Prepare scores */ if (game_in_progress && character_generated) { predict_score(); } #if 0 /* I don't like this - pelpel */ /* Mega-Hack - No current player XXX XXX XXX XXX */ else { display_scores_aux(0, MAX_HISCORES, -1, NULL); } #endif /* Close the high score file */ (void)fd_close(highscore_fd); /* Forget the fd */ highscore_fd = -1; /* Restore screen */ screen_load(); /* Hack - Flush it */ Term_fresh(); /* Mega-Hack - We are ready again */ initialized = TRUE; /* Done */ break; } #endif /* HAS_SCORE_MENU */ /* Quit (with save) */ case ITEM_QUIT: { /* Save the game (if necessary) */ if (game_in_progress && character_generated) { /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifndef ZANG_AUTO_SAVE do_cmd_save_game(); #else do_cmd_save_game(FALSE); #endif /* !ZANG_AUTO_SAVE */ } /* Quit */ quit(NULL); break; } } break; } /* Edit menu */ case MENU_EDIT: { /* Unused */ break; } /* Font menu */ case MENU_FONT: { /* Require a window */ if (!td) break; /* Memorize old */ old_win = active; /* Activate */ activate(td->w); /* Toggle the "bold" setting */ if (selection == ITEM_BOLD) { /* Toggle the setting */ if (td->font_face & bold) { td->font_face &= ~bold; } else { td->font_face |= bold; } /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Toggle the "wide" setting */ if (selection == ITEM_WIDE) { /* Toggle the setting */ if (td->font_face & extend) { td->font_face &= ~extend; } else { td->font_face |= extend; } /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Get a new font name */ GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s); GetFNum(s, &fid); /* Save the new font id */ td->font_id = fid; /* Current size is bad for new font */ if (!RealFont(td->font_id, td->font_size)) { /* Find similar size */ for (i = 1; i <= 32; i++) { /* Adjust smaller */ if (td->font_size - i >= 8) { if (RealFont(td->font_id, td->font_size - i)) { td->font_size -= i; break; } } /* Adjust larger */ if (td->font_size + i <= 128) { if (RealFont(td->font_id, td->font_size + i)) { td->font_size += i; break; } } } } /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore the window */ activate(old_win); break; } /* Size menu */ case MENU_SIZE: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); td->font_size = menu_size_values[selection]; /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* Window menu */ case MENU_WINDOWS: { /* Parse */ i = selection - 1; /* Check legality of choice */ if ((i < 0) || (i >= MAX_TERM_DATA)) break; /* Obtain the window */ td = &data[i]; /* Mapped */ td->mapped = TRUE; /* Link */ term_data_link(i); /* Mapped (?) */ td->t->mapped_flag = TRUE; /* Show the window */ TransitionWindow(td->w, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL); /* Bring to the front */ SelectWindow(td->w); break; } /* Special menu */ case MENU_SPECIAL: { switch (selection) { case ITEM_SOUND: { /* Toggle arg_sound */ arg_sound = !arg_sound; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); break; } case ITEM_FIDDLE: { arg_fiddle = !arg_fiddle; break; } case ITEM_WIZARD: { arg_wizard = !arg_wizard; break; } } break; } /* Graphics submenu */ case SUBMENU_GRAPH: { switch (selection) { case ITEM_NONE: { graf_mode_req = GRAF_MODE_NONE; break; } case ITEM_8X8: { graf_mode_req = GRAF_MODE_8X8; break; } case ITEM_16X16: { graf_mode_req = GRAF_MODE_16X16; break; } case ITEM_32X32: { graf_mode_req = GRAF_MODE_32X32; break; } #ifdef USE_DOUBLE_TILES case ITEM_BIGTILE: { term *old = Term; term_data *td = &data[0]; /* Toggle "use_bigtile" */ use_bigtile = !use_bigtile; /* Activate */ Term_activate(td->t); /* Resize the term */ Term_resize(td->cols, td->rows); /* Activate old */ Term_activate(old); break; } #endif /* USE_DOUBLE_TILES */ } /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } /* TileWidth menu */ case SUBMENU_TILEWIDTH: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); /* Analyse value */ td->tile_wid = menu_tilewidth_values[selection]; /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* TileHeight menu */ case SUBMENU_TILEHEIGHT: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); /* Analyse value */ td->tile_hgt = menu_tileheight_values[selection]; /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } } /* Clean the menu */ HiliteMenu(0); } /* * Check for extra required parameters -- From "Maarten Hazewinkel" */ static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent) { OSErr aeError; DescType returnedType; Size actualSize; aeError = AEGetAttributePtr( theAppleEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize); if (aeError == errAEDescNotFound) return (noErr); if (aeError == noErr) return (errAEParamMissed); return (aeError); } /* * Apple Event Handler -- Open Application */ static pascal OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply, SInt32 handlerRefCon) { #pragma unused(reply) #pragma unused(handlerRefCon) return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Quit Application */ static pascal OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply, SInt32 handlerRefCon) { #pragma unused(reply) #pragma unused(handlerRefCon) /* Quit later */ quit_when_ready = TRUE; /* Check arguments */ return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Print Documents */ static pascal OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply, SInt32 handlerRefCon) { #pragma unused(theAppleEvent) #pragma unused(reply) #pragma unused(handlerRefCon) return (errAEEventNotHandled); } /* * Apple Event Handler by Steve Linberg (slinberg@crocker.com). * * The old method of opening savefiles from the finder does not work * on the Power Macintosh, because CountAppFiles and GetAppFiles, * used to return information about the selected document files when * an application is launched, are part of the Segment Loader, which * is not present in the RISC OS due to the new memory architecture. * * The "correct" way to do this is with AppleEvents. The following * code is modeled on the "Getting Files Selected from the Finder" * snippet from Think Reference 2.0. (The prior sentence could read * "shamelessly swiped & hacked") */ static pascal OSErr AEH_Open(const AppleEvent *theAppleEvent, AppleEvent* reply, SInt32 handlerRefCon) { FSSpec myFSS; AEDescList docList; OSErr err; Size actualSize; AEKeyword keywd; DescType returnedType; char msg[128]; FInfo myFileInfo; #pragma unused(reply) #pragma unused(handlerRefCon) /* Put the direct parameter (a descriptor list) into a docList */ err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &docList); if (err) return err; /* * We ignore the validity check, because we trust the FInder, and we only * allow one savefile to be opened, so we ignore the depth of the list. */ err = AEGetNthPtr( &docList, 1L, typeFSS, &keywd, &returnedType, (Ptr) &myFSS, sizeof(myFSS), &actualSize); if (err) return err; /* Only needed to check savefile type below */ err = FSpGetFInfo(&myFSS, &myFileInfo); if (err) { strnfmt(msg, sizeof(msg), "Argh! FSpGetFInfo failed with code %d", err); mac_warning(msg); return err; } /* Ignore non 'SAVE' files */ if (myFileInfo.fdType != 'SAVE') return noErr; #ifdef MACH_O_CARBON /* Extract a file name */ (void)spec_to_path(&myFSS, savefile, sizeof(savefile)); #else /* XXX XXX XXX Extract a file name */ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile); pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name); /* Convert the string */ ptocstr((StringPtr)savefile); #endif /* MACH_O_CARBON */ /* Delay actual open */ open_when_ready = TRUE; /* Dispose */ err = AEDisposeDesc(&docList); /* Success */ return noErr; } /* * Apple Event Handler -- Re-open Application * * If no windows are currently open, show the Angband window. * This required AppleEvent was introduced by System 8 -- pelpel */ static pascal OSErr AEH_Reopen(const AppleEvent *theAppleEvent, AppleEvent* reply, long handlerRefCon) { #pragma unused(theAppleEvent, reply, handlerRefCon) term_data *td = NULL; /* No open windows */ if (NULL == FrontWindow()) { /* Obtain the Angband window */ td = &data[0]; /* Mapped */ td->mapped = TRUE; /* Link */ term_data_link(0); /* Mapped (?) */ td->t->mapped_flag = TRUE; /* Show the window */ ShowWindow(td->w); /* Bring to the front */ SelectWindow(td->w); /* Make it active */ activate(td->w); } /* Event handled */ return (noErr); } /* * Handle quit_when_ready, by Peter Ammon, * slightly modified to check inkey_flag. */ static void quit_calmly(void) { /* Quit immediately if game's not started */ if (!game_in_progress || !character_generated) quit(NULL); /* Save the game and Quit (if it's safe) */ if (p_ptr->cmd.inkey_flag) { /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifndef ZANG_AUTO_SAVE do_cmd_save_game(); #else do_cmd_save_game(FALSE); #endif /* !ZANG_AUTO_SAVE */ /* Quit */ quit(NULL); } /* Wait until inkey_flag is set */ } /* * Macintosh modifiers (event.modifier & ccc): * cmdKey, optionKey, shiftKey, alphaLock, controlKey * * * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra): * * Return:36 * Delete:51 * * Period:65 * Star:67 * Plus:69 * Clear:71 * Slash:75 * Enter:76 * Minus:78 * Equal:81 * 0-7:82-89 * 8-9:91-92 * * backslash/vertical bar (Japanese keyboard):93 * * F5: 96 * F6: 97 * F7: 98 * F3:99 * F8:100 * F10:101 * F11:103 * F13:105 * F14:107 * F9:109 * F12:111 * F15:113 * Help:114 * Home:115 * PgUp:116 * Del:117 * F4: 118 * End:119 * F2:120 * PgDn:121 * F1:122 * Lt:123 * Rt:124 * Dn:125 * Up:126 */ /* * Optimize non-blocking calls to "CheckEvents()" * Idea from "Maarten Hazewinkel " * * WAS: 6. The value of one (~ 60 FPS) seems to work better with the Borg, * and so should be for other CPU-intensive features like the autoroller. */ #define EVENT_TICKS 1 /* * Check for Events, return TRUE if we process any */ static bool CheckEvents(int wait) { EventRecord event; WindowPtr w; Rect r; UInt32 sleep_ticks; int ch, ck; int mc, ms, mo, mx; term_data *td = NULL; UInt32 curTicks; static UInt32 lastTicks = 0L; /* Access the clock */ curTicks = TickCount(); /* Hack -- Allow efficient checking for non-pending events */ if ((wait == CHECK_EVENTS_NO_WAIT) && (curTicks < lastTicks + EVENT_TICKS)) return (FALSE); /* Timestamp last check */ lastTicks = curTicks; /* Handles the quit_when_ready flag */ if (quit_when_ready) quit_calmly(); /* Blocking call to WaitNextEvent - should use MAX_INT XXX XXX */ if (wait == CHECK_EVENTS_WAIT) sleep_ticks = 0x7FFFFFFFL; /* Non-blocking */ else sleep_ticks = 0L; /* Get an event (or null) */ WaitNextEvent(everyEvent, &event, sleep_ticks, nil); /* Hack -- Nothing is ready yet */ if (event.what == nullEvent) return (FALSE); /* Analyze the event */ switch (event.what) { #if 0 case activateEvt: { w = (WindowPtr)event.message; activate(w); break; } #endif case updateEvt: { /* Extract the window */ w = (WindowPtr)event.message; /* Relevant "term_data" */ td = (term_data *)GetWRefCon(w); /* Clear window's update region and clip drawings with it */ BeginUpdate(w); /* Redraw the window */ if (td) term_data_redraw(td); /* Restore window's clipping region */ EndUpdate(w); break; } case keyDown: case autoKey: { /* Extract some modifiers */ mc = (event.modifiers & controlKey) ? TRUE : FALSE; ms = (event.modifiers & shiftKey) ? TRUE : FALSE; mo = (event.modifiers & optionKey) ? TRUE : FALSE; mx = (event.modifiers & cmdKey) ? TRUE : FALSE; /* Keypress: (only "valid" if ck < 96) */ ch = (event.message & charCodeMask) & 255; /* Keycode: see table above */ ck = ((event.message & keyCodeMask) >> 8) & 255; /* Command + "normal key" -> menu action */ if (mx && (ck < 64)) { /* Hack -- Prepare the menus */ setup_menus(); /* Run the Menu-Handler */ menu(MenuKey(ch)); /* Turn off the menus */ HiliteMenu(0); /* Done */ break; } /* Hide the mouse pointer */ ObscureCursor(); /* Normal key -> simple keypress */ if ((ck < 64) || (ck == 93)) { /* Enqueue the keypress */ Term_keypress(ch); } /* Keypad keys -> trigger plus simple keypress */ else if (!mc && !ms && !mo && !mx && (ck < 96)) { /* Hack -- "enter" is confused */ if (ck == 76) ch = '\n'; /* Begin special trigger */ Term_keypress(31); /* Send the "keypad" modifier */ Term_keypress('K'); /* Terminate the trigger */ Term_keypress(13); /* Send the "ascii" keypress */ Term_keypress(ch); } /* Bizarre key -> encoded keypress */ else if (ck <= 127) { /* Begin special trigger */ Term_keypress(31); /* Send some modifier keys */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (mo) Term_keypress('O'); if (mx) Term_keypress('X'); /* Downshift and encode the keycode */ Term_keypress(I2D((ck - 64) / 10)); Term_keypress(I2D((ck - 64) % 10)); /* Terminate the trigger */ Term_keypress(13); } break; } case mouseDown: { int code; /* Analyze click location */ code = FindWindow(event.where, &w); /* Relevant "term_data" */ td = (term_data *)GetWRefCon(w); /* Analyze */ switch (code) { case inMenuBar: { setup_menus(); menu(MenuSelect(event.where)); HiliteMenu(0); break; } case inDrag: { WindowPtr old_win; BitMap tBitMap; Rect pRect; r = GetQDGlobalsScreenBits(&tBitMap)->bounds; r.top += 20; /* GetMBarHeight() XXX XXX XXX */ InsetRect(&r, 4, 4); DragWindow(w, event.where, &r); /* Oops */ if (!td) break; /* Save */ old_win = active; /* Activate */ activate(td->w); /* Analyze */ GetWindowBounds( (WindowRef)td->w, kWindowContentRgn, &pRect); td->r.left = pRect.left; td->r.top = pRect.top; /* Apply and Verify */ term_data_check_size(td); /* Restore */ activate(old_win); break; } case inGoAway: { /* Oops */ if (!td) break; /* Track the go-away box */ if (TrackGoAway(w, event.where)) { /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ TransitionWindow(td->w, kWindowZoomTransitionEffect, kWindowHideTransitionAction, NULL); } break; } case inGrow: { int x, y; Rect nr; term *old = Term; /* Oops */ if (!td) break; #ifndef ALLOW_BIG_SCREEN /* Minimum and maximum sizes */ r.left = 20 * td->tile_wid + td->size_ow1; r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1; r.top = 1 * td->tile_hgt + td->size_oh1; r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1; /* Grow the rectangle */ if (!ResizeWindow(w, event.where, &r, NULL)) break; #else /* Grow the rectangle */ if (!ResizeWindow(w, event.where, NULL, NULL)) break; #endif /* !ALLOW_BIG_SCREEN */ /* Obtain geometry of resized window */ GetWindowBounds(w, kWindowContentRgn, &nr); /* Extract the new size in pixels */ y = nr.bottom - nr.top - td->size_oh1 - td->size_oh2; x = nr.right - nr.left - td->size_ow1 - td->size_ow2; /* Extract a "close" approximation */ td->rows = y / td->tile_hgt; td->cols = x / td->tile_wid; /* Apply and Verify */ term_data_check_size(td); /* Activate */ Term_activate(td->t); /* Hack -- Resize the term */ Term_resize(td->cols, td->rows); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ Term_activate(old); break; } case inContent: { SelectWindow(w); break; } } break; } /* OS Event -- From "Maarten Hazewinkel" */ case osEvt: { switch ((event.message >> 24) & 0x000000FF) { case suspendResumeMessage: /* Resuming: activate the front window */ if (event.message & resumeFlag) { Cursor tempCursor; WindowRef w; /* Find the window */ w = FrontWindow(); if (w != NULL) { /* Relevant "term_data" */ td = (term_data *)GetWRefCon(FrontWindow()); /* Activate the window */ SetPort(GetWindowPort(td->w)); /* Mega-Hack -- Synchronise 'active' */ active = td->w; SetCursor(GetQDGlobalsArrow(&tempCursor)); /* Synchronise term */ Term_activate(td->t); } } /* Suspend: deactivate the front window */ else { /* Nothing */ } break; } break; } /* From "Steve Linberg" and "Maarten Hazewinkel" */ case kHighLevelEvent: { /* Process apple events */ (void)AEProcessAppleEvent(&event); /* Handle "quit_when_ready" */ if (quit_when_ready) { #if 0 /* Doesn't work with Aqua well */ /* Forget */ quit_when_ready = FALSE; /* Do the menu key */ menu(MenuKey('q')); #endif /* Turn off the menus */ HiliteMenu(0); } /* Handle "open_when_ready" */ else if (open_when_ready) { handle_open_when_ready(); } break; } } /* Something happened */ return (TRUE); } /*** Some Hooks for various routines ***/ /* * Mega-Hack -- emergency lifeboat */ static void *lifeboat = NULL; /* * Hook to "release" memory */ #ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */ static void *hook_rnfree(void *v) #else static void *hook_rnfree(void *v, huge size) #endif /* NEW_ZVIRT_HOOKS */ { #ifdef USE_MALLOC /* Alternative method */ free(v); #else /* Dispose */ DisposePtr(v); #endif /* Success */ return (NULL); } /* * Hook to "allocate" memory */ static void *hook_ralloc(huge size) { #ifdef USE_MALLOC /* Make a new pointer */ return (malloc(size)); #else /* Make a new pointer */ return (NewPtr(size)); #endif } /* * Hook to handle "out of memory" errors */ static void *hook_rpanic(huge size) { #pragma unused(size) /* Free the lifeboat */ if (lifeboat) { /* Free the lifeboat */ DisposePtr(lifeboat); /* Forget the lifeboat */ lifeboat = NULL; /* Mega-Hack -- Warning */ mac_warning("Running out of Memory!\rAbort this process now!"); /* Mega-Hack -- Never leave this function */ while (TRUE) CheckEvents(CHECK_EVENTS_WAIT); } /* Mega-Hack -- Crash */ return (NULL); } /* * Hook to tell the user something important */ static void hook_plog(cptr str) { /* Warning message */ mac_warning(str); } /* * Hook to tell the user something, and then quit */ static void hook_quit(cptr str) { /* Warning if needed */ if (str) mac_warning(str); #ifdef USE_ASYNC_SOUND /* Clean up sound support */ cleanup_sound(); #endif /* USE_ASYNC_SOUND */ /* Dispose of graphic tiles */ if (frameP) { /* Unlock */ BenSWUnlockFrame(frameP); /* Dispose of the GWorld */ DisposeGWorld(frameP->framePort); /* Dispose of the memory */ DisposePtr((Ptr)frameP); } /* Write a preference file */ if (initialized) save_pref_file(); /* All done */ ExitToShell(); } /* * Hook to tell the user something, and then crash */ static void hook_core(cptr str) { /* XXX Use the debugger */ /* DebugStr(str); */ /* Warning */ if (str) mac_warning(str); /* Warn, then save player */ mac_warning("Fatal error.\rI will now attempt to save and quit."); /* Attempt to save */ if (!save_player()) mac_warning("Warning -- save failed!"); /* Quit */ quit(NULL); } /*** Main program ***/ /* * Init some stuff * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "Macintosh Save Bug" by using "absolute" path names, since on * System 7 machines anyway, the "current working directory" often * "changes" due to background processes, invalidating any "relative" * path names. Note that the Macintosh is limited to 255 character * path names, so be careful about deeply embedded directories... * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "missing lib folder bug" by allowing the user to help find the * "lib" folder by hand if the "application folder" code fails... * * * The problem description above no longer applies, but I left it here, * modified for Carbon, to allow the game proceeds when a user doesn't * placed the Angband binary and the lib folder in the same place for * whatever reasons. -- pelpel */ static void init_stuff(void) { Rect r; BitMap tBitMap; Rect screenRect; Point topleft; char path[1024]; OSErr err = noErr; NavDialogOptions dialogOptions; FSSpec theFolderSpec; NavReplyRecord theReply; /* Fake rectangle */ r.left = 0; r.top = 0; r.right = 344; r.bottom = 188; /* Center it */ screenRect = GetQDGlobalsScreenBits(&tBitMap)->bounds; center_rect(&r, &screenRect); /* Extract corner */ topleft.v = r.top; topleft.h = r.left; /* Default to the "lib" folder with the application */ #ifdef MACH_O_CARBON if (locate_lib(path, sizeof(path)) == NULL) quit(NULL); #else /* Metrowerks uses colon-separated path */ refnum_to_name(path, app_dir, app_vol, (char*)("\plib:")); #endif /* Check until done */ while (1) { /* Create directories for the users files */ create_user_dirs(); /* Prepare the paths */ init_file_paths(path); /* Build the filename */ path_build(path, sizeof(path), ANGBAND_DIR_FILE, "news.txt"); /* Attempt to open and close that file */ if (0 == fd_close(fd_open(path, O_RDONLY))) break; /* Warning */ plog_fmt("Unable to open the '%s' file.", path); /* Warning */ plog("The ZAngband 'lib' folder is probably missing or misplaced."); /* Ask the user to choose the lib folder */ err = NavGetDefaultDialogOptions(&dialogOptions); /* Paranoia */ if (err != noErr) quit(NULL); /* Set default location option */ dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation; /* Clear preview option */ dialogOptions.dialogOptionFlags &= ~(kNavAllowPreviews); /* Forbit selection of multiple files */ dialogOptions.dialogOptionFlags &= ~(kNavAllowMultipleFiles); /* Display location */ dialogOptions.location = topleft; #if 0 /* Load the message for the missing folder from the resource fork */ /* GetIndString(dialogOptions.message, 128, 1); */ #else /* Set the message for the missing folder XXX XXX */ strcpy((char *)dialogOptions.message + 1, "Please select the \"lib\" folder"); dialogOptions.message[0] = strlen((char *)dialogOptions.message + 1); #endif /* Wait for the user to choose a folder */ err = NavChooseFolder( nil, &theReply, &dialogOptions, nil, nil, nil); /* Assume the player doesn't want to go on */ if ((err != noErr) || !theReply.validRecord) quit(NULL); /* Retrieve FSSpec from the reply */ { AEKeyword theKeyword; DescType actualType; Size actualSize; /* Get a pointer to selected folder */ err = AEGetNthPtr( &(theReply.selection), 1, typeFSS, &theKeyword, &actualType, &theFolderSpec, sizeof(FSSpec), &actualSize); /* Paranoia */ if (err != noErr) quit(NULL); } /* Free navitagor reply */ err = NavDisposeReply(&theReply); /* Paranoia */ if (err != noErr) quit(NULL); #ifdef MACH_O_CARBON /* Extract textual file name for given file */ if (spec_to_path(&theFolderSpec, path, sizeof(path)) != noErr) { quit(NULL); } #else /* MACH_O_CARBON */ /* Extract textual file name for given file */ refnum_to_name( path, theFolderSpec.parID, theFolderSpec.vRefNum, (char *)theFolderSpec.name); #endif /* MACH_O_CARBON */ } } /* * Macintosh Main loop */ int main(void) { long response; OSStatus err; UInt32 numberOfMasters = 10; /* Get more Masters -- it is not recommended by Apple, should go away */ MoreMasterPointers(numberOfMasters); /* Flush events */ FlushEvents(everyEvent, 0); /* Initialise the cursor and turn it into an "arrow" */ InitCursor(); /* Check for existence of Carbon */ err = Gestalt(gestaltCarbonVersion, &response); if (err != noErr) quit("This program requires Carbon API"); /* See if we are running on Aqua */ err = Gestalt(gestaltMenuMgrAttr, &response); /* Cache the result */ if ((err == noErr) && (response & gestaltMenuMgrAquaLayoutMask)) is_aqua = TRUE; /* * Remember Mac OS version, in case we have to cope with version-specific * problems */ (void)Gestalt(gestaltSystemVersion, &mac_os_version); /* Install the start event hook (ignore error codes) */ (void)AEInstallEventHandler( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP(AEH_Start), 0L, FALSE); /* Install the quit event hook (ignore error codes) */ (void)AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(AEH_Quit), 0L, FALSE); /* Install the print event hook (ignore error codes) */ (void)AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerUPP(AEH_Print), 0L, FALSE); /* Install the open event hook (ignore error codes) */ (void)AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(AEH_Open), 0L, FALSE); /* Install the Re-open event hook (ignore error codes) */ (void)AEInstallEventHandler( kCoreEventClass, kAEReopenApplication, NewAEEventHandlerUPP(AEH_Reopen), 0L, FALSE); #ifndef MACH_O_CARBON /* Find the current application */ SetupAppDir(); #endif /* !MACH_O_CARBON */ /* Mark ourself as the file creator */ _fcreator = ANGBAND_CREATOR; /* Default to saving a "text" file */ _ftype = 'TEXT'; /* Hook in some "z-virt.c" hooks */ rnfree_aux = hook_rnfree; ralloc_aux = hook_ralloc; rpanic_aux = hook_rpanic; /* Hooks in some "z-util.c" hooks */ plog_aux = hook_plog; quit_aux = hook_quit; core_aux = hook_core; /* Initialize colors */ update_colour_info(); /* Show the "watch" cursor */ SetCursor(*(GetCursor(watchCursor))); /* Prepare the menubar */ init_menubar(); /* Prepare the windows */ init_windows(); /* Hack -- process all events */ while (CheckEvents(CHECK_EVENTS_DRAIN)) /* loop */; /* Reset the cursor */ { Cursor tempCursor; SetCursor(GetQDGlobalsArrow(&tempCursor)); } /* Mega-Hack -- Allocate a "lifeboat" */ lifeboat = NewPtr(16384); #ifdef USE_QT_SOUND /* Load sound effect resources */ load_sounds(); #endif /* USE_QT_SOUND */ /* Note the "system" */ ANGBAND_SYS = "mac"; /* Initialize */ init_stuff(); /* Initialize */ init_angband(); /* Validate the contents of the main window */ validate_main_window(); /* Hack -- process all events */ while (CheckEvents(CHECK_EVENTS_DRAIN)) /* loop */; /* We are now initialized */ initialized = TRUE; /* Handle "open_when_ready" */ handle_open_when_ready(); /* Let the player choose a savefile or start a new game */ if (!game_in_progress) { /* Prompt the user - You may have to change this for some variants */ prtf(15, 23, "[Choose 'New' or 'Open' from the 'File' menu]"); /* Flush the prompt */ Term_fresh(); /* Hack -- Process Events until "new" or "open" is selected */ while (!game_in_progress) CheckEvents(CHECK_EVENTS_WAIT); } /* Handle pending events (most notably update) and flush input */ Term_flush(); /* * Play a game -- "new_game" is set by "new", "open" or the open document * even handler as appropriate */ play_game(new_game); /* Quit */ quit(NULL); /* Since it's an int function */ return (0); } #endif /* MACINTOSH || MACH_O_CARBON */ zangband/src/main-dos.c0000755000000000000000000011660210250356274014017 0ustar rootroot/* File: main-dos.c */ /* * Copyright (c) 1997 Ben Harrison, Robert Ruehlmann, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with DOS computers. * * Adapted from "main-ibm.c". * * Author: Robert Ruehlmann (rr9@thangorodrim.net). * See "http://www.thangorodrim.net/". * * Initial framework (and some code) by Ben Harrison (benh@phial.com). * * This file requires the (free) DJGPP compiler (based on "gcc"). * See "http://www.delorie.com/djgpp/". * * This file uses the (free) "Allegro" library (SVGA graphics library). * See "http://www.talula.demon.co.uk/allegro/". * * To compile this file, use "Makefile.dos", which defines "USE_DOS". * * See also "main-ibm.c" and "main-win.c". * * * The "lib/user/pref.prf" file contains macro definitions and possible * alternative color set definitions. * * The "lib/user/font.prf" contains attr/char mappings for use with the * special fonts in the "lib/xtra/font/" directory. * * The "lib/user/graf.prf" contains attr/char mappings for use with the * special bitmaps in the "lib/xtra/graf/" directory. * * * Both "shift" keys are treated as "identical", and all the modifier keys * (control, shift, alt) are ignored when used with "normal" keys, unless * they modify the underlying "ascii" value of the key. You must use the * new "user pref files" to be able to interact with the keypad and such. * * Note that "Term_xtra_dos_react()" allows runtime color, graphics, * screen resolution, and sound modification. * * * The sound code uses *.wav files placed in the "lib/xtra/sound" folder. * Every sound-event can have several samples assigned to it and a random * one will be played when the event occures. Look at the * "lib/xtra/sound/sound.cfg" configuration file for more informations. * * The background music uses midi-files (and mod files) from the * "lib/xtra/music" folder. * * * Comment by Ben Harrison (benh@phial.com): * * On my Windows NT box, modes "VESA1" and "VESA2B" seem to work, but * result in the game running with no visible output. Mode "VESA2L" * clears the screen and then fails silently. All other modes fail * instantly. To recover from such "invisible" modes, you can try * typing escape, plus control-x, plus escape. XXX XXX XXX */ #include "angband.h" #ifdef USE_DOS cptr help_dos[] = { "To use DOS (Graphics)", NULL }; #include #ifdef USE_MOD_FILES #include #endif /* USE_MOD_FILES */ #include #include #include #include #include /* * Index of the first standard Angband color. * * All colors below this index are defined by * the palette of the tiles-bitmap. */ #define COLOR_OFFSET 240 /* * Maximum number of terminals */ #define MAX_TERM_DATA 8 /* * Forward declare */ typedef struct term_data term_data; /* * Extra "term" data */ struct term_data { term t; int number; int x; int y; int cols; int rows; int tile_wid; int tile_hgt; int font_wid; int font_hgt; FONT *font; #ifdef USE_GRAPHICS BITMAP *tiles; #endif /* USE_GRAPHICS */ #ifdef USE_BACKGROUND int window_type; #endif /* USE_BACKGROUND */ }; /* * The current screen resolution */ static int resolution; #ifdef USE_BACKGROUND /* * The background images */ BITMAP *background[17]; #endif /* USE_BACKGROUND */ /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; #ifdef USE_GRAPHICS /* * Are graphics already initialized ? */ static bool graphics_initialized = FALSE; #endif /* USE_GRAPHICS */ /* * Small bitmap for the cursor */ static BITMAP *cursor; #ifdef USE_SOUND /* * Is the sound already initialized ? */ static bool sound_initialized = FALSE; # ifdef USE_MOD_FILES /* * Is the mod-file support already initialized ? */ static bool mod_file_initialized = FALSE; # endif /* USE_MOD_FILES */ /* * Volume settings */ static int digi_volume; static int midi_volume; /* * The currently playing song */ static MIDI *midi_song = NULL; # ifdef USE_MOD_FILES static JGMOD *mod_song = NULL; # endif /* USE_MOD_FILES */ static int current_song; /* * The number of available songs */ static int song_number; /* * The maximum number of available songs */ #define MAX_SONGS 255 static char music_files[MAX_SONGS][16]; /* * The maximum number of samples per sound-event */ #define SAMPLE_MAX 10 /* * An array of sound files */ static SAMPLE* samples[SOUND_MAX][SAMPLE_MAX]; /* * The number of available samples for every event */ static int sample_count[SOUND_MAX]; #endif /* USE_SOUND */ /* * Extra paths */ static char xtra_font_dir[1024]; static char xtra_graf_dir[1024]; static char xtra_sound_dir[1024]; static char xtra_music_dir[1024]; /* * List of used videomodes to reduce executable size */ BEGIN_GFX_DRIVER_LIST GFX_DRIVER_VBEAF GFX_DRIVER_VGA GFX_DRIVER_VESA3 GFX_DRIVER_VESA2L GFX_DRIVER_VESA2B GFX_DRIVER_VESA1 END_GFX_DRIVER_LIST /* * List of used color depths to reduce executeable size */ BEGIN_COLOR_DEPTH_LIST COLOR_DEPTH_8 END_COLOR_DEPTH_LIST /* * Keypress input modifier flags (hard-coded by DOS) */ #define K_RSHIFT 0 /* Right shift key down */ #define K_LSHIFT 1 /* Left shift key down */ #define K_CTRL 2 /* Ctrl key down */ #define K_ALT 3 /* Alt key down */ #define K_SCROLL 4 /* Scroll lock on */ #define K_NUM 5 /* Num lock on */ #define K_CAPS 6 /* Caps lock on */ #define K_INSERT 7 /* Insert on */ /* * Prototypes */ static errr Term_xtra_dos_event(int v); static void Term_xtra_dos_react(void); static void Term_xtra_dos_clear(void); static errr Term_xtra_dos(int n, int v); static errr Term_user_dos(int n); static errr Term_curs_dos(int x, int y); static errr Term_wipe_dos(int x, int y, int n); static errr Term_text_dos(int x, int y, int n, byte a, const char *cp); static void Term_init_dos(term *t); static void Term_nuke_dos(term *t); static void term_data_link(term_data *td); static void dos_dump_screen(void); static void dos_quit_hook(cptr str); static bool init_windows(void); errr init_dos(void); #ifdef USE_SOUND static bool init_sound(void); static errr Term_xtra_dos_sound(int v); static void play_song(void); #endif /* USE_SOUND */ #ifdef USE_GRAPHICS static bool init_graphics(void); static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp); #endif /* USE_GRAPHICS */ /* * Process an event (check for a keypress) * * The keypress processing code is often the most system dependant part * of Angband, since sometimes even the choice of compiler is important. * * For this file, we divide all keypresses into two catagories, first, the * "normal" keys, including all keys required to play Angband, and second, * the "special" keys, such as keypad keys, function keys, and various keys * used in combination with various modifier keys. * * To simplify this file, we use Angband's "macro processing" ability, in * combination with the "lib/user/pref.prf" file, to handle most of the * "special" keys, instead of attempting to fully analyze them here. This * file only has to determine when a "special" key has been pressed, and * translate it into a simple string which signals the use of a "special" * key, the set of modifiers used, if any, and the hardware scan code of * the actual key which was pressed. To simplify life for the user, we * treat both "shift" keys as identical modifiers. * * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers * ("C" for control, "S" for shift, "A" for alt, or any ordered combination), * and "SS" encodes the keypress (as the two "digit" hexidecimal encoding of * the scan code of the key that was pressed), and the "^_" and "x" and "\r" * delimit the encoding for recognition by the macro processing code. * * Some important facts about scan codes follow. All "normal" keys use * scan codes from 1-58. The "function" keys use 59-68 (and 133-134). * The "keypad" keys use 69-83. Escape uses 1. Enter uses 28. Control * uses 29. Left Shift uses 42. Right Shift uses 54. PrtScrn uses 55. * Alt uses 56. Space uses 57. CapsLock uses 58. NumLock uses 69. * ScrollLock uses 70. The "keypad" keys which use scan codes 71-83 * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL. * * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more * information, including better access to the keypad keys in combination * with various modifiers, but only works on "PC's after 6/1/86", and there * is no way to determine if the function is provided on a machine. I have * been told that without it you cannot detect, for example, control-left. * The basic scan code + ascii value pairs returned by the keypad follow, * with values in parentheses only available to "bioskey(0x10)". * * / * - + 1 2 3 4 * Norm: 352f 372a 4a2d 4e2b 4f00 5000 5100 4b00 * Shft: 352f 372a 4a2d 4e2b 4f31 5032 5133 4b34 * Ctrl: (9500) (9600) (8e00) (9000) 7500 (9100) 7600 7300 * * 5 6 7 8 9 0 . Enter * Norm: (4c00) 4d00 4700 4800 4900 5200 5300 (e00d) * Shft: 4c35 4d36 4737 4838 4939 5230 532e (e00d) * Ctrl: (8f00) 7400 7700 (8d00) 8400 (9200) (9300) (e00a) * * See "lib/user/pref-win.prf" for the "standard" macros for various keys. * * Certain "bizarre" keypad keys (such as "enter") return a "scan code" * of "0xE0", and a "usable" ascii value. These keys should be treated * like the normal keys, see below. XXX XXX XXX Note that these "special" * keys could be prefixed with an optional "ctrl-^" which would allow them * to be used in macros without hurting their use in normal situations. * * This function also appears in "main-ibm.c". XXX XXX XXX * * Addition for the DOS version: Dump-screen function with the * "Ctrl-Print" key saves a bitmap with the screen contents to * "lib/user/dump.bmp". */ static errr Term_xtra_dos_event(int v) { int i, k, s; bool mc = FALSE; bool ms = FALSE; bool ma = FALSE; /* Hack -- Check for a keypress */ if (!v && !bioskey(1)) return (1); /* Wait for a keypress */ k = bioskey(0x10); /* Access the "modifiers" */ i = bioskey(2); /* Extract the "scan code" */ s = ((k >> 8) & 0xFF); /* Extract the "ascii value" */ k = (k & 0xFF); /* Process "normal" keys */ if ((s <= 58) || (s == 0xE0)) { /* Enqueue it */ if (k) Term_keypress(k); /* Success */ return (0); } /* Extract the modifier flags */ if (i & (1 << K_CTRL)) mc = TRUE; if (i & (1 << K_LSHIFT)) ms = TRUE; if (i & (1 << K_RSHIFT)) ms = TRUE; if (i & (1 << K_ALT)) ma = TRUE; /* Dump the screen with "Ctrl-Print" */ if ((s == 0x72) && mc) { /* Dump the screen */ dos_dump_screen(); /* Success */ return (0); } /* Begin a "macro trigger" */ Term_keypress(31); /* Hack -- Send the modifiers */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (ma) Term_keypress('A'); /* Introduce the hexidecimal scan code */ Term_keypress('x'); /* Encode the hexidecimal scan code */ Term_keypress(hexsym[s/16]); Term_keypress(hexsym[s%16]); /* End the "macro trigger" */ Term_keypress(13); /* Success */ return (0); } /* * React to global changes in the colors, graphics, and sound settings. */ static void Term_xtra_dos_react(void) { int i; #ifdef USE_SPECIAL_BACKGROUND int j; term_data *td; #endif /* USE_SPECIAL_BACKGROUND */ /* * Set the Angband colors */ for (i = 0; i < 16; i++) { RGB color; /* Extract desired values */ char rv = angband_color_table[i][1] >> 2; char gv = angband_color_table[i][2] >> 2; char bv = angband_color_table[i][3] >> 2; /* Set the colors */ color.r = rv; color.g = gv; color.b = bv; set_color(COLOR_OFFSET + i, &color); } #ifdef USE_GRAPHICS /* * Handle "arg_graphics" */ if (use_graphics != arg_graphics) { /* Initialize (if needed) */ if (arg_graphics && !init_graphics()) { /* Warning */ plog("Cannot initialize graphics!"); /* Cannot enable */ arg_graphics = GRAPHICS_NONE; } /* Change setting */ use_graphics = arg_graphics; } #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* * Handle "arg_sound" */ if (use_sound != arg_sound) { /* Clear the old song */ if (midi_song) destroy_midi(midi_song); midi_song = NULL; #ifdef USE_MOD_FILES if (mod_file_initialized) { stop_mod(); destroy_mod(mod_song); } #endif /* USE_MOD_FILES */ /* Initialize (if needed) */ if (arg_sound && !init_sound()) { /* Warning */ plog("Cannot initialize sound!"); /* Cannot enable */ arg_sound = FALSE; } /* Change setting */ use_sound = arg_sound; } #endif /* USE_SOUND */ #ifdef USE_SPECIAL_BACKGROUND /* * Initialize the window backgrounds */ for (i = 0; i < MAX_TERM_DATA; i++) { td = &data[i]; /* Window flags */ for (j = 0; j < 16; j++) { if (op_ptr->window_flag[i] & (1L << j)) { if (background[j + 1]) { td->window_type = j + 1; } else { td->window_type = 0; } } } } #endif /* USE_SPECIAL_BACKGROUND */ } /* * Clear a terminal * * Fills the terminal area with black color or with * the background image */ static void Term_xtra_dos_clear(void) { term_data *td = (term_data*)(Term->data); #ifdef USE_BACKGROUND int bgrnd; #endif /* USE_BACKGROUND */ int x1, y1; int w1, h1; /* Location */ x1 = td->x; y1 = td->y; /* Size */ w1 = td->tile_wid * td->cols; h1 = td->tile_hgt * td->rows; #ifdef USE_BACKGROUND bgrnd = td->window_type; if (background[bgrnd]) { /* Draw the background */ stretch_blit(background[bgrnd], screen, 0, 0, background[bgrnd]->w, background[bgrnd]->h, x1, y1, w1, h1); } else #endif /* USE_BACKGROUND */ { /* Draw the Term black */ rectfill(screen, x1, y1, x1 + w1 - 1, y1 + h1 - 1, COLOR_OFFSET + TERM_DARK); } } /* * Handle a "special request" * * The given parameters are "valid". */ static errr Term_xtra_dos(int n, int v) { /* Analyze the request */ switch (n) { /* Make a "bell" noise */ case TERM_XTRA_NOISE: { /* Make a bell noise */ (void)write(1, "\007", 1); /* Success */ return (0); } /* Process events */ case TERM_XTRA_EVENT: { /* Process one event */ return (Term_xtra_dos_event(v)); } /* Flush events */ case TERM_XTRA_FLUSH: { /* Strip events */ while (!Term_xtra_dos_event(FALSE)); /* Success */ return (0); } /* Do something useful if bored */ case TERM_XTRA_BORED: { #ifdef USE_SOUND /* * Check for end of song and start a new one */ if (!use_sound) return (0); #ifdef USE_MOD_FILES if (song_number && (midi_pos < 0) && !is_mod_playing()) #else /* USE_MOD_FILES */ if (song_number && (midi_pos < 0)) #endif /* USE_MOD_FILES */ { if (song_number > 1) { /* Get a *new* song at random */ while (1) { n = Rand_simple(song_number) + 1; if (n != current_song) break; } current_song = n; } else { /* We only have one song, so loop it */ current_song = 1; } /* Play the song */ play_song(); } #endif /* USE_SOUND */ /* Success */ return (0); } /* React to global changes */ case TERM_XTRA_REACT: { /* Change the colors */ Term_xtra_dos_react(); /* Success */ return (0); } /* Delay for some milliseconds */ case TERM_XTRA_DELAY: { /* Delay if needed */ if (v > 0) delay(v); /* Success */ return (0); } #ifdef USE_SOUND /* Make a sound */ case TERM_XTRA_SOUND: { return (Term_xtra_dos_sound(v)); } #endif /* USE_SOUND */ } /* Unknown request */ return (1); } /* * Do a "user action" on the current "term" */ static errr Term_user_dos(int n) { int k; char section[80]; /* Unused parameter */ (void)n; /* Interact */ while (1) { /* Clear screen */ Term_clear(); /* Print date and time of compilation */ prtf(45, 1, "Compiled: %s %s\n", __TIME__, __DATE__); /* Why are we here */ prtf(0, 2, "DOS options"); /* Give some choices */ #ifdef USE_SOUND prtf(5, 4, "(V) Sound Volume"); prtf(5, 5, "(M) Music Volume"); #endif /* USE_SOUND */ #ifdef USE_GRAPHICS prtf(5, 7, "(G) Graphics : %s", arg_graphics ? "On" : "Off"); #endif /* USE_GRAPHICS */ #ifdef USE_SOUND prtf(5, 8, "(S) Sound/Music : %s", arg_sound ? "On" : "Off"); #endif /* USE_SOUND */ prtf(5, 12, "(R) Screen resolution"); prtf(5, 14, "(W) Save current options"); /* Prompt */ prtf(0, 18, "Command: "); /* Get command */ k = inkey(); /* Exit */ if (k == ESCAPE) break; /* Analyze */ switch (k) { #ifdef USE_SOUND /* Sound Volume */ case 'V': case 'v': { /* Prompt */ prtf(0, 18, "Command: Sound Volume"); /* Get a new value */ while (1) { prtf(0, 22, "Current Volume: %d", digi_volume); prtf(0, 20, "Change Volume (+, - or ESC to accept): "); k = inkey(); if (k == ESCAPE) break; switch (k) { case '+': { digi_volume++; if (digi_volume > 255) digi_volume = 255; break; } case '-': { digi_volume--; if (digi_volume < 0) digi_volume = 0; break; } /* Unknown option */ default: { break; } } set_volume(digi_volume, -1); } break; } /* Music Volume */ case 'M': case 'm': { /* Prompt */ prtf(0, 18, "Command: Music Volume"); /* Get a new value */ while (1) { prtf(0, 22, "Current Volume: %d", midi_volume); prtf(0, 20, "Change Volume (+, - or ESC to accept): "); k = inkey(); if (k == ESCAPE) break; switch (k) { case '+': { midi_volume++; if (midi_volume > 255) midi_volume = 255; break; } case '-': { midi_volume--; if (midi_volume < 0) midi_volume = 0; break; } /* Unknown option */ default: { break; } } set_volume(-1, midi_volume); } break; } #endif /* USE_SOUND */ #ifdef USE_GRAPHICS /* Switch graphics on/off */ case 'G': case 'g': { /* Hack - Toggle "arg_graphics" */ arg_graphics = !arg_graphics; /* React to changes */ Term_xtra_dos_react(); /* Reset visuals */ #ifdef ANGBAND_2_8_1 reset_visuals(); #else /* ANGBAND_2_8_1 */ reset_visuals(TRUE); #endif /* ANGBAND_2_8_1 */ break; } #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* Sound/Music On/Off */ case 'S': case 's': { /* Toggle "arg_sound" */ arg_sound = !arg_sound; /* React to changes */ Term_xtra_dos_react(); break; } #endif /* USE_SOUND */ /* Screen Resolution */ case 'R': case 'r': { int h, w, i = 1; cptr descr; /* Clear screen */ Term_clear(); /* Prompt */ prtf(0, 1, "Command: Screen Resolution"); prtf(0, 3, "Restart %s to get the new screenmode.", VERSION_NAME); /* Get a list of the available presets */ while (1) { /* Section name */ strnfmt(section, 80, "Mode-%d", i); /* Get new values or end the list */ if (!(w = get_config_int(section, "screen_wid", 0)) || (i == 16)) break; h = get_config_int(section, "screen_hgt", 0); /* Get a extra description of the resolution */ descr = get_config_string(section, "Description", ""); /* Print it */ prtf(0, 4 + i, "(%d) %d x %d %s", i, w, h, descr); /* Next */ i++; } /* Get a new resolution */ prtf(0, 20, "Screen Resolution : %d", resolution); k = inkey(); if (k == ESCAPE) break; if (isdigit(k)) resolution = D2I(k); /* Check for min, max value */ if ((resolution < 1) || (resolution >= i)) resolution = 1; /* Save the resolution */ set_config_int("Angband", "Resolution", resolution); /* Return */ break; } /* Save current option */ case 'W': case 'w': { prtf(0, 18, "Saving current options"); #ifdef USE_SOUND set_config_int("sound", "digi_volume", digi_volume); set_config_int("sound", "midi_volume", midi_volume); #endif /* USE_SOUND */ set_config_int("Angband", "Graphics", arg_graphics); set_config_int("Angband", "Sound", arg_sound); break; } /* Unknown option */ default: { break; } } /* Flush messages */ message_flush(); } /* Redraw it */ Term_key_push(KTRL('R')); /* Unknown */ return (0); } /* * Move the cursor * * The given parameters are "valid". */ static errr Term_curs_dos(int x, int y) { term_data *td = (term_data*)(Term->data); int x1, y1; /* Location */ x1 = x * td->tile_wid + td->x; y1 = y * td->tile_hgt + td->y; /* Draw the cursor */ draw_sprite(screen, cursor, x1, y1); /* Success */ return (0); } /* * Erase a block of the screen * * The given parameters are "valid". */ static errr Term_wipe_dos(int x, int y, int n) { term_data *td = (term_data*)(Term->data); #ifdef USE_BACKGROUND int bgrnd; #endif /* USE_BACKGROUND */ int x1, y1; int w1, h1; /* Location */ x1 = x * td->tile_wid + td->x; y1 = y * td->tile_hgt + td->y; /* Size */ w1 = n * td->tile_wid; h1 = td->tile_hgt; #ifdef USE_BACKGROUND bgrnd = td->window_type; if (background[bgrnd]) { int source_x = x * background[bgrnd]->w / td->cols; int source_y = y * background[bgrnd]->h / td->rows; int source_w = n * background[bgrnd]->w / td->cols; int source_h = background[bgrnd]->h / td->rows; /* Draw the background */ stretch_blit(background[bgrnd], screen, source_x, source_y, source_w, source_h, x1, y1, w1, h1); } else #endif /* USE_BACKGROUND */ { /* Draw a black block */ rectfill(screen, x1, y1, x1 + w1 - 1, y1 + h1 - 1, COLOR_OFFSET + TERM_DARK); } /* Success */ return (0); } /* * Place some text on the screen using an attribute * * The given parameters are "valid". Be careful with "a". * * The string "cp" has length "n" and is NOT null-terminated. */ static errr Term_text_dos(int x, int y, int n, byte a, const char *cp) { term_data *td = (term_data*)(Term->data); int i; int x1, y1; unsigned char text[257]; /* Location */ x1 = x * td->tile_wid + td->x; y1 = y * td->tile_hgt + td->y; /* Erase old contents */ Term_wipe_dos(x, y, n); #ifdef USE_SPECIAL_BACKGROUND /* Show text in black in the message window */ if (op_ptr->window_flag[td->number] & (PW_MESSAGE)) a = 0; #endif /* USE_SPECIAL_BACKGROUND */ /* No stretch needed */ if (td->font_wid == td->tile_wid) { /* Copy the string */ for (i = 0; i < n; ++i) text[i] = cp[i]; /* Terminate */ text[i] = '\0'; /* Dump the text */ textout(screen, td->font, text, x1, y1, COLOR_OFFSET + (a & 0x0F)); } /* Stretch needed */ else { /* Pre-Terminate */ text[1] = '\0'; /* Write the chars to the screen */ for (i = 0; i < n; ++i) { /* Build a one character string */ text[0] = cp[i]; /* Dump some text */ textout(screen, td->font, text, x1, y1, COLOR_OFFSET + (a & 0x0F)); /* Advance */ x1 += td->tile_wid; } } /* Success */ return (0); } #ifdef USE_GRAPHICS /* * Place some attr/char pairs on the screen * * The given parameters are "valid". * * To prevent crashes, we must not only remove the high bits of the * "ap[i]" and "cp[i]" values, but we must map the resulting value * onto the legal bitmap size, which is normally 32x32. XXX XXX XXX */ static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { term_data *td = (term_data*)(Term->data); int i; int w, h; int x1, y1; int x2, y2; int x3, y3; /* Size */ w = td->tile_wid; h = td->tile_hgt; /* Location (window) */ x1 = x * w + td->x; y1 = y * h + td->y; /* Dump the tiles */ for (i = 0; i < n; i++) { /* Location (bitmap) */ x2 = (cp[i] & 0x7F) * w; y2 = (ap[i] & 0x7F) * h; x3 = (tcp[i] & 0x7F) * w; y3 = (tap[i] & 0x7F) * h; /* Blit the tile to the screen */ blit(td->tiles, screen, x3, y3, x1, y1, w, h); /* Blit the tile to the screen */ masked_blit(td->tiles, screen, x2, y2, x1, y1, w, h); /* Advance (window) */ x1 += w; } /* Success */ return (0); } #endif /* USE_GRAPHICS */ /* * Init a Term */ static void Term_init_dos(term *t) { /* Unused parameter */ (void)t; /* XXX Nothing */ } /* * Nuke a Term */ static void Term_nuke_dos(term *t) { term_data *td = (term_data*)(t->data); /* Free the terminal font */ if (td->font) { destroy_font(td->font); } #ifdef USE_GRAPHICS /* Free the terminal bitmap */ if (td->tiles) destroy_bitmap(td->tiles); #endif /* USE_GRAPHICS */ } /* * Instantiate a "term_data" structure */ static void term_data_link(term_data *td) { term *t = &td->t; /* Initialize the term */ term_init(t, td->cols, td->rows, 255); /* Use a "software" cursor */ t->soft_cursor = TRUE; /* Ignore the "TERM_XTRA_BORED" action */ t->never_bored = FALSE; /* Ignore the "TERM_XTRA_FROSH" action */ t->never_frosh = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Prepare the init/nuke hooks */ t->init_hook = Term_init_dos; t->nuke_hook = Term_nuke_dos; /* Prepare the template hooks */ t->xtra_hook = Term_xtra_dos; t->curs_hook = Term_curs_dos; t->wipe_hook = Term_wipe_dos; t->user_hook = Term_user_dos; t->text_hook = Term_text_dos; #ifdef USE_GRAPHICS /* Prepare the graphics hook */ t->pict_hook = Term_pict_dos; /* Use "Term_pict" for "graphic" data */ t->higher_pict = TRUE; #endif /* USE_GRAPHICS */ /* Remember where we came from */ t->data = (vptr)(td); } /* * Shut down visual system, then fall back into standard "quit()" */ static void dos_quit_hook(cptr str) { int i; /* Unused parameter */ (void)str; /* Destroy windows */ for (i = MAX_TERM_DATA - 1; i >= 0; i--) { /* Unused */ if (!angband_term[i]) continue; /* Nuke it */ term_nuke(angband_term[i]); } /* Free all resources */ if (cursor) destroy_bitmap(cursor); #ifdef USE_BACKGROUND /* Free the background bitmaps */ for (i = 0; i < 17; i++) { if (background[i]) destroy_bitmap(background[i]); } #endif /* USE_BACKGROUND */ #ifdef USE_SOUND if (sound_initialized) { /* Destroy samples */ for (i = 1; i < SOUND_MAX; i++) { int j; for (j = 0; j < sample_count[i]; j++) { if (samples[i][j]) destroy_sample(samples[i][j]); } } } /* Clear the old song */ if (midi_song) destroy_midi(midi_song); midi_song =NULL; # ifdef USE_MOD_FILES if (mod_file_initialized) { stop_mod(); destroy_mod(mod_song); } # endif /* USE_MOD_FILES */ #endif /* USE_SOUND */ /* Shut down Allegro */ allegro_exit(); } /* * Dump the screen to "lib/user/dump.bmp" */ static void dos_dump_screen(void) { /* Bitmap and palette of the screen */ BITMAP *bmp; PALETTE pal; /* Filename */ char filename[1024]; /* Get bitmap and palette of the screen */ bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H); get_palette(pal); /* Build the filename for the screen-dump */ path_make(filename, ANGBAND_DIR_USER, "dump.bmp"); /* Save it */ save_bmp(filename, bmp, pal); /* Free up the memory */ if (bmp) destroy_bitmap(bmp); /* Success message */ msgf("Screen dump saved."); message_flush(); } /* * Initialize the terminal windows */ static bool init_windows(void) { int i, num_windows; term_data *td; char section[80]; char filename[1024]; char buf[128]; /* Section name */ strnfmt(section, 80, "Mode-%d", resolution); /* Get number of windows */ num_windows = get_config_int(section, "num_windows", 1); /* Paranoia */ if (num_windows > MAX_TERM_DATA) num_windows = MAX_TERM_DATA; /* Init the terms */ for (i = 0; i < num_windows; i++) { td = &data[i]; WIPE(td, term_data); /* Section name */ strnfmt(section, 80, "Term-%d-%d", resolution, i); /* Term number */ td->number = i; /* Coordinates of left top corner */ td->x = get_config_int(section, "x", 0); td->y = get_config_int(section, "y", 0); /* Rows and cols of term */ td->rows = get_config_int(section, "rows", 24); td->cols = get_config_int(section, "cols", 80); /* Tile size */ td->tile_wid = get_config_int(section, "tile_wid", 8); td->tile_hgt = get_config_int(section, "tile_hgt", 13); /* Font size */ td->font_wid = get_config_int(section, "tile_wid", 8); td->font_hgt = get_config_int(section, "tile_hgt", 13); /* Get font filename */ strcpy(buf, get_config_string(section, "font_file", "xm8x13.fnt")); /* Build the name of the font file */ path_make(filename, xtra_font_dir, buf); /* Load a "*.dat" file */ if (suffix(filename, ".dat")) { DATAFILE *fontdata; /* Load the font file */ if (!(fontdata = load_datafile(filename))) { quit_fmt("Error reading font file '%s'", filename); } /* Save the font data */ td->font = fontdata[1].dat; /* Unload the font file */ unload_datafile_object(fontdata); } /* Oops */ else { quit_fmt("Unknown suffix in font file '%s'", filename); } /* Link the term */ term_data_link(td); angband_term[i] = &td->t; } /* Success */ return 0; } #ifdef USE_BACKGROUND /* * Initialize the window backgrounds */ static void init_background(void) { int i; char filename[1024]; char buf[128]; PALLETE background_pallete; /* Get the backgrounds */ for (i = 0; i < 16; i++) { /* Get background filename */ strcpy(buf, get_config_string("Background", format("Background-%d", i), "")); /* Build the filename for the background-bitmap */ path_make(filename, xtra_graf_dir, buf); /* Try to open the bitmap file */ background[i] = load_bitmap(filename, background_pallete); } #ifndef USE_SPECIAL_BACKGROUND /* * Set the palette for the background */ if (background[0]) { set_palette_range(background_pallete, 0, COLOR_OFFSET - 1, 0); } #endif /* USE_SPECIAL_BACKGROUND */ } #endif /* USE_BACKGROUND */ #ifdef USE_GRAPHICS /* * Initialize graphics */ static bool init_graphics(void) { char filename[1024]; char section[80]; char name_tiles[128]; /* Large bitmap for the tiles */ BITMAP *tiles = NULL; PALLETE tiles_pallete; /* Size of each bitmap tile */ int bitmap_wid; int bitmap_hgt; int num_windows; if (!graphics_initialized) { /* Section name */ strnfmt(section, 80, "Mode-%d", resolution); /* Get bitmap tile size */ bitmap_wid = get_config_int(section, "bitmap_wid", 8); bitmap_hgt = get_config_int(section, "bitmap_hgt", 8); /* Get bitmap filename */ strcpy(name_tiles, get_config_string(section, "bitmap_file", "8x8.bmp")); /* Get number of windows */ num_windows = get_config_int(section, "num_windows", 1); /* Build the name of the bitmap file */ path_make(filename, xtra_graf_dir, name_tiles); /* Open the bitmap file */ if ((tiles = load_bitmap(filename, tiles_pallete)) != NULL) { int i; cptr graf_mode; /* * Set the graphics mode to "new" if Adam Bolt's * new 16x16 tiles are used. */ graf_mode = get_config_string(section, "graf-mode", "old"); if (streq(graf_mode, "new")) { arg_graphics = GRAPHICS_ADAM_BOLT; /* Use transparent blits */ use_transparency = TRUE; } else if (streq(graf_mode, "old")) { arg_graphics = GRAPHICS_ORIGINAL; } else { arg_graphics = GRAPHICS_NONE; } /* Select the bitmap pallete */ set_palette_range(tiles_pallete, 0, COLOR_OFFSET - 1, 0); /* Prepare the graphics */ for (i = 0; i < num_windows; i++) { term_data *td; int col, row; int cols, rows; int width, height; int src_x, src_y; int tgt_x, tgt_y; td = &data[i]; cols = tiles->w / bitmap_wid; rows = tiles->h / bitmap_hgt; width = td->tile_wid * cols; height = td->tile_hgt * rows; /* Initialize the tile graphics */ td->tiles = create_bitmap(width, height); for (row = 0; row < rows; ++row) { src_y = row * bitmap_hgt; tgt_y = row * td->tile_hgt; for (col = 0; col < cols; ++col) { src_x = col * bitmap_wid; tgt_x = col * td->tile_wid; stretch_blit(tiles, td->tiles, src_x, src_y, bitmap_wid, bitmap_hgt, tgt_x, tgt_y, td->tile_wid, td->tile_hgt); } } } /* Free the old tiles bitmap */ if (tiles) destroy_bitmap(tiles); graphics_initialized = TRUE; /* Success */ return (TRUE); } /* Failure */ return (FALSE); } /* Success */ return (TRUE); } #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* * Initialize sound * We try to get a list of the available sound-files from "lib/xtra/sound/sound.cfg" * and then preload the samples. Every Angband-sound-event can have several samples * assigned. Angband will randomly select which is played. This makes it easy to * create "sound-packs", just copy wav-files into the "lib/xtra/sound/" folder and * add the filenames to "sound.cfg" in the same folder. */ static bool init_sound(void) { int i, j, done; char section[128]; char filename[1024]; char **argv; struct ffblk f; if (sound_initialized) return (TRUE); reserve_voices(16, -1); /* Initialize Allegro sound */ if (!install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL)) { #ifdef USE_MOD_FILES /* * Try to enable support for MOD-, and S3M-files * The parameter for install_mod() is the number * of channels reserved for the MOD/S3M-file. */ if (install_mod(8) > 0) mod_file_initialized = TRUE; #endif /* USE_MOD_FILES */ /* Access the new sample */ path_make(filename, xtra_sound_dir, "sound.cfg"); /* Read config info from "lib/xtra/sound/sound.cfg" */ override_config_file(filename); /* Sound section */ strcpy(section, "Sound"); /* Prepare the sounds */ for (i = 1; i < SOUND_MAX; i++) { /* Get the sample names */ argv = get_config_argv(section, (char *)angband_sound_name[i], &sample_count[i]); /* Limit the number of samples */ if (sample_count[i] > SAMPLE_MAX) sample_count[i] = SAMPLE_MAX; for (j = 0; j < sample_count[i]; j++) { /* Access the new sample */ path_make(filename, xtra_sound_dir, argv[j]); /* Load the sample */ samples[i][j] = load_sample(filename); } } /* * Get a list of music files */ #ifdef USE_MOD_FILES if (mod_file_initialized) { done = findfirst(format("%s/*.*", xtra_music_dir), &f, FA_ARCH|FA_RDONLY); } else #endif /* USE_MOD_FILES */ done = findfirst(format("%s/*.mid", xtra_music_dir), &f, FA_ARCH|FA_RDONLY); while (!done && (song_number < MAX_SONGS)) { /* Add music files */ strncpy(music_files[song_number], f.ff_name, 15); music_files[song_number][15] = '\0'; song_number++; done = findnext(&f); } /* Use "angdos.cfg" */ override_config_file("angdos.cfg"); /* Sound section */ strcpy(section, "Sound"); /* Get the volume setting */ digi_volume = get_config_int(section, "digi_volume", 255); midi_volume = get_config_int(section, "midi_volume", 255); /* Set the volume */ set_volume(digi_volume, midi_volume); /* Success */ return (TRUE); } /* Init failed */ return (FALSE); } /* * Make a sound */ static errr Term_xtra_dos_sound(int v) { int n; /* Sound disabled */ if (!use_sound) return (1); /* Illegal sound */ if ((v < 0) || (v >= SOUND_MAX)) return (1); /* Get a random sample from the available ones */ n = Rand_simple(sample_count[v]); /* Play the sound, catch errors */ if (samples[v][n]) { return (play_sample(samples[v][n], 255, 128, 1000, 0) == 0); } /* Oops */ return (1); } /* * Play a song-file */ static void play_song(void) { char filename[1024]; /* Clear the old song */ if (midi_song) destroy_midi(midi_song); midi_song = NULL; #ifdef USE_MOD_FILES if (mod_file_initialized) { stop_mod(); destroy_mod(mod_song); } #endif /* USE_MOD_FILES */ /* Access the new song */ path_make(filename, xtra_music_dir, music_files[current_song - 1]); /* Load and play the new song */ midi_song = load_midi(filename); if (midi_song) { play_midi(midi_song, 0); } #ifdef USE_MOD_FILES else if (mod_file_initialized) { mod_song = load_mod(filename); if (mod_song) play_mod(mod_song, FALSE); } #endif /* USE_MOD_FILES */ } #endif /* USE_SOUND */ /* * Attempt to initialize this file * * Hack -- we assume that "blank space" should be "white space" * (and not "black space" which might make more sense). * * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value * into an 8 bit value, without losing much precision, by using the 2 * most significant bits as the least significant bits in the new value. * * We should attempt to "share" bitmaps (and fonts) between windows * with the same "tile" size. XXX XXX XXX */ errr init_dos(void) { term_data *td; char section[80]; int screen_wid; int screen_hgt; /* Initialize the Allegro library (never fails) */ if (allegro_init()) return (-1); /* Install timer support for music and sound */ if (install_timer()) return (-1); /* Read config info from filename */ set_config_file("angdos.cfg"); /* Main section */ strcpy(section, "Angband"); /* Get screen size */ resolution = get_config_int(section, "Resolution", 1); /* Section name */ strnfmt(section, 80, "Mode-%d", resolution); /* Get the screen dimensions */ screen_wid = get_config_int(section, "screen_wid", 640); screen_hgt = get_config_int(section, "screen_hgt", 480); /* Set the color depth */ set_color_depth(8); /* Auto-detect, and instantiate, the appropriate graphics mode */ if ((set_gfx_mode(GFX_AUTODETECT, screen_wid, screen_hgt, 0, 0)) < 0) { /* * Requested graphics mode is not available * We retry with the basic 640x480 mode */ resolution = 1; if ((set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0)) < 0) { char error_text[1024]; /* Save the Allegro error description */ strcpy(error_text, allegro_error); /* Shut down Allegro */ allegro_exit(); /* Print the error description */ plog_fmt("Error selecting screen mode: %s", error_text); /* Failure */ return (-1); } } /* Hook in "z-util.c" hook */ quit_aux = dos_quit_hook; /* Build the "graf" path */ path_make(xtra_graf_dir, ANGBAND_DIR_XTRA, "graf"); /* Build the "font" path */ path_make(xtra_font_dir, ANGBAND_DIR_XTRA, "font"); /* Build the "sound" path */ path_make(xtra_sound_dir, ANGBAND_DIR_XTRA, "sound"); /* Build the "music" path */ path_make(xtra_music_dir, ANGBAND_DIR_XTRA, "music"); /* Initialize the windows */ init_windows(); #ifdef USE_SOUND /* Look for the sound preferences in "angdos.cfg" */ if (!arg_sound) { arg_sound = get_config_int("Angband", "Sound", TRUE); } #endif /* USE_SOUND */ #ifdef USE_GRAPHICS /* Look for the graphic preferences in "angdos.cfg" */ if (!arg_graphics) { arg_graphics = get_config_int("Angband", "Graphics", GRAPHICS_ORIGINAL); } #endif /* USE_GRAPHICS */ /* Initialize the "complex" RNG for the midi-shuffle function */ Rand_quick = FALSE; Rand_state_init(time(NULL)); /* Set the Angband colors/graphics/sound mode */ Term_xtra_dos_react(); #ifdef USE_BACKGROUND /* Initialize the background graphics */ init_background(); #endif /* USE_BACKGROUND */ /* Clear the screen */ clear_to_color(screen, COLOR_OFFSET + TERM_DARK); /* Main screen */ td = &data[0]; /* Build a cursor bitmap */ cursor = create_bitmap(td->tile_wid, td->tile_hgt); /* Erase the cursor sprite */ clear(cursor); /* Draw the cursor sprite (yellow rectangle) */ rect(cursor, 0, 0, td->tile_wid - 1, td->tile_hgt - 1, COLOR_OFFSET + TERM_YELLOW); /* Activate the main term */ Term_activate(term_screen); /* Place the cursor */ Term_curs_dos(0, 0); #ifdef USE_BACKGROUND /* Use transparent text */ text_mode(-1); #endif /* USE_BACKGROUND */ /* Success */ return 0; } #endif /* USE_DOS */ zangband/src/main-emx.c0000755000000000000000000007032510250356274014024 0ustar rootroot/* File: main-emx.c */ /* * Copyright (c) 1997 Ben Harrison, Ekkehard Kraemer, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* Purpose: Support for OS/2 EMX Angband */ /* Author: ekraemer@pluto.camelot.de (Ekkehard Kraemer) */ /* Current maintainer: silasd@psyber.com (Silas Dunsmore) */ /* Unless somebody else wants it.... */ #include "angband.h" #ifdef USE_EMX /* * === Instructions for using Angband 2.7.X with OS/2 === * * The patches (file "main-emx.c") to compile Angband 2.7.X under OS/2 * were written by ekraemer@pluto.camelot.de (Ekkehard Kraemer). * * TO COMPILE: * * - untar the archive into /angband (or /games/angband or whatever) * - change directory to /angband/src * - run "dmake -B -r -f makefile.emx" (not gmake or make) * * TO INSTALL: * * - change directory to /angband/src * - run "dmake -B -r -f makefile.emx install" (not gmake or make) * - copy your old savefile into ./lib/save and your old pref.prf into ./lib/user * - start /angband/angband.exe for one single window * - start /angband/startwnd.cmd for multiple windows * * TO REMOVE TEMPORARY FILES: * * - run 'dmake -B -r -f makefile.emx clean' * * * I use EMX 0.9b, but every EMX compiler since 0.8g or so should work * fine. EMX is available at ftp-os2.cdrom.com ("Hobbes"), as is dmake. * * dmake: ftp://ftp-os2.cdrom.com/all/program/dmake38X.zip * EMX: ftp://ftp-os2.cdrom.com/2_x/unix/emx???/ (most probably) * * Old savefiles must be renamed to follow the new "savefile" naming * conventions. Either rename the savefile to "PLAYER", or start the * program with the "-uName" flag. See "main.c" for details. The * savefiles are stores in "./lib/save" if you forgot the old names... * * Changes * ======= * * When By Version What * ------------------------------------------------------------------- * * 18.11.95 EK 2.7.8 Added window/pipe code * Introduced __EMX__CLIENT__ hack * * 15.12.95 EK 2.7.9 Updated for 2.7.9 * beta Added third view * Added number of line support in aclient * * 25.12.95 EK 2.7.9 Added 'install' target * non-beta Updated installation hints * Uploaded binary to export.andrew.cmu.edu * * 25.01.96 EK 2.7.9 Updated for 2.7.9v3 * v3 Removed (improved) keyboard hack * Introduced pref-emx.prf * Phew... Makefile.emx grows! (patches, export) * Uploaded binary to export.andrew.cmu.edu * * 26.01.96 EK Added files.uue target * * 22.02.96 EK Added PM support * * 2.03.96 EK 2.7.9 Uploaded binaries to export.andrew.cmu.edu * v4 * * 9.03.96 EK 2.7.9 Adjustable background color (PM) * v5 Added map window * 3 Dec 97 SWD 282 Brought key-handling, macros in sync with DOS. * Hacked on sub-window code -- it compiles, but * doesn't link. * * 23 Jan 98 SWD 282 Hacked more on sub-windows. Now links, with * warnings. Seems to work. * * 01 Nov 98 AGA 2.8.3 Adjusted for 2.8.3 sources, typos corrected * * 01 Nov 98 AGA Z214b Corrections for ZAngband 2.1.4 beta * */ #include #include #include #include #include #define INCL_KBD 1 #include #include /* * Maximum windows */ #define MAX_TERM_DATA 8 /* * Keypress input modifier flags (copied from main-ibm.c) * * SWD: these could be changed to the definitions in , which are * direct bitmasks instead of shift-counts. */ #define K_RSHIFT 0 /* Right shift key down */ #define K_LSHIFT 1 /* Left shift key down */ #define K_CTRL 2 /* Ctrl key down */ #define K_ALT 3 /* Alt key down */ #define K_SCROLL 4 /* Scroll lock on */ #define K_NUM 5 /* Num lock on */ #define K_CAPS 6 /* Caps lock on */ #define K_INSERT 7 /* Insert on */ /* * Prototypes! */ static errr Term_curs_emx(int x, int y); static errr Term_wipe_emx(int x, int y, int n); static errr Term_text_emx(int x, int y, int n, unsigned char a, cptr s); static void Term_init_emx(term *t); static void Term_nuke_emx(term *t); #ifndef EMXPM /* * termPipe* is sometimes cast to term* and vice versa, * so "term t;" must be the first line */ typedef struct { term t; FILE *out; /* used by the ..._pipe_emx stuff */ } termPipe; /* * Communication between server and client */ enum { PIP_INIT, PIP_NUKE, PIP_XTRA, PIP_CURS, PIP_WIPE, PIP_TEXT, }; /* * Current cursor "size" */ static int curs_start=0; static int curs_end=0; /* * Angband color conversion table * * Note that "Light Green"/"Yellow"/"Red" are used for * "good"/"fair"/"awful" stats and conditions. * * But "brown"/"light brown"/"orange" are really only used * for "objects" (such as doors and spellbooks), and these * values can in fact be re-defined. * * Future versions of Angband will probably allow the * "use" of more that 16 colors, so "extra" entries can be * made at the end of this table in preparation. */ static int colors[16]= { F_BLACK, /* Black */ F_WHITE|INTENSITY, /* White */ F_WHITE, /* XXX Gray */ F_RED|INTENSITY, /* Orange */ F_RED, /* Red */ F_GREEN, /* Green */ F_BLUE, /* Blue */ F_BROWN, /* Brown */ F_BLACK|INTENSITY, /* Dark-grey */ F_WHITE, /* XXX Light gray */ F_MAGENTA, /* Purple */ F_YELLOW|INTENSITY, /* Yellow */ F_RED|INTENSITY, /* Light Red */ F_GREEN|INTENSITY, /* Light Green */ F_BLUE|INTENSITY, /* Light Blue */ F_BROWN|INTENSITY /* Light brown */ }; /* * Display a cursor, on top of a given attr/char */ static errr Term_curs_emx(int x, int y) { v_gotoxy(x, y); v_ctype(curs_start, curs_end); return (0); } /* * Erase a grid of space (as if spaces were printed) */ static errr Term_wipe_emx(int x, int y, int n) { v_gotoxy(x, y); v_putn(' ', n); return (0); } /* * Draw some text, wiping behind it first */ static errr Term_text_emx(int x, int y, int n, unsigned char a, cptr s) { /* Convert the color and put the text */ v_attrib(colors[a & 0x0F]); v_gotoxy(x, y); v_putm(s, n); return (0); } /* * EMX initialization */ static void Term_init_emx(term *t) { struct _KBDINFO kbdinfo; /* see structure description ?somewhere? */ v_init(); v_getctype(&curs_start, &curs_end); /* hide cursor (?) XXX XXX XXX */ v_clear(); /* the documentation I (SWD) have implies, in passing, that setting */ /* "binary mode" on the keyboard device will prevent the O/S from */ /* acting on keys such as ^S (pause) and ^P (printer echo). */ /* note also that "KbdSetStatus is ignored for a Vio-windowed application." */ /* so there may well be problems with running this in a window. Damnit. */ /* this is kind of a nasty structure, as you can't just flip a bit */ /* to change binary/ASCII mode, or echo on/off mode... nor can you */ /* clear the whole thing -- certain bits need to be preserved. */ KbdGetStatus(&kbdinfo, (HKBD)0); kbdinfo.fsMask &= ~ (KEYBOARD_ECHO_ON| /* clear lowest four bits */ KEYBOARD_ECHO_OFF|KEYBOARD_BINARY_MODE|KEYBOARD_ASCII_MODE); kbdinfo.fsMask |= (KEYBOARD_BINARY_MODE); /* set bit two */ KbdSetStatus(&kbdinfo, (HKBD)0); #if 1 /* turn off for debug */ signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); /* signal(SIGILL,SIG_IGN); */ /* signal(SIGTRAP,SIG_IGN); */ /* signal(SIGABRT,SIG_IGN); */ /* signal(SIGEMT,SIG_IGN); */ /* signal(SIGFPE,SIG_IGN); */ /* signal(SIGBUS,SIG_IGN); */ /* signal(SIGSEGV,SIG_IGN); */ /* signal(SIGSYS,SIG_IGN); */ signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); /* signal(SIGTERM,SIG_IGN); */ signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGBREAK, SIG_IGN); #endif } /* * EMX shutdown */ static void Term_nuke_emx(term *t) { /* Move the cursor to bottom of screen */ v_gotoxy(0, 23); /* Restore the cursor (not necessary) */ v_ctype(curs_start, curs_end); /* Set attribute to gray on black */ v_attrib(F_WHITE); /* Clear the screen */ v_clear(); } #ifndef __EMX__CLIENT__ /* * Oh no, more prototypes! */ static errr CheckEvents(int returnImmediately); static errr Term_xtra_pipe_emx(int n, int v); static errr Term_curs_pipe_emx(int x, int y); static errr Term_wipe_pipe_emx(int x, int y, int n); static errr Term_text_pipe_emx(int x, int y, int n, unsigned char a, cptr s); static void Term_init_pipe_emx(term *t); static void Term_nuke_pipe_emx(term *t); static FILE *initPipe(const char *name); static void initPipeTerm(termPipe *pipe, const char *name, term **term); /* * Main initialization function */ errr init_emx(void); /* * The screens */ static termPipe term_screen_aga[MAX_TERM_DATA]; /* * Check for events -- called by "Term_scan_emx()" * * Note -- this is probably NOT the most efficient way * to "wait" for a keypress (TERM_XTRA_EVENT). * * * This code was ripped from "main-ibm.c" -- consult it to * figure out what's going on. * * See "main-ibm.c" for more information about "macro encoding". * * * The following documentation was cut&pasted from * the OS/2 Programming Reference, PRCP.INF * ------------------------------------------------------------------------------- This call returns a character data record from the keyboard. KbdCharIn (CharData, IOWait, KbdHandle) CharData (PKBDKEYINFO) - output Address of the character data structure: asciicharcode (UCHAR) ASCII character code. The scan code received from the keyboard is translated to the ASCII character code. scancode (UCHAR) Code received from the keyboard. The scan code received from the keyboard is translated to the ASCII character code. status (UCHAR) State of the keystroke event: Bit Description 7-6 00 = Undefined 01 = Final character, interim character flag off 10 = Interim character 11 = Final character, interim character flag on. 5 1 = Immediate conversion requested. 4-2 Reserved. 1 0 = Scan code is a character. 1 = Scan code is not a character; is an extended key code from the keyboard. 0 1 = Shift status returned without character. reserved (UCHAR) NLS shift status. Reserved, set to zero. shiftkeystat (USHORT) Shift key status. Bit Description 15 SysReq key down 14 CapsLock key down 13 NumLock key down 12 ScrollLock key down 11 Right Alt key down 10 Right Ctrl key down 9 Left Alt key down 8 Left Ctrl key down 7 Insert on 6 CapsLock on 5 NumLock on 4 ScrollLock on 3 Either Alt key down 2 Either Ctrl key down 1 Left Shift key down 0 Right Shift key down time (ULONG) Time stamp indicating when a key was pressed. It is specified in milliseconds from the time the system was started. IOWait (USHORT) - input Wait if a character is not available. Value Definition 0 Requestor waits for a character if one is not available. 1 Requestor gets an immediate return if no character is available. KbdHandle (HKBD) - input Default keyboard or the logical keyboard. rc (USHORT) - return Return code descriptions are: 0 NO_ERROR 375 ERROR_KBD_INVALID_IOWAIT 439 ERROR_KBD_INVALID_HANDLE 445 ERROR_KBD_FOCUS_REQUIRED 447 ERROR_KBD_KEYBOARD_BUSY 464 ERROR_KBD_DETACHED 504 ERROR_KBD_EXTENDED_SG Remarks On an enhanced keyboard, the secondary enter key returns the normal character 0DH and a scan code of E0H. Double-byte character codes (DBCS) require two function calls to obtain the entire code. If shift report is set with KbdSetStatus, the CharData record returned reflects changed shift information only. Extended ASCII codes are identified with the status byte, bit 1 on and the ASCII character code being either 00H or E0H. Both conditions must be satisfied for the character to be an extended keystroke. For extended ASCII codes, the scan code byte returned is the second code (extended code). Usually the extended ASCII code is the scan code of the primary key that was pressed. A thread in the foreground session that repeatedly polls the keyboard with KbdCharIn (with no wait), can prevent all regular priority class threads from executing. If polling must be used and a minimal amount of other processing is being performed, the thread should periodically yield to the CPU by issuing a DosSleep call for an interval of at least 5 milliseconds. Family API Considerations Some options operate differently in the DOS mode than in the OS /2 mode. Therefore, the following restrictions apply to KbdCharIn when coding in the DOS mode: o The CharData structure includes everything except the time stamp. o Interim character is not supported o Status can be 0 or 40H o KbdHandle is ignored. ------------------------------------------------------------------------------- typedef struct _KBDKEYINFO { / * kbci * / UCHAR chChar; / * ASCII character code * / UCHAR chScan; / * Scan Code * / UCHAR fbStatus; / * State of the character * / UCHAR bNlsShift; / * Reserved (set to zero) * / USHORT fsState; / * State of the shift keys * / ULONG time; / * Time stamp of keystroke (ms since ipl) * / }KBDKEYINFO; #define INCL_KBD USHORT rc = KbdCharIn(CharData, IOWait, KbdHandle); PKBDKEYINFO CharData; / * Buffer for data * / USHORT IOWait; / * Indicate if wait * / HKBD KbdHandle; / * Keyboard handle * / USHORT rc; / * return code * / ------------------------------------------------------------------------------- * */ static errr CheckEvents(int returnImmediately) { int i, k, s; bool mc = FALSE; bool ms = FALSE; bool ma = FALSE; /* start OS/2 specific section */ struct _KBDKEYINFO keyinfo; /* see structure description above */ /* Check (and possibly wait) for a keypress */ /* see function description above */ KbdCharIn( &keyinfo, returnImmediately, (HKBD)0 ); #if 0 printf("AC:%x SC:%x ST:%x R1:%x SH:%x TI:%ld\n", /* OS/2 debug */ keyinfo.chChar, keyinfo.chScan, keyinfo.fbStatus, keyinfo.bNlsShift, keyinfo.fsState, keyinfo.time ); #endif /* If there wasn't a key, leave now. */ if ((keyinfo.fbStatus & 0xC0) == 0) return(1); /* by a set of lucky coincidences, the data maps directly over. */ k = keyinfo.chChar; s = keyinfo.chScan; i = (keyinfo.fsState & 0xFF); /* end OS/2 specific section */ /* Process "normal" keys */ if ( k != 0 && ((s <= 58) || (s == 0xE0)) ) /* Tweak: allow for ALT-keys */ { /* Enqueue it */ Term_keypress(k); /* Success */ return (0); } /* Extract the modifier flags */ if (i & (1 << K_CTRL)) mc = TRUE; if (i & (1 << K_LSHIFT)) ms = TRUE; if (i & (1 << K_RSHIFT)) ms = TRUE; if (i & (1 << K_ALT)) ma = TRUE; /* Begin a "macro trigger" */ Term_keypress(31); /* Hack -- Send the modifiers */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (ma) Term_keypress('A'); /* Introduce the hexidecimal scan code */ Term_keypress('x'); /* Encode the hexidecimal scan code */ Term_keypress(hexsym[s/16]); Term_keypress(hexsym[s%16]); /* End the "macro trigger" */ Term_keypress(13); /* Success */ return (0); } /* * Do a special thing (beep, flush, etc) */ static errr Term_xtra_emx(int n, int v) { switch (n) { case TERM_XTRA_SHAPE: if (v) { v_ctype(curs_start, curs_end); } else { v_hidecursor(); } return (0); case TERM_XTRA_NOISE: DosBeep(440, 50); return (0); case TERM_XTRA_FLUSH: while (!CheckEvents(TRUE)); return 0; case TERM_XTRA_EVENT: /* Process an event */ return (CheckEvents(!v)); /* Success */ return (0); } return (1); } static errr Term_xtra_pipe_emx(int n, int v) { termPipe *tp=(termPipe*)Term; switch (n) { case TERM_XTRA_NOISE: DosBeep(440, 50); return (0); case TERM_XTRA_SHAPE: return (0); case TERM_XTRA_EVENT: return (CheckEvents(FALSE)); } return (1); } static errr Term_curs_pipe_emx(int x, int y) { termPipe *tp=(termPipe*)Term; if (!tp->out) return -1; fputc(PIP_CURS, tp->out); fwrite(&x, sizeof(x), 1, tp->out); fwrite(&y, sizeof(y), 1, tp->out); fflush(tp->out); return (0); } static errr Term_wipe_pipe_emx(int x, int y, int n) { termPipe *tp=(termPipe*)Term; if (!tp->out) return -1; fputc(PIP_WIPE, tp->out); fwrite(&x, sizeof(x), 1, tp->out); fwrite(&y, sizeof(y), 1, tp->out); fwrite(&n, sizeof(n), 1, tp->out); fflush(tp->out); return (0); } static errr Term_text_pipe_emx(int x, int y, int n, unsigned char a, cptr s) { termPipe *tp=(termPipe*)Term; if (!tp->out) return -1; fputc(PIP_TEXT, tp->out); fwrite(&x, sizeof(x), 1, tp->out); fwrite(&y, sizeof(y), 1, tp->out); fwrite(&n, sizeof(n), 1, tp->out); fwrite(&a, sizeof(a), 1, tp->out); fwrite(s, n, 1, tp->out); fflush(tp->out); return (0); } static void Term_init_pipe_emx(term *t) { termPipe *tp=(termPipe*)t; if (tp->out) { fputc(PIP_INIT, tp->out); fflush(tp->out); } } static void Term_nuke_pipe_emx(term *t) { termPipe *tp=(termPipe*)t; if (tp->out) { fputc(PIP_NUKE, tp->out); /* Terminate client */ fflush(tp->out); fclose(tp->out); /* Close Pipe */ tp->out=NULL; /* Paranoia */ } } static void initPipeTerm(termPipe *pipe, const char *name, term **termTarget) { term *t; t=&pipe->t; if ((pipe->out=initPipe(name))!=NULL) { /* Initialize the term */ term_init(t, 80, 24, 1); /* Special hooks */ t->init_hook = Term_init_pipe_emx; t->nuke_hook = Term_nuke_pipe_emx; /* Add the hooks */ t->text_hook = Term_text_pipe_emx; t->wipe_hook = Term_wipe_pipe_emx; t->curs_hook = Term_curs_pipe_emx; t->xtra_hook = Term_xtra_pipe_emx; /* Save it */ *termTarget = t; /* Activate it */ Term_activate(t); } } /* * Prepare "term.c" to use "USE_EMX" built-in video library */ errr init_emx(void) { int i; term *t; /* Initialize the pipe windows */ for (i = MAX_TERM_DATA-1; i > 0; --i) { const char *name = angband_term_name[i]; initPipeTerm(&term_screen_aga[i], name, &angband_term[i]); } /* Initialize main window */ t = &term_screen_aga[0].t; /* Initialize the term -- big key buffer */ term_init(t, 80, 24, 1024); /* Special hooks */ t->init_hook = Term_init_emx; t->nuke_hook = Term_nuke_emx; /* Add the hooks */ t->text_hook = Term_text_emx; t->wipe_hook = Term_wipe_emx; t->curs_hook = Term_curs_emx; t->xtra_hook = Term_xtra_emx; /* Save it */ angband_term[0] = t; /* Activate it */ Term_activate(t); /* Success */ return (0); } static FILE *initPipe(const char *name) { char buf[256]; FILE *fi; strnfmt(buf, 256, "\\pipe\\angband\\%s", name); /* Name of pipe */ fi=fopen(buf, "wb"); /* Look for server */ return fi; } #else /* __EMX__CLIENT__ */ int main(int argc, char **argv) { int c, end = 0, lines = 25; int x, y, h, n, v; FILE *in=NULL; char a; char buf[160]; HPIPE pipe; APIRET rc; char *target; /* Check command line */ if (argc!=2 && argc!=3) { printf("Usage: %s Mirror|Recall|Choice|Term-4|...|Term-7 [number of lines]\n" "Start this before angband.exe\n", argv[0]); exit(1); } if (argc==3) lines = atoi(argv[2]); if (lines <= 0) lines = 25; printf("Looking for Angband... press ^C to abort\n"); target=strdup(argv[1]); for (c=0; c */ if (!in) { printf("Sorry, the pipe connection to Angband could not be established.\n"); exit(1); } printf("Connected.\n"); strnfmt(buf, 160, "mode co80,%d", lines); system(buf); /* Infinite loop */ while (!end) { /* Get command */ c = fgetc(in); switch (c) { case PIP_XTRA: if (!fread(&n, sizeof(x), 1, in) || !fread(&v, sizeof(y), 1, in)) abort(); /* This hack prevents another hack */ printf("Sorry, angband.exe and aclient.exe don't fit together.\n"); exit(1); break; case PIP_CURS: if (!fread(&x, sizeof(x), 1, in) || !fread(&y, sizeof(y), 1, in)) abort(); Term_curs_emx(x, y); break; case PIP_WIPE: if (!fread(&x, sizeof(x), 1, in) || !fread(&y, sizeof(y), 1, in) || !fread(&n, sizeof(n), 1, in)) abort(); Term_wipe_emx(x, y, n); break; case PIP_TEXT: if (!fread(&x, sizeof(x), 1, in) || !fread(&y, sizeof(y), 1, in) || !fread(&n, sizeof(n), 1, in) || !fread(&a, sizeof(a), 1, in) || (n > 160) || !fread(buf, n, 1, in)) abort(); Term_text_emx(x, y, n, a, buf); break; case PIP_INIT: Term_init_emx(NULL); break; case PIP_NUKE: case EOF: default: Term_nuke_emx(NULL); end=1; break; } } return 0; } #endif /* __EMX__CLIENT__ */ #else /* EMXPM */ void emx_endPM(const char *reason); int emx_options(char **ANGBAND_DIR_USER, char **ANGBAND_DIR_SAVE, char **ANGBAND_DIR_INFO, char *arg_force_roguelike, char *arg_force_original, char *arg_fiddle, char *arg_wizard, char player_name[32]); void emx_init_window(void **instance, void *main_instance, int n); errr emx_curs(void *instance, int x, int y); errr emx_wipe(void *instance, int x, int y, int n); errr emx_text(void *instance, int x, int y, int n, unsigned char a, cptr s); void emx_init(void *instance); void emx_nuke(void *instance); int emx_read_kbd(void *instance, int wait); void emx_clear(void *instance); void emx_hidecursor(void *instance); void emx_showcursor(void *instance); /* * termWindow* is sometimes cast to term* and vice versa, * so "term t;" must be the first line */ typedef struct { term t; void *instance; /* Pointer to window */ } termWindow; /* * Display a cursor, on top of a given attr/char */ static errr Term_curs_emx(int x, int y) { return emx_curs(((termWindow*)Term)->instance, x, y); } /* * Erase a grid of space (as if spaces were printed) */ static errr Term_wipe_emx(int x, int y, int n) { return emx_wipe(((termWindow*)Term)->instance, x, y, n); } /* * Draw some text, wiping behind it first */ static errr Term_text_emx(int x, int y, int n, unsigned char a, cptr s) { return emx_text(((termWindow*)Term)->instance, x, y, n, a, s); } /* * EMX initialization */ static void Term_init_emx(term *t) { return emx_init(((termWindow*)t)->instance); } /* * EMX shutdown */ static void Term_nuke_emx(term *t) { } /* * Oh no, more prototypes! */ static errr CheckEvents(int returnImmediately); /* * Main initialization function */ errr init_emx(void); /* * The screens */ static termWindow term_screen_aga[MAX_TERM_DATA]; /* * Check for events -- called by "Term_scan_emx()" */ static errr CheckEvents(int returnImmediately) { /* Get key - Macro triggers are generated by emx_read_kbd() */ int k=emx_read_kbd(((termWindow*)Term)->instance, returnImmediately?0:1); /* Nothing ready */ if (k < 0) return (1); /* Enqueue the key */ Term_keypress(k); /* Success */ return (0); } /* * Do a special thing (beep, flush, etc) */ static errr Term_xtra_emx(int n, int v) { void *instance=((termWindow*)Term)->instance; switch (n) { case TERM_XTRA_SHAPE: if (v) { emx_showcursor(instance); } else { emx_hidecursor(instance); } return (0); case TERM_XTRA_NOISE: DosBeep(440, 50); return (0); case TERM_XTRA_FLUSH: while (!CheckEvents(TRUE)); return 0; case TERM_XTRA_EVENT: return (CheckEvents(!v)); case TERM_XTRA_DELAY: if (v > 0) _sleep2(v); return (0); } return (1); } void emx_init_term(termWindow *t, void *main_instance, term **angTerm, int n) { term *te=(term*)t; /* Initialize window */ emx_init_window(&t->instance, main_instance, n); *angTerm=te; /* Initialize the term -- big key buffer */ term_init(te, 80, 24, 1024); /* Special hooks */ te->init_hook = Term_init_emx; te->nuke_hook = Term_nuke_emx; /* Add the hooks */ te->text_hook = Term_text_emx; te->wipe_hook = Term_wipe_emx; te->curs_hook = Term_curs_emx; te->xtra_hook = Term_xtra_emx; } /* * Prepare "term.c" to use "USE_EMX" built-in faked video library */ errr init_emx(void) { int i; /* Initialize the windows */ emx_init_term(&term_screen_aga[0], NULL, &angband_term[0], 0); for (i = 1; i < MAX_TERM_DATA; ++i) { emx_init_term(&term_screen_aga[i], term_screen_aga[0].instance, &angband_term[i], i); } /* Activate main window */ Term_activate(angband_term[0]); /* Success */ return (0); } static void init_stuff(void) { char path[1024]; cptr tail; /* Get the environment variable */ tail = getenv("ANGBAND_PATH"); /* Use the angband_path, or a default */ strcpy(path, tail ? tail : DEFAULT_PATH); /* Hack -- Add a path separator (only if needed) */ if (!suffix(path, PATH_SEP)) strcat(path, PATH_SEP); /* Initialize */ init_file_paths(path); } static void quit_hook(cptr s) { int i; for (i = MAX_TERM_DATA - 1; i >= 0; --i) { /* Shut down the term windows */ if (angband_term[i]) { term_nuke(angband_term[i]); emx_nuke(((termWindow*)angband_term[i])->instance); } } /* Shut down window system - doesn't return */ emx_endPM(s); } void angbandThread(void *arg) { bool new_game = FALSE; int show_score = 0; char player_name_aga[32]; /* Save the "program name" */ argv0 = (char*)arg; /* Use the "main-emx.c" support */ init_emx(); ANGBAND_SYS = "ibm"; /* Get the file paths */ init_stuff(); if (!emx_options((char**)&ANGBAND_DIR_USER, (char**)&ANGBAND_DIR_SAVE, (char**)&ANGBAND_DIR_INFO, &arg_force_roguelike, &arg_force_original, &arg_fiddle, &arg_wizard, player_name_aga)) quit(NULL); /* XXX XXX XXX (?) */ strcpy(player_name, player_name_aga); /* Process the player name */ process_player_name(TRUE); /* Tell "quit()" to call "Term_nuke()" */ quit_aux = quit_hook; /* If requested, display scores and quit */ if (show_score > 0) display_scores(0, show_score); /* Catch nasty signals */ signals_init(); /* Initialize */ init_angband(); /* Wait for response */ pause_line(23); /* Play the game */ play_game(new_game); /* Quit */ quit(NULL); } #endif /* EMXPM */ #endif /* USE_EMX */ /* * Local Variables: * comment-column: 45 * End: * */ zangband/src/main-gcu.c0000755000000000000000000005366110250356274014015 0ustar rootroot/* File: main-gcu.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband run on Unix/Curses machines. * * * To use this file, you must define "USE_GCU" in the Makefile. * * * Hack -- note that "angband.h" is included AFTER the #ifdef test. * This was necessary because of annoying "curses.h" silliness. * * Note that this file is not "intended" to support non-Unix machines, * nor is it intended to support VMS or other bizarre setups. * * Also, this package assumes that the underlying "curses" handles both * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below. * * This code should work with most versions of "curses" or "ncurses", * and the "main-ncu.c" file (and USE_NCU define) are no longer used. * * See also "USE_CAP" and "main-cap.c" for code that bypasses "curses" * and uses the "termcap" information directly, or even bypasses the * "termcap" information and sends direct vt100 escape sequences. * * This file provides up to 4 term windows. * * This file will attempt to redefine the screen colors to conform to * standard Angband colors. It will only do so if the terminal type * indicates that it can do so. See the page: * * http://www.umr.edu/~keldon/ang-patch/ncurses_color.html * * for information on this. * * Consider the use of "savetty()" and "resetty()". XXX XXX XXX */ #include "angband.h" #ifdef USE_GCU cptr help_gcu[] = { "To use GCU (GNU Curses)", NULL }; /* * Hack -- play games with "bool" */ #undef bool /* Avoid 'struct term' name conflict with (via ) on AIX */ #define term System_term /* * Include the proper "header" file */ #ifdef USE_NCURSES # include #else # include #endif #undef term /* * Try redefining the colors at startup. */ #define REDEFINE_COLORS /* * Hack -- try to guess which systems use what commands * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set. * Mega-Hack -- try to guess when "POSIX" is available. * If the user defines two of these, we will probably crash. */ #if !defined(USE_TPOSIX) # if !defined(USE_TERMIO) && !defined(USE_TCHARS) # if defined(_POSIX_VERSION) # define USE_TPOSIX # else # if defined(USG) || defined(linux) || defined(SOLARIS) # define USE_TERMIO # else # define USE_TCHARS # endif # endif # endif #endif /* * Hack -- Amiga uses "fake curses" and cannot do any of this stuff */ #if defined(AMIGA) # undef USE_TPOSIX # undef USE_TERMIO # undef USE_TCHARS #endif /* * POSIX stuff */ #ifdef USE_TPOSIX # include # include #endif /* * One version needs these files */ #ifdef USE_TERMIO # include # include #endif /* * The other needs these files */ #ifdef USE_TCHARS # include # include # include # include # include #endif /* * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY" * * They should both work due to the "(i != 1)" test below. */ #ifndef O_NDELAY # define O_NDELAY O_NONBLOCK #endif /* * OPTION: some machines lack "cbreak()" * On these machines, we use an older definition */ /* #define cbreak() crmode() */ /* * OPTION: some machines cannot handle "nonl()" and "nl()" * On these machines, we can simply ignore those commands. */ /* #define nonl() */ /* #define nl() */ /* * Save the "normal" and "angband" terminal settings */ #ifdef USE_TPOSIX static struct termios norm_termios; static struct termios game_termios; #endif #ifdef USE_TERMIO static struct termio norm_termio; static struct termio game_termio; #endif #ifdef USE_TCHARS static struct ltchars norm_special_chars; static struct sgttyb norm_ttyb; static struct tchars norm_tchars; static int norm_local_chars; static struct ltchars game_special_chars; static struct sgttyb game_ttyb; static struct tchars game_tchars; static int game_local_chars; #endif /* * Information about a term */ typedef struct term_data term_data; struct term_data { term t; /* All term info */ WINDOW *win; /* Pointer to the curses window */ }; /* Max number of windows on screen */ #define MAX_TERM_DATA 4 /* Information about our windows */ static term_data data[MAX_TERM_DATA]; /* * Hack -- Number of initialized "term" structures */ static int active = 0; #ifdef A_COLOR /* * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many * machines, "A_BRIGHT" produces ugly "inverse" video. */ #ifndef A_BRIGHT # define A_BRIGHT A_BOLD #endif /* * Software flag -- we are allowed to use color */ static int can_use_color = FALSE; /* * Software flag -- we are allowed to change the colors */ static int can_fix_color = FALSE; /* * Simple Angband to Curses color conversion table */ static int colortable[16]; #endif /* A_COLOR */ #ifdef USE_GRAPHICS static bool use_blocks = FALSE; #endif /* USE_GRAPHICS */ /* * Place the "keymap" into its "normal" state */ static void keymap_norm(void) { #ifdef USE_TPOSIX /* restore the saved values of the special chars */ (void)tcsetattr(0, TCSAFLUSH, &norm_termios); #endif #ifdef USE_TERMIO /* restore the saved values of the special chars */ (void)ioctl(0, TCSETA, (char *)&norm_termio); #endif #ifdef USE_TCHARS /* restore the saved values of the special chars */ (void)ioctl(0, TIOCSLTC, (char *)&norm_special_chars); (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb); (void)ioctl(0, TIOCSETC, (char *)&norm_tchars); (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars); #endif } /* * Place the "keymap" into the "game" state */ static void keymap_game(void) { #ifdef USE_TPOSIX /* restore the saved values of the special chars */ (void)tcsetattr(0, TCSAFLUSH, &game_termios); #endif #ifdef USE_TERMIO /* restore the saved values of the special chars */ (void)ioctl(0, TCSETA, (char *)&game_termio); #endif #ifdef USE_TCHARS /* restore the saved values of the special chars */ (void)ioctl(0, TIOCSLTC, (char *)&game_special_chars); (void)ioctl(0, TIOCSETP, (char *)&game_ttyb); (void)ioctl(0, TIOCSETC, (char *)&game_tchars); (void)ioctl(0, TIOCLSET, (char *)&game_local_chars); #endif } /* * Save the normal keymap */ static void keymap_norm_prepare(void) { #ifdef USE_TPOSIX /* Get the normal keymap */ tcgetattr(0, &norm_termios); #endif #ifdef USE_TERMIO /* Get the normal keymap */ (void)ioctl(0, TCGETA, (char *)&norm_termio); #endif #ifdef USE_TCHARS /* Get the normal keymap */ (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb); (void)ioctl(0, TIOCGLTC, (char *)&norm_special_chars); (void)ioctl(0, TIOCGETC, (char *)&norm_tchars); (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars); #endif } /* * Save the keymaps (normal and game) */ static void keymap_game_prepare(void) { #ifdef USE_TPOSIX /* Acquire the current mapping */ tcgetattr(0, &game_termios); /* Force "Ctrl-C" to interupt */ game_termios.c_cc[VINTR] = (char)3; /* Force "Ctrl-Z" to suspend */ game_termios.c_cc[VSUSP] = (char)26; /* Hack -- Leave "VSTART/VSTOP" alone */ /* Disable the standard control characters */ game_termios.c_cc[VQUIT] = (char)-1; game_termios.c_cc[VERASE] = (char)-1; game_termios.c_cc[VKILL] = (char)-1; game_termios.c_cc[VEOF] = (char)-1; game_termios.c_cc[VEOL] = (char)-1; /* Normally, block until a character is read */ game_termios.c_cc[VMIN] = 1; game_termios.c_cc[VTIME] = 0; #endif #ifdef USE_TERMIO /* Acquire the current mapping */ (void)ioctl(0, TCGETA, (char *)&game_termio); /* Force "Ctrl-C" to interupt */ game_termio.c_cc[VINTR] = (char)3; /* Force "Ctrl-Z" to suspend */ game_termio.c_cc[VSUSP] = (char)26; /* Hack -- Leave "VSTART/VSTOP" alone */ /* Disable the standard control characters */ game_termio.c_cc[VQUIT] = (char)-1; game_termio.c_cc[VERASE] = (char)-1; game_termio.c_cc[VKILL] = (char)-1; game_termio.c_cc[VEOF] = (char)-1; game_termio.c_cc[VEOL] = (char)-1; #if 0 /* Disable the non-posix control characters */ game_termio.c_cc[VEOL2] = (char)-1; game_termio.c_cc[VSWTCH] = (char)-1; game_termio.c_cc[VDSUSP] = (char)-1; game_termio.c_cc[VREPRINT] = (char)-1; game_termio.c_cc[VDISCARD] = (char)-1; game_termio.c_cc[VWERASE] = (char)-1; game_termio.c_cc[VLNEXT] = (char)-1; game_termio.c_cc[VSTATUS] = (char)-1; #endif /* Normally, block until a character is read */ game_termio.c_cc[VMIN] = 1; game_termio.c_cc[VTIME] = 0; #endif #ifdef USE_TCHARS /* Get the default game characters */ (void)ioctl(0, TIOCGETP, (char *)&game_ttyb); (void)ioctl(0, TIOCGLTC, (char *)&game_special_chars); (void)ioctl(0, TIOCGETC, (char *)&game_tchars); (void)ioctl(0, TIOCLGET, (char *)&game_local_chars); /* Force suspend (^Z) */ game_special_chars.t_suspc = (char)26; /* Cancel some things */ game_special_chars.t_dsuspc = (char)-1; game_special_chars.t_rprntc = (char)-1; game_special_chars.t_flushc = (char)-1; game_special_chars.t_werasc = (char)-1; game_special_chars.t_lnextc = (char)-1; /* Force interupt (^C) */ game_tchars.t_intrc = (char)3; /* Force start/stop (^Q, ^S) */ game_tchars.t_startc = (char)17; game_tchars.t_stopc = (char)19; /* Cancel some things */ game_tchars.t_quitc = (char)-1; game_tchars.t_eofc = (char)-1; game_tchars.t_brkc = (char)-1; #endif } /* * Suspend/Resume */ static errr Term_xtra_gcu_alive(int v) { int x, y; /* Suspend */ if (!v) { /* Go to normal keymap mode */ keymap_norm(); /* Restore modes */ nocbreak(); echo(); nl(); /* Hack -- make sure the cursor is visible */ Term_xtra(TERM_XTRA_SHAPE, 1); /* Flush the curses buffer */ (void)refresh(); /* Get current cursor position */ getyx(curscr, y, x); /* Move the cursor to bottom right corner */ mvcur(y, x, LINES - 1, 0); /* Exit curses */ endwin(); /* Flush the output */ (void)fflush(stdout); } /* Resume */ else { /* Refresh */ /* (void)touchwin(curscr); */ /* (void)wrefresh(curscr); */ /* Restore the settings */ cbreak(); noecho(); nonl(); /* Go to angband keymap mode */ keymap_game(); } /* Success */ return (0); } /* * Init the "curses" system */ static void Term_init_gcu(term *t) { term_data *td = (term_data *)(t->data); #ifdef USE_GETCH /* * This is necessary to keep the first call to getch() * from clearing the screen */ wrefresh(stdscr); #endif /* USE_GETCH */ /* Count init's, handle first */ if (active++ != 0) return; /* Erase the window */ (void)wclear(td->win); /* Reset the cursor */ (void)wmove(td->win, 0, 0); /* Flush changes */ (void)wrefresh(td->win); /* Game keymap */ keymap_game(); } /* * Nuke the "curses" system */ static void Term_nuke_gcu(term *t) { int x, y; term_data *td = (term_data *)(t->data); /* Delete this window */ delwin(td->win); /* Count nuke's, handle last */ if (--active != 0) return; /* Hack -- make sure the cursor is visible */ Term_xtra(TERM_XTRA_SHAPE, 1); #ifdef A_COLOR /* Reset colors to defaults */ start_color(); #endif /* Get current cursor position */ getyx(curscr, y, x); /* Move the cursor to bottom right corner */ mvcur(y, x, LINES - 1, 0); /* Flush the curses buffer */ (void)refresh(); /* Exit curses */ endwin(); /* Flush the output */ (void)fflush(stdout); /* Normal keymap */ keymap_norm(); } #ifdef USE_GETCH /* * Process events, with optional wait */ static errr Term_xtra_gcu_event(int v) { int i, k; /* Wait */ if (v) { /* Paranoia -- Wait for it */ nodelay(stdscr, FALSE); /* Get a keypress */ i = getch(); /* Mega-Hack -- allow graceful "suspend" */ for (k = 0; (k < 10) && (i == ERR); k++) i = getch(); /* Broken input is special */ if (i == ERR) exit_game_panic(); if (i == EOF) exit_game_panic(); } /* Do not wait */ else { /* Do not wait for it */ nodelay(stdscr, TRUE); /* Check for keypresses */ i = getch(); /* Wait for it next time */ nodelay(stdscr, FALSE); /* None ready */ if (i == ERR) return (1); if (i == EOF) return (1); } /* Enqueue the keypress */ Term_keypress(i); /* Success */ return (0); } #else /* USE_GETCH */ /* * Process events (with optional wait) */ static errr Term_xtra_gcu_event(int v) { int i, k; char buf[2]; /* Wait */ if (v) { /* Wait for one byte */ i = read(0, buf, 1); /* Hack -- Handle bizarre "errors" */ if ((i <= 0) && (errno != EINTR)) exit_game_panic(); } /* Do not wait */ else { /* Get the current flags for stdin */ k = fcntl(0, F_GETFL, 0); /* Oops */ if (k < 0) return (1); /* Tell stdin not to block */ if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1); /* Read one byte, if possible */ i = read(0, buf, 1); /* Replace the flags for stdin */ if (fcntl(0, F_SETFL, k)) return (1); } /* Ignore "invalid" keys */ if ((i != 1) || (!buf[0])) return (1); /* Enqueue the keypress */ Term_keypress(buf[0]); /* Success */ return (0); } #endif /* USE_GETCH */ /* * React to changes */ static errr Term_xtra_gcu_react(void) { #ifdef A_COLOR int i; /* Cannot handle color redefinition */ if (!can_fix_color) return (0); /* Set the colors */ for (i = 0; i < 16; i++) { /* Set one color (note scaling) */ init_color(i, angband_color_table[i][1] * 1000 / 255, angband_color_table[i][2] * 1000 / 255, angband_color_table[i][3] * 1000 / 255); } #endif /* Success */ return (0); } /* * Handle a "special request" */ static errr Term_xtra_gcu(int n, int v) { term_data *td = (term_data *)(Term->data); /* Analyze the request */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: (void)write(1, "\007", 1); return (0); /* Flush the Curses buffer */ case TERM_XTRA_FRESH: (void)wrefresh(td->win); return (0); #ifdef USE_CURS_SET /* Change the cursor visibility */ case TERM_XTRA_SHAPE: curs_set(v); return (0); #endif /* Suspend/Resume curses */ case TERM_XTRA_ALIVE: return (Term_xtra_gcu_alive(v)); /* Process events */ case TERM_XTRA_EVENT: return (Term_xtra_gcu_event(v)); /* Flush events */ case TERM_XTRA_FLUSH: while (!Term_xtra_gcu_event(FALSE)); return (0); /* Delay */ case TERM_XTRA_DELAY: if (v > 0) usleep(1000 * v); return (0); /* React to events */ case TERM_XTRA_REACT: Term_xtra_gcu_react(); return (0); } /* Unknown */ return (1); } /* * Actually MOVE the hardware cursor */ static errr Term_curs_gcu(int x, int y) { term_data *td = (term_data *)(Term->data); /* Literally move the cursor */ wmove(td->win, y, x); /* Success */ return (0); } /* * Erase a grid of space * Hack -- try to be "semi-efficient". */ static errr Term_wipe_gcu(int x, int y, int n) { term_data *td = (term_data *)(Term->data); /* Place cursor */ wmove(td->win, y, x); /* Clear to end of line */ if (x + n >= td->t.wid) { wclrtoeol(td->win); } /* Clear some characters */ else { while (n-- > 0) waddch(td->win, ' '); } /* Success */ return (0); } /* * Place some text on the screen using an attribute */ static errr Term_text_gcu(int x, int y, int n, byte a, cptr s) { term_data *td = (term_data *)(Term->data); int i; #ifdef USE_GRAPHICS int pic; #endif #ifdef A_COLOR /* Set the color */ if (can_use_color) wattrset(td->win, colortable[a & 0x0F]); #endif /* Move the cursor */ wmove(td->win, y, x); /* Draw each character */ for (i = 0; i < n; i++) { #ifdef USE_GRAPHICS /* Special characters? */ if (use_blocks) { #ifdef ACS_CKBOARD /* Determine picture to use */ if (s[i] == '#') { /* Walls */ pic = ACS_CKBOARD; /* * Note that veins are '#' as well now. * Trees are '%' now - and this looks bad when redefined. */ } else { pic = s[i]; } #else /* ACS_CKBOARD */ pic = s[i]; #endif /* ACS_CKBOARD */ /* Draw the picture */ waddch(td->win, pic); /* Next character */ continue; } #endif /* Draw a normal character */ waddch(td->win, s[i]); } /* Success */ return (0); } /* * Create a window for the given "term_data" argument. */ static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x) { term *t = &td->t; /* Check window size */ if (rows <= 0 || cols <= 0) return (0); /* Create new window */ td->win = newwin(rows, cols, y, x); /* Check for failure */ if (!td->win) { /* Error */ quit("Failed to setup curses window."); } /* Initialize the term */ term_init(t, cols, rows, 256); /* Avoid bottom right corner */ t->icky_corner = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Set some hooks */ t->init_hook = Term_init_gcu; t->nuke_hook = Term_nuke_gcu; /* Set some more hooks */ t->text_hook = Term_text_gcu; t->wipe_hook = Term_wipe_gcu; t->curs_hook = Term_curs_gcu; t->xtra_hook = Term_xtra_gcu; /* Save the data */ t->data = td; /* Activate it */ Term_activate(t); /* Success */ return (0); } /* The hook to exit curses on a failure */ static void hook_quit(cptr str) { /* Ignore parameter */ (void) str; /* Exit curses */ endwin(); } /* * Prepare "curses" for use by the file "z-term.c" * * Installs the "hook" functions defined above, and then activates * the main screen "term", which clears the screen and such things. * * Someone should really check the semantics of "initscr()" */ errr init_gcu(void) { int i; int num_term = MAX_TERM_DATA, next_win = 0; /* Extract the normal keymap */ keymap_norm_prepare(); #if defined(USG) /* Initialize for USG Unix */ if (initscr() == NULL) return (-1); #else /* Initialize for other systems */ if (initscr() == (WINDOW*)ERR) return (-1); #endif /* Activate hooks */ quit_aux = hook_quit; core_aux = hook_quit; /* Require standard size screen */ if ((LINES < 24) || (COLS < 80)) { quit("Angband needs at least an 80x24 'curses' screen"); } #ifdef USE_GRAPHICS /* Set graphics flag */ use_graphics = GRAPHICS_NONE; /* Use the graphical wall tiles? */ use_blocks = arg_graphics; #endif #ifdef A_COLOR /*** Init the Color-pairs and set up a translation table ***/ /* Do we have color, and enough color, available? */ can_use_color = ((start_color() != ERR) && has_colors() && (COLORS >= 8) && (COLOR_PAIRS >= 8)); #ifdef REDEFINE_COLORS /* Can we change colors? */ can_fix_color = (can_use_color && can_change_color() && (COLORS >= 16) && (COLOR_PAIRS > 8)); #endif /* Attempt to use customized colors */ if (can_fix_color) { /* Prepare the color pairs */ for (i = 1; i <= 8; i++) { /* Reset the color */ if (init_pair(i, i - 1, 0) == ERR) { quit("Color pair init failed"); } /* Set up the colormap */ colortable[i - 1] = (COLOR_PAIR(i) | A_NORMAL); colortable[i + 7] = (COLOR_PAIR(i) | A_BRIGHT); } /* XXX XXX XXX Take account of "gamma correction" */ /* Prepare the "Angband Colors" */ Term_xtra_gcu_react(); } /* Attempt to use colors */ else if (can_use_color) { /* Color-pair 0 is *always* WHITE on BLACK */ /* Prepare the color pairs */ init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_YELLOW, COLOR_BLACK); init_pair(4, COLOR_BLUE, COLOR_BLACK); init_pair(5, COLOR_MAGENTA, COLOR_BLACK); init_pair(6, COLOR_CYAN, COLOR_BLACK); init_pair(7, COLOR_BLACK, COLOR_BLACK); /* Prepare the "Angband Colors" -- Bright white is too bright */ colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */ colortable[1] = (COLOR_PAIR(0) | A_BRIGHT); /* White */ colortable[2] = (COLOR_PAIR(0) | A_NORMAL); /* Grey XXX */ colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */ colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */ colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */ colortable[6] = (COLOR_PAIR(4) | A_NORMAL); /* Blue */ colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */ colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */ colortable[9] = (COLOR_PAIR(0) | A_NORMAL); /* Light-grey XXX */ colortable[10] = (COLOR_PAIR(5) | A_NORMAL); /* Purple */ colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */ colortable[12] = (COLOR_PAIR(5) | A_BRIGHT); /* Light Red XXX */ colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */ colortable[14] = (COLOR_PAIR(4) | A_BRIGHT); /* Light Blue */ colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */ } #endif /*** Low level preparation ***/ #ifdef USE_GETCH /* Paranoia -- Assume no waiting */ nodelay(stdscr, FALSE); #endif /* Prepare */ cbreak(); noecho(); nonl(); /* Extract the game keymap */ keymap_game_prepare(); /*** Now prepare the term(s) ***/ /* Create several terms */ for (i = 0; i < num_term; i++) { int rows, cols, y, x; /* Hack - the main window is huge */ /* Work out how much extra room we have */ cols = (COLS - 80) / 4; rows = (LINES - 24) / 2; /* Prevent stupidly small windows */ if (cols < 25) cols = 0; if (rows < 10) rows = 0; /* Ok - so we now have the size of the main window */ cols += 80; rows += 24; /* Decide on size and position */ switch (i) { /* Upper left */ case 0: y = x = 0; break; /* Lower left */ case 1: y = rows + 1; x = 0; rows = LINES - (rows + 1); break; /* Upper right */ case 2: y = 0; x = cols + 1; cols = COLS - (cols + 1); break; /* Lower right */ case 3: y = rows + 1; x = cols + 1; rows = LINES - (rows + 1); cols = COLS - (cols + 1); break; /* XXX */ default: rows = cols = y = x = 0; break; } /* Skip non-existant windows */ if (rows <= 0 || cols <= 0) continue; /* Create a term */ term_data_init_gcu(&data[next_win], rows, cols, y, x); /* Remember the term */ angband_term[next_win] = &data[next_win].t; /* One more window */ next_win++; } /* Activate the "Angband" window screen */ Term_activate(&data[0].t); /* Remember the active screen */ term_screen = &data[0].t; /* Success */ return (0); } #endif /* USE_GCU */ zangband/src/main-gtk.c0000644000000000000000000017317010250356274014017 0ustar rootroot/* File: main-gtk.c */ /* * Copyright (c) 2000 Robert Ruehlmann * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * Robert Ruehlmann wrote the original Gtk port. Since an initial work is * much harder than enhancements, his effort worth more credits than * others. * * Steven Fuerst implemented colour-depth independent X server support, * graphics, resizing and big screen support for ZAngband as well as * fast image rescaling that is included here. * * "pelpel" added GtkItemFactory based menu system and added comments. */ #include "angband.h" #include "maid-grf.h" #ifdef USE_GTK cptr help_gtk[] = { "To use GTK toolkit", #ifdef USE_GRAPHICS "-b# Set tileset bitmap", #endif /* USE_GRAPHICS */ "-n# Number of terms to use", NULL }; /* Mega-hack, these include files require double and float to work */ #undef float #undef double /* Ansi C please */ #define __STRICT_ANSI__ #include #include /* Mega-hack redefine them again */ #undef float #define float floating_point_is_not_allowed #undef double #define double floating_point_is_not_allowed /* * Number of pixels inserted between the menu bar and the main screen */ #define NO_PADDING 0 /* * Largest possible number of terminal windows supported by the game */ #define MAX_TERM_DATA 8 /* * Extra data to associate with each "window" * * Each "window" is represented by a "term_data" structure, which * contains a "term" structure, which contains a pointer (t->data) * back to the term_data structure. */ typedef struct term_data term_data; struct term_data { term t; GtkWidget *window; GtkWidget *drawing_area; GdkFont *font; GdkPixmap *pixmap; GdkGC *gc; #ifdef USE_GRAPHICS GdkImage *tiles; GdkImage *b_tiles; GdkImage *temp; #endif /* USE_GRAPHICS */ int font_wid; int font_twid; int font_hgt; int rows; int cols; cptr name; cptr fontname; bool shown; }; typedef struct infoclr infoclr; struct infoclr { /* The allocated pixel */ GdkColor pixel; /* The colour values we tried to allocate */ byte red; byte green; byte blue; }; /* * An array of "term_data" structures, one for each "sub-window" */ static term_data data[MAX_TERM_DATA]; /* * game in progress */ static bool game_in_progress = FALSE; /* * Start a new game */ static bool gtk_newgame; /* * Hack - exit the game */ static bool gtk_exitgame = FALSE; /* * Number of active terms */ static int num_term = 3; #ifdef SUPPORT_GAMMA static bool gamma_table_ready = FALSE; static int gamma_val = 0; #endif /* SUPPORT_GAMMA */ /* The colours */ static infoclr colours[256]; #ifdef USE_GRAPHICS static GdkImage *tiles_norm; static int xsize; static int ysize; static guint32 black_pixel; #endif /* USE_GRAPHICS */ /* Temp return value for create_pixel */ static GdkColor temp_color; /* * Hack -- Convert an RGB value to an X11 Pixel, or die. */ static GdkColor *create_pixel(byte red, byte green, byte blue) { #ifdef SUPPORT_GAMMA if (!gamma_table_ready) { cptr str = getenv("ANGBAND_X11_GAMMA"); if (str != NULL) gamma_val = atoi(str); gamma_table_ready = TRUE; /* Only need to build the table if gamma exists */ if (gamma_val) build_gamma_table(gamma_val); } /* Hack -- Gamma Correction */ if (gamma_val > 0) { red = gamma_table[red]; green = gamma_table[green]; blue = gamma_table[blue]; } #endif /* SUPPORT_GAMMA */ /* Build the color */ temp_color.red = red * 255; temp_color.green = green * 255; temp_color.blue = blue * 255; /* Attempt to Allocate the Parsed color */ if (!gdk_colormap_alloc_color(gdk_colormap_get_system(), &temp_color, FALSE, TRUE)) { g_print("Couldn't allocate color."); } return (&temp_color); } static void store_pixel_colors(void) { int i; infoclr *clr; for (i = 0; i < 256; i++) { clr = &colours[i]; /* Get the rgb of the colour */ clr->red = angband_color_table[i][1]; clr->green = angband_color_table[i][2]; clr->blue = angband_color_table[i][3]; /* Create the colour structure */ clr->pixel = *create_pixel(clr->red, clr->green, clr->blue); } } static errr Term_xtra_gtk_react(void) { int i; infoclr *clr; bool redraw = FALSE; /* Hack - check colours */ for (i = 0; i < 256; i++) { clr = &colours[i]; /* Has the colour changed? */ if ((clr->red != angband_color_table[i][1]) || (clr->green != angband_color_table[i][2]) || (clr->blue != angband_color_table[i][3])) { /* Save new colour values */ clr->red = angband_color_table[i][1]; clr->green = angband_color_table[i][2]; clr->blue = angband_color_table[i][3]; /* Create the colour structure */ clr->pixel = *create_pixel(clr->red, clr->green, clr->blue); /* Set flag */ redraw = TRUE; } } /* Hack - Redraw it if the colours have changed */ if (redraw) Term_redraw(); /* Success */ return (0); } #ifdef USE_GRAPHICS /* * The Win32 "BITMAPFILEHEADER" type. */ typedef struct BITMAPFILEHEADER { u16b bfType; u32b bfSize; u16b bfReserved1; u16b bfReserved2; u32b bfOffBits; } BITMAPFILEHEADER; /* * The Win32 "BITMAPINFOHEADER" type. */ typedef struct BITMAPINFOHEADER { u32b biSize; u32b biWidth; u32b biHeight; u16b biPlanes; u16b biBitCount; u32b biCompresion; u32b biSizeImage; u32b biXPelsPerMeter; u32b biYPelsPerMeter; u32b biClrUsed; u32b biClrImportand; } BITMAPINFOHEADER; /* * The Win32 "RGBQUAD" type. */ typedef struct RGBQUAD { unsigned char b, g, r; unsigned char filler; } RGBQUAD; /*** Helper functions for system independent file loading. ***/ static byte get_byte(FILE *fff) { /* Get a character, and return it */ return (getc(fff) & 0xFF); } static void rd_byte(FILE *fff, byte *ip) { *ip = get_byte(fff); } static void rd_u16b(FILE *fff, u16b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u16b)(get_byte(fff)) << 8); } static void rd_u32b(FILE *fff, u32b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u32b)(get_byte(fff)) << 8); (*ip) |= ((u32b)(get_byte(fff)) << 16); (*ip) |= ((u32b)(get_byte(fff)) << 24); } /* * Read a Win32 BMP file. * * This function replaces the old ReadRaw and RemapColors functions. * * Assumes that the bitmap has a size such that no padding is needed in * various places. Currently only handles bitmaps with 3 to 256 colors. */ static void ReadBMP(char *Name) { FILE *f; BITMAPFILEHEADER fileheader; BITMAPINFOHEADER infoheader; int ncol; int i; u32b x, y; gint32 clr_pixels[256]; /* No tiles yet */ tiles_norm = NULL; /* Open the BMP file */ f = fopen(Name, "r"); /* No such file */ if (f == NULL) return; /* Read the "BITMAPFILEHEADER" */ rd_u16b(f, &(fileheader.bfType)); rd_u32b(f, &(fileheader.bfSize)); rd_u16b(f, &(fileheader.bfReserved1)); rd_u16b(f, &(fileheader.bfReserved2)); rd_u32b(f, &(fileheader.bfOffBits)); /* Read the "BITMAPINFOHEADER" */ rd_u32b(f, &(infoheader.biSize)); rd_u32b(f, &(infoheader.biWidth)); rd_u32b(f, &(infoheader.biHeight)); rd_u16b(f, &(infoheader.biPlanes)); rd_u16b(f, &(infoheader.biBitCount)); rd_u32b(f, &(infoheader.biCompresion)); rd_u32b(f, &(infoheader.biSizeImage)); rd_u32b(f, &(infoheader.biXPelsPerMeter)); rd_u32b(f, &(infoheader.biYPelsPerMeter)); rd_u32b(f, &(infoheader.biClrUsed)); rd_u32b(f, &(infoheader.biClrImportand)); /* Verify the header */ if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) { plog_fmt("Incorrect BMP file format %s", Name); return; } /* The two headers above occupy 54 bytes total */ /* The "bfOffBits" field says where the data starts */ /* The "biClrUsed" field does not seem to be reliable */ /* Compute number of colors recorded */ ncol = (fileheader.bfOffBits - 54) / 4; for (i = 0; i < ncol; i++) { RGBQUAD clrg; /* Read an "RGBQUAD" */ rd_byte(f, &(clrg.b)); rd_byte(f, &(clrg.g)); rd_byte(f, &(clrg.r)); rd_byte(f, &(clrg.filler)); /* Analyze the color */ clr_pixels[i] = create_pixel(clrg.r, clrg.g, clrg.b)->pixel; } /* Make the normal bitmap */ tiles_norm = gdk_image_new(GDK_IMAGE_FASTEST, gdk_visual_get_system(), infoheader.biWidth, infoheader.biHeight); /* Failure */ if (tiles_norm == NULL) { fclose(f); return; } for (y = 0; y < infoheader.biHeight; y++) { u32b y2 = infoheader.biHeight - y - 1; for (x = 0; x < infoheader.biWidth; x++) { int ch = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) { plog_fmt("Unexpected end of file in %s", Name); /* Delete reference to image */ gdk_image_destroy(tiles_norm); /* Hack - clear tiles */ tiles_norm = NULL; return; } if (infoheader.biBitCount == 24) { int c3, c2 = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) { plog_fmt("Unexpected end of file in %s", Name); /* Delete reference to image */ gdk_image_destroy(tiles_norm); /* Hack - clear tiles */ tiles_norm = NULL; return; } c3 = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) { plog_fmt("Unexpected end of file in %s", Name); /* Delete reference to image */ gdk_image_destroy(tiles_norm); /* Hack - clear tiles */ tiles_norm = NULL; return; } gdk_image_put_pixel(tiles_norm, x, y2, create_pixel(ch, c2, c3)->pixel); } else if (infoheader.biBitCount == 8) { gdk_image_put_pixel(tiles_norm, x, y2, clr_pixels[ch]); } else if (infoheader.biBitCount == 4) { gdk_image_put_pixel(tiles_norm, x, y2, clr_pixels[ch / 16]); x++; gdk_image_put_pixel(tiles_norm, x, y2, clr_pixels[ch % 16]); } else { /* Technically 1 bit is legal too */ plog_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name); { /* Delete reference to image */ gdk_image_destroy(tiles_norm); /* Hack - clear tiles */ tiles_norm = NULL; return; } } } } /* Close the file */ fclose(f); } static void copy_pixels8(int wid, int y, int offset, int *xoffsets, GdkImage *new_image) { int i; /* Get source and destination */ byte *src = &((byte *)tiles_norm->mem)[offset * tiles_norm->bpl]; byte *dst = &((byte *)new_image->mem)[y * new_image->bpl]; /* Copy to the image */ for (i = 0; i < wid; i++) { *dst++ = src[xoffsets[i]]; } } static void copy_pixels16(int wid, int y, int offset, int *xoffsets, GdkImage *new_image) { int i; /* Get source and destination */ byte *src = &((byte *)tiles_norm->mem)[offset * tiles_norm->bpl]; byte *dst = &((byte *)new_image->mem)[y * new_image->bpl]; /* Copy to the image */ for (i = 0; i < wid; i++) { *dst++ = src[2 * xoffsets[i]]; *dst++ = src[2 * xoffsets[i] + 1]; } } static void copy_pixels24(int wid, int y, int offset, int *xoffsets, GdkImage *new_image) { int i; /* Get source and destination */ byte *src = &((byte *)tiles_norm->mem)[offset * tiles_norm->bpl]; byte *dst = &((byte *)new_image->mem)[y * new_image->bpl]; /* Copy to the image */ for (i = 0; i < wid; i++) { *dst++ = src[3 * xoffsets[i]]; *dst++ = src[3 * xoffsets[i] + 1]; *dst++ = src[3 * xoffsets[i] + 2]; } } static void copy_pixels32(int wid, int y, int offset, int *xoffsets, GdkImage *new_image) { int i; /* Get source and destination */ byte *src = &((byte *)tiles_norm->mem)[offset * tiles_norm->bpl]; byte *dst = &((byte *)new_image->mem)[y * new_image->bpl]; /* Copy to the image */ for (i = 0; i < wid; i++) { *dst++ = src[4 * xoffsets[i]]; *dst++ = src[4 * xoffsets[i] + 1]; *dst++ = src[4 * xoffsets[i] + 2]; *dst++ = src[4 * xoffsets[i] + 3]; } } /* * Resize the tiles for a given font size. */ static GdkImage *resize_tiles(int tile_wid, int tile_hgt) { int i; int add, remainder, rem_tot, offset; int *xoffsets; /* Function pointer used to copy line to image */ void (*copy_pixels)(int, int, int, int*, GdkImage*) = NULL; /* Get the size of the old image */ int old_wid = tiles_norm->width; int old_hgt = tiles_norm->height; /* Get the size of the new image */ int new_wid = (old_wid / xsize) * tile_wid; int new_hgt = (old_hgt / ysize) * tile_hgt; GdkImage *new_image = gdk_image_new(GDK_IMAGE_FASTEST, gdk_visual_get_system(), new_wid, new_hgt); /* Paranoia */ if (!new_image) return (NULL); /* * Calculate an offsets table, so the transformation * is faster. This is much like the Bresenham algorithm */ /* Set up x offset table */ C_MAKE(xoffsets, new_wid, int); /* Initialize line parameters */ add = old_wid / new_wid; remainder = old_wid % new_wid; /* Start at left */ offset = 0; /* Half-tile offset so 'line' is centered correctly */ rem_tot = new_wid / 2; for(i = 0; i < new_wid; i++) { /* Store into the table */ xoffsets[i] = offset; /* Move to next entry */ offset += add; /* Take care of fractional part */ rem_tot += remainder; if (rem_tot >= new_wid) { rem_tot -= new_wid; offset++; } } /* Initialize copy routine */ switch (new_image->bpp) { case 1: copy_pixels = copy_pixels8; break; case 2: copy_pixels = copy_pixels16; break; case 3: copy_pixels = copy_pixels24; break; case 4: copy_pixels = copy_pixels32; break; default: { quit_fmt("Invalid bits per pixel of image: %d", new_image->bpp); break; } } /* Scan each row */ /* Initialize line parameters */ add = old_hgt / new_hgt; remainder = old_hgt % new_hgt; /* Start at left */ offset = 0; /* Half-tile offset so 'line' is centered correctly */ rem_tot = new_hgt / 2; for(i = 0; i < new_hgt; i++) { /* Copy pixels to new image */ copy_pixels(new_wid, i, offset, xoffsets, new_image); /* Move to next entry */ offset += add; /* Take care of fractional part */ rem_tot += remainder; if (rem_tot >= new_hgt) { rem_tot -= new_hgt; offset++; } } /* Free offset table */ FREE(xoffsets); return (new_image); } #endif /* USE_GRAPHICS */ /* * Find the pixel at the top-left corner of a square. */ static void square_to_pixel(int *x, int *y, int ox, int oy) { term_data *td = (term_data*)(Term->data); (*y) = oy * td->font_hgt; if ((use_bigtile) && (oy >= Term->scr->big_y1) && (oy <= Term->scr->big_y2) && (ox > Term->scr->big_x1)) { (*x) = ox * td->font_twid - Term->scr->big_x1 * td->font_wid; } else { (*x) = ox * td->font_wid; } } /* * Erase some characters. */ static errr Term_wipe_gtk(int x, int y, int n) { int x1, y1, x2, y2; term_data *td = (term_data*)(Term->data); /* Don't draw to hidden windows */ if (!td->shown) return (0); g_assert(td->pixmap); g_assert(td->drawing_area->window); /*** Find the dimensions ***/ square_to_pixel(&x1, &y1, x, y); square_to_pixel(&x2, &y2, x + n, y); gdk_draw_rectangle(td->pixmap, td->drawing_area->style->black_gc, TRUE, x1, y1, x2 - x1, td->font_hgt); /* Copy it to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, x1, y1, x1, y1, x2 - x1, td->font_hgt); /* Success */ return (0); } /* * Draw some textual characters. */ static errr Term_text_gtk(int cx, int cy, int n, byte a, cptr s) { int i; term_data *td = (term_data*)(Term->data); GdkColor color; int x, y; int sx, sy; /* Don't draw to hidden windows */ if (!td->shown) return (0); /* Create the colour structure */ color = colours[a].pixel; g_assert(td->pixmap); g_assert(td->drawing_area->window); /* Set the forground colour */ gdk_gc_set_foreground(td->gc, &color); /* Clear the line */ Term_wipe_gtk(cx, cy, n); /*** Decide where to place the string, vertically ***/ square_to_pixel(&x, &y, cx, cy); /* Start location */ sx = x; sy = y; /* Ignore Vertical Justifications */ y += td->font->ascent; /* Draw the text to the pixmap */ for (i = 0; i < n; i++) { if (is_bigtiled(cx + i, cy)) { /* Note that the Infoclr is set up to contain the Infofnt */ gdk_draw_text(td->pixmap, td->font, td->gc, x, y, s + i, 1); x += td->font_twid; } else { /* Note that the Infoclr is set up to contain the Infofnt */ gdk_draw_text(td->pixmap, td->font, td->gc, x, y, s + i, 1); x += td->font_wid; } } /* Copy it to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, sx, sy, sx, sy, x, td->font_hgt); /* Success */ return (0); } static errr Term_curs_gtk(int x, int y) { term_data *td = (term_data*)(Term->data); int x1, y1; /* Don't draw to hidden windows */ if (!td->shown) return (0); g_assert(td->pixmap); g_assert(td->drawing_area->window); gdk_gc_set_foreground(td->gc, &colours[TERM_YELLOW].pixel); square_to_pixel(&x1, &y1, x, y); if (is_bigtiled(x, y)) { gdk_draw_rectangle(td->pixmap, td->gc, FALSE, x1, y1, td->font_twid - 1, td->font_hgt - 1); /* Copy it to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, x1, y1, x1, y1, td->font_twid, td->font_hgt); } else { gdk_draw_rectangle(td->pixmap, td->gc, FALSE, x1, y1, td->font_wid - 1, td->font_hgt - 1); /* Copy it to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, x1, y1, x1, y1, td->font_wid, td->font_hgt); } /* Success */ return (0); } #ifdef USE_GRAPHICS /* * Draw some graphical characters. */ static errr Term_pict_gtk(int ox, int oy, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i; int x1, y1; byte a; char c; byte ta; char tc; int x, y; int sx; int x2, y2; int k, l; guint32 pixel, blank; term_data *td = (term_data*)(Term->data); int wid, hgt = td->font_hgt; GdkImage *tiles; /* Mega Hack^2 - assume the top left corner is "black" */ blank = gdk_image_get_pixel(td->tiles, 0, td->font_hgt * 6); /* Don't draw to hidden windows */ if (!td->shown) return (0); /* Paranoia */ g_assert(td->tiles); g_assert(use_graphics); /* Starting point */ square_to_pixel(&x, &y, ox, oy); /* Save start x coord */ sx = x; for (i = 0; i < n; i++) { /* What are we drawing? */ if (is_bigtiled(ox + i, oy)) { tiles = td->b_tiles; wid = td->font_twid; } else { tiles = td->tiles; wid = td->font_wid; } a = *ap++; c = *cp++; /* For extra speed - cache these values */ x1 = (c&0x7F) * wid; y1 = (a&0x7F) * hgt; ta = *tap++; tc = *tcp++; /* For extra speed - cache these values */ x2 = (tc&0x7F) * wid; y2 = (ta&0x7F) * hgt; /* Optimise the common case */ if (!use_transparency || ((x1 == x2) && (y1 == y2))) { /* Draw object / terrain */ gdk_draw_image(td->pixmap, td->gc, tiles, x1, y1, x, y, wid, hgt); } else { for (k = 0; k < wid; k++) { for (l = 0; l < hgt; l++) { /* If mask set... */ if ((pixel = gdk_image_get_pixel(tiles, x1 + k, y1 + l)) == blank) { /* Output from the terrain */ pixel = gdk_image_get_pixel(tiles, x2 + k, y2 + l); } /* Store into the temp storage. */ gdk_image_put_pixel(td->temp, k, l, pixel); } } /* Draw to screen */ gdk_draw_image(td->pixmap, td->gc, td->temp, 0, 0, x, y, wid, hgt); /* Hack - flush the changes */ gdk_flush(); } x += wid; } /* Copy it to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, sx, y, sx, y, x - sx, td->font_hgt); /* Success */ return (0); } #endif /* USE_GRAPHICS */ static errr CheckEvent(bool wait) { /* Do not wait unless requested */ if (!wait && !gtk_events_pending()) return (1); /* Process some events */ gtk_main_iteration(); return (0); } static errr Term_flush_gtk(void) { /* Flush the pending events */ while (gtk_events_pending()) gtk_main_iteration(); /* Done */ return (0); } /* * Handle a "special request" */ static errr Term_xtra_gtk(int n, int v) { /* Handle a subset of the legal requests */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: { /* Beep */ gdk_beep(); /* Done */ return (0); } /* Flush the output */ case TERM_XTRA_FRESH: { /* Flush pending X requests - almost always no-op */ gdk_flush(); /* Done */ return (0); } /* Process random events */ case TERM_XTRA_BORED: return (CheckEvent(FALSE)); /* Process Events */ case TERM_XTRA_EVENT: return (CheckEvent(v)); /* Flush the events */ case TERM_XTRA_FLUSH: return (Term_flush_gtk()); /* Handle change in the "level" */ case TERM_XTRA_LEVEL: return (0); /* Delay for some milliseconds */ case TERM_XTRA_DELAY: { /* Delay */ if (v > 0) usleep(1000 * v); return (0); } /* React to changes */ case TERM_XTRA_REACT: return (Term_xtra_gtk_react()); } /* Unknown */ return (1); } /* * Make sure the pixmap is correctly allocated * for the size of the window */ static void init_pixmap(term_data *td) { /* Paranoia */ g_assert(td->drawing_area->window); if (td->pixmap) { /* Delete the old pixmap */ gdk_pixmap_unref(td->pixmap); } /* Create a pixmap as buffer for screenupdates */ td->pixmap = gdk_pixmap_new(td->drawing_area->window, td->cols * td->font_wid, td->rows * td->font_hgt, -1); gtk_object_set_data(GTK_OBJECT(td->drawing_area), "pixmap", td->pixmap); /* Clear the pixmap */ gdk_draw_rectangle(td->pixmap, td->drawing_area->style->black_gc, TRUE, 0, 0, td->cols * td->font_wid, td->rows * td->font_hgt); } /* * Display message in a modal dialog */ static void gtk_message(cptr msg) { GtkWidget *dialog, *label, *ok_button; /* Create the widgets */ dialog = gtk_dialog_new(); g_assert(dialog); label = gtk_label_new(msg); g_assert(label); ok_button = gtk_button_new_with_label("OK"); g_assert(ok_button); /* Ensure that the dialogue box is destroyed when OK is clicked */ gtk_signal_connect_object(GTK_OBJECT(ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)dialog); /* Add the button */ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), ok_button); /* Add the label, and show the dialog */ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); /* And make it modal */ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); /* Show the dialog */ gtk_widget_show_all(dialog); } static void set_size_hints(term_data *td) { GdkGeometry win_geom; /* Main window? */ if (td == &data[0]) { /* Initialize the geometry information */ win_geom.width_inc = td->font_wid; win_geom.height_inc = td->font_hgt; win_geom.min_width = 80 * td->font_wid; win_geom.min_height = 24 * td->font_hgt; win_geom.max_width = 255 * td->font_wid; win_geom.max_height = 255 * td->font_hgt; gtk_window_set_geometry_hints(GTK_WINDOW(td->window), td->drawing_area, &win_geom, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_RESIZE_INC); } else { /* Initialize the geometry information */ win_geom.width_inc = td->font_wid; win_geom.height_inc = td->font_hgt; win_geom.min_width = 1 * td->font_wid; win_geom.min_height = 1 * td->font_hgt; win_geom.max_width = 255 * td->font_wid; win_geom.max_height = 255 * td->font_hgt; gtk_window_set_geometry_hints(GTK_WINDOW(td->window), td->drawing_area, &win_geom, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_RESIZE_INC); } } static void load_font(term_data *td, cptr fontname) { GdkFont *old = td->font; /* Load font */ td->font = gdk_font_load(fontname); td->fontname = fontname; if (td->font) { /* Free the old font */ if (old) gdk_font_unref(old); } else { /* Oops, but we can still use the old one */ td->font = old; } /* Calculate the size of the font XXX */ td->font_wid = gdk_char_width(td->font, '@'); td->font_hgt = td->font->ascent + td->font->descent; /* Bigtile size */ td->font_twid = 2 * td->font_wid; } static void font_ok_callback(GtkWidget *widget, GtkWidget *font_selector) { gchar *fontname; term_data *old_td = (term_data*)(Term->data); term_data *td = gtk_object_get_data(GTK_OBJECT(font_selector), "term_data"); /* Hack - ignore widget */ (void) widget; g_assert(td); /* Hack -- activate current term */ Term_activate(&td->t); /* Retrieve font name from player's selection */ fontname = gtk_font_selection_dialog_get_font_name( GTK_FONT_SELECTION_DIALOG(font_selector)); /* The user hasn't selected a font? */ if (fontname == NULL) return; /* Load font and update font size info */ load_font(td, fontname); /* Hack - Hide the window - finally found the trick... */ gtk_widget_hide_all(td->window); #ifdef USE_GRAPHICS if (use_graphics) { /* Need to get a new tile image */ if (td->tiles) gdk_image_destroy(td->tiles); if (td->b_tiles) gdk_image_destroy(td->b_tiles); /* Resize tiles */ td->tiles = resize_tiles(td->font_wid, td->font_hgt); td->b_tiles = resize_tiles(td->font_twid, td->font_hgt); /* Get a new temp */ if (td->temp) gdk_image_destroy(td->temp); /* Initialize the transparency temp storage*/ td->temp = gdk_image_new(GDK_IMAGE_FASTEST, gdk_visual_get_system(), td->font_twid, td->font_hgt); } #endif /* USE_GRAPHICS */ init_pixmap(td); /* Recalculate size hints */ set_size_hints(td); /* Show the widgets */ gtk_widget_show_all(td->window); /* Resize the drawing area */ gtk_drawing_area_size(GTK_DRAWING_AREA(td->drawing_area), td->cols * td->font_wid, td->rows * td->font_hgt); gtk_window_set_default_size(GTK_WINDOW(td->window), td->cols * td->font_wid, td->rows * td->font_hgt); /* Redraw the term */ Term_redraw(); /* Copy it to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, 0, 0, 0, 0, td->cols * td->font_wid, td->rows * td->font_hgt); /* Hack -- Activate the old term */ Term_activate(&old_td->t); } static void change_font_event_handler(GtkWidget *widget, gpointer user_data) { GtkWidget *font_selector = gtk_font_selection_dialog_new("Select font"); term_data *td = user_data; gchar *foundery[] = { (char * ) "misc", NULL}; gchar *spacings[] = { (char * ) "c", (char *) "m", NULL }; gchar *charsets[] = { (char * ) "iso8859-1", NULL}; /* Hack - ignore widget */ (void) widget; gtk_object_set_data(GTK_OBJECT(font_selector), "term_data", user_data); /* Filter to show only fixed-width fonts */ gtk_font_selection_dialog_set_filter( GTK_FONT_SELECTION_DIALOG(font_selector), GTK_FONT_FILTER_BASE, GTK_FONT_ALL, foundery, NULL, NULL, NULL, spacings, charsets); /* Show the current font in the dialog */ gtk_font_selection_dialog_set_font_name( GTK_FONT_SELECTION_DIALOG(font_selector), td->fontname); gtk_signal_connect( GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->ok_button), "clicked", font_ok_callback, (gpointer)font_selector); /* Ensure that the dialog box is destroyed when the user clicks a button. */ gtk_signal_connect_object( GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)font_selector); gtk_signal_connect_object( GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)font_selector); gtk_widget_show(GTK_WIDGET(font_selector)); } /* * Process Terms-* menu command - hide/show terminal window */ static void term_event_handler(GtkWidget *widget, gpointer user_data) { term_data *td = (term_data *)user_data; /* Ignore unused parameter */ (void) widget; /* We don't mess with the Angband window */ if (td == &data[0]) return; /* It's shown */ if (td->shown) { /* Hide the window */ gtk_widget_hide_all(td->window); } /* It's hidden */ else { /* Show the window */ gtk_widget_show_all(td->window); } } /* * Widget customisation (for drawing area) - "realize" signal * * In this program, called when window containing the drawing * area is shown first time. */ static void realize_event_handler(GtkWidget *widget, gpointer user_data) { term_data *td = (term_data *)user_data; /* Paranoia */ g_assert(td->drawing_area->window); /* Create graphic context */ td->gc = gdk_gc_new(td->drawing_area->window); /* Set foreground and background colours - isn't bg used at all? */ gdk_gc_set_background(td->gc, &colours[TERM_DARK].pixel); gdk_gc_set_foreground(td->gc, &colours[TERM_WHITE].pixel); /* resize the pixmap */ init_pixmap(td); /* Clear the window */ gdk_draw_rectangle(td->drawing_area->window, widget->style->black_gc, TRUE, 0, 0, td->cols * td->font_wid, td->rows * td->font_hgt); } /* * Widget customisation (for drawing area) - "show" signal */ static void show_event_handler(GtkWidget *widget, gpointer user_data) { term_data *td = (term_data *)user_data; /* Hack - ignore widget */ (void) widget; /* Set the shown flag */ td->shown = TRUE; } /* * Widget customisation (for drawing area) - "hide" signal */ static void hide_event_handler(GtkWidget *widget, gpointer user_data) { term_data *td = (term_data *)user_data; /* Hack - ignore widget */ (void) widget; /* Set the shown flag */ td->shown = FALSE; } static void file_ok_callback(GtkWidget *widget, GtkWidget *file_selector) { /* Hack - ignore widget */ (void) widget; /* Get the savefile name */ strcpy(savefile, gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector))); gtk_widget_destroy(file_selector); /* Continue into angband */ game_in_progress = TRUE; } static void open_event_handler(GtkButton *was_clicked, gpointer user_data) { GtkWidget *file_selector; char buf[1024]; /* Hack - ignore parameters */ (void) was_clicked; (void) user_data; if (!game_in_progress) { /* Prepare the savefile path */ path_make(buf, ANGBAND_DIR_SAVE, "*"); file_selector = gtk_file_selection_new("Select a savefile"); gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_selector), buf); gtk_signal_connect( GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", file_ok_callback, (gpointer)file_selector); /* * Ensure that the dialog box is destroyed * when the user clicks a button. */ gtk_signal_connect_object( GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)file_selector); gtk_signal_connect_object( GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)file_selector); gtk_window_set_modal(GTK_WINDOW(file_selector), TRUE); gtk_widget_show(GTK_WIDGET(file_selector)); } } #ifdef USE_GRAPHICS /* * Free all tiles and graphics buffers associated with windows */ static void graf_nuke(void) { int i; term_data *td; /* Nuke all terms */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access term_data structure */ td = &data[i]; /* Free previously allocated tiles */ if (td->tiles) gdk_image_destroy(td->tiles); if (td->b_tiles) gdk_image_destroy(td->b_tiles); /* Forget pointer */ td->tiles = NULL; td->b_tiles = NULL; /* Free previously allocated transparency buffer */ if (td->temp) gdk_image_destroy(td->temp); /* Forget stale pointer */ td->temp = NULL; } } /* * Initialise the graphics */ static void graf_init(void) { int i; term_data *td; term *t; if (!tiles_norm) quit("Error - no tiles yet!"); /* Mega Hack^2 - assume the top left corner is "black" */ black_pixel = gdk_image_get_pixel(tiles_norm, 0, ysize * 6); /* Initialize the windows */ for (i = 0; i < MAX_TERM_DATA; i++) { td = &data[i]; t = &td->t; if (use_graphics) { t->pict_hook = Term_pict_gtk; t->higher_pict = TRUE; /* Resize tiles */ td->tiles = resize_tiles(td->font_wid, td->font_hgt); td->b_tiles = resize_tiles(td->font_twid, td->font_hgt); /* Initialize the transparency temp storage*/ td->temp = gdk_image_new(GDK_IMAGE_FASTEST, gdk_visual_get_system(), td->font_twid, td->font_hgt); } else { t->pict_hook = NULL; t->higher_pict = FALSE; } } } static bool set_graph_mode(int graphmode) { char filename[1024]; GdkImage *tiles_back = tiles_norm; int old_mode = use_graphics; /* See if can change tiles */ if (!pick_graphics(graphmode, &xsize, &ysize, filename)) { /* Revert to the old settings */ pick_graphics(old_mode, &xsize, &ysize, filename); /* Failed */ return (FALSE); } /* Erase the old graphics */ graf_nuke(); /* Load graphics */ if (use_graphics) { /* Read the bitmap */ ReadBMP(filename); /* Paranoia */ if (!tiles_norm) { plog("Could not load tiles properly."); /* No tiles */ use_graphics = GRAPHICS_NONE; } } /* Init the new graphics */ graf_init(); /* Destroy old tiles */ if (tiles_back) { gdk_image_destroy(tiles_back); } /* Success! */ return (TRUE); } /* * Set graf_mode_request according to user selection, * and let Term_xtra react to the change. */ static void change_graf_mode_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Hack - ignore parameter */ (void) was_clicked; /* Set request according to user selection */ if ((int)user_data != use_graphics) { /* Try to set mode */ if (set_graph_mode((int)user_data)) { /* Reset visuals */ #ifdef ANGBAND_2_8_1 reset_visuals(); #else /* ANGBAND_2_8_1 */ reset_visuals(TRUE); #endif /* ANGBAND_2_8_1 */ /* Hack - force redraw */ Term_key_push(KTRL('R')); } } } /* * Toggles the boolean value of use_transparency */ static void change_trans_mode_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Hack - Ignore unused parameters */ (void) was_clicked; (void) user_data; /* Toggle the transparency mode */ use_transparency = !use_transparency; /* Hack - force redraw */ Term_key_push(KTRL('R')); } /* * Toggles the boolean value of use_bigtile */ static void change_bigtile_mode_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Hack - Ignore unused parameters */ (void) was_clicked; (void) user_data; /* Toggle the bigtile mode */ toggle_bigtile(); } #endif /* USE_GRAPHICS */ static gboolean keypress_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { int i, mc, ms, mo, mx; char msg[128]; /* Hack - do not do anything until the player picks from the menu */ if (!game_in_progress) return (TRUE); /* Hack - Ignore parameters */ (void) widget; (void) user_data; /* Extract four "modifier flags" */ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE; ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE; mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE; mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE; /* * Hack XXX * Parse shifted numeric (keypad) keys specially. */ if ((event->state == GDK_SHIFT_MASK) && (event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9)) { /* Build the macro trigger string */ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13); /* Enqueue the "macro trigger" string */ for (i = 0; msg[i]; i++) Term_keypress(msg[i]); /* Hack -- auto-define macros as needed */ if (event->length && (macro_find_exact(msg) < 0)) { /* Create a macro */ macro_add(msg, event->string); } return (TRUE); } /* Normal keys with no modifiers */ if (event->length && !mo && !mx) { /* Enqueue the normal key(s) */ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]); /* All done */ return (TRUE); } /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */ switch ((uint) event->keyval) { case GDK_Escape: { Term_keypress(ESCAPE); return (TRUE); } case GDK_Return: { Term_keypress('\r'); return (TRUE); } case GDK_Tab: { Term_keypress('\t'); return (TRUE); } case GDK_Delete: case GDK_BackSpace: { Term_keypress('\010'); return (TRUE); } /* Hack - the cursor keys */ case GDK_Up: { Term_keypress('8'); return (TRUE); } case GDK_Down: { Term_keypress('2'); return (TRUE); } case GDK_Left: { Term_keypress('4'); return (TRUE); } case GDK_Right: { Term_keypress('6'); return (TRUE); } case GDK_Shift_L: case GDK_Shift_R: case GDK_Control_L: case GDK_Control_R: case GDK_Caps_Lock: case GDK_Shift_Lock: case GDK_Meta_L: case GDK_Meta_R: case GDK_Alt_L: case GDK_Alt_R: case GDK_Super_L: case GDK_Super_R: case GDK_Hyper_L: case GDK_Hyper_R: { /* Hack - do nothing to control characters */ return (TRUE); } } /* Build the macro trigger string */ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", event->keyval, 13); /* Enqueue the "macro trigger" string */ for (i = 0; msg[i]; i++) Term_keypress(msg[i]); /* Hack -- auto-define macros as needed */ if (event->length && (macro_find_exact(msg) < 0)) { /* Create a macro */ macro_add(msg, event->string); } return (TRUE); } /* * Widget customisation (for drawing area)- handle size allocation requests */ static void size_allocate_event_handler(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { term_data *td = user_data; int old_rows, old_cols; term_data *old_data = (term_data*)(Term->data); /* Paranoia */ g_return_if_fail(widget != NULL); g_return_if_fail(allocation != NULL); g_return_if_fail(td != NULL); /* Remember old values */ old_cols = td->cols; old_rows = td->rows; /* Update numbers of rows and columns */ td->cols = (allocation->width + td->font_wid - 1) / td->font_wid; td->rows = (allocation->height + td->font_hgt - 1) / td->font_hgt; /* Adjust size request and set it */ allocation->width = td->cols * td->font_wid; allocation->height = td->rows * td->font_hgt; widget->allocation = *allocation; /* Check height and width - and then resize */ if ((old_cols != td->cols) || (old_rows != td->rows)) { /* Resize pixmap */ init_pixmap(td); /* Hack -- activate the Term */ Term_activate(&td->t); /* Resize the Term (if needed) */ (void)Term_resize(td->cols, td->rows); /* Hack -- Activate the old term */ Term_activate(&old_data->t); } /* Widget is realized, so we do some drawing works */ if (GTK_WIDGET_REALIZED(widget)) { /* Copy the data to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, 0, 0, 0, 0, td->cols * td->font_wid, td->rows * td->font_hgt); /* Actually handles resizing in Gtk */ gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height); /* And in the term package */ Term_activate(&td->t); /* Resize if necessary */ if ((td->cols != old_cols) || (td->rows != old_rows)) (void)Term_resize(td->cols, td->rows); /* Hack -- Activate the old term */ Term_activate(&old_data->t); } } static gboolean expose_event_handler(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { term_data *td = user_data; term_data *old_data = (term_data*)(Term->data); gint height, width; GdkPixmap *pixmap = gtk_object_get_data(GTK_OBJECT(widget), "pixmap"); gdk_window_get_size(widget->window, &width, &height); /* Determine "proper" number of rows/cols */ width = (width + 1) / td->font_wid; height = (height + 1) / td->font_hgt; /* Check height and width - and then resize */ if ((width != td->cols) || (height != td->rows)) { /* Save correct size */ td->cols = width; td->rows = height; /* Resize pixmap */ init_pixmap(td); /* Copy the data to the window */ gdk_draw_pixmap(td->drawing_area->window, td->gc, td->pixmap, 0, 0, 0, 0, td->cols * td->font_wid, td->rows * td->font_hgt); /* Hack -- activate the Term */ Term_activate(&td->t); /* Resize the Term (if needed) */ (void)Term_resize(td->cols, td->rows); /* Hack -- Activate the old term */ Term_activate(&old_data->t); } /* Just redraw the exposed part */ else if (pixmap) { g_assert(widget->window); gdk_draw_pixmap(widget->window, td->gc, pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); } return (TRUE); } /* * Find widget corresponding to path name * return NULL on error */ static GtkWidget *get_widget_from_path(cptr path) { GtkItemFactory *item_factory; GtkWidget *widget; /* Paranoia */ g_assert(path); /* Look up item factory */ item_factory = gtk_item_factory_from_path(path); /* Paranoia */ g_assert(item_factory); /* Look up widget */ widget = gtk_item_factory_get_widget(item_factory, path); if (!widget) plog(path); /* Return result */ return (widget); } /* * Enable/disable a menu item */ static void enable_menu_item(cptr path, bool enabled) { GtkWidget *widget; /* Access menu item widget */ widget = get_widget_from_path(path); /* Paranoia */ g_assert(widget); g_assert(GTK_IS_MENU_ITEM(widget)); /* * In Gtk's terminology, enabled is sensitive * and disabled insensitive */ gtk_widget_set_sensitive(widget, enabled); } /* * Check/uncheck a menu item. The item should be of the GtkCheckMenuItem type */ static void check_menu_item(cptr path, bool checked) { GtkWidget *widget; /* Access menu item widget */ widget = get_widget_from_path(path); /* Paranoia */ g_assert(widget); g_assert(GTK_IS_CHECK_MENU_ITEM(widget)); /* Put/remove check mark * * Mega-Hack -- The function supposed to be used here, * gtk_check_menu_item_set_active(), emits an "activate" signal * to the GtkMenuItem class of the widget, as if the menu item * were selected by user, thereby causing bizarre behaviour. * XXX XXX XXX */ GTK_CHECK_MENU_ITEM(widget)->active = checked; } /* * Update the "File" menu */ static void file_menu_update_handler(GtkWidget *widget, gpointer user_data) { bool save_ok = FALSE; bool quit_ok = FALSE; bool start_ok = !gtk_newgame; /* Ignore parameter */ (void) widget; (void) user_data; /* Cave we save/quit now? */ if (!character_generated || !game_in_progress) { quit_ok = TRUE; } else { if (p_ptr->cmd.inkey_flag && game_in_progress && character_generated) { save_ok = TRUE; quit_ok = TRUE; } } /* Enable / disable menu items according to those conditions */ enable_menu_item("/File/New", start_ok); enable_menu_item("/File/Open", start_ok); enable_menu_item("/File/Save", save_ok); enable_menu_item("/File/Quit", quit_ok); } /* * Update the "Terms" menu */ static void term_menu_update_handler(GtkWidget *widget, gpointer user_data) { int i; char buf[64]; /* Ignore parameters */ (void) widget; (void) user_data; /* For each term */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Build the path name */ strnfmt(buf, 64, "/Terms/%s", angband_term_name[i]); /* Update the check mark on the item */ check_menu_item(buf, data[i].shown); } } /* * Update the "Font" submenu */ static void font_menu_update_handler(GtkWidget *widget, gpointer user_data) { int i; char buf[64]; /* Ignore parameters */ (void) widget; (void) user_data; /* For each term */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Build the path name */ strnfmt(buf, 64, "/Options/Font/%s", angband_term_name[i]); /* Enable selection if the term is shown */ enable_menu_item(buf, data[i].shown); } } #ifdef USE_GRAPHICS /* * Update the "Graphics" submenu */ static void graf_menu_update_handler(GtkWidget *widget, gpointer user_data) { /* Ignore unused parameters */ (void) widget; (void) user_data; /* Update menu items */ check_menu_item( "/Options/Graphics/None", (use_graphics == GRAPHICS_NONE)); check_menu_item( "/Options/Graphics/8x8", (use_graphics == GRAPHICS_ORIGINAL)); check_menu_item( "/Options/Graphics/16x16", (use_graphics == GRAPHICS_ADAM_BOLT)); check_menu_item( "/Options/Graphics/32x32", (use_graphics == GRAPHICS_DAVID_GERVAIS)); check_menu_item( "/Options/Graphics/Transparency", use_transparency); check_menu_item( "/Options/Graphics/BigTile", use_bigtile); } #endif /* USE_GRAPHICS */ /* * Install callbacks to update menus */ static void add_menu_update_callbacks(void) { GtkWidget *widget; /* Access the "File" menu */ widget = get_widget_from_path("/File"); /* Paranoia */ g_assert(widget); g_assert(GTK_IS_MENU(widget)); /* Assign callback */ gtk_signal_connect(GTK_OBJECT(widget), "show", GTK_SIGNAL_FUNC(file_menu_update_handler), NULL); /* Access the "Terms" menu */ widget = get_widget_from_path("/Terms"); /* Paranoia */ g_assert(widget); g_assert(GTK_IS_MENU(widget)); /* Assign callback */ gtk_signal_connect(GTK_OBJECT(widget), "show", GTK_SIGNAL_FUNC(term_menu_update_handler), NULL); /* Access the "Font" menu */ widget = get_widget_from_path("/Options/Font"); /* Paranoia */ g_assert(widget); g_assert(GTK_IS_MENU(widget)); /* Assign callback */ gtk_signal_connect(GTK_OBJECT(widget), "show", GTK_SIGNAL_FUNC(font_menu_update_handler), NULL); #ifdef USE_GRAPHICS /* Access Graphics menu */ widget = get_widget_from_path("/Options/Graphics"); /* Paranoia */ g_assert(widget); g_assert(GTK_IS_MENU(widget)); /* Assign callback */ gtk_signal_connect(GTK_OBJECT(widget), "show", GTK_SIGNAL_FUNC(graf_menu_update_handler), NULL); #endif /* USE_GRAPHICS */ } static void new_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Hack - Ignore parameters */ (void) was_clicked; (void) user_data; if (!game_in_progress) { /* Continue into angband code */ game_in_progress = TRUE; /* Start a new game */ gtk_newgame = TRUE; } else { plog("You can't start a new game while you're still playing!"); } } static void save_game_gtk(void) { if (game_in_progress && character_generated) { if (!p_ptr->cmd.inkey_flag) { plog("You may not do that right now."); return; } /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifdef ZANGBAND do_cmd_save_game(FALSE); #else /* ZANGBAND */ do_cmd_save_game(); #endif /* ZANGBAND */ } } /* * Process File-Save menu command */ static void save_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Ignore unused parameters */ (void) was_clicked; (void) user_data; /* Save current game */ save_game_gtk(); } static gboolean delete_event_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data) { /* Hack - ignore parameters */ (void) widget; (void) event; (void) user_data; save_game_gtk(); /* Hack - set exit flag */ gtk_exitgame = TRUE; /* Don't prevent closure */ return (FALSE); } /* * Process File-Quit menu command */ static void quit_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Hack - Ignore parameters */ (void) was_clicked; (void) user_data; save_game_gtk(); quit(NULL); } static void destroy_event_handler(GtkButton *was_clicked, gpointer user_data) { /* Hack - Ignore parameters */ (void) was_clicked; (void) user_data; cleanup_angband(); quit(NULL); } /* * Neater menu code with GtkItemFactory. * * Menu bar of the Angband window * * Entry format: Path, Accelerator, Callback, Callback arg, type * where type is one of: * - simple item, alias NULL * - has submenu * - as you read it * - has a check mark * - is a toggle */ static GtkItemFactoryEntry main_menu_items[] = { /* "File" menu */ { (char *) "/File", NULL, NULL, 0, (char *) "" }, { (char *) "/File/New", (char *) "N", new_event_handler, 0, NULL }, { (char *) "/File/Open", (char *) "O", open_event_handler, 0, NULL }, { (char *) "/File/sep1", NULL, NULL, 0, (char *) "" }, { (char *) "/File/Save", (char *) "S", save_event_handler, 0, NULL }, { (char *) "/File/Quit", (char *) "Q", quit_event_handler, 0, NULL }, /* "Terms" menu */ { (char * ) "/Terms", NULL, NULL, 0, (char * ) "" }, /* XXX XXX XXX NULL's are replaced by the program */ { NULL, (char * ) "0", term_event_handler, (guint)&data[0], (char * ) "" }, { NULL, (char * ) "1", term_event_handler, (guint)&data[1], (char * ) "" }, { NULL, (char * ) "2", term_event_handler, (guint)&data[2], (char * ) "" }, { NULL, (char * ) "3", term_event_handler, (guint)&data[3], (char * ) "" }, { NULL, (char * ) "4", term_event_handler, (guint)&data[4], (char * ) "" }, { NULL, (char * ) "5", term_event_handler, (guint)&data[5], (char * ) "" }, { NULL, (char * ) "6", term_event_handler, (guint)&data[6], (char * ) "" }, { NULL, (char * ) "7", term_event_handler, (guint)&data[7], (char * ) "" }, /* "Options" menu */ { (char * ) "/Options", NULL, NULL, 0, (char * ) "" }, /* "Font" submenu */ { (char * ) "/Options/Font", NULL, NULL, 0, (char * ) "" }, /* XXX XXX XXX Again, NULL's are filled by the program */ { NULL, NULL, change_font_event_handler, (guint)&data[0], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[1], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[2], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[3], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[4], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[5], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[6], NULL }, { NULL, NULL, change_font_event_handler, (guint)&data[7], NULL }, #ifdef USE_GRAPHICS /* "Graphics" submenu */ { (char * ) "/Options/Graphics", NULL, NULL, 0, (char * ) "" }, { (char * ) "/Options/Graphics/None", NULL, change_graf_mode_event_handler, GRAPHICS_NONE, (char * ) "" }, { (char * ) "/Options/Graphics/8x8", NULL, change_graf_mode_event_handler, GRAPHICS_ORIGINAL, (char * ) "" }, { (char * ) "/Options/Graphics/16x16", NULL, change_graf_mode_event_handler, GRAPHICS_ADAM_BOLT, (char * ) "" }, { (char * ) "/Options/Graphics/32x32", NULL, change_graf_mode_event_handler, GRAPHICS_DAVID_GERVAIS, (char * ) "" }, { (char * ) "/Options/Graphics/sep1", NULL, NULL, 0, (char * ) "" }, { (char * ) "/Options/Graphics/Transparency", NULL, change_trans_mode_event_handler, 0, (char * ) "" }, { (char * ) "/Options/Graphics/BigTile", NULL, change_bigtile_mode_event_handler, 0, (char * ) "" }, #endif /* USE_GRAPHICS */ }; /* * XXX XXX Fill those NULL's in the menu definition with * angband_term_name[] strings */ static void setup_menu_paths(void) { int i; int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]); GtkItemFactoryEntry *term_entry, *font_entry; char buf[64]; /* Find the "Terms" menu */ for (i = 0; i < nmenu_items; i++) { /* Skip NULLs */ if (main_menu_items[i].path == NULL) continue; /* Find a match */ if (streq(main_menu_items[i].path, "/Terms")) break; } g_assert(i < (nmenu_items - MAX_TERM_DATA)); /* Remember the location */ term_entry = &main_menu_items[i + 1]; /* Find "Font" menu */ for (i = 0; i < nmenu_items; i++) { /* Skip NULLs */ if (main_menu_items[i].path == NULL) continue; /* Find a match */ if (streq(main_menu_items[i].path, "/Options/Font")) break; } g_assert(i < (nmenu_items - MAX_TERM_DATA)); /* Remember the location */ font_entry = &main_menu_items[i + 1]; /* For each terminal */ for (i = 0; i < MAX_TERM_DATA; i++) { /* XXX XXX Build the real path name to the entry */ strnfmt(buf, 64, "/Terms/%s", angband_term_name[i]); /* XXX XXX Store it in the menu definition */ term_entry[i].path = (char *) string_make(buf); /* XXX XXX Build the real path name to the entry */ strnfmt(buf, 64, "/Options/Font/%s", angband_term_name[i]); /* XXX XXX Store it in the menu definition */ font_entry[i].path = (char *) string_make(buf); } } /* * Construct a menu hierarchy using GtkItemFactory, setting up * callbacks and accelerators along the way, and return * a GtkMenuBar widget. */ static GtkWidget *get_main_menu(term_data *td) { GtkItemFactory *item_factory; GtkAccelGroup *accel_group; gint nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]); /* XXX XXX Setup path names in the "Terms" and "Font" menus */ setup_menu_paths(); /* Allocate an accelerator group */ accel_group = gtk_accel_group_new(); g_assert(accel_group); /* Initialise the item factory */ item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", accel_group); g_assert(item_factory); /* Generate the menu items */ gtk_item_factory_create_items(item_factory, nmenu_items, main_menu_items, NULL); /* Attach the new accelerator group to the window */ gtk_window_add_accel_group(GTK_WINDOW(td->window), accel_group); /* Return the actual menu bar created */ return (gtk_item_factory_get_widget(item_factory, "")); } /* * XXX XXX Free strings allocated by setup_menu_paths() */ static void free_menu_paths(void) { int i; int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]); GtkItemFactoryEntry *term_entry, *font_entry; /* Find the "Terms" menu */ for (i = 0; i < nmenu_items; i++) { /* Skip NULLs */ if (main_menu_items[i].path == NULL) continue; /* Find a match */ if (streq(main_menu_items[i].path, "/Terms")) break; } /* Paranoia */ g_assert(i < (nmenu_items - MAX_TERM_DATA)); /* Remember the location */ term_entry = &main_menu_items[i + 1]; /* Find "Font" menu */ for (i = 0; i < nmenu_items; i++) { /* Skip NULLs */ if (main_menu_items[i].path == NULL) continue; /* Find a match */ if (streq(main_menu_items[i].path, "/Options/Font")) break; } /* Paranoia */ g_assert(i < (nmenu_items - MAX_TERM_DATA)); /* Remember the location */ font_entry = &main_menu_items[i + 1]; /* For each terminal */ for (i = 0; i < MAX_TERM_DATA; i++) { /* XXX XXX Free Term menu path */ string_free((cptr)term_entry[i].path); /* XXX XXX Free Font menu path */ string_free((cptr)font_entry[i].path); } } /* * Quit hook when exiting the game */ static void hook_quit(cptr str) { /* Hack - Ignore parameter */ (void) str; /* Free menu paths dynamically allocated */ free_menu_paths(); gtk_exit(0); } /* * Hook to tell the user something important */ static void hook_plog(cptr str) { /* Warning message */ gtk_message(str); } /* * Handle destruction of Subwindows */ static void destroy_sub_event_handler(GtkWidget *window, gpointer user_data) { term_data *td = (term_data *)user_data; /* Do nothing if window is not visible */ if (!td->shown) return; /* Hide the window */ gtk_widget_hide_all(window); } /* * Handle deletion of Subwindows */ static void delete_sub_event_handler(GtkWidget *window, gpointer user_data) { term_data *td = (term_data *)user_data; /* Do nothing if window is not visible */ if (!td->shown) return; td->shown = FALSE; /* Hide the window */ gtk_widget_hide_all(window); } static errr term_data_init(term_data *td, int i) { cptr font; term *t = &td->t; td->cols = 80; td->rows = 24; /* Initialize the term */ term_init(t, td->cols, td->rows, 1024); /* Save the name */ td->name = angband_term_name[i]; /* Use a "soft" cursor */ t->soft_cursor = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; t->xtra_hook = Term_xtra_gtk; t->text_hook = Term_text_gtk; t->wipe_hook = Term_wipe_gtk; t->curs_hook = Term_curs_gtk; #ifdef USE_GRAPHICS t->pict_hook = Term_pict_gtk; t->higher_pict = TRUE; #endif /* USE_GRAPHICS */ /* Save the data */ t->data = td; /* Activate (important) */ Term_activate(t); /* Get default font for this term */ font = get_default_font(i); load_font(td, font); /* Success */ return (0); } static void init_gtk_window(term_data *td, int i) { GtkWidget *menu_bar, *box; bool main_win = (i == 0) ? TRUE : FALSE; /* Create window */ td->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* Set title */ gtk_window_set_title(GTK_WINDOW(td->window), td->name); /* Create drawing area */ td->drawing_area = gtk_drawing_area_new(); /* Set the size of the drawing area */ gtk_drawing_area_size(GTK_DRAWING_AREA(td->drawing_area), td->cols * td->font_wid, td->rows * td->font_hgt); /* Set geometry hints */ set_size_hints(td); /* Set resize policy */ gtk_window_set_policy(GTK_WINDOW(td->window), TRUE, TRUE, TRUE); /* Install window event handlers */ gtk_signal_connect(GTK_OBJECT(td->window), "key_press_event", GTK_SIGNAL_FUNC(keypress_event_handler), NULL); /* Destroying the Angband window terminates the game */ if (main_win) { gtk_signal_connect(GTK_OBJECT(td->window), "delete_event", GTK_SIGNAL_FUNC(delete_event_handler), NULL); gtk_signal_connect(GTK_OBJECT(td->window), "destroy_event", GTK_SIGNAL_FUNC(destroy_event_handler), NULL); } /* The other windows are just hidden */ else { gtk_signal_connect(GTK_OBJECT(td->window), "delete_event", GTK_SIGNAL_FUNC(delete_sub_event_handler), td); gtk_signal_connect(GTK_OBJECT(td->window), "destroy_event", GTK_SIGNAL_FUNC(destroy_sub_event_handler), td); } /* Install drawing area event handlers */ gtk_signal_connect(GTK_OBJECT(td->drawing_area), "realize", GTK_SIGNAL_FUNC(realize_event_handler), (gpointer)td); gtk_signal_connect(GTK_OBJECT(td->drawing_area), "show", GTK_SIGNAL_FUNC(show_event_handler), (gpointer)td); gtk_signal_connect(GTK_OBJECT(td->drawing_area), "hide", GTK_SIGNAL_FUNC(hide_event_handler), (gpointer)td); gtk_signal_connect(GTK_OBJECT(td->drawing_area), "size_allocate", GTK_SIGNAL_FUNC(size_allocate_event_handler), (gpointer)td); gtk_signal_connect(GTK_OBJECT(td->drawing_area), "expose_event", GTK_SIGNAL_FUNC(expose_event_handler), (gpointer)td); /* Create menu */ if (main_win) { /* Build the main menu bar */ menu_bar = get_main_menu(td); g_assert(menu_bar); /* Since it's tedious to scatter the menu update code around */ add_menu_update_callbacks(); /* Pack the menu bar together with the main window */ /* For vertical placement of the menu bar and the drawing area */ box = gtk_vbox_new(FALSE, 0); /* Let the window widget own it */ gtk_container_add(GTK_CONTAINER(td->window), box); /* The main window has a menu bar */ gtk_box_pack_start(GTK_BOX(box), menu_bar, FALSE, FALSE, NO_PADDING); } else { /* Pack the menu bar together with the main window */ /* For vertical placement of the menu bar and the drawing area */ box = gtk_vbox_new(FALSE, 0); /* Let the window widget own it */ gtk_container_add(GTK_CONTAINER(td->window), box); } /* And place the drawing area just beneath it */ gtk_box_pack_start_defaults(GTK_BOX(box), td->drawing_area); /* Show the widgets - use of td->shown is a dirty hack XXX XXX */ if (!td->shown) return; /* Show the widgets */ gtk_widget_show_all(td->window); } /* * Initialization function */ errr init_gtk(int argc, char **argv, unsigned char *new_game) { int i; #ifdef USE_GRAPHICS int graphmode = GRAPHICS_ANY; #endif /* USE_GRAPHICS */ /* See if gtk exists and works */ if (!gtk_init_check(&argc, &argv)) return (1); /* Hack - save variable so that everyone can use it */ gtk_newgame = *new_game; /* Hack - commandline option sets new game? */ game_in_progress = gtk_newgame; /* Initialize the environment */ gtk_init(&argc, &argv); /* Prepare normal colors */ store_pixel_colors(); /* Parse args */ for (i = 1; i < argc; i++) { if (prefix(argv[i], "-n")) { num_term = atoi(&argv[i][2]); if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA; else if (num_term < 1) num_term = 1; continue; } #ifdef USE_GRAPHICS if (prefix(argv[i], "-b")) { int bitdepth = 0; bitdepth = atoi(&argv[i][2]); /* Paranoia */ if (bitdepth == 32) graphmode = GRAPHICS_DAVID_GERVAIS; if (bitdepth == 16) graphmode = GRAPHICS_ADAM_BOLT; if (bitdepth == 8) graphmode = GRAPHICS_ORIGINAL; continue; } #endif /* USE_GRAPHICS */ plog_fmt("Ignoring option: %s", argv[i]); } #ifdef USE_GRAPHICS /* We support bigtile mode */ if (arg_bigtile && arg_graphics) use_bigtile = TRUE; #endif /* USE_GRAPHICS */ /* * Initialize the terms */ for (i = 0; i < MAX_TERM_DATA; i++) { term_data *td = &data[i]; /* Initialize the term_data */ term_data_init(td, i); /* Save global entry */ angband_term[i] = Term; } #ifdef USE_GRAPHICS /* Set graphics mode */ if (arg_graphics) set_graph_mode(graphmode); #endif /* USE_GRAPHICS */ /* * Initialize the windows * (Backwards so main window is on top) */ for (i = MAX_TERM_DATA - 1; i >= 0; i--) { term_data *td = &data[i]; /* Hack - Set the shown flag */ if (i < num_term) { td->shown = TRUE; } else { td->shown = FALSE; } /* Init the window */ init_gtk_window(td, i); } #ifdef USE_GRAPHICS if (use_graphics) { /* Initialise the graphics */ graf_init(); } #endif /* USE_GRAPHICS */ /* Activate the "Angband" window screen */ Term_activate(&data[0].t); /* * Mega-Hack XXX XXX XXX * Initialize resize_hook of main term. * * For some reason, we cannot have this as the default in * z-term.c (Other ports do not work properly.) */ data[0].t.resize_hook = Term_fresh; /* Activate hooks */ plog_aux = hook_plog; quit_aux = hook_quit; core_aux = hook_quit; /* Catch nasty signals */ signals_init(); /* Need to initialize system type */ ANGBAND_SYS = "gtk"; /* Initialize */ init_angband(); /* Prompt the user */ prtf(17, 23, "[Choose 'New' or 'Open' from the 'File' menu]"); Term_fresh(); while (!game_in_progress) { while (gtk_events_pending()) { gtk_main_iteration(); } /* Wait so we don't waste all the processor */ usleep(200); /* Die if window is closed */ if (gtk_exitgame) quit(NULL); } /* Load 'newgame' flag */ *new_game = gtk_newgame; /* Press a key for the player */ Term_keypress(' '); /* Success */ return (0); } #endif /* USE_GTK */ zangband/src/main-ibm.c0000755000000000000000000007151710250356274014006 0ustar rootroot/* File: main-ibm.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* Purpose: Visual Display Support for "z-term.c", for the IBM */ /* * Original code by "Billy Tanksley (wtanksle@ucsd.edu)" * Use "Makefile.ibm" to compile Angband using this file. * * Support for DJGPP v2 by "Scott Egashira (egashira@u.washington.edu)" * * Extensive modifications by "Ben Harrison (benh@phial.com)", * including "collation" of the Watcom C/C++ and DOS-286 patches. * * Watcom C/C++ changes by "David Boeren (akemi@netcom.com)" * Use "Makefile.wat" to compile this file with Watcom C/C++, and * be sure to define "USE_IBM" and "USE_WAT". * * DOS-286 (conio.h) changes by (Roland Jay Roberts (jay@map.com) * Use "Makefile.286" (not ready) to compile this file for DOS-286, * and be sure to define "USE_IBM", "USE_WAT", and "USE_286". Also, * depending on your compiler, you may need to define "USE_CONIO". * * True color palette support by "Mike Marcelais (michmarc@microsoft.com)", * with interface to the "color_table" array by Ben Harrison. * * Both "shift" keys are treated as "identical", and all the modifier keys * (control, shift, alt) are ignored when used with "normal" keys, unless * they modify the underlying "ascii" value of the key. You must use the * new "user pref files" to be able to interact with the keypad and such. * * The "lib/user/pref-ibm.prf" file contains macro definitions and possible * alternative color set definitions. The "lib/user/font-ibm.prf" contains * attr/char mappings for walls and floors and such. * * Note the "Term_user_ibm()" function hook, which could allow the user * to interact with the "main-ibm.c" visual system. Currently this hook * is unused, but, for example, it could allow the user to toggle "sound" * or "graphics" modes, or to select the number of screen rows, with the * extra screen rows being used for the mirror window. */ #include "angband.h" #ifdef USE_IBM cptr help_ibm[] = { "To use IBM (BIOS text mode)", NULL }; /* * Use a "virtual" screen to "buffer" screen writes. */ #define USE_VIRTUAL #include #include #ifdef USE_WAT # include # ifdef USE_CONIO # else /* USE_CONIO */ # include # define bioskey(C) _bios_keybrd(C) # endif /* USE_CONIO */ # ifndef USE_286 # define int86(a,b,c) int386(a,b,c) # endif # define inportb(x) inp(x) # define outportb(x,y) outp(x,y) #else /* USE_WAT */ # if __DJGPP__ > 1 # include # include # else /* __DJGPP__ > 1 */ # ifdef __DJGPP__ # error "Upgrade to version 2.0 of DJGPP" # endif /* __DJGPP__ */ # endif /* __DJGPP__ > 1 */ #endif /* USE_WAT */ #ifdef USE_CONIO # include /* * Hack -- write directly to video card */ extern int directvideo = 1; /* * Hack -- no virtual screen */ # undef USE_VIRTUAL #endif /* USE_CONIO */ /* * Keypress input modifier flags (hard-coded by DOS) */ #define K_RSHIFT 0 /* Right shift key down */ #define K_LSHIFT 1 /* Left shift key down */ #define K_CTRL 2 /* Ctrl key down */ #define K_ALT 3 /* Alt key down */ #define K_SCROLL 4 /* Scroll lock on */ #define K_NUM 5 /* Num lock on */ #define K_CAPS 6 /* Caps lock on */ #define K_INSERT 7 /* Insert on */ /* * Foreground color bits (hard-coded by DOS) */ #define VID_BLACK 0x00 #define VID_BLUE 0x01 #define VID_GREEN 0x02 #define VID_CYAN 0x03 #define VID_RED 0x04 #define VID_MAGENTA 0x05 #define VID_YELLOW 0x06 #define VID_WHITE 0x07 /* * Bright text (hard-coded by DOS) */ #define VID_BRIGHT 0x08 /* * Background color bits (hard-coded by DOS) */ #define VUD_BLACK 0x00 #define VUD_BLUE 0x10 #define VUD_GREEN 0x20 #define VUD_CYAN 0x30 #define VUD_RED 0x40 #define VUD_MAGENTA 0x50 #define VUD_YELLOW 0x60 #define VUD_WHITE 0x70 /* * Blinking text (hard-coded by DOS) */ #define VUD_BRIGHT 0x80 /* * Screen Size */ static int rows = 25; static int cols = 80; /* * Physical Screen */ #ifdef USE_286 # define PhysicalScreen ((byte *)MK_FP(0xB800,0x0000)) #else # define PhysicalScreen ((byte *)(0xB800 << 4)) #endif #ifdef USE_VIRTUAL /* * Virtual Screen Contents */ static byte *VirtualScreen; #else /* * Physical screen access */ #define VirtualScreen PhysicalScreen #endif /* * Hack -- the cursor "visibility" */ static int saved_cur_v; static int saved_cur_high; static int saved_cur_low; #ifdef USE_CONIO #else /* USE_CONIO */ /* * This array is used for "wiping" the screen */ static byte wiper[160]; #endif /* USE_CONIO */ /* * The main screen (currently the only screen) */ static term term_screen_body; /* * Choose between the "complex" and "simple" color methods */ static byte use_color_complex = FALSE; /* * The "complex" color set */ static long ibm_color_complex[16]; /* * The "simple" color set * * This table is used by the "color" code to instantiate the "approximate" * Angband colors using the only colors available on crappy monitors. * * The entries below are taken from the "color bits" defined above. * * Note that values from 16 to 255 are extremely ugly. * * The values below came from various sources, if you do not like them, * get a better monitor, or edit "pref-ibm.prf" to use different codes. * * Note that many of the choices below suck, but so do crappy monitors. */ static byte ibm_color_simple[16] = { VID_BLACK, /* Dark */ VID_WHITE, /* White */ VID_CYAN, /* Slate XXX */ VID_RED | VID_BRIGHT, /* Orange XXX */ VID_RED, /* Red */ VID_GREEN, /* Green */ VID_BLUE, /* Blue */ VID_YELLOW, /* Umber XXX */ VID_BLACK | VID_BRIGHT, /* Light Dark */ VID_CYAN | VID_BRIGHT, /* Light Slate XXX */ VID_MAGENTA, /* Violet */ VID_YELLOW | VID_BRIGHT, /* Yellow */ VID_MAGENTA | VID_BRIGHT, /* Light Red XXX */ VID_GREEN | VID_BRIGHT, /* Light Green */ VID_BLUE | VID_BRIGHT, /* Light Blue */ VID_YELLOW /* Light Umber XXX */ }; /* * Activate the "ibm_color_complex" palette information. * * Code by Mike Marcelais, with help from "The programmer's guide * to the EGA and VGA video cards" [Farraro]. * * On VGA cards, colors go through a double-indirection when looking * up the `real' color when in 16 color mode. The color value in the * attribute is looked up in the EGA color registers. Then that value * is looked up in the VGA color registers. Then the color is displayed. * This is done for compatability. However, the EGA registers are * initialized by default to 0..5, 14, 7, 38..3F and not 0..F which means * that unless these are reset, the VGA setpalette function will not * update the correct palette register! * * DJGPP's GrSetColor() does _not_ set the EGA palette list, only the * VGA color list. * * Note that the "traditional" method, using "int86(0x10)", is very slow * when called in protected mode, so we use a faster method using video * ports instead. * * On Watcom machines, we could simply use the special "_remapallpalette()" * function, which not only sets both palette lists (see below) but also * checks for legality of the monitor mode, but, if we are doing bitmapped * graphics, that function forgets to set the EGA registers for some reason. */ static void activate_color_complex(void) { int i; printf("%c%c%c%c",8,8,8,8); #if 1 /* Edit the EGA palette */ inportb(0x3da); /* Edit the colors */ for (i = 0; i < 16; i++) { /* Set color "i" */ outportb(0x3c0, i); /* To value "i" */ outportb(0x3c0, i); }; /* Use that EGA palette */ outportb(0x3c0, 0x20); /* Edit VGA palette, starting at color zero */ outportb(0x3c8, 0); /* Send the colors */ for (i = 0; i < 16; i++) { /* Send the red, green, blue components */ outportb(0x3c9, ((ibm_color_complex[i]) & 0xFF)); outportb(0x3c9, ((ibm_color_complex[i] >> 8) & 0xFF)); outportb(0x3c9, ((ibm_color_complex[i] >> 16) & 0xFF)); } #else /* 1 */ /* Set the colors */ for (i = 0; i < 16; i++) { union REGS r; /* Set EGA color */ r.h.ah = 0x10; r.h.al = 0x00; /* Set color "i" */ r.h.bl = i; /* To value "i" */ r.h.bh = i; /* Do it */ int86(0x10, &r, &r); /* Set VGA color */ r.h.ah = 0x10; r.h.al = 0x10; /* Set color "i" */ r.h.bh = 0x00; r.h.bl = i; /* Use this "green" value */ r.h.ch = ((ibm_color_complex[i] >> 8) & 0xFF); /* Use this "blue" value */ r.h.cl = ((ibm_color_complex[i] >> 16) & 0xFF); /* Use this "red" value */ r.h.dh = ((ibm_color_complex[i]) & 0xFF); /* Do it */ int86(0x10, &r, &r); } #endif /* 1 */ } /* * Note the use of "(x >> 2)" to convert an 8 bit value to a 6 bit value * without losing much precision. */ static int Term_xtra_ibm_react(void) { int i; /* Complex method */ if (use_color_complex) { long rv, gv, bv, code; bool change = FALSE; /* Save the default colors */ for (i = 0; i < 16; i++) { /* Extract desired values */ rv = angband_color_table[i][1] >> 2; gv = angband_color_table[i][2] >> 2; bv = angband_color_table[i][3] >> 2; /* Extract a full color code */ code = ((rv) | (gv << 8) | (bv << 16)); /* Activate changes */ if (ibm_color_complex[i] != code) { /* Note the change */ change = TRUE; /* Apply the desired color */ ibm_color_complex[i] = code; } } /* Activate the palette if needed */ if (change) activate_color_complex(); } /* Simple method */ else { /* Save the default colors */ for (i = 0; i < 16; i++) { /* Simply accept the desired colors */ ibm_color_simple[i] = angband_color_table[i][0]; } } /* Success */ return (0); } /* * Hack -- set the cursor "visibility" */ static void curs_set(int v) { /* If needed */ if (saved_cur_v != v) { union REGS r; /* Set cursor */ r.h.ah = 1; /* Visible */ if (v) { /* Use the saved values */ r.h.ch = saved_cur_high; r.h.cl = saved_cur_low; } /* Invisible */ else { /* Make it invisible */ r.h.ch = 0x20; r.h.cl = 0x00; } /* Make the call */ int86(0x10, &r, &r); /* Save the cursor state */ saved_cur_v = v; } } /* * Process an event (check for a keypress) * * The keypress processing code is often the most system dependant part * of Angband, since sometimes even the choice of compiler is important. * * For the IBM, we divide all keypresses into two catagories, first, the * "normal" keys, including all keys required to play Angband, and second, * the "special" keys, such as keypad keys, function keys, and various keys * used in combination with various modifier keys. * * To simplify this file, we use Angband's "macro processing" ability, in * combination with a specialized "pref-ibm.prf" file, to handle most of the * "special" keys, instead of attempting to fully analyze them here. This * file only has to determine when a "special" key has been pressed, and * translate it into a simple string which signals the use of a "special" * key, the set of modifiers used, if any, and the hardware scan code of * the actual key which was pressed. To simplify life for the user, we * treat both "shift" keys as identical modifiers. * * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers * ("C" for control, "S" for shift, "A" for alt, or any ordered combination), * and "SS" encodes the keypress (as the two "digit" hexidecimal encoding of * the scan code of the key that was pressed), and the "^_" and "x" and "\r" * delimit the encoding for recognition by the macro processing code. * * Some important facts about scan codes follow. All "normal" keys use * scan codes from 1-58. The "function" keys use 59-68 (and 133-134). * The "keypad" keys use 69-83. Escape uses 1. Enter uses 28. Control * uses 29. Left Shift uses 42. Right Shift uses 54. PrtScrn uses 55. * Alt uses 56. Space uses 57. CapsLock uses 58. NumLock uses 69. * ScrollLock uses 70. The "keypad" keys which use scan codes 71-83 * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL. * * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more * information, including better access to the keypad keys in combination * with various modifiers, but only works on "PC's after 6/1/86", and there * is no way to determine if the function is provided on a machine. I have * been told that without it you cannot detect, for example, control-left. * The basic scan code + ascii value pairs returned by the keypad follow, * with values in parentheses only available to "bioskey(0x10)". * * / * - + 1 2 3 4 * Norm: 352f 372a 4a2d 4e2b 4f00 5000 5100 4b00 * Shft: 352f 372a 4a2d 4e2b 4f31 5032 5133 4b34 * Ctrl: (9500) (9600) (8e00) (9000) 7500 (9100) 7600 7300 * * 5 6 7 8 9 0 . Enter * Norm: (4c00) 4d00 4700 4800 4900 5200 5300 (e00d) * Shft: 4c35 4d36 4737 4838 4939 5230 532e (e00d) * Ctrl: (8f00) 7400 7700 (8d00) 8400 (9200) (9300) (e00a) * * See "pref-ibm.prf" for the "standard" macros for various keys. * * Certain "bizarre" keypad keys (such as "enter") return a "scan code" * of "0xE0", and a "usable" ascii value. These keys should be treated * like the normal keys, see below. XXX XXX XXX Note that these "special" * keys could be prefixed with an optional "ctrl-^" which would allow them * to be used in macros without hurting their use in normal situations. */ static errr Term_xtra_ibm_event(int v) { int i, k, s; bool mc = FALSE; bool ms = FALSE; bool ma = FALSE; /* Hack -- Check for a keypress */ if (!v && !bioskey(1)) return (1); /* Wait for a keypress */ k = bioskey(0x10); /* Access the "modifiers" */ i = bioskey(2); /* Extract the "scan code" */ s = ((k >> 8) & 0xFF); /* Extract the "ascii value" */ k = (k & 0xFF); /* Process "normal" keys */ if ((s <= 58) || (s == 0xE0)) { /* Enqueue it */ if (k) Term_keypress(k); /* Success */ return (0); } /* Extract the modifier flags */ if (i & (1 << K_CTRL)) mc = TRUE; if (i & (1 << K_LSHIFT)) ms = TRUE; if (i & (1 << K_RSHIFT)) ms = TRUE; if (i & (1 << K_ALT)) ma = TRUE; /* Begin a "macro trigger" */ Term_keypress(31); /* Hack -- Send the modifiers */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (ma) Term_keypress('A'); /* Introduce the hexidecimal scan code */ Term_keypress('x'); /* Encode the hexidecimal scan code */ Term_keypress(hexsym[s/16]); Term_keypress(hexsym[s%16]); /* End the "macro trigger" */ Term_keypress(13); /* Success */ return (0); } /* * Handle a "special request" * * The given parameters are "valid". */ static errr Term_xtra_ibm(int n, int v) { int i; /* Analyze the request */ switch (n) { /* Make a "bell" noise */ case TERM_XTRA_NOISE: { /* Make a bell noise */ (void)write(1, "\007", 1); /* Success */ return (0); } /* Set the cursor shape */ case TERM_XTRA_SHAPE: { /* Set cursor shape */ curs_set(v); /* Success */ return (0); } /* Flush one line of output */ case TERM_XTRA_FROSH: { #ifdef USE_VIRTUAL # ifdef USE_WAT /* Copy the virtual screen to the physical screen */ memcpy(PhysicalScreen + (v*160), VirtualScreen + (v*160), 160); # else /* USE_WAT */ /* Apply the virtual screen to the physical screen */ ScreenUpdateLine(VirtualScreen + ((v*cols) << 1), v); # endif /* USE_WAT */ #endif /* USE_VIRTUAL */ /* Success */ return (0); } /* Process events */ case TERM_XTRA_EVENT: { /* Process one event */ return (Term_xtra_ibm_event(v)); } /* Flush events */ case TERM_XTRA_FLUSH: { /* Strip events */ while (!Term_xtra_ibm_event(FALSE)) /* loop */; /* Success */ return (0); } /* React to global changes */ case TERM_XTRA_REACT: { /* React to "color_table" changes */ return (Term_xtra_ibm_react()); } /* Delay for some milliseconds */ case TERM_XTRA_DELAY: { /* Delay if needed */ if (v > 0) delay(v); /* Success */ return (0); } } /* Unknown request */ return (1); } /* * Move the cursor * * The given parameters are "valid". */ static errr Term_curs_ibm(int x, int y) { #ifdef USE_WAT # ifdef USE_CONIO /* Place the cursor */ gotoxy(x+1, y+1); # else /* USE_CONIO */ union REGS r; r.h.ah = 2; r.h.bh = 0; r.h.dl = x; r.h.dh = y; /* Place the cursor */ int86(0x10, &r, &r); # endif /* USE_CONIO */ #else /* USE_WAT */ /* Move the cursor */ ScreenSetCursor(y, x); #endif /* USE_WAT */ /* Success */ return (0); } /* * Erase a block of the screen * * The given parameters are "valid". */ static errr Term_wipe_ibm(int x, int y, int n) { #ifdef USE_CONIO /* Wipe the region */ window(x+1, y+1, x+n, y+1); clrscr(); window(1, 1, cols, rows); #else /* USE_CONIO */ /* Wipe part of the virtual (or physical) screen */ memcpy(VirtualScreen + ((cols*y + x)<<1), wiper, n<<1); #endif /* USE_CONIO */ /* Success */ return (0); } /* * Place some text on the screen using an attribute * * The given parameters are "valid". Be careful with "a". * * The string "cp" has length "n" and is NOT null-terminated. */ static errr Term_text_ibm(int x, int y, int n, byte a, const char *cp) { register int i; register byte attr; register byte *dest; /* Handle "complex" color */ if (use_color_complex) { /* Extract a color index */ attr = (a & 0x0F); } /* Handle "simple" color */ else { /* Extract a color value */ attr = ibm_color_simple[a & 0x0F]; } #ifdef USE_CONIO /* Place the cursor */ gotoxy(x+1, y+1); /* Set the attribute */ textattr(attr); /* Dump the text */ for (i = 0; i < n; i++) putch(cp[i]); #else /* USE_CONIO */ /* Access the virtual (or physical) screen */ dest = VirtualScreen + (((cols * y) + x) << 1); /* Save the data */ for (i = 0; i < n; i++) { /* Apply */ *dest++ = cp[i]; *dest++ = attr; } #endif /* USE_CONIO */ /* Success */ return (0); } /* * Place some attr/char pairs on the screen * * The given parameters are "valid". */ static errr Term_pict_ibm(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { register int i; register byte attr; register byte *dest; /* Unused parameter */ (void)tap; (void)tcp; #ifdef USE_CONIO /* Place the cursor */ gotoxy(x+1, y+1); /* Dump the text */ for (i = 0; i < n; i++) { /* Handle "complex" color */ if (use_color_complex) { /* Extract a color index */ attr = (ap[i] & 0x0F); } /* Handle "simple" color */ else { /* Extract a color value */ attr = ibm_color_simple[ap[i] & 0x0F]; } /* Set the attribute */ textattr(attr); /* Dump the char */ putch(cp[i]); } #else /* USE_CONIO */ /* Access the virtual (or physical) screen */ dest = VirtualScreen + (((cols * y) + x) << 1); /* Save the data */ for (i = 0; i < n; i++) { /* Handle "complex" color */ if (use_color_complex) { /* Extract a color index */ attr = (ap[i] & 0x0F); } /* Handle "simple" color */ else { /* Extract a color value */ attr = ibm_color_simple[ap[i] & 0x0F]; } /* Apply */ *dest++ = cp[i]; *dest++ = attr; } #endif /* USE_CONIO */ /* Success */ return (0); } /* * Init a Term */ static void Term_init_ibm(term *t) { /* Unused parameter */ (void)t; /* XXX Nothing */ } /* * Nuke a Term */ static void Term_nuke_ibm(term *t) { #ifdef USE_WAT /* Nothing */ #else /* USE_WAT */ union REGS r; #endif /* USE_WAT */ /* Unused parameter */ (void)t; /* Move the cursor to the bottom of the screen */ Term_curs_ibm(0, rows-1); #ifdef USE_WAT /* Restore the original video mode */ _setvideomode(_DEFAULTMODE); #else /* USE_WAT */ /* Restore the original video mode */ r.h.ah = 0x00; r.h.al = 0x03; int86(0x10, &r, &r); #endif /* USE_WAT */ /* Make the cursor visible */ curs_set(1); } #ifdef USE_GRAPHICS #ifdef USE_286 /* * In 286 mode we don't need to worry about translating from a 32bit * pointer to a 16 bit pointer so we just call the interrupt function * * Note the use of "intr()" instead of "int86()" so we can pass * segment registers. */ void enable_graphic_font(void *font) { union REGPACK regs = {0}; regs.h.ah = 0x11; /* Text font function */ regs.h.bh = 0x10; /* Size of a character -- 16 bytes */ regs.h.cl = 0xFF; /* Last character in font */ regs.x.es = FP_SEG(font); /* Pointer to font */ regs.x.bp = FP_OFF(font); intr(0x10, ®s); }; #else /* USE_286 */ #ifdef USE_WAT /* * This structure is used by the DMPI function to hold registers when * doing a real mode interrupt call. (Stolen from the DJGPP * header file). */ typedef union { struct { unsigned long edi; unsigned long esi; unsigned long ebp; unsigned long res; unsigned long ebx; unsigned long edx; unsigned long ecx; unsigned long eax; } d; struct { unsigned short di, di_hi; unsigned short si, si_hi; unsigned short bp, bp_hi; unsigned short res, res_hi; unsigned short bx, bx_hi; unsigned short dx, dx_hi; unsigned short cx, cx_hi; unsigned short ax, ax_hi; unsigned short flags; unsigned short es; unsigned short ds; unsigned short fs; unsigned short gs; unsigned short ip; unsigned short cs; unsigned short sp; unsigned short ss; } x; struct { unsigned char edi[4]; unsigned char esi[4]; unsigned char ebp[4]; unsigned char res[4]; unsigned char bl, bh, ebx_b2, ebx_b3; unsigned char dl, dh, edx_b2, edx_b3; unsigned char cl, ch, ecx_b2, ecx_b3; unsigned char al, ah, eax_b2, eax_b3; } h; } __dpmi_regs; unsigned __dpmi_allocate_dos_memory(int size, unsigned *selector) { union REGPACK regs = {0}; regs.w.ax = 0x100; /* DPMI function -- allocate low memory */ regs.w.bx = size; /* Number of Paragraphs to allocate */ intr(0x31, ®s); /* DPMI interface */ *selector = regs.w.dx; return (regs.w.ax); }; void __dpmi_free_dos_memory(unsigned sel) { union REGPACK regs = {0}; regs.w.ax = 0x101; /* DPMI function -- free low memory */ regs.x.edx = sel; /* PM selector for memory block */ intr(0x31, ®s); /* DPMI interface */ }; void __dpmi_int(int intno, __dpmi_regs *dblock) { union REGPACK regs = {0}; regs.w.ax = 0x300; /* DPMI function -- real mode interrupt */ regs.h.bl = intno; /* interrupt 0x10 */ regs.x.edi = FP_OFF(dblock); /* Pointer to dblock (offset and segment) */ regs.x.es = FP_SEG(dblock); intr(0x31, ®s); /* DPMI interface */ }; unsigned short __dpmi_sel = 0x0000; #define _farsetsel(x) __dpmi_sel=(x) extern void _farnspokeb(unsigned long offset, unsigned char value); #pragma aux _farnspokeb = \ "push fs" \ "mov fs,__dpmi_sel" \ "mov fs:[eax],bl" \ "pop fs" \ parm [eax] [bl]; #else /* USE_WAT */ #include #include #include #endif /* USE_WAT */ /* * Since you cannot send 32bit pointers to a 16bit interrupt handler * and the video BIOS wants a (16bit) pointer to the font, we have * to allocate a block of dos memory, copy the font into it, then * translate a 32bit pointer into a 16bit pointer to that block. * * DPMI - Dos Protected Mode Interface provides functions that let * us do that. */ void enable_graphic_font(const char *font) { __dpmi_regs dblock = {{0}}; unsigned int seg, i; int sel; /* * Allocate a block of memory 4096 bytes big in `low memory' so a real * mode interrupt can access it. Real mode pointer is returned as seg:0 * Protected mode pointer is sel:0. */ seg = __dpmi_allocate_dos_memory(256, &sel); /* Copy the information into low memory buffer, by copying one byte at * a time. According to the info in , the functions * _farsetsel() and _farnspokeb() will optimise away completely */ _farsetsel(sel); /* Set the selector to write to */ for (i = 0; i<4096; i++) { _farnspokeb(i, *font++); /* Copy 1 byte into low (far) memory */ } /* * Now we use DPMI as a jumper to call the real mode interrupt. This * is needed because loading `es' while in protected mode with a real * mode pointer will cause an Protection Fault and calling the interrupt * directly using the protected mode pointer will result in garbage * being received by the interrupt routine */ dblock.d.eax = 0x1100; /* BIOS function -- set font */ dblock.d.ebx = 0x1000; /* bh = size of a letter; bl = 0 (reserved) */ dblock.d.ecx = 0x00FF; /* Last character in font */ dblock.x.es = seg; /* Pointer to font segment */ dblock.d.ebp = 0x0000; /* Pointer to font offset */ __dpmi_int(0x10, &dblock); /* We're done with the low memory, free it */ __dpmi_free_dos_memory(sel); } #endif /* USE_286 */ #endif /* ALLOW_GRAPH */ /* * Initialize the IBM "visual module" * * Hack -- we assume that "blank space" should be "white space" * (and not "black space" which might make more sense). * * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value * into an 8 bit value, without losing much precision, by using the 2 * most significant bits as the least significant bits in the new value. */ errr init_ibm(void) { int i; int mode; term *t = &term_screen_body; union REGS r; /* Check for "Windows" */ if (getenv("windir")) { r.h.ah = 0x16; /* Windows API Call -- Set device focus */ r.h.al = 0x8B; /* Causes Dos boxes to become fullscreen */ r.h.bh = r.h.bl = 0x00; /* 0x0000 = current Dos box */ int86(0x2F, &r, &r); /* Call the Windows API */ }; /* Initialize "color_table" */ for (i = 0; i < 16; i++) { long rv, gv, bv; /* Extract desired values */ rv = angband_color_table[i][1] >> 2; gv = angband_color_table[i][2] >> 2; bv = angband_color_table[i][3] >> 2; /* Extract the "complex" codes */ ibm_color_complex[i] = ((rv) | (gv << 8) | (bv << 16)); /* Save the "simple" codes */ angband_color_table[i][0] = ibm_color_simple[i]; } #ifdef USE_WAT /* Set the video mode */ if (_setvideomode(_VRES16COLOR)) { mode = 0x13; } /* Wimpy monitor */ else { mode = 0x03; } /* Force 25 line mode */ _setvideomode(_TEXTC80); _settextrows(25); #else /* USE_WAT */ /* Set video mode */ r.h.ah = 0x00; r.h.al = 0x13; /* VGA only mode */ int86(0x10, &r, &r); /* Get video mode */ r.h.ah = 0x0F; int86(0x10, &r, &r); mode = r.h.al; /* Set video mode */ r.h.ah = 0x00; r.h.al = 0x03; /* Color text mode */ int86(0x10, &r, &r); #endif /* USE_WAT */ /* Check video mode */ if (mode == 0x13) { /* Remember the mode */ use_color_complex = TRUE; /* Instantiate the color set */ activate_color_complex(); } #ifdef USE_GRAPHICS /* Try to activate bitmap graphics */ if (arg_graphics && use_color_complex) { FILE *f; char buf[4096]; /* Build the filename */ path_make(buf, ANGBAND_DIR_XTRA, "angband.fnt"); /* Open the file */ f = fopen(buf, "rb"); /* Okay */ if (f) { /* Load the bitmap data */ if (fread(buf, 1, 4096, f) != 4096) { quit("Corrupt 'angband.fnt' file"); } /* Close the file */ fclose(f); /* Enable graphics */ enable_graphic_font(buf); /* Enable colors (again) */ activate_color_complex(); /* Use graphics */ use_graphics = GRAPHICS_ORIGINAL; } } #endif #ifdef USE_CONIO #else /* USE_CONIO */ /* Build a "wiper line" */ for (i = 0; i < 80; i++) { /* Space */ wiper[2*i] = ' '; /* Black */ wiper[2*i+1] = TERM_WHITE; } #endif /* USE_CONIO */ #ifdef USE_VIRTUAL /* Make the virtual screen */ C_MAKE(VirtualScreen, rows * cols * 2, byte); #endif /* USE_VIRTUAL */ #ifdef USE_CONIO /* Clear the screen */ clrscr(); #else /* USE_CONIO */ /* Clear each line (virtual or physical) */ for (i = 0; i < rows; i++) { /* Clear the line */ memcpy((VirtualScreen + ((i*cols) << 1)), wiper, (cols << 1)); } # ifdef USE_VIRTUAL # ifdef USE_WAT /* Copy the virtual screen to the physical screen */ memcpy(PhysicalScreen, VirtualScreen, 25*80*2); # else /* USE_WAT */ /* Erase the physical screen */ ScreenClear(); # endif /* USE_WAT */ # endif /* USE_VIRTUAL */ #endif /* USE_CONIO */ /* Place the cursor */ Term_curs_ibm(0, 0); /* Access the "default" cursor info */ r.h.ah = 3; r.h.bh = 0; /* Make the call */ int86(0x10, &r, &r); /* Extract the standard cursor info */ saved_cur_v = 1; saved_cur_high = r.h.ch; saved_cur_low = r.h.cl; /* Initialize the term */ term_init(t, 80, 24, 256); #ifdef USE_CONIO #else /* USE_CONIO */ /* Always use "Term_pict()" */ t->always_pict = TRUE; #endif /* USE_CONIO */ /* Use "black space" to erase */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Prepare the init/nuke hooks */ t->init_hook = Term_init_ibm; t->nuke_hook = Term_nuke_ibm; /* Connect the hooks */ t->xtra_hook = Term_xtra_ibm; t->curs_hook = Term_curs_ibm; t->wipe_hook = Term_wipe_ibm; t->text_hook = Term_text_ibm; t->pict_hook = Term_pict_ibm; /* Save it */ term_screen = t; /* Activate it */ Term_activate(term_screen); /* Success */ return 0; } #endif /* USE_IBM */ zangband/src/main-lsl.c0000644000000000000000000002713310250356274014021 0ustar rootroot/* * File: main-lsl.c * Purpose: Support for Linux-SVGALIB Angband * Original Author: Jon Taylor (taylorj@gaia.ecs.csus.edu) * Update by: Dennis Payne (dulsi@identicalsoftware.com) * Version: 1.4.0, 12/05/99 * * Large amounts of code rewritten by Steven Fuerst. 20/04/2001 * * It now uses a hacked-up version of the X11 bmp-loading code. * (Preparing to use 256 colour 16x16 mode) */ #include "angband.h" #ifdef USE_LSL cptr help_lsl[] = { "To use LSL (Linux-SVGALIB)", NULL }; /* Standard C header files */ #include #include /* SVGAlib header files */ #include #include #include #include #define COLOR_OFFSET 240 /* Hack - Define font/graphics cell width and height */ #define CHAR_W 8 #define CHAR_H 13 /* Global palette */ static byte *pal = NULL; #ifdef USE_GRAPHICS /* * The Win32 "BITMAPFILEHEADER" type. */ typedef struct BITMAPFILEHEADER { u16b bfType; u32b bfSize; u16b bfReserved1; u16b bfReserved2; u32b bfOffBits; } BITMAPFILEHEADER; /* * The Win32 "BITMAPINFOHEADER" type. */ typedef struct BITMAPINFOHEADER { u32b biSize; u32b biWidth; u32b biHeight; u16b biPlanes; u16b biBitCount; u32b biCompresion; u32b biSizeImage; u32b biXPelsPerMeter; u32b biYPelsPerMeter; u32b biClrUsed; u32b biClrImportand; } BITMAPINFOHEADER; /* * The Win32 "RGBQUAD" type. */ typedef struct RGBQUAD { unsigned char b, g, r; unsigned char filler; } RGBQUAD; /*** Helper functions for system independent file loading. ***/ static byte get_byte(FILE *fff) { /* Get a character, and return it */ return (getc(fff) & 0xFF); } static void rd_byte(FILE *fff, byte *ip) { *ip = get_byte(fff); } static void rd_u16b(FILE *fff, u16b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u16b)(get_byte(fff)) << 8); } static void rd_u32b(FILE *fff, u32b *ip) { (*ip) = get_byte(fff); (*ip) |= ((u32b)(get_byte(fff)) << 8); (*ip) |= ((u32b)(get_byte(fff)) << 16); (*ip) |= ((u32b)(get_byte(fff)) << 24); } /* * Read a Win32 BMP file. * * Assumes that the bitmap has a size such that no padding is needed in * various places. Currently only handles bitmaps with 3 to 256 colors. */ static byte *ReadBMP(char *Name, int *bw, int *bh) { FILE *f; BITMAPFILEHEADER fileheader; BITMAPINFOHEADER infoheader; byte *Data; int ncol; int total; int i; u16b x, y; /* Open the BMP file */ f = fopen(Name, "r"); /* No such file */ if (!f) { quit ("No bitmap to load!"); } /* Read the "BITMAPFILEHEADER" */ rd_u16b(f, &(fileheader.bfType)); rd_u32b(f, &(fileheader.bfSize)); rd_u16b(f, &(fileheader.bfReserved1)); rd_u16b(f, &(fileheader.bfReserved2)); rd_u32b(f, &(fileheader.bfOffBits)); /* Read the "BITMAPINFOHEADER" */ rd_u32b(f, &(infoheader.biSize)); rd_u32b(f, &(infoheader.biWidth)); rd_u32b(f, &(infoheader.biHeight)); rd_u16b(f, &(infoheader.biPlanes)); rd_u16b(f, &(infoheader.biBitCount)); rd_u32b(f, &(infoheader.biCompresion)); rd_u32b(f, &(infoheader.biSizeImage)); rd_u32b(f, &(infoheader.biXPelsPerMeter)); rd_u32b(f, &(infoheader.biYPelsPerMeter)); rd_u32b(f, &(infoheader.biClrUsed)); rd_u32b(f, &(infoheader.biClrImportand)); /* Verify the header */ if (feof(f) || (fileheader.bfType != 19778) || (infoheader.biSize != 40)) { quit_fmt("Incorrect BMP file format %s", Name); } /* The two headers above occupy 54 bytes total */ /* The "bfOffBits" field says where the data starts */ /* The "biClrUsed" field does not seem to be reliable */ /* Compute number of colors recorded */ ncol = (fileheader.bfOffBits - 54) / 4; for (i = 0; i < ncol; i++) { RGBQUAD clrg; /* Read an "RGBQUAD" */ rd_byte(f, &(clrg.b)); rd_byte(f, &(clrg.g)); rd_byte(f, &(clrg.r)); rd_byte(f, &(clrg.filler)); /* Analyze the color */ pal[i * 3] = clrg.b; pal[i * 3 + 1] = clrg.g; pal[i * 3 + 2] = clrg.r; } /* Look for illegal bitdepths. */ if ((infoheader.biBitCount == 1) || (infoheader.biBitCount == 24)) { quit_fmt("Illegal biBitCount %d in %s", infoheader.biBitCount, Name); } /* Determine total bytes needed for image */ total = infoheader.biWidth * (infoheader.biHeight + 2); /* Allocate image memory */ C_MAKE(Data, total, byte); for (y = 0; y < infoheader.biHeight; y++) { int y2 = infoheader.biHeight - y - 1; for (x = 0; x < infoheader.biWidth; x++) { int ch = getc(f); /* Verify not at end of file XXX XXX */ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name); if (infoheader.biBitCount == 8) { Data[x + y2 * infoheader.biWidth] = ch; } else if (infoheader.biBitCount == 4) { Data[x + y2 * infoheader.biWidth] = ch / 16; x++; Data[x + y2 * infoheader.biWidth] = ch % 16; } } } fclose(f); /* Save the size for later */ *bw = infoheader.biWidth; *bh = infoheader.biHeight; return (Data); } #endif /* USE_GRAPHICS */ /* The main "term" structure */ static term term_screen_body; /* The visible and virtual screens */ GraphicsContext *screen; GraphicsContext *buffer; /* The font data */ static void *font; /* Look for a font to use */ static gzFile get_fontfile(void) { gzFile fontfile; fontfile = gzopen("/usr/lib/kbd/consolefonts/lat1-12.psf.gz","r"); if (fontfile) return (fontfile); /* Try uncompressed */ fontfile = gzopen("/usr/lib/kbd/consolefonts/lat1-12.psf","r"); if (fontfile) return (fontfile); /* Debian is special... */ fontfile = gzopen("/usr/share/consolefonts/lat1-12.psf.gz","r"); if (fontfile) return (fontfile); /* Try uncompressed */ fontfile = gzopen("/usr/share/consolefonts/lat1-12.psf","r"); if (fontfile) return (fontfile); /* Failure */ return (NULL); } /* Initialize the screen font */ static void initfont(void) { gzFile fontfile; void *temp; long junk; fontfile = get_fontfile(); if (!(fontfile)) { quit("Error: could not open font file. Aborting....\n"); exit(1); } /* Junk the 4-byte header */ gzread(fontfile, &junk, 4); /* Initialize font */ /* * Read in 13 bytes per character, and there are 256 characters * in the font. This means we need to load 13x256 = 3328 bytes. */ C_MAKE(temp, 256 * 13, byte); gzread(fontfile, temp, 256 * 13); /* * I don't understand this code - SF * (Is it converting from 8x13 -> 8x12?) * * I assume 15 is a colour... */ font = malloc(256 * 8 * 12 * BYTESPERPIXEL); gl_expandfont(8, 12, 15, temp, font); gl_setfont(8, 12, font); /* Cleanup */ free(temp); gzclose(fontfile); } /* Initialize palette values for colors 0-15 */ static void setpal(void) { int i; gl_setpalette(pal); for (i = 0; i < 16; i++) { gl_setpalettecolor(COLOR_OFFSET + i, angband_color_table[i][1] >> 2, angband_color_table[i][2] >> 2, angband_color_table[i][3] >> 2); } } /* * Check for "events" * If block, then busy-loop waiting for event, else check once and exit. */ static errr CheckEvents(int block) { int k = 0; if (block) { k = vga_getkey(); if (k < 1) return (1); } else { k = vga_getch(); } Term_keypress(k); return(0); } /* * Low-level graphics routine (assumes valid input) * Do a "special thing" */ static errr term_xtra_svgalib(int n, int v) { switch (n) { case TERM_XTRA_EVENT: { /* Process some pending events */ if (v) return (CheckEvents(FALSE)); while (!CheckEvents(TRUE)); return 0; } case TERM_XTRA_FLUSH: { /* Flush all pending events */ /* Should discard all key presses but unimplemented */ return 0; } case TERM_XTRA_DELAY: { /* Delay for some milliseconds */ if (v > 0) usleep(1000 * v); return 0; } } return 1; } /* * Low-level graphics routine (assumes valid input) * Draws a "cursor" at (x,y) */ static errr term_curs_svgalib(int x, int y) { gl_fillbox(x * CHAR_W, y * CHAR_H, CHAR_W, CHAR_H, 15); return(0); } /* * Low-level graphics routine (assumes valid input) * Erases a rectangular block of characters from (x,y) to (x+w,y+h) */ static errr term_wipe_svgalib(int x, int y, int n) { gl_fillbox(x * CHAR_W, y * CHAR_H, n * CHAR_W, CHAR_H, 0); return(0); } /* * Low-level graphics routine (assumes valid input) * Draw n chars at location (x,y) with value s and attribute a */ static errr term_text_svgalib(int x, int y, int n, byte a, cptr s) { /* Clear the area */ term_wipe_svgalib(x, y, n); /* Draw the coloured text */ gl_colorfont(8, 12, COLOR_OFFSET + (a & 0x0F), font); gl_writen(x * CHAR_W, y * CHAR_H, n, (char *) s); return(0); } /* * Low-level graphics routine (assumes valid input) * Draw n chars at location (x,y) with value s and attribute a */ #ifdef USE_GRAPHICS static errr term_pict_svgalib(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i; int x2, y2; /* Hack - Ignore unused transparency data for now */ (void) tap; (void) tcp; for (i = 0; i < n; i++) { x2 = (cp[i] & 0x7F) * CHAR_W; y2 = (ap[i] & 0x7F) * CHAR_H; gl_copyboxfromcontext(buffer, x2, y2, CHAR_W, CHAR_H, (x + i) * CHAR_W, y * CHAR_H); } return(0); } static void term_load_bitmap(void) { char path[1024]; byte *temp = NULL; int bw, bh; /* Build the "graf" path */ path_make(path, ANGBAND_DIR_XTRA, "graf"); strnfmt(path, 1024, "%s/8x13.bmp", path); /* See if the file exists */ if (fd_close(fd_open(path, O_RDONLY))) { quit_fmt("Unable to load bitmap data file %s, bailing out....\n", path); exit (-1); } temp = ReadBMP(path, &bw, &bh); /* Blit bitmap into buffer */ gl_putbox(0, 0, bw, bh, temp); FREE(temp); return; } #endif /* USE_GRAPHICS */ /* * Term hook * Initialize a new term */ static void term_init_svgalib(term *t) { int vgamode; /* Only one term */ (void) t; vga_init(); /* The palette is 256x3 bytes big (RGB). */ C_MAKE(pal, 768, byte); #ifdef USE_GRAPHICS /* Hardwire this mode in for now */ vgamode = G1024x768x256; /* Set up the bitmap buffer context */ gl_setcontextvgavirtual(vgamode); buffer = gl_allocatecontext(); gl_getcontext(buffer); /* Load bitmap into virtual screen */ term_load_bitmap(); #endif /* USE_GRAPHICS */ /* Hardwire this mode in for now */ vgamode = G640x480x256; /* Set up the physical screen context */ if (vga_setmode(vgamode) < 0) { quit("Graphics mode not available!"); } gl_setcontextvga(vgamode); screen = gl_allocatecontext(); gl_getcontext(screen); /* Is this needed? */ gl_enablepageflipping(screen); /* Set up palette colors */ setpal(); /* Load the character font data */ initfont(); /* Color 0 isn't transparent */ gl_setwritemode(WRITEMODE_OVERWRITE); } /* * Term hook * Nuke an old term */ static void term_nuke_svgalib(term *t) { /* Only one term */ (void) t; vga_setmode(TEXT); } /* * Quit hook - go back to text mode */ static void hook_quit(cptr str) { /* Hack - Ignore parameter */ (void) str; /* Go back to text mode */ vga_setmode(TEXT); } /* * Hook SVGAlib routines into term.c */ errr init_lsl(void) { term *t = &term_screen_body; #ifdef USE_GRAPHICS if (arg_graphics) { use_graphics = GRAPHICS_ORIGINAL; } #endif /* USE_GRAPHICS */ /* Quit hook */ quit_aux = hook_quit; /* Initialize the term */ term_init(t, 80, 24, 1024); /* The cursor is done via software and needs erasing */ t->soft_cursor = TRUE; t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Add hooks */ t->init_hook = term_init_svgalib; t->nuke_hook = term_nuke_svgalib; t->text_hook = term_text_svgalib; #ifdef USE_GRAPHICS if (use_graphics) { t->pict_hook = term_pict_svgalib; t->higher_pict = TRUE; } #endif /* USE_GRAPHICS */ t->wipe_hook = term_wipe_svgalib; t->curs_hook = term_curs_svgalib; t->xtra_hook = term_xtra_svgalib; /* Save the term */ term_screen = t; /* Activate it */ Term_activate(term_screen); return(0); } #endif /* USE_LSL */ zangband/src/main-mac-carbon.c0000644000000000000000000036426110250356274015237 0ustar rootroot/* File: main-mac.c */ /* Purpose: Simple support for MACINTOSH Angband */ /* * This file should only be compiled with the "Macintosh" version * * This file written by "Ben Harrison (benh@phial.com)". * * Some code adapted from "MacAngband 2.6.1" by Keith Randall * * Maarten Hazewinkel (mmhazewi@cs.ruu.nl) provided some initial * suggestions for the PowerMac port. * * Steve Linberg (slinberg@crocker.com) provided the code surrounded * by "USE_SFL_CODE". * * The graphics code is adapted from an extremely minimal subset of * the code from "Sprite World II", an amazing animation package. * * See "z-term.c" for info on the concept of the "generic terminal" * * The preference file is now a text file named "Angband preferences". * * Note that the "preference" file is now a simple text file called * "Angband preferences", which contains the versions information, so * that obsolete preference files can be ignored (this may be bad). * * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c" * should probably be "unloaded" as soon as they are no longer needed, * to save space, but I do not know how to do this. * * Stange bug -- The first "ClipRect()" call crashes if the user closes * all the windows, switches to another application, switches back, and * then re-opens the main window, for example, using "command-a". * * By default, this file assumes that you will be using a 68020 or better * machine, running System 7 and Color Quickdraw. In fact, the game will * refuse to run unless these features are available. This allows the use * of a variety of interesting features such as graphics and sound. * * To create a version which can be used on 68000 machines, or on machines * which are not running System 7 or Color Quickdraw, simply activate the * "ANGBAND_LITE_MAC" compilation flag in the proper header file. This * will disable all "modern" features used in this file, including support * for multiple sub-windows, color, graphics, and sound. * * When compiling with the "ANGBAND_LITE_MAC" flag, the "ANGBAND_LITE" * flag will be automatically defined, which will disable many of the * advanced features of the game itself, reducing the total memory usage. * * If you are never going to use "graphics" (especially if you are not * compiling support for graphics anyway) then you can delete the "pict" * resource with id "1001" with no dangerous side effects. */ /* * Important Resources in the resource file: * * FREF 130 = 'A271' / 'APPL' (application) * FREF 129 = 'A271' / 'SAVE' (save file) * FREF 130 = 'A271' / 'TEXT' (bone file, generic text file) * FREF 131 = 'A271' / 'DATA' (binary image file, score file) * * DLOG 128 = "About Angband..." * * ALRT 128 = unused (?) * ALRT 129 = "Warning..." * ALRT 130 = "Are you sure you want to quit without saving?" * * DITL 128 = body for DLOG 128 * DITL 129 = body for ALRT 129 * DITL 130 = body for ALRT 130 * * ICON 128 = "warning" icon * * MENU 128 = apple (about, -, ...) * MENU 129 = File (new, open, close, save, -, exit, quit) * MENU 130 = Edit (undo, -, cut, copy, paste, clear) * * PICT 1001 = Graphics tile set */ /* * File name patterns: * all 'APEX' files have a filename of the form "*:apex:*" (?) * all 'BONE' files have a filename of the form "*:bone:*" (?) * all 'DATA' files have a filename of the form "*:data:*" * all 'SAVE' files have a filename of the form "*:save:*" * all 'USER' files have a filename of the form "*:user:*" (?) * * Perhaps we should attempt to set the "_ftype" flag inside this file, * to avoid nasty file type information being spread all through the * rest of the code. (?) This might require adding hooks into the * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX */ /* * Reasons for each header file: * * angband.h = Angband header file * * Types.h = (included anyway) * Gestalt.h = gestalt code * QuickDraw.h = (included anyway) * Files.h = file code * Fonts.h = font code * Menus.h = menu code * Dialogs.h = dialog code * MacWindows.h = window code * Palettes.h = palette code * ToolUtils.h = HiWord() / LoWord() * Events.h = event code * Resources.h = resource code * Controls.h = control code * SegLoad.h = ExitToShell() * Memory.h = memory code * QDOffscreen.h = GWorld code * Sound.h = sound code * Navigation.h = file dialog/navigation code * Processes.h = process code * * For backwards compatibility: * Use GestaltEqu.h instead of Gestalt.h * Add Desk.h to include simply includes Menus.h, Devices.h, Events.h */ #ifndef TARGET_CARBON #define TARGET_CARBON 1 #endif #include "angband.h" #include #include #include /* #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include */ #ifndef TARGET_CARBON #include #endif /* Non-Carbon backward compatibility */ #ifndef TARGET_CARBON #define EnableMenuItem(a,b) EnableItem(a,b) #define DisableMenuItem(a,b) DisableItem(a,b) #endif /* * Use "malloc()" instead of "NewPtr()" */ #define USE_MALLOC #if defined(powerc) || defined(__powerc) /* * Disable "LITE" version */ # undef ANGBAND_LITE_MAC #endif #ifdef ANGBAND_LITE_MAC /* * Maximum number of windows */ # define MAX_TERM_DATA 1 #else /* ANGBAND_LITE_MAC */ /* * Maximum number of windows */ # define MAX_TERM_DATA 8 /* * Activate some special code */ # define USE_SFL_CODE #endif /* ANGBAND_LITE_MAC */ #ifdef USE_SFL_CODE /* * Include the necessary header files */ /* #include #include #include */ #endif #if 0 /* * The Angband Color Set (0 to 15): * Black, White, Slate, Orange, Red, Blue, Green, Umber * D-Gray, L-Gray, Violet, Yellow, L-Red, L-Blue, L-Green, L-Umber * * Colors 8 to 15 are basically "enhanced" versions of Colors 0 to 7. * * On the Macintosh, we use color quickdraw, and we use actual "RGB" * values below to choose the 16 colors. * * If we are compiled for ancient machines, we bypass color and simply * draw everything in white (letting "z-term.c" automatically convert * "black" into "wipe" calls). */ static RGBColor foo[16] = { {0x0000, 0x0000, 0x0000}, /* TERM_DARK */ {0xFFFF, 0xFFFF, 0xFFFF}, /* TERM_WHITE */ {0x8080, 0x8080, 0x8080}, /* TERM_SLATE */ {0xFFFF, 0x8080, 0x0000}, /* TERM_ORANGE */ {0xC0C0, 0x0000, 0x0000}, /* TERM_RED */ {0x0000, 0x8080, 0x4040}, /* TERM_GREEN */ {0x0000, 0x0000, 0xFFFF}, /* TERM_BLUE */ {0x8080, 0x4040, 0x0000}, /* TERM_UMBER */ {0x4040, 0x4040, 0x4040}, /* TERM_L_DARK */ {0xC0C0, 0xC0C0, 0xC0C0}, /* TERM_L_WHITE */ {0xFFFF, 0x0000, 0xFFFF}, /* TERM_VIOLET */ {0xFFFF, 0xFFFF, 0x0000}, /* TERM_YELLOW */ {0xFFFF, 0x0000, 0x0000}, /* TERM_L_RED */ {0x0000, 0xFFFF, 0x0000}, /* TERM_L_GREEN */ {0x0000, 0xFFFF, 0xFFFF}, /* TERM_L_BLUE */ {0xC0C0, 0x8080, 0x4040} /* TERM_L_UMBER */ }; #endif /* * Forward declare */ typedef struct term_data term_data; /* * Extra "term" data */ struct term_data { term *t; Rect r; WindowPtr w; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ short padding; short pixelDepth; GWorldPtr theGWorld; GDHandle theGDH; GDHandle mainSWGDH; #endif /* ANGBAND_LITE_MAC */ Str15 title; s16b oops; s16b keys; s16b last; s16b mapped; s16b rows; s16b cols; s16b font_id; s16b font_size; s16b font_face; s16b font_mono; s16b font_o_x; s16b font_o_y; s16b font_wid; s16b font_hgt; s16b tile_o_x; s16b tile_o_y; s16b tile_wid; s16b tile_hgt; s16b size_wid; s16b size_hgt; s16b size_ow1; s16b size_oh1; s16b size_ow2; s16b size_oh2; }; /* * Forward declare -- see below */ static bool CheckEvents(bool wait); /* * Hack -- location of the main directory */ static short app_vol; static long app_dir; /* * Delay handling of double-clicked savefiles */ Boolean open_when_ready = FALSE; /* * Delay handling of pre-emptive "quit" event */ Boolean quit_when_ready = FALSE; /* * Hack -- game in progress */ static int game_in_progress = 0; /* * Only do "SetPort()" when needed */ static WindowPtr active = NULL; /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Note when "open"/"new" become valid */ static bool initialized = FALSE; /* * Constants */ /* * Strings that contain the basic "lib/xtra" directory paths */ #define ANGBAND_XTRA_FONT "/lib/xtra/font" #define ANGBAND_XTRA_GRAF "/lib/xtra/graf" #define ANGBAND_XTRA_MUSIC "/lib/xtra/music" #define ANGBAND_XTRA_SOUND "/lib/xtra/sound" #define ANGBAND_XTRA_GRAF_16x16 ":lib:xtra:graf:16x16.gif" #define ANGBAND_XTRA_GRAF_8x8 ":lib:xtra:graf:8x8.gif" /* * Constants that control the sound effects and music sound tracks */ #define SAMPLE_MAX 10 /* How many sample choices per sound effect are allowed */ #define kMaxChannels 20 /* How many sound channels will be pooled */ #define SOUND_VOLUME_MIN 0 /* Default minimum sound volume for sound effects */ #define SOUND_VOLUME_MAX 255 /* Default maximum sound volume for sound effects */ #define VOLUME_MIN 0 /* Minimum sound volume in % */ #define VOLUME_MAX 100 /* Maximum sound volume in % */ #define VOLUME_INC 5 /* Increment sound volume in % */ #define SONG_MAX 20 /* How many music sound tracks */ #define SONG_VOLUME_MIN 0 /* Default minimum sound volume for sound tracks */ #define SONG_VOLUME_MAX 255 /* Default maximum sound volume for sound tracks */ #define SONG_REPEAT_MIN 1 /* Minimum times a sound track can be repeated */ #define SONG_REPEAT_MAX 100 /* Maximum times a sound track can be repeated */ static char sample_name[SOUND_MAX][SAMPLE_MAX][32]; /* File names of each sound effect sample */ static Handle sample[SOUND_MAX][SAMPLE_MAX]; /* Data handle containing the sound data for each sample */ static int sample_count[SOUND_MAX]; /* Sample count for each sound effect (not necessarily SAMPLE_MAX for each sound effect) */ static SndChannelPtr mySndChannel[kMaxChannels]; /* Sound channel pool */ static long channelInit = 0; /* Has the sound channel pool been initialized */ static short gSoundVolume = 10; /* sound volume percentage */ static char song_name[SONG_MAX][32]; /* File names for each music sound track */ static char song_description[SONG_MAX][32]; /* File description for each music sound track */ static short song_volume[SONG_MAX]; /* Volume for each music sound track */ static short song_repeat[SONG_MAX]; /* Repeat count for each music sound track */ static Movie song[SONG_MAX]; /* QT Movie for each music sound track */ static int current_song = 0; /* Which music sound track is currently playing */ static bool songReady = false; /* Has the music sound tracks been loaded */ static bool songStarted = false; /* Has the music sound tracks been started */ static bool songOn = false; /* Do we want the music to play or not */ short gTileWidth; /* Graf Size (X) */ short gTileHeight; /* Graf Size (Y) */ short gTileCols; /* Number of Cols in Pict */ short gTileRows; /* Number of Rows in Pict */ /* * Forward Declare */ typedef struct FrameRec FrameRec; /* * Frame * * - GWorld for the frame image * - Handle to pix map (saved for unlocking/locking) * - Pointer to color pix map (valid only while locked) */ struct FrameRec { GWorldPtr framePort; /* Graphic World for storing graphic tiles */ PixMapHandle framePixHndl; /* Handle to PixMap for storing graphic tiles */ PixMapPtr framePix; /* Ptr to PixMap for storing graphic tiles */ GWorldPtr bufferPort; /* Graphic World for buffering one row of drawing */ PixMapHandle bufferPixHndl; /* Handle to PixMap for buffering one row of drawing */ PixMapPtr bufferPix; /* Ptr to PixMap for buffering one row of drawing */ }; /* * The global picture data */ static FrameRec *frameP = NULL; /* * CodeWarrior uses Universal Procedure Pointers */ #ifdef USE_SFL_CODE /* * Apple Event Hooks */ AEEventHandlerUPP AEH_Start_UPP; AEEventHandlerUPP AEH_Quit_UPP; AEEventHandlerUPP AEH_Print_UPP; AEEventHandlerUPP AEH_Open_UPP; #endif static void local_to_global( Rect *r ) { Point temp; temp.h = r->left; temp.v = r->top; LocalToGlobal( &temp ); r->left = temp.h; r->top = temp.v; temp.h = r->right; temp.v = r->bottom; LocalToGlobal( &temp ); r->right = temp.h; r->bottom = temp.v; } static void global_to_local( Rect *r ) { Point temp; temp.h = r->left; temp.v = r->top; GlobalToLocal( &temp ); r->left = temp.h; r->top = temp.v; temp.h = r->right; temp.v = r->bottom; GlobalToLocal( &temp ); r->right = temp.h; r->bottom = temp.v; } /* * Center a rectangle inside another rectangle */ static void center_rect(Rect *r, Rect *s) { int centerx = (s->left + s->right)/2; int centery = (2*s->top + s->bottom)/3; int dx = centerx - (r->right - r->left)/2 - r->left; int dy = centery - (r->bottom - r->top)/2 - r->top; r->left += dx; r->right += dx; r->top += dy; r->bottom += dy; } /* * Convert a pascal string in place * * This function may be defined elsewhere, but since it is so * small, it is not worth finding the proper function name for * all the different platforms. */ static void ptocstr(StringPtr src) { int i; /* Hack -- pointer */ char *s = (char*)(src); /* Hack -- convert the string */ for (i = s[0]; i; i--, s++) s[0] = s[1]; /* Hack -- terminate the string */ s[0] = '\0'; } #if defined(USE_SFL_CODE) /* * The following three routines (pstrcat, pstrinsert, and PathNameFromDirID) * were taken from the Think Reference section called "Getting a Full Pathname" * (under the File Manager section). We need PathNameFromDirID to get the * full pathname of the opened savefile, making no assumptions about where it * is. * * I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully * nice. */ static void pstrcat(StringPtr dst, StringPtr src) { /* copy string in */ BlockMove(src + 1, dst + *dst + 1, *src); /* adjust length byte */ *dst += *src; } /* * pstrinsert - insert string 'src' at beginning of string 'dst' */ static void pstrinsert(StringPtr dst, StringPtr src) { /* make room for new string */ BlockMove(dst + 1, dst + *src + 1, *dst); /* copy new string in */ BlockMove(src + 1, dst + 1, *src); /* adjust length byte */ *dst += *src; } static void p2c_stringcopy( char *dst, StringPtr src ) { BlockMove( src + 1, (void *)dst, *src ); dst[*src] = 0x00; } static void c2p_stringcopy( StringPtr dst, const char *src ) { short len = strlen(src); if( len > 255 ) len = 255; BlockMove( (void *)src, dst + 1, len ); *dst = (unsigned char)len; } static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName) { CInfoPBRec block; Str255 directoryName; OSErr err; fullPathName[0] = '\0'; block.dirInfo.ioDrParID = dirID; block.dirInfo.ioNamePtr = directoryName; while (1) { block.dirInfo.ioVRefNum = vRefNum; block.dirInfo.ioFDirIndex = -1; block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID; err = PBGetCatInfo(&block, FALSE); pstrcat(directoryName, (StringPtr)"\p/"); pstrinsert(fullPathName, directoryName); if (block.dirInfo.ioDrDirID == 2) break; } /* Add the root slash */ pstrinsert(fullPathName, (StringPtr)"\p/" ); } #endif /* * Activate a given window, if necessary */ static void activate(WindowPtr w) { /* Activate */ if (active != w) { /* Activate */ if (w) { SetPort(GetWindowPort(w)); } /* Remember */ active = w; } } /* * Display a confirm dialog */ static bool mac_confirm(cptr message, cptr description) { bool return_value = false; OSErr err; Str255 errorMessage; Str255 descriptionMessage; SInt16 itemHit; /* Four types of alert we can choose from: kAlertStopAlert kAlertNoteAlert kAlertCautionAlert kAlertPlainAlert */ if( message != NULL ) c2p_stringcopy( errorMessage, message ); else c2p_stringcopy( errorMessage, "Warning!" ); if( description != NULL ) c2p_stringcopy( descriptionMessage, description ); else c2p_stringcopy( descriptionMessage, "" ); err = StandardAlert( kAlertCautionAlert, errorMessage, descriptionMessage, NULL, &itemHit ); if( err == noErr ) { /* Four Alert Button Choices kAlertStdAlertOKButton kAlertStdAlertCancelButton kAlertStdAlertOtherButton kAlertStdAlertHelpButton */ switch( itemHit ) { case kAlertStdAlertOKButton: { return_value = true; } break; case kAlertStdAlertCancelButton: { return_value = false; } break; case kAlertStdAlertOtherButton: { return_value = false; } break; case kAlertStdAlertHelpButton: { return_value = false; } break; default: { return_value = false; } } } return( return_value ); } /* * Display a warning message */ static void mac_warning(cptr warning) { /* ignore the button pressed */ mac_confirm( warning, "" ); } static OSType GetProcessSignature( void ) { ProcessSerialNumber thePSN; ProcessInfoRec info; OSErr err; thePSN.highLongOfPSN = 0; thePSN.lowLongOfPSN = kCurrentProcess; info.processInfoLength = sizeof(ProcessInfoRec); info.processName = nil; info.processAppSpec = nil; err = GetProcessInformation(&thePSN, &info); if( err != noErr ) quit( "Internal System Error. Process Information could not be found." ); return info.processSignature; } static OSErr ChooseFile( StringPtr filename, OSType *typelist, long typeCount ) { NavReplyRecord reply; NavDialogOptions dialogOptions; NavTypeListHandle navTypeList = NULL; OSErr err; err = NavGetDefaultDialogOptions( &dialogOptions ); if( err == noErr ) { if( typeCount > 0 ) { navTypeList = (NavTypeListHandle)NewHandle( sizeof(NavTypeList) + (sizeof(OSType)*(typeCount-1)) ); if( navTypeList == NULL ) quit( "Could not allocate memory for navigation file filter list." ); /* populate the navtypelist object */ { NavTypeListPtr typesP = (NavTypeListPtr) *((Handle) navTypeList); OSType signature = GetProcessSignature(); typesP->componentSignature = signature; typesP->reserved = 0; typesP->osTypeCount = typeCount; BlockMoveData(typelist, typesP->osType, (Size) (sizeof(OSType) * typeCount)); } } err = NavChooseFile( NULL, &reply, &dialogOptions, NULL, NULL, NULL, navTypeList, NULL ); if( reply.validRecord && err == noErr ) { FSSpec finalFSSpec; long index; long count; /* * We are ready to open the document(s), * grab information about each file for opening: */ err = AECountItems( &(reply.selection), &count ); for ( index=1; index<=count; index++ ) { AEKeyword keyWord; DescType typeCode; Size actualSize = 0; if (( err = AEGetNthPtr( &(reply.selection), index, typeFSS, &keyWord, &typeCode, &finalFSSpec, sizeof( FSSpec ), &actualSize )) == noErr ) { FSRef fileRef; Handle pathH; short length; err = FSpMakeFSRef( &finalFSSpec, &fileRef ); if( err == noErr ) { err = FSRefMakePath( &fileRef, filename, &length ); /* plog_fmt( "filename = %s", filename ); */ } } } } if( navTypeList != NULL ) { DisposeHandle( (Handle)navTypeList ); navTypeList = NULL; } } return err; } static errr process_sound_config_file( const char *name, const char *section ) { FILE *fp; char buf[1024]; int num = -1; errr err = 0; bool bypass = FALSE; bool foundSection = FALSE; char soundpath[1024]; /* Build the filename */ path_make(soundpath, ANGBAND_DIR_XTRA, "sound"); /* plog_fmt("XTRA/SOUND = %s", soundpath ); */ path_make(buf, soundpath, name); /* Open the file */ fp = my_fopen(buf, "r"); /* No such file */ if (!fp) return (-1); /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { /* Count lines */ num++; /* Skip "empty" lines */ if (!buf[0]) continue; /* Skip "blank" lines */ if (isspace(buf[0])) continue; /* Skip comments */ if (buf[0] == '#') continue; /* Look for section, presumably [SOUND] */ if( buf[0] == '[' ) { char section_string[64]; strnfmt( section_string, 64, "[%s]", section ); if( streq( section_string, buf ) ) { foundSection = TRUE; continue; } } if( foundSection ) { int i, index = 0, count = 0; char *t; char *cptr = &(buf[0]); char *the_file; char *the_name = strtok(buf, "="); if( !the_name ) continue; /* trim spaces */ while( *the_name && *the_name == ' ' ) the_name++; t = the_name + strlen(the_name) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } /* Find sound effect in pre-defined sound effect list */ for( i = 1; i < SOUND_MAX; i++ ) { if(streq( angband_sound_name[i], the_name )) { index = i; break; } } /* If sound effect number not found, then skip it */ if( index == 0 ) continue; /* Load in the specified sample filenames */ do { the_file = strtok(NULL, " "); if( the_file ) { /* trim spaces */ while( *the_file && *the_file == ' ' ) the_file++; t = the_file + strlen(the_file) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } /* Add the sample filename */ strcpy( sample_name[index][count], the_file ); count++; } } while( the_file ); /* Set the sound effect sample count */ sample_count[index] = count; } /* Oops */ if (err) break; } /* Error */ if (err) { /* Useful error message */ msgf("Error %d in line %d of file '%s'.", err, num, name); msgf("Parsing '%s'", buf); } /* Close the file */ my_fclose(fp); /* Result */ return (err); } static errr process_music_config_file( const char *name, const char *section ) { FILE *fp; char musicpath[1024]; char buf[1024]; int num = -1; errr err = 0; bool bypass = FALSE; bool foundSection = FALSE; /* Build the filename */ path_make(musicpath, ANGBAND_DIR_XTRA, "music"); path_make(buf, musicpath, name); /* Open the file */ fp = my_fopen(buf, "r"); /* No such file */ if (!fp) return (-1); /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { char *pbuf = &(buf[0]); /* Count lines */ num++; /* Skip "empty" lines */ if (!pbuf[0]) continue; /* Skip "blank" lines */ if (isspace(pbuf[0])) continue; /* Skip comments */ if (pbuf[0] == '#') continue; /* Look for section, presumably [MUSIC] */ if( pbuf[0] == '[' ) { char section_string[64]; strnfmt( section_string, 64, "[%s]", section ); if( streq( section_string, pbuf ) ) { foundSection = TRUE; continue; } } if( foundSection ) { int i, index = 0, count = 0, volume = 0, repeat = 0; char *t; char *the_file; char *the_description; char *the_volume; char *the_repeat; char *the_name = strtok(pbuf, "="); if( !the_name ) continue; /* trim spaces */ while( *the_name && *the_name == ' ' ) the_name++; t = the_name + strlen(the_name) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } /* Get the music sound track number */ index = atoi( the_name ); /* If the sound track number is invalid, then skip it */ if( index < 0 || index > SONG_MAX ) continue; /* Get the filename for the sound track, assumed to be in the lib/xtra/music directory */ the_file = strtok(NULL, "|"); if( the_file ) { /* trim spaces */ while( *the_file && *the_file == ' ' ) the_file++; t = the_file + strlen(the_file) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } strcpy( song_name[index], the_file ); strcpy( song_description[index], the_file ); } /* Get the description for the sound track, assumed to be in the lib/xtra/music directory */ the_description = strtok(NULL, "|"); if( the_description ) { /* trim spaces */ while( *the_description && *the_description == ' ' ) the_description++; t = the_description + strlen(the_description) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } if( *the_description != '-' || *the_description != (char)0x00 ) { strcpy( song_description[index], the_description ); } else if( the_file != NULL ) { strcpy( song_description[index], the_file ); } else { song_description[index][0] = 0); } } /* Get the volume (0-255) of this sound track, if it exists */ the_volume = strtok(NULL, "|"); if( the_volume ) { /* trim spaces */ while( *the_volume && *the_volume == ' ' ) the_volume++; t = the_volume + strlen(the_volume) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } if( *the_volume != '-' ) { volume = atoi( the_volume ); /* Ignore specified volume if it is out of range, default is SONG_VOLUME_MAX */ if( volume < SONG_VOLUME_MIN || volume > SONG_VOLUME_MAX ) { song_volume[index] = SONG_VOLUME_MAX; /*continue;*/ } else { song_volume[index] = volume; } } } /* Get the repeat value for the sound track, if it exists */ the_repeat = strtok(NULL, "|"); if( the_repeat ) { /* trim spaces */ while( *the_repeat && *the_repeat == ' ' ) the_repeat++; t = the_repeat + strlen(the_repeat) - 1; while( *t && *t == ' ' ) { *t = '\0'; t--; } if( *the_repeat != '-' ) { repeat = atoi( the_repeat ); /* Ignore the specified repeat value if it is out of range, default is SONG_REPEAT_MIN */ if( repeat < SONG_REPEAT_MIN || repeat > SONG_REPEAT_MAX ) { song_repeat[index] = SONG_REPEAT_MIN; /*continue;*/ } else { song_repeat[index] = repeat; } } } } /* Oops */ if (err) break; } /* Error */ if (err) { /* Useful error message */ msgf("Error %d in line %d of file '%s'.", err, num, name); msgf("Parsing '%s'", buf); } /* Close the file */ my_fclose(fp); /* Result */ return (err); } /* * get_picture_from_file * * Instead of loading the graphic tiles from macintosh resources, load the tiles from * a single graphic file, of any format supported by QuickTime. * Pass in the filename (path relative to the application), and it returns a PicHandle. */ static PicHandle get_picture_from_file( const char *filename ) { GraphicsImportComponent gi; PicHandle thePicture = NULL; OSErr theErr; Str255 pFileName; FSSpec theFile; c2p_stringcopy( pFileName, filename ); theErr = FSMakeFSSpec( app_vol, app_dir, pFileName, &theFile ); if( theErr == noErr ) { theErr = GetGraphicsImporterForFile( &theFile, &gi ); if( theErr == noErr ) { GraphicsImportGetAsPicture( gi, &thePicture ); if( thePicture == NULL ) { char message[512]; strcpy( message, "The graphics file storing the appropriate tiles could not be loaded. This file must be authentic and the format must be recognized by Quicktime. Please verify and try again. The target file is -> " ); strcat( message, filename ); mac_warning( message ); } } else { char message[512]; strcpy( message, "The graphics file storing the appropriate tiles could not be loaded. Verify its validity and try again. The target file is -> " ); strcat( message, filename ); mac_warning( message ); } } else { char message[512]; strcpy( message, "The graphics file storing the appropriate tiles could not be found. Verify its existence and try again. The target file is -> " ); strcat( message, filename ); mac_warning( message ); } return thePicture; } /* * get_next_song * * Determine the next sound track to play */ static int get_next_song( int current ) { int last_song = current++; int next = current; while( next != last_song ) { if( next == SONG_MAX ) next = 0; if( streq( song_name[next], "" ) ) { next++; } else { break; } } return next; } /* * check_music * * Check status of music sound track. * If the music sound track is playing, then devote some QT time for various tasks (MoviesTask) * If the sound track is done, then start the next sound track * If we haven't started any sound track yet, then go ahead and start everything going. * Call this function OFTEN. Best called from the idle thread or something. */ static void check_music( void ) { if( songReady ) { if( songOn ) { if( !songStarted ) { GoToBeginningOfMovie( song[current_song] ); SetMovieVolume( song[current_song], (short)song_volume[current_song] ); StartMovie( song[current_song] ); songStarted = true; } else if( songStarted && IsMovieDone( song[current_song] ) ) { /* restart from first available song */ current_song = get_next_song( current_song ); GoToBeginningOfMovie( song[current_song] ); SetMovieVolume( song[current_song], (short)song_volume[current_song] ); StartMovie( song[current_song] ); songStarted = true; } else { if( !streq( song_name[current_song], "" ) ) { MoviesTask( song[current_song], 0 ); } } } else { if( songStarted && !IsMovieDone( song[current_song] ) ) { /* stop the current song */ StopMovie( song[current_song] ); songStarted = false; } } } } /* * restart_music * * When someone selects a specific sound track to play, * interrupt the currently playing sound track, and start the specified one */ static void restart_music( int new_song ) { if( songReady ) { if( !songOn && (new_song != current_song)) { songOn = true; if( new_song >= 0 && new_song < SONG_MAX ) { if( songStarted && !IsMovieDone( song[current_song] ) ) { StopMovie( song[current_song] ); current_song = new_song; songStarted = false; } else { current_song = new_song; songStarted = false; } } } else { /* stop song playing */ songOn = false; } } } /* * init_music * * Load the music sound tracks specified in the "lib/xtra/music/music.cfg" file */ static void init_music( void ) { OSErr err; int i; short movieResFile; char musicpath[1024]; char filepath[1024]; Str255 pFilePath; FSSpec theFile; songReady = false; songStarted = false; for( i = 0; i < SONG_MAX; i++ ) { song_name[i][0] = 0; song_description[i][0] = 0; song_volume[i] = ((gSoundVolume*255)/100); song_repeat[i] = 1; song[i] = (Movie)NULL; songReady = false; } err = process_music_config_file( "music.cfg", "Music" ); if( err == noErr ) { for( i = 0; i < SONG_MAX; i++ ) { if( !streq( song_name[i], "" ) ) { /*path_make(musicpath, ANGBAND_DIR_XTRA, "music");*/ /*path_make( filepath, musicpath, song_name[i]);*/ strnfmt( filepath, 1024, ":lib:xtra:music:%s", song_name[i] ); /*path_make( filepath, ANGBAND_XTRA_MUSIC, song_name[i] );*/ c2p_stringcopy( pFilePath, filepath ); err = FSMakeFSSpec( app_vol, app_dir, pFilePath, &theFile ); if( err == noErr ) { err = OpenMovieFile( &theFile, &movieResFile, fsRdPerm ); if( err == noErr ) { short movieResID = 0; Str255 movieName; Boolean wasChanged; err = NewMovieFromFile( &song[i], movieResFile, &movieResID, movieName, newMovieActive, &wasChanged ); if( err == noErr ) { songReady = true; } } } } } } } /* * init_sounds * * Load the sound effect samples specified in the "lib/xtra/sound/sound.cfg" file */ static void init_sounds( void ) { OSErr err; int i, j; err = EnterMovies(); /* Start Loading Sounds */ err = process_sound_config_file( "sound.cfg", "Sound" ); /* * This set of loops may take a while depending on the count * and size of samples to load. * * We should use a progress dialog for this. */ for( i = 1; i < SOUND_MAX; i++ ) { if( sample_count[i] > SAMPLE_MAX ) sample_count[i] = SAMPLE_MAX; for( j = 0; j < sample_count[i]; j++ ) { short movieResFile; char soundpath[1024]; char filepath[1024]; Str255 pFilePath; FSSpec theFile; /*path_make( soundpath, ANGBAND_DIR_XTRA, "sound");*/ /*path_make( filepath, soundpath, sample_name[i][j]);*/ /*path_make( filepath, ANGBAND_XTRA_SOUND, sample_name[i][j] );*/ strnfmt( filepath, 1024, ":lib:xtra:sound:%s", sample_name[i][j] ); c2p_stringcopy( pFilePath, filepath ); err = FSMakeFSSpec( app_vol, app_dir, pFilePath, &theFile ); if( err == noErr ) { err = OpenMovieFile( &theFile, &movieResFile, fsRdPerm ); if( err == noErr ) { short movieResID = 0; Str255 movieName; Boolean wasChanged; Movie theMovie; err = NewMovieFromFile( &theMovie, movieResFile, &movieResID, movieName, newMovieActive, &wasChanged ); if( err == noErr ) { Track myTrack = NULL; myTrack = GetMovieIndTrackType( theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly ); if( myTrack != NULL ) { sample[i][j] = NewHandle(0); if( sample[i][j] != NULL ) { /*SetMovieProgressProc(theMovie, (MovieProgressUPP) -1L, 0 );*/ err = PutMovieIntoTypedHandle( theMovie, myTrack, soundListRsrc, sample[i][j], 0, GetTrackDuration(myTrack), 0L, NULL ); } } } CloseMovieFile( movieResFile ); DisposeMovie( theMovie ); } } if( err != noErr ) { sample[i][j] = (Handle)NULL; } } } /* Stop Loading Sounds */ } /* * close_sounds * * Call this when quitting the application. */ static void close_sounds( void ) { ExitMovies(); } /* * play_sound * * Play an asynchronous sound */ static void play_sound( int sound_number, int sound_volume, bool async ) { if( sound_number > 0 && sound_number < SOUND_MAX ) { if( sample_count[sound_number] > 0 ) { int random_sample = randint(sample_count[sound_number]) - 1; if (sample[sound_number][random_sample]) { /* Lock */ HLock(sample[sound_number][random_sample]); { long kk; OSErr myErr = noErr; if( !channelInit ) { for( kk=0; kk < kMaxChannels; kk++ ) { /* Create sound channel for all sounds to play from */ SndNewChannel( &mySndChannel[kk], sampledSynth, initMono, 0L ); } channelInit = 1; } if( sample[sound_number][random_sample] != nil ) { SCStatus status; long found; for( kk=0,found=0; kk < kMaxChannels && !found; kk++ ) { myErr = SndChannelStatus( mySndChannel[kk], sizeof(SCStatus), &status ); if( myErr == noErr && !status.scChannelBusy ) { SndCommand theVolumeCommand; theVolumeCommand.cmd = volumeCmd; theVolumeCommand.param1 = 0; theVolumeCommand.param2 = ((short)(sound_volume)<<4) | ((short)(sound_volume)); /* Set up volume for channel */ SndDoImmediate( mySndChannel[kk], &theVolumeCommand ); /* Play new sound ansynchronously */ SndPlay( mySndChannel[kk], (SndListHandle)sample[sound_number][random_sample], async ); found = 1; } } if( !found ) { SndCommand theQuietCommand; SndCommand theVolumeCommand; theQuietCommand.cmd = quietCmd; theQuietCommand.param1 = 0; theQuietCommand.param2 = 0; theVolumeCommand.cmd = volumeCmd; theVolumeCommand.param1 = 0; theVolumeCommand.param2 = ((short)(sound_volume)<<4) | ((short)(sound_volume)); /* Lets grab the first sound channel and play now */ /* I'm sure a better algorithm could be used here. */ SndDoImmediate( mySndChannel[0], &theQuietCommand ); SndDoImmediate( mySndChannel[0], &theVolumeCommand ); SndPlay( mySndChannel[0], (SndListHandle)sample[sound_number][random_sample], async ); } } } /* Unlock */ HUnlock(sample[sound_number][random_sample]); } } } } /*** Some generic functions ***/ #ifdef ANGBAND_LITE_MAC /* * Hack -- activate a color (0 to 255) */ #define term_data_color(TD,A) /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* * Hack -- activate a color (0 to 255) */ static void term_data_color(term_data *td, int a) { /* Activate the color */ /*if (td->last != a) */ { u16b rv, gv, bv; RGBColor color; /* Extract the R,G,B data */ rv = angband_color_table[a][1]; gv = angband_color_table[a][2]; bv = angband_color_table[a][3]; /* Set the color */ color.red = (rv | (rv << 8)); color.green = (gv | (gv << 8)); color.blue = (bv | (bv << 8)); /* Activate the color */ RGBForeColor(&color); /* Memorize color */ td->last = a; } } #endif /* ANGBAND_LITE_MAC */ /* * Hack -- Apply and Verify the "font" info * * This should usually be followed by "term_data_check_size()" */ static void term_data_check_font(term_data *td) { int i; FontInfo info; WindowPtr old = active; /* Activate */ activate(td->w); /* Instantiate font */ TextFont(td->font_id); TextSize(td->font_size); TextFace(td->font_face); /* Extract the font info */ GetFontInfo(&info); /* Assume monospaced */ td->font_mono = TRUE; /* Extract the font sizing values XXX XXX XXX */ td->font_wid = CharWidth('@'); /* info.widMax; */ td->font_hgt = info.ascent + info.descent; td->font_o_x = 0; td->font_o_y = info.ascent; /* Check important characters */ for (i = 33; i < 127; i++) { /* Hack -- notice non-mono-space */ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE; /* Hack -- collect largest width */ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i); } /* Set default offsets */ td->tile_o_x = td->font_o_x; td->tile_o_y = td->font_o_y; /* Set default tile size */ td->tile_wid = td->font_wid; td->tile_hgt = td->font_hgt; /* Re-activate the old window */ activate(old); } /* * Hack -- Apply and Verify the "size" info */ static void term_data_check_size(term_data *td) { BitMap screen; #ifdef TARGET_CARBON GetQDGlobalsScreenBits( &screen ); #else screen = qd.screenBits; #endif /* Minimal window size */ if (td->cols < 1) td->cols = 1; if (td->rows < 1) td->rows = 1; /* Minimal tile size */ if (td->tile_wid < 4) td->tile_wid = 4; if (td->tile_hgt < 4) td->tile_hgt = 4; /* Default tile offsets */ td->tile_o_x = (td->tile_wid - td->font_wid) / 2; td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2; /* Minimal tile offsets */ if (td->tile_o_x < 0) td->tile_o_x = 0; if (td->tile_o_y < 0) td->tile_o_y = 0; /* Apply font offsets */ td->tile_o_x += td->font_o_x; td->tile_o_y += td->font_o_y; /* Calculate full window size */ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2; td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2; /* verify the window is within the currently available screens */ { GDHandle gdNthDevice; Boolean inside = false; gdNthDevice = GetDeviceList(); while( gdNthDevice != nil ) { if( TestDeviceAttribute( gdNthDevice, screenDevice ) ) { if( TestDeviceAttribute( gdNthDevice, screenActive ) ) { Point topleft; Boolean cornerIn = false; topleft.h = td->r.left; topleft.v = td->r.top; cornerIn = PtInRect( topleft, &(*gdNthDevice)->gdRect ); if( cornerIn ) { inside = true; break; } } } gdNthDevice = GetNextDevice( gdNthDevice ); } if( !inside ) { /* Verify the top */ if (td->r.top > screen.bounds.bottom - td->size_hgt) { td->r.top = screen.bounds.bottom - td->size_hgt; } /* Verify the top */ if (td->r.top < screen.bounds.top + GetMBarHeight()) { td->r.top = screen.bounds.top + GetMBarHeight(); } /* Verify the left */ if (td->r.left > screen.bounds.right - td->size_wid) { td->r.left = screen.bounds.right - td->size_wid; } /* Verify the left */ if (td->r.left < screen.bounds.left) { td->r.left = screen.bounds.left; } } /* Calculate bottom right corner */ td->r.right = td->r.left + td->size_wid; td->r.bottom = td->r.top + td->size_hgt; } /* Assume no graphics */ td->t->always_pict = FALSE; #ifdef ANGBAND_LITE_MAC /* No graphics */ #else /* ANGBAND_LITE_MAC */ /* Handle graphics */ if (use_graphics && ((td == &data[0]) || (td == &data[6]))) { td->t->always_pict = TRUE; } #endif /* ANGBAND_LITE_MAC */ /* Fake mono-space */ if (!td->font_mono || (td->font_wid != td->tile_wid) || (td->font_hgt != td->tile_hgt)) { /* Handle fake monospace */ td->t->always_pict = TRUE; } } /* * Hack -- resize a term_data * * This should normally be followed by "term_data_resize()" */ static void term_data_resize(term_data *td) { /* Actually resize the window */ SizeWindow(td->w, td->size_wid, td->size_hgt, 0); } /* * Hack -- redraw a term_data */ static void term_data_redraw(term_data *td) { /* We want to make a multi-threaded redraw implementation for MacOSX! So, lets use our locally defined thread routines to take use of multi-cpu machines. */ term *old = Term; /* Activate the term */ Term_activate(td->t); /* Redraw the contents */ Term_redraw(); /* Flush the output */ Term_fresh(); /* Restore the old term */ Term_activate(old); /* No need to redraw */ #ifdef TARGET_CARBON { RgnHandle theRgn = NewRgn(); GetWindowRegion( td->w, kWindowContentRgn, theRgn ); ValidWindowRgn( (WindowRef)(td->w), theRgn ); DisposeRgn( theRgn ); } #else ValidRect( &(td->w->portRect) ); #endif } #ifdef ANGBAND_LITE_MAC /* No graphics */ #else /* ANGBAND_LITE_MAC */ /* * Lock a frame */ static void BenSWLockFrame(FrameRec *srcFrameP) { PixMapHandle pixMapH; pixMapH = GetGWorldPixMap(srcFrameP->framePort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->framePixHndl = pixMapH; srcFrameP->framePix = (PixMapPtr)*(Handle)pixMapH; pixMapH = GetGWorldPixMap(srcFrameP->bufferPort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->bufferPixHndl = pixMapH; srcFrameP->bufferPix = (PixMapPtr)*(Handle)pixMapH; } /* * Unlock a frame */ static void BenSWUnlockFrame(FrameRec *srcFrameP) { if (srcFrameP->framePort != NULL) { HUnlock((Handle)srcFrameP->framePixHndl); UnlockPixels(srcFrameP->framePixHndl); } srcFrameP->framePix = NULL; if (srcFrameP->bufferPort != NULL) { HUnlock((Handle)srcFrameP->bufferPixHndl); UnlockPixels(srcFrameP->bufferPixHndl); } srcFrameP->bufferPix = NULL; } static OSErr BenSWCreateGWorldFromPict( GWorldPtr *pictGWorld, GWorldPtr *bufferGWorld, PicHandle pictH, term_data *td ) { OSErr err; GWorldPtr saveGWorld; GDHandle saveGDevice; GWorldPtr tempGWorld; Rect pictRect; short depth; GDHandle theGDH; { tempGWorld = NULL; /* Reset */ *pictGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /*depth = 8; */ /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect = (**pictH).picFrame; OffsetRect(&pictRect, -pictRect.left, -pictRect.top); /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Success */ if (err != noErr) { return (err); } /* Save pointer */ *pictGWorld = tempGWorld; /* Save GWorld */ GetGWorld(&saveGWorld, &saveGDevice); /* Activate */ SetGWorld(tempGWorld, nil); /* Dump the pict into the GWorld */ (void)LockPixels(GetGWorldPixMap(tempGWorld)); EraseRect(&pictRect); DrawPicture(pictH, &pictRect); UnlockPixels(GetGWorldPixMap(tempGWorld)); /* Restore GWorld */ SetGWorld(saveGWorld, saveGDevice); } { tempGWorld = NULL; /* Reset */ *bufferGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /*depth = 8;*/ /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect.left = 0; pictRect.right = ((COL_MAP+1)+(66+1)) * 32; pictRect.top = 0; pictRect.bottom = td->tile_hgt; /*OffsetRect(&pictRect, -pictRect.left, -pictRect.top); */ /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Success */ if (err != noErr) { return (err); } /* Save pointer */ *bufferGWorld = tempGWorld; } /* Success */ return (0); } /* * Nuke the global "frameP" */ static errr globe_nuke(void) { /* Dispose */ if (frameP) { /* Unlock */ BenSWUnlockFrame(frameP); /* Dispose of the GWorld */ DisposeGWorld(frameP->framePort); DisposeGWorld(frameP->bufferPort); /* Dispose of the memory */ DisposePtr((Ptr)frameP); /* Forget */ frameP = NULL; } /* Flush events */ FlushEvents(everyEvent, 0); /* Success */ return (0); } /* * Init the global "frameP" */ static errr globe_init(term_data *td) { OSErr err; GWorldPtr tempPictGWorldP; GWorldPtr tempPictBufferGWorldP; PicHandle newPictH; /* If we already initialized, then dispose of the previous objects */ { globe_nuke(); } /* Use window XXX XXX XXX */ SetPort(GetWindowPort(data[0].w)); /* Get the pict resource */ switch( arg_graphics ) { case GRAPHICS_ORIGINAL: { Rect pictRect; char grafpath[1024]; char filepath[1024]; newPictH = get_picture_from_file( ANGBAND_XTRA_GRAF_8x8 ); gTileWidth = 8; gTileHeight = 8; pictRect = (**newPictH).picFrame; gTileCols = (pictRect.right - pictRect.left) / gTileWidth; gTileCols = (pictRect.bottom - pictRect.top) / gTileHeight; use_transparency = false; use_graphics = GRAPHICS_ORIGINAL; break; } case GRAPHICS_ADAM_BOLT: { Rect pictRect; char grafpath[1024]; char filepath[1024]; newPictH = get_picture_from_file( ANGBAND_XTRA_GRAF_16x16 ); gTileWidth = 16; gTileHeight = 16; pictRect = (**newPictH).picFrame; gTileCols = (pictRect.right - pictRect.left) / gTileWidth; gTileCols = (pictRect.bottom - pictRect.top) / gTileHeight; use_transparency = true; use_graphics = GRAPHICS_ADAM_BOLT; break; } case GRAPHICS_NONE: default: { use_graphics = GRAPHICS_NONE; use_transparency = false; } return 0; } /* Analyze result */ err = (newPictH ? 0 : -1); /* Oops */ if (err == noErr) { /* Create GWorld */ err = BenSWCreateGWorldFromPict( &tempPictGWorldP, &tempPictBufferGWorldP, newPictH, td ); /* Release resource */ ReleaseResource((Handle)newPictH); /* Error */ if (err == noErr) { /* Create the frame */ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec)); /* Analyze result */ err = (frameP ? 0 : -1); /* Oops */ if (err == noErr) { /* Save GWorld */ frameP->framePort = tempPictGWorldP; frameP->bufferPort = tempPictBufferGWorldP; /* Lock it */ BenSWLockFrame(frameP); } } } /* Result */ return (err); } #endif /* ANGBAND_LITE_MAC */ /*** Support for the "z-term.c" package ***/ /* * Initialize a new Term * * Note also the "window type" called "noGrowDocProc", which might be more * appropriate for the main "screen" window. * * Note the use of "srcCopy" mode for optimized screen writes. */ static void Term_init_mac(term *t) { term_data *td = (term_data*)(t->data); static RGBColor black = {0x0000,0x0000,0x0000}; static RGBColor white = {0xFFFF,0xFFFF,0xFFFF}; #ifdef ANGBAND_LITE_MAC /* Make the window */ td->w = NewWindow(0, &td->r, td->title, 0, noGrowDocProc, (WindowPtr)-1, 1, 0L); #else /* ANGBAND_LITE_MAC */ /* Make the window */ td->w = NewCWindow(0, &td->r, td->title, 0, documentProc, (WindowPtr)-1, 1, 0L); #endif /* ANGBAND_LITE_MAC */ /* Activate the window */ activate(td->w); /* Erase behind words */ TextMode(srcCopy); /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize the window */ term_data_resize(td); #ifdef ANGBAND_LITE_MAC /* Prepare the colors (base colors) */ BackColor(blackColor); ForeColor(whiteColor); #else /* ANGBAND_LITE_MAC */ /* Prepare the colors (real colors) */ RGBBackColor(&black); RGBForeColor(&white); /* Block */ { Rect globalRect; GDHandle mainGDH; GDHandle currentGDH; GWorldPtr windowGWorld; PixMapHandle basePixMap; /* Obtain the rect */ #ifdef TARGET_CARBON GetWindowBounds( (WindowRef)td->w, kWindowContentRgn, &globalRect ); #else globalRect = td->w->portRect; local_to_global( &globalRect ); #endif /* Obtain the proper GDH */ mainGDH = GetMaxDevice(&globalRect); /* Extract GWorld and GDH */ GetGWorld(&windowGWorld, ¤tGDH); /* Obtain base pixmap */ basePixMap = (**mainGDH).gdPMap; /* Save pixel depth */ td->pixelDepth = (**basePixMap).pixelSize; /* Save Window GWorld */ td->theGWorld = windowGWorld; /* Save Window GDH */ td->theGDH = currentGDH; /* Save main GDH */ td->mainSWGDH = mainGDH; } #endif /* ANGBAND_LITE_MAC */ { Rect portRect; #ifdef TARGET_CARBON GetWindowBounds( (WindowRef)td->w, kWindowContentRgn, &portRect ); global_to_local( &portRect ); #else portRect = td->w->portRect; #endif /* Clip to the window */ ClipRect(&portRect); /* Erase the window */ EraseRect(&portRect); /* Invalidate the window */ #ifdef TARGET_CARBON InvalWindowRect((WindowRef)(td->w), (const Rect *)(&portRect)); #else InvalRect( &portRect ); #endif /* Display the window if needed */ if (td->mapped) ShowWindow(td->w); /* Hack -- set "mapped" flag */ t->mapped_flag = td->mapped; /* Forget color */ td->last = -1; } } /* * Nuke an old Term */ static void Term_nuke_mac(term *t) { #pragma unused (t) /* XXX */ } /* * Unused */ static errr Term_user_mac(int n) { #pragma unused (n) /* Success */ return (0); } /* * React to changes */ static errr Term_xtra_mac_react(void) { term_data *td = (term_data*)(Term->data); static byte current_graphics = -1; /* Reset color */ td->last = -1; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Handle sound */ if (use_sound != arg_sound) { /* Apply request */ use_sound = arg_sound; } /* We are comparing a byte with a boolean, not good! */ /* Handle graphics */ if (((td == &data[0]) || (td == &data[6])) && (current_graphics != arg_graphics)) { /* Initialize graphics */ if (globe_init(td) != 0) { plog("Cannot initialize graphics!"); arg_graphics = GRAPHICS_NONE; use_graphics = GRAPHICS_NONE; } /* Apply request */ current_graphics = arg_graphics; use_graphics = current_graphics; /* Apply and Verify */ term_data_check_size(td); /* Resize the window */ term_data_resize(td); /* Reset visuals */ reset_visuals(); } #endif /* ANGBAND_LITE_MAC */ /* Success */ return (0); } /* * Do a "special thing" */ static errr Term_xtra_mac(int n, int v) { term_data *td = (term_data*)(Term->data); Rect r; /* Analyze */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: { /* Make a noise */ SysBeep(1); /* Success */ return (0); } #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Make a sound */ case TERM_XTRA_SOUND: { /* play sound effect */ short volume = ( gSoundVolume > 0 ? (SOUND_VOLUME_MAX*gSoundVolume)/100 : 0 ); play_sound( v, volume, true ); /* Success */ return (0); } #endif /* ANGBAND_LITE_MAC */ /* Process random events */ case TERM_XTRA_BORED: { /* Process an event */ (void)CheckEvents(FALSE); /* Success */ return (0); } /* Process pending events */ case TERM_XTRA_EVENT: { /* Process an event */ (void)CheckEvents(v); /* Success */ return (0); } /* Flush all pending events (if any) */ case TERM_XTRA_FLUSH: { /* Hack -- flush all events */ while (CheckEvents(FALSE)) /* loop */; /* Success */ return (0); } /* Hack -- Change the "soft level" */ case TERM_XTRA_LEVEL: { /* Activate if requested */ if (v) activate(td->w); /* Success */ return (0); } /* React to changes */ case TERM_XTRA_REACT: { /* React to changes */ return (Term_xtra_mac_react()); } /* Delay (milliseconds) */ case TERM_XTRA_DELAY: { /* If needed */ if (v > 0) { EventRecord tmp; UInt32 ticks; /* Convert milliseconds to ticks */ ticks = (v * 60L) / 1000; /* Hack - block for those ticks */ WaitNextEvent(~everyEvent, &tmp, ticks, nil); } /* Success */ return (0); } } /* Oops */ return (1); } /* * Low level graphics (Assumes valid input). * Draw a "cursor" at (x,y), using a "yellow box". * We are allowed to use "Term_grab()" to determine * the current screen contents (for inverting, etc). */ static errr Term_curs_mac(int x, int y) { Rect r; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, TERM_YELLOW); /* Frame the grid */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; FrameRect(&r); /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ static errr Term_wipe_mac(int x, int y, int n) { Rect r; term_data *td = (term_data*)(Term->data); /* Erase the block of characters */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + n * td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; EraseRect(&r); /* Success */ return (0); } /* * Low level graphics. Assumes valid input. * * Draw several ("n") chars, with an attr, at a given location. */ static errr Term_text_mac(int x, int y, int n, byte a, const char *cp) { int xp, yp; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, (a & 0x0F)); /* Starting pixel */ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1; yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ if (n == 1) DrawChar(*cp); /* Draw the string */ else DrawText(cp, 0, n); /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i; Rect r2; bool use_buffer = false; term_data *td = (term_data*)(Term->data); GDHandle saveGDevice; GWorldPtr saveGWorld; /* Save GWorld */ GetGWorld(&saveGWorld, &saveGDevice); if( n > 200 ) { /* Use the row buffer if we are drawing more than one cell in the row */ use_buffer = true; /* Destination rectangle */ r2.left = x * td->tile_wid + td->size_ow1; r2.right = r2.left + td->tile_wid; r2.top = 0; r2.bottom = r2.top + td->tile_hgt; /* We must change to the buffer port for output */ /* Activate */ SetGWorld(frameP->bufferPort, nil); /* Instantiate font */ TextFont(td->font_id); TextSize(td->font_size); TextFace(td->font_face); /* Restore colors */ BackColor(blackColor); ForeColor(whiteColor); #ifdef TARGET_CARBON { Rect portRect; GetPortBounds( frameP->bufferPort, &portRect ); EraseRect( &portRect ); } #else EraseRect( &td->w->portRect ); #endif } else { /* no buffering, so we use the normal current port */ use_buffer = false; /* Destination rectangle */ r2.left = x * td->tile_wid + td->size_ow1; r2.right = r2.left + td->tile_wid; r2.top = y * td->tile_hgt + td->size_oh1; r2.bottom = r2.top + td->tile_hgt; } /* Scan the input */ for (i = 0; i < n; i++) { bool done = FALSE; byte a = ap[i]; char c = cp[i]; byte ta = tap[i]; char tc = tcp[i]; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Graphics -- if Available and Needed */ if (use_graphics && ((td == &data[0]) || (td == &data[6])) && ((byte)a & 0x80) && ((byte)c & 0x80)) { int col, row; Rect r1; int terrain_col, terrain_row; Rect terrain_rect; /* Row and Col */ row = ((byte)a & 0x7F) % gTileRows; col = ((byte)c & 0x7F) % gTileCols; terrain_row = ((byte)ta & 0x7F) % gTileRows; terrain_col = ((byte)tc & 0x7F) % gTileCols; /* Source rectangle */ r1.left = col * gTileWidth; r1.top = row * gTileHeight; r1.right = r1.left + gTileWidth; r1.bottom = r1.top + gTileHeight; /* Hardwire CopyBits */ BackColor(whiteColor); ForeColor(blackColor); /* Draw the picture */ { int lock_pixels = 0; BitMapPtr srcBitMap = (BitMapPtr)(frameP->framePix); BitMapPtr destBitMap = 0L; #ifdef TARGET_CARBON if( use_buffer ) { destBitMap = (BitMapPtr)(frameP->bufferPix); } else { PixMapHandle bMap = 0L; LockPixels( GetPortPixMap(GetWindowPort(td->w)) ); bMap = GetPortPixMap(GetWindowPort(td->w)); destBitMap = *bMap; lock_pixels = 1; /*destBitMap = GetPortBitMapForCopyBits( (CGrafPtr)(td->w) ); */ } #else if( use_buffer ) { destBitMap = (BitMapPtr)(frameP->bufferPix); } else { destBitMap = (BitMapPtr)&(td->w->portBits); } #endif /* Terrain rectangle */ terrain_rect.left = terrain_col * gTileWidth; terrain_rect.top = terrain_row * gTileHeight; terrain_rect.right = terrain_rect.left + gTileWidth; terrain_rect.bottom = terrain_rect.top + gTileHeight; /* draw terrain */ CopyBits( srcBitMap, destBitMap, &terrain_rect, &r2, srcCopy, NULL ); /* draw transparent tile */ BackColor(blackColor); CopyBits( srcBitMap, destBitMap, &r1, &r2, transparent, NULL ); if( lock_pixels == 1 ) UnlockPixels( GetPortPixMap(GetWindowPort(td->w)) ); } /* Restore colors */ BackColor(blackColor); ForeColor(whiteColor); /* Forget color */ td->last = -1; /* Done */ done = TRUE; } #endif /* ANGBAND_LITE_MAC */ /* Normal */ if (!done) { int xp, yp; /* Erase */ EraseRect(&r2); /* Set the color */ term_data_color(td, (a & 0x0F)); /* Starting pixel */ xp = r2.left + td->tile_o_x; yp = r2.top + td->tile_o_y; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ DrawChar(c); } /* Advance */ r2.left += td->tile_wid; r2.right += td->tile_wid; } if( use_buffer ) { int lock_pixels = 0; Rect srcRect; Rect destRect; /* Now we blast the buffer pixmap onto the screen in the right place */ BitMapPtr srcBitMap = (BitMapPtr)(frameP->bufferPix); #ifdef TARGET_CARBON BitMapPtr destBitMap = 0L; PixMapHandle bMap = 0L; LockPixels( GetPortPixMap(GetWindowPort(td->w)) ); bMap = GetPortPixMap(GetWindowPort(td->w)); destBitMap = *bMap; lock_pixels = 1; /*BitMapPtr destBitMap = GetPortBitMapForCopyBits((CGrafPtr)(td->w));*/ #else BitMapPtr destBitMap = (BitMapPtr)&(td->w->portBits); #endif srcRect.left = x * td->tile_wid + td->size_ow1; srcRect.top = 0; srcRect.right = srcRect.left + (td->tile_wid * n); srcRect.bottom = td->tile_hgt; destRect.left = x * td->tile_wid + td->size_ow1; destRect.right = destRect.left + (td->tile_wid * n); destRect.top = y * td->tile_hgt + td->size_oh1; destRect.bottom = destRect.top + td->tile_hgt; /* Restore GWorld */ SetGWorld(saveGWorld, saveGDevice); /* Hardwire CopyBits */ BackColor(whiteColor); ForeColor(blackColor); CopyBits( srcBitMap, destBitMap, &srcRect, &destRect, srcCopy, NULL ); /* Restore colors */ BackColor(blackColor); ForeColor(whiteColor); if( lock_pixels == 1 ) UnlockPixels( GetPortPixMap(GetWindowPort(td->w)) ); } /* Success */ return (0); } /* * Create and initialize window number "i" */ static void term_data_link(int i) { term *old = Term; term_data *td = &data[i]; /* Only once */ if (td->t) return; /* Require mapped */ if (!td->mapped) return; /* Allocate */ MAKE(td->t, term); /* Initialize the term */ term_init(td->t, td->cols, td->rows, td->keys); /* Use a "software" cursor */ td->t->soft_cursor = TRUE; /* Erase with "black space" */ td->t->attr_blank = TERM_DARK; td->t->char_blank = ' '; /* Prepare the init/nuke hooks */ td->t->init_hook = Term_init_mac; td->t->nuke_hook = Term_nuke_mac; /* Prepare the function hooks */ td->t->user_hook = Term_user_mac; td->t->xtra_hook = Term_xtra_mac; td->t->wipe_hook = Term_wipe_mac; td->t->curs_hook = Term_curs_mac; td->t->text_hook = Term_text_mac; td->t->pict_hook = Term_pict_mac; /* Link the local structure */ td->t->data = (vptr)(td); /* Activate it */ Term_activate(td->t); /* Global pointer */ angband_term[i] = td->t; /* Activate old */ Term_activate(old); } /* * Set the "current working directory" (also known as the "default" * volume/directory) to the location of the current application. * * Code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl) * * This function does not appear to work correctly with System 6. */ static void SetupAppDir(void) { OSErr err = noErr; ProcessSerialNumber psn; ProcessInfoRec info; Str255 myName; FSSpec mySpec; char errString[255]; err = GetCurrentProcess( &psn ); if( err == noErr ) { info.processInfoLength = sizeof(ProcessInfoRec); info.processName = myName; info.processAppSpec = &mySpec; err = GetProcessInformation( &psn, &info ); if( err == noErr ) { app_vol = info.processAppSpec->vRefNum; app_dir = info.processAppSpec->parID; } } /* Set the current working directory to that location */ err = HSetVol(NULL, app_vol, app_dir); if (err != noErr) { strnfmt(errString, 255, "Fatal HSetVol Error #%d.\r Exiting.", err); mac_warning(errString); ExitToShell(); } } /* * Global "preference" file pointer */ static FILE *fff; /* * Read a "short" from the file */ static int getshort(void) { int x = 0; char buf[256]; if (0 == my_fgets(fff, buf, 256)) x = atoi(buf); return (x); } /* * Read a "byte" from the file */ static int getbyte(void) { byte x = 0; unsigned char buf[256]; if (0 == my_fgets(fff, (char*)buf, 256)) x = buf[0]; return (x); } /* * Dump a "short" to the file */ static void putshort(int x) { fprintf(fff, "%d\n", x); } /* * Dump a "byte" to the file */ static void putbyte(byte x) { fprintf(fff, "%c\n", x); } /* * Write the "preference" data to the current "file" */ static void save_prefs(void) { int i; term_data *td; /*** The current version ***/ putshort(VER_MAJOR); putshort(VER_MINOR); putshort(VER_PATCH); putshort(VER_EXTRA); /* Dump */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access */ td = &data[i]; putshort(td->mapped); putshort(td->font_id); putshort(td->font_size); putshort(td->font_face); putshort(td->cols); putshort(td->rows); putshort(td->r.left); putshort(td->r.top); } putshort(arg_graphics); putshort(arg_sound); } /* * Load the preferences from the current "file" * * XXX XXX XXX Being able to undefine various windows is * slightly bizarre, and may cause problems. */ static void load_prefs(void) { int i; int old_major, old_minor, old_patch, old_extra; term_data *td; /*** Version information ***/ /* Preferences version */ old_major = getshort(); old_minor = getshort(); old_patch = getshort(); old_extra = getshort(); /* Hack -- Verify or ignore */ if ((old_major != VER_MAJOR) || (old_minor != VER_MINOR) || (old_patch != VER_PATCH) || (old_extra != VER_EXTRA)) { /* Message */ mac_warning("Ignoring old preferences."); /* Ignore */ return; } /* Windows */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access */ td = &data[i]; td->mapped = getshort(); td->font_id = getshort(); td->font_size = getshort(); td->font_face = getshort(); td->cols = getshort(); td->rows = getshort(); td->r.left = getshort(); td->r.top = getshort(); /* Done */ if (feof(fff)) break; } arg_graphics = getshort(); arg_sound = getshort(); } /* * Hack -- default data for a window */ static void term_data_hack(term_data *td) { short fid; #ifdef TARGET_CARBON /* Default to Monaco font */ fid = FMGetFontFamilyFromName( "\pmonaco" ); #else GetFNum("\pmonaco", &fid); #endif /* Wipe it */ WIPE(td, term_data); /* No color */ td->last = -1; /* Default borders */ td->size_ow1 = 2; td->size_ow2 = 2; td->size_oh2 = 2; /* Start hidden */ td->mapped = FALSE; /* Default font */ td->font_id = fid; /* Default font size */ td->font_size = 12; /* Default font face */ td->font_face = 0; /* Default size */ td->rows = 24; td->cols = 80; /* Default position */ td->r.left = 10; td->r.top = 40; /* Minimal keys */ td->keys = 16; } /* * Read the preference file, Create the windows. * * We attempt to use "FindFolder()" to track down the preference file, * but if this fails, for any reason, we will try the "SysEnvirons()" * method, which may work better with System 6. */ static void init_windows(void) { int i, b = 0; term_data *td; bool oops; /*** Default values ***/ /* Initialize (backwards) */ for (i = MAX_TERM_DATA - 1; i >= 0; i--) { int n; cptr s; /* Obtain */ td = &data[i]; /* Defaults */ term_data_hack(td); /* Obtain title */ s = angband_term_name[i]; /* Get length */ n = strlen(s); /* Maximal length */ if (n > 15) n = 15; /* Copy the title */ strncpy((char*)(td->title) + 1, s, n); /* Save the length */ td->title[0] = n; /* Tile the windows */ td->r.left += (b * 30); td->r.top += (b * 30); /* Tile */ b++; } /*** Load preferences ***/ load_pref_file(); /*** Instantiate ***/ /* Main window */ td = &data[0]; /* Many keys */ td->keys = 1024; /* Start visible */ td->mapped = TRUE; /* Link (backwards, for stacking order) */ for (i = MAX_TERM_DATA - 1; i >= 0; i--) { term_data_link(i); } /* Main window */ td = &data[0]; /* Main window */ Term_activate(td->t); } /* * Exit the program */ static void save_prefs_carbon( void ) { /* Creating a plist prefs file using the new Core Preferences Services */ short theShortValue = 0; /* Version info */ { CFStringRef prefVersionMajorKey = CFSTR( "version.major" ); CFStringRef prefVersionMinorKey = CFSTR( "version.minor" ); CFStringRef prefVersionPatchKey = CFSTR( "version.patch" ); CFStringRef prefVersionExtraKey = CFSTR( "version.extra" ); CFNumberRef prefVersionMajorValue; CFNumberRef prefVersionMinorValue; CFNumberRef prefVersionPatchValue; CFNumberRef prefVersionExtraValue; theShortValue = VER_MAJOR; prefVersionMajorValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = VER_MINOR; prefVersionMinorValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = VER_PATCH; prefVersionPatchValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = VER_EXTRA; prefVersionExtraValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); CFPreferencesSetAppValue( prefVersionMajorKey, prefVersionMajorValue, kCFPreferencesCurrentApplication ); CFPreferencesSetAppValue( prefVersionMinorKey, prefVersionMinorValue, kCFPreferencesCurrentApplication ); CFPreferencesSetAppValue( prefVersionPatchKey, prefVersionPatchValue, kCFPreferencesCurrentApplication ); CFPreferencesSetAppValue( prefVersionExtraKey, prefVersionExtraValue, kCFPreferencesCurrentApplication ); /* Cleanup CoreFoundation property list value refs */ #if 0 CFRelease( prefVersionMajorKey ); CFRelease( prefVersionMinorKey ); CFRelease( prefVersionPatchKey ); CFRelease( prefVersionExtraKey ); CFRelease( prefVersionMajorValue ); CFRelease( prefVersionMinorValue ); CFRelease( prefVersionPatchValue ); CFRelease( prefVersionExtraValue ); #endif /* 0 */ } /* Misc info */ { CFStringRef prefGraphicsKey = CFSTR( "arg.graphics" ); CFStringRef prefSoundKey = CFSTR( "arg.sound" ); CFNumberRef prefGraphicsValue; CFNumberRef prefSoundValue; theShortValue = arg_graphics; prefGraphicsValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = arg_sound; prefSoundValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); CFPreferencesSetAppValue( prefGraphicsKey, prefGraphicsValue, kCFPreferencesCurrentApplication ); CFPreferencesSetAppValue( prefSoundKey, prefSoundValue, kCFPreferencesCurrentApplication ); /* Cleanup CoreFoundation property list value refs */ #if 0 CFRelease( prefGraphicsKey ); CFRelease( prefSoundKey ); CFRelease( prefGraphicsValue ); CFRelease( prefSoundValue ); #endif /* 0 */ } /* store term/window prefs */ { /* Create an array collection */ int i; CFMutableArrayRef theTermArray = CFArrayCreateMutable( NULL, (CFIndex)MAX_TERM_DATA, &kCFTypeArrayCallBacks ); for( i=0; i < MAX_TERM_DATA; i++ ) { CFMutableDictionaryRef myDictionary = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); CFStringRef prefTermMappedKey = CFSTR( "term.mapped" ); CFStringRef prefTermFontIDKey = CFSTR( "term.font_id" ); CFStringRef prefTermFontSizeKey = CFSTR( "term.font_size" ); CFStringRef prefTermFontFaceKey = CFSTR( "term.font_face" ); CFStringRef prefTermColsKey = CFSTR( "term.cols" ); CFStringRef prefTermRowsKey = CFSTR( "term.rows" ); CFStringRef prefTermLeftKey = CFSTR( "term.left" ); CFStringRef prefTermTopKey = CFSTR( "term.top" ); CFNumberRef prefTermMappedValue; CFNumberRef prefTermFontIDValue; CFNumberRef prefTermFontSizeValue; CFNumberRef prefTermFontFaceValue; CFNumberRef prefTermColsValue; CFNumberRef prefTermRowsValue; CFNumberRef prefTermLeftValue; CFNumberRef prefTermTopValue; /* Access */ term_data *td = &data[i]; theShortValue = td->mapped; prefTermMappedValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->font_id; prefTermFontIDValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->font_size; prefTermFontSizeValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->font_face; prefTermFontFaceValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->cols; prefTermColsValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->rows; prefTermRowsValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->r.left; prefTermLeftValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); theShortValue = td->r.top; prefTermTopValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberShortType, &theShortValue ); CFDictionaryAddValue( myDictionary, prefTermMappedKey, prefTermMappedValue ); CFDictionaryAddValue( myDictionary, prefTermFontIDKey, prefTermFontIDValue ); CFDictionaryAddValue( myDictionary, prefTermFontSizeKey, prefTermFontSizeValue ); CFDictionaryAddValue( myDictionary, prefTermFontFaceKey, prefTermFontFaceValue ); CFDictionaryAddValue( myDictionary, prefTermColsKey, prefTermColsValue ); CFDictionaryAddValue( myDictionary, prefTermRowsKey, prefTermRowsValue ); CFDictionaryAddValue( myDictionary, prefTermLeftKey, prefTermLeftValue ); CFDictionaryAddValue( myDictionary, prefTermTopKey, prefTermTopValue ); CFArrayAppendValue( theTermArray, (void *)myDictionary ); } CFPreferencesSetAppValue( CFSTR("terms"), theTermArray, kCFPreferencesCurrentApplication ); /* Cleanup CoreFoundation property list value refs */ /* CFRelease( theTermArray ); */ } CFPreferencesAppSynchronize( kCFPreferencesCurrentApplication ); } static void load_prefs_carbon( void ) { /* Load Version Info */ CFNumberRef prefVersionMajorValue = CFPreferencesCopyAppValue( CFSTR("version.major"), kCFPreferencesCurrentApplication ); CFNumberRef prefVersionMinorValue = CFPreferencesCopyAppValue( CFSTR("version.minor"), kCFPreferencesCurrentApplication ); CFNumberRef prefVersionPatchValue = CFPreferencesCopyAppValue( CFSTR("version.patch"), kCFPreferencesCurrentApplication ); CFNumberRef prefVersionExtraValue = CFPreferencesCopyAppValue( CFSTR("version.extra"), kCFPreferencesCurrentApplication ); /* Load Misc Info */ CFNumberRef prefGraphicsValue = CFPreferencesCopyAppValue( CFSTR("arg.graphics"), kCFPreferencesCurrentApplication ); CFNumberRef prefSoundValue = CFPreferencesCopyAppValue( CFSTR("arg.sound"), kCFPreferencesCurrentApplication ); /* Load Term/Window Info */ CFArrayRef prefTermArray = CFPreferencesCopyAppValue( CFSTR("terms"), kCFPreferencesCurrentApplication ); short old_major, old_minor, old_patch, old_extra; short old_graphics, old_sound; int i; if( prefVersionMajorValue == NULL || prefVersionMinorValue == NULL || prefVersionPatchValue == NULL || prefVersionExtraValue == NULL ) { /* Preferences have not been saved yet */ mac_warning("Preferences have not been found."); /* Ignore */ return; } CFNumberGetValue( prefVersionMajorValue, kCFNumberShortType, &old_major ); CFNumberGetValue( prefVersionMinorValue, kCFNumberShortType, &old_minor ); CFNumberGetValue( prefVersionPatchValue, kCFNumberShortType, &old_patch ); CFNumberGetValue( prefVersionExtraValue, kCFNumberShortType, &old_extra ); /* Hack -- Verify or ignore */ if ((old_major != VER_MAJOR) || (old_minor != VER_MINOR) || (old_patch != VER_PATCH) || (old_extra != VER_EXTRA)) { /* Message */ mac_warning("Ignoring old preferences."); /* Ignore */ return; } CFNumberGetValue( prefGraphicsValue, kCFNumberShortType, &old_graphics ); CFNumberGetValue( prefSoundValue, kCFNumberShortType, &old_sound ); /* Windows */ for (i = 0; i < MAX_TERM_DATA; i++) { CFDictionaryRef termDictionary = CFArrayGetValueAtIndex( prefTermArray, i ); CFNumberRef prefTermMappedValue = CFDictionaryGetValue( termDictionary, CFSTR("term.mapped" ) ); CFNumberRef prefTermFontIDValue = CFDictionaryGetValue( termDictionary, CFSTR("term.font_id" ) ); CFNumberRef prefTermFontSizeValue = CFDictionaryGetValue( termDictionary, CFSTR("term.font_size" ) ); CFNumberRef prefTermFontFaceValue = CFDictionaryGetValue( termDictionary, CFSTR("term.font_face" ) ); CFNumberRef prefTermColsValue = CFDictionaryGetValue( termDictionary, CFSTR("term.cols" ) ); CFNumberRef prefTermRowsValue = CFDictionaryGetValue( termDictionary, CFSTR("term.rows" ) ); CFNumberRef prefTermLeftValue = CFDictionaryGetValue( termDictionary, CFSTR("term.left" ) ); CFNumberRef prefTermTopValue = CFDictionaryGetValue( termDictionary, CFSTR("term.top" ) ); short old_mapped, old_font_id, old_font_size, old_font_face, old_cols, old_rows, old_left, old_top; /* Access */ term_data *td = &data[i]; CFNumberGetValue( prefTermMappedValue, kCFNumberShortType, &old_mapped ); CFNumberGetValue( prefTermFontIDValue, kCFNumberShortType, &old_font_id ); CFNumberGetValue( prefTermFontSizeValue, kCFNumberShortType, &old_font_size ); CFNumberGetValue( prefTermFontFaceValue, kCFNumberShortType, &old_font_face ); CFNumberGetValue( prefTermColsValue, kCFNumberShortType, &old_cols ); CFNumberGetValue( prefTermRowsValue, kCFNumberShortType, &old_rows ); CFNumberGetValue( prefTermLeftValue, kCFNumberShortType, &old_left ); CFNumberGetValue( prefTermTopValue, kCFNumberShortType, &old_top ); td->mapped = old_mapped; td->font_id = old_font_id; td->font_size = old_font_size; td->font_face = old_font_face; td->cols = old_cols; td->rows = old_rows; td->r.left = old_left; td->r.top = old_top; /* Cleanup CoreFoundation property list value refs */ /* CFRelease( termDictionary ); */ } arg_graphics = old_graphics; arg_sound = old_sound; /* Cleanup CoreFoundation property list value refs */ #if 0 CFRelease( prefVersionMajorValue ); CFRelease( prefVersionMinorValue ); CFRelease( prefVersionPatchValue ); CFRelease( prefVersionExtraValue ); CFRelease( prefGraphicsValue ); CFRelease( prefSoundValue ); CFRelease( prefTermArray ); #endif /* 0 */ } static void save_pref_file(void) { #ifdef TARGET_CARBON save_prefs_carbon(); #else bool oops; /* Assume failure */ oops = TRUE; /* Assume failure */ fff = NULL; #if defined(MACINTOSH) && !defined(applec) /* Text file */ _ftype = 'TEXT'; #endif #ifdef USE_SFL_CODE /* Block */ if (TRUE) { OSErr err; short vref; long dirID; char foo[128]; /* Find the folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &vref, &dirID); /* Success */ if (!err) { /* Extract a path name */ PathNameFromDirID(dirID, vref, (StringPtr)foo); /* Convert the string */ ptocstr((StringPtr)foo); /* Append the preference file name */ strcat(foo, "Angband Preferences"); /* Open the preference file */ fff = fopen(foo, "w"); /* Success */ oops = FALSE; } } #endif /* USE_SFL_CODE */ /* Save preferences */ if (fff) { /* Write the preferences */ save_prefs(); /* Close it */ my_fclose(fff); } #endif /* TARGET_CARBON */ } static void load_pref_file(void) { #ifdef TARGET_CARBON load_prefs_carbon(); #else /* Assume failure */ oops = TRUE; /* Assume failure */ fff = NULL; #ifdef USE_SFL_CODE /* Block */ if (TRUE) { OSErr err; short vref; long dirID; char foo[128]; /* Find the folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &vref, &dirID); /* Success */ if (err == noErr) { /* Extract a path name */ PathNameFromDirID(dirID, vref, (StringPtr)foo); /* Convert the string */ ptocstr((StringPtr)foo); /* Append the preference file name */ strcat(foo, "Angband Preferences"); /* Open the preference file */ fff = fopen(foo, "r"); if( fff ) { /* Success */ oops = FALSE; } else { oops = TRUE; } } } #endif /* USE_SFL_CODE */ /* Load preferences */ if (fff) { /* Load a real preference file */ load_prefs(); /* Close the file */ my_fclose(fff); } #endif /* TARGET_CARBON */ } /* * Handle menu: "File" + "New" */ static void do_menu_file_new(void) { /* Hack */ HiliteMenu(0); /* Game is in progress */ game_in_progress = 1; /* Flush input */ flush(); /* Play a game */ play_game(TRUE); /* Hack -- quit */ quit(NULL); } /* * Handle menu: "File" + "Open" */ static void do_menu_file_open(bool all) { int err; BitMap screen; /* Window location */ #ifdef TARGET_CARBON GetQDGlobalsScreenBits( &screen ); #else screen = qd.screenBits; #endif /* Allow "all" files */ if (all) { /* Get any file */ err = ChooseFile( (unsigned char*)savefile, NULL, 0 ); } /* Allow "save" files */ else { OSType types[1]; types[0] = 'SAVE'; err = ChooseFile( (unsigned char*)savefile, types, 1 ); } /* Allow cancel */ if (err != noErr) return; /* Hack */ HiliteMenu(0); /* Game is in progress */ game_in_progress = 1; /* Flush input */ flush(); /* Play a game */ play_game(FALSE); /* Hack -- quit */ quit(NULL); } /* * Handle the "open_when_ready" flag */ static void handle_open_when_ready(void) { /* Check the flag XXX XXX XXX make a function for this */ if (open_when_ready && initialized && !game_in_progress) { /* Forget */ open_when_ready = FALSE; /* Game is in progress */ game_in_progress = 1; /* Wait for it */ pause_line(23); /* Flush input */ flush(); /* Play a game */ play_game(FALSE); /* Quit */ quit(NULL); } } /* * Initialize the menus * * Verify menus 128, 129, 130 * Create menus 131, 132, 133, 134, 135, 136, 137 * * The standard menus are: * * Apple (128) = { About, -, ... } * File (129) = { New,Open,Import,Close,Save,-,Exit,Quit } * Edit (130) = { Undo, Cut, Copy, Paste, Delete } (?) * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... } * Size (132) = { ... } * Window (133) = { Angband, Mirror, Recall, Choice, * Term-4, Term-5, Term-6, Term-7 } * Special (134) = { arg_sound, arg_graphics->137, arg_music->138, -, * arg_fiddle, arg_wizard } * Tile Width (135) = {...} * Tile Height (136) = {...} * Graphics (137) = { Text Only, Original, Adam Bolt } */ static void init_menubar(void) { int i, n; Rect r; WindowPtr tmpw; MenuHandle menu_apple, menu_file, menu_edit, menu_font, menu_size, menu_window, menu_special, menu_tilewidth, menu_tileheight, menu_graphics, menu_music; { /* Get the "apple" menu */ menu_apple = GetMenu(128); /* Insert the menu */ InsertMenu(menu_apple, 0); /* Add the DA's to the "apple" menu */ AppendResMenu(menu_apple, 'DRVR'); } { /* Get the "File" menu */ /* menu_file = GetMenu(129); */ menu_file = NewMenu( 129, "\pFile" ); /* Add New */ AppendMenu( menu_file, "\pNew/N" ); /* Add Open */ AppendMenu( menu_file, "\pOpen/O" ); /* Add Import */ AppendMenu( menu_file, "\pImport/I" ); /* Add Close */ AppendMenu( menu_file, "\pClose/W" ); /* Add Save */ AppendMenu( menu_file, "\pSave/S" ); /* Add --- */ AppendMenu( menu_file, "\p-" ); /* Add Exit */ AppendMenu( menu_file, "\pExit/X" ); /* Add Quit */ AppendMenu( menu_file, "\pQuit/Q" ); /* Insert the menu */ InsertMenu(menu_file, 0); } { /* Get the "Edit" menu */ /* menu_edit = GetMenu(130); */ menu_edit = NewMenu( 130, "\pEdit" ); /* Add Undo */ AppendMenu( menu_edit, "\p(Undo/Z" ); /* Add --- */ AppendMenu( menu_edit, "\p-" ); /* Add Cut */ AppendMenu( menu_edit, "\p(Cut/X" ); /* Add Copy */ AppendMenu( menu_edit, "\p(Copy/C" ); /* Add Paste */ AppendMenu( menu_edit, "\p(Paste/V" ); /* Add Delete */ AppendMenu( menu_edit, "\p(Delete" ); /* Insert the menu */ InsertMenu(menu_edit, 0); } { /* Make the "Font" menu */ menu_font = NewMenu(131, "\pFont"); /* Insert the menu */ InsertMenu(menu_font, 0); /* Add "bold" */ AppendMenu(menu_font, "\pBold"); /* Add "wide" */ AppendMenu(menu_font, "\pWide"); /* Add a separator */ AppendMenu(menu_font, "\p-"); /* Fake window */ r.left = r.right = r.top = r.bottom = 0; /* Make the fake window */ tmpw = NewWindow(0, &r, "\p", false, documentProc, 0, 0, 0); /* Activate the "fake" window */ SetPort(GetWindowPort(tmpw)); /* Default mode */ TextMode( srcCopy ); /* Default size */ TextSize(12); /* Add the fonts to the menu */ AppendResMenu(menu_font, 'FONT'); /* Size of menu */ n = CountMenuItems(menu_font); /* Scan the menu */ for (i = n; i >= 4; i--) { Str255 tmpName; short fontNum; /* Acquire the font name */ /* GetMenuItemText(m, i, tmpName); */ GetMenuItemText(menu_font, i, tmpName); /* Acquire the font index */ GetFNum(tmpName, &fontNum); /* Apply the font index */ TextFont(fontNum); /* Remove non-mono-spaced fonts */ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0)) { /* Delete the menu item XXX XXX XXX */ /* DeleteMenuItem(m, i); */ DeleteMenuItem(menu_font, i); } } /* Destroy the old window */ DisposeWindow(tmpw); /* Add a separator */ AppendMenu(menu_font, "\p-"); /* Add the fonts to the menu */ AppendResMenu(menu_font, 'FONT'); } { /* Make the "Size" menu */ menu_size = NewMenu(132, "\pSize"); /* Insert the menu */ InsertMenu(menu_size, 0); /* Add some sizes (stagger choices) */ for (i = 8; i <= 32; i += ((i / 16) + 1)) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(menu_size, buf); } } { /* Make the "Windows" menu */ menu_window = NewMenu(133, "\pWindows"); /* Insert the menu */ InsertMenu(menu_window, 0); /* Default choices */ for (i = 0; i < MAX_TERM_DATA; i++) { Str15 buf; /* Describe the item */ strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(menu_window, buf); /* Command-Key shortcuts */ if (i < 8) SetItemCmd(menu_window, i + 1, '0' + i); } } { /* Make the "Special" menu */ menu_special = NewMenu(134, "\pSpecial"); /* Insert the menu */ InsertMenu(menu_special, 0); /* Append the choices */ AppendMenu(menu_special, "\parg_sound"); AppendMenu(menu_special, "\parg_graphics"); AppendMenu(menu_special, "\parg_muisic"); AppendMenu(menu_special, "\p-"); AppendMenu(menu_special, "\parg_fiddle"); AppendMenu(menu_special, "\parg_wizard"); } { /* Make the "TileWidth" menu */ menu_tilewidth = NewMenu(135, "\pTileWidth"); /* Insert the menu */ InsertMenu(menu_tilewidth, 0); /* Add some sizes */ for (i = 4; i <= 32; i++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(menu_tilewidth, buf); } } { /* Make the "TileHeight" menu */ menu_tileheight = NewMenu(136, "\pTileHeight"); /* Insert the menu */ InsertMenu(menu_tileheight, 255); /* Add some sizes */ for (i = 4; i <= 32; i++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(menu_tileheight, buf); } } { /* Make the "Graphics" menu */ menu_graphics = NewMenu(137, "\parg_graphics"); /* Append the choices */ AppendMenu(menu_graphics, "\pText Only" ); AppendMenu(menu_graphics, "\pOrignial" ); AppendMenu(menu_graphics, "\pAdam Bolt" ); /* Insert SubMenu */ InsertMenu( menu_graphics, -1 ); /* This call is only support with AppearanceLib 1.0 or greater */ SetMenuItemHierarchicalID( menu_special, 2, 137 ); } { short i; /* Make the "Music" menu */ menu_music = NewMenu(138, "\parg_music"); /* Append the choices */ for( i=0; i < SONG_MAX; i++ ) { if( !streq(song_description[i], "" ) ) { Str255 menu_title; c2p_stringcopy( menu_title, song_description[i] ); AppendMenu( menu_music, menu_title ); } else { AppendMenu( menu_music, "\pEmpty" ); } } /* Insert SubMenu */ InsertMenu( menu_music, -1 ); /* This call is only support with AppearanceLib 1.0 or greater */ SetMenuItemHierarchicalID( menu_special, 3, 138 ); } /* Update the menu bar */ DrawMenuBar(); } /* * Prepare the menus */ static void setup_menus(void) { int i, n; short value; Str255 s; MenuHandle m, subm; term_data *td = NULL; /* Relevant "term_data" */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Unused */ if (!data[i].t) continue; /* Notice the matching window */ if (data[i].w == FrontWindow()) td = &data[i]; } /* File menu */ { m = GetMenuHandle(129); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Enable "new"/"open..."/"import..." */ if (initialized && !game_in_progress) { EnableMenuItem(m, 1); EnableMenuItem(m, 2); EnableMenuItem(m, 3); } /* Enable "close" */ if (initialized) { EnableMenuItem(m, 4); } /* Enable "save" */ if (initialized && character_generated) { EnableMenuItem(m, 5); } /* Enable "exit"/"quit" */ if (TRUE) { EnableMenuItem(m, 7); EnableMenuItem(m, 8); } } /* Edit menu */ { m = GetMenuHandle(130); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Enable "edit" options if "needed" */ if (!td) { EnableMenuItem(m, 1); EnableMenuItem(m, 3); EnableMenuItem(m, 4); EnableMenuItem(m, 5); EnableMenuItem(m, 6); } } /* Font menu */ { m = GetMenuHandle(131); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, 1, bold); */ /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, 2, extend); */ /* Active window */ if (td) { /* Enable "bold" */ EnableMenuItem(m, 1); /* Enable "extend" */ EnableMenuItem(m, 2); /* Check the appropriate "bold-ness" */ if (td->font_face & bold) CheckMenuItem(m, 1, TRUE); /* Check the appropriate "wide-ness" */ if (td->font_face & extend) CheckMenuItem(m, 2, TRUE); /* Analyze fonts */ for (i = 4; i <= n; i++) { /* Enable it */ EnableMenuItem(m, i); /* Analyze font */ GetMenuItemText(m,i,s); /*GetItem(m, i, s); */ GetFNum(s, &value); /* Check active font */ if (td->font_id == value) CheckMenuItem(m, i, TRUE); } } } /* Size menu */ { m = GetMenuHandle(132); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Active window */ if (td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m,i,s); /*GetItem(m, i, s); */ s[s[0]+1] = '\0'; value = atoi((char*)(s+1)); /* Enable the "real" sizes */ if (RealFont(td->font_id, value)) EnableMenuItem(m, i); /* Check the current size */ if (td->font_size == value) CheckMenuItem(m, i, TRUE); } } } /* Windows menu */ { m = GetMenuHandle(133); /* Get menu size */ n = CountMenuItems(m); /* Check active windows */ for (i = 1; i <= n; i++) { /* Check if needed */ CheckMenuItem(m, i, data[i-1].mapped); } } /* Special menu */ { m = GetMenuHandle(134); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Item "arg_sound" */ EnableMenuItem(m, 1); CheckMenuItem(m, 1, arg_sound); /* graphics state */ { /* Item "arg_graphics" */ EnableMenuItem(m, 2); subm = GetMenuHandle(137); /* Get menu size */ n = CountMenuItems(subm); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ EnableMenuItem(subm, i); CheckMenuItem(subm, i, FALSE); } switch( arg_graphics ) { case GRAPHICS_ORIGINAL: { EnableMenuItem(subm, 2); CheckMenuItem(subm, 2, true); } break; case GRAPHICS_ADAM_BOLT: { EnableMenuItem(subm, 3); CheckMenuItem(subm, 3, true); } break; case GRAPHICS_NONE: default: { EnableMenuItem(subm, 1); CheckMenuItem(subm, 1, true); } break; } } /* music state */ { /* Item arg_music */ EnableMenuItem(m, 3); subm = GetMenuHandle(138); /* Get menu size */ n = CountMenuItems(subm); /* add songs to menu */ for( i=1; i <= n; i++ ) { short song_index = i-1; if( !streq( song_name[song_index], "" ) ) { if( song_index == current_song ) { /* Reset */ EnableMenuItem(subm, i); CheckMenuItem(subm, i, TRUE); } else { /* Reset */ EnableMenuItem(subm, i); CheckMenuItem(subm, i, FALSE); } } else { /* Reset */ CheckMenuItem(subm, i, FALSE); DisableMenuItem(subm, i); } } } /* Item "arg_fiddle" */ EnableMenuItem(m, 5); CheckMenuItem(m, 5, arg_fiddle); /* Item "arg_wizard" */ EnableMenuItem(m, 6); CheckMenuItem(m, 6, arg_wizard); /* Item "Hack" */ /* EnableMenuItem(m, 9); */ } /* TileWidth menu */ { m = GetMenuHandle(135); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Active window */ if (td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m,i,s); /*GetItem(m, i, s); */ s[s[0]+1] = '\0'; value = atoi((char*)(s+1)); /* Enable */ EnableMenuItem(m, i); /* Check the current size */ if (td->tile_wid == value) CheckMenuItem(m, i, TRUE); } } } /* TileHeight menu */ { m = GetMenuHandle(136); /* Get menu size */ n = CountMenuItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableMenuItem(m, i); CheckMenuItem(m, i, FALSE); } /* Active window */ if (td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m,i,s); /*GetItem(m, i, s); */ s[s[0]+1] = '\0'; value = atoi((char*)(s+1)); /* Enable */ EnableMenuItem(m, i); /* Check the current size */ if (td->tile_hgt == value) CheckMenuItem(m, i, TRUE); } } } } /* * Process a menu selection (see above) * * Hack -- assume that invalid menu selections are disabled above, * which I have been informed may not be reliable. XXX XXX XXX */ static void menu(long mc) { int i; int menuid, selection; static unsigned char s[1000]; short fid; term_data *td = NULL; WindowPtr old_win; /* Analyze the menu command */ menuid = HiWord(mc); selection = LoWord(mc); /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == FrontWindow()) td = &data[i]; } /* Branch on the menu */ switch (menuid) { /* Apple Menu */ case 128: { /* About Angband... */ if (selection == 1) { DialogPtr dialog; Rect r; short item_hit; BitMap screen; #ifdef TARGET_CARBON GetQDGlobalsScreenBits( &screen ); #else screen = qd.screenBits; #endif dialog=GetNewDialog(128, 0, (WindowPtr)-1); #ifdef TARGET_CARBON GetWindowBounds( (WindowRef)dialog, kWindowContentRgn, &r ); #else r = dialog->portRect; local_to_global( &r ); #endif center_rect(&r, &screen.bounds); MacMoveWindow((WindowRef)dialog, r.left, r.top, 1); MacShowWindow((WindowRef)dialog); ModalDialog(0, &item_hit); DisposeDialog(dialog); break; } #ifdef TARGET_CARBON #else /* Desk accessory */ GetMenuItemText(GetMenuHandle(128),selection,s); /* GetMenuItem(GetMenuHandle(128), selection, s); */ OpenDeskAcc(s); #endif break; } /* File Menu */ case 129: { switch (selection) { /* New */ case 1: { do_menu_file_new(); break; } /* Open... */ case 2: { /* do_menu_file_open(FALSE); */ do_menu_file_open(TRUE); break; } /* Import... */ case 3: { do_menu_file_open(TRUE); break; } /* Close */ case 4: { /* No window */ if (!td) break; /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ HideWindow(td->w); break; } /* Save */ case 5: { /* Hack -- Forget messages */ msg_flag = FALSE; /* Hack -- Save the game */ do_cmd_save_game(FALSE); break; } /* Exit (without save) */ case 7: { /* Allow user to cancel "dangerous" exit */ if (game_in_progress && character_generated) { if( mac_confirm( "Quitting without save?", "The current player state will not be saved." ) == true ) { break; } } /* Quit */ quit(NULL); break; } /* Quit (with save) */ case 8: { /* Save the game (if necessary) */ if (game_in_progress && character_generated) { /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ do_cmd_save_game(FALSE); } /* Quit */ quit(NULL); break; } } break; } /* Edit menu */ case 130: { /* Unused */ break; } /* Font menu */ case 131: { /* Require a window */ if (!td) break; /* Memorize old */ old_win = active; /* Activate */ activate(td->w); /* Toggle the "bold" setting */ if (selection == 1) { /* Toggle the setting */ if (td->font_face & bold) { td->font_face &= ~bold; } else { td->font_face |= bold; } /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Toggle the "wide" setting */ if (selection == 2) { /* Toggle the setting */ if (td->font_face & extend) { td->font_face &= ~extend; } else { td->font_face |= extend; } /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Get a new font name */ GetMenuItemText(GetMenuHandle(131), selection, s); /* GetItem(GetMenuHandle(131), selection, s); */ GetFNum(s, &fid); /* Save the new font id */ td->font_id = fid; /* Current size is bad for new font */ if (!RealFont(td->font_id, td->font_size)) { /* Find similar size */ for (i = 1; i <= 32; i++) { /* Adjust smaller */ if (td->font_size - i >= 8) { if (RealFont(td->font_id, td->font_size - i)) { td->font_size -= i; break; } } /* Adjust larger */ if (td->font_size + i <= 128) { if (RealFont(td->font_id, td->font_size + i)) { td->font_size += i; break; } } } } /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore the window */ activate(old_win); break; } /* Size menu */ case 132: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(132), selection, s); /* GetItem(GetMenuHandle(132), selection, s); */ s[s[0]+1]=0; td->font_size = atoi((char*)(s+1)); /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* Window menu */ case 133: { /* Parse */ i = selection - 1; /* Check legality of choice */ if ((i < 0) || (i >= MAX_TERM_DATA)) break; /* Obtain the window */ td = &data[i]; /* Mapped */ td->mapped = TRUE; /* Link */ term_data_link(i); /* Mapped (?) */ td->t->mapped_flag = TRUE; /* Show the window */ ShowWindow(td->w); /* Bring to the front */ SelectWindow(td->w); break; } /* Special menu */ case 134: { switch (selection) { case 1: { /* Toggle arg_sound */ arg_sound = !arg_sound; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); break; } case 2: { /* Toggle arg_graphics */ /* arg_graphics = !arg_graphics; */ /* Hack -- Force redraw */ /* Term_key_push(KTRL('R')); */ break; } case 5: { arg_fiddle = !arg_fiddle; break; } case 6: { arg_wizard = !arg_wizard; break; } } break; } /* TileWidth menu */ case 135: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(135), selection, s); /* GetItem(GetMenuHandle(135), selection, s); */ s[s[0]+1]=0; td->tile_wid = atoi((char*)(s+1)); /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* TileHeight menu */ case 136: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(136), selection, s); /* GetItem(GetMenuHandle(136), selection, s); */ s[s[0]+1]=0; td->tile_hgt = atoi((char*)(s+1)); /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* Graphics menu */ case 137: { switch (selection) { /* Text Only */ case 1: { /* Toggle arg_sound */ arg_graphics = GRAPHICS_NONE; use_graphics = GRAPHICS_NONE; use_transparency = false; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } /* Original Graphics 8x8 tiles */ case 2: { /* Toggle arg_sound */ arg_graphics = GRAPHICS_ORIGINAL; use_graphics = GRAPHICS_ORIGINAL; use_transparency = false; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } /* Adam Bolt Graphics 16x16 tiles */ case 3: { /* Toggle arg_sound */ arg_graphics = GRAPHICS_ADAM_BOLT; use_graphics = GRAPHICS_ADAM_BOLT; use_transparency = true; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } } break; } /* Music menu */ case 138: { restart_music( selection - 1 ); } } /* Clean the menu */ HiliteMenu(0); } #ifdef USE_SFL_CODE /* * Check for extra required parameters -- From "Maarten Hazewinkel" */ static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent) { OSErr aeError; DescType returnedType; Size actualSize; aeError = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize); if (aeError == errAEDescNotFound) return (noErr); if (aeError == noErr) return (errAEParamMissed); return (aeError); } /* * Apple Event Handler -- Open Application */ static pascal OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply, UInt32 handlerRefCon) { #pragma unused(reply, handlerRefCon) return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Quit Application */ static pascal OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply, UInt32 handlerRefCon) { #pragma unused(reply, handlerRefCon) /* Quit later */ quit_when_ready = TRUE; /* Check arguments */ return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Print Documents */ static pascal OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply, UInt32 handlerRefCon) { #pragma unused(theAppleEvent, reply, handlerRefCon) return (errAEEventNotHandled); } /* * Apple Event Handler by Steve Linberg (slinberg@crocker.com). * * The old method of opening savefiles from the finder does not work * on the Power Macintosh, because CountAppFiles and GetAppFiles, * used to return information about the selected document files when * an application is launched, are part of the Segment Loader, which * is not present in the RISC OS due to the new memory architecture. * * The "correct" way to do this is with AppleEvents. The following * code is modeled on the "Getting Files Selected from the Finder" * snippet from Think Reference 2.0. (The prior sentence could read * "shamelessly swiped & hacked") */ static pascal OSErr AEH_Open(const AppleEvent *theAppleEvent, AppleEvent* reply, UInt32 handlerRefCon) { #pragma unused(reply, handlerRefCon) FSSpec myFSS; AEDescList docList; OSErr err; Size actualSize; AEKeyword keywd; DescType returnedType; char foo[128]; FInfo myFileInfo; /* Put the direct parameter (a descriptor list) into a docList */ err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList); if (err) return err; /* * We ignore the validity check, because we trust the FInder, and we only * allow one savefile to be opened, so we ignore the depth of the list. */ err = AEGetNthPtr(&docList, 1L, typeFSS, &keywd, &returnedType, (Ptr) &myFSS, sizeof(myFSS), &actualSize); if (err) return err; /* Only needed to check savefile type below */ err = FSpGetFInfo(&myFSS, &myFileInfo); if (err) { strnfmt(foo, 128, "Arg! FSpGetFInfo failed with code %d", err); mac_warning (foo); return err; } /* Ignore non 'SAVE' files */ if (myFileInfo.fdType != 'SAVE') return noErr; /* XXX XXX XXX Extract a file name */ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile); pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name); /* Convert the string */ ptocstr((StringPtr)savefile); /* Delay actual open */ open_when_ready = TRUE; /* Dispose */ err = AEDisposeDesc(&docList); /* Success */ return noErr; } #endif /* * Macintosh modifiers (event.modifier & ccc): * cmdKey, optionKey, shiftKey, alphaLock, controlKey * * * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra): * * Return:36 * Delete:51 * * Period:65 * Star:67 * Plus:69 * Clear:71 * Slash:75 * Enter:76 * Minus:78 * Equal:81 * 0-7:82-89 * 8-9:91-92 * * F5: 96 * F6: 97 * F7: 98 * F3:99 * F8:100 * F10:101 * F11:103 * F13:105 * F14:107 * F9:109 * F12:111 * F15:113 * Help:114 * Home:115 * PgUp:116 * Del:117 * F4: 118 * End:119 * F2:120 * PgDn:121 * F1:122 * Lt:123 * Rt:124 * Dn:125 * Up:126 */ /* * Optimize non-blocking calls to "CheckEvents()" * Idea from "Maarten Hazewinkel " */ #define EVENT_TICKS 6 /* * Check for Events, return TRUE if we process any * * Hack -- Handle AppleEvents if appropriate (ignore result code). */ static bool CheckEvents(bool wait) { EventRecord event; WindowPtr w; Rect r; long newsize; int ch, ck; int mc, ms, mo, mx; int i; term_data *td = NULL; UInt32 sleep_ticks; #ifndef TARGET_CARBON huge curTicks; static huge lastTicks = 0L; /* Access the clock */ curTicks = TickCount(); /* Hack -- Allow efficient checking for non-pending events */ if (!wait && (curTicks < lastTicks + EVENT_TICKS)) return (FALSE); /* Timestamp last check */ lastTicks = curTicks; /* Let the "system" run */ SystemTask(); if( use_sound ) { check_music(); } /* Blocking call to WaitNextEvent - Should use MAX_INT XXX XXX XXX */ if (wait) { sleep_ticks = 0x7FFFFFFFL; } else { /* Non-blocking call */ sleep_ticks = 0L; } /* Get an event (or null) */ WaitNextEvent(everyEvent, &event, sleep_ticks, nil); /* Hack -- Nothing is ready yet */ if (event.what == nullEvent) return (FALSE); /* Analyze the event */ switch (event.what) { #if 0 case activateEvt: { w = (WindowPtr)event.message; activate(w); break; } #endif case updateEvt: { /* Extract the window */ w = (WindowPtr)event.message; /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == w) td = &data[i]; } /* Hack XXX XXX XXX */ BeginUpdate(w); EndUpdate(w); /* Redraw the window */ if (td) term_data_redraw(td); break; } case keyDown: case autoKey: { /* Extract some modifiers */ mc = (event.modifiers & controlKey) ? TRUE : FALSE; ms = (event.modifiers & shiftKey) ? TRUE : FALSE; mo = (event.modifiers & optionKey) ? TRUE : FALSE; mx = (event.modifiers & cmdKey) ? TRUE : FALSE; /* Keypress: (only "valid" if ck < 96) */ ch = (event.message & charCodeMask) & 255; /* Keycode: see table above */ ck = ((event.message & keyCodeMask) >> 8) & 255; /* Command + "normal key" -> menu action */ if( mx ) { /* Handle a few special keyboard shortcuts */ if( ch == 44 ) { int inc = ( ms ? VOLUME_INC : 1 ); /* Adjust sound volume DOWN */ if( gSoundVolume > (VOLUME_MIN + inc) ) gSoundVolume -= inc; else gSoundVolume = VOLUME_MIN; /* test sound volume */ sound( SOUND_HIT ); /* Done */ break; } else if( ch == 46 ) { int inc = ( ms ? VOLUME_INC : 1 ); /* Adjust sound volume UP */ if( gSoundVolume < (VOLUME_MAX - inc) ) gSoundVolume += inc; else gSoundVolume = VOLUME_MAX; /* test sound volume */ sound( SOUND_HIT ); /* Done */ break; } else if(ck < 64) { /* Hack -- Prepare the menus */ setup_menus(); /* Mega-Hack -- allow easy exit if nothing to save */ if (!character_generated && (ch=='Q' || ch=='q')) ch = 'e'; /* Run the Menu-Handler */ menu(MenuKey(ch)); /* Turn off the menus */ HiliteMenu(0); /* Done */ break; } } /* Hide the mouse pointer */ ObscureCursor(); /* plog_fmt( "Keypress code=%ld, char=%ld", (long)ck, (long)ch );*/ /* Normal key -> simple keypress */ if (ck < 64) { /* Enqueue the keypress */ Term_keypress(ch); } /* Hack -- normal "keypad keys" -> special keypress */ else if (!mc && !ms && !mo && !mx && (ck < 96)) { /*plog( "KeyPad keypress being handled by Zangband!" ); */ /* Hack -- "enter" is confused */ if (ck == 76) ch = '\n'; /* Send control-caret as a trigger */ Term_keypress(30); /* Send the "ascii" keypress */ Term_keypress(ch); } /* Bizarre key -> encoded keypress */ else if (ck <= 127) { /* Hack -- introduce with control-underscore */ Term_keypress(31); /* Send some modifier keys */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (mo) Term_keypress('O'); if (mx) Term_keypress('X'); /* Hack -- Downshift and encode the keycode */ Term_keypress('0' + (ck - 64) / 10); Term_keypress('0' + (ck - 64) % 10); /* Hack -- Terminate the sequence */ Term_keypress(13); } break; } case mouseDown: { int code; /* Analyze click location */ code = FindWindow(event.where, &w); /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == w) td = &data[i]; } /* Analyze */ switch (code) { case inMenuBar: { setup_menus(); menu(MenuSelect(event.where)); HiliteMenu(0); break; } case inSysWindow: { #ifdef TARGET_CARBON #else SystemClick(&event, w); #endif break; } case inDrag: { Point p; WindowPtr old_win; BitMap screen; Rect portRect; #ifdef TARGET_CARBON GetQDGlobalsScreenBits( &screen ); #else screen = qd.screenBits; #endif r = screen.bounds; r.top += 20; /* GetMBarHeight() XXX XXX XXX */ InsetRect(&r, 4, 4); DragWindow(w, event.where, &r); /* Oops */ if (!td) break; /* Save */ old_win = active; /* Activate */ activate(td->w); /* Analyze */ #ifdef TARGET_CARBON GetWindowBounds( (WindowRef)td->w, kWindowContentRgn, &portRect ); #else portRect = td->w->portRect; local_to_global( &portRect ); #endif p.h = portRect.left; p.v = portRect.top; td->r.left = p.h; td->r.top = p.v; /* Restore */ activate(old_win); /* Apply and Verify */ term_data_check_size(td); break; } case inGoAway: { /* Oops */ if (!td) break; /* Track the go-away box */ if (TrackGoAway(w, event.where)) { /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ HideWindow(td->w); } break; } case inGrow: { int x, y; term *old = Term; /* Oops */ if (!td) break; /* Fake rectangle */ r.left = 20 * td->tile_wid + td->size_ow1; r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1; r.top = 1 * td->tile_hgt + td->size_oh1; r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1; /* Grow the rectangle */ newsize = GrowWindow(w, event.where, &r); /* Handle abort */ if (!newsize) break; /* Extract the new size in pixels */ y = HiWord(newsize) - td->size_oh1 - td->size_oh2; x = LoWord(newsize) - td->size_ow1 - td->size_ow2; /* Extract a "close" approximation */ td->rows = y / td->tile_hgt; td->cols = x / td->tile_wid; /* Apply and Verify */ term_data_check_size(td); /* Activate */ Term_activate(td->t); /* Hack -- Resize the term */ Term_resize(td->cols, td->rows); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ Term_activate(old); break; } case inContent: { SelectWindow(w); break; } } break; } /* Disk Event -- From "Maarten Hazewinkel" */ case diskEvt: { /* check for error when mounting the disk */ #ifdef TARGET_CARBON #else if (HiWord(event.message) != noErr) { Point p = {120, 120}; DILoad(); DIBadMount(p, event.message); DIUnload(); } #endif break; } /* OS Event -- From "Maarten Hazewinkel" */ case osEvt: { switch ((event.message >> 24) & 0x000000FF) { case suspendResumeMessage: /* Resuming: activate the front window */ if (event.message & resumeFlag) { SetPort( GetWindowPort(FrontWindow()) ); #ifdef TARGET_CARBON { Cursor arrow; GetQDGlobalsArrow( &arrow ); SetCursor(&arrow); } #else SetCursor( &qd.arrow ); #endif } /* Suspend: deactivate the front window */ else { /* Nothing */ } break; } break; } #ifdef USE_SFL_CODE /* From "Steve Linberg" and "Maarten Hazewinkel" */ case kHighLevelEvent: { /* Process apple events */ if (AEProcessAppleEvent(&event) != noErr) { plog("Error in Apple Event Handler!"); } /* Handle "quit_when_ready" */ if (quit_when_ready) { /* Forget */ quit_when_ready = FALSE; /* Do the menu key */ menu(MenuKey('q')); /* Turn off the menus */ HiliteMenu(0); } /* Handle "open_when_ready" */ handle_open_when_ready(); break; } #endif } /* Something happened */ return (TRUE); } /*** Some Hooks for various routines ***/ /* * Mega-Hack -- emergency lifeboat */ static vptr lifeboat = NULL; /* * Hook to "release" memory */ static vptr hook_rnfree(vptr v) { #ifdef USE_MALLOC /* Alternative method */ free(v); #else /* Dispose */ DisposePtr(v); #endif /* Success */ return (NULL); } /* * Hook to "allocate" memory */ static vptr hook_ralloc(huge size) { #ifdef USE_MALLOC /* Make a new pointer */ return (malloc(size)); #else /* Make a new pointer */ return (NewPtr(size)); #endif } /* * Hook to handle "out of memory" errors */ static vptr hook_rpanic(huge size) { #pragma unused (size) vptr mem = NULL; /* Free the lifeboat */ if (lifeboat) { /* Free the lifeboat */ DisposePtr(lifeboat); /* Forget the lifeboat */ lifeboat = NULL; /* Mega-Hack -- Warning */ mac_warning("Running out of Memory!\rAbort this process now!"); /* Mega-Hack -- Never leave this function */ while (TRUE) CheckEvents(TRUE); } /* Mega-Hack -- Crash */ return (NULL); } /* * Hook to tell the user something important */ static void hook_plog(cptr str) { /* Warning message */ mac_warning(str); } /* * Hook to tell the user something, and then quit */ static void hook_quit(cptr str) { /* Warning if needed */ if (str) mac_warning(str); /* Write a preference file */ save_pref_file(); /* Exit the sound support */ close_sounds(); /* All done */ ExitToShell(); } /* * Hook to tell the user something, and then crash */ static void hook_core(cptr str) { /* XXX Use the debugger */ /* DebugStr(str); */ /* Warning */ if (str) mac_warning(str); /* Warn, then save player */ mac_warning("Fatal error.\rI will now attempt to save and quit."); /* Attempt to save */ if (!save_player()) mac_warning("Warning -- save failed!"); /* Quit */ quit(NULL); } /*** Main program ***/ static void init_paths( void ) { char basepath[1024]; /* Default to the "lib" folder with the application */ { char app_path[1024]; PathNameFromDirID(app_dir, app_vol, (StringPtr)app_path); ptocstr((StringPtr)app_path); strcat(app_path, "lib/" ); strcpy(basepath,app_path); } /* Prepare the paths */ init_file_paths(basepath); } /* * Init some stuff * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "Macintosh Save Bug" by using "absolute" path names, since on * System 7 machines anyway, the "current working directory" often * "changes" due to background processes, invalidating any "relative" * path names. Note that the Macintosh is limited to 255 character * path names, so be careful about deeply embedded directories... * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "missing lib folder bug" by allowing the user to help find the * "lib" folder by hand if the "application folder" code fails... */ static void init_stuff(void) { int i; Rect r; Point topleft; char path[1024]; BitMap screen; /* Fake rectangle */ r.left = 0; r.top = 0; r.right = 344; r.bottom = 188; /* Center it */ #ifdef TARGET_CARBON GetQDGlobalsScreenBits( &screen ); #else screen = qd.screenBits; #endif center_rect(&r, &screen.bounds); /* Extract corner */ topleft.v = r.top; topleft.h = r.left; /* Check until done */ while (1) { OSType types[3]; /* Build the filename */ path_make(path, ANGBAND_DIR_FILE, "news.txt"); /* Attempt to open and close that file */ if (0 == fd_close(fd_open(path, O_RDONLY))) break; /* Warning */ plog_fmt("Unable to open the file '%s'", path); /* Warning */ plog("The Angband 'lib' folder is probably missing or misplaced."); /* Warning */ plog("Please 'open' any file in any sub-folder of the 'lib' folder."); /* Allow "text" files */ types[0] = 'TEXT'; /* Allow "save" files */ types[1] = 'SAVE'; /* Allow "data" files */ types[2] = 'DATA'; /* Get any file */ { OSErr err; err = ChooseFile( (unsigned char *)path, types, 3 ); if( err != noErr ) { quit(NULL); } } /* Hack -- Remove the "filename" */ i = strlen(path) - 1; while ((i > 0) && (path[i] != '/')) i--; if (path[i] == '/') path[i+1] = '\0'; /* Hack -- allow "lib" folders */ if (suffix(path, "/lib")) continue; /* Hack -- Remove the "sub-folder" */ i = i - 1; while ((i > 1) && (path[i] != '/')) i--; if (path[i] == '/') path[i+1] = '\0'; } } /* * Macintosh Main loop */ int main(void) { EventRecord tempEvent; int numberOfMasters = 10; /* Get more Masters */ /* Only effective on MacOS 8/9 platform */ while (numberOfMasters--) MoreMasters(); #ifndef TARGET_CARBON /* Set up the Macintosh */ InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); /* TEInit(); */ InitDialogs(NULL); #endif InitCursor(); /* Flush events */ FlushEvents(everyEvent, 0); /* Flush events some more (?) */ (void)EventAvail(everyEvent, &tempEvent); (void)EventAvail(everyEvent, &tempEvent); (void)EventAvail(everyEvent, &tempEvent); #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ # if defined(powerc) || defined(__powerc) /* Assume System 7 */ /* Assume Color Quickdraw */ # else /* Block */ if (TRUE) { OSErr err; long versionNumber; /* Check the Gestalt */ err = Gestalt(gestaltSystemVersion, &versionNumber); /* Check the version */ if ((err != noErr) || (versionNumber < 0x0700)) { quit("You must have System 7 to use this program."); } } # endif #endif /* ANGBAND_LITE_MAC */ #ifdef USE_SFL_CODE /* Obtain a "Universal Procedure Pointer" */ AEH_Start_UPP = NewAEEventHandlerUPP(AEH_Start); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, AEH_Start_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Quit_UPP = NewAEEventHandlerUPP(AEH_Quit); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, AEH_Quit_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Print_UPP = NewAEEventHandlerUPP(AEH_Print); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, AEH_Print_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Open_UPP = NewAEEventHandlerUPP(AEH_Open); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, AEH_Open_UPP, 0L, FALSE); #endif /* Find the current application */ SetupAppDir(); #if defined(MACINTOSH) && !defined(applec) /* Mark ourself as the file creator */ _fcreator = 'A271'; /* Default to saving a "text" file */ _ftype = 'TEXT'; #endif /* Hook in some "z-virt.c" hooks */ rnfree_aux = hook_rnfree; ralloc_aux = hook_ralloc; rpanic_aux = hook_rpanic; /* Hooks in some "z-util.c" hooks */ plog_aux = hook_plog; quit_aux = hook_quit; core_aux = hook_core; /* Show the "watch" cursor */ SetCursor(*(GetCursor(watchCursor))); /* Prepare paths */ init_paths(); /* Prepare sound effects */ init_sounds(); /* Prepare music soundtrack */ init_music(); /* Prepare the menubar */ init_menubar(); /* Prepare the windows */ init_windows(); /* Hack -- process all events */ while (CheckEvents(FALSE)) /* loop */; /* Reset the cursor */ #ifdef TARGET_CARBON { Cursor arrow; GetQDGlobalsArrow( &arrow ); SetCursor(&arrow); } #else SetCursor( &qd.arrow ); #endif /* Mega-Hack -- Allocate a "lifeboat" */ lifeboat = NewPtr(16384); /* Note the "system" */ ANGBAND_SYS = "mac"; /* Initialize */ init_stuff(); /* Initialize */ init_angband(); /* Hack -- process all events */ while (CheckEvents(FALSE)) /* loop */; /* We are now initialized */ initialized = TRUE; /* Handle "open_when_ready" */ handle_open_when_ready(); /* Prompt the user */ prtf(15, 23, "[Choose 'New' or 'Open' from the 'File' menu]"); /* Flush the prompt */ Term_fresh(); /* Hack -- Process Events Forever */ while (TRUE) CheckEvents(TRUE); } zangband/src/main-mac.c0000755000000000000000000024454510250356274014002 0ustar rootroot/* File: main-mac.c */ /* Purpose: Simple support for MACINTOSH Angband */ /* * This file should only be compiled with the "Macintosh" version * * This file written by "Ben Harrison (benh@phial.com)". * * Some code adapted from "MacAngband 2.6.1" by Keith Randall * * Maarten Hazewinkel (mmhazewi@cs.ruu.nl) provided some initial * suggestions for the PowerMac port. * * Steve Linberg (slinberg@crocker.com) provided the code surrounded * by "USE_SFL_CODE". * * The graphics code is adapted from an extremely minimal subset of * the code from "Sprite World II", an amazing animation package. * * See "z-term.c" for info on the concept of the "generic terminal" * * The preference file is now a text file named "Angband preferences". * * Note that the "preference" file is now a simple text file called * "Angband preferences", which contains the versions information, so * that obsolete preference files can be ignored (this may be bad). * * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c" * should probably be "unloaded" as soon as they are no longer needed, * to save space, but I do not know how to do this. * * Stange bug -- The first "ClipRect()" call crashes if the user closes * all the windows, switches to another application, switches back, and * then re-opens the main window, for example, using "command-a". * * By default, this file assumes that you will be using a 68020 or better * machine, running System 7 and Color Quickdraw. In fact, the game will * refuse to run unless these features are available. This allows the use * of a variety of interesting features such as graphics and sound. * * To create a version which can be used on 68000 machines, or on machines * which are not running System 7 or Color Quickdraw, simply activate the * "ANGBAND_LITE_MAC" compilation flag in the proper header file. This * will disable all "modern" features used in this file, including support * for multiple sub-windows, color, graphics, and sound. * * When compiling with the "ANGBAND_LITE_MAC" flag, the "ANGBAND_LITE" * flag will be automatically defined, which will disable many of the * advanced features of the game itself, reducing the total memory usage. * * If you are never going to use "graphics" (especially if you are not * compiling support for graphics anyway) then you can delete the "pict" * resource with id "1001" with no dangerous side effects. */ /* * Important Resources in the resource file: * * FREF 130 = 'A271' / 'APPL' (application) * FREF 129 = 'A271' / 'SAVE' (save file) * FREF 130 = 'A271' / 'TEXT' (bone file, generic text file) * FREF 131 = 'A271' / 'DATA' (binary image file, score file) * * DLOG 128 = "About Angband..." * * ALRT 128 = unused (?) * ALRT 129 = "Warning..." * ALRT 130 = "Are you sure you want to quit without saving?" * * DITL 128 = body for DLOG 128 * DITL 129 = body for ALRT 129 * DITL 130 = body for ALRT 130 * * ICON 128 = "warning" icon * * MENU 128 = apple (about, -, ...) * MENU 129 = File (new, open, close, save, -, exit, quit) * MENU 130 = Edit (undo, -, cut, copy, paste, clear) * * PICT 1001 = Graphics tile set */ /* * File name patterns: * all 'APEX' files have a filename of the form "*:apex:*" (?) * all 'BONE' files have a filename of the form "*:bone:*" (?) * all 'DATA' files have a filename of the form "*:data:*" * all 'SAVE' files have a filename of the form "*:save:*" * all 'USER' files have a filename of the form "*:user:*" (?) * * Perhaps we should attempt to set the "_ftype" flag inside this file, * to avoid nasty file type information being spread all through the * rest of the code. (?) This might require adding hooks into the * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX */ /* * Reasons for each header file: * * angband.h = Angband header file * * Types.h = (included anyway) * Gestalt.h = gestalt code * QuickDraw.h = (included anyway) * OSUtils.h = (included anyway) * Files.h = file code * Fonts.h = font code * Menus.h = menu code * Dialogs.h = dialog code * Windows.h = (included anyway) * Palettes.h = palette code * StandardFile.h = file dialog box * DiskInit.h = disk initialization * ToolUtils.h = HiWord() / LoWord() * Desk.h = OpenDeskAcc() * Devices.h = OpenDeskAcc() * Events.h = event code * Resources.h = resource code * Controls.h = button code * SegLoad.h = ExitToShell(), AppFile, etc * Memory.h = SetApplLimit(), NewPtr(), etc * QDOffscreen.h = GWorld code * Sound.h = Sound code * * For backwards compatibility: * Use GestaltEqu.h instead of Gestalt.h * Add Desk.h to include simply includes Menus.h, Devices.h, Events.h */ #include "angband.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Use "malloc()" instead of "NewPtr()" */ /* #define USE_MALLOC */ #if defined(powerc) || defined(__powerc) /* * Disable "LITE" version */ # undef ANGBAND_LITE_MAC #endif #ifdef ANGBAND_LITE_MAC /* * Maximum number of windows */ # define MAX_TERM_DATA 1 #else /* ANGBAND_LITE_MAC */ /* * Maximum number of windows */ # define MAX_TERM_DATA 8 /* * Activate some special code */ # define USE_SFL_CODE #endif /* ANGBAND_LITE_MAC */ #ifdef USE_SFL_CODE /* * Include the necessary header files */ #include #include #include #endif #if 0 /* * The Angband Color Set (0 to 15): * Black, White, Slate, Orange, Red, Blue, Green, Umber * D-Gray, L-Gray, Violet, Yellow, L-Red, L-Blue, L-Green, L-Umber * * Colors 8 to 15 are basically "enhanced" versions of Colors 0 to 7. * * On the Macintosh, we use color quickdraw, and we use actual "RGB" * values below to choose the 16 colors. * * If we are compiled for ancient machines, we bypass color and simply * draw everything in white (letting "z-term.c" automatically convert * "black" into "wipe" calls). */ static RGBColor foo[16] = { {0x0000, 0x0000, 0x0000}, /* TERM_DARK */ {0xFFFF, 0xFFFF, 0xFFFF}, /* TERM_WHITE */ {0x8080, 0x8080, 0x8080}, /* TERM_SLATE */ {0xFFFF, 0x8080, 0x0000}, /* TERM_ORANGE */ {0xC0C0, 0x0000, 0x0000}, /* TERM_RED */ {0x0000, 0x8080, 0x4040}, /* TERM_GREEN */ {0x0000, 0x0000, 0xFFFF}, /* TERM_BLUE */ {0x8080, 0x4040, 0x0000}, /* TERM_UMBER */ {0x4040, 0x4040, 0x4040}, /* TERM_L_DARK */ {0xC0C0, 0xC0C0, 0xC0C0}, /* TERM_L_WHITE */ {0xFFFF, 0x0000, 0xFFFF}, /* TERM_VIOLET */ {0xFFFF, 0xFFFF, 0x0000}, /* TERM_YELLOW */ {0xFFFF, 0x0000, 0x0000}, /* TERM_L_RED */ {0x0000, 0xFFFF, 0x0000}, /* TERM_L_GREEN */ {0x0000, 0xFFFF, 0xFFFF}, /* TERM_L_BLUE */ {0xC0C0, 0x8080, 0x4040} /* TERM_L_UMBER */ }; #endif /* * Forward declare */ typedef struct term_data term_data; /* * Extra "term" data */ struct term_data { term *t; Rect r; WindowPtr w; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ short padding; short pixelDepth; GWorldPtr theGWorld; GDHandle theGDH; GDHandle mainSWGDH; #endif /* ANGBAND_LITE_MAC */ Str15 title; s16b oops; s16b keys; s16b last; s16b mapped; s16b rows; s16b cols; s16b font_id; s16b font_size; s16b font_face; s16b font_mono; s16b font_o_x; s16b font_o_y; s16b font_wid; s16b font_hgt; s16b tile_o_x; s16b tile_o_y; s16b tile_wid; s16b tile_hgt; s16b size_wid; s16b size_hgt; s16b size_ow1; s16b size_oh1; s16b size_ow2; s16b size_oh2; }; /* * Forward declare -- see below */ static bool CheckEvents(bool wait); /* * Hack -- location of the main directory */ static short app_vol; static long app_dir; /* * Delay handling of double-clicked savefiles */ Boolean open_when_ready = FALSE; /* * Delay handling of pre-emptive "quit" event */ Boolean quit_when_ready = FALSE; /* * Hack -- game in progress */ static int game_in_progress = 0; /* * Only do "SetPort()" when needed */ static WindowPtr active = NULL; /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Note when "open"/"new" become valid */ static bool initialized = FALSE; /* * CodeWarrior uses Universal Procedure Pointers */ static ModalFilterUPP ynfilterUPP; #ifdef USE_SFL_CODE /* * Apple Event Hooks */ AEEventHandlerUPP AEH_Start_UPP; AEEventHandlerUPP AEH_Quit_UPP; AEEventHandlerUPP AEH_Print_UPP; AEEventHandlerUPP AEH_Open_UPP; #endif /* * Convert refnum+vrefnum+fname into a full file name * Store this filename in 'buf' (make sure it is long enough) * Note that 'fname' looks to be a "pascal" string */ static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname) { DirInfo pb; Str255 name; int err; int i, j; char res[1000]; i=999; res[i]=0; i--; for (j=1; j<=fname[0]; j++) { res[i-fname[0]+j] = fname[j]; } i-=fname[0]; pb.ioCompletion=NULL; pb.ioNamePtr=name; pb.ioVRefNum=vrefnum; pb.ioDrParID=refnum; pb.ioFDirIndex=-1; while (1) { pb.ioDrDirID=pb.ioDrParID; err = PBGetCatInfo((CInfoPBPtr)&pb, FALSE); res[i] = ':'; i--; for (j=1; j<=name[0]; j++) { res[i-name[0]+j] = name[j]; } i -= name[0]; if (pb.ioDrDirID == fsRtDirID) break; } /* Extract the result */ for (j = 0, i++; res[i]; j++, i++) buf[j] = res[i]; buf[j] = 0; } #if 0 /* * XXX XXX XXX Allow the system to ask us for a filename */ static bool askfor_file(char *buf, int len) { SFReply reply; Str255 dflt; Point topleft; short vrefnum; long drefnum, junk; /* Default file name */ sprintf((char*)dflt + 1, "%s's description", buf); dflt[0] = strlen((char*)dflt + 1); /* Ask for a file name */ topleft.h=(qd.screenBits.bounds.left+qd.screenBits.bounds.right)/2-344/2; topleft.v=(2*qd.screenBits.bounds.top+qd.screenBits.bounds.bottom)/3-188/2; SFPutFile(topleft, "\pSelect a filename:", dflt, NULL, &reply); /* StandardPutFile("\pSelect a filename:", dflt, &reply); */ /* Process */ if (reply.good) { int fc; /* Get info */ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk); /* Extract the name */ refnum_to_name(buf, drefnum, vrefnum, (char*)reply.fName); /* Success */ return (TRUE); } /* Failure */ return (FALSE); } #endif /* * Center a rectangle inside another rectangle */ static void center_rect(Rect *r, Rect *s) { int centerx = (s->left + s->right)/2; int centery = (2*s->top + s->bottom)/3; int dx = centerx - (r->right - r->left)/2 - r->left; int dy = centery - (r->bottom - r->top)/2 - r->top; r->left += dx; r->right += dx; r->top += dy; r->bottom += dy; } /* * Convert a pascal string in place * * This function may be defined elsewhere, but since it is so * small, it is not worth finding the proper function name for * all the different platforms. */ static void ptocstr(StringPtr src) { int i; /* Hack -- pointer */ char *s = (char*)(src); /* Hack -- convert the string */ for (i = s[0]; i; i--, s++) s[0] = s[1]; /* Hack -- terminate the string */ s[0] = '\0'; } #if defined(USE_SFL_CODE) /* * The following three routines (pstrcat, pstrinsert, and PathNameFromDirID) * were taken from the Think Reference section called "Getting a Full Pathname" * (under the File Manager section). We need PathNameFromDirID to get the * full pathname of the opened savefile, making no assumptions about where it * is. * * I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully * nice. */ static void pstrcat(StringPtr dst, StringPtr src) { /* copy string in */ BlockMove(src + 1, dst + *dst + 1, *src); /* adjust length byte */ *dst += *src; } /* * pstrinsert - insert string 'src' at beginning of string 'dst' */ static void pstrinsert(StringPtr dst, StringPtr src) { /* make room for new string */ BlockMove(dst + 1, dst + *src + 1, *dst); /* copy new string in */ BlockMove(src + 1, dst + 1, *src); /* adjust length byte */ *dst += *src; } static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName) { CInfoPBRec block; Str255 directoryName; OSErr err; fullPathName[0] = '\0'; block.dirInfo.ioDrParID = dirID; block.dirInfo.ioNamePtr = directoryName; while (1) { block.dirInfo.ioVRefNum = vRefNum; block.dirInfo.ioFDirIndex = -1; block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID; err = PBGetCatInfo(&block, FALSE); pstrcat(directoryName, (StringPtr)"\p:"); pstrinsert(fullPathName, directoryName); if (block.dirInfo.ioDrDirID == 2) break; } } #endif /* * Activate a given window, if necessary */ static void activate(WindowPtr w) { /* Activate */ if (active != w) { /* Activate */ if (w) SetPort(w); /* Remember */ active = w; } } /* * Display a warning message */ static void mac_warning(cptr warning) { Str255 text; int len, i; /* Limit of 250 chars */ len = strlen(warning); if (len > 250) len = 250; /* Make a "Pascal" string */ text[0] = len; for (i=0; ilast != a) { u16b rv, gv, bv; RGBColor color; /* Extract the R,G,B data */ rv = angband_color_table[a][1]; gv = angband_color_table[a][2]; bv = angband_color_table[a][3]; /* Set the color */ color.red = (rv | (rv << 8)); color.green = (gv | (gv << 8)); color.blue = (bv | (bv << 8)); /* Activate the color */ RGBForeColor(&color); /* Memorize color */ td->last = a; } } #endif /* ANGBAND_LITE_MAC */ /* * Hack -- Apply and Verify the "font" info * * This should usually be followed by "term_data_check_size()" */ static void term_data_check_font(term_data *td) { int i; FontInfo info; WindowPtr old = active; /* Activate */ activate(td->w); /* Instantiate font */ TextFont(td->font_id); TextSize(td->font_size); TextFace(td->font_face); /* Extract the font info */ GetFontInfo(&info); /* Assume monospaced */ td->font_mono = TRUE; /* Extract the font sizing values XXX XXX XXX */ td->font_wid = CharWidth('@'); /* info.widMax; */ td->font_hgt = info.ascent + info.descent; td->font_o_x = 0; td->font_o_y = info.ascent; /* Check important characters */ for (i = 33; i < 127; i++) { /* Hack -- notice non-mono-space */ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE; /* Hack -- collect largest width */ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i); } /* Set default offsets */ td->tile_o_x = td->font_o_x; td->tile_o_y = td->font_o_y; /* Set default tile size */ td->tile_wid = td->font_wid; td->tile_hgt = td->font_hgt; /* Re-activate the old window */ activate(old); } /* * Hack -- Apply and Verify the "size" info */ static void term_data_check_size(term_data *td) { /* Minimal window size */ if (td == &data[0]) { /* The main window has a minimum size */ if (td->cols < 80) td->cols = 80; if (td->rows < 24) td->rows = 24; } else { /* Otherwise use min size is 1x1 */ if (td->cols < 1) td->cols = 1; if (td->rows < 1) td->rows = 1; } /* Minimal tile size */ if (td->tile_wid < 4) td->tile_wid = 4; if (td->tile_hgt < 4) td->tile_hgt = 4; /* Default tile offsets */ td->tile_o_x = (td->tile_wid - td->font_wid) / 2; td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2; /* Minimal tile offsets */ if (td->tile_o_x < 0) td->tile_o_x = 0; if (td->tile_o_y < 0) td->tile_o_y = 0; /* Apply font offsets */ td->tile_o_x += td->font_o_x; td->tile_o_y += td->font_o_y; /* Calculate full window size */ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2; td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2; /* Verify the top */ if (td->r.top > qd.screenBits.bounds.bottom - td->size_hgt) { td->r.top = qd.screenBits.bounds.bottom - td->size_hgt; } /* Verify the top */ if (td->r.top < qd.screenBits.bounds.top + 30) { td->r.top = qd.screenBits.bounds.top + 30; } /* Verify the left */ if (td->r.left > qd.screenBits.bounds.right - td->size_wid) { td->r.left = qd.screenBits.bounds.right - td->size_wid; } /* Verify the left */ if (td->r.left < qd.screenBits.bounds.left) { td->r.left = qd.screenBits.bounds.left; } /* Calculate bottom right corner */ td->r.right = td->r.left + td->size_wid; td->r.bottom = td->r.top + td->size_hgt; /* Assume no graphics */ td->t->always_pict = FALSE; #ifdef ANGBAND_LITE_MAC /* No graphics */ #else /* ANGBAND_LITE_MAC */ /* Handle graphics */ if (use_graphics && ((td == &data[0]) || (td == &data[6]))) { td->t->always_pict = TRUE; } #endif /* ANGBAND_LITE_MAC */ /* Fake mono-space */ if (!td->font_mono || (td->font_wid != td->tile_wid) || (td->font_hgt != td->tile_hgt)) { /* Handle fake monospace */ td->t->always_pict = TRUE; } } /* * Hack -- resize a term_data * * This should normally be followed by "term_data_resize()" */ static void term_data_resize(term_data *td) { /* Actually resize the window */ SizeWindow(td->w, td->size_wid, td->size_hgt, 0); } /* * Hack -- redraw a term_data */ static void term_data_redraw(term_data *td) { term *old = Term; /* Activate the term */ Term_activate(td->t); /* Redraw the contents */ Term_redraw(); /* Flush the output */ Term_fresh(); /* Restore the old term */ Term_activate(old); /* No need to redraw */ ValidRect(&td->w->portRect); } #ifdef ANGBAND_LITE_MAC /* No graphics */ #else /* ANGBAND_LITE_MAC */ /* * Constants */ #define kPictID 1001 /* Graf 'pict' resource */ #define kPictMaskID 1002 /* Graf 'pict' resource */ #define kGrafWidth 16 /* Graf Size (X) */ #define kGrafHeight 16 /* Graf Size (Y) */ #define kPictCols 32 /* Number of Cols in Pict */ #define kPictRows 63 /* Number of Rows in Pict */ #define kMaxChannels 10 /* * Forward Declare */ typedef struct FrameRec FrameRec; /* * Frame * * - GWorld for the frame image * - Handle to pix map (saved for unlocking/locking) * - Pointer to color pix map (valid only while locked) */ struct FrameRec { GWorldPtr framePort; PixMapHandle framePixHndl; PixMapPtr framePix; GWorldPtr maskPort; PixMapHandle maskPixHndl; PixMapPtr maskPix; GWorldPtr bufferPort; PixMapHandle bufferPixHndl; PixMapPtr bufferPix; }; /* * The global picture data */ static FrameRec *frameP = NULL; /* * Lock a frame */ static void BenSWLockFrame(FrameRec *srcFrameP) { PixMapHandle pixMapH; pixMapH = GetGWorldPixMap(srcFrameP->framePort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->framePixHndl = pixMapH; srcFrameP->framePix = (PixMapPtr)StripAddress(*(Handle)pixMapH); pixMapH = GetGWorldPixMap(srcFrameP->maskPort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->maskPixHndl = pixMapH; srcFrameP->maskPix = (PixMapPtr)StripAddress(*(Handle)pixMapH); pixMapH = GetGWorldPixMap(srcFrameP->bufferPort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->bufferPixHndl = pixMapH; srcFrameP->bufferPix = (PixMapPtr)StripAddress(*(Handle)pixMapH); } /* * Unlock a frame */ static void BenSWUnlockFrame(FrameRec *srcFrameP) { if (srcFrameP->framePort != NULL) { HUnlock((Handle)srcFrameP->framePixHndl); UnlockPixels(srcFrameP->framePixHndl); } srcFrameP->framePix = NULL; if (srcFrameP->maskPort != NULL) { HUnlock((Handle)srcFrameP->maskPixHndl); UnlockPixels(srcFrameP->maskPixHndl); } srcFrameP->maskPix = NULL; if (srcFrameP->bufferPort != NULL) { HUnlock((Handle)srcFrameP->bufferPixHndl); UnlockPixels(srcFrameP->bufferPixHndl); } srcFrameP->bufferPix = NULL; } static OSErr BenSWCreateGWorldFromPict( GWorldPtr *pictGWorld, GWorldPtr *maskGWorld, GWorldPtr *bufferGWorld, PicHandle pictH, PicHandle maskH, term_data *td ) { OSErr err; GWorldPtr saveGWorld; GDHandle saveGDevice; GWorldPtr tempGWorld; Rect pictRect; short depth; GDHandle theGDH; { tempGWorld = NULL; /* Reset */ *pictGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect = (**pictH).picFrame; OffsetRect(&pictRect, -pictRect.left, -pictRect.top); /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Success */ if (err != noErr) { return (err); } /* Save pointer */ *pictGWorld = tempGWorld; /* Save GWorld */ GetGWorld(&saveGWorld, &saveGDevice); /* Activate */ SetGWorld(tempGWorld, nil); /* Dump the pict into the GWorld */ (void)LockPixels(GetGWorldPixMap(tempGWorld)); EraseRect(&pictRect); DrawPicture(pictH, &pictRect); UnlockPixels(GetGWorldPixMap(tempGWorld)); /* Restore GWorld */ SetGWorld(saveGWorld, saveGDevice); } { tempGWorld = NULL; /* Reset */ *maskGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect = (**maskH).picFrame; OffsetRect(&pictRect, -pictRect.left, -pictRect.top); /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Success */ if (err != noErr) { return (err); } /* Save pointer */ *maskGWorld = tempGWorld; /* Save GWorld */ GetGWorld(&saveGWorld, &saveGDevice); /* Activate */ SetGWorld(tempGWorld, nil); /* Dump the pict into the GWorld */ (void)LockPixels(GetGWorldPixMap(tempGWorld)); EraseRect(&pictRect); DrawPicture(maskH, &pictRect); UnlockPixels(GetGWorldPixMap(tempGWorld)); /* Restore GWorld */ SetGWorld(saveGWorld, saveGDevice); } { tempGWorld = NULL; /* Reset */ *bufferGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect.left = 0; pictRect.right = td->r.right - td->r.left; pictRect.top = 0; pictRect.bottom = td->tile_hgt; /* OffsetRect(&pictRect, -pictRect.left, -pictRect.top); */ /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Success */ if (err != noErr) { return (err); } /* Save pointer */ *bufferGWorld = tempGWorld; } /* Success */ return (0); } /* * Init the global "frameP" */ static errr globe_init(void) { OSErr err; GWorldPtr tempPictGWorldP; GWorldPtr tempPictMaskGWorldP; GWorldPtr tempPictBufferGWorldP; PicHandle newPictH; PicHandle newMaskH; /* Use window XXX XXX XXX */ SetPort(data[0].w); /* Get the pict resource */ newPictH = GetPicture(kPictID); newMaskH = GetPicture(kPictMaskID); /* Analyze result */ err = (newPictH ? 0 : -1) || (newMaskH ? 0 : -1); /* Oops */ if (err != noErr) { mac_warning("There were problems loading the graphics.\rYou'll have to play on in ASCII.\rWhat a bodyblow."); mac_warning("Email Rowan Beentje at rftb2@cam.ac.uk,\rgiving error code 404.\rThen step well clear of your screen\ras he targets the nuke."); } if (err == noErr) { /* Create GWorld */ err = BenSWCreateGWorldFromPict(&tempPictGWorldP, &tempPictMaskGWorldP, &tempPictBufferGWorldP, newPictH, newMaskH, &data[0]); /* rftb - main term included. */ /* Release resource */ ReleaseResource((Handle)newPictH); ReleaseResource((Handle)newMaskH); /* Error */ if (err == noErr) { /* Create the frame */ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec)); /* Analyze result */ err = (frameP ? 0 : -1); /* Oops */ if (err == noErr) { /* Save GWorld */ frameP->framePort = tempPictGWorldP; frameP->maskPort = tempPictMaskGWorldP; frameP->bufferPort = tempPictBufferGWorldP; /* Lock it */ BenSWLockFrame(frameP); } } } /* Result */ return (err); } /* * Nuke the global "frameP" */ static errr globe_nuke(void) { /* Dispose */ if (frameP) { /* Unlock */ BenSWUnlockFrame(frameP); /* Dispose of the GWorld */ DisposeGWorld(frameP->framePort); DisposeGWorld(frameP->maskPort); DisposeGWorld(frameP->bufferPort); /* Dispose of the memory */ DisposePtr((Ptr)frameP); /* Forget */ frameP = NULL; } /* Flush events */ FlushEvents(everyEvent, 0); /* Success */ return (0); } #endif /* ANGBAND_LITE_MAC */ /*** Support for the "z-term.c" package ***/ /* * Initialize a new Term * * Note also the "window type" called "noGrowDocProc", which might be more * appropriate for the main "screen" window. * * Note the use of "srcCopy" mode for optimized screen writes. */ static void Term_init_mac(term *t) { term_data *td = (term_data*)(t->data); static RGBColor black = {0x0000,0x0000,0x0000}; static RGBColor white = {0xFFFF,0xFFFF,0xFFFF}; #ifdef ANGBAND_LITE_MAC /* Make the window */ td->w = NewWindow(0, &td->r, td->title, 0, noGrowDocProc, (WindowPtr)-1, 1, 0L); #else /* ANGBAND_LITE_MAC */ /* Make the window */ td->w = NewCWindow(0, &td->r, td->title, 0, documentProc, (WindowPtr)-1, 1, 0L); #endif /* ANGBAND_LITE_MAC */ /* Activate the window */ activate(td->w); /* Erase behind words */ TextMode(srcCopy); /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize the window */ term_data_resize(td); #ifdef ANGBAND_LITE_MAC /* Prepare the colors (base colors) */ BackColor(blackColor); ForeColor(whiteColor); #else /* ANGBAND_LITE_MAC */ /* Prepare the colors (real colors) */ RGBBackColor(&black); RGBForeColor(&white); /* Block */ { Rect tempRect; Rect globalRect; GDHandle mainGDH; GDHandle currentGDH; GWorldPtr windowGWorld; PixMapHandle basePixMap; /* Obtain the rect */ tempRect = td->w->portRect; /* Obtain the global rect */ globalRect = tempRect; LocalToGlobal((Point*)&globalRect.top); LocalToGlobal((Point*)&globalRect.bottom); /* Obtain the proper GDH */ mainGDH = GetMaxDevice(&globalRect); /* Extract GWorld and GDH */ GetGWorld(&windowGWorld, ¤tGDH); /* Obtain base pixmap */ basePixMap = (**mainGDH).gdPMap; /* Save pixel depth */ td->pixelDepth = (**basePixMap).pixelSize; /* Save Window GWorld */ td->theGWorld = windowGWorld; /* Save Window GDH */ td->theGDH = currentGDH; /* Save main GDH */ td->mainSWGDH = mainGDH; } #endif /* ANGBAND_LITE_MAC */ /* Clip to the window */ ClipRect(&td->w->portRect); /* Erase the window */ EraseRect(&td->w->portRect); /* Invalidate the window */ InvalRect(&td->w->portRect); /* Display the window if needed */ if (td->mapped) ShowWindow(td->w); /* Hack -- set "mapped" flag */ t->mapped_flag = td->mapped; /* Forget color */ td->last = -1; } /* * Nuke an old Term */ static void Term_nuke_mac(term *t) { #pragma unused (t) /* XXX */ } /* * Unused */ static errr Term_user_mac(int n) { #pragma unused (n) /* Success */ return (0); } /* * React to changes */ static errr Term_xtra_mac_react(void) { term_data *td = (term_data*)(Term->data); /* Reset color */ td->last = -1; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Handle sound */ if (use_sound != arg_sound) { /* Apply request */ use_sound = arg_sound; } /* Handle graphics */ if (((td == &data[0]) || (td == &data[6])) && (use_graphics != arg_graphics)) { /* Initialize graphics */ if (!use_graphics && !frameP && (globe_init() != 0)) { plog("Cannot initialize graphics!"); arg_graphics = FALSE; } /* Apply request */ use_graphics = arg_graphics; /* Apply and Verify */ term_data_check_size(td); /* Resize the window */ term_data_resize(td); /* Reset visuals */ reset_visuals(); } #endif /* ANGBAND_LITE_MAC */ /* Success */ return (0); } /* * Do a "special thing" */ static errr Term_xtra_mac(int n, int v) { term_data *td = (term_data*)(Term->data); Rect r; /* Analyze */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: { /* Make a noise */ SysBeep(1); /* Success */ return (0); } #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Make a sound */ case TERM_XTRA_SOUND: { Handle handle; Str255 sound; #if 0 short oldResFile; short newResFile; /* Open the resource file */ oldResFile = CurResFile(); newResFile = OpenResFile(sound); /* Close the resource file */ CloseResFile(newResFile); UseResFile(oldResFile); #endif /* Get the proper sound name */ sprintf((char*)sound + 1, "%.16s.wav", angband_sound_name[v]); sound[0] = strlen((char*)sound + 1); /* Obtain resource XXX XXX XXX */ handle = GetNamedResource('snd ', sound); /* Oops */ if (handle) { /* Load and Lock */ LoadResource(handle); HLock(handle); /* Play sound (wait for completion) */ /*SndPlay(nil, (SndListHandle)handle, false);*/ { static SndChannelPtr mySndChannel[kMaxChannels]; static long channelInit = 0; long kk; OSErr myErr = noErr; if (!channelInit) { for (kk=0; kk < kMaxChannels; kk++) { /* Create sound channel for all sounds to play from */ SndNewChannel(&mySndChannel[kk], sampledSynth, initMono, 0L); } channelInit = 1; } if (handle != nil) { SCStatus status; long found; for (kk=0,found=0; kk < kMaxChannels && !found; kk++) { myErr = SndChannelStatus(mySndChannel[kk], sizeof(SCStatus), &status); if (myErr == noErr && !status.scChannelBusy) { /* Play new sound ansynchronously */ SndPlay(mySndChannel[kk], (SndListHandle)handle, true); found = 1; } } /* if (!found) { SndPlay(0L, (SndListHandle)handle, false); } */ } } /* Unlock and release */ HUnlock(handle); ReleaseResource(handle); } /* Success */ return (0); } #endif /* ANGBAND_LITE_MAC */ /* Process random events */ case TERM_XTRA_BORED: { /* Process an event */ (void)CheckEvents(0); /* Success */ return (0); } /* Process pending events */ case TERM_XTRA_EVENT: { /* Process an event */ (void)CheckEvents(v); /* Success */ return (0); } /* Flush all pending events (if any) */ case TERM_XTRA_FLUSH: { /* Hack -- flush all events */ while (CheckEvents(TRUE)) /* loop */; /* Success */ return (0); } /* Hack -- Change the "soft level" */ case TERM_XTRA_LEVEL: { /* Activate if requested */ if (v) activate(td->w); /* Success */ return (0); } /* React to changes */ case TERM_XTRA_REACT: { /* React to changes */ return (Term_xtra_mac_react()); } /* Delay (milliseconds) */ case TERM_XTRA_DELAY: { /* If needed */ if (v > 0) { long m = TickCount() + (v * 60L) / 1000; /* Wait for it */ while (TickCount() < m) /* loop */; } /* Success */ return (0); } } /* Oops */ return (1); } /* * Low level graphics (Assumes valid input). * Draw a "cursor" at (x,y), using a "yellow box". * We are allowed to use "Term_grab()" to determine * the current screen contents (for inverting, etc). */ static errr Term_curs_mac(int x, int y) { Rect r; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, TERM_YELLOW); /* Frame the grid */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; FrameRect(&r); /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ static errr Term_wipe_mac(int x, int y, int n) { Rect r; term_data *td = (term_data*)(Term->data); /* Erase the block of characters */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + n * td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; EraseRect(&r); /* Success */ return (0); } /* * Low level graphics. Assumes valid input. * * Draw several ("n") chars, with an attr, at a given location. */ static errr Term_text_mac(int x, int y, int n, byte a, const char *cp) { int xp, yp; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, (a & 0x0F)); /* Starting pixel */ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1; yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ if (n == 1) DrawChar(*cp); /* Draw the string */ else DrawText(cp, 0, n); /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i; Rect dirtyRect; Rect r2; term_data *td = (term_data*)(Term->data); /* Destination rectangle */ r2.left = x * td->tile_wid + td->size_ow1; r2.right = r2.left + td->tile_wid; r2.top = y * td->tile_hgt + td->size_oh1; r2.bottom = r2.top + td->tile_hgt; /* Scan the input */ for (i = 0; i < n; i++) { bool done = FALSE; byte a = ap[i]; char c = cp[i]; byte ta = tap[i]; char tc = tcp[i]; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Graphics -- if Available and Needed */ if (use_graphics && ((td == &data[0]) || (td == &data[6])) && ((byte)a & 0x80) && ((byte)c & 0x80)) { int col, row; Rect r1; int terrain_col, terrain_row; Rect terrain_rect; /* Row and Col */ row = ((byte)a & 0x7F) % kPictRows; col = ((byte)c & 0x7F) % kPictCols; terrain_row = ((byte)ta & 0x7F) % kPictRows; terrain_col = ((byte)tc & 0x7F) % kPictCols; /* Source rectangle */ r1.left = col * kGrafWidth; r1.top = row * kGrafHeight; r1.right = r1.left + kGrafWidth; r1.bottom = r1.top + kGrafHeight; /* Hardwire CopyBits */ BackColor(whiteColor); ForeColor(blackColor); /* Draw the picture */ /* CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &r1, &r2, srcCopy, NULL); */ /* Terrain rectangle */ terrain_rect.left = terrain_col * kGrafWidth; terrain_rect.top = terrain_row * kGrafHeight; terrain_rect.right = terrain_rect.left + kGrafWidth; terrain_rect.bottom = terrain_rect.top + kGrafHeight; CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &terrain_rect, &r2, srcCopy, NULL); CopyMask((BitMap*)frameP->framePix, (BitMap*)frameP->maskPix, &(td->w->portBits), &r1, &r1, &r2); /* Restore colors */ BackColor(blackColor); ForeColor(whiteColor); /* Forget color */ td->last = -1; /* Done */ done = TRUE; } #endif /* ANGBAND_LITE_MAC */ /* Normal */ if (!done) { int xp, yp; /* Erase */ EraseRect(&r2); /* Set the color */ term_data_color(td, (a & 0x0F)); /* Starting pixel */ xp = r2.left + td->tile_o_x; yp = r2.top + td->tile_o_y; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ DrawChar(c); } /* Advance */ r2.left += td->tile_wid; r2.right += td->tile_wid; } /* Success */ return (0); } /* * Create and initialize window number "i" */ static void term_data_link(int i) { term *old = Term; term_data *td = &data[i]; /* Only once */ if (td->t) return; /* Require mapped */ if (!td->mapped) return; /* Allocate */ MAKE(td->t, term); /* Initialize the term */ term_init(td->t, td->cols, td->rows, td->keys); /* Use a "software" cursor */ td->t->soft_cursor = TRUE; /* Erase with "black space" */ td->t->attr_blank = TERM_DARK; td->t->char_blank = ' '; /* Prepare the init/nuke hooks */ td->t->init_hook = Term_init_mac; td->t->nuke_hook = Term_nuke_mac; /* Prepare the function hooks */ td->t->user_hook = Term_user_mac; td->t->xtra_hook = Term_xtra_mac; td->t->wipe_hook = Term_wipe_mac; td->t->curs_hook = Term_curs_mac; td->t->text_hook = Term_text_mac; td->t->pict_hook = Term_pict_mac; /* Link the local structure */ td->t->data = (vptr)(td); /* Activate it */ Term_activate(td->t); /* Global pointer */ angband_term[i] = td->t; /* Activate old */ Term_activate(old); } /* * Set the "current working directory" (also known as the "default" * volume/directory) to the location of the current application. * * Code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl) * * This function does not appear to work correctly with System 6. */ static void SetupAppDir(void) { FCBPBRec fcbBlock; OSErr err = noErr; char errString[100]; /* Get the location of the Angband executable */ fcbBlock.ioCompletion = NULL; fcbBlock.ioNamePtr = NULL; fcbBlock.ioVRefNum = 0; fcbBlock.ioRefNum = CurResFile(); fcbBlock.ioFCBIndx = 0; err = PBGetFCBInfo(&fcbBlock, FALSE); if (err != noErr) { sprintf(errString, "Fatal PBGetFCBInfo Error #%d.\r Exiting.", err); mac_warning(errString); ExitToShell(); } /* Extract the Vol and Dir */ app_vol = fcbBlock.ioFCBVRefNum; app_dir = fcbBlock.ioFCBParID; /* Set the current working directory to that location */ err = HSetVol(NULL, app_vol, app_dir); if (err != noErr) { sprintf(errString, "Fatal HSetVol Error #%d.\r Exiting.", err); mac_warning(errString); ExitToShell(); } } /* * Global "preference" file pointer */ static FILE *fff; /* * Read a "short" from the file */ static int getshort(void) { int x = 0; char buf[256]; if (0 == my_fgets(fff, buf, 256)) x = atoi(buf); return (x); } /* * Dump a "short" to the file */ static void putshort(int x) { fprintf(fff, "%d\n", x); } /* * Write the "preference" data to the current "file" */ static void save_prefs(void) { int i; term_data *td; /*** The current version ***/ putshort(VER_MAJOR); putshort(VER_MINOR); putshort(VER_PATCH); putshort(VER_EXTRA); putshort(arg_sound); putshort(arg_graphics); /* Dump */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access */ td = &data[i]; putshort(td->mapped); putshort(td->font_id); putshort(td->font_size); putshort(td->font_face); putshort(td->cols); putshort(td->rows); putshort(td->r.left); putshort(td->r.top); } } /* * Load the preferences from the current "file" * * XXX XXX XXX Being able to undefine various windows is * slightly bizarre, and may cause problems. */ static void load_prefs(void) { int i; int old_major, old_minor, old_patch, old_extra; bool old_sound, old_graphics; term_data *td; /*** Version information ***/ /* Preferences version */ old_major = getshort(); old_minor = getshort(); old_patch = getshort(); old_extra = getshort(); /* Hack -- Verify or ignore */ if ((old_major != VER_MAJOR) || (old_minor != VER_MINOR) || (old_patch != VER_PATCH) || (old_extra != VER_EXTRA)) { /* Message */ mac_warning("Ignoring old preferences."); /* Ignore */ return; } old_sound = getshort(); old_graphics = getshort(); arg_graphics = old_graphics; arg_sound = old_sound; /* Windows */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access */ td = &data[i]; td->mapped = getshort(); td->font_id = getshort(); td->font_size = getshort(); td->font_face = getshort(); td->cols = getshort(); td->rows = getshort(); td->r.left = getshort(); td->r.top = getshort(); /* Done */ if (feof(fff)) break; } } /* * Hack -- default data for a window */ static void term_data_hack(term_data *td) { short fid; /* Default to Monaco font */ GetFNum("\pmonaco", &fid); /* Wipe it */ WIPE(td, term_data); /* No color */ td->last = -1; /* Default borders */ td->size_ow1 = 2; td->size_ow2 = 2; td->size_oh2 = 2; /* Start hidden */ td->mapped = FALSE; /* Default font */ td->font_id = fid; /* Default font size */ td->font_size = 12; /* Default font face */ td->font_face = 0; /* Default size */ td->rows = 24; td->cols = 80; /* Default position */ td->r.left = 10; td->r.top = 40; /* Minimal keys */ td->keys = 16; } /* * Read the preference file, Create the windows. * * We attempt to use "FindFolder()" to track down the preference file, * but if this fails, for any reason, we will try the "SysEnvirons()" * method, which may work better with System 6. */ static void init_windows(void) { int i, b = 0; term_data *td; SysEnvRec env; short savev; long saved; bool oops; /*** Default values ***/ /* Initialize (backwards) */ for (i = MAX_TERM_DATA - 1; i >= 0; i--) { int n; cptr s; /* Obtain */ td = &data[i]; /* Defaults */ term_data_hack(td); /* Obtain title */ s = angband_term_name[i]; /* Get length */ n = strlen(s); /* Maximal length */ if (n > 15) n = 15; /* Copy the title */ strncpy((char*)(td->title) + 1, s, n); /* Save the length */ td->title[0] = n; /* Tile the windows */ td->r.left += (b * 30); td->r.top += (b * 30); /* Tile */ b++; } /*** Load preferences ***/ /* Assume failure */ oops = TRUE; /* Assume failure */ fff = NULL; #ifdef USE_SFL_CODE /* Block */ if (TRUE) { OSErr err; short vref; long dirID; char foo[128]; /* Find the folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &vref, &dirID); /* Success */ if (!err) { /* Extract a path name */ PathNameFromDirID(dirID, vref, (StringPtr)foo); /* Convert the string */ ptocstr((StringPtr)foo); /* Append the preference file name */ strcat(foo, "zAngband 2.5 Preferences"); /* Open the preference file */ fff = fopen(foo, "r"); /* Success */ oops = FALSE; } } #endif /* USE_SFL_CODE */ /* Oops */ if (oops) { /* Save */ HGetVol(0, &savev, &saved); /* Go to the "system" folder */ SysEnvirons(curSysEnvVers, &env); SetVol(0, env.sysVRefNum); /* Open the file */ fff = fopen(":Preferences:zAngband 2.5 Preferences", "r"); if (!fff) fff = fopen(":zAngband 2.5 Preferences", "r"); /* Restore */ HSetVol(0, savev, saved); } /* Load preferences */ if (fff) { /* Load a real preference file */ load_prefs(); /* Close the file */ my_fclose(fff); } /*** Instantiate ***/ /* Main window */ td = &data[0]; /* Many keys */ td->keys = 1024; /* Start visible */ td->mapped = TRUE; /* Link (backwards, for stacking order) */ for (i = MAX_TERM_DATA - 1; i >= 0; i--) { term_data_link(i); } /* Main window */ td = &data[0]; /* Main window */ Term_activate(td->t); } /* * Exit the program */ static void save_pref_file(void) { bool oops; SysEnvRec env; short savev; long saved; /* Assume failure */ oops = TRUE; /* Assume failure */ fff = NULL; #if defined(MACINTOSH) && !defined(applec) /* Text file */ _ftype = 'TEXT'; #endif #ifdef USE_SFL_CODE /* Block */ if (TRUE) { OSErr err; short vref; long dirID; char foo[128]; /* Find the folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &vref, &dirID); /* Success */ if (!err) { /* Extract a path name */ PathNameFromDirID(dirID, vref, (StringPtr)foo); /* Convert the string */ ptocstr((StringPtr)foo); /* Append the preference file name */ strcat(foo, "zAngband 2.5 Preferences"); /* Open the preference file */ fff = fopen(foo, "w"); /* Success */ oops = FALSE; } } #endif /* USE_SFL_CODE */ /* Oops */ if (oops) { /* Save */ HGetVol(0, &savev, &saved); /* Go to "system" folder */ SysEnvirons(curSysEnvVers, &env); SetVol(0, env.sysVRefNum); /* Open the preference file */ fff = fopen(":Preferences:zAngband 2.5 Preferences", "w"); if (!fff) fff = fopen(":zAngband 2.5 Preferences", "w"); /* Restore */ HSetVol(0, savev, saved); } /* Save preferences */ if (fff) { /* Write the preferences */ save_prefs(); /* Close it */ my_fclose(fff); } } /* * A simple "Yes/No" filter to parse "key press" events in dialog windows */ static pascal Boolean ynfilter(DialogPtr dialog, EventRecord *event, short *ip) { /* Parse key press events */ if (event->what == keyDown) { int i = 0; char c; /* Extract the pressed key */ c = (event->message & charCodeMask); /* Accept "no" and and */ if ((c=='n') || (c=='N') || (c==13) || (c==3)) i = 1; /* Accept "yes" */ else if ((c=='y') || (c=='Y')) i = 2; /* Handle "yes" or "no" */ if (i) { short type; ControlHandle control; Rect r; /* Get the button */ GetDialogItem(dialog, i, &type, (Handle*)&control, &r); /* Blink button for 1/10 second */ HiliteControl(control, 1); Term_xtra_mac(TERM_XTRA_DELAY, 100); HiliteControl(control, 0); /* Result */ *ip = i; return (1); } } /* Ignore */ return (0); } /* * Handle menu: "File" + "New" */ static void do_menu_file_new(void) { /* Hack */ HiliteMenu(0); /* Game is in progress */ game_in_progress = 1; /* Flush input */ flush(); /* Play a game */ play_game(TRUE); /* Hack -- quit */ quit(NULL); } /* * Handle menu: "File" + "Open" */ static void do_menu_file_open(bool all) { int err; short vrefnum; long drefnum; long junk; DirInfo pb; SFTypeList types; SFReply reply; Point topleft; /* XXX XXX XXX */ /* vrefnum = GetSFCurVol(); */ vrefnum = -*((short*)0x214); /* drefnum = GetSFCurDir(); */ drefnum = *((long*)0x398); /* Descend into "lib" folder */ pb.ioCompletion = NULL; pb.ioNamePtr = "\plib"; pb.ioVRefNum = vrefnum; pb.ioDrDirID = drefnum; pb.ioFDirIndex = 0; /* Check for errors */ err = PBGetCatInfo((CInfoPBPtr)&pb, FALSE); /* Success */ if ((err == noErr) && (pb.ioFlAttrib & 0x10)) { /* Descend into "lib/save" folder */ pb.ioCompletion = NULL; pb.ioNamePtr = "\psave"; pb.ioVRefNum = vrefnum; pb.ioDrDirID = pb.ioDrDirID; pb.ioFDirIndex = 0; /* Check for errors */ err = PBGetCatInfo((CInfoPBPtr)&pb, FALSE); /* Success */ if ((err == noErr) && (pb.ioFlAttrib & 0x10)) { /* SetSFCurDir(pb.ioDrDirID); */ *((long*)0x398) = pb.ioDrDirID; } } /* Window location */ topleft.h = (qd.screenBits.bounds.left+qd.screenBits.bounds.right)/2-344/2; topleft.v = (2*qd.screenBits.bounds.top+qd.screenBits.bounds.bottom)/3-188/2; /* Allow "all" files */ if (all) { /* Get any file */ SFGetFile(topleft, "\p", NULL, -1, types, NULL, &reply); } /* Allow "save" files */ else { /* Legal types */ types[0] = 'SAVE'; /* Get a file */ SFGetFile(topleft, "\p", NULL, 1, types, NULL, &reply); } /* Allow cancel */ if (!reply.good) return; /* Extract textual file name for save file */ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk); refnum_to_name(savefile, drefnum, vrefnum, (char*)reply.fName); /* Hack */ HiliteMenu(0); /* Game is in progress */ game_in_progress = 1; /* Flush input */ flush(); /* Play a game */ play_game(FALSE); /* Hack -- quit */ quit(NULL); } /* * Handle the "open_when_ready" flag */ static void handle_open_when_ready(void) { /* Check the flag XXX XXX XXX make a function for this */ if (open_when_ready && initialized && !game_in_progress) { /* Forget */ open_when_ready = FALSE; /* Game is in progress */ game_in_progress = 1; /* Wait for it */ pause_line(23); /* Flush input */ flush(); /* Play a game */ play_game(FALSE); /* Quit */ quit(NULL); } } /* * Initialize the menus * * Verify menus 128, 129, 130 * Create menus 131, 132, 133, 134 * * The standard menus are: * * Apple (128) = { About, -, ... } * File (129) = { New,Open,Import,Close,Save,-,Exit,Quit } * Edit (130) = { Cut, Copy, Paste, Clear } (?) * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... } * Size (132) = { ... } * Window (133) = { Angband, Mirror, Recall, Choice, * Term-4, Term-5, Term-6, Term-7 } * Special (134) = { arg_sound, arg_graphics, -, * arg_fiddle, arg_wizard } */ static void init_menubar(void) { int i, n; Rect r; WindowPtr tmpw; MenuHandle m; /* Get the "apple" menu */ m = GetMenu(128); /* Insert the menu */ InsertMenu(m, 0); /* Add the DA's to the "apple" menu */ AppendResMenu(m, 'DRVR'); /* Get the "File" menu */ m = GetMenu(129); /* Insert the menu */ InsertMenu(m, 0); /* Get the "Edit" menu */ m = GetMenu(130); /* Insert the menu */ InsertMenu(m, 0); /* Make the "Font" menu */ m = NewMenu(131, "\pFont"); /* Insert the menu */ InsertMenu(m, 0); /* Add "bold" */ AppendMenu(m, "\pBold"); /* Add "wide" */ AppendMenu(m, "\pWide"); /* Add a separator */ AppendMenu(m, "\p-"); /* Fake window */ r.left = r.right = r.top = r.bottom = 0; /* Make the fake window */ tmpw = NewWindow(0, &r, "\p", false, documentProc, 0, 0, 0); /* Activate the "fake" window */ SetPort(tmpw); /* Default mode */ TextMode(0); /* Default size */ TextSize(12); /* Add the fonts to the menu */ AppendResMenu(m, 'FONT'); /* Size of menu */ n = CountMItems(m); /* Scan the menu */ for (i = n; i >= 4; i--) { Str255 tmpName; short fontNum; /* Acquire the font name */ GetMenuItemText(m, i, tmpName); /* GetItem(m, i, tmpName); */ /* Acquire the font index */ GetFNum(tmpName, &fontNum); /* Apply the font index */ TextFont(fontNum); /* Remove non-mono-spaced fonts */ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0)) { /* Delete the menu item XXX XXX XXX */ DeleteMenuItem(m, i); /* DelMenuItem(m, i); */ } } /* Destroy the old window */ DisposeWindow(tmpw); /* Add a separator */ AppendMenu(m, "\p-"); /* Add the fonts to the menu */ AppendResMenu(m, 'FONT'); /* Make the "Size" menu */ m = NewMenu(132, "\pSize"); /* Insert the menu */ InsertMenu(m, 0); /* Add some sizes (stagger choices) */ for (i = 8; i <= 32; i += ((i / 16) + 1)) { Str15 buf; /* Textual size */ sprintf((char*)buf + 1, "%d", i); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(m, buf); } /* Make the "Windows" menu */ m = NewMenu(133, "\pWindows"); /* Insert the menu */ InsertMenu(m, 0); /* Default choices */ for (i = 0; i < MAX_TERM_DATA; i++) { Str15 buf; /* Describe the item */ sprintf((char*)buf + 1, "%.15s", angband_term_name[i]); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(m, buf); /* Command-Key shortcuts */ if (i < 8) SetItemCmd(m, i + 1, '0' + i); } /* Make the "Special" menu */ m = NewMenu(134, "\pSpecial"); /* Insert the menu */ InsertMenu(m, 0); /* Append the choices */ AppendMenu(m, "\puse sounds"); AppendMenu(m, "\puse 16*16 graphics"); AppendMenu(m, "\p-"); AppendMenu(m, "\pfiddle mode - cheat"); AppendMenu(m, "\pwizard mode - cheat"); /* Make the "TileWidth" menu */ m = NewMenu(135, "\pTileWidth"); /* Insert the menu */ InsertMenu(m, 0); /* Add some sizes */ for (i = 4; i <= 32; i++) { Str15 buf; /* Textual size */ sprintf((char*)buf + 1, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(m, buf); } /* Make the "TileHeight" menu */ m = NewMenu(136, "\pTileHeight"); /* Insert the menu */ InsertMenu(m, 255); /* Add some sizes */ for (i = 4; i <= 32; i++) { Str15 buf; /* Textual size */ sprintf((char*)buf + 1, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(m, buf); } /* Update the menu bar */ DrawMenuBar(); } /* * Prepare the menus */ static void setup_menus(void) { int i, n; short value; Str255 s; MenuHandle m; term_data *td = NULL; /* Relevant "term_data" */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Unused */ if (!data[i].t) continue; /* Notice the matching window */ if (data[i].w == FrontWindow()) td = &data[i]; } /* File menu */ m = GetMenuHandle(129); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Enable "new"/"open..."/"import..." */ if (initialized && !game_in_progress) { EnableItem(m, 1); EnableItem(m, 2); EnableItem(m, 3); } /* Enable "close" */ if (initialized) { EnableItem(m, 4); } /* Enable "save" */ if (initialized && character_generated) { EnableItem(m, 5); } /* Enable "exit"/"quit" */ if (TRUE) { EnableItem(m, 7); EnableItem(m, 8); } /* Edit menu */ m = GetMenuHandle(130); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Enable "edit" options if "needed" */ if (!td) { EnableItem(m, 1); EnableItem(m, 3); EnableItem(m, 4); EnableItem(m, 5); EnableItem(m, 6); } /* Font menu */ m = GetMenuHandle(131); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, 1, bold); */ /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, 2, extend); */ /* Active window */ if (td) { /* Enable "bold" */ EnableItem(m, 1); /* Enable "extend" */ EnableItem(m, 2); /* Check the appropriate "bold-ness" */ if (td->font_face & bold) CheckItem(m, 1, TRUE); /* Check the appropriate "wide-ness" */ if (td->font_face & extend) CheckItem(m, 2, TRUE); /* Analyze fonts */ for (i = 4; i <= n; i++) { /* Enable it */ EnableItem(m, i); /* Analyze font */ GetMenuItemText(m,i,s); /* GetItem(m, i, s); */ GetFNum(s, &value); /* Check active font */ if (td->font_id == value) CheckItem(m, i, TRUE); } } /* Size menu */ m = GetMenuHandle(132); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Active window */ if (td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m,i,s); /* GetItem(m, i, s); */ s[s[0]+1] = '\0'; value = atoi((char*)(s+1)); /* Enable the "real" sizes */ if (RealFont(td->font_id, value)) EnableItem(m, i); /* Check the current size */ if (td->font_size == value) CheckItem(m, i, TRUE); } } /* Windows menu */ m = GetMenuHandle(133); /* Get menu size */ n = CountMItems(m); /* Check active windows */ for (i = 1; i <= n; i++) { /* Check if needed */ CheckItem(m, i, data[i-1].mapped); } /* Special menu */ m = GetMenuHandle(134); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Item "arg_sound" */ EnableItem(m, 1); CheckItem(m, 1, arg_sound); /* Item "arg_graphics" */ EnableItem(m, 2); CheckItem(m, 2, arg_graphics); /* Item "arg_fiddle" */ EnableItem(m, 4); CheckItem(m, 4, arg_fiddle); /* Item "arg_wizard" */ EnableItem(m, 5); CheckItem(m, 5, arg_wizard); /* Item "Hack" */ /* EnableItem(m, 9); */ /* TileWidth menu */ m = GetMenuHandle(135); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Active window */ if (td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m,i,s); /* GetItem(m, i, s); */ s[s[0]+1] = '\0'; value = atoi((char*)(s+1)); /* Enable */ EnableItem(m, i); /* Check the current size */ if (td->tile_wid == value) CheckItem(m, i, TRUE); } } /* TileHeight menu */ m = GetMenuHandle(136); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Active window */ if (td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m,i,s); /* GetItem(m, i, s); */ s[s[0]+1] = '\0'; value = atoi((char*)(s+1)); /* Enable */ EnableItem(m, i); /* Check the current size */ if (td->tile_hgt == value) CheckItem(m, i, TRUE); } } } /* * Process a menu selection (see above) * * Hack -- assume that invalid menu selections are disabled above, * which I have been informed may not be reliable. XXX XXX XXX */ static void menu(long mc) { int i; int menuid, selection; static unsigned char s[1000]; short fid; term_data *td = NULL; WindowPtr old_win; /* Analyze the menu command */ menuid = HiWord(mc); selection = LoWord(mc); /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == FrontWindow()) td = &data[i]; } /* Branch on the menu */ switch (menuid) { /* Apple Menu */ case 128: { /* About Angband... */ if (selection == 1) { DialogPtr dialog; Rect r; short item_hit; dialog=GetNewDialog(128, 0, (WindowPtr)-1); r=dialog->portRect; center_rect(&r, &qd.screenBits.bounds); MoveWindow(dialog, r.left, r.top, 1); ShowWindow(dialog); ModalDialog(0, &item_hit); DisposeDialog(dialog); break; } /* Desk accessory */ GetMenuItemText(GetMenuHandle(128),selection,s); /* GetItem(GetMenuHandle(128), selection, s); */ OpenDeskAcc(s); break; } /* File Menu */ case 129: { switch (selection) { /* New */ case 1: { do_menu_file_new(); break; } /* Open... */ case 2: { do_menu_file_open(FALSE); break; } /* Import... */ case 3: { do_menu_file_open(TRUE); break; } /* Close */ case 4: { /* No window */ if (!td) break; /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ HideWindow(td->w); break; } /* Save */ case 5: { /* Hack -- Forget messages */ msg_flag = FALSE; /* Hack -- Save the game */ do_cmd_save_game(FALSE); break; } /* Exit (without save) */ case 7: { /* Allow user to cancel "dangerous" exit */ if (game_in_progress && character_generated) { AlertTHndl alert; short item_hit; /* Get the "alert" info */ alert = (AlertTHndl)GetResource('ALRT', 130); /* Center the "alert" rectangle */ center_rect(&(*alert)->boundsRect, &qd.screenBits.bounds); /* Display the Alert, get "No" or "Yes" */ item_hit = Alert(130, ynfilterUPP); /* Require "yes" button */ if (item_hit != 2) break; } /* Quit */ quit(NULL); break; } /* Quit (with save) */ case 8: { /* Save the game (if necessary) */ if (game_in_progress && character_generated) { /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ do_cmd_save_game(FALSE); } /* Quit */ quit(NULL); break; } } break; } /* Edit menu */ case 130: { /* Unused */ break; } /* Font menu */ case 131: { /* Require a window */ if (!td) break; /* Memorize old */ old_win = active; /* Activate */ activate(td->w); /* Toggle the "bold" setting */ if (selection == 1) { /* Toggle the setting */ if (td->font_face & bold) { td->font_face &= ~bold; } else { td->font_face |= bold; } /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Toggle the "wide" setting */ if (selection == 2) { /* Toggle the setting */ if (td->font_face & extend) { td->font_face &= ~extend; } else { td->font_face |= extend; } /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Get a new font name */ GetMenuItemText(GetMenuHandle(131), selection, s); /* GetItem(GetMenuHandle(131), selection, s); */ GetFNum(s, &fid); /* Save the new font id */ td->font_id = fid; /* Current size is bad for new font */ if (!RealFont(td->font_id, td->font_size)) { /* Find similar size */ for (i = 1; i <= 32; i++) { /* Adjust smaller */ if (td->font_size - i >= 8) { if (RealFont(td->font_id, td->font_size - i)) { td->font_size -= i; break; } } /* Adjust larger */ if (td->font_size + i <= 128) { if (RealFont(td->font_id, td->font_size + i)) { td->font_size += i; break; } } } } /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore the window */ activate(old_win); break; } /* Size menu */ case 132: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(132), selection, s); /* GetItem(GetMenuHandle(132), selection, s); */ s[s[0]+1]=0; td->font_size = atoi((char*)(s+1)); /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* Window menu */ case 133: { /* Parse */ i = selection - 1; /* Check legality of choice */ if ((i < 0) || (i >= MAX_TERM_DATA)) break; /* Obtain the window */ td = &data[i]; /* Mapped */ td->mapped = TRUE; /* Link */ term_data_link(i); /* Mapped (?) */ td->t->mapped_flag = TRUE; /* Show the window */ ShowWindow(td->w); /* Bring to the front */ SelectWindow(td->w); break; } /* Special menu */ case 134: { switch (selection) { case 1: { /* Toggle arg_sound */ arg_sound = !arg_sound; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); break; } case 2: { /* Toggle arg_graphics */ arg_graphics = !arg_graphics; /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } case 4: { arg_fiddle = !arg_fiddle; break; } case 5: { arg_wizard = !arg_wizard; break; } } break; } /* TileWidth menu */ case 135: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(135), selection, s); /* GetItem(GetMenuHandle(135), selection, s); */ s[s[0]+1]=0; td->tile_wid = atoi((char*)(s+1)); /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* TileHeight menu */ case 136: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(136), selection, s); /* GetItem(GetMenuHandle(136), selection, s); */ s[s[0]+1]=0; td->tile_hgt = atoi((char*)(s+1)); /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } } /* Clean the menu */ HiliteMenu(0); } #ifdef USE_SFL_CODE /* * Check for extra required parameters -- From "Maarten Hazewinkel" */ static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent) { OSErr aeError; DescType returnedType; Size actualSize; aeError = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize); if (aeError == errAEDescNotFound) return (noErr); if (aeError == noErr) return (errAEParamMissed); return (aeError); } /* * Apple Event Handler -- Open Application */ static pascal OSErr AEH_Start(const AppleEvent *theAppleEvent, const AppleEvent *reply, long handlerRefCon) { #pragma unused(reply, handlerRefCon) return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Quit Application */ static pascal OSErr AEH_Quit(const AppleEvent *theAppleEvent, const AppleEvent *reply, long handlerRefCon) { #pragma unused(reply, handlerRefCon) /* Quit later */ quit_when_ready = TRUE; /* Check arguments */ return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Print Documents */ static pascal OSErr AEH_Print(const AppleEvent *theAppleEvent, const AppleEvent *reply, long handlerRefCon) { #pragma unused(theAppleEvent, reply, handlerRefCon) return (errAEEventNotHandled); } /* * Apple Event Handler by Steve Linberg (slinberg@crocker.com). * * The old method of opening savefiles from the finder does not work * on the Power Macintosh, because CountAppFiles and GetAppFiles, * used to return information about the selected document files when * an application is launched, are part of the Segment Loader, which * is not present in the RISC OS due to the new memory architecture. * * The "correct" way to do this is with AppleEvents. The following * code is modeled on the "Getting Files Selected from the Finder" * snippet from Think Reference 2.0. (The prior sentence could read * "shamelessly swiped & hacked") */ static pascal OSErr AEH_Open(AppleEvent *theAppleEvent, AppleEvent* reply, long handlerRefCon) { #pragma unused(reply, handlerRefCon) FSSpec myFSS; AEDescList docList; OSErr err; Size actualSize; AEKeyword keywd; DescType returnedType; char foo[128]; FInfo myFileInfo; /* Put the direct parameter (a descriptor list) into a docList */ err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList); if (err) return err; /* * We ignore the validity check, because we trust the FInder, and we only * allow one savefile to be opened, so we ignore the depth of the list. */ err = AEGetNthPtr(&docList, 1L, typeFSS, &keywd, &returnedType, (Ptr) &myFSS, sizeof(myFSS), &actualSize); if (err) return err; /* Only needed to check savefile type below */ err = FSpGetFInfo(&myFSS, &myFileInfo); if (err) { sprintf(foo, "Arg! FSpGetFInfo failed with code %d", err); mac_warning (foo); return err; } /* Ignore non 'SAVE' files */ if (myFileInfo.fdType != 'SAVE') return noErr; /* XXX XXX XXX Extract a file name */ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile); pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name); /* Convert the string */ ptocstr((StringPtr)savefile); /* Delay actual open */ open_when_ready = TRUE; /* Dispose */ err = AEDisposeDesc(&docList); /* Success */ return noErr; } #endif /* * Macintosh modifiers (event.modifier & ccc): * cmdKey, optionKey, shiftKey, alphaLock, controlKey * * * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra): * * Return:36 * Delete:51 * * Period:65 * Star:67 * Plus:69 * Clear:71 * Slash:75 * Enter:76 * Minus:78 * Equal:81 * 0-7:82-89 * 8-9:91-92 * * F5: 96 * F6: 97 * F7: 98 * F3:99 * F8:100 * F10:101 * F11:103 * F13:105 * F14:107 * F9:109 * F12:111 * F15:113 * Help:114 * Home:115 * PgUp:116 * Del:117 * F4: 118 * End:119 * F2:120 * PgDn:121 * F1:122 * Lt:123 * Rt:124 * Dn:125 * Up:126 */ /* * Optimize non-blocking calls to "CheckEvents()" * Idea from "Maarten Hazewinkel " */ #define EVENT_TICKS 6 /* * Check for Events, return TRUE if we process any * * Hack -- Handle AppleEvents if appropriate (ignore result code). */ static bool CheckEvents(bool wait) { EventRecord event; WindowPtr w; Rect r; long newsize; int ch, ck; int mc, ms, mo, mx; int i; term_data *td = NULL; huge curTicks; static huge lastTicks = 0L; /* Access the clock */ curTicks = TickCount(); /* Hack -- Allow efficient checking for non-pending events */ if (!wait && (curTicks < lastTicks + EVENT_TICKS)) return (FALSE); /* Timestamp last check */ lastTicks = curTicks; /* Let the "system" run */ SystemTask(); /* Get an event (or null) */ GetNextEvent(everyEvent, &event); /* Hack -- Nothing is ready yet */ if (event.what == nullEvent) return (FALSE); /* Analyze the event */ switch (event.what) { #if 0 case activateEvt: { w = (WindowPtr)event.message; activate(w); break; } #endif case updateEvt: { /* Extract the window */ w = (WindowPtr)event.message; /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == w) td = &data[i]; } /* Hack XXX XXX XXX */ BeginUpdate(w); EndUpdate(w); /* Redraw the window */ if (td) term_data_redraw(td); break; } case keyDown: case autoKey: { /* Extract some modifiers */ mc = (event.modifiers & controlKey) ? TRUE : FALSE; ms = (event.modifiers & shiftKey) ? TRUE : FALSE; mo = (event.modifiers & optionKey) ? TRUE : FALSE; mx = (event.modifiers & cmdKey) ? TRUE : FALSE; /* Keypress: (only "valid" if ck < 96) */ ch = (event.message & charCodeMask) & 255; /* Keycode: see table above */ ck = ((event.message & keyCodeMask) >> 8) & 255; /* Command + "normal key" -> menu action */ if (mx && (ck < 64)) { /* Hack -- Prepare the menus */ setup_menus(); /* Mega-Hack -- allow easy exit if nothing to save */ if (!character_generated && (ch=='Q' || ch=='q')) ch = 'e'; /* Run the Menu-Handler */ menu(MenuKey(ch)); /* Turn off the menus */ HiliteMenu(0); /* Done */ break; } /* Hide the mouse pointer */ ObscureCursor(); /* Normal key -> simple keypress */ if (ck < 64) { /* Enqueue the keypress */ Term_keypress(ch); } /* Hack -- normal "keypad keys" -> special keypress */ else if (!mc && !ms && !mo && !mx && (ck < 96)) { /* Hack -- "enter" is confused */ if (ck == 76) ch = '\n'; /* Send control-caret as a trigger */ Term_keypress(30); /* Send the "ascii" keypress */ Term_keypress(ch); } /* Bizarre key -> encoded keypress */ else if (ck <= 127) { /* Hack -- introduce with control-underscore */ Term_keypress(31); /* Send some modifier keys */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (mo) Term_keypress('O'); if (mx) Term_keypress('X'); /* Hack -- Downshift and encode the keycode */ Term_keypress('0' + (ck - 64) / 10); Term_keypress('0' + (ck - 64) % 10); /* Hack -- Terminate the sequence */ Term_keypress(13); } break; } case mouseDown: { int code; int window; /* Analyze click location */ code = FindWindow(event.where, &w); /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == w) { td = &data[i]; window = i; } } /* Analyze */ switch (code) { case inMenuBar: { setup_menus(); menu(MenuSelect(event.where)); HiliteMenu(0); break; } case inSysWindow: { SystemClick(&event, w); break; } case inDrag: { Point p; WindowPtr old_win; r = qd.screenBits.bounds; r.top += 20; /* GetMBarHeight() XXX XXX XXX */ InsetRect(&r, 4, 4); DragWindow(w, event.where, &r); /* Oops */ if (!td) break; /* Save */ old_win = active; /* Activate */ activate(td->w); /* Analyze */ p.h = td->w->portRect.left; p.v = td->w->portRect.top; LocalToGlobal(&p); td->r.left = p.h; td->r.top = p.v; /* Restore */ activate(old_win); /* Apply and Verify */ term_data_check_size(td); break; } case inGoAway: { /* Oops */ if (!td) break; /* Track the go-away box */ if (TrackGoAway(w, event.where)) { /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ HideWindow(td->w); } break; } case inGrow: { int x, y; term *old = Term; /* Oops */ if (!td) break; /* Fake rectangle */ r.left = 20 * td->tile_wid + td->size_ow1; r.right = 400 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1; r.top = 1 * td->tile_hgt + td->size_oh1; r.bottom = 150 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1; /* Grow the rectangle */ newsize = GrowWindow(w, event.where, &r); /* Handle abort */ if (!newsize) break; /* Extract the new size in pixels */ y = HiWord(newsize) - td->size_oh1 - td->size_oh2; x = LoWord(newsize) - td->size_ow1 - td->size_ow2; /* Extract a "close" approximation */ td->rows = y / td->tile_hgt; td->cols = x / td->tile_wid; /* Apply and Verify */ term_data_check_size(td); /* Activate */ Term_activate(td->t); /* Hack -- Resize the term */ (void) Term_resize(td->cols, td->rows); /* Resize and Redraw */ term_data_resize(td); /* Restore */ Term_activate(old); break; } case inContent: { SelectWindow(w); break; } } break; } /* Disk Event -- From "Maarten Hazewinkel" */ case diskEvt: { /* check for error when mounting the disk */ if (HiWord(event.message) != noErr) { Point p = {120, 120}; DILoad(); DIBadMount(p, event.message); DIUnload(); } break; } /* OS Event -- From "Maarten Hazewinkel" */ case osEvt: { switch ((event.message >> 24) & 0x000000FF) { case suspendResumeMessage: /* Resuming: activate the front window */ if (event.message & resumeFlag) { SetPort(FrontWindow()); SetCursor(&qd.arrow); } /* Suspend: deactivate the front window */ else { /* Nothing */ } break; } break; } #ifdef USE_SFL_CODE /* From "Steve Linberg" and "Maarten Hazewinkel" */ case kHighLevelEvent: { /* Process apple events */ if (AEProcessAppleEvent(&event) != noErr) { plog("Error in Apple Event Handler!"); } /* Handle "quit_when_ready" */ if (quit_when_ready) { /* Forget */ quit_when_ready = FALSE; /* Do the menu key */ menu(MenuKey('q')); /* Turn off the menus */ HiliteMenu(0); } /* Handle "open_when_ready" */ handle_open_when_ready(); break; } #endif } /* Something happened */ return (TRUE); } /*** Some Hooks for various routines ***/ /* * Mega-Hack -- emergency lifeboat */ static vptr lifeboat = NULL; /* * Hook to "release" memory */ static vptr hook_rnfree(vptr v, huge size) { #pragma unused (size) #ifdef USE_MALLOC /* Alternative method */ free(v); #else /* Dispose */ DisposePtr(v); #endif /* Success */ return (NULL); } /* * Hook to "allocate" memory */ static vptr hook_ralloc(huge size) { #ifdef USE_MALLOC /* Make a new pointer */ return (malloc(size)); #else /* Make a new pointer */ return (NewPtr(size)); #endif } /* * Hook to handle "out of memory" errors */ static vptr hook_rpanic(huge size) { #pragma unused (size) vptr mem = NULL; /* Free the lifeboat */ if (lifeboat) { /* Free the lifeboat */ DisposePtr(lifeboat); /* Forget the lifeboat */ lifeboat = NULL; /* Mega-Hack -- Warning */ mac_warning("Running out of Memory!\rStop whatever you're doing,\rSAVE, and quit and re-open.\rEmail rftb2@cam.ac.uk error 67m"); /* Mega-Hack -- Never leave this function */ while (TRUE) CheckEvents(TRUE); } /* Mega-Hack -- Crash */ return (NULL); } /* * Hook to tell the user something important */ static void hook_plog(cptr str) { /* Warning message */ mac_warning(str); } /* * Hook to tell the user something, and then quit */ static void hook_quit(cptr str) { /* Warning if needed */ if (str) mac_warning(str); /* Write a preference file */ save_pref_file(); /* All done */ ExitToShell(); } /* * Hook to tell the user something, and then crash */ static void hook_core(cptr str) { /* XXX Use the debugger */ /* DebugStr(str); */ /* Warning */ if (str) mac_warning(str); /* Warn, then save player */ mac_warning("Fatal error.\rI will now attempt to save and quit."); /* Attempt to save */ if (!save_player()) mac_warning("Warning -- save failed!"); /* Quit */ quit(NULL); } /*** Main program ***/ /* * Init some stuff * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "Macintosh Save Bug" by using "absolute" path names, since on * System 7 machines anyway, the "current working directory" often * "changes" due to background processes, invalidating any "relative" * path names. Note that the Macintosh is limited to 255 character * path names, so be careful about deeply embedded directories... * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "missing lib folder bug" by allowing the user to help find the * "lib" folder by hand if the "application folder" code fails... */ static void init_stuff(void) { int i; short vrefnum; long drefnum; long junk; SFTypeList types; SFReply reply; Rect r; Point topleft; char path[1024]; /* Fake rectangle */ r.left = 0; r.top = 0; r.right = 344; r.bottom = 188; /* Center it */ center_rect(&r, &qd.screenBits.bounds); /* Extract corner */ topleft.v = r.top; topleft.h = r.left; /* Default to the "lib" folder with the application */ refnum_to_name(path, app_dir, app_vol, (char*)("\plib:")); /* Check until done */ while (1) { /* Prepare the paths */ init_file_paths(path); /* Build the filename */ path_make(path, ANGBAND_DIR_FILE, "news.txt"); /* Attempt to open and close that file */ if (0 == fd_close(fd_open(path, O_RDONLY))) break; /* Warning */ plog_fmt("Unable to open the '%s' file.", path); /* Warning */ plog("The Angband 'lib' folder is probably missing or misplaced."); /* Warning */ plog("Please 'open' any file in any sub-folder of the 'lib' folder."); /* Allow "text" files */ types[0] = 'TEXT'; /* Allow "save" files */ types[1] = 'SAVE'; /* Allow "data" files */ types[2] = 'DATA'; /* Get any file */ SFGetFile(topleft, "\p", NULL, 3, types, NULL, &reply); /* Allow cancel */ if (!reply.good) quit(NULL); /* Extract textual file name for given file */ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk); refnum_to_name(path, drefnum, vrefnum, (char*)reply.fName); /* Hack -- Remove the "filename" */ i = strlen(path) - 1; while ((i > 0) && (path[i] != ':')) i--; if (path[i] == ':') path[i+1] = '\0'; /* Hack -- allow "lib" folders */ if (suffix(path, "lib:")) continue; /* Hack -- Remove the "sub-folder" */ i = i - 1; while ((i > 1) && (path[i] != ':')) i--; if (path[i] == ':') path[i+1] = '\0'; } } /* * Macintosh Main loop */ int main(void) { EventRecord tempEvent; int numberOfMasters = 10; /* Increase stack space by 64K */ SetApplLimit(GetApplLimit() - 65536L); /* Stretch out the heap to full size */ MaxApplZone(); /* Get more Masters */ while (numberOfMasters--) MoreMasters(); /* Set up the Macintosh */ InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); /* TEInit(); */ InitDialogs(NULL); InitCursor(); /* Flush events */ FlushEvents(everyEvent, 0); /* Flush events some more (?) */ (void)EventAvail(everyEvent, &tempEvent); (void)EventAvail(everyEvent, &tempEvent); (void)EventAvail(everyEvent, &tempEvent); #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ # if defined(powerc) || defined(__powerc) /* Assume System 7 */ /* Assume Color Quickdraw */ # else /* Block */ if (TRUE) { OSErr err; long versionNumber; /* Check the Gestalt */ err = Gestalt(gestaltSystemVersion, &versionNumber); /* Check the version */ if ((err != noErr) || (versionNumber < 0x0700)) { quit("You must have System 7 to use this program."); } } /* Block */ if (TRUE) { SysEnvRec env; /* Check the environs */ if (SysEnvirons(1, &env) != noErr) { quit("The SysEnvirons call failed!"); } /* Check for System Seven Stuff */ if (env.systemVersion < 0x0700) { quit("You must have System 7 to use this program."); } /* Check for Color Quickdraw */ if (!env.hasColorQD) { quit("You must have Color Quickdraw to use this program."); } } # endif #endif /* ANGBAND_LITE_MAC */ #ifdef USE_SFL_CODE /* Obtain a "Universal Procedure Pointer" */ AEH_Start_UPP = NewAEEventHandlerProc(AEH_Start); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, AEH_Start_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Quit_UPP = NewAEEventHandlerProc(AEH_Quit); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, AEH_Quit_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Print_UPP = NewAEEventHandlerProc(AEH_Print); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, AEH_Print_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Open_UPP = NewAEEventHandlerProc(AEH_Open); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, AEH_Open_UPP, 0L, FALSE); #endif /* Find the current application */ SetupAppDir(); #if defined(MACINTOSH) && !defined(applec) /* Mark ourself as the file creator */ _fcreator = 'zNG5'; /* Default to saving a "text" file */ _ftype = 'TEXT'; #endif #if defined(__MWERKS__) /* Obtian a "Universal Procedure Pointer" */ ynfilterUPP = NewModalFilterProc(ynfilter); #endif /* Hook in some "z-virt.c" hooks */ rnfree_aux = hook_rnfree; ralloc_aux = hook_ralloc; rpanic_aux = hook_rpanic; /* Hooks in some "z-util.c" hooks */ plog_aux = hook_plog; quit_aux = hook_quit; core_aux = hook_core; /* Show the "watch" cursor */ SetCursor(*(GetCursor(watchCursor))); /* Prepare the menubar */ init_menubar(); /* Prepare the windows */ init_windows(); /* Hack -- process all events */ while (CheckEvents(TRUE)) /* loop */; /* Reset the cursor */ SetCursor(&qd.arrow); /* Mega-Hack -- Allocate a "lifeboat" */ lifeboat = NewPtr(16384); /* Note the "system" */ ANGBAND_SYS = "mac"; /* Initialize */ init_stuff(); /* Initialize */ init_angband(); /* Hack -- process all events */ while (CheckEvents(TRUE)) /* loop */; /* We are now initialized */ initialized = TRUE; /* Handle "open_when_ready" */ handle_open_when_ready(); /* Prompt the user */ prtf(15, 23, "[Choose 'New' or 'Open' from the 'File' menu]"); /* Flush the prompt */ Term_fresh(); /* Hack -- Process Events Forever */ while (TRUE) CheckEvents(TRUE); } zangband/src/main-ros.c0000644000000000000000000055126610250356274014043 0ustar rootroot/* * File: main-ros.c * * Abstract: Support for RISC OS versions of Angband, including support * for multitasking and dynamic areas. * * Authors: Musus Umbra, Andrew Sidwell, Ben Harrison, and others. * * Licences: Angband licence. */ #ifdef __riscos #include "angband.h" /* * Purpose: Support for RISC OS Angband 2.9.x onwards (and variants) * * NB: This code is still under continuous development - if you want to use * it for your own compilation/variant, please contact me so that I can * keep you up to date and give you support :) * * Prerequisites to compiling: * * DeskLib 2.30 or later (earlier versions may be OK though) * * An ANSI C compiler (tested with Acorn's C/C++ and GCC, but should be OK * with any decent compiler) * * My binary distribution (for the templates and other bits) * * Note: * The following symbols are *required* and *must* be defined properly. */ /* * VARIANT & VERSION * These two get variant and version data from Angband itself; older * variants may not have these defined and will have to be altered. */ #define VARIANT VERSION_NAME #define VERSION VERSION_STRING /* * PORTVERSION * This is the port version; it appears in the infobox. */ #define PORTVERSION "1.29-dev (2003-07-27)" /* * RISCOS_VARIANT * This must match the entry in the !Variant Obey file, and it must only * contain characters that are valid as part of a RISC OS path variable. * [eg. "Yin-Yangband" is not okay, "EyAngband" is.] */ #define RISCOS_VARIANT "Zangband" /* * AUTHORS * For the info box. [eg. "Ben Harrison"] */ #define AUTHORS "Zangband dev team" /* * PORTERS * For the info box. [eg. "Musus Umbra"] */ #define PORTERS "A. Sidwell" /* * ICONNAME * Iconbar icon sprite name eg. "!angband". Note that this must be a valid * sprite name; it may need modifying for long variant names. */ #define ICONNAME "!"RISCOS_VARIANT /* * PDEADCHK * This should expand to an expression that is true if the player is dead. * [eg. (p_ptr->is_dead) for Angband or (!alive || dead) for some Zangbands] */ #define PDEADCHK (p_ptr->state.is_dead) /* * The following symbols control the (optional) file-cache: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * NB: Variants that don't repeatedly read any files whilst running * (eg. vanilla, sang, etc) should NOT define USE_FILECACHE, etc. as * it causes a non-negligable amount of code to be compiled in. * * NB: The file-cache functions require that some code in files.c is modified * to use the cached_* functions. This should be utterly trivial. * * NB: The returned handle from cached_fopen() is almost certainly *NOT* * a |FILE*| (although it may be if the cache cannot accomodate the file). * * Therefore, you *MUST* ensure that any file opened with cached_fopen() * is only ever accessed via cached_fgets() and cached_fclose(). * * Failure to do so will result in, ahem, unpleasantness. Extreme * unpleasantness. "Him fall down, go boom." * * This /may/ change in the near future (ie. to apply caching in a * transparent manner), so do keep a backup of files.c (and any other files * you modify). You always keep backups anyway, don't you? Don't you?! */ /* * USE_FILECACHE * if defined then some caching functions will be compiled for use by the * various get_rnd_line(), etc. in files.c. This could be used in a * variety of places that read data repeatedly, but it's up to you to * implement it then. */ /* #define USE_FILECACHE */ /* * SMART_FILECACHE * This causes lines beginning with '#' (and blank lines) to be discarded * when caching files. This should help Zangband 2.2.5+ but could cause * trouble for other variants. If defined, then smart file caching will be * on by default. */ /* #define SMART_FILECACHE */ /* * ABBR_FILECACHE * ABBR_FILECACHE causes data read into file-cache to be compressed (using a * simple set of abbreviations) by default. This can be overridden using a * command line option. If this symbol is not defined then no compression * code will be compiled and the user option will be ignored/unavailable. */ /* #define ABBR_FILECACHE */ /* * Note: * The following symbols control debugging information. */ /* * FE_DEBUG_INFO * If defined, some functions will be compiled to display some info. on the * state of the front-end (accessible) from the '!' user menu. * * NB: For actual releases you should NOT define this symbol since it causes * a non-negligable amount of code/data to be sucked in. */ /* #define FE_DEBUG_INFO */ /* * USE_DA * If defined, it enables the use of dynamic areas (these are still only * used when the !Variant file allows it). It is likely that this option * will eventually be removed altogether as there is no major advantege * to using DAs over just using the Wimpslot. */ #define USE_DA /* Constants, etc. ---------------------------------------------------------*/ /* Deal with any weird file-caching symbols */ #ifndef USE_FILECACHE # undef ABBR_FILECACHE # undef SMART_FILECACHE #endif /* Maximum terminals */ #define MAX_TERM_DATA 8 /* Menu entry numbers */ #define IBAR_MENU_INFO 0 #define IBAR_MENU_SAVE 1 #define IBAR_MENU_FULLSCREEN 2 #define IBAR_MENU_GAMMA 3 #define IBAR_MENU_SOUND 4 #define IBAR_MENU_WINDOWS 5 #define IBAR_MENU_SAVECHOICES 6 #define IBAR_MENU_QUIT 7 #define TERM_MENU_INFO 0 #define TERM_MENU_SAVE 1 #define TERM_MENU_FONT 2 #define TERM_MENU_WINDOWS 3 /* Icon numbers */ #define SND_VOL_SLIDER 0 #define SND_VOL_DOWN 1 #define SND_VOL_UP 2 #define SND_ENABLE 3 #define GAMMA_ICN 0 #define GAMMA_DOWN 1 #define GAMMA_UP 2 #define SAVE_ICON 2 #define SAVE_PATH 1 #define SAVE_OK 0 #define SAVE_CANCEL 3 /* Position and size of the colours strip in the gamma window */ #define GC_XOFF 20 #define GC_YOFF -14 #define GC_WIDTH 512 #define GC_HEIGHT 72 /* Maximum and minimum allowed volume levels */ #define SOUND_VOL_MIN 16 #define SOUND_VOL_MAX 176 /*--------------------------------------------------------------------------*/ #undef rename #undef remove #include "Desklib:Event.h" #include "Desklib:EventMsg.h" #include "Desklib:Template.h" #include "Desklib:Window.h" #include "Desklib:Handler.h" #include "Desklib:Screen.h" #include "Desklib:Menu.h" #include "Desklib:Msgs.h" #include "Desklib:Icon.h" #include "Desklib:Resource.h" #include "Desklib:SWI.h" #include "Desklib:Time.h" #include "Desklib:Sound.h" #include "Desklib:KeyCodes.h" #include "Desklib:Kbd.h" #include "Desklib:GFX.h" #include "Desklib:ColourTran.h" #include "Desklib:Error.h" #include "Desklib:Coord.h" #include "Desklib:Slider.h" #include "Desklib:Hourglass.h" #include "Desklib:Save.h" #include "Desklib:Sprite.h" #include "Desklib:KernelSWIs.h" #include #include #include #include #include #include #include #include /*--------------------------------------------------------------------------*/ /* | We use the hourglass around calls to Wimp_Poll in an attempt to stop | users thinking that the game has 'hung'. | Kamband/Zangband and the Borg in particular can have quite long delays at | times. */ #define Start_Hourglass \ { if ( use_glass && !glass_on ) { glass_on=1; Hourglass_Start(50); } } #define Stop_Hourglass \ { if ( glass_on ) { glass_on=0; Hourglass_Off(); } } /*--------------------------------------------------------------------------*/ /* Types */ /*--------------------------------------------------------------------------*/ /* | A ZapRedraw block */ typedef struct { union { unsigned int value; struct { unsigned int vdu:1; unsigned int double_height:1; unsigned int extension:1; unsigned int padding:29; } bits; } r_flags; int r_minx; /* min x of redraw in pixels from LHS, incl */ int r_miny; /* min y of redraw in pixels from top, incl */ int r_maxx; /* max x of redraw in pixels from LHS, excl */ int r_maxy; /* max y of redraw in pixels from top, excl */ void *r_screen; /* DSA: address of screen to write to (0=>read) */ int r_bpl; /* DSA: bytes per raster line */ int r_bpp; /* log base 2 of bits per pixel */ int r_charw; /* width of a character in pixels */ int r_charh; /* height of a character in pixels */ void *r_caddr; /* DSA: ->character cache | VDU: ->font name */ int r_cbpl; /* DSA: #bytes/character line | VDU: x OS offset */ int r_cbpc; /* DSA: #bytes/character | VDU: y OS offset */ int r_linesp; /* line spacing (pixels) */ void *r_data; /* -> text to display */ int r_scrollx; /* see Redraw dox */ int r_scrolly; /* see Redraw dox */ void *r_palette; /* -> palette lookup table */ int r_for; /* foreground colour at start of line */ int r_bac; /* background colour at start of line */ void *r_workarea; /* -> word aligned workspace */ int r_magx; /* log2 x OS coords per pixel */ int r_magy; /* log2 y OS coords per pixel */ int r_xsize; /* width of screen in pixels */ int r_ysize; /* height of screen in pixels */ int r_mode; /* current screen mode */ } ZapRedrawBlock; /* | We cache font data using an array of 'font handles' (since there is a | known maximum no. of fonts required). | This is what a font 'handle' looks like: */ typedef struct { char *name; /* font name */ int usage; /* usage count */ int w, h; /* width, height */ int f, l; /* first and last character defined */ void *bpp_1; /* source bitmap */ void *bpp_n; /* bitmap for the current screen mode */ } ZapFont; /* | A struct to hold all the data relevant to a term window */ typedef struct { term t; /* The Term itself */ window_handle w; /* Window handle */ ZapFont *font; /* Font */ wimp_box changed_box; /* Area out of date */ struct { wimp_point pos; /* Cursor position */ BOOL visible; /* visibility flag */ } cursor; char name[12]; /* Name to give menus opened from the term */ int def_open; /* Open by default? */ wimp_box def_pos; /* default position */ wimp_point def_scroll; /* default scroll offset */ int unopened; /* Has this window not been opened yet? */ } term_data; /*--------------------------------------------------------------------------*/ /* ZapRedraw SWI numbers */ /*--------------------------------------------------------------------------*/ #define SWI_ZapRedraw_ 0x48480 #define SWI_ZapRedraw_RedrawArea (SWI_ZapRedraw_ + 0x00) #define SWI_ZapRedraw_GetPaletteEntry (SWI_ZapRedraw_ + 0x01) #define SWI_ZapRedraw_RedrawRaster (SWI_ZapRedraw_ + 0x02) #define SWI_ZapRedraw_ConvertBitmap (SWI_ZapRedraw_ + 0x03) #define SWI_ZapRedraw_PrepareDataLine (SWI_ZapRedraw_ + 0x04) #define SWI_ZapRedraw_AddCursor (SWI_ZapRedraw_ + 0x05) #define SWI_ZapRedraw_FindCharacter (SWI_ZapRedraw_ + 0x06) #define SWI_ZapRedraw_MoveBytes (SWI_ZapRedraw_ + 0x07) #define SWI_ZapRedraw_CachedCharSize (SWI_ZapRedraw_ + 0x08) #define SWI_ZapRedraw_ConvBitmapChar (SWI_ZapRedraw_ + 0x09) #define SWI_ZapRedraw_CreatePalette (SWI_ZapRedraw_ + 0x0a) #define SWI_ZapRedraw_InsertChar (SWI_ZapRedraw_ + 0x0b) #define SWI_ZapRedraw_ReadSystemChars (SWI_ZapRedraw_ + 0x0c) #define SWI_ZapRedraw_ReverseBitmaps (SWI_ZapRedraw_ + 0x0d) #define SWI_ZapRedraw_ReadVduVars (SWI_ZapRedraw_ + 0x0e) #define SWI_ZapRedraw_GetRectangle (SWI_ZapRedraw_ + 0x0f) #define SWI_ZapRedraw_AddVduBitmaps (SWI_ZapRedraw_ + 0x10) #define SWI_ZapRedraw_CacheFontChars (SWI_ZapRedraw_ + 0x11) #define SWI_ZapRedraw_SpriteSize (SWI_ZapRedraw_ + 0x12) #define SWI_ZapRedraw_RedrawWindow (SWI_ZapRedraw_ + 0x13) /* | Other SWI numbers that aren't defined in DeskLib's SWI.h: */ #define SWI_OS_ScreenMode 0x65 #define SWI_OS_DynamicArea 0x66 #define SWI_ColourTrans_ReturnColourNumber 0x40744 #define SWI_Wimp_ReportError 0x400df #define SWI_PlayIt_Volume 0x4d146 /*--------------------------------------------------------------------------* | File scope variables | *--------------------------------------------------------------------------*/ static int ftype = 0xffd; /* hack so saved games get the right type */ static int filehandle[16]; /* we keep track of open files with this */ static int openfiles = 0; /* how many files are currently open */ /* | Paths we use... */ static char resource_path[260] = ""; /* Path pointng to "!Angband.Lib." */ static char scrap_path[260] = ""; /* Path to create scrap files on */ static char choices_file[3][260] = { "", "", "" }; /* Choices paths (read/write, mirror, read) */ static char alarm_file[2][260] = { "", "" }; /* Alarm choices paths (read/write, mirror, read) */ /* | So we can use something more meaningful later... | NB: Mirror is only meaningful for Choices and we don't | even reserve space for alarm_file[CHFILE_MIRROR]. */ #define CHFILE_WRITE 0 #define CHFILE_READ 1 #define CHFILE_MIRROR 2 /* | Other 'globals': */ static int initialised = 0; /* Used to determine whether to try to save */ static int game_in_progress = 0; /* if Quit (or core() is called), etc. */ static byte a_palette[256][4]; /* a copy of the raw Angband palette */ static unsigned int palette[256]; /* palette as gamma'd bbggrrxx words */ static unsigned int zpalette[256]; /* And our version for ZapRedraw */ static double gamma = 1.0; /* assume gamma of 1.0 if unspecified */ static int enable_sound = 0; /* enable sound FX */ static int sound_volume = 127; /* Full volume */ static int force_mono = 0; /* force monochrome */ static int start_fullscreen = 0; /* start up full screen (added in 1.18) */ static int hack_flush = 0; /* Should TERM_XTRA_FLUSH wait for all keys to be released? */ static int flush_scrap = 1; /* Should any scrapfiles (incl. filecache) be deleted at exit? */ static int max_file_cache_size = 64 << 10; static unsigned int vfiletype; static int allow_iclear_hack = 0; /* Allow the hideously evil Iclear workaround thing */ static int alarm_type = 0; /* is there an alarm set? */ static int alarm_h = 0, alarm_m = 0; /* alarm time (midnight) */ static char alarm_message[80] = "Time for bed!"; /* the message to give */ static int alarm_disp = 0; /* is the alarm being displayed? */ static int alarm_beep = 0; /* should be beep? */ static const char *alarm_types[] = { "Off", "On (one-shot)", "On (repeating)", "On (one-shot)" }; static unsigned int alarm_lastcheck = 0; /* A little macro to save some typing later: */ #define COLOUR_CHANGED(x) \ ( (angband_color_table[x][1]!=a_palette[x][1]) || \ (angband_color_table[x][2]!=a_palette[x][2]) || \ (angband_color_table[x][3]!=a_palette[x][3]) ) static int got_caret = 0; /* Do we own the caret? */ static int key_pressed = 0; /* 'Key has been pressed' Flag */ static int use_glass = 1; /* use the hourglass between WimpPolls? */ static int glass_on = 1; /* is the hourglass on? */ static int user_menu_active = FALSE; /* set to TRUE when the user menu is active */ /* Font system variables */ static ZapFont fonts[MAX_TERM_DATA + 1]; /* The +1 is for the system font */ /* The system font is always font 0 */ #define SYSTEM_FONT (&(fonts[0])) /* Term system variables */ static term_data data[MAX_TERM_DATA]; /* One per term */ #ifndef FULLSCREEN_ONLY static char r_data[24 * (80 * 5 + 4) + 25 * 4]; /* buffer for ZapRedraw data */ /* Wimp variables */ static icon_handle ibar_icon; /* Iconbar icon handle */ static window_handle info_box; /* handle of the info window */ static window_handle gamma_win; /* gamma correction window */ static window_handle sound_win; /* sound options window */ static window_handle save_box; /* The savebox */ static menu_ptr ibar_menu; /* Iconbar menu */ static menu_ptr term_menu; /* Term window menu */ static menu_ptr wind_menu; /* windows (sub) menu */ static menu_ptr font_menu; /* Font (sub)menu */ static save_saveblock *saveblk = NULL; /* For the save box */ /* List of Wimp messages we want to be given */ static int message_list[] = { message_MODECHANGE, message_PALETTECHANGE, /* For the savebox */ message_MENUWARN, message_DATASAVEACK, message_PREQUIT, 0 }; static term_data *menu_term; /* term the last menu was opened for */ #endif /* FULLSCREEN_ONLY */ static ZapRedrawBlock zrb; /* a redraw block */ /* Cursor colour */ #define CURSOR_COLOUR 255 /* Cursor's Angband colour */ #define CURSOR_RGB 0x00ffff00 /* if undefined, use bbggrrxx */ static int cursor_rgb = -1; /* colour to use for cursor */ static int fullscreen_mode = 0; /* screen mode in use */ static int old_screenmode = 0; /* Mode we started out in */ static int *fullscreen_font = 0; /* font data for fullscreen use */ static int *fullscreen_base = 0; /* base address of screen */ static int fullscreen_height; /* height of the fullscreen font */ static int fullscreen_topline; /* raster offset of fullscreen */ #define KEYPRESS_QUIT 0x1cc /* F12 gets back to the desktop */ #define TERM_TOPLINE_HR 32 /* vertical pixel offset in mode 27 */ #define TERM_TOPLINE_LR 16 /* vertical pixel offset in mode 12 */ #define TIME_LINE 26 /* Line to display the clock on */ /* text to display at the bottom left of the fullscreen display */ static const char *fs_quit_key_text = "Press f12 to return to the desktop"; static const char *alarm_cancel_text = "(Press ^Escape to cancel the alarm)"; /* Debugging flags, etc. */ static int log_g_malloc = 0; /* Log calls to ralloc, etc */ static int show_sound_alloc = 0; /* Log sound mappings, etc */ /* Activate file caching? */ #ifdef USE_FILECACHE static int use_filecache = TRUE; #else static int use_filecache = FALSE; #endif /* Cripple some things to save memory */ static int minimise_memory = 0; /* Forward declarations of some of the Full Screen Mode stuff */ static void enter_fullscreen_mode(void); static void leave_fullscreen_mode(void); static void set_keys(int claim); /* Forwards declarations of the sound stuff */ static void initialise_sound(void); static void play_sound(int event); /* Forward declarations of Term hooks, etc. */ static void Term_init_acn(term *t); static errr Term_user_acn(int n); #ifndef FULLSCREEN_ONLY static errr Term_curs_acn(int x, int y); static errr Term_text_acn(int x, int y, int n, byte a, cptr s); static errr Term_xtra_acn(int n, int v); static errr Term_wipe_acn(int x, int y, int n); static errr Term_xtra_acn_check(void); static errr Term_xtra_acn_event(void); static errr Term_xtra_acn_react(void); #endif /* FULLSCREEN_ONLY */ static errr Term_curs_acnFS(int x, int y); static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s); static errr Term_xtra_acnFS(int n, int v); static errr Term_wipe_acnFS(int x, int y, int n); static errr Term_xtra_acn_checkFS(void); static errr Term_xtra_acn_clearFS(void); static errr Term_xtra_acn_eventFS(void); static errr Term_xtra_acn_reactFS(int force); #ifdef USE_DA /* Forward declarations of the memory stuff */ static void init_memory(int, int); #endif /* Forward declarations of the alarm stuff */ static void check_alarm(void); #ifndef FULLSCREEN_ONLY static void trigger_alarm_desktop(void); #endif /* FULLSCREEN_ONLY */ static void ack_alarm(void); static void write_alarm_choices(void); static void read_alarm_choices(void); /* This just shows some debugging info (if enabled with FE_DEBUG_INFO) */ static void show_debug_info(void); /* File-caching functions (if enabled at compile time) */ #ifdef USE_FILECACHE FILE *cached_fopen(char *name, char *mode); errr cached_fclose(FILE *fch); errr cached_fgets(FILE *fch, char *buffer, int max_len); #endif /* | These functions act as malloc/free, but (if possible) using memory | in the 'Game' Dynamic Area created by init_memory() | We attach these functions to the ralloc_aux and rnfree_aux hooks | that z-virt.c provides. */ #ifdef USE_DA static vptr g_malloc(huge size); static vptr g_free(vptr blk); #else #define g_malloc(size) malloc(size); #define g_free(block) free(block); #endif /* | These functions act as malloc/free, but (if possible) using memory | in the 'Fonts' Dynamic Area created by init_memory() */ #ifdef USE_DA static void* f_malloc(size_t size); static void f_free(void *blk); #else #define f_malloc(size) malloc(size); #define f_free(block) free(block); #endif /* | These two functions perpetrate great evil to stop IClear from mucking | with the cursor keys in fullscreen mode. */ static void iclear_hack(void); static void remove_iclear_hack(void); /* | We use this to locate the choices file(s)... */ static char *find_choices(int write); static char *find_choices_mirror(void); static char *find_alarmfile(int write); /* | This function is supplied as a wrapper to the save_player function. | | Its purpose is to change the filename that the game will be saved with | the leafname "!!PANIC!!" so that panic saves that break the savefile | won't overwrite the original savefile. | | To get this to work, you'll need to ammend files.c and change the call | to save_player in the panic save function(s) (search for "panic save") | to a call to save_player_panic_acn. You can declare a prototype for | the function if you like. */ extern int save_player_panic_acn(void) { char *e, *l; /* Find the final / in the savefile name */ for (l = e = savefile; *e; e++) if (*e == '/') { l = e + 1; } /* Write over the current leaf with the special panic one */ strcpy(l, "!!PANIC!!"); /* save the game */ return save_player(); } /*--------------------------------------------------------------------------*/ /* Error reporting, etc. */ /*--------------------------------------------------------------------------*/ /* Tell the user something important */ static void plog_hook(cptr str) { Msgs_Report(1, "err.plog", str); } /* Tell the user something, then quit */ static void quit_hook(cptr str) { /* str may be null */ if (str) Msgs_Report(1, "err.quit", str); exit(0); } /* Tell the user something then crash ;) */ static void core_hook(cptr str) { Msgs_Report(1, "err.core", str); if (game_in_progress && character_generated) save_player_panic_acn(); quit(NULL); } static void debug(const char *fmt, ...) { va_list ap; char buffer[260]; va_start(ap, fmt); vsprintf(buffer, fmt, ap); va_end(ap); plog(buffer); } /* static void oserror_handler( int sig ) { core(_kernel_last_oserror()->errmess); } */ /*--------------------------------------------------------------------------*/ /* File handling */ /*--------------------------------------------------------------------------*/ static int myFile_Open(const char *name, int mode) { int handle; if (SWI(2, 1, SWI_OS_Find, mode, name, /**/ &handle)) return 0; return handle; } static int myFile_Size(const char *name) { int size, type; if (SWI(2, 5, SWI_OS_File, 17, name, /**/ &type, 0, 0, 0, &size)) return -2; return type ? size : -1; } static os_error *myFile_Close(const int handle) { return SWI(2, 0, SWI_OS_Find, 0, handle); } static os_error *myFile_Seek(const int handle, const int offset) { return SWI(3, 0, SWI_OS_Args, 1, handle, offset); } static int myFile_WriteBytes(const int handle, const void *buf, const int n) { int ntf; if (SWI(4, 4, SWI_OS_GBPB, 2, handle, buf, n, /**/ NULL, NULL, NULL, &ntf)) return n; return ntf; } static int myFile_ReadBytes(const int handle, void *buf, const int n) { int ntf; if (SWI(4, 4, SWI_OS_GBPB, 4, handle, buf, n, /**/ NULL, NULL, NULL, &ntf)) return n; return ntf; } static os_error *myFile_SetType(const char *n, const int type) { return SWI(3, 0, SWI_OS_File, 18, n, type); } static int myFile_Extent(const int handle) { int ext; if (SWI(2, 3, SWI_OS_Args, 2, handle, /**/ NULL, NULL, &ext)) return -1; return ext; } /* | Determine if one file is newer than another. | | The filenames should be specified in RISC OS style. | | Returns -1 if 'a' is newer than 'b'. */ static int file_is_newer(const char *a, const char *b) { os_error *e; struct { unsigned int msw; unsigned int lsw; } a_time; struct { unsigned int msw; unsigned int lsw; } b_time; int a_type, b_type; /* Get the datestamp of the 'a' file */ e = SWI(2, 4, SWI_OS_File, /* In */ 17, (int)a, /* Out */ &a_type, /* object type */ NULL, &(a_time.msw), /* Load Addr */ &(a_time.lsw) /* Exec Addr */ ); if (e) { core(e->errmess); } /* Get the datestamp of the 'b' file */ e = SWI(2, 4, SWI_OS_File, /* In */ 17, (int)b, /* Out */ &b_type, /* object type */ NULL, &(b_time.msw), /* Load Addr */ &(b_time.lsw) /* Exec Addr */ ); if (e) { core(e->errmess); } /* If 'b' doesn't exist then 'b' is OOD. */ if (!b_type) { return -1; } /* If 'a' doesn't exist then 'b' isn't OOD. (?) */ if (!a_type) { return 0; } /* Compare the timestamps (assume that the files are typed) */ if ((a_time.msw & 0xff) >= (b_time.msw & 0xff)) if ((a_time.lsw) > (b_time.lsw)) return -1; /* OOD */ return 0; /* Not OOD */ } /* | As fprintf, but outout to all files (if their handles are non zero). | NB: void type. */ static void f2printf(FILE *a, FILE *b, const char *fmt, ...) { va_list ap; char buffer[2048]; va_start(ap, fmt); vsprintf(buffer, fmt, ap); va_end(ap); if (a) { fprintf(a, buffer); } if (b) { fprintf(b, buffer); } va_end(ap); } /*--------------------------------------------------------------------------*/ /* Clean up (ie. close files, etc). */ /*--------------------------------------------------------------------------*/ static void final_acn(void) { int i; for (i = 0; i < openfiles; i++) myFile_Close(filehandle[i]); if (fullscreen_mode) { /* Restore the screen mode */ Wimp_SetMode(old_screenmode); /* Restore the various soft keys */ set_keys(FALSE); /* | Hack: Early WIMP versions do the "Press SPACE" thing, or something | odd. It's bloody annoying, whatever it is... */ if (event_wimpversion < 300) Wimp_CommandWindow(-1); } if (flush_scrap && *scrap_path) { char tmp[512]; strcpy(tmp, scrap_path); tmp[strlen(tmp) - 1] = 0; /* Remove trailing dot */ SWI(4, 0, SWI_OS_FSControl, 27, tmp, 0, 1); /* ie. "*Wipe r~c~v~f" */ } #ifdef FULLSCREEN_ONLY Wimp_CommandWindow(-1); #endif /* FULLSCREEN_ONLY */ Stop_Hourglass; } /*--------------------------------------------------------------------------* | Various UNIX-like support funtions | *--------------------------------------------------------------------------*/ /* | Hack: determine whether filenames should be truncated to 10 chars or not. | | Needed since RO2 (and RO3 with Truncate configured off) will return | errors instead of automatically truncating long filenames. */ static int truncate_names(void) { int r1, r2; /* First, check the OS version */ OS_Byte(osbyte_READOSIDENTIFIER, 0x00, 0xff, &r1, &r2); /* Assume that we need to truncate if running under RO2 */ if (r1 == 0xa1 || r1 == 0xa2) return TRUE; /* Okay, so we've got RO3 (or later), so check the CMOS RAM */ OS_Byte(osbyte_READCMOSRAM, 28, 0, &r1, &r2); /* Bit 0 of byte 28 is the Truncate flag */ return !(r2 & 1); } /* | The PathName translation is now done by two separate functions: | unixify_name() and riscosify_name(). | | This is done because only the UNIX=>RISCOS translation should | ever affect the length of the leafname (ie. by truncating it to | 10 chars if necessary). | | Note that the two functions are identical but for the truncation | check so all that's really been done is that translate_name() now | takes an extra argument: 'trunc' that controls whether truncation | is applied, and riscosify and unixify just call translate_name(). */ static char *translate_name(const char *path, int trunc) { static char buf[260]; char c, *p; /* Copy 'path' into 'buf', swapping dots and slashes */ p = buf; /* Output position */ do { c = *path++; if (c == '/') c = '.'; else if (c == '.') c = '/'; *p++ = c; } while (c); /* Terminator /is/ copied */ /* | When saving a game, the old game is renamed as | "SavedGame.old", the new one is saved as "SavedGame.new", | "SavedGame.old" is deleted, "SavedGame.new" is renamed | as "SavedGame". This will go wrong on a Filecore based filing | system if the saved game has a leafname > 8 chars. */ if ((p = strstr(buf, "/old")) == NULL) { p = strstr(buf, "/new"); } if (!p) { ftype = 0xffd; } else { char *q = strrchr(buf, '.'); if (q) if (p - q > 6) { memmove(q + 6, p, 5); } ftype = vfiletype; } /* | Hack: Do we need to truncate the leafname? */ if (trunc) { if (truncate_names()) { char *a, *b; /* | Assume that only the leafname needs attention | (this should be true for any variant) */ for (a = b = buf; *a; a++) if (*a == '.') b = a + 1; /* | Now b points to the start of the leafname. | If the leafname is >10 chars, write over the 10th with a | terminator. */ if (strlen(b) > 10) { b[10] = 0; }; } } return buf; } extern char *riscosify_name(const char *path) { return translate_name(path, TRUE); } static char *unixify_name(const char *path) { return translate_name(path, FALSE); } /*--------------------------------------------------------------------------*/ /* Open a file [as fopen()] but translate the requested filename first */ FILE *my_fopen(const char *f, const char *m) { FILE *fp; char *n = riscosify_name(f); /* translate for RO */ /* Try to open the file */ fp = fopen(n, m); /* If it succeded and the file was opened for binary output | then set the type according to the 'ftype' hack. | NB: This will fail on some filing systems. */ if (fp && strstr(m, "wb")) { myFile_SetType(n, ftype); } return fp; } /* Close a file, a la fclose() */ void my_fclose(FILE *fp) { /* Close the file, return 1 for an error, 0 otherwise */ fclose(fp); } /* Open/Create a file */ int fd_make(cptr file, int mode) { char *real_path; int handle; /* Translate the filename into a RISCOS one */ real_path = riscosify_name(file); /* Try to OPENOUT the file (no path, error if dir or not found) */ handle = myFile_Open(real_path, 0x8f); /* Check for failure */ if (!handle) { return -1; } /* Try to set the filetype according to the ftype hack */ myFile_SetType(real_path, ftype); /* We keep track of up to 16 open files at any given time */ if (openfiles < 16) filehandle[openfiles++] = handle; return (handle); } /* Delete a file [as remove()] */ errr fd_kill(cptr file) { return remove(riscosify_name(file)) ? 1 : 0; } /* Rename a file [as rename()] */ errr fd_move(cptr old, cptr new) { char new_[260]; strcpy(new_, riscosify_name(new)); return rename(riscosify_name(old), new_) ? 1 : 0; } /* Open a file */ int fd_open(cptr path, int flags) { int handle = 0; char *real_path = riscosify_name(path); switch (flags & 0x0f) { case O_RDONLY: /* Read only */ handle = myFile_Open(real_path, 0x4f); break; case O_WRONLY: /* Write only */ case O_RDWR: /* Read/Write */ handle = myFile_Open(real_path, 0xcf); } /* Check for failure */ if (!handle) { return (-1); } /* Keep track of upto 16 open files... */ if (openfiles < 16) filehandle[openfiles++] = handle; return (handle); } /* Close a file opened with fd_make or fd_open */ errr fd_close(int handle) { int i; if (handle <= 0) { return -1; } /* Illegal handle */ /* Try to close the file */ if (myFile_Close(handle)) { return 1; } /* Mark the file as closed in our array of file handles */ openfiles--; /* Find the entry in the array (if it exists) */ for (i = 0; i < 16; i++) if (filehandle[i] == handle) { break; } /* Shuffle the remaining entries down */ for (; i < openfiles; i++) filehandle[i] = filehandle[i + 1]; return 0; /* Sucess */ } /* Read some bytes from a file */ errr fd_read(int handle, char *buf, huge nbytes) { int unread; if (handle <= 0) { return -1; } /* Illegal handle */ unread = myFile_ReadBytes(handle, buf, (int)nbytes); return unread ? 1 : 0; } /* Write some bytes to a file */ errr fd_write(int handle, const char *buf, huge nbytes) { int unwritten; if (handle <= 0) { return -1; } /* Illegal handle */ unwritten = myFile_WriteBytes(handle, (const void *)buf, (int)nbytes); return unwritten ? 1 : 0; } /* Seek in a file */ errr fd_seek(int handle, huge offset) { os_error *e; if (handle <= 0) { return -1; } /* Illegal handle */ e = myFile_Seek(handle, (int)offset); return e ? 1 : 0; } /* RISC OS provides no file locking facilities, so: */ errr fd_lock(int handle, int what) { return 0; } /* Get a temporary filename */ errr path_temp(char *buf, size_t max) { /* | New in 1.25 - use the scrap path we decided on earlier, or | fall back on tmpnam() if that fails for some reason. */ if (*scrap_path) { time_t t; int m; char tmp[512]; time(&t); for (m = 0; m < 80; m++) { sprintf(tmp, "%s0x%08x", scrap_path, (int)t + m); if (myFile_Size(tmp) == -1) { break; } } if (m < 80) { strncpy(buf, unixify_name(tmp), max); return 0; } } strncpy(buf, unixify_name(tmpnam(NULL)), max); return 0; } /* * Create a new path by appending a file (or directory) to a path * * This requires no special processing on simple machines, except * for verifying the size of the filename, but note the ability to * bypass the given "path" with certain special file-names. * * Note that the "file" may actually be a "sub-path", including * a path and a file. * * Note that this function yields a path which must be "parsed" * using the "parse" function above. */ void path_build(char *buf, int max, cptr path, cptr file) { /* Special file */ if (file[0] == '~') { /* Use the file itself */ strnfmt(buf, max, "%s", file); } /* Absolute file, on "normal" systems */ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, "")) { /* Use the file itself */ strnfmt(buf, max, "%s", file); } /* No path given */ else if (!path[0]) { /* Use the file itself */ strnfmt(buf, max, "%s", file); } /* Path and File */ else { /* Build the new path */ strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file); } } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ /* Font Functions */ /*--------------------------------------------------------------------------*/ /* | Cache the system font as fonts[0] | Returns 1 for sucess or 0 for failure. | NB: The n_bpp data is *not* cached, just the 1bpp data and font info. | Also, the usage is never affected. */ static int cache_system_font(void) { ZapFont *sys = SYSTEM_FONT; ZapRedrawBlock zrb; char work_area[16]; int i; /* Cache the system font (as fonts[0]) */ if (sys->bpp_1) { f_free(sys->bpp_1); sys->bpp_1 = 0; } sys->bpp_1 = f_malloc(8 * 256); /* 2K */ if (!sys->bpp_1) { return 0; } /* Mung so that undefined characters show up as inverted ?s */ work_area[3] = '?'; SWI(2, 0, SWI_OS_Word, 10, work_area + 3); for (i = 4; i < 12; i++) work_area[i] ^= 255; /* invert colours */ SWI(4, 0, SWI_ZapRedraw_ReverseBitmaps, 0, work_area + 4, work_area + 4, 8); for (i = 0; i < 0x20; i++) memcpy(((char *)sys->bpp_1) + i * 8, work_area + 4, 8); /* Read the system font */ zrb.r_workarea = work_area; SWI(2, 0, SWI_ZapRedraw_ReadSystemChars, sys->bpp_1, &zrb); /* Set up some little bits of info */ sys->name = (char *) ""; sys->w = sys->h = 8; sys->f = 0; sys->l = 255; return 1; } /* | Prepare the font system */ static void initialise_fonts(void) { /* Initialise the array */ memset(fonts, 0, sizeof(fonts)); /* Clear to zeroes */ /* Cache the system font */ cache_system_font(); fonts[0].usage = 0; /* No users */ } #ifndef FULLSCREEN_ONLY /* | Find a font (by name) in the array. | Returns 0 if the font isn't loaded, or a ZapFont* for it if it is. */ static ZapFont *find_font_by_name(char *name) { int i; for (i = 0; i <= MAX_TERM_DATA; i++) if (fonts[i].name) if (!strcmp(fonts[i].name, name)) return &(fonts[i]); return NULL; } /* | Find a free slot in the fonts array */ static ZapFont *find_free_font(void) { int i; for (i = 1; i <= MAX_TERM_DATA; i++) if (!fonts[i].name) { return &(fonts[i]); } return NULL; } /* | Load a font from disc and set up the header info, etc. | NB: doesn't cache the nbpp data, just the 1bpp data. | (Sets usage to 1) | Returns NULL if failed. */ static ZapFont *load_font(char *name, ZapFont *f) { int handle, extent; char path[260]; struct { char id[8]; int w, h, f, l, r1, r2; } header; char *font_path; char *t; char *real_name = name; /* need to preserve this */ /* | 1.10 - the first element of the name determines the path to load | the font from. */ /* The font paths start $ */ t = path + sprintf(path, "%s$", RISCOS_VARIANT); /* Copy the path specifier and move 'name' past it */ for (; *name != '.'; *t++ = *name++) ; /* After this, the name now points to the font name proper */ name++; /* Append the end of the path name */ strcpy(t, "$FontPath"); /* Get the path setting */ font_path = getenv(path); if (!font_path || !*font_path) strcpy(path, "null:$."); else { strcpy(path, font_path); for (t = path; *t > ' '; t++) ; if (t[-1] != '.' && t[-1] != ':') { *t++ = '.'; } *t = 0; } strcat(path, name); /* Open the file */ handle = myFile_Open(path, 0x4f); if (!handle) { return NULL; } /* Read the header */ if (myFile_ReadBytes(handle, &header, sizeof(header))) { myFile_Close(handle); return NULL; } /* Check that it's a zapfont */ if (strncmp(header.id, "ZapFont\r", 8)) { myFile_Close(handle); return NULL; } /* Calculate the size of the 1bpp data */ extent = myFile_Extent(handle) - sizeof(header); /* Allocate the storage for the 1bpp data */ f->bpp_1 = f_malloc(extent); if (!f->bpp_1) { myFile_Close(handle); return NULL; } /* Load the 1bpp data */ if (myFile_ReadBytes(handle, f->bpp_1, extent)) { f_free(f->bpp_1); f->bpp_1 = 0; myFile_Close(handle); return NULL; } /* Close the file and set the header, etc. */ myFile_Close(handle); f->name = f_malloc(strlen(real_name) + 1); if (!f->name) { f_free(f->bpp_1); f->bpp_1 = 0; return NULL; } strcpy(f->name, real_name); f->w = header.w; f->h = header.h; f->f = header.f; f->l = header.l; f->usage = 1; return f; } /* | Cache a font at a suitable number of bpp for the current mode | Returns 0 for failure, 1 for sucess. | If the call fails then the font's bpp_n entry will be NULL. */ static int cache_font_for_mode(ZapFont *f) { ZapRedrawBlock b; char work_area[128]; int size; if (!f) { return 0; } if (!f->bpp_1) { return 0; } b.r_workarea = work_area; SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b); b.r_workarea = work_area; /* Paranoia */ b.r_charh = f->h; b.r_charw = f->w; SWI(4, 4, SWI_ZapRedraw_CachedCharSize, b.r_bpp, 0, f->w, f->h, NULL, NULL, &(b.r_cbpl), &(b.r_cbpc)); size = 256 * b.r_cbpc; if (f->bpp_n) { f_free(f->bpp_n); f->bpp_n = NULL; } f->bpp_n = f_malloc(size); if (!f->bpp_n) { return 0; } b.r_workarea = work_area; /* Paranoia */ b.r_caddr = f->bpp_n; SWI(5, 0, SWI_ZapRedraw_ConvertBitmap, 0, &b, 0, 255, f->bpp_1); return 1; } /* | Stop using a font. | If the font's usage drops to zero then the font data is purged. */ static void lose_font(ZapFont *f) { if (--f->usage) { /*debug("Losing font %s (still cached)",f->name); */ return; } /*debug("Losing font %s (no longer in use)",f->name); */ f_free(f->name); f_free(f->bpp_1); if (f->bpp_n) { f_free(f->bpp_n); } memset(f, 0, sizeof(ZapFont)); } /* | Get a font. */ static ZapFont *find_font(char *name) { ZapFont *f; /* Check to see if it's already loaded */ f = find_font_by_name(name); if (f) { /*debug("Find font %s (already cached)",name); */ f->usage++; if (f == SYSTEM_FONT) { if (!cache_system_font()) core("Failed to cache system font!"); if (!cache_font_for_mode(SYSTEM_FONT)) core("Failed to cache system font!"); } return f; } /* Ok, now check to see if there's a free slot for it */ f = find_free_font(); if (!f) { return NULL; } /* Oh dear :( */ /* Load the font */ /*debug("Find font %s (loading)",name); */ f = load_font(name, f); if (f) { if (!cache_font_for_mode(f)) return NULL; return f; } return NULL; } /* | Cache the n_bpp data for all the active fonts (including system) */ static void cache_fonts(void) { int i; for (i = 0; i <= MAX_TERM_DATA; i++) if (fonts[i].name) if (!cache_font_for_mode(&(fonts[i]))) core("Failed to (re)cache font tables"); } typedef struct { int load, exec, size, attr, type; char name[4]; /* Actual size is unknown */ } osgbpb10_block; static char *leafname(char *path) { char *s = path + strlen(path); while (--s > path) if (*s == '.' || *s == ':') { return s + 1; } return path; } /* | NB: This function is recursive. */ static menu_ptr make_zfont_menu(char *dir) { int entries, entry; int read, offset; unsigned int max_width; menu_ptr m; menu_item *mi; char *temp; osgbpb10_block *item_info; char buffer[1024]; /* 1Kb buffer */ /* Count the entries in the directory */ entries = read = offset = 0; while (offset != -1) { if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0, NULL, NULL, NULL, &read, &offset)) { offset = -1; read = 0; } entries += read; } if (!entries) { return NULL; } /* Allocate a big enough area of storage for the number of entries */ m = f_malloc(sizeof(menu_block) + entries * sizeof(menu_item)); if (!m) { return NULL; } memset(m, 0, sizeof(menu_block) + entries * sizeof(menu_item)); /* Set up the menu header */ strncpy(m->title, leafname(dir), 12); m->titlefore = 7; m->titleback = 2; m->workfore = 7; m->workback = 0; m->height = 44; m->gap = 0; mi = (menu_item *) (((int)m) + sizeof(menu_block)); max_width = strlen(m->title); entry = 0; /* Read the entries */ read = offset = 0; while (offset != -1) { if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0, NULL, NULL, NULL, &read, &offset)) { offset = -1; read = 0; /*free(m);return NULL; */ } item_info = (osgbpb10_block *) buffer; /* Create a menu item for each entry read (if it fits) */ while (read-- > 0) { switch (item_info->type) { case 1: /* File */ if ((item_info->load & 0xffffff00) == 0xfffffd00) { /* Data file */ mi[entry].submenu.value = -1; mi[entry].iconflags.data.text = 1; mi[entry].iconflags.data.filled = 1; mi[entry].iconflags.data.foreground = 7; mi[entry].iconflags.data.background = 0; strncpy(mi[entry].icondata.text, item_info->name, 12); if (strlen(mi[entry].icondata.text) > max_width) max_width = strlen(mi[entry].icondata.text); entry++; } break; case 2: /* Directory */ case 3: /* Image */ { menu_ptr sub; char new_path[260]; if (strchr(":.", dir[strlen(dir) - 1])) sprintf(new_path, "%s%s", dir, item_info->name); else sprintf(new_path, "%s.%s", dir, item_info->name); sub = make_zfont_menu(new_path); if (sub) { /* Add the submenu */ mi[entry].submenu.menu = sub; mi[entry].iconflags.data.text = 1; mi[entry].iconflags.data.filled = 1; mi[entry].iconflags.data.foreground = 7; mi[entry].iconflags.data.background = 0; strncpy(mi[entry].icondata.text, item_info->name, 12); if (strlen(mi[entry].icondata.text) > max_width) max_width = strlen(mi[entry].icondata.text); entry++; } } break; } temp = ((char *)item_info) + 20; while (*temp++) ; item_info = (osgbpb10_block *) ((((int)temp) + 3) & ~3); } } if (entry) { m->width = (max_width + 2) * 16; mi[entry - 1].menuflags.data.last = 1; /* | We could possibly realloc() the storage to fit the | actual no. of entries read, but this is probably more | trouble than it's worth. */ } else { /* | No point in returning an empty menu. */ f_free(m); m = NULL; } return m; } #endif /* FULLSCREEN_ONLY */ /*--------------------------------------------------------------------------*/ /* | Initialise the palette stuff */ static void initialise_palette(void) { memset(a_palette, 0, sizeof(a_palette)); memset(palette, 0, sizeof(palette)); memset(zpalette, 0, sizeof(zpalette)); } #ifndef FULLSCREEN_ONLY /* | Cache the ZapRedraw palette */ static void cache_palette(void) { static ZapRedrawBlock b; char workspace[128]; int i; static double old_gamma = -1.0; /* Idiocy check: */ if (gamma < 0.01) { plog("Internal error: Attempt to apply zero gamma - recovering..."); gamma = 1.00; } if (gamma != old_gamma) { memset(a_palette, 0, sizeof(a_palette)); old_gamma = gamma; } /* Go through the palette updating any changed values */ for (i = 0; i < 256; i++) { if (COLOUR_CHANGED(i)) { int r, g, b; r = (int)(255.0 * pow(angband_color_table[i][1] / 255.0, 1.0 / gamma)); g = (int)(255.0 * pow(angband_color_table[i][2] / 255.0, 1.0 / gamma)); b = (int)(255.0 * pow(angband_color_table[i][3] / 255.0, 1.0 / gamma)); palette[i] = (b << 24) | (g << 16) | (r << 8); a_palette[i][1] = angband_color_table[i][1]; a_palette[i][2] = angband_color_table[i][2]; a_palette[i][3] = angband_color_table[i][3]; } } cursor_rgb = palette[CURSOR_COLOUR]; /* Cache the ZapRedraw palette for it */ b.r_workarea = workspace; if (b.r_mode != screen_mode) SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b); SWI(5, 0, SWI_ZapRedraw_CreatePalette, 2, &b, palette, zpalette, 256); } /*--------------------------------------------------------------------------*/ /* | Functions for dealing with the SaveBox */ /* | Create the window and claim various handlers for it */ static void init_save_window(void) { /* Create the window */ save_box = Window_Create("save", template_TITLEMIN); /* Set the file icon */ Icon_printf(save_box, SAVE_ICON, "file_%03x", vfiletype); } #endif /* FULLSCREEN_ONLY */ /* | Hack: can't use Str.h without defining HAS_STRICMP. Rather than | require that the header files are altered we simply provide our | own strnicmp() function. */ static int my_strnicmp(const char *a, const char *b, int n) { int i; n--; for (i = 0; i <= n; i++) { if (tolower((unsigned char)a[i]) != tolower((unsigned char)b[i])) return tolower((unsigned char)a[i]) - tolower((unsigned char)b[i]); if (a[i] == '\0') break; } return 0; } #ifndef FULLSCREEN_ONLY /* | This is the handler called when a 'save' occurrs. | All it does is to update the game's own savefile setting and | then (if possible) save the character. */ static BOOL SaveHnd_FileSave(char *filename, void *ref) { char old_savefile[1024]; /* Hack: refuse to save if the character is dead */ if (PDEADCHK) { Msgs_Report(0, "err.cheat"); return FALSE; } /* Hack: disallow saves to * */ if (!my_strnicmp("", filename, 12)) { Msgs_Report(0, "err.scrap"); return FALSE; } /* Preserve the old path, in case something goes wrong... */ strcpy(old_savefile, savefile); /* Set the new path */ strcpy(savefile, unixify_name(filename)); /* Try a save (if sensible) */ if (game_in_progress && character_generated) { if (!save_player()) { Msgs_Report(0, "err.save", filename); strcpy(savefile, old_savefile); return FALSE; /* => failure */ } } /* Set the pathname icon */ Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile)); /* Kill the menu */ Wimp_CreateMenu((menu_block *) - 1, -1, -1); return TRUE; /* => Success */ } /* | Init the handlers for the savebox (eg. as a result of a menuwarning | being received for the savebox) */ static void init_savehandlers(void) { if (saveblk) { Save_ReleaseSaveHandlers(saveblk); saveblk = 0; } saveblk = Save_InitSaveWindowHandler(save_box, /* Window handle */ TRUE, /* it's part of a menu */ FALSE, /* not a window */ FALSE, /* Don't auto release the handlers */ SAVE_ICON, /* The file icon */ SAVE_OK, /* The OK icon */ SAVE_CANCEL, /* The cancel icon */ SAVE_PATH, /* The pathname icon */ SaveHnd_FileSave, /* Handler to "save the file" */ NULL, /* No RAM transfer support */ NULL, /* No 'result handler' */ 100 << 10, /* Est. size (irelevant anyway) */ vfiletype, /* filetype (irelevant) */ NULL /* ref */ ); } /* | Handle a MenuWarning message for the savebox */ static BOOL Hnd_SaveWarning(event_pollblock * pb, void *ref) { os_error *e; init_savehandlers(); /* Set the pathname */ Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile)); /* Open the submenu */ e = Wimp_CreateSubMenu((menu_block *) save_box, pb->data.message.data.menuwarn.openpos.x, pb->data.message.data.menuwarn.openpos.y); if (e) { Msgs_ReportFatal(0, "err.swi", __LINE__, e->errmess); } return TRUE; } /*--------------------------------------------------------------------------*/ /* | Initialise the r_data array | Mainly we just set up the line offset pointers and make sure that the | lines themselves are 'safe' by writing end-of-line codes to them. */ static void initialise_r_data(void) { int *lo = (int *)r_data; char *ld; int j; for (j = 0; j < 24; j++) { lo[j] = 25 * 4 + (80 * 5 + 4) * j; /* Offset of line */ ld = r_data + lo[j]; *ld++ = 0; /* 0,2 == */ *ld = 2; /* end of line */ } lo[j] = 0; /* Terminate line index */ } /* | Create the r_data array for a term | This is typically quite fast (1ms or so on a RPC700) | so we don't bother caching r_data for each term or using the | 'frosh' concept. */ static void make_r_data(term_data *t) { char **c = t->t.old->c; /* char array [24][80] */ byte **a = t->t.old->a; /* attr array [24][80] */ char *o; int i, j, cf; /* New code: */ o = r_data + 25 * 4; /* First byte of r_data after line index */ if (force_mono) { for (j = 0; j < 24; j++) { /* Set up the line offset entry */ ((int *)r_data)[j] = o - r_data; for (i = 0; i < 80; i++) *o++ = a[j][i] != TERM_DARK ? c[j][i] : ' '; /* 0,2 => end of line */ *o++ = 0; *o++ = 2; } } else { for (j = 0; j < 24; j++) { /* Set up the line offset entry */ ((int *)r_data)[j] = o - r_data; /* Each line starts in white */ cf = TERM_WHITE; for (i = 0; i < 80; i++) { if (a[j][i] != cf) { /* 0,6 => change FG */ *o++ = 0; *o++ = 6; cf = *o++ = a[j][i]; } *o++ = c[j][i]; } /* 0,2 => end of line */ *o++ = 0; *o++ = 2; } } } /* | Set up 'zrb' for the current screen mode. */ static void set_up_zrb_for_mode(void) { static char work_area[4096]; zrb.r_workarea = work_area; zrb.r_palette = zpalette; zrb.r_linesp = 0; zrb.r_for = TERM_WHITE; zrb.r_bac = 0; zrb.r_data = r_data; SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &zrb); } /* | Set up the ZapRedrawBlock ready to redraw term 't' | (caches the r_data as part of the process) */ static void set_up_zrb(term_data *t) { int fw, fh; zrb.r_flags.value = 0; /* Set font info up */ fw = t->font->w; fh = t->font->h; SWI(4, 4, SWI_ZapRedraw_CachedCharSize, zrb.r_bpp, 0, fw, fh, NULL, NULL, &(zrb.r_cbpl), &(zrb.r_cbpc)); zrb.r_caddr = (void *)(((int)t->font->bpp_n) - (t->font->f * zrb.r_cbpc)); zrb.r_charw = fw; /* Character size in pixels */ zrb.r_charh = fh; if (t->font == SYSTEM_FONT) zrb.r_flags.bits.double_height = screen_eig.y == 1; else zrb.r_flags.bits.double_height = 0; make_r_data(t); /* Cache the r_data */ } static void RO_redraw_window(window_redrawblock * rb, BOOL *more, term_data *t) { int cx, cy, cw, ch; /* set GCOL for cursor colour */ if (t->cursor.visible) { cw = zrb.r_charw << screen_eig.x; ch = -(zrb.r_charh << screen_eig.y); if (zrb.r_flags.bits.double_height) { ch *= 2; } cx = t->cursor.pos.x * cw; cy = t->cursor.pos.y * ch; cx += (rb->rect.min.x - rb->scroll.x); cy += (rb->rect.max.y - rb->scroll.y); cw -= (1 << screen_eig.x); ch += (1 << screen_eig.y); cy -= (1 << screen_eig.y); } while (*more) { SWI(2, 0, SWI_ZapRedraw_GetRectangle, rb, &zrb); SWI(2, 0, SWI_ZapRedraw_RedrawArea, NULL, &zrb); if (t->cursor.visible) { ColourTrans_SetGCOL(cursor_rgb, 0, 0); GFX_Move(cx, cy); GFX_DrawBy(cw, 0); GFX_DrawBy(0, ch); GFX_DrawBy(-cw, 0); GFX_DrawBy(0, -ch); } Wimp_GetRectangle(rb, more); } } static BOOL Hnd_Redraw(event_pollblock * pb, void *ref) { term_data *t = (term_data *)ref; window_redrawblock rb; BOOL more; rb.window = t->w; Wimp_RedrawWindow(&rb, &more); set_up_zrb(t); RO_redraw_window(&rb, &more, t); return TRUE; } static void refresh_window(term_data *t) { window_redrawblock rb; BOOL more; int fw, fh; if ((t->changed_box.min.x >= t->changed_box.max.x) || (t->changed_box.min.y >= t->changed_box.max.y)) return; set_up_zrb(t); fw = zrb.r_charw << screen_eig.x; fh = -(zrb.r_charh << screen_eig.y); if (zrb.r_flags.bits.double_height) { fh *= 2; } rb.window = t->w; rb.rect.min.x = fw * t->changed_box.min.x; rb.rect.max.x = fw * t->changed_box.max.x; rb.rect.max.y = fh * t->changed_box.min.y; rb.rect.min.y = fh * t->changed_box.max.y; Wimp_UpdateWindow(&rb, &more); RO_redraw_window(&rb, &more, t); t->changed_box.min.x = t->changed_box.min.y = 255; t->changed_box.max.x = t->changed_box.max.y = 0; } static void refresh_windows(void) { int i; window_info info; for (i = 0; i < MAX_TERM_DATA; i++) { info.window = data[i].w; Wimp_GetWindowInfo(&info); if (info.block.flags.data.open) refresh_window(&(data[i])); } } /* | Set the size of a window. | If the window grows but has no scroll bars then it is re-sized to | the new extent. If it shrinks then it is resized regardless. | | If the window isn't open then it is opened behind the backwindow, | resized and then closed again... I /did/ have a reason for doing this | rather than simply recreating the window at the new size, but for the | life of me I can't remember what it was... */ static void set_window_size(window_handle w, int width, int height) { window_state ws; int reclose; /* * int cw,ch; */ Wimp_GetWindowState(w, &ws); Window_SetExtent(w, 0, -height, width, 0); reclose = !ws.flags.data.open; if (!(ws.flags.value & (0xf << 27))) { if (reclose) { ws.openblock.behind = -3; Wimp_OpenWindow(&(ws.openblock)); } /* Removed - caused "pixel creep" :) * cw = ws.openblock.screenrect.max.x; * ch = ws.openblock.screenrect.max.y; * cw -= ws.openblock.screenrect.min.x; * ch -= ws.openblock.screenrect.min.y; * ws.openblock.screenrect.min.x -= ((width-cw)/2)-(1<w, fw, fh); } static BOOL Hnd_Caret(event_pollblock * pb, void *ref) { if (ref) got_caret = 1; else got_caret = 0; return TRUE; } /* | Attach a (named) font to the specified term. | If 'font' is NULL then the system font is attached. | The bpp_n data is calculated if necessary | returns: | 1 => the font was attached OK | 0 => the system font was substituted */ static int attach_font_to_term(term_data *t, char *font) { if (t->font != SYSTEM_FONT) { lose_font(t->font); } if (font) { t->font = find_font(font); } if (!t->font) { t->font = SYSTEM_FONT; if (font) { Msgs_Report(1, "err.font_l", font); } } else { if (!t->font->bpp_n) { lose_font(t->font); t->font = SYSTEM_FONT; if (font) { Msgs_Report(1, "err.font_c", font); } } } resize_term_for_font(t); return !(t->font == SYSTEM_FONT); } /*--------------------------------------------------------------------------*/ /* | Create a menu of all the (probable!) fonts in the specified location | NB: Any file of type 'data' is considered a font. | | Subdirectories are recursively searched. | | 1.10 - Uses $FontPaths to get a (space separated) list of paths | to search. For each path name, the menu text will be the name and the | path searched will be $$FontPath | | Eg. (for angband): | Angband$FontPaths Zap Angband | Angband$Zap$FontPath ZapFonts: | Angband$Angband$FontPath Angband:xtra.fonts. */ static void make_font_menu(void) { char *t; char buffer[260]; char menu_buffer[260]; int paths; int i; unsigned int max_width; const char *path[64]; /* pointers to path names */ menu_item *mi; font_menu = NULL; /* Get the path (ie. dir) to look under */ t = getenv(RISCOS_VARIANT "$FontPaths"); /* Hack: cope if the path isn't set */ if (!t) { t = ""; } strcpy(buffer, t); /* | Count how many paths there are, build an array of pointers to them | and terminate them in the buffer */ paths = 1; /* including the system font fake path '' */ for (t = buffer; *t; t++) { if (*t == ' ') { *t = 0; } else { if (t == buffer || !t[-1]) { path[paths] = t; paths++; } } } /* | Create the menu */ path[0] = SYSTEM_FONT->name; font_menu = f_malloc(sizeof(menu_block) + paths * sizeof(menu_item)); if (!font_menu) { core("Out of memory (building font menu)"); } memset(font_menu, 0, sizeof(menu_block) + paths * sizeof(menu_item)); strncpy(font_menu->title, "Fonts", 12); font_menu->titlefore = 7; font_menu->titleback = 2; font_menu->workfore = 7; font_menu->workback = 0; font_menu->height = 44; font_menu->gap = 0; max_width = strlen(font_menu->title); mi = (menu_item *) (font_menu + 1); for (i = 0; i < paths; i++) { mi[i].submenu.value = -1; mi[i].iconflags.data.text = 1; mi[i].iconflags.data.filled = 1; mi[i].iconflags.data.foreground = 7; mi[i].iconflags.data.background = 0; strncpy(mi[i].icondata.text, path[i], 12); if (strlen(mi[i].icondata.text) > max_width) max_width = strlen(mi[i].icondata.text); } font_menu->width = (max_width + 2) * 16; mi[i - 1].menuflags.data.last = 1; /* | Hack: add a dotted line after the system font entry if appropriate */ if (paths > 1) mi[0].menuflags.data.dotted = 1; /* | Iterate over the paths, building the appropriate submenus */ for (i = 1; i < paths; i++) { menu_ptr sub_menu = NULL; sprintf(menu_buffer, "%s$%s$FontPath", RISCOS_VARIANT, path[i]); t = getenv(menu_buffer); /* Hack: cope if the path isn't defined */ if (!t) { t = ""; } /* Fudge so that the fontpath can be a path, not just a dir. */ strcpy(menu_buffer, t); for (t = menu_buffer; *t > ' '; t++) ; if (t[-1] == '.') { t--; } *t = 0; /* Build the menu. Don't bother if the path variable was empty */ if (*menu_buffer) sub_menu = make_zfont_menu(menu_buffer); if (!sub_menu) { mi[i].iconflags.data.shaded = 1; } else { mi[i].submenu.menu = sub_menu; /* Override the title of the 'root' sub-menu */ strncpy(sub_menu->title, path[i], 12); /* Add the submenu to the main menu */ } } return; } /* ----------------------------------------------- musus, xxxx-xx-xx --- * Create and set up the infobox. * --------------------------------------------------------------------- */ static void create_info_box(void) { info_box = Window_Create("info", template_TITLEMIN); Icon_printf(info_box, 0, "%s %s", VARIANT, VERSION); Icon_SetText(info_box, 2, AUTHORS); Icon_SetText(info_box, 3, PORTERS); Icon_SetText(info_box, 7, PORTVERSION); return; } /* | Create the various menus */ static void init_menus(void) { char buffer1[256]; char buffer2[32]; char *o; create_info_box(); /* For the Info> entries */ make_font_menu(); /* Make the fonts menu */ Msgs_Lookup("menu.ibar:Info|>Save As|Full screen,Gamma correction,Sound," "Windows|Save choices|Quit (& save)", buffer1, 256); ibar_menu = Menu_New(VARIANT, buffer1); if (!ibar_menu) core("Can't create Iconbar menu!"); Msgs_Lookup("menu.term:Info|>Save As|Font,Windows", buffer1, 256); term_menu = Menu_New(VARIANT, buffer1); if (!term_menu) core("Can't create Term menu!"); #ifndef OLD_TERM_MENU o = buffer1; o += sprintf(buffer1, "%s|", VARIANT); o += sprintf(o, "%s,%s,%s|", angband_term_name[1], angband_term_name[2], angband_term_name[3]); sprintf(o, "%s,%s,%s,%s", angband_term_name[4], angband_term_name[5], angband_term_name[6], angband_term_name[7]); #else Msgs_printf(buffer1, "menu.windows:%s|Term-1 (Mirror),Term-2 (Recall)," "Term-3 (Choice)|Term-4,Term-5,Term-6,Term-7", VARIANT); #endif Msgs_Lookup("menu.winT:Windows", buffer2, 32); wind_menu = Menu_New(buffer2, buffer1); if (!wind_menu) { core("Can't create Windows menu!"); } /* Now attach the various submenus to where they belong */ Menu_AddSubMenu(ibar_menu, IBAR_MENU_INFO, (menu_ptr) info_box); Menu_AddSubMenu(ibar_menu, IBAR_MENU_GAMMA, (menu_ptr) gamma_win); Menu_AddSubMenu(ibar_menu, IBAR_MENU_SOUND, (menu_ptr) sound_win); Menu_AddSubMenu(ibar_menu, IBAR_MENU_WINDOWS, (menu_ptr) wind_menu); Menu_AddSubMenu(term_menu, TERM_MENU_INFO, (menu_ptr) info_box); Menu_AddSubMenu(term_menu, TERM_MENU_WINDOWS, wind_menu); /* Add the savebox */ Menu_Warn(ibar_menu, IBAR_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL); Menu_Warn(term_menu, TERM_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL); if (font_menu) /* add the submenu */ Menu_AddSubMenu(term_menu, TERM_MENU_FONT, font_menu); else /* If the font menu is buggered, shade its entry */ /* unticked, shaded */ Menu_SetFlags(term_menu, TERM_MENU_FONT, FALSE, TRUE); } static void grab_caret(void) { caret_block cb; cb.window = data[0].w; cb.icon = -1; cb.height = 1 << 25; /* Invisible */ Wimp_SetCaretPosition(&cb); } /* | (Recursively) clear all ticks from the specified menu */ static void clear_all_menu_ticks(menu_ptr mp) { menu_item *mi = (menu_item *) (mp + 1); do { if (mi->menuflags.data.ticked) mi->menuflags.data.ticked = 0; if (mi->submenu.value != -1) clear_all_menu_ticks(mi->submenu.menu); mi++; } while (mi[-1].menuflags.data.last != 1); } /* | Set the font menu's ticks to match the specifed font name. | | fm is the (sub) menu to scan down (recursing into it's submenus (if any)) | fn is the font name to match | prefix is the menu text to be prepended to the menu entries due to | previous menus (eg. "08x16" will cause fn="08x16.fred" to match menu | entry "fred". | | NB: recursive. | */ static void set_font_menu_ticks(menu_ptr fm, char *fn, const char *prefix) { char buffer[260]; char *b_leaf; /* -> menu 'leaf' text in buffer */ int pl; /* prefix string length */ menu_item *mi = (menu_item *) (fm + 1); strcpy(buffer, prefix); pl = strlen(buffer); b_leaf = buffer + pl; do { /* Check for (substring) match */ strncpy(b_leaf, mi->icondata.text, 12); /* Is it a sub-menu? */ if (mi->submenu.value == -1) { /* No - must be an exact match */ mi->menuflags.data.ticked = !strcmp(buffer, fn); } else { /* Yes - must be a partial match (with a dot on :) */ strcat(b_leaf, "."); mi->menuflags.data.ticked = !strncmp(buffer, fn, pl + strlen(b_leaf)); if (mi->menuflags.data.ticked) set_font_menu_ticks(mi->submenu.menu, fn, buffer); else clear_all_menu_ticks(mi->submenu.menu); } /* Next item */ mi++; } while (mi[-1].menuflags.data.last != 1); /* Until finished */ } /* | Set ticks, etc. in the term_menu to reflect the current state of the | term 't' */ static void set_up_term_menu(term_data *t) { int i; menu_ptr mp; menu_item *mi; window_state ws; /* First of all, set up menu title to be the term's title */ strncpy(term_menu->title, t->name, 12); /* Now set the ticks in the Windows> submenu (cuz it's easy) */ mp = wind_menu; /* Windows submenu */ mi = (menu_item *) (mp + 1); /* First entry */ for (i = 0; i < MAX_TERM_DATA; i++) { Wimp_GetWindowState(data[i].w, &ws); mi[i].menuflags.data.ticked = ws.flags.data.open; } /* | Now, the tricky bit: find out which font is selected in this | term and tick it in the menu. Untick all the rest. */ set_font_menu_ticks(font_menu, t->font->name, ""); /* Shade the 'Save>' entry if saving isn't possible (yet) */ if (game_in_progress && character_generated) Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, PDEADCHK); else Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, TRUE); } /* | Generic 'click' handler for windows - turns a drag on the window | into a move-window style drag. */ static BOOL Hnd_Click(event_pollblock * pb, void *ref) { if (pb->data.mouse.button.data.dragselect || pb->data.mouse.button.data.dragadjust) { drag_block b; b.window = pb->data.mouse.window; b.screenrect.min.x = b.screenrect.min.y = 0; b.screenrect.max.x = screen_size.x; b.screenrect.max.y = screen_size.y; b.type = drag_MOVEWINDOW; Wimp_DragBox(&b); } return TRUE; } /* | Handle a click on a Term window. */ static BOOL Hnd_TermClick(event_pollblock * pb, void *ref) { term_data *t = (term_data *)ref; if (pb->data.mouse.button.data.menu) { menu_term = t; set_up_term_menu(t); Menu_Show(term_menu, pb->data.mouse.pos.x - 32, pb->data.mouse.pos.y + 32); } else { grab_caret(); Hnd_Click(pb, ref); } return TRUE; } #endif /* FULLSCREEN_ONLY */ static void mark_ood(term_data *t, int minx, int miny, int maxx, int maxy) { if (t->changed_box.min.x > minx) t->changed_box.min.x = minx; if (t->changed_box.max.x < maxx) t->changed_box.max.x = maxx; if (t->changed_box.min.y > miny) t->changed_box.min.y = miny; if (t->changed_box.max.y < maxy) t->changed_box.max.y = maxy; } #ifndef FULLSCREEN_ONLY /* Check for an event (ie. key press) */ static errr Term_xtra_acn_check(void) { static int last_poll = 0; int curr_time; int bh, bl; /* | Only poll the wimp if there's something in the keyboard buffer | or every 10cs */ /* Check the kbd buffer */ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh); bl = (bl & 0xff) + (bh << 8); /* Check how long it is since we last polled */ curr_time = Time_Monotonic(); if ((bl > 0 && got_caret) || ((curr_time - last_poll) > 9)) { last_poll = curr_time; Stop_Hourglass; Event_Poll(); Start_Hourglass; } /* | This allows the user to interrupt the borg. */ if (key_pressed) return key_pressed = 0; return 1; } /* | Wait for an event (ie. keypress) | Note that we idle poll once a second to allow us to implement the | alarm system. */ static errr Term_xtra_acn_event(void) { Stop_Hourglass; while (!key_pressed && !fullscreen_font) { Event_PollIdle(100); } Start_Hourglass; return key_pressed = 0; } /* React to changes (eg. palette change) */ static errr Term_xtra_acn_react(void) { int c; cache_palette(); /* Mark the entirety of each window as out of date */ for (c = 0; c < MAX_TERM_DATA; c++) mark_ood(&data[c], 0, 0, 80, 24); /* Force a redraw of the windows */ refresh_windows(); /* Success */ return 0; } /* Do various things to a term */ static errr Term_xtra_acn(int n, int v) { term_data *t = (term_data *)Term; switch (n) { case TERM_XTRA_EVENT: /* Wait/check for an event */ if (v) return Term_xtra_acn_event(); else return Term_xtra_acn_check(); case TERM_XTRA_BORED: /* Bored */ return Term_xtra_acn_check(); case TERM_XTRA_FLUSH: /* Flush input */ if (got_caret) { /* 1.21 - Hack: wait until no keys are pressed */ if (hack_flush) for (v = 0; v != 0xff;) SWI(1, 2, SWI_OS_Byte, 122, 0, &v); SWI(3, 0, SWI_OS_Byte, 21, 0, 0); /* Flush Kbd buffer */ } return 0; case TERM_XTRA_FRESH: /* Flush output */ refresh_window(t); return 0; case TERM_XTRA_FROSH: /* Ensure line 'v' is plotted */ /* Doesn't do anything */ return 0; case TERM_XTRA_SHAPE: /* Set cursor visibility */ t->cursor.visible = v ? TRUE : FALSE; mark_ood(t, t->cursor.pos.x, t->cursor.pos.y, t->cursor.pos.x + 1, t->cursor.pos.y + 1); refresh_window(t); /* needed? */ return 0; case TERM_XTRA_NOISE: /* Make a beep */ Sound_SysBeep(); return 0; case TERM_XTRA_REACT: /* React to, eg. palette changes */ return Term_xtra_acn_react(); case TERM_XTRA_DELAY: /* Delay for 'v' ms */ if (v > 0) { unsigned int start = Time_Monotonic(); v = (v + 5) / 10; /* Round to nearest cs */ GFX_Wait(); while ((Time_Monotonic() - start) < v) ; } return 0; case TERM_XTRA_SOUND: /* Play a sound :) */ if (enable_sound) { play_sound(v); } return 0; default: return 1; /* Unsupported */ } } /* Move (but don't necessarily display) the cursor */ static errr Term_curs_acn(int x, int y) { term_data *t = (term_data *)Term; if (t->cursor.visible) mark_ood(t, t->cursor.pos.x, t->cursor.pos.y, t->cursor.pos.x + 1, t->cursor.pos.y + 1); t->cursor.pos.x = x; t->cursor.pos.y = y; if (t->cursor.visible) mark_ood(t, t->cursor.pos.x, t->cursor.pos.y, t->cursor.pos.x + 1, t->cursor.pos.y + 1); return 0; } /* | NB: these two are very simple since we use the Term's contents | directly to generate the r_data for ZapRedraw. */ /* Erase 'n' characters at (x,y) */ static errr Term_wipe_acn(int x, int y, int n) { mark_ood((term_data *)Term, x, y, x + n, y + 1); return 0; } /* Write 'n' characters from 's' with attr 'a' at (x,y) */ static errr Term_text_acn(int x, int y, int n, byte a, cptr s) { mark_ood((term_data *)Term, x, y, x + n, y + 1); return 0; } #endif /* FULLSCREEN_ONLY */ /* Initialise one of our terms */ static void Term_init_acn(term *t) { term_data *term = (term_data *)t; /* Ludicrous changed box settings :) */ term->changed_box.min.x = 256; term->changed_box.min.y = 256; term->changed_box.max.x = 0; term->changed_box.max.y = 0; } static void term_data_link(term_data *td, int k) { term *t = &(td->t); /* Initialise the term */ term_init(t, 80, 24, k); /* Set flags and hooks */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Experiment (FS mode requires them) */ t->always_text = TRUE; t->never_frosh = TRUE; /* Experiment (FS mode requires them) */ #ifdef FULLSCREEN_ONLY t->wipe_hook = Term_wipe_acnFS; t->xtra_hook = Term_xtra_acnFS; t->curs_hook = Term_curs_acnFS; t->text_hook = Term_text_acnFS; #else t->init_hook = Term_init_acn; t->xtra_hook = Term_xtra_acn; t->wipe_hook = Term_wipe_acn; t->curs_hook = Term_curs_acn; t->text_hook = Term_text_acn; t->user_hook = Term_user_acn; #endif /* FULLSCREEN_ONLY */ t->data = td; Term_activate(t); } #ifndef FULLSCREEN_ONLY /* Open default windows (ie. as set in choices) at the appropriate sizes */ static void show_windows(void) { int i; for (i = MAX_TERM_DATA; i-- > 0;) { if (!data[i].unopened) { if (data[i].def_open) Window_Show(data[i].w, open_WHEREVER); } else { if (data[i].def_open) { window_openblock ob; ob.window = data[i].w; ob.screenrect = data[i].def_pos; ob.scroll = data[i].def_scroll; ob.behind = -1; Wimp_OpenWindow(&ob); data[i].unopened = 0; } } } } /* | 'ref' is used to indicate whether this close is being forced by some other | part of the code (eg. the Windows> submenu code). This is used to modify | the 'adjust doesn't close other terms' behavior. */ static BOOL Hnd_MainClose(event_pollblock * pb, void *ref) { int i; window_state ws; mouse_block mb; /* New in 1.08: don't close other Terms if closed with adjust */ Wimp_GetPointerInfo(&mb); if (ref || mb.button.data.adjust) { Wimp_CloseWindow(data[0].w); } else { /* Close all the terms, but mark the open ones as 'def_open' */ for (i = 0; i < MAX_TERM_DATA; i++) { Wimp_GetWindowState(data[i].w, &ws); if (!ws.flags.data.open) { data[i].def_open = 0; } else { Wimp_CloseWindow(data[i].w); data[i].def_open = 1; } } } return TRUE; } static BOOL Hnd_PaletteChange(event_pollblock * pb, void *ref) { cache_palette(); return TRUE; } static BOOL Hnd_ModeChange(event_pollblock * pb, void *ref) { int i; Screen_CacheModeInfo(); set_up_zrb_for_mode(); /* (re)set up the redraw block */ cache_palette(); /* (re)cache the palette */ cache_fonts(); /* (re)cache the fonts */ /* Enforce sizes (eg. if screen_eig.y has changed) */ for (i = 0; i < MAX_TERM_DATA; i++) resize_term_for_font(&(data[i])); return TRUE; } static BOOL Hnd_Keypress(event_pollblock * pb, void *ref) { static const char hex[] = "0123456789ABCDEF"; int c = pb->data.key.code; /* Check whether this key was pressed in Term 0 */ if (pb->data.key.caret.window == data[0].w) { switch (c) { case keycode_F12: case keycode_SHIFT_F12: case keycode_CTRL_F12: case keycode_CTRL_SHIFT_F12: /* Never intercept these */ break; case 27: /* handle escape specially */ if (Kbd_KeyDown(inkey_CTRL)) { ack_alarm(); return TRUE; } /* Send everything else onto the Term package */ default: /* Take care of "special" keypresses */ switch (c) { case keycode_TAB: { c = '\t'; break; } case keycode_PAGEUP: { c = '9'; break; } case keycode_PAGEDOWN: { c = '3'; break; } case keycode_COPY: { c = '1'; break; } case keycode_HOME: { c = '7'; break; } } /* Pass to the angband engine */ /* Allow shift & ctrl to modify the keypad keys */ if (c >= '0' && c <= '9') { kbd_modifiers m = Kbd_GetModifiers(FALSE); if (m.shift) { c |= 0x800; } if (m.ctrl) { c |= 0x400; } /* Could maybe add ALT as 0x1000 ??? */ } /* Keys >255 have to be send as escape sequences (31=escape) */ if (c > 255 || c == 31) { Term_keypress(31); Term_keypress(hex[(c & 0xf00) >> 8]); Term_keypress(hex[(c & 0x0f0) >> 4]); Term_keypress(hex[(c & 0x00f)]); c = 13; } Term_keypress(c); key_pressed = 1; /*if ( c==27 ) { escape_pressed = 1; } */ return TRUE; } } Wimp_ProcessKey(c); return TRUE; } /*--------------------------------------------------------------------------*/ /* Gamma correction window stuff */ /*--------------------------------------------------------------------------*/ static void redraw_gamma(window_redrawblock * rb, BOOL *more) { int i, y, x, h, w; int bx, by; int dither; bx = Coord_XToScreen(GC_XOFF, (convert_block *) & (rb->rect)); by = Coord_YToScreen(GC_YOFF, (convert_block *) & (rb->rect)); h = GC_HEIGHT / 4; w = GC_WIDTH / 16; x = bx; while (*more) { for (i = 0; i < 16; i++) { y = by; for (dither = 0; dither < 2; dither++) { /* Solid block: */ ColourTrans_SetGCOL(palette[i], dither << 8, 0); GFX_RectangleFill(x, y, w, -h); y -= h; /* Dot on black: */ ColourTrans_SetGCOL(palette[0], dither << 8, 0); GFX_RectangleFill(x, y, w, -h); ColourTrans_SetGCOL(palette[i], dither << 8, 0); GFX_RectangleFill(x + (w / 2) - 2, y - (h / 2), 2, 2); y -= h; } x += w; } Wimp_GetRectangle(rb, more); } } static void update_gamma(void) { window_redrawblock rb; BOOL more; rb.window = gamma_win; rb.rect.min.x = GC_XOFF; rb.rect.min.y = GC_YOFF - GC_HEIGHT; rb.rect.max.y = GC_XOFF + GC_WIDTH + screen_delta.x; rb.rect.max.y = GC_YOFF + screen_delta.y; Wimp_UpdateWindow(&rb, &more); if (more) { redraw_gamma(&rb, &more); } } static BOOL Hnd_RedrawGamma(event_pollblock * pb, void *ref) { window_redrawblock rb; BOOL more; rb.window = pb->data.openblock.window; Wimp_RedrawWindow(&rb, &more); if (more) { redraw_gamma(&rb, &more); } return TRUE; } static BOOL Hnd_GammaClick(event_pollblock * pb, void *ref) { int up = (ref == 0); if (up) { if (gamma < 9.0) { gamma += 0.05; Icon_SetDouble(gamma_win, 0, gamma, 2); Term_xtra_acn_react(); update_gamma(); } } else { if (gamma > 0.05) { gamma -= 0.05; Icon_SetDouble(gamma_win, GAMMA_ICN, gamma, 2); Term_xtra_acn_react(); update_gamma(); } } /* Hack: if the user menu is active then force it to redraw */ if (user_menu_active) { Term_keypress(18); key_pressed = 1; } return TRUE; } /* | Reflect the current options in the gamma window */ static void set_gamma_window_state(void) { if (minimise_memory) return; Icon_SetDouble(gamma_win, 0, gamma, 2); } static void init_gamma_window(void) { if (minimise_memory) return; Template_UseSpriteArea(resource_sprites); gamma_win = Window_Create("gamma", template_TITLEMIN); Template_UseSpriteArea(NULL); Event_Claim(event_REDRAW, gamma_win, event_ANY, Hnd_RedrawGamma, 0); Event_Claim(event_CLICK, gamma_win, GAMMA_DOWN, Hnd_GammaClick, (void *)1); Event_Claim(event_CLICK, gamma_win, GAMMA_UP, Hnd_GammaClick, (void *)0); set_gamma_window_state(); } /*--------------------------------------------------------------------------*/ /* Sound options window stuff */ /*--------------------------------------------------------------------------*/ static slider_info volume_slider; /* | Reflect the current sound config in the sound options window */ static void set_sound_window_state(void) { if (minimise_memory) return; Icon_SetSelect(sound_win, SND_ENABLE, enable_sound); Slider_SetValue(&volume_slider, sound_volume, NULL, NULL); if (sound_volume > 127) volume_slider.colour.foreground = colour_RED; else volume_slider.colour.foreground = colour_GREEN; } /* | The sound slider has been dragged, so update the sound volume setting */ static int update_volume_from_slider(slider_info *si, void *ref) { sound_volume = Slider_ReadValue(si); if (sound_volume > 127) volume_slider.colour.foreground = colour_RED; else volume_slider.colour.foreground = colour_GREEN; return 0; } /* | Handle redraw events for the sound options window */ static BOOL Hnd_RedrawSnd(event_pollblock * pb, void *ref) { window_redrawblock redraw; BOOL more; redraw.window = pb->data.openblock.window; Wimp_RedrawWindow(&redraw, &more); while (more) { Slider_Redraw(((slider_info *) ref), &redraw.cliprect); Wimp_GetRectangle(&redraw, &more); } return (TRUE); } /* | Handle clicks on the sound options window */ static BOOL Hnd_SndClick(event_pollblock * pb, void *ref) { int icn = pb->data.mouse.icon; int adj = pb->data.mouse.button.data.adjust; slider_info *si = (slider_info *) ref; switch (icn) { /* Bump arrows for the slider: */ case SND_VOL_DOWN: adj = !adj; case SND_VOL_UP: adj = adj ? -1 : 1; sound_volume += adj; if (sound_volume < SOUND_VOL_MIN) { sound_volume = SOUND_VOL_MIN; } if (sound_volume > SOUND_VOL_MAX) { sound_volume = SOUND_VOL_MAX; } set_sound_window_state(); break; /* The slider itself */ case SND_VOL_SLIDER: Icon_ForceRedraw(sound_win, SND_VOL_SLIDER); Slider_Drag(si, NULL, NULL, NULL); break; /* The enable/disable icon */ case SND_ENABLE: enable_sound = !enable_sound; Icon_SetSelect(sound_win, SND_ENABLE, enable_sound); if (enable_sound) { initialise_sound(); } break; } /* Hack: if the user menu is active then force it to redraw */ if (user_menu_active) { Term_keypress(18); key_pressed = 1; } return TRUE; } /* | Set the sound options window up, ready to rock :) */ static void init_sound_window(void) { Template_UseSpriteArea(resource_sprites); sound_win = Window_Create("sound", template_TITLEMIN); Template_UseSpriteArea(NULL); Event_Claim(event_REDRAW, sound_win, event_ANY, Hnd_RedrawSnd, (void *)&volume_slider); Event_Claim(event_CLICK, sound_win, event_ANY, Hnd_SndClick, (void *)&volume_slider); /* Set up the slider info */ volume_slider.window = sound_win; volume_slider.icon = SND_VOL_SLIDER; volume_slider.limits.min = SOUND_VOL_MIN; volume_slider.limits.max = SOUND_VOL_MAX; volume_slider.colour.foreground = colour_GREEN; volume_slider.colour.background = colour_WHITE; volume_slider.border.x = 8; volume_slider.border.y = 8; volume_slider.update = update_volume_from_slider; set_sound_window_state(); } /*--------------------------------------------------------------------------*/ /* | A font has been selected. | At this point, menu_term is a pointer to the term for which the | menu was opened. */ static void handle_font_selection(int *s) { char name[260]; os_error *e; char *r; menu_ptr mp = font_menu; int *mis; /* Follow the >s to the entry specified */ for (mis = s; *mis != -1; mis++) mp = ((menu_item *) (mp + 1))[*mis].submenu.menu; /* | Now, check to see if we've hit a leaf entry. | NB: If the entry isn't a leaf entry then the first entry in its submenu | is used instead */ if (((int)mp) != -1) { mis[0] = 0; mis[1] = -1; mp = ((menu_item *) (mp + 1))[0].submenu.menu; } if (((int)mp) != -1) return; e = Wimp_DecodeMenu(font_menu, s, name); if (e) { plog(e->errmess); return; } /* Make sure that the string is NULL terminated */ for (r = name; *r >= ' '; r++) ; *r = 0; attach_font_to_term(menu_term, name); mark_ood(menu_term, 0, 0, 80, 24); refresh_window(menu_term); } #endif /* FULLSCREEN_ONLY */ static void load_choices(void) { FILE *fp = NULL; char *cf; int i; char buffer[260]; cf = find_choices(FALSE); if (*cf) fp = fopen(cf, "r"); /* Implement default choices */ data[0].def_open = 1; data[0].unopened = 1; /* ie. force def_pos */ data[0].def_pos.min.x = (screen_size.x - 1280) / 2; data[0].def_pos.max.x = (screen_size.x + 1280) / 2; data[0].def_pos.min.y = (screen_size.y - 768) / 2 - 32; data[0].def_pos.max.y = (screen_size.y + 768) / 2 - 32; data[0].def_scroll.x = data[0].def_scroll.y = 0; for (i = 1; i < MAX_TERM_DATA; i++) { data[i].def_open = 0; data[i].unopened = 1; /* ie. force def_pos */ data[i].def_pos.min.x = (screen_size.x - 1280) / 2; data[i].def_pos.max.x = (screen_size.x + 1280) / 2; data[i].def_pos.min.y = (screen_size.y - 768) / 2; data[i].def_pos.max.y = (screen_size.y + 768) / 2; data[i].def_scroll.x = data[i].def_scroll.y = 0; } if (fp) { const char *t_; char *o_; if (!fgets(buffer, sizeof(buffer), fp)) { fclose(fp); return; } if (strcmp(buffer, "[Angband config, Musus' port]\n")) { fclose(fp); return; } /* Load choices */ while (fgets(buffer, sizeof(buffer), fp)) { t_ = strtok(buffer, " "); /* Term number (or keyword, "Gamma", etc.) */ o_ = strtok(NULL, "\n"); /* argument string */ if (!o_) { o_ = ""; } /* missing (or null) argument? */ if (t_) { if (!strcmp(t_, "Gamma")) gamma = atof(o_); else if (!strcmp(t_, "Monochrome")) force_mono = !strcmp(o_, "on"); else if (!strcmp(t_, "Sound")) enable_sound = !strcmp(o_, "on"); else if (!strcmp(t_, "Volume")) sound_volume = atoi(o_); else if (!strcmp(t_, "FullScreen")) start_fullscreen = !strcmp(o_, "on"); else if (!strcmp(t_, "Hourglass")) use_glass = !strcmp(o_, "on"); else if (!strcmp(t_, "HackFlush")) hack_flush = !strcmp(o_, "on"); else if (!strcmp(t_, "AlarmTimeH")) alarm_h = atoi(o_); else if (!strcmp(t_, "AlarmTimeM")) alarm_m = atoi(o_); else if (!strcmp(t_, "AlarmText")) strcpy(alarm_message, o_); else if (!strcmp(t_, "AlarmBeep")) alarm_beep = !strcmp(o_, "on"); else if (!strcmp(t_, "AlarmType")) { int i; for (i = 0; i < 4; i++) if (!strcmp(alarm_types[i], o_)) alarm_type = i; } else if (isdigit((unsigned char)*t_)) { int t = atoi(t_); if (t >= 0 && t < MAX_TERM_DATA) { char *f_, *x0_, *y0_, *x1_, *y1_, *sx_, *sy_; o_ = strtok(o_, " "); /* first word */ f_ = strtok(NULL, " "); /* font name */ x0_ = strtok(NULL, " "); /* x posn (min) */ y0_ = strtok(NULL, " "); /* y posn (min) */ x1_ = strtok(NULL, " "); /* x posn (max) */ y1_ = strtok(NULL, " "); /* y posn (max) */ sx_ = strtok(NULL, " "); /* x scroll offset */ sy_ = strtok(NULL, "\n"); /* y scroll offset */ data[t].def_open = (t == 0) || atoi(o_); data[t].def_pos.min.x = atoi(x0_); data[t].def_pos.min.y = atoi(y0_); data[t].def_pos.max.x = atoi(x1_); data[t].def_pos.max.y = atoi(y1_); data[t].def_scroll.x = atoi(sx_); data[t].def_scroll.y = atoi(sy_); data[t].unopened = 1; /* ie. force def_pos */ #ifndef FULLSCREEN_ONLY attach_font_to_term(&(data[t]), f_); #endif /* FULLSCREEN_ONLY */ } } } } fclose(fp); } #ifndef FULLSCREEN_ONLY /* | Fudge so that the main term is *always* fullsize */ { int fw, fh; set_up_zrb(&(data[0])); fw = zrb.r_charw << screen_eig.x; fh = zrb.r_charh << screen_eig.y; if (zrb.r_flags.bits.double_height) { fh *= 2; } fw *= 80; fh *= 24; data[0].def_pos.max.x = data[0].def_pos.min.x + fw; data[0].def_pos.max.y = data[0].def_pos.min.y + fh; data[0].def_scroll.x = 0; data[0].def_scroll.y = 0; } #endif /* FULLSCREEN_ONLY */ } static void save_choices(void) { FILE *fp = NULL; FILE *fpm = NULL; char *cf; int i; write_alarm_choices(); cf = find_choices(TRUE); if (!*cf) { plog("Failed to locate writable choices file!"); return; } fp = fopen(cf, "w"); if (!fp) { plog("Can't write choices file"); return; } fpm = fopen(find_choices_mirror(), "w"); f2printf(fp, fpm, "[Angband config, Musus' port]\n"); f2printf(fp, fpm, "Gamma %.2lf\n", gamma); f2printf(fp, fpm, "Monochrome %s\n", force_mono ? "on" : "off"); f2printf(fp, fpm, "Sound %s\n", enable_sound ? "on" : "off"); f2printf(fp, fpm, "Volume %d\n", sound_volume); f2printf(fp, fpm, "FullScreen %s\n", start_fullscreen ? "on" : "off"); f2printf(fp, fpm, "Hourglass %s\n", use_glass ? "on" : "off"); f2printf(fp, fpm, "HackFlush %s\n", hack_flush ? "on" : "off"); for (i = 0; i < MAX_TERM_DATA; i++) { window_state ws; Wimp_GetWindowState(data[i].w, &ws); f2printf(fp, fpm, "%d %d %s ", i, ws.flags.data.open, data[i].font->name); f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.x); f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.y); f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.x); f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.y); f2printf(fp, fpm, "%d %d\n", ws.openblock.scroll.x, ws.openblock.scroll.y); } fclose(fp); if (fpm) { fclose(fpm); } } /* | Update the Alarm choices file to reflect changed alarm settings. */ static void write_alarm_choices(void) { FILE *fp; char *cf; /* Open the choices file for reading */ cf = find_alarmfile(TRUE); if (!*cf) { plog("Can't determine Alarm file location!"); return; } fp = fopen(cf, "w"); if (!fp) { plog("Can't write Alarm file"); return; } /* Write the new alarm options */ fprintf(fp, "AlarmType %s\n", alarm_types[alarm_type]); fprintf(fp, "AlarmTimeH %d\n", alarm_h); fprintf(fp, "AlarmTimeM %d\n", alarm_m); fprintf(fp, "AlarmText %s\n", alarm_message); fprintf(fp, "AlarmBeep %s\n", alarm_beep ? "on" : "off"); fclose(fp); } /* | Read the Alarm choices file. */ static void read_alarm_choices(void) { char buffer[260]; FILE *fp; char *cf; cf = find_alarmfile(FALSE); if (!*cf) { return; } fp = fopen(cf, "r"); if (fp) { const char *t_, *o_; /* Load choices */ while (fgets(buffer, sizeof(buffer), fp)) { t_ = strtok(buffer, " "); /* Keyword */ o_ = strtok(NULL, "\n"); /* argument string */ if (!o_) { o_ = ""; } /* missing (or null) argument? */ if (t_) { if (!strcmp(t_, "AlarmTimeH")) alarm_h = atoi(o_); else if (!strcmp(t_, "AlarmTimeM")) alarm_m = atoi(o_); else if (!strcmp(t_, "AlarmText")) strcpy(alarm_message, o_); else if (!strcmp(t_, "AlarmBeep")) alarm_beep = !strcmp(o_, "on"); else if (!strcmp(t_, "AlarmType")) { int i; for (i = 0; i < 4; i++) if (!strcmp(alarm_types[i], o_)) alarm_type = i; } } } fclose(fp); } } #ifndef FULLSCREEN_ONLY /* | Handle selections from the term menu(s) */ static BOOL Hnd_TermMenu(event_pollblock * pb, void *ref) { mouse_block mb; int i; Wimp_GetPointerInfo(&mb); switch (pb->data.selection[0]) { case TERM_MENU_INFO: /* Info> */ break; case TERM_MENU_FONT: /* Font> */ /* Sub item selected? */ if (pb->data.selection[1] == -1) { break; } handle_font_selection(pb->data.selection + 1); break; case TERM_MENU_WINDOWS: /* Windows> */ if (pb->data.selection[1] == -1) { break; } i = pb->data.selection[1]; { window_state ws; Wimp_GetWindowState(data[i].w, &ws); if (ws.flags.data.open) { if (!i) Hnd_MainClose(NULL, (void *)TRUE); else Window_Hide(data[i].w); } else { if (!i) { show_windows(); grab_caret(); } else { if (!data[i].unopened) { Window_Show(data[i].w, open_WHEREVER); } else { window_openblock ob; ob.window = data[i].w; ob.screenrect = data[i].def_pos; ob.scroll = data[i].def_scroll; ob.behind = -1; /* could use data[0].w; ? */ Wimp_OpenWindow(&ob); data[i].unopened = 0; } } } } break; } if (mb.button.data.adjust) { set_up_term_menu(menu_term); Menu_ShowLast(); } return TRUE; } /* | Handle selections from the iconbar menu */ static BOOL Hnd_IbarMenu(event_pollblock * pb, void *ref) { mouse_block mb; Wimp_GetPointerInfo(&mb); switch (pb->data.selection[0]) { case IBAR_MENU_INFO: /* Info> */ break; case IBAR_MENU_FULLSCREEN: /* Full screen */ /* Do Full Screen mode */ enter_fullscreen_mode(); break; case IBAR_MENU_GAMMA: /* Gamma correction */ break; case IBAR_MENU_SOUND: /* Sound */ /* | enable_sound = !enable_sound; | if ( enable_sound ) { initialise_sound(); } | Menu_SetFlags( ibar_menu, IBAR_MENU_SOUND, enable_sound, 0 ); | set_sound_window_state(); */ break; case IBAR_MENU_WINDOWS: /* Windows> */ /* | Hack: pass it off as the equivalent selection from | the term menu. */ pb->data.selection[0] = TERM_MENU_WINDOWS; return Hnd_TermMenu(pb, ref); break; case IBAR_MENU_SAVECHOICES: /* Save choices */ save_choices(); break; case IBAR_MENU_QUIT: /* Quit */ if (game_in_progress && character_generated) save_player(); quit(NULL); break; } if (mb.button.data.adjust) Menu_ShowLast(); return TRUE; } static BOOL Hnd_MenuSel(event_pollblock * pb, void *ref) { if (menu_currentopen == ibar_menu) return Hnd_IbarMenu(pb, ref); else if (menu_currentopen == term_menu) return Hnd_TermMenu(pb, ref); return FALSE; } static BOOL Hnd_IbarClick(event_pollblock * pb, void *ref) { if (pb->data.mouse.button.data.menu) { set_gamma_window_state(); set_sound_window_state(); /* Hack: shade the Save> option if appropriate */ if (game_in_progress && character_generated) Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, PDEADCHK); else Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, TRUE); /* | Hack: set up the Term menu as if it was opened over the main | window (so that the Windows> submenu is set correctly) */ menu_term = (term_data *)&data[0]; set_up_term_menu(menu_term); Menu_Show(ibar_menu, pb->data.mouse.pos.x, -1); return TRUE; } if (pb->data.mouse.button.data.select) { show_windows(); grab_caret(); return TRUE; } if (pb->data.mouse.button.data.adjust) { enter_fullscreen_mode(); return TRUE; } return FALSE; } /* * Handler for NULL events (should this check the alarm in the desktop? */ static BOOL Hnd_null(event_pollblock *event, void *ref) { /* Really no need to check the alarm more than twice per second. */ if (alarm_type && Time_Monotonic() > alarm_lastcheck + 200) { check_alarm(); } return TRUE; } /* | Handler for PreQuit messages (eg. at shutdown). */ static BOOL Hnd_PreQuit(event_pollblock * b, void *ref) { BOOL shutdown = (b->data.message.data.words[0] & 1) == 0; task_handle originator = b->data.message.header.sender; unsigned int quitref; message_block mb; char buffer1[64]; os_error e; int ok; if (!(game_in_progress && character_generated)) return TRUE; /* ignore, we're OK to die */ /* Stop the shutdown/quit */ memcpy(&mb, &(b->data.message), 24); quitref = mb.header.yourref; mb.header.yourref = mb.header.myref; Wimp_SendMessage(event_ACK, &mb, originator, 0); /* | We handle this differently depending on the version of the Wimp; | newer versions give us much more flexibility. */ if (event_wimpversion < 350) { /* | Older versions - use 'OK' and 'Cancel'. | There is no "Save & Quit" button. */ Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64); sprintf(buffer1, e.errmess, VARIANT); Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?", e.errmess, 260); e.errnum = 0; SWI(3, 2, SWI_Wimp_ReportError, &e, 3 | 16, buffer1, NULL, &ok); if (ok != 1) return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */ } else { /* | Newer version: can add buttons to the dialog. | we add a 'Save and Quit' button to allow the shutdown to | continue /after/ saving. */ int flags; char buttons[64]; Msgs_Lookup("err.shutbuts:Save & quit,Don't quit,Quit anyway", buttons, 64); Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64); sprintf(buffer1, e.errmess, VARIANT); Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?", e.errmess, 260); e.errnum = 0; flags = 0 | 16 | 256 | (4 << 9); SWI(6, 2, SWI_Wimp_ReportError, &e, flags, buffer1, ICONNAME, 0, buttons, NULL, &ok); if (ok == 4) return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */ if (ok == 3) save_player(); /* Save & Quit */ } /* RO2 doesn't use the shudown flag */ if (shutdown && event_wimpversion >= 300 && mb.header.size >= 24) { key_block kb; kb.code = 0x1fc; /* restart shutdown sequence */ Wimp_SendMessage(event_KEY, (message_block *) & kb, originator, 0); } /* "Time... to die." */ Event_CloseDown(); exit(0); return TRUE; /* The one great certainty (sic) */ } #endif /* FULLSCREEN_ONLY */ static void initialise_terms(void) { char t[80]; int i; #ifndef FULLSCREEN_ONLY if (!minimise_memory) { /* Create a window for each term. Term 0 is special (no scroll bars) */ data[0].w = Window_Create("angband", template_TITLEMIN); data[0].font = SYSTEM_FONT; data[0].def_open = 1; data[0].unopened = 1; sprintf(t, "%s %s", VARIANT, VERSION); Window_SetTitle(data[0].w, t); strncpy(data[0].name, VARIANT, 12); Event_Claim(event_KEY, data[0].w, event_ANY, Hnd_Keypress, (void *)&(data[0])); Event_Claim(event_REDRAW, data[0].w, event_ANY, Hnd_Redraw, (void *)&(data[0])); Event_Claim(event_CLICK, data[0].w, event_ANY, Hnd_TermClick, (void *)&(data[0])); Event_Claim(event_CLOSE, data[0].w, event_ANY, Hnd_MainClose, NULL); for (i = 1; i < MAX_TERM_DATA; i++) { data[i].w = Window_Create("term", template_TITLEMIN); data[i].font = SYSTEM_FONT; data[i].def_open = 0; data[i].unopened = 1; #ifndef OLD_TERM_MENU sprintf(t, "%s (%s %s)", angband_term_name[i], VARIANT, VERSION); #else sprintf(t, "Term-%d (%s %s)", i, VARIANT, VERSION); #endif Window_SetTitle(data[i].w, t); strncpy(data[i].name, t, 12); Event_Claim(event_CLICK, data[i].w, event_ANY, Hnd_TermClick, (void *)&(data[i])); Event_Claim(event_REDRAW, data[i].w, event_ANY, Hnd_Redraw, (void *)&(data[i])); } } #endif /* FULLSCREEN_ONLY */ term_data_link(&(data[0]), 256); for (i = 1; i < MAX_TERM_DATA; i++) { term_data_link(&(data[i]), 16); angband_term[i] = &(data[i].t); } angband_term[0] = &(data[0].t); Term_activate(&(data[0].t)); } /* | Hack(ish) - determine the version of RISC OS */ static int os_version(void) { int osv; SWI(3, 2, SWI_OS_Byte, 129, 0, 255, NULL, &osv); switch (osv) { case 0xa0: return 120; case 0xa1: return 200; case 0xa2: return 201; case 0xa3: return 300; case 0xa4: return 310; case 0xa5: return 350; case 0xa6: return 360; case 0xa7: return 370; case 0xa8: return 400; default: return 370; } return -1; /* -sigh- */ } #ifndef FULLSCREEN_ONLY /* | Determine whether the current screen mode is "high-res" | (ie. should we use the "Sprites22" or the "Sprites" file. */ static int sprites22(void) { int yeig; OS_ReadModeVariable(-1, modevar_YEIGFACTOR, &yeig); return yeig < 2; } /* | Determine whether we should use 2D or 3D templates. | 2D templates *must* be used under Wimp <3.00, but under RO3 we | use the CMOS settings to decide. */ static int templates2d(void) { int r1, r2; if (event_wimpversion < 300) return TRUE; /* The 3D bit is bit 0 of byte 140 */ OS_Byte(osbyte_READCMOSRAM, 140, 0, &r1, &r2); return !(r2 && 1); } #endif /* FULLSCREEN_ONLY */ static unsigned int htoi(char *s) { static const char hex[] = "0123456789ABCDEF"; unsigned int v = 0; while (*s) { char *m; int d = toupper((unsigned char)*s++); m = strchr(hex, d); if (!m) { return v; } v = (v << 4) + (m - hex); } return v; } static int read_unsigned(char *t) { int r; if (SWI(2, 3, SWI_OS_ReadUnsigned, 2, t, NULL, NULL, &r)) r = 0; return r; } /* | Scan the string at 'n', replacing dodgy characters with underbars */ static void sanitise_name(char *n) { for (; *n; n++) { if (strchr("\"$%^&*\\\'@#.,", *n)) *n = '_'; } } /* | Ensure that the path to a given object exists. | Ie. if |p| = "a.b.c.d" then we attempt to | create directories a, a.b and a.b.c if they don't | already exist. | Note that 'd' may be absent. */ static int ensure_path(char *p) { char tmp[260]; char *l = tmp; while (*p) { if (*p == '.') { *l = 0; if (SWI(5, 0, SWI_OS_File, 8, tmp, 0, 0, 77)) return 0; /* Eeek! */ } *l++ = *p++; } return 1; } /* * Set up the Scrap, Choices and Alarm paths, trying for * Choices:blah...,etc. by preference, but falling back on lib/xtra * if need be. */ static void init_paths(void) { char tmp[512]; char subpath[128]; char *v; char *t; /* Form the sub-path we use for both Choices and Scrap dirs: */ v = subpath + sprintf(subpath, "%s", VARIANT); sanitise_name(subpath); sprintf(v, ".%s", VERSION); sanitise_name(v + 1); /* Do the Scrap path first: */ *scrap_path = 0; /* Try for Wimp$ScrapDir... */ t = getenv("Wimp$ScrapDir"); if (t && *t) { sprintf(tmp, "%s.AngbandEtc.%s.", t, subpath); if (ensure_path(tmp)) { strcpy(scrap_path, tmp); } } /* Couldn't use Wimp$ScrapDir, so fall back on lib.xtra.scrap */ if (!*scrap_path) { sprintf(tmp, "%sxtra.scrap.", resource_path); if (ensure_path(tmp)) { strcpy(scrap_path, tmp); } } /* Now set up the Choices and Alarm files: */ /* Read only Choices file is always lib.xtra.Choices */ sprintf(choices_file[CHFILE_READ], "%sXtra.Choices", resource_path); /* Default writable Choices file is the same */ strcpy(choices_file[CHFILE_WRITE], choices_file[CHFILE_READ]); /* No default mirror Choices file */ strcpy(choices_file[CHFILE_MIRROR], ""); /* Read only Alarm file is always lib.xtra.Alarm */ sprintf(alarm_file[CHFILE_READ], "%sXtra.Alarm", resource_path); /* Default writable Alarm file is the same */ strcpy(alarm_file[CHFILE_WRITE], alarm_file[CHFILE_READ]); /* Try to use Choices$Path, etc. for the others... */ t = getenv("Choices$Write"); /* Ie. where choices should be written */ if (t && *t) { /* Choices file: */ sprintf(tmp, "%s.AngbandEtc.%s", t, subpath); if (ensure_path(tmp)) { /* Use for writable file: */ strcpy(choices_file[CHFILE_WRITE], tmp); /* Form 'mirror' filename: same path but with a fixed leafname */ strcpy(v + 1, "Default"); sprintf(tmp, "%s.AngbandEtc.%s", t, subpath); strcpy(choices_file[CHFILE_MIRROR], tmp); } /* Alarm file (doesn't involve subpath) */ sprintf(tmp, "%s.AngbandEtc.Global.Alarm", t); if (ensure_path(tmp)) { /* Use for read/writable file */ strcpy(alarm_file[CHFILE_WRITE], tmp); } } } /* * Return the appropriate (full) pathname. * * For write ops, the read/write file is returned. * * For read ops, either the read/write file, the mirror file, * or the read only file will be returned as appropriate. */ static char *find_choices(int write) { if (write) return choices_file[CHFILE_WRITE]; if (myFile_Size(choices_file[CHFILE_WRITE]) > 0) return choices_file[CHFILE_WRITE]; if (myFile_Size(choices_file[CHFILE_MIRROR]) > 0) return choices_file[CHFILE_MIRROR]; return choices_file[CHFILE_READ]; } static char *find_choices_mirror(void) { return choices_file[CHFILE_MIRROR]; } static char *find_alarmfile(int write) { if (write) return alarm_file[CHFILE_WRITE]; if (myFile_Size(alarm_file[CHFILE_WRITE]) > 0) return alarm_file[CHFILE_WRITE]; return alarm_file[CHFILE_READ]; } int main(int argc, char *argv[]) { int i, j; int start_full = 0; char *arg_savefile = 0; char *t; #ifdef USE_DA int da_font = 1, da_game = 1; #endif atexit(final_acn); /* "I never did care about the little things." */ Start_Hourglass; /* Parse arguments */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (tolower((unsigned char)argv[i][1])) { case 'm': { /* Minimise Memory */ minimise_memory = 1; /* Break */ break; } case 'c': /* -c[a][s][f][] */ for (j = 2; argv[i][j]; j++) { int on = isupper((unsigned char)argv[i][j]); switch (tolower((unsigned char)argv[i][j])) { #ifdef ABBR_FILECACHE case 'a': abbr_filecache = on; break; case 'f': abbr_tmpfile = on; break; #endif #ifdef SMART_FILECACHE case 's': smart_filecache = on; break; #endif case 'p': flush_scrap = !on; break; default: if (isdigit((unsigned char)argv[i][j])) { max_file_cache_size = atoi(argv[i] + j) << 10; while (isdigit((unsigned char)argv[i][++j])) ; if (max_file_cache_size <= 0) { use_filecache = 0; } j--; } else { fprintf(stderr, "Unrecognised option: -c%s", argv[i] + j); exit(EXIT_FAILURE); } } } break; case 'w': /* -waitrelease */ hack_flush = 1; break; case 'e': /* -Evil */ allow_iclear_hack = 1; break; case 's': /* -s */ if (argv[i][2]) arg_savefile = argv[i] + 2; break; case 'f': /* -fullscreen */ start_full = 1; break; case 'h': /* -hourglass */ use_glass = 1; break; case 't': /* -T */ if (argv[i][2]) vfiletype = htoi(argv[i] + 2); break; #ifdef USE_DA case 'd': /* -df, -dg, -dc or -d : disable DAs */ switch (tolower((unsigned char)argv[i][2])) { case 0: /* -d => disable both */ da_font = da_game = 0; break; case 'f': /* -df => disable font only */ da_font = 0; break; case 'g': /* -dg => disable game only */ da_game = 0; break; } break; #endif case '%': /* -% */ { int v = read_unsigned(argv[i] + 2); log_g_malloc = v & 1; show_sound_alloc = v & 2; } break; default: fprintf(stderr, "Unrecognised option: %s", argv[i]); exit(EXIT_FAILURE); } } } /* 1.27 - new handling of -minimise-memory: */ #ifndef FULLSCREEN_ONLY if (minimise_memory) #endif /* FULLSCREEN_ONLY */ { start_full = 1; fs_quit_key_text = "(fullscreen only mode)"; } #ifdef USE_DA init_memory(da_font, da_game); /* Set up dynamic areas, etc. if possible */ /* Install memory allocation hooks */ ralloc_aux = g_malloc; rnfree_aux = g_free; #endif /* Install replacement error reporting routines */ quit_aux = quit_hook; plog_aux = plog_hook; core_aux = core_hook; /* Expand the (Angband) resource path */ t = getenv(RISCOS_VARIANT "$Path"); if (!t || !*t) Msgs_ReportFatal(0, "A resources path could not be formed."); strcpy(resource_path, t); /* Decide where scrap, choices and alarm files live: */ init_paths(); /* Hack: if no savefile specified, use a default */ if (!arg_savefile) { arg_savefile = malloc(strlen(resource_path) + 32); if (!arg_savefile) { Msgs_ReportFatal(0, "err.mem"); } sprintf(arg_savefile, "%s%s", resource_path, ">Save.Savefile"); } /* This crap appears here so that plog() will work properly before init_acn() is called... */ Resource_Initialise(RISCOS_VARIANT); Msgs_LoadFile("Messages"); #ifndef FULLSCREEN_ONLY if (!minimise_memory) { /* This is a hack to only call Event_Initialise3 under RO3 */ if (os_version() < 300) Event_Initialise(RISCOS_VARIANT); else Event_Initialise3(RISCOS_VARIANT, 300, message_list); EventMsg_Initialise(); /* | This is a possible workaround for the FP regs getting | bolloxed in the ! menu because the compiler sets them | up before a call to Wimp_Poll if CSE optimisation is on. | At the moment I've just turned off CSE for the function | affected. | | event_mask.data.keepfpregisters = 1; */ /* Load Templates */ Template_Initialise(); if (templates2d()) Template_LoadFile("Templates2"); else Template_LoadFile("Templates"); /* Load Sprites */ if (sprites22()) resource_sprites = Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites22"); else resource_sprites = Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites"); } #endif /* FULLSCREEN_ONLY */ Screen_CacheModeInfo(); /* Initialise some ZapRedraw stuff */ initialise_palette(); initialise_fonts(); /* Set up the fonts */ #ifndef FULLSCREEN_ONLY initialise_r_data(); /* Set up the r_data buffer */ if (!minimise_memory) { /* Initialise some Wimp specific stuff */ init_gamma_window(); init_sound_window(); init_save_window(); init_menus(); ibar_icon = Icon_BarIcon(ICONNAME, iconbar_RIGHT); /* Global handlers */ Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL); Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL); Event_Claim(event_GAINCARET, event_ANY, event_ANY, Hnd_Caret, (void *)1); Event_Claim(event_LOSECARET, event_ANY, event_ANY, Hnd_Caret, (void *)0); Event_Claim(event_MENU, event_ANY, event_ANY, Hnd_MenuSel, NULL); Event_Claim(event_CLICK, window_ICONBAR, ibar_icon, Hnd_IbarClick, NULL); Event_Claim(event_CLICK, event_ANY, event_ANY, Hnd_Click, NULL); EventMsg_Claim(message_PALETTECHANGE, event_ANY, Hnd_PaletteChange, NULL); EventMsg_Claim(message_MODECHANGE, event_ANY, Hnd_ModeChange, NULL); EventMsg_Claim(message_PREQUIT, event_ANY, Hnd_PreQuit, NULL); /* Initialise the sound stuff */ initialise_sound(); } #endif /* FULLSCREEN_ONLY */ /* Initialise some Angband stuff */ initialise_terms(); load_choices(); read_alarm_choices(); init_file_paths(unixify_name(resource_path)); Start_Hourglass; /* Paranoia */ /* Hack - override the saved options if -F was on the command line */ start_fullscreen |= start_full; /* hack so that the cursor is yellow if undefined */ if (palette[CURSOR_COLOUR] == palette[0]) { angband_color_table[CURSOR_COLOUR][1] = (CURSOR_RGB & 0xff00) >> 8; angband_color_table[CURSOR_COLOUR][2] = (CURSOR_RGB & 0xff0000) >> 16; angband_color_table[CURSOR_COLOUR][3] = (CURSOR_RGB & 0xff000000) >> 24; } /* Catch nasty signals */ signals_init(); /* use pref-acn.prf */ ANGBAND_SYS = "acn"; #ifndef FULLSCREEN_ONLY if (start_fullscreen) { #endif /* FULLSCREEN_ONLY */ enter_fullscreen_mode(); #ifndef FULLSCREEN_ONLY } else { Start_Hourglass; /* Paranoia */ Hnd_ModeChange(NULL, NULL); /* Caches the various fonts/palettes */ show_windows(); grab_caret(); Event_Claim(event_NULL, event_ANY, event_ANY, Hnd_null, NULL); /* Wait for a null poll so that the windows can appear */ do { Event_Poll(); } while (event_lastevent.type != 0); } #endif /* FULLSCREEN_ONLY */ /* Initialise Angband */ Start_Hourglass; /* Paranoia */ strncpy(savefile, unixify_name(arg_savefile), sizeof(savefile)); savefile[sizeof(savefile) - 1] = '\0'; use_sound = 1; init_angband(); initialised = 1; game_in_progress = 1; pause_line(23); flush(); /* Stop_Hourglass; */ play_game(FALSE); if (fullscreen_mode) leave_fullscreen_mode(); Stop_Hourglass; quit(NULL); return 0; debug("to stop the 'unused' warning :)"); } /*--------------------------------------------------------------------------*/ /* Stuff below here is for the full screen display */ /*--------------------------------------------------------------------------*/ static errr Term_xtra_acn_checkFS(void); static errr Term_xtra_acn_eventFS(void); static errr Term_xtra_acn_reactFS(int force); static errr Term_curs_acnFS(int x, int y); static errr Term_xtra_acn_clearFS(void); static errr Term_xtra_acnFS(int n, int v); static errr Term_wipe_acnFS(int x, int y, int n); static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s); static void bored(void); static void redraw_areaFS(int x, int y, int w, int h); static void draw_cursor(int x, int y); /* | We use this to keep the mouse in the same place on return from full-screen | mode. */ static unsigned char old_mouse_posn[5]; /* | Take a copy of the current mode descriptor/number and return either | a pointer to it (as an int) if it's a new mode, or the mode number. | NB: A static pointer is used and the descriptor returned is only | valid until the next call to this function. | | Basically, a replacement for OS_Byte 135 / OS_ScreenMode1, IYSWIM. */ static int current_mode(void) { static void *descriptor = NULL; int mode; int size; int i; int *vals; if (descriptor) { free(descriptor); descriptor = NULL; } SWI(1, 3, SWI_OS_Byte, 135, NULL, NULL, &mode); if (mode < 256) { return mode; } vals = (int *)(mode + 20); for (i = 0; vals[i] != -1; i += 2) ; size = 24 + 8 * i; /* Size of data */ descriptor = malloc(size); if (!descriptor) { core("Out of memory!"); } memcpy(descriptor, (void *)mode, size); return (int)descriptor; } /* | Select the best mode we can for full screen. | Returns 12 for (low-res, ie. mode 12) or 27 for high-res, | or a pointer to a mode descriptor (as an int). */ static int select_fullscreen_mode(void) { static struct { int flags, x, y, l2bpp, hz, term; } desc; int mode = 0; desc.flags = 1; /* format 0 */ desc.x = 640; desc.y = 480; /* 640x480 */ desc.l2bpp = 2; /* 16 colours */ desc.hz = -1; /* best we can get */ desc.term = -1; /* don't fuss about modevars */ SWI(1, 1, SWI_OS_CheckModeValid, &desc, &mode); if (mode != (int)&desc) { SWI(1, 1, SWI_OS_CheckModeValid, 27, /**/ &mode); if (mode != 27) { SWI(1, 1, SWI_OS_CheckModeValid, 12, /**/ &mode); if (mode != 12) { mode = 0; } } } return mode; } /* | Change screen mode */ static void change_screenmode(int to) { if (SWI(2, 0, SWI_OS_ScreenMode, 0, to)) { if (to < 256) { GFX_VDU(22); GFX_VDU(to); } else { /* Finished with my woman / cos she couldn't help me with ... */ core("Eeek! mode isn't valid, but it /should/ be..."); } } } /* | Constrain the mouse pointer to a point - this means that the damn | hourglass won't move around with the mouse :) */ static void constrain_pointer(void) { wimp_rect r; int ys = screen_eig.y == 1 ? 32 : 64; /* Cope with dbl height glass */ Screen_CacheModeInfo(); /* Make sure we know the screen size */ r.min.x = r.max.x = screen_size.x - 32; r.min.y = r.max.y = screen_size.y - ys; Pointer_RestrictToRect(r); } /* | Convert a 1bpp bitmap into a 4bpp bitmap (bit flipped) */ static int byte_to_word_flipped(int b) { int w; if (b & 128) { w = 0xf0000000; } else { w = 0; } if (b & 64) { w |= 0x0f000000; } if (b & 32) { w |= 0x00f00000; } if (b & 16) { w |= 0x000f0000; } if (b & 8) { w |= 0x0000f000; } if (b & 4) { w |= 0x00000f00; } if (b & 2) { w |= 0x000000f0; } if (b & 1) { w |= 0x0000000f; } return w; } /* | try to load the fallback fullscreen font and convert it to 4bpp */ static int cache_zapfontHR(void) { int handle; unsigned int extent; char buffer[260]; struct { char id[8]; int w, h, f, l, r1, r2; } zfh; int *op; char *ip; int l, i; /* Try to open the file */ sprintf(buffer, "%s%s", resource_path, "xtra.FullScreen"); handle = myFile_Open(buffer, 0x4f); if (!handle) { return 0; } /* Check file's extent */ extent = myFile_Extent(handle); if (extent > sizeof(zfh) + 256 * 16) { myFile_Close(handle); return 0; } /* Load the header */ if (myFile_ReadBytes(handle, &zfh, sizeof(zfh))) { myFile_Close(handle); return 0; } /* Check font size */ if ((zfh.w != 8) || (zfh.h > 16)) { myFile_Close(handle); return 0; } /* Load the 1bpp data */ if (myFile_ReadBytes(handle, fullscreen_font, extent - sizeof(zfh))) { myFile_Close(handle); return 0; } myFile_Close(handle); l = zfh.l > 255 ? 255 : zfh.l; if (zfh.h > 8) { op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 4); ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h - (zfh.f * zfh.h)); while (l-- >= zfh.f) { for (i = 0; i < zfh.h; i++) { *--op = byte_to_word_flipped(*--ip); } } fullscreen_height = zfh.h; } else { op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 8); ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h - (zfh.f * zfh.h)); while (l-- >= zfh.f) { for (i = -zfh.h; i < zfh.h; i++) { int t = byte_to_word_flipped(*--ip); *--op = t; *--op = t; } } fullscreen_height = zfh.h * 2; } fullscreen_topline = TERM_TOPLINE_HR; fullscreen_topline += ((16 - fullscreen_height) * 13); return 1; } static int cache_fs_fontHR(void) { ZapFont *src = SYSTEM_FONT; int c; int *op; char *ip; /* Allocate the storage for the font */ fullscreen_font = f_malloc(256 * 4 * 16); if (!fullscreen_font) { return 0; } op = (int *)fullscreen_font; /* Check to see if the main term's font is suitable (ie. 8x16 or 8x8) */ if ((data[0].font->w == 8) && (data[0].font->h <= 16)) src = data[0].font; /* | Hack: if we're forced to use the system font, try to load the | 'fullscreen' font from lib.xtra. If that fails, then I guess we're | stuck with the system font. */ if (src == SYSTEM_FONT) if (cache_zapfontHR()) { return 1; } ip = (char *)(src->bpp_1); /* Now, create the font */ if (src->h > 8) { int e = src->h * (src->l > 256 ? 256 : src->l); op += (src->f * src->h); for (c = src->f * src->h; c < e; c++) *op++ = byte_to_word_flipped(*ip++); fullscreen_height = src->h; } else { int e = src->h * (src->l > 256 ? 256 : src->l); op += (src->f * src->h) * 2; for (c = src->f * src->h; c < e; c++) { int t = byte_to_word_flipped(*ip++); *op++ = t; *op++ = t; } fullscreen_height = src->h * 2; } fullscreen_topline = TERM_TOPLINE_HR; fullscreen_topline += ((16 - fullscreen_height) * 13); return 1; } static int cache_fs_fontLR(void) { ZapFont *src = SYSTEM_FONT; int c, e; int *op; char *ip; /* Allocate the storage for the font */ fullscreen_font = f_malloc(256 * 4 * 8); if (!fullscreen_font) { return 0; } op = (int *)fullscreen_font; /* Check to see if the main term's font is suitable (ie. 8x8) */ if ((data[0].font->w == 8) && (data[0].font->h <= 8)) src = data[0].font; ip = (char *)(src->bpp_1); /* Now, create the font */ e = src->h * (src->l > 256 ? 256 : src->l); op += (src->f * src->h); for (c = src->f * src->h; c < e; c++) *op++ = byte_to_word_flipped(*ip++); fullscreen_height = src->h; fullscreen_topline = TERM_TOPLINE_LR; fullscreen_topline += ((8 - fullscreen_height) * 13); return 1; } static void set_keys(int claim) { static int old_c_state; static int old_f_state[8]; int i; if (claim) { /* Cursors/copy act as function keys */ /* f0-f9, cursors, generate 0x80-0x8f */ /* sh-f0-f9,cursors, generate 0x90-0x9f */ /* ctrl f0-f9,cursors, generate 0xa0-0xaf */ /* sh-c-f0-f9,cursors, generate 0xb0-0xbf */ /* f10-f12 generate 0xca-0xcc */ /* shift f10-f12 generate 0xda-0xdc */ /* ctrl f10-f12 generate 0xea-0xec */ /* ctrlshift f10-f12 generate 0xfa-0xfc */ SWI(3, 2, SWI_OS_Byte, 4, 2, 0, /**/ NULL, &old_c_state); for (i = 0; i < 4; i++) { SWI(3, 2, SWI_OS_Byte, 225 + i, 0x80 + (i * 0x10), 0, NULL, old_f_state + i); SWI(3, 2, SWI_OS_Byte, 221 + i, 0xc0 + (i * 0x10), 0, NULL, old_f_state + i + 4); } } else { SWI(3, 0, SWI_OS_Byte, 4, old_c_state, 0); for (i = 0; i < 4; i++) { SWI(3, 0, SWI_OS_Byte, 225 + i, old_f_state[i], 0); SWI(3, 0, SWI_OS_Byte, 221 + i, old_f_state[i + 4], 0); } } } /* | Enter the full screen mode. | | Full screen display uses either mode 27 (if supported) and 8x16 fonts | (or system font 'twiddled' to double height), or mode 12 (if mode 27 | is unavailable) and the system font (or an 8x8 font). | */ static void enter_fullscreen_mode(void) { int vduvars[2] = { 149, -1 }; int i; /* New in 1.18 - protect against 're-entracy' */ if (fullscreen_font) return; /* New in 1.20 - hack IClear out of the way */ if (allow_iclear_hack) iclear_hack(); /* Remember where the mouse is */ old_mouse_posn[0] = 4; SWI(2, 0, SWI_OS_Word, 21, &old_mouse_posn); /* Choose the mode we want */ fullscreen_mode = select_fullscreen_mode(); if (!fullscreen_mode) { plog("Unable to select a suitable screen mode (27 or 12)"); return; } if (!((fullscreen_mode == 12) ? cache_fs_fontLR() : cache_fs_fontHR())) { plog("Unable to cache a font for full screen mode"); return; } /* Read the current screen mode */ /* SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &old_screenmode ); */ old_screenmode = current_mode(); Stop_Hourglass; /* Change to the chosen screen mode */ change_screenmode(fullscreen_mode); /* Restrict the pointer */ constrain_pointer(); /* Remove the cursors */ SWI(0, 0, SWI_OS_RemoveCursors); Start_Hourglass; /* Get the base address of screen memory */ SWI(2, 0, SWI_OS_ReadVduVariables, vduvars, vduvars); fullscreen_base = (int *)(vduvars[0]); /* Fudge the Term interface */ for (i = 0; i < MAX_TERM_DATA; i++) { term *t = &(data[i].t); t->xtra_hook = Term_xtra_acnFS; t->wipe_hook = Term_wipe_acnFS; t->curs_hook = Term_curs_acnFS; t->text_hook = Term_text_acnFS; } /* Grab the palette */ Term_xtra_acn_reactFS(TRUE); /* Make sure that the keys work properly */ set_keys(TRUE); /* refresh the term */ /*Term_activate( &(data[0].t) ); */ redraw_areaFS(0, 0, 80, 24); if (data[0].cursor.visible) draw_cursor(data[0].cursor.pos.x, data[0].cursor.pos.y); /* Display a reminder of how to get back... */ /* Hack: disable force_mono */ i = force_mono; force_mono = 0; Term_text_acnFS(0, TIME_LINE, strlen(fs_quit_key_text), 8, fs_quit_key_text); force_mono = i; } static void leave_fullscreen_mode(void) { int i; /* New in 1.18 - protect against 're-entracy' */ if (!fullscreen_font) return; /* Restore the Term interface */ for (i = 0; i < MAX_TERM_DATA; i++) { #ifndef FULLSCREEN_ONLY term *t = &(data[i].t); t->xtra_hook = Term_xtra_acn; t->wipe_hook = Term_wipe_acn; t->curs_hook = Term_curs_acn; t->text_hook = Term_text_acn; #endif mark_ood(&(data[i]), 0, 0, 80, 24); } /* Deallocate the font */ f_free(fullscreen_font); fullscreen_font = 0; fullscreen_mode = 0; Stop_Hourglass; /* Restore the screen mode */ Wimp_SetMode(old_screenmode); /* Restore the pointer position */ old_mouse_posn[0] = 3; SWI(2, 0, SWI_OS_Word, 21, &old_mouse_posn); Start_Hourglass; /* Restore the various soft keys */ set_keys(FALSE); /* New in 1.20 Remove the IClear hack */ if (allow_iclear_hack) remove_iclear_hack(); #ifndef FULLSCREEN_ONLY /* Refresh the windows - this probably isn't necessary anyway */ if (!minimise_memory) refresh_windows(); #endif /* FULLSCREEN_ONLY */ } static void fs_writechars(int x, int y, int n, const char *chars, char attr) { int *scr, *scrb; int *cdat; int j; unsigned int fgm; if (force_mono) { if (attr != TERM_DARK) { attr = TERM_WHITE; } } fgm = (unsigned int)zpalette[(unsigned int) attr]; scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320 + x * 4 + 320 * fullscreen_topline); while (n--) { scr = scrb++; cdat = (int *)(((int)fullscreen_font) + (*chars++) * (fullscreen_height << 2)); for (j = 0; j < fullscreen_height; j++) { *scr = *cdat++ & fgm; scr += 80; } } } static void fs_writechar(int x, int y, char c, char attr) { int *scrb; int *cdat; int j; unsigned int fgm; if (force_mono) { if (attr != TERM_DARK) { attr = TERM_WHITE; } } fgm = (unsigned int)zpalette[(unsigned int) attr]; scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320 + x * 4 + 320 * fullscreen_topline); cdat = (int *)(((int)fullscreen_font) + (c * (fullscreen_height << 2))); for (j = 0; j < fullscreen_height; j++) { *scrb = *cdat++ & fgm; scrb += 80; } } static void draw_cursorHR(int x, int y) { ColourTrans_SetGCOL(cursor_rgb, 0, 0); GFX_Move(x * 16, 959 - y * (fullscreen_height * 2) - fullscreen_topline * 2); GFX_DrawBy(14, 0); GFX_DrawBy(0, -(fullscreen_height * 2 - 2)); GFX_DrawBy(-14, 0); GFX_DrawBy(0, fullscreen_height * 2 - 2); } static void draw_cursorLR(int x, int y) { ColourTrans_SetGCOL(cursor_rgb, 0, 0); GFX_Move(x * 16, 1023 - y * (fullscreen_height * 4) - fullscreen_topline * 4); GFX_DrawBy(14, 0); GFX_DrawBy(0, -(fullscreen_height * 4 - 4)); GFX_DrawBy(-14, 0); GFX_DrawBy(0, fullscreen_height * 4 - 4); } static void draw_cursor(int x, int y) { if (fullscreen_mode == 12) draw_cursorLR(x, y); else draw_cursorHR(x, y); } static void redraw_areaFS(int x, int y, int w, int h) { int i, j; for (j = y; j < y + h; j++) for (i = x; i < x + w; i++) fs_writechar(i, j, data[0].t.old->c[j][i], data[0].t.old->a[j][i]); } static int wimp_code(int c) { /* shift/ctrl keypad? */ if (c >= '0' && c <= '9') { kbd_modifiers m = Kbd_GetModifiers(FALSE); if (m.shift) { c |= 0x800; } if (m.ctrl) { c |= 0x400; } return c; } if (c == 9) { return 0x18a; } /* Tab */ if (c <= 127) { return c; } /* normal ASCII/ctrl */ if (c >= 0x80 && c <= 0xff) { return c + 0x100; } /* f0-f9, etc. */ return -1; /* unknown */ } static void do_keypress(int code) { static const char hex[] = "0123456789ABCDEF"; if (code == KEYPRESS_QUIT && !minimise_memory) { #ifdef FULLSCREEN_ONLY Sound_SysBeep(); #else leave_fullscreen_mode(); #endif return; } if (code == 27) { if (Kbd_KeyDown(inkey_CTRL)) { ack_alarm(); return; } } if (code <= 255) { Term_keypress(code); } else { Term_keypress(31); Term_keypress(hex[(code & 0xf00) >> 8]); Term_keypress(hex[(code & 0x0f0) >> 4]); Term_keypress(hex[(code & 0x00f)]); Term_keypress(13); } } static errr Term_xtra_acn_checkFS(void) { int bh, bl; int c; Stop_Hourglass; bored(); SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh); bl = (bl & 0xff) + (bh << 8); if (bl > 0) { SWI(0, 1, SWI_OS_ReadC, &c); bl = wimp_code(c); if (bl >= 0) { do_keypress(bl); } } Start_Hourglass; return 0; } static errr Term_xtra_acn_eventFS(void) { int c; int w = -1; Stop_Hourglass; for (w = -1; w == -1;) { int bh, bl; do { bored(); SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh); bl = (bl & 0xff) + (bh << 8); } while (!bl); SWI(0, 1, SWI_OS_ReadC, &c); w = wimp_code(c); if (w >= 0) { do_keypress(w); } } Start_Hourglass; return 0; } /* * React to changes */ static errr Term_xtra_acn_reactFS(int force) { unsigned int i; int p, r, g, b; static double old_gamma = -1.0; if (gamma != old_gamma) { force = 1; old_gamma = gamma; } /* Set the screen colours */ for (i = 0; i < 16; i++) { if (COLOUR_CHANGED(i) || force) { r = (int)(255.0 * pow(angband_color_table[i][1] / 255.0, 1.0 / gamma)); g = (int)(255.0 * pow(angband_color_table[i][2] / 255.0, 1.0 / gamma)); b = (int)(255.0 * pow(angband_color_table[i][3] / 255.0, 1.0 / gamma)); GFX_VDU(19); GFX_VDU(i); GFX_VDU(16); GFX_VDU(r); GFX_VDU(g); GFX_VDU(b); palette[i] = (b << 24) | (g << 16) | (r << 8); p = i; p |= (p << 4); p |= (p << 8); p |= (p << 16); zpalette[i] = p; a_palette[i][1] = angband_color_table[i][1]; a_palette[i][2] = angband_color_table[i][2]; a_palette[i][3] = angband_color_table[i][3]; /* Find any higher colour numbers and make them "wrong" */ for (p = 16; p < 256; p++) if ((zpalette[p] & 0xf) == i) a_palette[p][1] = angband_color_table[p][1] + 2; } } /* Go through the palette updating any changed values */ for (i = 16; i < 256; i++) { if (COLOUR_CHANGED(i) || force) { r = (int)(255.0 * pow(angband_color_table[i][1] / 255.0, 1.0 / gamma)); g = (int)(255.0 * pow(angband_color_table[i][2] / 255.0, 1.0 / gamma)); b = (int)(255.0 * pow(angband_color_table[i][3] / 255.0, 1.0 / gamma)); p = (b << 24) | (g << 16) | (r << 8); palette[i] = p; SWI(1, 1, SWI_ColourTrans_ReturnColourNumber, palette[i], &p); p |= (p << 4); p |= (p << 8); p |= (p << 16); zpalette[i] = p; a_palette[i][1] = angband_color_table[i][1]; a_palette[i][2] = angband_color_table[i][2]; a_palette[i][3] = angband_color_table[i][3]; } } cursor_rgb = palette[CURSOR_COLOUR]; return 0; } static errr Term_curs_acnFS(int x, int y) { if (Term == &(data[0].t)) { if (data[0].cursor.visible) redraw_areaFS(data[0].cursor.pos.x, data[0].cursor.pos.y, 1, 1); data[0].cursor.pos.x = x; data[0].cursor.pos.y = y; if (data[0].cursor.visible) draw_cursor(x, y); } return 0; } static errr Term_xtra_acn_clearFS(void) { char e[80]; int j; if (Term == &(data[0].t)) { for (j = 0; j < 80; j++) e[j] = ' '; GFX_Wait(); for (j = 0; j < 24; j++) fs_writechars(0, j, 80, e, 0); } return 0; } static errr Term_xtra_acnFS(int n, int v) { term_data *t = (term_data *)Term; switch (n) { case TERM_XTRA_EVENT: if (v) return Term_xtra_acn_eventFS(); else return Term_xtra_acn_checkFS(); case TERM_XTRA_BORED: bored(); return Term_xtra_acn_checkFS(); case TERM_XTRA_FLUSH: /* 1.21 - Hack: wait until no keys are pressed */ if (hack_flush) for (v = 0; v != 0xff;) SWI(1, 2, SWI_OS_Byte, 122, 0, &v); SWI(3, 0, SWI_OS_Byte, 21, 0, 0); /* Flush Kbd buffer */ return 0; case TERM_XTRA_FRESH: return 0; case TERM_XTRA_FROSH: return 0; case TERM_XTRA_SHAPE: if (t == (&data[0])) { t->cursor.visible = v; if (v) draw_cursor(t->cursor.pos.x, t->cursor.pos.y); else redraw_areaFS(t->cursor.pos.x, t->cursor.pos.y, 1, 1); } return 0; case TERM_XTRA_NOISE: Sound_SysBeep(); return 0; case TERM_XTRA_REACT: return Term_xtra_acn_reactFS(FALSE); case TERM_XTRA_DELAY: if (v > 0) { unsigned int start = Time_Monotonic(); v = (v + 5) / 10; /* Round to nearest cs */ GFX_Wait(); while ((Time_Monotonic() - start) < v) ; } return (0); case TERM_XTRA_SOUND: /* Play a sound :) */ if (enable_sound) { play_sound(v); } return 0; default: return 1; } } static errr Term_wipe_acnFS(int x, int y, int n) { if (Term == &(data[0].t)) while (n--) fs_writechar(x++, y, ' ', 0); return 0; } static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s) { if (Term == &(data[0].t)) fs_writechars(x, y, n, s, (char)a); return 0; } static void bored() { static int last = -1; char ts[80]; time_t ct; struct tm *lt; unsigned int l; int ofm; static int alarm_flash = 1; /* Really no need to check the alarm more than once per second. */ if (alarm_type && Time_Monotonic() > alarm_lastcheck + 200) { check_alarm(); } l = Time_Monotonic(); if ((l - last) < (alarm_flash ? 25 : 50)) { return; } last = l; time(&ct); lt = localtime(&ct); l = strftime(ts, 80, "%c %Z", lt); /* Hack: disable force_mono around printing the time */ ofm = force_mono; force_mono = 0; /* Hack: Is the alarm supposed to be going off? */ if (alarm_disp || alarm_flash) { char blk[60]; int c = 8; if (!alarm_disp) { alarm_flash = 11; } switch (alarm_flash / 2) { case 4: sprintf(blk, "%-57s", alarm_cancel_text); break; case 5: sprintf(blk, "%-57s", fs_quit_key_text); break; default: c = alarm_flash & 1 ? TERM_RED : TERM_WHITE; sprintf(blk, "%02d:%02d %-51s", alarm_h, alarm_m, alarm_message); } fs_writechars(0, TIME_LINE, 57, blk, c); if (++alarm_flash > 11) { alarm_flash = 0; } } /* Display time */ fs_writechar(79 - l, TIME_LINE, ' ', 0); fs_writechars(80 - l, TIME_LINE, l, ts, 8); force_mono = ofm; } #ifdef USE_DA /*--------------------------------------------------------------------------*/ /* (Simple) Heap management (using OS_Heap) */ /*--------------------------------------------------------------------------*/ typedef void *heap; static os_error *Heap_Initialise(heap h, size_t size) { return SWI(4, 0, SWI_OS_Heap, 0, h, 0, size); } static void *Heap_Claim(heap h, size_t size) { void *fred; os_error *e; e = SWI(4, 3, SWI_OS_Heap, 2, h, 0, size, NULL, NULL, &fred); return e ? NULL : fred; } static os_error *Heap_Release(heap h, void *block) { return SWI(3, 0, SWI_OS_Heap, 3, h, block); } static int Heap_ChangeHeapSize(heap h, int resize_by) { int by; SWI(4, 4, SWI_OS_Heap, 5, h, 0, resize_by, 0, 0, 0, &by); return by; } /*--------------------------------------------------------------------------*/ /* Stuff below here is for using Dynamic areas (under RO3.5+) */ /*--------------------------------------------------------------------------*/ static int game_area = -1; /* The DA the game is using */ static int font_area = -1; /* The DA the fonts are using */ static void *game_area_base; /* base address of game area */ static void *font_area_base; /* base address of font area */ static int font_area_size; /* size of the fonts' DA */ static int font_heap_size; /* size of the fonts' heap */ static int game_area_size; /* size of the game's DA */ static int game_heap_size; /* size of the game's heap */ #define MAX_F_DA_SIZE (2<<20) /* Max size of font area (2Mb) */ #define MAX_G_DA_SIZE (4<<20) /* Max size of game area (4Mb) */ #define SHRINK_GRAN (4<<10) /* Try to recalaim wastage > this (4Kb) */ /* | Free dynamic areas when we exit */ static void cleanup_memory(void) { if (game_area != -1) { SWI(2, 0, SWI_OS_DynamicArea, 1, game_area); game_area = -1; } if (font_area != -1) { SWI(2, 0, SWI_OS_DynamicArea, 1, font_area); font_area = -1; } } /* | Set up the memory allocation stuff. | We check to see if DAs are possible and if so initialise two: | one for the game's use (via the rnalloc() hooks) and one for | our own use (for fonts, etc). | | Each area is created 16Kb in size, with a max size of 2/4Mb. | | If 'daf' is TRUE, an area is created for the fonts. | If 'dag' is TRUE, an area is created for the game. */ static void init_memory(int daf, int dag) { os_error *e = NULL; if (!daf) { /* Paranoia */ font_area = -1; font_area_base = 0; } else { e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */ -1, /* Let OS allocate no. */ 16 << 10, /* Initial size */ -1, /* Let OS allocate address */ 1 << 7, /* Cacheable, bufferable, RW */ MAX_F_DA_SIZE, /* Max size */ 0, /* handler */ 0, /* handler workspace */ VARIANT " font data", /* Name */ /* */ NULL, /* r0 */ &font_area, /* area number allocated */ NULL, /* r2 */ &font_area_base /* base address of area */ ); if (e) { game_area = font_area = -1; game_area_base = font_area_base = 0; /* paranoia */ return; } else { e = SWI(2, 3, SWI_OS_DynamicArea, 2, font_area, NULL, NULL, &font_area_size); if (e) { Error_ReportFatal(e->errnum, "%d:%s", e->errmess); } e = Heap_Initialise((heap) font_area_base, font_area_size); if (e) { Error_ReportFatal(e->errnum, "%d:%s", e->errmess); } font_heap_size = font_area_size; } } /* Make sure DA(s) are removed when we quit */ atexit(cleanup_memory); if (!dag) { /* Paranoia */ game_area = -1; game_area_base = 0; } else { e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */ -1, /* Let OS allocate no. */ 16 << 10, /* Initial size */ -1, /* Let OS allocate address */ 1 << 7, /* Cacheable, bufferable, RW */ MAX_G_DA_SIZE, /* Max size */ 0, /* handler */ 0, /* handler workspace */ VARIANT " game data", /* Name */ /* */ NULL, /* r0 */ &game_area, /* area number allocated */ NULL, /* r2 */ &game_area_base /* base address of area */ ); if (e) { game_area = -1; game_area_base = 0; /* paranoia */ } else { e = SWI(2, 3, SWI_OS_DynamicArea, 2, game_area, NULL, NULL, &game_area_size); if (e) { Error_ReportFatal(e->errnum, "%d:%s", e->errmess); } e = Heap_Initialise((heap) game_area_base, game_area_size); if (e) { Error_ReportFatal(e->errnum, "%d:%s", e->errmess); } game_heap_size = game_area_size; } } } static int grow_dynamicarea(int area, int by) { os_error *e; e = SWI(2, 2, SWI_OS_ChangeDynamicArea, area, by, /**/ NULL, &by); /* Can't check errors since a 'failed' shrink returns one... */ return by; } /* | Try to shrink the font-cache heap and area as much as possible. */ static void f_shrink_heap(void) { int s; /* Shrink the heap as far as possible */ font_heap_size -= Heap_ChangeHeapSize((heap) font_area_base, -MAX_F_DA_SIZE); /* Shrink the dynamic area if necessary */ s = font_area_size - font_heap_size; if (s >= SHRINK_GRAN) font_area_size -= grow_dynamicarea(font_area, -s); } /* | Allocate a block of memory in the font heap */ static void *f_malloc(size_t size) { void *c; int s; if (font_area == -1) { return malloc(size); } c = Heap_Claim((heap) font_area_base, size); if (!c) { /* The Claim failed. Try to grow the area by the size of the block */ s = grow_dynamicarea(font_area, size + 64); /* 64 is overkill */ if (!s) { return NULL; } font_area_size += s; s = font_area_size - font_heap_size; font_heap_size += Heap_ChangeHeapSize((heap) font_area_base, s); c = Heap_Claim((heap) font_area_base, size); if (c) { f_shrink_heap(); } } return c; } /* | Free a block of memory in the font heap */ static void f_free(void *blk) { os_error *e; if (font_area == -1) { free(blk); return; } e = Heap_Release((heap) font_area_base, blk); if (e) Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess); f_shrink_heap(); } /* | Allocate a block of memory in the game heap */ static vptr g_malloc(huge size) { void *c; int s; if (game_area == -1) { return malloc((size_t) size); } c = Heap_Claim((heap) game_area_base, (size_t) size + 4); if (!c) { /* The Claim failed. Try to grow the area by the size of the block */ s = grow_dynamicarea(game_area, (size_t) size + 64); /* 64 is overkill */ if (!s) { return NULL; } game_area_size += s; s = game_area_size - game_heap_size; game_heap_size += Heap_ChangeHeapSize((heap) game_area_base, s); c = Heap_Claim((heap) game_area_base, (size_t) size + 4); } if (c) { strcpy((char *)c, "MUSH"); c = (void *)(((int)c) + 4); } if (log_g_malloc) fprintf(stderr, "ralloc(%ld) == %p\n", (long)size, c); return c; } /* | Free a block of memory in the game heap | | The 'len' is to be compatible with z-virt.c (we don't need/use it) | Returns NULL. */ static vptr g_free(vptr blk) { os_error *e; int s; if (game_area == -1) { free(blk); return NULL; } if (log_g_malloc) fprintf(stderr, "rnfree(%p)\n", blk); if (strncmp(((char *)blk) - 4, "MUSH", 4)) core("game heap corrupt / bad attempt to free memory"); blk = (void *)(((int)blk) - 4); e = Heap_Release((heap) game_area_base, blk); if (e) Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess); /* Shrink the heap as far as possible */ game_heap_size -= Heap_ChangeHeapSize((heap) game_area_base, -MAX_G_DA_SIZE); /* Shrink the dynamic area if necessary */ s = game_area_size - game_heap_size; if (s >= SHRINK_GRAN) game_area_size -= grow_dynamicarea(game_area, -s); return NULL; } #endif /* USE_DA */ /*--------------------------------------------------------------------------*/ /* | New to 1.04: Sound support :) | | We use the PlayIt module (for convenience). | | The Lib/xtra/sound/sound.cfg file is used to map sample names onto | event names. | | Since textual names are used in the .cfg file, we need to have a lookup | table to translate them into numbers. At present we use the | angband_sound_name array defined in variable.c | | Since there can be multiple sounds for each event we need to use a | list to store them. */ /* NB: This will be clipped to 10 under RISC OS 2 */ #define MAX_SAMPNAME_LEN 64 /* | The list format: */ typedef struct samp_node { char sample_name[MAX_SAMPNAME_LEN + 1]; /* Sample name */ struct samp_node *next; /* -> next node */ } SampNode; typedef struct samp_info { int samples; /* # samples for this event */ SampNode *samplist; /* list of sample names */ } SampInfo; /* | Just need an array of SampInfos */ static SampInfo sample[SOUND_MAX]; /* | This flag will only be set non-zero if the SampInfo array is | valid. */ static int sound_initd = 0; static void read_sound_config(void) { int i; char buffer[2048]; FILE *f; int max_sampname_len = truncate_names()? 10 : MAX_SAMPNAME_LEN; FILE *dbo = NULL; if (show_sound_alloc) { sprintf(buffer, "%s%s", resource_path, "sndmap/out"); dbo = fopen(buffer, "w"); if (!dbo) { core("can't create sndmap/out debugging file"); } } if (!sound_initd) { /* Initialise the sample array */ for (i = 0; i < SOUND_MAX; i++) { sample[i].samples = 0; sample[i].samplist = NULL; } sound_initd = 1; } else { /* Deallocate the sample lists */ for (i = 0; i < SOUND_MAX; i++) { SampNode *si = sample[i].samplist; sample[i].samples = 0; sample[i].samplist = NULL; while (si) { SampNode *ns = si->next; free(si); si = ns; } } } /* Open the config file */ sprintf(buffer, "%sSound:%s", RISCOS_VARIANT, "sound/cfg"); f = fopen(buffer, "r"); /* No cfg file => no sounds */ if (!f) { if (show_sound_alloc) { fprintf(dbo, "** Can't open cfg file '%s'\n", buffer); fclose(dbo); } return; } /* Parse the file */ while (fgets(buffer, sizeof(buffer), f)) { char *sample_name; int event_number; /* Skip comments and lines that begin with whitespace */ if (*buffer == '#' || isspace((unsigned char)*buffer)) { continue; } /* Hack: ignore any line beginning '[' (section marker) */ if (*buffer == '[') { continue; } /* Place a NULL after the event name and find the first sample name */ sample_name = buffer; while (*sample_name && !isspace((unsigned char)*sample_name)) sample_name++; /* Bad line? */ if (*sample_name == 0) { continue; } /* just ignore it */ /* Terminate the sample name */ *sample_name++ = 0; /* Look up the event name to get the event number */ for (event_number = SOUND_MAX - 1; event_number >= 0; event_number--) if (!strcmp(buffer, angband_sound_name[event_number])) break; /* No match -> just ignore the line */ if (event_number < 0) { if (show_sound_alloc) fprintf(dbo, "* Ignoring unknown event '%s'\n", buffer); continue; } /* Find the = */ while (*sample_name && *sample_name != '=') sample_name++; /* Bad line? */ if (*sample_name == 0) { continue; } /* just ignore it */ /* Skip the '=' */ sample_name++; /* | Now we find all the sample names and add them to the | appropriate list in the sample mapping array */ while (*sample_name) { char *s; SampNode *sn; /* Find the start of the next word */ while (isspace((unsigned char)*sample_name) && *sample_name) sample_name++; /* End of line? */ if (!*sample_name) { break; } /* Find the end of the sample name */ s = sample_name; /* start of the name */ while (!isspace((unsigned char)*sample_name) && *sample_name) sample_name++; /* Hack: shorten sample names that are too long */ if ((sample_name - s) > max_sampname_len) s[max_sampname_len] = ' '; /* Allocate a node in the sample list for the event */ if ((sn = malloc(sizeof(SampNode))) == NULL) core("Out of memory (scanning sound.cfg)"); /* Link the node to the list */ sn->next = sample[event_number].samplist; sample[event_number].samplist = sn; /* Imcrement the sample count for that event */ sample[event_number].samples++; /* | Copy the sample name into the node, converting it into | RISC OS style as we go. */ for (i = 0; !isspace((unsigned char)s[i]) && s[i]; i++) { if (s[i] == '.') sn->sample_name[i] = '/'; else if (s[i] == '/') sn->sample_name[i] = '.'; else sn->sample_name[i] = s[i]; } /* | The sample name '*' is special and means "no new sound" | so don't store a filename for these mappings. */ if (i == 1 && sn->sample_name[0] == '*') { i = 0; } sn->sample_name[i] = 0; } } /* Close the file */ fclose(f); if (show_sound_alloc) { int i; SampNode *l; for (i = 0; i < SOUND_MAX; i++) { fprintf(dbo, "\n\nEvent '%s'", angband_sound_name[i]); fprintf(dbo, " (%d sounds)\n", sample[i].samples); for (l = sample[i].samplist; l; l = l->next) fprintf(dbo, "\t%s\n", l->sample_name); } fclose(dbo); } } /* | Try to make sure that PlayIt is loaded. | This requires AngSound rel. 4 */ static void check_playit(void) { if (SWI(2, 0, SWI_OS_Module, 18, "PlayIt")) { int t; SWI(2, 1, SWI_OS_File, 17, "Angsound:LoadPlayIt", &t); if (t == 1) SWI(1, 0, SWI_OS_CLI, "RMEnsure PlayIt 0.00 Run AngSound:LoadPlayIt"); } } static void initialise_sound(void) { /* Load the configuration file */ Hourglass_On(); read_sound_config(); check_playit(); Hourglass_Off(); } static void play_sample(char *leafname) { char buffer[260]; strcpy(buffer, "%playit_stop"); if (!SWI(1, 0, SWI_OS_CLI, buffer)) { SWI(1, 0, SWI_PlayIt_Volume, sound_volume); sprintf(buffer, "%%playit_play %sSound:%s", RISCOS_VARIANT, leafname); SWI(1, 0, SWI_OS_CLI, buffer); } return; } static void play_sound(int event) { /* Paranoia */ if (!sound_initd) { return; } /* Paranoia */ if (event < 0 || event >= SOUND_MAX) return; /* msg_format("Sound '%s'",angband_sound_name[event]); */ /* Choose a sample */ if (sample[event].samples) { int s = rand() % sample[event].samples; SampNode *sn = sample[event].samplist; while (s--) { sn = sn->next; if (!sn) { plog("Adny botched the sound config - please send him a copy of your sound/cfg file."); } } if (*(sn->sample_name)) play_sample(sn->sample_name); } } /*--------------------------------------------------------------------------*/ /* | Let the user change the alarm message */ static void do_alarm_message_input(int y) { int k; int inspos = strlen(alarm_message); char old_message[52]; strcpy(old_message, alarm_message); do { put_fstr(26, y, CLR_YELLOW "%-51s", alarm_message); Term_gotoxy(26 + inspos, y); k = inkey(); switch (k) { case 21: /* ^U */ *alarm_message = 0; inspos = 0; break; case 128: case 8: /* delete */ if (inspos > 0) { alarm_message[--inspos] = 0; } break; case 27: /* escape */ strcpy(alarm_message, old_message); k = 13; break; default: if (k > 31 && k < 127 && inspos < 50) { alarm_message[inspos++] = k; alarm_message[inspos] = 0; } } } while (k != 13); put_fstr(26, y, CLR_WHITE "%-51s", alarm_message); } #define tum_col(X) ((X) ? CLR_L_BLUE : CLR_WHITE ) #define tum_onoff(X) ((X) ? "On " : "Off") static errr Term_user_acn(int n) { int cursor_state; int optn = 0; int k, adj; int redraw_mung = 0; int max_opt = 11; int alarm_modified = 0; /* Will be true if the alarm choices need to be (re)saved */ /* | Hack: let the desktop front end know that | the user menu is active... */ user_menu_active = TRUE; /* | This is thanks to Norcroft CC... it seems to want | to set up FP regs nice and early and then relies | on them remaining constant over the function call. | The trouble is that we implicitly call Wimp_Poll | whilst waiting for a key press... */ event_mask.data.keepfpregisters = 1; /* | Hack: alarm type 1 /looks/ the same as type 3 but doesn't get | cancelled as a type 3 would. This allows alarms to go off and | be cancelled without affecting the alarm type whilst it's being | set up here. */ if (alarm_type == 3) { alarm_type = 1; } /* | Store the screen */ Term_activate(&(data[0].t)); Term_save(); Term_get_cursor(&cursor_state); Term_set_cursor(TRUE); do { redraw_mung = 0; Term_clear(); put_fstr(2, 1, CLR_YELLOW "%s %s", VARIANT, VERSION); put_fstr(2, 2, CLR_SLATE "Front-end %s", PORTVERSION); put_fstr(2, 4, CLR_WHITE "Use cursor up/down to select an option then cursor left/right to alter it."); put_fstr(2, 5, CLR_WHITE "Hit 'S' to save these settings (alarm settings are saved automatically)."); put_fstr(2, 6, CLR_WHITE "Hit ESC to return to the game."); for (k = 0; k < 32; k++) { Term_putch(31 + k + (k / 2), 8, k / 2, '#'); } do { /* Gamma value never goes about 9.00, 5 char array should be fine */ char gammas[5]; sprintf(gammas, "%.2lf", gamma); put_fstr(2, 8, "%s Gamma correction : %s", tum_col(optn == 0), gammas); put_fstr(2, 9, "%s Force monochrome : %s", tum_col(optn == 1), tum_onoff(force_mono)); put_fstr(2, 10, "%s Sound effects : %s", tum_col(optn == 2), tum_onoff(enable_sound)); put_fstr(2, 11, "%s Sound effect volume : ", tum_col(optn == 3)); put_fstr(26, 11, "%s%-3d", sound_volume > 127 ? CLR_RED : tum_col(optn == 3), sound_volume); put_fstr(30, 11, "%s(127 = full volume)", tum_col(optn == 3)); put_fstr(2, 12, "%s Start fullscreen : %s", tum_col(optn == 4), tum_onoff(start_fullscreen)); put_fstr(30, 12, "%s(also selects fullscreen/desktop now)", tum_col(optn == 4)); put_fstr(2, 13, "%s Use hourglass : %s", tum_col(optn == 5), tum_onoff(use_glass)); put_fstr(2, 14, "%s'Hard' input flushing : %s", tum_col(optn == 6), tum_onoff(hack_flush)); put_fstr(7, 16, "%s Alarm type : %-20s", tum_col(optn == 7), alarm_types[alarm_type]); put_fstr(7, 17, CLR_WHITE " Time : "); put_fstr(26, 17, "%s%02d", tum_col(optn == 8), alarm_h); put_fstr(28, 17, CLR_WHITE, ":"); put_fstr(29, 17, "%s%02d", tum_col(optn == 9), alarm_m); put_fstr(7, 18, "%s Message : %-51s", tum_col(optn == 10), alarm_message); put_fstr(7, 19, "%s Beep : %s", tum_col(optn == 11), tum_onoff(alarm_beep)); #ifdef FE_DEBUG_INFO put_fstr(2, 23, "%sShow debug info", tum_col(optn == 23)); max_opt = 12; #endif switch (optn) { case 12: Term_gotoxy(2, 23); break; case 11: Term_gotoxy(26, 19); break; case 10: Term_gotoxy(26, 18); break; case 9: Term_gotoxy(29, 17); break; case 8: Term_gotoxy(26, 17); break; case 7: Term_gotoxy(26, 16); break; default: Term_gotoxy(26, optn + 8); } k = inkey(); adj = (k == '4' || k == 'h') ? -1 : (k == '6' || k == 'l') ? 1 : 0; switch (k) { case 18: /* Hack: force the screen to update */ redraw_mung = 1; k = 27; break; case 's': case 'S': save_choices(); put_fstr(2, 23, CLR_YELLOW "Options saved. "); Term_fresh(); Term_xtra(TERM_XTRA_DELAY, 750); Term_erase(2, 23, 60); break; case '8': case 'k': if (--optn < 0) { optn = max_opt; } break; case '2': case 'j': if (++optn > max_opt) { optn = 0; } break; case 13: case 32: case 't': /* Allow return, space and t to toggle some options */ case '4': case 'h': case '6': case 'l': { switch (optn) { case 0: /* Gamma correction */ gamma += adj * 0.05; if (gamma > 9.00) { gamma = 9.00; } if (gamma < 0.05) { gamma = 0.05; } Term_xtra(TERM_XTRA_REACT, 0); #ifndef FULLSCREEN_ONLY set_gamma_window_state(); #endif /* FULLSCREEN_ONLY */ /* flush(); */ Term_fresh(); break; case 1: /* Force monochrome */ force_mono = !force_mono; if (fullscreen_font) redraw_areaFS(0, 0, 80, 24); else Term_xtra(TERM_XTRA_REACT, 0); /* flush(); */ Term_fresh(); break; case 2: /* Sound enable / disable */ enable_sound = !enable_sound; #ifndef FULLSCREEN_ONLY set_sound_window_state(); #endif /* FULLSCREEN_ONLY */ if (enable_sound) { initialise_sound(); } break; case 3: /* Sound volume */ sound_volume += adj; if (sound_volume < SOUND_VOL_MIN) sound_volume = SOUND_VOL_MIN; if (sound_volume > SOUND_VOL_MAX) sound_volume = SOUND_VOL_MAX; #ifndef FULLSCREEN_ONLY set_sound_window_state(); #endif /* FULLSCREEN_ONLY */ break; case 4: /* Start fullscreen */ start_fullscreen = !start_fullscreen; if (start_fullscreen) enter_fullscreen_mode(); else if (!minimise_memory) leave_fullscreen_mode(); break; case 5: /* Start fullscreen */ use_glass = !use_glass; if (!use_glass) { if (glass_on) { Hourglass_Off(); } glass_on = 0; } break; case 6: /* hack flush */ hack_flush = !hack_flush; break; case 7: /* Alarm on/off */ alarm_type += adj; if (adj) { alarm_modified = 1; } if (alarm_type > 2) { alarm_type = 0; } if (alarm_type < 0) { alarm_type = 2; } if (!alarm_type && alarm_disp) { ack_alarm(); } /* XXXXX Cancel an already active alarm? */ break; case 8: /* Alarm hours */ alarm_h += adj; if (adj) { alarm_modified = 1; } if (alarm_h < 0) { alarm_h += 24; } if (alarm_h > 23) { alarm_h -= 24; } if (alarm_disp) { ack_alarm(); } break; case 9: /* Alarm minutes */ alarm_m += adj; if (adj) { alarm_modified = 1; } if (alarm_m < 0) { alarm_m += 60; } if (alarm_m > 59) { alarm_m -= 60; } if (alarm_disp) { ack_alarm(); } break; case 10: alarm_modified = 1; do_alarm_message_input(18); break; case 11: alarm_modified = 1; alarm_beep = !alarm_beep; break; case 12: show_debug_info(); redraw_mung = 1; k = 27; break; } } } } while (k != 27); } while (redraw_mung); /* Rehack the alarm type: */ if (alarm_type == 1) { alarm_type = 3; } if (alarm_modified) { write_alarm_choices(); } Term_set_cursor(cursor_state); /* Restore the screen */ Term_load(); /* Don't need to preserve FP regs any more */ event_mask.data.keepfpregisters = 0; /* | Hack: tell the desktop front end that we're done. */ user_menu_active = FALSE; return 0; } /*--------------------------------------------------------------------------*/ #ifdef USE_FILECACHE /* | 'Random' File-cacheing for *band. | | Rewritten since as of Zang 225 the mechanism for handling | these files has changed dramatically and the old system | is no longer viable. | | These new functions basically provide an alternative to the | normal my_fopen() (or fopen()) and my_fgets() functions. | | To use the file caching it is therefore necessary to alter | files.c to call cached_fopen(), cached_fclose() and cached_fgets() | rather than the normal functions. | | Note that these funtions will only work for files that are intended | to be read as a series of \n terminated lines of ASCII text using my_fgets(). | */ /* | Hack: use the game's dynamic area if possible: */ #define fc_malloc(X) (g_malloc(X)) #define fc_free(X) (g_free(X,0)) #ifndef ABBR_FILECACHE /* | Make these to do nothing. They'll never | be called anyway. Having them present makes | for neater code later on (ie. we use a variable | rather than the pre-processor to decide whether | to do compression). */ static int compress_string(char *os, char *s) { core("main-acn internal logic error 001"); return 0; } static int decompress_string(char *d, char *s, int max_len) { core("main-acn internal logic error 002"); return 0; } static int compressed_length(char *s) { core("main-acn internal logic error 003"); return 0; } #else /* | When caching files we try to use some abbreviations. | We use both whole words and pairs of letters. | NB: For this to work, the file must contain only | 7 bit characters. */ static char *abbrv_w[] = { /* These words all begin with a space */ " of ", " the ", " you ", " to ", " a ", " says", " is ", " that ", " and ", " your ", " are ", " it ", " be ", " for ", " me", " will ", " in ", " not ", " this ", " have ", " can ", " on ", " my ", " with ", " say ", " all", " by ", " get ", " but ", " just ", " die", " as ", " time ", " if ", " like ", /* These words do not */ "I ", "The ", "You ", "They ", "It ", "don", 0 }; /* Number of words */ #define FC_ABBRV_NUMWORDS 41 /* Number of them that don't start with a space */ #define FC_ABBRV_NONSPC 6 /* | NB: No letter pair may start with \0. */ static char abbrv_lp[] = "e ttht s heiner aoure'\0, anonf sd y r ongofator.\0" "n arllstha wes m ieaisen bl yndtoo yometele d f hve" "ayuralitneelN: chig ilroassaseliti lraa otedbede 'ri" "..u nntno!'ee\0\0"; /* | Compress the given string using the abbreviation tables above. | Returns compressed length *including* terminator (it may | be part of an abbreviation, you see...) | Note that we can compress the string in-place, ie. 'os' may be | the same as 's'. */ static int compress_string(char *os, char *s) { char *o, *f, *d; int i; o = os; while (*s) { int fw, lw; if (*s == ' ') { fw = 0; lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC; } else { fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC; lw = FC_ABBRV_NUMWORDS; } for (i = fw; i < lw; i++) { d = abbrv_w[i]; /* Word to check against */ for (f = s; *f && *f == *d; f++, d++) ; if (*d == 0) /* Match? */ { s = *f ? f : f - 1; /* Update string pointer */ *o++ = 128 + i; /* store code */ break; /* Quit looking for words */ } } /* Do we need to check the letter pairs? */ if (i == lw) { for (i = 0; abbrv_lp[i]; i += 2) { if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1]) { *o++ = 128 + FC_ABBRV_NUMWORDS + i / 2; /* NB: If the next character is the terminator then we're done. */ if (!s[1]) { return (o - os); } s += 2; /* Quit looking for letters */ break; } } /* NB: This next check is only safe because no letter pair starts with a NULL */ if (!abbrv_lp[i]) *o++ = *s++; } } /* Don't forget that terminator! */ *o++ = 0; return o - os; } /* | As compress_string (above), but stores nothing and | only returns the length of the compressed string. */ static int compressed_length(char *s) { char *f, *d; int i, l; l = 0; while (*s) { int fw, lw; if (*s == ' ') { fw = 0; lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC; } else { fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC; lw = FC_ABBRV_NUMWORDS; } for (i = fw; i < lw; i++) { d = abbrv_w[i]; for (f = s; *f && *f == *d; f++, d++) ; if (*d == 0) /* Match? */ { s = *f ? f : f - 1; /* Update string pointer */ l++; /* increment output length */ break; /* Quit looking for words */ } } /* Do we need to check the letter pairs? */ if (i == lw) { for (i = 0; abbrv_lp[i]; i += 2) { if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1]) { l++; /* increment output length */ /* NB: If the next character is the terminator then we're done. */ if (!s[1]) { return l; } s += 2; /* Quit looking for letters */ break; } } /* NB: This next check is only safe because no letter pair starts with a NULL */ if (!abbrv_lp[i]) { l++; s++; } } } /* Don't forget that terminator! */ return l + 1; } /* | Decompress the given string 's' into the buffer at 'd'. | At most, max_len characters (incl. \0 terminator) will be | written into d. | Returns the length of 's'. */ static int decompress_string(char *d, char *s, int max_len) { char *os = s; while (max_len > 1) { int nc = *s++; /* Get next character */ if (nc < 128) /* Is it a plain character? */ { if (0 == (*d++ = nc)) { break; } max_len--; } else /* Abbreviation to expand. */ { if (nc >= FC_ABBRV_NUMWORDS + 128) /* Letter pair? */ { *d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2]; if (0 == (*d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2 + 1])) break; max_len -= 2; } else /* It's a word */ { char *ws = abbrv_w[nc - 128]; while (*ws && max_len > 1) { *d++ = *ws++; max_len--; } } } } /* Skip over the rest of the abbreviated string if we ran out of space */ if (max_len <= 1) /* Out of space? */ { int nc; *d = 0; /* Terminate */ do { nc = *s++; /* Next char */ if (nc >= 128 + FC_ABBRV_NUMWORDS) /* Ignore words */ nc = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128) * 2) + 1]; /* Only check 2nd letter of pair */ } while (nc); } return s - os; /* Length of abbreviated string */ } #endif /* ABBR_FILECACHE */ /* Each entry in the cache looks like this: */ typedef struct fce_ { char *name; /* canonical pathname of file */ char *text; /* text (be it compressed or not) */ char *eof; /* byte beyond the last byte of text */ int used; /* access counter when the file was last used */ int compressed; /* compression method, (ie. 0 for none or 1 for abbreviations) */ } FileCacheEntry; /* | The handles we chuck around are pointers to one of these structs. | Note that since we actually return (and take) |FILE*|s we just | compare the value of a |FILE*| with the limits of the array of | |CachedFileHandle|s to decide whether its 'ours' or a genuine | (ie. stdio) file handle. This /is/ pretty lame, I know... */ typedef struct cfh_ { char *ptr; /* sequential file pointer, as it were */ FileCacheEntry *fce; /* ->the file-cache entry data */ } CachedFileHandle; #define MAX_OPEN_CACHED_FILES 16 /* We allow up to 16 of these files open at once */ #define MAX_CACHE_ENTRIES 64 /* We allow up to 64 cache entries */ static FileCacheEntry *file_cache; /* to be used as file_cache[MAX_CACHE_ENTRIES] */ static CachedFileHandle *cached_file_handle; /* to be used as cached_file_handle[MAX_OPEN_CACHED_FILES] */ static int file_cache_initd = 0; /* Is the cache initialised? */ static int file_cache_size = 0; /* Total size of the cached files */ static int fc_access_counter = 1; /* incremented on each cache access */ /* | Pre-calculate max. possible value of a FILE* (ie. address in memory) | that could be a valid |CachedFileHandle*|. */ static FILE *max_cfh_addr; /* == (FILE*) (&(cached_file_handle[MAX_OPEN_CACHED_FILES-1])) */ /* | Initialise the file cache */ static void init_file_cache(void) { int i; /* Allocate storage */ file_cache = fc_malloc(MAX_CACHE_ENTRIES * sizeof(FileCacheEntry)); cached_file_handle = fc_malloc(MAX_OPEN_CACHED_FILES * sizeof(CachedFileHandle)); if (!file_cache || !cached_file_handle) { /* Disable file-caching */ if (file_cache) { fc_free(file_cache); } if (cached_file_handle) { fc_free(cached_file_handle); } use_filecache = 0; } else { /* Initialise the cache */ for (i = 0; i < MAX_CACHE_ENTRIES; i++) file_cache[i].name = NULL; for (i = 0; i < MAX_OPEN_CACHED_FILES; i++) cached_file_handle[i].fce = NULL; fc_access_counter = 1; file_cache_size = 0; max_cfh_addr = (FILE *)(&(cached_file_handle[MAX_OPEN_CACHED_FILES - 1])); } file_cache_initd = 1; } /* | Helper: take a copy of the string and return a pointer to it */ static char *string_cpy(char *s) { char *d = fc_malloc(strlen(s) + 1L); if (d) { strcpy(d, s); } return d; } /* | Cache the specified file, returning either the cache entry | that it has been cached at, or NULL for failure. | | Note that for the abbreviated file cache a temporary file | is used to allow the compression to be applied just once. | (otherwise it has to be done twice - once to determine the | eventual compressed size and once to actually store and compress | it). */ static FileCacheEntry *cache_file(char *name) { int i, size = 0; FILE *fp; char buffer[1024]; char *d; FILE *tf = NULL; /* Used if abbr_filecache and abbr_tmpfile are set */ char cfn[1024]; /* Used if abbr_filecache and abbr_tmpfile are set */ /* Find the first free slot in the cache */ for (i = 0; i < MAX_CACHE_ENTRIES; i++) if (!file_cache[i].name) break; /* No more entries? */ if (i >= MAX_CACHE_ENTRIES) { return NULL; } /* Set up the info on the file */ if ((file_cache[i].name = string_cpy(name)) == NULL) { return NULL; } /* Open the file */ fp = my_fopen(name, "r"); if (!fp) { fc_free(file_cache[i].name); file_cache[i].name = 0; return NULL; } /* Open/create tempfile if need be: */ if (abbr_filecache && abbr_tmpfile) { /* Hack: Form the pathname of the cached compressed file (in canonical form) */ sprintf(cfn, "%s%s", scrap_path, riscosify_name(name + strlen(resource_path))); /* Ensure that that particular directory exists... */ ensure_path(cfn); /* Check whether cache file is out of date */ if (file_is_newer(riscosify_name(name), cfn)) { tf = fopen(cfn, "wb"); size = 0; } else { tf = fopen(cfn, "rb"); if (tf) { size = myFile_Size(cfn); } } } /* If we don't have the cached file (but want it), compress the source text to it */ if (tf) { if (!size) { int k; while (!my_fgets(fp, buffer, sizeof(buffer))) { if (smart_filecache && (!*buffer || *buffer == '#')) continue; k = compress_string(buffer, buffer); if (fwrite(buffer, 1, k, tf) != k) { fclose(tf); remove(cfn); core("error writing tempfile"); } size += k; } fclose(tf); tf = fopen(cfn, "rb"); } } else { /* Count the number of bytes */ while (!my_fgets(fp, buffer, sizeof(buffer))) { if (smart_filecache && (!*buffer || *buffer == '#')) continue; if (abbr_filecache) size += compressed_length(buffer); else size += strlen(buffer) + 1; } } /* Close the (source) file */ my_fclose(fp); /* Allocate enough storage for the text */ file_cache[i].text = fc_malloc(size + 1L); if (!file_cache[i].text) { fc_free(file_cache[i].name); file_cache[i].name = 0; if (tf) { fclose(tf); } return NULL; } /* Do we have a tempfile to load? */ if (tf) { if (fread(file_cache[i].text, 1, size, tf) != size) core("error reading tempfile"); fclose(tf); } else { /* Re-open the file... */ fp = my_fopen(name, "r"); if (!fp) { fc_free(file_cache[i].name); fc_free(file_cache[i].text); file_cache[i].name = 0; return NULL; } /* And read it into the buffer... */ d = file_cache[i].text; while (!my_fgets(fp, buffer, sizeof(buffer))) { if (smart_filecache && (!*buffer || *buffer == '#')) continue; if (abbr_filecache) d += compress_string(d, buffer); else { strcpy(d, buffer); d += strlen(buffer) + 1; } } if ((d - file_cache[i].text) != size) { debug("Calculated size is %d, pointer offset is %d", size, (d - file_cache[i].text)); core("Cached file is larger than calculated!"); } /* Close the file */ my_fclose(fp); } /* Set up the 'last accessed' value, etc. */ file_cache[i].used = fc_access_counter++; file_cache[i].eof = file_cache[i].text + size; file_cache[i].compressed = abbr_filecache; file_cache_size += size; /* Return success */ return &(file_cache[i]); } /* | Discard a file from the cache */ static void discard_cached_file(int i) { if (!file_cache[i].name) { return; } /* invalid request */ fc_free(file_cache[i].text); fc_free(file_cache[i].name); file_cache_size -= (file_cache[i].eof) - (file_cache[i].text); file_cache[i].name = 0; } /* | Attempt to flush as much of the cache as required | to bring it within the size limit. | If protect != 0 then that entry in the cache won't be flushed. */ static void flush_file_cache(FileCacheEntry * protect) { int i, j, done; int oldest_u, oldest_e; FileCacheEntry *fce; int needed = (4 << 10); /* Hack: try to free at least 4K */ done = (file_cache_size + needed) <= max_file_cache_size; while (!done) { oldest_u = fc_access_counter; oldest_e = -1; fce = file_cache; /* Find oldest entry that isn't in use */ for (i = 0; i < MAX_CACHE_ENTRIES; fce++, i++) { if (fce == protect) { continue; } /* Hack ;) */ if (fce->name) /* Is this cache slot full? */ { for (j = 0; j < MAX_OPEN_CACHED_FILES; j++) if (cached_file_handle[j].fce == fce) break; if (j < MAX_OPEN_CACHED_FILES) continue; /* Cached file is still open */ if (fce->used < oldest_u) { oldest_e = i; oldest_u = file_cache[i].used; } } } if (oldest_e < 0) done = 1; /* We can flush nothing more */ else { discard_cached_file(oldest_e); done = (file_cache_size + needed) <= max_file_cache_size; } } } /* | Locate the specified file within the cache. | Returns NULL if the file is not cached */ static FileCacheEntry *find_cached_file(char *name) { int i; FileCacheEntry *fce = file_cache; for (i = 0; i < MAX_CACHE_ENTRIES; i++, fce++) { if (fce->name) if (streq(fce->name, name)) return fce; } return NULL; } /*--------------------------------------------------------------------------*/ /* Externally visible file cache stuff */ /*--------------------------------------------------------------------------*/ /* | Open a file... | Returns the file cache handle of the file, or NULL for failure. | Note that if mode is anything other than "r" the call defers to | my_fopen(). | | NB: The returned handle is almost certainly *NOT* a |FILE*| | (although it may be if the cache cannot accomodate the file). | | Therefore, you *MUST* ensure that any file opened with cached_fopen() | is only ever accessed via cached_fgets() and cached_fclose(). | | Failure to do so will result in, ahem, unpleasantness. Extreme | unpleasantness. */ FILE *cached_fopen(char *name, char *mode) { FileCacheEntry *fcs = NULL; int fch; if (strcmp(mode, "r") || !use_filecache) return my_fopen(name, mode); if (!file_cache_initd) { init_file_cache(); } if (max_file_cache_size >= 0) { /* Find a free cache entry */ for (fch = 0; fch < MAX_OPEN_CACHED_FILES; fch++) if (!cached_file_handle[fch].fce) break; /* Out of handles? */ if (fch >= MAX_OPEN_CACHED_FILES) return my_fopen(name, mode); /* Is the file already cached? */ fcs = find_cached_file(name); if (!fcs) { /* File wasn't cached, so cache it */ flush_file_cache(NULL); /* Clean stuff out of the cache if need be */ fcs = cache_file(name); /* Cache the new file */ flush_file_cache(fcs); /* Flush, but keep the latest file */ } } /* Did we fail to cache the file? */ if (!fcs) { return my_fopen(name, mode); } /* File was cached OK */ cached_file_handle[fch].ptr = fcs->text; /* Init sequential pointer */ cached_file_handle[fch].fce = fcs; /* Cache block pointer */ fcs->used = fc_access_counter++; /* Opening the file counts as an access */ return (FILE *)(&cached_file_handle[fch]); } /* | Close a file */ errr cached_fclose(FILE *fch_) { CachedFileHandle *fch; /* Is the FILE* genuine? */ if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr)) return my_fclose(fch_); fch = (CachedFileHandle *) fch_; /* Check for "Ooopses": */ if (fch->fce == NULL) core("cached_fclose called on a non-open file handle"); flush_file_cache(NULL); /* Clean out the cache if need be */ fch->fce = NULL; /* Mark file handle as inactive */ return 0; } /* | Do the my_fgets thing on a file */ errr cached_fgets(FILE *fch_, char *buffer, int max_len) { CachedFileHandle *fch; char *eof; char *ptr; /* Is the FILE* genuine? */ if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr)) return my_fgets(fch_, buffer, max_len); fch = (CachedFileHandle *) fch_; /* Check for "Oopses": */ if (!file_cache_initd) core("cached_fgets() on uninitialised file-cache"); if (!fch->fce) core("cached_fgets called for a un-open file"); eof = fch->fce->eof; ptr = fch->ptr; /* Out of bounds? */ if (ptr >= eof) { return 1; } /* Read failed */ /* | Read the next line, up to \0 (which would have v=been \n in the original file), | or max_len-1 characters */ if (fch->fce->compressed) ptr += decompress_string(buffer, ptr, max_len); else { if (eof - ptr < max_len) { max_len = eof - ptr; } for (; max_len >= 1; max_len--) if ((*buffer++ = *ptr++) == 0) break; *buffer = 0; /* terminate (paranoia) */ } /* Update sequential pointer */ fch->ptr = ptr; return 0; } #endif /* USE_FILECACHE */ /* | This section deals with checking that the .raw files are up to date | wrt to the .txt files. Note that this function won't work for RISC OS 2 | (due to the lack of OS_Args 7) so it simply returns 0 to indicate that | the file isn't OOD. | | For this to work, the equivalent function (in init2.c) needs to be | #if-d out (and this function should be declared). You'll probably | also need to zap the UNIX #includes at the top of the file */ extern errr check_modification_date(int fd, cptr template_file) { char raw_buf[1024]; char txt_buf[1024]; int i; os_error *e; if (os_version() < 300) { return 0; } /* Use OS_Args 7 to find out the pathname 'fd' refers to */ e = SWI(6, 0, SWI_OS_Args, /* In: */ 7, /* Get path from filehandle */ fd, /* file handle */ raw_buf, /* buffer */ 0, 0, /* unused */ 1024 /* size of buffer */ /* No output regs used */ ); if (e) { core(e->errmess); } /* Build the path to the template_file */ path_make(txt_buf, ANGBAND_DIR_EDIT, template_file); i = file_is_newer(riscosify_name(txt_buf), raw_buf); return i; } /*--------------------------------------------------------------------------*/ /* | This is the hideous IClear hack. Basically what we do is to take a copy | of the module and kill it in the RMA. Then, on return to the desktop | we reinstate the module. | | NB: This is truly, truly evil. */ static int *iclear_module = NULL; /* | Start the IClear hack */ static void iclear_hack(void) { os_error *e; int code = 0; int *i; /* Get base address of module */ e = SWI(2, 4, SWI_OS_Module, 18, "IClear", 0, 0, 0, &code); if (e || !code) return; /* Module size is at code!-4 */ i = (int *)code; iclear_module = malloc(i[-1] + 4); if (!iclear_module) return; /* Copy the module */ *iclear_module = i[-1]; memcpy(iclear_module + 1, (void *)code, i[-1]); /* Kill the current version */ e = SWI(2, 0, SWI_OS_Module, 4, "IClear"); if (e) { free(iclear_module); iclear_module = NULL; } } /* | Remove the IClear hack */ static void remove_iclear_hack(void) { os_error *e; if (!iclear_module) return; e = SWI(3, 0, SWI_OS_Module, 11, iclear_module + 1, *iclear_module); if (e) debug("Failed to reinstall IClear: %s", e->errmess); free(iclear_module); iclear_module = NULL; } /*--------------------------------------------------------------------------*/ /* Alarm functions */ static int alarm_ackd = 0; /* has the alarm been acknowledged? */ static window_handle aw = 0; /* alarm window */ /* | Is the alarm due to go off, ie. is it enabled, and if so | does the current time match the alarm time? */ static void check_alarm() { time_t t; struct tm *lt; alarm_lastcheck = Time_Monotonic(); time(&t); lt = localtime(&t); if (lt->tm_hour == alarm_h && lt->tm_min == alarm_m) { if (!alarm_ackd) alarm_disp = 1; } else { alarm_ackd = 0; } /* Hack: if the alarm has already been acknowledged then don't re-trigger it */ if (alarm_ackd) { alarm_disp = 0; } /* Hack: if the alarm should make a noise, then make one: */ if (alarm_disp && alarm_beep == 1) { static unsigned int last_beep = 0; unsigned int t = Time_Monotonic(); if (t > last_beep + 100) { Sound_SysBeep(); last_beep = t; } } /* | If we're in the desktop then fire the alarm off if need be. | If we aren't then do nothing - the fullscreen bored() function | will take care of the alarm. */ #ifndef FULLSCREEN_ONLY if (!fullscreen_font && alarm_disp) trigger_alarm_desktop(); #endif /* FULLSCREEN_ONLY */ } static void ack_alarm(void) { if (aw) { Window_Delete(aw); aw = 0; } alarm_ackd = 1; alarm_disp = 0; if (alarm_type == 3) { /* One shot alarm */ alarm_type = 0; write_alarm_choices(); } } #ifndef FULLSCREEN_ONLY /* | Click in the (desktop) alarm window */ static BOOL Hnd_AlarmClick(event_pollblock * pb, void *ref) { ack_alarm(); return TRUE; } /* | The alarm has gone off in the desktop */ static void trigger_alarm_desktop(void) { char buffer[120]; if (aw) return; aw = Window_Create("alarm", template_TITLEMIN); if (!aw) { core("failed to create Alarm window!"); } sprintf(buffer, "Alarm from %s", VARIANT); Window_SetTitle(aw, buffer); Event_Claim(event_CLICK, aw, 0, Hnd_AlarmClick, NULL); Event_Claim(event_CLOSE, aw, event_ANY, Hnd_AlarmClick, NULL); Icon_printf(aw, 1, "An alarm was set for %02d:%02d", alarm_h, alarm_m); Icon_SetText(aw, 2, alarm_message); Window_Show(aw, open_CENTERED); } #endif /* FULLSCREEN_ONLY */ /*--------------------------------------------------------------------------*/ #ifndef FE_DEBUG_INFO static void show_debug_info(void) { core("main-acn internal logic error 004"); } #else static int debug_cx = 0; static int debug_cy = 0; static int debug_cl = TERM_WHITE; static int debug_sl = 0; static void debug_cls(void) { Term_clear(); debug_cx = debug_cy = debug_sl = 0; } static void debug_tcol(int c) { debug_cl = c; } static void debug_scroll(void) { char **c = ((term_data *)Term)->t.scr->c; /* char array [24][80] */ byte **a = ((term_data *)Term)->t.scr->a; /* attr array [24][80] */ int cc; char tmp[82]; int y, x, p; cc = a[1][0]; for (y = 1; y < 23; y++) { Term_gotoxy(0, y - 1); for (x = p = 0; x < 80; x++) { Term_addch(a[y][x], c[y][x]); } } Term_erase(0, 22, 80); } static void debug_print_line(char *l) { char *le; int cr = 0; /* Handle scrolling */ if (debug_cy > 22) { debug_cy = 22; if (--debug_sl < 0) { int k; put_fstr(0, 23, CLR_YELLOW "[RET one line, SPC one page]"); do { k = inkey(); } while (k != 32 && k != 13); Term_erase(0, 23, 79); debug_sl = k == 32 ? 21 : 0; } debug_scroll(); } /* Hack: check for NL */ for (le = l; *le; le++) if (*le == '\n') { cr = 1; break; } /* display text */ put_fstr(debug_cx, debug_cy, "$%c%.*s", 'A' + debug_cl, l); /* move cursor */ if (!cr) { debug_cx += (le - l); if (debug_cx >= 80) { cr = 1; } } if (cr) { debug_cx = 0; debug_cy += 1; } Term_gotoxy(debug_cx, debug_cy); } static int debug_next_line(char *lb, char **t, int cx) { int i = 0; char *lt = *t; if (!*lt) { return -1; } /* Out of text */ while (*lt && cx < 80) { lb[i] = *lt++; if (lb[i] == '\n') /* New line */ { cx = 0; /* Cursor x will be 0 after displaying */ i++; /* Keep the \n in the output */ break; /* All done */ } else if (lb[i] == '\t') /* Tab */ { while (cx < 80) { lb[i++] = ' '; cx++; if ((cx & 7) == 0) { break; } } } else /* Anything else */ { cx++; i++; } } lb[i] = 0; /* terminate line buffer */ *t = lt; /* update text pointer */ return cx; /* return cursor x after printing */ } static void debug_printf(char *fmt, ...) { char buffer[1024]; char line[82]; va_list ap; char *p = buffer; va_start(ap, fmt); vsprintf(buffer, fmt, ap); va_end(ap); /* Now split the string into display lines */ while (debug_next_line(line, &p, debug_cx) >= 0) debug_print_line(line); } static void debug_version_info(void) { debug_tcol(TERM_YELLOW); debug_printf("\n\nMisc. Info:\n"); debug_tcol(TERM_WHITE); debug_printf("\tVariant name = \"%s\"\n", VARIANT); debug_printf("\tFront-end version: %s\n", PORTVERSION); debug_printf("\tFront-end compiled: %s %s\n", __TIME__, __DATE__); debug_printf("\tCompile time flags:\n"); #ifdef USE_FILECACHE debug_printf("\t\tUSE_FILECACHE\n"); #endif #ifdef ABBR_FILECACHE debug_printf("\t\tABBR_FILECACHE\n"); #endif #ifdef SMART_FILECACHE debug_printf("\t\tSMART_FILECACHE\n"); #endif debug_tcol(TERM_YELLOW); debug_printf("\nResource path:\n"); debug_tcol(TERM_WHITE); debug_printf("\t\"%s\"\n", resource_path); debug_tcol(TERM_YELLOW); debug_printf("\nTempfile path:\n"); debug_tcol(TERM_WHITE); debug_printf("\t\"%s\"\n", scrap_path); debug_printf("\tScrapfiles are %s deleted at exit.\n", (flush_scrap ? "" : "NOT")); debug_tcol(TERM_YELLOW); debug_printf("\nChoices files:\n"); debug_tcol(TERM_L_BLUE); debug_printf("\tDesired files:\n"); debug_tcol(TERM_WHITE); debug_printf("\tPrimary (r/w): \"%s\"\n", choices_file[CHFILE_WRITE]); debug_printf("\t Fallback (r): \"%s\"\n", choices_file[CHFILE_READ]); debug_printf("\t Mirror (r/w): \"%s\"\n", choices_file[CHFILE_MIRROR]); debug_tcol(TERM_L_BLUE); debug_printf("\tActual files:\n"); debug_tcol(TERM_WHITE); debug_printf("\t Write: \"%s\"\n", find_choices(TRUE)); debug_printf("\t Read: \"%s\"\n", find_choices(FALSE)); debug_tcol(TERM_YELLOW); debug_printf("\nAlarm files:\n"); debug_tcol(TERM_L_BLUE); debug_printf("\tDesired files:\n"); debug_tcol(TERM_WHITE); debug_printf("\tPrimary (r/w): \"%s\"\n", alarm_file[CHFILE_WRITE]); debug_printf("\t Fallback (r): \"%s\"\n", alarm_file[CHFILE_READ]); debug_tcol(TERM_L_BLUE); debug_printf("\tActual files:\n"); debug_tcol(TERM_WHITE); debug_printf("\t Write: \"%s\"\n", find_alarmfile(TRUE)); debug_printf("\t Read: \"%s\"\n", find_alarmfile(FALSE)); #ifdef USE_DA debug_tcol(TERM_YELLOW); debug_printf("\nDynamic areas:\n"); debug_tcol(TERM_WHITE); debug_printf("\tFontcache DA = %d\t", font_area); debug_printf("size = %d\theap size = %d\n", font_area_size, font_heap_size); debug_printf("\t ralloc DA = %d\t", game_area); debug_printf("size = %d\theap size = %d\n", game_area_size, game_heap_size); #endif } static void debug_filecache_info(void) { #ifndef USE_FILECACHE debug_tcol(TERM_L_DARK); debug_printf("File cache disabled at compile time.\n"); #else int j, k; int t, cs, ucs, cf; cf = cs = ucs = j = k = 0; /* To stop an usused warning if USE_FILECACHE is undefined */ t = strlen(resource_path); if (!file_cache_initd) { init_file_cache(); } /* Paranoia */ debug_tcol(TERM_YELLOW); debug_printf("\nFilecache contents:\n"); debug_tcol(TERM_L_BLUE); debug_printf("Flags: Smart=%d; Abbrv=%d; Slave=%d; Enable=%d\n", smart_filecache, abbr_filecache, abbr_tmpfile, use_filecache); debug_tcol(TERM_SLATE); if (smart_filecache || abbr_filecache) debug_printf("\t\t%3s %6s/%-6s %6s %6s Path (relative to lib/)\n", "Hnd", "Cache", "Disc", "Time", "Status"); else debug_printf("\t\t%3s %6s %6s %6s Path (relative to lib/)\n", "Hnd", "Size", "Time", "Status"); for (j = 0; j < MAX_CACHE_ENTRIES; j++) { FileCacheEntry *fce = &(file_cache[j]); if (fce->name) { cf++; debug_tcol(TERM_L_GREEN); debug_printf("\t\t%3d ", j); debug_tcol(TERM_L_UMBER); if (!smart_filecache && !abbr_filecache) debug_printf("%6d ", fce->eof - fce->text); else { debug_printf("%6d/", fce->eof - fce->text); k = myFile_Size(riscosify_name(fce->name)); debug_printf("%-6d ", k); if (k > 0) { ucs += k; } } cs += fce->eof - fce->text; debug_printf("%6d ", fce->used); for (k = 0; k < MAX_OPEN_CACHED_FILES; k++) if (cached_file_handle[k].fce == fce) break; debug_tcol(TERM_RED); debug_printf("%-6s ", k < MAX_OPEN_CACHED_FILES ? "Open" : ""); debug_tcol(TERM_L_UMBER); debug_printf("%s\n", fce->name + t); } } debug_tcol(TERM_L_BLUE); debug_printf("\tTotal:\t%3d ", cf); if (ucs) debug_printf("%6d/%-6d\n", cs, ucs); else debug_printf("%6d\n", cs); debug_tcol(TERM_BLUE); #endif /* USE_FILECACHE */ } static void show_debug_info(void) { int k; /* blank the term */ debug_cls(); /* Repeatedly prompt for a command */ do { debug_tcol(TERM_VIOLET); debug_printf("\nInfo: (V)ersion, (F)ilecache, ESC=exit "); do { k = inkey(); switch (k) { case 'v': case 'V': debug_version_info(); break; case 'f': case 'F': debug_filecache_info (); break; case 27: break; default: k = 0; } } while (!k); } while (k != 27); Term_clear(); } #endif /* FE_DEBUG_INFO */ #endif /* __riscos */ zangband/src/main-sla.c0000755000000000000000000001735410250356274014015 0ustar rootroot/* File: main-sla.c */ /* Purpose: Actual Unix "slang" support for Angband */ /* * Author: hans@grumbeer.pfalz.de (Hans-Joachim Baader) * * Most of this code is adapted directly from "main-gcu.c" */ #include "angband.h" #ifdef USE_SLA cptr help_sla[] = { "To use SLA (SLANG)", NULL }; #include /* * Are we "active"? */ static int slang_on = FALSE; /* * Can we use "color"? */ static bool can_use_color = FALSE; /* * Angband to SLang color conversion table */ static int colortable[16]; /* * Currently, only a single "term" is supported here */ static term term_screen_body; /* * Hack -- see below */ void init_pair (int index, char *foreground, char *background) { SLtt_set_color (index, "", foreground, background); } #define A_NORMAL 0 #define A_BOLD 8 #define A_REVERSE 0 #define REVERSE 8 #define COLOR_BLACK "black" #define COLOR_BLUE "blue" #define COLOR_GREEN "green" #define COLOR_CYAN "cyan" #define COLOR_RED "red" #define COLOR_MAGENTA "magenta" #define COLOR_YELLOW "brown" #define COLOR_WHITE "lightgray" #define COLOR_BBLACK "gray" #define COLOR_BBLUE "brightblue" #define COLOR_BGREEN "brightgreen" #define COLOR_BCYAN "brightcyan" #define COLOR_BRED "brightred" #define COLOR_BMAGENTA "brightmagenta" #define COLOR_BYELLOW "yellow" #define COLOR_BWHITE "white" static char *color_terminals [] = { #ifdef linux "console", #endif "linux", "xterm-color", "color-xterm", "xtermc", "ansi", 0 }; /* * Stolen from the Midnight Commander */ int has_colors(void) { int i; char *terminal; /* Access the terminal type */ terminal = getenv("TERM"); /* Check for colors */ SLtt_Use_Ansi_Colors = 0; if (NULL != getenv ("COLORTERM")) { SLtt_Use_Ansi_Colors = 1; } /* We want to allow overriding */ for (i = 0; color_terminals [i]; i++) { if (streq(color_terminals [i], terminal)) { SLtt_Use_Ansi_Colors = 1; } } /* Setup emulated colors */ if (SLtt_Use_Ansi_Colors) { /*init_pair (REVERSE, "black", "white");*/ } /* Setup bizarre colors */ else { SLtt_set_mono (A_BOLD, NULL, SLTT_BOLD_MASK); SLtt_set_mono (A_REVERSE, NULL, SLTT_REV_MASK); SLtt_set_mono (A_BOLD|A_REVERSE, NULL, SLTT_BOLD_MASK | SLTT_REV_MASK); } return SLtt_Use_Ansi_Colors; } /* * Nuke SLang */ static void Term_nuke_sla(term *t) { if (!slang_on) return; /* Show the cursor */ /* curs_set(1); */ /* Clear the screen */ (void)SLsmg_cls(); /* Refresh */ SLsmg_refresh(); /* We are now off */ slang_on = FALSE; /* Shut down */ SLsmg_reset_smg(); SLang_reset_tty(); } /* * Init SLang */ static void Term_init_sla(term *t) { /* Note that we are on */ slang_on = TRUE; } /* * Process an event, wait if requested */ static errr Term_xtra_sla_event(int v) { /* Do not wait unless requested */ if (!v && (SLang_input_pending(0) == 0)) return (1); /* Get and enqueue the key */ Term_keypress(SLang_getkey()); /* Success */ return 0; } /* * Suspend / Resume */ static errr Term_xtra_sla_alive(int v) { /* Suspend */ if (!v) { /* Oops */ if (!slang_on) return (1); /* We are now off */ slang_on = FALSE; /* Shut down (temporarily) */ SLsmg_reset_smg(); SLang_reset_tty(); } /* Resume */ else { /* Oops */ if (slang_on) return (1); /* Fix the screen */ SLsmg_refresh(); /* Note that we are on */ slang_on = TRUE; } /* Success */ return (0); } /* * Handle a "special request" */ static errr Term_xtra_sla(int n, int v) { /* Analyze the request */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: (void)SLsmg_write_char('\007'); return (0); /* Flush the ncurses buffer */ case TERM_XTRA_FRESH: (void)SLsmg_refresh(); return (0); /* Make the cursor invisible or visible */ case TERM_XTRA_SHAPE: /* curs_set(v); */ return (0); /* Handle events */ case TERM_XTRA_EVENT: return (Term_xtra_sla_event(v)); /* Handle events */ case TERM_XTRA_FLUSH: while (!Term_xtra_sla_event(FALSE)); return (0); /* Suspend/Resume */ case TERM_XTRA_ALIVE: return (Term_xtra_sla_alive(v)); /* Delay */ case TERM_XTRA_DELAY: if (v > 0) usleep(1000 * v); return (0); } /* Oops */ return (1); } /* * Actually MOVE the hardware cursor */ static errr Term_curs_sla(int x, int y, int z) { /* Literally move the cursor */ SLsmg_gotorc(y, x); /* Success */ return 0; } /* * Erase some characters */ static errr Term_wipe_sla(int x, int y, int n) { int i; /* Place the cursor */ SLsmg_gotorc(y, x); /* Dump spaces */ for (i = 0; i < n; i++) SLsmg_write_char(' '); /* Success */ return 0; } /* * Place some text on the screen using an attribute */ static errr Term_text_sla(int x, int y, int n, byte a, cptr s) { /* Move the cursor */ SLsmg_gotorc(y, x); /* Set the color */ if (can_use_color) SLsmg_set_color(colortable[a&0x0F]); /* Dump the string */ SLsmg_write_nchars(s, n); /* Success */ return 0; } /* * Prepare "SLang" for use by the file "term.c" * Installs the "hook" functions defined above */ errr init_sla(void) { int i, err; term *t = &term_screen_body; /* Initialize, check for errors */ err = (SLang_init_tty(-1, TRUE, 0) == -1); /* Quit on error */ if (err) quit("SLang initialization failed"); /* Get terminal info */ SLtt_get_terminfo(); /* Initialize some more */ if (SLsmg_init_smg() == 0) { quit("Could not get virtual display memory"); } /* Check we have enough screen. */ err = ((SLtt_Screen_Rows < 24) || (SLtt_Screen_Cols < 80)); /* Quit with message */ if (err) quit("SLang screen must be at least 80x24"); /* Now let's go for a little bit of color! */ err = !has_colors(); /* Do we have color available? */ can_use_color = !err; /* Init the Color-pairs and set up a translation table */ /* If the terminal has enough colors */ /* Color-pair 0 is *always* WHITE on BLACK */ /* XXX XXX XXX See "main-gcu.c" for proper method */ /* Only do this on color machines */ if (can_use_color) { /* Prepare the color pairs */ init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_YELLOW, COLOR_BLACK); init_pair(4, COLOR_BLUE, COLOR_BLACK); init_pair(5, COLOR_MAGENTA, COLOR_BLACK); init_pair(6, COLOR_CYAN, COLOR_BLACK); init_pair(7, COLOR_BLACK, COLOR_BLACK); init_pair(9, COLOR_BRED, COLOR_BLACK); init_pair(10, COLOR_BGREEN, COLOR_BLACK); init_pair(11, COLOR_BYELLOW, COLOR_BLACK); init_pair(12, COLOR_BBLUE, COLOR_BLACK); init_pair(13, COLOR_BMAGENTA, COLOR_BLACK); init_pair(14, COLOR_BCYAN, COLOR_BLACK); init_pair(15, COLOR_BBLACK, COLOR_BLACK); /* Prepare the color table */ colortable[0] = 7; /* Black */ colortable[1] = 0; /* White */ colortable[2] = 6; /* Grey XXX */ colortable[3] = 11; /* Orange XXX */ colortable[4] = 1; /* Red */ colortable[5] = 2; /* Green */ colortable[6] = 4; /* Blue */ colortable[7] = 3; /* Brown */ colortable[8] = 15; /* Dark-grey XXX */ colortable[9] = 14; /* Light-grey XXX */ colortable[10] = 5; /* Purple */ colortable[11] = 11; /* Yellow */ colortable[12] = 9; /* Light Red */ colortable[13] = 10; /* Light Green */ colortable[14] = 12; /* Light Blue */ colortable[15] = 3; /* Light Brown XXX */ } /* Initialize the term */ term_init(t, 80, 24, 64); /* Stick in some hooks */ t->nuke_hook = Term_nuke_sla; t->init_hook = Term_init_sla; /* Stick in some more hooks */ t->xtra_hook = Term_xtra_sla; t->curs_hook = Term_curs_sla; t->wipe_hook = Term_wipe_sla; t->text_hook = Term_text_sla; /* Save the term */ term_screen = t; /* Activate it */ Term_activate(t); /* Success */ return 0; } #endif /* USE_SLA */ zangband/src/main-tnb.c0000644000000000000000000001341510250356274014010 0ustar rootroot/* File: main-tnb.c */ /* Purpose: program entry point */ /* * Copyright (c) 1997-2001 Tim Baker * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #ifdef USE_TNB #include "tk/tnb.h" #include "maid-grf.h" cptr help_tnb[] = { "To use tk/tcl port", NULL }; static term data; bool game_in_progress = FALSE; char ANGBAND_DIR_TK[1024]; Tcl_Interp *g_interp; /* Graphics information */ int tnb_tile_x; int tnb_tile_y; char tnb_tile_file[1024]; /* Font information */ char tnb_font_file[1024]; int tnb_font_size; void tnb_get_term(int x, int y, byte *a, char *c, byte *ta, char *tc) { /* Get the term information */ *a = data.scr->a[y][x]; *c = data.scr->c[y][x]; *ta = data.scr->ta[y][x]; *tc = data.scr->tc[y][x]; } static errr Term_user_tnb(int n) { /* Hack - ignore parameters for now */ (void) n; return (0); } /* * Process at least one event */ static errr Term_xtra_tnb_event(int v) { int flags; /* Wait for an event */ if (v) { /* Block */ flags = TCL_ALL_EVENTS; /* Check for an event */ } else { /* Check */ flags = TCL_ALL_EVENTS | TCL_DONT_WAIT; } (void) Tcl_DoOneEvent(flags); /* Success */ return 0; } /* * Process all pending events */ static errr Term_xtra_tnb_flush(void) { int flags = TCL_ALL_EVENTS | TCL_DONT_WAIT; while (Tcl_DoOneEvent(flags)) ; /* Success */ return (0); } /* * Hack -- make a noise */ static errr Term_xtra_tnb_noise(void) { #ifdef PLATFORM_WIN MessageBeep(MB_ICONASTERISK); #endif /* PLATFORM_WIN */ return (0); } /* * Hack -- make a sound */ static errr Term_xtra_tnb_sound(int v) { /* Hack - ignore parameters for now */ (void) v; return (0); } /* * Delay for "x" milliseconds */ static int Term_xtra_tnb_delay(int v) { if (v <= 0) return (0); #ifdef PLATFORM_WIN /* Sleep */ Sleep(v); #endif /* PLATFORM_WIN */ #ifdef PLATFORM_X11 usleep(1000 * v); #endif /* PLATFORM_X11 */ /* Success */ return (0); } /* * Do a "special thing" */ static errr Term_xtra_tnb(int n, int v) { /* Handle a subset of the legal requests */ switch (n) { /* Make a bell sound */ case TERM_XTRA_NOISE: { return (Term_xtra_tnb_noise()); } /* Make a special sound */ case TERM_XTRA_SOUND: { return (Term_xtra_tnb_sound(v)); } /* Process random events */ case TERM_XTRA_BORED: { return (Term_xtra_tnb_event(0)); } /* Process an event */ case TERM_XTRA_EVENT: { return (Term_xtra_tnb_event(v)); } /* Flush all events */ case TERM_XTRA_FLUSH: { return (Term_xtra_tnb_flush()); } /* React to global changes */ case TERM_XTRA_REACT: { return (Term_xtra_tnb_react()); } /* Delay for some milliseconds */ case TERM_XTRA_DELAY: { return (Term_xtra_tnb_delay(v)); } /* Flush the output XXX XXX */ case TERM_XTRA_FRESH: { int flags = TCL_WINDOW_EVENTS | TCL_IDLE_EVENTS | TCL_DONT_WAIT; while (Tcl_DoOneEvent(flags) != 0) ; return (0); } } /* Oops */ return 1; } static void term_data_link(term *t) { /* Initialize the term */ term_init(t, 80, 24, 1024); /* Use a "software" cursor */ t->soft_cursor = TRUE; /* Use "Term_pict" for "graphic" data */ t->higher_pict = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Prepare the template hooks */ t->user_hook = Term_user_tnb; t->xtra_hook = Term_xtra_tnb; t->curs_hook = Term_curs_tnb; t->wipe_hook = Term_wipe_tnb; t->text_hook = Term_text_tnb; t->pict_hook = Term_pict_tnb; /* Remember where we came from */ t->data = NULL; } static void init_windows(void) { int i; term *t = &data; /* Main window */ term_data_link(t); angband_term[0] = t; /* No extra Term's required */ for (i = 1; i < 8; i++) { angband_term[i] = NULL; } Term_activate(t); } /* * Display error message and quit (see "z-util.c") */ static void hook_quit(cptr str) { (void) str; Icon_Exit(); /* cleanup_angband(); */ /* Cleanup Tcl and Tk */ Tcl_DeleteInterp(g_interp); /* Hack - no longer hook tcl memory routines */ rnfree_aux = NULL; } /* * Memory allocation wrappers */ static vptr my_tcl_free(vptr data) { Tcl_Free((char *) data); return (NULL); } static vptr my_tcl_alloc(huge size) { return (Tcl_Alloc(size)); } #ifdef PLATFORM_X11 /* * Init the tk port */ int init_tnb(int argc, cptr *argv) { /* Hack -ignore parameter */ (void) argc; /* Save the "tk" directory */ path_make(ANGBAND_DIR_TK, ANGBAND_DIR_SCRIPT, "tk"); /* Use graphics */ pick_graphics(GRAPHICS_ADAM_BOLT, &tnb_tile_x, &tnb_tile_y, tnb_tile_file); /* Try the "16x16.bmp" file */ path_make(tnb_font_file, ANGBAND_DIR_XTRA, "font/16x16.txt"); /* Use the "16x16.bmp" file if it exists */ if (fd_close(fd_open(tnb_font_file, O_RDONLY))) { quit("Could not initialise font metrics!"); } /* Always 16x16 for now */ tnb_font_size = 16; /* Prepare the windows */ init_windows(); /* Activate hooks */ quit_aux = hook_quit; core_aux = hook_quit; /* Hack - TclTk_Init doesn't fail gracefully, so check manually for X11 */ if (!XOpenDisplay("")) return (1); /* Initialize Tcl and Tk. */ g_interp = TclTk_Init(argv); /* Paranoia */ if (!g_interp) return(1); /* Set the memory-allocation hooks */ rnfree_aux = my_tcl_free; ralloc_aux = my_tcl_alloc; /* Initialize */ angtk_init(); /* Catch nasty signals */ signals_init(); /* Initialize */ init_angband(); /* Program is intialized */ angtk_angband_initialized(); /* Init colours */ Term_xtra_tnb_react(); #if 0 while (TRUE) { while (Tcl_DoOneEvent(TRUE) != 0) ; } #endif /* 0 */ /* Press a key for the player */ Term_keypress(' '); /* Paranoia */ return (0); } #endif /* PLATFORM_X11 */ #endif /* USE_TNB */ zangband/src/main-vcs.c0000644000000000000000000003056110250356274014021 0ustar rootroot/* File: main-vcs.c */ /* * copyright 2001 Alexander Malmberg */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * Here is the post to rec.games.roguelike.angband that linked to this * file. -SF- */ /* * * * Hi all, * * I've written (mostly for my own use) a /dev/vcsa* based display driver * for Linux and thought it could be nice to announce it here. The * main-vcs.c file is available at * http://w1.423.telia.com/~u42303319/main-vcs.c and a patch that adds it * to main.c and changes the .prf files to use the gcu versions for this * display driver is at http://w1.423.telia.com/~u42303319/vcsa.patch . To * get it to compile, you'll have to add main-vcs.o to the object list in * your makefile, and add '-D"USE_VCS"' to CFLAGS. Note that the patch adds * vcs last in the list, so you'll probably need to run 'angband -mvcs' to * use it. * * Features: * - It's really fast, and there's no flickering. * * - Palette changing works properly. * * - Easily customizable windows through command line options. The syntax * is "x0,y0,x1,y1", eg. "angband -mvcs -- 0,0,79,23 0,25,79,49" for an * 80x50 console. By default, a 0,0,79,23 window is added (and a bunch of * other windows if your console is 132x60). Make sure the first window is * at least 80x24 large, since it will be the main window. By default, a * frame (well, sortof) will be added around the windows (I like it that * way). If you don't want the frame, add --noframe somewhere on the * command line. * * - The escape key works without delay. * * Drawbacks: * - Only works on a local console. OTOH, if it can't initialize, it'll * signal failure and main.c should fall back to some other display driver. * * - Needs read/write access to the /dev/vcsa* file for your console. My * login handles this automatically, YMMV. * * - It seems that if you switch away from the console running Angband * while it's updating the screen part of the update might end up on the * new console. Shouldn't be a problem in practice. * * Comments, suggestions, etc. are welcome. * * - Alexander Malmberg * */ /* * This module uses /dev/vcsa* to write characters and attributes * directly to console memory. /dev/vcsa*'s first 4 bytes are the * number of lines and columns on the screen, and X and Y position * of the cursor with (0,0) as top left. Then follows lines*columns * pairs of (char, attr) with the contents of the screen. */ #include "angband.h" #ifdef USE_VCS cptr help_vcs[] = { "To use /dev/vcsa*", "x0,y0,x1,y1 Create new term", "--noframe No window frames", NULL }; /* Change the palette. If this is un-define'd, the standard palette will be used, and it'll try to map colors to an equivalent color. */ #define SET_COLORS #include #include #define VCSA_CURSOR 2 /* Seek offset of cursor position */ #define VCSA_SCREEN 4 /* Seek offset of screen contents */ static int fd_vcsa; static unsigned char *screen,*row_clear; static byte s_width,s_height; static byte cursor[2]; typedef struct term_data { term t; int x0,y0,x1,y1,sx,sy; unsigned char *base; } term_data; #define MAX_VCS_TERM 1 static term_data data[MAX_VCS_TERM]; static int num_term; /* This is (basically) the same color mapping as in main-gcu */ static unsigned char attr_for_color[16]= {0x00,0x07,0x02,0x03,0x04,0x05,0x06,0x01, 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }; #define COLOR_BLANK 0x07 /* attr_for_color[TERM_WHITE] */ /* For some reason, the kernel moves the color indeces around, so we need to reverse that here. */ static int reverse_linux_color_table[16]= /*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ { 0, 7, 2, 6, 1, 5, 3, 4, 8,12,10,14, 9,13,11,15}; static void set_colors(void) { int i; for (i=0;i<16;i++) { printf("\033]P%c%02x%02x%02x", hexsym[reverse_linux_color_table[i]], angband_color_table[i][1], angband_color_table[i][2], angband_color_table[i][3]); } fflush(stdout); } static struct termios norm_termios; static struct termios game_termios; static void reset_terminal(void) { /* Turn off non-blocking on stdin */ fcntl(0,F_SETFL,(~O_NONBLOCK)&fcntl(0,F_GETFL)); /* Reset terminal parameters */ tcsetattr(0, TCSAFLUSH, &norm_termios); /* Reset the palette */ printf("\033]R"); fflush(stdout); } static void setup_terminal(void) { /* Make stdin non-blocking */ fcntl(0,F_SETFL,O_NONBLOCK|fcntl(0,F_GETFL)); /* Set terminal parameters */ tcsetattr(0, TCSAFLUSH, &game_termios); /* Set up palette */ set_colors(); } /* * Reset terminal and move to last line */ static void leave_vcs(void) { int i; unsigned char *c; /* Blank out line in memory */ for (i = 0, c = screen; i < s_width; i++) { *c++ = ' '; *c++ = COLOR_BLANK; } /* Write blanked-out line to last line in console (vcsa) file */ lseek(fd_vcsa, VCSA_SCREEN + 2 * s_width * (s_height - 1), SEEK_SET); write(fd_vcsa, screen, s_width * 2); /* Set cursor position to last line and write it to console */ lseek(fd_vcsa, VCSA_CURSOR, SEEK_SET); cursor[0] = 0; cursor[1] = s_height - 1; write(fd_vcsa, cursor, sizeof(cursor)); /* Reset terminal */ reset_terminal(); } /* * Handle a "special request" */ static errr Term_xtra_vcs(int n, int v) { switch (n) { case TERM_XTRA_EVENT: { int lch; unsigned char ch; fd_set s; if (v) { /* Wait for input */ FD_ZERO(&s); FD_SET(0,&s); select(1,&s,NULL,NULL,NULL); } lch=-1; /* Loop while reading a character */ while (read(0,&ch,1)==1) { /* Consume previous character, if any */ if (lch!=-1) Term_keypress(lch); lch=ch; } /* Consume last character */ if (lch!=-1) { /* A bit of a hack. If we get a lone escape (no pending input after it) we assume it's the escape key and not part of an escape sequence and change it to a `. This shouldn't cause any problems as long as we're running locally, and if we're using the /dev/vcsa* devices, we are. */ if (lch== ESCAPE) { Term_keypress('`'); } else { Term_keypress(lch); } } return (0); } case TERM_XTRA_FLUSH: { /* Flush input */ unsigned char ch; while (read(0,&ch,1)==1) ; return (0); } case TERM_XTRA_FRESH: { /* Write screen memory to console (vcsa) file */ lseek(fd_vcsa,VCSA_SCREEN,SEEK_SET); write(fd_vcsa,screen,2 * s_width * s_height); return (0); } case TERM_XTRA_NOISE: { /* Bell */ write(1,"\007",1); return (0); } case TERM_XTRA_ALIVE: { if (!v) leave_vcs(); else setup_terminal(); return (0); } case TERM_XTRA_DELAY: { /* Delay for some milliseconds */ if (v > 0) usleep(v*1000); return (0); } case TERM_XTRA_REACT: { /* Set up colours */ set_colors(); return 0; } case TERM_XTRA_SHAPE: { printf("\033[?25%c",v?'h':'l'); fflush(stdout); return 0; } } /* Unknown or Unhandled action */ return (1); } /* * Actually move the hardware cursor */ static errr Term_curs_vcs(int x, int y) { term_data *td = (term_data*)(Term->data); /* Set the cursor position */ cursor[0] = x + td->x0; cursor[1] = y + td->y0; /* Write cursor position to console (vcsa) file */ lseek(fd_vcsa,VCSA_CURSOR,SEEK_SET); write(fd_vcsa,cursor,sizeof(cursor)); return 0; } /* * Erase a grid of space */ static errr Term_wipe_vcs(int x, int y, int n) { term_data *td = (term_data*)(Term->data); unsigned char *c; c = &td->base[2 * (s_width * y + x)]; /* Blank out n characters in memory */ for (; n; n--) { *c++ = ' '; *c++ = COLOR_BLANK; } return 0; } /* * Place some text on the screen using an attribute */ static errr Term_text_vcs(int x, int y, int n, byte a, const char *cp) { term_data *td = (term_data*)(Term->data); unsigned char *c,col; col=attr_for_color[a&0xf]; c=&td->base[2*(s_width*y+x)]; /* Copy n characters to screen memory */ for (;n;n--) { *c++=*cp++; *c++=col; } return 0; } static int active; /* * Init the /dev/vcsa* system */ static void Term_init_vcs(term *t) { /* Ignore t */ (void) t; if (active++) return; setup_terminal(); } /* * Nuke the /dev/vcsa* system */ static void Term_nuke_vcs(term *t) { /* Ignore t */ (void) t; if (--active) return; leave_vcs(); close(fd_vcsa); } /* * Set up a new console terminal */ static void term_data_link(int x0,int y0,int x1,int y1) { term_data *td = &data[num_term]; term *t = &td->t; if (x0<0 || y0<0 || x0>=s_width || y0>=s_height || x1<=x0 || y1<=y0 || x1>=s_width || y1>=s_height || num_term==MAX_VCS_TERM) { fprintf(stderr,"ignoring invalid window (%i %i)-(%i %i) num %i/%i\n", x0,y0,x1,y1,num_term,MAX_VCS_TERM); return; } td->x0=x0; td->y0=y0; td->x1=x1; td->y1=y1; td->sx=x1-x0+1; td->sy=y1-y0+1; td->base=&screen[2*(x0+y0*s_width)]; /* Initialize the term */ term_init(t, td->sx, td->sy, num_term?0:256); t->soft_cursor = FALSE; t->icky_corner = FALSE; t->always_text = TRUE; t->never_bored = TRUE; t->never_frosh = TRUE; t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Prepare the init/nuke hooks */ t->init_hook = Term_init_vcs; t->nuke_hook = Term_nuke_vcs; /* Prepare the template hooks */ t->xtra_hook = Term_xtra_vcs; t->curs_hook = Term_curs_vcs; t->wipe_hook = Term_wipe_vcs; t->text_hook = Term_text_vcs; /* Remember where we came from */ t->data = (vptr)(td); /* Activate it */ if (!num_term) Term_activate(t); /* Global pointer */ angband_term[num_term] = t; num_term++; } /* * Prepare /dev/vcsa* for use by the terms package */ errr init_vcs(int argc,char **argv) { int i; char *c; byte *cc; int frame=1,add_std_win=1; /* Find and open console (vcsa) file */ char buf[256]; c=ttyname(0); if (!c || sscanf(c,"/dev/tty%i",&i)!=1) { fprintf(stderr,"can't find my tty\n"); return 1; } strnfmt(buf, 256, "/dev/vcsa%i",i); fd_vcsa=open(buf,O_RDWR); if (fd_vcsa==-1) { perror(buf); return 1; } /* Read screen lines and columns from console (vcsa) file */ s_width=s_height=0; read(fd_vcsa,&s_height,1); read(fd_vcsa,&s_width,1); /* we allocate an extra row that's always cleared so we can use memcpy to clear other parts of the screen */ screen=malloc((s_height+1)*s_width*2); if (!screen) { fprintf(stderr,"vcsa: out of memory\n"); close(fd_vcsa); return 1; } /* clear screen and last row */ for (cc=screen,i=0;i1) { int i; for (i=1;i | 80x24 | 52x24 | <- equipment | | | 24 +---------+-+-------+ 24 object -> | 65x13 | | recall 38 +---------+ 66x24 | <- inventory | | | monster -> | 65x21 +---------+ 49 recall | | 66x10 | <- messages 60 +---------+---------+ 60 65 */ term_data_link( 0,25, 64,37); term_data_link( 81, 0,131,23); term_data_link( 66,25,131,48); term_data_link( 0,39, 64,59); term_data_link( 66,50,131,59); } } if (!num_term) { fprintf(stderr,"no windows created!\n"); return 1; } if (frame) { int y; for (cc=screen,i=0;i #include #include /* Some prototypes and definitions */ #define FIELD_SY 1 #define FIELD_SX 80 #define FIELD_EY 2 #define FIELD_EX 10 void GetAddr(int, int, char *); void InitConsole(void); void TerminateConsole(void); void ResetScrBuf(void); void AddScrBuf(char *, int); char InKey(void); char InKeyBuf(void); void ScreenClear(void); void ResetDISP(void); int kbhit(void); void ShowLine(int y, int x, int len); void LoadProfile(void); /* Changed lines marker. */ char DISP[26]; /* Max rows & columns number in screen (note that in older version ** than 2.7.9 we needed 25 lines, though 24th was unused. So it is set ** to 25 although screen actually has only 24 lines. (we just used to ** transfer 25th line into 24th. ** But in new version we no longer use it. */ int rows, cols; /* Game cursor position on screen */ int curx=1; int cury=1; /* * Virtual Screen */ byte VirtualScreen[2048]; byte ScreenAttr[2048]; /* * This array is used for "wiping" the screen, initialized in "init_wat()" */ byte wiper[256]; /* * The main screen */ static term term_screen_body; /* Update line on screen. ** Actually just set flag that we should update it. */ void ScreenUpdateLine(int line) { DISP[line+1]=1; } /* * Clear the whole screen */ void ScreenClear(void) { int iy; for (iy = 0; iy < rows; iy++) { memcpy(VirtualScreen + (iy*cols), wiper, cols); memset(ScreenAttr + (iy*cols), 0xF1, cols); ScreenUpdateLine(iy); } } /* * Nuke a Term */ static void Term_nuke_vm(term *t) { TerminateConsole(); puts("Console has been terminated(nuke)."); } /* * Move the cursor */ static errr Term_curs_vm(int x, int y) { /* Hack: mark line cursor was at as changed to ensure that old ** cursor would be removed. */ DISP[cury]=1; curx=x+1; cury=y+1; return (0); } /* * The Angband color table * * Comment: White has been changed to blue to make screen look better. * Green and Light Green are exchanged for the same reason. * -0x80 means changing to bold font. * * Color Table: * Dark to Blue, White to Blue, Slate to Blue, Orange to Pink, * Red to Red, Green to GreenB, Blue to BlueB, Umber to Yellow, * Gray to Blue, Light White to WhiteB, Violet to PinkB, Yellow to Yellow * Light colors: Red to RedB,Green to Green,Blue to Cyan, Umber to YellB */ static const byte vm_color[] = { 0xF1, 0xF1, 0xF1, 0xF3, 0xF2, 0xF4-0x80, 0xF1-0x80, 0xF6, 0xF1, 0xF7-0x80, 0xF3-0x80, 0xF6, 0xF2-0x80, 0xF4, 0xF5, 0xF6-0x80 }; /* * Place some text on the screen using an attribute */ static errr Term_text_vm(int x, int y, int n, byte a, cptr s) { register int i; register byte attr; register byte *dest; register byte *dest2; /* Attribute... */ attr = vm_color[a&0x0F]; /* Paranoia */ if (n > cols - x) n = cols - x; /* Access the destination */ dest = VirtualScreen + ((cols * y) + x); dest2 = ScreenAttr + ((cols * y) + x); /* Virtually write the string */ for (i = 0; (i < n) && s[i]; i++) { *dest++ = s[i]; *dest2++ = attr; } /* Dump the line */ ScreenUpdateLine(y); /* Success */ return (0); } /* * Erase part of line. */ static errr Term_wipe_vm(int x, int y, int w) { /* Paranoia -- Verify the dimensions */ if (cols < (w+x)) w = (cols-x); /* Wipe part of the virtual screen, and update */ memcpy(VirtualScreen + (y*cols + x), wiper, w); memset(ScreenAttr + (y*cols + x), 0xF1, w); ScreenUpdateLine(y); /* Success */ return (0); } /* * Handle special request. */ static errr Term_xtra_vm(int n, int v) { int i, j; char tmp; /* Analyze the request */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: /* No noises here! :) */ return (0); /* Wait for a single event */ case TERM_XTRA_EVENT: /* No wait key press check */ if (!v && !kbhit()) return (1); /* Wait for a keypress */ i = getkey(); /* Save keypress */ Term_keypress(i); /* Success */ return (0); #if 0 case TERM_XTRA_FROSH: ScreenUpdateLine(VirtualScreen + (cols*v), v); return (0); #endif case TERM_XTRA_FLUSH: /* Flush keys */ while (1) { tmp=getkeybuf(); if (!tmp) break; Term_keypress(tmp); } /* Success */ return (0); } /* Unknown request */ return (1); } /* * Initialize the VM/CNSconsole. */ errr init_vme(void) { register i; term *t = &term_screen_body; short blank = ' '; static int done = FALSE; /* Paranoia -- Already done */ if (done) return (-1); /* Build a "wiper line" of blank spaces */ for (i = 0; i < 256; i++) wiper[i] = blank; /* Acquire the size of the screen */ rows = 25; cols = 80; /* Initialize the console */ InitConsole(); /* Wipe the screen */ for (i = 0; i < rows; i++) { /* Wipe that row */ memcpy(VirtualScreen + (i*cols), wiper, cols); memset(ScreenAttr + (i*cols), 0xF1, cols); } /* Erase the screen */ ScreenClear(); /* Initialize the term -- very large key buffer */ term_init(t, cols, rows - 1, 1024); /* Prepare the init/nuke hooks */ t->nuke_hook = Term_nuke_vm; /* Connect the hooks */ t->text_hook = Term_text_vm; t->wipe_hook = Term_wipe_vm; t->curs_hook = Term_curs_vm; t->xtra_hook = Term_xtra_vm; /* Save it */ term_screen = t; /* Activate it */ Term_activate(term_screen); /* Done */ done = TRUE; /* Success */ return 0; } /* Wait for keypress */ int getkey(void) { return ((int)InKey()); } /* Read key buffer if not empty */ int getkeybuf(void) { return ((int)InKeyBuf()); } /*********************************************************************/ /*********************************************************************/ /****************** Actual work with console *********************/ /*********************************************************************/ /* Low-level functions */ int CNSINIT(char *path, int device); int CNSTERM(char *path); int CNSREAD(char *path, char *buffer, int buflen); int CNSWRITE(char *path, char *buffer, int buflen); #define _PF1 (0xF1) #define _PF2 (0xF2) #define _PF3 (0xF3) #define _PF4 (0xF4) #define _PF5 (0xF5) #define _PF6 (0xF6) #define _PF7 (0xF7) #define _PF8 (0xF8) #define _PF9 (0xF9) #define _PF10 (0x7A) #define _PF11 (0x7B) #define _PF12 (0x7C) #define _PF13 (0xC1) #define _PF14 (0xC2) #define _PF15 (0xC3) #define _PF16 (0xC4) #define _PF17 (0xC5) #define _PF18 (0xC6) #define _PF19 (0xC7) #define _PF20 (0xC8) #define _PF21 (0xC9) #define _PF22 (0x4A) #define _PF23 (0x4B) #define _PF24 (0x4C) #define _PA2 (0x6E) #define _PA3 (0x6B) #define _CLEAR (0x60) #define _ENTER (0x7D) extern char cnscrstb[]; /* Hardware 3270 cursor offsets */ /* Console identificator */ char *cons="console"; /* Console interrupt flag; should be set by assembler patch */ int CNSINTR; /* Assembler function prototype; this handles console interrupt */ extern int CNSHD(); /* Buffer for output stream (VM/ESA uses streams to control terminals */ static char * ScrBuf; /* Incoming buffer from console after cnsread */ static char * InBuf; /* User entered command buffer (paranoia: make it so long no one will ** ever run short of it :) */ static char ComBuf[256]; /* Pointer to current position in ComBuf */ static char * ComPtr; /* This array is used to clean up input field. ** Comment: erase everything user entered after we accepted it. */ static char wiping[]= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* Flag: should we wipe input field ? */ static int wipe=0; /* Counter: how many times did game checked if we pressed any key. ** Comment: after every 100th we update the screen. ** Comment2: because of slow work with screen it would be _very_ ** unwise to make it more often. */ static int kbhitcount; /* Keep length of collected output stream */ static int BufLen; /* Define array which will be used to bring actual cursor ** (not game cursor!!!) back to the beginning of input field. */ static char CursorHome[]= {0, 0, 0, 0x13}; /* Define array to make fields on the screen. ** Comment: 2 fields: input field(writable), and game field(protected). ** Comment2: We will change PSS settings into dumb repeating of color ** setting if PSS fonts are not abailable. */ static char ScrField[]= {0, 0, 0, 0x29, 0x03, 0xC0, 0xC1, 0x42, 0xF3, 0x43, 0xC1, \ 0, 0, 0, 0x29, 0x03, 0xC0, 0x60, 0x42, 0xF1, 0x43, 0xC1}; /* Last command user entered (for repeating purposes */ static char LastCmd[256]; /* Array which holds PFkeys definitions */ static char PFcmd[25][80]; /* Actually initialize console ... */ void InitConsole(void) { /* Use this to activate full screen mode after console has been ** initialized. */ unsigned char init[6] = {0xC2, 0x11, 0x40, 0x40, 0, 0}; int i; /* Array for checking is PSS fonts are loaded */ char pss[256]; /* Allocate memory */ ScrBuf=malloc(4100); if (ScrBuf==NULL) { puts("Cannot mallocate memory for screen buffer!"); exit(77); } /* Block system standarts... */ system("cp set msg off"); system("cp set emsg off"); system("cp set imsg off"); system("cp term brkkey none"); /* Test PSS */ system("desbuf"); system("query display (stack"); pss[0] = '\0'; fgets(pss, sizeof(pss), stdin); i=1; if (pss[63]!='P') i=0; if (pss[64]!='S') i=0; if (pss[65]!='S') i=0; if (pss[72]!='0') i=0; if (pss[73]!='1') i=0; if (pss[74]!='A') i=0; if (pss[75]!='B') i=0; if (i==0) { /* No PSS. Cannot run without them... */ puts("ERROR: Cannot run without PSS fonts!"); exit(77); } ScrBuf[0]=0xC2; ScrBuf[1]=0; BufLen=1; /* Allocate memory */ InBuf=malloc(200); if (InBuf==NULL) { puts("Cannot mallocate memory for screen buffer!"); exit(77); } /* Okay, lets make some intial assigments */ InBuf[0]=0; ComBuf[0]=0; ComPtr=ComBuf; LastCmd[0]=0; GetAddr(FIELD_SY, FIELD_SX, ScrField); GetAddr(FIELD_EY, FIELD_EX, ScrField+11); GetAddr(FIELD_SY, FIELD_SX+1, wiping); GetAddr(FIELD_SY, FIELD_SX+1, CursorHome); /* Initialize console */ cnsxinit(cons, 0x9, 1, CNSHD); /* Activate full-screen mode */ cnswrite(cons, init, 6); /* No lines on screen were changed */ for (i=1; i<25; i++) DISP[i]=0; /* Corsor home... */ AddScrBuf(CursorHome, sizeof(CursorHome)); /* No console interrupt yet */ CNSINTR=0; /* No check keypresses yet */ kbhitcount=0; /* Let's load 'profile angband' for PFs settings */ LoadProfile(); } void TerminateConsole(void) { free(ScrBuf); free(InBuf); /* Terminate console */ cnsterm(cons); /* Restore system defaults. ** Comment: better we should have written them down at start but ** it is reasonably difficult to make, so just set standart values. */ system("cp set msg on"); system("cp set emsg on"); system("cp set imsg on"); system("cp term brkkey pa1"); } /* Reset collected output stream */ void ResetScrBuf(void) { int i; unsigned char attr; /* Where shall cursor be ? */ GetAddr(cury, curx, ScrBuf+BufLen); BufLen+=3; /* Actually 'make' cursor : underline symbol */ ScrBuf[BufLen++]=0x28; ScrBuf[BufLen++]=0x41; ScrBuf[BufLen++]=0xF4; attr=*(ScreenAttr+(cury-1)*cols+curx-1); ScrBuf[BufLen++]=0x28; ScrBuf[BufLen++]=0x42; ScrBuf[BufLen++]=attr|0x80; ScrBuf[BufLen++]=0x28; ScrBuf[BufLen++]=0x43; if (attr>0x80) ScrBuf[BufLen++]=0xC1; else ScrBuf[BufLen++]=0xC2; ScrBuf[BufLen++]=*(VirtualScreen+(cury-1)*cols+curx-1); ScrBuf[BufLen++]=0x28; ScrBuf[BufLen++]=0x41; ScrBuf[BufLen++]=0x00; ScrBuf[BufLen++]=0x28; ScrBuf[BufLen++]=0x43; ScrBuf[BufLen++]=0xC1; /* Draw screen fields. ** Comment: paranoia, should always be there, but won't be any worse ** if we redraw them. */ memcpy(ScrBuf+BufLen, ScrField, 22); if (wipe) { /* If we should wipe input field, let's do it. */ wipe=0; memcpy(ScrBuf+BufLen+22, wiping, sizeof(wiping)); /* Actually write to terminal */ cnswrite(cons, ScrBuf, BufLen+22+sizeof(wiping)); } else { /* Actually write to terminal */ cnswrite(cons, ScrBuf, BufLen+22); } /* Discard old output stream */ ScrBuf[0]=0xC2; ScrBuf[1]=0; BufLen=1; } /* Just add some more to output stream */ void AddScrBuf(char * ptr, int len) { /* Stream cannot be longer than 4k, so reset it */ if (len+BufLen>4000) ResetScrBuf(); memcpy(ScrBuf+BufLen, ptr, len); BufLen+=len; } /* Calculate codes for cursor setting and write them down into *stream. ** Comment: system has _really crazy tables for this calculation so ** we need function for this. */ void GetAddr(int y, int x, char *stream) { int sx, sy, len; len = y * 80 + x - 81; sx = len & 0x3F; sy = len >> 6; *stream++ = 0x11; /* Use standart array for cursor offsets (VMSERV TXTLIB) */ *stream++ = cnscrstb[sy]; *stream = cnscrstb[sx]; } /* Okay, program requested pressed key, let's return it */ char InKey(void) { char ret; char * info; int i; char *ptr, *ptrs; int PF; int prev; /* If we didn't finish previous line user entered just return ** next symbol. ** Comment: unlikely IBM PC and others user may enter string ** of any length (limited by input field length to 9 ** symbols) and only them by pressing ENTER key or ** functional key make real console interrupt. ** So we should handle everything user entered. */ if (*ComPtr) { ret=*ComPtr; ComPtr++; return (ret); } /* If we get here than user command buffer is empty and we have ** actually wait for new command. */ /* Okay, update screen before :) */ ResetDISP(); /* Well, now wait till user enters something and read it. */ cnsread(cons, InBuf, 200); /* Oh, we already handling interrupt so, reset interrupt flag */ CNSINTR=0; /* No previous cmd inserted yet */ prev=0; /* User pressed CLEAR key. Redraw all screen then. */ if (*InBuf==0x6D) { /* Hack: mark all lines as changed */ for (i=1; i<26; i++) DISP[i]=1; /* Cursor home */ AddScrBuf(CursorHome, sizeof(CursorHome)); /* Redraw the whole screen */ ResetDISP(); /* Hack: we should return something, shouldn't we? ** Just return CR. It seems safest. */ return (13); } /* Okay parse stream from console to make string only of ** user's input. */ info=memchr(InBuf, 0x1D, 200); info+=2; info[9]=0; /* Mark that we should clear input field */ wipe=1; /* Cursor home */ AddScrBuf(CursorHome, sizeof(CursorHome)); /* Pointer to current letter in buffered command set to start */ ComPtr=ComBuf; /* Clear length buffer */ ComBuf[0]=0; switch (*InBuf) { /* PA2 & PA1 */ case 0x6E:; case 0x6C: /* These 2 keys are supposed to mean 'ESC' */ return (27); break; /* Hack: determine which PF key was exactly pressed. */ case _PF1: PF=1; break; case _PF2: PF=2; break; case _PF3: PF=3; break; case _PF4: PF=4; break; case _PF5: PF=5; break; case _PF6: PF=6; break; case _PF7: PF=7; break; case _PF8: PF=8; break; case _PF9: PF=9; break; case _PF10: PF=10; break; case _PF11: PF=11; break; case _PF12: PF=12; break; case _PF13: PF=13; break; case _PF14: PF=14; break; case _PF15: PF=15; break; case _PF16: PF=16; break; case _PF17: PF=17; break; case _PF18: PF=18; break; case _PF19: PF=19; break; case _PF20: PF=20; break; case _PF21: PF=21; break; case _PF22: PF=22; break; case _PF23: PF=23; break; case _PF24: PF=24; break; /* Uh, user pressed ENTER. ** Determine if we should add 'CR' at the end or not ? */ case _ENTER: PF=0; /* Copy entered string into buffer */ strcpy(ComBuf, info); /* Never end strings of length 1 or 0 with CR */ if (strlen(ComBuf)<2) break; /* If string ends with R* or R& add CR */ ptr=info+strlen(info)-1; if (*ptr=='*'||*ptr=='&') { ptr--; if (ptr=info) { if (*ptr=='R') break; if (*ptr=='@'&&ptr==info) break; if (*ptr=='/') { i=0; if (*(ptr-1)!='8') break; if (*(ptr-2)!='1') break; if ((ptr-2)!=info) break; i=1; break; } if (!isdigit((unsigned char)*ptr)) { i=0; break; } ptr--; } if (i) ptr=ComBuf+strlen(ComBuf); ptr[0]=13; ptr[1]=0; break; default: PF=0; } /* Okay let's proceed with parsing PFkeys commands */ ptrs=PFcmd[PF]; ptr=ComBuf; /* If key actually was PF and we didn't run into end of PF ** command definition... */ while (*ptrs && PF) { /* If not '\' just copy symbol into buffer */ if (*ptrs!='\\') { *ptr=*ptrs; ptr++; ptrs++; continue; } /* We have discovered '\'. Let's resolve it. */ ptrs++; switch (*ptrs) { /* End of line ? Do nothing then. */ case 0: break; /* User actually wants '\' */ case '\\': *ptr='\\'; ptr++; break; /* User wants his previous command inserted. */ case 'p': /* Set flag that we used previous command */ prev=1; strcpy(ptr, LastCmd); ptr=ComBuf+strlen(ComBuf); break; /* Insert string user actually entered */ case 's': strcpy(ptr, info); ptr=ComBuf+strlen(ComBuf); break; /* User wants 'CR' */ case 'n': *ptr=13; ptr++; break; /* Simulate CTRL key pressing */ case '^': ptrs++; if (!(*ptrs)) break; *ptr=KTRL(*ptrs); ptr++; break; /* ESC */ case 'c': *ptr=27; ptr++; break; /* Hmm, urecognized command. Just copy letter. */ default: *ptr=*ptrs; ptr++; } if (ptrs) ptrs++; } /* Terminate string with 0 if neccessary */ if (PF) *ptr=0; /* Empty string? User probably wants to send CR. */ if (!(*ComBuf)) { return (13); } /* Okay, set pointer to next letter */ ComPtr++; /* Backup last command if not empty */ ptr=ComBuf; /* String still "empty" */ i=1; while ((*ptr)&&i) { if ((*ptr!=13)&&(*ptr!=27)&&(*ptr!=' ')) i=0; ptr++; } /* Never update previous command if we inserted it. ** Comment: this is to avoid recursively multiplying previous ** commands. */ if ((!i)&&(!prev)) strcpy(LastCmd, ComBuf); /* Return something */ return (*ComBuf); } /* Get symbol from buffer if exists */ char InKeyBuf(void) { char ret; ret=*ComPtr; if (ret) ComPtr++; return (ret); } /* Here we draw all changed lines */ void ResetDISP(void) { int i; /* No need to redraw screen in near future */ kbhitcount=0; for (i=1; i<25; i++) if (DISP[i]!=0) { /* If line was changed, it won't stay changed any more */ DISP[i]=0; /* We don't want to erase input field definitons from ** screen, so check it carefully. */ switch (i) { case FIELD_SY: ShowLine(i, 1, FIELD_SX-1); if (i==FIELD_EY) { ShowLine(i, FIELD_EX+1, 80-FIELD_EX); } break; case FIELD_EY: ShowLine(i, FIELD_EX+1, 80-FIELD_EX); break; default: ShowLine(i, 1, 80); } } /* Okay, actually show something */ ResetScrBuf(); } /* Did user press something? */ int kbhit(void) { kbhitcount++; if (kbhitcount>99) { /* We waited long enough, time to redraw screen. ** Comment: during sleep every 100th turn is shown. */ kbhitcount=0; ResetDISP(); } return (CNSINTR); } /* Let's show requested line(or part of it). */ void ShowLine(int y, int x, int len) { char slbuf[512]; char *ptr, *scr, *attr; int i; unsigned char curattr; ptr=slbuf+3; /* Hack: was used for older version to place 25th string on ** 24th. */ if (y<25) GetAddr(y, x, slbuf); else GetAddr(24, x, slbuf); /* Set some adresses */ curattr=0; scr=VirtualScreen+(y-1)*cols+x-1; attr=ScreenAttr+(y-1)*cols+x-1; /* Let's proceed with string. */ for (i=0; i0x80) *ptr++=0xC1; else *ptr++=0xC2; } /* Color has changed ? Let's change it. */ if (((*attr)&0x7F)!=(curattr&0x7F)) { *ptr++=0x28; *ptr++=0x42; if (*attr>0x80) *ptr++=*attr; else *ptr++=*attr+0x80; } curattr=*attr; } *ptr++=*scr; scr++; attr++; } /* Add string to output stream */ AddScrBuf(slbuf, ptr-slbuf); } void LoadProfile(void) { FILE *fp; char line[128], *ptr, *pfptr, *p; int pf, i; for (i = 1; i <= 24; i++) strcpy(PFcmd[i], "\\s"); fp = fopen("PROFILE ANGBAND", "r"); if (!fp) return; { while (fgets(line, sizeof(line), fp)) { if (*line == '#') continue; ptr = strstr(line, "PF"); if (!ptr) continue; ptr += 2; p = ptr; while (isdigit((unsigned char)*p)) ++p; *p++ = 0; pf = atoi(ptr); if (pf < 1 || pf > 24) continue; ptr = strchr(p, '"'); ++ptr; p = strchr(ptr, '"'); if (!p) { puts("'\"' missing in PROFILE ANGBAND."); continue; } *p = 0; strcpy(PFcmd[pf], ptr); } } } /* ** ** File descriptor emulation for VM/ESA ** ** */ #include #include #include "fcntl.h" static FILE *file_descriptors[40]; int open(char *name, int flags, int mode) { char fmode[16]; FILE *fp; int i; strcpy(fmode, "rb"); if ((flags & O_WRONLY) || (flags & O_RDWR)) strcpy(fmode, "wb+"); fp = fopen(name, fmode); if (!fp) return (-1); for (i = 1; i < 40; i++) if (!file_descriptors[i]) { file_descriptors[i] = fp; return (i); } return (-1); } void close(int fd) { fclose(file_descriptors[fd]); } int read(int fd, char *buff, int bytes) { return (fread(buff, 1, bytes, file_descriptors[fd])); } int write(int fd, char *buff, int bytes) { return (fwrite(buff, 1, bytes, file_descriptors[fd])); } long lseek(int fd, long pos, int set) { return (fseek(file_descriptors[fd], pos, set)); } void unlink(char *filename) { remove(filename); } #endif /* USE_VME */ zangband/src/main-win.c0000755000000000000000000031066610250356274014035 0ustar rootroot/* File: main-win.c */ /* * Copyright (c) 1997 Ben Harrison, Skirmantas Kligys, Robert Ruehlmann, * and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with Windows computers. * * To use this file, use an appropriate "Makefile" or "Project File", * make sure that "WINDOWS" and/or "WIN32" are defined somewhere, and * make sure to obtain various extra files as described below. * * The Windows version has been tested to compile with Visual C++ 5.0 * and 6.0, Cygwin 1.0, Borland C++ 5.5 command line tools, and lcc-win32. * * * See also "main-dos.c" and "main-ibm.c". * * * The "lib/pref/pref-win.prf" file contains keymaps, macro definitions, * and/or color redefinitions. * * The "lib/pref/font-win.prf" contains attr/char mappings for use with the * normal "*.fon" font files in the "lib/xtra/font/" directory. * * The "lib/pref/graf-win.prf" contains attr/char mappings for use with the * special "*.bmp" bitmap files in the "lib/xtra/graf/" directory, which * are activated by a menu item. * * * Compiling this file, and using the resulting executable, requires * several extra files not distributed with the standard Angband code. * If "USE_GRAPHICS" is defined, then "readdib.h" and "readdib.c" must * be placed into "src/", and the "8x8.bmp" bitmap file must be placed * into "lib/xtra/graf". In any case, some "*.fon" files (including * "8X13.FON" if nothing else) must be placed into "lib/xtra/font/". * If "USE_SOUND" is defined, then some special library (for example, * "winmm.lib") may need to be linked in, and desired "*.WAV" sound * files must be placed into "lib/xtra/sound/". All of these extra * files can be found in the "ext-win" archive. * * * The "Term_xtra_win_clear()" function should probably do a low-level * clear of the current window, and redraw the borders and other things, * if only for efficiency. XXX XXX XXX * * A simpler method is needed for selecting the "tile size" for windows. * XXX XXX XXX * * Special "Windows Help Files" can be placed into "lib/xtra/help/" for * use with the "winhelp.exe" program. These files *may* be available * at the ftp site somewhere, but I have not seen them. XXX XXX XXX * * ToDo: The screensaver mode should implement ScreenSaverConfigureDialog, * DefScreenSaverProc, and ScreenSaverProc. * * Initial framework (and most code) by Ben Harrison (benh@phial.com). * * Original code by Skirmantas Kligys (kligys@scf.usc.edu). * * Additional code by Ross E Becker (beckerr@cis.ohio-state.edu), * and Chris R. Martin (crm7479@tam2000.tamu.edu). * * Additional code by Robert Ruehlmann . */ #include "angband.h" #include "maid-grf.h" #ifdef WINDOWS /* * Use HTML-Help. */ /* #define HTML_HELP */ #ifdef HTML_HELP # define HELP_GENERAL "angband.chm" # define HELP_SPOILERS "angband.chm" #else /* HTML_HELP */ # define HELP_GENERAL "angband.hlp" # define HELP_SPOILERS "spoilers.hlp" #endif /* HTML_HELP */ /* * Extract the "WIN32" flag from the compiler */ #if defined(__WIN32__) || defined(__WINNT__) || defined(__NT__) # ifndef WIN32 # define WIN32 # endif #endif #ifdef ALLOW_BORG /* * Hack -- allow use of "screen saver" mode */ /* #define USE_SAVER */ #endif /* ALLOW_BORG */ /* * Menu constants -- see "ANGBAND.RC" */ #define IDM_FILE_NEW 100 #define IDM_FILE_OPEN 101 #define IDM_FILE_SAVE 110 #define IDM_FILE_SCORE 120 #define IDM_FILE_EXIT 130 #define IDM_WINDOW_VIS_0 200 #define IDM_WINDOW_VIS_1 201 #define IDM_WINDOW_VIS_2 202 #define IDM_WINDOW_VIS_3 203 #define IDM_WINDOW_VIS_4 204 #define IDM_WINDOW_VIS_5 205 #define IDM_WINDOW_VIS_6 206 #define IDM_WINDOW_VIS_7 207 #define IDM_WINDOW_FONT_0 210 #define IDM_WINDOW_FONT_1 211 #define IDM_WINDOW_FONT_2 212 #define IDM_WINDOW_FONT_3 213 #define IDM_WINDOW_FONT_4 214 #define IDM_WINDOW_FONT_5 215 #define IDM_WINDOW_FONT_6 216 #define IDM_WINDOW_FONT_7 217 #define IDM_WINDOW_I_WID_0 240 #define IDM_WINDOW_I_WID_1 241 #define IDM_WINDOW_I_WID_2 242 #define IDM_WINDOW_I_WID_3 243 #define IDM_WINDOW_I_WID_4 244 #define IDM_WINDOW_I_WID_5 245 #define IDM_WINDOW_I_WID_6 246 #define IDM_WINDOW_I_WID_7 247 #define IDM_WINDOW_D_WID_0 250 #define IDM_WINDOW_D_WID_1 251 #define IDM_WINDOW_D_WID_2 252 #define IDM_WINDOW_D_WID_3 253 #define IDM_WINDOW_D_WID_4 254 #define IDM_WINDOW_D_WID_5 255 #define IDM_WINDOW_D_WID_6 256 #define IDM_WINDOW_D_WID_7 257 #define IDM_WINDOW_I_HGT_0 260 #define IDM_WINDOW_I_HGT_1 261 #define IDM_WINDOW_I_HGT_2 262 #define IDM_WINDOW_I_HGT_3 263 #define IDM_WINDOW_I_HGT_4 264 #define IDM_WINDOW_I_HGT_5 265 #define IDM_WINDOW_I_HGT_6 266 #define IDM_WINDOW_I_HGT_7 267 #define IDM_WINDOW_D_HGT_0 270 #define IDM_WINDOW_D_HGT_1 271 #define IDM_WINDOW_D_HGT_2 272 #define IDM_WINDOW_D_HGT_3 273 #define IDM_WINDOW_D_HGT_4 274 #define IDM_WINDOW_D_HGT_5 275 #define IDM_WINDOW_D_HGT_6 276 #define IDM_WINDOW_D_HGT_7 277 #define IDM_OPTIONS_GRAPHICS_NONE 400 #define IDM_OPTIONS_GRAPHICS_OLD 401 #define IDM_OPTIONS_GRAPHICS_ADAM 402 #define IDM_OPTIONS_GRAPHICS_DAVID 403 #define IDM_OPTIONS_BIGTILE 409 #define IDM_OPTIONS_SOUND 410 #define IDM_OPTIONS_LOW_PRIORITY 420 #define IDM_OPTIONS_SAVER 430 #define IDM_OPTIONS_MAP 440 #define IDM_HELP_GENERAL 901 #define IDM_HELP_SPOILERS 902 /* * This may need to be removed for some compilers XXX XXX XXX */ #define STRICT /* * Exclude parts of WINDOWS.H that are not needed */ #define NOCOMM /* Comm driver APIs and definitions */ #define NOLOGERROR /* LogError() and related definitions */ #define NOPROFILER /* Profiler APIs */ #define NOLFILEIO /* _l* file I/O routines */ #define NOOPENFILE /* OpenFile and related definitions */ #define NORESOURCE /* Resource management */ #define NOATOM /* Atom management */ #define NOLANGUAGE /* Character test routines */ #define NOLSTRING /* lstr* string management routines */ #define NODBCS /* Double-byte character set routines */ #define NOKEYBOARDINFO /* Keyboard driver routines */ #define NOCOLOR /* COLOR_* color values */ #define NODRAWTEXT /* DrawText() and related definitions */ #define NOSCALABLEFONT /* Truetype scalable font support */ #define NOMETAFILE /* Metafile support */ #define NOSYSTEMPARAMSINFO /* SystemParametersInfo() and SPI_* definitions */ #define NODEFERWINDOWPOS /* DeferWindowPos and related definitions */ #define NOKEYSTATES /* MK_* message key state flags */ #define NOWH /* SetWindowsHook and related WH_* definitions */ #define NOCLIPBOARD /* Clipboard APIs and definitions */ #define NOICONS /* IDI_* icon IDs */ #define NOMDI /* MDI support */ #define NOHELP /* Help support */ /* Not defined since it breaks Borland C++ 5.5 */ /* #define NOCTLMGR */ /* Control management and controls */ /* * Exclude parts of WINDOWS.H that are not needed (Win32) */ #define WIN32_LEAN_AND_MEAN #define NONLS /* All NLS defines and routines */ #define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */ #define NOKANJI /* Kanji support stuff. */ #define NOMCX /* Modem Configuration Extensions */ /* Mega-hack, these include files require double and float to work */ #undef float #undef double /* * Include the "windows" support file */ #include #ifdef USE_SOUND /* * Exclude parts of MMSYSTEM.H that are not needed */ #define MMNODRV /* Installable driver support */ #define MMNOWAVE /* Waveform support */ #define MMNOMIDI /* MIDI support */ #define MMNOAUX /* Auxiliary audio support */ #define MMNOTIMER /* Timer support */ #define MMNOJOY /* Joystick support */ #define MMNOMCI /* MCI support */ #define MMNOMMIO /* Multimedia file I/O support */ #define MMNOMMSYSTEM /* General MMSYSTEM functions */ #include #endif /* USE_SOUND */ #include /* * HTML-Help requires htmlhelp.h and htmlhelp.lib from Microsoft's * HTML Workshop < msdn.microsoft.com/workshop/author/htmlhelp/ >. */ #ifdef HTML_HELP #include #endif /* HTML_HELP */ /* * Include the support for loading bitmaps */ #ifdef USE_GRAPHICS # include "readdib.h" #endif /* USE_GRAPHICS */ /* * Hack -- Fake declarations from "dos.h" XXX XXX XXX */ #ifdef WIN32 #define INVALID_FILE_NAME (DWORD)0xFFFFFFFF #else /* WIN32 */ #define FA_LABEL 0x08 /* Volume label */ #define FA_DIREC 0x10 /* Directory */ unsigned _cdecl _dos_getfileattr(const char *, unsigned *); #endif /* WIN32 */ /* Mega-hack redefine them again */ #undef float #define float floating_point_is_not_allowed #undef double #define double floating_point_is_not_allowed /* * Silliness in WIN32 drawing routine */ #ifdef WIN32 # define MoveTo(H,X,Y) MoveToEx(H, X, Y, NULL) #endif /* WIN32 */ /* * Silliness for Windows 95 */ #ifndef WS_EX_TOOLWINDOW # define WS_EX_TOOLWINDOW 0 #endif /* WS_EX_TOOLWINDOW */ /* * Foreground color bits (hard-coded by DOS) */ #define VID_BLACK 0x00 #define VID_BLUE 0x01 #define VID_GREEN 0x02 #define VID_CYAN 0x03 #define VID_RED 0x04 #define VID_MAGENTA 0x05 #define VID_YELLOW 0x06 #define VID_WHITE 0x07 /* * Bright text (hard-coded by DOS) */ #define VID_BRIGHT 0x08 /* * Background color bits (hard-coded by DOS) */ #define VUD_BLACK 0x00 #define VUD_BLUE 0x10 #define VUD_GREEN 0x20 #define VUD_CYAN 0x30 #define VUD_RED 0x40 #define VUD_MAGENTA 0x50 #define VUD_YELLOW 0x60 #define VUD_WHITE 0x70 /* * Blinking text (hard-coded by DOS) */ #define VUD_BRIGHT 0x80 /* * Forward declare */ typedef struct _term_data term_data; /* * Extra "term" data * * Note the use of "font_want" for the names of the font file requested by * the user, and the use of "font_file" for the currently active font file. * * The "font_file" is uppercased, and takes the form "8X13.FON", while * "font_want" can be in almost any form as long as it could be construed * as attempting to represent the name of a font. */ struct _term_data { term t; cptr s; HWND w; DWORD dwStyle; DWORD dwExStyle; uint keys; byte rows; byte cols; uint pos_x; uint pos_y; uint size_wid; uint size_hgt; uint size_ow1; uint size_oh1; uint size_ow2; uint size_oh2; bool size_hack; bool xtra_hack; bool visible; bool maximized; cptr font_want; cptr font_file; HFONT font_id; uint font_wid; uint font_hgt; uint tile_wid; uint tile_hgt; uint map_tile_wid; uint map_tile_hgt; bool map_active; }; /* * Maximum number of windows XXX XXX XXX */ #define MAX_TERM_DATA 8 /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Hack -- global "window creation" pointer */ static term_data *my_td; /* * game in progress */ bool game_in_progress = FALSE; /* * note when "open"/"new" become valid */ bool initialized = FALSE; /* * screen paletted, i.e. 256 colors */ bool paletted = FALSE; /* * 16 colors screen, don't use RGB() */ bool colors16 = FALSE; static bool low_priority = FALSE; /* * Saved instance handle */ static HINSTANCE hInstance; /* * Yellow brush for the cursor */ static HBRUSH hbrYellow; /* * An icon */ static HICON hIcon; /* * A palette */ static HPALETTE hPal; #ifdef USE_SAVER /* * The screen saver window */ static HWND hwndSaver; static bool screensaver = FALSE; static bool screensaver_active = FALSE; static HANDLE screensaverSemaphore; static char saverfilename[1024]; static HMENU main_menu; #define MOUSE_SENS 10 #endif /* USE_SAVER */ #ifdef USE_GRAPHICS /* * Flag set once "graphics" has been initialized */ static bool can_use_graphics = FALSE; /* * The global bitmap */ static DIBINIT infGraph; /* * The global bitmap mask */ static DIBINIT infMask; #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* * Flag set once "sound" has been initialized */ static bool can_use_sound = FALSE; #define SAMPLE_MAX 8 /* * An array of sound file names */ static cptr sound_file[SOUND_MAX][SAMPLE_MAX]; #endif /* USE_SOUND */ /* * Full path to ANGBAND.INI */ static cptr ini_file = NULL; /* * Name of application */ static cptr AppName = VERSION_NAME; /* * Name of sub-window type */ static cptr AngList = "AngList"; /* * Directory names */ static cptr ANGBAND_DIR_XTRA_FONT; static cptr ANGBAND_DIR_XTRA_GRAF; static cptr ANGBAND_DIR_XTRA_SOUND; static cptr ANGBAND_DIR_XTRA_HELP; #if USE_MUSIC static cptr ANGBAND_DIR_XTRA_MUSIC; #endif /* USE_MUSIC */ /* * The "complex" color values */ static COLORREF win_clr[256]; /* * The "simple" color values * * See "main-ibm.c" for original table information * * The entries below are taken from the "color bits" defined above. * * Note that many of the choices below suck, but so do crappy monitors. */ static byte win_pal[256] = { VID_BLACK, /* Dark */ VID_WHITE, /* White */ VID_CYAN, /* Slate XXX */ VID_RED | VID_BRIGHT, /* Orange XXX */ VID_RED, /* Red */ VID_GREEN, /* Green */ VID_BLUE, /* Blue */ VID_YELLOW, /* Umber XXX */ VID_BLACK | VID_BRIGHT, /* Light Dark */ VID_CYAN | VID_BRIGHT, /* Light Slate XXX */ VID_MAGENTA, /* Violet XXX */ VID_YELLOW | VID_BRIGHT, /* Yellow */ VID_MAGENTA | VID_BRIGHT, /* Light Red XXX */ VID_GREEN | VID_BRIGHT, /* Light Green */ VID_BLUE | VID_BRIGHT, /* Light Blue */ VID_YELLOW /* Light Umber XXX */ }; #ifdef SUPPORT_GAMMA static int gamma_correction; #endif /* SUPPORT_GAMMA */ /* * Hack -- define which keys are "special" */ static bool special_key[256]; /* * Hack -- initialization list for "special_key" * * We ignore the modifier keys (shift, control, alt, num lock, scroll lock), * and the normal keys (escape, tab, return, letters, numbers, etc), but we * catch the keypad keys (with and without numlock set, including keypad 5), * the function keys (including the "menu" key which maps to F10), and the * "pause" key (between scroll lock and numlock). We also catch a few odd * keys which I do not recognize, but which are listed among keys which we * do catch, so they should be harmless to catch. */ static const byte special_key_list[] = { VK_CLEAR, /* 0x0C (KP<5>) */ VK_PAUSE, /* 0x13 (pause) */ VK_PRIOR, /* 0x21 (KP<9>) */ VK_NEXT, /* 0x22 (KP<3>) */ VK_END, /* 0x23 (KP<1>) */ VK_HOME, /* 0x24 (KP<7>) */ VK_LEFT, /* 0x25 (KP<4>) */ VK_UP, /* 0x26 (KP<8>) */ VK_RIGHT, /* 0x27 (KP<6>) */ VK_DOWN, /* 0x28 (KP<2>) */ VK_SELECT, /* 0x29 (?) */ VK_PRINT, /* 0x2A (?) */ VK_EXECUTE, /* 0x2B (?) */ VK_SNAPSHOT, /* 0x2C (?) */ VK_INSERT, /* 0x2D (KP<0>) */ VK_DELETE, /* 0x2E (KP<.>) */ VK_HELP, /* 0x2F (?) */ #if 0 VK_NUMPAD0, /* 0x60 (KP<0>) */ VK_NUMPAD1, /* 0x61 (KP<1>) */ VK_NUMPAD2, /* 0x62 (KP<2>) */ VK_NUMPAD3, /* 0x63 (KP<3>) */ VK_NUMPAD4, /* 0x64 (KP<4>) */ VK_NUMPAD5, /* 0x65 (KP<5>) */ VK_NUMPAD6, /* 0x66 (KP<6>) */ VK_NUMPAD7, /* 0x67 (KP<7>) */ VK_NUMPAD8, /* 0x68 (KP<8>) */ VK_NUMPAD9, /* 0x69 (KP<9>) */ VK_MULTIPLY, /* 0x6A (KP<*>) */ VK_ADD, /* 0x6B (KP<+>) */ VK_SEPARATOR, /* 0x6C (?????) */ VK_SUBTRACT, /* 0x6D (KP<->) */ VK_DECIMAL, /* 0x6E (KP<.>) */ VK_DIVIDE, /* 0x6F (KP) */ #endif /* 0 */ VK_F1, /* 0x70 */ VK_F2, /* 0x71 */ VK_F3, /* 0x72 */ VK_F4, /* 0x73 */ VK_F5, /* 0x74 */ VK_F6, /* 0x75 */ VK_F7, /* 0x76 */ VK_F8, /* 0x77 */ VK_F9, /* 0x78 */ VK_F10, /* 0x79 */ VK_F11, /* 0x7A */ VK_F12, /* 0x7B */ VK_F13, /* 0x7C */ VK_F14, /* 0x7D */ VK_F15, /* 0x7E */ VK_F16, /* 0x7F */ VK_F17, /* 0x80 */ VK_F18, /* 0x81 */ VK_F19, /* 0x82 */ VK_F20, /* 0x83 */ VK_F21, /* 0x84 */ VK_F22, /* 0x85 */ VK_F23, /* 0x86 */ VK_F24, /* 0x87 */ 0 }; #if 0 /* * Hack -- given a pathname, point at the filename */ static cptr extract_file_name(cptr s) { cptr p; /* Start at the end */ p = s + strlen(s) - 1; /* Back up to divider */ while ((p >= s) && (*p != ':') && (*p != '\\')) p--; /* Return file name */ return (p+1); } #endif /* 0 */ static void show_win_error(void) { LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); MessageBox(NULL, lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION); LocalFree(lpMsgBuf); } /* * Hack -- given a simple filename, extract the "font size" info * * Return a pointer to a static buffer holding the capitalized base name. */ static char *analyze_font(char *path, int *wp, int *hp) { int wid, hgt; char *s, *p; /* Start at the end */ p = path + strlen(path) - 1; /* Back up to divider */ while ((p >= path) && (*p != ':') && (*p != '\\')) --p; /* Advance to file name */ ++p; /* Capitalize */ for (s = p; *s; ++s) { /* Capitalize (be paranoid) */ if (islower((unsigned char)*s)) *s = toupper((unsigned char)*s); } /* Find first 'X' */ s = strchr(p, 'X'); /* Extract font width */ wid = atoi(p); /* Extract height */ hgt = s ? atoi(s+1) : 0; /* Save results */ (*wp) = wid; (*hp) = hgt; /* Result */ return (p); } /* * Check for existance of a file */ static bool check_file(cptr s) { char path[1024]; #ifdef WIN32 DWORD attrib; #else /* WIN32 */ unsigned int attrib; #endif /* WIN32 */ /* Copy it */ strnfmt(path, 1024, "%s", s); #ifdef WIN32 /* Examine */ attrib = GetFileAttributes(path); /* Require valid filename */ if (attrib == INVALID_FILE_NAME) return (FALSE); /* Prohibit directory */ if (attrib & FILE_ATTRIBUTE_DIRECTORY) return (FALSE); #else /* WIN32 */ /* Examine and verify */ if (_dos_getfileattr(path, &attrib)) return (FALSE); /* Prohibit something */ if (attrib & FA_LABEL) return (FALSE); /* Prohibit directory */ if (attrib & FA_DIREC) return (FALSE); #endif /* WIN32 */ /* Success */ return (TRUE); } /* * Check for existance of a directory */ static bool check_dir(cptr s) { int i; char path[1024]; #ifdef WIN32 DWORD attrib; #else /* WIN32 */ unsigned int attrib; #endif /* WIN32 */ /* Copy it */ strcpy(path, s); /* Check length */ i = strlen(path); /* Remove trailing backslash */ if (i && (path[i-1] == '\\')) path[--i] = '\0'; #ifdef WIN32 /* Examine */ attrib = GetFileAttributes(path); /* Require valid filename */ if (attrib == INVALID_FILE_NAME) return (FALSE); /* Require directory */ if (!(attrib & FILE_ATTRIBUTE_DIRECTORY)) return (FALSE); #else /* WIN32 */ /* Examine and verify */ if (_dos_getfileattr(path, &attrib)) return (FALSE); /* Prohibit something */ if (attrib & FA_LABEL) return (FALSE); /* Require directory */ if (!(attrib & FA_DIREC)) return (FALSE); #endif /* WIN32 */ /* Success */ return (TRUE); } /* * Validate a file */ static void validate_file(cptr s) { /* Verify or fail */ if (!check_file(s)) { quit_fmt("Cannot find required file:\n%s", s); } } /* * Validate a directory */ static void validate_dir(cptr s) { /* Verify or fail */ if (!check_dir(s)) { quit_fmt("Cannot find required directory:\n%s", s); } } /* * Get the "size" for a window */ static void term_getsize(term_data *td) { RECT rc; int wid, hgt; /* Paranoia */ if (td->cols < 1) td->cols = 1; if (td->rows < 1) td->rows = 1; /* Window sizes */ wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2; hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2; /* Fake window size */ rc.left = 0; rc.right = rc.left + wid; rc.top = 0; rc.bottom = rc.top + hgt; /* XXX XXX XXX */ /* rc.right += 1; */ /* rc.bottom += 1; */ /* Adjust */ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle); /* Total size */ td->size_wid = rc.right - rc.left; td->size_hgt = rc.bottom - rc.top; /* See CreateWindowEx */ if (!td->w) return; /* Extract actual location */ GetWindowRect(td->w, &rc); /* Save the location */ td->pos_x = rc.left; td->pos_y = rc.top; } /* * Write the "prefs" for a single term */ static void save_prefs_aux(term_data *td, cptr sec_name) { char buf[1024]; RECT rc; WINDOWPLACEMENT lpwndpl; /* Paranoia */ if (!td->w) return; /* Visible */ strcpy(buf, td->visible ? "1" : "0"); WritePrivateProfileString(sec_name, "Visible", buf, ini_file); /* Font */ strcpy(buf, td->font_file ? td->font_file : "8X13.FON"); WritePrivateProfileString(sec_name, "Font", buf, ini_file); /* Tile size (x) */ wsprintf(buf, "%d", td->tile_wid); WritePrivateProfileString(sec_name, "TileWid", buf, ini_file); /* Tile size (y) */ wsprintf(buf, "%d", td->tile_hgt); WritePrivateProfileString(sec_name, "TileHgt", buf, ini_file); /* Window size (x) */ wsprintf(buf, "%d", td->cols); WritePrivateProfileString(sec_name, "NumCols", buf, ini_file); /* Window size (y) */ wsprintf(buf, "%d", td->rows); WritePrivateProfileString(sec_name, "NumRows", buf, ini_file); /* Get window placement and dimensions */ lpwndpl.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(td->w, &lpwndpl); /* Acquire position in *normal* mode (not minimized) */ rc = lpwndpl.rcNormalPosition; /* Get information about the placement of the window */ if (lpwndpl.flags & SW_SHOWMAXIMIZED) td->maximized = TRUE; else td->maximized = FALSE; /* Window position (x) */ wsprintf(buf, "%d", rc.left); WritePrivateProfileString(sec_name, "PositionX", buf, ini_file); /* Window position (y) */ wsprintf(buf, "%d", rc.top); WritePrivateProfileString(sec_name, "PositionY", buf, ini_file); /* Maximized */ strcpy(buf, td->maximized ? "1" : "0"); WritePrivateProfileString(sec_name, "Maximized", buf, ini_file); } /* * Write the "prefs" * * We assume that the windows have all been initialized */ static void save_prefs(void) { int i; char buf[128]; /* Save the "arg_graphics" flag */ strnfmt(buf, 128, "%d", arg_graphics); WritePrivateProfileString("Angband", "Graphics", buf, ini_file); /* Save the "use_bigtile" flag */ strnfmt(buf, 128, "%d", use_bigtile ? 1 : 0); WritePrivateProfileString("Angband", "Bigtile", buf, ini_file); /* Save the "arg_sound" flag */ strnfmt(buf, 128, "%d", arg_sound ? 1 : 0); WritePrivateProfileString("Angband", "Sound", buf, ini_file); /* Save window prefs */ for (i = 0; i < MAX_TERM_DATA; i++) { term_data *td = &data[i]; strnfmt(buf, 128, "Term-%d", i); save_prefs_aux(td, buf); } } /* * Load the "prefs" for a single term */ static void load_prefs_aux(term_data *td, cptr sec_name) { char tmp[1024]; int wid, hgt; /* Visible */ td->visible = (GetPrivateProfileInt(sec_name, "Visible", td->visible, ini_file) != 0); /* Maximized */ td->maximized = (GetPrivateProfileInt(sec_name, "Maximized", td->maximized, ini_file) != 0); /* Desired font, with default */ GetPrivateProfileString(sec_name, "Font", "8X13.FON", tmp, 127, ini_file); /* Analyze font, save desired font name */ td->font_want = string_make(analyze_font(tmp, &wid, &hgt)); /* Tile size */ td->tile_wid = GetPrivateProfileInt(sec_name, "TileWid", wid, ini_file); td->tile_hgt = GetPrivateProfileInt(sec_name, "TileHgt", hgt, ini_file); /* Window size */ td->cols = GetPrivateProfileInt(sec_name, "NumCols", td->cols, ini_file); td->rows = GetPrivateProfileInt(sec_name, "NumRows", td->rows, ini_file); /* Window position */ td->pos_x = GetPrivateProfileInt(sec_name, "PositionX", td->pos_x, ini_file); td->pos_y = GetPrivateProfileInt(sec_name, "PositionY", td->pos_y, ini_file); } /* * Load the "prefs" */ static void load_prefs(void) { int i; char buf[1024]; /* Extract the "arg_graphics" flag */ arg_graphics = GetPrivateProfileInt("Angband", "Graphics", GRAPHICS_NONE, ini_file); /* Extract the "use_bigtile" flag */ use_bigtile = GetPrivateProfileInt("Angband", "Bigtile", FALSE, ini_file); /* Extract the "arg_sound" flag */ arg_sound = (GetPrivateProfileInt("Angband", "Sound", 0, ini_file) != 0); /* Extract the "arg_fiddle" flag */ arg_fiddle = (GetPrivateProfileInt("Angband", "Fiddle", 0, ini_file) != 0); /* Extract the "arg_wizard" flag */ arg_wizard = (GetPrivateProfileInt("Angband", "Wizard", 0, ini_file) != 0); /* Extract the "arg_roguelike" flag */ arg_force_roguelike = (GetPrivateProfileInt("Angband", "force_roguelike", 0, ini_file) != 0); /* Extract the "arg_original" flag */ arg_force_original = (GetPrivateProfileInt("Angband", "force_original", 0, ini_file) != 0); #ifdef SUPPORT_GAMMA /* Extract the gamma correction */ gamma_correction = GetPrivateProfileInt("Angband", "Gamma", 0, ini_file); #endif /* SUPPORT_GAMMA */ /* Load window prefs */ for (i = 0; i < MAX_TERM_DATA; i++) { term_data *td = &data[i]; strnfmt(buf, 1024, "Term-%d", i); load_prefs_aux(td, buf); } /* Paranoia */ if (data[0].cols < 80) data[0].cols = 80; if (data[0].rows < 24) data[0].rows = 24; } #ifdef USE_SOUND /* * XXX XXX XXX - Taken from files.c. * * Extract "tokens" from a buffer * * This function uses "whitespace" as delimiters, and treats any amount of * whitespace as a single delimiter. We will never return any empty tokens. * When given an empty buffer, or a buffer containing only "whitespace", we * will return no tokens. We will never extract more than "num" tokens. * * By running a token through the "text_to_ascii()" function, you can allow * that token to include (encoded) whitespace, using "\s" to encode spaces. * * We save pointers to the tokens in "tokens", and return the number found. */ static s16b tokenize_whitespace(char *buf, s16b num, char **tokens) { int k = 0; char *s = buf; /* Process */ while (k < num) { char *t; /* Skip leading whitespace */ for ( ; *s && isspace((unsigned char)*s); ++s) /* loop */; /* All done */ if (!*s) break; /* Find next whitespace, if any */ for (t = s; *t && !isspace((unsigned char)*t); ++t) /* loop */; /* Nuke and advance (if necessary) */ if (*t) *t++ = '\0'; /* Save the token */ tokens[k++] = s; /* Advance */ s = t; } /* Count */ return (k); } static void load_sound_prefs(void) { int i, j, num; char tmp[1024]; char ini_path[1024]; char wav_path[1024]; char *zz[SAMPLE_MAX]; /* Access the sound.cfg */ path_make(ini_path, ANGBAND_DIR_XTRA_SOUND, "sound.cfg"); for (i = 0; i < SOUND_MAX; i++) { /* Ignore empty sound strings */ if (!angband_sound_name[i][0]) continue; GetPrivateProfileString("Sound", angband_sound_name[i], "", tmp, sizeof(tmp), ini_path); num = tokenize_whitespace(tmp, SAMPLE_MAX, zz); for (j = 0; j < num; j++) { /* Access the sound */ path_make(wav_path, ANGBAND_DIR_XTRA_SOUND, zz[j]); /* Save the sound filename, if it exists */ if (check_file(wav_path)) sound_file[i][j] = string_make(zz[j]); } } } #endif /* USE_SOUND */ /* * Create the new global palette based on the bitmap palette * (if any), and the standard 16 entry palette derived from * "win_clr[]" which is used for the basic 16 Angband colors. * * This function is never called before all windows are ready. * * This function returns FALSE if the new palette could not be * prepared, which should normally be a fatal error. XXX XXX * * Note that only some machines actually use a "palette". */ static int new_palette(void) { #ifdef USE_GRAPHICS HPALETTE hBmPal; #endif /* USE_GRAPHICS */ HPALETTE hNewPal; HDC hdc; int i, nEntries; int pLogPalSize; LPLOGPALETTE pLogPal; LPPALETTEENTRY lppe; term_data *td; /* This makes no sense */ if (!paletted) return (TRUE); /* No bitmap */ lppe = NULL; nEntries = 0; #ifdef USE_GRAPHICS /* Check the bitmap palette */ hBmPal = infGraph.hPalette; /* Use the bitmap */ if (hBmPal) { lppe = ralloc(256 * sizeof(PALETTEENTRY)); nEntries = GetPaletteEntries(hBmPal, 0, 255, lppe); if ((nEntries == 0) || (nEntries > 220)) { /* Warn the user */ plog("Please switch to high- or true-color mode."); /* Cleanup */ free(lppe); /* Fail */ return (FALSE); } } #endif /* USE_GRAPHICS */ /* Size of palette */ pLogPalSize = sizeof(LOGPALETTE) + (nEntries + 16) * sizeof(PALETTEENTRY); /* Allocate palette */ pLogPal = (LPLOGPALETTE)ralloc(pLogPalSize); /* Version */ pLogPal->palVersion = 0x300; /* Make room for bitmap and normal data */ pLogPal->palNumEntries = nEntries + 16; /* Save the bitmap data */ for (i = 0; i < nEntries; i++) { pLogPal->palPalEntry[i] = lppe[i]; } /* Save the normal data */ for (i = 0; i < 16; i++) { LPPALETTEENTRY p; /* Access the entry */ p = &(pLogPal->palPalEntry[i+nEntries]); /* Save the colors */ p->peRed = GetRValue(win_clr[i]); p->peGreen = GetGValue(win_clr[i]); p->peBlue = GetBValue(win_clr[i]); #ifdef SUPPORT_GAMMA if (gamma_correction > 0) { p->peRed = gamma_table[p->peRed]; p->peGreen = gamma_table[p->peGreen]; p->peBlue = gamma_table[p->peBlue]; } #endif /* SUPPORT_GAMMA */ /* Save the flags */ p->peFlags = PC_NOCOLLAPSE; } /* Free something */ if (lppe) free(lppe); /* Create a new palette, or fail */ hNewPal = CreatePalette(pLogPal); if (!hNewPal) quit("Cannot create palette!"); /* Free the palette */ free(pLogPal); /* Main window */ td = &data[0]; /* Realize the palette */ hdc = GetDC(td->w); SelectPalette(hdc, hNewPal, 0); i = RealizePalette(hdc); ReleaseDC(td->w, hdc); if (i == 0) quit("Cannot realize palette!"); /* Sub-windows */ for (i = 1; i < MAX_TERM_DATA; i++) { td = &data[i]; hdc = GetDC(td->w); SelectPalette(hdc, hNewPal, 0); ReleaseDC(td->w, hdc); } /* Delete old palette */ if (hPal) DeleteObject(hPal); /* Save new palette */ hPal = hNewPal; /* Success */ return (TRUE); } #ifdef USE_GRAPHICS /* * Initialize graphics */ static bool init_graphics(void) { /* Initialize once */ /* if (can_use_graphics != arg_graphics) */ { char buf[1024]; int wid, hgt; cptr name; if (arg_graphics == GRAPHICS_DAVID_GERVAIS) { wid = 32; hgt = 32; name = "32x32.bmp"; use_transparency = TRUE; } else if (arg_graphics == GRAPHICS_ADAM_BOLT) { wid = 16; hgt = 16; name = "16X16.BMP"; use_transparency = TRUE; } else { wid = 8; hgt = 8; name = "8X8.BMP"; } /* Access the bitmap file */ path_make(buf, ANGBAND_DIR_XTRA_GRAF, name); /* Load the bitmap or quit */ if (!ReadDIB(data[0].w, buf, &infGraph)) { plog_fmt("Cannot read bitmap file '%s'", name); return (FALSE); } /* Save the new sizes */ infGraph.CellWidth = wid; infGraph.CellHeight = hgt; if (arg_graphics == GRAPHICS_ADAM_BOLT) { /* Access the mask file */ path_make(buf, ANGBAND_DIR_XTRA_GRAF, "mask.bmp"); /* Load the bitmap or quit */ if (!ReadDIB(data[0].w, buf, &infMask)) { plog_fmt("Cannot read bitmap file '%s'", buf); return (FALSE); } } else if (arg_graphics == GRAPHICS_DAVID_GERVAIS) { /* Access the mask file */ path_make(buf, ANGBAND_DIR_XTRA_GRAF, "mask32.bmp"); /* Load the bitmap or quit */ if (!ReadDIB(data[0].w, buf, &infMask)) { plog_fmt("Cannot read bitmap file '%s'", buf); return (FALSE); } } /* Activate a palette */ if (!new_palette()) { /* Free bitmap XXX XXX XXX */ /* Oops */ plog("Cannot activate palette!"); return (FALSE); } /* Graphics available */ can_use_graphics = arg_graphics; } /* Result */ return (can_use_graphics); } #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* * Initialize sound */ static bool init_sound(void) { /* Initialize once */ if (!can_use_sound) { /* Load the prefs */ load_sound_prefs(); /* Sound available */ can_use_sound = TRUE; } /* Result */ return (can_use_sound); } #endif /* USE_SOUND */ /* * Resize a window */ static void term_window_resize(const term_data *td) { /* Require window */ if (!td->w) return; /* Resize the window */ SetWindowPos(td->w, 0, 0, 0, td->size_wid, td->size_hgt, SWP_NOMOVE | SWP_NOZORDER); /* Redraw later */ InvalidateRect(td->w, NULL, TRUE); } /* * Force the use of a new "font file" for a term_data * * This function may be called before the "window" is ready * * This function returns zero only if everything succeeds. * * Note that the "font name" must be capitalized!!! */ static errr term_force_font(term_data *td, cptr path) { int i; int wid, hgt; char *base; char buf[1024]; /* Forget the old font (if needed) */ if (td->font_id) DeleteObject(td->font_id); /* Forget old font */ if (td->font_file) { bool used = FALSE; /* Scan windows */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Don't check when closing the application */ if (!path) break; /* Check "screen" */ if ((td != &data[i]) && (data[i].font_file) && (streq(data[i].font_file, td->font_file))) { used = TRUE; } } /* Remove unused font resources */ if (!used) RemoveFontResource(td->font_file); /* Free the old name */ string_free(td->font_file); /* Forget it */ td->font_file = NULL; } /* No path given */ if (!path) return (1); /* Local copy */ strnfmt(buf, sizeof(buf), "%s", path); /* Analyze font path */ base = analyze_font(buf, &wid, &hgt); /* Verify suffix */ if (!suffix(base, ".FON")) return (1); /* Verify file */ if (!check_file(buf)) return (1); /* Load the new font */ if (!AddFontResource(buf)) return (1); /* Save new font name */ td->font_file = string_make(base); /* Remove the "suffix" */ base[strlen(base)-4] = '\0'; /* Create the font (using the 'base' of the font file name!) */ td->font_id = CreateFont(hgt, wid, 0, 0, FW_DONTCARE, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, base); /* Hack -- Unknown size */ if (!wid || !hgt) { HDC hdcDesktop; HFONT hfOld; TEXTMETRIC tm; /* all this trouble to get the cell size */ hdcDesktop = GetDC(HWND_DESKTOP); hfOld = SelectObject(hdcDesktop, td->font_id); GetTextMetrics(hdcDesktop, &tm); SelectObject(hdcDesktop, hfOld); ReleaseDC(HWND_DESKTOP, hdcDesktop); /* Font size info */ wid = tm.tmAveCharWidth; hgt = tm.tmHeight; } /* Save the size info */ td->font_wid = wid; td->font_hgt = hgt; /* Success */ return (0); } /* * Allow the user to change the font for this window. */ static void term_change_font(term_data *td) { OPENFILENAME ofn; char tmp[1024] = ""; /* Extract a default if possible */ if (td->font_file) strcpy(tmp, td->font_file); /* Ask for a choice */ memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = data[0].w; ofn.lpstrFilter = "Angband Font Files (*.fon)\0*.fon\0"; ofn.nFilterIndex = 1; ofn.lpstrFile = tmp; ofn.nMaxFile = 128; ofn.lpstrInitialDir = ANGBAND_DIR_XTRA_FONT; ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; ofn.lpstrDefExt = "fon"; /* Force choice if legal */ if (GetOpenFileName(&ofn)) { /* Force the font */ if (term_force_font(td, tmp)) { /* Access the standard font file */ path_make(tmp, ANGBAND_DIR_XTRA_FONT, "8X13.FON"); /* Force the use of that font */ (void)term_force_font(td, tmp); } /* Reset the tile info */ td->tile_wid = td->font_wid; td->tile_hgt = td->font_hgt; /* Analyze the font */ term_getsize(td); /* Resize the window */ term_window_resize(td); } } static void windows_map_aux(void); /* * Hack -- redraw a term_data */ static void term_data_redraw(term_data *td) { if (td->map_active) { /* Redraw the map */ windows_map_aux(); } else { /* Activate the term */ Term_activate(&td->t); /* Redraw the contents */ Term_redraw(); /* Restore the term */ Term_activate(term_screen); } } /* * Hack -- redraw a term_data */ static void term_data_redraw_section(term_data *td, int x1, int y1, int x2, int y2) { /* Activate the term */ Term_activate(&td->t); /* Redraw the area */ Term_redraw_section(x1, y1, x2, y2); /* Restore the term */ Term_activate(term_screen); } /*** Function hooks needed by "Term" ***/ #if 0 /* * Initialize a new Term */ static void Term_init_win(term *t) { /* XXX Unused */ } /* * Nuke an old Term */ static void Term_nuke_win(term *t) { /* XXX Unused */ } #endif /* 0 */ /* * Interact with the User */ static errr Term_user_win(int n) { /* Unused parameter */ (void)n; /* Success */ return (0); } /* * React to global changes */ static errr Term_xtra_win_react(void) { int i; /* Simple color */ if (colors16) { /* Save the default colors */ for (i = 0; i < 256; i++) { /* Simply accept the desired colors */ win_pal[i] = angband_color_table[i][0]; } } /* Complex color */ else { COLORREF code; byte rv, gv, bv; bool change = FALSE; /* Save the default colors */ for (i = 0; i < 256; i++) { /* Extract desired values */ rv = angband_color_table[i][1]; gv = angband_color_table[i][2]; bv = angband_color_table[i][3]; #ifdef SUPPORT_GAMMA if (gamma_correction > 0) { rv = gamma_table[rv]; gv = gamma_table[gv]; bv = gamma_table[bv]; } #endif /* SUPPORT_GAMMA */ /* Extract a full color code */ code = PALETTERGB(rv, gv, bv); /* Activate changes */ if (win_clr[i] != code) { /* Note the change */ change = TRUE; /* Apply the desired color */ win_clr[i] = code; } } /* Activate the palette if needed */ if (change) (void)new_palette(); } #ifdef USE_SOUND /* Handle "arg_sound" */ if (use_sound != arg_sound) { /* Initialize (if needed) */ if (arg_sound && !init_sound()) { /* Warning */ plog("Cannot initialize sound!"); /* Cannot enable */ arg_sound = FALSE; } /* Change setting */ use_sound = arg_sound; } #endif /* USE_SOUND */ #ifdef USE_GRAPHICS /* Handle "arg_graphics" */ if (use_graphics != arg_graphics) { /* Switch off transparency */ use_transparency = FALSE; /* Free the bitmap stuff */ FreeDIB(&infGraph); FreeDIB(&infMask); /* Initialize (if needed) */ if (arg_graphics && !init_graphics()) { /* Warning */ plog("Cannot initialize graphics!"); /* Cannot enable */ arg_graphics = GRAPHICS_NONE; } /* Change setting */ use_graphics = arg_graphics; /* Reset visuals */ #ifdef ANGBAND_2_8_1 reset_visuals(); #else /* ANGBAND_2_8_1 */ reset_visuals(TRUE); #endif /* ANGBAND_2_8_1 */ } #endif /* USE_GRAPHICS */ /* Clean up windows */ for (i = 0; i < MAX_TERM_DATA; i++) { term *old = Term; term_data *td = &data[i]; /* Update resized windows */ if ((td->cols != td->t.wid) || (td->rows != td->t.hgt)) { /* Activate */ Term_activate(&td->t); /* Hack -- Resize the term */ Term_resize(td->cols, td->rows); /* Redraw the contents */ Term_redraw(); /* Restore */ Term_activate(old); } } /* Success */ return (0); } /* * Process at least one event */ static errr Term_xtra_win_event(int v) { MSG msg; /* Wait for an event */ if (v) { /* Block */ if (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /* Check for an event */ else { /* Check */ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /* Success */ return 0; } /* * Process all pending events */ static errr Term_xtra_win_flush(void) { MSG msg; /* Process all pending events */ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* Success */ return (0); } /* * Hack -- clear the screen * * Make this more efficient XXX XXX XXX */ static errr Term_xtra_win_clear(void) { term_data *td = (term_data*)(Term->data); HDC hdc; RECT rc; /* Rectangle to erase */ rc.left = td->size_ow1; rc.right = rc.left + td->cols * td->tile_wid; rc.top = td->size_oh1; rc.bottom = rc.top + td->rows * td->tile_hgt; /* Erase it */ hdc = GetDC(td->w); SetBkColor(hdc, RGB(0, 0, 0)); SelectObject(hdc, td->font_id); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); ReleaseDC(td->w, hdc); /* Success */ return 0; } /* * Hack -- make a noise */ static errr Term_xtra_win_noise(void) { MessageBeep(MB_ICONASTERISK); return (0); } /* * Hack -- make a sound */ static errr Term_xtra_win_sound(int v) { #ifdef USE_SOUND int i; char buf[1024]; #endif /* USE_SOUND */ /* Sound disabled */ if (!use_sound) return (1); /* Illegal sound */ if ((v < 0) || (v >= SOUND_MAX)) return (1); #ifdef USE_SOUND /* Count the samples */ for (i = 0; i < SAMPLE_MAX; i++) { if (!sound_file[v][i]) break; } /* No sample */ if (i == 0) return (1); /* Build the path */ path_make(buf, ANGBAND_DIR_XTRA_SOUND, sound_file[v][Rand_simple(i)]); #ifdef WIN32 /* Play the sound, catch errors */ return (PlaySound(buf, 0, SND_FILENAME | SND_ASYNC)); #else /* WIN32 */ /* Play the sound, catch errors */ return (sndPlaySound(buf, SND_ASYNC)); #endif /* WIN32 */ #else /* USE_SOUND */ /* Oops */ return (1); #endif /* USE_SOUND */ } /* * Delay for "x" milliseconds */ static int Term_xtra_win_delay(int v) { #ifdef WIN32 /* Sleep */ if (v > 0) Sleep(v); #else /* WIN32 */ DWORD t; MSG msg; /* Final count */ t = GetTickCount() + v; /* Wait for it */ while (GetTickCount() < t) { /* Handle messages */ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #endif /* WIN32 */ /* Success */ return (0); } /* * Do a "special thing" */ static errr Term_xtra_win(int n, int v) { /* Handle a subset of the legal requests */ switch (n) { /* Make a bell sound */ case TERM_XTRA_NOISE: { return (Term_xtra_win_noise()); } /* Make a special sound */ case TERM_XTRA_SOUND: { return (Term_xtra_win_sound(v)); } /* Process random events */ case TERM_XTRA_BORED: { return (Term_xtra_win_event(0)); } /* Process an event */ case TERM_XTRA_EVENT: { return (Term_xtra_win_event(v)); } /* Flush all events */ case TERM_XTRA_FLUSH: { return (Term_xtra_win_flush()); } /* React to global changes */ case TERM_XTRA_REACT: { return (Term_xtra_win_react()); } /* Delay for some milliseconds */ case TERM_XTRA_DELAY: { return (Term_xtra_win_delay(v)); } } /* Oops */ return 1; } #if 0 /* * Find the square a particular pixel is part of. */ static void pixel_to_square(int *x, int *y, int ox, int oy) { term_data *td = (term_data*)(Term->data); if (td->map_active) { (*x) = (ox - td->size_ow1) / td->map_tile_wid; (*y) = (oy - td->size_oh1) / td->map_tile_hgt; } else { (*x) = (ox - td->size_ow1) / td->tile_wid; (*y) = (oy - td->size_oh1) / td->tile_hgt; if ((use_bigtile) && ((*y) >= Term->scr->big_y1) && ((*y) <= Term->scr->big_y2) && ((*x) >= Term->scr->big_x1)) { (*x) -= ((*x) - Term->scr->big_x1 + 1) / 2; } } } #endif /* 0 */ /* * Find the pixel at the top-left corner of a square. */ static void square_to_pixel(int *x, int *y, int ox, int oy) { term_data *td = (term_data*)(Term->data); if (td->map_active) { (*x) = ox * td->map_tile_wid + td->size_ow1; (*y) = oy * td->map_tile_hgt + td->size_oh1; } else { (*y) = oy * td->tile_hgt + td->size_oh1; if ((use_bigtile) && (oy >= Term->scr->big_y1) && (oy <= Term->scr->big_y2) && (ox > Term->scr->big_x1)) { (*x) = ox * td->tile_wid * 2 + td->size_ow1 - Term->scr->big_x1 * td->tile_wid; } else { (*x) = ox * td->tile_wid + td->size_ow1; } } } /* * Low level graphics (Assumes valid input). * * Draw a "cursor" at (x,y), using a "yellow box". */ static errr Term_curs_win(int x, int y) { term_data *td = (term_data*)(Term->data); RECT rc; HDC hdc; int x1, y1; int tile_wid, tile_hgt; /* Top left hand corner */ square_to_pixel(&x1, &y1, x, y); /* Frame the grid */ rc.left = x1; rc.top = y1; if (td->map_active) { tile_wid = td->map_tile_wid; tile_hgt = td->map_tile_hgt; rc.right = rc.left + tile_wid; } else { tile_wid = td->tile_wid; tile_hgt = td->tile_hgt; if (is_bigtiled(x, y)) { rc.right = rc.left + tile_wid * 2; } else { rc.right = rc.left + tile_wid; } } rc.bottom = rc.top + tile_hgt; /* Cursor is done as a yellow "box" */ hdc = GetDC(td->w); FrameRect(hdc, &rc, hbrYellow); ReleaseDC(td->w, hdc); /* Success */ return 0; } /* * * Erase a "block" of "n" characters starting at (x,y). */ static errr Term_wipe_win(int x, int y, int n) { term_data *td = (term_data*)(Term->data); HDC hdc; RECT rc; int x1, y1, x2, y2; /*** Find the dimensions ***/ square_to_pixel(&x1, &y1, x, y); square_to_pixel(&x2, &y2, x + n, y); /* Rectangle to erase in client coords */ rc.left = x1; rc.right = x2; rc.top = y1; rc.bottom = y1 + td->tile_hgt; hdc = GetDC(td->w); SetBkColor(hdc, RGB(0, 0, 0)); SelectObject(hdc, td->font_id); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); ReleaseDC(td->w, hdc); /* Success */ return 0; } /* * Low level graphics. Assumes valid input. * * Draw several ("n") chars, with an attr, at a given location. * * All "graphic" data is handled by "Term_pict_win()", below. * * One would think there is a more efficient method for telling a window * what color it should be using to draw with, but perhaps simply changing * it every time is not too inefficient. XXX XXX XXX */ static errr Term_text_win(int cx, int cy, int n, byte a, cptr s) { term_data *td = (term_data*)(Term->data); RECT rc; HDC hdc; int i; int x, y; /* Acquire DC */ hdc = GetDC(td->w); /* Background color */ SetBkColor(hdc, RGB(0, 0, 0)); /* Foreground color */ if (colors16) { SetTextColor(hdc, PALETTEINDEX(win_pal[a])); } else if (paletted) { SetTextColor(hdc, win_clr[a&0x0F]); } else { SetTextColor(hdc, win_clr[a]); } /* Use the font */ SelectObject(hdc, td->font_id); /* Erase complete rectangle */ Term_wipe_win(cx, cy, n); /* Get top left corner */ square_to_pixel(&x, &y, cx, cy); rc.top = y + ((td->tile_hgt - td->font_hgt) / 2); rc.bottom = rc.top + td->font_hgt; /* Dump each character */ for (i = 0; i < n; i++) { if (is_bigtiled(cx + i, cy)) { rc.left = x + ((td->tile_wid - td->font_wid) / 2); rc.right = rc.left + td->font_wid; /* Dump the text */ ExtTextOut(hdc, rc.left, rc.top, 0, &rc, s+i, 1, NULL); /* Advance */ x += td->tile_wid * 2; } else { rc.left = x + ((td->tile_wid - td->font_wid) / 2); rc.right = rc.left + td->font_wid; /* Dump the text */ ExtTextOut(hdc, rc.left, rc.top, 0, &rc, s+i, 1, NULL); /* Advance */ x += td->tile_wid; } } /* Release DC */ ReleaseDC(td->w, hdc); /* Success */ return 0; } /* * Low level graphics. Assumes valid input. * * Draw an array of "special" attr/char pairs at the given location. * * We use the "Term_pict_win()" function for "graphic" data, which are * encoded by setting the "high-bits" of both the "attr" and the "char" * data. We use the "attr" to represent the "row" of the main bitmap, * and the "char" to represent the "col" of the main bitmap. The use * of this function is induced by the "higher_pict" flag. * * If "graphics" is not available, we simply "wipe" the given grids. */ static errr Term_pict_win(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { term_data *td = (term_data*)(Term->data); #ifdef USE_GRAPHICS int i; int x1, y1, w1, h1; int x2, y2, w2, h2, tw2; int x3, y3; HDC hdcMask; HDC hdc; HDC hdcSrc; HBITMAP hbmSrcOld; /* Paranoia */ if (!use_graphics) { /* Erase the grids */ return (Term_wipe_win(x, y, n)); } /* Size of bitmap cell */ w1 = infGraph.CellWidth; h1 = infGraph.CellHeight; /* Size of window cell */ if (td->map_active) { w2 = td->map_tile_wid; h2 = td->map_tile_hgt; tw2 = w2; } else { w2 = td->tile_wid; h2 = td->tile_hgt; /* big tile mode */ if (use_bigtile) tw2 = 2 * w2; else tw2 = w2; } /* Starting point */ square_to_pixel(&x2, &y2, x, y); /* Info */ hdc = GetDC(td->w); /* More info */ hdcSrc = CreateCompatibleDC(hdc); hbmSrcOld = SelectObject(hdcSrc, infGraph.hBitmap); if ((arg_graphics == GRAPHICS_ADAM_BOLT) || (arg_graphics == GRAPHICS_DAVID_GERVAIS)) { hdcMask = CreateCompatibleDC(hdc); SelectObject(hdcMask, infMask.hBitmap); } else { hdcMask = NULL; } /* Draw attr/char pairs */ for (i = 0; i < n; i++, x2 += w2) { byte a = ap[i]; char c = cp[i]; /* Extract picture */ int row = (a & 0x7F); int col = (c & 0x7F); /* Location of bitmap cell */ x1 = col * w1; y1 = row * h1; if ((arg_graphics == GRAPHICS_ADAM_BOLT) || (arg_graphics == GRAPHICS_DAVID_GERVAIS)) { x3 = (tcp[i] & 0x7F) * w1; y3 = (tap[i] & 0x7F) * h1; /* Perfect size */ if ((w1 == tw2) && (h1 == h2)) { /* Copy the terrain picture from the bitmap to the window */ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, SRCCOPY); /* Mask out the tile */ BitBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, SRCAND); /* Draw the tile */ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCPAINT); } /* Need to stretch */ else { /* Set the correct mode for stretching the tiles */ SetStretchBltMode(hdc, COLORONCOLOR); /* Copy the terrain picture from the bitmap to the window */ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, w1, h1, SRCCOPY); /* Only draw if terrain and overlay are different */ if ((x1 != x3) || (y1 != y3)) { /* Mask out the tile */ StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, w1, h1, SRCAND); /* Draw the tile */ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCPAINT); } } } else { /* Perfect size */ if ((w1 == tw2) && (h1 == h2)) { /* Copy the picture from the bitmap to the window */ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCCOPY); } /* Need to stretch */ else { /* Set the correct mode for stretching the tiles */ SetStretchBltMode(hdc, COLORONCOLOR); /* Copy the picture from the bitmap to the window */ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCCOPY); } } } /* Release */ SelectObject(hdcSrc, hbmSrcOld); DeleteDC(hdcSrc); if ((arg_graphics == GRAPHICS_ADAM_BOLT) || (arg_graphics == GRAPHICS_DAVID_GERVAIS)) { /* Release */ SelectObject(hdcMask, hbmSrcOld); DeleteDC(hdcMask); } /* Release */ ReleaseDC(td->w, hdc); #else /* USE_GRAPHICS */ /* Just erase this grid */ return (Term_wipe_win(x, y, n)); #endif /* USE_GRAPHICS */ /* Success */ return 0; } static int player_x; static int player_y; /* * Notice that the player has moved */ static void win_player_move(int x, int y, vptr dummy) { /* Hack - ignore parameter */ (void) dummy; /* Save for later */ player_x = x; player_y = y; } /* * Save the information so we can access it later */ static void win_map_info(map_block *mb_ptr, const term_map *map, vptr dummy) { /* Hack -- ignore parameter */ (void) dummy; /* Store feature code for later */ if (map->terrain) { mb_ptr->a = f_info[map->terrain].x_attr; mb_ptr->c = f_info[map->terrain].x_char; } else { mb_ptr->a = 0; mb_ptr->c = 0; } } static void windows_map_aux(void) { term_data *td = &data[0]; map_block *mb_ptr; byte a; char c; int x, y; int min_x, min_y, max_x, max_y; /* Get size */ td->map_tile_wid = (td->tile_wid * td->cols) / MAX_WID; td->map_tile_hgt = (td->tile_hgt * td->rows) / MAX_HGT; /* Get bounds */ min_x = player_x - MAX_WID / 2; max_x = min_x + MAX_WID; min_y = player_y - MAX_HGT / 2; max_y = player_y + MAX_HGT; /* Draw the map */ for (x = min_x; x < max_x; x++) { for (y = min_y; y < max_y; y++) { if (map_in_bounds(x, y)) { mb_ptr = map_loc(x, y); /* Attr / char */ a = mb_ptr->a; c = mb_ptr->c; /* Ignore non-graphics */ if ((a & 0x80) && (c & 0x80)) { Term_pict_win(x - min_x, y - min_y, 1, &a, &c, &a, &c); } } } } /* Hilite the player */ Term_curs_win(player_x - min_x, player_y - min_y); } /* * MEGA_HACK - Display a graphical map of the dungeon. */ static void windows_map(void) { term_data *td = &data[0]; char ch; /* Only in graphics mode since the fonts can't be scaled */ if (!use_graphics) return; /* Prevent various menu-actions from working */ initialized = FALSE; /* Clear screen */ Term_xtra_win_clear(); td->map_active = TRUE; /* Draw the map */ windows_map_aux(); /* Wait for a keypress, flush key buffer */ Term_inkey(&ch, TRUE, TRUE); Term_flush(); /* Switch off the map display */ td->map_active = FALSE; /* Restore screen */ Term_xtra_win_clear(); Term_redraw(); /* We are ready again */ initialized = TRUE; } /*** Other routines ***/ /* * Create and initialize a "term_data" given a title */ static void term_data_link(term_data *td) { term *t = &td->t; /* Initialize the term */ term_init(t, td->cols, td->rows, td->keys); /* Use a "software" cursor */ t->soft_cursor = TRUE; /* Use "Term_pict" for "graphic" data */ t->higher_pict = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; #if 0 /* Prepare the init/nuke hooks */ t->init_hook = Term_init_win; t->nuke_hook = Term_nuke_win; #endif /* 0 */ /* Prepare the template hooks */ t->user_hook = Term_user_win; t->xtra_hook = Term_xtra_win; t->curs_hook = Term_curs_win; t->wipe_hook = Term_wipe_win; t->text_hook = Term_text_win; t->pict_hook = Term_pict_win; /* Remember where we came from */ t->data = (vptr)(td); } /* * Create the windows * * First, instantiate the "default" values, then read the "ini_file" * to over-ride selected values, then create the windows, and fonts. * * Must use SW_SHOW not SW_SHOWNA, since on 256 color display * must make active to realize the palette. XXX XXX XXX */ static void init_windows(void) { int i; term_data *td; char buf[1024]; /* Main window */ td = &data[0]; WIPE(td, term_data); td->s = angband_term_name[0]; td->keys = 1024; td->rows = 24; td->cols = 80; td->visible = TRUE; td->size_ow1 = 2; td->size_ow2 = 2; td->size_oh1 = 2; td->size_oh2 = 2; td->pos_x = 30; td->pos_y = 20; /* Sub windows */ for (i = 1; i < MAX_TERM_DATA; i++) { td = &data[i]; WIPE(td, term_data); td->s = angband_term_name[i]; td->keys = 16; td->rows = 24; td->cols = 80; td->visible = FALSE; td->size_ow1 = 1; td->size_ow2 = 1; td->size_oh1 = 1; td->size_oh2 = 1; td->pos_x = (7 - i) * 30; td->pos_y = (7 - i) * 20; } /* Load prefs */ load_prefs(); /* Main window (need these before term_getsize gets called) */ td = &data[0]; td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_VISIBLE); if (td->maximized) td->dwStyle |= WS_MAXIMIZE; td->dwExStyle = 0; td->visible = TRUE; /* Sub windows (need these before term_getsize gets called) */ for (i = 1; i < MAX_TERM_DATA; i++) { td = &data[i]; td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU | WS_CAPTION); td->dwExStyle = (WS_EX_TOOLWINDOW); } /* All windows */ for (i = 0; i < MAX_TERM_DATA; i++) { td = &data[i]; /* Access the standard font file */ path_make(buf, ANGBAND_DIR_XTRA_FONT, td->font_want); /* Activate the chosen font */ if (term_force_font(td, buf)) { /* Access the standard font file */ path_make(buf, ANGBAND_DIR_XTRA_FONT, "8X13.FON"); /* Force the use of that font */ (void)term_force_font(td, buf); /* Oops */ td->tile_wid = 8; td->tile_hgt = 13; } /* Analyze the font */ term_getsize(td); /* Resize the window */ term_window_resize(td); } /* Sub windows (reverse order) */ for (i = MAX_TERM_DATA - 1; i >= 1; --i) { td = &data[i]; my_td = td; td->w = CreateWindowEx(td->dwExStyle, AngList, td->s, td->dwStyle, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, HWND_DESKTOP, NULL, hInstance, NULL); my_td = NULL; if (!td->w) quit("Failed to create sub-window"); if (td->visible) { td->size_hack = TRUE; ShowWindow(td->w, SW_SHOW); td->size_hack = FALSE; } term_data_link(td); angband_term[i] = &td->t; if (td->visible) { /* Activate the window */ SetActiveWindow(td->w); /* Bring window to top */ SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } } /* Main window */ td = &data[0]; /* Main window */ my_td = td; td->w = CreateWindowEx(td->dwExStyle, AppName, td->s, td->dwStyle, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, HWND_DESKTOP, NULL, hInstance, NULL); my_td = NULL; if (!td->w) quit_fmt("Failed to create %s window", VERSION_NAME); term_data_link(td); term_screen = &td->t; /* Activate the main window */ SetActiveWindow(td->w); /* Bring main window back to top */ SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); #ifdef SUPPORT_GAMMA if (gamma_correction > 0) build_gamma_table(gamma_correction); #endif /* SUPPORT_GAMMA */ /* New palette XXX XXX XXX */ (void)new_palette(); /* Create a "brush" for drawing the "cursor" */ hbrYellow = CreateSolidBrush(win_clr[TERM_YELLOW]); /* Process pending messages */ (void)Term_xtra_win_flush(); } #ifdef USE_SAVER /* * Stop the screensaver */ static void stop_screensaver(void) { if (screensaver) SendMessage(data[0].w, WM_CLOSE, 0, 0); else SendMessage(data[0].w, WM_COMMAND, IDM_OPTIONS_SAVER, 0); } #endif /* USE_SAVER */ /* * Prepare the menus */ static void setup_menus(void) { int i; HMENU hm = GetMenu(data[0].w); #ifdef USE_SAVER main_menu = hm; #endif /* USE_SAVER */ /* Menu "File", Disable all */ EnableMenuItem(hm, IDM_FILE_NEW, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_FILE_OPEN, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_FILE_SAVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_FILE_EXIT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_FILE_SCORE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); /* No character available */ if (!character_generated) { /* Menu "File", Item "New" */ EnableMenuItem(hm, IDM_FILE_NEW, MF_BYCOMMAND | MF_ENABLED); /* Menu "File", Item "Open" */ EnableMenuItem(hm, IDM_FILE_OPEN, MF_BYCOMMAND | MF_ENABLED); } /* A character available */ if (game_in_progress && character_generated && p_ptr->cmd.inkey_flag) { /* Menu "File", Item "Save" */ EnableMenuItem(hm, IDM_FILE_SAVE, MF_BYCOMMAND | MF_ENABLED); } if (!game_in_progress || !character_generated || (p_ptr->cmd.inkey_flag)) { /* Menu "File", Item "Exit" */ EnableMenuItem(hm, IDM_FILE_EXIT, MF_BYCOMMAND | MF_ENABLED); } if (initialized) { /* Menu "File", Item "Show Scores" */ EnableMenuItem(hm, IDM_FILE_SCORE, MF_BYCOMMAND | MF_ENABLED); } /* Menu "Window::Visibility" */ for (i = 0; i < MAX_TERM_DATA; i++) { EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); CheckMenuItem(hm, IDM_WINDOW_VIS_0 + i, (data[i].visible ? MF_CHECKED : MF_UNCHECKED)); EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i, MF_BYCOMMAND | MF_ENABLED); } /* Menu "Window::Font" */ for (i = 0; i < MAX_TERM_DATA; i++) { EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); if (data[i].visible) { EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i, MF_BYCOMMAND | MF_ENABLED); } } /* Menu "Window::Increase Tile Width" */ for (i = 0; i < MAX_TERM_DATA; i++) { EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); if (data[i].visible) { EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i, MF_BYCOMMAND | MF_ENABLED); } } /* Menu "Window::Decrease Tile Width" */ for (i = 0; i < MAX_TERM_DATA; i++) { EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); if (data[i].visible) { EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i, MF_BYCOMMAND | MF_ENABLED); } } /* Menu "Window::Increase Tile Height" */ for (i = 0; i < MAX_TERM_DATA; i++) { EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); if (data[i].visible) { EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i, MF_BYCOMMAND | MF_ENABLED); } } /* Menu "Window::Decrease Tile Height" */ for (i = 0; i < MAX_TERM_DATA; i++) { EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); if (data[i].visible) { EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i, MF_BYCOMMAND | MF_ENABLED); } } /* Menu "Options", disable all */ EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_NONE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_OLD, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_ADAM, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_DAVID, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_BIGTILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_SOUND, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_SAVER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); EnableMenuItem(hm, IDM_OPTIONS_LOW_PRIORITY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); /* Menu "Options", Item "Map" */ if (p_ptr->cmd.inkey_flag && initialized && (use_graphics != GRAPHICS_NONE)) EnableMenuItem(GetMenu(data[0].w), IDM_OPTIONS_MAP, MF_BYCOMMAND | MF_ENABLED); else EnableMenuItem(GetMenu(data[0].w), IDM_OPTIONS_MAP, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); /* Menu "Options", update all */ CheckMenuItem(hm, IDM_OPTIONS_GRAPHICS_NONE, (arg_graphics == GRAPHICS_NONE ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(hm, IDM_OPTIONS_GRAPHICS_OLD, (arg_graphics == GRAPHICS_ORIGINAL ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(hm, IDM_OPTIONS_GRAPHICS_ADAM, (arg_graphics == GRAPHICS_ADAM_BOLT ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(hm, IDM_OPTIONS_GRAPHICS_DAVID, (arg_graphics == GRAPHICS_DAVID_GERVAIS ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(hm, IDM_OPTIONS_BIGTILE, (use_bigtile ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(hm, IDM_OPTIONS_SOUND, (arg_sound ? MF_CHECKED : MF_UNCHECKED)); #ifdef USE_SAVER CheckMenuItem(hm, IDM_OPTIONS_SAVER, (hwndSaver ? MF_CHECKED : MF_UNCHECKED)); #endif /* USE_SAVER */ CheckMenuItem(hm, IDM_OPTIONS_LOW_PRIORITY, (low_priority ? MF_CHECKED : MF_UNCHECKED)); #ifdef USE_GRAPHICS if (initialized && p_ptr->cmd.inkey_flag) { EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_NONE, MF_ENABLED); EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_OLD, MF_ENABLED); EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_ADAM, MF_ENABLED); EnableMenuItem(hm, IDM_OPTIONS_GRAPHICS_DAVID, MF_ENABLED); EnableMenuItem(hm, IDM_OPTIONS_BIGTILE, MF_ENABLED); } #endif /* USE_GRAPHICS */ #ifdef USE_SOUND if (initialized && p_ptr->cmd.inkey_flag) { /* Menu "Options", Item "Sound" */ EnableMenuItem(hm, IDM_OPTIONS_SOUND, MF_ENABLED); } #endif /* USE_SOUND */ #ifdef USE_SAVER /* Menu "Options", Item "ScreenSaver" */ EnableMenuItem(hm, IDM_OPTIONS_SAVER, MF_BYCOMMAND | MF_ENABLED); #endif /* USE_SAVER */ EnableMenuItem(hm, IDM_OPTIONS_LOW_PRIORITY, MF_BYCOMMAND | MF_ENABLED); } /* * Check for double clicked (or dragged) savefile * * Apparently, Windows copies the entire filename into the first * piece of the "command line string". Perhaps we should extract * the "basename" of that filename and append it to the "save" dir. */ static void check_for_save_file(LPSTR cmd_line) { char *s, *p; /* First arg */ s = cmd_line; /* No args */ if (!s || !*s) return; /* Next arg */ p = strchr(s, ' '); /* Tokenize */ if (p) *p = '\0'; /* Extract filename */ *savefile = '\0'; strncat(savefile, s, sizeof(savefile) - 1); /* Validate the file */ validate_file(savefile); /* Game in progress */ game_in_progress = TRUE; Term_fresh(); /* Play game */ play_game(FALSE); /* Quit */ quit(NULL); } #ifdef USE_SAVER #ifdef ALLOW_BORG /* * Hook into the inkey() function so that flushing keypresses * doesn't affect us. * * ToDo: Try to implement recording and playing back of games * by saving/reading the keypresses to/from a file. Note that * interrupting certain actions (resting, running, and other * repeated actions) would mess that up, so this would have to * be switched off when recording. */ extern char (*inkey_hack)(int flush_first); static char screensaver_inkey_hack_buffer[1024]; static char screensaver_inkey_hack(int flush_first) { static int screensaver_inkey_hack_index = 0; if (screensaver_inkey_hack_index < sizeof(screensaver_inkey_hack_buffer)) return (screensaver_inkey_hack_buffer[screensaver_inkey_hack_index++]); else return ESCAPE; } #endif /* ALLOW_BORG */ /* * Start the screensaver */ static void start_screensaver(void) { bool file_exists; #ifdef ALLOW_BORG int i, j; #endif /* ALLOW_BORG */ /* Set the name for process_player_name() */ strnfmt(op_ptr->full_name, sizeof(op_ptr->full_name), "%s", saverfilename); /* Set 'savefile' to a valid name */ process_player_name(TRUE); /* Does the savefile already exist? */ file_exists = check_file(savefile); /* Don't try to load a non-existant savefile */ if (!file_exists) savefile[0] = '\0'; /* Game in progress */ game_in_progress = TRUE; Term_fresh(); /* Screensaver mode on */ SendMessage(data[0].w, WM_COMMAND, IDM_OPTIONS_SAVER, 0); /* Low priority */ SendMessage(data[0].w, WM_COMMAND, IDM_OPTIONS_LOW_PRIORITY, 0); #ifdef ALLOW_BORG /* * MegaHack - Try to start the Borg. * * The simulated keypresses will be processed when play_game() * is called. */ inkey_hack = screensaver_inkey_hack; j = 0; /* * If no savefile is present or then go through the steps necessary * to create a random character. If a savefile already is present * then the simulated keypresses will either clean away any [-more-] * prompts (if the character is alive), or create a new random * character. * * Luckily it's possible to send the same keypresses no matter if * the character is alive, dead, or not even yet created. */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Gender */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Race */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Class */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Modify options */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Reroll */ if (!file_exists) { /* Savefile name */ int n = strlen(saverfilename); for (i = 0; i < n; i++) screensaver_inkey_hack_buffer[j++] = saverfilename[i]; } screensaver_inkey_hack_buffer[j++] = '\r'; /* Return */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Character info */ /* * Make sure the "verify_special" options is off, so that we can * get into Borg mode without confirmation. */ screensaver_inkey_hack_buffer[j++] = '='; /* Enter options screen */ screensaver_inkey_hack_buffer[j++] = '2'; /* Disturbance options */ /* Cursor down to "verify_special" */ for (i = 0; i < 10; i++) screensaver_inkey_hack_buffer[j++] = '2'; screensaver_inkey_hack_buffer[j++] = 'n'; /* Switch off "verify_special" */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Leave disturbance options */ /* * Make sure the "cheat_live" option is set, so that the Borg can * automatically restart. */ screensaver_inkey_hack_buffer[j++] = '6'; /* Cheat options */ /* Cursor down to "cheat live" */ for (i = 0; i < OPT_cheat_live - OPT_CHEAT; i++) screensaver_inkey_hack_buffer[j++] = '2'; screensaver_inkey_hack_buffer[j++] = 'y'; /* Switch on "cheat_live" */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Leave cheat options */ screensaver_inkey_hack_buffer[j++] = ESCAPE; /* Leave options */ /* * Now start the Borg! */ screensaver_inkey_hack_buffer[j++] = KTRL('Z'); /* Enter borgmode */ screensaver_inkey_hack_buffer[j++] = 'z'; /* Run Borg */ #endif /* ALLOW_BORG */ /* Play game */ play_game((bool)!file_exists); } #endif /* USE_SAVER */ /* * Display a help file */ static void display_help(cptr filename) { char tmp[1024]; path_make(tmp, ANGBAND_DIR_XTRA_HELP, filename); if (check_file(tmp)) { #ifdef HTML_HELP HtmlHelp(data[0].w, tmp, HH_DISPLAY_TOPIC, 0); #else /* HTML_HELP */ char buf[1024]; strnfmt(buf, 1024, "winhelp.exe %s", tmp); WinExec(buf, SW_NORMAL); #endif /* HTML_HELP */ } else { plog_fmt("Cannot find help file: %s", tmp); plog("Use the online help files instead."); } } /* * Process a menu command */ static void process_menus(WORD wCmd) { int i; term_data *td; OPENFILENAME ofn; /* Analyze */ switch (wCmd) { /* New game */ case IDM_FILE_NEW: { if (!initialized) { plog("You cannot do that yet..."); } else if (game_in_progress) { plog("You can't start a new game while you're still playing!"); } else { game_in_progress = TRUE; Term_flush(); play_game(TRUE); quit(NULL); } break; } /* Open game */ case IDM_FILE_OPEN: { if (!initialized) { plog("You cannot do that yet..."); } else if (game_in_progress) { plog("You can't open a new game while you're still playing!"); } else { memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = data[0].w; ofn.lpstrFilter = "Save Files (*.)\0*\0"; ofn.nFilterIndex = 1; ofn.lpstrFile = savefile; ofn.nMaxFile = 1024; ofn.lpstrInitialDir = ANGBAND_DIR_SAVE; ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; if (GetOpenFileName(&ofn)) { /* Load 'savefile' */ validate_file(savefile); game_in_progress = TRUE; Term_flush(); play_game(FALSE); quit(NULL); } } break; } /* Save game */ case IDM_FILE_SAVE: { if (game_in_progress && character_generated && p_ptr->cmd.inkey_flag) { /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifdef ZANGBAND do_cmd_save_game(FALSE); #else /* ZANGBAND */ do_cmd_save_game(); #endif /* ZANGBAND */ } else { /* Paranoia */ plog("You may not do that right now."); } break; } /* Show scores */ case IDM_FILE_SCORE: { if (!initialized) { plog("You may not do that right now."); break; } /* Show score */ ingame_score(&initialized, game_in_progress); break; } /* Exit */ case IDM_FILE_EXIT: { if (game_in_progress && character_generated) { /* Paranoia */ if (!p_ptr->cmd.inkey_flag) { plog("You may not do that right now."); break; } /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifdef ZANGBAND do_cmd_save_game(FALSE); #else /* ZANGBAND */ do_cmd_save_game(); #endif /* ZANGBAND */ } quit(NULL); break; } case IDM_WINDOW_VIS_0: { plog("You are not allowed to do that!"); break; } /* Window visibility */ case IDM_WINDOW_VIS_1: case IDM_WINDOW_VIS_2: case IDM_WINDOW_VIS_3: case IDM_WINDOW_VIS_4: case IDM_WINDOW_VIS_5: case IDM_WINDOW_VIS_6: case IDM_WINDOW_VIS_7: { i = wCmd - IDM_WINDOW_VIS_0; if ((i < 0) || (i >= MAX_TERM_DATA)) break; td = &data[i]; if (!td->visible) { td->visible = TRUE; ShowWindow(td->w, SW_SHOW); term_data_redraw(td); } else { td->visible = FALSE; ShowWindow(td->w, SW_HIDE); } break; } /* Window fonts */ case IDM_WINDOW_FONT_0: case IDM_WINDOW_FONT_1: case IDM_WINDOW_FONT_2: case IDM_WINDOW_FONT_3: case IDM_WINDOW_FONT_4: case IDM_WINDOW_FONT_5: case IDM_WINDOW_FONT_6: case IDM_WINDOW_FONT_7: { i = wCmd - IDM_WINDOW_FONT_0; if ((i < 0) || (i >= MAX_TERM_DATA)) break; td = &data[i]; term_change_font(td); break; } /* Increase Tile Width */ case IDM_WINDOW_I_WID_0: case IDM_WINDOW_I_WID_1: case IDM_WINDOW_I_WID_2: case IDM_WINDOW_I_WID_3: case IDM_WINDOW_I_WID_4: case IDM_WINDOW_I_WID_5: case IDM_WINDOW_I_WID_6: case IDM_WINDOW_I_WID_7: { i = wCmd - IDM_WINDOW_I_WID_0; if ((i < 0) || (i >= MAX_TERM_DATA)) break; td = &data[i]; td->tile_wid += 1; term_getsize(td); term_window_resize(td); break; } /* Decrease Tile Height */ case IDM_WINDOW_D_WID_0: case IDM_WINDOW_D_WID_1: case IDM_WINDOW_D_WID_2: case IDM_WINDOW_D_WID_3: case IDM_WINDOW_D_WID_4: case IDM_WINDOW_D_WID_5: case IDM_WINDOW_D_WID_6: case IDM_WINDOW_D_WID_7: { i = wCmd - IDM_WINDOW_D_WID_0; if ((i < 0) || (i >= MAX_TERM_DATA)) break; td = &data[i]; td->tile_wid -= 1; term_getsize(td); term_window_resize(td); break; } /* Increase Tile Height */ case IDM_WINDOW_I_HGT_0: case IDM_WINDOW_I_HGT_1: case IDM_WINDOW_I_HGT_2: case IDM_WINDOW_I_HGT_3: case IDM_WINDOW_I_HGT_4: case IDM_WINDOW_I_HGT_5: case IDM_WINDOW_I_HGT_6: case IDM_WINDOW_I_HGT_7: { i = wCmd - IDM_WINDOW_I_HGT_0; if ((i < 0) || (i >= MAX_TERM_DATA)) break; td = &data[i]; td->tile_hgt += 1; term_getsize(td); term_window_resize(td); break; } /* Decrease Tile Height */ case IDM_WINDOW_D_HGT_0: case IDM_WINDOW_D_HGT_1: case IDM_WINDOW_D_HGT_2: case IDM_WINDOW_D_HGT_3: case IDM_WINDOW_D_HGT_4: case IDM_WINDOW_D_HGT_5: case IDM_WINDOW_D_HGT_6: case IDM_WINDOW_D_HGT_7: { i = wCmd - IDM_WINDOW_D_HGT_0; if ((i < 0) || (i >= MAX_TERM_DATA)) break; td = &data[i]; td->tile_hgt -= 1; term_getsize(td); term_window_resize(td); break; } case IDM_OPTIONS_GRAPHICS_NONE: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } /* Toggle "arg_graphics" */ if (arg_graphics != GRAPHICS_NONE) { arg_graphics = GRAPHICS_NONE; /* React to changes */ Term_xtra_win_react(); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); } break; } case IDM_OPTIONS_GRAPHICS_OLD: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } /* Toggle "arg_graphics" */ if (arg_graphics != GRAPHICS_ORIGINAL) { arg_graphics = GRAPHICS_ORIGINAL; /* React to changes */ Term_xtra_win_react(); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); } break; } case IDM_OPTIONS_GRAPHICS_ADAM: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } /* Toggle "arg_graphics" */ if (arg_graphics != GRAPHICS_ADAM_BOLT) { arg_graphics = GRAPHICS_ADAM_BOLT; /* React to changes */ Term_xtra_win_react(); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); } break; } case IDM_OPTIONS_GRAPHICS_DAVID: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } /* Toggle "arg_graphics" */ if (arg_graphics != GRAPHICS_DAVID_GERVAIS) { arg_graphics = GRAPHICS_DAVID_GERVAIS; /* React to changes */ Term_xtra_win_react(); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); } break; } case IDM_OPTIONS_BIGTILE: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } /* Toggle "use_bigtile" */ toggle_bigtile(); break; } case IDM_OPTIONS_SOUND: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } /* Toggle "arg_sound" */ arg_sound = !arg_sound; /* React to changes */ Term_xtra_win_react(); /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } #ifdef USE_SAVER case IDM_OPTIONS_SAVER: { if (hwndSaver) { DestroyWindow(hwndSaver); hwndSaver = NULL; screensaver_active = FALSE; /* Switch main menu back on */ SetMenu(data[0].w, main_menu); for (i = MAX_TERM_DATA - 1; i >= 0; --i) { td = &data[i]; if (td->visible) { /* Turn the Windows back to normal */ SetWindowLong(td->w, GWL_STYLE, td->dwStyle); /* Push the window to the top */ SetWindowPos(td->w, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); } } ShowCursor(TRUE); } else { /* Create a screen saver window */ hwndSaver = CreateWindowEx(WS_EX_TOPMOST, "WindowsScreenSaverClass", "Angband Screensaver", WS_POPUP | WS_MAXIMIZE | WS_VISIBLE, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL); if (hwndSaver) { for (i = MAX_TERM_DATA - 1; i >= 0; --i) { td = &data[i]; if (td->visible) { /* Switch off border and titlebar */ SetWindowLong(td->w, GWL_STYLE, WS_VISIBLE); /* Switch off menu */ SetMenu(td->w, NULL); /* Push the window to the top */ SetWindowPos(td->w, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); } } ShowCursor(FALSE); screensaver_active = TRUE; } else { plog("Failed to create saver window"); } } break; } #endif /* USE_SAVER */ case IDM_OPTIONS_LOW_PRIORITY: { /* Lower or reset the priority of the current process */ if (low_priority) SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); else SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); /* Toggle priority */ low_priority = !low_priority; break; } case IDM_OPTIONS_MAP: { /* Paranoia */ if (!p_ptr->cmd.inkey_flag || !initialized) { plog("You may not do that right now."); break; } windows_map(); break; } case IDM_HELP_GENERAL: { display_help(HELP_GENERAL); break; } case IDM_HELP_SPOILERS: { display_help(HELP_SPOILERS); break; } } } /* * Redraw a section of a window */ static void handle_wm_paint(HWND hWnd) { int x1, y1, x2, y2; PAINTSTRUCT ps; term_data *td; /* Acquire proper "term_data" info */ td = (term_data *)GetWindowLong(hWnd, 0); BeginPaint(hWnd, &ps); if (td->map_active) { /* Redraw the map */ /* ToDo: Only redraw the necessary parts */ windows_map_aux(); } else { /* Get the area that should be updated (rounding up/down) */ /* ToDo: Take the window borders into account */ x1 = (ps.rcPaint.left / td->tile_wid) - 1; x2 = (ps.rcPaint.right / td->tile_wid) + 1; y1 = (ps.rcPaint.top / td->tile_hgt) - 1; y2 = (ps.rcPaint.bottom / td->tile_hgt) + 1; /* Redraw */ if (td) term_data_redraw_section(td, x1, y1, x2, y2); } EndPaint(hWnd, &ps); } static LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; term_data *td; int i; #ifdef USE_SAVER static int iMouse = 0; static WORD xMouse = 0; static WORD yMouse = 0; int dx, dy; #endif /* USE_SAVER */ /* Acquire proper "term_data" info */ td = (term_data *)GetWindowLong(hWnd, 0); /* Handle message */ switch (uMsg) { /* XXX XXX XXX */ case WM_NCCREATE: { SetWindowLong(hWnd, 0, (LONG)(my_td)); break; } /* XXX XXX XXX */ case WM_CREATE: { return 0; } case WM_GETMINMAXINFO: { MINMAXINFO FAR *lpmmi; RECT rc; lpmmi = (MINMAXINFO FAR *)lParam; /* this message was sent before WM_NCCREATE */ if (!td) return 1; /* Minimum window size is 80x24 */ rc.left = rc.top = 0; rc.right = rc.left + 80 * td->tile_wid + td->size_ow1 + td->size_ow2; rc.bottom = rc.top + 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1; /* Adjust */ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle); /* Save minimum size */ lpmmi->ptMinTrackSize.x = rc.right - rc.left; lpmmi->ptMinTrackSize.y = rc.bottom - rc.top; return 0; } case WM_PAINT: { handle_wm_paint(hWnd); return 0; } case WM_SYSKEYDOWN: case WM_KEYDOWN: { bool mc = FALSE; bool ms = FALSE; bool ma = FALSE; #ifdef USE_SAVER if (screensaver_active) { stop_screensaver(); return 0; } #endif /* USE_SAVER */ /* Extract the modifiers */ if (GetKeyState(VK_CONTROL) & 0x8000) mc = TRUE; if (GetKeyState(VK_SHIFT) & 0x8000) ms = TRUE; if (GetKeyState(VK_MENU) & 0x8000) ma = TRUE; /* Handle "special" keys */ if (special_key[(byte)(wParam)]) { /* Begin the macro trigger */ Term_keypress(31); /* Send the modifiers */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (ma) Term_keypress('A'); /* Extract "scan code" */ i = LOBYTE(HIWORD(lParam)); /* Introduce the scan code */ Term_keypress('x'); /* Encode the hexidecimal scan code */ Term_keypress(hexsym[i/16]); Term_keypress(hexsym[i%16]); /* End the macro trigger */ Term_keypress(13); return 0; } break; } case WM_CHAR: { Term_keypress(wParam); return 0; } #ifdef USE_SAVER case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: { if (screensaver_active) { stop_screensaver(); return 0; } break; } case WM_MOUSEMOVE: { if (!screensaver_active) break; if (iMouse) { dx = LOWORD(lParam) - xMouse; dy = HIWORD(lParam) - yMouse; if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; if ((dx > MOUSE_SENS) || (dy > MOUSE_SENS)) { stop_screensaver(); } } /* Save last location */ iMouse = 1; xMouse = LOWORD(lParam); yMouse = HIWORD(lParam); return 0; } #endif /* USE_SAVER */ case WM_INITMENU: { setup_menus(); return 0; } case WM_CLOSE: { if (game_in_progress && character_generated) { if (!p_ptr->cmd.inkey_flag) { plog("You may not do that right now."); return 0; } /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifdef ZANGBAND do_cmd_save_game(FALSE); #else /* ZANGBAND */ do_cmd_save_game(); #endif /* ZANGBAND */ } quit(NULL); return 0; } case WM_QUIT: { quit(NULL); return 0; } case WM_COMMAND: { process_menus(LOWORD(wParam)); return 0; } case WM_SIZE: { /* this message was sent before WM_NCCREATE */ if (!td) return 1; /* it was sent from inside CreateWindowEx */ if (!td->w) return 1; /* was sent from WM_SIZE */ if (td->size_hack) return 1; switch (wParam) { case SIZE_MINIMIZED: { /* Hide sub-windows */ for (i = 1; i < MAX_TERM_DATA; i++) { if (data[i].visible) ShowWindow(data[i].w, SW_HIDE); } return 0; } case SIZE_MAXIMIZED: { /* fall through XXX XXX XXX */ } case SIZE_RESTORED: { int cols = (LOWORD(lParam) - td->size_ow1) / td->tile_wid; int rows = (HIWORD(lParam) - td->size_oh1) / td->tile_hgt; /* New size */ if ((td->cols != cols) || (td->rows != rows)) { /* Save the new size */ td->cols = cols; td->rows = rows; /* Activate */ Term_activate(&td->t); /* Resize the term */ Term_resize(td->cols, td->rows); /* Redraw later */ InvalidateRect(td->w, NULL, TRUE); } td->size_hack = TRUE; /* Show sub-windows */ for (i = 1; i < MAX_TERM_DATA; i++) { if (data[i].visible) ShowWindow(data[i].w, SW_SHOW); } td->size_hack = FALSE; return 0; } } break; } case WM_PALETTECHANGED: { /* Ignore if palette change caused by itself */ if ((HWND)wParam == hWnd) return 0; /* Fall through... */ } case WM_QUERYNEWPALETTE: { if (!paletted) return 0; hdc = GetDC(hWnd); SelectPalette(hdc, hPal, FALSE); i = RealizePalette(hdc); /* if any palette entries changed, repaint the window. */ if (i) InvalidateRect(hWnd, NULL, TRUE); ReleaseDC(hWnd, hdc); return 0; } case WM_ACTIVATE: { if (wParam && !HIWORD(lParam)) { /* Do something to sub-windows */ for (i = 1; i < MAX_TERM_DATA; i++) { SetWindowPos(data[i].w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } /* Focus on main window */ SetFocus(hWnd); return 0; } break; } } return DefWindowProc(hWnd, uMsg, wParam, lParam); } static LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { term_data *td; HDC hdc; int i; #ifdef USE_SAVER static int iMouse = 0; static WORD xMouse = 0; static WORD yMouse = 0; int dx, dy; #endif /* USE_SAVER */ /* Acquire proper "term_data" info */ td = (term_data *)GetWindowLong(hWnd, 0); /* Process message */ switch (uMsg) { /* XXX XXX XXX */ case WM_NCCREATE: { SetWindowLong(hWnd, 0, (LONG)(my_td)); break; } /* XXX XXX XXX */ case WM_CREATE: { return 0; } case WM_GETMINMAXINFO: { #if 0 MINMAXINFO FAR *lpmmi; RECT rc; /* this message was sent before WM_NCCREATE */ if (!td) return 1; lpmmi = (MINMAXINFO FAR *)lParam; /* Minimum size */ rc.left = rc.top = 0; rc.right = rc.left + 8 * td->tile_wid + td->size_ow1 + td->size_ow2; rc.bottom = rc.top + 2 * td->tile_hgt + td->size_oh1 + td->size_oh2; /* Adjust */ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle); /* Save the minimum size */ lpmmi->ptMinTrackSize.x = rc.right - rc.left; lpmmi->ptMinTrackSize.y = rc.bottom - rc.top; /* Maximum window size */ rc.left = rc.top = 0; rc.right = rc.left + 80 * td->tile_wid + td->size_ow1 + td->size_ow2; rc.bottom = rc.top + 24 * td->tile_hgt + td->size_oh1 + td->size_oh2; /* Paranoia */ rc.right += (td->tile_wid - 1); rc.bottom += (td->tile_hgt - 1); /* Adjust */ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle); /* Save maximum size */ lpmmi->ptMaxSize.x = rc.right - rc.left; lpmmi->ptMaxSize.y = rc.bottom - rc.top; /* Save the maximum size */ lpmmi->ptMaxTrackSize.x = rc.right - rc.left; lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top; #endif /* 0 */ return 0; } case WM_SIZE: { int cols; int rows; /* this message was sent before WM_NCCREATE */ if (!td) return 1; /* it was sent from inside CreateWindowEx */ if (!td->w) return 1; /* was sent from inside WM_SIZE */ if (td->size_hack) return 1; td->size_hack = TRUE; cols = (LOWORD(lParam) - td->size_ow1) / td->tile_wid; rows = (HIWORD(lParam) - td->size_oh1) / td->tile_hgt; /* New size */ if ((td->cols != cols) || (td->rows != rows)) { /* Save old term */ term *old_term = Term; /* Save the new size */ td->cols = cols; td->rows = rows; /* Activate */ Term_activate(&td->t); /* Resize the term */ Term_resize(td->cols, td->rows); /* Activate */ Term_activate(old_term); /* Redraw later */ InvalidateRect(td->w, NULL, TRUE); /* HACK - Redraw all windows */ p_ptr->window = 0xFFFFFFFF; window_stuff(); } td->size_hack = FALSE; return 0; } case WM_PAINT: { handle_wm_paint(hWnd); return 0; } case WM_SYSKEYDOWN: case WM_KEYDOWN: { bool mc = FALSE; bool ms = FALSE; bool ma = FALSE; #ifdef USE_SAVER if (screensaver_active) { stop_screensaver(); return 0; } #endif /* USE_SAVER */ /* Extract the modifiers */ if (GetKeyState(VK_CONTROL) & 0x8000) mc = TRUE; if (GetKeyState(VK_SHIFT) & 0x8000) ms = TRUE; if (GetKeyState(VK_MENU) & 0x8000) ma = TRUE; /* Handle "special" keys */ if (special_key[(byte)(wParam)]) { /* Begin the macro trigger */ Term_keypress(31); /* Send the modifiers */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (ma) Term_keypress('A'); /* Extract "scan code" */ i = LOBYTE(HIWORD(lParam)); /* Introduce the scan code */ Term_keypress('x'); /* Encode the hexidecimal scan code */ Term_keypress(hexsym[i/16]); Term_keypress(hexsym[i%16]); /* End the macro trigger */ Term_keypress(13); return 0; } break; } case WM_CHAR: { Term_keypress(wParam); return 0; } #ifdef USE_SAVER case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: { if (screensaver_active) { stop_screensaver(); return 0; } break; } case WM_MOUSEMOVE: { if (!screensaver_active) break; if (iMouse) { dx = LOWORD(lParam) - xMouse; dy = HIWORD(lParam) - yMouse; if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; if ((dx > MOUSE_SENS) || (dy > MOUSE_SENS)) { stop_screensaver(); } } /* Save last location */ iMouse = 1; xMouse = LOWORD(lParam); yMouse = HIWORD(lParam); return 0; } #endif /* USE_SAVER */ case WM_PALETTECHANGED: { /* ignore if palette change caused by itself */ if ((HWND)wParam == hWnd) return FALSE; /* otherwise, fall through!!! */ } case WM_QUERYNEWPALETTE: { if (!paletted) return 0; hdc = GetDC(hWnd); SelectPalette(hdc, hPal, FALSE); i = RealizePalette(hdc); /* if any palette entries changed, repaint the window. */ if (i) InvalidateRect(hWnd, NULL, TRUE); ReleaseDC(hWnd, hdc); return 0; } case WM_NCLBUTTONDOWN: { #ifdef HTCLOSE if (wParam == HTCLOSE) wParam = HTSYSMENU; #endif /* HTCLOSE */ if (wParam == HTSYSMENU) { if (td->visible) { td->visible = FALSE; ShowWindow(td->w, SW_HIDE); } return 0; } break; } } return DefWindowProc(hWnd, uMsg, wParam, lParam); } #ifdef USE_SAVER LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int iMouse = 0; static WORD xMouse = 0; static WORD yMouse = 0; int dx, dy; /* Process */ switch (uMsg) { /* XXX XXX XXX */ case WM_NCCREATE: { break; } case WM_SETCURSOR: { SetCursor(NULL); return 0; } #if 0 case WM_ACTIVATE: { if (LOWORD(wParam) == WA_INACTIVE) break; /* else fall through */ } #endif /* 0 */ case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: { stop_screensaver(); return 0; } case WM_MOUSEMOVE: { if (iMouse) { dx = LOWORD(lParam) - xMouse; dy = HIWORD(lParam) - yMouse; if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; if ((dx > MOUSE_SENS) || (dy > MOUSE_SENS)) { stop_screensaver(); } } /* Save last location */ iMouse = 1; xMouse = LOWORD(lParam); yMouse = HIWORD(lParam); return 0; } case WM_CLOSE: { DestroyWindow(hwndSaver); if (screensaver) SendMessage(data[0].w, WM_CLOSE, 0, 0); hwndSaver = NULL; return 0; } } /* Oops */ return DefWindowProc(hWnd, uMsg, wParam, lParam); } #endif /* USE_SAVER */ /*** Temporary Hooks ***/ /* * Display warning message (see "z-util.c") */ static void hack_plog(cptr str) { /* Give a warning */ if (str) { MessageBox(NULL, str, "Warning", MB_ICONEXCLAMATION | MB_OK); } } /* * Display error message and quit (see "z-util.c") */ static void hack_quit(cptr str) { /* Give a warning */ if (str) { MessageBox(NULL, str, "Error", MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP); } /* Unregister the classes */ UnregisterClass(AppName, hInstance); /* Destroy the icon */ if (hIcon) DestroyIcon(hIcon); #ifdef USE_SAVER if (screensaverSemaphore) CloseHandle(screensaverSemaphore); #endif /* USE_SAVER */ /* Exit */ exit(0); } /*** Various hooks ***/ /* * Display warning message (see "z-util.c") */ static void hook_plog(cptr str) { #ifdef USE_SAVER if (screensaver_active) return; #endif /* USE_SAVER */ /* Warning */ if (str) { MessageBox(data[0].w, str, "Warning", MB_ICONEXCLAMATION | MB_OK); } } /* * Display error message and quit (see "z-util.c") */ static void hook_quit(cptr str) { int i; #ifdef USE_SOUND int j; #endif /* USE_SOUND */ #ifdef USE_SAVER if (!screensaver_active) #endif /* USE_SAVER */ { /* Give a warning */ if (str) { MessageBox(data[0].w, str, "Error", MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP); } /* Save the preferences */ save_prefs(); } /*** Could use 'Term_nuke_win()' XXX XXX XXX */ /* Destroy all windows */ for (i = MAX_TERM_DATA - 1; i >= 0; --i) { term_force_font(&data[i], NULL); if (data[i].font_want) string_free(data[i].font_want); if (data[i].w) DestroyWindow(data[i].w); data[i].w = 0; term_nuke(&data[i].t); } #ifdef USE_GRAPHICS /* Free the bitmap stuff */ FreeDIB(&infGraph); FreeDIB(&infMask); #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* Free the sound names */ for (i = 0; i < SOUND_MAX; i++) { for (j = 0; j < SAMPLE_MAX; j++) { if (!sound_file[i][j]) break; string_free(sound_file[i][j]); } } #endif /* USE_SOUND */ /*** Free some other stuff ***/ DeleteObject(hbrYellow); if (hPal) DeleteObject(hPal); UnregisterClass(AppName, hInstance); if (hIcon) DestroyIcon(hIcon); /* Free strings */ string_free(ini_file); string_free(argv0); string_free(ANGBAND_DIR_XTRA_FONT); string_free(ANGBAND_DIR_XTRA_GRAF); string_free(ANGBAND_DIR_XTRA_SOUND); string_free(ANGBAND_DIR_XTRA_HELP); #ifdef USE_MUSIC string_free(ANGBAND_DIR_XTRA_MUSIC); #endif /* USE_MUSIC */ #ifdef HAS_CLEANUP cleanup_angband(); #endif /* HAS_CLEANUP */ exit(0); } /*** Initialize ***/ /* * Init some stuff */ static void init_stuff(void) { int i; char path[1024]; #ifdef USE_SAVER char tmp[1024]; #endif /* USE_SAVER */ /* Get program name with full path */ if (GetModuleFileName(hInstance, path, sizeof(path)) == 0) show_win_error(); /* Paranoia */ path[sizeof(path) - 1] = '\0'; /* Save the "program name" */ argv0 = string_make(path); /* Get the name of the "*.ini" file */ strcpy(path + strlen(path) - 4, ".INI"); #ifdef USE_SAVER /* Try to get the path to the Angband folder */ if (screensaver) { /* Extract the filename of the savefile for the screensaver */ GetPrivateProfileString("Angband", "SaverFile", "", saverfilename, sizeof(saverfilename), path); GetPrivateProfileString("Angband", "AngbandPath", "", tmp, sizeof(tmp), path); strnmt(path, sizeof(path), "%szangband.ini", tmp); } #endif /* USE_SAVER */ /* Save the the name of the ini-file */ ini_file = string_make(path); /* Analyze the path */ i = strlen(path); /* Get the path */ for (; i > 0; i--) { if (path[i] == '\\') { /* End of path */ break; } } /* Add "lib" to the path */ strcpy(path + i + 1, "lib\\"); /* Validate the path */ validate_dir(path); /* Init the file paths */ init_file_paths(path); /* Hack -- Validate the paths */ validate_dir(ANGBAND_DIR_APEX); validate_dir(ANGBAND_DIR_BONE); validate_dir(ANGBAND_DIR_DATA); validate_dir(ANGBAND_DIR_EDIT); validate_dir(ANGBAND_DIR_SCRIPT); validate_dir(ANGBAND_DIR_FILE); validate_dir(ANGBAND_DIR_HELP); validate_dir(ANGBAND_DIR_INFO); validate_dir(ANGBAND_DIR_PREF); validate_dir(ANGBAND_DIR_SAVE); validate_dir(ANGBAND_DIR_USER); validate_dir(ANGBAND_DIR_XTRA); /* Build the filename */ path_make(path, ANGBAND_DIR_FILE, "news.txt"); /* Hack -- Validate the "news.txt" file */ validate_file(path); /* Build the "font" path */ path_make(path, ANGBAND_DIR_XTRA, "font"); /* Allocate the path */ ANGBAND_DIR_XTRA_FONT = string_make(path); /* Validate the "font" directory */ validate_dir(ANGBAND_DIR_XTRA_FONT); /* Build the filename */ path_make(path, ANGBAND_DIR_XTRA_FONT, "8X13.FON"); /* Hack -- Validate the basic font */ validate_file(path); #ifdef USE_GRAPHICS /* Build the "graf" path */ path_make(path, ANGBAND_DIR_XTRA, "graf"); /* Allocate the path */ ANGBAND_DIR_XTRA_GRAF = string_make(path); /* Validate the "graf" directory */ validate_dir(ANGBAND_DIR_XTRA_GRAF); #endif /* USE_GRAPHICS */ #ifdef USE_SOUND /* Build the "sound" path */ path_make(path, ANGBAND_DIR_XTRA, "sound"); /* Allocate the path */ ANGBAND_DIR_XTRA_SOUND = string_make(path); /* Validate the "sound" directory */ validate_dir(ANGBAND_DIR_XTRA_SOUND); #endif /* USE_SOUND */ #ifdef USE_MUSIC /* Build the "music" path */ path_make(path, ANGBAND_DIR_XTRA, "music"); /* Allocate the path */ ANGBAND_DIR_XTRA_MUSIC = string_make(path); /* Validate the "music" directory */ validate_dir(ANGBAND_DIR_XTRA_MUSIC); #endif /* USE_MUSIC */ /* Build the "help" path */ path_make(path, ANGBAND_DIR_XTRA, "help"); /* Allocate the path */ ANGBAND_DIR_XTRA_HELP = string_make(path); #if 0 /* Validate the "help" directory */ validate_dir(ANGBAND_DIR_XTRA_HELP); #endif /* 0 */ } /* * Test to see if we need to work-around bugs in * the windows ascii-drawing routines. */ bool broken_ascii(void) { OSVERSIONINFO Dozeversion; Dozeversion.dwOSVersionInfoSize = sizeof(Dozeversion); if (GetVersionEx(&Dozeversion)) { /* Win XP is b0rken */ if ((Dozeversion.dwPlatformId >= VER_PLATFORM_WIN32_NT) && (Dozeversion.dwMajorVersion >= 5)) { return (TRUE); } } return (FALSE); } int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { int i; WNDCLASS wc; HDC hdc; MSG msg; /* Unused parameter */ (void)nCmdShow; #ifdef USE_SAVER if (lpCmdLine && ((*lpCmdLine == '-') || (*lpCmdLine == '/'))) { lpCmdLine++; switch (*lpCmdLine) { case 's': case 'S': { screensaver = TRUE; /* Only run one screensaver at the time */ screensaverSemaphore = CreateSemaphore(NULL, 0, 1, "AngbandSaverSemaphore"); if (!screensaverSemaphore) exit(0); if (GetLastError() == ERROR_ALREADY_EXISTS) { CloseHandle(screensaverSemaphore); exit(0); } break; } case 'P': case 'p': case 'C': case 'c': case 'A': case 'a': { /* * ToDo: implement preview, configuration, and changing * the password (as well as checking it). */ exit(0); } } } #endif /* USE_SAVER */ /* Initialize */ if (hPrevInst == NULL) { wc.style = CS_CLASSDC; wc.lpfnWndProc = AngbandWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 4; /* one long pointer to term_data */ wc.hInstance = hInst; wc.hIcon = hIcon = LoadIcon(hInst, "ANGBAND"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(BLACK_BRUSH); wc.lpszMenuName = "ANGBAND"; wc.lpszClassName = AppName; if (!RegisterClass(&wc)) exit(1); wc.lpfnWndProc = AngbandListProc; wc.lpszMenuName = NULL; wc.lpszClassName = AngList; if (!RegisterClass(&wc)) exit(2); #ifdef USE_SAVER wc.style = CS_VREDRAW | CS_HREDRAW | CS_SAVEBITS | CS_DBLCLKS; wc.lpfnWndProc = AngbandSaverProc; wc.hCursor = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "WindowsScreenSaverClass"; if (!RegisterClass(&wc)) exit(3); #endif /* USE_SAVER */ } /* Save globally */ hInstance = hInst; /* Temporary hooks */ plog_aux = hack_plog; quit_aux = hack_quit; core_aux = hack_quit; /* Prepare the filepaths */ init_stuff(); /* Initialize the keypress analyzer */ for (i = 0; special_key_list[i]; i++) { special_key[special_key_list[i]] = TRUE; } /* Determine if display is 16/256/true color */ hdc = GetDC(NULL); colors16 = (GetDeviceCaps(hdc, BITSPIXEL) == 4); paletted = ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) ? TRUE : FALSE); ReleaseDC(NULL, hdc); /* Initialize the colors */ for (i = 0; i < 256; i++) { byte rv, gv, bv; /* Extract desired values */ rv = angband_color_table[i][1]; gv = angband_color_table[i][2]; bv = angband_color_table[i][3]; /* Extract the "complex" code */ win_clr[i] = PALETTERGB(rv, gv, bv); /* Save the "simple" code */ angband_color_table[i][0] = win_pal[i]; } /* Prepare the windows */ init_windows(); /* Activate hooks */ plog_aux = hook_plog; quit_aux = hook_quit; core_aux = hook_quit; /* Set the system suffix */ ANGBAND_SYS = "win"; if (broken_ascii()) { ANGBAND_SYS = "w2k"; } /* Initialize */ init_angband(); /* We are now initialized */ initialized = TRUE; #ifdef USE_SAVER if (screensaver) { /* Start the screensaver */ start_screensaver(); /* Paranoia */ quit(NULL); } #endif /* USE_SAVER */ /* Did the user double click on a save file? */ check_for_save_file(lpCmdLine); /* Save the hooks into the overhead map */ set_callback((callback_type) win_map_info, CALL_MAP_INFO, NULL); /* Save player movement hook */ set_callback((callback_type) win_player_move, CALL_PLAYER_MOVE, NULL); /* Prompt the user */ prtf(17, 23, "[Choose 'New' or 'Open' from the 'File' menu]"); Term_fresh(); /* Process messages forever */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* Paranoia */ quit(NULL); /* Paranoia */ return (0); } #endif /* WINDOWS */ zangband/src/main-x11.c0000755000000000000000000016223410250356274013645 0ustar rootroot/* File: main-x11.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with UNIX/X11 computers. * * To use this file, compile with "USE_X11" defined, and link against all * the various "X11" libraries which may be needed. * * See also "main-xaw.c". * * Part of this file provides a user interface package composed of several * pseudo-objects, including "metadpy" (a display), "infowin" (a window), * "infoclr" (a color), and "infofnt" (a font). Actually, the package was * originally much more interesting, but it was bastardized to keep this * file simple. * * The rest of this file is an implementation of "main-xxx.c" for X11. * * Most of this file is by Ben Harrison (benh@phial.com). */ /* * The following shell script can be used to launch Angband, assuming that * it was extracted into "~/Angband", and compiled using "USE_X11", on a * Linux machine, with a 1280x1024 screen, using 6 windows (with the given * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142, * and without graphics (add "-g" for graphics). Just copy this comment * into a file, remove the leading " * " characters (and the head/tail of * this comment), and make the file executable. * * * #!/bin/csh * * # Describe attempt * echo "Launching angband..." * sleep 2 * * # Main window * setenv ANGBAND_X11_FONT_0 10x20 * setenv ANGBAND_X11_AT_X_0 5 * setenv ANGBAND_X11_AT_Y_0 510 * * # Message window * setenv ANGBAND_X11_FONT_1 8x13 * setenv ANGBAND_X11_AT_X_1 5 * setenv ANGBAND_X11_AT_Y_1 22 * setenv ANGBAND_X11_ROWS_1 35 * * # Inventory window * setenv ANGBAND_X11_FONT_2 8x13 * setenv ANGBAND_X11_AT_X_2 635 * setenv ANGBAND_X11_AT_Y_2 182 * setenv ANGBAND_X11_ROWS_2 23 * * # Equipment window * setenv ANGBAND_X11_FONT_3 8x13 * setenv ANGBAND_X11_AT_X_3 635 * setenv ANGBAND_X11_AT_Y_3 22 * setenv ANGBAND_X11_ROWS_3 12 * * # Monster recall window * setenv ANGBAND_X11_FONT_4 6x13 * setenv ANGBAND_X11_AT_X_4 817 * setenv ANGBAND_X11_AT_Y_4 847 * setenv ANGBAND_X11_COLS_4 76 * setenv ANGBAND_X11_ROWS_4 11 * * # Object recall window * setenv ANGBAND_X11_FONT_5 6x13 * setenv ANGBAND_X11_AT_X_5 817 * setenv ANGBAND_X11_AT_Y_5 520 * setenv ANGBAND_X11_COLS_5 76 * setenv ANGBAND_X11_ROWS_5 24 * * # The build directory * cd ~/Angband * * # Gamma correction * setenv ANGBAND_X11_GAMMA 142 * * # Launch Angband * ./src/angband -mx11 -- -n6 & * */ #include "angband.h" #ifdef USE_X11 cptr help_x11[] = { "To use X11", "-d Set display name", #ifdef USE_GRAPHICS "-s Turn off smoothscaling graphics", "-b# Set tileset bitmap", #endif /* USE_GRAPHICS */ "-n# Number of terms to use", NULL }; #ifndef __MAKEDEPEND__ #include #include #include #include #include #endif /* __MAKEDEPEND__ */ /* * Include some helpful X11 code. */ #include "maid-x11.h" /* * Hack -- avoid some compiler warnings */ #define IGNORE_UNUSED_FUNCTIONS /* * Notes on Colors: * * 1) On a monochrome (or "fake-monochrome") display, all colors * will be "cast" to "fg," except for the bg color, which is, * obviously, cast to "bg". Thus, one can ignore this setting. * * 2) Because of the inner functioning of the color allocation * routines, colors may be specified as (a) a typical color name, * (b) a hexidecimal color specification (preceded by a pound sign), * or (c) by strings such as "fg", "bg", "zg". * * 3) Due to the workings of the init routines, many colors * may also be dealt with by their actual pixel values. Note that * the pixel with all bits set is "zg = (1<depth)-1", which * is not necessarily either black or white. */ /**** Generic Types ****/ /* * An X11 pixell specifier */ typedef unsigned long Pixell; /* * The structures defined below */ typedef struct metadpy metadpy; typedef struct infowin infowin; typedef struct infoclr infoclr; typedef struct infofnt infofnt; /* * A structure summarizing a given Display. * * - The Display itself * - The default Screen for the display * - The virtual root (usually just the root) * - The default colormap (from a macro) * * - The "name" of the display * * - The socket to listen to for events * * - The width of the display screen (from a macro) * - The height of the display screen (from a macro) * - The bit depth of the display screen (from a macro) * * - The black Pixell (from a macro) * - The white Pixell (from a macro) * * - The background Pixell (default: black) * - The foreground Pixell (default: white) * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly) * * - Bit Flag: Force all colors to black and white (default: !color) * - Bit Flag: Allow the use of color (default: depth > 1) * - Bit Flag: We created 'dpy', and so should nuke it when done. */ struct metadpy { Display *dpy; Screen *screen; Window root; Colormap cmap; char *name; int fd; uint width; uint height; uint depth; Pixell black; Pixell white; Pixell bg; Pixell fg; Pixell zg; uint mono:1; uint color:1; uint nuke:1; }; /* * A Structure summarizing Window Information. * * I assume that a window is at most 30000 pixels on a side. * I assume that the root windw is also at most 30000 square. * * - The Window * - The current Input Event Mask * * - The location of the window * - The width, height of the window * - The border width of this window * * - Byte: 1st Extra byte * * - Bit Flag: This window is currently Mapped * - Bit Flag: This window needs to be redrawn * - Bit Flag: This window has been resized * * - Bit Flag: We should nuke 'win' when done with it */ struct infowin { Window win; long mask; s16b ox, oy; s16b x, y; s16b w, h; u16b b; byte byte1; bool mapped; bool redraw; bool resize; bool nuke; }; /* * A Structure summarizing Operation+Color Information * * - The actual GC corresponding to this info * * - The Foreground Pixell Value * - The Background Pixell Value * * - Num (0-15): The operation code (As in Clear, Xor, etc) * - Bit Flag: The GC is in stipple mode * - Bit Flag: Destroy 'gc' at Nuke time. */ struct infoclr { GC gc; Pixell fg; Pixell bg; uint code:4; uint stip:1; uint nuke:1; }; /* * A Structure to Hold Font Information * * - The 'XFontStruct*' (yields the 'Font') * * - The font name * * - The default character width * - The default character height * - The default character ascent * * - Byte: Pixel offset used during fake mono * * - Flag: Force monospacing via 'wid' * - Flag: Nuke info when done */ struct infofnt { XFontStruct *info; cptr name; s16b wid; s16b twid; s16b hgt; s16b asc; byte off; uint mono:1; uint nuke:1; }; /**** Generic Macros ****/ /* Set current metadpy (Metadpy) to 'M' */ #define Metadpy_set(M) \ Metadpy = M /* Initialize 'M' using Display 'D' */ #define Metadpy_init_dpy(D) \ Metadpy_init_2(D,cNULL) /* Initialize 'M' using a Display named 'N' */ #define Metadpy_init_name(N) \ Metadpy_init_2((Display*)(NULL),N) /* Initialize 'M' using the standard Display */ #define Metadpy_init() \ Metadpy_init_name("") /* Init an infowin by giving father as an (info_win*) (or NULL), and data */ #define Infowin_init_dad(D,X,Y,W,H,B,FG,BG) \ Infowin_init_data(((D) ? ((D)->win) : (Window)(None)), \ X,Y,W,H,B,FG,BG) /* Init a top level infowin by pos,size,bord,Colors */ #define Infowin_init_top(X,Y,W,H,B,FG,BG) \ Infowin_init_data(None,X,Y,W,H,B,FG,BG) /* Request a new standard window by giving Dad infowin and X,Y,W,H */ #define Infowin_init_std(D,X,Y,W,H,B) \ Infowin_init_dad(D,X,Y,W,H,B,Metadpy->fg,Metadpy->bg) /* Set the current Infowin */ #define Infowin_set(I) \ (Infowin = (I)) /* Set the current Infoclr */ #define Infoclr_set(C) \ (Infoclr = (C)) #define Infoclr_init_ppo(F,B,O,M) \ Infoclr_init_data(F,B,O,M) #define Infoclr_init_cco(F,B,O,M) \ Infoclr_init_ppo(Infoclr_Pixell(F),Infoclr_Pixell(B),O,M) #define Infoclr_init_ppn(F,B,O,M) \ Infoclr_init_ppo(F,B,Infoclr_Opcode(O),M) #define Infoclr_init_ccn(F,B,O,M) \ Infoclr_init_cco(F,B,Infoclr_Opcode(O),M) /* Set the current infofnt */ #define Infofnt_set(I) \ (Infofnt = (I)) /**** Generic Globals ****/ /* * The "default" values */ static metadpy metadpy_default; /* * The "current" variables */ static metadpy *Metadpy = &metadpy_default; static infowin *Infowin = (infowin*)(NULL); static infoclr *Infoclr = (infoclr*)(NULL); static infofnt *Infofnt = (infofnt*)(NULL); /**** Generic code ****/ /* * Init the current metadpy, with various initialization stuff. * * Inputs: * dpy: The Display* to use (if NULL, create it) * name: The name of the Display (if NULL, the current) * * Notes: * If 'name' is NULL, but 'dpy' is set, extract name from dpy * If 'dpy' is NULL, then Create the named Display * If 'name' is NULL, and so is 'dpy', use current Display * * Return -1 if no Display given, and none can be opened. */ static errr Metadpy_init_2(Display *dpy, cptr name) { metadpy *m = Metadpy; /*** Open the display if needed ***/ /* If no Display given, attempt to Create one */ if (!dpy) { /* Attempt to open the display */ dpy = XOpenDisplay(name); /* Failure */ if (!dpy) return (-1); /* We will have to nuke it when done */ m->nuke = 1; } /* Since the Display was given, use it */ else { /* We will not have to nuke it when done */ m->nuke = 0; } /*** Save some information ***/ /* Save the Display itself */ m->dpy = dpy; /* Get the Screen and Virtual Root Window */ m->screen = DefaultScreenOfDisplay(dpy); m->root = RootWindowOfScreen(m->screen); /* Get the default colormap */ m->cmap = DefaultColormapOfScreen(m->screen); /* Extract the true name of the display */ m->name = DisplayString(dpy); /* Extract the fd */ m->fd = ConnectionNumber(Metadpy->dpy); /* Save the Size and Depth of the screen */ m->width = WidthOfScreen(m->screen); m->height = HeightOfScreen(m->screen); m->depth = DefaultDepthOfScreen(m->screen); /* Save the Standard Colors */ m->black = BlackPixelOfScreen(m->screen); m->white = WhitePixelOfScreen(m->screen); /*** Make some clever Guesses ***/ /* Guess at the desired 'fg' and 'bg' Pixell's */ m->bg = m->black; m->fg = m->white; /* Calculate the Maximum allowed Pixel value. */ m->zg = ((Pixell)1 << m->depth) - 1; /* Save various default Flag Settings */ m->color = ((m->depth > 1) ? 1 : 0); m->mono = ((m->color) ? 0 : 1); /* Return "success" */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Nuke the current metadpy */ static errr Metadpy_nuke(void) { metadpy *m = Metadpy; /* If required, Free the Display */ if (m->nuke) { /* Close the Display */ XCloseDisplay(m->dpy); /* Forget the Display */ m->dpy = (Display*)(NULL); /* Do not nuke it again */ m->nuke = 0; } /* Return Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * General Flush/ Sync/ Discard routine */ static errr Metadpy_update(int flush, int sync, int discard) { /* Flush if desired */ if (flush) XFlush(Metadpy->dpy); /* Sync if desired, using 'discard' */ if (sync) XSync(Metadpy->dpy, discard); /* Success */ return (0); } /* * Make a simple beep */ static errr Metadpy_do_beep(void) { /* Make a simple beep */ XBell(Metadpy->dpy, 100); return (0); } /* * Set the name (in the title bar) of Infowin */ static errr Infowin_set_name(cptr name) { Status st; XTextProperty tp; char buf[128]; char *bp = buf; strnfmt(buf, sizeof(buf), "%s", name); st = XStringListToTextProperty(&bp, 1, &tp); if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp); return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Set the icon name of Infowin */ static errr Infowin_set_icon_name(cptr name) { Status st; XTextProperty tp; char buf[128]; char *bp = buf; strnfmt(buf, sizeof(buf), "%s", name); st = XStringListToTextProperty(&bp, 1, &tp); if (st) XSetWMIconName(Metadpy->dpy, Infowin->win, &tp); return (0); } /* * Nuke Infowin */ static errr Infowin_nuke(void) { infowin *iwin = Infowin; /* Nuke if requested */ if (iwin->nuke) { /* Destory the old window */ XDestroyWindow(Metadpy->dpy, iwin->win); } /* Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Prepare a new 'infowin'. */ static errr Infowin_prepare(Window xid) { infowin *iwin = Infowin; Window tmp_win; XWindowAttributes xwa; int x, y; unsigned int w, h, b, d; /* Assign stuff */ iwin->win = xid; /* Check For Error XXX Extract some ACTUAL data from 'xid' */ XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d); /* Apply the above info */ iwin->x = x; iwin->y = y; iwin->w = w; iwin->h = h; iwin->b = b; /* Check Error XXX Extract some more ACTUAL data */ XGetWindowAttributes(Metadpy->dpy, xid, &xwa); /* Apply the above info */ iwin->mask = xwa.your_event_mask; iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1); /* Success */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Initialize a new 'infowin'. */ static errr Infowin_init_real(Window xid) { /* Wipe it clean */ (void)WIPE(Infowin, infowin); /* Start out non-nukable */ Infowin->nuke = 0; /* Attempt to Prepare ourself */ return (Infowin_prepare(xid)); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Init an infowin by giving some data. * * Inputs: * dad: The Window that should own this Window (if any) * x,y: The position of this Window * w,h: The size of this Window * b,d: The border width and pixel depth * * Notes: * If 'dad == None' assume 'dad == root' */ static errr Infowin_init_data(Window dad, int x, int y, int w, int h, int b, Pixell fg, Pixell bg) { Window xid; /* Wipe it clean */ (void)WIPE(Infowin, infowin); /*** Error Check XXX ***/ /*** Create the Window 'xid' from data ***/ /* What happened here? XXX XXX XXX */ /* If no parent given, depend on root */ if (dad == None) /* #ifdef USE_GRAPHICS xid = XCreateWindow(Metadpy->dpy, Metadpy->root, x, y, w, h, b, 8, InputOutput, CopyFromParent, 0, 0); else */ /* #else */ dad = Metadpy->root; /* #endif */ /* Create the Window XXX Error Check */ xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg); /* Start out selecting No events */ XSelectInput(Metadpy->dpy, xid, 0L); /*** Prepare the new infowin ***/ /* Mark it as nukable */ Infowin->nuke = 1; /* Attempt to Initialize the infowin */ return (Infowin_prepare(xid)); } /* * Modify the event mask of an Infowin */ static errr Infowin_set_mask(long mask) { /* Save the new setting */ Infowin->mask = mask; /* Execute the Mapping */ XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask); /* Success */ return (0); } /* * Request that Infowin be mapped */ static errr Infowin_map(void) { /* Execute the Mapping */ XMapWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Request that Infowin be unmapped */ static errr Infowin_unmap(void) { /* Execute the Un-Mapping */ XUnmapWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Request that Infowin be raised */ static errr Infowin_raise(void) { /* Raise towards visibility */ XRaiseWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Request that Infowin be lowered */ static errr Infowin_lower(void) { /* Lower towards invisibility */ XLowerWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Request that Infowin be moved to a new location */ static errr Infowin_impell(int x, int y) { /* Execute the request */ XMoveWindow(Metadpy->dpy, Infowin->win, x, y); /* Success */ return (0); } /* * Resize an infowin */ static errr Infowin_resize(int w, int h) { /* Execute the request */ XResizeWindow(Metadpy->dpy, Infowin->win, w, h); /* Success */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Move and Resize an infowin */ static errr Infowin_locate(int x, int y, int w, int h) { /* Execute the request */ XMoveResizeWindow(Metadpy->dpy, Infowin->win, x, y, w, h); /* Success */ return (0); } /* * Visually clear Infowin */ static errr Infowin_wipe(void) { /* Execute the request */ XClearWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } /* * Visually Paint Infowin with the current color */ static errr Infowin_fill(void) { /* Execute the request */ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, 0, 0, Infowin->w, Infowin->h); /* Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * A NULL terminated pair list of legal "operation names" * * Pairs of values, first is texttual name, second is the string * holding the decimal value that the operation corresponds to. */ static cptr opcode_pairs[] = { "cpy", "3", "xor", "6", "and", "1", "ior", "7", "nor", "8", "inv", "10", "clr", "0", "set", "15", "src", "3", "dst", "5", "+andReverse", "2", "+andInverted", "4", "+noop", "5", "+equiv", "9", "+orReverse", "11", "+copyInverted", "12", "+orInverted", "13", "+nand", "14", NULL }; /* * Parse a word into an operation "code" * * Inputs: * str: A string, hopefully representing an Operation * * Output: * 0-15: if 'str' is a valid Operation * -1: if 'str' could not be parsed */ static int Infoclr_Opcode(cptr str) { register int i; /* Scan through all legal operation names */ for (i = 0; opcode_pairs[i*2]; ++i) { /* Is this the right oprname? */ if (streq(opcode_pairs[i*2], str)) { /* Convert the second element in the pair into a Code */ return (atoi(opcode_pairs[i*2+1])); } } /* The code was not found, return -1 */ return (-1); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Request a Pixell by name. Note: uses 'Metadpy'. * * Inputs: * name: The name of the color to try to load (see below) * * Output: * The Pixell value that metched the given name * 'Metadpy->fg' if the name was unparseable * * Valid forms for 'name': * 'fg', 'bg', 'zg', '' and '#' */ static Pixell Infoclr_Pixell(cptr name) { XColor scrn; /* Attempt to Parse the name */ if (name && name[0]) { /* The 'bg' color is available */ if (streq(name, "bg")) return (Metadpy->bg); /* The 'fg' color is available */ if (streq(name, "fg")) return (Metadpy->fg); /* The 'zg' color is available */ if (streq(name, "zg")) return (Metadpy->zg); /* The 'white' color is available */ if (streq(name, "white")) return (Metadpy->white); /* The 'black' color is available */ if (streq(name, "black")) return (Metadpy->black); /* Attempt to parse 'name' into 'scrn' */ if (!(XParseColor(Metadpy->dpy, Metadpy->cmap, name, &scrn))) { plog_fmt("Warning: Couldn't parse color '%s'\n", name); } /* Attempt to Allocate the Parsed color */ if (!(XAllocColor(Metadpy->dpy, Metadpy->cmap, &scrn))) { plog_fmt("Warning: Couldn't allocate color '%s'\n", name); } /* The Pixel was Allocated correctly */ else return (scrn.pixel); } /* Warn about the Default being Used */ plog_fmt("Warning: Using 'fg' for unknown color '%s'\n", name); /* Default to the 'Foreground' color */ return (Metadpy->fg); } /* * Initialize a new 'infoclr' with a real GC. */ static errr Infoclr_init_1(GC gc) { infoclr *iclr = Infoclr; /* Wipe the iclr clean */ (void)WIPE(iclr, infoclr); /* Assign the GC */ iclr->gc = gc; /* Success */ return (0); } /* * Nuke an old 'infoclr'. */ static errr Infoclr_nuke(void) { infoclr *iclr = Infoclr; /* Deal with 'GC' */ if (iclr->nuke) { /* Free the GC */ XFreeGC(Metadpy->dpy, iclr->gc); } /* Forget the current */ Infoclr = (infoclr*)(NULL); /* Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Initialize an infoclr with some data * * Inputs: * fg: The Pixell for the requested Foreground (see above) * bg: The Pixell for the requested Background (see above) * op: The Opcode for the requested Operation (see above) * stip: The stipple mode */ static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip) { infoclr *iclr = Infoclr; GC gc; XGCValues gcv; unsigned long gc_mask; /*** Simple error checking of opr and clr ***/ /* Check the 'Pixells' for realism */ if (bg > Metadpy->zg) return (-1); if (fg > Metadpy->zg) return (-1); /* Check the data for trueness */ if ((op < 0) || (op > 15)) return (-1); /*** Create the requested 'GC' ***/ /* Assign the proper GC function */ gcv.function = op; /* Assign the proper GC background */ gcv.background = bg; /* Assign the proper GC foreground */ gcv.foreground = fg; /* Hack -- Handle XOR (xor is code 6) by hacking bg and fg */ if (op == 6) gcv.background = 0; if (op == 6) gcv.foreground = (bg ^ fg); /* Assign the proper GC Fill Style */ gcv.fill_style = (stip ? FillStippled : FillSolid); /* Turn off 'Give exposure events for pixmap copying' */ gcv.graphics_exposures = False; /* Set up the GC mask */ gc_mask = (GCFunction | GCBackground | GCForeground | GCFillStyle | GCGraphicsExposures); /* Create the GC detailed above */ gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv); /*** Initialize ***/ /* Wipe the iclr clean */ (void)WIPE(iclr, infoclr); /* Assign the GC */ iclr->gc = gc; /* Nuke it when done */ iclr->nuke = 1; /* Assign the parms */ iclr->fg = fg; iclr->bg = bg; iclr->code = op; iclr->stip = stip ? 1 : 0; /* Success */ return (0); } /* * Change the 'fg' for an infoclr * * Inputs: * fg: The Pixell for the requested Foreground (see above) */ static errr Infoclr_change_fg(Pixell fg) { infoclr *iclr = Infoclr; /*** Simple error checking of opr and clr ***/ /* Check the 'Pixells' for realism */ if (fg > Metadpy->zg) return (-1); /*** Change ***/ /* Change */ XSetForeground(Metadpy->dpy, iclr->gc, fg); /* Success */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Nuke an old 'infofnt'. */ static errr Infofnt_nuke(void) { infofnt *ifnt = Infofnt; /* Deal with 'name' */ if (ifnt->name) { /* Free the name */ string_free(ifnt->name); } /* Nuke info if needed */ if (ifnt->nuke) { /* Free the font */ XFreeFont(Metadpy->dpy, ifnt->info); } /* Success */ return (0); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Prepare a new 'infofnt' */ static errr Infofnt_prepare(XFontStruct *info) { infofnt *ifnt = Infofnt; XCharStruct *cs; /* Assign the struct */ ifnt->info = info; /* Jump into the max bounds thing */ cs = &(info->max_bounds); /* Extract default sizing info */ ifnt->asc = info->ascent; ifnt->hgt = info->ascent + info->descent; ifnt->wid = cs->width; #ifdef OBSOLETE_SIZING_METHOD /* Extract default sizing info */ ifnt->asc = cs->ascent; ifnt->hgt = (cs->ascent + cs->descent); ifnt->wid = cs->width; #endif if (use_bigtile) ifnt->twid = 2 * cs->width; else ifnt->twid = cs->width; /* Success */ return (0); } #ifndef IGNORE_UNUSED_FUNCTIONS /* * Initialize a new 'infofnt'. */ static errr Infofnt_init_real(XFontStruct *info) { /* Wipe the thing */ (void)WIPE(Infofnt, infofnt); /* No nuking */ Infofnt->nuke = 0; /* Attempt to prepare it */ return (Infofnt_prepare(info)); } #endif /* IGNORE_UNUSED_FUNCTIONS */ /* * Init an infofnt by its Name * * Inputs: * name: The name of the requested Font */ static errr Infofnt_init_data(cptr name) { XFontStruct *info; /*** Load the info Fresh, using the name ***/ /* If the name is not given, report an error */ if (!name) { plog_fmt("Missing font! %s", name); return (-1); } /* Attempt to load the font */ info = XLoadQueryFont(Metadpy->dpy, name); /* The load failed */ if (!info) { plog_fmt("Failed to find font:\"%s\"", name); return (-1); } /*** Init the font ***/ /* Wipe the thing */ (void)WIPE(Infofnt, infofnt); /* Attempt to prepare it */ if (Infofnt_prepare(info)) { /* Free the font */ XFreeFont(Metadpy->dpy, info); /* Fail */ plog_fmt("Failed to prepare font:\"%s\"", name); return (-1); } /* Save a copy of the font name */ Infofnt->name = string_make(name); /* Mark it as nukable */ Infofnt->nuke = 1; /* Success */ return (0); } /* * Find the square a particular pixel is part of. */ static void pixel_to_square(int *x, int *y, int ox, int oy) { (*x) = (ox - Infowin->ox) / Infofnt->wid; (*y) = (oy - Infowin->oy) / Infofnt->hgt; if ((use_bigtile) && ((*y) >= Term->scr->big_y1) && ((*y) <= Term->scr->big_y2) && ((*x) >= Term->scr->big_x1)) { (*x) -= ((*x) - Term->scr->big_x1 + 1) / 2; } } /* * Find the pixel at the top-left corner of a square. */ static void square_to_pixel(int *x, int *y, int ox, int oy) { (*y) = oy * Infofnt->hgt + Infowin->oy; if ((use_bigtile) && (oy >= Term->scr->big_y1) && (oy <= Term->scr->big_y2) && (ox > Term->scr->big_x1)) { (*x) = ox * Infofnt->twid + Infowin->ox - Term->scr->big_x1 * Infofnt->wid; } else { (*x) = ox * Infofnt->wid + Infowin->ox; } } /* * Painting where text would be */ static errr Infofnt_text_non(int x, int y, int len) { int x1, y1, x2, y2; /*** Find the dimensions ***/ square_to_pixel(&x1, &y1, x, y); square_to_pixel(&x2, &y2, x + len, y); /*** Actually 'paint' the area ***/ /* Just do a Fill Rectangle */ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x1, y1, x2 - x1, Infofnt->hgt); /* Success */ return (0); } /* * Standard Text */ static errr Infofnt_text_std(int cx, int cy, cptr str, int len) { int i; int x, y; /*** Do a brief info analysis ***/ /* Do nothing if the string is null */ if (!str || !*str) return (-1); /* Get the length of the string */ if (len < 0) len = strlen(str); /*** Decide where to place the string, vertically ***/ square_to_pixel(&x, &y, cx, cy); /* Ignore Vertical Justifications */ y += Infofnt->asc; /*** Actually draw 'str' onto the infowin ***/ /* Be sure the correct font is ready */ XSetFont(Metadpy->dpy, Infoclr->gc, Infofnt->info->fid); /*** Handle the fake mono we can enforce on fonts ***/ /* Monotize the font if required */ if (Infofnt->mono || is_bigtiled(cx + len - 1, cy)) { /* Horizontal Justification */ x += Infofnt->off; /* Do each character */ for (i = 0; i < len; ++i) { if (is_bigtiled(cx + i, cy)) { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, str + i, 1); x += Infofnt->twid; } else { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, str + i, 1); x += Infofnt->wid; } } } /* Assume monoospaced font */ else { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, str, len); } /* Success */ return (0); } /* * Hack -- cursor color */ static infoclr *xor; /* * Actual color table */ static infoclr *clr[256]; /* * Color info (unused, red, green, blue). */ static byte color_table[256][4]; /* * Forward declare */ typedef struct term_data term_data; /* * A structure for each "term" */ struct term_data { term t; infofnt *fnt; infowin *win; #ifdef USE_GRAPHICS XImage *tiles; XImage *b_tiles; /* Temporary storage for overlaying tiles. */ XImage *TmpImage; #endif /* USE_GRAPHICS */ }; /* * The number of term data structures */ #define MAX_TERM_DATA 8 /* * The array of term data structures */ static term_data data[MAX_TERM_DATA]; /* Use short names for the most commonly used elements of various structures. */ #define DPY (Metadpy->dpy) #define WIN (Infowin->win) /* Describe a set of co-ordinates. */ typedef struct co_ord co_ord; struct co_ord { int x; int y; }; /* * A special structure to store information about the text currently * selected. */ typedef struct x11_selection_type x11_selection_type; struct x11_selection_type { bool select; /* The selection is currently in use. */ bool drawn; /* The selection is currently displayed. */ term *t; /* The window where the selection is found. */ co_ord init; /* The starting co-ordinates. */ co_ord cur; /* The end co-ordinates (the current ones if still copying). */ co_ord old; /* The previous end co-ordinates. */ Time time; /* The time at which the selection was finalised. */ }; static x11_selection_type x11_selection[1]; /* * Process a keypress event * * Also appears in "main-xaw.c". */ static void react_keypress(XKeyEvent *ev) { int i, n, mc, ms, mo, mx; uint ks1; KeySym ks; char buf[128]; char msg[128]; /* Check for "normal" keypresses */ n = XLookupString(ev, buf, 125, &ks, NULL); /* Terminate */ buf[n] = '\0'; /* Hack -- Ignore "modifier keys" */ if (IsModifierKey(ks)) return; /* Hack -- convert into an unsigned int */ ks1 = (uint)(ks); /* Extract four "modifier flags" */ mc = (ev->state & ControlMask) ? TRUE : FALSE; ms = (ev->state & ShiftMask) ? TRUE : FALSE; mo = (ev->state & Mod1Mask) ? TRUE : FALSE; mx = (ev->state & Mod2Mask) ? TRUE : FALSE; /* Normal keys with no modifiers */ if (n && !mo && !mx && !IsSpecialKey(ks)) { /* Enqueue the normal key(s) */ for (i = 0; buf[i]; i++) Term_keypress(buf[i]); /* All done */ return; } /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */ switch (ks1) { case XK_Escape: { Term_keypress(ESCAPE); return; } case XK_Return: { Term_keypress('\r'); return; } case XK_Tab: { Term_keypress('\t'); return; } case XK_Delete: case XK_BackSpace: { Term_keypress('\010'); return; } } /* Hack -- Use the KeySym */ if (ks) { strnfmt(msg, sizeof(msg), "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13); } /* Hack -- Use the Keycode */ else { strnfmt(msg, sizeof(msg), "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13); } /* Enqueue the "macro trigger" string */ for (i = 0; msg[i]; i++) Term_keypress(msg[i]); /* Hack -- auto-define macros as needed */ if (n && (macro_find_exact(msg) < 0)) { /* Create a macro */ macro_add(msg, buf); } } /* * Convert co-ordinates from starting corner/opposite corner to minimum/maximum. */ static void sort_co_ord(co_ord *min, co_ord *max, const co_ord *b, const co_ord *a) { min->x = MIN(a->x, b->x); min->y = MIN(a->y, b->y); max->x = MAX(a->x, b->x); max->y = MAX(a->y, b->y); /* Prevent bigtile wierdness */ if (is_bigtiled(min->x, min->y) != is_bigtiled(max->x, max->y)) { if (min->y < Term->scr->big_y1) min->y = Term->scr->big_y1; if (max->y > Term->scr->big_y2) max->y = Term->scr->big_y2; } } /* * Select an area by drawing a grey box around it. * Since we use XOR, we can undraw the box by using this * routine again. (The XOR method also works best for things like snow) */ static void mark_selection_mark(int x1, int y1, int x2, int y2) { square_to_pixel(&x1, &y1, x1, y1); if (is_bigtiled(x2, y2)) { square_to_pixel(&x2, &y2, x2, y2); XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc, x1, y1, x2-x1+Infofnt->twid - 1, y2-y1+Infofnt->hgt - 1); } else { square_to_pixel(&x2, &y2, x2, y2); XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc, x1, y1, x2-x1+Infofnt->wid - 1, y2-y1+Infofnt->hgt - 1); } } /* * Mark a selection by drawing boxes around it (for now). */ static void mark_selection(void) { co_ord min, max; term *old = Term; bool draw = x11_selection->select; bool clear = x11_selection->drawn; /* Open the correct term if necessary. */ if (x11_selection->t != old) Term_activate(x11_selection->t); if (clear) { sort_co_ord(&min, &max, &x11_selection->init, &x11_selection->old); mark_selection_mark(min.x, min.y, max.x, max.y); } if (draw) { sort_co_ord(&min, &max, &x11_selection->init, &x11_selection->cur); mark_selection_mark(min.x, min.y, max.x, max.y); } /* Finish on the current term. */ if (x11_selection->t != old) Term_activate(old); x11_selection->old.x = x11_selection->cur.x; x11_selection->old.y = x11_selection->cur.y; x11_selection->drawn = x11_selection->select; } /* * Forget a selection for one reason or another. */ static void copy_x11_release(void) { /* Deselect the current selection. */ x11_selection->select = FALSE; /* Remove its graphical represesntation. */ mark_selection(); } /* * Start to select some text on the screen. */ static void copy_x11_start(int x, int y) { if (x11_selection->select) copy_x11_release(); /* Remember where the selection started. */ x11_selection->t = Term; x11_selection->init.x = x11_selection->cur.x = x11_selection->old.x = x; x11_selection->init.y = x11_selection->cur.y = x11_selection->old.y = y; } /* * Respond to movement of the mouse when selecting text. */ static void copy_x11_cont(int x, int y, unsigned int buttons) { /* Use the nearest square within bounds if the mouse is outside. */ x = MIN(MAX(x, 0), Term->wid-1); y = MIN(MAX(y, 0), Term->hgt-1); /* The left mouse button isn't pressed. */ if (~buttons & Button1Mask) return; /* Not a selection in this window. */ if (x11_selection->t != Term) return; /* Not enough movement. */ if ((x == x11_selection->old.x) && (y == x11_selection->old.y) && x11_selection->select) return; /* Something is being selected. */ x11_selection->select = TRUE; /* Track the selection. */ x11_selection->cur.x = x; x11_selection->cur.y = y; /* Hack - display it inefficiently. */ mark_selection(); } /* * Respond to release of the left mouse button by putting the selected text in * the primary buffer. */ static void copy_x11_end(const Time time) { /* No selection. */ if (!x11_selection->select) return; /* Not a selection in this window. */ if (x11_selection->t != Term) return; /* Remember when the selection was finalised. */ x11_selection->time = time; /* Acquire the primary selection. */ XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time); if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win) { /* Failed to acquire the selection, so forget it. */ bell("Failed to acquire primary buffer."); x11_selection->select = FALSE; mark_selection(); } } /* * Send some text requested by another X client */ static void paste_x11_send(XSelectionRequestEvent *rq) { XEvent event; XSelectionEvent *ptr = &(event.xselection); static Atom xa_targets = None; if (xa_targets == None) xa_targets = XInternAtom(DPY, "TARGETS", False); /* Set the event parameters */ ptr->type = SelectionNotify; ptr->property = rq->property; ptr->display = rq->display; ptr->requestor = rq->requestor; ptr->selection = rq->selection; ptr->target = rq->target; ptr->time = rq->time; if (rq->target == xa_targets) { Atom target_list[2]; target_list[0] = xa_targets; target_list[1] = XA_STRING; XChangeProperty(DPY, rq->requestor, rq->property, rq->target, (8 * sizeof(target_list[0])), PropModeReplace, (unsigned char *)target_list, (sizeof(target_list) / sizeof(target_list[0]))); event.xselection.property = rq->property; } else if (rq->target == XA_STRING) { /* Reply to a known target received recently with data */ byte buf[1024]; co_ord max, min; int x, y, i; byte a; char c; /* Work out which way around to paste */ sort_co_ord(&min, &max, &x11_selection->init, &x11_selection->cur); /* Delete the old value of the property */ XDeleteProperty(DPY, rq->requestor, rq->property); for (y = 0; y < Term->hgt; y++) { if (y < min.y) continue; if (y > max.y) break; for (x = i = 0; x < Term->wid; x++) { if (x < min.x) continue; if (x > max.x) break; /* Protect the buffer boundary */ if (i >= (int)(sizeof(buf) - 2)) break; /* Find the character */ Term_what(x, y, &a, &c); /* Add it */ buf[i++] = c; } /* Terminate all but the last line in an appropriate way */ if (y != max.y) buf[i++] = '\n'; /* Send the (non-empty) string */ XChangeProperty(DPY, rq->requestor, rq->property, rq->target, 8, PropModeAppend, buf, i); } } else { /* Respond to all bad requests with property None */ ptr->property = None; } /* Send whatever event we're left with */ XSendEvent(DPY, rq->requestor, FALSE, NoEventMask, &event); } /* * Handle various events conditional on presses of a mouse button. */ static void handle_button(Time time, int x, int y, int button, bool press) { /* The co-ordinates are only used in Angband format. */ pixel_to_square(&x, &y, x, y); if (press && button == 1) copy_x11_start(x, y); if (!press && button == 1) copy_x11_end(time); } /* * Delay resizing/redrawing windows so that don't waste cpu */ static bool event_pending = FALSE; static void scan_pending_windows(void) { term_data *old_td = (term_data*)(Term->data); term_data *td; int i; /* Unset flag */ event_pending = FALSE; /* Scan the windows */ for (i = 0; i < MAX_TERM_DATA; i++) { td = &data[i]; /* Skip nonexistant terms */ if (!td->win) continue; /* Hack -- activate the Term */ Term_activate(&td->t); /* Need to resize? */ if (td->win->resize == TRUE) { int cols, rows, wid, hgt; int ox = Infowin->ox; int oy = Infowin->oy; /* Unset flags */ td->win->resize = FALSE; td->win->redraw = FALSE; /* Hack -- activate the window */ Infowin_set(td->win); /* Determine "proper" number of rows/cols */ cols = ((Infowin->w - (ox + ox)) / td->fnt->wid); rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt); /* Hack -- minimal size */ if (cols < 1) cols = 1; if (rows < 1) rows = 1; /* Hack the main window must be at least 80x24 */ if (i == 0) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Desired size of window */ wid = cols * td->fnt->wid + (ox + ox); hgt = rows * td->fnt->hgt + (oy + oy); /* Resize the windows if any "change" is needed */ if ((Infowin->w != wid) || (Infowin->h != hgt)) { /* Resize window */ Infowin_set(td->win); Infowin_resize(wid, hgt); } /* Resize the Term (if needed) */ (void)Term_resize(cols, rows); } else if (td->win->redraw == TRUE) { /* Unset flag */ td->win->redraw = FALSE; Term_redraw(); } } /* Hack -- Activate the old term */ Term_activate(&old_td->t); /* Hack -- Activate the proper window */ Infowin_set(old_td->win); } /* * Process events */ static errr CheckEvent(bool wait) { term_data *old_td = (term_data*)(Term->data); XEvent xev_body, *xev = &xev_body; term_data *td = NULL; infowin *iwin = NULL; int i; int window = 0; /* No pending events? */ if (!XPending(Metadpy->dpy)) { /* Need to resize/redraw? */ if (event_pending) scan_pending_windows(); /* Do not wait unless requested */ if (!wait) return (1); } /* * Hack - redraw the selection, if needed. * This doesn't actually check that one of its squares was drawn to, * only that this may have happened. */ if (x11_selection->select && !x11_selection->drawn) mark_selection(); /* Load the Event */ XNextEvent(Metadpy->dpy, xev); /* Notice new keymaps */ if (xev->type == MappingNotify) { XRefreshKeyboardMapping(&xev->xmapping); return 0; } /* Scan the windows */ for (i = 0; i < MAX_TERM_DATA; i++) { if (xev->xany.window == data[i].win->win) { td = &data[i]; iwin = td->win; window = i; break; } } /* Unknown window */ if (!td || !iwin) return (0); /* Hack -- activate the Term */ Term_activate(&td->t); /* Hack -- activate the window */ Infowin_set(iwin); /* Switch on the Type */ switch (xev->type) { case ButtonPress: case ButtonRelease: { bool press = (xev->type == ButtonPress); /* Where is the mouse */ int x = xev->xbutton.x; int y = xev->xbutton.y; int z; /* Which button is involved */ if (xev->xbutton.button == Button1) z = 1; else if (xev->xbutton.button == Button2) z = 2; else if (xev->xbutton.button == Button3) z = 3; else if (xev->xbutton.button == Button4) z = 4; else if (xev->xbutton.button == Button5) z = 5; else z = 0; /* XXX Handle */ handle_button(xev->xbutton.time, x, y, z, press); break; } case MotionNotify: { /* Where is the mouse */ int x = xev->xmotion.x; int y = xev->xmotion.y; unsigned int z = xev->xmotion.state; /* Convert to co-ordinates Angband understands. */ pixel_to_square(&x, &y, x, y); /* Alter the selection if appropriate. */ copy_x11_cont(x, y, z); break; } case SelectionRequest: { paste_x11_send(&(xev->xselectionrequest)); break; } case SelectionClear: { x11_selection->select = FALSE; mark_selection(); break; } case KeyRelease: { /* Nothing */ break; } case KeyPress: { /* Hack -- use "old" term */ Term_activate(&old_td->t); /* Process the key */ react_keypress(&(xev->xkey)); break; } case Expose: { int x1, x2, y1, y2; /* Hack - if we have a pending resize, ignore */ if (!event_pending) { pixel_to_square(&x1, &y1, xev->xexpose.x, xev->xexpose.y); pixel_to_square(&x2, &y2, xev->xexpose.x + xev->xexpose.width, xev->xexpose.y + xev->xexpose.height); Term_redraw_section(x1, y1, x2, y2); } else { /* Make sure to redraw later */ event_pending = TRUE; Infowin->redraw = TRUE; } break; } case MapNotify: { Infowin->mapped = 1; Term->mapped_flag = TRUE; break; } case UnmapNotify: { Infowin->mapped = 0; Term->mapped_flag = FALSE; break; } /* Move and/or Resize */ case ConfigureNotify: { int cols, rows, wid, hgt; int ox = Infowin->ox; int oy = Infowin->oy; /* Save the new Window Parms */ Infowin->x = xev->xconfigure.x; Infowin->y = xev->xconfigure.y; if ((Infowin->w != xev->xconfigure.width) || (Infowin->h != xev->xconfigure.height)) { Infowin->w = xev->xconfigure.width; Infowin->h = xev->xconfigure.height; /* We need to notice the resize of this window (later) */ Infowin->resize = TRUE; event_pending = TRUE; } /* Determine "proper" number of rows/cols */ cols = ((Infowin->w - (ox + ox)) / td->fnt->wid); rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt); /* Hack -- minimal size */ if (cols < 1) cols = 1; if (rows < 1) rows = 1; /* Hack the main window must be at least 80x24 */ if (i == 0) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Desired size of window */ wid = cols * td->fnt->wid + (ox + ox); hgt = rows * td->fnt->hgt + (oy + oy); /* Resize the windows if any "change" is needed */ if ((Infowin->w != wid) || (Infowin->h != hgt)) { /* Resize window */ Infowin_set(td->win); Infowin_resize(wid, hgt); } break; } } /* Hack -- Activate the old term */ Term_activate(&old_td->t); /* Hack -- Activate the proper window */ Infowin_set(old_td->win); /* Success */ return (0); } /* * Handle "activation" of a term */ static errr Term_xtra_x11_level(int v) { term_data *td = (term_data*)(Term->data); /* Handle "activate" */ if (v) { /* Activate the window */ Infowin_set(td->win); /* Activate the font */ Infofnt_set(td->fnt); } /* Success */ return (0); } /* * React to changes */ static errr Term_xtra_x11_react(void) { int i; if (Metadpy->color) { /* Check the colors */ for (i = 0; i < 256; i++) { if ((color_table[i][0] != angband_color_table[i][0]) || (color_table[i][1] != angband_color_table[i][1]) || (color_table[i][2] != angband_color_table[i][2]) || (color_table[i][3] != angband_color_table[i][3])) { Pixell pixel; /* Save new values */ color_table[i][0] = angband_color_table[i][0]; color_table[i][1] = angband_color_table[i][1]; color_table[i][2] = angband_color_table[i][2]; color_table[i][3] = angband_color_table[i][3]; /* Create pixel */ pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]); /* Change the foreground */ Infoclr_set(clr[i]); Infoclr_change_fg(pixel); } } } /* Success */ return (0); } /* * Handle a "special request" */ static errr Term_xtra_x11(int n, int v) { /* Handle a subset of the legal requests */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: Metadpy_do_beep(); return (0); /* Flush the output XXX XXX */ case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0); /* Process random events XXX */ case TERM_XTRA_BORED: return (CheckEvent(0)); /* Process Events XXX */ case TERM_XTRA_EVENT: return (CheckEvent(v)); /* Flush the events XXX */ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0); /* Handle change in the "level" */ case TERM_XTRA_LEVEL: return (Term_xtra_x11_level(v)); /* Delay for some milliseconds */ case TERM_XTRA_DELAY: if (v > 0) usleep(1000 * v); return (0); /* React to changes */ case TERM_XTRA_REACT: return (Term_xtra_x11_react()); } /* Unknown */ return (1); } /* * Draw the cursor as a rectangular outline */ static errr Term_curs_x11(int x, int y) { int x1, y1; square_to_pixel(&x1, &y1, x, y); if (is_bigtiled(x, y)) { XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc, x1, y1, Infofnt->twid - 1, Infofnt->hgt - 1); } else { XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc, x1, y1, Infofnt->wid - 1, Infofnt->hgt - 1); } /* Success */ return (0); } /* * Erase some characters. */ static errr Term_wipe_x11(int x, int y, int n) { /* Erase (use black) */ Infoclr_set(clr[TERM_DARK]); /* Erase some space */ Infofnt_text_non(x, y, n); /* Redraw the selection if any, as it may have been obscured. (later) */ x11_selection->drawn = FALSE; /* Success */ return (0); } /* * Draw some textual characters. */ static errr Term_text_x11(int x, int y, int n, byte a, cptr s) { /* Erase area first */ Term_wipe_x11(x, y, n); /* Draw the text */ Infoclr_set(clr[a]); /* Draw the text */ Infofnt_text_std(x, y, s, n); /* Redraw the selection if any, as it may have been obscured. (later) */ x11_selection->drawn = FALSE; /* Success */ return (0); } #ifdef USE_GRAPHICS /* * Draw some graphical characters. */ static errr Term_pict_x11(int ox, int oy, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i; int x1 = 0, y1 = 0; byte a; char c; byte ta; char tc; int x, y; int x2, y2; int k,l; unsigned long pixel, blank; term_data *td = (term_data*)(Term->data); int wid, hgt = td->fnt->hgt; XImage *tiles; /* Starting point */ square_to_pixel(&x, &y, ox, oy); for (i = 0; i < n; ++i) { a = *ap++; c = *cp++; ta = *tap++; tc = *tcp++; /* What are we drawing? */ if (is_bigtiled(ox + i, oy)) { tiles = td->b_tiles; wid = td->fnt->twid; } else { tiles = td->tiles; wid = td->fnt->wid; } /* For extra speed - cache these values */ x1 = (c & 0x7F) * wid; y1 = (a & 0x7F) * hgt; /* For extra speed - cache these values */ x2 = (tc & 0x7F) * wid; y2 = (ta & 0x7F) * hgt; /* Optimise the common case */ if (((x1 == x2) && (y1 == y2)) || !(((byte)ta & 0x80) && ((byte)tc & 0x80))) { /* Draw object / terrain */ XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, tiles, x1, y1, x, y, wid, hgt); } else { /* Mega Hack^2 - assume the top left corner is "blank" */ if (arg_graphics == GRAPHICS_DAVID_GERVAIS) blank = XGetPixel(tiles, 0, 0); else blank = XGetPixel(tiles, 0, hgt * 6); for (k = 0; k < wid; k++) { for (l = 0; l < hgt; l++) { /* If mask set... */ if ((pixel = XGetPixel(tiles, x1 + k, y1 + l)) == blank) { /* Output from the terrain */ pixel = XGetPixel(tiles, x2 + k, y2 + l); if (pixel == blank) pixel = 0L; } /* Store into the temp storage. */ XPutPixel(td->TmpImage, k, l, pixel); } } /* Draw to screen */ XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->TmpImage, 0, 0, x, y, wid, hgt); } x += wid; } /* Redraw the selection if any, as it may have been obscured. (later) */ x11_selection->drawn = FALSE; /* Success */ return (0); } #endif /* USE_GRAPHICS */ /* * Initialize a term_data */ static errr term_data_init(term_data *td, int i) { term *t = &td->t; cptr name = angband_term_name[i]; cptr font; int x = 0; int y = 0; int cols = 80; int rows = 24; int ox = 1; int oy = 1; int wid, hgt, num; char buf[80]; cptr str; int val; XClassHint *ch; char res_name[20]; char res_class[20]; XSizeHints *sh; /* Get default font for this term */ font = get_default_font(i); /* Window specific location (x) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_AT_X_%d", i); str = getenv(buf); x = (str != NULL) ? atoi(str) : -1; /* Window specific location (y) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_AT_Y_%d", i); str = getenv(buf); y = (str != NULL) ? atoi(str) : -1; /* Window specific cols */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_COLS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) cols = val; /* Window specific rows */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_ROWS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) rows = val; /* Hack the main window must be at least 80x24 */ if (!i) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Window specific inner border offset (ox) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOX_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) ox = val; /* Window specific inner border offset (oy) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOY_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) oy = val; /* Prepare the standard font */ MAKE(td->fnt, infofnt); Infofnt_set(td->fnt); if (Infofnt_init_data(font)) { quit("Stopping"); } /* Hack -- key buffer size */ num = ((i == 0) ? 1024 : 16); /* Assume full size windows */ wid = cols * td->fnt->wid + (ox + ox); hgt = rows * td->fnt->hgt + (oy + oy); /* Create a top-window */ MAKE(td->win, infowin); Infowin_set(td->win); Infowin_init_top(x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg); /* Ask for certain events */ Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); /* Set the window name */ Infowin_set_name(name); /* Save the inner border */ Infowin->ox = ox; Infowin->oy = oy; /* Make Class Hints */ ch = XAllocClassHint(); if (ch == NULL) quit("XAllocClassHint failed"); strnfmt(res_name, sizeof(res_name), "%s", name); res_name[0] = FORCELOWER(res_name[0]); ch->res_name = res_name; strnfmt(res_class, sizeof(res_class), "Angband"); ch->res_class = res_class; XSetClassHint(Metadpy->dpy, Infowin->win, ch); /* Make Size Hints */ sh = XAllocSizeHints(); /* Oops */ if (sh == NULL) quit("XAllocSizeHints failed"); /* Main window has a differing minimum size */ if (i == 0) { /* Main window min size is 80x24 */ sh->flags = PMinSize | PMaxSize; sh->min_width = 80 * td->fnt->wid + (ox + ox); sh->min_height = 24 * td->fnt->hgt + (oy + oy); sh->max_width = 255 * td->fnt->wid + (ox + ox); sh->max_height = 255 * td->fnt->hgt + (oy + oy); } /* Other windows can be shrunk to 1x1 */ else { /* Other windows */ sh->flags = PMinSize | PMaxSize; sh->min_width = td->fnt->wid + (ox + ox); sh->min_height = td->fnt->hgt + (oy + oy); sh->max_width = 255 * td->fnt->wid + (ox + ox); sh->max_height = 255 * td->fnt->hgt + (oy + oy); } /* Resize increment */ sh->flags |= PResizeInc; sh->width_inc = td->fnt->wid; sh->height_inc = td->fnt->hgt; /* Base window size */ sh->flags |= PBaseSize; sh->base_width = (ox + ox); sh->base_height = (oy + oy); /* Use the size hints */ XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh); /* Map the window */ Infowin_map(); /* Move the window to requested location */ if ((x >= 0) && (y >= 0)) Infowin_impell(x, y); /* Initialize the term */ term_init(t, cols, rows, num); /* Use a "soft" cursor */ t->soft_cursor = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Hooks */ t->xtra_hook = Term_xtra_x11; t->curs_hook = Term_curs_x11; t->wipe_hook = Term_wipe_x11; t->text_hook = Term_text_x11; /* Save the data */ t->data = td; /* Activate (important) */ Term_activate(t); /* Success */ return (0); } /* * Initialization function for an "X11" module to Angband */ errr init_x11(int argc, char *argv[]) { int i; cptr dpy_name = ""; int num_term = 3; #ifdef USE_GRAPHICS char filename[1024]; int pict_wid = 0; int pict_hgt = 0; int graphmode = GRAPHICS_ANY; char *TmpData; #endif /* USE_GRAPHICS */ /* Parse args */ for (i = 1; i < argc; i++) { if (prefix(argv[i], "-d")) { dpy_name = &argv[i][2]; continue; } #ifdef USE_GRAPHICS if (prefix(argv[i], "-s")) { smoothRescaling = FALSE; continue; } if (prefix(argv[i], "-b")) { int bitdepth = 0; bitdepth = atoi(&argv[i][2]); /* Paranoia */ if (bitdepth == 32) graphmode = GRAPHICS_DAVID_GERVAIS; if (bitdepth == 16) graphmode = GRAPHICS_ADAM_BOLT; if (bitdepth == 8) graphmode = GRAPHICS_ORIGINAL; continue; } #endif /* USE_GRAPHICS */ if (prefix(argv[i], "-n")) { num_term = atoi(&argv[i][2]); if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA; else if (num_term < 1) num_term = 1; continue; } plog_fmt("Ignoring option: %s", argv[i]); } /* Init the Metadpy if possible */ if (Metadpy_init_name(dpy_name)) return (-1); #ifdef USE_GRAPHICS /* We support bigtile mode */ if (arg_bigtile && arg_graphics) use_bigtile = TRUE; #endif /* USE_GRAPHICS */ /* Prepare cursor color */ MAKE(xor, infoclr); Infoclr_set(xor); Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0); /* Prepare normal colors */ for (i = 0; i < 256; ++i) { Pixell pixel; MAKE(clr[i], infoclr); Infoclr_set(clr[i]); /* Acquire Angband colors */ color_table[i][0] = angband_color_table[i][0]; color_table[i][1] = angband_color_table[i][1]; color_table[i][2] = angband_color_table[i][2]; color_table[i][3] = angband_color_table[i][3]; /* Default to monochrome */ pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg); /* Handle color */ if (Metadpy->color) { /* Create pixel */ pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]); } /* Initialize the color */ Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0); } /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; /* Initialize the term_data */ term_data_init(td, i); /* Save global entry */ angband_term[i] = Term; } #ifdef USE_GRAPHICS /* Try graphics */ if (arg_graphics) { (void) pick_graphics(graphmode, &pict_wid, &pict_hgt, filename); } /* Load graphics */ if (use_graphics) { Display *dpy = Metadpy->dpy; XImage *tiles_raw; /* Initialize */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; td->tiles = NULL; } /* Load the graphical tiles */ tiles_raw = ReadBMP(dpy, filename); if (tiles_raw) { /* Initialize the windows */ for (i = 0; i < num_term; i++) { int j; term_data *td = &data[i]; term_data *o_td = NULL; term *t = &td->t; /* Graphics hook */ t->pict_hook = Term_pict_x11; /* Use graphics sometimes */ t->higher_pict = TRUE; /* Look for another term with same font size */ for (j = 0; j < i; j++) { o_td = &data[j]; if ((td->fnt->wid == o_td->fnt->wid) && (td->fnt->hgt == o_td->fnt->hgt)) { /* Use same graphics */ td->tiles = o_td->tiles; td->b_tiles = o_td->b_tiles; break; } } /* Resize Tiles */ if (!td->tiles) { if (arg_bigtile) { td->b_tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->fnt->twid, td->fnt->hgt); } else { td->b_tiles = NULL; } td->tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->fnt->wid, td->fnt->hgt); } } /* Free tiles_raw */ FREE(tiles_raw); } /* Initialize the transparency masks */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; int ii, jj; int depth = DefaultDepth(dpy, DefaultScreen(dpy)); Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int total; /* Determine total bytes needed for image */ ii = 1; jj = (depth - 1) >> 2; while (jj >>= 1) ii <<= 1; /* Pad the scanline to a multiple of 4 bytes */ total = td->fnt->twid * ii; total = (total + 3) & ~3; total *= td->fnt->hgt; TmpData = (char *)malloc(total); td->TmpImage = XCreateImage(dpy,visual,depth, ZPixmap, 0, TmpData, td->fnt->twid, td->fnt->hgt, 32, 0); } } #endif /* USE_GRAPHICS */ /* Raise the "Angband" window */ Infowin_set(data[0].win); Infowin_raise(); /* Activate the "Angband" window screen */ Term_activate(&data[0].t); /* Success */ return (0); } #endif /* USE_X11 */ zangband/src/main-xaw.c0000755000000000000000000012706010250356274014031 0ustar rootroot/* File: main-xaw.c */ /* * Copyright (c) 1997 Ben Harrison, Torbjorn Lindgren, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with UNIX/X11 computers. * * To use this file, compile with "USE_XAW" defined, and link against all * the various "X11" libraries which may be needed. * * See also "main-x11.c". * * The Angband widget is not as self-contained as it really should be. * Originally everything was output to a Pixmap which was later copied * to the screen when necessary. The idea was abandoned since Pixmaps * create big performance problems for some really old X terminals (such * as 3/50's running Xkernel). * * Initial framework (and some code) by Ben Harrison (benh@phial.com). * * Most of this file is by Torbjorn Lindgren (tl@cd.chalmers.se). * * Major modifications by Ben Harrison (benh@phial.com). */ #include "angband.h" #ifdef USE_XAW cptr help_xaw[] = { "To use XAW", "-d Set display name", #ifdef USE_GRAPHICS "-s Turn off smoothscaling graphics", "-b# Set tileset bitmap", #endif /* USE_GRAPHICS */ "-n# Number of terms to use", NULL }; #ifndef __MAKEDEPEND__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __MAKEDEPEND__ */ /* * Include some helpful X11 code. */ #include "maid-x11.h" /**** Resources ****/ /* Name Class RepType Default Value ---- ----- ------- ------------- background Background Pixel XtDefaultBackground border BorderColor Pixel XtDefaultForeground borderWidth BorderWidth Dimension 1 cursor Cursor Cursor None cursorName Cursor String NULL destroyCallback Callback Pointer NULL height Height Dimension 0 insensitiveBorder Insensitive Pixmap Gray mappedWhenManaged MappedWhenManaged Boolean True pointerColor Foreground Pixel XtDefaultForeground pointerColorBackground Background Pixel XtDefaultBackground sensitive Sensitive Boolean True width Width Dimension 0 x Position Position 0 y Position Position 0 The colors can be changed using the standard Angband user pref files, which can also be used to provide black text on a white background, by setting color zero to "#FFFFFF" and color one to "#000000", since the other colors are unused. */ /* * New resource names */ #define XtNstartRows "startRows" #define XtNstartColumns "startColumns" #define XtNminRows "minRows" #define XtNminColumns "minColumns" #define XtNmaxRows "maxRows" #define XtNmaxColumns "maxColumns" #define XtNinternalBorder "internalBorder" #define XtNredrawCallback "redrawCallback" /* * Total normal colors */ #define NUM_COLORS 256 /* * Special "XOR" color */ #define COLOR_XOR 256 /**** The Widget Code ****/ /* * Forward declarations */ typedef struct AngbandPart AngbandPart; typedef struct AngbandRec *AngbandWidget; typedef struct AngbandRec AngbandRec; typedef struct AngbandClassRec *AngbandWidgetClass; typedef struct AngbandClassPart AngbandClassPart; typedef struct AngbandClassRec AngbandClassRec; typedef struct term_data term_data; /* * A structure for each "term" */ struct term_data { term t; AngbandWidget widget; }; /* * Maximum number of windows */ #define MAX_TERM_DATA 8 /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Current number of windows open */ static int num_term = 3; /* * New fields for the Angband widget record */ struct AngbandPart { /* Settable resources */ int start_rows; int start_columns; int min_rows; int min_columns; int max_rows; int max_columns; int internal_border; String font; XtCallbackList redraw_callbacks; #ifdef USE_GRAPHICS /* Tiles */ XImage *tiles; XImage *b_tiles; /* Tempory storage for overlaying tiles. */ XImage *TmpImage; #endif /* USE_GRAPHICS */ /* Private state */ XFontStruct *fnt; Dimension fontheight; Dimension fontwidth; Dimension tilewidth; Dimension fontascent; /* Color info for GC's */ byte color[NUM_COLORS][4]; /* GC's (including "xor") */ GC gc[NUM_COLORS+1]; }; /* * Full instance record declaration */ struct AngbandRec { CorePart core; SimplePart simple; AngbandPart angband; }; /* * New fields for the Angband widget class record */ struct AngbandClassPart { int dummy; }; /* * Full class record declaration */ struct AngbandClassRec { CoreClassPart core_class; SimpleClassPart simple_class; AngbandClassPart angband_class; }; /* * Hack -- see below */ #define offset(field) XtOffsetOf(AngbandRec, angband.field) /* * Fallback resources for Angband widget */ static XtResource resources[] = { { (String) XtNstartRows, (String) XtCValue, (String) XtRInt, sizeof(int), offset(start_rows), XtRImmediate, (XtPointer) 24 }, { (String) XtNstartColumns, (String) XtCValue, (String) XtRInt, sizeof(int), offset(start_columns), XtRImmediate, (XtPointer) 80 }, { (String) XtNminRows, (String) XtCValue, (String) XtRInt, sizeof(int), offset(min_rows), XtRImmediate, (XtPointer) 1 }, { (String) XtNminColumns, (String) XtCValue, (String) XtRInt, sizeof(int), offset(min_columns), XtRImmediate, (XtPointer) 1 }, { (String) XtNmaxRows, (String) XtCValue, (String) XtRInt, sizeof(int), offset(max_rows), XtRImmediate, (XtPointer) 24 }, { (String) XtNmaxColumns, (String) XtCValue, (String) XtRInt, sizeof(int), offset(max_columns), XtRImmediate, (XtPointer) 80 }, { (String) XtNinternalBorder, (String) XtCValue, (String) XtRInt, sizeof(int), offset(internal_border), XtRImmediate, (XtPointer) 2 }, { (String) XtNfont, (String) XtCFont, (String) XtRString, sizeof(char *), offset(font), XtRString, (String) DEFAULT_X11_FONT }, { (String) XtNredrawCallback, (String) XtCCallback, (String) XtRCallback, sizeof(XtPointer), offset(redraw_callbacks), XtRCallback, (XtPointer)NULL } }; /* * Hack -- see above */ #undef offset /* * Forward declarations for Widget functions */ static void Initialize(AngbandWidget request, AngbandWidget wnew); static void Redisplay(AngbandWidget w, XEvent *event, Region region); static Boolean SetValues(AngbandWidget current, AngbandWidget request, AngbandWidget wnew, ArgList args, Cardinal *num_args); static void Destroy(AngbandWidget widget); static void Resize_term(AngbandWidget wnew); /* * Forward declaration for internal functions */ static void calculateSizeHints(AngbandWidget wnew); static XFontStruct *getFont(AngbandWidget widget, String font, Boolean fallback); /* * Hack -- see below */ #define superclass (&simpleClassRec) /* * Class record constanst */ AngbandClassRec angbandClassRec = { { /* Core class fields initialization */ /* superclass */ (WidgetClass) superclass, /* class_name */ (String) "Angband", /* widget_size */ sizeof(AngbandRec), /* class_initialize */ NULL, /* class_part_initialize*/ NULL, /* class_inited */ FALSE, /* initialize */ (XtInitProc) Initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ NULL, /* num_actions */ 0, /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ TRUE, /* compress_exposure */ XtExposeCompressMultiple, /* compress_enterleave */ TRUE, /* visible_interest */ FALSE, /* destroy */ (XtWidgetProc) Destroy, /* resize */ (XtWidgetProc) Resize_term, /* expose */ (XtExposeProc) Redisplay, /* set_values */ (XtSetValuesFunc) SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ NULL, /* display_accelerator */ XtInheritDisplayAccelerator, /* extension */ NULL }, /* Simple class fields initialization */ { /* change_sensitive */ XtInheritChangeSensitive, #if XawVersion >= 7000000L /* extension */ NULL #endif /* XawVersion >= 7000000L */ }, /* Angband class fields initialization */ { /* nothing */ 0 } }; /* * Hack -- see above */ #undef superclass /* * Class record pointer */ WidgetClass angbandWidgetClass = (WidgetClass) &angbandClassRec; /* * Public procedures */ /* * Find the square a particular pixel is part of. */ static void pixel_to_square(AngbandWidget widget, int *x, int *y, int ox, int oy) { (*x) = (ox - widget->angband.internal_border) / widget->angband.fontwidth; (*y) = (oy - widget->angband.internal_border) / widget->angband.fontheight; if ((use_bigtile) && ((*y) >= Term->scr->big_y1) && ((*y) <= Term->scr->big_y2) && ((*x) >= Term->scr->big_x1)) { (*x) -= ((*x) - Term->scr->big_x1 + 1) / 2; } } /* * Find the pixel at the top-left corner of a square. */ static void square_to_pixel(AngbandWidget widget, int *x, int *y, int ox, int oy) { (*y) = oy * widget->angband.fontheight + widget->angband.internal_border; if ((use_bigtile) && (oy >= Term->scr->big_y1) && (oy <= Term->scr->big_y2) && (ox > Term->scr->big_x1)) { (*x) = ox * widget->angband.tilewidth + widget->angband.internal_border - Term->scr->big_x1 * widget->angband.fontwidth; } else { (*x) = ox * widget->angband.fontwidth + widget->angband.internal_border; } } /* * Clear an area */ static void AngbandClearArea(AngbandWidget widget, int x, int y, int w) { int x1, y1, x2, y2; /*** Find the dimensions ***/ square_to_pixel(widget, &x1, &y1, x, y); square_to_pixel(widget, &x2, &y2, x + w, y); /* Clear the area */ XFillRectangle(XtDisplay(widget), XtWindow(widget), widget->angband.gc[0], x1, y1, x2 - x1, widget->angband.fontheight); } /* * Output some text */ static void AngbandOutputText(AngbandWidget widget, int cx, int cy, String txt, int len, int a) { int x, y; int i; /* Do nothing if the string is null */ if (!txt || !*txt) return; /* Check the length, and fix it if it's below zero */ if (len < 0) len = strlen(txt); /* Figure out where to place the text */ square_to_pixel(widget, &x, &y, cx, cy); /* Ignore Vertical Justifications */ y += widget->angband.fontascent; /* Monotize the font if required */ if (is_bigtiled(cx + len - 1, cy)) { /* Do each character */ for (i = 0; i < len; ++i) { if (is_bigtiled(cx + i, cy)) { /* Place a character */ XDrawImageString(XtDisplay(widget), XtWindow(widget), widget->angband.gc[a], x, y, txt, 1); x += widget->angband.tilewidth; } else { /* Place a character */ XDrawImageString(XtDisplay(widget), XtWindow(widget), widget->angband.gc[a], x, y, txt, 1); x += widget->angband.fontwidth; } } } /* Assume monoospaced font */ else { /* Place the string */ XDrawImageString(XtDisplay(widget), XtWindow(widget), widget->angband.gc[a], x, y, txt, len); } } #ifdef USE_GRAPHICS /* * Draw some graphical characters. */ static void AngbandOutputPict(AngbandWidget widget, int ox, int oy, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i, x1, y1; byte a; char c; byte ta; char tc; int x, y; int x2, y2; int k,l; unsigned long pixel, blank; int wid, hgt = widget->angband.fontheight; XImage *tiles; /* Starting point */ square_to_pixel(widget, &x, &y, ox, oy); for (i = 0; i < n; ++i) { a = *ap++; c = *cp++; ta = *tap++; tc = *tcp++; if (is_bigtiled(ox + i, oy)) { tiles = widget->angband.b_tiles; wid = widget->angband.tilewidth; } else { tiles = widget->angband.tiles; wid = widget->angband.fontwidth; } /* For extra speed - cache these values */ x1 = (c & 0x7F) * wid; y1 = (a & 0x7F) * hgt; /* For extra speed - cache these values */ x2 = (tc & 0x7F) * wid; y2 = (ta & 0x7F) * hgt; /* Optimise the common case */ if (((x1 == x2) && (y1 == y2)) || !(((byte)ta & 0x80) && ((byte)tc & 0x80))) { /* Draw object / terrain */ XPutImage(XtDisplay(widget), XtWindow(widget), widget->angband.gc[0], tiles, x1, y1, x, y, wid, hgt); } else { /* Mega Hack^2 - assume the top left corner is "blank" */ if (arg_graphics == GRAPHICS_DAVID_GERVAIS) blank = XGetPixel(tiles, 0, 0); else blank = XGetPixel(tiles, 0, hgt * 6); for (k = 0; k < wid; k++) { for (l = 0; l < hgt; l++) { /* If mask set... */ if ((pixel = XGetPixel(tiles, x1 + k, y1 + l)) == blank) { /* Output from the terrain */ pixel = XGetPixel(tiles, x2 + k, y2 + l); if (pixel == blank) pixel = 0L; } /* Store into the temp storage. */ XPutPixel(widget->angband.TmpImage, k, l, pixel); } } /* Draw to screen */ /* Draw object / terrain */ XPutImage(XtDisplay(widget), XtWindow(widget), widget->angband.gc[0], widget->angband.TmpImage, 0, 0, x, y, wid, hgt); } x += wid; } } #endif /* USE_GRAPHICS */ /* * Private procedures */ /* * Procedure Initialize() is called during the widget creation * process. Initialize() load fonts and calculates window geometry. * The request parameter is filled in by parents to this widget. The * wnew parameter is the request parameter plus data filled in by this * widget. All changes should be done to the wnew parameter. */ static void Initialize(AngbandWidget request, AngbandWidget wnew) { Display *dpy = XtDisplay(wnew); int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew)); XGCValues gcv; TopLevelShellWidget parent = (TopLevelShellWidget)XtParent((Widget) wnew); int i; /* Default background pixel */ unsigned long bg = create_pixel(dpy, angband_color_table[0][1], angband_color_table[0][2], angband_color_table[0][3]); /* Default foreground pixel */ unsigned long fg = create_pixel(dpy, angband_color_table[1][1], angband_color_table[1][2], angband_color_table[1][3]); /* Ignore this parameter */ (void) request; /* Fix the background color */ wnew->core.background_pixel = bg; /* Get some information about the font */ wnew->angband.fnt = getFont(wnew, wnew->angband.font, TRUE); wnew->angband.fontheight = wnew->angband.fnt->ascent + wnew->angband.fnt->descent; wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width; wnew->angband.fontascent = wnew->angband.fnt->ascent; if (use_bigtile) wnew->angband.tilewidth = 2 * wnew->angband.fontwidth; else wnew->angband.tilewidth = wnew->angband.fontwidth; wnew->angband.fontascent = wnew->angband.fnt->ascent; /* Create and initialize the graphics contexts */ /* GXset? */ gcv.font = wnew->angband.fnt->fid; gcv.graphics_exposures = FALSE; gcv.background = bg; for (i = 0; i < NUM_COLORS; i++) { unsigned long pixel; /* Acquire Angband colors */ wnew->angband.color[i][0] = angband_color_table[i][0]; wnew->angband.color[i][1] = angband_color_table[i][1]; wnew->angband.color[i][2] = angband_color_table[i][2]; wnew->angband.color[i][3] = angband_color_table[i][3]; if (depth > 1) { /* Create pixel */ pixel = create_pixel(dpy, wnew->angband.color[i][1], wnew->angband.color[i][2], wnew->angband.color[i][3]); } else { /* Use background or foreground */ pixel = ((i == 0) ? bg : fg); } gcv.foreground = pixel; /* Copy */ gcv.function = 3; wnew->angband.gc[i] = XtGetGC((Widget)wnew, (GCFont | GCForeground | GCFunction | GCBackground | GCGraphicsExposures), &gcv); } /* Create a special GC for highlighting */ gcv.foreground = (BlackPixelOfScreen(XtScreen((Widget)wnew)) ^ WhitePixelOfScreen(XtScreen((Widget)wnew))); gcv.background = 0; gcv.function = GXxor; wnew->angband.gc[COLOR_XOR] = XtGetGC((Widget)wnew, (GCFunction | GCForeground | GCBackground | GCGraphicsExposures), &gcv); /* Calculate window geometry */ wnew->core.height = (wnew->angband.start_rows * wnew->angband.fontheight + 2 * wnew->angband.internal_border); wnew->core.width = (wnew->angband.start_columns * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* We need to be able to resize the Widget if the user wants to */ /* change font on the fly! */ parent->shell.allow_shell_resize = TRUE; /* Calculates all the size hints */ calculateSizeHints(wnew); } /* * Procedure Destroy() is called during the destruction of the widget. * Destroy() releases and frees GCs, frees the pixmaps and frees the * fonts. */ static void Destroy(AngbandWidget widget) { int n; /* Free all GC's */ for (n = 0; n < NUM_COLORS + 1; n++) { XtReleaseGC((Widget)widget, widget->angband.gc[n]); } /* Free the font */ XFreeFont(XtDisplay((Widget)widget), widget->angband.fnt); } static void Resize_term(AngbandWidget wnew) { int cols, rows, wid, hgt; int ox = wnew->angband.internal_border; int oy = wnew->angband.internal_border; int i; term_data *old_td = (term_data*)(Term->data); term_data *td = &data[0]; /* Hack - Find the term to activate */ for (i = 0; i < num_term; i++) { td = &data[i]; /* Have we found it? */ if (td->widget == wnew) break; /* Paranoia: none of the widgets matched */ if (!td) return; } /* Activate the proper Term */ Term_activate(&td->t); /* Determine "proper" number of rows/cols */ cols = ((wnew->core.width - (ox + ox)) / wnew->angband.fontwidth); rows = ((wnew->core.height - (oy + oy)) / wnew->angband.fontheight); /* Hack -- minimal size */ if (cols < 1) cols = 1; if (rows < 1) rows = 1; if (i == 0) { /* Hack the main window must be at least 80x24 */ if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Desired size of window */ wid = cols * wnew->angband.fontwidth + (ox + ox); hgt = rows * wnew->angband.fontheight + (oy + oy); /* Resize the Term (if needed) */ (void) Term_resize(cols, rows); /* Activate the old term */ Term_activate(&old_td->t); } /* * Procedure Redisplay() is called as the result of an Expose event. * Use the redraw callback to do a full redraw */ static void Redisplay(AngbandWidget wnew, XEvent *xev, Region region) { int x1, x2, y1, y2; int i; term_data *old_td = (term_data*)(Term->data); term_data *td = &data[0]; /* Ignore parameter */ (void) region; /* Hack - Find the term to activate */ for (i = 0; i < num_term; i++) { td = &data[i]; /* Have we found it? */ if (td->widget == wnew) break; /* Paranoia: none of the widgets matched */ if (!td) return; } /* Activate the proper Term */ Term_activate(&td->t); /* Find the bounds of the exposed region */ pixel_to_square(wnew, &x1, &y1, xev->xexpose.x, xev->xexpose.y); pixel_to_square(wnew, &x2, &y2, xev->xexpose.x + xev->xexpose.width, xev->xexpose.y + xev->xexpose.height); Term_redraw_section(x1, y1, x2, y2); /* Activate the old term */ Term_activate(&old_td->t); #if 0 if (XtHasCallbacks((Widget)widget, XtNredrawCallback) == XtCallbackHasSome) { XtCallCallbacks((Widget)widget, XtNredrawCallback, NULL); } #endif /* 0 */ } /* * Font and internal_border can be changed on the fly. * * The entire widget is redrawn if any of those parameters change (all * can potentially have effects that spans the whole widget). * * Color changes are handled elsewhere. * * This function is very underspecified, in terms of how these changes can * occur, and what is true about the various AngbandWidget's passed in. It * is very likely that this code no longer works. */ static Boolean SetValues(AngbandWidget current, AngbandWidget request, AngbandWidget wnew, ArgList args, Cardinal *num_args) { Display *dpy = XtDisplay(wnew); Boolean font_changed = FALSE; Boolean border_changed = FALSE; int height, width; int i; /* Ignore parameters */ (void) request; (void) args; (void) num_args; /* Handle font change */ if (wnew->angband.font != current->angband.font) { /* Check if the font exists */ wnew->angband.fnt = getFont(wnew, wnew->angband.font, FALSE); /* The font didn't exist */ if (wnew->angband.fnt == NULL) { wnew->angband.fnt = current->angband.fnt; wnew->angband.font = current->angband.font; XtWarning("Couldn't find the requested font!"); } else { font_changed = TRUE; /* Free the old font */ XFreeFont(XtDisplay((Widget)wnew), current->angband.fnt); /* Update font information */ wnew->angband.fontheight = wnew->angband.fnt->ascent + wnew->angband.fnt->descent; wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width; wnew->angband.fontascent = wnew->angband.fnt->ascent; } } /* Handle font change */ if (font_changed) { /* Update all GC's */ for (i = 0; i < NUM_COLORS; i++) { /* Steal the old GC */ wnew->angband.gc[i] = current->angband.gc[i]; current->angband.gc[i] = NULL; /* Be sure the correct font is ready */ XSetFont(dpy, wnew->angband.gc[i], wnew->angband.fnt->fid); /* Steal the old GC */ wnew->angband.gc[NUM_COLORS] = current->angband.gc[NUM_COLORS]; current->angband.gc[NUM_COLORS] = NULL; } } /* Check if internal border width has changed, used later */ if (current->angband.internal_border != wnew->angband.internal_border) { border_changed = TRUE; } /* If the font or the internal border has changed, all geometry */ /* has to be recalculated */ if (font_changed || border_changed) { /* Change window size */ height = ((current->core.height - 2 * current->angband.internal_border) / current->angband.fontheight * wnew->angband.fontheight + 2 * current->angband.internal_border); width = ((current->core.width - 2 * current->angband.internal_border) / current->angband.fontwidth * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* Get the new width */ if (XtMakeResizeRequest((Widget)wnew, width, height, NULL, NULL) == XtGeometryNo) { /* Not allowed */ XtWarning("Size change denied!"); } else { /* Recalculate size hints */ calculateSizeHints(wnew); } } /* Tell it to redraw the widget if anything has changed */ return (font_changed || border_changed); } /* * Calculate size hints */ static void calculateSizeHints(AngbandWidget wnew) { TopLevelShellWidget parent = (TopLevelShellWidget)XtParent((Widget) wnew); /* Calculate minimum size */ parent->wm.size_hints.min_height = (wnew->angband.min_rows * wnew->angband.fontheight + 2 * wnew->angband.internal_border); /* Calculate minimum size */ parent->wm.size_hints.min_width = (wnew->angband.min_columns * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* Calculate minimum size */ parent->wm.size_hints.flags |= PMinSize; /* Calculate maximum size */ parent->wm.size_hints.max_height = (wnew->angband.max_rows * wnew->angband.fontheight + 2 * wnew->angband.internal_border); /* Calculate maximum size */ parent->wm.size_hints.max_width = (wnew->angband.max_columns * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* Calculate maximum size */ parent->wm.size_hints.flags |= PMaxSize; /* Calculate increment size */ parent->wm.size_hints.height_inc = wnew->angband.fontheight; parent->wm.size_hints.width_inc = wnew->angband.fontwidth; parent->wm.size_hints.flags |= PResizeInc; /* Calculate base size */ parent->wm.base_height = 2 * wnew->angband.internal_border; parent->wm.base_width = 2 * wnew->angband.internal_border; parent->wm.size_hints.flags |= PBaseSize; } /* * Load a font */ static XFontStruct *getFont(AngbandWidget widget, String font, Boolean fallback) { Display *dpy = XtDisplay((Widget) widget); char buf[256]; XFontStruct *fnt = NULL; if (!(fnt = XLoadQueryFont(dpy, font)) && fallback) { strnfmt(buf, sizeof(buf), "Can't find the font \"%s\", trying fixed\n", font); XtWarning(buf); if (!(fnt = XLoadQueryFont(dpy, "fixed"))) { XtError("Can't fint the font \"fixed\"!, bailing out\n"); } } return fnt; } /*** The Angband code ****/ /* * Number of fallback resources per window */ #define TERM_FALLBACKS 7 /* * The names of the term_data's */ char *termNames[MAX_TERM_DATA] = { (String) "angband", (String) "term-1", (String) "term-2", (String) "term-3", (String) "term-4", (String) "term-5", (String) "term-6", (String) "term-7" }; /* * The special Arg's */ Arg specialArgs[TERM_FALLBACKS] = { { (String) XtNstartRows, 24}, { (String) XtNstartColumns, 80}, { (String) XtNminRows, 24}, { (String) XtNminColumns, 80}, { (String) XtNmaxRows, 255}, { (String) XtNmaxColumns, 255}, { (String) XtNinternalBorder, 2} }; /* * The default Arg's */ Arg defaultArgs[TERM_FALLBACKS] = { { (String) XtNstartRows, 24}, { (String) XtNstartColumns, 80}, { (String) XtNminRows, 1}, { (String) XtNminColumns, 1}, { (String) XtNmaxRows, 255}, { (String) XtNmaxColumns, 255}, { (String) XtNinternalBorder, 2} }; /* * The application context */ XtAppContext appcon; /* * User changable information about widgets */ static String fallback[] = { (String) "Angband.angband.iconName: Angband", (String) "Angband.angband.title: Angband", (String) "Angband.term-1.iconName: Term 1", (String) "Angband.term-1.title: Term 1", (String) "Angband.term-2.iconName: Term 2", (String) "Angband.term-2.title: Term 2", (String) "Angband.term-3.iconName: Term 3", (String) "Angband.term-3.title: Term 3", (String) "Angband.term-4.iconName: Term 4", (String) "Angband.term-4.title: Term 4", (String) "Angband.term-5.iconName: Term 5", (String) "Angband.term-5.title: Term 5", (String) "Angband.term-6.iconName: Term 6", (String) "Angband.term-6.title: Term 6", (String) "Angband.term-7.iconName: Term 7", (String) "Angband.term-7.title: Term 7", NULL }; /* * Do a redraw */ static void react_redraw(Widget widget, XtPointer client_data, XtPointer call_data) { term_data *old_td = (term_data*)(Term->data); term_data *td = (term_data*)client_data; /* Ignore parameters */ (void) widget; (void) call_data; /* Activate the proper Term */ Term_activate(&td->t); /* Request a redraw */ Term_redraw(); /* Activate the old Term */ Term_activate(&old_td->t); } /* * Process a keypress event * * Also appears in "main-x11.c". */ static void react_keypress(XKeyEvent *ev) { int i, n, mc, ms, mo, mx; uint ks1; KeySym ks; char buf[128]; char msg[128]; /* Check for "normal" keypresses */ n = XLookupString(ev, buf, 125, &ks, NULL); /* Terminate */ buf[n] = '\0'; /* Hack -- Ignore "modifier keys" */ if (IsModifierKey(ks)) return; /* Hack -- convert into an unsigned int */ ks1 = (uint)(ks); /* Extract four "modifier flags" */ mc = (ev->state & ControlMask) ? TRUE : FALSE; ms = (ev->state & ShiftMask) ? TRUE : FALSE; mo = (ev->state & Mod1Mask) ? TRUE : FALSE; mx = (ev->state & Mod2Mask) ? TRUE : FALSE; /* Normal keys with no modifiers */ if (n && !mo && !mx && !IsSpecialKey(ks)) { /* Enqueue the normal key(s) */ for (i = 0; buf[i]; i++) Term_keypress(buf[i]); /* All done */ return; } /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */ switch (ks1) { case XK_Escape: { Term_keypress(ESCAPE); return; } case XK_Return: { Term_keypress('\r'); return; } case XK_Tab: { Term_keypress('\t'); return; } case XK_Delete: case XK_BackSpace: { Term_keypress('\010'); return; } } /* Hack -- Use the KeySym */ if (ks) { strnfmt(msg, sizeof(msg), "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13); } /* Hack -- Use the Keycode */ else { strnfmt(msg, sizeof(msg), "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13); } /* Enqueue the "macro trigger" string */ for (i = 0; msg[i]; i++) Term_keypress(msg[i]); /* Hack -- auto-define macros as needed */ if (n && (macro_find_exact(msg) < 0)) { /* Create a macro */ macro_add(msg, buf); } } /* * Handle an event */ static void handle_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch) { term_data *old_td = (term_data*)(Term->data); term_data *td = (term_data *)client_data; /* Ignore parameter */ (void) widget; /* Continue to process the event by default */ *continue_to_dispatch = TRUE; /* Activate the Term */ Term_activate(&td->t); switch (event->type) { case KeyPress: { /* Hack -- use old term */ Term_activate(&old_td->t); /* Handle the keypress */ react_keypress(&(event->xkey)); /* We took care of the event */ *continue_to_dispatch = FALSE; break; } /* Oops */ default: { break; } } /* Activate the old term */ Term_activate(&old_td->t); return; } /* * Process an event (or just check for one) */ static errr CheckEvent(bool wait) { XEvent event; /* No events ready, and told to just check */ if (!wait && !XtAppPending(appcon)) return 1; /* Process */ while (1) { XtAppNextEvent(appcon, &event); XtDispatchEvent(&event); if (!XtAppPending(appcon)) break; } return (0); } /* * Monstrous hack. */ static void Term_xtra_xaw_react_aux(term_data *td) { AngbandWidget wnew = td->widget; Display *dpy = XtDisplay((Widget) wnew); int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew)); int i; /* See if any colors need to be changed */ for (i = 0; i < NUM_COLORS; i++) { if (depth > 1) { if ((wnew->angband.color[i][0] != angband_color_table[i][0]) || (wnew->angband.color[i][1] != angband_color_table[i][1]) || (wnew->angband.color[i][2] != angband_color_table[i][2]) || (wnew->angband.color[i][3] != angband_color_table[i][3])) { unsigned long pixel; /* Save new values */ wnew->angband.color[i][0] = angband_color_table[i][0]; wnew->angband.color[i][1] = angband_color_table[i][1]; wnew->angband.color[i][2] = angband_color_table[i][2]; wnew->angband.color[i][3] = angband_color_table[i][3]; /* Create pixel */ pixel = create_pixel(dpy, wnew->angband.color[i][1], wnew->angband.color[i][2], wnew->angband.color[i][3]); /* Change */ XSetForeground(dpy, wnew->angband.gc[i], pixel); } } } } /* * Monstrous hack. */ static errr Term_xtra_xaw_react(void) { int i; /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; if (!td) break; Term_xtra_xaw_react_aux(td); } return (0); } /* * Handle a "special request" */ static errr Term_xtra_xaw(int n, int v) { term_data *td = (term_data*)(Term->data); Widget widget = (Widget)(td->widget); Display *dpy = XtDisplay(widget); /* Handle a subset of the legal requests */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: XBell(dpy, 100); return (0); /* Flush the output */ case TERM_XTRA_FRESH: XFlush(dpy); /* Allow flushed events to be showed */ CheckEvent(FALSE); return (0); /* Process random events */ case TERM_XTRA_BORED: return (CheckEvent(0)); /* Process events */ case TERM_XTRA_EVENT: return (CheckEvent(v)); /* Flush events */ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0); /* Delay */ case TERM_XTRA_DELAY: if (v > 0) usleep(1000 * v); return (0); case TERM_XTRA_REACT: return (Term_xtra_xaw_react()); } /* Unknown */ return (1); } /* * Erase a number of characters */ static errr Term_wipe_xaw(int x, int y, int n) { term_data *td = (term_data*)(Term->data); /* Erase using color 0 */ AngbandClearArea(td->widget, x, y, n); /* Success */ return (0); } /* * Draw the cursor, a rectangular outline */ static errr Term_curs_xaw(int x, int y) { term_data *td = (term_data*)(Term->data); int x1, y1; /* Top left hand corner of cursor */ square_to_pixel(td->widget, &x1, &y1, x, y); /* Hilite the cursor character with a box */ if (is_bigtiled(x, y)) { XDrawRectangle(XtDisplay(td->widget), XtWindow(td->widget), td->widget->angband.gc[COLOR_XOR], x1, y1, td->widget->angband.tilewidth - 1, td->widget->angband.fontheight - 1); } else { XDrawRectangle(XtDisplay(td->widget), XtWindow(td->widget), td->widget->angband.gc[COLOR_XOR], x1, y1, td->widget->angband.fontwidth - 1, td->widget->angband.fontheight - 1); } /* Success */ return (0); } /* * Draw a number of characters */ static errr Term_text_xaw(int x, int y, int n, byte a, cptr s) { term_data *td = (term_data*)(Term->data); /* Wipe region first */ Term_wipe_xaw(x, y, n); /* Draw the text */ AngbandOutputText(td->widget, x, y, (String)s, n, a); /* Success */ return (0); } #ifdef USE_GRAPHICS /* * Draw some graphical characters. */ static errr Term_pict_xaw(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { term_data *td = (term_data*)(Term->data); /* Draw the pictures */ AngbandOutputPict(td->widget, x, y, n, ap, cp, tap, tcp); /* Success */ return (0); } #endif /* USE_GRAPHICS */ /* * Raise a term */ static void term_raise(term_data *td) { Widget widget = (Widget)(td->widget); XRaiseWindow(XtDisplay(XtParent(widget)), XtWindow(XtParent(widget))); } /* * Initialize a term_data */ static errr term_data_init(term_data *td, Widget topLevel, int key_buf, String name, ArgList widget_arg, Cardinal widget_arg_no, int i) { Widget parent; term *t = &td->t; int cols = 80; int rows = 24; char buf[80]; cptr str; int val; /* Create the shell widget */ parent = XtCreatePopupShell(name, topLevelShellWidgetClass, topLevel, NULL, 0); /* Window specific cols */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_COLS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) cols = val; /* Window specific rows */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_ROWS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) rows = val; /* Hack the main window must be at least 80x24 */ if (i == 0) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Reset the initial size */ widget_arg[0].value = rows; widget_arg[1].value = cols; /* Hack ox==oy in xaw port */ /* Window specific inner border offset (ox) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOX_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) widget_arg[6].value = val; /* Window specific inner border offset (oy) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOY_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) widget_arg[6].value = val; /* Create the interior widget */ td->widget = (AngbandWidget) XtCreateManagedWidget(name, angbandWidgetClass, parent, widget_arg, widget_arg_no); /* Initialize the term (full size) */ term_init(t, cols, rows, key_buf); /* Use a "soft" cursor */ t->soft_cursor = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Hooks */ t->xtra_hook = Term_xtra_xaw; t->curs_hook = Term_curs_xaw; t->wipe_hook = Term_wipe_xaw; t->text_hook = Term_text_xaw; /* Save the data */ t->data = td; /* Register the keypress event handler */ XtAddEventHandler((Widget)td->widget, KeyPressMask, False, (XtEventHandler) handle_event, td); /* Redraw callback */ XtAddCallback((Widget)td->widget, XtNredrawCallback, react_redraw, td); /* Realize the widget */ XtRealizeWidget(parent); /* Have we redefined the font? */ if (streq(td->widget->angband.font, DEFAULT_X11_FONT)) { XFontStruct *fnt; /* Check if the font exists */ fnt = getFont(td->widget, (String) get_default_font(i), FALSE); /* The font didn't exist */ if (fnt == NULL) { XtWarning("Couldn't find the requested font!"); } else { int height, width; /* Free the old font */ XFreeFont(XtDisplay((Widget)td->widget), td->widget->angband.fnt); /* Update font information */ td->widget->angband.fontheight = fnt->ascent + fnt->descent; td->widget->angband.fontwidth = fnt->max_bounds.width; if (use_bigtile) td->widget->angband.tilewidth = 2 * td->widget->angband.fontwidth; else td->widget->angband.tilewidth = td->widget->angband.fontwidth; td->widget->angband.fontascent = fnt->ascent; for (i = 0; i < NUM_COLORS; i++) { /* Be sure the correct font is ready */ XSetFont(XtDisplay((Widget)td->widget), td->widget->angband.gc[i], fnt->fid); } /* Get the window shape */ height = (td->widget->angband.start_rows * td->widget->angband.fontheight + 2 * td->widget->angband.internal_border); width = (td->widget->angband.start_columns * td->widget->angband.fontwidth + 2 * td->widget->angband.internal_border); /* Request a change to the new shape */ if (XtMakeResizeRequest((Widget)td->widget, width, height, NULL, NULL) == XtGeometryNo) { /* Not allowed */ XtWarning("Size change denied!"); } else { /* Recalculate size hints */ calculateSizeHints(td->widget); } } } /* Make it visible */ XtPopup(parent, XtGrabNone); /* Activate (important) */ Term_activate(t); Resize_term(td->widget); return 0; } /* * Initialization function for an X Athena Widget module to Angband * * We should accept "-d" requests in the "argv" array. XXX XXX XXX */ errr init_xaw(int argc, char *argv[]) { int i; Widget topLevel; Display *dpy; cptr dpy_name = ""; #ifdef USE_GRAPHICS char filename[1024]; int pict_wid = 0; int pict_hgt = 0; int graphmode = GRAPHICS_ANY; char *TmpData; #endif /* USE_GRAPHICS */ /* Parse args */ for (i = 1; i < argc; i++) { if (prefix(argv[i], "-d")) { dpy_name = &argv[i][2]; continue; } #ifdef USE_GRAPHICS if (prefix(argv[i], "-s")) { smoothRescaling = FALSE; continue; } if (prefix(argv[i], "-b")) { int bitdepth = 0; bitdepth = atoi(&argv[i][2]); /* Paranoia */ if (bitdepth == 32) graphmode = GRAPHICS_DAVID_GERVAIS; if (bitdepth == 16) graphmode = GRAPHICS_ADAM_BOLT; if (bitdepth == 8) graphmode = GRAPHICS_ORIGINAL; continue; } #endif /* USE_GRAPHICS */ if (prefix(argv[i], "-n")) { num_term = atoi(&argv[i][2]); if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA; else if (num_term < 1) num_term = 1; continue; } plog_fmt("Ignoring option: %s", argv[i]); } /* Attempt to open the local display */ dpy = XOpenDisplay(dpy_name); /* Failure -- assume no X11 available */ if (!dpy) return (-1); /* Close the local display */ XCloseDisplay(dpy); #ifdef USE_GRAPHICS /* We support bigtile mode */ if (arg_bigtile && arg_graphics) use_bigtile = TRUE; #endif /* USE_GRAPHICS */ #ifdef USE_XAW_LANG /* Support locale processing */ XtSetLanguageProc(NULL, NULL, NULL); #endif /* USE_XAW_LANG */ /* Initialize the toolkit */ topLevel = XtAppInitialize(&appcon, "Angband", NULL, 0, &argc, argv, fallback, NULL, 0); /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; term_data_init(td, topLevel, 1024, termNames[i], (i == 0) ? specialArgs : defaultArgs, TERM_FALLBACKS, i); angband_term[i] = Term; } #ifdef USE_GRAPHICS /* Try graphics */ if (arg_graphics) { (void) pick_graphics(graphmode, &pict_wid, &pict_hgt, filename); } /* Load graphics */ if (use_graphics) { /* Hack -- Get the Display */ term_data *td = &data[0]; Widget widget = (Widget)(td->widget); Display *dpy = XtDisplay(widget); XImage *tiles_raw; for (i = 0; i < num_term; i++) { term_data *td = &data[i]; td->widget->angband.tiles = NULL; } /* Load the graphical tiles */ tiles_raw = ReadBMP(dpy, filename); if (tiles_raw) { /* Initialize the windows */ for (i = 0; i < num_term; i++) { int j; term_data *td = &data[i]; term_data *o_td = NULL; term *t = &td->t; t->pict_hook = Term_pict_xaw; t->higher_pict = TRUE; /* Look for another term with same font size */ for (j = 0; j < i; j++) { o_td = &data[j]; if ((td->widget->angband.fontwidth == o_td->widget->angband.fontwidth) && (td->widget->angband.fontheight == o_td->widget->angband.fontheight)) { /* Use same graphics */ td->widget->angband.tiles = o_td->widget->angband.tiles; td->widget->angband.b_tiles = o_td->widget->angband.b_tiles; break; } } if (!td->widget->angband.tiles) { /* Resize tiles */ if (arg_bigtile) { td->widget->angband.b_tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->widget->angband.tilewidth, td->widget->angband.fontheight); } else { td->widget->angband.b_tiles = NULL; } td->widget->angband.tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->widget->angband.fontwidth, td->widget->angband.fontheight); } } /* Free tiles_raw */ FREE(tiles_raw); } /* Initialize the transparency temp storage */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; int ii, jj; int depth = DefaultDepth(dpy, DefaultScreen(dpy)); Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int total; /* Determine total bytes needed for image */ ii = 1; jj = (depth - 1) >> 2; while (jj >>= 1) ii <<= 1; total = td->widget->angband.tilewidth * td->widget->angband.fontheight * ii; TmpData = (char *)malloc(total); td->widget->angband.TmpImage = XCreateImage(dpy, visual,depth, ZPixmap, 0, TmpData, td->widget->angband.tilewidth, td->widget->angband.fontheight, 8, 0); } } #endif /* USE_GRAPHICS */ /* Activate the "Angband" window screen */ Term_activate(&data[0].t); /* Raise the "Angband" window */ term_raise(&data[0]); /* Success */ return (0); } #endif /* USE_XAW */ zangband/src/main-xpj.c0000644000000000000000000020506710250356274014034 0ustar rootroot/* File: main-x11.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with UNIX/X11 computers. * * To use this file, compile with "USE_XPJ" defined, and link against all * the various "X11" libraries which may be needed. * * See also "main-x11.c" and "main-xaw.c". * * Part of this file provides a user interface package composed of several * pseudo-objects, including "metadpy" (a display), "infowin" (a window), * "infoclr" (a color), and "infofnt" (a font). Actually, the package was * originally much more interesting, but it was bastardized to keep this * file simple. * * The rest of this file is an implementation of "main-xxx.c" for X11. * * Most of this file is by Ben Harrison (benh@phial.com). * The old main-x11.c has been changed to use 3d-projection by * Steven Fuerst. */ #include "angband.h" #ifdef USE_XPJ cptr help_xpj[] = { "To use XPJ (Projected view)", "-d Set display name", "-s Turn off smoothscaling graphics", "-n# Number of terms to use", NULL }; #ifndef USE_GRAPHICS #error Must have USE_GRAPHICS compile-time flag on. #endif /* * The tile size to use. (Configure this if you want.) * * This is 16 by default - but any multiple of 4 works. * Try setting this to be 20 or larger if you have a big screen. * Settings as small as 8 also work. (However, things are a bit * hard to see on that setting.) -SF- * * XXX XXX This probably could be converted to work with any * even number, with a few minor changes. */ #define P_TILE_SIZE 16 /* * Set this to be 1 if you want the walls to be bright. * Bright walls may or may not look better. */ #define BRIGHT_WALLS 0 /* Rest of the dependencies */ #ifndef __MAKEDEPEND__ #include #include #include #include #endif /* __MAKEDEPEND__ */ /* * Include some helpful X11 code. */ #include "maid-x11.h" /* * Notes on Colors: * * 1) On a monochrome (or "fake-monochrome") display, all colors * will be "cast" to "fg," except for the bg color, which is, * obviously, cast to "bg". Thus, one can ignore this setting. * * 2) Because of the inner functioning of the color allocation * routines, colors may be specified as (a) a typical color name, * (b) a hexidecimal color specification (preceded by a pound sign), * or (c) by strings such as "fg", "bg", "zg". * * 3) Due to the workings of the init routines, many colors * may also be dealt with by their actual pixel values. Note that * the pixel with all bits set is "zg = (1<depth)-1", which * is not necessarily either black or white. */ /**** Generic Types ****/ /* * An X11 pixell specifier */ typedef unsigned long Pixell; /* * The structures defined below */ typedef struct metadpy metadpy; typedef struct infowin infowin; typedef struct infoclr infoclr; typedef struct infofnt infofnt; /* * A structure summarizing a given Display. * * - The Display itself * - The default Screen for the display * - The virtual root (usually just the root) * - The default colormap (from a macro) * * - The "name" of the display * * - The socket to listen to for events * * - The width of the display screen (from a macro) * - The height of the display screen (from a macro) * - The bit depth of the display screen (from a macro) * * - The black Pixell (from a macro) * - The white Pixell (from a macro) * * - The background Pixell (default: black) * - The foreground Pixell (default: white) * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly) * * - Bit Flag: Force all colors to black and white (default: !color) * - Bit Flag: Allow the use of color (default: depth > 1) * - Bit Flag: We created 'dpy', and so should nuke it when done. */ struct metadpy { Display *dpy; Screen *screen; Window root; Colormap cmap; char *name; int fd; uint width; uint height; uint depth; Pixell black; Pixell white; Pixell bg; Pixell fg; Pixell zg; uint mono:1; uint color:1; uint nuke:1; }; /* * A Structure summarizing Window Information. * * I assume that a window is at most 30000 pixels on a side. * I assume that the root windw is also at most 30000 square. * * - The Window * - The current Input Event Mask * * - The location of the window * - The width, height of the window * - The border width of this window * * - Byte: 1st Extra byte * * - Bit Flag: This window is currently Mapped * - Bit Flag: This window has been resized * * - Bit Flag: We should nuke 'win' when done with it * * - Bit Flag: 1st extra flag * - Bit Flag: 2nd extra flag * - Bit Flag: 3rd extra flag * - Bit Flag: 4th extra flag * - Bit Flag: 5th extra flag */ struct infowin { Window win; long mask; s16b ox, oy; s16b x, y; s16b w, h; u16b b; byte byte1; uint mapped:1; uint resize:1; uint nuke:1; uint flag1:1; uint flag2:1; uint flag3:1; uint flag4:1; uint flag5:1; }; /* * A Structure summarizing Operation+Color Information * * - The actual GC corresponding to this info * * - The Foreground Pixell Value * - The Background Pixell Value * * - Num (0-15): The operation code (As in Clear, Xor, etc) * - Bit Flag: The GC is in stipple mode * - Bit Flag: Destroy 'gc' at Nuke time. */ struct infoclr { GC gc; Pixell fg; Pixell bg; uint code:4; uint stip:1; uint nuke:1; }; /* * A Structure to Hold Font Information * * - The 'XFontStruct*' (yields the 'Font') * * - The font name * * - The default character width * - The default character height * - The default character ascent * * - Byte: Pixel offset used during fake mono * * - Flag: Force monospacing via 'wid' * - Flag: Nuke info when done */ struct infofnt { XFontStruct *info; cptr name; s16b wid; s16b hgt; s16b asc; byte off; uint mono:1; uint nuke:1; }; /**** Angband data structures ****/ /* * Forward declare */ typedef struct term_data term_data; /* * A structure for each "term" */ struct term_data { term t; infofnt *fnt; infowin *win; XImage *tiles; /* Tempory storage for overlaying tiles. */ XImage *TmpImage; /* Tempory storage for skewing tiles. */ XImage *SkewImage; }; /* * The number of term data structures */ #define MAX_TERM_DATA 8 /* * The array of term data structures */ static term_data data[MAX_TERM_DATA]; /* Tables used to rapidly calculate which pixel to plot */ static u16b pj_table1[P_TILE_SIZE][P_TILE_SIZE / 2]; static u16b pj_table2[P_TILE_SIZE][P_TILE_SIZE / 2]; /* Bitflags used in the tables */ /* Transparent diagonal walls */ #define PJ_T_WALL1_T 0x0001 #define PJ_T_WALL2_T 0x0002 /* Transparent Top */ #define PJ_T_TOP_T1 0x0004 #define PJ_T_TOP_T2 0x0008 /* Floor */ #define PJ_T_FLOOR1 0x0010 #define PJ_T_FLOOR2 0x0020 /* Diagonal walls "behind" everything */ #define PJ_T_WALL1 0x0040 /* Overlaying tiles */ #define PJ_T_OVER1 0x0080 #define PJ_T_OVER3 0x0100 /* Horizontal walls in front of overlays */ #define PJ_T_WALLF 0x0200 #define PJ_T_WALLB 0x0400 /* Diagonal walls in front of everything */ #define PJ_T_WALL2 0x0800 /* Overlaying tiles */ #define PJ_T_OVER2 0x1000 #define PJ_T_OVER4 0x2000 /* Ceiling */ #define PJ_T_TOP1 0x4000 #define PJ_T_TOP2 0x8000 /**** Bit numbers ****/ /* Transparent diagonal walls */ #define PJ_WALL1_T 0 #define PJ_WALL2_T 1 /* Transparent Top */ #define PJ_TOP_T1 2 #define PJ_TOP_T2 3 /* Floor */ #define PJ_FLOOR1 4 #define PJ_FLOOR2 5 /* Diagonal walls "behind" everything */ #define PJ_WALL1 6 /* Overlaying tiles */ #define PJ_OVER1 7 #define PJ_OVER3 8 /* Horizontal walls in front of overlays */ #define PJ_WALLF 9 #define PJ_WALLB 10 /* Diagonal walls in front of everything */ #define PJ_WALL2 11 /* Overlaying tiles */ #define PJ_OVER2 12 #define PJ_OVER4 13 /* Ceiling */ #define PJ_TOP1 14 #define PJ_TOP2 15 /* Number of bits used */ #define PJ_MAX 16 static const byte bit_high_lookup[256] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }; static const int t_offsetx1[PJ_MAX] = { 0, 0, P_TILE_SIZE / 2, P_TILE_SIZE / 2, 0, P_TILE_SIZE, 0, -P_TILE_SIZE / 4, (3 * P_TILE_SIZE) / 4, P_TILE_SIZE / 2, P_TILE_SIZE / 2, 0, P_TILE_SIZE / 4, 0, P_TILE_SIZE / 2, P_TILE_SIZE / 2 }; static const int t_offsety1[PJ_MAX] = { P_TILE_SIZE, P_TILE_SIZE, 0, 0, 0, 0, P_TILE_SIZE, P_TILE_SIZE / 2, P_TILE_SIZE / 2, 0, 0, P_TILE_SIZE, -P_TILE_SIZE / 2, 0, 0, 0 }; static const int t_xscale[PJ_MAX] = { 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 }; static const int t_offsetx2[PJ_MAX] = { 0, 0, 0, P_TILE_SIZE, P_TILE_SIZE / 2, 0, 0, P_TILE_SIZE / 4, 0, 0, 0, 0, (3 * P_TILE_SIZE) / 4, -P_TILE_SIZE / 4, 0, P_TILE_SIZE }; static const int t_offsety2[PJ_MAX] = { 0, 0, 0, 0, 0, 0, 0, P_TILE_SIZE / 2, 0, 0, 0, 0, -P_TILE_SIZE / 2, -P_TILE_SIZE / 2, 0, 0 }; static const bool wall_flip[PJ_MAX] = { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; /* * For optimisation purposes - two arrays of bits for a row, * marking whether or not this 8x16 block has changed. */ static u32b pj_row1[64]; static u32b pj_row2[64]; static int pj_cur_row; /* Font data */ static XImage *font_data; /* Number of bytes per pixel */ static int bytes_per_pixel; /* * Function pointer that points to the function that * moves the correctly sized pixels to the overlay block. * This optimises away the need for the slow * getpixel and putpixel pairs. */ static void (*draw_block)(void *tiles[PJ_MAX], u16b pj_table[P_TILE_SIZE][P_TILE_SIZE / 2], u16b mask, term_data *td); /* * Hack -- cursor color */ static infoclr *xor; /* * Actual color table */ static infoclr *clr[256]; /* * Color info (unused, red, green, blue). */ static byte color_table[256][4]; /* * The "blank" pixel - used for transparency */ static Pixell pix_blank; /**** Generic Macros ****/ /* Set current metadpy (Metadpy) to 'M' */ #define Metadpy_set(M) \ Metadpy = M /* Initialize 'M' using Display 'D' */ #define Metadpy_init_dpy(D) \ Metadpy_init_2(D,cNULL) /* Initialize 'M' using a Display named 'N' */ #define Metadpy_init_name(N) \ Metadpy_init_2((Display*)(NULL),N) /* Initialize 'M' using the standard Display */ #define Metadpy_init() \ Metadpy_init_name("") /* Init an infowin by giving father as an (info_win*) (or NULL), and data */ #define Infowin_init_dad(D,X,Y,W,H,B,FG,BG) \ Infowin_init_data(((D) ? ((D)->win) : (Window)(None)), \ X,Y,W,H,B,FG,BG) /* Init a top level infowin by pos,size,bord,Colors */ #define Infowin_init_top(X,Y,W,H,B,FG,BG) \ Infowin_init_data(None,X,Y,W,H,B,FG,BG) /* Request a new standard window by giving Dad infowin and X,Y,W,H */ #define Infowin_init_std(D,X,Y,W,H,B) \ Infowin_init_dad(D,X,Y,W,H,B,Metadpy->fg,Metadpy->bg) /* Set the current Infowin */ #define Infowin_set(I) \ (Infowin = (I)) /* Set the current Infoclr */ #define Infoclr_set(C) \ (Infoclr = (C)) #define Infoclr_init_ppo(F,B,O,M) \ Infoclr_init_data(F,B,O,M) #define Infoclr_init_cco(F,B,O,M) \ Infoclr_init_ppo(Infoclr_Pixell(F),Infoclr_Pixell(B),O,M) #define Infoclr_init_ppn(F,B,O,M) \ Infoclr_init_ppo(F,B,Infoclr_Opcode(O),M) #define Infoclr_init_ccn(F,B,O,M) \ Infoclr_init_cco(F,B,Infoclr_Opcode(O),M) /* Set the current infofnt */ #define Infofnt_set(I) \ (Infofnt = (I)) /**** Generic Globals ****/ /* * The "default" values */ static metadpy metadpy_default; /* * The "current" variables */ static metadpy *Metadpy = &metadpy_default; static infowin *Infowin = (infowin*)(NULL); static infoclr *Infoclr = (infoclr*)(NULL); static infofnt *Infofnt = (infofnt*)(NULL); /**** Generic code ****/ /* * Init the current metadpy, with various initialization stuff. * * Inputs: * dpy: The Display* to use (if NULL, create it) * name: The name of the Display (if NULL, the current) * * Notes: * If 'name' is NULL, but 'dpy' is set, extract name from dpy * If 'dpy' is NULL, then Create the named Display * If 'name' is NULL, and so is 'dpy', use current Display * * Return -1 if no Display given, and none can be opened. */ static errr Metadpy_init_2(Display *dpy, cptr name) { metadpy *m = Metadpy; /*** Open the display if needed ***/ /* If no Display given, attempt to Create one */ if (!dpy) { /* Attempt to open the display */ dpy = XOpenDisplay(name); /* Failure */ if (!dpy) return (-1); /* We will have to nuke it when done */ m->nuke = 1; } /* Since the Display was given, use it */ else { /* We will not have to nuke it when done */ m->nuke = 0; } /*** Save some information ***/ /* Save the Display itself */ m->dpy = dpy; /* Get the Screen and Virtual Root Window */ m->screen = DefaultScreenOfDisplay(dpy); m->root = RootWindowOfScreen(m->screen); /* Get the default colormap */ m->cmap = DefaultColormapOfScreen(m->screen); /* Extract the true name of the display */ m->name = DisplayString(dpy); /* Extract the fd */ m->fd = ConnectionNumber(Metadpy->dpy); /* Save the Size and Depth of the screen */ m->width = WidthOfScreen(m->screen); m->height = HeightOfScreen(m->screen); m->depth = DefaultDepthOfScreen(m->screen); /* Save the Standard Colors */ m->black = BlackPixelOfScreen(m->screen); m->white = WhitePixelOfScreen(m->screen); /*** Make some clever Guesses ***/ /* Guess at the desired 'fg' and 'bg' Pixell's */ m->bg = m->black; m->fg = m->white; /* Calculate the Maximum allowed Pixel value. */ m->zg = ((Pixell)1 << m->depth) - 1; /* Save various default Flag Settings */ m->color = ((m->depth > 1) ? 1 : 0); m->mono = ((m->color) ? 0 : 1); /* Return "success" */ return (0); } /* * General Flush/ Sync/ Discard routine */ static errr Metadpy_update(int flush, int sync, int discard) { /* Flush if desired */ if (flush) XFlush(Metadpy->dpy); /* Sync if desired, using 'discard' */ if (sync) XSync(Metadpy->dpy, discard); /* Clear the arrays used for optimisation */ (void) C_WIPE(pj_row1, 64, u32b); (void) C_WIPE(pj_row2, 64, u32b); /* Hack - use crazy row to mark "nothing entered yet" */ pj_cur_row = -255; /* Success */ return (0); } /* * Make a simple beep */ static errr Metadpy_do_beep(void) { /* Make a simple beep */ XBell(Metadpy->dpy, 100); return (0); } /* * Set the name (in the title bar) of Infowin */ static errr Infowin_set_name(cptr name) { Status st; XTextProperty tp; char buf[128]; char *bp = buf; strnfmt(buf, sizeof(buf), "%s", name); st = XStringListToTextProperty(&bp, 1, &tp); if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp); return (0); } /* * Prepare a new 'infowin'. */ static errr Infowin_prepare(Window xid) { infowin *iwin = Infowin; Window tmp_win; XWindowAttributes xwa; int x, y; unsigned int w, h, b, d; /* Assign stuff */ iwin->win = xid; /* Check For Error XXX Extract some ACTUAL data from 'xid' */ XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d); /* Apply the above info */ iwin->x = x; iwin->y = y; iwin->w = w; iwin->h = h; iwin->b = b; /* Check Error XXX Extract some more ACTUAL data */ XGetWindowAttributes(Metadpy->dpy, xid, &xwa); /* Apply the above info */ iwin->mask = xwa.your_event_mask; iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1); /* Success */ return (0); } /* * Init an infowin by giving some data. * * Inputs: * dad: The Window that should own this Window (if any) * x,y: The position of this Window * w,h: The size of this Window * b,d: The border width and pixel depth * * Notes: * If 'dad == None' assume 'dad == root' */ static errr Infowin_init_data(Window dad, int x, int y, int w, int h, int b, Pixell fg, Pixell bg) { Window xid; /* Wipe it clean */ (void)WIPE(Infowin, infowin); /*** Error Check XXX ***/ /*** Create the Window 'xid' from data ***/ /* What happened here? XXX XXX XXX */ /* If no parent given, depend on root */ if (dad == None) dad = Metadpy->root; /* Create the Window XXX Error Check */ xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg); /* Start out selecting No events */ XSelectInput(Metadpy->dpy, xid, 0L); /*** Prepare the new infowin ***/ /* Mark it as nukable */ Infowin->nuke = 1; /* Attempt to Initialize the infowin */ return (Infowin_prepare(xid)); } /* * Modify the event mask of an Infowin */ static errr Infowin_set_mask(long mask) { /* Save the new setting */ Infowin->mask = mask; /* Execute the Mapping */ XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask); /* Success */ return (0); } /* * Request that Infowin be mapped */ static errr Infowin_map(void) { /* Execute the Mapping */ XMapWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } /* * Request that Infowin be raised */ static errr Infowin_raise(void) { /* Raise towards visibility */ XRaiseWindow(Metadpy->dpy, Infowin->win); /* Success */ return (0); } /* * Request that Infowin be moved to a new location */ static errr Infowin_impell(int x, int y) { /* Execute the request */ XMoveWindow(Metadpy->dpy, Infowin->win, x, y); /* Success */ return (0); } /* * Resize an infowin */ static errr Infowin_resize(int w, int h) { /* Execute the request */ XResizeWindow(Metadpy->dpy, Infowin->win, w, h); /* Success */ return (0); } /* * A NULL terminated pair list of legal "operation names" * * Pairs of values, first is texttual name, second is the string * holding the decimal value that the operation corresponds to. */ static cptr opcode_pairs[] = { "cpy", "3", "xor", "6", "and", "1", "ior", "7", "nor", "8", "inv", "10", "clr", "0", "set", "15", "src", "3", "dst", "5", "+andReverse", "2", "+andInverted", "4", "+noop", "5", "+equiv", "9", "+orReverse", "11", "+copyInverted", "12", "+orInverted", "13", "+nand", "14", NULL }; /* * Parse a word into an operation "code" * * Inputs: * str: A string, hopefully representing an Operation * * Output: * 0-15: if 'str' is a valid Operation * -1: if 'str' could not be parsed */ static int Infoclr_Opcode(cptr str) { register int i; /* Scan through all legal operation names */ for (i = 0; opcode_pairs[i*2]; ++i) { /* Is this the right oprname? */ if (streq(opcode_pairs[i*2], str)) { /* Convert the second element in the pair into a Code */ return (atoi(opcode_pairs[i*2+1])); } } /* The code was not found, return -1 */ return (-1); } /* * Initialize an infoclr with some data * * Inputs: * fg: The Pixell for the requested Foreground (see above) * bg: The Pixell for the requested Background (see above) * op: The Opcode for the requested Operation (see above) * stip: The stipple mode */ static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip) { infoclr *iclr = Infoclr; GC gc; XGCValues gcv; unsigned long gc_mask; /*** Simple error checking of opr and clr ***/ /* Check the 'Pixells' for realism */ if (bg > Metadpy->zg) return (-1); if (fg > Metadpy->zg) return (-1); /* Check the data for trueness */ if ((op < 0) || (op > 15)) return (-1); /*** Create the requested 'GC' ***/ /* Assign the proper GC function */ gcv.function = op; /* Assign the proper GC background */ gcv.background = bg; /* Assign the proper GC foreground */ gcv.foreground = fg; /* Hack -- Handle XOR (xor is code 6) by hacking bg and fg */ if (op == 6) gcv.background = 0; if (op == 6) gcv.foreground = (bg ^ fg); /* Assign the proper GC Fill Style */ gcv.fill_style = (stip ? FillStippled : FillSolid); /* Turn off 'Give exposure events for pixmap copying' */ gcv.graphics_exposures = False; /* Set up the GC mask */ gc_mask = (GCFunction | GCBackground | GCForeground | GCFillStyle | GCGraphicsExposures); /* Create the GC detailed above */ gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv); /*** Initialize ***/ /* Wipe the iclr clean */ (void)WIPE(iclr, infoclr); /* Assign the GC */ iclr->gc = gc; /* Nuke it when done */ iclr->nuke = 1; /* Assign the parms */ iclr->fg = fg; iclr->bg = bg; iclr->code = op; iclr->stip = stip ? 1 : 0; /* Success */ return (0); } /* * Change the 'fg' for an infoclr * * Inputs: * fg: The Pixell for the requested Foreground (see above) */ static errr Infoclr_change_fg(Pixell fg) { infoclr *iclr = Infoclr; /*** Simple error checking of opr and clr ***/ /* Check the 'Pixells' for realism */ if (fg > Metadpy->zg) return (-1); /*** Change ***/ /* Change */ XSetForeground(Metadpy->dpy, iclr->gc, fg); /* Success */ return (0); } /* * Prepare a new 'infofnt' */ static errr Infofnt_prepare(XFontStruct *info) { infofnt *ifnt = Infofnt; XCharStruct *cs; /* Assign the struct */ ifnt->info = info; /* Jump into the max bouonds thing */ cs = &(info->max_bounds); /* Extract default sizing info */ ifnt->asc = info->ascent; ifnt->hgt = info->ascent + info->descent; ifnt->wid = cs->width; /* Success */ return (0); } /* * Init an infofnt by its Name * * Inputs: * name: The name of the requested Font */ static errr Infofnt_init_data(cptr name) { XFontStruct *info; /*** Load the info Fresh, using the name ***/ /* If the name is not given, report an error */ if (!name) { plog_fmt("Missing font! %s", name); return (-1); } /* Attempt to load the font */ info = XLoadQueryFont(Metadpy->dpy, name); /* The load failed */ if (!info) { plog_fmt("Failed to find font:\"%s\"", name); return (-1); } /*** Init the font ***/ /* Wipe the thing */ (void)WIPE(Infofnt, infofnt); /* Attempt to prepare it */ if (Infofnt_prepare(info)) { /* Free the font */ XFreeFont(Metadpy->dpy, info); /* Fail */ plog_fmt("Failed to prepare font:\"%s\"", name); return (-1); } /* Save a copy of the font name */ Infofnt->name = string_make(name); /* Mark it as nukable */ Infofnt->nuke = 1; /* Success */ return (0); } /* * Standard Text */ static errr Infofnt_text_std(int x, int y, cptr str, int len) { int i; /*** Do a brief info analysis ***/ /* Do nothing if the string is null */ if (!str || !*str) return (-1); /* Get the length of the string */ if (len < 0) len = strlen(str); /* Ignore Vertical Justifications */ y = y * Infofnt->hgt + Infofnt->asc + Infowin->oy; /*** Decide where to place the string, horizontally ***/ /* Line up with x at left edge of column 'x' */ x = x * Infofnt->wid + Infowin->ox; /*** Actually draw 'str' onto the infowin ***/ /* Be sure the correct font is ready */ XSetFont(Metadpy->dpy, Infoclr->gc, Infofnt->info->fid); /*** Handle the fake mono we can enforce on fonts ***/ /* Monotize the font */ if (Infofnt->mono) { /* Do each character */ for (i = 0; i < len; ++i) { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x + i * Infofnt->wid + Infofnt->off, y, str + i, 1); } } /* Assume monospaced font */ else { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, str, len); } /* Success */ return (0); } /*************************************************************************/ /* * Angband specific code follows... (ANGBAND) */ /* * Process a keypress event * * Also appears in "main-xaw.c". */ static void react_keypress(XKeyEvent *ev) { int i, n, mc, ms, mo, mx; uint ks1; KeySym ks; char buf[128]; char msg[128]; /* Check for "normal" keypresses */ n = XLookupString(ev, buf, 125, &ks, NULL); /* Terminate */ buf[n] = '\0'; /* Hack -- Ignore "modifier keys" */ if (IsModifierKey(ks)) return; /* Hack -- convert into an unsigned int */ ks1 = (uint)(ks); /* Extract four "modifier flags" */ mc = (ev->state & ControlMask) ? TRUE : FALSE; ms = (ev->state & ShiftMask) ? TRUE : FALSE; mo = (ev->state & Mod1Mask) ? TRUE : FALSE; mx = (ev->state & Mod2Mask) ? TRUE : FALSE; /* Normal keys with no modifiers */ if (n && !mo && !mx && !IsSpecialKey(ks)) { /* Enqueue the normal key(s) */ for (i = 0; buf[i]; i++) Term_keypress(buf[i]); /* All done */ return; } /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */ switch (ks1) { case XK_Escape: { Term_keypress(ESCAPE); return; } case XK_Return: { Term_keypress('\r'); return; } case XK_Tab: { Term_keypress('\t'); return; } case XK_Delete: case XK_BackSpace: { Term_keypress('\010'); return; } } /* Hack -- Use the KeySym */ if (ks) { strnfmt(msg, sizeof(msg), "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13); } /* Hack -- Use the Keycode */ else { strnfmt(msg, sizeof(msg), "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13); } /* Enqueue the "macro trigger" string */ for (i = 0; msg[i]; i++) Term_keypress(msg[i]); /* Hack -- auto-define macros as needed */ if (n && (macro_find_exact(msg) < 0)) { /* Create a macro */ macro_add(msg, buf); } } /* * Process events */ static errr CheckEvent(bool wait) { term_data *old_td = (term_data*)(Term->data); XEvent xev_body, *xev = &xev_body; term_data *td = NULL; infowin *iwin = NULL; int i, x, y; int window = 0; /* Do not wait unless requested */ if (!wait && !XPending(Metadpy->dpy)) return (1); /* Load the Event */ XNextEvent(Metadpy->dpy, xev); /* Notice new keymaps */ if (xev->type == MappingNotify) { XRefreshKeyboardMapping(&xev->xmapping); return 0; } /* Scan the windows */ for (i = 0; i < MAX_TERM_DATA; i++) { if (xev->xany.window == data[i].win->win) { td = &data[i]; iwin = td->win; window = i; break; } } /* Unknown window */ if (!td || !iwin) return (0); /* Hack -- activate the Term */ Term_activate(&td->t); /* Hack -- activate the window */ Infowin_set(iwin); /* Switch on the Type */ switch (xev->type) { #if 0 case ButtonPress: case ButtonRelease: { int z = 0; /* Which button is involved */ if (xev->xbutton.button == Button1) z = 1; else if (xev->xbutton.button == Button2) z = 2; else if (xev->xbutton.button == Button3) z = 3; else if (xev->xbutton.button == Button4) z = 4; else if (xev->xbutton.button == Button5) z = 5; /* Where is the mouse */ x = xev->xbutton.x; y = xev->xbutton.y; /* XXX Handle */ break; } case EnterNotify: case LeaveNotify: { /* Where is the mouse */ x = xev->xcrossing.x; y = xev->xcrossing.y; /* XXX Handle */ break; } case MotionNotify: { /* Where is the mouse */ x = xev->xmotion.x; y = xev->xmotion.y; /* XXX Handle */ break; } case KeyRelease: { /* Nothing */ break; } #endif case KeyPress: { /* Save the mouse location */ x = xev->xkey.x; y = xev->xkey.y; /* Hack -- use "old" term */ Term_activate(&old_td->t); /* Process the key */ react_keypress(&(xev->xkey)); break; } case Expose: { int x1, x2, y1, y2; /* Ignore "extra" exposes */ /*if (xev->xexpose.count) break;*/ /* Clear the window */ /*Infowin_wipe();*/ /* Redraw */ if (window == 0) { x1 = (xev->xexpose.x - Infowin->ox)/P_TILE_SIZE; x2 = (xev->xexpose.x + xev->xexpose.width - Infowin->ox)/P_TILE_SIZE; y1 = (xev->xexpose.y - Infowin->oy)/P_TILE_SIZE; y2 = (xev->xexpose.y + xev->xexpose.height - Infowin->oy)/P_TILE_SIZE; /* Hack - area invalidated on main window is not a rectangle */ Term_redraw_section(x1 - y2 / 2, y1, x2 + y2 / 2, y2); /* Term_redraw(); */ } else { x1 = (xev->xexpose.x - Infowin->ox)/Infofnt->wid; x2 = (xev->xexpose.x + xev->xexpose.width - Infowin->ox)/Infofnt->wid; y1 = (xev->xexpose.y - Infowin->oy)/Infofnt->hgt; y2 = (xev->xexpose.y + xev->xexpose.height - Infowin->oy)/Infofnt->hgt; Term_redraw_section(x1, y1, x2, y2); } break; } case MapNotify: { Infowin->mapped = 1; Term->mapped_flag = TRUE; break; } case UnmapNotify: { Infowin->mapped = 0; Term->mapped_flag = FALSE; break; } /* Move and/or Resize */ case ConfigureNotify: { int cols, rows, wid, hgt; int ox = Infowin->ox; int oy = Infowin->oy; /* Save the new Window Parms */ Infowin->x = xev->xconfigure.x; Infowin->y = xev->xconfigure.y; Infowin->w = xev->xconfigure.width; Infowin->h = xev->xconfigure.height; if (window == 0) { /* Determine "proper" number of rows/cols */ #if 0 rows = 24; cols = 80; #endif /* 0 */ rows = (Infowin->h - (oy + oy)) / P_TILE_SIZE - 1; cols = (Infowin->w - (ox + ox) - rows * P_TILE_SIZE / 2) / P_TILE_SIZE - 1; } else { /* Determine "proper" number of rows/cols */ cols = (Infowin->w - (ox + ox)) / td->fnt->wid; rows = (Infowin->h - (oy + oy)) / td->fnt->hgt; } if (window == 0) { /* Hack the main window must be at least 80x24 */ if ((cols < 80) || (rows < 24)) { /* Hack XXX setting both prevents resize loops */ cols = 80; rows = 24; } } else { /* Hack -- minimal size for normal windows */ if (cols < 1) cols = 1; if (rows < 1) rows = 1; } if (window == 0) { /* Desired size of window */ wid = (cols + 1) * P_TILE_SIZE + rows * P_TILE_SIZE / 2 + ox * 2; hgt = (rows + 1) * P_TILE_SIZE + oy * 2; } else { /* Desired size of window */ wid = cols * td->fnt->wid + ox * 2; hgt = rows * td->fnt->hgt + oy * 2; } /* Resize the Term (if needed) */ (void) Term_resize(cols, rows); /* Resize the windows if any "change" is needed */ if ((Infowin->w != wid) || (Infowin->h != hgt)) { /* Resize window */ Infowin_set(td->win); Infowin_resize(wid, hgt); } break; } } /* Hack -- Activate the old term */ Term_activate(&old_td->t); /* Hack -- Activate the proper window */ Infowin_set(old_td->win); /* Success */ return (0); } /* * Handle "activation" of a term */ static errr Term_xtra_xpj_level(int v) { term_data *td = (term_data*)(Term->data); /* Handle "activate" */ if (v) { /* Activate the window */ Infowin_set(td->win); /* Activate the font */ Infofnt_set(td->fnt); } /* Success */ return (0); } /* * React to changes */ static errr Term_xtra_xpj_react(void) { int i; int j, k; if (Metadpy->color) { /* Check the colors */ for (i = 0; i < 256; i++) { if ((color_table[i][0] != angband_color_table[i][0]) || (color_table[i][1] != angband_color_table[i][1]) || (color_table[i][2] != angband_color_table[i][2]) || (color_table[i][3] != angband_color_table[i][3])) { Pixell pixel; /* Save new values */ color_table[i][0] = angband_color_table[i][0]; color_table[i][1] = angband_color_table[i][1]; color_table[i][2] = angband_color_table[i][2]; color_table[i][3] = angband_color_table[i][3]; /* Create pixel */ pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]); /* Change the foreground */ Infoclr_set(clr[i]); Infoclr_change_fg(pixel); /* Only need to readjust fonts for lower 32 colours */ if (i > 31) continue; /* Need to redo the font metrics */ for (j = 0; j < P_TILE_SIZE; j++) { for (k = 0; k < 128 * P_TILE_SIZE; k++) { /* Recolour the changed pixels */ if (XGetPixel(font_data, i * P_TILE_SIZE + j, k)) { XPutPixel(font_data, i * P_TILE_SIZE + j, k, pixel); } } } } } } /* Success */ return (0); } /* * Handle a "special request" */ static errr Term_xtra_xpj(int n, int v) { /* Handle a subset of the legal requests */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: Metadpy_do_beep(); return (0); /* Flush the output XXX XXX */ case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0); /* Process random events XXX */ case TERM_XTRA_BORED: return (CheckEvent(0)); /* Process Events XXX */ case TERM_XTRA_EVENT: return (CheckEvent(v)); /* Flush the events XXX */ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0); /* Handle change in the "level" */ case TERM_XTRA_LEVEL: return (Term_xtra_xpj_level(v)); /* Delay for some milliseconds */ case TERM_XTRA_DELAY: usleep(1000 * v); return (0); /* React to changes */ case TERM_XTRA_REACT: return (Term_xtra_xpj_react()); } /* Unknown */ return (1); } /* * Draw the cursor as an inverted rectangle. * * Consider a rectangular outline like "main-mac.c". XXX XXX */ static errr Term_curs_xpj(int x, int y) { /* Are we on the main window? */ if (Term->data == &data[0]) { XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc, x * P_TILE_SIZE + y * P_TILE_SIZE / 2 + P_TILE_SIZE / 4 + Infowin->ox, y * P_TILE_SIZE + P_TILE_SIZE / 2 + Infowin->oy, P_TILE_SIZE - 1, P_TILE_SIZE - 1); } else { XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1); } /* Success */ return (0); } /* * Draw some textual characters. */ static errr Term_text_xpj(int x, int y, int n, byte a, cptr s) { /* Draw the text */ Infoclr_set(clr[a]); /* Draw the text */ Infofnt_text_std(x, y, s, n); /* Success */ return (0); } /* * Draw a block with byte-sized pixels */ static void draw_block8(void *tiles[PJ_MAX], u16b pj_table[P_TILE_SIZE][P_TILE_SIZE / 2], u16b mask, term_data *td) { int i, j; byte pixel, val; /* List of possibly visible tiles */ u16b value; /* Plot the pixels onto the bitmap */ for (i = 0; i < P_TILE_SIZE / 2; i++) { for (j = 0; j < P_TILE_SIZE; j++) { /* Get tiles on this pixel */ value = pj_table[j][i] & mask; pixel = 0; while (!pixel) { /* Get the number of the bit to look at */ val = bit_high_lookup[value / 256]; if (val) { val += 7; } else { val = bit_high_lookup[value]; /* Check for null case */ if (!val) { /* No allowable tile - use (0,0) */ pixel = pix_blank; break; } val--; } /* * Update the bit so that transparency works * (by getting rid of the bit we are using now.) */ value &= (~(1 << val)); /* * Pick the pixel to use. * * This is majorly optimised. * The offsets have been moved into the pointers passed * by tiles. All that remains are the parts dependant * on i and j. */ pixel = ((byte *) tiles[val]) [t_xscale[val] * j / 2 + i * (wall_flip[val] + 1) + (j - 2 * i * wall_flip[val]) * 32 * P_TILE_SIZE]; } /* Write the pixel onto the bitmap */ ((byte *) td->SkewImage->data)[i + j * P_TILE_SIZE / 2] = pixel; } } } /* * Draw a block with 16bit pixels */ static void draw_block16(void *tiles[PJ_MAX], u16b pj_table[P_TILE_SIZE][P_TILE_SIZE / 2], u16b mask, term_data *td) { int i, j; byte val; u16b pixel; /* List of possibly visible tiles */ u16b value; /* Plot the pixels onto the bitmap */ for (i = 0; i < P_TILE_SIZE / 2; i++) { for (j = 0; j < P_TILE_SIZE; j++) { /* Get tiles on this pixel */ value = pj_table[j][i] & mask; pixel = 0; while (!pixel) { /* Get the number of the bit to look at */ val = bit_high_lookup[value / 256]; if (val) { val += 7; } else { val = bit_high_lookup[value]; /* Check for null case */ if (!val) { /* No allowable tile - use (0,0) */ pixel = pix_blank; break; } val--; } /* * Update the mask so that transparency works * (by getting rid of the particular bit we are using now.) */ value &= (~(1 << val)); /* * Pick the pixel to use. * * This is majorly optimised. * The offsets have been moved into the pointers passed * by tiles. All that remains are the parts dependant * on i and j. */ pixel = ((u16b *)tiles[val]) [t_xscale[val] * j / 2 + i * (wall_flip[val] + 1) + (j - 2 * i * wall_flip[val]) * 32 * P_TILE_SIZE]; } /* Write the pixel onto the bitmap */ ((u16b *) td->SkewImage->data)[i + j * P_TILE_SIZE / 2] = pixel; } } } /* * Draw a block with 32bit pixels */ static void draw_block32(void *tiles[PJ_MAX], u16b pj_table[P_TILE_SIZE][P_TILE_SIZE / 2], u16b mask, term_data *td) { int i, j; byte val; u32b pixel; /* List of possibly visible tiles */ u16b value; /* Plot the pixels onto the bitmap */ for (i = 0; i < P_TILE_SIZE / 2; i++) { for (j = 0; j < P_TILE_SIZE; j++) { /* Get tiles on this pixel */ value = pj_table[j][i] & mask; pixel = 0; while (!pixel) { /* Get the number of the bit to look at */ val = bit_high_lookup[value / 256]; if (val) { val += 7; } else { val = bit_high_lookup[value]; /* Check for null case */ if (!val) { /* No allowable tile - use (0,0) */ pixel = pix_blank; break; } val--; } /* * Update the bit so that transparency works * (by getting rid of the bit we are using now.) */ value &= (~(1 << val)); /* * Pick the pixel to use. * * This is majorly optimised. * The offsets have been moved into the pointers passed * by tiles. All that remains are the parts dependant * on i and j. */ pixel = ((u32b *)tiles[val]) [t_xscale[val] * j / 2 + i * (wall_flip[val]+1) + (j - 2 * i * wall_flip[val]) * 32 * P_TILE_SIZE]; } /* Write the pixel onto the bitmap */ ((u32b *) td->SkewImage->data)[i + j * P_TILE_SIZE / 2] = pixel; } } } /* Macro used to set the tile information */ #define set_tile1(N, X, Y) table[N] = &(((byte *)td->tiles->data)\ [(t_offsetx1[N] + (X) * P_TILE_SIZE\ + (t_offsety1[N] + (Y) * P_TILE_SIZE) * 32 * P_TILE_SIZE)\ * bytes_per_pixel]) /* Macro used to set the tile (font-based) information */ #define set_font1(N, Y, X) table[N] = &(((byte *)font_data->data)\ [(t_offsetx1[N] + (X) * P_TILE_SIZE\ + (t_offsety1[N] + (Y) * P_TILE_SIZE) * 32 * P_TILE_SIZE)\ * bytes_per_pixel]) /* Hack XXX XXX - convert to illuminated wall */ #define ILLUMINATE(X) (((((X & 0x3f) - 1) / 3) * 3) + 2 + BRIGHT_WALLS) static errr draw_rect_t1(int x, int y, term_data *td, int xp, int yp) { term_win *window = td->t.scr; void *table[PJ_MAX]; /* List of possibly visible tiles */ u16b mask = 0; /* The tile attr / char pairs */ byte a; char c; byte ta; char tc; bool floor_blank1 = FALSE, floor_blank2 = FALSE; /* Look to see if we are already drawn */ int cur_col = x / 16; u32b row_mask = (1L << (x % 16)); /* Paranoia */ if ((x < -1) || (y < -1) || (x >= td->t.wid) || (y >= td->t.hgt)) { return (0); } /* Look to see if we are already drawn */ if (y == pj_cur_row) { /* Are we drawn? */ if (pj_row1[cur_col] & row_mask) return (0); /* Set "drawn" flag */ pj_row1[cur_col] |= row_mask; } else if (y == pj_cur_row - 1) { /* Are we drawn? */ if (pj_row2[cur_col] & row_mask) return (0); /* Set "drawn" flag */ pj_row2[cur_col] |= row_mask; } else if (y == pj_cur_row + 1) { /* We've moved to another row */ /* Copy the contents of the pj_row1[] array */ C_COPY(pj_row2, pj_row1, 64, u32b); /* Wipe the old "current row" */ (void) C_WIPE(pj_row1, 64, u32b); /* We are now at a larger row */ pj_cur_row++; /* Set "drawn" flag */ pj_row1[cur_col] |= row_mask; } else { /* Clear the arrays used for optimisation */ (void) C_WIPE(pj_row1, 64, u32b); (void) C_WIPE(pj_row2, 64, u32b); /* We are at row y */ pj_cur_row = y; /* Set "drawn" flag */ pj_row1[cur_col] |= row_mask; } if ((x > 0) && (y >= 0)) { /* Get tiles we need */ a = window->a[y][x - 1]; c = window->c[y][x - 1]; ta = window->ta[y][x - 1]; tc = window->tc[y][x - 1]; /* Are we overlaying anything? */ if ((a != ta) || (c != tc) || !(a & 0x80)) { mask |= PJ_T_OVER3; if (a & 0x80) { set_tile1(PJ_OVER3, c & 0x3F, a & 0x7F); } else { set_font1(PJ_OVER3, c & 0x7F, a & 0x1F); } } /* Solid wall? */ else if (tc & 0x40) { mask |= (PJ_T_WALLF | PJ_T_WALL1 | PJ_T_WALL1_T); set_tile1(PJ_WALLF, tc & 0x3F, ta & 0x7F); set_tile1(PJ_WALL1, tc & 0x3F, ta & 0x7F); set_tile1(PJ_WALL1_T, tc & 0x3F, ta & 0x7F); } /* Is the terrain null? */ if ((ta & 0x80) && !(tc & 0x40)) { mask |= PJ_T_FLOOR2; set_tile1(PJ_FLOOR2, tc & 0x3F, ta & 0x7F); /* Blank floor? */ if (!((tc & 0x3F) || (ta & 0x7F))) floor_blank2 = TRUE; } } if ((x >= 0) && (y >= 0)) { /* Get tiles we need */ a = window->a[y][x]; c = window->c[y][x]; ta = window->ta[y][x]; tc = window->tc[y][x]; /* Are we overlaying anything? */ if ((a != ta) || (c != tc) || !(a & 0x80)) { mask |= PJ_T_OVER1; if (a & 0x80) { set_tile1(PJ_OVER1, c & 0x3F, a & 0x7F); } else { set_font1(PJ_OVER1, c & 0x7F, a & 0x1F); } } /* Solid wall? */ else if (tc & 0x40) { if (mask & PJ_T_WALL1) { mask &= ~(PJ_T_WALL1 | PJ_T_WALL1_T); } else { mask |= (PJ_T_WALL1 | PJ_T_WALL1_T); set_tile1(PJ_WALL1, tc & 0x3F, ta & 0x7F); set_tile1(PJ_WALL1_T, tc & 0x3F, ta & 0x7F); } } /* Is the terrain null? */ if ((ta & 0x80) && !(tc & 0x40)) { mask |= PJ_T_FLOOR1; set_tile1(PJ_FLOOR1, tc & 0x3F, ta & 0x7F); /* Blank floor? */ if (!((tc & 0x3F) || (ta & 0x7F))) floor_blank1 = TRUE; } } /* Check below */ if ((x > 0) && (y < td->t.hgt - 1)) { /* Get tiles we need */ a = window->a[y + 1][x - 1]; c = window->c[y + 1][x - 1]; ta = window->ta[y + 1][x - 1]; tc = window->tc[y + 1][x - 1]; /* Are we overlaying anything? */ if ((a != ta) || (c != tc) || !(a & 0x80)) { mask |= PJ_T_OVER2; if (a & 0x80) { set_tile1(PJ_OVER2, c & 0x3F, a & 0x7F); } else { set_font1(PJ_OVER2, c & 0x7F, a & 0x1F); } } /* Solid wall? */ else if (tc & 0x40) { mask |= PJ_T_TOP1 | PJ_T_TOP_T1; if (tc & 0x80) { /* Special lighting for walls */ set_tile1(PJ_TOP1, ILLUMINATE(tc), ta & 0x7F); set_tile1(PJ_TOP_T1, ILLUMINATE(tc), ta & 0x7F); } else { set_tile1(PJ_TOP1, tc & 0x3F, ta & 0x7F); set_tile1(PJ_TOP_T1, tc & 0x3F, ta & 0x7F); } if (mask & PJ_T_WALLF) { mask &= ~(PJ_T_WALLF); } else { mask |= PJ_T_WALLB; set_tile1(PJ_WALLB, tc & 0x3F, ta & 0x7F); } /* Hack - check for "blank floor" */ if (floor_blank1) { mask &= ~(PJ_T_FLOOR1); } if (floor_blank2) { mask &= ~(PJ_T_FLOOR2); } } } /* Draw the overlay block */ draw_block(table, pj_table1, mask, td); /* Copy to screen */ XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->SkewImage, 0, 0, xp, yp, P_TILE_SIZE / 2, P_TILE_SIZE); return (0); } /* Macro used to set the tile information */ #define set_tile2(N, X, Y) table[N] = &(((byte *)td->tiles->data)\ [(t_offsetx2[N] + (X) * P_TILE_SIZE\ + (t_offsety2[N] + (Y) * P_TILE_SIZE) * 32 * P_TILE_SIZE)\ * bytes_per_pixel]) /* Macro used to set the tile (font-based) information */ #define set_font2(N, Y, X) table[N] = &(((byte *)font_data->data)\ [(t_offsetx2[N] + (X) * P_TILE_SIZE\ + (t_offsety2[N] + (Y) * P_TILE_SIZE) * 32 * P_TILE_SIZE)\ * bytes_per_pixel]) static errr draw_rect_t2(int x, int y, term_data *td, int xp, int yp) { term_win *window = td->t.scr; /* Locations of tiles in bitmap */ void *table[PJ_MAX]; /* List of possibly visible tiles */ u16b mask = 0; /* The tile attr / char pairs */ byte a; char c; byte ta; char tc; bool floor_blank = FALSE; /* Look to see if we are already drawn */ int cur_col = x / 16; u32b row_mask = (1L << (x % 16 + 16)); /* Paranoia */ if ((x < -1) || (y < -1) || (x >= td->t.wid) || (y >= td->t.hgt)) { return (0); } if (y == pj_cur_row) { /* Are we drawn? */ if (pj_row1[cur_col] & row_mask) return (0); /* Not drawn - mark we are drawn */ pj_row1[cur_col] |= row_mask; } else if (y == pj_cur_row - 1) { /* Are we drawn? */ if (pj_row2[cur_col] & row_mask) return (0); /* Not drawn - mark we are drawn */ pj_row2[cur_col] |= row_mask; } /* This should never happen */ #if 0 else { /* Clear the arrays used for optimisation */ (void) C_WIPE(pj_row1, 64, u32b); (void) C_WIPE(pj_row2, 64, u32b); /* We are at row y */ pj_cur_row = y; } #endif /* 0 */ if ((x >= 0) && (y >= 0)) { /* Get tiles we need */ a = window->a[y][x]; c = window->c[y][x]; ta = window->ta[y][x]; tc = window->tc[y][x]; /* Are we overlaying anything? */ if ((a != ta) || (c != tc) || !(a & 0x80)) { mask |= PJ_T_OVER1; if (a & 0x80) { set_tile2(PJ_OVER1, c & 0x3F, a & 0x7F); } else { set_font2(PJ_OVER1, c & 0x7F, a & 0x1F); } } /* Solid wall? */ else if (tc & 0x40) { mask |= (PJ_T_WALLF); set_tile2(PJ_WALLF, tc & 0x3F, ta & 0x7F); } /* Is the terrain null? */ if ((ta & 0x80) && !(tc & 0x40)) { mask |= PJ_T_FLOOR1; set_tile2(PJ_FLOOR1, tc & 0x3F, ta & 0x7F); /* Blank floor? */ if (!((tc & 0x3F) || (ta & 0x7F))) floor_blank = TRUE; } } /* Check to the left */ if ((x > 0) && (y < td->t.hgt - 1)) { /* Get tiles we need */ a = window->a[y + 1][x - 1]; c = window->c[y + 1][x - 1]; ta = window->ta[y + 1][x - 1]; tc = window->tc[y + 1][x - 1]; /* Are we overlaying anything? */ if ((a != ta) || (c != tc) || !(a & 0x80)) { mask |= PJ_T_OVER2; if (a & 0x80) { set_tile2(PJ_OVER2, c & 0x3F, a & 0x7F); } else { set_font2(PJ_OVER2, c & 0x7F, a & 0x1F); } } /* Solid wall? */ else if (tc & 0x40) { mask |= (PJ_T_WALL2 | PJ_T_WALL2_T | PJ_T_TOP2 | PJ_T_TOP_T2); set_tile2(PJ_WALL2, tc & 0x3F, ta & 0x7F); set_tile2(PJ_WALL2_T, tc & 0x3F, ta & 0x7F); if (tc & 0x80) { /* Special lighting for walls */ set_tile2(PJ_TOP2, ILLUMINATE(tc), ta & 0x7F); set_tile2(PJ_TOP_T2, ILLUMINATE(tc), ta & 0x7F); } else { set_tile2(PJ_TOP2, tc & 0x3F, ta & 0x7F); set_tile2(PJ_TOP_T2, tc & 0x3F, ta & 0x7F); } /* Hack - check for "blank floor" */ if (floor_blank) { mask &= ~(PJ_T_FLOOR1); } } } /* Check below */ if ((x >= 0) && (y < td->t.hgt - 1)) { /* Get tiles we need */ a = window->a[y + 1][x]; c = window->c[y + 1][x]; ta = window->ta[y + 1][x]; tc = window->tc[y + 1][x]; /* Are we overlaying anything? */ if ((a != ta) || (c != tc) || !(a & 0x80)) { mask |= PJ_T_OVER4; if (a & 0x80) { set_tile2(PJ_OVER4, c & 0x3F, a & 0x7F); } else { set_font2(PJ_OVER4, c & 0x7F, a & 0x1F); } } /* Solid wall? */ else if (tc & 0x40) { mask |= PJ_T_TOP1 | PJ_T_TOP_T1; if (tc & 0x80) { /* Special lighting for walls */ set_tile2(PJ_TOP1, ILLUMINATE(tc), ta & 0x7F); set_tile2(PJ_TOP_T1, ILLUMINATE(tc), ta & 0x7F); } else { set_tile2(PJ_TOP1, tc & 0x3F, ta & 0x7F); set_tile2(PJ_TOP_T1, tc & 0x3F, ta & 0x7F); } /* Hack - check for "blank floor" */ if (floor_blank) { mask &= ~(PJ_T_FLOOR1); } if (mask & PJ_T_WALL2) { mask &= ~(PJ_T_WALL2 | PJ_T_WALL2_T); } else { mask |= (PJ_T_WALL2 | PJ_T_WALL2_T); set_tile2(PJ_WALL2, tc & 0x3F, ta & 0x7F); set_tile2(PJ_WALL2_T, tc & 0x3F, ta & 0x7F); } if (mask & PJ_T_WALLF) { mask &= (~PJ_T_WALLF); } else { mask |= PJ_T_WALLB; set_tile2(PJ_WALLB, tc & 0x3F, ta & 0x7F); } } } /* Draw the overlay block */ draw_block(table, pj_table2, mask, td); /* Copy to screen */ XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->SkewImage, 0, 0, xp, yp, P_TILE_SIZE / 2, P_TILE_SIZE); return (0); } static errr Term_skew_xpj(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i, xp, yp; term_data *td = (term_data*)(Term->data); /* Hack - ignore some of the parameters */ (void) ap; (void) cp; (void) tap; (void) tcp; xp = x * P_TILE_SIZE + y * P_TILE_SIZE / 2 + Infowin->ox; yp = (y + 1) * P_TILE_SIZE + Infowin->oy; /* Draw left 8x16 bock */ draw_rect_t1(x, y, td, xp, yp); /* Draw center 8x16 block */ draw_rect_t2(x, y, td, xp + P_TILE_SIZE / 2, yp); /* Draw upper left */ draw_rect_t2(x, y - 1, td, xp, yp - P_TILE_SIZE); /* Draw upper center */ draw_rect_t1(x + 1, y - 1, td, xp + P_TILE_SIZE / 2, yp - P_TILE_SIZE); /* Draw the middle section */ for (i = 0; i < n - 1; i++) { x++; xp += P_TILE_SIZE; /* Draw end 8x16 block */ draw_rect_t1(x, y, td, xp, yp); /* Draw center 8x16 block */ draw_rect_t2(x, y, td, xp + P_TILE_SIZE / 2, yp); /* Draw upper right */ draw_rect_t2(x, y - 1, td, xp, yp - P_TILE_SIZE); /* Draw upper center */ draw_rect_t1(x + 1, y - 1, td, xp + P_TILE_SIZE / 2, yp - P_TILE_SIZE); } x++; xp += P_TILE_SIZE; /* Draw end 8x16 block */ draw_rect_t1(x, y, td, xp, yp); /* Draw upper right */ draw_rect_t2(x, y - 1, td, xp, yp - P_TILE_SIZE); /* Done */ return(0); } /* * Draw some graphical characters. */ static errr Term_pict_xpj(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { int i, x1, y1; byte a; char c; byte wid, hgt; byte ta; char tc; int x2, y2; int k,l; Pixell pixel; term_data *td = (term_data*)(Term->data); hgt = Infofnt->hgt; wid = Infofnt->wid; y *= hgt; x *= wid; /* Add in affect of window boundaries */ y += Infowin->oy; x += Infowin->ox; for (i = 0; i < n; ++i) { a = *ap++; c = *cp++; /* For extra speed - cache these values */ x1 = (c&0x3F) * wid; y1 = (a&0x7F) * hgt; ta = *tap++; tc = *tcp++; /* For extra speed - cache these values */ x2 = (tc&0x3F) * wid; y2 = (ta&0x7F) * hgt; /* Optimise the common case */ if ((x1 == x2) && (y1 == y2)) { /* Draw object / terrain */ XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->tiles, x1, y1, x, y, wid, hgt); } else { for (k = 0; k < wid; k++) { for (l = 0; l < hgt; l++) { /* If mask set... */ if ((pixel = XGetPixel(td->tiles, x1 + k, y1 + l)) == pix_blank) { /* Output from the terrain */ pixel = XGetPixel(td->tiles, x2 + k, y2 + l); } /* Store into the temp storage. */ XPutPixel(td->TmpImage, k, l, pixel); } } /* Draw to screen */ XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->TmpImage, 0, 0, x, y, wid, hgt); } x += wid; } /* Success */ return (0); } /* * Erase some characters. */ static errr Term_wipe_xpj(int x, int y, int n) { #if 0 /* Erase (use black) */ Infoclr_set(clr[TERM_DARK]); /* Mega-Hack -- Erase some space */ Infofnt_text_non(x, y, "", n); #endif /* 0 */ byte dummy[3] = {0x80, 0x80, 0x80}; int i; for (i = 0; i < n; i++) { /* Mega-hack */ Term_pict_xpj(x, y, 1, &dummy[1], (char *) &dummy[1], &dummy[1], (char *) &dummy[1]); } /* Success */ return (0); } /* Load the font data */ static XImage *ReadFONT(Display *dpy, char *Name, u16b size) { Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int depth = DefaultDepth(dpy, DefaultScreen(dpy)); FILE *fp; XImage *Res = NULL; Pixell pixel; char *Data; char buf[1024]; char line[16]; int i, j, k, m, total; int num = 0, error_idx = -1; /* Determine total bytes needed for image */ total = 32 * size * 128 * size * bytes_per_pixel; /* Open the BMP file */ fp = fopen(Name, "r"); /* No such file */ if (fp == NULL) { return (NULL); } /* Allocate image memory */ C_MAKE(Data, total, char); Res = XCreateImage(dpy, visual, depth, ZPixmap, 0, Data, size * 32, size * 128, 32, 0); /* Failure */ if (Res == NULL) { KILL(Data); fclose(fp); return (NULL); } /* Reset the counters for use in parsing the font data */ i = 0; j = 16; /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { /* Count lines */ num++; /* Skip "empty" lines */ if (!buf[0]) continue; /* Skip "blank" lines */ if (isspace(buf[0])) continue; /* Skip comments */ if (buf[0] == '#') continue; /* Look at the line */ /* Verify correct "colon" format */ if (buf[1] != ':') { quit_fmt("Incorrect font file format on line %d", num); } /* Get number */ if (buf[0] == 'N') { /* Get the index */ i = atoi(buf+2); /* Verify information */ if (i <= error_idx) { quit_fmt("Incorrect font file numbering on line %d", num); } error_idx = i; /* Verify information */ if (i >= 128) { quit_fmt("Incorrect font file numbering on line %d", num); } /* Verify information */ if (j != size) { quit_fmt("Incorrect font size on line %d", num); } /* Start from the top */ j = 0; } /* Get font data */ if (buf[0] == 'F') { /* Verify information */ if (j >= size) { quit_fmt("Incorrect font size length on line %d", num); } if (((int) strlen(buf)) != size + 2) { quit_fmt("Incorrect font size width on line %d", num); } /* Create the line */ for (k = 0; k < size; k++) { line[k] = buf[k + 2]; } /* Copy it to the 32 coloured locations */ for (k = 0; k < 32; k++) { pixel = clr[k]->fg; for (m = 0; m < size; m++) { if (line[m] == '*') { /* Coloured pixel */ XPutPixel(Res, k * size + m, i * size + j, pixel); } else { /* Blank pixel */ XPutPixel(Res, k * size + m, i * size + j, 0); } } } /* Next line of the character */ j++; } } /* Close the file */ my_fclose(fp); /* Paranoia - does the file end early? */ if ((i != 127) || (j != size)) return (NULL); return (Res); } /* * Initialize a term_data */ static errr term_data_init(term_data *td, int i) { term *t = &td->t; cptr name = angband_term_name[i]; cptr font; int x = 0; int y = 0; int cols = 80; int rows = 24; int ox = 1; int oy = 1; int wid, hgt, num; char buf[80]; cptr str; int val; XClassHint *ch; char res_name[20]; char res_class[20]; XSizeHints *sh; /* Get default font for this term */ font = get_default_font(i); /* Window specific location (x) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_AT_X_%d", i); str = getenv(buf); x = (str != NULL) ? atoi(str) : -1; /* Window specific location (y) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_AT_Y_%d", i); str = getenv(buf); y = (str != NULL) ? atoi(str) : -1; /* Window specific cols */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_COLS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) cols = val; /* Window specific rows */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_ROWS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) rows = val; /* Hack the main window must be at least 80x24 */ if (!i) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Window specific inner border offset (ox) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOX_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) ox = val; /* Window specific inner border offset (oy) */ strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOY_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) oy = val; /* Prepare the standard font */ MAKE(td->fnt, infofnt); Infofnt_set(td->fnt); if (Infofnt_init_data(font)) { quit("Stopping"); } /* Hack -- key buffer size */ num = ((i == 0) ? 1024 : 16); if (i == 0) { wid = (cols + 1) * P_TILE_SIZE + rows * P_TILE_SIZE / 2 + ox * 2; hgt = (rows + 1) * P_TILE_SIZE + oy * 2; } else { /* Assume full size windows */ wid = cols * td->fnt->wid + (ox + ox); hgt = rows * td->fnt->hgt + (oy + oy); } /* Create a top-window */ MAKE(td->win, infowin); Infowin_set(td->win); Infowin_init_top(x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg); /* Ask for certain events */ Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask); /* Set the window name */ Infowin_set_name(name); /* Save the inner border */ Infowin->ox = ox; Infowin->oy = oy; /* Make Class Hints */ ch = XAllocClassHint(); if (ch == NULL) quit("XAllocClassHint failed"); strnfmt(res_name, sizeof(res_name), "%s", name); res_name[0] = FORCELOWER(res_name[0]); ch->res_name = res_name; strnfmt(res_class, sizeof(res_class), "Angband"); ch->res_class = res_class; XSetClassHint(Metadpy->dpy, Infowin->win, ch); /* Make Size Hints */ sh = XAllocSizeHints(); /* Oops */ if (sh == NULL) quit("XAllocSizeHints failed"); /* Main window has a differing minimum size */ if (i == 0) { sh->flags = PMinSize | PMaxSize; sh->min_height = (24 + 1) * P_TILE_SIZE + oy * 2; sh->min_width = (80 + 1) * P_TILE_SIZE + ox * 2; sh->max_height = (255 + 1) * P_TILE_SIZE + oy * 2; sh->max_width = (255 + 1) * P_TILE_SIZE + ox * 2 + 255 * P_TILE_SIZE / 2; /* Resize increment */ sh->flags |= PResizeInc; sh->width_inc = P_TILE_SIZE * 3 / 2; sh->height_inc = P_TILE_SIZE; } /* Other windows can be shrunk to 1x1 */ else { /* Other windows */ sh->flags = PMinSize | PMaxSize; sh->min_width = td->fnt->wid + ox * 2; sh->min_height = td->fnt->hgt + oy * 2; sh->max_width = 255 * td->fnt->wid + ox * 2; sh->max_height = 255 * td->fnt->hgt + oy * 2; /* Resize increment */ sh->flags |= PResizeInc; sh->width_inc = td->fnt->wid; sh->height_inc = td->fnt->hgt; } /* Base window size */ sh->flags |= PBaseSize; sh->base_width = (ox + ox); sh->base_height = (oy + oy); /* Use the size hints */ XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh); /* Map the window */ Infowin_map(); /* Move the window to requested location */ if ((x >= 0) && (y >= 0)) Infowin_impell(x, y); /* Initialize the term */ term_init(t, cols, rows, num); /* Use a "soft" cursor */ t->soft_cursor = TRUE; /* Erase with "black space" */ t->attr_blank = TERM_DARK; t->char_blank = ' '; /* Hooks */ t->xtra_hook = Term_xtra_xpj; t->curs_hook = Term_curs_xpj; t->wipe_hook = Term_wipe_xpj; t->text_hook = Term_text_xpj; /* Save the data */ t->data = td; /* Activate (important) */ Term_activate(t); /* Success */ return (0); } /* * Initialization function for an "XPJ" module to Angband */ errr init_xpj(int argc, char *argv[]) { int i, j; cptr dpy_name = ""; int num_term = 3; char filename[1024]; int pict_wid = 0; int pict_hgt = 0; int graphmode; char *TmpData; /* Load graphics */ Display *dpy; XImage *tiles_raw; XImage *font_raw; /* Check tile size */ if (P_TILE_SIZE % 4) { quit("Need to compile with P_TILE_SIZE as a multiple of four."); } /* Parse args */ for (i = 1; i < argc; i++) { if (prefix(argv[i], "-d")) { dpy_name = &argv[i][2]; continue; } if (prefix(argv[i], "-s")) { smoothRescaling = FALSE; continue; } if (prefix(argv[i], "-n")) { num_term = atoi(&argv[i][2]); if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA; else if (num_term < 1) num_term = 1; continue; } plog_fmt("Ignoring option: %s", argv[i]); } /* Init the Metadpy if possible */ if (Metadpy_init_name(dpy_name)) return (-1); /* Prepare cursor color */ MAKE(xor, infoclr); Infoclr_set(xor); Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0); /* Prepare normal colors */ for (i = 0; i < 256; ++i) { Pixell pixel; MAKE(clr[i], infoclr); Infoclr_set(clr[i]); /* Acquire Angband colors */ color_table[i][0] = angband_color_table[i][0]; color_table[i][1] = angband_color_table[i][1]; color_table[i][2] = angband_color_table[i][2]; color_table[i][3] = angband_color_table[i][3]; /* Default to monochrome */ pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg); /* Handle color */ if (Metadpy->color) { /* Create pixel */ pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]); } /* Initialize the color */ Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0); } /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; /* Initialize the term_data */ term_data_init(td, i); /* Save global entry */ angband_term[i] = Term; } /* Raise the "Angband" window */ Infowin_set(data[0].win); Infowin_raise(); /* Activate the "Angband" window screen */ Term_activate(&data[0].t); if (arg_graphics) { /* And use tiles */ graphmode = GRAPHICS_ADAM_BOLT; } else { /* But not for monsters / items */ graphmode = GRAPHICS_HALF_3D; } /* Try graphics */ if (!pick_graphics(graphmode, &pict_wid, &pict_hgt, filename)) { quit("Could not initialise graphics! (Need 16x16.bmp)"); } dpy = Metadpy->dpy; /* Load the graphical tiles */ tiles_raw = ReadBMP(dpy, filename); /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; term *t = &td->t; /* Use graphics sometimes */ t->higher_pict = TRUE; if (i == 0) { /* Graphics hook */ t->pict_hook = Term_skew_xpj; /* Always use graphics */ t->always_pict = TRUE; /* Resize tiles */ td->tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, P_TILE_SIZE, P_TILE_SIZE); } else { /* Graphics hook */ t->pict_hook = Term_pict_xpj; /* Resize tiles */ td->tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->fnt->wid, td->fnt->hgt); } } /* Initialize the transparency masks */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; int ii, jj; int depth = DefaultDepth(dpy, DefaultScreen(dpy)); Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int total; /* Determine total bytes needed for image */ ii = 1; jj = (depth - 1) >> 2; while (jj >>= 1) ii <<= 1; /* Pad the scanline to a multiple of 4 bytes */ if (i == 0) { /* Main screen */ total = P_TILE_SIZE * ii; total = (total + 3) & ~3; total *= P_TILE_SIZE; } else { /* Other terms */ total = td->fnt->wid * ii; total = (total + 3) & ~3; total *= td->fnt->hgt; } /* Save number of bytes per pixel */ bytes_per_pixel = ii; switch (bytes_per_pixel) { case 1: { draw_block = draw_block8; /* Mega Hack^2 - assume the top left corner is "black" */ pix_blank = ((byte *) data[0].tiles->data) [6 * P_TILE_SIZE * 32]; break; } case 2: { draw_block = draw_block16; /* Mega Hack^2 - assume the top left corner is "black" */ pix_blank = ((u16b *) data[0].tiles->data) [6 * P_TILE_SIZE * 32]; break; } case 4: { draw_block = draw_block32; /* Mega Hack^2 - assume the top left corner is "black" */ pix_blank = ((u32b *) data[0].tiles->data) [6 * P_TILE_SIZE * 32]; break; } default: { quit("Unsupported bytes per pixel format of screen"); break; } } TmpData = (char *)malloc(total); if (i == 0) { /* Normal tiles */ td->TmpImage = XCreateImage(dpy,visual,depth, ZPixmap, 0, TmpData, P_TILE_SIZE, P_TILE_SIZE, 32, 0); /* Skewed tiles */ TmpData = (char *)malloc(total / 2); td->SkewImage = XCreateImage(dpy, visual, depth, ZPixmap, 0, TmpData, P_TILE_SIZE / 2, P_TILE_SIZE, 32, 0); } else { td->TmpImage = XCreateImage(dpy, visual, depth, ZPixmap, 0, TmpData, td->fnt->wid, td->fnt->hgt, 32, 0); } } /* Free tiles_raw? XXX XXX */ /* * Precalculate the tables used to draw the tiles * * This caches the position of each possibility of * where tiles can be. By masking the value in the table, * you can quickly determine what to draw. */ for (i = 0; i < P_TILE_SIZE / 2; i++) { for (j = 0; j < P_TILE_SIZE; j++) { /* Table 1 */ pj_table1[j][i] = PJ_T_WALLF; if ((j + i) % 2) { pj_table1[j][i] |= PJ_T_TOP1; pj_table1[j][i] |= PJ_T_WALLB; } else { pj_table1[j][i] |= PJ_T_TOP_T1; } if (j >= P_TILE_SIZE / 2) { pj_table1[j][i] |= PJ_T_OVER2; } else if (i < P_TILE_SIZE / 4) { pj_table1[j][i] |= PJ_T_OVER3; } else { pj_table1[j][i] |= PJ_T_OVER1; } if (i - j / 2 >= 0) { pj_table1[j][i] |= PJ_T_FLOOR1; if ((i + j) % 2) { pj_table1[j][i] |= PJ_T_WALL1; } else { pj_table1[j][i] |= PJ_T_WALL1_T; } } else { pj_table1[j][i] |= PJ_T_FLOOR2; } /* Table 2 */ pj_table2[j][i] = PJ_T_FLOOR1 | PJ_T_WALLF; if ((j + i) % 2) { pj_table2[j][i] |= PJ_T_WALLB; } if (j < P_TILE_SIZE / 2) { pj_table2[j][i] |= PJ_T_OVER1; } else if (i < P_TILE_SIZE / 4) { pj_table2[j][i] |= PJ_T_OVER2; } else { pj_table2[j][i] |= PJ_T_OVER4; } if (i - j / 2 >= 0) { if ((i + j) % 2) { pj_table2[j][i] |= PJ_T_TOP1; } else { pj_table2[j][i] |= PJ_T_TOP_T1; } } else { if ((i + j) % 2) { pj_table2[j][i] |= PJ_T_TOP2 | PJ_T_WALL2; } else { pj_table2[j][i] |= PJ_T_TOP_T2 | PJ_T_WALL2_T; } } } } /* Clear the arrays used for optimisation */ (void) C_WIPE(pj_row1, 64, u32b); (void) C_WIPE(pj_row2, 64, u32b); /* Hack - use crazy row to mark "nothing entered yet" */ pj_cur_row = -255; /* Try the "16x16.bmp" file */ path_make(filename, ANGBAND_DIR_XTRA, "font/16x16.txt"); /* Use the "16x16.bmp" file if it exists */ if (0 == fd_close(fd_open(filename, O_RDONLY))) { Display *dpy = Metadpy->dpy; /* Load the graphical tiles */ font_raw = ReadFONT(dpy, filename, 16); if (!font_raw) quit("Could not allocate font metrics!"); /* Hack - Resize font */ font_data = ResizeImage(dpy, font_raw, 16, 16, P_TILE_SIZE, P_TILE_SIZE); } else { quit("Could not initialise font metrics!"); } /* Success */ return (0); } #endif /* USE_XPJ */ zangband/src/main-xxx.c0000755000000000000000000005271010250356274014060 0ustar rootroot/* File: main-xxx.c */ /* Purpose: Sample visual module for Angband 2.8.1 */ /* * This file written by "Ben Harrison (benh@phial.com)". * * This file is intended to show one way to build a "visual module" * for Angband to allow it to work with a new system. It does not * actually work, but if the code near "XXX XXX XXX" comments were * replaced with functional code, then it probably would. * * See "z-term.c" for info on the concept of the "generic terminal", * and for more comments about what this file must supply. * * There are two basic ways to port Angband to a new system. The * first involves modifying the "main-gcu.c" and/or "main-x11.c" * files to support some version of "curses" and/or "X11" on your * machine, and to compile with the "USE_GCU" and/or "USE_X11" * compilation flags defined. The second involves creating a * new "main-xxx.c" file, based on this sample file (or on any * existing "main-xxx.c" file), and comes in two flavors, based * on whether it contains a "main()" function (as in "main-mac.c" * and "main-win.c") or not (as in "main-gcu.c" or "main-x11.c"). * * If the "main-xxx.c" file includes its own "main()" function, * then you should NOT link in the "main.c" file, and your "main()" * function must process any command line arguments, initialize the * "visual system", and call "play_game()" with appropriate arguments. * * If the "main-xxx.c" file does not include its own "main()" * function, then you must add some code to "main.c" which, if * the appropriate "USE_XXX" compilation flag is defined, will * attempt to call the "init_xxx()" function in the "main-xxx.c" * file, which should initialize the "visual system" and return * zero if it was successful. The "main()" function in "main.c" * will take care of processing command line arguments and then * calling "play_game()" with appropriate arguments. * * Note that the "util.c" file often contains functions which must * be modified in small ways for various platforms, even if you are * able to use the existing "main-gcu.c" and/or "main-x11.c" files, * in particular, the "file handling" functions may not work on all * systems. * * When you complete a port to a new system, you should email any * newly created files, and any changes made to existing files, * including "h-config.h", "z-config.h", and any of the "Makefile" * files, to "benh@phial.com" for inclusion in the next version. * * Try to stick to a "three letter" naming scheme for "main-xxx.c" * and "Makefile.xxx" and such for consistency and simplicity. */ #include "angband.h" #ifdef USE_XXX cptr help_xxx[] = { "To use XXX", "-f Do something", "-g Do something else", NULL }; /* * Extra data to associate with each "window" * * Each "window" is represented by a "term_data" structure, which * contains a "term" structure, which contains a pointer (t->data) * back to the term_data structure. */ typedef struct term_data term_data; struct term_data { term t; /* Other fields if needed XXX XXX XXX */ }; /* * Number of "term_data" structures to support XXX XXX XXX * * You MUST support at least one "term_data" structure, and the * game will currently use up to eight "term_data" structures if * they are available. * * If only one "term_data" structure is supported, then a lot of * the things that would normally go into a "term_data" structure * could be made into global variables instead. */ #define MAX_TERM_DATA 1 /* * An array of "term_data" structures, one for each "sub-window" */ static term_data data[MAX_TERM_DATA]; #if 0 /* Fix the syntax below XXX XXX XXX */ /* * The "color" array for the visual module XXX XXX XXX * * This table should be used in whetever way is necessary to * convert the Angband Color Indexes into the proper "color data" * for the visual system. On the Macintosh, these are arrays of * three shorts, on the IBM, these are combinations of the eight * basic color codes with optional "bright" bits, on X11, these * are actual "pixel" codes extracted from another table which * contains textual color names. * * The Angband Color Set (0 to 15): * Black, White, Slate, Orange, Red, Blue, Green, Umber * D-Gray, L-Gray, Violet, Yellow, L-Red, L-Blue, L-Green, L-Umber * * Colors 8 to 15 are basically "enhanced" versions of Colors 0 to 7. * * As decribed in one of the header files, in a perfect world, the * colors below should fit a nice clean "quartered" specification * in RGB codes, but this must often be Gamma Corrected. The 1/4 * parts of each Red,Green,Blue are shown in the comments below, * again, these values are *before* gamma correction. */ static local_color_data_type color_data[16] = { /* XXX XXX XXX 0,0,0 */, /* TERM_DARK */ /* XXX XXX XXX 4,4,4 */, /* TERM_WHITE */ /* XXX XXX XXX 2,2,2 */, /* TERM_SLATE */ /* XXX XXX XXX 4,2,0 */, /* TERM_ORANGE */ /* XXX XXX XXX 3,0,0 */, /* TERM_RED */ /* XXX XXX XXX 0,2,1 */, /* TERM_GREEN */ /* XXX XXX XXX 0,0,4 */, /* TERM_BLUE */ /* XXX XXX XXX 2,1,0 */, /* TERM_UMBER */ /* XXX XXX XXX 1,1,1 */, /* TERM_L_DARK */ /* XXX XXX XXX 3,3,3 */, /* TERM_L_WHITE */ /* XXX XXX XXX 4,0,4 */, /* TERM_VIOLET */ /* XXX XXX XXX 4,4,0 */, /* TERM_YELLOW */ /* XXX XXX XXX 4,0,0 */, /* TERM_L_RED */ /* XXX XXX XXX 0,4,0 */, /* TERM_L_GREEN */ /* XXX XXX XXX 0,4,4 */, /* TERM_L_BLUE */ /* XXX XXX XXX 3,2,1 */ /* TERM_L_UMBER */ }; #endif /*** Function hooks needed by "Term" ***/ /* * Init a new "term" * * This function should do whatever is necessary to prepare a new "term" * for use by the "term.c" package. This may include clearing the window, * preparing the cursor, setting the font/colors, etc. Usually, this * function does nothing, and the "init_xxx()" function does it all. */ static void Term_init_xxx(term *t) { term_data *td = (term_data*)(t->data); /* XXX XXX XXX */ } /* * Nuke an old "term" * * This function is called when an old "term" is no longer needed. It should * do whatever is needed to clean up before the program exits, such as wiping * the screen, restoring the cursor, fixing the font, etc. Often this function * does nothing and lets the operating system clean up when the program quits. */ static void Term_nuke_xxx(term *t) { term_data *td = (term_data*)(t->data); /* XXX XXX XXX */ } /* * Do a "user action" on the current "term" * * This function allows the visual module to do implementation defined * things when the user activates the "system defined command" command. * * This function is normally not used. * * In general, this function should return zero if the action is successfully * handled, and non-zero if the action is unknown or incorrectly handled. */ static errr Term_user_xxx(int n) { term_data *td = (term_data*)(Term->data); /* XXX XXX XXX */ /* Unknown */ return (1); } /* * Do a "special thing" to the current "term" * * This function must react to a large number of possible arguments, each * corresponding to a different "action request" by the "z-term.c" package, * or by the application itself. * * The "action type" is specified by the first argument, which must be a * constant of the form "TERM_XTRA_*" as given in "term.h", and the second * argument specifies the "information" for that argument, if any, and will * vary according to the first argument. * * In general, this function should return zero if the action is successfully * handled, and non-zero if the action is unknown or incorrectly handled. */ static errr Term_xtra_xxx(int n, int v) { term_data *td = (term_data*)(Term->data); /* Analyze */ switch (n) { case TERM_XTRA_EVENT: { /* * Process some pending events XXX XXX XXX * * Wait for at least one event if "v" is non-zero * otherwise, if no events are ready, return at once. * When "keypress" events are encountered, the "ascii" * value corresponding to the key should be sent to the * "Term_keypress()" function. Certain "bizarre" keys, * such as function keys or arrow keys, may send special * sequences of characters, such as control-underscore, * plus letters corresponding to modifier keys, plus an * underscore, plus carriage return, which can be used by * the main program for "macro" triggers. This action * should handle as many events as is efficiently possible * but is only required to handle a single event, and then * only if one is ready or "v" is true. * * This action is required. */ return (0); } case TERM_XTRA_FLUSH: { /* * Flush all pending events XXX XXX XXX * * This action should handle all events waiting on the * queue, optionally discarding all "keypress" events, * since they will be discarded anyway in "z-term.c". * * This action is required, but may not be "essential". */ return (0); } case TERM_XTRA_SHAPE: { /* * Set the cursor visibility XXX XXX XXX * * This action should change the visibility of the cursor, * if possible, to the requested value (0=off, 1=on) * * This action is optional, but can improve both the * efficiency (and attractiveness) of the program. */ return (0); } case TERM_XTRA_FROSH: { /* * Flush a row of output XXX XXX XXX * * This action should make sure that row "v" of the "output" * to the window will actually appear on the window. * * This action is optional, assuming that "Term_text_xxx()" * (and similar functions) draw directly to the screen, or * that the "TERM_XTRA_FRESH" entry below takes care of any * necessary flushing issues. */ return (0); } case TERM_XTRA_FRESH: { /* * Flush output XXX XXX XXX * * This action should make sure that all "output" to the * window will actually appear on the window. * * This action is optional, assuming that "Term_text_xxx()" * (and similar functions) draw directly to the screen, or * that the "TERM_XTRA_FROSH" entry above takes care of any * necessary flushing issues. */ return (0); } case TERM_XTRA_NOISE: { /* * Make a noise XXX XXX XXX * * This action should produce a "beep" noise. * * This action is optional, but convenient. */ return (0); } case TERM_XTRA_SOUND: { /* * Make a sound XXX XXX XXX * * This action should produce sound number "v", where the * "name" of that sound is "sound_names[v]". This method * is still under construction. * * This action is optional, and not very important. */ return (0); } case TERM_XTRA_BORED: { /* * Handle random events when bored XXX XXX XXX * * This action is optional, and normally not important */ return (0); } case TERM_XTRA_REACT: { /* * React to global changes XXX XXX XXX * * For example, this action can be used to react to * changes in the global "color_table[256][4]" array. * * This action is optional, but can be very useful for * handling "color changes" and the "arg_sound" and/or * "arg_graphics" options. */ return (0); } case TERM_XTRA_ALIVE: { /* * Change the "hard" level XXX XXX XXX * * This action is used if the program changes "aliveness" * by being either "suspended" (v=0) or "resumed" (v=1) * This action is optional, unless the computer uses the * same "physical screen" for multiple programs, in which * case this action should clean up to let other programs * use the screen, or resume from such a cleaned up state. * * This action is currently only used by "main-gcu.c", * on UNIX machines, to allow proper "suspending". */ return (0); } case TERM_XTRA_LEVEL: { /* * Change the "soft" level XXX XXX XXX * * This action is used when the term window changes "activation" * either by becoming "inactive" (v=0) or "active" (v=1) * * This action can be used to do things like activate the proper * font / drawing mode for the newly active term window. This * action should NOT change which window has the "focus", which * window is "raised", or anything like that. * * This action is optional if all the other things which depend * on what term is active handle activation themself, or if only * one "term_data" structure is supported by this file. */ return (0); } case TERM_XTRA_DELAY: { /* * Delay for some milliseconds XXX XXX XXX * * This action is useful for proper "timing" of certain * visual effects, such as breath attacks. * * This action is optional, but may be required by this file, * especially if special "macro sequences" must be supported. */ return (0); } } /* Unknown or Unhandled action */ return (1); } /* * Display the cursor * * This routine should display the cursor at the given location * (x,y) in some manner. On some machines this involves actually * moving the physical cursor, on others it involves drawing a fake * cursor in some form of graphics mode. Note the "soft_cursor" * flag which tells "z-term.c" to treat the "cursor" as a "visual" * thing and not as a "hardware" cursor. * * You may assume "valid" input if the window is properly sized. * * You may use the "Term_grab(x, y, &a, &c)" function, if needed, * to determine what attr/char should be "under" the new cursor, * for "inverting" purposes or whatever. */ static errr Term_curs_xxx(int x, int y) { term_data *td = (term_data*)(Term->data); /* XXX XXX XXX */ /* Success */ return (0); } /* * Erase some characters * * This function should erase "n" characters starting at (x,y). * * You may assume "valid" input if the window is properly sized. */ static errr Term_wipe_xxx(int x, int y, int n) { term_data *td = (term_data*)(Term->data); /* XXX XXX XXX */ /* Success */ return (0); } /* * Draw some text on the screen * * This function should actually display an array of characters * starting at the given location, using the given "attribute", * and using the given string of characters, which contains * exactly "n" characters and which is NOT null-terminated. * * You may assume "valid" input if the window is properly sized. * * You must be sure that the string, when written, erases anything * (including any visual cursor) that used to be where the text is * drawn. On many machines this happens automatically, on others, * you must first call "Term_wipe_xxx()" to clear the area. * * In color environments, you should activate the color contained * in "color_data[a & 0x0F]", if needed, before drawing anything. * * You may ignore the "attribute" if you are only supporting a * monochrome environment, since this routine is normally never * called to display "black" (invisible) text, including the * default "spaces", and all other colors should be drawn in * the "normal" color in a monochrome environment. * * Note that if you have changed the "attr_blank" to something * which is not black, then this function must be able to draw * the resulting "blank" correctly. * * Note that this function must correctly handle "black" text if * the "always_text" flag is set, if this flag is not set, all the * "black" text will be handled by the "Term_wipe_xxx()" hook. */ static errr Term_text_xxx(int x, int y, int n, byte a, const char *cp) { term_data *td = (term_data*)(Term->data); /* XXX XXX XXX */ /* Success */ return (0); } /* * Draw some attr/char pairs on the screen * * This routine should display the given "n" attr/char pairs at * the given location (x,y). This function is only used if one * of the flags "always_pict" or "higher_pict" is defined. * * You must be sure that the attr/char pairs, when displayed, will * erase anything (including any visual cursor) that used to be at * the given location. On many machines this is automatic, but on * others, you must first call "Term_wipe_xxx(x, y, 1)". * * With the "higher_pict" flag, this function can be used to allow * the display of "pseudo-graphic" pictures, for example, by using * the attr/char pair as an encoded index into a pixmap of special * "pictures". * * With the "always_pict" flag, this function can be used to force * every attr/char pair to be drawn by this function, which can be * very useful if this file can optimize its own display calls. * * This function is often associated with the "arg_graphics" flag. * * This function is only used if one of the "higher_pict" and/or * "always_pict" flags are set. */ static errr Term_pict_xxx(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { term_data *td = (term_data*)(Term->data); /* XXX XXX XXX */ /* Success */ return (0); } /*** Internal Functions ***/ /* * Instantiate a "term_data" structure * * This is one way to prepare the "term_data" structures and to * "link" the various informational pieces together. * * This function assumes that every window should be 80x24 in size * (the standard size) and should be able to queue 256 characters. * Technically, only the "main screen window" needs to queue any * characters, but this method is simple. One way to allow some * variation is to add fields to the "term_data" structure listing * parameters for that window, initialize them in the "init_xxx()" * function, and then use them in the code below. * * Note that "activation" calls the "Term_init_xxx()" hook for * the "term" structure, if needed. */ static void term_data_link(int i) { term_data *td = &data[i]; /* Initialize the term */ term_init(td->t, 80, 24, 256); /* Choose "soft" or "hard" cursor XXX XXX XXX */ /* A "soft" cursor must be explicitly "drawn" by the program */ /* while a "hard" cursor has some "physical" existance and is */ /* moved whenever text is drawn on the screen. See "term.c". */ /* td->t->soft_cursor = TRUE; */ /* Avoid the "corner" of the window XXX XXX XXX */ /* td->t->icky_corner = TRUE; */ /* Use "Term_pict()" for all attr/char pairs XXX XXX XXX */ /* See the "Term_pict_xxx()" function above. */ /* td->t->always_pict = TRUE; */ /* Use "Term_pict()" for some attr/char pairs XXX XXX XXX */ /* See the "Term_pict_xxx()" function above. */ /* td->t->higher_pict = TRUE; */ /* Use "Term_text()" even for "black" text XXX XXX XXX */ /* See the "Term_text_xxx()" function above. */ /* td->t->always_text = TRUE; */ /* Ignore the "TERM_XTRA_BORED" action XXX XXX XXX */ /* This may make things slightly more efficient. */ /* td->t->never_bored = TRUE; */ /* Ignore the "TERM_XTRA_FROSH" action XXX XXX XXX */ /* This may make things slightly more efficient. */ /* td->t->never_frosh = TRUE; */ /* Erase with "white space" (use if don't have Term_wipe_xxx) XXX XXX XXX */ /* td->t->attr_blank = TERM_WHITE; */ /* td->t->char_blank = ' '; */ /* Prepare the init/nuke hooks */ td->t->init_hook = Term_init_xxx; td->t->nuke_hook = Term_nuke_xxx; /* Prepare the template hooks */ td->t->user_hook = Term_user_xxx; td->t->xtra_hook = Term_xtra_xxx; td->t->curs_hook = Term_curs_xxx; td->t->wipe_hook = Term_wipe_xxx; td->t->text_hook = Term_text_xxx; td->t->pict_hook = Term_pict_xxx; /* Remember where we came from */ td->t->data = (vptr)(td); /* Activate it */ Term_activate(td->t); /* Global pointer */ ang_term[i] = td->t; } /* * Initialization function */ errr init_xxx(void) { /* Initialize globals XXX XXX XXX */ /* Initialize "term_data" structures XXX XXX XXX */ /* Create windows (backwards!) */ for (i = TERM_DATA_MAX - 1; i >= 0; i--) { /* Link */ term_data_link(i); } /* Success */ return (0); } #ifdef INTERNAL_MAIN /* * Some special machines need their own "main()" function, which they * can provide here, making sure NOT to compile the "main.c" file. * * These systems usually have some form of "event loop", run forever * as the last step of "main()", which handles things like menus and * window movement, and calls "play_game(FALSE)" to load a game after * initializing "savefile" to a filename, or "play_game(TRUE)" to make * a new game. The event loop would also be triggered by "Term_xtra()" * (the TERM_XTRA_EVENT action), in which case the event loop would not * actually "loop", but would run once and return. */ /* * An event handler XXX XXX XXX * * You may need an event handler, which can be used by both * by the "TERM_XTRA_BORED" and "TERM_XTRA_EVENT" entries in * the "Term_xtra_xxx()" function, and also to wait for the * user to perform whatever user-interface operation is needed * to request the start of a new game or the loading of an old * game, both of which should launch the "play_game()" function. */ static bool CheckEvents(bool wait) { /* XXX XXX XXX */ return (0); } /* * Init some stuff * * This function is used to keep the "path" variable off the stack. */ static void init_stuff(void) { char path[1024]; /* Prepare the path XXX XXX XXX */ /* This must in some way prepare the "path" variable */ /* so that it points at the "lib" directory. Every */ /* machine handles this in a different way... */ strcpy(path, "XXX XXX XXX"); /* Prepare the filepaths */ init_file_paths(path); } /* * Main function * * This function must do a lot of stuff. */ int main(int argc, char *argv[]) { /* Initialize the machine itself XXX XXX XXX */ /* Process command line arguments XXX XXX XXX */ /* Initialize the windows */ if (init_xxx()) quit("Oops!"); /* XXX XXX XXX */ ANGBAND_SYS = "xxx"; /* Initialize some stuff */ init_stuff(); /* Initialize */ init_angband */ /* Allow auto-startup XXX XXX XXX */ /* Event loop forever XXX XXX XXX */ while (TRUE) CheckEvents(TRUE); } #endif /* INTERNAL_MAIN */ #endif /* USE_XXX */ zangband/src/main.c0000755000000000000000000002525710250356274013241 0ustar rootroot/* File: main.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ #include "angband.h" /* * Some machines have a "main()" function in their "main-xxx.c" file, * all the others use this file for their "main()" function. */ #if !defined(MACINTOSH) && !defined(WINDOWS) && !defined(ACORN) && !defined(MACH_O_CARBON) /* * List of available modules in the order they are tried. */ static const module_type modules[] = { #ifdef USE_GTK INIT_MODULE(gtk), #endif /* USE_GTK */ #ifdef USE_XAW INIT_MODULE(xaw), #endif /* USE_XAW */ #ifdef USE_X11 INIT_MODULE(x11), #endif /* USE_X11 */ #ifdef USE_XPJ INIT_MODULE(xpj), #endif /* USE_XPJ */ #ifdef USE_TNB INIT_MODULE(tnb), #endif /* USE_TNB */ #ifdef USE_GCU INIT_MODULE(gcu), #endif /* USE_GCU */ #ifdef USE_CAP INIT_MODULE(cap), #endif /* USE_CAP */ #ifdef USE_DOS INIT_MODULE(dos), #endif /* USE_DOS */ #ifdef USE_IBM INIT_MODULE(ibm), #endif /* USE_IBM */ #ifdef USE_EMX INIT_MODULE(emx), #endif /* USE_EMX */ #ifdef USE_SLA INIT_MODULE(sla), #endif /* USE_SLA */ #ifdef USE_LSL INIT_MODULE(lsl), #endif /* USE_LSL */ #ifdef USE_AMI INIT_MODULE(ami), #endif /* USE_AMI */ #ifdef USE_VME INIT_MODULE(vme), #endif /* USE_VME */ #ifdef USE_VCS INIT_MODULE(vcs) #endif /* USE_VCS */ }; /* * A hook for "quit()". * * Close down, then fall back into "quit()". */ static void quit_hook(cptr s) { int j; /* Unused parameter */ (void)s; /* Scan windows */ for (j = ANGBAND_TERM_MAX - 1; j >= 0; j--) { /* Unused */ if (!angband_term[j]) continue; /* Nuke it */ term_nuke(angband_term[j]); } } /* * Set the stack size (for the Amiga) */ #ifdef AMIGA #include __near long __stack = 32768L; #endif /* AMIGA */ /* * Set the stack size and overlay buffer (see main-286.c") */ #ifdef USE_286 #include extern unsigned _stklen = 32768U; extern unsigned _ovrbuffer = 0x1500; #endif /* USE_286 */ /* * Initialize and verify the file paths, and the score file. * * Use the ANGBAND_PATH environment var if possible, else use * DEFAULT_PATH, and in either case, branch off appropriately. * * First, we'll look for the ANGBAND_PATH environment variable, * and then look for the files in there. If that doesn't work, * we'll try the DEFAULT_PATH constant. So be sure that one of * these two things works... * * We must ensure that the path ends with "PATH_SEP" if needed, * since the "init_file_paths()" function will simply append the * relevant "sub-directory names" to the given path. * * Note that the "path" must be "Angband:" for the Amiga, and it * is ignored for "VM/ESA", so I just combined the two. * * Make sure that the path doesn't overflow the buffer. We have * to leave enough space for the path separator, directory, and * filenames. */ static void init_stuff(void) { char path[1024]; int len = 0; #if defined(AMIGA) || defined(VM) /* Hack -- prepare "path" */ strnfmt(path, 511, "Angband:"); #else /* AMIGA / VM */ cptr tail = NULL; path[0] = 0; #ifndef FIXED_PATHS /* Get the environment variable */ tail = getenv("ANGBAND_PATH"); #endif /* FIXED_PATHS */ /* Use the angband_path, or a default */ strnfcat(path, 511, &len, "%s", tail ? tail : DEFAULT_PATH); /* Hack -- Add a path separator (only if needed) */ if (!suffix(path, PATH_SEP)) strnfcat(path, 511, &len, PATH_SEP); #endif /* AMIGA / VM */ #ifdef HAVE_CONFIG_H { /* * XXX XXX Hack - fall back to './lib/' if DEFAULT_PATH doesn't work. * Autoconf sets the default installation directory to be in * /usr/local/share/games/zangband/lib/ however, for development, that sucks. */ char buf[1024]; int fd = -1; /* Look for "news" file - see init2.c */ path_make(buf, path, "file/news.txt"); fd = fd_open(buf, O_RDONLY); /* Failure */ if (fd < 0) { /* plog_fmt("Cannot access the '%s' file!", buf); */ /* Reset to be "./lib/" */ strcpy(path, "./lib/"); } fd_close(fd); } #endif /* HAVE_CONFIG_H */ /* Initialize */ init_file_paths(path); } /* * Handle a "-d=" option * * The "" can be any string starting with the same letter as the * name of a subdirectory of the "lib" folder (i.e. "i" or "info"). * * The "" can be any legal path for the given system, and should * not end in any special path separator (i.e. "/tmp" or "~/.ang-info"). */ static void change_path(cptr info) { cptr s; /* Find equal sign */ s = strchr(info, '='); /* Verify equal sign */ if (!s) quit_fmt("Try '-d=' not '-d%s'", info); /* Analyze */ switch (tolower(info[0])) { #ifndef FIXED_PATHS case 'a': { string_free(ANGBAND_DIR_APEX); ANGBAND_DIR_APEX = string_make(s + 1); break; } case 'f': { string_free(ANGBAND_DIR_FILE); ANGBAND_DIR_FILE = string_make(s + 1); break; } case 'h': { string_free(ANGBAND_DIR_HELP); ANGBAND_DIR_HELP = string_make(s + 1); break; } case 'i': { string_free(ANGBAND_DIR_INFO); ANGBAND_DIR_INFO = string_make(s + 1); break; } case 'x': { string_free(ANGBAND_DIR_XTRA); ANGBAND_DIR_XTRA = string_make(s + 1); break; } case 'b': { string_free(ANGBAND_DIR_BONE); ANGBAND_DIR_BONE = string_make(s + 1); break; } case 'd': { string_free(ANGBAND_DIR_DATA); ANGBAND_DIR_DATA = string_make(s + 1); break; } case 'e': { string_free(ANGBAND_DIR_EDIT); ANGBAND_DIR_EDIT = string_make(s + 1); break; } case 's': { string_free(ANGBAND_DIR_SAVE); ANGBAND_DIR_SAVE = string_make(s + 1); break; } case 'z': { string_free(ANGBAND_DIR_SCRIPT); ANGBAND_DIR_SCRIPT = string_make(s + 1); break; } #endif /* FIXED_PATHS */ case 'u': { string_free(ANGBAND_DIR_USER); ANGBAND_DIR_USER = string_make(s + 1); break; } default: { quit_fmt("Bad semantics in '-d%s'", info); } } } /* * The default message to print when we get bad input. */ static void game_usage(void) { int i, j; /* Dump usage information */ puts("Usage: angband [options] [-- subopts]"); puts(" -n Start a new character"); puts(" -f Request fiddle (verbose) mode"); puts(" -w Request wizard mode"); puts(" -v Request sound mode"); puts(" -g Request graphics mode"); puts(" -t Request bigtile mode"); puts(" -o Request original keyset (default)"); puts(" -r Request rogue-like keyset"); puts(" -M Request monochrome mode"); puts(" -s Show high scores (default 10)"); puts(" -u Use your savefile"); #ifdef FIXED_PATHS puts(" -du= Define user dir path"); #else /* FIXED_PATHS */ puts(" -d Define a 'lib' dir sub-path"); #endif /* FIXED_PATHS */ /* Print the name and help for each available module */ for (i = 0; i < (int)NUM_ELEMENTS(modules); i++) { /* Spacer */ puts(""); for (j = 0; modules[i].help[j]; j++) { if (j) { /* Display seperator */ if (j == 1) { puts(" -- Sub options"); } puts(format(" -- %s", modules[i].help[j])); } else { /* The first line is special */ puts(format(" -m%s %s", modules[i].name, modules[i].help[j])); } } } /* Actually abort the process */ quit(NULL); } /* * Simple "main" function for multiple platforms. * * Note the special "--" option which terminates the processing of * standard options. All non-standard options (if any) are passed * directly to the "init_xxx()" function. */ int main(int argc, char *argv[]) { int i; bool done = FALSE; bool new_game = FALSE; int show_score = 0; cptr mstr = NULL; bool args = TRUE; /* Save the "program name" XXX XXX XXX */ argv0 = argv[0]; #ifdef USE_286 /* Attempt to use XMS (or EMS) memory for swap space */ if (_OvrInitExt(0L, 0L)) { _OvrInitEms(0, 0, 64); } #endif /* USE_286 */ /* Get the file paths */ init_stuff(); /* Catch nasty signals */ signals_init(); /* Drop permissions and initialize multiuser stuff */ init_setuid(); /* Process the command line arguments */ for (i = 1; args && (i < argc); i++) { /* Require proper options */ if (argv[i][0] != '-') game_usage(); /* Analyze option */ switch (argv[i][1]) { case 'N': case 'n': { new_game = TRUE; break; } case 'F': case 'f': { arg_fiddle = TRUE; break; } case 'W': case 'w': { arg_wizard = TRUE; break; } case 'V': case 'v': { arg_sound = TRUE; break; } case 'G': case 'g': { arg_graphics = TRUE; break; } case 'T': case 't': { arg_bigtile = TRUE; break; } case 'R': case 'r': { arg_force_roguelike = TRUE; break; } case 'O': case 'o': { arg_force_original = TRUE; break; } case 'S': case 's': { show_score = atoi(&argv[i][2]); if (show_score <= 0) show_score = 10; break; } case 'u': case 'U': { if (!argv[i][2]) game_usage(); /* Get the savefile name */ strncpy(player_name, &argv[i][2], 32); /* Make sure it's terminated */ player_name[31] = '\0'; break; } case 'm': { if (!argv[i][2]) game_usage(); mstr = &argv[i][2]; break; } case 'M': { arg_monochrome = TRUE; break; } case 'd': case 'D': { change_path(&argv[i][2]); break; } case '-': { argv[i] = argv[0]; argc = argc - i; argv = argv + i; args = FALSE; break; } default: { /* Default usage-help */ game_usage(); } } } /* Hack -- Forget standard args */ if (args) { argc = 1; argv[1] = NULL; } /* Process the player name */ process_player_name(TRUE); /* Install "quit" hook */ quit_aux = quit_hook; for (i = 0; i < (int)NUM_ELEMENTS(modules); i++) { if (!mstr || (streq(mstr, modules[i].name))) { /* Try to use port */ if (0 == modules[i].init(argc, argv, (unsigned char *)&new_game)) { /* Set port name */ ANGBAND_SYS = modules[i].name; done = TRUE; break; } } } /* Make sure we have a display! */ if (!done) quit("Unable to prepare any 'display module'!"); /* Gtk and Tk initialise earlier */ if (!(streq(ANGBAND_SYS, "gtk") || streq(ANGBAND_SYS, "tnb"))) { /* Initialize */ init_angband(); } /* Hack -- If requested, display scores and quit */ if (show_score > 0) display_scores(0, show_score); /* Wait for response */ pause_line(23); /* Play the game */ play_game(new_game); /* Free resources */ cleanup_angband(); /* Quit */ quit(NULL); /* Exit */ return (0); } #endif /* !defined(MACINTOSH) && !defined(WINDOWS) && !defined(ACORN) */ zangband/src/melee1.c0000755000000000000000000007613110250356274013462 0ustar rootroot/* File: melee1.c */ /* Purpose: Monster attacks */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Critical blow. All hits that do 95% of total possible damage, * and which also do at least 20 damage, or, sometimes, N damage. * This is used only to determine "cuts" and "stuns". */ static int monster_critical(int dice, int sides, int dam) { int max = 0; int total = dice * sides; /* Luck isn't always good for you... */ if (FLAG(p_ptr, TR_STRANGE_LUCK)) dam = dam * 3 / 2; /* Must do at least 95% of perfect */ if (dam < total * 19 / 20) return (0); /* Weak blows rarely work */ if ((dam < 20) && (randint0(100) >= dam)) return (0); /* Perfect damage */ if (dam == total) max++; /* Super-charge */ if (dam >= 20) { while (one_in_(50)) max++; } /* Critical damage */ if (dam > 160) return (6 + max); if (dam > 80) return (5 + max); if (dam > 40) return (4 + max); if (dam > 20) return (3 + max); if (dam > 10) return (2 + max); return (1 + max); } /* * Determine if a monster attack against the player succeeds. * Always miss 5% of the time, Always hit 5% of the time. * Otherwise, match monster power against player armor. */ static int check_hit(int power, int level) { int i, k, ac; /* Percentile dice */ k = randint0(100); /* Hack -- Always miss or hit */ if (k < 10) return (k < 5); /* Calculate the "attack quality" */ i = (power + (level * 3)); /* Total armor */ ac = p_ptr->ac + p_ptr->to_a; /* Power and Level compete against Armor */ if ((i > 0) && (randint1(i) > ((ac * 3) / 4))) return (TRUE); /* Assume miss */ return (FALSE); } /* * Hack -- possible "insult" messages */ static cptr desc_insult[] = { "insults you!", "insults your mother!", "gives you the finger!", "humiliates you!", "defiles you!", "dances around you!", "makes obscene gestures!", "moons you!!!" }; /* * Hack -- possible "insult" messages */ static cptr desc_moan[] = { "seems sad about something.", "asks if you have seen his dogs.", "tells you to get off his land.", "mumbles something about mushrooms." }; /* * The monster wants to flee so print a message. */ void flee_message(cptr m_name, u16b r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Immobile monsters can never flee */ if (FLAG(r_ptr, RF_NEVER_MOVE)) return; /* Sound */ sound(SOUND_FLEE); /* Message */ msgf(MSGT_FLEE, "%^s flees in terror!", m_name); msg_effect(MSG_FLEE, r_idx); } /* * Attack the player via physical attacks. */ bool make_attack_normal(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int ap_cnt; int k, tmp, ac, rlev; bool do_cut, do_stun; s32b gold; object_type *o_ptr; char m_name[80]; char ddesc[80]; bool blinked; bool touched = FALSE, fear = FALSE, alive = TRUE; bool explode = FALSE; bool resist_drain = FALSE; /* Save visibility */ bool visible = m_ptr->ml; /* Not allowed to attack */ if (FLAG(r_ptr, RF_NEVER_BLOW)) return (FALSE); /* ...nor if friendly */ if (!is_hostile(m_ptr)) return FALSE; /* Total armor */ ac = p_ptr->ac + p_ptr->to_a; /* Extract the effective monster level */ rlev = ((r_ptr->hdice * 2 >= 1) ? r_ptr->level : 1); /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0, 80); /* Get the "died from" information (i.e. "a kobold") */ monster_desc(ddesc, m_ptr, 0x88, 80); /* Assume no blink */ blinked = FALSE; /* Scan through all four blows */ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++) { bool obvious = FALSE; int power = 0; int damage = 0; cptr act = NULL; /* Extract the attack infomation */ int effect = r_ptr->blow[ap_cnt].effect; int method = r_ptr->blow[ap_cnt].method; int d_dice = r_ptr->blow[ap_cnt].d_dice; int d_side = r_ptr->blow[ap_cnt].d_side; /* Stop attacking if the aggressor dies (fire sheath etc.) */ if (!alive) break; /* Hack -- no more attacks */ if (!method) break; /* Stop if player is dead or gone */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Handle "leaving" */ if (p_ptr->state.leaving) break; /* Extract the attack "power" */ switch (effect) { case RBE_HURT: { power = 60; break; } case RBE_POISON: { power = 5; break; } case RBE_UN_BONUS: { power = 20; break; } case RBE_UN_POWER: { power = 15; break; } case RBE_EAT_GOLD: { power = 5; break; } case RBE_EAT_ITEM: { power = 5; break; } case RBE_EAT_FOOD: { power = 5; break; } case RBE_EAT_LITE: { power = 5; break; } case RBE_ACID: { power = 0; break; } case RBE_ELEC: { power = 10; break; } case RBE_FIRE: { power = 10; break; } case RBE_COLD: { power = 10; break; } case RBE_BLIND: { power = 2; break; } case RBE_CONFUSE: { power = 10; break; } case RBE_TERRIFY: { power = 10; break; } case RBE_PARALYZE: { power = 2; break; } case RBE_LOSE_STR: { power = 0; break; } case RBE_LOSE_DEX: { power = 0; break; } case RBE_LOSE_CON: { power = 0; break; } case RBE_LOSE_INT: { power = 0; break; } case RBE_LOSE_WIS: { power = 0; break; } case RBE_LOSE_CHR: { power = 0; break; } case RBE_LOSE_ALL: { power = 2; break; } case RBE_SHATTER: { power = 60; break; } case RBE_EXP_10: { power = 5; break; } case RBE_EXP_20: { power = 5; break; } case RBE_EXP_40: { power = 5; break; } case RBE_EXP_80: { power = 5; break; } case RBE_DISEASE: { power = 5; break; } case RBE_TIME: { power = 5; break; } case RBE_EXP_VAMP: { power = 5; break; } } /* Monster hits player */ if (!effect || check_hit(power, rlev)) { int protect = 0; /* Always disturbing */ disturb(TRUE); if (FLAG(p_ptr, TR_SLAY_DRAGON) && FLAG(r_ptr, RF_DRAGON)) protect = 5; else if (FLAG(p_ptr, TR_SLAY_DEMON) && FLAG(r_ptr, RF_DEMON)) protect = 5; else if (FLAG(p_ptr, TR_SLAY_UNDEAD) && FLAG(r_ptr, RF_UNDEAD)) protect = 4; else if (FLAG(p_ptr, TR_SLAY_ORC) && FLAG(r_ptr, RF_ORC)) protect = 4; else if (FLAG(p_ptr, TR_SLAY_TROLL) && FLAG(r_ptr, RF_TROLL)) protect = 4; else if (FLAG(p_ptr, TR_SLAY_GIANT) && FLAG(r_ptr, RF_GIANT)) protect = 4; else if (FLAG(p_ptr, TR_SLAY_ANIMAL) && FLAG(r_ptr, RF_ANIMAL)) protect = 3; else if (FLAG(p_ptr, TR_SLAY_EVIL) && FLAG(r_ptr, RF_EVIL)) protect = 3; else if ((p_ptr->tim.protevil > 0) && FLAG(r_ptr, RF_EVIL)) protect = 3; /* Protection gets stronger with experience */ protect *= p_ptr->lev + 20; /* Apply "protection" */ if (protect > 0 && randint1(protect) > 2 * (rlev + 20)) { #if 0 /* Remember the Evil-ness */ if (m_ptr->ml) { r_ptr->r_flags[2] |= RF2_EVIL; } #endif /* Message */ msgf("%^s is repelled.", m_name); /* Hack -- Next attack */ continue; } /* Get action */ act = format(rbm_info[method].action, "you"); /* Get flag status */ touched = rbm_info[method].touched; do_cut = rbm_info[method].cut; do_stun = rbm_info[method].stun; /* Play the sound */ if (rbm_info[method].sound) { sound(rbm_info[method].sound); } /* Special cases */ if (method == RBM_EXPLODE) { explode = TRUE; } else if (method == RBM_INSULT) { act = desc_insult[randint0(8)]; } else if (method == RBM_MOAN) { act = desc_moan[randint0(4)]; } else if (method == RBM_SHOW) { if (one_in_(3)) { act = "sings 'We are a happy family.'"; } else { act = "sings 'I love you, you love me.'"; } } /* Message */ if (act) { if ((p_ptr->tim.image) && one_in_(3)) { msgf("%^s %s you.", m_name, silly_attacks[randint0(MAX_SILLY_ATTACK)]); } else msgf("%^s %s", m_name, act); } /* Hack -- assume all attacks are obvious */ obvious = TRUE; /* Roll out the damage */ damage = damroll(d_dice, d_side); /* * Skip the effect when exploding, since the explosion * already causes the effect. */ if (!explode) { /* Apply appropriate damage */ switch (effect) { case 0: { /* Hack -- Assume obvious */ obvious = TRUE; /* Hack -- No damage */ damage = 0; break; } case RBE_HURT: { /* Obvious */ obvious = TRUE; /* Hack -- Player armor reduces total damage */ damage -= (damage * ((ac < 150) ? ac : 150) / 250); /* Take damage */ take_hit(damage, ddesc); break; } case RBE_POISON: { /* Message */ msgf("You receive a dose of poison!"); /* Special damage */ obvious = pois_dam(damage, ddesc, randint1(rlev) + 5); /* Learn about the player */ update_smart_learn(m_idx, DRS_POIS); break; } case RBE_UN_BONUS: { /* Take some damage */ take_hit(damage, ddesc); /* Allow complete resist */ if (!(FLAG(p_ptr, TR_RES_DISEN))) { /* Apply disenchantment */ if (apply_disenchant()) obvious = TRUE; } /* Learn about the player */ update_smart_learn(m_idx, DRS_DISEN); break; } case RBE_UN_POWER: { /* Take some damage */ take_hit(damage, ddesc); /* Find an item */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Only work some of the time */ if (one_in_(2)) continue; /* Drain charged wands/staffs */ if (((o_ptr->tval == TV_STAFF) || (o_ptr->tval == TV_WAND)) && (o_ptr->pval)) { /* Calculate healed hitpoints */ int heal = rlev * o_ptr->pval * o_ptr->number; /* Don't heal more than max hp */ heal = MIN(heal, m_ptr->maxhp - m_ptr->hp); /* Message */ msgf("Energy drains from your pack!"); /* Obvious */ obvious = TRUE; /* Heal the monster */ m_ptr->hp += heal; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Uncharge */ if (o_ptr->tval == TV_WAND) { o_ptr->ac += o_ptr->pval; } o_ptr->pval = 0; /* Notice changes */ notice_inven(); /* Done */ break; } } OBJ_ITT_END; break; } case RBE_EAT_GOLD: { /* Take some damage */ take_hit(damage, ddesc); /* Confused monsters cannot steal successfully. -LM- */ if (m_ptr->confused) break; /* Obvious */ obvious = TRUE; /* Saving throw (unless paralyzed) based on dex and level */ if (!p_ptr->tim.paralyzed && (randint0(100) < (adj_dex_safe[p_ptr->stat[A_DEX].ind] + p_ptr->lev))) { /* Saving throw message */ msgf("You quickly protect your money pouch!"); /* Occasional blink anyway */ if (!one_in_(3)) blinked = TRUE; } /* Eat gold */ else { gold = (p_ptr->au / 10) + randint1(25); if (gold < 2) gold = 2; if (gold > 5000) gold = (p_ptr->au / 20) + randint1(3000); if (gold > p_ptr->au) gold = p_ptr->au; p_ptr->au -= gold; if (gold <= 0) { msgf("Nothing was stolen."); } else if (p_ptr->au) { msgf("Your purse feels lighter."); msgf("%ld coins were stolen!", (long)gold); chg_virtue(V_SACRIFICE, 1); } else { msgf("Your purse feels lighter."); msgf("All of your coins were stolen!"); chg_virtue(V_SACRIFICE, 2); } /* Redraw gold */ p_ptr->redraw |= (PR_GOLD); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Blink away */ blinked = TRUE; } break; } case RBE_EAT_ITEM: { /* Take some damage */ take_hit(damage, ddesc); /* Confused monsters cannot steal successfully. -LM- */ if (m_ptr->confused) break; /* Saving throw (unless paralyzed) based on dex and level */ if (!p_ptr->tim.paralyzed && (randint0(100) < (adj_dex_safe[p_ptr->stat[A_DEX].ind] + p_ptr->lev))) { /* Saving throw message */ msgf("You grab hold of your backpack!"); /* Occasional "blink" anyway */ blinked = TRUE; /* Obvious */ obvious = TRUE; /* Done */ break; } /* Find an item */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Only some of the time */ if (!one_in_(INVEN_PACK)) continue; /* Skip artifacts */ if (FLAG(o_ptr, TR_INSTA_ART)) continue; /* Message */ msgf("%sour %v was stolen!", ((o_ptr->number > 1) ? "One of y" : "Y"), OBJECT_FMT(o_ptr, FALSE, 3)); chg_virtue(V_SACRIFICE, 1); /* Split object */ o_ptr = item_split(o_ptr, 1); /* Forget mark */ o_ptr->info &= ~(OB_SEEN); /* Give to the monster */ o_ptr = add_object_list(&m_ptr->hold_o_idx, o_ptr); /* Obvious */ obvious = TRUE; /* Blink away */ blinked = TRUE; /* Done */ break; } OBJ_ITT_END; break; } case RBE_EAT_FOOD: { /* Take some damage */ take_hit(damage, ddesc); /* Steal some food */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Pick an item from the pack */ if (!one_in_(INVEN_PACK)) continue; /* Skip non-food objects */ if (o_ptr->tval != TV_FOOD) continue; /* Message */ msgf("%sour %v was eaten!", ((o_ptr->number > 1) ? "One of y" : "Y"), OBJECT_FMT(o_ptr, FALSE, 0)); /* Steal the items */ item_increase(o_ptr, -1); /* Obvious */ obvious = TRUE; /* Done */ break; } OBJ_ITT_END; break; } case RBE_EAT_LITE: { /* Take some damage */ take_hit(damage, ddesc); /* Access the lite */ o_ptr = &p_ptr->equipment[EQUIP_LITE]; /* Drain fuel */ if ((o_ptr->pval > 0) && (!(FLAG(o_ptr, TR_INSTA_ART)))) { /* Reduce fuel */ o_ptr->pval -= (s16b)rand_range(250, 500); if (o_ptr->pval < 1) o_ptr->pval = 1; /* Notice */ if (!p_ptr->tim.blind) { msgf("Your light dims."); obvious = TRUE; } /* Notice changes */ notice_equip(); } break; } case RBE_ACID: { /* Message */ msgf("You are covered in acid!"); /* Special damage */ obvious = acid_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_ACID); break; } case RBE_ELEC: { /* Message */ msgf("You are struck by electricity!"); /* Special damage */ obvious = elec_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_ELEC); break; } case RBE_FIRE: { /* Message */ msgf("You are enveloped in flames!"); /* Special damage */ obvious = fire_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_FIRE); break; } case RBE_COLD: { /* Message */ msgf("You are covered with frost!"); /* Special damage */ obvious = cold_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_COLD); break; } case RBE_BLIND: { /* Take damage */ take_hit(damage, ddesc); /* Increase "blind" */ if (!(FLAG(p_ptr, TR_RES_BLIND))) { if (inc_blind(10 + randint1(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_BLIND); break; } case RBE_CONFUSE: { /* Take damage */ take_hit(damage, ddesc); /* Increase "confused" */ if (!(FLAG(p_ptr, TR_RES_CONF))) { if (inc_confused(3 + randint1(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_CONF); break; } case RBE_TERRIFY: { /* Saving throw difficulty */ int power = MAX(r_ptr->hdice * 2, damage); /* Take damage */ take_hit(damage, ddesc); /* Increase "afraid" */ if (FLAG(p_ptr, TR_RES_FEAR)) { msgf("You stand your ground!"); obvious = TRUE; } else if (player_save(power)) { msgf("You stand your ground!"); obvious = TRUE; } else { if (inc_afraid(3 + randint1(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_FEAR); break; } case RBE_PARALYZE: { /* Saving throw difficulty */ int power = MAX(r_ptr->hdice * 2, damage); /* Hack -- Prevent perma-paralysis via damage */ if (p_ptr->tim.paralyzed && (damage < 1)) damage = 1; /* Take damage */ take_hit(damage, ddesc); /* Increase "paralyzed" */ if (FLAG(p_ptr, TR_FREE_ACT)) { msgf("You are unaffected!"); obvious = TRUE; } else if (player_save(power)) { msgf("You resist the effects!"); obvious = TRUE; } else { if (inc_paralyzed(3 + randint1(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_FREE); break; } case RBE_LOSE_STR: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_STR)) obvious = TRUE; break; } case RBE_LOSE_INT: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_INT)) obvious = TRUE; break; } case RBE_LOSE_WIS: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_WIS)) obvious = TRUE; break; } case RBE_LOSE_DEX: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_DEX)) obvious = TRUE; break; } case RBE_LOSE_CON: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_CON)) obvious = TRUE; break; } case RBE_LOSE_CHR: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_CHR)) obvious = TRUE; break; } case RBE_LOSE_ALL: { /* Damage (physical) */ take_hit(damage, ddesc); /* Damage (stats) */ if (do_dec_stat(A_STR)) obvious = TRUE; if (do_dec_stat(A_DEX)) obvious = TRUE; if (do_dec_stat(A_CON)) obvious = TRUE; if (do_dec_stat(A_INT)) obvious = TRUE; if (do_dec_stat(A_WIS)) obvious = TRUE; if (do_dec_stat(A_CHR)) obvious = TRUE; break; } case RBE_SHATTER: { /* Obvious */ obvious = TRUE; /* Hack Reduce damage based on the player armor class */ damage -= (damage * ((ac < 150) ? ac : 150) / 250); /* Take damage */ take_hit(damage, ddesc); /* Radius 8 earthquake centered at the monster */ if (damage > 23) { (void)earthquake(m_ptr->fx, m_ptr->fy, 8); } break; } case RBE_EXP_10: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 95)) { msgf("You keep hold of your life force!"); } else { s32b d = damroll(10, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if ((FLAG(p_ptr, TR_HOLD_LIFE))) { msgf("You feel your life slipping away!"); lose_exp(d / 10); } else { msgf("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_EXP_20: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 90)) { msgf("You keep hold of your life force!"); } else { s32b d = damroll(20, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if (FLAG(p_ptr, TR_HOLD_LIFE)) { msgf("You feel your life slipping away!"); lose_exp(d / 10); } else { msgf("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_EXP_40: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 75)) { msgf("You keep hold of your life force!"); } else { s32b d = damroll(40, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if (FLAG(p_ptr, TR_HOLD_LIFE)) { msgf("You feel your life slipping away!"); lose_exp(d / 10); } else { msgf("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_EXP_80: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 50)) { msgf("You keep hold of your life force!"); } else { s32b d = damroll(80, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if ((FLAG(p_ptr, TR_HOLD_LIFE))) { msgf("You feel your life slipping away!"); lose_exp(d / 10); } else { msgf("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_DISEASE: { /* Take some damage */ take_hit(damage, ddesc); /* Take "poison" effect */ obvious = pois_dam(10, "disease", randint1(rlev) + 5); /* Damage CON (10% chance) */ if (randint0(100) < 10) { /* 1% chance for perm. damage */ bool perm = (one_in_(10)); if (dec_stat(A_CON, randint1(10), perm)) obvious = TRUE; } break; } case RBE_TIME: { switch (randint1(10)) { case 1: case 2: case 3: case 4: case 5: { msgf("You feel life has clocked back."); lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); break; } case 6: case 7: case 8: case 9: { int stat = randint0(6); switch (stat) { case A_STR: { act = "strong"; break; } case A_INT: { act = "bright"; break; } case A_WIS: { act = "wise"; break; } case A_DEX: { act = "agile"; break; } case A_CON: { act = "hale"; break; } case A_CHR: { act = "beautiful"; break; } } msgf ("You're not as %s as you used to be...", act); /* Note: this is a change from old behavior -RML */ p_ptr->stat[stat].cur = (p_ptr->stat[stat].cur * 3) / 4; if (p_ptr->stat[stat].cur < 30) p_ptr->stat[stat].cur = 30; p_ptr->update |= (PU_BONUS); break; } case 10: { msgf ("You're not as powerful as you used to be..."); for (k = 0; k < A_MAX; k++) { p_ptr->stat[k].cur = (p_ptr->stat[k].cur * 3) / 4; if (p_ptr->stat[k].cur < 30) p_ptr->stat[k].cur = 30; } p_ptr->update |= (PU_BONUS); break; } } take_hit(damage, ddesc); break; } case RBE_EXP_VAMP: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 50)) { msgf("You keep hold of your life force!"); resist_drain = TRUE; } else { s32b d = damroll(60, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if (FLAG(p_ptr, TR_HOLD_LIFE)) { msgf("You feel your life slipping away!"); lose_exp(d / 10); } else { msgf("You feel your life draining away!"); lose_exp(d); } } /* Heal the attacker? */ if (!(p_ptr->rp.prace == RACE_ZOMBIE || p_ptr->rp.prace == RACE_VAMPIRE || p_ptr->rp.prace == RACE_SPECTRE || p_ptr->rp.prace == RACE_SKELETON || p_ptr->rp.prace == RACE_GOLEM || p_ptr->rp.prace == RACE_GHOUL) && (damage > 2) && !(resist_drain)) { bool did_heal = FALSE; if (m_ptr->hp < m_ptr->maxhp) did_heal = TRUE; /* Heal */ m_ptr->hp += damroll(4, damage / 6); if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Special message */ if ((visible) && (did_heal)) { msgf("%^s appears healthier.", m_name); } } } } } /* Hack -- only one of cut or stun */ if (do_cut && do_stun) { /* Cancel cut */ if (randint0(100) < 50) { do_cut = FALSE; } /* Cancel stun */ else { do_stun = FALSE; } } /* Handle cut */ if (do_cut) { /* Critical hit (zero if non-critical) */ tmp = monster_critical(d_dice, d_side, damage); /* Roll for damage */ switch (tmp) { case 0: { k = 0; break; } case 1: { k = randint1(5); break; } case 2: { k = rand_range(5, 10); break; } case 3: { k = rand_range(20, 40); break; } case 4: { k = rand_range(50, 100); break; } case 5: { k = rand_range(100, 200); break; } case 6: { k = 300; break; } default: { k = 500; break; } } /* Apply the cut */ if (k) (void)inc_cut(k); } /* Handle stun */ if (do_stun) { /* Critical hit (zero if non-critical) */ tmp = monster_critical(d_dice, d_side, damage); /* Roll for damage */ switch (tmp) { case 0: { k = 0; break; } case 1: { k = randint1(5); break; } case 2: { k = rand_range(10, 20); break; } case 3: { k = rand_range(20, 40); break; } case 4: { k = rand_range(30, 60); break; } case 5: { k = rand_range(40, 80); break; } case 6: { k = 100; break; } default: { k = 200; break; } } /* Apply the stun */ if (k) (void)inc_stun(k); } if (explode) { sound(SOUND_EXPLODE); if (mon_take_hit(m_idx, m_ptr->hp + 1, &fear, NULL)) { blinked = FALSE; alive = FALSE; } } if (touched) { if ((FLAG(p_ptr, TR_SH_FIRE)) && alive) { if (!FLAG(r_ptr, RF_IM_FIRE)) { int dam = damroll(2, 6); /* Modify the damage */ dam = mon_damage_mod(m_ptr, dam, 0); msgf("%^s is suddenly very hot!", m_name); if (mon_take_hit(m_idx, dam, &fear, " turns into a pile of ash.")) { blinked = FALSE; alive = FALSE; } } else { if (visible) r_ptr->r_flags[2] |= RF2_IM_FIRE; } } if ((FLAG(p_ptr, TR_SH_ELEC)) && alive) { if (!FLAG(r_ptr, RF_IM_ELEC)) { int dam = damroll(2, 6); /* Modify the damage */ dam = mon_damage_mod(m_ptr, dam, 0); msgf("%^s gets zapped!", m_name); if (mon_take_hit(m_idx, dam, &fear, " turns into a pile of cinder.")) { blinked = FALSE; alive = FALSE; } } else { if (visible) r_ptr->r_flags[2] |= RF2_IM_ELEC; } } if ((FLAG(p_ptr, TR_SH_ACID)) && alive) { if (!FLAG(r_ptr, RF_IM_ACID)) { int dam = damroll(2, 6); /* Modify the damage */ dam = mon_damage_mod(m_ptr, dam, 0); msgf("%^s gets melted!", m_name); if (mon_take_hit(m_idx, dam, &fear, " turns into a puddle of goo.")) { blinked = FALSE; alive = FALSE; } } else { if (visible) r_ptr->r_flags[2] |= RF2_IM_ACID; } } if ((FLAG(p_ptr, TR_SH_COLD)) && alive) { if (!FLAG(r_ptr, RF_IM_COLD)) { int dam = damroll(2, 6); /* Modify the damage */ dam = mon_damage_mod(m_ptr, dam, 0); msgf("%^s gets frozen!", m_name); if (mon_take_hit(m_idx, dam, &fear, " turns into an icicle.")) { blinked = FALSE; alive = FALSE; } } else { if (visible) r_ptr->r_flags[2] |= RF2_IM_COLD; } } touched = FALSE; } } /* Monster missed player */ else { /* Analyze failed attacks */ switch (method) { case RBM_HIT: case RBM_TOUCH: case RBM_PUNCH: case RBM_KICK: case RBM_CLAW: case RBM_BITE: case RBM_STING: case RBM_XXX1: case RBM_BUTT: case RBM_CRUSH: case RBM_ENGULF: case RBM_CHARGE: { /* Visible monsters */ if (visible) { /* Disturbing */ disturb(TRUE); /* Message */ msgf("%^s misses you.", m_name); } break; } } } /* Analyze "visible" monsters only */ if (alive && visible) { /* Count "obvious" attacks (and ones that cause damage) */ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) { /* Count attacks of this type */ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) { r_ptr->r_blows[ap_cnt]++; } /* Look to see if we've spotted a mimic */ if (m_ptr->smart & SM_MIMIC) { /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now */ update_mon_vis(m_ptr->r_idx, 1); } } } } /* Blink away */ if (blinked && alive) { msgf("The thief flees laughing!"); (void)teleport_away(m_idx, MAX_SIGHT * 2 + 5); } /* Always notice cause of death */ if (p_ptr->state.is_dead && (r_ptr->r_deaths < MAX_SHORT)) { r_ptr->r_deaths++; } if (alive && visible && fear) { flee_message(m_name, m_ptr->r_idx); } /* Assume we attacked */ return (TRUE); } zangband/src/melee2.c0000755000000000000000000022006310250356274013456 0ustar rootroot/* File: melee2.c */ /* Purpose: Monster spells and movement */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* * This file has several additions to it by Keldon Jones (keldon@umr.edu) * to improve the general quality of the AI (version 0.1.1). */ #include "angband.h" #define SPEAK_CHANCE 8 #define GRINDNOISE 20 #define CYBERNOISE 20 /* * Convert the target a monster has * to a direction to move in. * * This uses the old direction encoding still. */ static void convert_target_dir(monster_type *m_ptr, int *mm) { int x = m_ptr->tx; int y = m_ptr->ty; /* Paranoia */ if (!x && !y) return; /* Extract the direction */ x -= m_ptr->fx; y -= m_ptr->fy; /* North */ if ((y < 0) && (x == 0)) { mm[0] = 8; mm[1] = 7; mm[2] = 9; } /* South */ else if ((y > 0) && (x == 0)) { mm[0] = 2; mm[1] = 1; mm[2] = 3; } /* East */ else if ((x > 0) && (y == 0)) { mm[0] = 6; mm[1] = 9; mm[2] = 3; } /* West */ else if ((x < 0) && (y == 0)) { mm[0] = 4; mm[1] = 7; mm[2] = 1; } /* North-West */ else if ((y < 0) && (x < 0)) { mm[0] = 7; mm[1] = 4; mm[2] = 8; } /* North-East */ else if ((y < 0) && (x > 0)) { mm[0] = 9; mm[1] = 6; mm[2] = 8; } /* South-West */ else if ((y > 0) && (x < 0)) { mm[0] = 1; mm[1] = 4; mm[2] = 2; } /* South-East */ else if ((y > 0) && (x > 0)) { mm[0] = 3; mm[1] = 6; mm[2] = 2; } } /* * Is the monster worth targetting? */ static bool nice_target(monster_type *m_ptr, monster_race *r_ptr, monster_type *t_ptr) { /* The monster itself isn't a target */ if (t_ptr == m_ptr) return (FALSE); /* Paranoia -- Skip dead monsters */ if (!t_ptr->r_idx) return (FALSE); /* Monster must be 'an enemy' */ if (!are_enemies(m_ptr, t_ptr)) return (FALSE); /* Mega Hack - Monster must be close */ if (ABS(m_ptr->fy - t_ptr->fy) + ABS(m_ptr->fx - t_ptr->fx) > 20) { return (FALSE); } if (is_pet(m_ptr)) { /* Hack -- only fight away from player */ if (p_ptr->pet_follow_distance < 0) { /* No fighting near player */ if (t_ptr->cdis <= (0 - p_ptr->pet_follow_distance)) { return (FALSE); } } /* Hack -- no fighting away from player */ else if ((m_ptr->cdis < t_ptr->cdis) && (t_ptr->cdis > p_ptr->pet_follow_distance)) { return (FALSE); } } /* Monster must be projectable if we can't pass through walls */ if (!(FLAG(r_ptr, RF_PASS_WALL) || FLAG(r_ptr, RF_KILL_WALL)) && !projectable(m_ptr->fx, m_ptr->fy, t_ptr->fx, t_ptr->fy)) { return (FALSE); } /* We have a target */ return (TRUE); } /* * Find an enemy to target */ static bool get_enemy_target(monster_type *m_ptr) { int i; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_type *t_ptr; cave_type *c_ptr; /* Do we already have a nice enemy? */ if (m_ptr->tx || m_ptr->ty) { /* paranoia */ if (in_bounds2(m_ptr->tx, m_ptr->ty)) { c_ptr = area(m_ptr->tx, m_ptr->ty); /* Is there a monster on our target? */ if (c_ptr->m_idx) { t_ptr = &m_list[c_ptr->m_idx]; /* Is it a a good monster to target? */ if (nice_target(m_ptr, r_ptr, t_ptr)) return (TRUE); } } /* Forget target */ m_ptr->tx = 0; m_ptr->ty = 0; } /* * Scan through all monsters * * Scanning backwards seems to give the best results - * We tend to find the "newer" monsters first. * "Newer" monsters tend to be closer. */ for (i = m_max; i > 0; i--) { t_ptr = &m_list[i]; if (nice_target(m_ptr, r_ptr, t_ptr)) { /* OK -- we've got a target */ m_ptr->ty = t_ptr->fy; m_ptr->tx = t_ptr->fx; /* Pick the first such monster... */ return (TRUE); } } /* No monster found */ return (FALSE); } #define HURT_TERRAIN_CHANCE 10 /* * Can the monster enter this grid? How easy is it for them to do so? * * The code that uses this function sometimes assumes that it will never * return a value greater than 100. * * The usage of exp to determine whether one monster can kill another is * a kludge. Maybe use HPs, plus a big bonus for acidic monsters * against monsters that don't like acid. * * The usage of exp to determine whether one monster can push past * another is also a tad iffy, but ensures that black orcs can always * push past other black orcs. */ static int cave_passable_mon(monster_type *m_ptr, cave_type *c_ptr) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Assume nothing in the grid other than the terrain hinders movement */ int move_chance = 100; int feat = c_ptr->feat; /* The grid is occupied by the player. */ if (character_dungeon && (area(p_ptr->px, p_ptr->py) == c_ptr)) { /* Monster has no melee blows - character's grid is off-limits. */ if (FLAG(r_ptr, RF_NEVER_BLOW)) return (0); } /* The grid is occupied by a monster. */ else if (c_ptr->m_idx) { monster_type *n_ptr = &m_list[c_ptr->m_idx]; monster_race *nr_ptr = &r_info[n_ptr->r_idx]; /* Kill weaker monsters + attack 'enemies' */ if ((FLAG(r_ptr, RF_KILL_BODY) && (r_ptr->mexp > nr_ptr->mexp)) || are_enemies(m_ptr, n_ptr) || m_ptr->confused) { /* This is a null statement... */ move_chance = 100; } /* Push past weaker or similar monsters */ else if (FLAG(r_ptr, RF_MOVE_BODY) && (r_ptr->mexp >= nr_ptr->mexp)) { /* It's easier to push past weaker monsters */ if (r_ptr->mexp == nr_ptr->mexp) { move_chance = 50; } else { move_chance = 80; } } else { /* Cannot do anything to clear away the other monster */ return (0); } } /* Insert field logic here */ /*** Check passability of various features. ***/ /* Feature is not a wall */ if (cave_floor_grid(c_ptr)) { /* Ocean */ if (feat == FEAT_OCEAN_WATER) { if (!(FLAG(r_ptr, RF_WILD_OCEAN))) return (HURT_TERRAIN_CHANCE); } /* Deep water */ else if (feat == FEAT_DEEP_WATER) { if (!((FLAG(r_ptr, RF_AQUATIC)) || (FLAG(r_ptr, RF_CAN_FLY)) || (FLAG(r_ptr, RF_CAN_SWIM)))) { return (HURT_TERRAIN_CHANCE); } } /* Shallow water */ else if (feat == FEAT_SHAL_WATER) { if (FLAG(r_ptr, RF_AURA_FIRE)) return (0); } /* Aquatic creatues need water */ if ((FLAG(r_ptr, RF_AQUATIC)) && !(FLAG(r_ptr, RF_CAN_FLY))) { return (0); } /* Lava */ if ((feat == FEAT_SHAL_LAVA) || (feat == FEAT_DEEP_LAVA)) { if (!((FLAG(r_ptr, RF_IM_FIRE)) || (FLAG(r_ptr, RF_CAN_FLY)))) { return (HURT_TERRAIN_CHANCE); } } /* Acid */ else if ((feat == FEAT_SHAL_ACID) || (feat == FEAT_DEEP_ACID)) { if (!((FLAG(r_ptr, RF_IM_ACID)) || (FLAG(r_ptr, RF_CAN_FLY)))) { return (HURT_TERRAIN_CHANCE); } } /* Swamp */ else if ((feat == FEAT_SHAL_SWAMP) || (feat == FEAT_DEEP_SWAMP)) { if (!((FLAG(r_ptr, RF_IM_POIS)) || (FLAG(r_ptr, RF_CAN_FLY)))) { return (HURT_TERRAIN_CHANCE); } } /* Anything else that's not a wall we assume to be passable. */ return (move_chance); } /* Closed or secret doors can be opened */ if ((feat == FEAT_CLOSED) || (feat == FEAT_SECRET)) { if (((FLAG(r_ptr, RF_OPEN_DOOR)) || (FLAG(r_ptr, RF_BASH_DOOR))) && (!is_pet(m_ptr) || p_ptr->pet_open_doors)) { /* Opening doors takes time */ return (move_chance / 2); } } /* Permanent walls + the pattern block movement */ else if (cave_perma_grid(c_ptr)) { return (0); } /* Monster can burrow through walls */ if (FLAG(r_ptr, RF_PASS_WALL) || FLAG(r_ptr, RF_KILL_WALL)) { return (move_chance); } /* Otherwise cannot pass through */ return (0); } /* * Hack, based on mon_take_hit... perhaps all monster attacks on * other monsters should use this? */ void mon_take_hit_mon(int m_idx, int dam, bool *fear, cptr note) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[160]; /* Can the player be aware of this attack? */ bool known = (m_ptr->cdis <= MAX_SIGHT); /* Extract monster name */ monster_desc(m_name, m_ptr, 0, 160); /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Wake it up */ m_ptr->csleep = 0; if (m_ptr->invulner && !one_in_(PENETRATE_INVULNERABILITY)) { if (m_ptr->ml) { msgf("%^s is unharmed.", m_name); } return; } /* Hurt it */ m_ptr->hp -= dam; /* It is dead now... or is it? */ if (m_ptr->hp < 0) { if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR)) || (FLAG(r_ptr, RF_UNIQUE_7))) { m_ptr->hp = 1; } else { /* Make a sound */ if (!monster_living(r_ptr)) { sound(SOUND_N_KILL); } else { sound(SOUND_KILL); } if (known) { /* Unseen death by normal attack */ if (!m_ptr->ml) { p_ptr->state.mon_fight = TRUE; } /* Death by special attack */ else if (note) { msgf("%^s%s", m_name, note); } /* Death by normal attack -- nonliving monster */ else if (!monster_living(r_ptr)) { msgf("%^s is destroyed.", m_name); } /* Death by normal attack -- living monster */ else { msgf("%^s is killed.", m_name); } } /* Generate treasure */ (void)monster_death(m_idx, TRUE); /* Delete the monster */ delete_monster_idx(m_idx); /* Not afraid */ (*fear) = FALSE; /* Monster is dead */ return; } } /* Mega-Hack -- Pain cancels fear */ if (m_ptr->monfear && (dam > 0)) { int tmp = randint1(dam / 4); /* Cure a little fear */ if (tmp < m_ptr->monfear) { /* Reduce fear */ m_ptr->monfear -= tmp; } /* Cure all the fear */ else { /* Cure fear */ m_ptr->monfear = 0; /* No more fear */ (*fear) = FALSE; } } /* Sometimes a monster gets scared by damage */ if (!m_ptr->monfear && !(FLAG(r_ptr, RF_NO_FEAR))) { int percentage; /* Percentage of fully healthy */ percentage = (100L * m_ptr->hp) / m_ptr->maxhp; /* * Run (sometimes) if at 10% or less of max hit points, * or (usually) when hit for half its current hit points */ if (((percentage <= 10) && (randint0(10) < percentage)) || ((dam >= m_ptr->hp) && (randint0(100) < 80))) { /* Hack -- note fear */ (*fear) = TRUE; /* XXX XXX XXX Hack -- Add some timed fear */ m_ptr->monfear += (randint1(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5))); } } /* Not dead yet */ return; } /* * Returns whether a given monster will try to run from the player. * * Monsters will attempt to avoid very powerful players. See below. * * Because this function is called so often, little details are important * for efficiency. Like not using "mod" or "div" when possible. And * attempting to check the conditions in an optimal order. Note that * "(x << 2) == (x * 4)" if "x" has enough bits to hold the result. * * Note that this function is responsible for about one to five percent * of the processor use in normal conditions... */ static int mon_will_run(monster_type *m_ptr) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; u16b p_lev, m_lev; u16b p_chp, p_mhp; u16b m_chp, m_mhp; u32b p_val, m_val; /* Friends can be commanded to avoid the player */ if (is_pet(m_ptr)) { /* Are we trying to avoid the player? */ return ((p_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - p_ptr->pet_follow_distance))); } /* Keep monsters from running too far away */ if (m_ptr->cdis > MAX_SIGHT + 5) return (FALSE); /* All "afraid" monsters will run away */ if (m_ptr->monfear) return (TRUE); /* Nearby monsters will not become terrified */ if (m_ptr->cdis <= 5) return (FALSE); /* Examine player power (level) */ p_lev = p_ptr->lev; /* Examine monster power (level plus morale) */ m_lev = r_ptr->hdice * 2 + 25; /* Optimize extreme cases below */ if (m_lev > p_lev + 4) return (FALSE); if (m_lev + 4 <= p_lev) return (TRUE); /* Examine player health */ p_chp = p_ptr->chp; p_mhp = p_ptr->mhp; /* Examine monster health */ m_chp = m_ptr->hp; m_mhp = m_ptr->maxhp; /* Prepare to optimize the calculation */ p_val = (p_lev * p_mhp) + (p_chp << 2); /* div p_mhp */ m_val = (m_lev * m_mhp) + (m_chp << 2); /* div m_mhp */ /* Strong players scare strong monsters */ if (p_val * m_mhp > m_val * p_mhp) return (TRUE); /* Assume no terror */ return (FALSE); } /* * Can the monster catch a whiff of the character? * * Many more monsters can smell, but they find it hard to smell and * track down something at great range. * * XXX XXX Use SMELL_STRENGTH as a function of monster race... */ static bool monster_can_smell(monster_type *m_ptr) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; cave_type *c_ptr = area(m_ptr->fx, m_ptr->fy); /* No information? */ if (!c_ptr->when) return (FALSE); #if 0 /* Scent is too old */ if (age > SMELL_STRENGTH) return (FALSE); #endif /* 0 */ /* Canines and Zephyer Hounds are amazing trackers */ if (strchr("CZ", r_ptr->d_char)) return (TRUE); /* So are the Nazgul */ else if ((strchr("W", r_ptr->d_char)) && FLAG(r_ptr, RF_UNIQUE)) { /* Bloodscent! */ return (TRUE); } #if 0 /* Other monsters can sometimes make good use of scent */ else if (strchr("fkoqyHORTY", r_ptr->d_char)) { if (age <= SMELL_STRENGTH - 10) { /* Something's in the air... */ return (TRUE); } } #endif /* 0 */ /* You're imagining things. */ return (FALSE); } /* * Hack -- Precompute a bunch of calls to distance() in find_safety() and * find_hiding(). * * The pair of arrays dist_offsets_y[n] and dist_offsets_x[n] contain the * offsets of all the locations with a distance of n from a central point, * with an offset of (0,0) indicating no more offsets at this distance. * * This is, of course, fairly unreadable, but it eliminates multiple loops * from the previous version. * * It is probably better to replace these arrays with code to compute * the relevant arrays, even if the storage is pre-allocated in hard * coded sizes. At the very least, code should be included which is * able to generate and dump these arrays (ala "los()"). XXX XXX XXX * * Also, the storage needs could be halved by using bytes. XXX XXX XXX * * These arrays could be combined into two big arrays, using sub-arrays * to hold the offsets and lengths of each portion of the sub-arrays, and * this could perhaps also be used somehow in the "look" code. XXX XXX XXX */ static sint d_off_y_0[] = { 0 }; static sint d_off_x_0[] = { 0 }; static sint d_off_y_1[] = { -1, -1, -1, 0, 0, 1, 1, 1, 0 }; static sint d_off_x_1[] = { -1, 0, 1, -1, 1, -1, 0, 1, 0 }; static sint d_off_y_2[] = { -1, -1, -2, -2, -2, 0, 0, 1, 1, 2, 2, 2, 0 }; static sint d_off_x_2[] = { -2, 2, -1, 0, 1, -2, 2, -2, 2, -1, 0, 1, 0 }; static sint d_off_y_3[] = { -1, -1, -2, -2, -3, -3, -3, 0, 0, 1, 1, 2, 2, 3, 3, 3, 0 }; static sint d_off_x_3[] = { -3, 3, -2, 2, -1, 0, 1, -3, 3, -3, 3, -2, 2, -1, 0, 1, 0 }; static sint d_off_y_4[] = { -1, -1, -2, -2, -3, -3, -3, -3, -4, -4, -4, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 0 }; static sint d_off_x_4[] = { -4, 4, -3, 3, -2, -3, 2, 3, -1, 0, 1, -4, 4, -4, 4, -3, 3, -2, -3, 2, 3, -1, 0, 1, 0 }; static sint d_off_y_5[] = { -1, -1, -2, -2, -3, -3, -4, -4, -4, -4, -5, -5, -5, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 0 }; static sint d_off_x_5[] = { -5, 5, -4, 4, -4, 4, -2, -3, 2, 3, -1, 0, 1, -5, 5, -5, 5, -4, 4, -4, 4, -2, -3, 2, 3, -1, 0, 1, 0 }; static sint d_off_y_6[] = { -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -5, -5, -6, -6, -6, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 0 }; static sint d_off_x_6[] = { -6, 6, -5, 5, -5, 5, -4, 4, -2, -3, 2, 3, -1, 0, 1, -6, 6, -6, 6, -5, 5, -5, 5, -4, 4, -2, -3, 2, 3, -1, 0, 1, 0 }; static sint d_off_y_7[] = { -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -5, -5, -6, -6, -6, -6, -7, -7, -7, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 0 }; static sint d_off_x_7[] = { -7, 7, -6, 6, -6, 6, -5, 5, -4, -5, 4, 5, -2, -3, 2, 3, -1, 0, 1, -7, 7, -7, 7, -6, 6, -6, 6, -5, 5, -4, -5, 4, 5, -2, -3, 2, 3, -1, 0, 1, 0 }; static sint d_off_y_8[] = { -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -6, -6, -7, -7, -7, -7, -8, -8, -8, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 0 }; static sint d_off_x_8[] = { -8, 8, -7, 7, -7, 7, -6, 6, -6, 6, -4, -5, 4, 5, -2, -3, 2, 3, -1, 0, 1, -8, 8, -8, 8, -7, 7, -7, 7, -6, 6, -6, 6, -4, -5, 4, 5, -2, -3, 2, 3, -1, 0, 1, 0 }; static sint d_off_y_9[] = { -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -7, -7, -8, -8, -8, -8, -9, -9, -9, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 0 }; static sint d_off_x_9[] = { -9, 9, -8, 8, -8, 8, -7, 7, -7, 7, -6, 6, -4, -5, 4, 5, -2, -3, 2, 3, -1, 0, 1, -9, 9, -9, 9, -8, 8, -8, 8, -7, 7, -7, 7, -6, 6, -4, -5, 4, 5, -2, -3, 2, 3, -1, 0, 1, 0 }; static sint *dist_offsets_y[10] = { d_off_y_0, d_off_y_1, d_off_y_2, d_off_y_3, d_off_y_4, d_off_y_5, d_off_y_6, d_off_y_7, d_off_y_8, d_off_y_9 }; static sint *dist_offsets_x[10] = { d_off_x_0, d_off_x_1, d_off_x_2, d_off_x_3, d_off_x_4, d_off_x_5, d_off_x_6, d_off_x_7, d_off_x_8, d_off_x_9 }; /* * Choose a "safe" location near a monster for it to run toward. * * A location is "safe" if it can be reached quickly and the player * is not able to fire into it (it isn't a "clean shot"). So, this will * cause monsters to "duck" behind walls. Hopefully, monsters will also * try to run towards corridor openings if they are in a room. * * This function may take lots of CPU time if lots of monsters are * fleeing. */ static void find_safety(monster_type *m_ptr, int *xp, int *yp) { int py = p_ptr->py; int px = p_ptr->px; int fy = m_ptr->fy; int fx = m_ptr->fx; int y, x, d, dis, i; int gdis = 0; sint *y_offsets; sint *x_offsets; cave_type *c_ptr; /* Start with adjacent locations, spread further */ for (d = 1; d < 10; d++) { /* Get the lists of points with a distance d from (fx, fy) */ y_offsets = dist_offsets_y[d]; x_offsets = dist_offsets_x[d]; /* Check the locations */ for (i = 0; x_offsets[i] != 0 || y_offsets[i] != 0; i++) { y = fy + y_offsets[i]; x = fx + x_offsets[i]; /* Skip illegal locations */ if (!in_boundsp(x, y)) continue; c_ptr = area(x, y); /* Skip locations in a wall */ if (cave_wall_grid(c_ptr)) continue; /* Ignore grids very far from the player */ if (c_ptr->when < area(px, py)->when) continue; /* Ignore too-distant grids */ if (c_ptr->cost > area(fx, fy)->cost + 2 * d) continue; /* Check for absence of shot (more or less) */ if (clean_shot(fx, fy, x, y, FALSE)) { /* Calculate distance from player */ dis = distance(x, y, px, py); /* Remember if further than previous */ if (dis > gdis) { *yp = y; *xp = x; if (in_boundsp(x, y) && player_has_los_grid(parea(x, y))) { gdis = dis; } else { gdis = dis * 5; } } } } /* Bail out when have a safe square */ if (gdis > d * 2) return; } } /* * Choose a good hiding place near a monster for it to run toward. * * Pack monsters will use this to "ambush" the player and lure him out * of corridors into open space so they can swarm him. * * Return TRUE if a good location is available. */ static bool find_hiding(monster_type *m_ptr, int *xp, int *yp) { int fy = m_ptr->fy; int fx = m_ptr->fx; int py = p_ptr->py; int px = p_ptr->px; int y, x, d, dis, i; int gdis = 999; sint *y_offsets, *x_offsets; cave_type *c_ptr; /* Start with adjacent locations, spread further */ for (d = 1; d < 10; d++) { /* Get the lists of points with a distance d from (fx, fy) */ y_offsets = dist_offsets_y[d]; x_offsets = dist_offsets_x[d]; /* Check the locations */ for (i = 0; x_offsets[i] != 0 || y_offsets[i] != 0; i++) { y = fy + y_offsets[i]; x = fx + x_offsets[i]; /* Skip illegal locations */ if (!in_boundsp(x, y)) continue; c_ptr = area(x, y); /* Skip occupied locations */ if (!cave_empty_grid(c_ptr)) continue; /* Not on player */ if ((y == py) && (x == px)) continue; /* Check for hidden, available grid */ if (!player_has_los_grid(parea(x, y)) && clean_shot(fx, fy, x, y, FALSE)) { /* Calculate distance from player */ dis = distance(x, y, px, py); /* Remember if closer than previous */ if (dis < gdis && dis >= 2) { *yp = y; *xp = x; gdis = dis; } } } /* Check for success */ if (gdis < 999) { /* Found good place */ return (TRUE); } } /* No good place */ return (FALSE); } /* * Helper function for monsters that want to advance toward the character. * Assumes that the monster isn't frightened, and is not in LOS of the * character. * * Ghosts and rock-eaters do not use flow information, because they * can - in general - move directly towards the character. We could make * them look for a grid at their preferred range, but the character * would then be able to avoid them better (it might also be a little * hard on those poor warriors...). * * Other monsters will use target information, then their ears, then their * noses (if they can), and advance blindly if nothing else works. * * When flowing, monsters prefer non-diagonal directions. * * XXX - At present, this function does not handle difficult terrain * intelligently. Monsters using flow may bang right into a door that * they can't handle. Fixing this may require code to set monster * paths. */ static void get_move_advance(monster_type *m_ptr, int *tx, int *ty) { int px = p_ptr->px; int py = p_ptr->py; int i, x, y; int mx = m_ptr->fx; int my = m_ptr->fy; int best_val = 0; cave_type *c_ptr; bool use_sound = FALSE; bool use_scent = FALSE; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Hack - Monster can go through rocks - head straight for character */ if (FLAG(r_ptr, RF_PASS_WALL) || FLAG(r_ptr, RF_KILL_WALL)) { *tx = px; *ty = py; return; } /* Can the player see us? - if so run towards him */ if (in_boundsp(mx, my) && player_has_los_grid(parea(mx, my))) { /* Spellcasters may try to keep distance between them and the player */ if (m_ptr->cdis < MAX_RANGE / 2 && m_ptr->cdis > 2 && m_ptr->hp < p_ptr->lev * 3 && ((r_ptr->flags[3] & RF3_ATTACK_MASK) || (r_ptr->flags[4] & RF4_ATTACK_MASK) || (r_ptr->flags[5] & RF5_ATTACK_MASK))) { /* Move directly away from character. */ *tx = mx + -(px - mx); *ty = my + -(py - my); return; } *tx = px; *ty = py; return; } #if 0 /* Use target information if available */ if ((m_ptr->ty) || (m_ptr->tx)) { *tx = m_ptr->tx; *ty = m_ptr->ty; return; } #endif /* 0 */ /* Monster location */ c_ptr = area(mx, my); /* If we can hear noises, advance towards them */ if (c_ptr->cost) { use_sound = TRUE; } /* Otherwise, try to follow a scent trail */ else if (monster_can_smell(m_ptr)) { use_scent = TRUE; } /* Otherwise, advance blindly */ if ((!use_sound) && (!use_scent)) { *ty = py; *tx = px; return; } /* Paranoia - nothing to do? */ *tx = mx; *ty = my; /* Using flow information. Check nearby grids, diagonals first. */ for (i = 7; i >= 0; i--) { /* Get the location */ x = mx + ddx_ddd[i]; y = my + ddy_ddd[i]; /* Check Bounds */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* We're following a scent trail */ if (use_scent) { byte when = c_ptr->when; /* Accept younger scent */ if (best_val < when) continue; best_val = when; } /* We're using sound */ else { byte cost = c_ptr->cost; /* Accept louder sounds */ if (cost < best_val) continue; best_val = cost; } /* Save the location */ *ty = y; *tx = x; } } #define FLEE_RANGE 15 /* * Helper function for monsters that want to retreat from the character. * Used for any monster that is terrified, frightened, is looking for a * temporary hiding spot, or just wants to open up some space between it * and the character. * * If the monster is well away from danger, let it relax. * If the monster's current target is not in LOS, use it (+). * If the monster is not in LOS, and cannot pass through walls, try to * use flow (noise) information. * If the monster is in LOS, even if it can pass through walls, * search for a hiding place (helper function "find_safety()"): * search for a hiding place (helper function "find_safety()"). * If no hiding place is found, and there seems no way out, go down * fighting. * * If none of the above solves the problem, run away blindly. * * (+) There is one exception to the automatic usage of a target. If the * target is only out of LOS because of "knight's move" rules (distance * along one axis is 2, and along the other, 1), then the monster will try * to find another adjacent grid that is out of sight. What all this boils * down to is that monsters can now run around corners properly! * * Return TRUE if the monster did actually want to do anything. */ static bool get_move_retreat(monster_type *m_ptr, int *tx, int *ty) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; cave_type *c_ptr; int i; int x, y; /* If the monster is well away from danger, let it relax. */ if (m_ptr->cdis >= FLEE_RANGE) { /* Don't go anywhere */ *tx = m_ptr->fx; *ty = m_ptr->fy; return (FALSE); } /* Monster has a target */ if ((m_ptr->tx) && (m_ptr->ty) && in_boundsp(m_ptr->tx, m_ptr->ty)) { if (player_has_los_grid(parea(m_ptr->tx, m_ptr->ty))) { /* It's in LOS; cancel it. */ m_ptr->tx = 0; m_ptr->ty = 0; } else { /* * It's out of LOS; * keep using it, except in "knight's move" cases */ /* Get axis distance from character to current target */ int dist_x = ABS(p_ptr->px - m_ptr->tx); int dist_y = ABS(p_ptr->py - m_ptr->ty); /* It's only out of LOS because of "knight's move" rules */ if (((dist_y == 2) && (dist_x == 1)) || ((dist_y == 1) && (dist_x == 2))) { /* * If there is another grid adjacent to the monster that * the character cannot see into, and it isn't any harder * to enter, use it instead. Prefer diagonals. */ for (i = 7; i >= 0; i--) { x = m_ptr->fx + ddx_ddd[i]; y = m_ptr->fy + ddy_ddd[i]; /* Check Bounds */ if (!in_bounds2(x, y)) continue; if (in_boundsp(x, y) && player_has_los_grid(parea(x, y))) continue; if ((x == m_ptr->tx) && (y == m_ptr->ty)) continue; if (cave_passable_mon(m_ptr, area(m_ptr->tx, m_ptr->ty)) > cave_passable_mon(m_ptr, area(x, y))) continue; m_ptr->ty = x; m_ptr->tx = y; break; } } /* Move towards the target */ *tx = m_ptr->tx; *ty = m_ptr->ty; return (TRUE); } } c_ptr = area(m_ptr->fx, m_ptr->fy); /* The monster is not in LOS, but thinks it's still too close. */ if (in_boundsp(m_ptr->fx, m_ptr->fy) && !player_has_los_grid(parea(m_ptr->fx, m_ptr->fy))) { /* Monster cannot pass through walls */ if (!(FLAG(r_ptr, RF_PASS_WALL) || FLAG(r_ptr, RF_KILL_WALL))) { /* Run away from noise */ if (c_ptr->cost) { int start_cost = c_ptr->cost; /* Look at adjacent grids, diagonals first */ for (i = 7; i >= 0; i--) { x = m_ptr->fx + ddx_ddd[i]; y = m_ptr->fy + ddy_ddd[i]; /* Check Bounds */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* Accept the first non-visible grid with a lower cost */ if (c_ptr->cost < start_cost) { if (!(in_boundsp(x, y) && player_has_los_grid(parea(x, y)))) { *tx = x; *ty = y; /* Success */ return (TRUE); } } } } } /* No flow info, or don't need it -- see bottom of function */ } /* The monster is in line of sight. */ else { int prev_cost = c_ptr->cost; int start = randint0(8); /* Look for adjacent hiding places */ for (i = start; i < 8 + start; i++) { x = m_ptr->fx + ddx_ddd[i % 8]; y = m_ptr->fy + ddy_ddd[i % 8]; /* Check Bounds */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* No grids in LOS */ if (in_boundsp(x, y) && player_has_los_grid(parea(x, y))) continue; /* Grid must be pretty easy to enter */ if (cave_passable_mon(m_ptr, c_ptr) < 50) continue; /* Accept any grid that doesn't have a higher flow (noise) cost. */ if (c_ptr->cost <= prev_cost) { *tx = x; *ty = y; /* Success */ return (TRUE); } } /* Find a nearby grid not in LOS of the character. */ find_safety(m_ptr, tx, ty); #if 0 /* * No safe place found. If monster is in LOS and close, * it will turn to fight. */ if ((player_has_los_bold(m_ptr->fx, m_ptr->fy)) && (m_ptr->cdis < TURN_RANGE)) { /* Turn and fight */ m_ptr->monfear = 0; /* Recalculate combat range (later) */ m_ptr->min_range = 0; /* Visible */ if (m_ptr->ml) { /* Dump a message */ msgf("%^v turns to fight!", MONSTER_FMT(m_ptr, 0)); } /* Charge! */ *tx = p_ptr->px; *ty = p_ptr->py; } #endif /* 0 */ return (TRUE); } /* Move directly away from character. */ *tx = -(p_ptr->px - m_ptr->fx); *ty = -(p_ptr->py - m_ptr->fy); /* We want to run away */ return (TRUE); } /* * Choose "logical" directions for monster movement */ static bool get_moves(int m_idx, int *mm) { int py = p_ptr->py; int px = p_ptr->px; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int xx, yy; int tx, ty; bool will_run = mon_will_run(m_ptr); bool done = FALSE; cave_type *c_ptr; if (will_run) { /* Get flow target */ get_move_retreat(m_ptr, &tx, &ty); } else { /* Get flow target */ get_move_advance(m_ptr, &tx, &ty); } if (!stupid_monsters && !will_run && is_hostile(m_ptr)) { /* * Animal packs try to get the player out of corridors * (...unless they can move through walls -- TY) */ if ((FLAG(r_ptr, RF_FRIENDS)) && (FLAG(r_ptr, RF_ANIMAL)) && !(FLAG(r_ptr, RF_PASS_WALL) || FLAG(r_ptr, RF_KILL_WALL)) && smart_packs) { int i, room = 0; /* Count room grids next to player */ for (i = 0; i < 8; i++) { xx = px + ddx_ddd[i]; yy = py + ddy_ddd[i]; if (!in_bounds2(xx, yy)) continue; c_ptr = area(xx, yy); /* Check grid */ if (cave_floor_grid(c_ptr) && test_monster_square(c_ptr, r_ptr)) { /* One more room grid */ room++; } } /* Not in a room and strong player */ if (room <= (8 * (p_ptr->chp + p_ptr->csp)) / (p_ptr->mhp + p_ptr->msp)) { /* Find hiding place */ if (find_hiding(m_ptr, &tx, &ty)) done = TRUE; } } /* Monster groups try to surround the player */ if (!done && (FLAG(r_ptr, RF_FRIENDS)) && randint0(2) == 0) { int i, i2; int xx2, yy2; int cx = px, cy = py; monster_type *fm_ptr; monster_race *fr_ptr; int free_squares = 0; /* Count alternate squares we can move to */ for (i = 0; i < 8; i++) { xx = m_ptr->fx + ddx_ddd[i]; yy = m_ptr->fy + ddy_ddd[i]; if (!in_bounds2(xx, yy)) continue; /* Require next to player */ if (ABS(xx - px) > 1) continue; if (ABS(yy - py) > 1) continue; /* Require a square we can move to */ if (cave_passable_mon(m_ptr, area(xx, yy)) < 50) continue; /* Count this square */ free_squares++; /* Possibly move here */ if (randint0(free_squares) == 0) { cx = xx; cy = yy; } } /* Count monsters which are "pushing" */ if (free_squares) { for (i = 0; i < 8; i++) { int blocked = TRUE; xx = m_ptr->fx + ddx_ddd[i]; yy = m_ptr->fy + ddy_ddd[i]; if (!in_bounds2(xx, yy)) continue; c_ptr = area(xx, yy); /* Must be a monster */ if (!c_ptr->m_idx) continue; fm_ptr = &m_list[c_ptr->m_idx]; fr_ptr = &r_info[fm_ptr->r_idx]; /* Must be awake and mobile */ if (fm_ptr->csleep || (FLAG(r_ptr, RF_NEVER_MOVE))) continue; /* Check if this monster can move */ for (i2 = 0; i2 < 8; i2++) { xx2 = fm_ptr->fx + ddx_ddd[i2]; yy2 = fm_ptr->fy + ddy_ddd[i2]; if (!in_bounds2(xx2, yy2)) continue; /* Require next to player */ if (ABS(xx2 - px) > 1) continue; if (ABS(yy2 - py) > 1) continue; if (cave_passable_mon(fm_ptr, area(xx2, yy2)) >= 50) { blocked = FALSE; break; } } if (!blocked) continue; /* If we're blocking something, move to free space */ tx = cx; ty = cy; break; } } } } if (!stupid_monsters) { /* Check for no move */ if (!tx && !ty) return (FALSE); } /* Set our target? */ m_ptr->tx = tx; m_ptr->ty = ty; /* Move towards our target */ convert_target_dir(m_ptr, mm); /* Wants to move... */ return (TRUE); } static int check_hit2(int power, int level, int ac) { int i, k; /* Percentile dice */ k = randint0(100); /* Hack -- Always miss or hit */ if (k < 10) return (k < 5); /* Calculate the "attack quality" */ i = (power + (level * 3)); /* Power and Level compete against Armor */ if ((i > 0) && (randint1(i) > ((ac * 3) / 4))) return (TRUE); /* Assume miss */ return (FALSE); } /* Monster attacks monster */ static bool monst_attack_monst(int m_idx, int t_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_type *t_ptr = &m_list[t_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_race *tr_ptr = &r_info[t_ptr->r_idx]; int ap_cnt; int ac, rlev, pt; char m_name[80], t_name[80]; char ddesc[80], temp[80]; bool blinked, heal_effect; bool explode = FALSE, touched = FALSE, fear = FALSE; int y_saver = t_ptr->fy; int x_saver = t_ptr->fx; bool see_m = m_ptr->ml; bool see_t = t_ptr->ml; bool see_either = see_m || see_t; /* Can the player be aware of this attack? */ bool known = (m_ptr->cdis <= MAX_SIGHT) || (t_ptr->cdis <= MAX_SIGHT); /* Cannot attack self */ if (m_idx == t_idx) return FALSE; /* Not allowed to attack */ if (FLAG(r_ptr, RF_NEVER_BLOW)) return FALSE; /* Wake it up */ t_ptr->csleep = 0; /* Total armor */ ac = tr_ptr->ac; /* Extract the effective monster level */ rlev = ((r_ptr->hdice * 2 >= 1) ? r_ptr->hdice * 2 : 1); /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0, 80); /* Get the monster name (or "it") */ monster_desc(t_name, t_ptr, 0, 80); /* Get the "died from" information (i.e. "a kobold") */ monster_desc(ddesc, m_ptr, 0x88, 80); /* Assume no blink */ blinked = FALSE; if (!see_either && known) { p_ptr->state.mon_fight = TRUE; } /* Scan through all four blows */ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++) { bool obvious = FALSE; int power = 0; int damage = 0; cptr act = NULL; /* Extract the attack infomation */ int effect = r_ptr->blow[ap_cnt].effect; int method = r_ptr->blow[ap_cnt].method; int d_dice = r_ptr->blow[ap_cnt].d_dice; int d_side = r_ptr->blow[ap_cnt].d_side; /* Stop attacking if the target teleports away */ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) break; /* Stop attacking if the aggressor dies (fire sheath etc.) */ if ((!m_ptr->r_idx) || (!t_ptr->r_idx)) return TRUE; /* Hack -- no more attacks */ if (!method) break; if (blinked) /* Stop! */ { /* break; */ } /* Extract the attack "power" */ switch (effect) { case RBE_HURT: { power = 60; break; } case RBE_POISON: { power = 5; break; } case RBE_UN_BONUS: { power = 20; break; } case RBE_UN_POWER: { power = 15; break; } case RBE_EAT_GOLD: { power = 5; break; } case RBE_EAT_ITEM: { power = 5; break; } case RBE_EAT_FOOD: { power = 5; break; } case RBE_EAT_LITE: { power = 5; break; } case RBE_ACID: { power = 0; break; } case RBE_ELEC: { power = 10; break; } case RBE_FIRE: { power = 10; break; } case RBE_COLD: { power = 10; break; } case RBE_BLIND: { power = 2; break; } case RBE_CONFUSE: { power = 10; break; } case RBE_TERRIFY: { power = 10; break; } case RBE_PARALYZE: { power = 2; break; } case RBE_LOSE_STR: { power = 0; break; } case RBE_LOSE_DEX: { power = 0; break; } case RBE_LOSE_CON: { power = 0; break; } case RBE_LOSE_INT: { power = 0; break; } case RBE_LOSE_WIS: { power = 0; break; } case RBE_LOSE_CHR: { power = 0; break; } case RBE_LOSE_ALL: { power = 2; break; } case RBE_SHATTER: { power = 60; break; } case RBE_EXP_10: { power = 5; break; } case RBE_EXP_20: { power = 5; break; } case RBE_EXP_40: { power = 5; break; } case RBE_EXP_80: { power = 5; break; } case RBE_DISEASE: { power = 5; break; } case RBE_TIME: { power = 5; break; } case RBE_EXP_VAMP: { power = 5; break; } } /* Monster hits */ if (!effect || check_hit2(power, rlev, ac)) { /* Hack - set explosion flag */ if (method == RBM_EXPLODE) { if (see_either) disturb(TRUE); explode = TRUE; } /* Get action + touch flag */ act = rbm_info[method].action; touched = rbm_info[method].touched; /* Message */ if (act && see_either) { /* Look to see if we've spotted a mimic */ if ((m_ptr->smart & SM_MIMIC) && m_ptr->ml) { /* We've spotted it */ msgf("You see %v!", MONSTER_FMT( m_ptr, 0x88)); /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now */ update_mon_vis(m_ptr->r_idx, 1); } /* Look to see if we've spotted a mimic */ if ((t_ptr->smart & SM_MIMIC) && t_ptr->ml) { /* We've spotted it */ msgf("You see %v!", MONSTER_FMT(t_ptr, 0x88)); /* Toggle flag */ t_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now */ update_mon_vis(t_ptr->r_idx, 1); } if ((p_ptr->tim.image) && one_in_(3)) { strnfmt(temp, 80, "%s %s.", silly_attacks[randint0(MAX_SILLY_ATTACK)], t_name); } else strnfmt(temp, 80, act, t_name); msgf("%^s %s", m_name, temp); } /* Hack -- assume all attacks are obvious */ obvious = TRUE; /* Roll out the damage */ damage = damroll(d_dice, d_side); /* Assume no healing effect */ heal_effect = FALSE; pt = GF_MISSILE; /* Apply appropriate damage */ switch (effect) { case 0: { damage = 0; pt = 0; break; } case RBE_HURT: { damage -= (damage * ((ac < 150) ? ac : 150) / 250); break; } case RBE_POISON: case RBE_DISEASE: { pt = GF_POIS; break; } case RBE_UN_BONUS: case RBE_UN_POWER: { pt = GF_DISENCHANT; break; } case RBE_EAT_FOOD: case RBE_EAT_LITE: { pt = damage = 0; break; } case RBE_EAT_ITEM: case RBE_EAT_GOLD: { pt = damage = 0; if (one_in_(2)) blinked = TRUE; break; } case RBE_ACID: { pt = GF_ACID; break; } case RBE_ELEC: { pt = GF_ELEC; break; } case RBE_FIRE: { pt = GF_FIRE; break; } case RBE_COLD: { pt = GF_COLD; break; } case RBE_BLIND: { break; } case RBE_CONFUSE: { pt = GF_CONFUSION; break; } case RBE_TERRIFY: { pt = GF_TURN_ALL; break; } case RBE_PARALYZE: { pt = GF_OLD_SLEEP; /* sort of close... */ break; } case RBE_LOSE_STR: case RBE_LOSE_INT: case RBE_LOSE_WIS: case RBE_LOSE_DEX: case RBE_LOSE_CON: case RBE_LOSE_CHR: case RBE_LOSE_ALL: { break; } case RBE_SHATTER: { if (damage > 23) { (void)earthquake(m_ptr->fx, m_ptr->fy, 8); } break; } case RBE_EXP_10: case RBE_EXP_20: case RBE_EXP_40: case RBE_EXP_80: { pt = GF_NETHER; break; } case RBE_TIME: { pt = GF_TIME; break; } case RBE_EXP_VAMP: { pt = GF_OLD_DRAIN; heal_effect = TRUE; break; } default: { pt = 0; break; } } if (pt) { /* Do damage if not exploding */ if (!explode) { (void)project(m_idx, 0, t_ptr->fx, t_ptr->fy, (pt == GF_OLD_SLEEP ? r_ptr->hdice * 2 : damage), pt, PROJECT_KILL | PROJECT_STOP); } if (heal_effect) { if ((monster_living(tr_ptr)) && (damage > 2)) { bool did_heal = FALSE; if (m_ptr->hp < m_ptr->maxhp) did_heal = TRUE; /* Heal */ m_ptr->hp += damroll(4, damage / 6); if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Special message */ if (see_m && did_heal) { msgf("%^s appears healthier.", m_name); } } } if (touched) { /* Aura fire */ if ((FLAG(tr_ptr, RF_AURA_FIRE)) && !(FLAG(r_ptr, RF_IM_FIRE))) { if (see_either) { blinked = FALSE; msgf("%^s is suddenly very hot!", m_name); if (see_t) tr_ptr->r_flags[1] |= RF1_AURA_FIRE; } (void)project(t_idx, 0, m_ptr->fx, m_ptr->fy, damroll(1 + ((tr_ptr->hdice * 2) / 26), 1 + ((tr_ptr->hdice * 2) / 17)), GF_FIRE, PROJECT_KILL | PROJECT_STOP); } /* Aura cold */ if ((FLAG(tr_ptr, RF_AURA_COLD)) && !(FLAG(r_ptr, RF_IM_COLD))) { if (see_either) { blinked = FALSE; msgf("%^s is suddenly very cold!", m_name); if (see_t) tr_ptr->r_flags[2] |= RF2_AURA_COLD; } (void)project(t_idx, 0, m_ptr->fx, m_ptr->fy, damroll(1 + ((tr_ptr->hdice * 2) / 26), 1 + ((tr_ptr->hdice * 2) / 17)), GF_COLD, PROJECT_KILL | PROJECT_STOP); } /* Aura elec */ if (FLAG(tr_ptr, RF_AURA_ELEC) && !FLAG(r_ptr, RF_IM_ELEC)) { if (see_either) { blinked = FALSE; msgf("%^s gets zapped!", m_name); if (see_t) tr_ptr->r_flags[1] |= RF1_AURA_ELEC; } (void)project(t_idx, 0, m_ptr->fx, m_ptr->fy, damroll(1 + ((tr_ptr->hdice * 2) / 26), 1 + ((tr_ptr->hdice * 2) / 17)), GF_ELEC, PROJECT_KILL | PROJECT_STOP); } } } } /* Monster missed the monster */ else { /* Analyze failed attacks */ switch (method) { case RBM_HIT: case RBM_TOUCH: case RBM_PUNCH: case RBM_KICK: case RBM_CLAW: case RBM_BITE: case RBM_STING: case RBM_XXX1: case RBM_BUTT: case RBM_CRUSH: case RBM_ENGULF: case RBM_CHARGE: { /* Visible monsters */ if (see_m) { /* Message */ msgf("%^s misses %s.", m_name, t_name); } break; } } } /* Analyze "visible" monsters only */ if (see_m) { /* Count "obvious" attacks (and ones that cause damage) */ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) { /* Count attacks of this type */ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) { r_ptr->r_blows[ap_cnt]++; } } } } if (explode) { sound(SOUND_EXPLODE); /* Cancel Invulnerability */ if (m_ptr->invulner) m_ptr->invulner = 0; mon_take_hit_mon(m_idx, m_ptr->hp + 1, &fear, " explodes into tiny shreds."); blinked = FALSE; } /* Blink away */ if (blinked) { if (see_m) { msgf("The thief flees laughing!"); } else if (known) { p_ptr->state.mon_fight = TRUE; } (void)teleport_away(m_idx, MAX_SIGHT * 2 + 5); } return TRUE; } /* If the monster is on a tricky feat add it to the monster memory */ static void monster_memory_feat(cave_type *c_ptr, monster_race *r_ptr) { switch (c_ptr->feat) { case FEAT_SHAL_LAVA: case FEAT_DEEP_LAVA: { /* If the monster can not fly it must have IM_FIRE */ if (!FLAG(r_ptr, RF_CAN_FLY)) r_ptr->r_flags[2] |= (RF2_IM_FIRE); break; } case FEAT_SHAL_ACID: case FEAT_DEEP_ACID: { /* If the monster can not fly it must have IM_ACID */ if (!FLAG(r_ptr, RF_CAN_FLY)) r_ptr->r_flags[2] |= (RF2_IM_ACID); break; } case FEAT_SHAL_SWAMP: case FEAT_DEEP_SWAMP: { /* If the monster can not fly it must have IM_POIS */ if (!FLAG(r_ptr, RF_CAN_FLY)) r_ptr->r_flags[2] |= (RF2_IM_POIS); break; } case FEAT_OCEAN_WATER: case FEAT_DEEP_WATER: { /* If the monster can not fly or isn't aquatic it can swim */ if (!FLAG(r_ptr, RF_CAN_FLY) && !FLAG(r_ptr, RF_AQUATIC)) r_ptr->r_flags[6] |= (RF6_CAN_SWIM); break; } } } /* * Actually move the monster */ static void take_move(int m_idx, int *mm) { int i; int d; int ny, nx; int oy, ox; cave_type *c_ptr; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_type *y_ptr; char m_name[80]; /* Assume nothing */ bool do_turn = FALSE; bool do_move = TRUE; bool did_open_door = FALSE; bool did_bash_door = FALSE; bool did_take_item = FALSE; bool did_kill_item = FALSE; bool did_move_body = FALSE; bool did_pass_wall = FALSE; bool did_kill_wall = FALSE; bool see_grid = FALSE; /* Get the origin */ oy = m_ptr->fy; ox = m_ptr->fx; /* Take a zero-terminated array of "directions" */ for (i = 0; mm[i]; i++) { /* Get the direction */ d = mm[i]; /* Hack -- allow "randomized" motion */ if (d == 5) d = ddd[randint0(8)]; /* Get the destination */ ny = oy + ddy[d]; nx = ox + ddx[d]; /* Ignore locations off of edge */ if (!in_boundsp(nx, ny)) continue; /* Access that cave grid */ c_ptr = area(nx, ny); if (in_boundsp(nx, ny) && player_can_see_grid(parea(nx, ny))) { see_grid = TRUE; } /* Access that cave grid's contents */ y_ptr = &m_list[c_ptr->m_idx]; /* Floor is open? */ if (cave_floor_grid(c_ptr)) { /* Go ahead and move */ do_move = TRUE; } /* Hack -- player 'in' wall */ else if ((ny == p_ptr->py) && (nx == p_ptr->px)) { do_move = TRUE; } else if (c_ptr->m_idx) { /* Possibly a monster to attack */ do_move = TRUE; } /* Permanent wall */ else if (cave_perma_grid(c_ptr) && cave_wall_grid(c_ptr)) { do_move = FALSE; } /* Hack -- closed or secret doors are no obstacle */ else if ((c_ptr->feat == FEAT_CLOSED) || (c_ptr->feat == FEAT_SECRET)) { do_move = TRUE; } /* Monster moves through walls (and doors) */ else if (FLAG(r_ptr, RF_PASS_WALL)) { /* Pass through walls/doors/rubble */ do_move = TRUE; /* Monster went through a wall */ did_pass_wall = TRUE; } /* Monster destroys walls (and doors) */ else if (FLAG(r_ptr, RF_KILL_WALL)) { /* Eat through walls/doors/rubble */ do_move = TRUE; /* Monster destroyed a wall */ did_kill_wall = TRUE; if (one_in_(GRINDNOISE)) { msgf("There is a grinding sound."); } /* Notice */ cave_set_feat(nx, ny, the_floor()); } else if (cave_wall_grid(c_ptr)) { /* This monster cannot walk through walls */ do_move = FALSE; } /* Require "empty" fields */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) { do_move = FALSE; } /* Call the hook */ field_script(c_ptr, FIELD_ACT_MON_ENTER_TEST, "ibb:bbbb", LUA_VAR_NAMED(m_ptr->r_idx, "r_idx"), LUA_VAR_NAMED(see_grid, "visible"), LUA_VAR_NAMED((!is_pet(m_ptr) || p_ptr->pet_open_doors), "allow_open"), LUA_RETURN(do_move), LUA_RETURN(did_open_door), LUA_RETURN(do_turn), LUA_RETURN(did_bash_door)); /* Open / bash doors */ if (did_open_door) { cave_set_feat(nx, ny, FEAT_OPEN); } else if (did_bash_door) { if (one_in_(2)) { cave_set_feat(nx, ny, FEAT_BROKEN); } /* Open the door */ else { cave_set_feat(nx, ny, FEAT_OPEN); } } /* Some monsters never attack */ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px) && (FLAG(r_ptr, RF_NEVER_BLOW))) { /* Hack -- memorize lack of attacks */ if (m_ptr->ml) r_ptr->r_flags[0] |= (RF0_NEVER_BLOW); /* Do not move */ do_move = FALSE; } /* Handle closed doors and secret doors */ if (do_move && ((c_ptr->feat == FEAT_CLOSED) || (c_ptr->feat == FEAT_SECRET)) && (FLAG(r_ptr, RF_OPEN_DOOR)) && (!is_pet(m_ptr) || p_ptr->pet_open_doors)) { /* Open the door */ cave_set_feat(nx, ny, FEAT_OPEN); /* Take a turn */ do_turn = TRUE; /* Do not move in any case. */ do_move = FALSE; /* The door was opened */ did_open_door = TRUE; } else if (((c_ptr->feat == FEAT_CLOSED) || (c_ptr->feat == FEAT_SECRET)) && !did_pass_wall) { /* Monsters cannot walk through closed doors */ do_move = FALSE; } /* The player is in the way. Attack him. */ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px)) { /* Do the attack */ (void)make_attack_normal(m_idx); /* Do not move */ do_move = FALSE; /* Took a turn */ do_turn = TRUE; } /* Not over the pattern */ if (cave_pattern_grid(c_ptr) && !do_turn && !(FLAG(r_ptr, RF_CAN_FLY))) { do_move = FALSE; } /* A monster is in the way */ if (do_move && c_ptr->m_idx) { monster_race *z_ptr = &r_info[y_ptr->r_idx]; monster_type *m2_ptr = &m_list[c_ptr->m_idx]; /* Assume no movement */ do_move = FALSE; /* Attack 'enemies' */ if ((FLAG(r_ptr, RF_KILL_BODY) && (r_ptr->mexp * r_ptr->hdice * 2 > z_ptr->mexp * z_ptr->level) && (cave_floor_grid(c_ptr))) || are_enemies(m_ptr, m2_ptr) || m_ptr->confused) { do_move = FALSE; if (FLAG(r_ptr, RF_KILL_BODY)) r_ptr->r_flags[1] |= (RF1_KILL_BODY); /* attack */ if ((m2_ptr->r_idx) && (m2_ptr->hp >= 0)) { if (monst_attack_monst(m_idx, area(nx, ny)->m_idx)) return; } } /* Push past weaker monsters (unless leaving a wall) */ else if ((FLAG(r_ptr, RF_MOVE_BODY)) && (r_ptr->mexp > z_ptr->mexp) && cave_floor_grid(c_ptr) && (cave_floor_grid(area(m_ptr->fx, m_ptr->fy)))) { /* Allow movement */ do_move = TRUE; /* Monster pushed past another monster */ did_move_body = TRUE; /* XXX XXX XXX Message */ } } /* * Check if monster can cross terrain * This is checked after the normal attacks * to allow monsters to attack an enemy, * even if it can't enter the terrain. */ if (do_move && !test_monster_square(c_ptr, r_ptr)) { /* Assume no move allowed */ do_move = FALSE; } /* Maybe add info to the monster memory */ if (m_ptr->ml) monster_memory_feat(c_ptr, r_ptr); /* Some monsters never move */ if (do_move && (FLAG(r_ptr, RF_NEVER_MOVE))) { /* Hack -- memorize lack of attacks */ if (m_ptr->ml) r_ptr->r_flags[0] |= (RF0_NEVER_MOVE); /* Do not move */ do_move = FALSE; } /* Creature has been allowed move */ if (do_move) { cave_type *old_ptr = area(ox, oy); object_type *o_ptr; /* Take a turn */ do_turn = TRUE; /* Look to see if we've spotted a mimic */ if ((m_ptr->smart & SM_MIMIC) && m_ptr->ml) { /* We've spotted it */ msgf("You see %v!", MONSTER_FMT(m_ptr, 0x88)); /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now */ update_mon_vis(m_ptr->r_idx, 1); } /* Hack -- Update the old location */ old_ptr->m_idx = c_ptr->m_idx; /* Mega-Hack -- move the old monster, if any */ if (c_ptr->m_idx) { /* Move the old monster */ y_ptr->fy = oy; y_ptr->fx = ox; /* Update the old monster */ update_mon(c_ptr->m_idx, TRUE); /* Wake up the moved monster */ m_list[c_ptr->m_idx].csleep = 0; } /* Hack -- Update the new location */ c_ptr->m_idx = m_idx; /* Move the monster */ m_ptr->fy = ny; m_ptr->fx = nx; /* Update the monster */ update_mon(m_idx, TRUE); /* Process fields under the monster. */ field_script(old_ptr, FIELD_ACT_MONSTER_ENTER, ""); /* Redraw the old grid */ lite_spot(ox, oy); /* Redraw the new grid */ lite_spot(nx, ny); /* Possible disturb */ if (m_ptr->ml && (m_ptr->mflag & MFLAG_VIEW) && disturb_near) { /* Disturb */ if (is_hostile(m_ptr)) disturb(FALSE); } /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Skip gold */ if (o_ptr->tval == TV_GOLD) continue; /* * Skip statues, to avoid extreme silliness * like a novice rogue with pockets full of them. */ if (o_ptr->tval == TV_STATUE) continue; /* Take or Kill objects on the floor */ if ((FLAG(r_ptr, RF_TAKE_ITEM) || FLAG(r_ptr, RF_KILL_ITEM)) && (!is_pet(m_ptr) || p_ptr->pet_pickup_items)) { u32b flg3 = 0L; /* Acquire the monster name */ monster_desc(m_name, m_ptr, 0x04, 80); /* React to objects that hurt the monster */ if (FLAG(o_ptr, TR_KILL_DRAGON)) flg3 |= (RF2_DRAGON); if (FLAG(o_ptr, TR_SLAY_DRAGON)) flg3 |= (RF2_DRAGON); if (FLAG(o_ptr, TR_SLAY_TROLL)) flg3 |= (RF2_TROLL); if (FLAG(o_ptr, TR_SLAY_GIANT)) flg3 |= (RF2_GIANT); if (FLAG(o_ptr, TR_SLAY_ORC)) flg3 |= (RF2_ORC); if (FLAG(o_ptr, TR_SLAY_DEMON)) flg3 |= (RF2_DEMON); if (FLAG(o_ptr, TR_SLAY_UNDEAD)) flg3 |= (RF2_UNDEAD); if (FLAG(o_ptr, TR_SLAY_ANIMAL)) flg3 |= (RF2_ANIMAL); if (FLAG(o_ptr, TR_SLAY_EVIL)) flg3 |= (RF2_EVIL); /* The object cannot be picked up by the monster */ if ((FLAG(o_ptr, TR_INSTA_ART)) || (r_ptr->flags[2] & flg3)) { /* Only give a message for "take_item" */ if ((FLAG(r_ptr, RF_TAKE_ITEM)) && (FLAG(r_ptr, RF_STUPID))) { /* Take note */ did_take_item = TRUE; /* Describe observable situations */ if (m_ptr->ml && see_grid) { /* Dump a message */ msgf("%^s tries to pick up %v, but fails.", m_name, OBJECT_FMT(o_ptr, TRUE, 3)); } } } /* Pick up the item */ else if (FLAG(r_ptr, RF_TAKE_ITEM)) { /* Take note */ did_take_item = TRUE; /* Describe observable situations */ if (see_grid) { /* Dump a message */ msgf("%^s picks up %v.", m_name, OBJECT_FMT(o_ptr, TRUE, 3)); } /* Forget mark */ o_ptr->info &= ~(OB_SEEN); /* Forget location */ o_ptr->iy = o_ptr->ix = 0; /* XXX Hack - Forget region */ o_ptr->region = 0; /* Hold the object */ move_object(&m_ptr->hold_o_idx, &c_ptr->o_idx, o_ptr); } /* Destroy the item if not a pet */ else if (!is_pet(m_ptr)) { /* Take note */ did_kill_item = TRUE; /* Describe observable situations */ if (see_grid) { /* Dump a message */ msgf("%^s destroys %v.", m_name, OBJECT_FMT(o_ptr, TRUE, 3)); } /* Delete the object */ delete_dungeon_object(o_ptr); } } } OBJ_ITT_END; } /* Stop when done */ if (do_turn) break; } /* If we haven't done anything, try casting a spell again */ if (!do_turn && !do_move && !m_ptr->monfear && !stupid_monsters && !make_attack_spell(m_idx)) { /* Cast spell */ if (make_attack_spell(m_idx)) return; } /* Notice changes in view */ if (do_move && (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2))) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Learn things from observable monster */ if (m_ptr->ml) { /* Monster opened a door */ if (did_open_door) r_ptr->r_flags[1] |= (RF1_OPEN_DOOR); /* Monster bashed a door */ if (did_bash_door) r_ptr->r_flags[1] |= (RF1_BASH_DOOR); /* Monster tried to pick something up */ if (did_take_item) r_ptr->r_flags[1] |= (RF1_TAKE_ITEM); /* Monster tried to crush something */ if (did_kill_item) r_ptr->r_flags[1] |= (RF1_KILL_ITEM); /* Monster pushed past another monster */ if (did_move_body) r_ptr->r_flags[1] |= (RF1_MOVE_BODY); /* Monster passed through a wall */ if (did_pass_wall) r_ptr->r_flags[1] |= (RF1_PASS_WALL); /* Monster destroyed a wall */ if (did_kill_wall) r_ptr->r_flags[1] |= (RF1_KILL_WALL); } /* Hack -- get "bold" if out of options */ if (!do_turn && !do_move && m_ptr->monfear) { /* No longer afraid */ m_ptr->monfear = 0; /* Message if seen */ if (m_ptr->ml) { /* Dump a message */ msgf("%^v turns to fight!", MONSTER_FMT(m_ptr, 0)); chg_virtue(V_COMPASSION, -1); } /* XXX XXX XXX Actually do something now (?) */ } } /* * Process a monster * * The monster is known to be within 100 grids of the player * * In several cases, we directly update the monster lore * * Note that a monster is only allowed to "reproduce" if there * are a limited number of "reproducing" monsters on the current * level. This should prevent the level from being "swamped" by * reproducing monsters. It also allows a large mass of mice to * prevent a louse from multiplying, but this is a small price to * pay for a simple multiplication method. * * XXX Monster fear is slightly odd, in particular, monsters will * fixate on opening a door even if they cannot open it. Actually, * the same thing happens to normal monsters when they hit a door * * XXX XXX XXX In addition, monsters which *cannot* open or bash * down a door will still stand there trying to open it... * * XXX Technically, need to check for monster in the way * combined with that monster being in a wall (or door?) * * A "direction" of "5" means "pick a random direction". */ static void process_monster(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80]; int d, oy, ox; int mm[8]; cave_type *c_ptr; bool gets_angry = FALSE; int rand_move = 0; /* Quantum monsters are odd */ if (FLAG(r_ptr, RF_QUANTUM)) { /* Sometimes skip move */ if (one_in_(2)) return; /* Sometimes die */ if (one_in_((m_idx % 100) + 10) && !(FLAG(r_ptr, RF_QUESTOR))) { bool sad = FALSE; if (is_pet(m_ptr) && !(m_ptr->ml)) sad = TRUE; if (m_ptr->ml) { /* Oops */ msgf("%^v disappears!", MONSTER_FMT(m_ptr, 0)); } /* Generate treasure, etc */ (void)monster_death(m_idx, TRUE); /* Delete the monster */ delete_monster_idx(m_idx); if (sad) { msgf("You feel sad for a moment."); } return; } } /* Get the origin */ oy = m_ptr->fy; ox = m_ptr->fx; /* Access that cave grid */ c_ptr = area(ox, oy); /* Process fields under the monster. */ field_script(c_ptr, FIELD_ACT_MONSTER_ON, ""); /* Handle "sleep" */ if (m_ptr->csleep) { u32b notice = 0; /* Hack -- handle non-aggravation */ if (!(FLAG(p_ptr, TR_AGGRAVATE))) notice = randint0(1024); /* Nightmare monsters are more alert */ if (ironman_nightmare) notice /= 2; /* Hack -- See if monster "notices" player */ if ((notice * notice * notice) <= p_ptr->noise) { /* Hack -- amount of "waking" */ d = 1; /* Wake up faster near the player */ if (m_ptr->cdis < 50) d = (100 / m_ptr->cdis); /* Hack -- handle aggravation */ if (FLAG(p_ptr, TR_AGGRAVATE)) d = m_ptr->csleep; /* Still asleep */ if (m_ptr->csleep > d) { /* Monster wakes up "a little bit" */ m_ptr->csleep -= d; /* Notice the "not waking up" */ if (m_ptr->ml) { /* Hack -- Count the ignores */ if (r_ptr->r_ignore < MAX_UCHAR) { r_ptr->r_ignore++; } } } /* Just woke up */ else { /* Reset sleep counter */ m_ptr->csleep = 0; /* Notice the "waking up" */ if ((m_ptr->ml) && (!(m_ptr->smart & SM_MIMIC))) { /* Dump a message */ msgf("%^v wakes up.", MONSTER_FMT(m_ptr, 0)); /* Redraw the health bar */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Hack -- Count the wakings */ if (r_ptr->r_wake < MAX_UCHAR) { r_ptr->r_wake++; } } } } /* Still sleeping */ if (m_ptr->csleep) return; } /* Handle "stun" */ if (m_ptr->stunned) { d = 1; /* Make a "saving throw" against stun */ if (randint0(10000) <= r_ptr->hdice * 2 * r_ptr->hdice * 2) { /* Recover fully */ d = m_ptr->stunned; } /* Hack -- Recover from stun */ if (m_ptr->stunned > d) { /* Recover somewhat */ m_ptr->stunned -= d; } /* Fully recover */ else { /* Recover fully */ m_ptr->stunned = 0; /* Message if visible */ if (m_ptr->ml) { /* Dump a message */ msgf("%^v is no longer stunned.", MONSTER_FMT(m_ptr, 0)); } } /* Still stunned */ if (m_ptr->stunned) return; } /* Handle confusion */ if (m_ptr->confused) { /* Amount of "boldness" */ d = randint1(r_ptr->hdice * 2 / 20 + 1); /* Still confused */ if (m_ptr->confused > d) { /* Reduce the confusion */ m_ptr->confused -= d; } /* Recovered */ else { /* No longer confused */ m_ptr->confused = 0; /* Message if visible */ if ((m_ptr->ml) && (!(m_ptr->smart & SM_MIMIC))) { /* Dump a message */ msgf("%^v is no longer confused.", MONSTER_FMT(m_ptr, 0)); } } } /* Handle Invulnerability */ if (m_ptr->invulner) { /* Reduce by one, note if expires */ m_ptr->invulner--; if (!(m_ptr->invulner) && m_ptr->ml) { /* Dump a message */ msgf("%^v is no longer invulnerable.", MONSTER_FMT(m_ptr, 0)); } } /* No one wants to be your friend if you're aggravating */ if (!is_hostile(m_ptr) && (FLAG(p_ptr, TR_AGGRAVATE))) gets_angry = TRUE; /* Acquire the monster name */ monster_desc(m_name, m_ptr, 0, 80); if (gets_angry) { msgf("%^s suddenly becomes hostile!", m_name); set_hostile(m_ptr); } /* Handle "fear" */ if (m_ptr->monfear) { /* Amount of "boldness" */ d = randint1(r_ptr->hdice * 2 / 20 + 1); /* Still afraid */ if (m_ptr->monfear > d) { /* Reduce the fear */ m_ptr->monfear -= d; } /* Recover from fear, take note if seen */ else { /* No longer afraid */ m_ptr->monfear = 0; /* Visual note */ if (m_ptr->ml) { /* Acquire the monster poss + dump message*/ msgf("%^s recovers %v courage.", m_name, MONSTER_FMT(m_ptr, 0x22)); } } } /* Attempt to "multiply" if able and allowed */ if ((FLAG(r_ptr, RF_MULTIPLY)) && (num_repro < MAX_REPRO)) { int k, y, x; /* Count the adjacent monsters */ for (k = 0, y = oy - 1; y <= oy + 1; y++) { for (x = ox - 1; x <= ox + 1; x++) { /* Ignore locations off of edge */ if (!in_bounds2(x, y)) continue; if (area(x, y)->m_idx) k++; } } /* Hack -- multiply slower in crowded areas */ if ((k < 4) && (!k || one_in_(k * MON_MULT_ADJ))) { /* Try to multiply */ if (multiply_monster (m_idx, FALSE, is_friendly(m_ptr), is_pet(m_ptr))) { /* Take note if visible */ if (m_ptr->ml) { r_ptr->r_flags[1] |= (RF1_MULTIPLY); } /* Multiplying takes energy */ return; } } } /* Hack! "Cyber" monster makes noise... */ if (mon_name_cont(r_ptr, "Cyber") && one_in_(CYBERNOISE) && !m_ptr->ml && (m_ptr->cdis <= MAX_SIGHT)) { msgf("You hear heavy steps."); } /* Access that cave grid */ c_ptr = area(ox, oy); /* Some monsters can speak */ if (speak_unique && (FLAG(r_ptr, RF_CAN_SPEAK)) && one_in_(SPEAK_CHANCE) && in_boundsp(ox, oy) && player_has_los_grid(parea(ox, oy))) { char monmessage[1024]; cptr filename; /* Acquire the monster name/poss */ if (!m_ptr->ml) strcpy(m_name, "It"); /* Select the file for monster quotes */ if (m_ptr->monfear) filename = "monfear.txt"; else if (is_friendly(m_ptr)) filename = "monfrien.txt"; else filename = "monspeak.txt"; /* Get the monster line */ if (get_rnd_line(filename, m_ptr->r_idx, monmessage) == 0) { /* Say something */ msgf("%^s %s", m_name, monmessage); } } /* Attempt to cast a spell */ if (make_attack_spell(m_idx)) return; /* * Attempt to cast a spell at an enemy other than the player * (may slow the game a smidgeon, but I haven't noticed.) */ if (monst_spell_monst(m_idx)) return; /* Hack -- Assume no movement */ mm[0] = mm[1] = mm[2] = mm[3] = 0; mm[4] = mm[5] = mm[6] = mm[7] = 0; if (FLAG(r_ptr, RF_RAND_50)) rand_move += 50; if (FLAG(r_ptr, RF_RAND_25)) rand_move += 25; /* Confused -- 100% random */ if (m_ptr->confused) { /* Try four "random" directions */ mm[0] = mm[1] = mm[2] = mm[3] = 5; } /* Random movement */ else if (rand_move && (randint0(100) < rand_move)) { /* Memorize flags */ if (m_ptr->ml && (FLAG(r_ptr, RF_RAND_50))) r_ptr->r_flags[0] |= (RF0_RAND_50); if (m_ptr->ml && (FLAG(r_ptr, RF_RAND_25))) r_ptr->r_flags[0] |= (RF0_RAND_25); /* Try four "random" directions */ mm[0] = mm[1] = mm[2] = mm[3] = 5; } /* Can't reach player - find something else to hit */ else if ((FLAG(r_ptr, RF_NEVER_MOVE)) && (m_ptr->cdis > 1)) { /* Try four "random" directions */ mm[0] = mm[1] = mm[2] = mm[3] = 5; } /* Pets will follow the player */ else if (is_pet(m_ptr)) { /* Are we trying to avoid the player? */ bool avoid = ((p_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - p_ptr->pet_follow_distance))); /* Do we want to find the player? */ bool lonely = (!avoid && (m_ptr->cdis > p_ptr->pet_follow_distance)); /* Should we find the player if we can't find a monster? */ bool distant = (m_ptr->cdis > PET_SEEK_DIST); /* by default, move randomly */ mm[0] = mm[1] = mm[2] = mm[3] = 5; /* Look for an enemy */ if (get_enemy_target(m_ptr)) { convert_target_dir(m_ptr, mm); } else { /* Find the player if necessary */ if (avoid || lonely || distant) { /* Remember the leash length */ int dis = p_ptr->pet_follow_distance; /* Hack -- adjust follow distance temporarily */ if (p_ptr->pet_follow_distance > PET_SEEK_DIST) { p_ptr->pet_follow_distance = PET_SEEK_DIST; } /* Find the player */ (void)get_moves(m_idx, mm); /* Restore the leash */ p_ptr->pet_follow_distance = dis; } } } /* Friendly monster movement */ else if (!is_hostile(m_ptr)) { /* by default, move randomly */ mm[0] = mm[1] = mm[2] = mm[3] = 5; /* Look for an enemy */ if (get_enemy_target(m_ptr)) { convert_target_dir(m_ptr, mm); } else { /* Failure... we need to do something else */ } } /* Normal movement */ else if (stupid_monsters) { /* Logical moves */ (void)get_moves(m_idx, mm); } else { /* Logical moves, may do nothing */ if (!get_moves(m_idx, mm)) return; } /* Make the move */ take_move(m_idx, mm); } /* * Process all the "live" monsters, once per game turn. * * During each game turn, we scan through the list of all the "live" monsters, * (backwards, so we can excise any "freshly dead" monsters), energizing each * monster, and allowing fully energized monsters to move, attack, pass, etc. * * Note that monsters can never move in the monster array (except when the * "compact_monsters()" function is called by "dungeon()" or "save_player()"). * * This function is responsible for at least half of the processor time * on a normal system with a "normal" amount of monsters and a player doing * normal things. * * When the player is resting, virtually 90% of the processor time is spent * in this function, and its children, "process_monster()" and "make_move()". * * Most of the rest of the time is spent in "update_view()" and "lite_spot()", * especially when the player is running. * * Note the special "MFLAG_BORN" flag, which allows us to ignore "fresh" * monsters while they are still being "born". A monster is "fresh" only * during the turn in which it is created, and we use the "hack_m_idx" to * determine if the monster is yet to be processed during the current turn. * * Note the special "MFLAG_NICE" flag, which allows the player to get one * move before any "nasty" monsters get to use their spell attacks. * * Note that when the "knowledge" about the currently tracked monster * changes (flags, attacks, spells), we induce a redraw of the monster * recall window. */ void process_monsters(int min_energy) { int i; int fx, fy; bool test; monster_type *m_ptr; monster_race *r_ptr; int old_monster_race_idx; u32b old_r_flags1 = 0L; u32b old_r_flags2 = 0L; u32b old_r_flags3 = 0L; u32b old_r_flags4 = 0L; u32b old_r_flags5 = 0L; u32b old_r_flags6 = 0L; byte old_r_blows0 = 0; byte old_r_blows1 = 0; byte old_r_blows2 = 0; byte old_r_blows3 = 0; byte old_r_cast_inate = 0; byte old_r_cast_spell = 0; int old_total_friends = total_friends; s32b old_friend_align = friend_align; cave_type *c_ptr; /* Clear some variables */ total_friends = 0; total_friend_levels = 0; friend_align = 0; /* Clear monster fighting indicator */ p_ptr->state.mon_fight = FALSE; /* Memorize old race */ old_monster_race_idx = p_ptr->monster_race_idx; /* Acquire knowledge */ if (p_ptr->monster_race_idx) { /* Acquire current monster */ r_ptr = &r_info[p_ptr->monster_race_idx]; /* Memorize flags */ old_r_flags1 = r_ptr->r_flags[0]; old_r_flags2 = r_ptr->r_flags[1]; old_r_flags3 = r_ptr->r_flags[2]; old_r_flags4 = r_ptr->r_flags[3]; old_r_flags5 = r_ptr->r_flags[4]; old_r_flags6 = r_ptr->r_flags[5]; /* Memorize blows */ old_r_blows0 = r_ptr->r_blows[0]; old_r_blows1 = r_ptr->r_blows[1]; old_r_blows2 = r_ptr->r_blows[2]; old_r_blows3 = r_ptr->r_blows[3]; /* Memorize castings */ old_r_cast_inate = r_ptr->r_cast_inate; old_r_cast_spell = r_ptr->r_cast_spell; } /* Process the monsters (backwards) */ for (i = m_max - 1; i >= 1; i--) { /* Access the monster */ m_ptr = &m_list[i]; r_ptr = &r_info[m_ptr->r_idx]; /* Handle "leaving" */ if (p_ptr->state.leaving) break; /* Ignore "dead" monsters */ if (!m_ptr->r_idx) continue; /* Calculate "upkeep" for pets */ if (is_pet(m_ptr)) { total_friends++; total_friend_levels += r_ptr->hdice * 2; /* Determine pet alignment */ if (FLAG(r_ptr, RF_GOOD)) { friend_align += r_ptr->hdice * 2; } else if (FLAG(r_ptr, RF_EVIL)) { friend_align -= r_ptr->hdice * 2; } } /* Has the monster already moved? */ if (m_ptr->mflag & MFLAG_MOVE) continue; /* Make a move */ m_ptr->mflag |= (MFLAG_MOVE); /* Not enough energy to move */ if (m_ptr->energy < min_energy) continue; /* Use up "some" energy */ m_ptr->energy -= 100; /* Hack -- Require proximity */ if (m_ptr->cdis >= 100) continue; /* Access the location */ fx = m_ptr->fx; fy = m_ptr->fy; c_ptr = area(fx, fy); /* Assume no move */ test = FALSE; /* Allow more activity with pets around */ if (old_total_friends) { test = TRUE; } /* Handle "sensing radius" */ if (m_ptr->cdis <= r_ptr->aaf) { /* We can "sense" the player */ test = TRUE; } /* Handle "sight" and "aggravation" */ else if ((m_ptr->cdis <= MAX_SIGHT) && ((in_boundsp(fx, fy) && player_has_los_grid(parea(fx, fy))) || (FLAG(p_ptr, TR_AGGRAVATE)))) { /* We can "see" or "feel" the player */ test = TRUE; } /* * Hack -- Monsters can "smell" the player from far away * Note that most monsters have "aaf" of "20" or so */ else if ((area(p_ptr->px, p_ptr->py)->when == c_ptr->when) && (c_ptr->cost < MONSTER_FLOW_DEPTH) && (c_ptr->cost < r_ptr->aaf)) { /* We can "smell" the player */ test = TRUE; } /* Do nothing */ if (!test) continue; /* Save global index */ hack_m_idx = i; /* Process the monster */ process_monster(i); /* Hack -- notice death or departure */ if (!p_ptr->state.playing || p_ptr->state.is_dead) break; /* Notice leaving */ if (p_ptr->state.leaving) break; } /* Reset global index */ hack_m_idx = 0; /* Tracking a monster race (the same one we were before) */ if (p_ptr->monster_race_idx && (p_ptr->monster_race_idx == old_monster_race_idx)) { /* Acquire monster race */ r_ptr = &r_info[p_ptr->monster_race_idx]; /* Check for knowledge change */ if ((old_r_flags1 != r_ptr->r_flags[0]) || (old_r_flags2 != r_ptr->r_flags[1]) || (old_r_flags3 != r_ptr->r_flags[2]) || (old_r_flags4 != r_ptr->r_flags[3]) || (old_r_flags5 != r_ptr->r_flags[4]) || (old_r_flags6 != r_ptr->r_flags[5]) || (old_r_blows0 != r_ptr->r_blows[0]) || (old_r_blows1 != r_ptr->r_blows[1]) || (old_r_blows2 != r_ptr->r_blows[2]) || (old_r_blows3 != r_ptr->r_blows[3]) || (old_r_cast_inate != r_ptr->r_cast_inate) || (old_r_cast_spell != r_ptr->r_cast_spell)) { /* Window stuff */ p_ptr->window |= (PW_MONSTER); } } if (old_friend_align != friend_align) p_ptr->update |= (PU_BONUS); } /* * Clear 'moved' status from all monsters. * * Clear noise if appropriate??? */ void reset_monsters(void) { int i; monster_type *m_ptr; /* Process the monsters */ for (i = 1; i < m_max; i++) { /* Access the monster */ m_ptr = &m_list[i]; /* Monster is ready to go again */ m_ptr->mflag &= ~(MFLAG_MOVE); } #if 0 /* Clear the current noise after it is used to wake up monsters */ if (turn % 10 == 0) { total_wakeup_chance = 0L; add_wakeup_chance = 0; } #endif /* 0 */ } zangband/src/mind.c0000755000000000000000000003065410250356274013241 0ustar rootroot/* File: mind.c */ /* Purpose: Mindcrafter code */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* Level gained, cost, %fail, name */ mindcraft_power mindcraft_powers[MINDCRAFT_MAX] = { {1, 1, 15, "Neural Blast"}, {2, 1, 20, "Precognition"}, {3, 2, 25, "Minor Displacement"}, {7, 6, 35, "Major Displacement"}, {9, 7, 50, "Domination"}, {11, 7, 30, "Pulverise"}, {13, 12, 50, "Character Armour"}, {15, 12, 60, "Psychometry"}, {18, 10, 45, "Mind Wave"}, {23, 15, 50, "Adrenaline Channeling"}, {25, 10, 40, "Psychic Drain"}, {28, 20, 45, "Telekinetic Wave"} }; void mindcraft_info(char *p, int power) { int plev = p_ptr->lev; p[0] = 0; switch (power) { case MINDCRAFT_NEURAL_BLAST: { strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15); break; } case MINDCRAFT_PRECOGNITION: { break; } case MINDCRAFT_MINOR_DISPLACEMENT: { strnfmt(p, 80, " range %d", (plev < 40 ? 10 : plev + 2)); break; } case MINDCRAFT_MAJOR_DISPLACEMENT: { strnfmt(p, 80, " range %d", plev * 5); break; } case MINDCRAFT_DOMINATION: { break; } case MINDCRAFT_PULVERISE: { strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4)); break; } case MINDCRAFT_CHARACTER_ARMOUR: { strnfmt(p, 80, " dur %d", plev); break; } case MINDCRAFT_PSYCHOMETRY: { break; } case MINDCRAFT_MIND_WAVE: { strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1)); break; } case MINDCRAFT_ADRENALINE_CHANNELING: { strnfmt(p, 80, " dur 11-%d", plev + plev / 2 + 10); break; } case MINDCRAFT_PSYCHIC_DRAIN: { strnfmt(p, 80, " dam %dd6", plev / 2); break; } case MINDCRAFT_TELEKINETIC_WAVE: { strnfmt(p, 80, " dam %d", plev * (plev > 39 ? 4 : 3)); break; } } } /* * Allow user to choose a mindcrafter power. * * If a valid spell is chosen, saves it in '*sn' and returns TRUE * If the user hits escape, returns FALSE, and set '*sn' to -1 * If there are no legal choices, returns FALSE, and sets '*sn' to -2 * * The "prompt" should be "cast", "recite", or "study" * The "known" should be TRUE for cast/pray, FALSE for study * * nb: This function has a (trivial) display bug which will be obvious * when you run it. It's probably easy to fix but I haven't tried, * sorry. * * What bug? -SF- */ static int get_mindcraft_power(int *sn) { int i; int num = 0; int y = 1; int x = 20; int minfail; int plev = p_ptr->lev; int chance; int ask; char choice; char out_val[160]; char comment[80]; cptr p = "power"; mindcraft_power spell; bool flag; /* Assume cancelled */ *sn = (-1); /* Get the spell, if available */ if (repeat_pull(sn)) { /* Verify the spell */ if (mindcraft_powers[*sn].min_lev <= plev) { /* Success */ return (TRUE); } } /* Nothing chosen yet */ flag = FALSE; for (i = 0; i < MINDCRAFT_MAX; i++) { if (mindcraft_powers[i].min_lev <= plev) { num++; } } /* Build a prompt (accept all spells) */ (void)strnfmt(out_val, 78, "(%^ss %c-%c, ESC=exit) Use which %s? ", p, I2A(0), I2A(num - 1), p); /* Save the screen */ screen_save(); /* Display a list of spells */ prtf(x, y, ""); put_fstr(x + 5, y, "Name"); put_fstr(x + 35, y, "Lv Mana Fail Info"); /* Dump the spells */ for (i = 0; i < MINDCRAFT_MAX; i++) { /* Access the spell */ spell = mindcraft_powers[i]; if (spell.min_lev > plev) break; chance = spell.fail; /* Reduce failure rate by "effective" level adjustment */ chance -= 3 * (plev - spell.min_lev); /* Reduce failure rate by INT/WIS adjustment */ chance -= adj_mag_stat[p_ptr->stat[mp_ptr->spell_stat].ind] - 3; /* Not enough mana to cast */ if (spell.mana_cost > p_ptr->csp) { chance += 5 * (spell.mana_cost - p_ptr->csp); } /* Extract the minimum failure rate */ minfail = adj_mag_fail[p_ptr->stat[mp_ptr->spell_stat].ind]; /* Minimum failure rate */ if (chance < minfail) chance = minfail; /* Stunning makes spells harder */ if (p_ptr->tim.stun > 50) chance += 25; else if (p_ptr->tim.stun) chance += 15; /* Always a 5 percent chance of working */ if (chance > 95) chance = 95; /* Get info */ mindcraft_info(comment, i); /* Dump the spell --(-- */ prtf(x, y + i + 1, " %c) %-30s%2d %4d %3d%%%s", I2A(i), spell.name, spell.min_lev, spell.mana_cost, chance, comment); } /* Clear the bottom line */ prtf(x, y + i + 1, ""); /* Get a spell from the user */ while (!flag && get_com(out_val, &choice)) { /* Note verify */ ask = isupper(choice); /* Lowercase */ if (ask) choice = tolower(choice); /* Extract request */ i = (islower(choice) ? A2I(choice) : -1); /* Totally Illegal */ if ((i < 0) || (i >= num)) { bell("Illegal mindcrafter power choice!"); continue; } /* Save the spell index */ spell = mindcraft_powers[i]; /* Verify it */ if (ask) { /* Belay that order */ if (!get_check("Use %s? ", mindcraft_powers[i].name)) continue; } /* Stop the loop */ flag = TRUE; } /* Restore the screen */ screen_load(); /* Show choices */ /* Update */ p_ptr->window |= (PW_SPELL); /* Window stuff */ window_stuff(); /* Abort if needed */ if (!flag) return (FALSE); /* Save the choice */ (*sn) = i; repeat_push(*sn); /* Success */ return (TRUE); } /* * do_cmd_cast calls this function if the player's class * is 'mindcrafter'. */ static bool cast_mindcrafter_spell(int spell) { int b; int dir; int plev = p_ptr->lev; /* spell code */ switch (spell) { case MINDCRAFT_NEURAL_BLAST: /* Mindblast */ if (!get_aim_dir(&dir)) return FALSE; if (randint1(100) < plev * 2) (void)fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15))); else (void)fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0); break; case MINDCRAFT_PRECOGNITION: if (plev > 44) wiz_lite(); else if (plev > 19) map_area(); if (plev < 30) { b = detect_monsters_normal(); if (plev > 14) b |= detect_monsters_invis(); if (plev > 4) { b |= detect_traps(TRUE); b |= detect_doors(); } } else { b = detect_all(); } if ((plev > 24) && (plev < 40)) { (void)inc_tim_esp(plev); } if (!b) msgf("You feel safe."); break; case MINDCRAFT_MINOR_DISPLACEMENT: /* Minor displace */ if (plev < 40) { teleport_player(10); } else { msgf("You open a dimensional gate. Choose a destination."); return dimension_door(); } break; case MINDCRAFT_MAJOR_DISPLACEMENT: /* Major displace */ if (plev > 29) (void)banish_monsters(plev); teleport_player(plev * 5); break; case MINDCRAFT_DOMINATION: /* Domination */ if (plev < 30) { if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_DOMINATION, dir, plev, 0); } else { (void)charm_monsters(plev * 2); } break; case MINDCRAFT_PULVERISE: /* Fist of Force --- not 'true' TK */ if (!get_aim_dir(&dir)) return FALSE; (void)fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8), (plev > 20 ? (plev - 20) / 8 + 1 : 0)); break; case MINDCRAFT_CHARACTER_ARMOUR: /* Character Armour */ (void)inc_shield(plev); if (plev > 16) (void)inc_oppose_acid(plev); if (plev > 20) (void)inc_oppose_fire(plev); if (plev > 24) (void)inc_oppose_cold(plev); if (plev > 28) (void)inc_oppose_elec(plev); if (plev > 32) (void)inc_oppose_pois(plev); break; case MINDCRAFT_PSYCHOMETRY: /* Psychometry */ if (plev < 25) return psychometry(); else return ident_spell(); case MINDCRAFT_MIND_WAVE: /* Mindwave */ msgf("Mind-warping forces emanate from your brain!"); if (plev < 25) (void)project(0, 2 + plev / 10, p_ptr->px, p_ptr->py, (plev * 3) / 2, GF_PSI, PROJECT_KILL); else (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1)); break; case MINDCRAFT_ADRENALINE_CHANNELING: /* Adrenaline */ (void)clear_afraid(); (void)clear_stun(); /* * Only heal when Adrenalin Channeling is not active. We check * that by checking if the player isn't fast and 'heroed' atm. */ if (!p_ptr->tim.fast || !(p_ptr->tim.hero || p_ptr->tim.shero)) { (void)hp_player(plev); } b = 10 + randint1((plev * 3) / 2); if (plev < 35) (void)inc_hero(b); else (void)inc_shero(b); /* Haste */ (void)inc_fast(b); break; case MINDCRAFT_PSYCHIC_DRAIN: /* Psychic Drain */ if (!get_aim_dir(&dir)) return FALSE; b = damroll(plev / 2, 6); /* This is always a radius-0 ball now */ if (fire_ball(GF_PSI_DRAIN, dir, b, 0)) p_ptr->energy -= randint1(150); break; case MINDCRAFT_TELEKINETIC_WAVE: /* Telekinesis */ msgf ("A wave of pure physical force radiates out from your body!"); (void)project(0, 3 + plev / 10, p_ptr->px, p_ptr->py, plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS, PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID); break; default: msgf("Unknown Mindcrafter power!"); } return TRUE; } /* * do_cmd_cast calls this function if the player's class * is 'mindcrafter'. */ void do_cmd_mindcraft(void) { int n = 0; int chance; int minfail; int plev = p_ptr->lev; int old_csp = p_ptr->csp; mindcraft_power spell; bool cast; /* not if confused */ if (p_ptr->tim.confused) { msgf("You are too confused!"); return; } /* get power */ if (!get_mindcraft_power(&n)) return; spell = mindcraft_powers[n]; /* Verify "dangerous" spells */ if (spell.mana_cost > p_ptr->csp) { /* Warning */ msgf("You do not have enough mana to use this power."); /* Verify */ if (!get_check("Attempt it anyway? ")) return; } /* Spell failure chance */ chance = spell.fail; /* Reduce failure rate by "effective" level adjustment */ chance -= 3 * (plev - spell.min_lev); /* Reduce failure rate by INT/WIS adjustment */ chance -= adj_mag_stat[p_ptr->stat[mp_ptr->spell_stat].ind] - 3; /* Not enough mana to cast */ if (spell.mana_cost > p_ptr->csp) { chance += 5 * (spell.mana_cost - p_ptr->csp); } /* Extract the minimum failure rate */ minfail = adj_mag_fail[p_ptr->stat[mp_ptr->spell_stat].ind]; /* Minimum failure rate */ if (chance < minfail) chance = minfail; /* Stunning makes spells harder */ if (p_ptr->tim.stun > 50) chance += 25; else if (p_ptr->tim.stun) chance += 15; /* Always a 5 percent chance of working */ if (chance > 95) chance = 95; /* Failed spell */ if (randint0(100) < chance) { if (flush_failure) flush(); msgf("You failed to concentrate hard enough!"); sound(SOUND_FAIL); /* Backfire */ if (randint1(100) < (chance / 2)) { int b = randint1(100); if (b < 5) { msgf("Oh, no! Your mind has gone blank!"); (void)lose_all_info(); } else if (b < 15) { msgf("Weird visions seem to dance before your eyes..."); (void)inc_image(rand_range(5, 15)); } else if (b < 45) { msgf("Your brain is addled!"); (void)inc_confused(randint1(8)); } else if (b < 90) { (void)inc_stun(randint1(8)); } else { /* Mana storm */ msgf ("Your mind unleashes its power in an uncontrollable storm!"); (void)project(1, 2 + plev / 10, p_ptr->px, p_ptr->py, plev * 2, GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM); p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10)); } } } else { sound(SOUND_ZAP); /* Cast the spell */ cast = cast_mindcrafter_spell(n); if (!cast) return; } /* Take a turn */ p_ptr->state.energy_use = 100; /* Sufficient mana */ if (spell.mana_cost <= old_csp) { /* Use some mana */ p_ptr->csp -= spell.mana_cost; /* Limit */ if (p_ptr->csp < 0) p_ptr->csp = 0; } /* Over-exert the player */ else { int oops = spell.mana_cost - old_csp; /* No mana left */ p_ptr->csp = 0; p_ptr->csp_frac = 0; /* Message */ msgf("You faint from the effort!"); /* Hack -- Bypass free action */ (void)inc_paralyzed(randint1(5 * oops + 1)); /* Damage WIS (possibly permanently) */ if (randint0(100) < 50) { bool perm = (randint0(100) < 25); /* Message */ msgf("You have damaged your mind!"); /* Reduce constitution */ (void)dec_stat(A_WIS, rand_range(15, 25), perm); } } /* Redraw mana */ p_ptr->redraw |= (PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); } zangband/src/monster1.c0000755000000000000000000011450510250356274014060 0ustar rootroot/* File: monster1.c */ /* Purpose: describe monsters (using monster memory) */ /* * Copyright (c) 1989 James E. Wilson, Christopher J. Stuart * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Pronoun arrays, by gender. */ static cptr wd_he[3] = { "it", "he", "she" }; static cptr wd_his[3] = { "its", "his", "her" }; /* * Get the monster race in r_info[] */ monster_race *monst_race(int r_idx) { return (&r_info[r_idx]); } /* * Get the monster name from r_info[] */ cptr mon_race_name(const monster_race *r_ptr) { return (r_name + r_ptr->name); } /* * Does the monster name contain string str? */ bool mon_name_cont(const monster_race *r_ptr, cptr str) { return (strstr(mon_race_name(r_ptr), str) ? TRUE : FALSE); } /* * Pluralizer. Args(count, singular, plural) */ #define plural(c,s,p) \ (((c) == 1) ? (s) : (p)) /* * Determine if the "armor" is known * The higher the level, the fewer kills needed. */ static bool know_armour(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; s32b level = r_ptr->level; s32b kills = r_ptr->r_tkills; /* Normal monsters */ if (kills > 304 / (4 + level)) return (TRUE); /* Skip non-uniques */ if (!(FLAG(r_ptr, RF_UNIQUE))) return (FALSE); /* Unique monsters */ if (kills > 304 / (38 + (5 * level) / 4)) return (TRUE); /* Assume false */ return (FALSE); } /* * Determine if the "damage" of the given attack is known * the higher the level of the monster, the fewer the attacks you need, * the more damage an attack does, the more attacks you need */ static bool know_damage(int r_idx, int i) { monster_race *r_ptr = &r_info[r_idx]; s32b level = r_ptr->level; s32b a = r_ptr->r_blows[i]; s32b d1 = r_ptr->blow[i].d_dice; s32b d2 = r_ptr->blow[i].d_side; s32b d = d1 * d2; /* Normal monsters */ if ((4 + level) * a > 80 * d) return (TRUE); /* Skip non-uniques */ if (!(FLAG(r_ptr, RF_UNIQUE))) return (FALSE); /* Unique monsters */ if ((4 + level) * (2 * a) > 80 * d) return (TRUE); /* Assume false */ return (FALSE); } /* * Hack - a type for 'monster flags' */ typedef struct monster_flags monster_flags; struct monster_flags { u32b flags[7]; }; /* * Hack -- display monster information using "roff()" * * Note that there is now a compiler option to only read the monster * descriptions from the raw file when they are actually needed, which * saves about 60K of memory at the cost of disk access during monster * recall, which is optional to the user. * * This function should only be called with the cursor placed at the * left edge of the screen, on a cleared line, in which the recall is * to take place. One extra blank line is left after the recall. */ static void roff_mon_aux(int r_idx, int remem) { monster_race *r_ptr = &r_info[r_idx]; bool old = FALSE; bool sin = FALSE; /* Should all knowledge be displayed? */ bool know_all = cheat_know || (r_ptr->r_flags[6] & RF6_LIBRARY); int m, n, r; cptr p, q; int msex = 0; int speed = (ironman_nightmare) ? r_ptr->speed + 5 : r_ptr->speed; bool breath = FALSE; bool magic = FALSE; monster_flags mflags; monster_flags *mf_ptr = &mflags; int vn = 0; cptr vp[80]; monster_race save_mem; /* Descriptions */ char buf[2048]; /* Cheat -- Know everything */ if (know_all) { /* XXX XXX XXX */ /* Save the "old" memory (structure copy) */ save_mem = *r_ptr; /* Hack -- Maximal info */ r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR; /* Observe "maximal" attacks */ for (m = 0; m < 4; m++) { /* Examine "actual" blows */ if (r_ptr->blow[m].effect || r_ptr->blow[m].method) { /* Hack -- maximal observations */ r_ptr->r_blows[m] = MAX_UCHAR; } } /* Hack -- maximal drops */ r_ptr->r_drop_gold = r_ptr->r_drop_item = (((FLAG(r_ptr, RF_DROP_4D2)) ? 8 : 0) + ((FLAG(r_ptr, RF_DROP_3D2)) ? 6 : 0) + ((FLAG(r_ptr, RF_DROP_2D2)) ? 4 : 0) + ((FLAG(r_ptr, RF_DROP_1D2)) ? 2 : 0) + ((FLAG(r_ptr, RF_DROP_90)) ? 1 : 0) + ((FLAG(r_ptr, RF_DROP_60)) ? 1 : 0)); /* Hack -- but only "valid" drops */ if (FLAG(r_ptr, RF_ONLY_GOLD)) r_ptr->r_drop_item = 0; if (FLAG(r_ptr, RF_ONLY_ITEM)) r_ptr->r_drop_gold = 0; /* Hack -- observe many spells */ r_ptr->r_cast_inate = MAX_UCHAR; r_ptr->r_cast_spell = MAX_UCHAR; /* Hack -- know all the flags */ r_ptr->r_flags[0] = r_ptr->flags[0]; r_ptr->r_flags[1] = r_ptr->flags[1]; r_ptr->r_flags[2] = r_ptr->flags[2]; r_ptr->r_flags[3] = r_ptr->flags[3]; r_ptr->r_flags[4] = r_ptr->flags[4]; r_ptr->r_flags[5] = r_ptr->flags[5]; } /* Extract a gender (if applicable) */ if (FLAG(r_ptr, RF_FEMALE)) msex = 2; else if (FLAG(r_ptr, RF_MALE)) msex = 1; /* Obtain a copy of the "known" flags */ mf_ptr->flags[0] = (r_ptr->flags[0] & r_ptr->r_flags[0]); mf_ptr->flags[1] = (r_ptr->flags[1] & r_ptr->r_flags[1]); mf_ptr->flags[2] = (r_ptr->flags[2] & r_ptr->r_flags[2]); mf_ptr->flags[3] = (r_ptr->flags[3] & r_ptr->r_flags[3]); mf_ptr->flags[4] = (r_ptr->flags[4] & r_ptr->r_flags[4]); mf_ptr->flags[5] = (r_ptr->flags[5] & r_ptr->r_flags[5]); mf_ptr->flags[6] = (r_ptr->flags[6]); /* * Hack. All flags from flag[6] are known. But the swimming flag should * not be known until discovered. I suppose I can write it in one * line of code but then noone knows why I did so. * So if the monster is not known to be a swimmer */ if (!(r_ptr->r_flags[6] & RF6_CAN_SWIM)) { /* Take away the swimming flag */ mf_ptr->flags[6] &= ~(RF6_CAN_SWIM); } /* Assume some "obvious" flags */ COPY_FLAG(r_ptr, mf_ptr, RF_UNIQUE); COPY_FLAG(r_ptr, mf_ptr, RF_QUESTOR); COPY_FLAG(r_ptr, mf_ptr, RF_MALE); COPY_FLAG(r_ptr, mf_ptr, RF_FEMALE); /* Assume some "creation" flags */ COPY_FLAG(r_ptr, mf_ptr, RF_CHAR_MIMIC); COPY_FLAG(r_ptr, mf_ptr, RF_FRIENDS); COPY_FLAG(r_ptr, mf_ptr, RF_ESCORT); COPY_FLAG(r_ptr, mf_ptr, RF_ESCORTS); /* Killing a monster reveals some properties */ if (r_ptr->r_tkills || know_all) { /* Know "race" flags */ COPY_FLAG(r_ptr, mf_ptr, RF_ORC); COPY_FLAG(r_ptr, mf_ptr, RF_TROLL); COPY_FLAG(r_ptr, mf_ptr, RF_GIANT); COPY_FLAG(r_ptr, mf_ptr, RF_DRAGON); COPY_FLAG(r_ptr, mf_ptr, RF_DEMON); COPY_FLAG(r_ptr, mf_ptr, RF_UNDEAD); COPY_FLAG(r_ptr, mf_ptr, RF_EVIL); COPY_FLAG(r_ptr, mf_ptr, RF_GOOD); COPY_FLAG(r_ptr, mf_ptr, RF_ANIMAL); COPY_FLAG(r_ptr, mf_ptr, RF_AMBERITE); /* Know 'quantum' flag */ COPY_FLAG(r_ptr, mf_ptr, RF_QUANTUM); /* Know "forced" flags */ COPY_FLAG(r_ptr, mf_ptr, RF_FORCE_DEPTH); COPY_FLAG(r_ptr, mf_ptr, RF_FORCE_MAXHP); } /* Treat uniques differently */ if (FLAG(mf_ptr, RF_UNIQUE)) { /* Hack -- Determine if the unique is "dead" */ bool dead = (r_ptr->max_num == 0) ? TRUE : FALSE; /* We've been killed... */ if (r_ptr->r_deaths) { /* Killed ancestors */ roff("%^s has slain %d of your ancestors", wd_he[msex], r_ptr->r_deaths); /* But we've also killed it */ if (dead) { roff(", but you have avenged %s! ", plural(r_ptr->r_deaths, "him", "them")); } /* Unavenged (ever) */ else { roff(", who %s unavenged. ", plural(r_ptr->r_deaths, "remains", "remain")); } } /* Dead unique who never hurt us */ else if (dead) { roff(CLR_L_DARK "You have slain this foe. "); } } /* Not unique, but killed us */ else if (r_ptr->r_deaths) { /* Dead ancestors */ roff("%d of your ancestors %s been killed by this creature, ", r_ptr->r_deaths, plural(r_ptr->r_deaths, "has", "have")); /* Some kills this life */ if (r_ptr->r_pkills) { roff("and you have exterminated at least %d of the creatures. ", r_ptr->r_pkills); } /* Some kills past lives */ else if (r_ptr->r_tkills) { roff("and %s have exterminated at least %d of the creatures. ", "your ancestors", r_ptr->r_tkills); } /* No kills */ else { roff(CLR_RED "and %s is not ever known to have been defeated. ", wd_he[msex]); } } /* Normal monsters */ else { /* Killed some this life */ if (r_ptr->r_pkills) { roff("You have killed at least %d of these creatures. ", r_ptr->r_pkills); } /* Killed some last life */ else if (r_ptr->r_tkills) { roff("Your ancestors have killed at least %d of these creatures. ", r_ptr->r_tkills); } /* Killed none */ else { roff("No battles to the death are recalled. "); } } #ifdef DELAY_LOAD_R_TEXT int fd; /* Build the filename */ path_make(buf, ANGBAND_DIR_DATA, "r_info.raw"); /* Open the "raw" file */ fd = fd_open(buf, O_RDONLY); /* Use file */ if (fd >= 0) { huge pos; /* Starting position */ pos = r_ptr->text; /* Additional offsets */ pos += r_head->head_size; pos += r_head->info_size; pos += r_head->name_size; /* Seek */ (void)fd_seek(fd, pos); /* Read a chunk of data */ (void)fd_read(fd, buf, 2048); /* Close it */ (void)fd_close(fd); } #else /* Simple method */ strcpy(buf, r_text + r_ptr->text); #endif /* Dump it */ roff(buf); roff(" "); /* Nothing yet */ old = FALSE; /* Describe location */ if (r_ptr->level == 0) { roff("%^s lives in the town", wd_he[msex]); old = TRUE; } else if (r_ptr->r_tkills || know_all) { roff(CLR_SLATE "%^s is ", wd_he[msex]); if (r_ptr->r_tkills * r_ptr->rarity >= 30 || know_all) { if (r_ptr->rarity < 2) roff(CLR_SLATE "very common"); else if (r_ptr->rarity < 4) roff(CLR_SLATE "common"); else if (r_ptr->rarity < 8) roff(CLR_SLATE "uncommon"); else if (r_ptr->rarity < 16) roff(CLR_SLATE "rare"); else roff(CLR_SLATE "very rare"); } else { roff(CLR_SLATE "normally found"); } if (FLAG(mf_ptr, RF_AQUATIC)) { roff(CLR_SLATE " in water"); } if (depth_in_feet) { roff(CLR_SLATE " at depths of %d feet", r_ptr->level * 50); } else { roff(CLR_SLATE " on dungeon level %d", r_ptr->level); } old = TRUE; } /* Describe movement */ if (TRUE) { /* Introduction */ if (old) { roff(" and "); } else { roff("%^s ", wd_he[msex]); old = TRUE; } if (FLAG(mf_ptr, RF_CAN_FLY)) { roff("flies"); } else roff("moves"); /* Random-ness */ if ((FLAG(mf_ptr, RF_RAND_50)) || (FLAG(mf_ptr, RF_RAND_25))) { /* Adverb */ if ((FLAG(mf_ptr, RF_RAND_50)) && (FLAG(mf_ptr, RF_RAND_25))) { roff(" extremely"); } else if (FLAG(mf_ptr, RF_RAND_50)) { roff(" somewhat"); } else if (FLAG(mf_ptr, RF_RAND_25)) { roff(" a bit"); } /* Adjective */ roff(" erratically"); /* Hack -- Occasional conjunction */ if (speed != 110) roff(" and"); } /* Speed */ if (speed > 110) { if (speed > 130) roff(CLR_GREEN " incredibly"); else if (speed > 120) roff(CLR_GREEN " very"); roff(CLR_GREEN " quickly"); } else if (speed < 110) { if (speed < 90) roff(CLR_GREEN " incredibly"); else if (speed < 100) roff(CLR_GREEN " very"); roff(CLR_GREEN " slowly"); } else { roff(CLR_GREEN " at normal speed"); } } /* The code above includes "attack speed" */ if (FLAG(mf_ptr, RF_NEVER_MOVE)) { /* Introduce */ if (old) { roff(", but "); } else { roff("%^s ", wd_he[msex]); old = TRUE; } /* Describe */ roff("does not deign to chase intruders"); } /* End this sentence */ if (old) { roff(". "); old = FALSE; } /* Describe experience if known */ if (r_ptr->r_tkills || know_all) { /* Introduction */ if (FLAG(mf_ptr, RF_UNIQUE)) { roff("Killing this"); } else { roff("A kill of this"); } /* Describe the "quality" */ if (FLAG(mf_ptr, RF_XXX_1)) roff(CLR_L_BLUE " some property"); if (FLAG(mf_ptr, RF_ANIMAL)) roff(CLR_L_BLUE " natural"); if (FLAG(mf_ptr, RF_EVIL)) roff(CLR_L_BLUE " evil"); if (FLAG(mf_ptr, RF_GOOD)) roff(CLR_L_BLUE " good"); if (FLAG(mf_ptr, RF_UNDEAD)) roff(CLR_L_BLUE " undead"); /* Describe the "race" */ if (FLAG(mf_ptr, RF_DRAGON)) roff(CLR_L_BLUE " dragon"); else if (FLAG(mf_ptr, RF_DEMON)) roff(CLR_L_BLUE " demon"); else if (FLAG(mf_ptr, RF_GIANT)) roff(CLR_L_BLUE " giant"); else if (FLAG(mf_ptr, RF_TROLL)) roff(CLR_L_BLUE " troll"); else if (FLAG(mf_ptr, RF_ORC)) roff(CLR_L_BLUE " orc"); else if (FLAG(mf_ptr, RF_AMBERITE)) roff(CLR_L_BLUE " Amberite"); else if (FLAG(mf_ptr, RF_QUANTUM)) roff(CLR_L_BLUE " quantum creature"); else roff(" creature"); /* Group some variables */ if (TRUE) { s32b new_exp, new_exp_frac; int i; /* Get the xp for a kill */ exp_for_kill(r_ptr, &new_exp, &new_exp_frac); /* calculate the fractional exp part scaled by 100, */ /* must use long arithmetic to avoid overflow */ new_exp_frac = (((long)new_exp_frac + 0x10000L / 500) * 100) / 0x10000L; /* Mention the experience */ roff(" is worth %ld.%02ld point%s", (long)new_exp, (long)new_exp_frac, (((new_exp == 1) && (new_exp_frac == 0)) ? "" : "s")); /* Take account of annoying English */ p = "th"; i = p_ptr->lev % 10; if ((p_ptr->lev / 10) == 1) /* nothing */ ; else if (i == 1) p = "st"; else if (i == 2) p = "nd"; else if (i == 3) p = "rd"; /* Take account of "leading vowels" in numbers */ q = ""; i = p_ptr->lev; if ((i == 8) || (i == 11) || (i == 18)) q = "n"; /* Mention the dependance on the player's level */ roff(" for a%s %lu%s level character. ", q, (long)i, p); } } if ((FLAG(mf_ptr, RF_AURA_FIRE)) && (FLAG(mf_ptr, RF_AURA_ELEC))) { roff(CLR_YELLOW "%^s is surrounded by flames and electricity. ", wd_he[msex]); } else if ((FLAG(mf_ptr, RF_AURA_COLD)) && (FLAG(mf_ptr, RF_AURA_ELEC))) { roff(CLR_YELLOW "%^s is surrounded by ice and electricity. ", wd_he[msex]); } else if (FLAG(mf_ptr, RF_AURA_FIRE)) { roff(CLR_YELLOW "%^s is surrounded by flames. ", wd_he[msex]); } else if (FLAG(mf_ptr, RF_AURA_COLD)) { roff(CLR_YELLOW "%^s is surrounded by ice. ", wd_he[msex]); } else if (FLAG(mf_ptr, RF_AURA_ELEC)) { roff(CLR_YELLOW "%^s is surrounded by electricity. ", wd_he[msex]); } if (FLAG(mf_ptr, RF_REFLECTING)) { roff(CLR_YELLOW "%^s reflects bolt spells. ", wd_he[msex]); } /* Describe escorts */ if ((FLAG(mf_ptr, RF_ESCORT)) || (FLAG(mf_ptr, RF_ESCORTS))) { roff("%^s usually appears with escorts. ", wd_he[msex]); } /* Describe friends */ else if (FLAG(mf_ptr, RF_FRIENDS)) { roff("%^s usually appears in groups. ", wd_he[msex]); } else if (FLAG(mf_ptr, RF_CHAR_MIMIC)) { roff("%^s is a mimic. ", wd_he[msex]); } /* Collect inate attacks */ if (FLAG(mf_ptr, RF_SHRIEK)) vp[vn++] = "shriek for help"; if (FLAG(mf_ptr, RF_ELDRITCH_HORROR)) vp[vn++] = "blast your sanity"; if (FLAG(mf_ptr, RF_ROCKET)) vp[vn++] = "shoot a rocket"; if (FLAG(mf_ptr, RF_ARROW)) vp[vn++] = "fire arrows"; /* Describe inate attacks */ if (vn) { /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" may "); else if (n < vn - 1) roff(", "); else roff(" or "); /* Dump */ roff(CLR_L_RED "%s", vp[n]); } /* End */ roff(". "); } /* Collect breaths */ vn = 0; if (FLAG(mf_ptr, RF_BR_ACID)) vp[vn++] = "acid"; if (FLAG(mf_ptr, RF_BR_ELEC)) vp[vn++] = "lightning"; if (FLAG(mf_ptr, RF_BR_FIRE)) vp[vn++] = "fire"; if (FLAG(mf_ptr, RF_BR_COLD)) vp[vn++] = "frost"; if (FLAG(mf_ptr, RF_BR_POIS)) vp[vn++] = "poison"; if (FLAG(mf_ptr, RF_BR_NETH)) vp[vn++] = "nether"; if (FLAG(mf_ptr, RF_BR_LITE)) vp[vn++] = "light"; if (FLAG(mf_ptr, RF_BR_DARK)) vp[vn++] = "darkness"; if (FLAG(mf_ptr, RF_BR_CONF)) vp[vn++] = "confusion"; if (FLAG(mf_ptr, RF_BR_SOUN)) vp[vn++] = "sound"; if (FLAG(mf_ptr, RF_BR_CHAO)) vp[vn++] = "chaos"; if (FLAG(mf_ptr, RF_BR_DISE)) vp[vn++] = "disenchantment"; if (FLAG(mf_ptr, RF_BR_NEXU)) vp[vn++] = "nexus"; if (FLAG(mf_ptr, RF_BR_TIME)) vp[vn++] = "time"; if (FLAG(mf_ptr, RF_BR_INER)) vp[vn++] = "inertia"; if (FLAG(mf_ptr, RF_BR_GRAV)) vp[vn++] = "gravity"; if (FLAG(mf_ptr, RF_BR_SHAR)) vp[vn++] = "shards"; if (FLAG(mf_ptr, RF_BR_PLAS)) vp[vn++] = "plasma"; if (FLAG(mf_ptr, RF_BR_WALL)) vp[vn++] = "force"; if (FLAG(mf_ptr, RF_BR_MANA)) vp[vn++] = "mana"; if (FLAG(mf_ptr, RF_BR_NUKE)) vp[vn++] = "toxic waste"; if (FLAG(mf_ptr, RF_BR_DISI)) vp[vn++] = "disintegration"; /* Describe breaths */ if (vn) { /* Note breath */ breath = TRUE; /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" may breathe "); else if (n < vn - 1) roff(", "); else roff(" or "); /* Dump */ roff(CLR_L_RED "%s", vp[n]); } } /* Collect spells */ vn = 0; if (FLAG(mf_ptr, RF_BA_ACID)) vp[vn++] = "produce acid balls"; if (FLAG(mf_ptr, RF_BA_ELEC)) vp[vn++] = "produce lightning balls"; if (FLAG(mf_ptr, RF_BA_FIRE)) vp[vn++] = "produce fire balls"; if (FLAG(mf_ptr, RF_BA_COLD)) vp[vn++] = "produce frost balls"; if (FLAG(mf_ptr, RF_BA_POIS)) vp[vn++] = "produce poison balls"; if (FLAG(mf_ptr, RF_BA_NETH)) vp[vn++] = "produce nether balls"; if (FLAG(mf_ptr, RF_BA_WATE)) vp[vn++] = "produce water balls"; if (FLAG(mf_ptr, RF_BA_NUKE)) vp[vn++] = "produce balls of radiation"; if (FLAG(mf_ptr, RF_BA_MANA)) vp[vn++] = "invoke mana storms"; if (FLAG(mf_ptr, RF_BA_DARK)) vp[vn++] = "invoke darkness storms"; if (FLAG(mf_ptr, RF_BA_CHAO)) vp[vn++] = "invoke raw Logrus"; if (FLAG(mf_ptr, RF_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom"; if (FLAG(mf_ptr, RF_DRAIN_MANA)) vp[vn++] = "drain mana"; if (FLAG(mf_ptr, RF_MIND_BLAST)) vp[vn++] = "cause mind blasting"; if (FLAG(mf_ptr, RF_BRAIN_SMASH)) vp[vn++] = "cause brain smashing"; if (FLAG(mf_ptr, RF_CAUSE_1)) vp[vn++] = "cause light wounds and cursing"; if (FLAG(mf_ptr, RF_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing"; if (FLAG(mf_ptr, RF_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing"; if (FLAG(mf_ptr, RF_CAUSE_4)) vp[vn++] = "cause mortal wounds"; if (FLAG(mf_ptr, RF_BO_ACID)) vp[vn++] = "produce acid bolts"; if (FLAG(mf_ptr, RF_BO_ELEC)) vp[vn++] = "produce lightning bolts"; if (FLAG(mf_ptr, RF_BO_FIRE)) vp[vn++] = "produce fire bolts"; if (FLAG(mf_ptr, RF_BO_COLD)) vp[vn++] = "produce frost bolts"; if (FLAG(mf_ptr, RF_BO_POIS)) vp[vn++] = "produce poison bolts"; if (FLAG(mf_ptr, RF_BO_NETH)) vp[vn++] = "produce nether bolts"; if (FLAG(mf_ptr, RF_BO_WATE)) vp[vn++] = "produce water bolts"; if (FLAG(mf_ptr, RF_BO_MANA)) vp[vn++] = "produce mana bolts"; if (FLAG(mf_ptr, RF_BO_PLAS)) vp[vn++] = "produce plasma bolts"; if (FLAG(mf_ptr, RF_BO_ICEE)) vp[vn++] = "produce ice bolts"; if (FLAG(mf_ptr, RF_MISSILE)) vp[vn++] = "produce magic missiles"; if (FLAG(mf_ptr, RF_SCARE)) vp[vn++] = "terrify"; if (FLAG(mf_ptr, RF_BLIND)) vp[vn++] = "blind"; if (FLAG(mf_ptr, RF_CONF)) vp[vn++] = "confuse"; if (FLAG(mf_ptr, RF_SLOW)) vp[vn++] = "slow"; if (FLAG(mf_ptr, RF_HOLD)) vp[vn++] = "paralyze"; if (FLAG(mf_ptr, RF_HASTE)) vp[vn++] = "haste-self"; if (FLAG(mf_ptr, RF_HEAL)) vp[vn++] = "heal-self"; if (FLAG(mf_ptr, RF_INVULNER)) vp[vn++] = "make invulnerable"; if (FLAG(mf_ptr, RF_BLINK)) vp[vn++] = "blink-self"; if (FLAG(mf_ptr, RF_TPORT)) vp[vn++] = "teleport-self"; if (FLAG(mf_ptr, RF_XXX3)) vp[vn++] = "do something"; if (FLAG(mf_ptr, RF_XXX4)) vp[vn++] = "do something"; if (FLAG(mf_ptr, RF_TELE_TO)) vp[vn++] = "teleport to"; if (FLAG(mf_ptr, RF_TELE_AWAY)) vp[vn++] = "teleport away"; if (FLAG(mf_ptr, RF_TELE_LEVEL)) vp[vn++] = "teleport level"; if (FLAG(mf_ptr, RF_XXX5)) vp[vn++] = "do something"; if (FLAG(mf_ptr, RF_DARKNESS)) vp[vn++] = "create darkness"; if (FLAG(mf_ptr, RF_TRAPS)) vp[vn++] = "create traps"; if (FLAG(mf_ptr, RF_FORGET)) vp[vn++] = "cause amnesia"; if (FLAG(mf_ptr, RF_RAISE_DEAD)) vp[vn++] = "raise dead"; if (FLAG(mf_ptr, RF_S_MONSTER)) vp[vn++] = "summon a monster"; if (FLAG(mf_ptr, RF_S_MONSTERS)) vp[vn++] = "summon monsters"; if (FLAG(mf_ptr, RF_S_KIN)) vp[vn++] = "summon aid"; if (FLAG(mf_ptr, RF_S_ANT)) vp[vn++] = "summon ants"; if (FLAG(mf_ptr, RF_S_SPIDER)) vp[vn++] = "summon spiders"; if (FLAG(mf_ptr, RF_S_HOUND)) vp[vn++] = "summon hounds"; if (FLAG(mf_ptr, RF_S_HYDRA)) vp[vn++] = "summon hydras"; if (FLAG(mf_ptr, RF_S_ANGEL)) vp[vn++] = "summon an angel"; if (FLAG(mf_ptr, RF_S_DEMON)) vp[vn++] = "summon a demon"; if (FLAG(mf_ptr, RF_S_UNDEAD)) vp[vn++] = "summon an undead"; if (FLAG(mf_ptr, RF_S_DRAGON)) vp[vn++] = "summon a dragon"; if (FLAG(mf_ptr, RF_S_HI_UNDEAD)) vp[vn++] = "summon Greater Undead"; if (FLAG(mf_ptr, RF_S_HI_DRAGON)) vp[vn++] = "summon Ancient Dragons"; if (FLAG(mf_ptr, RF_S_CYBER)) vp[vn++] = "summon Cyberdemons"; if (FLAG(mf_ptr, RF_S_AMBERITES)) vp[vn++] = "summon Lords of Amber"; if (FLAG(mf_ptr, RF_S_UNIQUE)) vp[vn++] = "summon Unique Monsters"; /* Describe spells */ if (vn) { /* Note magic */ magic = TRUE; /* Intro */ if (breath) { roff(" and is also"); } else { roff("%^s is", wd_he[msex]); } /* Verb Phrase */ roff(" magical, casting spells"); /* Adverb */ if (FLAG(mf_ptr, RF_SMART)) roff(CLR_ORANGE " intelligently"); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" which "); else if (n < vn - 1) roff(", "); else roff(" or "); /* Dump */ roff(CLR_L_RED "%s", vp[n]); } } /* End the sentence about inate/other spells */ if (breath || magic) { /* Total casting */ m = r_ptr->r_cast_inate + r_ptr->r_cast_spell; /* Average frequency */ n = (r_ptr->freq_inate + r_ptr->freq_spell) / 2; /* Describe the spell frequency */ if (m > 100) { roff("; 1 time in %d", 100 / n); } /* Guess at the frequency */ else if (m) { n = ((n + 9) / 10) * 10; roff("; about 1 time in %d", 100 / n); } /* End this sentence */ roff(". "); } /* Describe monster "toughness" */ if (know_armour(r_idx)) { /* Armor */ roff("%^s has an armor rating of %d", wd_he[msex], r_ptr->ac); /* Maximized hitpoints */ if (FLAG(mf_ptr, RF_FORCE_MAXHP)) { roff(" and a life rating of %d. ", r_ptr->hdice * r_ptr->hside); } /* Variable hitpoints */ else { roff(" and a life rating of %dd%d. ", r_ptr->hdice, r_ptr->hside); } } /* Collect special abilities. */ vn = 0; if (FLAG(mf_ptr, RF_CAN_SWIM)) vp[vn++] = "swim"; if (FLAG(mf_ptr, RF_OPEN_DOOR)) vp[vn++] = "open doors"; if (FLAG(mf_ptr, RF_BASH_DOOR)) vp[vn++] = "bash down doors"; if (FLAG(mf_ptr, RF_PASS_WALL)) vp[vn++] = "pass through walls"; if (FLAG(mf_ptr, RF_KILL_WALL)) vp[vn++] = "bore through walls"; if (FLAG(mf_ptr, RF_MOVE_BODY)) vp[vn++] = "push past weaker monsters"; if (FLAG(mf_ptr, RF_KILL_BODY)) vp[vn++] = "destroy weaker monsters"; if (FLAG(mf_ptr, RF_TAKE_ITEM)) vp[vn++] = "pick up objects"; if (FLAG(mf_ptr, RF_KILL_ITEM)) vp[vn++] = "destroy objects"; if (FLAG(mf_ptr, RF_LITE_1) || FLAG(mf_ptr, RF_LITE_2)) vp[vn++] = "light the dungeon"; /* Describe special abilities. */ if (vn) { /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" can "); else if (n < vn - 1) roff(", "); else roff(" and "); /* Dump */ roff(CLR_L_UMBER "%s", vp[n]); } /* End */ roff(". "); } /* Describe special abilities. */ if (FLAG(mf_ptr, RF_INVISIBLE)) { roff(CLR_L_BLUE "%^s is invisible. ", wd_he[msex]); } if (FLAG(mf_ptr, RF_COLD_BLOOD)) { roff("%^s is cold blooded. ", wd_he[msex]); } if (FLAG(mf_ptr, RF_EMPTY_MIND)) { roff("%^s is not detected by telepathy. ", wd_he[msex]); } if (FLAG(mf_ptr, RF_WEIRD_MIND)) { roff("%^s is rarely detected by telepathy. ", wd_he[msex]); } if (FLAG(mf_ptr, RF_MULTIPLY)) { roff(CLR_L_UMBER "%^s breeds explosively. ", wd_he[msex]); } if (FLAG(mf_ptr, RF_REGENERATE)) { roff("%^s regenerates quickly. ", wd_he[msex]); } /* Collect susceptibilities */ vn = 0; if (FLAG(mf_ptr, RF_HURT_ROCK)) vp[vn++] = "rock remover"; if (FLAG(mf_ptr, RF_HURT_LITE)) vp[vn++] = "bright light"; if (FLAG(mf_ptr, RF_HURT_FIRE)) vp[vn++] = "fire"; if (FLAG(mf_ptr, RF_HURT_COLD)) vp[vn++] = "cold"; /* Describe susceptibilities */ if (vn) { /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" is hurt by "); else if (n < vn - 1) roff(", "); else roff(" and "); /* Dump */ roff(CLR_YELLOW "%s", vp[n]); } /* End */ roff(". "); } /* Collect immunities */ vn = 0; if (FLAG(mf_ptr, RF_IM_ACID)) vp[vn++] = "acid"; if (FLAG(mf_ptr, RF_IM_ELEC)) vp[vn++] = "lightning"; if (FLAG(mf_ptr, RF_IM_FIRE)) vp[vn++] = "fire"; if (FLAG(mf_ptr, RF_IM_COLD)) vp[vn++] = "cold"; if (FLAG(mf_ptr, RF_IM_POIS)) vp[vn++] = "poison"; /* Describe immunities */ if (vn) { /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" resists "); else if (n < vn - 1) roff(", "); else roff(" and "); /* Dump */ roff(CLR_ORANGE "%s", vp[n]); } /* End */ roff(". "); } /* Collect resistances */ vn = 0; if (FLAG(mf_ptr, RF_RES_NETH)) vp[vn++] = "nether"; if (FLAG(mf_ptr, RF_RES_WATE)) vp[vn++] = "water"; if (FLAG(mf_ptr, RF_RES_PLAS)) vp[vn++] = "plasma"; if (FLAG(mf_ptr, RF_RES_NEXU)) vp[vn++] = "nexus"; if (FLAG(mf_ptr, RF_RES_DISE)) vp[vn++] = "disenchantment"; if ((FLAG(mf_ptr, RF_RES_TELE)) && !(FLAG(r_ptr, RF_UNIQUE))) vp[vn++] = "teleportation"; /* Describe resistances */ if (vn) { /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" resists "); else if (n < vn - 1) roff(", "); else roff(" and "); /* Dump */ roff(CLR_ORANGE "%s", vp[n]); } /* End */ roff(". "); } /* Collect non-effects */ vn = 0; if (FLAG(mf_ptr, RF_NO_STUN)) vp[vn++] = "stunned"; if (FLAG(mf_ptr, RF_NO_FEAR)) vp[vn++] = "frightened"; if (FLAG(mf_ptr, RF_NO_CONF)) vp[vn++] = "confused"; if (FLAG(mf_ptr, RF_NO_SLEEP)) vp[vn++] = "slept"; if ((FLAG(mf_ptr, RF_RES_TELE)) && (FLAG(r_ptr, RF_UNIQUE))) vp[vn++] = "teleported"; /* Describe non-effects */ if (vn) { /* Intro */ roff("%^s", wd_he[msex]); /* Scan */ for (n = 0; n < vn; n++) { /* Intro */ if (n == 0) roff(" cannot be "); else if (n < vn - 1) roff(", "); else roff(" or "); /* Dump */ roff(CLR_YELLOW "%s", vp[n]); } /* End */ roff(". "); } /* Do we know how aware it is? */ if ((((int)r_ptr->r_wake * (int)r_ptr->r_wake) > r_ptr->sleep) || (r_ptr->r_ignore == MAX_UCHAR) || (r_ptr->sleep == 0 && (r_ptr->r_tkills >= 10 || know_all))) { cptr act; if (r_ptr->sleep > 200) { act = "prefers to ignore"; } else if (r_ptr->sleep > 95) { act = "pays very little attention to"; } else if (r_ptr->sleep > 75) { act = "pays little attention to"; } else if (r_ptr->sleep > 45) { act = "tends to overlook"; } else if (r_ptr->sleep > 25) { act = "takes quite a while to see"; } else if (r_ptr->sleep > 10) { act = "takes a while to see"; } else if (r_ptr->sleep > 5) { act = "is fairly observant of"; } else if (r_ptr->sleep > 3) { act = "is observant of"; } else if (r_ptr->sleep > 1) { act = "is very observant of"; } else if (r_ptr->sleep > 0) { act = "is vigilant for"; } else { act = "is ever vigilant for"; } roff("%^s %s intruders, which %s may notice from %d feet. ", wd_he[msex], act, wd_he[msex], 10 * r_ptr->aaf); } /* Drops gold and/or items */ if (r_ptr->r_drop_gold || r_ptr->r_drop_item) { /* No "n" needed */ sin = FALSE; /* Intro */ roff("%^s may carry", wd_he[msex]); /* Count maximum drop */ n = MAX(r_ptr->r_drop_gold, r_ptr->r_drop_item); /* One drop (may need an "n") */ if (n == 1) { roff(" a"); sin = TRUE; } /* Two drops */ else if (n == 2) { roff(" one or two"); } /* Many drops */ else { roff(" up to %d", n); } /* Great */ if (FLAG(mf_ptr, RF_DROP_GREAT)) { p = " exceptional"; } /* Good (no "n" needed) */ else if (FLAG(mf_ptr, RF_DROP_GOOD)) { p = " good"; sin = FALSE; } /* Okay */ else { p = NULL; } /* Objects */ if (r_ptr->r_drop_item) { /* Handle singular "an" */ if (sin) roff("n"); sin = FALSE; /* Dump "object(s)" */ if (p) roff(p); roff(" object"); if (n != 1) roff("s"); /* Conjunction replaces variety, if needed for "gold" below */ p = " or"; } /* Treasures */ if (r_ptr->r_drop_gold) { /* Cancel prefix */ if (!p) sin = FALSE; /* Handle singular "an" */ if (sin) roff("n"); sin = FALSE; /* Dump "treasure(s)" */ if (p) roff(p); roff(" treasure"); if (n != 1) roff("s"); } /* End this sentence */ roff(". "); } /* Count the number of "known" attacks */ for (n = 0, m = 0; m < 4; m++) { /* Skip non-attacks */ if (!r_ptr->blow[m].method) continue; /* Count known attacks */ if (r_ptr->r_blows[m]) n++; } /* Examine (and count) the actual attacks */ for (r = 0, m = 0; m < 4; m++) { int method, effect, d1, d2; /* Skip non-attacks */ if (!r_ptr->blow[m].method) continue; /* Skip unknown attacks */ if (!r_ptr->r_blows[m]) continue; /* Extract the attack info */ method = r_ptr->blow[m].method; effect = r_ptr->blow[m].effect; d1 = r_ptr->blow[m].d_dice; d2 = r_ptr->blow[m].d_side; /* Acquire the method */ p = rbm_info[method].name; /* Default effect */ q = NULL; /* Acquire the effect */ switch (effect) { case RBE_HURT: { q = "attack"; break; } case RBE_POISON: { q = "poison"; break; } case RBE_UN_BONUS: { q = "disenchant"; break; } case RBE_UN_POWER: { q = "drain charges"; break; } case RBE_EAT_GOLD: { q = "steal gold"; break; } case RBE_EAT_ITEM: { q = "steal items"; break; } case RBE_EAT_FOOD: { q = "eat your food"; break; } case RBE_EAT_LITE: { q = "absorb light"; break; } case RBE_ACID: { q = "shoot acid"; break; } case RBE_ELEC: { q = "electrocute"; break; } case RBE_FIRE: { q = "burn"; break; } case RBE_COLD: { q = "freeze"; break; } case RBE_BLIND: { q = "blind"; break; } case RBE_CONFUSE: { q = "confuse"; break; } case RBE_TERRIFY: { q = "terrify"; break; } case RBE_PARALYZE: { q = "paralyze"; break; } case RBE_LOSE_STR: { q = "reduce strength"; break; } case RBE_LOSE_INT: { q = "reduce intelligence"; break; } case RBE_LOSE_WIS: { q = "reduce wisdom"; break; } case RBE_LOSE_DEX: { q = "reduce dexterity"; break; } case RBE_LOSE_CON: { q = "reduce constitution"; break; } case RBE_LOSE_CHR: { q = "reduce charisma"; break; } case RBE_LOSE_ALL: { q = "reduce all stats"; break; } case RBE_SHATTER: { q = "shatter"; break; } case RBE_EXP_10: { q = "lower experience (by 10d6+)"; break; } case RBE_EXP_20: { q = "lower experience (by 20d6+)"; break; } case RBE_EXP_40: { q = "lower experience (by 40d6+)"; break; } case RBE_EXP_80: { q = "lower experience (by 80d6+)"; break; } case RBE_DISEASE: { q = "disease"; break; } case RBE_TIME: { q = "distrupt the time continuum"; break; } case RBE_EXP_VAMP: { q = "drain life force"; break; } } /* Introduce the attack description */ if (!r) { roff("%^s can ", wd_he[msex]); } else if (r < n - 1) { roff(", "); } else { roff(" and "); } /* Hack -- force a method */ if (!p) p = "do something weird"; /* Describe the method */ roff(p); /* Describe the effect (if any) */ if (q) { /* Describe the attack type */ roff(" to "); roff(CLR_L_RED "%s", q); /* Describe damage (if known) */ if (d1 && d2 && know_damage(r_idx, m)) { /* Display the damage */ roff(" with damage"); roff(" %dd%d", d1, d2); } } /* Count the attacks as printed */ r++; } /* Finish sentence above */ if (r) { roff(". "); } /* Notice lack of attacks */ else if (FLAG(mf_ptr, RF_NEVER_BLOW)) { roff("%^s has no physical attacks. ", wd_he[msex]); } /* Or describe the lack of knowledge */ else { roff("Nothing is known about %s attack. ", wd_his[msex]); } /* * Notice "Quest" monsters, but only if you * already encountered the monster. */ if ((FLAG(mf_ptr, RF_QUESTOR)) && (r_ptr->r_sights)) { roff("You feel an intense desire to kill this monster... "); } /* All done */ roff("\n"); /* Cheat -- know everything */ if (know_all && (remem == 0)) { /* Hack -- restore memory */ COPY(r_ptr, &save_mem, monster_race); } } /* * Hack -- Display the "name" and "attr/chars" of a monster race */ void roff_mon_top(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; byte a1, a2; char c1, c2; /* Access the chars */ c1 = r_ptr->d_char; c2 = r_ptr->x_char; /* Access the attrs */ a1 = r_ptr->d_attr; a2 = r_ptr->x_attr; /* Hack -- fake monochrome */ if (!use_color) { a1 = TERM_WHITE; a2 = TERM_WHITE; } /* Clear the top line */ clear_msg(); /* Reset the cursor */ Term_gotoxy(0, 0); /* A title (use "The" for non-uniques) */ if (!(FLAG(r_ptr, RF_UNIQUE))) { roff("The "); } /* Dump the name */ roff(mon_race_name(r_ptr)); /* Append the "standard" attr/char info */ roff(" ('"); Term_addch(a1, c1); roff("')"); /* Append the "optional" attr/char info */ roff("/('"); Term_addch(a2, c2); roff("'):"); /* Wizards get extra info */ if (p_ptr->state.wizard) { roff(" (" CLR_L_BLUE "%d)", r_idx); } } /* * Hack -- describe the given monster race at the top of the screen */ void screen_roff_mon(int r_idx, int remember) { /* Flush messages */ message_flush(); /* Begin recall */ clear_row(1); /* Recall monster */ roff_mon_aux(r_idx, remember); /* Describe monster */ roff_mon_top(r_idx); } /* * Hack -- describe the given monster race in the current "term" window */ void display_roff_mon(int r_idx) { /* Erase the window */ clear_from(0); /* Begin recall */ Term_gotoxy(0, 1); /* Recall monster */ roff_mon_aux(r_idx, 0); /* Describe monster */ roff_mon_top(r_idx); } /* * Hack -- show a list of the visible monsters in the current "term" window */ void display_visible(void) { int i, y; char c1, c2; byte a1, a2; monster_race *r_ptr; /* Erase the window */ clear_from(0); /* Are we hallucinating? */ if (p_ptr->tim.image) { put_fstr(0, 10, CLR_VIOLET "Hallucinations"); return; } i = p_ptr->max_seen_r_idx; /* Show the list */ for (y = 0; y < Term->hgt; y++) { /* No more to display */ if (!i) return; /* Go to left of screen */ Term_gotoxy(0, y); /* Note we have assumed that r_info.txt has been sorted */ /* Access monster */ r_ptr = &r_info[i]; /* Access the chars */ c1 = r_ptr->d_char; c2 = r_ptr->x_char; /* Access the attrs */ a1 = r_ptr->d_attr; a2 = r_ptr->x_attr; /* Hack -- fake monochrome */ if (!use_color) { a1 = TERM_WHITE; a2 = TERM_WHITE; } /* Dump the name */ if (FLAG(r_ptr, RF_UNIQUE)) { roff(CLR_L_BLUE "%s", mon_race_name(r_ptr)); } else if (FLAG(r_ptr, RF_QUESTOR)) { roff(CLR_L_RED "%s", mon_race_name(r_ptr)); } else { roff("%s", mon_race_name(r_ptr)); } /* Append the "standard" attr/char info */ roff(" ('"); Term_addch(a1, c1); roff("')"); /* Append the "optional" attr/char info */ roff("/('"); Term_addch(a2, c2); roff("'):"); /* Wizards get extra info */ if (p_ptr->state.wizard) { roff(" (" CLR_L_BLUE "%d)", i); } /* Append count */ roff("[%d]", r_ptr->r_see); /* Look for the next one */ while (i > 0) { i--; if (r_info[i].r_see) { break; } } } } void set_friendly(monster_type *m_ptr) { m_ptr->smart |= SM_FRIENDLY; } void set_pet(monster_type *m_ptr) { m_ptr->smart |= SM_PET; } /* * Makes the monster hostile towards the player */ void set_hostile(monster_type *m_ptr) { m_ptr->smart &= ~SM_PET; m_ptr->smart &= ~SM_FRIENDLY; } /* * Anger the monster */ void anger_monster(monster_type *m_ptr) { if (!is_hostile(m_ptr)) { msgf("%^v gets angry!", MONSTER_FMT(m_ptr, 0)); set_hostile(m_ptr); chg_virtue(V_INDIVIDUALISM, 1); chg_virtue(V_HONOUR, -1); chg_virtue(V_JUSTICE, -1); chg_virtue(V_COMPASSION, -1); } } /* * Check if two monsters are enemies */ bool are_enemies(const monster_type *m_ptr, const monster_type *n_ptr) { const monster_race *r_ptr = &r_info[m_ptr->r_idx]; const monster_race *s_ptr = &r_info[n_ptr->r_idx]; /* Friendly vs. opposite aligned normal or pet */ if (((FLAG(r_ptr, RF_EVIL)) && (FLAG(s_ptr, RF_GOOD))) || ((FLAG(r_ptr, RF_GOOD)) && (FLAG(s_ptr, RF_EVIL)))) { return TRUE; } /* Hostile vs. non-hostile */ if (is_hostile(m_ptr) != is_hostile(n_ptr)) { return TRUE; } /* Default */ return FALSE; } /* * Is the monster "alive"? * * Used to determine the message to print for a killed monster. * ("dies", "destroyed") */ bool monster_living(const monster_race *r_ptr) { /* Non-living, undead, or demon */ if (FLAG(r_ptr, RF_DEMON) || FLAG(r_ptr, RF_UNDEAD) || FLAG(r_ptr, RF_NONLIVING)) return FALSE; else return TRUE; } /* * Shimmer monsters */ void change_shimmer(void) { int i; monster_type *m_ptr; monster_race *r_ptr; /* Shimmer multi-hued monsters */ for (i = 1; i < m_max; i++) { /* Access monster */ m_ptr = &m_list[i]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Access the monster race */ r_ptr = &r_info[m_ptr->r_idx]; /* Skip non-multi-hued monsters */ if (!FLAG(r_ptr, RF_ATTR_MULTI)) continue; /* Reset the shimmer flag */ p_ptr->change |= PC_SHIMMER; /* Redraw regardless */ lite_spot(m_ptr->fx, m_ptr->fy); } } /* * Repair monsters after detection. */ void change_repair(void) { int i; monster_type *m_ptr; /* Rotate detection flags */ for (i = 1; i < m_max; i++) { /* Access monster */ m_ptr = &m_list[i]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Nice monsters get mean */ m_ptr->mflag &= ~(MFLAG_NICE); /* Handle memorized monsters */ if (m_ptr->mflag & MFLAG_MARK) { /* Maintain detection */ if (m_ptr->mflag & MFLAG_SHOW) { /* Forget flag */ m_ptr->mflag &= ~(MFLAG_SHOW); /* Still need repairs */ p_ptr->change |= (PC_REPAIR); } /* Remove detection */ else { /* Forget flag */ m_ptr->mflag &= ~(MFLAG_MARK); /* Update the monster */ update_mon(i, FALSE); } } } } zangband/src/monster2.c0000755000000000000000000021013110250356274014051 0ustar rootroot/* File: monster2.c */ /* Purpose: misc code for monsters */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" cptr horror_desc[MAX_SAN_HORROR] = { "abominable", "abysmal", "appalling", "baleful", "blasphemous", "disgusting", "dreadful", "filthy", "grisly", "hideous", "hellish", "horrible", "infernal", "loathsome", "nightmarish", "repulsive", "sacrilegious", "terrible", "unclean", "unspeakable", }; cptr funny_desc[MAX_SAN_FUNNY] = { "silly", "hilarious", "absurd", "insipid", "ridiculous", "laughable", "ludicrous", "far-out", "groovy", "postmodern", "fantastic", "dadaistic", "cubistic", "cosmic", "awesome", "incomprehensible", "fabulous", "amazing", "incredible", "chaotic", "wild", "preposterous", }; cptr funny_comments[MAX_SAN_COMMENT] = { "Wow, cosmic, man!", "Rad!", "Groovy!", "Cool!", "Far out!" }; /* * Delete a monster by index. * * When a monster is deleted, all of its objects are deleted. */ void delete_monster_idx(int i) { int x, y; monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Get location */ y = m_ptr->fy; x = m_ptr->fx; /* Hack -- Reduce the racial counter */ r_ptr->cur_num--; /* Hack -- count the number of "reproducers" */ if (FLAG(r_ptr, RF_MULTIPLY)) num_repro--; /* Notice changes in lighting */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Decrement visibility count */ if (m_ptr->ml && !(m_ptr->smart & SM_MIMIC)) { update_mon_vis(m_ptr->r_idx, -1); } /* Hack -- remove target monster */ if (i == p_ptr->target_who) p_ptr->target_who = 0; /* Hack -- remove tracked monster */ if (i == p_ptr->health_who) health_track(0); /* Monster is gone */ if (in_bounds2(x, y)) { area(x, y)->m_idx = 0; } /* Delete objects */ delete_object_list(&m_ptr->hold_o_idx); /* Wipe the Monster */ (void)WIPE(m_ptr, monster_type); /* Count monsters */ m_cnt--; /* Visual update */ lite_spot(x, y); } /* * Delete the monster, if any, at a given location */ void delete_monster(int x, int y) { cave_type *c_ptr; /* Paranoia */ if (!in_bounds2(x, y)) return; /* Check the grid */ c_ptr = area(x, y); /* Delete the monster (if any) */ if (c_ptr->m_idx) delete_monster_idx(c_ptr->m_idx); } /* * Move an object from index i1 to index i2 in the object list */ static void compact_monsters_aux(int i1, int i2) { int y, x; cave_type *c_ptr; monster_type *m_ptr; /* Do nothing */ if (i1 == i2) return; /* Old monster */ m_ptr = &m_list[i1]; /* Location */ y = m_ptr->fy; x = m_ptr->fx; /* Cave grid */ c_ptr = area(x, y); /* Update the cave */ c_ptr->m_idx = i2; /* Hack -- Update the target */ if (p_ptr->target_who == i1) p_ptr->target_who = i2; /* Hack -- Update the health bar */ if (p_ptr->health_who == i1) health_track(i2); /* Structure copy */ COPY(&m_list[i2], &m_list[i1], monster_type); /* Wipe the hole */ (void)WIPE(&m_list[i1], monster_type); } /* * Compact and Reorder the monster list * * This function can be very dangerous, use with caution! * * When actually "compacting" monsters, we base the saving throw * on a combination of monster level, distance from player, and * current "desperation". * * After "compacting" (if needed), we "reorder" the monsters into a more * compact order, and we reset the allocation info, and the "live" array. */ void compact_monsters(int size) { int i, num, cnt; int cur_lev, cur_dis, chance; /* Message (only if compacting) */ if (size) msgf("Compacting monsters..."); /* Compact at least 'size' objects */ for (num = 0, cnt = 1; num < size; cnt++) { /* Get more vicious each iteration */ cur_lev = 5 * cnt; /* Get closer each iteration */ cur_dis = 5 * (20 - cnt); /* Check all the monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Paranoia -- skip "dead" monsters */ if (!m_ptr->r_idx) continue; /* Hack -- High level monsters start out "immune" */ if (r_ptr->level > cur_lev) continue; /* Ignore nearby monsters */ if ((cur_dis > 0) && (m_ptr->cdis < cur_dis)) continue; /* Saving throw chance */ chance = 90; /* Only compact "Quest" Monsters in emergencies */ if (FLAG(r_ptr, RF_QUESTOR) && (cnt < 1000)) chance = 100; /* Try not to compact Unique Monsters */ if (FLAG(r_ptr, RF_UNIQUE)) chance = 99; /* All monsters get a saving throw */ if (randint0(100) < chance) continue; /* Delete the monster */ delete_monster_idx(i); /* Count the monster */ num++; } } /* Excise dead monsters (backwards!) */ for (i = m_max - 1; i >= 1; i--) { /* Get the i'th monster */ monster_type *m_ptr = &m_list[i]; /* Hack - kill monsters out of bounds. */ if (!in_bounds2(m_ptr->fx, m_ptr->fy)) { delete_monster_idx(i); } /* Skip real monsters */ if (m_ptr->r_idx) continue; /* Move last monster into open hole */ compact_monsters_aux(m_max - 1, i); /* Compress "m_max" */ m_max--; } } /* * Delete/Remove all the monsters in the game * * This is an efficient method of simulating multiple calls to the * "delete_monster()" function, with no visual effects. */ void wipe_m_list(void) { int i, x, y; /* Delete all the monsters */ for (i = m_max - 1; i >= 1; i--) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Mega-Hack -- preserve Unique's XXX XXX XXX */ /* Hack -- Reduce the racial counter */ r_ptr->cur_num--; /* Clear seen list */ r_ptr->r_see = 0; /* Check to see if monster is accessable on map */ y = m_ptr->fy; x = m_ptr->fx; if (in_bounds2(x, y)) { /* Monster is gone */ area(x, y)->m_idx = 0; } /* Wipe the Monster */ (void)WIPE(m_ptr, monster_type); } /* Reset "m_max" */ m_max = 1; /* Reset "m_cnt" */ m_cnt = 0; /* Hack -- reset "reproducer" count */ num_repro = 0; /* Hack -- no more target */ p_ptr->target_who = 0; /* Hack -- no more tracking */ health_track(0); /* Hack -- reset "visible" counter */ p_ptr->max_seen_r_idx = 0; p_ptr->window |= PW_VISIBLE; } /* * Wipe monsters in region */ void wipe_monsters(int rg_idx) { int i; monster_type *m_ptr; /* Delete all the monsters */ for (i = 1; i < m_max; i++) { m_ptr = &m_list[i]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Enforce region */ if (m_ptr->region != rg_idx) continue; /* Delete the monster */ delete_monster_idx(i); } /* Compress the monster list */ compact_monsters(0); } /* * Acquires and returns the index of a "free" monster. * * This routine should almost never fail, but it *can* happen. */ s16b m_pop(void) { int i; /* Normal allocation */ if (m_max < z_info->m_max) { /* Access the next hole */ i = m_max; /* Expand the array */ m_max++; /* Count monsters */ m_cnt++; /* Return the index */ return (i); } /* Recycle dead monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr; /* Acquire monster */ m_ptr = &m_list[i]; /* Skip live monsters */ if (m_ptr->r_idx) continue; /* Count monsters */ m_cnt++; /* Use this monster */ return (i); } /* Warn the player (except during dungeon creation) */ if (character_dungeon) msgf("Too many monsters!"); /* Try not to crash */ return (0); } /* * Apply a "monster restriction function" to the "monster allocation table" */ void get_mon_num_prep(monster_hook_type monster_hook) { int i; /* Scan the allocation table */ for (i = 0; i < alloc_race_size; i++) { /* Get the entry */ alloc_entry *entry = &alloc_race_table[i]; /* Accept monsters which pass the restriction, if any */ /* * Hack - check for silly monsters here. * This makes more sense then adding the test to every * hook function. */ if ((!monster_hook || (*monster_hook) (entry->index)) && (silly_monsters || !FLAG(&r_info[entry->index], RF_SILLY))) { /* Accept this monster */ entry->prob2 = entry->prob1; } /* Do not use this monster */ else { /* Decline this monster */ entry->prob2 = 0; } } /* Success */ return; } /* * Are we allowed to place monsters of this race on this square? */ bool test_monster_square(cave_type *c_ptr, monster_race *r_ptr) { /* Permanent walls are out */ if (cave_perma_grid(c_ptr) && !cave_floor_grid(c_ptr)) return (FALSE); /* Nor on the Pattern */ if (cave_pattern_grid(c_ptr)) return (FALSE); /* Check to see if fields dissallow placement or movement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) { /* Cannot create */ return (FALSE); } /* Set the monster list */ switch (c_ptr->feat) { case FEAT_OCEAN_WATER: { if (!(FLAG(r_ptr, RF_WILD_OCEAN))) return (FALSE); /* Hack - no break */ } case FEAT_DEEP_WATER: { if (!((FLAG(r_ptr, RF_AQUATIC)) || (FLAG(r_ptr, RF_CAN_FLY)) || (FLAG(r_ptr, RF_CAN_SWIM)))) { return (FALSE); } /* Hack - no break */ } case FEAT_SHAL_WATER: { if (FLAG(r_ptr, RF_AURA_FIRE)) return (FALSE); return (TRUE); } case FEAT_DEEP_LAVA: case FEAT_SHAL_LAVA: { /* Immunity to fire is nice */ if (FLAG(r_ptr, RF_IM_FIRE)) return (TRUE); /* If we are cold - then we can't cross */ if (FLAG(r_ptr, RF_AURA_COLD)) return (FALSE); /* If we can't fly, then we can't cross */ if (!(FLAG(r_ptr, RF_CAN_FLY))) return (FALSE); break; } case FEAT_DEEP_ACID: case FEAT_SHAL_ACID: { /* Immunity to acid is nice */ if (FLAG(r_ptr, RF_IM_ACID)) return (TRUE); /* If we can't fly, then we can't cross */ if (!(FLAG(r_ptr, RF_CAN_FLY))) return (FALSE); break; } case FEAT_DEEP_SWAMP: case FEAT_SHAL_SWAMP: { /* Immunity to poison is nice */ if (FLAG(r_ptr, RF_IM_POIS)) return (TRUE); /* If we can't fly, then we can't cross */ if (!(FLAG(r_ptr, RF_CAN_FLY))) return (FALSE); break; } } /* Aquatic monster */ if ((FLAG(r_ptr, RF_AQUATIC)) && !(FLAG(r_ptr, RF_CAN_FLY))) { return FALSE; } /* Looks ok then */ return (TRUE); } /* * Should the monster be placed at this point in the wilderness? */ static bool test_monster_wild(wild_done_type *w_ptr, monster_race *r_ptr) { byte mon_wild; /* Are we a town or city? */ if (w_ptr->place) { /* Not a quest? */ if ((!place[w_ptr->place].quest_num) && (FLAG(r_ptr, RF_WILD_TOWN))) return TRUE; } /* Ocean? */ if (w_ptr->wild > WILD_SEA) { if (FLAG(r_ptr, RF_WILD_OCEAN)) return TRUE; return FALSE; } /* Shore */ if (w_ptr->info & WILD_INFO_WATER) { if (FLAG(r_ptr, RF_WILD_SHORE)) return TRUE; return FALSE; } /* Acid */ if (w_ptr->info & WILD_INFO_ACID) { /* Immunity to acid is nice */ if (FLAG(r_ptr, RF_IM_ACID)) return TRUE; /* If we can't fly, then we can't cross */ if (FLAG(r_ptr, RF_CAN_FLY)) return TRUE; return FALSE; } /* Lava */ if (w_ptr->info & WILD_INFO_LAVA) { /* Immunity to fire is nice */ if (FLAG(r_ptr, RF_IM_FIRE)) return TRUE; /* If we are cold - then we can't cross */ if (FLAG(r_ptr, RF_AURA_COLD)) return FALSE; /* If we can't fly, then we can't cross */ if (FLAG(r_ptr, RF_CAN_FLY)) return TRUE; return FALSE; } /* * Get type of wilderness. */ mon_wild = wild_gen_data[w_ptr->wild].rough_type; /* Test to see if the monster likes this terrain */ if (r_ptr->flags[7] & mon_wild) return TRUE; if (!mon_wild) { /* No other terrain - use grass */ if (FLAG(r_ptr, RF_WILD_GRASS)) return TRUE; } return FALSE; } /* * Filter the list of creatable monsters based on location */ static int filter_mon_loc(int x, int y) { wild_done_type *w_ptr; monster_race *r_ptr; place_type *pl_ptr = &place[p_ptr->place_num]; int level; int i; /* In the wilderness? */ if (!p_ptr->depth) { /* Point to wilderness block info */ w_ptr = &wild[y / 16][x / 16].done; /* Scan the allocation table */ for (i = 0; i < alloc_race_size; i++) { /* Get the entry */ alloc_entry *entry = &alloc_race_table[i]; /* Only bother checking monsters we would have created */ if (entry->prob2) { /* Get the race */ r_ptr = &r_info[entry->index]; /* Not allowed in this part of the wilderness? */ if (!test_monster_wild(w_ptr, r_ptr)) { entry->prob2 = 0; } } } /* The level of the monsters */ level = w_ptr->mon_gen; } else { /* In the dungeon */ /* Scan the allocation table */ for (i = 0; i < alloc_race_size; i++) { /* Get the entry */ alloc_entry *entry = &alloc_race_table[i]; /* Only bother checking monsters we would have created */ if (entry->prob2) { /* Get the race */ r_ptr = &r_info[entry->index]; /* Not a monster for this dungeon? */ if (!(r_ptr->flags[7] & (pl_ptr->dungeon->habitat))) { entry->prob2 = 0; } } } /* The level of the monsters */ level = base_level(); } return (level); } /* * Choose a monster race that seems "appropriate" to the given level * * This function uses the "prob2" field of the "monster allocation table", * and various local information, to calculate the "prob3" field of the * same table, which is then used to choose an "appropriate" monster, in * a relatively efficient manner. * * Note that "town" monsters will *only* be created in the town, and * "normal" monsters will *never* be created in the town, unless the * "level" is "modified", for example, by polymorph or summoning. * * There is a small chance (1/50) of "boosting" the given depth by * a small amount (up to ten levels), except in the town. * * It is (slightly) more likely to acquire a monster of the given level * than one of a lower level. This is done by choosing several monsters * appropriate to the given level and keeping the "hardest" one. * * Note that if no monsters are "appropriate", then this function will * fail, and return zero, but this should *almost* never happen. */ s16b get_mon_num(int level) { int i, p; int r_idx; long value1, value2, total; monster_race *r_ptr; alloc_entry *table = alloc_race_table; /* Boost the level */ if (level > 0) { /* Nightmare mode allows more out-of depth monsters */ if (ironman_nightmare && one_in_(NASTY_MON)) { /* What a bizarre calculation */ level = 1 + (level * MAX_DEPTH / randint1(MAX_DEPTH)); } else { int checks = 2; for ( ; checks > 0; checks--) { /* Occasional "nasty" monster */ if (one_in_(NASTY_MON)) { /* Boost the level */ level += 7; } } /* Luck gives occasional very out-of-depth monsters */ if ((FLAG(p_ptr, TR_STRANGE_LUCK)) && one_in_(13)) { level += randint1(one_in_(7) ? 40 : 10); } } } /* Reset total */ total = 0L; /* Process probabilities */ for (i = 0; i < alloc_race_size; i++) { /* Monsters are sorted by depth */ if (table[i].level > level) break; /* Default */ table[i].prob3 = 0; /* Access the "r_idx" of the chosen monster */ r_idx = table[i].index; /* Access the actual race */ r_ptr = &r_info[r_idx]; /* Hack -- "unique" monsters must be "unique" */ if ((FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_UNIQUE_7)) && (r_ptr->cur_num >= r_ptr->max_num)) { continue; } /* Hack -- don't create questors */ if (FLAG(r_ptr, RF_QUESTOR)) { continue; } /* Depth Monsters never appear out of depth */ if (FLAG(r_ptr, RF_FORCE_DEPTH) && (r_ptr->level > p_ptr->depth)) { continue; } /* Accept */ table[i].prob3 = table[i].prob2; /* Total */ total += table[i].prob3; } /* No legal monsters */ if (total <= 0) return (0); /* Pick a monster */ value1 = randint0(total); /* Power boost */ p = randint0(100); /* Try for a "better" monster once (50%) or twice (10%) */ if (p < 60) { value2 = randint0(total); /* Is it better? */ if (value2 > value1) { /* This hack works because the monster table is sorted by depth */ value1 = value2; } } /* Try for a "better" monster twice (10%) */ if (p < 10) { value2 = randint0(total); /* Is it better? */ if (value2 > value1) { /* This hack works because the monster table is sorted by depth */ value1 = value2; } } /* Find the monster */ for (i = 0; i < alloc_race_size; i++) { /* Found the entry */ if (value1 < table[i].prob3) { return (table[i].index); } /* Decrement */ value1 -= table[i].prob3; } msgf("Aborting - Could not generate a monster!!!! %d", total); /* Result */ return (0); } /* * Get a monster from the list using a filter function * at the given level */ s16b get_filter_mon_num(int level, monster_hook_type monster_hook) { s16b race; /* Apply the monster restriction */ get_mon_num_prep(monster_hook); /* Pick a race */ race = get_mon_num(level); /* Remove the monster restriction */ get_mon_num_prep(NULL); return (race); } /* * Build a string describing a monster in some way. * * We can correctly describe monsters based on their visibility. * We can force all monsters to be treated as visible or invisible. * We can build nominatives, objectives, possessives, or reflexives. * We can selectively pronominalize hidden, visible, or all monsters. * We can use definite or indefinite descriptions for hidden monsters. * We can use definite or indefinite descriptions for visible monsters. * * Pronominalization involves the gender whenever possible and allowed, * so that by cleverly requesting pronominalization / visibility, you * can get messages like "You hit someone. She screams in agony!". * * Reflexives are acquired by requesting Objective plus Possessive. * * If no m_ptr arg is given (?), the monster is assumed to be hidden, * unless the "Assume Visible" mode is requested. * Does this really work??? It looks like r_ptr is initialised even * if m_ptr is NULL. Perhaps this craziness can be removed. -SF- * * * If no r_ptr arg is given, it is extracted from m_ptr and r_info * If neither m_ptr nor r_ptr is given, the monster is assumed to * be neuter, singular, and hidden (unless "Assume Visible" is set), * in which case you may be in trouble... :-) * * I am assuming that no monster name is more than 70 characters long, * so that "char desc[80];" is sufficiently large for any result. * * Mode Flags: * 0x01 --> Objective (or Reflexive) * 0x02 --> Possessive (or Reflexive) * 0x04 --> Use indefinites for hidden monsters ("something") * 0x08 --> Use indefinites for visible monsters ("a kobold") * 0x10 --> Pronominalize hidden monsters * 0x20 --> Pronominalize visible monsters * 0x40 --> Assume the monster is hidden * 0x80 --> Assume the monster is visible * * Useful Modes: * 0x00 --> Full nominative name ("the kobold") or "it" * 0x04 --> Full nominative name ("the kobold") or "something" * 0x80 --> Genocide resistance name ("the kobold") * 0x88 --> Killing name ("a kobold") * 0x22 --> Possessive, genderized if visable ("his") or "its" * 0x23 --> Reflexive, genderized if visable ("himself") or "itself" */ void monster_desc(char *desc, const monster_type *m_ptr, int mode, int max) { cptr res; monster_race *r_ptr = &r_info[m_ptr->r_idx]; cptr name = mon_race_name(r_ptr); char silly_name[1024]; bool seen, pron; bool named = FALSE; int n; /* Are we hallucinating? (Idea from Nethack...) */ if (p_ptr->tim.image) { if (one_in_(2)) { if (!get_rnd_line("silly.txt", m_ptr->r_idx, silly_name)) named = TRUE; } if (!named) { monster_race *hallu_race; do { hallu_race = &r_info[randint1(z_info->r_max - 1)]; } while (FLAG(hallu_race, RF_UNIQUE)); strcpy(silly_name, mon_race_name(hallu_race)); } /* Better not strcpy it, or we could corrupt r_info... */ name = silly_name; } /* Can we "see" it (exists + forced, or visible + not unforced) */ seen = (m_ptr && ((mode & 0x80) || (!(mode & 0x40) && m_ptr->ml))); /* Sexed Pronouns (seen and allowed, or unseen and allowed) */ pron = (m_ptr && ((seen && (mode & 0x20)) || (!seen && (mode & 0x10)))); /* First, try using pronouns, or describing hidden monsters */ if (!seen || pron) { /* an encoding of the monster "sex" */ int kind = 0x00; /* Extract the gender (if applicable) */ if (FLAG(r_ptr, RF_FEMALE)) kind = 0x20; else if (FLAG(r_ptr, RF_MALE)) kind = 0x10; /* Ignore the gender (if desired) */ if (!m_ptr || !pron) kind = 0x00; /* Assume simple result */ res = "it"; /* Brute force: split on the possibilities */ switch (kind + (mode & 0x07)) { /*** Neuter, or unknown ***/ case 0x00: { res = "it"; break; } case 0x01: { res = "it"; break; } case 0x02: { res = "its"; break; } case 0x03: { res = "itself"; break; } case 0x04: { res = "something"; break; } case 0x05: { res = "something"; break; } case 0x06: { res = "something's"; break; } case 0x07: { res = "itself"; break; } /*** Male (assume human if vague) ***/ case 0x10: { res = "he"; break; } case 0x11: { res = "him"; break; } case 0x12: { res = "his"; break; } case 0x13: { res = "himself"; break; } case 0x14: { res = "someone"; break; } case 0x15: { res = "someone"; break; } case 0x16: { res = "someone's"; break; } case 0x17: { res = "himself"; break; } /*** Female (assume human if vague) ***/ case 0x20: { res = "she"; break; } case 0x21: { res = "her"; break; } case 0x22: { res = "her"; break; } case 0x23: { res = "herself"; break; } case 0x24: { res = "someone"; break; } case 0x25: { res = "someone"; break; } case 0x26: { res = "someone's"; break; } case 0x27: { res = "herself"; break; } } /* Copy the result */ strnfmt(desc, max, "%s", res); } /* Handle visible monsters, "reflexive" request */ else if ((mode & 0x02) && (mode & 0x01)) { /* The monster is visible, so use its gender */ if (FLAG(r_ptr, RF_FEMALE)) strnfmt(desc, max, "herself"); else if (FLAG(r_ptr, RF_MALE)) strnfmt(desc, max, "himself"); else strnfmt(desc, max, "itself"); } /* Handle all other visible monster requests */ else { /* It could be a Unique */ if ((FLAG(r_ptr, RF_UNIQUE)) && !p_ptr->tim.image) { /* Start with the name (thus nominative and objective) */ n = strnfmt(desc, max, "%s", name); } /* It could be an indefinite monster */ else if (mode & 0x08) { /* XXX Check plurality for "some" */ /* Indefinite monsters need an indefinite article */ n = strnfmt(desc, max, is_a_vowel(name[0]) ? "an %s" : "a %s", name); } /* It could be a normal, definite, monster */ else { /* Definite monsters need a definite article */ if (is_pet(m_ptr)) n = strnfmt(desc, max, "your %s", name); else n = strnfmt(desc, max, "the %s", name); } /* Handle the Possessive as a special afterthought */ if (mode & 0x02) { /* XXX Check for trailing "s" */ /* Simply append "apostrophe" and "s" */ strnfcat(desc, max, &n, "'s"); } } } /* * Wrapper around monster_desc() for the '%v' * format option. This allows monster_desc() to be * called in a format string. * * The parameters are monster_type (m_ptr) and mode(int). */ void monster_fmt(char *buf, uint max, cptr fmt, va_list *vp) { const monster_type *m_ptr; int mode; /* Unused parameter */ (void)fmt; /* Get the object */ m_ptr = va_arg(*vp, const monster_type*); /* Get the mode */ mode = va_arg(*vp, int); /* Print the description into the buffer */ monster_desc(buf, m_ptr, mode, max); } /* * Learn about a monster (by "probing" it) */ void lore_do_probe(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Hack -- Memorize some flags */ r_ptr->r_flags[0] = r_ptr->flags[0]; r_ptr->r_flags[1] = r_ptr->flags[1]; r_ptr->r_flags[2] = r_ptr->flags[2]; /* Update monster recall window */ if (p_ptr->monster_race_idx == m_ptr->r_idx) { /* Window stuff */ p_ptr->window |= (PW_MONSTER); } } /* * Take note that the given monster just dropped some treasure * * Note that learning the "GOOD"/"GREAT" flags gives information * about the treasure (even when the monster is killed for the first * time, such as uniques, and the treasure has not been examined yet). * * This "indirect" method is used to prevent the player from learning * exactly how much treasure a monster can drop from observing only * a single example of a drop. This method actually observes how much * gold and items are dropped, and remembers that information to be * described later by the monster recall code. */ void lore_treasure(int m_idx, int num_item, int num_gold) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Note the number of things dropped */ if (num_item > r_ptr->r_drop_item) r_ptr->r_drop_item = num_item; if (num_gold > r_ptr->r_drop_gold) r_ptr->r_drop_gold = num_gold; /* Hack -- memorize the good/great flags */ if (FLAG(r_ptr, RF_DROP_GOOD)) r_ptr->r_flags[0] |= (RF0_DROP_GOOD); if (FLAG(r_ptr, RF_DROP_GREAT)) r_ptr->r_flags[0] |= (RF0_DROP_GREAT); /* Update monster recall window */ if (p_ptr->monster_race_idx == m_ptr->r_idx) { /* Window stuff */ p_ptr->window |= (PW_MONSTER); } } void update_mon_vis(u16b r_idx, int increment) { monster_race *r_ptr = &r_info[r_idx]; int i; /* Changes on screen */ p_ptr->window |= PW_VISIBLE; /* Paranoia */ #if 0 if (!r_ptr->r_see && (increment == -1)) core("Monster visibility error!"); #else /* 0 */ /* Ignore the bug, until we know what is really going on. */ if (!r_ptr->r_see && (increment == -1)) return; #endif /* 0 */ /* Update the counter */ r_ptr->r_see += increment; /* Disturb if necessary */ if (disturb_view) disturb(FALSE); /* Update 'most powerful seen monster' */ if (r_ptr->r_see) { /* Check to see if we have spotted a more powerful monster */ if (r_idx > p_ptr->max_seen_r_idx) { /* Track this monster */ p_ptr->max_seen_r_idx = r_idx; } } else { /* Look to see if we need to recalculate max_seen_ridx */ if (r_idx == p_ptr->max_seen_r_idx) { for (i = r_idx - 1; i > 0; i--) { /* Can we see this monster? */ if (r_info[i].r_see) break; } /* Record it */ p_ptr->max_seen_r_idx = i; } } } /* * This function updates the monster record of the given monster * * This involves extracting the distance to the player (if requested), * and then checking for visibility (natural, infravision, see-invis, * telepathy), updating the monster visibility flag, redrawing (or * erasing) the monster when its visibility changes, and taking note * of any interesting monster flags (cold-blooded, invisible, etc). * * Note the new "mflag" field which encodes several monster state flags, * including "view" for when the monster is currently in line of sight, * and "mark" for when the monster is currently visible via detection. * * The only monster fields that are changed here are "cdis" (the * distance from the player), "ml" (visible to the player), and * "mflag" (to maintain the "MFLAG_VIEW" flag). * * Note the special "update_monsters()" function which can be used to * call this function once for every monster. * * Note the "full" flag which requests that the "cdis" field be updated, * this is only needed when the monster (or the player) has moved. * * Every time a monster moves, we must call this function for that * monster, and update the distance, and the visibility. Every time * the player moves, we must call this function for every monster, and * update the distance, and the visibility. Whenever the player "state" * changes in certain ways ("blindness", "infravision", "telepathy", * and "see invisible"), we must call this function for every monster, * and update the visibility. * * Routines that change the "illumination" of a grid must also call this * function for any monster in that grid, since the "visibility" of some * monsters may be based on the illumination of their grid. * * Note that this function is called once per monster every time the * player moves. When the player is running, this function is one * of the primary bottlenecks, along with "update_view()" and the * "process_monsters()" code, so efficiency is important. * * Note the optimized "inline" version of the "distance()" function. * * A monster is "visible" to the player if (1) it has been detected * by the player, (2) it is close to the player and the player has * telepathy, or (3) it is close to the player, and in line of sight * of the player, and it is "illuminated" by some combination of * infravision, torch light, or permanent light (invisible monsters * are only affected by "light" if the player can see invisible). * * Monsters which are not on the current panel may be "visible" to * the player, and their descriptions will include an "offscreen" * reference. Currently, offscreen monsters cannot be targetted * or viewed directly, but old targets will remain set. XXX XXX * * The player can choose to be disturbed by several things, including * "disturb_near" (monster which is "easily" viewable moves in some * way). Note that "moves" includes "appears" and "disappears". */ void update_mon(int m_idx, bool full) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int d; /* Current location */ int fy = m_ptr->fy; int fx = m_ptr->fx; /* Seen at all */ bool flag = FALSE; /* Seen by vision */ bool easy = FALSE; pcave_type *pc_ptr; /* Exit if monster does not exist. */ if (!m_idx) return; /* Compute distance */ if (full) { int py = p_ptr->py; int px = p_ptr->px; /* Distance components */ int dy = (py > fy) ? (py - fy) : (fy - py); int dx = (px > fx) ? (px - fx) : (fx - px); /* Approximate distance */ d = (dy > dx) ? (dy + (dx / 2)) : (dx + (dy / 2)); /* Restrict distance */ if (d > 255) d = 255; /* Save the distance */ m_ptr->cdis = d; } /* Extract distance */ else { /* Extract the distance */ d = m_ptr->cdis; } /* Detected */ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE; /* Nearby */ if (d <= MAX_SIGHT) { /* Basic telepathy */ if (FLAG(p_ptr, TR_TELEPATHY)) { /* Empty mind, no telepathy */ if (FLAG(r_ptr, RF_EMPTY_MIND)) { /* Memorize flags */ r_ptr->r_flags[1] |= (RF1_EMPTY_MIND); } /* Weird mind, occasional telepathy */ else if (FLAG(r_ptr, RF_WEIRD_MIND)) { /* One in ten individuals are detectable */ if ((m_idx % 10) == 5) { /* Detectable */ flag = TRUE; /* Memorize flags */ r_ptr->r_flags[1] |= (RF1_WEIRD_MIND); /* Hack -- Memorize mental flags */ if (FLAG(r_ptr, RF_SMART)) r_ptr->r_flags[1] |= (RF1_SMART); if (FLAG(r_ptr, RF_STUPID)) r_ptr->r_flags[1] |= (RF1_STUPID); } } /* Normal mind, allow telepathy */ else { /* Detectable */ flag = TRUE; /* Hack -- Memorize mental flags */ if (FLAG(r_ptr, RF_SMART)) r_ptr->r_flags[1] |= (RF1_SMART); if (FLAG(r_ptr, RF_STUPID)) r_ptr->r_flags[1] |= (RF1_STUPID); } } /* Paranoia */ if (in_boundsp(fx, fy)) { pc_ptr = parea(fx, fy); /* Normal line of sight, and not blind */ if (player_has_los_grid(pc_ptr) && !p_ptr->tim.blind) { bool do_invisible = FALSE; bool do_cold_blood = FALSE; /* Use "infravision" */ if (d <= p_ptr->see_infra) { /* Handle "cold blooded" monsters */ if (FLAG(r_ptr, RF_COLD_BLOOD)) { /* Take note */ do_cold_blood = TRUE; } /* Handle "warm blooded" monsters */ else { /* Easy to see */ easy = flag = TRUE; } } /* Use "illumination" */ if ((player_can_see_grid(pc_ptr)) || FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Handle "invisible" monsters */ if (FLAG(r_ptr, RF_INVISIBLE)) { /* Take note */ do_invisible = TRUE; /* See invisible */ if (FLAG(p_ptr, TR_SEE_INVIS)) { /* Easy to see */ easy = flag = TRUE; } } /* Handle "normal" monsters */ else { /* Easy to see */ easy = flag = TRUE; } } /* Visible */ if (flag) { /* Memorize flags */ if (do_invisible) r_ptr->r_flags[1] |= (RF1_INVISIBLE); if (do_cold_blood) r_ptr->r_flags[1] |= (RF1_COLD_BLOOD); } } } } /* The monster is now visible */ if (flag) { /* It was previously unseen */ if (!m_ptr->ml) { /* Mark as visible */ m_ptr->ml = TRUE; /* Increment monster visibility counter if we know about it */ if (!(m_ptr->smart & SM_MIMIC)) { update_mon_vis(m_ptr->r_idx, 1); } /* Draw the monster */ lite_spot(fx, fy); /* Update health bar as needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Hack -- Count "fresh" sightings */ if (r_ptr->r_sights < MAX_SHORT) r_ptr->r_sights++; } } /* The monster is not visible */ else { /* It was previously seen */ if (m_ptr->ml) { /* Mark as not visible */ m_ptr->ml = FALSE; /* Decrement monster visibility counter if we know about it */ if (!(m_ptr->smart & SM_MIMIC)) { update_mon_vis(m_ptr->r_idx, -1); } /* Erase the monster */ lite_spot(fx, fy); /* Update health bar as needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); } } /* The monster is now easily visible */ if (easy) { /* Change */ if (!(m_ptr->mflag & (MFLAG_VIEW))) { /* Mark as easily visible */ m_ptr->mflag |= (MFLAG_VIEW); } } /* The monster is not easily visible */ else { /* Change */ if (m_ptr->mflag & (MFLAG_VIEW)) { /* Mark as not easily visible */ m_ptr->mflag &= ~(MFLAG_VIEW); } } } /* * This function simply updates all the (non-dead) monsters (see above). */ void update_monsters(bool full) { int i; /* Update each (live) monster */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Update the monster */ update_mon(i, full); } } /* * Attempt to place a monster of the given race at the given location. * * To give the player a sporting chance, any monster that appears in * line-of-sight and is extremely dangerous can be marked as * "FORCE_SLEEP", which will cause them to be placed with low energy, * which often (but not always) lets the player move before they do. * * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters. * * XXX XXX XXX Use special "here" and "dead" flags for unique monsters, * remove old "cur_num" and "max_num" fields. * * XXX XXX XXX Actually, do something similar for artifacts, to simplify * the "preserve" mode, and to make the "what artifacts" flag more useful. * * This is the only function which may place a monster in the dungeon, * except for the savefile loading code. */ monster_type *place_monster_one(int x, int y, int r_idx, bool slp, bool friendly, bool pet) { int i; cave_type *c_ptr; monster_type *m_ptr; monster_race *r_ptr = &r_info[r_idx]; cptr name; /* Paranoia */ if (!r_idx) return (NULL); /* Paranoia */ if (!r_ptr->name) return (NULL); /* Lookup the name of the monster */ name = mon_race_name(r_ptr); /* Verify location */ if (!in_bounds2(x, y)) return (NULL); /* Access the location */ c_ptr = area(x, y); /* Walls also stops generation if we aren't ghostly */ if (cave_wall_grid(c_ptr) && !(FLAG(r_ptr, RF_PASS_WALL))) { return (NULL); } /* Not if other monster is here */ if (c_ptr->m_idx) return (NULL); /* Not if player is here */ if ((y == p_ptr->py) && (x == p_ptr->px)) return (NULL); if (!test_monster_square(c_ptr, r_ptr)) return (NULL); /* Hack -- "unique" monsters must be "unique" */ if ((FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_UNIQUE_7)) && (r_ptr->cur_num >= r_ptr->max_num)) { /* Cannot create */ return (NULL); } /* Depth monsters may NOT be created out of depth, unless in Nightmare mode */ if (FLAG(r_ptr, RF_FORCE_DEPTH) && (p_ptr->depth < r_ptr->level) && (!ironman_nightmare || FLAG(r_ptr, RF_QUESTOR))) { /* Cannot create */ return (NULL); } /* * Test for fields that will not allow monsters to * be generated on them. (i.e. Glyph of warding) */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MPLACE)) return (NULL); /* Powerful monster */ if (r_ptr->level > p_ptr->depth) { /* Unique monsters */ if (FLAG(r_ptr, RF_UNIQUE)) { /* Message for cheaters */ if (cheat_hear) msgf("Deep Unique (%s).", name); /* Boost rating by twice delta-depth */ inc_rating((r_ptr->level - p_ptr->depth) * 2); } /* Normal monsters */ else { /* Message for cheaters */ if (cheat_hear) msgf("Deep Monster (%s).", name); if (!FLAG(r_ptr, RF_FRIENDS)) { /* Boost rating by delta-depth */ inc_rating(r_ptr->level - p_ptr->depth); } } } /* Note the monster */ else if (FLAG(r_ptr, RF_UNIQUE)) { /* Unique monsters induce message */ if (cheat_hear) msgf("Unique (%s).", name); } /* Make a new monster */ c_ptr->m_idx = m_pop(); /* Mega-Hack -- catch "failure" */ if (!c_ptr->m_idx) return (NULL); /* Get a new monster record */ m_ptr = &m_list[c_ptr->m_idx]; /* Save the race */ m_ptr->r_idx = r_idx; /* Place the monster at the location */ m_ptr->fy = y; m_ptr->fx = x; /* Region */ m_ptr->region = cur_region; /* No "damage" yet */ m_ptr->stunned = 0; m_ptr->confused = 0; m_ptr->monfear = 0; /* Unknown distance */ m_ptr->cdis = 0; /* No flags */ m_ptr->mflag = 0; /* Not visible */ m_ptr->ml = FALSE; /* Pet? */ if (pet) { set_pet(m_ptr); } /* Friendly? */ else if (friendly || (FLAG(r_ptr, RF_FRIENDLY))) { set_friendly(m_ptr); } /* Assume no sleeping */ m_ptr->csleep = 0; /* Enforce sleeping if needed */ if (slp && r_ptr->sleep && !ironman_nightmare) { int val = r_ptr->sleep; m_ptr->csleep = ((val * 2) + randint1(val * 10)); } /* Assign maximal hitpoints */ if (FLAG(r_ptr, RF_FORCE_MAXHP)) { m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside); } else { m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside); } /* Monsters have double hitpoints in Nightmare mode */ if (ironman_nightmare) { u32b hp = m_ptr->maxhp * 2L; m_ptr->maxhp = (s16b)MIN(30000, hp); } /* And start out fully healthy */ m_ptr->hp = m_ptr->maxhp; /* Extract the monster base speed */ m_ptr->mspeed = r_ptr->speed; /* Hack -- small racial variety */ if (!(FLAG(r_ptr, RF_UNIQUE))) { /* Allow some small variation per monster */ i = extract_energy[r_ptr->speed] / 10; if (i) m_ptr->mspeed += rand_spread(0, i); } /* Give a random starting energy */ m_ptr->energy = (byte)randint0(100); /* Nightmare monsters are more prepared */ if (ironman_nightmare) { m_ptr->energy *= 2; } /* Force monster to wait for player, unless in Nightmare mode */ if ((FLAG(r_ptr, RF_FORCE_SLEEP)) && !ironman_nightmare) { /* Monster is still being nice */ m_ptr->mflag |= (MFLAG_NICE); /* Must repair monsters */ p_ptr->change |= (PC_REPAIR); } /* Hack -- see "process_monsters()" */ if (c_ptr->m_idx < hack_m_idx) { /* Monster is still being born */ m_ptr->mflag |= (MFLAG_MOVE); } /* Hack - are we a mimic? */ if (FLAG(r_ptr, RF_CHAR_MIMIC)) { /* The player doesn't know about us yet */ m_ptr->smart |= SM_MIMIC; } /* Update the monster */ update_mon(c_ptr->m_idx, TRUE); /* Hack -- Count the monsters on the level */ r_ptr->cur_num++; /* Hack -- Count the number of "reproducers" */ if (FLAG(r_ptr, RF_MULTIPLY)) num_repro++; /* Hack -- Notice new multi-hued monsters */ if (FLAG(r_ptr, RF_ATTR_MULTI)) p_ptr->change |= (PC_SHIMMER); /* Success */ return (m_ptr); } /* * Maximum size of a group of monsters */ #define GROUP_MAX 32 /* * Attempt to place a "group" of monsters around the given location */ static bool place_monster_group(int x, int y, int r_idx, bool slp, bool friendly, bool pet) { monster_race *r_ptr = &r_info[r_idx]; int n, i; int total = 0, extra = 0; int hack_n = 0; int hack_y[GROUP_MAX]; int hack_x[GROUP_MAX]; cave_type *c_ptr; /* Pick a group size */ total = randint1(13); /* Hard monsters, small groups */ if (r_ptr->level > p_ptr->depth) { extra = r_ptr->level - p_ptr->depth; extra = 0 - randint1(extra); } /* Easy monsters, large groups */ else if (r_ptr->level < p_ptr->depth) { extra = p_ptr->depth - r_ptr->level; extra = randint1(extra); } /* Hack -- limit group reduction */ if (extra > 12) extra = 12; /* Modify the group size */ total += extra; /* Minimum size */ if (total < 1) total = 1; /* Maximum size */ if (total > GROUP_MAX) total = GROUP_MAX; /* Start on the monster */ hack_n = 1; hack_x[0] = x; hack_y[0] = y; /* Puddle monsters, breadth first, up to total */ for (n = 0; (n < hack_n) && (hack_n < total); n++) { /* Grab the location */ int hx = hack_x[n]; int hy = hack_y[n]; /* Check each direction, up to total */ for (i = 0; (i < 8) && (hack_n < total); i++) { int mx, my; scatter(&mx, &my, hx, hy, 4); /* paranoia */ if (!in_bounds2(mx, my)) continue; /* Not on player */ if ((my == p_ptr->py) && (mx == p_ptr->px)) continue; /* Walls and Monsters block flow */ c_ptr = area(mx, my); if (!cave_empty_grid(c_ptr)) continue; /* Attempt to place another monster */ if (place_monster_one(mx, my, r_idx, slp, friendly, pet)) { /* Add it to the "hack" set */ hack_y[hack_n] = my; hack_x[hack_n] = mx; hack_n++; } } } /* Success */ return (TRUE); } /* * Hack -- help pick an escort type */ static int place_monster_idx = 0; /* * Hack -- help pick an escort type */ static bool place_monster_okay(int r_idx) { monster_race *r_ptr = &r_info[place_monster_idx]; monster_race *z_ptr = &r_info[r_idx]; /* Require similar "race" */ if (z_ptr->d_char != r_ptr->d_char) return (FALSE); /* Skip more advanced monsters */ if (z_ptr->level > r_ptr->level) return (FALSE); /* Skip unique monsters */ if (FLAG(z_ptr, RF_UNIQUE)) return (FALSE); /* Paranoia -- Skip identical monsters */ if (place_monster_idx == r_idx) return (FALSE); /* Good vs. evil */ if ((FLAG(r_ptr, RF_EVIL) && FLAG(z_ptr, RF_GOOD)) || (FLAG(r_ptr, RF_GOOD) && FLAG(z_ptr, RF_EVIL))) { return FALSE; } /* Hostile vs. non-hostile */ if (FLAG(r_ptr, RF_FRIENDLY) != FLAG(z_ptr, RF_FRIENDLY)) return FALSE; /* Okay */ return (TRUE); } /* * Attempt to place a monster of the given race at the given location * * Note that certain monsters are now marked as requiring "friends". * These monsters, if successfully placed, and if the "grp" parameter * is TRUE, will be surrounded by a "group" of identical monsters. * * Note that certain monsters are now marked as requiring an "escort", * which is a collection of monsters with similar "race" but lower level. * * Some monsters induce a fake "group" flag on their escorts. * * Note the "bizarre" use of non-recursion to prevent annoying output * when running a code profiler. * * Note the use of the new "monster allocation table" code to restrict * the "get_mon_num()" function to "legal" escort types. */ monster_type *place_monster_aux(int x, int y, int r_idx, bool slp, bool grp, bool friendly, bool pet, bool summon) { int i; monster_race *r_ptr = &r_info[r_idx]; cave_type *c_ptr; monster_type *m_ptr; /* Place one monster, or fail */ m_ptr = place_monster_one(x, y, r_idx, slp, friendly, pet); if (!m_ptr) return (NULL); /* Require the "group" flag */ if (!grp) return (m_ptr); /* Friends for certain monsters */ if (FLAG(r_ptr, RF_FRIENDS)) { /* Attempt to place a group */ (void)place_monster_group(x, y, r_idx, slp, friendly, pet); } /* Escorts for certain monsters */ if (FLAG(r_ptr, RF_ESCORT)) { /* Set the escort index */ place_monster_idx = r_idx; /* Try to place several "escorts" */ for (i = 0; i < 50; i++) { int nx, ny, z, d = 3; /* Pick a location */ scatter(&nx, &ny, x, y, d); /* paranoia */ if (!in_bounds2(x, y)) continue; /* Not on player */ if ((y == p_ptr->py) && (x == p_ptr->px)) continue; /* Require empty grids */ c_ptr = area(nx, ny); if (!cave_empty_grid(c_ptr)) continue; /* Prepare allocation table */ get_mon_num_prep(place_monster_okay); /* Default to filtering out monsters not normally on this dungeon */ if (!summon) { (void) filter_mon_loc(nx, ny); } /* Pick a random race */ z = get_mon_num(r_ptr->level); /* Handle failure */ if (!z) break; /* Place a single escort */ (void)place_monster_one(nx, ny, z, slp, friendly, pet); /* Place a "group" of escorts if needed */ if (FLAG(&r_info[z], RF_FRIENDS) || FLAG(r_ptr, RF_ESCORTS)) { /* Place a group of monsters */ (void)place_monster_group(nx, ny, z, slp, friendly, pet); } } } /* Success */ return (m_ptr); } /* * Hack -- attempt to place a monster at the given location * * Attempt to find a monster appropriate to the level returned by * filter_mon_loc() */ bool place_monster(int x, int y, bool slp, bool grp, int delta_level) { int r_idx; int level; /* Prepare allocation table */ get_mon_num_prep(NULL); level = filter_mon_loc(x, y); /* Pick a monster */ r_idx = get_mon_num(level + delta_level); /* Handle failure */ if (!r_idx) return (FALSE); /* Attempt to place the monster */ if (place_monster_aux(x, y, r_idx, slp, grp, FALSE, FALSE, FALSE)) return (TRUE); /* Oops */ return (FALSE); } #ifdef MONSTER_HORDES bool alloc_horde(int x, int y) { monster_race *r_ptr = NULL; int r_idx = 0; int m_idx; int attempts = 1000; int cy = y; int cx = x; int level; /* Prepare allocation table */ get_mon_num_prep(NULL); level = filter_mon_loc(x, y); while (--attempts) { /* Pick a monster */ r_idx = get_mon_num(level); /* Handle failure */ if (!r_idx) return (FALSE); r_ptr = &r_info[r_idx]; if (!(FLAG(r_ptr, RF_UNIQUE))) break; } if (attempts < 1) return FALSE; attempts = 1000; while (--attempts) { /* Attempt to place the monster */ if (place_monster_aux(x, y, r_idx, FALSE, FALSE, FALSE, FALSE, FALSE)) break; } if (attempts < 1) return FALSE; m_idx = area(x, y)->m_idx; summon_kin_type = r_ptr->d_char; for (attempts = rand_range(5, 15); attempts; attempts--) { scatter(&cx, &cy, x, y, 5); (void)summon_specific(m_idx, cx, cy, p_ptr->depth + 5, SUMMON_KIN, TRUE, FALSE, FALSE); y = cy; x = cx; } return TRUE; } #endif /* MONSTER_HORDES */ /* * Attempt to allocate a random monster in the dungeon. * * Place the monster at least "dis" distance from the player. * * Use "slp" to choose the initial "sleep" status */ bool alloc_monster(int dis, bool slp, int delta_level) { int py = p_ptr->py; int px = p_ptr->px; int y = 0, x = 0; int attempts_left = 10000; cave_type *c_ptr; /* Find a legal, distant, unoccupied, space */ while (--attempts_left) { /* Pick a location */ y = rand_range(p_ptr->min_hgt, p_ptr->max_hgt - 1); x = rand_range(p_ptr->min_wid, p_ptr->max_wid - 1); /* Not on player */ if ((y == py) && (x == px)) continue; /* Require empty floor grid (was "naked") */ c_ptr = area(x, y); if (!cave_empty_grid(c_ptr)) continue; /* Accept far away grids */ if (distance(x, y, px, py) > dis) break; } if (!attempts_left) { if (cheat_xtra || cheat_hear) { msgf("Warning! Could not allocate a new monster. Small level?"); } return (FALSE); } #ifdef MONSTER_HORDES if (randint1(5000) <= p_ptr->depth) { if (alloc_horde(x, y)) { if (cheat_hear) msgf("Monster horde."); return (TRUE); } } else { #endif /* MONSTER_HORDES */ /* Attempt to place the monster, allow groups */ if (place_monster(x, y, slp, TRUE, delta_level)) return (TRUE); #ifdef MONSTER_HORDES } #endif /* MONSTER_HORDES */ /* Nope */ return (FALSE); } /* * Hack -- the "type" of the current "summon specific" */ static int summon_specific_type = 0; /* * Hack -- the index of the summoning monster */ static int summon_specific_who = -1; /* * Hack -- the hostility of the summoned monster */ static int summon_specific_hostile = TRUE; /* * Hack -- help decide if a monster race is "okay" to summon */ static bool summon_specific_okay(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; bool okay = FALSE; /* Hack -- identify the summoning monster */ if (summon_specific_who > 0) { monster_type *m_ptr = &m_list[summon_specific_who]; monster_race *s_ptr = &r_info[m_ptr->r_idx]; /* Do not summon enemies */ /* Good vs. evil */ if ((FLAG(r_ptr, RF_EVIL) && FLAG(s_ptr, RF_GOOD)) || (FLAG(r_ptr, RF_GOOD) && FLAG(s_ptr, RF_EVIL))) { return FALSE; } /* Hostile vs. non-hostile */ if (is_hostile(m_ptr) != summon_specific_hostile) { return FALSE; } } /* Use the player's alignment */ else if (summon_specific_who < 0) { /* Do not summon enemies of the pets */ if (((p_ptr->align < 0) && (FLAG(r_ptr, RF_GOOD))) || ((p_ptr->align > 0) && (FLAG(r_ptr, RF_EVIL)))) { return FALSE; } } /* Hack -- no specific type specified */ if (!summon_specific_type) return (TRUE); /* Check our requirements */ switch (summon_specific_type) { case SUMMON_ANT: { okay = ((r_ptr->d_char == 'a') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_SPIDER: { okay = ((r_ptr->d_char == 'S') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_HOUND: { okay = (((r_ptr->d_char == 'C') || (r_ptr->d_char == 'Z')) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_HYDRA: { okay = ((r_ptr->d_char == 'M') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_ANGEL: { okay = ((r_ptr->d_char == 'A') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_DEMON: { okay = ((FLAG(r_ptr, RF_DEMON)) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_UNDEAD: { okay = ((FLAG(r_ptr, RF_UNDEAD)) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_DRAGON: { okay = ((FLAG(r_ptr, RF_DRAGON)) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_HI_UNDEAD: { okay = ((r_ptr->d_char == 'L') || (r_ptr->d_char == 'V') || (r_ptr->d_char == 'W')); break; } case SUMMON_HI_DRAGON: { okay = (r_ptr->d_char == 'D'); break; } case SUMMON_AMBERITES: { okay = FLAG(r_ptr, RF_AMBERITE); break; } case SUMMON_UNIQUE: { okay = FLAG(r_ptr, RF_UNIQUE); break; } case SUMMON_BIZARRE1: { okay = ((r_ptr->d_char == 'm') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_BIZARRE2: { okay = ((r_ptr->d_char == 'b') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_BIZARRE3: { okay = ((r_ptr->d_char == 'Q') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_BIZARRE4: { okay = ((r_ptr->d_char == 'v') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_BIZARRE5: { okay = ((r_ptr->d_char == '$') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_BIZARRE6: { okay = (((r_ptr->d_char == '!') || (r_ptr->d_char == '?') || (r_ptr->d_char == '=') || (r_ptr->d_char == '$') || (r_ptr->d_char == '|')) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_CYBER: { okay = ((r_ptr->d_char == 'U') && (FLAG(r_ptr, RF_ROCKET)) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_KIN: { okay = ((r_ptr->d_char == summon_kin_type) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_DAWN: { okay = (mon_name_cont(r_ptr, "the Dawn") && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_ANIMAL: { okay = ((FLAG(r_ptr, RF_ANIMAL)) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_ANIMAL_RANGER: { okay = ((FLAG(r_ptr, RF_ANIMAL)) && (strchr("abcflqrwBCIJKMRS", r_ptr->d_char)) && !(FLAG(r_ptr, RF_DRAGON)) && !(FLAG(r_ptr, RF_EVIL)) && !(FLAG(r_ptr, RF_UNDEAD)) && !(FLAG(r_ptr, RF_DEMON)) && !(r_ptr->flags[3] || r_ptr->flags[4] || r_ptr->flags[5]) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_HI_UNDEAD_NO_UNIQUES: { okay = (((r_ptr->d_char == 'L') || (r_ptr->d_char == 'V') || (r_ptr->d_char == 'W')) && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_HI_DRAGON_NO_UNIQUES: { okay = ((r_ptr->d_char == 'D') && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_NO_UNIQUES: { okay = (!(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_PHANTOM: { okay = (mon_name_cont(r_ptr, "Phantom") && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_ELEMENTAL: { okay = (mon_name_cont(r_ptr, "lemental") && !(FLAG(r_ptr, RF_UNIQUE))); break; } case SUMMON_BLUE_HORROR: { okay = (mon_name_cont(r_ptr, "lue horror") && !(FLAG(r_ptr, RF_UNIQUE))); break; } } /* Result */ return (okay); } /* * Place a monster (of the specified "type") near the given * location. Return TRUE if a monster was actually summoned. * * We will attempt to place the monster up to 10 times before giving up. * * Note: SUMMON_UNIQUE and SUMMON_AMBERITES will summon Unique's * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's * Note: None of the other summon codes will ever summon Unique's. * * This function has been changed. We now take the "monster level" * of the summoning monster as a parameter, and use that, along with * the current dungeon level, to help determine the level of the * desired monster. Note that this is an upper bound, and also * tends to "prefer" monsters of that level. Currently, we use * the average of the dungeon and monster levels, and then add * five to allow slight increases in monster power. * * Note that we use the new "monster allocation table" creation code * to restrict the "get_mon_num()" function to the set of "legal" * monsters, making this function much faster and more reliable. * * Note that this function may not succeed, though this is very rare. */ bool summon_specific(int who, int x1, int y1, int req_lev, int type, bool group, bool friendly, bool pet) { int i, x, y, r_idx; cave_type *c_ptr; /* Look for a location */ for (i = 0; i < 20; ++i) { /* Pick a distance */ int d = (i / 15) + 1; /* Pick a location */ scatter(&x, &y, x1, y1, d); /* paranoia */ if (!in_bounds2(x, y)) continue; /* Not on top of player */ if ((y == p_ptr->py) && (x == p_ptr->px)) continue; /* Require "empty" floor grid */ c_ptr = area(x, y); if (!cave_empty_grid(c_ptr)) continue; /* ... nor on the Pattern */ if (cave_pattern_grid(c_ptr)) continue; /* Check for a field that blocks movement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) continue; /* * Test for fields that will not allow monsters to * be generated on them. (i.e. Glyph of warding) */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MPLACE)) continue; /* Okay */ break; } /* Failure */ if (i == 20) return (FALSE); /* Save the summoner */ summon_specific_who = who; /* Save the "summon" type */ summon_specific_type = type; /* Save the hostility */ summon_specific_hostile = (!friendly && !pet); /* Prepare allocation table */ get_mon_num_prep(summon_specific_okay); /* Pick a monster, using the level calculation */ r_idx = get_mon_num((base_level() + req_lev) / 2 + 5); /* Handle failure */ if (!r_idx) return (FALSE); /* Attempt to place the monster (awake, allow groups) */ if (!place_monster_aux(x, y, r_idx, FALSE, group, friendly, pet, TRUE)) return (FALSE); /* Success */ return (TRUE); } /* * A "dangerous" function, creates a pet of the specified type */ monster_type *summon_named_creature(int x1, int y1, int r_idx, bool slp, bool group_ok, bool pet) { int i, x, y; cave_type *c_ptr; monster_type *m_ptr = NULL; /* Paranoia */ /* if (!r_idx) return; */ /* Prevent illegal monsters */ if (r_idx >= z_info->r_max) return FALSE; /* Try 10 times */ for (i = 0; i < 10; i++) { int d = 1; /* Pick a location */ scatter(&x, &y, x1, y1, d); /* paranoia */ if (!in_bounds2(x, y)) continue; /* Not on top of player */ if ((y == p_ptr->py) && (x == p_ptr->px)) continue; /* Require empty grids */ c_ptr = area(x, y); if (!cave_empty_grid(c_ptr)) continue; /* Place it (allow groups) */ m_ptr = place_monster_aux(x, y, r_idx, slp, group_ok, FALSE, pet, TRUE); if (m_ptr) break; } return (m_ptr); } /* * Same as above, but set SM_CLONE, * and assume there is only one monster created, which is awake. */ monster_type *summon_cloned_creature(int x1, int y1, int r_idx, bool pet) { monster_type *m_ptr = summon_named_creature(x1, y1, r_idx, FALSE, FALSE, pet); if (m_ptr) { /* Set the cloned flag, so no treasure is dropped */ m_ptr->smart |= SM_CLONED; } return (m_ptr); } /* * Let the given monster attempt to reproduce. * * Note that "reproduction" REQUIRES empty space. */ monster_type *multiply_monster(int m_idx, bool clone, bool friendly, bool pet) { monster_type *m_ptr = &m_list[m_idx]; monster_type *t_ptr = NULL; int i, y, x; cave_type *c_ptr; /* Try up to 18 times */ for (i = 0; i < 18; i++) { int d = 1; /* Pick a location */ scatter(&x, &y, m_ptr->fx, m_ptr->fy, d); /* paranoia */ if (!in_bounds2(x, y)) continue; /* Not on top of player */ if ((y == p_ptr->py) && (x == p_ptr->px)) continue; /* Require an "empty" floor grid */ c_ptr = area(x, y); if (!cave_empty_grid(c_ptr)) continue; /* Create a new monster (awake, no groups) */ t_ptr = place_monster_aux(x, y, m_ptr->r_idx, FALSE, FALSE, friendly, pet, TRUE); /* Done */ break; } if (clone && t_ptr) t_ptr->smart |= SM_CLONED; /* Result */ return (t_ptr); } /* * Dump a message describing a monster's reaction to damage * * Technically should attempt to treat "Beholder"'s as jelly's */ void message_pain(int m_idx, int dam) { long oldhp, newhp, tmp; int percentage; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80]; /* Get the monster name */ monster_desc(m_name, m_ptr, 0, 80); /* Notice non-damage */ if (dam == 0) { msgf("%^s is unharmed.", m_name); return; } /* Note -- subtle fix -CFT */ newhp = (long)(m_ptr->hp); oldhp = newhp + (long)(dam); tmp = (newhp * 100L) / oldhp; percentage = (int)(tmp); /* Mushrooms, Eyes, Jellies, Molds, Vortices, Worms, Quylthulgs */ if (strchr(",ejmvwQ", r_ptr->d_char)) { if (percentage > 95) msgf("%^s barely notices.", m_name); else if (percentage > 75) msgf("%^s flinches.", m_name); else if (percentage > 50) msgf("%^s squelches.", m_name); else if (percentage > 35) msgf("%^s quivers in pain.", m_name); else if (percentage > 20) msgf("%^s writhes about.", m_name); else if (percentage > 10) msgf("%^s writhes in agony.", m_name); else msgf("%^s jerks limply.", m_name); } /* Fish */ else if (strchr("~", r_ptr->d_char)) { if (percentage > 95) msgf("%^s barely notices.", m_name); else if (percentage > 75) msgf("%^s flinches.", m_name); else if (percentage > 50) msgf("%^s hesitates.", m_name); else if (percentage > 35) msgf("%^s quivers in pain.", m_name); else if (percentage > 20) msgf("%^s writhes about.", m_name); else if (percentage > 10) msgf("%^s writhes in agony.", m_name); else msgf("%^s jerks limply.", m_name); } /* Golems, Walls, Doors, Stairs */ else if (strchr("g#+<>", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 50) msgf("%^s roars thunderously.", m_name); else if (percentage > 35) msgf("%^s rumbles.", m_name); else if (percentage > 20) msgf("%^s grunts.", m_name); else if (percentage > 10) msgf("%^s hesitates.", m_name); else msgf("%^s crumples.", m_name); } /* Snakes, Hydrae, Reptiles, Mimics */ else if (strchr("JMR", r_ptr->d_char) || !isalpha(r_ptr->d_char)) { if (percentage > 95) msgf("%^s barely notices.", m_name); else if (percentage > 75) msgf("%^s hisses.", m_name); else if (percentage > 50) msgf("%^s rears up in anger.", m_name); else if (percentage > 35) msgf("%^s hisses furiously.", m_name); else if (percentage > 20) msgf("%^s writhes about.", m_name); else if (percentage > 10) msgf("%^s writhes in agony.", m_name); else msgf("%^s jerks limply.", m_name); } /* Felines */ else if (strchr("f", r_ptr->d_char)) { if (percentage > 95) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 75) msgf("%^s roars.", m_name); else if (percentage > 50) msgf("%^s growls angrily.", m_name); else if (percentage > 35) msgf("%^s hisses with pain.", m_name); else if (percentage > 20) msgf("%^s mewls in pain.", m_name); else if (percentage > 10) msgf("%^s hisses in agony.", m_name); else msgf("%^s mewls pitifully.", m_name); } /* Ants, Centipedes, Flies, Insects, Beetles, Spiders */ else if (strchr("acFIKS", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s chitters.", m_name); else if (percentage > 50) msgf("%^s scuttles about.", m_name); else if (percentage > 35) msgf("%^s twitters.", m_name); else if (percentage > 20) msgf("%^s jerks in pain.", m_name); else if (percentage > 10) msgf("%^s jerks in agony.", m_name); else msgf("%^s twitches.", m_name); } /* Birds */ else if (strchr("B", r_ptr->d_char)) { if (percentage > 95) msgf("%^s chirps.", m_name); else if (percentage > 75) msgf("%^s twitters.", m_name); else if (percentage > 50) msgf("%^s squawks.", m_name); else if (percentage > 35) msgf("%^s chatters.", m_name); else if (percentage > 20) msgf("%^s jeers.", m_name); else if (percentage > 10) msgf("%^s flutters about.", m_name); else msgf("%^s squeaks.", m_name); } /* Dragons, Demons, High Undead */ else if (strchr("duDLUW", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s flinches.", m_name); else if (percentage > 50) msgf("%^s hisses in pain.", m_name); else if (percentage > 35) msgf("%^s snarls with pain.", m_name); else if (percentage > 20) msgf("%^s roars with pain.", m_name); else if (percentage > 10) msgf("%^s gasps.", m_name); else msgf("%^s snarls feebly.", m_name); } /* Skeletons */ else if (strchr("s", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 50) msgf("%^s rattles.", m_name); else if (percentage > 35) msgf("%^s stumbles.", m_name); else if (percentage > 20) msgf("%^s rattles.", m_name); else if (percentage > 10) msgf("%^s staggers.", m_name); else msgf("%^s clatters.", m_name); } /* Zombies */ else if (strchr("z", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 50) msgf("%^s groans.", m_name); else if (percentage > 35) msgf("%^s moans.", m_name); else if (percentage > 20) msgf("%^s hesitates.", m_name); else if (percentage > 10) msgf("%^s grunts.", m_name); else msgf("%^s staggers.", m_name); } /* Ghosts */ else if (strchr("G", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 50) msgf("%^s moans.", m_name); else if (percentage > 35) msgf("%^s wails.", m_name); else if (percentage > 20) msgf("%^s howls.", m_name); else if (percentage > 10) msgf("%^s moans softly.", m_name); else msgf("%^s sighs.", m_name); } /* Dogs and Hounds */ else if (strchr("CZ", r_ptr->d_char)) { if (percentage > 95) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 75) msgf("%^s snarls with pain.", m_name); else if (percentage > 50) msgf("%^s yelps in pain.", m_name); else if (percentage > 35) msgf("%^s howls in pain.", m_name); else if (percentage > 20) msgf("%^s howls in agony.", m_name); else if (percentage > 10) msgf("%^s writhes in agony.", m_name); else msgf("%^s yelps feebly.", m_name); } /* One type of monsters (ignore,squeal,shriek) */ else if (strchr("Xbilqrt", r_ptr->d_char)) { if (percentage > 95) msgf("%^s ignores the attack.", m_name); else if (percentage > 75) msgf("%^s grunts with pain.", m_name); else if (percentage > 50) msgf("%^s squeals in pain.", m_name); else if (percentage > 35) msgf("%^s shrieks in pain.", m_name); else if (percentage > 20) msgf("%^s shrieks in agony.", m_name); else if (percentage > 10) msgf("%^s writhes in agony.", m_name); else msgf("%^s cries out feebly.", m_name); } /* Another type of monsters (shrug,cry,scream) */ else { if (percentage > 95) msgf("%^s shrugs off the attack.", m_name); else if (percentage > 75) msgf("%^s grunts with pain.", m_name); else if (percentage > 50) msgf("%^s cries out in pain.", m_name); else if (percentage > 35) msgf("%^s screams in pain.", m_name); else if (percentage > 20) msgf("%^s screams in agony.", m_name); else if (percentage > 10) msgf("%^s writhes in agony.", m_name); else msgf("%^s cries out feebly.", m_name); } } /* * Learn about an "observed" resistance. */ void update_smart_learn(int m_idx, int what) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Too stupid to learn anything */ if (FLAG(r_ptr, RF_STUPID)) return; /* Not intelligent, only learn sometimes */ if (!FLAG(r_ptr, RF_SMART) && (randint0(100) < 50)) return; /* XXX XXX XXX */ /* Analyze the knowledge */ switch (what) { case DRS_ACID: { if (FLAG(p_ptr, TR_RES_ACID)) m_ptr->smart |= (SM_RES_ACID); if (p_ptr->tim.oppose_acid) m_ptr->smart |= (SM_OPP_ACID); if (FLAG(p_ptr, TR_IM_ACID)) m_ptr->smart |= (SM_IMM_ACID); break; } case DRS_ELEC: { if (FLAG(p_ptr, TR_RES_ELEC)) m_ptr->smart |= (SM_RES_ELEC); if (p_ptr->tim.oppose_elec) m_ptr->smart |= (SM_OPP_ELEC); if (FLAG(p_ptr, TR_IM_ELEC)) m_ptr->smart |= (SM_IMM_ELEC); break; } case DRS_FIRE: { if (FLAG(p_ptr, TR_RES_FIRE)) m_ptr->smart |= (SM_RES_FIRE); if (p_ptr->tim.oppose_fire) m_ptr->smart |= (SM_OPP_FIRE); if (FLAG(p_ptr, TR_IM_FIRE)) m_ptr->smart |= (SM_IMM_FIRE); break; } case DRS_COLD: { if (FLAG(p_ptr, TR_RES_COLD)) m_ptr->smart |= (SM_RES_COLD); if (p_ptr->tim.oppose_cold) m_ptr->smart |= (SM_OPP_COLD); if (FLAG(p_ptr, TR_IM_COLD)) m_ptr->smart |= (SM_IMM_COLD); break; } case DRS_POIS: { if (FLAG(p_ptr, TR_RES_POIS)) m_ptr->smart |= (SM_RES_POIS); if (p_ptr->tim.oppose_pois) m_ptr->smart |= (SM_OPP_POIS); break; } case DRS_NETH: { if (FLAG(p_ptr, TR_RES_NETHER)) m_ptr->smart |= (SM_RES_NETH); break; } case DRS_LITE: { if (FLAG(p_ptr, TR_RES_LITE)) m_ptr->smart |= (SM_RES_LITE); break; } case DRS_DARK: { if (FLAG(p_ptr, TR_RES_DARK)) m_ptr->smart |= (SM_RES_DARK); break; } case DRS_FEAR: { if (FLAG(p_ptr, TR_RES_FEAR)) m_ptr->smart |= (SM_RES_FEAR); break; } case DRS_CONF: { if (FLAG(p_ptr, TR_RES_CONF)) m_ptr->smart |= (SM_RES_CONF); break; } case DRS_CHAOS: { if (FLAG(p_ptr, TR_RES_CHAOS)) m_ptr->smart |= (SM_RES_CHAOS); break; } case DRS_DISEN: { if (FLAG(p_ptr, TR_RES_DISEN)) m_ptr->smart |= (SM_RES_DISEN); break; } case DRS_BLIND: { if (FLAG(p_ptr, TR_RES_BLIND)) m_ptr->smart |= (SM_RES_BLIND); break; } case DRS_NEXUS: { if (FLAG(p_ptr, TR_RES_NEXUS)) m_ptr->smart |= (SM_RES_NEXUS); break; } case DRS_SOUND: { if (FLAG(p_ptr, TR_RES_SOUND)) m_ptr->smart |= (SM_RES_SOUND); break; } case DRS_SHARD: { if (FLAG(p_ptr, TR_RES_SHARDS)) m_ptr->smart |= (SM_RES_SHARD); break; } case DRS_FREE: { if (FLAG(p_ptr, TR_FREE_ACT)) m_ptr->smart |= (SM_IMM_FREE); break; } case DRS_MANA: { if (!p_ptr->msp) m_ptr->smart |= (SM_IMM_MANA); break; } case DRS_REFLECT: { if (FLAG(p_ptr, TR_REFLECT)) m_ptr->smart |= (SM_IMM_REFLECT); break; } } } zangband/src/mspells1.c0000755000000000000000000015373610250356274014061 0ustar rootroot/* File: mspells1.c */ /* Purpose: Monster spells (attack player) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * And now for Intelligent monster attacks (including spells). * * Original idea and code by "DRS" (David Reeve Sward). * Major modifications by "BEN" (Ben Harrison). * * Give monsters more intelligent attack/spell selection based on * observations of previous attacks on the player, and/or by allowing * the monster to "cheat" and know the player status. * * Maintain an idea of the player status, and use that information * to occasionally eliminate "ineffective" spell attacks. We could * also eliminate ineffective normal attacks, but there is no reason * for the monster to do this, since he gains no benefit. * Note that MINDLESS monsters are not allowed to use this code. * And non-INTELLIGENT monsters only use it partially effectively. * * Actually learn what the player resists, and use that information * to remove attacks or spells before using them. This will require * much less space, if I am not mistaken. Thus, each monster gets a * set of 32 bit flags, "smart", build from the various "SM_*" flags. * * This has the added advantage that attacks and spells are related. * The "smart_learn" option means that the monster "learns" the flags * that should be set over time, by trial and error. This is no longer * optional, and is on all the time. * * The old smart_cheat option was removed. No one plays with it on. */ /* * Internal probability routine */ #define int_outof(dumb, prob) \ (randint1((dumb) ? ((prob) / 2) : (prob)) < 100) /* * Remove the "bad" spells from a spell list */ static void remove_bad_spells(int m_idx, u32b *f4p, u32b *f5p, u32b *f6p) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; u32b f4 = (*f4p); u32b f5 = (*f5p); u32b f6 = (*f6p); u32b smart = 0L; bool is_dumb = (!FLAG(r_ptr, RF_SMART)); /* Too stupid to know anything */ if (FLAG(r_ptr, RF_STUPID)) return; /* Update acquired knowledge */ /* Hack -- Occasionally forget player status */ if (m_ptr->smart && one_in_(100)) m_ptr->smart = 0L; /* Use the memorized flags */ smart = m_ptr->smart; /* Nothing known */ if (!smart) return; /* * Hack - some of the RNG calls have been removed from the * earlier code. This should speed it up. */ if ((smart & SM_IMM_ACID) && (int_outof(is_dumb, 100))) { f4 &= ~(RF3_BR_ACID); f5 &= ~(RF4_BA_ACID | RF4_BO_ACID); } else if ((smart & (SM_OPP_ACID)) && (smart & (SM_RES_ACID)) && (int_outof(is_dumb, 80))) { f4 &= ~(RF3_BR_ACID); f5 &= ~(RF4_BA_ACID | RF4_BO_ACID); } else if (((smart & (SM_OPP_ACID)) || (smart & (SM_RES_ACID))) && (int_outof(is_dumb, 30))) { f4 &= ~(RF3_BR_ACID); f5 &= ~(RF4_BA_ACID | RF4_BO_ACID); } if ((smart & (SM_IMM_ELEC)) && (int_outof(is_dumb, 100))) { f4 &= ~(RF3_BR_ELEC); f5 &= ~(RF4_BA_ELEC | RF4_BO_ELEC); } else if ((smart & (SM_OPP_ELEC)) && (smart & (SM_RES_ELEC)) && (int_outof(is_dumb, 80))) { f4 &= ~(RF3_BR_ELEC); f5 &= ~(RF4_BA_ELEC | RF4_BO_ELEC); } else if (((smart & (SM_OPP_ELEC)) || (smart & (SM_RES_ELEC))) && (int_outof(is_dumb, 30))) { f4 &= ~(RF3_BR_ELEC); f5 &= ~(RF4_BA_ELEC | RF4_BO_ELEC); } if ((smart & (SM_IMM_FIRE)) && (int_outof(is_dumb, 100))) { f4 &= ~(RF3_BR_FIRE); f5 &= ~(RF4_BA_FIRE | RF4_BO_FIRE); } else if ((smart & (SM_OPP_FIRE)) && (smart & (SM_RES_FIRE)) && (int_outof(is_dumb, 80))) { f4 &= ~(RF3_BR_FIRE); f5 &= ~(RF4_BA_FIRE | RF4_BO_FIRE); } else if (((smart & (SM_OPP_FIRE)) || (smart & (SM_RES_FIRE))) && (int_outof(is_dumb, 30))) { f4 &= ~(RF3_BR_FIRE); f5 &= ~(RF4_BA_FIRE | RF4_BO_FIRE); } if ((smart & (SM_IMM_COLD)) && (int_outof(is_dumb, 100))) { f4 &= ~(RF3_BR_COLD); f5 &= ~(RF4_BA_COLD | RF4_BO_COLD | RF4_BO_ICEE); } else if ((smart & (SM_OPP_COLD)) && (smart & (SM_RES_COLD)) && (int_outof(is_dumb, 80))) { f4 &= ~(RF3_BR_COLD); f5 &= ~(RF4_BA_COLD | RF4_BO_COLD | RF4_BO_ICEE); } else if (((smart & (SM_OPP_COLD)) || (smart & (SM_RES_COLD))) && (int_outof(is_dumb, 30))) { f4 &= ~(RF3_BR_COLD); f5 &= ~(RF4_BA_COLD | RF4_BO_COLD | RF4_BO_ICEE); } if ((smart & (SM_OPP_POIS)) && (smart & (SM_RES_POIS)) && (int_outof(is_dumb, 80))) { f4 &= ~(RF3_BR_POIS); f5 &= ~(RF4_BA_POIS); if (one_in_(2)) { f4 &= ~(RF3_BA_NUKE | RF3_BR_NUKE); } } else if (((smart & (SM_OPP_POIS)) || (smart & (SM_RES_POIS))) && (int_outof(is_dumb, 30))) { f4 &= ~(RF3_BR_POIS); f5 &= ~(RF4_BA_POIS); } if ((smart & (SM_RES_NETH)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_NETH); f5 &= ~(RF4_BA_NETH | RF4_BO_NETH); } if ((f4 & (RF3_BR_LITE)) && (smart & (SM_RES_LITE)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_LITE); } if ((smart & (SM_RES_DARK)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_DARK); f5 &= ~(RF4_BA_DARK); } if ((f5 & (RF4_SCARE)) && (smart & (SM_RES_FEAR)) && (int_outof(is_dumb, 100))) { f5 &= ~(RF4_SCARE); } if ((smart & (SM_RES_CONF)) && (int_outof(is_dumb, 100))) { f5 &= ~(RF4_CONF); if (one_in_(2)) { f4 &= ~(RF3_BR_CONF); } } if ((smart & (SM_RES_CHAOS)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_CHAO | RF3_BA_CHAO); } if ((f4 & (RF3_BR_DISE)) && (smart & (SM_RES_DISEN)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_DISE); } if ((f5 & (RF4_BLIND)) && (smart & (SM_RES_BLIND)) && (int_outof(is_dumb, 100))) { f5 &= ~(RF4_BLIND); } if ((smart & (SM_RES_NEXUS)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_NEXU); f6 &= ~(RF5_TELE_LEVEL); } if ((f4 & (RF3_BR_SOUN)) && (smart & (SM_RES_SOUND)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_SOUN); } if ((smart & (SM_RES_SHARD)) && (int_outof(is_dumb, 50))) { f4 &= ~(RF3_BR_SHAR); if (one_in_(2)) { f4 &= ~(RF3_ROCKET); } } if ((smart & (SM_IMM_REFLECT)) && (int_outof(is_dumb, 100))) { f5 &= ~(RF4_BO_COLD | RF4_BO_FIRE | RF4_BO_ACID | RF4_BO_ELEC | RF4_BO_POIS | RF4_BO_NETH | RF4_BO_WATE | RF4_BO_MANA | RF4_BO_PLAS | RF4_BO_ICEE | RF4_MISSILE); f4 &= ~(RF3_ARROW); } if ((smart & (SM_IMM_FREE)) && (int_outof(is_dumb, 100))) { f5 &= ~(RF4_HOLD | RF4_SLOW); } if ((f5 & (RF4_DRAIN_MANA)) && (smart & (SM_IMM_MANA)) && (int_outof(is_dumb, 100))) { f5 &= ~(RF4_DRAIN_MANA); } /* XXX XXX XXX No spells left? */ /* if (!f4 && !f5 && !f6) ... */ (*f4p) = f4; (*f5p) = f5; (*f6p) = f6; } /* * Determine if there is a space near the player in which * a summoned creature can appear */ static bool summon_possible(int x1, int y1) { int y, x; int dy, dx; cave_type *c_ptr; /* Start at the player's location, and check 2 grids in each dir */ for (dy = -2; dy <= 2; dy++) { for (dx = -2; dx <= 2; dx++) { /* Only check a circular area */ if ((ABS(dx) == 2) && (ABS(dy) == 2)) continue; /* Not on top of player */ if ((dx == 0) && (dy == 0)) continue; /* Get square */ x = x1 + dx; y = y1 + dy; /* Ignore illegal locations */ if (!in_boundsp(x, y)) continue; /* Access Grid */ c_ptr = area(x, y); /* ...nor on the Pattern */ if (cave_perma_grid(c_ptr)) continue; /* Check to see if fields dissallow placement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) { /* Cannot create */ return (FALSE); } /* * Test for fields that will not allow monsters to * be generated on them. (i.e. Glyph of warding) */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MPLACE)) return (FALSE); /* Require empty floor grid in line of sight of player */ if (cave_empty_grid(c_ptr) && (player_has_los_grid(parea(x, y)))) return (TRUE); } } return FALSE; } /* * Determine if a bolt spell will hit the player. * * This is exactly like "projectable", but it will * return FALSE if a monster is in the way. * no equally friendly monster is * between the attacker and target. * * This change has been implelemented via a flag - much quicker * and simpler than before. * * Originally, it was possible for a friendly to shoot another friendly. * Change it so a "clean shot" means no equally friendly monster is * between the attacker and target. * * This must be the same as projectable(). */ bool clean_shot(int x1, int y1, int x2, int y2, bool friendly) { int grid_n; coord grid_g[512]; u16b flg; /* Try not to hit friends. */ if (friendly) { flg = PROJECT_FRND; } else { flg = PROJECT_STOP; } /* Check the projection path - endpoints reversed */ grid_n = project_path(grid_g, x2, y2, x1, y1, flg); /* No grid is ever projectable from itself */ if (!grid_n) return (FALSE); /* May not end in an unrequested grid */ if ((grid_g[grid_n - 1].y != y1) || (grid_g[grid_n - 1].x != x1)) return (FALSE); return (TRUE); } /* * Cast a bolt at the player * Stop if we hit a monster * Affect monsters and the player */ static void bolt(int m_idx, int typ, int dam_hp) { int py = p_ptr->py; int px = p_ptr->px; u16b flg = PROJECT_STOP | PROJECT_KILL; /* Target the player with a bolt attack */ (void)project(m_idx, 0, px, py, dam_hp, typ, flg); } /* * Cast a breath (or ball) attack at the player * Pass over any monsters that may be in the way * Affect grids, objects, monsters, and the player */ static void breath(int m_idx, int typ, int dam_hp, int rad, bool breath) { int py = p_ptr->py; int px = p_ptr->px; u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Determine the radius of the blast */ if (rad < 1) rad = FLAG(r_ptr, RF_POWERFUL) ? 3 : 2; /* Handle breath attacks */ if (breath) rad = 0 - rad; /* Target the player with a ball attack */ (void)project(m_idx, rad, px, py, dam_hp, typ, flg); } void curse_equipment(int chance, int heavy_chance) { bool changed = FALSE; object_type *o_ptr = &p_ptr->equipment[randint0(EQUIP_MAX)]; /* Ow! */ if (FLAG(p_ptr, TR_STRANGE_LUCK)) { chance = chance * 2; heavy_chance = heavy_chance * 2; } if (randint1(100) > chance) return; if (!o_ptr->k_idx) return; /* Extra, biased saving throw for blessed items */ if ((FLAG(o_ptr, TR_BLESSED)) && (randint1(888) > chance)) { msgf("Your %v resists cursing!", OBJECT_FMT(o_ptr, FALSE, 0)); return; } if ((randint1(100) <= heavy_chance) && o_ptr->xtra_name) { if (!(FLAG(o_ptr, TR_HEAVY_CURSE))) { changed = TRUE; } SET_FLAG(o_ptr, TR_HEAVY_CURSE); SET_FLAG(o_ptr, TR_CURSED); } else { if (!cursed_p(o_ptr)) { changed = TRUE; } SET_FLAG(o_ptr, TR_CURSED); } if (changed) { msgf("There is a malignant black aura surrounding you..."); o_ptr->feeling = FEEL_NONE; } } /* * Have a monster choose a spell from a list of "useful" spells. * * Note that this list does NOT include spells that will just hit * other monsters, and the list is restricted when the monster is * "desperate". Should that be the job of this function instead? * * Stupid monsters will just pick a spell randomly. Smart monsters * will choose more "intelligently". * * Use the helper functions above to put spells into categories. * * This function may well be an efficiency bottleneck. */ static int choose_attack_spell(int m_idx, u32b f4, u32b f5, u32b f6) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; u32b f4_mask = 0L; u32b f5_mask = 0L; u32b f6_mask = 0L; bool has_escape, has_attack, has_summon, has_tactic; bool has_annoy, has_invul, has_haste, has_heal; int num = 0; byte spells[96]; int i; /* Smart monsters restrict their spell choices. */ if (!stupid_monsters && !FLAG(r_ptr, RF_STUPID)) { /* What have we got? */ has_escape = ((f4 & (RF3_ESCAPE_MASK)) || (f5 & (RF4_ESCAPE_MASK)) || (f6 & (RF5_ESCAPE_MASK))); has_attack = ((f4 & (RF3_ATTACK_MASK)) || (f5 & (RF4_ATTACK_MASK)) || (f6 & (RF5_ATTACK_MASK))); has_summon = ((f4 & (RF3_SUMMON_MASK)) || (f5 & (RF4_SUMMON_MASK)) || (f6 & (RF5_SUMMON_MASK))); has_tactic = ((f4 & (RF3_TACTIC_MASK)) || (f5 & (RF4_TACTIC_MASK)) || (f6 & (RF5_TACTIC_MASK))); has_annoy = ((f4 & (RF3_ANNOY_MASK)) || (f5 & (RF4_ANNOY_MASK)) || (f6 & (RF5_ANNOY_MASK))); has_invul = ((f4 & (RF3_INVULN_MASK)) || (f5 & (RF4_INVULN_MASK)) || (f6 & (RF5_INVULN_MASK))); has_haste = ((f4 & (RF3_HASTE_MASK)) || (f5 & (RF4_HASTE_MASK)) || (f6 & (RF5_HASTE_MASK))); has_heal = ((f4 & (RF3_HEAL_MASK)) || (f5 & (RF4_HEAL_MASK)) || (f6 & (RF5_HEAL_MASK))); /*** Try to pick an appropriate spell type ***/ /* Hurt badly or afraid, attempt to flee */ if (has_escape && ((m_ptr->hp < m_ptr->maxhp / 4) || m_ptr->monfear) && one_in_(2)) { /* Choose escape spell */ f4_mask = (RF3_ESCAPE_MASK); f5_mask = (RF4_ESCAPE_MASK); f6_mask = (RF5_ESCAPE_MASK); } /* Still hurt badly, couldn't flee, attempt to heal */ else if (has_heal && (m_ptr->hp < m_ptr->maxhp / 4) && one_in_(2)) { /* Choose heal spell */ f4_mask = (RF3_HEAL_MASK); f5_mask = (RF4_HEAL_MASK); f6_mask = (RF5_HEAL_MASK); } /* Player is close and we have attack spells, blink away */ else if (has_tactic && (m_ptr->cdis < 4) && has_attack && (randint0(100) < 75)) { /* Choose tactical spell */ f4_mask = (RF3_TACTIC_MASK); f5_mask = (RF4_TACTIC_MASK); f6_mask = (RF5_TACTIC_MASK); } /* We're hurt (not badly), try to heal */ else if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (randint0(100) < 60)) { /* Choose heal spell */ f4_mask = (RF3_HEAL_MASK); f5_mask = (RF4_HEAL_MASK); f6_mask = (RF5_HEAL_MASK); } /* Summon if possible (sometimes) */ else if (has_summon && one_in_(2)) { /* Choose summon spell */ f4_mask = (RF3_SUMMON_MASK); f5_mask = (RF4_SUMMON_MASK); f6_mask = (RF5_SUMMON_MASK); } /* Attack spell (most of the time) */ else if (has_attack && (randint0(100) < 85)) { /* Choose attack spell */ f4_mask = (RF3_ATTACK_MASK); f5_mask = (RF4_ATTACK_MASK); f6_mask = (RF5_ATTACK_MASK); } /* Try another tactical spell (sometimes) */ else if (has_tactic && one_in_(2)) { /* Choose tactic spell */ f4_mask = (RF3_TACTIC_MASK); f5_mask = (RF4_TACTIC_MASK); f6_mask = (RF5_TACTIC_MASK); } /* Cast globe of invulnerability if not already in effect */ else if (has_invul && !(m_ptr->invulner) && one_in_(2)) { /* Choose Globe of Invulnerability */ f4_mask = (RF3_INVULN_MASK); f5_mask = (RF4_INVULN_MASK); f6_mask = (RF5_INVULN_MASK); } /* Haste self if we aren't already somewhat hasted (rarely) */ else if (has_haste && (randint0(100) < (20 + r_ptr->speed - m_ptr->mspeed))) { /* Choose haste spell */ f4_mask = (RF3_HASTE_MASK); f5_mask = (RF4_HASTE_MASK); f6_mask = (RF5_HASTE_MASK); } /* Annoy player (most of the time) */ else if (has_annoy && (randint0(100) < 85)) { /* Choose annoyance spell */ f4_mask = (RF3_ANNOY_MASK); f5_mask = (RF4_ANNOY_MASK); f6_mask = (RF5_ANNOY_MASK); } /* Else choose no spell (The masks default to this.) */ /* Keep only the interesting spells */ f4 &= f4_mask; f5 &= f5_mask; f6 &= f6_mask; /* Anything left? */ if (!(f4 || f5 || f6)) return (0); } /* Extract the "innate" spells */ for (i = 0; i < 32; i++) { if (f4 & (1L << i)) spells[num++] = i + 32 * 3; } /* Extract the "normal" spells */ for (i = 0; i < 32; i++) { if (f5 & (1L << i)) spells[num++] = i + 32 * 4; } /* Extract the "bizarre" spells */ for (i = 0; i < 32; i++) { if (f6 & (1L << i)) spells[num++] = i + 32 * 5; } /* Paranoia */ if (num == 0) return 0; /* Pick at random */ return (spells[randint0(num)]); } /* * Creatures can cast spells, shoot missiles, and breathe. * * Returns "TRUE" if a spell (or whatever) was (successfully) cast. * * XXX XXX XXX This function could use some work, but remember to * keep it as optimized as possible, while retaining generic code. * * Verify the various "blind-ness" checks in the code. * * XXX XXX XXX Note that several effects should really not be "seen" * if the player is blind. See also "effects.c" for other "mistakes". * * Perhaps monsters should breathe at locations *near* the player, * since this would allow them to inflict "partial" damage. * * Perhaps smart monsters should decline to use "bolt" spells if * there is a monster in the way, unless they wish to kill it. * * Note that, to allow the use of the "track_target" option at some * later time, certain non-optimal things are done in the code below, * including explicit checks against the "direct" variable, which is * currently always true by the time it is checked, but which should * really be set according to an explicit "projectable()" test, and * the use of generic "x,y" locations instead of the player location, * with those values being initialized with the player location. * * It will not be possible to "correctly" handle the case in which a * monster attempts to attack a location which is thought to contain * the player, but which in fact is nowhere near the player, since this * might induce all sorts of messages about the attack itself, and about * the effects of the attack, which the player might or might not be in * a position to observe. Thus, for simplicity, it is probably best to * only allow "faulty" attacks by a monster if one of the important grids * (probably the initial or final grid) is in fact in view of the player. * It may be necessary to actually prevent spell attacks except when the * monster actually has line of sight to the player. Note that a monster * could be left in a bizarre situation after the player ducked behind a * pillar and then teleported away, for example. * * Note that certain spell attacks do not use the "project()" function * but "simulate" it via the "direct" variable, which is always at least * as restrictive as the "project()" function. This is necessary to * prevent "blindness" attacks and such from bending around walls, etc, * and to allow the use of the "track_target" option in the future. * * Note that this function attempts to optimize the use of spells for the * cases in which the monster has no spells, or has spells but cannot use * them, or has spells but they will have no "useful" effect. Note that * this function has been an efficiency bottleneck in the past. * * Note the special "MFLAG_NICE" flag, which prevents a monster from using * any spell attacks until the player has had a single chance to move. */ bool make_attack_spell(int m_idx) { int py = p_ptr->py; int px = p_ptr->px; int k, chance, thrown_spell, rlev, failrate; u32b f4, f5, f6; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80]; char m_poss[80]; char ddesc[80]; /* Saving throw difficulty */ int power = r_ptr->hdice * 2; /* Target location */ int x = px; int y = py; /* Summon count */ int count = 0; /* Extract the blind-ness */ bool blind = (p_ptr->tim.blind ? TRUE : FALSE); /* Extract the "see-able-ness" */ bool seen = (!blind && m_ptr->ml); /* Assume "normal" target */ bool normal = TRUE; /* Assume "projectable" */ bool direct = TRUE; int i; /* Cannot cast spells when confused */ if (m_ptr->confused) return (FALSE); /* Cannot cast spells when nice */ if (m_ptr->mflag & MFLAG_NICE) return (FALSE); if (!is_hostile(m_ptr)) return (FALSE); /* Hack -- Extract the spell probability */ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2; /* Not allowed to cast spells */ if (!chance) return (FALSE); /* Stop if player is dead or gone */ if (!p_ptr->state.playing || p_ptr->state.is_dead) return (FALSE); /* Stop if player is leaving */ if (p_ptr->state.leaving) return (FALSE); /* Only do spells occasionally */ if (randint0(100) >= chance) return (FALSE); /* XXX XXX XXX Handle "track_target" option (?) */ /* Hack -- require projectable player */ if (normal) { /* Check range */ if (m_ptr->cdis > MAX_RANGE) return (FALSE); /* Check path */ if (!projectable(m_ptr->fx, m_ptr->fy, px, py)) return (FALSE); } /* Extract the racial spell flags */ f4 = r_ptr->flags[3]; f5 = r_ptr->flags[4]; f6 = r_ptr->flags[5]; /* Hack -- allow "desperate" spells */ if (FLAG(r_ptr, RF_SMART) && (m_ptr->hp < m_ptr->maxhp / 10) && one_in_(2)) { /* Require intelligent spells */ f4 &= (RF3_INT_MASK); f5 &= (RF4_INT_MASK); f6 &= (RF5_INT_MASK); /* No spells left */ if (!f4 && !f5 && !f6) return (FALSE); } /* Remove the "ineffective" spells */ remove_bad_spells(m_idx, &f4, &f5, &f6); /* No spells left */ if (!f4 && !f5 && !f6) return (FALSE); if (!stupid_monsters) { /* Check for a clean bolt shot */ if (((f4 & RF3_BOLT_MASK) || (f5 & RF4_BOLT_MASK) || (f6 & RF5_BOLT_MASK)) && !FLAG(r_ptr, RF_STUPID) && !clean_shot(m_ptr->fx, m_ptr->fy, px, py, FALSE)) { /* Remove spells that will only hurt friends */ f4 &= ~(RF3_BOLT_MASK); f5 &= ~(RF4_BOLT_MASK); f6 &= ~(RF5_BOLT_MASK); } /* Check for a possible summon */ if (((f4 & RF3_SUMMON_MASK) || (f5 & RF4_SUMMON_MASK) || (f6 & RF5_SUMMON_MASK)) && !FLAG(r_ptr, RF_STUPID) && !(summon_possible(px, py))) { /* Remove summoning spells */ f4 &= ~(RF3_SUMMON_MASK); f5 &= ~(RF4_SUMMON_MASK); f6 &= ~(RF5_SUMMON_MASK); } /* No spells left */ if (!f4 && !f5 && !f6) return (FALSE); } /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0x00, 80); /* Get the monster possessive ("his"/"her"/"its") */ monster_desc(m_poss, m_ptr, 0x22, 80); /* Hack -- Get the "died from" name */ monster_desc(ddesc, m_ptr, 0x88, 80); thrown_spell = choose_attack_spell(m_idx, f4, f5, f6); /* Abort if no spell was chosen */ if (!thrown_spell) return (FALSE); /* Extract the monster level */ rlev = r_ptr->hdice * 2; /* Calculate spell failure rate */ failrate = 25 - (r_ptr->hdice + 1) / 2; /* Hack -- Stupid monsters will never fail (for jellies and such) */ if (FLAG(r_ptr, RF_STUPID)) failrate = 0; /* Check for spell failure (inate attacks never fail) */ if ((thrown_spell >= 128) && (randint0(100) < failrate)) { /* Message */ msgf("%^s tries to cast a spell, but fails.", m_name); return (TRUE); } /* Cast the spell. */ switch (thrown_spell) { case 96 + 0: { /* RF3_SHRIEK */ if (!direct) break; disturb(TRUE); msgf("%^s makes a high pitched shriek.", m_name); aggravate_monsters(m_idx); break; } case 96 + 1: { /* RF3_ELDRITCH_HORROR */ if (!direct || !seen) break; /* What's another gibbering monstrosity or two? */ if (p_ptr->muta2 & MUT2_HALLU) break; disturb(TRUE); sanity_blast(m_ptr); break; } case 96 + 2: { /* RF3_XXX3X4 */ break; } case 96 + 3: { /* RF3_XXX4X4 */ disturb(TRUE); if (blind) msgf("%^s shoots something.", m_name); else msgf("%^s fires a rocket.", m_name); breath(m_idx, GF_ROCKET, ((m_ptr->hp / 4) > 600 ? 600 : (m_ptr->hp / 4)), 2, FALSE); update_smart_learn(m_idx, DRS_SHARD); break; } case 96 + 4: { int dice = r_ptr->hdice < 4 ? 1 : r_ptr->hdice / 4; if (dice > 7) dice = 7; /* RF3_ARROW */ disturb(TRUE); if (blind) msgf("%^s makes a strange noise.", m_name); else msgf("%^s fires an arrow.", m_name); bolt(m_idx, GF_ARROW, damroll(dice, 6)); update_smart_learn(m_idx, DRS_REFLECT); break; } case 96 + 5: { /* RF3_XXX6X4 */ break; } case 96 + 6: { /* RF3_XXX7X4 */ break; } case 96 + 7: { /* RF3_XXX8X4 */ break; } case 96 + 8: { /* RF3_BR_ACID */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes acid.", m_name); breath(m_idx, GF_ACID, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_ACID); break; } case 96 + 9: { /* RF3_BR_ELEC */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes lightning.", m_name); breath(m_idx, GF_ELEC, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_ELEC); break; } case 96 + 10: { /* RF3_BR_FIRE */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes fire.", m_name); breath(m_idx, GF_FIRE, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_FIRE); break; } case 96 + 11: { /* RF3_BR_COLD */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes frost.", m_name); breath(m_idx, GF_COLD, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_COLD); break; } case 96 + 12: { /* RF3_BR_POIS */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes gas.", m_name); breath(m_idx, GF_POIS, ((m_ptr->hp / 2) > 600 ? 600 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_POIS); break; } case 96 + 13: { /* RF3_BR_NETH */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes nether.", m_name); breath(m_idx, GF_NETHER, ((m_ptr->hp / 4) > 450 ? 450 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_NETH); break; } case 96 + 14: { /* RF3_BR_LITE */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes light.", m_name); breath(m_idx, GF_LITE, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_LITE); break; } case 96 + 15: { /* RF3_BR_DARK */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes darkness.", m_name); breath(m_idx, GF_DARK, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_DARK); break; } case 96 + 16: { /* RF3_BR_CONF */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes confusion.", m_name); breath(m_idx, GF_CONFUSION, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_CONF); break; } case 96 + 17: { /* RF3_BR_SOUN */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes sound.", m_name); breath(m_idx, GF_SOUND, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_SOUND); break; } case 96 + 18: { /* RF3_BR_CHAO */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes chaos.", m_name); breath(m_idx, GF_CHAOS, ((m_ptr->hp / 4) > 500 ? 500 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_CHAOS); break; } case 96 + 19: { /* RF3_BR_DISE */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes disenchantment.", m_name); breath(m_idx, GF_DISENCHANT, ((m_ptr->hp / 4) > 400 ? 400 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_DISEN); break; } case 96 + 20: { /* RF3_BR_NEXU */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes nexus.", m_name); breath(m_idx, GF_NEXUS, ((m_ptr->hp / 2) > 250 ? 250 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_NEXUS); break; } case 96 + 21: { /* RF3_BR_TIME */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes time.", m_name); breath(m_idx, GF_TIME, ((m_ptr->hp / 2) > 150 ? 150 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 22: { /* RF3_BR_INER */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes inertia.", m_name); breath(m_idx, GF_INERTIA, ((m_ptr->hp / 4) > 200 ? 200 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 23: { /* RF3_BR_GRAV */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes gravity.", m_name); breath(m_idx, GF_GRAVITY, ((m_ptr->hp / 2) > 200 ? 200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 24: { /* RF3_BR_SHAR */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes shards.", m_name); breath(m_idx, GF_SHARDS, ((m_ptr->hp / 4) > 400 ? 400 : (m_ptr->hp / 4)), 0, TRUE); update_smart_learn(m_idx, DRS_SHARD); break; } case 96 + 25: { /* RF3_BR_PLAS */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes plasma.", m_name); breath(m_idx, GF_PLASMA, ((m_ptr->hp / 4) > 150 ? 150 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 26: { /* RF3_BR_WALL */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes force.", m_name); breath(m_idx, GF_FORCE, ((m_ptr->hp / 4) > 200 ? 200 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 27: { /* RF3_BR_MANA */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes mana.", m_name); breath(m_idx, GF_MANA, ((m_ptr->hp / 2) > 200 ? 200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 28: { /* RF3_BA_NUKE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a ball of radiation.", m_name); breath(m_idx, GF_NUKE, (r_ptr->hdice * 2 + damroll(10, 6)), 2, FALSE); update_smart_learn(m_idx, DRS_POIS); break; } case 96 + 29: { /* RF3_BR_NUKE */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes toxic waste.", m_name); breath(m_idx, GF_NUKE, ((m_ptr->hp / 2) > 600 ? 600 : (m_ptr->hp / 2)), 0, TRUE); update_smart_learn(m_idx, DRS_POIS); break; } case 96 + 30: { /* RF3_BA_CHAO */ disturb(TRUE); if (blind) msgf("%^s mumbles frighteningly.", m_name); else msgf("%^s invokes raw Logrus.", m_name); breath(m_idx, GF_CHAOS, (r_ptr->hdice * 4) + damroll(10, 10), 4, FALSE); update_smart_learn(m_idx, DRS_CHAOS); break; } case 96 + 31: { /* RF3_BR_DISI */ disturb(TRUE); if (blind) msgf("%^s breathes.", m_name); else msgf("%^s breathes disintegration.", m_name); breath(m_idx, GF_DISINTEGRATE, ((m_ptr->hp / 2) > 300 ? 300 : (m_ptr->hp / 2)), 0, TRUE); break; } case 128 + 0: { /* RF4_BA_ACID */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts an acid ball.", m_name); breath(m_idx, GF_ACID, damroll(r_ptr->hdice, 6), 2, FALSE); update_smart_learn(m_idx, DRS_ACID); break; } case 128 + 1: { /* RF4_BA_ELEC */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a lightning ball.", m_name); breath(m_idx, GF_ELEC, damroll(r_ptr->hdice, 6), 2, FALSE); update_smart_learn(m_idx, DRS_ELEC); break; } case 128 + 2: { /* RF4_BA_FIRE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a fire ball.", m_name); breath(m_idx, GF_FIRE, damroll(r_ptr->hdice, 6), 2, FALSE); update_smart_learn(m_idx, DRS_FIRE); break; } case 128 + 3: { /* RF4_BA_COLD */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a frost ball.", m_name); breath(m_idx, GF_COLD, damroll(r_ptr->hdice, 6), 2, FALSE); update_smart_learn(m_idx, DRS_COLD); break; } case 128 + 4: { /* RF4_BA_POIS */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a stinking cloud.", m_name); breath(m_idx, GF_POIS, damroll(12, 2), 2, FALSE); update_smart_learn(m_idx, DRS_POIS); break; } case 128 + 5: { /* RF4_BA_NETH */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a nether ball.", m_name); breath(m_idx, GF_NETHER, (50 + damroll(10, 10) + r_ptr->hdice * 2), 2, FALSE); update_smart_learn(m_idx, DRS_NETH); break; } case 128 + 6: { /* RF4_BA_WATE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s gestures fluidly.", m_name); msgf("You are engulfed in a whirlpool."); breath(m_idx, GF_WATER, damroll(r_ptr->hdice, 8), 4, FALSE); break; } case 128 + 7: { /* RF4_BA_MANA */ disturb(TRUE); if (blind) msgf("%^s mumbles powerfully.", m_name); else msgf("%^s invokes a mana storm.", m_name); breath(m_idx, GF_MANA, (r_ptr->hdice * 8) + damroll(10, 10), 4, FALSE); break; } case 128 + 8: { /* RF4_BA_DARK */ disturb(TRUE); if (blind) msgf("%^s mumbles powerfully.", m_name); else msgf("%^s invokes a darkness storm.", m_name); breath(m_idx, GF_DARK, (r_ptr->hdice * 8) + damroll(10, 10), 4, FALSE); update_smart_learn(m_idx, DRS_DARK); break; } case 128 + 9: { /* RF4_DRAIN_MANA */ if (!direct) break; if (p_ptr->csp) { int r1; /* Disturb if legal */ disturb(TRUE); /* Basic message */ msgf("%^s draws psychic energy from you!", m_name); /* Attack power */ r1 = randint1(r_ptr->hdice); /* Full drain */ if (r1 >= p_ptr->csp) { r1 = p_ptr->csp; p_ptr->csp = 0; p_ptr->csp_frac = 0; } /* Partial drain */ else { p_ptr->csp -= r1; } /* Redraw mana */ p_ptr->redraw |= (PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); /* Heal the monster */ if (m_ptr->hp < m_ptr->maxhp) { /* Heal */ m_ptr->hp += (6 * r1); if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Special message */ if (seen) { msgf("%^s appears healthier.", m_name); } } } update_smart_learn(m_idx, DRS_MANA); break; } case 128 + 10: { /* RF4_MIND_BLAST */ if (!direct) break; disturb(TRUE); if (!seen) { msgf("You feel something focusing on your mind."); } else { msgf("%^s gazes deep into your eyes.", m_name); } if (player_save(power)) { msgf("You resist the effects!"); } else { msgf("Your mind is blasted by psionic energy."); if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(4, 8)); } if (!(FLAG(p_ptr, TR_RES_CHAOS)) && one_in_(3)) { (void)inc_image(rand_range(150, 400)); } take_hit(damroll(8, 8), ddesc); } break; } case 128 + 11: { /* RF4_BRAIN_SMASH */ if (!direct) break; disturb(TRUE); if (!seen) { msgf("You feel something focusing on your mind."); } else { msgf("%^s looks deep into your eyes.", m_name); } if (player_save(power + 10)) { msgf("You resist the effects!"); } else { msgf("Your mind is blasted by psionic energy."); take_hit(damroll(12, 15), ddesc); if (!(FLAG(p_ptr, TR_RES_BLIND))) { (void)inc_blind(rand_range(8, 16)); } if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(4, 8)); } if (!(FLAG(p_ptr, TR_FREE_ACT))) { (void)inc_paralyzed(rand_range(4, 8)); } (void)inc_slow(rand_range(4, 8)); for (i = 0; !player_save(power - i); i += 10) { (void)do_dec_stat(A_INT); (void)do_dec_stat(A_WIS); } if (!(FLAG(p_ptr, TR_RES_CHAOS))) { (void)inc_image(rand_range(150, 400)); } } break; } case 128 + 12: { /* RF4_CAUSE_1 */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s points at you and curses.", m_name); if (player_save(power - 20)) { msgf("You resist the effects!"); } else { curse_equipment(33, 0); take_hit(damroll(3, 8), ddesc); } break; } case 128 + 13: { /* RF4_CAUSE_2 */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s points at you and curses horribly.", m_name); if (player_save(power - 10)) { msgf("You resist the effects!"); } else { curse_equipment(50, 5); take_hit(damroll(8, 8), ddesc); } break; } case 128 + 14: { /* RF4_CAUSE_3 */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles loudly.", m_name); else msgf("%^s points at you, incanting terribly!", m_name); if (player_save(power)) { msgf("You resist the effects!"); } else { curse_equipment(80, 15); take_hit(damroll(10, 15), ddesc); } break; } case 128 + 15: { /* RF4_CAUSE_4 */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s screams the word 'DIE!'", m_name); else msgf("%^s points at you, screaming the word DIE!", m_name); if (player_save(power + 10)) { msgf("You resist the effects!"); } else { take_hit(damroll(15, 15), ddesc); (void)inc_cut(damroll(10, 10)); } break; } case 128 + 16: { /* RF4_BO_ACID */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a acid bolt.", m_name); bolt(m_idx, GF_ACID, damroll(7, 8) + (r_ptr->hdice * 2 / 3)); update_smart_learn(m_idx, DRS_ACID); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 17: { /* RF4_BO_ELEC */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a lightning bolt.", m_name); bolt(m_idx, GF_ELEC, damroll(4, 8) + (r_ptr->hdice * 2 / 3)); update_smart_learn(m_idx, DRS_ELEC); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 18: { /* RF4_BO_FIRE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a fire bolt.", m_name); bolt(m_idx, GF_FIRE, damroll(9, 8) + (r_ptr->hdice * 2 / 3)); update_smart_learn(m_idx, DRS_FIRE); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 19: { /* RF4_BO_COLD */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a frost bolt.", m_name); bolt(m_idx, GF_COLD, damroll(6, 8) + (r_ptr->hdice * 2 / 3)); update_smart_learn(m_idx, DRS_COLD); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 20: { /* RF4_BO_POIS */ /* XXX XXX XXX */ break; } case 128 + 21: { /* RF4_BO_NETH */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a nether bolt.", m_name); bolt(m_idx, GF_NETHER, 30 + damroll(5, 5) + (r_ptr->hdice * 3)); update_smart_learn(m_idx, DRS_NETH); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 22: { /* RF4_BO_WATE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a water bolt.", m_name); bolt(m_idx, GF_WATER, damroll(10, 10) + (r_ptr->hdice * 2)); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 23: { /* RF4_BO_MANA */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a mana bolt.", m_name); bolt(m_idx, GF_MANA, damroll(r_ptr->hdice, 10)); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 24: { /* RF4_BO_PLAS */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a plasma bolt.", m_name); bolt(m_idx, GF_PLASMA, 10 + damroll(8, 7) + (r_ptr->hdice * 2)); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 25: { /* RF4_BO_ICEE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts an ice bolt.", m_name); bolt(m_idx, GF_ICE, damroll(6, 6) + (r_ptr->hdice * 2)); update_smart_learn(m_idx, DRS_COLD); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 26: { /* RF4_MISSILE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a magic missile.", m_name); bolt(m_idx, GF_MISSILE, damroll(2, 6) + (r_ptr->hdice * 2 / 3)); update_smart_learn(m_idx, DRS_REFLECT); break; } case 128 + 27: { /* RF4_SCARE */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles, and you hear scary noises.", m_name); else msgf("%^s casts a fearful illusion.", m_name); if (FLAG(p_ptr, TR_RES_FEAR)) { msgf("You refuse to be frightened."); } else if (player_save(power)) { msgf("You refuse to be frightened."); } else { (void)inc_afraid(rand_range(4, 8)); } update_smart_learn(m_idx, DRS_FEAR); break; } case 128 + 28: { /* RF4_BLIND */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s casts a spell, burning your eyes!", m_name); if (FLAG(p_ptr, TR_RES_BLIND)) { msgf("You are unaffected!"); } else if (player_save(power)) { msgf("You resist the effects!"); } else { (void)inc_blind(rand_range(12, 16)); } update_smart_learn(m_idx, DRS_BLIND); break; } case 128 + 29: { /* RF4_CONF */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles, and you hear puzzling noises.", m_name); else msgf("%^s creates a mesmerising illusion.", m_name); if (FLAG(p_ptr, TR_RES_CONF)) { msgf("You disbelieve the feeble spell."); } else if (player_save(power)) { msgf("You disbelieve the feeble spell."); } else { (void)inc_confused(rand_range(4, 8)); } update_smart_learn(m_idx, DRS_CONF); break; } case 128 + 30: { /* RF4_SLOW */ if (!direct) break; disturb(TRUE); msgf("%^s drains power from your muscles!", m_name); if (FLAG(p_ptr, TR_FREE_ACT)) { msgf("You are unaffected!"); } else if (player_save(power)) { msgf("You resist the effects!"); } else { (void)inc_slow(rand_range(4, 8)); } update_smart_learn(m_idx, DRS_FREE); break; } case 128 + 31: { /* RF4_HOLD */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s stares deep into your eyes!", m_name); if (FLAG(p_ptr, TR_FREE_ACT)) { msgf("You are unaffected!"); } else if (player_save(power)) { msgf("You resist the effects!"); } else { (void)inc_paralyzed(rand_range(4, 8)); } update_smart_learn(m_idx, DRS_FREE); break; } case 160 + 0: { /* RF5_HASTE */ disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s concentrates on %s body.", m_name, m_poss); } /* Allow quick speed increases to base+10 */ if (m_ptr->mspeed < r_ptr->speed + 10) { msgf("%^s starts moving faster.", m_name); m_ptr->mspeed += 10; } /* Allow small speed increases to base+20 */ else if (m_ptr->mspeed < r_ptr->speed + 20) { msgf("%^s starts moving faster.", m_name); m_ptr->mspeed += 2; } break; } case 160 + 1: { /* RF5_HAND_DOOM */ disturb(TRUE); msgf("%^s invokes the Hand of Doom!", m_name); if (player_save(power + 10)) { msgf("You resist the effects!"); } else { int dummy = (((s32b)(rand_range(65, 90) * (p_ptr->chp))) / 100); msgf("You feel your life fade away!"); take_hit(dummy, m_name); curse_equipment(100, 20); if (p_ptr->chp < 1) p_ptr->chp = 1; } break; } case 160 + 2: { /* RF5_HEAL */ disturb(TRUE); /* Message */ if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s concentrates on %s wounds.", m_name, m_poss); } /* Heal some */ m_ptr->hp += (r_ptr->hdice * 12); /* Fully healed */ if (m_ptr->hp >= m_ptr->maxhp) { /* Fully healed */ m_ptr->hp = m_ptr->maxhp; /* Message */ if (seen) { msgf("%^s looks completely healed!", m_name); } else { msgf("%^s sounds completely healed!", m_name); } } /* Partially healed */ else { /* Message */ if (seen) { msgf("%^s looks healthier.", m_name); } else { msgf("%^s sounds healthier.", m_name); } } /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Cancel fear */ if (m_ptr->monfear) { /* Cancel fear */ m_ptr->monfear = 0; /* Message */ msgf("%^s recovers %s courage.", m_name, m_poss); } break; } case 160 + 3: { /* RF5_INVULNER */ disturb(TRUE); /* Message */ if (!seen) { msgf("%^s mumbles powerfully.", m_name); } else { msgf("%^s casts a Globe of Invulnerability.", m_name); } if (!(m_ptr->invulner)) m_ptr->invulner = (byte)rand_range(4, 8); break; } case 160 + 4: { /* RF5_BLINK */ disturb(TRUE); msgf("%^s blinks away.", m_name); (void)teleport_away(m_idx, 10); break; } case 160 + 5: { /* RF5_TPORT */ disturb(TRUE); msgf("%^s teleports away.", m_name); (void)teleport_away(m_idx, MAX_SIGHT * 2 + 5); break; } case 160 + 6: { /* RF5_XXX3X6 */ break; } case 160 + 7: { /* RF5_XXX4X6 */ break; } case 160 + 8: { /* RF5_TELE_TO */ if (!direct) break; disturb(TRUE); msgf("%^s commands you to return.", m_name); teleport_player_to(m_ptr->fx, m_ptr->fy); break; } case 160 + 9: { /* RF5_TELE_AWAY */ if (!direct) break; disturb(TRUE); msgf("%^s teleports you away.", m_name); teleport_player(100); break; } case 160 + 10: { /* RF5_TELE_LEVEL */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles strangely.", m_name); else msgf("%^s gestures at your feet.", m_name); if (FLAG(p_ptr, TR_RES_NEXUS)) { msgf("You are unaffected!"); } else if (player_save(power)) { msgf("You resist the effects!"); } else { teleport_player_level(); } update_smart_learn(m_idx, DRS_NEXUS); break; } case 160 + 11: { /* RF5_XXX5 */ break; } case 160 + 12: { /* RF5_DARKNESS */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s gestures in shadow.", m_name); (void)unlite_area(0, 3); break; } case 160 + 13: { /* RF5_TRAPS */ if (!direct) break; disturb(TRUE); if (blind) msgf("%^s mumbles, and then cackles evilly.", m_name); else msgf("%^s casts a spell and cackles evilly.", m_name); (void)trap_creation(); break; } case 160 + 14: { /* RF5_FORGET */ if (!direct) break; disturb(TRUE); msgf("%^s tries to blank your mind.", m_name); if (player_save(power)) { msgf("You resist the effects!"); } else if (lose_all_info()) { msgf("Your memories fade away."); } break; } case 160 + 15: { /* RF5_RAISE_DEAD */ disturb(TRUE); msgf("%^s mutters quietly.", m_name); (void)raise_dead(m_ptr->fx, m_ptr->fy, FALSE); break; } case 160 + 16: { /* RF5_SUMMON_KIN */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons %s %s.", m_name, m_poss, FLAG(r_ptr, RF_UNIQUE) ? "minions" : "kin"); summon_kin_type = r_ptr->d_char; /* Big hack */ for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_KIN, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear many things appear nearby."); break; } case 160 + 17: { /* RF5_S_CYBER */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons Cyberdemons!", m_name); if (blind && count) msgf("You hear heavy steps nearby."); (void)summon_cyber(m_idx, x, y); break; } case 160 + 18: { /* RF5_S_MONSTER */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons help!", m_name); for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, 0, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear something appear nearby."); break; } case 160 + 19: { /* RF5_S_MONSTERS */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons monsters!", m_name); for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, 0, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear many things appear nearby."); break; } case 160 + 20: { /* RF5_S_ANT */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons ants.", m_name); for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_ANT, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear many things appear nearby."); break; } case 160 + 21: { /* RF5_S_SPIDER */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons spiders.", m_name); for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_SPIDER, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear many things appear nearby."); break; } case 160 + 22: { /* RF5_S_HOUND */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons hounds.", m_name); for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HOUND, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear many things appear nearby."); break; } case 160 + 23: { /* RF5_S_HYDRA */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons hydras.", m_name); for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HYDRA, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear many things appear nearby."); break; } case 160 + 24: { /* RF5_S_ANGEL */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons an angel!", m_name); for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_ANGEL, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear something appear nearby."); break; } case 160 + 25: { /* RF5_S_DEMON */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf ("%^s magically summons a demon from the Courts of Chaos!", m_name); for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_DEMON, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear something appear nearby."); break; } case 160 + 26: { /* RF5_S_UNDEAD */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons an undead adversary!", m_name); for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_UNDEAD, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear something appear nearby."); break; } case 160 + 27: { /* RF5_S_DRAGON */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons a dragon!", m_name); for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_DRAGON, TRUE, FALSE, FALSE); } if (blind && count) msgf("You hear something appear nearby."); break; } case 160 + 28: { /* RF5_S_HI_UNDEAD */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons greater undead!", m_name); for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HI_UNDEAD, TRUE, FALSE, FALSE); } if (blind && count) { msgf("You hear many creepy things appear nearby."); } break; } case 160 + 29: { /* RF5_S_HI_DRAGON */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons ancient dragons!", m_name); for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HI_DRAGON, TRUE, FALSE, FALSE); } if (blind && count) { msgf("You hear many powerful things appear nearby."); } break; } case 160 + 30: { /* RF5_S_AMBERITES */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons Lords of Amber!", m_name); for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_AMBERITES, TRUE, FALSE, FALSE); } if (blind && count) { msgf("You hear immortal beings appear nearby."); } break; } case 160 + 31: { /* RF5_S_UNIQUE */ disturb(TRUE); if (blind) msgf("%^s mumbles.", m_name); else msgf("%^s magically summons special opponents!", m_name); for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_UNIQUE, TRUE, FALSE, FALSE); } for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HI_UNDEAD, TRUE, FALSE, FALSE); } if (blind && count) { msgf("You hear many powerful things appear nearby."); } break; } } /* Remember what the monster did to us */ if (seen) { /* Look to see if we've spotted a mimic */ if (m_ptr->smart & SM_MIMIC) { /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now if visible */ if (m_ptr->ml) update_mon_vis(m_ptr->r_idx, 1); /*Hack - no need for a message */ } /* Inate spell */ if (thrown_spell < 32 * 4) { r_ptr->r_flags[3] |= (1L << (thrown_spell - 32 * 3)); if (r_ptr->r_cast_inate < MAX_UCHAR) r_ptr->r_cast_inate++; } /* Bolt or Ball */ else if (thrown_spell < 32 * 5) { r_ptr->r_flags[4] |= (1L << (thrown_spell - 32 * 4)); if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++; } /* Special spell */ else if (thrown_spell < 32 * 6) { r_ptr->r_flags[5] |= (1L << (thrown_spell - 32 * 5)); if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++; } } /* Always take note of monsters that kill you */ if (p_ptr->state.is_dead && (r_ptr->r_deaths < MAX_SHORT)) { r_ptr->r_deaths++; } /* A spell was cast */ return (TRUE); } zangband/src/mspells2.c0000755000000000000000000014256010250356274014053 0ustar rootroot/* File: mspells2.c */ /* Purpose: Monster spells (attack monster) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Monster casts a breath (or ball) attack at another monster. * Pass over any monsters that may be in the way * Affect grids, objects, monsters, and the player */ static void monst_breath_monst(int m_idx, int x, int y, int typ, int dam_hp, int rad, bool breath) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Determine the radius of the blast */ if (rad < 1) rad = FLAG(r_ptr, RF_POWERFUL) ? 3 : 2; /* Handle breath attacks */ if (breath) rad = 0 - rad; (void)project(m_idx, rad, x, y, dam_hp, typ, flg); } /* * Monster casts a bolt at another monster * Stop if we hit a monster * Affect monsters and the player */ static void monst_bolt_monst(int m_idx, int x, int y, int typ, int dam_hp) { u16b flg = PROJECT_STOP | PROJECT_KILL; (void)project(m_idx, 0, x, y, dam_hp, typ, flg); } /* * Monster tries to 'cast a spell' (or breath, etc) * at another monster. * * The player is only disturbed if able to be affected by the spell. */ bool monst_spell_monst(int m_idx) { int y = 0, x = 0; int i, k, t_idx; int chance, thrown_spell, count = 0; int rlev; byte spell[96], num = 0; char m_name[160]; char t_name[160]; char m_poss[160]; char ddesc[160]; monster_type *m_ptr = &m_list[m_idx]; monster_type *t_ptr; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_race *tr_ptr; u32b f4, f5, f6; /* Expected ball spell radius */ int rad = FLAG(r_ptr, RF_POWERFUL) ? 3 : 2; bool wake_up = FALSE; bool fear = FALSE; bool blind = (p_ptr->tim.blind ? TRUE : FALSE); bool see_m = m_ptr->ml; bool see_t; bool see_either; bool see_both; bool known; bool friendly = is_friendly(m_ptr); bool pet = is_pet(m_ptr); /* Cannot cast spells when confused */ if (m_ptr->confused) return (FALSE); /* Hack -- Extract the spell probability */ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2; /* Not allowed to cast spells */ if (!chance) return (FALSE); if (randint0(100) >= chance) return (FALSE); /* Stop if player is dead or gone */ if (!p_ptr->state.playing || p_ptr->state.is_dead) return (FALSE); /* Handle "leaving" */ if (p_ptr->state.leaving) return (FALSE); /* Scan thru all monsters */ for (i = 1; i < m_max; i++) { /* The monster itself isn't a target */ if (i == m_idx) continue; t_idx = i; t_ptr = &m_list[t_idx]; tr_ptr = &r_info[t_ptr->r_idx]; /* Paranoia -- Skip dead monsters */ if (!t_ptr->r_idx) continue; /* Monster must be 'an enemy' */ if (!are_enemies(m_ptr, t_ptr)) continue; /* Monster must be projectable */ if (!projectable(m_ptr->fx, m_ptr->fy, t_ptr->fx, t_ptr->fy)) continue; /* OK -- we've got a target */ y = t_ptr->fy; x = t_ptr->fx; /* Extract the monster level */ rlev = ((r_ptr->hdice * 2 >= 1) ? r_ptr->hdice * 2 : 1); /* Extract the racial spell flags */ f4 = r_ptr->flags[3]; f5 = r_ptr->flags[4]; f6 = r_ptr->flags[5]; /* Disallow blink unless close */ if ((distance(m_ptr->fx, m_ptr->fy, x, y) > 1) || one_in_(3)) f6 &= ~(RF5_BLINK); /* Disallow teleport unless wounded */ if (m_ptr->hp > m_ptr->maxhp / 2) f6 &= ~(RF5_TPORT); /* Disallow teleport away unless a friend is wounded */ if (friendly && (p_ptr->chp > p_ptr->mhp / 4)) f6 &= ~(RF5_TELE_AWAY); /* Remove some spells if necessary */ if (!stupid_monsters && ((f4 & RF3_BOLT_MASK) || (f5 & RF4_BOLT_MASK) || (f6 & RF5_BOLT_MASK)) && !FLAG(r_ptr, RF_STUPID) && !clean_shot(m_ptr->fx, m_ptr->fy, t_ptr->fx, t_ptr->fy, is_pet(m_ptr))) { f4 &= ~(RF3_BOLT_MASK); f5 &= ~(RF4_BOLT_MASK); f6 &= ~(RF5_BOLT_MASK); } /* Prevent collateral damage */ if (friendly && (distance(p_ptr->px, p_ptr->py, x, y) <= rad)) { f4 &= ~(RF3_BALL_MASK); f5 &= ~(RF4_BALL_MASK); f6 &= ~(RF5_BALL_MASK); } /* Find another target if no spells available */ if (!f4 && !f5 && !f6) { f4 = r_ptr->flags[3]; f5 = r_ptr->flags[4]; f6 = r_ptr->flags[5]; continue; } /* Hack -- allow "desperate" spells */ if (FLAG(r_ptr, RF_SMART) && (m_ptr->hp < m_ptr->maxhp / 10) && one_in_(2)) { /* Require intelligent spells */ f4 &= (RF3_INT_MASK); f5 &= (RF4_INT_MASK); f6 &= (RF5_INT_MASK); /* No spells left */ if (!f4 && !f5 && !f6) return (FALSE); } /* Extract the "inate" spells */ for (k = 0; k < 32; k++) { if (f4 & (1L << k)) spell[num++] = k + 32 * 3; } /* Extract the "normal" spells */ for (k = 0; k < 32; k++) { if (f5 & (1L << k)) spell[num++] = k + 32 * 4; } /* Extract the "bizarre" spells */ for (k = 0; k < 32; k++) { if (f6 & (1L << k)) spell[num++] = k + 32 * 5; } /* No spells left */ if (!num) return (FALSE); /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0, 160); /* Get the monster possessive ("his"/"her"/"its") */ monster_desc(m_poss, m_ptr, 0x22, 160); /* Get the target's name (or "it") */ monster_desc(t_name, t_ptr, 0, 160); /* Hack -- Get the "died from" name */ monster_desc(ddesc, m_ptr, 0x88, 160); /* Choose a spell to cast */ thrown_spell = spell[randint0(num)]; see_t = t_ptr->ml; see_either = (see_m || see_t); see_both = (see_m && see_t); /* Can the player be aware of this attack? */ known = (m_ptr->cdis <= MAX_SIGHT) || (t_ptr->cdis <= MAX_SIGHT); switch (thrown_spell) { case 96 + 0: { /* RF3_SHRIEK */ if (known) { if (see_m) { msgf("%^s shrieks at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } wake_up = TRUE; break; } case 96 + 1: { /* RF3_ELDRITCH_HORROR */ if (known) { if (see_m) { msgf("%^s stares at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } wake_up = TRUE; break; } case 96 + 2: { /* RF3_XXX3X4 */ break; } case 96 + 3: { /* RF3_ROCKET */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s shoots something.", m_name); } else { msgf("%^s fires a rocket at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_ROCKET, ((m_ptr->hp / 4) > 600 ? 600 : (m_ptr->hp / 4)), 2, FALSE); break; } case 96 + 4: { int dice = r_ptr->hdice < 4 ? 1 : r_ptr->hdice / 4; if (dice > 7) dice = 7; /* RF3_ARROW */ if (known) { if (see_either) { if (blind) { msgf("%^s makes a strange noise.", m_name); } else { msgf("%^s fires an arrow at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_SHOOT); } monst_bolt_monst(m_idx, x, y, GF_ARROW, damroll(dice, 6)); break; } case 96 + 5: { /* RF3_XXX6 */ break; } case 96 + 6: { /* RF3_XXX7 */ break; } case 96 + 7: { /* RF3_XXX8 */ break; } case 96 + 8: { /* RF3_BR_ACID */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes acid at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_ACID, ((m_ptr->hp / 3) > 1200 ? 1200 : (m_ptr->hp / 3)), 0, TRUE); break; } case 96 + 9: { /* RF3_BR_ELEC */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes lightning at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_ELEC, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 10: { /* RF3_BR_FIRE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes fire at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_FIRE, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 11: { /* RF3_BR_COLD */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes frost at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_COLD, ((m_ptr->hp / 2) > 1200 ? 1200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 12: { /* RF3_BR_POIS */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes gas at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_POIS, ((m_ptr->hp / 2) > 600 ? 600 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 13: { /* RF3_BR_NETH */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes nether at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_NETHER, ((m_ptr->hp / 4) > 450 ? 450 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 14: { /* RF3_BR_LITE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes light at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_LITE, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 15: { /* RF3_BR_DARK */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes darkness at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_DARK, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 16: { /* RF3_BR_CONF */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes confusion at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_CONFUSION, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 17: { /* RF3_BR_SOUN */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes sound at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_SOUND, ((m_ptr->hp / 4) > 350 ? 350 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 18: { /* RF3_BR_CHAO */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes chaos at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_CHAOS, ((m_ptr->hp / 4) > 500 ? 500 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 19: { /* RF3_BR_DISE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes disenchantment at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_DISENCHANT, ((m_ptr->hp / 4) > 400 ? 400 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 20: { /* RF3_BR_NEXU */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes nexus at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_NEXUS, ((m_ptr->hp / 2) > 250 ? 250 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 21: { /* RF3_BR_TIME */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes time at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_TIME, ((m_ptr->hp / 2) > 150 ? 150 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 22: { /* RF3_BR_INER */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes inertia at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_INERTIA, ((m_ptr->hp / 4) > 200 ? 200 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 23: { /* RF3_BR_GRAV */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes gravity at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_GRAVITY, ((m_ptr->hp / 2) > 200 ? 200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 24: { /* RF3_BR_SHAR */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes shards at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_SHARDS, ((m_ptr->hp / 4) > 400 ? 400 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 25: { /* RF3_BR_PLAS */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes plasma at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_PLASMA, ((m_ptr->hp / 4) > 150 ? 150 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 26: { /* RF3_BR_WALL */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes force at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_FORCE, ((m_ptr->hp / 4) > 200 ? 200 : (m_ptr->hp / 4)), 0, TRUE); break; } case 96 + 27: { /* RF3_BR_MANA */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes mana at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_MANA, ((m_ptr->hp / 2) > 200 ? 200 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 28: { /* RF3_BA_NUKE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts a ball of radiation at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_NUKE, (rlev + damroll(10, 6)), 2, FALSE); break; } case 96 + 29: { /* RF3_BR_NUKE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes toxic waste at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_NUKE, ((m_ptr->hp / 2) > 600 ? 600 : (m_ptr->hp / 2)), 0, TRUE); break; } case 96 + 30: { /* RF3_BA_CHAO */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles frighteningly.", m_name); } else { msgf("%^s invokes raw Logrus upon %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_CHAOS, (rlev * 2) + damroll(10, 10), 4, FALSE); break; } case 96 + 31: { /* RF3_BR_DISI */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s breathes.", m_name); } else { msgf("%^s breathes disintegration at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } sound(SOUND_BREATH); } monst_breath_monst(m_idx, x, y, GF_DISINTEGRATE, ((m_ptr->hp / 2) > 300 ? 300 : (m_ptr->hp / 2)), 0, TRUE); break; } case 128 + 0: { /* RF4_BA_ACID */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts an acid ball at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_ACID, damroll(r_ptr->hdice, 6), 2, FALSE); break; } case 128 + 1: { /* RF4_BA_ELEC */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts a lightning ball at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_ELEC, damroll(r_ptr->hdice, 6), 2, FALSE); break; } case 128 + 2: { /* RF4_BA_FIRE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts a fire ball at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_FIRE, damroll(r_ptr->hdice, 6), 2, FALSE); break; } case 128 + 3: { /* RF4_BA_COLD */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts a frost ball at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_COLD, damroll(r_ptr->hdice, 6), 2, FALSE); break; } case 128 + 4: { /* RF4_BA_POIS */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts a stinking cloud at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_POIS, damroll(12, 2), 2, FALSE); break; } case 128 + 5: { /* RF4_BA_NETH */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s casts a nether ball at %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_NETHER, (50 + damroll(10, 10) + rlev), 2, FALSE); break; } case 128 + 6: { /* RF4_BA_WATE */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles.", m_name); } else { msgf("%^s gestures fluidly at %s.", m_name, t_name); msgf("%^s is engulfed in a whirlpool.", t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_WATER, damroll(r_ptr->hdice, 8), 4, FALSE); break; } case 128 + 7: { /* RF4_BA_MANA */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles powerfully.", m_name); } else { msgf("%^s invokes a mana storm upon %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_MANA, (rlev * 4) + damroll(10, 10), 4, FALSE); break; } case 128 + 8: { /* RF4_BA_DARK */ if (known) { if (see_either) { disturb(TRUE); if (blind) { msgf("%^s mumbles powerfully.", m_name); } else { msgf("%^s invokes a darkness storm upon %s.", m_name, t_name); } } else { p_ptr->state.mon_fight = TRUE; } } monst_breath_monst(m_idx, x, y, GF_DARK, (rlev * 4) + damroll(10, 10), 4, FALSE); break; } case 128 + 9: { /* RF4_DRAIN_MANA */ /* Attack power */ int power = (randint1(rlev) / 2) + 1; if (see_m) { /* Basic message */ msgf("%^s draws psychic energy from %s.", m_name, t_name); } /* Heal the monster */ if (m_ptr->hp < m_ptr->maxhp) { if (!tr_ptr->flags[3] && !tr_ptr->flags[4] && !tr_ptr->flags[5]) { if (see_both) { msgf("%^s is unaffected!", t_name); } } else { /* Heal */ m_ptr->hp += 6 * power; if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Special message */ if (see_m) { msgf("%^s appears healthier.", m_name); } } } wake_up = TRUE; break; } case 128 + 10: { /* RF4_MIND_BLAST */ if (see_m) { msgf("%^s gazes intently at %s.", m_name, t_name); } /* Attempt a saving throw */ if (FLAG(tr_ptr, RF_UNIQUE) || FLAG(tr_ptr, RF_NO_CONF) || (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2)) { /* No obvious effect */ if (see_both) { /* Memorize a flag */ if (FLAG(tr_ptr, RF_NO_CONF)) { tr_ptr->r_flags[2] |= (RF2_NO_CONF); } msgf("%^s is unaffected!", t_name); } } else { if (see_t) { msgf("%^s is blasted by psionic energy.", t_name); } t_ptr->confused += (byte)rand_range(4, 8); mon_take_hit_mon(t_idx, damroll(8, 8), &fear, " collapses, a mindless husk."); } wake_up = TRUE; break; } case 128 + 11: { /* RF4_BRAIN_SMASH */ if (see_m) { msgf("%^s gazes intently at %s.", m_name, t_name); } /* Attempt a saving throw */ if (FLAG(tr_ptr, RF_UNIQUE) || FLAG(tr_ptr, RF_NO_CONF) || (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2)) { /* No obvious effect */ if (see_both) { /* Memorize a flag */ if (FLAG(tr_ptr, RF_NO_CONF)) { tr_ptr->r_flags[2] |= (RF2_NO_CONF); } msgf("%^s is unaffected!", t_name); } } else { if (see_t) { msgf("%^s is blasted by psionic energy.", t_name); } t_ptr->confused += (byte)rand_range(4, 8); t_ptr->mspeed -= (byte)rand_range(4, 8); t_ptr->stunned += (byte)rand_range(4, 8); mon_take_hit_mon(t_idx, damroll(12, 15), &fear, " collapses, a mindless husk."); } wake_up = TRUE; break; } case 128 + 12: { /* RF4_CAUSE_1 */ if (known) { if (see_m) { msgf("%^s points at %s and curses.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_both) msgf("%^s resists!", t_name); } else { mon_take_hit_mon(t_idx, damroll(3, 8), &fear, " is destroyed."); } wake_up = TRUE; break; } case 128 + 13: { /* RF4_CAUSE_2 */ if (known) { if (see_m) { msgf("%^s points at %s and curses horribly.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_both) msgf("%^s resists!", t_name); } else { mon_take_hit_mon(t_idx, damroll(8, 8), &fear, " is destroyed."); } wake_up = TRUE; break; } case 128 + 14: { /* RF4_CAUSE_3 */ if (known) { if (see_m) { msgf("%^s points at %s, incanting terribly!", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_both) msgf("%^s resists!", t_name); } else { mon_take_hit_mon(t_idx, damroll(10, 15), &fear, " is destroyed."); } wake_up = TRUE; break; } case 128 + 15: { /* RF4_CAUSE_4 */ if (known) { if (see_m) { msgf ("%^s points at %s, screaming the word, 'DIE!'", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_both) msgf("%^s resists!", t_name); } else { mon_take_hit_mon(t_idx, damroll(15, 15), &fear, " is destroyed."); } wake_up = TRUE; break; } case 128 + 16: { /* RF4_BO_ACID */ if (known) { if (see_either) { msgf("%^s casts an acid bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_ACID, damroll(7, 8) + (rlev / 3)); break; } case 128 + 17: { /* RF4_BO_ELEC */ if (known) { if (see_either) { msgf("%^s casts a lightning bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_ELEC, damroll(4, 8) + (rlev / 3)); break; } case 128 + 18: { /* RF4_BO_FIRE */ if (known) { if (see_either) { msgf("%^s casts a fire bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_FIRE, damroll(9, 8) + (rlev / 3)); break; } case 128 + 19: { /* RF4_BO_COLD */ if (known) { if (see_either) { msgf("%^s casts a frost bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_COLD, damroll(6, 8) + (rlev / 3)); break; } case 128 + 20: { /* RF4_BO_POIS */ /* XXX XXX XXX */ break; } case 128 + 21: { /* RF4_BO_NETH */ if (known) { if (see_either) { msgf("%^s casts a nether bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_NETHER, 30 + damroll(5, 5) + (rlev * 3) / 2); break; } case 128 + 22: { /* RF4_BO_WATE */ if (known) { if (see_either) { msgf("%^s casts a water bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_WATER, damroll(10, 10) + rlev); break; } case 128 + 23: { /* RF4_BO_MANA */ if (known) { if (see_either) { msgf("%^s casts a mana bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_MANA, damroll(r_ptr->hdice, 10)); break; } case 128 + 24: { /* RF4_BO_PLAS */ if (known) { if (see_either) { msgf("%^s casts a plasma bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_PLASMA, 10 + damroll(8, 7) + rlev); break; } case 128 + 25: { /* RF4_BO_ICEE */ if (known) { if (see_either) { msgf("%^s casts an ice bolt at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_ICE, damroll(6, 6) + rlev); break; } case 128 + 26: { /* RF4_MISSILE */ if (known) { if (see_either) { msgf("%^s casts a magic missile at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } monst_bolt_monst(m_idx, x, y, GF_MISSILE, damroll(2, 6) + (rlev / 3)); break; } case 128 + 27: { /* RF4_SCARE */ if (known) { if (see_either) { msgf ("%^s casts a fearful illusion in front of %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (FLAG(tr_ptr, RF_NO_FEAR)) { if (see_t) msgf("%^s refuses to be frightened.", t_name); } else if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_t) msgf("%^s refuses to be frightened.", t_name); } else { if (!t_ptr->monfear) fear = TRUE; t_ptr->monfear += (byte)rand_range(4, 8); } wake_up = TRUE; break; } case 128 + 28: { /* RF4_BLIND */ if (known) { if (see_either) { msgf("%^s casts a spell, burning %s%s eyes.", m_name, t_name, (streq(t_name, "it") ? "s" : "'s")); } else { p_ptr->state.mon_fight = TRUE; } } /* Simulate blindness with confusion */ if (FLAG(tr_ptr, RF_NO_CONF)) { if (see_t) msgf("%^s is unaffected.", t_name); } else if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_t) msgf("%^s is unaffected.", t_name); } else { if (see_t) msgf("%^s is blinded!", t_name); t_ptr->confused += (byte)rand_range(12, 16); } wake_up = TRUE; break; } case 128 + 29: { /* RF4_CONF */ if (known) { if (see_either) { msgf ("%^s casts a mesmerizing illusion in front of %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (FLAG(tr_ptr, RF_NO_CONF)) { if (see_t) msgf("%^s disbelieves the feeble spell.", t_name); } else if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_t) msgf("%^s disbelieves the feeble spell.", t_name); } else { if (see_t) msgf("%^s seems confused.", t_name); t_ptr->confused += (byte)rand_range(12, 16); } wake_up = TRUE; break; } case 128 + 30: { /* RF4_SLOW */ if (known) { if (see_either) { msgf("%^s drains power from %s%s muscles.", m_name, t_name, (streq(t_name, "it") ? "s" : "'s")); } else { p_ptr->state.mon_fight = TRUE; } } if (FLAG(tr_ptr, RF_UNIQUE)) { if (see_t) msgf("%^s is unaffected.", t_name); } else if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_t) msgf("%^s is unaffected.", t_name); } else { if (see_t) msgf("%^s starts moving slower.", t_name); t_ptr->mspeed -= 10; } wake_up = TRUE; break; } case 128 + 31: { /* RF4_HOLD */ if (known) { if (see_either) { msgf("%^s stares intently at %s.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (FLAG(tr_ptr, RF_UNIQUE) || FLAG(tr_ptr, RF_NO_STUN)) { if (see_t) msgf("%^s is unaffected.", t_name); } else if (tr_ptr->hdice * 2 > randint1(rlev * 3) / 2) { if (see_t) msgf("%^s is unaffected.", t_name); } else { if (see_t) msgf("%^s is paralyzed!", t_name); t_ptr->stunned += (byte)rand_range(4, 8); } wake_up = TRUE; break; } case 160 + 0: { /* RF5_HASTE */ if (known) { if (see_m) { msgf("%^s concentrates on %s body.", m_name, m_poss); } else { p_ptr->state.mon_fight = TRUE; } } /* Allow quick speed increases to base+10 */ if (m_ptr->mspeed < r_ptr->speed + 10) { if (see_m) msgf("%^s starts moving faster.", m_name); m_ptr->mspeed += 10; } /* Allow small speed increases to base+20 */ else if (m_ptr->mspeed < r_ptr->speed + 20) { if (see_m) msgf("%^s starts moving faster.", m_name); m_ptr->mspeed += 2; } break; } case 160 + 1: { /* RF5_HAND_DOOM */ if (known) { if (see_m) { msgf("%^s invokes the Hand of Doom upon %s!", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (FLAG(tr_ptr, RF_UNIQUE)) { if (see_both) msgf("^%s is unaffected!", t_name); } else { if ((r_ptr->hdice * 2 + randint1(20)) > (tr_ptr->hdice * 2 + rand_range(10, 30))) { t_ptr->hp = t_ptr->hp - (((s32b)(rand_range(65, 90) * t_ptr->hp)) / 100); if (t_ptr->hp < 1) t_ptr->hp = 1; } else { if (see_both) msgf("%^s resists!", t_name); } } wake_up = TRUE; break; } case 160 + 2: { /* RF5_HEAL */ if (known) { if (see_m) { msgf("%^s concentrates on %s wounds.", m_name, m_poss); } else { p_ptr->state.mon_fight = TRUE; } } /* Heal some */ m_ptr->hp += (rlev * 6); /* Fully healed */ if (m_ptr->hp >= m_ptr->maxhp) { /* Fully healed */ m_ptr->hp = m_ptr->maxhp; if (known) { if (see_m) { msgf("%^s looks completely healed!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } } /* Partially healed */ else if (known) { if (see_m) { msgf("%^s looks healthier.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Cancel fear */ if (m_ptr->monfear) { /* Cancel fear */ m_ptr->monfear = 0; /* Message */ if (see_m) msgf("%^s recovers %s courage.", m_name, m_poss); } break; } case 160 + 3: { /* RF5_INVULNER */ if (known) { if (see_m) { disturb(TRUE); msgf("%^s casts a Globe of Invulnerability.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } if (!m_ptr->invulner) m_ptr->invulner = (byte)rand_range(4, 8); break; } case 160 + 4: { /* RF5_BLINK */ if (see_m) { msgf("%^s blinks away.", m_name); } (void)teleport_away(m_idx, 10); break; } case 160 + 5: { /* RF5_TPORT */ if (see_m) { msgf("%^s teleports away.", m_name); } (void)teleport_away(m_idx, MAX_SIGHT * 2 + 5); break; } case 160 + 6: { /* RF5_XXX3X6 */ break; } case 160 + 7: { /* RF5_XXX4X6 */ break; } case 160 + 8: { /* RF5_TELE_TO */ /* Not implemented */ break; } case 160 + 9: { /* RF5_TELE_AWAY */ bool resists_tele = FALSE; if (known) { if (see_either) { msgf("%^s teleports %s away.", m_name, t_name); } else { p_ptr->state.mon_fight = TRUE; } } if (FLAG(tr_ptr, RF_RES_TELE)) { if (FLAG(tr_ptr, RF_UNIQUE)) { if (see_t) { tr_ptr->r_flags[2] |= RF2_RES_TELE; msgf("%^s is unaffected!", t_name); } resists_tele = TRUE; } else if (tr_ptr->hdice * 2 > randint1(100)) { if (see_t) { tr_ptr->r_flags[2] |= RF2_RES_TELE; msgf("%^s resists!", t_name); } resists_tele = TRUE; } } if (!resists_tele) { (void)teleport_away(t_idx, MAX_SIGHT * 2 + 5); } break; } case 160 + 10: { /* RF5_TELE_LEVEL */ /* Not implemented */ break; } case 160 + 11: { /* RF5_XXX5 */ break; } case 160 + 12: { /* RF5_DARKNESS */ if (known) { if (see_m) { msgf("%^s gestures in shadow.", m_name); if (see_t) { msgf("%^s is surrounded by darkness.", t_name); } } else { p_ptr->state.mon_fight = TRUE; } } (void)project(m_idx, 3, x, y, 0, GF_DARK_WEAK, PROJECT_GRID | PROJECT_KILL); unlite_room(x, y); break; } case 160 + 13: { /* RF5_TRAPS */ /* Not implemented */ break; } case 160 + 14: { /* RF5_FORGET */ /* Not implemented */ break; } case 160 + 15: { /* RF5_RAISE_DEAD */ if (raise_dead (m_ptr->fx, m_ptr->fy, (bool)(!is_hostile(m_ptr))) && known && see_m) { msgf("%^s mutters quietly.", m_name); } break; } case 160 + 16: { /* RF5_SUMMON_KIN */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons %s %s.", m_name, m_poss, (FLAG(r_ptr, RF_UNIQUE) ? "minions" : "kin")); } else { p_ptr->state.mon_fight = TRUE; } } summon_kin_type = r_ptr->d_char; for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_KIN, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 17: { /* RF5_S_CYBER */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons Cyberdemons!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } count += summon_cyber(m_idx, x, y); if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 18: { /* RF5_S_MONSTER */ int type = (friendly ? SUMMON_NO_UNIQUES : 0); if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons help!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } count += summon_specific(m_idx, x, y, rlev, type, FALSE, friendly, pet); if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 19: { /* RF5_S_MONSTERS */ int type = (friendly ? SUMMON_NO_UNIQUES : 0); if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons monsters!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, type, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 20: { /* RF5_S_ANT */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons ants.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_ANT, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 21: { /* RF5_S_SPIDER */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons spiders.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_SPIDER, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 22: { /* RF5_S_HOUND */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons hounds.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HOUND, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 23: { /* RF5_S_HYDRA */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons hydras.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 6; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_HYDRA, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 24: { /* RF5_S_ANGEL */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons an angel!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_ANGEL, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 25: { /* RF5_S_DEMON */ if (known) { if (see_either) { disturb(TRUE); msgf ("%^s magically summons a demon from the Courts of Chaos!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_DEMON, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 26: { /* RF5_S_UNDEAD */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons undead.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_UNDEAD, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 27: { /* RF5_S_DRAGON */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons a dragon!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 1; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_DRAGON, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 28: { /* RF5_S_HI_UNDEAD */ int type = (friendly ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_HI_UNDEAD); if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons undead.", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, type, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 29: { /* RF5_S_HI_DRAGON */ int type = (friendly ? SUMMON_HI_DRAGON_NO_UNIQUES : SUMMON_HI_DRAGON); if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons ancient dragons!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, type, TRUE, friendly, pet); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 30: { /* RF5_S_AMBERITES */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons Lords of Amber!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_AMBERITES, TRUE, FALSE, FALSE); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } case 160 + 31: { /* RF5_S_UNIQUE */ if (known) { if (see_either) { disturb(TRUE); msgf("%^s magically summons special opponents!", m_name); } else { p_ptr->state.mon_fight = TRUE; } } for (k = 0; k < 8; k++) { count += summon_specific(m_idx, x, y, rlev, SUMMON_UNIQUE, TRUE, FALSE, FALSE); } if (known && !see_t && count) { p_ptr->state.mon_fight = TRUE; } break; } } if (wake_up) { t_ptr->csleep = 0; } if (fear && see_t) { flee_message(t_name, t_ptr->r_idx); } /* Remember what the monster did, if we saw it */ if (m_ptr->ml) { /* Look to see if we've spotted a mimic */ if (m_ptr->smart & SM_MIMIC) { /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now */ update_mon_vis(m_ptr->r_idx, 1); /*Hack - no need for a message */ } /* Inate spell */ if (thrown_spell < 32 * 4) { r_ptr->r_flags[3] |= (1L << (thrown_spell - 32 * 3)); if (r_ptr->r_cast_inate < MAX_UCHAR) r_ptr->r_cast_inate++; } /* Bolt or Ball */ else if (thrown_spell < 32 * 5) { r_ptr->r_flags[4] |= (1L << (thrown_spell - 32 * 4)); if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++; } /* Special spell */ else if (thrown_spell < 32 * 6) { r_ptr->r_flags[5] |= (1L << (thrown_spell - 32 * 5)); if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++; } } /* Always take note of monsters that kill you */ if (p_ptr->state.is_dead && (r_ptr->r_deaths < MAX_SHORT)) { r_ptr->r_deaths++; } /* A spell was cast */ return (TRUE); } /* No enemy found */ return (FALSE); } zangband/src/mutation.c0000755000000000000000000011144710250356274014152 0ustar rootroot/* File: mutation.c */ /* Purpose: Mutation effects (and racial powers) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Hack - to allow bias for collecting * certain mutations dependent upon your race */ #define M1_HYPN_GAZE 2 #define M1_SHRIEK 13 #define M1_POLYMORPH 17 #define M2_HORNS 39 #define M2_TENTACLES 48 /* Does the player have the given mutation? */ bool player_has_mut(int mutation) { if (mutation < MUT_PER_SET) { return (p_ptr->muta1 & mutations[mutation].which ? TRUE : FALSE); } else if (mutation < MUT_PER_SET * 2) { return (p_ptr->muta2 & mutations[mutation].which ? TRUE : FALSE); } else { return (p_ptr->muta3 & mutations[mutation].which ? TRUE : FALSE); } } /* * Select a random mutation. * * Note that if choose_mut is non-zero (specified * via p_ptr->command_arg in debugging mode) we * select the specified mutation. * * This function is really silly. * We should use the (depth) rarity method used * for everything else to select the mutation */ static bool select_mutation(int choose_mut, bool gain, int *mutation) { u32b flag; int attempts_left; int num = -1; /* Sanity check */ if (choose_mut < 0 || choose_mut > 193) choose_mut = 0; attempts_left = (choose_mut ? 1 : (gain ? 20 : 2000)); while (attempts_left--) { switch (choose_mut ? choose_mut : randint1(193)) { case 1: case 2: case 3: case 4: /* Spit acid */ num = 0; break; case 5: case 6: case 7: /* Breathe fire */ num = 1; break; case 8: case 9: /* Hypnotic gaze */ num = 2; break; case 10: case 11: /* Telekinesis */ num = 3; break; case 12: case 13: case 14: /* Vteleport */ num = 4; break; case 15: case 16: /* Mind blast */ num = 5; break; case 17: case 18: /* Radiation */ num = 6; break; case 19: case 20: /* Vampirism */ num = 7; break; case 21: case 22: case 23: /* Smell metal */ num = 8; break; case 24: case 25: case 26: case 27: /* Smell monsters */ num = 9; break; case 28: case 29: case 30: /* Blink */ num = 10; break; case 31: case 32: /* Eat rock */ num = 11; break; case 33: case 34: /* Swap position */ num = 12; break; case 35: case 36: case 37: /* Shriek */ num = 13; break; case 38: case 39: case 40: /* Illuminate */ num = 14; break; case 41: case 42: /* Detect curse */ num = 15; break; case 43: case 44: case 45: /* Berserk */ num = 16; break; case 46: /* Polymorph */ num = 17; break; case 47: case 48: /* Midas */ /* Only rich characters can get this */ if (p_ptr->au >= p_ptr->lev * 1000L) { num = 18; } break; case 49: /* Mold */ num = 19; break; case 50: case 51: case 52: /* Resist Elements */ num = 20; break; case 53: case 54: case 55: /* Earthquake */ num = 21; break; case 56: /* Eat magic */ num = 22; break; case 57: case 58: /* Weigh magic */ num = 23; break; case 59: /* Sterilize */ num = 24; break; case 60: case 61: /* Panic hit */ num = 25; break; case 62: case 63: case 64: /* Dazzle */ num = 26; break; case 65: case 66: case 67: /* Laser eye */ num = 27; break; case 68: case 69: /* Recall */ num = 28; break; case 70: /* Banish */ num = 29; break; case 71: case 72: /* Cold touch */ num = 30; break; case 73: case 74: /* Throw */ num = 31; break; case 75: /* Berserk */ num = 32; break; case 76: /* Fear */ num = 33; break; case 77: /* Teleport */ num = 34; break; case 78: /* Ethanol */ num = 35; break; case 79: /* Hallucinate */ num = 36; break; case 80: /* Flatulent */ num = 37; break; case 81: case 82: /* Scorpion */ num = 38; break; case 83: case 84: /* Horns */ num = 39; break; case 85: case 86: /* Beak */ num = 40; break; case 87: case 88: /* Demons */ num = 41; break; case 89: /* Mana */ num = 42; break; case 90: case 91: /* Speed flux */ num = 43; break; case 92: case 93: /* Banish */ num = 44; break; case 94: /* Eat lite */ num = 45; break; case 95: case 96: /* Trunk */ num = 46; break; case 97: /* Animal */ num = 47; break; case 98: /* Tentacles */ num = 48; break; case 99: /* Raw Chaos */ num = 49; break; case 100: case 101: case 102: /* Normal */ num = 50; break; case 103: /* Wraith */ num = 51; break; case 104: /* Poly wound */ num = 52; break; case 105: /* Disease */ num = 53; break; case 106: /* Dragon */ num = 54; break; case 107: case 108: /* Esp */ num = 55; break; case 109: /* Sick */ num = 56; break; case 110: case 111: /* Chaos warriors already have a chaos deity */ if (p_ptr->rp.pclass != CLASS_CHAOS_WARRIOR) { /* Patron */ num = 57; } break; case 112: /* Shadow walk */ num = 58; break; case 113: case 114: /* Warn */ num = 59; break; case 115: /* Invuln */ num = 60; break; case 116: case 117: /* Healing */ num = 61; break; case 118: /* HP2SP */ num = 62; break; case 119: /* Disarm */ num = 63; break; case 120: case 121: case 122: /* Strong */ num = 64; break; case 123: case 124: case 125: /* Weak */ num = 65; break; case 126: case 127: case 128: /* Smart */ num = 66; break; case 129: case 130: case 131: /* Dumb */ num = 67; break; case 132: case 133: /* Con */ num = 68; break; case 134: case 135: /* Fat */ num = 69; break; case 136: case 137: /* Frail */ num = 70; break; case 138: case 139: case 140: /* Rot */ num = 71; break; case 141: case 142: /* Squeak */ /* Restricted to chars with several muts already */ if (count_mutations() >= 3) { num = 72; } break; case 143: case 144: /* Blank face */ num = 73; break; case 145: /* Illusion face */ num = 74; break; case 146: case 147: case 148: /* eyes */ num = 75; break; case 149: case 150: /* Res magic */ num = 76; break; case 151: case 152: case 153: /* Noise */ num = 77; break; case 154: case 155: case 156: /* Infra */ num = 78; break; case 157: case 158: /* Legs fast */ num = 79; break; case 159: case 160: /* Legs slow */ num = 80; break; case 161: case 162: /* Aura elec */ num = 81; break; case 163: case 164: /* Aura fire */ num = 82; break; case 165: case 166: case 167: /* Warts */ num = 83; break; case 168: case 169: case 170: /* Scales */ num = 84; break; case 171: case 172: /* Iron */ num = 85; break; case 173: case 174: /* Wings */ num = 86; break; case 175: case 176: case 177: /* Res fear */ num = 87; break; case 178: case 179: /* Regen */ num = 88; break; case 180: case 181: /* ESP */ num = 89; break; case 182: case 183: case 184: /* Limber */ num = 90; break; case 185: case 186: case 187: /* Arthritis */ num = 91; break; case 188: /* Bad luck */ num = 92; break; case 189: /* Bad element */ /* Restricted to chars with several muts already */ if (count_mutations() >= 3) { num = 93; } break; case 190: case 191: case 192: /* Stealth */ num = 94; break; case 193: /* Good luck */ num = 95; break; default: num = -1; } /* Lower chance of getting an unusable mutation */ if (num >= 0 && mutations[num].level > p_ptr->lev && !one_in_(mutations[num].level - p_ptr->lev + 1)) { num = -1; } /* Have we picked anything? */ if (num >= 0) { /* Can we gain / lose the mutation as desired? */ if (num < MUT_PER_SET) { flag = p_ptr->muta1; } else if (num < MUT_PER_SET * 2) { flag = p_ptr->muta2; } else { flag = p_ptr->muta3; } /* Save the mutation we are using */ *mutation = num; return ((flag & mutations[num].which ? FALSE : TRUE) == gain); } } return FALSE; } /* * Gain a mutation */ bool gain_mutation(int choose_mut) { const mutation_type *mut_ptr; u32b muta_which; int num; /* Choose a mutation */ if (!select_mutation(choose_mut, TRUE, &num)) { msgf("You feel normal."); return FALSE; } else { chg_virtue(V_CHANCE, 1); if (p_ptr->rp.prace == RACE_VAMPIRE && !(p_ptr->muta1 & MUT1_HYPN_GAZE) && (randint1(10) < 7)) { num = M1_HYPN_GAZE; } else if (p_ptr->rp.prace == RACE_IMP && !(p_ptr->muta2 & MUT2_HORNS) && (randint1(10) < 7)) { num = M2_HORNS; } else if (p_ptr->rp.prace == RACE_YEEK && !(p_ptr->muta1 & MUT1_SHRIEK) && (randint1(10) < 7)) { num = M1_SHRIEK; } else if (p_ptr->rp.prace == RACE_BEASTMAN && !(p_ptr->muta1 & MUT1_POLYMORPH) && (randint1(10) < 2)) { num = M1_POLYMORPH; } else if (p_ptr->rp.prace == RACE_MIND_FLAYER && !(p_ptr->muta2 & MUT2_TENTACLES) && (randint1(10) < 7)) { num = M2_TENTACLES; } /* Point to the mutation */ mut_ptr = &mutations[num]; muta_which = mut_ptr->which; msgf("You mutate!"); msgf(mut_ptr->gain_text); /* Gain the mutation */ if (num < MUT_PER_SET) { p_ptr->muta1 |= muta_which; } else if (num < MUT_PER_SET * 2) { p_ptr->muta2 |= muta_which; } else { p_ptr->muta3 |= muta_which; } /* Some mutations cancel others */ if (num >= MUT_PER_SET * 2) { if (muta_which == MUT3_PUNY) { if (p_ptr->muta3 & MUT3_HYPER_STR) { msgf("You no longer feel super-strong!"); p_ptr->muta3 &= ~(MUT3_HYPER_STR); } } else if (muta_which == MUT3_HYPER_STR) { if (p_ptr->muta3 & MUT3_PUNY) { msgf("You no longer feel puny!"); p_ptr->muta3 &= ~(MUT3_PUNY); } } else if (muta_which == MUT3_MORONIC) { if (p_ptr->muta3 & MUT3_HYPER_INT) { msgf("Your brain is no longer a living computer."); p_ptr->muta3 &= ~(MUT3_HYPER_INT); } } else if (muta_which == MUT3_HYPER_INT) { if (p_ptr->muta3 & MUT3_MORONIC) { msgf("You are no longer moronic."); p_ptr->muta3 &= ~(MUT3_MORONIC); } } else if (muta_which == MUT3_IRON_SKIN) { if (p_ptr->muta3 & MUT3_SCALES) { msgf("You lose your scales."); p_ptr->muta3 &= ~(MUT3_SCALES); } if (p_ptr->muta3 & MUT3_FLESH_ROT) { msgf("Your flesh rots no longer."); p_ptr->muta3 &= ~(MUT3_FLESH_ROT); } if (p_ptr->muta3 & MUT3_WART_SKIN) { msgf("You lose your warts."); p_ptr->muta3 &= ~(MUT3_WART_SKIN); } } else if ((muta_which == MUT3_WART_SKIN) || (muta_which == MUT3_SCALES) || (muta_which == MUT3_FLESH_ROT)) { if (p_ptr->muta3 & MUT3_IRON_SKIN) { msgf("Your skin is no longer made of steel."); p_ptr->muta3 &= ~(MUT3_IRON_SKIN); } } else if (muta_which == MUT3_FEARLESS) { if (p_ptr->muta2 & MUT2_COWARDICE) { msgf("You are no longer cowardly."); p_ptr->muta2 &= ~(MUT2_COWARDICE); } } else if (muta_which == MUT3_FLESH_ROT) { if (p_ptr->muta3 & MUT3_REGEN) { msgf("You stop regenerating."); p_ptr->muta3 &= ~(MUT3_REGEN); } } else if (muta_which == MUT3_REGEN) { if (p_ptr->muta3 & MUT3_FLESH_ROT) { msgf("Your flesh stops rotting."); p_ptr->muta3 &= ~(MUT3_FLESH_ROT); } } else if (muta_which == MUT3_LIMBER) { if (p_ptr->muta3 & MUT3_ARTHRITIS) { msgf("Your joints stop hurting."); p_ptr->muta3 &= ~(MUT3_ARTHRITIS); } } else if (muta_which == MUT3_ARTHRITIS) { if (p_ptr->muta3 & MUT3_LIMBER) { msgf("You no longer feel limber."); p_ptr->muta3 &= ~(MUT3_LIMBER); } } } else if (num >= MUT_PER_SET) { if (muta_which == MUT2_COWARDICE) { if (p_ptr->muta3 & MUT3_FEARLESS) { msgf("You no longer feel fearless."); p_ptr->muta3 &= ~(MUT3_FEARLESS); } } if (muta_which == MUT2_BEAK) { if (p_ptr->muta2 & MUT2_TRUNK) { msgf("Your nose is no longer elephantine."); p_ptr->muta2 &= ~(MUT2_TRUNK); } } if (muta_which == MUT2_TRUNK) { if (p_ptr->muta2 & MUT2_BEAK) { msgf("You no longer have a hard beak."); p_ptr->muta2 &= ~(MUT2_BEAK); } } } p_ptr->update |= PU_BONUS; handle_stuff(); return TRUE; } } /* * Lose a mutation */ bool lose_mutation(int choose_mut) { int num; u32b muta_which; const mutation_type *mut_ptr; if (!select_mutation(choose_mut, FALSE, &num)) { return FALSE; } else { /* Point to the mutation */ mut_ptr = &mutations[num]; muta_which = mut_ptr->which; msgf(mut_ptr->lose_text); if (num < MUT_PER_SET) { p_ptr->muta1 &= ~(muta_which); } else if (num < MUT_PER_SET * 2) { p_ptr->muta2 &= ~(muta_which); } else { p_ptr->muta3 &= ~(muta_which); } p_ptr->update |= PU_BONUS; handle_stuff(); return TRUE; } } /* * Print out a description of the current mutations */ void dump_mutations(FILE *fff) { const mutation_type *mut_ptr; int i; if (!fff) return; /* Run through the mutations */ for (i = 0; i < MUT_PER_SET * 3; i++) { mut_ptr = &mutations[i]; if (player_has_mut(i)) { froff(fff, "%s\n", mut_ptr->desc_text); } } } /* * List mutations we have... */ bool do_cmd_knowledge_mutations(int dummy) { FILE *fff; char file_name[1024]; /* Hack - ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, sizeof(file_name)); /* Failure */ if (!fff) return (FALSE); /* Dump the mutations to file */ if (fff) dump_mutations(fff); /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Mutations", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } int count_mutations(void) { return (count_bits(p_ptr->muta1) + count_bits(p_ptr->muta2) + count_bits(p_ptr->muta3)); } /* * Use an activatable mutation power */ void mutation_power_aux(const mutation_type *mut_ptr) { int px = p_ptr->px; int py = p_ptr->py; int dir = 0; int lvl = p_ptr->lev; cptr q, s; if (!(racial_aux(mut_ptr->level, mut_ptr->cost, mut_ptr->stat, mut_ptr->diff))) return; if (mut_ptr->which == MUT1_SPIT_ACID) { msgf("You spit acid..."); if (get_aim_dir(&dir)) { (void)fire_ball(GF_ACID, dir, lvl, 1 + (lvl / 30)); } } else if (mut_ptr->which == MUT1_BR_FIRE) { msgf("You breathe fire..."); if (get_aim_dir(&dir)) { (void)fire_ball(GF_FIRE, dir, lvl * 2, 1 + (lvl / 20)); } } else if (mut_ptr->which == MUT1_HYPN_GAZE) { msgf("Your eyes look mesmerizing..."); if (get_aim_dir(&dir)) { (void)charm_monster(dir, lvl); } } else if (mut_ptr->which == MUT1_TELEKINES) { msgf("You concentrate..."); if (get_aim_dir(&dir)) { fetch(dir, lvl * 10, TRUE); } } else if (mut_ptr->which == MUT1_VTELEPORT) { msgf("You concentrate..."); teleport_player(10 + 4 * lvl); } else if (mut_ptr->which == MUT1_MIND_BLST) { msgf("You concentrate..."); if (get_aim_dir(&dir)) { (void)fire_bolt(GF_PSI, dir, damroll(3 + ((lvl - 1) / 5), 3)); } else { /* Is this statement needed? */ return; } } else if (mut_ptr->which == MUT1_RADIATION) { msgf("Radiation flows from your body!"); (void)fire_ball(GF_NUKE, 0, (lvl * 2), 3 + (lvl / 20)); } else if (mut_ptr->which == MUT1_VAMPIRISM) { int x, y, dummy; cave_type *c_ptr; /* Handle player fear */ if (p_ptr->tim.afraid) { /* Message */ msgf("You are too afraid!"); return; } /* Only works on adjacent monsters */ if (!get_rep_dir(&dir)) return; y = py + ddy[dir]; x = px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; c_ptr = area(x, y); if (!(c_ptr->m_idx)) { msgf("You bite into thin air!"); return; } msgf("You grin and bare your fangs..."); dummy = lvl * 2; if (drain_gain_life(dir, dummy)) { /* Gain nutritional sustenance: 150/hp drained */ /* A Food ration gives 5000 food points (by contrast) */ /* Don't ever get more than "Full" this way */ /* But if we ARE Gorged, it won't cure us */ dummy = p_ptr->food + MIN(5000, 100 * dummy); if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy); } else msgf("Yechh. That tastes foul."); } else if (mut_ptr->which == MUT1_SMELL_MET) { (void)detect_treasure(); } else if (mut_ptr->which == MUT1_SMELL_MON) { (void)detect_monsters_normal(); } else if (mut_ptr->which == MUT1_BLINK) { teleport_player(10); } else if (mut_ptr->which == MUT1_EAT_ROCK) { int x, y, ox, oy; cave_type *c_ptr; if (!get_rep_dir(&dir)) return; y = py + ddy[dir]; x = px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; c_ptr = area(x, y); if (cave_floor_grid(c_ptr)) { msgf("You bite into thin air!"); return; } else if (cave_perma_grid(c_ptr) || (c_ptr->feat == FEAT_MOUNTAIN)) { msgf("Ouch! This wall is harder than your teeth!"); return; } else if (c_ptr->m_idx) { msgf("There's something in the way!"); return; } else if (c_ptr->feat == FEAT_TREES) { msgf("You don't like the woody taste!"); return; } else { if ((c_ptr->feat >= FEAT_CLOSED) && (c_ptr->feat <= FEAT_RUBBLE)) { (void)set_food(p_ptr->food + 3000); } else if ((c_ptr->feat >= FEAT_MAGMA) && (c_ptr->feat <= FEAT_QUARTZ_K)) { (void)set_food(p_ptr->food + 5000); } else { msgf("This granite is very filling!"); (void)set_food(p_ptr->food + 10000); } } (void)wall_to_mud(dir); /* Save old location */ oy = py; ox = px; /* Move the player */ py = y; px = x; /* Move the player */ p_ptr->py = y; p_ptr->px = x; /* Notice movement */ Term_move_player(); if (!p_ptr->depth) { /* Scroll wilderness */ p_ptr->wilderness_x = px; p_ptr->wilderness_y = py; move_wild(); } lite_spot(px, py); lite_spot(ox, oy); /* Process fields under the player. */ field_script(area(px, py), FIELD_ACT_PLAYER_ENTER, ""); verify_panel(); p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); p_ptr->update |= (PU_DISTANCE); p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } else if (mut_ptr->which == MUT1_SWAP_POS) { if (get_aim_dir(&dir)) { (void)teleport_swap(dir); } } else if (mut_ptr->which == MUT1_SHRIEK) { (void)fire_ball(GF_SOUND, 0, 2 * lvl, 8); (void)aggravate_monsters(0); } else if (mut_ptr->which == MUT1_ILLUMINE) { (void)lite_area(damroll(2, (lvl / 2)), (lvl / 10) + 1); } else if (mut_ptr->which == MUT1_DET_CURSE) { object_type *o_ptr; OBJ_ITT_START (p_ptr->inventory, o_ptr) { if (!o_ptr->k_idx) continue; if (!cursed_p(o_ptr)) continue; o_ptr->feeling = FEEL_CURSED; } OBJ_ITT_END; } else if (mut_ptr->which == MUT1_BERSERK) { if (!p_ptr->tim.shero) { (void)hp_player(30); } (void)inc_shero(rand_range(25, 50)); (void)clear_afraid(); } else if (mut_ptr->which == MUT1_POLYMORPH) { do_poly_self(); } else if (mut_ptr->which == MUT1_MIDAS_TCH) { (void)alchemy(); } /* Summon pet molds around the player */ else if (mut_ptr->which == MUT1_GROW_MOLD) { int i; for (i = 0; i < 8; i++) { (void)summon_specific(-1, px, py, lvl, SUMMON_BIZARRE1, FALSE, TRUE, TRUE); } } else if (mut_ptr->which == MUT1_RESIST) { int num = lvl / 10; int dur = rand_range(20, 40); if (randint0(5) < num) { (void)inc_oppose_acid(dur); num--; } if (randint0(4) < num) { (void)inc_oppose_elec(dur); num--; } if (randint0(3) < num) { (void)inc_oppose_fire(dur); num--; } if (randint0(2) < num) { (void)inc_oppose_cold(dur); num--; } if (num) { (void)inc_oppose_pois(dur); num--; } } else if (mut_ptr->which == MUT1_EARTHQUAKE) { (void)earthquake(px, py, 10); } else if (mut_ptr->which == MUT1_EAT_MAGIC) { object_type *o_ptr; int lev; item_tester_hook = item_tester_hook_recharge; /* Get an item */ q = "Drain which item? "; s = "You have nothing to drain."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return; lev = get_object_level(o_ptr); if (o_ptr->tval == TV_ROD) { if (o_ptr->pval > 0) { msgf("You can't absorb energy from a discharged rod."); } else { p_ptr->csp += 2 * lev; o_ptr->pval = 500; } } else { if (o_ptr->pval > 0) { p_ptr->csp += o_ptr->pval * lev; o_ptr->pval = 0; } else { msgf("There's no energy there to absorb!"); } o_ptr->info |= OB_EMPTY; } if (p_ptr->csp > p_ptr->msp) { p_ptr->csp = p_ptr->msp; } /* Notice changes */ notice_inven(); } else if (mut_ptr->which == MUT1_WEIGH_MAG) { report_magics(); } /* Fake a population explosion. */ else if (mut_ptr->which == MUT1_STERILITY) { msgf("You suddenly have a headache!"); take_hit(rand_range(17, 34), "the strain of forcing abstinence"); num_repro += MAX_REPRO; } else if (mut_ptr->which == MUT1_PANIC_HIT) { int x, y; if (!get_rep_dir(&dir)) return; y = py + ddy[dir]; x = px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; if (area(x, y)->m_idx) { py_attack(x, y); teleport_player(30); } else { msgf("You don't see any monster in this direction"); message_flush(); } } else if (mut_ptr->which == MUT1_DAZZLE) { (void)stun_monsters(lvl * 4); (void)confuse_monsters(lvl * 4); (void)turn_monsters(lvl * 4); } else if (mut_ptr->which == MUT1_LASER_EYE) { if (get_aim_dir(&dir)) (void)fire_beam(GF_LITE, dir, 2 * lvl); } else if (mut_ptr->which == MUT1_RECALL) { word_of_recall(); } else if (mut_ptr->which == MUT1_BANISH) { int x, y; cave_type *c_ptr; monster_type *m_ptr; monster_race *r_ptr; if (!get_rep_dir(&dir)) return; y = py + ddy[dir]; x = px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; c_ptr = area(x, y); if (!c_ptr->m_idx) { msgf("You sense no evil there!"); return; } m_ptr = &m_list[c_ptr->m_idx]; r_ptr = &r_info[m_ptr->r_idx]; if (FLAG(r_ptr, RF_EVIL) && !FLAG(r_ptr, RF_QUESTOR) && !FLAG(r_ptr, RF_UNIQUE)) { /* Delete the monster, rather than killing it. */ delete_monster_idx(c_ptr->m_idx); msgf ("The evil creature vanishes in a puff of sulfurous smoke!"); } else { msgf("Your invocation is ineffectual!"); } } else if (mut_ptr->which == MUT1_COLD_TOUCH) { int x, y; cave_type *c_ptr; if (!get_rep_dir(&dir)) return; y = py + ddy[dir]; x = px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; c_ptr = area(x, y); if (!c_ptr->m_idx) { msgf("You wave your hands in the air."); return; } (void)fire_bolt(GF_COLD, dir, 2 * lvl); } /* Gives a multiplier of 2 at first, up to 3 at level 30 */ else if (mut_ptr->which == MUT1_LAUNCHER) { do_cmd_throw_aux(2 + lvl / 30); } } /* * Proces the random mutations */ void mutation_random_aux(const mutation_type *mut_ptr) { if (!one_in_(mut_ptr->chance * 100)) return; if (mut_ptr->which == MUT2_BERS_RAGE) { disturb(FALSE); msgf("RAAAAGHH!"); msgf("You feel a fit of rage coming over you!"); (void)inc_shero(10 + randint1(p_ptr->lev)); } else if (mut_ptr->which == MUT2_COWARDICE) { if (!((FLAG(p_ptr, TR_RES_FEAR)) || p_ptr->tim.hero || p_ptr->tim.shero)) { disturb(FALSE); msgf("It's so dark... so scary!"); (void)inc_afraid(rand_range(13, 40)); } } else if (mut_ptr->which == MUT2_RTELEPORT) { if (!(FLAG(p_ptr, TR_RES_NEXUS)) && !(p_ptr->muta1 & MUT1_VTELEPORT) && !(FLAG(p_ptr, TR_NO_TELE))) { disturb(FALSE); /* Teleport player */ msgf("Your position suddenly seems very uncertain..."); message_flush(); teleport_player(40); } } else if (mut_ptr->which == MUT2_ALCOHOL) { if (!(FLAG(p_ptr, TR_RES_CONF)) && !(FLAG(p_ptr, TR_RES_CHAOS))) { disturb(FALSE); p_ptr->redraw |= PR_EXTRA; msgf("You feel a SSSCHtupor cOmINg over yOu... *HIC*!"); } if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(15, 35)); } if (!(FLAG(p_ptr, TR_RES_CHAOS))) { if (one_in_(20)) { message_flush(); if (one_in_(3)) (void)lose_all_info(); else wiz_dark(); teleport_player(100); wiz_dark(); msgf("You wake up somewhere with a sore head..."); msgf("You can't remember a thing, or how you got here!"); } else { if (one_in_(3)) { msgf("Thishcischs GooDSChtuff!"); (void)inc_image(rand_range(150, 300)); } } } } else if (mut_ptr->which == MUT2_HALLU) { if (!(FLAG(p_ptr, TR_RES_CHAOS))) { disturb(FALSE); p_ptr->redraw |= PR_EXTRA; (void)inc_image(rand_range(20, 70)); } } else if (mut_ptr->which == MUT2_FLATULENT) { disturb(FALSE); msgf("BRRAAAP! Oops."); message_flush(); (void)fire_ball(GF_POIS, 0, p_ptr->lev, 3); } else if ((mut_ptr->which == MUT2_PROD_MANA) && !(FLAG(p_ptr, TR_NO_MAGIC))) { int dire = 0; disturb(FALSE); msgf("Magical energy flows through you! You must release it!"); flush(); message_flush(); (void)get_hack_dir(&dire); (void)fire_ball(GF_MANA, dire, p_ptr->lev * 2, 3); } else if ((mut_ptr->which == MUT2_ATT_DEMON) && !(FLAG(p_ptr, TR_NO_MAGIC))) { bool pet = (one_in_(6)); if (summon_specific((pet ? -1 : 0), p_ptr->px, p_ptr->py, p_ptr->depth, SUMMON_DEMON, TRUE, FALSE, pet)) { msgf("You have attracted a demon!"); disturb(FALSE); } } else if (mut_ptr->which == MUT2_SPEED_FLUX) { disturb(FALSE); if (one_in_(2)) { msgf("You feel less energetic."); if (p_ptr->tim.fast > 0) { (void)clear_fast(); } else { (void)inc_slow(rand_range(10, 40)); } } else { msgf("You feel more energetic."); if (p_ptr->tim.slow > 0) { (void)clear_slow(); } else { (void)inc_fast(rand_range(10, 40)); } } message_flush(); } else if (mut_ptr->which == MUT2_BANISH_ALL) { disturb(FALSE); msgf("You suddenly feel almost lonely."); (void)banish_monsters(100); message_flush(); } else if (mut_ptr->which == MUT2_EAT_LIGHT) { object_type *o_ptr; cave_type *c_ptr = area(p_ptr->px, p_ptr->py); msgf("A shadow passes over you."); message_flush(); /* Absorb light from the current possition */ if (c_ptr->info & CAVE_GLOW) { (void)hp_player(10); } o_ptr = &p_ptr->equipment[EQUIP_LITE]; /* Absorb some fuel in the current lite */ if (o_ptr->tval == TV_LITE) { /* Use some fuel (except on artifacts) */ if (!(FLAG(o_ptr, TR_INSTA_ART)) && (o_ptr->timeout > 0)) { /* Heal the player a bit */ (void)hp_player(o_ptr->timeout / 20); /* Decrease life-span of lite */ o_ptr->timeout /= 2; msgf("You absorb energy from your light!"); /* Notice interesting fuel steps */ notice_lite_change(o_ptr); } } /* * Unlite the area (radius 10) around player and * do 50 points damage to every affected monster */ (void)unlite_area(50, 10); } else if ((mut_ptr->which == MUT2_ATT_ANIMAL) && !(FLAG(p_ptr, TR_NO_MAGIC))) { bool pet = (one_in_(3)); if (summon_specific((pet ? -1 : 0), p_ptr->px, p_ptr->py, p_ptr->depth, SUMMON_ANIMAL, TRUE, FALSE, pet)) { msgf("You have attracted an animal!"); disturb(FALSE); } } else if ((mut_ptr->which == MUT2_RAW_CHAOS) && !(FLAG(p_ptr, TR_NO_MAGIC))) { disturb(FALSE); msgf("You feel the world warping around you!"); message_flush(); (void)fire_ball(GF_CHAOS, 0, p_ptr->lev, 8); } else if (mut_ptr->which == MUT2_NORMALITY) { if (!lose_mutation(0)) { msgf("You feel oddly normal."); } } else if ((mut_ptr->which == MUT2_WRAITH) && !(FLAG(p_ptr, TR_NO_MAGIC))) { disturb(FALSE); msgf("You feel insubstantial!"); message_flush(); (void)inc_wraith_form(rand_range(p_ptr->lev / 2, p_ptr->lev)); } else if (mut_ptr->which == MUT2_POLY_WOUND) { do_poly_wounds(); } else if (mut_ptr->which == MUT2_WASTING) { int which_stat = randint0(6); int sustained = FALSE; switch (which_stat) { case A_STR: if (FLAG(p_ptr, TR_SUST_STR)) sustained = TRUE; break; case A_INT: if (FLAG(p_ptr, TR_SUST_INT)) sustained = TRUE; break; case A_WIS: if (FLAG(p_ptr, TR_SUST_WIS)) sustained = TRUE; break; case A_DEX: if (FLAG(p_ptr, TR_SUST_DEX)) sustained = TRUE; break; case A_CON: if (FLAG(p_ptr, TR_SUST_CON)) sustained = TRUE; break; case A_CHR: if (FLAG(p_ptr, TR_SUST_CHR)) sustained = TRUE; break; default: msgf("Invalid stat chosen!"); sustained = TRUE; } if (!sustained) { disturb(FALSE); msgf("You can feel yourself wasting away!"); message_flush(); (void)dec_stat(which_stat, rand_range(6, 12), 0); } } else if ((mut_ptr->which == MUT2_ATT_DRAGON) && !(FLAG(p_ptr, TR_NO_MAGIC))) { bool pet = (one_in_(5)); if (summon_specific((pet ? -1 : 0), p_ptr->px, p_ptr->py, p_ptr->depth, SUMMON_DRAGON, TRUE, FALSE, pet)) { msgf("You have attracted a dragon!"); disturb(FALSE); } } else if ((mut_ptr->which == MUT2_WEIRD_MIND) && !(FLAG(p_ptr, TR_NO_MAGIC))) { if (p_ptr->tim.esp > 0) { msgf("Your mind feels cloudy!"); (void)clear_tim_esp(); } else { msgf("Your mind expands!"); (void)inc_tim_esp(p_ptr->lev); } } else if ((mut_ptr->which == MUT2_NAUSEA) && !(FLAG(p_ptr, TR_SLOW_DIGEST))) { disturb(FALSE); msgf("Your stomach roils, and you lose your lunch!"); message_flush(); (void)set_food(PY_FOOD_WEAK); } else if ((mut_ptr->which == MUT2_WALK_SHAD) && !(FLAG(p_ptr, TR_NO_MAGIC))) { alter_reality(); } else if (mut_ptr->which == MUT2_WARNING) { int danger_amount = 0; int monster; for (monster = 0; monster < m_max; monster++) { monster_type *m_ptr = &m_list[monster]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; if (r_ptr->level >= p_ptr->lev) { danger_amount += r_ptr->level - p_ptr->lev + 1; } } if (danger_amount > 100) msgf("You feel utterly terrified!"); else if (danger_amount > 50) msgf("You feel terrified!"); else if (danger_amount > 20) msgf("You feel very worried!"); else if (danger_amount > 10) msgf("You feel paranoid!"); else if (danger_amount > 5) msgf("You feel almost safe."); else msgf("You feel lonely."); } else if ((mut_ptr->which == MUT2_INVULN) && !(FLAG(p_ptr, TR_NO_MAGIC))) { disturb(FALSE); msgf("You feel invincible!"); message_flush(); (void)inc_invuln(rand_range(8, 16)); } else if (mut_ptr->which == MUT2_SP_TO_HP) { int wounds = p_ptr->mhp - p_ptr->chp; if (wounds > 0) { int healing = p_ptr->csp; if (healing > wounds) { healing = wounds; } (void)hp_player(healing); p_ptr->csp -= healing; } } else if ((mut_ptr->which == MUT2_HP_TO_SP) && !(FLAG(p_ptr, TR_NO_MAGIC))) { int wounds = p_ptr->msp - p_ptr->csp; if (wounds > 0) { int healing = p_ptr->chp; if (healing > wounds) { healing = wounds; } p_ptr->csp += healing; take_hit(healing, "blood rushing to the head"); } } else if (mut_ptr->which == MUT2_DISARM) { object_type *o_ptr; disturb(FALSE); msgf("You trip over your own feet!"); take_hit(randint1(p_ptr->rp.wt / 6), "tripping"); message_flush(); o_ptr = &p_ptr->equipment[EQUIP_WIELD]; if ((o_ptr->k_idx) && !cursed_p(o_ptr)) { msgf("You drop your weapon!"); inven_drop(o_ptr, 1); } } } /* * Constant mutation effects * * Note that the commented out effects are actually handled in player_flags(). */ void mutation_effect(void) { if (p_ptr->muta1 & MUT1_HYPN_GAZE) { p_ptr->stat[A_WIS].add -= 1; } if (p_ptr->muta1 & MUT1_TELEKINES) { p_ptr->stat[A_CON].add -= 1; } if (p_ptr->muta1 & MUT1_MIND_BLST) { p_ptr->stat[A_STR].add -= 1; } if (p_ptr->muta1 & MUT1_RADIATION) { p_ptr->stat[A_CON].add -= 1; } if (p_ptr->muta1 & MUT1_SHRIEK) { p_ptr->stat[A_CHR].add -= 1; } if (p_ptr->muta1 & MUT1_ILLUMINE) { p_ptr->skills[SKILL_STL] -= 1; } if (p_ptr->muta1 & MUT1_BERSERK) { p_ptr->stat[A_WIS].add -= 1; } if (p_ptr->muta1 & MUT1_MIDAS_TCH) { p_ptr->stat[A_CHR].add -= 1; p_ptr->stat[A_WIS].add -= 1; } if (p_ptr->muta1 & MUT1_RESIST) { p_ptr->skills[SKILL_SAV] -= 10; } if (p_ptr->muta1 & MUT1_EARTHQUAKE) { p_ptr->stat[A_WIS].add -= 1; } if (p_ptr->muta1 & MUT1_STERILITY) { p_ptr->stat[A_INT].add -= 1; } if (p_ptr->muta1 & MUT1_DAZZLE) { p_ptr->skills[SKILL_STL] -= 1; } if (p_ptr->muta1 & MUT1_LASER_EYE) { p_ptr->skills[SKILL_SNS] -= 10; } if (p_ptr->muta1 & MUT1_COLD_TOUCH) { p_ptr->stat[A_DEX].add -= 1; } if (p_ptr->muta1 & MUT1_LAUNCHER) { p_ptr->stat[A_DEX].add -= 1; } if (p_ptr->muta2 & MUT2_SCOR_TAIL) { p_ptr->stat[A_CHR].add -= 2; } if (p_ptr->muta2 & MUT2_TRUNK) { p_ptr->stat[A_CHR].add -= 1; } if (p_ptr->muta2 & MUT2_TENTACLES) { p_ptr->stat[A_DEX].add += 1; p_ptr->stat[A_CHR].add -= 2; } if (p_ptr->muta2 & MUT2_WRAITH) { p_ptr->stat[A_CON].add -= 3; } if (p_ptr->muta2 & MUT2_INVULN) { p_ptr->stat[A_WIS].add -= 2; } /* Hyper Strength */ if (p_ptr->muta3 & MUT3_HYPER_STR) { p_ptr->stat[A_STR].add += 4; p_ptr->stat[A_INT].add -= 1; p_ptr->stat[A_WIS].add -= 1; } /* Puny */ if (p_ptr->muta3 & MUT3_PUNY) { p_ptr->stat[A_STR].add -= 4; p_ptr->stat[A_DEX].add += 2; } /* Living computer */ if (p_ptr->muta3 & MUT3_HYPER_INT) { p_ptr->stat[A_INT].add += 4; p_ptr->stat[A_WIS].add += 4; /* p_ptr->flags[3] |= TR3_HURT_ELEC */ } /* Moronic */ if (p_ptr->muta3 & MUT3_MORONIC) { p_ptr->stat[A_INT].add -= 4; p_ptr->stat[A_WIS].add -= 4; /* p_ptr->flags[1] |= TR1_RES_FEAR */ /* p_ptr->flags[1] |= TR1_RES_CONF */ } if (p_ptr->muta3 & MUT3_RESILIENT) { p_ptr->stat[A_CON].add += 4; } if (p_ptr->muta3 & MUT3_XTRA_FAT) { p_ptr->stat[A_CON].add += 2; p_ptr->pspeed -= 2; } if (p_ptr->muta3 & MUT3_ALBINO) { p_ptr->stat[A_CON].add -= 4; /* p_ptr->flags[1] |= TR1_RES_DARK */ } if (p_ptr->muta3 & MUT3_FLESH_ROT) { p_ptr->stat[A_CON].add -= 2; p_ptr->stat[A_CHR].add -= 1; /* p_ptr->flags[2] &= ~(TR2_REGEN); */ /* Cancel innate regeneration */ } if (p_ptr->muta3 & MUT3_SILLY_VOI) { p_ptr->stat[A_CHR].add -= 4; } if (p_ptr->muta3 & MUT3_BLANK_FAC) { p_ptr->stat[A_CHR].add -= 1; /* p_ptr->Flags3 |= TR2_SEE_INVIS; */ } if (p_ptr->muta3 & MUT3_XTRA_EYES) { p_ptr->skills[SKILL_FOS] += 15; p_ptr->skills[SKILL_SNS] += 15; p_ptr->stat[A_CHR].add -= 1; } if (p_ptr->muta3 & MUT3_MAGIC_RES) { p_ptr->skills[SKILL_SAV] += (15 + (p_ptr->lev / 5)); } if (p_ptr->muta3 & MUT3_XTRA_NOIS) { p_ptr->skills[SKILL_STL] -= 3; } if (p_ptr->muta3 & MUT3_INFRAVIS) { p_ptr->see_infra += 3; } if (p_ptr->muta3 & MUT3_XTRA_LEGS) { p_ptr->pspeed += 3; p_ptr->stat[A_DEX].add -= 1; } if (p_ptr->muta3 & MUT3_SHORT_LEG) { p_ptr->stat[A_CON].add += 1; p_ptr->pspeed -= 3; } if (p_ptr->muta3 & MUT3_ELEC_TOUC) { p_ptr->stat[A_CON].add -= 1; /* SET_FLAG(p_ptr, TR_SH_ELEC) */; } if (p_ptr->muta3 & MUT3_FIRE_BODY) { p_ptr->stat[A_DEX].add -= 1; /* SET_FLAG(p_ptr, TR_SH_FIRE) */; /* SET_FLAG(p_ptr, TR_LITE) */; } if (p_ptr->muta3 & MUT3_WART_SKIN) { p_ptr->stat[A_CHR].add -= 2; p_ptr->to_a += 5; p_ptr->dis_to_a += 5; } if (p_ptr->muta3 & MUT3_SCALES) { p_ptr->stat[A_CHR].add -= 1; p_ptr->to_a += 10; p_ptr->dis_to_a += 10; } if (p_ptr->muta3 & MUT3_IRON_SKIN) { p_ptr->stat[A_DEX].add -= 3; p_ptr->to_a += 25; p_ptr->dis_to_a += 25; } if (p_ptr->muta3 & MUT3_WINGS) { p_ptr->stat[A_CON].add -= 1; p_ptr->stat[A_CHR].add += 3; /* SET_FLAG(p_ptr, TR_FEATHER) */; } if (p_ptr->muta3 & MUT3_FEARLESS) { /* SET_FLAG(p_ptr, TR_RES_FEAR) */; } if (p_ptr->muta3 & MUT3_REGEN) { /* SET_FLAG(p_ptr, TR_REGEN) */; } if (p_ptr->muta3 & MUT3_ESP) { p_ptr->stat[A_CON].add -= 1; /* SET_FLAG(p_ptr, TR_TELEPATHY) */; } if (p_ptr->muta3 & MUT3_LIMBER) { p_ptr->stat[A_DEX].add += 3; p_ptr->stat[A_STR].add -= 1; } if (p_ptr->muta3 & MUT3_ARTHRITIS) { p_ptr->stat[A_DEX].add -= 3; } if (p_ptr->muta3 & MUT3_MOTION) { /* SET_FLAG(p_ptr, TR_FREE_ACT) */; p_ptr->skills[SKILL_STL] += 1; } if (p_ptr->muta3 & MUT3_ILL_NORM) { p_ptr->stat[A_CHR].add = 0; } } zangband/src/notes.c0000755000000000000000000001020510250356274013430 0ustar rootroot/* File: notes.c */ /* Purpose: Note taking to a file */ /* * Copyright (c) 1989, 1999 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * A short helper function for add_note and other * functions that returns the file name for the note-taking file. */ cptr notes_file(void) { char fname[15]; static char buf[1024]; /* * Hack -- extract first 8 characters of name and * Create the file name from the character's name plus .txt */ (void)strnfmt(fname, 15, "%.8s.txt", player_base); path_make(buf, ANGBAND_DIR_USER, fname); /* return the filename */ return buf; } /* * Output a string to the notes file. * This is the only function that references that file. */ void output_note(cptr final_note, ...) { FILE *fff; va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, final_note); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, final_note, &vp); /* End the Varargs Stuff */ va_end(vp); /* Open notes file */ fff = my_fopen(notes_file(), "a"); /* Failure */ if (!fff) return; /* Add note, and close note file */ froff(fff, "%s", buf); my_fclose(fff); } /* * Add note to file using a string + character symbol * to specify its type so that the notes file can be * searched easily by external utilities. */ void add_note(char code, cptr note, ...) { char long_day[25]; time_t ct = time((time_t *) NULL); char depths[32]; va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, note); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, note, &vp); /* End the Varargs Stuff */ va_end(vp); /* Get depth */ if (!p_ptr->depth) { if (p_ptr->place_num) { if (place[p_ptr->place_num].quest_num) { (void)strnfmt(depths, 32, " Quest"); } else { (void)strnfmt(depths, 32, " Town"); } } else { (void)strnfmt(depths, 32, " Wild"); } } else if (depth_in_feet) { (void)strnfmt(depths, 32, "%4dft", p_ptr->depth * 50); } else { (void)strnfmt(depths, 32, "Lev%3d", p_ptr->depth); } /* Get the time */ (void)strftime(long_day, 10, "%H:%M:%S", localtime(&ct)); /* Output to the notes file */ output_note("%s %9ld %s %c: %s\n", long_day, turn, depths, code, buf); } /* * Add note to file using type specified by note_number */ void add_note_type(int note_number) { char long_day[30]; time_t ct = time((time_t *) 0); int len; /* Get the date */ (void)strftime(long_day, 30, "%Y-%m-%d at %H:%M:%S", localtime(&ct)); switch (note_number) { case NOTE_BIRTH: { /* Player has just been born */ char player[100]; /* Build the string containing the player information */ len = strnfmt(player, 100, "the %s %s", race_info[p_ptr->rp.prace].title, class_info[p_ptr->rp.pclass].title); /* No "Chaos-Warrior of Chaos" */ if (p_ptr->rp.pclass != CLASS_CHAOS_WARRIOR && p_ptr->spell.r[0].realm != REALM_NONE) { strnfcat(player, 100, &len, " of %s", realm_names[p_ptr->spell.r[0].realm]); } if (p_ptr->spell.r[1].realm != REALM_NONE) { strnfcat(player, 100, &len, " and %s", realm_names[p_ptr->spell.r[1].realm]); } /* Add in "character start" information */ output_note("\n================================================\n" "%s the %s\n" "Born on %s\n" "================================================\n\n", player_name, player, long_day); break; } case NOTE_WINNER: { output_note("%s slew the Serpent of Chaos on %s\n." "Long live %s!\n" "================================================\n", player_name, long_day, player_name); break; } case NOTE_SAVE_GAME: { /* Saving the game */ output_note("\nSession end: %s\n", long_day); break; } case NOTE_ENTER_DUNGEON: { /* Entering the game after a break. */ output_note("================================================\n" "New session start: %s\n\n", long_day); break; } } } zangband/src/obj_kind.c0000755000000000000000000001263210250356274014065 0ustar rootroot/* File: obj_kind.c */ /* Purpose: Code for the object templates */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* Base size of k_info */ #define K_INFO_BASE_SIZE 500 /* Amount of entries to add when resizing k_info */ #define K_INFO_RESIZE 50 /* Size of the allocated */ static s32b k_info_size = K_INFO_BASE_SIZE; /* Allocate k_info */ errr k_info_alloc(void) { /* Create the storage for the object templates */ C_MAKE(k_info, k_info_size, object_kind); /* Success */ return 0; } /* Free k_info */ errr k_info_free(void) { k_info_size = K_INFO_BASE_SIZE; KILL(k_info); /* Success */ return 0; } void k_info_reset(void) { int i; /* Reset the "objects" */ for (i = 1; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; /* Reset "tried" */ k_ptr->tried = FALSE; /* Reset "aware" */ k_ptr->aware = FALSE; } } /* Add a new object template */ object_kind *k_info_add(object_kind *k_info_entry) { /* Resize if necessary */ while (k_info_size <= z_info->k_max) { k_info_size += K_INFO_RESIZE; /* Reallocate the extra memory */ k_info = (object_kind *)realloc(k_info, k_info_size * sizeof(object_kind)); /* Failure */ if (!k_info) quit("Out of memory!"); /* Wipe the new memory */ (void)C_WIPE(&k_info[(k_info_size - K_INFO_RESIZE)], K_INFO_RESIZE, object_kind); } /* Increase the maximum index of the array */ z_info->k_max++; /* Copy the new object_kind */ COPY(&k_info[z_info->k_max - 1], k_info_entry, object_kind); /* Success */ return (&k_info[z_info->k_max - 1]); } /* * Initialize some other arrays */ errr init_object_alloc(void) { int i, j, p, x, y, z; object_kind *k_ptr; ego_item_type *e_ptr; alloc_entry *table; s16b num[MAX_DEPTH]; s16b aux[MAX_DEPTH]; /*** Analyze object allocation info ***/ /* Clear the "aux" array */ (void)C_WIPE(aux, MAX_DEPTH, s16b); /* Clear the "num" array */ (void)C_WIPE(num, MAX_DEPTH, s16b); /* Free the old "alloc_kind_table" (if it exists) */ if (alloc_kind_table) { KILL(alloc_kind_table); } /* Size of "alloc_kind_table" */ alloc_kind_size = 0; /* Scan the objects */ for (i = 1; i < z_info->k_max; i++) { k_ptr = &k_info[i]; /* Scan allocation pairs */ for (j = 0; j < 4; j++) { /* Count the "legal" entries */ if (k_ptr->chance[j]) { /* Count the entries */ alloc_kind_size++; /* Group by level */ num[k_ptr->locale[j]]++; } } } /* Collect the level indexes */ for (i = 1; i < MAX_DEPTH; i++) { /* Group by level */ num[i] += num[i - 1]; } /* Paranoia */ if (!num[0]) quit("No town objects!"); /*** Initialize object allocation info ***/ /* Allocate the alloc_kind_table */ C_MAKE(alloc_kind_table, alloc_kind_size, alloc_entry); /* Access the table entry */ table = alloc_kind_table; /* Scan the objects */ for (i = 1; i < z_info->k_max; i++) { k_ptr = &k_info[i]; /* Scan allocation pairs */ for (j = 0; j < 4; j++) { /* Count the "legal" entries */ if (k_ptr->chance[j]) { /* Extract the base level */ x = k_ptr->locale[j]; /* Extract the base probability */ p = (255 / k_ptr->chance[j]); /* Skip entries preceding our locale */ y = (x > 0) ? num[x - 1] : 0; /* Skip previous entries at this locale */ z = y + aux[x]; /* Load the entry */ table[z].index = i; table[z].level = x; table[z].prob1 = p; table[z].prob2 = p; /* Another entry complete for this locale */ aux[x]++; } } } /* Clear the temp arrays */ (void)C_WIPE(aux, MAX_DEPTH, s16b); (void)C_WIPE(num, MAX_DEPTH, s16b); /* Free the old ego item allocation table (if it exists) */ if (alloc_ego_table) { KILL(alloc_ego_table); } /* Create the ego item allocation table */ C_MAKE(alloc_ego_table, z_info->e_max, alloc_entry); /* Access the table */ table = alloc_ego_table; /* No ego items in the table yet */ alloc_ego_size = 0; /* Count the number of legal entries */ for (i = 1; i < z_info->e_max; i++) { e_ptr = &e_info[i]; if (e_ptr->slot) { /* Count the item */ alloc_ego_size++; /* Group by level */ num[e_ptr->level]++; } } /* Collect the level indexes */ for (i = 1; i < MAX_DEPTH; i++) { /* Group by level */ num[i] += num[i - 1]; } /* Scan the ego items */ for (i = 1; i < z_info->e_max; i++) { e_ptr = &e_info[i]; if (e_ptr->slot) { /* Extract the base level */ x = e_ptr->level; /* Extract the base probability */ p = (255 / e_ptr->rarity); /* Skip entries preceding our locale */ y = (x > 0) ? num[x - 1] : 0; /* Skip previous entries at this locale */ z = y + aux[x]; /* Load the entry */ table[z].index = i; table[z].level = x; table[z].prob1 = p; table[z].prob2 = p; /* Another entry complete for this locale */ aux[x]++; } } /* Success */ return (0); } byte get_object_level(const object_type *o_ptr) { #if 0 return (byte)get_object_level_callback(o_ptr); #else return k_info[o_ptr->k_idx].level; #endif } cptr get_object_name(const object_type *o_ptr) { #if 0 return get_object_name_callback(o_ptr); #else return (k_name + k_info[o_ptr->k_idx].name); #endif } bool object_is_potion(const object_type *o_ptr) { return (k_info[o_ptr->k_idx].tval == TV_POTION); } zangband/src/object1.c0000755000000000000000000017066210250356274013645 0ustar rootroot/* File: object1.c */ /* Purpose: Object code, part 1 */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* * Reset the "visual" lists * * This involves resetting various things to their "default" state. * * If the "prefs" flag is TRUE, then we will also load the appropriate * "user pref file" based on the current setting of the "use_graphics" * flag. This is useful for switching "graphics" on/off. * * The features, objects, and monsters, should all be encoded in the * relevant "font.pref" and/or "graf.prf" files. XXX XXX XXX * * The "prefs" parameter is no longer meaningful. XXX XXX XXX */ void reset_visuals(void) { int i; /* Extract some info about terrain features */ for (i = 0; i < z_info->f_max; i++) { feature_type *f_ptr = &f_info[i]; /* Assume we will use the underlying values */ f_ptr->x_attr = f_ptr->d_attr; f_ptr->x_char = f_ptr->d_char; /* No extra information */ f_ptr->w_attr = 0; f_ptr->w_char = 0; } /* Extract default attr/char code for objects */ for (i = 0; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; /* Default attr/char */ k_ptr->x_attr = k_ptr->d_attr; k_ptr->x_char = k_ptr->d_char; } /* Extract default attr/char code for monsters */ for (i = 0; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Default attr/char */ r_ptr->x_attr = r_ptr->d_attr; r_ptr->x_char = r_ptr->d_char; } /* Extract default attr/char code for fields */ for (i = 0; i < z_info->t_max; i++) { field_thaum *t_ptr = &t_info[i]; /* Default attr/char */ t_ptr->f_attr = t_ptr->d_attr; t_ptr->f_char = t_ptr->d_char; } if (use_graphics) { /* Process "graf.prf" */ (void)process_pref_file("graf.prf"); } /* Normal symbols */ else { /* Process "font.prf" */ (void)process_pref_file("font.prf"); } /* Reset the fake monochrome flag */ fake_monochrome = (!use_graphics || streq(ANGBAND_SYS, "ibm")) ? TRUE : FALSE; /* Fields have to notice the change of visuals. */ init_fields(); /* Update map to notice change in visuals */ update_overhead_map(); } /* * Obtain the "flags" for an item which are known to the player */ void object_flags_known(const object_type *o_ptr, object_flags *of_ptr) { const object_kind *k_ptr = &k_info[o_ptr->k_idx]; bool known = object_known_p(o_ptr); /* Clear */ of_ptr->flags[0] = 0L; of_ptr->flags[1] = 0L; of_ptr->flags[2] = 0L; of_ptr->flags[3] = 0L; if (cursed_p(o_ptr) && (known || (o_ptr->info & (OB_SENSE)))) { SET_FLAG(of_ptr, TR_CURSED); } /* Must be identified */ if (!known) return; /* Base object */ of_ptr->flags[0] = k_ptr->flags[0]; of_ptr->flags[1] = k_ptr->flags[1]; of_ptr->flags[2] = k_ptr->flags[2]; of_ptr->flags[3] = k_ptr->flags[3]; /* Show modifications to stats (can't use FLAG() here.) */ of_ptr->flags[0] |= (o_ptr->flags[0] & TR0_EASY_MASK); /* * *Identify* sets these flags, * and ego items have some set on creation. */ of_ptr->flags[0] |= o_ptr->kn_flags[0]; of_ptr->flags[1] |= o_ptr->kn_flags[1]; of_ptr->flags[2] |= o_ptr->kn_flags[2]; of_ptr->flags[3] |= o_ptr->kn_flags[3]; /* We now now whether or not it is an artifact */ COPY_FLAG(o_ptr, of_ptr, TR_INSTA_ART); } static char string_buf[200]; /* * Determine the "Activation" (if any) for an artifact * Return a string, or NULL for "no activation" */ cptr item_activation(const object_type *o_ptr) { cptr desc = NULL; /* Empty string */ string_buf[0] = '\0'; /* Require activation ability */ if (!(FLAG(o_ptr, TR_ACTIVATE))) return ("nothing"); /* Get description and copy to temporary buffer */ /* Lua better not try to modify the object ... */ apply_object_trigger(TRIGGER_DESC, (object_type *) o_ptr, ":s", LUA_RETURN(desc)); if (desc) { strncpy(string_buf, desc, 199); /* Free string allocated to hold return value */ string_free(desc); } /* Return the description */ return string_buf; } #define TR0_STAT_MASK \ (TR0_STR | TR0_INT | TR0_WIS | TR0_DEX | TR0_CON | TR0_CHR) #define TR1_SUST_MASK \ (TR1_SUST_STR | TR1_SUST_INT | TR1_SUST_WIS | \ TR1_SUST_DEX | TR1_SUST_CON | TR1_SUST_CHR) /* * Fully describe the known information about an item */ static void roff_obj_aux(const object_type *o_ptr) { object_kind *k_ptr; bonuses_type b; int i, n; object_flags oflags; object_flags *of_ptr = &oflags; int vn; cptr vp[80]; k_ptr = &k_info[o_ptr->k_idx]; /* Extract the flags */ object_flags_known(o_ptr, of_ptr); /* Extract the bonuses */ object_bonuses_known(o_ptr, &b); /* Make sure the examination starts top left */ Term_gotoxy(0, 0); /* Show the item in a message including its pack letter */ item_describe_roff((object_type *)o_ptr); /* Start the description a bit lower */ roff("\n\n"); /* If you don't know anything about the item */ if (!object_known_p(o_ptr) && !object_aware_p(o_ptr)) { /* say so */ roff("You see nothing special."); } /* Hack. Not all armour and weapons have a description in k_idx.txt */ if (o_ptr->tval >= TV_HAFTED && o_ptr->tval <= TV_DRAG_ARMOR && !k_ptr->text) { /* If the object has no id or has no interesting flags */ if (!object_known_p(o_ptr) || (o_ptr->flags[0] == 0 && o_ptr->flags[1] == TR2_SHOW_MODS && o_ptr->flags[2] == 0 && o_ptr->flags[3] == 0)) { /* say nothing is known */ roff("You see nothing special."); } } /* Indicate if fully known */ if (object_known_full(o_ptr)) { roff("You have full knowledge of this item. "); } /* Add the 'description' if any */ if (object_known_p(o_ptr) || object_aware_p(o_ptr)) { artifact_type *a_ptr = NULL; if (o_ptr->a_idx) a_ptr = &a_info[o_ptr->a_idx]; if (a_ptr && a_ptr->text) { roff("%s ", a_text + a_ptr->text); } else if (k_ptr->text) { roff("%s ", k_text + k_ptr->text); } } /* Mega-Hack -- describe activation if item is identified */ if ((FLAG(o_ptr, TR_ACTIVATE)) && object_known_p(o_ptr)) { roff("It can be activated for "); roff(CLR_UMBER "%s", item_activation(o_ptr)); roff(" if it is being worn. "); } /* Figurines, a hack */ if (o_ptr->tval == TV_FIGURINE) { roff("It will transform into a pet when thrown. "); } /* Hack -- describe lite's */ if (o_ptr->tval == TV_LITE) { if (FLAG(o_ptr, TR_INSTA_ART)) { roff("It provides light (radius 3). "); } else if (o_ptr->sval == SV_LITE_LANTERN) { roff("It provides light (radius 2) when fueled. "); } else { roff("It provides light (radius 1) when fueled. "); } } /* And then describe it fully */ for (i = 99; i >= -99; i--) { if (!i) continue; /* Collect stat boosts */ vn = 0; if (b.stat[A_STR] == i) vp[vn++] = "strength"; if (b.stat[A_INT] == i) vp[vn++] = "intelligence"; if (b.stat[A_WIS] == i) vp[vn++] = "wisdom"; if (b.stat[A_DEX] == i) vp[vn++] = "dexterity"; if (b.stat[A_CON] == i) vp[vn++] = "constitution"; if (b.stat[A_CHR] == i) vp[vn++] = "charisma"; /* All stats is handled specially */ if (vn == 6) { vn = 0; vp[vn++] = "all your stats"; } if (b.pspeed == i) vp[vn++] = "speed"; if (b.skills[SKILL_STL] == i) vp[vn++] = "stealth"; if (b.skills[SKILL_SNS] / 5 == i) vp[vn++] = "perception"; if (b.skills[SKILL_DIG] / 20 == i) vp[vn++] = "ability to dig"; if (b.skills[SKILL_SAV] == i) vp[vn++] = "saving throws"; /* Describe stat boosts */ if (vn > 0) { if (i > 0) roff("It increases "); else roff("It decreases "); /* Omit "your" for "all stats" */ if (strncmp(vp[0], "all ", 4) != 0) roff("your "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_L_GREEN "%s", vp[n]); } roff(" by %+i. ", i); } } if (b.sp_bonus) { if (b.sp_bonus > 0) { roff("It increases your "); } else { roff("It decreases your "); } roff(CLR_L_GREEN "maximum sp" CLR_DEFAULT " by %i per level. ", b.sp_bonus); } if (b.see_infra) { if (b.see_infra > 0) { roff("It increases your "); roff(CLR_L_GREEN "infravision"); roff(" by %i feet. ", b.see_infra * 10); } else { roff("It decreases your "); roff(CLR_L_GREEN "infravision"); roff(" by %i feet. ", -b.see_infra * 10); } } /* Food that can be thrown is worth noting */ if (k_ptr->ds > 1 && k_ptr->dd > 1 && k_ptr->tval == TV_FOOD) { roff("It can be thrown for %id%i damage. ", k_ptr->dd, k_ptr->ds); } if (b.extra_blows) { if (b.extra_blows > 0) { roff("It provides %i extra ", b.extra_blows); } else { roff("It provides %i fewer ", -b.extra_blows); } roff(CLR_L_GREEN "blows per turn" CLR_DEFAULT ". "); } if (b.extra_shots) { if (b.extra_shots > 0) { roff("It provides %i extra ", b.extra_shots); } else { roff("It provides %i fewer ", -b.extra_shots); } roff(CLR_L_GREEN "shots per turn" CLR_DEFAULT ". "); } /* Collect brands */ vn = 0; if (FLAG(of_ptr, TR_BRAND_ACID)) vp[vn++] = "acid"; if (FLAG(of_ptr, TR_BRAND_ELEC)) vp[vn++] = "electricity"; if (FLAG(of_ptr, TR_BRAND_FIRE)) vp[vn++] = "fire"; if (FLAG(of_ptr, TR_BRAND_COLD)) vp[vn++] = "frost"; /* Describe brands */ if (vn) { roff("It does extra damage from "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_VIOLET "%s", vp[n]); } roff(". "); } if (FLAG(of_ptr, TR_BRAND_POIS)) { roff("It " CLR_VIOLET "poisons" CLR_DEFAULT " your foes. "); } if (FLAG(of_ptr, TR_CHAOTIC)) { roff("It produces " CLR_VIOLET "chaotic effects" CLR_DEFAULT ". "); } if (FLAG(of_ptr, TR_VAMPIRIC)) { roff("It " CLR_VIOLET "drains life" CLR_DEFAULT " from your foes. "); } if (FLAG(of_ptr, TR_IMPACT)) { roff("It can cause " CLR_VIOLET "earthquakes" CLR_DEFAULT ". "); } if (FLAG(of_ptr, TR_VORPAL)) { roff("It is very sharp and can cut your foes. "); } if (o_ptr->tval >= TV_DIGGING && o_ptr->tval <= TV_SWORD) { if (FLAG(of_ptr, TR_KILL_DRAGON)) { roff("It is a great bane of " CLR_YELLOW "dragons" CLR_DEFAULT ". "); } /* Collect slays */ vn = 0; if (FLAG(of_ptr, TR_SLAY_DRAGON) && !FLAG(of_ptr, TR_KILL_DRAGON)) vp[vn++] = "dragons"; if (FLAG(of_ptr, TR_SLAY_ORC)) vp[vn++] = "orcs"; if (FLAG(of_ptr, TR_SLAY_TROLL)) vp[vn++] = "trolls"; if (FLAG(of_ptr, TR_SLAY_GIANT)) vp[vn++] = "giants"; if (FLAG(of_ptr, TR_SLAY_DEMON)) vp[vn++] = "demons"; if (FLAG(of_ptr, TR_SLAY_UNDEAD)) vp[vn++] = "the undead"; if (FLAG(of_ptr, TR_SLAY_EVIL)) vp[vn++] = "evil monsters"; if (FLAG(of_ptr, TR_SLAY_ANIMAL)) vp[vn++] = "natural creatures"; /* Print slays */ if (vn) { roff("It is especially deadly against "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_YELLOW "%s", vp[n]); } roff(". "); } } if (FLAG(of_ptr, TR_GHOUL_TOUCH)) { roff("It gives you a paralyzing touch. "); } if (FLAG(of_ptr, TR_PSI_CRIT)) { roff("It uses psychic energy to strike great blows. "); } if (FLAG(of_ptr, TR_RETURN)) { roff("It returns when thrown. "); } if (FLAG(of_ptr, TR_EXPLODE)) { roff("It explodes when fired. "); } /* Collect sustains */ if ((of_ptr->flags[1] & TR1_SUST_MASK) == TR1_SUST_MASK) { /* Handle all stats specially */ roff("It sustains " CLR_GREEN "all your stats" CLR_DEFAULT ". "); } else { vn = 0; if (FLAG(of_ptr, TR_SUST_STR)) vp[vn++] = "strength"; if (FLAG(of_ptr, TR_SUST_INT)) vp[vn++] = "intelligence"; if (FLAG(of_ptr, TR_SUST_WIS)) vp[vn++] = "wisdom"; if (FLAG(of_ptr, TR_SUST_DEX)) vp[vn++] = "dexterity"; if (FLAG(of_ptr, TR_SUST_CON)) vp[vn++] = "constitution"; if (FLAG(of_ptr, TR_SUST_CHR)) vp[vn++] = "charisma"; /* Print sustains */ if (vn) { roff("It sustains your "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_GREEN "%s", vp[n]); } roff(". "); } } /* Collect immunities */ vn = 0; if (FLAG(of_ptr, TR_IM_ACID)) vp[vn++] = "acid"; if (FLAG(of_ptr, TR_IM_ELEC)) vp[vn++] = "electricity"; if (FLAG(of_ptr, TR_IM_FIRE)) vp[vn++] = "fire"; if (FLAG(of_ptr, TR_IM_COLD)) vp[vn++] = "cold"; if (FLAG(of_ptr, TR_IM_POIS)) vp[vn++] = "poison"; if (FLAG(of_ptr, TR_IM_LITE)) vp[vn++] = "light"; if (FLAG(of_ptr, TR_IM_DARK)) vp[vn++] = "darkness"; if (FLAG(of_ptr, TR_FREE_ACT)) vp[vn++] = "paralysis"; /* Print immunities */ if (vn) { roff("It provides immunity to "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_BLUE "%s", vp[n]); } roff(". "); } /* Collect resistances */ vn = 0; if (FLAG(of_ptr, TR_RES_ACID) && !FLAG(of_ptr, TR_IM_ACID)) vp[vn++] = "acid"; if (FLAG(of_ptr, TR_RES_ELEC) && !FLAG(of_ptr, TR_IM_ELEC)) vp[vn++] = "electricity"; if (FLAG(of_ptr, TR_RES_FIRE) && !FLAG(of_ptr, TR_IM_FIRE)) vp[vn++] = "fire"; if (FLAG(of_ptr, TR_RES_COLD) && !FLAG(of_ptr, TR_IM_COLD)) vp[vn++] = "cold"; if (FLAG(of_ptr, TR_RES_POIS) && !FLAG(of_ptr, TR_IM_POIS)) vp[vn++] = "poison"; if (FLAG(of_ptr, TR_RES_LITE) && !FLAG(of_ptr, TR_IM_LITE)) vp[vn++] = "bright light"; if (FLAG(of_ptr, TR_RES_DARK) && !FLAG(of_ptr, TR_IM_DARK)) vp[vn++] = "magical darkness"; if (FLAG(of_ptr, TR_RES_FEAR)) vp[vn++] = "fear"; if (FLAG(of_ptr, TR_RES_BLIND)) vp[vn++] = "blindness"; if (FLAG(of_ptr, TR_RES_CONF)) vp[vn++] = "confusion"; if (FLAG(of_ptr, TR_RES_SOUND)) vp[vn++] = "sound"; if (FLAG(of_ptr, TR_RES_SHARDS)) vp[vn++] = "shards"; if (FLAG(of_ptr, TR_RES_NETHER)) vp[vn++] = "nether"; if (FLAG(of_ptr, TR_RES_NEXUS)) vp[vn++] = "nexus"; if (FLAG(of_ptr, TR_RES_CHAOS)) vp[vn++] = "chaos"; if (FLAG(of_ptr, TR_RES_DISEN)) vp[vn++] = "disenchantment"; if (FLAG(of_ptr, TR_HOLD_LIFE)) vp[vn++] = "life draining"; /* Print resistances */ if (vn) { roff("It provides resistance to "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_L_BLUE "%s", vp[n]); } roff(". "); } if (FLAG(of_ptr, TR_THROW)) { roff("It is perfectly balanced for throwing. "); } if (FLAG(of_ptr, TR_WILD_SHOT)) { roff("Its shots are not hindered by trees. "); } if (FLAG(of_ptr, TR_EASY_ENCHANT)) { roff("It is easy to enchant. "); } /* Collect miscellaneous */ vn = 0; if (FLAG(of_ptr, TR_XXX7)) vp[vn++] = "renders you XXX7'ed"; if (FLAG(of_ptr, TR_FEATHER)) vp[vn++] = "allows you to levitate"; if (FLAG(of_ptr, TR_LITE)) vp[vn++] = "provides permanent light"; if (FLAG(of_ptr, TR_SEE_INVIS)) vp[vn++] = "allows you to see invisible monsters"; if (FLAG(of_ptr, TR_TELEPATHY)) vp[vn++] = "gives telepathic powers"; if (FLAG(of_ptr, TR_SLOW_DIGEST)) vp[vn++] = "slows your metabolism"; if (FLAG(of_ptr, TR_REGEN)) vp[vn++] = "speeds your regenerative powers"; if (FLAG(of_ptr, TR_REFLECT)) vp[vn++] = "reflects bolts and arrows"; if (FLAG(of_ptr, TR_WILD_WALK)) vp[vn++] = "allows you to walk the wild unhindered"; if (FLAG(of_ptr, TR_MUTATE)) vp[vn++] = "causes mutations"; if (FLAG(of_ptr, TR_PATRON)) vp[vn++] = "attracts the attention of chaos gods"; if (FLAG(of_ptr, TR_STRANGE_LUCK)) vp[vn++] = "warps fate around you"; if (FLAG(of_ptr, TR_PASS_WALL)) vp[vn++] = "allows you to pass through solid rock"; if (FLAG(of_ptr, TR_NO_TELE)) vp[vn++] = "prevents teleportation"; /* Print miscellaneous */ if (vn) { roff("It "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff("%s", vp[n]); } roff(". "); } /* Collect "produces" */ vn = 0; if (FLAG(of_ptr, TR_SH_FIRE)) vp[vn++] = "a fiery sheath"; if (FLAG(of_ptr, TR_SH_ELEC)) vp[vn++] = "an electric sheath"; if (FLAG(of_ptr, TR_SH_ACID)) vp[vn++] = "an acidic sheath"; if (FLAG(of_ptr, TR_SH_COLD)) vp[vn++] = "a freezing sheath"; if (FLAG(of_ptr, TR_NO_MAGIC)) vp[vn++] = "an anti-magic shell"; /* Print "produces" */ if (vn) { roff("It produces "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_VIOLET "%s", vp[n]); } roff(". "); } if (FLAG(of_ptr, TR_XTRA_MIGHT)) { roff("It fires missiles with " CLR_GREEN "extra might" CLR_DEFAULT ". "); } /* Collect curses */ vn = 0; if (FLAG(of_ptr, TR_DRAIN_EXP)) vp[vn++] = "drains your experience"; if (FLAG(of_ptr, TR_DRAIN_STATS)) vp[vn++] = "drains your stats"; if (FLAG(of_ptr, TR_TELEPORT)) vp[vn++] = "induces random teleportation"; if (FLAG(of_ptr, TR_AGGRAVATE)) vp[vn++] = "aggravates nearby creatures"; if (FLAG(of_ptr, TR_AUTO_CURSE)) vp[vn++] = "becomes cursed randomly"; if (FLAG(of_ptr, TR_CANT_EAT)) vp[vn++] = "makes you unable to eat normal food"; if (FLAG(of_ptr, TR_SLOW_HEAL)) vp[vn++] = "slows your healing"; /* Print curses */ if (vn) { roff("It "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_RED "%s", vp[n]); } roff(". "); } if (FLAG(of_ptr, TR_BLESSED)) { roff("It has been blessed by the gods. "); } /* Collect protections */ vn = 0; if (FLAG(of_ptr, TR_SLAY_ANIMAL)) vp[vn++] = "natural creatures"; if (FLAG(of_ptr, TR_SLAY_EVIL)) vp[vn++] = "evil monsters"; if (FLAG(of_ptr, TR_SLAY_UNDEAD)) vp[vn++] = "the undead"; if (FLAG(of_ptr, TR_SLAY_DEMON)) vp[vn++] = "demons"; if (FLAG(of_ptr, TR_SLAY_ORC)) vp[vn++] = "orcs"; if (FLAG(of_ptr, TR_SLAY_TROLL)) vp[vn++] = "trolls"; if (FLAG(of_ptr, TR_SLAY_GIANT)) vp[vn++] = "giants"; if (FLAG(of_ptr, TR_SLAY_DRAGON)) vp[vn++] = "dragons"; /* Print protections */ if (vn) { roff("It provides protection from "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_BLUE "%s", vp[n]); } roff(". "); } /* Collect vulnerabilities */ vn = 0; if (FLAG(of_ptr, TR_HURT_ACID) && !FLAG(of_ptr, TR_IM_ACID)) vp[vn++] = "acid"; if (FLAG(of_ptr, TR_HURT_ELEC) && !FLAG(of_ptr, TR_IM_ELEC)) vp[vn++] = "lightning"; if (FLAG(of_ptr, TR_HURT_FIRE) && !FLAG(of_ptr, TR_IM_FIRE)) vp[vn++] = "fire"; if (FLAG(of_ptr, TR_HURT_COLD) && !FLAG(of_ptr, TR_IM_COLD)) vp[vn++] = "frost"; if (FLAG(of_ptr, TR_HURT_LITE) && !FLAG(of_ptr, TR_IM_LITE)) vp[vn++] = "bright light"; if (FLAG(of_ptr, TR_HURT_DARK) && !FLAG(of_ptr, TR_IM_DARK)) vp[vn++] = "magical darkness"; /* Print vulnerabilities */ if (vn) { roff("It renders you vulnerable to "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" and "); else if (n > 0) roff(", "); roff(CLR_RED "%s", vp[n]); } roff(". "); } if (cursed_p(o_ptr)) { if (FLAG(of_ptr, TR_PERMA_CURSE)) { roff(CLR_L_RED "It is permanently cursed. "); } else if (FLAG(of_ptr, TR_HEAVY_CURSE)) { roff(CLR_L_RED "It is heavily cursed. "); } else if (FLAG(of_ptr, TR_CURSED)) { roff(CLR_RED "It is cursed. "); } } if (FLAG(of_ptr, TR_TY_CURSE)) { roff(CLR_L_RED "It carries an ancient foul curse. "); } if ((of_ptr->flags[2] & TR2_IGNORE_MASK) == TR2_IGNORE_MASK) { roff("It cannot be harmed by the elements. "); } else { /* Collect ignores */ vn = 0; if (FLAG(of_ptr, TR_IGNORE_ACID)) vp[vn++] = "acid"; if (FLAG(of_ptr, TR_IGNORE_ELEC)) vp[vn++] = "electricity"; if (FLAG(of_ptr, TR_IGNORE_FIRE)) vp[vn++] = "fire"; if (FLAG(of_ptr, TR_IGNORE_COLD)) vp[vn++] = "cold"; /* Print ignores */ if (vn) { roff("It cannot be harmed by "); /* Scan */ for (n = 0; n < vn; n++) { if (n > 0 && n == vn - 1) roff(" or "); else if (n > 0) roff(", "); roff("%s", vp[n]); } roff(". "); } } /* If it is a weapon */ if (o_ptr->tval >= TV_BOW && o_ptr->tval <= TV_SWORD) { /* Obtain the "hold" value */ int hold = adj_str_hold[p_ptr->stat[A_STR].ind]; /* If you are not strong enough for this weapon */ if (hold < o_ptr->weight / 10) { roff("You are not strong enough to wield this weapon effectively. "); } } /* Final blank line */ roff("\n"); } static const object_type *resize_o_ptr; static void resize_ident_fully(void) { /* Recall object */ roff_obj_aux(resize_o_ptr); } void identify_fully_aux(const object_type *o_ptr) { void (*old_hook) (void); /* Books, a hack */ if ((o_ptr->tval >= TV_BOOKS_MIN) && (o_ptr->tval <= TV_BOOKS_MAX)) { do_cmd_browse_aux(o_ptr); return; } /* Save the screen */ screen_save(); /* Recall object */ roff_obj_aux(o_ptr); /* Remember what the resize hook was */ old_hook = angband_term[0]->resize_hook; /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = resize_ident_fully; /* Remember essentials for resizing */ resize_o_ptr = o_ptr; /* Wait for the player to read the info */ (void)inkey(); /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = old_hook; /* The size may have changed during the object description */ angband_term[0]->resize_hook(); /* Hack - Flush it */ Term_fresh(); /* Restore the screen */ screen_load(); } /* * Convert a label into the object pointer * to an item in a list. * Return NULL if the label does not indicate a real item. */ static object_type *label_to_list(int c, s16b list_start) { int i; /* Convert */ i = (islower(c) ? A2I(c) : -1); /* Return the item */ return (get_list_item(list_start, i)); } /* * Convert a label into the object pointer * to an item in the equipment. * Return NULL if the label does not indicate a real item. */ static object_type *label_to_equip(int c) { object_type *o_ptr; int i; /* Convert */ i = (islower(c) ? A2I(c) : -1); /* Verify the index */ if ((i < 0) || (i >= EQUIP_MAX)) return (NULL); /* Get the item */ o_ptr = &p_ptr->equipment[i]; /* Empty slots can never be chosen */ if (!o_ptr->k_idx) return (NULL); /* Return the item */ return (o_ptr); } /* * The the "choosable" range of objects from the list. */ static void get_label_bounds(s16b list, int *n1, int *n2) { object_type *o_ptr; int i = -1; *n1 = -1; *n2 = -1; OBJ_ITT_START (list, o_ptr) { i++; if (item_tester_okay(o_ptr)) { /* Get lower bounds */ if (*n1 == -1) *n1 = i; /* Get higher bounds */ *n2 = i; } } OBJ_ITT_END; } /* * Determine which equipment slot (if any) an item likes */ s16b wield_slot(const object_type *o_ptr) { /* Slot for equipment */ switch (o_ptr->tval) { case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: { return (EQUIP_WIELD); } case TV_BOW: { return (EQUIP_BOW); } case TV_RING: { /* Use the right hand first */ if (!p_ptr->equipment[EQUIP_RIGHT].k_idx) return (EQUIP_RIGHT); /* Use the left hand for swapping (by default) */ return (EQUIP_LEFT); } case TV_AMULET: { return (EQUIP_NECK); } case TV_LITE: { return (EQUIP_LITE); } case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: { return (EQUIP_BODY); } case TV_CLOAK: { return (EQUIP_OUTER); } case TV_SHIELD: { return (EQUIP_ARM); } case TV_CROWN: case TV_HELM: { return (EQUIP_HEAD); } case TV_GLOVES: { return (EQUIP_HANDS); } case TV_BOOTS: { return (EQUIP_FEET); } } /* No slot available */ return (-1); } /* * Return a string mentioning how a given item is carried */ cptr mention_use(int i) { cptr p; /* Examine the location */ switch (i) { case EQUIP_WIELD: { p = "Wielding"; break; } case EQUIP_BOW: { p = "Shooting"; break; } case EQUIP_LEFT: { p = "On left hand"; break; } case EQUIP_RIGHT: { p = "On right hand"; break; } case EQUIP_NECK: { p = "Around neck"; break; } case EQUIP_LITE: { p = "Light source"; break; } case EQUIP_BODY: { p = "On body"; break; } case EQUIP_OUTER: { p = "About body"; break; } case EQUIP_ARM: { p = "On arm"; break; } case EQUIP_HEAD: { p = "On head"; break; } case EQUIP_HANDS: { p = "On hands"; break; } case EQUIP_FEET: { p = "On feet"; break; } default: { p = "In pack"; break; } } /* Hack -- Heavy weapon */ if (i == EQUIP_WIELD) { object_type *o_ptr; o_ptr = &p_ptr->equipment[i]; if (adj_str_hold[p_ptr->stat[A_STR].ind] < o_ptr->weight / 10) { p = "Just lifting"; } } /* Hack -- Heavy bow */ if (i == EQUIP_BOW) { object_type *o_ptr; o_ptr = &p_ptr->equipment[i]; if (adj_str_hold[p_ptr->stat[A_STR].ind] < o_ptr->weight / 10) { p = "Just holding"; } } /* Return the result */ return (p); } /* * Return a string describing how a given item is being worn. * Currently, only used for items in the equipment, not inventory. */ cptr describe_use(int i) { cptr p; switch (i) { case EQUIP_WIELD: { p = "attacking monsters with"; break; } case EQUIP_BOW: { p = "shooting missiles with"; break; } case EQUIP_LEFT: { p = "wearing on your left hand"; break; } case EQUIP_RIGHT: { p = "wearing on your right hand"; break; } case EQUIP_NECK: { p = "wearing around your neck"; break; } case EQUIP_LITE: { p = "using to light the way"; break; } case EQUIP_BODY: { p = "wearing on your body"; break; } case EQUIP_OUTER: { p = "wearing on your back"; break; } case EQUIP_ARM: { p = "wearing on your arm"; break; } case EQUIP_HEAD: { p = "wearing on your head"; break; } case EQUIP_HANDS: { p = "wearing on your hands"; break; } case EQUIP_FEET: { p = "wearing on your feet"; break; } default: { p = "invalid item to describe_use()!"; break; } } /* Hack -- Heavy weapon */ if (i == EQUIP_WIELD) { object_type *o_ptr = &p_ptr->equipment[i]; if (adj_str_hold[p_ptr->stat[A_STR].ind] < o_ptr->weight / 10) { p = "just lifting"; } } /* Hack -- Heavy bow */ if (i == EQUIP_BOW) { object_type *o_ptr = &p_ptr->equipment[i]; if (adj_str_hold[p_ptr->stat[A_STR].ind] < o_ptr->weight / 10) { p = "just holding"; } } /* Return the result */ return (p); } /* * Hook to specify "tval" */ bool item_tester_hook_tval(const object_type *o_ptr, byte tval) { return (o_ptr->tval == tval); } /* * Hook to specify "weapon" */ bool item_tester_hook_weapon(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SWORD: case TV_HAFTED: case TV_POLEARM: case TV_DIGGING: case TV_BOW: case TV_BOLT: case TV_ARROW: case TV_SHOT: { return (TRUE); } } return (FALSE); } /* * Hook to specify "melee weapon" */ bool item_tester_hook_melee_weapon(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SWORD: case TV_HAFTED: case TV_POLEARM: case TV_DIGGING: { return (TRUE); } } return (FALSE); } /* * Hook to specify "non-sword melee" */ bool item_tester_hook_nonsword(const object_type *o_ptr) { if ((o_ptr->tval == TV_HAFTED) || (o_ptr->tval == TV_POLEARM)) { return (TRUE); } return (FALSE); } /* * Hook to specify "ammo" */ bool item_tester_hook_ammo(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: { return (TRUE); } } return (FALSE); } /* * Hook to specify "Bows+arrows" */ bool item_tester_hook_fletcher(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: case TV_BOW: { return (TRUE); } } return (FALSE); } /* * Hook to specify "armour" */ bool item_tester_hook_armour(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: case TV_SHIELD: case TV_CLOAK: case TV_CROWN: case TV_HELM: case TV_BOOTS: case TV_GLOVES: { return (TRUE); } } return (FALSE); } /* * Hook to specify "armour" without acid resistance */ bool item_tester_hook_armour_no_acid(const object_type *o_ptr) { /* If this item is an armour and is not acid proof */ if (item_tester_hook_armour(o_ptr) && !FLAG(o_ptr, TR_IGNORE_ACID)) return (TRUE); return (FALSE); } /* * Hook to specify "soft armour" */ bool item_tester_hook_soft_armour(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SOFT_ARMOR: case TV_CLOAK: case TV_BOOTS: case TV_GLOVES: { return (TRUE); } } return (FALSE); } /* * Hook to specify "hard armour" */ bool item_tester_hook_hard_armour(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SHIELD: case TV_CROWN: case TV_HELM: { return (TRUE); } } return (FALSE); } /* * Hook to specify "helm or crown" */ bool item_tester_hook_helm(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_CROWN: case TV_HELM: { return (TRUE); } } return (FALSE); } /* * Hook to specify "pure hard armour" */ bool item_tester_hook_pure_hard_armour(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_DRAG_ARMOR: case TV_HARD_ARMOR: { return (TRUE); } } return (FALSE); } /* * Check if an object is weapon or armour (but not arrow, bolt, or shot) */ bool item_tester_hook_weapon_armour(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SWORD: case TV_HAFTED: case TV_POLEARM: case TV_BOW: case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: case TV_SHIELD: case TV_CLOAK: case TV_CROWN: case TV_HELM: case TV_BOOTS: case TV_GLOVES: { return (TRUE); } } return (FALSE); } /* * The "wearable" tester */ bool item_tester_hook_wear(const object_type *o_ptr) { /* Check for a usable slot */ if (wield_slot(o_ptr) >= EQUIP_WIELD) return (TRUE); /* Assume not wearable */ return (FALSE); } /* * Determine if something is rechargable. */ bool item_tester_hook_recharge(const object_type *o_ptr) { /* Staffs */ if (o_ptr->tval == TV_STAFF) return (TRUE); /* Wands */ if (o_ptr->tval == TV_WAND) return (TRUE); /* Rods */ if (o_ptr->tval == TV_ROD) return (TRUE); /* Nope */ return (FALSE); } /* * Determine if something is a 'jewel' */ bool item_tester_hook_jewel(const object_type *o_ptr) { /* Rings */ if (o_ptr->tval == TV_RING) return (TRUE); /* Amulets */ if (o_ptr->tval == TV_AMULET) return (TRUE); /* Nope */ return (FALSE); } bool item_tester_hook_is_blessed(const object_type *o_ptr) { object_flags oflags; object_flags_known(o_ptr, &oflags); /* Is it blessed? */ if (FLAG(&oflags, TR_BLESSED)) return (TRUE); /* Check for unallowable weapons */ if ((o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM)) return (FALSE); /* Everthing else is ok */ return (TRUE); } bool item_tester_hook_is_good(const object_type *o_ptr) { if (!object_known_p(o_ptr)) return (FALSE); if (cursed_p(o_ptr)) return (FALSE); /* Ego item or artifact */ if (o_ptr->xtra_name) return (TRUE); /* Positve AC bonus */ if (o_ptr->to_a > 0) return (TRUE); /* Good attack + defence */ if (o_ptr->to_h + o_ptr->to_d > 0) return (TRUE); /* Everthing else isn't good */ return (FALSE); } bool item_tester_hook_is_great(const object_type *o_ptr) { if (!object_known_p(o_ptr)) return (FALSE); if (cursed_p(o_ptr)) return (FALSE); /* Ego item or artifact */ if (o_ptr->xtra_name) return (TRUE); /* Everthing else isn't great */ return (FALSE); } bool item_tester_hook_is_book(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_SORCERY_BOOK: case TV_NATURE_BOOK: case TV_CHAOS_BOOK: case TV_DEATH_BOOK: case TV_TRUMP_BOOK: case TV_ARCANE_BOOK: case TV_LIFE_BOOK: /* It is a book */ return (TRUE); } /* It isn't a book */ return (FALSE); } /* Hack: Check if a spellbook is one of the realms we can use. -- TY */ static bool check_book_realm(const byte book_tval) { return (REALM1_BOOK == book_tval || REALM2_BOOK == book_tval); } /* * Check an item against the item tester info */ bool item_tester_okay(const object_type *o_ptr) { /* Paranoia */ if (!o_ptr) return (FALSE); /* Hack -- allow listing empty slots */ if (item_tester_full) return (TRUE); /* Require an item */ if (!o_ptr->k_idx) return (FALSE); /* Hack -- ignore "gold" */ if (o_ptr->tval == TV_GOLD) return (FALSE); /* Check the tval */ if (item_tester_tval) { /* Is it a spellbook? If so, we need a hack -- TY */ if ((item_tester_tval <= TV_BOOKS_MAX) && (item_tester_tval >= TV_BOOKS_MIN)) return check_book_realm(o_ptr->tval); else if (item_tester_tval != o_ptr->tval) return (FALSE); } /* Check the hook */ if (item_tester_hook) { if (!(*item_tester_hook) (o_ptr)) return (FALSE); } /* Assume okay */ return (TRUE); } static bool item_is_recharging(object_type *o_ptr) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; int power; /* Is the item recharging? */ if (o_ptr->timeout && ((o_ptr->tval != TV_LITE) || (FLAG(o_ptr, TR_INSTA_ART)))) { if (o_ptr->tval == TV_ROD) { /* Paranoia. */ if (!k_ptr->pval) return (FALSE); /* * Find out how many rods are charging, by dividing * current timeout by each rod's maximum timeout. */ power = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval; /* Only darken fully-charging stacks. */ if (power >= o_ptr->number) return (TRUE); } else { return (TRUE); } } return (FALSE); } /* * Choice window "shadow" of the "show_inven()" function */ void display_inven(void) { int i = 0; object_type *o_ptr; cptr attr; char tmp_val[80]; int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Display the pack */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Start with an empty "index" */ tmp_val[0] = tmp_val[1] = ' '; tmp_val[2] = 0; /* Is this item "acceptable"? */ if (item_tester_okay(o_ptr)) { /* Prepare an "index" */ tmp_val[0] = I2A(i); /* Bracket the "index" --(-- */ tmp_val[1] = ')'; } /* Display the index (or blank space) */ prtf(0, i, tmp_val); /* Get a color */ attr = color_seq[tval_to_attr[o_ptr->tval % 128]]; /* Grey out charging items */ if (item_is_recharging(o_ptr)) attr = CLR_L_DARK; /* Display the entry itself */ put_fstr(3, i, "%s" CLR_SET_DEFAULT "%v", attr, OBJECT_FMT(o_ptr, TRUE, 3)); /* Display the weight if needed */ if (show_weights && o_ptr->weight) { int wgt = o_ptr->weight * o_ptr->number; prtf(wid - 9, i, "%3d.%1d lb", wgt / 10, wgt % 10); } /* Count items in inventory */ i++; } OBJ_ITT_END; /* Erase the rest of the window */ clear_from(i); } /* * Choice window "shadow" of the "show_equip()" function */ void display_equip(void) { int i; object_type *o_ptr; cptr attr; char tmp_val[80]; int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Display the equipment */ for (i = 0; i < EQUIP_MAX; i++) { /* Examine the item */ o_ptr = &p_ptr->equipment[i]; /* Start with an empty "index" */ tmp_val[0] = tmp_val[1] = ' '; tmp_val[2] = 0; /* Is this item "acceptable"? */ if (item_tester_okay(o_ptr)) { /* Prepare an "index" */ tmp_val[0] = I2A(i); /* Bracket the "index" --(-- */ tmp_val[1] = ')'; } /* Display the index (or blank space) */ prtf(0, i, tmp_val); /* Get the color */ attr = color_seq[tval_to_attr[o_ptr->tval % 128]]; /* Grey out charging items */ if (item_is_recharging(o_ptr)) attr = CLR_L_DARK; /* Display the entry itself */ put_fstr(3, i, "%s" CLR_SET_DEFAULT "%v", attr, OBJECT_FMT(o_ptr, TRUE, 3)); /* Display the slot description (if needed) */ if (show_labels) { prtf(wid - 19, i, "<--"); put_fstr(wid - 15, i, mention_use(i)); } /* Display the weight (if needed) */ if (show_weights && o_ptr->weight) { int wgt = o_ptr->weight * o_ptr->number; int col = (show_labels ? wid - 28 : wid - 9); put_fstr(col, i, "%3d.%1d lb", wgt / 10, wgt % 10); } } /* Erase the rest of the window */ clear_from(EQUIP_MAX); } /* * Display a list of objects. * This can be used to show the player's inventory, * or to show a list of floor items. * * Hack -- do not display "trailing" empty slots */ void show_list(s16b o_list_ptr, bool store) { int i, j; int k, l; int col, len, lim; object_type *o_ptr; object_type *out_object[23]; int out_index[23]; cptr out_color[23]; char out_desc[23][256]; byte a; char c; int wid, hgt; int extra = 0; /* Get size */ Term_get_size(&wid, &hgt); /* Default "max-length" */ len = wid - 51; /* Maximum space allowed for descriptions */ lim = wid - 6; /* Initialise counters */ i = -1; j = 0; k = -1; /* How much extra room do we need? */ if (show_weights) extra += 9; if (store) extra += 11; /* Notice the extra space required */ lim -= extra; /* Display the inventory */ OBJ_ITT_START (o_list_ptr, o_ptr) { /* Paranoia - don't display too many items */ if (k >= INVEN_PACK - 1) break; i++; /* Is this item acceptable? */ if (!item_tester_okay(o_ptr)) continue; /* Advance to next "line" */ k++; /* Save the object index, color, and description */ out_index[k] = i; out_object[k] = o_ptr; out_color[k] = color_seq[tval_to_attr[o_ptr->tval % 128]]; /* Grey out charging items */ if (item_is_recharging(o_ptr)) out_color[k] = CLR_L_DARK; /* Save the name for later */ l = strnfmt(out_desc[k], lim, "%v", OBJECT_FMT(o_ptr, TRUE, 3)); /* Find the predicted "line length" */ l += 5; /* Be sure to account for the weight */ if (show_weights) l += 9; /* Account for the price */ if (store) l += 10; /* Account for icon if displayed */ l += 2; /* Maintain the maximum length */ if (l > len) len = l; } OBJ_ITT_END; /* Hack -- Find a column to start in and to put weights */ if (len > wid - 4) { col = 0; lim = wid - extra; } else { col = (wid - len - 1) / 2; lim = col + len - extra; } /* Output each entry */ for (j = 0; j <= k; j++) { /* Get the item */ o_ptr = out_object[j]; /* Clear the line */ prtf(col ? col - 2 : col, j + 1, ""); /* Clear the line with the (possibly indented) index */ put_fstr(col, j + 1, "%c)", I2A(out_index[j])); /* Display graphics for object, if desired */ a = object_attr(o_ptr); c = object_char(o_ptr); /* Fake monochrome */ if (!use_color) { /* Hack - no equippy char */ a = TERM_WHITE; c = ' '; } Term_draw(col + 3, j + 1, a, c); /* Display the entry itself */ put_fstr(col + 5, j + 1, "%s" CLR_SET_DEFAULT "%s", out_color[j], out_desc[j]); /* Display the weight if needed */ if (show_weights) { int wgt = o_ptr->weight * o_ptr->number; put_fstr(lim, j + 1, "%3d.%1d lb", wgt / 10, wgt % 10); if (store) { /* Extract the price */ u32b price = price_item(o_ptr, TRUE); /* Actually draw the price */ put_fstr(lim + 9, j + 1, "%9ld ", (long)price); } } else if (store) { /* Extract the price */ u32b price = price_item(o_ptr, TRUE); /* Actually draw the price */ put_fstr(lim, j + 1, "%9ld ", (long)price); } } /* Make a "shadow" below the list (only if needed) */ if (j && (j < hgt - 2)) prtf(col ? col - 2 : col, j + 1, ""); } /* * Display the equipment. */ void show_equip(bool store) { int i, j, k, l; int col, len, lim; object_type *o_ptr; char o_name[256]; int out_index[EQUIP_MAX]; cptr out_color[EQUIP_MAX]; char out_desc[EQUIP_MAX][256]; byte a; char c; int wid, hgt; int extra = 0; /* Get size */ Term_get_size(&wid, &hgt); /* Maximal length */ len = wid - 51; /* Maximum space allowed for descriptions */ lim = wid - 6; /* Require space for labels (if needed) */ if (show_labels) lim -= (14 + 2); /* How much extra room do we need? */ if (show_weights) extra += 9; if (store) extra += 11; /* Notice the extra space required */ lim -= extra; /* Scan the equipment list */ for (k = 0, i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Is this item acceptable? */ if (!item_tester_okay(o_ptr)) continue; /* Description */ object_desc(o_name, o_ptr, TRUE, 3, 256); /* Truncate the description */ o_name[lim] = '\0'; /* Save the color */ out_index[k] = i; out_color[k] = color_seq[tval_to_attr[o_ptr->tval % 128]]; /* Grey out charging items */ if (item_is_recharging(o_ptr)) out_color[k] = CLR_L_DARK; (void)strcpy(out_desc[k], o_name); /* Extract the maximal length (see below) */ l = strlen(out_desc[k]) + (2 + 3); /* Increase length for labels (if needed) */ if (show_labels) l += (14 + 2); /* Increase length for weight (if needed) */ if (show_weights) l += 9; /* Increase length for price (if needed) */ if (store) l += 11; /* old show_equip_graph option perm. on. */ l += 2; /* Maintain the max-length */ if (l > len) len = l; /* Advance the entry */ k++; } /* Hack -- Find a column to start in and to put weights */ if (len > wid - 4) { col = 0; lim = wid - extra; } else { col = (wid - len - 1) / 2; lim = col + len - extra; } /* Output each entry */ for (j = 0; j < k; j++) { /* Get the index */ i = out_index[j]; /* Get the item */ o_ptr = &p_ptr->equipment[i]; /* Clear the line */ prtf(col ? col - 2 : col, j + 1, ""); /* Clear the line with the (possibly indented) index */ put_fstr(col, j + 1,"%c)", I2A(i)); /* Show_equip_graph perm. on. */ a = object_attr(o_ptr); c = object_char(o_ptr); /* Hack - if no object, don't display the 'nothing' symbol */ if (!o_ptr->number) c = ' '; /* Fake monochrome */ if (!use_color) { /* Hack - no equippy char */ a = TERM_WHITE; c = ' '; } Term_draw(col + 3, j + 1, a, c); /* Use labels */ if (show_labels) { /* Mention the use */ put_fstr(col + 5, j + 1, "%-14s: ", mention_use(i)); /* Display the entry itself */ put_fstr(col + 21, j + 1, "%s" CLR_SET_DEFAULT "%s", out_color[j], out_desc[j]); } /* No labels */ else { /* Display the entry itself */ put_fstr(col + 5, j + 1, "%s" CLR_SET_DEFAULT "%s", out_color[j], out_desc[j]); } /* Item here? */ if (o_ptr->number) { /* Display the weight if needed */ if (show_weights) { int wgt = o_ptr->weight * o_ptr->number; put_fstr(lim, j + 1, "%3d.%1d lb", wgt / 10, wgt % 10); if (store) { /* Extract the price */ u32b price = price_item(o_ptr, TRUE); /* Actually draw the price */ put_fstr(lim + 9, j + 1, "%9ld ", (long)price); } } else if (store) { /* Extract the price */ u32b price = price_item(o_ptr, TRUE); /* Actually draw the price */ put_fstr(lim, j + 1, "%9ld ", (long)price); } } } /* Make a "shadow" below the list (only if needed) */ if (j && (j < hgt - 2)) prtf(col ? col - 2 : col, j + 1, ""); } /* * Flip "inven" and "equip" in any sub-windows */ void toggle_inven_equip(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Unused */ if (!angband_term[j]) continue; /* Flip inven to equip */ if (window_flag[j] & (PW_INVEN)) { /* Flip flags */ window_flag[j] &= ~(PW_INVEN); window_flag[j] |= (PW_EQUIP); /* Window stuff */ p_ptr->window |= (PW_EQUIP); } /* Flip inven to equip */ else if (window_flag[j] & (PW_EQUIP)) { /* Flip flags */ window_flag[j] &= ~(PW_EQUIP); window_flag[j] |= (PW_INVEN); /* Window stuff */ p_ptr->window |= (PW_INVEN); } } } /* * Verify the choice of an item. * * The item can be negative to mean "item on floor". */ static bool verify(cptr prompt, object_type *o_ptr) { /* Query */ return (get_check("%s %v? ", prompt, OBJECT_FMT(o_ptr, TRUE, 3))); } /* * Hack -- allow user to "prevent" certain choices */ static bool get_item_allow(object_type *o_ptr) { cptr s; /* No inscription */ if (!o_ptr->inscription) return (TRUE); /* Find a '!' */ s = strchr(quark_str(o_ptr->inscription), '!'); /* Process preventions */ while (s) { /* Check the "restriction" */ if ((s[1] == p_ptr->cmd.cmd) || (s[1] == '*')) { /* Verify the choice */ if (!verify("Really try", o_ptr)) return (FALSE); } /* Find another '!' */ s = strchr(s + 1, '!'); } /* Allow it */ return (TRUE); } /* * Find the "first" inventory object with the given "tag". * * A "tag" is a char "n" appearing as "@n" anywhere in the * inscription of an object. * * Also, the tag "@xn" will work as well, where "n" is a tag-char, * and "x" is the "current" cmd.cmd code. */ static object_type *get_tag(bool *inven, char tag) { int i; cptr s; object_type *o_ptr; /* Check inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Skip empty inscriptions */ if (!o_ptr->inscription) continue; /* Find a '@' */ s = strchr(quark_str(o_ptr->inscription), '@'); /* Process all tags */ while (s) { /* Check the normal tags */ if (s[1] == tag) { /* Save the actual inventory ID */ *inven = TRUE; /* Success */ return (o_ptr); } /* Check the special tags */ if ((s[1] == p_ptr->cmd.cmd) && (s[2] == tag)) { /* Save the actual inventory ID */ *inven = TRUE; /* Success */ return (o_ptr); } /* Find another '@' */ s = strchr(s + 1, '@'); } } OBJ_ITT_END; /* Check equipment */ for (i = 0; i < EQUIP_MAX; i++) { object_type *o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Skip empty inscriptions */ if (!o_ptr->inscription) continue; /* Find a '@' */ s = strchr(quark_str(o_ptr->inscription), '@'); /* Process all tags */ while (s) { /* Check the normal tags */ if (s[1] == tag) { /* Save the actual inventory ID */ *inven = FALSE; /* Success */ return (o_ptr); } /* Check the special tags */ if ((s[1] == p_ptr->cmd.cmd) && (s[2] == tag)) { /* Save the actual inventory ID */ *inven = FALSE; /* Success */ return (o_ptr); } /* Find another '@' */ s = strchr(s + 1, '@'); } } /* No such tag */ return (NULL); } /* * test_floor -- * * Return the first valid item at a location + * Return the number of valid items at the location. * * Valid flags are: * * mode & 0x01 -- Item tester * mode & 0x02 -- Marked items only */ object_type *test_floor(int *num, cave_type *c_ptr, int mode) { object_type *q_ptr = NULL; object_type *o_ptr; /* No items yet */ *num = 0; /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Item tester */ if ((mode & 0x01) && !item_tester_okay(o_ptr)) continue; /* Marked */ if ((mode & 0x02) && !(o_ptr->info & OB_SEEN)) continue; /* Save first item */ if (!q_ptr) q_ptr = o_ptr; /* Count objects */ (*num)++; } OBJ_ITT_END; /* First item in list, if it exists */ return (q_ptr); } /* * Toggle the inventory and equipment terms when needed */ static bool toggle_windows(bool toggle, int command_wrk) { bool ni = FALSE; bool ne = FALSE; int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Unused */ if (!angband_term[j]) continue; /* Count windows displaying inven */ if (window_flag[j] & (PW_INVEN)) ni = TRUE; /* Count windows displaying equip */ if (window_flag[j] & (PW_EQUIP)) ne = TRUE; } /* Toggle if needed */ if ((command_wrk == (USE_EQUIP) && ni && !ne) || (command_wrk == (USE_INVEN) && !ni && ne)) { /* Toggle */ toggle_inven_equip(); /* Track toggles */ toggle = !toggle; } /* Update */ p_ptr->window |= (PW_INVEN | PW_EQUIP); /* Redraw windows */ window_stuff(); return (toggle); } /* * Show the prompt for items */ static void show_item_prompt(bool inven, bool equip, bool floor, bool store, cptr pmt, int command_wrk) { int i; int n1, n2; char out_val[160]; object_type *eo_ptr; int len = 0; switch (command_wrk) { case USE_INVEN: { /* Extract the legal requests */ get_label_bounds(p_ptr->inventory, &n1, &n2); /* Redraw */ show_list(p_ptr->inventory, store); /* Begin the prompt */ len = strnfmt(out_val, 160, "Inven:"); /* Append */ if (equip) strnfcat(out_val, 160, &len, " / for Equip,"); /* Append */ if (floor) strnfcat(out_val, 160, &len, " - for floor,"); break; } case USE_EQUIP: { /* Nothing yet */ n1 = -1; n2 = -1; /* Test for usable equipment */ for (i = 0; i < EQUIP_MAX; i++) { eo_ptr = &p_ptr->equipment[i]; if (item_tester_okay(eo_ptr)) { /* Get lower bound */ if (n1 == -1) n1 = i; /* Get higher bound */ n2 = i; } } /* Redraw */ show_equip(store); /* Begin the prompt */ len = strnfmt(out_val, 160, "Equip:"); /* Append */ if (inven) strnfcat(out_val, 160, &len, " / for Inven,"); /* Append */ if (floor) strnfcat(out_val, 160, &len, " - for floor,"); break; } case USE_FLOOR: { cave_type *c_ptr = area(p_ptr->px, p_ptr->py); /* Extract the legal requests */ get_label_bounds(p_ptr->inventory, &n1, &n2); if (easy_floor) { /* Redraw */ show_list(c_ptr->o_idx, store); /* Begin the prompt */ len = strnfmt(out_val, 160, "Floor:"); /* Append */ if (inven) { strnfcat(out_val, 160, &len, " / for Inven,"); } else if (equip) { strnfcat(out_val, 160, &len, " / for Equip,"); } } else { /* Begin the prompt */ len = strnfmt(out_val, 160, "Top item on floor: '-'"); } break; } } /* Show the prompt */ prtf(0, 0, "(%s ESC) %s", out_val, pmt); } /* * Remember the object we selected so that * we can repeat the action if required. */ static void save_object_choice(object_type *o_ptr, int command_wrk) { int index = -1; cave_type *c_ptr; /* Paranoia */ if (!o_ptr) return; /* Save type of prompt */ repeat_push(command_wrk); switch (command_wrk) { case USE_INVEN: { index = get_item_position(p_ptr->inventory, o_ptr); break; } case USE_EQUIP: { index = GET_ARRAY_INDEX(p_ptr->equipment, o_ptr); break; } case USE_FLOOR: { c_ptr = area(p_ptr->px, p_ptr->py); index = get_item_position(c_ptr->o_idx, o_ptr); break; } } /* Save the index */ repeat_push(index); } /* * Recall which object we have previously used. */ static object_type *recall_object_choice(int *command_wrk) { int type; int index; cave_type *c_ptr; object_type *o_ptr = NULL; /* Get type of prompt */ if (!repeat_pull(&type)) { /* Not a valid repeat - return invalid object */ return (NULL); } /* Paranoia */ if (type == -1) { /* Invalid repeat - reset it */ repeat_clear(); return (NULL); } /* Set type of prompt */ *command_wrk = type; /* Get index */ if (!repeat_pull(&index)) { /* Not a valid repeat - return invalid object */ return (NULL); } /* Paranoia */ if (index == -1) { /* Invalid repeat - reset it */ repeat_clear(); return (NULL); } /* Get item */ switch (type) { case USE_INVEN: { o_ptr = get_list_item(p_ptr->inventory, index); break; } case USE_EQUIP: { if ((index < 0) || (index >= EQUIP_MAX)) { o_ptr = NULL; } else { o_ptr = &p_ptr->equipment[index]; } break; } case USE_FLOOR: { c_ptr = area(p_ptr->px, p_ptr->py); o_ptr = get_list_item(c_ptr->o_idx, index); break; } default: { /* Invalid repeat - reset it */ repeat_clear(); return (NULL); } } /* Validate the item */ if (item_tester_okay(o_ptr)) { /* Forget the item_tester_tval restriction */ item_tester_tval = 0; /* Forget the item_tester_hook restriction */ item_tester_hook = NULL; /* Success */ return (o_ptr); } /* Invalid repeat - reset it */ repeat_clear(); return (NULL); } /* * Let the user select an item and return a pointer to it. * * The selected item must satisfy the "item_tester_hook()" function, * if that hook is set, and the "item_tester_tval", if that value is set. * * All "item_tester" restrictions are cleared before this function returns. * * The user is allowed to choose acceptable items from the equipment, * inventory, or floor, respectively, if the proper flag was given, * and there are any acceptable items in that location. * * The equipment or inventory are displayed (even if no acceptable * items are in that location) if the proper flag was given. * * If there are no acceptable items available anywhere, and "str" is * not NULL, then it will be used as the text of a warning message * before the function returns. * * Note that the user must press "-" to specify the item on the floor, * and there is no way to "examine" the item on the floor, while the * use of "capital" letters will "examine" an inventory/equipment item, * and prompt for its use. * * Global "p_ptr->command_new" is used when viewing the inventory or equipment * to allow the user to enter a command while viewing those screens, and * also to induce "auto-enter" of stores, and other such stuff. * * We always erase the prompt when we are done, leaving a blank line, * or a warning message, if appropriate, if no items are available. * * This version of get_item() includes the modifications due to the * easy_floor flag. This flag changes how items on the floor are treated. */ object_type *get_item(cptr pmt, cptr str, int mode) { cave_type *c_ptr = area(p_ptr->px, p_ptr->py); char which; int i; bool done = FALSE; bool equip = FALSE; bool inven = FALSE; bool floor = FALSE; bool store = FALSE; bool allow_equip = FALSE; bool allow_inven = FALSE; bool allow_floor = FALSE; int command_wrk; bool toggle = FALSE; int floor_num; /* First Floor item */ object_type *fo_ptr; /* Temp item */ object_type *q_ptr; object_type *o_ptr; /* Extract args */ if (mode & (USE_EQUIP)) equip = TRUE; if (mode & (USE_INVEN)) inven = TRUE; if (mode & (USE_FLOOR)) floor = TRUE; if (mode & (USE_STORE)) store = TRUE; /* Paranoia XXX XXX XXX */ message_flush(); /* Not done */ done = FALSE; /* Test for equipment */ if (equip) { for (i = 0; i < EQUIP_MAX; i++) { q_ptr = &p_ptr->equipment[i]; /* Only want valid items */ if (q_ptr->k_idx && item_tester_okay(q_ptr)) { allow_equip = TRUE; break; } } } /* Scan all objects in the grid */ fo_ptr = test_floor(&floor_num, c_ptr, 0x01); /* Accept floor */ if (floor_num && floor) allow_floor = TRUE; /* Scan inventory */ if (inven) { OBJ_ITT_START (p_ptr->inventory, q_ptr) { /* Only want valid items */ if (item_tester_okay(q_ptr)) { allow_inven = TRUE; break; } } OBJ_ITT_END; } /* Require at least one legal choice */ if (!allow_inven && !allow_equip && !allow_floor) { /* Warning if needed */ if (str) msgf(str); /* Forget the item_tester_tval restriction */ item_tester_tval = 0; /* Forget the item_tester_hook restriction */ item_tester_hook = NULL; /* Done */ return (FALSE); } /* Analyze choices */ /* Use inventory if allowed */ if (allow_inven) { command_wrk = (USE_INVEN); } /* Use equipment if allowed */ else if (allow_equip) { command_wrk = (USE_EQUIP); } /* Use floor if allowed */ else if (allow_floor) { command_wrk = (USE_FLOOR); } /* Get the saved item index */ o_ptr = recall_object_choice(&command_wrk); if (o_ptr) { /* Forget the item_tester_tval restriction */ item_tester_tval = 0; /* Forget the item_tester_hook restriction */ item_tester_hook = NULL; /* Done */ return (o_ptr); } /* Hack -- start out in "display" mode */ /* Save screen */ screen_save(); /* Repeat until done */ while (!done) { /* Make sure object is in the unselected state */ o_ptr = NULL; /* Activate the correct term info */ toggle = toggle_windows(toggle, command_wrk); /* Display the prompt */ show_item_prompt(allow_inven, allow_equip, allow_floor, store, pmt, command_wrk); /* Get a key */ which = inkey(); /* Parse it */ switch (which) { case ESCAPE: { done = TRUE; break; } case '/': { if (command_wrk == (USE_INVEN)) { if (!allow_equip) { bell("Cannot switch item selector!"); break; } command_wrk = (USE_EQUIP); } else if (command_wrk == (USE_EQUIP)) { if (!allow_inven) { bell("Cannot switch item selector!"); break; } command_wrk = (USE_INVEN); } else if (command_wrk == (USE_FLOOR)) { if (allow_inven) { command_wrk = (USE_INVEN); } else if (allow_equip) { command_wrk = (USE_EQUIP); } else { bell("Cannot switch item selector!"); break; } } /* Hack -- Fix screen */ /* Load screen */ screen_load(); /* Save screen */ screen_save(); /* Need to redraw */ break; } case '-': { if (!allow_floor) { bell("Cannot select floor!"); break; } if (!easy_floor) { /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Valid items only */ if (!item_tester_okay(o_ptr)) continue; /* Allow player to "refuse" certain actions */ if (!get_item_allow(o_ptr)) continue; /* Accept that choice */ done = TRUE; break; } OBJ_ITT_END; } /* * If we are already examining the floor, and there * is only one item, we will always select it. * If we aren't examining the floor and there is only * one item, we will select it if floor_query_flag * is FALSE. */ else if (floor_num == 1) { if ((command_wrk == (USE_FLOOR)) || (!carry_query_flag)) { /* Allow player to "refuse" certain actions */ if (!get_item_allow(fo_ptr)) continue; /* We use the first floor item */ o_ptr = fo_ptr; /* Accept that choice */ done = TRUE; break; } } /* Hack -- Fix screen */ /* Load screen */ screen_load(); /* Save screen */ screen_save(); command_wrk = (USE_FLOOR); break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool was_inven; /* Look up the tag */ o_ptr = get_tag(&was_inven, which); if (!o_ptr) { bell("Illegal object choice (tag)!"); break; } /* Hack -- Validate the item */ if (was_inven ? !inven : !equip) { bell("Illegal object choice (tag)!"); /* Invalid item */ o_ptr = NULL; break; } /* Validate the item */ if (!item_tester_okay(o_ptr)) { bell("Illegal object choice (tag)!"); /* Invalid item */ o_ptr = NULL; break; } /* Allow player to "refuse" certain actions */ if (!get_item_allow(o_ptr)) continue; /* Accept that choice */ done = TRUE; break; } case '\n': case '\r': { /* No object selected yet */ o_ptr = NULL; /* Choose "default" floor item */ if (command_wrk == (USE_FLOOR)) { if (floor_num == 1) { o_ptr = fo_ptr; } } /* Validate the item */ if (!o_ptr || !item_tester_okay(o_ptr)) { bell("Illegal object choice (default)!"); /* Invalid item */ o_ptr = NULL; break; } /* Allow player to "refuse" certain actions */ if (!get_item_allow(o_ptr)) continue; /* Accept that choice */ done = TRUE; break; } default: { int ver; /* Extract "query" setting */ ver = isupper(which); which = tolower(which); /* Convert letter to inventory index */ if (command_wrk == (USE_INVEN)) { o_ptr = label_to_list(which, p_ptr->inventory); } /* Convert letter to equipment index */ else if (command_wrk == (USE_EQUIP)) { o_ptr = label_to_equip(which); } /* Convert letter to floor index */ else if (command_wrk == USE_FLOOR) { o_ptr = label_to_list(which, c_ptr->o_idx); } /* Make sure selection is in bounds */ if (!o_ptr) { bell("Illegal object choice (bounds)!"); break; } /* Validate the item */ if (!item_tester_okay(o_ptr)) { bell("Illegal object choice (normal)!"); /* Invalid item */ o_ptr = NULL; break; } /* Verify the item */ if (ver && !verify("Try", o_ptr)) { done = TRUE; o_ptr = NULL; break; } /* Allow player to "refuse" certain actions */ if (!get_item_allow(o_ptr)) continue; /* Accept that choice */ done = TRUE; break; } } } /* Fix the screen */ /* Load screen */ screen_load(); /* Forget the item_tester_tval restriction */ item_tester_tval = 0; /* Forget the item_tester_hook restriction */ item_tester_hook = NULL; /* Clean up */ /* Toggle again if needed */ if (toggle) toggle_inven_equip(); /* Update */ p_ptr->window |= (PW_INVEN | PW_EQUIP); /* Window stuff */ window_stuff(); /* Clear the prompt line */ clear_msg(); /* Save this object */ save_object_choice(o_ptr, command_wrk); /* Done */ return (o_ptr); } zangband/src/object2.c0000755000000000000000000033605110250356274013642 0ustar rootroot/* File: object2.c */ /* Purpose: Object code, part 2 */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* * Temp object used to return an object not allocated via o_pop(). * * This is used by object_dup() and by object_prep() to * return "new" objects. Note that these routines delete * any object stored here before they use it. This means * that object (quark) refcounting will work. This should * be the only "static" object used, unless you make * absolutely sure the memory management is correct. * * Be very careful - object_copy() does not duplicate * references, where as object_dup() does. Make sure any * allocated object will be wiped eventually. temp_object * is wiped when ever it is used so it is safe. * * Note - because several routines use this variable, any * statically allocated object returned must be used asap. */ static object_type temp_object; /* * Prepare an object based on an existing object */ static void object_copy(object_type *o_ptr, const object_type *j_ptr) { /* Copy the structure */ COPY(o_ptr, j_ptr, object_type); } /* * Excise a dungeon object from any stacks */ static void excise_object_idx(s16b *o_idx_ptr, object_type *o_ptr) { object_type *j_ptr = NULL; object_type *q_ptr; /* Scan all objects in the list */ OBJ_ITT_START (*o_idx_ptr, q_ptr) { /* Hack - Done? */ if (q_ptr == o_ptr) { /* No previous */ if (!j_ptr) { /* Remove from list */ *o_idx_ptr = q_ptr->next_o_idx; } /* Real previous */ else { /* Remove from list */ j_ptr->next_o_idx = q_ptr->next_o_idx; } /* Forget next pointer */ q_ptr->next_o_idx = 0; /* Done */ break; } /* Save previous object */ j_ptr = q_ptr; } OBJ_ITT_END; } /* * Delete a dungeon object * * Handle "stacks" of objects correctly. */ void delete_held_object(s16b *o_idx_ptr, object_type *o_ptr) { /* Excise */ excise_object_idx(o_idx_ptr, o_ptr); /* Wipe the object */ object_wipe(o_ptr); /* Count objects */ o_cnt--; } /* * Delete an object we know is lying on the dungeon floor. */ void delete_dungeon_object(object_type *o_ptr) { int x, y; cave_type *c_ptr; /* Location */ x = o_ptr->ix; y = o_ptr->iy; c_ptr = area(x, y); /* Excise */ excise_object_idx(&c_ptr->o_idx, o_ptr); /* Visual update */ lite_spot(x, y); /* Wipe the object */ object_wipe(o_ptr); /* Count objects */ o_cnt--; } /* * Deletes all objects at given location */ void delete_object(int x, int y) { cave_type *c_ptr; /* Refuse "illegal" locations */ if (!in_bounds2(x, y)) return; /* Grid */ c_ptr = area(x, y); /* Delete the objects */ delete_object_list(&c_ptr->o_idx); /* Visual update */ lite_spot(x, y); } /* * Delete a statically-allocated object */ static void delete_static_object(object_type *o_ptr) { int i; /* Deallocate quarks */ quark_remove(&o_ptr->xtra_name); quark_remove(&o_ptr->inscription); for (i = 0; i < MAX_TRIGGER; i++) quark_remove(&o_ptr->trigger[i]); /* Do nothing */ return; } /* * Deletes all objects at given location */ void delete_object_list(s16b *o_idx_ptr) { object_type *o_ptr; /* * Special exception: if the first object is (nothing), * just zero the index. * This happens when loading savefiles. */ if (!o_list[*o_idx_ptr].k_idx) *o_idx_ptr = 0; /* Scan all objects in the grid */ OBJ_ITT_START (*o_idx_ptr, o_ptr) { /* Wipe the object */ object_wipe(o_ptr); /* Count objects */ if (o_cnt) o_cnt--; } OBJ_ITT_END; /* Objects are gone */ *o_idx_ptr = 0; } /* * Drop all the items in the list near the location (x, y). */ void drop_object_list(s16b *o_idx_ptr, int x, int y) { object_type *o_ptr; object_type *q_ptr; /* Drop objects being carried */ OBJ_ITT_START (*o_idx_ptr, o_ptr) { /* Duplicate object */ q_ptr = object_dup(o_ptr); /* Delete held object */ delete_held_object(o_idx_ptr, o_ptr); /* Drop object */ drop_near(q_ptr, -1, x, y); } OBJ_ITT_END; /* No more objects in the list */ *o_idx_ptr = 0; } /* * Compact and Reorder the object list * * This function can be very dangerous, use with caution! * * When actually "compacting" objects, we base the saving throw on a * combination of object level, distance from player, and current * "desperation". * * After "compacting" (if needed), we "reorder" the objects into a more * compact order, and we reset the allocation info, and the "live" array. */ void compact_objects(int size) { int i, y, x, num, cnt; int cur_lev, cur_dis, chance; object_type *o_ptr; monster_type *m_ptr; /* Compact */ if (size) { /* Message */ msgf("Compacting objects..."); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* Compact at least 'size' objects */ for (num = 0, cnt = 1; num < size; cnt++) { /* Get more vicious each iteration */ cur_lev = 5 * cnt; /* Get closer each iteration */ cur_dis = 5 * (20 - cnt); /* Examine the objects */ for (i = 1; i < o_max; i++) { o_ptr = &o_list[i]; /* Skip dead objects */ if (!o_ptr->k_idx) continue; /* Hack -- High level objects start out "immune" */ if (get_object_level(o_ptr) > cur_lev) continue; /* Only objects in the dungeon */ if (!((o_ptr->ix) && (o_ptr->iy))) continue; /* Get the location */ x = o_ptr->ix; y = o_ptr->iy; /* Nearby objects start out "immune" */ if (distance(p_ptr->px, p_ptr->py, x, y) < cur_dis) continue; /* Saving throw */ chance = 90; /* Hack -- only compact artifacts in emergencies */ if ((FLAG(o_ptr, TR_INSTA_ART)) && (cnt < 1000)) chance = 100; /* Apply the saving throw */ if (randint0(100) < chance) continue; /* Delete the object */ delete_dungeon_object(o_ptr); /* Count it */ num++; } /* Scan monster inventories */ for (i = 1; i < m_max; i++) { m_ptr = &m_list[i]; /* Ignore dead monsters */ if (!m_ptr->r_idx) continue; /* We need to hold some objects */ if (!m_ptr->hold_o_idx) continue; /* Get the location */ x = m_ptr->fx; y = m_ptr->fy; /* Nearby objects start out "immune" */ if (distance(p_ptr->px, p_ptr->py, x, y) < cur_dis) continue; /* Saving throw (higher than dungeon objects) */ chance = 99; OBJ_ITT_START (m_ptr->hold_o_idx, o_ptr) { /* Hack -- High level objects start out "immune" */ if (get_object_level(o_ptr) > cur_lev) continue; /* Hack -- only compact artifacts in emergencies */ if ((FLAG(o_ptr, TR_INSTA_ART)) && (cnt < 1000)) chance = 100; /* Apply the saving throw */ if (randint0(100) < chance) continue; /* Delete the object */ delete_held_object(&m_ptr->hold_o_idx, o_ptr); /* Count it */ num++; } OBJ_ITT_END; } } /* Excise dead objects (backwards!) */ while (o_max > 1) { object_type *o_ptr = &o_list[o_max - 1]; /* Stop when we get to an object */ if (o_ptr->k_idx) break; /* Compress "o_max" */ o_max--; } } /* * Delete all the non-held items. * Items in the players inventory are not touched. * Items in monster inventories are wiped by * calling wipe_m_list() before this function. * * Hack -- we clear the "c_ptr->o_idx" field for every grid * since we know we are clearing every object. Technically, we * only clear those fields for grids containing objects, * and we clear it once for every such object. */ void wipe_o_list(void) { int i; int x, y; cave_type *c_ptr; object_type *o_ptr; /* Set all objects to be unallocated */ for (i = 1; i < o_max; i++) { o_ptr = &o_list[i]; o_ptr->allocated = FALSE; } /* Only if inventory exists */ if (o_list[p_ptr->inventory].k_idx) { /* Save players inventory (only objects in a list to save) */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { o_ptr->allocated = TRUE; } OBJ_ITT_END; } /* Delete the existing objects */ for (i = 1; i < o_max; i++) { o_ptr = &o_list[i]; /* Skip dead objects */ if (!o_ptr->k_idx) continue; /* Skip allocated objects */ if (o_ptr->allocated) continue; /* Preserve artifacts */ if (preserve_mode && (FLAG(o_ptr, TR_INSTA_ART)) && o_ptr->a_idx && (a_info[o_ptr->a_idx].cur_num == 1)) { a_info[o_ptr->a_idx].cur_num = 0; } /* Access location */ y = o_ptr->iy; x = o_ptr->ix; /* Store item? */ if (!x && !y) { /* Hack - just kill it */ object_wipe(o_ptr); /* Count objects */ o_cnt--; continue; } /* Access grid */ c_ptr = area(x, y); /* Hack -- see above */ c_ptr->o_idx = 0; /* Delete the object */ delete_dungeon_object(o_ptr); } /* Compress the object list */ compact_objects(0); } /* * Wipe objects in region */ void wipe_objects(int rg_idx) { int i; object_type *o_ptr; /* Delete the existing objects */ for (i = 1; i < o_max; i++) { o_ptr = &o_list[i]; /* Skip dead objects */ if (!o_ptr->k_idx) continue; /* Enforce region */ if (o_ptr->region != rg_idx) continue; /* Preserve artifacts */ if (preserve_mode && (FLAG(o_ptr, TR_INSTA_ART)) && o_ptr->a_idx && (a_info[o_ptr->a_idx].cur_num == 1)) { a_info[o_ptr->a_idx].cur_num = 0; } /* Delete the object */ delete_dungeon_object(o_ptr); } } /* Current object counter */ static s16b o_cur = 1; /* * Acquires and returns the index of a "free" object. * * This routine should almost never fail, but in case it does, * we must be sure to handle "failure" of this routine. * * * We have a choice... scan from o_cur onwards, or use o_max. * * Using o_max is fast, but it leads to fragmentation. * Using o_cur is slower (and might not even give a better * result than using o_max), but it leads to a more compact * object distribution. */ static s16b o_pop(void) { bool wrapped = FALSE; /* Wrap counter */ if (o_cur >= o_max) o_cur = 1; /* * If the number remaining is less than one third of the * total number of allocated objects, then add a new object * to the end of the list. * * We add 1 to o_cnt because object 0 is unusable. * * Feel free to tune this parameter. */ if ((o_max - (o_cnt + 1)) * 3 < o_max) { /* Initial allocation */ if (o_max < z_info->o_max) { /* Expand object array */ o_max++; /* Count objects */ o_cnt++; /* Use this object */ return (o_max - 1); } } /* Recycle dead objects */ while (TRUE) { object_type *o_ptr; /* Acquire object */ o_ptr = &o_list[o_cur]; /* Skip live objects */ if (o_ptr->k_idx) { /* Increment counter */ o_cur++; /* Wrap counter */ if (o_cur >= o_max) { /* Infinite loop protection */ if (wrapped) break; o_cur = 1; wrapped = TRUE; } continue; } /* Count objects */ o_cnt++; /* Use this object */ return (o_cur); } /* Warn the player (except during dungeon creation) */ if (character_dungeon) msgf("Too many objects!"); /* Oops */ return (0); } /* * Add an object to a list. o_ptr is a pointer * to a statically declared object that is assumed * not to be in the standard object array. * * We do not adjust position, held or region information. * * The old object is wiped. */ object_type *add_object_list(s16b *o_idx_ptr, object_type *o_ptr) { s16b o_idx; object_type *j_ptr; /* Get a new object */ o_idx = o_pop(); /* Hack - bail out */ if (!o_idx) return (NULL); /* Point to the object */ j_ptr = &o_list[o_idx]; /* Move to the list */ swap_objects(j_ptr, o_ptr); /* Add to the list */ j_ptr->next_o_idx = *o_idx_ptr; *o_idx_ptr = o_idx; /* Now held */ j_ptr->allocated = TRUE; /* Return the new item */ return (j_ptr); } /* * Move an object from one list to another */ void move_object(s16b *tgt_list_ptr, s16b *cur_list_ptr, object_type *o_ptr) { /* Excise object from the first list */ excise_object_idx(cur_list_ptr, o_ptr); /* Add to the new list */ o_ptr->next_o_idx = *tgt_list_ptr; /* *tgt_list_ptr = o_idx; */ *tgt_list_ptr = GET_ARRAY_INDEX(o_list, o_ptr); } /* * Swap objects */ void swap_objects(object_type *o1_ptr, object_type *o2_ptr) { object_type temp; /* Copy the object */ object_copy(&temp, o2_ptr); /* Copy the object */ object_copy(o2_ptr, o1_ptr); /* Get correct next-object fields */ o2_ptr->next_o_idx = temp.next_o_idx; temp.next_o_idx = o1_ptr->next_o_idx; /* Get correct position fields */ o2_ptr->ix = temp.ix; temp.ix = o1_ptr->ix; o2_ptr->iy = temp.iy; temp.iy = o1_ptr->iy; /* Get correct region */ o2_ptr->region = temp.region; temp.region = o1_ptr->region; /* Get correct allocated value */ o2_ptr->allocated = temp.allocated; temp.allocated = o1_ptr->allocated; /* Copy the object */ object_copy(o1_ptr, &temp); } /* * Apply a "object restriction function" to the "object allocation table" */ void get_obj_num_prep(object_hook_type object_hook) { int i; byte prob; /* Get the entry */ alloc_entry *table = alloc_kind_table; /* Scan the allocation table */ for (i = 0; i < alloc_kind_size; i++) { /* Accept objects which pass the restriction, if any */ if (object_hook) { /* Get probability */ prob = (*object_hook) (table[i].index); /* Paranoia */ if (prob > 100) prob = 100; /* Accept this object */ table[i].prob2 = (table[i].prob1 * prob) / 100; } else { /* Accept this object */ table[i].prob2 = table[i].prob1; } } } /* * Choose an object kind that seems "appropriate" to the given level * * This function uses the "prob2" field of the "object allocation table", * and various local information, to calculate the "prob3" field of the * same table, which is then used to choose an "appropriate" object, in * a relatively efficient manner. * * It is more likely to acquire an object of the given level * than one of a lower level. This is done by choosing three objects * appropriate to the given level and keeping the "hardest" one. * * Note that if no objects are "appropriate", then this function will * fail, and return zero, but this should *almost* never happen. */ s16b get_obj_num(int level, int min_level) { int i; long value1, value2, total; alloc_entry *table = alloc_kind_table; /* Luck gives occasional out-of-depth items */ if ((FLAG(p_ptr, TR_STRANGE_LUCK)) && one_in_(13)) { level += randint1(one_in_(7) ? 40 : 10); } /* Occasional "boost" */ if (one_in_(GREAT_OBJ)) { /* What a bizarre calculation */ level = 1 + (level * MAX_DEPTH / randint1(MAX_DEPTH)); } /* Reset total */ total = 0L; /* Process probabilities */ for (i = 0; i < alloc_kind_size; i++) { /* Objects are sorted by depth */ if (table[i].level > level) break; /* What John West rejects, makes John West the best. */ if (table[i].level < min_level) continue; /* Total */ total += table[i].prob2; } /* No legal objects */ if (total <= 0) return (0); /* Pick an object */ value1 = randint0(total); for (i = 0; i < 3; i++) { /* Try for a "better" object once */ value2 = randint0(total); /* Is it better? */ if (value2 > value1) { /* This hack works because the object table is sorted by depth */ value1 = value2; } } /* Find the object */ for (i = 0; i < alloc_kind_size; i++) { /* What John West rejects, makes John West the best. */ if (table[i].level < min_level) continue; /* Decrement */ value1 -= table[i].prob2; /* A match? */ if (value1 < 0L) break; } /* Result */ return (table[i].index); } /* * Known is true when the "attributes" of an object are "known". * These include tohit, todam, toac, cost, and pval (charges). * * Note that "knowing" an object gives you everything that an "awareness" * gives you, and much more. In fact, the player is always "aware" of any * item of which he has full "knowledge". * * But having full knowledge of, say, one "wand of wonder", does not, by * itself, give you knowledge, or even awareness, of other "wands of wonder". * It happens that most "identify" routines (including "buying from a shop") * will make the player "aware" of the object as well as fully "know" it. * * This routine also removes any inscriptions generated by "feelings". */ void object_known(object_type *o_ptr) { /* Remove "default inscriptions" */ o_ptr->feeling = FEEL_NONE; /* Clear the "Felt" info */ o_ptr->info &= ~(OB_SENSE); /* Clear the "Empty" info */ o_ptr->info &= ~(OB_EMPTY); /* Now we know about the item */ o_ptr->info |= (OB_KNOWN); } /* * The player is now aware of the effects of the given object. */ void object_aware(object_type *o_ptr) { /* Fully aware of the effects */ k_info[o_ptr->k_idx].aware = TRUE; } /* * Something has been "sampled" */ void object_tried(object_type *o_ptr) { /* Mark it as tried (even if "aware") */ k_info[o_ptr->k_idx].tried = TRUE; } /* * The player has now *identified* the item */ void object_mental(object_type *o_ptr) { o_ptr->info |= (OB_MENTAL); } /* * Return the "value" of an "unknown" item * Make a guess at the value of non-aware items */ static s32b object_value_base(const object_type *o_ptr) { /* Aware item -- use template cost */ if (object_aware_p(o_ptr)) return (k_info[o_ptr->k_idx].cost); /* Analyze the type */ switch (o_ptr->tval) { case TV_FOOD: { /* Un-aware Food */ return (5L); } case TV_POTION: { /* Un-aware Potions */ return (20L); } case TV_SCROLL: { /* Un-aware Scrolls */ return (20L); } case TV_STAFF: { /* Un-aware Staffs */ return (70L); } case TV_WAND: { /* Un-aware Wands */ return (50L); } case TV_ROD: { /* Un-aware Rods */ return (90L); } case TV_RING: { /* Un-aware Rings */ return (45L); } case TV_AMULET: { /* Un-aware Amulets */ return (45L); } } /* Paranoia -- Oops */ return (0L); } /* Return the sign of the argument * x * x. */ static s32b sqvalue(s32b x) { if (x < 0) return (-x * x); return (x * x); } /* Return a number dependent on the argument */ static s32b bonus_value(s32b x) { if (x <= 0) return 5 * x; if (x < 10) return x + 5; if (x < 20) return 2 * (x - 10) + 15; return 5 * (x - 20) + 35; } /* Return the value of the flags the object has... */ s32b flag_cost(const object_type *o_ptr, int plusses) { s32b total = 0; if (FLAG(o_ptr, TR_STR)) total += (500 * plusses); if (FLAG(o_ptr, TR_INT)) total += (500 * plusses); if (FLAG(o_ptr, TR_WIS)) total += (500 * plusses); if (FLAG(o_ptr, TR_DEX)) total += (500 * plusses); if (FLAG(o_ptr, TR_CON)) total += (500 * plusses); if (FLAG(o_ptr, TR_CHR)) total += (250 * plusses); if ((FLAG(o_ptr, TR_SP)) && (plusses > 0)) total += (2500 * plusses); if (FLAG(o_ptr, TR_CHAOTIC)) total += 500; if (FLAG(o_ptr, TR_VAMPIRIC)) total += 5000; if (FLAG(o_ptr, TR_STEALTH)) total += (50 * plusses); if (FLAG(o_ptr, TR_SEARCH)) total += (50 * plusses); if (FLAG(o_ptr, TR_INFRA)) total += (30 * plusses); if (FLAG(o_ptr, TR_TUNNEL)) total += (20 * plusses); if ((FLAG(o_ptr, TR_SPEED)) && (plusses > 0)) total += (500 * sqvalue(plusses)); if ((FLAG(o_ptr, TR_BLOWS)) && (plusses > 0)) total += (2500 * sqvalue(plusses)); if (FLAG(o_ptr, TR_SLAY_ANIMAL)) total += 750; if (FLAG(o_ptr, TR_SLAY_EVIL)) total += 750; if (FLAG(o_ptr, TR_SLAY_UNDEAD)) total += 750; if (FLAG(o_ptr, TR_SLAY_DEMON)) total += 750; if (FLAG(o_ptr, TR_SLAY_ORC)) total += 750; if (FLAG(o_ptr, TR_SLAY_TROLL)) total += 750; if (FLAG(o_ptr, TR_SLAY_GIANT)) total += 750; if (FLAG(o_ptr, TR_SLAY_DRAGON)) total += 750; if (FLAG(o_ptr, TR_KILL_DRAGON)) total += 1500; if (FLAG(o_ptr, TR_VORPAL)) total += 1500; if (FLAG(o_ptr, TR_IMPACT)) total += 1500; if (FLAG(o_ptr, TR_BRAND_POIS)) total += 750; if (FLAG(o_ptr, TR_BRAND_ACID)) total += 750; if (FLAG(o_ptr, TR_BRAND_ELEC)) total += 750; if (FLAG(o_ptr, TR_BRAND_FIRE)) total += 750; if (FLAG(o_ptr, TR_BRAND_COLD)) total += 750; if (FLAG(o_ptr, TR_SUST_STR)) total += 200; if (FLAG(o_ptr, TR_SUST_INT)) total += 200; if (FLAG(o_ptr, TR_SUST_WIS)) total += 200; if (FLAG(o_ptr, TR_SUST_DEX)) total += 200; if (FLAG(o_ptr, TR_SUST_CON)) total += 200; if (FLAG(o_ptr, TR_SUST_CHR)) total += 200; if (FLAG(o_ptr, TR_IM_POIS)) total += 10000; if (FLAG(o_ptr, TR_IM_ACID)) total += 10000; if (FLAG(o_ptr, TR_IM_ELEC)) total += 10000; if (FLAG(o_ptr, TR_IM_FIRE)) total += 10000; if (FLAG(o_ptr, TR_IM_COLD)) total += 10000; if (FLAG(o_ptr, TR_IM_LITE)) total += 10000; if (FLAG(o_ptr, TR_IM_DARK)) total += 10000; if (FLAG(o_ptr, TR_THROW)) total += 2000; if (FLAG(o_ptr, TR_REFLECT)) total += 5000; if (FLAG(o_ptr, TR_RES_ACID)) total += 500; if (FLAG(o_ptr, TR_RES_ELEC)) total += 500; if (FLAG(o_ptr, TR_RES_FIRE)) total += 500; if (FLAG(o_ptr, TR_RES_COLD)) total += 500; if (FLAG(o_ptr, TR_RES_POIS)) total += 1500; if (FLAG(o_ptr, TR_RES_FEAR)) total += 1500; if (FLAG(o_ptr, TR_RES_LITE)) total += 1500; if (FLAG(o_ptr, TR_RES_DARK)) total += 1500; if (FLAG(o_ptr, TR_RES_BLIND)) total += 1500; if (FLAG(o_ptr, TR_RES_CONF)) total += 1500; if (FLAG(o_ptr, TR_RES_SOUND)) total += 1500; if (FLAG(o_ptr, TR_RES_SHARDS)) total += 1500; if (FLAG(o_ptr, TR_RES_NETHER)) total += 1500; if (FLAG(o_ptr, TR_RES_NEXUS)) total += 1500; if (FLAG(o_ptr, TR_RES_CHAOS)) total += 1500; if (FLAG(o_ptr, TR_RES_DISEN)) total += 1500; if (FLAG(o_ptr, TR_SH_FIRE)) total += 1000; if (FLAG(o_ptr, TR_SH_ELEC)) total += 1000; if (FLAG(o_ptr, TR_SH_ACID)) total += 1000; if (FLAG(o_ptr, TR_SH_COLD)) total += 1000; if (FLAG(o_ptr, TR_QUESTITEM)) total += 0; if (FLAG(o_ptr, TR_XXX4)) total += 0; if (FLAG(o_ptr, TR_NO_TELE)) total += 2500; if (FLAG(o_ptr, TR_NO_MAGIC)) total += 2500; if (FLAG(o_ptr, TR_TY_CURSE)) total -= 15000; if (FLAG(o_ptr, TR_EASY_KNOW)) total += 0; if (FLAG(o_ptr, TR_HIDE_TYPE)) total += 0; if (FLAG(o_ptr, TR_SHOW_MODS)) total += 0; if (FLAG(o_ptr, TR_INSTA_ART)) total += 0; /* EGO_XTRA_ABILITY */ if (FLAG(o_ptr, TR_SLOW_DIGEST)) total += 250; if (FLAG(o_ptr, TR_FEATHER)) total += 250; if (FLAG(o_ptr, TR_LITE)) total += 500; if (FLAG(o_ptr, TR_SEE_INVIS)) total += 500; if (FLAG(o_ptr, TR_REGEN)) total += 1000; if (FLAG(o_ptr, TR_FREE_ACT)) total += 1000; if (FLAG(o_ptr, TR_HOLD_LIFE)) total += 2000; if (FLAG(o_ptr, TR_TELEPATHY)) total += 2000; if (FLAG(o_ptr, TR_XTRA_MIGHT)) total += 1000; if (FLAG(o_ptr, TR_XTRA_SHOTS)) total += 1000; if (FLAG(o_ptr, TR_IGNORE_ACID)) total += 50; if (FLAG(o_ptr, TR_IGNORE_ELEC)) total += 50; if (FLAG(o_ptr, TR_IGNORE_FIRE)) total += 50; if (FLAG(o_ptr, TR_IGNORE_COLD)) total += 50; if (FLAG(o_ptr, TR_ACTIVATE)) total += 0; if (FLAG(o_ptr, TR_DRAIN_EXP)) total -= 12500; if (FLAG(o_ptr, TR_TELEPORT)) { if (cursed_p(o_ptr)) total -= 7500; else total += 250; } if (FLAG(o_ptr, TR_AGGRAVATE)) total -= 5000; if (FLAG(o_ptr, TR_BLESSED)) total += 200; if (FLAG(o_ptr, TR_CURSED)) total -= 5000; if (FLAG(o_ptr, TR_HEAVY_CURSE)) total -= 12500; if (FLAG(o_ptr, TR_PERMA_CURSE)) total -= 15000; if (FLAG(o_ptr, TR_LUCK_10)) total += 3000; if (FLAG(o_ptr, TR_WILD_SHOT)) total += 50; if (FLAG(o_ptr, TR_WILD_WALK)) total += 200; if (FLAG(o_ptr, TR_MUTATE)) total += 500; if (FLAG(o_ptr, TR_PATRON)) total += 500; if (FLAG(o_ptr, TR_STRANGE_LUCK)) total += 2000; if (FLAG(o_ptr, TR_PASS_WALL)) total += 25000; if (FLAG(o_ptr, TR_GHOUL_TOUCH)) total += 750; if (FLAG(o_ptr, TR_PSI_CRIT)) total += 1500; if (FLAG(o_ptr, TR_RETURN)) total += 500; if (FLAG(o_ptr, TR_EXPLODE)) total += 500; if (FLAG(o_ptr, TR_HURT_ACID)) total -= 5000; if (FLAG(o_ptr, TR_HURT_ELEC)) total -= 5000; if (FLAG(o_ptr, TR_HURT_FIRE)) total -= 5000; if (FLAG(o_ptr, TR_HURT_COLD)) total -= 5000; if (FLAG(o_ptr, TR_HURT_LITE)) total -= 2500; if (FLAG(o_ptr, TR_HURT_DARK)) total -= 2500; if (FLAG(o_ptr, TR_XXX27)) total += 0; if (FLAG(o_ptr, TR_XXX28)) total += 0; if (FLAG(o_ptr, TR_AUTO_CURSE)) total -= 2500; if (FLAG(o_ptr, TR_DRAIN_STATS)) total -= 5000; if (FLAG(o_ptr, TR_CANT_EAT)) total -= 500; if (FLAG(o_ptr, TR_SLOW_HEAL)) total -= 2000; return total; } /* * Return the "real" price of a "known" item, not including discounts * * Wand and staffs get cost for each charge * * Armor is worth an extra 5 gold per bonus point to armor class^2. * * Weapons are worth an extra 5 gold per bonus point^2 (AC,TH,TD). * * Note: the bonuses are proportional to the points squared. * * Missiles are only worth 5 gold per bonus point, since they * usually appear in groups of 20, and we want the player to get * the same amount of cash for any "equivalent" item. Note that * missiles never have any of the "pval" flags, and in fact, they * only have a few of the available flags, primarily of the "slay" * and "brand" and "ignore" variety. * * Armor with a negative armor bonus is worthless. * * Every wearable item with a "pval" bonus is worth extra (see below). */ s32b object_value_real(const object_type *o_ptr) { s32b value; object_kind *k_ptr = &k_info[o_ptr->k_idx]; object_type dummy; /* Base cost */ value = o_ptr->cost; /* Hack -- "worthless" items */ if (!value) return (0L); /* * For non-artifact items, count the flag value */ if (!o_ptr->a_idx) { int divisor = 1; /* Ammo gets less value from flags */ if (o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW || o_ptr->tval == TV_BOLT) { divisor = 20; } /* Initialize the dummy object */ memcpy(&dummy, o_ptr, sizeof(object_type)); /* Copy the flags from the type to the dummy object */ memcpy(dummy.flags, k_ptr->flags, sizeof(dummy.flags)); /* Price the object's flags vs the default flags */ value += flag_cost(o_ptr, o_ptr->pval) / divisor; value -= flag_cost(&dummy, 1) / divisor; } /* Analyze the item */ switch (o_ptr->tval) { case TV_WAND: { /* Wands */ /* Pay extra for charges, depending on standard number of * charges. Handle new-style wands correctly. -LM- */ value += (value * o_ptr->pval / o_ptr->number / (k_ptr->pval * 2)); /* Done */ break; } case TV_STAFF: { /* Staffs */ /* Pay extra for charges, depending on standard number of * charges. -LM- */ value += (value * o_ptr->pval / (k_ptr->pval * 2)); /* Done */ break; } case TV_RING: case TV_AMULET: { /* Rings/Amulets */ /* Hack -- special handling for amulets of Berserk Strength */ if (o_ptr->sval == SV_AMULET_BERSERK) { value += (bonus_value(o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 20); /* Done */ break; } /* Hack -- negative bonuses are bad */ if (o_ptr->to_a < 0) return (0L); if (o_ptr->to_h < 0) return (0L); if (o_ptr->to_d < 0) return (0L); /* Factor in the bonuses */ /* * Note that combat bonuses on a ring/amulet are worth * twice what they are on a weapon/armor */ value += (bonus_value(o_ptr->to_h + o_ptr->to_d) * 40 + bonus_value(o_ptr->to_a * 2) * 20); /* Done */ break; } case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_CROWN: case TV_HELM: case TV_SHIELD: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Armour */ /* Factor in the bonuses */ value += (bonus_value(o_ptr->to_h - k_ptr->to_h + o_ptr->to_d - k_ptr->to_d) * 20); value += (bonus_value((o_ptr->to_a - k_ptr->to_a) * 2) * 10); /* Done */ break; } case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_SWORD: case TV_POLEARM: { /* Bows/Weapons */ /* Factor in the bonuses */ value += (bonus_value(o_ptr->to_h + o_ptr->to_d) * 20 + bonus_value(o_ptr->to_a * 2) * 10); /* Hack -- Factor in extra damage dice */ if (k_ptr->dd * k_ptr->ds) { value = value * o_ptr->dd * o_ptr->ds / (k_ptr->dd * k_ptr->ds); } /* Done */ break; } case TV_SHOT: case TV_ARROW: case TV_BOLT: { /* Ammo */ /* Factor in the bonuses */ value += (bonus_value(o_ptr->to_h + o_ptr->to_d) * 2); /* Hack -- Factor in extra damage dice */ if (k_ptr->dd * k_ptr->ds) { value = value * o_ptr->dd * o_ptr->ds / (k_ptr->dd * k_ptr->ds); } /* Done */ break; } case TV_FIGURINE: { /* Figurines, relative to monster level */ value = (r_info[o_ptr->pval].level * r_info[o_ptr->pval].level * 5L); break; } } /* No negative value */ if (value < 0) value = 0; /* Return the value */ return (value); } /* * Return the price of an item including plusses (and charges) * * This function returns the "value" of the given item (qty one) * * Never notice "unknown" bonuses or properties, including "curses", * since that would give the player information he did not have. * * Note that discounted items stay discounted forever, even if * the discount is "forgotten" by the player via memory loss. */ s32b object_value(const object_type *o_ptr) { s32b value; /* Known items -- acquire the actual value */ if (object_known_p(o_ptr)) { /* Broken items -- worthless */ if (!o_ptr->cost) return (0L); /* Cursed items -- worthless */ if (cursed_p(o_ptr)) return (0L); /* Real value (see above) */ value = object_value_real(o_ptr); } /* Unknown items -- acquire a base value */ else { /* Hack -- Felt broken items */ if ((o_ptr->info & (OB_SENSE)) && !o_ptr->cost) return (0L); /* Hack -- Felt cursed items */ if ((o_ptr->info & (OB_SENSE)) && cursed_p(o_ptr)) return (0L); /* Base value (see above) */ value = object_value_base(o_ptr); } /* Apply discount (if any) */ if (o_ptr->discount) value -= (value * o_ptr->discount / 100L); /* Return the final value */ return (value); } /* * Distribute charges of rods or wands. * * o_ptr = source item * q_ptr = target item, must be of the same type as o_ptr * amt = number of items that are transfered */ void distribute_charges(object_type *o_ptr, object_type *q_ptr, int amt) { int new_charges; /* Paranoia */ if (!o_ptr->number) return; /* * Hack -- If rods or wands are dropped, the total maximum timeout or * charges needs to be allocated between the two stacks. If all the items * are being dropped, it makes for a neater message to leave the original * stack's pval alone. -LM- */ if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_ROD)) { new_charges = (o_ptr->pval + o_ptr->ac) * amt / o_ptr->number; /* Hack - AC is a count of the "used" charges */ if (o_ptr->tval == TV_WAND) { /* Used more changes than a single wand has */ if (o_ptr->ac > new_charges) { /* Drop an empty wand */ q_ptr->pval = 0; o_ptr->ac -= new_charges; q_ptr->ac = new_charges; } else { /* Split the charges evenly - then move the "used ones" */ q_ptr->pval = new_charges - o_ptr->ac; q_ptr->ac = o_ptr->ac; o_ptr->ac = 0; } } else { /* Rods are simple - just split them geometrically */ q_ptr->pval = o_ptr->pval * amt / o_ptr->number; } /* Subtract moved charges */ if (amt < o_ptr->number) o_ptr->pval -= q_ptr->pval; /* Hack -- Rods also need to have their timeouts distributed. The * dropped stack will accept all time remaining to charge up to its * maximum. */ if ((o_ptr->tval == TV_ROD) && (o_ptr->timeout)) { if (q_ptr->pval > o_ptr->timeout) q_ptr->timeout = o_ptr->timeout; else q_ptr->timeout = q_ptr->pval; if (amt < o_ptr->number) o_ptr->timeout -= q_ptr->timeout; } } } void reduce_charges(object_type *o_ptr, int amt) { /* * Hack -- If rods or wand are destroyed, the total maximum timeout or * charges of the stack needs to be reduced, unless all the items are * being destroyed. -LM- */ if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_ROD)) && (amt < o_ptr->number)) { o_ptr->pval -= o_ptr->pval * amt / o_ptr->number; o_ptr->ac -= o_ptr->ac * amt / o_ptr->number; } } /* * Determine if an item can "absorb" a second item * * See "object_absorb()" for the actual "absorption" code. * * If permitted, we allow staffs (if they are known to have equal charges * and both are either known or confirmed empty) and rods (in all cases) * to combine. * Staffs will unstack (if necessary) when they are used, but wands and * rods will only unstack if one is dropped. -LM- * * If permitted, we allow weapons/armor to stack, if fully "known". * * Missiles will combine if both stacks have the same "known" status. * This is done to make unidentified stacks of missiles useful. * * Food, potions, scrolls, and "easy know" items always stack. * * Chests, and activatable items, never stack (for various reasons). */ bool object_similar(const object_type *o_ptr, const object_type *j_ptr) { int i; /* Require identical object types */ if (o_ptr->k_idx != j_ptr->k_idx) return (FALSE); /* Analyze the items */ switch (o_ptr->tval) { case TV_CHEST: { /* Chests */ /* Never okay */ return (FALSE); } case TV_FIGURINE: case TV_STATUE: { /* Figurines and Statues */ /* Never okay */ return (FALSE); } case TV_FOOD: case TV_POTION: case TV_SCROLL: { /* Food and Potions and Scrolls */ /* Assume okay */ break; } case TV_STAFF: { /* Staffs */ /* Require either knowledge or known empty for both staffs. */ if ((!(o_ptr->info & (OB_EMPTY)) && !object_known_p(o_ptr)) || (!(j_ptr->info & (OB_EMPTY)) && !object_known_p(j_ptr))) return (FALSE); /* Require identical charges, since staffs are bulky. */ if (o_ptr->pval != j_ptr->pval) return (FALSE); /* Assume okay */ break; } case TV_WAND: { /* Wands */ /* Wand charges combine in O&ZAngband. */ if (object_known_p(o_ptr) != object_known_p(j_ptr)) return (FALSE); /* Assume okay */ break; } case TV_ROD: { /* Rods */ /* Assume okay */ break; } case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Weapons and Armor stack if throwable */ if (!(FLAG(o_ptr, TR_THROW))) return (FALSE); /* Fall through */ } case TV_LITE: { /* Hack - Require identical fuel levels / timeouts */ if (o_ptr->timeout != j_ptr->timeout) return (FALSE); /* Fall through */ } case TV_RING: case TV_AMULET: { /* Rings, Amulets, Lites */ /* Require full knowledge of both items */ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (FALSE); /* Fall through */ } case TV_BOLT: case TV_ARROW: case TV_SHOT: { /* Missiles */ /* Require identical knowledge of both items */ if (object_known_p(o_ptr) != object_known_p(j_ptr)) return (FALSE); /* Require identical "bonuses" */ if (o_ptr->to_h != j_ptr->to_h) return (FALSE); if (o_ptr->to_d != j_ptr->to_d) return (FALSE); if (o_ptr->to_a != j_ptr->to_a) return (FALSE); /* Require identical "pval" code */ if (o_ptr->pval != j_ptr->pval) return (FALSE); /* Require identical "values" */ if (o_ptr->ac != j_ptr->ac) return (FALSE); if (o_ptr->dd != j_ptr->dd) return (FALSE); if (o_ptr->ds != j_ptr->ds) return (FALSE); /* Probably okay */ break; } default: { /* Various */ /* Require knowledge */ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (FALSE); /* Probably okay */ break; } } /* Hack -- Identical flags! */ if ((o_ptr->flags[0] != j_ptr->flags[0]) || (o_ptr->flags[1] != j_ptr->flags[1]) || (o_ptr->flags[2] != j_ptr->flags[2]) || (o_ptr->flags[3] != j_ptr->flags[3])) return (FALSE); /* Hack -- Require identical "cursed" status */ if (cursed_p(o_ptr) != cursed_p(j_ptr)) return (FALSE); /* Hack -- Require identical "broken" status */ if ((!o_ptr->cost) != (!j_ptr->cost)) return (FALSE); /* Need to be identical ego items or artifacts */ if (o_ptr->xtra_name != j_ptr->xtra_name) return (FALSE); /* Hack -- require semi-matching "inscriptions" */ if (o_ptr->inscription && j_ptr->inscription && (o_ptr->inscription != j_ptr->inscription)) return (FALSE); /* Require matching scripts */ for (i = 0; i < MAX_TRIGGER; i++) if (o_ptr->trigger[i] != j_ptr->trigger[i]) return (FALSE); /* Maximal "stacking" limit */ if (o_ptr->number + j_ptr->number >= MAX_STACK_SIZE) return (FALSE); /* They match, so they must be similar */ return (TRUE); } /* * Allow one item to "absorb" another, assuming they are similar */ void object_absorb(object_type *o_ptr, const object_type *j_ptr) { int total = o_ptr->number + j_ptr->number; /* Add together the item counts */ o_ptr->number = ((total < MAX_STACK_SIZE) ? total : (MAX_STACK_SIZE - 1)); /* Hack -- blend "known" status */ if (object_known_p(j_ptr)) object_known(o_ptr); /* If the extra object was fully known */ if (object_known_full(j_ptr)) { /* Then the stack is fully known too */ object_mental(o_ptr); } /* Hack -- blend "mental" status */ o_ptr->kn_flags[0] |= j_ptr->kn_flags[0]; o_ptr->kn_flags[1] |= j_ptr->kn_flags[1]; o_ptr->kn_flags[2] |= j_ptr->kn_flags[2]; o_ptr->kn_flags[3] |= j_ptr->kn_flags[3]; /* Hack -- blend "inscriptions" */ if (j_ptr->inscription) { quark_remove(&o_ptr->inscription); o_ptr->inscription = j_ptr->inscription; quark_dup(j_ptr->inscription); } /* Hack -- blend "feelings" */ if (j_ptr->feeling) o_ptr->feeling = j_ptr->feeling; /* Hack -- could average discounts XXX XXX XXX */ /* Hack -- save largest discount XXX XXX XXX */ if (o_ptr->discount < j_ptr->discount) o_ptr->discount = j_ptr->discount; /* * Hack -- if rods are stacking, add the pvals * (maximum timeouts) and current timeouts together. -LM- */ if (o_ptr->tval == TV_ROD) { o_ptr->pval += j_ptr->pval; o_ptr->timeout += j_ptr->timeout; } /* Hack -- if wands are stacking, combine the charges. -LM- */ if (o_ptr->tval == TV_WAND) { o_ptr->pval += j_ptr->pval; o_ptr->ac += j_ptr->ac; /* Hack XXX XXX - remove {empty} inscription. */ if (o_ptr->pval) o_ptr->info &= ~(OB_EMPTY); } } /* * Are these objects the same except for next_o_idx? Originally meant for * matching object in the inventory so there may be some redundancy here. */ bool object_equal(const object_type *o_ptr, const object_type *j_ptr) { int i; /* Require identical object types */ if (o_ptr->k_idx != j_ptr->k_idx) return (FALSE); /* Identical position */ if (o_ptr->ix != j_ptr->ix || o_ptr->iy != j_ptr->iy) return (FALSE); /* Require identical weights */ if (o_ptr->weight != j_ptr->weight) return (FALSE); /* Require identical tval, sval, pval */ if (o_ptr->tval != j_ptr->tval || o_ptr->sval != j_ptr->sval || o_ptr->pval != j_ptr->pval) return (FALSE); /* Require identical discounts */ if (o_ptr->discount != j_ptr->discount) return (FALSE); /* There is an equal number in the pile */ if (o_ptr->number != j_ptr->number) return (FALSE); /* Require identical to_h, to_d, to_a, ac */ if (o_ptr->to_h != j_ptr->to_h || o_ptr->to_d != j_ptr->to_d || o_ptr->to_a != j_ptr->to_a || o_ptr->ac != j_ptr->ac) return (FALSE); /* The timeout is the same */ if (o_ptr->timeout != j_ptr->timeout) return (FALSE); /* Identical dice */ if (o_ptr->dd != j_ptr->dd || o_ptr->ds != j_ptr->ds) return (FALSE); /* Hack -- require semi-matching "inscriptions" */ if (o_ptr->inscription && j_ptr->inscription && (o_ptr->inscription != j_ptr->inscription)) return (FALSE); /* Need to be identical ego items or artifacts */ if (o_ptr->xtra_name != j_ptr->xtra_name) return (FALSE); /* Identical flags! */ if ((o_ptr->flags[0] != j_ptr->flags[0]) || (o_ptr->flags[1] != j_ptr->flags[1]) || (o_ptr->flags[2] != j_ptr->flags[2]) || (o_ptr->flags[3] != j_ptr->flags[3])) return (FALSE); /* Identical kn_flags! */ if ((o_ptr->kn_flags[0] != j_ptr->kn_flags[0]) || (o_ptr->kn_flags[1] != j_ptr->kn_flags[1]) || (o_ptr->kn_flags[2] != j_ptr->kn_flags[2]) || (o_ptr->kn_flags[3] != j_ptr->kn_flags[3])) return (FALSE); /* Require identical "broken" status */ if ((!o_ptr->cost) != (!j_ptr->cost)) return (FALSE); /* The feeling is the same */ if (o_ptr->feeling != j_ptr->feeling) return (FALSE); /* The a_idx is the same */ if (o_ptr->a_idx != j_ptr->a_idx) return (FALSE); /* The info is the same */ if (o_ptr->info != j_ptr->info) return (FALSE); /* Require matching scripts */ for (i = 0; i < MAX_TRIGGER; i++) if (o_ptr->trigger[i] != j_ptr->trigger[i]) return (FALSE); /* The objects are the same */ return (TRUE); } /* * Find the index of the object_kind with the given tval and sval */ s16b lookup_kind(int tval, int sval) { int k; int num = 0; int bk = 0; /* Look for it */ for (k = 1; k < z_info->k_max; k++) { object_kind *k_ptr = &k_info[k]; /* Require correct tval */ if (k_ptr->tval != tval) continue; /* Found a match */ if (k_ptr->sval == sval) return (k); /* Ignore illegal items */ if (sval != SV_ANY) continue; /* Apply the randomizer */ if (!one_in_(++num)) continue; /* Use this value */ bk = k; } /* Return this choice */ if (sval == SV_ANY) { return bk; } /* Oops */ msgf("No object (%d,%d)", tval, sval); /* Oops */ return (0); } /* * Wipe an object clean. */ void object_wipe(object_type *o_ptr) { /* Delete the references */ delete_static_object(o_ptr); /* Wipe the structure */ (void)WIPE(o_ptr, object_type); } /* * Duplicate an object, and have the copy stored in the * standard static temp object. */ object_type *object_dup(const object_type *o_ptr) { int i; object_type *q_ptr = &temp_object; /* Delete old static object */ delete_static_object(q_ptr); /* Copy it */ object_copy(q_ptr, o_ptr); /* Allocate quarks */ quark_dup(o_ptr->xtra_name); quark_dup(o_ptr->inscription); for (i = 0; i < MAX_TRIGGER; i++) quark_dup(o_ptr->trigger[i]); /* Return a pointer to the static object */ return (q_ptr); } /* * Prepare an object based on an object kind. */ object_type *object_prep(int k_idx) { object_type *o_ptr = &temp_object; object_kind *k_ptr = &k_info[k_idx]; /* Clear the record */ object_wipe(o_ptr); /* Save the kind index */ o_ptr->k_idx = k_idx; /* Efficiency -- tval/sval */ o_ptr->tval = k_ptr->tval; o_ptr->sval = k_ptr->sval; /* Default "pval" */ o_ptr->pval = k_ptr->pval; /* Default number */ o_ptr->number = 1; /* Default weight */ o_ptr->weight = k_ptr->weight; /* Default magic */ o_ptr->to_h = k_ptr->to_h; o_ptr->to_d = k_ptr->to_d; o_ptr->to_a = k_ptr->to_a; /* Default power */ o_ptr->ac = k_ptr->ac; o_ptr->dd = k_ptr->dd; o_ptr->ds = k_ptr->ds; /* Set cost */ o_ptr->cost = k_ptr->cost; /* Save the flags */ o_ptr->flags[0] = k_ptr->flags[0]; o_ptr->flags[1] = k_ptr->flags[1]; o_ptr->flags[2] = k_ptr->flags[2]; o_ptr->flags[3] = k_ptr->flags[3]; #if 0 /* * Don't add the triggers - apply_object_trigger looks up the object * kind's trigger if the specific object has none */ for (i = 0; i < MAX_TRIGGER; i++) { if (k_ptr->trigger[i]) { o_ptr->trigger[i] = quark_add(k_text + k_ptr->trigger[i]); } } #endif return (o_ptr); } /* * Help determine an "enchantment bonus" for an object. * * To avoid floating point but still provide a smooth distribution of bonuses, * we simply round the results of division in such a way as to "average" the * correct floating point value. * * This function has been changed. It uses "Rand_mormal()" to choose values from * a normal distribution, whose mean moves from zero towards the max as the * level increases, and whose standard deviation is equal to 1/4 of the max, * and whose values are forced to lie between zero and the max, inclusive. * * Since the "level" rarely passes 100 before Morgoth is dead, it is very * rare to get the "full" enchantment on an object, even at deep levels. * * It is always possible (albeit unlikely) to get the "full" enchantment. * * A sample distribution of values from "m_bonus(10, N)" is shown below: * * N 0 1 2 3 4 5 6 7 8 9 10 * --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- * 0 66.37 13.01 9.73 5.47 2.89 1.31 0.72 0.26 0.12 0.09 0.03 * 8 46.85 24.66 12.13 8.13 4.20 2.30 1.05 0.36 0.19 0.08 0.05 * 16 30.12 27.62 18.52 10.52 6.34 3.52 1.95 0.90 0.31 0.15 0.05 * 24 22.44 15.62 30.14 12.92 8.55 5.30 2.39 1.63 0.62 0.28 0.11 * 32 16.23 11.43 23.01 22.31 11.19 7.18 4.46 2.13 1.20 0.45 0.41 * 40 10.76 8.91 12.80 29.51 16.00 9.69 5.90 3.43 1.47 0.88 0.65 * 48 7.28 6.81 10.51 18.27 27.57 11.76 7.85 4.99 2.80 1.22 0.94 * 56 4.41 4.73 8.52 11.96 24.94 19.78 11.06 7.18 3.68 1.96 1.78 * 64 2.81 3.07 5.65 9.17 13.01 31.57 13.70 9.30 6.04 3.04 2.64 * 72 1.87 1.99 3.68 7.15 10.56 20.24 25.78 12.17 7.52 4.42 4.62 * 80 1.02 1.23 2.78 4.75 8.37 12.04 27.61 18.07 10.28 6.52 7.33 * 88 0.70 0.57 1.56 3.12 6.34 10.06 15.76 30.46 12.58 8.47 10.38 * 96 0.27 0.60 1.25 2.28 4.30 7.60 10.77 22.52 22.51 11.37 16.53 * 104 0.22 0.42 0.77 1.36 2.62 5.33 8.93 13.05 29.54 15.23 22.53 * 112 0.15 0.20 0.56 0.87 2.00 3.83 6.86 10.06 17.89 27.31 30.27 * 120 0.03 0.11 0.31 0.46 1.31 2.48 4.60 7.78 11.67 25.53 45.72 * 128 0.02 0.01 0.13 0.33 0.83 1.41 3.24 6.17 9.57 14.22 64.07 */ s16b m_bonus(int max, int level) { int bonus, stand, extra, value; /* Paranoia -- enforce maximal "level" */ if (level > MAX_DEPTH - 1) level = MAX_DEPTH - 1; /* The "bonus" moves towards the max */ bonus = ((max * level) / MAX_DEPTH); /* Hack -- determine fraction of error */ extra = ((max * level) % MAX_DEPTH); /* Hack -- simulate floating point computations */ if (randint0(MAX_DEPTH) < extra) bonus++; /* The "stand" is equal to one quarter of the max */ stand = (max / 4); /* Hack -- determine fraction of error */ extra = (max % 4); /* Hack -- simulate floating point computations */ if (randint0(4) < extra) stand++; /* Choose an "interesting" value */ value = Rand_normal(bonus, stand); /* Enforce the minimum value */ if (value < 0) return (0); /* Enforce the maximum value */ if (value > max) return (max); /* Result */ return (value); } /* * Weighted bonus * * This function creates a "wieghted bonus" that * depends on the difference between the "normal" * level of an object, and the current level. * [O] based combat makes the extra plusses not * quite as important as in the old combat system. * * A 4d4 weapon with negative multipliers often is * much better than a 1d4 weapon with high positive * modifiers. * * This function attempts to balance that effect by * making things that are normally "junk" more powerful, * and things that are too powerful, weak. */ static s16b w_bonus(int max, int lev_dif) { /* Paranoia - max must be greater than 5 */ if (max < 6) return (0); /* Level difference is too small? */ if (ABS(lev_dif) < 10) return (0); if (lev_dif < 0) { /* Negative bonus */ return (-m_bonus(max - 5, lev_dif)); } else { /* Positive bonus */ return (randint1(5) + m_bonus(max - 5, lev_dif * 3)); } } /* * Cheat -- describe a created object for the user */ static void object_mention(object_type *o_ptr) { cptr type; /* Artifact */ if (FLAG(o_ptr, TR_INSTA_ART)) { if (o_ptr->a_idx) { /* Silly message */ type = "Artifact ("; } else { /* Silly message */ type = "Random artifact ("; } } /* Ego-item */ else if (ego_item_p(o_ptr)) { /* Silly message */ type = "Ego-item ("; } /* Normal item */ else { /* Silly message */ type = "Object ("; } msgf("%s%v)", type, OBJECT_STORE_FMT(o_ptr, FALSE, 0)); } /* Select ego items for the required slot */ static bool get_ego_prep(byte slot, bool good) { int i; ego_item_type *e_ptr; bool match = FALSE; alloc_entry *table = alloc_ego_table; /* Scan the allocation table */ for (i = 0; i < alloc_ego_size; i++) { /* Get pointer to ego item type */ e_ptr = &e_info[table[i].index]; /* Keep matching items */ if ((e_ptr->slot == slot) && ((good && e_ptr->rating) || (!good && !e_ptr->rating))) { /* Accept this ego item */ table[i].prob2 = table[i].prob1; /* There is a matching type */ match = TRUE; } else { /* Reject this ego item */ table[i].prob2 = 0; } } /* Was there a matching item? */ return (match); } /* * Get an ego item appropriate to a level. * * The "standard" method of using an allocation table is used to * make the selection. */ static byte get_ego_num(int level) { int i; long total, value; alloc_entry *table = alloc_ego_table; /* Boost the level from time to time */ if (one_in_(EGO_INFLATE)) { /* What a bizzare calculation */ level = 1 + (level * MAX_DEPTH / randint1(MAX_DEPTH)); } total = 0L; /* Process the probabilities */ for (i = 0; i < alloc_ego_size; i++) { /* Ego items are _not_ sorted by depth (yet) */ if (table[i].level <= level) { total += table[i].prob2; } } /* No legal ego items? */ if (total <= 0L) return (0); /* Pick an ego item */ value = randint1(total); /* Find the ego item */ for (i = 0; i < alloc_ego_size; i++) { if (table[i].level <= level) { value -= table[i].prob2; /* Match? */ if (value <= 0L) break; } } /* Result */ return ((byte)(table[i].index)); } static void init_ego_item(object_type *o_ptr, byte ego) { ego_item_type *e_ptr = &e_info[ego]; /* Hack -- apply extra penalties if needed */ if (cursed_p(o_ptr) || !o_ptr->cost) { /* Hack -- obtain bonuses */ if (e_ptr->max_to_h) o_ptr->to_h -= randint1(abs(e_ptr->max_to_h)); if (e_ptr->max_to_d) o_ptr->to_d -= randint1(abs(e_ptr->max_to_d)); if (e_ptr->max_to_a) o_ptr->to_a -= randint1(abs(e_ptr->max_to_a)); /* Hack -- obtain pval */ if (e_ptr->max_pval) o_ptr->pval -= randint1(abs(e_ptr->max_pval)); } /* Hack -- apply extra bonuses if needed */ else { /* Hack -- obtain bonuses */ if (e_ptr->max_to_h > 0) o_ptr->to_h += randint1(e_ptr->max_to_h); else if (e_ptr->max_to_h < 0) o_ptr->to_h -= randint1(-e_ptr->max_to_h); if (e_ptr->max_to_d > 0) o_ptr->to_d += randint1(e_ptr->max_to_d); else if (e_ptr->max_to_d < 0) o_ptr->to_d -= randint1(-e_ptr->max_to_d); if (e_ptr->max_to_a > 0) o_ptr->to_a += randint1(e_ptr->max_to_a); else if (e_ptr->max_to_a < 0) o_ptr->to_a -= randint1(-e_ptr->max_to_a); /* Hack -- obtain pval */ if ((e_ptr->max_pval) && ((!o_ptr->pval) || k_info[o_ptr->k_idx].pval)) { /* * Add the ego pval only if object has no pval, or normally * has a pval - in which case the bonus should be added. * (Eg with diggers) */ if (e_ptr->max_pval > 0) o_ptr->pval += randint1(e_ptr->max_pval); else o_ptr->pval -= randint1(-e_ptr->max_pval); } } /* Hack -- apply rating bonus */ inc_rating(e_ptr->rating); /* Cheat -- describe the item */ if (cheat_peek) object_mention(o_ptr); } /* * Turn an item into an ego item */ void add_ego_flags(object_type *o_ptr, byte ego) { int i; ego_item_type *e_ptr = &e_info[ego]; /* Set the flags */ o_ptr->flags[0] |= e_ptr->flags[0]; o_ptr->flags[1] |= e_ptr->flags[1]; o_ptr->flags[2] |= e_ptr->flags[2]; o_ptr->flags[3] |= e_ptr->flags[3]; /* Save all the known ego flags */ o_ptr->kn_flags[0] = e_ptr->flags[0]; o_ptr->kn_flags[1] = e_ptr->flags[1]; o_ptr->kn_flags[2] = e_ptr->flags[2]; o_ptr->kn_flags[3] = e_ptr->flags[3]; /* Save the inscription */ o_ptr->xtra_name = quark_add(e_name + e_ptr->name); /* Add any special scripts */ for (i = 0; i < MAX_TRIGGER; i++) { if (e_ptr->trigger[i]) o_ptr->trigger[i] = quark_add(e_text + e_ptr->trigger[i]); } /* Add in cost of ego item */ o_ptr->cost = k_info[o_ptr->k_idx].cost + e_ptr->cost; /* Lose information */ o_ptr->info &= ~(OB_MENTAL | OB_KNOWN); } /* * Mega-Hack -- Attempt to create an artifact. */ static object_type *make_artifact(void) { int i; int k_idx = 0; object_type *o_ptr; /* Check the artifact list */ for (i = 1; i < z_info->a_max; i++) { artifact_type *a_ptr = &a_info[i]; /* Skip "empty" artifacts */ if (!a_ptr->name) continue; /* Cannot make an artifact twice */ if (a_ptr->cur_num) continue; /* No quest items */ if (FLAG(a_ptr, TR_QUESTITEM)) continue; /* XXX XXX Enforce minimum "depth" (loosely) */ if (a_ptr->level > p_ptr->depth) { /* Acquire the "out-of-depth factor" */ int d = (a_ptr->level - p_ptr->depth) * 2; /* Roll for out-of-depth creation */ if (!one_in_(d)) continue; } /* Artifact "rarity roll" */ if (!one_in_(a_ptr->rarity)) continue; /* Find the base object */ k_idx = lookup_kind(a_ptr->tval, a_ptr->sval); /* XXX XXX Enforce minimum "object" level (loosely) */ if (!(FLAG(&k_info[k_idx], TR_INSTA_ART)) && k_info[k_idx].level > base_level()) { /* Acquire the "out-of-depth factor" */ int d = (k_info[k_idx].level - base_level()) * 5; /* Roll for out-of-depth creation */ if (!one_in_(d)) continue; } /* Assign the template */ o_ptr = object_prep(k_idx); /* Save the artifact flags */ o_ptr->flags[0] |= a_ptr->flags[0]; o_ptr->flags[1] |= a_ptr->flags[1]; o_ptr->flags[2] |= a_ptr->flags[2]; o_ptr->flags[3] |= a_ptr->flags[3]; /* Set the fields */ o_ptr->pval = a_ptr->pval; o_ptr->ac = a_ptr->ac; o_ptr->dd = a_ptr->dd; o_ptr->ds = a_ptr->ds; o_ptr->to_a = a_ptr->to_a; o_ptr->to_h = a_ptr->to_h; o_ptr->to_d = a_ptr->to_d; o_ptr->weight = a_ptr->weight; /* Mega-Hack XXX XXX -- set activation */ o_ptr->a_idx = i; /* Add any special scripts */ for (i = 0; i < MAX_TRIGGER; i++) { if (a_ptr->trigger[i]) o_ptr->trigger[i] = quark_add(a_text + a_ptr->trigger[i]); } /* Do not make another one */ a_ptr->cur_num = 1; /* Save the inscription */ o_ptr->xtra_name = quark_add(a_name + a_ptr->name); /* Apply special scripts */ apply_object_trigger(TRIGGER_MAKE, o_ptr, "i", "lev", a_ptr->level); /* Hack - increase the level rating */ inc_rating(30); if (!a_ptr->cost) { /* Hack -- "worthless" artifacts */ o_ptr->cost = 0L; } else { /* Hack - use the artifact price */ o_ptr->cost = k_info[o_ptr->k_idx].cost + a_ptr->cost; } /* Cheat -- peek at the item */ if (cheat_peek) object_mention(o_ptr); /* Set the good item flag */ set_special(); /* Success */ return (o_ptr); } /* Failure */ return (NULL); } /* * Mega-Hack -- Attempt to create a "special" randart. */ static object_type *make_special_randart(void) { int i; int k_idx = 0; object_type *o_ptr; /* Check the artifact list */ for (i = 1; i <= MAX_ART_SPECIAL; i++) { int lev; artifact_type *a_ptr = &a_info[i]; /* Skip "empty" artifacts */ if (!a_ptr->name) continue; /* Find the base object */ k_idx = lookup_kind(a_ptr->tval, a_ptr->sval); /* Skip "forbidden" artifacts */ if (!k_info[k_idx].extra) continue; /* Enforce minimum "object" level (loosely) */ if (k_info[k_idx].level > base_level()) { /* Acquire the "out-of-depth factor" */ int d = (k_info[k_idx].level - base_level()) * 2; /* Roll for out-of-depth creation */ if (!one_in_(d)) continue; } /* Enforce "object rarity" */ if (!one_in_(k_info[k_idx].extra)) continue; /* Assign the template */ o_ptr = object_prep(k_idx); /* Use the object kind's depth */ lev = k_info[k_idx].level; /* Create using the object kind's depth */ create_artifact(o_ptr, lev, FALSE); /* Run special scripts */ apply_object_trigger(TRIGGER_MAKE, o_ptr, "i", LUA_VAR(lev)); return (o_ptr); } return (NULL); } /* * Apply magic to an item known to be a "weapon" * * Hack -- note special base damage dice boosting * Hack -- note special processing for weapon/digger * Hack -- note special rating boost for dragon scale mail */ static void a_m_aux_1(object_type *o_ptr, int level, int lev_dif, byte flags) { int tohit1 = w_bonus(10, lev_dif); int todam1 = w_bonus(10, lev_dif); int tohit2 = m_bonus(10, level); int todam2 = m_bonus(10, level); byte ego = 0; /* Enchant */ o_ptr->to_h += tohit1; o_ptr->to_d += todam1; /* Good */ if (flags & OC_FORCE_GOOD) { /* Enchant again */ o_ptr->to_h += tohit2; o_ptr->to_d += todam2; } /* Bad */ else if (flags & OC_FORCE_BAD) { /* Penalize again */ o_ptr->to_h -= tohit2 * 2; o_ptr->to_d -= todam2 * 2; } /* Analyze type */ switch (o_ptr->tval) { case TV_DIGGING: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for ego item */ if (get_ego_prep(ES_DIG, TRUE)) { ego = get_ego_num(level); } } /* Very bad */ else if (flags & OC_FORCE_BAD) { /* Hack -- Horrible digging bonus */ o_ptr->pval = 0 - (s16b)(rand_range(2, 7)); } break; } case TV_HAFTED: case TV_POLEARM: case TV_SWORD: { /* Elfblades are always special */ if (o_ptr->sval == SV_ELFBLADE) { char new_name[1024]; (void)create_artifact(o_ptr, level, FALSE); /* Hack - always use name made of random syllables */ quark_remove(&o_ptr->xtra_name); get_table_name(new_name, TRUE); o_ptr->xtra_name = quark_add(new_name); break; } /* Very Good */ else if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(40)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for an ego-item */ if (get_ego_prep(ES_WIELD, TRUE)) { ego = get_ego_num(level); } /* Only sharp weapons can have sharpness */ if (ego == EGO_SHARPNESS && o_ptr->tval != TV_SWORD) ego = 0; /* Only hafted weapons can have earthquakes */ if (ego == EGO_EARTHQUAKES && o_ptr->tval != TV_HAFTED) ego = 0; /* Hack -- Super-charge the damage dice */ if (ego) { if (one_in_(10L * o_ptr->dd * o_ptr->ds)) { o_ptr->ds += (o_ptr->ds * randint1(5)) / 5; } } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for an ego-item */ if (get_ego_prep(ES_WIELD, FALSE)) { ego = get_ego_num(level); /* Extra powers */ if (ego == EGO_MORGUL) { SET_FLAG(o_ptr, TR_TY_CURSE); } } } break; } case TV_BOW: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(21)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_BOW, TRUE)) { ego = get_ego_num(level); } } break; } case TV_BOLT: case TV_ARROW: case TV_SHOT: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for ego-item */ if (get_ego_prep(ES_AMMO, TRUE)) { ego = get_ego_num(level); } /* Hack -- super-charge the damage dice */ if (one_in_(10L * o_ptr->dd * o_ptr->ds)) { o_ptr->ds += (o_ptr->ds * randint1(5)) / 5; } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for ego-item */ if (get_ego_prep(ES_AMMO, FALSE)) { ego = get_ego_num(level); } } break; } } /* Add ego item powers */ if (ego) { add_ego_flags(o_ptr, ego); init_ego_item(o_ptr, ego); } /* Cursed some of the time (only wearable items) */ if ((randint0(100) < 15) && (flags & OC_NORMAL)) { SET_FLAG(o_ptr, TR_CURSED); } /* Run any special scripts */ apply_object_trigger(TRIGGER_MAKE, o_ptr, "i", LUA_VAR(level)); } static void dragon_resist(object_type *o_ptr) { do { add_ego_power(EGO_XTRA_ANY_RESIST, o_ptr); } while (one_in_(2)); } /* * Apply magic to an item known to be "armor" * * Hack -- note special processing for crown/helm * Hack -- note special processing for robe of permanence */ static void a_m_aux_2(object_type *o_ptr, int level, int lev_dif, byte flags) { int toac1 = w_bonus(10, lev_dif); int toac2 = m_bonus(10, level); byte ego = 0; /* Hack - some items have an increased chance of being great */ if ((((o_ptr->tval == TV_HELM) && (o_ptr->sval == SV_DRAGON_HELM)) || ((o_ptr->tval == TV_SHIELD) && (o_ptr->sval == SV_DRAGON_SHIELD)) || ((o_ptr->tval == TV_CLOAK) && (o_ptr->sval == SV_ELVEN_CLOAK))) && lev_dif > 30) { /* Not cursed */ flags |= OC_FORCE_GOOD; flags &= ~OC_FORCE_BAD; } /* Enchant */ o_ptr->to_a += toac1; /* Good */ if (flags & OC_FORCE_GOOD) { /* Enchant again */ o_ptr->to_a += toac2; } /* Bad */ else if (flags & OC_FORCE_BAD) { /* Penalize again */ o_ptr->to_a -= toac2 * 2; } /* Analyze type */ switch (o_ptr->tval) { case TV_DRAG_ARMOR: { /* Rating boost */ inc_rating(30); /* Mention the item */ if (cheat_peek) object_mention(o_ptr); break; } case TV_HARD_ARMOR: case TV_SOFT_ARMOR: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(21)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for an ego item */ if (get_ego_prep(ES_BODY, TRUE)) { ego = get_ego_num(level); } /* Only robes can have permanence */ if (ego == EGO_PERMANENCE && (o_ptr->tval != TV_SOFT_ARMOR || o_ptr->sval != SV_ROBE)) ego = 0; } break; } case TV_SHIELD: { /* Dragon shields are already good */ if (o_ptr->sval == SV_DRAGON_SHIELD) { /* Rating boost */ inc_rating(5); /* Mention the item */ if (cheat_peek) object_mention(o_ptr); dragon_resist(o_ptr); } else { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for random artifact */ if (one_in_(21)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_ARM, TRUE)) { ego = get_ego_num(level); } } } break; } case TV_GLOVES: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(20)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_HANDS, TRUE)) { ego = get_ego_num(level); } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for ego-item */ if (get_ego_prep(ES_HANDS, FALSE)) { ego = get_ego_num(level); } } break; } case TV_BOOTS: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(20)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_FEET, TRUE)) { ego = get_ego_num(level); } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for ego-item */ if (get_ego_prep(ES_FEET, FALSE)) { ego = get_ego_num(level); } } break; } case TV_CROWN: { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(20)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_CROWN, TRUE)) { ego = get_ego_num(level); } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for ego-item */ if (get_ego_prep(ES_HEAD, FALSE)) { ego = get_ego_num(level); } } break; } case TV_HELM: { if (o_ptr->sval == SV_DRAGON_HELM) { /* Rating boost */ inc_rating(5); /* Mention the item */ if (cheat_peek) object_mention(o_ptr); dragon_resist(o_ptr); } else { /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifacts */ if (one_in_(20)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_HEAD, TRUE)) { ego = get_ego_num(level); } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for ego-item */ if (get_ego_prep(ES_HEAD, FALSE)) { ego = get_ego_num(level); } } } break; } case TV_CLOAK: { if (o_ptr->sval == SV_ELVEN_CLOAK) { /* No cursed elven cloaks */ o_ptr->pval = randint1(4); } /* Very good */ if (flags & OC_FORCE_GOOD) { /* Roll for a random artifact */ if (one_in_(20)) { (void)create_artifact(o_ptr, level, FALSE); break; } /* Roll for ego-item */ if (get_ego_prep(ES_OUTER, TRUE)) { ego = get_ego_num(level); } } /* Very cursed */ else if (flags & OC_FORCE_BAD) { /* Roll for ego-item */ if (get_ego_prep(ES_OUTER, TRUE)) { ego = get_ego_num(level); } } break; } } /* Add ego item powers */ if (ego) { add_ego_flags(o_ptr, ego); init_ego_item(o_ptr, ego); } /* Cursed some of the time */ if ((randint0(100) < 15) && (flags & OC_NORMAL)) { SET_FLAG(o_ptr, TR_CURSED); } /* Run any special scripts */ apply_object_trigger(TRIGGER_MAKE, o_ptr, "i", LUA_VAR(level)); } /* * Apply magic to an item known to be a "ring" or "amulet" */ static void a_m_aux_3(object_type *o_ptr, int level, byte flags) { bool allow_curse = FALSE; int rating_boost = 0; if (!(flags & OC_FORCE_GOOD) && one_in_(3)) { /* Sometimes, the stuff can be bad */ flags |= OC_FORCE_BAD; } /* Apply magic according to type */ apply_object_trigger(TRIGGER_MAKE, o_ptr, "i:bi", LUA_VAR(level), LUA_RETURN(allow_curse), LUA_RETURN(rating_boost)); /* Cursed? */ if (allow_curse && (flags & OC_FORCE_BAD)) { /* Broken */ o_ptr->cost = 0; /* Cursed */ SET_FLAG(o_ptr, TR_CURSED); /* Reverse bonuses */ o_ptr->pval = 0 - o_ptr->pval; o_ptr->to_a = 0 - o_ptr->to_a; o_ptr->to_h = 0 - o_ptr->to_h; o_ptr->to_d = 0 - o_ptr->to_d; } else if (rating_boost) { /* Boost the rating */ inc_rating(rating_boost); /* Mention the item */ if (cheat_peek && rating_boost >= 10) object_mention(o_ptr); } } /* * Apply magic to an item known to be "boring" * * Hack -- note the special code for various items */ static void a_m_aux_4(object_type *o_ptr, int level, byte flags) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; byte ego = 0; /* Apply magic (good or bad) according to type */ switch (o_ptr->tval) { case TV_LITE: { /* Hack -- Torches -- random fuel */ if (o_ptr->sval == SV_LITE_TORCH) { if (o_ptr->pval > 0) o_ptr->timeout = randint1(o_ptr->pval); } /* Hack -- Lanterns -- random fuel */ if (o_ptr->sval == SV_LITE_LANTERN) { if (o_ptr->pval > 0) o_ptr->timeout = randint1(o_ptr->pval); } /* Hack - remove pval */ o_ptr->pval = 0; if (flags & OC_FORCE_GOOD) { /* Roll for a random ego */ if (get_ego_prep(ES_LITE, TRUE)) { ego = get_ego_num(level); /* Initialise the ego item, if one is picked */ if (ego) { add_ego_flags(o_ptr, ego); init_ego_item(o_ptr, ego); } } } break; } case TV_WAND: { /* * The wand or staff gets a number of initial charges equal * to between 1/2 (+1) and the full object kind's pval. -LM- */ o_ptr->pval = k_ptr->pval / 2 + randint1((k_ptr->pval + 1) / 2); /* The number of "used" charges starts out as zero */ o_ptr->ac = 0; break; } case TV_STAFF: { /* * The wand or staff gets a number of initial charges equal * to between 1/2 (+1) and the full object kind's pval. -LM- */ o_ptr->pval = k_ptr->pval / 2 + randint1((k_ptr->pval + 1) / 2); break; } case TV_ROD: { /* Transfer the pval. -LM- */ o_ptr->pval = k_ptr->pval; break; } case TV_FIGURINE: { int i = 1; monster_race *r_ptr; /* Pick a random non-unique monster race */ while (1) { i = randint1(z_info->r_max - 1); r_ptr = &r_info[i]; /* Prefer less out-of-depth monsters */ if ((level < r_ptr->level) && !one_in_(r_ptr->level - level)) continue; /* Ignore dead monsters */ if (!r_ptr->rarity) continue; /* No uniques */ if (FLAG(r_ptr, RF_UNIQUE)) continue; break; } o_ptr->pval = i; if (cheat_peek) { msgf("Figurine of %s", mon_race_name(r_ptr)); } break; } case TV_STATUE: { int i = 1; monster_race *r_ptr; /* Pick a random monster race */ while (1) { i = randint1(z_info->r_max - 1); r_ptr = &r_info[i]; /* Ignore dead monsters */ if (!r_ptr->rarity) continue; break; } o_ptr->pval = i; if (cheat_peek) { msgf("Statue of %s", mon_race_name(r_ptr)); } break; } case TV_CHEST: { byte obj_level = get_object_level(o_ptr); /* Hack -- skip ruined chests */ if (obj_level <= 0) break; /* Hack -- pick a "difficulty" */ o_ptr->pval = randint1(obj_level); /* Never exceed "difficulty" of 55 to 59 */ if (o_ptr->pval > 55) o_ptr->pval = (byte)rand_range(55, 60); break; } } /* Run any special scripts */ apply_object_trigger(TRIGGER_MAKE, o_ptr, "i", LUA_VAR(level)); } void add_ego_power(int power, object_type *o_ptr) { if (power == EGO_XTRA_ANY_RESIST) { if (one_in_(4)) power = EGO_XTRA_LO_RESIST; else power = EGO_XTRA_HI_RESIST; } if (power == EGO_XTRA_POWER) { if (one_in_(2)) power = EGO_XTRA_ABILITY; else power = EGO_XTRA_HI_RESIST; } switch (power) { case EGO_XTRA_ABILITY: { /* Choose an ability */ switch (randint0(8)) { case 0: { (o_ptr->flags[2]) |= (TR2_FEATHER); break; } case 1: { (o_ptr->flags[2]) |= (TR2_LITE); break; } case 2: { (o_ptr->flags[2]) |= (TR2_SEE_INVIS); break; } case 3: { (o_ptr->flags[2]) |= (TR2_TELEPATHY); break; } case 4: { (o_ptr->flags[2]) |= (TR2_SLOW_DIGEST); break; } case 5: { (o_ptr->flags[2]) |= (TR2_REGEN); break; } case 6: { (o_ptr->flags[1]) |= (TR1_FREE_ACT); break; } case 7: { (o_ptr->flags[1]) |= (TR1_HOLD_LIFE); break; } } break; } case EGO_XTRA_SUSTAIN: { /* Choose a sustain */ switch (randint0(6)) { case 0: { (o_ptr->flags[1]) |= (TR1_SUST_STR); break; } case 1: { (o_ptr->flags[1]) |= (TR1_SUST_INT); break; } case 2: { (o_ptr->flags[1]) |= (TR1_SUST_WIS); break; } case 3: { (o_ptr->flags[1]) |= (TR1_SUST_DEX); break; } case 4: { (o_ptr->flags[1]) |= (TR1_SUST_CON); break; } case 5: { (o_ptr->flags[1]) |= (TR1_SUST_CHR); break; } } break; } case EGO_XTRA_LO_RESIST: { /* Choose a low resistance */ switch (randint0(4)) { case 0: { (o_ptr->flags[1]) |= (TR1_RES_ACID); break; } case 1: { (o_ptr->flags[1]) |= (TR1_RES_ELEC); break; } case 2: { (o_ptr->flags[1]) |= (TR1_RES_FIRE); break; } case 3: { (o_ptr->flags[1]) |= (TR1_RES_COLD); break; } } break; } case EGO_XTRA_HI_RESIST: { /* Choose a power */ switch (randint0(12)) { case 0: { (o_ptr->flags[1]) |= (TR1_RES_BLIND); break; } case 1: { (o_ptr->flags[1]) |= (TR1_RES_CONF); break; } case 2: { (o_ptr->flags[1]) |= (TR1_RES_SOUND); break; } case 3: { (o_ptr->flags[1]) |= (TR1_RES_SHARDS); break; } case 4: { (o_ptr->flags[1]) |= (TR1_RES_NETHER); break; } case 5: { (o_ptr->flags[1]) |= (TR1_RES_NEXUS); break; } case 6: { (o_ptr->flags[1]) |= (TR1_RES_CHAOS); break; } case 7: { (o_ptr->flags[1]) |= (TR1_RES_DISEN); break; } case 8: { (o_ptr->flags[1]) |= (TR1_RES_POIS); break; } case 9: { (o_ptr->flags[1]) |= (TR1_RES_DARK); break; } case 10: { (o_ptr->flags[1]) |= (TR1_RES_LITE); break; } case 11: { (o_ptr->flags[1]) |= (TR1_RES_FEAR); break; } } break; } } } /* * Complete the "creation" of an object by applying "magic" to the item * * This includes not only rolling for random bonuses, but also putting the * finishing touches on ego-items and artifacts, giving charges to wands and * staffs, giving fuel to lites, and placing traps on chests. * * The base "chance" of the item being "good" increases with the "level" * parameter, which is usually derived from the dungeon level, being equal * to the level plus 10, up to a maximum of 75. If "good" is true, then * the object is guaranteed to be "good". If an object is "good", then * the chance that the object will be "great" (ego-item or artifact), also * increases with the "level", being equal to half the level, plus 5, up to * a maximum of 20. If "great" is true, then the object is guaranteed to be * "great". At dungeon level 65 and below, 15/100 objects are "great". * * If the object is not "good", there is a chance it will be "cursed", and * if it is "cursed", there is a chance it will be "broken". These chances * are related to the "good" / "great" chances above. * * Otherwise "normal" rings and amulets will be "good" half the time and * "cursed" half the time, unless the ring/amulet is always good or cursed. * * If "okay" is true, and the object is going to be "great", then there is * a chance that an artifact will be created. This is true even if both the * "good" and "great" arguments are false. As a total hack, if "great" is * true, then the item gets 3 extra "attempts" to become an artifact. */ void apply_magic(object_type *o_ptr, int lev, int lev_dif, byte flags) { int f; /* Maximum "level" for various things */ if (lev > MAX_DEPTH - 1) lev = MAX_DEPTH - 1; /* Base chance of being "good" */ f = (lev * 3) / 5 + 10; /* Maximal chance of being "good" */ if (f > 42) f = 42; if (FLAG(p_ptr, TR_STRANGE_LUCK)) f = f * 3 / 2; /* Lights are more likely to be ego-items */ if (o_ptr->tval == TV_LITE) f *= 2; /* Roll for ego items */ if ((flags & OC_NORMAL) && (randint0(100) < f)) { if (randint0(100) < f) flags |= OC_FORCE_GOOD; else if (randint0(100) < f) flags |= OC_FORCE_BAD; } if (FLAG(o_ptr, TR_INSTA_ART)) { /* Paranoia - we have an artifact!!! */ msgf("Error Condition - artifact passed to apply_magic"); msgf("Object sval:%d Object flags3:%d", o_ptr->sval, o_ptr->flags[2]); msgf("Submit a bugreport please. :-)"); return; } /* Apply magic */ switch (o_ptr->tval) { case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOW: case TV_SHOT: case TV_ARROW: case TV_BOLT: { a_m_aux_1(o_ptr, lev, lev_dif, flags); break; } case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: case TV_SHIELD: case TV_HELM: case TV_CROWN: case TV_CLOAK: case TV_GLOVES: case TV_BOOTS: { a_m_aux_2(o_ptr, lev, lev_dif, flags); break; } case TV_RING: case TV_AMULET: { a_m_aux_3(o_ptr, lev, flags); break; } default: { a_m_aux_4(o_ptr, lev, flags); break; } } /* Change level feeling for random artifacts */ if (FLAG(o_ptr, TR_INSTA_ART)) inc_rating(30); /* Examine real objects */ if (o_ptr->k_idx) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* Mega Hack - reset cost if not a powerful item */ if (!o_ptr->xtra_name) { o_ptr->cost = k_ptr->cost; } } } /* The tval / sval pair to match */ static byte match_tv; static byte match_sv; void init_match_hook(byte tval, byte sval) { /* Save the tval/ sval pair to match */ match_tv = tval; match_sv = sval; } /* * Hack -- match certain types of object only. * * Return 0% or 100% of matching based on tval and sval. */ byte kind_is_match(int k_idx) { object_kind *k_ptr = &k_info[k_idx]; /* Does the tval match? */ if ((match_tv != TV_ANY) && (k_ptr->tval != match_tv)) return (0); /* Does the sval match? */ if ((match_sv == SV_ANY) || (k_ptr->sval == match_sv)) return (100); /* Not a match */ return (0); } /* The themed objects to use */ static obj_theme match_theme; void init_match_theme(obj_theme theme) { /* Save the theme */ match_theme = theme; } /* * Hack -- match certain types of object only. * * Return percentage probability of match. */ byte kind_is_theme(int k_idx) { object_kind *k_ptr = &k_info[k_idx]; /* Pick probability to use */ switch (k_ptr->tval) { case TV_SKELETON: case TV_BOTTLE: case TV_JUNK: { /* Degree of junk is defined in terms of the other 4 quantities */ return (100 - (match_theme.treasure + match_theme.combat + match_theme.magic + match_theme.tools)); } case TV_SPIKE: return (match_theme.tools); case TV_CHEST: return (match_theme.treasure); case TV_FIGURINE: return (match_theme.treasure); case TV_STATUE: return (match_theme.treasure); case TV_SHOT: return (match_theme.combat); case TV_ARROW: return (match_theme.combat); case TV_BOLT: return (match_theme.combat); case TV_BOW: return (match_theme.combat); case TV_DIGGING: return (match_theme.tools); case TV_HAFTED: return (match_theme.combat); case TV_POLEARM: return (match_theme.combat); case TV_SWORD: return (match_theme.combat); case TV_BOOTS: return (match_theme.combat); case TV_GLOVES: return (match_theme.combat); case TV_HELM: return (match_theme.combat); case TV_CROWN: return (match_theme.treasure); case TV_SHIELD: return (match_theme.combat); case TV_CLOAK: return (match_theme.combat); case TV_SOFT_ARMOR: return (match_theme.combat); case TV_HARD_ARMOR: return (match_theme.combat); case TV_DRAG_ARMOR: return (match_theme.treasure + match_theme.combat); case TV_LITE: return (match_theme.tools); case TV_AMULET: return (match_theme.treasure); case TV_RING: return (match_theme.treasure); case TV_STAFF: return (match_theme.magic); case TV_WAND: return (match_theme.magic); case TV_ROD: return (match_theme.magic); case TV_SCROLL: return (match_theme.magic); case TV_POTION: return (match_theme.magic); case TV_FLASK: return (match_theme.tools); case TV_FOOD: return (match_theme.tools); case TV_LIFE_BOOK: return (match_theme.magic); case TV_SORCERY_BOOK: return (match_theme.magic); case TV_NATURE_BOOK: return (match_theme.magic); case TV_CHAOS_BOOK: return (match_theme.magic); case TV_DEATH_BOOK: return (match_theme.magic); case TV_TRUMP_BOOK: return (match_theme.magic); case TV_ARCANE_BOOK: return (match_theme.magic); /* Paranoia */ default: return (0); } } /* * Attempt to make an object (normal or good/great) * * This routine plays nasty games to generate the "special artifacts". * * This routine uses "level" for the "generation level". * * We assume that the given object has been "wiped". */ object_type *make_object(int level, int delta_level, obj_theme *theme) { int prob, base, min_level; byte obj_level; byte flags; int k_idx = 0, count = 5; object_type *o_ptr; /* Chance of "special object" */ if (delta_level > 0) { prob = 800 / delta_level; /* bounds checking */ if (prob < 10) prob = 10; } else { /* No divide by zero */ prob = 800; } /* "Good Luck" mutation */ if ((p_ptr->muta3 & MUT3_GOOD_LUCK) && one_in_(13)) { /* The player is lucky - the item is better than normal */ delta_level += 20; } /* Renormalise the change in level */ delta_level = randint0(delta_level); /* Base level for the object */ base = level + delta_level; /* Paranoia - don't let this get too high */ if (base > 100) base = 100; /* Hack - Set flags based on delta_level */ if (delta_level > 15) { flags = OC_FORCE_GOOD; min_level = level + delta_level / 2; } else { flags = OC_NORMAL; min_level = 0; } /* Make an artifact */ if (one_in_(prob)) { /* Try for a fixed art */ o_ptr = make_artifact(); if (o_ptr) return (o_ptr); } if (one_in_(prob * 2)) { /* Try for a special randart */ o_ptr = make_special_randart(); if (o_ptr) return (o_ptr); } /* Default to themed objects? */ if (theme) { while (!k_idx && (count > 0)) { /* No infinite loops */ count--; /* Select items based on "theme" */ init_match_theme(*theme); /* Prepare allocation table */ get_obj_num_prep(kind_is_theme); /* Pick a random object */ k_idx = get_obj_num(base, min_level); /* Paranoia - try less hard to get something */ if (!k_idx) min_level /= 2; } } else { /* We already have a restriction */ /* Pick a random object using the current restriction */ k_idx = get_obj_num(base, 0); } /* Handle failure */ if (!k_idx) return (NULL); /* Prepare the object */ o_ptr = object_prep(k_idx); /* Apply magic (allow artifacts) */ apply_magic(o_ptr, base, base - k_info[k_idx].level, flags); /* Hack -- generate multiple spikes/missiles/ mushrooms */ switch (o_ptr->tval) { case TV_SPIKE: case TV_SHOT: case TV_ARROW: case TV_BOLT: { o_ptr->number = (byte)damroll(6, 7); break; } case TV_FOOD: { if (o_ptr->sval < SV_FOOD_BISCUIT) { /* mushrooms appear in clumps */ o_ptr->number = (byte)randint1(6); } break; } } obj_level = get_object_level(o_ptr); /* Notice "okay" out-of-depth objects */ if (!cursed_p(o_ptr) && o_ptr->cost && (obj_level > p_ptr->depth)) { /* Rating increase */ inc_rating(obj_level - p_ptr->depth); /* Cheat -- peek at items */ if (cheat_peek) object_mention(o_ptr); } /* Success */ return (o_ptr); } /* * Put an object on the ground. * We assume the grid is in bounds. */ static bool put_object(object_type *o_ptr, int x, int y) { /* Acquire grid */ cave_type *c_ptr = area(x, y); object_type *j_ptr; /* Require nice floor space */ if (!cave_nice_grid(c_ptr)) return (FALSE); /* Paranoia */ if (!o_ptr) return (FALSE); /* Add the object to the ground */ j_ptr = add_object_list(&c_ptr->o_idx, o_ptr); /* Success */ if (j_ptr) { /* Location */ j_ptr->iy = y; j_ptr->ix = x; /* Region */ j_ptr->region = cur_region; /* Notice + Redraw */ note_spot(x, y); /* Debug - scan player list for this item and complain if we find it */ look_up_list(j_ptr); return (TRUE); } /* Warn the player */ msgf("Failed to place object!"); /* Paranoia - preserve artifacts */ if ((preserve_mode) && FLAG(o_ptr, TR_INSTA_ART) && o_ptr->a_idx) { a_info[o_ptr->a_idx].cur_num = 0; } /* Failure */ return (FALSE); } /* * Put an object of the requested type on the location given. * * This is mostly used for creating items in quests. */ void place_specific_object(int x, int y, int level, int k_idx) { object_type *o_ptr; object_kind *k_ptr; int i; /* Paranoia */ if (!k_idx) return; k_ptr = &k_info[k_idx]; /* Instant artifacts are special */ if (FLAG(k_ptr, TR_INSTA_ART)) { /* Find the "special" artifact this object belongs to */ for (i = 1; i < z_info->a_max; i++) { artifact_type *a_ptr = &a_info[i]; /* Skip "empty" artifacts */ if (!a_ptr->name) continue; if ((a_ptr->tval == k_ptr->tval) && (a_ptr->sval == k_ptr->sval)) { /* Found it */ create_named_art(i, x, y); return; } } /* Exit */ return; } else { /* Create the item */ o_ptr = object_prep(k_idx); /* Apply magic */ apply_magic(o_ptr, level, 0, OC_NORMAL); /* Hack -- generate multiple spikes/missiles/ mushrooms */ switch (o_ptr->tval) { case TV_SPIKE: case TV_SHOT: case TV_ARROW: case TV_BOLT: { o_ptr->number = (byte)damroll(6, 7); break; } case TV_FOOD: { if (o_ptr->sval < SV_FOOD_BISCUIT) { /* Mushrooms appear in clumps */ o_ptr->number = (byte)randint1(6); } break; } } } /* Add the object to the ground */ drop_near(o_ptr, -1, x, y); } /* * Attempt to place an object (normal or good/great) at the given location. * * This routine plays nasty games to generate the "special artifacts". * * This routine uses "base_level()" + "delta_level" for the "generation level". * * This routine requires a clean floor grid destination. */ void place_object(int x, int y, bool good, bool great, int delta_level) { cave_type *c_ptr; object_type *o_ptr; place_type *pl_ptr = &place[p_ptr->place_num]; obj_theme *o_theme = &pl_ptr->dungeon->theme; /* Paranoia -- check bounds */ if (!in_bounds2(x, y)) return; /* Acquire grid */ c_ptr = area(x, y); /* Do not generate items on "nasty" terrain */ if ((c_ptr->feat == FEAT_SHAL_LAVA) || (c_ptr->feat == FEAT_SHAL_WATER) || (c_ptr->feat == FEAT_SHAL_ACID)) { return; } /* Make an object (if possible) */ o_ptr = make_object(base_level() + delta_level, (good ? 15 : 0) + (great ? 15 : 0), o_theme); /* Put it on the ground */ (void)put_object(o_ptr, x, y); } /* * Make a treasure object */ object_type *make_gold(int level, int coin_type) { s16b i; s32b base; object_type *o_ptr; if (coin_type) { /* Hack -- Creeping Coins only generate "themselves" */ i = coin_type; } else { /* Hack -- Pick a Treasure variety */ i = ((randint1(level + 2) + 2) / 2) - 1; /* Apply "extra" magic */ if (one_in_(GREAT_OBJ)) { i += randint1(level + 1); } } /* Do not create "illegal" Treasure Types */ if (i >= MAX_GOLD) i = MAX_GOLD - 1; /* Prepare a gold object */ o_ptr = object_prep(OBJ_GOLD_LIST + i); /* Hack -- Base coin cost */ base = k_info[OBJ_GOLD_LIST + i].cost; /* Determine how much the treasure is "worth" */ o_ptr->pval = (base + (8L * randint1(base)) + randint1(8)); /* return pointer to gold */ return (o_ptr); } /* * Places a treasure (Gold or Gems) at given location * * The location must be a legal, clean, floor grid. */ void place_gold(int x, int y) { object_type *o_ptr; /* Make some gold */ o_ptr = make_gold(base_level(), 0); /* Put it on the ground */ (void)put_object(o_ptr, x, y); } /* * Let an object fall to the ground at or near a location. * * The initial location is assumed to be "in_bounds2()". * * This function takes a parameter "chance". This is the percentage * chance that the item will "disappear" instead of drop. If the object * has been thrown, then this is the chance of disappearance on contact. * * Hack -- this function uses "chance" to determine if it should produce * some form of "description" of the drop event (under the player). * * We check several locations to see if we can find a location at which * the object can combine, stack, or be placed. Artifacts will try very * hard to be placed, including "teleporting" to a useful grid if needed. */ void drop_near(object_type *j_ptr, int chance, int x, int y) { int i, k, d, s; int bs, bn; int by, bx; int dy, dx; int ty, tx; cave_type *c_ptr; object_type *o_ptr = NULL; char o_name[256]; bool flag = FALSE; bool done = FALSE; bool plural = FALSE; /* Extract plural */ if (j_ptr->number != 1) plural = TRUE; /* Describe object */ object_desc(o_name, j_ptr, FALSE, 0, 256); /* Handle normal "breakage" */ if (!(FLAG(j_ptr, TR_INSTA_ART)) && (randint0(100) < chance)) { /* Message */ msgf("The %s disappear%s.", o_name, (plural ? "" : "s")); /* Debug */ if (p_ptr->state.wizard) msgf("(breakage)"); /* Failure */ return; } /* Score */ bs = -1; /* Picker */ bn = 0; /* Default */ by = y; bx = x; /* Scan local grids */ for (dy = -3; dy <= 3; dy++) { /* Scan local grids */ for (dx = -3; dx <= 3; dx++) { bool comb = FALSE; /* Calculate actual distance */ d = (dy * dy) + (dx * dx); /* Ignore distant grids */ if (d > 10) continue; /* Location */ ty = y + dy; tx = x + dx; /* Skip illegal grids */ if (!in_bounds2(tx, ty)) continue; /* Require line of sight */ if (!los(x, y, tx, ty)) continue; /* Obtain grid */ c_ptr = area(tx, ty); /* Require floor space */ if (!cave_nice_grid(c_ptr)) continue; /* Not on "nasty" terrains */ if ((c_ptr->feat == FEAT_SHAL_LAVA) || (c_ptr->feat == FEAT_SHAL_ACID) || (c_ptr->feat == FEAT_SHAL_WATER)) continue; /* Check to see if fields dissallow placement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_OBJCT)) { continue; } /* No objects */ k = 0; /* Scan objects in that grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Check for possible combination */ if (object_similar(o_ptr, j_ptr)) comb = TRUE; /* Count objects */ k++; } OBJ_ITT_END; /* Add new object */ if (!comb) k++; /* No stacking (allow combining) [Optional for Topi] */ if (!testing_stack && (k > 1)) continue; /* Paranoia */ if (k > 99) continue; /* Calculate score */ s = 1000 - (d + k * 5); /* Skip bad values */ if (s < bs) continue; /* New best value */ if (s > bs) bn = 0; /* Apply the randomizer to equivalent values */ if ((++bn >= 2) && !one_in_(bn)) continue; /* Keep score */ bs = s; /* Track it */ by = ty; bx = tx; /* Okay */ flag = TRUE; } } /* Handle lack of space */ if (!flag && !(FLAG(j_ptr, TR_INSTA_ART))) { /* Message */ msgf("The %s disappear%s.", o_name, (plural ? "" : "s")); /* Debug */ if (p_ptr->state.wizard) msgf("(no floor space)"); /* Failure */ return; } /* Find a grid */ for (i = 0; !flag; i++) { /* Bounce around */ if (i < 1000) { ty = rand_spread(by, 1); tx = rand_spread(bx, 1); /* Do some bound checking */ ty = MAX(ty, p_ptr->min_hgt); ty = MIN(ty, p_ptr->max_hgt - 1); tx = MAX(tx, p_ptr->min_wid); tx = MIN(tx, p_ptr->max_wid - 1); } /* Random locations */ else { /* Pick a location */ ty = rand_range(p_ptr->min_hgt, p_ptr->max_hgt - 1); tx = rand_range(p_ptr->min_wid, p_ptr->max_wid - 1); } /* Grid */ c_ptr = area(tx, ty); /* Check to see if fields dissallow placement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_OBJCT)) continue; /* Bounce to that location */ by = ty; bx = tx; /* Require floor space */ if (!cave_nice_grid(c_ptr)) continue; /* Not on "nasty" terrains */ if ((c_ptr->feat == FEAT_SHAL_LAVA) || (c_ptr->feat == FEAT_SHAL_ACID) || (c_ptr->feat == FEAT_SHAL_WATER)) continue; /* Okay */ flag = TRUE; } /* Grid */ c_ptr = area(bx, by); /* Hack - artifacts will not be affected by terrain */ if (!(FLAG(j_ptr, TR_INSTA_ART))) { /* Check to see if the object will burn on contact with lava. */ if ((c_ptr->feat == FEAT_SHAL_LAVA) && ((j_ptr->tval == TV_STAFF) || (j_ptr->tval == TV_SCROLL) || (j_ptr->tval == TV_WAND))) { /* only display messages if player throws */ if (!chance) { /* Message */ msgf("The %s%s burns in the lava.", o_name, (plural ? "" : "s")); } /* Debug */ if (p_ptr->state.wizard) msgf("(contact with lava)"); /* Failure */ return; } /* Check to see if the object will disappear in water. */ if ((c_ptr->feat == FEAT_SHAL_WATER) && (j_ptr->tval == TV_RING)) { /* only display messages if player throws */ if (!chance) { /* Message */ msgf("The %s disappear%s.", o_name, (plural ? "" : "s")); } /* Debug */ if (p_ptr->state.wizard) msgf("(contact with water)"); /* Failure */ return; } } /* Scan objects in that grid for combination */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Check for combination */ if (object_similar(o_ptr, j_ptr)) { /* Combine the items */ object_absorb(o_ptr, j_ptr); /* Success */ done = TRUE; /* Done */ break; } } OBJ_ITT_END; /* Get new object */ if (!done) { /* Put it on the ground */ if (!put_object(j_ptr, bx, by)) { /* Message */ msgf("The %s disappear%s.", o_name, (plural ? "" : "s")); /* Debug */ if (p_ptr->state.wizard) msgf("(too many objects)"); /* Failure */ return; } } /* Sound */ sound(SOUND_DROP); /* Mega-Hack -- no message if "dropped" by player */ /* Message when an object falls under the player */ if (chance && (by == p_ptr->py) && (bx == p_ptr->px)) { msgf("You feel something roll beneath your feet."); } /* Fields may interact with an object in some way */ field_script(area(bx, by), FIELD_ACT_OBJECT_DROP, "p", LUA_OBJECT(o_ptr)); } /* * Scatter some "great" objects near the player */ void acquirement(int x1, int y1, int num, bool great, bool known) { object_type *o_ptr = NULL; int i; obj_theme theme; /* Set theme - more weapons than normal */ theme.treasure = 10; theme.combat = 80; theme.magic = 10; theme.tools = 0; /* Acquirement */ while (num--) { /* We want a good object */ for (i = 0; i < 1000; i++) { if (great) { /* Make a great object (if possible) */ o_ptr = make_object(base_level(), 40, &theme); /* Paranoia */ if (!o_ptr) continue; } else { /* Make a good object (if possible) */ o_ptr = make_object(base_level(), 20, &theme); /* Paranoia */ if (!o_ptr) continue; } /* Skip cursed items */ if (cursed_p(o_ptr)) continue; /* Check to see if the object is worth anything */ if (object_value_real(o_ptr) > 0) break; } /* Paranoia */ if (i >= 1000) return; if (known) { object_aware(o_ptr); object_known(o_ptr); } /* Drop the object */ drop_near(o_ptr, -1, x1, y1); } } /* * Look up the list that corresponds to a given * object returned by the get_item() function. * * We know the item is in our equipment, our * inventory, or is on the floor underneith us. */ s16b *look_up_list(object_type *o_ptr) { object_type *j_ptr; cave_type *c_ptr; place_type *pl_ptr = &place[p_ptr->place_num]; int i; /* Some objects have no list */ if (!o_ptr->allocated) return (NULL); /* Scan player inventory */ OBJ_ITT_START (p_ptr->inventory, j_ptr) { if (o_ptr == j_ptr) return (&p_ptr->inventory); /* Debug - make sure we don't have a corrupted inventory */ if (j_ptr->ix || j_ptr->iy) quit("Corrupted inventory contains dungeon objects!"); /* Debug - test array bounds */ if (GET_ARRAY_INDEX(o_list, j_ptr) >= o_max) quit("Inven outside bounds!"); } OBJ_ITT_END; /* Scan dungeon */ if (o_ptr->ix || o_ptr->iy) { c_ptr = area(o_ptr->ix, o_ptr->iy); /* Scan square */ OBJ_ITT_START (c_ptr->o_idx, j_ptr) { if (o_ptr == j_ptr) return (&c_ptr->o_idx); } OBJ_ITT_END; } /* Scan stores */ for (i = 0; i < pl_ptr->numstores; i++) { /* Scan store stock */ OBJ_ITT_START (pl_ptr->store[i].stock, j_ptr) { if (o_ptr == j_ptr) return (&pl_ptr->store[i].stock); } OBJ_ITT_END; } /* Failure - the object is inconsistant */ quit("Failed to look up object."); return (NULL); } /* * Get the nth item in a list */ object_type *get_list_item(s16b list_start, int number) { object_type *o_ptr; /* Paranoia */ if (number < 0) return (NULL); OBJ_ITT_START (list_start, o_ptr) { if (!number) return (o_ptr); number--; } OBJ_ITT_END; /* We didn't find it */ return (NULL); } /* * The inverse function of get_list_item() * * Find the position in the list of a given item */ int get_item_position(s16b list_start, object_type *o_ptr) { int i = 0; object_type *j_ptr; OBJ_ITT_START (list_start, j_ptr) { if (j_ptr == o_ptr) return (i); i++; } OBJ_ITT_END; /* Failure */ return (-1); } /* * How many items are in the list? */ int get_list_length(s16b list_start) { int i = 0; object_type *j_ptr; OBJ_ITT_START (list_start, j_ptr) { /* Count items */ i++; } OBJ_ITT_END; return (i); } /* Is the item on the floor? */ bool floor_item(object_type *o_ptr) { s16b *o_list = look_up_list(o_ptr); cave_type *c_ptr = area(p_ptr->px, p_ptr->py); /* On floor? */ if (o_list == &c_ptr->o_idx) return (TRUE); /* Elsewhere */ return (FALSE); } /* Is the item in the players inventory or equipment? */ bool player_item(object_type *o_ptr) { s16b *o_list = look_up_list(o_ptr); /* Equipment? */ if (!o_list) return (TRUE); /* Inventory */ if (o_list == &p_ptr->inventory) return (TRUE); /* Elsewhere */ return (TRUE); } /* * Describe the charges on an item in the inventory. */ void item_charges(object_type *o_ptr) { /* Require staff/wand */ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return; /* Require known item */ if (!object_known_p(o_ptr)) return; /* Print a message */ msgf("%s %d charge%s remaining.", floor_item(o_ptr) ? "It has" : "You have", o_ptr->pval, (o_ptr->pval != 1) ? "s" : ""); } /* * Describe an item in the inventory. */ static cptr item_describe_aux(object_type *o_ptr, bool back_step) { char o_name[256]; char lab[40] = ""; int item; /* Get list */ s16b *list = look_up_list(o_ptr); cave_type *c_ptr = area(p_ptr->px, p_ptr->py); /* Get a description */ object_desc(o_name, o_ptr, TRUE, 3, 256); if (o_ptr->number <= 0) { if (!list) { /* Hack XXX XXX pretend there is one item */ int num = o_ptr->number; o_ptr->number = 1; /* Get a (new) description */ object_desc(o_name, o_ptr, TRUE, 3, 256); /* Item is in the equipment */ item = GET_ARRAY_INDEX(p_ptr->equipment, o_ptr); /* No more items? */ return (format("You were %s: %s (%c).", describe_use(item), o_name, I2A(item))); /* Restore old number of items */ o_ptr->number = num; } else if (list == &p_ptr->inventory) { /* No more items? */ return (format("There are %s.", o_name)); } else if (list == &c_ptr->o_idx) { return (format("On the ground: %s.", o_name)); } } else { if (!list) { /* Item is in the equipment */ item = GET_ARRAY_INDEX(p_ptr->equipment, o_ptr); if (show_labels) strnfmt(lab, 40, "%^s: ", describe_use(item)); return (format("%s%s (%c).", lab, o_name, I2A(item))); } else if (list == &p_ptr->inventory) { /* Get number of item in inventory */ item = get_item_position(p_ptr->inventory, o_ptr); if (show_labels) strnfmt(lab, 40, "In your pack: "); /* Hack to get that letter correct in case a scroll disappears */ if (back_step) return (format("%s%s (%c).", lab, o_name, I2A(item - 1))); else return (format("%s%s (%c).", lab, o_name, I2A(item))); } else if (list == &c_ptr->o_idx) { return (format("On the ground: %s.", o_name)); } /* Then it is in the shop */ else { if (show_labels) strnfmt(lab, 40, "In the shop: "); return (format("%s%s", lab, o_name)); } } /* Missed it all */ return (NULL); } /* * Describe an item in the inventory. */ void item_describe(object_type *o_ptr) { msgf("%s", item_describe_aux(o_ptr, FALSE)); } /* * Describe an item in the inventory. */ void item_describe_roff(object_type *o_ptr) { roff("%s", item_describe_aux(o_ptr, FALSE)); } /* * Describe an item in the inventory and pretend it is one slot lower than it * seems to be. This is usefull when a the item is being identified by a scroll * of idenitfy and it was the last scroll of identify so there is some shuffling * in the inventory. * * Faux is for faux pas as this is a hack. */ void item_describe_faux(object_type *o_ptr) { msgf("%s", item_describe_aux(o_ptr, TRUE)); } /* * Erase an inventory slot if it has no more items */ static void item_optimize(object_type *o_ptr) { s16b *list; /* Default to looking under the player */ cave_type *c_ptr = area(p_ptr->px, p_ptr->py); /* The player could have moved due to a phase door scroll */ if (in_bounds2(o_ptr->ix, o_ptr->iy)) { c_ptr = area(o_ptr->ix, o_ptr->iy); } /* Only optimize real items */ if (!o_ptr->k_idx) return; /* Only optimize empty items */ if (o_ptr->number) return; /* Get list */ list = look_up_list(o_ptr); /* The item is being wielded */ if (!list) { /* Erase the empty slot */ object_wipe(o_ptr); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate torch */ p_ptr->update |= (PU_TORCH); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Window stuff */ p_ptr->window |= (PW_EQUIP); } else { /* Delete the object */ if (list == &p_ptr->inventory) { /* Inventory item */ delete_held_object(list, o_ptr); /* Window stuff */ p_ptr->window |= (PW_INVEN); } else if (list == &c_ptr->o_idx) { /* Floor item */ delete_dungeon_object(o_ptr); } else { /* Store item */ delete_held_object(list, o_ptr); } } /* Window stuff */ p_ptr->window |= (PW_SPELL); } /* * Split a pile into two bits. Return a pointer to * the split off piece. This piece will not be in the * original pile's list, but will be in the static * temp_object defined above. */ object_type *item_split(object_type *o_ptr, int num) { object_type *q_ptr; /* Paranoia */ if (o_ptr->number < num) num = o_ptr->number; /* Duplicate the object */ q_ptr = object_dup(o_ptr); /* Distribute charges of wands or rods */ distribute_charges(o_ptr, q_ptr, num); /* Update item totals */ o_ptr->number -= num; q_ptr->number = num; /* Notice the change */ if (num && player_item(o_ptr)) { /* Recalculate bonuses and weight */ p_ptr->update |= (PU_BONUS | PU_WEIGHT); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Notice changes */ notice_item(); } /* Fill in holes... */ item_optimize(o_ptr); /* Done - return new item */ return (q_ptr); } /* * Increase the "number" of an item in the inventory */ static void item_increase_aux(object_type *o_ptr, int num, bool silent) { /* Apply */ num += o_ptr->number; /* Bounds check */ if (num > 255) num = 255; else if (num < 0) num = 0; /* Un-apply */ num -= o_ptr->number; /* Add the number */ o_ptr->number += num; /* Notice the change */ if (num && player_item(o_ptr)) { /* Recalculate bonuses and weight */ p_ptr->update |= (PU_BONUS | PU_WEIGHT); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Notice changes */ notice_item(); } /* The shop upkeeping shouldn't be mentioned */ if (!silent) item_describe(o_ptr); item_optimize(o_ptr); } /* * Increase the "number" of an item in the inventory */ void item_increase(object_type *o_ptr, int num) { item_increase_aux(o_ptr, num, FALSE); } /* * Increase the "number" of an item in the inventory without a message */ void item_increase_silent(object_type *o_ptr, int num) { item_increase_aux(o_ptr, num, TRUE); } /* * Check if we have space for an item in the pack without overflow */ bool inven_carry_okay(const object_type *o_ptr) { object_type *j_ptr; /* Empty slot? */ if (get_list_length(p_ptr->inventory) < INVEN_PACK) return (TRUE); /* Similar slot? */ OBJ_ITT_START (p_ptr->inventory, j_ptr) { /* Check if the two items can be combined */ if (object_similar(j_ptr, o_ptr)) return (TRUE); } OBJ_ITT_END; /* Nope */ return (FALSE); } /* * Compare two items to see if they are in pack-order. */ static bool reorder_pack_comp(const object_type *o1_ptr, const object_type *o2_ptr) { /* Hack -- readable books always come first */ if ((o1_ptr->tval == REALM1_BOOK) && (o2_ptr->tval != REALM1_BOOK)) return (TRUE); if ((o2_ptr->tval == REALM1_BOOK) && (o1_ptr->tval != REALM1_BOOK)) return (FALSE); if ((o1_ptr->tval == REALM2_BOOK) && (o2_ptr->tval != REALM2_BOOK)) return (TRUE); if ((o2_ptr->tval == REALM2_BOOK) && (o1_ptr->tval != REALM2_BOOK)) return (FALSE); /* Objects sort by decreasing type */ if (o1_ptr->tval > o2_ptr->tval) return (TRUE); if (o1_ptr->tval < o2_ptr->tval) return (FALSE); /* Non-aware (flavored) items always come last */ if (!object_aware_p(o2_ptr)) return (TRUE); if (!object_aware_p(o1_ptr)) return (FALSE); /* Objects sort by increasing sval */ if (o1_ptr->sval < o2_ptr->sval) return (TRUE); if (o1_ptr->sval > o2_ptr->sval) return (FALSE); /* Unidentified objects always come last */ if (!object_known_p(o2_ptr)) return (TRUE); if (!object_known_p(o1_ptr)) return (FALSE); /* Lites sort by increasing timeout */ if (o1_ptr->tval == TV_LITE) { if (o1_ptr->sval < o2_ptr->sval) return (TRUE); if (o1_ptr->sval > o2_ptr->sval) return (FALSE); } /* * Hack: otherwise identical rods sort by * increasing recharge time -DSB- */ if (o1_ptr->tval == TV_ROD) { if (o1_ptr->pval < o2_ptr->pval) return (TRUE); if (o1_ptr->pval > o2_ptr->pval) return (FALSE); } /* Objects sort by decreasing value */ if (object_value(o1_ptr) >= object_value(o2_ptr)) return (TRUE); return (FALSE); } /* * Actually reorder the objects * * This uses a simple bubble sort. * * Usually we only need to make a few swaps. */ object_type *reorder_objects_aux(object_type *q_ptr, object_comp comp_func, u16b o_idx) { object_type *o_ptr, *j_ptr; int i; /* * Hack - Do this twice because we invert the order * of 'similar' objects on the first pass */ for (i = 0; i < 2; i++) { /* Re-order the pack */ OBJ_ITT_START (o_idx, o_ptr) { OBJ_ITT_START (o_ptr->next_o_idx, j_ptr) { /* Are they in the right order? */ if (comp_func(j_ptr, o_ptr)) { /* Are we moving the watched item? */ if (q_ptr) { if (q_ptr == o_ptr) q_ptr = j_ptr; else if (q_ptr == j_ptr) q_ptr = o_ptr; } /* Swap the objects */ swap_objects(o_ptr, j_ptr); } } OBJ_ITT_END; } OBJ_ITT_END; } return (q_ptr); } /* * Add an item to the players inventory, and return the slot used. * * If the new item can combine with an existing item in the inventory, * it will do so, using "object_similar()" and "object_absorb()", else, * the item will be placed into the "proper" location in the inventory. * * This function can be used to "over-fill" the player's pack, but only * once, and such an action must trigger the "overflow" code immediately. * Note that when the pack is being "over-filled", the new item must be * placed into the "overflow" slot, and the "overflow" must take place * before the pack is reordered, but (optionally) after the pack is * combined. This may be tricky. See "dungeon.c" for info. * * Note that this code must remove any location/stack information * from the object once it is placed into the inventory. */ object_type *inven_carry(object_type *o_ptr) { object_type *j_ptr; /* Check for combining */ OBJ_ITT_START (p_ptr->inventory, j_ptr) { /* Check if the two items can be combined */ if (object_similar(j_ptr, o_ptr)) { /* Combine the items */ object_absorb(j_ptr, o_ptr); /* Recalculate bonuses and weight */ p_ptr->update |= (PU_BONUS | PU_WEIGHT); /* Notice changes */ notice_inven(); /* Wipe old object */ object_wipe(o_ptr); /* Success */ return (j_ptr); } } OBJ_ITT_END; /* Add the item to the pack */ o_ptr = add_object_list(&p_ptr->inventory, o_ptr); /* Paranoia */ if (!o_ptr) return (NULL); /* Forget location */ o_ptr->iy = o_ptr->ix = 0; /* Forget Region */ o_ptr->region = 0; /* No longer marked */ o_ptr->info &= ~(OB_SEEN); /* Reorder the pack */ o_ptr = reorder_objects_aux(o_ptr, reorder_pack_comp, p_ptr->inventory); /* Recalculate bonuses and weight */ p_ptr->update |= (PU_BONUS | PU_WEIGHT); /* Notice changes */ notice_inven(); /* Return the new item */ return (o_ptr); } /* * Take off (some of) a non-cursed equipment item * * Note that only one item at a time can be wielded per slot. * * Note that taking off an item when "full" may cause that item * to fall to the ground. * * Return the inventory slot into which the item is placed. */ object_type *inven_takeoff(object_type *o_ptr) { int item; object_type *q_ptr; cptr act; /* Look up item number */ item = GET_ARRAY_INDEX(p_ptr->equipment, o_ptr); /* Took off weapon */ if (item == EQUIP_WIELD) { act = "You were wielding"; } /* Took off bow */ else if (item == EQUIP_BOW) { act = "You were holding"; } /* Took off light */ else if (item == EQUIP_LITE) { act = "You were holding"; } /* Took off something */ else { act = "You were wearing"; } /* Message */ msgf("%s %v (%c).", act, OBJECT_FMT(o_ptr, TRUE, 3), I2A(item)); /* Carry the object */ q_ptr = inven_carry(o_ptr); /* Paranoia */ if (!q_ptr) { msgf("You cannot take off the item - too many dungeon objects!"); return (NULL); } /* Recalculate torch */ p_ptr->update |= (PU_TORCH); /* Recalculate mana */ p_ptr->update |= (PU_MANA); p_ptr->redraw |= (PR_EQUIPPY); /* Window stuff */ p_ptr->window |= (PW_PLAYER | PW_EQUIP); /* Return the item */ return (q_ptr); } /* * Drop (some of) a non-cursed inventory/equipment item * * The object will be dropped "near" the current location */ void inven_drop(object_type *o_ptr, int amt) { object_type *q_ptr; int slot; s16b *list; /* Error check */ if (amt <= 0) return; /* Describe item */ item_describe(o_ptr); /* Get list */ list = look_up_list(o_ptr); /* Take off equipment */ if (!list) { /* Take off first */ o_ptr = inven_takeoff(o_ptr); /* Paranoia */ if (!o_ptr) return; } /* Get item slot */ slot = get_item_position(p_ptr->inventory, o_ptr); /* Get local object */ q_ptr = item_split(o_ptr, amt); /* Message */ msgf("You drop %v (%c).", OBJECT_FMT(q_ptr, TRUE, 3), I2A(slot)); /* Drop it near the player */ drop_near(q_ptr, 0, p_ptr->px, p_ptr->py); /* Update total weight */ p_ptr->update |= PU_WEIGHT; } /* * Combine items in the pack */ static object_type *combine_pack_aux(object_type *q_ptr) { object_type *o_ptr; object_type *j_ptr; bool flag = FALSE; /* Combine the pack */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Scan the items above that item */ OBJ_ITT_START (o_ptr->next_o_idx, j_ptr) { /* Can we drop "o_ptr" onto "j_ptr"? */ if (object_similar(o_ptr, j_ptr)) { /* Take note */ flag = TRUE; /* The original is about to disappear so assign to the new */ if (o_ptr == q_ptr) q_ptr = j_ptr; /* Add together the item counts */ object_absorb(j_ptr, o_ptr); /* Delete the item */ delete_held_object(&p_ptr->inventory, o_ptr); /* Window stuff */ p_ptr->window |= (PW_INVEN); /* Done */ break; } } OBJ_ITT_END; } OBJ_ITT_END; /* Message */ if (flag) msgf("You combine some items in your pack."); return (q_ptr); } /* * Combine items in the pack */ void combine_pack(void) { (void)combine_pack_aux(NULL); } /* * Combine items in the pack and keep track of an object */ object_type *combine_pack_watch(object_type *q_ptr) { return (combine_pack_aux(q_ptr)); } /* * Reorder items in the pack */ void reorder_pack(void) { (void)reorder_objects_aux(NULL, reorder_pack_comp, p_ptr->inventory); /* Window stuff */ p_ptr->window |= (PW_INVEN); } /* * Reorder items in the pack and return the watched object. */ object_type *reorder_pack_watch(object_type *o_ptr) { /* Window stuff */ p_ptr->window |= (PW_INVEN); return (reorder_objects_aux(o_ptr, reorder_pack_comp, p_ptr->inventory)); } bool can_player_destroy_object(object_type *o_ptr) { /* * Artifacts cannot be destroyed * However only notice if we have not *id'ed* it. */ if (FLAG(o_ptr, TR_INSTA_ART)) { if (!(o_ptr->info & OB_MENTAL)) { byte feel = FEEL_SPECIAL; /* Hack -- Handle icky artifacts */ if (cursed_p(o_ptr) || !o_ptr->cost) feel = FEEL_TERRIBLE; /* Hack -- inscribe the artifact */ o_ptr->feeling = feel; /* We have "felt" it (again) */ o_ptr->info |= (OB_SENSE); /* Redraw equippy chars */ p_ptr->redraw |= (PR_EQUIPPY); /* Notice changes */ notice_item(); } /* Done */ return FALSE; } return TRUE; } /* * Hack -- display an object kind in the current window * * Include list of usable spells for readible books */ void display_koff(int k_idx) { /* Get local object */ object_type *q_ptr; /* Erase the window */ clear_from(0); /* No info */ if (!k_idx) return; /* Prepare the object */ q_ptr = object_prep(k_idx); /* Mention the object name */ prtf(0, 0, "%v", OBJECT_STORE_FMT(q_ptr, FALSE, 0)); /* Warriors are illiterate */ if (!(p_ptr->spell.r[0].realm || p_ptr->spell.r[1].realm)) return; /* Display spells in readible books */ if ((q_ptr->tval == REALM1_BOOK) || (q_ptr->tval == REALM2_BOOK)) { int sval; int spell = -1; int num = 0; byte spells[PY_MAX_SPELLS]; /* Access the item's sval */ sval = q_ptr->sval; /* Extract spells */ for (spell = 0; spell < 32; spell++) { /* Check for this spell */ if (fake_spell_flags[sval] & (1L << spell)) { /* Collect this spell */ spells[num++] = spell; } } /* Print spells */ print_spells(spells, num, 0, 2, ((q_ptr->tval == REALM1_BOOK) ? p_ptr->spell.r[0].realm - 1 : p_ptr->spell.r[1].realm - 1)); } } zangband/src/quest.c0000644000000000000000000015104310250356274013444 0ustar rootroot/* File: quest.c */ /* Purpose: Quest code */ /* * Copyright (c) 1989, 2003 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann, Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "wild.h" /* * Maximum number of tries for selection of a proper quest monster */ #define MAX_TRIES 100 /* * Wipe a quest */ static void quest_wipe(int i) { quest_type *q_ptr = &quest[i]; q_ptr->status = QUEST_STATUS_UNTAKEN; q_ptr->flags = 0x00; q_ptr->type = QUEST_TYPE_NONE; /* No artificial quest item */ q_ptr->item = 0; /* No quest-giver */ q_ptr->place = 0; q_ptr->shop = 0; /* No reward */ q_ptr->reward = 0; /* Types of creation and trigger hooks */ q_ptr->c_type = QC_NONE; q_ptr->x_type = QX_NONE; /* Timeout */ q_ptr->timeout = 0; /* No name */ q_ptr->name[0] = '\0'; /* * Do not need to clear the extra data * - it is ignored since q_ptr->type = QUEST_TYPE_NONE */ } /* Current location in scan for completed quests */ static s16b q_cnt = 0; /* * Acquires and returns the index of a "free" quest. * * This routine should almost never fail, but in case it does, * we must be sure to handle "failure" of this routine. */ u16b q_pop(void) { int i; /* Initial allocation */ if (q_max < z_info->q_max) { /* Get next space */ i = q_max; /* Count quests */ q_max++; /* Use this quest */ return (i); } /* Recycle finished quests */ for (i = 1; i < q_max; i++) { quest_type *q_ptr; /* Make sure we have a linear algorithm */ q_cnt++; /* loop back to start */ if (q_cnt >= q_max) q_cnt = 0; /* Acquire quest */ q_ptr = &quest[q_cnt]; /* Skip live quests */ if (q_ptr->status != QUEST_STATUS_FINISHED) continue; /* Skip find_place quests as these can be completed as a group */ if (q_ptr->type == QUEST_TYPE_FIND_PLACE) continue; /* Clear the old data */ quest_wipe(q_cnt); /* Use this field */ return (q_cnt); } /* Warn the player */ msgf("Too many quests!"); /* Oops */ return (0); } /* See if this quest is a wild quest and activate it if necessary */ void discover_wild_quest(int q_num) { /* Is there a quest here? */ if (!q_num) return; /* Is it a wild quest */ if (quest[q_num].type != QUEST_TYPE_WILD) return; /* Was this a taken quest? */ if (quest[q_num].status == QUEST_STATUS_UNTAKEN) { /* Now we take it */ quest[q_num].status = QUEST_STATUS_TAKEN; /* Hack -- make him active to make the discovery */ quest[q_num].flags |= QUEST_FLAG_ACTIVE; /* Announce */ quest_discovery(); } } /* * Make a quest for killing n monsters of a certain type on a certain level */ u16b insert_dungeon_monster_quest(u16b r_idx, u16b num, u16b level) { int q_num; quest_type *q_ptr; monster_race *r_ptr = &r_info[r_idx]; /* get a new quest */ q_num = q_pop(); /* Paranoia */ if (!q_num) return (0); q_ptr = &quest[q_num]; /* Store in information */ q_ptr->type = QUEST_TYPE_DUNGEON; /* We need to place the monster(s) when the dungeon is made */ q_ptr->c_type = QC_DUN_MONST; /* We need to trigger when the monsters are killed */ if (FLAG(r_ptr, RF_UNIQUE)) { q_ptr->x_type = QX_KILL_UNIQUE; } else { q_ptr->x_type = QX_KILL_MONST; } if (num != 1) { char buf[80]; strcpy(buf, mon_race_name(r_ptr)); plural_aux(buf); /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Kill %d %s.", (int)num, buf); } else { /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Kill %s.", mon_race_name(r_ptr)); } /* Save the quest data */ q_ptr->data.dun.r_idx = r_idx; q_ptr->data.dun.level = level; q_ptr->data.dun.cur_num = 0; q_ptr->data.dun.max_num = num; q_ptr->data.dun.num_mon = 0; /* Return number of quest */ return (q_num); } /* * Create the quests for the Serpent and Oberon */ static void insert_winner_quest(u16b r_idx, u16b num, u16b level) { /* Normal monster quest */ u16b q_idx = insert_dungeon_monster_quest(r_idx, num, level); /* Winner result of quest */ quest[q_idx].x_type = QX_KILL_WINNER; quest[q_idx].flags |= QUEST_FLAG_KNOWN; quest[q_idx].status = QUEST_STATUS_TAKEN; } /* * Look for an appropriate dungeon for a given level */ static u16b find_good_dungeon(int level, bool repeat) { int i; int score, best_score = 0; int best_place = 0; place_type *pl_ptr; for (i = 0; i < place_count; i++) { pl_ptr = &place[i]; /* Want dungeons */ if (pl_ptr->type != TOWN_DUNGEON) continue; /* Reuse this dungeon? (relevant for quest_find_place) */ if (!repeat && pl_ptr->quest_num) continue; /* Get difference in levels */ score = ABS(pl_ptr->dungeon->max_level - level); /* The bigger the difference, the less likely a high score is */ score = randint1(127 - score); if (score > best_score) { best_score = score; best_place = i; } } /* Best match to reward level */ return (best_place); } /* * Look for an appropriate town with a distance appropriate * for the given level. */ static u16b find_good_town(int *dist) { int i; int score, best_score = 0; int best_place = 0; place_type *pl_ptr; for (i = 0; i < place_count; i++) { /* Not current town */ if (i == p_ptr->place_num) continue; pl_ptr = &place[i]; /* Want towns with buildings */ if (!pl_ptr->numstores) continue; /* Get difference of distance in wilderness blocks and difficulty level */ score = abs(distance(pl_ptr->x, pl_ptr->y, p_ptr->px / 16, p_ptr->py / 16) - *dist); /* The bigger the difference, the less likely a high score is */ score = randint1(WILD_SIZE - score); if (score > best_score) { best_score = score; best_place = i; } } /* Save distance to best town */ pl_ptr = &place[best_place]; *dist = distance(pl_ptr->x, pl_ptr->y, p_ptr->px / 16, p_ptr->py / 16); /* Best match to reward level */ return (best_place); } /* * This function returns the closest name of the closest town and the * direction to that town. If known == TRUE then the player must have * seen the closest town too, in order not to give away clues to the map. */ cptr describe_quest_location(cptr * dirn, int x, int y, bool known) { int i; int dx, dy; /* Find the nearest town */ int best_dist = 99999; int best_town = 0; for (i = 0; i < place_count; i++) { bool visit = FALSE; int d; wild_done_type *w_ptr; /* Only real towns */ if (place[i].type != TOWN_FRACT) continue; /* Should this be a known town? */ if (known) { for (dx = 0; dx < 8 && !visit; dx++) { for (dy = 0; dy < 8 && !visit; dy++) { /* Get wilderness square where the town could be */ w_ptr = &wild[place[i].y + dy][place[i].x + dx].done; /* Is this a town square */ if (w_ptr->place != i) continue; /* Has the player visited this square? */ visit |= (w_ptr->info & WILD_INFO_SEEN); } } /* Unmapped town */ if (!visit) continue; } /* Find closest town */ d = distance(x, y, place[i].x, place[i].y); /* Keep track of the best town */ if (d < best_dist) { best_dist = d; best_town = i; } } dx = x - place[best_town].x; dy = y - place[best_town].y; if (ABS(dy) > ABS(dx) * 3) { if (dy > 0) *dirn = "south"; else *dirn = "north"; } else if (ABS(dx) > ABS(dy) * 3) { if (dx > 0) *dirn = "east"; else *dirn = "west"; } else if (dx > 0) { if (dy > 0) *dirn = "south-east"; else *dirn = "north-east"; } else { if (dy > 0) *dirn = "south-west"; else *dirn = "north-west"; } return (place[best_town].name); } /* * Initialise the quests */ errr init_quests(void) { int i; /* Make the quest array */ C_MAKE(quest, z_info->q_max, quest_type); /* Reset number of quests */ q_max = 1; /* Wipe the quests */ for (i = 0; i < z_info->q_max; i++) { quest_wipe(i); } return (0); } /* * Quests * */ void init_player_quests(void) { int i; /* Reset number of quests */ q_max = 1; /* Clear all the quests */ for (i = 0; i < z_info->q_max; i++) { quest_wipe(i); } /* Add the winner quests */ /* Hack XXX XXX Oberon, hard coded */ insert_winner_quest(QW_OBERON, 1, 99); /* Hack XXX XXX Serpent, hard coded */ insert_winner_quest(QW_SERPENT, 1, 100); } /* Array of places to find an inscription */ static cptr find_quest[] = { "You find the following inscription in the floor", "You see a message inscribed in the wall", "There is a sign saying", "Something is written on the staircase", "You find a scroll with the following message", "You hear", }; /* * Discover quests on this level */ void quest_discovery(void) { int i; quest_type *q_ptr; char name[80]; for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; /* Quest needs to be taken. */ if (q_ptr->status != QUEST_STATUS_TAKEN) continue; /* Is the quest active? */ if (!(q_ptr->flags & QUEST_FLAG_ACTIVE)) continue; /* Is the quest known already? */ if (q_ptr->flags & QUEST_FLAG_KNOWN) continue; /* Hack - The quest is now known */ q_ptr->flags |= QUEST_FLAG_KNOWN; /* See what type of quest it is */ switch (q_ptr->type) { case QUEST_TYPE_NONE: { /* Paranoia */ continue; } case QUEST_TYPE_BOUNTY: { /* Paranoia */ continue; } case QUEST_TYPE_DUNGEON: { monster_race *r_ptr = &r_info[q_ptr->data.dun.r_idx]; int q_num = q_ptr->data.dun.max_num - q_ptr->data.dun.cur_num; /* Assume the quest is a 'kill n monsters quest' for now. */ strcpy(name, mon_race_name(r_ptr)); if (FLAG(r_ptr, RF_UNIQUE)) { /* Unique */ msgf("%s: Beware, this level is protected by %s!", find_quest[rand_range(0, 5)], name); } else { /* Normal monsters */ if (q_num > 1) plural_aux(name); msgf("%s: Be warned, this level is guarded by %d %s!", find_quest[rand_range(0, 5)], q_num, name); } /* Disturb */ disturb(FALSE); continue; } case QUEST_TYPE_WILD: { msgf("You discover something unusual in the wilderness."); /* Disturb */ disturb(FALSE); /* Paranoia */ continue; } case QUEST_TYPE_FIND_ITEM: { msgf("You feel there is something interesting about this level."); /* Disturb */ disturb(FALSE); /* Paranoia */ continue; } default: { /* Paranoia */ continue; } } } } /* * Is this dungeon level a special (winner) quest level? */ bool is_special_level(int level) { int i; quest_type *q_ptr; for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; /* Must be dungeon quest */ if (q_ptr->type != QUEST_TYPE_DUNGEON) continue; /* Must be winner quest */ if (q_ptr->x_type != QX_KILL_WINNER) continue; /* Is the quest still there? */ if (q_ptr->status > QUEST_STATUS_TAKEN) continue; /* Does the level match? */ if (q_ptr->data.dun.level == level) return (TRUE); } return (FALSE); } /* * Activate quests valid for this level */ void activate_quests(int level) { int i; quest_type *q_ptr; for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; /* Is the quest still there? */ if (q_ptr->status > QUEST_STATUS_TAKEN) continue; /* Assume no longer active */ q_ptr->flags &= ~(QUEST_FLAG_ACTIVE); /* Is the quest relevant? */ switch (q_ptr->type) { case QUEST_TYPE_DUNGEON: { /* Correct dungeon level? */ if (q_ptr->data.dun.level != level) break; /* Hack - toggle QUESTOR flag */ SET_FLAG(&r_info[q_ptr->data.dun.r_idx], RF_QUESTOR); /* Activate the quest */ q_ptr->flags |= QUEST_FLAG_ACTIVE; break; } case QUEST_TYPE_BOUNTY: { dun_type *d_ptr = dungeon(); monster_race *r_ptr = &r_info[q_ptr->data.bnt.r_idx]; /* Hack - toggle QUESTOR flag */ SET_FLAG(r_ptr, RF_QUESTOR); /* Is the player inside the right sort of dungeon? */ if (level && r_ptr->flags[7] & d_ptr->habitat) { /* Create a starting level */ int min_level = d_ptr->min_level; /* How deep is the dungeon? */ int depth = MIN(d_ptr->max_level, 99) - min_level; /* How many have been killed? */ int cur_num = q_ptr->data.bnt.cur_num; /* Top out at how many monsters? */ int max_num = q_ptr->data.bnt.max_num; /* Spread the monsters evenly throughout the dungeon */ if (level == min_level + (depth * (cur_num + 1)) / (max_num + 1)) { /* Activate the quest */ q_ptr->flags |= QUEST_FLAG_ACTIVE; } } break; } case QUEST_TYPE_WILD: { /* In Wilderness? */ if (!level) q_ptr->flags |= QUEST_FLAG_ACTIVE; break; } case QUEST_TYPE_FIND_ITEM: { /* Always active until the relic has been id'd */ q_ptr->flags |= QUEST_FLAG_ACTIVE; } case QUEST_TYPE_MESSAGE: { int place_num = q_ptr->data.msg.place; place_type *pl_ptr; /* Not correct place? */ if (place_num != p_ptr->place_num) break; pl_ptr = &place[place_num]; /* Need to be on the surface */ if (p_ptr->depth) break; q_ptr->flags |= QUEST_FLAG_ACTIVE; } case QUEST_TYPE_FIND_PLACE: { int place_num = q_ptr->data.fpl.place; place_type *pl_ptr; /* Not correct place? */ if (place_num != p_ptr->place_num) break; pl_ptr = &place[place_num]; /* Need to be on the surface */ if (p_ptr->depth) break; q_ptr->flags |= QUEST_FLAG_ACTIVE; } } } } /* * Create a magical staircase */ static void create_stairs(int x, int y) { int i = 0; int ny, nx; cave_type *c_ptr = area(x, y); /* Paranoia - not on deepest dungeon level */ if (p_ptr->depth == dungeon()->max_level) return; /* Stagger around */ while ((cave_perma_grid(c_ptr) || c_ptr->o_idx) && !(i > 100)) { /* Pick a location */ scatter(&nx, &ny, x, y, 1); /* Stagger */ y = ny; x = nx; /* paranoia - increment counter */ i++; /* paranoia */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); } /* Explain the staircase */ msgf("A magical staircase appears..."); /* Destroy the fields on the square */ delete_field(x, y); /* Create stairs down */ cave_set_feat(x, y, FEAT_MORE); } static void display_monster_quest(quest_type *q_ptr) { int j, k; cave_type *c_ptr; int x, y; monster_race *r_ptr = &r_info[q_ptr->data.dun.r_idx]; /* Hack -- "unique" monsters must be "unique" */ if ((FLAG(r_ptr, RF_UNIQUE)) && (r_ptr->cur_num >= r_ptr->max_num)) { /* Hack - the unique is already dead */ q_ptr->status = QUEST_STATUS_FINISHED; } else { bool group; int number, r_idx; /* Create a number of monsters depending on quest_type */ if (q_ptr->type == QUEST_TYPE_BOUNTY) { /* One at a time */ number = 1; /* Which monster? */ r_idx = q_ptr->data.bnt.r_idx; } else { /* All remaining at once */ number = q_ptr->data.dun.max_num - q_ptr->data.dun.cur_num; /* Which monster? */ r_idx = q_ptr->data.dun.r_idx; } for (j = 0; j < number; j++) { for (k = 0; k < 5000; k++) { /* Find an empty grid */ while (TRUE) { y = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); x = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); /* Access the grid */ c_ptr = area(x, y); if (!cave_naked_grid(c_ptr)) continue; if (distance(x, y, p_ptr->px, p_ptr->py) < 10) continue; else break; } if (FLAG(r_ptr, RF_FRIENDS)) group = FALSE; else group = TRUE; /* Try to place the monster */ if (place_monster_aux (x, y, r_idx, FALSE, group, FALSE, FALSE, TRUE)) { /* Success */ break; } else { /* Failure - Try again */ continue; } } } } } static void display_artifact_quest(quest_type *q_ptr) { int x = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); int y = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); /* Drop artifact in dungeon */ create_named_art(q_ptr->data.fit.a_idx, x, y); } /* * Test each quest to see which ones are created */ void trigger_quest_create(byte c_type, vptr data) { int i; quest_type *q_ptr; /* Ignore data - it may be unused */ (void)data; for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; /* Quest must be chosen */ if (q_ptr->status != QUEST_STATUS_TAKEN) continue; /* Quest must be active */ if (!(q_ptr->flags & QUEST_FLAG_ACTIVE)) continue; /* Must be relevant */ if (q_ptr->c_type != c_type) continue; /* Handle the trigger */ switch (c_type) { case QC_NONE: { /* Paranoia */ continue; } case QC_DUN_MONST: { display_monster_quest(q_ptr); continue; } case QC_DUN_ARTIFACT: { int place_num = q_ptr->data.fit.place; place_type *pl_ptr; /* Not correct place? */ if (place_num != p_ptr->place_num) continue; pl_ptr = &place[place_num]; /* Need to be in the dungeon */ if (!pl_ptr->dungeon) continue; /* Correct dungeon level? */ if (p_ptr->depth != pl_ptr->dungeon->max_level) continue; display_artifact_quest(q_ptr); continue; } } } } /* * Test each quest to see if they are completed */ void trigger_quest_complete(byte x_type, vptr data) { int i; quest_type *q_ptr; for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; /* Quest must be chosen */ if (q_ptr->status != QUEST_STATUS_TAKEN) continue; /* Quest must be active */ if (!(q_ptr->flags & QUEST_FLAG_ACTIVE)) continue; /* Must be relevant */ if (q_ptr->x_type != x_type) continue; /* Handle the trigger */ switch (x_type) { case QX_NONE: { /* Paranoia */ break; } case QX_KILL_MONST: case QX_KILL_UNIQUE: { monster_type *m_ptr = ((monster_type *)data); if (q_ptr->data.bnt.r_idx == m_ptr->r_idx) { /* Don't count clones */ if (m_ptr->smart & SM_CLONED) break; /* Increment number killed */ q_ptr->data.bnt.cur_num++; if (q_ptr->data.bnt.cur_num >= q_ptr->data.bnt.max_num) { /* Complete the quest */ q_ptr->status = QUEST_STATUS_COMPLETED; /* Monster is no longer 'QUESTOR' */ r_info[q_ptr->data.bnt.r_idx].flags[0] &= ~(RF0_QUESTOR); } } break; } case QX_KILL_WINNER: { monster_type *m_ptr = ((monster_type *)data); monster_race *r_ptr = &r_info[m_ptr->r_idx]; if (q_ptr->data.dun.r_idx == m_ptr->r_idx) { /* Winner? */ if (mon_name_cont(r_ptr, "Serpent of Chaos")) { /* Total winner */ p_ptr->state.total_winner = TRUE; /* Redraw the "title" */ p_ptr->redraw |= (PR_TITLE); /* Congratulations */ msgf("*** CONGRATULATIONS ***"); msgf("You have won the game!"); msgf ("You may retire (commit suicide) when you are ready."); } else { /* Oberon */ /* A message */ msgf("Well done!"); msgf("You have beaten Oberon."); msgf ("You now can meet the final challenge of the Serpent of Chaos."); } /* Complete the quest */ q_ptr->status = QUEST_STATUS_COMPLETED; /* Mega-hack */ create_stairs(m_ptr->fx, m_ptr->fy); } break; } case QX_WILD_ENTER: { place_type *pl_ptr; /* Only trigger for the correct quest */ if (q_ptr != data) continue; pl_ptr = &place[p_ptr->place_num]; /* Wilderness quests turn off the monsters */ if (q_ptr->type == QUEST_TYPE_WILD) { /* Unlink location from wilderness */ int x = ((u16b)p_ptr->wilderness_x / WILD_BLOCK_SIZE); int y = ((u16b)p_ptr->wilderness_y / WILD_BLOCK_SIZE); wild_done_type *w_ptr = &wild[y][x].done; /* No more place here */ w_ptr->place = 0; /* Decrement active block counter */ pl_ptr->data--; /* Are we done yet? */ if (pl_ptr->data) break; /* Finish the quest */ q_ptr->status = QUEST_STATUS_FINISHED; } if (q_ptr->type == QUEST_TYPE_FIND_PLACE) { msgf("You find the ruin you were looking for."); /* Complete the quest */ q_ptr->status = QUEST_STATUS_COMPLETED; } break; } case QX_KNOW_ARTIFACT: { if (*((int *) data) == q_ptr->data.fit.a_idx) { msgf("You find the relic you were looking for."); /* Complete the quest */ q_ptr->status = QUEST_STATUS_COMPLETED; break; } } case QX_FIND_SHOP: { place_type *pl_ptr; /* Towns must match */ if (p_ptr->place_num != q_ptr->data.msg.place) continue; pl_ptr = &place[p_ptr->place_num]; /* Do the stores match? */ if ((store_type *) data != &pl_ptr->store[q_ptr->data.msg.shop]) continue; /* Complete the quest */ q_ptr->status = QUEST_STATUS_COMPLETED; msgf("You have found the place you were looking for and you deliver the message!"); message_flush(); break; } } /* Finished the quest? */ if ((q_ptr->status == QUEST_STATUS_FINISHED) || (q_ptr->status == QUEST_STATUS_COMPLETED)) { msgf("You just completed your quest!"); } } } /* * Look up an open quest that corresponds to the given building. * * If there is none, return NULL. * * (This assumes one quest per building.) */ quest_type *lookup_quest_building(const store_type *b_ptr) { int i; quest_type *q_ptr; place_type *pl_ptr = &place[p_ptr->place_num]; for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; if (q_ptr->place != p_ptr->place_num) continue; /* Bounds checking */ if (q_ptr->shop >= pl_ptr->numstores) continue; /* Disregard finished quests */ if (q_ptr->status == QUEST_STATUS_FINISHED) continue; /* A match? */ if (&pl_ptr->store[q_ptr->shop] == b_ptr) { return (q_ptr); } } /* No match */ return (NULL); } void reward_quest(quest_type *q_ptr) { switch (q_ptr->type) { case QUEST_TYPE_FIND_ITEM: { if (q_ptr->status == QUEST_STATUS_COMPLETED) { msgf("You can keep it if you like."); /* Allow this quest to be deleted if needed */ q_ptr->status = QUEST_STATUS_FINISHED; /* Take note */ if (auto_notes) { add_note('Q', "Finished quest: %s", q_ptr->name); } } else { msgf("%s", q_ptr->name); msgf("Still looking?"); } break; } case QUEST_TYPE_BOUNTY: { if (q_ptr->status == QUEST_STATUS_COMPLETED) { /* Give to player */ p_ptr->au += q_ptr->reward; /* And tell him */ msgf("You are given %d gold pieces for your efforts.", q_ptr->reward); /* Allow this quest to be deleted if needed */ q_ptr->status = QUEST_STATUS_FINISHED; /* Take note */ if (auto_notes) { add_note('Q', "Finished quest: %s", q_ptr->name); } } else { /* Remind what the quest is */ msgf("%s", q_ptr->name); /* If you have killed all but one monster */ if (q_ptr->data.bnt.max_num - q_ptr->data.bnt.cur_num == 1) { monster_race *r_ptr = &r_info[q_ptr->data.bnt.r_idx]; if (FLAG(r_ptr, RF_MALE)) { /* Male reference */ msgf("Still looking for him?"); } else if (FLAG(r_ptr, RF_FEMALE)) { /* Female reference */ msgf("Still looking for her?"); } else { /* Neuter reference */ msgf("Still looking for it?"); } } else { /* More than one monster */ msgf("Still looking for them?"); } } break; } case QUEST_TYPE_MESSAGE: { if (q_ptr->status == QUEST_STATUS_COMPLETED) { /* Give to player */ p_ptr->au += q_ptr->reward; msgf("You are given %d gold pieces for your efforts.", q_ptr->reward); /* Allow this quest to be deleted if needed */ q_ptr->status = QUEST_STATUS_FINISHED; /* Take note */ if (auto_notes) { add_note('Q', "Finished quest: %s", q_ptr->name); } } else { /* Tell the player what he was trying */ msgf("%s", q_ptr->name); msgf("Please deliver the message as soon as possible!"); } break; } case QUEST_TYPE_FIND_PLACE: { if (q_ptr->status == QUEST_STATUS_COMPLETED) { /* Give to player */ p_ptr->au += q_ptr->reward; msgf("You are given %d gold pieces for your efforts.", q_ptr->reward); /* Break the link between place and quest */ place[q_ptr->data.fpl.place].quest_num = z_info->q_max; /* Allow this quest to be deleted if needed */ q_ptr->status = QUEST_STATUS_FINISHED; /* Take note */ if (auto_notes) { add_note('Q', "Finished quest: %s", q_ptr->name); } } else { /* Remind the player what he was doing */ msgf("%s", q_ptr->name); msgf("Please find the ruin as soon as possible!"); } break; } default: { } } message_flush(); } static const store_type *curr_build; static int curr_scale; /* Save the quest giver (current town + building) */ static void set_quest_giver(quest_type *q_ptr) { place_type *pl_ptr = &place[p_ptr->place_num]; /* Remember quest giver for later */ q_ptr->place = p_ptr->place_num; q_ptr->shop = GET_ARRAY_INDEX(pl_ptr->store, curr_build); /* We know of this quest */ q_ptr->flags |= QUEST_FLAG_KNOWN; } static quest_type *insert_artifact_quest(u16b a_idx) { artifact_type *a_ptr = &a_info[a_idx]; place_type *pl_ptr; cptr town_name, town_dir; quest_type *q_ptr; int q_num; /* Skip "empty" artifacts */ if (!a_ptr->name) return (NULL); /* Cannot make an artifact twice */ if (a_ptr->cur_num) return (NULL); /* No quest items */ if (FLAG(a_ptr, TR_QUESTITEM)) return (NULL); /* get a new quest */ q_num = q_pop(); /* Paranoia */ if (!q_num) return (NULL); q_ptr = &quest[q_num]; /* Store in information */ q_ptr->type = QUEST_TYPE_FIND_ITEM; /* We have taken the quest */ q_ptr->status = QUEST_STATUS_TAKEN; /* We need to place the artifact in the dungeon */ q_ptr->c_type = QC_DUN_ARTIFACT; /* Finished when the player identifies it */ q_ptr->x_type = QX_KNOW_ARTIFACT; /* Find an available dungeon to place it in */ /* Save the quest data */ q_ptr->data.fit.a_idx = a_idx; /* Boost the level to make this quest harder. */ q_ptr->data.fit.place = find_good_dungeon(a_ptr->level * MAX_DEPTH / 100, TRUE); /* Where is it? */ pl_ptr = &place[q_ptr->data.fit.place]; /* Get name of closest town + direction away from it */ town_name = describe_quest_location(&town_dir, pl_ptr->x, pl_ptr->y, FALSE); /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Find the relic %s, which is hidden %s of %s.", a_name + a_ptr->name, town_dir, town_name); /* Artifact is now a quest item */ SET_FLAG(a_ptr, TR_QUESTITEM); /* Done */ return (q_ptr); } /* This function returns TRUE if nr1 and nr2 share a divider > 1 */ static bool share_divider(int nr1, int nr2) { int i; /* make sure that nr2 is larger/equal to nr1 */ if (nr1 > nr2) { /* swap them */ i = nr1; nr1 = nr2; nr2 = i; } /* Try all the numbers between 2 and nr1 */ for (i = 2; i <= nr1; i++) { /* if i can divide both nr1 and nr2 they share a divider */ if (!(nr1 % i) && !(nr2 % i)) return (TRUE); } /* The greatest common denominator of nr1 and nr2 is 1. */ return (FALSE); } /* * Supply an artifact-idx that has not been found yet. Do this on a weighted * basis. The product of the artifact's depth and level should be lower than * some random number. This ensures that low depth/level artifacts are more * easily chosen */ static u16b find_random_artifact(void) { u16b a_idx; int min = 999999, max = 0; int rand, step; artifact_type *a_ptr; /* Loop through the artifacts */ for (a_idx = 0; a_idx < z_info->a_max; a_idx++) { a_ptr = &a_info[a_idx]; /* Skip "empty" artifacts */ if (!a_ptr->name) continue; /* Cannot make an artifact twice */ if (a_ptr->cur_num) continue; /* No quest items */ if (FLAG(a_ptr, TR_QUESTITEM)) continue; /* keep track of the lowest level * rarity */ min = MIN(min, a_ptr->level * a_ptr->rarity); /* keep track of the highest level * rarity */ max = MAX(max, a_ptr->level * a_ptr->rarity); } /* All the artifacts have been found! */ if (!max) return (0); /* Find the selection condition */ rand = rand_range(min, max); /* * Select a step to go through the artifact array. That step should not * share a divider with z_info->a_max to ensure that all artifacts are * tried. (basic group theory) */ do step = randint(z_info->a_max); while (share_divider(step, z_info->a_max)); /* Randomly loop through the artifacts */ do { /* Finf the next artifact */ a_idx = (a_idx + step) % z_info->a_max; /* Make a pointer to the artifact */ a_ptr = &a_info[a_idx]; /* Skip "empty" artifacts */ if (!a_ptr->name) continue; /* Cannot make an artifact twice */ if (a_ptr->cur_num) continue; /* No quest items */ if (FLAG(a_ptr, TR_QUESTITEM)) continue; /* deliver this artifact maybe */ if (a_ptr->level * a_ptr->rarity <= rand) return (a_idx); } /* No termnation needed because there is a garantee to find an artifact */ while (TRUE); } static bool request_find_item(int dummy) { quest_type *q_ptr; /* Hack - ignore parameter */ (void) dummy; /* Try to find a artifact to quest for */ q_ptr = insert_artifact_quest(find_random_artifact()); if (!q_ptr) { msgf("You have found all the relics and still want more? Impossible."); message_flush(); /* No available quests, unfortunately. */ return (FALSE); } /* Display a helpful message. */ msgf("%s", q_ptr->name); message_flush(); /* Remember who gave us the quest */ set_quest_giver(q_ptr); /* Exit */ return (TRUE); } static bool monster_quest(const monster_race *r_ptr) { int i; /* No bounty quests for multiplying monsters */ if (FLAG(r_ptr, RF_MULTIPLY)) return (FALSE); /* No bounty to kill friendly monsters */ if (FLAG(r_ptr, RF_FRIENDLY)) return (FALSE); /* Allow silly monsters only if the silly_monster flag is set */ if (!silly_monsters && FLAG(r_ptr, RF_SILLY)) return (FALSE); /* Only "hard" monsters for quests */ if (FLAG(r_ptr, RF_NEVER_MOVE) || FLAG(r_ptr, RF_FRIENDS)) return (FALSE); /* No uniques that are already dead */ if ((FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_UNIQUE_7)) && (r_ptr->cur_num >= r_ptr->max_num)) { return (FALSE); } /* For all the quests */ for (i = 0; i < q_max; i++) { /* If there is already a bounty quest for this monster then give up */ if (quest[i].type == QUEST_TYPE_BOUNTY && &r_info[quest[i].data.bnt.r_idx] == r_ptr) return (FALSE); /* Don't accidentally quest for Oberon or the Serpent */ if (quest[i].x_type == QX_KILL_WINNER && &r_info[quest[i].data.dun.r_idx] == r_ptr) return (FALSE); } return (TRUE); } static quest_type *insert_bounty_quest(u16b r_idx, u16b num) { quest_type *q_ptr; int q_num; monster_race *r_ptr = &r_info[r_idx]; /* get a new quest */ q_num = q_pop(); /* Paranoia */ if (!q_num) return (NULL); q_ptr = &quest[q_num]; /* Bounty quest */ q_ptr->type = QUEST_TYPE_BOUNTY; /* We have taken the quest */ q_ptr->status = QUEST_STATUS_TAKEN; if (num != 1) { char buf[80]; strcpy(buf, mon_race_name(r_ptr)); plural_aux(buf); /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Kill %d %s.", num, buf); } else { /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Kill %s.", mon_race_name(r_ptr)); } /* We need to place the monster(s) when the dungeon is made */ q_ptr->c_type = QC_DUN_MONST; /* We need to trigger when the monsters are killed */ if (FLAG(r_ptr, RF_UNIQUE)) { q_ptr->x_type = QX_KILL_UNIQUE; } else { q_ptr->x_type = QX_KILL_MONST; } /* Save the quest data */ q_ptr->data.bnt.r_idx = r_idx; q_ptr->data.bnt.cur_num = 0; q_ptr->data.bnt.max_num = num; q_ptr->reward = r_ptr->level * r_ptr->level * num * 2; /* bonus reward for uniques */ if (num == 1) q_ptr->reward *= 10; /* Done */ return (q_ptr); } static bool request_bounty(int dummy) { int i; u16b num; u16b best_r_idx = 1; int best_level = 1; int r_idx; monster_race *r_ptr; quest_type *q_ptr; /* Hack - ignore parameter */ (void) dummy; /* Get monster */ for (i = 0; i < MAX_TRIES; i++) { /* * Random monster out of depth * (depending on level + number of quests) */ r_idx = get_mon_num(p_ptr->max_lev + curr_scale); r_ptr = &r_info[r_idx]; /* Look at the monster - only "hard" monsters for quests */ if (!monster_quest(r_ptr)) continue; /* Save the index if the monster is deeper than current monster */ if (!best_r_idx || (r_info[r_idx].level > best_level)) { best_r_idx = r_idx; best_level = r_info[r_idx].level; } /* Accept monsters that are a few levels out of depth */ if (best_level > p_ptr->max_lev * 2) break; } r_ptr = &r_info[best_r_idx]; /* Get the number of monsters */ if (FLAG(r_ptr, RF_UNIQUE)) { num = 1; } else if (FLAG(r_ptr, RF_UNIQUE_7)) { num = randint1(r_ptr->max_num - r_ptr->cur_num); } else { num = 5 + randint0(p_ptr->max_lev / 2) / r_ptr->rarity; } /* Generate the quest */ q_ptr = insert_bounty_quest(best_r_idx, num); if (!q_ptr) { msgf("Sorry, I don't need any bounties today."); message_flush(); /* No available quests, unfortunately. */ return (FALSE); } /* Show it on the screen? */ /* Display a helpful message. */ msgf("%s", q_ptr->name); message_flush(); /* Remember who gave us the quest */ set_quest_giver(q_ptr); /* Exit */ return (TRUE); } static quest_type *insert_message_quest(int dist) { place_type *pl_ptr; quest_type *q_ptr; store_type *st_ptr; int store; u16b place_num; /* Get a new quest */ int q_num = q_pop(); /* Paranoia */ if (!q_num) return (NULL); q_ptr = &quest[q_num]; /* Store in information */ q_ptr->type = QUEST_TYPE_MESSAGE; /* We have taken the quest */ q_ptr->status = QUEST_STATUS_TAKEN; /* We don't need any special creation operation */ q_ptr->c_type = QC_NONE; /* Finished when the player finds it */ q_ptr->x_type = QX_FIND_SHOP; /* Find a town that is roughly dist wilderness blocks away */ place_num = find_good_town(&dist); /* Get the place */ pl_ptr = &place[place_num]; /* Find a store at that town */ do { store = randint0(pl_ptr->numstores); st_ptr = &pl_ptr->store[store]; /* Want a store with an owner */ } while (st_ptr->type == BUILD_NONE || st_ptr->type == BUILD_STAIRS || st_ptr->type == BUILD_BLANK || st_ptr->type == BUILD_STORE_HOME); /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Carry a message to %s in %s.", quark_str(st_ptr->owner_name), pl_ptr->name); /* Save the quest data */ q_ptr->data.msg.shop = store; q_ptr->data.msg.place = place_num; /* Set the reward level */ q_ptr->reward = dist * 100; /* Done */ return (q_ptr); } static bool request_message(int dummy) { quest_type *q_ptr; /* Hack - ignore parameter */ (void) dummy; /* * Generate a quest to send a message to a town * roughly 20 to 50 wilderness squares away. */ q_ptr = insert_message_quest(curr_scale * 2); if (!q_ptr) { msgf("Sorry, I don't need any to send any messages today."); message_flush(); /* No available quests, unfortunately. */ return (FALSE); } /* Show it on the screen? */ /* Display a helpful message. */ msgf("%s", q_ptr->name); message_flush(); /* Remember who gave us the quest */ set_quest_giver(q_ptr); /* Exit */ return (TRUE); } static quest_type *insert_find_place_quest(void) { place_type *pl_ptr; quest_type *q_ptr; cptr town_name, town_dir; u16b place_num; int q_num; /* Find a dungeon appropriate for this player */ place_num = find_good_dungeon(2 * p_ptr->lev, FALSE); /* All dungeons have been found */ if (!place_num) return (NULL); /* Get a new quest */ q_num = q_pop(); /* Paranoia */ if (!q_num) return (NULL); q_ptr = &quest[q_num]; /* Store in information */ q_ptr->type = QUEST_TYPE_FIND_PLACE; /* We have taken the quest */ q_ptr->status = QUEST_STATUS_TAKEN; /* We don't need any special creation operation */ q_ptr->c_type = QC_NONE; /* Finished when the player finds it */ q_ptr->x_type = QX_WILD_ENTER; /* Get the place */ pl_ptr = &place[place_num]; pl_ptr->quest_num = q_num; /* Get name of closest town + direction away from it */ town_name = describe_quest_location(&town_dir, pl_ptr->x, pl_ptr->y, FALSE); /* XXX XXX Create quest name */ (void)strnfmt(q_ptr->name, 128, "Find a certain lost ruin, which is hidden %s of %s.", town_dir, town_name); q_ptr->data.fpl.place = place_num; /* Set the reward level */ q_ptr->reward = 100 * distance(pl_ptr->x, pl_ptr->y, p_ptr->wilderness_x / 16, p_ptr->wilderness_y / 16); /* Done */ return (q_ptr); } static bool request_find_place(int dummy) { quest_type *q_ptr; /* Hack - ignore parameter */ (void) dummy; /*Generate a quest to find an unknown dungeon */ q_ptr = insert_find_place_quest(); if (!q_ptr) { msgf("You've found all the ruins that I know of."); message_flush(); /* No available quests, unfortunately. */ return (FALSE); } /* Show it on the screen? */ /* Display a helpful message. */ msgf("%s", q_ptr->name); message_flush(); /* Remember who gave us the quest */ set_quest_giver(q_ptr); /* Exit */ return (TRUE); } #define QUEST_MENU_MAX 7 #define QUEST_MENU_RELIC 3 /* The quest selection menu */ static menu_type quest_menu[QUEST_MENU_MAX] = { {"To hunt down a bounty of monsters", NULL, request_bounty, MN_ACTIVE}, {"To send a message to someone far away", NULL, request_message, MN_ACTIVE}, {"To find a lost ruin", NULL, request_find_place, MN_ACTIVE}, MENU_END, MENU_END, {"To find a lost relic", NULL, request_find_item, MN_ACTIVE}, MENU_END }; void request_quest(const store_type *b_ptr, int scale) { /* Save building so we can remember the quest giver */ curr_build = b_ptr; /* Save scale so we can work out how hard to make the quest */ curr_scale = scale; /* Only allow artifact quests from large castles */ if (scale >= 20) { /* Copy this quest into the menu */ quest_menu[QUEST_MENU_RELIC] = quest_menu[QUEST_MENU_RELIC + 2]; } display_menu(quest_menu, -1, FALSE, NULL, "What type of quest would you like?"); /* Remove the artifact quest from the menu */ quest_menu[QUEST_MENU_RELIC] = quest_menu[QUEST_MENU_RELIC + 1]; } /* Show the quest status as a string */ static cptr quest_status_string(quest_type *q_ptr) { int monst_num = 0; int max_num = 0; /* Just checking */ if (!q_ptr) return (NULL); /* Surely you jest? */ if (q_ptr->type == QUEST_TYPE_NONE) return (NULL); /* Check the various statuses */ switch (q_ptr->status) { /* Unknown quest */ case QUEST_STATUS_UNTAKEN: return (NULL); /* Underway */ case QUEST_STATUS_TAKEN: { /* Count the bounty monsters */ if (q_ptr->type == QUEST_TYPE_BOUNTY) { monst_num = q_ptr->data.bnt.cur_num; max_num = q_ptr->data.bnt.max_num; } /* Count the dungeon monsters */ if (q_ptr->type == QUEST_TYPE_DUNGEON) { monst_num = q_ptr->data.dun.cur_num; max_num = q_ptr->data.dun.max_num; } /* Don't show the count for zero or one monsters */ if (max_num <= 1) return ("\n\n"); /* Tell the world */ return (format("You have killed %d.\n\n", monst_num)); } /* Report back to quest_giver */ case QUEST_STATUS_COMPLETED: { /* All done killing */ if (q_ptr->type == QUEST_TYPE_BOUNTY) return ("(Killed)\n\n"); /* All done killing */ if (q_ptr->type == QUEST_TYPE_DUNGEON) return ("(Killed)\n\n"); /* All done defeating */ if (q_ptr->type == QUEST_TYPE_WILD) return ("(Completed)\n"); /* All done delivering */ if (q_ptr->type == QUEST_TYPE_MESSAGE) return ("(Delivered)\n\n"); /* All done finding */ if (q_ptr->type == QUEST_TYPE_FIND_ITEM) return ("(Found)\n\n"); /* All done finding */ if (q_ptr->type == QUEST_TYPE_FIND_PLACE) return ("(Found)\n\n"); } /* Finnished */ case QUEST_STATUS_FINISHED: return ("(Completed)\n"); default: return ("(BUG!)\n"); } } /* * Print quest status of all active quests */ bool do_cmd_knowledge_quests(int dummy) { FILE *fff; char file_name[1024]; char tmp_str[256]; quest_type *q_ptr; int i; /* Hack - ignore parameter */ (void) dummy; /* Open a temporary file */ fff = my_fopen_temp(file_name, 1024); /* Failure */ if (!fff) return (FALSE); for (i = 0; i < q_max; i++) { q_ptr = &quest[i]; /* Do we know about it? */ if (!(q_ptr->flags & QUEST_FLAG_KNOWN)) continue; /* See what type of quest it is */ switch (q_ptr->type) { case QUEST_TYPE_NONE: { /* Paranoia */ continue; } case QUEST_TYPE_DUNGEON: { char level[20]; /* In feet, or in levels */ if (depth_in_feet) { strnfmt(level, 20, "%4dft", (int)q_ptr->data.dun.level * 50); } else { strnfmt(level, 20, "%3d", (int)q_ptr->data.dun.level); } /* Hack - assume kill n monsters of type m */ strnfmt(tmp_str, 256, "%s (Dungeon level: %s) %s", q_ptr->name, level, quest_status_string(q_ptr)); break; } case QUEST_TYPE_BOUNTY: case QUEST_TYPE_MESSAGE: case QUEST_TYPE_FIND_ITEM: case QUEST_TYPE_FIND_PLACE: case QUEST_TYPE_WILD: { /* Hack - this is simple */ strnfmt(tmp_str, 256, "%s %s", q_ptr->name, quest_status_string(q_ptr)); break; } default: { /* Paranoia */ strnfmt(tmp_str, 256, "Invalid quest type!"); } } /* Copy to the file */ froff(fff, "%s", tmp_str); } /* Close the file */ my_fclose(fff); /* Display the file contents */ (void)show_file(file_name, "Quest status", 0, 0); /* Remove the file */ (void)fd_kill(file_name); return (FALSE); } /* Dump the quests related to this town into fff, only when display is set */ void dump_castle_info(FILE *fff, int town) { int i; bool quest_in_town = FALSE; quest_type *q_ptr; /* Loop through the quests */ for (i = 0; i < z_info->q_max; i++) { /* Find a quest */ q_ptr = &quest[i]; /* Is it from this town? */ if (town != q_ptr->place) continue; /* There is a quest */ quest_in_town = TRUE; /* Show it */ froff(fff, "%s %s", q_ptr->name, quest_status_string(q_ptr)); } /* If no quest was issued */ if (!quest_in_town) { /* Say so */ froff(fff, "No quest was issued in this town.\n"); } } /* * The following functions are used to determine if the given monster * is appropriate for inclusion in a quest of the given type. * * The general selections are not allowed to include "unique" monsters. */ /* * Hack - Monster validation macro * * Line 1 -- forbid town monsters * Line 2 -- forbid uniques * Line 3 -- forbid aquatic monsters */ #define quest_monster_okay(I) \ (!FLAG(&r_info[I], RF_WILD_TOWN) && \ !FLAG(&r_info[I], RF_UNIQUE) && \ !FLAG(&r_info[I], RF_AQUATIC)) #ifdef UNUSED_FUNC /* * Helper monster selection function */ static bool quest_aux_simple(int r_idx) { /* Okay */ return (quest_monster_okay(r_idx)); } #endif /* UNUSED_FUNC */ /* * Helper function for selecting undead */ static bool quest_aux_undead(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!quest_monster_okay(r_idx)) return (FALSE); /* Require Undead */ if (!FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for selecting orcs */ static bool quest_aux_orc(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!quest_monster_okay(r_idx)) return (FALSE); /* Require orc */ if (!(FLAG(r_ptr, RF_ORC))) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for selecting trolls */ static bool quest_aux_troll(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!quest_monster_okay(r_idx)) return (FALSE); /* Require troll */ if (!(FLAG(r_ptr, RF_TROLL))) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for selecting giants */ static bool quest_aux_giant(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!quest_monster_okay(r_idx)) return (FALSE); /* Require giant */ if (!(FLAG(r_ptr, RF_GIANT))) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for selecting dragons */ static bool quest_aux_dragon(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!quest_monster_okay(r_idx)) return (FALSE); /* Require dragon */ if (!(FLAG(r_ptr, RF_DRAGON))) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } static int pick_quest_type(quest_aux_type *l_ptr, int level) { int tmp, total; quest_aux_type *n_ptr; int i; /* Calculate the total possibilities */ for (i = 0, total = 0; TRUE; i++) { n_ptr = &l_ptr[i]; /* Note end */ if (!n_ptr->hook_func) break; /* Ignore excessive depth */ if (n_ptr->level > level) continue; /* Count this possibility */ total += n_ptr->chance * MAX_DEPTH / (level - n_ptr->level + 5); } if (!total) return (-1); /* Pick a random type */ tmp = randint0(total); /* Find this type */ for (i = 0, total = 0; TRUE; i++) { n_ptr = &l_ptr[i]; /* Note end */ if (!n_ptr->hook_func) break; /* Ignore excessive depth */ if (n_ptr->level > level) continue; /* Count this possibility */ total += n_ptr->chance * MAX_DEPTH / (level - n_ptr->level + 5); /* Found the type */ if (tmp < total) break; } return (i); } static quest_aux_type camp_types[] = { {quest_aux_undead, 10, 1, "undead"}, {quest_aux_orc, 20, 2, "orc"}, {quest_aux_troll, 40, 4, "troll"}, {quest_aux_giant, 60, 1, "giant"}, {quest_aux_dragon, 80, 1, "dragon"}, {NULL, 0, 0, NULL}, }; /* * Pick a quest to use */ void pick_wild_quest(int *xsize, int *ysize, byte *flags) { /* Hack - don't worry too much now, we only have one type of quest */ /* Random size */ *xsize = randint1(5); *ysize = randint1(5); /* On normal terrain */ *flags = Q_GEN_PICKY; } /* * Look to see if a wilderness block is able to have * a quest overlayed on top. */ bool quest_blank(int x, int y, int xsize, int ysize, int place_num, byte flags) { int i, j; wild_gen2_type *w_ptr; place_type *pl_ptr = &place[place_num]; /* Hack - Population check */ if (randint0(256) > wild[y][x].trans.pop_map) return (FALSE); for (i = x - 1; i < x + xsize + 2; i++) { for (j = y - 1; j < y + ysize + 2; j++) { /* Hack - Not next to boundary */ if ((i <= 0) || (i >= max_wild - 1) || (j <= 0) || (j >= max_wild - 1)) { return (FALSE); } w_ptr = &wild[j][i].trans; /* No place already */ if (w_ptr->place) return (FALSE); /* Picky quests require "normal terrain" */ if (flags & Q_GEN_PICKY) { /* No water or lava or acid */ if (w_ptr-> info & (WILD_INFO_WATER | WILD_INFO_LAVA | WILD_INFO_ACID)) { return (FALSE); } } /* Ocean quests must be on water */ if (flags & Q_GEN_OCEAN) { /* Not on Ocean? */ if (w_ptr->hgt_map >= (256 / SEA_FRACTION)) return (FALSE); } else { /* Otherwise, Ocean is not allowed */ if (w_ptr->hgt_map < (256 / SEA_FRACTION)) return (FALSE); } } } /* Look to see if another town / quest is too close */ for (i = 1; i < place_num; i++) { if (distance(place[i].x, place[i].y, x, y) < MIN_DIST_QUEST) { /* Too close? */ return (FALSE); } } /* Save size */ pl_ptr->xsize = xsize; pl_ptr->ysize = ysize; /* Ok then */ return (TRUE); } /* * Create a quest in the wilderness */ bool create_quest(int x, int y, int place_num) { int i, j; int q_num, qtype; cptr town_name, town_dir; wild_type *w_ptr = &wild[y][x]; place_type *pl_ptr = &place[place_num]; quest_type *q_ptr; /* Select type of monster to place in the camp */ qtype = pick_quest_type(camp_types, (255 - w_ptr->trans.law_map) / 3); /* Is the area too easy for the quests? */ if (qtype == -1) return (FALSE); /* Get a new quest */ q_num = q_pop(); /* Paranoia */ if (!q_num) return (FALSE); /* Get a random seed for later */ pl_ptr->seed = randint0(0x10000000); /* Quest */ pl_ptr->type = TOWN_QUEST; pl_ptr->monst_type = TOWN_MONST_MONST; pl_ptr->x = x; pl_ptr->y = y; pl_ptr->quest_num = q_num; /* Data value is used as a counter of "active" blocks */ pl_ptr->data = 0; if ((!pl_ptr->xsize) || (!pl_ptr->ysize)) quit("Zero quest size"); /* Link wilderness to quest */ for (i = 0; i < pl_ptr->xsize; i++) { for (j = 0; j < pl_ptr->ysize; j++) { w_ptr = &wild[y + j][x + i]; /* * Add quest to wilderness * Note: only 255 can be stored currently. */ w_ptr->trans.place = (byte)place_num; /* Increment "active block" counter */ pl_ptr->data++; } } /* Set up quest */ q_ptr = &quest[q_num]; /* Store in information */ q_ptr->type = QUEST_TYPE_WILD; /* We don't need a special generator */ q_ptr->c_type = QC_NONE; /* We need to trigger when the player enters the wilderness block */ q_ptr->x_type = QX_WILD_ENTER; /* Get name and direction of closest town to quest */ town_name = describe_quest_location(&town_dir, pl_ptr->x, pl_ptr->y, FALSE); /* Create quest name */ (void)strnfmt(q_ptr->name, 128, "Defeat the %s camp %s of %s.", camp_types[qtype].name, town_dir, town_name); /* Save the quest data */ q_ptr->data.wld.place = place_num; q_ptr->data.wld.data = qtype; /* q_ptr->data.wld.depth = (255 - w_ptr->trans.law_map) / 3; */ return (TRUE); } /* * Draw the quest onto its region */ void draw_quest(place_type *pl_ptr) { int x, y, n; int i, j; wild_type *w_ptr = &wild[pl_ptr->y][pl_ptr->x]; quest_type *q_ptr = &quest[pl_ptr->quest_num]; cave_type *c_ptr; /* Object theme */ obj_theme theme; int depth = w_ptr->done.mon_gen; /* Paranoia */ if (pl_ptr->region) quit("Quest already has region during creation."); /* Get region */ create_region(pl_ptr, pl_ptr->xsize * WILD_BLOCK_SIZE, pl_ptr->ysize * WILD_BLOCK_SIZE, REGION_OVER); /* Hack - do not increment refcount here - let allocate_block do that */ /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant quest layout */ Rand_value = pl_ptr->seed; /* Apply the monster restriction */ get_mon_num_prep(camp_types[q_ptr->data.wld.data].hook_func); /* Set theme for weapons / armour */ theme.treasure = 0; theme.combat = 100; theme.magic = 0; theme.tools = 0; init_match_theme(theme); /* Prepare allocation table */ get_obj_num_prep(kind_is_theme); /* Pick number random spots within region */ n = (pl_ptr->xsize * pl_ptr->ysize) / 4; while (n != 0) { /* Decrement counter */ n--; /* Get spot */ x = randint0(pl_ptr->xsize * 2); y = randint0(pl_ptr->ysize * 2); /* Place ground */ for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { /* Get location */ c_ptr = cave_p(x * 8 + i, y * 8 + j); /* Draw a roughly circular blob */ if (randint0(distance(0, 0, i, j)) < 4) { if (one_in_(3)) { c_ptr->feat = FEAT_PEBBLES; } else { c_ptr->feat = FEAT_DIRT; } /* Place monsters on spots */ if (one_in_(QUEST_CAMP_MON)) { /* Pick a race to clone */ c_ptr->m_idx = get_mon_num(depth); } /* Place weapons + armour around the spots */ if (one_in_(QUEST_CAMP_OBJ)) { c_ptr->o_idx = get_obj_num(depth, depth / 3); } } } } } /* Set theme for junk */ theme.treasure = 5; theme.combat = 0; theme.magic = 0; theme.tools = 5; init_match_theme(theme); /* Clear allocation table */ get_obj_num_prep(kind_is_theme); /* Scatter stuff over the region */ for (i = 0; i < pl_ptr->xsize * WILD_BLOCK_SIZE; i++) { for (j = 0; j < pl_ptr->ysize * WILD_BLOCK_SIZE; j++) { /* Only on some squares */ if (!one_in_(QUEST_CAMP_SCATTER)) continue; /* Get location */ c_ptr = cave_p(i, j); /* Not on allocated squares */ if (c_ptr->feat) continue; if (one_in_(3)) { c_ptr->feat = FEAT_PEBBLES; } else { c_ptr->feat = FEAT_DIRT; } /* Place monsters on spots */ if (one_in_(QUEST_CAMP_MON)) { /* Pick a race to clone */ c_ptr->m_idx = get_mon_num(depth); /* Place junk under monsters */ if (one_in_(QUEST_CAMP_OBJ)) { c_ptr->o_idx = get_obj_num(depth, 0); } } } } /* Activate quest + we know about the quest */ q_ptr->flags |= (QUEST_FLAG_ACTIVE); /* Hack - we now take this quest */ if (q_ptr->status == QUEST_STATUS_UNTAKEN) { q_ptr->status = QUEST_STATUS_TAKEN; } /* Mega-hack Give a message if we "discover" it */ quest_discovery(); /* We know about it now */ q_ptr->flags |= QUEST_FLAG_KNOWN; /* Hack XXX XXX (No quest-giving store yet) */ /* Hack -- use the "complex" RNG */ Rand_quick = FALSE; /* Remove the monster restriction */ get_mon_num_prep(NULL); } zangband/src/racial.c0000755000000000000000000003551110250356274013542 0ustar rootroot/* File: racial.c */ /* Purpose: Racial powers (and mutations) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Returns the chance to activate a racial power/mutation */ static int racial_chance(s16b min_level, int use_stat, int difficulty) { int i; int val; int sum = 0; int stat = p_ptr->stat[use_stat].cur; if (stat <= 180) stat /= 10; else stat += 18-180; /* No chance for success */ if ((p_ptr->lev < min_level) || p_ptr->tim.confused) { return (0); } /* Calculate difficulty */ if (p_ptr->tim.stun) { difficulty += p_ptr->tim.stun; } else if (p_ptr->lev > min_level) { int lev_adj = ((p_ptr->lev - min_level) / 3); if (lev_adj > 10) lev_adj = 10; difficulty -= lev_adj; } if (difficulty < 5) difficulty = 5; /* We only need halfs of the difficulty */ difficulty = difficulty / 2; for (i = 1; i <= stat; i++) { val = i - difficulty; if (val > 0) sum += (val <= difficulty) ? val : difficulty; } if (difficulty == 0) return (100); else return (((sum * 100) / difficulty) / stat); } /* * Helper function for ghouls. * I realize it is somewhat illogical to have this as a "power" rather * than an extension of the "eat" command, but I could not think of * a handy solution to the conceptual/UI problem of having food objects AND * an edible corpse in the same square... * Eating corpses should probably take more than 1 turn (realistically). * (OTOH, you can swap your full-plate armour for a dragonscalemail in * 1 turn *shrug*) */ static void eat_corpse(void) { field_type *f_ptr; /* While there are fields in the linked list */ FLD_ITT_START (area(p_ptr->px, p_ptr->py)->fld_idx, f_ptr) { /* Want a corpse / skeleton */ if ((f_ptr->t_idx == FT_CORPSE || f_ptr->t_idx == FT_SKELETON)) { if (f_ptr->t_idx == FT_CORPSE) { msgf("The corpse tastes delicious!"); (void)set_food(p_ptr->food + 2000); } else { msgf("The bones taste delicious!"); (void)set_food(p_ptr->food + 1000); } /* Sound */ sound(SOUND_EAT); delete_field_ptr(f_ptr); /* Done */ return; } } FLD_ITT_END; /* Nothing to eat */ msgf("There is no fresh skeleton or corpse here!"); p_ptr->state.energy_use = 0; /* Done */ return; } /* * Note: return value indicates that we have succesfully used the power */ bool racial_aux(s16b min_level, int cost, int use_stat, int difficulty) { bool use_hp = FALSE; int stat = p_ptr->stat[use_stat].cur; if (stat <= 180) stat /= 10; else stat += 18-180; /* Not enough mana - use hp */ if (p_ptr->csp < cost) use_hp = TRUE; /* Power is not available yet */ if (p_ptr->lev < min_level) { msgf("You need to attain level %d to use this power.", min_level); p_ptr->state.energy_use = 0; return FALSE; } /* Too confused */ else if (p_ptr->tim.confused) { msgf("You are too confused to use this power."); p_ptr->state.energy_use = 0; return FALSE; } /* Risk death? */ else if (use_hp && (p_ptr->chp < cost)) { if (!get_check("Really use the power in your weakened state? ")) { p_ptr->state.energy_use = 0; return FALSE; } } /* Else attempt to do it! */ if (p_ptr->tim.stun) { difficulty += p_ptr->tim.stun; } else if (p_ptr->lev > min_level) { int lev_adj = ((p_ptr->lev - min_level) / 3); if (lev_adj > 10) lev_adj = 10; difficulty -= lev_adj; } if (difficulty < 5) difficulty = 5; /* take time and pay the price */ p_ptr->state.energy_use = 100; if (use_hp) { take_hit(rand_range(cost / 2, cost), "concentrating too hard"); } else { p_ptr->csp -= (s16b)rand_range(cost / 2, cost); } /* Redraw mana and hp */ p_ptr->redraw |= (PR_HP | PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER | PW_SPELL); /* Success? */ if (randint1(stat) >= rand_range(difficulty / 2, difficulty)) { return TRUE; } msgf("You've failed to concentrate hard enough."); return FALSE; } static void cmd_racial_power_aux(const mutation_type *mut_ptr) { s16b plev = p_ptr->lev; int dir = 0; if (racial_aux(mut_ptr->level, mut_ptr->cost, mut_ptr->stat, mut_ptr->diff)) { switch (p_ptr->rp.prace) { case RACE_DWARF: { msgf("You examine your surroundings."); (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); break; } case RACE_HOBBIT: { create_food(); msgf("You cook some food."); break; } case RACE_GNOME: { msgf("Blink!"); teleport_player(10 + plev); break; } case RACE_HALF_ORC: { msgf("You play tough."); (void)clear_afraid(); break; } case RACE_HALF_TROLL: { msgf("RAAAGH!"); if (!p_ptr->tim.shero) { (void)hp_player(30); } (void)clear_afraid(); (void)inc_shero(10 + randint1(plev)); break; } case RACE_AMBERITE: { /* Hack - use levels to choose ability */ if (mut_ptr->level == 40) { msgf ("You picture the Pattern in your mind and walk it..."); (void)clear_poisoned(); (void)clear_image(); (void)clear_stun(); (void)clear_cut(); (void)clear_blind(); (void)clear_afraid(); (void)do_res_stat(A_STR); (void)do_res_stat(A_INT); (void)do_res_stat(A_WIS); (void)do_res_stat(A_DEX); (void)do_res_stat(A_CON); (void)do_res_stat(A_CHR); (void)restore_level(); } else if (mut_ptr->level == 30) { msgf("You start walking around. Your surroundings change."); /* Leaving */ p_ptr->state.leaving = TRUE; } break; } case RACE_BARBARIAN: { msgf("Raaagh!"); if (!p_ptr->tim.shero) { (void)hp_player(30); } (void)clear_afraid(); (void)inc_shero(10 + randint1(plev)); break; } case RACE_HALF_OGRE: { msgf("You carefully set an explosive rune..."); (void)explosive_rune(); break; } case RACE_HALF_GIANT: { if (!get_aim_dir(&dir)) break; msgf("You bash at a stone wall."); (void)wall_to_mud(dir); break; } case RACE_HALF_TITAN: { msgf("You examine your foes..."); (void)probing(); break; } case RACE_CYCLOPS: { if (!get_aim_dir(&dir)) break; msgf("You throw a huge boulder."); (void)fire_bolt(GF_MISSILE, dir, (3 * plev) / 2); break; } case RACE_YEEK: { if (!get_aim_dir(&dir)) break; msgf("You make a horrible scream!"); (void)fear_monster(dir, plev); break; } case RACE_KLACKON: { if (!get_aim_dir(&dir)) break; msgf("You spit acid."); if (plev < 25) (void)fire_bolt(GF_ACID, dir, plev); else (void)fire_ball(GF_ACID, dir, plev, 2); break; } case RACE_KOBOLD: { if (!get_aim_dir(&dir)) break; msgf("You throw a dart of poison."); (void)fire_bolt(GF_POIS, dir, plev); break; } case RACE_NIBELUNG: { msgf("You examine your surroundings."); (void)detect_traps(TRUE); (void)detect_doors(); (void)detect_stairs(); break; } case RACE_DARK_ELF: { if (!get_aim_dir(&dir)) break; msgf("You cast a magic missile."); (void)fire_bolt_or_beam(10, GF_MISSILE, dir, damroll(3 + ((plev - 1) / 5), 4)); break; } case RACE_DRACONIAN: { int Type = (one_in_(3) ? GF_COLD : GF_FIRE); cptr Type_desc = ((Type == GF_COLD) ? "cold" : "fire"); if (randint1(100) < plev) { switch (p_ptr->rp.pclass) { case CLASS_WARRIOR: case CLASS_RANGER: if (one_in_(3)) { Type = GF_MISSILE; Type_desc = "the elements"; } else { Type = GF_SHARDS; Type_desc = "shards"; } break; case CLASS_MAGE: case CLASS_WARRIOR_MAGE: case CLASS_HIGH_MAGE: if (one_in_(3)) { Type = GF_MANA; Type_desc = "mana"; } else { Type = GF_DISENCHANT; Type_desc = "disenchantment"; } break; case CLASS_CHAOS_WARRIOR: if (!one_in_(3)) { Type = GF_CONFUSION; Type_desc = "confusion"; } else { Type = GF_CHAOS; Type_desc = "chaos"; } break; case CLASS_MONK: if (!one_in_(3)) { Type = GF_CONFUSION; Type_desc = "confusion"; } else { Type = GF_SOUND; Type_desc = "sound"; } break; case CLASS_MINDCRAFTER: if (!one_in_(3)) { Type = GF_CONFUSION; Type_desc = "confusion"; } else { Type = GF_PSI; Type_desc = "mental energy"; } break; case CLASS_PRIEST: case CLASS_PALADIN: if (one_in_(3)) { Type = GF_HELL_FIRE; Type_desc = "hellfire"; } else { Type = GF_HOLY_FIRE; Type_desc = "holy fire"; } break; case CLASS_ROGUE: if (one_in_(3)) { Type = GF_DARK; Type_desc = "darkness"; } else { Type = GF_POIS; Type_desc = "poison"; } break; } } if (!get_aim_dir(&dir)) break; msgf("You breathe %s.", Type_desc); (void)fire_ball(Type, dir, plev * 2, (plev / 15) + 1); break; } case RACE_MIND_FLAYER: { if (!get_aim_dir(&dir)) break; else { msgf("You concentrate and your eyes glow red..."); (void)fire_bolt(GF_PSI, dir, plev); } break; } case RACE_IMP: { if (!get_aim_dir(&dir)) break; if (plev >= 30) { msgf("You cast a ball of fire."); (void)fire_ball(GF_FIRE, dir, plev, 2); } else { msgf("You cast a bolt of fire."); (void)fire_bolt(GF_FIRE, dir, plev); } break; } case RACE_GOLEM: { (void)inc_shield(rand_range(30, 50)); break; } case RACE_SKELETON: case RACE_ZOMBIE: { msgf("You attempt to restore your lost energies."); (void)restore_level(); break; } case RACE_VAMPIRE: { int y, x, dummy; cave_type *c_ptr; /* Only works on adjacent monsters */ if (!get_rep_dir(&dir)) break; y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* Paranoia */ if (!in_bounds2(x, y)) break; c_ptr = area(x, y); if (!c_ptr->m_idx) { msgf("You bite into thin air!"); break; } msgf("You grin and bare your fangs..."); dummy = plev + randint1(plev) * MAX(1, plev / 10); /* Dmg */ if (drain_gain_life(dir, dummy)) { /* Gain nutritional sustenance: 150/hp drained */ /* A Food ration gives 5000 food points (by contrast) */ /* Don't ever get more than "Full" this way */ /* But if we ARE Gorged, it won't cure us */ dummy = p_ptr->food + MIN(5000, 100 * dummy); if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy); } else msgf("Yechh. That tastes foul."); break; } case RACE_SPECTRE: { msgf("You emit an eldritch howl!"); if (!get_aim_dir(&dir)) break; (void)fear_monster(dir, plev); break; } case RACE_SPRITE: { msgf("You throw some magic dust..."); if (plev < 25) (void)sleep_monsters_touch(); else (void)sleep_monsters(); break; } case RACE_GHOUL: { if (mut_ptr->level == 30) { /* Sense living */ (void)detect_monsters_living(); } else { eat_corpse(); } break; } default: msgf("This race has no bonus power."); p_ptr->state.energy_use = 0; } } /* Redraw mana and hp */ p_ptr->redraw |= (PR_HP | PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER | PW_SPELL); } /* * Header for racial menu */ static int display_racial_header(int num) { /* Print header(s) */ if (num < 18) prtf(0, 2, " Lv Cost Fail"); else prtf(0, 2, " Lv Cost Fail Lv Cost Fail"); /* Move the options down one row */ return (1); } typedef struct power_desc_type power_desc_type; struct power_desc_type { cptr name; int level; int cost; int fail; int number; const mutation_type *power; }; static power_desc_type power_desc[36]; /* * Access the power and use it. */ static bool do_cmd_power_aux(int num) { if (power_desc[num].number == -1) { /* A racial power */ cmd_racial_power_aux(power_desc[num].power); } else { /* A mutation */ mutation_power_aux(power_desc[num].power); } /* Exit the menu */ return (TRUE); } /* * Allow user to choose a power (racial / mutation) to activate */ void do_cmd_racial_power(void) { menu_type racial_menu[37]; int num = 0, i = 0; char buf[1024]; const mutation_type *mut_ptr; /* Not when we're confused */ if (p_ptr->tim.confused) { msgf("You are too confused to use any powers!"); p_ptr->state.energy_use = 0; return; } /* Look for racial powers */ for (i = 0; i < MAX_RACE_POWERS; i++) { mut_ptr = &race_powers[i]; if (mut_ptr->which == p_ptr->rp.prace) { power_desc[num].name = mut_ptr->name; power_desc[num].level = mut_ptr->level; power_desc[num].cost = mut_ptr->cost; power_desc[num].fail = 100 - racial_chance(mut_ptr->level, mut_ptr->stat, mut_ptr->diff); power_desc[num].number = -1; power_desc[num].power = mut_ptr; num++; } } /* Look for appropriate mutations */ for (i = 0; i < MUT_PER_SET; i++) { mut_ptr = &mutations[i]; if (p_ptr->muta1 & mut_ptr->which) { power_desc[num].name = mut_ptr->name; power_desc[num].level = mut_ptr->level; power_desc[num].cost = mut_ptr->cost; power_desc[num].fail = 100 - racial_chance(mut_ptr->level, mut_ptr->stat, mut_ptr->diff); power_desc[num].number = mut_ptr->which; power_desc[num].power = mut_ptr; num++; } } /* Not if we don't have any */ if (num == 0) { msgf("You have no powers to activate."); p_ptr->state.energy_use = 0; return; } /* Initialise the options for the menu */ for (i = 0; i < num; i++) { strnfmt(buf, 1024, "%-23.23s %2d %4d %3d%%", power_desc[i].name, power_desc[i].level, power_desc[i].cost, power_desc[i].fail); /* Add option to menu */ racial_menu[i].text = string_make(buf); racial_menu[i].help = NULL; racial_menu[i].action = do_cmd_power_aux; racial_menu[i].flags = MN_ACTIVE | MN_CLEAR; } /* Make sure the menu is terminated */ racial_menu[num].text = NULL; racial_menu[num].help = NULL; racial_menu[num].action = NULL; racial_menu[num].flags = 0x00; if (!display_menu(racial_menu, -1, FALSE, display_racial_header, "Use which power?")) { /* We aborted */ p_ptr->state.energy_use = 0; } /* Free the allocated strings */ for (i = 0; i < num; i++) { string_free(racial_menu[i].text); } } zangband/src/readdib.c0000755000000000000000000002066310250356274013703 0ustar rootroot/* File: readdib.c */ /* * This package provides a routine to read a DIB file and set up the * device dependent version of the image. * * This file has been modified for use with "Angband 2.9.2" * * COPYRIGHT: * * (C) Copyright Microsoft Corp. 1993. All rights reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. */ #include #include "readdib.h" /* * Extract the "WIN32" flag from the compiler */ #if defined(__WIN32__) || defined(__WINNT__) || defined(__NT__) # ifndef WIN32 # define WIN32 # endif #endif /* * Needed for lcc-win32 */ #ifndef SEEK_SET #define SEEK_SET 0 #endif /* * Number of bytes to be read during each read operation */ #define MAXREAD 32768 /* * Private routine to read more than 64K at a time * * Reads data in steps of 32k till all the data has been read. * * Returns number of bytes requested, or zero if something went wrong. */ static DWORD PASCAL lread(HFILE fh, VOID FAR *pv, DWORD ul) { DWORD ulT = ul; BYTE *hp = pv; while (ul > (DWORD)MAXREAD) { if (_lread(fh, (LPSTR)hp, (WORD)MAXREAD) != MAXREAD) return 0; ul -= MAXREAD; hp += MAXREAD; } if (_lread(fh, (LPSTR)hp, (WORD)ul) != (WORD)ul) return 0; return ulT; } /* * Given a BITMAPINFOHEADER, create a palette based on the color table. * * Returns the handle of a palette, or zero if something went wrong. */ static HPALETTE PASCAL NEAR MakeDIBPalette(LPBITMAPINFOHEADER lpInfo) { PLOGPALETTE npPal; RGBQUAD FAR *lpRGB; HPALETTE hLogPal; DWORD i; /* * since biClrUsed field was filled during the loading of the DIB, * we know it contains the number of colors in the color table. */ if (lpInfo->biClrUsed) { npPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) + (WORD)lpInfo->biClrUsed * sizeof(PALETTEENTRY)); if (!npPal) return (FALSE); npPal->palVersion = 0x300; npPal->palNumEntries = (WORD)lpInfo->biClrUsed; /* get pointer to the color table */ lpRGB = (RGBQUAD FAR *)((LPSTR)lpInfo + lpInfo->biSize); /* copy colors from the color table to the LogPalette structure */ for (i = 0; i < lpInfo->biClrUsed; i++, lpRGB++) { npPal->palPalEntry[i].peRed = lpRGB->rgbRed; npPal->palPalEntry[i].peGreen = lpRGB->rgbGreen; npPal->palPalEntry[i].peBlue = lpRGB->rgbBlue; npPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; } hLogPal = CreatePalette((LPLOGPALETTE)npPal); LocalFree((HANDLE)npPal); return (hLogPal); } /* * 24-bit DIB with no color table. return default palette. Another * option would be to create a 256 color "rainbow" palette to provide * some good color choices. */ else { return (GetStockObject(DEFAULT_PALETTE)); } } /* * Given a DIB, create a bitmap and corresponding palette to be used for a * device-dependent representation of the image. * * Returns TRUE on success (phPal and phBitmap are filled with appropriate * handles. Caller is responsible for freeing objects) and FALSE on failure * (unable to create objects, both pointer are invalid). */ static BOOL NEAR PASCAL MakeBitmapAndPalette(HDC hDC, HANDLE hDIB, HPALETTE * phPal, HBITMAP * phBitmap) { LPBITMAPINFOHEADER lpInfo; BOOL result = FALSE; HBITMAP hBitmap; HPALETTE hPalette, hOldPal; LPSTR lpBits; lpInfo = (LPBITMAPINFOHEADER) GlobalLock(hDIB); if ((hPalette = MakeDIBPalette(lpInfo)) != 0) { /* Need to realize palette for converting DIB to bitmap. */ hOldPal = SelectPalette(hDC, hPalette, TRUE); RealizePalette(hDC); lpBits = ((LPSTR)lpInfo + (WORD)lpInfo->biSize + (WORD)lpInfo->biClrUsed * sizeof(RGBQUAD)); hBitmap = CreateDIBitmap(hDC, lpInfo, CBM_INIT, lpBits, (LPBITMAPINFO)lpInfo, DIB_RGB_COLORS); SelectPalette(hDC, hOldPal, TRUE); RealizePalette(hDC); if (!hBitmap) { DeleteObject(hPalette); } else { *phBitmap = hBitmap; *phPal = hPalette; result = TRUE; } } return (result); } /* * Reads a DIB from a file, obtains a handle to its BITMAPINFO struct, and * loads the DIB. Once the DIB is loaded, the function also creates a bitmap * and palette out of the DIB for a device-dependent form. * * Returns TRUE if the DIB is loaded and the bitmap/palette created, in which * case, the DIBINIT structure pointed to by pInfo is filled with the appropriate * handles, and FALSE if something went wrong. */ BOOL ReadDIB(HWND hWnd, LPSTR lpFileName, DIBINIT *pInfo) { HFILE fh; LPBITMAPINFOHEADER lpbi; OFSTRUCT of; BITMAPFILEHEADER bf; WORD nNumColors; BOOL result = FALSE; DWORD offBits; HDC hDC; BOOL bCoreHead = FALSE; /* Open the file and get a handle to it's BITMAPINFO */ fh = OpenFile(lpFileName, &of, OF_READ); if (fh == -1) return (FALSE); pInfo->hDIB = GlobalAlloc(GHND, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); if (!pInfo->hDIB) return (FALSE); lpbi = (LPBITMAPINFOHEADER)GlobalLock(pInfo->hDIB); /* read the BITMAPFILEHEADER */ if (sizeof (bf) != _lread(fh, (LPSTR)&bf, sizeof(bf))) goto ErrExit; /* 'BM' */ if (bf.bfType != 0x4d42) goto ErrExit; if (sizeof(BITMAPCOREHEADER) != _lread(fh, (LPSTR)lpbi, sizeof(BITMAPCOREHEADER))) goto ErrExit; if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) { lpbi->biSize = sizeof(BITMAPINFOHEADER); lpbi->biBitCount = ((LPBITMAPCOREHEADER)lpbi)->bcBitCount; lpbi->biPlanes = ((LPBITMAPCOREHEADER)lpbi)->bcPlanes; lpbi->biHeight = ((LPBITMAPCOREHEADER)lpbi)->bcHeight; lpbi->biWidth = ((LPBITMAPCOREHEADER)lpbi)->bcWidth; bCoreHead = TRUE; } else { /* get to the start of the header and read INFOHEADER */ _llseek(fh, sizeof(BITMAPFILEHEADER), SEEK_SET); if (sizeof(BITMAPINFOHEADER) != _lread(fh, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) goto ErrExit; } if (!(nNumColors = (WORD)lpbi->biClrUsed)) { /* no color table for 24-bit, default size otherwise */ if (lpbi->biBitCount != 24) nNumColors = 1 << lpbi->biBitCount; } /* fill in some default values if they are zero */ if (lpbi->biClrUsed == 0) lpbi->biClrUsed = nNumColors; if (lpbi->biSizeImage == 0) { lpbi->biSizeImage = (((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight); } /* otherwise wouldn't work with 16 color bitmaps -- S.K. */ else if ((nNumColors == 16) && (lpbi->biSizeImage > bf.bfSize)) { lpbi->biSizeImage /= 2; } /* get a proper-sized buffer for header, color table and bits */ GlobalUnlock(pInfo->hDIB); pInfo->hDIB = GlobalReAlloc(pInfo->hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0); /* can't resize buffer for loading */ if (!pInfo->hDIB) goto ErrExit2; lpbi = (LPBITMAPINFOHEADER)GlobalLock(pInfo->hDIB); /* read the color table */ if (!bCoreHead) { _lread(fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); } else { signed int i; RGBQUAD FAR *pQuad; RGBTRIPLE FAR *pTriple; _lread(fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBTRIPLE)); pQuad = (RGBQUAD FAR *)((LPSTR)lpbi + lpbi->biSize); pTriple = (RGBTRIPLE FAR *) pQuad; for (i = nNumColors - 1; i >= 0; i--) { pQuad[i].rgbRed = pTriple[i].rgbtRed; pQuad[i].rgbBlue = pTriple[i].rgbtBlue; pQuad[i].rgbGreen = pTriple[i].rgbtGreen; pQuad[i].rgbReserved = 0; } } /* offset to the bits from start of DIB header */ offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); if (bf.bfOffBits != 0L) { _llseek(fh,bf.bfOffBits,SEEK_SET); } /* Use local version of '_lread()' above */ if (lpbi->biSizeImage == lread(fh, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) { GlobalUnlock(pInfo->hDIB); hDC = GetDC(hWnd); if (!MakeBitmapAndPalette(hDC, pInfo->hDIB, &(pInfo->hPalette), &(pInfo->hBitmap))) { ReleaseDC(hWnd,hDC); goto ErrExit2; } else { ReleaseDC(hWnd,hDC); result = TRUE; } } else { ErrExit: GlobalUnlock(pInfo->hDIB); ErrExit2: GlobalFree(pInfo->hDIB); } _lclose(fh); return (result); } /* Free a DIB */ void FreeDIB(DIBINIT *dib) { /* Free the bitmap stuff */ if (dib->hPalette) DeleteObject(dib->hPalette); if (dib->hBitmap) DeleteObject(dib->hBitmap); if (dib->hDIB) GlobalFree(dib->hDIB); dib->hPalette = NULL; dib->hBitmap = NULL; dib->hDIB = NULL; } zangband/src/rooms.c0000644000000000000000000032675210250356274013455 0ustar rootroot/* * File: rooms.c * Purpose: make rooms. Used by generate.c when creating dungeons. */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "generate.h" #include "grid.h" #include "rooms.h" /* * This funtion makes a very small room centred at (x0, y0) * This is used in crypts, and random elemental vaults. * * Note - this should be used only on allocated regions * within another room. */ static void build_small_room(int x0, int y0) { /* Generate outer walls */ generate_draw(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FEAT_WALL_INNER); /* Place a secret door on one side */ generate_door(x0 - 1, y0 - 1, x0 + 1, y0 + 1, TRUE); /* Add inner open space */ set_feat_bold(x0, y0, dun->feat_floor); } /* * This function tunnels around a room if * it will cut off part of a cave system. */ static void check_room_boundary(int x1, int y1, int x2, int y2) { int count, x, y; bool old_is_floor, new_is_floor; /* Initialize */ count = 0; old_is_floor = get_is_floor(x1 - 1, y1); /* * Count the number of floor-wall boundaries around the room * Note: diagonal squares are ignored since the player can move diagonally * to bypass these if needed. */ /* Above the top boundary */ for (x = x1; x <= x2; x++) { new_is_floor = get_is_floor(x, y1 - 1); /* Increment counter if they are different */ if (new_is_floor != old_is_floor) count++; old_is_floor = new_is_floor; } /* Right boundary */ for (y = y1; y <= y2; y++) { new_is_floor = get_is_floor(x2 + 1, y); /* increment counter if they are different */ if (new_is_floor != old_is_floor) count++; old_is_floor = new_is_floor; } /* Bottom boundary */ for (x = x2; x >= x1; x--) { new_is_floor = get_is_floor(x, y2 + 1); /* increment counter if they are different */ if (new_is_floor != old_is_floor) count++; old_is_floor = new_is_floor; } /* Left boundary */ for (y = y2; y >= y1; y--) { new_is_floor = get_is_floor(x1 - 1, y); /* increment counter if they are different */ if (new_is_floor != old_is_floor) count++; old_is_floor = new_is_floor; } /* If all the same, or only one connection exit. */ if (count <= 2) return; /* Tunnel around the room so to prevent problems with caves */ for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { set_floor(x, y); } } } /* * This function is used to allocate the space needed by a room in the room_map * array. * x, y represent the size of the room (0...x-1) by (0...y-1). * crowded is used to denote a monset nest. * by0, bx0 are the positions in the room_map array given to the build_type'x' * function. * xx, yy are the returned center of the allocated room in coordinates for * cave.feat and cave.info etc. */ static bool room_alloc(int x, int y, bool crowded, int bx0, int by0, int *xx, int *yy) { int temp, bx1, bx2, by1, by2, by, bx; /* Calculate number of room_map squares to allocate */ /* temp is total number along width */ temp = ((x - 1) / BLOCK_WID) + 1; /* bx2 = ending block */ bx2 = temp / 2 + bx0; /* bx1 = starting block (Note: rounding taken care of here.) */ bx1 = bx2 + 1 - temp; /* temp is total number along height */ temp = ((y - 1) / BLOCK_HGT) + 1; /* by2 = ending block */ by2 = temp / 2 + by0; /* by1 = starting block */ by1 = by2 + 1 - temp; /* Never run off the screen */ if ((by1 < 0) || (by2 >= dun->row_rooms)) return (FALSE); if ((bx1 < 0) || (bx2 >= dun->col_rooms)) return (FALSE); /* Verify open space */ for (by = by1; by <= by2; by++) { for (bx = bx1; bx <= bx2; bx++) { if (dun->room_map[by][bx]) return (FALSE); } } /* It is *extremely* important that the following calculation */ /* be *exactly* correct to prevent memory errors XXX XXX XXX */ /* Acquire the location of the room */ *yy = ((by1 + by2 + 1) * BLOCK_HGT) / 2; *xx = ((bx1 + bx2 + 1) * BLOCK_WID) / 2; /* Save the room location */ if (dun->cent_n < CENT_MAX) { dun->cent[dun->cent_n].y = *yy; dun->cent[dun->cent_n].x = *xx; dun->cent_n++; } /* Reserve some blocks */ for (by = by1; by <= by2; by++) { for (bx = bx1; bx <= bx2; bx++) { dun->room_map[by][bx] = TRUE; } } /* Count "crowded" rooms */ if (crowded) dun->crowded++; /* * Hack- See if room will cut off a cavern. * If so, fix by tunneling outside the room in such a way as to connect the caves. */ check_room_boundary(*xx - x / 2 - 1, *yy - y / 2 - 1, *xx + (x - 1) / 2 + 1, *yy + (y - 1) / 2 + 1); /* Success */ return (TRUE); } /* * Room building routines. */ /* * Type 1 -- normal rectangular rooms */ static void build_type1(int bx0, int by0) { int y, x, y2, x2, yval, xval; int y1, x1, xsize, ysize; bool light; /* Pick a room size */ y1 = randint1(4); x1 = randint1(11); y2 = randint1(3); x2 = randint1(11); xsize = x1 + x2 + 1; ysize = y1 + y2 + 1; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 2, ysize + 2, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get corner values */ y1 = yval - ysize / 2; x1 = xval - xsize / 2; y2 = yval + (ysize - 1) / 2; x2 = xval + (xsize - 1) / 2; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Hack -- Occasional pillar room */ if (one_in_(20)) { for (y = y1; y <= y2; y += 2) { for (x = x1; x <= x2; x += 2) { set_feat_bold(x, y, FEAT_PILLAR); } } } /* Hack -- Occasional room with four pillars */ else if (one_in_(40)) { if ((y1 + 4 < y2) && (x1 + 4 < x2)) { set_feat_bold(x1 + 1, y1 + 1, FEAT_PILLAR); set_feat_bold(x2 - 1, y1 + 1, FEAT_PILLAR); set_feat_bold(x1 + 1, y2 - 1, FEAT_PILLAR); set_feat_bold(x2 - 1, y2 - 1, FEAT_PILLAR); } } /* Hack -- Occasional room with rounded corners */ else if (one_in_(40)) { set_feat_bold(x1, y1, FEAT_WALL_INNER); set_feat_bold(x2, y1, FEAT_WALL_INNER); set_feat_bold(x1, y2, FEAT_WALL_INNER); set_feat_bold(x2, y2, FEAT_WALL_INNER); } /* Hack -- Occasional ragged-edge room */ else if (one_in_(50)) { for (y = y1 + 2; y <= y2 - 2; y += 2) { set_feat_bold(x1, y, FEAT_PILLAR); set_feat_bold(x2, y, FEAT_PILLAR); } for (x = x1 + 2; x <= x2 - 2; x += 2) { set_feat_bold(x, y1, FEAT_PILLAR); set_feat_bold(x, y2, FEAT_PILLAR); } } /* Hack -- Occasional divided room */ else if (one_in_(50)) { if (one_in_(2)) { /* Horizontal wall */ for (x = x1; x <= x2; x++) { set_feat_bold(x, yval, FEAT_WALL_INNER); } /* Prevent edge of wall from being tunneled */ set_feat_bold(x1 - 1, yval, FEAT_WALL_SOLID); set_feat_bold(x2 + 1, yval, FEAT_WALL_SOLID); } else { /* Vertical wall */ for (y = y1; y <= y2; y++) { set_feat_bold(xval, y, FEAT_WALL_INNER); } /* Prevent edge of wall from being tunneled */ set_feat_bold(xval, y1 - 1, FEAT_WALL_SOLID); set_feat_bold(xval, y2 + 1, FEAT_WALL_SOLID); } place_random_door(xval, yval); } } /* * Type 2 -- Overlapping rectangular rooms */ static void build_type2(int bx0, int by0) { int xval, yval; int y1, x1, y2, x2; bool light; int num, i; #define MAX_ROOM_OVERLAY 4 coord uleft[MAX_ROOM_OVERLAY]; coord lright[MAX_ROOM_OVERLAY]; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Determine number of rooms to overlay */ num = rand_range(2, MAX_ROOM_OVERLAY); /* Make num rooms */ for (i = 0; i < num; i++) { /* Determine extents of a room */ y1 = yval - randint0(5); y2 = yval + randint0(4); x1 = xval - randint0(12); x2 = xval + randint0(11); /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Save the bounds for later */ uleft[i].x = x1; uleft[i].y = y1; lright[i].x = x2; lright[i].y = y2; } /* Make num rooms */ for (i = 0; i < num; i++) { /* Generate inner floors */ generate_fill(uleft[i].x, uleft[i].y, lright[i].x, lright[i].y, dun->feat_floor); } } /* * Type 3 -- Cross shaped rooms * * Builds a room at a row, column coordinate * * Room "a" runs north/south, and Room "b" runs east/east * So the "central pillar" runs from x1a, y1b to x2a, y2b. * * Note that currently, the "center" is always 3x3, but I think that * the code below will work (with "bounds checking") for 5x5, or even * for unsymetric values like 4x3 or 5x3 or 3x4 or 3x5, or even larger. */ static void build_type3(int bx0, int by0) { int y, x, dy, dx, wy, wx; int y1a, x1a, y2a, x2a; int y1b, x1b, y2b, x2b; int yval, xval; bool light; /* Try to allocate space for room. */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* For now, always 3x3 */ wx = wy = 1; /* Pick max vertical size (at most 4) */ dy = rand_range(3, 4); /* Pick max horizontal size (at most 15) */ dx = rand_range(3, 11); /* Determine extents of the north/south room */ y1a = yval - dy; y2a = yval + dy; x1a = xval - wx; x2a = xval + wx; /* Determine extents of the east/west room */ y1b = yval - wy; y2b = yval + wy; x1b = xval - dx; x2b = xval + dx; /* Generate new room (a) */ generate_room(x1a - 1, y1a - 1, x2a + 1, y2a + 1, light); /* Generate new room (b) */ generate_room(x1b - 1, y1b - 1, x2b + 1, y2b + 1, light); /* Generate outer walls (a) */ generate_draw(x1a - 1, y1a - 1, x2a + 1, y2a + 1, FEAT_WALL_OUTER); /* Generate outer walls (b) */ generate_draw(x1b - 1, y1b - 1, x2b + 1, y2b + 1, FEAT_WALL_OUTER); /* Generate inner floors (a) */ generate_fill(x1a, y1a, x2a, y2a, dun->feat_floor); /* Generate inner floors (b) */ generate_fill(x1b, y1b, x2b, y2b, dun->feat_floor); /* Special features (3/4) */ switch (randint1(4)) { case 1: { /* Nothing */ break; } case 2: { /* Large solid middle pillar */ /* Generate a small inner solid pillar */ generate_fill(x1b, y1a, x2b, y2a, FEAT_WALL_INNER); break; } case 3: { /* Inner treasure vault */ /* Generate a small inner vault */ generate_draw(x1b, y1a, x2b, y2a, FEAT_WALL_INNER); /* Open the inner vault with a secret door */ generate_door(x1b, y1a, x2b, y2a, TRUE); /* Place a treasure in the vault */ place_object(xval, yval, FALSE, FALSE, 0); /* Let's guard the treasure well */ vault_monsters(xval, yval, rand_range(3, 4)); /* Traps naturally */ vault_traps(xval, yval, 4, 4, rand_range(2, 4)); break; } case 4: { /* Something else */ /* Occasionally pinch the center shut */ if (one_in_(3)) { /* Pinch the east/west sides */ for (y = y1b; y <= y2b; y++) { if (y == yval) continue; set_feat_bold(x1a - 1, y, FEAT_WALL_INNER); set_feat_bold(x2a + 1, y, FEAT_WALL_INNER); } /* Pinch the north/south sides */ for (x = x1a; x <= x2a; x++) { if (x == xval) continue; set_feat_bold(x, y1b - 1, FEAT_WALL_INNER); set_feat_bold(x, y2b + 1, FEAT_WALL_INNER); } /* Sometimes shut using secret doors */ if (one_in_(3)) { place_secret_door(x1a - 1, yval); place_secret_door(x2a + 1, yval); place_secret_door(xval, y1b - 1); place_secret_door(xval, y2b + 1); } } /* Occasionally put a "plus" in the center */ else if (one_in_(3)) { generate_plus(x1b, y1a, x2b, y2a, FEAT_WALL_INNER); } /* Occasionally put a pillar in the center */ else if (one_in_(3)) { set_feat_bold(xval, yval, FEAT_PILLAR); } break; } } } /* * Type 4 -- Large room with inner features * * Possible sub-types: * 1 - Just an inner room with one door * 2 - An inner room within an inner room * 3 - An inner room with pillar(s) * 4 - Inner room has a maze * 5 - A set of four inner rooms */ static void build_type4(int bx0, int by0) { int y, x, y1, x1; int y2, x2, tmp, yval, xval; bool light; /* Try to allocate space for room. */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Large room */ y1 = yval - 4; y2 = yval + 4; x1 = xval - 11; x2 = xval + 11; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* The inner room */ y1 = y1 + 2; y2 = y2 - 2; x1 = x1 + 2; x2 = x2 - 2; /* Generate inner walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_INNER); /* Inner room variations */ switch (randint1(5)) { case 1: { /* Just an inner room with a monster */ /* Open the inner room with a secret door */ generate_door(x1 - 1, y1 - 1, x2 + 1, y2 + 1, TRUE); /* Place a monster in the room */ vault_monsters(xval, yval, 1); break; } case 2: { /* Treasure Vault (with a door) */ /* Open the inner room with a secret door */ generate_door(x1 - 1, y1 - 1, x2 + 1, y2 + 1, TRUE); /* Place another inner room */ generate_draw(xval - 1, yval - 1, xval + 1, yval + 1, FEAT_WALL_INNER); /* Open the inner room with a door */ generate_door(xval - 1, yval - 1, xval + 1, yval + 1, FALSE); /* Monsters to guard the "treasure" */ vault_monsters(xval, yval, rand_range(2, 4)); /* Object (80%) */ if (randint0(100) < 80) { place_object(xval, yval, FALSE, FALSE, 0); } /* Stairs (20%) */ else { place_random_stairs(xval, yval); } /* Traps to protect the treasure */ vault_traps(xval, yval, 10, 4, rand_range(3, 6)); break; } case 3: { /* Inner pillar(s). */ /* Open the inner room with a secret door */ generate_door(x1 - 1, y1 - 1, x2 + 1, y2 + 1, TRUE); /* Inner pillar */ generate_fill(xval - 1, yval - 1, xval + 1, yval + 1, FEAT_WALL_INNER); /* Occasionally, two more Large Inner Pillars */ if (one_in_(2)) { tmp = randint1(2); /* Inner pillar */ generate_fill(xval - 6 - tmp, yval - 1, xval - 4 - tmp, yval + 1, FEAT_WALL_INNER); /* Inner pillar */ generate_fill(xval + 4 + tmp, yval - 1, xval + 6 + tmp, yval + 1, FEAT_WALL_INNER); } /* Occasionally, some Inner rooms */ if (one_in_(3)) { /* Inner rectangle */ generate_draw(xval - 5, yval - 1, xval + 5, yval + 1, FEAT_WALL_INNER); /* Secret doors (random top/bottom) */ place_secret_door(xval - 3, yval - 3 + (randint1(2) * 2)); place_secret_door(xval + 3, yval - 3 + (randint1(2) * 2)); /* Monsters */ vault_monsters(xval - 2, yval, randint1(2)); vault_monsters(xval + 2, yval, randint1(2)); /* Objects */ if (one_in_(3)) place_object(xval - 2, yval, FALSE, FALSE, 0); if (one_in_(3)) place_object(xval + 2, yval, FALSE, FALSE, 0); } break; } case 4: { /* Maze inside. */ /* Open the inner room with a secret door */ generate_door(x1 - 1, y1 - 1, x2 + 1, y2 + 1, TRUE); /* Maze (really a checkerboard) */ for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { if (0x1 & (x + y)) { set_feat_bold(x, y, FEAT_WALL_INNER); } } } /* Monsters just love mazes. */ vault_monsters(xval - 5, yval, randint1(3)); vault_monsters(xval + 5, yval, randint1(3)); /* Traps make them entertaining. */ vault_traps(xval - 3, yval, 8, 2, randint1(3)); vault_traps(xval + 3, yval, 8, 2, randint1(3)); /* Mazes should have some treasure too. */ vault_objects(xval, yval, 3); break; } case 5: { /* Four small rooms. */ /* Inner "cross" */ generate_plus(x1, y1, x2, y2, FEAT_WALL_INNER); /* Doors into the rooms */ if (randint0(100) < 50) { int i = randint1(10); place_secret_door(xval - i, y1 - 1); place_secret_door(xval + i, y1 - 1); place_secret_door(xval - i, y2 + 1); place_secret_door(xval + i, y2 + 1); } else { int i = randint1(3); place_secret_door(x1 - 1, yval + i); place_secret_door(x1 - 1, yval - i); place_secret_door(x2 + 1, yval + i); place_secret_door(x2 + 1, yval - i); } /* Treasure, centered at the center of the cross */ vault_objects(xval, yval, rand_range(3, 4)); /* Gotta have some monsters. */ vault_monsters(xval - 4, yval + 1, randint1(4)); vault_monsters(xval + 4, yval + 1, randint1(4)); vault_monsters(xval - 4, yval - 1, randint1(4)); vault_monsters(xval + 4, yval - 1, randint1(4)); break; } } } /* * The following functions are used to determine if the given monster * is appropriate for inclusion in a monster nest or monster pit or * the given type. * * None of the pits/nests are allowed to include "unique" monsters. */ /* * Monster validation macro * * Line 1 -- forbid town monsters * Line 2 -- forbid uniques * Line 3 -- forbid aquatic monsters */ #define vault_monster_okay(I) \ (!FLAG(&r_info[I], RF_WILD_TOWN) && \ !FLAG(&r_info[I], RF_UNIQUE) && \ !FLAG(&r_info[I], RF_AQUATIC)) /* Race index for "monster pit (clone)" */ static int vault_aux_race; /* Race index for "monster pit (symbol clone)" */ static char vault_aux_char; /* Breath mask for "monster pit (dragon)" */ static u32b vault_aux_dragon_mask4; /* Breath mask for "monster pit (elemental)" */ static u32b vault_aux_elemental_mask4; /* Attack type for "monster pit (elemental)" */ static int vault_aux_elemental_attack; /* * Helper monster selection function */ static bool vault_aux_simple(int r_idx) { /* Okay */ return (vault_monster_okay(r_idx)); } /* * Helper function for "monster nest (jelly)" */ static bool vault_aux_jelly(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Also decline evil jellies (like death molds and shoggoths) */ if (FLAG(r_ptr, RF_EVIL)) return (FALSE); /* Require icky thing, jelly, mold, or mushroom */ if (!strchr("ijm,", r_ptr->d_char)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster nest (animal)" */ static bool vault_aux_animal(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Decline unique monsters */ if (FLAG(r_ptr, RF_UNIQUE)) return (FALSE); /* Require "animal" flag */ if (!FLAG(r_ptr, RF_ANIMAL)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster nest (undead)" */ static bool vault_aux_undead(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require Undead */ if (!FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster nest (chapel)" */ static bool vault_aux_chapel(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require "priest" or Angel */ if ((r_ptr->d_char != 'A') && !mon_name_cont(r_ptr, "riest")) { return (FALSE); } /* Okay */ return (TRUE); } /* * Helper function for "monster nest (kennel)" */ static bool vault_aux_kennel(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require a Zephyr Hound or a dog */ if (!strchr("CZ", r_ptr->d_char)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster nest (mimic)" */ static bool vault_aux_mimic(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require mimic */ if (!strchr("!|$?=", r_ptr->d_char)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster nest (clone)" */ static bool vault_aux_clone(int r_idx) { return (r_idx == vault_aux_race); } /* * Helper function for "monster nest (symbol clone)" */ static bool vault_aux_symbol(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Decline incorrect symbol */ if (r_ptr->d_char != vault_aux_char) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (orc)" */ static bool vault_aux_orc(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require orc */ if (!FLAG(r_ptr, RF_ORC)) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (troll)" */ static bool vault_aux_troll(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require troll */ if (!FLAG(r_ptr, RF_TROLL)) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (giant)" */ static bool vault_aux_giant(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require giant */ if (!FLAG(r_ptr, RF_GIANT)) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (dragon)" */ static bool vault_aux_dragon(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require dragon */ if (!FLAG(r_ptr, RF_DRAGON)) return (FALSE); /* Hack -- Require correct "breath attack" */ if (r_ptr->flags[3] != vault_aux_dragon_mask4) return (FALSE); /* Decline undead */ if (FLAG(r_ptr, RF_UNDEAD)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (demon)" */ static bool vault_aux_demon(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require demon */ if (!FLAG(r_ptr, RF_DEMON)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (lovecraftian)" */ static bool vault_aux_cthulhu(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Require eldritch horror */ if (!FLAG(r_ptr, RF_ELDRITCH_HORROR)) return (FALSE); /* Okay */ return (TRUE); } /* * Helper function for "monster pit (elemental)" */ static bool vault_aux_elemental(int r_idx) { int i; monster_race *r_ptr = &r_info[r_idx]; /* Validate the monster */ if (!vault_monster_okay(r_idx)) return (FALSE); /* Hack -- Accept correct "breath attack" */ if (r_ptr->flags[3] == vault_aux_elemental_mask4) return (TRUE); /* Accept correct "melee attack" */ for (i = 0; i < 4; i++) { if (!r_ptr->blow[i].method) break; if (r_ptr->blow[i].effect == vault_aux_elemental_attack) return (TRUE); } /* Reject */ return (FALSE); } /* * Helper function for "monster pit (clone)" */ static void vault_prep_clone(void) { /* Pick a race to clone */ get_filter_mon_num(p_ptr->depth + 10, vault_aux_simple); } /* * Helper function for "monster pit (symbol clone)" */ static void vault_prep_symbol(void) { int r_idx; /* Pick a race to clone */ r_idx = get_filter_mon_num(p_ptr->depth + 10, vault_aux_simple); /* Extract the symbol */ vault_aux_char = r_info[r_idx].d_char; } /* * Helper function for "monster pit (dragon)" */ static void vault_prep_dragon(void) { /* Pick dragon type */ switch (randint0(6)) { case 0: { /* Black */ /* Restrict dragon breath type */ vault_aux_dragon_mask4 = RF3_BR_ACID; /* Done */ break; } case 1: { /* Blue */ /* Restrict dragon breath type */ vault_aux_dragon_mask4 = RF3_BR_ELEC; /* Done */ break; } case 2: { /* Red */ /* Restrict dragon breath type */ vault_aux_dragon_mask4 = RF3_BR_FIRE; /* Done */ break; } case 3: { /* White */ /* Restrict dragon breath type */ vault_aux_dragon_mask4 = RF3_BR_COLD; /* Done */ break; } case 4: { /* Green */ /* Restrict dragon breath type */ vault_aux_dragon_mask4 = RF3_BR_POIS; /* Done */ break; } default: { /* Multi-hued */ /* Restrict dragon breath type */ vault_aux_dragon_mask4 = (RF3_BR_ACID | RF3_BR_ELEC | RF3_BR_FIRE | RF3_BR_COLD | RF3_BR_POIS); /* Done */ break; } } } /* * Helper function for "monster pit (elemental)" */ static void vault_prep_elemental(void) { /* Pick dragon type */ switch (randint0(3)) { case 0: { /* Fire */ /* Restrict elemental breath type */ vault_aux_elemental_mask4 = RF3_BR_FIRE; /* Restrict melee attack type */ vault_aux_elemental_attack = RBE_FIRE; /* Done */ break; } case 1: { /* Cold */ /* Restrict elemental breath type */ vault_aux_elemental_mask4 = RF3_BR_COLD; /* Restrict melee attack type */ vault_aux_elemental_attack = RBE_COLD; /* Done */ break; } default: { /* Elec */ /* Restrict elemental breath type */ vault_aux_elemental_mask4 = RF3_BR_ELEC; /* Restrict melee attack type */ vault_aux_elemental_attack = RBE_ELEC; /* Done */ break; } } } typedef struct vault_aux_type vault_aux_type; struct vault_aux_type { cptr name; bool (*hook_func) (int r_idx); void (*prep_func) (void); int level; int chance; }; /* * Pick a type of pit or nest out of a list at random. * Types with higher numbers of chances are more likely to be picked. * * This function tries to pick an out-of-depth type occasionally. */ static const vault_aux_type *pick_vault_type(const vault_aux_type *l_ptr) { int tmp, total; const vault_aux_type *n_ptr; /* Calculate the total possibilities */ for (n_ptr = l_ptr, total = 0; n_ptr->name; n_ptr++) { /* Count this possibility */ if (n_ptr->level > p_ptr->depth) { /* Out of depth - decreased chances */ total += n_ptr->chance * MAX_DEPTH * 2 / (n_ptr->level - p_ptr->depth + 1); } else { /* Normal selection */ total += n_ptr->chance * MAX_DEPTH * 10 / (p_ptr->depth - n_ptr->level + 5); } } /* Pick a random type */ tmp = randint0(total); /* Find this type */ for (n_ptr = l_ptr, total = 0; n_ptr->name; n_ptr++) { /* Count this possibility */ if (n_ptr->level > p_ptr->depth) { total += n_ptr->chance * MAX_DEPTH * 2 / (n_ptr->level - p_ptr->depth + 1); } else { total += n_ptr->chance * MAX_DEPTH * 10 / (p_ptr->depth - n_ptr->level + 5); } /* Found the type */ if (tmp < total) break; } return (n_ptr->name ? n_ptr : NULL); } static const vault_aux_type nest_types[] = { {"clone", vault_aux_clone, vault_prep_clone, 7, 6}, {"jelly", vault_aux_jelly, NULL, 7, 2}, {"elemental", vault_aux_elemental, vault_prep_elemental, 15, 3}, {"symbol clone", vault_aux_symbol, vault_prep_symbol, 40, 6}, {"mimic", vault_aux_mimic, NULL, 45, 2}, {"lovecraftian", vault_aux_cthulhu, NULL, 80, 2}, {"kennel", vault_aux_kennel, NULL, 50, 2}, {"animal", vault_aux_animal, NULL, 50, 8}, {"chapel", vault_aux_chapel, NULL, 90, 4}, {"undead", vault_aux_undead, NULL, 90, 4}, {NULL, NULL, NULL, 0, 0}, }; /* * Draw the outside of a pit / nest * * Add pillars on inner or outer wall - idea from Deric Taylor */ static void draw_pit(int x1, int y1, int x2, int y2, int xh, int yh) { int x, y; /* Decide what type of hall pattern to use */ int wid = (xh > 1) ? randint0(xh + 1) : 0; int hgt = (yh > 1) ? randint0(yh + 1) : 0; int outer = randint0(2); /* Generate new room */ generate_room(x1, y1, x2, y2, FALSE); /* Generate outer walls */ generate_draw(x1, y1, x2, y2, FEAT_WALL_OUTER); /* Shrink to inner floor */ x1++; x2--; y1++; y2--; /* Generate inner floor */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* No pillars? */ if (!wid && !hgt) return; /* Draw pillars */ for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { /* Only on checkerboard squares */ if ((x + y) & 1) continue; /* Not in middle room area */ if ((x >= x1 + xh) && (x <= x2 - xh) && (y >= y1 + yh) && (y <= y2 - yh)) { continue; } if (outer) { /* Outer wall */ if ((x < x1 + wid) || (x > x2 - wid) || (y < y1 + hgt) || (y > y2 - hgt)) { set_feat_bold(x, y, FEAT_PILLAR); } } else { /* Inner wall */ if ((x > x1 + xh - 1 - wid) && (x < x2 - xh + 1 + wid) && (y > y1 + yh - 1 - hgt) && (y < y2 - yh + 1 + hgt)) { set_feat_bold(x, y, FEAT_PILLAR); } } } } } /* Maximum pit sizes */ #define PIT_SIZE 3 #define PIT_HGT ((PIT_SIZE) * 2 + 1) #define PIT_WID ((PIT_SIZE) * 8 + 3) /* * Type 5 -- Monster nests * * A monster nest is a "big" room, with an "inner" room, containing * a "collection" of monsters of a given type strewn about the room. * * The hallway may vary in width, and may contain pillars. * * The monsters are chosen from a set of 64 randomly selected monster * races, to allow the nest creation to fail instead of having "holes". * * Note the use of the "get_mon_num_prep()" function, and the special * "get_mon_num_hook()" restriction function, to prepare the "monster * allocation table" in such a way as to optimize the selection of * "appropriate" non-unique monsters for the nest. * * Note that the "get_mon_num()" function may (rarely) fail, in which * case the nest will be empty, and will not affect the level rating. * * Note that "monster nests" will never contain "unique" monsters. */ static void build_type5(int bx0, int by0) { int x, y; int wid, hgt; int dx, dy; int x1, y1; int x2, y2; int in_x1, in_y1; int in_x2, in_y2; int cx, cy; int x_cent, y_cent; int x_hall, y_hall; int i; int align = 0; int what[64]; const vault_aux_type *n_ptr = pick_vault_type(nest_types); /* No type available */ if (!n_ptr) return; /* Determine size of room - up to 7x27 now! */ y_cent = rand_range(1, PIT_SIZE); /* Nests may have an aspect ratio up to 4:1 */ x_cent = rand_range(3, 4 * y_cent + 1); /* Determine width of hall */ x_hall = rand_range(1, x_cent / 3 + 1); y_hall = rand_range(1, y_cent + 1); /* Distances to outer wall */ dx = x_cent + 1 + x_hall + 1; dy = y_cent + 1 + y_hall + 1; /* Size of room */ wid = 2 * dx + 1; hgt = 2 * dy + 1; /* Try to allocate space for room. */ if (!room_alloc(wid, hgt, TRUE, bx0, by0, &cx, &cy)) return; /* Process a preparation function if necessary */ if (n_ptr->prep_func) (*(n_ptr->prep_func)) (); /* Outer room walls */ x1 = cx - dx; x2 = cx + dx; y1 = cy - dy; y2 = cy + dy; /* Inner room walls */ in_x1 = cx - x_cent - 1; in_x2 = cx + x_cent + 1; in_y1 = cy - y_cent - 1; in_y2 = cy + y_cent + 1; /* Draw the outer room */ draw_pit(x1, y1, x2, y2, x_hall, y_hall); /* Generate inner walls */ generate_draw(in_x1, in_y1, in_x2, in_y2, FEAT_WALL_INNER); /* Open the inner room with a secret door */ generate_door(in_x1, in_y1, in_x2, in_y2, TRUE); /* Prepare allocation table */ get_mon_num_prep(n_ptr->hook_func); /* Pick some monster types */ for (i = 0; i < 64; i++) { int r_idx = 0, attempts = 100; while (attempts--) { /* Get a (hard) monster type */ r_idx = get_mon_num(p_ptr->depth + 10); /* Decline incorrect alignment */ if (((align < 0) && FLAG(&r_info[r_idx], RF_GOOD)) || ((align > 0) && FLAG(&r_info[r_idx], RF_EVIL))) { continue; } /* Accept this monster */ break; } /* Notice failure */ if (!r_idx || !attempts) return; /* Note the alignment */ if (FLAG(&r_info[r_idx], RF_GOOD)) align++; else if (FLAG(&r_info[r_idx], RF_EVIL)) align--; what[i] = r_idx; } /* Reset allocation table */ get_mon_num_prep(NULL); /* Describe */ if (cheat_room) { /* Room type */ msgf("Monster nest (%s)", n_ptr->name); } /* Shrink to contents of nest */ in_x1++; in_x2--; in_y1++; in_y2--; /* Place some monsters */ for (y = in_y1; y <= in_y2; y++) { for (x = in_x1; x <= in_x2; x++) { int r_idx = what[randint0(64)]; /* Place that "random" monster (no groups) */ (void)place_monster_aux(x, y, r_idx, FALSE, FALSE, FALSE, FALSE, TRUE); } } /* Increase the level rating */ inc_rating(10); /* Sometimes nests cause a special feeling */ if ((p_ptr->depth <= 40) && (randint1(p_ptr->depth * p_ptr->depth + 50) < 300)) { set_special(); } } static const vault_aux_type pit_types[] = { {"orc", vault_aux_orc, NULL, 7, 4}, {"troll", vault_aux_troll, NULL, 35, 4}, {"giant", vault_aux_giant, NULL, 70, 2}, {"lovecraftian", vault_aux_cthulhu, NULL, 90, 1}, {"clone", vault_aux_symbol, vault_prep_symbol, 85, 2}, {"chapel", vault_aux_chapel, NULL, 85, 2}, {"dragon", vault_aux_dragon, vault_prep_dragon, 80, 4}, {"demon", vault_aux_demon, NULL, 90, 4}, {NULL, NULL, NULL, 0, 0}, }; /* * Type 6 -- Monster pits * * A monster pit is a "big" room, with an "inner" room, containing * a "collection" of monsters of a given type organized in the room. * * The hallway may vary in width, and may contain pillars. * The inside room in a monster pit has a randomized, but ordered, layout. * * Note that the monsters in the pit are now chosen by using "get_mon_num()" * to request 16 "appropriate" monsters, sorting them by level. * * Hack -- all of the "dragons" in a "dragon" pit must be the same "color", * which is handled by requiring a specific "breath" attack for all of the * dragons. This may include "multi-hued" breath. Note that "wyrms" may * be present in many of the dragon pits, if they have the proper breath. * * Note the use of the "get_mon_num_prep()" function, and the special * "get_mon_num_hook()" restriction function, to prepare the "monster * allocation table" in such a way as to optimize the selection of * "appropriate" non-unique monsters for the pit. * * Note that the "get_mon_num()" function may (rarely) fail, in which case * the pit will be empty, and will not effect the level rating. * * Note that "monster pits" will never contain "unique" monsters. */ static void build_type6(int bx0, int by0) { int x, y; int wid, hgt; int in_wid, in_hgt; int dx, dy; int x1, y1; int x2, y2; int in_x1, in_y1; int in_x2, in_y2; int cx, cy; int x_cent, y_cent; int x_hall, y_hall; int i, j; int align = 0; int what[16]; byte power[PIT_HGT][PIT_WID]; int min = 256; int max = 0; bool copy_v = (randint0(2)) ? TRUE : FALSE; bool copy_h = (randint0(2)) ? TRUE : FALSE; const vault_aux_type *n_ptr = pick_vault_type(pit_types); /* No type available */ if (!n_ptr) return; /* Determine size of room - up to 7x27 now! */ y_cent = rand_range(1, PIT_SIZE); /* Pits may have an aspect ratio up to 4:1 */ x_cent = rand_range(3, 4 * y_cent + 1); /* Determine width of hall */ x_hall = rand_range(1, x_cent / 3 + 1); y_hall = rand_range(1, y_cent + 1); /* Distances to outer wall */ dx = x_cent + 1 + x_hall + 1; dy = y_cent + 1 + y_hall + 1; /* Size of outer room */ wid = 2 * dx + 1; hgt = 2 * dy + 1; /* Try to allocate space for room. */ if (!room_alloc(wid, hgt, TRUE, bx0, by0, &cx, &cy)) return; /* Prepare the monster distribution array */ for (i = 0; i < PIT_HGT; i++) { (void)C_WIPE(power[i], PIT_WID, byte); } /* Process a preparation function if necessary */ if (n_ptr->prep_func) (*(n_ptr->prep_func)) (); /* Outer room walls */ x1 = cx - dx; x2 = cx + dx; y1 = cy - dy; y2 = cy + dy; /* Inner room walls */ in_x1 = cx - x_cent - 1; in_x2 = cx + x_cent + 1; in_y1 = cy - y_cent - 1; in_y2 = cy + y_cent + 1; /* Size of inner room */ in_wid = 2 * x_cent + 1; in_hgt = 2 * y_cent + 1; /* Draw the outer room */ draw_pit(x1, y1, x2, y2, x_hall, y_hall); /* Generate inner walls */ generate_draw(in_x1, in_y1, in_x2, in_y2, FEAT_WALL_INNER); /* Open the inner room with a secret door */ generate_door(in_x1, in_y1, in_x2, in_y2, TRUE); /* Prepare allocation table */ get_mon_num_prep(n_ptr->hook_func); /* Pick some monster types */ for (i = 0; i < 16; i++) { int r_idx = 0, attempts = 100; while (attempts--) { /* Get a (hard) monster type */ r_idx = get_mon_num(p_ptr->depth + 10); /* Decline incorrect alignment */ if (((align < 0) && FLAG(&r_info[r_idx], RF_GOOD)) || ((align > 0) && FLAG(&r_info[r_idx], RF_EVIL))) { continue; } /* Accept this monster */ break; } /* Notice failure */ if (!r_idx || !attempts) return; /* Note the alignment */ if (FLAG(&r_info[r_idx], RF_GOOD)) align++; else if (FLAG(&r_info[r_idx], RF_EVIL)) align--; what[i] = r_idx; } /* Reset allocation table */ get_mon_num_prep(NULL); /* Sort the entries */ for (i = 0; i < 16 - 1; i++) { /* Sort the entries */ for (j = 0; j < 16 - 1; j++) { int i1 = j; int i2 = j + 1; int p1 = r_info[what[i1]].level; int p2 = r_info[what[i2]].level; /* Bubble */ if (p1 > p2) { int tmp = what[i1]; what[i1] = what[i2]; what[i2] = tmp; } } } /* Message */ if (cheat_room) { /* Room type */ msgf("Monster pit (%s)", n_ptr->name); } /* Create a random pit layout - EB */ for (i = 0; i < 16; i++) { /* Pick a block inside the pit */ int w = randint0(in_wid); int n = randint0(in_hgt); int e = randint0(in_wid); int s = randint0(in_hgt); /* Fix order */ if (w > e) { x = w; w = e; e = x; } /* Fix order */ if (n > s) { y = n; n = s; s = y; } /* Increase the power of this block */ for (y = n; y <= s; y++) { for (x = w; x <= e; x++) { power[y][x]++; if (copy_v) power[in_hgt - 1 - y][x]++; if (copy_h) power[y][in_wid - 1 - x]++; if (copy_v && copy_h) power[in_hgt - 1 - y][in_wid - 1 - x]++; } } } /* Normalize */ for (y = 0; y < in_hgt; y++) { for (x = 0; x < in_wid; x++) { /* Find least powerful monster */ if (power[y][x] < min) min = power[y][x]; /* Find most powerful monster */ if (power[y][x] > max) max = power[y][x]; } } /* Shrink to contents of pit */ in_x1++; in_x2--; in_y1++; in_y2--; /* Place the monsters */ for (y = 0; y < in_hgt; y++) { for (x = 0; x < in_wid; x++) { /* Normalize */ power[y][x] = (power[y][x] - min) * 16 / (max - min + 1); /* And place the monster */ place_monster_aux(x + in_x1, y + in_y1, what[power[y][x]], FALSE, FALSE, FALSE, FALSE, TRUE); } } /* Increase the level rating */ inc_rating(10); /* Sometimes pits cause a special feeling */ if ((p_ptr->depth <= 40) && (randint1(p_ptr->depth * p_ptr->depth + 50) < 300)) { set_special(); } } /* coordinate translation code */ static void coord_trans(int *x, int *y, int xoffset, int yoffset, int transno) { int i; int temp; /* * transno specifies what transformation is required. (0-7) * The lower two bits indicate by how much the vault is rotated, * and the upper bit indicates a reflection. * This is done by using rotation matrices... however since * these are mostly zeros for rotations by 90 degrees this can * be expressed simply in terms of swapping and inverting the * x and y coordinates. */ for (i = 0; i <= transno % 4; i++) { /* rotate by 90 degrees */ temp = *x; *x = -(*y); *y = temp; } if (transno / 4) { /* Reflect depending on status of 3rd bit. */ *x = -(*x); } /* Add offsets so vault stays in the first quadrant */ *x += xoffset; *y += yoffset; } /* * Hack -- fill in "vault" rooms */ static void build_vault(int xval, int yval, int xmax, int ymax, cptr data, int xoffset, int yoffset, int transno) { int dx, dy, x, y, i, j; cptr t; cave_type *c_ptr; /* Place dungeon features and objects */ for (t = data, dy = 0; dy < ymax; dy++) { for (dx = 0; dx < xmax; dx++, t++) { /* prevent loop counter from being overwritten */ i = dx; j = dy; /* Flip / rotate */ coord_trans(&i, &j, xoffset, yoffset, transno); /* Extract the location */ if (transno % 2) { /* no swap of x/y */ x = xval - (xmax / 2) + i; y = yval - (ymax / 2) + j; } else { /* swap of x/y */ x = xval - (ymax / 2) + i; y = yval - (xmax / 2) + j; } /* Hack -- skip "non-grids" */ if (*t == ' ') continue; /* Access the grid */ c_ptr = cave_p(x, y); /* Lay down a floor */ set_feat_grid(c_ptr, dun->feat_floor); /* Part of a vault */ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY); /* Analyze the grid */ switch (*t) { case '%': { /* Granite wall (outer) */ set_feat_grid(c_ptr, FEAT_WALL_OUTER); break; } case '#': { /* Granite wall (inner) */ set_feat_grid(c_ptr, FEAT_WALL_INNER); break; } case 'X': { /* Permanent wall (inner) */ set_feat_grid(c_ptr, FEAT_PERM_INNER); break; } case '*': { /* Treasure/trap */ if (randint0(100) < 75) { place_object(x, y, FALSE, FALSE, 0); } else { place_trap(x, y); } break; } case '+': { /* Secret doors */ place_secret_door(x, y); break; } case '^': { /* Trap */ place_trap(x, y); break; } case 'p': { set_feat_grid(c_ptr, FEAT_PATTERN_START); break; } case 'a': { set_feat_grid(c_ptr, FEAT_PATTERN_1); break; } case 'b': { set_feat_grid(c_ptr, FEAT_PATTERN_2); break; } case 'c': { set_feat_grid(c_ptr, FEAT_PATTERN_3); break; } case 'd': { set_feat_grid(c_ptr, FEAT_PATTERN_4); break; } case 'P': { set_feat_grid(c_ptr, FEAT_PATTERN_END); break; } case 'B': { set_feat_grid(c_ptr, FEAT_PATTERN_XTRA1); break; } } } } /* Place dungeon monsters and objects */ for (t = data, dy = 0; dy < ymax; dy++) { for (dx = 0; dx < xmax; dx++, t++) { /* prevent loop counter from being overwritten */ i = dx; j = dy; /* Flip / rotate */ coord_trans(&i, &j, xoffset, yoffset, transno); /* Extract the location */ if (transno % 2) { /* no swap of x/y */ x = xval - (xmax / 2) + i; y = yval - (ymax / 2) + j; } else { /* swap of x/y */ x = xval - (ymax / 2) + i; y = yval - (xmax / 2) + j; } /* Hack -- skip "non-grids" */ if (*t == ' ') continue; /* Analyze the symbol */ switch (*t) { case '&': { /* Monster */ (void)place_monster(x, y, TRUE, TRUE, 4); break; } case '@': { /* Meaner monster */ (void)place_monster(x, y, TRUE, TRUE, 8); break; } case '9': { /* Meaner monster, plus treasure */ (void)place_monster(x, y, TRUE, TRUE, 6); place_object(x, y, TRUE, FALSE, 6); break; } case '8': { /* Nasty monster and treasure */ (void)place_monster(x, y, TRUE, TRUE, 25); place_object(x, y, TRUE, TRUE, 20); break; } case ',': { /* Monster and/or object */ if (randint0(100) < 50) { (void)place_monster(x, y, TRUE, TRUE, 3); } if (randint0(100) < 50) { place_object(x, y, FALSE, FALSE, 5); } break; } case 'A': { /* Object */ place_object(x, y, TRUE, FALSE, 10); } break; } } } } /* * Type 7 -- simple vaults (see "v_info.txt") */ static void build_type7(int bx0, int by0) { vault_type *v_ptr = NULL; int dummy = 0; int x, y; int xval, yval; int xoffset, yoffset; int transno; /* Pick a lesser vault */ while (dummy < SAFE_MAX_ATTEMPTS) { dummy++; /* Access a random vault record */ v_ptr = &v_info[randint0(z_info->v_max)]; /* Accept the first lesser vault */ if (v_ptr->typ == 7) break; } /* No lesser vault found */ if (!v_ptr) return; if (!one_in_(3)) { /* pick type of transformation (0-7) */ transno = randint0(8); } else { /* No change */ transno = 3; } /* calculate offsets */ x = v_ptr->wid; y = v_ptr->hgt; coord_trans(&x, &y, 0, 0, transno); if (x < 0) { xoffset = -x - 1; } else { xoffset = 0; } if (y < 0) { yoffset = -y - 1; } else { yoffset = 0; } /* Try to allocate space for room. */ if (!room_alloc(ABS(x), ABS(y), FALSE, bx0, by0, &xval, &yval)) return; if (dummy >= SAFE_MAX_ATTEMPTS) { if (cheat_room) msgf("Warning! Could not place lesser vault!"); return; } /* Message */ if (cheat_room) msgf("%s", v_name + v_ptr->name); /* Boost the rating */ inc_rating(v_ptr->rat); /* (Sometimes) Cause a special feeling */ if ((p_ptr->depth <= 50) || (randint1((p_ptr->depth - 40) * (p_ptr->depth - 40) + 50) < 400)) { set_special(); } /* Hack -- Build the vault */ build_vault(xval, yval, v_ptr->wid, v_ptr->hgt, v_text + v_ptr->text, xoffset, yoffset, transno); } /* * Type 8 -- greater vaults (see "v_info.txt") */ static void build_type8(int bx0, int by0) { vault_type *v_ptr = NULL; int dummy = 0; int xval, yval; int x, y; int transno; int xoffset, yoffset; /* Pick a greater vault */ while (dummy < SAFE_MAX_ATTEMPTS) { dummy++; /* Access a random vault record */ v_ptr = &v_info[randint0(z_info->v_max)]; /* Accept the first greater vault */ if (v_ptr->typ == 8) break; } /* No greater vault found */ if (!v_ptr) return; if (!one_in_(3)) { /* pick type of transformation (0-7) */ transno = randint0(8); } else { /* No Change */ transno = 3; } /* calculate offsets */ x = v_ptr->wid; y = v_ptr->hgt; coord_trans(&x, &y, 0, 0, transno); if (x < 0) { xoffset = -x - 1; } else { xoffset = 0; } if (y < 0) { yoffset = -y - 1; } else { yoffset = 0; } /* Try to allocate space for room. If fails, exit */ if (!room_alloc(ABS(x), ABS(y), FALSE, bx0, by0, &xval, &yval)) return; if (dummy >= SAFE_MAX_ATTEMPTS) { if (cheat_room) msgf("Warning! Could not place greater vault!"); return; } /* Message */ if (cheat_room) msgf("%s", v_name + v_ptr->name); /* Boost the rating */ inc_rating(v_ptr->rat); /* (Sometimes) Cause a special feeling */ if ((p_ptr->depth <= 50) || (randint1((p_ptr->depth - 40) * (p_ptr->depth - 40) + 50) < 400)) { set_special(); } /* Hack -- Build the vault */ build_vault(xval, yval, v_ptr->wid, v_ptr->hgt, v_text + v_ptr->text, xoffset, yoffset, transno); } /* * Type 9 -- Fractal cave system */ static void build_type9(int bx0, int by0) { int grd, roug, cutoff, xsize, ysize, xhsize, yhsize, y0, x0; bool done, light; /* get size: note 'Evenness' */ xsize = randint1(22) * 2 + 6; ysize = randint1(10) * 2 + 6; /* round to make sizes even */ xhsize = (xsize - 1) / 2; yhsize = (ysize - 1) / 2; xsize = xhsize * 2; ysize = yhsize * 2; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 1, ysize + 1, FALSE, bx0, by0, &x0, &y0)) return; light = done = FALSE; if (p_ptr->depth <= randint1(25)) light = TRUE; while (!done) { /* Note: size must be even or there are rounding problems * This causes the tunnels not to connect properly to the room */ /* testing values for these parameters feel free to adjust */ grd = 1 << (randint0(4)); /* want average of about 16 */ roug = randint1(8) * randint1(4); /* about size/2 */ cutoff = randint1(xsize / 4) + randint1(ysize / 4) + randint1(xsize / 4) + randint1(ysize / 4); /* make it */ generate_hmap(x0, y0, xsize, ysize, grd, roug, cutoff); /* Convert to normal format + clean up */ done = generate_fracave(x0, y0, xsize, ysize, cutoff, light); } } /* * Routine used by the random vault creators to add a door to a location * Note that range checking has to be done in the calling routine. * * The doors must be INSIDE the allocated region. */ static bool add_door(int x, int y) { /* Need to have a wall in the center square */ if (cave_p(x, y)->feat != FEAT_WALL_OUTER) return (FALSE); /* look at: * x#x * .#. * x#x * * where x=don't care * .=floor, #=wall */ if ((cave_p(x, y - 1)->feat == dun->feat_floor) && (cave_p(x, y + 1)->feat == dun->feat_floor) && (cave_p(x - 1, y)->feat == FEAT_WALL_OUTER) && (cave_p(x + 1, y)->feat == FEAT_WALL_OUTER)) { place_random_door(x, y); /* set boundarys so don't get wide doors */ set_feat_bold(x - 1, y, FEAT_WALL_SOLID); set_feat_bold(x + 1, y, FEAT_WALL_SOLID); return (TRUE); } /* look at: * x#x * .#. * x#x * * where x = don't care * .=floor, #=wall */ if ((cave_p(x, y - 1)->feat == FEAT_WALL_OUTER) && (cave_p(x, y + 1)->feat == FEAT_WALL_OUTER) && (cave_p(x - 1, y)->feat == dun->feat_floor) && (cave_p(x + 1, y)->feat == dun->feat_floor)) { place_random_door(x, y); /* set boundarys so don't get wide doors */ set_feat_bold(x, y - 1, FEAT_WALL_SOLID); set_feat_bold(x, y + 1, FEAT_WALL_SOLID); return (TRUE); } return (FALSE); } /* * Routine that fills the empty areas of a room with treasure and monsters. */ static void fill_treasure(int x1, int y1, int x2, int y2, int difficulty) { int x, y, cx, cy, size; s32b value; cave_type *c_ptr; /* center of room: */ cx = (x1 + x2) / 2; cy = (y1 + y2) / 2; /* Rough measure of size of vault= sum of lengths of sides */ size = ABS(x2 - x1) + ABS(y2 - y1); for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { /* * Thing added based on distance to center of vault * Difficulty is 1-easy to 10-hard */ value = ((((s32b)(distance(cx, cy, x, y))) * 100) / size) + randint1(10) - difficulty; /* hack- empty square part of the time */ if ((randint1(100) - difficulty * 3) > 50) value = 20; c_ptr = cave_p(x, y); /* if floor, shallow water and lava */ if ((c_ptr->feat == dun->feat_floor) || (c_ptr->feat == FEAT_SHAL_WATER) || (c_ptr->feat == FEAT_SHAL_LAVA)) { /* The smaller 'value' is, the better the stuff */ if (value < 0) { /* Meanest monster + treasure */ (void)place_monster(x, y, TRUE, TRUE, 25); place_object(x, y, TRUE, FALSE, 25); } else if (value < 5) { /* Mean monster + treasure */ (void)place_monster(x, y, TRUE, TRUE, 15); place_object(x, y, TRUE, FALSE, 10); } else if (value < 10) { /* Monster */ (void)place_monster(x, y, TRUE, TRUE, 6); } else if (value < 17) { /* Intentional Blank space */ /* * (Want some of the vault to be empty * so have room for group monsters. * This is used in the hack above to lower * the density of stuff in the vault.) */ } else if (value < 23) { /* Object or trap */ if (randint0(100) < 25) { place_object(x, y, FALSE, FALSE, 0); } else { place_trap(x, y); } } else if (value < 30) { /* Monster and trap */ (void)place_monster(x, y, TRUE, TRUE, 5); place_trap(x, y); } else if (value < 40) { /* Monster or object */ if (randint0(100) < 50) { (void)place_monster(x, y, TRUE, TRUE, 3); } if (randint0(100) < 50) { place_object(x, y, FALSE, FALSE, 7); } } else if (value < 50) { /* Trap */ place_trap(x, y); } else { /* Various Stuff */ /* 20% monster, 40% trap, 20% object, 20% blank space */ if (randint0(100) < 20) { (void)place_monster(x, y, TRUE, TRUE, 0); } else if (randint0(100) < 50) { place_trap(x, y); } else if (randint0(100) < 50) { place_object(x, y, FALSE, FALSE, 0); } } } } } } /* * This function creates a random vault that looks like a * collection of bubbles. It works by getting a set of * coordinates that represent the center of each bubble. * The entire room is made by seeing which bubble center * is closest. If two centers are equidistant then the grid * is a wall, otherwise it is a floor. The only exception is * for squares really near a center, these are always floor. * (It looks better than without this check.) */ static void build_bubble_vault(int x0, int y0, int xsize, int ysize) { /* number of bubbles */ #define BUBBLENUM 10 /* array of center points of bubbles */ coord center[BUBBLENUM]; int i, x, y; u16b min1, min2, temp; /* Offset from center to top left hand corner */ int xhsize = xsize / 2; int yhsize = ysize / 2; cave_type *c_ptr; if (cheat_room) msgf("Bubble Vault"); /* Allocate center of bubbles */ for (i = 0; i < BUBBLENUM; i++) { /* Uniqueness check removed */ center[i].x = (u16b)rand_range(1, xsize - 2); center[i].y = (u16b)rand_range(1, ysize - 2); } /* Set vault flags */ generate_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1); /* Draw outer walls */ generate_draw(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, FEAT_WALL_OUTER); /* Fill in middle with bubbles */ for (x = 1; x < xsize - 1; x++) { for (y = 1; y < ysize - 1; y++) { /* Get distances to two closest centers */ /* initialize */ min1 = distance(x, y, center[0].x, center[0].y); min2 = distance(x, y, center[1].x, center[1].y); if (min1 > min2) { /* swap if in wrong order */ temp = min1; min1 = min2; min2 = temp; } /* Scan the rest */ for (i = 2; i < BUBBLENUM; i++) { temp = distance(x, y, center[i].x, center[i].y); if (temp < min1) { /* smallest */ min2 = min1; min1 = temp; } else if (temp < min2) { /* second smallest */ min2 = temp; } } c_ptr = cave_p(x0 - xhsize + x, y0 - yhsize + y); if (((min2 - min1) <= 2) && (!(min1 < 3))) { /* Boundary at midpoint+ not at inner region of bubble */ set_feat_grid(c_ptr, FEAT_WALL_OUTER); } else { /* middle of a bubble */ set_feat_grid(c_ptr, dun->feat_floor); } } } /* Try to add some random doors */ for (i = 0; i < 500; i++) { x = rand_range(x0 - xhsize + 1, x0 - xhsize + xsize - 2); y = rand_range(y0 - yhsize + 1, y0 - yhsize + ysize - 2); add_door(x, y); } /* Fill with monsters and treasure, low difficulty */ fill_treasure(x0 - xhsize + 1, y0 - yhsize + 1, x0 - xhsize + xsize - 2, y0 - yhsize + ysize - 2, randint1(5)); } /* * Overlay a rectangular room given its bounds * This routine is used by build_room_vault * The area inside the walls is not touched: * only granite is removed- normal walls stay */ static void build_room(int x1, int y1, int x2, int y2) { int x, y, xsize, ysize, temp; cave_type *c_ptr; /* Check if rectangle has no width */ if ((x1 == x2) || (y1 == y2)) return; /* initialize */ if (x1 > x2) { /* Swap boundaries if in wrong order */ temp = x1; x1 = x2; x2 = temp; } if (y1 > y2) { /* Swap boundaries if in wrong order */ temp = y1; y1 = y2; y2 = temp; } /* get total widths */ xsize = x2 - x1; ysize = y2 - y1; /* Set vault flags */ generate_vault(x1, y1, x2, y2); /* Draw outer wall */ generate_draw(x1, y1, x2, y2, FEAT_WALL_OUTER); /* Middle */ for (x = 1; x < xsize; x++) { for (y = 1; y < ysize; y++) { c_ptr = cave_p(x1 + x, y1 + y); if (c_ptr->feat == FEAT_WALL_EXTRA) { /* Clear the untouched region */ set_feat_grid(c_ptr, dun->feat_floor); } } } } /* * Create a random vault that looks like a collection of overlapping rooms */ static void build_room_vault(int x0, int y0, int xsize, int ysize) { int i, x1, x2, y1, y2, xhsize, yhsize; /* get offset from center */ xhsize = xsize / 2; yhsize = ysize / 2; if (cheat_room) msgf("Room Vault"); /* fill area so don't get problems with arena levels */ generate_fill(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, FEAT_WALL_EXTRA); /* Clear the CAVE_ICKY flag in the region */ clear_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1); /* add ten random rooms */ for (i = 0; i < 10; i++) { x1 = randint1(xhsize) * 2 + x0 - xhsize; x2 = randint1(xhsize) * 2 + x0 - xhsize; y1 = randint1(yhsize) * 2 + y0 - yhsize; y2 = randint1(yhsize) * 2 + y0 - yhsize; build_room(x1, y1, x2, y2); } /* Add some random doors */ for (i = 0; i < 500; i++) { x1 = rand_range(x0 - xhsize + 1, x0 - xhsize + xsize - 2); y1 = rand_range(y0 - yhsize + 1, y0 - yhsize + ysize - 2); add_door(x1, y1); } /* Fill with monsters and treasure, high difficulty */ fill_treasure(x0 - xhsize + 1, y0 - yhsize + 1, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, rand_range(5, 10)); } /* * Create a random vault out of a fractal cave */ static void build_cave_vault(int x0, int y0, int xsiz, int ysiz) { int grd, roug, cutoff, xhsize, yhsize, xsize, ysize; bool done; int x, y; cave_type *c_ptr; /* round to make sizes even */ xhsize = (xsiz - 1) / 2; yhsize = (ysiz - 1) / 2; xsize = xhsize * 2; ysize = yhsize * 2; if (cheat_room) msgf("Cave Vault"); done = FALSE; while (!done) { /* testing values for these parameters feel free to adjust */ grd = 1 << randint0(4); /* want average of about 16 */ roug = randint1(8) * randint1(4); /* about size/2 */ cutoff = randint1(xsize / 4) + randint1(ysize / 4) + randint1(xsize / 4) + randint1(ysize / 4); /* make it */ generate_hmap(x0, y0, xsize, ysize, grd, roug, cutoff); /* Convert to normal format+ clean up */ done = generate_fracave(x0, y0, xsize, ysize, cutoff, FALSE); } /* Set vault flags */ for (y = y0 - yhsize; y < y0 - yhsize + ysize; y++) { for (x = x0 - xhsize; x < x0 - xhsize + xsize; x++) { c_ptr = cave_p(x, y); /* Is it a floor? */ if (c_ptr->feat == dun->feat_floor) { /* Set the icky flag */ c_ptr->info |= CAVE_ICKY; } } } /* Fill with monsters and treasure, low difficulty */ fill_treasure(x0 - xhsize + 1, y0 - yhsize + 1, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, randint1(5)); } /* * maze vault -- rectangular labyrinthine rooms * * maze vault uses two routines: * r_visit - a recursive routine that builds the labyrinth * build_maze_vault - a driver routine that calls r_visit and adds * monsters, traps and treasure * * The labyrinth is built by creating a spanning tree of a graph. * The graph vertices are at * (x, y) = (2j + x1, 2k + y1) j = 0,...,m-1 k = 0,...,n-1 * and the edges are the vertical and horizontal nearest neighbors. * * The spanning tree is created by performing a suitably randomized * depth-first traversal of the graph. The only adjustable parameter * is the randint0(3) below; it governs the relative density of * twists and turns in the labyrinth: smaller number, more twists. */ static void r_visit(int x1, int y1, int x2, int y2, int node, int dir, int *visited) { int i, j, m, n, temp, x, y, adj[4]; /* dimensions of vertex array */ m = (x2 - x1) / 2 + 1; n = (y2 - y1) / 2 + 1; /* mark node visited and set it to a floor */ visited[node] = 1; x = 2 * (node % m) + x1; y = 2 * (node / m) + y1; set_feat_bold(x, y, dun->feat_floor); /* setup order of adjacent node visits */ if (one_in_(3)) { /* pick a random ordering */ for (i = 0; i < 4; i++) { adj[i] = i; } for (i = 0; i < 4; i++) { j = randint0(4); /* Swap */ temp = adj[i]; adj[i] = adj[j]; adj[j] = temp; } dir = adj[0]; } else { /* pick a random ordering with dir first */ adj[0] = dir; for (i = 1; i < 4; i++) { adj[i] = i; } for (i = 1; i < 4; i++) { j = randint1(3); /* Swap */ temp = adj[i]; adj[i] = adj[j]; adj[j] = temp; } } for (i = 0; i < 4; i++) { switch (adj[i]) { case 0: { /* (0,+) - check for bottom boundary */ if ((node / m < n - 1) && (visited[node + m] == 0)) { set_feat_bold(x, y + 1, dun->feat_floor); r_visit(x1, y1, x2, y2, node + m, dir, visited); } break; } case 1: { /* (0,-) - check for top boundary */ if ((node / m > 0) && (visited[node - m] == 0)) { set_feat_bold(x, y - 1, dun->feat_floor); r_visit(x1, y1, x2, y2, node - m, dir, visited); } break; } case 2: { /* (+,0) - check for right boundary */ if ((node % m < m - 1) && (visited[node + 1] == 0)) { set_feat_bold(x + 1, y, dun->feat_floor); r_visit(x1, y1, x2, y2, node + 1, dir, visited); } break; } case 3: { /* (-,0) - check for left boundary */ if ((node % m > 0) && (visited[node - 1] == 0)) { set_feat_bold(x - 1, y, dun->feat_floor); r_visit(x1, y1, x2, y2, node - 1, dir, visited); } } } } } static void build_maze_vault(int x0, int y0, int xsize, int ysize) { int dy, dx; int y1, x1, y2, x2; int m, n, num_vertices, *visited; if (cheat_room) msgf("Maze Vault"); /* Pick a random room size - randomized by calling routine */ dy = ysize / 2 - 1; dx = xsize / 2 - 1; y1 = y0 - dy; x1 = x0 - dx; y2 = y0 + dy; x2 = x0 + dx; /* generate the room */ generate_vault(x1 - 1, y1 - 1, x2 + 1, y2 + 1); /* Draw outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Fill room with inner walls */ generate_fill(x1, y1, x2, y2, FEAT_WALL_INNER); /* dimensions of vertex array */ m = dx + 1; n = dy + 1; num_vertices = m * n; /* initialize array of visited vertices */ C_MAKE(visited, num_vertices, int); /* traverse the graph to create a spaning tree, pick a random root */ r_visit(x1, y1, x2, y2, randint0(num_vertices), 0, visited); /* Fill with monsters and treasure, low difficulty */ fill_treasure(x1, y1, x2, y2, randint1(5)); FREE(visited); } /* * Build a "mini" checkerboard vault * * This is done by making a permanent wall maze and setting * the diagonal sqaures of the checker board to be granite. * The vault has two entrances on opposite sides to guarantee * a way to get in even if the vault abuts a side of the dungeon. */ static void build_mini_c_vault(int x0, int y0, int xsize, int ysize) { int dy, dx; int y1, x1, y2, x2, y, x, total; int m, n, num_vertices; int *visited; cave_type *c_ptr; if (cheat_room) msgf("Mini Checker Board Vault"); /* Pick a random room size */ dy = ysize / 2 - 1; dx = xsize / 2 - 1; y1 = y0 - dy; x1 = x0 - dx; y2 = y0 + dy; x2 = x0 + dx; /* generate the room */ generate_vault(x1 - 1, y1 - 1, x2 + 1, y2 + 1); /* Fill room with perm walls. */ generate_fill(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_PERM_INNER); /* dimensions of vertex array */ m = dx + 1; n = dy + 1; num_vertices = m * n; /* initialize array of visited vertices */ C_MAKE(visited, num_vertices, int); /* traverse the graph to create a spannng tree, pick a random root */ r_visit(x1, y1, x2, y2, randint0(num_vertices), 0, visited); /* Make it look like a checker board vault */ for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { c_ptr = cave_p(x, y); total = x - x1 + y - y1; /* If total is odd- and is a floor then make a wall */ if ((total % 2 == 1) && (c_ptr->feat == dun->feat_floor)) { set_feat_grid(c_ptr, FEAT_WALL_INNER); } } } /* Make a couple of entrances */ if (one_in_(2)) { /* left and right */ y = randint1(dy) + dy / 2; set_feat_bold(x1 - 1, y1 + y, FEAT_WALL_OUTER); set_feat_bold(x2 + 1, y1 + y, FEAT_WALL_OUTER); } else { /* top and bottom */ x = randint1(dx) + dx / 2; set_feat_bold(x1 + x, y1 - 1, FEAT_WALL_OUTER); set_feat_bold(x1 + x, y2 + 1, FEAT_WALL_OUTER); } /* Fill with monsters and treasure, highest difficulty */ fill_treasure(x1, y1, x2, y2, 10); FREE(visited); } /* * Build a town/ castle by using a recursive algorithm. * Basically divide each region to create smaller regions. * When they get too small, stop. * * The power variable is a measure of how well defended a region is. * This alters the possible choices when a region is split. */ static void build_recursive_room(int x1, int y1, int x2, int y2, int power) { int xsize, ysize; int x, y; int choice; /* Temp variables */ int t1, t2, t3, t4; xsize = x2 - x1; ysize = y2 - y1; if ((power < 3) && (xsize > 12) && (ysize > 12)) { /* Need outside wall +keep */ choice = 1; } else { if (power < 10) { /* Make rooms + subdivide */ if ((randint1(10) > 2) && (xsize < 8) && (ysize < 8)) { choice = 4; } else { choice = randint1(2) + 1; } } else { /* Mostly subdivide */ choice = randint1(3) + 1; } } /* Based on the choice made above, do something */ switch (choice) { case 1: { /* Outer walls */ generate_draw(x1, y1, x2, y2, FEAT_WALL_OUTER); /* Make a couple of entrances */ if (one_in_(2)) { /* left and right */ y = randint1(ysize) + y1; set_feat_bold(x1, y, dun->feat_floor); set_feat_bold(x2, y, dun->feat_floor); } else { /* top and bottom */ x = randint1(xsize) + x1; set_feat_bold(x, y1, dun->feat_floor); set_feat_bold(x, y2, dun->feat_floor); } /* Select size of keep */ t1 = randint1(ysize / 3) + y1; t2 = y2 - randint1(ysize / 3); t3 = randint1(xsize / 3) + x1; t4 = x2 - randint1(xsize / 3); /* Do outside areas */ /* Above and below keep */ build_recursive_room(x1 + 1, y1 + 1, x2 - 1, t1, power + 1); build_recursive_room(x1 + 1, t2, x2 - 1, y2, power + 1); /* Left and right of keep */ build_recursive_room(x1 + 1, t1 + 1, t3, t2 - 1, power + 3); build_recursive_room(t4, t1 + 1, x2 - 1, t2 - 1, power + 3); /* Make the keep itself: */ x1 = t3; x2 = t4; y1 = t1; y2 = t2; xsize = x2 - x1; ysize = y2 - y1; power += 2; /* Fall through */ } case 4: { /* Try to build a room */ if ((xsize < 3) || (ysize < 3)) { generate_fill(x1, y1, x2 - 1, y2 - 1, FEAT_WALL_INNER); /* Too small */ return; } /* Make outside walls */ generate_draw(x1 + 1, y1 + 1, x2 - 1, y2 - 1, FEAT_WALL_INNER); /* Make a door */ y = rand_range(y1 + 1, y1 + ysize - 2); if (one_in_(2)) { /* left */ set_feat_bold(x1 + 1, y, dun->feat_floor); } else { /* right */ set_feat_bold(x2 - 1, y, dun->feat_floor); } /* Build the room */ build_recursive_room(x1 + 2, y1 + 2, x2 - 2, y2 - 2, power + 3); break; } case 2: { /* Try and divide vertically */ if (xsize < 3) { generate_fill(x1, y1, x2 - 1, y2 - 1, FEAT_WALL_INNER); /* Too small */ return; } t1 = rand_range(x1 + 2, x1 + xsize - 1); build_recursive_room(x1, y1, t1, y2, power - 2); build_recursive_room(t1 + 1, y1, x2, y2, power - 2); break; } case 3: { /* Try and divide horizontally */ if (ysize < 3) { generate_fill(x1, y1, x2 - 1, y2 - 1, FEAT_WALL_INNER); /* Too small */ return; } t1 = rand_range(y1 + 2, y1 + ysize - 1); build_recursive_room(x1, y1, x2, t1, power - 2); build_recursive_room(x1, t1 + 1, x2, y2, power - 2); break; } } } /* Build a castle */ /* * Driver routine: clear the region and call the recursive * room routine. * * This makes a vault that looks like a castle/ city in the dungeon. */ static void build_castle_vault(int x0, int y0, int xsize, int ysize) { int dy, dx; int y1, x1, y2, x2; /* Pick a random room size */ dy = ysize / 2 - 1; dx = xsize / 2 - 1; y1 = y0 - dy; x1 = x0 - dx; y2 = y0 + dy; x2 = x0 + dx; if (cheat_room) msgf("Castle Vault"); /* generate the room */ generate_vault(x1 - 1, y1 - 1, x2 + 1, y2 + 1); /* Make the whole room floor */ generate_fill(x1 - 1, y1 - 1, x2 + 1, y2 + 1, dun->feat_floor); /* Make the castle */ build_recursive_room(x1, y1, x2, y2, randint1(5)); /* Fill with monsters and treasure, low difficulty */ fill_treasure(x1, y1, x2, y2, randint1(3)); } /* * Add outer wall to a floored region * Note: no range checking is done so must be inside dungeon * This routine also stomps on doors */ static void add_outer_wall(int x, int y, int light, int x1, int y1, int x2, int y2) { int i, j; cave_type *c_ptr; if (!in_bounds2(x, y)) return; c_ptr = cave_p(x, y); /* * Hack- check to see if square has been visited before * if so, then exit (use room flag to do this) */ if (c_ptr->info & CAVE_ROOM) return; /* set room flag */ c_ptr->info |= CAVE_ROOM; if (cave_floor_grid(c_ptr)) { for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { if ((x + i >= x1) && (x + i <= x2) && (y + j >= y1) && (y + j <= y2)) { add_outer_wall(x + i, y + j, light, x1, y1, x2, y2); } } } if (light) c_ptr->info |= CAVE_GLOW; } else if (c_ptr->feat == FEAT_WALL_EXTRA) { /* Set bounding walls */ set_feat_grid(c_ptr, FEAT_WALL_OUTER); if (light) c_ptr->info |= CAVE_GLOW; } else if (c_ptr->feat == FEAT_PERM_OUTER) { /* Set bounding walls */ if (light) c_ptr->info |= CAVE_GLOW; } } /* * Hacked distance formula - gives the 'wrong' answer. * Used to build crypts */ static int dist2(int x1, int y1, int x2, int y2, int h1, int h2, int h3, int h4) { int dx, dy; dx = ABS(x2 - x1); dy = ABS(y2 - y1); /* Basically this works by taking the normal pythagorean formula * and using an expansion to express this in a way without the * square root. This approximate formula is then perturbed to give * the distorted results. (I found this by making a mistake when I was * trying to fix the circular rooms.) */ /* h1-h4 are constants that describe the metric */ if (dx >= 2 * dy) return (dx + (dy * h1) / h2); if (dy >= 2 * dx) return (dy + (dx * h1) / h2); return (((dx + dy) * 128) / 181 + (dx * dx / (dy * h3) + dy * dy / (dx * h3)) * h4); /* 128/181 is approx. 1/sqrt(2) */ } /* * Build target vault. * This is made by two concentric "crypts" with perpendicular * walls creating the cross-hairs. */ static void build_target_vault(int x0, int y0, int xsize, int ysize) { int rad, x, y; cave_type *c_ptr; /* Make a random metric */ int h1, h2, h3, h4; h1 = randint1(32) - 16; h2 = randint1(16); h3 = randint1(32); h4 = randint1(32) - 16; if (cheat_room) msgf("Target Vault"); /* work out outer radius */ if (xsize > ysize) { rad = ysize / 2; } else { rad = xsize / 2; } /* Outer walls */ generate_draw(x0 - rad, y0 - rad, x0 + rad, y0 + rad, FEAT_WALL_EXTRA); /* Make floor */ for (x = x0 - rad + 1; x <= x0 + rad - 1; x++) { for (y = y0 - rad + 1; y <= y0 + rad - 1; y++) { c_ptr = cave_p(x, y); /* clear room flag */ c_ptr->info &= ~(CAVE_ROOM); /* Vault - so is "icky" */ c_ptr->info |= CAVE_ICKY; if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1) { /* inside- so is floor */ set_feat_grid(c_ptr, dun->feat_floor); } else { /* make granite outside so arena works */ set_feat_grid(c_ptr, FEAT_WALL_EXTRA); } } } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(x0, y0, FALSE, x0 - rad - 1, y0 - rad - 1, x0 + rad + 1, y0 + rad + 1); /* Add inner wall */ for (x = x0 - rad / 2; x <= x0 + rad / 2; x++) { for (y = y0 - rad / 2; y <= y0 + rad / 2; y++) { if (dist2(y0, x0, y, x, h1, h2, h3, h4) == rad / 2) { /* Make an internal wall */ set_feat_bold(x, y, FEAT_WALL_INNER); } } } /* Make perpendicular walls */ generate_plus(x0 - rad, y0 - rad, x0 + rad, y0 + rad, FEAT_WALL_INNER); /* Make inner vault */ generate_draw(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FEAT_WALL_INNER); /* Make inner room */ set_feat_bold(x0, y0, dun->feat_floor); /* Add doors to vault */ /* get two distances so can place doors relative to centre */ x = (rad - 2) / 4 + 1; y = rad / 2 + x; add_door(x0 + x, y0); add_door(x0 + y, y0); add_door(x0 - x, y0); add_door(x0 - y, y0); add_door(x0, y0 + x); add_door(x0, y0 + y); add_door(x0, y0 - x); add_door(x0, y0 - y); /* Fill with stuff - medium difficulty */ fill_treasure(x0 - rad, y0 - rad, x0 + rad, y0 + rad, rand_range(3, 6)); } /* * This routine uses a modified version of the lake code to make a * distribution of some terrain type over the vault. This type * depends on the dungeon depth. * * Miniature rooms are then scattered across the vault. */ static void build_elemental_vault(int x0, int y0, int xsiz, int ysiz) { int grd, roug; int c1, c2, c3; bool done = FALSE; int xsize, ysize, xhsize, yhsize, i; byte f1, f2, f3; if (cheat_room) msgf("Elemental Vault"); /* round to make sizes even */ xhsize = xsiz / 2; yhsize = ysiz / 2; xsize = xhsize * 2; ysize = yhsize * 2; if (p_ptr->depth < 25) { /* Earth vault (Rubble) */ f1 = FEAT_RUBBLE; f2 = dun->feat_floor; f3 = FEAT_RUBBLE; } else if (p_ptr->depth < 50) { /* Air vault (Trees) */ f1 = FEAT_GRASS; f2 = FEAT_TREES; f3 = FEAT_GRASS; } else if (p_ptr->depth < 75) { /* Water vault (shallow water) */ f1 = FEAT_SHAL_WATER; f2 = FEAT_DEEP_WATER; f3 = FEAT_SHAL_WATER; } else { /* Fire vault (shallow lava) */ f1 = FEAT_SHAL_LAVA; f2 = FEAT_DEEP_LAVA; f3 = FEAT_SHAL_LAVA; } while (!done) { /* testing values for these parameters: feel free to adjust */ grd = 1 << (randint0(3)); /* want average of about 16 */ roug = randint1(8) * randint1(4); /* Make up size of various componants */ /* Floor */ c3 = 2 * xsize / 3; /* Deep water/lava */ c1 = randint0(c3 / 2) + randint0(c3 / 2) - 5; /* Shallow boundary */ c2 = (c1 + c3) / 2; /* make it */ generate_hmap(x0, y0, xsize, ysize, grd, roug, c3); /* Convert to normal format + clean up */ done = generate_lake(x0, y0, xsize, ysize, c1, c2, c3, f1, f2, f3); } /* Set icky flag because is a vault */ generate_vault(x0 - xhsize, y0 - yhsize, x0 - xhsize + xsize, y0 - yhsize + ysize); /* make a few rooms in the vault */ for (i = 1; i <= (xsize * ysize) / 50; i++) { build_small_room(x0 + randint0(xsize - 4) - xsize / 2 + 2, y0 + randint0(ysize - 4) - ysize / 2 + 2); } /* Fill with monsters and treasure, low difficulty */ fill_treasure(x0 - xhsize + 1, y0 - yhsize + 1, x0 - xhsize + xsize - 1, y0 - yhsize + ysize - 1, randint1(5)); } /* * This makes a vault that has many micro-rooms. */ static void build_micro_room_vault(int x0, int y0, int xsize, int ysize) { int dy, dx; int y1, x1, y2, x2; int i, j; /* Pick a random room size */ dy = ysize / 2 - 1; dx = xsize / 2 - 1; y1 = y0 - dy; x1 = x0 - dx; y2 = y0 + dy; x2 = x0 + dx; if (cheat_room) msgf("Micro-Room Vault"); /* generate the room */ generate_vault(x1 - 1, y1 - 1, x2 + 1, y2 + 1); /* Make the whole room floor */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Make outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Make a grid of "features" */ for (j = y1 + 2; j < y2 - 1; j += 4) { for (i = x1 + 2; i < x2 - 1; i += 4) { if (one_in_(2)) { /* A tiny room */ build_small_room(i, j); } else if (one_in_(2)) { /* 1/4 chance for a pillar */ generate_fill(i - 1, j - 1, i + 1, j + 1, FEAT_WALL_INNER); } else if (one_in_(2)) { /* 1/8 chance for a plus */ generate_plus(i - 1, j - 1, i + 1, j + 1, FEAT_WALL_INNER); } } } /* Make a set of walls to break up the flow */ for (j = y1; j < y2 - 1; j += 4) { for (i = x1; i < x2 - 1; i += 4) { if (one_in_(2)) { set_feat_bold(i, j, FEAT_WALL_INNER); } } } /* Fill with monsters and treasure, low difficulty */ fill_treasure(x1, y1, x2, y2, randint1(5)); } /* * Type 10 -- Random vault */ static void build_type10(int bx0, int by0) { int y0, x0, xsize, ysize, vtype; /* * Get size * Big enough to look good, small enough to be fairly common. */ xsize = rand_range(22, 44); ysize = rand_range(11, 22); /* Allocate in room_map. If will not fit, exit */ if (!room_alloc(xsize + 1, ysize + 1, FALSE, bx0, by0, &x0, &y0)) return; /* * Boost the rating- higher than lesser vaults * and lower than greater vaults */ inc_rating(10); /* (Sometimes) Cause a special feeling */ if ((p_ptr->depth <= 50) || (randint1((p_ptr->depth - 40) * (p_ptr->depth - 40) + 1) < 400)) { set_special(); } /* Select type of vault */ vtype = randint1(9); /* Build an appropriate room */ switch (vtype) { case 1: { build_bubble_vault(x0, y0, xsize, ysize); break; } case 2: { build_room_vault(x0, y0, xsize, ysize); break; } case 3: { build_cave_vault(x0, y0, xsize, ysize); break; } case 4: { build_maze_vault(x0, y0, xsize, ysize); break; } case 5: { build_mini_c_vault(x0, y0, xsize, ysize); break; } case 6: { build_castle_vault(x0, y0, xsize, ysize); break; } case 7: { build_target_vault(x0, y0, xsize, ysize); break; } case 8: { build_elemental_vault(x0, y0, xsize, ysize); break; } case 9: { build_micro_room_vault(x0, y0, xsize, ysize); break; } } } /* * Type 11 -- Vertical oval room. * For every grid in the possible square, check the distance. * If it's less than the radius, make it a room square. * * When done fill from the inside to find the walls, */ static void build_type11(int bx0, int by0) { int rad, x, y, x0, y0; int light = FALSE; /* Occasional light */ if (randint1(p_ptr->depth) <= 15) light = TRUE; rad = rand_range(2, 9); /* Allocate in room_map. If will not fit, exit */ if (!room_alloc(rad * 2 + 1, rad * 2 + 1, FALSE, bx0, by0, &x0, &y0)) return; /* Make circular floor */ for (x = x0 - rad; x <= x0 + rad; x++) { for (y = y0 - rad; y <= y0 + rad; y++) { if (distance(x0, y0, x, y) <= rad - 1) { /* inside- so is floor */ set_feat_bold(x, y, dun->feat_floor); } else if (distance(x0, y0, x, y) <= rad + 1) { /* make granite outside so arena works */ set_feat_bold(x, y, FEAT_WALL_EXTRA); } } } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(x0, y0, light, x0 - rad, y0 - rad, x0 + rad, y0 + rad); if (one_in_(3)) { rad = randint1(rad); /* Make circular liquid feature */ for (x = x0 - rad; x <= x0 + rad; x++) { for (y = y0 - rad; y <= y0 + rad; y++) { if (distance(x0, y0, x, y) <= rad - 1) { /* inside- so is floor */ set_feat_bold(x, y, dun->feat_shal_liquid); } } } } } /* * Type 12 -- Crypt room. * For every grid in the possible square, check the (fake) distance. * If it's less than the radius, make it a room square. * * When done fill from the inside to find the walls, */ static void build_type12(int bx0, int by0) { int rad, x, y, x0, y0; int light = FALSE; bool emptyflag = TRUE; cave_type *c_ptr; /* Make a random metric */ int h1, h2, h3, h4; h1 = randint1(32) - 16; h2 = randint1(16); h3 = randint1(32); h4 = randint1(32) - 16; /* Occasional light */ if (randint1(p_ptr->depth) <= 5) light = TRUE; rad = randint1(9); /* Allocate in room_map. If will not fit, exit */ if (!room_alloc(rad * 2 + 3, rad * 2 + 3, FALSE, bx0, by0, &x0, &y0)) return; /* Add outer wall */ generate_draw(x0 - rad, y0 - rad, x0 + rad, y0 + rad, FEAT_WALL_EXTRA); /* Make floor */ for (x = x0 - rad + 1; x <= x0 + rad - 1; x++) { for (y = y0 - rad + 1; y <= y0 + rad - 1; y++) { c_ptr = cave_p(x, y); /* clear room flag */ c_ptr->info &= ~(CAVE_ROOM); if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1) { /* inside - so is floor */ set_feat_grid(c_ptr, dun->feat_floor); } else if (distance(x0, y0, x, y) < 3) { set_feat_grid(c_ptr, dun->feat_floor); } else { /* make granite outside so arena works */ set_feat_grid(c_ptr, FEAT_WALL_EXTRA); } } } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(x0, y0, light, x0 - rad - 1, y0 - rad - 1, x0 + rad + 1, y0 + rad + 1); /* Check to see if there is room for an inner vault */ for (x = x0 - 2; x <= x0 + 2; x++) { for (y = y0 - 2; y <= y0 + 2; y++) { if (cave_p(x, y)->feat != dun->feat_floor) { /* Wall in the way */ emptyflag = FALSE; } } } if (emptyflag && one_in_(2)) { /* Build the vault */ build_small_room(x0, y0); /* Place a treasure in the vault */ place_object(x0, y0, FALSE, FALSE, 0); /* Let's guard the treasure well */ vault_monsters(x0, y0, rand_range(2, 4)); /* Traps naturally */ vault_traps(x0, y0, 4, 4, rand_range(2, 4)); } } /* * Type 13 -- Rectangular room with central fractal feature */ static void build_type13(int bx0, int by0) { int grd, roug, xsize, ysize, xhsize, yhsize, y0, x0; int c1, c2, c3; byte f1 = 0, f2 = 0, f3 = 0; bool done; /* get size: note 'Evenness' */ xsize = randint1(22) * 2 + 6; ysize = randint1(15) * 2 + 6; /* round to make sizes even */ xhsize = (xsize - 1) / 2; yhsize = (ysize - 1) / 2; xsize = xhsize * 2; ysize = yhsize * 2; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 1, ysize + 1, FALSE, bx0, by0, &x0, &y0)) return; done = FALSE; /* Pick the type */ switch (randint0(4)) { case 0: { /* Water */ f1 = FEAT_DEEP_WATER; f2 = FEAT_SHAL_WATER; f3 = dun->feat_floor; break; } case 1: { /* Lava */ f1 = FEAT_DEEP_LAVA; f2 = FEAT_SHAL_LAVA; f3 = dun->feat_floor; break; } case 2: { /* Rock */ f1 = FEAT_WALL_INNER; f2 = FEAT_WALL_INNER; f3 = dun->feat_floor; break; } case 3: { /* Rubble - oooh treasure */ f1 = FEAT_RUBBLE; f2 = FEAT_RUBBLE; f3 = dun->feat_floor; break; } } while (!done) { /* testing values for these parameters: feel free to adjust */ grd = 1 << (randint0(4)); /* want average of about 16 */ roug = randint1(8) * randint1(4); /* Make up size of various componants */ /* Floor */ c3 = xsize; /* Deep water/lava */ c1 = randint0(c3 / 2); /* Shallow boundary */ c2 = (c1 + c3) / 2; /* make it */ generate_hmap(x0, y0, xsize, ysize, grd, roug, c3); /* Convert to normal format + clean up */ done = generate_lake(x0, y0, xsize, ysize, c1, c2, c3, f1, f2, f3); } /* Make inner passage */ generate_draw(x0 - xhsize + 1, y0 - yhsize + 1, x0 - xhsize + xsize - 2, y0 - yhsize + ysize - 2, dun->feat_floor); } /* * Type 14 -- Large room with inner walls * * Possible sub-types: * 1 - Many horizontal walls * 2 - S-shaped passage * 3 - Single horizontal wall * 4 - Inner Plus * 5 - Anti-Plus breaking the room into 4 segments */ static void build_type14(int bx0, int by0) { int y, x, y1, x1; int y2, x2, yval, xval; bool light; /* Try to allocate space for room. */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Large room */ y1 = yval - 4; y2 = yval + 4; x1 = xval - 11; x2 = xval + 11; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Add some walls */ /* Select what type of room */ switch (randint0(5)) { case 0: { /* Horizontal walls */ for (x = x1 + 1; x < x2; x++) { set_feat_bold(x, yval - 3, FEAT_WALL_INNER); set_feat_bold(x, yval - 1, FEAT_WALL_INNER); set_feat_bold(x, yval + 1, FEAT_WALL_INNER); set_feat_bold(x, yval + 3, FEAT_WALL_INNER); } break; } case 1: { /* S shape */ /* Select handedness */ int sgn = (randint0(2) * 2 - 1) * 3; /* Top wall */ for (y = y1; y <= yval; y++) { set_feat_bold(xval + sgn, y, FEAT_WALL_INNER); } /* Bottom Wall */ for (y = y2; y >= yval; y--) { set_feat_bold(xval - sgn, y, FEAT_WALL_INNER); } break; } case 2: { /* Horizontal internal wall */ for (x = xval - 4; x <= xval + 4; x++) { set_feat_bold(x, yval, FEAT_WALL_INNER); } break; } case 3: { /* Plus */ generate_plus(x1 + 2, y1 + 2, x2 - 2, y2 - 2, FEAT_WALL_INNER); break; } case 4: { /* Anti-plus */ generate_plus(x1, y1, x2, y2, FEAT_WALL_INNER); generate_plus(x1 + 5, y1 + 3, x2 - 5, y2 - 3, dun->feat_floor); break; } } } /* * Type 15 -- Parallelogram Shaped Rooms */ static void build_type15(int bx0, int by0) { u16b h, w; int y, x, y1, x1, yval, xval; bool light, type; /* Get size + shape */ h = (u16b)rand_range(6, 15); w = (u16b)rand_range(11, 22); /* Try to allocate space for room. */ if (!room_alloc(w + h, h, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get top left corner */ x1 = xval - (w + h) / 2; y1 = yval - h / 2; /* Get handedness */ type = (one_in_(2) ? TRUE : FALSE); /* Fill in floor */ for (y = 1; y < h; y++) { for (x = x1; x < x1 + w; x++) { if (type) { /* Sloping down and right */ set_feat_bold(x + y, y + y1, dun->feat_floor); } else { /* Sloping up and right */ set_feat_bold(x + h - y, y + y1, dun->feat_floor); } } } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(xval, yval, light, x1, y1, x1 + h + w, y1 + h); } /* * Type 16 -- Rectangular room with chunks removed */ static void build_type16(int bx0, int by0) { int xval, yval; int y1, x1, y2, x2; int tx1 = 0, tx2 = 0, ty1 = 0, ty2 = 0; bool light; int num, i; int xsize, ysize; /* Pick a room size */ y1 = randint1(4); x1 = randint1(11); y2 = randint1(3); x2 = randint1(11); xsize = x1 + x2 + 1; ysize = y1 + y2 + 1; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 2, ysize + 2, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get corner values */ y1 = yval - ysize / 2; x1 = xval - xsize / 2; y2 = yval + (ysize - 1) / 2; x2 = xval + (xsize - 1) / 2; /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Fill boundary with random rectangles */ /* Determine number of sections to use */ num = rand_range(1, 2); for (i = 0; i < num; i++) { switch (randint1(4)) { case 1: { /* Top Left */ tx1 = x1; tx2 = rand_range((x1 + xval) / 2, xval - 1); ty1 = y1; ty2 = rand_range((y1 + yval) / 2, yval - 1); break; } case 2: { /* Bottom Left */ tx1 = x1; tx2 = rand_range((x1 + xval) / 2, xval - 1); ty1 = rand_range(yval + 1, (yval + y2) / 2); ty2 = y2; break; } case 3: { /* Top Right */ tx1 = rand_range(xval + 1, (xval + x2) / 2); tx2 = x2; ty1 = y1; ty2 = rand_range((y1 + yval) / 2, yval - 1); break; } case 4: { /* Bottom Right */ tx1 = rand_range(xval + 1, (xval + x2) / 2); tx2 = x2; ty1 = rand_range(yval + 1, (yval + y2) / 2); ty2 = y2; break; } } /* Create regions */ generate_fill(tx1, ty1, tx2, ty2, FEAT_WALL_EXTRA); } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(xval, yval, light, x1 - 1, y1 - 1, x2 + 1, y2 + 1); } /* * Test a point to see if it lies within a triangular * shaped region defined by three other points. * * det is the determinate of two vectors. * * (It is the sign of the "handedness" of the triangle * 1/2 * of its area.) */ static bool test_tri(int px, int py, int x1, int y1, int x2, int y2, int x3, int y3, int det) { /* * Use the cross product to make sure the point is on the 'right side' * of the angle defined by the particular side, and the vector from a * vertex of that side to the specified point. * * Do this three times, once for each side of the triangle. */ if (det > 0) { if ((x2 - x1) * (py - y1) - (px - x1) * (y2 - y1) < 0) return (FALSE); if ((x3 - x2) * (py - y2) - (px - x2) * (y3 - y2) < 0) return (FALSE); if ((x1 - x3) * (py - y3) - (px - x3) * (y1 - y3) < 0) return (FALSE); } else { if ((x2 - x1) * (py - y1) - (px - x1) * (y2 - y1) > 0) return (FALSE); if ((x3 - x2) * (py - y2) - (px - x2) * (y3 - y2) > 0) return (FALSE); if ((x1 - x3) * (py - y3) - (px - x3) * (y1 - y3) > 0) return (FALSE); } /* Inside the region. */ return (TRUE); } /* * Make sure two squares are connected by floors. */ static void connectsq(int x1, int y1, int x2, int y2) { int x, y; int l, length = distance(x1, y1, x2, y2); /* Paranoia */ if (!length) return; /* Be dumb, and use a straight line */ for (l = 0; l <= length; l++) { x = x1 + l * (x2 - x1) / length; y = y1 + l * (y2 - y1) / length; set_feat_bold(x, y, dun->feat_floor); } } /* * Type 17 -- Room made of Triangles */ static void build_type17(int bx0, int by0) { int xval, yval; int y1, x1, y2, x2; bool light; int det; int x, y; int vx1, vy1, vx2, vy2, vx3, vy3; int num, i; int xsize, ysize; /* Pick a room size */ y1 = randint1(14); x1 = randint1(14); y2 = randint1(14); x2 = randint1(14); xsize = x1 + x2 + 1; ysize = y1 + y2 + 1; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 2, ysize + 2, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get corner values */ y1 = yval - ysize / 2; x1 = xval - xsize / 2; y2 = yval + (ysize - 1) / 2; x2 = xval + (xsize - 1) / 2; /* Paranoia - Room is too small, just make a rectangle */ if (xsize * ysize < 20) { /* Generate new room */ generate_room(x1, y1, x2, y2, light); /* Generate inner floors */ generate_fill(x1 + 1, y1 + 1, x2 - 1, y2 - 1, dun->feat_floor); /* Generate outer walls */ generate_draw(x1, y1, x2, y2, FEAT_WALL_OUTER); /* Done */ return; } /* Determine number of shapes to use */ num = rand_range(2, 4); /* Fill with random triangles */ /* Make num rooms */ for (i = 0; i < num; i++) { do { /* Get vertices */ vx1 = rand_range(x1, x2); vx2 = rand_range(x1, x2); vx3 = rand_range(x1, x2); vy1 = rand_range(y1, y2); vy2 = rand_range(y1, y2); vy3 = rand_range(y1, y2); /* * Calculate the cross product of two vectors that * define the sides of the triangle. * * This will be equal to twice the area of the triangle * times a sign factor that depends on the ordering * of the vertex points in space. */ det = (vx2 - vx1) * (vy3 - vy1) - (vx3 - vx1) * (vy2 - vy1); /* Make sure the triangle is large enough. */ } while (abs(det) < 10); for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { if (test_tri(x, y, vx1, vy1, vx2, vy2, vx3, vy3, det)) { set_feat_bold(x, y, dun->feat_floor); } } } /* Hack - connect to room center */ connectsq(xval, yval, (vx1 + vx2 + vx3) / 3, (vy1 + vy2 + vy3) / 3); /* Hack - connect vertexes to avoid problems with rounding */ connectsq(vx1, vy1, vx2, vy2); connectsq(vx1, vy1, vx3, vy3); connectsq(vx3, vy3, vx2, vy2); } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(xval, yval, light, x1 - 1, y1 - 1, x2 + 1, y2 + 1); } /* * Type 18 -- Large room with many small rooms inside. * * (A jail). */ static void build_type18(int bx0, int by0) { int y1, x1; int y2, x2, yval, xval; bool light; int i; /* Try to allocate space for room. */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Large room */ y1 = yval - 4; y2 = yval + 4; x1 = xval - 11; x2 = xval + 11; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); for (i = 0; i < 6; i++) { generate_draw(x1 - 1 + i * 4, y1 - 1, x1 + 3 + i * 4, yval - 1, FEAT_WALL_INNER); generate_draw(x1 - 1 + i * 4, yval + 1, x1 + 3 + i * 4, y2 + 1, FEAT_WALL_INNER); place_random_door(x1 + 1 + i * 4, yval - 1); place_random_door(x1 + 1 + i * 4, yval + 1); } /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); } /* * Type 19 -- Channel or reservoir. */ static void build_type19(int bx0, int by0) { int y, x, y1, x1; int y2, x2, yval, xval; bool light; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Vertical */ if (one_in_(2)) { /* Try to allocate space for room. */ if (!room_alloc(41, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Large, long room */ y1 = yval - 4; y2 = yval + 4; x1 = xval - 19; x2 = xval + 19; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate floor */ generate_draw(x1, y1, x2, y2, dun->feat_floor); /* Generate liquid */ generate_draw(x1, y1 + 1, x2, y2 - 1, dun->feat_shal_liquid); for (x = x1; x <= x2; x++) { for (y = y1 + 2; y <= y2 - 2; y++) { if (randint1(p_ptr->depth + 10) > 10) set_feat_bold(x, y, dun->feat_deep_liquid); else set_feat_bold(x, y, dun->feat_shal_liquid); } } } else { /* Try to allocate space for room. */ if (!room_alloc(11, 33, FALSE, bx0, by0, &xval, &yval)) return; /* Large, long room */ y1 = yval - 15; y2 = yval + 15; x1 = xval - 4; x2 = xval + 4; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate floor */ generate_draw(x1, y1, x2, y2, dun->feat_floor); /* Generate liquid */ generate_draw(x1 + 1, y1, x2 - 1, y2, dun->feat_shal_liquid); for (x = x1 + 2; x <= x2 - 2; x++) { for (y = y1; y <= y2; y++) { if (randint1(p_ptr->depth + 10) > 10) set_feat_bold(x, y, dun->feat_deep_liquid); else set_feat_bold(x, y, dun->feat_shal_liquid); } } } } /* * Type 20 -- Collapsed room. */ static void build_type20(int bx0, int by0) { int y1, x1; int y2, x2, yval, xval; bool light; int xsize, ysize; int i; /* Pick a room size */ y1 = randint1(4); x1 = randint1(11); y2 = randint1(3); x2 = randint1(11); xsize = x1 + x2 + 1; ysize = y1 + y2 + 1; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 2, ysize + 2, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get corner values */ y1 = yval - ysize / 2; x1 = xval - xsize / 2; y2 = yval + (ysize - 1) / 2; x2 = xval + (xsize - 1) / 2; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Fill with rock and rubble */ for (i = randint1(xsize * ysize / 4); i > 0; i--) { if (one_in_(2)) { /* Rock */ set_feat_bold(rand_range(x1, x2), rand_range(y1, y2), FEAT_WALL_INNER); } else { /* Rubble */ set_feat_bold(rand_range(x1, x2), rand_range(y1, y2), FEAT_RUBBLE); } } } /* * Type 21 -- Crypt Mk II. */ static void build_type21(int bx0, int by0) { int y1, x1; int y2, yval, xval; bool light; int i; /* Try to allocate space for room. */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Large room */ y1 = yval - 4; y2 = yval + 4; x1 = xval - 11; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x1 + 21, y2 + 1, light); /* Draw the chambers */ for (i = 0; i < 3; i++) { /* Top room */ /* Generate outer walls */ generate_draw(x1 - 1 + i * 8, y1 - 1, x1 + 5 + i * 8, y1 + 3, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1 + i * 8, y1, x1 + 4 + i * 8, y1 + 2, dun->feat_floor); /* Bottom room */ /* Generate outer walls */ generate_draw(x1 - 1 + i * 8, y2 - 3, x1 + 5 + i * 8, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1 + i * 8, y2 - 2, x1 + 4 + i * 8, y2, dun->feat_floor); } /* * Hack - fill middle area with FEAT_WALL_INNER * so it doesn't get disturbed. */ generate_draw(x1, yval - 1, x1 + 20, yval + 1, FEAT_WALL_INNER); /* Draw the connecting tunnels */ for (i = 0; i < 3; i++) { generate_line(x1 + i * 8 + 2, y1 + 3, x1 + i * 8 + 2, y2 - 3, dun->feat_floor); } /* Finally - connect the chambers */ generate_line(x1 - 1, yval, x1 + 21, yval, dun->feat_floor); } /* * Type 22 -- Large Chamber */ static void build_type22(int bx0, int by0) { int xval, yval; int y1, x1, y2, x2; bool light; int x, y; int xcount, ycount; int xsize, ysize; /* Pick a room size */ y1 = rand_range(5, 14); x1 = rand_range(5, 20); y2 = rand_range(5, 14); x2 = rand_range(5, 20); xsize = x1 + x2 + 1; ysize = y1 + y2 + 1; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 2, ysize + 2, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get corner values */ y1 = yval - ysize / 2; x1 = xval - xsize / 2; y2 = yval + (ysize - 1) / 2; x2 = xval + (xsize - 1) / 2; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Work out how many pillars to use in each direction */ xcount = xsize / 5; ycount = ysize / 5; /* Add some pillars */ for (y = 0; y <= ycount; y++) { for (x = 0; x <= xcount; x++) { set_feat_bold(x1 + (xsize - 1) * x / xcount, y1 + (ysize - 1) * y / ycount, FEAT_PILLAR); } } } /* * Type 23 -- Semicircular room. */ static void build_type23(int bx0, int by0) { int rad, x, y, x0, y0; int light = FALSE; int xc, yc; /* Occasional light */ if (randint1(p_ptr->depth) <= 15) light = TRUE; rad = rand_range(2, 9); /* Get orientation */ if (one_in_(2)) { /* Allocate in room_map. If will not fit, exit */ if (!room_alloc(rad + 1, rad * 2 + 1, FALSE, bx0, by0, &x0, &y0)) return; /* Flip left or right? */ if (one_in_(2)) { xc = x0 - rad / 2; } else { xc = x0 + rad / 2; } /* Make semicircular floor */ for (x = x0 - rad / 2; x <= x0 + rad / 2; x++) { for (y = y0 - rad; y <= y0 + rad; y++) { if (distance(xc, y0, x, y) <= rad - 1) { /* inside- so is floor */ set_feat_bold(x, y, dun->feat_floor); } else if (distance(xc, y0, x, y) <= rad + 1) { /* make granite outside so arena works */ set_feat_bold(x, y, FEAT_WALL_EXTRA); } } } } else { /* Allocate in room_map. If will not fit, exit */ if (!room_alloc(rad * 2 + 1, rad + 1, FALSE, bx0, by0, &x0, &y0)) return; /* Flip up or down? */ if (one_in_(2)) { yc = y0 - rad / 2; } else { yc = y0 + rad / 2; } /* Make semicircular floor */ for (x = x0 - rad; x <= x0 + rad; x++) { for (y = y0 - rad / 2; y <= y0 + rad / 2; y++) { if (distance(x0, yc, x, y) <= rad - 1) { /* inside- so is floor */ set_feat_bold(x, y, dun->feat_floor); } else if (distance(x0, yc, x, y) <= rad + 1) { /* make granite outside so arena works */ set_feat_bold(x, y, FEAT_WALL_EXTRA); } } } } /* Find visible outer walls and set to be FEAT_OUTER */ add_outer_wall(x0, y0, light, x0 - rad, y0 - rad, x0 + rad, y0 + rad); } /* * Type 24 -- Hourglass-shaped room. */ static void build_type24(int bx0, int by0) { int y1, x1; int y2, x2, yval, xval; bool light; int i; /* Try to allocate space for room. */ if (!room_alloc(25, 11, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Large room */ y1 = yval - 4; y2 = yval + 4; x1 = xval - 11; x2 = xval + 11; /* Generate new room */ generate_room(x1 - 1, y1 - 1, x2 + 1, y2 + 1, light); /* Generate outer walls */ generate_draw(x1 - 1, y1 - 1, x2 + 1, y2 + 1, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(x1, y1, x2, y2, dun->feat_floor); /* Create triangular features */ for (i = 1; i < 4; i++) { generate_line(x1 + i * 3, y1 + i, x2 - i * 3, y1 + i, FEAT_WALL_INNER); generate_line(x1 + i * 3, y2 - i, x2 - i * 3, y2 - i, FEAT_WALL_INNER); } /* Put a door in the middle */ place_random_door(xval, yval); } /* * Overlay a room that will overlap others. */ static void overdraw_room(int x1, int y1, int x2, int y2, bool light) { int x, y, i, j; cave_type *c_ptr; bool connected = FALSE; int overlap = 0; int empty = 0; int wallcount = 0; /* Pass 1: Check that there is overlap */ for (x = x1; x <= x2; x++) { for(y = y1; y <= y2; y++) { c_ptr = cave_p(x, y); /* There is a room square here */ if (c_ptr->info & CAVE_ROOM) { overlap++; /* Is it an outer wall? */ if (c_ptr->feat == FEAT_WALL_OUTER) { wallcount++; } } else { empty++; } } } /* The room must connect with others */ if (overlap < 5) return; /* Not too much overlap */ if (overlap > 20) return; /* Does it add enough? */ if (empty < 30) return; /* Pass 2: Add floor */ for (x = x1 + 1; x <= x2 - 1; x++) { for(y = y1 + 1; y <= y2 - 1; y++) { c_ptr = cave_p(x, y); /* There is a room square here */ if (c_ptr->info & CAVE_ROOM) continue; cave_set_feat(x, y, dun->feat_floor); } } /* Pass 3: Add outer walls */ for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { c_ptr = cave_p(x, y); /* There is a room square here */ if (!(c_ptr->info & CAVE_ROOM)) { if (c_ptr->feat == FEAT_WALL_EXTRA) { /* Scan for adjacent floor */ for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { /* Paranoia */ if (!in_bounds(x + i, y + j)) continue; /* Are we next to a floor? */ if (cave_p(x + i, y + j)->feat == dun->feat_floor) { /* Outer walls */ cave_set_feat(x, y, FEAT_WALL_OUTER); /* Quickly exit the nested loops */ goto exitloop; } } } } } exitloop: ; } } /* Pass 4: Add doors */ for (x = x1; x <= x2; x++) { for(y = y1; y <= y2; y++) { c_ptr = cave_p(x, y); /* There is a room square here */ if (c_ptr->info & CAVE_ROOM) { if (one_in_(wallcount / 2) && add_door(x, y)) { connected = TRUE; } } } } /* Pass 5: Convert to inner walls, and build the room itself */ for (x = x1; x <= x2; x++) { for(y = y1; y <= y2; y++) { c_ptr = cave_p(x, y); /* There is a room square here */ if (c_ptr->info & CAVE_ROOM) { if (c_ptr->feat == FEAT_WALL_OUTER) { if (!connected && one_in_(wallcount--)) { /* Emergency - open passage into room */ cave_set_feat(x, y, dun->feat_floor); connected = TRUE; } else { /* Inner walls */ cave_set_feat(x, y, FEAT_WALL_INNER); } } } else { if (c_ptr->feat != FEAT_WALL_EXTRA) { /* Add room */ c_ptr->info |= CAVE_ROOM; if (light) c_ptr->info |= CAVE_GLOW; } } } } } /* * Type 25 -- Connected small rooms */ static void build_type25(int bx0, int by0) { int xval, yval; int y1, x1, y2, x2; bool light; int xi1, yi1, xi2, yi2; int xsize, ysize; int i; /* Pick a room size */ y1 = rand_range(5, 14); x1 = rand_range(5, 20); y2 = rand_range(5, 14); x2 = rand_range(5, 20); xsize = x1 + x2 + 1; ysize = y1 + y2 + 1; /* Try to allocate space for room. If fails, exit */ if (!room_alloc(xsize + 2, ysize + 2, FALSE, bx0, by0, &xval, &yval)) return; /* Choose lite or dark */ light = (p_ptr->depth <= randint1(25)); /* Get corner values */ y1 = yval - ysize / 2; x1 = xval - xsize / 2; y2 = yval + (ysize - 1) / 2; x2 = xval + (xsize - 1) / 2; /* Add "starting" room */ /* Generate new room */ generate_room(xval - 3, yval - 3, xval + 3, yval + 3, light); /* Generate outer walls */ generate_draw(xval - 3, yval - 3, xval + 3, yval + 3, FEAT_WALL_OUTER); /* Generate inner floors */ generate_fill(xval - 2, yval - 2, xval + 2, yval + 2, dun->feat_floor); /* * Add other rooms * * Note that the size of the rooms is forced * to be odd, and the walls are forced to be * on "even" x and y coordinates. This makes * them fit together better. */ for (i = 20; i > 0; i--) { /* Pick new room to add */ xi1 = rand_range(x1 - 1, x2 - 5); yi1 = rand_range(y1 - 1, y2 - 5); if (xi1 % 2) xi1++; if (yi1 % 2) yi1++; xi2 = xi1 + rand_range(5, 10); yi2 = yi1 + rand_range(5, 10); if (xi2 % 2) xi2++; if (yi2 % 2) yi2++; /* Out of the box */ if ((xi2 > x2) || (yi2 > y2)) continue; /* Draw the room, and connect it */ overdraw_room(xi1, yi1, xi2, yi2, light); } } #define ROOM_TYPES 30 typedef void (*room_build_type)(int, int); typedef struct room_type room_type; struct room_type { const int depth; /* Minimum depth */ const int chance; /* Relative probability (higher is more common) */ const room_build_type build_func; /* Function to build room */ const u16b flags; }; /* * There are some duplicate entries, which cause the more interesting rooms * to become more common at deeper depths. */ room_type room_list[ROOM_TYPES] = { {1, 30, build_type1, RT_SIMPLE}, /* Simple Rectangle */ {1, 10, build_type2, RT_FANCY}, /* Overlapping */ {1, 10, build_type20, RT_RUIN}, /* Collapsed */ {3, 10, build_type3, RT_FANCY}, /* Crossed */ {3, 10, build_type4, RT_BUILDING | RT_CRYPT}, /* Large nested */ {3, 10, build_type11, RT_NATURAL | RT_FANCY}, /* Circle */ {3, 10, build_type14, RT_COMPLEX}, /* Large with walls */ {3, 10, build_type15, RT_STRANGE}, /* Parallelogram */ {3, 10, build_type16, RT_RUIN | RT_NATURAL}, /* Rectangle minus inverse overlap */ {3, 10, build_type17, RT_RUIN}, /* Triangles */ {3, 10, build_type23, RT_FANCY}, /* Semicircle */ {3, 10, build_type24, RT_COMPLEX}, /* Hourglass */ {3, 10, build_type25, RT_BUILDING}, /* Connected rooms */ {5, 10, build_type9, RT_NATURAL}, /* Fractal cave */ {5, 10, build_type13, RT_NATURAL}, /* Large with fractal feature */ {5, 10, build_type18, RT_BUILDING}, /* Chambers */ {5, 10, build_type19, RT_STRANGE}, /* Channel */ {7, 10, build_type22, RT_COMPLEX}, /* Very large pillared chamber */ {10, 10, build_type5, RT_ANIMAL | RT_TAG_CROWDED}, /* Monster nest */ {10, 10, build_type12, RT_CRYPT}, /* Crypt I */ {10, 10, build_type21, RT_CRYPT}, /* Crypt II */ {12, 10, build_type7, RT_DENSE}, /* Small vault */ {12, 10, build_type10, RT_RVAULT}, /* Random vault */ {15, 10, build_type6, RT_DENSE | RT_TAG_CROWDED}, /* Monster pit */ {20, 10, build_type8, RT_DENSE}, /* Large vault */ {25, 10, build_type10, RT_RVAULT}, /* Random vault */ {30, 10, build_type5, RT_ANIMAL | RT_TAG_CROWDED}, /* Monster nest */ {35, 10, build_type7, RT_DENSE}, /* Small vault */ {40, 10, build_type6, RT_DENSE | RT_TAG_CROWDED}, /* Monster pit */ {45, 10, build_type8, RT_DENSE}, /* Large vault */ }; /* * Select a room type, and then attempt to build a it at the given block * * Note that we restrict the number of "crowded" rooms to reduce * the chance of overflowing the monster list during level creation. */ bool room_build(void) { int x, y; int type = 0; int i; int val, total; int depth; /* Choose base depth */ depth = p_ptr->depth; /* Occasionally give a chance for an "out-of-depth" room */ if (one_in_(10)) depth += randint1(5); if (one_in_(10)) depth += randint1(5); /* Collect the total possible chance */ total = 0; for (i = 0; i < ROOM_TYPES; i++) { if (depth < room_list[i].depth) continue; if (!(dun->room_types & room_list[i].flags)) continue; if ((dun->crowded >= 2) && (room_list[i].flags & RT_TAG_CROWDED)) continue; total += room_list[i].chance; } /* Find a room type for this dungeon */ val = randint0(total); for (i = 0; i < ROOM_TYPES; i++) { if (depth < room_list[i].depth) continue; if (!(dun->room_types & room_list[i].flags)) continue; if ((dun->crowded >= 2) && (room_list[i].flags & RT_TAG_CROWDED)) continue; val -= room_list[i].chance; if (val < 0) { type = i; break; } } /* Pick a block for the room */ x = randint0(dun->col_rooms); y = randint0(dun->row_rooms); /* Build a room at a random position */ room_list[type].build_func(x, y); return (TRUE); } zangband/src/run.c0000644000000000000000000004645610250356274013122 0ustar rootroot/* File: run.c */ /* Purpose: running code */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* * Check for a "known wall" */ static int see_wall(int x, int y) { pcave_type *pc_ptr; feature_type *f_ptr; /* Illegal grids are "walls" */ if (!in_boundsp(x, y)) return (TRUE); pc_ptr = parea(x, y); f_ptr = &f_info[pc_ptr->feat]; /* Return block-los status */ return (f_ptr->flags & FF_BLOCK); } /* * Check for an "unknown corner" */ static int see_nothing(int x, int y) { cave_type *c_ptr; pcave_type *pc_ptr; /* Illegal grids are unknown */ if (!in_boundsp(x, y)) return (FALSE); c_ptr = area(x, y); pc_ptr = parea(x, y); /* Memorized grids are always known */ if (pc_ptr->feat) return (FALSE); /* Non-floor grids are unknown */ if (cave_wall_grid(c_ptr)) return (TRUE); /* Viewable door/wall grids are known */ if (player_can_see_grid(pc_ptr)) return (FALSE); /* Default */ return (TRUE); } /* * Check for "interesting" terrain */ static int see_interesting(int x, int y) { cave_type *c_ptr; pcave_type *pc_ptr; object_type *o_ptr; /* Illegal grids are boring */ if (!in_boundsp(x, y)) return (FALSE); c_ptr = area(x, y); pc_ptr = parea(x, y); /* Check for a monster */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; /* If it's visible, it's interesting */ if (m_ptr->ml) return (TRUE); } /* Check for objects */ OBJ_ITT_START(c_ptr->o_idx, o_ptr) { /* If it's visible, it's interesting */ if (o_ptr->info & OB_SEEN) return (TRUE); } OBJ_ITT_END; /* Check for traps */ if (is_visible_trap(c_ptr)) return (TRUE); /* Check for building */ if (is_build(c_ptr)) return (TRUE); /* Check memorized grids */ if (pc_ptr->feat) { switch (c_ptr->feat) { case FEAT_DEEP_LAVA: case FEAT_DEEP_ACID: case FEAT_DEEP_SWAMP: return (TRUE); case FEAT_SHAL_SWAMP: case FEAT_SHAL_ACID: case FEAT_SHAL_LAVA: /* Water */ case FEAT_DEEP_WATER: case FEAT_OCEAN_WATER: /* Levitation makes these feats boring */ if (FLAG(p_ptr, TR_FEATHER)) break; return (TRUE); /* Open doors */ case FEAT_OPEN: case FEAT_BROKEN: if (find_ignore_doors) break; return (TRUE); /* Closed doors */ case FEAT_CLOSED: return (TRUE); /* Stairs */ case FEAT_LESS: case FEAT_MORE: if (find_ignore_stairs) break; return (TRUE); } } /* Boring */ return (FALSE); } #define RUN_MODE_START 0 /* Beginning of run */ #define RUN_MODE_OPEN 1 /* Running in a room or open area */ #define RUN_MODE_CORRIDOR 2 /* Running in a corridor */ #define RUN_MODE_WALL 3 /* Running along a wall */ #define RUN_MODE_FINISH 4 /* End of run */ /* * Due to a fortunate coincidence, there are exactly 32 interesting * 2-move combinations. We only consider combinations where the * second move shares at least one component (N, E, S or W) with * the first move. * * Since there are 32, we can use bit fields to simplify the tables. */ #define RUN_N_NW 0x00000001L #define RUN_N_N 0x00000002L #define RUN_N_NE 0x00000004L #define RUN_NE_NW 0x00000008L #define RUN_NE_N 0x00000010L #define RUN_NE_NE 0x00000020L #define RUN_NE_E 0x00000040L #define RUN_NE_SE 0x00000080L #define RUN_E_NE 0x00000100L #define RUN_E_E 0x00000200L #define RUN_E_SE 0x00000400L #define RUN_SE_NE 0x00000800L #define RUN_SE_E 0x00001000L #define RUN_SE_SE 0x00002000L #define RUN_SE_S 0x00004000L #define RUN_SE_SW 0x00008000L #define RUN_S_SE 0x00010000L #define RUN_S_S 0x00020000L #define RUN_S_SW 0x00040000L #define RUN_SW_SE 0x00080000L #define RUN_SW_S 0x00100000L #define RUN_SW_SW 0x00200000L #define RUN_SW_W 0x00400000L #define RUN_SW_NW 0x00800000L #define RUN_W_SW 0x01000000L #define RUN_W_W 0x02000000L #define RUN_W_NW 0x04000000L #define RUN_NW_SW 0x08000000L #define RUN_NW_W 0x10000000L #define RUN_NW_NW 0x20000000L #define RUN_NW_N 0x40000000L #define RUN_NW_NE 0x80000000L /* All the moves that start with each direction */ #define RUN_N (RUN_N_NW | RUN_N_N | RUN_N_NE) #define RUN_NE (RUN_NE_NW | RUN_NE_N | RUN_NE_NE | RUN_NE_E | RUN_NE_SE) #define RUN_E (RUN_E_NE | RUN_E_E | RUN_E_SE) #define RUN_SE (RUN_SE_NE | RUN_SE_E | RUN_SE_SE | RUN_SE_S | RUN_SE_SW) #define RUN_S (RUN_S_SE | RUN_S_S | RUN_S_SW) #define RUN_SW (RUN_SW_SE | RUN_SW_S | RUN_SW_SW | RUN_SW_W | RUN_SW_NW) #define RUN_W (RUN_W_SW | RUN_W_W | RUN_W_NW) #define RUN_NW (RUN_NW_SW | RUN_NW_W | RUN_NW_NW | RUN_NW_N | RUN_NW_NE) #define TEST_NONE 0 #define TEST_WALL 1 #define TEST_UNSEEN 2 #define TEST_FLOOR 3 #define TEST_FLOOR_S 4 /* * The tests. * * Some of these are hard to understand. Don't worry too * much, unless you really need to modify the table. * * The order the groups of checks are in is important! * Don't reorder them unless you're sure you know what * you're doing. The order within each group is arbitrary. * * Each test consists of: * * What type of square test to do (if any), and what * square to test as an offset from the player. * * What other moves must be valid (0 for none). If there * are multiple moves listed, the test passes if any of * the listed moves are valid. * * What moves to remove from consideration if both the * checks listed pass. */ static const struct { int test; int dx, dy; u32b test_mask; u32b remove_mask; } run_checks[] = { /* * If the player starts out by moving into a branch corridor: * * ### ### * b.@ -> b.. * #.# #@# * #a# #a# * * we should realize that the player means to continue to the square marked * 'a', and we should ignore the possibility of moving to 'b' instead. * * To recognize this, we check for the presence of a floor tile that the * player might have come from next to, and remove any moves that would * result in "doubling back". * * This test is done *before* removing impossible moves, because * we need to know which direction the player came from. */ {TEST_FLOOR_S, 0, -1, RUN_S, RUN_NE | RUN_NW}, {TEST_FLOOR_S, 1, 0, RUN_W, RUN_NE | RUN_SE}, {TEST_FLOOR_S, 0, 1, RUN_N, RUN_SE | RUN_SW}, {TEST_FLOOR_S, -1, 0, RUN_E, RUN_NW | RUN_SW}, /* Eliminate impossible moves */ {TEST_WALL, -2, -2, 0, RUN_NW_NW}, {TEST_WALL, -1, -2, 0, RUN_NW_N | RUN_N_NW}, {TEST_WALL, 0, -2, 0, RUN_NW_NE | RUN_N_N | RUN_NE_NW}, {TEST_WALL, 1, -2, 0, RUN_NE_N | RUN_N_NE}, {TEST_WALL, 2, -2, 0, RUN_NE_NE}, {TEST_WALL, 2, -1, 0, RUN_NE_E | RUN_E_NE}, {TEST_WALL, 2, 0, 0, RUN_NE_SE | RUN_E_E | RUN_SE_NE}, {TEST_WALL, 2, 1, 0, RUN_SE_E | RUN_E_SE}, {TEST_WALL, 2, 2, 0, RUN_SE_SE}, {TEST_WALL, 1, 2, 0, RUN_SE_S | RUN_S_SE}, {TEST_WALL, 0, 2, 0, RUN_SE_SW | RUN_S_S | RUN_SW_SE}, {TEST_WALL, -1, 2, 0, RUN_SW_S | RUN_S_SW}, {TEST_WALL, -2, 2, 0, RUN_SW_SW}, {TEST_WALL, -2, 1, 0, RUN_SW_W | RUN_W_SW}, {TEST_WALL, -2, 0, 0, RUN_SW_NW | RUN_W_W | RUN_NW_SW}, {TEST_WALL, -2, -1, 0, RUN_NW_W | RUN_W_NW}, {TEST_WALL, -1, -1, 0, RUN_NW}, {TEST_WALL, 0, -1, 0, RUN_N}, {TEST_WALL, 1, -1, 0, RUN_NE}, {TEST_WALL, 1, 0, 0, RUN_E}, {TEST_WALL, 1, 1, 0, RUN_SE}, {TEST_WALL, 0, 1, 0, RUN_S}, {TEST_WALL, -1, 1, 0, RUN_SW}, {TEST_WALL, -1, 0, 0, RUN_W}, /* * Allow the player to run in a pillared corridor with * a radius-2 light source, by removing some diagonal * moves into unknown squares. Example: * * #.# * #...# * ##@## * * Note that this, like the previous set, can result * in missing an actual (but unusual) branch. * Generally the player will see the branch once we * move another step, but by that point it's too * late, so we keep going forward. */ {TEST_UNSEEN, -2, -2, RUN_N | RUN_W, RUN_NW_NW}, {TEST_UNSEEN, 2, -2, RUN_N | RUN_E, RUN_NE_NE}, {TEST_UNSEEN, 2, 2, RUN_S | RUN_E, RUN_SE_SE}, {TEST_UNSEEN, -2, 2, RUN_S | RUN_W, RUN_SW_SW}, /* * Ensure that the player will take unknown corners by * preventing orthagonal moves to unknown squares when * a diagonal move exists. * * Example: * * ## * .@ * .# * * In this situation we remove all the 'west' moves, because * they are to unknown squares, and it's possible to move * southwest instead. * * Note that this can miss a branch if the unknown square * is actually a floor square and cutting corners is * enabled. * * We have to be careful that we differentiate between these * two situations: * * ### ## * .@ vs .@ * #.# .# * * The first we should NOT turn because there is obviously * a real branch. We handle this by (indirectly) checking * for the presence of a wall in the "unknown" direction, * in this case the wall SWW of the player. */ {TEST_UNSEEN, -1, -2, RUN_NE_N | RUN_NW_N, RUN_N_NW}, {TEST_UNSEEN, 0, -2, RUN_NE_N | RUN_NW_N, RUN_N_N}, {TEST_UNSEEN, 1, -2, RUN_NE_N | RUN_NW_N, RUN_N_NE}, {TEST_UNSEEN, 2, -1, RUN_NE_E | RUN_SE_E, RUN_E_NE}, {TEST_UNSEEN, 2, 0, RUN_NE_E | RUN_SE_E, RUN_E_E}, {TEST_UNSEEN, 2, 1, RUN_NE_E | RUN_SE_E, RUN_E_SE}, {TEST_UNSEEN, 1, 2, RUN_SE_S | RUN_SW_S, RUN_S_SE}, {TEST_UNSEEN, 0, 2, RUN_SE_S | RUN_SW_S, RUN_S_S}, {TEST_UNSEEN, -1, 2, RUN_SE_S | RUN_SW_S, RUN_S_SW}, {TEST_UNSEEN, -2, 1, RUN_NW_W | RUN_SW_W, RUN_W_SW}, {TEST_UNSEEN, -2, 0, RUN_NW_W | RUN_SW_W, RUN_W_W}, {TEST_UNSEEN, -2, -1, RUN_NW_W | RUN_SW_W, RUN_W_NW}, /* Prefer going straight over zig-zagging */ {TEST_NONE, 0, 0, RUN_N_N, RUN_NE_NW | RUN_NW_NE}, {TEST_NONE, 0, 0, RUN_E_E, RUN_NE_SE | RUN_SE_NE}, {TEST_NONE, 0, 0, RUN_S_S, RUN_SE_SW | RUN_SW_SE}, {TEST_NONE, 0, 0, RUN_W_W, RUN_NW_SW | RUN_SW_NW}, /* Prefer moving diagonal then orthagonal over the reverse */ {TEST_NONE, 0, 0, RUN_NW_W, RUN_W_NW}, {TEST_NONE, 0, 0, RUN_NW_N, RUN_N_NW}, {TEST_NONE, 0, 0, RUN_NE_N, RUN_N_NE}, {TEST_NONE, 0, 0, RUN_NE_E, RUN_E_NE}, {TEST_NONE, 0, 0, RUN_SE_E, RUN_E_SE}, {TEST_NONE, 0, 0, RUN_SE_S, RUN_S_SE}, {TEST_NONE, 0, 0, RUN_SW_S, RUN_S_SW}, {TEST_NONE, 0, 0, RUN_SW_W, RUN_W_SW}, }; static const u32b valid_dir_mask[10] = { /* 0 */ 0, /* 1 */ RUN_NW | RUN_W | RUN_SW | RUN_S | RUN_SE, /* 2 */ RUN_SW | RUN_S | RUN_SE, /* 3 */ RUN_SW | RUN_S | RUN_SE | RUN_E | RUN_NE, /* 4 */ RUN_NW | RUN_W | RUN_SW, /* 5 */ 0, /* 6 */ RUN_SE | RUN_E | RUN_NE, /* 7 */ RUN_NE | RUN_N | RUN_NW | RUN_W | RUN_SW, /* 8 */ RUN_NE | RUN_N | RUN_NW, /* 9 */ RUN_SE | RUN_E | RUN_NE | RUN_N | RUN_NW }; static const u32b basic_dir_mask[10] = { /* 0 */ 0, /* 1 */ RUN_SW, /* 2 */ RUN_S, /* 3 */ RUN_SE, /* 4 */ RUN_W, /* 5 */ 0, /* 6 */ RUN_E, /* 7 */ RUN_NW, /* 8 */ RUN_N, /* 9 */ RUN_NE }; /* * Lists of walls that, if all set, mean we're * probably in a corridor. * * Generally we decide we're in a corridor if * two opposite squares are walls, or one square * and the two opposite corners, or all four * corners. */ static const u32b corridor_test_mask[] = { RUN_N | RUN_S, RUN_E | RUN_W, RUN_N | RUN_SE | RUN_SW, RUN_E | RUN_NW | RUN_SW, RUN_S | RUN_NE | RUN_NW, RUN_W | RUN_SE | RUN_NE, RUN_NW | RUN_NE | RUN_SE | RUN_SW }; /* * Lists of walls that, if all set, mean we're probably running * alongside a wall. * * For horizontal or vertical walls this means that at least two walls * are present on one side along our path. * * ToDo: support diagonal walls */ static const u32b wall_test_mask[10][6] = { /* 0 */ {0, 0, 0, 0, 0, 0}, /* 1 */ {0, 0, 0, 0, 0, 0}, /* 2 */ { RUN_E | RUN_SE, RUN_E | RUN_NE, RUN_W | RUN_SW, RUN_W | RUN_NW, RUN_SE | RUN_NE, RUN_SW | RUN_NW, }, /* 3 */ {0, 0, 0, 0, 0, 0}, /* 4 */ { RUN_S | RUN_SW, RUN_S | RUN_SE, RUN_N | RUN_NW, RUN_N | RUN_NE, RUN_SW | RUN_SE, RUN_NW | RUN_NE, }, /* 5 */ {0, 0, 0, 0, 0, 0}, /* 6 */ { RUN_S | RUN_SW, RUN_S | RUN_SE, RUN_N | RUN_NW, RUN_N | RUN_NE, RUN_SW | RUN_SE, RUN_NW | RUN_NE, }, /* 7 */ {0, 0, 0, 0, 0, 0}, /* 8 */ { RUN_E | RUN_SE, RUN_E | RUN_NE, RUN_W | RUN_SW, RUN_W | RUN_NW, RUN_SE | RUN_NE, RUN_SW | RUN_NW, }, /* 9 */ {0, 0, 0, 0, 0, 0}, }; /* * Determine the run algorithm to use. * * If we seem to be in a corridor, use the "follow" algorithm. * otherwise, use the "open" algorithm. * * Note that we delay selecting the mode to use until after * the player has moved the first step of the run. * * In general determining which algorithm to use is complicated. */ static void run_choose_mode(void) { int px = p_ptr->px; int py = p_ptr->py; unsigned int i; u32b wall_dirs = 0; /* Check valid dirs */ for (i = 1; i < 10; i++) { if (see_wall(px + ddx[i], py + ddy[i])) wall_dirs |= basic_dir_mask[i]; } /* Check for evidence we're in a corridor */ for (i = 0; i < NUM_ELEMENTS(corridor_test_mask); i++) { /* * If none of the elements in the mask are _not_ * set, we're in a corridor. */ if (!(~wall_dirs & corridor_test_mask[i])) { p_ptr->run.mode = RUN_MODE_CORRIDOR; return; } } /* Check for evidence we're following a wall */ for (i = 0; i < NUM_ELEMENTS(wall_test_mask[p_ptr->run.cur_dir]); i++) { /* * If none of the elements in the mask are _not_ * set, we're following a wall. */ if (!(~wall_dirs & wall_test_mask[p_ptr->run.cur_dir][i])) { p_ptr->run.mode = RUN_MODE_WALL; return; } } /* Assume we're in the open */ p_ptr->run.mode = RUN_MODE_OPEN; } /* * Check if anything interesting is nearby */ static int check_interesting(void) { int px = p_ptr->px; int py = p_ptr->py; int i; for (i = 0; i < 8; i++) { /* Ignore the square we just moved off of */ if (ddd[i] == p_ptr->run.old_dir) continue; /* Subtract rather than add so the previous check works */ if (see_interesting(px - ddx_ddd[i], py - ddy_ddd[i])) return (TRUE); } return (FALSE); } /* * The corridor running algorithm. * * We start by determining all two-move combinations that the player could possibly * make, where the first move shares at least one component (N, E, S, or W) with * the move made last turn. * * Then we use various tests (given in the run_checks[] table above) to remove * some of the possibilities. * * If, after all the checks, all the two-move combinations left start with the * same move, we use that move. Otherwise, we're at a branch, so we stop. * * See run_checks[] for more detailed comments on some of the situations we * test for. */ static void run_corridor(int starting) { int px = p_ptr->px; int py = p_ptr->py; u32b valid_dirs = 0; unsigned int i; /* Check if we're next to something interesting. If we are, stop. */ if (check_interesting()) { p_ptr->run.mode = RUN_MODE_FINISH; return; } /* Add all possibly-legal dirs depending on previous direction */ valid_dirs = valid_dir_mask[p_ptr->run.old_dir]; /* Do magic */ for (i = 0; i < NUM_ELEMENTS(run_checks); i++) { if (run_checks[i].remove_mask & valid_dirs) { int dx = run_checks[i].dx; int dy = run_checks[i].dy; u32b test_mask = run_checks[i].test_mask; /* Check mask if present */ if (test_mask) { if (!(valid_dirs & test_mask)) continue; } /* Check square if present */ if (dx || dy) { bool ok = TRUE; switch (run_checks[i].test) { case TEST_NONE: break; case TEST_WALL: ok = see_wall(px + dx, py + dy); break; case TEST_UNSEEN: ok = see_nothing(px + dx, py + dy); break; case TEST_FLOOR: ok = !see_wall(px + dx, py + dy) && !see_nothing(px + dx, py + dy); break; case TEST_FLOOR_S: ok = starting && !see_wall(px + dx, py + dy) && !see_nothing(px + dx, py + dy); break; } if (!ok) continue; } valid_dirs &= ~run_checks[i].remove_mask; } } /* If there are no valid paths left, stop */ if (!valid_dirs) { p_ptr->run.mode = RUN_MODE_FINISH; return; } p_ptr->run.cur_dir = 0; /* Check if we can go a single direction */ if (!(valid_dirs & ~RUN_SW)) p_ptr->run.cur_dir = 1; else if (!(valid_dirs & ~RUN_S)) p_ptr->run.cur_dir = 2; else if (!(valid_dirs & ~RUN_SE)) p_ptr->run.cur_dir = 3; else if (!(valid_dirs & ~RUN_W)) p_ptr->run.cur_dir = 4; else if (!(valid_dirs & ~RUN_E)) p_ptr->run.cur_dir = 6; else if (!(valid_dirs & ~RUN_NW)) p_ptr->run.cur_dir = 7; else if (!(valid_dirs & ~RUN_N)) p_ptr->run.cur_dir = 8; else if (!(valid_dirs & ~RUN_NE)) p_ptr->run.cur_dir = 9; /* If we can go multiple directions, we're at a branch. Stop. */ if (!p_ptr->run.cur_dir) { p_ptr->run.mode = RUN_MODE_FINISH; return; } p_ptr->run.old_dir = p_ptr->run.cur_dir; /* XXX Don't-cut-corners code goes here */ } /* * Run in an open area * * XXX Write this */ static void run_open(void) { int px = p_ptr->px; int py = p_ptr->py; int dir = p_ptr->run.old_dir; int dx = ddx[dir]; int dy = ddy[dir]; if (check_interesting()) { p_ptr->run.mode = RUN_MODE_FINISH; return; } p_ptr->run.cur_dir = dir; if (see_wall(px + dx, py + dy)) { p_ptr->run.mode = RUN_MODE_FINISH; return; } } static const int run_wall_check[10][2][2] = { /* 0 */ {{0, 0}, {0, 0}}, /* 1 */ {{0, 0}, {0, 0}}, /* 2 */ {{4, 1}, {6, 3}}, /* 3 */ {{0, 0}, {0, 0}}, /* 4 */ {{2, 1}, {8, 7}}, /* 5 */ {{0, 0}, {0, 0}}, /* 6 */ {{2, 3}, {8, 9}}, /* 7 */ {{0, 0}, {0, 0}}, /* 8 */ {{4, 7}, {6, 9}}, /* 9 */ {{0, 0}, {0, 0}}, }; /* * Run alongside a wall * * XXX Write this */ static void run_wall(void) { unsigned int i; int px = p_ptr->px; int py = p_ptr->py; int dir = p_ptr->run.old_dir; int dx = ddx[dir]; int dy = ddy[dir]; if (check_interesting()) { p_ptr->run.mode = RUN_MODE_FINISH; return; } /* Check if we are coming up to an opening in the wall */ for (i = 0; i < NUM_ELEMENTS(run_wall_check[dir]); i++) { int wall_dir = run_wall_check[dir][i][0]; int floor_dir = run_wall_check[dir][i][1]; if ( see_wall(px + ddx[wall_dir ], py + ddy[wall_dir ]) && !see_wall(px + ddx[floor_dir], py + ddy[floor_dir])) { p_ptr->run.mode = RUN_MODE_FINISH; return; } } p_ptr->run.cur_dir = dir; if (see_wall(px + dx, py + dy)) { p_ptr->run.mode = RUN_MODE_FINISH; return; } } /* * Take one step along a run path */ void run_step(int dir) { if (dir) { /* Don't start by running into a wall! */ if (see_wall(p_ptr->px + ddx[dir], p_ptr->py + ddy[dir])) { /* Message */ msgf("You cannot run in that direction."); /* Disturb */ disturb(FALSE); /* Done */ return; } /* Start by moving this direction */ p_ptr->run.cur_dir = dir; p_ptr->run.old_dir = dir; /* In "start run" state */ p_ptr->run.mode = RUN_MODE_START; } else { int starting = FALSE; /* If we've just started our run, determine the algorithm now */ if (p_ptr->run.mode == RUN_MODE_START) { starting = TRUE; run_choose_mode(); } /* Use the selected algorithm */ switch (p_ptr->run.mode) { case RUN_MODE_OPEN: run_open(); break; case RUN_MODE_CORRIDOR: run_corridor(starting); break; case RUN_MODE_WALL: run_wall(); break; case RUN_MODE_FINISH: break; default: msgf("Error: Bad run mode %i.", p_ptr->run.mode); msgf("Please submit a bug report."); disturb(FALSE); return; } } /* Check for end of run */ if (p_ptr->run.mode == RUN_MODE_FINISH) { disturb(FALSE); return; } /* Decrease the run counter */ p_ptr->state.running--; /* Use energy */ p_ptr->state.energy_use = 100; /* Take a step */ move_player(p_ptr->run.cur_dir, FALSE); } zangband/src/save.c0000755000000000000000000010605010250356274013242 0ustar rootroot/* File: save.c */ /* Purpose: interact with savefiles */ #include "angband.h" #ifdef FUTURE_SAVEFILES /* * XXX XXX XXX Ignore this for now... * * The basic format of Angband 2.8.0 (and later) savefiles is simple. * * The savefile itself is a "header" (4 bytes) plus a series of "blocks", * plus, perhaps, some form of "footer" at the end. * * The "header" contains information about the "version" of the savefile. * Conveniently, pre-2.8.0 savefiles also use a 4 byte header, though the * interpretation of the "sf_extra" byte is very different. Unfortunately, * savefiles from Angband 2.5.X reverse the sf_major and sf_minor fields, * and must be handled specially, until we decide to start ignoring them. * * Each "block" is a "type" (2 bytes), plus a "size" (2 bytes), plus "data", * plus a "check" (2 bytes), plus a "stamp" (2 bytes). The format of the * "check" and "stamp" bytes is still being contemplated, but it would be * nice for one to be a simple byte-checksum, and the other to be a complex * additive checksum of some kind. Both should be zero if the block is empty. * * Standard types: * TYPE_BIRTH --> creation info * TYPE_OPTIONS --> option settings * TYPE_MESSAGES --> message recall * TYPE_PLAYER --> player information * TYPE_SPELLS --> spell information * TYPE_INVEN --> player inven/equip * TYPE_STORES --> store information * TYPE_RACES --> monster race data * TYPE_KINDS --> object kind data * TYPE_UNIQUES --> unique info * TYPE_ARTIFACTS --> artifact info * TYPE_QUESTS --> quest info * * Dungeon information: * TYPE_DUNGEON --> dungeon info * TYPE_FEATURES --> dungeon features * TYPE_OBJECTS --> dungeon objects * TYPE_MONSTERS --> dungeon monsters * * Question: * Should there be a single "block" for info about all the stores, or one * "block" for each store? Or one "block", which contains "sub-blocks" of * some kind? Should we dump every "sub-block", or just the "useful" ones? */ /* * XXX XXX XXX */ #define TYPE_OPTIONS 17362 /* * Hack -- current savefile */ static int data_fd = -1; /* * Hack -- current block type */ static u16b data_type; /* * Hack -- current block size */ static u16b data_size; /* * Hack -- pointer to the data buffer */ static byte *data_head; /* * Hack -- pointer into the data buffer */ static byte *data_next; /* * Hack -- write the current "block" to the savefile */ static errr wr_block(void) { errr err; byte fake[4]; /* Save the type and size */ fake[0] = (byte)(data_type); fake[1] = (byte)(data_type >> 8); fake[2] = (byte)(data_size); fake[3] = (byte)(data_size >> 8); /* Dump the head */ err = fd_write(data_fd, (char *)&fake, 4); /* Dump the actual data */ err = fd_write(data_fd, (char *)data_head, data_size); /* XXX XXX XXX */ fake[0] = 0; fake[1] = 0; fake[2] = 0; fake[3] = 0; /* Dump the tail */ err = fd_write(data_fd, (char *)&fake, 4); /* Hack -- reset */ data_next = data_head; /* Wipe the data block */ C_WIPE(data_head, 65535, byte); /* Success */ return (0); } /* * Hack -- add data to the current block */ static void put_byte(byte v) { *data_next++ = v; } /* * Hack -- add data to the current block */ static void put_char(char v) { put_byte((byte)(v)); } /* * Hack -- add data to the current block */ static void put_u16b(u16b v) { *data_next++ = (byte)(v); *data_next++ = (byte)(v >> 8); } /* * Hack -- add data to the current block */ static void put_s16b(s16b v) { put_u16b((u16b)(v)); } /* * Hack -- add data to the current block */ static void put_u32b(u32b v) { *data_next++ = (byte)(v); *data_next++ = (byte)(v >> 8); *data_next++ = (byte)(v >> 16); *data_next++ = (byte)(v >> 24); } /* * Hack -- add data to the current block */ static void put_s32b(s32b v) { put_u32b((u32b)(v)); } /* * Hack -- add data to the current block */ static void put_string(char *str) { while ((*data_next++ = *str++) != '\0') ; } /* * Write a savefile for Angband 2.8.0 */ static errr wr_savefile(void) { int i; u32b now; byte tmp8u; u16b tmp16u; errr err; byte fake[4]; /*** Hack -- extract some data ***/ /* Hack -- Acquire the current time */ now = time((time_t *) (NULL)); /* Note the operating system */ sf_xtra = 0L; /* Note when the file was saved */ sf_when = now; /* Note the number of saves */ sf_saves++; /*** Actually write the file ***/ /* Open the file XXX XXX XXX */ data_fd = -1; /* Dump the version */ fake[0] = (byte)(VER_MAJOR); fake[1] = (byte)(VER_MINOR); fake[2] = (byte)(VER_PATCH); fake[3] = (byte)(VER_EXTRA); /* Dump the data */ err = fd_write(data_fd, (char *)&fake, 4); /* Make array XXX XXX XXX */ C_MAKE(data_head, 65535, byte); /* Hack -- reset */ data_next = data_head; /* Dump the "options" */ put_options(); /* Set the type */ data_type = TYPE_OPTIONS; /* Set the "options" size */ data_size = (data_next - data_head); /* Write the block */ wr_block(); /* XXX XXX XXX */ /* Dump the "final" marker XXX XXX XXX */ /* Type zero, Size zero, Contents zero, etc */ /* XXX XXX XXX Check for errors */ /* Kill array XXX XXX XXX */ KILL(data_head); /* Success */ return (0); } /* * Hack -- read the next "block" from the savefile */ static errr rd_block(void) { errr err; byte fake[4]; /* Read the head data */ err = fd_read(data_fd, (char *)&fake, 4); /* Extract the type and size */ data_type = (fake[0] | ((u16b)fake[1] << 8)); data_size = (fake[2] | ((u16b)fake[3] << 8)); /* Wipe the data block */ C_WIPE(data_head, 65535, byte); /* Read the actual data */ err = fd_read(data_fd, (char *)data_head, data_size); /* Read the tail data */ err = fd_read(data_fd, (char *)&fake, 4); /* XXX XXX XXX Verify */ /* Hack -- reset */ data_next = data_head; /* Success */ return (0); } /* * Hack -- get data from the current block */ static void get_byte(byte *ip) { byte d1; d1 = (*data_next++); (*ip) = (d1); } /* * Hack -- get data from the current block */ static void get_char(char *ip) { get_byte((byte *)ip); } /* * Hack -- get data from the current block */ static void get_u16b(u16b *ip) { u16b d0, d1; d0 = (*data_next++); d1 = (*data_next++); (*ip) = (d0 | (d1 << 8)); } /* * Hack -- get data from the current block */ static void get_s16b(s16b *ip) { get_u16b((u16b *)ip); } /* * Hack -- get data from the current block */ static void get_u32b(u32b *ip) { u32b d0, d1, d2, d3; d0 = (*data_next++); d1 = (*data_next++); d2 = (*data_next++); d3 = (*data_next++); (*ip) = (d0 | (d1 << 8) | (d2 << 16) | (d3 << 24)); } /* * Hack -- get data from the current block */ static void get_s32b(s32b *ip) { get_u32b((u32b *)ip); } /* * Read a savefile for Angband 2.8.0 */ static errr rd_savefile(void) { bool done = FALSE; byte fake[4]; /* Open the savefile */ data_fd = fd_open(savefile, O_RDONLY); /* No file */ if (data_fd < 0) return (1); /* Strip the first four bytes (see below) */ if (fd_read(data_fd, (char *)(fake), 4)) return (1); /* Make array XXX XXX XXX */ C_MAKE(data_head, 65535, byte); /* Hack -- reset */ data_next = data_head; /* Read blocks */ while (!done) { /* Read the block */ if (rd_block()) break; /* Analyze the type */ switch (data_type) { /* Done XXX XXX XXX */ case 0: { done = TRUE; break; } /* Grab the options */ case TYPE_OPTIONS: { if (get_options()) err = -1; break; } } /* XXX XXX XXX verify "data_next" */ if (data_next - data_head > data_size) break; } /* XXX XXX XXX Check for errors */ /* Kill array XXX XXX XXX */ KILL(data_head); /* Success */ return (0); } #endif /* FUTURE_SAVEFILES */ /* * Some "local" parameters, used to help write savefiles */ static FILE *fff; /* Current save "file" */ static byte xor_byte; /* Simple encryption */ static u32b v_stamp = 0L; /* A simple "checksum" on the actual values */ static u32b x_stamp = 0L; /* A simple "checksum" on the encoded bytes */ /* * These functions place information into a savefile a byte at a time */ static void sf_put(byte v) { /* Encode the value, write a character */ xor_byte ^= v; (void)putc((int)xor_byte, fff); /* Maintain the checksum info */ v_stamp += v; x_stamp += xor_byte; } static void wr_byte(byte v) { sf_put(v); } static void wr_u16b(u16b v) { sf_put((byte)(v & 0xFF)); sf_put((byte)((v >> 8) & 0xFF)); } static void wr_s16b(s16b v) { wr_u16b((u16b)v); } static void wr_u32b(u32b v) { sf_put((byte)(v & 0xFF)); sf_put((byte)((v >> 8) & 0xFF)); sf_put((byte)((v >> 16) & 0xFF)); sf_put((byte)((v >> 24) & 0xFF)); } static void wr_s32b(s32b v) { wr_u32b((u32b)v); } static void wr_string(cptr str) { while (*str) { wr_byte(*str); str++; } wr_byte(*str); } /* * These functions write info in larger logical records */ /* * Write an "item" record */ static void wr_item(const object_type *o_ptr) { byte i; wr_s16b(o_ptr->k_idx); /* Location */ wr_s16b(o_ptr->iy); wr_s16b(o_ptr->ix); wr_byte(o_ptr->tval); wr_byte(o_ptr->sval); wr_s16b(o_ptr->pval); wr_byte(o_ptr->discount); wr_byte(o_ptr->number); wr_s16b(o_ptr->weight); wr_s16b(o_ptr->timeout); wr_s16b(o_ptr->to_h); wr_s16b(o_ptr->to_d); wr_s16b(o_ptr->to_a); wr_s16b(o_ptr->ac); wr_byte(o_ptr->dd); wr_byte(o_ptr->ds); wr_byte(o_ptr->info); wr_byte(NUM_TR_SETS); for (i = 0; i < NUM_TR_SETS; i++) wr_u32b(o_ptr->flags[i]); /* Next object in list */ wr_s16b(o_ptr->next_o_idx); /* Remove this soon... */ wr_byte(o_ptr->allocated); /* Feelings */ wr_byte(o_ptr->feeling); /* Save the inscription (if any) */ if (o_ptr->inscription) { wr_string(quark_str(o_ptr->inscription)); } else { wr_string(""); } /* If it is a named item, save the name */ if (o_ptr->xtra_name) { wr_string(quark_str(o_ptr->xtra_name)); } else { wr_string(""); } /* Save attached scripts */ for (i = 0; i < MAX_TRIGGER; i++) { if (o_ptr->trigger[i]) { wr_byte(i); wr_string(quark_str(o_ptr->trigger[i])); } } wr_byte(255); /* No Python object */ wr_s32b(0); /* The new flags */ wr_s32b(o_ptr->cost); wr_byte(o_ptr->a_idx); for (i = 0; i < NUM_TR_SETS; i++) wr_u32b(o_ptr->kn_flags[i]); } /* * Write a "monster" record */ static void wr_monster(const monster_type *m_ptr) { wr_s16b(m_ptr->r_idx); wr_s16b(m_ptr->fy); wr_s16b(m_ptr->fx); wr_s16b(m_ptr->hp); wr_s16b(m_ptr->maxhp); wr_s16b(m_ptr->csleep); wr_byte(m_ptr->mspeed); wr_byte(m_ptr->energy); wr_byte(m_ptr->stunned); wr_byte(m_ptr->confused); wr_byte(m_ptr->monfear); wr_byte(m_ptr->invulner); wr_u32b(m_ptr->smart); wr_s16b(m_ptr->hold_o_idx); } /* * Write a "field" record */ static void wr_field(const field_type *f_ptr) { int i; wr_s16b(f_ptr->t_idx); /* Location */ wr_s16b(f_ptr->fy); wr_s16b(f_ptr->fx); /* Info flags */ wr_u16b(f_ptr->info); /* Counter */ wr_s16b(f_ptr->counter); /* Data */ for (i = 0; i < 8; i++) { wr_byte(f_ptr->data[i]); } /* No Python object */ wr_s32b(0); } /* * Write a "lore" record */ static void wr_lore(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* Count sights/deaths/kills */ wr_s16b(r_ptr->r_sights); wr_s16b(r_ptr->r_deaths); wr_s16b(r_ptr->r_pkills); wr_s16b(r_ptr->r_tkills); /* Count wakes and ignores */ wr_byte(r_ptr->r_wake); wr_byte(r_ptr->r_ignore); /* Extra stuff */ wr_byte(r_ptr->r_xtra1); wr_byte(r_ptr->r_xtra2); /* Count drops */ wr_byte(r_ptr->r_drop_gold); wr_byte(r_ptr->r_drop_item); /* Count spells */ wr_byte(r_ptr->r_cast_inate); wr_byte(r_ptr->r_cast_spell); /* Count blows of each type */ wr_byte(r_ptr->r_blows[0]); wr_byte(r_ptr->r_blows[1]); wr_byte(r_ptr->r_blows[2]); wr_byte(r_ptr->r_blows[3]); /* Memorize flags */ wr_u32b(r_ptr->r_flags[0]); wr_u32b(r_ptr->r_flags[1]); wr_u32b(r_ptr->r_flags[2]); wr_u32b(r_ptr->r_flags[3]); wr_u32b(r_ptr->r_flags[4]); wr_u32b(r_ptr->r_flags[5]); wr_u32b(r_ptr->r_flags[6]); /* Monster limit per level */ wr_byte(r_ptr->max_num); /* Later (?) */ wr_byte(0); wr_byte(0); wr_byte(0); } /* * Write an "xtra" record */ static void wr_xtra(int k_idx) { byte tmp8u = 0; object_kind *k_ptr = &k_info[k_idx]; if (k_ptr->aware) tmp8u |= 0x01; if (k_ptr->tried) tmp8u |= 0x02; wr_byte(tmp8u); } /* * Write a "store" record */ static void wr_store(const store_type *st_ptr) { /* Save the "data" */ wr_s16b(st_ptr->data); /* Save the current owner */ if (quark_str(st_ptr->owner_name)) { wr_string(quark_str(st_ptr->owner_name)); } else { wr_string(""); } wr_s16b(st_ptr->max_cost); wr_byte(st_ptr->greed); /* Hack - Save whether or not we have stock */ wr_byte((byte) (st_ptr->stock ? TRUE : FALSE)); /* Position in the town */ wr_u16b(st_ptr->x); wr_u16b(st_ptr->y); /* Type of store */ wr_byte(st_ptr->type); wr_s32b(st_ptr->last_visit); /* Pointer to stock list */ wr_s16b(st_ptr->stock); } /* * Write RNG state */ static void wr_randomizer(void) { int i; /* Zero */ wr_u16b(0); /* Place */ wr_u16b(Rand_place); /* State */ for (i = 0; i < RAND_DEG; i++) { wr_u32b(Rand_state[i]); } } /* * Write the "options" */ static void wr_options(void) { int i, n; u16b c; u32b flag = 0; /*** Oops ***/ /* Oops */ for (i = 0; i < 4; i++) wr_u32b(0L); /*** Special Options ***/ /* Write "delay_factor" */ wr_byte(delay_factor); /* Write "hitpoint_warn" */ wr_byte(hitpoint_warn); /*** Cheating options ***/ c = 0; if (p_ptr->state.wizard) c |= 0x0002; if (cheat_peek) c |= 0x0100; if (cheat_hear) c |= 0x0200; if (cheat_room) c |= 0x0400; if (cheat_xtra) c |= 0x0800; if (cheat_know) c |= 0x1000; if (cheat_live) c |= 0x2000; wr_u16b(c); /* Autosave info */ wr_byte(autosave_l); wr_byte(autosave_t); wr_s16b(autosave_freq); /*** Normal options ***/ /* Analyze the options */ for (n = 0; n < 8; n++) { /* Analyze the options */ for (i = 0; i < 32; i++) { if (option_info[n * 32 + i].o_val) { /* Set flag */ flag |= (1L << i); } else { /* Clear flag */ flag &= ~(1L << i); } } /* Dump the flag */ wr_u32b(flag); } /* Oops */ for (i = 0; i < 8; i++) wr_u32b(option_mask[i]); /*** Window options ***/ /* Dump the flags */ for (i = 0; i < ANGBAND_TERM_MAX; i++) wr_u32b(window_flag[i]); /* Dump the masks */ for (i = 0; i < ANGBAND_TERM_MAX; i++) wr_u32b(window_mask[i]); } /* * Hack -- Write the "ghost" info */ static void wr_ghost(void) { int i; /* Name */ wr_string("Broken Ghost"); /* Hack -- stupid data */ for (i = 0; i < 60; i++) wr_byte(0); } /* * Write some "extra" info */ static void wr_extra(void) { int i; wr_string(player_name); wr_string(p_ptr->state.died_from); /* Old history-dumping code */ for (i = 0; i < 4; i++) { wr_string(""); } /* Race/Class/Gender/Spells */ wr_byte(p_ptr->rp.prace); wr_byte(p_ptr->rp.pclass); wr_byte(p_ptr->rp.psex); wr_byte(p_ptr->spell.r[0].realm); wr_byte(p_ptr->spell.r[1].realm); wr_byte(0); /* oops */ wr_byte(p_ptr->rp.hitdie); wr_u16b(p_ptr->expfact); wr_s16b(p_ptr->rp.age); wr_s16b(p_ptr->rp.ht); wr_s16b(p_ptr->rp.wt); /* Dump the stats (maximum and current) */ for (i = 0; i < A_MAX; ++i) wr_s16b(p_ptr->stat[i].max); for (i = 0; i < A_MAX; ++i) wr_s16b(p_ptr->stat[i].cur); /* Ignore the transient stats */ for (i = 0; i < 12; ++i) wr_s16b(0); wr_u32b(p_ptr->au); wr_u32b(p_ptr->max_exp); wr_u32b(p_ptr->exp); wr_u16b(p_ptr->exp_frac); wr_s16b(p_ptr->lev); wr_s16b(p_ptr->place_num); /* Write arena and rewards information -KMW- */ wr_s16b(0); wr_s16b(0); wr_s16b(0); wr_byte(0); wr_byte(0); /* oldpy and px are not required any more. */ wr_s16b(0); wr_s16b(0); /* Save building rewards - not used. */ wr_s16b(0); wr_s16b(p_ptr->mhp); wr_s16b(p_ptr->chp); wr_u16b(p_ptr->chp_frac); wr_s16b(p_ptr->msp); wr_s16b(p_ptr->csp); wr_u16b(p_ptr->csp_frac); /* Max Player and Dungeon Levels */ wr_s16b(p_ptr->max_lev); wr_s16b(0); /* oops */ /* More info */ wr_s16b(0); /* oops */ wr_s16b(0); /* oops */ wr_s16b(0); /* oops */ wr_s16b(0); /* oops */ wr_s16b(p_ptr->rp.sc); wr_s16b(0); /* oops */ wr_s16b(0); /* old "rest" */ wr_s16b(p_ptr->tim.blind); wr_s16b(p_ptr->tim.paralyzed); wr_s16b(p_ptr->tim.confused); wr_s16b(p_ptr->food); wr_s16b(0); /* old "food_digested" */ wr_s16b(0); /* old "protection" */ wr_s16b(p_ptr->energy); wr_s16b(p_ptr->tim.fast); wr_s16b(p_ptr->tim.slow); wr_s16b(p_ptr->tim.afraid); wr_s16b(p_ptr->tim.cut); wr_s16b(p_ptr->tim.stun); wr_s16b(p_ptr->tim.poisoned); wr_s16b(p_ptr->tim.image); wr_s16b(p_ptr->tim.protevil); wr_s16b(p_ptr->tim.invuln); wr_s16b(p_ptr->tim.hero); wr_s16b(p_ptr->tim.shero); wr_s16b(p_ptr->tim.shield); wr_s16b(p_ptr->tim.blessed); wr_s16b(p_ptr->tim.invis); wr_s16b(p_ptr->tim.word_recall); wr_s16b(p_ptr->see_infra); wr_s16b(p_ptr->tim.infra); wr_s16b(p_ptr->tim.oppose_fire); wr_s16b(p_ptr->tim.oppose_cold); wr_s16b(p_ptr->tim.oppose_acid); wr_s16b(p_ptr->tim.oppose_elec); wr_s16b(p_ptr->tim.oppose_pois); wr_s16b(p_ptr->tim.esp); wr_s16b(p_ptr->tim.wraith_form); wr_s16b(p_ptr->tim.resist_magic); wr_s16b(p_ptr->chaos_patron); wr_u32b(p_ptr->muta1); wr_u32b(p_ptr->muta2); wr_u32b(p_ptr->muta3); for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { wr_s16b(p_ptr->virtues[i]); } for (i = 0; i < MAX_PLAYER_VIRTUES; i++) { wr_s16b(p_ptr->vir_types[i]); } wr_byte(p_ptr->state.confusing); wr_byte(0); /* oops */ wr_byte(0); /* oops */ wr_byte(0); /* oops */ wr_byte(p_ptr->state.searching); wr_byte(0); /* oops */ wr_byte(0); /* oops */ wr_byte(0); /* oops */ /* Future use */ for (i = 0; i < 12; i++) wr_u32b(0L); /* Ignore some flags */ wr_u32b(0L); /* oops */ wr_u32b(0L); /* oops */ wr_u32b(0L); /* oops */ /* Write the "object seeds" */ wr_u32b(seed_flavor); /* Special stuff */ wr_u16b(p_ptr->state.panic_save); wr_u16b(p_ptr->state.total_winner); wr_u16b(p_ptr->state.noscore); /* Write death */ wr_byte(p_ptr->state.is_dead); /* Write "feeling" */ wr_byte(p_ptr->state.feeling); /* Turn of last "feeling" */ wr_s32b(old_turn); /* Current turn */ wr_s32b(turn); /* Trap detection status */ wr_byte(p_ptr->state.detected); /* Player inventory item */ wr_s16b(p_ptr->inventory); } /* * Save the dungeon or wilderness */ static void save_map(int xmin, int ymin, int xmax, int ymax) { int y, x; byte tmp8u; byte count; byte prev_char; cave_type *c_ptr; pcave_type *pc_ptr; /*** Simple "Run-Length-Encoding" of cave ***/ /* Note that this will induce two wasted bytes */ count = 0; prev_char = 0; /* Dump the cave */ for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { /* Get the cave */ c_ptr = area(x, y); /* Extract a byte */ tmp8u = c_ptr->info; /* If the run is broken, or too full, flush it */ if ((tmp8u != prev_char) || (count == MAX_UCHAR)) { wr_byte((byte)count); wr_byte((byte)prev_char); prev_char = tmp8u; count = 1; } /* Continue the run */ else { count++; } } } /* Flush the data (if any) */ if (count) { wr_byte((byte)count); wr_byte((byte)prev_char); } /* Note that this will induce two wasted bytes */ count = 0; prev_char = 0; /* Dump the cave */ for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { /* Get the cave */ pc_ptr = parea(x, y); /* Extract a byte */ tmp8u = pc_ptr->player; /* If the run is broken, or too full, flush it */ if ((tmp8u != prev_char) || (count == MAX_UCHAR)) { wr_byte((byte)count); wr_byte((byte)prev_char); prev_char = tmp8u; count = 1; } /* Continue the run */ else { count++; } } } /* Flush the data (if any) */ if (count) { wr_byte((byte)count); wr_byte((byte)prev_char); } /* Note that this will induce two wasted bytes */ count = 0; prev_char = 0; /* Dump the cave */ for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { /* Get the cave */ pc_ptr = parea(x, y); /* Extract a byte */ tmp8u = pc_ptr->feat; /* If the run is broken, or too full, flush it */ if ((tmp8u != prev_char) || (count == MAX_UCHAR)) { wr_byte((byte)count); wr_byte((byte)prev_char); prev_char = tmp8u; count = 1; } /* Continue the run */ else { count++; } } } /* Flush the data (if any) */ if (count) { wr_byte((byte)count); wr_byte((byte)prev_char); } /*** Simple "Run-Length-Encoding" of cave ***/ /* Note that this will induce two wasted bytes */ count = 0; prev_char = 0; /* Dump the cave */ for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { /* Get the cave */ c_ptr = area(x, y); /* Extract a byte */ tmp8u = c_ptr->feat; /* If the run is broken, or too full, flush it */ if ((tmp8u != prev_char) || (count == MAX_UCHAR)) { wr_byte((byte)count); wr_byte((byte)prev_char); prev_char = tmp8u; count = 1; } /* Continue the run */ else { count++; } } } /* Flush the data (if any) */ if (count) { wr_byte((byte)count); wr_byte((byte)prev_char); } } /* * Save wilderness data */ static void save_wild_data(void) { int i, j; /* Save wilderness seed */ wr_u32b(wild_seed); /* Save wilderness map */ for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { /* Terrain */ wr_u16b(wild[j][i].done.wild); /* Places */ wr_byte(wild[j][i].done.place); /* Info flag */ wr_byte(wild[j][i].done.info); /* Monster Gen type */ wr_byte(wild[j][i].done.mon_gen); /* Monster Probability */ wr_byte(wild[j][i].done.mon_prob); } } } /* * Write the current dungeon */ static void wr_dungeon(void) { int i; /*** Basic info ***/ /* Dungeon specific info follows */ wr_u16b(p_ptr->depth); wr_u16b((u16b) base_level()); wr_u16b(num_repro); wr_u16b(p_ptr->py); wr_u16b(p_ptr->px); wr_u16b(p_ptr->max_hgt); wr_u16b(p_ptr->max_wid); /* Old panel stuff */ wr_u16b(0); wr_u16b(0); /* Panel bounds */ wr_s16b(p_ptr->panel_x1); wr_s16b(p_ptr->panel_y1); wr_s16b(p_ptr->panel_x2); wr_s16b(p_ptr->panel_y2); /* Save wilderness data */ save_wild_data(); /* Save map */ save_map(p_ptr->min_wid, p_ptr->min_hgt, p_ptr->max_wid, p_ptr->max_hgt); /* Compact the monsters */ compact_monsters(0); /* Compact the fields */ compact_fields(0); /*** Dump objects ***/ /* Total objects */ wr_u16b(o_max); /* Dump the objects */ for (i = 1; i < o_max; i++) { object_type *o_ptr = &o_list[i]; /* Dump it */ wr_item(o_ptr); } /*** Dump the monsters ***/ /* Total monsters */ wr_u16b(m_max); /* Dump the monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Dump it */ wr_monster(m_ptr); } /*** Dump the fields ***/ /* Total fields */ wr_u16b(fld_max); /* Dump the fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr = &fld_list[i]; /* Dump it */ wr_field(f_ptr); } } /* * Actually write a save-file */ static bool wr_savefile_new(void) { int i, j; u32b now; byte tmp8u; u16b tmp16u; /* Guess at the current time */ now = time((time_t *) 0); /* Note the operating system */ sf_xtra = 0L; /* Note when the file was saved */ sf_when = now; /* Note the number of saves */ sf_saves++; /*** Actually write the file ***/ /* Dump the file header */ xor_byte = 0; wr_byte(VER_MAJOR); xor_byte = 0; wr_byte(VER_MINOR); xor_byte = 0; wr_byte(VER_PATCH); xor_byte = 0; tmp8u = (byte)randint0(256); wr_byte(tmp8u); /* Reset the checksum */ v_stamp = 0L; x_stamp = 0L; /* Write the savefile version */ wr_u32b(SAVEFILE_VERSION); /* Operating system */ wr_u32b(sf_xtra); /* Time file last saved */ wr_u32b(sf_when); /* Number of past lives */ wr_u16b(sf_lives); /* Number of times saved */ wr_u16b(sf_saves); /* Space */ wr_u32b(0L); wr_u32b(0L); /* Write the RNG state */ wr_randomizer(); /* Write the boolean "options" */ wr_options(); /* Dump the number of "messages" */ tmp16u = message_num(); if (compress_savefile && (tmp16u > 40)) tmp16u = 40; wr_u16b(tmp16u); /* Dump the messages and colors (oldest first!) */ for (i = tmp16u - 1; i >= 0; i--) { wr_string(message_str((s16b)i)); wr_byte((byte) message_type((s16b)i)); } /* Dump the monster lore */ tmp16u = z_info->r_max; wr_u16b(tmp16u); for (i = 0; i < tmp16u; i++) wr_lore(i); /* Dump the object memory */ tmp16u = z_info->k_max; wr_u16b(tmp16u); for (i = 0; i < tmp16u; i++) wr_xtra(i); /* Dump the towns */ tmp16u = z_info->wp_max; wr_u16b(tmp16u); /* Dump the quests */ wr_s16b(q_max); for (i = 1; i < q_max; i++) { wr_byte(quest[i].status); wr_byte(quest[i].flags); wr_byte(quest[i].type); wr_byte(quest[i].item); wr_u16b(quest[i].place); wr_u16b(quest[i].shop); wr_u16b(quest[i].reward); wr_byte(quest[i].c_type); wr_byte(quest[i].x_type); wr_u32b(quest[i].timeout); wr_string(quest[i].name); /* Data - quest-type specific */ switch (quest[i].type) { case QUEST_TYPE_NONE: break; case QUEST_TYPE_BOUNTY: { /* Bounty quests */ wr_u16b(quest[i].data.bnt.r_idx); wr_u16b(quest[i].data.bnt.cur_num); wr_u16b(quest[i].data.bnt.max_num); break; } case QUEST_TYPE_DUNGEON: { /* Dungeon quests */ wr_u16b(quest[i].data.dun.r_idx); wr_u16b(quest[i].data.dun.level); wr_s16b(quest[i].data.dun.cur_num); wr_s16b(quest[i].data.dun.max_num); wr_s16b(quest[i].data.dun.num_mon); break; } case QUEST_TYPE_WILD: { /* Wilderness quests */ wr_u16b(quest[i].data.wld.place); wr_u16b(quest[i].data.wld.data); wr_byte(quest[i].data.wld.depth); break; } case QUEST_TYPE_MESSAGE: { /* Message quests */ wr_u16b(quest[i].data.msg.place); wr_u16b(quest[i].data.msg.shop); break; } case QUEST_TYPE_FIND_ITEM: { /* Find item quests */ wr_u16b(quest[i].data.fit.a_idx); wr_u16b(quest[i].data.fit.place); break; } case QUEST_TYPE_FIND_PLACE: { /* Find place quests */ wr_u16b(quest[i].data.fpl.place); break; } default: { /* Unknown quest type... panic */ quit("Cannot save unknown quest type."); } } } /* Dump the position in the wilderness */ wr_s32b(p_ptr->wilderness_x); wr_s32b(p_ptr->wilderness_y); wr_s32b((s32b)max_wild); wr_s32b((s32b)max_wild); /* Hack -- Dump the artifacts */ tmp16u = z_info->a_max; wr_u16b(tmp16u); for (i = 0; i < tmp16u; i++) { artifact_type *a_ptr = &a_info[i]; wr_byte(a_ptr->cur_num); wr_byte(0); wr_byte(0); wr_byte(0); } /* Write the "extra" information */ wr_extra(); /* Dump the "player hp" entries */ tmp16u = PY_MAX_LEVEL; wr_u16b(tmp16u); for (i = 0; i < tmp16u; i++) { wr_s16b(p_ptr->player_hp[i]); } /* Write spell data */ wr_u32b(p_ptr->spell.r[0].learned); wr_u32b(p_ptr->spell.r[1].learned); wr_u32b(p_ptr->spell.r[0].worked); wr_u32b(p_ptr->spell.r[1].worked); wr_u32b(p_ptr->spell.r[0].forgotten); wr_u32b(p_ptr->spell.r[1].forgotten); /* Dump the ordered spells */ for (i = 0; i < PY_MAX_SPELLS; i++) { wr_byte(p_ptr->spell.order[i]); } /* Write the equipment */ for (i = 0; i < EQUIP_MAX; i++) { object_type *o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Dump index */ wr_u16b((u16b)i); /* Dump object */ wr_item(o_ptr); } /* Add a sentinel */ wr_u16b(0xFFFF); /* Note the towns */ tmp16u = place_count; wr_u16b(tmp16u); /* Dump the town data */ for (i = 1; i < place_count; i++) { place_type *pl_ptr = &place[i]; /* RNG seed */ wr_u32b(pl_ptr->seed); /* Number of stores */ wr_byte(pl_ptr->numstores); /* Type */ wr_u16b(pl_ptr->type); wr_byte(pl_ptr->data); /* Gates */ wr_byte(pl_ptr->gates_x[0]); wr_byte(pl_ptr->gates_x[1]); wr_byte(pl_ptr->gates_x[2]); wr_byte(pl_ptr->gates_x[3]); wr_byte(pl_ptr->gates_y[0]); wr_byte(pl_ptr->gates_y[1]); wr_byte(pl_ptr->gates_y[2]); wr_byte(pl_ptr->gates_y[3]); /* Location */ wr_byte(pl_ptr->x); wr_byte(pl_ptr->y); /* Size */ wr_byte(pl_ptr->xsize); wr_byte(pl_ptr->ysize); wr_u16b(pl_ptr->quest_num); wr_byte(pl_ptr->monst_type); /* Name */ wr_string(pl_ptr->name); if (pl_ptr->dungeon) { dun_type *dun_ptr; /* Is dungeon here */ wr_byte(TRUE); dun_ptr = pl_ptr->dungeon; /* Object theme */ wr_byte(dun_ptr->theme.treasure); wr_byte(dun_ptr->theme.combat); wr_byte(dun_ptr->theme.magic); wr_byte(dun_ptr->theme.tools); /* Habitat */ wr_u32b(dun_ptr->habitat); /* Levels in dungeon */ wr_byte(dun_ptr->min_level); wr_byte(dun_ptr->max_level); /* Rating + feeling */ wr_s16b(dun_ptr->rating); /* Extra info */ wr_u16b(dun_ptr->rooms); wr_byte(dun_ptr->floor); wr_byte(dun_ptr->liquid); wr_byte(dun_ptr->flags); /* Recall depth */ wr_byte(dun_ptr->recall_depth); } else { /* No dungeon here */ wr_byte(FALSE); } /* Dump the stores of all towns */ for (j = 0; j < pl_ptr->numstores; j++) { wr_store(&pl_ptr->store[j]); } } /* Write the pet command settings */ wr_s16b(p_ptr->pet_follow_distance); wr_byte(p_ptr->pet_open_doors); wr_byte(p_ptr->pet_pickup_items); /* Player is not dead, write the dungeon */ if (!p_ptr->state.is_dead) { /* Dump the dungeon */ wr_dungeon(); /* Dump the ghost */ wr_ghost(); /* No scripts */ wr_s32b(0); } /* Write the "value check-sum" */ wr_u32b(v_stamp); /* Write the "encoded checksum" */ wr_u32b(x_stamp); /* Error in save */ if (ferror(fff) || (fflush(fff) == EOF)) return FALSE; /* Successful save */ return TRUE; } /* * Medium level player saver * * XXX XXX XXX Angband 2.8.0 will use "fd" instead of "fff" if possible */ static bool save_player_aux(char *name) { bool ok = FALSE; int fd; int mode = 0644; /* No file yet */ fff = NULL; /* File type is "SAVE" */ FILE_TYPE(FILE_TYPE_SAVE); /* Grab permissions */ safe_setuid_grab(); /* Create the savefile */ fd = fd_make(name, mode); /* Drop permissions */ safe_setuid_drop(); /* File is okay */ if (fd >= 0) { /* Close the "fd" */ (void)fd_close(fd); /* Grab permissions */ safe_setuid_grab(); /* Open the savefile */ fff = my_fopen(name, "wb"); /* Drop permissions */ safe_setuid_drop(); /* Successful open */ if (fff) { /* Write the savefile */ if (wr_savefile_new()) ok = TRUE; /* Attempt to close it */ my_fclose(fff); } /* Grab permissions */ safe_setuid_grab(); /* Remove "broken" files */ if (!ok) (void)fd_kill(name); /* Drop permissions */ safe_setuid_drop(); } /* Failure */ if (!ok) return (FALSE); /* Successful save */ character_saved = TRUE; /* Success */ return (TRUE); } /* * Attempt to save the player in a savefile */ bool save_player(void) { int result = FALSE; char safe[1024]; /* New savefile */ strnfmt(safe, 1024, "%s.new", savefile); #ifdef VM /* Hack -- support "flat directory" usage on VM/ESA */ strnfmt(safe, 1024, "%sn", savefile); #endif /* VM */ /* Grab permissions */ safe_setuid_grab(); /* Remove it */ (void)fd_kill(safe); /* Drop permissions */ safe_setuid_drop(); /* Attempt to save the player */ if (save_player_aux(safe)) { char temp[1024]; /* Old savefile */ strnfmt(temp, 1024, "%s.old", savefile); #ifdef VM /* Hack -- support "flat directory" usage on VM/ESA */ strnfmt(temp, 1024, "%so", savefile); #endif /* VM */ /* Grab permissions */ safe_setuid_grab(); /* Remove it */ (void)fd_kill(temp); /* Preserve old savefile */ (void)fd_move(savefile, temp); /* Activate new savefile */ (void)fd_move(safe, savefile); /* Remove preserved savefile */ (void)fd_kill(temp); /* Drop permissions */ safe_setuid_drop(); /* Hack -- Pretend the character was loaded */ character_loaded = TRUE; /* Success */ result = TRUE; } /* Return the result */ return (result); } /* * Attempt to Load a "savefile" * * * On multi-user systems, you may only "read" a savefile if you will be * allowed to "write" it later, this prevents painful situations in which * the player loads a savefile belonging to someone else, and then is not * allowed to save his game when he quits. * * We return "TRUE" if the savefile was usable, and we set the global * flag "character_loaded" if a real, living, character was loaded. * * Note that we always try to load the "current" savefile, even if * there is no such file, so we must check for "empty" savefile names. */ bool load_player(void) { int fd = -1; errr err = 0; byte vvv[4]; cptr what = "generic"; /* Paranoia */ turn = 0; /* Paranoia */ p_ptr->state.is_dead = FALSE; /* Allow empty savefile name */ if (!savefile[0]) return (TRUE); /* Grab permissions */ safe_setuid_grab(); /* Open the savefile */ fd = fd_open(savefile, O_RDONLY); /* Drop permissions */ safe_setuid_drop(); /* No file */ if (fd < 0) { /* Give a message */ msgf("Savefile does not exist."); message_flush(); /* Allow this */ return (TRUE); } /* Close the file */ (void)fd_close(fd); /* Okay */ if (!err) { /* Grab permissions */ safe_setuid_grab(); /* Open the savefile */ fd = fd_open(savefile, O_RDONLY); /* Drop permissions */ safe_setuid_drop(); /* No file */ if (fd < 0) err = -1; /* Message (below) */ if (err) what = "Cannot open savefile"; } /* Process file */ if (!err) { /* Read the first four bytes */ if (fd_read(fd, (char *)(vvv), 4)) err = -1; /* What */ if (err) what = "Cannot read savefile"; /* Close the file */ (void)fd_close(fd); } /* Process file */ if (!err) { /* Extract version */ z_major = vvv[0]; z_minor = vvv[1]; z_patch = vvv[2]; sf_extra = vvv[3]; /* Clear screen */ Term_clear(); /* Parse "new" savefiles */ /* Attempt to load */ err = rd_savefile_new(); /* Message (below) */ if (err) what = "Cannot parse savefile"; } /* Paranoia */ if (!err) { /* Invalid turn */ if (!turn) err = -1; /* Message (below) */ if (err) what = "Broken savefile"; } /* Okay */ if (!err) { /* Give a conversion warning */ if ((VER_MAJOR != z_major) || (VER_MINOR != z_minor) || (VER_PATCH != z_patch)) { /* Message */ msgf("Converted a %d.%d.%d savefile.", z_major, z_minor, z_patch); message_flush(); } /* Player is dead */ if (p_ptr->state.is_dead) { /* Player is no longer "dead" */ p_ptr->state.is_dead = FALSE; /* Cheat death */ if (arg_wizard) { /* A character was loaded */ character_loaded = TRUE; /* * We need to initialise things properly here. * The wilderness is not loaded (or saved when dead) * - so we need to create it now. */ create_wilderness(); /* If we are dead, then our inventory is corrupt */ p_ptr->inventory = 0; /* Done */ return (TRUE); } /* Count lives */ sf_lives++; /* Forget turns */ turn = old_turn = 0; /* Done */ return (TRUE); } /* A character was loaded */ character_loaded = TRUE; /* Still alive */ if (p_ptr->chp >= 0) { /* Reset cause of death */ (void)strcpy(p_ptr->state.died_from, "(alive and well)"); } /* Success */ return (TRUE); } /* Message */ msgf("Error (%s) reading %d.%d.%d savefile.", what, z_major, z_minor, z_patch); message_flush(); /* Oops */ return (FALSE); } zangband/src/scores.c0000755000000000000000000010011710250356274013600 0ustar rootroot/* File: scores.c */ /* Purpose: Highscores handling */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * The "highscore" file descriptor, if available. */ int highscore_fd = -1; /* * Seek score 'i' in the highscore file */ static int highscore_seek(int i) { /* Seek for the requested record */ return (fd_seek(highscore_fd, (huge) (i) * sizeof(high_score))); } /* * Read one score from the highscore file */ static errr highscore_read(high_score *score) { /* Read the record, note failure */ return (fd_read(highscore_fd, (char *)(score), sizeof(high_score))); } /* * Write one score to the highscore file */ static int highscore_write(const high_score *score) { /* Write the record, note failure */ return (fd_write(highscore_fd, (char *)(score), sizeof(high_score))); } /* * Just determine where a new score *would* be placed * Return the location (0 is best) or -1 on failure */ static int highscore_where(const high_score *score) { int i; high_score the_score; /* Paranoia -- it may not have opened */ if (highscore_fd < 0) return (-1); /* Go to the start of the highscore file */ if (highscore_seek(0)) return (-1); /* Read until we get to a higher score */ for (i = 0; i < MAX_HISCORES; i++) { if (highscore_read(&the_score)) return (i); if (strcmp(the_score.pts, score->pts) < 0) return (i); } /* The "last" entry is always usable */ return (MAX_HISCORES); } /* * Actually place an entry into the high score file * Return the location (0 is best) or -1 on "failure" */ static int highscore_add(const high_score *score) { int i, slot; bool done = FALSE; high_score the_score, tmpscore; /* Paranoia -- it may not have opened */ if (highscore_fd < 0) return (-1); /* Determine where the score should go */ slot = highscore_where(score); /* Hack -- Not on the list */ if (slot < 0) return (-1); /* Hack -- prepare to dump the new score */ the_score = (*score); /* Slide all the scores down one */ for (i = slot; !done && (i <= MAX_HISCORES); i++) { /* Read the old guy, note errors */ if (highscore_seek(i)) return (-1); if (highscore_read(&tmpscore)) done = TRUE; /* Back up and dump the score we were holding */ if (highscore_seek(i)) return (-1); if (highscore_write(&the_score)) return (-1); /* Hack -- Save the old score, for the next pass */ the_score = tmpscore; } /* Return location used */ return (slot); } /* * How much valuable stuff do we carry. Returns total cost of all found * items currently in equipment (ie. those that haven't been storebought * or started with) */ static long equip_value(void) { object_type *o_ptr; long total = 0L; int i; /* Scan equipment */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; if (o_ptr->info & OB_NO_EXP) continue; if (!(o_ptr->info & OB_KNOWN)) continue; total += object_value(o_ptr); } /* Scan inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { if (o_ptr->info & OB_NO_EXP) continue; if (!(o_ptr->info & OB_KNOWN)) continue; total += object_value(o_ptr); } OBJ_ITT_END; return (total); } /* * Hack -- Calculates the total number of points earned -JWT- * Now with up to 80% penalty for having mutations & other extra things -GSN- * Fixed this up to be "fairer" -CK- */ static long total_points(void) { long temp; long mult = 85; /* AI is not that big a deal (yet) */ if (stupid_monsters) mult -= 20; /* Penalize preserve mode */ if (preserve_mode) mult -= 10; /* Vanilla town is harder than normal */ if (vanilla_town) mult += 5; /* Not too much of a reward since some people like playing with this. */ if (ironman_small_levels) mult += 5; /* More ironman options */ if (ironman_nightmare) mult += 20; if (mult < 5) mult = 5; /* At least 5% of the original score */ temp = p_ptr->max_exp; temp = (temp * mult) / race_info[p_ptr->rp.prace].r_exp; temp += equip_value() / 10; if (ironman_downward) temp *= 2; return (temp); } /* * Hack - save index of player's high score */ static int score_idx = -1; /* Hack - save from-to for resizing purposes */ static int score_from = -1; static int score_to = -1; static high_score *score_score = NULL; static bool score_resize = FALSE; /* find out how many score entries you can have on a page */ static int entries_on_page(void) { int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* determine how many entries that is on a page */ return (hgt / 4 - 1); } /* Find out what range is nice on the current page size */ static void determine_scores_page(int *from, int *to, int note) { int entries; /* Determine how many entries that is on a page */ entries = entries_on_page(); /* If the note belongs on the first or second page */ if (note <= 3 * entries / 2) { /* Show the second page */ *from = entries; *to = 2 * entries; return; } /* A page with the score nicely in the middle */ *from = note - entries / 2; *to = note + entries / 2; /* even entries lead to too large, odd range */ if (!(entries % 2)) *to -= 1; /* If the range overshoots the last page */ if (*to >= MAX_HISCORES) { *from = MAX_HISCORES - entries; *to = MAX_HISCORES; /* This entry does not get saved, only displayed */ if (note == MAX_HISCORES) { *from += 1; *to += 1; } } } /* * Display the scores in a given range. * Assumes the high score list is already open. * * Mega-Hack -- allow "fake" entry at the given position. */ static bool display_scores_aux2(int from, int to, int note, const high_score *score, bool no_wait) { int i, j, k, n, place; byte attr; high_score the_score; char out_val[256]; int len; int entries; /* Paranoia -- it may not have opened */ if (highscore_fd < 0) return (FALSE); /* Seek to the beginning */ if (highscore_seek(0)) return (FALSE); /* Determine how many entries that is on a page */ entries = entries_on_page(); /* Dimensions of the first page */ if (to < entries) { from = 0; to = entries; } /* Remember ending of the list */ score_to = to; /* Hack -- Count the high scores */ for (i = 0; i < MAX_HISCORES; i++) { if (highscore_read(&the_score)) break; } /* Hack -- allow "fake" entry to be last */ if (note == i) i++; /* Forget about the last entries */ if (i > to) i = to; /* Show 'entries' per page, until "done" */ for (k = from, place = k + 1; k < i; k += entries) { /* Clear screen */ Term_clear(); /* Title */ put_fstr(0, 0, " %s Hall of Fame", VERSION_NAME); /* Indicate non-top scores */ if (k > 0) { put_fstr(40, 0, "(from position %d)", k + 1); } /* Dump entries */ for (j = k, n = 0; j < i && n < entries; place++, j++, n++) { int pr, pc, clev, mlev, cdun, mdun; cptr user, gold, when, aged; /* Remember the current starting point */ score_from = k; /* Hack -- indicate death in yellow */ attr = (j == note) ? TERM_YELLOW : TERM_WHITE; /* Mega-Hack -- insert a "fake" record */ if ((note == j) && score) { the_score = (*score); attr = TERM_L_GREEN; score = NULL; note = -1; j--; } /* Read a normal record */ else { /* Read the proper record */ if (highscore_seek(j)) break; if (highscore_read(&the_score)) break; } /* Extract the race/class */ pr = atoi(the_score.p_r); pc = atoi(the_score.p_c); /* Extract the level info */ clev = atoi(the_score.cur_lev); mlev = atoi(the_score.max_lev); cdun = atoi(the_score.cur_dun); mdun = atoi(the_score.max_dun); /* Hack -- extract the gold and such */ for (user = the_score.uid; isspace(*user); user++) /* loop */ ; for (when = the_score.day; isspace(*when); when++) /* loop */ ; for (gold = the_score.gold; isspace(*gold); gold++) /* loop */ ; for (aged = the_score.turns; isspace(*aged); aged++) /* loop */ ; /* Dump some info */ len = strnfmt(out_val, 256, "%3d.%9s %s the %s %s, Level %d", place, the_score.pts, the_score.who, race_info[pr].title, class_info[pc].title, clev); /* Append a "maximum level" */ if (mlev > clev) strnfcat(out_val, 256, &len, " (Max %d)", mlev); /* Dump the first line */ put_fstr(0, n * 4 + 2, "%s%s", color_seq[attr], out_val); /* Some people die outside of the dungeon */ if (cdun) { /* Another line of info */ len = strnfmt(out_val, 256, " Killed by %s on %s %d", the_score.how, "Dungeon Level", cdun); } else { /* Died outside */ len = strnfmt(out_val, 256, " Killed by %s in the outside world.", the_score.how); } /* Append a "maximum level" */ if (mdun > cdun) strnfcat(out_val, 256, &len, " (Max %d)", mdun); /* Dump the info */ put_fstr(0, n * 4 + 3, "%s%s", color_seq[attr], out_val); /* And still another line of info */ put_fstr(0, n * 4 + 4, "%s (User %s, Date %s, Gold %s, Turn %s).", color_seq[attr], user, when, gold, aged); } /* Wait for response */ prtf(15, entries * 4 + 3, "[Press ESC to quit, any other key to continue.]"); /* No keystrokes needed during resizing */ if (no_wait) return (TRUE); j = inkey(); clear_row(entries * 4 + 3); /* Check for resize, entries may have changed */ if (score_resize) { /* set back to default */ score_resize = FALSE; /* Not all of the previous range was shown yet */ if (score_to < to) { /* Show rest of the range */ return (display_scores_aux2(score_from + entries_on_page(), to, score_idx, score_score, FALSE)); } } /* Hack -- notice Escape */ if (j == ESCAPE) return (FALSE); } return (TRUE); } /* Make sense of the display range and redraw the screen */ static void resize_scores(void) { int from, to; /* Alert the public */ score_resize = TRUE; /* Displaying the whole list? */ if (score_idx == -1 || score_to < score_idx) { /* Display the list */ (void)display_scores_aux2(score_from, score_from + entries_on_page(), -1, NULL, TRUE); } else /* So this page has score_idx at its center */ { /* Make new range for the new page */ determine_scores_page(&from, &to, score_idx); /* Display the list */ (void)display_scores_aux2(from, to, score_idx, score_score, TRUE); } } bool display_scores_aux(int from, int to, int note, const high_score *score) { bool outcome; void (*hook) (void); /* Remember the old hook */ hook = angband_term[0]->resize_hook ; /* set the resize hook to scores */ angband_term[0]->resize_hook = resize_scores; /* Display the scores */ outcome = display_scores_aux2(from, to, note, score, FALSE); /* Restore the old resize hook */ angband_term[0]->resize_hook = hook; /* The size may have changed during the scores display */ angband_term[0]->resize_hook(); /* Hack - Flush it */ Term_fresh(); /* Allow another call depending on the outcome of this call */ return (outcome); } /* * Hack -- Display the scores in a given range and quit. * * This function is only called from "main.c" when the user asks * to see the "high scores". */ void display_scores(int from, int to) { char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_APEX, "scores.raw"); /* Open the binary high score file, for reading */ highscore_fd = fd_open(buf, O_RDONLY); /* Paranoia -- No score file */ if (highscore_fd < 0) quit("Score file unavailable."); /* Clear screen */ Term_clear(); /* Display the scores */ (void)display_scores_aux(from, to, -1, NULL); /* Shut the high score file */ (void)fd_close(highscore_fd); /* Forget the high score fd */ highscore_fd = -1; /* Quit */ quit(NULL); } /* * Enters a players name on a hi-score table, if "legal". * * Assumes "signals_ignore_tstp()" has been called. */ void enter_score(void) { int i; high_score the_score; #ifndef HIGHSCORE_DATE_HACK char long_day[12]; #endif time_t ct = time((time_t *) 0); /* No score file */ if (highscore_fd < 0) { return; } #ifndef SCORE_WIZARDS /* Wizard-mode pre-empts scoring */ if (p_ptr->state.noscore & 0x003F) { msgf("Score not registered for wizards."); message_flush(); score_idx = -1; return; } #endif #ifndef SCORE_BORGS /* Borg-mode pre-empts scoring */ if (p_ptr->state.noscore & 0x00C0) { msgf("Score not registered for borgs."); message_flush(); score_idx = -1; return; } #endif #ifndef SCORE_CHEATERS /* Cheaters are not scored */ if (p_ptr->state.noscore & 0xFF00) { msgf("Score not registered for cheaters."); message_flush(); score_idx = -1; return; } #endif /* Interupted */ if (!p_ptr->state.total_winner && streq(p_ptr->state.died_from, "Interrupting")) { msgf("Score not registered due to interruption."); message_flush(); score_idx = -1; return; } /* Quitter */ if (!p_ptr->state.total_winner && streq(p_ptr->state.died_from, "Quitting")) { msgf("Score not registered due to quitting."); message_flush(); score_idx = -1; return; } /* Clear the record */ (void)WIPE(&the_score, high_score); /* Save the version */ strnfmt(the_score.what, 8, "%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH); /* Calculate and save the points */ strnfmt(the_score.pts, 10, "%9lu", (unsigned long)total_points()); the_score.pts[9] = '\0'; /* Save the current gold */ strnfmt(the_score.gold, 10, "%9lu", (unsigned long)p_ptr->au); the_score.gold[9] = '\0'; /* Save the current turn */ strnfmt(the_score.turns, 10, "%9lu", (unsigned long)turn); the_score.turns[9] = '\0'; #ifdef HIGHSCORE_DATE_HACK /* Save the date in a hacked up form (9 chars) */ (void)strnfmt(the_score.day, 10, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22); #else /* HIGHSCORE_DATE_HACK */ /* Get the date with a 4-digit year */ (void)strftime(long_day, 11, "%m/%d/%Y", localtime(&ct)); /* Remove the century */ i = 7; while (1) { i++; long_day[i - 2] = long_day[i]; /* Exit if get a zero */ if (!long_day[i] || (i == 11)) break; } /* Save the date in standard form (8 chars) */ (void)strnfmt(the_score.day, 9, "%s", long_day); #endif /* HIGHSCORE_DATE_HACK */ /* Save the player name (15 chars) */ strnfmt(the_score.who, 16, "%-.15s", player_name); /* Save the player info XXX XXX XXX */ strnfmt(the_score.uid, 8, "%7u", (uint)player_uid); strnfmt(the_score.sex, 2, "%c", (p_ptr->rp.psex ? 'm' : 'f')); strnfmt(the_score.p_r, 3, "%2d", (int)p_ptr->rp.prace); strnfmt(the_score.p_c, 3, "%2d", (int)p_ptr->rp.pclass); /* Save the level and such */ strnfmt(the_score.cur_lev, 4, "%3d", p_ptr->lev); strnfmt(the_score.cur_dun, 4, "%3d", p_ptr->depth); strnfmt(the_score.max_lev, 4, "%3d", p_ptr->max_lev); strnfmt(the_score.max_dun, 4, "%3d", max_dun_level_reached()); /* Save the cause of death (31 chars) */ strnfmt(the_score.how, 32, "%-.31s", p_ptr->state.died_from); /* Grab permissions */ safe_setuid_grab(); /* Lock (for writing) the highscore file, or fail */ if (fd_lock(highscore_fd, F_WRLCK)) return; /* Drop permissions */ safe_setuid_drop(); /* Add a new entry to the score list, see where it went */ score_idx = highscore_add(&the_score); /* Grab permissions */ safe_setuid_grab(); /* Unlock the highscore file, or fail */ if (fd_lock(highscore_fd, F_UNLCK)) return; /* Drop permissions */ safe_setuid_drop(); /* Success */ return; } /* * Displays some relevant portion of the high score list. */ static void top_twenty(void) { int from, to; bool cont; /* Clear screen */ Term_clear(); /* No score file */ if (highscore_fd < 0) { msgf("Score file unavailable."); message_flush(); return; } /* Show the first page of the highscore */ cont = display_scores_aux(0, 5, score_idx, NULL); /* If the user didn't press ESC, show the second page too */ if (cont) { /* Determine what the second page will be */ determine_scores_page(&from, &to, score_idx); /* Show the second page */ (void)display_scores_aux(from, to, score_idx, NULL); } /* Success */ return; } /* * Predict the players location, and display it. */ void predict_score(void) { int j, from, to; bool cont; high_score the_score; /* No score file */ if (highscore_fd < 0) { msgf("Score file unavailable."); message_flush(); return; } /* Save the version */ strnfmt(the_score.what, 8, "%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH); /* Calculate and save the points */ strnfmt(the_score.pts, 10, "%9lu", (unsigned long)total_points()); /* Save the current gold */ strnfmt(the_score.gold, 10, "%9lu", (unsigned long)p_ptr->au); /* Save the current turn */ strnfmt(the_score.turns, 10, "%9lu", (unsigned long)turn); /* Hack -- no time needed */ strcpy(the_score.day, "TODAY"); /* Save the player name (15 chars) */ strnfmt(the_score.who, 16, "%-.15s", player_name); /* Save the player info XXX XXX XXX */ strnfmt(the_score.uid, 8, "%7u", (uint)player_uid); strnfmt(the_score.sex, 2, "%c", (p_ptr->rp.psex ? 'm' : 'f')); strnfmt(the_score.p_r, 3, "%2d", (int)p_ptr->rp.prace); strnfmt(the_score.p_c, 3, "%2d", (int)p_ptr->rp.pclass); /* Save the level and such */ strnfmt(the_score.cur_lev, 4, "%3d", p_ptr->lev); strnfmt(the_score.cur_dun, 4, "%3d", p_ptr->depth); strnfmt(the_score.max_lev, 4, "%3d", p_ptr->max_lev); strnfmt(the_score.max_dun, 4, "%3d", max_dun_level_reached()); /* Hack -- no cause of death */ strcpy(the_score.how, "nobody (yet!)"); /* See where the entry would be placed */ j = highscore_where(&the_score); /* Keep it for resizing */ score_idx = j; score_score = &the_score; /* Show the first page of the highscore */ cont = display_scores_aux(0, 5, score_idx, &the_score); /* If the user didn't press ESC, show the second page too */ if (cont) { /* Determine what the second page will be */ determine_scores_page(&from, &to, score_idx); /* Show the second page */ (void)display_scores_aux(from, to, score_idx, &the_score); } } /* * Selectively list highscores based on class -KMW- */ void show_highclass(void) { int i = 0, j, m = 0; int pr, pc, clev /*, al */ ; high_score the_score; char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_APEX, "scores.raw"); highscore_fd = fd_open(buf, O_RDONLY); if (highscore_fd < 0) { msgf("Score file unavailable."); message_flush(); return; } if (highscore_seek(0)) return; for (i = 0; i < MAX_HISCORES; i++) if (highscore_read(&the_score)) break; m = 0; j = 0; clev = 0; while ((m < 9) || (j < MAX_HISCORES)) { if (highscore_seek(j)) break; if (highscore_read(&the_score)) break; pr = atoi(the_score.p_r); pc = atoi(the_score.p_c); clev = atoi(the_score.cur_lev); prtf(0, m + 7, "%3d) %s the %s (Level %2d)", (m + 1), the_score.who, race_info[pr].title, clev); m++; j++; } prtf(0, m + 8, "You) %s the %s (Level %2d)", player_name, race_info[p_ptr->rp.prace].title, p_ptr->lev); (void)fd_close(highscore_fd); highscore_fd = -1; msgf("Hit any key to continue"); message_flush(); clear_region(0, 5, 17); } /* * Race Legends * -KMW- */ void race_score(int race_num) { int i = 0, j, m = 0; int pr, pc, clev, lastlev; high_score the_score; char buf[1024]; lastlev = 0; /* rr9: TODO - pluralize the race */ prtf(15, 5, "The Greatest of all the %s", race_info[race_num].title); /* Build the filename */ path_make(buf, ANGBAND_DIR_APEX, "scores.raw"); highscore_fd = fd_open(buf, O_RDONLY); if (highscore_fd < 0) { msgf("Score file unavailable."); message_flush(); return; } if (highscore_seek(0)) return; for (i = 0; i < MAX_HISCORES; i++) { if (highscore_read(&the_score)) break; } m = 0; j = 0; while ((m < 10) || (j < MAX_HISCORES)) { if (highscore_seek(j)) break; if (highscore_read(&the_score)) break; pr = atoi(the_score.p_r); pc = atoi(the_score.p_c); clev = atoi(the_score.cur_lev); if (pr == race_num) { prtf(0, m + 7, "%3d) %s the %s (Level %3d)", (m + 1), the_score.who, race_info[pr].title, clev); m++; lastlev = clev; } j++; } /* add player if qualified */ if ((p_ptr->rp.prace == race_num) && (p_ptr->lev >= lastlev)) { prtf(0, m + 8, "You) %s the %s (Level %3d)", player_name, race_info[p_ptr->rp.prace].title, p_ptr->lev); } (void)fd_close(highscore_fd); highscore_fd = -1; } /* * Race Legends * -KMW- */ void race_legends(void) { int i; for (i = 0; i < MAX_RACES; i++) { race_score(i); msgf("Hit any key to continue"); message_flush(); clear_region(0, 5, 18); } } /* * Change the player into a King! -RAK- */ static void kingly(void) { /* Hack -- retire in town */ p_ptr->depth = 0; /* Fake death */ (void)strcpy(p_ptr->state.died_from, "Ripe Old Age"); /* Restore the experience */ p_ptr->exp = p_ptr->max_exp; /* Restore the level */ p_ptr->lev = p_ptr->max_lev; /* Hack -- Instant Gold */ p_ptr->au += 10000000L; /* Clear screen */ Term_clear(); /* Display a crown */ put_fstr(22, 1, " %%%\n" " %@%\n" " ___ % ___\n" " _=$$###\\ ##### /###$$=_\n" " $$_______##$$_____$$##_______$$\n" "(** ** ** ** ** **)\n" "(** ** ** ** ** **)\n" " TTTTTTTTTTTTTTTTTTTTTTTTTTT\n" " \\. $$$$$$ .\\. $$$$$$ ./. $$$$$$ ./\n" " \\ * | * | * /\n" " \\. .|. .|. ./\n" " ##H##H##H###H##H##H##\n" " ##H##H##H###H##H##H##\n"); /* Display a message */ put_fstr(26, 15, "Veni, Vidi, Vici!"); put_fstr(21, 16, "I came, I saw, I conquered!"); put_fstr(22, 17, "All Hail the Mighty %s!", sp_ptr->winner); /* Flush input */ flush(); /* Wait for response */ pause_line(23); } void ingame_score(bool *initialized, bool game_in_progress) { char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_APEX, "scores.raw"); /* Open the binary high score file, for reading */ highscore_fd = fd_open(buf, O_RDONLY); /* Paranoia -- No score file */ if (highscore_fd < 0) { msgf("Score file unavailable."); } else { /* Prevent various functions */ *initialized = FALSE; /* Display the scores */ if (game_in_progress && character_generated) { /* Show a selection of the score list */ predict_score(); } else { /* Save Screen */ screen_save(); /* Show all the scores */ (void)display_scores_aux(0, MAX_HISCORES, -1, NULL); /* Load screen */ screen_load(); /* Hack - Flush it */ Term_fresh(); } /* Shut the high score file */ (void)fd_close(highscore_fd); /* Forget the high score fd */ highscore_fd = -1; /* We are ready again */ *initialized = TRUE; } } /* * Display a "tomb-stone" */ static void print_tomb(void) { bool done = FALSE; /* Print the text-tombstone */ if (!done) { char buf[1024]; FILE *fp; time_t ct = time((time_t) 0); /* Clear screen */ Term_clear(); /* Build the filename */ path_make(buf, ANGBAND_DIR_FILE, "dead.txt"); /* Open the News file */ fp = my_fopen(buf, "r"); /* Dump */ if (fp) { int i = 0; /* Dump the file to the screen */ while (0 == my_fgets(fp, buf, 1024)) { /* Display and advance */ put_fstr(0, i++, buf); } /* Close */ my_fclose(fp); } put_fstr(11, 6, "%v", center_string, 31, player_name); put_fstr(11, 7, "%v", center_string, 31, "the"); /* King or Queen */ if (p_ptr->state.total_winner || (p_ptr->lev > PY_MAX_LEVEL)) { put_fstr(11, 8, "%v", center_string, 31, "Magnificent"); } /* Normal */ else { put_fstr(11, 8, "%v", center_string, 31, player_title[p_ptr->rp.pclass][(p_ptr->lev - 1) / 5]); } put_fstr(11, 10, "%v", center_string, 31, cp_ptr->title); put_fstr(11, 11, "%v", center_string, 31, "Level: %d", (int)p_ptr->lev); put_fstr(11, 12, "%v", center_string, 31, "Exp: %ld", (long)p_ptr->exp); put_fstr(11, 13, "%v", center_string, 31, "AU: %ld", (long)p_ptr->au); put_fstr(11, 14, "%v", center_string, 31, "Killed on Level %d", p_ptr->depth); if (strlen(p_ptr->state.died_from) > 24) { put_fstr(11, 15, "%v", center_string, 31, "by %.24s.", p_ptr->state.died_from); } else { put_fstr(11, 15, "%v", center_string, 31, "by %s.", p_ptr->state.died_from); } put_fstr(11, 17, "%v", center_string, 31, "%-.24s", ctime(&ct)); } } /* * Display some character info */ static void show_info(void) { int i, j, l; object_type *o_ptr; store_type *st_ptr; /* Hack -- Know everything in the equipment */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Aware and Known */ object_aware(o_ptr); object_known(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; } /* Hack -- Know everything in the inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Aware and Known */ object_aware(o_ptr); object_known(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; } OBJ_ITT_END; for (i = 1; i < z_info->wp_max; i++) { for (j = 0; j < place[i].numstores; j++) { st_ptr = &place[i].store[j]; if (st_ptr->type == BUILD_STORE_HOME) { /* Hack -- Know everything in the home */ OBJ_ITT_START (st_ptr->stock, o_ptr) { /* Aware and Known */ object_aware(o_ptr); object_known(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; } OBJ_ITT_END; } } } /* Hack -- Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Display player */ display_player(DISPLAY_PLAYER_STANDARD); /* Prompt for inventory */ prtf(0, 23, "Hit any key to see more information (ESC to abort): "); /* Flush keys */ flush(); /* Allow abort at this point */ if (inkey() == ESCAPE) return; /* Show equipment */ Term_clear(); /* Equipment -- if any */ item_tester_full = TRUE; show_equip(FALSE); prtf(0, 0, "You are using: -more-"); /* Flush keys */ flush(); if (inkey() == ESCAPE) return; /* Show inventory */ Term_clear(); /* Inventory -- if any */ item_tester_full = TRUE; show_list(p_ptr->inventory, FALSE); prtf(0, 0, "You are carrying: -more-"); /* Flush keys */ flush(); if (inkey() == ESCAPE) return; for (i = 1; i < z_info->wp_max; i++) { for (l = 0; l < place[i].numstores; l++) { st_ptr = &place[i].store[l]; if (st_ptr->type == BUILD_STORE_HOME) { /* Home -- if anything there */ if (st_ptr->stock) { /* Initialise counter */ j = 0; /* Clear screen */ Term_clear(); /* Display contents of the home */ OBJ_ITT_START (st_ptr->stock, o_ptr) { /* Print header, clear line */ prtf(4, j + 2, "%c) %s%v", I2A(j), color_seq[tval_to_attr[o_ptr->tval]], OBJECT_FMT(o_ptr, TRUE, 3)); /* Show 12 items at a time */ if (j == 12) { /* Caption */ prtf(0, 0, "Your home in %s: -more-", place[i].name); /* Flush keys */ flush(); /* Wait for it */ if (inkey() == ESCAPE) return; /* Restart counter */ j = 0; /* Clear screen */ Term_clear(); } } OBJ_ITT_END; } } } } } static void close_game_handle_death(void) { char ch; /* Handle retirement */ if (p_ptr->state.total_winner) { /* Save winning message to notes file. */ if (take_notes) { add_note_type(NOTE_WINNER); } kingly(); } /* Save memories */ if (!munchkin_death || get_check("Save death? ")) { if (!save_player()) msgf("death save failed!"); } #if 0 /* Dump bones file */ make_bones(); #endif /* Inform notes file that you are dead */ if (take_notes) { char long_day[30]; time_t ct = time((time_t *) NULL); /* Get the date */ (void)strftime(long_day, 30, "%Y-%m-%d at %H:%M:%S", localtime(&ct)); /* Output to the notes file */ output_note("\n%s was killed by %s on %s\n", player_name, p_ptr->state.died_from, long_day); } /* Enter player in high score list */ enter_score(); /* You are dead */ print_tomb(); /* Describe options */ prtf(0, 23, "(D) Dump char record (C) Show char info (T) Show top scores (ESC) Exit"); /* Flush messages */ message_flush(); /* Flush all input keys */ flush(); /* Player selection */ while (TRUE) { /* Save screen */ /* Note that Term_save() and Term_load() must match in pairs */ Term_save(); /* Flush all input keys */ flush(); ch = inkey(); switch (ch) { case ESCAPE: { /* Flush the keys */ flush(); if (get_check("Do you really want to exit? ")) { /* Save dead player */ if (!save_player()) { msgf("Death save failed!"); message_flush(); } #if 0 /* Dump bones file */ make_bones(); #endif /* XXX We now have an unmatched Term_save() */ Term_load(); /* Go home, we're done */ return; } else { break; } } case 'd': case 'D': { /* Dump char file */ char tmp[160] = ""; /* Clear this line first */ clear_row(23); /* Prompt */ put_fstr(0, 23, "Filename: "); /* Ask for filename (or abort) */ if (!askfor_aux(tmp, 60)) break; /* Ignore Return */ if (!tmp[0]) break; /* Dump a character file */ (void)file_character(tmp, FALSE); break; } case 'c': case 'C': { /* Remove options line, so we don't dump it */ clear_row(23); /* Show char info */ show_info(); break; } case 't': case 'T': { /* Show top twenty */ top_twenty(); break; } } /* Restore the screen */ Term_load(); } } /* * Close up the current game (player may or may not be dead) * * This function is called only from "main.c" and "signals.c". */ void close_game(void) { char buf[1024]; /* Handle stuff */ handle_stuff(); /* Flush the messages */ message_flush(); /* Flush the input */ flush(); /* No suspending now */ signals_ignore_tstp(); /* Hack -- Character is now "icky" */ screen_save(); /* Build the filename */ path_make(buf, ANGBAND_DIR_APEX, "scores.raw"); /* Grab permissions */ safe_setuid_grab(); /* Open the high score file, for reading/writing */ highscore_fd = fd_open(buf, O_RDWR); /* Drop permissions */ safe_setuid_drop(); if (p_ptr->state.is_dead) { /* Handle death */ close_game_handle_death(); } /* Still alive */ else { int wid, hgt; /* Save the game */ do_cmd_save_game(FALSE); /* If note-taking enabled, write session end to notes file */ if (take_notes) { add_note_type(NOTE_SAVE_GAME); } /* Get size */ Term_get_size(&wid, &hgt); /* Prompt for scores XXX XXX XXX */ prtf(0, hgt - 1, "Press Return (or Escape)."); /* Predict score (or ESCAPE) */ if (inkey() != ESCAPE) predict_score(); } /* Shut the high score file */ (void)fd_close(highscore_fd); /* Forget the high score fd */ highscore_fd = -1; /* No longer icky */ screen_load(); /* Allow suspending now */ signals_handle_tstp(); } /* * Handle abrupt death of the visual system * * This routine is called only in very rare situations, and only * by certain visual systems, when they experience fatal errors. * * XXX XXX Hack -- clear the death flag when creating a HANGUP * save file so that player can see tombstone when restart. */ void exit_game_panic(void) { /* If nothing important has happened, just quit */ if (!character_generated || character_saved) quit("panic"); /* Mega-Hack -- see "msgf()" */ msg_flag = FALSE; /* Clear the top line */ clear_msg(); /* Hack -- turn off some things */ disturb(TRUE); /* Mega-Hack -- Delay death */ if (p_ptr->chp < 0) p_ptr->state.is_dead = FALSE; /* Hardcode panic save */ p_ptr->state.panic_save = 1; /* Forbid suspend */ signals_ignore_tstp(); /* Indicate panic save */ (void)strcpy(p_ptr->state.died_from, "(panic save)"); /* Panic save, or get worried */ if (!save_player()) quit("panic save failed!"); /* Successful panic save */ quit("panic save succeeded!"); } zangband/src/script.c0000755000000000000000000004450310250356274013614 0ustar rootroot/* File: script.c */ #include "angband.h" #include "script.h" #include "lua/lua.h" #include "lua/lualib.h" #include "lua/lauxlib.h" #include "lua/tolua.h" #include "lua/luadebug.h" /* * Lua state */ static lua_State* L = NULL; static int xxx_msgf(lua_State *L) { cptr text = lua_tostring(L, 1); if (text) msgf(text); lua_pop(L, 1); return 0; } static int xxx_msg_flush(lua_State *L) { /* Hack - ignore parameter */ (void) L; message_flush(); return 0; } static int xxx_build_script_path(lua_State *L) { char buf[1024]; cptr filename; if (!tolua_istype(L, 1, LUA_TSTRING,0)) tolua_error(L, "#vinvalid type in variable assignment."); filename = tolua_getstring(L, 1, 0); path_make(buf, ANGBAND_DIR_SCRIPT, filename); tolua_pushstring(L, buf); return 1; } static int xxx_get_rumor(lua_State *L) { errr err; char buf[1024]; switch (randint1(20)) { case 1: err = get_rnd_line("chainswd.txt", 0, buf); break; case 2: err = get_rnd_line("error.txt", 0, buf); break; case 3: case 4: case 5: err = get_rnd_line("death.txt", 0, buf); break; default: err = get_rnd_line("rumors.txt", 0, buf); break; } /* An error occured */ if (err) strcpy(buf, "Some rumors are wrong."); /* Push the rumor */ tolua_pushstring(L, buf); /* One result */ return (1); } static int xxx_get_rnd_line(lua_State *L) { cptr filename; char buf[1024]; int err; filename = lua_tostring(L, 1); /* Pop off the filename */ lua_pop(L, 1); err = get_rnd_line(filename, 0, buf); /* Check for error */ if (err) return 0; tolua_pushstring(L, buf); return (1); } static int xxx_get_aim_dir(lua_State *L) { int dir; bool success; success = get_aim_dir(&dir); tolua_pushbool(L, success); lua_pushnumber(L, dir); return 2; } static int xxx_fire_beam(lua_State *L) { int typ, dir, dam; bool result; typ = (int)luaL_check_number(L, 1); dir = (int)luaL_check_number(L, 2); dam = (int)luaL_check_number(L, 3); result = fire_beam(typ, dir, dam); tolua_pushbool(L, result); return 1; } #ifdef RISCOS extern char *riscosify_name(const char *); static int xxx_dofile(lua_State *L) { cptr filename = lua_tostring(L, 1); /* Pop off the filename */ lua_pop(L, 1); if (filename) { return lua_dofile(L, riscosify_name(filename)); } return 0; } #endif /* RISCOS */ static int xxx_building_options(lua_State *L) { int i; /* number of arguments */ int n = lua_gettop(L); cptr *strings; /* Make array of strings */ C_MAKE(strings, n, cptr); for (i = 1; i <= n; i++) { if (!tolua_istype(L,i,LUA_TSTRING,0)) { tolua_error(L, "#vinvalid type in lua_building_options()."); } strings[i - 1] = lua_tostring(L, i); } /* Print them out */ print_building_options(strings, n); /* Free saved pointers */ FREE((vptr) strings); return 0; } static const struct luaL_reg anglib[] = { {"msg_print", xxx_msgf}, {"msg_flush", xxx_msg_flush}, {"get_aim_dir", xxx_get_aim_dir}, {"fire_beam", xxx_fire_beam}, {"build_script_path", xxx_build_script_path}, {"get_rumor", xxx_get_rumor}, {"get_rnd_line", xxx_get_rnd_line}, {"building_options", xxx_building_options}, #ifdef RISCOS {"dofile", xxx_dofile}, #endif /* RISCOS */ }; #define MULTIADIC(name, op) \ static int name(lua_State* L) \ { \ int i, n = lua_gettop(L);\ \ int result = luaL_check_int(L, 1); \ \ for (i = 2; i <= n; i++)\ { \ result = result op luaL_check_int(L, i); \ } \ lua_pushnumber(L, result); \ return 1; \ } #define DYADIC(name, op) \ static int name(lua_State* L) \ { \ lua_pushnumber(L, luaL_check_int(L, 1) op luaL_check_int(L, 2)); \ return 1; \ } #define MONADIC(name, op) \ static int name(lua_State* L) \ { \ lua_pushnumber(L, op luaL_check_int(L, 1)); \ return 1; \ } DYADIC(intMod, % ) MULTIADIC(intAnd, & ) MULTIADIC(intOr, | ) DYADIC(intXor, ^ ) DYADIC(intShiftl, <<) DYADIC(intShiftr, >>) MONADIC(intBitNot, ~ ) static int math_min(lua_State *L) { int i; /* number of arguments */ int n = lua_gettop(L); long dmin = luaL_check_number(L, 1); for (i = 2; i <= n; i++) { long d = luaL_check_number(L, i); if (d < dmin) dmin = d; } lua_pushnumber(L, dmin); return 1; } static int math_max(lua_State *L) { int i; /* number of arguments */ int n = lua_gettop(L); long dmax = luaL_check_number(L, 1); for (i = 2; i <= n; i++) { long d = luaL_check_number(L, i); if (d > dmax) dmax = d; } lua_pushnumber(L, dmax); return 1; } static const struct luaL_reg intMathLib[] = { {"mod", intMod }, {"bAnd", intAnd }, {"bOr", intOr }, {"bXor", intXor }, {"bNot", intBitNot}, {"shiftl", intShiftl}, {"shiftr", intShiftr}, {"min", math_min }, {"max", math_max }, }; /* * Execute a piece of lua code, passing global variables and returning values. * * The "format" string should consist of zero or more format codes for * values to pass to the script, optionally followed by a ':' and zero or more * format codes for values to pass and return. Format codes are: * * i - an integer * b - a boolean * s - a string [cptr] * p - a pointer to a user-defined type * * For 'i', 'b', and 's', the vararg list should include a cptr giving the name * of the variable to pass it in, followed by a value of the correct type (if * before the ':') or a pointer to a variable of the correct type (if after). * The macros LUA_VAR(x) and LUA_RETURN(x) pass a variable in the appropriate * form for before or after the ':' respectively. * * For 'p', it should include a cptr with the variable name, a cptr with the * name of the type, and the pointer itself, in order. * * If the script returns values, they are returned in the return variables, * in the order they are declared in the format. If fewer values are returned * than there are returned variables, the remaining variables recieve the * values they have at the end of the lua code - generally the same as was * passed in, unless the lua code changed it. Any excess return values are * ignored. * * The value returned for string arguments is always made with string_make() * and should be freed with string_free() when no longer needed. */ static bool call_lua_hook(cptr script, cptr format, va_list vp) { int i, status; cptr vars[20]; void *out[20]; int first_return = 0; int oldtop = lua_gettop(L); bool success; for (i = 0; format[i] && i < 20; i++) { cptr type, var; switch (format[i]) { case 'i': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; lua_pushnumber(L, va_arg(vp, int)); lua_setglobal(L, var); break; case 'b': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; tolua_pushbool(L, va_arg(vp, int)); lua_setglobal(L, var); break; case 's': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; tolua_pushstring(L, va_arg(vp, char *)); lua_setglobal(L, var); break; case 'p': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; /* Get the type */ type = va_arg(vp, cptr); tolua_pushusertype(L, va_arg(vp, void *), tolua_tag(L, type)); lua_setglobal(L, var); break; case ':': /* Start of return arguments */ break; } if (format[i] == ':') { i++; break; } } /* Save the first return argument (or the '\0' if none) */ first_return = i; for (i = first_return; format[i] && i < 20; i++) { cptr var; int *ival; bool *bval; cptr *sval; switch (format[i]) { case 'i': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; ival = va_arg(vp, int *); out[i] = (void *)ival; lua_pushnumber(L, *ival); lua_setglobal(L, var); break; case 'b': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; bval = va_arg(vp, bool *); out[i] = (void *)bval; tolua_pushbool(L, *bval); lua_setglobal(L, var); break; case 's': /* Get the next argument */ var = va_arg(vp, cptr); vars[i] = var; sval = va_arg(vp, cptr *); out[i] = (void *)sval; lua_pushstring(L, *sval); lua_setglobal(L, var); break; default: vars[i] = NULL; out[i] = NULL; break; } } status = lua_dostring(L, script); if (status == 0) { int *ival; bool *bval; cptr *sval; int n = 1; int top = lua_gettop(L); for (i = first_return; format[i] && i < 20; i++) { int where; if (top < oldtop + n) { lua_getglobal(L, vars[i]); where = -1; } else where = oldtop + n; switch (format[i]) { case 'i': ival = (int *)out[i]; *ival = tolua_getnumber(L, where, 0); n++; break; case 'b': bval = (bool *)out[i]; *bval = tolua_getbool(L, where, FALSE); n++; break; case 's': sval = (cptr *)out[i]; *sval = string_make(tolua_getstring(L, where, "")); n++; break; } } lua_settop(L, oldtop); /* Worked */ success = TRUE; } else { /* We failed */ success = FALSE; } /* Clear variables */ for (i = 0; format[i] && i < 20; i++) { switch (format[i]) { case 'i': case 'b': case 's': case 'p': lua_pushnil(L); lua_setglobal(L, vars[i]); break; } } /* Done */ return (success); } /* * Apply an object trigger, a small lua script which can be attached to an * object type or a specific item (usually an ego-item or artifact). * * Currently defined triggers, and their normal arguments, include: * * TRIGGER_USE - for activating a wearable item or using any other item. * Wearable items neither take nor return values. Other items may or may not * have a 'dir' value, depending on type, and may return 'result' and 'ident' * which indicate if the action used a charge and if it should identify the * object, respectively. * * TRIGGER_MAKE - called once near the end of object generation. Takes one * argument, 'lev', which is the level the object is being generated at for * non-artifacts and the level of the artifact for artifacts. * * TRIGGER_BONUS - called on worn items during calc_bonuses(). No arguments. * * TRIGGER_SMASH - called for potions when they break. * * TRIGGER_DESC - called to get an activation/use description for an item. * Returns a string describing the activation or use. */ void apply_object_trigger(int trigger_id, object_type *o_ptr, cptr format, ...) { va_list vp; object_kind *k_ptr = &k_info[o_ptr->k_idx]; cptr script = NULL; void *q_ptr = NULL; bool success; if (o_ptr->trigger[trigger_id]) script = quark_str(o_ptr->trigger[trigger_id]); else if (k_ptr->trigger[trigger_id]) script = k_text + k_ptr->trigger[trigger_id]; else return; /* Save parameter so recursion works. */ lua_getglobal (L, "object"); if (tolua_istype(L, -1, tolua_tag(L, "object_type"), 0)) { q_ptr = tolua_getuserdata(L, -1, NULL); } lua_pop(L,1); /* Set parameters (really global) */ tolua_pushusertype(L, (void*)o_ptr, tolua_tag(L, "object_type")); lua_setglobal(L, "object"); /* Begin the Varargs Stuff */ va_start(vp, format); success = call_lua_hook(script, format, vp); /* End the Varargs Stuff */ va_end(vp); /* Restore global so recursion works*/ tolua_pushusertype(L, q_ptr, tolua_tag(L,"object_type")); lua_setglobal(L, "object"); /* Paranoia */ if (!success) { msgf("Script for object: %v failed.", OBJECT_STORE_FMT(o_ptr, FALSE, 3)); } } /* * Callback for using an object * * If the object is a scroll of identify this function may have a side effect. * The side effect is that the o_ptr no longer points at the scroll of identity * because of the sorting done by the identify_spell. * This side effect is countered in do_cmd_read_scroll_aux, the only place where * it matters. */ bool use_object(object_type *o_ptr, bool *id_return, int dir) { bool result = TRUE, ident = *id_return; apply_object_trigger(TRIGGER_USE, o_ptr, "i:bb", LUA_VAR(dir), LUA_RETURN(result), LUA_RETURN(ident)); *id_return = ident; return result; } static bool field_delete = FALSE; /* * Delete current field when finish processing. * * This is a horrible name... */ void deleteme(void) { field_delete = TRUE; } /* * Apply an field trigger, a small lua script which does * what the old field action functions did. */ bool apply_field_trigger(cptr script, field_type *f_ptr, cptr format, va_list vp) { field_thaum *t_ptr = &t_info[f_ptr->t_idx]; void *q_ptr = NULL; bool success; bool delete_save, delete; /* Save parameter so recursion works. */ lua_getglobal (L, "field"); if (tolua_istype(L, -1, tolua_tag(L, "field_type"), 0)) { q_ptr = tolua_getuserdata(L, -1, NULL); } lua_pop(L,1); delete_save = field_delete; /* Default to no deletion */ field_delete = FALSE; /* Set parameters (really global) */ tolua_pushusertype(L, (void*)f_ptr, tolua_tag(L, "field_type")); lua_setglobal(L, "field"); success = call_lua_hook(script, format, vp); /* Restore global so recursion works*/ tolua_pushusertype(L, q_ptr, tolua_tag(L,"field_type")); lua_setglobal(L, "field"); delete = field_delete; field_delete = delete_save; /* Paranoia */ if (!success) { msgf("Script for field: %s failed.", t_ptr->name); } /* Does the field want to be deleted? */ return (delete); } /* * Apply an field trigger, a small lua script which does * what the old field action functions did. * * This version doesn't modify the field, but uses a copy instead. * This allows const versions of field hooks. * * The field cannot be deleted. */ void const_field_trigger(cptr script, const field_type *f_ptr, cptr format, va_list vp) { /* Structure copy to get local working version */ field_type temp_field = *f_ptr; (void) apply_field_trigger(script, &temp_field, format, vp); } static void line_hook(lua_State *L, lua_Debug *ar) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (window_flag[j] & PW_SCRIPT_SOURCE) { /* Activate */ Term_activate(angband_term[j]); lua_getstack(L, 0, ar); lua_getinfo(L, "S", ar); show_file(ar->source + 1, ar->short_src, ar->currentline - 1, 1); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } else if (window_flag[j] & PW_SCRIPT_VARS) { char buf[1024]; /* Activate */ Term_activate(angband_term[j]); path_make(buf, ANGBAND_DIR_SCRIPT, "trace.lua"); /* Execute the file */ script_do_file(buf); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } } static void script_trace_start(void) { if (!L) return; lua_setlinehook(L, line_hook); } static void script_trace_stop(void) { if (!L) return; lua_setlinehook(L, NULL); } void do_cmd_script(void) { int ch; char tmp[80]; /* Save screen */ screen_save(); /* Clear screen */ Term_clear(); /* Ask for a choice */ prtf(0, 2, "Debug scripts"); /* Give some choices */ prtf(5, 4, "(1) Execute a script file"); prtf(5, 5, "(2) Execute a script command"); prtf(5, 6, "(3) Start tracing scripts"); prtf(5, 7, "(4) Stop tracing scripts"); prtf(5, 8, "(5) Re-initialize scripts"); /* Prompt */ prtf(0, 15, "Command: "); /* Prompt */ ch = inkey(); /* Load screen */ screen_load(); switch (ch) { case '1': { char buf[1024]; /* Prompt */ prtf(0, 0, "Lua script: "); /* Default filename */ sprintf(tmp, "test.lua"); /* Ask for a file */ if (!askfor_aux(tmp, 80)) break; /* Clear the prompt */ clear_msg(); path_make(buf, ANGBAND_DIR_SCRIPT, tmp); /* Execute the file */ script_do_file(buf); break; } case '2': { /* Prompt */ prtf(0, 0, "Lua command: "); /* Empty default */ tmp[0] = 0; /* Ask for a command */ if (!askfor_aux(tmp, 80)) break; /* Clear the prompt */ clear_msg(); /* Execute the command */ script_do_string(tmp); break; } case '3': { script_trace_start(); break; } case '4': { script_trace_stop(); break; } case '5': { char buf[1024]; /* Initialization code */ path_make(buf, ANGBAND_DIR_SCRIPT, "init.lua"); script_do_file(buf); break; } } } extern int tolua_player_open(lua_State* tolua_S); extern void tolua_player_close(lua_State* tolua_S); extern int tolua_object_open(lua_State* tolua_S); extern void tolua_object_close(lua_State* tolua_S); extern int tolua_monst_open(lua_State* tolua_S); extern void tolua_monst_close(lua_State* tolua_S); extern int tolua_random_open(lua_State* tolua_S); extern void tolua_random_close(lua_State* tolua_S); extern int tolua_ui_open(lua_State* tolua_S); extern void tolua_ui_close(lua_State* tolua_S); extern int tolua_misc_open(lua_State* tolua_S); extern void tolua_misc_close(lua_State* tolua_S); extern int tolua_spell_open(lua_State* tolua_S); extern void tolua_spell_close(lua_State* tolua_S); extern int tolua_field_open(lua_State* tolua_S); extern void tolua_field_close(lua_State* tolua_S); /* * Initialize scripting support */ errr script_init(void) { char buf[1024]; /* Start the interpreter with default stack size */ L = lua_open(0); /* Register the Lua base libraries */ lua_baselibopen(L); lua_strlibopen(L); lua_dblibopen(L); /* Register library with binary functions */ luaL_openl(L, intMathLib); /* Register the Angband base library */ luaL_openl(L, anglib); /* Register various Angband libraries */ tolua_player_open(L); tolua_object_open(L); tolua_monst_open(L); tolua_random_open(L); tolua_ui_open(L); tolua_misc_open(L); tolua_spell_open(L); tolua_field_open(L); /* Initialization code */ path_make(buf, ANGBAND_DIR_SCRIPT, "init.lua"); script_do_file(buf); return 0; } errr script_free(void) { if (L) { lua_close(L); return 0; } else { /* Error */ return -1; } } bool script_do_string(cptr script) { if (!L) return FALSE; if (!lua_dostring(L, script)) return TRUE; return FALSE; } bool script_do_file(cptr filename) { if (!L) return FALSE; #ifdef RISCOS { char *realname = riscosify_name(filename); if (!lua_dofile(L, realname)) return TRUE; } #else /* RISCOS */ if (!lua_dofile(L, filename)) return TRUE; #endif /* RISCOS */ return FALSE; } /* * Does the player have a certain resistance? */ bool player_res(u32b flag) { return ((p_ptr->flags[1] & flag) ? TRUE : FALSE); } /* * Debug lua stack overflow */ #include "lua/lstate.h" void debug_lua_stack(void) { /* Need interpreter */ if (!L) return; msgf("Current stack depth: %d", L->stack_last - L->top); } zangband/src/spells1.c0000755000000000000000000030072510250356274013674 0ustar rootroot/* File: spells1.c */ /* Purpose: Spell projection */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Array of monsters who have died from the result of a spell effect. * * This is used to prevent the stack-smash when too many monsters * die in an explosion chain-reaction. (This is used as a circular * queue.) */ static s16b mon_d_head = 0; static s16b mon_d_tail = 0; static s16b mon_d_m_idx[DEATH_MAX]; /* * Get a legal "multi-hued" color for drawing "spells" */ static byte mh_attr(int max) { switch (randint1(max)) { case 1: return (TERM_RED); case 2: return (TERM_GREEN); case 3: return (TERM_BLUE); case 4: return (TERM_YELLOW); case 5: return (TERM_ORANGE); case 6: return (TERM_VIOLET); case 7: return (TERM_L_RED); case 8: return (TERM_L_GREEN); case 9: return (TERM_L_BLUE); case 10: return (TERM_UMBER); case 11: return (TERM_L_UMBER); case 12: return (TERM_SLATE); case 13: return (TERM_WHITE); case 14: return (TERM_L_WHITE); case 15: return (TERM_L_DARK); } return (TERM_WHITE); } /* * Return a color to use for the bolt/ball spells */ static byte spell_color(int type) { /* Check if can use graphics */ if ((use_graphics == GRAPHICS_ADAM_BOLT) || (use_graphics == GRAPHICS_HALF_3D) || (use_graphics == GRAPHICS_DAVID_GERVAIS)) { /* Analyze */ switch (type) { case GF_MISSILE: return (0x0F); case GF_ACID: return (0x04); case GF_ELEC: return (0x02); case GF_FIRE: return (0x00); case GF_COLD: return (0x01); case GF_POIS: return (0x03); case GF_HOLY_FIRE: return (0x00); case GF_HELL_FIRE: return (0x00); case GF_MANA: return (0x0E); case GF_ARROW: return (0x0F); case GF_WATER: return (0x04); case GF_NETHER: return (0x07); case GF_CHAOS: return (mh_attr(15)); case GF_DISENCHANT: return (0x05); case GF_NEXUS: return (0x0C); case GF_CONFUSION: return (mh_attr(4)); case GF_SOUND: return (0x09); case GF_SHARDS: return (0x08); case GF_FORCE: return (0x09); case GF_INERTIA: return (0x09); case GF_GRAVITY: return (0x09); case GF_TIME: return (0x09); case GF_LITE_WEAK: return (0x06); case GF_LITE: return (0x06); case GF_DARK_WEAK: return (0x07); case GF_DARK: return (0x07); case GF_PLASMA: return (0x0B); case GF_METEOR: return (0x00); case GF_ICE: return (0x01); case GF_ROCKET: return (0x0F); case GF_DEATH_RAY: return (0x07); case GF_NUKE: return (mh_attr(2)); case GF_DISINTEGRATE: return (0x05); case GF_PSI: case GF_PSI_DRAIN: case GF_TELEKINESIS: case GF_DOMINATION: return (0x09); } } /* Normal tiles or ASCII */ else if (use_color) { byte a; char c; /* Lookup the default colors for this type */ cptr s = gf_color[type]; /* Oops */ if (!s) return (TERM_WHITE); /* Pick a random color */ c = s[randint0(strlen(s))]; /* Lookup this color */ a = strchr(color_char, c) - color_char; /* * Invalid color (note check for < 0 removed, gave a silly * warning because bytes are always >= 0 -- RG) */ if (a > 15) return (TERM_WHITE); /* Use this color */ return (a); } /* Standard "color" */ return (TERM_WHITE); } /* * Find the attr/char pair to use for a spell effect * * It is moving (or has moved) from (x,y) to (nx,ny). * * If the distance is not "one", we (may) return "*". */ static void bolt_pict(int x, int y, int nx, int ny, int typ, byte *a, byte *c) { int base; byte k; /* No motion (*) */ if ((ny == y) && (nx == x)) base = 0x30; /* Vertical (|) */ else if (nx == x) base = 0x40; /* Horizontal (-) */ else if (ny == y) base = 0x50; /* Diagonal (/) */ else if ((ny - y) == (x - nx)) base = 0x60; /* Diagonal (\) */ else if ((ny - y) == (nx - x)) base = 0x70; /* Weird (*) */ else base = 0x30; /* Basic spell color */ k = spell_color(typ); /* Obtain attr/char */ *a = misc_to_attr[base + k]; *c = misc_to_char[base + k]; } /* * Mega-Hack -- track "affected" monsters (see "project()" comments) */ static int project_m_n; static int project_m_x; static int project_m_y; /* * We are called from "project()" to "damage" terrain features * * We are called both for "beam" effects and "ball" effects. * * The "r" parameter is the "distance from ground zero". * * Note that we determine if the player can "see" anything that happens * by taking into account: blindness, line-of-sight, and illumination. * * We return "TRUE" if the effect of the projection is "obvious". * * XXX XXX XXX We also "see" grids which are "memorized", probably a hack * * XXX XXX XXX Perhaps we should affect doors? * * XXX XXX XXX Bounds checking is broken - we can affect grids out of * view of the player, causing a crash... */ static bool project_f(int who, int r, int x, int y, int dam, int typ) { cave_type *c_ptr = area(x, y); bool obvious = FALSE; bool known = player_can_see_bold(x, y); /* XXX XXX XXX */ who = who ? who : 0; /* Reduce damage by distance */ dam = (dam + r) / (r + 1); /* Analyze the type */ switch (typ) { case GF_ACID: case GF_ELEC: case GF_FIRE: case GF_COLD: case GF_PLASMA: case GF_METEOR: case GF_ICE: case GF_SHARDS: case GF_FORCE: case GF_SOUND: case GF_MANA: case GF_HOLY_FIRE: case GF_HELL_FIRE: case GF_DISINTEGRATE: case GF_PSI: case GF_PSI_DRAIN: case GF_TELEKINESIS: case GF_DOMINATION: { /* Ignore most effects */ break; } case GF_KILL_DOOR: { /* Destroy Doors (and traps) */ /* Fields can block destruction */ if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) break; /* Destroy all open doors */ if ((c_ptr->feat == FEAT_OPEN) || (c_ptr->feat == FEAT_BROKEN) || (c_ptr->feat == FEAT_CLOSED)) { /* Check line of sight */ if (known) { /* Message */ msgf("There is a bright flash of light!"); obvious = TRUE; } /* Now is floor */ cave_set_feat(x, y, the_floor()); } break; } case GF_KILL_TRAP: { /* Destroy Traps (and Locks) */ /* Fields can block destruction */ if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) break; /* Reveal secret doors */ if (c_ptr->feat == FEAT_SECRET) { /* Pick a door */ create_closed_door(x, y); /* Check line of sight */ if (known) { obvious = TRUE; } } break; } case GF_JAM_DOOR: { /* Jams a door (as if with a spike) */ if (c_ptr->feat == FEAT_CLOSED) { make_lockjam_door(x, y, 1, TRUE); /* Check line of sight */ if (known) { /* Message */ msgf("The door seems stuck."); obvious = TRUE; } } break; } case GF_KILL_WALL: { /* Destroy walls (and doors) */ /* Non-walls (etc) */ if (cave_floor_grid(c_ptr)) break; /* Permanent walls */ if (cave_perma_grid(c_ptr)) break; /* Fields can block destruction */ if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) break; /* Terrain */ if (c_ptr->feat >= FEAT_TREES) { /* Message */ if (known) { if (c_ptr->feat == FEAT_JUNGLE) msgf("The jungle dissolves!"); else msgf("It disappears!"); obvious = TRUE; } /* Destroy the wall */ cave_set_feat(x, y, FEAT_DIRT); } /* Granite */ else if (c_ptr->feat >= FEAT_WALL_EXTRA) { /* Destroy the wall */ cave_set_feat(x, y, the_floor()); /* Message */ if (known) { msgf("The wall turns into mud!"); obvious = TRUE; } } /* Quartz / Magma with treasure */ else if (c_ptr->feat >= FEAT_MAGMA_K) { /* Destroy the wall */ cave_set_feat(x, y, the_floor()); /* Place some gold */ place_gold(x, y); /* Message */ if (known) { msgf("The vein turns into mud!"); msgf("You have found something!"); obvious = TRUE; } } /* Quartz / Magma */ else if (c_ptr->feat >= FEAT_MAGMA) { /* Destroy the wall */ cave_set_feat(x, y, the_floor()); /* Message */ if (known) { msgf("The vein turns into mud!"); obvious = TRUE; } } /* Rubble */ else if (c_ptr->feat == FEAT_RUBBLE) { /* Destroy the rubble */ cave_set_feat(x, y, the_floor()); /* Message */ if (known) { msgf("The rubble turns into mud!"); obvious = TRUE; } /* Hack -- place an object */ if (randint0(100) < 10) { /* Place gold */ place_object(x, y, FALSE, FALSE, 0); /* Found something */ if (known) { msgf("There was something buried in the rubble!"); obvious = TRUE; } } } /* Destroy doors (and secret doors) */ else if ((c_ptr->feat == FEAT_OPEN) || (c_ptr->feat == FEAT_SECRET) || (c_ptr->feat == FEAT_CLOSED)) { /* Destroy the feature */ cave_set_feat(x, y, the_floor()); /* Hack -- special message */ if (known) { msgf("The door turns into mud!"); obvious = TRUE; } } /* Pillar */ else if (c_ptr->feat == FEAT_PILLAR) { /* Destroy the pillar */ cave_set_feat(x, y, the_floor()); /* Message */ if (known) { msgf("The pillar turns into mud!"); obvious = TRUE; } } break; } case GF_MAKE_DOOR: { /* Make doors */ /* Require a "naked" floor grid */ if (!cave_naked_grid(c_ptr)) break; /* Not under the player */ if ((x == p_ptr->px) && (y == p_ptr->py)) break; /* Create a closed door */ cave_set_feat(x, y, FEAT_CLOSED); /* Observe */ if (known) { obvious = TRUE; } break; } case GF_MAKE_TRAP: { /* Make traps */ /* Require a "naked" floor grid */ if (!cave_naked_grid(c_ptr)) break; /* Place a trap */ place_trap(x, y); break; } case GF_MAKE_GLYPH: { /* Make a Glyph of Warding */ /* Require a "naked" floor grid */ if ((c_ptr->o_idx != 0) || (c_ptr->m_idx != 0)) break; /* Require a floor grid */ if (cave_wall_grid(c_ptr)) break; /* Add the glyph here as a field */ (void)place_field(x, y, FT_GLYPH_WARDING); /* Notice it */ note_spot(x, y); break; } case GF_STONE_WALL: { /* Require a "naked" floor grid */ if ((c_ptr->o_idx != 0) || (c_ptr->m_idx != 0)) break; if (!cave_floor_grid(c_ptr)) break; /* Not on permanent grids */ if (cave_perma_grid(c_ptr)) break; /* Place a wall */ cave_set_feat(x, y, FEAT_WALL_EXTRA); break; } case GF_LITE_WEAK: case GF_LITE: { /* Lite up the grid */ /* Turn on the light */ c_ptr->info |= (CAVE_GLOW); /* Notice + Redraw */ note_spot(x, y); /* Observe (after lighting) */ if (player_can_see_bold(x, y)) obvious = TRUE; /* Mega-Hack -- Update the monster in the affected grid */ /* This allows "spear of light" (etc) to work "correctly" */ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE); break; } case GF_DARK_WEAK: case GF_DARK: { /* Darken the grid */ /* Notice */ if (known) obvious = TRUE; /* Turn off the light. */ c_ptr->info &= ~(CAVE_GLOW); /* Notice + Redraw */ note_spot(x, y); /* Mega-Hack -- Update the monster in the affected grid */ /* This allows "spear of light" (etc) to work "correctly" */ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE); /* All done */ break; } } /* Return "Anything seen?" */ return (obvious); } /* * We are called from "project()" to "damage" objects * * We are called both for "beam" effects and "ball" effects. * * Perhaps we should only SOMETIMES damage things on the ground. * * The "r" parameter is the "distance from ground zero". * * Note that we determine if the player can "see" anything that happens * by taking into account: blindness, line-of-sight, and illumination. * * XXX XXX XXX We also "see" grids which are "memorized", probably a hack * * We return "TRUE" if the effect of the projection is "obvious". */ static bool project_o(int who, int r, int x, int y, int dam, int typ) { cave_type *c_ptr = area(x, y); bool obvious = FALSE; bool known = player_can_see_bold(x, y); int k_idx = 0; object_type *o_ptr; bool is_art; bool ignore; bool plural; bool do_kill; cptr note_kill = NULL; /* XXX XXX XXX */ who = who ? who : 0; /* Reduce damage by distance */ dam = (dam + r) / (r + 1); /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Reset the state */ is_art = FALSE; ignore = FALSE; plural = FALSE; do_kill = FALSE; /* Get the "plural"-ness */ if (o_ptr->number > 1) plural = TRUE; /* Check for artifact */ if (FLAG(o_ptr, TR_INSTA_ART)) is_art = TRUE; /* Analyze the type */ switch (typ) { case GF_ACID: { /* Acid -- Lots of things */ if (hates_acid(o_ptr)) { do_kill = TRUE; note_kill = (plural ? " melt!" : " melts!"); if (FLAG(o_ptr, TR_IGNORE_ACID)) ignore = TRUE; } break; } case GF_ELEC: { /* Elec -- Rings and Wands */ if (hates_elec(o_ptr)) { do_kill = TRUE; note_kill = (plural ? " are destroyed!" : " is destroyed!"); if (FLAG(o_ptr, TR_IGNORE_ELEC)) ignore = TRUE; } break; } case GF_FIRE: { /* Fire -- Flammable objects */ if (hates_fire(o_ptr)) { do_kill = TRUE; note_kill = (plural ? " burn up!" : " burns up!"); if (FLAG(o_ptr, TR_IGNORE_FIRE)) ignore = TRUE; } break; } case GF_COLD: { /* Cold -- potions and flasks */ if (hates_cold(o_ptr)) { note_kill = (plural ? " shatter!" : " shatters!"); do_kill = TRUE; if (FLAG(o_ptr, TR_IGNORE_COLD)) ignore = TRUE; } break; } case GF_PLASMA: { /* Fire + Elec */ if (hates_fire(o_ptr)) { do_kill = TRUE; note_kill = (plural ? " burn up!" : " burns up!"); if (FLAG(o_ptr, TR_IGNORE_FIRE)) ignore = TRUE; } if (hates_elec(o_ptr)) { ignore = FALSE; do_kill = TRUE; note_kill = (plural ? " are destroyed!" : " is destroyed!"); if (FLAG(o_ptr, TR_IGNORE_ELEC)) ignore = TRUE; } break; } case GF_METEOR: { /* Fire + Cold */ if (hates_fire(o_ptr)) { do_kill = TRUE; note_kill = (plural ? " burn up!" : " burns up!"); if (FLAG(o_ptr, TR_IGNORE_FIRE)) ignore = TRUE; } if (hates_cold(o_ptr)) { ignore = FALSE; do_kill = TRUE; note_kill = (plural ? " shatter!" : " shatters!"); if (FLAG(o_ptr, TR_IGNORE_COLD)) ignore = TRUE; } break; } case GF_ICE: case GF_SHARDS: case GF_FORCE: case GF_SOUND: { /* Hack -- break potions and such */ if (hates_cold(o_ptr)) { note_kill = (plural ? " shatter!" : " shatters!"); do_kill = TRUE; } break; } case GF_MANA: { /* Mana and Chaos -- destroy everything */ do_kill = TRUE; note_kill = (plural ? " are destroyed!" : " is destroyed!"); break; } case GF_DISINTEGRATE: { /* Disintegration -- destroy everything */ do_kill = TRUE; note_kill = (plural ? " evaporate!" : " evaporates!"); break; } case GF_CHAOS: { do_kill = TRUE; note_kill = (plural ? " are destroyed!" : " is destroyed!"); if (FLAG(o_ptr, TR_RES_CHAOS)) ignore = TRUE; break; } case GF_HOLY_FIRE: case GF_HELL_FIRE: { /* Holy Fire and Hell Fire -- destroys cursed non-artifacts */ if (cursed_p(o_ptr)) { do_kill = TRUE; note_kill = (plural ? " are destroyed!" : " is destroyed!"); } break; } case GF_KILL_TRAP: case GF_KILL_DOOR: { /* Unlock chests */ /* Chests are noticed only if trapped or locked */ if (o_ptr->tval == TV_CHEST) { /* Disarm/Unlock traps */ if (o_ptr->pval > 0) { /* Disarm or Unlock */ o_ptr->pval = (0 - o_ptr->pval); /* Identify */ object_known(o_ptr); /* Notice */ if (known && (o_ptr->info & OB_SEEN)) { msgf("Click!"); obvious = TRUE; } } } break; } } /* Attempt to destroy the object */ if (do_kill) { /* Effect "observed" */ if (known && (o_ptr->info & OB_SEEN)) { obvious = TRUE; } /* Artifacts, and other objects, get to resist */ if (is_art || ignore) { /* Observe the resist */ if (obvious) { msgf("The %v %s unaffected!", OBJECT_FMT(o_ptr, FALSE, 0), (plural ? "are" : "is")); } } /* Kill it */ else { bool is_potion = FALSE; object_type *j_ptr; /* Describe if needed */ if (obvious && note_kill) { msgf("The %v%s", OBJECT_FMT(o_ptr, FALSE, 0), note_kill); } k_idx = o_ptr->k_idx; is_potion = object_is_potion(o_ptr); /* Delete the object, but keep a temp copy */ j_ptr = object_dup(o_ptr); delete_dungeon_object(o_ptr); /* Potions produce effects when 'shattered' */ if (is_potion) { (void)potion_smash_effect(who, x, y, j_ptr); } /* Redraw */ lite_spot(x, y); } } } OBJ_ITT_END; /* Return "Anything seen?" */ return (obvious); } /* * Helper function for "project()" below. * * Handle a beam/bolt/ball causing damage to a monster. * * This routine takes a "source monster" (by index) which is mostly used to * determine if the player is causing the damage, and a "radius" (see below), * which is used to decrease the power of explosions with distance, and a * location, via integers which are modified by certain types of attacks * (polymorph and teleport being the obvious ones), a default damage, which * is modified as needed based on various properties, and finally a "damage * type" (see below). * * Note that this routine can handle "no damage" attacks (like teleport) by * taking a "zero" damage, and can even take "parameters" to attacks (like * confuse) by accepting a "damage", using it to calculate the effect, and * then setting the damage to zero. Note that the "damage" parameter is * divided by the radius, so monsters not at the "epicenter" will not take * as much damage (or whatever)... * * Note that "polymorph" is dangerous, since a failure in "place_monster()"' * may result in a dereference of an invalid pointer. XXX XXX XXX * * Various messages are produced, and damage is applied. * * Just "casting" a substance (i.e. plasma) does not make you immune, you must * actually be "made" of that substance, or "breathe" big balls of it. * * We assume that "Plasma" monsters, and "Plasma" breathers, are immune * to plasma. * * We assume "Nether" is an evil, necromantic force, so it doesn't hurt undead, * and hurts evil less. If can breath nether, then it resists it as well. * * Damage reductions use the following formulas: * Note that "dam = dam * 6 / (randint1(6) + 6);" * gives avg damage of .655, ranging from .858 to .500 * Note that "dam = dam * 5 / (randint1(6) + 6);" * gives avg damage of .544, ranging from .714 to .417 * Note that "dam = dam * 4 / (randint1(6) + 6);" * gives avg damage of .444, ranging from .556 to .333 * Note that "dam = dam * 3 / (randint1(6) + 6);" * gives avg damage of .327, ranging from .427 to .250 * Note that "dam = dam * 2 / (randint1(6) + 6);" * gives something simple. * * In this function, "result" messages are postponed until the end, where * the "note" string is appended to the monster name, if not NULL. So, * to make a spell have "no effect" just set "note" to NULL. You should * also set "notice" to FALSE, or the player will learn what the spell does. * * We attempt to return "TRUE" if the player saw anything "useful" happen. */ static bool project_m(int who, int r, int x, int y, int dam, int typ) { int tmp; cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; char killer[80]; cptr name = mon_race_name(r_ptr); /* Is the monster "seen"? */ bool seen = m_ptr->ml; /* Were the effects "obvious" (if seen)? */ bool obvious = FALSE; /* Can the player know about this effect? */ bool known = (m_ptr->cdis <= MAX_SIGHT); /* Can the player see the source of this effect? */ bool see_s = ((who <= 0) || m_list[who].ml); /* Were the effects "irrelevant"? */ bool skipped = FALSE; /* Gets the monster angry at the source of the effect? */ bool get_angry = FALSE; /* Polymorph setting (true or false) */ int do_poly = 0; /* Teleport setting (max distance) */ int do_dist = 0; /* Confusion setting (amount to confuse) */ int do_conf = 0; /* Stunning setting (amount to stun) */ int do_stun = 0; /* Sleep amount (amount to sleep) */ int do_sleep = 0; /* Fear amount (amount to fear) */ int do_fear = 0; bool heal_leper = FALSE; /* Hold the monster name */ char m_name[80]; /* Assume no note */ cptr note = NULL; /* Assume a default death */ cptr note_dies = " dies."; /* Nobody here */ if (!c_ptr->m_idx) return (FALSE); /* Never affect projector */ if (who && (c_ptr->m_idx == who)) return (FALSE); /* * Don't affect already dead monsters * This prevents problems with chain reactions of exploding monsters */ if (m_ptr->hp < 0) return (FALSE); /* Reduce damage by distance */ dam = (dam + r) / (r + 1); /* Get the monster name (BEFORE polymorphing) */ monster_desc(m_name, m_ptr, 0, 80); /* Some monsters get "destroyed" */ if (!monster_living(r_ptr)) { /* Special note at death */ note_dies = " is destroyed."; } /* Analyze the damage type */ switch (typ) { case GF_MISSILE: { /* Magic Missile -- pure damage */ if (seen) obvious = TRUE; break; } case GF_ACID: { /* Acid */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_IM_ACID)) { note = " resists a lot."; dam /= 9; if (seen) r_ptr->r_flags[2] |= (RF2_IM_ACID); } break; } case GF_ELEC: { /* Electricity */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_IM_ELEC)) { note = " resists a lot."; dam /= 9; if (seen) r_ptr->r_flags[2] |= (RF2_IM_ELEC); } break; } case GF_FIRE: { /* Fire damage */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_IM_FIRE)) { note = " resists a lot."; dam /= 9; if (seen) r_ptr->r_flags[2] |= (RF2_IM_FIRE); } break; } case GF_COLD: { /* Cold */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_IM_COLD)) { note = " resists a lot."; dam /= 9; if (seen) r_ptr->r_flags[2] |= (RF2_IM_COLD); } break; } case GF_POIS: { /* Poison */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_IM_POIS)) { note = " resists a lot."; dam /= 9; if (seen) r_ptr->r_flags[2] |= (RF2_IM_POIS); } break; } case GF_NUKE: { /* Nuclear waste */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_IM_POIS)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); if (seen) r_ptr->r_flags[2] |= (RF2_IM_POIS); } else if (one_in_(3)) do_poly = TRUE; break; } case GF_HELL_FIRE: { /* Hellfire -- hurts Evil */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_EVIL)) { dam *= 2; note = " is hit hard."; if (seen) r_ptr->r_flags[2] |= (RF2_EVIL); } break; } case GF_HOLY_FIRE: { /* Holy Fire -- hurts Evil, Good are immune, others _resist_ */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_GOOD)) { dam = 0; note = " is immune."; if (seen) r_ptr->r_flags[2] |= RF2_GOOD; } else if (FLAG(r_ptr, RF_EVIL)) { dam *= 2; note = " is hit hard."; if (seen) r_ptr->r_flags[2] |= RF2_EVIL; } else { note = " resists."; dam *= 3; dam /= rand_range(7, 12); } break; } case GF_ARROW: { /* Arrow -- XXX no defense */ if (seen) obvious = TRUE; break; } case GF_PLASMA: { /* Plasma -- XXX perhaps check ELEC or FIRE */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_RES_PLAS)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); if (seen) r_ptr->r_flags[2] |= (RF2_RES_PLAS); } break; } case GF_NETHER: { /* Nether -- see above */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_UNDEAD)) { note = " is immune."; dam = 0; if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); } else if (FLAG(r_ptr, RF_RES_NETH)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); if (seen) r_ptr->r_flags[2] |= (RF2_RES_NETH); } else if (FLAG(r_ptr, RF_EVIL)) { dam /= 2; note = " resists somewhat."; if (seen) r_ptr->r_flags[2] |= (RF2_EVIL); } break; } case GF_WATER: { /* Water (acid) damage -- Water spirits/elementals are immune */ if (seen) obvious = TRUE; if ((r_ptr->d_char == 'E') && (prefix(name, "W") || (mon_name_cont(r_ptr, "Unmaker")))) { note = " is immune."; dam = 0; } else if (FLAG(r_ptr, RF_RES_WATE)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); if (seen) r_ptr->r_flags[2] |= (RF2_RES_WATE); } break; } case GF_CHAOS: { /* Chaos -- Chaos breathers resist */ if (seen) obvious = TRUE; do_poly = TRUE; do_conf = (rand_range(5, 16) + r) / (r + 1); if ((FLAG(r_ptr, RF_BR_CHAO)) || ((FLAG(r_ptr, RF_DEMON)) && one_in_(3))) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); do_poly = FALSE; } break; } case GF_SHARDS: { /* Shards -- Shard breathers resist */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_BR_SHAR)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); } break; } case GF_ROCKET: { /* Rocket: Shard resistance helps */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_BR_SHAR)) { note = " resists somewhat."; dam /= 2; } break; } case GF_SOUND: { /* Sound -- Sound breathers resist */ if (seen) obvious = TRUE; do_stun = (rand_range(10, 25) + r) / (r + 1); if (FLAG(r_ptr, RF_BR_SOUN)) { note = " resists."; dam *= 2; dam /= rand_range(7, 12); } break; } case GF_CONFUSION: { /* Confusion */ if (seen) obvious = TRUE; do_conf = (rand_range(10, 25) + r) / (r + 1); if (FLAG(r_ptr, RF_BR_CONF)) { note = " resists."; dam *= 2; dam /= rand_range(7, 12); } else if (FLAG(r_ptr, RF_NO_CONF)) { note = " resists somewhat."; dam /= 2; } break; } case GF_DISENCHANT: { /* Disenchantment -- Breathers and Disenchanters resist */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_RES_DISE)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); if (seen) r_ptr->r_flags[2] |= (RF2_RES_DISE); } break; } case GF_NEXUS: { /* Nexus -- Breathers and Existers resist */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_RES_NEXU)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); if (seen) r_ptr->r_flags[2] |= (RF2_RES_NEXU); } break; } case GF_FORCE: { /* Force */ if (seen) obvious = TRUE; do_stun = (randint1(15) + r) / (r + 1); if (FLAG(r_ptr, RF_BR_WALL)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); } break; } case GF_INERTIA: { /* Inertia -- breathers resist */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_BR_INER)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); } else { /* Powerful monsters can resist */ if ((FLAG(r_ptr, RF_UNIQUE)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { obvious = FALSE; } /* Normal monsters slow down */ else { if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10; note = " starts moving slower."; } } break; } case GF_TIME: { /* Time -- breathers resist */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_BR_TIME)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); } break; } case GF_GRAVITY: { /* Gravity -- breathers resist */ bool resist_tele = FALSE; if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_RES_TELE)) { if (FLAG(r_ptr, RF_UNIQUE)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " is unaffected!"; resist_tele = TRUE; } else if (r_ptr->hdice * 2 > randint1(150)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " resists!"; resist_tele = TRUE; } } if (!resist_tele) do_dist = 10; else do_dist = 0; if (FLAG(r_ptr, RF_BR_GRAV)) { note = " resists."; dam *= 3; dam /= rand_range(7, 12); do_dist = 0; } else { /* 1. slowness */ /* Powerful monsters can resist */ if ((FLAG(r_ptr, RF_UNIQUE)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { obvious = FALSE; } /* Normal monsters slow down */ else { if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10; note = " starts moving slower."; } /* 2. stun */ do_stun = damroll((p_ptr->lev / 10) + 3, (dam)) + 1; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Resist */ do_stun = 0; /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } } break; } case GF_MANA: { /* Pure damage */ if (seen) obvious = TRUE; break; } case GF_DISINTEGRATE: { /* Pure damage */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_HURT_ROCK)) { if (seen) r_ptr->r_flags[2] |= (RF2_HURT_ROCK); note = " loses some skin!"; note_dies = " evaporates!"; dam *= 2; } if (FLAG(r_ptr, RF_UNIQUE)) { if (r_ptr->hdice * 2 > randint0(p_ptr->lev * 3)) { note = " resists."; dam /= 8; } } break; } case GF_PSI: { if (seen) obvious = TRUE; /* PSI only works if the monster can see you! -- RG */ if (!player_has_los_grid(pc_ptr)) { dam = 0; note = " can't see you, and isn't affected!"; } if (FLAG(r_ptr, RF_EMPTY_MIND)) { dam = 0; note = " is immune!"; /* Memorize a flag */ if (seen) r_ptr->r_flags[1] |= (RF1_EMPTY_MIND); } else if ((FLAG(r_ptr, RF_STUPID)) || (FLAG(r_ptr, RF_WEIRD_MIND)) || (FLAG(r_ptr, RF_ANIMAL)) || (r_ptr->hdice * 2 > randint1(6 * dam))) { dam /= 3; note = " resists."; /* * Powerful demons & undead can turn a mindcrafter's * attacks back on them */ if (((FLAG(r_ptr, RF_UNDEAD)) || (FLAG(r_ptr, RF_DEMON))) && (r_ptr->hdice * 2 > p_ptr->lev) && one_in_(2)) { note = NULL; msgf("%^s%s corrupted mind backlashes your attack!", m_name, (seen ? "'s" : "s")); /* Saving throw */ if (player_save(r_ptr->hdice * 2)) { msgf("You resist the effects!"); } else { /* Injure +/- confusion */ monster_desc(killer, m_ptr, 0x88, 80); take_hit(dam, killer); /* has already been /3 */ if (one_in_(4)) { switch (randint1(4)) { case 1: (void)inc_confused(3 + randint1(dam)); break; case 2: (void)inc_stun(randint1(dam)); break; case 3: { if (FLAG(r_ptr, RF_NO_FEAR)) note = " is unaffected."; else (void)inc_afraid(3 + randint1(dam)); break; } default: if (!(FLAG(p_ptr, TR_FREE_ACT))) (void)inc_paralyzed(randint1(dam)); break; } } } dam = 0; } } if ((dam > 0) && one_in_(4)) { switch (randint1(4)) { case 1: do_conf = 3 + randint1(dam); break; case 2: do_stun = 3 + randint1(dam); break; case 3: do_fear = 3 + randint1(dam); break; default: note = " falls asleep!"; do_sleep = 3 + randint1(dam); break; } } note_dies = " collapses, a mindless husk."; break; } case GF_PSI_DRAIN: { if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_EMPTY_MIND)) { dam = 0; note = " is immune!"; /* Memorize a flag */ if (seen) r_ptr->r_flags[1] |= (RF1_EMPTY_MIND); } else if ((FLAG(r_ptr, RF_STUPID)) || (FLAG(r_ptr, RF_WEIRD_MIND)) || (FLAG(r_ptr, RF_ANIMAL)) || (r_ptr->hdice * 2 > randint1(6 * dam))) { dam /= 3; note = " resists."; /* * Powerful demons & undead can turn a mindcrafter's * attacks back on them */ if (((FLAG(r_ptr, RF_UNDEAD)) || (FLAG(r_ptr, RF_DEMON))) && (r_ptr->hdice * 2 > p_ptr->lev) && one_in_(2)) { note = NULL; msgf("%^s%s corrupted mind backlashes your attack!", m_name, (seen ? "'s" : "s")); /* Saving throw */ if (player_save(r_ptr->hdice * 2)) { msgf("You resist the effects!"); } else { /* Injure + mana drain */ monster_desc(killer, m_ptr, 0x88, 80); msgf("Your psychic energy is drained!"); p_ptr->csp = MAX(0, p_ptr->csp - damroll(5, dam) / 2); p_ptr->redraw |= PR_MANA; p_ptr->window |= (PW_SPELL); take_hit(dam, killer); /* has already been /3 */ } dam = 0; } } else if (dam > 0) { int b = damroll(5, dam) / 4; msgf("You convert %s%s pain into psychic energy!", m_name, (seen ? "'s" : "s")); b = MIN(p_ptr->msp, p_ptr->csp + b); p_ptr->csp = b; p_ptr->redraw |= PR_MANA; p_ptr->window |= (PW_SPELL); } note_dies = " collapses, a mindless husk."; break; } case GF_TELEKINESIS: { if (seen) obvious = TRUE; do_dist = 7; /* 1. stun */ do_stun = damroll((p_ptr->lev / 10) + 3, dam) + 1; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (r_ptr->hdice * 2 > randint1(dam) * 2)) { /* Resist */ do_stun = 0; /* No obvious effect */ obvious = FALSE; } break; } case GF_METEOR: { /* Meteor -- powerful magic missile */ if (seen) obvious = TRUE; break; } case GF_DOMINATION: { if (!is_hostile(m_ptr)) break; if (seen) obvious = TRUE; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR)) || (FLAG(r_ptr, RF_NO_CONF)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Memorize a flag */ if (FLAG(r_ptr, RF_NO_CONF)) { if (seen) r_ptr->r_flags[2] |= (RF2_NO_CONF); } /* Resist */ do_conf = 0; /* * Powerful demons & undead can turn a mindcrafter's * attacks back on them */ if (((FLAG(r_ptr, RF_UNDEAD)) || (FLAG(r_ptr, RF_DEMON))) && (r_ptr->hdice * 2 > p_ptr->lev) && one_in_(2)) { note = NULL; msgf("%^s%s corrupted mind backlashes your attack!", m_name, (seen ? "'s" : "s")); /* Saving throw */ if (player_save(r_ptr->hdice * 2)) { msgf("You resist the effects!"); } else { /* Confuse, stun, terrify */ switch (randint1(4)) { case 1: (void)inc_stun(dam / 2); break; case 2: (void)inc_confused(dam / 2); break; default: { if (FLAG(r_ptr, RF_NO_FEAR)) note = " is unaffected."; else (void)inc_afraid(dam); } } } } else { /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } } else { if ((dam > 29) && (randint1(100) < dam)) { note = " is in your thrall!"; set_pet(m_ptr); } else { switch (randint1(4)) { case 1: do_stun = dam / 2; break; case 2: do_conf = dam / 2; break; default: do_fear = dam; } } } /* No "real" damage */ dam = 0; break; } case GF_ICE: { /* Ice -- Cold + Cuts + Stun */ if (seen) obvious = TRUE; do_stun = rand_range(2, 16) / (r + 1); if (FLAG(r_ptr, RF_IM_COLD)) { note = " resists a lot."; dam /= 9; if (seen) r_ptr->r_flags[2] |= (RF2_IM_COLD); } break; } case GF_OLD_DRAIN: { /* Drain Life */ if (seen) obvious = TRUE; if (!monster_living(r_ptr)) { if (FLAG(r_ptr, RF_UNDEAD)) { if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); } if (FLAG(r_ptr, RF_DEMON)) { if (seen) r_ptr->r_flags[2] |= (RF2_DEMON); } note = " is unaffected!"; obvious = FALSE; dam = 0; } break; } case GF_NEW_DRAIN: { /* Drain Life + give it to the player */ if (seen) obvious = TRUE; if (!monster_living(r_ptr)) { if (FLAG(r_ptr, RF_UNDEAD)) { if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); } if (FLAG(r_ptr, RF_DEMON)) { if (seen) r_ptr->r_flags[2] |= (RF2_DEMON); } note = " is unaffected!"; obvious = FALSE; dam = 0; } else { u16b hp = dam; /* Cannot drain more than monsters life */ if (m_ptr->hp < dam) hp = m_ptr->hp; /* Cannot drain more than 100hp at a time */ if (hp > 100) hp = 100; /* Give the player the hit points */ (void)hp_player(hp); } break; } case GF_DEATH_RAY: { /* Death Ray */ if (seen) obvious = TRUE; if ((FLAG(r_ptr, RF_UNDEAD)) || (FLAG(r_ptr, RF_NONLIVING))) { if (FLAG(r_ptr, RF_UNDEAD)) { if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); } note = " is immune."; obvious = FALSE; dam = 0; } else if (((FLAG(r_ptr, RF_UNIQUE)) && !one_in_(666)) || (r_ptr->hdice * 2 > randint1(dam / 30))) { note = " resists!"; obvious = FALSE; dam = 0; } break; } case GF_OLD_POLY: { /* Polymorph monster (Use "dam" as "power") */ if (seen) obvious = TRUE; /* Attempt to polymorph (see below) */ do_poly = TRUE; /* Powerful monsters can resist */ if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { note = " is unaffected!"; do_poly = FALSE; obvious = FALSE; } /* No "real" damage */ dam = 0; break; } case GF_OLD_CLONE: { /* Clone monsters (Ignore "dam") */ bool friendly = FALSE; bool pet = FALSE; if (seen) obvious = TRUE; if (is_friendly(m_ptr) && !one_in_(3)) friendly = TRUE; if (is_pet(m_ptr) && !one_in_(3)) pet = TRUE; /* Heal fully */ m_ptr->hp = m_ptr->maxhp; /* Speed up */ if (m_ptr->mspeed < 150) m_ptr->mspeed += 10; /* Attempt to clone. */ if (multiply_monster(c_ptr->m_idx, TRUE, friendly, pet)) { note = " spawns!"; } /* No "real" damage */ dam = 0; break; } case GF_OLD_HEAL: { /* Heal Monster (use "dam" as amount of healing) */ if (seen) obvious = TRUE; /* Wake up */ m_ptr->csleep = 0; /* Heal */ m_ptr->hp += dam; /* No overflow */ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; chg_virtue(V_VITALITY, 1); if (FLAG(r_ptr, RF_UNIQUE)) chg_virtue(V_INDIVIDUALISM, 1); if (is_friendly(m_ptr)) chg_virtue(V_HONOUR, 1); else if (!(FLAG(r_ptr, RF_EVIL))) { if (FLAG(r_ptr, RF_GOOD)) chg_virtue(V_COMPASSION, 2); else chg_virtue(V_COMPASSION, 1); } if (mon_name_cont(r_ptr, "leper")) { heal_leper = TRUE; chg_virtue(V_COMPASSION, 5); } if (FLAG(r_ptr, RF_ANIMAL)) chg_virtue(V_NATURE, 1); /* Redraw (later) if needed */ if (p_ptr->health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH); /* Message */ note = " looks healthier."; /* No "real" damage */ dam = 0; break; } case GF_OLD_SPEED: { /* Speed Monster (Ignore "dam") */ if (seen) obvious = TRUE; /* Speed up */ if (m_ptr->mspeed < 150) { m_ptr->mspeed += (40 - m_ptr->mspeed + r_ptr->speed) / 4; } note = " starts moving faster."; if (FLAG(r_ptr, RF_UNIQUE)) chg_virtue(V_INDIVIDUALISM, 1); if (is_friendly(m_ptr)) chg_virtue(V_HONOUR, 1); /* No "real" damage */ dam = 0; break; } case GF_OLD_SLOW: { /* Slow Monster (Use "dam" as "power") */ if (seen) obvious = TRUE; /* Powerful monsters can resist */ if ((FLAG(r_ptr, RF_UNIQUE)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { note = " is unaffected!"; obvious = FALSE; } /* Normal monsters slow down */ else { if (m_ptr->mspeed > 60) { m_ptr->mspeed -= (40 + m_ptr->mspeed - r_ptr->speed) / 4; } note = " starts moving slower."; } /* No "real" damage */ dam = 0; break; } case GF_OLD_SLEEP: { /* Sleep (Use "dam" as "power") */ if (seen) obvious = TRUE; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_NO_SLEEP)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Memorize a flag */ if (FLAG(r_ptr, RF_NO_SLEEP)) { if (seen) r_ptr->r_flags[2] |= (RF2_NO_SLEEP); } /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } else { /* Go to sleep (much) later */ note = " falls asleep!"; do_sleep = 500; } /* No "real" damage */ dam = 0; break; } case GF_STASIS: { /* Sleep (Use "dam" as "power") */ if (seen) obvious = TRUE; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (r_ptr->hdice * 2 > randint1(dam * 4))) { note = " is unaffected!"; obvious = FALSE; } else { /* Go to sleep (much) later */ note = " is suspended!"; do_sleep = 500; } /* No "real" damage */ dam = 0; break; } case GF_CHARM: { /* Charm monster */ dam += (adj_con_fix[p_ptr->stat[A_CHR].ind] - 1); if (seen) obvious = TRUE; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR)) || (FLAG(r_ptr, RF_NO_CONF)) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Memorize a flag */ if (FLAG(r_ptr, RF_NO_CONF)) { if (seen) r_ptr->r_flags[2] |= (RF2_NO_CONF); } /* Resist */ /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } else if (FLAG(p_ptr, TR_AGGRAVATE)) { note = " hates you too much!"; } else { note = " suddenly seems friendly!"; set_pet(m_ptr); chg_virtue(V_INDIVIDUALISM, -1); if (FLAG(r_ptr, RF_ANIMAL)) chg_virtue(V_NATURE, 1); } /* No "real" damage */ dam = 0; break; } case GF_CONTROL_UNDEAD: { /* Control undead */ if (seen) obvious = TRUE; /* Attempt a saving throw */ if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR)) || (!(FLAG(r_ptr, RF_UNDEAD))) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } else if (FLAG(p_ptr, TR_AGGRAVATE)) { note = " hates you too much!"; } else { note = " is in your thrall!"; set_pet(m_ptr); } /* No "real" damage */ dam = 0; break; } case GF_CONTROL_ANIMAL: { /* Tame animal */ if (seen) obvious = TRUE; /* Attempt a saving throw */ if (FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_QUESTOR) || !FLAG(r_ptr, RF_ANIMAL) || FLAG(r_ptr, RF_NO_CONF) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Memorize a flag */ if (FLAG(r_ptr, RF_NO_CONF)) { if (seen) r_ptr->r_flags[2] |= (RF2_NO_CONF); } /* Resist */ /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } else if (FLAG(p_ptr, TR_AGGRAVATE)) { note = " hates you too much!"; } else { note = " is tamed!"; set_pet(m_ptr); if (FLAG(r_ptr, RF_ANIMAL)) chg_virtue(V_NATURE, 1); } /* No "real" damage */ dam = 0; break; } case GF_OLD_CONF: { /* Confusion (Use "dam" as "power") */ if (seen) obvious = TRUE; /* Get confused later */ do_conf = damroll(3, (dam / 2)) + 1; /* Attempt a saving throw */ if (FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_NO_CONF) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Memorize a flag */ if (FLAG(r_ptr, RF_NO_CONF)) { if (seen) r_ptr->r_flags[2] |= (RF2_NO_CONF); } /* Resist */ do_conf = 0; /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } /* No "real" damage */ dam = 0; break; } case GF_STUN: { if (seen) obvious = TRUE; do_stun = damroll((p_ptr->lev / 10) + 3, (dam)) + 1; /* Attempt a saving throw */ if (FLAG(r_ptr, RF_UNIQUE) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* Resist */ do_stun = 0; /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; } /* No "real" damage */ dam = 0; break; } case GF_LITE_WEAK: { /* Lite, but only hurts susceptible creatures */ /* Hurt by light */ if (FLAG(r_ptr, RF_HURT_LITE)) { /* Obvious effect */ if (seen) obvious = TRUE; /* Memorize the effects */ if (seen) r_ptr->r_flags[2] |= (RF2_HURT_LITE); /* Special effect */ note = " cringes from the light!"; note_dies = " shrivels away in the light!"; } /* Normally no damage */ else { /* No damage */ dam = 0; } break; } case GF_LITE: { /* Lite -- opposite of Dark */ if (seen) obvious = TRUE; if (FLAG(r_ptr, RF_BR_LITE)) { note = " resists."; dam *= 2; dam /= (rand_range(7, 12)); } else if (FLAG(r_ptr, RF_HURT_LITE)) { if (seen) r_ptr->r_flags[2] |= (RF2_HURT_LITE); note = " cringes from the light!"; note_dies = " shrivels away in the light!"; dam *= 2; } break; } case GF_DARK: { /* Dark -- opposite of Lite */ if (seen) obvious = TRUE; /* Likes darkness... */ if (FLAG(r_ptr, RF_BR_DARK) || FLAG(r_ptr, RF_ORC) || FLAG(r_ptr, RF_HURT_LITE)) { note = " resists."; dam *= 2; dam /= (rand_range(7, 12)); } break; } case GF_KILL_WALL: { /* Stone to Mud */ /* Hurt by rock remover */ if (FLAG(r_ptr, RF_HURT_ROCK)) { /* Notice effect */ if (seen) obvious = TRUE; /* Memorize the effects */ if (seen) r_ptr->r_flags[2] |= (RF2_HURT_ROCK); /* Cute little message */ note = " loses some skin!"; note_dies = " dissolves!"; } /* Usually, ignore the effects */ else { /* No damage */ dam = 0; } break; } case GF_AWAY_UNDEAD: { /* Teleport undead (Use "dam" as "power") */ /* Only affect undead */ if (FLAG(r_ptr, RF_UNDEAD)) { bool resists_tele = FALSE; if (FLAG(r_ptr, RF_RES_TELE)) { if (FLAG(r_ptr, RF_UNIQUE)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " is unaffected!"; resists_tele = TRUE; } else if (r_ptr->hdice * 2 > randint1(150)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " resists!"; resists_tele = TRUE; } } if (!resists_tele) { if (seen) obvious = TRUE; if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); do_dist = dam; } } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; } /* No "real" damage */ dam = 0; break; } case GF_AWAY_EVIL: { /* Teleport evil (Use "dam" as "power") */ /* Only affect evil */ if (FLAG(r_ptr, RF_EVIL)) { bool resists_tele = FALSE; if (FLAG(r_ptr, RF_RES_TELE)) { if (FLAG(r_ptr, RF_UNIQUE)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " is unaffected!"; resists_tele = TRUE; } else if (r_ptr->hdice * 2 > randint1(150)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " resists!"; resists_tele = TRUE; } } if (!resists_tele) { if (seen) obvious = TRUE; if (seen) r_ptr->r_flags[2] |= (RF2_EVIL); do_dist = dam; } } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; } /* No "real" damage */ dam = 0; break; } case GF_AWAY_ALL: { /* Teleport monster (Use "dam" as "power") */ bool resists_tele = FALSE; if (FLAG(r_ptr, RF_RES_TELE)) { if (FLAG(r_ptr, RF_UNIQUE)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " is unaffected!"; resists_tele = TRUE; } else if (r_ptr->hdice * 2 > randint1(150)) { if (seen) r_ptr->r_flags[2] |= RF2_RES_TELE; note = " resists!"; resists_tele = TRUE; } } if (!resists_tele) { /* Obvious */ if (seen) obvious = TRUE; /* Prepare to teleport */ do_dist = dam; } /* No "real" damage */ dam = 0; break; } case GF_TURN_UNDEAD: { /* Turn undead (Use "dam" as "power") */ /* Only affect undead */ if (FLAG(r_ptr, RF_UNDEAD)) { /* Learn about type */ if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); /* Obvious */ if (seen) obvious = TRUE; /* Apply some fear */ do_fear = damroll(3, (dam / 2)) + 1; /* Attempt a saving throw */ if (r_ptr->hdice * 2 > randint1(dam * 3)) { /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; do_fear = 0; } } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; } /* No "real" damage */ dam = 0; break; } case GF_TURN_EVIL: { /* Turn evil (Use "dam" as "power") */ /* Only affect evil */ if (FLAG(r_ptr, RF_EVIL)) { /* Learn about type */ if (seen) r_ptr->r_flags[2] |= (RF2_EVIL); /* Obvious */ if (seen) obvious = TRUE; /* Apply some fear */ do_fear = damroll(3, (dam / 2)) + 1; /* Attempt a saving throw */ if (r_ptr->hdice * 2 > randint1(dam * 3)) { /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; do_fear = 0; } } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; } /* No "real" damage */ dam = 0; break; } case GF_TURN_ALL: { /* Turn monster (Use "dam" as "power") */ /* Obvious */ if (seen) obvious = TRUE; /* Apply some fear */ do_fear = damroll(3, (dam / 2)) + 1; /* Attempt a saving throw */ if (FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_NO_FEAR) || (r_ptr->hdice * 2 > randint1(dam * 3))) { /* No obvious effect */ note = " is unaffected!"; obvious = FALSE; do_fear = 0; } /* No "real" damage */ dam = 0; break; } case GF_DISP_UNDEAD: { /* Dispel undead */ /* Only affect undead */ if (FLAG(r_ptr, RF_UNDEAD)) { /* Learn about type */ if (seen) r_ptr->r_flags[2] |= (RF2_UNDEAD); /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " shudders."; note_dies = " dissolves!"; } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; /* No damage */ dam = 0; } break; } case GF_DISP_EVIL: { /* Dispel evil */ /* Only affect evil */ if (FLAG(r_ptr, RF_EVIL)) { /* Learn about type */ if (seen) r_ptr->r_flags[2] |= (RF2_EVIL); /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " shudders."; note_dies = " dissolves!"; } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; /* No damage */ dam = 0; } break; } case GF_DISP_GOOD: { /* Dispel good */ /* Only affect good */ if (FLAG(r_ptr, RF_GOOD)) { /* Learn about type */ if (seen) r_ptr->r_flags[2] |= (RF2_GOOD); /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " shudders."; note_dies = " dissolves!"; } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; /* No damage */ dam = 0; } break; } case GF_DISP_LIVING: { /* Dispel living */ /* Only affect non-undead */ if (monster_living(r_ptr)) { /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " shudders."; note_dies = " dissolves!"; } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; /* No damage */ dam = 0; } break; } case GF_DISP_DEMON: { /* Dispel demons */ /* Only affect demons */ if (FLAG(r_ptr, RF_DEMON)) { /* Learn about type */ if (seen) r_ptr->r_flags[2] |= (RF2_DEMON); /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " shudders."; note_dies = " dissolves!"; } /* Others ignore */ else { /* Irrelevant */ skipped = TRUE; /* No damage */ dam = 0; } break; } case GF_DISP_ALL: { /* Dispel monster */ /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " shudders."; note_dies = " dissolves!"; break; } default: { /* Default */ /* Irrelevant */ skipped = TRUE; /* No damage */ dam = 0; break; } } /* Absolutely no effect */ if (skipped) return (FALSE); /* "Unique" monsters cannot be polymorphed */ if (FLAG(r_ptr, RF_UNIQUE)) do_poly = FALSE; /* Quest monsters cannot be polymorphed */ if (FLAG(r_ptr, RF_QUESTOR)) do_poly = FALSE; /* "Unique" and "quest" monsters can only be "killed" by the player. */ if ((FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR)) || (FLAG(r_ptr, RF_UNIQUE_7))) { if (who && (dam > m_ptr->hp)) dam = m_ptr->hp; } /* Modify the damage */ dam = mon_damage_mod(m_ptr, dam, 0); /* Check for death */ if (dam > m_ptr->hp) { /* Extract method of death */ note = note_dies; } /* Mega-Hack -- Handle "polymorph" -- monsters get a saving throw */ else if (do_poly && (randint1(150) > r_ptr->hdice * 2)) { if (polymorph_monster(x, y)) { /* Obvious */ if (seen) obvious = TRUE; /* Turn off the damage */ dam = 0; /* Hack -- Get new monster */ m_ptr = &m_list[c_ptr->m_idx]; /* Hack -- Get new race */ r_ptr = &r_info[m_ptr->r_idx]; /* Show the polymorph message (note is not used) */ msgf("%^s changes!", m_name); /* Get the monster name (AFTER polymorphing) */ monster_desc(m_name, m_ptr, 0, 80); } else { /* No polymorph */ note = " is unaffected!"; } } /* Handle "teleport" */ else if (do_dist) { /* Obvious */ if (seen) obvious = TRUE; /* Message */ note = " disappears!"; chg_virtue(V_VALOUR, -1); /* Teleport */ (void)teleport_away(c_ptr->m_idx, do_dist); /* Hack -- get new location */ y = m_ptr->fy; x = m_ptr->fx; /* Hack -- get new grid */ c_ptr = area(x, y); } /* Sound and Impact breathers never stun */ else if (do_stun && !FLAG(r_ptr, RF_BR_SOUN) && !FLAG(r_ptr, RF_BR_WALL)) { /* Obvious */ if (seen) obvious = TRUE; /* Get confused */ if (m_ptr->stunned) { note = " is more dazed."; tmp = m_ptr->stunned + (do_stun / 2); } else { note = " is dazed."; tmp = do_stun; } /* Apply stun */ m_ptr->stunned = (tmp < 200) ? tmp : 200; /* Get angry */ get_angry = TRUE; } /* Confusion and Chaos breathers (and sleepers) never confuse */ else if (do_conf && !FLAG(r_ptr, RF_NO_CONF) && !FLAG(r_ptr, RF_BR_CONF) && !FLAG(r_ptr, RF_BR_CHAO)) { /* Obvious */ if (seen) obvious = TRUE; /* Already partially confused */ if (m_ptr->confused) { note = " looks more confused."; tmp = m_ptr->confused + (do_conf / 2); } /* Was not confused */ else { note = " looks confused."; tmp = do_conf; } /* Apply confusion */ m_ptr->confused = (tmp < 200) ? tmp : 200; /* Get angry */ get_angry = TRUE; } /* Look to see if we've spotted a mimic */ if ((m_ptr->smart & SM_MIMIC) && obvious) { /* Toggle flag */ m_ptr->smart &= ~(SM_MIMIC); /* It is in the monster list now if visible */ if (m_ptr->ml) update_mon_vis(m_ptr->r_idx, 1); /* We've spotted it */ msgf("You've found %s!", m_name); } /* Fear */ if (do_fear) { /* Increase fear */ tmp = m_ptr->monfear + do_fear; /* Set fear */ m_ptr->monfear = (tmp < 200) ? tmp : 200; /* Get angry */ get_angry = TRUE; } /* If another monster did the damage, hurt the monster by hand */ if (who) { /* Redraw (later) if needed */ if (p_ptr->health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH); /* Wake the monster up */ m_ptr->csleep = 0; /* Hurt the monster */ m_ptr->hp -= dam; /* Dead monster */ if (m_ptr->hp < 0) { bool sad = FALSE; s16b old_m_d_head = mon_d_head; if (is_pet(m_ptr) && !(m_ptr->ml)) sad = TRUE; /* Increase the number of dying monsters */ mon_d_head++; /* Go back to the start of the queue */ if (mon_d_head >= DEATH_MAX) mon_d_head = 0; if (mon_d_head == mon_d_tail) { /* * We have greater than the maximum number of monsters dying, * revert to the old number, and do not queue this one. */ mon_d_head = old_m_d_head; /* Hack XXX Die, but do not explode and call project() */ (void)monster_death(c_ptr->m_idx, FALSE); /* Delete the monster */ delete_monster_idx(c_ptr->m_idx); } else { /* Queue the monster */ mon_d_m_idx[old_m_d_head] = c_ptr->m_idx; } /* Give detailed messages if destroyed */ if (known && note) { if (see_s) { msgf("%^s%s", m_name, note); } else { p_ptr->state.mon_fight = TRUE; } } if (sad) { msgf("You feel sad for a moment."); } } /* Damaged monster */ else { /* Give detailed messages if visible or destroyed */ if (note && seen) { msgf("%^s%s", m_name, note); } /* Hack -- Pain message */ else if (see_s) { message_pain(c_ptr->m_idx, dam); } /* Hack -- handle sleep */ if (do_sleep) m_ptr->csleep = do_sleep; } } else if (heal_leper) { msgf("%^s is healed!", m_name); /* Note that lepers do not glow - so no update for mon_lite */ /* Remove the leper */ delete_monster_idx(c_ptr->m_idx); } /* If the player did it, give him experience, check fear */ else { bool fear = FALSE; /* Hurt the monster, check for fear and death */ if (mon_take_hit(c_ptr->m_idx, dam, &fear, note_dies)) { /* Dead monster */ } /* Damaged monster */ else { /* HACK - anger the monster before showing the sleep message */ if (do_sleep) anger_monster(m_ptr); /* Give detailed messages if visible or destroyed */ if (note && seen) { msgf("%^s%s", m_name, note); } /* Hack -- Pain message */ else if (see_s) { message_pain(c_ptr->m_idx, dam); } else { p_ptr->state.mon_fight = TRUE; } /* Anger monsters */ if (((dam > 0) || get_angry) && !do_sleep) anger_monster(m_ptr); /* Take note */ if ((fear || do_fear) && (m_ptr->ml)) { flee_message(m_name, m_ptr->r_idx); } /* Hack -- handle sleep */ if (do_sleep) m_ptr->csleep = do_sleep; } } /* XXX XXX XXX Verify this code */ /* Update the monster */ update_mon(c_ptr->m_idx, FALSE); /* Redraw the monster grid */ lite_spot(x, y); /* Update monster recall window */ if (p_ptr->monster_race_idx == m_ptr->r_idx) { /* Window stuff */ p_ptr->window |= (PW_MONSTER); } /* Track it */ project_m_n++; project_m_x = x; project_m_y = y; /* Return "Anything seen?" */ return (obvious); } /* * Helper function for "project()" below. * * Handle a beam/bolt/ball causing damage to the player. * * This routine takes a "source monster" (by index), a "distance", a default * "damage", and a "damage type". See "project_m()" above. * * If "rad" is non-zero, then the blast was centered elsewhere, and the damage * is reduced (see "project_m()" above). This can happen if a monster breathes * at the player and hits a wall instead. * * NOTE (Zangband): 'Bolt' attacks can be reflected back, so we need * to know if this is actually a ball or a bolt spell * * * We return "TRUE" if any "obvious" effects were observed. XXX XXX Actually, * we just assume that the effects were obvious, for historical reasons. */ static bool project_p(int who, int r, int x, int y, int dam, int typ, int a_rad) { int k = 0; /* Hack -- assume obvious */ bool obvious = TRUE; /* Player blind-ness */ bool blind = (p_ptr->tim.blind ? TRUE : FALSE); /* Source monster */ monster_type *m_ptr; /* Monster name (for attacks) */ char m_name[80]; /* Monster name (for damage) */ char killer[80]; /* Hack -- messages */ cptr act = NULL; /* Player is not here */ if ((x != p_ptr->px) || (y != p_ptr->py)) return (FALSE); /* Player cannot hurt himself */ if (!who) return (FALSE); if ((FLAG(p_ptr, TR_REFLECT)) && !a_rad && !one_in_(10)) { int t_y, t_x; int max_attempts = 10; if (blind) msgf("Something bounces!"); else msgf("The attack bounces!"); /* Choose 'new' target */ while (TRUE) { t_y = m_list[who].fy + rand_range(-1, 1); t_x = m_list[who].fx + rand_range(-1, 1); max_attempts--; /* paranoia */ if (!max_attempts) break; /* not off edge */ if (!in_boundsp(t_x, t_y)) continue; /* Hack - exit if can see the reflection */ if (player_has_los_grid(parea(t_x, t_y))) break; } if (max_attempts < 1) { t_y = m_list[who].fy; t_x = m_list[who].fx; } (void)project(0, 0, t_x, t_y, dam, typ, (PROJECT_STOP | PROJECT_KILL)); disturb(TRUE); return TRUE; } /* XXX XXX XXX */ /* Limit maximum damage */ if (dam > 1600) dam = 1600; /* Reduce damage by distance */ dam = (dam + r) / (r + 1); /* Get the source monster */ m_ptr = &m_list[who]; /* Get the monster name */ monster_desc(m_name, m_ptr, 0, 80); /* Get the monster's real name */ monster_desc(killer, m_ptr, 0x88, 80); /* Analyze the damage */ switch (typ) { case GF_ACID: { /* Standard damage -- hurts inventory too */ if (blind) msgf("You are hit by acid!"); (void)acid_dam(dam, killer); break; } case GF_FIRE: { /* Standard damage -- hurts inventory too */ if (blind) msgf("You are hit by fire!"); (void)fire_dam(dam, killer); break; } case GF_COLD: { /* Standard damage -- hurts inventory too */ if (blind) msgf("You are hit by cold!"); (void)cold_dam(dam, killer); break; } case GF_ELEC: { /* Standard damage -- hurts inventory too */ if (blind) msgf("You are hit by lightning!"); (void)elec_dam(dam, killer); break; } case GF_POIS: { /* Standard damage -- also poisons player */ if (blind) msgf("You are hit by poison!"); (void)pois_dam(dam, killer, randint0(dam) + 10); break; } case GF_NUKE: { /* Standard damage -- also poisons / mutates player */ if (blind) msgf("You are hit by radiation!"); dam = resist(dam, res_pois_lvl); take_hit(dam, killer); if (pois_dam(10, killer, randint0(dam) + 10)) { if (one_in_(5)) { msgf("You undergo a freakish metamorphosis!"); if (one_in_(4)) do_poly_self(); else mutate_player(); } if (one_in_(6)) { (void)inven_damage(set_acid_destroy, 2); } } break; } case GF_MISSILE: { /* Standard damage */ if (blind) msgf("You are hit by something!"); take_hit(dam, killer); break; } case GF_HOLY_FIRE: { /* Holy Orb -- Player only takes partial damage */ if (blind) msgf("You are hit by something!"); if ((p_ptr->spell.r[0].realm == REALM_LIFE) || (p_ptr->spell.r[1].realm == REALM_LIFE)) dam /= 2; else if ((p_ptr->spell.r[0].realm == REALM_DEATH) || (p_ptr->spell.r[1].realm == REALM_DEATH)) dam *= 2; take_hit(dam, killer); break; } case GF_HELL_FIRE: { if (blind) msgf("You are hit by something!"); if ((p_ptr->spell.r[0].realm == REALM_DEATH) || (p_ptr->spell.r[1].realm == REALM_DEATH)) dam /= 2; else if ((p_ptr->spell.r[0].realm == REALM_LIFE) || (p_ptr->spell.r[1].realm == REALM_LIFE)) dam *= 2; take_hit(dam, killer); break; } case GF_ARROW: { /* Arrow -- XXX no dodging */ if (blind) msgf("You are hit by something sharp!"); take_hit(dam, killer); break; } case GF_PLASMA: { /* Plasma -- XXX No resist */ if (blind) msgf("You are hit by something *HOT*!"); take_hit(dam, killer); if (!(FLAG(p_ptr, TR_RES_SOUND))) { (void)inc_stun(randint1((dam > 40) ? 35 : (dam * 3 / 4 + 5))); } if (res_acid_lvl() < 9) { (void)inven_damage(set_acid_destroy, 3); } break; } case GF_NETHER: { /* Nether -- drain experience */ if (blind) msgf("You are hit by nether forces!"); if (FLAG(p_ptr, TR_RES_NETHER)) { if (p_ptr->rp.prace != RACE_SPECTRE) dam *= 6; dam /= rand_range(7, 12); } else { if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 75)) { msgf("You keep hold of your life force!"); } else if (FLAG(p_ptr, TR_HOLD_LIFE)) { msgf("You feel your life slipping away!"); lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE); } else { msgf("You feel your life draining away!"); lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); } } if (p_ptr->rp.prace == RACE_SPECTRE) { msgf("You feel invigorated!"); (void)hp_player(dam / 4); } else { take_hit(dam, killer); } break; } case GF_WATER: { /* Water -- stun/confuse */ if (blind) msgf("You are hit by something wet!"); if (!(FLAG(p_ptr, TR_RES_SOUND))) { (void)inc_stun(randint1(40)); } if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(5, 10)); } if (one_in_(5)) { (void)inven_damage(set_cold_destroy, 3); } take_hit(dam, killer); break; } case GF_CHAOS: { /* Chaos -- many effects */ if (blind) msgf("You are hit by a wave of anarchy!"); if (FLAG(p_ptr, TR_RES_CHAOS)) { dam *= 6; dam /= rand_range(7, 12); } if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(20, 30)); } if (!(FLAG(p_ptr, TR_RES_CHAOS))) { (void)inc_image(randint1(10)); if (one_in_(3)) { msgf("Your body is twisted by chaos!"); (void)gain_mutation(0); } } if (!(FLAG(p_ptr, TR_RES_NETHER)) && !(FLAG(p_ptr, TR_RES_CHAOS))) { if ((FLAG(p_ptr, TR_HOLD_LIFE)) && (randint0(100) < 75)) { msgf("You keep hold of your life force!"); } else if (FLAG(p_ptr, TR_HOLD_LIFE)) { msgf("You feel your life slipping away!"); lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE); } else { msgf("You feel your life draining away!"); lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); } } if (!(FLAG(p_ptr, TR_RES_CHAOS)) || one_in_(9)) { (void)inven_damage(set_elec_destroy, 2); (void)inven_damage(set_fire_destroy, 2); } take_hit(dam, killer); break; } case GF_SHARDS: { /* Shards -- mostly cutting */ if (blind) msgf("You are hit by something sharp!"); if (FLAG(p_ptr, TR_RES_SHARDS)) { dam *= 6; dam /= rand_range(7, 12); } else { (void)inc_cut(dam); } if (!(FLAG(p_ptr, TR_RES_SHARDS)) || one_in_(13)) { (void)inven_damage(set_cold_destroy, 2); } take_hit(dam, killer); break; } case GF_SOUND: { /* Sound -- mostly stunning */ if (blind) msgf("You are hit by a loud noise!"); if (FLAG(p_ptr, TR_RES_SOUND)) { dam *= 5; dam /= rand_range(7, 12); } else { (void)inc_stun(randint1((dam > 90) ? 35 : (dam / 3 + 5))); } if (!(FLAG(p_ptr, TR_RES_SOUND)) || one_in_(13)) { (void)inven_damage(set_cold_destroy, 2); } take_hit(dam, killer); break; } case GF_CONFUSION: { /* Pure confusion */ if (blind) msgf("You are hit by something puzzling!"); if (FLAG(p_ptr, TR_RES_CONF)) { dam *= 5; dam /= rand_range(7, 12); } if (!(FLAG(p_ptr, TR_RES_CONF))) { (void)inc_confused(rand_range(10, 30)); } take_hit(dam, killer); break; } case GF_DISENCHANT: { /* Disenchantment -- see above */ if (blind) msgf("You are hit by something static!"); if (FLAG(p_ptr, TR_RES_DISEN)) { dam *= 6; dam /= rand_range(7, 12); } else { (void)apply_disenchant(); } take_hit(dam, killer); break; } case GF_NEXUS: { /* Nexus -- see above */ if (blind) msgf("You are hit by something strange!"); /* Mutations can be weird... */ if ((p_ptr->muta1 & MUT1_VTELEPORT) || (p_ptr->muta1 & MUT1_BLINK) || (p_ptr->muta1 & MUT1_SWAP_POS) || (p_ptr->muta1 & MUT1_RECALL)) { dam = dam * 4 / 3; } if (FLAG(p_ptr, TR_RES_NEXUS)) { dam *= 6; dam /= rand_range(7, 12); } else { apply_nexus(m_ptr); } take_hit(dam, killer); break; } case GF_FORCE: { /* Force -- mostly stun */ if (blind) msgf("You are hit by kinetic force!"); if (!(FLAG(p_ptr, TR_RES_SOUND))) { (void)inc_stun(randint1(20)); } take_hit(dam, killer); break; } case GF_ROCKET: { /* Rocket -- stun, cut */ if (blind) msgf("There is an explosion!"); if (!(FLAG(p_ptr, TR_RES_SOUND))) { (void)inc_stun(randint1(20)); } if (FLAG(p_ptr, TR_RES_SHARDS)) { dam /= 2; } else { (void)inc_cut(dam / 2); } if (!(FLAG(p_ptr, TR_RES_SHARDS)) || one_in_(12)) { (void)inven_damage(set_cold_destroy, 3); } take_hit(dam, killer); break; } case GF_INERTIA: { /* Inertia -- slowness */ if (blind) msgf("You are hit by something slow!"); (void)inc_slow(rand_range(4, 8)); take_hit(dam, killer); break; } case GF_LITE: { /* Lite -- blinding */ if (blind) msgf("You are hit by something!"); if (FLAG(p_ptr, TR_IM_LITE)) { dam = 0; } else if (FLAG(p_ptr, TR_RES_LITE)) { dam *= 4; dam /= rand_range(7, 12); } else if (!blind && !(FLAG(p_ptr, TR_RES_BLIND))) { (void)inc_blind(rand_range(2, 7)); } if (FLAG(p_ptr, TR_HURT_LITE)) { msgf("The light scorches your flesh!"); dam *= 2; } take_hit(dam, killer); if (p_ptr->tim.wraith_form) { p_ptr->tim.wraith_form = 0; msgf("The light forces you out of your incorporeal shadow form."); p_ptr->redraw |= PR_MAP; /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } break; } case GF_DARK: { /* Dark -- blinding */ if (blind) msgf("You are hit by something!"); if (FLAG(p_ptr, TR_IM_DARK)) { dam = 0; } else if (FLAG(p_ptr, TR_RES_DARK)) { dam *= 4; dam /= rand_range(7, 12); } else if (!blind && !(FLAG(p_ptr, TR_RES_BLIND))) { (void)inc_blind(rand_range(2, 7)); } if (FLAG(p_ptr, TR_HURT_DARK)) { dam *= 2; } if (p_ptr->tim.wraith_form) (void)hp_player(dam); else take_hit(dam, killer); break; } case GF_TIME: { /* Time -- bolt fewer effects XXX */ if (blind) msgf("You are hit by a blast from the past!"); switch (randint1(10)) { case 1: case 2: case 3: case 4: case 5: { msgf("You feel life has clocked back."); lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); break; } case 6: case 7: case 8: case 9: { switch (randint1(6)) { case 1: { k = A_STR; act = "strong"; break; } case 2: { k = A_INT; act = "bright"; break; } case 3: { k = A_WIS; act = "wise"; break; } case 4: { k = A_DEX; act = "agile"; break; } case 5: { k = A_CON; act = "healthy"; break; } case 6: { k = A_CHR; act = "beautiful"; break; } } msgf("You're not as %s as you used to be...", act); /* Note: this is a change from old behavior -RML */ p_ptr->stat[k].cur = (p_ptr->stat[k].cur * 3) / 4; if (p_ptr->stat[k].cur < 3) p_ptr->stat[k].cur = 3; p_ptr->update |= (PU_BONUS); break; } case 10: { msgf("You're not as powerful as you used to be..."); for (k = 0; k < A_MAX; k++) { p_ptr->stat[k].cur = (p_ptr->stat[k].cur * 3) / 4; if (p_ptr->stat[k].cur < 3) p_ptr->stat[k].cur = 3; } p_ptr->update |= (PU_BONUS); break; } } take_hit(dam, killer); break; } case GF_GRAVITY: { /* Gravity -- stun plus slowness plus teleport */ if (blind) msgf("You are hit by something heavy!"); msgf("Gravity warps around you."); teleport_player(5); if (!(FLAG(p_ptr, TR_FEATHER))) (void)inc_slow(rand_range(4, 8)); if (!((FLAG(p_ptr, TR_RES_SOUND)) || (FLAG(p_ptr, TR_FEATHER)))) { (void)inc_stun(randint1((dam > 90) ? 35 : (dam / 3 + 5))); } if (FLAG(p_ptr, TR_FEATHER)) { dam = (dam * 2) / 3; } if (!(FLAG(p_ptr, TR_FEATHER)) || one_in_(13)) { (void)inven_damage(set_cold_destroy, 2); } take_hit(dam, killer); break; } case GF_DISINTEGRATE: { /* Standard damage */ if (blind) msgf("You are hit by pure energy!"); take_hit(dam, killer); break; } case GF_OLD_HEAL: { if (blind) msgf("You are hit by something invigorating!"); (void)hp_player(dam); dam = 0; break; } case GF_OLD_SPEED: { if (blind) msgf("You are hit by something!"); (void)inc_fast(randint1(5)); dam = 0; break; } case GF_OLD_SLOW: { if (blind) msgf("You are hit by something slow!"); (void)inc_slow(rand_range(4, 8)); break; } case GF_OLD_SLEEP: { if (FLAG(p_ptr, TR_FREE_ACT)) break; if (blind) msgf("You fall asleep!"); if (ironman_nightmare) { msgf("A horrible vision enters your mind."); /* Have some nightmares */ have_nightmare(); } (void)inc_paralyzed(dam); dam = 0; break; } case GF_MANA: { /* Pure damage */ if (blind) msgf("You are hit by an aura of magic!"); take_hit(dam, killer); break; } case GF_METEOR: { /* Pure damage */ if (blind) msgf("Something falls from the sky on you!"); take_hit(dam, killer); if (!(FLAG(p_ptr, TR_RES_SHARDS)) || one_in_(13)) { if (!(FLAG(p_ptr, TR_IM_FIRE))) { (void)inven_damage(set_fire_destroy,2); } (void)inven_damage(set_cold_destroy, 2); } break; } case GF_ICE: { /* Ice -- cold plus stun plus cuts */ if (blind) msgf("You are hit by something sharp and cold!"); (void)cold_dam(dam, killer); if (!(FLAG(p_ptr, TR_RES_SHARDS))) { (void)inc_cut(damroll(5, 8)); } if (!(FLAG(p_ptr, TR_RES_SOUND))) { (void)inc_stun(randint1(15)); } if (!((FLAG(p_ptr, TR_IM_COLD)) || p_ptr->tim.oppose_cold) || one_in_(12)) { if (!(FLAG(p_ptr, TR_IM_COLD))) { (void)inven_damage(set_cold_destroy, 3); } } break; } case GF_DEATH_RAY: { /* Death Ray */ if (blind) msgf("You are hit by something extremely cold!"); switch (p_ptr->rp.prace) { /* Some races are immune */ case RACE_GOLEM: case RACE_SKELETON: case RACE_ZOMBIE: case RACE_VAMPIRE: case RACE_SPECTRE: case RACE_GHOUL: { dam = 0; break; } /* Hurt a lot */ default: { take_hit(dam, killer); break; } } break; } default: { /* Default */ /* No damage */ dam = 0; break; } } /* Disturb */ disturb(TRUE); /* Return "Anything seen?" */ return (obvious); } /* * Find the distance from (x, y) to a line. */ int dist_to_line(int x, int y, int x1, int y1, int x2, int y2) { /* Vector from (x, y) to (x1, y1) */ int py = y1 - y; int px = x1 - x; /* Normal vector */ int ny = x2 - x1; int nx = y1 - y2; /* Length of N */ int d = distance(x1, y1, x2, y2); /* Component of P on N */ d = ((d) ? ((py * ny + px * nx) / d) : 0); /* Absolute value */ return ((d >= 0) ? d : 0 - d); } /* * Generic "beam"/"bolt"/"ball" projection routine. * * Input: * who: Index of "source" monster (zero for "player") * rad: Radius of explosion (0 = beam/bolt, 1 to 9 = ball) * y,x: Target location (or location to travel "towards") * dam: Base damage roll to apply to affected monsters (or player) * typ: Type of damage to apply to monsters (and objects) * flg: Extra bit flags (see PROJECT_xxxx in "defines.h") * * Return: * TRUE if any "effects" of the projection were observed, else FALSE * * Allows a monster (or player) to project a beam/bolt/ball of a given kind * towards a given location (optionally passing over the heads of interposing * monsters), and have it do a given amount of damage to the monsters (and * optionally objects) within the given radius of the final location. * * A "bolt" travels from source to target and affects only the target grid. * A "beam" travels from source to target, affecting all grids passed through. * A "ball" travels from source to the target, exploding at the target, and * affecting everything within the given radius of the target location. * * Traditionally, a "bolt" does not affect anything on the ground, and does * not pass over the heads of interposing monsters, much like a traditional * missile, and will "stop" abruptly at the "target" even if no monster is * positioned there, while a "ball", on the other hand, passes over the heads * of monsters between the source and target, and affects everything except * the source monster which lies within the final radius, while a "beam" * affects every monster between the source and target, except for the casting * monster (or player), and rarely affects things on the ground. * * Two special flags allow us to use this function in special ways, the * "PROJECT_HIDE" flag allows us to perform "invisible" projections, while * the "PROJECT_JUMP" flag allows us to affect a specific grid, without * actually projecting from the source monster (or player). * * The player will only get "experience" for monsters killed by himself * Unique monsters can only be destroyed by attacks from the player * * Only 1024 grids can be affected per projection. This affects the maximum * possible effect per projection. * * One can project in a given "direction" by combining PROJECT_THRU with small * offsets to the initial location (see "line_spell()"), or by calculating * "virtual targets" far away from the player. * * One can also use PROJECT_THRU to send a beam/bolt along an angled path, * continuing until it actually hits something (useful for "stone to mud"). * * Bolts and Beams explode INSIDE walls, so that they can destroy doors. * * Balls must explode BEFORE hitting walls, or they would affect monsters * on both sides of a wall. * * We "pre-calculate" the blast area only in part for efficiency. * More importantly, this lets us do "explosions" from the "inside" out. * This results in a more logical distribution of "blast" treasure. * It also produces a better (in my opinion) animation of the explosion. * It could be (but is not) used to have the treasure dropped by monsters * in the middle of the explosion fall "outwards", and then be damaged by * the blast as it spreads outwards towards the treasure drop location. * * Walls and doors are included in the blast area, so that they can be * "burned" or "melted" in later versions. * * This algorithm is intended to maximize simplicity, not necessarily * efficiency, since this function is not a bottleneck in the code. * * We apply the blast effect from ground zero outwards, in several passes, * first affecting features, then objects, then monsters, then the player. * This allows walls to be removed before checking the object or monster * in the wall, and protects objects which are dropped by monsters killed * in the blast, and allows the player to see all affects before he is * killed or teleported away. The semantics of this method are open to * various interpretations, but they seem to work well in practice. * * We process the blast area from ground-zero outwards to allow for better * distribution of treasure dropped by monsters, and because it provides a * pleasing visual effect at low cost. * * Note that the damage done by "ball" explosions decreases with distance. * This decrease is rapid, grids at radius "dist" take "1/dist" damage. * * Notice the "napalm" effect of "beam" weapons. First they "project" to * the target, and then the damage "flows" along this beam of destruction. * The damage at every grid is the same as at the "center" of a "ball" * explosion, since the "beam" grids are treated as if they ARE at the * center of a "ball" explosion. * * Currently, specifying "beam" plus "ball" means that locations which are * covered by the initial "beam", and also covered by the final "ball", except * for the final grid (the epicenter of the ball), will be "hit twice", once * by the initial beam, and once by the exploding ball. For the grid right * next to the epicenter, this results in 150% damage being done. The center * does not have this problem, for the same reason the final grid in a "beam" * plus "bolt" does not -- it is explicitly removed. Simply removing "beam" * grids which are covered by the "ball" will NOT work, as then they will * receive LESS damage than they should. Do not combine "beam" with "ball". * * The array "gy[],gx[]" with current size "grids" is used to hold the * collected locations of all grids in the "blast area" plus "beam path". * * Note the rather complex usage of the "gm[]" array. First, gm[0] is always * zero. Second, for N>1, gm[N] is always the index (in gy[],gx[]) of the * first blast grid (see above) with radius "N" from the blast center. Note * that only the first gm[1] grids in the blast area thus take full damage. * Also, note that gm[rad+1] is always equal to "grids", which is the total * number of blast grids. * * Note that once the projection is complete, (y2,x2) holds the final location * of bolts/beams, and the "epicenter" of balls. * * Note also that "rad" specifies the "inclusive" radius of projection blast, * so that a "rad" of "one" actually covers 5 or 9 grids, depending on the * implementation of the "distance" function. Also, a bolt can be properly * viewed as a "ball" with a "rad" of "zero". * * Note that if no "target" is reached before the beam/bolt/ball travels the * maximum distance allowed (MAX_RANGE), no "blast" will be induced. This * may be relevant even for bolts, since they have a "1x1" mini-blast. * * Note that for consistency, we "pretend" that the bolt actually takes "time" * to move from point A to point B, even if the player cannot see part of the * projection path. Note that in general, the player will *always* see part * of the path, since it either starts at the player or ends on the player. * * Hack -- we assume that every "projection" is "self-illuminating". * * Hack -- when only a single monster is affected, we automatically track * (and recall) that monster, unless "PROJECT_JUMP" is used. * * Note that all projections now "explode" at their final destination, even * if they were being projected at a more distant destination. This means * that "ball" spells will *always* explode. * * Note that we must call "handle_stuff()" after affecting terrain features * in the blast radius, in case the "illumination" of the grid was changed, * and "update_view()" and "update_monsters()" need to be called. */ bool project(int who, int rad, int x, int y, int dam, int typ, u16b flg) { int i, j, t, dist; int y1, x1; int y2, x2; int dist_hack = 0; int y_saver, x_saver; /* For reflecting monsters */ int msec = delay_factor * delay_factor * delay_factor; /* Assume the player sees nothing */ bool notice = FALSE; /* Assume the player has seen nothing */ bool visual = FALSE; /* Assume the player has seen no blast grids */ bool drawn = FALSE; /* Assume to be a normal ball spell */ bool breath = FALSE; /* Is the player blind? */ bool blind = (p_ptr->tim.blind ? TRUE : FALSE); /* Number of grids in the "path" */ int path_n = 0; /* Actual grids in the "path" */ coord path_g[512]; /* Number of grids in the "blast area" (including the "beam" path) */ int grids = 0; /* Coordinates of the affected grids */ int gx[1024], gy[1024]; /* Encoded "radius" info (see above) */ byte gm[32]; /* Actual radius encoded in gm[] */ int gm_rad = rad; bool jump = FALSE; cave_type *c_ptr; /* Are there no monsters queued to die? */ bool mon_explode = (mon_d_head == mon_d_tail) ? TRUE : FALSE; /* Hack -- some weapons always stop at monsters */ if (typ == GF_ROCKET) flg |= PROJECT_STOP; /* Hack -- Jump to target */ if (flg & (PROJECT_JUMP)) { x1 = x; y1 = y; /* Default "destination" */ y2 = y; x2 = x; /* Clear the flag */ flg &= ~(PROJECT_JUMP); jump = TRUE; } /* Start at player */ else if (who <= 0) { x1 = p_ptr->px; y1 = p_ptr->py; /* Default "destination" */ y2 = y; x2 = x; } /* Start at monster */ else if (who > 0) { /* * Start at player, and go to monster * This means that monsters always can hit the player * if the player can see them */ y1 = y; x1 = x; x2 = m_list[who].fx; y2 = m_list[who].fy; } /* Hack -- verify stuff */ if (flg & (PROJECT_THRU)) { if ((x1 == x2) && (y1 == y2)) { flg &= ~(PROJECT_THRU); } } /* Handle a breath attack */ if (rad < 0) { rad = 0 - rad; breath = TRUE; flg |= PROJECT_HIDE; } /* Calculate the projection path */ path_n = project_path(path_g, x1, y1, x2, y2, flg); /* Do we need to invert the path? */ if ((path_n > 0) && !jump && (who > 0)) { /* Reverse the path */ for (i = path_n - 2, j = 0; i > j; i--, j++) { /* Swap y coords */ t = path_g[i].y; path_g[i].y = path_g[j].y; path_g[j].y = t; /* Swap x coords */ t = path_g[i].x; path_g[i].x = path_g[j].x; path_g[j].x = t; } /* Get correct ending coords */ path_g[path_n - 1].x = x1; path_g[path_n - 1].y = y1; /* Swap the initial and final coords */ t = y1; y1 = y2; y2 = t; t = x1; x1 = x2; x2 = t; } /* Hack -- Assume there will be no blast (max radius 32) */ for (dist = 0; dist < 32; dist++) gm[dist] = 0; /* Initial grid */ y = y1; x = x1; y_saver = y1; x_saver = x1; dist = 0; /* Collect beam grids */ if (flg & (PROJECT_BEAM)) { gy[grids] = y; gx[grids] = x; grids++; } /* Hack -- Handle stuff */ handle_stuff(); /* Project along the path */ for (i = 0; i < path_n; i++) { int oy = y; int ox = x; int ny = path_g[i].y; int nx = path_g[i].x; c_ptr = area(nx, ny); /* Hack -- Balls explode before reaching walls */ if (cave_wall_grid(c_ptr) && (rad > 0)) break; /* Require fields do not block magic */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MAGIC)) break; /* Advance */ y = ny; x = nx; /* Collect beam grids */ if (flg & (PROJECT_BEAM)) { gy[grids] = y; gx[grids] = x; grids++; } /* Only do visuals if requested */ if (!blind && !(flg & (PROJECT_HIDE))) { /* Only do visuals if the player can "see" the bolt */ if (in_boundsp(x, y) && panel_contains(x, y) && player_has_los_grid(parea(x, y))) { byte a, c; /* Obtain the bolt pict */ bolt_pict(ox, oy, x, y, typ, &a, &c); /* Visual effects */ print_rel(c, a, x, y); move_cursor_relative(x, y); if (fresh_before) Term_fresh(); /* Delay */ Term_xtra(TERM_XTRA_DELAY, msec); /* Show it */ lite_spot(x, y); if (fresh_before) Term_fresh(); /* Display "beam" grids */ if (flg & (PROJECT_BEAM)) { /* Obtain the explosion pict */ bolt_pict(x, y, x, y, typ, &a, &c); /* Visual effects */ print_rel(c, a, x, y); } /* Hack -- Activate delay */ visual = TRUE; } /* Hack -- delay anyway for consistency */ else if (visual) { /* Delay for consistency */ Term_xtra(TERM_XTRA_DELAY, msec); } } } /* Save the "blast epicenter" */ y2 = y; x2 = x; /* Start the "explosion" */ gm[0] = 0; /* Hack -- make sure beams get to "explode" */ gm[1] = grids; dist_hack = dist; dist = path_n; /* If we found a "target", explode there */ if (dist <= MAX_RANGE) { /* Mega-Hack -- remove the final "beam" grid */ if ((flg & (PROJECT_BEAM)) && (grids > 0)) grids--; /* * Create a conical breath attack * * *** * ******** * D********@** * ******** * *** */ if (breath) { int by, bx; int brad = 0; int bdis = 0; int cdis; /* Not done yet */ bool done = FALSE; flg &= ~(PROJECT_HIDE); by = y1; bx = x1; /* Initialise the multi-move */ mmove_init(x1, y1, x2, y2); while (bdis <= dist + rad) { /* Travel from center outward */ for (cdis = 0; cdis <= brad; cdis++) { /* Scan the maximal blast area of radius "cdis" */ for (y = by - cdis; y <= by + cdis; y++) { for (x = bx - cdis; x <= bx + cdis; x++) { /* Ignore "illegal" locations */ if (!in_bounds2(x, y)) continue; /* Enforce a circular "ripple" */ if (distance(x1, y1, x, y) != bdis) continue; /* Enforce an arc */ if (distance(bx, by, x, y) != cdis) continue; /* The blast is stopped by walls */ if (!in_ball_range(bx, by, x, y)) continue; /* Save this grid */ gy[grids] = y; gx[grids] = x; grids++; } } } /* Encode some more "radius" info */ gm[bdis + 1] = grids; /* Stop moving */ if ((by == y2) && (bx == x2)) done = TRUE; /* Finish */ if (done) { bdis++; continue; } /* Ripple outwards */ mmove(&bx, &by, x1, y1); /* Find the next ripple */ bdis++; /* Increase the size */ brad = (rad * bdis) / dist; } /* Store the effect size */ gm_rad = bdis; } else { /* Determine the blast area, work from the inside out */ for (dist = 0; dist <= rad; dist++) { /* Scan the maximal blast area of radius "dist" */ for (y = y2 - dist; y <= y2 + dist; y++) { for (x = x2 - dist; x <= x2 + dist; x++) { /* Ignore "illegal" locations */ if (!in_bounds2(x, y)) continue; /* Enforce a "circular" explosion */ if (distance(x2, y2, x, y) != dist) continue; if (typ == GF_DISINTEGRATE) { /* Disintegration balls explosions are stopped by perma-walls */ if (!in_disintegration_range(x2, y2, x, y)) continue; c_ptr = area(x, y); if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) continue; /* Delete fields on the square */ delete_field_location(c_ptr); if (cave_valid_grid(c_ptr) && (c_ptr->feat <= FEAT_WALL_SOLID || c_ptr->feat > FEAT_SHAL_ACID)) { cave_set_feat(x, y, the_floor()); } } else { /* Ball explosions are stopped by walls/fields */ if (!in_ball_range(x2, y2, x, y)) continue; } /* Save this grid */ gy[grids] = y; gx[grids] = x; grids++; } } /* Encode some more "radius" info */ gm[dist + 1] = grids; } } } /* Speed -- ignore "non-explosions" */ if (!grids) return (FALSE); /* Display the "blast area" if requested */ if (!blind && !(flg & (PROJECT_HIDE))) { /* Then do the "blast", from inside out */ for (t = 0; t <= gm_rad; t++) { /* Dump everything with this radius */ for (i = gm[t]; i < gm[t + 1]; i++) { /* Extract the location */ y = gy[i]; x = gx[i]; /* Only do visuals if the player can "see" the blast */ if (in_boundsp(x, y) && panel_contains(x, y) && player_has_los_grid(parea(x, y))) { byte a, c; drawn = TRUE; /* Obtain the explosion pict */ bolt_pict(x, y, x, y, typ, &a, &c); /* Visual effects -- Display */ print_rel(c, a, x, y); } } /* Hack -- center the cursor */ move_cursor_relative(x2, y2); /* Flush each "radius" seperately */ if (fresh_before) Term_fresh(); /* Delay (efficiently) */ if (visual || drawn) { Term_xtra(TERM_XTRA_DELAY, msec); } } /* Flush the erasing */ if (drawn) { /* Erase the explosion drawn above */ for (i = 0; i < grids; i++) { /* Extract the location */ y = gy[i]; x = gx[i]; /* Hack -- Erase if needed */ if (in_boundsp(x, y) && player_has_los_grid(parea(x, y))) { lite_spot(x, y); } } /* Hack -- center the cursor */ move_cursor_relative(x2, y2); /* Flush the explosion */ if (fresh_before) Term_fresh(); } } /* Check features */ if (flg & (PROJECT_GRID)) { /* Start with "dist" of zero */ dist = 0; /* Scan for features */ for (i = 0; i < grids; i++) { /* Hack -- Notice new "dist" values */ if (gm[dist + 1] == i) dist++; /* Get the grid location */ y = gy[i]; x = gx[i]; /* Find the closest point in the blast */ if (breath) { int d = dist_to_line(x, y, x1, y1, x2, y2); /* Affect the grid */ if (project_f(who, d, x, y, dam, typ)) notice = TRUE; /* Affect fields on the grid */ field_script(area(x, y), FIELD_ACT_MAGIC_TARGET, "iiiib:b", LUA_VAR(who), LUA_VAR_NAMED(d, "dist"), LUA_VAR(dam), LUA_VAR_NAMED(typ, "type"), LUA_VAR_NAMED(player_can_see_bold(x, y), "known"), LUA_RETURN(notice)); } else { /* Affect the grid */ if (project_f(who, dist, x, y, dam, typ)) notice = TRUE; /* Affect fields on the grid */ field_script(area(x, y), FIELD_ACT_MAGIC_TARGET, "iiiib:b", LUA_VAR(who), LUA_VAR(dist), LUA_VAR(dam), LUA_VAR_NAMED(typ, "type"), LUA_VAR_NAMED(player_can_see_bold(x, y), "known"), LUA_RETURN(notice)); } } } /* Update if required */ handle_stuff(); /* Check objects */ if (flg & (PROJECT_ITEM)) { /* Start with "dist" of zero */ dist = 0; /* Scan for objects */ for (i = 0; i < grids; i++) { /* Hack -- Notice new "dist" values */ if (gm[dist + 1] == i) dist++; /* Get the grid location */ y = gy[i]; x = gx[i]; /* Find the closest point in the blast */ if (breath) { int d = dist_to_line(x, y, x1, y1, x2, y2); /* Affect the object in the grid */ if (project_o(who, d, x, y, dam, typ)) notice = TRUE; } else { /* Affect the object in the grid */ if (project_o(who, dist, x, y, dam, typ)) notice = TRUE; } } } /* Check monsters */ if (flg & (PROJECT_KILL)) { /* Mega-Hack */ project_m_n = 0; project_m_x = 0; project_m_y = 0; /* Start with "dist" of zero */ dist = 0; /* Scan for monsters */ for (i = 0; i < grids; i++) { /* Hack -- Notice new "dist" values */ if (gm[dist + 1] == i) dist++; /* Get the grid location */ y = gy[i]; x = gx[i]; if (grids > 1) { /* Find the closest point in the blast */ if (breath) { int d = dist_to_line(x, y, x1, y1, x2, y2); /* Affect the monster in the grid */ if (project_m(who, d, x, y, dam, typ)) notice = TRUE; } else { /* Affect the monster in the grid */ if (project_m(who, dist, x, y, dam, typ)) notice = TRUE; } } else { monster_race *ref_ptr = &r_info[m_list[area(x, y)->m_idx].r_idx]; if (FLAG(ref_ptr, RF_REFLECTING) && !one_in_(10) && (dist_hack > 1)) { int t_y, t_x; int max_attempts = 10; /* Choose 'new' target */ do { t_y = y_saver + rand_range(-1, 1); t_x = x_saver + rand_range(-1, 1); max_attempts--; } while (max_attempts && in_bounds2(t_x, t_y) && !(los(x, y, t_x, t_y))); if (max_attempts < 1) { t_y = y_saver; t_x = x_saver; } if (m_list[area(x, y)->m_idx].ml) { msgf("The attack bounces!"); ref_ptr->r_flags[1] |= RF1_REFLECTING; } /* Recursion... */ (void)project(area(x, y)->m_idx, 0, t_x, t_y, dam, typ, flg); } else { if (project_m(who, dist, x, y, dam, typ)) notice = TRUE; } } } /* Player affected one monster (without "jumping") */ if (!who && (project_m_n == 1) && !jump) { /* Location */ x = project_m_x; y = project_m_y; /* Track if possible */ if (area(x, y)->m_idx > 0) { monster_type *m_ptr = &m_list[area(x, y)->m_idx]; /* Hack -- auto-recall */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Hack - auto-track */ if (m_ptr->ml) health_track(area(x, y)->m_idx); } } } /* Check player */ if (flg & (PROJECT_KILL)) { /* Start with "dist" of zero */ dist = 0; /* Scan for player */ for (i = 0; i < grids; i++) { /* Hack -- Notice new "dist" values */ if (gm[dist + 1] == i) dist++; /* Get the grid location */ y = gy[i]; x = gx[i]; /* Find the closest point in the blast */ if (breath) { int d = dist_to_line(x, y, x1, y1, x2, y2); /* Affect the player */ if (project_p(who, d, x, y, dam, typ, rad)) notice = TRUE; } else { /* Affect the player */ if (project_p(who, dist, x, y, dam, typ, rad)) notice = TRUE; } } } if (mon_explode) { /* * Run through the queue of monsters waiting to die, * calling monster_death() for each. * * This prevents chain reactions of explosions from * causing a stack smash, by only having one level * of recursion. * * Monster_death() might call project() again - but * if that happens, then mon_explode is FALSE, and * it will fall back to us to process the monsters * killed by that explosion. */ while (mon_d_tail != mon_d_head) { /* Generate treasure, etc */ (void)monster_death(mon_d_m_idx[mon_d_tail], TRUE); /* Delete the monster */ delete_monster_idx(mon_d_m_idx[mon_d_tail]); /* * Increase mon_d_tail after the death. * (This means that mon_explode will be FALSE in other * calls to this function.) */ mon_d_tail++; /* Cycle back to the start */ if (mon_d_tail >= DEATH_MAX) mon_d_tail = 0; } } /* Return "something was noticed" */ return (notice); } zangband/src/spells2.c0000755000000000000000000023550610250356274013701 0ustar rootroot/* File: spells2.c */ /* Purpose: Spell code (part 2) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * self-knowledge... idea from nethack. Useful for determining powers and * resistences of items. It saves the screen, clears it, then starts listing * attributes, a screenful at a time. (There are a LOT of attributes to * list. It will probably take 2 or 3 screens for a powerful character whose * using several artifacts...) -CFT * * It is now a lot more efficient. -BEN- * * See also "identify_fully()". * * XXX XXX XXX Use the "show_file()" method, perhaps. */ void self_knowledge(void) { int i = 0, j, k, x, height; int res; int v_nr; char v_string[8][128]; u32b ff[4] = {0, 0, 0, 0}; object_type *o_ptr; const mutation_type *mut_ptr; char Dummy[80], Liferating[80]; cptr info[220]; int plev = p_ptr->lev; int percent; Dummy[0] = 0; Liferating[0] = 0; percent = (int)(((long)p_ptr->player_hp[PY_MAX_LEVEL - 1] * 200L) / (2 * p_ptr->rp.hitdie + ((PY_MAX_LEVEL - 1) * (p_ptr->rp.hitdie + 1)))); strnfmt(Liferating, 80, "Your current Life Rating is %d/100.", percent); info[i++] = Liferating; chg_virtue(V_KNOWLEDGE, 1); chg_virtue(V_ENLIGHTEN, 1); /* Acquire item flags from equipment */ for (k = 0; k < EQUIP_MAX; k++) { o_ptr = &p_ptr->equipment[k]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Extract flags */ ff[0] |= o_ptr->flags[0]; ff[1] |= o_ptr->flags[1]; ff[2] |= o_ptr->flags[2]; ff[3] |= o_ptr->flags[3]; } for (v_nr = 0; v_nr < MAX_PLAYER_VIRTUES; v_nr++) { char virt_name[20]; char vir_desc[80]; int tester = p_ptr->virtues[v_nr]; strcpy(virt_name, virtue[(p_ptr->vir_types[v_nr]) - 1]); strnfmt(vir_desc, 80, "Oops. No info about %s.", virt_name); if (tester < -100) strnfmt(vir_desc, 80, "You are the polar opposite of %s (%d).", virt_name, tester); else if (tester < -80) strnfmt(vir_desc, 80, "You are an arch-enemy of %s (%d).", virt_name, tester); else if (tester < -60) strnfmt(vir_desc, 80, "You are a bitter enemy of %s (%d).", virt_name, tester); else if (tester < -40) strnfmt(vir_desc, 80, "You are an enemy of %s (%d).", virt_name, tester); else if (tester < -20) strnfmt(vir_desc, 80, "You have sinned against %s (%d).", virt_name, tester); else if (tester < 0) strnfmt(vir_desc, 80, "You have strayed from the path of %s (%d).", virt_name, tester); else if (tester == 0) strnfmt(vir_desc, 80, "You are neutral to %s (%d).", virt_name, tester); else if (tester < 20) strnfmt(vir_desc, 80, "You are somewhat virtuous in %s (%d).", virt_name, tester); else if (tester < 40) strnfmt(vir_desc, 80, "You are virtuous in %s (%d).", virt_name, tester); else if (tester < 60) strnfmt(vir_desc, 80, "You are very virtuous in %s (%d).", virt_name, tester); else if (tester < 80) strnfmt(vir_desc, 80, "You are a champion of %s (%d).", virt_name, tester); else if (tester < 100) strnfmt(vir_desc, 80, "You are a great champion of %s (%d).", virt_name, tester); else strnfmt(vir_desc, 80, "You are the living embodiment of %s (%d).", virt_name, tester); strcpy(v_string[v_nr], vir_desc); info[i++] = v_string[v_nr]; } /* Racial powers... */ for (x = 0; x < MAX_RACE_POWERS; x++) { mut_ptr = &race_powers[x]; if ((mut_ptr->which == p_ptr->rp.prace) && (plev >= mut_ptr->level)) { info[i++] = mut_ptr->desc_text; } } /* Activatble mutations */ for (x = 0; x < MUT_PER_SET * 3; x++) { mut_ptr = &mutations[x]; /* * Only show activatable mutations if we * are of sufficiently high level */ if ((x < MUT_PER_SET) && (mut_ptr->level > plev)) continue; /* Do we have the mutation? */ if (player_has_mut(x)) { info[i++] = mut_ptr->desc_text; } } if (p_ptr->tim.blind) { info[i++] = "You cannot see."; } if (p_ptr->tim.confused) { info[i++] = "You are confused."; } if (p_ptr->tim.afraid) { info[i++] = "You are terrified."; } if (p_ptr->tim.cut) { info[i++] = "You are bleeding."; } if (p_ptr->tim.stun) { info[i++] = "You are stunned."; } if (p_ptr->tim.poisoned) { info[i++] = "You are poisoned."; } if (p_ptr->tim.image) { info[i++] = "You are hallucinating."; } if (FLAG(p_ptr, TR_AGGRAVATE)) { info[i++] = "You aggravate monsters."; } if (FLAG(p_ptr, TR_TELEPORT)) { info[i++] = "Your position is very uncertain."; } if (FLAG(p_ptr, TR_CANT_EAT)) { info[i++] = "You cannot survive on normal food."; } if (p_ptr->tim.blessed) { info[i++] = "You feel righteous."; } if (p_ptr->tim.hero) { info[i++] = "You feel heroic."; } if (p_ptr->tim.shero) { info[i++] = "You are in a battle rage."; } if (p_ptr->tim.protevil || (FLAG(p_ptr, TR_SLAY_EVIL))) { info[i++] = "You are protected from evil."; } if (FLAG(p_ptr, TR_SLAY_ANIMAL)) { info[i++] = "You are protected from animals."; } if (FLAG(p_ptr, TR_SLAY_UNDEAD)) { info[i++] = "You are protected from undead."; } if (FLAG(p_ptr, TR_SLAY_DEMON)) { info[i++] = "You are protected from demons."; } if (FLAG(p_ptr, TR_SLAY_ORC)) { info[i++] = "You are protected from orcs."; } if (FLAG(p_ptr, TR_SLAY_TROLL)) { info[i++] = "You are protected from trolls."; } if (FLAG(p_ptr, TR_SLAY_GIANT)) { info[i++] = "You are protected from giants."; } if (FLAG(p_ptr, TR_SLAY_DRAGON)) { info[i++] = "You are protected from dragons."; } if (p_ptr->tim.shield) { info[i++] = "You are protected by a mystic shield."; } if (p_ptr->tim.invuln) { info[i++] = "You are temporarily invulnerable."; } if (p_ptr->tim.wraith_form) { info[i++] = "You are temporarily incorporeal."; } if (p_ptr->state.confusing) { info[i++] = "Your hands are glowing dull red."; } if (p_ptr->state.searching) { info[i++] = "You are looking around very carefully."; } if (p_ptr->new_spells) { info[i++] = "You can learn some spells/prayers."; } if (p_ptr->tim.word_recall) { info[i++] = "You will soon be recalled."; } if (p_ptr->see_infra) { info[i++] = "Your eyes are sensitive to infrared light."; } if (FLAG(p_ptr, TR_SEE_INVIS)) { info[i++] = "You can see invisible creatures."; } if (FLAG(p_ptr, TR_FEATHER)) { info[i++] = "You can fly."; } if (FLAG(p_ptr, TR_FREE_ACT)) { info[i++] = "You have free action."; } if (FLAG(p_ptr, TR_MUTATE)) { info[i++] = "You mutate spontaneously."; } if (FLAG(p_ptr, TR_PATRON)) { info[i++] = "You have a chaos patron."; } if (FLAG(p_ptr, TR_STRANGE_LUCK)) { info[i++] = "Chance is warped around you."; } if (FLAG(p_ptr, TR_PASS_WALL)) { info[i++] = "You can pass through solid rock."; } if ((FLAG(p_ptr, TR_REGEN)) && (!(p_ptr->muta3 & MUT3_REGEN))) { if (FLAG(p_ptr, TR_SLOW_HEAL)) info[i++] = "You regenerate slowly."; else info[i++] = "You regenerate quickly."; } else if (FLAG(p_ptr, TR_SLOW_HEAL)) { info[i++] = "You regenerate very slowly."; } if ((FLAG(p_ptr, TR_SLOW_DIGEST)) && (FLAG(p_ptr, TR_SLOW_HEAL))) { info[i++] = "Your appetite is very small."; } else if ((FLAG(p_ptr, TR_SLOW_DIGEST)) || (FLAG(p_ptr, TR_SLOW_HEAL))) { info[i++] = "Your appetite is small."; } if ((FLAG(p_ptr, TR_TELEPATHY)) && (!(p_ptr->muta3 & MUT3_ESP))) { info[i++] = "You have ESP."; } if (FLAG(p_ptr, TR_HOLD_LIFE)) { info[i++] = "You have a firm hold on your life force."; } if (FLAG(p_ptr, TR_REFLECT)) { info[i++] = "You reflect arrows and bolts."; } if (FLAG(p_ptr, TR_SH_FIRE)) { info[i++] = "You are surrounded with a fiery aura."; } if (FLAG(p_ptr, TR_SH_ELEC)) { info[i++] = "You are surrounded with electricity."; } if (FLAG(p_ptr, TR_SH_ACID)) { info[i++] = "You are surrounded by an acidic cloud."; } if (FLAG(p_ptr, TR_SH_COLD)) { info[i++] = "You are surrounded by a freezing aura."; } if (FLAG(p_ptr, TR_NO_MAGIC)) { info[i++] = "You are surrounded by an anti-magic shell."; } if (FLAG(p_ptr, TR_NO_TELE)) { info[i++] = "You cannot teleport."; } if (FLAG(p_ptr, TR_LITE)) { info[i++] = "You have a source of permanent light."; } res = res_acid_lvl(); if (res == 0) { info[i++] = "You are completely immune to acid."; } else if (res <= 25) { info[i++] = "You resist acid exceptionally well."; } else if (res <= 50) { info[i++] = "You are resistant to acid."; } else if (res < 100) { info[i++] = "You are somewhat resistant to acid."; } else if (res > 100) { info[i++] = "You are vulnerable to acid."; } res = res_elec_lvl(); if (res == 0) { info[i++] = "You are completely immune to lightning."; } else if (res <= 25) { info[i++] = "You resist lightning exceptionally well."; } else if (res <= 50) { info[i++] = "You are resistant to lightning."; } else if (res < 100) { info[i++] = "You are somewhat resistant to lightning."; } else if (res > 100) { info[i++] = "You are vulnerable to lightning."; } res = res_fire_lvl(); if (res == 0) { info[i++] = "You are completely immune to fire."; } else if (res <= 25) { info[i++] = "You resist fire exceptionally well."; } else if (res <= 50) { info[i++] = "You are resistant to fire."; } else if (res < 100) { info[i++] = "You are somewhat resistant to fire."; } else if (res > 100) { info[i++] = "You are vulnerable to fire."; } res = res_cold_lvl(); if (res == 0) { info[i++] = "You are completely immune to cold."; } else if (res <= 25) { info[i++] = "You resist cold exceptionally well."; } else if (res <= 50) { info[i++] = "You are resistant to cold."; } else if (res < 100) { info[i++] = "You are somewhat resistant to cold."; } else if (res > 100) { info[i++] = "You are vulnerable to cold."; } res = res_pois_lvl(); if (res == 0) { info[i++] = "You are completely immune to poison."; } else if (res <= 25) { info[i++] = "You resist poison exceptionally well."; } else if (res <= 50) { info[i++] = "You are resistant to poison."; } else if (res < 100) { info[i++] = "You are somewhat resistant to poison."; } else if (res > 100) { info[i++] = "You are vulnerable to poison."; } if (FLAG(p_ptr, TR_IM_LITE)) { info[i++] = "You are completely immune to bright light."; } else if (FLAG(p_ptr, TR_RES_LITE)) { if (FLAG(p_ptr, TR_HURT_LITE)) info[i++] = "You are somewhat resistant to bright light."; else info[i++] = "You are resistant to bright light."; } else if (FLAG(p_ptr, TR_HURT_LITE)) { info[i++] = "You are vulnerable to bright light."; } if (FLAG(p_ptr, TR_IM_DARK)) { info[i++] = "You are completely immune to darkness."; } else if (FLAG(p_ptr, TR_RES_DARK)) { if (FLAG(p_ptr, TR_HURT_DARK)) info[i++] = "You are somewhat resistant to darkness."; else info[i++] = "You are resistant to darkness."; } else if (FLAG(p_ptr, TR_HURT_DARK)) { info[i++] = "You are vulnerable to darkness."; } if (FLAG(p_ptr, TR_RES_CONF)) { info[i++] = "You are resistant to confusion."; } if (FLAG(p_ptr, TR_RES_SOUND)) { info[i++] = "You are resistant to sonic attacks."; } if (FLAG(p_ptr, TR_RES_DISEN)) { info[i++] = "You are resistant to disenchantment."; } if (FLAG(p_ptr, TR_RES_CHAOS)) { info[i++] = "You are resistant to chaos."; } if (FLAG(p_ptr, TR_RES_SHARDS)) { info[i++] = "You are resistant to blasts of shards."; } if (FLAG(p_ptr, TR_RES_NEXUS)) { info[i++] = "You are resistant to nexus attacks."; } if (FLAG(p_ptr, TR_RES_NETHER)) { info[i++] = "You are resistant to nether forces."; } if ((FLAG(p_ptr, TR_RES_FEAR)) && (!(p_ptr->muta3 & MUT3_ESP))) { info[i++] = "You are completely fearless."; } if (FLAG(p_ptr, TR_RES_BLIND)) { info[i++] = "Your eyes are resistant to blindness."; } if (FLAG(p_ptr, TR_SUST_STR)) { info[i++] = "Your strength is sustained."; } if (FLAG(p_ptr, TR_SUST_INT)) { info[i++] = "Your intelligence is sustained."; } if (FLAG(p_ptr, TR_SUST_WIS)) { info[i++] = "Your wisdom is sustained."; } if (FLAG(p_ptr, TR_SUST_CON)) { info[i++] = "Your constitution is sustained."; } if (FLAG(p_ptr, TR_SUST_DEX)) { info[i++] = "Your dexterity is sustained."; } if (FLAG(p_ptr, TR_SUST_CHR)) { info[i++] = "Your charisma is sustained."; } if (FLAG(p_ptr, TR_GHOUL_TOUCH)) { info[i++] = "Your touch paralyzes your foes."; } if (FLAG(p_ptr, TR_WILD_SHOT)) { info[i++] = "Your shots are not affected by forest."; } if (FLAG(p_ptr, TR_WILD_WALK)) { info[i++] = "You are not hindered by natural terrain."; } if (ff[0] & (TR0_STR)) { info[i++] = "Your strength is affected by your equipment."; } if (ff[0] & (TR0_INT)) { info[i++] = "Your intelligence is affected by your equipment."; } if (ff[0] & (TR0_WIS)) { info[i++] = "Your wisdom is affected by your equipment."; } if (ff[0] & (TR0_DEX)) { info[i++] = "Your dexterity is affected by your equipment."; } if (ff[0] & (TR0_CON)) { info[i++] = "Your constitution is affected by your equipment."; } if (ff[0] & (TR0_CHR)) { info[i++] = "Your charisma is affected by your equipment."; } if (ff[0] & (TR0_STEALTH)) { info[i++] = "Your stealth is affected by your equipment."; } if (ff[0] & (TR0_SEARCH)) { info[i++] = "Your searching ability is affected by your equipment."; } if (ff[0] & (TR0_INFRA)) { info[i++] = "Your infravision is affected by your equipment."; } if (ff[0] & (TR0_TUNNEL)) { info[i++] = "Your digging ability is affected by your equipment."; } if (ff[0] & (TR0_SPEED)) { info[i++] = "Your speed is affected by your equipment."; } if (ff[0] & (TR0_BLOWS)) { info[i++] = "Your attack speed is affected by your equipment."; } /* Access the current weapon */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Analyze the weapon */ if (o_ptr->k_idx) { /* Indicate Blessing */ if (FLAG(o_ptr, TR_BLESSED)) { info[i++] = "Your weapon has been blessed by the gods."; } if (FLAG(o_ptr, TR_CHAOTIC)) { info[i++] = "Your weapon is branded with the Sign of Logrus."; } /* Hack */ if (FLAG(o_ptr, TR_IMPACT)) { info[i++] = "The impact of your weapon can cause earthquakes."; } if (FLAG(o_ptr, TR_VORPAL)) { info[i++] = "Your weapon is very sharp."; } if (FLAG(o_ptr, TR_VAMPIRIC)) { info[i++] = "Your weapon drains life from your foes."; } /* Special "Attack Bonuses" */ if (ff[0] & (TR0_BRAND_ACID)) { info[i++] = "Your weapon melts your foes."; } if (ff[0] & (TR0_BRAND_ELEC)) { info[i++] = "Your weapon shocks your foes."; } if (ff[0] & (TR0_BRAND_FIRE)) { info[i++] = "Your weapon burns your foes."; } if (ff[0] & (TR0_BRAND_COLD)) { info[i++] = "Your weapon freezes your foes."; } if (ff[0] & (TR0_BRAND_POIS)) { info[i++] = "Your weapon poisons your foes."; } /* Special "slay" flags */ if (FLAG(o_ptr, TR_SLAY_ANIMAL)) { info[i++] = "Your weapon strikes at animals with extra force."; } if (FLAG(o_ptr, TR_SLAY_EVIL)) { info[i++] = "Your weapon strikes at evil with extra force."; } if (FLAG(o_ptr, TR_SLAY_UNDEAD)) { info[i++] = "Your weapon strikes at undead with holy wrath."; } if (FLAG(o_ptr, TR_SLAY_DEMON)) { info[i++] = "Your weapon strikes at demons with holy wrath."; } if (FLAG(o_ptr, TR_SLAY_ORC)) { info[i++] = "Your weapon is especially deadly against orcs."; } if (FLAG(o_ptr, TR_SLAY_TROLL)) { info[i++] = "Your weapon is especially deadly against trolls."; } if (FLAG(o_ptr, TR_SLAY_GIANT)) { info[i++] = "Your weapon is especially deadly against giants."; } if (FLAG(o_ptr, TR_SLAY_DRAGON)) { info[i++] = "Your weapon is especially deadly against dragons."; } /* Special "kill" flags */ if (ff[0] & (TR0_KILL_DRAGON)) { info[i++] = "Your weapon is a great bane of dragons."; } if (ff[1] & (TR1_THROW)) { info[i++] = "Your weapon can be thrown well."; } if (ff[3] & (TR3_PSI_CRIT)) { info[i++] = "Your weapon uses magical power to strike great blows."; } } /* Save the screen */ screen_save(); /* Calculate how much lines we can put on a page */ height = MIN(Term->hgt - 3, i + 2); /* Erase the screen */ clear_region(13, 1, height + 1); /* Label the information */ prtf(15, 1, " Your Attributes:"); /* We will print on top of the map (column 13) */ for (k = 2, j = 0; j < i; j++) { /* Show the info */ prtf(15, k++, info[j]); /* Every heightth entry, start a new page */ if ((k == height) && (j + 1 < i)) { prtf(15, k, "-- more --"); (void)inkey(); for (; k > 2; k--) prtf(15, k, ""); } } /* Pause */ prtf(13, k, "[Press any key to continue]"); (void)inkey(); /* Restore the screen */ screen_load(); } static int report_magics_aux(int dur) { if (dur <= 5) { return 0; } else if (dur <= 10) { return 1; } else if (dur <= 20) { return 2; } else if (dur <= 50) { return 3; } else if (dur <= 100) { return 4; } else if (dur <= 200) { return 5; } else { return 6; } } static cptr report_magic_durations[] = { "for a short time", "for a little while", "for a while", "for a long while", "for a long time", "for a very long time", "for an incredibly long time", "until you hit a monster" }; /* * Report all currently active magical effects. */ void report_magics(void) { int i = 0, j, k; cptr info[128]; int info2[128]; if (p_ptr->tim.blind) { info2[i] = report_magics_aux(p_ptr->tim.blind); info[i++] = "You cannot see"; } if (p_ptr->tim.confused) { info2[i] = report_magics_aux(p_ptr->tim.confused); info[i++] = "You are confused"; } if (p_ptr->tim.afraid) { info2[i] = report_magics_aux(p_ptr->tim.afraid); info[i++] = "You are terrified"; } if (p_ptr->tim.poisoned) { info2[i] = report_magics_aux(p_ptr->tim.poisoned); info[i++] = "You are poisoned"; } if (p_ptr->tim.image) { info2[i] = report_magics_aux(p_ptr->tim.image); info[i++] = "You are hallucinating"; } if (p_ptr->tim.blessed) { info2[i] = report_magics_aux(p_ptr->tim.blessed); info[i++] = "You feel righteous"; } if (p_ptr->tim.hero) { info2[i] = report_magics_aux(p_ptr->tim.hero); info[i++] = "You feel heroic"; } if (p_ptr->tim.shero) { info2[i] = report_magics_aux(p_ptr->tim.shero); info[i++] = "You are in a battle rage"; } if (p_ptr->tim.protevil) { info2[i] = report_magics_aux(p_ptr->tim.protevil); info[i++] = "You are protected from evil"; } if (p_ptr->tim.shield) { info2[i] = report_magics_aux(p_ptr->tim.shield); info[i++] = "You are protected by a mystic shield"; } if (p_ptr->tim.invuln) { info2[i] = report_magics_aux(p_ptr->tim.invuln); info[i++] = "You are invulnerable"; } if (p_ptr->tim.wraith_form) { info2[i] = report_magics_aux(p_ptr->tim.wraith_form); info[i++] = "You are incorporeal"; } if (p_ptr->state.confusing) { info2[i] = 7; info[i++] = "Your hands are glowing dull red."; } if (p_ptr->tim.word_recall) { info2[i] = report_magics_aux(p_ptr->tim.word_recall); info[i++] = "You are waiting to be recalled"; } if (p_ptr->tim.oppose_acid) { info2[i] = report_magics_aux(p_ptr->tim.oppose_acid); info[i++] = "You are resistant to acid"; } if (p_ptr->tim.oppose_elec) { info2[i] = report_magics_aux(p_ptr->tim.oppose_elec); info[i++] = "You are resistant to lightning"; } if (p_ptr->tim.oppose_fire) { info2[i] = report_magics_aux(p_ptr->tim.oppose_fire); info[i++] = "You are resistant to fire"; } if (p_ptr->tim.oppose_cold) { info2[i] = report_magics_aux(p_ptr->tim.oppose_cold); info[i++] = "You are resistant to cold"; } if (p_ptr->tim.oppose_pois) { info2[i] = report_magics_aux(p_ptr->tim.oppose_pois); info[i++] = "You are resistant to poison"; } /* Save the screen */ screen_save(); /* Erase the screen */ clear_region(13, 1, 23); /* Label the information */ prtf(15, 1, " Your Current Magic:"); /* We will print on top of the map (column 13) */ for (k = 2, j = 0; j < i; j++) { /* Show the info */ prtf(15, k++, "%s %s.", info[j], report_magic_durations[info2[j]]); /* Every 20 entries (lines 2 to 21), start over */ if ((k == 22) && (j + 1 < i)) { prtf(15, k, "-- more --"); (void)inkey(); for (; k > 2; k--) prtf(15, k, ""); } } /* Pause */ prtf(13, k, "[Press any key to continue]"); (void)inkey(); /* Restore the screen */ screen_load(); } /* * Detect things on a square. * * The location (x,y) is passed to the tester function pointer. * * This is an "engine" function that does all the work, and * prevents a huge amount of code duplication. */ static bool detect_sq_aux(bool tester(int x, int y), cptr msg) { int px = p_ptr->px; int py = p_ptr->py; int x, y; bool detect = FALSE; /* Scan a radius MAX_DETECT circle */ for (y = py - MAX_DETECT; y <= py + MAX_DETECT; y++) { for (x = px - MAX_DETECT; x <= px + MAX_DETECT; x++) { if (!in_bounds2(x, y)) continue; if (distance(px, py, x, y) > MAX_DETECT) continue; /* Detect something? */ if (tester(x, y)) detect = TRUE; } } if (detect) { /* Describe */ if (msg) msgf(msg); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } return (detect); } /* Test for the existance of traps here */ static bool trap_tester(int x, int y) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Save the 'detected' status for this square */ pc_ptr->player |= GRID_DTCT; /* Detect traps */ return (field_detect_type(c_ptr, FTYPE_TRAP)); } /* Test for the existance of traps here */ static bool trap_ident_tester(int x, int y) { cave_type *c_ptr = area(x, y); /* Detect traps */ return (field_detect_type(c_ptr, FTYPE_TRAP)); } /* * Detect all traps in range */ bool detect_traps(bool ident) { /* The source is identified? */ if (ident || detect_sq_aux(trap_ident_tester, NULL)) { /* Have detected traps on this level */ p_ptr->state.detected = TRUE; /* Detect them properly now */ return(detect_sq_aux(trap_tester, "You sense the presence of traps!")); } return (FALSE); } /* * Place a random type of normal door at the given location. * Use this in-game */ void create_closed_door(int x, int y) { int tmp; /* Invisible wall */ if (ironman_nightmare && one_in_(666)) { /* Create invisible wall */ cave_set_feat(x, y, the_floor()); (void)place_field(x, y, FT_WALL_INVIS); return; } /* Choose an object */ tmp = randint0(400); /* Closed doors (300/400) */ if (tmp < 300) { /* Create closed door */ cave_set_feat(x, y, FEAT_CLOSED); } /* Locked doors (99/400) */ else if (tmp < 399) { /* Create locked door */ make_lockjam_door(x, y, randint1(10) + p_ptr->depth / 10, FALSE); } /* Stuck doors (1/400) */ else { /* Create jammed door */ make_lockjam_door(x, y, randint1(5) + p_ptr->depth / 10, TRUE); } } /* Test for the existance of doors here */ static bool door_tester(int x, int y) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Detect secret doors */ if (c_ptr->feat == FEAT_SECRET) { /* Pick a door */ create_closed_door(x, y); } /* Detect doors */ if ((c_ptr->feat == FEAT_CLOSED) || (c_ptr->feat == FEAT_OPEN) || (c_ptr->feat == FEAT_BROKEN)) { /* Hack -- Memorize */ remember_grid(c_ptr, pc_ptr); /* Redraw */ lite_spot(x, y); /* Obvious */ return (TRUE); } return (FALSE); } /* * Detect all doors in range */ bool detect_doors(void) { return (detect_sq_aux(door_tester, "You sense the presence of doors!")); } /* Test for the existance of stairs here */ static bool stair_tester(int x, int y) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Detect stairs */ if ((c_ptr->feat == FEAT_LESS) || (c_ptr->feat == FEAT_MORE)) { /* Hack -- Memorize */ remember_grid(c_ptr, pc_ptr); /* Redraw */ lite_spot(x, y); /* Obvious */ return (TRUE); } return (FALSE); } /* * Detect all stairs in range */ bool detect_stairs(void) { return (detect_sq_aux(stair_tester, "You sense the presence of stairs!")); } /* Test for the existance of treasure here */ static bool treasure_tester(int x, int y) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); /* Magma/Quartz + Known Gold */ if ((c_ptr->feat == FEAT_MAGMA_K) || (c_ptr->feat == FEAT_QUARTZ_K)) { /* Hack -- Memorize */ remember_grid(c_ptr, pc_ptr); /* Redraw */ lite_spot(x, y); /* Detect */ return (TRUE); } return (FALSE); } /* * Detect any treasure in range */ bool detect_treasure(void) { return (detect_sq_aux(treasure_tester, "You sense the presence of buried treasure!")); } /* * Detect objects with a given property. * * The object o_ptr is passed to the tester function pointer. * * This is an "engine" function that does all the work, and * prevents a huge amount of code duplication. */ static bool detect_obj_aux(bool tester(const object_type *o_ptr), cptr msg) { int px = p_ptr->px; int py = p_ptr->py; int x, y, i; bool detect = FALSE; /* Scan objects */ for (i = 1; i < o_max; i++) { object_type *o_ptr = &o_list[i]; /* Skip dead objects */ if (!o_ptr->k_idx) continue; /* Skip held objects */ if (!(o_ptr->ix || o_ptr->iy)) continue; /* Location */ y = o_ptr->iy; x = o_ptr->ix; if (distance(px, py, x, y) > MAX_DETECT) continue; /* Detect */ if (tester(o_ptr)) { /* Detect */ detect = TRUE; /* Hack -- memorize it */ o_ptr->info |= OB_SEEN; /* Redraw */ lite_spot(x, y); } } if (detect) { /* Describe */ msgf(msg); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } return (detect); } /* Gold item? */ static bool gold_tester(const object_type *o_ptr) { /* Detect "gold" objects */ return (o_ptr->tval == TV_GOLD); } /* * Detect all "gold" objects in range */ bool detect_objects_gold(void) { return (detect_obj_aux(gold_tester, "You sense the presence of treasure!") || detect_monsters_string("$")); } /* Real nongold item? */ static bool nongold_tester(const object_type *o_ptr) { return (o_ptr->tval != TV_GOLD); } /* * Detect all "normal" objects in range */ bool detect_objects_normal(void) { return (detect_obj_aux(nongold_tester, "You sense the presence of objects!") || detect_monsters_string("!=?|")); } static bool magic_tester(const object_type *o_ptr) { /* Examine the tval */ int tv = o_ptr->tval; /* Artifacts, misc magic items, or enchanted wearables */ return (o_ptr->xtra_name || (tv == TV_AMULET) || (tv == TV_RING) || (tv == TV_STAFF) || (tv == TV_WAND) || (tv == TV_ROD) || (tv == TV_SCROLL) || (tv == TV_POTION) || (tv == TV_LIFE_BOOK) || (tv == TV_SORCERY_BOOK) || (tv == TV_NATURE_BOOK) || (tv == TV_CHAOS_BOOK) || (tv == TV_DEATH_BOOK) || (tv == TV_TRUMP_BOOK) || (tv == TV_ARCANE_BOOK) || ((o_ptr->to_a > 0) || (o_ptr->to_h + o_ptr->to_d > 0))); } /* * Detect all "magic" objects in range. * * This will light up all spaces with "magic" items, including artifacts, * ego-items, potions, scrolls, books, rods, wands, staves, amulets, rings, * and "enchanted" items of the "good" variety. * * It can probably be argued that this function is now too powerful. */ bool detect_objects_magic(void) { return (detect_obj_aux(magic_tester, "You sense the presence of magic objects!")); } /* * Detect monster with a given property. * * The monster m_ptr is passed to the tester function pointer, * along with the pointer to the extra parameters. * * This is an "engine" function that does all the work, and * prevents a huge amount of code duplication. */ static bool detect_mon_aux(bool tester(const monster_type *m_ptr, const vptr data), cptr msg, const vptr data) { int px = p_ptr->px; int py = p_ptr->py; int x, y, i; bool detect = FALSE; /* Scan monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Location */ y = m_ptr->fy; x = m_ptr->fx; if (distance(px, py, x, y) > MAX_DETECT) continue; /* Do not detect mimics */ if (m_ptr->smart & (SM_MIMIC)) continue; /* Detect monsters satisfying restriction */ if (tester(m_ptr, data)) { /* Update monster recall window */ if (p_ptr->monster_race_idx == m_ptr->r_idx) { /* Window stuff */ p_ptr->window |= (PW_MONSTER); } /* Repair visibility later */ p_ptr->change |= (PC_REPAIR); /* Hack -- Detect monster */ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW); /* Update the monster */ update_mon(i, FALSE); /* Detect */ detect = TRUE; } } if (detect) { /* Describe */ msgf(msg); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } return (detect); } /* Normal monster? */ static bool norm_mon_tester(const monster_type *m_ptr, vptr data) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Ignore parameter */ (void) data; /* Do not detect mimics */ if (m_ptr->smart & (SM_MIMIC)) return (FALSE); /* Detect all non-invisible monsters */ if (!FLAG(r_ptr, RF_INVISIBLE) || FLAG(p_ptr, TR_SEE_INVIS) || p_ptr->tim.invis) return (TRUE); return (FALSE); } /* * Detect all "normal" monsters in range */ bool detect_monsters_normal(void) { return (detect_mon_aux(norm_mon_tester, "You sense the presence of monsters!", NULL)); } /* Invisible monster? */ static bool invis_mon_tester(const monster_type *m_ptr, vptr data) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Ignore parameter */ (void) data; /* Detect invisible monsters */ return (FLAG(r_ptr, RF_INVISIBLE)); } /* * Detect all "invisible" monsters in range */ bool detect_monsters_invis(void) { return (detect_mon_aux(invis_mon_tester, "You sense the presence of invisible creatures!", NULL)); } /* Evil monster? */ static bool evil_mon_tester(const monster_type *m_ptr, vptr data) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Ignore parameter */ (void) data; /* Detect evil monsters */ if (FLAG(r_ptr, RF_EVIL)) { /* Take note that they are evil */ r_ptr->r_flags[2] |= (RF2_EVIL); return (TRUE); } return (FALSE); } /* * Detect all "evil" monsters in range */ bool detect_monsters_evil(void) { return (detect_mon_aux(evil_mon_tester, "You sense the presence of evil creatures!", NULL)); } /* Nonliving monster? */ static bool nonlive_mon_tester(const monster_type *m_ptr, vptr data) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Ignore parameter */ (void) data; /* Not living? */ return (!monster_living(r_ptr)); } /* * Detect all "nonliving", "undead" or "demonic" monsters in range */ bool detect_monsters_nonliving(void) { return (detect_mon_aux(nonlive_mon_tester, "You sense the presence of unnatural beings!", NULL)); } /* Living monster? */ static bool live_mon_tester(const monster_type *m_ptr, vptr data) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Ignore parameter */ (void) data; /* Not living? */ return (monster_living(r_ptr)); } /* * Detect all "living" monsters in range */ bool detect_monsters_living(void) { return (detect_mon_aux(live_mon_tester, "You sense the presence of natural beings!", NULL)); } /* Is a monster in the list of races? */ static bool race_mon_tester(const monster_type *m_ptr, vptr data) { cptr match; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Get string to compare with */ match = (cptr) data; /* Detect monsters with the same symbol */ return(strchr(match, r_ptr->d_char) ? TRUE : FALSE); } /* * Detect all (string) monsters in range */ bool detect_monsters_string(cptr match) { return (detect_mon_aux(race_mon_tester, "You sense the presence of monsters!", (vptr) match)); } /* Generic monster tester */ static bool flag_mon_tester(const monster_type *m_ptr, vptr data) { /* Get flags to compare with */ const u32b flag = *((const u32b *) data); monster_race *r_ptr = &r_info[m_ptr->r_idx]; if (r_ptr->flags[2] & flag) { /* Take note that they are something */ r_ptr->r_flags[2] |= (flag); return (TRUE); } return (FALSE); } /* * A "generic" detect monsters routine, tagged to flags3 */ bool detect_monsters_xxx(u32b match_flag) { cptr desc_monsters; /* Describe */ switch (match_flag) { case RF2_DEMON: desc_monsters = "You sense the presence of demons!"; break; case RF2_UNDEAD: desc_monsters = "You sense the presence of the undead!"; break; default: desc_monsters = "You sense the presence of weird monsters!"; } /* Result (note that ints might only be 16bit...) */ return (detect_mon_aux(flag_mon_tester, desc_monsters, (vptr) &match_flag)); } /* * Detect everything */ bool detect_all(void) { bool detect = FALSE; /* Detect everything */ if (detect_traps(TRUE)) detect = TRUE; if (detect_doors()) detect = TRUE; if (detect_stairs()) detect = TRUE; if (detect_treasure()) detect = TRUE; if (detect_objects_gold()) detect = TRUE; if (detect_objects_normal()) detect = TRUE; if (detect_monsters_invis()) detect = TRUE; if (detect_monsters_normal()) detect = TRUE; /* Result */ return (detect); } /* * Apply a "project()" directly to all viewable monsters * * Note that affected monsters are NOT auto-tracked by this usage. * * To avoid misbehavior when monster deaths have side-effects, * this is done in two passes. -- JDL */ bool project_hack(int typ, int dam) { int i, x, y; u16b flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE; bool obvious = FALSE; /* Mark all (nearby) monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Location */ y = m_ptr->fy; x = m_ptr->fx; /* Paranoia */ if (!in_boundsp(x, y)) continue; /* Require line of sight */ if (!player_has_los_grid(parea(x, y))) continue; /* Mark the monster */ m_ptr->mflag |= (MFLAG_TEMP); } /* Affect all marked monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Skip unmarked monsters */ if (!(m_ptr->mflag & (MFLAG_TEMP))) continue; /* Remove mark */ m_ptr->mflag &= ~(MFLAG_TEMP); /* Jump directly to the target monster */ if (project(0, 0, m_ptr->fx, m_ptr->fy, dam, typ, flg)) obvious = TRUE; } /* Result */ return (obvious); } /* * Speed monsters */ bool speed_monsters(void) { return (project_hack(GF_OLD_SPEED, p_ptr->lev)); } /* * Slow monsters */ bool slow_monsters(void) { return (project_hack(GF_OLD_SLOW, p_ptr->lev)); } /* * Sleep monsters */ bool sleep_monsters(void) { return (project_hack(GF_OLD_SLEEP, p_ptr->lev)); } /* * Banish evil monsters */ bool banish_evil(int dist) { return (project_hack(GF_AWAY_EVIL, dist)); } /* * Turn undead */ bool turn_undead(void) { bool tester = (project_hack(GF_TURN_UNDEAD, p_ptr->lev)); if (tester) chg_virtue(V_UNLIFE, -1); return tester; } /* * Dispel undead monsters */ bool dispel_undead(int dam) { bool tester = (project_hack(GF_DISP_UNDEAD, dam)); if (tester) chg_virtue(V_UNLIFE, -2); return tester; } /* * Dispel evil monsters */ bool dispel_evil(int dam) { return (project_hack(GF_DISP_EVIL, dam)); } /* * Dispel good monsters */ bool dispel_good(int dam) { return (project_hack(GF_DISP_GOOD, dam)); } /* * Dispel all monsters */ bool dispel_monsters(int dam) { return (project_hack(GF_DISP_ALL, dam)); } /* * Dispel 'living' monsters */ bool dispel_living(int dam) { return (project_hack(GF_DISP_LIVING, dam)); } /* * Dispel demons */ bool dispel_demons(int dam) { return (project_hack(GF_DISP_DEMON, dam)); } /* * Raise the dead */ bool raise_dead(int x, int y, bool pet) { s16b i; int fx, fy; bool obvious = FALSE; cave_type *c_ptr; /* Check all (nearby) fields */ for (i = 1; i < fld_max; i++) { field_type *f_ptr = &fld_list[i]; /* Paranoia -- Skip missing objects */ if (!f_ptr->t_idx) continue; /* Want a corpse / skeleton */ if (!(f_ptr->t_idx == FT_CORPSE || f_ptr->t_idx == FT_SKELETON)) continue; /* Location */ fy = f_ptr->fy; fx = f_ptr->fx; /* Require line of sight */ if (!los(fx, fy, x, y)) continue; c_ptr = area(fx, fy); /* Raise Corpses / Skeletons */ if (field_script_special(c_ptr, FTYPE_CORPSE, "i", LUA_VAR(pet))) { if (player_has_los_grid(parea(fx, fy))) obvious = TRUE; } } /* Result */ return (obvious); } /* * Wake up all monsters, and speed up "los" monsters. */ void aggravate_monsters(int who) { int i; bool sleep = FALSE; bool speed = FALSE; /* Aggravate everyone nearby */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Skip aggravating monster (or player) */ if (i == who) continue; /* Wake up nearby sleeping monsters */ if (m_ptr->cdis < MAX_SIGHT * 2) { /* Wake up */ if (m_ptr->csleep) { /* Wake up */ m_ptr->csleep = 0; sleep = TRUE; /* Redraw (later) if needed */ if (p_ptr->health_who == i) p_ptr->redraw |= (PR_HEALTH); } } /* Speed up monsters in line of sight */ if (player_has_los_grid(parea(m_ptr->fx, m_ptr->fy))) { /* Speed up (instantly) to racial base + 10 */ if (m_ptr->mspeed < r_ptr->speed + 10) { /* Speed up */ m_ptr->mspeed = r_ptr->speed + 10; speed = TRUE; } } } /* Messages */ if (speed) msgf("You feel a sudden stirring nearby!"); else if (sleep) msgf("You hear a sudden stirring in the distance!"); } /* * Delete all non-unique/non-quest monsters of a given "type" from the level */ bool genocide(int player_cast) { int i; char typ; bool result = FALSE; int msec = delay_factor * delay_factor * delay_factor; /* Mega-Hack -- Get a monster symbol */ (void)(get_com("Choose a monster race (by symbol) to genocide: ", &typ)); /* Delete the monsters of that "type" */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Hack -- Skip Unique Monsters */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Hack -- Skip Quest Monsters */ if (FLAG(r_ptr, RF_QUESTOR)) continue; /* Skip "wrong" monsters */ if (r_ptr->d_char != typ) continue; /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Delete the monster */ delete_monster_idx(i); if (player_cast) { /* Take damage */ take_hit(randint1(4), "the strain of casting Genocide"); } /* Visual feedback */ move_cursor_relative(p_ptr->px, p_ptr->py); /* Redraw */ p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Handle */ handle_stuff(); /* Fresh */ Term_fresh(); /* Delay */ Term_xtra(TERM_XTRA_DELAY, msec); /* Take note */ result = TRUE; } if (result) chg_virtue(V_VITALITY, -2); return (result); } /* * Delete all nearby (non-unique) monsters */ bool mass_genocide(int player_cast) { int i; bool result = FALSE; int msec = delay_factor * delay_factor * delay_factor; /* This needs to be rethought - the wilderness is a problem... */ /* Delete the (nearby) monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Hack -- Skip unique monsters */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Hack -- Skip Quest Monsters */ if (FLAG(r_ptr, RF_QUESTOR)) continue; /* Skip distant monsters */ if (m_ptr->cdis > MAX_SIGHT) continue; /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Delete the monster */ delete_monster_idx(i); if (player_cast) { /* Hack -- visual feedback */ take_hit(randint1(3), "the strain of casting Mass Genocide"); } move_cursor_relative(p_ptr->px, p_ptr->py); /* Redraw */ p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Handle */ handle_stuff(); /* Fresh */ Term_fresh(); /* Delay */ Term_xtra(TERM_XTRA_DELAY, msec); /* Note effect */ result = TRUE; } if (result) chg_virtue(V_VITALITY, -2); return (result); } /* * Probe nearby monsters */ bool probing(void) { int i; bool probe = FALSE; /* Probe all (nearby) monsters */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Require line of sight */ if (!player_has_los_grid(parea(m_ptr->fx, m_ptr->fy))) continue; /* Probe visible monsters */ if (m_ptr->ml) { /* Start the message */ if (!probe) msgf("Probing..."); /* Describe the monster */ msgf("%^v has %d hit points.", MONSTER_FMT(m_ptr, 0x04), m_ptr->hp); /* Learn all of the non-spell, non-treasure flags */ lore_do_probe(i); /* Probe worked */ probe = TRUE; } } /* Done */ if (probe) { if (probe) chg_virtue(V_KNOWLEDGE, 1); msgf("That's all."); } /* Result */ return (probe); } /* * The spell of destruction * * This spell "deletes" monsters (instead of "killing" them). * * Later we may use one function for both "destruction" and * "earthquake" by using the (removed) "full" parameter * to select "destruction". */ bool destroy_area(int x1, int y1, int r) { int y, x, k, t; bool flag = FALSE; cave_type *c_ptr; pcave_type *pc_ptr; /* Prevent destruction of town and wilderness */ if (!p_ptr->depth) { return (FALSE); } /* Big area of affect */ for (y = (y1 - r); y <= (y1 + r); y++) { for (x = (x1 - r); x <= (x1 + r); x++) { /* Skip illegal grids */ if (!in_boundsp(x, y)) continue; /* Extract the distance */ k = distance(x1, y1, x, y); /* Stay in the circle of death */ if (k > r) continue; /* Access the grid */ c_ptr = area(x, y); pc_ptr = parea(x, y); /* Lose room and vault */ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY); /* Lose light */ c_ptr->info &= ~(CAVE_GLOW); /* Hack -- Notice player affect */ if ((x == p_ptr->px) && (y == p_ptr->py)) { /* Hurt the player later */ flag = TRUE; /* Do not hurt this grid */ continue; } /* Hack -- Skip the epicenter */ if ((y == y1) && (x == x1)) continue; if (FLAG(&r_info[m_list[c_ptr->m_idx].r_idx], RF_QUESTOR)) { /* Heal the monster */ m_list[c_ptr->m_idx].hp = m_list[c_ptr->m_idx].maxhp; /* Try to teleport away quest monsters */ if (!teleport_away(c_ptr->m_idx, (r * 2) + 1)) continue; } else { /* Delete the monster (if any) */ delete_monster(x, y); } /* Fields can block destruction */ if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) continue; /* Destroy the fields on the square */ delete_field(x, y); /* Destroy "valid" grids */ if (cave_valid_grid(c_ptr)) { /* Delete objects */ delete_object(x, y); /* Wall (or floor) type */ t = randint0(200); /* Granite */ if (t < 20) { /* Create granite wall */ cave_set_feat(x, y, FEAT_WALL_EXTRA); } /* Quartz */ else if (t < 70) { /* Create quartz vein */ cave_set_feat(x, y, FEAT_QUARTZ); } /* Magma */ else if (t < 100) { /* Create magma vein */ cave_set_feat(x, y, FEAT_MAGMA); } /* Floor */ else { /* Create floor */ cave_set_feat(x, y, the_floor()); } } /* Hack - forget the square */ forget_grid(pc_ptr); } } /* Hack -- Affect player */ if (flag) { /* Message */ msgf("There is a searing blast of light!"); /* Blind the player */ if (!(FLAG(p_ptr, TR_RES_BLIND)) && !(FLAG(p_ptr, TR_RES_LITE)) && !(FLAG(p_ptr, TR_IM_LITE))) { /* Become blind */ (void)inc_blind(rand_range(10, 20)); } } /* Success */ return (TRUE); } /* * Induce an "earthquake" of the given radius at the given location. * * This will turn some walls into floors and some floors into walls. * * The player will take damage and "jump" into a safe grid if possible, * otherwise, he will "tunnel" through the rubble instantaneously. * * Monsters will take damage, and "jump" into a safe grid if possible, * otherwise they will be "buried" in the rubble, disappearing from * the level in the same way that they do when genocided. * * Note that thus the player and monsters (except eaters of walls and * passers through walls) will never occupy the same grid as a wall. * Note that as of now (2.7.8) no monster may occupy a "wall" grid, even * for a single turn, unless that monster can pass_walls or kill_walls. * This has allowed massive simplification of the "monster" code. */ bool earthquake(int cx, int cy, int r) { int py = p_ptr->py; int px = p_ptr->px; int i, t, y, x, yy, xx, dy, dx, oy, ox; int damage = 0; int sn = 0, sy = 0, sx = 0; bool hurt = FALSE; cave_type *c_ptr; pcave_type *pc_ptr; bool map[32][32]; /* Prevent destruction of town and wilderness */ if (!p_ptr->depth) { return (FALSE); } /* Paranoia -- Enforce maximum range */ if (r > 12) r = 12; /* Clear the "maximal blast" area */ for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) { map[y][x] = FALSE; } } /* Check around the epicenter */ for (dy = -r; dy <= r; dy++) { for (dx = -r; dx <= r; dx++) { /* Extract the location */ yy = cy + dy; xx = cx + dx; /* Skip illegal grids */ if (!in_boundsp(xx, yy)) continue; /* Skip distant grids */ if (distance(cx, cy, xx, yy) > r) continue; /* Access the grid */ c_ptr = area(xx, yy); pc_ptr = parea(xx, yy); /* Lose room and vault */ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY); /* Lose light */ c_ptr->info &= ~(CAVE_GLOW); /* Skip the epicenter */ if (!dx && !dy) continue; /* Skip most grids */ if (randint0(100) < 85) continue; /* Damage this grid */ map[16 + yy - cy][16 + xx - cx] = TRUE; /* Hack -- Take note of player damage */ if ((yy == py) && (xx == px)) hurt = TRUE; } } /* First, affect the player (if necessary) */ if (hurt) { /* Check around the player */ for (i = 0; i < 8; i++) { /* Access the location */ y = py + ddy_ddd[i]; x = px + ddx_ddd[i]; if (!in_bounds2(x, y)) continue; /* Access the grid */ c_ptr = area(x, y); /* Skip non-empty grids */ if (!cave_empty_grid(c_ptr)) continue; /* Important -- Skip "quake" grids */ if (map[16 + y - cy][16 + x - cx]) continue; /* Check for a field that blocks movement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) { continue; } /* Count "safe" grids */ sn++; /* Randomize choice */ if (randint0(sn) > 0) continue; /* Save the safe location */ sy = y; sx = x; } /* Random message */ switch (randint1(3)) { case 1: { msgf("The cave ceiling collapses!"); break; } case 2: { msgf("The cave floor twists in an unnatural way!"); break; } default: { msgf("The cave quakes! You are pummeled with debris!"); break; } } /* Hurt the player a lot */ if (!sn) { /* Message and damage */ msgf("You are severely crushed!"); damage = 300; } /* Destroy the grid, and push the player to safety */ else { /* Calculate results */ switch (randint1(3)) { case 1: { msgf("You nimbly dodge the blast!"); damage = 0; break; } case 2: { msgf("You are bashed by rubble!"); damage = damroll(10, 4); (void)inc_stun(randint1(50)); break; } case 3: { msgf("You are crushed between the floor and ceiling!"); damage = damroll(10, 4); (void)inc_stun(randint1(50)); break; } } /* Save old location */ oy = py; ox = px; /* Move the player */ py = sy; px = sx; /* Move the player */ p_ptr->py = sy; p_ptr->px = sx; /* Notice movement */ Term_move_player(); if (!p_ptr->depth) { /* Scroll wilderness */ p_ptr->wilderness_x = px; p_ptr->wilderness_y = py; move_wild(); } /* Redraw the old spot */ lite_spot(ox, oy); /* Redraw the new spot */ lite_spot(px, py); /* Process fields under the player. */ field_script(area(px, py), FIELD_ACT_PLAYER_ENTER, ""); /* Check for new panel */ verify_panel(); } /* Important -- no wall on player */ map[16 + py - cy][16 + px - cx] = FALSE; /* Take some damage */ if (damage) take_hit(damage, "an earthquake"); } /* Examine the quaked region */ for (dy = -r; dy <= r; dy++) { for (dx = -r; dx <= r; dx++) { /* Extract the location */ yy = cy + dy; xx = cx + dx; /* Skip unaffected grids */ if (!map[16 + yy - cy][16 + xx - cx]) continue; if (!in_bounds2(xx, yy)) continue; /* Access the grid */ c_ptr = area(xx, yy); /* Process monsters */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Quest monsters */ if (FLAG(r_ptr, RF_QUESTOR)) { /* No wall on quest monsters */ map[16 + yy - cy][16 + xx - cx] = FALSE; continue; } /* Most monsters cannot co-exist with rock */ if (!FLAG(r_ptr, RF_KILL_WALL) && !FLAG(r_ptr, RF_PASS_WALL)) { /* Assume not safe */ sn = 0; /* Monster can move to escape the wall */ if (!FLAG(r_ptr, RF_NEVER_MOVE)) { /* Look for safety */ for (i = 0; i < 8; i++) { /* Access the grid */ y = yy + ddy_ddd[i]; x = xx + ddx_ddd[i]; if (!in_bounds2(x, y)) continue; /* Access the grid */ c_ptr = area(x, y); /* Skip non-empty grids */ if (!cave_empty_grid(c_ptr)) continue; /* Not on player */ if ((y == py) && (x == px)) continue; /* Check for a field that blocks movement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) continue; /* * Test for fields that will not allow monsters to * be generated on them. (i.e. Glyph of warding) */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MPLACE)) continue; /* ... nor on the Pattern */ if (cave_pattern_grid(c_ptr)) { continue; } /* Important -- Skip "quake" grids */ if (map[16 + y - cy][16 + x - cx]) continue; /* Count "safe" grids */ sn++; /* Randomize choice */ if (randint0(sn) > 0) continue; /* Save the safe grid */ sy = y; sx = x; } } /* Scream in pain */ msgf("%^v wails out in pain!", MONSTER_FMT(m_ptr, 0)); /* Take damage from the quake */ damage = (sn ? damroll(4, 8) : (m_ptr->hp + 1)); /* Monster is certainly awake */ m_ptr->csleep = 0; /* Apply damage directly */ m_ptr->hp -= damage; /* Delete (not kill) "dead" monsters */ if (m_ptr->hp < 0) { /* Message */ msgf("%^v is embedded in the rock!", MONSTER_FMT(m_ptr, 0)); /* Delete the monster */ delete_monster(xx, yy); /* No longer safe */ sn = 0; } /* Hack -- Escape from the rock */ if (sn) { int m_idx = area(xx, yy)->m_idx; /* Update the new location */ area(sx, sy)->m_idx = m_idx; /* Update the old location */ area(xx, yy)->m_idx = 0; /* Move the monster */ m_ptr->fy = sy; m_ptr->fx = sx; /* Update the monster (new location) */ update_mon(m_idx, TRUE); /* Redraw the old grid */ lite_spot(xx, yy); /* Redraw the new grid */ lite_spot(sx, sy); } } } } } /* Examine the quaked region */ for (dy = -r; dy <= r; dy++) { for (dx = -r; dx <= r; dx++) { /* Extract the location */ yy = cy + dy; xx = cx + dx; /* Skip unaffected grids */ if (!map[16 + yy - cy][16 + xx - cx]) continue; if (!in_bounds2(xx, yy)) continue; /* Access the cave grid */ c_ptr = area(xx, yy); pc_ptr = parea(xx, yy); /* Paranoia -- never affect player */ if ((yy == py) && (xx == px)) continue; /* Fields can block destruction */ if (fields_have_flags(c_ptr, FIELD_INFO_PERM)) continue; /* Destroy the fields on the square */ delete_field(xx, yy); /* Destroy location (if valid) */ if (cave_valid_grid(c_ptr)) { bool floor = cave_floor_grid(c_ptr); /* Delete objects */ delete_object(xx, yy); /* Wall (or floor) type */ t = (floor ? randint0(100) : 200); /* Granite */ if (t < 20) { /* Create granite wall */ cave_set_feat(xx, yy, FEAT_WALL_EXTRA); } /* Quartz */ else if (t < 70) { /* Create quartz vein */ cave_set_feat(xx, yy, FEAT_QUARTZ); } /* Magma */ else if (t < 100) { /* Create magma vein */ cave_set_feat(xx, yy, FEAT_MAGMA); } /* Floor */ else { /* Create floor */ cave_set_feat(xx, yy, the_floor()); } } /* Hack - forget square */ forget_grid(pc_ptr); } } /* Update the health bar */ p_ptr->redraw |= (PR_HEALTH); /* Success */ return (TRUE); } /* * This routine clears the entire "temp" set. * * This routine will Perma-Lite all "temp" grids. * * This routine is used (only) by "lite_room()" * * Dark grids are illuminated. * * Also, process all affected monsters. * * SMART monsters always wake up when illuminated * NORMAL monsters wake up 1/4 the time when illuminated * STUPID monsters wake up 1/10 the time when illuminated */ static void cave_temp_room_lite(void) { int i, j; /* Clear them all */ for (i = 0; i < temp_n; i++) { for (j = 0; j < 8; j++) { int y = temp_y[i] + ddy_ddd[j]; int x = temp_x[i] + ddx_ddd[j]; cave_type *c_ptr; /* Verify */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* No longer in the array */ c_ptr->info &= ~(CAVE_TEMP); /* Update only non-CAVE_GLOW grids */ if (c_ptr->info & (CAVE_GLOW)) continue; /* Perma-Lite */ c_ptr->info |= (CAVE_GLOW); /* Process affected monsters */ if (c_ptr->m_idx) { int chance = 25; monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Stupid monsters rarely wake up */ if (FLAG(r_ptr, RF_STUPID)) chance = 10; /* Smart monsters always wake up */ if (FLAG(r_ptr, RF_SMART)) chance = 100; /* Sometimes monsters wake up */ if (m_ptr->csleep && (randint0(100) < chance)) { /* Wake up! */ m_ptr->csleep = 0; /* Notice the "waking up" */ if (m_ptr->ml) { /* Dump a message */ msgf("%^v wakes up.", MONSTER_FMT(m_ptr, 0)); /* Redraw the health bar */ if (p_ptr->health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH); } } } /* Note + Redraw */ note_spot(x, y); } } /* * Hack - recalculate the view * * The view must be redone afterwards because of the way * light affects walls. We simply don't know which squares * are lit until the effect has completed */ p_ptr->update |= PU_VIEW | PU_MONSTERS; /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* None left */ temp_n = 0; } /* * This routine clears the entire "temp" set. * * This routine will "darken" all "temp" grids. * * In addition, some of these grids will be "unmarked". * * This routine is used (only) by "unlite_room()" * * Also, process all affected monsters */ static void cave_temp_room_unlite(void) { int i, j; /* Clear them all */ for (i = 0; i < temp_n; i++) { for (j = 0; j < 8; j++) { int y = temp_y[i] + ddy_cdd[j]; int x = temp_x[i] + ddx_cdd[j]; cave_type *c_ptr; pcave_type *pc_ptr; /* Verify */ if (!in_boundsp(x, y)) continue; c_ptr = area(x, y); pc_ptr = parea(x, y); /* No longer in the array */ c_ptr->info &= ~(CAVE_TEMP); /* Darken the grid */ c_ptr->info &= ~(CAVE_GLOW); /* Notice + Redraw */ note_spot(x, y); } } /* * Hack - recalculate the view * * The view must be redone afterwards because of the way * light affects walls. We simply don't know which squares * are unlit until the effect has completed. */ p_ptr->update |= PU_VIEW | PU_MONSTERS; /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* None left */ temp_n = 0; /* None left */ temp_n = 0; } /* * Determine how much contiguous open space this grid is next to */ static int next_to_open(int cx, int cy) { int i; int y, x; int len = 0; int blen = 0; cave_type *c_ptr; for (i = 0; i < 16; i++) { y = cy + ddy_cdd[i % 8]; x = cx + ddx_cdd[i % 8]; if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* Found a wall, break the length */ if (cave_wall_grid(c_ptr)) { /* Track best length */ if (len > blen) { blen = len; } len = 0; } else { len++; } } return (MAX(len, blen)); } static int next_to_walls_adj(int cx, int cy) { int i; int y, x; int c = 0; cave_type *c_ptr; for (i = 0; i < 8; i++) { y = cy + ddy_ddd[i]; x = cx + ddx_ddd[i]; if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); if (cave_wall_grid(c_ptr)) c++; } return c; } /* * Aux function -- see below */ static void cave_temp_room_aux(int x, int y) { cave_type *c_ptr; /* Verify */ if (!in_bounds2(x, y)) return; /* Get the grid */ c_ptr = area(x, y); /* Avoid infinite recursion */ if (c_ptr->info & (CAVE_TEMP)) return; /* If a wall, exit */ if (cave_wall_grid(c_ptr)) return; /* Do not exceed the maximum spell range */ if (distance(p_ptr->px, p_ptr->py, x, y) > MAX_RANGE) return; /* Verify this grid * * The reason why it is ==6 instead of >5 is that 8 is impossible * due to the check for cave_floor_grid above. * 7 lights dead-end corridors (you need to do this for the * checkboard interesting rooms, so that the boundary is lit * properly. * This leaves only a check for 6 bounding walls! */ if ((next_to_walls_adj(x, y) == 6) && (next_to_open(x, y) <= 1)) return; /* Paranoia -- verify space */ if (temp_n == TEMP_MAX) return; /* Mark the grid as "seen" */ c_ptr->info |= (CAVE_TEMP); /* Add it to the "seen" set */ temp_y[temp_n] = y; temp_x[temp_n] = x; temp_n++; } /* * Illuminate any room containing the given location. */ void lite_room(int x1, int y1) { int i, x, y; cave_type *c_ptr; /* Add the initial grid */ cave_temp_room_aux(x1, y1); /* While grids are in the queue, add their neighbors */ for (i = 0; i < temp_n; i++) { x = temp_x[i], y = temp_y[i]; c_ptr = area(x, y); /* Walls get lit, but stop light */ if (cave_wall_grid(c_ptr)) continue; /* Spread adjacent */ cave_temp_room_aux(x + 1, y); cave_temp_room_aux(x - 1, y); cave_temp_room_aux(x, y + 1); cave_temp_room_aux(x, y - 1); /* Spread diagonal */ cave_temp_room_aux(x + 1, y + 1); cave_temp_room_aux(x - 1, y - 1); cave_temp_room_aux(x - 1, y + 1); cave_temp_room_aux(x + 1, y - 1); } /* Now, lite them all up at once */ cave_temp_room_lite(); } /* * Darken all rooms containing the given location */ void unlite_room(int x1, int y1) { int i, x, y; cave_type *c_ptr; /* Add the initial grid */ cave_temp_room_aux(x1, y1); /* Spread, breadth first */ for (i = 0; i < temp_n; i++) { x = temp_x[i], y = temp_y[i]; c_ptr = area(x, y); /* Walls get dark, but stop darkness */ if (cave_wall_grid(c_ptr)) continue; /* Spread adjacent */ cave_temp_room_aux(x + 1, y); cave_temp_room_aux(x - 1, y); cave_temp_room_aux(x, y + 1); cave_temp_room_aux(x, y - 1); /* Spread diagonal */ cave_temp_room_aux(x + 1, y + 1); cave_temp_room_aux(x - 1, y - 1); cave_temp_room_aux(x - 1, y + 1); cave_temp_room_aux(x + 1, y - 1); } /* Now, darken them all at once */ cave_temp_room_unlite(); } /* * Hack -- call light around the player * Affect all monsters in the projection radius */ bool lite_area(int dam, int rad) { u16b flg = PROJECT_GRID | PROJECT_KILL; /* Hack -- Message */ if (!p_ptr->tim.blind) { msgf("You are surrounded by a white light."); } /* Hook into the "project()" function */ (void)project(0, rad, p_ptr->px, p_ptr->py, dam, GF_LITE_WEAK, flg); /* Lite up the room */ lite_room(p_ptr->px, p_ptr->py); /* Assume seen */ return (TRUE); } /* * Hack -- call darkness around the player * Affect all monsters in the projection radius */ bool unlite_area(int dam, int rad) { u16b flg = PROJECT_GRID | PROJECT_KILL; /* Hack -- Message */ if (!p_ptr->tim.blind) { msgf("Darkness surrounds you."); } /* Hook into the "project()" function */ (void)project(0, rad, p_ptr->px, p_ptr->py, dam, GF_DARK_WEAK, flg); /* Lite up the room */ unlite_room(p_ptr->px, p_ptr->py); /* Assume seen */ return (TRUE); } /* * Cast a ball spell * Stop if we hit a monster, act as a "ball" * Allow "target" mode to pass over monsters * Affect grids, objects, and monsters */ bool fire_ball(int typ, int dir, int dam, int rad) { int tx, ty; u16b flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; /* Use the given direction */ tx = p_ptr->px + 99 * ddx[dir]; ty = p_ptr->py + 99 * ddy[dir]; /* Hack -- Use an actual "target" */ if ((dir == 5) && target_okay()) { flg &= ~(PROJECT_STOP); tx = p_ptr->target_col; ty = p_ptr->target_row; } /* Analyze the "dir" and the "target". Hurt items on floor. */ return (project(0, rad, tx, ty, dam, typ, flg)); } /* * Switch position with a monster. * * This function allows the player to teleport into a wall! */ bool teleport_swap(int dir) { int px = p_ptr->px; int py = p_ptr->py; int tx, ty; cave_type *c_ptr; monster_type *m_ptr; monster_race *r_ptr; if ((dir == 5) && target_okay()) { tx = p_ptr->target_col; ty = p_ptr->target_row; } else { tx = px + ddx[dir]; ty = py + ddy[dir]; } if (!in_bounds2(tx, ty)) { msgf("You can't trade places with that!"); /* Failure */ return FALSE; } c_ptr = area(tx, ty); if (!c_ptr->m_idx) { msgf("You can't trade places with that!"); /* Failure */ return FALSE; } m_ptr = &m_list[c_ptr->m_idx]; r_ptr = &r_info[m_ptr->r_idx]; if (FLAG(r_ptr, RF_RES_TELE)) { msgf("Your teleportation is blocked!"); /* Failure */ return FALSE; } sound(SOUND_TELEPORT); /* Move monster */ area(px, py)->m_idx = c_ptr->m_idx; /* Update the old location */ c_ptr->m_idx = 0; /* Move the monster */ m_ptr->fy = py; m_ptr->fx = px; /* Move the player */ px = tx; py = ty; /* Move the player */ p_ptr->px = tx; p_ptr->py = ty; /* Notice movement */ Term_move_player(); tx = m_ptr->fx; ty = m_ptr->fy; if (!p_ptr->depth) { /* Scroll wilderness */ p_ptr->wilderness_x = px; p_ptr->wilderness_y = py; move_wild(); } /* Update the monster (new location) */ update_mon(area(tx, ty)->m_idx, TRUE); /* Redraw the old grid */ lite_spot(tx, ty); /* Redraw the new grid */ lite_spot(px, py); /* Process fields under the player. */ field_script(area(px, py), FIELD_ACT_PLAYER_ENTER, ""); /* Process fields under the monster. */ field_script(area(m_ptr->fx, m_ptr->fy), FIELD_ACT_MONSTER_ENTER, ""); /* Check for new panel (redraw map) */ verify_panel(); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW); /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Update the monsters */ p_ptr->update |= (PU_DISTANCE); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff XXX XXX XXX */ handle_stuff(); /* Success */ return TRUE; } /* * Hack -- apply a "projection()" in a direction (or at the target) */ bool project_hook(int typ, int dir, int dam, u16b flg) { int tx, ty; /* Pass through the target if needed */ flg |= (PROJECT_THRU); /* Use the given direction */ tx = p_ptr->px + 99 * ddx[dir]; ty = p_ptr->py + 99 * ddy[dir]; /* Hack -- Use an actual "target" */ if ((dir == 5) && target_okay()) { tx = p_ptr->target_col; ty = p_ptr->target_row; } /* Analyze the "dir" and the "target", do NOT explode */ return (project(0, 0, tx, ty, dam, typ, flg)); } /* * Cast a bolt spell * Stop if we hit a monster, as a "bolt" * Affect monsters (not grids or objects) */ bool fire_bolt(int typ, int dir, int dam) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(typ, dir, dam, flg)); } /* * Cast a beam spell * Pass through monsters, as a "beam" * Affect monsters (not grids or objects) */ bool fire_beam(int typ, int dir, int dam) { u16b flg = PROJECT_BEAM | PROJECT_KILL; return (project_hook(typ, dir, dam, flg)); } /* * Cast a bolt spell, or rarely, a beam spell */ bool fire_bolt_or_beam(int prob, int typ, int dir, int dam) { if (randint0(100) < prob) { return (fire_beam(typ, dir, dam)); } else { return (fire_bolt(typ, dir, dam)); } } /* * Some of the old functions */ bool lite_line(int dir, int dam) { u16b flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL; return (project_hook(GF_LITE_WEAK, dir, dam, flg)); } /* Drain life from monster, and do not give it to the player */ bool drain_life(int dir, int dam) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_DRAIN, dir, dam, flg)); } /* Drain life from monster, and give it to the player */ bool drain_gain_life(int dir, int dam) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_NEW_DRAIN, dir, dam, flg)); } bool wall_to_mud(int dir) { u16b flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; return (project_hook(GF_KILL_WALL, dir, rand_range(20, 50), flg)); } bool wizard_lock(int dir) { u16b flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; return (project_hook(GF_JAM_DOOR, dir, rand_range(20, 50), flg)); } bool destroy_door(int dir) { u16b flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM; return (project_hook(GF_KILL_DOOR, dir, 60, flg)); } bool disarm_trap(int dir) { u16b flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM; return (project_hook(GF_KILL_TRAP, dir, 60, flg)); } bool heal_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_HEAL, dir, damroll(4, 6), flg)); } bool speed_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_SPEED, dir, p_ptr->lev, flg)); } bool slow_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_SLOW, dir, p_ptr->lev, flg)); } bool sleep_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_SLEEP, dir, p_ptr->lev, flg)); } bool stasis_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_STASIS, dir, p_ptr->lev, flg)); } bool confuse_monster(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_CONF, dir, plev, flg)); } bool stun_monster(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_STUN, dir, plev, flg)); } bool poly_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; bool tester = (project_hook(GF_OLD_POLY, dir, p_ptr->lev, flg)); if (tester) chg_virtue(V_CHANCE, 1); return (tester); } bool clone_monster(int dir) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_OLD_CLONE, dir, 0, flg)); } bool fear_monster(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_TURN_ALL, dir, plev, flg)); } bool death_ray(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_DEATH_RAY, dir, plev * 50, flg)); } bool teleport_monster(int dir) { u16b flg = PROJECT_BEAM | PROJECT_KILL; return (project_hook(GF_AWAY_ALL, dir, MAX_SIGHT * 5, flg)); } /* * Hooks -- affect adjacent grids (radius 1 ball attack) */ bool door_creation(void) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE; return (project(0, 1, p_ptr->px, p_ptr->py, 0, GF_MAKE_DOOR, flg)); } bool trap_creation(void) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE; return (project(0, 1, p_ptr->px, p_ptr->py, 0, GF_MAKE_TRAP, flg)); } bool glyph_creation(void) { u16b flg = PROJECT_GRID | PROJECT_ITEM; return (project(0, 1, p_ptr->px, p_ptr->py, 0, GF_MAKE_GLYPH, flg)); } bool wall_stone(void) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE; bool dummy = (project(0, 1, p_ptr->px, p_ptr->py, 0, GF_STONE_WALL, flg)); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); return dummy; } bool destroy_doors_touch(void) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE; return (project(0, 1, p_ptr->px, p_ptr->py, 60, GF_KILL_DOOR, flg)); } bool sleep_monsters_touch(void) { u16b flg = PROJECT_KILL | PROJECT_HIDE; return (project(0, 1, p_ptr->px, p_ptr->py, p_ptr->lev, GF_OLD_SLEEP, flg)); } void call_chaos(void) { int Chaos_type, dummy, dir; int plev = p_ptr->lev; bool line_chaos = FALSE; int hurt_types[30] = { GF_ELEC, GF_POIS, GF_ACID, GF_COLD, GF_FIRE, GF_MISSILE, GF_ARROW, GF_PLASMA, GF_HOLY_FIRE, GF_WATER, GF_LITE, GF_DARK, GF_FORCE, GF_INERTIA, GF_MANA, GF_METEOR, GF_ICE, GF_CHAOS, GF_NETHER, GF_DISENCHANT, GF_SHARDS, GF_SOUND, GF_NEXUS, GF_CONFUSION, GF_TIME, GF_GRAVITY, GF_ROCKET, GF_NUKE, GF_HELL_FIRE, GF_DISINTEGRATE }; Chaos_type = hurt_types[randint0(30)]; if (one_in_(4)) line_chaos = TRUE; if (one_in_(6)) { for (dummy = 1; dummy < 10; dummy++) { if (dummy - 5) { if (line_chaos) (void)fire_beam(Chaos_type, dummy, 75); else (void)fire_ball(Chaos_type, dummy, 75, 2); } } } else if (one_in_(3)) { (void)fire_ball(Chaos_type, 0, 300, 8); } else { if (!get_aim_dir(&dir)) return; if (line_chaos) (void)fire_beam(Chaos_type, dir, 150); else (void)fire_ball(Chaos_type, dir, 150, 3 + (plev / 35)); } } /* * Activate the evil Topi Ylinen curse * rr9: Stop the nasty things when a Cyberdemon is summoned * or the player gets paralyzed. */ bool activate_ty_curse(bool stop_ty, int *count) { int px = p_ptr->px; int py = p_ptr->py; u16b flg = (PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP); do { switch (randint1(34)) { case 28: case 29: { if (!(*count)) { msgf("The ground trembles..."); (void)earthquake(px, py, rand_range(5, 15)); if (!one_in_(6)) break; } /* Fall through */ } case 30: case 31: { if (!(*count)) { msgf("A portal opens to a plane of raw mana!"); (void)destroy_area(px, py, 20); project(1, 3, px, py, damroll(10, 5), GF_MANA, flg); if (!one_in_(6)) break; } /* Fall through */ } case 32: case 33: { if (!(*count)) { msgf("Space warps about you!"); teleport_player(damroll(10, 10)); if (!one_in_(13)) (*count) += activate_hi_summon(); if (!one_in_(6)) break; } /* Fall through */ } case 34: { msgf("You feel a surge of energy!"); wall_breaker(); if (one_in_(7)) { (void)project(1, 7, px, py, 50, GF_KILL_WALL, flg); } if (!one_in_(6)) break; /* Fall through */ } case 1: case 2: case 3: case 16: case 17: { aggravate_monsters(0); if (!one_in_(6)) break; /* Fall through */ } case 4: case 5: case 6: { (*count) += activate_hi_summon(); if (!one_in_(6)) break; /* Fall through */ } case 7: case 8: case 9: case 18: { (*count) += summon_specific(0, px, py, p_ptr->depth, 0, TRUE, FALSE, FALSE); if (!one_in_(6)) break; /* Fall through */ } case 10: case 11: case 12: { msgf("You feel your life draining away..."); lose_exp(p_ptr->exp / 16); if (!one_in_(6)) break; /* Fall through */ } case 13: case 14: case 15: case 19: case 20: { /* The TY_CURSE is effectively a level 80 monster */ if (stop_ty || ((FLAG(p_ptr, TR_FREE_ACT)) && (player_save(80)))) { /* Do nothing */ ; } else { msgf("You feel like a statue!"); if (FLAG(p_ptr, TR_FREE_ACT)) { (void)inc_paralyzed(randint1(3)); } else { (void)inc_paralyzed(randint1(13)); } stop_ty = TRUE; } if (!one_in_(6)) break; /* Fall through */ } case 21: case 22: case 23: { (void)do_dec_stat(randint0(6)); if (!one_in_(6)) break; /* Fall through */ } case 24: { msgf("Huh? Who am I? What am I doing here?"); (void)lose_all_info(); if (!one_in_(6)) break; /* Fall through */ } case 25: { /* * Only summon Cyberdemons deep in the dungeon. */ if ((p_ptr->depth > 65) && !stop_ty) { (*count) += summon_cyber(-1, px, py); stop_ty = TRUE; break; } if (!one_in_(6)) break; /* Fall through */ } default: { int stat = 0; while (stat < A_MAX) { do { (void)do_dec_stat(stat); } while (one_in_(2)); stat++; } } } } while (one_in_(3) && !stop_ty); return stop_ty; } int activate_hi_summon(void) { int px = p_ptr->px; int py = p_ptr->py; int i; int count = 0; for (i = 0; i < (randint1(9) + (p_ptr->depth / 40)); i++) { switch ((randint1(26) + (p_ptr->depth / 20)) / 2) { case 1: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_ANT, TRUE, FALSE, FALSE); break; } case 2: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_SPIDER, TRUE, FALSE, FALSE); break; } case 3: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_HOUND, TRUE, FALSE, FALSE); break; } case 4: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_HYDRA, TRUE, FALSE, FALSE); break; } case 5: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_ANGEL, TRUE, FALSE, FALSE); break; } case 6: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_UNDEAD, TRUE, FALSE, FALSE); break; } case 7: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_DRAGON, TRUE, FALSE, FALSE); break; } case 8: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_DEMON, TRUE, FALSE, FALSE); break; } case 9: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_AMBERITES, TRUE, FALSE, FALSE); break; } case 10: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_UNIQUE, TRUE, FALSE, FALSE); break; } case 11: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_HI_UNDEAD, TRUE, FALSE, FALSE); break; } case 12: { count += summon_specific(0, px, py, p_ptr->depth, SUMMON_HI_DRAGON, TRUE, FALSE, FALSE); break; } case 13: { count += summon_specific(0, px, py, 100, SUMMON_CYBER, TRUE, FALSE, FALSE); break; } default: { count += summon_specific(0, px, py, (((p_ptr->depth * 3) / 2) + 5), 0, TRUE, FALSE, FALSE); } } } return count; } /* ToDo: check */ int summon_cyber(int who, int x, int y) { int i; int max_cyber = (p_ptr->depth / 50) + randint1(6); int count = 0; bool friendly = FALSE; bool pet = FALSE; /* Summoned by a monster */ if (who > 0) { monster_type *m_ptr = &m_list[who]; friendly = is_friendly(m_ptr); pet = is_pet(m_ptr); } for (i = 0; i < max_cyber; i++) { count += summon_specific(who, x, y, 100, SUMMON_CYBER, FALSE, friendly, pet); } return count; } void wall_breaker(void) { int px = p_ptr->px; int py = p_ptr->py; int i; int y, x; int attempts = 1000; if (randint1(80 + p_ptr->lev) < 70) { while (attempts--) { scatter(&x, &y, px, py, 4); if ((y != py) || (x != px)) break; } (void)project(0, 0, x, y, rand_range(20, 50), GF_KILL_WALL, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL)); } else if (randint1(100) > 30) { (void)earthquake(px, py, 1); } else { int num = damroll(5, 3); for (i = 0; i < num; i++) { while (1) { scatter(&x, &y, px, py, 4); if ((y != py) || (x != px)) break; } (void)project(0, 0, x, y, rand_range(20, 50), GF_KILL_WALL, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL)); } } } /* * Confuse monsters */ bool confuse_monsters(int dam) { return (project_hack(GF_OLD_CONF, dam)); } /* * Charm monsters */ bool charm_monsters(int dam) { return (project_hack(GF_CHARM, dam)); } /* * Charm animals */ bool charm_animals(int dam) { return (project_hack(GF_CONTROL_ANIMAL, dam)); } /* * Stun monsters */ bool stun_monsters(int dam) { return (project_hack(GF_STUN, dam)); } /* * Stasis monsters */ bool stasis_monsters(int dam) { return (project_hack(GF_STASIS, dam)); } /* * Mindblast monsters */ bool mindblast_monsters(int dam) { return (project_hack(GF_PSI, dam)); } /* * Banish all monsters */ bool banish_monsters(int dist) { return (project_hack(GF_AWAY_ALL, dist)); } /* * Turn evil */ bool turn_evil(int dam) { return (project_hack(GF_TURN_EVIL, dam)); } /* * Turn everyone */ bool turn_monsters(int dam) { return (project_hack(GF_TURN_ALL, dam)); } /* * Death-ray all monsters (note: OBSCENELY powerful) */ bool deathray_monsters(void) { return (project_hack(GF_DEATH_RAY, p_ptr->lev * 50)); } bool charm_monster(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_CHARM, dir, plev, flg)); } bool control_one_undead(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_CONTROL_UNDEAD, dir, plev, flg)); } bool charm_animal(int dir, int plev) { u16b flg = PROJECT_STOP | PROJECT_KILL; return (project_hook(GF_CONTROL_ANIMAL, dir, plev, flg)); } bool starlite(void) { int k; int num = damroll(5, 3); int y, x; int attempts; cave_type *c_ptr; for (k = 0; k < num; k++) { attempts = 1000; while (attempts--) { scatter(&x, &y, p_ptr->px, p_ptr->py, 4); /* paranoia */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); if (cave_wall_grid(c_ptr)) continue; if ((y != p_ptr->py) || (x != p_ptr->px)) break; } (void)project(0, 0, x, y, damroll(6, 8), GF_LITE_WEAK, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL)); } return (TRUE); } bool scatter_ball(int num, int type, int dam, int rad) { int k; int y, x; int attempts; cave_type *c_ptr; for (k = 0; k < num; k++) { attempts = 1000; while (attempts--) { scatter(&x, &y, p_ptr->px, p_ptr->py, 4); /* paranoia */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); if (cave_wall_grid(c_ptr)) continue; if ((y != p_ptr->py) || (x != p_ptr->px)) break; } project(0, rad, x, y, dam, type, (PROJECT_THRU | PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL)); } return (TRUE); } void create_food() { object_type *q_ptr; /* Hack - create a food ration */ q_ptr = object_prep(lookup_kind(TV_FOOD, SV_FOOD_RATION)); /* Drop the object from heaven */ drop_near(q_ptr, -1, p_ptr->px, p_ptr->py); } void whirlwind_attack() { int y = 0, x = 0, dir; cave_type *c_ptr; monster_type *m_ptr; for (dir = 0; dir <= 9; dir++) { y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* paranoia */ if (!in_bounds2(x, y)) return; c_ptr = area(x, y); /* Get the monster */ m_ptr = &m_list[c_ptr->m_idx]; /* Hack -- attack monsters */ if (c_ptr->m_idx && (m_ptr->ml || cave_floor_grid(c_ptr))) py_attack(x, y); } } zangband/src/spells3.c0000755000000000000000000025634310250356274013704 0ustar rootroot/* File: spells3.c */ /* Purpose: Spell code (part 3) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* Maximum number of tries for teleporting */ #define MAX_TRIES 100 /* * Teleport a monster, normally up to "dis" grids away. * * Attempt to move the monster at least "dis/2" grids away. * * But allow variation to prevent infinite loops. */ bool teleport_away(int m_idx, int dis) { int ny = 0, nx = 0, oy, ox, d, i, min; int tries = 0; bool look = TRUE; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; cave_type *c_ptr = NULL; /* Paranoia */ if (!m_ptr->r_idx) return (FALSE); /* Save the old location */ oy = m_ptr->fy; ox = m_ptr->fx; /* Minimum distance */ min = dis / 2; if ((((p_ptr->chp * 10) / p_ptr->mhp) < 5) && (randint1(5) > ((p_ptr->chp * 10) / p_ptr->mhp))) { chg_virtue(V_VALOUR, -1); } /* Look until done */ while (look) { tries++; /* Verify max distance */ if (dis > 200) dis = 200; /* Try several locations */ for (i = 0; i < 500; i++) { /* Pick a (possibly illegal) location */ while (1) { ny = rand_spread(oy, dis); nx = rand_spread(ox, dis); d = distance(ox, oy, nx, ny); if ((d >= min) && (d <= dis)) break; } /* Ignore illegal locations */ if (!in_bounds2(nx, ny)) continue; c_ptr = area(nx, ny); /* Require "empty" floor space */ if (!cave_empty_grid(c_ptr)) continue; /* Not on player */ if ((ny == p_ptr->py) && (nx == p_ptr->px)) continue; /* Not on bad terrain */ if (!test_monster_square(c_ptr, r_ptr)) continue; /* * Test for fields that will not allow monsters to * be generated on them. (i.e. Glyph of warding) */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MPLACE)) continue; /* No teleporting into vaults and such */ if (c_ptr->info & CAVE_ICKY) continue; /* This grid looks good */ look = FALSE; /* Stop looking */ break; } /* Increase the maximum distance */ dis = dis * 2; /* Decrease the minimum distance */ min = min / 2; /* Stop after MAX_TRIES tries */ if (tries > MAX_TRIES) return (FALSE); } /* Sound */ sound(SOUND_TPOTHER); /* Update the new location */ area(nx, ny)->m_idx = m_idx; /* Update the old location */ area(ox, oy)->m_idx = 0; /* Move the monster */ m_ptr->fy = ny; m_ptr->fx = nx; /* Update the monster (new location) */ update_mon(m_idx, TRUE); /* Process fields under the monster. */ field_script(c_ptr, FIELD_ACT_MONSTER_ENTER, ""); /* Redraw the old grid */ lite_spot(ox, oy); /* Redraw the new grid */ lite_spot(nx, ny); /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } return (TRUE); } /* * Teleport monster next to the player */ void teleport_to_player(int m_idx) { int ny, nx, oy, ox, px, py, d, i, min; int attempts = 500; int dis = 2; bool look = TRUE; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; cave_type *c_ptr = NULL; /* Paranoia */ if (!m_ptr->r_idx) return; /* "Skill" test */ if (randint1(100) > r_info[m_ptr->r_idx].level) return; /* Initialize */ ny = m_ptr->fy; nx = m_ptr->fx; /* Initialize */ py = p_ptr->py; px = p_ptr->px; /* Save the old location */ oy = m_ptr->fy; ox = m_ptr->fx; /* Minimum distance */ min = dis / 2; /* Look until done */ while (look && --attempts) { /* Verify max distance */ if (dis > 200) dis = 200; /* Try several locations */ for (i = 0; i < 500; i++) { /* Pick a (possibly illegal) location */ while (1) { ny = rand_spread(py, dis); nx = rand_spread(px, dis); d = distance(px, py, nx, ny); if ((d >= min) && (d <= dis)) break; } /* Ignore illegal locations */ if (!in_bounds2(nx, ny)) continue; c_ptr = area(nx, ny); /* Check for a field that blocks movement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) { continue; } /* * Test for fields that will not allow monsters to * be generated on them. (i.e. Glyph of warding) */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_MPLACE)) continue; /* Require "empty" floor space */ if (!cave_empty_grid(c_ptr)) continue; /* Not on player */ if ((ny == py) && (nx == px)) continue; /* ...nor onto the Pattern */ if (cave_pattern_grid(c_ptr)) continue; /* No teleporting into vaults and such */ /* if (c_ptr->info & (CAVE_ICKY)) continue; */ /* This grid looks good */ look = FALSE; /* Stop looking */ break; } /* Increase the maximum distance */ dis = dis * 2; /* Decrease the minimum distance */ min = min / 2; } if (attempts < 1) return; /* Sound */ sound(SOUND_TPOTHER); /* Update the new location */ area(nx, ny)->m_idx = m_idx; /* Update the old location */ area(ox, oy)->m_idx = 0; /* Move the monster */ m_ptr->fy = ny; m_ptr->fx = nx; /* Update the monster (new location) */ update_mon(m_idx, TRUE); /* Process fields under the monster. */ field_script(c_ptr, FIELD_ACT_MONSTER_ENTER, ""); /* Redraw the old grid */ lite_spot(ox, oy); /* Redraw the new grid */ lite_spot(nx, ny); /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } } /* * Teleport the player to a location up to "dis" grids away. * * If no such spaces are readily available, the distance may increase. * Try very hard to move the player at least a quarter that distance. * * When long-range teleport effects are considered, there is a nasty * tendency to "bounce" the player between two or three different spots * because these are the only spots that are "far enough" way to satisfy * the algorithm. Therefore, if the teleport distance is more than 50, * we decrease the minimum acceptable distance to try to increase randomness. * -GJW */ void teleport_player(int dis) { int px = p_ptr->px; int py = p_ptr->py; int d, i, min, ox, oy; int tries = 0; int xx, yy; /* Initialize */ int y = py; int x = px; monster_type *m_ptr; u16b m_idx; bool look = TRUE; cave_type *c_ptr; if (FLAG(p_ptr, TR_NO_TELE)) { msgf("A mysterious force prevents you from teleporting!"); return; } if (dis > 200) dis = 200; /* To be on the safe side... */ /* Minimum distance */ min = dis / (dis > 50 ? 3 : 2); /* Look until done */ while (look) { tries++; /* Verify max distance */ if (dis > 200) dis = 200; /* Try several locations */ for (i = 0; i < 500; i++) { /* Pick a (possibly illegal) location */ while (1) { y = rand_spread(py, dis); x = rand_spread(px, dis); d = distance(px, py, x, y); if ((d >= min) && (d <= dis)) break; } /* Ignore illegal locations */ if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* Require empty space */ if (!cave_empty_grid(c_ptr)) continue; /* No non-movement */ if ((y == py) && (x == px)) continue; /* Check for a field that blocks movement */ if (fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER)) { continue; } /* No teleporting into vaults and such */ if (c_ptr->info & CAVE_ICKY) continue; /* This grid looks good */ look = FALSE; /* Stop looking */ break; } /* Increase the maximum distance */ dis = dis * 2; /* Decrease the minimum distance */ min = min / 2; /* Stop after MAX_TRIES tries */ if (tries > MAX_TRIES) return; } /* Sound */ sound(SOUND_TELEPORT); /* Save old location */ oy = py; ox = px; /* Move the player */ py = y; px = x; /* Move the player */ p_ptr->py = y; p_ptr->px = x; /* Notice movement */ Term_move_player(); if (!p_ptr->depth) { /* Scroll wilderness */ p_ptr->wilderness_x = px; p_ptr->wilderness_y = py; move_wild(); } /* Redraw the old spot */ lite_spot(ox, oy); /* Redraw the new spot */ lite_spot(px, py); /* Process fields under the player. */ field_script(area(px, py), FIELD_ACT_PLAYER_ENTER, ""); /* Monsters with teleport ability may follow the player */ for (xx = -1; xx <= 1; xx++) { for (yy = -1; yy <= 1; yy++) { if ((xx == 0) && (yy == 0)) { /* Do nothing */ } else { x = ox + xx; y = oy + yy; if (in_bounds2(x, y) && area(x, y)->m_idx) { m_idx = area(x, y)->m_idx; m_ptr = &m_list[m_idx]; if ((FLAG(&r_info[m_ptr->r_idx], RF_TPORT)) && !(FLAG(&r_info[m_ptr->r_idx], RF_RES_TELE)) && !(m_ptr->csleep)) /* * The RES_TELE limitation is to avoid * totally unkillable suckers... */ { teleport_to_player(m_idx); } } } } } /* Check for new panel (redraw map) */ verify_panel(); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); /* Update the monsters */ p_ptr->update |= (PU_DISTANCE); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff XXX XXX XXX */ handle_stuff(); } /* * Teleport player to a grid near the given location * * This function is slightly obsessive about correctness. * This function allows teleporting into vaults (!) */ void teleport_player_to(int nx, int ny) { int py = p_ptr->py; int px = p_ptr->px; int y, x, oy, ox, dis = 0, ctr = 0; cave_type *c_ptr; /* No movement at all */ if ((ny == py) && (nx == px)) return; if (FLAG(p_ptr, TR_NO_TELE)) { msgf("A mysterious force prevents you from teleporting!"); return; } /* Find a usable location */ while (1) { /* Pick a nearby legal location */ while (1) { y = rand_spread(ny, dis); x = rand_spread(nx, dis); if (in_bounds2(x, y)) break; } /* Accept "naked" floor grids */ c_ptr = area(x, y); /* No non-movement */ if ((y == py) && (x == px)) continue; /* Can enter grid? */ if (cave_empty_grid(c_ptr) && !(c_ptr->info & CAVE_ICKY) && !(fields_have_flags(c_ptr, FIELD_INFO_NO_ENTER))) break; /* Occasionally advance the distance */ if (++ctr > (4 * dis * dis + 4 * dis + 1)) { ctr = 0; dis++; } } /* Sound */ sound(SOUND_TELEPORT); /* Save old location */ oy = py; ox = px; /* Move the player */ py = y; px = x; /* Move the player */ p_ptr->py = y; p_ptr->px = x; /* Notice movement */ Term_move_player(); if (!p_ptr->depth) { /* Scroll wilderness */ p_ptr->wilderness_x = px; p_ptr->wilderness_y = py; move_wild(); } /* Redraw the old spot */ lite_spot(ox, oy); /* Redraw the new spot */ lite_spot(px, py); /* Process fields under the player. */ field_script(area(px, py), FIELD_ACT_PLAYER_ENTER, ""); /* Check for new panel (redraw map) */ verify_panel(); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); /* Update the monsters */ p_ptr->update |= (PU_DISTANCE); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff XXX XXX XXX */ handle_stuff(); } /* * Teleport the player one level up or down (random when legal) */ void teleport_player_level(void) { /* No effect in final quest */ if (is_special_level(p_ptr->depth) && ironman_downward) { msgf("There is no effect."); return; } if (!check_down_wild()) { msgf("There is no effect."); return; } if (FLAG(p_ptr, TR_NO_TELE)) { msgf("A mysterious force prevents you from teleporting!"); return; } if (!p_ptr->depth || ironman_downward) { msgf(MSGT_TPLEVEL, "You sink through the floor."); /* Go down */ move_dun_level(1); } else if (is_special_level(p_ptr->depth)) { msgf(MSGT_TPLEVEL, "You rise up through the ceiling."); /* Go up */ move_dun_level(-1); } else if (one_in_(2) || (p_ptr->depth >= dungeon()->max_level)) { msgf(MSGT_TPLEVEL, "You rise up through the ceiling."); /* Go down */ move_dun_level(-1); } else { msgf(MSGT_TPLEVEL, "You sink through the floor."); /* Go up */ move_dun_level(1); } /* Sound */ sound(SOUND_TPLEVEL); } bool check_down_wild(void) { place_type *pl_ptr; /* Can always recall from dungeon */ if (p_ptr->depth) return (TRUE); /* Hack - no recalling in the middle of the wilderness */ if (!p_ptr->place_num) { msgf("Nothing happens."); return (FALSE); } /* Cannot recall in towns with no dungeon */ if (!vanilla_town) { pl_ptr = &place[p_ptr->place_num]; /* Look for dungeon */ if (!pl_ptr->dungeon) { msgf("Nothing happens."); return (FALSE); } /* Else we must have been down before */ if (!pl_ptr->dungeon->recall_depth) { msgf("You need to visit the dungeon first."); return (FALSE); } } return (TRUE); } /* * Recall the player to town or dungeon */ void recall_player(int turns) { dun_type *d_ptr = dungeon(); /* * TODO: Recall the player to the last * visited town when in the wilderness */ /* Ironman option */ if (ironman_downward) { msgf("Nothing happens."); return; } if (!check_down_wild()) return; if (p_ptr->depth && (d_ptr->recall_depth > p_ptr->depth)) { if (get_check("Reset recall depth? ")) d_ptr->recall_depth = (char) p_ptr->depth; } else if (p_ptr->depth > d_ptr->recall_depth) { d_ptr->recall_depth = (char) p_ptr->depth; } if (!p_ptr->tim.word_recall) { p_ptr->tim.word_recall = turns; msgf("The air about you becomes charged..."); p_ptr->redraw |= (PR_STATUS); } else { p_ptr->tim.word_recall = 0; msgf("A tension leaves the air around you..."); p_ptr->redraw |= (PR_STATUS); } } void word_of_recall(void) { recall_player(rand_range(15, 35)); } /* * Apply disenchantment to the player's stuff * * XXX XXX XXX This function is also called from the "melee" code * * Return "TRUE" if the player notices anything */ bool apply_disenchant(void) { int t = 0; object_type *o_ptr; /* Pick a random slot */ switch (randint1(8)) { case 1: { t = EQUIP_WIELD; break; } case 2: { t = EQUIP_BOW; break; } case 3: { t = EQUIP_BODY; break; } case 4: { t = EQUIP_OUTER; break; } case 5: { t = EQUIP_ARM; break; } case 6: { t = EQUIP_HEAD; break; } case 7: { t = EQUIP_HANDS; break; } case 8: { t = EQUIP_FEET; break; } } /* Get the item */ o_ptr = &p_ptr->equipment[t]; /* No item, nothing happens */ if (!o_ptr->k_idx) return (FALSE); /* Nothing to disenchant */ if ((o_ptr->to_h <= 0) && (o_ptr->to_d <= 0) && (o_ptr->to_a <= 0)) { /* Nothing to notice */ return (FALSE); } /* Artifacts have 71% chance to resist */ if ((FLAG(o_ptr, TR_INSTA_ART)) && (randint0(100) < 71)) { /* Message */ msgf("Your %v (%c) resist%s disenchantment!", OBJECT_FMT(o_ptr, FALSE, 0), I2A(t), ((o_ptr->number != 1) ? "" : "s")); /* Notice */ return (TRUE); } /* Message */ msgf("Your %v (%c) %s disenchanted!", OBJECT_FMT(o_ptr, FALSE, 0), I2A(t), ((o_ptr->number != 1) ? "were" : "was")); /* Disenchant tohit */ if (o_ptr->to_h > 0) o_ptr->to_h--; if ((o_ptr->to_h > 10) && (randint0(100) < 20)) o_ptr->to_h--; /* Disenchant todam */ if (o_ptr->to_d > 0) o_ptr->to_d--; if ((o_ptr->to_d > 10) && (randint0(100) < 20)) o_ptr->to_d--; /* Disenchant toac */ if (o_ptr->to_a > 0) o_ptr->to_a--; if ((o_ptr->to_a > 10) && (randint0(100) < 20)) o_ptr->to_a--; /* Trigger scripts */ apply_object_trigger(TRIGGER_ALTER, o_ptr, ""); chg_virtue(V_HARMONY, 1); chg_virtue(V_ENCHANT, -2); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Notice changes */ notice_equip(); /* Notice */ return (TRUE); } void mutate_player(void) { int max1, cur1, max2, cur2, ii, jj; int bonus1, bonus2; /* Pick a pair of stats */ ii = randint0(A_MAX); for (jj = ii; jj == ii; jj = randint0(A_MAX)) /* loop */ ; max1 = p_ptr->stat[ii].max; cur1 = p_ptr->stat[ii].cur; max2 = p_ptr->stat[jj].max; cur2 = p_ptr->stat[jj].cur; /* Adjust the swapped stats... */ bonus1 = rp_ptr->r_adj[ii] + cp_ptr->c_adj[ii]; bonus2 = rp_ptr->r_adj[jj] + cp_ptr->c_adj[jj]; max1 = adjust_stat(jj, max1, bonus2 - bonus1); max2 = adjust_stat(ii, max2, bonus1 - bonus2); /* Hack - restore both stats rather than figure try to swap drainage */ cur1 = max1; cur2 = max2; p_ptr->stat[ii].max = max2; p_ptr->stat[ii].cur = cur2; p_ptr->stat[jj].max = max1; p_ptr->stat[jj].cur = cur1; p_ptr->update |= (PU_BONUS); } /* * Apply Nexus */ void apply_nexus(const monster_type *m_ptr) { monster_race *r_ptr = &r_info[m_ptr->r_idx]; switch (randint1(7)) { case 1: case 2: case 3: { teleport_player(200); break; } case 4: case 5: { teleport_player_to(m_ptr->fx, m_ptr->fy); break; } case 6: { if (player_save(r_ptr->hdice * 2)) { msgf("You resist the effects!"); break; } /* Teleport Level */ teleport_player_level(); break; } case 7: { if (player_save(r_ptr->hdice * 2)) { msgf("You resist the effects!"); break; } msgf("Your body starts to scramble..."); mutate_player(); break; } } } /* * Charge a lite (torch or latern) */ void phlogiston(void) { int max_flog; object_type *o_ptr = &p_ptr->equipment[EQUIP_LITE]; cptr lite_item = NULL; /* It's a lamp */ if ((o_ptr->tval == TV_LITE) && (o_ptr->sval == SV_LITE_LANTERN)) { max_flog = FUEL_LAMP; /* Remember what the item is */ lite_item = "lantern"; } /* It's a torch */ else if ((o_ptr->tval == TV_LITE) && (o_ptr->sval == SV_LITE_TORCH)) { max_flog = FUEL_TORCH; /* Remember what the item is */ lite_item = "torch"; } /* No torch to refill */ else { msgf("You are not wielding anything which uses phlogiston."); return; } if (o_ptr->timeout >= max_flog) { msgf("No more phlogiston can be put in this %s.", lite_item); return; } /* Refuel */ o_ptr->timeout += (max_flog / 2); /* Message */ msgf("You add phlogiston to your %s.", lite_item); /* Comment */ if (o_ptr->timeout >= max_flog) { o_ptr->timeout = max_flog; msgf("Your %s is full.", lite_item); } /* Recalculate torch */ p_ptr->update |= (PU_TORCH); /* Window stuff */ p_ptr->window |= (PW_EQUIP); } /* * Brand the current weapon */ void brand_weapon(int brand_type) { object_type *o_ptr = &p_ptr->equipment[EQUIP_WIELD]; byte ego = 0; /* you can never modify artifacts / ego-items */ /* you can never modify cursed items */ /* TY: You _can_ modify broken items (if you're silly enough) */ if (o_ptr->k_idx && !o_ptr->xtra_name && !cursed_p(o_ptr)) { cptr act; switch (brand_type) { case 1: { act = "is engulfed in raw Logrus!"; ego = EGO_CHAOTIC; break; } case 2: { act = "is coated with poison."; ego = EGO_BRAND_POIS; break; } case 3: { act = "thirsts for blood!"; ego = EGO_VAMPIRIC; break; } case 4: { act = "seems very unstable now."; ego = EGO_TRUMP; o_ptr->pval = randint1(2); break; } default: { if (randint0(100) < 25) { act = "is covered in a fiery shield!"; ego = EGO_BRAND_FIRE; } else { act = "glows deep, icy blue!"; ego = EGO_BRAND_COLD; } } } msgf("Your %v %s", OBJECT_FMT(o_ptr, FALSE, 0), act); (void)enchant(o_ptr, rand_range(4, 6), ENCH_TOHIT | ENCH_TODAM); } else { if (flush_failure) flush(); msgf("The Branding failed."); chg_virtue(V_ENCHANT, -2); } if (ego) { /* Hack - save the price */ s32b cost = o_ptr->cost; add_ego_flags(o_ptr, ego); o_ptr->cost = cost; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); } } void call_the_(void) { int py = p_ptr->py; int px = p_ptr->px; int i; if (in_bounds(px, py) && cave_floor_grid(area(px - 1, py - 1)) && cave_floor_grid(area(px - 1, py)) && cave_floor_grid(area(px - 1, py + 1)) && cave_floor_grid(area(px, py - 1)) && cave_floor_grid(area(px, py + 1)) && cave_floor_grid(area(px + 1, py - 1)) && cave_floor_grid(area(px + 1, py)) && cave_floor_grid(area(px + 1, py + 1))) { for (i = 1; i < 10; i++) { if (i != 5) (void)fire_ball(GF_ROCKET, i, 175, 2); } for (i = 1; i < 10; i++) { if (i != 5) (void)fire_ball(GF_MANA, i, 175, 3); } for (i = 1; i < 10; i++) { if (i != 5) (void)fire_ball(GF_NUKE, i, 175, 4); } } else { msgf("You %s the %s too close to a wall!", ((mp_ptr->spell_book == TV_LIFE_BOOK) ? "recite" : "cast"), ((mp_ptr->spell_book == TV_LIFE_BOOK) ? "prayer" : "spell")); msgf("There is a loud explosion!"); if (destroy_area(px, py, 20 + p_ptr->lev)) msgf("The dungeon collapses..."); else msgf("The dungeon trembles."); take_hit(rand_range(100, 250), "a suicidal Call the Void"); } } /* * Fetch an item (teleport it right underneath the caster) * * This is a massive hack. */ void fetch(int dir, int wgt, bool require_los) { int py = p_ptr->py; int px = p_ptr->px; int tx, ty; cave_type *c_ptr; object_type *o_ptr; /* Check to see if an object is already there */ if (area(px, py)->o_idx) { msgf("You can't fetch when you're already standing on something."); return; } /* Use a target */ if ((dir == 5) && target_okay()) { tx = p_ptr->target_col; ty = p_ptr->target_row; /* Paranoia */ if ((distance(px, py, tx, ty) > MAX_RANGE) || (!in_bounds2(tx, ty))) { msgf("You can't fetch something that far away!"); return; } c_ptr = area(tx, ty); /* We need an item to fetch */ if (!c_ptr->o_idx) { msgf("There is no object at this place."); return; } /* No fetching from vault */ if (c_ptr->info & CAVE_ICKY) { msgf("The item slips from your control."); return; } /* We need to see the item */ if (require_los && !player_has_los_grid(parea(tx, ty))) { msgf("You have no direct line of sight to that location."); return; } } else { /* Use a direction */ ty = py; /* Where to drop the item */ tx = px; while (TRUE) { ty += ddy[dir]; tx += ddx[dir]; /* paranoia */ if (!in_bounds2(tx, ty)) continue; c_ptr = area(tx, ty); if ((distance(px, py, tx, ty) > MAX_RANGE) || cave_wall_grid(c_ptr)) return; /* found a spot */ if (!c_ptr->o_idx) break; } } o_ptr = &o_list[c_ptr->o_idx]; if (o_ptr->weight > wgt) { /* Too heavy to 'fetch' */ msgf("The object is too heavy."); return; } /* * Hack - do not get artifacts. * This interacts badly with preserve mode. */ if (FLAG(o_ptr, TR_INSTA_ART)) { msgf("The object seems to have a will of its own!"); return; } /* Move the object */ move_object(&area(px, py)->o_idx, &c_ptr->o_idx, o_ptr); /* Record the new location */ o_ptr->ix = px; o_ptr->iy = py; msgf("%^v flies through the air to your feet.", OBJECT_FMT(o_ptr, TRUE, 0)); /* Notice the moved object (The player gets redrawn) */ note_spot(px, py); /* Redraw the map??? Can we just use lite_spot() a few times? */ p_ptr->redraw |= PR_MAP; } void alter_reality(void) { if (p_ptr->depth) { msgf("The world changes!"); /* Leaving */ p_ptr->state.leaving = TRUE; } else { msgf("The world seems to change for a moment!"); } } /* * Leave a "glyph of warding" which prevents monster movement */ bool warding_glyph(void) { int py = p_ptr->py; int px = p_ptr->px; cave_type *c_ptr = area(px, py); /* XXX XXX XXX */ if (!cave_naked_grid(c_ptr)) { msgf("The object resists the spell."); return FALSE; } /* Not in a wall */ if (cave_wall_grid(c_ptr)) { msgf("You need open space to draw the rune."); return FALSE; } /* Add the glyph here as a field */ (void)place_field(px, py, FT_GLYPH_WARDING); /* Notice it */ note_spot(px, py); return TRUE; } /* * Leave an "explosive rune" which prevents monster movement */ bool explosive_rune(void) { int py = p_ptr->py; int px = p_ptr->px; cave_type *c_ptr = area(px, py); /* XXX XXX XXX */ if (!cave_naked_grid(c_ptr)) { msgf("The object resists the spell."); return FALSE; } /* Not in a wall */ if (cave_wall_grid(c_ptr)) { msgf("You need open space to draw the rune."); return FALSE; } /* Add the glyph here as a field */ (void)place_field(px, py, FT_GLYPH_EXPLODE); /* Notice it */ note_spot(px, py); return TRUE; } /* * Identify everything being carried. * Done by a potion of "self knowledge". */ void identify_pack(void) { int i; object_type *o_ptr; /* Identify equipment */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Identify it */ identify_item(o_ptr); } /* Identify inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Identify it */ identify_item(o_ptr); } OBJ_ITT_END; /* Notice changes */ notice_inven(); } /* * Try to remove a curse from an item * * Note that Items which are "Perma-Cursed" (The One Ring, * The Crown of Morgoth) can NEVER be uncursed. * * Note that if "all" is FALSE, then Items which are * "Heavy-Cursed" (Mormegil, Calris, and Weapons of Morgul) * will not be uncursed. * * If the item is heavily or permanently cursed, we add that flag * to the 'known' flags so the player can see that it is cursed on * the 'C'haracter screen. This may allow a player to learn that * an unidentified scroll is remove curse when it has no apparent * effect, in rare circumstances. */ static bool uncurse_item(object_type *o_ptr, bool all) { bool heavy; /* Uncursed already */ if (!cursed_p(o_ptr)) return (FALSE); /* Heavily Cursed Items need a special spell */ if (!all && (FLAG(o_ptr, TR_HEAVY_CURSE))) { /* Let the player know */ o_ptr->kn_flags[2] |= TR2_HEAVY_CURSE; /* Done */ return (FALSE); } /* Perma-Cursed Items can NEVER be uncursed */ if (FLAG(o_ptr, TR_PERMA_CURSE)) { /* Let the player know */ o_ptr->kn_flags[2] |= TR2_PERMA_CURSE; /* Done */ return (FALSE); } /* Uncurse the item */ o_ptr->flags[2] &= ~(TR2_CURSED | TR2_HEAVY_CURSE); o_ptr->kn_flags[2] &= ~(TR2_CURSED | TR2_HEAVY_CURSE); /* Heavy sensing? */ heavy = class_info[p_ptr->rp.pclass].heavy_sense; /* Take away the feeling */ o_ptr->feeling = FEEL_NONE; /* Strip awareness of feeling */ o_ptr->info &= ~(OB_SENSE); /* Renew feeling */ sense_item(o_ptr, heavy, TRUE, FALSE); /* Recalculate the bonuses */ p_ptr->update |= (PU_BONUS); /* Notice changes */ notice_item(); return (TRUE); } /* * Removes curses from items in inventory */ static int remove_curse_aux(bool all) { int i, cnt = 0; object_type *o_ptr; /* Attempt to uncurse equipment */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Count the uncursings */ if (uncurse_item(o_ptr, all)) cnt++; } /* Attempt to uncurse inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Count the uncursings */ if (uncurse_item(o_ptr, all)) cnt++; } OBJ_ITT_END; /* Return "something uncursed" */ return (cnt); } /* * Remove most curses */ bool remove_curse(void) { return (remove_curse_aux(FALSE)); } /* * Remove all curses */ bool remove_all_curse(void) { return (remove_curse_aux(TRUE)); } /* * Turns an object into gold, gain some of its value in a shop */ bool alchemy(void) { int amt = 1; int old_number; long price; bool force = FALSE; object_type *o_ptr; char o_name[256]; cptr q, s; /* Hack -- force destruction */ if (p_ptr->cmd.arg > 0) force = TRUE; /* Get an item */ q = "Turn which item to gold? "; s = "You have nothing to turn to gold."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* See how many items */ if (o_ptr->number > 1) { /* Get a quantity */ amt = get_quantity(NULL, o_ptr->number); /* Allow user abort */ if (amt <= 0) return FALSE; } /* Describe the object */ old_number = o_ptr->number; o_ptr->number = amt; object_desc(o_name, o_ptr, TRUE, 3, 256); o_ptr->number = old_number; /* Verify unless quantity given */ if (!force) { if (!(auto_destroy && (object_value(o_ptr) < 1))) { /* Make a verification */ if (!get_check("Really turn %s to gold? ", o_name)) return FALSE; } } /* Check for artifacts */ if (!can_player_destroy_object(o_ptr)) { /* Message */ msgf("You fail to turn %s to gold!", o_name); /* Done */ return FALSE; } price = object_value_real(o_ptr); if (price <= 0) { /* Message */ msgf("You turn %s to fool's gold.", o_name); } else { price /= 3; if (amt > 1) price *= amt; if (price > 30000) price = 30000; msgf("You turn %s to %ld coins worth of gold.", o_name, price); p_ptr->au += price; /* Redraw gold */ p_ptr->redraw |= (PR_GOLD); /* Window stuff */ p_ptr->window |= (PW_PLAYER); } /* Eliminate the item */ item_increase(o_ptr, -amt); return TRUE; } /* * Create stairs at the player location */ void stair_creation(void) { int py = p_ptr->py; int px = p_ptr->px; cave_type *c_ptr = area(px, py); /* XXX XXX XXX */ if (!cave_valid_grid(c_ptr)) { msgf("The object resists the spell."); return; } if (!check_down_wild()) return; /* XXX XXX XXX */ delete_object(px, py); if (!p_ptr->depth || ironman_downward) { /* Town/wilderness or Ironman */ cave_set_feat(px, py, FEAT_MORE); } else if (is_special_level(p_ptr->depth)) { /* Quest level */ cave_set_feat(px, py, FEAT_LESS); } else if (one_in_(2) || (p_ptr->depth >= dungeon()->max_level)) { cave_set_feat(px, py, FEAT_LESS); } else { cave_set_feat(px, py, FEAT_MORE); } } /* * Break the curse of an item */ static void break_curse(object_type *o_ptr) { if (cursed_p(o_ptr) && !(FLAG(o_ptr, TR_PERMA_CURSE)) && (randint0(100) < 25)) { msgf("The curse is broken!"); /* Uncurse it */ uncurse_item(o_ptr, TRUE); } } #define ENCHANT_MAX_DAM 25 #define ENCHANT_MAX 15 /* * Used by the "enchant" function (chance of failure) * * Formula: 1000-0.064x^3 */ static int enchant_table_dam[ENCHANT_MAX_DAM + 1] = { 0, 115, 221, 319, 407, 488, 561, 627, 686, 738, 784, 824, 859, 889, 914, 936, 953, 967, 978, 986, 992, 996, 998, 999, 999, 1000 }; /* * Used by the "enchant" function (chance of failure) */ static int enchant_table[ENCHANT_MAX + 1] = { 0, 10, 50, 100, 200, 300, 400, 500, 650, 800, 950, 987, 993, 995, 998, 1000 }; /* * Enchants a plus onto an item. -RAK- * * Revamped! Now takes item pointer, number of times to try enchanting, * and a flag of what to try enchanting. Artifacts resist enchantment * some of the time, and successful enchantment to at least +0 might * break a curse on the item. -CFT- * * Note that an item can technically be enchanted all the way to +15 if * you wait a very, very, long time. Going from +9 to +10 only works * about 5% of the time, and from +10 to +11 only about 1% of the time. * * Note that this function can now be used on "piles" of items, and * the larger the pile, the lower the chance of success. */ bool enchant(object_type *o_ptr, int n, int eflag) { int i, chance, prob, change; bool res = FALSE; bool a = ((FLAG(o_ptr, TR_INSTA_ART)) ? TRUE : FALSE); bool force = (eflag & ENCH_FORCE); /* Large piles resist enchantment */ prob = o_ptr->number * 100; /* Missiles are easy to enchant */ if ((o_ptr->tval == TV_BOLT) || (o_ptr->tval == TV_ARROW) || (o_ptr->tval == TV_SHOT)) { prob = prob / 50; } /* Some items are easier to enchant */ if (FLAG(o_ptr, TR_EASY_ENCHANT)) { /* Don't apply artifact failure chance */ a = FALSE; /* Apply more enchantment attempts */ n *= 2; } /* Try "n" times */ for (i = 0; i < n; i++) { /* Hack -- Roll for pile resistance */ if (!force && randint0(prob) >= 100) continue; /* Enchant to hit */ if (eflag & ENCH_TOHIT) { if (o_ptr->to_h < 0) chance = 0; else if (o_ptr->to_h > ENCHANT_MAX) chance = 1000; else chance = enchant_table[o_ptr->to_h]; if (force || ((randint1(1000) > chance) && (!a || one_in_(2)))) { /* The amount you enchant varys */ if ((o_ptr->to_h > 7) || force) change = 1; else if (o_ptr->to_h > 4) change = randint1(2); else change = randint1(3); o_ptr->to_h += change; res = TRUE; /* only when you get it above -1 -CFT */ if (o_ptr->to_h >= 0) break_curse(o_ptr); } } /* Enchant to damage */ if (eflag & ENCH_TODAM) { if (o_ptr->to_d < 0) chance = 0; else if (o_ptr->to_d > ENCHANT_MAX_DAM) chance = 1000; else chance = enchant_table_dam[o_ptr->to_d]; if (force || ((randint1(1000) > chance) && (!a || one_in_(2)))) { /* The amount you enchant varys */ if ((o_ptr->to_d > 7) || force) change = 1; else if (o_ptr->to_d > 4) change = randint1(2); else change = randint1(3); o_ptr->to_d += change; res = TRUE; /* only when you get it above -1 -CFT */ if (o_ptr->to_d >= 0) break_curse(o_ptr); } } /* Enchant to armor class */ if (eflag & ENCH_TOAC) { if (o_ptr->to_a < 0) chance = 0; else if (o_ptr->to_a > ENCHANT_MAX) chance = 1000; else chance = enchant_table[o_ptr->to_a]; if (force || ((randint1(1000) > chance) && (!a || one_in_(2)))) { /* The amount you enchant varys */ if ((o_ptr->to_a > 7) || force) change = 1; else if (o_ptr->to_a > 4) change = randint1(2); else change = randint1(3); o_ptr->to_a += change; res = TRUE; /* only when you get it above -1 -CFT */ if (o_ptr->to_a >= 0) break_curse(o_ptr); } } } /* Failure */ if (!res) return (FALSE); /* Apply trigger */ apply_object_trigger(TRIGGER_ALTER, o_ptr, ""); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); /* Success */ return (TRUE); } /* * Enchant an item (in the inventory or on the floor) * Note that "num_ac" requires armour, else weapon * Returns TRUE if attempted, FALSE if cancelled */ bool enchant_spell(int num_hit, int num_dam, int num_ac) { bool okay = FALSE; object_type *o_ptr; cptr q, s; /* Assume enchant weapon */ item_tester_hook = item_tester_hook_weapon; /* Enchant armor if requested */ if (num_ac) item_tester_hook = item_tester_hook_armour; /* Get an item */ q = "Enchant which item? "; s = "You have nothing to enchant."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* Describe */ msgf("The %v glow%s brightly!", OBJECT_FMT(o_ptr, FALSE, 0), ((o_ptr->number > 1) ? "" : "s")); /* Enchant */ if (enchant(o_ptr, num_hit, ENCH_TOHIT)) okay = TRUE; if (enchant(o_ptr, num_dam, ENCH_TODAM)) okay = TRUE; if (enchant(o_ptr, num_ac, ENCH_TOAC)) okay = TRUE; /* Failure */ if (!okay) { /* Flush */ if (flush_failure) flush(); /* Message */ msgf("The enchantment failed."); if (one_in_(3)) chg_virtue(V_ENCHANT, -1); } else chg_virtue(V_ENCHANT, 1); /* Something happened */ return (TRUE); } bool artifact_scroll(void) { bool okay; object_type *o_ptr; char o_name[256]; cptr q, s; /* Enchant weapon/armour */ item_tester_hook = item_tester_hook_weapon_armour; /* Get an item */ q = "Enchant which item? "; s = "You have nothing to enchant."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* Description */ object_desc(o_name, o_ptr, FALSE, 0, 256); /* Describe */ msgf("The %s radiate%s a blinding light!", o_name, ((o_ptr->number > 1) ? "" : "s")); /* No artifact creation of Dragon Scale Mail */ if (o_ptr->tval == TV_DRAG_ARMOR) { /* ToDo: Maybe allow some of the DSMs to be enchanted */ msgf("The %s %s already magical!", o_name, ((o_ptr->number > 1) ? "are" : "is")); okay = FALSE; } else if (o_ptr->xtra_name) { msgf("The %s %s already %s!", o_name, ((o_ptr->number > 1) ? "are" : "is"), ((o_ptr->number > 1) ? "powerful items" : "a powerful item")); okay = FALSE; } else { if (o_ptr->number > 1) { msgf ("Not enough enough energy to enchant more than one object!"); msgf("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, ((o_ptr->number > 2) ? "were" : "was")); o_ptr->number = 1; /* Notice weight changes */ p_ptr->update |= PU_WEIGHT; } /* The power of the generated artifact depends on player level */ okay = create_artifact(o_ptr, p_ptr->lev * 2, TRUE); } /* Failure */ if (!okay) { /* Flush */ if (flush_failure) flush(); /* Message */ msgf("The enchantment failed."); if (one_in_(3)) chg_virtue(V_ENCHANT, -1); } else chg_virtue(V_ENCHANT, 1); /* Something happened */ return (TRUE); } /* * Apply bad luck to an object */ static void bad_luck(object_type *o_ptr) { bool is_art = ((FLAG(o_ptr, TR_INSTA_ART)) ? TRUE : FALSE); object_type *q_ptr; /* Do not curse unwieldable items */ if (wield_slot(o_ptr) == -1) return; /* Objects become worse sometimes */ if (one_in_(13)) { int number = o_ptr->number; /* Non-artifacts get rerolled */ if (!is_art) { SET_FLAG(o_ptr, TR_CURSED); /* Prepare it */ q_ptr = object_prep(o_ptr->k_idx); /* Swap it */ swap_objects(o_ptr, q_ptr); /* Restore the number */ o_ptr->number = number; /* Apply bad magic */ apply_magic(o_ptr, p_ptr->depth, 0, OC_FORCE_BAD); } /* Now curse it */ SET_FLAG(o_ptr, TR_CURSED); } /* Objects are blasted sometimes */ if (one_in_(666) && (!is_art || one_in_(3))) { /* Blast it */ if (o_ptr->to_a) o_ptr->to_a = 0 - (s16b)rand_range(5, 10); if (o_ptr->to_h) o_ptr->to_h = 0 - (s16b)rand_range(5, 10); if (o_ptr->to_d) o_ptr->to_d = 0 - (s16b)rand_range(5, 10); o_ptr->ac = 0; o_ptr->dd = 1; o_ptr->ds = 1; o_ptr->flags[0] = 0; o_ptr->flags[1] = 0; o_ptr->flags[2] = 0; o_ptr->flags[3] = 0; add_ego_flags(o_ptr, EGO_BLASTED); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); } } /* * Identify an object */ void identify_item(object_type *o_ptr) { if (p_ptr->muta3 & MUT3_BAD_LUCK) { bad_luck(o_ptr); } if (!object_known_full(o_ptr)) { if (FLAG(o_ptr, TR_INSTA_ART)) chg_virtue(V_KNOWLEDGE, 3); else chg_virtue(V_KNOWLEDGE, 1); } /* Identify it fully */ object_aware(o_ptr); object_known(o_ptr); /* Save knowledge of artifact */ if (o_ptr->a_idx) { /* Have we seen it before? */ if (a_info[o_ptr->a_idx].cur_num != 2) { int artifact = o_ptr->a_idx; /* Notice a quest for this artifact */ trigger_quest_complete(QX_KNOW_ARTIFACT, &artifact); /* * If the item was an artifact, and if the * auto-note is selected, write a message. */ if (auto_notes && take_notes) { /* Write note */ add_note('A', "Found The %v", OBJECT_FMT(o_ptr, FALSE, 0)); } } a_info[o_ptr->a_idx].cur_num = 2; } /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); } static bool item_tester_unknown(const object_type *o_ptr) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* Check to see if we don't know the flavor */ if (k_ptr->flavor && !k_ptr->aware) return (TRUE); /* Check to see if we have identified the item */ if (object_known_p(o_ptr)) return (FALSE); return (TRUE); } static bool item_tester_unknown_star(const object_type *o_ptr) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; /* Check to see if we don't know the flavor */ if (k_ptr->flavor && !k_ptr->aware) return (TRUE); /* Check to see if we have identified the item */ if (object_known_full(o_ptr)) return (FALSE); return (TRUE); } /* * Identify an object in the inventory (or on the floor) * Returns TRUE if something was identified, else FALSE. * As a side effect it also sorts and combines the objects in the inventory. * * This has been rewritten so that when the identification was of an inventory * object the correct inv slot (after sorting and combining) is shown in the message. * To do this the routine combines and sorts the objects right after the * identification and before the message is generated. This way the combined * and sorted object will have the right letter for the slot in the message. * Except in the case where the player had 1 scroll of identify and that scroll * disappeared after use. So then the letter for the slot in the message is * one too high. This is solved by determining that the identification was by * scroll, there was only one scroll and so the letter must be one lower. */ static bool ident_spell_aux(int k_idx) { cptr q, s; object_type *o_ptr, *j_ptr; bool disappear = FALSE, back_step = FALSE, skip = FALSE; /* Only un-id'ed items */ item_tester_hook = item_tester_unknown; /* Get an item */ q = "Identify which item? "; s = "You have nothing to identify."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* Identify it */ identify_item(o_ptr); /* Hack. Do the sorting now */ o_ptr = reorder_pack_watch(o_ptr); /* Hack. Do the combining now */ o_ptr = combine_pack_watch(o_ptr); /* Find out if the id was by scroll */ OBJ_ITT_START (p_ptr->inventory, j_ptr) { /* No need to skip anything now */ skip = FALSE; /* Was it exactly one scroll? */ if (j_ptr->k_idx == k_idx && j_ptr->number == 1) { /* That will disappear */ disappear = TRUE; /* If you read the id scroll on itself, list it just once */ skip = TRUE; } /* Found the original */ if (o_ptr == j_ptr) { /* The id scroll is located before object */ if (disappear) back_step = TRUE; /* We know enough */ break; } } OBJ_ITT_END; /* Do we need the hack for the right letter in the inventory? */ if (back_step) { /* Not quite the description */ if (!skip) item_describe_faux(o_ptr); } else /* Description */ item_describe(o_ptr); /* Something happened */ return (TRUE); } /* Identify an object by some non-scroll method. */ bool ident_spell(void) { return (ident_spell_aux(0)); } /* Identify an object by reading an identify scroll */ bool ident_scroll(int k_idx) { return (ident_spell_aux(k_idx)); } /* * Mundanify an object in the inventory (or on the floor) * This routine does *not* automatically combine objects. * Returns TRUE if something was mundanified, else FALSE. */ bool mundane_spell(void) { object_type *o_ptr; object_kind *k_ptr; cptr q, s; /* Get an item */ q = "Use which item? "; s = "You have nothing you can use."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); k_ptr = &k_info[o_ptr->k_idx]; /* Oops */ msgf("There is a bright flash of light!"); /* No discount */ o_ptr->discount = 0; /* Not identified yet */ o_ptr->info &= ~(OB_SENSE | OB_KNOWN | OB_EMPTY | OB_STOREB); /* Erase the inscription */ quark_remove(&o_ptr->inscription); /* No longer a numbered artifact */ o_ptr->a_idx = 0; /* Erase the "feeling" */ o_ptr->feeling = FEEL_NONE; /* Default "pval" */ o_ptr->pval = k_ptr->pval; /* Default weight */ o_ptr->weight = k_ptr->weight; /* Default magic */ o_ptr->to_h = k_ptr->to_h; o_ptr->to_d = k_ptr->to_d; o_ptr->to_a = k_ptr->to_a; /* No longer artifact / ego item */ quark_remove(&o_ptr->xtra_name); /* Default power */ o_ptr->ac = k_ptr->ac; o_ptr->dd = k_ptr->dd; o_ptr->ds = k_ptr->ds; /* No artifact powers */ o_ptr->flags[0] = k_ptr->flags[0]; o_ptr->flags[1] = k_ptr->flags[1]; o_ptr->flags[2] = k_ptr->flags[2]; o_ptr->flags[3] = k_ptr->flags[3]; /* For rod-stacking */ if (o_ptr->tval == TV_ROD) { o_ptr->timeout = o_ptr->pval * o_ptr->number; o_ptr->pval = k_ptr->pval * o_ptr->number; } /* Initialise cost */ o_ptr->cost = k_ptr->cost; /* Something happened */ return (TRUE); } /* * Fully "identify" an object in the inventory -BEN- * This routine returns TRUE if an item was identified. */ bool identify_fully(void) { object_type *o_ptr; cptr q, s; /* Only un-*id*'ed items */ item_tester_hook = item_tester_unknown_star; /* Get an item */ q = "Identify which item? "; s = "You have nothing to *identify*."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* Identify it */ identify_item(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; /* Handle stuff */ handle_stuff(); /* Describe it fully */ identify_fully_aux(o_ptr); /* Success */ return (TRUE); } /* * Recharge a wand/staff/rod from the pack or on the floor. * This function has been rewritten in Oangband and ZAngband. * * Sorcery/Arcane -- Recharge --> recharge(plev * 4) * Chaos -- Arcane Binding --> recharge(90) * * Scroll of recharging --> recharge(130) * Artifact activation/Thingol --> recharge(130) * * It is harder to recharge high level, and highly charged wands, * staffs, and rods. The more wands in a stack, the more easily and * strongly they recharge. Staffs, however, each get fewer charges if * stacked. * * XXX XXX XXX Beware of "sliding index errors". */ bool recharge(int power) { int lev; int recharge_strength, recharge_amount; object_type *o_ptr; object_kind *k_ptr; bool fail = FALSE; byte fail_type = 1; cptr q, s; char o_name[256]; /* Only accept legal items */ item_tester_hook = item_tester_hook_recharge; /* Get an item */ q = "Recharge which item? "; s = "You have nothing to recharge."; o_ptr = get_item(q, s, (USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); /* Get the object kind. */ k_ptr = &k_info[o_ptr->k_idx]; /* * Extract the object "level" * (Rescaled due to change in dungeon distribtuion) */ lev = k_info[o_ptr->k_idx].level / 2; /* Recharge a rod */ if (o_ptr->tval == TV_ROD) { /* Extract a recharge strength by comparing object level to power. */ recharge_strength = ((power > lev) ? (power - lev) : 0) / 5; /* Back-fire */ if (one_in_(recharge_strength)) { /* Activate the failure code. */ fail = TRUE; } /* Recharge */ else { /* Recharge amount */ recharge_amount = (power * damroll(3, 2)); /* Recharge by that amount */ if (o_ptr->timeout > recharge_amount) o_ptr->timeout -= recharge_amount; else o_ptr->timeout = 0; } } /* Recharge wand/staff */ else { /* Extract a recharge strength by comparing object level to power. * Divide up a stack of wands' charges to calculate charge penalty. */ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1)) recharge_strength = (100 + power - lev - (8 * o_ptr->pval / o_ptr->number)) / 15; /* All staffs, unstacked wands. */ else recharge_strength = (100 + power - lev - (8 * o_ptr->pval)) / 15; /* Back-fire */ if ((recharge_strength < 0) || one_in_(recharge_strength)) { /* Activate the failure code. */ fail = TRUE; } /* If the spell didn't backfire, recharge the wand or staff. */ else { /* Recharge based on the standard number of charges. */ recharge_amount = randint1(1 + k_ptr->pval); /* Multiple wands in a stack increase recharging somewhat. */ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1)) { recharge_amount += (randint1(recharge_amount * (o_ptr->number - 1))) / 2; if (recharge_amount < 1) recharge_amount = 1; if (recharge_amount > 12) recharge_amount = 12; } /* But each staff in a stack gets fewer additional charges, * although always at least one. */ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) { recharge_amount /= o_ptr->number; if (recharge_amount < 1) recharge_amount = 1; } /* Recharge the wand or staff. */ o_ptr->pval += recharge_amount; /* Reduce "used" charges */ if (o_ptr->tval == TV_WAND) { o_ptr->ac -= recharge_amount; /* Never less than zero */ if (o_ptr->ac < 0) o_ptr->ac = 0; } /* Hack -- we no longer "memorize" the item */ o_ptr->info &= ~(OB_MENTAL); /* Hack -- we no longer "know" the item */ o_ptr->info &= ~(OB_KNOWN); /* Hack -- we no longer think the item is empty */ o_ptr->info &= ~(OB_EMPTY); } } /* Inflict the penalties for failing a recharge. */ if (fail) { /* Artifacts are never destroyed. */ if (FLAG(o_ptr, TR_INSTA_ART)) { msgf("The recharging backfires - %v is completely drained!", OBJECT_FMT(o_ptr, TRUE, 0)); /* Artifact rods. */ if ((o_ptr->tval == TV_ROD) && (o_ptr->timeout < 10000)) o_ptr->timeout = (o_ptr->timeout + 100) * 2; /* Artifact wands and staffs. */ else { if (o_ptr->tval == TV_WAND) { o_ptr->ac += o_ptr->pval; } o_ptr->pval = 0; } } else { /* Get the object description */ object_desc(o_name, o_ptr, FALSE, 0, 256); /*** Determine Seriousness of Failure ***/ /* (High) Mages recharge objects more safely. */ if ((p_ptr->rp.pclass == CLASS_MAGE) || (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) { /* 10% chance to blow up one rod, otherwise draining. */ if (o_ptr->tval == TV_ROD) { if (one_in_(10)) fail_type = 2; else fail_type = 1; } /* 75% chance to blow up one wand, otherwise draining. */ else if (o_ptr->tval == TV_WAND) { if (!one_in_(3)) fail_type = 2; else fail_type = 1; } /* 50% chance to blow up one staff, otherwise no effect. */ else if (o_ptr->tval == TV_STAFF) { if (one_in_(2)) fail_type = 2; else fail_type = 0; } } /* All other classes get no special favors. */ else { /* 33% chance to blow up one rod, otherwise draining. */ if (o_ptr->tval == TV_ROD) { if (one_in_(3)) fail_type = 2; else fail_type = 1; } /* 20% chance of the entire stack, else destroy one wand. */ else if (o_ptr->tval == TV_WAND) { if (one_in_(5)) fail_type = 3; else fail_type = 2; } /* Blow up one staff. */ else if (o_ptr->tval == TV_STAFF) { fail_type = 2; } } /*** Apply draining and destruction. ***/ /* Drain object or stack of objects. */ if (fail_type == 1) { if (o_ptr->tval == TV_ROD) { msgf("The recharge backfires, draining the rod further!"); if (o_ptr->timeout < 10000) o_ptr->timeout = (o_ptr->timeout + 100) * 2; } else if (o_ptr->tval == TV_WAND) { msgf("You save your %s from destruction, but all charges are lost.", o_name); o_ptr->ac += o_ptr->pval; o_ptr->pval = 0; } /* Staffs aren't drained. */ } /* Destroy an object or one in a stack of objects. */ if (fail_type == 2) { if (o_ptr->number > 1) msgf("Wild magic consumes one of your %s!", o_name); else msgf("Wild magic consumes your %s!", o_name); /* Reduce rod stack maximum timeout, drain wands. */ if (o_ptr->tval == TV_ROD) o_ptr->pval -= k_ptr->pval; if (o_ptr->tval == TV_WAND) { o_ptr->ac += o_ptr->pval; o_ptr->pval = 0; reduce_charges(o_ptr, 1); } /* Reduce and describe */ item_increase(o_ptr, -1); } /* Destroy all members of a stack of objects. */ if (fail_type == 3) { if (o_ptr->number > 1) msgf("Wild magic consumes all your %s!", o_name); else msgf("Wild magic consumes your %s!", o_name); /* Reduce and describe */ item_increase(o_ptr, -999); } } } /* Notice changes */ notice_inven(); /* Something was done */ return (TRUE); } /* * Bless a weapon */ bool bless_weapon(void) { object_type *o_ptr; char o_name[256]; cptr q, s; /* Assume enchant weapon */ item_tester_hook = item_tester_hook_weapon; /* Get an item */ q = "Bless which weapon? "; s = "You have weapon to bless."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return FALSE; /* Description */ object_desc(o_name, o_ptr, FALSE, 0, 256); if (cursed_p(o_ptr)) { if (((FLAG(o_ptr, TR_HEAVY_CURSE)) && (randint1(100) < 33)) || (FLAG(o_ptr, TR_PERMA_CURSE))) { msgf("The black aura on the %s disrupts the blessing!", o_name); return TRUE; } msgf("A malignant aura leaves the %s.", o_name); /* Uncurse it */ uncurse_item(o_ptr, TRUE); } /* * Next, we try to bless it. Artifacts have a 1/3 chance of * being blessed, otherwise, the operation simply disenchants * them, godly power negating the magic. Ok, the explanation * is silly, but otherwise priests would always bless every * artifact weapon they find. Ego weapons and normal weapons * can be blessed automatically. */ if (FLAG(o_ptr, TR_BLESSED)) { msgf("The %s %s blessed already.", o_name, ((o_ptr->number > 1) ? "were" : "was")); return TRUE; } if (!(o_ptr->xtra_name) || one_in_(3)) { /* Describe */ msgf("The %s shine%s!", o_name, ((o_ptr->number > 1) ? "" : "s")); SET_FLAG(o_ptr, TR_BLESSED); o_ptr->kn_flags[2] |= TR2_BLESSED; } else { bool dis_happened = FALSE; msgf("The artifact resists your blessing!"); /* Disenchant tohit */ if (o_ptr->to_h > 0) { o_ptr->to_h--; dis_happened = TRUE; } if ((o_ptr->to_h > 5) && (randint0(100) < 33)) o_ptr->to_h--; /* Disenchant todam */ if (o_ptr->to_d > 0) { o_ptr->to_d--; dis_happened = TRUE; } if ((o_ptr->to_d > 5) && (randint0(100) < 33)) o_ptr->to_d--; /* Disenchant toac */ if (o_ptr->to_a > 0) { o_ptr->to_a--; dis_happened = TRUE; } if ((o_ptr->to_a > 5) && (randint0(100) < 33)) o_ptr->to_a--; if (dis_happened) { msgf("There is a static feeling in the air..."); msgf("The %s %s disenchanted!", o_name, ((o_ptr->number > 1) ? "were" : "was")); } } /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Notice changes */ notice_item(); return TRUE; } /* * Potions "smash open" and cause an area effect when * (1) they are shattered while in the player's inventory, * due to cold (etc) attacks; * (2) they are thrown at a monster, or obstacle; * (3) they are shattered by a "cold ball" or other such spell * while lying on the floor. * * Arguments: * who --- who caused the potion to shatter (0=player) * potions that smash on the floor are assumed to * be caused by no-one (who = 1), as are those that * shatter inside the player inventory. * (Not anymore -- I changed this; TY) * y, x --- coordinates of the potion (or player if * the potion was in her inventory); * k_idx --- type of object. */ bool potion_smash_effect(int who, int x, int y, object_type *o_ptr) { int k_idx = o_ptr->k_idx; bool ident = FALSE; bool angry = FALSE; object_kind *k_ptr = &k_info[k_idx]; bool result = FALSE; apply_object_trigger(TRIGGER_SMASH, o_ptr, "iii:bb", LUA_VAR(who), LUA_VAR(x), LUA_VAR(y), LUA_RETURN(result), LUA_RETURN(ident)); angry = result; /* An identification was made */ if (ident && !(k_ptr->aware)) { k_ptr->aware = TRUE; gain_exp((k_ptr->level + p_ptr->lev / 2) / p_ptr->lev); } /* Notice changes */ notice_item(); return (angry); } /* * Hack -- Display all known spells in a window * * XXX XXX XXX Need more color coding. */ void display_spell_list(void) { int i, j; int y = 0, x = 0; int use_realm[2]; const magic_type *s_ptr; char name[80]; char out_val[160]; int row = 0, col = 0; unsigned int max_wid = 0; use_realm[0] = p_ptr->spell.r[0].realm - 1; use_realm[1] = p_ptr->spell.r[1].realm - 1; /* Erase window */ clear_from(0); /* Warriors are illiterate */ if (!mp_ptr->spell_book) return; /* Mindcrafter spell-list */ if (p_ptr->rp.pclass == CLASS_MINDCRAFTER) { int minfail; int plev = p_ptr->lev; int chance; mindcraft_power spell; char comment[80]; /* Display a list of spells */ put_fstr(x + 3, y, "Name"); put_fstr(x + 33, y, "Lv Mana Fail Info"); /* Dump the spells */ for (i = 0; (i < MINDCRAFT_MAX) && (i < Term->hgt - 1); i++) { cptr a = CLR_WHITE; /* Access the available spell */ spell = mindcraft_powers[i]; if (spell.min_lev > plev) break; /* Get the failure rate */ chance = spell.fail; /* Reduce failure rate by "effective" level adjustment */ chance -= 3 * (p_ptr->lev - spell.min_lev); /* Reduce failure rate by INT/WIS adjustment */ chance -= adj_mag_stat[p_ptr->stat[mp_ptr->spell_stat].ind] - 3; /* Not enough mana to cast */ if (spell.mana_cost > p_ptr->csp) { chance += 5 * (spell.mana_cost - p_ptr->csp); a = CLR_ORANGE; } /* Extract the minimum failure rate */ minfail = adj_mag_fail[p_ptr->stat[mp_ptr->spell_stat].ind]; /* Minimum failure rate */ if (chance < minfail) chance = minfail; /* Stunning makes spells harder */ if (p_ptr->tim.stun > 50) chance += 25; else if (p_ptr->tim.stun) chance += 15; /* Always a 5 percent chance of working */ if (chance > 95) chance = 95; /* Get info */ mindcraft_info(comment, i); /* Dump the spell */ put_fstr(x, y + i + 1, "%s%c) %-30s%2d %4d %3d%%%s", a, I2A(i), spell.name, spell.min_lev, spell.mana_cost, chance, comment); } return; } /* Normal spellcaster with books */ /* Scan books */ for (j = 0; j < ((use_realm[1] > -1) ? 2 : 1); j++) { int n = 0; /* Scan spells */ for (i = 0; i < 32; i++) { cptr a = CLR_WHITE; /* Access the spell */ s_ptr = &mp_ptr->info[use_realm[j]][i % 32]; strcpy(name, spell_names[use_realm[j]][i % 32]); /* Illegible */ if (s_ptr->slevel >= 99) { /* Illegible */ strcpy(name, "(illegible)"); /* Unusable */ a = CLR_L_DARK; } /* Forgotten */ else if (p_ptr->spell.r[j].forgotten & (1L << (i % 32))) { /* Forgotten */ a = CLR_ORANGE; } /* Unknown */ else if (!(p_ptr->spell.r[j].learned & (1L << (i % 32)))) { /* Unknown */ a = CLR_RED; } /* Untried */ else if (!(p_ptr->spell.r[j].worked & (1L << (i % 32)))) { /* Untried */ a = CLR_YELLOW; } /* Dump the spell --(-- */ strnfmt(out_val, 160, "%c/%c) %s", I2A(n / 8), I2A(n % 8), name); max_wid = MAX(max_wid, strlen(out_val) + 1); /* Dump onto the window */ put_fstr(col, row, "%s%s", a, out_val); /* Next row */ row++; if (row >= Term->hgt) { row = 0; col += max_wid; max_wid = 0; } /* Next */ n++; } } } /* * Returns spell chance of failure for spell -RAK- */ s16b spell_chance(int spell, int realm) { int chance, minfail; const magic_type *s_ptr; int smana; /* Paranoia -- must be literate */ if (!mp_ptr->spell_book) return (100); /* Access the spell */ s_ptr = &mp_ptr->info[realm][spell]; /* Extract the base spell failure rate */ if (realm == REALM_ARCANE-1) chance = s_ptr->slevel + 20; else chance = s_ptr->slevel * 3 / 2 + 20; /* Reduce failure rate by "effective" level adjustment */ chance -= 3 * (p_ptr->lev - s_ptr->slevel); /* Reduce failure rate by INT/WIS adjustment */ chance -= adj_mag_stat[p_ptr->stat[mp_ptr->spell_stat].ind]; /* Get mana cost */ smana = spell_mana(spell, realm); /* Not enough mana to cast */ if (smana > p_ptr->csp) { chance += 5 * (smana - p_ptr->csp); } /* Some mutations increase spell failure */ if ((p_ptr->muta3 & MUT3_MAGIC_RES) || (p_ptr->muta1 & MUT1_EAT_MAGIC)) { chance += 5; } if (realm == REALM_DEATH-1 && (p_ptr->muta1 & MUT1_BANISH)) { chance += 10; } if (p_ptr->muta3 & MUT3_SILLY_VOI) { chance += s_ptr->slevel; } /* Extract the minimum failure rate */ minfail = adj_mag_fail[p_ptr->stat[mp_ptr->spell_stat].ind]; /* * Non mage/priest characters never get too good * (added high mage, mindcrafter) */ if ((p_ptr->rp.pclass != CLASS_PRIEST) && (p_ptr->rp.pclass != CLASS_MAGE) && (p_ptr->rp.pclass != CLASS_MINDCRAFTER) && (p_ptr->rp.pclass != CLASS_HIGH_MAGE)) { if (minfail < 5) minfail = 5; } /* Hack -- Priest prayer penalty for "edged" weapons -DGK */ if ((p_ptr->rp.pclass == CLASS_PRIEST) && p_ptr->state.icky_wield) chance += 25; /* Minimum failure rate */ if (chance < minfail) chance = minfail; /* Stunning makes spells harder */ if (p_ptr->tim.stun > 50) chance += 25; else if (p_ptr->tim.stun) chance += 15; /* Always a 5 percent chance of working */ if (chance > 95) chance = 95; /* Return the chance */ return (chance); } /* * Returns spell mana cost for spell */ int spell_mana(int spell, int realm) { const magic_type *s_ptr; int smana; /* Paranoia -- must be literate */ if (!mp_ptr->spell_book) return (100); /* Access the spell */ s_ptr = &mp_ptr->info[realm][spell]; smana = s_ptr->smana; /* Chaos patrons improve chaos magic */ if ((realm == REALM_CHAOS - 1) && (FLAG(p_ptr, TR_PATRON))) { smana = (smana * 2 + 2) / 3; } return (smana); } /* * Determine if a spell is "okay" for the player to cast or study * The spell must be legible, not forgotten, and also, to cast, * it must be known, and to study, it must not be known. */ bool spell_okay(int spell, bool known, int realm) { const magic_type *s_ptr; /* Access the spell */ s_ptr = &mp_ptr->info[realm][spell]; /* Spell is illegal */ if (s_ptr->slevel > p_ptr->lev) return (FALSE); /* Spell is forgotten */ if ((realm == p_ptr->spell.r[1].realm - 1) ? (p_ptr->spell.r[1].forgotten & (1L << spell)) : (p_ptr->spell.r[0].forgotten & (1L << spell))) { /* Never okay */ return (FALSE); } /* Spell is learned */ if ((realm == p_ptr->spell.r[1].realm - 1) ? (p_ptr->spell.r[1].learned & (1L << spell)) : (p_ptr->spell.r[0].learned & (1L << spell))) { /* Okay to cast, not to study */ return (known); } /* Okay to study, not to cast */ return (!known); } /* * Extra information on a spell -DRS- * * We can use up to 14 characters of the buffer 'p' * * The strings in this function were extracted from the code in the * functions "do_cmd_cast()" and "do_cmd_pray()" and may be dated. */ void spell_info(char *p, int spell, int realm) { /* Default */ p[0] = 0; { int plev = p_ptr->lev; /* See below */ int orb = (plev / ((p_ptr->rp.pclass == CLASS_PRIEST || p_ptr->rp.pclass == CLASS_HIGH_MAGE) ? 2 : 4)); /* Analyze the spell */ switch (realm) { case 0: { /* Life */ switch (spell) { case 1: { strcpy(p, " heal 2d10"); break; } case 2: { /* Actually rand_range(12,24) */ strcpy(p, " dur 12+d12 turns"); break; } case 4: { strnfmt(p, 80, " dam 2d%d", (plev / 2)); break; } case 6: { strcpy(p, " heal 4d10"); break; } case 10: { strcpy(p, " heal 8d10"); break; } case 11: { /* Actually rand_range(24,48) */ strcpy(p, " dur 24+d24"); break; } case 12: { strnfmt(p, 80, " dam %d+3d6", plev + orb); break; } case 13: { strnfmt(p, 80, " dur %d+d25", 3 * plev); break; } case 14: { strcpy(p, " heal 300"); break; } case 16: { strnfmt(p, 80, " dam %d+%d", plev, plev); break; } case 18: { strnfmt(p, 80, " dam %d+%d", 3 * plev, 3 * plev); break; } case 20: { strnfmt(p, 80, " dam %d", 4 * plev); break; } case 22: { strnfmt(p, 80, " d %d/h 1000", 4 * plev); break; } case 24: { /* Actually rand_range(25,50) */ strcpy(p, " dur 25+d25"); break; } case 25: { /* Actually rand_range(50,100) */ strcpy(p, " dur 50+d50"); break; } case 28: { strcpy(p, " heal 2000"); break; } case 30: { strnfmt(p, 80, " h300/d%d+388", plev * 4); break; } case 31: { /* Actually rand_range(7,14) */ strcpy(p, " dur 7+d7"); break; } } break; } case 1: { /* Sorcery */ switch (spell) { case 1: { strcpy(p, " range 10"); break; } case 3: { strnfmt(p, 80, " dam 2d%d", (plev / 2)); break; } case 5: { strnfmt(p, 80, " range %d", plev * 5); break; } case 13: { strnfmt(p, 80, " dur %d+d%d", plev, plev + 20); break; } case 19: { strnfmt(p, 80, " range %d", plev + 2); break; } case 20: { /* Actually rand_range(25,55) */ strcpy(p, " dur 25+d30"); break; } case 23: { strcpy(p, " delay 15+d21"); break; } case 25: { strnfmt(p, 80, " max wgt %d", plev * 15 / 10); break; } case 26: { strnfmt(p, 80, " dam %d+7d7", plev / 2); break; } case 27: { /* Actually rand_range(25,55) */ strcpy(p, " dur 25+d30"); break; } case 31: { /* Actually rand_range(8,16) */ strcpy(p, " dur 8+d8"); break; } } break; } case 2: { /* Nature */ switch (spell) { case 1: { strcpy(p, " heal 2d8"); break; } case 4: { strnfmt(p, 80, " dam 2d%d", (plev / 2)); break; } case 6: { /* Actually rand_range(20,40) */ strcpy(p, " dur 20+d20"); break; } case 9: { strnfmt(p, 80, " dam %dd8", (3 + ((plev - 5) / 4))); break; } case 11: { strnfmt(p, 80, " dam %dd8", (5 + ((plev - 5) / 4))); break; } case 12: { strcpy(p, " dam 6d8"); break; } case 15: { strcpy(p, " heal 1000"); break; } case 18: { /* Actually rand_range(30,50) */ strcpy(p, " dur 20+d30"); break; } case 19: { /* Actually rand_range(20,40) */ strcpy(p, " dur 20+d20"); break; } case 24: { strcpy(p, " rad 10"); break; } case 26: { strnfmt(p, 80, " dam %d", 70 + plev); break; } case 27: { strnfmt(p, 80, " dam %d", 90 + plev); break; } case 28: { strnfmt(p, 80, " dam %d", 100 + plev); break; } case 29: { strcpy(p, " dam 75"); break; } case 31: { strnfmt(p, 80, " dam %d+%d", 4 * plev, (100 + plev) / 2); break; } } break; } case 3: { /* Chaos */ switch (spell) { case 0: { strnfmt(p, 80, " dam %dd4", 3 + ((plev - 1) / 5)); break; } case 2: { strnfmt(p, 80, " dam 2d%d", (plev / 2)); break; } case 4: { strnfmt(p, 80, " dam %d+3d5", plev + (plev / (((p_ptr->rp.pclass == CLASS_MAGE) || (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) ? 2 : 4))); break; } case 5: { strnfmt(p, 80, " dam %dd8", (8 + ((plev - 5) / 4))); break; } case 6: { strnfmt(p, 80, " dam %dd8", (8 + ((plev - 5) / 4))); break; } case 7: { strnfmt(p, 80, " range %d", plev * 5); break; } case 8: { strcpy(p, " random"); break; } case 9: { strnfmt(p, 80, " dam %dd8", (10 + ((plev - 5) / 4))); break; } case 10: { strnfmt(p, 80, " dam %d", (45 + plev) / 2); break; } case 11: { strnfmt(p, 80, " dam %dd8", (11 + ((plev - 5) / 4))); break; } case 12: { strnfmt(p, 80, " dam %d", 55 + plev); break; } case 15: { strnfmt(p, 80, " dam %d", 66 + plev); break; } case 17: { strnfmt(p, 80, " dam %dd8", (5 + (plev / 10))); break; } case 19: { strnfmt(p, 80, " dam %d", 80 + plev); break; } case 24: { strnfmt(p, 80, " dam %dd8", (9 + ((plev - 5) / 4))); break; } case 25: { strnfmt(p, 80, " dam %d each", (3 * plev) / 2); break; } case 26: { strnfmt(p, 80, " dam %d", 75 + plev); break; } case 27: { strcpy(p, " dam 75 / 150"); break; } case 28: { strnfmt(p, 80, " dam %d", 120 + plev); break; } case 29: { strnfmt(p, 80, " dam %d", 300 + (plev * 2)); break; } case 30: { strnfmt(p, 80, " dam %d", p_ptr->chp); break; } case 31: { strcpy(p, " dam 3 * 175"); break; } } break; } case 4: { /* Death */ switch (spell) { case 1: { strnfmt(p, 80, " dam %dd3", (3 + ((plev - 1) / 5))); break; } case 3: { strnfmt(p, 80, " dam %d", 10 + (plev / 2)); break; } case 5: { /* Actually rand_range(20,40) */ strnfmt(p, 80, " dur 20+d20"); break; } case 8: { strnfmt(p, 80, " dam %d+3d6", plev + (plev / (((p_ptr->rp.pclass == CLASS_MAGE) || (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) ? 2 : 4))); break; } case 9: { strnfmt(p, 80, " dam %dd8", (6 + ((plev - 5) / 4))); break; } case 11: { strnfmt(p, 80, " dm %d+%d*d15", plev, MAX(1, plev / 10)); break; } case 13: { strnfmt(p, 80, " dam %d", 4 * plev); break; } case 16: { /* Actually rand_range(25,50) */ strcpy(p, " dur 25+d25"); break; } case 17: { strcpy(p, " random"); break; } case 18: { strnfmt(p, 80, " dam %dd8", (4 + ((plev - 5) / 4))); break; } case 19: { /* This is too complicated to give accurately */ strcpy(p, " max dur 50"); break; } case 20: { strcpy(p, " dam 3*100"); break; } case 22: { strcpy(p, " dam 120"); break; } case 27: { strnfmt(p, 80, " dam %d", plev * 3); break; } case 28: { strnfmt(p, 80, " dam %d", plev * 4); break; } case 29: { strcpy(p, " dam 666"); break; } case 31: { /* Actually rand_range(plev/2,plev) */ strnfmt(p, 80, " dur %d+d%d", (plev / 2), (plev / 2)); break; } } break; } case 5: { /* Trump */ switch (spell) { case 0: { strcpy(p, " range 10"); break; } case 1: { strnfmt(p, 80, " dam %dd3", 3 + ((plev - 1) / 5)); break; } case 2: { strcpy(p, " random"); break; } case 4: { strnfmt(p, 80, " range %d", plev * 4); break; } case 5: { strnfmt(p, 80, " range %d", plev + 2); break; } case 6: { /* Actually rand_range(25,55) */ strcpy(p, " dur 25+d30"); break; } case 8: { strnfmt(p, 80, " max wgt %d", plev * 15 / 10); break; } case 14: { strcpy(p, " delay 15+d21"); break; } case 22: { strnfmt(p, 80, " dam %d", plev * 3); /* break; */ } } break; } case 6: { /* Arcane */ switch (spell) { case 0: { strnfmt(p, 80, " dam %dd3", 3 + ((plev - 1) / 5)); break; } case 4: { strcpy(p, " range 10"); break; } case 5: { strnfmt(p, 80, " dam 2d%d", plev / 2); break; } case 7: { strcpy(p, " heal 2d8"); break; } case 14: case 15: case 16: case 17: { /* Actually rand_range(20,40) */ strcpy(p, " dur 20+d20"); break; } case 18: { strcpy(p, " heal 4d8"); break; } case 19: { strnfmt(p, 80, " range %d", plev * 5); break; } case 21: { strcpy(p, " dam 6d8"); break; } case 23: { /* Actually rand_range(24,48) */ strcpy(p, " dur 24+d24"); break; } case 28: { strnfmt(p, 80, " dam %d", 75 + plev); break; } case 30: { strcpy(p, " delay 15+d21"); break; } case 31: { /* Actually rand_range(25,55) */ strcpy(p, " dur 25+d30"); break; } } break; } default: { strnfmt(p, 80, "Unknown type: %d.", realm); } } } } /* * Print a list of spells (for browsing or casting or viewing) */ void print_spells(byte *spells, int num, int x, int y, int realm) { int i, spell; const magic_type *s_ptr; cptr comment; char info[80]; if (((realm < 0) || (realm >= MAX_REALM)) && p_ptr->state.wizard) msgf("Warning! print_spells called with null realm"); /* Title the list */ prtf(x, y, ""); put_fstr(x + 5, y, "Name"); put_fstr(x + 35, y, "Lv Mana Fail Info"); /* Dump the spells */ for (i = 0; i < num; i++) { /* Access the spell */ spell = spells[i]; /* Access the spell */ s_ptr = &mp_ptr->info[realm][spell]; /* Skip illegible spells */ if (s_ptr->slevel >= 99) { prtf(x, y + i + 1, CLR_L_DARK " %c) %-30s", I2A(i), "(illegible)"); continue; } /* XXX XXX Could label spells above the players level */ /* Get extra info */ spell_info(info, spell, realm); /* Use that info */ comment = info; /* Analyze the spell */ if ((realm + 1 != p_ptr->spell.r[0].realm) && (realm + 1 != p_ptr->spell.r[1].realm)) { comment = CLR_SLATE " uncastable"; } /* We know these books */ else if ((realm + 1 == p_ptr->spell.r[0].realm) ? ((p_ptr->spell.r[0].forgotten & (1L << spell))) : ((p_ptr->spell.r[1].forgotten & (1L << spell)))) { comment = CLR_YELLOW " forgotten"; } else if (!((realm + 1 == p_ptr->spell.r[0].realm) ? (p_ptr->spell.r[0].learned & (1L << spell)) : (p_ptr->spell.r[1].learned & (1L << spell)))) { comment = CLR_L_BLUE " unknown"; } else if (!((realm + 1 == p_ptr->spell.r[0].realm) ? (p_ptr->spell.r[0].worked & (1L << spell)) : (p_ptr->spell.r[1].worked & (1L << spell)))) { comment = CLR_L_GREEN " untried"; } /* Dump the spell --(-- */ prtf(x, y + i + 1, " %c) %-30s%2d %4d %3d%%%s", I2A(i), spell_names[realm][spell], (int)s_ptr->slevel, spell_mana(spell, realm), spell_chance(spell, realm), comment); } /* Clear the bottom line */ prtf(x, y + i + 1, ""); } /* * Note that amulets, rods, and high-level spell books are immune * to "inventory damage" of any kind. Also sling ammo and shovels. */ /* * Does a given class of objects (usually) hate acid? * Note that acid can either melt or corrode something. */ bool hates_acid(const object_type *o_ptr) { switch (o_ptr->tval) { /* Wearable items */ case TV_ARROW: case TV_BOLT: case TV_BOW: case TV_SWORD: case TV_HAFTED: case TV_POLEARM: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Analyze the type */ return (TRUE); } case TV_STAFF: case TV_SCROLL: { /* Staffs/Scrolls are wood/paper */ return (TRUE); } case TV_CHEST: { /* Ouch */ return (TRUE); } case TV_SKELETON: case TV_BOTTLE: case TV_JUNK: { /* Junk is useless */ return (TRUE); } } return (FALSE); } /* * Does a given object (usually) hate electricity? */ bool hates_elec(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_RING: case TV_WAND: { return (TRUE); } } return (FALSE); } /* * Does a given object (usually) hate fire? * Hafted/Polearm weapons have wooden shafts. * Arrows/Bows are mostly wooden. */ bool hates_fire(const object_type *o_ptr) { /* Analyze the type */ switch (o_ptr->tval) { case TV_LITE: case TV_ARROW: case TV_BOW: case TV_HAFTED: case TV_POLEARM: case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_SOFT_ARMOR: { /* Wearable */ return (TRUE); } case TV_LIFE_BOOK: case TV_SORCERY_BOOK: case TV_NATURE_BOOK: case TV_CHAOS_BOOK: case TV_DEATH_BOOK: case TV_TRUMP_BOOK: case TV_ARCANE_BOOK: { /* Books */ return (TRUE); } case TV_CHEST: { /* Chests */ return (TRUE); } case TV_STAFF: case TV_SCROLL: { /* Staffs/Scrolls burn */ return (TRUE); } } return (FALSE); } /* * Does a given object (usually) hate cold? */ bool hates_cold(const object_type *o_ptr) { switch (o_ptr->tval) { case TV_POTION: case TV_FLASK: case TV_BOTTLE: { return (TRUE); } } return (FALSE); } /* * Melt something */ int set_acid_destroy(object_type *o_ptr) { if (!hates_acid(o_ptr)) return (FALSE); if (FLAG(o_ptr, TR_IGNORE_ACID)) return (FALSE); return (TRUE); } /* * Electrical damage */ int set_elec_destroy(object_type *o_ptr) { if (!hates_elec(o_ptr)) return (FALSE); if (FLAG(o_ptr, TR_IGNORE_ELEC)) return (FALSE); return (TRUE); } /* * Burn something */ int set_fire_destroy(object_type *o_ptr) { if (!hates_fire(o_ptr)) return (FALSE); if (FLAG(o_ptr, TR_IGNORE_FIRE)) return (FALSE); return (TRUE); } /* * Freeze things */ int set_cold_destroy(object_type *o_ptr) { if (!hates_cold(o_ptr)) return (FALSE); if (FLAG(o_ptr, TR_IGNORE_COLD)) return (FALSE); return (TRUE); } /* * Destroys a type of item on a given percent chance * Note that missiles are no longer necessarily all destroyed * Destruction taken from "melee.c" code for "stealing". * New-style wands and rods handled correctly. -LM- * Returns number of items destroyed. */ int inven_damage(inven_func typ, int perc) { int j, k, amt; object_type *o_ptr; int slot; /* Count the casualties */ k = 0; /* Scan the inventory */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Hack -- for now, skip artifacts */ if (FLAG(o_ptr, TR_INSTA_ART)) continue; /* Give this item slot a shot at death */ if ((*typ) (o_ptr)) { /* Count the casualties */ for (amt = j = 0; j < o_ptr->number; ++j) { if (randint0(100) < perc) amt++; } /* Some casualities */ if (amt) { /* Get slot */ slot = get_item_position(p_ptr->inventory, o_ptr); /* Message */ msgf("%sour %v (%c) %s destroyed!", ((o_ptr->number > 1) ? ((amt == o_ptr->number) ? "All of y" : (amt > 1 ? "Some of y" : "One of y")) : "Y"), OBJECT_FMT(o_ptr, FALSE, 3), I2A(slot), ((amt > 1) ? "were" : "was")); /* Potions smash open */ if (object_is_potion(o_ptr)) { int px = p_ptr->px; int py = p_ptr->py; (void)potion_smash_effect(0, px, py, o_ptr); } /* Reduce the charges of rods/wands */ reduce_charges(o_ptr, amt); /* Destroy "amt" items */ item_increase(o_ptr, -amt); /* Count the casualties */ k += amt; } } } OBJ_ITT_END; /* Return the casualty count */ return (k); } bool rustproof(void) { object_type *o_ptr; cptr q, s; /* Select a piece of armour */ item_tester_hook = item_tester_hook_armour_no_acid; /* Get an item */ q = "Rustproof which piece of armour? "; s = "You have nothing to rustproof."; o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!o_ptr) return (FALSE); SET_FLAG(o_ptr, TR_IGNORE_ACID); o_ptr->kn_flags[2] |= TR2_IGNORE_ACID; if ((o_ptr->to_a < 0) && !(cursed_p(o_ptr))) { msgf("The %v look%s as good as new!", OBJECT_FMT(o_ptr, FALSE, 0), ((o_ptr->number > 1) ? "" : "s")); o_ptr->to_a = 0; } msgf("The %v %s now protected against corrosion.", OBJECT_FMT(o_ptr, FALSE, 0), ((o_ptr->number > 1) ? "are" : "is")); return TRUE; } /* * Curse the players armor */ bool curse_armor(void) { object_type *o_ptr; /* Curse the body armor */ o_ptr = &p_ptr->equipment[EQUIP_BODY]; /* Nothing to curse */ if (!o_ptr->k_idx) return (FALSE); /* Attempt a saving throw for artifacts */ if ((FLAG(o_ptr, TR_INSTA_ART)) && !one_in_(3)) { /* Cool */ msgf("A terrible black aura tries to surround your armor, but your %v resists the effects!", OBJECT_FMT(o_ptr, FALSE, 3)); } /* not artifact or failed save... */ else { /* Oops */ msgf("A terrible black aura blasts your %v!", OBJECT_FMT(o_ptr, FALSE, 3)); chg_virtue(V_ENCHANT, -5); /* Blast the armor */ o_ptr->to_a = 0 - (s16b)rand_range(5, 10); o_ptr->to_h = 0; o_ptr->to_d = 0; o_ptr->ac = 0; o_ptr->dd = 1; o_ptr->ds = 1; o_ptr->flags[0] = 0; o_ptr->flags[1] = 0; o_ptr->flags[2] = 0; o_ptr->flags[3] = 0; /* Lose your feeling */ o_ptr->feeling = FEEL_NONE; add_ego_flags(o_ptr, EGO_BLASTED); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); } return (TRUE); } /* * Curse the players weapon */ bool curse_weapon(void) { object_type *o_ptr; /* Curse the weapon */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Nothing to curse */ if (!o_ptr->k_idx) return (FALSE); /* Attempt a saving throw */ if ((FLAG(o_ptr, TR_INSTA_ART)) && !one_in_(3)) { /* Cool */ msgf("A terrible black aura tries to surround your weapon, but your %v resists the effects!", OBJECT_FMT(o_ptr, FALSE, 3)); } /* not artifact or failed save... */ else { /* Oops */ msgf("A terrible black aura blasts your %v!", OBJECT_FMT(o_ptr, FALSE, 3)); chg_virtue(V_ENCHANT, -5); /* Shatter the weapon */ o_ptr->to_h = 0 - (s16b)rand_range(5, 10); o_ptr->to_d = 0 - (s16b)rand_range(5, 10); o_ptr->to_a = 0; o_ptr->ac = 0; o_ptr->dd = 1; o_ptr->ds = 1; o_ptr->flags[0] = 0; o_ptr->flags[1] = 0; o_ptr->flags[2] = 0; o_ptr->flags[3] = 0; /* Lose your feeling */ o_ptr->feeling = FEEL_NONE; add_ego_flags(o_ptr, EGO_SHATTERED); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Notice changes */ notice_item(); } /* Notice */ return (TRUE); } /* * Enchant some bolts */ bool brand_bolts(void) { object_type *o_ptr; /* Use the first acceptable bolts */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Skip non-bolts */ if (o_ptr->tval != TV_BOLT) continue; /* Skip artifacts and ego-items */ if (o_ptr->xtra_name) continue; /* Skip cursed/broken items */ if (cursed_p(o_ptr) || !o_ptr->cost) continue; /* Randomize */ if (randint0(100) < 75) continue; /* Message */ msgf("Your bolts are covered in a fiery aura!"); /* Ego-item */ add_ego_flags(o_ptr, EGO_FLAME); /* Enchant */ (void)enchant(o_ptr, rand_range(2, 6), ENCH_TOHIT | ENCH_TODAM); /* Notice changes */ notice_inven(); /* Notice */ return (TRUE); } OBJ_ITT_END; /* Flush */ if (flush_failure) flush(); /* Fail */ msgf("The fiery enchantment failed."); /* Notice */ return (TRUE); } /* * Helper function -- return a "nearby" race for polymorphing * * Note that this function is one of the more "dangerous" ones... */ static s16b poly_r_idx(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; int i, r, lev1, lev2; /* Hack -- Uniques/Questors never polymorph */ if (FLAG(r_ptr, RF_UNIQUE) || FLAG(r_ptr, RF_QUESTOR)) return (r_idx); /* Allowable range of "levels" for resulting monster */ lev1 = r_ptr->level - ((randint1(20) / randint1(9)) + 1); lev2 = r_ptr->level + ((randint1(20) / randint1(9)) + 1); /* Pick a (possibly new) non-unique race */ for (i = 0; i < 1000; i++) { /* Pick a new race, using a level calculation */ r = get_mon_num((p_ptr->depth + r_ptr->level) / 2 + 5); /* Handle failure */ if (!r) break; /* Obtain race */ r_ptr = &r_info[r]; /* Ignore unique monsters */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Ignore monsters with incompatible levels */ if ((r_ptr->level < lev1) || (r_ptr->level > lev2)) continue; /* Use that index */ r_idx = r; /* Done */ break; } /* Result */ return (r_idx); } bool polymorph_monster(int x, int y) { cave_type *c_ptr = area(x, y); monster_type *m_ptr = &m_list[c_ptr->m_idx]; bool friendly, pet; bool polymorphed = FALSE; int new_r_idx; int old_r_idx = m_ptr->r_idx; /* Get the monsters attitude */ friendly = is_friendly(m_ptr); pet = is_pet(m_ptr); /* Pick a "new" monster race */ new_r_idx = poly_r_idx(old_r_idx); /* Handle polymorph */ if (new_r_idx != old_r_idx) { /* "Kill" the "old" monster */ delete_monster_idx(c_ptr->m_idx); /* Create a new monster (no groups) */ if (place_monster_aux(x, y, new_r_idx, FALSE, FALSE, friendly, pet, TRUE)) { /* Success */ polymorphed = TRUE; } else { /* Placing the new monster failed - use the old. */ (void)place_monster_aux(x, y, old_r_idx, FALSE, FALSE, friendly, pet, TRUE); } } /* Update some things */ p_ptr->update |= (PU_MON_LITE); return (polymorphed); } /* * Dimension Door */ bool dimension_door(void) { int px = p_ptr->px; int py = p_ptr->py; int plev = p_ptr->lev; int x = 0, y = 0; cave_type *c_ptr; if (!tgt_pt(&x, &y)) return FALSE; p_ptr->energy -= 60 - plev; /* paranoia */ if (!in_bounds2(x, y)) return FALSE; c_ptr = area(x, y); if (!cave_empty_grid(c_ptr) || (c_ptr->info & CAVE_ICKY) || (distance(x, y, px, py) > plev + 2) || (one_in_(plev * plev / 2))) { msgf("You fail to exit the astral plane correctly!"); p_ptr->energy -= 100; teleport_player(10); } else teleport_player_to(x, y); return (TRUE); } /* * Map the wilderness */ void map_wilderness(int radius, s32b x, s32b y) { int i, j; int dist; /* Map a rough circle around the target position in the wilderness */ for (i = x - radius; i < x + radius + 1; i++) { for (j = y - radius; j < y + radius + 1; j++) { /* In bounds? */ if ((i >= 0) && (i < max_wild - 1) && (j >= 0) && (j < max_wild - 1)) { dist = distance(i, j, x, y); if ((randint0(dist) < radius / 2) && (dist < radius)) { /* Memorise the location */ wild[j][i].done.info |= WILD_INFO_SEEN; /* Alert player to a new wilderness quest */ discover_wild_quest(place[wild[j][i].done.place].quest_num); } } } } } void sanity_blast(const monster_type *m_ptr) { int power = 100; monster_race *r_ptr = &r_info[m_ptr->r_idx]; power = r_ptr->hdice * 2 + 10; if (!FLAG(r_ptr, RF_UNIQUE)) { if (FLAG(r_ptr, RF_FRIENDS)) power -= 50; } else { power += 50; } /* Can we see it? */ if (!m_ptr->ml) return; /* Paranoia */ if (!FLAG(r_ptr, RF_ELDRITCH_HORROR)) return; /* Pet eldritch horrors are safe most of the time */ if (is_pet(m_ptr) && !one_in_(8)) return; /* Do we pass the saving throw? */ if (player_save(power)) return; if (p_ptr->tim.image) { /* Something silly happens... */ msgf("You behold the %s visage of %v!", funny_desc[randint0(MAX_SAN_FUNNY)], MONSTER_FMT(m_ptr, 0)); if (one_in_(3)) { msgf(funny_comments[randint0(MAX_SAN_COMMENT)]); (void)inc_image(randint1(r_ptr->hdice * 2)); } /* Never mind; we can't see it clearly enough */ return; } /* Something frightening happens... */ msgf("You behold the %s visage of %v!", horror_desc[randint0(MAX_SAN_HORROR)], MONSTER_FMT(m_ptr, 0)); /* Monster memory */ r_ptr->r_flags[3] |= RF3_ELDRITCH_HORROR; /* Demon characters are unaffected */ if (p_ptr->rp.prace == RACE_IMP) return; /* Undead characters are 50% likely to be unaffected */ if (((p_ptr->rp.prace == RACE_SKELETON) || (p_ptr->rp.prace == RACE_ZOMBIE) || (p_ptr->rp.prace == RACE_VAMPIRE) || (p_ptr->rp.prace == RACE_SPECTRE) || (p_ptr->rp.prace == RACE_GHOUL)) && saving_throw(25 + p_ptr->lev)) return; /* Mind blast */ if (!player_save(power)) { if ((!(FLAG(p_ptr, TR_RES_FEAR))) || one_in_(5)) { /* Get afraid, even if have resist fear! */ (void)inc_afraid(rand_range(10, 20)); } if (!(FLAG(p_ptr, TR_RES_CHAOS))) { (void)inc_image(rand_range(150, 400)); } return; } if (lose_all_info()) { msgf("You forget everything in your utmost terror!"); } p_ptr->update |= PU_BONUS; handle_stuff(); } zangband/src/store.c0000755000000000000000000015546410250356274013455 0ustar rootroot/* File: store.c */ /* Purpose: Store commands */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #define MAX_COMMENT_1 6 static cptr comment_1[MAX_COMMENT_1] = { "Okay.", "Fine.", "Accepted!", "Agreed!", "Done!", "Taken!" }; /* * Successful haggle. */ static void say_comment_1(void) { msgf(comment_1[randint0(MAX_COMMENT_1)]); } /* * Messages for reacting to purchase prices. */ #define MAX_COMMENT_7A 4 static cptr comment_7a[MAX_COMMENT_7A] = { "Arrgghh!", "You bastard!", "You hear someone sobbing...", "The shopkeeper howls in agony!" }; #define MAX_COMMENT_7B 4 static cptr comment_7b[MAX_COMMENT_7B] = { "Damn!", "You fiend!", "The shopkeeper curses at you.", "The shopkeeper glares at you." }; #define MAX_COMMENT_7C 4 static cptr comment_7c[MAX_COMMENT_7C] = { "Cool!", "You've made my day!", "The shopkeeper giggles.", "The shopkeeper laughs loudly." }; #define MAX_COMMENT_7D 4 static cptr comment_7d[MAX_COMMENT_7D] = { "Yipee!", "I think I'll retire!", "The shopkeeper jumps for joy.", "The shopkeeper smiles gleefully." }; /* * Let a shop-keeper React to a purchase * * We paid "price", it was worth "value", and we thought it was worth "guess" */ static void purchase_analyze(s32b price, s32b value, s32b guess) { /* Item was worthless, but we bought it */ if ((value <= 0) && (price > value)) { /* Comment */ msgf(MSGT_STORE1, comment_7a[randint0(MAX_COMMENT_7A)]); chg_virtue(V_HONOUR, -1); chg_virtue(V_JUSTICE, -1); /* Sound */ sound(SOUND_STORE1); } /* Item was cheaper than we thought, and we paid more than necessary */ else if ((value < guess) && (price > value)) { /* Comment */ msgf(MSGT_STORE2, comment_7b[randint0(MAX_COMMENT_7B)]); chg_virtue(V_JUSTICE, -1); if (one_in_(4)) chg_virtue(V_HONOUR, -1); /* Sound */ sound(SOUND_STORE2); } /* Item was a good bargain, and we got away with it */ else if ((value > guess) && (value < (4 * guess)) && (price < value)) { /* Comment */ msgf(MSGT_STORE3, comment_7c[randint0(MAX_COMMENT_7C)]); if (one_in_(4)) chg_virtue(V_HONOUR, -1); else if (one_in_(4)) chg_virtue(V_HONOUR, 1); /* Sound */ sound(SOUND_STORE3); } /* Item was a great bargain, and we got away with it */ else if ((value > guess) && (price < value)) { /* Comment */ msgf(MSGT_STORE4, comment_7d[randint0(MAX_COMMENT_7D)]); if (one_in_(2)) chg_virtue(V_HONOUR, -1); if (one_in_(4)) chg_virtue(V_HONOUR, 1); if (10 * price < value) chg_virtue(V_SACRIFICE, 1); /* Sound */ sound(SOUND_STORE4); } } /* * We store the current "store pointer" here so everyone can access it */ static store_type *st_ptr = NULL; /* * We store the current field here so that it can be accessed everywhere */ static const field_type *f_ptr = NULL; /* Save info flags for store */ static byte info_flags; /* * Determine the price of an item (qty one) in a store. * * This function takes into account the player's charisma, and the * shop-keepers friendliness, and the shop-keeper's base greed, but * never lets a shop-keeper lose money in a transaction. * * The "greed" value should exceed 100 when the player is "buying" the * item, and should be less than 100 when the player is "selling" it. * * Hack -- the black market always charges twice as much as it should. * * Charisma adjustment runs from 80 to 130 * Racial adjustment runs from 95 to 130 * * Since greed/charisma/racial adjustments are centered at 100, we need * to adjust (by 200) to extract a usable multiplier. Note that the * "greed" value is always something (?). */ s32b price_item(object_type *o_ptr, bool flip) { int factor; int adjust; s32b price; int greed = st_ptr->greed; /* Get the value of one of the items */ price = object_value(o_ptr); /* Worthless items */ if (price <= 0) return (0L); /* The charisma factor */ factor = adj_chr_gold[p_ptr->stat[A_CHR].ind]; /* Shop is buying */ if (flip) { /* Adjust for greed */ adjust = 100 + (200 - (greed + factor)); /* Never get "silly" */ if (adjust > 100) adjust = 100; /* Mega-Hack -- Black market sucks */ if ((info_flags & ST_GREED) || (info_flags & ST_ULTRA_GREED)) { price = price / 2; } } /* Shop is selling */ else { /* Adjust for greed */ adjust = 100 + ((greed + factor) - 200); /* Never get "silly" */ if (adjust < 100) adjust = 100; /* Mega-Hack -- Black market sucks */ if (info_flags & ST_GREED) price = price * 2; if (info_flags & ST_ULTRA_GREED) price = price * 4; } /* Compute the final price (with rounding) */ price = (price * adjust + 50L) / 100L; /* Cap the price */ if (flip && price > st_ptr->max_cost * 100L) { price = st_ptr->max_cost * 100L; } /* Note -- Never become "free" */ if (price <= 0L) return (1L); /* Hack - save price for object list code */ o_ptr->temp_cost = price; /* Return the price */ return (price); } /* * Certain "cheap" objects should be created in "piles" * Some objects can be sold at a "discount" (in small piles) */ static void mass_produce(object_type *o_ptr) { int size = 1; int discount = 0; s32b cost = object_value(o_ptr); /* Analyze the type */ switch (o_ptr->tval) { case TV_FOOD: case TV_FLASK: case TV_LITE: { /* Food, Flasks, and Lites */ if (cost <= 5L) size += damroll(3, 5); if (cost <= 20L) size += damroll(3, 5); break; } case TV_POTION: case TV_SCROLL: { if (cost <= 60L) size += damroll(3, 5); if (cost <= 240L) size += damroll(1, 5); break; } case TV_LIFE_BOOK: case TV_SORCERY_BOOK: case TV_NATURE_BOOK: case TV_CHAOS_BOOK: case TV_DEATH_BOOK: case TV_TRUMP_BOOK: case TV_ARCANE_BOOK: { if (cost <= 50L) size += damroll(2, 3); if (cost <= 500L) size += damroll(1, 3); break; } case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_SHIELD: case TV_GLOVES: case TV_BOOTS: case TV_CLOAK: case TV_HELM: case TV_CROWN: case TV_SWORD: case TV_POLEARM: case TV_HAFTED: case TV_DIGGING: case TV_BOW: { if (o_ptr->xtra_name) break; if (cost <= 10L) size += damroll(3, 5); if (cost <= 100L) size += damroll(3, 5); break; } case TV_SPIKE: case TV_SHOT: case TV_ARROW: case TV_BOLT: { if (cost <= 5L) size += damroll(5, 5); if (cost <= 50L) size += damroll(5, 5); if (cost <= 500L) size += damroll(5, 5); break; } case TV_FIGURINE: case TV_STATUE: { if (cost <= 100L) size += damroll(2, 2); if (cost <= 1000L) size += damroll(2, 2); break; } case TV_ROD: case TV_WAND: case TV_STAFF: { if (one_in_(3)) { if (cost < 1601L) size += damroll(1, 5); else if (cost < 3201L) size += damroll(1, 3); } /* * Ensure that mass-produced rods and wands * get the correct pvals. */ if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_WAND)) { o_ptr->pval *= size; } break; } } /* Pick a discount */ if (cost < 5) { discount = 0; } else if (one_in_(25)) { discount = 25; } else if (one_in_(150)) { discount = 50; } else if (one_in_(300)) { discount = 75; } else if (one_in_(500)) { discount = 90; } if (o_ptr->xtra_name) { if (cheat_peek && discount) { msgf("No discount on powerful items."); } discount = 0; } /* Save the discount */ o_ptr->discount = discount; /* Save the total pile size */ o_ptr->number = size - (size * discount / 100); } /* * Determine if a store item can "absorb" another item * * See "object_similar()" for the same function for the "player" */ static bool store_object_similar(const object_type *o_ptr, const object_type *j_ptr) { /* Hack -- Identical items cannot be stacked */ if (o_ptr == j_ptr) return (FALSE); /* Different objects cannot be stacked */ if (o_ptr->k_idx != j_ptr->k_idx) return (FALSE); /* Different charges (etc) cannot be stacked, unless wands or rods. */ if ((o_ptr->pval != j_ptr->pval) && (o_ptr->tval != TV_WAND) && (o_ptr->tval != TV_ROD)) return (FALSE); /* Require many identical values */ if (o_ptr->to_h != j_ptr->to_h) return (FALSE); if (o_ptr->to_d != j_ptr->to_d) return (FALSE); if (o_ptr->to_a != j_ptr->to_a) return (FALSE); /* Artifacts and ego items don't stack ! */ if (o_ptr->xtra_name || j_ptr->xtra_name) return (FALSE); /* Hack -- Identical flags! */ if ((o_ptr->flags[0] != j_ptr->flags[0]) || (o_ptr->flags[1] != j_ptr->flags[1]) || (o_ptr->flags[2] != j_ptr->flags[2]) || (o_ptr->flags[3] != j_ptr->flags[3])) return (FALSE); /* Require identical recharge times / fuel level */ if (o_ptr->timeout != j_ptr->timeout) return (FALSE); /* Require many identical values */ if (o_ptr->ac != j_ptr->ac) return (FALSE); if (o_ptr->dd != j_ptr->dd) return (FALSE); if (o_ptr->ds != j_ptr->ds) return (FALSE); /* Hack -- Never stack chests */ if (o_ptr->tval == TV_CHEST) return (FALSE); /* Require matching discounts */ if (o_ptr->discount != j_ptr->discount) return (FALSE); /* They match, so they must be similar */ return (TRUE); } /* * Allow a store item to absorb another item */ static void store_object_absorb(object_type *o_ptr, const object_type *j_ptr) { int total = o_ptr->number + j_ptr->number; /* Combine quantity, lose excess items */ o_ptr->number = (total > 99) ? 99 : total; /* * Hack -- if rods are stacking, add the pvals * (maximum timeouts) together. -LM- */ if (o_ptr->tval == TV_ROD) { o_ptr->pval += j_ptr->pval; } /* Hack -- if wands are stacking, combine the charges. -LM- */ if (o_ptr->tval == TV_WAND) { o_ptr->pval += j_ptr->pval; /* No "used" charges in store stock */ o_ptr->ac = 0; } } /* * Check to see if the shop will be carrying too many objects * Note that the shop, just like a player, will not accept things * it cannot hold. Before, one could "nuke" potions this way. */ static bool store_check_num(const object_type *o_ptr) { object_type *j_ptr; /* Free space is always usable */ if (get_list_length(st_ptr->stock) < st_ptr->max_stock) return TRUE; /* The "home" acts like the player */ if (st_ptr->type == BUILD_STORE_HOME) { /* Check all the items */ OBJ_ITT_START (st_ptr->stock, j_ptr) { /* Can the new object be combined with the old one? */ if (object_similar(j_ptr, o_ptr)) return (TRUE); } OBJ_ITT_END; } /* Normal stores do special stuff */ else { /* Check all the items */ OBJ_ITT_START (st_ptr->stock, j_ptr) { /* Can the new object be combined with the old one? */ if (store_object_similar(j_ptr, o_ptr)) return (TRUE); } OBJ_ITT_END; } /* But there was no room at the inn... */ return (FALSE); } /* * Determine if the current store will purchase the given item * (Check restriction flags, and object theme) */ static bool store_will_buy(const object_type *o_ptr) { obj_theme theme; /* Check restriction flags */ /* Blessed items only */ if (info_flags & ST_REST_BLESSED) { if (!item_tester_hook_is_blessed(o_ptr)) return (FALSE); } /* Good items only */ if ((info_flags & ST_REST_GOOD) || (info_flags & ST_REST_GREAT)) { if (!item_tester_hook_is_good(o_ptr)) return (FALSE); } /* Ignore "worthless" items XXX XXX XXX */ if (object_value(o_ptr) <= 0) return (FALSE); /* Final stage, check the theme */ /* Set theme */ theme.treasure = f_ptr->data[3]; theme.combat = f_ptr->data[4]; theme.magic = f_ptr->data[5]; theme.tools = f_ptr->data[6]; /* Initialise the theme tester */ init_match_theme(theme); /* * Final check: * Does the object have a chance of being made? */ return (kind_is_theme(o_ptr->k_idx)); } /* * The player wants to sell something to the store. * This is a much more restrictive case than the store_will_buy() * function below. * Only objects that pass the field hooks will be accepted. * (As well as only selecting store_will_buy() objects. * * Two field action functions are called for a store. * The first checks to see if a store will not buy something. * The second checks the opposite. * By combining different action functions, lots of different * types of store can be made. */ static bool store_will_stock(const object_type *o_ptr) { /* Default is to reject this rejection */ bool result = FALSE; /* Will the store !not! buy this item? */ field_script_const(f_ptr, FIELD_ACT_STORE_ACT1, "p:b", LUA_OBJECT(o_ptr), LUA_RETURN(result)); /* We don't want this item type? */ if (result == TRUE) return (FALSE); /* Change the default to acceptance */ result = TRUE; /* Will the store buy this item? */ field_script_const(f_ptr, FIELD_ACT_STORE_ACT2, "p:b", LUA_OBJECT(o_ptr), LUA_RETURN(result)); /* Finally check to see if we will buy the item */ return (result && store_will_buy(o_ptr)); } /* * Compare two items to see if they are in store-order. */ static bool reorder_store_comp(const object_type *o1_ptr, const object_type *o2_ptr) { /* Hack -- readable books always come first */ if ((o1_ptr->tval == mp_ptr->spell_book) && (o2_ptr->tval != mp_ptr->spell_book)) return (TRUE); if ((o1_ptr->tval == mp_ptr->spell_book) && (o2_ptr->tval != mp_ptr->spell_book)) return (FALSE); /* Objects sort by decreasing type */ if (o1_ptr->tval > o2_ptr->tval) return (TRUE); if (o1_ptr->tval < o2_ptr->tval) return (FALSE); /* Can happen in the home */ if (!object_aware_p(o2_ptr)) return (TRUE); if (!object_aware_p(o1_ptr)) return (FALSE); /* Objects sort by increasing sval */ if (o1_ptr->sval < o2_ptr->sval) return (TRUE); if (o1_ptr->sval > o2_ptr->sval) return (FALSE); /* Objects in the home can be unknown */ if (!object_known_p(o2_ptr)) return (TRUE); if (!object_known_p(o1_ptr)) return (FALSE); /* Objects sort by decreasing value */ if (object_value(o1_ptr) > object_value(o2_ptr)) return (TRUE); return (FALSE); } /* * Add the item "o_ptr" to the inventory of the "Home" * * In all cases, return the slot (or -1) where the object was placed * * Note that this is a hacked up version of "inven_carry()". * * Also note that it may not correctly "adapt" to "knowledge" becoming * known, the player may have to pick stuff up and drop it again. */ static object_type *home_carry(object_type *o_ptr) { object_type *j_ptr; /* Check each existing item (try to combine) */ OBJ_ITT_START (st_ptr->stock, j_ptr) { /* The home acts just like the player */ if (object_similar(j_ptr, o_ptr)) { /* Save the new number of items */ object_absorb(j_ptr, o_ptr); /* All done */ return (j_ptr); } } OBJ_ITT_END; /* No space? */ if (get_list_length(st_ptr->stock) >= st_ptr->max_stock) return (NULL); /* Add the item to the store */ o_ptr = add_object_list(&st_ptr->stock, o_ptr); /* Paranoia */ if (!o_ptr) return (NULL); /* Forget location */ o_ptr->iy = o_ptr->ix = 0; /* Forget Region */ o_ptr->region = 0; /* No longer marked */ o_ptr->info &= ~(OB_SEEN); /* Reorder the items */ o_ptr = reorder_objects_aux(o_ptr, reorder_store_comp, st_ptr->stock); chg_virtue(V_SACRIFICE, -1); /* Return the location */ return (o_ptr); } /* * Add the item "o_ptr" to a real stores inventory. * * If the item is "worthless", it is thrown away (except in the home). * * If the item cannot be combined with an object already in the inventory, * make a new slot for it, and calculate its "per item" price. Note that * this price will be negative, since the price will not be "fixed" yet. * Adding an item to a "fixed" price stack will not change the fixed price. * * In all cases, return the slot (or NULL) where the object was placed */ static object_type *store_carry(object_type *o_ptr) { object_type *j_ptr; /* Evaluate the object */ s32b value = object_value(o_ptr); /* Cursed/Worthless items "disappear" when sold */ if (value <= 0) return (NULL); /* Identify it fully */ object_known(o_ptr); object_mental(o_ptr); /* Save all the known flags */ o_ptr->kn_flags[0] = o_ptr->flags[0]; o_ptr->kn_flags[1] = o_ptr->flags[1]; o_ptr->kn_flags[2] = o_ptr->flags[2]; o_ptr->kn_flags[3] = o_ptr->flags[3]; /* Erase the inscription */ quark_remove(&o_ptr->inscription); /* Erase the "feeling" */ o_ptr->feeling = FEEL_NONE; /* Check each existing item (try to combine) */ OBJ_ITT_START (st_ptr->stock, j_ptr) { /* Can the existing items be incremented? */ if (store_object_similar(j_ptr, o_ptr)) { /* Hack -- extra items disappear */ store_object_absorb(j_ptr, o_ptr); /* All done */ return (j_ptr); } } OBJ_ITT_END; /* No space? */ if (get_list_length(st_ptr->stock) >= st_ptr->max_stock) return (NULL); /* Add the item to the store */ o_ptr = add_object_list(&st_ptr->stock, o_ptr); /* Paranoia */ if (!o_ptr) return (NULL); /* Forget location */ o_ptr->iy = o_ptr->ix = 0; /* Forget Region */ o_ptr->region = 0; /* No longer marked */ o_ptr->info &= ~(OB_SEEN); /* Reorder the items */ o_ptr = reorder_objects_aux(o_ptr, reorder_store_comp, st_ptr->stock); /* Return the location */ return (o_ptr); } /* * Attempt to delete (some of) a random item from the store * Hack -- we attempt to "maintain" piles of items when possible. */ static void store_delete(void) { int what, num; object_type *o_ptr; /* Pick a random slot */ what = randint0(get_list_length(st_ptr->stock)); /* Get the item */ o_ptr = get_list_item(st_ptr->stock, what); /* Determine how many items are here */ num = o_ptr->number; /* Hack -- sometimes, only destroy half the items */ if (one_in_(2)) num = (num + 1) / 2; /* Hack -- sometimes, only destroy a single item */ if (one_in_(2)) num = 1; /* * Hack -- decrement the maximum timeouts and * total charges of rods and wands. -LM- */ if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_WAND)) { o_ptr->pval -= num * o_ptr->pval / o_ptr->number; } /* Actually destroy (part of) the item */ item_increase_silent(o_ptr, -num); } /* * Creates a random item and gives it to a store */ static void store_create(void) { int kind, tries, level, delta; byte flags; object_type *q_ptr; obj_theme theme; byte restricted = f_ptr->data[7]; /* Paranoia -- no room left */ if (get_list_length(st_ptr->stock) >= st_ptr->max_stock) return; /* Set theme */ theme.treasure = f_ptr->data[3]; theme.combat = f_ptr->data[4]; theme.magic = f_ptr->data[5]; theme.tools = f_ptr->data[6]; /* Select items based on "theme" */ init_match_theme(theme); /* Prepare allocation table */ get_obj_num_prep(kind_is_theme); /* Hack -- consider up to fifty items */ for (tries = 0; tries < 50; tries++) { /* Assume no flags */ flags = OC_NORMAL; /* Assume no bonus */ delta = 0; /* Get level to use */ level = rand_range(f_ptr->data[1], f_ptr->data[2]); /* Get an item */ kind = get_obj_num(level, 0); /* Handle failure */ if (!kind) continue; /* Create a new object of the chosen kind */ q_ptr = object_prep(kind); /* Create object based on restrictions */ if (restricted & ST_REST_GREAT) { /* Apply "great" magic */ delta = 30; flags = OC_FORCE_GOOD; } else if (restricted & ST_REST_GOOD) { /* Apply "good" magic */ delta = 15; } /* Occasionally generate unusually good items */ while (one_in_(30)) { delta += rand_range(5, 15); } /* Apply some magic */ apply_magic(q_ptr, level, delta, flags); /* Mega-Hack -- no chests in stores */ if (q_ptr->tval == TV_CHEST) continue; /* The item is "known" */ object_known(q_ptr); /* Mark it storebought */ q_ptr->info |= OB_STOREB; q_ptr->info |= OB_NO_EXP; /* Require valid object */ if (!store_will_stock(q_ptr)) continue; /* Hack -- Charge lite's */ if (q_ptr->tval == TV_LITE) { if (q_ptr->sval == SV_LITE_TORCH) q_ptr->timeout = FUEL_TORCH / 2; if (q_ptr->sval == SV_LITE_LANTERN) q_ptr->timeout = FUEL_LAMP / 2; } /* Mass produce and/or Apply discount */ mass_produce(q_ptr); /* Attempt to carry the (known) item */ (void)store_carry(q_ptr); /* Definitely done */ break; } } /* * Re-displays a single store entry */ static void display_entry(int pos) { int i; /* Get the object */ object_type *o_ptr = get_list_item(st_ptr->stock, pos); s32b x; byte a; char c; int maxwid; int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Get the "offset" */ i = (pos % 12); /* Label it, clear the line --(-- */ prtf(0, i + 6, "%c) ", I2A(i)); /* Show_store_graph perm on. */ a = object_attr(o_ptr); c = object_char(o_ptr); /* Hack -- fake monochrome */ if (!use_color) { a = TERM_WHITE; c = ' '; } if (object_aware_p(o_ptr)) Term_draw(3, i + 6, a, c); /* Describe an item in the home */ if (st_ptr->type == BUILD_STORE_HOME) { maxwid = wid - 4; /* Leave room for weights, if necessary -DRS- */ if (show_weights) maxwid -= 10; /* Describe the object */ put_fstr(5, i + 6, "%s" CLR_SET_DEFAULT "%v", color_seq[tval_to_attr[o_ptr->tval]], OBJECT_FMT(o_ptr, TRUE, 3)); /* Show weights */ if (show_weights) { /* Only show the weight of an individual item */ int wgt = o_ptr->weight; put_fstr(wid - 12, i + 6, "%3d.%d lb", wgt / 10, wgt % 10); } } /* Describe an item (fully) in a store */ else { /* Must leave room for the "price" */ maxwid = wid - 14; /* Leave room for weights, if necessary -DRS- */ if (show_weights) maxwid -= 7; /* Describe the object (fully) */ put_fstr(5, i + 6, "%s" CLR_SET_DEFAULT "%v", color_seq[tval_to_attr[o_ptr->tval]], OBJECT_STORE_FMT(o_ptr, TRUE, 3)); /* Show weights */ if (show_weights) { /* Only show the weight of an individual item */ int wgt = o_ptr->weight; put_fstr(wid - 19, i + 6, "%3d.%d", wgt / 10, wgt % 10); } /* Extract the "minimum" price */ x = price_item(o_ptr, FALSE); /* Actually draw the price */ put_fstr(wid - 12, i + 6, "%9ld ", (long)x); } } /* * Displays a store's inventory * All prices are listed as "per individual object". -BEN- */ static void display_inventory(void) { int k; int stocknum = get_list_length(st_ptr->stock); /* Display the next 12 items */ for (k = 0; k < 12; k++) { /* Do not display "dead" items */ if (p_ptr->state.store_top + k >= stocknum) break; /* Display that line */ display_entry(p_ptr->state.store_top + k); } /* Erase the extra lines and the "more" prompt */ clear_region(0, k + 6, 18); /* Assume "no current page" */ put_fstr(20, 5, " "); /* Visual reminder of "more items" */ if (stocknum > 12) { /* Show "more" reminder (after the last item) */ prtf(3, k + 6, "-more-"); /* Indicate the "current page" */ put_fstr(20, 5, "(Page %d)", p_ptr->state.store_top / 12 + 1); } } /* * Displays players gold -RAK- */ static void store_prt_gold(void) { prtf(53, 19, "Gold Remaining: "); prtf(68, 19, "%9ld", (long)p_ptr->au); } /* * Displays store (after clearing screen) -RAK- */ static void display_store(void) { int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Clear screen */ Term_clear(); /* The "Home" is special */ if (st_ptr->type == BUILD_STORE_HOME) { /* Put the owner name */ put_fstr(30, 3, "Your Home"); /* Label the item descriptions */ put_fstr(3, 5, "Item Description"); /* If showing weights, show label */ if (show_weights) { put_fstr(70, 5, "Weight"); } } /* Normal stores */ else { cptr store_name = field_name(f_ptr); cptr owner_name = quark_str(st_ptr->owner_name); /* Put the owner name */ put_fstr(5, 3, "%s", owner_name); /* Show the max price in the store (above prices) */ put_fstr(45, 3, "%s (%ld)", store_name, (long)(st_ptr->max_cost) * 100); /* Label the item descriptions */ put_fstr(3, 5, "Item Description"); /* If showing weights, show label */ if (show_weights) { put_fstr(wid - 20, 5, "Weight"); } /* Label the asking price (in stores) */ put_fstr(wid - 8, 5, "Price"); } /* Display the current gold */ store_prt_gold(); /* Draw in the inventory */ display_inventory(); /* Basic commands */ prtf(0, 22, " ESC) Exit from Building."); /* Browse if necessary */ if (get_list_length(st_ptr->stock) > 12) { prtf(0, 23, " SPACE) Next page of stock"); } /* Home commands */ if (st_ptr->type == BUILD_STORE_HOME) { prtf(31, 22, " g) Get an item."); prtf(31, 23, " d) Drop an item."); } /* Shop commands XXX XXX XXX */ else { prtf(31, 22, " p) Purchase an item."); prtf(31, 23, " s) Sell an item."); } /* Add in the eXamine option */ prtf(56, 22, " x) eXamine an item."); /* Prompt */ prtf(0, 21, "You may: "); /* Refresh */ Term_fresh(); } /* * Maintain the inventory at the stores. */ static void store_maint(void) { int i = 0, j; /* Ignore home + locker */ if (st_ptr->type == BUILD_STORE_HOME) return; /* Choose the number of slots to keep */ j = get_list_length(st_ptr->stock); /* Sell a few items */ j = j - randint1(STORE_TURNOVER); if (st_ptr->max_stock == STORE_INVEN_MAX) { /* Never keep more than "STORE_MAX_KEEP" slots */ if (j > STORE_MAX_KEEP1) j = STORE_MAX_KEEP1; /* Always "keep" at least "STORE_MIN_KEEP" items */ if (j < STORE_MIN_KEEP1) j = STORE_MIN_KEEP1; } else { /* The store has half the normal inventory space */ /* Never keep more than "STORE_MAX_KEEP" slots */ if (j > STORE_MAX_KEEP2) j = STORE_MAX_KEEP2; /* Always "keep" at least "STORE_MIN_KEEP" items */ if (j < STORE_MIN_KEEP2) j = STORE_MIN_KEEP2; } /* Hack -- prevent "underflow" (This should never happen anyway.) */ if (j < 0) j = 0; /* Destroy objects until only "j" slots are left */ while (get_list_length(st_ptr->stock) > j) store_delete(); /* Choose the number of slots to fill */ j = get_list_length(st_ptr->stock); /* Buy some more items */ j = j + randint1(STORE_TURNOVER); if (st_ptr->max_stock == STORE_INVEN_MAX) { /* Never keep more than "STORE_MAX_KEEP" slots */ if (j > STORE_MAX_KEEP1) j = STORE_MAX_KEEP1; /* Always "keep" at least "STORE_MIN_KEEP" items */ if (j < STORE_MIN_KEEP1) j = STORE_MIN_KEEP1; } else { /* The store has half the normal inventory space */ /* Never keep more than "STORE_MAX_KEEP" slots */ if (j > STORE_MAX_KEEP2) j = STORE_MAX_KEEP2; /* Always "keep" at least "STORE_MIN_KEEP" items */ if (j < STORE_MIN_KEEP2) j = STORE_MIN_KEEP2; } /* Hack -- prevent "overflow" (This shouldn't do anything) */ if (j >= st_ptr->max_stock) j = st_ptr->max_stock - 1; /* Acquire some new items */ while ((get_list_length(st_ptr->stock) < j) && (i < 30)) { /* Increment counter so we avoid taking too long */ i++; /* Try to allocate some items */ store_create(); } } /* * Shuffle one of the stores. */ static void store_shuffle(store_type *st_ptr) { cptr own_name = owner_names[randint0(owner_names_max)]; cptr own_suffix = owner_suffix[randint0(owner_suffix_max)]; object_type *o_ptr; /* Ignore home + locker */ if (st_ptr->type == BUILD_STORE_HOME) return; /* Pick a new owner */ st_ptr->owner_name = quark_fmt("%s %s", own_name, own_suffix); /* These are set in place_sb() via the lua hook below */ st_ptr->greed = 0; st_ptr->max_cost = 0; /* * Hack - Init store * * Note that this assumes the player is in this store */ field_script_const(f_ptr, FIELD_ACT_SB_INIT, ""); /* Reset the owner data */ st_ptr->data = 0; /* Hack -- discount all the items */ OBJ_ITT_START (st_ptr->stock, o_ptr) { /* Hack -- Sell all old items for "half price" */ if (!(o_ptr->xtra_name)) { o_ptr->discount = 50; } /* Mega-Hack -- Note that the item is "on sale" */ o_ptr->inscription = quark_add("on sale"); } OBJ_ITT_END; } /* * Get the ID of a store item and return its value */ static int get_stock(int *com_val, cptr pmt, int maxobj) { char command; char out_val[160]; /* Get the item index */ if (repeat_pull(com_val)) { /* Verify the item */ if ((*com_val >= 0) && (*com_val < maxobj)) { /* Success */ return (TRUE); } } else { /* Invalid repeat - reset it */ repeat_clear(); } /* Paranoia XXX XXX XXX */ message_flush(); /* Assume failure */ *com_val = (-1); /* Build the prompt */ strnfmt(out_val, 160, "(Items a-%c, ESC to exit) %s", I2A(maxobj - 1), pmt); /* Ask until done */ while (TRUE) { int k; /* Escape */ if (!get_com(out_val, &command)) break; /* Convert */ k = (islower(command) ? A2I(command) : -1); /* Legal responses */ if ((k >= 0) && (k < maxobj)) { *com_val = k; break; } /* Oops */ bell("Illegal store object choice!"); } /* Clear the prompt */ clear_msg(); /* Cancel */ if (command == ESCAPE) return (FALSE); repeat_push(*com_val); /* Success */ return (TRUE); } static bool store_access_item(const object_type *o_ptr, s32b price, bool buy) { if (buy) { /* Describe the object (fully) */ put_fstr(0, 1, "%s %v", (buy) ? "Buying" : "Selling", OBJECT_STORE_FMT(o_ptr, TRUE, 3)); } else { /* Describe the object (only what we know) */ put_fstr(0, 1, "%s %v", (buy) ? "Buying" : "Selling", OBJECT_FMT(o_ptr, TRUE, 3)); } put_fstr(0, 2, "Offer : %ld", (long)price); /* Ask the user for a response */ if (check_transaction && !get_check_ext(TRUE, FALSE, buy ? "Buy? ": "Sell? ")) { return (FALSE); } /* Chose to make transaction */ return (TRUE); } /* * Buy an item from a store */ static void store_purchase(void) { int i, amt; int item, item_new; s32b price, best; object_type *j_ptr; object_type *o_ptr; char out_val[160]; /* Empty? */ if (!st_ptr->stock) { if (st_ptr->type == BUILD_STORE_HOME) msgf("Your home is empty."); else msgf("I am currently out of stock."); return; } /* Find the number of objects on this and following pages */ i = (get_list_length(st_ptr->stock) - p_ptr->state.store_top); /* And then restrict it to the current page */ if (i > 12) i = 12; /* Prompt */ if (st_ptr->type == BUILD_STORE_HOME) { strnfmt(out_val, 160, "Which item do you want to take? "); } else { strnfmt(out_val, 160, "Which item are you interested in? "); } /* Get the item number to be bought */ if (!get_stock(&item, out_val, i)) return; /* Get the actual index */ item = item + p_ptr->state.store_top; /* Get the actual item */ o_ptr = get_list_item(st_ptr->stock, item); /* Assume the player wants just one of them */ amt = 1; /* Get a duplicate of the object */ j_ptr = object_dup(o_ptr); /* Recalculate charges for a single wand/rod */ reduce_charges(j_ptr, j_ptr->number - 1); /* Modify quantity */ j_ptr->number = amt; /* Hack -- require room in pack */ if (!inven_carry_okay(j_ptr)) { msgf("You cannot carry that many different items."); return; } /* Determine the "best" price (per item) */ best = price_item(j_ptr, FALSE); /* * Paranoia - you can only buy one weapon / armour item at a time * * This prevents the player getting stacks of weapons etc. in * his pack. I suppose we could make an extension to * inven_carry_okay() to do this properly. */ if ((j_ptr->tval < TV_BOW) || (j_ptr->tval > TV_DRAG_ARMOR)) { /* Find out how many the player wants */ if (o_ptr->number > 1) { /* Describe the object (fully) */ put_fstr(0, 1, "%s %v", "Buying", OBJECT_STORE_FMT(o_ptr, TRUE, 3)); /* Get a quantity */ amt = get_quantity(NULL, o_ptr->number); /* Allow user abort */ if (amt <= 0) return; } } /* Get desired object */ j_ptr = object_dup(o_ptr); /* * If a rod or wand, allocate total maximum timeouts or charges * between those purchased and left on the shelf. */ reduce_charges(j_ptr, j_ptr->number - amt); /* Modify quantity */ j_ptr->number = amt; /* Hack -- require room in pack */ if (!inven_carry_okay(j_ptr)) { msgf("You cannot carry that many items."); return; } /* Attempt to buy it */ if (!(st_ptr->type == BUILD_STORE_HOME)) { /* Get price */ price = price_item(j_ptr, FALSE) * amt; /* Player can afford it */ if (p_ptr->au < price) { /* Simple message (no insult) */ msgf("You do not have enough gold."); } /* Player wants it? */ else if (store_access_item(j_ptr, price, TRUE)) { /* Say "okay" */ say_comment_1(); /* Make a sound */ sound(SOUND_BUY); /* Spend the money */ p_ptr->au -= price; /* Update the display */ store_prt_gold(); /* Hack -- buying an item makes you aware of it */ object_aware(j_ptr); object_mental(j_ptr); j_ptr->info &= ~(OB_STOREB); /* Describe the transaction */ msgf("You bought %v for %ld gold.", OBJECT_FMT(j_ptr, TRUE, 3), (long)price); /* Now, reduce the original stack's pval. */ if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_WAND)) { o_ptr->pval -= j_ptr->pval; /* No used charges in store stock */ o_ptr->ac = 0; } /* Erase the inscription */ quark_remove(&j_ptr->inscription); /* Erase the "feeling" */ j_ptr->feeling = FEEL_NONE; /* Give it to the player */ j_ptr = inven_carry(j_ptr); /* Paranoia */ if (!j_ptr) { msgf("Too many allocated objects!"); return; } /* Get slot */ item_new = get_item_position(p_ptr->inventory, j_ptr); /* Describe the final result */ msgf("You have %v (%c).", OBJECT_FMT(j_ptr, TRUE, 3), I2A(item_new)); /* Handle stuff */ handle_stuff(); /* Note how many slots the store used to have */ i = get_list_length(st_ptr->stock); /* Remove the bought items from the store */ item_increase_silent(o_ptr, -amt); /* Store is empty */ if (!st_ptr->stock) { /* Shuffle */ if (one_in_(STORE_SHUFFLE)) { /* Message */ msgf("The shopkeeper retires."); /* Shuffle the store */ store_shuffle(st_ptr); } /* Maintain */ else { /* Message */ msgf("The shopkeeper brings out some new stock."); } /* New inventory */ for (i = 0; i < 10; i++) { /* Maintain the store */ store_maint(); } /* Start over */ p_ptr->state.store_top = 0; } /* The item is gone */ else if (get_list_length(st_ptr->stock) != i) { /* Pick the correct screen */ if (p_ptr->state.store_top >= get_list_length(st_ptr->stock)) { p_ptr->state.store_top -= 12; } } /* Redraw everything */ display_inventory(); } } /* Home is much easier */ else { /* Distribute charges of wands/rods */ distribute_charges(o_ptr, j_ptr, amt); /* Give it to the player */ j_ptr = inven_carry(j_ptr); /* Paranoia */ if (!j_ptr) { msgf("Too many allocated objects!"); return; } /* Get slot */ item_new = get_item_position(p_ptr->inventory, j_ptr); /* Describe just the result */ msgf("You have %v (%c).", OBJECT_FMT(j_ptr, TRUE, 3), I2A(item_new)); /* Handle stuff */ handle_stuff(); /* Take note if we take the last one */ i = get_list_length(st_ptr->stock); /* Remove the items from the home */ item_increase(o_ptr, -amt); /* Hack -- Item is still here */ if (i == get_list_length(st_ptr->stock)) { /* Redraw the item */ display_entry(item); } /* The item is gone */ else { /* Nothing left */ if (!st_ptr->stock) p_ptr->state.store_top = 0; /* Nothing left on that screen */ else if (p_ptr->state.store_top >= get_list_length(st_ptr->stock)) { p_ptr->state.store_top -= 12; } /* Redraw everything */ display_inventory(); chg_virtue(V_SACRIFICE, 1); } } } /* * Sell an item to the store (or home) */ static void store_sell(void) { int item_pos; int amt; s32b price, value, dummy; object_type *q_ptr; object_type *o_ptr; cptr q, s; s16b *list; /* Get an item */ s = "You have nothing that I want."; /* Prepare a prompt */ if (st_ptr->type == BUILD_STORE_HOME) { q = "Drop which item? "; /* Home takes anything */ item_tester_hook = NULL; /* Get an item */ o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN)); } else { q = "Sell which item? "; /* Only allow items the store will buy */ item_tester_hook = store_will_stock; /* Get an item */ o_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_STORE)); } /* Not a valid item */ if (!o_ptr) return; /* Hack -- Cannot remove cursed items */ if ((!o_ptr->allocated) && cursed_p(o_ptr)) { /* Oops */ msgf("Hmmm, it seems to be cursed."); /* Set the knowledge flag for the player */ o_ptr->kn_flags[2] |= TR2_CURSED; /* Nope */ return; } /* Assume one item */ amt = 1; /* Find out how many the player wants to sell */ if (o_ptr->number > 1) { /* Describe the object (only what we know) */ put_fstr(0, 1, "%s %v", "Selling", OBJECT_FMT(o_ptr, TRUE, 3)); /* Get a quantity */ amt = get_quantity(NULL, o_ptr->number); /* Allow user abort */ if (amt <= 0) return; } /* Duplicate the object */ q_ptr = object_dup(o_ptr); /* Modify quantity */ q_ptr->number = amt; /* * Hack -- If a rod or wand, allocate total maximum * timeouts or charges to those being sold. -LM- */ if (o_ptr->tval == TV_WAND) { q_ptr->pval = (o_ptr->pval + o_ptr->ac) * amt / o_ptr->number; /* Remove "used" charges */ if (q_ptr->pval < o_ptr->ac) { q_ptr->pval = 0; } else { q_ptr->pval -= o_ptr->ac; } } if (o_ptr->tval == TV_ROD) { q_ptr->pval = o_ptr->pval * amt / o_ptr->number; } /* Remove any inscription, feeling for stores */ if (!(st_ptr->type == BUILD_STORE_HOME)) { quark_remove(&q_ptr->inscription); q_ptr->feeling = FEEL_NONE; } /* Is there room in the store (or the home?) */ if (!store_check_num(q_ptr)) { if (st_ptr->type == BUILD_STORE_HOME) msgf("Your home is full."); else msgf("I have not the room in my store to keep it."); return; } /* Get list to ensure that the object is in the inv */ list = look_up_list(o_ptr); /* Take off equipment */ if (!list) { /* Take off first */ o_ptr = inven_takeoff(o_ptr); /* Paranoia */ if (!o_ptr) return; } /* Real store */ if (!(st_ptr->type == BUILD_STORE_HOME)) { /* Get price */ price = price_item(q_ptr, TRUE) * amt; /* Sold... */ if (store_access_item(q_ptr, price, FALSE)) { /* Say "okay" */ say_comment_1(); /* Make a sound */ sound(SOUND_SELL); /* Get some money */ p_ptr->au += price; /* Update the display */ store_prt_gold(); /* Get the "apparent" value */ dummy = object_value(q_ptr) * q_ptr->number; /* Duplicate the object */ q_ptr = object_dup(o_ptr); /* Identify sold item */ identify_item(q_ptr); /* Don't want to let out how many charges on wands */ if (o_ptr->tval != TV_WAND) { /* Identify pack item */ identify_item(o_ptr); } /* Modify quantity */ q_ptr->number = amt; /* * Hack -- Allocate charges between those wands or rods sold * and retained, unless all are being sold. -LM- */ distribute_charges(o_ptr, q_ptr, amt); /* Get the "actual" value */ value = object_value(q_ptr) * q_ptr->number; /* Describe the result (in message buffer) */ msgf("You sold %v for %ld gold.", OBJECT_FMT(q_ptr, TRUE, 3), (long)price); if (!((q_ptr->tval == TV_FIGURINE) && (value > 0))) { /* * Analyze the prices (and comment verbally) * unless object is a figurine */ purchase_analyze(price, value, dummy); } if (q_ptr->tval != TV_LITE) { /* Reset timeouts of the sold items */ q_ptr->timeout = 0; } if (q_ptr->tval == TV_WAND) { /* Reset the "used" charges. */ q_ptr->ac = 0; } /* Take the item from the player, describe the result */ item_increase(o_ptr, -amt); /* Handle stuff */ handle_stuff(); /* The store gets that (known) item */ q_ptr = store_carry(q_ptr); /* Get position */ item_pos = get_item_position(st_ptr->stock, q_ptr); /* Re-display if item is now in store */ if (item_pos >= 0) { p_ptr->state.store_top = (item_pos / 12) * 12; display_inventory(); } } } /* Player is at home */ else { /* Distribute charges of wands/rods */ distribute_charges(o_ptr, q_ptr, amt); /* Describe */ msgf("You drop %v.", OBJECT_FMT(q_ptr, TRUE, 3)); /* Take it from the players inventory */ item_increase(o_ptr, -amt); /* Handle stuff */ handle_stuff(); /* Let the home carry it */ q_ptr = home_carry(q_ptr); /* Get position */ item_pos = get_item_position(st_ptr->stock, q_ptr); /* Update store display */ if (item_pos >= 0) { p_ptr->state.store_top = (item_pos / 12) * 12; display_inventory(); } } } /* * Examine an item in a store -JDL- */ static void store_examine(void) { int i; int item; object_type *o_ptr; char out_val[160]; /* Empty? */ if (!st_ptr->stock) { if (st_ptr->type == BUILD_STORE_HOME) msgf("Your home is empty."); else msgf("I am currently out of stock."); return; } /* Find the number of objects on this and following pages */ i = (get_list_length(st_ptr->stock) - p_ptr->state.store_top); /* And then restrict it to the current page */ if (i > 12) i = 12; /* Prompt */ strnfmt(out_val, 160, "Which item do you want to examine? "); /* Get the item number to be examined */ if (!get_stock(&item, out_val, i)) return; /* Get the actual index */ item = item + p_ptr->state.store_top; /* Get the actual item */ o_ptr = get_list_item(st_ptr->stock, item); /* Describe it fully */ identify_fully_aux(o_ptr); return; } /* * Hack -- set this to leave the store */ static bool leave_store = FALSE; /* These commands are available inside stores and buildings both. */ bool do_standard_command(s16b c) { /* Is this the right sort of command? */ switch (c) { /*** Inventory Commands ***/ case 'w': { /* Wear/wield equipment */ do_cmd_wield(); return (TRUE); } case 't': { /* Take off equipment */ do_cmd_takeoff(); return (TRUE); } case 'k': { /* Destroy an item */ do_cmd_destroy(); return (TRUE); } case 'e': { /* Equipment list */ do_cmd_equip(); return (TRUE); } case 'i': { /* Inventory list */ do_cmd_inven(); return (TRUE); } /*** Various commands ***/ case 'M': { /* Full dungeon map */ do_cmd_view_map(); return (TRUE); } case 'I': { /* Identify an object */ do_cmd_observe(); return (TRUE); } case KTRL('I'): { /* Hack -- toggle windows */ toggle_inven_equip(); return (TRUE); } /*** Use various objects ***/ case 'b': { /* Browse a book */ do_cmd_browse(); return (TRUE); } case '{': { /* Inscribe an object */ do_cmd_inscribe(); return (TRUE); } case '}': { /* Uninscribe an object */ do_cmd_uninscribe(); return (TRUE); } /*** Help and Such ***/ case '?': { /* Help */ do_cmd_help(); return (TRUE); } case '/': { /* Identify symbol */ do_cmd_query_symbol(); return (TRUE); } case 'C': { /* Character description */ do_cmd_character(); return (TRUE); } /*** System Commands ***/ case '!': { /* Hack -- User interface */ (void)Term_user(0); return (TRUE); } case '"': { /* Single line from a pref file */ do_cmd_pref(); return (TRUE); } case '@': { /* Interact with macros */ do_cmd_macros(); return (TRUE); } case '%': { /* Interact with visuals */ do_cmd_visuals(); return (TRUE); } case '&': { /* Interact with colors */ do_cmd_colors(); return (TRUE); } case '=': { /* Interact with options */ do_cmd_options(OPT_FLAG_SERVER | OPT_FLAG_PLAYER); return (TRUE); } /*** Misc Commands ***/ case ':': { /* Take notes */ do_cmd_note(); return (TRUE); } case 'V': { /* Version info */ do_cmd_version(); return (TRUE); } case KTRL('F'): { /* Repeat level feeling */ do_cmd_feeling(); return (TRUE); } case KTRL('P'): { /* Show previous messages */ do_cmd_messages(); return (TRUE); } case KTRL('Q'): { /* Show quest status -KMW- */ do_cmd_checkquest(); return (TRUE); } case KTRL('T'): { /* Get the time of day */ do_cmd_time(); return (TRUE); } case '~': case '|': { /* Check artifacts, uniques, quests etc. */ do_cmd_knowledge(); return (TRUE); } case '(': { /* Load "screen dump" */ do_cmd_load_screen(); return (TRUE); } case ')': { /* Save "screen dump" */ do_cmd_save_screen(); return (TRUE); } } /* So the commands didn't match */ return (FALSE); } /* * Process a command in a store * * Note that we must allow the use of a few "special" commands * in the stores which are not allowed in the dungeon, and we * must disable some commands which are allowed in the dungeon * but not in the stores, to prevent chaos. */ static void store_process_command(void) { int stocknum = get_list_length(st_ptr->stock); /* Handle repeating the last command */ repeat_check(); if (rogue_like_commands && p_ptr->cmd.cmd == 'l') { p_ptr->cmd.cmd = 'x'; /* hack! */ } /* Parse the command */ switch (p_ptr->cmd.cmd) { case '\r': { /* Ignore return */ break; } case ESCAPE: { /* Leave */ leave_store = TRUE; break; } case ' ': { /* Browse */ if (stocknum <= 12) { msgf("Entire inventory is shown."); } else { p_ptr->state.store_top += 12; if (p_ptr->state.store_top >= stocknum) p_ptr->state.store_top = 0; display_inventory(); } break; } case KTRL('R'): { /* Redraw */ do_cmd_redraw(); display_store(); break; } case 'g': { /* Get (purchase) */ store_purchase(); break; } case 'd': { /* Drop (Sell) */ store_sell(); break; } case 'x': { /* Examine */ store_examine(); break; } default: { /* Is it a standard command? */ if (!do_standard_command(p_ptr->cmd.cmd)) { /* Hack -- Unknown command */ msgf("That command does not work in stores."); break; } } } } /* * Deallocate stores stock. * * This routine is used to deallocate the first store in the * store stock cache. This is done to save memory. */ static void deallocate_store(void) { int i; store_type *home; /* Return if there are no stores with stock */ if (store_cache_num == 0) return; /* Do not deallocate homes or lockers */ while (store_cache[0]->type == BUILD_STORE_HOME) { /* Hack - move home to end of cache */ /* Keep track of stuff in home */ home = store_cache[0]; /* Resort the rest of the stores */ for (i = 1; i < store_cache_num; i++) { store_cache[i - 1] = store_cache[i]; } /* Move home to the end */ store_cache[store_cache_num - 1] = home; } /* Delete store least used. */ delete_object_list(&store_cache[0]->stock); /* Shift all other stores down the cache to fill the gap */ for (i = 1; i < store_cache_num; i++) { store_cache[i - 1] = store_cache[i]; } /* Decrease number of stores with stock */ store_cache_num--; } /* * Allocate memory for a stores stock. * * This routine is used to save memory. It is a waste to record * what is in every store in every town in the wilderness. This * allocates the required array if the stockpointer is NULL. */ bool allocate_store(store_type *st_ptr) { int i, n = -1; /* Find the location in the cache */ for (i = 0; i < store_cache_num; i++) { /* See if cache location matches */ if (st_ptr == store_cache[i]) { /* Resort order based on last_visit */ for (n = i + 1; n < store_cache_num; n++) { store_cache[n - 1] = store_cache[n]; } /* Move current one to end */ store_cache[store_cache_num - 1] = st_ptr; /* (No need to maintain store) */ return FALSE; } } /* Store does not have stock - so need to allocate. */ /* See if cache is full */ if (store_cache_num == STORE_CACHE_AMNT) { /* Delete least used store */ deallocate_store(); } /* Add store to end of cache */ store_cache[store_cache_num] = st_ptr; /* The number in the cache has increased */ store_cache_num++; /* (Need to maintain stores) */ return TRUE; } store_type *get_current_store(void) { place_type *pl_ptr = &place[p_ptr->place_num]; int i, which = -1; /* Get the building the player is on */ for (i = 0; i < pl_ptr->numstores; i++) { if ((p_ptr->py - pl_ptr->y * 16 == pl_ptr->store[i].y) && (p_ptr->px - pl_ptr->x * 16 == pl_ptr->store[i].x)) { which = i; } } /* Paranoia */ if (which == -1) { msgf("Could not locate building!"); return (NULL); } /* Return a pointer to the store */ return (&pl_ptr->store[which]); } /* * Enter a store, and interact with it. * * Note that we use the standard "request_command()" function * to get a command, allowing us to use "cmd.arg" and all * command macros and other nifty stuff, but we use the special * "shopping" argument, to force certain commands to be converted * into other commands, normally, we convert "p" (pray) and "m" * (cast magic) into "g" (get), and "s" (search) into "d" (drop). */ void do_cmd_store(const field_type *f1_ptr) { int maintain_num; int tmp_chr; int i; object_type *o_ptr; /* Disturb */ disturb(FALSE); /* Hack - save f1_ptr for later */ f_ptr = f1_ptr; /* Save the store pointer */ st_ptr = get_current_store(); /* Paranoia */ if (!st_ptr) return; /* Init store if required */ field_script_const(f1_ptr, FIELD_ACT_SB_INIT, ""); /* Some quests are finished by finding a shop */ trigger_quest_complete(QX_FIND_SHOP, (vptr)st_ptr); /* Hack - save interesting flags for later */ info_flags = f_ptr->data[7]; /* Hack -- Check the "locked doors" */ if (ironman_shops) { msgf("The doors are locked."); return; } /* Calculate the number of store maintainances since the last visit */ maintain_num = (turn - st_ptr->last_visit) / (10L * STORE_TURNS); /* Recalculate maximum number of items in store */ if (f_ptr->data[7] & ST_HALF_INVEN) { st_ptr->max_stock = STORE_INVEN_MAX / 2; } else { st_ptr->max_stock = STORE_INVEN_MAX; } /* Allocate object storage if required */ if (allocate_store(st_ptr)) { /* Hack - Maintain store if it is just allocated. */ maintain_num++; } /* Maintain the store max. 20 times */ if (maintain_num > 20) maintain_num = 20; if (maintain_num) { /* Maintain the store */ for (i = 0; i < maintain_num; i++) { store_maint(); } /* Save the visit */ st_ptr->last_visit = turn; } /* Forget the view */ forget_view(); /* Hack -- Character is in "icky" mode */ screen_save(); /* No command argument */ p_ptr->cmd.arg = 0; /* No repeated command */ p_ptr->cmd.rep = 0; /* No automatic command */ p_ptr->cmd.new = 0; /* Start at the beginning */ p_ptr->state.store_top = 0; /* Display the store */ display_store(); /* Hack - change the redraw hook so bigscreen works */ angband_term[0]->resize_hook = display_store; /* Do not leave */ leave_store = FALSE; /* Interact with player */ while (!leave_store) { /* Hack -- Clear lines 1 and 2 */ clear_region(0, 1, 2); /* Hack -- Check the charisma */ tmp_chr = p_ptr->stat[A_CHR].use; /* Clear */ clear_from(21); /* Update store inventory information */ if (st_ptr->type == BUILD_STORE_HOME) { Term_write_list(st_ptr->stock, LIST_HOME); } else { Term_write_list(st_ptr->stock, LIST_STORE); } /* What can we sell? */ if (st_ptr->type == BUILD_STORE_HOME) { /* Home takes anything */ item_tester_hook = NULL; } else { /* Only allow items the store will buy */ item_tester_hook = store_will_stock; } /* Update player inventory information */ OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Not right type of item? */ if (item_tester_hook && !item_tester_hook(o_ptr)) { /* Hack - cannot sell item */ o_ptr->temp_cost = 0; continue; } /* Not enough room? */ if (!store_check_num(o_ptr)) { /* Hack - cannot sell item */ o_ptr->temp_cost = 0; continue; } if (st_ptr->type == BUILD_STORE_HOME) { /* * Hack - you can 'sell' anything to your home * if there is room */ o_ptr->temp_cost = 1; } else { /* * Hack - Otherwise, get store price * for one item into o_ptr->temp_cost. * (This is set inside price_item().) */ (void)price_item(o_ptr, TRUE); } } OBJ_ITT_END; /* Send information */ Term_write_list(p_ptr->inventory, LIST_INVEN); /* Reset tester hook */ item_tester_hook = NULL; /* Basic commands */ prtf(0, 22, " ESC) Exit from Building."); /* Browse if necessary */ if (get_list_length(st_ptr->stock) > 12) { prtf(0, 23, " SPACE) Next page of stock"); } /* Home commands */ if (st_ptr->type == BUILD_STORE_HOME) { prtf(31, 22, " g) Get an item."); prtf(31, 23, " d) Drop an item."); } /* Shop commands XXX XXX XXX */ else { prtf(31, 22, " p) Purchase an item."); prtf(31, 23, " s) Sell an item."); } /* Add in the eXamine option */ prtf(56, 22, " x) eXamine an item."); /* Prompt */ prtf(0, 21, "You may: "); /* Get a command */ request_command(TRUE); /* Process the command */ store_process_command(); /* Notice stuff */ notice_stuff(); /* Handle stuff */ handle_stuff(); /* XXX XXX XXX Pack Overflow */ if (get_list_length(p_ptr->inventory) > INVEN_PACK) { /* Message */ msgf("Your pack is so full that you flee outside..."); /* Leave */ leave_store = TRUE; } /* Hack -- Redisplay store prices if charisma changes */ if (tmp_chr != p_ptr->stat[A_CHR].use) { display_inventory(); } } /* Free turn XXX XXX XXX */ p_ptr->state.energy_use = 0; /* Hack -- Character is no longer in "icky" mode */ screen_load(); /* Hack -- Cancel automatic command */ p_ptr->cmd.new = 0; /* Flush messages XXX XXX XXX */ message_flush(); /* Hack - reset the redraw hook */ angband_term[0]->resize_hook = resize_map; /* Clear the screen */ Term_clear(); /* Update for the changed screen size */ resize_map(); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); } /* * Initialize a store */ void store_init(int town_num, int store_num, byte store) { cptr own_name = owner_names[randint0(owner_names_max)]; cptr own_suffix = owner_suffix[randint0(owner_suffix_max)]; /* Activate that building */ store_type *st_ptr = &place[town_num].store[store_num]; /* Pick an owner */ st_ptr->owner_name = quark_fmt("%s %s", own_name, own_suffix); /* These are set in place_sb() via lua hooks */ st_ptr->greed = 0; st_ptr->max_cost = 0; /* Do not allocate the stock yet. */ st_ptr->stock = 0; /* Set the store type */ st_ptr->type = store; /* Initialize the store */ st_ptr->data = 0; /* * Hack - maximum items in stock * (This number may be changed when the store * is actually created.) * The reason we do it later, is because we do not * have the field data at this stage. This line * perhaps can be removed, but beware of strange bugs * popping up in other code. */ st_ptr->max_stock = STORE_INVEN_MAX; /* * MEGA-HACK - Last visit to store is * BEFORE player birth to enable store restocking */ st_ptr->last_visit = -100L * STORE_TURNS; } void place_sb(int greed, int max_cost) { store_type *st_ptr = get_current_store(); /* Set greed / max_cost values if unset */ if (!st_ptr->greed) { st_ptr->greed = greed; st_ptr->max_cost = max_cost; } } zangband/src/streams.c0000644000000000000000000002261410250356275013763 0ustar rootroot/* * File: streams.c * Purpose: Used by dungeon generation. This file holds all the * functions that are applied to a level after the rest has been * generated, ie streams and level destruction. */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "generate.h" #include "streams.h" #include "grid.h" /* * Recursive fractal algorithm to place water through the dungeon. */ static void recursive_river(int x1, int y1, int x2, int y2, int feat1, int feat2, int width) { int dx, dy, length, l, x, y; int changex, changey; int ty, tx; bool done; cave_type *c_ptr; length = distance(x1, y1, x2, y2); if (length > 4) { /* * Divide path in half and call routine twice. * There is a small chance of splitting the river */ dx = (x2 - x1) / 2; dy = (y2 - y1) / 2; if (dy != 0) { /* perturbation perpendicular to path */ changex = randint1(ABS(dy)) * 2 - ABS(dy); } else { changex = 0; } if (dx != 0) { /* perturbation perpendicular to path */ changey = randint1(ABS(dx)) * 2 - ABS(dx); } else { changey = 0; } if (!in_bounds(x1 + dx + changex, y1 + dy + changey)) { changex = 0; changey = 0; } /* construct river out of two smaller ones */ recursive_river(x1, y1, x1 + dx + changex, y1 + dy + changey, feat1, feat2, width); recursive_river(x1 + dx + changex, y1 + dy + changey, x2, y2, feat1, feat2, width); /* Split the river some of the time - junctions look cool */ if (one_in_(DUN_WAT_CHG) && (width > 0)) { recursive_river(x1 + dx + changex, y1 + dy + changey, x1 + 8 * (dx + changex), y1 + 8 * (dy + changey), feat1, feat2, width - 1); } } else { /* Actually build the river */ for (l = 0; l < length; l++) { x = x1 + l * (x2 - x1) / length; y = y1 + l * (y2 - y1) / length; done = FALSE; while (!done) { for (ty = y - width - 1; ty <= y + width + 1; ty++) { for (tx = x - width - 1; tx <= x + width + 1; tx++) { if (!in_bounds(tx, ty)) continue; c_ptr = cave_p(tx, ty); if (c_ptr->feat == feat1) continue; if (c_ptr->feat == feat2) continue; if (distance(tx, ty, x, y) > rand_spread(width, 1)) continue; /* Do not convert permanent features */ if (cave_perma_grid(c_ptr)) continue; /* Making a door on top of fields is problematical */ delete_field_location(c_ptr); /* * Clear previous contents, add feature * The border mainly gets feat2, while the center gets feat1 */ if (distance(tx, ty, x, y) > width) set_feat_grid(c_ptr, feat2); else set_feat_grid(c_ptr, feat1); /* Lava terrain glows */ if ((feat1 == FEAT_DEEP_LAVA) || (feat1 == FEAT_SHAL_LAVA)) { c_ptr->info |= CAVE_GLOW; } /* Hack -- don't teleport here */ c_ptr->info |= CAVE_ICKY; } } done = TRUE; } } } } /* * Places water /lava through dungeon. */ void add_river(int feat1, int feat2) { int y2, x2; int y1 = 0, x1 = 0; int wid; /* Hack -- Choose starting point */ y2 = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); x2 = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); /* Hack -- Choose ending point somewhere on boundary */ switch (randint1(4)) { case 1: { /* top boundary */ x1 = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); y1 = p_ptr->min_hgt + 1; break; } case 2: { /* left boundary */ x1 = p_ptr->min_wid + 1; y1 = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); break; } case 3: { /* right boundary */ x1 = p_ptr->max_wid - 2; y1 = rand_range(p_ptr->min_hgt + 1, p_ptr->max_hgt - 2); break; } case 4: { /* bottom boundary */ x1 = rand_range(p_ptr->min_wid + 1, p_ptr->max_wid - 2); y1 = p_ptr->max_hgt - 2; break; } } wid = randint1(DUN_WAT_RNG); recursive_river(x1, y1, x2, y2, feat1, feat2, wid); /* Hack - Save the location as a "room" */ if (dun->cent_n < CENT_MAX) { dun->cent[dun->cent_n].y = y2; dun->cent[dun->cent_n].x = x2; dun->cent_n++; } } /* * Places "streamers" of rock through dungeon * * Note that their are actually six different terrain features used * to represent streamers. Three each of magma and quartz, one for * basic vein, one with hidden gold, and one with known gold. The * hidden gold types are currently unused. */ void build_streamer(int feat, int chance) { int i, tx, ty; int y, x, dir; int dummy = 0; cave_type *c_ptr; /* Hack -- Choose starting point */ y = rand_spread(p_ptr->max_hgt / 2, (p_ptr->max_hgt / 2 > 10 ? 10 : p_ptr->max_hgt / 2)); x = rand_spread(p_ptr->max_wid / 2, (p_ptr->max_wid / 2 > 15 ? 15 : p_ptr->max_wid / 2)); /* Choose a random compass direction */ dir = ddd[randint0(8)]; /* Place streamer into dungeon */ while (dummy < SAFE_MAX_ATTEMPTS) { dummy++; /* One grid per density */ for (i = 0; i < DUN_STR_DEN; i++) { int d = DUN_STR_RNG; /* Pick a nearby grid */ while (1) { ty = rand_spread(y, d); tx = rand_spread(x, d); if (!in_bounds2(tx, ty)) continue; break; } /* Access the grid */ c_ptr = cave_p(tx, ty); /* Only convert "granite" walls */ if (c_ptr->feat < FEAT_WALL_EXTRA) continue; if (c_ptr->feat > FEAT_WALL_SOLID) continue; /* Clear previous contents, add proper vein type */ set_feat_grid(c_ptr, feat); /* Hack XXX XXX -- Add some (known) treasure */ if (one_in_(chance)) c_ptr->feat += 0x04; /* * So this means that all the treasure is known as soon as it is * seen or detected... Why do the FEAT_MAGMA_H and FEAT_QUARTZ_H * terrain types exist? If they are never made, then the "mimic" * feature struct field can be removed, and so can some code in * map_info() - which will speed the game up significantly. */ } if (dummy >= SAFE_MAX_ATTEMPTS) { if (cheat_room) { msgf("Warning! Could not place streamer!"); } return; } /* Advance the streamer */ y += ddy[dir]; x += ddx[dir]; /* Quit before leaving the dungeon */ if (!in_bounds(x, y)) break; } } /* * Put trees near a hole in the dungeon roof (rubble on ground + up stairway) * This happens in real world lava tubes. */ void place_trees(int x, int y) { int i, j; cave_type *c_ptr; /* place trees/ rubble in ovalish distribution */ for (i = x - 3; i < x + 4; i++) { for (j = y - 3; j < y + 4; j++) { /* Paranoia */ if (!in_bounds(i, j)) continue; c_ptr = cave_p(i, j); /* Want square to be in the circle and accessable. */ if ((distance(i, j, x, y) < 4) && !cave_perma_grid(c_ptr)) { /* Adding to grids with fields is problematical */ delete_field_location(c_ptr); /* * Clear previous contents, add feature * The border mainly gets trees, while the center gets rubble */ if ((distance(i, j, x, y) > 1) || one_in_(4)) { if (randint1(100) < 75) set_feat_bold(i, j, FEAT_TREES); } else { set_feat_bold(i, j, FEAT_RUBBLE); } /* Light area since is open above */ cave_p(i, j)->info |= (CAVE_GLOW | CAVE_ROOM); } } } /* No up stairs in ironman mode */ if (!ironman_downward && one_in_(3)) { /* up stair */ set_feat_bold(x, y, FEAT_LESS); } /* Hack - Save the location as a "room" */ if (dun->cent_n < CENT_MAX) { dun->cent[dun->cent_n].y = y; dun->cent[dun->cent_n].x = x; dun->cent_n++; } } /* * Builds a cave system in the center of the dungeon. */ void build_cavern(void) { int grd, roug, cutoff, xsize, ysize, x0, y0; bool done; done = FALSE; /* Make a cave the size of the dungeon */ xsize = p_ptr->max_wid - 2; ysize = p_ptr->max_hgt - 2; x0 = xsize / 2; y0 = ysize / 2; /* Paranoia: make size even */ xsize = x0 * 2; ysize = y0 * 2; while (!done) { /* testing values for these parameters: feel free to adjust */ grd = rand_range(4, 8); /* want average of about 16 */ roug = randint1(8) * randint1(4); /* about size/2 */ cutoff = xsize / 2; /* make it */ generate_hmap(x0 + 1, y0 + 1, xsize, ysize, grd, roug, cutoff); /* Convert to normal format+ clean up */ done = generate_lake(x0 + 1, y0 + 1, xsize, ysize, cutoff, cutoff, cutoff, dun->feat_floor, dun->feat_floor, dun->feat_floor); } } /* * makes a lake/collapsed cave system in the center of the dungeon */ void build_lake(byte f1, byte f2, byte f3) { int grd, roug, xsize, ysize, x0, y0; bool done = FALSE; int c1, c2, c3; /* Make the size of the dungeon */ xsize = p_ptr->max_wid - 2; ysize = p_ptr->max_hgt - 2; x0 = xsize / 2; y0 = ysize / 2; /* Paranoia: make size even */ xsize = x0 * 2; ysize = y0 * 2; while (!done) { /* testing values for these parameters: feel free to adjust */ grd = rand_range(3, 7); /* want average of about 16 */ roug = randint1(8) * randint1(4); /* Make up size of various componants */ /* Floor */ c3 = 3 * xsize / 4; /* Deep water/lava */ c1 = randint0(c3 / 2) + randint0(c3 / 2) - 5; /* Shallow boundary */ c2 = (c1 + c3) / 2; /* make it */ generate_hmap(x0 + 1, y0 + 1, xsize, ysize, grd, roug, c3); /* Convert to normal format+ clean up */ done = generate_lake(x0 + 1, y0 + 1, xsize, ysize, c1, c2, c3, f1, f2, f3); } } zangband/src/tables.c0000755000000000000000000044753010250356275013572 0ustar rootroot /* File: tables.c */ /* Purpose: Angband Tables */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Global array for looping through the "keypad directions" */ const s16b ddd[9] = { 2, 8, 6, 4, 3, 1, 9, 7, 5 }; /* * Global arrays for converting "keypad direction" into offsets */ const s16b ddx[10] = { 0, -1, 0, 1, -1, 0, 1, -1, 0, 1 }; const s16b ddy[10] = { 0, 1, 1, 1, 0, 0, 0, -1, -1, -1 }; /* * Global arrays for optimizing "ddx[ddd[i]]" and "ddy[ddd[i]]" */ const s16b ddx_ddd[9] = { 0, 0, 1, -1, 1, -1, 1, -1, 0 }; const s16b ddy_ddd[9] = { 1, -1, 0, 0, 1, 1, -1, -1, 0 }; /* * Circular keypad direction array */ const s16b cdd[8] = { 2, 3, 6, 9, 8, 7, 4, 1 }; /* * Global arrays for optimizing "ddx[cdd[i]]" and "ddy[cdd[i]]" */ const s16b ddx_cdd[8] = { 0, 1, 1, 1, 0, -1, -1, -1 }; const s16b ddy_cdd[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; /* * Global array for converting numbers to uppercase hecidecimal digit * This array can also be used to convert a number to an octal digit */ const char hexsym[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /* * Encode the screen colors */ cptr color_char = "dwsorgbuDWvyRGBU"; /* * Stat Table (INT/WIS) -- Number of spells at level 50 */ const byte adj_mag_study[] = { 0 /* 3 */, 5 /* 4 */, 10 /* 5 */, 15 /* 6 */, 20 /* 7 */, 25 /* 8 */, 30 /* 9 */, 35 /* 10 */, 40 /* 11 */, 45 /* 12 */, 50 /* 13 */, 52 /* 14 */, 54 /* 15 */, 56 /* 16 */, 58 /* 17 */, 60 /* 18/00-18/09 */, 62 /* 18/10-18/19 */, 64 /* 18/20-18/29 */, 66 /* 18/30-18/39 */, 68 /* 18/40-18/49 */, 70 /* 18/50-18/59 */, 75 /* 18/60-18/69 */, 80 /* 18/70-18/79 */, 85 /* 18/80-18/89 */, 90 /* 18/90-18/99 */, 95 /* 18/100-18/109 */, 100 /* 18/110-18/119 */, 105 /* 18/120-18/129 */, 110 /* 18/130-18/139 */, 115 /* 18/140-18/149 */, 120 /* 18/150-18/159 */, 125 /* 18/160-18/169 */, 130 /* 18/170-18/179 */, 135 /* 18/180-18/189 */, 140 /* 18/190-18/199 */, 145 /* 18/200-18/209 */, 150 /* 18/210-18/219 */, 155 /* 18/220+ */ }; /* * Stat Table (INT/WIS) -- extra mana at level 50 divided by 2. */ const byte adj_mag_mana[] = { 0 /* 3 */, 0 /* 4 */, 2 /* 5 */, 5 /* 6 */, 7 /* 7 */, 10 /* 8 */, 12 /* 9 */, 15 /* 10 */, 17 /* 11 */, 20 /* 12 */, 22 /* 13 */, 25 /* 14 */, 27 /* 15 */, 30 /* 16 */, 32 /* 17 */, 35 /* 18/00-18/09 */, 39 /* 18/10-18/19 */, 43 /* 18/20-18/29 */, 47 /* 18/30-18/39 */, 52 /* 18/40-18/49 */, 57 /* 18/50-18/59 */, 62 /* 18/60-18/69 */, 67 /* 18/70-18/79 */, 72 /* 18/80-18/89 */, 78 /* 18/90-18/99 */, 84 /* 18/100-18/109 */, 90 /* 18/110-18/119 */, 96 /* 18/120-18/129 */, 102 /* 18/130-18/139 */, 109 /* 18/140-18/149 */, 116 /* 18/150-18/159 */, 123 /* 18/160-18/169 */, 130 /* 18/170-18/179 */, 137 /* 18/180-18/189 */, 145 /* 18/190-18/199 */, 153 /* 18/200-18/209 */, 161 /* 18/210-18/219 */, 170 /* 18/220+ */ }; /* * Stat Table (INT/WIS) -- Minimum failure rate (percentage) */ const byte adj_mag_fail[] = { 99 /* 3 */, 99 /* 4 */, 99 /* 5 */, 99 /* 6 */, 99 /* 7 */, 50 /* 8 */, 30 /* 9 */, 20 /* 10 */, 15 /* 11 */, 12 /* 12 */, 11 /* 13 */, 10 /* 14 */, 9 /* 15 */, 8 /* 16 */, 7 /* 17 */, 6 /* 18/00-18/09 */, 6 /* 18/10-18/19 */, 5 /* 18/20-18/29 */, 5 /* 18/30-18/39 */, 5 /* 18/40-18/49 */, 4 /* 18/50-18/59 */, 4 /* 18/60-18/69 */, 4 /* 18/70-18/79 */, 4 /* 18/80-18/89 */, 3 /* 18/90-18/99 */, 3 /* 18/100-18/109 */, 2 /* 18/110-18/119 */, 2 /* 18/120-18/129 */, 2 /* 18/130-18/139 */, 2 /* 18/140-18/149 */, 1 /* 18/150-18/159 */, 1 /* 18/160-18/169 */, 1 /* 18/170-18/179 */, 1 /* 18/180-18/189 */, 1 /* 18/190-18/199 */, 0 /* 18/200-18/209 */, 0 /* 18/210-18/219 */, 0 /* 18/220+ */ }; /* * Stat Table (INT/WIS) -- Spell failure rates */ const byte adj_mag_stat[] = { 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 1 /* 6 */, 1 /* 7 */, 2 /* 8 */, 2 /* 9 */, 3 /* 10 */, 3 /* 11 */, 4 /* 12 */, 4 /* 13 */, 5 /* 14 */, 5 /* 15 */, 6 /* 16 */, 6 /* 17 */, 7 /* 18/00-18/09 */, 7 /* 18/10-18/19 */, 8 /* 18/20-18/29 */, 9 /* 18/30-18/39 */, 10 /* 18/40-18/49 */, 11 /* 18/50-18/59 */, 12 /* 18/60-18/69 */, 15 /* 18/70-18/79 */, 18 /* 18/80-18/89 */, 21 /* 18/90-18/99 */, 24 /* 18/100-18/109 */, 27 /* 18/110-18/119 */, 30 /* 18/120-18/129 */, 33 /* 18/130-18/139 */, 36 /* 18/140-18/149 */, 39 /* 18/150-18/159 */, 42 /* 18/160-18/169 */, 45 /* 18/170-18/179 */, 48 /* 18/180-18/189 */, 51 /* 18/190-18/199 */, 54 /* 18/200-18/209 */, 57 /* 18/210-18/219 */, 60 /* 18/220+ */ }; /* * Stat Table (CHR) -- payment percentages */ const byte adj_chr_gold[] = { 140 /* 3 */, 135 /* 4 */, 132 /* 5 */, 130 /* 6 */, 128 /* 7 */, 126 /* 8 */, 124 /* 9 */, 122 /* 10 */, 120 /* 11 */, 118 /* 12 */, 116 /* 13 */, 114 /* 14 */, 113 /* 15 */, 112 /* 16 */, 111 /* 17 */, 110 /* 18/00-18/09 */, 109 /* 18/10-18/19 */, 108 /* 18/20-18/29 */, 107 /* 18/30-18/39 */, 106 /* 18/40-18/49 */, 105 /* 18/50-18/59 */, 104 /* 18/60-18/69 */, 103 /* 18/70-18/79 */, 102 /* 18/80-18/89 */, 101 /* 18/90-18/99 */, 100 /* 18/100-18/109 */, 99 /* 18/110-18/119 */, 98 /* 18/120-18/129 */, 97 /* 18/130-18/139 */, 96 /* 18/140-18/149 */, 95 /* 18/150-18/159 */, 94 /* 18/160-18/169 */, 93 /* 18/170-18/179 */, 92 /* 18/180-18/189 */, 91 /* 18/190-18/199 */, 90 /* 18/200-18/209 */, 89 /* 18/210-18/219 */, 88 /* 18/220+ */ }; /* * Stat Table (INT) -- Magic devices */ const byte adj_int_dev[] = { 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 0 /* 6 */, 0 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* 10 */, 1 /* 11 */, 1 /* 12 */, 1 /* 13 */, 1 /* 14 */, 2 /* 15 */, 2 /* 16 */, 2 /* 17 */, 3 /* 18/00-18/09 */, 3 /* 18/10-18/19 */, 4 /* 18/20-18/29 */, 4 /* 18/30-18/39 */, 5 /* 18/40-18/49 */, 5 /* 18/50-18/59 */, 6 /* 18/60-18/69 */, 6 /* 18/70-18/79 */, 7 /* 18/80-18/89 */, 7 /* 18/90-18/99 */, 8 /* 18/100-18/109 */, 9 /* 18/110-18/119 */, 10 /* 18/120-18/129 */, 11 /* 18/130-18/139 */, 12 /* 18/140-18/149 */, 13 /* 18/150-18/159 */, 14 /* 18/160-18/169 */, 15 /* 18/170-18/179 */, 16 /* 18/180-18/189 */, 17 /* 18/190-18/199 */, 18 /* 18/200-18/209 */, 19 /* 18/210-18/219 */, 20 /* 18/220+ */ }; /* * Stat Table (WIS) -- Saving throw */ const byte adj_wis_sav[] = { 128 + -20 /* 3 */, 128 + -17 /* 4 */, 128 + -14 /* 5 */, 128 + -11 /* 6 */, 128 + -8 /* 7 */, 128 + -5 /* 8 */, 128 + -2 /* 9 */, 128 + 0 /* 10 */, 128 + 2 /* 11 */, 128 + 5 /* 12 */, 128 + 8 /* 13 */, 128 + 10 /* 14 */, 128 + 13 /* 15 */, 128 + 15 /* 16 */, 128 + 18 /* 17 */, 128 + 20 /* 18/00-18/09 */, 128 + 22 /* 18/10-18/19 */, 128 + 25 /* 18/20-18/29 */, 128 + 27 /* 18/30-18/39 */, 128 + 29 /* 18/40-18/49 */, 128 + 31 /* 18/50-18/59 */, 128 + 33 /* 18/60-18/69 */, 128 + 35 /* 18/70-18/79 */, 128 + 37 /* 18/80-18/89 */, 128 + 39 /* 18/90-18/99 */, 128 + 41 /* 18/100-18/109 */, 128 + 43 /* 18/110-18/119 */, 128 + 45 /* 18/120-18/129 */, 128 + 46 /* 18/130-18/139 */, 128 + 48 /* 18/140-18/149 */, 128 + 50 /* 18/150-18/159 */, 128 + 51 /* 18/160-18/169 */, 128 + 53 /* 18/170-18/179 */, 128 + 54 /* 18/180-18/189 */, 128 + 56 /* 18/190-18/199 */, 128 + 57 /* 18/200-18/209 */, 128 + 58 /* 18/210-18/219 */, 128 + 60 /* 18/220+ */ }; /* * Stat Table (DEX) -- disarming */ const byte adj_dex_dis[] = { 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 0 /* 6 */, 0 /* 7 */, 0 /* 8 */, 0 /* 9 */, 0 /* 10 */, 0 /* 11 */, 0 /* 12 */, 1 /* 13 */, 1 /* 14 */, 1 /* 15 */, 2 /* 16 */, 2 /* 17 */, 4 /* 18/00-18/09 */, 4 /* 18/10-18/19 */, 4 /* 18/20-18/29 */, 4 /* 18/30-18/39 */, 5 /* 18/40-18/49 */, 5 /* 18/50-18/59 */, 5 /* 18/60-18/69 */, 6 /* 18/70-18/79 */, 6 /* 18/80-18/89 */, 7 /* 18/90-18/99 */, 8 /* 18/100-18/109 */, 8 /* 18/110-18/119 */, 8 /* 18/120-18/129 */, 8 /* 18/130-18/139 */, 8 /* 18/140-18/149 */, 9 /* 18/150-18/159 */, 9 /* 18/160-18/169 */, 9 /* 18/170-18/179 */, 9 /* 18/180-18/189 */, 9 /* 18/190-18/199 */, 10 /* 18/200-18/209 */, 10 /* 18/210-18/219 */, 10 /* 18/220+ */ }; /* * Stat Table (INT) -- disarming */ const byte adj_int_dis[] = { 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 0 /* 6 */, 0 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* 10 */, 1 /* 11 */, 1 /* 12 */, 1 /* 13 */, 1 /* 14 */, 2 /* 15 */, 2 /* 16 */, 2 /* 17 */, 3 /* 18/00-18/09 */, 3 /* 18/10-18/19 */, 3 /* 18/20-18/29 */, 4 /* 18/30-18/39 */, 4 /* 18/40-18/49 */, 5 /* 18/50-18/59 */, 6 /* 18/60-18/69 */, 7 /* 18/70-18/79 */, 8 /* 18/80-18/89 */, 9 /* 18/90-18/99 */, 10 /* 18/100-18/109 */, 10 /* 18/110-18/119 */, 11 /* 18/120-18/129 */, 12 /* 18/130-18/139 */, 13 /* 18/140-18/149 */, 14 /* 18/150-18/159 */, 15 /* 18/160-18/169 */, 16 /* 18/170-18/179 */, 17 /* 18/180-18/189 */, 18 /* 18/190-18/199 */, 19 /* 18/200-18/209 */, 19 /* 18/210-18/219 */, 20 /* 18/220+ */ }; /* * Stat Table (DEX) -- bonus to ac (plus 128) */ const byte adj_dex_ta[] = { 128 + -4 /* 3 */, 128 + -3 /* 4 */, 128 + -2 /* 5 */, 128 + -1 /* 6 */, 128 + 0 /* 7 */, 128 + 0 /* 8 */, 128 + 0 /* 9 */, 128 + 0 /* 10 */, 128 + 0 /* 11 */, 128 + 0 /* 12 */, 128 + 0 /* 13 */, 128 + 0 /* 14 */, 128 + 1 /* 15 */, 128 + 1 /* 16 */, 128 + 1 /* 17 */, 128 + 2 /* 18/00-18/09 */, 128 + 2 /* 18/10-18/19 */, 128 + 2 /* 18/20-18/29 */, 128 + 2 /* 18/30-18/39 */, 128 + 2 /* 18/40-18/49 */, 128 + 3 /* 18/50-18/59 */, 128 + 3 /* 18/60-18/69 */, 128 + 3 /* 18/70-18/79 */, 128 + 4 /* 18/80-18/89 */, 128 + 5 /* 18/90-18/99 */, 128 + 6 /* 18/100-18/109 */, 128 + 7 /* 18/110-18/119 */, 128 + 8 /* 18/120-18/129 */, 128 + 9 /* 18/130-18/139 */, 128 + 9 /* 18/140-18/149 */, 128 + 10 /* 18/150-18/159 */, 128 + 11 /* 18/160-18/169 */, 128 + 12 /* 18/170-18/179 */, 128 + 13 /* 18/180-18/189 */, 128 + 14 /* 18/190-18/199 */, 128 + 15 /* 18/200-18/209 */, 128 + 15 /* 18/210-18/219 */, 128 + 16 /* 18/220+ */ }; /* * Stat Table (STR) -- bonus to Deadliness (plus 128). To compensate * for changes elsewhere, STR now has a larger effect. -LM- */ const byte adj_str_td[] = { 128 + -2 /* 3 */, 128 + -2 /* 4 */, 128 + -1 /* 5 */, 128 + -1 /* 6 */, 128 + 0 /* 7 */, 128 + 0 /* 8 */, 128 + 0 /* 9 */, 128 + 0 /* 10 */, 128 + 0 /* 11 */, 128 + 0 /* 12 */, 128 + 0 /* 13 */, 128 + 0 /* 14 */, 128 + 0 /* 15 */, 128 + 1 /* 16 */, 128 + 2 /* 17 */, 128 + 3 /* 18/00-18/09 */, 128 + 4 /* 18/10-18/19 */, 128 + 4 /* 18/20-18/29 */, 128 + 5 /* 18/30-18/39 */, 128 + 6 /* 18/40-18/49 */, 128 + 7 /* 18/50-18/59 */, 128 + 8 /* 18/60-18/69 */, 128 + 9 /* 18/70-18/79 */, 128 + 10 /* 18/80-18/89 */, 128 + 11 /* 18/90-18/99 */, 128 + 12 /* 18/100-18/109 */, 128 + 13 /* 18/110-18/119 */, 128 + 14 /* 18/120-18/129 */, 128 + 15 /* 18/130-18/139 */, 128 + 16 /* 18/140-18/149 */, 128 + 17 /* 18/150-18/159 */, 128 + 18 /* 18/160-18/169 */, 128 + 19 /* 18/170-18/179 */, 128 + 20 /* 18/180-18/189 */, 128 + 21 /* 18/190-18/199 */, 128 + 22 /* 18/200-18/209 */, 128 + 23 /* 18/210-18/219 */, 128 + 25 /* 18/220+ */ }; /* * Stat Table (DEX) -- bonus to Skill (plus 128. To compensate for * changes elsewhere, DEX now has a larger effect. -LM- */ const byte adj_dex_th[] = { 128 + -4 /* 3 */, 128 + -3 /* 4 */, 128 + -2 /* 5 */, 128 + -1 /* 6 */, 128 + -1 /* 7 */, 128 + 0 /* 8 */, 128 + 0 /* 9 */, 128 + 0 /* 10 */, 128 + 0 /* 11 */, 128 + 0 /* 12 */, 128 + 0 /* 13 */, 128 + 0 /* 14 */, 128 + 1 /* 15 */, 128 + 2 /* 16 */, 128 + 3 /* 17 */, 128 + 3 /* 18/00-18/09 */, 128 + 3 /* 18/10-18/19 */, 128 + 4 /* 18/20-18/29 */, 128 + 4 /* 18/30-18/39 */, 128 + 4 /* 18/40-18/49 */, 128 + 5 /* 18/50-18/59 */, 128 + 5 /* 18/60-18/69 */, 128 + 6 /* 18/70-18/79 */, 128 + 6 /* 18/80-18/89 */, 128 + 7 /* 18/90-18/99 */, 128 + 8 /* 18/100-18/109 */, 128 + 9 /* 18/110-18/119 */, 128 + 10 /* 18/120-18/129 */, 128 + 11 /* 18/130-18/139 */, 128 + 12 /* 18/140-18/149 */, 128 + 13 /* 18/150-18/159 */, 128 + 14 /* 18/160-18/169 */, 128 + 15 /* 18/170-18/179 */, 128 + 16 /* 18/180-18/189 */, 128 + 17 /* 18/190-18/199 */, 128 + 18 /* 18/200-18/209 */, 128 + 19 /* 18/210-18/219 */, 128 + 20 /* 18/220+ */ }; /* * Stat Table (STR) -- weight limit in deca-pounds */ const byte adj_str_wgt[] = { 5 /* 3 */, 6 /* 4 */, 7 /* 5 */, 8 /* 6 */, 9 /* 7 */, 10 /* 8 */, 11 /* 9 */, 12 /* 10 */, 13 /* 11 */, 14 /* 12 */, 15 /* 13 */, 16 /* 14 */, 17 /* 15 */, 18 /* 16 */, 19 /* 17 */, 20 /* 18/00-18/09 */, 22 /* 18/10-18/19 */, 24 /* 18/20-18/29 */, 26 /* 18/30-18/39 */, 28 /* 18/40-18/49 */, 30 /* 18/50-18/59 */, 31 /* 18/60-18/69 */, 31 /* 18/70-18/79 */, 32 /* 18/80-18/89 */, 32 /* 18/90-18/99 */, 33 /* 18/100-18/109 */, 33 /* 18/110-18/119 */, 34 /* 18/120-18/129 */, 34 /* 18/130-18/139 */, 35 /* 18/140-18/149 */, 35 /* 18/150-18/159 */, 36 /* 18/160-18/169 */, 36 /* 18/170-18/179 */, 37 /* 18/180-18/189 */, 37 /* 18/190-18/199 */, 38 /* 18/200-18/209 */, 38 /* 18/210-18/219 */, 39 /* 18/220+ */ }; /* * Stat Table (STR) -- weapon weight limit in pounds */ const byte adj_str_hold[] = { 4 /* 3 */, 5 /* 4 */, 6 /* 5 */, 7 /* 6 */, 8 /* 7 */, 10 /* 8 */, 12 /* 9 */, 14 /* 10 */, 16 /* 11 */, 18 /* 12 */, 20 /* 13 */, 22 /* 14 */, 24 /* 15 */, 26 /* 16 */, 28 /* 17 */, 30 /* 18/00-18/09 */, 30 /* 18/10-18/19 */, 35 /* 18/20-18/29 */, 40 /* 18/30-18/39 */, 45 /* 18/40-18/49 */, 50 /* 18/50-18/59 */, 55 /* 18/60-18/69 */, 60 /* 18/70-18/79 */, 65 /* 18/80-18/89 */, 70 /* 18/90-18/99 */, 80 /* 18/100-18/109 */, 80 /* 18/110-18/119 */, 80 /* 18/120-18/129 */, 80 /* 18/130-18/139 */, 80 /* 18/140-18/149 */, 90 /* 18/150-18/159 */, 90 /* 18/160-18/169 */, 90 /* 18/170-18/179 */, 90 /* 18/180-18/189 */, 90 /* 18/190-18/199 */, 100 /* 18/200-18/209 */, 100 /* 18/210-18/219 */, 100 /* 18/220+ */ }; /* * Stat Table (STR) -- digging value */ const byte adj_str_dig[] = { 0 /* 3 */, 0 /* 4 */, 1 /* 5 */, 2 /* 6 */, 3 /* 7 */, 4 /* 8 */, 4 /* 9 */, 5 /* 10 */, 5 /* 11 */, 6 /* 12 */, 6 /* 13 */, 7 /* 14 */, 7 /* 15 */, 8 /* 16 */, 8 /* 17 */, 9 /* 18/00-18/09 */, 10 /* 18/10-18/19 */, 12 /* 18/20-18/29 */, 15 /* 18/30-18/39 */, 20 /* 18/40-18/49 */, 25 /* 18/50-18/59 */, 30 /* 18/60-18/69 */, 35 /* 18/70-18/79 */, 40 /* 18/80-18/89 */, 45 /* 18/90-18/99 */, 50 /* 18/100-18/109 */, 55 /* 18/110-18/119 */, 60 /* 18/120-18/129 */, 65 /* 18/130-18/139 */, 70 /* 18/140-18/149 */, 75 /* 18/150-18/159 */, 80 /* 18/160-18/169 */, 85 /* 18/170-18/179 */, 90 /* 18/180-18/189 */, 95 /* 18/190-18/199 */, 100 /* 18/200-18/209 */, 100 /* 18/210-18/219 */, 100 /* 18/220+ */ }; /* * Stat Table (STR) -- help index into the "blow" table */ const byte adj_str_blow[] = { 3 /* 3 */, 4 /* 4 */, 5 /* 5 */, 6 /* 6 */, 7 /* 7 */, 8 /* 8 */, 9 /* 9 */, 10 /* 10 */, 11 /* 11 */, 12 /* 12 */, 13 /* 13 */, 14 /* 14 */, 15 /* 15 */, 16 /* 16 */, 17 /* 17 */, 20 /* 18/00-18/09 */, 30 /* 18/10-18/19 */, 40 /* 18/20-18/29 */, 50 /* 18/30-18/39 */, 60 /* 18/40-18/49 */, 70 /* 18/50-18/59 */, 80 /* 18/60-18/69 */, 90 /* 18/70-18/79 */, 100 /* 18/80-18/89 */, 110 /* 18/90-18/99 */, 120 /* 18/100-18/109 */, 130 /* 18/110-18/119 */, 140 /* 18/120-18/129 */, 150 /* 18/130-18/139 */, 160 /* 18/140-18/149 */, 170 /* 18/150-18/159 */, 180 /* 18/160-18/169 */, 190 /* 18/170-18/179 */, 200 /* 18/180-18/189 */, 210 /* 18/190-18/199 */, 220 /* 18/200-18/209 */, 230 /* 18/210-18/219 */, 240 /* 18/220+ */ }; /* * Stat Table (DEX) -- index into the "blow" table */ const byte adj_dex_blow[] = { 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 0 /* 6 */, 0 /* 7 */, 0 /* 8 */, 0 /* 9 */, 1 /* 10 */, 1 /* 11 */, 1 /* 12 */, 1 /* 13 */, 1 /* 14 */, 1 /* 15 */, 1 /* 16 */, 1 /* 17 */, 1 /* 18/00-18/09 */, 2 /* 18/10-18/19 */, 2 /* 18/20-18/29 */, 2 /* 18/30-18/39 */, 2 /* 18/40-18/49 */, 3 /* 18/50-18/59 */, 3 /* 18/60-18/69 */, 4 /* 18/70-18/79 */, 4 /* 18/80-18/89 */, 5 /* 18/90-18/99 */, 6 /* 18/100-18/109 */, 7 /* 18/110-18/119 */, 8 /* 18/120-18/129 */, 9 /* 18/130-18/139 */, 10 /* 18/140-18/149 */, 11 /* 18/150-18/159 */, 11 /* 18/160-18/169 */, 11 /* 18/170-18/179 */, 11 /* 18/180-18/189 */, 11 /* 18/190-18/199 */, 11 /* 18/200-18/209 */, 11 /* 18/210-18/219 */, 11 /* 18/220+ */ }; /* * Stat Table (DEX) -- chance of avoiding "theft" and "falling" */ const byte adj_dex_safe[] = { 0 /* 3 */, 1 /* 4 */, 2 /* 5 */, 3 /* 6 */, 4 /* 7 */, 5 /* 8 */, 5 /* 9 */, 6 /* 10 */, 6 /* 11 */, 7 /* 12 */, 7 /* 13 */, 8 /* 14 */, 8 /* 15 */, 9 /* 16 */, 9 /* 17 */, 10 /* 18/00-18/09 */, 10 /* 18/10-18/19 */, 15 /* 18/20-18/29 */, 15 /* 18/30-18/39 */, 20 /* 18/40-18/49 */, 25 /* 18/50-18/59 */, 30 /* 18/60-18/69 */, 35 /* 18/70-18/79 */, 40 /* 18/80-18/89 */, 45 /* 18/90-18/99 */, 50 /* 18/100-18/109 */, 60 /* 18/110-18/119 */, 70 /* 18/120-18/129 */, 80 /* 18/130-18/139 */, 90 /* 18/140-18/149 */, 100 /* 18/150-18/159 */, 100 /* 18/160-18/169 */, 100 /* 18/170-18/179 */, 100 /* 18/180-18/189 */, 100 /* 18/190-18/199 */, 100 /* 18/200-18/209 */, 100 /* 18/210-18/219 */, 100 /* 18/220+ */ }; /* * Stat Table (CON) -- base regeneration rate */ const byte adj_con_fix[] = { 0 /* 3 */, 0 /* 4 */, 0 /* 5 */, 0 /* 6 */, 0 /* 7 */, 0 /* 8 */, 0 /* 9 */, 0 /* 10 */, 0 /* 11 */, 0 /* 12 */, 0 /* 13 */, 1 /* 14 */, 1 /* 15 */, 1 /* 16 */, 1 /* 17 */, 2 /* 18/00-18/09 */, 2 /* 18/10-18/19 */, 2 /* 18/20-18/29 */, 2 /* 18/30-18/39 */, 2 /* 18/40-18/49 */, 3 /* 18/50-18/59 */, 3 /* 18/60-18/69 */, 3 /* 18/70-18/79 */, 3 /* 18/80-18/89 */, 3 /* 18/90-18/99 */, 4 /* 18/100-18/109 */, 4 /* 18/110-18/119 */, 5 /* 18/120-18/129 */, 6 /* 18/130-18/139 */, 6 /* 18/140-18/149 */, 7 /* 18/150-18/159 */, 7 /* 18/160-18/169 */, 8 /* 18/170-18/179 */, 8 /* 18/180-18/189 */, 8 /* 18/190-18/199 */, 9 /* 18/200-18/209 */, 9 /* 18/210-18/219 */, 9 /* 18/220+ */ }; /* * Stat Table (CON) -- extra half-hitpoints per level (plus 128). * Because monsters don't breath as powerfully now, I have reduced the * effect of this stat. -LM- */ const byte adj_con_mhp[] = { 128 + -5 /* 3 */, 128 + -3 /* 4 */, 128 + -2 /* 5 */, 128 + -1 /* 6 */, 128 + -1 /* 7 */, 128 + 0 /* 8 */, 128 + 0 /* 9 */, 128 + 0 /* 10 */, 128 + 0 /* 11 */, 128 + 0 /* 12 */, 128 + 0 /* 13 */, 128 + 0 /* 14 */, 128 + 1 /* 15 */, 128 + 1 /* 16 */, 128 + 2 /* 17 */, 128 + 2 /* 18/00-18/09 */, 128 + 3 /* 18/10-18/19 */, 128 + 4 /* 18/20-18/29 */, 128 + 4 /* 18/30-18/39 */, 128 + 5 /* 18/40-18/49 */, 128 + 5 /* 18/50-18/59 */, 128 + 6 /* 18/60-18/69 */, 128 + 6 /* 18/70-18/79 */, 128 + 7 /* 18/80-18/89 */, 128 + 7 /* 18/90-18/99 */, 128 + 8 /* 18/100-18/109 */, 128 + 9 /* 18/110-18/119 */, 128 + 9 /* 18/120-18/129 */, 128 + 10 /* 18/130-18/139 */, 128 + 11 /* 18/140-18/149 */, 128 + 12 /* 18/150-18/159 */, 128 + 12 /* 18/160-18/169 */, 128 + 13 /* 18/170-18/179 */, 128 + 14 /* 18/180-18/189 */, 128 + 15 /* 18/190-18/199 */, 128 + 17 /* 18/200-18/209 */, 128 + 18 /* 18/210-18/219 */, 128 + 20 /* 18/220+ */ }; /* * This is changed for [O] combat V2. (From L.M.) * * This table is used to help calculate the number of blows the player * can make in a single round of attacks (one player turn) with a * weapon that is not too heavy to wield effectively. * * The player gets "blows_table[P][D]" blows/round, as shown below. * * To get "P", we look up the relevant "adj_str_blow[]" (see above), * multiply it by 6, and then divide it by the effective weapon * weight (in deci-pounds), rounding down. * * To get "D", we look up the relevant "adj_dex_blow[]" (see above). * * (Some interesting calculations) * The character cannot get five blows with any weapon greater than 36 * lb, and cannot get six with any weapon greater than 20 lb. */ const byte blows_table[12][12] = { /* <- Dexterity factor -> */ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11+ */ { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, /* 0 */ { 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3 }, /* 1 ^ */ { 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4 }, /* 2 | */ { 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4 }, /* 3 */ { 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5 }, /* 4 Ratio */ { 2, 2, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5 }, /* 5 of STR */ { 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }, /* 6 over */ { 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6 }, /* 7 weight */ { 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6 }, /* 8 */ { 2, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6 }, /* 9 | */ { 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6 }, /* 10 V */ { 2, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6 } /* 11+ */ }; /* * List of shop owner names */ cptr owner_names[] = { "Bilbo", "Raistlin", "Sultan", "Lyar-el", "Falilmawen", "Voirin", "Erashnak", "Grug", "Forovir", "Ellis", "Filbert", "Fthnargl", "Eloise", "Fundi", "Granthus", "Lorax", "Butch", "Elbereth", "Sarleth", "Narlock", "Haneka", "Loirin", "Wuto", "Araaka", "Poogor", "Felorfiliand", "Maroka", "Sasin", "Abiemar", "Hurk", "Soalin", "Merulla", "Kon-Dar", "Darg-Low", "Decado", "Wieland", "Kon-Dar", "Darg-Low", "Decado", "Elo", "Delicatus", "Gruce", "Animus", "Malvus", "Selaxis", "Deathchill", "Drios", "Bathric", "Vengella", "Wyrana", "Yojo", "Ranalar", "Horbag", "Elelen", "Isedrelias", "Vegnar", "Rodish", "Hesin", "Elvererith", "Zzathath", "Kon-Dar", "Darg-Low", "Decado", "Wieland", "Arnold", "Arndal", "Eddie", "Oglign", "Drew", "Orrax", "Anthrax", "Arkhoth", "Sarlyas", "Tuethic", "Bilious", "Fasgul", "Ellefris", "K'trrik'k", "Drocus", "Fungus", "Delantha", "Solvistani", "Xoril", "Aeonflux", "Nadoc", "Eramog", "Eowilith", "Huimog", "Peadus", "Vamog", "Hooshnak", "Balenn", "Ludwig", "Gunnar", "Torin", "Sarastro", "Sir Parsival", "Asenath", "McKinnon", "Hashnik", "Finak", "Krikkik", "Morival", "Hoshak", "Atal", "Ibenidd", "Eridish", "Vrudush", "Haob", "Proogdish", "Lumwis", "Muirt", "Dardobard", "Mauser", "Wizzle", "Midas", "Ja-Far", "Kakalrakakal", "Jal-Eth", "Fanelath", "Runcie", "Grumbleworth", "Flitter", "Xarillus", "Egbert", "Valindra", "Tae", "Cayd", "Fulir", "Domli", "Yaarjukka", "Gelaraldor", "Olelaldan", "Fthoglo", "Dridash", "Nelir", "Lignus", "Tilba", "Myrildric", "Mauser", "Lo Pan", "Buggerby", "Rjak", "Skidney", "Kyria", "Nikki", "Solostoran", "Achshe", "Kaza", "Fazzil", "Keldorn", "Philanthropus", "Agnar", "Buliance", "Vuirak", "Madish", "Falebrimbor", "Felil-Gand", "Thalegord", "Cthoaloth", "Ibeli", "Heto", "Nikki", "Solostoran", "Gary", "Histor", "Quark", "Topi", "Vhassa", "Kyn", "Bubonicus", "Corpselight", "Parrish", "Vile", "Prentice", "Griella", "Angel", "Flotsam", "Nieval", "Anastasia", "Charity", "Pugnacious", "Footsore", "Sidria", "Riatho", "Janaaka", "Cina", "Arunikki", "Chaeand", "Afardorf", "Lathaxl", "Falarewyn", "Vosur", "Araord", "Theradfrid", "One-Legged", "Dolaf", "Odnar", "Gandar", "Ro-sha", "Sarai", "Bodril", "Veloin", "Vanthylas", "Ossein", "Olva", "Shallowgrave", "Asuunu", "Prirand", "Ronar", "Galil-Gamir", "Rorbag", "Kiriarikirk", "Rilin", "Isung", "Dolaf", "Odnar", "Ro-shat", "Vanthylas", "Ossein", "Olvar", "Suiyan", "Aadocpeth", "Ognoqutoin", "Nothall", "Athoang", "Tanistil", "Paitnaw", "Thiaeth", "Kaoghequin", "Yaowing", "Aotnron", "Pwetholn", "Tim", "Waowenth", "Yiquent", "Paoingth", "Aargh'nt", "Wylntes", "Baongthan", "Vitholm", "Aaognwth", "Naothwell", "Jaltip", "Yillwyn", "Zyxlen", "Fsanong", "Paginoth", "Xaingol", "Wop", "Kaquin", "Agpoan", "Wewton", "Masoognnix", "Pagpon", "Leiwthen", "Pwvnom", "Laign Mawan", "Palson", "Thwynyhtm", "Chaillnew", "Nethlew", "Alorn Peln", "Ahsilth Peon" "McPallion", "Qonwyn", NULL }; cptr owner_suffix[] = { "the Friendly", "the Chicken", "the Midget", "the Comely", "the Friendly", "the Cowardly", "the Cheap", "the Fool", "the Hungry", "Psathiggua", "Long-Dead", "the Slow", "the Suave", "the Beautiful", "the Sneaky", "the Small", "the Mad", "Poisonbreath", "the Rotund", "the Dumb", "the Aged", "the Bold", "the Peasant", "the Poor", "the Wretched", "the Humble", "the Ugly", "the Grim", "the Handsome", "the Smith", "Dragonscale", "the Huge", "the Faint", "the Cold", "the Cruel", "the Mighty", "II", "III", "IV", "V", "VI", "the Sweet", "the Unclean", "the Telepath", "One-eye", "the Chaotic", "Swordmaster", "the Cheat", "the Beastly", "Beast-Slayer", "Dragon-Slayer", "Dthe Skilled", "Dragonson", "Disease-Carrier", "the Stout", "the Rotten", "Bare-Bones", "Spiderfriend", "Giant-Slayer", "the Slow", "the Strong", "the Weak", "the Fair", "Balrog-Slayer", "the Cruel", "Slayer", "the Vicious", "War-Dancer", "the Chosen", "the Wise", "the Pure", "the Holy", "the Druid", "the Wild", "the Dark", "the Chaste", "the Berserker", "the Youthfull", "the Mad", "the Virtuous", "the Chemist", "the Greedy", "the Cautious", "the Insane", "the Proud", "the Sweet", "Demonspawn", "the Herbmaster", "the Demonicist", "the Pungent", "the Wealthy", "the Great", "of Yendor", "the Noble", "the Smart", "the Dead", "the Treacherous", "the Bloodthirsty", "the Trusted", "Humanslayer", "the Bloated", "the Luminous", "the Pugilist", "the Lucky", "Lightfingered", "the Juggler", "the Shifty", "Greatclaw", "the Brigand", "the Wrinkled", "the Loser", "the Neutral", "the Patient", "Carter", "the Swift", "the Seer", "the Quiet", "the Learned", "the Iron", "the Lord", NULL }; /* * Buying and selling adjustments for race combinations. * Entry[owner][player] gives the basic "cost inflation". */ const byte rgold_adj[MAX_RACES][MAX_RACES] = { /* * Hum, HfE, Elf, Hal, Gno, Dwa, HfO, HfT, Dun, HiE, Barbarian, * HfOg, HGn, HTn, Cyc, Yek, Klc, Kbd, Nbl, DkE, Drc, Mind Flayer, * Imp, Glm, Skl, Zombie, Vampire, Spectre, Sprite, Beastman, Ghoul */ /* Human */ { 100, 105, 105, 110, 113, 115, 120, 125, 100, 105, 100, 124, 120, 110, 125, 115, 120, 120, 120, 120, 115, 120, 115, 105, 125, 125, 125, 125, 105, 120, 125 }, /* Half-Elf */ { 110, 100, 100, 105, 110, 120, 125, 130, 110, 100, 110, 120, 115, 108, 115, 110, 110, 120, 120, 115, 115, 110, 120, 110, 110, 110, 120, 110, 100, 125, 110 }, /* Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 120, 120, 105, 120, 110, 105, 125, 125, 110, 115, 108, 120, 115, 110, 110, 120, 110, 100, 125, 125}, /* Halfling */ { 115, 110, 105, 95, 105, 110, 115, 130, 115, 105, 115, 125, 120, 120, 125, 115, 110, 120, 120, 120, 115, 115, 120, 110, 120, 120, 130, 110, 110, 130, 120 }, /* Gnome */ { 115, 115, 110, 105, 95, 110, 115, 130, 115, 110, 115, 120, 125, 110, 120, 110, 105, 120, 110, 110, 105, 110, 120, 101, 110, 110, 120, 120, 115, 130, 110 }, /* Dwarf */ { 115, 120, 120, 110, 110, 95, 125, 135, 115, 120, 115, 125, 140, 130, 130, 120, 115, 115, 115, 135, 125, 120, 120, 105, 115, 115, 115, 115, 120, 130, 125 }, /* Half-Orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 115, 120, 125, 115, 120}, /* Half-Troll */ { 110, 115, 115, 110, 110, 130, 110, 110, 110, 115, 110, 110, 115, 120, 110, 120, 120, 110, 110, 110, 115, 110, 110, 115, 112, 112, 115, 112, 120, 110, 110 }, /* Amberite (Dunedain) */ { 100, 105, 105, 110, 113, 115, 120, 125, 100, 105, 100, 120, 120, 105, 120, 115, 105, 115, 120, 110, 105, 105, 120, 105, 120, 120, 125, 120, 105, 135, 120 }, /* High_Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 125, 125, 101, 120, 115, 110, 115, 125, 110, 110, 110, 125, 115, 120, 120, 125, 120, 100, 125, 120 }, /* Human / Barbarian (copied from human) */ { 100, 105, 105, 110, 113, 115, 120, 125, 100, 105, 100, 124, 120, 110, 125, 115, 120, 120, 120, 120, 115, 120, 115, 105, 125, 125, 130, 125, 115, 120, 125 }, /* Half-Ogre: theoretical, copied from half-troll */ { 110, 115, 115, 110, 110, 130, 110, 110, 110, 115, 110, 110, 115, 120, 110, 120, 120, 110, 110, 110, 115, 110, 110, 115, 112, 112, 115, 112, 120, 110, 110 }, /* Half-Giant: theoretical, copied from half-troll */ { 110, 115, 115, 110, 110, 130, 110, 110, 110, 115, 110, 110, 115, 120, 110, 120, 120, 110, 110, 110, 115, 110, 110, 115, 112, 112, 115, 112, 130, 120, 110 }, /* Half-Titan: theoretical, copied from High_Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 125, 125, 101, 120, 115, 110, 115, 125, 110, 110, 110, 125, 115, 120, 120, 120, 120, 130, 130, 120 }, /* Cyclops: theoretical, copied from half-troll */ { 110, 115, 115, 110, 110, 130, 110, 110, 110, 115, 110, 110, 115, 120, 110, 120, 120, 110, 110, 110, 115, 110, 110, 115, 112, 112, 115, 112, 130, 130, 110 }, /* Yeek: theoretical, copied from Half-Orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 120 }, /* Klackon: theoretical, copied from Gnome */ { 115, 115, 110, 105, 95, 110, 115, 130, 115, 110, 115, 120, 125, 110, 120, 110, 105, 120, 110, 110, 105, 110, 120, 101, 110, 110, 120, 120, 130, 130, 110 }, /* Kobold: theoretical, copied from Half-Orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 110 }, /* Nibelung: theoretical, copied from Dwarf */ { 115, 120, 120, 110, 110, 95, 125, 135, 115, 120, 115, 125, 140, 130, 130, 120, 115, 115, 115, 135, 125, 120, 120, 105, 115, 115, 120, 120, 130, 130, 115 }, /* Dark Elf */ { 110, 110, 110, 115, 120, 130, 115, 115, 120, 110, 115, 115, 115, 116, 115, 120, 120, 115, 115, 101, 110, 110, 110, 110, 112, 122, 110, 110, 110, 115, 122 }, /* Draconian: theoretical, copied from High_Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 125, 125, 101, 120, 115, 110, 115, 125, 110, 110, 110, 125, 115, 120, 120, 120, 120, 130, 130, 120 }, /* Mind Flayer: theoretical, copied from High_Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 125, 125, 101, 120, 115, 110, 115, 125, 110, 110, 110, 125, 115, 120, 120, 120, 120, 130, 130, 115 }, /* Imp: theoretical, copied from High_Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 125, 125, 101, 120, 115, 110, 115, 125, 110, 110, 110, 125, 115, 120, 120, 120, 120, 130, 130, 110 }, /* Golem: theoretical, copied from High_Elf */ { 110, 105, 100, 105, 110, 120, 125, 130, 110, 100, 110, 125, 125, 101, 120, 115, 110, 115, 125, 110, 110, 110, 125, 115, 120, 120, 120, 120, 130, 130, 120 }, /* Skeleton: theoretical, copied from half-orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 110 }, /* Zombie: Theoretical, copied from half-orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 110 }, /* Vampire: Theoretical, copied from half-orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 110 }, /* Spectre: Theoretical, copied from half-orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 110 }, /* Sprite: Theoretical, copied from half-orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 130 }, /* Beastman: Theoretical, copied from half-orc */ { 115, 120, 125, 115, 115, 130, 110, 115, 115, 125, 115, 110, 110, 120, 110, 120, 125, 115, 115, 110, 120, 110, 115, 125, 120, 120, 120, 120, 130, 130, 115 }, /* Ghoul */ { 120, 125, 125, 120, 117, 120, 113, 110, 120, 125, 115, 112, 111, 120, 111, 125, 120, 116, 115, 110, 115, 105, 105, 120, 105, 105, 105, 105, 130, 115, 100 }, }; /* * This table allows quick conversion from "speed" to "energy" * The basic function WAS ((S>=110) ? (S-110) : (100 / (120-S))) * Note that table access is *much* quicker than computation. * * Note that the table has been changed at high speeds. From * "Slow (-40)" to "Fast (+30)" is pretty much unchanged, but * at speeds above "Fast (+30)", one approaches an asymptotic * effective limit of 50 energy per turn. This means that it * is relatively easy to reach "Fast (+30)" and get about 40 * energy per turn, but then speed becomes very "expensive", * and you must get all the way to "Fast (+50)" to reach the * point of getting 45 energy per turn. After that point, * further increases in speed are more or less pointless, * except to balance out heavy inventory. * * It should be possible to lower the energy threshhold from * 100 units to 50 units, though this may interact badly with * the (compiled out) small random energy boost code. It may * also tend to cause more "clumping" at high speeds. */ const byte extract_energy[200] = { /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* S-50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* S-40 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* S-30 */ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, /* S-20 */ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, /* S-10 */ 5, 5, 5, 5, 6, 6, 7, 7, 8, 9, /* Norm */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* F+10 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, /* F+20 */ 30, 31, 32, 33, 34, 35, 36, 36, 37, 37, /* F+30 */ 38, 38, 39, 39, 40, 40, 40, 41, 41, 41, /* F+40 */ 42, 42, 42, 43, 43, 43, 44, 44, 44, 44, /* F+50 */ 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, /* F+60 */ 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, /* F+70 */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, /* Fast */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, }; /* * Base experience levels, may be adjusted up for race and/or class */ const s32b player_exp[PY_MAX_LEVEL] = { 10, 25, 45, 70, 100, 140, 200, 280, 380, 500, 650, 850, 1100, 1400, 1800, 2300, 2900, 3600, 4400, 5400, 6800, 8400, 10200, 12500, 17500, 25000, 35000L, 50000L, 75000L, 100000L, 150000L, 200000L, 275000L, 350000L, 450000L, 550000L, 700000L, 850000L, 1000000L, 1250000L, 1500000L, 1800000L, 2100000L, 2400000L, 2700000L, 3000000L, 3500000L, 4000000L, 4500000L, 5000000L }; /* * Player Sexes * * Title, * Winner */ player_sex sex_info[MAX_SEXES] = { { "Female", "Queen" }, { "Male", "King" } }; /* * Player Races * * Title, * {STR,INT,WIS,DEX,CON,CHR}, * r_dis, r_dev, r_sav, r_stl, r_sns, r_fos, r_thn, r_thb, * hitdie, exp base, * Age (Base, Mod), * Male (Hgt, Wgt), * Female (Hgt, Wgt) * infra */ player_race race_info[MAX_RACES] = { { "Human", { 0, 0, 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 10, 0, 0, 10, 100, 14, 6, 72, 6, 180, 25, 66, 4, 150, 20, 0, }, { "Half-Elf", { -1, 1, 1, 1, -1, 1 }, 2, 3, 3, 1, 6, 11, -2, 3, 9, 110, 24, 16, 66, 6, 130, 15, 62, 6, 100, 10, 2, }, { "Elf", { -1, 2, 2, 1, -2, 2 }, 5, 6, 6, 2, 8, 12, -6, 6, 8, 120, 75, 75, 60, 4, 100, 6, 54, 4, 80, 6, 3, }, { "Hobbit", { -2, 2, 1, 3, 2, 1 }, 15, 18, 18, 5, 12, 15, -11, 6, 7, 110, 21, 12, 36, 3, 60, 3, 33, 3, 50, 3, 4, }, { "Gnome", { -1, 2, 0, 2, 1, -2 }, 10, 12, 12, 3, 6, 13, -8, 0, 8, 135, 50, 40, 42, 3, 90, 6, 39, 3, 75, 3, 4, }, { "Dwarf", { 2, -2, 2, -2, 2, -3 }, 2, 9, 10, -1, 7, 10, 7, 0, 11, 125, 35, 15, 48, 3, 150, 10, 46, 3, 120, 10, 5, }, { "Half-Orc", { 2, -1, 0, 0, 1, -4 }, -3, -3, -3, -1, 0, 7, 3, -3, 10, 110, 11, 4, 66, 1, 150, 5, 62, 1, 120, 5, 3, }, { "Half-Troll", { 4, -4, -2, -4, 3, -6 }, -5, -8, -8, -2, -1, 5, 10, -5, 12, 137, 20, 10, 96, 10, 250, 50, 84, 8, 225, 40, 3, }, { "Amberite", { 1, 2, 2, 2, 3, 2 }, 4, 5, 5, 2, 3, 13, 6, 0, 10, 225, 50, 50, 82, 5, 190, 20, 78, 6, 180, 15, 0, }, { "High-Elf", { 1, 3, 2, 3, 1, 5 }, 4, 20, 20, 4, 3, 14, 0, 10, 10, 200, 100, 30, 90, 10, 190, 20, 82, 10, 180, 15, 4, }, { "Barbarian", { 3, -2, -1, 1, 2, -2 }, -2, -10, 2, -1, 1, 7, 10, 0, 11, 120, 14, 8, 82, 5, 200, 20, 78, 6, 190, 15, 0, }, { "Half-Ogre", { 3, -1, -1, -1, 3, -3 }, -3, -5, -5, -2, -1, 5, 12, 0, 12, 130, 40, 10, 92, 10, 255, 60, 80, 8, 235, 60, 3, }, { "Half-Giant", { 4, -2, -2, -2, 3, -3 }, -6, -8, -6, -2, -1, 5, 13, 2, 13, 150, 40, 10, 100, 10, 255, 65, 80, 10, 240, 64, 3, }, { "Half-Titan", { 5, 1, 1, -2, 3, 1 }, -5, 5, 2, -2, 1, 8, 13, 0, 14, 255, 100, 30, 111, 11, 255, 86, 99, 11, 250, 86, 0, }, { "Cyclops", { 4, -3, -3, -3, 4, -6 }, -4, -5, -5, -2, -2, 5, 10, 6, 13, 130, 50, 24, 92, 10, 255, 60, 80, 8, 235, 60, 1, }, { "Yeek", { -2, 1, 1, 1, -2, -7 }, 2, 4, 10, 3, 5, 15, -5, -5, 7, 100, 14, 3, 50, 3, 90, 6, 50, 3, 75, 3, 2, }, { "Klackon", { 2, -1, -1, 1, 2, -2 }, 10, 5, 5, 0, -1, 10, 5, 5, 12, 135, 20, 3, 60, 3, 80, 4, 54, 3, 70, 4, 2, }, { "Kobold", { 1, -1, 0, 1, 0, -4 }, -2, -3, -2, -1, 1, 8, 8, -8, 9, 125, 11, 3, 60, 1, 130, 5, 55, 1, 100, 5, 3, }, { "Nibelung", { 1, -1, 2, 0, 2, -4 }, 3, 5, 10, 1, 5, 10, 5, 0, 11, 135, 40, 12, 43, 3, 92, 6, 40, 3, 78, 3, 5, }, { "Dark-Elf", { -1, 3, 2, 2, -2, 1 }, 5, 15, 20, 3, 8, 12, -5, 7, 9, 150, 75, 75, 60, 4, 100, 6, 54, 4, 80, 6, 5, }, { "Draconian", { 2, 1, 1, 1, 2, -3 }, -2, 5, 3, 0, 1, 10, 5, 5, 11, 250, 75, 33, 76, 1, 160, 5, 72, 1, 130, 5, 2, }, { "Mindflayer", { -3, 4, 4, 0, -2, -5 }, 10, 25, 15, 2, 5, 12, -8, -5, 9, 140, 100, 25, 68, 6, 142, 15, 63, 6, 112, 10, 4, }, { "Imp", { -1, -1, -1, 1, 2, -3 }, -3, 2, -1, 1, -1, 10, 5, -5, 10, 110, 13, 4, 68, 1, 150, 5, 64, 1, 120, 5, 3, }, { "Golem", { 4, -5, -5, -2, 4, -4 }, -5, -5, 10, -1, -1, 8, 10, 0, 12, 200, 1, 100, 66, 1, 200, 6, 62, 1, 180, 6, 4, }, { "Skeleton", { 0, -2, -2, 0, 1, -4 }, -5, -5, 5, -1, -1, 8, 8, 0, 10, 145, 100, 35, 72, 6, 50, 5, 66, 4, 50, 5, 2, }, { "Zombie", { 2, -6, -6, 1, 4, -5 }, -5, -5, 8, -1, -1, 5, 10, 0, 13, 135, 100, 30, 72, 6, 100, 25, 66, 4, 100, 20, 2, }, { "Vampire", { 3, 3, -1, -1, 1, 2 }, 4, 10, 10, 4, 1, 8, 5, 0, 11, 200, 100, 30, 72, 6, 180, 25, 66, 4, 150, 20, 5, }, { "Spectre", { -5, 4, 4, 2, -3, -6 }, 10, 25, 20, 5, 5, 14, -10, -5, 7, 180, 100, 30, 72, 6, 100, 25, 66, 4, 100, 20, 5, }, { "Sprite", { -4, 3, 3, 3, -2, 2 }, 10, 10, 10, 4, 10, 10, -8, 0, 7, 175, 50, 25, 32, 2, 75, 2, 29, 2, 65, 2, 4, }, { "Beastman", { 2, -2, -1, -1, 2, -4 }, -5, -2, -1, -1, -1, 5, 9, 5, 11, 140, 14, 6, 65, 6, 150, 20, 61, 6, 120, 15, 0, }, { "Ghoul", { 0, -1, -1, -1, 1, -5 }, -3, -3, 6, 1, 0, 5, 5, 0, 9, 125, 100, 30, 72, 6, 100, 25, 66, 4, 100, 20, 2, } }; /* * Player Classes * * Title, * {STR,INT,WIS,DEX,CON,CHR}, * c_dis, c_dev, c_sav, c_stl, c_sns, c_fos, c_thn, c_thb, * x_dis, x_dev, x_sav, x_stl, x_sns, x_fos, x_thn, x_thb, * HD, Exp, pet_upkeep_div, heavy_sense */ player_class class_info[MAX_CLASS] = { { "Warrior", { 5, -2, -2, 2, 2, -1}, 25, 18, 18, 1, 14, 2, 25, 17, 12, 7, 24, 0, 0, 0, 100, 55, 9, 0, 20, TRUE }, { "Mage", {-5, 3, 0, 1, -2, 1}, 30, 36, 30, 2, 16, 20, 10, 10, 7, 13, 22, 0, 0, 0, 25, 14, 0, 30, 15, FALSE }, { "Priest", {-1, -3, 3, -1, 0, 2}, 25, 30, 32, 2, 16, 8, 16, 7, 7, 10, 27, 0, 0, 0, 50, 18, 2, 20, 20, FALSE }, { "Rogue", { 2, 1, -2, 3, 1, -1}, 45, 32, 28, 5, 32, 24, 15, 20, 15, 10, 24, 0, 0, 0, 70, 40, 6, 25, 20, TRUE }, { "Ranger", { 2, 2, 0, 1, 1, 1}, 30, 32, 28, 3, 24, 16, 15, 20, 8, 10, 24, 0, 0, 0, 65, 63, 4, 30, 20, TRUE }, { "Paladin", { 3, -3, 1, 0, 2, 2}, 20, 24, 26, 1, 12, 2, 19, 10, 7, 10, 25, 0, 0, 0, 76, 14, 6, 35, 20, TRUE }, { "Warrior-Mage", { 2, 2, 0, 1, 0, 1}, 30, 30, 28, 2, 18, 16, 20, 20, 7, 10, 22, 0, 0, 0, 75, 50, 4, 50, 20, FALSE }, { "Chaos-Warrior", { 2, 1, 0, 1, 2, -2}, 20, 25, 25, 1, 14, 12, 23, 7, 7, 11, 24, 0, 0, 0, 90, 40, 6, 35, 20, TRUE }, { "Monk", { 2, -1, 1, 3, 2, 1}, 45, 32, 28, 5, 16, 24, 12, 14, 15, 11, 25, 0, 0, 0, 30, 25, 6, 40, 20, FALSE }, { "Mindcrafter", {-1, 0, 3, -1, -1, 2}, /* note: spell stat is Wis */ 30, 30, 30, 3, 22, 16, 15, 15, 10, 10, 24, 0, 0, 0, 30, 20, 2, 25, 20, FALSE }, { "High-Mage", {-5, 4, 0, 0, -2, 1}, 30, 36, 30, 2, 16, 20, 10, 10, 7, 13, 22, 0, 0, 0, 15, 10, 0, 30, 12, FALSE }, }; /* * Hack -- the spell information table. * * Class Name * * Spell Book * Spell Xtra * * Spell Stat, * Spell Type, * * Spell Level, * Spell Encumbrance, * * Array of { Lev, Mana, Fail, Exp/Lev } */ player_magic magic_info[MAX_CLASS] = { { /*** Warrior ***/ 0, 0, A_STR, 0, 99, 0, { { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, }, }, { /*** Mage ***/ TV_SORCERY_BOOK, 0, A_INT, 0, 1, 300, { { /* Mage: Life magic */ { 1, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 7, 7 }, { 9, 8 }, { 12, 12 }, { 15, 14 }, { 16, 16 }, { 17, 17 }, { 18, 18 }, { 19, 19 }, { 20, 20 }, { 23, 23 }, { 30, 30 }, { 35, 70 }, { 26, 30 }, { 28, 25 }, { 33, 33 }, { 35, 35 }, { 35, 35 }, { 35, 55 }, { 39, 40 }, { 46, 70 }, { 9, 9 }, { 25, 25 }, { 35, 85 }, { 42, 100 }, { 45, 90 }, { 48, 50 }, { 49, 100 }, { 50, 100 } }, /* Mage: Sorcery */ { { 1, 1 }, { 1, 2 }, { 3, 3 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 5 }, { 7, 7 }, { 9, 7 }, { 10, 7 }, { 11, 7 }, { 13, 7 }, { 18, 12 }, { 22, 12 }, { 28, 20 }, { 33, 30 }, { 3, 3 }, { 10, 10 }, { 10, 10 }, { 12, 12 }, { 14, 10 }, { 20, 18 }, { 20, 18 }, { 25, 25 }, { 10, 10 }, { 25, 25 }, { 25, 30 }, { 30, 40 }, { 40, 80 }, { 40, 100 }, { 42, 50 }, { 45, 70 }, }, /* Mage: Nature Magic */ { { 1, 1 }, { 3, 3 }, { 3, 3 }, { 4, 4 }, { 4, 4 }, { 4, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 }, { 7, 6 }, { 7, 6 }, { 9, 6 }, { 19, 12 }, { 25, 25 }, { 40, 100 }, { 7, 7 }, { 9, 12 }, { 10, 12 }, { 15, 20 }, { 30, 30 }, { 37, 40 }, { 38, 45 }, { 40, 90 }, { 20, 18 }, { 23, 23 }, { 25, 25 }, { 30, 27 }, { 35, 30 }, { 37, 35 }, { 40, 90 }, { 40, 75 } }, /* Mage: Chaos Magic */ { { 1, 1 }, { 1, 2 }, { 2, 2 }, { 5, 5 }, { 9, 6 }, { 13, 9 }, { 14, 9 }, { 15, 9 }, { 17, 10 }, { 19, 12 }, { 21, 13 }, { 23, 15 }, { 25, 16 }, { 25, 18 }, { 30, 20 }, { 35, 40 }, { 11, 7 }, { 15, 15 }, { 16, 14 }, {25, 25 }, { 30, 25 }, { 42, 50 }, { 45, 90 }, { 47, 100 }, { 20, 20 }, { 35, 32 }, { 37, 34 }, { 41, 42 }, { 43, 44 }, { 45, 48 }, { 47, 75 }, { 49, 100 } }, /* Mage: Death Magic */ { { 1, 1 }, { 2, 2 }, { 2, 2 }, { 3, 3 }, { 5, 5 }, { 7, 10 }, { 9, 9 }, { 10, 10 }, { 12, 12 }, { 13, 12 }, { 18, 15 }, { 23, 20 }, { 30, 75 }, { 33, 35 }, { 37, 25 }, { 45, 50 }, { 10, 20 }, { 10, 15 }, { 11, 11 }, { 30, 25 }, { 33, 35 }, { 33, 90 }, { 40, 40 }, { 40, 75 }, { 20, 20 }, { 25, 66 }, { 30, 40 }, { 33, 35 }, { 37, 35 }, { 42, 120 }, { 45, 100 }, { 47, 100 } }, /* Mage: Trump Magic */ { { 1, 1 }, { 3, 3 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 9, 9 }, { 14, 12 }, { 17, 15 }, { 20, 20 }, { 24, 22 }, { 28, 24 }, { 30, 25 }, { 33, 28 }, { 35, 30 }, { 40, 35 }, { 42, 40 }, { 15, 15 }, { 24, 24 }, { 26, 26 }, { 30, 30 }, { 35, 70 }, { 40, 100 }, { 42, 50 }, { 45, 100 }, { 30, 30 }, { 35, 50 }, { 36, 80 }, { 39, 80 }, { 42, 100 }, { 47, 100 }, { 48, 100 }, { 49, 100 } }, /* Mage: Arcane Magic */ { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 2, 1 }, { 2, 2 }, { 4, 4 }, { 5, 5 }, { 6, 5 }, { 7, 6 }, { 8, 8 }, { 9, 8 }, { 9, 9 }, { 9, 9 }, { 11, 10 }, { 12, 12 }, { 13, 12 }, { 14, 12 }, { 15, 12 }, { 16, 14 }, { 18, 15 }, { 20, 16 }, { 23, 18 }, { 25, 20 }, { 25, 20 }, { 28, 25 }, { 35, 35 }, { 38, 30 }, { 40, 30 }, { 41, 30 }, { 42, 30 }, { 45, 50 }, { 49, 100 } } } }, { /*** Priest ***/ TV_LIFE_BOOK, 0, A_WIS, 1, 1, 350, { /* Priest: Life Magic */ { { 1, 1 }, { 1, 2 }, { 1, 2 }, { 3, 2 }, { 3, 3 }, { 4, 4 }, { 5, 4 }, { 7, 5 }, { 7, 6 }, { 9, 6 }, { 9, 7 }, { 10, 8 }, { 10, 8 }, { 11, 8 }, { 20, 16 }, { 33, 55 }, { 15, 14 }, { 16, 14 }, { 17, 14 }, { 24, 20 }, { 25, 20 }, { 25, 25 }, { 39, 32 }, { 44, 44 }, { 5, 5 }, { 15, 14 }, { 30, 50 }, { 35, 70 }, { 40, 50 }, { 40, 40 }, { 42, 90 }, { 45, 90 }, }, /* Priest: Sorcery */ { { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 }, { 7, 6 }, { 9, 7 }, { 11, 10 }, { 13, 11 }, { 14, 12 }, { 15, 13 }, { 16, 14 }, { 22, 15 }, { 27, 17 }, { 30, 22 }, { 36, 33 }, { 7, 7 }, { 12, 12 }, { 14, 14 }, { 15, 15 }, { 18, 18 }, { 20, 20 }, { 22, 22 }, { 27, 27 }, { 13, 13 }, { 24, 24 }, { 27, 30 }, { 33, 40 }, { 42, 80 }, { 42, 100 }, { 45, 50 }, { 48, 70 }, }, /* Priest: Nature Magic */ { { 2, 1 }, { 5, 3 }, { 5, 4 }, { 6, 5 }, { 6, 5 }, { 6, 6 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 8, 7 }, { 9, 10 }, { 10, 10 }, { 11, 11 }, { 20, 20 }, { 30, 30 }, { 42, 100 }, { 9, 9 }, { 11, 12 }, { 12, 13 }, { 18, 20 }, { 35, 35 }, { 39, 40 }, { 40, 50 }, { 42, 90 }, { 22, 22 }, { 25, 25 }, { 27, 27 }, { 32, 30 }, { 37, 32 }, { 39, 37 }, { 42, 90 }, { 44, 80 }, }, /* Priest: Chaos Magic */ { { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 10, 6 }, { 11, 6 }, { 16, 11 }, { 17, 11 }, { 19, 15 }, { 21, 16 }, { 23, 18 }, { 25, 18 }, { 27, 20 }, { 29, 22 }, { 33, 23 }, { 37, 42 }, { 14, 11 }, { 17, 17 }, { 20, 18 }, { 27, 27 }, { 35, 30 }, { 45, 55 }, { 47, 90 }, { 49, 100 }, { 25, 25 }, { 37, 37 }, { 39, 37 }, { 43, 45 }, { 45, 47 }, { 47, 50 }, { 49, 95 }, { 50, 100 }, }, /* Priest: Death Magic */ { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 5, 4 }, { 7, 7 }, { 9, 11 }, { 11, 11 }, { 12, 12 }, { 14, 14 }, { 16, 16 }, { 21, 20 }, { 25, 24 }, { 33, 75 }, { 35, 35 }, { 40, 30 }, { 50, 52 }, { 13, 20 }, { 13, 15 }, { 14, 15 }, { 33, 33 }, { 35, 35 }, { 35, 95 }, { 44, 44 }, { 45, 75 }, { 25, 25 }, { 30, 75 }, { 35, 45 }, { 40, 40 }, { 42, 40 }, { 48, 125 }, { 49, 100 }, { 50, 111 }, }, /* Priest: Trump Magic */ { { 1, 1 }, { 4, 4 }, { 6, 5 }, { 7, 7 }, { 9, 9 }, { 11, 11 }, { 17, 14 }, { 19, 17 }, { 22, 22 }, { 26, 24 }, { 30, 25 }, { 32, 30 }, { 35, 30 }, { 38, 35 }, { 42, 40 }, { 45, 45 }, { 17, 17 }, { 27, 25 }, { 29, 27 }, { 33, 30 }, { 38, 75 }, { 41, 110 }, { 45, 55 }, { 49, 125 }, { 32, 30 }, { 38, 55 }, { 40, 85 }, { 43, 85 }, { 46, 110 }, { 48, 115 }, { 49, 120 }, { 50, 125 } }, /* Priest: Arcane Magic */ { { 1, 1 }, { 1, 1 }, { 2, 1 }, { 2, 2 }, { 3, 3 }, { 5, 5 }, { 6, 6 }, { 7, 6 }, { 8, 7 }, { 9, 8 }, { 10, 9 }, { 11, 10 }, { 12, 11 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, { 16, 15 }, { 17, 16 }, { 18, 17 }, { 19, 18 }, { 22, 20 }, { 24, 22 }, { 27, 24 }, { 29, 26 }, { 33, 30 }, { 37, 36 }, { 40, 37 }, { 42, 38 }, { 44, 39 }, { 46, 40 }, { 47, 55 }, { 50, 120 } } } }, { /*** Rogue ***/ TV_SORCERY_BOOK, 0, A_INT, 0, 5, 350, { { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Rogue (Burglar): Sorcery */ { { 5, 1 }, { 7, 2 }, { 8, 3 }, { 9, 3 }, { 13, 6 }, { 15, 7 }, { 17, 9 }, { 21, 12 }, { 25, 14 }, { 27, 15 }, { 29, 17 }, { 30, 20 }, { 31, 23 }, { 32, 25 }, { 35, 30 }, { 40, 35 }, { 9, 3 }, { 13, 10 }, { 14, 10 }, { 15, 10 }, { 16, 10 }, { 17, 20 }, { 18, 17 }, { 30, 35 }, { 15, 15 }, { 20, 20 }, { 35, 40 }, { 37, 40 }, { 43, 80 }, { 44, 100 }, { 45, 50 }, { 99, 0 }, }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Rogue (Assassin): Death Magic */ { { 5, 3 }, { 7, 4 }, { 9, 5 }, { 13, 7 }, { 15, 7 }, { 17, 15 }, { 19, 17 }, { 19, 19 }, { 21, 21 }, { 23, 23 }, { 27, 25 }, { 30, 30 }, { 35, 35 }, { 45, 45 }, { 99, 0 }, { 99, 0 }, { 20, 25 }, { 23, 20 }, { 28, 28 }, { 32, 32 }, { 46, 45 }, { 48, 100 }, { 50, 50 }, { 99, 0 }, { 30, 30 }, { 31, 80 }, { 32, 40 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 50, 125 }, }, /* Rogue (Card Shark): Trump Magic */ { { 5, 2 }, { 7, 5 }, { 9, 7 }, { 11, 9 }, { 13, 11 }, { 15, 13 }, { 19, 15 }, { 21, 20 }, { 25, 22 }, { 30, 26 }, { 33, 26 }, { 35, 32 }, { 40, 35 }, { 42, 38 }, { 46, 44 }, { 49, 50 }, { 20, 15 }, { 30, 30 }, { 33, 30 }, { 38, 33 }, { 42, 90 }, { 45, 150 }, { 48, 75 }, { 99, 0 }, { 35, 30 }, { 42, 65 }, { 44, 100 }, { 46, 100 }, { 99, 0 }, { 49, 125 }, { 99, 0 }, { 99, 0 }, }, /* Rogue (Thief): Arcane Magic */ { { 1, 1 }, { 1, 1 }, { 2, 1 }, { 2, 2 }, { 3, 3 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 13, 11 }, { 14, 12 }, { 15, 13 }, { 16, 14 }, { 17, 15 }, { 18, 16 }, { 19, 17 }, { 20, 20 }, { 23, 22 }, { 25, 24 }, { 28, 25 }, { 30, 28 }, { 35, 30 }, { 39, 36 }, { 42, 37 }, { 44, 38 }, { 46, 40 }, { 47, 42 }, { 48, 60 }, { 50, 125 } } } }, { /*** Ranger ***/ TV_SORCERY_BOOK, 0, A_INT, 0, 3, 400, { { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Ranger: Sorcery */ { { 3, 2 }, { 3, 3 }, { 5, 4 }, { 7, 4 }, { 9, 6 }, { 11, 8 }, { 13, 8 }, { 17, 17 }, { 20, 19 }, { 23, 25 }, { 25, 25 }, { 27, 25 }, { 31, 27 }, { 34, 35 }, { 38, 37 }, { 42, 40 }, { 15, 7 }, { 15, 20 }, { 17, 17 }, { 18, 18 }, { 19, 25 }, { 23, 25 }, { 27, 27 }, { 35, 35 }, { 20, 20 }, { 27, 27 }, { 37, 60 }, { 40, 40 }, { 45, 80 }, { 45, 100 }, { 50, 50 }, { 99, 0 }, }, /* Ranger: Nature Magic */ { { 3, 1 }, { 4, 3 }, { 4, 4 }, { 5, 7 }, { 6, 7 }, { 7, 7 }, { 8, 7 }, { 9, 7 }, { 9, 7 }, { 10, 7 }, { 11, 9 }, { 12, 9 }, { 14, 9 }, { 18, 20 }, { 23, 23 }, { 40, 100 }, { 10, 10 }, { 12, 12 }, { 14, 15 }, { 20, 30 }, { 35, 30 }, { 38, 40 }, { 40, 55 }, { 42, 80 }, { 25, 28 }, { 26, 26 }, { 30, 35 }, { 32, 29 }, { 36, 33 }, { 40, 35 }, { 41, 80 }, { 42, 80 }, }, /* Ranger: Chaos Magic */ { { 3, 1 }, { 3, 3 }, { 5, 3 }, { 7, 5 }, { 14, 12 }, { 20, 16 }, { 25, 21 }, { 25, 22 }, { 27, 23 }, { 30, 25 }, { 33, 30 }, { 35, 31 }, { 37, 35 }, { 39, 29 }, { 43, 30 }, { 48, 50 }, { 22, 20 }, { 25, 25 }, { 28, 25 }, { 35, 32 }, { 38, 35 }, { 42, 75 }, { 48, 100 }, { 99, 0 }, { 33, 33 }, { 40, 45 }, { 42, 42 }, { 48, 48 }, { 50, 50 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Ranger: Death Magic */ { { 5, 2 }, { 5, 3 }, { 7, 4 }, { 9, 5 }, { 11, 8 }, { 17, 25 }, { 19, 19 }, { 22, 22 }, { 24, 24 }, { 26, 26 }, { 28, 28 }, { 30, 30 }, { 40, 80 }, { 45, 40 }, { 99, 0 }, { 99, 0 }, { 25, 30 }, { 25, 25 }, { 27, 27 }, { 39, 39 }, { 45, 45 }, { 46, 100 }, { 99, 0 }, { 99, 0 }, { 35, 35 }, { 38, 90 }, { 40, 45 }, { 48, 50 }, { 50, 50 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, /* Ranger: Trump Magic */ { { 3, 1 }, { 6, 6 }, { 9, 7 }, { 10, 8 }, { 13, 10 }, { 17, 15 }, { 20, 17 }, { 22, 20 }, { 24, 22 }, { 28, 25 }, { 33, 26 }, { 36, 32 }, { 38, 33 }, { 42, 38 }, { 45, 42 }, { 99, 0 }, { 20, 20 }, { 28, 26 }, { 31, 30 }, { 36, 33 }, { 41, 80 }, { 44, 120 }, { 99, 0 }, { 99, 0 }, { 35, 33 }, { 40, 65 }, { 99, 0 }, { 47, 95 }, { 50, 120 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Ranger: Arcane Magic */ { { 3, 2 }, { 3, 2 }, { 4, 3 }, { 4, 3 }, { 5, 4 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 12, 12 }, { 14, 13 }, { 15, 14 }, { 16, 15 }, { 17, 16 }, { 18, 17 }, { 19, 18 }, { 20, 19 }, { 22, 20 }, { 25, 23 }, { 27, 26 }, { 29, 27 }, { 33, 30 }, { 38, 36 }, { 42, 38 }, { 44, 38 }, { 46, 40 }, { 47, 42 }, { 48, 44 }, { 49, 65 }, { 99, 0 } } } }, { /*** Paladin ***/ TV_LIFE_BOOK, 0, A_WIS, 1, 1, 400, { /* Paladin: Life Magic */ { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 3 }, { 5, 4 }, { 8, 5 }, { 11, 9 }, { 13, 10 }, { 14, 11 }, { 15, 15 }, { 17, 15 }, { 18, 15 }, { 18, 15 }, { 19, 15 }, { 30, 25 }, { 35, 70 }, { 25, 22 }, { 28, 24 }, { 30, 25 }, { 33, 30 }, { 35, 32 }, { 35, 55 }, { 39, 38 }, { 46, 60 }, { 9, 9 }, { 25, 20 }, { 35, 65 }, { 40, 80 }, { 45, 80 }, { 45, 45 }, { 48, 100 }, { 50, 100 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, /* Paladin: Death Magic */ { { 1, 1 }, { 3, 2 }, { 4, 3 }, { 6, 5 }, { 8, 8 }, { 10, 11 }, { 12, 12 }, { 15, 15 }, { 17, 17 }, { 19, 19 }, { 23, 23 }, { 28, 26 }, { 35, 75 }, { 40, 35 }, { 45, 35 }, { 50, 52 }, { 15, 20 }, { 15, 20 }, { 18, 20 }, { 38, 38 }, { 40, 40 }, { 42, 100 }, { 48, 50 }, { 48, 75 }, { 30, 35 }, { 36, 85 }, { 38, 45 }, { 45, 45 }, { 47, 45 }, { 50, 150 }, { 50, 100 }, { 50, 111 } }, /* Paladin: No Trump magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Paladin: No Arcane Magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, } } }, { /*** Warrior-Mage ***/ TV_SORCERY_BOOK, 0, A_INT, 0, 1, 350, { { { 2, 2 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 8, 8 }, { 9, 9 }, { 14, 14 }, { 16, 16 }, { 18, 18 }, { 20, 20 }, { 22, 22 }, { 24, 24 }, { 26, 26 }, { 28, 28 }, { 33, 33 }, { 40, 70 }, { 28, 28 }, { 30, 30 }, { 34, 34 }, { 36, 36 }, { 38, 38 }, { 42, 55 }, { 45, 45 }, { 50, 70 }, { 10, 10 }, { 28, 28 }, { 38, 85 }, { 45, 90 }, { 46, 90 }, { 48, 50 }, { 49, 100 }, { 50, 100 } }, /* Warrior-Mage: Sorcery */ { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 10, 9 }, { 11, 10 }, { 12, 11 }, { 13, 12 }, { 20, 15 }, { 27, 18 }, { 33, 25 }, { 40, 40 }, { 4, 4 }, { 12, 12 }, { 14, 12 }, { 15, 12 }, { 16, 14 }, { 19, 19 }, { 24, 22 }, { 28, 28 }, { 12, 12 }, { 19, 19 }, { 30, 35 }, { 35, 45 }, { 42, 85 }, { 45, 100 }, { 46, 55 }, { 48, 75 }, }, /* Warrior-Mage: Nature Magic */ { { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 12, 12 }, { 13, 13 }, { 14, 14 }, { 19, 15 }, { 31, 31 }, { 45, 100 }, { 9, 9 }, { 12, 12 }, { 15, 15 }, { 20, 22 }, { 38, 38 }, { 40, 42 }, { 45, 48 }, { 49, 95 }, { 25, 25 }, { 27, 27 }, { 28, 28 }, { 33, 33 }, { 38, 38 }, { 41, 41 }, { 45, 95 }, { 50, 85 }, }, /* Warrior-Mage: Chaos Magic */ { { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 8, 8 }, { 11, 11 }, { 17, 15 }, { 18, 17 }, { 21, 21 }, { 23, 22 }, { 27, 25 }, { 29, 30 }, { 33, 33 }, { 37, 35 }, { 41, 40 }, { 48, 50 }, { 12, 12 }, { 17, 16 }, { 20, 18 }, { 27, 25 }, { 35, 30 }, { 45, 55 }, { 49, 95 }, { 50, 111 }, { 24, 20 }, { 40, 35 }, { 42, 40 }, { 46, 44 }, { 48, 48 }, { 49, 50 }, { 50, 100 }, { 50, 100 }, }, /* Warrior-Mage: Death Magic */ { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 7, 7 }, { 9, 10 }, { 10, 10 }, { 12, 12 }, { 14, 14 }, { 16, 16 }, { 21, 21 }, { 25, 25 }, { 35, 75 }, { 40, 40 }, { 44, 45 }, { 48, 55 }, { 10, 22 }, { 12, 18 }, { 14, 18 }, { 30, 30 }, { 40, 40 }, { 42, 90 }, { 45, 50 }, { 48, 85 }, { 24, 24 }, { 33, 80 }, { 35, 45 }, { 42, 50 }, { 45, 55 }, { 50, 135 }, { 50, 100 }, { 50, 123 }, }, /* Warrior-Mage: Trump Magic */ { { 1, 1 }, { 5, 5 }, { 7, 7 }, { 8, 7 }, { 10, 10 }, { 12, 12 }, { 18, 15 }, { 20, 18 }, { 24, 23 }, { 28, 25 }, { 31, 26 }, { 33, 30 }, { 38, 32 }, { 40, 38 }, { 44, 42 }, { 48, 46 }, { 19, 18 }, { 29, 27 }, { 31, 30 }, { 35, 33 }, { 40, 80 }, { 42, 120 }, { 46, 55 }, { 50, 135 }, { 33, 30 }, { 40, 60 }, { 42, 95 }, { 45, 95 }, { 46, 120 }, { 48, 125 }, { 49, 130 }, { 50, 135 } }, /* Warrior-Mage: Arcane Magic */ { { 1, 1 }, { 2, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 11, 10 }, { 12, 11 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, { 16, 15 }, { 17, 16 }, { 18, 17 }, { 19, 18 }, { 20, 20 }, { 23, 22 }, { 25, 23 }, { 29, 25 }, { 30, 27 }, { 35, 30 }, { 39, 38 }, { 41, 40 }, { 43, 42 }, { 45, 44 }, { 47, 45 }, { 48, 65 }, { 50, 140 } } } }, { /*** Chaos Warrior ***/ TV_SORCERY_BOOK, 0, A_INT, 0, 2, 400, { { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, /* Chaos Warrior: Chaos Magic */ { { 2, 1 }, { 3, 3 }, { 4, 4 }, { 5, 6 }, { 7, 9 }, { 8, 9 }, { 15, 12 }, { 16, 15 }, { 19, 18 }, { 22, 21 }, { 25, 25 }, { 28, 27 }, { 30, 30 }, { 33, 36 }, { 36, 39 }, { 40, 67 }, { 11, 16 }, { 14, 21 }, { 16, 22 }, { 23, 34 }, { 30, 45 }, { 42, 75 }, { 45, 135 }, { 47, 150 }, { 23, 34 }, { 35, 52 }, { 37, 55 }, { 41, 63 }, { 43, 66 }, { 45, 72 }, { 48, 150 }, { 49, 150 }, }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, /* Chaos Warrior: No Trump magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Chaos Warrior: No Arcane Magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, } } }, { /*** Monk ***/ TV_LIFE_BOOK, 0, A_WIS, 0, 1, 300, { { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 8, 6 }, { 11, 10 }, { 13, 12 }, { 15, 12 }, { 16, 15 }, { 17, 15 }, { 18, 16 }, { 19, 16 }, { 20, 18 }, { 30, 25 }, { 35, 70 }, { 26, 26 }, { 28, 28 }, { 32, 32 }, { 36, 35 }, { 38, 35 }, { 40, 60 }, { 45, 45 }, { 48, 64 }, { 10, 10 }, { 25, 25 }, { 40, 65 }, { 44, 84 }, { 46, 64 }, { 48, 40 }, { 49, 100 }, { 50, 100 } }, /* Monk: Sorcery */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, /* Monk: Nature Magic */ { { 1, 1 }, { 3, 3 }, { 4, 4 }, { 7, 7 }, { 7, 7 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 8 }, { 11, 9 }, { 12, 10 }, { 14, 12 }, { 16, 12 }, { 18, 22 }, { 31, 31 }, { 40, 100 }, { 12, 12 }, { 14, 14 }, { 16, 16 }, { 22, 30 }, { 35, 35 }, { 40, 40 }, { 45, 55 }, { 50, 80 }, { 28, 28 }, { 30, 30 }, { 35, 50 }, { 33, 33 }, { 38, 38 }, { 42, 40 }, { 45, 85 }, { 48, 85 } }, /* Monk: Chaos Magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, /* Monk: Death Magic */ { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 5, 5 }, { 7, 7 }, { 11, 11 }, { 12, 12 }, { 14, 14 }, { 16, 16 }, { 19, 19 }, { 22, 22 }, { 25, 25 }, { 33, 80 }, { 40, 40 }, { 45, 45 }, { 50, 60 }, { 15, 20 }, { 16, 16 }, { 18, 18 }, { 35, 35 }, { 40, 40 }, { 42, 95 }, { 48, 50 }, { 49, 80 }, { 30, 30 }, { 37, 85 }, { 38, 50 }, { 44, 44 }, { 46, 50 }, { 50, 140 }, { 50, 100 }, { 50, 115 } }, /* Monk: No Trump magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, }, /* Monk: No Arcane Magic */ { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, } } }, { /*** Mindcrafter ***/ TV_LIFE_BOOK, 0, A_WIS, 0, 99, 300, { { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, { { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 }, { 99, 0 } }, }, }, { /*** High Mage ***/ TV_SORCERY_BOOK, 0, A_INT, 0, 1, 300, { /* High Mage: Life Magic */ { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 5 }, { 9, 9 }, { 12, 10 }, { 14, 12 }, { 15, 14 }, { 15, 15 }, { 17, 15 }, { 19, 17 }, { 21, 19 }, { 25, 25 }, { 30, 50 }, { 20, 20 }, { 24, 24 }, { 30, 30 }, { 31, 30 }, { 32, 30 }, { 33, 40 }, { 35, 35 }, { 40, 70 }, { 5, 5 }, { 20, 20 }, { 30, 70 }, { 40, 80 }, { 42, 75 }, { 45, 40 }, { 47, 90 }, { 49, 90 } }, /* High Mage: Sorcery */ { { 1, 1 }, { 1, 1 }, { 2, 2 }, { 2, 2 }, { 3, 3 }, { 4, 3 }, { 5, 4 }, { 5, 5 }, { 7, 5 }, { 7, 5 }, { 9, 5 }, { 9, 5 }, { 13, 8 }, { 17, 10 }, { 24, 15 }, { 28, 20 }, { 2, 2 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 12, 9 }, { 15, 12 }, { 17, 12 }, { 20, 20 }, { 8, 8 }, { 20, 20 }, { 20, 25 }, { 25, 30 }, { 30, 65 }, { 35, 80 }, { 40, 40 }, { 42, 65 }, }, /* High Mage: Nature Magic */ { { 1, 1 }, { 2, 1 }, { 2, 2 }, { 3, 2 }, { 3, 3 }, { 4, 3 }, { 4, 4 }, { 5, 4 }, { 5, 4 }, { 5, 4 }, { 5, 5 }, { 5, 5 }, { 7, 5 }, { 14, 10 }, { 20, 20 }, { 35, 80 }, { 5, 5 }, { 7, 7 }, { 8, 8 }, { 12, 15 }, { 25, 25 }, { 33, 35 }, { 35, 40 }, { 37, 65 }, { 15, 15 }, { 20, 20 }, { 22, 22 }, { 28, 25 }, { 32, 28 }, { 34, 30 }, { 36, 80 }, { 39, 65 } }, /* High Mage: Chaos Magic */ { { 1, 1 }, { 1, 1 }, { 2, 1 }, { 4, 2 }, { 6, 4 }, { 10, 5 }, { 12, 6 }, { 14, 7 }, { 15, 9 }, { 17, 10 }, { 19, 11 }, { 21, 12 }, { 22, 13 }, { 23, 15 }, { 27, 17 }, { 30, 35 }, { 9, 5 }, { 12, 12 }, { 14, 12 }, { 21, 21 }, { 26, 22 }, { 39, 40 }, { 42, 80 }, { 44, 90 }, { 16, 16 }, { 32, 30 }, { 34, 32 }, { 36, 36 }, { 38, 38 }, { 40, 45 }, { 43, 55 }, { 46, 90 } }, /* High Mage: Death Magic */ { { 1, 1 }, { 1, 1 }, { 2, 1 }, { 2, 2 }, { 3, 3 }, { 5, 9 }, { 7, 7 }, { 8, 8 }, { 10, 10 }, { 11, 10 }, { 14, 12 }, { 20, 16 }, { 26, 65 }, { 30, 30 }, { 33, 30 }, { 40, 40 }, { 8, 15 }, { 8, 10 }, { 9, 9 }, { 25, 20 }, { 30, 30 }, { 30, 80 }, { 36, 35 }, { 38, 66 }, { 16, 16 }, { 22, 60 }, { 26, 35 }, { 29, 30 }, { 33, 30 }, { 39, 100 }, { 41, 85 }, { 44, 75 } }, /* High Mage: Trump Magic */ { { 1, 1 }, { 2, 2 }, { 4, 4 }, { 5, 5 }, { 6, 5 }, { 7, 7 }, { 10, 10 }, { 14, 12 }, { 16, 16 }, { 20, 20 }, { 25, 22 }, { 28, 24 }, { 28, 26 }, { 30, 28 }, { 35, 30 }, { 39, 36 }, { 11, 11 }, { 21, 21 }, { 23, 23 }, { 25, 25 }, { 31, 65 }, { 36, 90 }, { 39, 45 }, { 42, 90 }, { 25, 25 }, { 32, 45 }, { 34, 75 }, { 36, 75 }, { 38, 90 }, { 42, 90 }, { 44, 90 }, { 46, 90 } }, /* High Mage: Arcane Magic */ { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 4 }, { 5, 4 }, { 6, 5 }, { 7, 7 }, { 8, 7 }, { 8, 8 }, { 9, 8 }, { 10, 9 }, { 10, 10 }, { 11, 10 }, { 12, 10 }, { 13, 10 }, { 14, 11 }, { 15, 12 }, { 17, 15 }, { 20, 15 }, { 20, 16 }, { 22, 18 }, { 24, 22 }, { 30, 30 }, { 33, 28 }, { 38, 28 }, { 40, 28 }, { 41, 28 }, { 43, 40 }, { 46, 80 } } } }, }; /* * Zangband uses this array instead of the spell flags table, as there * are 5 realms of magic, each with 4 spellbooks and 8 spells per book -- TY */ const u32b fake_spell_flags[4]= { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; const byte realm_choices1[] = { (CH_NONE), /* Warrior */ (CH_LIFE | CH_SORCERY | CH_NATURE | CH_CHAOS | CH_DEATH | CH_TRUMP | CH_ARCANE), /* Mage */ (CH_LIFE | CH_DEATH), /* Priest */ (CH_SORCERY | CH_DEATH | CH_TRUMP | CH_ARCANE), /* Rogue */ (CH_NATURE), /* Ranger */ (CH_LIFE | CH_DEATH), /* Paladin */ (CH_ARCANE), /* Warrior-Mage */ (CH_CHAOS), /* Chaos-Warrior */ (CH_LIFE | CH_NATURE | CH_DEATH), /* Monk */ (CH_NONE), /* Mindcrafter */ (CH_LIFE | CH_SORCERY | CH_NATURE | CH_CHAOS | CH_DEATH | CH_TRUMP | CH_ARCANE), /* High-Mage */ }; const byte realm_choices2[] = { (CH_NONE), /* Warrior */ (CH_LIFE | CH_SORCERY | CH_NATURE | CH_CHAOS | CH_DEATH | CH_TRUMP | CH_ARCANE), /* Mage */ (CH_SORCERY | CH_NATURE | CH_CHAOS | CH_TRUMP | CH_ARCANE), /* Priest */ (CH_NONE), /* Rogue */ (CH_SORCERY | CH_CHAOS | CH_DEATH | CH_TRUMP | CH_ARCANE), /* Ranger */ (CH_NONE), /* Paladin */ (CH_LIFE | CH_NATURE | CH_CHAOS | CH_DEATH | CH_TRUMP | CH_ARCANE | CH_SORCERY), /* Warrior-Mage */ (CH_NONE), /* Chaos-Warrior */ (CH_NONE), /* Monk */ (CH_NONE), /* Mindcrafter */ (CH_NONE), /* High-Mage */ }; cptr realm_names[] = { "no magic", "Life", "Sorcery", "Nature", "Chaos", "Death", "Trump", "Arcane", "unknown" }; /* * Names of the spells (mage spells then priest spells) */ cptr spell_names[7][32] = { /*** Life Spells ***/ { /* Common Life Spellbooks */ "Detect Evil", "Cure Light Wounds", "Bless", "Remove Fear", "Call Light", "Detect Traps and Secret Doors", "Cure Medium Wounds", "Satisfy Hunger", "Remove Curse", "Cure Poison", "Cure Critical Wounds", "Sense Unseen", "Holy Orb", "Protection from Evil", "Healing", "Glyph of Warding", /* Rare Life Spellbooks */ "Exorcism", "Dispel Curse", "Dispel Undead & Demons", "Day of the Dove", "Dispel Evil", "Banish", "Holy Word", "Warding True", "Heroism", "Prayer", "Bless Weapon", "Restoration", "Healing True", "Holy Vision", "Divine Intervention", "Holy Invulnerability" }, /*** Sorcery Spells ***/ { /* Common Sorcery Spellbooks */ "Detect Monsters", "Phase Door", "Detect Doors and Traps", "Light Area", "Confuse Monster", "Teleport", "Sleep Monster", "Recharging", "Magic Mapping", "Identify", "Slow Monster", "Mass Sleep", "Teleport Away", "Haste Self", "Detection True", "Identify True", /* Rare Sorcery Spellbooks */ "Detect Objects and Treasure", "Detect Enchantment", "Charm Monster", "Dimension Door", "Sense Minds", "Self Knowledge", "Teleport Level", "Word of Recall", "Stasis", "Telekinesis", "Explosive Rune", "Clairvoyance", "Enchant Weapon", "Enchant Armour", "Alchemy", "Globe of Invulnerability" }, /*** Nature Spellbooks ***/ { /* Common Nature Spellbooks */ "Detect Creatures", "First Aid", "Detect Doors and Traps", "Foraging", "Daylight", "Animal Taming", "Resist Environment", "Cure Wounds & Poison", "Stone to Mud", "Lightning Bolt", "Nature Awareness", "Frost Bolt", "Ray of Sunlight", "Entangle", "Summon Animal", "Herbal Healing", /* Rare Nature Spellbooks */ "Door Building", "Stair Building", "Stone Skin", "Resistance True", "Animal Friendship", "Stone Tell", "Wall of Stone", "Protect from Corrosion", "Earthquake", "Whirlwind Attack", "Blizzard", "Lightning Storm", "Whirlpool", "Call Sunlight", "Elemental Branding", "Nature's Wrath" }, /*** Chaos Spells ***/ { /* Common Chaos Spellbooks */ "Magic Missile", "Trap / Door Destruction", "Flash of Light", "Touch of Confusion", "Mana Burst", "Fire Bolt", "Fist of Force", "Teleport Self", "Wonder", "Chaos Bolt", "Sonic Boom", "Doom Bolt", "Fire Ball", "Teleport Other", "Word of Destruction", "Invoke Logrus", /* Rare Chaos Spellbooks */ "Polymorph Other", "Chain Lightning", "Arcane Binding", "Disintegrate", "Alter Reality", "Polymorph Self", "Chaos Branding", "Summon Demon", "Beam of Gravity", "Meteor Swarm", "Flame Strike", "Call Chaos", "Magic Rocket", "Mana Storm", "Breathe Logrus", "Call the Void" }, /*** Death Spells ***/ { /* Common Death Spellbooks */ "Detect Unlife", "Malediction", "Detect Evil", "Stinking Cloud", "Black Sleep", "Resist Poison", "Horrify", "Enslave Undead", "Orb of Entropy", "Nether Bolt", "Terror", "Vampiric Drain", "Poison Branding", "Dispel Good", "Genocide", "Restore Life", /* Rare Death Spellbooks */ "Berserk", "Invoke Spirits", "Dark Bolt", "Battle Frenzy", "Vampirism True", "Vampiric Branding", "Darkness Storm", "Mass Genocide", "Death Ray", "Raise the Dead", "Esoteria", "Word of Death", "Evocation", "Hellfire", "Omnicide", "Wraithform" }, /* Trump Spellbooks */ { /* Common Trump Spellbooks */ "Phase Door", "Mind Blast", "Shuffle", "Reset Recall", "Teleport", "Dimension Door", "Trump Spying", "Teleport Away", "Trump Reach", "Trump Animal", "Phantasmal Servant", "Trump Monster", "Conjure Elemental", "Teleport Level", "Word of Recall", "Banish", /* Rare Trump Spellbooks */ "Joker Card", "Trump Spiders", "Trump Reptiles", "Trump Hounds", "Trump Branding", "Living Trump", "Death Dealing", "Trump Cyberdemon", "Trump Divination", "Trump Lore", "Trump Undead", "Trump Dragon", "Mass Trump", "Trump Demon", "Trump Ancient Dragon", "Trump Greater Undead" }, /* Arcane Spellbooks (_only_ common spells) */ { "Zap", "Wizard Lock", "Detect Invisibility", "Detect Monsters", "Blink", "Light Area", "Trap & Door Destruction", "Cure Light Wounds", "Detect Doors & Traps", "Phlogiston", "Detect Treasure", "Detect Enchantment", "Detect Objects", "Cure Poison", "Resist Cold", "Resist Fire", "Resist Lightning", "Resist Acid", "Cure Medium Wounds", "Teleport", "Stone to Mud", "Ray of Light", "Satisfy Hunger", "See Invisible", "Recharging", "Teleport Level", "Identify", "Teleport Away", "Elemental Ball", "Detection", "Word of Recall", "Clairvoyance" } }; /* * Each chest has a certain set of traps, determined by pval * Each chest has a "pval" from 1 to the chest level (max 55) * If the "pval" is negative then the trap has been disarmed * The "pval" of a chest determines the quality of its treasure * Note that disarming a trap on a chest also removes the lock. */ const byte chest_traps[64] = { 0, /* 0 == empty */ (CHEST_POISON), (CHEST_LOSE_STR), (CHEST_LOSE_CON), (CHEST_LOSE_STR), (CHEST_LOSE_CON), /* 5 == best small wooden */ 0, (CHEST_POISON), (CHEST_POISON), (CHEST_LOSE_STR), (CHEST_LOSE_CON), (CHEST_POISON), (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_SUMMON), /* 15 == best large wooden */ 0, (CHEST_LOSE_STR), (CHEST_LOSE_CON), (CHEST_PARALYZE), (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_SUMMON), (CHEST_PARALYZE), (CHEST_LOSE_STR), (CHEST_LOSE_CON), (CHEST_EXPLODE), /* 25 == best small iron */ 0, (CHEST_POISON | CHEST_LOSE_STR), (CHEST_POISON | CHEST_LOSE_CON), (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_PARALYZE), (CHEST_POISON | CHEST_SUMMON), (CHEST_SUMMON), (CHEST_EXPLODE), (CHEST_EXPLODE | CHEST_SUMMON), /* 35 == best large iron */ 0, (CHEST_SUMMON), (CHEST_EXPLODE), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_POISON | CHEST_PARALYZE), (CHEST_EXPLODE), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_POISON | CHEST_PARALYZE), /* 45 == best small steel */ 0, (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_POISON | CHEST_PARALYZE | CHEST_LOSE_STR), (CHEST_POISON | CHEST_PARALYZE | CHEST_LOSE_CON), (CHEST_POISON | CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_POISON | CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_POISON | CHEST_PARALYZE | CHEST_LOSE_STR | CHEST_LOSE_CON), (CHEST_POISON | CHEST_PARALYZE), (CHEST_POISON | CHEST_PARALYZE), /* 55 == best large steel */ (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), (CHEST_EXPLODE | CHEST_SUMMON), }; /* * Class titles for the player. * * The player gets a new title every five levels, so each class * needs only ten titles total. */ cptr player_title[MAX_CLASS][PY_MAX_LEVEL / 5] = { /* Warrior */ { "Rookie", "Soldier", "Mercenary", "Veteran", "Swordsman", "Champion", "Hero", "Baron", "Duke", "Lord", }, /* Mage */ { "Apprentice", "Trickster", "Illusionist", "Spellbinder", "Evoker", "Conjurer", "Warlock", "Sorcerer", "Ipsissimus", "Archimage", }, /* Priest */ { "Believer", "Acolyte", "Adept", "Curate", "Canon", "Priest", "High Priest", "Cardinal", "Inquisitor", "Pope", }, /* Rogues */ { "Cutpurse", "Robber", "Burglar", "Filcher", "Sharper", "Low Thief", "High Thief", "Master Thief", "Assassin", "Guildmaster", }, /* Rangers */ { "Runner", "Strider", "Scout", "Courser", "Tracker", "Guide", "Pathfinder", "Low Ranger", "High Ranger", "Ranger Lord", }, /* Paladins */ { "Gallant", "Keeper", "Protector", "Defender", "Warder", "Knight", "Guardian", "Low Paladin", "High Paladin", "Paladin Lord", }, /* Warrior-Mage */ { "Novice", "Apprentice", "Journeyman", "Veteran", "Enchanter", "Champion", "Mage-Hero", "Baron Mage", "Battlemage", "Wizard Lord", }, /* Chaos Warrior */ { "Rookie", "Soldier", "Mercenary", "Veteran", "Swordsman", "Champion", "Chaos Hero", "Chaos Baron", "Chaos Duke", "Chaos Lord", }, /* Monk */ { "Initiate", "Brother", "Disciple", "Immaculate", "Master", "Soft Master", "Hard Master", "Flower Master", "Dragon Master", "Grand Master", }, /* Mindcrafter */ { "Trainee", "Acolyte", "Adept", "Immaculate", "Contemplator", "Mentalist", "Psychic", "Psionicist", "Esper", "Mindmaster", }, /* High Mage; same as Mage */ { "Apprentice", "Trickster", "Illusionist", "Spellbinder", "Evoker", "Conjurer", "Warlock", "Sorcerer", "Ipsissimus", "Archimage", }, }; /* * Hack -- the "basic" color names (see "TERM_xxx") */ cptr color_names[16] = { "Dark", "White", "Slate", "Orange", "Red", "Green", "Blue", "Umber", "Light Dark", "Light Slate", "Violet", "Yellow", "Light Red", "Light Green", "Light Blue", "Light Umber", }; /* * Table of colour escape sequences for string formatting */ cptr color_seq[16] = { CLR_DARK, CLR_WHITE, CLR_SLATE, CLR_ORANGE, CLR_RED, CLR_GREEN, CLR_BLUE, CLR_UMBER, CLR_L_DARK, CLR_L_WHITE, CLR_VIOLET, CLR_YELLOW, CLR_L_RED, CLR_L_GREEN, CLR_L_BLUE, CLR_L_UMBER, }; /* * Hack -- the message colour names (MSG_* in defines.h) * Several are unused, although the corresponding sounds are not. * ToDo: Use colours where appropriate (eg where sounds are used). */ cptr msg_names[MSG_MAX] = { "Generic", "Hit monster", "Miss monster", "Monster flees", "Monster drop (unused)", "Monster kill", "Gain level", "Player death", "Spell/prayer gained", "Teleport (unused)", "Shoot (unused)", "Quaff (unused)", "Activate artifact", "Walk (unused)", "Teleport other (unused)", "Hit wall", "Eat (unused)", "Sell worthless (unidentified) item (unused)", "Sell poor (unidentified) item (unused)", "Sell good (unidentified) item (unused)", "Sell excellent (unidentified) item (unused)", "Dig (unused)", "Pick chest", /* sound is for opening doors */ "Shut door", "Teleport level", /* sound also for recall */ "Error", "Nothing to open", "Fail to pick lock", "Use stairs", "Hitpoint warning" }; /* * Abbreviations of healthy stats */ cptr stat_names[A_MAX] = { "STR: ", "INT: ", "WIS: ", "DEX: ", "CON: ", "CHR: " }; /* * Abbreviations of damaged stats */ cptr stat_names_reduced[A_MAX] = { "Str: ", "Int: ", "Wis: ", "Dex: ", "Con: ", "Chr: " }; /* * Certain "screens" always use the main screen, including News, Birth, * Dungeon, Tomb-stone, High-scores, Macros, Colors, Visuals, Options. * * Later, special flags may allow sub-windows to "steal" stuff from the * main window, including File dump (help), File dump (artifacts, uniques), * Character screen, Small scale map, Previous Messages, Store screen, etc. * * The "ctrl-i" (tab) command flips the "Display inven/equip" and "Display * equip/inven" flags for all windows. * * The "ctrl-g" command (or pseudo-command) should perhaps grab a snapshot * of the main screen into any interested windows. */ cptr window_flag_desc[WINDOW_CHOICE_MAX] = { "Display inven/equip", "Display equip/inven", "Display spell list", "Display character", "Display script variables", "Display script source", "Display messages", "Display overhead view", "Display monster recall", "Display object recall", "Display dungeon view", "Display snap-shot", "Display visible monsters", "Display borg messages", "Display borg status", }; /* * Available Options */ option_type option_info[OPT_MAX] = { {FALSE, 1, "rogue_like_commands", "Rogue-like commands" }, {TRUE, 1, "quick_messages", "Activate quick messages" }, {TRUE, 0, NULL, "Number 2" }, {TRUE, 1, "carry_query_flag", "Prompt before picking things up" }, {FALSE, 1, "use_old_target", "Use old target by default" }, {TRUE, 1, "always_pickup", "Pick things up by default" }, {TRUE, 0, NULL, "Number 6" }, {FALSE, 5, "depth_in_feet", "Show dungeon level in feet" }, {TRUE, 0, NULL, "Number 8" }, {FALSE, 0, NULL, "Number 9" }, {TRUE, 5, "show_labels", "Show labels in object listings" }, {TRUE, 5, "show_weights", "Show weights in object listings" }, {FALSE, 8, "view_monster_grids", "Map remembers monster-lit grids" }, {FALSE, 1, "toggle_xp", "Reverse experience display" }, {FALSE, 2, "ring_bell", "Audible bell (on errors, etc)" }, {TRUE, 5, "use_color", "Use color if possible (slow)" }, {FALSE, 2, "find_ignore_stairs", "Run past stairs" }, {TRUE, 2, "find_ignore_doors", "Run through open doors" }, {FALSE, 2, "find_cut", "Run past known corners" }, {TRUE, 2, "find_examine", "Run into potential corners" }, {TRUE, 2, "disturb_view", "Disturb whenever new monster is seen" }, {TRUE, 2, "disturb_near", "Disturb whenever viewable monster moves" }, {TRUE, 2, "disturb_panel", "Disturb whenever map panel changes" }, {TRUE, 2, "disturb_state", "Disturb whenever player state changes" }, {TRUE, 2, "disturb_minor", "Disturb whenever boring things happen" }, {TRUE, 2, "disturb_other", "Disturb whenever random things happen" }, {TRUE, 2, "disturb_traps", "Disturb when you leave detection radius" }, {FALSE, 2, "auto_more", "Automatically clear '-more-' prompts" }, {TRUE, 3, "last_words", "Get last words when the character dies" }, {TRUE, 3, "speak_unique", "Allow uniques to speak" }, {TRUE, 3, "small_levels", "Allow unusually small dungeon levels" }, {TRUE, 0, NULL, "Number 31" }, {TRUE, 0, NULL, "Number 32" }, {TRUE, 0, NULL, "Number 33" }, {TRUE, 0, NULL, "Number 34" }, {TRUE, 0, NULL, "Number 35" }, {TRUE, 0, NULL, "Number 36" }, {TRUE, 1, "expand_list", "Expand the power of the list commands" }, {TRUE, 0, NULL, "Number 38" }, {FALSE, 3, "view_torch_grids", "Map remembers all torch-lit grids" }, {TRUE, 0, NULL, "Number 40" }, {TRUE, 3, "dungeon_stair", "Generate dungeons with connected stairs" }, {TRUE, 0, NULL, "Number 42" }, {TRUE, 0, NULL, "Number 43" }, {TRUE, 0, NULL, "Number 44" }, {TRUE, 0, NULL, "Number 45" }, {TRUE, 7, "smart_packs", "Pack monsters use new AI" }, {FALSE, 0, NULL, "Number 47" }, {FALSE, 0, NULL, "Number 48" }, {FALSE, 0, NULL, "Number 49" }, {FALSE, 0, NULL, "Number 50" }, {FALSE, 0, NULL, "Number 51" }, {TRUE, 4, "flush_failure", "Flush input on various failures" }, {FALSE, 4, "flush_disturb", "Flush input whenever disturbed" }, {FALSE, 0, NULL, "Number 54" }, {TRUE, 4, "fresh_before", "Flush output before every command" }, {FALSE, 4, "fresh_after", "Flush output after every command" }, {FALSE, 2, "emergency_stop", "Halt all input after hp warning" }, /* {FALSE, 0, NULL, "Number 57" },*/ {TRUE, 4, "compress_savefile", "Compress messages in savefiles" }, {TRUE, 5, "hilite_player", "Hilite the player with the cursor" }, {FALSE, 5, "view_yellow_lite", "Use special colors for torch-lit grids" }, {FALSE, 5, "view_bright_lite", "Use special colors for 'viewable' grids" }, {FALSE, 5, "view_granite_lite", "Use special colors for wall grids (slow)" }, {FALSE, 5, "view_special_lite", "Use special colors for floor grids (slow)" }, {TRUE, 5, "view_player_colour", "Use special colours for the player" }, {TRUE, 0, NULL, "Number 65" }, {TRUE, 0, NULL, "Number 66" }, {TRUE, 0, NULL, "Number 67" }, {TRUE, 0, NULL, "Number 68" }, {TRUE, 0, NULL, "Number 69" }, {TRUE, 0, NULL, "Number 70" }, {TRUE, 0, NULL, "Number 71" }, {TRUE, 0, NULL, "Number 72" }, {TRUE, 0, NULL, "Number 73" }, {TRUE, 0, NULL, "Number 74" }, {TRUE, 0, NULL, "Number 75" }, {TRUE, 0, NULL, "Number 76" }, {TRUE, 0, NULL, "Number 77" }, {TRUE, 0, NULL, "Number 78" }, {TRUE, 0, NULL, "Number 79" }, {TRUE, 0, NULL, "Number 80" }, {TRUE, 0, NULL, "Number 81" }, {TRUE, 0, NULL, "Number 82" }, {TRUE, 0, NULL, "Number 83" }, {TRUE, 0, NULL, "Number 84" }, {TRUE, 0, NULL, "Number 85" }, {TRUE, 0, NULL, "Number 86" }, {TRUE, 0, NULL, "Number 87" }, {TRUE, 0, NULL, "Number 88" }, {TRUE, 0, NULL, "Number 89" }, {TRUE, 0, NULL, "Number 90" }, {TRUE, 0, NULL, "Number 91" }, {TRUE, 0, NULL, "Number 92" }, {TRUE, 0, NULL, "Number 93" }, {TRUE, 0, NULL, "Number 94" }, {TRUE, 0, NULL, "Number 95" }, {TRUE, 0, NULL, "Number 96" }, {TRUE, 0, NULL, "Number 97" }, {TRUE, 0, NULL, "Number 98" }, {TRUE, 0, NULL, "Number 99" }, {TRUE, 0, NULL, "Number 100" }, {TRUE, 0, NULL, "Number 101" }, {TRUE, 0, NULL, "Number 102" }, {TRUE, 0, NULL, "Number 103" }, {TRUE, 0, NULL, "Number 104" }, {TRUE, 0, NULL, "Number 105" }, {TRUE, 0, NULL, "Number 106" }, {TRUE, 0, NULL, "Number 107" }, {TRUE, 0, NULL, "Number 108" }, {TRUE, 0, NULL, "Number 109" }, {TRUE, 0, NULL, "Number 110" }, {TRUE, 0, NULL, "Number 111" }, {TRUE, 0, NULL, "Number 112" }, {TRUE, 0, NULL, "Number 113" }, {TRUE, 0, NULL, "Number 114" }, {TRUE, 0, NULL, "Number 115" }, {TRUE, 0, NULL, "Number 116" }, {TRUE, 0, NULL, "Number 117" }, {TRUE, 0, NULL, "Number 118" }, {TRUE, 0, NULL, "Number 119" }, {TRUE, 0, NULL, "Number 120" }, {TRUE, 0, NULL, "Number 121" }, {TRUE, 0, NULL, "Number 122" }, {TRUE, 0, NULL, "Number 123" }, {TRUE, 0, NULL, "Number 124" }, {TRUE, 0, NULL, "Number 125" }, {TRUE, 0, NULL, "Number 126" }, {TRUE, 0, NULL, "Number 127" }, {TRUE, 0, NULL, "Number 128" }, {TRUE, 0, NULL, "Number 129" }, {TRUE, 0, NULL, "Number 130" }, {TRUE, 0, NULL, "Number 131" }, {TRUE, 0, NULL, "Number 132" }, {TRUE, 0, NULL, "Number 133" }, {TRUE, 0, NULL, "Number 134" }, {TRUE, 0, NULL, "Number 135" }, {TRUE, 0, NULL, "Number 136" }, {TRUE, 0, NULL, "Number 137" }, {TRUE, 0, NULL, "Number 138" }, {TRUE, 0, NULL, "Number 139" }, {TRUE, 0, NULL, "Number 140" }, {TRUE, 0, NULL, "Number 141" }, {TRUE, 0, NULL, "Number 142" }, {TRUE, 0, NULL, "Number 143" }, {TRUE, 0, NULL, "Number 144" }, {TRUE, 0, NULL, "Number 145" }, {TRUE, 0, NULL, "Number 146" }, {TRUE, 0, NULL, "Number 147" }, {TRUE, 0, NULL, "Number 148" }, {TRUE, 0, NULL, "Number 149" }, {TRUE, 0, NULL, "Number 150" }, {TRUE, 0, NULL, "Number 151" }, {TRUE, 0, NULL, "Number 152" }, {TRUE, 0, NULL, "Number 153" }, {TRUE, 0, NULL, "Number 154" }, {TRUE, 0, NULL, "Number 155" }, {TRUE, 0, NULL, "Number 156" }, {TRUE, 0, NULL, "Number 157" }, {TRUE, 0, NULL, "Number 158" }, {TRUE, 0, NULL, "Number 159" }, {TRUE, 0, NULL, "Number 160" }, {FALSE, 5, "plain_descriptions", "Plain object descriptions" }, {FALSE, 6, "stupid_monsters", "Monsters behave stupidly" }, {FALSE, 1, "auto_destroy", "No query to destroy known worthless items" }, {FALSE, 1, "confirm_wear", "Confirm to wear/wield known cursed items" }, {FALSE, 0, NULL, "Number 165" }, {TRUE, 1, "easy_open", "Automatically open doors" }, {TRUE, 1, "easy_disarm", "Automatically disarm traps" }, {FALSE, 1, "easy_floor", "Display floor stacks in a list" }, {TRUE, 0, NULL, "Number 169" }, {FALSE, 5, "center_player", "Always center on the player (*slow*)" }, {FALSE, 5, "avoid_center", "Avoid centering while running" }, {TRUE, 0, NULL, "Number 172" }, {TRUE, 5, "limit_messages", "Only display last 50 messages in dumps" }, {FALSE, 1, "check_transaction", "Prompt when buying or selling" }, {TRUE, 0, NULL, "Number 175" }, {TRUE, 0, NULL, "Number 176" }, {TRUE, 0, NULL, "Number 177" }, {TRUE, 0, NULL, "Number 178" }, {TRUE, 0, NULL, "Number 179" }, {TRUE, 0, NULL, "Number 180" }, {TRUE, 0, NULL, "Number 181" }, {TRUE, 0, NULL, "Number 182" }, {TRUE, 0, NULL, "Number 183" }, {TRUE, 0, NULL, "Number 184" }, {TRUE, 0, NULL, "Number 185" }, {TRUE, 0, NULL, "Number 186" }, {TRUE, 0, NULL, "Number 187" }, {TRUE, 0, NULL, "Number 188" }, {TRUE, 0, NULL, "Number 189" }, {TRUE, 0, NULL, "Number 190" }, {TRUE, 0, NULL, "Number 191" }, {FALSE, 6, "vanilla_town", "Use 'vanilla' town without quests and wilderness" }, {TRUE, 0, NULL, "Number 193" }, {FALSE, 6, "ironman_shops", "Stores are permanently closed" }, {FALSE, 6, "ironman_small_levels", "Always create unusually small dungeon levels" }, {FALSE, 6, "ironman_downward", "Don't allow climbing upwards/recalling" }, {TRUE, 0, NULL, "Number 197" }, {TRUE, 0, NULL, "Number 198" }, {TRUE, 0, NULL, "Number 199" }, {TRUE, 0, NULL, "Number 200" }, {TRUE, 0, NULL, "Number 201" }, {TRUE, 0, NULL, "Number 202" }, {FALSE, 6, "munchkin_death", "Ask for saving death" }, {TRUE, 0, NULL, "Number 204" }, {TRUE, 0, NULL, "Number 205" }, {TRUE, 6, "preserve_mode", "Preserve artifacts" }, {TRUE, 6, "autoroller", "Specify stat weightings" }, {FALSE, 6, "point_based", "Generate character using a point system" }, {TRUE, 6, "silly_monsters", "Allow silly monsters" }, {FALSE, 6, "ironman_nightmare", "Nightmare mode (this isn't even remotely fair!)" }, {TRUE, 0, NULL, "Number 211" }, {TRUE, 0, NULL, "Number 212" }, {TRUE, 0, NULL, "Number 213" }, {TRUE, 0, NULL, "Number 214" }, {TRUE, 0, NULL, "Number 215" }, {TRUE, 0, NULL, "Number 216" }, {TRUE, 0, NULL, "Number 217" }, {TRUE, 0, NULL, "Number 218" }, {TRUE, 0, NULL, "Number 219" }, {TRUE, 0, NULL, "Number 220" }, {TRUE, 0, NULL, "Number 221" }, {TRUE, 0, NULL, "Number 222" }, {TRUE, 0, NULL, "Number 223" }, {FALSE, 0, NULL, "Number 224" }, {FALSE, 5, "monster_light", "Allow monsters to carry lights" }, {TRUE, 0, NULL, "Turn on muliplayer client - server code" }, {TRUE, 0, NULL, "Number 227" }, {TRUE, 0, NULL, "Number 228" }, {TRUE, 0, NULL, "Number 229" }, {TRUE, 0, NULL, "Number 230" }, {TRUE, 0, NULL, "Number 231" }, {TRUE, 0, NULL, "Number 232" }, {TRUE, 0, NULL, "Number 233" }, {TRUE, 0, NULL, "Number 234" }, {TRUE, 0, NULL, "Number 235" }, {TRUE, 0, NULL, "Number 236" }, {TRUE, 0, NULL, "Number 237" }, {TRUE, 0, NULL, "Number 238" }, {TRUE, 0, NULL, "Number 239" }, {TRUE, 0, NULL, "Number 240" }, {TRUE, 0, NULL, "Number 241" }, {TRUE, 0, NULL, "Number 242" }, {TRUE, 0, NULL, "Number 243" }, {TRUE, 0, NULL, "Number 244" }, {TRUE, 0, NULL, "Number 245" }, {TRUE, 0, NULL, "Number 246" }, {TRUE, 0, NULL, "Number 247" }, {TRUE, 0, NULL, "Number 248" }, {TRUE, 0, NULL, "Number 249" }, {TRUE, 0, NULL, "Number 250" }, {FALSE, 3, "auto_notes", "Automatically note important events" }, {FALSE, 3, "take_notes", "Allow notes to be appended to a file" }, {TRUE, 0, NULL, "Number 253" }, {TRUE, 8, "testing_stack", "Allow objects to stack on floor" }, {TRUE, 0, NULL, NULL }, }; const int birth_options[OPT_BIRTH + 1] = { 162, 192, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 0 }; const int server_options[OPT_SERVER + 1] = { 30, 31, 33, 34, 35, 36, 37, 40, 41, 42, 43, 44, 45, 46, 47, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 254, 255, 0 }; cptr chaos_patrons[MAX_PATRON] = { "Slortar", "Mabelode", "Chardros", "Hionhurn", "Xiombarg", "Pyaray", "Balaan", "Arioch", "Eequor", "Narjhan", "Balo", "Khorne", "Slaanesh", "Nurgle", "Tzeentch", "Khaine" }; const int chaos_stats[MAX_PATRON] = { A_CON, /* Slortar */ A_CON, /* Mabelode */ A_STR, /* Chardros */ A_STR, /* Hionhurn */ A_STR, /* Xiombarg */ A_INT, /* Pyaray */ A_STR, /* Balaan */ A_INT, /* Arioch */ A_CON, /* Eequor */ A_CHR, /* Narjhan */ -1, /* Balo */ A_STR, /* Khorne */ A_CHR, /* Slaanesh */ A_CON, /* Nurgle */ A_INT, /* Tzeentch */ A_STR, /* Khaine */ }; const int chaos_rewards[MAX_PATRON][20] = { /* Slortar the Old: */ { REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_LOSE_ABL, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_SLF, REW_GAIN_ABL, REW_GAIN_ABL, REW_GAIN_EXP, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_AUGM_ABL, REW_AUGM_ABL }, /* Mabelode the Faceless: */ { REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_H_SUMMON, REW_SUMMON_M, REW_SUMMON_M, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_WND, REW_POLY_SLF, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_ABL, REW_SER_UNDE, REW_CHAOS_WP, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_GOOD_OBS, REW_GOOD_OBS }, /* Chardros the Reaper: */ { REW_WRATH, REW_WRATH, REW_HURT_LOT, REW_PISS_OFF, REW_H_SUMMON, REW_SUMMON_M, REW_IGNORE, REW_IGNORE, REW_DESTRUCT, REW_SER_UNDE, REW_GENOCIDE, REW_MASS_GEN, REW_MASS_GEN, REW_DISPEL_C, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_AUGM_ABL, REW_AUGM_ABL }, /* Hionhurn the Executioner: */ { REW_WRATH, REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_IGNORE, REW_IGNORE, REW_SER_UNDE, REW_DESTRUCT, REW_GENOCIDE, REW_MASS_GEN, REW_MASS_GEN, REW_HEAL_FUL, REW_GAIN_ABL, REW_GAIN_ABL, REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_AUGM_ABL, REW_AUGM_ABL }, /* Xiombarg the Sword-Queen: */ { REW_TY_CURSE, REW_TY_CURSE, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_ABL, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_POLY_WND, REW_GENOCIDE, REW_DISPEL_C, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_SER_MONS, REW_GAIN_ABL, REW_CHAOS_WP, REW_GAIN_EXP, REW_AUGM_ABL, REW_GOOD_OBS }, /* Pyaray the Tentacled Whisperer of Impossible Secretes: */ { REW_WRATH, REW_TY_CURSE, REW_PISS_OFF, REW_H_SUMMON, REW_H_SUMMON, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_SLF, REW_POLY_SLF, REW_SER_DEMO, REW_HEAL_FUL, REW_GAIN_ABL, REW_GAIN_ABL, REW_CHAOS_WP, REW_DO_HAVOC, REW_GOOD_OBJ, REW_GREA_OBJ, REW_GREA_OBS }, /* Balaan the Grim: */ { REW_TY_CURSE, REW_HURT_LOT, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_SUMMON_M, REW_LOSE_EXP, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_SER_UNDE, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_EXP, REW_GAIN_EXP, REW_CHAOS_WP, REW_GOOD_OBJ, REW_GOOD_OBS, REW_GREA_OBS, REW_AUGM_ABL }, /* Arioch, Duke of Hell: */ { REW_WRATH, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_EXP, REW_H_SUMMON, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_MASS_GEN, REW_SER_DEMO, REW_HEAL_FUL, REW_CHAOS_WP, REW_CHAOS_WP, REW_GOOD_OBJ, REW_GAIN_EXP, REW_GREA_OBJ, REW_AUGM_ABL }, /* Eequor, Blue Lady of Dismay: */ { REW_WRATH, REW_TY_CURSE, REW_PISS_OFF, REW_CURSE_WP, REW_RUIN_ABL, REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_SER_MONS, REW_HEAL_FUL, REW_GAIN_EXP, REW_GAIN_ABL, REW_CHAOS_WP, REW_GOOD_OBS, REW_GREA_OBJ, REW_AUGM_ABL }, /* Narjhan, Lord of Beggars: */ { REW_WRATH, REW_CURSE_AR, REW_CURSE_WP, REW_CURSE_WP, REW_CURSE_AR, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_EXP, REW_AUGM_ABL, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_GREA_OBS }, /* Balo the Jester: */ { REW_WRATH, REW_SER_DEMO, REW_CURSE_WP, REW_CURSE_AR, REW_LOSE_EXP, REW_GAIN_ABL, REW_LOSE_ABL, REW_POLY_WND, REW_POLY_SLF, REW_IGNORE, REW_DESTRUCT, REW_MASS_GEN, REW_CHAOS_WP, REW_GREA_OBJ, REW_HURT_LOT, REW_AUGM_ABL, REW_RUIN_ABL, REW_H_SUMMON, REW_GREA_OBS, REW_AUGM_ABL }, /* Khorne the Bloodgod: */ { REW_WRATH, REW_HURT_LOT, REW_HURT_LOT, REW_H_SUMMON, REW_H_SUMMON, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_SER_MONS, REW_SER_DEMO, REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_GREA_OBJ, REW_GREA_OBS }, /* Slaanesh: */ { REW_WRATH, REW_PISS_OFF, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_ABL, REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_SER_DEMO, REW_POLY_SLF, REW_HEAL_FUL, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GAIN_EXP, REW_GAIN_EXP, REW_CHAOS_WP, REW_GAIN_ABL, REW_GREA_OBJ, REW_AUGM_ABL }, /* Nurgle: */ { REW_WRATH, REW_PISS_OFF, REW_HURT_LOT, REW_RUIN_ABL, REW_LOSE_ABL, REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GAIN_ABL, REW_GAIN_ABL, REW_SER_UNDE, REW_CHAOS_WP, REW_GREA_OBJ, REW_AUGM_ABL }, /* Tzeentch: */ { REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_LOSE_ABL, REW_LOSE_EXP, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_CHAOS_WP, REW_GREA_OBJ, REW_GAIN_ABL, REW_GAIN_ABL, REW_GAIN_EXP, REW_GAIN_EXP, REW_AUGM_ABL }, /* Khaine: */ { REW_WRATH, REW_HURT_LOT, REW_PISS_OFF, REW_LOSE_ABL, REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_DISPEL_C, REW_DO_HAVOC, REW_DO_HAVOC, REW_POLY_SLF, REW_POLY_SLF, REW_GAIN_EXP, REW_GAIN_ABL, REW_GAIN_ABL, REW_SER_MONS, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_GOOD_OBS } }; const martial_arts ma_blows[MAX_MA] = { #ifdef VERBOSE_MARTIAL_ARTS { "You punch %s.", 1, 0, 1, 4, 0 }, { "You kick %s.", 2, 0, 1, 5, 0 }, { "You strike %s.", 3, 0, 1, 6, 0 }, { "You hit %s with your knee.", 5, 5, 2, 3, MA_KNEE }, { "You hit %s with your elbow.", 7, 5, 1, 7, 0 }, { "You butt %s.", 9, 10, 2, 4, 0 }, { "You kick %s.", 11, 10, 2, 5, MA_SLOW }, { "You uppercut %s.", 13, 12, 3, 5, 6 }, { "You double-kick %s.", 16, 15, 6, 3, 8 }, { "You hit %s with a Cat's Claw.", 20, 20, 4, 6, 0 }, { "You hit %s with a jump kick.", 25, 25, 4, 7, 10 }, { "You hit %s with an Eagle's Claw.", 29, 25, 5, 6, 0 }, { "You hit %s with a circle kick.", 33, 30, 5, 8, 10 }, { "You hit %s with an Iron Fist.", 37, 35, 6, 8, 10 }, { "You hit %s with a flying kick.", 41, 35, 7, 8, 12 }, { "You hit %s with a Dragon Fist.", 45, 35, 7, 10, 16 }, { "You hit %s with a Crushing Blow.", 48, 35, 7, 12, 18 }, #else { "You punch %s.", 1, 0, 1, 4, 0 }, { "You kick %s.", 2, 0, 1, 5, 0 }, { "You strike %s.", 3, 0, 1, 6, 0 }, { "You knee %s.", 5, 5, 2, 3, MA_KNEE }, { "You hit %s.", 7, 5, 1, 7, 0 }, { "You butt %s.", 9, 10, 2, 4, 0 }, { "You kick %s.", 11, 10, 2, 5, MA_SLOW }, { "You uppercut %s.", 13, 12, 3, 5, 6 }, { "You double-kick %s.", 16, 15, 6, 3, 8 }, { "You hit %s.", 20, 20, 4, 6, 0 }, { "You kick %s.", 25, 25, 4, 7, 10 }, { "You hit %s.", 29, 25, 5, 6, 0 }, { "You kick %s.", 33, 30, 5, 8, 10 }, { "You punch %s.", 37, 35, 6, 8, 10 }, { "You kick %s.", 41, 35, 7, 8, 12 }, { "You punch %s.", 45, 35, 7, 10, 16 }, { "You punch %s.", 48, 35, 7, 12, 18 }, #endif }; /* * Table of game-generated inscriptions (indexed by the defines in * defines.h). -- RG */ cptr game_inscriptions[FEEL_MAX] = { NULL, /* FEEL_NONE */ "broken", /* FEEL_BROKEN */ "terrible", /* FEEL_TERRIBLE */ "worthless", /* FEEL_WORTHLESS */ "cursed", /* FEEL_CURSED */ "uncursed", /* FEEL_UNCURSED */ "average", /* FEEL_AVERAGE */ "good", /* FEEL_GOOD */ "excellent", /* FEEL_EXCELLENT */ "special", /* FEEL_SPECIAL */ "bad", /* FEEL_BAD */ "dubious", /* FEEL_DUBIOUS */ "tainted", /* FEEL_TAINTED */ }; /* Weird melee attack types when hallucinating */ cptr silly_attacks[MAX_SILLY_ATTACK] = { "smothers", "hugs", "humiliates", "whips", "kisses", "disgusts", "pees all over", "passes the gas on", "makes obscene gestures at", "licks", "stomps on", "swallows", "drools on", "misses", "shrinks", "emasculates", "evaporates", "solidifies", "digitizes", "insta-kills", "massacres", "slaughters", "drugs", "psychoanalyzes", "deconstructs", "falsifies", "disbelieves", "molests" }; /* Monster Blow-Method types */ const rbm_type rbm_info[MAX_RBM] = { { NULL, NULL, SOUND_NONE, FALSE, FALSE, FALSE }, { "hit", "hits %s.", SOUND_HIT, TRUE, TRUE, TRUE }, { "touch", "touches %s.", SOUND_TOUCH, TRUE, FALSE, FALSE }, { "punch", "punches %s.", SOUND_HIT, TRUE, FALSE, TRUE }, { "kick", "kicks %s.", SOUND_HIT, TRUE, FALSE, TRUE }, { "claw", "claws %s.", SOUND_CLAW, TRUE, TRUE, FALSE }, { "bite", "bites %s.", SOUND_BITE, TRUE, TRUE, FALSE }, { "sting", "stings %s.", SOUND_STING, TRUE, FALSE, FALSE }, { "XXX1","XXX1's %s.", SOUND_NONE, FALSE, FALSE, FALSE }, { "butt", "butts %s.", SOUND_HIT, TRUE, FALSE, TRUE }, { "crush", "crushes %s.", SOUND_CRUSH, TRUE, FALSE, TRUE }, { "engulf", "engulfs %s.", SOUND_CRUSH, TRUE, FALSE, FALSE }, { "charge", "charges %s.", SOUND_BUY, TRUE, FALSE, FALSE }, { "crawl on you", "crawls on %s.", SOUND_SLIME, TRUE, FALSE, FALSE }, { "drool on you", "drools on %s.", SOUND_SLIME, FALSE, FALSE, FALSE }, { "spit", "spits on %s.", SOUND_SLIME, FALSE, FALSE, FALSE }, { "explode", "explodes.", SOUND_NONE, FALSE, FALSE, FALSE }, { "gaze", "gazes at %s.", SOUND_NONE, FALSE, FALSE, FALSE }, { "wail", "wails at %s.", SOUND_WAIL, FALSE, FALSE, FALSE }, { "release spores", "releases spores at %s.", SOUND_SLIME, FALSE, FALSE, FALSE }, { "XXX4", "projects XXX4's at %s.", SOUND_NONE, FALSE, FALSE, FALSE }, { "beg", "begs %s for money.", SOUND_MOAN, FALSE, FALSE, FALSE }, { "insult", "insults %s.", SOUND_MOAN, FALSE, FALSE, FALSE }, { "moan", "moans at %s.", SOUND_MOAN, FALSE, FALSE, FALSE }, { "sing", "sings to %s.", SOUND_SHOW, FALSE, FALSE, FALSE } }; /* * The mutations: * * Actual mutation, * Description of mutation * Text on gaining mutation * Text on losing mutation * Short text (for choosing activatable mutations) * level, cost, stat, difficulty (each for activatable mutations only) * chance (random mutations only) */ const mutation_type mutations[MUT_SETS_MAX * MUT_PER_SET] = { /* Activatable mutations */ { MUT1_SPIT_ACID, "You can spit acid.", "You gain the ability to spit acid.", "You lose the ability to spit acid.", "Spit acid (dam lvl)", 9, 9, A_DEX, 15, 0 }, { MUT1_BR_FIRE, "You can breathe fire.", "You gain the ability to breathe fire.", "You lose the ability to breathe fire.", "Fire breath (lvl*2)", 20, 20, A_CON, 18, 0 }, { MUT1_HYPN_GAZE, "Your gaze is hypnotic (-1 WIS).", "Your eyes look mesmerizing...", "Your eyes look uninteresting.", "Hypnotic gaze", 12, 12, A_CHR, 18, 0 }, { MUT1_TELEKINES, "You are telekinetic (-1 CON).", "You gain the ability to move objects telekinetically.", "You lose the ability to move objects telekinetically.", "Telekinesis", 9, 9, A_WIS, 14, 0 }, { MUT1_VTELEPORT, "You can teleport at will.", "You gain the power of teleportation at will.", "You lose the power of teleportation at will.", "Teleport", 7, 7, A_WIS, 15, 0 }, { MUT1_MIND_BLST, "You can Mind Blast your enemies (-1 STR).", "You gain the power of Mind Blast.", "You lose the power of Mind Blast.", "Mind blast", 5, 3, A_WIS, 15, 0 }, { MUT1_RADIATION, "You can emit hard radiation at will (-1 CON).", "You start emitting hard radiation.", "You stop emitting hard radiation.", "Emit radiation", 15, 15, A_CON, 14, 0 }, { MUT1_VAMPIRISM, "You can drain life from a foe like a vampire.", "You become vampiric.", "You are no longer vampiric.", "Vampiric drain", 10, 10, A_CON, 9, 0 }, { MUT1_SMELL_MET, "You can smell nearby precious metal.", "You smell a metallic odor.", "You no longer smell a metallic odor.", "Smell metal", 3, 2, A_INT, 12, 0 }, { MUT1_SMELL_MON, "You can smell nearby monsters.", "You smell filthy monsters.", "You no longer smell filthy monsters.", "Smell monsters", 5, 4, A_INT, 15, 0 }, { MUT1_BLINK, "You can teleport yourself short distances.", "You gain the power of minor teleportation.", "You lose the power of minor teleportation.", "Blink", 3, 3, A_WIS, 12, 0 }, { MUT1_EAT_ROCK, "You can consume solid rock.", "The walls look delicious.", "The walls look unappetizing.", "Eat rock", 8, 12, A_CON, 18, 0 }, { MUT1_SWAP_POS, "You can switch locations with another being.", "You feel like walking a mile in someone else's shoes.", "You feel like staying in your own shoes.", "Swap position", 15, 12, A_DEX, 16, 0 }, { MUT1_SHRIEK, "You can emit a horrible shriek. (-1 CHR)", "Your vocal cords get much tougher.", "Your vocal cords get much weaker.", "Shriek", 20, 14, A_CON, 16, 0 }, { MUT1_ILLUMINE, "You can emit bright light (-2 stealth).", "You can light up rooms with your presence.", "You can no longer light up rooms with your presence.", "Illuminate area", 3, 2, A_INT, 10, 0 }, { MUT1_DET_CURSE, "You can feel the danger of evil magic.", "You can feel evil magics.", "You can no longer feel evil magics.", "Detect curses", 7, 14, A_WIS, 14, 0 }, { MUT1_BERSERK, "You can drive yourself into a berserk frenzy (-1 WIS).", "You feel a controlled rage.", "You no longer feel a controlled rage.", "Berserk", 8, 8, A_STR, 14, 0 }, { MUT1_POLYMORPH, "You can polymorph yourself at will.", "Your body seems mutable.", "Your body seems stable.", "Polymorph", 18, 20, A_CON, 18, 0 }, { MUT1_MIDAS_TCH, "You can turn ordinary items to gold (-1 WIS, -1 CHR).", "You gain the Midas touch.", "You lose the Midas touch.", "Midas touch", 10, 5, A_INT, 12, 0 }, { MUT1_GROW_MOLD, "You can cause mold to grow near you.", "You feel a sudden affinity for mold.", "You feel a sudden dislike for mold.", "Grow mold", 1, 6, A_CON, 14, 0 }, { MUT1_RESIST, "You can harden yourself to the ravages of the elements.", "You feel like you can protect yourself.", "You feel like you might be vulnerable.", "Resist elements", 10, 12, A_CON, 12, 0 }, { MUT1_EARTHQUAKE, "You can bring down the dungeon around your ears (-1 WIS).", "You gain the ability to wreck the dungeon.", "You lose the ability to wreck the dungeon.", "Earthquake", 12, 12, A_STR, 16, 0 }, { MUT1_EAT_MAGIC, "You can consume magic energy for your own use.", "Your magic items look delicious.", "Your magic items no longer look delicious.", "Eat magic", 17, 1, A_WIS, 15, 0 }, { MUT1_WEIGH_MAG, "You can feel the strength of the magics affecting you.", "You feel you can better understand the magic around you.", "You no longer sense magic.", "Weigh magic", 6, 6, A_INT, 10, 0 }, { MUT1_STERILITY, "You can cause mass impotence (-1 INT).", "You can give everything around you a headache.", "You hear a massed sigh of relief.", "Sterilize", 12, 23, A_CHR, 15, 0 }, { MUT1_PANIC_HIT, "You can run for your life after hitting something.", "You suddenly understand how thieves feel.", "You no longer feel jumpy.", "Panic hit", 10, 12, A_DEX, 14, 0 }, { MUT1_DAZZLE, "You can emit confusing, blinding radiation (-1 stealth).", "You gain the ability to emit dazzling lights.", "You lose the ability to emit dazzling lights.", "Dazzle", 7, 15, A_CHR, 8, 0 }, { MUT1_LASER_EYE, "Your eyes can fire laser beams (-2 sensing).", "Your eyes burn for a moment.", "Your eyes burn for a moment, then feel soothed.", "Laser eye", 7, 10, A_WIS, 9, 0 }, { MUT1_RECALL, "You can travel between town and the depths.", "You feel briefly homesick, but it passes.", "You feel briefly homesick.", "Recall", 17, 50, A_INT, 16, 0 }, { MUT1_BANISH, "You can send evil creatures directly to Hell.", "You feel a holy wrath fill you.", "You no longer feel a holy wrath.", "Banish evil", 25, 25, A_WIS, 18, 0 }, { MUT1_COLD_TOUCH, "You can freeze things with a touch (-1 DEX).", "Your hands get very cold.", "Your hands warm up.", "Cold touch", 2, 2, A_CON, 11, 0 }, { MUT1_LAUNCHER, "You can hurl objects with great force (-1 DEX).", "Your throwing arm feels much stronger.", "Your throwing arm feels much weaker.", "Throw object", 10, 15, A_STR, 6, 0 }, /* Randomly activating mutations */ { MUT2_BERS_RAGE, "You are subject to berserker fits.", "You become subject to fits of berserk rage!", "You are no longer subject to fits of berserk rage!", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_COWARDICE, "You are subject to cowardice.", "You become an incredible coward!", "You are no longer an incredible coward!", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_RTELEPORT, "You are teleporting randomly.", "Your position seems very uncertain...", "Your position seems more certain.", "(nothing)", 0, 0, 0, 0, 50 }, { MUT2_ALCOHOL, "Your body produces alcohol.", "Your body starts producing alcohol!", "Your body stops producing alcohol!", "(nothing)", 0, 0, 0, 0, 64 }, { MUT2_HALLU, "You have a hallucinatory insanity.", "You are afflicted by a hallucinatory insanity!", "You are no longer afflicted by a hallucinatory insanity!", "(nothing)", 0, 0, 0, 0, 64 }, { MUT2_FLATULENT, "You are subject to uncontrollable flatulence.", "You become subject to uncontrollable flatulence.", "You are no longer subject to uncontrollable flatulence.", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_SCOR_TAIL, "You have a scorpion tail (poison, 3d7, -3 CHR).", "You grow a scorpion tail!", "You lose your scorpion tail!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT2_HORNS, "You have horns (dam. 2d6).", "Horns pop forth into your forehead!", "Your horns vanish from your forehead!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT2_BEAK, "You have a beak (dam. 2d4).", "Your mouth turns into a sharp, powerful beak!", "Your mouth reverts to normal!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT2_ATT_DEMON, "You attract demons.", "You start attracting demons.", "You stop attracting demons.", "(nothing)", 0, 0, 0, 0, 66 }, { MUT2_PROD_MANA, "You are producing magical energy uncontrollably.", "You start producing magical energy uncontrollably.", "You stop producing magical energy uncontrollably.", "(nothing)", 0, 0, 0, 0, 90 }, { MUT2_SPEED_FLUX, "You move faster or slower randomly.", "You become manic-depressive.", "You are no longer manic-depressive.", "(nothing)", 0, 0, 0, 0, 60 }, { MUT2_BANISH_ALL, "You sometimes cause nearby creatures to vanish.", "You feel a terrifying power lurking behind you.", "You no longer feel a terrifying power lurking behind you.", "(nothing)", 0, 0, 0, 0, 90 }, { MUT2_EAT_LIGHT, "You sometimes feed off of the light around you.", "You feel a strange kinship with Ungoliant.", "You feel the world's a brighter place.", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_TRUNK, "You have an elephantine trunk (dam 1d4, -1 CHR).", "Your nose grows into an elephant-like trunk.", "Your nose returns to a normal length.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT2_ATT_ANIMAL, "You attract animals.", "You start attracting animals.", "You stop attracting animals.", "(nothing)", 0, 0, 0, 0, 70 }, { MUT2_TENTACLES, "You have evil looking tentacles (dam 2d5, +1 DEX, -2 CHR).", "Evil-looking tentacles sprout from your sides.", "Your tentacles vanish from your sides.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT2_RAW_CHAOS, "You occasionally are surrounded with raw chaos.", "You feel the universe is less stable around you.", "You feel the universe is more stable around you.", "(nothing)", 0, 0, 0, 0, 80 }, { MUT2_NORMALITY, "You may be mutated, but you're recovering.", "You feel strangely normal.", "You feel normally strange.", "(nothing)", 0, 0, 0, 0, 50 }, { MUT2_WRAITH, "You fade in and out of physical reality (-3 CON).", "You start to fade in and out of the physical world.", "You are firmly in the physical world.", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_POLY_WOUND, "Your health is subject to chaotic forces.", "You feel forces of chaos entering your old scars.", "You feel forces of chaos departing your old scars.", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_WASTING, "You have a horrible wasting disease.", "You suddenly contract a horrible wasting disease.", "You are cured of the horrible wasting disease!", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_ATT_DRAGON, "You attract dragons.", "You start attracting dragons.", "You stop attracting dragons.", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_WEIRD_MIND, "Your mind randomly expands and contracts.", "Your thoughts suddenly take off in strange directions.", "Your thoughts return to boring paths.", "(nothing)", 0, 0, 0, 0, 30 }, { MUT2_NAUSEA, "You have a seriously upset stomach.", "Your stomach starts to roil nauseously.", "Your stomach stops roiling.", "(nothing)", 0, 0, 0, 0, 90 }, { MUT2_CHAOS_GIFT, "Chaos deities give you gifts.", "You attract the notice of a chaos deity!", "You lose the attention of the chaos deities.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT2_WALK_SHAD, "You occasionally stumble into other shadows.", "You feel like reality is as thin as paper.", "You feel like you're trapped in reality.", "(nothing)", 0, 0, 0, 0, 120 }, { MUT2_WARNING, "You receive warnings about your foes.", "You suddenly feel paranoid.", "You no longer feel paranoid.", "(nothing)", 0, 0, 0, 0, 10 }, { MUT2_INVULN, "You occasionally feel invincible (-2 WIS).", "You are blessed with fits of invulnerability.", "You are no longer blessed with fits of invulnerability.", "(nothing)", 0, 0, 0, 0, 50 }, { MUT2_SP_TO_HP, "Your blood sometimes rushes to your muscles.", "You are subject to fits of magical healing.", "You are no longer subject to fits of magical healing.", "(nothing)", 0, 0, 0, 0, 20 }, { MUT2_HP_TO_SP, "Your blood sometimes rushes to your head.", "You are subject to fits of painful clarity.", "You are no longer subject to fits of painful clarity.", "(nothing)", 0, 0, 0, 0, 40 }, { MUT2_DISARM, "You occasionally stumble and drop things.", "Your feet grow to four times their former size.", "Your feet shrink to their former size.", "(nothing)", 0, 0, 0, 0, 100 }, /* Other Mutations */ { MUT3_HYPER_STR, "You are superhumanly strong (+4 STR, -1 INT, -1 WIS).", "You turn into a superhuman he-man!", "Your muscles revert to normal.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_PUNY, "You are puny (+2 DEX, -4 STR).", "Your muscles wither away...", "Your muscles revert to normal.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_HYPER_INT, "Your brain is a living computer (+4 INT/WIS).", "Your brain evolves into a living computer!", "Your brain reverts to normal.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_MORONIC, "You are moronic (-4 INT/WIS).", "Your brain withers away...", "Your brain reverts to normal.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_RESILIENT, "You are very resilient (+4 CON).", "You become extraordinarily resilient.", "You become ordinarily resilient again.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_XTRA_FAT, "You are extremely fat (+2 CON, -2 speed).", "You become sickeningly fat!", "You benefit from a miracle diet!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_ALBINO, "You are albino (-4 CON).", "You turn into an albino! You feel frail...", "You are no longer an albino!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_FLESH_ROT, "Your flesh is rotting (-2 CON, -1 CHR).", "Your flesh is afflicted by a rotting disease!", "Your flesh is no longer afflicted by a rotting disease!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_SILLY_VOI, "Your voice is a silly squeak (-4 CHR).", "Your voice turns into a ridiculous squeak!", "Your voice returns to normal.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_BLANK_FAC, "Your face is featureless (-1 CHR).", "Your face becomes completely featureless!", "Your facial features return.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_ILL_NORM, "Your appearance is masked with illusion.", "You start projecting a reassuring image.", "You stop projecting a reassuring image.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_XTRA_EYES, "You have an extra pair of eyes (+3 sensing, -1 CHR).", "You grow an extra pair of eyes!", "Your extra eyes vanish!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_MAGIC_RES, "You are resistant to magic.", "You become resistant to magic.", "You become susceptible to magic again.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_XTRA_NOIS, "You make a lot of strange noise (-3 stealth).", "You start making strange noise!", "You stop making strange noise!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_INFRAVIS, "You have remarkable infravision (+3).", "Your infravision is improved.", "Your infravision is degraded.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_XTRA_LEGS, "You have an extra pair of legs (+3 speed, -1 DEX).", "You grow an extra pair of legs!", "Your extra legs disappear!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_SHORT_LEG, "Your legs are short stubs (+1 CON, -3 speed).", "Your legs turn into short stubs!", "Your legs lengthen to normal.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_ELEC_TOUC, "Electricity is running through your veins (-1 CON).", "Electricity starts running through you!", "Electricity stops running through you.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_FIRE_BODY, "Your body is enveloped in flames (-1 DEX).", "Your body is enveloped in flames!", "Your body is no longer enveloped in flames.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_WART_SKIN, "Your skin is covered with warts (-2 CHR, +5 AC).", "Disgusting warts appear everywhere on you!", "Your warts disappear!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_SCALES, "Your skin has turned into scales (-1 CHR, +10 AC).", "Your skin turns into black scales!", "Your scales vanish!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_IRON_SKIN, "Your skin is made of steel (-3 DEX, +25 AC).", "Your skin turns to steel!", "Your skin reverts to flesh!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_WINGS, "You have wings (+3 CHR, -1 CON).", "You grow a pair of wings.", "Your wings fall off.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_FEARLESS, "You are completely fearless.", "You become completely fearless.", "You begin to feel fear again.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_REGEN, "You are regenerating.", "You start regenerating.", "You stop regenerating.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_ESP, "You are telepathic (-1 CON).", "You develop a telepathic ability!", "You lose your telepathic ability!", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_LIMBER, "Your body is very limber (+3 DEX, -1 STR).", "Your muscles become limber.", "Your muscles stiffen.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_ARTHRITIS, "Your joints ache constantly (-3 DEX).", "Your joints suddenly hurt.", "Your joints stop hurting.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_BAD_LUCK, "There is a black aura surrounding you.", "There is a malignant black aura surrounding you...", "Your black aura swirls and fades.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_VULN_ELEM, "You are susceptible to damage from the elements.", "You feel strangely exposed.", "You feel less exposed.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_MOTION, "Your movements are precise and forceful (+1 stealth).", "You move with new assurance.", "You move with less assurance.", "(nothing)", 0, 0, 0, 0, 0 }, { MUT3_GOOD_LUCK, "There is a white aura surrounding you.", "There is a benevolent white aura surrounding you...", "Your white aura shimmers and fades.", "(nothing)", 0, 0, 0, 0, 0 } }; /* * The racial powers: * * Actual mutation, * Description of mutation * Text on gaining mutation * Text on losing mutation * Short text (for choosing activatable mutations) * level, cost, stat, difficulty (each for activatable mutations only) * chance (random mutations only) */ const mutation_type race_powers[MAX_RACE_POWERS] = { { RACE_DWARF, "You can find traps, doors and stairs.", "(nothing)", "(nothing)", "Detect doors+traps", 5, 5, A_WIS, 12, 0 }, { RACE_NIBELUNG, "You can find traps, doors and stairs.", "(nothing)", "(nothing)", "Detect doors+traps", 10, 5, A_WIS, 10, 0 }, { RACE_HOBBIT, "You can forage in the dungeon.", "(nothing)", "(nothing)", "Create food", 15, 10, A_INT, 10, 0 }, { RACE_GNOME, "You can move youself accross the dungeon.", "(nothing)", "(nothing)", "Telport (range 10 + lvl)", 5, 10, A_INT, 12, 0 }, { RACE_HALF_ORC, "You can conquer your fears.", "(nothing)", "(nothing)", "Remove fear", 3, 5, A_WIS, 8, 0 }, { RACE_HALF_TROLL, "You can drive yourself into a berserk frenzy.", "(nothing)", "(nothing)", "Berserk", 10, 12, A_WIS, 9, 0 }, { RACE_BARBARIAN, "You can drive yourself into a berserk frenzy.", "(nothing)", "(nothing)", "Berserk", 8, 10, A_WIS, 9, 0 }, { RACE_AMBERITE, "You can cross into other shadows.", "(nothing)", "(nothing)", "Shadow shifting", 30, 50, A_INT, 50, 0 }, { RACE_AMBERITE, "You can mentally walk the pattern.", "(nothing)", "(nothing)", "Pattern mindwalking", 40, 75, A_WIS, 50, 0 }, { RACE_HALF_OGRE, "You can set traps for your enemies.", "(nothing)", "(nothing)", "Explosive rune", 25, 35, A_INT, 15, 0 }, { RACE_HALF_GIANT, "You can reduce the dungeon to rubble.", "(nothing)", "(nothing)", "Stone to mud", 20, 10, A_STR, 12, 0 }, { RACE_HALF_TITAN, "You can learn about the dungeon's inhabitants.", "(nothing)", "(nothing)", "Probing", 35, 20, A_STR, 12, 0 }, { RACE_CYCLOPS, "You can throw boulders with great force.", "(nothing)", "(nothing)", "Throw Boulder (3*lvl)/2", 20, 15, A_STR, 12, 0 }, { RACE_YEEK, "You can make a terrifying scream.", "(nothing)", "(nothing)", "Scare monsters", 15, 15, A_WIS, 10, 0 }, { RACE_SPECTRE, "You can wail to terrify your enemies.", "(nothing)", "(nothing)", "Scare monsters", 4, 6, A_INT, 3, 0 }, { RACE_KLACKON, "You can spit acid.", "(nothing)", "(nothing)", "Spit acid (dam lvl)", 9, 9, A_DEX, 14, 0 }, { RACE_KOBOLD, "You can throw poisoned darts.", "(nothing)", "(nothing)", "Poison dart (dam lvl)", 12, 8, A_DEX, 14, 0 }, { RACE_DARK_ELF, "You can fire magic missiles.", "(nothing)", "(nothing)", "Magic missile", 2, 2, A_INT, 9, 0 }, { RACE_DRACONIAN, "You can breathe like a dragon.", "(nothing)", "(nothing)", "Dragon breath", 15, 25, A_CON, 12, 0 }, { RACE_MIND_FLAYER, "You can blast your enemies with psionic energy.", "(nothing)", "(nothing)", "Mind blast (dam lvl)", 15, 12, A_INT, 14, 0 }, { RACE_IMP, "You can cast fire bolts/balls.", "(nothing)", "(nothing)", "Fire bolt/ball (dam lvl)", 9, 15, A_WIS, 15, 0 }, { RACE_GOLEM, "You can turn your skin to stone.", "(nothing)", "(nothing)", "Stone skin (dur 1d20+30)", 20, 15, A_CON, 8, 0 }, { RACE_SKELETON, "You can recover lost life force.", "(nothing)", "(nothing)", "Restore life", 30, 30, A_WIS, 18, 0 }, { RACE_ZOMBIE, "You can recover lost life force.", "(nothing)", "(nothing)", "Restore life", 30, 30, A_WIS, 18, 0 }, { RACE_VAMPIRE, "You can steal life from a foe.", "(nothing)", "(nothing)", "Drain life", 5, 10, A_CON, 9, 0 }, { RACE_SPRITE, "You can throw magic dust which induces sleep.", "(nothing)", "(nothing)", "Sleeping dust", 12, 12, A_INT, 15, 0 }, { RACE_GHOUL, "You can eat corpses and skeletons to gain nutrition.", "(nothing)", "(nothing)", "Eat corpse/skeleton", 1, 0, A_CON, 0, 0 }, { RACE_GHOUL, "You can sense living creatures.", "(nothing)", "(nothing)", "Sense living", 30, 10, A_WIS, 12, 0 } }; zangband/src/ui.c0000644000000000000000000007472710250356275012736 0ustar rootroot/* File: ui.c */ /* Purpose: Angband user interface -SF- */ #include "angband.h" /* * Center a format string in the buffer. * * The first parameter on the stack must be the width * to center in. * * The second must be the string to center with. * This is treated as a format string - so may contain * other commands etc... */ void center_string(char *buf, uint max, cptr fmt, va_list *vp) { int i, j; cptr str; char tmp[1024]; int size; /* Unused parameter */ (void)fmt; /* Get the size of the string to center in */ size = va_arg(*vp, int); /* Get the string to center with. */ str = va_arg(*vp, cptr); /* Expand the string */ vstrnfmt(tmp, 1024, str, vp); /* Total length */ i = strlen(tmp); /* Necessary border */ j = (size - i) / 2; /* Mega-Hack center the (format) string in the buffer */ strnfmt(buf, max, "%*s%s%*s", j, "", tmp, size - i - j, ""); } /* * Function used to print a flag in coloured binary. */ void binary_fmt(char *buf, uint max, cptr fmt, va_list *vp) { uint i; u32b mask = 1; int len = 0; u32b arg; /* Unused parameter */ (void)fmt; /* Pre-terminate buffer */ buf[0] = '\0'; /* Get the argument */ arg = va_arg(*vp, u32b); /* Scan the flags */ for (i = 1; ((i <= 32) && (i < max)); i++) { /* Dump set bits */ if (arg & mask) { strnfcat(buf, max, &len, CLR_BLUE "*"); } /* Dump unset bits */ else { strnfcat(buf, max, &len, CLR_WHITE "-"); } mask *= 2; } } /* * Generic "get choice from menu" function */ int get_player_choice(cptr *choices, int num, int col, int wid, cptr helpfile, void (*hook) (cptr)) { int top = 0, cur = 0; /* int bot = 13; */ int i, dir; char c; char buf[80]; bool done = FALSE; int hgt; /* Autoselect if able */ if (num == 1) done = TRUE; /* Clear */ for (i = TABLE_ROW; i < Term->hgt; i++) { /* Clear */ Term_erase(col, i, Term->wid - wid); } /* Choose */ while (TRUE) { /* * Note to Melkor: What happens when the screen is resized? * There is no 'redraw' hook at this point... * (That is why the original code restricted itself to what * would fit in the smallest possible screen.) -SF- */ hgt = Term->hgt - TABLE_ROW - 1; /* Redraw the list */ for (i = 0; ((i + top < num) && (i <= hgt)); i++) { if (i + top < 26) { strnfmt(buf, 80, "%c) %s", I2A(i + top), choices[i + top]); } else { /* ToDo: Fix the ASCII dependency */ strnfmt(buf, 80, "%c) %s", 'A' + (i + top - 26), choices[i + top]); } /* Clear */ Term_erase(col, i + TABLE_ROW, wid); /* Display */ if (i == (cur - top)) { /* Highlight the current selection */ put_fstr(col, i + TABLE_ROW, CLR_L_BLUE "%s", buf); } else { put_fstr(col, i + TABLE_ROW, buf); } } if (done) return (cur); /* Display auxiliary information if any is available. */ if (hook) hook(choices[cur]); /* Move the cursor */ Term_gotoxy(col, TABLE_ROW + cur - top); c = inkey(); if (c == KTRL('X')) { quit(NULL); } if (c == ESCAPE) { /* Mega Hack - go back. */ return (INVALID_CHOICE); } if (c == '*') { /* Select at random */ cur = randint0(num); /* Move it onto the screen */ if ((cur < top) || (cur > top + hgt)) { top = cur; } /* Done */ done = TRUE; } else if (c == '?') { (void)show_file(helpfile, NULL, 0, 0); } else if (c == '=') { do_cmd_options(OPT_FLAG_BIRTH | OPT_FLAG_SERVER | OPT_FLAG_PLAYER); } else if ((c == '\n') || (c == '\r')) { /* Done */ return (cur); } else if (isdigit(c)) { /* Get a direction from the key */ dir = get_keymap_dir(c); /* Going up? */ if (dir == 8) { if (cur != 0) { /* Move selection */ cur--; } if ((top > 0) && ((cur - top) < 4)) { /* Scroll up */ top--; } } /* Going down? */ if (dir == 2) { if (cur != (num - 1)) { /* Move selection */ cur++; } if ((top + hgt < (num - 1)) && ((top + hgt - cur) < 4)) { /* Scroll down */ top++; } } } else if (isalpha(c)) { int choice; if (islower(c)) { choice = A2I(c); } else { choice = c - 'A' + 26; } /* Validate input */ if ((choice > -1) && (choice < num)) { cur = choice; /* Move it onto the screen */ if ((cur < top) || (cur > top + hgt)) { top = cur; } /* Done */ done = TRUE; } else { /* Invalid input */ bell("Illegal birth choice!"); } } } return (INVALID_CHOICE); } /* * Sorting hook -- comp function -- strings (see below) * * We use "u" to point to an array of strings. */ static bool ang_sort_comp_hook_string(const vptr u, const vptr v, int a, int b) { cptr *x = (cptr *)(u); /* Hack - ignore v */ (void)v; return (strcmp(x[a], x[b]) <= 0); } /* * Sorting hook -- swap function -- array of strings (see below) * * We use "u" to point to an array of strings. */ static void ang_sort_swap_hook_string(const vptr u, const vptr v, int a, int b) { cptr *x = (cptr *)(u); cptr temp; /* Hack - ignore v */ (void)v; /* Swap */ temp = x[a]; x[a] = x[b]; x[b] = temp; } /* * Present a sorted list to the player, and get a selection */ int get_player_sort_choice(cptr *choices, int num, int col, int wid, cptr helpfile, void (*hook) (cptr)) { int i; int choice; cptr *strings; C_MAKE(strings, num, cptr); /* Initialise the sorted string array */ for (i = 0; i < num; i++) { strings[i] = choices[i]; } /* Sort the strings */ ang_sort_comp = ang_sort_comp_hook_string; ang_sort_swap = ang_sort_swap_hook_string; /* Sort the (unique) slopes */ ang_sort((void *)strings, NULL, num); /* Get the choice */ choice = get_player_choice(strings, num, col, wid, helpfile, hook); /* Invert the choice */ for (i = 0; i < num; i++) { /* Does the string match the one we selected? */ if (choices[i] == strings[choice]) { /* Save the choice + exit */ choice = i; break; } } /* Free the strings */ FREE((void *)strings); /* Return the value from the list */ return (choice); } /* Show the option */ static bool show_option(int x, int y, menu_type *option, char c, bool scroll, bool select) { if (option->flags & MN_ACTIVE) { /* Is this option selected */ if (select) { /* Highlight this option? */ if (scroll) { prtf(x, y, " %c) " CLR_L_BLUE "%s", c, option->text); } else { prtf(x, y, "*%c) %s", c, option->text); } } else { prtf(x, y, " %c) %s", c, option->text); } return (TRUE); } /* Not a valid option */ prtf(x, y, " %s", option->text); return (FALSE); } /* * Array for converting numbers to a logical list symbol */ static const char listsym[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\0' }; /* * Display a menu of choices on the screen * * We return the number of active options. */ static int show_menu(int num, menu_type *options, int select, bool scroll, int disp(int), cptr prompt) { int cnt = 0; int i; bool select_me; int x, y; int offset = 0; /* * Display 'special' information */ if (disp) offset = disp(num); /* Border on top of menu */ clear_row(1); /* Will they fit in one column? */ if (num < 19) { for (i = 0; i < num; i++) { select_me = i == select; if (show_option(0, i + 2 + offset, &options[i], I2A(cnt), scroll, select_me)) { cnt++; } } /* Border below menu */ clear_row(num + 2 + offset); } /* Two columns (use numbers as well) */ else if (num < 37) { for (i = 0; i < num; i++) { select_me = i == select; x = (i / 18) * 40; y = (i % 18) + 2; if (show_option(x, y + offset, &options[i], listsym[cnt], scroll, select_me)) { cnt++; } } /* Border below menu */ clear_row(20 + offset); } /* Three columns - need to use upper case letters */ else { for (i = 0; i < num; i++) { select_me = i == select; x = (i / 20) * 30; y = (i % 20) + 2; if (show_option(x, y + offset, &options[i], listsym[cnt], scroll, select_me)) { cnt++; } } /* Border below menu */ clear_row(22 + offset); } /* * Display the prompt. * (Do this last, so we get the cursor in the right spot) */ if (!cnt) { prtf(0, 0, "%s (No commands available, ESC=exit)", prompt); } else if (cnt == 1) { /* Display the prompt */ prtf(0, 0, "%s (Command (a), ESC=exit)", prompt ? prompt : "Select a command: "); } else if (cnt < 19) { /* Display the prompt */ prtf(0, 0, "%s (Command (a-%c), ESC=exit)", prompt ? prompt : "Select a command: " ,I2A(cnt - 1)); } else { /* Display the prompt */ prtf(0, 0, "%s (Command (0-%c), ESC=exit)", prompt ? prompt : "Select a command: " ,listsym[cnt - 1]); } return (cnt); } /* * Get the choice corresponding to the character * chosen, and the number of possible choices. * * Return whether we want this choice verified or not. */ static int get_choice(char *c, int num, bool *ask) { int asked; int i; *c = inkey(); /* Handle "cancel" */ if (*c == ESCAPE) { return (-2); } if (num < 19) { if (isalpha(*c)) { /* Note verify */ asked = (isupper(*c)); /* Lowercase */ if (asked) *c = tolower(*c); *ask = (asked != FALSE); /* Extract request */ return(A2I(*c)); } /* Invalid choice */ *ask = FALSE; return (-1); } /* Else - look for a match */ for (i = 0; i < num; i++) { if (listsym[i] == *c) { /* Hack - we cannot ask if there are too many options */ *ask = FALSE; return (i); } } /* No match? */ *ask = FALSE; return (-1); } /* * Display a menu, and get a choice. * Return false if escape is pressed. * * 'options' is an array that contains the strings to print. * plus all the functions to call together with the * flags for each option. * 'select' shows the default/current option. * If negative, it is ignored. * 'scroll' controls whether or not to allow scrolling option * selection. * 'disp' contains an optional function to print some extra * information when constucting the menu. * 'prompt' is an optional prompt. */ bool display_menu(menu_type *options, int select, bool scroll, int disp(int), cptr prompt) { int i = -1, j, cnt; bool ask = FALSE; char choice; int num = 0; int save_choice; /* Calculate the number of strings we have */ while (options[num].text) num++; /* Paranoia XXX XXX XXX */ message_flush(); /* Save the screen */ screen_save(); /* Show the list */ cnt = show_menu(num, options, select, scroll, disp, prompt); /* Paranoia */ if (!cnt) { while (inkey() != ESCAPE) { /* Do nothing */ } /* Restore the screen */ screen_load(); return (FALSE); } /* Get a command from the user */ while (TRUE) { /* Try to get previously saved value */ if (!repeat_pull(&i)) { /* Try to match with available options */ i = get_choice(&choice, num, &ask); } /* Handle "cancel" */ if (i == -2) { /* Restore the screen */ screen_load(); return (FALSE); } /* No match? */ if (i == -1) { /* Pick default option */ if ((choice == '\r') || (choice == ' ')) { i = 0; /* Scan options */ if (num > 1) { /* Count active options up to our selection */ for (j = 0; j < select; j++) { if (options[j].flags & MN_ACTIVE) { i++; } } } } /* Scroll selected option up or down */ else if ((choice == '8') && scroll) { do { /* Find previous option */ select--; /* Scroll over */ if (select < 0) select = num - 1; } while(!(options[select].flags & MN_SELECT)); /* Show the list */ show_menu(num, options, select, scroll, disp, prompt); /* Next time */ continue; } /* Scroll selected option up or down */ else if ((choice == '2') && scroll) { do { /* Find next option */ select++; /* Scroll over */ if (select >= num) select = 0; } while(!(options[select].flags & MN_SELECT)); /* Show the list */ show_menu(num, options, select, scroll, disp, prompt); /* Next time */ continue; } /* Context-sensitive help */ else if (choice == '?') { /* Do we have a help entry? */ if ((select >= 0) && options[select].help) { /* Show the information */ show_file(options[select].help, NULL, 0, 0); /* Show the list */ show_menu(num, options, select, scroll, disp, prompt); /* Next time */ continue; } else { bell("No context sensitive help available!"); continue; } } } /* Totally Illegal */ if ((i < 0) || (i >= cnt)) { bell("Illegal choice!"); continue; } save_choice = i; /* Find the action to call */ for (j = 0; j < num; j++) { if (options[j].flags & MN_ACTIVE) { if (!i) { /* Verify it */ if (ask) { /* Belay that order */ if (!get_check("Use %s? ", options[j].text)) { /* Show the list */ show_menu(num, options, select, scroll, disp, prompt); break; } } /* Hack - restore the screen */ if (options[j].flags & MN_CLEAR) { screen_load(); } if (options[j].action(j)) { /* Hack - restore the screen */ if (!(options[j].flags & MN_CLEAR)) { /* Restore the screen */ screen_load(); } /* Save for later */ repeat_push(save_choice); /* Success */ return (TRUE); } /* Hack - save the screen */ if (options[j].flags & MN_CLEAR) { screen_save(); } /* * Select this option for next time * if had a previous selection. */ if ((select >= 0) && (options[j].flags & MN_SELECT)) { select = j; } /* Hack - flush messages */ message_flush(); /* Show the list */ show_menu(num, options, select, scroll, disp, prompt); /* Get a new command */ break; } /* Decrement count until reach selected option */ i--; } } } /* Paranoia for dumb compilers */ quit("Unreachable code in display_menu"); return (FALSE); } /* * Flush the screen, make a noise */ void bell(cptr reason) { /* Mega-Hack -- Flush the output */ Term_fresh(); /* Hack -- memorize the reason if possible */ if (character_generated && reason) { message_add(reason, MSG_BELL); /* Window stuff */ p_ptr->window |= (PW_MESSAGE); /* Force window redraw */ window_stuff(); } /* Make a bell noise (if allowed) */ if (ring_bell) Term_xtra(TERM_XTRA_NOISE, 0); /* Flush the input (later!) */ flush(); } /* * Hack -- Make a (relevant?) sound */ void sound(int val) { /* No sound */ if (!use_sound) return; /* Make a sound (if allowed) */ Term_xtra(TERM_XTRA_SOUND, val); } /* * Convert a "color letter" into an "actual" color * The colors are: dwsorgbuDWvyRGBU, as shown below */ int color_char_to_attr(char c) { switch (c) { case 'd': return (TERM_DARK); case 'w': return (TERM_WHITE); case 's': return (TERM_SLATE); case 'o': return (TERM_ORANGE); case 'r': return (TERM_RED); case 'g': return (TERM_GREEN); case 'b': return (TERM_BLUE); case 'u': return (TERM_UMBER); case 'D': return (TERM_L_DARK); case 'W': return (TERM_L_WHITE); case 'v': return (TERM_VIOLET); case 'y': return (TERM_YELLOW); case 'R': return (TERM_L_RED); case 'G': return (TERM_L_GREEN); case 'B': return (TERM_L_BLUE); case 'U': return (TERM_L_UMBER); } return (-1); } /* * Save the screen, and increase the "icky" depth. * * This function must match exactly one call to "screen_load()". */ void screen_save(void) { /* Hack -- Flush messages */ message_flush(); /* Save the screen */ Term_save(); /* Increase "icky" depth */ character_icky++; } /* * Load the screen, and decrease the "icky" depth. * * This function must match exactly one call to "screen_save()". */ void screen_load(void) { /* Hack -- Flush messages */ message_flush(); /* Load the screen */ Term_load(); /* Decrease "icky" depth */ character_icky--; } /* * Find offset of str2 in str1, including the * effects of formatting characters. * * This is not equal to str2 - str1 in general. */ int fmt_offset(cptr str1, cptr str2) { cptr c = str1; int i = 0; while (*c && (c != str2)) { /* Does this character match the escape code? */ if (*c == '$') { /* Scan the next character */ c++; /* Is it a colour specifier? */ if (((*c >= 'A') && (*c <= 'R')) || ((*c >= 'a') && (*c <= 'r'))) { c++; continue; } } /* Next position */ i++; c++; } return (i); } /* * Remove the formatting escape sequences from a buffer. */ void fmt_clean(char *buf) { char *p = buf, *c = buf; while (*c) { /* Does this character match the escape code? */ if (*c == '$') { /* Scan the next character */ c++; /* Is it an escape sequence? */ if ((*c >= 'A') && (*c <= 'R')) { /* Ignore it */ c++; continue; } /* * Hack XXX XXX - otherwise, ignore the dollar sign * and copy the string value. * * This makes "$$" turn into just "$". */ *p++ = *c; /* Stop if reach null */ if (*c == 0) break; } else { /* Copy the value */ *p++ = *c++; } } /* Terminate buffer */ *p = '\0'; } /* * Put a string with control characters at a given location */ static void put_cstr(int col, int row, cptr str, bool clear) { cptr c = str; /* Default to white */ byte a = TERM_WHITE; byte da = a; int x = col; /* Clear line, position cursor */ if (clear) Term_erase(col, row, 255); while (*c) { /* Does this character match the escape code? */ if (*c == '$') { /* Scan the next character */ c++; /* Is it a colour specifier? */ if ((*c >= 'A') && (*c <= 'P')) { /* * Save the new colour * * Hack - this depends on ASCII symbols */ a = *c - 'A'; c++; /* Hack -- fake monochrome */ if (!use_color) a = TERM_WHITE; continue; } /* Default colour change? */ else if (*c == 'Q') { /* Save current colour as 'default' */ da = a; c++; continue; } /* Go back to default colour */ else if (*c == 'R') { a = da; c++; continue; } /* * Hack XXX XXX - otherwise, ignore the dollar sign * * This makes "$$" turn into just "$". */ /* Stop if reach null */ else if (*c == 0) break; } if (*c == '\n') { /* Reset to the 'start' of the next row. */ row++; x = col; c++; /* Clear line, position cursor */ if (clear) Term_erase(col, row, 255); continue; } /* Display the character */ Term_putch(x, row, a, *c); /* Next position */ x++; c++; } } /* * Put a string with formatting information at a given location */ void put_fstr(int col, int row, cptr str, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, str); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, str, &vp); /* End the Varargs Stuff */ va_end(vp); /* Display */ put_cstr(col, row, buf, FALSE); } /* * Put a string with formatting information at a given location. * Clear the line before we start printing. */ void prtf(int col, int row, cptr str, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, str); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, str, &vp); /* End the Varargs Stuff */ va_end(vp); /* Display */ put_cstr(col, row, buf, TRUE); } /* * Print some (colored) text to the screen at the current cursor position, * automatically "wrapping" existing text (at spaces) when necessary to * avoid placing any text into the last column, and clearing every line * before placing any text in that line. Also, allow "newline" to force * a "wrap" to the next line. Advance the cursor as needed so sequential * calls to this function will work correctly. * * Once this function has been called, the cursor should not be moved * until all the related "roff()" calls to the window are complete. * * This function will correctly handle any width up to the maximum legal * value of 256, though it works best for a standard 80 character width. */ void roff(cptr str, ...) { int x, y; int w, h; cptr s; byte a = TERM_WHITE; byte da = a; va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, str); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, str, &vp); /* End the Varargs Stuff */ va_end(vp); /* Obtain the size */ (void)Term_get_size(&w, &h); /* Obtain the cursor */ (void)Term_locate(&x, &y); /* Process the string */ for (s = buf; *s; s++) { char ch; /* Force wrap */ if (*s == '\n') { /* Wrap */ x = 0; y++; /* Clear line, move cursor */ Term_erase(x, y, 255); continue; } /* Does this character match the escape code? */ if (*s == '$') { /* Scan the next character */ s++; /* Is it a colour specifier? */ if ((*s >= 'A') && (*s <= 'P')) { /* * Save the new colour * * Hack - this depends on ASCII symbols */ a = *s - 'A'; /* Hack -- fake monochrome */ if (!use_color) a = TERM_WHITE; continue; } /* Default colour change? */ else if (*s == 'Q') { /* Save current colour as 'default' */ da = a; continue; } /* Go back to default colour */ else if (*s == 'R') { a = da; continue; } /* * Hack XXX XXX - otherwise, ignore the dollar sign * * This makes "$$" turn into just "$". */ /* Stop if now reach null */ else if (*s == 0) break; } /* Clean up the char */ ch = (isprint(*s) ? *s : ' '); /* Wrap words as needed */ if ((x >= w - 1) && (ch != ' ')) { int i, n = 0; byte av[256]; char cv[256]; /* Wrap word */ if (x < w) { /* Scan existing text */ for (i = w - 2; i >= 0; i--) { /* Grab existing attr/char */ (void)Term_what(i, y, &av[i], &cv[i]); /* Break on space */ if (cv[i] == ' ') break; /* Track current word */ n = i; } } /* Special case */ if (n == 0) n = w; /* Clear line */ Term_erase(n, y, 255); /* Wrap */ x = 0; y++; /* Clear line, move cursor */ Term_erase(x, y, 255); /* Wrap the word (if any) */ for (i = n; i < w - 1; i++) { /* Dump */ Term_addch(av[i], cv[i]); /* Advance (no wrap) */ if (++x > w) x = w; } } /* Dump */ Term_addch(a, ch); /* Advance */ if (++x > w) x = w; } } /* * Like the above roff(), print lines. * However, print them to a file like fprintf(). * * froff() is smarter than fprintf() though. * It will also understand the '%v' format control sequence. */ void froff(FILE *fff, cptr str, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, str); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, str, &vp); /* End the Varargs Stuff */ va_end(vp); /* Output it to the file */ fprintf(fff, "%s", buf); } /* * Clear part of the screen */ void clear_from(int row) { int y; /* Erase requested rows */ for (y = row; y < Term->hgt; y++) { /* Erase part of the screen */ Term_erase(0, y, 255); } } /* * Clear top line of screen */ void clear_msg(void) { Term_erase(0, 0, 255); } /* * Clear a line of the screen */ void clear_row(int row) { Term_erase(0, row, 255); } /* * Clear a region of the screen, starting from (x,y1) * going to (255,y2) */ void clear_region(int x, int y1, int y2) { int y; for (y = y1; (y < Term->hgt) && (y <= y2); y++) { /* Erase part of the screen */ Term_erase(x, y, 255); } } /* * Get some input at the cursor location. * Assume the buffer is initialized to a default string. * Note that this string is often "empty" (see below). * The default buffer is displayed in yellow until cleared. * Pressing RETURN right away accepts the default entry. * Normal chars clear the default and append the char. * Backspace clears the default or deletes the final char. * ESCAPE clears the buffer and the window and returns FALSE. * RETURN accepts the current buffer contents and returns TRUE. * * Note that 'len' refers to the size of the buffer. The maximum length * of the input is 'len-1'. */ bool askfor_aux(char *buf, int len) { int y, x; int i = 0; int k = 0; bool done = FALSE; /* Locate the cursor */ (void)Term_locate(&x, &y); /* Paranoia -- check len */ if (len < 1) len = 1; /* Paranoia -- check column */ if ((x < 0) || (x >= 80)) x = 0; /* Restrict the length */ if (x + len > 80) len = 80 - x; /* Paranoia -- Clip the default entry */ buf[len - 1] = '\0'; /* Display the default answer */ Term_erase(x, y, len); put_fstr(x, y, CLR_YELLOW "%s", buf); /* Process input */ while (!done) { /* Get a key */ i = inkey(); /* Analyze the key */ switch (i) { case ESCAPE: k = 0; done = TRUE; break; case '\n': case '\r': k = strlen(buf); done = TRUE; break; case 0x7F: case '\010': if (k > 0) k--; break; default: if ((k < len - 1) && (isprint(i))) { buf[k++] = i; } else { bell("Illegal edit key!"); } break; } /* Terminate */ buf[k] = '\0'; /* Update the entry */ Term_erase(x, y, len); put_fstr(x, y, "%s", buf); } /* Aborted */ if (i == ESCAPE) return (FALSE); /* Success */ return (TRUE); } /* * Get a string from the user * * The "prompt" should take the form "Prompt: " * * Note that the initial contents of the string is used as * the default response, so be sure to "clear" it if needed. * * We clear the input, and return FALSE, on "ESCAPE". */ bool get_string(char *buf, int len, cptr str, ...) { bool res; va_list vp; char prompt[1024]; /* Begin the Varargs Stuff */ va_start(vp, str); /* Format the args, save the length */ (void)vstrnfmt(prompt, 1024, str, &vp); /* End the Varargs Stuff */ va_end(vp); /* Paranoia XXX XXX XXX */ message_flush(); /* Display prompt */ prtf(0, 0, prompt); /* Ask the user for a string */ res = askfor_aux(buf, len); /* Clear prompt */ clear_msg(); /* Result */ return (res); } /* * Verify something with the user * * The "prompt" should take the form "Query? " * * Note that "[y/n]" is appended to the prompt. */ static bool get_check_base(bool def, bool esc, cptr prompt) { int i; /* Do not skip */ p_ptr->state.skip_more = FALSE; /* Paranoia XXX XXX XXX */ message_flush(); /* Prompt for it */ prtf(0, 0, "%.70s[y/n] ", prompt); /* Get an acceptable answer */ while (TRUE) { i = inkey(); if (quick_messages) break; if (i == ESCAPE) break; if (strchr("YyNn\n\r", i)) break; bell("Illegal response to a 'yes/no' question!"); } /* Erase the prompt */ clear_msg(); /* Success? */ switch (i) { case 'y': case 'Y': return (TRUE); case ESCAPE: return (esc); case '\n': case '\r': return (def); default: return (FALSE); } } bool get_check_ext(bool def, bool esc, cptr prompt, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, prompt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, prompt, &vp); /* End the Varargs Stuff */ va_end(vp); return get_check_base(def, esc, buf); } bool get_check(cptr prompt, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, prompt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, prompt, &vp); /* End the Varargs Stuff */ va_end(vp); return get_check_base(FALSE, FALSE, buf); } /* * Prompts for a keypress * * The "prompt" should take the form "Command: " * * Returns TRUE unless the character is "Escape" */ bool get_com(cptr prompt, char *command) { /* Paranoia XXX XXX XXX */ message_flush(); /* Display a prompt */ prtf(0, 0, prompt); /* Get a key */ *command = inkey(); /* Clear the prompt */ clear_msg(); /* Handle "cancel" */ if (*command == ESCAPE) return (FALSE); /* Success */ return (TRUE); } /* * Request a "quantity" from the user * * Hack -- allow "command_arg" to specify a quantity */ s16b get_quantity(cptr prompt, int max) { int amt; char tmp[80]; char buf[80]; /* Use "command_arg" */ if (p_ptr->cmd.arg) { /* Extract a number */ amt = p_ptr->cmd.arg; /* Clear "cmd.arg" */ p_ptr->cmd.arg = 0; /* Enforce the maximum */ if (amt > max) amt = max; /* Use it */ return (amt); } /* Get the item index */ if ((max != 1) && repeat_pull(&amt)) { /* Enforce the maximum */ if (amt > max) amt = max; /* Enforce the minimum */ if (amt < 0) amt = 0; /* Use it */ return (amt); } /* Build a prompt if needed */ if (!prompt) { /* Build a prompt */ strnfmt(tmp, 80, "Quantity (1-%d): ", max); /* Use that prompt */ prompt = tmp; } /* Default to one */ amt = 1; /* Build the default */ strnfmt(buf, 80, "%d", amt); /* Ask for a quantity */ if (!get_string(buf, 7, prompt)) return (0); /* Extract a number */ amt = atoi(buf); /* A letter means "all" */ if (isalpha(buf[0])) amt = max; /* Enforce the maximum */ if (amt > max) amt = max; /* Enforce the minimum */ if (amt < 0) amt = 0; if (amt) repeat_push(amt); /* Return the result */ return (amt); } /* * Pause for user response XXX XXX XXX */ void pause_line(int row) { prtf(0, row, ""); put_fstr(23, row, "[Press any key to continue]"); inkey(); prtf(0, row, ""); } /* * GH * Called from cmd4.c and a few other places. Just extracts * a direction from the keymap for ch (the last direction, * in fact) byte or char here? I'm thinking that keymaps should * generally only apply to single keys, which makes it no more * than 128, so a char should suffice... but keymap_act is 256... */ int get_keymap_dir(char ch) { cptr act, s; int d = 0; if (isdigit(ch)) { d = D2I(ch); } else { if (rogue_like_commands) { act = keymap_act[KEYMAP_MODE_ROGUE][(byte)ch]; } else { act = keymap_act[KEYMAP_MODE_ORIG][(byte)ch]; } if (act) { /* Convert to a direction */ for (s = act; *s; ++s) { /* Use any digits in keymap */ if (isdigit(*s)) d = D2I(*s); } } } return d; } zangband/src/util.c0000755000000000000000000020455010250356275013266 0ustar rootroot/* File: util.c */ /* Purpose: Angband utilities -BEN- */ #include "angband.h" #ifdef SET_UID #ifdef PRIVATE_USER_PATH /* * Create an ".angband/" directory in the users home directory. * * ToDo: Add error handling. * ToDo: Only create the directories when actually writing files. */ void create_user_dirs(void) { char dirpath[1024]; char subdirpath[1024]; /* Get an absolute path from the filename */ path_parse(dirpath, 1024, PRIVATE_USER_PATH); /* Create the ~/.angband/ directory */ mkdir(dirpath, 0700); /* Build the path to the variant-specific sub-directory */ path_make(subdirpath, dirpath, VERSION_NAME); /* Create the directory */ mkdir(subdirpath, 0700); #ifdef USE_PRIVATE_PATHS /* Build the path to the scores sub-directory */ path_build(dirpath, sizeof(dirpath), subdirpath, "scores"); /* Create the directory */ mkdir(dirpath, 0700); /* Build the path to the savefile sub-directory */ path_build(dirpath, sizeof(dirpath), subdirpath, "bone"); /* Create the directory */ mkdir(dirpath, 0700); /* Build the path to the savefile sub-directory */ path_build(dirpath, sizeof(dirpath), subdirpath, "data"); /* Create the directory */ mkdir(dirpath, 0700); /* Build the path to the savefile sub-directory */ path_build(dirpath, sizeof(dirpath), subdirpath, "save"); /* Create the directory */ mkdir(dirpath, 0700); #endif /* USE_PRIVATE_PATHS */ } #endif /* PRIVATE_USER_PATH */ /* * Hack -- drop permissions */ void safe_setuid_drop(void) { #ifdef SAFE_SETUID #ifdef HAVE_SETEGID if (setegid(getgid()) != 0) { quit("setegid(): cannot set permissions correctly!"); } #else /* HAVE_SETEGID */ #ifdef SAFE_SETUID_POSIX if (setgid(getgid()) != 0) { quit("setgid(): cannot set permissions correctly!"); } #else /* SAFE_SETUID_POSIX */ if (setregid(getegid(), getgid()) != 0) { quit("setregid(): cannot set permissions correctly!"); } #endif /* SAFE_SETUID_POSIX */ #endif /* HAVE_SETEGID */ #endif /* SAFE_SETUID */ } /* * Hack -- grab permissions */ void safe_setuid_grab(void) { #ifdef SAFE_SETUID #ifdef HAVE_SETEGID if (setegid(player_egid) != 0) { quit("setegid(): cannot set permissions correctly!"); } #else /* HAVE_SETEGID */ #ifdef SAFE_SETUID_POSIX if (setgid(player_egid) != 0) { quit("setgid(): cannot set permissions correctly!"); } #else /* SAFE_SETUID_POSIX */ if (setregid(getegid(), getgid()) != 0) { quit("setregid(): cannot set permissions correctly!"); } #endif /* SAFE_SETUID_POSIX */ #endif /* HAVE_SETEGID */ #endif /* SAFE_SETUID */ } /* * Initialise things for multiuser machines * Pay special attention to permisions. */ void init_setuid(void) { /* Default permissions on files */ (void)umask(022); /* Get the user id (?) */ player_uid = getuid(); #ifdef VMS /* Mega-Hack -- Factor group id */ player_uid += (getgid() * 1000); #endif /* VMS */ #ifdef SAFE_SETUID #if defined(HAVE_SETEGID) || defined(SAFE_SETUID_POSIX) /* Save some info for later */ player_euid = geteuid(); player_egid = getegid(); #endif /* defined(HAVE_SETEGID) || defined(SAFE_SETUID_POSIX) */ /* XXX XXX XXX */ #if 0 /* Redundant setting necessary in case root is running the game */ /* If not root or game not setuid the following two calls do nothing */ if (setgid(getegid()) != 0) { quit("setgid(): cannot set permissions correctly!"); } if (setuid(geteuid()) != 0) { quit("setuid(): cannot set permissions correctly!"); } #endif /* 0 */ #endif /* SAFE_SETUID */ /* Drop permissions */ safe_setuid_drop(); /* Get the "user name" as a default player name */ user_name(player_name, player_uid); #ifdef PRIVATE_USER_PATH /* Create a directory for the users files. */ create_user_dirs(); #endif /* PRIVATE_USER_PATH */ } #else /* SET_UID */ void safe_setuid_drop(void) { } void safe_setuid_grab(void) { } void init_setuid(void) { } #endif /* SET_UID */ #ifdef HANDLE_SIGNALS #include /* * Handle signals -- suspend * * Actually suspend the game, and then resume cleanly */ static void handle_signal_suspend(int sig) { /* Disable handler */ (void)signal(sig, SIG_IGN); #ifdef SIGSTOP /* Flush output */ Term_fresh(); /* Suspend the "Term" */ Term_xtra(TERM_XTRA_ALIVE, 0); /* Suspend ourself */ (void)kill(0, SIGSTOP); /* Resume the "Term" */ Term_xtra(TERM_XTRA_ALIVE, 1); /* Redraw the term */ Term_redraw(); /* Flush the term */ Term_fresh(); #endif /* Restore handler */ (void)signal(sig, handle_signal_suspend); } /* * Handle signals -- simple (interrupt and quit) * * This function was causing a *huge* number of problems, so it has * been simplified greatly. We keep a global variable which counts * the number of times the user attempts to kill the process, and * we commit suicide if the user does this a certain number of times. * * We attempt to give "feedback" to the user as he approaches the * suicide thresh-hold, but without penalizing accidental keypresses. * * To prevent messy accidents, we should reset this global variable * whenever the user enters a keypress, or something like that. */ static void handle_signal_simple(int sig) { /* Disable handler */ (void)signal(sig, SIG_IGN); /* Nothing to save, just quit */ if (!character_generated || character_saved) quit(NULL); /* Count the signals */ signal_count++; /* Terminate dead characters */ if (p_ptr->state.is_dead) { /* Mark the savefile */ (void)strcpy(p_ptr->state.died_from, "Abortion"); /* Close stuff */ close_game(); /* Quit */ quit("interrupt"); } /* Allow suicide (after 5) */ else if (signal_count >= 5) { /* Cause of "death" */ (void)strcpy(p_ptr->state.died_from, "Interrupting"); /* Stop playing */ p_ptr->state.playing = FALSE; /* Suicide */ p_ptr->state.is_dead = TRUE; /* Leaving */ p_ptr->state.leaving = TRUE; /* Close stuff */ close_game(); /* Quit */ quit("interrupt"); } /* Give warning (after 4) */ else if (signal_count >= 4) { /* Make a noise */ Term_xtra(TERM_XTRA_NOISE, 0); /* Display the cause */ prtf(0, 0, "Contemplating suicide!"); /* Flush */ Term_fresh(); } /* Give warning (after 2) */ else if (signal_count >= 2) { /* Make a noise */ Term_xtra(TERM_XTRA_NOISE, 0); } /* Restore handler */ (void)signal(sig, handle_signal_simple); } /* * Handle signal -- abort, kill, etc */ static void handle_signal_abort(int sig) { /* Disable handler */ (void)signal(sig, SIG_IGN); /* Nothing to save, just quit */ if (!character_generated || character_saved) quit(NULL); /* Give a warning */ prtf(0, 23, CLR_RED "A gruesome software bug LEAPS out at you!"); /* Message */ put_fstr(45, 23, CLR_RED "Panic save..."); /* Flush output */ Term_fresh(); /* Panic Save */ p_ptr->state.panic_save = 1; /* Panic save */ (void)strcpy(p_ptr->state.died_from, "(panic save)"); /* Forbid suspend */ signals_ignore_tstp(); /* Attempt to save */ if (save_player()) { put_fstr(45, 23, CLR_RED "Panic save succeeded!"); } /* Save failed */ else { put_fstr(45, 23, CLR_RED "Panic save failed!"); } /* Flush output */ Term_fresh(); /* Quit */ quit("software bug"); } /* * Ignore SIGTSTP signals (keyboard suspend) */ void signals_ignore_tstp(void) { #ifdef SIGTSTP (void)signal(SIGTSTP, SIG_IGN); #endif } /* * Handle SIGTSTP signals (keyboard suspend) */ void signals_handle_tstp(void) { #ifdef SIGTSTP (void)signal(SIGTSTP, handle_signal_suspend); #endif } /* * Prepare to handle the relevant signals */ void signals_init(void) { #ifdef SIGHUP (void)signal(SIGHUP, SIG_IGN); #endif #ifdef SIGTSTP (void)signal(SIGTSTP, handle_signal_suspend); #endif #ifdef SIGINT (void)signal(SIGINT, handle_signal_simple); #endif #ifdef SIGQUIT (void)signal(SIGQUIT, handle_signal_simple); #endif #ifdef SIGFPE (void)signal(SIGFPE, handle_signal_abort); #endif #ifdef SIGILL (void)signal(SIGILL, handle_signal_abort); #endif #ifdef SIGTRAP (void)signal(SIGTRAP, handle_signal_abort); #endif #ifdef SIGIOT (void)signal(SIGIOT, handle_signal_abort); #endif #ifdef SIGKILL (void)signal(SIGKILL, handle_signal_abort); #endif #ifdef SIGBUS (void)signal(SIGBUS, handle_signal_abort); #endif #ifdef SIGSEGV (void)signal(SIGSEGV, handle_signal_abort); #endif #ifdef SIGTERM (void)signal(SIGTERM, handle_signal_abort); #endif #ifdef SIGPIPE (void)signal(SIGPIPE, handle_signal_abort); #endif #ifdef SIGEMT (void)signal(SIGEMT, handle_signal_abort); #endif #ifdef SIGDANGER (void)signal(SIGDANGER, handle_signal_abort); #endif #ifdef SIGSYS (void)signal(SIGSYS, handle_signal_abort); #endif #ifdef SIGXCPU (void)signal(SIGXCPU, handle_signal_abort); #endif #ifdef SIGPWR (void)signal(SIGPWR, handle_signal_abort); #endif } #else /* HANDLE_SIGNALS */ /* * Do nothing */ void signals_ignore_tstp(void) { } /* * Do nothing */ void signals_handle_tstp(void) { } /* * Do nothing */ void signals_init(void) { } #endif /* HANDLE_SIGNALS */ #ifdef SET_UID # ifndef HAS_USLEEP /* * For those systems that don't have "usleep()" but need it. * * Fake "usleep()" function grabbed from the inl netrek server -cba */ int usleep(huge usecs) { struct timeval Timer; int nfds = 0; #ifdef FD_SET fd_set *no_fds = NULL; #else int *no_fds = NULL; #endif /* Was: int readfds, writefds, exceptfds; */ /* Was: readfds = writefds = exceptfds = 0; */ /* Paranoia -- No excessive sleeping */ if (usecs > 4000000L) core("Illegal usleep() call"); /* Wait for it */ Timer.tv_sec = (usecs / 1000000L); Timer.tv_usec = (usecs % 1000000L); /* Wait for it */ if (select(nfds, no_fds, no_fds, no_fds, &Timer) < 0) { /* Hack -- ignore interrupts */ if (errno != EINTR) return -1; } /* Success */ return 0; } # endif /* !HAS_USLEEP */ /* * Hack -- External functions */ #ifndef _PWD_H # ifndef HAVE_GETPWUID extern struct passwd *getpwuid(); # endif # ifndef HAVE_GETPWNAM extern struct passwd *getpwnam(); # endif #endif /* _PWD_H */ /* * Find a default user name from the system. */ void user_name(char *buf, int id) { struct passwd *pw; /* Look up the user name */ if ((pw = getpwuid(id))) { /* Get the first 15 characters of the user name */ (void)strncpy(buf, pw->pw_name, 16); buf[15] = '\0'; #ifdef CAPITALIZE_USER_NAME /* Hack -- capitalize the user name */ if (islower(buf[0])) buf[0] = toupper(buf[0]); #endif /* CAPITALIZE_USER_NAME */ return; } /* Oops. Hack -- default to "PLAYER" */ strcpy(buf, "PLAYER"); } #endif /* SET_UID */ /* * Helper function to assert something inside an expression * * (Note that the normal assert macro can only be used * as a statement - which prevents debugging via * function wrappers.) */ bool assert_helper(cptr expr, cptr file, int line, bool result) { if (!result) { signals_ignore_tstp();\ ANG__assert_save;\ ANG__assert_fmt("\n" "Assertion failed:%s\n" "in file %s\n" "on line %d\n\n", expr, file, line); } return (result); } /* * The concept of the "file" routines below (and elsewhere) is that all * file handling should be done using as few routines as possible, since * every machine is slightly different, but these routines always have the * same semantics. * * In fact, perhaps we should use the "path_parse()" routine below to convert * from "canonical" filenames (optional leading tilde's, internal wildcards, * slash as the path seperator, etc) to "system" filenames (no special symbols, * system-specific path seperator, etc). This would allow the program itself * to assume that all filenames are "Unix" filenames, and explicitly "extract" * such filenames if needed (by "path_parse()", or perhaps "path_canon()"). * * Note that "path_temp" should probably return a "canonical" filename. * * Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()" * and "my_move()" and "my_copy()" should all take "canonical" filenames. * * Note that "canonical" filenames use a leading "slash" to indicate an absolute * path, and a leading "tilde" to indicate a special directory, and default to a * relative path, but MSDOS uses a leading "drivename plus colon" to indicate the * use of a "special drive", and then the rest of the path is parsed "normally", * and MACINTOSH uses a leading colon to indicate a relative path, and an embedded * colon to indicate a "drive plus absolute path", and finally defaults to a file * in the current working directory, which may or may not be defined. * * We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?) */ #ifdef ACORN /* * Most of the "file" routines for "ACORN" should be in "main-acn.c" */ #else /* ACORN */ #ifdef SET_UID /* * Extract a "parsed" path from an initial filename * Normally, we simply copy the filename into the buffer * But leading tilde symbols must be handled in a special way * Replace "~user/" by the home directory of the user named "user" * Replace "~/" by the home directory of the current user */ errr path_parse(char *buf, int max, cptr file) { cptr u, s; struct passwd *pw; char user[128]; /* Assume no result */ buf[0] = '\0'; /* No file? */ if (!file) return (-1); /* File needs no parsing */ if (file[0] != '~') { strncpy(buf, file, max - 1); /* Terminate */ buf[max - 1] = '\0'; return (0); } /* Point at the user */ u = file + 1; /* Look for non-user portion of the file */ s = strstr(u, PATH_SEP); /* Hack -- no long user names */ if (s && (s >= u + sizeof(user))) return (1); /* Extract a user name */ if (s) { int i; for (i = 0; u < s; ++i) user[i] = *u++; user[i] = '\0'; u = user; } /* Look up the "current" user */ if (u[0] == '\0') u = getlogin(); /* Look up a user (or "current" user) */ if (u) pw = getpwnam(u); else pw = getpwuid(getuid()); /* Nothing found? */ if (!pw) return (1); /* Make use of the info */ (void)strncpy(buf, pw->pw_dir, max - 1); /* Append the rest of the filename, if any */ if (s) (void)strncat(buf, s, max - 1); /* Terminate */ buf[max - 1] = '\0'; /* Success */ return (0); } #else /* SET_UID */ /* * Extract a "parsed" path from an initial filename * * This requires no special processing on simple machines, * except for verifying the size of the filename. */ errr path_parse(char *buf, int max, cptr file) { /* Accept the filename */ (void)strnfmt(buf, max, "%s", file); /* Success */ return (0); } #endif /* SET_UID */ #ifndef HAVE_MKSTEMP /* * Hack -- acquire a "temporary" file name if possible * * This filename is always in "system-specific" form. */ static errr path_temp(char *buf, int max) { cptr s; /* * Temp file * * If the following line gives you a compile-time warning, * then turn on the HAVE_MKSTEMP if you have mkstemp(). */ s = tmpnam(NULL); /* Oops */ if (!s) return (-1); /* Format to length */ (void)strnfmt(buf, max, "%s", s); /* Success */ return (0); } #endif /* HAVE_MKSTEMP */ /* * Create a new path by appending a file (or directory) to a path. * * This requires no special processing on simple machines, except * for verifying the size of the filename, but note the ability to * bypass the given "path" with certain special file-names. * * Note that the "file" may actually be a "sub-path", including * a path and a file. * * Note that this function yields a path which must be "parsed" * using the "parse" function above. */ void path_build(char *buf, int max, cptr path, cptr file) { /* Special file */ if (file[0] == '~') { /* Use the file itself */ (void)strnfmt(buf, max, "%s", file); } /* Absolute file, on "normal" systems */ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, "")) { /* Use the file itself */ (void)strnfmt(buf, max, "%s", file); } /* No path given */ else if (!path[0]) { /* Use the file itself */ (void)strnfmt(buf, max, "%s", file); } /* Path and File */ else { /* Build the new path */ (void)strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file); } } /* * Hack -- replacement for "fopen()" */ FILE *my_fopen(cptr file, cptr mode) { char buf[1024]; FILE *fff; /* Hack -- Try to parse the path */ if (path_parse(buf, 1024, file)) return (NULL); /* Attempt to fopen the file anyway */ fff = fopen(buf, mode); #if defined(MAC_MPW) || defined(MACH_O_CARBON) /* Set file creator and type */ if (fff && strchr(mode, 'w')) fsetfileinfo(buf, _fcreator, _ftype); #endif /* Return open file or NULL */ return (fff); } /* * Hack -- replacement for "fclose()" */ void my_fclose(FILE *fff) { /* Require a file */ if (!fff) return; /* Close, check for error */ (void)fclose(fff); } #endif /* ACORN */ #ifdef HAVE_MKSTEMP FILE *my_fopen_temp(char *buf, int max) { int fd; /* Prepare the buffer for mkstemp */ strncpy(buf, "/tmp/anXXXXXX", max); /* Secure creation of a temporary file */ fd = mkstemp(buf); /* Check the file-descriptor */ if (fd < 0) return (NULL); /* Return a file stream */ return (fdopen(fd, "w")); } #else /* HAVE_MKSTEMP */ FILE *my_fopen_temp(char *buf, int max) { /* Generate a temporary filename */ if (path_temp(buf, max)) return (NULL); /* Open the file */ return (my_fopen(buf, "w")); } #endif /* HAVE_MKSTEMP */ /* * Hack -- replacement for "fgets()" * * Read a string, without a newline, to a file * * Process tabs, strip internal non-printables if told. */ static errr my_fgets_aux(FILE *fff, char *buf, huge n, bool strip) { huge i = 0; char *s; char tmp[1024]; /* Read a line */ if (fgets(tmp, 1024, fff)) { /* Convert weirdness */ for (s = tmp; *s; s++) { /* Handle newline */ if (*s == '\n') { /* Terminate */ buf[i] = '\0'; /* Success */ return (0); } /* Handle tabs */ else if (*s == '\t') { /* Hack -- require room */ if (i + 8 >= n) break; /* Append a space */ buf[i++] = ' '; /* Append some more spaces */ while (!(i % 8)) buf[i++] = ' '; } /* Strip non-printables if asked */ else if(!strip || isprint(*s)) { /* Copy */ buf[i++] = *s; /* Check length */ if (i >= n) break; } } } /* Nothing */ buf[0] = '\0'; /* Failure */ return (1); } /* * Hack -- replacement for "fgets()" * * Read a string, without a newline, to a file * * Process tabs, strip internal non-printables */ errr my_fgets(FILE *fff, char *buf, huge n) { return (my_fgets_aux(fff, buf, n, TRUE)); } /* * Hack -- replacement for "fgets()" * * Read a string, without a newline, to a file * * Process tabs, do not strip internal non-printables */ errr my_raw_fgets(FILE *fff, char *buf, huge n) { return (my_fgets_aux(fff, buf, n, FALSE)); } #ifdef ACORN /* * Most of the "file" routines for "ACORN" should be in "main-acn.c" * * Many of them can be rewritten now that only "fd_open()" and "fd_make()" * and "my_fopen()" should ever create files. */ #else /* ACORN */ /* * Code Warrior is a little weird about some functions */ #ifdef BEN_HACK extern int open(const char *, int, ...); extern int close(int); extern int read(int, void *, unsigned int); extern int write(int, const void *, unsigned int); extern long lseek(int, long, int); #endif /* BEN_HACK */ /* * The Macintosh is a little bit brain-dead sometimes */ #ifdef MACINTOSH # define open(N,F,M) \ ((M), open((char*)(N),F)) # define write(F,B,S) \ write(F,(char*)(B),S) #endif /* MACINTOSH */ /* * Several systems have no "O_BINARY" flag */ #ifndef O_BINARY # define O_BINARY 0 #endif /* O_BINARY */ /* * Hack -- attempt to delete a file */ errr fd_kill(cptr file) { char buf[1024]; /* Hack -- Try to parse the path */ if (path_parse(buf, 1024, file)) return (-1); /* Remove */ (void)remove(buf); /* Assume success XXX XXX XXX */ return (0); } /* * Hack -- attempt to move a file */ errr fd_move(cptr file, cptr what) { char buf[1024]; char aux[1024]; /* Hack -- Try to parse the path */ if (path_parse(buf, 1024, file)) return (-1); /* Hack -- Try to parse the path */ if (path_parse(aux, 1024, what)) return (-1); /* Rename */ (void)rename(buf, aux); /* Assume success XXX XXX XXX */ return (0); } /* * Hack -- attempt to open a file descriptor (create file) * * This function should fail if the file already exists * * Note that we assume that the file should be "binary" */ int fd_make(cptr file, int mode) { char buf[1024]; int fd; /* Hack -- Try to parse the path */ if (path_parse(buf, sizeof(buf), file)) return (-1); #if defined(MACINTOSH) /* Create the file, fail if exists, write-only, binary */ fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY); #else /* Create the file, fail if exists, write-only, binary */ fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode); #endif #if defined(MAC_MPW) || defined(MACH_O_CARBON) /* Set file creator and type */ if (fd >= 0) fsetfileinfo(buf, _fcreator, _ftype); #endif /* Return descriptor */ return (fd); } /* * Hack -- attempt to open a file descriptor (existing file) * * Note that we assume that the file should be "binary" */ int fd_open(cptr file, int flags) { char buf[1024]; /* Hack -- Try to parse the path */ if (path_parse(buf, 1024, file)) return (-1); /* Attempt to open the file */ return (open(buf, flags | O_BINARY, 0)); } /* * Hack -- attempt to lock a file descriptor * * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK */ errr fd_lock(int fd, int what) { /* XXX XXX */ what = what ? what : 0; /* Verify the fd */ if (fd < 0) return (-1); #ifdef SET_UID # ifdef USG # if defined(F_ULOCK) && defined(F_LOCK) /* Un-Lock */ if (what == F_UNLCK) { /* Unlock it, Ignore errors */ lockf(fd, F_ULOCK, 0); } /* Lock */ else { /* Lock the score file */ if (lockf(fd, F_LOCK, 0) != 0) return (1); } # endif # else # if defined(LOCK_UN) && defined(LOCK_EX) /* Un-Lock */ if (what == F_UNLCK) { /* Unlock it, Ignore errors */ (void)flock(fd, LOCK_UN); } /* Lock */ else { /* Lock the score file */ if (flock(fd, LOCK_EX) != 0) return (1); } # endif # endif #endif /* Success */ return (0); } /* * Hack -- attempt to seek on a file descriptor */ errr fd_seek(int fd, huge n) { huge p; /* Verify fd */ if (fd < 0) return (-1); /* Seek to the given position */ p = lseek(fd, n, SEEK_SET); /* Failure */ if (p != n) return (1); /* Success */ return (0); } /* * Hack -- attempt to read data from a file descriptor */ errr fd_read(int fd, char *buf, huge n) { /* Verify the fd */ if (fd < 0) return (-1); #ifndef SET_UID /* Read pieces */ while (n >= 16384) { /* Read a piece */ if (read(fd, buf, 16384) != 16384) return (1); /* Shorten the task */ buf += 16384; /* Shorten the task */ n -= 16384; } #endif /* Read the final piece */ if (read(fd, buf, n) != (int)n) return (1); /* Success */ return (0); } /* * Hack -- Attempt to write data to a file descriptor */ errr fd_write(int fd, cptr buf, huge n) { /* Verify the fd */ if (fd < 0) return (-1); #ifndef SET_UID /* Write pieces */ while (n >= 16384) { /* Write a piece */ if (write(fd, buf, 16384) != 16384) return (1); /* Shorten the task */ buf += 16384; /* Shorten the task */ n -= 16384; } #endif /* Write the final piece */ if (write(fd, buf, n) != (int)n) return (1); /* Success */ return (0); } /* * Hack -- attempt to close a file descriptor */ errr fd_close(int fd) { /* Verify the fd */ if (fd < 0) return (-1); /* Close */ (void)close(fd); /* XXX XXX XXX */ return (0); } #endif /* ACORN */ /* This works by masking off the lowest order set bits one at a time */ int count_bits(u32b x) { int n = 0; if (x) { do { n++; } while (0 != (x = x & (x - 1))); } return (n); } /* * Convert a decimal to a single digit hex number */ static char hexify(uint i) { return (hexsym[i % 16]); } /* * Convert a hexidecimal-digit into a decimal */ static int dehex(char c) { if (isdigit(c)) return (D2I(c)); if (islower(c)) return (A2I(c) + 10); if (isupper(c)) return (A2I(tolower(c)) + 10); return (0); } /* * Hack -- convert a printable string into real ascii * * I have no clue if this function correctly handles, for example, * parsing "\xFF" into a (signed) char. Whoever thought of making * the "sign" of a "char" undefined is a complete moron. Oh well. */ void text_to_ascii(char *buf, cptr str) { char *s = buf; /* Analyze the "ascii" string */ while (*str) { /* Backslash codes */ if (*str == '\\') { /* Skip the backslash */ str++; /* Hex-mode XXX */ if (*str == 'x') { *s = 16 * dehex(*++str); *s++ += dehex(*++str); } /* Hack -- simple way to specify "backslash" */ else if (*str == '\\') { *s++ = '\\'; } /* Hack -- simple way to specify "caret" */ else if (*str == '^') { *s++ = '^'; } /* Hack -- simple way to specify "space" */ else if (*str == 's') { *s++ = ' '; } /* Hack -- simple way to specify Escape */ else if (*str == 'e') { *s++ = ESCAPE; } /* Backspace */ else if (*str == 'b') { *s++ = '\b'; } /* Newline */ else if (*str == 'n') { *s++ = '\n'; } /* Return */ else if (*str == 'r') { *s++ = '\r'; } /* Tab */ else if (*str == 't') { *s++ = '\t'; } /* Skip the final char */ str++; } /* Normal Control codes */ else if (*str == '^') { str++; *s++ = (*str++ & 037); } /* Normal chars */ else { *s++ = *str++; } } /* Terminate */ *s = '\0'; } /* * Hack -- convert a string into a printable form */ void ascii_to_text(char *buf, cptr str) { char *s = buf; /* Analyze the "ascii" string */ while (*str) { byte i = (byte)(*str++); if (i == ESCAPE) { *s++ = '\\'; *s++ = 'e'; } else if (i == ' ') { *s++ = '\\'; *s++ = 's'; } else if (i == '\b') { *s++ = '\\'; *s++ = 'b'; } else if (i == '\t') { *s++ = '\\'; *s++ = 't'; } else if (i == '\n') { *s++ = '\\'; *s++ = 'n'; } else if (i == '\r') { *s++ = '\\'; *s++ = 'r'; } else if (i == '^') { *s++ = '\\'; *s++ = '^'; } else if (i == '\\') { *s++ = '\\'; *s++ = '\\'; } else if (i < 32) { *s++ = '^'; *s++ = i + 64; } else if (i < 127) { *s++ = i; } else { *s++ = '\\'; *s++ = 'x'; *s++ = hexify(i / 16); *s++ = hexify(i % 16); } } /* Terminate */ *s = '\0'; } /* * The "macro" package * * Functions are provided to manipulate a collection of macros, each * of which has a trigger pattern string and a resulting action string * and a small set of flags. */ /* * Determine if any macros have ever started with a given character. */ static bool macro__use[256]; /* * Find the macro (if any) which exactly matches the given pattern */ sint macro_find_exact(cptr pat) { int i; /* Nothing possible */ if (!macro__use[(byte)(pat[0])]) { return (-1); } /* Scan the macros */ for (i = 0; i < macro__num; ++i) { /* Skip macros which do not match the pattern */ if (!streq(macro__pat[i], pat)) continue; /* Found one */ return (i); } /* No matches */ return (-1); } /* * Find the first macro (if any) which contains the given pattern */ static sint macro_find_check(cptr pat) { int i; /* Nothing possible */ if (!macro__use[(byte)(pat[0])]) { return (-1); } /* Scan the macros */ for (i = 0; i < macro__num; ++i) { /* Skip macros which do not contain the pattern */ if (!prefix(macro__pat[i], pat)) continue; /* Found one */ return (i); } /* Nothing */ return (-1); } /* * Find the first macro (if any) which contains the given pattern and more */ static sint macro_find_maybe(cptr pat) { int i; /* Nothing possible */ if (!macro__use[(byte)(pat[0])]) { return (-1); } /* Scan the macros */ for (i = 0; i < macro__num; ++i) { /* Skip macros which do not contain the pattern */ if (!prefix(macro__pat[i], pat)) continue; /* Skip macros which exactly match the pattern XXX XXX */ if (streq(macro__pat[i], pat)) continue; /* Found one */ return (i); } /* Nothing */ return (-1); } /* * Find the longest macro (if any) which starts with the given pattern */ static sint macro_find_ready(cptr pat) { int i, t, n = -1, s = -1; /* Nothing possible */ if (!macro__use[(byte)(pat[0])]) { return (-1); } /* Scan the macros */ for (i = 0; i < macro__num; ++i) { /* Skip macros which are not contained by the pattern */ if (!prefix(pat, macro__pat[i])) continue; /* Obtain the length of this macro */ t = strlen(macro__pat[i]); /* Only track the "longest" pattern */ if ((n >= 0) && (s > t)) continue; /* Track the entry */ n = i; s = t; } /* Result */ return (n); } /* * Add a macro definition (or redefinition). * * We should use "act == NULL" to "remove" a macro, but this might make it * impossible to save the "removal" of a macro definition. XXX XXX XXX * * We should consider refusing to allow macros which contain existing macros, * or which are contained in existing macros, because this would simplify the * macro analysis code. XXX XXX XXX * * We should consider removing the "command macro" crap, and replacing it * with some kind of "powerful keymap" ability, but this might make it hard * to change the "roguelike" option from inside the game. XXX XXX XXX */ void macro_add(cptr pat, cptr act) { int n; /* Paranoia -- require data */ if (!pat || !act) return; /* Look for any existing macro */ n = macro_find_exact(pat); /* Replace existing macro */ if (n >= 0) { /* Free the old macro action */ string_free(macro__act[n]); } /* Create a new macro */ else { /* Acquire a new index */ n = macro__num++; /* Save the pattern */ macro__pat[n] = string_make(pat); } /* Save the action */ macro__act[n] = string_make(act); /* Efficiency */ macro__use[(byte)(pat[0])] = TRUE; } /* This is never used. */ #if 0 /* * Initialize the "macro" package */ errr macro_init(void) { /* Macro patterns */ C_MAKE(macro__pat, MACRO_MAX, cptr); /* Macro actions */ C_MAKE(macro__act, MACRO_MAX, cptr); /* Success */ return (0); } #endif /* 0 */ /* * Local variable -- we are inside a "macro action" * * Do not match any macros until "ascii 30" is found. */ static bool parse_macro = FALSE; /* * Local variable -- we are inside a "macro trigger" * * Strip all keypresses until a low ascii value is found. */ static bool parse_under = FALSE; /* * Flush all input chars. Actually, remember the flush, * and do a "special flush" before the next "inkey()". * * This is not only more efficient, but also necessary to make sure * that various "inkey()" codes are not "lost" along the way. */ void flush(void) { /* Do it later */ p_ptr->cmd.inkey_xtra = TRUE; } /* * Helper function called only from "inkey()" * * This function does almost all of the "macro" processing. * * We use the "Term_key_push()" function to handle "failed" macros, as well * as "extra" keys read in while choosing the proper macro, and also to hold * the action for the macro, plus a special "ascii 30" character indicating * that any macro action in progress is complete. Embedded macros are thus * illegal, unless a macro action includes an explicit "ascii 30" character, * which would probably be a massive hack, and might break things. * * Only 500 (0+1+2+...+29+30) milliseconds may elapse between each key in * the macro trigger sequence. If a key sequence forms the "prefix" of a * macro trigger, 500 milliseconds must pass before the key sequence is * known not to be that macro trigger. XXX XXX XXX */ static char inkey_aux(void) { int k = 0, n, p = 0, w = 0; char ch; cptr pat, act; char buf[1024]; /* Wait for a keypress */ (void)(Term_inkey(&ch, TRUE, TRUE)); /* End "macro action" */ if (ch == 30) parse_macro = FALSE; /* Inside "macro action" */ if (ch == 30) return (ch); /* Inside "macro action" */ if (parse_macro) return (ch); /* Inside "macro trigger" */ if (parse_under) return (ch); /* Save the first key, advance */ buf[p++] = ch; buf[p] = '\0'; /* Check for possible macro */ k = macro_find_check(buf); /* No macro pending */ if (k < 0) return (ch); /* Wait for a macro, or a timeout */ while (TRUE) { /* Check for pending macro */ k = macro_find_maybe(buf); /* No macro pending */ if (k < 0) break; /* Check for (and remove) a pending key */ if (0 == Term_inkey(&ch, FALSE, TRUE)) { /* Append the key */ buf[p++] = ch; buf[p] = '\0'; /* Restart wait */ w = 0; } /* No key ready */ else { /* Increase "wait" */ w += 10; /* Excessive delay */ if (w >= 100) break; /* Delay */ Term_xtra(TERM_XTRA_DELAY, w); } } /* Check for available macro */ k = macro_find_ready(buf); /* No macro available */ if (k < 0) { /* Push all the keys back on the queue */ while (p > 0) { /* Push the key, notice over-flow */ if (Term_key_push(buf[--p])) return (0); } /* Wait for (and remove) a pending key */ (void)Term_inkey(&ch, TRUE, TRUE); /* Return the key */ return (ch); } /* Get the pattern */ pat = macro__pat[k]; /* Get the length of the pattern */ n = strlen(pat); /* Push the "extra" keys back on the queue */ while (p > n) { /* Push the key, notice over-flow */ if (Term_key_push(buf[--p])) return (0); } /* Begin "macro action" */ parse_macro = TRUE; /* Push the "end of macro action" key */ if (Term_key_push(30)) return (0); /* Access the macro action */ act = macro__act[k]; /* Get the length of the action */ n = strlen(act); /* Push the macro "action" onto the key queue */ while (n > 0) { /* Push the key, notice over-flow */ if (Term_key_push(act[--n])) return (0); } /* Hack -- Force "inkey()" to call us again */ return (0); } /* * Mega-Hack -- special "inkey_next" pointer. XXX XXX XXX * * This special pointer allows a sequence of keys to be "inserted" into * the stream of keys returned by "inkey()". This key sequence will not * trigger any macros, and cannot be bypassed by the Borg. It is used * in Angband to handle "keymaps". */ static cptr inkey_next = NULL; #ifdef ALLOW_BORG /* * Mega-Hack -- special "inkey_hack" hook. XXX XXX XXX * * This special function hook allows the "Borg" (see elsewhere) to take * control of the "inkey()" function, and substitute in fake keypresses. */ char (*inkey_hack) (int flush_first) = NULL; #endif /* ALLOW_BORG */ /* * Get a keypress from the user. * * This function recognizes a few "global parameters". These are variables * which, if set to TRUE before calling this function, will have an effect * on this function, and which are always reset to FALSE by this function * before this function returns. Thus they function just like normal * parameters, except that most calls to this function can ignore them. * * If "inkey_xtra" is TRUE, then all pending keypresses will be flushed, * and any macro processing in progress will be aborted. This flag is * set by the "flush()" function, which does not actually flush anything * itself, but rather, triggers delayed input flushing via "inkey_xtra". * * If "inkey_scan" is TRUE, then we will immediately return "zero" if no * keypress is available, instead of waiting for a keypress. * * If "inkey_base" is TRUE, then all macro processing will be bypassed. * If "inkey_base" and "inkey_scan" are both TRUE, then this function will * not return immediately, but will wait for a keypress for as long as the * normal macro matching code would, allowing the direct entry of macro * triggers. The "inkey_base" flag is extremely dangerous! * * If "inkey_flag" is TRUE, then we will assume that we are waiting for a * normal command, and we will only show the cursor if "hilite_player" is * TRUE (or if the player is in a store), instead of always showing the * cursor. The various "main-xxx.c" files should avoid saving the game * in response to a "menu item" request unless "inkey_flag" is TRUE, to * prevent savefile corruption. * * If we are waiting for a keypress, and no keypress is ready, then we will * refresh (once) the window which was active when this function was called. * * Note that "back-quote" is automatically converted into "escape" for * convenience on machines with no "escape" key. This is done after the * macro matching, so the user can still make a macro for "backquote". * * Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six) * and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to * provide support for simple keyboard "macros". These keys are so strange * that their loss as normal keys will probably be noticed by nobody. The * "ascii 30" key is used to indicate the "end" of a macro action, which * allows recursive macros to be avoided. The "ascii 31" key is used by * some of the "main-xxx.c" files to introduce macro trigger sequences. * * Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key, * which can be used to give a variety of "sub-commands" which can be used * any time. These sub-commands could include commands to take a picture of * the current screen, to start/stop recording a macro action, etc. * * If "angband_term[0]" is not active, we will make it active during this * function, so that the various "main-xxx.c" files can assume that input * is only requested (via "Term_inkey()") when "angband_term[0]" is active. * * Mega-Hack -- This function is used as the entry point for clearing the * "signal_count" variable, and of the "character_saved" variable. * * Hack -- Note the use of "inkey_next" to allow "keymaps" to be processed. * * Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal * control of the keyboard from the user. */ char inkey(void) { int v; char kk; char ch = 0; bool done = FALSE; term *old = Term; /* Hack -- Use the "inkey_next" pointer */ if (inkey_next && *inkey_next && !p_ptr->cmd.inkey_xtra) { /* Get next character, and advance */ ch = *inkey_next++; /* Cancel the various "global parameters" */ p_ptr->cmd.inkey_base = FALSE; p_ptr->cmd.inkey_xtra = FALSE; p_ptr->cmd.inkey_flag = FALSE; p_ptr->cmd.inkey_scan = FALSE; /* Accept result */ return (ch); } /* Forget pointer */ inkey_next = NULL; #ifdef ALLOW_BORG /* Mega-Hack -- Use the special hook */ if (inkey_hack && ((ch = (*inkey_hack) (p_ptr->cmd.inkey_xtra)) != 0)) { /* Cancel the various "global parameters" */ p_ptr->cmd.inkey_base = FALSE; p_ptr->cmd.inkey_xtra = FALSE; p_ptr->cmd.inkey_flag = FALSE; p_ptr->cmd.inkey_scan = FALSE; /* Accept result */ return (ch); } #endif /* ALLOW_BORG */ /* Hack -- handle delayed "flush()" */ if (p_ptr->cmd.inkey_xtra) { /* End "macro action" */ parse_macro = FALSE; /* End "macro trigger" */ parse_under = FALSE; /* Forget old keypresses */ Term_flush(); } /* Access cursor state */ (void)Term_get_cursor(&v); /* Show the cursor if waiting, except sometimes in "command" mode */ if (!p_ptr->cmd.inkey_scan && (!p_ptr->cmd.inkey_flag || hilite_player || character_icky)) { /* Show the cursor */ (void)Term_set_cursor(1); } /* Hack -- Activate main screen */ Term_activate(angband_term[0]); /* Get a key */ while (!ch) { /* Hack -- Handle "inkey_scan" */ if (!p_ptr->cmd.inkey_base && p_ptr->cmd.inkey_scan && (0 != Term_inkey(&kk, FALSE, FALSE))) { break; } /* Hack -- Flush output once when no key ready */ if (!done && (0 != Term_inkey(&kk, FALSE, FALSE))) { /* Hack -- activate proper term */ Term_activate(old); /* Flush output */ Term_fresh(); /* Hack -- activate main screen */ Term_activate(angband_term[0]); /* Mega-Hack -- reset saved flag */ character_saved = FALSE; /* Mega-Hack -- reset signal counter */ signal_count = 0; /* Only once */ done = TRUE; } /* Hack -- Handle "inkey_base" */ if (p_ptr->cmd.inkey_base) { int w = 0; /* Wait forever */ if (!p_ptr->cmd.inkey_scan) { /* Wait for (and remove) a pending key */ if (0 == Term_inkey(&ch, TRUE, TRUE)) { /* Done */ break; } /* Oops */ break; } /* Wait */ while (TRUE) { /* Check for (and remove) a pending key */ if (0 == Term_inkey(&ch, FALSE, TRUE)) { /* Done */ break; } /* No key ready */ else { /* Increase "wait" */ w += 10; /* Excessive delay */ if (w >= 100) break; /* Delay */ Term_xtra(TERM_XTRA_DELAY, w); } } /* Done */ break; } /* Get a key (see above) */ ch = inkey_aux(); /* Handle "control-right-bracket" */ if (ch == 29) { /* Strip this key */ ch = 0; /* Continue */ continue; } /* Treat back-quote as escape */ if (ch == '`') ch = ESCAPE; /* End "macro trigger" */ if (parse_under && (ch <= 32)) { /* Strip this key */ ch = 0; /* End "macro trigger" */ parse_under = FALSE; } /* Handle "control-caret" */ if (ch == 30) { /* Strip this key */ ch = 0; } /* Handle "control-underscore" */ else if (ch == 31) { /* Strip this key */ ch = 0; /* Begin "macro trigger" */ parse_under = TRUE; } /* Inside "macro trigger" */ else if (parse_under) { /* Strip this key */ ch = 0; } } /* Hack -- restore the term */ Term_activate(old); /* Restore the cursor */ (void)Term_set_cursor(v); /* Cancel the various "global parameters" */ p_ptr->cmd.inkey_base = FALSE; p_ptr->cmd.inkey_xtra = FALSE; p_ptr->cmd.inkey_flag = FALSE; p_ptr->cmd.inkey_scan = FALSE; /* Return the keypress */ return (ch); } /* * The "quark" package * * This package is used to reduce the memory usage of object inscriptions. * * We use dynamic string allocation because otherwise it is necessary to * pre-guess the amount of quark activity. We limit the total number of * quarks, but this is much easier to "expand" as needed. XXX XXX XXX * * Two objects with the same inscription will have the same "quark" index. * * Some code uses "zero" to indicate the non-existance of a quark. * * Note that "quark zero" is NULL and should never be "dereferenced". * * ToDo: Automatically resize the array if necessary. */ /* * The number of quarks */ static s16b quark__num; /* * The pointers to the quarks [QUARK_MAX] */ static cptr *quark__str; /* * Refcount for Quarks */ static u16b *quark__ref; /* * Add a new "quark" to the set of quarks. */ s16b quark_add(cptr str) { int i; int posn = 0; /* Look for an existing quark */ for (i = 1; i < quark__num; i++) { /* Test refcount */ if (!quark__ref[i]) continue; /* Check for equality */ if (streq(quark__str[i], str)) { /* Increase refcount */ quark__ref[i]++; return (i); } } /* Look for an empty quark */ for (i = 1; i < quark__num; i++) { if (!quark__ref[i]) { posn = i; break; } } /* Did we fail to find room? */ if (!posn) { /* Paranoia -- Require room */ if (quark__num == QUARK_MAX) { /* Paranoia - no room? */ return (0); } else { /* Use new quark */ posn = quark__num; /* New maximal quark */ quark__num++; } } /* Add a new quark */ quark__str[posn] = string_make(str); /* One use of this quark */ quark__ref[posn] = 1; /* Return the index */ return (posn); } /* * Like quark_add(), but take a format string. */ s16b quark_fmt(cptr str, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, str); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, str, &vp); /* End the Varargs Stuff */ va_end(vp); /* Quark stuff */ return (quark_add(buf)); } /* * Remove the quark */ void quark_remove(s16b *i) { /* Only need to remove real quarks */ if (!(*i)) return; /* Verify */ if ((*i < 0) || (*i >= quark__num)) return; /* Decrease refcount */ quark__ref[*i]--; /* Deallocate? */ if (!quark__ref[*i]) { string_free(quark__str[*i]); quark__str[*i] = NULL; } /* No longer have a quark here */ *i = 0; } /* * Duplicate a quark */ void quark_dup(s16b i) { /* Verify */ if ((i < 0) || (i >= quark__num)) return; /* Paranoia */ if (!quark__ref[i]) return; /* Increase refcount */ quark__ref[i]++; } /* * This function looks up a quark */ cptr quark_str(s16b i) { cptr q; /* Verify */ if ((i < 0) || (i >= quark__num)) return (NULL); /* Get the quark */ q = quark__str[i]; /* Return the quark */ return (q); } /* * Initialize the "quark" package */ errr quarks_init(void) { /* Quark variables */ C_MAKE(quark__str, QUARK_MAX, cptr); C_MAKE(quark__ref, QUARK_MAX, u16b); quark__num = 1; /* Success */ return (0); } /* * Free the entire "quark" package */ errr quarks_free(void) { int i; /* Free the "quarks" */ for (i = 1; i < quark__num; i++) { /* Paranoia - only try to free existing quarks */ if (quark__str[i]) { string_free(quark__str[i]); } } /* Free the list of "quarks" */ FREE((void *)quark__str); FREE((void *)quark__ref); /* Success */ return (0); } /* * The "message memorization" package. * * Each call to "message_add(s)" will add a new "most recent" message * to the "message recall list", using the contents of the string "s". * * The number of memorized messages is available as "message_num()". * * Old messages can be retrieved by "message_str(age)", where the "age" * of the most recently memorized message is zero, and the oldest "age" * which is available is "message_num() - 1". Messages outside this * range are returned as the empty string. * * The messages are stored in a special manner that maximizes "efficiency", * that is, we attempt to maximize the number of semi-sequential messages * that can be retrieved, given a limited amount of storage space, without * causing the memorization of new messages or the recall of old messages * to be too expensive. * * We keep a buffer of chars to hold the "text" of the messages, more or * less in the order they were memorized, and an array of offsets into that * buffer, representing the actual messages, but we allow the "text" to be * "shared" by two messages with "similar" ages, as long as we never cause * sharing to reach too far back in the the buffer. * * The implementation is complicated by the fact that both the array of * offsets, and the buffer itself, are both treated as "circular arrays" * for efficiency purposes, but the strings may not be "broken" across * the ends of the array. * * When we want to memorize a new message, we attempt to "reuse" the buffer * space by checking for message duplication within the recent messages. * * Otherwise, if we need more buffer space, we grab a full quarter of the * total buffer space at a time, to keep the reclamation code efficient. * * The "message_add()" function is rather "complex", because it must be * extremely efficient, both in space and time, for use with the Borg. */ /* * The next "free" index to use */ static u16b message__next; /* * The index of the oldest message (none yet) */ static u16b message__last; /* * The next "free" offset */ static u16b message__head; /* * The offset to the oldest used char (none yet) */ static u16b message__tail; /* * The array[MESSAGE_MAX] of offsets, by index */ static u16b *message__ptr; /* * The array[MESSAGE_BUF] of chars, by offset */ static char *message__buf; /* * The array[MESSAGE_MAX] of u16b for the types of messages */ static u16b *message__type; /* * Table of colors associated to message-types */ static byte message__color[MSG_MAX]; /* * Wrapper function to get values out of message__color */ byte get_msg_type_color(byte a) { /* Paranoia */ if (a >= MSG_MAX) return TERM_WHITE; /* Return the color */ return (message__color[(int)a]); } /* * How many messages are "available"? */ s16b message_num(void) { /* Determine how many messages are "available" */ return (message__next + MESSAGE_MAX - message__last) % MESSAGE_MAX; } /* * Recall the "text" of a saved message */ cptr message_str(s16b age) { s16b x; s16b o; cptr s; /* Forgotten messages have no text */ if ((age < 0) || (age >= message_num())) return (""); /* Get the "logical" index */ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX; /* Get the "offset" for the message */ o = message__ptr[x]; /* Get the message text */ s = &message__buf[o]; /* Return the message text */ return (s); } /* * Recall the "type" of a saved message */ u16b message_type(s16b age) { s16b x; /* Forgotten messages have no special color */ if ((age < 0) || (age >= message_num())) return (TERM_WHITE); /* Get the "logical" index */ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX; /* Return the message type */ return (message__type[x]); } /* * Recall the "color" of a saved message */ byte message_color(s16b age) { return message__color[message_type(age)]; } errr message_color_define(u16b type, byte color) { /* Ignore illegal types */ if (type >= MSG_MAX) return (1); /* Store the color */ message__color[type] = color % 16; /* Success */ return (0); } /* * Add a new message, with great efficiency * * We must ignore long messages to prevent internal overflow, since we * assume that we can always get enough space by advancing "message__tail" * by one quarter the total buffer space. * * We must not attempt to optimize using a message index or buffer space * which is "far away" from the most recent entries, or we will lose a lot * of messages when we "expire" the old message index and/or buffer space. * * We attempt to minimize the use of "string compare" operations in this * function, because they are expensive when used in mass quantities. */ void message_add(cptr str, u16b type) { int m, n, k, i, x, o; char w[1024]; cptr u; char *v; /*** Step 1 -- Analyze the message ***/ /* Hack -- Ignore "non-messages" */ if (!str) return; /* Message length */ n = strlen(str); /* Hack -- Ignore "long" messages */ if (n >= MESSAGE_BUF / 4) return; /*** Step 2 -- Attempt to optimize ***/ /* Limit number of messages to check */ m = message_num(); /* Limit number of messages to check */ k = m / 4; /* Limit number of messages to check */ if (k > 32) k = 32; /* Check previous message */ for (i = message__next; m; m--) { int j = 1; char buf[1024]; char *t; cptr old; /* Back up, wrap if needed */ if (i-- == 0) i = MESSAGE_MAX - 1; /* Index */ o = message__ptr[i]; /* Get the old string */ old = &message__buf[o]; /* Skip small messages */ if (!old) continue; strcpy(buf, old); /* Find multiple */ for (t = buf; *t && (*t != '<'); t++) ; if (*t) { /* Message is too small */ if (strlen(buf) < 6) break; /* Drop the space */ *(t - 1) = '\0'; /* Get multiplier */ j = atoi(++t); } /* Limit the multiplier to 1000 */ if (buf && streq(buf, str) && (j < 1000)) { j++; /* Overwrite */ message__next = i; str = w; /* Write it out */ strnfmt(w, 1024, "%s <%dx>", buf, j); /* Message length */ n = strlen(str); } /* Done */ break; } /* Start just after the most recent message */ i = message__next; /* Check the last few messages for duplication */ for (; k; k--) { u16b q; cptr old; /* Back up, wrap if needed */ if (i-- == 0) i = MESSAGE_MAX - 1; /* Stop before oldest message */ if (i == message__last) break; /* Index */ o = message__ptr[i]; /* Extract "distance" from "head" */ q = (message__head + MESSAGE_BUF - o) % MESSAGE_BUF; /* Do not optimize over large distances */ if (q >= MESSAGE_BUF / 4) continue; /* Get the old string */ old = &message__buf[o]; /* Compare */ if (!streq(old, str)) continue; /* Get the next available message index */ x = message__next; /* Advance 'message__next', wrap if needed */ if (++message__next == MESSAGE_MAX) message__next = 0; /* Kill last message if needed */ if (message__next == message__last) { /* Advance 'message__last', wrap if needed */ if (++message__last == MESSAGE_MAX) message__last = 0; } /* Assign the starting address */ message__ptr[x] = message__ptr[i]; /* Store the message type */ message__type[x] = type; /* Success */ return; } /*** Step 3 -- Ensure space before end of buffer ***/ /* Kill messages, and wrap, if needed */ if (message__head + (n + 1) >= MESSAGE_BUF) { /* Kill all "dead" messages */ for (i = message__last; TRUE; i++) { /* Wrap if needed */ if (i == MESSAGE_MAX) i = 0; /* Stop before the new message */ if (i == message__next) break; /* Get offset */ o = message__ptr[i]; /* Kill "dead" messages */ if (o >= message__head) { /* Track oldest message */ message__last = i + 1; } } /* Wrap "tail" if needed */ if (message__tail >= message__head) message__tail = 0; /* Start over */ message__head = 0; } /*** Step 4 -- Ensure space for actual characters ***/ /* Kill messages, if needed */ if (message__head + (n + 1) > message__tail) { /* Advance to new "tail" location */ message__tail += (MESSAGE_BUF / 4); /* Kill all "dead" messages */ for (i = message__last; TRUE; i++) { /* Wrap if needed */ if (i == MESSAGE_MAX) i = 0; /* Stop before the new message */ if (i == message__next) break; /* Get offset */ o = message__ptr[i]; /* Kill "dead" messages */ if ((o >= message__head) && (o < message__tail)) { /* Track oldest message */ message__last = i + 1; } } } /*** Step 5 -- Grab a new message index ***/ /* Get the next available message index */ x = message__next; /* Advance 'message__next', wrap if needed */ if (++message__next == MESSAGE_MAX) message__next = 0; /* Kill last message if needed */ if (message__next == message__last) { /* Advance 'message__last', wrap if needed */ if (++message__last == MESSAGE_MAX) message__last = 0; } /*** Step 6 -- Insert the message text ***/ /* Assign the starting address */ message__ptr[x] = message__head; /* Inline 'strcpy(message__buf + message__head, str)' */ v = message__buf + message__head; for (u = str; *u;) *v++ = *u++; *v = '\0'; /* Advance the "head" pointer */ message__head += (n + 1); /* Store the message type */ message__type[x] = type; } /* * Initialize the "message" package */ errr messages_init(void) { /* Message variables */ C_MAKE(message__ptr, MESSAGE_MAX, u16b); C_MAKE(message__buf, MESSAGE_BUF, char); C_MAKE(message__type, MESSAGE_MAX, u16b); /* Init the message colors to white */ (void)C_BSET(message__color, TERM_WHITE, MSG_MAX, byte); /* Hack -- No messages yet */ message__tail = MESSAGE_BUF; /* Success */ return (0); } /* * Free the "message" package */ void messages_free(void) { /* Free the messages */ FREE(message__ptr); FREE(message__buf); FREE(message__type); } /* * Hack -- flush */ static void msg_flush(int x) { if (!p_ptr->state.skip_more && !auto_more) { /* Pause for response */ prtf(x, 0, CLR_L_BLUE "-more-"); /* Get an acceptable keypress */ while (1) { int cmd = inkey(); if (cmd == ESCAPE) { /* Skip all the prompt until player's turn */ p_ptr->state.skip_more = TRUE; break; } if (quick_messages) break; if (cmd == ' ') break; if ((cmd == '\n') || (cmd == '\r')) break; bell("Illegal response to a 'more' prompt!"); } } /* Clear the line */ Term_erase(0, 0, 255); } static int message_column = 0; /* * Output a message to the top line of the screen. * * Break long messages into multiple pieces (40-72 chars). * * Allow multiple short messages to "share" the top line. * * Prompt the user to make sure he has a chance to read them. * * These messages are memorized for later reference (see above). * * We could do a "Term_fresh()" to provide "flicker" if needed. * * The global "msg_flag" variable can be cleared to tell us to "erase" any * "pending" messages still on the screen, instead of using "msg_flush()". * This should only be done when the user is known to have read the message. * * We must be very careful about using the "msgf()" functions without * explicitly calling the special "message_flush()" function, since this may * result in the loss of information if the screen is cleared, or if anything * is displayed on the top line. * * Hack -- Note that "msgf(NULL)" will clear the top line * even if no messages are pending. This is probably a hack. */ static void msg_print_aux(u16b type, cptr msg) { int n; char *t; char buf[1024]; byte color = TERM_WHITE; /* Hack -- fake monochrome */ if (!use_color) type = MSG_GENERIC; /* Hack -- Reset */ if (!msg_flag) message_column = 0; /* Message Length */ n = (msg ? strlen(msg) : 0); /* Hack -- flush when requested or needed */ if (message_column && (!msg || ((message_column + n) > 72))) { /* Flush */ msg_flush(message_column); /* Forget it */ msg_flag = FALSE; /* Reset */ message_column = 0; } /* No message */ if (!msg) return; /* Paranoia */ if (n > 1000) return; /* Memorize the message (if legal) */ if (character_generated && !(p_ptr->state.is_dead)) message_add(msg, type); /* Window stuff */ p_ptr->window |= (PW_MESSAGE); /* Copy it */ strcpy(buf, msg); /* Analyze the buffer */ t = buf; /* Get the color of the message (if legal) */ if (message__color) color = message__color[type]; /* HACK -- no "black" messages */ if (color == TERM_DARK) color = TERM_WHITE; /* Split message */ while (n > 72) { char oops; int check, split; /* Default split */ split = 72; /* Find the "best" split point */ for (check = 40; check < 72; check++) { /* Found a valid split point */ if (t[check] == ' ') split = check; } /* Save the split character */ oops = t[split]; /* Split the message */ t[split] = '\0'; /* Display part of the message */ prtf(0, 0, "%s%s", color_seq[color], t); /* Flush it */ msg_flush(split + 1); /* Restore the split character */ t[split] = oops; /* Insert a space */ t[--split] = ' '; /* Prepare to recurse on the rest of "buf" */ t += split; n -= split; } /* Display the tail of the message */ prtf(message_column, 0, "%s%s", color_seq[color], t); /* Remember the message */ msg_flag = TRUE; /* Remember the position */ message_column += n + 1; /* Optional refresh */ if (fresh_after) Term_fresh(); } static u16b current_message_type = MSG_GENERIC; /* * Change the message type, and parse the following * format string. See defines.h for the macro this * is used in. */ void set_message_type(char *buf, uint max, cptr fmt, va_list *vp) { cptr str; /* Unused parameter */ (void)fmt; /* Get the argument - and set the message type */ current_message_type = va_arg(*vp, int); /* Get the string to format with. */ str = va_arg(*vp, cptr); /* Expand the string */ vstrnfmt(buf, max, str, vp); } /* * Display a formatted message, using "vstrnfmt()" and "msg_print()". */ void msgf(cptr fmt, ...) { va_list vp; int i; char buf[1024]; /* Set the message type */ current_message_type = MSG_GENERIC; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); sound(current_message_type); /* Clean the string of '\n' characters */ for (i = 0; buf[i]; i++) { /* Erase carriage returns */ if (buf[i] == '\n') buf[i] = ' '; } /* Display */ msg_print_aux(current_message_type, buf); } /* * Process a message effect * * (The "extra" parameter is currently unused) */ void msg_effect(u16b type, s16b extra) { /* Unused parameters */ (void)type; (void)extra; } /* * Print the queued messages. */ void message_flush(void) { /* Hack -- Reset */ if (!msg_flag) message_column = 0; /* Flush when needed */ if (message_column) { /* Print pending messages */ msg_flush(message_column); /* Forget it */ msg_flag = FALSE; /* Reset */ message_column = 0; } } /* * Check a char for "vowel-hood" */ bool is_a_vowel(int ch) { switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u': case 'A': case 'E': case 'I': case 'O': case 'U': return (TRUE); } return (FALSE); } /* * Hack -- special buffer to hold the action of the current keymap */ static char request_command_buffer[256]; /* * Request a command from the user. * * Sets p_ptr->cmd.cmd, p_ptr->cmd.dir, p_ptr->cmd.rep, * p_ptr->cmd.arg. May modify p_ptr->cmd.new. * * Note that "caret" ("^") is treated specially, and is used to * allow manual input of control characters. This can be used * on many machines to request repeated tunneling (Ctrl-H) and * on the Macintosh to request "Control-Caret". * * Note that "backslash" is treated specially, and is used to bypass any * keymap entry for the following character. This is useful for macros. * * Note that this command is used both in the dungeon and in * stores, and must be careful to work in both situations. * * Note that "p_ptr->cmd.new" may not work any more. XXX XXX XXX */ void request_command(int shopping) { int i; char cmd; int mode; cptr act; /* Roguelike */ if (rogue_like_commands) { mode = KEYMAP_MODE_ROGUE; } /* Original */ else { mode = KEYMAP_MODE_ORIG; } /* No command yet */ p_ptr->cmd.cmd = 0; /* No "argument" yet */ p_ptr->cmd.arg = 0; /* No "direction" yet */ p_ptr->cmd.dir = 0; /* Get command */ while (1) { /* Hack -- auto-commands */ if (p_ptr->cmd.new) { /* Flush messages */ message_flush(); /* Use auto-command */ cmd = (char)p_ptr->cmd.new; /* Forget it */ p_ptr->cmd.new = 0; } /* Get a keypress in "command" mode */ else { /* Hack -- no flush needed */ msg_flag = FALSE; /* Reset the skip_more flag */ p_ptr->state.skip_more = FALSE; /* Activate "command mode" */ p_ptr->cmd.inkey_flag = TRUE; /* Get a command */ cmd = inkey(); } /* Clear top line */ clear_msg(); /* Command Count */ if (cmd == '0') { int old_arg = p_ptr->cmd.arg; /* Reset */ p_ptr->cmd.arg = 0; /* Begin the input */ prtf(0, 0, "Count: "); /* Get a command count */ while (1) { /* Get a new keypress */ cmd = inkey(); /* Simple editing (delete or backspace) */ if ((cmd == 0x7F) || (cmd == KTRL('H'))) { /* Delete a digit */ p_ptr->cmd.arg = p_ptr->cmd.arg / 10; /* Show current count */ prtf(0, 0, "Count: %d", p_ptr->cmd.arg); } /* Actual numeric data */ else if (cmd >= '0' && cmd <= '9') { /* Stop count at 9999 */ if (p_ptr->cmd.arg >= 1000) { /* Warn */ bell("Invalid repeat count!"); /* Limit */ p_ptr->cmd.arg = 9999; } /* Increase count */ else { /* Incorporate that digit */ p_ptr->cmd.arg = p_ptr->cmd.arg * 10 + D2I(cmd); } /* Show current count */ prtf(0, 0, "Count: %d", p_ptr->cmd.arg); } /* Exit on "unusable" input */ else { break; } } /* Hack -- Handle "zero" */ if (p_ptr->cmd.arg == 0) { /* Default to 99 */ p_ptr->cmd.arg = 99; /* Show current count */ prtf(0, 0, "Count: %d", p_ptr->cmd.arg); } /* Hack -- Handle "old_arg" */ if (old_arg != 0) { /* Restore old_arg */ p_ptr->cmd.arg = old_arg; /* Show current count */ prtf(0, 0, "Count: %d", p_ptr->cmd.arg); } /* Hack -- white-space means "enter command now" */ if ((cmd == ' ') || (cmd == '\n') || (cmd == '\r')) { /* Get a real command */ if (!get_com("Command: ", &cmd)) { /* Clear count */ p_ptr->cmd.arg = 0; /* Continue */ continue; } } } /* Allow "keymaps" to be bypassed */ if (cmd == '\\') { /* Get a real command */ (void)get_com("Command: ", &cmd); /* Hack -- bypass keymaps */ if (!inkey_next) inkey_next = ""; } /* Allow "control chars" to be entered */ if (cmd == '^') { /* Get a new command and controlify it */ if (get_com("Control: ", &cmd)) cmd = KTRL(cmd); } /* Look up applicable keymap */ act = keymap_act[mode][(byte)(cmd)]; /* Apply keymap if not inside a keymap already */ if (act && !inkey_next) { /* Install the keymap (limited buffer size) */ (void)strnfmt(request_command_buffer, 256, "%s", act); /* Start using the buffer */ inkey_next = request_command_buffer; /* Continue */ continue; } /* Paranoia */ if (!cmd) continue; /* Use command */ p_ptr->cmd.cmd = cmd; /* Done */ break; } /* Hack -- Auto-repeat certain commands */ if (p_ptr->cmd.arg <= 0) { /* Hack -- auto repeat certain commands */ if (strchr("TBDoc+", p_ptr->cmd.cmd)) { /* Repeat 99 times */ p_ptr->cmd.arg = 99; } } /* Shopping */ if (shopping == 1) { /* Convert */ switch (p_ptr->cmd.cmd) { /* Command "p" -> "purchase" (get) */ case 'p': p_ptr->cmd.cmd = 'g'; break; /* Command "m" -> "purchase" (get) */ case 'm': p_ptr->cmd.cmd = 'g'; break; /* Command "s" -> "sell" (drop) */ case 's': p_ptr->cmd.cmd = 'd'; break; } } /* Hack -- Scan equipment */ for (i = 0; i < EQUIP_MAX; i++) { cptr s; object_type *o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* No inscription */ if (!o_ptr->inscription) continue; /* Obtain the inscription */ s = quark_str(o_ptr->inscription); /* Find a '^' */ s = strchr(s, '^'); /* Process preventions */ while (s) { /* Check the "restriction" character */ if ((s[1] == p_ptr->cmd.cmd) || (s[1] == '*')) { /* Hack -- Verify command */ if (!get_check("Are you sure? ")) { /* Hack -- Use space */ p_ptr->cmd.cmd = ' '; } } /* Find another '^' */ s = strchr(s + 1, '^'); } } /* Hack -- erase the message line. */ clear_msg(); } #define REPEAT_MAX 20 /* Number of chars saved */ static int repeat__cnt = 0; /* Current index */ static int repeat__idx = 0; /* Saved "stuff" */ static int repeat__key[REPEAT_MAX]; void repeat_push(int what) { /* Too many keys */ if (repeat__cnt == REPEAT_MAX) return; /* Push the "stuff" */ repeat__key[repeat__cnt++] = what; /* Prevents us from pulling keys */ ++repeat__idx; } void repeat_clear(void) { /* Start over from the failed pull */ if (repeat__idx) { /* Decrease the number of characters */ --repeat__idx; } /* Set the counter */ repeat__cnt = repeat__idx; } bool repeat_pull(int *what) { /* All out of keys */ if (repeat__idx == repeat__cnt) return (FALSE); /* Grab the next key, advance */ *what = repeat__key[repeat__idx++]; /* Success */ return (TRUE); } void repeat_check(void) { int what; /* Ignore some commands */ if (p_ptr->cmd.cmd == ESCAPE) return; if (p_ptr->cmd.cmd == ' ') return; if (p_ptr->cmd.cmd == '\r') return; if (p_ptr->cmd.cmd == '\n') return; /* Repeat Last Command */ if (p_ptr->cmd.cmd == 'n') { /* Reset */ repeat__idx = 0; /* Get the command */ if (repeat_pull(&what)) { /* Save the command */ p_ptr->cmd.cmd = what; } } /* Start saving new command */ else { /* Reset */ repeat__cnt = 0; repeat__idx = 0; what = p_ptr->cmd.cmd; /* Save this command */ repeat_push(what); } } zangband/src/variable.c0000755000000000000000000003405610250356275014100 0ustar rootroot/* File: variable.c */ /* Purpose: Angband variables */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Hack -- Link a copyright message into the executable */ cptr copyright[5] = { "Copyright (c) 1989 James E. Wilson, Robert A. Keoneke", "", "This software may be copied and distributed for educational, research,", "and not for profit purposes provided that this copyright and statement", "are included in all such copies." }; /* * Executable version */ byte version_major = VER_MAJOR; byte version_minor = VER_MINOR; byte version_patch = VER_PATCH; byte version_extra = VER_EXTRA; /* * Savefile version */ byte sf_extra; /* Savefile's "version_extra" */ u32b sf_version; /* Savefile's "version" */ byte z_major; /* Savefile version for Zangband */ byte z_minor; byte z_patch; /* * Savefile information */ u32b sf_xtra; /* Operating system info */ u32b sf_when; /* Time when savefile created */ u16b sf_lives; /* Number of past "lives" with this file */ u16b sf_saves; /* Number of "saves" during this life */ /* * Run-time arguments */ bool arg_fiddle; /* Command arg -- Request fiddle mode */ bool arg_wizard; /* Command arg -- Request wizard mode */ bool arg_sound; /* Command arg -- Request special sounds */ byte arg_graphics; /* Command arg -- Request graphics mode */ bool arg_bigtile; /* Command arg -- Request bigtile mode */ bool arg_monochrome; /* Command arg -- Request monochrome mode */ bool arg_force_original; /* Command arg -- Request original keyset */ bool arg_force_roguelike; /* Command arg -- Request roguelike keyset */ /* * Various things */ bool character_generated = FALSE; /* The character exists */ bool character_dungeon = FALSE; /* The character has a dungeon */ bool character_loaded = FALSE; /* The character was loaded from a savefile */ bool character_saved = FALSE; /* The character was just saved to a savefile */ bool character_icky = FALSE; /* The game is in an icky full screen mode */ bool character_xtra = FALSE; /* The game is in an icky startup mode */ u32b seed_flavor; /* Hack -- consistent object colors */ bool msg_flag; /* Used in msg_print() for "buffering" */ s16b num_repro; /* Current reproducer count */ s32b turn; /* Current game turn */ s32b old_turn; /* Turn when level began (feelings) */ bool use_sound; /* The "sound" mode is enabled */ byte use_graphics; /* The "graphics" mode enabled (0 is none) */ bool use_bigtile = FALSE; /* Use square map tiles */ bool use_transparency = FALSE; /* Use transparent tiles */ s16b signal_count; /* Hack -- Count interupts */ s16b o_max = 1; /* Number of allocated objects */ s16b o_cnt = 0; /* Number of live objects */ s16b m_max = 1; /* Number of allocated monsters */ s16b m_cnt = 0; /* Number of live monsters */ s16b fld_max = 1; /* Number of allocated fields */ s16b fld_cnt = 0; /* Number of live fields */ s16b rg_max = 1; /* Number of allocated regions */ s16b rg_cnt = 0; /* Number of live regions */ s16b q_max = 1; /* Number of allocated quests */ s16b hack_m_idx = 0; /* Hack -- see "process_monsters()" */ /* Can we get rid of this at all? */ char summon_kin_type; /* Hack, by Julian Lighton: summon 'relatives' */ /* This probably can be moved to player_type */ int total_friends = 0; s32b total_friend_levels = 0; s32b friend_align = 0; s16b store_cache_num = 0; /* Number of stores with stock */ store_type **store_cache; /* The cache of store stocks */ /* Special options */ byte hitpoint_warn; /* Hitpoint warning (0 to 9) */ byte delay_factor; /* Delay factor (0 to 9) */ byte autosave_l; /* Autosave before entering new levels */ byte autosave_t; /* Timed autosave */ s16b autosave_freq; /* Autosave frequency */ /* Cheating options */ bool cheat_peek; bool cheat_hear; bool cheat_room; bool cheat_xtra; bool cheat_know; bool cheat_live; bool fake_monochrome; /* Use fake monochrome for effects */ /* * Dungeon size info */ s16b panel_row_min, panel_row_max; s16b panel_col_min, panel_col_max; byte *mp_a = NULL; char *mp_c = NULL; byte *mp_ta = NULL; char *mp_tc = NULL; /* * User info */ int player_uid; int player_euid; int player_egid; /* * Current player's character name */ char player_name[32]; /* * Stripped version of "player_name" */ char player_base[32]; /* * Buffer to hold the current savefile name */ char savefile[1024]; /* * Array of grids viewable to the player (see "cave.c") */ s16b view_n; s16b view_y[VIEW_MAX]; s16b view_x[VIEW_MAX]; /* * Array of grids for use by various functions (see "cave.c") */ s16b temp_n; s16b temp_y[TEMP_MAX]; s16b temp_x[TEMP_MAX]; /* * Array of grids for use in monster lighting effects (see "cave.c") */ s16b lite_n = 0; s16b lite_y[LITE_MAX]; s16b lite_x[LITE_MAX]; /* * Number of active macros. */ s16b macro__num; /* * Array of macro patterns [MACRO_MAX] */ cptr *macro__pat; /* * Array of macro actions [MACRO_MAX] */ cptr *macro__act; /* * Array of macro types [MACRO_MAX] */ bool *macro__cmd; /* * Current macro action [1024] */ char *macro__buf; /* * The array of window options */ u32b window_flag[ANGBAND_TERM_MAX]; u32b window_mask[ANGBAND_TERM_MAX]; /* Normal option masks */ u32b option_mask[8]; /* * The array of window pointers */ term *angband_term[ANGBAND_TERM_MAX]; /* * Standard window names */ char angband_term_name[ANGBAND_TERM_MAX][16] = { VERSION_NAME, "Term-1", "Term-2", "Term-3", "Term-4", "Term-5", "Term-6", "Term-7" }; /* * Global table of color definitions */ byte angband_color_table[256][4] = { {0x00, 0x00, 0x00, 0x00}, /* TERM_DARK */ {0x00, 0xFF, 0xFF, 0xFF}, /* TERM_WHITE */ {0x00, 0x80, 0x80, 0x80}, /* TERM_SLATE */ {0x00, 0xFF, 0x80, 0x00}, /* TERM_ORANGE */ {0x00, 0xC0, 0x00, 0x00}, /* TERM_RED */ {0x00, 0x00, 0x80, 0x40}, /* TERM_GREEN */ {0x00, 0x00, 0x00, 0xFF}, /* TERM_BLUE */ {0x00, 0x80, 0x40, 0x00}, /* TERM_UMBER */ {0x00, 0x40, 0x40, 0x40}, /* TERM_L_DARK */ {0x00, 0xC0, 0xC0, 0xC0}, /* TERM_L_WHITE */ {0x00, 0xFF, 0x00, 0xFF}, /* TERM_VIOLET */ {0x00, 0xFF, 0xFF, 0x00}, /* TERM_YELLOW */ {0x00, 0xFF, 0x00, 0x00}, /* TERM_L_RED */ {0x00, 0x00, 0xFF, 0x00}, /* TERM_L_GREEN */ {0x00, 0x00, 0xFF, 0xFF}, /* TERM_L_BLUE */ {0x00, 0xC0, 0x80, 0x40} /* TERM_L_UMBER */ }; /* * Standard sound names */ char angband_sound_name[SOUND_MAX][16] = { "", "hit", "miss", "flee", "drop", "kill", "level", "death", "study", "teleport", "shoot", "quaff", "zap", "walk", "tpother", "hitwall", "eat", "store1", "store2", "store3", "store4", "dig", "opendoor", "shutdoor", "tplevel", "scroll", "buy", "sell", "warn", "rocket", "n_kill", "u_kill", "quest", "heal", "x_heal", "bite", "claw", "m_spell", "summon", "breath", "ball", "m_heal", "atkspell", "evil", "touch", "sting", "crush", "slime", "wail", "winner", "fire", "acid", "elec", "cold", "illegal", "fail", "wakeup", "invuln", "fall", "pain", "destitem", "moan", "show", "unused", "explode", }; /* * The function pointer that is used to access the dungeon / wilderness. * It points to a simple function when in the dungeon, that evaluates * cave[y][x] * In the wilderness, things are more complicated. */ cave_type *(*area_aux) (int, int); /* * Equivalent function pointer used to get player information * for each grid. */ pcave_type *(*parea_aux) (int, int); /* * Variables used to access the scrollable wilderness. * This is designed to be as fast as possible - whilst using as little * RAM as possible to store a massive wilderness. * * The wilderness is generated "on the fly" as the player moves around it. * To save time - blocks of 16x16 squares are saved in a cache so they * don't need to be redone if the player moves back and forth. */ /* block used to generate plasma fractal for random wilderness */ u16b *temp_block[WILD_BLOCK_SIZE + 1]; /* List of 16x16 blocks in the wilderness */ blk_ptr *wild_cache; /* Reference count of each 16x16 block in the wilderness */ int **wild_refcount; /* Counter of where in the list of cache blocks we are */ u32b wc_cnt = 0; /* The wilderness itself - grid of 16x16 blocks*/ blk_ptr **wild_grid; /* The data used to generate the wilderness */ wild_type **wild; /* The seed for the wilderness */ u32b wild_seed; /* Description of wilderness block types */ wild_gen_data_type *wild_gen_data; /* The decision tree for working out what block type to pick */ wild_choice_tree_type *wild_choice_tree; /* Bounds checking function pointers */ bool (*in_bounds) (int, int); bool (*in_bounds2) (int, int); bool (*in_boundsp) (int, int); /* * Current size of the wilderness */ s32b max_wild; /* * The current global region. */ region_type cave_data; /* * Index of current global region */ int cur_region; /* * The array of dungeon items [z_info->o_max] */ object_type *o_list; /* * The array of dungeon monsters [z_info->m_max] */ monster_type *m_list; /* * The array of fields [z_info->fld_max] */ field_type *fld_list; /* * The array of regions [z_info->rg_max] */ region_type *rg_list; /* * The array of region information [z_info->rg_max] */ region_info *ri_list; /* * Number of towns used. */ u16b place_count; /* * Places in the wilderness [z_info->wp_max] */ place_type *place; /* * The size of "alloc_kind_table" (at most z_info->k_max * 4) */ s16b alloc_kind_size; /* * The entries in the "kind allocator table" */ alloc_entry *alloc_kind_table; /* * The size of "alloc_race_table" (at most z_info->r_max) */ s16b alloc_race_size; /* * The entries in the "race allocator table" */ alloc_entry *alloc_race_table; /* * The size of the "alloc_ego_table" (at most z_info->e_max) */ s16b alloc_ego_size; /* * The entries in the "ego item allocator table" */ alloc_entry *alloc_ego_table; /* * Specify attr/char pairs for visual special effects * Be sure to use "index & 0x7F" to avoid illegal access * * Can we decrease the size to 128?? */ byte misc_to_attr[256]; char misc_to_char[256]; /* * Specify attr/char pairs for inventory items (by tval) * Be sure to use "index & 0x7F" to avoid illegal access */ byte tval_to_attr[128]; char tval_to_char[128]; /* * Keymaps for each "mode" associated with each keypress. */ cptr keymap_act[KEYMAP_MODES][256]; /*** Player information ***/ /* * Static player info record */ player_type p_body; /* * Pointer to the player info */ player_type *p_ptr = &p_body; /* * Pointer to the player tables * (sex, race, class, magic) */ player_sex *sp_ptr; player_race *rp_ptr; player_class *cp_ptr; player_magic *mp_ptr; /**** Server Information ****/ server_type s_body; /* * Pointer to the server information */ server_type *svr_ptr = &s_body; /* * Structure (not array) of size limits */ maxima *z_info; /* * The vault generation arrays */ vault_type *v_info; char *v_name; char *v_text; /* * The terrain feature arrays */ feature_type *f_info; char *f_name; char *f_text; /* * The object kind arrays */ object_kind *k_info; char *k_name; char *k_text; /* * The artifact arrays */ artifact_type *a_info; char *a_name; char *a_text; /* * The ego-item arrays */ ego_item_type *e_info; char *e_name; char *e_text; /* * The monster race arrays */ monster_race *r_info; char *r_name; char *r_text; /* * The field thaumatergical array */ field_thaum *t_info; /* * Quest data array */ quest_type *quest; /* * Hack -- The special Angband "System Suffix" * This variable is used to choose an appropriate "pref-xxx" file */ cptr ANGBAND_SYS = "xxx"; /* * Path name: The main "lib" directory * This variable is not actually used anywhere in the code */ cptr ANGBAND_DIR; /* * High score files (binary) * These files may be portable between platforms */ cptr ANGBAND_DIR_APEX; /* * Bone files for player ghosts (ascii) * These files are portable between platforms */ cptr ANGBAND_DIR_BONE; /* * Binary image files for the "*_info" arrays (binary) * These files are not portable between platforms */ cptr ANGBAND_DIR_DATA; /* * Textual template files for the "*_info" arrays (ascii) * These files are portable between platforms */ cptr ANGBAND_DIR_EDIT; /* * Script files * These files are portable between platforms. */ cptr ANGBAND_DIR_SCRIPT; /* * Various extra files (ascii) * These files may be portable between platforms */ cptr ANGBAND_DIR_FILE; /* * Help files (normal) for the online help (ascii) * These files are portable between platforms */ cptr ANGBAND_DIR_HELP; /* * Help files (spoilers) for the online help (ascii) * These files are portable between platforms */ cptr ANGBAND_DIR_INFO; /* * Default user "preference" files (ascii) * These files are rarely portable between platforms */ cptr ANGBAND_DIR_PREF; /* * Savefiles for current characters (binary) * These files are portable between platforms */ cptr ANGBAND_DIR_SAVE; /* * User "preference" files (ascii) * These files are rarely portable between platforms */ cptr ANGBAND_DIR_USER; /* * Various extra files (binary) * These files are rarely portable between platforms */ cptr ANGBAND_DIR_XTRA; /* Can these inventory hacks be cleaned up somehow? */ /* * Total Hack -- allow all items to be listed (even empty ones) * This is only used by "do_cmd_inven_e()" and is cleared there. */ bool item_tester_full; /* * Here is a "pseudo-hook" used during calls to "get_item()" and * "show_inven()" and "show_equip()", and the choice window routines. */ byte item_tester_tval; /* * Here is a "hook" used during calls to "get_item()" and * "show_inven()" and "show_equip()", and the choice window routines. */ bool (*item_tester_hook) (const object_type *); /* * Current "comp" function for ang_sort() */ bool (*ang_sort_comp) (const vptr u, const vptr v, int a, int b); /* * Current "swap" function for ang_sort() */ void (*ang_sort_swap) (const vptr u, const vptr v, int a, int b); /* * Default spell color table (quark index) */ cptr gf_color[MAX_GF]; /* * Store owner table sizes */ int owner_names_max; int owner_suffix_max; /* Get rid of this? */ /* * Flags for initialization */ int init_flags; zangband/src/wild1.c0000755000000000000000000023163610250356275013336 0ustar rootroot/* File: wild1.c */ /* Purpose: Wilderness generation */ /* * Copyright (c) 1989, 1999 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "wild.h" /* * This section deals with wilderness generation * - both at the start of the game, and sorting * out what routines are used to make things as * the player moves around. * * Note that these routines return zero as a fail * indicator. They return a non zero value for * a success - the value being the node last added * to the decision tree. (This usually can be * ignored.) */ /* * This function returns a wilderness block type that fits * the required parameters. * * The set of generation types is stored in a "decision tree" * - so the required time to get the wilderness type from the * three parameters (hgt,pop,law) is proportional to log(n). * This speeds up wilderness generation alot. (Note the * "obvious" method of using a linear search to find matching * wilderness creation functions is too slow.) * * The "type" value has two different uses. One is to specify * which axis of the parameter space is being split. The other * is to show whether or not a node is a terminal "leaf" node. * If it is a leaf node - the value returned is the number of * the type of wilderness generation function. */ static u16b get_gen_type(byte hgt, byte pop, byte law) { /* Current node in choice tree - node zero is the "trunk" */ int node = 0; /* branch of tree to go down */ bool branch = TRUE; wild_choice_tree_type *tree_ptr; /* Find matching generation type */ /* The while loop is used instead of the "obvious" recursion */ while (TRUE) { /* Access Node */ tree_ptr = &wild_choice_tree[node]; /* * If are near end - look at leaves of tree * * (cutoff == 0) is used as a flag since it doesn't * split the possibility tree in any useful way. */ if (tree_ptr->cutoff == 0) { /* randomly choose branch */ if (randint1(tree_ptr->chance1 + tree_ptr->chance2) > tree_ptr->chance2) { /* Chance1 of going "left" */ branch = TRUE; } else { /* Chance2 of going "right" */ branch = FALSE; } } else { /* * Get lower two bits of type to decribe which of * (hgt,pop,law) cutoff refers to. */ switch (tree_ptr->info & 0x03) { case DT_HGT: { /* Look at height */ if (tree_ptr->cutoff >= hgt) { branch = TRUE; } else { branch = FALSE; } break; } case DT_POP: { /* Look at population */ if (tree_ptr->cutoff >= pop) { branch = TRUE; } else { branch = FALSE; } break; } case DT_LAW: { /* Look at lawfulness */ if (tree_ptr->cutoff >= law) { branch = TRUE; } else { branch = FALSE; } break; } default: { msgf("Invalid stat chosen!"); break; } } } /* Look at the proper branch of the tree */ if (branch) { /* Go "left" */ /* See if references leaf node */ if (tree_ptr->info & DT_LEFT) { /* If the bit is set - leaf */ return (tree_ptr->ptrnode1); } else { /* use the while loop to recurse */ node = tree_ptr->ptrnode1; } } else { /* Go "right" */ /* See if references leaf node */ if (tree_ptr->info & DT_RIGHT) { /* If the bit is set - leaf */ return (tree_ptr->ptrnode2); } else { /* use the while loop to recurse */ node = tree_ptr->ptrnode2; } } } /* For some dumb compilers */ return (0); } /* * The number of allocated nodes in the decision tree */ static u16b d_tree_count; /* * This function creates a new node on the decision tree. * It then connects that node to the node referenced by * the variable "node". The process of making the link * stomps on any information about the old link. * * branch == TRUE is "left", FALSE is "right" * * This function returns the location of the new node in * the decision tree array. */ static u16b create_node(u16b node, bool branch) { u16b new_node; wild_choice_tree_type *tree_ptr; if (d_tree_count >= z_info->wn_max) { /* * Return zero (known as the location of the tree's * "root" - so can act as a flag) if all of the * memory allocated has been used. * * Always check the return value - and report the error * * The number of nodes required is roughly proportional * to nlog(n) for a random input of ranges. Since the * ranges in w_info.txt are in a "nice" order, the * coefficient for this algorithmic complexity is fairly small. */ return (0); } /* Get location of new node */ new_node = d_tree_count; /* Access old node */ tree_ptr = &wild_choice_tree[node]; if (branch) { /* Link new node to left of old */ tree_ptr->ptrnode1 = new_node; /* Link is not to a leaf */ tree_ptr->info &= ~DT_LEFT; } else { /* Link new node to right of old */ tree_ptr->ptrnode2 = new_node; /* Link is not to a leaf */ tree_ptr->info &= ~DT_RIGHT; } /* Increase count of allocated nodes */ d_tree_count++; return (new_node); } /* * This function deletes the last node on the decision tree. * It is needed for a major hack when something is being added * to a "null" region. * * This routine, and the above routine are the only ones that can * modify the number of nodes in the array. (This makes checking * for the array-full case much easier.) */ static void delete_last_node(void) { d_tree_count--; } /* * This function adds a node to the tree between a "leaf" * and the rest of the tree. As nodes are added to the "leaf" * the chance of each wilderness generation type is collated. * * Note - this function used so that several different wilderness * generation types can exist within the same region in parameter * space. Each possibility has a "chance" associated with it. * At generation time - the RNG is used to determine which node * in the "leaf" of several different generation types is used. * The wilderness generation type of that node is then used. * * This function also takes care of the case when something is * being added to a "null" node. "Null" nodes of type zero * describe areas of parameter space that are outside the currently * used area. This is needed because the decision tree starts out * empty. As wilderness generation types are added, the null area * is slowly clipped away. If at the end of creating the decision * tree and there is any "null" area left, the types do not fill * parameter space. This will flag an error. */ static u16b add_node_chance(u16b type, u16b node, bool branch) { /* Is the "leaf" a tree of nodes - or a single node. */ bool is_tree; /* The node inserted into the decision tree */ u16b new_node; wild_choice_tree_type *tree_ptr; /* The old connection */ u16b old_node; tree_ptr = &wild_choice_tree[node]; if (branch) { old_node = tree_ptr->ptrnode1; /* Check for null case. */ if (old_node == 0) { /* Easy - just replace with data */ tree_ptr->ptrnode1 = type; /* Return current node. */ return (node); } /* Get left leaf status */ is_tree = (wild_choice_tree[old_node].info & DT_LEFT); } else { old_node = tree_ptr->ptrnode2; /* Check for null case. */ if (old_node == 0) { /* Easy - just replace with data */ tree_ptr->ptrnode2 = type; /* Return current node. */ return (node); } /* Get right leaf status */ is_tree = (wild_choice_tree[old_node].info & DT_RIGHT); } /* Insert new node */ new_node = create_node(node, branch); /* Error if array is full */ if (new_node == 0) { /* Return zero as error code */ return (0); } /* Access node */ tree_ptr = &wild_choice_tree[new_node]; /* Cutoff = 0 since is a leaf node */ tree_ptr->cutoff = 0; /* Connect to old leaf */ tree_ptr->ptrnode1 = old_node; /* Connect to new type */ tree_ptr->ptrnode2 = type; if (is_tree) { /* Set "info" bit-flag */ /* Only new node is a pointer to gen. type */ tree_ptr->info = DT_RIGHT; /* Calculate the chance fields */ tree_ptr->chance1 = wild_choice_tree[old_node].chance1 + wild_choice_tree[old_node].chance2; tree_ptr->chance2 = wild_gen_data[type].chance; } else { /* Set "info" bit-flag */ /* Both links are to wild. gen. types. */ tree_ptr->info = DT_LEFT | DT_RIGHT; /* Calculate the chance fields */ tree_ptr->chance1 = wild_gen_data[old_node].chance; tree_ptr->chance2 = wild_gen_data[type].chance; } /* Return location of new node if required */ return (new_node); } /* * This function copies the contents of one "leaf" (specified by * node1 + branch1) to the side of another node. * * This is needed because as the tree splits the parameter space * the leaves occupy regions. When new wild. gen. types are added * to the decision tree - the "leaves" may not match their size. * This means that the leaves need to be split - in other words * copied. */ static u16b copy_branch(u16b node1, bool branch1, u16b node2, bool branch2) { /* This function assumes that the "leaves" are of this form: * *StartNode * / \ *x Node * / \ * type Node * / \ * type Node * / \ * type type * * (Where one pointer connects to a node, and one to a wild. gen. type) */ /* * The complexity of this function is due to the large number of * possibilities: both branches can be left of right, and the node * can be terminal or not. This gives a set of nested "if's" resulting * in eight small sections of code. */ u16b new_node; u16b temp_node; wild_choice_tree_type *tree_ptr1; wild_choice_tree_type *tree_ptr2; /* point to node to be copied from */ tree_ptr1 = &wild_choice_tree[node1]; /* work out what has to be copied. */ if (branch1) { if (tree_ptr1->info & DT_LEFT) { /* need to copy tree of nodes */ /* make new node */ new_node = create_node(node2, branch2); /* Exit on failure */ if (new_node == 0) return (0); /* Point to block to copy */ temp_node = tree_ptr1->ptrnode1; tree_ptr1 = &wild_choice_tree[temp_node]; /* Point to new block */ tree_ptr2 = &wild_choice_tree[new_node]; /* Copy data to new node */ tree_ptr2->info = tree_ptr1->info; tree_ptr2->cutoff = tree_ptr1->cutoff; tree_ptr2->chance1 = tree_ptr1->chance1; tree_ptr2->chance2 = tree_ptr1->chance2; tree_ptr2->ptrnode1 = tree_ptr1->ptrnode1; tree_ptr2->ptrnode2 = tree_ptr1->ptrnode2; /* Recurse along branches to this node */ if (!(tree_ptr2->info & DT_LEFT)) { /* Recurse along "left" branch */ if (copy_branch(temp_node, TRUE, new_node, TRUE) == 0) return (0); } if (!(tree_ptr2->info & DT_RIGHT)) { /* Recurse along "right" branch */ if (copy_branch(temp_node, TRUE, new_node, TRUE) == 0) return (0); } /* Done */ return (new_node); } else { /* point to node to be copied to */ tree_ptr2 = &wild_choice_tree[node2]; /* only need to copy a single wild. gen. type */ if (branch2) { /* terminal branch */ tree_ptr2->info |= DT_LEFT; /* Copy information */ tree_ptr2->ptrnode1 = tree_ptr1->ptrnode1; tree_ptr2->chance1 = tree_ptr1->chance1; } else { /* terminal branch */ tree_ptr2->info |= DT_RIGHT; /* Copy information */ tree_ptr2->ptrnode2 = tree_ptr1->ptrnode1; tree_ptr2->chance2 = tree_ptr1->chance1; } /* done */ return (node2); } } else { if (tree_ptr1->info & DT_RIGHT) { /* need to copy tree of nodes */ /* make new node */ new_node = create_node(node2, branch2); /* Exit on failure */ if (new_node == 0) return (0); /* Point to block to copy */ temp_node = tree_ptr1->ptrnode2; tree_ptr1 = &wild_choice_tree[temp_node]; /* Point to new block */ tree_ptr2 = &wild_choice_tree[new_node]; /* Copy data to new node */ tree_ptr2->info = tree_ptr1->info; tree_ptr2->cutoff = tree_ptr1->cutoff; tree_ptr2->chance1 = tree_ptr1->chance1; tree_ptr2->chance2 = tree_ptr1->chance2; tree_ptr2->ptrnode1 = tree_ptr1->ptrnode1; tree_ptr2->ptrnode2 = tree_ptr1->ptrnode2; /* Recurse along branches to this node */ if (!(tree_ptr2->info & DT_LEFT)) { /* Recurse along "left" branch */ if (copy_branch(temp_node, TRUE, new_node, TRUE) == 0) return (0); } if (!(tree_ptr2->info & DT_RIGHT)) { /* Recurse along "right" branch */ if (copy_branch(temp_node, TRUE, new_node, TRUE) == 0) return (0); } /* Done */ return (new_node); } else { /* point to node to be copied to */ tree_ptr2 = &wild_choice_tree[node2]; /* only need to copy a single wild. gen. type */ if (branch2) { /* terminal branch */ tree_ptr2->info |= DT_LEFT; /* Copy information */ tree_ptr2->ptrnode1 = tree_ptr1->ptrnode2; tree_ptr2->chance1 = tree_ptr1->chance2; } else { /* terminal branch */ tree_ptr2->info |= DT_RIGHT; /* Copy information */ tree_ptr2->ptrnode2 = tree_ptr1->ptrnode2; tree_ptr2->chance2 = tree_ptr1->chance2; } /* done */ return (node2); } } } /* * This function is used to add a wilderness generation type within another * typed region of parameter space described by the decision tree. This is * the only function that actually extends the decision tree itself. (The * add_node_chance() function increases the size of the "leaves" though.) * * The bounding box of the bigger region (number 1) is repeatedly clipped onto * the sides of the smaller region2. This can result with up to 6 nodes being * used. Finally, when the two regions are the same size, the add_node_chance() * function is called to extend the "leaves" of the decision tree. * * This function must be called with a new empty node. The node must be * connected to the tree by the calling routine. (This "feature" is so that * this routine can be used to initialise an empty decision tree.) This means * that the calling routine must check for the "null" node + completely filled * case. XXX XXX */ static u16b add_node_inside(u16b node, u16b type1, wild_bound_box_type *bound1, u16b type2, wild_bound_box_type *bound2) { /* The node inserted into the decision tree */ u16b new_node; wild_choice_tree_type *tree_ptr; tree_ptr = &wild_choice_tree[node]; if (bound1->hgtmin != bound2->hgtmin) { /* Split node along face of region */ tree_ptr->cutoff = bound2->hgtmin; /* Excess is smaller than cutoff */ tree_ptr->ptrnode1 = type1; /* Cutoff = hgt , ptrnode1 = wild. gen. type. */ tree_ptr->info = DT_HGT | DT_LEFT; /* Wipe chance values (this probably isn't needed) */ tree_ptr->chance1 = 0; tree_ptr->chance2 = 0; /* Add new node to decision tree */ new_node = create_node(node, FALSE); /* Exit if out of space */ if (new_node == 0) return (0); /* reset node to current end of tree */ node = new_node; tree_ptr = &wild_choice_tree[node]; } if (bound1->hgtmax != bound2->hgtmax) { /* Split node along face of region */ tree_ptr->cutoff = bound2->hgtmax; /* Excess is larger than cutoff */ tree_ptr->ptrnode2 = type1; /* Cutoff = hgt , ptrnode2 = wild. gen. type. */ tree_ptr->info = DT_HGT | DT_RIGHT; /* Wipe chance values (this probably isn't needed) */ tree_ptr->chance1 = 0; tree_ptr->chance2 = 0; /* Add new node to decision tree */ new_node = create_node(node, TRUE); /* Exit if out of space */ if (new_node == 0) return (0); /* reset node to current end of tree */ node = new_node; tree_ptr = &wild_choice_tree[node]; } if (bound1->popmin != bound2->popmin) { /* Split node along face of region */ tree_ptr->cutoff = bound2->popmin; /* Excess is smaller than cutoff */ tree_ptr->ptrnode1 = type1; /* Cutoff = pop , ptrnode1 = wild. gen. type. */ tree_ptr->info = DT_POP | DT_LEFT; /* Wipe chance values (this probably isn't needed) */ tree_ptr->chance1 = 0; tree_ptr->chance2 = 0; /* Add new node to decision tree */ new_node = create_node(node, FALSE); /* Exit if out of space */ if (new_node == 0) return (0); /* reset node to current end of tree */ node = new_node; tree_ptr = &wild_choice_tree[node]; } if (bound1->popmax != bound2->popmax) { /* Split node along face of region */ tree_ptr->cutoff = bound2->popmax; /* Excess is larger than cutoff */ tree_ptr->ptrnode2 = type1; /* Cutoff = pop , ptrnode2 = wild. gen. type. */ tree_ptr->info = DT_POP | DT_RIGHT; /* Wipe chance values (this probably isn't needed) */ tree_ptr->chance1 = 0; tree_ptr->chance2 = 0; /* Add new node to decision tree */ new_node = create_node(node, TRUE); /* Exit if out of space */ if (new_node == 0) return (0); /* reset node to current end of tree */ node = new_node; tree_ptr = &wild_choice_tree[node]; } if (bound1->lawmin != bound2->lawmin) { /* Split node along face of region */ tree_ptr->cutoff = bound2->lawmin; /* Excess is smaller than cutoff */ tree_ptr->ptrnode1 = type1; /* Cutoff = law , ptrnode1 = wild. gen. type. */ tree_ptr->info = DT_LAW | DT_LEFT; /* Wipe chance values (this probably isn't needed) */ tree_ptr->chance1 = 0; tree_ptr->chance2 = 0; /* Add new node to decision tree */ new_node = create_node(node, FALSE); /* Exit if out of space */ if (new_node == 0) return (0); /* reset node to current end of tree */ node = new_node; tree_ptr = &wild_choice_tree[node]; } if (bound1->lawmax != bound2->lawmax) { /* Split node along face of region */ tree_ptr->cutoff = bound2->lawmax; /* Excess is larger than cutoff */ tree_ptr->ptrnode2 = type1; /* Cutoff = law , ptrnode2 = wild. gen. type. */ tree_ptr->info = DT_LAW | DT_RIGHT; /* Wipe chance values (this probably isn't needed) */ tree_ptr->chance1 = 0; tree_ptr->chance2 = 0; /* Add new node to decision tree */ new_node = create_node(node, TRUE); /* Exit if out of space */ if (new_node == 0) return (0); /* reset node to current end of tree */ node = new_node; tree_ptr = &wild_choice_tree[node]; } /* * "null" case - don't need the extra node. * Hack - delete extra node and go up one (should be the last node on the * array.) XXX XXX XXX * Once there - look for "null" type on _one_ branch. * The other branch was previously a link to the now deleted node. * Replace that link with the wilderness gen. type. * * This only works because we know that at least one node was added * to the bottom of the array. This is why this routine must never * be called with a "null" region the same size as the region to be * added. */ if (type1 == 0) { /* Delete last node on array - and move back one. */ delete_last_node(); node--; tree_ptr = &wild_choice_tree[node]; /* look "left" for null */ if (tree_ptr->ptrnode1 == 0) { /* Paranoia - check for both branches null */ if (tree_ptr->ptrnode2 == 0) return (0); /* link to wild. gen. type. */ tree_ptr->ptrnode2 = type2; /* right branch is to a wild. gen. type - not a node. */ tree_ptr->info |= DT_RIGHT; /* Done */ return (node); } /* look "right" for null */ if (tree_ptr->ptrnode2 == 0) { /* Paranoia - check for both branches null */ if (tree_ptr->ptrnode1 == 0) return (0); /* link to wild. gen. type. */ tree_ptr->ptrnode1 = type2; /* left branch is to a wild. gen. type - not a node. */ tree_ptr->info |= DT_LEFT; /* Done */ return (node); } } /* * Have two wild. gen. types that want to be in the same region of * parameter space. This is accomedated by using the "chance" fields. * chance1 of going "left", and chance2 of going "right". This state * is flagged by having cutoff == 0. */ /* Set flag for existance of "chance" fields. */ tree_ptr->cutoff = 0; /* connect to wild. gen. types */ tree_ptr->ptrnode1 = type1; tree_ptr->ptrnode2 = type2; /* Set info flag to show both branches are "leaves" */ tree_ptr->info = DT_LEFT | DT_RIGHT; /* Look up chances and add to node. */ tree_ptr->chance1 = wild_gen_data[type1].chance; tree_ptr->chance2 = wild_gen_data[type2].chance; /* Done */ return (node); } /* * This routine compares two bounding boxes * and returns true if they are the same. */ static bool compare_bounds(wild_bound_box_type *bound1, wild_bound_box_type *bound2) { return ((bound2->hgtmin == bound1->hgtmin) && (bound2->hgtmax == bound1->hgtmax) && (bound2->popmin == bound1->popmin) && (bound2->popmax == bound1->popmax) && (bound2->lawmin == bound1->lawmin) && (bound2->lawmax == bound1->lawmax)); } /* * This function adds a type within a leaf that has a bigger bounding box. * This means that the nodes containing the leaf are copied several times * until the bounding boxes match - and the node can be added to the leaf. * * This function is similar to the above one - except that the input is * node + branch rather than just node. (This is to simplify the copying * function.) */ static u16b inside_leaf(u16b node, u16b type, wild_bound_box_type *bound1, wild_bound_box_type *bound2, bool branch) { /* The node inserted into the decision tree */ u16b new_node; u16b branch_node; wild_choice_tree_type *tree_ptr; tree_ptr = &wild_choice_tree[node]; if (bound1->hgtmin != bound2->hgtmin) { /* Record branch node */ if (branch) { branch_node = tree_ptr->ptrnode1; } else { branch_node = tree_ptr->ptrnode2; } /* Make empty node connected along branch */ new_node = create_node(node, branch); if (new_node == 0) return (0); /* Reconnect to new node */ tree_ptr = &wild_choice_tree[new_node]; tree_ptr->ptrnode1 = branch_node; /* Copy so that leaf is duplicated */ if (copy_branch(new_node, TRUE, new_node, FALSE) == 0) return (0); /* Split node along face of region */ tree_ptr->cutoff = bound2->hgtmin; /* Cutoff = hgt */ tree_ptr->info = DT_HGT; /* work out branch to follow */ branch = FALSE; } if (bound1->hgtmax != bound2->hgtmax) { /* Record branch node */ if (branch) { branch_node = tree_ptr->ptrnode1; } else { branch_node = tree_ptr->ptrnode2; } /* Make empty node connected along branch */ new_node = create_node(node, branch); if (new_node == 0) return (0); /* Reconnect to new node */ tree_ptr = &wild_choice_tree[new_node]; tree_ptr->ptrnode1 = branch_node; /* Copy so that leaf is duplicated */ if (copy_branch(new_node, TRUE, new_node, FALSE) == 0) return (0); /* Split node along face of region */ tree_ptr->cutoff = bound2->hgtmax; /* Cutoff = hgt */ tree_ptr->info = DT_HGT; /* work out branch to follow */ branch = TRUE; } if (bound1->popmin != bound2->popmin) { /* Record branch node */ if (branch) { branch_node = tree_ptr->ptrnode1; } else { branch_node = tree_ptr->ptrnode2; } /* Make empty node connected along branch */ new_node = create_node(node, branch); if (new_node == 0) return (0); /* Reconnect to new node */ tree_ptr = &wild_choice_tree[new_node]; tree_ptr->ptrnode1 = branch_node; /* Copy so that leaf is duplicated */ if (copy_branch(new_node, TRUE, new_node, FALSE) == 0) return (0); /* Split node along face of region */ tree_ptr->cutoff = bound2->popmin; /* Cutoff = pop */ tree_ptr->info = DT_POP; /* work out branch to follow */ branch = FALSE; } if (bound1->popmax != bound2->popmax) { /* Record branch node */ if (branch) { branch_node = tree_ptr->ptrnode1; } else { branch_node = tree_ptr->ptrnode2; } /* Make empty node connected along branch */ new_node = create_node(node, branch); if (new_node == 0) return (0); /* Reconnect to new node */ tree_ptr = &wild_choice_tree[new_node]; tree_ptr->ptrnode1 = branch_node; /* Copy so that leaf is duplicated */ if (copy_branch(new_node, TRUE, new_node, FALSE) == 0) return (0); /* Split node along face of region */ tree_ptr->cutoff = bound2->popmax; /* Cutoff = pop */ tree_ptr->info = DT_POP; /* work out branch to follow */ branch = TRUE; } if (bound1->lawmin != bound2->lawmin) { /* Record branch node */ if (branch) { branch_node = tree_ptr->ptrnode1; } else { branch_node = tree_ptr->ptrnode2; } /* Make empty node connected along branch */ new_node = create_node(node, branch); if (new_node == 0) return (0); /* Reconnect to new node */ tree_ptr = &wild_choice_tree[new_node]; tree_ptr->ptrnode1 = branch_node; /* Copy so that leaf is duplicated */ if (copy_branch(new_node, TRUE, new_node, FALSE) == 0) return (0); /* Split node along face of region */ tree_ptr->cutoff = bound2->lawmin; /* Cutoff = law */ tree_ptr->info = DT_LAW; /* work out branch to follow */ branch = FALSE; } if (bound1->lawmax != bound2->lawmax) { /* Record branch node */ if (branch) { branch_node = tree_ptr->ptrnode1; } else { branch_node = tree_ptr->ptrnode2; } /* Make empty node connected along branch */ new_node = create_node(node, branch); if (new_node == 0) return (0); /* Reconnect to new node */ tree_ptr = &wild_choice_tree[new_node]; tree_ptr->ptrnode1 = branch_node; /* Copy so that leaf is duplicated */ if (copy_branch(new_node, TRUE, new_node, FALSE) == 0) return (0); /* Split node along face of region */ tree_ptr->cutoff = bound2->lawmax; /* Cutoff = law */ tree_ptr->info = DT_LAW; /* work out branch to follow */ branch = TRUE; } /* Finally - merge wild. gen. type with leaf of the same size */ return (add_node_chance(type, node, branch)); } /* * This function copies the parameter bounds from one variable to another. */ static void copy_bounds(wild_bound_box_type *bound1, wild_bound_box_type *bound2) { bound2->hgtmin = bound1->hgtmin; bound2->hgtmax = bound1->hgtmax; bound2->popmin = bound1->popmin; bound2->popmax = bound1->popmax; bound2->lawmin = bound1->lawmin; bound2->lawmax = bound1->lawmax; } /* * Add a wilderness generation function to the decision tree. * * There are many special cases to take care of here. First the * current tree is followed until the required region either * 1) Is split * 2) Is subsumed inside a "leaf" node. * 3) Takes over a "null" node. * * Note: Null nodes exist because no generation routine covers the * whole parameter space. This means that the inital state of the * decision tree does not cover every case. Therefore, as nodes are * added - checks are made to see if the region falls ouside of the * current "reach" of the tree. */ static u16b add_node(wild_bound_box_type *bound, wild_bound_box_type *cur_bound, u16b type, u16b node) { /* * Temp storage of the current bounds and current type bounds * (Used in splitting a region that overlaps a cutoff) */ wild_bound_box_type temp_bound1; wild_bound_box_type temp_bound2; u16b oldnode = node; bool branch = FALSE; wild_choice_tree_type *tree_ptr; /* Scan tree until hit a leaf or split required region */ /* Use a while loop instead of recursion to follow tree */ while (TRUE) { /* Access Node */ tree_ptr = &wild_choice_tree[node]; /* If are near end - look at leaves of tree * * (cutoff == 0) is used as a flag since it doesn't * split the possibility tree in any useful way. */ if (tree_ptr->cutoff == 0) { /* leaf node */ return (inside_leaf(oldnode, type, bound, cur_bound, branch)); } else { /* * Get lower two bits of type to decribe which of * (hgt,pop,law) cutoff refers to. */ switch (tree_ptr->info & 3) { case DT_HGT: { /* Look at height */ if (tree_ptr->cutoff >= bound->hgtmax) { branch = TRUE; cur_bound->hgtmax = tree_ptr->cutoff; } else if (tree_ptr->cutoff <= bound->hgtmin) { branch = FALSE; cur_bound->hgtmin = tree_ptr->cutoff; } else { /* make backups before recursion */ copy_bounds(bound, &temp_bound1); copy_bounds(cur_bound, &temp_bound2); /* upper bound = cutoff */ temp_bound1.hgtmax = tree_ptr->cutoff; temp_bound2.hgtmax = tree_ptr->cutoff; /* rescan with smaller domain */ if (!add_node(&temp_bound1, &temp_bound2, type, node)) return (0); /* make backups before recursion */ copy_bounds(bound, &temp_bound1); copy_bounds(cur_bound, &temp_bound2); /* lower bound = cutoff */ temp_bound1.hgtmin = tree_ptr->cutoff; temp_bound2.hgtmin = tree_ptr->cutoff; /* rescan with smaller domain */ return (add_node (&temp_bound1, &temp_bound2, type, node)); } break; } case DT_POP: { /* Look at population */ if (tree_ptr->cutoff >= bound->popmax) { branch = TRUE; cur_bound->popmax = tree_ptr->cutoff; } else if (tree_ptr->cutoff <= bound->popmin) { branch = FALSE; cur_bound->popmin = tree_ptr->cutoff; } else { /* make backups before recursion */ copy_bounds(bound, &temp_bound1); copy_bounds(cur_bound, &temp_bound2); /* upper bound = cutoff */ temp_bound1.popmax = tree_ptr->cutoff; temp_bound2.popmax = tree_ptr->cutoff; /* rescan with smaller domain */ if (!add_node(&temp_bound1, &temp_bound2, type, node)) return (0); /* make backups before recursion */ copy_bounds(bound, &temp_bound1); copy_bounds(cur_bound, &temp_bound2); /* lower bound = cutoff */ temp_bound1.popmin = tree_ptr->cutoff; temp_bound2.popmin = tree_ptr->cutoff; /* rescan with smaller domain */ return (add_node (&temp_bound1, &temp_bound2, type, node)); } break; } case DT_LAW: { /* Look at lawfulness */ if (tree_ptr->cutoff >= bound->lawmax) { branch = TRUE; cur_bound->lawmax = tree_ptr->cutoff; } else if (tree_ptr->cutoff <= bound->lawmin) { branch = FALSE; cur_bound->lawmin = tree_ptr->cutoff; } else { /* make backups before recursion */ copy_bounds(bound, &temp_bound1); copy_bounds(cur_bound, &temp_bound2); /* upper bound = cutoff */ temp_bound1.lawmax = tree_ptr->cutoff; temp_bound2.lawmax = tree_ptr->cutoff; /* rescan with smaller domain */ if (!add_node(&temp_bound1, &temp_bound2, type, node)) return (0); /* make backups before recursion */ copy_bounds(bound, &temp_bound1); copy_bounds(cur_bound, &temp_bound2); /* lower bound = cutoff */ temp_bound1.lawmin = tree_ptr->cutoff; temp_bound2.lawmin = tree_ptr->cutoff; /* rescan with smaller domain */ return (add_node (&temp_bound1, &temp_bound2, type, node)); } break; } default: { msgf("Info - %d", tree_ptr->info); msgf("Invalid stat chosen!"); break; } } } /* Look at the proper branch of the tree */ if (branch) { /* Go "left" */ /* See if references leaf node */ if (tree_ptr->info & DT_LEFT) { /* Hit leaf node */ /* store connection */ oldnode = tree_ptr->ptrnode1; /* Take care of null case */ if ((oldnode == 0) && compare_bounds(cur_bound, bound)) { /* simply set the branch to point to the wild. gen. type */ tree_ptr->ptrnode1 = type; /* * Done - don't return zero as can happen * with the root node */ return (1); } /* Make new node */ node = create_node(node, TRUE); if (node == 0) return (0); return (add_node_inside(node, oldnode, cur_bound, type, bound)); } else { /* use the while loop to recurse */ oldnode = node; node = tree_ptr->ptrnode1; } } else { /* Go "right" */ /* See if references leaf node */ if (tree_ptr->info & DT_RIGHT) { /* Hit leaf node */ /* store connection */ oldnode = tree_ptr->ptrnode2; /* Take care of null case */ if ((oldnode == 0) && compare_bounds(cur_bound, bound)) { /* simply set the branch to point to the wild. gen. type */ tree_ptr->ptrnode2 = type; /* done - don't return zero as can happen with the root node */ return (1); } /* Make new node */ node = create_node(node, FALSE); if (node == 0) return (0); return (add_node_inside(node, oldnode, cur_bound, type, bound)); } else { /* use the while loop to recurse */ oldnode = node; node = tree_ptr->ptrnode2; } } } } /* * Initialise the decision tree with the first wilderness generation type. */ u16b init_choice_tree(wild_bound_box_type *bound, u16b type) { wild_bound_box_type start_bounds; /* The decision tree has one (empty) node */ d_tree_count = 1; /* * Set the starting bounds of the decision tree - this covers * the whole parameter space used by the wilderness generation * types. */ start_bounds.hgtmin = 0; start_bounds.hgtmax = 255; start_bounds.popmin = 0; start_bounds.popmax = 255; start_bounds.lawmin = 0; start_bounds.lawmax = 255; /* Assume first node is cleared by C_MAKE */ /* * Start the tree off by adding the type within a "null" region covering * the whole parameter space. (Note this routine requires one empty node. * - that is why d_tree_count starts out as one.) */ return (add_node_inside(0, 0, &start_bounds, type, bound)); } u16b add_node_tree_root(wild_bound_box_type *bound, u16b type) { /* default bounds */ wild_bound_box_type start_bounds; start_bounds.hgtmin = 0; start_bounds.hgtmax = 255; start_bounds.popmin = 0; start_bounds.popmax = 255; start_bounds.lawmin = 0; start_bounds.lawmax = 255; /* Add to root of tree */ return (add_node(bound, &start_bounds, type, 0)); } /* * Debug code for the wilderness decision tree. */ #ifdef DEBUG void test_decision_tree(void) { byte hgt, pop, law; u16b type; /* get parameters */ msgf("Type in hgt"); hgt = (byte)get_quantity(NULL, 255); msgf("Type in pop"); pop = (byte)get_quantity(NULL, 255); msgf("Type in law"); law = (byte)get_quantity(NULL, 255); /* Get value from decision tree */ type = get_gen_type(hgt, pop, law); msgf("Type returned: %d .", type); } #endif /* DEBUG */ #if 0 /* * "Testing" function, used to find where the "invisible monster" bug * is being caused. * This tests the wilderness to see if everything is ok in the monster- * wilderness data structures. */ void test_mon_wild_integrity(void) { int i, j; cave_type *c_ptr; monster_type *m_ptr; /* Only when in wilderness */ if (p_ptr->depth) return; /* Check the wilderness */ for (i = min_wid; i < max_wid; i++) { for (j = min_hgt; j < max_hgt; j++) { /* Point to location */ c_ptr = area(i, j); /* Want a monster */ if (!c_ptr->m_idx) continue; m_ptr = &m_list[c_ptr->m_idx]; /* Dead monster? */ if (!m_ptr->r_idx) { msgf("Dead Monster"); } if (c_ptr->m_idx > m_max) { msgf("Monster index inconsistancy."); } if ((m_ptr->fy != j) || (m_ptr->fx != i)) { msgf("Monster location inconsistancy."); msgf("Monster x, cave x,%d,%d", m_ptr->fx, i); msgf("Monster y, cave y,%d,%d", m_ptr->fy, j); } } } } #endif /* 0 */ /* * Test to see that there are no null nodes in the decision tree. */ static void test_wild_data(void) { int i; for (i = 0; i < d_tree_count; i++) { if ((wild_choice_tree[i].ptrnode1 == 0) || (wild_choice_tree[i].ptrnode2 == 0)) { msgf("Missing value at %d ", i); msgf("Cutoff %d ", wild_choice_tree[i].cutoff); /* * The "missing value" will be close to the error in * w_info.txt * * The cutoff provides a hint as to the lawmax value of * the error. (Note - if this is zero - the error is * in a "leaf" and is a bug in the code.) */ } } } /* * The rest of the wilderness creation routines * These deal with actually making the wilderness, * not merely picking which terrain satisfies a * set of constraints. */ /* * Is the specified place able to be connected by roads? */ static bool is_road_place(u16b place_num) { place_type *pl_ptr = &place[place_num]; switch (pl_ptr->type) { case TOWN_QUEST: { /* No roads to wilderness quests */ return (FALSE); } case TOWN_DUNGEON: { dun_type *d_ptr = pl_ptr->dungeon; wild_gen2_type *w_ptr = &wild[pl_ptr->y][pl_ptr->x].trans; if (w_ptr->law_map + w_ptr->pop_map < 256) { /* Can we connect a track? */ if (d_ptr->flags & DF_TRACK) return (TRUE); } else { /* Can we connect a road? */ if (d_ptr->flags & (DF_ROAD)) return (TRUE); } /* No roads here */ return (FALSE); } default: { /* Default to true otherwise */ return (TRUE); } } } /* * Link two points in the wilderness with a road */ static void road_link(u16b x1, u16b y1, u16b x2, u16b y2) { s16b xn, yn, i; s16b dx, dy, changex, changey; u16b dist = distance(x1, y1, x2, y2); wild_gen2_type *w_ptr; if (dist > 6) { /* Divide path in half and call routine twice. */ dx = (x2 - x1) / 2; dy = (y2 - y1) / 2; if (dy != 0) { /* perturbation perpendicular to path */ changex = randint1(ABS(dy)) - ABS(dy) / 2; } else { changex = 0; } if (dx != 0) { /* perturbation perpendicular to path */ changey = randint1(ABS(dx)) - ABS(dx) / 2; } else { changey = 0; } xn = x1 + dx + changex; yn = y1 + dy + changey; /* Bounds checking */ if (xn < 0) xn = 0; if (yn < 0) yn = 0; if (xn >= max_wild) xn = max_wild - 1; if (yn >= max_wild) yn = max_wild - 1; /* Link the roads up */ road_link(x1, y1, xn, yn); road_link(xn, yn, x2, y2); /* Done */ return; } /* Hack - not too small */ if (dist < 2) return; /* Connect the road */ for (i = 0; i <= dist; i++) { xn = x1 + i * (x2 - x1) / dist; yn = y1 + i * (y2 - y1) / dist; w_ptr = &wild[yn][xn].trans; /* No bridges over acid or lava */ if (w_ptr->info & (WILD_INFO_LAVA | WILD_INFO_ACID)) continue; /* Not over ocean */ if (w_ptr->hgt_map < 256 / SEA_FRACTION) continue; /* Add the road to the wilderness */ if (w_ptr->law_map + w_ptr->pop_map < 256) { w_ptr->info |= WILD_INFO_TRACK; } else { w_ptr->info |= WILD_INFO_ROAD; } } } /* * Try to find a connecting square to a place. * * x and y point to a square outside the place. * A line is drawn from that point to the place. * The "gate" closest to the point where this * imaginary line crosses the town border is then * stored into x, y. (Wilderness coords) */ static void road_connect(u16b *x, u16b *y, u16b place_num) { place_type *pl_ptr = &place[place_num]; /* Big distance */ int dist = max_wild * 2; int cdist, k; u16b x1 = *x, y1 = *y; /* Check place type */ if (pl_ptr->type == TOWN_FRACT) { for (k = 0; k < MAX_GATES; k++) { /* Get distance from gate to target square */ cdist = distance(x1, y1, pl_ptr->x + pl_ptr->gates_x[k] / 2, pl_ptr->y + pl_ptr->gates_y[k] / 2); if (cdist < dist) { /* save minimal path */ dist = cdist; switch (k) { case 0: { *x = pl_ptr->x + pl_ptr->gates_x[0] / 2; *y = pl_ptr->y + pl_ptr->gates_y[0] / 2; break; } case 1: { *x = pl_ptr->x + pl_ptr->gates_x[1] / 2; *y = pl_ptr->y + pl_ptr->gates_y[1] / 2; break; } case 2: { *x = pl_ptr->x + pl_ptr->gates_x[2] / 2; *y = pl_ptr->y + pl_ptr->gates_y[2] / 2; break; } case 3: { *x = pl_ptr->x + pl_ptr->gates_x[3] / 2; *y = pl_ptr->y + pl_ptr->gates_y[3] / 2; break; } } } } /* Done */ return; } /* Dodgy hack = just output median place square */ *x = pl_ptr->x + pl_ptr->xsize / 2; *y = pl_ptr->y + pl_ptr->ysize / 2; } /* * Create the roads in the wildernes. * * Link towns that are close together. * * Look for good places to place "crossroads" */ static void create_roads(void) { u16b i, j, places = 0, links = 0; u16b x1, x2, x3, y1, y2, y3; s16b place1, place2, place3, place4; u16b dist, dist2, max_dist; u16b **link_list; u16b *place_number; /* Find number of linkable towns */ for (i = 1; i < place_count; i++) { if (is_road_place(i)) { /* Increment number of places */ places++; } } /* Make places x places array of u16b's */ C_MAKE(link_list, places, u16b *); for (i = 0; i < places; i++) { C_MAKE(link_list[i], places, u16b); } /* Place lookup table */ C_MAKE(place_number, places, u16b); /* Fill the lookup table */ places = 0; for (i = 1; i < place_count; i++) { if (is_road_place(i)) { place_number[places] = i; /* Increment number of places */ places++; } } /* Tabulate distances less than ROAD_DIST */ for (i = 0; i < places; i++) { for (j = i + 1; j < places; j++) { /* Get distance */ dist = distance(place[place_number[i]].x, place[place_number[i]].y, place[place_number[j]].x, place[place_number[j]].y); /* Only save it if the distance is smaller than ROAD_DIST */ if (dist < ROAD_DIST) { link_list[j][i] = dist; link_list[i][j] = dist; links += 2; } } } /* While there are unconnected links left */ while (links) { max_dist = ROAD_DIST; place1 = -1; place2 = -1; /* Find the shortest link */ for (i = 0; i < places; i++) { for (j = i + 1; j < places; j++) { /* Get distance */ dist = link_list[j][i]; /* Already linked or no link at all? */ if (!dist) continue; if (dist < max_dist) { /* This link is better */ max_dist = dist; place1 = i; place2 = j; } } } /* No third town yet */ place3 = -1; /* Reset max distance so we can find another town */ max_dist = ROAD_DIST; /* * Compare the connections for the two places to see * if they share a connection in common. * * Pick the shortest such dual link. */ for (i = 0; i < places; i++) { /* Want a new town */ if ((i == place1) || (i == place2)) continue; /* Distance from place1 to the new place */ dist = link_list[place1][i]; /* No link? */ if (!dist) continue; /* Distance from place2 to the new place */ dist2 = link_list[place2][i]; /* No link? */ if (!dist2) continue; if ((dist2 == ROAD_DIST * 2 + 1) && (link_list[i][place1] != ROAD_DIST * 2 + 1)) { /* Prevent "overlinking" with third place */ link_list[i][place1] = ROAD_DIST * 2 + 1; link_list[place1][i] = ROAD_DIST * 2 + 1; links -= 2; } if ((dist == ROAD_DIST * 2 + 1) && (link_list[i][place2] != ROAD_DIST * 2 + 1)) { /* Prevent "overlinking" with third place */ link_list[i][place2] = ROAD_DIST * 2 + 1; link_list[place2][i] = ROAD_DIST * 2 + 1; links -= 2; } /* There is a link! */ if (dist + dist2 < max_dist) { /* Save the possible cross-roads partner */ place3 = i; /* Update distance so we pick the closest three places */ max_dist = dist + dist2; } } if (place3 != -1) { /* Mark places as connected to each other */ link_list[place1][place2] = ROAD_DIST * 2 + 1; link_list[place1][place3] = ROAD_DIST * 2 + 1; link_list[place2][place1] = ROAD_DIST * 2 + 1; link_list[place2][place3] = ROAD_DIST * 2 + 1; link_list[place3][place1] = ROAD_DIST * 2 + 1; link_list[place3][place2] = ROAD_DIST * 2 + 1; /* Decrement link total */ links -= 6; /* Have a triangle of connected places */ place1 = place_number[place1]; place2 = place_number[place2]; place3 = place_number[place3]; /* Find midpoint */ x2 = (place[place1].x + place[place2].x + place[place3].x) / 3; y2 = (place[place1].y + place[place2].y + place[place3].y) / 3; /* Connect the three places to the midpoint */ x1 = x2; y1 = y2; /* Get connection square for place1 */ road_connect(&x1, &y1, place1); /* Link place1 with the midpoint */ road_link(x1, y1, x2, y2); x1 = x2; y1 = y2; /* Get connection square for place2 */ road_connect(&x1, &y1, place2); /* Link place2 with the midpoint */ road_link(x1, y1, x2, y2); x1 = x2; y1 = y2; /* Get connection square for place3 */ road_connect(&x1, &y1, place3); /* Link place3 with the midpoint */ road_link(x1, y1, x2, y2); } else { dist = link_list[place1][place2]; max_dist = (dist / 2) + 1; /* Mark the two places as connected to each other */ link_list[place1][place2] = ROAD_DIST * 2 + 1; link_list[place2][place1] = ROAD_DIST * 2 + 1; /* Decrement link total */ links -= 2; ; /* Hack - save the place number in link_list */ place3 = place1; place4 = place2; /* Hack - set j to be zero */ j = 0; place1 = place_number[place1]; place2 = place_number[place2]; /* Get first point */ x1 = place[place2].x; y1 = place[place2].y; /* Get second point */ x2 = place[place2].x; y2 = place[place2].y; /* * In some cases, the road will "run into" other places. * The following code hopefully checks for that. */ for (i = 0; i < places; i++) { /* Ignore the places we want to connect */ if ((i == place3) || (i == place4)) continue; /* Get location of the current place */ x3 = place[place_number[i]].x; y3 = place[place_number[i]].y; /* See if is close */ if ((distance(x1, y1, x3, y3) > max_dist) && (distance(x2, y2, x3, y3) > max_dist)) continue; /* See if the place is "in the way" */ if (dist_to_line(x3, y3, x1, y1, x2, y2) > dist / ROAD_MIN) { continue; } /* We have a problem - set j to be 1 (a flag) */ j = 1; /* Exit */ break; } /* If there are no problems - link the two places */ if (j == 0) { x1 = place[place2].x; y1 = place[place2].y; /* Get connection square for place1 */ road_connect(&x1, &y1, place1); x2 = x1; y2 = y1; /* Get connection square for place2 */ road_connect(&x2, &y2, place2); /* Link the two places */ road_link(x1, y1, x2, y2); } } } /* Free the array */ for (i = 0; i < places; i++) { FREE(link_list[i]); } FREE(link_list); /* Place lookup table */ FREE(place_number); } /* * Sorting hook -- comp function -- by "wilderness height" * * We use "u" and "v" to point to arrays of "x" and "y" positions, * and sort the arrays by the value in wild[y][x].gen.hgt_map */ static bool ang_sort_comp_height(vptr u, vptr v, int a, int b) { s16b *x = (s16b *)(u); s16b *y = (s16b *)(v); int ha, hb; /* Get heights */ ha = wild[y[a]][x[a]].trans.hgt_map; hb = wild[y[b]][x[b]].trans.hgt_map; /* Compare them */ return (ha >= hb); } /* * Sorting hook -- swap function -- by "wilderness height" * * We use "u" and "v" to point to arrays of "x" and "y" positions, * and sort the arrays by the value in wild[y][x].gen.hgt_map */ static void ang_sort_swap_height(vptr u, vptr v, int a, int b) { s16b *x = (s16b *)(u); s16b *y = (s16b *)(v); s16b temp; /* Swap "x" */ temp = x[a]; x[a] = x[b]; x[b] = temp; /* Swap "y" */ temp = y[a]; y[a] = y[b]; y[b] = temp; } /* * Make river between two points. * Do not change the value of the two points */ static void link_river(int x1, int y1, int x2, int y2) { int xn, yn; int x, y, dx, dy, changex, changey; int length, l; length = distance(x1, y1, x2, y2); if (length > 6) { /* * Divide path in half and call routine twice. * There is a small chance of splitting the river */ dx = (x2 - x1) / 2; dy = (y2 - y1) / 2; if (dy != 0) { /* perturbation perpendicular to path */ changex = randint1(ABS(dy)) - ABS(dy) / 2; } else { changex = 0; } if (dx != 0) { /* perturbation perpendicular to path */ changey = randint1(ABS(dx)) - ABS(dx) / 2; } else { changey = 0; } xn = x1 + dx + changex; yn = y1 + dy + changey; /* Bounds checking */ if (xn < 0) xn = 0; if (yn < 0) yn = 0; if (xn >= max_wild) xn = max_wild - 1; if (yn >= max_wild) yn = max_wild - 1; /* construct river out of two smaller ones */ link_river(x1, y1, xn, yn); link_river(xn, yn, x2, y2); } else { /* Actually build the river */ for (l = 0; l < length; l++) { x = x1 + l * (x2 - x1) / length; y = y1 + l * (y2 - y1) / length; /* Set the river flag */ wild[y][x].trans.info |= WILD_INFO_WATER; } } } /* * Make a few rivers in the wilderness. * * This is done by generating a few random "starting points" * The highest closest points are connected by a fractal line. * This is repeated until the highest point is below sea level. */ static void create_rivers(void) { int i, cur_posn, high_posn, dh, river_start; int cx, cy, ch; int r1, r2; long val, h_val; /* Number of river starting points. */ river_start = RIVER_NUM * RIVER_NUM; /* paranoia - bounds checking */ if (river_start > TEMP_MAX) river_start = TEMP_MAX; /* Make some random starting positions */ for (i = 0; i < river_start; i++) { /* Evenly spread out the points */ r1 = ((i % RIVER_NUM) * max_wild) / RIVER_NUM; r2 = r1 + (max_wild / RIVER_NUM); temp_y[i] = (s16b)rand_range(r1, r2); r1 = ((i / RIVER_NUM) * max_wild) / RIVER_NUM; r2 = r1 + (max_wild / RIVER_NUM); temp_x[i] = (s16b)rand_range(r1, r2); } temp_n = river_start; /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_height; ang_sort_swap = ang_sort_swap_height; /* Sort positions by height of wilderness */ ang_sort(temp_x, temp_y, temp_n); /* Start at highest position */ cur_posn = 0; cx = temp_x[cur_posn]; cy = temp_y[cur_posn]; ch = wild[cy][cx].trans.hgt_map; /* * Link highest position to closest next highest position. * Stop when all positions above sea level are used, or * (rarely) if there is only one left in the array. */ while ((ch > (256 / SEA_FRACTION)) && (temp_n > cur_posn + 1)) { /* The highest position is at (0,0) in the array. */ /* Find the closest next highest one. */ high_posn = cur_posn + 1; /* Large value that should be bigger than anything below. */ h_val = 10000; /* Check the other positions in the array */ for (i = high_posn; i < temp_n; i++) { /* Hack - ignore deltas that already have been matched */ if ((temp_x[i] == -1) || (temp_y[i] == -1)) continue; /* Change in Height */ dh = ch - wild[temp_y[i]][temp_x[i]].trans.hgt_map; /* Small val for close high positions */ /*val = dh + distance(cx, cy, temp_x[i], temp_y[i]); */ val = distance(cx, cy, temp_x[i], temp_y[i]); /* Is this position better than previous best? */ if (val < h_val) { h_val = val; high_posn = i; } } /* No match */ if (h_val == 10000) break; /* Make river between two points */ link_river(cx, cy, temp_x[high_posn], temp_y[high_posn]); /* * Mega hack - flag below sea level points * to stop "deltas" being made. */ if (wild[temp_y[high_posn]][temp_x[high_posn]].trans.hgt_map < (256 / SEA_FRACTION)) { temp_x[high_posn] = -1; temp_y[high_posn] = -1; } /* Get new highest point */ cur_posn++; cx = temp_x[cur_posn]; cy = temp_y[cur_posn]; while (((cx == -1) || (cy == -1)) && (cur_posn < temp_n - 1)) { /* Ignore the point below sea level - already linked */ cur_posn++; cx = temp_x[cur_posn]; cy = temp_y[cur_posn]; } /* Hack - failure to find a new node */ if (cur_posn >= temp_n - 1) break; ch = wild[cy][cx].trans.hgt_map; } /* hack - reset viewable grid set. */ temp_n = 0; } /* * Create random lakes. * * This is done by using the frac_block routine * to build a 17x17 plasma fractal. This is interpreted * via a cutoff to make the lake. * * There are several types of lake - (water, lava and acid) * The type depends on the HPL of the location. * * Note the logic used to see that lava and acid lakes do not * overlap rivers, and that all lakes are above sea level. */ static void create_lakes(void) { int count, i, j, x, y; wild_gen2_type *w_ptr; bool river, clear; byte lake_type; /* Try LAKE_NUM times */ for (count = 0; count < LAKE_NUM; count++) { /* Make a plasma fractal */ /* Initialise temporary block */ clear_temp_block(); set_temp_corner_val(WILD_BLOCK_SIZE * 256); set_temp_mid(WILD_BLOCK_SIZE * 64); /* Generate plasma factal */ frac_block(); /* Get location */ x = randint1(max_wild - 16 - 1); y = randint1(max_wild - 16 - 1); /* Clear river flag */ river = FALSE; /* Is the area clear? */ clear = TRUE; /* Look for free space */ for (i = x; i < x + 16; i++) { /* Early exit */ if (!clear) break; for (j = y; j < y + 16; j++) { w_ptr = &wild[j][i].trans; /* If non-lake square */ if (temp_block[j - y][i - x] > WILD_BLOCK_SIZE * 128) continue; /* Below sea level? */ if (w_ptr->hgt_map <= 256 / SEA_FRACTION) { clear = FALSE; break; } if (w_ptr->info & WILD_INFO_WATER) river = TRUE; } } /* Try again somewhere else */ if (!clear) continue; /* What type of lake do we want? */ if (river) { /* Water */ lake_type = 1; } else { w_ptr = &wild[y][x].trans; if ((w_ptr->law_map > 64) || (w_ptr->pop_map > 64)) { /* Water if in lawful or populous region */ lake_type = 1; } else { if (w_ptr->hgt_map > 128) { /* Lava */ lake_type = 2; } else { /* Acid */ lake_type = 3; } } } /* Make the lake */ for (i = 0; i < 16; i++) { for (j = 0; j < 16; j++) { /* If non-lake square */ if (temp_block[j][i] > WILD_BLOCK_SIZE * 128) continue; w_ptr = &wild[j + y][i + x].trans; switch (lake_type) { case 1: { w_ptr->info |= WILD_INFO_WATER; break; } case 2: { w_ptr->info |= WILD_INFO_LAVA; break; } case 3: { w_ptr->info |= WILD_INFO_ACID; break; } } } } } } /* * Plasma routines used to build the wilderness. * These store the results scaled by a factor of 16 * (Done for a less "griddy" result.) */ /* Value used for sea-level calculation */ static u32b *wild_temp_dist; /* * this routine probably should be an inline function or a macro. */ static void store_hgtmap(int x, int y, int val) { /* bounds checking */ if (val < 0) val = 0; if ((val / 16) >= max_wild) val = (max_wild * 16) - 1; /* Save distribution information */ wild_temp_dist[val / 16]++; /* store the value in height-map format */ wild[y][x].gen.hgt_map = val; return; } /* * This function creates the first of the three parameters used to generate * the wilderness. This is done by making a plasma fractal. The distribution * of the values in the height map is stored so that they can be scaled to * generate a wilderness with an even distribution of terrain. */ static void create_hgt_map(void) { int grd; /* * Fixed point variables- these are stored as 16 x normal value * This gives 4 binary places of fractional part + 12 places of normal part */ int lstep, hstep, i, j, ii, jj, size; /* * Size is one bigger than normal blocks * because of speed of algorithm with size = 2^n + 1 */ size = max_wild - 1; /* Clear the section */ for (i = 0; i <= size; i++) { for (j = 0; j <= size; j++) { /* MAX_SHORT is a flag for "not done yet" */ wild[j][i].gen.hgt_map = MAX_SHORT; } /* Clear distribution information */ wild_temp_dist[i] = 0; } /* Set maximum correlation length to be 256 squares */ grd = 16 * 16; /* Set the corner values just in case grd > size. */ store_hgtmap(0, 0, randint0(size)); store_hgtmap(size, 0, randint0(size)); store_hgtmap(0, size, randint0(size)); store_hgtmap(size, size, randint0(size)); /* Initialize the step sizes */ lstep = hstep = size * 16; size = size * 16; /* * Fill in the square with fractal height data - * like the 'plasma fractal' in fractint. */ while (hstep > 16) { /* Halve the step sizes */ lstep = hstep; hstep /= 2; /* middle top to bottom. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = 0; j <= size; j += lstep) { /* cache values of i,j divided by 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.hgt_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_hgtmap(ii, jj, randint1(max_wild * 16)); } else { /* Average of left and right points +random bit */ store_hgtmap(ii, jj, ((wild[jj][(i - hstep) / 16].gen.hgt_map + wild[jj][(i + hstep) / 16].gen.hgt_map) / 2) + ((randint1(lstep) - hstep) / 2)); } } } } /* middle left to right. */ for (j = hstep; j <= size - hstep; j += lstep) { for (i = 0; i <= size; i += lstep) { /* cache values of i,j / 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.hgt_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_hgtmap(ii, jj, randint1(max_wild * 16)); } else { /* Average of up and down points +random bit */ store_hgtmap(ii, jj, ((wild[(j - hstep) / 16][ii].gen.hgt_map + wild[(j + hstep) / 16][ii].gen.hgt_map) / 2) + ((randint1(lstep) - hstep) / 2)); } } } } /* center. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = hstep; j <= size - hstep; j += lstep) { /* cache values of i,j / 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.hgt_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_hgtmap(ii, jj, randint1(max_wild * 16)); } else { /* average over all four corners + scale by 181 to * reduce the effect of the square grid on the shape of the fractal */ store_hgtmap(ii, jj, ((wild[(j - hstep) / 16][(i - hstep) / 16]. gen.hgt_map + wild[(j + hstep) / 16][(i - hstep) / 16].gen.hgt_map + wild[(j - hstep) / 16][(i + hstep) / 16].gen.hgt_map + wild[(j + hstep) / 16][(i + hstep) / 16].gen.hgt_map) / 4) + (((randint1(lstep) - hstep) * 181) / 256)); } } } } } } /* * this routine probably should be an inline function or a macro. */ static void store_popmap(int x, int y, int val, u16b sea) { /* bounds checking */ if (val < 0) val = 0; if ((val / 16) >= max_wild) val = (max_wild * 16) - 1; /* Save distribution information (only if not below sea level) */ if (wild[y][x].gen.hgt_map > sea) wild_temp_dist[val / 16]++; /* store the value in height-map format */ wild[y][x].gen.pop_map = val; return; } /* * This function creates the second of the three parameters used to generate * the wilderness. This is done by making a plasma fractal. */ static void create_pop_map(u16b sea) { int grd; /* * fixed point variables- these are stored as 16 x normal value * this gives 4 binary places of fractional part + 12 places of normal part */ int lstep, hstep, i, j, ii, jj, size; /* Size is one bigger than normal blocks for speed of algorithm with 2^n + 1 */ size = max_wild - 1; /* Clear the section */ for (i = 0; i <= size; i++) { for (j = 0; j <= size; j++) { /* MAX_SHORT is a flag for "not done yet" */ wild[j][i].gen.pop_map = MAX_SHORT; } /* Clear distribution information */ wild_temp_dist[i] = 0; } /* Set maximum correlation length to be 256 squares */ grd = 16 * 16; /* Set the corner values just in case grd > size. */ store_popmap(0, 0, randint0(size), sea); store_popmap(size, 0, randint0(size), sea); store_popmap(0, size, randint0(size), sea); store_popmap(size, size, randint0(size), sea); /* Initialize the step sizes */ lstep = hstep = size * 16; size = size * 16; /* * Fill in the square with fractal height data - * like the 'plasma fractal' in fractint. */ while (hstep > 16) { /* Halve the step sizes */ lstep = hstep; hstep /= 2; /* middle top to bottom. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = 0; j <= size; j += lstep) { /* cache values of i,j divided by 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.pop_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_popmap(ii, jj, randint1(max_wild * 16), sea); } else { /* Average of left and right points +random bit */ store_popmap(ii, jj, ((wild[jj][(i - hstep) / 16].gen.pop_map + wild[jj][(i + hstep) / 16].gen.pop_map) / 2) + ((randint1(lstep) - hstep) / 2), sea); } } } } /* middle left to right. */ for (j = hstep; j <= size - hstep; j += lstep) { for (i = 0; i <= size; i += lstep) { /* cache values of i,j / 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.pop_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_popmap(ii, jj, randint1(max_wild * 16), sea); } else { /* Average of up and down points +random bit */ store_popmap(ii, jj, ((wild[(j - hstep) / 16][ii].gen.pop_map + wild[(j + hstep) / 16][ii].gen.pop_map) / 2) + ((randint1(lstep) - hstep) / 2), sea); } } } } /* center. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = hstep; j <= size - hstep; j += lstep) { /* cache values of i,j / 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.pop_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_popmap(ii, jj, randint1(max_wild * 16), sea); } else { /* average over all four corners + scale by 181 to * reduce the effect of the square grid on the shape of the fractal */ store_popmap(ii, jj, ((wild[(j - hstep) / 16][(i - hstep) / 16]. gen.pop_map + wild[(j + hstep) / 16][(i - hstep) / 16].gen.pop_map + wild[(j - hstep) / 16][(i + hstep) / 16].gen.pop_map + wild[(j + hstep) / 16][(i + hstep) / 16].gen.pop_map) / 4) + (((randint1(lstep) - hstep) * 181) / 256), sea); } } } } } } /* * this routine probably should be an inline function or a macro. */ static void store_lawmap(int x, int y, int val, u16b sea) { /* bounds checking */ if (val < 0) val = 0; if ((val / 16) >= max_wild) val = (max_wild * 16) - 1; /* Save distribution information (only if not below sea level) */ if (wild[y][x].gen.hgt_map > sea) wild_temp_dist[val / 16]++; /* store the value in height-map format */ wild[y][x].gen.law_map = val; return; } /* * This function creates the third of the three parameters used to generate * the wilderness. This is done by making a plasma fractal. */ static void create_law_map(u16b sea) { int grd; /* * fixed point variables- these are stored as 16 x normal value * this gives 4 binary places of fractional part + 12 places of normal part */ int lstep, hstep, i, j, ii, jj, size; /* * Size is one bigger than normal blocks for speed of * algorithm with 2^n + 1 */ size = max_wild - 1; /* Clear the section */ for (i = 0; i <= size; i++) { for (j = 0; j <= size; j++) { /* MAX_SHORT is a flag for "not done yet" */ wild[j][i].gen.law_map = MAX_SHORT; } /* Clear distribution information */ wild_temp_dist[i] = 0; } /* Set maximum correlation length to be 256 squares */ grd = 16 * 16; /* Set the corner values just in case grd > size. */ store_lawmap(0, 0, randint0(size), sea); store_lawmap(size, 0, randint0(size), sea); store_lawmap(0, size, randint0(size), sea); store_lawmap(size, size, randint0(size), sea); /* Initialize the step sizes */ lstep = hstep = size * 16; size = size * 16; /* * Fill in the square with fractal height data - * like the 'plasma fractal' in fractint. */ while (hstep > 16) { /* Halve the step sizes */ lstep = hstep; hstep /= 2; /* middle top to bottom. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = 0; j <= size; j += lstep) { /* cache values of i,j divided by 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.law_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_lawmap(ii, jj, randint1(max_wild * 16), sea); } else { /* Average of left and right points +random bit */ store_lawmap(ii, jj, ((wild[jj][(i - hstep) / 16].gen.law_map + wild[jj][(i + hstep) / 16].gen.law_map) / 2) + ((randint1(lstep) - hstep) / 2), sea); } } } } /* middle left to right. */ for (j = hstep; j <= size - hstep; j += lstep) { for (i = 0; i <= size; i += lstep) { /* cache values of i,j / 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.law_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_lawmap(ii, jj, randint1(max_wild * 16), sea); } else { /* Average of up and down points +random bit */ store_lawmap(ii, jj, ((wild[(j - hstep) / 16][ii].gen.law_map + wild[(j + hstep) / 16][ii].gen.law_map) / 2) + ((randint1(lstep) - hstep) / 2), sea); } } } } /* center. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = hstep; j <= size - hstep; j += lstep) { /* cache values of i,j / 16 */ ii = i / 16; jj = j / 16; /* only write to points that are "blank" */ if (wild[jj][ii].gen.law_map == MAX_SHORT) { if (hstep > grd) { /* If greater than 'grid' level then is random */ store_lawmap(ii, jj, randint1(max_wild * 16), sea); } else { /* average over all four corners + scale by 181 to * reduce the effect of the square grid on the shape of the fractal */ store_lawmap(ii, jj, ((wild[(j - hstep) / 16][(i - hstep) / 16]. gen.law_map + wild[(j + hstep) / 16][(i - hstep) / 16].gen.law_map + wild[(j - hstep) / 16][(i + hstep) / 16].gen.law_map + wild[(j + hstep) / 16][(i + hstep) / 16].gen.law_map) / 4) + (((randint1(lstep) - hstep) * 181) / 256), sea); } } } } } } /* * Finish making the wilderness - recenter the screen around the player. */ static void wild_done(void) { p_ptr->px = (s16b)p_ptr->wilderness_x; p_ptr->py = (s16b)p_ptr->wilderness_y; /* Refresh random number seed */ wild_seed = randint0(0x10000000); /* Change back to inside wilderness */ p_ptr->depth = 0; /* Change to the wilderness */ change_level(0); /* We now are in the wilderness */ character_dungeon = TRUE; } /* * Make the vanilla wilderness with only the 'standard' town */ static void create_vanilla_wilderness(void) { int i, j; wild_type *w_ptr; /* Tiny wilderness */ max_wild = WILD_VIEW + 1; /* Mega Hack - make an "empty" wilderness. */ for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { /* Mega Hack - Use the 0 value (normally empty) to denote grass. */ w_ptr = &wild[j][i]; w_ptr->done.wild = 0; /* Nothing interesting here */ w_ptr->done.info = 0; /* No town yet */ w_ptr->done.place = 0; /* Monsters are easy */ w_ptr->done.mon_gen = 0; /* Monsters are fairly common */ w_ptr->done.mon_prob = 64; } } /* Make a single vanilla town. */ init_vanilla_town(); /* Done */ wild_done(); } /* * Set wilderness stats depending on town type */ static void set_mon_wild_values(byte town_type, wild_done_type *w_ptr) { /* This function is very rudimentary at the moment */ /* One and only one type of monster distribution */ switch (town_type) { case TOWN_MONST_VILLAGER: { /* Monsters are easy */ w_ptr->mon_gen = 0; /* Monsters are fairly common */ w_ptr->mon_prob = 64; break; } case TOWN_MONST_ABANDONED: { /* Monsters are moderately difficult */ w_ptr->mon_gen = 30; /* Monsters are rare */ w_ptr->mon_prob = 0; break; } case TOWN_MONST_MONST: { /* Do nothing - keep defaults */ break; } /* * Add in other probabilities in here for the * other TOWN_MONST_XXX types */ default: { /* Monsters are easy */ w_ptr->mon_gen = 0; /* Monsters are fairly common */ w_ptr->mon_prob = 64; } } } /* * Clear the wilderness */ static void wipe_wilderness(void) { int i, j; place_type *pl_ptr; wild_type *w_ptr; /* Erase all places */ for (i = 1; i < z_info->wp_max; i++) { pl_ptr = &place[i]; /* Do we have any stores? */ if (pl_ptr->numstores) { /* Free the stores */ FREE(pl_ptr->store); } /* Free the dungeon data */ if (pl_ptr->dungeon) { FREE(pl_ptr->dungeon); } /* Wipe the place */ (void)WIPE(pl_ptr, place_type); } /* Wipe the wild info */ for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { w_ptr = &wild[j][i]; /* Wipe the block */ (void)WIPE(w_ptr, wild_type); } } } /* * Create the random terrain information and fill in * the "gen" info in the wild structure. * * Using that, fill in the transition wild structure * with the height, population an "law" information. */ static void create_wild_info(int *bestx, int *besty) { int i, j; int x, y; byte hgt, pop, law; u16b hgt_min, hgt_max, pop_min, pop_max; byte sea_level; int t; long hgt_scale, pop_scale; wild_type *w_ptr; /* Huge wilderness */ max_wild = WILD_SIZE; C_MAKE(wild_temp_dist, WILD_SIZE, u32b); /* Create "height" information of wilderness */ create_hgt_map(); /* Work out extremes of height so it can be scaled. */ hgt_min = hgt_max = pop_min = pop_max = 0; /* Minimum height */ for (i = 0; i < max_wild; i++) { if (wild_temp_dist[i] != 0) { hgt_min = i; break; } } /* Maximum height */ for (i = max_wild - 1; i >= 0; i--) { if (wild_temp_dist[i] != 0) { hgt_max = i; break; } } /* Height scale factor */ hgt_scale = (hgt_max - hgt_min); /* * The sea covers 1/SEA_FRACTION of the wilderness */ sea_level = (byte)(hgt_scale / SEA_FRACTION); hgt_min *= 16; /* Create "population density" information */ create_pop_map((u16b) (sea_level * 16 + hgt_min)); /* Work out extremes of population so it can be scaled. */ /* Minimum population */ for (i = 0; i < max_wild; i++) { if (wild_temp_dist[i] != 0) { pop_min = i; break; } } /* Maximum population */ for (i = max_wild - 1; i >= 0; i--) { if (wild_temp_dist[i] != 0) { pop_max = i; break; } } /* Population scale factor */ pop_scale = (pop_max - pop_min); /* Rescale minimum. */ pop_min *= 16; create_law_map((u16b) (sea_level * 16 + hgt_min)); /* Work out extremes of "lawfulness" so it can be scaled. */ /* Calculate lawfulness map */ for (i = t = 0; i < max_wild; i++) { t += wild_temp_dist[i]; wild_temp_dist[i] = t / (max_wild * max_wild / 256); } /* Best place in wilderness for starting town */ x = -1; y = -1; /* Fill wilderness with scaled information */ for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { /* Get wilderness grid */ w_ptr = &wild[j][i]; /* * Store parameters before change the information * in the union. (Want to scale values to be 0 - 255) */ hgt = (byte)((w_ptr->gen.hgt_map - hgt_min) * 16 / hgt_scale); pop = (byte)((w_ptr->gen.pop_map - pop_min) * 16 / pop_scale); law = (byte) wild_temp_dist[w_ptr->gen.law_map / 16]; /* * Go to transition data structure */ w_ptr->trans.hgt_map = hgt; w_ptr->trans.pop_map = pop; w_ptr->trans.law_map = law; /* No town yet */ w_ptr->trans.place = 0; /* No info flags set yet */ w_ptr->trans.info = 0; /* How good is this spot to put a town? */ if ((law > 230) && (hgt > 160)) { /* Hack - Only record the first such place */ if ((x == -1) && (y == -1)) { x = i; y = j; } } } } /* Save best town location */ *bestx = x; *besty = y; /* Free the temp data */ FREE(wild_temp_dist); } /* * Fill the wilderness with terrain * * Convert from the transition structure * to the final "done" structure. */ static void create_terrain(void) { int i, j, k; int x, y; byte hgt, pop, law; wild_type *w_ptr; /* Fill wilderness with terrain */ for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { byte place_num, info; /* Get wilderness grid */ w_ptr = &wild[j][i]; /* Save town and info status */ place_num = w_ptr->trans.place; info = w_ptr->trans.info; /* Get HPL of grid */ hgt = w_ptr->trans.hgt_map; pop = w_ptr->trans.pop_map; law = w_ptr->trans.law_map; if (hgt < 256 / SEA_FRACTION) { /* Ocean */ wild[j][i].done.wild = 65535 - hgt; if (hgt > 128 / SEA_FRACTION) { /* Set to be water boundary */ w_ptr->done.info = WILD_INFO_WATER; } else { /* No rivers / roads / all unknown */ w_ptr->done.info = 0; } } else { /* Rescale the height */ hgt = hgt - 256 / SEA_FRACTION; hgt = (hgt * SEA_FRACTION) / (SEA_FRACTION - 1); /* Get wilderness type. */ w_ptr->done.wild = get_gen_type(hgt, pop, law); } /* Town */ w_ptr->done.place = place_num; /* Set wilderness monsters to default values */ /* Toughness (level 0 - 64) */ w_ptr->done.mon_gen = (256 - law) / 4; w_ptr->done.mon_gen = MAX(1, w_ptr->done.mon_gen - 5); /* No monsters (probability 0 - 16) */ w_ptr->done.mon_prob = pop / 16; if (place_num) { /* Set values depending on type of place */ set_mon_wild_values(place[place_num].monst_type, &w_ptr->done); } /* Info flags */ w_ptr->done.info = info; } } /* Create ocean boundaries (This might be very slow) */ for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { if (wild[j][i].done.wild >= WILD_SEA) { for (k = 0; k < 8; k++) { x = i + ddx_ddd[k]; y = j + ddy_ddd[k]; /* Must be in bounds */ if ((x < 0) || (x >= max_wild) || (y < 0) || (y >= max_wild)) { continue; } /* Get wilderness grid */ w_ptr = &wild[y][x]; /* Is ocean? */ if (w_ptr->done.wild < WILD_SEA) { /* * Set all squares next to ocean to be "water" * This makes the ocean boundaries look like * those of rivers - rough on the sub-block * level. */ w_ptr->done.info |= WILD_INFO_WATER; } } } } } } /* * Create the wilderness * * This is done by making three plasma fractals * The three values for each 16x16 block are then passed into * the decision tree code to get a wilderness type. (This * is done for speed. The binary tree takes O(log(n)) steps to * find a matching type from w_info.txt, a linear search will * obviously be a O(n) algorithm. With hundreds of types, the * difference is noticable. * * The old three values for height, law level, and population level * are then merged to work out the monster generation statistics for * each 16x16 block. * * Finally large features like towns, rivers, roads and lakes are placed. */ void create_wilderness(void) { int x, y; bool done = FALSE; /* Invalidate the player while we make everything */ character_dungeon = FALSE; /* * XXX XXX Hack Pretend we have a loaded player. * (Must make sure the object, monsters and fields created * now do not break the savefile, if we are half-way through * loading it.) * * We also must make sure we create the objects, monsters and fields * if we are just starting the game. */ character_loaded = TRUE; /* Delete everything */ wipe_rg_list(); /* Test wilderness generation information */ test_wild_data(); /* Minimal wilderness */ if (vanilla_town) { create_vanilla_wilderness(); return; } /* * Try to make the wilderness until we * get one that works. * * Keep track of how long it takes - and * if it takes too long, bail out. */ while (!done) { /* Clear the wilderness */ wipe_wilderness(); /* Create the height, population, and law info */ create_wild_info(&x, &y); /* * Add in large level features. */ /* Add in rivers... */ create_rivers(); /* Add in lakes... */ create_lakes(); /* Add towns + dungeons etc */ done = init_places(x, y); } /* Connect the places with roads */ create_roads(); /* * Finish everything off */ /* Convert the wilderness into the final data structure */ create_terrain(); /* * We can check the wilderness structures in debug mode - * So don't delete them in that case... */ #ifndef DEBUG /* Free up memory used to create the wilderness */ FREE(wild_choice_tree); #endif /* !DEBUG */ /* Done */ wild_done(); } zangband/src/wild2.c0000755000000000000000000017754010250356275013342 0ustar rootroot/* File: wild2.c */ /* Purpose: Wilderness generation */ /* * Copyright (c) 1989, 2003 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann, Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "wild.h" /* The starting position of the player */ static int wild_stairs_x = 0; static int wild_stairs_y = 0; /* * Building information * * Number currently created in this town * Field to place, if applicable. * Type of building * Pop, magic, law levels * Rarity */ wild_building_type wild_build[MAX_CITY_BUILD] = { {0, FT_STORE_GENERAL, BT_STORE, 100, 150, 150, 2}, {0, FT_STORE_ARMOURY, BT_STORE, 150, 150, 100, 1}, {0, FT_STORE_WEAPON, BT_STORE, 150, 150, 100, 1}, {0, FT_STORE_TEMPLE, BT_STORE, 150, 150, 200, 1}, {0, FT_STORE_ALCHEMIST, BT_STORE, 100, 150, 200, 21}, {0, FT_STORE_MAGIC, BT_STORE, 200, 150, 200, 1}, {0, FT_STORE_BLACK, BT_STORE, 250, 150, 50, 5}, {0, FT_STORE_HOME, BT_STORE, 150, 150, 150, 2}, {0, FT_STORE_BOOK, BT_STORE, 250, 150, 150, 2}, {0, 0, BT_GENERAL, 150, 150, 150, 10}, {0, FT_BUILD_WEAPON, BT_BUILD, 100, 150, 150, 5}, {0, FT_BUILD_RECHARGE, BT_BUILD, 200, 150, 150, 10}, {0, FT_BUILD_PLUS_WEAPON, BT_BUILD, 200, 150, 200, 10}, {0, FT_BUILD_PLUS_ARMOUR, BT_BUILD, 200, 150, 200, 10}, {0, FT_BUILD_MUTATE, BT_BUILD, 200, 150, 50, 20}, {0, 0, BT_GENERAL, 150, 150, 150, 1}, {0, 0, BT_GENERAL, 150, 150, 150, 1}, {0, FT_BUILD_MAP, BT_BUILD, 150, 150, 150, 5}, {0, FT_STORE_WEAPON1, BT_STORE, 100, 100, 100, 10}, {0, FT_STORE_WEAPON2, BT_STORE, 100, 150, 100, 20}, {0, FT_STORE_WEAPON3, BT_STORE, 100, 50, 100, 50}, {0, FT_STORE_WEAPON4, BT_STORE, 150, 200, 100, 100}, {0, FT_STORE_WEAPON5, BT_STORE, 200, 200, 50, 200}, {0, FT_STORE_ARMOUR1, BT_STORE, 100, 100, 100, 10}, {0, FT_STORE_ARMOUR2, BT_STORE, 100, 150, 100, 20}, {0, FT_STORE_ARMOUR3, BT_STORE, 100, 150, 100, 50}, {0, FT_STORE_ARMOUR4, BT_STORE, 150, 200, 100, 100}, {0, FT_STORE_ARMOUR5, BT_STORE, 200, 250, 50, 200}, {0, FT_STORE_SWORD0, BT_STORE, 100, 50, 100, 5}, {0, FT_STORE_SWORD1, BT_STORE, 100, 50, 100, 10}, {0, FT_STORE_SWORD2, BT_STORE, 100, 100, 100, 25}, {0, FT_STORE_SWORD3, BT_STORE, 150, 150, 100, 50}, {0, FT_STORE_SWORD4, BT_STORE, 200, 150, 100, 100}, {0, FT_STORE_SWORD5, BT_STORE, 200, 200, 50, 200}, {0, FT_STORE_SHIELD0, BT_STORE, 100, 100, 100, 5}, {0, FT_STORE_SHIELD1, BT_STORE, 100, 100, 100, 10}, {0, FT_STORE_SHIELD2, BT_STORE, 100, 150, 100, 25}, {0, FT_STORE_SHIELD3, BT_STORE, 150, 150, 100, 50}, {0, FT_STORE_SHIELD4, BT_STORE, 200, 200, 50, 200}, {0, FT_STORE_SHIELD5, BT_STORE, 200, 250, 50, 400}, {0, FT_STORE_AXE0, BT_STORE, 150, 50, 100, 5}, {0, FT_STORE_AXE1, BT_STORE, 150, 50, 100, 10}, {0, FT_STORE_AXE2, BT_STORE, 150, 100, 100, 25}, {0, FT_STORE_AXE3, BT_STORE, 150, 100, 100, 50}, {0, FT_STORE_AXE4, BT_STORE, 200, 150, 100, 100}, {0, FT_STORE_AXE5, BT_STORE, 200, 150, 50, 200}, {0, FT_STORE_AMMO0, BT_STORE, 150, 100, 100, 5}, {0, FT_STORE_AMMO1, BT_STORE, 200, 200, 150, 10}, {0, FT_STORE_AMMO2, BT_STORE, 250, 250, 150, 100}, {0, FT_STORE_FLET0, BT_STORE, 100, 50, 100, 15}, {0, FT_STORE_FLET1, BT_STORE, 100, 100, 100, 25}, {0, FT_STORE_FLET2, BT_STORE, 150, 150, 150, 100}, {0, FT_STORE_FLET3, BT_STORE, 150, 200, 150, 400}, {0, FT_STORE_WARHALL0, BT_STORE, 50, 50, 50, 15}, {0, FT_STORE_WARHALL1, BT_STORE, 50, 50, 50, 50}, {0, FT_STORE_WARHALL2, BT_STORE, 100, 50, 100, 100}, {0, FT_STORE_WARHALL3, BT_STORE, 100, 100, 100, 150}, {0, FT_STORE_WARHALL4, BT_STORE, 150, 100, 200, 200}, {0, FT_STORE_WARHALL5, BT_STORE, 150, 150, 250, 250}, {0, FT_STORE_CLOTH0, BT_STORE, 200, 100, 150, 15}, {0, FT_STORE_CLOTH1, BT_STORE, 150, 150, 150, 25}, {0, FT_STORE_HARMOUR0, BT_STORE, 150, 100, 100, 25}, {0, FT_STORE_HARMOUR1, BT_STORE, 150, 100, 100, 25}, {0, FT_STORE_HARMOUR2, BT_STORE, 200, 150, 150, 50}, {0, FT_STORE_HARMOUR3, BT_STORE, 200, 150, 150, 100}, {0, FT_STORE_HARMOUR4, BT_STORE, 250, 200, 200, 200}, {0, FT_STORE_HARMOUR5, BT_STORE, 250, 250, 200, 400}, {0, FT_STORE_HAT0, BT_STORE, 200, 50, 150, 15}, {0, FT_STORE_HAT1, BT_STORE, 200, 150, 150, 25}, {0, FT_STORE_HAT2, BT_STORE, 200, 150, 200, 50}, {0, FT_STORE_HAT3, BT_STORE, 250, 200, 200, 400}, {0, FT_STORE_JEWEL0, BT_STORE, 150, 150, 150, 25}, {0, FT_STORE_JEWEL1, BT_STORE, 150, 200, 150, 50}, {0, FT_STORE_JEWEL2, BT_STORE, 200, 200, 200, 100}, {0, FT_STORE_JEWEL3, BT_STORE, 200, 250, 200, 200}, {0, FT_STORE_JEWEL4, BT_STORE, 200, 250, 250, 400}, {0, FT_STORE_STATUE0, BT_STORE, 250, 150, 150, 50}, {0, FT_STORE_STATUE1, BT_STORE, 250, 150, 150, 50}, {0, FT_STORE_FIGUR0, BT_STORE, 200, 200, 150, 50}, {0, FT_STORE_FIGUR1, BT_STORE, 200, 200, 200, 50}, {0, FT_STORE_POTION0, BT_STORE, 150, 150, 150, 15}, {0, FT_STORE_POTION1, BT_STORE, 150, 150, 150, 50}, {0, FT_STORE_POTION2, BT_STORE, 200, 200, 200, 100}, {0, FT_STORE_POTION3, BT_STORE, 200, 200, 200, 200}, {0, FT_STORE_POTION4, BT_STORE, 200, 200, 200, 400}, {0, FT_STORE_SCROLL0, BT_STORE, 150, 150, 150, 15}, {0, FT_STORE_SCROLL1, BT_STORE, 150, 150, 150, 50}, {0, FT_STORE_SCROLL2, BT_STORE, 200, 200, 200, 100}, {0, FT_STORE_SCROLL3, BT_STORE, 200, 200, 200, 200}, {0, FT_STORE_SCROLL4, BT_STORE, 200, 200, 200, 400}, {0, FT_STORE_MAGIC0, BT_STORE, 50, 150, 200, 15}, {0, FT_STORE_MAGIC1, BT_STORE, 100, 200, 200, 25}, {0, FT_STORE_MAGIC2, BT_STORE, 100, 200, 200, 50}, {0, FT_STORE_MAGIC3, BT_STORE, 150, 250, 250, 100}, {0, FT_STORE_MAGIC4, BT_STORE, 200, 250, 250, 150}, {0, FT_STORE_BOOK1, BT_STORE, 200, 250, 250, 50}, {0, FT_STORE_TEMPLE1, BT_STORE, 50, 100, 150, 25}, {0, FT_STORE_TEMPLE2, BT_STORE, 100, 150, 150, 50}, {0, FT_STORE_TEMPLE3, BT_STORE, 150, 200, 200, 200}, {0, FT_STORE_SUPPLIES0, BT_STORE, 150, 50, 150, 50}, {0, FT_STORE_SUPPLIES1, BT_STORE, 100, 100, 150, 20}, {0, FT_STORE_BLACK1, BT_STORE, 200, 150, 50, 75}, {0, FT_STORE_BLACK2, BT_STORE, 200, 200, 50, 200}, {0, FT_STORE_ALCHEMY1, BT_STORE, 100, 150, 150, 25}, {0, FT_STORE_ALCHEMY2, BT_STORE, 150, 200, 150, 100}, {0, FT_STORE_JUNK, BT_STORE, 200, 50, 150, 10}, {0, FT_STORE_FOOD, BT_STORE, 200, 100, 150, 10}, {0, FT_BUILD_LIBRARY, BT_BUILD, 200, 200, 200, 20}, {0, FT_BUILD_CASINO, BT_BUILD, 100, 200, 200, 20}, {0, FT_BUILD_INN, BT_BUILD, 100, 100, 200, 1}, {0, FT_BUILD_HEALER, BT_BUILD, 250, 250, 200, 20}, {0, FT_STORE_BLACK0, BT_STORE, 100, 100, 100, 10}, {0, FT_BUILD_MAGETOWER0, BT_BUILD, 100, 150, 100, 6}, {0, FT_BUILD_MAGETOWER1, BT_BUILD, 150, 250, 150, 20}, {0, FT_BUILD_CASTLE0, BT_BUILD, 100, 150, 150, 10}, {0, FT_BUILD_CASTLE1, BT_BUILD, 200, 150, 250, 20}, }; /* The stores in the starting town */ static byte wild_first_town[START_STORE_NUM] = { BUILD_STAIRS, BUILD_STORE_HOME, BUILD_SUPPLIES0, BUILD_WARHALL0, BUILD_STORE_TEMPLE, BUILD_STORE_MAGIC, BUILD_BLACK0 }; /* * Return the building name given a building "type" */ cptr building_name(byte build_type) { u16b field_num; /* Look up the field type */ field_num = wild_build[build_type].field; /* Return the name of the building */ return (t_info[field_num].name); } /* * Return the building attributes given a building "type" */ void building_char(byte build_type, byte *a, char *c) { u16b field_num; /* Look up the field type */ field_num = wild_build[build_type].field; /* Get attr/char */ *a = t_info[field_num].d_attr; *c = t_info[field_num].d_char; } /* Find a place for the player */ static void place_player_start(s32b *x, s32b *y, u16b this_town) { int tempx, tempy; tempx = (int)place[this_town].x + wild_stairs_x / 16; tempy = (int)place[this_town].y + wild_stairs_y / 16; /* Get corner of visible region */ shift_in_bounds(&tempx, &tempy); /* Set corner of visible region */ p_ptr->old_wild_x = tempx; p_ptr->old_wild_y = tempy; /* Hack - Reset player position to be on the stairs in town */ *x = place[this_town].x * 16 + wild_stairs_x; *y = place[this_town].y * 16 + wild_stairs_y; /* Set current town */ p_ptr->place_num = this_town; } /* Pick a name for the town based on population */ void select_town_name(char *name, int pop) { char buf[T_NAME_LEN + 1]; int len; /* Get a normal 'elvish' name */ get_table_name(buf, FALSE); /* Get length */ len = strlen(buf) - 1; if (pop < T_SIZE_SMALL) { /* Hamlet */ if ((len < T_NAME_LEN - 5) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%sville", buf); } else { /* Simply copy it */ strnfmt(name, T_NAME_LEN + 1, "%s", buf); } } else if (pop < T_SIZE_TOWN) { /* Tiny town */ if ((len < T_NAME_LEN - 4) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%s Dun", buf); } else { /* Simply copy it */ strnfmt(name, T_NAME_LEN + 1, "%s", buf); } } else if (pop < T_SIZE_CITY) { /* Large Town */ if ((len < T_NAME_LEN - 3) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%ston", buf); } else { /* Simply copy it */ strnfmt(name, T_NAME_LEN + 1, "%s", buf); } } else if (pop < T_SIZE_CASTLE) { /* City */ if ((len < T_NAME_LEN - 4) && one_in_(4)) { strnfmt(name, T_NAME_LEN + 1, "%sford", buf); } else if ((len < T_NAME_LEN - 5) && one_in_(3)) { strnfmt(name, T_NAME_LEN + 1, "%s City", buf); } else if ((len < T_NAME_LEN - 5) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%s View", buf); } else if ((len < T_NAME_LEN - 5) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%s Fort", buf); } else { /* Simply copy it */ strnfmt(name, T_NAME_LEN + 1, "%s", buf); } } else { /* Castle */ if ((len < T_NAME_LEN - 7) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%s Castle", buf); } else if ((len < T_NAME_LEN - 5) && one_in_(2)) { strnfmt(name, T_NAME_LEN + 1, "%s Keep", buf); } else { /* Simply copy it */ strnfmt(name, T_NAME_LEN + 1, "%s", buf); } } } /* Select a store or building "appropriate" for a given position */ static byte select_building(byte pop, byte magic, int law, u16b *build, int build_num) { int i; s32b total = 0; /* Draw stairs first for small towns */ if ((build_num < 11) && (!build[BUILD_STAIRS])) return (BUILD_STAIRS); for (i = 0; i < MAX_CITY_BUILD; i++) { /* Work out total effects due to location */ total = (ABS(pop - wild_build[i].pop) + ABS(magic - wild_build[i].magic) + ABS(law - wild_build[i].law)) + 1; /* Effect due to rarity */ total *= wild_build[i].rarity; /* Effect due to total count */ total += build[i] * 200; /* calculate probability based on location */ wild_build[i].gen = (u16b)(MAX_SHORT / total); } /* Note that cities of size 11 have a small chance to have stairs. */ /* Effects for cities */ if (build_num > 11) { /* Hack - Dungeons are not in large cities */ wild_build[BUILD_STAIRS].gen = 0; /* Hack - Increase possibility of 'general' features */ for (i = 0; i < MAX_CITY_BUILD; i++) { if (build_is_general(i)) { wild_build[i].gen *= ((build_num - 5) / 6); } } } /* Some buildings don't exist for small towns */ else { for (i = 0; i < MAX_CITY_BUILD; i++) { /* No 'filler' buildings in small towns. */ if (build_is_general(i)) { wild_build[i].gen = 0; } } } /* Hack - Not more than one home per city */ if (build[BUILD_STORE_HOME]) { wild_build[BUILD_STORE_HOME].gen = 0; } /* Hack - Not more than one magetower per city */ if (build[BUILD_MAGETOWER0] || build[BUILD_MAGETOWER1]) { wild_build[BUILD_MAGETOWER0].gen = 0; wild_build[BUILD_MAGETOWER1].gen = 0; } total = 0; /* Calculate total */ for (i = 0; i < MAX_CITY_BUILD; i++) { total += wild_build[i].gen; } /* Pick a building */ total = randint0(total); /* Later add checks for silliness */ /* (A small town with 5 "homes" would be silly) */ /* Find which building we've got */ for (i = 0; i < MAX_CITY_BUILD; i++) { total -= wild_build[i].gen; if (total < 0) return (i); } /* paranoia - we didn't find it */ msgf("FAILED to generate building!"); return (0); } static void general_init(int town_num, int store_num, byte general_type) { /* Activate that feature */ store_type *st_ptr = &place[town_num].store[store_num]; /* Set the type */ st_ptr->type = general_type; /* Initialize */ st_ptr->data = 0; st_ptr->last_visit = 0; } static byte build_x[WILD_BLOCK_SIZE * WILD_BLOCK_SIZE]; static byte build_y[WILD_BLOCK_SIZE * WILD_BLOCK_SIZE]; static byte build_pop[WILD_BLOCK_SIZE * WILD_BLOCK_SIZE]; static byte build_count; /* * Recursive function used to generate towns with no islands */ static void fill_town(byte x, byte y) { byte i; /* Hack - deliberate braces to lower memory cost of recursion */ { u16b *block_data = &temp_block[y][x]; /* Do not continue if hit a previously done area. */ if (*block_data == CITY_WALL) return; /* Do not redo a building */ if (*block_data == CITY_INSIDE) return; /* Save the square */ build_pop[build_count] = *block_data / WILD_BLOCK_SIZE; /* Do not redo this square */ *block_data = CITY_INSIDE; } build_x[build_count] = x; build_y[build_count] = y; /* Increment store counter */ build_count++; /* Look at adjacent squares */ for (i = 0; i < 8; i++) { /* Recurse */ fill_town((byte)(x + ddx_ddd[i]), (byte)(y + ddy_ddd[i])); } } /* Work out where the walls are */ static void find_walls(void) { int i, j, k, l; /* Copy the temp block to the town block */ for (i = 0; i < WILD_BLOCK_SIZE + 1; i++) { for (j = 0; j < WILD_BLOCK_SIZE + 1; j++) { if (temp_block[j][i] < WILD_BLOCK_SIZE * 128) { /* Outside the town */ temp_block[j][i] = CITY_OUTSIDE; } } } /* Find walls */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Is a "city block" */ if (temp_block[j][i] != CITY_OUTSIDE) { /* Scan around */ for (k = -1; k <= 1; k++) { for (l = -1; l <= 1; l++) { /* In bounds? */ if ((i + k >= 0) && (i + k < WILD_BLOCK_SIZE) && (j + l >= 0) && (j + l < WILD_BLOCK_SIZE)) { /* Is it outside? */ if (temp_block[j + l][i + k] == CITY_OUTSIDE) { /* Make a wall */ temp_block[j][i] = CITY_WALL; } } else { /* Make a wall */ temp_block[j][i] = CITY_WALL; } } } } } } } /* * Driver function for the fill_town() routine */ static byte fill_town_driver(void) { /* Paranoia - middle square must be in the town */ if (temp_block[WILD_BLOCK_SIZE / 2][WILD_BLOCK_SIZE / 2] == CITY_OUTSIDE) return (0); build_count = 0; /* 'Fill' the town with buildings, stopping at the walls */ fill_town(WILD_BLOCK_SIZE / 2, WILD_BLOCK_SIZE / 2); /* Return number of buildings allocated */ return (build_count); } /* * Remove "islands" from cities. * * Check that the city is fully connected... */ static void remove_islands(void) { int i, j, k, l; bool city_block; /* Rescan walls to avoid "islands" */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Is a "wall block" */ if (temp_block[j][i] == CITY_WALL) { city_block = FALSE; /* Scan around */ for (k = -1; k <= 1; k++) { for (l = -1; l <= 1; l++) { /* In bounds? */ if ((i + k >= 0) && (i + k < WILD_BLOCK_SIZE) && (j + l >= 0) && (j + l < WILD_BLOCK_SIZE)) { /* Is it a city block? */ if (temp_block[j + l][i + k] == CITY_INSIDE) { /* We are next to a city */ city_block = TRUE; } } } } /* No islands */ if (!city_block) temp_block[j][i] = CITY_OUTSIDE; } } } } /* * Create a city + contained stores and buildings */ static bool create_city(int x, int y, int town_num) { int i, j; /* Hack - fix this XXX XXX */ /* int pop = wild[y][x].trans.pop_map; */ byte pop = ((wild[y][x].trans.pop_map + wild[y][x].trans.law_map) / rand_range(4, 32)) + 128; byte law = wild[y][x].trans.law_map; byte magic; int build_num = 0, build_tot; byte building; byte count; byte gate_value[MAX_GATES]; byte gate_num[MAX_GATES]; u32b rng_seed_save; wild_gen2_type *w_ptr; place_type *pl_ptr = &place[town_num]; u16b build[MAX_CITY_BUILD]; u16b build_list[WILD_BLOCK_SIZE * WILD_BLOCK_SIZE]; /* Hack - the first town is special */ if (town_num == 1) { /* Use a low pop - we don't want too many blank buildings */ pop = 64 + 128; } /* Wipe the list of allocated buildings */ (void)C_WIPE(build, MAX_CITY_BUILD, u16b); (void)C_WIPE(build_list, (WILD_BLOCK_SIZE * WILD_BLOCK_SIZE), u16b); /* Add town */ select_town_name(pl_ptr->name, pop); pl_ptr->seed = randint0(0x10000000); pl_ptr->type = TOWN_FRACT; pl_ptr->monst_type = TOWN_MONST_VILLAGER; pl_ptr->x = x; pl_ptr->y = y; /* Save the population value in the 'data' value */ pl_ptr->data = pop; /* Hack - the size is constant... */ pl_ptr->xsize = 8; pl_ptr->ysize = 8; /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant town layout */ Rand_value = pl_ptr->seed; /* We don't have to save this in the town structure */ magic = (byte) randint0(256); /* Generate plasma factal */ clear_temp_block(); set_temp_corner_val(WILD_BLOCK_SIZE * 64); set_temp_mid((u16b) (WILD_BLOCK_SIZE * pop)); frac_block(); /* Locate the walls */ find_walls(); /* 'Fill' the town with buildings */ count = fill_town_driver(); /* Too few squares??? */ if (count < 7) return (FALSE); /* Make sure the city is self-connected properly */ remove_islands(); /* Clear the gates locations */ (void)C_WIPE(pl_ptr->gates_x, MAX_GATES, byte); (void)C_WIPE(pl_ptr->gates_y, MAX_GATES, byte); (void)C_WIPE(gate_num, MAX_GATES, byte); /* Initialise min and max values */ gate_value[0] = 0; gate_value[1] = 255; gate_value[2] = 0; gate_value[3] = 255; /* Hack - save seed of rng */ rng_seed_save = Rand_value; /* * Link wilderness to the new city * and find position of town gates. */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Is it a city block? */ if (temp_block[j][i] != CITY_OUTSIDE) { w_ptr = &wild[y + j / 2][x + i / 2].trans; /* * Add city to wilderness * Note: only 255 towns can be stored currently. */ w_ptr->place = (byte)town_num; /* Hack - make a flat area around the town */ w_ptr->info |= WILD_INFO_ROAD; /* Right gate */ if (gate_value[0] < i) { /* save it */ gate_value[0] = i; gate_num[0] = 2; pl_ptr->gates_x[0] = i; pl_ptr->gates_y[0] = j; } else if ((gate_value[0] == i) && one_in_(gate_num[0])) { /* save it */ gate_value[0] = i; gate_num[0]++; pl_ptr->gates_x[0] = i; pl_ptr->gates_y[0] = j; } /* Left gate */ if (gate_value[1] > i) { /* save it */ gate_value[1] = i; gate_num[1] = 2; pl_ptr->gates_x[1] = i; pl_ptr->gates_y[1] = j; } else if ((gate_value[1] == i) && one_in_(gate_num[1])) { /* save it */ gate_value[1] = i; gate_num[1]++; pl_ptr->gates_x[1] = i; pl_ptr->gates_y[1] = j; } /* Bottom gate */ if (gate_value[2] < j) { /* save it */ gate_value[2] = j; gate_num[2] = 2; pl_ptr->gates_x[2] = i; pl_ptr->gates_y[2] = j; } else if ((gate_value[2] == j) && one_in_(gate_num[2])) { /* save it */ gate_value[2] = j; gate_num[2]++; pl_ptr->gates_x[2] = i; pl_ptr->gates_y[2] = j; } /* Top gate */ if (gate_value[3] > j) { /* save it */ gate_value[3] = j; gate_num[3] = 2; pl_ptr->gates_x[3] = i; pl_ptr->gates_y[3] = j; } else if ((gate_value[3] == j) && one_in_(gate_num[3])) { /* save it */ gate_value[3] = j; gate_num[3]++; pl_ptr->gates_x[3] = i; pl_ptr->gates_y[3] = j; } } } } /* * Generate second fractal */ clear_temp_block(); set_temp_corner_val(WILD_BLOCK_SIZE * 64); set_temp_mid((u16b)(WILD_BLOCK_SIZE * law)); frac_block(); /* Restore the old seed */ Rand_value = rng_seed_save; /* Save the total number of buildings */ build_tot = count; /* Scan blocks in a random order */ while (count) { /* Pick a square */ i = randint0(count); /* Get parameters for the 8x8 section the building is on */ pop = build_pop[i]; law = temp_block[build_y[i]][build_x[i]] / WILD_BLOCK_SIZE; /* * "place" building, and then record in the * list of allocated buildings. */ building = select_building(pop, magic, law, build, build_tot); /* Count number of this type */ build[building]++; /* Record list of created buildings */ build_list[build_num++] = building; /* * Decrement free space in city * Note deliberate use of count-- in initialiser */ for (count--; i < count; i++) { /* Shift unallocated buildings down */ build_pop[i] = build_pop[i + 1]; build_x[i] = build_x[i + 1]; build_y[i] = build_y[i + 1]; } } /* * Generate store and building data structures * * We need to do this second, because we need to * know exactly how many stores we have - and realloc * is silly, unless you need to use it. */ /* Allocate the stores */ C_MAKE(pl_ptr->store, build_num, store_type); pl_ptr->numstores = build_num; /* Initialise the stores */ for (i = 0; i < build_num; i++) { building = (byte)build_list[i]; if (build_is_store(building)) { /* Initialise the store */ store_init(town_num, i, building); } else if (build_is_general(building)) { /* Initialise general feature */ general_init(town_num, i, building); } else { /* Initialise the building */ build_init(town_num, i, building); } } /* Success */ return (TRUE); } /* * Draw the gates to the city */ static void draw_gates(byte i, byte j, place_type *pl_ptr) { int k; int x = i * 8, y = j * 8; int xx = x, yy = y; /* Draw gates if visible */ for (k = 0; k < MAX_GATES; k++) { if ((pl_ptr->gates_x[k] == i) && (pl_ptr->gates_y[k] == j)) { /* Add doors (hack) */ switch (k) { case 0: { /* Hack - shift gate if next to walls */ if (cave_perma_grid(cave_p(x + 3, y + 2))) yy -= 3; if (cave_perma_grid(cave_p(x + 3, y + 5))) yy += 3; y = yy; /* Draw an empty square */ generate_fill(x + 3, y + 3, x + 4, y + 4, FEAT_FLOOR); /* Right gate */ make_lockjam_door(x + 4, y + 3, 0, FALSE); make_lockjam_door(x + 4, y + 4, 0, FALSE); return; } case 1: { /* Hack - shift gate if next to walls */ if (cave_perma_grid(cave_p(x + 3, y + 2))) yy -= 3; if (cave_perma_grid(cave_p(x + 3, y + 5))) yy += 3; y = yy; /* Draw an empty square */ generate_fill(x + 3, y + 3, x + 4, y + 4, FEAT_FLOOR); /* Left gate */ make_lockjam_door(x + 3, y + 3, 0, FALSE); make_lockjam_door(x + 3, y + 4, 0, FALSE); return; } case 2: { /* Hack - shift gate if next to walls */ if (cave_perma_grid(cave_p(x + 2, y + 3))) xx -= 3; if (cave_perma_grid(cave_p(x + 5, y + 3))) xx += 3; x = xx; /* Draw an empty square */ generate_fill(x + 3, y + 3, x + 4, y + 4, FEAT_FLOOR); /* Bottom gate */ make_lockjam_door(x + 3, y + 4, 0, FALSE); make_lockjam_door(x + 4, y + 4, 0, FALSE); return; } case 3: { /* Hack - shift gate if next to walls */ if (cave_perma_grid(cave_p(x + 2, y + 3))) xx -= 3; if (cave_perma_grid(cave_p(x + 5, y + 3))) xx += 3; x = xx; /* Draw an empty square */ generate_fill(x + 3, y + 3, x + 4, y + 4, FEAT_FLOOR); /* Top gate */ make_lockjam_door(x + 3, y + 3, 0, FALSE); make_lockjam_door(x + 4, y + 3, 0, FALSE); return; } } } } } static void draw_store(int x0, int y0, store_type *st_ptr, int x, int y) { int x1, y1, x2, y2; int i, j; int tmp; cave_type *c_ptr; /* Determine the store boundaries */ y1 = y0 - randint1(3); y2 = y0 + randint1(2); x1 = x0 - randint1(3); x2 = x0 + randint1(3); /* Build an invulnerable rectangular building */ generate_fill(x1, y1, x2, y2, FEAT_PERM_EXTRA); /* Pick a door direction (S,N,E,W) */ tmp = randint0(4); /* Extract a "door location" */ switch (tmp) { case 0: { /* Bottom side */ i = rand_range(x1, x2); j = y2; break; } case 1: { /* Top side */ i = rand_range(x1, x2); j = y1; break; } case 2: { /* Right side */ i = x2; j = rand_range(y1, y2); break; } default: { /* Left side */ i = x1; j = rand_range(y1, y2); break; } } c_ptr = cave_p(i, j); /* Clear previous contents, add a store door */ set_feat_grid(c_ptr, FEAT_FLOOR); c_ptr->fld_idx = wild_build[st_ptr->type].field; /* Save location of store door */ st_ptr->x = x * 8 + i % 8; st_ptr->y = y * 8 + j % 8; } static void draw_general(int x0, int y0, store_type *st_ptr, int x, int y) { int i, j; /* Ignore currently unused parameters */ (void)x; (void)y; switch (st_ptr->type) { case BUILD_STAIRS: { /* Put dungeon floor next to stairs so they are easy to find. */ for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { /* Convert square to dungeon floor */ set_feat_bold(x0 + i, y0 + j, FEAT_FLOOR); } } /* Clear previous contents, add down stairs */ set_feat_bold(x0, y0, FEAT_MORE); break; } case BUILD_NONE: { int x1, y1, x2, y2; /* Determine the store boundaries */ y1 = y0 - randint1(3); y2 = y0 + randint1(2); x1 = x0 - randint1(3); x2 = x0 + randint1(3); /* Build an invulnerable rectangular building */ generate_fill(x1, y1, x2, y2, FEAT_PERM_EXTRA); /* No doors */ /* break; Restore this when we do something for BUILD_BLANK */ } case BUILD_BLANK: { /* Do Nothing */ break; } } } /* * Draw a building / store of a given type at a given position */ static void draw_building(byte type, byte x, byte y, u16b store, place_type *pl_ptr) { /* Really dodgy - just a rectangle, independent of type, for now */ int xx, yy; /* Hack - save the rng seed */ u32b rng_save_seed = Rand_value; store_type *st_ptr = &pl_ptr->store[store]; /* Hack, ignore building draw type for now */ (void)type; /* Get location in region */ xx = x * 8; yy = y * 8; /* Hack - set location of stairs so we can start on them. */ if (st_ptr->type == BUILD_STAIRS) { wild_stairs_x = xx + 4; wild_stairs_y = yy + 4; } /* What are we drawing? */ if (build_is_store(st_ptr->type)) { /* Draw the store */ draw_store(xx + 4, yy + 4, st_ptr, x, y); } else if (build_is_general(st_ptr->type)) { /* Draw the general feature */ draw_general(xx + 4, yy + 4, st_ptr, x, y); } else { /* Hack - Draw the "normal" building */ draw_store(xx + 4, yy + 4, st_ptr, x, y); } /* Hack - restore the rng seed */ Rand_value = rng_save_seed; } /* Actually draw the city in the region */ void draw_city(place_type *pl_ptr) { int x, y; int count = 0; byte i, j; byte magic; u16b build; /* Paranoia */ if (pl_ptr->region) quit("Town already has region during creation."); /* Get region */ create_region(pl_ptr, pl_ptr->xsize * WILD_BLOCK_SIZE, pl_ptr->ysize * WILD_BLOCK_SIZE, REGION_OVER); /* Hack - do not increment refcount here - let allocate_block do that */ /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant town layout */ Rand_value = pl_ptr->seed; /* Get value of "magic" level of buildings */ magic = (byte)randint0(256); /* Generate plasma factal */ clear_temp_block(); set_temp_corner_val(WILD_BLOCK_SIZE * 64); /* Use population value saved in data. */ set_temp_mid((u16b)(WILD_BLOCK_SIZE * pl_ptr->data)); frac_block(); /* Locate the walls */ find_walls(); /* 'Fill' the town with buildings */ count = fill_town_driver(); /* Paranoia */ if (count < 7) quit("Random number generator failure"); /* Make sure the city is self-connected properly */ remove_islands(); /* Draw walls */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Are we a wall? */ if (temp_block[j][i] == CITY_WALL) { /* Get coords in region */ y = j * 8; x = i * 8; /* Wall goes up */ if ((j > 0) && (temp_block[j - 1][i] == CITY_WALL)) { generate_fill(x + 3, y, x + 4, y + 4, FEAT_PERM_SOLID); } /* Wall goes left */ if ((i > 0) && (temp_block[j][i - 1] == CITY_WALL)) { generate_fill(x, y + 3, x + 4, y + 4, FEAT_PERM_SOLID); } /* Wall goes right */ if ((i < WILD_BLOCK_SIZE - 1) && (temp_block[j][i + 1] == CITY_WALL)) { generate_fill(x + 3, y + 3, x + 7, y + 4, FEAT_PERM_SOLID); } /* Wall goes down */ if ((j < WILD_BLOCK_SIZE - 1) && (temp_block[j + 1][i] == CITY_WALL)) { generate_fill(x + 3, y + 3, x + 4, y + 7, FEAT_PERM_SOLID); } /* Draw the gates */ draw_gates(i, j, pl_ptr); } } } /* Scan blocks in a random order */ for (build = 0; count; build++) { /* Pick a square */ i = (byte)randint0(count); /* Draw the building */ draw_building(0, build_x[i], build_y[i], build, pl_ptr); /* * Decrement free space in city * Note deliberate use of count-- in initialiser */ for (count--; i < count; i++) { /* Shift unallocated buildings down */ build_x[i] = build_x[i + 1]; build_y[i] = build_y[i + 1]; } } /* Hack -- use the "complex" RNG */ Rand_quick = FALSE; } /* * Helper function to determine which wilderness blocks * have been used in the region drawn to. */ static void set_place(byte place_num) { int i, j, k, l; int xmax = 0, ymax = 0; place_type *pl_ptr = &place[place_num]; s16b ri_idx = pl_ptr->region; wild_gen2_type *w_ptr; region_info *ri_ptr; cave_type *c_ptr; if (!ri_idx) quit("Place does not have a region!"); /* Acquire region info */ ri_ptr = &ri_list[ri_idx]; /* Look for blocks that are used */ for (i = 0; i < ri_ptr->xsize; i += WILD_BLOCK_SIZE) { for (j = 0; j < ri_ptr->ysize; j += WILD_BLOCK_SIZE) { /* Scan for something in this block */ for (k = i; (k < i + WILD_BLOCK_SIZE) && (k < ri_ptr->xsize); k++) { for (l = j; (l < j + WILD_BLOCK_SIZE) && (l < ri_ptr->ysize); l++) { c_ptr = access_region(k, l, ri_idx); /* Anything here? */ if (c_ptr->feat || c_ptr->o_idx || c_ptr->m_idx || c_ptr->fld_idx) { w_ptr = &wild[pl_ptr->y + j / WILD_BLOCK_SIZE] [pl_ptr->x + i / WILD_BLOCK_SIZE].trans; /* Link the block to the wilderness map */ w_ptr->place = (byte)place_num; /* Record max bounds */ if (i > xmax) xmax = i; if (j > ymax) ymax = j; /* Break out two levels */ goto out; } } } /* Found something */ out:; } } /* Shrink region size to minimum required */ ri_ptr->xsize = xmax + WILD_BLOCK_SIZE; ri_ptr->ysize = ymax + WILD_BLOCK_SIZE; /* Shrink place size to minimum required */ pl_ptr->xsize = (ri_ptr->xsize / WILD_BLOCK_SIZE) + 1; pl_ptr->ysize = (ri_ptr->ysize / WILD_BLOCK_SIZE) + 1; } static u32b dun_habitat; static u16b dun_level; /* * Helper monster selection function */ static bool monster_habitat_ok(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; /* No multiplying monsters */ if (FLAG(r_ptr, RF_MULTIPLY)) return (FALSE); /* Hack - no aquatic monsters */ if (FLAG(r_ptr, RF_AQUATIC)) return (FALSE); /* In this dungeon? */ if (r_ptr->flags[7] & dun_habitat) return (TRUE); /* Not here */ return (FALSE); } /* * Add monsters to the dungeon entrance */ static void entrance_monsters(int x_max, int y_max) { int x, y; int i; long size = x_max * y_max; int count = size / 25; cave_type *c_ptr; /* Bail out if we don't want to make any monsters here */ if (!dun_habitat) return; /* Apply the monster restriction */ get_mon_num_prep(monster_habitat_ok); for (i = 0; i < count; i++) { x = randint0(x_max); y = randint0(y_max); c_ptr = cave_p(x, y); /* Only place monsters on 'nice' grids */ if (!cave_nice_grid(c_ptr)) continue; /* Hack Pick a race, and store it into monster slot of cave_type */ c_ptr->m_idx = get_mon_num(dun_level); } /* Remove the monster restriction */ get_mon_num_prep(NULL); } /* * Open a clearing with ruffled edges in the wilderness. */ static void open_clearing(int x_max, int y_max) { int x, y; int dist, distx, disty; for (x = 0; x < x_max; x++) { for (y = 0; y < y_max; y++) { /* Get distance to boundary */ distx = MIN(x, x_max - x); disty = MIN(y, y_max - y); dist = MIN(x, y); /* Rough edges 5 squares deep */ if (randint0(6) < dist) { /* Add 'cleared' terrain */ if (one_in_(3)) { set_feat_bold(x, y, FEAT_PEBBLES); } else { set_feat_bold(x, y, FEAT_DIRT); } } } } } /* * Draw 'count' buildings in the given region */ static void make_dun_buildings(int count, int x_max, int y_max) { int x, y; int xwid, ywid; int i, j, k; cave_type *c_ptr; for (i = 0; i < count; i++) { /* Get location of building */ x = rand_range(2, x_max - 12); y = rand_range(2, y_max - 12); /* Get size */ xwid = rand_range(6, 10); ywid = rand_range(6, 10); /* Can we place it here? */ for (j = -1; j <= xwid + 1; j++) { for (k = -1; k <= ywid + 1; k++) { c_ptr = cave_p(x + j, y + k); /* Can't place a building here? */ if ((c_ptr->feat == FEAT_PERM_OUTER) || (c_ptr->feat == FEAT_MORE)) goto out; } } /* Make building */ /* Add floor */ generate_fill(x, y, x + xwid, y + ywid, FEAT_FLOOR_WOOD); /* Add walls */ generate_draw(x, y, x + xwid, y + ywid, FEAT_PERM_OUTER); /* Add a door */ generate_door(x, y, x + xwid, y + ywid, FALSE); out:; } } /* * Dungeon drawing routines. * * These draw the entrance on a pre-allocated region. */ static void draw_dun_dark_water(void) { int i; int x, y; /* Scatter swamp around */ for (i = 0; i < 200; i++) { x = randint0(32); y = randint0(32); set_feat_bold(x, y, FEAT_SHAL_SWAMP); } /* Add some rubble */ for (i = 0; i < 10; i++) { x = rand_range(16 - 4, 16 + 4); y = rand_range(16 - 4, 16 + 4); set_feat_bold(x, y, FEAT_RUBBLE); } /* Add stairs */ set_feat_bold(16, 16, FEAT_MORE); /* Add monsters */ entrance_monsters(32, 32); } static void draw_dun_cave(void) { int xsize, ysize; int x, y; int i = 0, j = 0; int c1, c2, c3; cave_type *c_ptr; while (TRUE) { xsize = rand_range(2, 4) * 16; ysize = rand_range(2, 4) * 16; /* Floor */ c3 = xsize; /* Boundary level */ c1 = randint0(c3 / 2); /* Only two types of terrain */ c2 = 0; /* Make a fractal heightmap */ generate_hmap(xsize / 2, ysize / 2, xsize, ysize, xsize / 2, 16, c3); /* Did it work? */ if (generate_lake(xsize / 2, ysize / 2, xsize, ysize, c1, c2, c3, FEAT_DIRT, FEAT_DIRT, FEAT_DIRT)) break; /* Erase, and repeat until it works */ generate_fill(0, 0, xsize, ysize, FEAT_NONE); } /* Remove outer edge */ generate_draw(0, 0, xsize, ysize, FEAT_NONE); generate_draw(0, 0, xsize - 1, ysize - 1, FEAT_NONE); /* Make walls only one level thick */ for (x = 1; x < xsize - 1; x++) { for (y = 1; y < ysize - 1; y++) { c_ptr = cave_p(x, y); if ((c_ptr->feat == FEAT_PERM_OUTER) || (c_ptr->feat == FEAT_WALL_OUTER) || (c_ptr->feat == FEAT_WALL_EXTRA)) { /* Make sure we are permanent */ set_feat_grid(c_ptr, FEAT_PERM_OUTER); for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { c_ptr = cave_p(x + i, y + j); /* We are next to floor? - Keep */ if (c_ptr->feat == FEAT_DIRT) goto out; } } /* Else we are not needed */ set_feat_bold(x, y, FEAT_NONE); } out:; } } /* Scan in one of the cardinal directions */ switch (randint0(4)) { case 0: { i = -1; j = 0; } case 1: { i = 1; j = 0; } case 2: { i = 0; j = -1; } case 3: { i = 0; j = 1; } } x = xsize / 2; y = ysize / 2; /* Scan for first wall */ while (cave_p(x, y)->feat != FEAT_PERM_OUTER) { x += i; y += j; } /* Paranoia */ if (x == 0) x = 1; if (y == 0) y = 1; /* Finally, add entrance to cave */ generate_fill(x - 1, y - 1, x + 1, y + 1, FEAT_NONE); /* Add stairs */ set_feat_bold(xsize / 2, ysize / 2, FEAT_MORE); /* XXX XXX XXX Hack - make sure we have the correct sized region later on */ set_feat_bold(xsize, ysize, FEAT_DIRT); /* Add monsters */ entrance_monsters(xsize, ysize); } static void draw_dun_temple(void) { int xsize, ysize; int x0, y0; int x, y; xsize = rand_range(2, 4) * 16; ysize = rand_range(2, 4) * 16; x0 = xsize / 2; y0 = ysize / 2; /* Open clearing */ open_clearing(xsize, ysize); x = xsize / 4; y = ysize / 4; /* Add floor */ generate_fill(x0 - x, y0 - y, x0 + x, y0 + y, FEAT_FLOOR_TILE); /* Draw outer walls */ generate_draw(x0 - x, y0 - y, x0 + x, y0 + y, FEAT_PERM_OUTER); /* Draw inner walls */ generate_draw(x0 - x, y0 - y, x0 - 1, y0 - 1, FEAT_PERM_OUTER); generate_draw(x0 + 1, y0 - y, x0 + x, y0 - 1, FEAT_PERM_OUTER); generate_draw(x0 - x, y0 + 1, x0 - 1, y0 + y, FEAT_PERM_OUTER); generate_draw(x0 + 1, y0 + 1, x0 + x, y0 + y, FEAT_PERM_OUTER); /* Add some doors */ generate_door(x0 - x, y0 - y, x0 + x, y0 + y, FALSE); generate_door(x0 - x, y0 - y, x0 - 1, y0 - 1, FALSE); generate_door(x0 - x, y0 - y, x0 - 1, y0 - 1, FALSE); generate_door(x0 + 1, y0 - y, x0 + x, y0 - 1, FALSE); generate_door(x0 + 1, y0 - y, x0 + x, y0 - 1, FALSE); generate_door(x0 - x, y0 + 1, x0 - 1, y0 + y, FALSE); generate_door(x0 - x, y0 + 1, x0 - 1, y0 + y, FALSE); generate_door(x0 + 1, y0 + 1, x0 + x, y0 + y, FALSE); generate_door(x0 + 1, y0 + 1, x0 + x, y0 + y, FALSE); /* Add stairs */ switch (randint0(4)) { case 0: { set_feat_bold(x0 - x + 1, y0 - y + 1, FEAT_MORE); } case 1: { set_feat_bold(x0 - x + 1, y0 + y - 1, FEAT_MORE); } case 2: { set_feat_bold(x0 + x - 1, y0 - y + 1, FEAT_MORE); } case 3: { set_feat_bold(x0 + x - 1, y0 + y - 1, FEAT_MORE); } } /* Add monsters */ entrance_monsters(xsize, ysize); } static void draw_dun_tower(void) { int xsize, ysize; int x0, y0; int x, y; xsize = rand_range(2, 4) * 16; ysize = rand_range(2, 4) * 16; x0 = xsize / 2; y0 = ysize / 2; /* Open clearing */ open_clearing(xsize, ysize); x = xsize / 4; y = ysize / 4; /* Add floor */ generate_fill(x0 - x, y0 - y, x0 + x, y0 + y, FEAT_FLOOR_WOOD); /* Draw nested rectangles */ while ((x > 2) && (y > 2)) { /* Add walls */ generate_draw(x0 - x, y0 - y, x0 + x, y0 + y, FEAT_PERM_OUTER); /* Add a door */ generate_door(x0 - x, y0 - y, x0 + x, y0 + y, FALSE); /* Make smaller room inside */ x /= 2; y /= 2; } /* Add stairs */ set_feat_bold(xsize / 2, ysize / 2, FEAT_MORE); /* Add monsters */ entrance_monsters(xsize, ysize); } static void draw_dun_ruin(void) { int xsize, ysize; int x0, y0; int i, j; int count; cave_type *c_ptr; xsize = rand_range(2, 4) * 16; ysize = rand_range(2, 4) * 16; x0 = xsize / 2; y0 = ysize / 2; /* Add entrance */ generate_draw(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FEAT_PERM_OUTER); /* Open the inner vault with a door */ generate_door(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FALSE); /* Place stairs */ set_feat_bold(x0, y0, FEAT_MORE); count = (xsize / 16) * (ysize / 16); /* Draw a random number of buildings */ make_dun_buildings(count, xsize, ysize); /* Wreck them! */ for (i = 0; i < xsize; i++) { for (j = 0; j < ysize; j++) { c_ptr = cave_p(i, j); switch (c_ptr->feat) { case FEAT_MORE: { /* Keep the stairs */ continue; } case FEAT_PERM_OUTER: { /* Convert wall to be diggable to prevent problems */ set_feat_grid(c_ptr, FEAT_WALL_OUTER); /* Wreck the walls */ if (one_in_(4)) set_feat_grid(c_ptr, FEAT_RUBBLE); if (one_in_(4)) set_feat_grid(c_ptr, FEAT_DIRT); if (one_in_(8)) set_feat_grid(c_ptr, FEAT_NONE); break; } case FEAT_FLOOR_WOOD: { /* Wreck the floors less often */ if (one_in_(100)) set_feat_grid(c_ptr, FEAT_RUBBLE); if (one_in_(10)) set_feat_grid(c_ptr, FEAT_DIRT); if (one_in_(25)) set_feat_grid(c_ptr, FEAT_NONE); break; } case FEAT_NONE: { /* Rarely affect the area between buildings */ if (one_in_(100)) set_feat_grid(c_ptr, FEAT_RUBBLE); if (one_in_(100)) set_feat_grid(c_ptr, FEAT_DIRT); if (one_in_(100)) set_feat_grid(c_ptr, FEAT_FLOOR_WOOD); } } } } /* Add monsters */ entrance_monsters(xsize, ysize); } static void draw_dun_grave(void) { int xsize, ysize; int x0, y0; int x, y; int i, count; int j, k; cave_type *c_ptr; xsize = rand_range(2, 4) * 16; ysize = rand_range(2, 4) * 16; /* Open clearing */ open_clearing(xsize, ysize); x0 = xsize / 2; y0 = ysize / 2; /* Add crypt entrance */ generate_draw(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FEAT_PERM_OUTER); /* Open the inner vault with a secret door */ generate_door(x0 - 1, y0 - 1, x0 + 1, y0 + 1, TRUE); /* Place stairs */ set_feat_bold(x0, y0, FEAT_MORE); count = (xsize / 4) * (ysize / 4); /* Draw a random number of graves */ for (i = 0; i < count; i++) { x = randint1(xsize - 1); y = randint1(ysize - 1); /* Scan for empty space */ for (j = -1; j < 1; j++) { for (k = -1; k < 1; k++) { c_ptr = cave_p(x + j, y + k); /* Not next to or on top of other stuff */ if ((c_ptr->feat == FEAT_PERM_OUTER) || (c_ptr->feat == FEAT_MORE)) goto out; } } if (one_in_(5)) { /* Draw obelisk */ set_feat_grid(c_ptr, FEAT_OBELISK); } else { /* Draw grave */ set_feat_grid(c_ptr, FEAT_BOULDER); } out:; } /* Add monsters */ entrance_monsters(xsize, ysize); } /* * Draw mine entrance. * * XXX XXX Should we have tracks? */ static void draw_dun_mine(void) { int x0 = 8, y0 = 8; /* Add entrance */ generate_draw(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FEAT_PERM_OUTER); /* Add door to the mine */ generate_door(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FALSE); /* Place stairs */ set_feat_bold(x0, y0, FEAT_MORE); /* Add monsters */ entrance_monsters(16, 16); } static void draw_dun_city(void) { int xsize, ysize; int x0, y0; int count; xsize = rand_range(2, 4) * 16; ysize = rand_range(2, 4) * 16; /* Open clearing */ open_clearing(xsize, ysize); x0 = xsize / 2; y0 = ysize / 2; /* Add entrance */ generate_draw(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FEAT_PERM_OUTER); /* Open the inner vault with a door */ generate_door(x0 - 1, y0 - 1, x0 + 1, y0 + 1, FALSE); /* Place stairs */ set_feat_bold(x0, y0, FEAT_MORE); count = (xsize / 16) * (ysize / 16); /* Draw a random number of buildings */ make_dun_buildings(count, xsize, ysize); /* Add monsters */ entrance_monsters(xsize, ysize); } /* * Draw a the entrance to a dungeon applicable to its 'habitat'. */ void draw_dungeon(place_type *pl_ptr) { int x, y; int i, j; /* Paranoia */ if (pl_ptr->region) quit("Dungeon entrance already has region during creation."); /* Get region */; create_region(pl_ptr, pl_ptr->xsize * WILD_BLOCK_SIZE, pl_ptr->ysize * WILD_BLOCK_SIZE, REGION_OVER); /* Hack - do not increment refcount here - let allocate_block do that */ /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant layout */ Rand_value = pl_ptr->seed; /* Save for monster placement on entrance */ dun_habitat = pl_ptr->dungeon->habitat; dun_level = pl_ptr->dungeon->min_level + 1; /* Hack - no monsters if have been here before */ if (pl_ptr->dungeon->recall_depth) dun_habitat = 0; switch (pl_ptr->dungeon->habitat) { case RF7_DUN_DARKWATER: { draw_dun_dark_water(); break; } case RF7_DUN_LAIR: case RF7_DUN_CAVERN: case RF7_DUN_HELL: { draw_dun_cave(); break; } case RF7_DUN_TEMPLE: { draw_dun_temple(); break; } case RF7_DUN_TOWER: case RF7_DUN_PLANAR: case RF7_DUN_HORROR: { draw_dun_tower(); break; } case RF7_DUN_RUIN: { draw_dun_ruin(); break; } case RF7_DUN_GRAVE: { draw_dun_grave(); break; } case RF7_DUN_MINE: { draw_dun_mine(); break; } case RF7_DUN_CITY: { draw_dun_city(); break; } default: { /* A really crappy dungeon entrance */ /* Get location of stairs */ x = randint1(14); y = randint1(14); /* Put dungeon floor next to stairs so they are easy to find. */ for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { /* Convert square to dungeon floor */ set_feat_bold(x + i, y + j, FEAT_FLOOR); } } /* Add down stairs */ set_feat_bold(x, y, FEAT_MORE); } } /* Hack -- use the "complex" RNG */ Rand_quick = FALSE; } /* * Look to see if a wilderness block is able to have * a town/dungeon overlayed on top. */ static bool blank_spot(int x, int y, int xsize, int ysize, int town_num, bool town) { int i, j; wild_gen2_type *w_ptr; int dist; /* Hack - Population check */ if (town_num != 1) { if (randint0(256) > wild[y][x].trans.pop_map) return (FALSE); } for (i = x - 1; i < x + xsize + 2; i++) { for (j = y - 1; j < y + ysize + 2; j++) { /* Hack - Not next to boundary */ if ((i <= 0) || (i >= max_wild - 1) || (j <= 0) || (j >= max_wild - 1)) { return (FALSE); } w_ptr = &wild[j][i].trans; /* No place already */ if (w_ptr->place) return (FALSE); /* No water or lava or acid */ if (w_ptr->info & (WILD_INFO_WATER | WILD_INFO_LAVA | WILD_INFO_ACID)) return (FALSE); /* No Ocean */ if (w_ptr->hgt_map < (256 / SEA_FRACTION)) return (FALSE); } } if (town) { dist = MIN_DIST_TOWN; } else { dist = MIN_DIST_DUNGEON; } /* Look to see if another place is too close */ for (i = 1; i < town_num; i++) { if (distance(place[i].x, place[i].y, x, y) < dist) { /* Too close? */ return (FALSE); } } /* Ok then */ return (TRUE); } #define DUN_LIST_NUM 12 /* * A few dungeon types. * * {Object theme}, habitat, minlevel, maxlevel, chance, * population, height, * rooms, * floor, * roads, * liquid, * flags} */ static const dun_gen_type dungeons[] = { {{0, 10, 0, 40}, RF7_DUN_DARKWATER, 1, 15, 1, 100, 0, RT_SIMPLE | RT_NATURAL | RT_ANIMAL | RT_STRANGE, FEAT_DRY_MUD, LQ_WATER | LQ_SWAMP, DF_TRACK | DF_ROAD}, {{50, 10, 10, 0}, RF7_DUN_LAIR, 10, 50, 1, 100, 100, RT_NATURAL | RT_COMPLEX | RT_RUIN, FEAT_DIRT, LQ_WATER | LQ_ACID | LQ_SWAMP, DF_NONE}, {{10, 30, 30, 30}, RF7_DUN_TEMPLE, 20, 60, 1, 250, 250, RT_SIMPLE | RT_COMPLEX | RT_DENSE | RT_FANCY | RT_BUILDING | RT_CRYPT, FEAT_FLOOR_TILE, LQ_WATER | LQ_LAVA, DF_ROAD}, {{20, 0, 80, 0}, RF7_DUN_TOWER, 20, 60, 1, 250, 200, RT_SIMPLE | RT_COMPLEX | RT_BUILDING | RT_RVAULT, FEAT_FLOOR_WOOD, LQ_ACID | LQ_LAVA, DF_TRACK}, {{10, 20, 20, 0}, RF7_DUN_RUIN, 20, 80, 1, 0, 150, RT_RUIN, FEAT_PEBBLES, LQ_WATER | LQ_LAVA | LQ_SWAMP, DF_TRACK | DF_ROAD}, {{50, 20, 20, 0}, RF7_DUN_GRAVE, 30, 100, 1, 50, 150, RT_COMPLEX | RT_FANCY | RT_CRYPT, FEAT_FLOOR_TILE, LQ_WATER | LQ_SWAMP, DF_TRACK | DF_ROAD}, {{30, 30, 30, 10}, RF7_DUN_CAVERN, 40, 80, 1, 50, 200, RT_SIMPLE | RT_ANIMAL | RT_DENSE | RT_RUIN | RT_RVAULT, FEAT_DIRT, LQ_WATER | LQ_ACID | LQ_LAVA, DF_TRACK}, {{30, 30, 40, 0}, RF7_DUN_PLANAR, 40, 127, 1, 0, 250, RT_COMPLEX | RT_DENSE | RT_FANCY | RT_RVAULT, FEAT_SAND, LQ_ACID | LQ_LAVA, DF_TRACK}, {{20, 40, 40, 0}, RF7_DUN_HELL, 60, 127, 1, 0, 0, RT_SIMPLE | RT_NATURAL | RT_ANIMAL | RT_DENSE | RT_RUIN | RT_FANCY | RT_RVAULT | RT_STRANGE, FEAT_SOLID_LAVA, LQ_LAVA, DF_TRACK}, {{0, 20, 20, 0}, RF7_DUN_HORROR, 80, 127, 1, 0, 150, RT_SIMPLE | RT_NATURAL | RT_ANIMAL | RT_DENSE | RT_RUIN | RT_STRANGE, FEAT_SALT, LQ_ACID, DF_TRACK}, {{10, 20, 10, 40}, RF7_DUN_MINE, 1, 40, 1, 200, 200, RT_SIMPLE | RT_NATURAL | RT_ANIMAL | RT_RUIN | RT_STRANGE, FEAT_DIRT, LQ_WATER | LQ_LAVA, DF_ROAD}, {{30, 30, 10, 10}, RF7_DUN_CITY, 20, 60, 1, 200, 200, RT_SIMPLE | RT_COMPLEX | RT_DENSE | RT_FANCY | RT_BUILDING | RT_CRYPT | RT_RVAULT | RT_STRANGE, FEAT_FLOOR_TILE, LQ_WATER, DF_TRACK | DF_ROAD}, {{0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, FEAT_NONE, LQ_NONE, DF_NONE}, }; /* * Pick a type of dungeon from the above list */ const dun_gen_type *pick_dungeon_type(void) { int tmp, total; const dun_gen_type *d_ptr; /* Calculate the total possibilities */ for (d_ptr = dungeons, total = 0; d_ptr->habitat; d_ptr++) { /* Count this possibility */ if (d_ptr->min_level > p_ptr->depth) continue; /* Normal selection */ total += d_ptr->chance * MAX_DEPTH * 10 / (p_ptr->depth - d_ptr->min_level + 5); } /* Pick a random type */ tmp = randint0(total); /* Find this type */ for (d_ptr = dungeons, total = 0; d_ptr->habitat; d_ptr++) { /* Count this possibility */ if (d_ptr->min_level > p_ptr->depth) continue; total += d_ptr->chance * MAX_DEPTH * 10 / (p_ptr->depth - d_ptr->min_level + 5); /* Found the type */ if (tmp < total) break; } /* Return the index of the chosen dungeon */ return (d_ptr); } cptr dungeon_type_name(u32b dun) { switch(dun) { case RF7_DUN_DARKWATER: return ("Darkwater"); case RF7_DUN_LAIR: return("Lair"); case RF7_DUN_TEMPLE: return("Temple"); case RF7_DUN_TOWER: return("Tower"); case RF7_DUN_RUIN: return("Ruin"); case RF7_DUN_GRAVE: return("Grave"); case RF7_DUN_CAVERN: return("Cavern"); case RF7_DUN_PLANAR: return("Planar"); case RF7_DUN_HELL: return("Hell"); case RF7_DUN_HORROR: return("Horror"); case RF7_DUN_MINE: return("Mine"); case RF7_DUN_CITY: return("City"); default: return ("Unknown"); } } /* Save dungeon information so we know what to build later */ static void init_dungeon(place_type *pl_ptr, const dun_gen_type *d_ptr) { dun_type *dt_ptr; /* Create it */ MAKE(pl_ptr->dungeon, dun_type); dt_ptr = pl_ptr->dungeon; /* Set the object theme (structure copy) */ dt_ptr->theme = d_ptr->theme; /* Hack - Reset the dungeon habitat to be everything */ dt_ptr->habitat = d_ptr->habitat; /* Save level bounds */ dt_ptr->min_level = (d_ptr->min_level * rand_range(80, 120) + 50) / 100; dt_ptr->max_level = (d_ptr->max_level * rand_range(80, 120) + 50) / 100; /* Cap min/max level */ if (dt_ptr->min_level < 1) dt_ptr->min_level = 1; if (dt_ptr->max_level > 127) dt_ptr->max_level = 127; /* Copy dungeon creation info */ dt_ptr->rooms = d_ptr->rooms; dt_ptr->floor = d_ptr->floor; dt_ptr->liquid = d_ptr->liquid; /* Extra flags */ dt_ptr->flags = d_ptr->flags; } /* Hack - return the current type of "floor" */ byte the_floor(void) { /* In the wilderness */ if (!p_ptr->depth) return (FEAT_DIRT); /* In the dungeon */ return (place[p_ptr->place_num].dungeon->floor); } static bool create_towns(int *xx, int *yy) { int x, y, i; bool first_try = TRUE; wild_gen2_type *w_ptr; place_type *pl_ptr; /* Variables to pick "easiest" town. */ u16b best_town = 0, town_value = 0; /* * Try to add z_info->wp_max towns. */ while (place_count < NUM_TOWNS) { if (first_try) { /* Try the "easiest" spot in the wilderness */ x = *xx; y = *yy; } else { /* Get a random position */ x = randint0(max_wild); y = randint0(max_wild); } /* * See if a city will fit. * (Need a 8x8 block free.) */ if (!blank_spot(x, y, 8, 8, place_count, TRUE)) { /* Need to make town on easiest place */ if (first_try) return (FALSE); continue; } /* Generate it (could use short-circuit here, but is ugly) */ if (!create_city(x, y, place_count)) { /* Need to make town on easiest place */ if (first_try) return (FALSE); continue; } /* We have a town at the easiest spot */ first_try = FALSE; /* get wildernesss + place pointers */ w_ptr = &wild[y][x].trans; pl_ptr = &place[place_count]; /* Check to see if the town has stairs */ for (i = 0; i < pl_ptr->numstores; i++) { if (pl_ptr->store[i].type == BUILD_STAIRS) { /* Create dungeon information */ if (!pl_ptr->dungeon) { /* Use sewer */ init_dungeon(pl_ptr, &dungeons[0]); } /* Select easiest town */ if (w_ptr->law_map > town_value) { /* Save this town */ town_value = w_ptr->law_map; best_town = place_count; /* Done */ continue; } } } /* Increment number of places */ place_count++; } /* Paranoia */ if (!best_town) return (FALSE); /* Hack - the starting town uses pre-defined stores */ for (i = 0; i < place[best_town].numstores; i++) { if (i == 0) { /* Hack - make stairs */ store_init(best_town, i, wild_first_town[i]); } else if (i < START_STORE_NUM) { if (build_is_store(wild_first_town[i])) { /* Hack - use the pre-defined stores */ store_init(best_town, i, wild_first_town[i]); } else { build_init(best_town, i, wild_first_town[i]); } } else { /* Blank spot */ general_init(best_town, i, BUILD_NONE); } } pl_ptr = &place[best_town]; /* Build starting city / town */ draw_city(pl_ptr); place_player_start(&p_ptr->wilderness_x, &p_ptr->wilderness_y, best_town); /* Hack - No current region */ set_region(0); *xx = pl_ptr->x; *yy = pl_ptr->y; /* Success */ return (TRUE); } /* * What is the "score" for a dungeon of the given type * at this location in the wilderness? * The lower the score, the better the match. */ static long score_dungeon(const wild_gen2_type *w_ptr, const dun_gen_type *d_ptr, int dist) { long score = 0, value; /* Height */ value = w_ptr->hgt_map - d_ptr->height; score += value * value; /* Population */ value = w_ptr->pop_map - d_ptr->pop; score += value * value; /* Lawless level */ value = w_ptr->law_map - d_ptr->min_level; score += value * value; /* Near dungeons should be easy */ value = dist * d_ptr->min_level; score += value * value; return (score); } /* Add in dungeons into the wilderness */ static void create_dungeons(int xx, int yy) { byte i, j; int x, y; int best; long best_val, score; place_type *pl_ptr; wild_gen2_type *w_ptr; int dungeon_list[NUM_DUNGEON]; /* * Scan for places to add dungeons. */ while (place_count < NUM_TOWNS + NUM_DUNGEON) { /* Get a random position */ x = randint0(max_wild); y = randint0(max_wild); pl_ptr = &place[place_count]; /* * See if a dungeon will fit. * (Need a 8x8 block free.) */ if (!blank_spot(x, y, 8, 8, place_count, FALSE)) continue; pl_ptr->x = x; pl_ptr->y = y; /* Hack - the size is constant... (Is this even needed?) */ pl_ptr->xsize = 8; pl_ptr->ysize = 8; /* We are a dugneon */ pl_ptr->type = TOWN_DUNGEON; /* We have monsters */ pl_ptr->monst_type = TOWN_MONST_ABANDONED; /* Hack - A really crap name */ strcpy(pl_ptr->name, "Dungeon"); /* Increment number of places */ place_count++; } /* Select list of dungeon types to use */ for (i = 0; i < NUM_DUNGEON; i++) { /* Make sure we have at least one of each */ if (i < DUN_LIST_NUM) { dungeon_list[i] = i; } else { dungeon_list[i] = randint0(DUN_LIST_NUM); } } /* Match available dungeon types to locations */ for (i = 0; i < NUM_DUNGEON; i++) { /* Score each available location, and pick the best one. */ best = -1; best_val = -1; for (j = NUM_TOWNS; j < NUM_TOWNS + NUM_DUNGEON; j++) { pl_ptr = &place[j]; /* Skip already created dungeons */ if (pl_ptr->dungeon) continue; /* Get location */ w_ptr = &wild[pl_ptr->y][pl_ptr->x].trans; score = score_dungeon(w_ptr, &dungeons[dungeon_list[i]], distance(xx, yy, pl_ptr->x, pl_ptr->y)); /* Better dungeon? */ if ((best == -1) || (score < best_val)) { best = j; best_val = score; } } /* Initialise best dungeon */ init_dungeon(&place[best], &dungeons[dungeon_list[i]]); } /* Link dungeons to the wilderness */ for (i = NUM_TOWNS; i < NUM_TOWNS + NUM_DUNGEON; i++) { /* Get the place */ pl_ptr = &place[i]; /* Draw it */ draw_dungeon(pl_ptr); /* We are now using the region */ incref_region(pl_ptr->region); /* Link to wilderness */ set_place(i); /* Finish with the region allocated by draw_dungeon() */ pl_ptr->region = unref_region(pl_ptr->region); } } /* * Place the quests on the wilderness */ static void create_quests(int xx, int yy) { int x, y; int xsize, ysize; byte flags; /* * Try to add z_info->wp_max towns. */ while (place_count < z_info->wp_max) { /* Get a random position */ x = randint0(max_wild); y = randint0(max_wild); /* Not too close to the starting town */ if (distance(xx, yy, x, y) < 20) continue; /* Pick quest size / type */ pick_wild_quest(&xsize, &ysize, &flags); /* See if a quest will fit */ if (!quest_blank(x, y, xsize, ysize, place_count, flags)) continue; /* Build it */ if (!create_quest(x, y, place_count)) continue; /* Increment number of places */ place_count++; } } /* * Initialise the place structures * * There are currently, cities and quests. * * Soon there will be: * Ruins, barracks, towers etc. */ bool init_places(int xx, int yy) { /* No towns yet */ place_count = 1; /* Create towns */ if (!create_towns(&xx, &yy)) return (FALSE); /* Create dungeons */ create_dungeons(xx, yy); /* Create quests */ create_quests(xx, yy); /* Hack - set global region back to wilderness value */ set_region(0); /* Done */ return (TRUE); } /* * Builds a store at a given pseudo-location * * As of Z 2.5.0 the town is moved back to (0,0) - and is overlayed * on top of the wilderness. * * As of 2.8.1 (?) the town is actually centered in the middle of a * complete level, and thus the top left corner of the town itself * is no longer at (0,0), but rather, at (qy,qx), so the constants * in the comments below should be mentally modified accordingly. * * As of 2.7.4 (?) the stores are placed in a more "user friendly" * configuration, such that the four "center" buildings always * have at least four grids between them, to allow easy running, * and the store doors tend to face the middle of town. * * The stores now lie inside boxes from 3-9 and 12-18 vertically, * and from 7-17, 21-31, 35-45, 49-59. Note that there are thus * always at least 2 open grids between any disconnected walls. * * Note the use of "town_illuminate()" to handle all "illumination" * and "memorization" issues. */ static void build_store(int xx, int yy, store_type *st_ptr) { int y, x, y0, x0, y1, x1, y2, x2, tmp; cave_type *c_ptr; /* Find the "center" of the store */ y0 = yy * 6 + 4; x0 = xx * 16 + 8; /* Determine the store boundaries */ y1 = y0 - randint1(2); y2 = y0 + randint1(2); x1 = x0 - randint1(5); x2 = x0 + randint1(5); /* Build an invulnerable rectangular building */ for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { /* Create the building */ set_feat_bold(x, y, FEAT_PERM_EXTRA); } } /* Pick a door direction (S,N,E,W) */ tmp = randint0(4); /* Re-roll "annoying" doors */ if (((tmp == 0) && (yy == 2)) || ((tmp == 1) && (yy == 0)) || ((tmp == 2) && (xx == 2)) || ((tmp == 3) && (xx == 0))) { /* Pick a new direction */ tmp = randint0(4); } /* Extract a "door location" */ switch (tmp) { case 0: { /* Bottom side */ y = y2; x = rand_range(x1, x2); break; } case 1: { /* Top side */ y = y1; x = rand_range(x1, x2); break; } case 2: { /* Right side */ y = rand_range(y1, y2); x = x2; break; } default: { /* Left side */ y = rand_range(y1, y2); x = x1; break; } } c_ptr = cave_p(x, y); /* Clear previous contents, add a store door */ set_feat_grid(c_ptr, FEAT_FLOOR); c_ptr->fld_idx = wild_build[st_ptr->type].field; /* Save location of store door */ st_ptr->x = x; st_ptr->y = y; } /* * Generate the "consistent" town features, and place the player * * Hack -- play with the R.N.G. to always yield the same town * layout, including the size and shape of the buildings, the * locations of the doorways, and the location of the stairs. * * This simple routine does not check the type of stores town_num wants. */ static void town_gen_hack(place_type *pl_ptr) { int y, x, k, n, xx, yy; /* Add an extra column to make it symmetrical */ int rooms[3 * 4]; cave_type *c_ptr; /* Prepare an array of "remaining stores", and count them */ for (n = 0; n < 3 * 4; n++) rooms[n] = n; /* Place three rows of stores */ for (y = 0; y < 3; y++) { /* Place four stores per row */ for (x = 0; x < 4; x++) { /* Pick a random unplaced store */ k = ((n <= 1) ? 0 : randint0(n)); /* Only build real stores */ if (rooms[k] < MAX_STORES) { /* Build that store at the proper location */ build_store(x, y, &pl_ptr->store[rooms[k]]); } /* Shift the stores down, remove one store */ rooms[k] = rooms[--n]; } } /* Place the stairs */ while (TRUE) { /* Pick a location at least "three" from the outer walls */ yy = rand_range(3, TOWN_HGT - 4); xx = rand_range(3, TOWN_WID - 4); c_ptr = cave_p(xx, yy); /* If square is a shop then try again */ if (!cave_naked_grid(c_ptr)) continue; /* Blank square */ break; } /* Put dungeon floor next to stairs so they are easy to find. */ for (y = -1; y <= 1; y++) { for (x = -1; x <= 1; x++) { c_ptr = cave_p(xx + x, yy + y); if (!cave_naked_grid(c_ptr)) continue; /* Convert square to dungeon floor */ set_feat_grid(c_ptr, FEAT_FLOOR); } } /* Clear previous contents, add down stairs */ set_feat_bold(xx, yy, FEAT_MORE); wild_stairs_x = xx; wild_stairs_y = yy; } /* * Town logic flow for generation of new town * * We start with a fully wiped cave of normal floors. * * Note that town_gen_hack() plays games with the R.N.G. * * This function does NOT do anything about the owners of the stores, * nor the contents thereof. It only handles the physical layout. * * xx and yy point to the location of the stairs (So the player can * start there.) * * (Vanilla town only now.) */ void van_town_gen(place_type *pl_ptr) { int y, x; cave_type *c_ptr; /* Paranoia */ if (pl_ptr->region) quit("Town already has region during creation."); /* Get region */ create_region(pl_ptr, V_TOWN_BLOCK_WID, V_TOWN_BLOCK_HGT, REGION_OVER); /* Hack - do not increment refcount here - let allocate_block do that */ /* Place transparent area */ for (y = 0; y < V_TOWN_BLOCK_HGT; y++) { for (x = 0; x < V_TOWN_BLOCK_WID; x++) { c_ptr = cave_p(x, y); /* Create empty area */ set_feat_grid(c_ptr, FEAT_PERM_EXTRA); } } /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant town layout */ Rand_value = pl_ptr->seed; /* Place some floors */ for (y = 1; y < TOWN_HGT - 1; y++) { for (x = 1; x < TOWN_WID - 1; x++) { /* Create see-through terrain */ set_feat_bold(x, y, FEAT_FLOOR); } } /* Build stuff */ town_gen_hack(pl_ptr); /* Hack -- use the "complex" RNG */ Rand_quick = FALSE; } /* * Place a single town in the middle of the tiny wilderness */ void init_vanilla_town(void) { int i, j; place_type *pl_ptr = &place[1]; dun_type *d_ptr; /* Only one town */ strcpy(pl_ptr->name, "Town"); pl_ptr->seed = randint0(0x10000000); pl_ptr->numstores = 9; pl_ptr->type = TOWN_OLD; pl_ptr->x = (max_wild / 2) - TOWN_WID / (WILD_BLOCK_SIZE * 2) - 1; pl_ptr->y = (max_wild / 2) - TOWN_HGT / (WILD_BLOCK_SIZE * 2) - 1; pl_ptr->xsize = V_TOWN_BLOCK_WID / WILD_BLOCK_SIZE; pl_ptr->ysize = V_TOWN_BLOCK_HGT / WILD_BLOCK_SIZE; /* Allocate the stores */ C_MAKE(place[1].store, MAX_STORES, store_type); /* Init the stores */ for (i = 0; i < MAX_STORES; i++) { /* Initialize */ store_init(1, i, (byte)i); } /* Place town on wilderness */ for (i = pl_ptr->x; i < pl_ptr->x + pl_ptr->xsize; i++) { for (j = pl_ptr->y; j < pl_ptr->y + pl_ptr->ysize; j++) { wild[j][i].done.place = 1; } } /* Create dungeon */ MAKE(pl_ptr->dungeon, dun_type); d_ptr = pl_ptr->dungeon; /* Set dungeon depths */ d_ptr->max_level = MAX_DEPTH - 1; d_ptr->min_level = 1; /* Make the town - and get the location of the stairs */ van_town_gen(&place[1]); place_player_start(&p_ptr->wilderness_x, &p_ptr->wilderness_y, 1); /* One town + 1 for bounds */ place_count = 2; /* Hack - set global region back to wilderness value */ set_region(0); } zangband/src/wild3.c0000644000000000000000000013663410250356275013337 0ustar rootroot/* File: wild3.c */ /* Purpose: Wilderness drawing */ /* * Copyright (c) 1989, 2003 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann, Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "wild.h" /* * Helper functions that can be called recursively. (Need function prototypes.) * See make_wild_03() for an instance of this. * This ability will also be used by other routines in the future. */ static void gen_block_helper(blk_ptr block_ptr, byte *data, int gen_type, bool road); static void blend_helper(cave_type *c_ptr, byte *data, int g_type); /* * Generate the selected place */ static void place_gen(place_type *pl_ptr) { switch (pl_ptr->type) { case TOWN_OLD: { van_town_gen(pl_ptr); break; } case TOWN_FRACT: { draw_city(pl_ptr); break; } case TOWN_QUEST: { draw_quest(pl_ptr); break; } case TOWN_DUNGEON: { draw_dungeon(pl_ptr); break; } default: { quit("Unknown town/quest type in wilderness"); } } /* Hack - set global region back to wilderness value */ set_region(0); } /* * Overlay the town block * If the town is not built correctly, build it */ static void overlay_place(int x, int y, u16b w_place, blk_ptr block_ptr) { int i, j, x1, y1, x2, y2; int fld_idx, type; wild_done_type *w_ptr = &wild[y][x].done; /* Generation level for monsters and objects */ int level = w_ptr->mon_gen; cave_type *c_ptr; place_type *pl_ptr = &place[w_place]; /* Check that place region exists */ if (!pl_ptr->region) { /* Create the place */ place_gen(pl_ptr); } /* Paranoia */ if (!pl_ptr->region) quit("Could not get a region for the town/quest"); /* Find block to copy */ x1 = (x - place[w_place].x) * WILD_BLOCK_SIZE; y1 = (y - place[w_place].y) * WILD_BLOCK_SIZE; /* copy 16x16 block from the region */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { for (i = 0; i < WILD_BLOCK_SIZE; i++) { /* Get pointer to overlay info */ c_ptr = access_region(x1 + i, y1 + j, pl_ptr->region); /* Only copy terrain if there is something there. */ if (c_ptr->feat != FEAT_NONE) { /* Copy the terrain */ block_ptr[j][i].feat = c_ptr->feat; } /* Get destination */ x2 = x * WILD_BLOCK_SIZE + i; y2 = y * WILD_BLOCK_SIZE + j; /* * Instantiate field * * Note that most types of field are not in this list. * * Doors, buildings, traps, quests etc. * are all that are in this list. */ fld_idx = c_ptr->fld_idx; if (fld_idx) { type = t_info[c_ptr->fld_idx].type; } else { type = FTYPE_NOTHING; } switch (type) { case FTYPE_NOTHING: { /* Nothing */ break; } case FTYPE_TRAP: { /* Trap */ /* Activate the trap */ if (place_field(x2, y2, c_ptr->fld_idx)) { field_type *f_ptr = &fld_list[block_ptr[j][i].fld_idx]; /* Initialise it */ (void)field_script_single(f_ptr, FIELD_ACT_INIT, ""); } break; } case FTYPE_DOOR: { /* Door */ int data = 9; /* Add a door field */ if (place_field(x2, y2, c_ptr->fld_idx)) { field_type *f_ptr = &fld_list[block_ptr[j][i].fld_idx]; /* Add "power" of lock / jam to the field */ (void)field_script_single(f_ptr, FIELD_ACT_INIT, "i:", LUA_VAR_NAMED(data, "power")); } break; } case FTYPE_BUILD: { /* Stores + buildings */ (void)place_field(x2, y2, c_ptr->fld_idx); break; } } /* * Instantiate object */ place_specific_object(x2, y2, level, c_ptr->o_idx); /* * Instantiate monster */ if (c_ptr->m_idx) { place_monster_one(x2, y2, c_ptr->m_idx, FALSE, FALSE, FALSE); } } } } /* Clear the temporary block */ void clear_temp_block(void) { int i, j; /* Clear the section */ for (i = 0; i <= WILD_BLOCK_SIZE; i++) { for (j = 0; j <= WILD_BLOCK_SIZE; j++) { /* MAX_SHORT is a flag for "not done yet" */ temp_block[j][i] = MAX_SHORT; } } } /* Set the corners of the temporary block to val */ void set_temp_corner_val(u16b val) { temp_block[0][0] = val; temp_block[0][WILD_BLOCK_SIZE] = val; temp_block[WILD_BLOCK_SIZE][0] = val; temp_block[WILD_BLOCK_SIZE][WILD_BLOCK_SIZE] = val; } /* Set the middle of the temporary block to val */ void set_temp_mid(u16b val) { temp_block[WILD_BLOCK_SIZE / 2][WILD_BLOCK_SIZE / 2] = val; } /* * Initialise the temporary block based on the value of a wilderness * 'info' flag for gradient information. * * This is used for rivers, beaches, lakes etc. */ static bool wild_info_bounds(int x, int y, byte info) { int i, x1, y1; bool grad1[10], grad2[10], any; /* No flags set yet */ any = FALSE; /* If center is set, then whole square is "on" */ if (wild[y][x].done.info & info) { /* Set all flags */ grad1[5] = TRUE; /* A flag is set */ any = TRUE; } else { /* Check each adjacent square to see if flag is set */ for (i = 1; i < 10; i++) { /* Get direction */ x1 = x + ddx[i]; y1 = y + ddy[i]; grad1[i] = FALSE; /* Check bounds */ if ((x1 >= 0) && (x1 < max_wild) && (y1 >= 0) && (y1 < max_wild)) { /* Check flag status */ if (wild[y1][x1].done.info & info) { /* Flag is set */ grad1[i] = TRUE; any = TRUE; } } } } /* Exit if there are no set flags */ if (any == FALSE) return (FALSE); /* Clear temporary block */ clear_temp_block(); /* Set grad2[] depending on values of grad1[] */ /* If center is set - all are set */ if (grad1[5]) { for (i = 1; i < 10; i++) { grad2[i] = TRUE; } } else { /* Clear grad2[] */ for (i = 1; i < 10; i++) { grad2[i] = FALSE; } /* Copy orthogonal flags */ for (i = 1; i < 5; i++) { grad2[i * 2] = grad1[i * 2]; } /* Set diagonally adjacent flags depending on values of orthogonal flags. */ /* Upper left */ if (grad1[4] || grad1[8]) { grad2[7] = TRUE; } /* Upper right */ if (grad1[8] || grad1[6]) { grad2[9] = TRUE; } /* Lower right */ if (grad1[6] || grad1[2]) { grad2[3] = TRUE; } /* Lower left */ if (grad1[2] || grad1[4]) { grad2[1] = TRUE; } } /* If a flag is set - make that side maximum */ for (i = 1; i < 10; i++) { /* Hack - get only orthogonal directions */ x1 = (1 + ddx[i]) * WILD_BLOCK_SIZE / 2; y1 = (1 + ddy[i]) * WILD_BLOCK_SIZE / 2; if (grad2[i]) { temp_block[y1][x1] = WILD_BLOCK_SIZE * 256; } else { temp_block[y1][x1] = WILD_BLOCK_SIZE * 64; } } /* There are flags set */ return (TRUE); } /* * Explanation of the plasma fractal algorithm: * * A grid of points is created with the properties of a 'height-map' * This is done by making the corners of the grid have a random value. * The grid is then subdivided into one with twice the resolution. * The new points midway between two 'known' points can be calculated * by taking the average value of the 'known' ones and randomly adding * or subtracting an amount proportional to the distance between those * points. The final 'middle' points of the grid are then calculated * by averaging all four of the originally 'known' corner points. An * random amount is added or subtracted from this to get a value of the * height at that point. The scaling factor here is adjusted to the * slightly larger distance diagonally as compared to orthogonally. * * This is then repeated recursively to fill an entire 'height-map' * A rectangular map is done the same way, except there are different * scaling factors along the x and y directions. * */ void frac_block(void) { u16b lstep, hstep, i, j, size; /* * Size is one bigger than normal blocks for speed * of algorithm with 2^n + 1 */ size = WILD_BLOCK_SIZE; /* Initialize the step sizes */ lstep = hstep = size; /* * Fill in the square with fractal height data - * like the 'plasma fractal' in fractint. */ while (hstep > 1) { /* Halve the step sizes */ lstep = hstep; hstep /= 2; /* middle top to bottom. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = 0; j <= size; j += lstep) { /* only write to points that are "blank" */ if (temp_block[j][i] == MAX_SHORT) { /* Average of left and right points +random bit */ temp_block[j][i] = (((temp_block[j][i - hstep] + temp_block[j][i + hstep]) + (randint1(lstep * 256) - (hstep * 256))) / 2); } } } /* middle left to right. */ for (j = hstep; j <= size - hstep; j += lstep) { for (i = 0; i <= size; i += lstep) { /* only write to points that are "blank" */ if (temp_block[j][i] == MAX_SHORT) { /* Average of up and down points +random bit */ temp_block[j][i] = (((temp_block[j - hstep][i] + temp_block[j + hstep][i]) + (randint1(lstep * 256) - (hstep * 256))) / 2); } } } /* center. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = hstep; j <= size - hstep; j += lstep) { /* only write to points that are "blank" */ if (temp_block[j][i] == MAX_SHORT) { /* * Average over all four corners + scale by 181 to * reduce the effect of the square grid on the * shape of the fractal */ temp_block[j][i] = ((temp_block[j - hstep][i - hstep] + temp_block[j + hstep][i - hstep] + temp_block[j - hstep][i + hstep] + temp_block[j + hstep][i + hstep]) / 4) + (((randint1(lstep * 256) - (hstep * 256)) * 181) / 256); } } } } } /* * This function smoothly interpolates between * the points on the grid. (As opposed to frac_block() * which adds random offsets to make a rough * pattern. */ static void smooth_block(void) { u16b lstep, hstep, i, j, size; /* * Size is one bigger than normal blocks for speed * of algorithm with 2^n + 1 */ size = WILD_BLOCK_SIZE; /* Initialize the step sizes */ lstep = hstep = size; while (hstep > 1) { /* Halve the step sizes */ lstep = hstep; hstep /= 2; /* middle top to bottom. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = 0; j <= size; j += lstep) { /* only write to points that are "blank" */ if (temp_block[j][i] == MAX_SHORT) { /* Average of left and right points */ temp_block[j][i] = ((temp_block[j][i - hstep] + temp_block[j][i + hstep]) / 2); } } } /* middle left to right. */ for (j = hstep; j <= size - hstep; j += lstep) { for (i = 0; i <= size; i += lstep) { /* only write to points that are "blank" */ if (temp_block[j][i] == MAX_SHORT) { /* Average of up and down points */ temp_block[j][i] = ((temp_block[j - hstep][i] + temp_block[j + hstep][i]) / 2); } } } /* center. */ for (i = hstep; i <= size - hstep; i += lstep) { for (j = hstep; j <= size - hstep; j += lstep) { /* only write to points that are "blank" */ if (temp_block[j][i] == MAX_SHORT) { /* Average of corner points */ temp_block[j][i] = ((temp_block[j - hstep][i - hstep] + temp_block[j + hstep][i - hstep] + temp_block[j - hstep][i + hstep] + temp_block[j + hstep][i + hstep]) / 4); } } } } } /* * This function picks a terrain feature from a list of four * based on a "probability factor". The further 'prob' is * from 'prob1' etc. the less likely that feature is. * This weights the distribution. * * As a special case, feature 0 is defined to be "nonexistant" * so that choices can be made with less than 4 features. */ static byte pick_feat(byte feat1, byte feat2, byte feat3, byte feat4, byte prob1, byte prob2, byte prob3, byte prob4, byte prob) { /* Chance factors */ u32b c1, c2, c3, c4, choice; /* Zero the chance factors */ c1 = c2 = c3 = c4 = 0; /* Calculate chance factors if feature != 0 */ if (feat1) { if (prob1 == prob) { c1 = 0x1000000; } else { c1 = 0x1000000 / ABS((long)prob1 - prob); } } if (feat2) { if (prob2 == prob) { c2 = 0x1000000; } else { c2 = 0x1000000 / ABS((long)prob2 - prob); } } if (feat3) { if (prob3 == prob) { c3 = 0x1000000; } else { c3 = 0x1000000 / ABS((long)prob3 - prob); } } if (feat4) { if (prob4 == prob) { c4 = 0x1000000; } else { c4 = 0x1000000 / ABS((long)prob4 - prob); } } /* get choice */ choice = Rand_div(c1 + c2 + c3 + c4); /* Return terrain feature based on weighted chance */ if (choice < c1) return (feat1); choice -= c1; if (choice < c2) return (feat2); choice -= c2; if (choice < c3) return (feat3); return (feat4); } /* * This function creates the sea based on the number in sea_type. * The higher the number - the greater the chance of deeper water. * * Note WILD_SEA and above generation types are reserved for use * with this function. */ static void make_wild_sea(blk_ptr block_ptr, byte sea_type) { int i, j; for (j = 0; j < WILD_BLOCK_SIZE; j++) { for (i = 0; i < WILD_BLOCK_SIZE; i++) { block_ptr[j][i].feat = pick_feat(FEAT_SHAL_WATER, FEAT_DEEP_WATER, FEAT_OCEAN_WATER, FEAT_NONE, 0, 10, 20, 40, sea_type); block_ptr[j][i].info = 0; } } } /* * Build a road or a track at this location * * This function counts the number of other road / tracks * adjacent to this square, and uses that information to * build a plasma fractal. The fractal then is used to * make a road. */ static void make_wild_road(blk_ptr block_ptr, int x, int y) { int i, j, x1, y1; u16b grad1[10], grad2[10], any; cave_type *c_ptr; bool bridge = FALSE, need_bridge = FALSE; /* Only draw if road is on the square */ if (!(wild[y][x].done.info & (WILD_INFO_TRACK | WILD_INFO_ROAD))) { /* No flags set yet */ any = FALSE; /* Only do the sides */ for (i = 2; i < 10; i += 2) { /* Get direction */ x1 = x + ddx[i]; y1 = y + ddy[i]; grad1[i] = 0; /* Check bounds */ if ((x1 >= 0) && (x1 < max_wild) && (y1 >= 0) && (y1 < max_wild)) { /* Check flag status */ if (wild[y1][x1].done.info & WILD_INFO_TRACK) { /* Flag is set */ grad1[i] = TRACK_LEVEL; any = TRUE; } if (wild[y1][x1].done.info & WILD_INFO_ROAD) { /* Flag is set */ grad1[i] = ROAD_LEVEL; any = TRUE; /* Bridges are narrow */ if (wild[y1][x1].done.info & WILD_INFO_WATER) { grad1[i] = TRACK_LEVEL; } } } } /* No nearby roads */ if (!any) return; /* Convert from grad1 to grad2 */ for (i = 1; i < 10; i++) { grad2[i] = GROUND_LEVEL; } /* Upper left */ if (grad1[4] && grad1[8]) { grad2[7] = MAX(grad1[4], grad1[8]); any = FALSE; } /* Upper right */ if (grad1[8] && grad1[6]) { grad2[9] = MAX(grad1[8], grad1[6]); any = FALSE; } /* Lower right */ if (grad1[6] && grad1[2]) { grad2[3] = MAX(grad1[6], grad1[2]); any = FALSE; } /* Lower left */ if (grad1[2] && grad1[4]) { grad2[1] = MAX(grad1[2], grad1[4]); any = FALSE; } /* Hack - only if there really is a road */ if (any) return; } else { /* Do everything */ /* Check each adjacent square to see if is road or track */ for (i = 1; i < 10; i++) { /* Get direction */ x1 = x + ddx[i]; y1 = y + ddy[i]; grad2[i] = GROUND_LEVEL; /* Check bounds */ if ((x1 >= 0) && (x1 < max_wild) && (y1 >= 0) && (y1 < max_wild)) { /* Check flag status */ if (wild[y1][x1].done.info & WILD_INFO_TRACK) { /* Flag is set */ grad2[i] = TRACK_LEVEL; } if (wild[y1][x1].done.info & WILD_INFO_ROAD) { /* Flag is set */ grad2[i] = ROAD_LEVEL; /* Bridges are narrow */ if (wild[y1][x1].done.info & WILD_INFO_WATER) { grad2[i] = TRACK_LEVEL; } } } } } /* Scan sides for ground */ for (i = 1; i < 10; i++) { /* Get direction */ x1 = x + ddx[i]; y1 = y + ddy[i]; if (wild[y1][x1].done.info & WILD_INFO_WATER) { /* We are over water - so we need a bridge */ need_bridge = TRUE; } else { /* We are adjacent to solid ground */ bridge = TRUE; } } /* Only use wood terrain if we need a bridge */ if (!need_bridge) bridge = FALSE; /* Clear temporary block */ clear_temp_block(); /* Set sides of block */ for (i = 1; i < 10; i++) { x1 = (1 + ddx[i]) * WILD_BLOCK_SIZE / 2; y1 = (1 + ddy[i]) * WILD_BLOCK_SIZE / 2; temp_block[y1][x1] = grad2[i]; } /* Build the road "density map" */ smooth_block(); /* Copy the result over the block */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Is it a road square? */ if (temp_block[j][i] >= ROAD_BORDER) { /* Point to square */ c_ptr = &block_ptr[j][i]; /* Bad liquid terrain? */ if ((c_ptr->feat == FEAT_SHAL_LAVA) || (c_ptr->feat == FEAT_DEEP_LAVA) || (c_ptr->feat == FEAT_SHAL_ACID) || (c_ptr->feat == FEAT_DEEP_ACID)) { c_ptr->feat = FEAT_PEBBLES; } else if (bridge) { c_ptr->feat = FEAT_FLOOR_WOOD; } else if ((c_ptr->feat == FEAT_SHAL_WATER) || (c_ptr->feat == FEAT_DEEP_WATER)) { c_ptr->feat = FEAT_PEBBLES; } else if (c_ptr->feat == FEAT_OCEAN_WATER) { c_ptr->feat = FEAT_SHAL_WATER; } else { if (one_in_(3)) { c_ptr->feat = FEAT_PEBBLES; } else { c_ptr->feat = FEAT_DIRT; } } } } } } /* * Using gradient information given in temp_block, * overlay on top of wilderness block. * * This is used to make rivers, beaches etc. */ static void wild_add_gradient(blk_ptr block_ptr, byte feat1, byte feat2) { int i, j; for (j = 0; j < WILD_BLOCK_SIZE; j++) { for (i = 0; i < WILD_BLOCK_SIZE; i++) { if (temp_block[j][i] >= WILD_BLOCK_SIZE * 213) { /* 25% of the time use the other tile : it looks better this way */ if (one_in_(4)) { block_ptr[j][i].feat = feat1; } else { block_ptr[j][i].feat = feat2; } } else if (temp_block[j][i] >= WILD_BLOCK_SIZE * 128) { /* 25% of the time use the other tile : it looks better this way */ if (one_in_(4)) { block_ptr[j][i].feat = feat2; } else { block_ptr[j][i].feat = feat1; } } } } } /* * Make wilderness generation type 1 * * Make a plasma fractal. Convert the heightmap to terrain * via the pick_feat function. * This routine uses all data fields. * Odd fields in the data[] array are the terrain features. * The even fields are the region of the hieght-map where * those features are most common. */ static void make_wild_01(blk_ptr block_ptr, byte *data, bool road) { int i, j; byte new_feat, element; /* Ignore road for now */ (void)road; /* Initialise temporary block */ clear_temp_block(); set_temp_corner_val(WILD_BLOCK_SIZE * 128); set_temp_mid(WILD_BLOCK_SIZE * 128); /* Generate plasma factal */ frac_block(); /* Make terrain block based on height map */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { for (i = 0; i < WILD_BLOCK_SIZE; i++) { /* Get value */ element = temp_block[j][i] / WILD_BLOCK_SIZE; /* Work out terrain feature to use */ new_feat = pick_feat(data[0], data[2], data[4], data[6], data[1], data[3], data[5], data[7], element); block_ptr[j][i].feat = new_feat; block_ptr[j][i].info = 0; } } } /* * Make wilderness generation type 2 * * Make a uniform field from the feature in data[0] * Next, add the lower probability features in data[2], [4] etc. * using the probabilities in data[1], [3] etc. * Use feat = 0 to mark the end of the list of features. * * This uses a different probability function than type 1. * (It is cumulative.) * * This is good for making "flat density" regions like grasslands etc. */ static void make_wild_02(blk_ptr block_ptr, byte *data, bool road) { int i, j, k; byte new_feat, feat, chance; /* Ignore road for now */ (void)road; for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Init. counter */ k = 0; /* Hack - if first feature is zero - use grass */ feat = FEAT_GRASS; while (1) { /* Get feature */ new_feat = data[k * 2]; /* End of list? */ if (new_feat == 0) break; /* Use new feature */ feat = new_feat; /* Done counting? */ if (k == 3) break; chance = data[k * 2 + 1]; /* Exit if chance is zero */ if (!chance) break; /* Stop if chance fails */ if (randint0(chance + 1)) break; /* Increment counter + loop */ k++; } /* Store feature in block */ block_ptr[j][i].feat = feat; } } } /* * This function makes a wilderness type specifed by data[0]. * It then overlays a "circle" of other terrain on top. * data[1], [2] and [3] specify these. * Note - this function is a major hack. It uses the _number_ * of another wilderness type - which in turn has its own data[] * and generation type. * It is possible to use recursion to make some interesting effects. * These include: Tiny lakes of water, lava, acid. Craters. Rock pillars. * Bogs. Clumps of trees. etc. */ static void make_wild_03(blk_ptr block_ptr, byte *data, bool road) { int i, j, element; /* Call the other routine to make the "base" terrain. */ gen_block_helper(block_ptr, wild_gen_data[data[0]].data, wild_gen_data[data[0]].gen_routine, road); /* Initialise temporary block */ clear_temp_block(); /* Large in center - small on sides */ set_temp_corner_val(WILD_BLOCK_SIZE * 64); set_temp_mid(WILD_BLOCK_SIZE * 256); /* Generate plasma factal */ frac_block(); /* Overlay the "circle" of terrain */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { element = temp_block[j][i]; /* Outside circle? */ if (element < WILD_BLOCK_SIZE * 128) continue; if ((element < WILD_BLOCK_SIZE * 171) && one_in_(2)) { /* Outermost terrain */ block_ptr[j][i].feat = data[1]; continue; } if ((element < WILD_BLOCK_SIZE * 213) && one_in_(2)) { /* Middle terrain */ block_ptr[j][i].feat = data[2]; continue; } /* Inner terrain */ block_ptr[j][i].feat = data[3]; } } } /* * Draw a pleasant field (farm) */ static void make_wild_04(blk_ptr block_ptr, byte *data, bool road) { int x, y, x1, y1, x2, y2, i, j; int type; cave_type *c_ptr; /* Hack - ignore parameter */ (void)data; /* Hack - generate and throw away a few random numbers */ randint0(100); randint0(100); randint0(100); /* Get location of building */ x = rand_range(4, 11); y = rand_range(3, 12); /* Get size of building */ x1 = x - randint1(3); x2 = x + randint1(3); y1 = y - randint1(2); y2 = y + randint1(2); /* Get type of ground */ switch (randint0(8)) { case 0: case 1: case 2: { /* Grass */ type = 1; break; } case 3: #if 0 { /* Use "underlying" type */ gen_block_helper(block_ptr, wild_gen_data[data[0]].data, wild_gen_data[data[0]].gen_routine, road); return; } #endif case 4: { /* Alternating grass & dirt */ type = 3; break; } case 5: { /* Dirt */ type = 2; break; } case 6: { /* Dirt with building */ type = 4; break; } default: { /* Grass with building */ type = 5; break; } } /* * If there is a road or river going through here we should * use types 1 or 2 because we don't want roads running through * buildings and other weirdness. */ if (road && (type > 2)) type = rand_range(1, 2); for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* Get location */ c_ptr = &block_ptr[j][i]; /* Place ground */ if (type == 1 || (type == 3 && j % 2 == 0) || type == 5) { c_ptr->feat = FEAT_GRASS; } else { c_ptr->feat = FEAT_DIRT; } if ((i >= x1) && (i <= x2) && (j >= y1) && (j <= y2) && (type >= 4)) { /* Build an invulnerable rectangular building */ c_ptr->feat = FEAT_PERM_EXTRA; } else if ((i >= x1 - 1) && (i <= x2 + 1) && (j >= y1 - 1) && (j <= y2 + 1) && (type >= 4)) { c_ptr->feat = FEAT_DIRT; } } } } /* * This function blends adjacent sea blocks * (by picking the feat type to use) */ static void blend_sea(cave_type *c_ptr, byte sea_type) { c_ptr->feat = pick_feat(FEAT_SHAL_WATER, FEAT_DEEP_WATER, FEAT_OCEAN_WATER, FEAT_NONE, 0, 10, 20, 40, sea_type); } /* * The function that picks a "blending feature" for wild. gen. type 1 */ static void blend_wild_01(cave_type *c_ptr, byte *data) { /* Store an "average" terrain feature */ c_ptr->feat = pick_feat(data[0], data[2], data[4], data[6], data[1], data[3], data[5], data[7], 128); } /* * The function that picks a "blending feature" for wild. gen. type 2 */ static void blend_wild_02(cave_type *c_ptr, byte *data) { /* Store the most likely terrain feature */ c_ptr->feat = data[0]; } static void blend_helper(cave_type *c_ptr, byte *data, int g_type) { /* Based on type - choose wilderness block generation function */ switch (g_type) { case 1: { /* Fractal plasma with weighted terrain probabilites */ blend_wild_01(c_ptr, data); break; } case 2: { /* Simple weighted probabilities on flat distribution */ blend_wild_02(c_ptr, data); break; } case 3: { /* Use the other terrain's blend function */ blend_helper(c_ptr, wild_gen_data[data[0]].data, wild_gen_data[data[0]].gen_routine); /* break; Restore this when we do something for case 4 */ } case 4: { /* Don't do anything */ break; } default: { msgf("Illegal wilderness type %d ", g_type); } } } /* * Blend a block based on the adjacent blocks * This makes the wilderness look much better. */ static void blend_block(int x, int y, blk_ptr block_ptr, u16b type) { int i, j, dx, dy; u16b w_type; /* Get current location */ w_type = wild[y][x].done.wild; /* Farms do not blend */ if (wild_gen_data[w_type].gen_routine == 4) return; /* Blend based on height map */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { for (i = 0; i < WILD_BLOCK_SIZE; i++) { /* Chance to blend is 1 in 2 */ if (quick_rand()) continue; /* Work out adjacent block */ if (i < WILD_BLOCK_SIZE / 4) { dx = -1; } else if (i > (WILD_BLOCK_SIZE * 3) / 4) { dx = +1; } else { dx = 0; } if (j < WILD_BLOCK_SIZE / 4) { dy = -1; } else if (j > (WILD_BLOCK_SIZE * 3) / 4) { dy = +1; } else { dy = 0; } /* Check to see if adjacent square is not in bounds */ if (((y + dy) < 0) || ((y + dy) >= max_wild) || ((x + dx) < 0) || ((x + dx) >= max_wild)) continue; /* Don't blend with yourself */ if ((dx == 0) && (dy == 0)) continue; w_type = wild[y + dy][x + dx].done.wild; /* If adjacent type is the same as this one - don't blend */ if (w_type == type) continue; /* The sea doesn't blend. (Use rivers) */ if (w_type >= WILD_SEA) { if (type >= WILD_SEA) { blend_sea(&block_ptr[j][i], (byte)(w_type - WILD_SEA)); } else { /* Do not try to blend sea with land */ /* We need to fix the blocky look of oceans though */ continue; } } /* Blend with generation type specified by gen_routine */ blend_helper(&block_ptr[j][i], wild_gen_data[w_type].data, wild_gen_data[w_type].gen_routine); } } } /* * Make the specified terrain type at a wilderness block */ static void gen_block_helper(blk_ptr block_ptr, byte *data, int gen_type, bool road) { /* Based on type - choose wilderness block generation function */ switch (gen_type) { case 1: { /* Fractal plasma with weighted terrain probabilites */ make_wild_01(block_ptr, data, road); break; } case 2: { /* Uniform field + rare "out-crops" */ make_wild_02(block_ptr, data, road); break; } case 3: { /* Use another type + overlay a "circle" of terrain. */ make_wild_03(block_ptr, data, road); break; } case 4: { /* Draw a farm. */ make_wild_04(block_ptr, data, road); break; } default: { quit("Illegal wilderness block type."); } } } /* * Fill the block with perm. walls. This is only used by the vanilla town option. */ static void fill_perm_wall(blk_ptr block_ptr) { int i, j; /* Overlay the block with permament walls */ for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { block_ptr[j][i].feat = FEAT_PERM_OUTER; } } } /* Add monsters to the wilderness block */ static void add_monsters_block(int x, int y) { int i, j, xx, yy; long prob; /* Day time */ if ((turn % (10L * TOWN_DAWN)) < ((10L * TOWN_DAWN) / 2)) { /* Monsters are rarer in the day */ prob = 32786; } else { /* Monsters are more common at night */ prob = 20000; } /* * Probability of a monster being on a certain sqaure. * Perhaps this should include the effects of stealth. */ prob /= (wild[y][x].done.mon_prob + 1); xx = x * WILD_BLOCK_SIZE; yy = y * WILD_BLOCK_SIZE; for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* See if monster should go on square */ if (!randint0(prob)) { if (one_in_(2)) { /* Monsters are awake */ (void)place_monster(xx + i, yy + j, FALSE, TRUE, 0); } else { /* Monsters are asleep */ (void)place_monster(xx + i, yy + j, TRUE, TRUE, 0); } } } } } void light_dark_square(int x, int y, bool daytime) { cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); if (daytime) { /* Assume lit */ c_ptr->info |= (CAVE_GLOW); /* Memorize lit grids */ remember_grid(c_ptr, pc_ptr); /* If is daytime - have seen this square */ wild[y / 16][x / 16].done.info |= WILD_INFO_SEEN; } else { /* Darken "boring" features */ if (!(((c_ptr->feat >= FEAT_OPEN) && (c_ptr->feat <= FEAT_MORE)) || ((c_ptr->feat >= FEAT_CLOSED) && (c_ptr->feat <= FEAT_PERM_SOLID)))) { /* Hack - Forget the grid */ c_ptr->info &= ~(CAVE_GLOW); forget_grid(pc_ptr); } else { /* Assume lit */ c_ptr->info |= (CAVE_GLOW); /* Memorize lit grids */ remember_grid(c_ptr, pc_ptr); } } /* Hack -- Light spot */ lite_spot(x, y); } /* Lighten / Darken new block depending on Day/ Night */ static void light_dark_block(int x, int y) { int i, j; bool daytime; /* Day time */ if ((turn % (10L * TOWN_DAWN)) < ((10L * TOWN_DAWN) / 2)) daytime = TRUE; else daytime = FALSE; /* If is daytime - have seen this square */ if (daytime) wild[y][x].done.info |= WILD_INFO_SEEN; /* Light up or darken the area */ for (j = 0; j < WILD_BLOCK_SIZE; j++) { for (i = 0; i < WILD_BLOCK_SIZE; i++) { light_dark_square(x * WILD_BLOCK_SIZE + i, y * WILD_BLOCK_SIZE + j, daytime); } } } /* * Make a new block based on the terrain type */ static void gen_block(int x, int y) { u16b w_place, w_type; blk_ptr block_ptr = wild_grid[y][x]; bool road = FALSE; /* Hack -- Use the "simple" RNG */ Rand_quick = TRUE; /* Hack -- Induce consistant wilderness blocks */ Rand_value = wild_seed + x + y * max_wild; /* Generate a terrain block */ /* Get wilderness type */ w_type = wild[y][x].done.wild; /* Is there a road here? */ if (wild[y][x].done.info & (WILD_INFO_TRACK | WILD_INFO_ROAD)) { road = TRUE; } /* Create sea terrains if type >= WILD_SEA */ if (w_type >= WILD_SEA) { make_wild_sea(block_ptr, (byte)(w_type - WILD_SEA)); } /* Hack -Check for the vanilla town wall option. */ else if (w_type == 0) { /* Fill the block with permanent walls */ fill_perm_wall(block_ptr); } else { /* Make terrain based on wilderness generation type */ gen_block_helper(block_ptr, wild_gen_data[w_type].data, wild_gen_data[w_type].gen_routine, road); /* Blend with adjacent terrains */ blend_block(x, y, block_ptr, w_type); /* Add water boundary effects. (Rivers / Ocean) */ if (wild_info_bounds(x, y, WILD_INFO_WATER)) { /* Hack, above function sets bounds */ /* Generate plasma factal */ frac_block(); /* Overlay water */ wild_add_gradient(block_ptr, FEAT_SHAL_WATER, FEAT_DEEP_WATER); } /* Add lava boundary effects. */ else if (wild_info_bounds(x, y, WILD_INFO_LAVA)) { /* Hack, above function sets bounds */ /* Generate plasma factal */ frac_block(); /* Overlay lava */ wild_add_gradient(block_ptr, FEAT_SHAL_LAVA, FEAT_DEEP_LAVA); } /* Add acid boundary effects. */ else if (wild_info_bounds(x, y, WILD_INFO_ACID)) { /* Hack, above function sets bounds */ /* Generate plasma factal */ frac_block(); /* Overlay acid */ wild_add_gradient(block_ptr, FEAT_SHAL_ACID, FEAT_DEEP_ACID); } /* Add roads */ make_wild_road(block_ptr, x, y); } /* Hack -- Use the "complex" RNG */ Rand_quick = FALSE; /* Overlay place */ w_place = wild[y][x].done.place; /* Is there a place? */ if (w_place) { /* overlay place on wilderness */ overlay_place(x, y, w_place, block_ptr); /* Paranoia */ if (!place[w_place].region) quit("Unallocated place region"); } /* Day / Night - lighten or darken the new block */ light_dark_block(x, y); /* Add monsters */ add_monsters_block(x, y); } /* * Erase the player grid information in a block */ static void erase_grids(pblk_ptr block_ptr) { int i, j; for (i = 0; i < WILD_BLOCK_SIZE; i++) { for (j = 0; j < WILD_BLOCK_SIZE; j++) { /* No memorised feature */ block_ptr[i][j].feat = FEAT_NONE; /* All flags off */ block_ptr[i][j].player = 0x00; } } } /* * The following four functions shift the visible * section of the wilderness by 16 units. This is * done by scrolling the grid of pointers. */ static void shift_down(void) { u16b i, j; pblk_ptr block_ptr; for (i = 0; i < WILD_VIEW; i++) { /* The block on the edge */ block_ptr = p_ptr->pwild[0][i]; /* Delete the block */ erase_grids(block_ptr); /* Scroll pointers */ for (j = 1; j < WILD_VIEW; j++) { p_ptr->pwild[j - 1][i] = p_ptr->pwild[j][i]; } /* Connect new grid to wilderness */ p_ptr->pwild[WILD_VIEW - 1][i] = block_ptr; } } static void shift_up(void) { u16b i, j; pblk_ptr block_ptr; for (i = 0; i < WILD_VIEW; i++) { /* The block on the edge */ block_ptr = p_ptr->pwild[WILD_VIEW - 1][i]; /* Delete the block */ erase_grids(block_ptr); /* Scroll pointers */ for (j = WILD_VIEW - 1; j > 0; j--) { p_ptr->pwild[j][i] = p_ptr->pwild[j - 1][i]; } /* Connect new grid to wilderness */ p_ptr->pwild[0][i] = block_ptr; } } static void shift_right(void) { u16b i, j; pblk_ptr block_ptr; for (j = 0; j < WILD_VIEW; j++) { /* The block on the edge */ block_ptr = p_ptr->pwild[j][0]; /* Delete the block */ erase_grids(block_ptr); /* Scroll pointers */ for (i = 1; i < WILD_VIEW; i++) { p_ptr->pwild[j][i - 1] = p_ptr->pwild[j][i]; } /* Connect new grid to wilderness */ p_ptr->pwild[j][WILD_VIEW - 1] = block_ptr; } } static void shift_left(void) { u16b i, j; pblk_ptr block_ptr; for (j = 0; j < WILD_VIEW; j++) { /* The block on the edge */ block_ptr = p_ptr->pwild[j][WILD_VIEW - 1]; /* Delete the block */ erase_grids(block_ptr); /* Scroll pointers */ for (i = WILD_VIEW - 1; i > 0; i--) { p_ptr->pwild[j][i] = p_ptr->pwild[j][i - 1]; } /* Connect new grid to wilderness */ p_ptr->pwild[j][0] = block_ptr; } } /* Delete a wilderness block */ static void del_block(int x, int y) { blk_ptr block_ptr; int xx, yy; int m_idx; wild_type *w_ptr = &wild[y][x]; place_type *pl_ptr = &place[w_ptr->done.place]; if (!wild_refcount[y][x]) quit("Dead wilderness cache!"); /* Decrement refcount */ wild_refcount[y][x]--; /* Don't do anything if someone else is here */ if (wild_refcount[y][x]) return; /* Is there a place? */ if (w_ptr->done.place) { /* Decrease refcount region */ pl_ptr->region = unref_region(pl_ptr->region); } /* Time to delete it - get block pointer */ block_ptr = wild_grid[y][x]; for (xx = 0; xx < WILD_BLOCK_SIZE; xx++) { for (yy = 0; yy < WILD_BLOCK_SIZE; yy++) { /* Clear old terrain data */ block_ptr[yy][xx].info = 0; block_ptr[yy][xx].feat = 0; /* Delete monster on the square */ m_idx = block_ptr[yy][xx].m_idx; /* Only delete if one exists */ if (m_idx) { delete_monster_idx(m_idx); block_ptr[yy][xx].m_idx = 0; } /* Delete objects on the square */ delete_object_list(&block_ptr[yy][xx].o_idx); /* Delete fields on the square */ delete_field_location(&block_ptr[yy][xx]); } } /* Clear old reference */ wild_grid[y][x] = NULL; /* Attach to head of the list */ wild_cache[--wc_cnt] = block_ptr; } /* * Allocate a new block */ static void allocate_block(int x, int y) { byte place_num = wild[y][x].done.place; /* Increment refcount */ wild_refcount[y][x]++; /* Need to make the block if it doesn't exist */ if (!wild_grid[y][x]) { /* Paranoia */ if (wc_cnt >= WILD_CACHE) quit("Out of wilderness cache"); /* Get new block */ wild_grid[y][x] = wild_cache[wc_cnt++]; /* Are we in the process of loading the game? */ if (character_loaded) { /* Generate the block */ gen_block(x, y); if (place_num) { /* Increase refcount for region */ incref_region(place[place_num].region); } } /* We need to make sure the refcounted regions work */ else if (place_num) { place_type *pl_ptr = &place[place_num]; /* Do we need to make the map? */ if (!pl_ptr->region) { /* Create the place */ place_gen(pl_ptr); } /* Increase refcount for region */ incref_region(pl_ptr->region); } } } void shift_in_bounds(int *x, int *y) { /* Vanilla town is special */ if (vanilla_town) { *x = 0; *y = 0; return; } /* Recenter map */ *x -= WILD_VIEW / 2; *y -= WILD_VIEW / 2; /* Move if out of bounds */ if (*x < 0) *x = 0; if (*y < 0) *y = 0; /* Hack XXX This isn't set when we are called during loading */ if (max_wild) { if (*x + WILD_VIEW >= max_wild) *x = max_wild - WILD_VIEW - 1; if (*y + WILD_VIEW >= max_wild) *y = max_wild - WILD_VIEW - 1; } } /* * Centre grid of wilderness blocks around player. * This must be called after the player moves in the wilderness. * If the player is just walking around, all that needs to be done is * to scroll the grid of pointers - not recalculate them all. * However, when the player teleports, all have to ba allocated. */ void move_wild(void) { int x, y; int ox = p_ptr->old_wild_x, oy = p_ptr->old_wild_y; int i, j; quest_type *q_ptr; place_type *pl_ptr; wild_done_type *w_ptr; /* Get upper left hand block in grid. */ /* Divide by WILD_BLOCK_SIZE to get block from (x,y) coord */ x = ((u16b)p_ptr->wilderness_x / WILD_BLOCK_SIZE); y = ((u16b)p_ptr->wilderness_y / WILD_BLOCK_SIZE); w_ptr = &wild[y][x].done; /* The player sees the wilderness block he is on. */ w_ptr->info |= WILD_INFO_SEEN; /* Hack - set place */ p_ptr->place_num = w_ptr->place; /* Move boundary */ shift_in_bounds(&x, &y); /* If we haven't moved block - exit */ if ((ox == x) && (oy == y)) return; /* We have moved, notice the new town/dungeon */ activate_quests(0); pl_ptr = &place[p_ptr->place_num]; /* Check for wilderness quests */ if (pl_ptr->quest_num) { q_ptr = &quest[pl_ptr->quest_num]; /* Some quests are completed by walking on them */ if (q_ptr->x_type == QX_WILD_ENTER) { /* Done? */ trigger_quest_complete(QX_WILD_ENTER, (vptr)q_ptr); } } /* Shift the player information */ while (ox < x) { ox++; shift_right(); } while (ox > x) { ox--; shift_left(); } while (oy < y) { oy++; shift_down(); } while (oy > y) { oy--; shift_up(); } /* Reset bounds */ p_ptr->min_wid = x * WILD_BLOCK_SIZE; p_ptr->min_hgt = y * WILD_BLOCK_SIZE; p_ptr->max_wid = p_ptr->min_wid + WILD_VIEW * WILD_BLOCK_SIZE; p_ptr->max_hgt = p_ptr->min_hgt + WILD_VIEW * WILD_BLOCK_SIZE; /* Allocate new blocks */ for (i = 0; i < WILD_VIEW; i++) { for (j = 0; j < WILD_VIEW; j++) { allocate_block(x + i, y + j); } } /* Deallocate old blocks */ for (i = 0; i < WILD_VIEW; i++) { for (j = 0; j < WILD_VIEW; j++) { del_block(p_ptr->old_wild_x + i, p_ptr->old_wild_y + j); } } /* Redraw depth */ p_ptr->redraw |= (PR_DEPTH); /* Save the new location */ p_ptr->old_wild_x = x; p_ptr->old_wild_y = y; } /* * Access the cave region data. */ static cave_type *access_cave(int x, int y) { return (cave_p(x, y)); } /* * Access player information in dungeon */ static pcave_type *access_pcave(int x, int y) { return (&p_ptr->pcave[y][x]); } /* * Access wilderness */ static cave_type *access_wild(int x, int y) { /* * Divide by 16 to get block. * Logical AND with 15 to get location within block. */ return (&wild_grid[y / WILD_BLOCK_SIZE][x / WILD_BLOCK_SIZE] [y & 15][x & 15]); } /* * Access player information in wilderness */ static pcave_type *access_pwild(int x, int y) { /* * Divide by 16 to get block. * Logical AND with 15 to get location within block. */ return (&p_ptr->pwild[(y - p_ptr->min_hgt) / WILD_BLOCK_SIZE] [(x - p_ptr->min_wid) / WILD_BLOCK_SIZE][y & 15][x & 15]); } /* * Bounds checking * * Hack - in_bounds() and in_bounds2() are the same * in the wilderness. */ static bool in_bounds_wild(int x, int y) { /* Make sure we are inside the wilderness */ if ((y < 0) || (x < 0) || (y >= max_wild * WILD_BLOCK_SIZE) || (x >= max_wild * WILD_BLOCK_SIZE)) { return (FALSE); } /* Return TRUE if block is in use */ return (wild_refcount[y / WILD_BLOCK_SIZE][x / WILD_BLOCK_SIZE] != 0); } static bool in_bounds_cave(int x, int y) { return ((y > p_ptr->min_hgt) && (x > p_ptr->min_wid) && (y < p_ptr->max_hgt - 1) && (x < p_ptr->max_wid - 1)); } static bool in_bounds2_cave(int x, int y) { return ((y >= p_ptr->min_hgt) && (x >= p_ptr->min_wid) && (y < p_ptr->max_hgt) && (x < p_ptr->max_wid)); } /* * In bounds for the player information? */ static bool in_bounds_wild_player(int x, int y) { /* Use the same player bounds information as in_bounds_cave() */ return ((y >= p_ptr->min_hgt) && (x >= p_ptr->min_wid) && (y < p_ptr->max_hgt) && (x < p_ptr->max_wid)); } /* Allocate all grids around player */ static void init_wild_cache(void) { int x = p_ptr->old_wild_x, y = p_ptr->old_wild_y; int i, j; pblk_ptr block_ptr; /* Allocate blocks around player */ for (i = 0; i < WILD_VIEW; i++) { for (j = 0; j < WILD_VIEW; j++) { /* Hack - erase the player knowledge */ block_ptr = p_ptr->pwild[j][i]; erase_grids(block_ptr); allocate_block(x + i, y + j); } } } /* Deallocate all grids around player */ static void del_wild_cache(void) { int x = p_ptr->old_wild_x, y = p_ptr->old_wild_y; int i, j; if (!wc_cnt) quit("Deleting empty wilderness cache!"); /* The player no longer is in the wilderness */ character_dungeon = FALSE; /* Deallocate blocks around player */ for (i = 0; i < WILD_VIEW; i++) { for (j = 0; j < WILD_VIEW; j++) { del_block(x + i, y + j); } } } /* * Fix problems due to dungeons not starting at level 1. * * direction is -1 for going down, and +1 for up. */ void move_dun_level(int direction) { place_type *pl_ptr = &place[p_ptr->place_num]; dun_type *d_ptr = pl_ptr->dungeon; /* Change depth */ p_ptr->depth += direction; /* Leaving */ p_ptr->state.leaving = TRUE; /* Out of bounds? */ if (p_ptr->depth < d_ptr->min_level) { /* We have just decended - and have to decend more? */ if (direction == 1) { p_ptr->depth = d_ptr->min_level; } else { /* Go to surface. */ p_ptr->depth = 0; } } /* Make sure the deepest level is set correctly */ d_ptr->recall_depth = MAX(d_ptr->recall_depth, p_ptr->depth); } /* * This function _must_ be called whenever the dungeon level changes. * It makes sure the bounds and access functions point to the correct * functions. If this is not done - bad things happen. */ void change_level(int level) { place_type *pl_ptr = &place[p_ptr->place_num]; bool switched = FALSE; /* Hack - reset trap detection flag */ p_ptr->state.detected = FALSE; /* Clear the monster lights */ clear_mon_lite(); /* Toggle list of active quests */ activate_quests(level); if (level == 0) { if (pl_ptr->dungeon && pl_ptr->dungeon->region) { /* Delete dungeon */ pl_ptr->dungeon->region = unref_region(pl_ptr->dungeon->region); } /* In the wilderness */ p_ptr->px = (s16b)p_ptr->wilderness_x; p_ptr->py = (s16b)p_ptr->wilderness_y; /* Notice player location */ Term_move_player(); /* Used to be in the dungeon? */ if (area_aux != access_wild) switched = TRUE; /* Access the wilderness */ area_aux = access_wild; parea_aux = access_pwild; /* Bounds checking rountine */ in_bounds = in_bounds_wild; in_bounds2 = in_bounds_wild; in_boundsp = in_bounds_wild_player; /* Initialise the boundary */ p_ptr->min_wid = p_ptr->old_wild_x * WILD_BLOCK_SIZE; p_ptr->min_hgt = p_ptr->old_wild_y * WILD_BLOCK_SIZE; p_ptr->max_wid = p_ptr->min_wid + WILD_VIEW * WILD_BLOCK_SIZE; p_ptr->max_hgt = p_ptr->min_hgt + WILD_VIEW * WILD_BLOCK_SIZE; /* Update panels (later) */ p_ptr->update |= (PU_MAP); /* * Restore the outside town if it exists * This is mainly done to reinit the fields */ if (switched) { /* Create wilderness */ init_wild_cache(); } } else { /* In the dungeon */ if (pl_ptr->dungeon && pl_ptr->dungeon->region) { /* Delete old dungeon */ pl_ptr->dungeon->region = unref_region(pl_ptr->dungeon->region); /* New dungeon is created in generate.c */ } /* Used to be in the wilderness? */ if (area_aux == access_wild) { /* Hack XXX XXX Delete the wilderness cache */ del_wild_cache(); } /* * Default bounds - allocated in generate.c * * Should these be set here at all??? */ p_ptr->min_hgt = 0; p_ptr->max_hgt = MAX_HGT; p_ptr->min_wid = 0; p_ptr->max_wid = MAX_WID; /* Access the cave */ area_aux = access_cave; parea_aux = access_pcave; /* Bounds checking */ in_bounds = in_bounds_cave; in_bounds2 = in_bounds2_cave; in_boundsp = in_bounds2_cave; } /* Tell the rest of the world that the map is no longer valid */ Term_erase_map(); } /* * Get the base level for objects and monsters * around the player. */ int base_level(void) { wild_done_type *w_ptr; /* Are we in the dungeon? */ if (p_ptr->depth) return (p_ptr->depth); /* Point to wilderness block info */ w_ptr = &wild[p_ptr->py / 16][p_ptr->px / 16].done; /* The level of the wilderness */ return(w_ptr->mon_gen); } /* * What is the current dungeon? */ dun_type *dungeon(void) { place_type *pl_ptr = &place[p_ptr->place_num]; /* Return the dungeon */ return (pl_ptr->dungeon); } /* * Delete all active things */ void wipe_all_list(void) { int i; /* Hack - cull the players inventory */ if (p_ptr->inventory) delete_object_list(&p_ptr->inventory); /* Clear the store cache */ for (i = 0; i < store_cache_num; i++) { if (store_cache[i]->stock) { delete_object_list(&store_cache[i]->stock); } } store_cache_num = 0; if (p_ptr->depth) { /* In the dungeon */ wipe_rg_list(); /* No more dungeon */ cur_region = 0; } else { /* In the wilderness - delete cache if it exists */ if (wc_cnt) del_wild_cache(); } /* reset function pointers */ area_aux = NULL; parea_aux = NULL; in_bounds = NULL; in_bounds2 = NULL; in_boundsp = NULL; } /* * Get the maximum dungeon level ever reached. */ int max_dun_level_reached(void) { int i, max = 0; place_type *pl_ptr; dun_type *d_ptr; /* Scan all places */ for (i = 0; i < place_count; i++) { pl_ptr = &place[i]; if (pl_ptr->dungeon) { d_ptr = pl_ptr->dungeon; /* Best depth? */ if (d_ptr->recall_depth > max) { max = d_ptr->recall_depth; } } } /* Done */ return (max); } zangband/src/wizard1.c0000755000000000000000000016513010250356275013672 0ustar rootroot/* File: wizard1.c */ /* Purpose: Spoiler generation -BEN- */ #include "angband.h" #include "script.h" #ifdef ALLOW_SPOILERS /* Uncomment to show estimated "correct" artifact costs in spoilers */ /* #define ESTIMATED_COST */ /* * The spoiler file being created */ static FILE *fff = NULL; /* * Extract a textual representation of an attribute */ static cptr attr_to_text(byte a) { switch (a) { case TERM_DARK: return ("xxx"); case TERM_WHITE: return ("White"); case TERM_SLATE: return ("Slate"); case TERM_ORANGE: return ("Orange"); case TERM_RED: return ("Red"); case TERM_GREEN: return ("Green"); case TERM_BLUE: return ("Blue"); case TERM_UMBER: return ("Umber"); case TERM_L_DARK: return ("L.Dark"); case TERM_L_WHITE: return ("L.Slate"); case TERM_VIOLET: return ("Violet"); case TERM_YELLOW: return ("Yellow"); case TERM_L_RED: return ("L.Red"); case TERM_L_GREEN: return ("L.Green"); case TERM_L_BLUE: return ("L.Blue"); case TERM_L_UMBER: return ("L.Umber"); } /* Oops */ return ("Icky"); } /* * A tval grouper */ typedef struct { byte tval; cptr name; } grouper; /* * Item Spoilers by: benh@phial.com (Ben Harrison) */ /* * The basic items categorized by type */ static const grouper group_item[] = { {TV_SHOT, "Ammo"}, {TV_ARROW, NULL}, {TV_BOLT, NULL}, {TV_BOW, "Bows"}, {TV_SWORD, "Weapons"}, {TV_POLEARM, NULL}, {TV_HAFTED, NULL}, {TV_DIGGING, NULL}, {TV_SOFT_ARMOR, "Armour (Body)"}, {TV_HARD_ARMOR, NULL}, {TV_DRAG_ARMOR, NULL}, {TV_CLOAK, "Armour (Misc)"}, {TV_SHIELD, NULL}, {TV_HELM, NULL}, {TV_CROWN, NULL}, {TV_GLOVES, NULL}, {TV_BOOTS, NULL}, {TV_AMULET, "Amulets"}, {TV_RING, "Rings"}, {TV_SCROLL, "Scrolls"}, {TV_POTION, "Potions"}, {TV_FOOD, "Food"}, {TV_ROD, "Rods"}, {TV_WAND, "Wands"}, {TV_STAFF, "Staffs"}, {TV_LIFE_BOOK, "Books (Life)"}, {TV_SORCERY_BOOK, "Books (Sorcery)"}, {TV_NATURE_BOOK, "Books (Nature)"}, {TV_CHAOS_BOOK, "Books (Chaos)"}, {TV_DEATH_BOOK, "Books (Death)"}, {TV_TRUMP_BOOK, "Books (Trump)"}, {TV_ARCANE_BOOK, "Books (Arcane)"}, {TV_CHEST, "Chests"}, {TV_FIGURINE, "Magical Figurines"}, {TV_STATUE, "Statues"}, {TV_SPIKE, "Various"}, {TV_LITE, NULL}, {TV_FLASK, NULL}, {TV_JUNK, NULL}, {TV_BOTTLE, NULL}, {TV_SKELETON, NULL}, {0, ""} }; /* * Describe the kind */ static void kind_info(char *buf, char *dam, char *wgt, int *lev, s32b *val, int k) { object_type *q_ptr; /* Prepare a fake item */ q_ptr = object_prep(k); /* It is known */ q_ptr->info |= (OB_STOREB); /* Cancel bonuses */ q_ptr->pval = 0; q_ptr->to_a = 0; q_ptr->to_h = 0; q_ptr->to_d = 0; /* Level */ (*lev) = get_object_level(q_ptr); /* Value */ (*val) = object_value(q_ptr); /* Hack */ if (!buf || !dam || !wgt) return; /* Description (too brief) */ object_desc_store(buf, q_ptr, FALSE, 0, 256); /* Misc info */ dam[0] = 0; /* Damage */ switch (q_ptr->tval) { case TV_BOW: { /* Bows */ break; } case TV_SHOT: case TV_BOLT: case TV_ARROW: { /* Ammo */ strnfmt(dam, 80, "%dd%d", (int)q_ptr->dd, (int)q_ptr->ds); break; } case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_DIGGING: { /* Weapons */ strnfmt(dam, 80, "%dd%d", (int)q_ptr->dd, (int)q_ptr->ds); break; } case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_CROWN: case TV_HELM: case TV_SHIELD: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Armour */ strnfmt(dam, 80, "%d", q_ptr->ac); break; } } /* Weight */ strnfmt(wgt, 80, "%3d.%d", q_ptr->weight / 10, q_ptr->weight % 10); } /* * Create a spoiler file for items */ static void spoil_obj_desc(cptr fname) { int i, k, s, t, n = 0; u16b who[200]; char buf[1024]; char wgt[80]; char dam[80]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msgf("Cannot create spoiler file."); return; } /* Header */ froff(fff, "Spoiler File -- Basic Items (%s %s)\n\n\n", VERSION_NAME, VERSION_STRING); /* More Header */ froff(fff, "%-45s %8s%7s%5s%9s\n", "Description", "Dam/AC", "Wgt", "Lev", "Cost"); froff(fff, "%-45s %8s%7s%5s%9s\n", "----------------------------------------", "------", "---", "---", "----"); /* List the groups */ for (i = 0; TRUE; i++) { /* Write out the group title */ if (group_item[i].name) { /* Hack -- bubble-sort by cost and then level */ for (s = 0; s < n - 1; s++) { for (t = 0; t < n - 1; t++) { int i1 = t; int i2 = t + 1; int e1; int e2; s32b t1; s32b t2; kind_info(NULL, NULL, NULL, &e1, &t1, who[i1]); kind_info(NULL, NULL, NULL, &e2, &t2, who[i2]); if ((t1 > t2) || ((t1 == t2) && (e1 > e2))) { int tmp = who[i1]; who[i1] = who[i2]; who[i2] = tmp; } } } /* Spoil each item */ for (s = 0; s < n; s++) { int e; s32b v; /* Describe the kind */ kind_info(buf, dam, wgt, &e, &v, who[s]); /* Dump it */ froff(fff, " %-45s%8s%7s%5d%9ld\n", buf, dam, wgt, e, (long)(v)); } /* Start a new set */ n = 0; /* Notice the end */ if (!group_item[i].tval) break; /* Start a new set */ froff(fff, "\n\n%s\n\n", group_item[i].name); } /* Acquire legal item types */ for (k = 1; k < z_info->k_max; k++) { object_kind *k_ptr = &k_info[k]; /* Skip wrong tval's */ if (k_ptr->tval != group_item[i].tval) continue; /* Hack -- Skip instant-artifacts */ if (FLAG(k_ptr, TR_INSTA_ART)) continue; /* Save the index */ who[n++] = k; } } /* Check for errors */ if (ferror(fff)) { msgf("Cannot close spoiler file."); return; } my_fclose(fff); /* Message */ msgf("Successfully created a spoiler file."); } /* * Artifact Spoilers by: randy@PICARD.tamu.edu (Randy Hutson) */ /* * Returns a "+" string if a number is non-negative and an empty * string if negative */ #define POSITIZE(v) (((v) >= 0) ? "+" : "") /* * These are used to format the artifact spoiler file. INDENT1 is used * to indent all but the first line of an artifact spoiler. INDENT2 is * used when a line "wraps". (Bladeturner's resistances cause this.) */ #define INDENT1 " " #define INDENT2 " " /* * MAX_LINE_LEN specifies when a line should wrap. */ #define MAX_LINE_LEN 75 /* * Given an array, determine how many elements are in the array */ #define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0])) /* * The artifacts categorized by type */ static const grouper group_artifact[] = { {TV_SWORD, "Edged Weapons"}, {TV_POLEARM, "Polearms"}, {TV_HAFTED, "Hafted Weapons"}, {TV_BOW, "Bows"}, {TV_SOFT_ARMOR, "Body Armor"}, {TV_HARD_ARMOR, NULL}, {TV_DRAG_ARMOR, NULL}, {TV_CLOAK, "Cloaks"}, {TV_SHIELD, "Shields"}, {TV_HELM, "Helms/Crowns"}, {TV_CROWN, NULL}, {TV_GLOVES, "Gloves"}, {TV_BOOTS, "Boots"}, {TV_LITE, "Light Sources"}, {TV_AMULET, "Amulets"}, {TV_RING, "Rings"}, {0, NULL} }; /* * Pair together a constant flag with a textual description. * * Used by both "init.c" and "wiz-spo.c". * * Note that it sometimes more efficient to actually make an array * of textual names, where entry 'N' is assumed to be paired with * the flag whose value is "1L << N", but that requires hard-coding. */ typedef struct flag_desc flag_desc; struct flag_desc { int set; const u32b flag; const char *const desc; }; /* * These are used for "+3 to STR, DEX", etc. These are separate from * the other pval affected traits to simplify the case where an object * affects all stats. In this case, "All stats" is used instead of * listing each stat individually. */ static const flag_desc stat_flags_desc[] = { {TR_STR, "STR"}, {TR_INT, "INT"}, {TR_WIS, "WIS"}, {TR_DEX, "DEX"}, {TR_CON, "CON"}, {TR_CHR, "CHR"} }; /* * Besides stats, these are the other player traits * which may be affected by an object's pval */ static const flag_desc pval_flags1_desc[] = { {TR_SP, "Mana"}, {TR_STEALTH, "Stealth"}, {TR_SEARCH, "Searching"}, {TR_INFRA, "Infravision"}, {TR_TUNNEL, "Tunneling"}, {TR_BLOWS, "Attacks"}, {TR_SPEED, "Speed"}, {TR_SP, "SP"} }; /* * Slaying preferences for weapons */ static const flag_desc slay_flags_desc[] = { {TR_SLAY_ANIMAL, "Animal"}, {TR_SLAY_EVIL, "Evil"}, {TR_SLAY_UNDEAD, "Undead"}, {TR_SLAY_DEMON, "Demon"}, {TR_SLAY_ORC, "Orc"}, {TR_SLAY_TROLL, "Troll"}, {TR_SLAY_GIANT, "Giant"}, {TR_SLAY_DRAGON, "Dragon"}, {TR_KILL_DRAGON, "Xdragon"} }; /* * Elemental brands for weapons * * Clearly, TR0_IMPACT is a bit out of place here. To simplify * coding, it has been included here along with the elemental * brands. It does seem to fit in with the brands and slaying * more than the miscellaneous section. */ static const flag_desc brand_flags_desc[] = { {TR_BRAND_ACID, "Acid Brand"}, {TR_BRAND_ELEC, "Lightning Brand"}, {TR_BRAND_FIRE, "Flame Tongue"}, {TR_BRAND_COLD, "Frost Brand"}, {TR_BRAND_POIS, "Poisoned"}, {TR_CHAOTIC, "Mark of Chaos"}, {TR_VAMPIRIC, "Vampiric"}, {TR_IMPACT, "Earthquake impact on hit"}, {TR_VORPAL, "Very sharp"}, }; /* * The 15 resistables */ static const flag_desc resist_flags_desc[] = { {TR_RES_ACID, "Acid"}, {TR_RES_ELEC, "Lightning"}, {TR_RES_FIRE, "Fire"}, {TR_RES_COLD, "Cold"}, {TR_RES_POIS, "Poison"}, {TR_RES_FEAR, "Fear"}, {TR_RES_LITE, "Light"}, {TR_RES_DARK, "Dark"}, {TR_RES_BLIND, "Blindness"}, {TR_RES_CONF, "Confusion"}, {TR_RES_SOUND, "Sound"}, {TR_RES_SHARDS, "Shards"}, {TR_RES_NETHER, "Nether"}, {TR_RES_NEXUS, "Nexus"}, {TR_RES_CHAOS, "Chaos"}, {TR_RES_DISEN, "Disenchantment"}, }; /* * Elemental immunities (along with poison) */ static const flag_desc immune_flags_desc[] = { {TR_IM_POIS, "Poison"}, {TR_IM_ACID, "Acid"}, {TR_IM_ELEC, "Lightning"}, {TR_IM_FIRE, "Fire"}, {TR_IM_COLD, "Cold"}, {TR_IM_LITE, "Light"}, {TR_IM_DARK, "Darkness"} }; /* * Sustain stats - these are given their "own" line in the * spoiler file, mainly for simplicity */ static const flag_desc sustain_flags_desc[] = { {TR_SUST_STR, "STR"}, {TR_SUST_INT, "INT"}, {TR_SUST_WIS, "WIS"}, {TR_SUST_DEX, "DEX"}, {TR_SUST_CON, "CON"}, {TR_SUST_CHR, "CHR"}, }; /* * Miscellaneous magic * * Note that cursed artifacts and objects with permanent light * are handled "directly" -- see analyze_misc_magic() */ static const flag_desc misc_flags2_desc[] = { {TR_THROW, "Throwing"}, {TR_REFLECT, "Reflection"}, {TR_FREE_ACT, "Free Action"}, {TR_HOLD_LIFE, "Hold Life"}, {TR_SH_FIRE, "Fiery Aura"}, {TR_SH_ELEC, "Electric Aura"}, {TR_SH_COLD, "Frost Aura"}, {TR_SH_ACID, "Acid Aura"}, {TR_NO_TELE, "Prevent Teleportation"}, {TR_NO_MAGIC, "Anti-Magic"}, {TR_FEATHER, "Levitation"}, {TR_SEE_INVIS, "See Invisible"}, {TR_TELEPATHY, "ESP"}, {TR_SLOW_DIGEST, "Slow Digestion"}, {TR_REGEN, "Regeneration"}, {TR_XTRA_SHOTS, "+1 Extra Shot"}, /* always +1? */ {TR_DRAIN_EXP, "Drains Experience"}, {TR_AGGRAVATE, "Aggravates"}, {TR_HURT_FIRE, "Fire Vulnerability"}, {TR_HURT_COLD, "Cold Vulnerability"}, {TR_HURT_ELEC, "Lightning Vulnerability"}, {TR_HURT_ACID, "Acid Vulnerability"}, {TR_HURT_LITE, "Light Vulnerability"}, {TR_HURT_DARK, "Darkness Vulnerability"}, {TR_AUTO_CURSE, "Spontaneous Curse"}, {TR_CANT_EAT, "Can't Eat"}, {TR_SLOW_HEAL, "Slow Healing"}, {TR_DRAIN_STATS, "Drains Stats"}, {TR_BLESSED, "Blessed Blade"}, {TR_LUCK_10, "+10 Save"}, {TR_MUTATE, "Mutatagen"}, {TR_PATRON, "Chaos Patron"}, {TR_STRANGE_LUCK, "Warp Fate"}, {TR_PASS_WALL, "Pass Walls"}, {TR_GHOUL_TOUCH, "Ghoul Touch"}, {TR_PSI_CRIT, "Magic-Powered Criticals"}, {TR_RETURN, "Returning"}, {TR_EXPLODE, "Explosive"}, }; /* * A special type used just for deailing with pvals */ typedef struct { /* * This will contain a string such as "+2", "-10", etc. */ char pval_desc[12]; /* * A list of various player traits affected by an object's pval such * as stats, speed, stealth, etc. "Extra attacks" is NOT included in * this list since it will probably be desirable to format its * description differently. * * Note that room need only be reserved for the number of stats - 1 * since the description "All stats" is used if an object affects all * all stats. Also, room must be reserved for a sentinel NULL pointer. * * This will be a list such as ["STR", "DEX", "Stealth", NULL] etc. * * This list includes extra attacks, for simplicity. */ cptr pval_affects[N_ELEMENTS(stat_flags_desc) - 1 + N_ELEMENTS(pval_flags1_desc) + 1]; } pval_info_type; /* * An "object analysis structure" * * It will be filled with descriptive strings detailing an object's * various magical powers. The "ignore X" traits are not noted since * all artifacts ignore "normal" destruction. */ typedef struct { /* "The Longsword Dragonsmiter (6d4) (+20, +25)" */ char description[256]; /* Description of what is affected by an object's pval */ pval_info_type pval_info; /* A list of an object's slaying preferences */ cptr slays[N_ELEMENTS(slay_flags_desc) + 1]; /* A list if an object's elemental brands */ cptr brands[N_ELEMENTS(brand_flags_desc) + 1]; /* A list of immunities granted by an object */ cptr immunities[N_ELEMENTS(immune_flags_desc) + 1]; /* A list of resistances granted by an object */ cptr resistances[N_ELEMENTS(resist_flags_desc) + 1]; /* A list of stats sustained by an object */ cptr sustains[N_ELEMENTS(sustain_flags_desc) - 1 + 1]; /* A list of various magical qualities an object may have */ cptr misc_magic[N_ELEMENTS(misc_flags2_desc) + 1 + /* Permanent Light */ 1 + /* type of curse */ 1]; /* sentinel NULL */ /* A string describing an artifact's activation */ cptr activation; /* A string describing miscellaneous powers */ cptr special; /* "Level 20, Rarity 30, 3.0 lbs, 20000 Gold" */ char misc_desc[80]; } obj_desc_list; /* * Write out `n' of the character `c' to the spoiler file */ static void spoiler_out_n_chars(int n, char c) { while (--n >= 0) fputc(c, fff); } /* * Write out `n' blank lines to the spoiler file */ static void spoiler_blanklines(int n) { spoiler_out_n_chars(n, '\n'); } /* * Write a line to the spoiler file and then "underline" it with hypens */ static void spoiler_underline(cptr str) { froff(fff, "%s\n", str); spoiler_out_n_chars(strlen(str), '-'); froff(fff, "\n"); } /* * This function does most of the actual "analysis". Given a set of bit flags * (which will be from one of the flags fields from the object in question), * a "flag description structure", a "description list", and the number of * elements in the "flag description structure", this function sets the * "description list" members to the appropriate descriptions contained in * the "flag description structure". * * The possibly updated description pointer is returned. */ static cptr *spoiler_flag_aux(const u32b *flags, const flag_desc *flag_ptr, cptr *desc_ptr, const int n_elmnts) { int i; for (i = 0; i < n_elmnts; ++i) { if (flags[flag_ptr[i].set] & flag_ptr[i].flag) { *desc_ptr++ = flag_ptr[i].desc; } } return desc_ptr; } /* * Acquire a "basic" description "The Cloak of Death [1,+10]" */ static void analyze_general(const object_type *o_ptr, char *desc_ptr) { /* Get a "useful" description of the object */ object_desc_store(desc_ptr, o_ptr, TRUE, 1, 256); } /* * List "player traits" altered by an artifact's pval. These include stats, * speed, infravision, tunneling, stealth, searching, and extra attacks. */ static void analyze_pval(const object_type *o_ptr, pval_info_type *p_ptr) { const u32b all_stats = (TR0_STR | TR0_INT | TR0_WIS | TR0_DEX | TR0_CON | TR0_CHR); cptr *affects_list; /* If pval == 0, there is nothing to do. */ if (!o_ptr->pval) { /* An "empty" pval description indicates that pval == 0 */ p_ptr->pval_desc[0] = '\0'; return; } affects_list = p_ptr->pval_affects; /* Create the "+N" string */ strnfmt(p_ptr->pval_desc, 12, "%s%d", POSITIZE(o_ptr->pval), o_ptr->pval); /* First, check to see if the pval affects all stats */ if ((o_ptr->flags[0] & all_stats) == all_stats) { *affects_list++ = "All stats"; } /* Are any stats affected? */ else if (o_ptr->flags[0] & all_stats) { affects_list = spoiler_flag_aux(o_ptr->flags, stat_flags_desc, affects_list, N_ELEMENTS(stat_flags_desc)); } /* And now the "rest" */ affects_list = spoiler_flag_aux(o_ptr->flags, pval_flags1_desc, affects_list, N_ELEMENTS(pval_flags1_desc)); /* Terminate the description list */ *affects_list = NULL; } /* Note the slaying specialties of a weapon */ static void analyze_slay(const object_type *o_ptr, cptr *slay_list) { slay_list = spoiler_flag_aux(o_ptr->flags, slay_flags_desc, slay_list, N_ELEMENTS(slay_flags_desc)); /* Terminate the description list */ *slay_list = NULL; } /* Note an object's elemental brands */ static void analyze_brand(const object_type *o_ptr, cptr *brand_list) { brand_list = spoiler_flag_aux(o_ptr->flags, brand_flags_desc, brand_list, N_ELEMENTS(brand_flags_desc)); /* Terminate the description list */ *brand_list = NULL; } /* Note the resistances granted by an object */ static void analyze_resist(const object_type *o_ptr, cptr *resist_list) { resist_list = spoiler_flag_aux(o_ptr->flags, resist_flags_desc, resist_list, N_ELEMENTS(resist_flags_desc)); /* Terminate the description list */ *resist_list = NULL; } /* Note the immunities granted by an object */ static void analyze_immune(const object_type *o_ptr, cptr *immune_list) { immune_list = spoiler_flag_aux(o_ptr->flags, immune_flags_desc, immune_list, N_ELEMENTS(immune_flags_desc)); /* Terminate the description list */ *immune_list = NULL; } /* Note which stats an object sustains */ static void analyze_sustains(const object_type *o_ptr, cptr *sustain_list) { const u32b all_sustains = (TR1_SUST_STR | TR1_SUST_INT | TR1_SUST_WIS | TR1_SUST_DEX | TR1_SUST_CON | TR1_SUST_CHR); /* Simplify things if an item sustains all stats */ if ((o_ptr->flags[1] & all_sustains) == all_sustains) { *sustain_list++ = "All stats"; } /* Should we bother? */ else if ((o_ptr->flags[1] & all_sustains)) { sustain_list = spoiler_flag_aux(o_ptr->flags, sustain_flags_desc, sustain_list, N_ELEMENTS(sustain_flags_desc)); } /* Terminate the description list */ *sustain_list = NULL; } /* * Note miscellaneous powers bestowed by an artifact such as see invisible, * free action, permanent light, etc. */ static void analyze_misc_magic(const object_type *o_ptr, cptr *misc_list) { misc_list = spoiler_flag_aux(o_ptr->flags, misc_flags2_desc, misc_list, N_ELEMENTS(misc_flags2_desc)); /* * Artifact lights -- large radius light. */ if ((o_ptr->tval == TV_LITE) && (FLAG(o_ptr, TR_LITE))) { *misc_list++ = "Permanent Light(3)"; } /* * Glowing artifacts -- small radius light. */ else if (FLAG(o_ptr, TR_LITE)) { *misc_list++ = "Permanent Light(1)"; } /* * Handle cursed objects here to avoid redundancies such as noting * that a permanently cursed object is heavily cursed as well as * being "lightly cursed". */ if (cursed_p(o_ptr)) { if (FLAG(o_ptr, TR_TY_CURSE)) { *misc_list++ = "Ancient Curse"; } if (FLAG(o_ptr, TR_PERMA_CURSE)) { *misc_list++ = "Permanently Cursed"; } else if (FLAG(o_ptr, TR_HEAVY_CURSE)) { *misc_list++ = "Heavily Cursed"; } else { *misc_list++ = "Cursed"; } } /* Terminate the description list */ *misc_list = NULL; } /* * Determine the minimum depth an artifact can appear, its rarity, its weight, * and its value in gold pieces */ static void analyze_misc(const object_type *o_ptr, char *misc_desc) { artifact_type *a_ptr; /* Only look at artifacts */ if (!o_ptr->a_idx) return; a_ptr = &a_info[o_ptr->a_idx]; #ifndef ESTIMATED_COST strnfmt(misc_desc, 80, "Level %u, Rarity %u, %d.%d lbs, %ld Gold", (uint)a_ptr->level, (uint)a_ptr->rarity, a_ptr->weight / 10, a_ptr->weight % 10, a_ptr->cost); #else { int est_cost = flag_cost(o_ptr, o_ptr->pval); if (wield_slot(o_ptr) == EQUIP_WIELD) est_cost += o_ptr->dd * o_ptr->ds * (20 + o_ptr->to_h + o_ptr->to_d) * 5; else est_cost += 200 * (o_ptr->to_h + o_ptr->to_d); est_cost += 100 * (o_ptr->ac + o_ptr->to_a); strnfmt(misc_desc, 80, "Level %u, Rarity %u, %d.%d lbs, %ld Gold, %ld Est", (uint)a_ptr->level, (uint)a_ptr->rarity, a_ptr->weight / 10, a_ptr->weight % 10, a_ptr->cost, est_cost); } #endif } /* * Fill in an object description structure for a given object */ static void object_analyze(const object_type *o_ptr, obj_desc_list *desc_ptr) { analyze_general(o_ptr, desc_ptr->description); analyze_pval(o_ptr, &desc_ptr->pval_info); analyze_brand(o_ptr, desc_ptr->brands); analyze_slay(o_ptr, desc_ptr->slays); analyze_immune(o_ptr, desc_ptr->immunities); analyze_resist(o_ptr, desc_ptr->resistances); analyze_sustains(o_ptr, desc_ptr->sustains); analyze_misc_magic(o_ptr, desc_ptr->misc_magic); analyze_misc(o_ptr, desc_ptr->misc_desc); desc_ptr->activation = item_activation(o_ptr); if (streq("nothing", desc_ptr->activation)) { /* Display nothing, if there is no activation */ desc_ptr->activation = NULL; } desc_ptr->special = ""; apply_object_trigger(TRIGGER_SPOIL, (object_type *) o_ptr, ":s", LUA_RETURN_NAMED(desc_ptr->special, "desc")); if (streq("", desc_ptr->special)) desc_ptr->special = NULL; } static void print_header(void) { spoiler_underline("Artifact Spoilers for " VERSION_NAME " Version " VERSION_STRING); } /* * This is somewhat ugly. * * Given a header ("Resist", e.g.), a list ("Fire", "Cold", Acid", e.g.), * and a separator character (',', e.g.), write the list to the spoiler file * in a "nice" format, such as: * * Resist Fire, Cold, Acid * * That was a simple example, but when the list is long, a line wrap * should occur, and this should induce a new level of indention if * a list is being spread across lines. So for example, Bladeturner's * list of resistances should look something like this * * Resist Acid, Lightning, Fire, Cold, Poison, Light, Dark, Blindness, * Confusion, Sound, Shards, Nether, Nexus, Chaos, Disenchantment * * However, the code distinguishes between a single list of many items vs. * many lists. (The separator is used to make this determination.) A single * list of many items will not cause line wrapping (since there is no * apparent reason to do so). So the lists of Ulmo's miscellaneous traits * might look like this: * * Free Action; Hold Life; See Invisible; Slow Digestion; Regeneration * Blessed Blade * * So comparing the two, "Regeneration" has no trailing separator and * "Blessed Blade" was not indented. (Also, Ulmo's lists have no headers, * but that's not relevant to line wrapping and indention.) */ /* ITEM_SEP separates items within a list */ #define ITEM_SEP ',' /* LIST_SEP separates lists */ #define LIST_SEP ';' static void spoiler_outlist(cptr header, cptr *list, char separator) { int line_len, buf_len; char line[MAX_LINE_LEN + 1], buf[80]; /* Ignore an empty list */ if (*list == NULL) return; /* This function always indents */ line_len = strnfmt(line, MAX_LINE_LEN + 1, INDENT1); /* Create header (if one was given) */ if (header &&(header[0])) { strnfcat(line, MAX_LINE_LEN + 1, &line_len, "%s ", header); } line_len = strlen(line); /* Now begin the tedious task */ while (1) { /* Copy the current item to a buffer */ strcpy(buf, *list); /* Note the buffer's length */ buf_len = strlen(buf); /* * If there is an item following this one, pad with separator and * a space and adjust the buffer length */ if (list[1]) { strnfmt(buf + buf_len, 80 - buf_len, "%c ", separator); buf_len += 2; } /* * If the buffer will fit on the current line, just append the * buffer to the line and adjust the line length accordingly. */ if (line_len + buf_len <= MAX_LINE_LEN) { strnfcat(line, MAX_LINE_LEN + 1, &line_len, buf); } /* Apply line wrapping and indention semantics described above */ else { /* * Don't print a trailing list separator but do print a trailing * item separator. */ if ((line_len > 1) && (line[line_len - 1] == ' ') && (line[line_len - 2] == LIST_SEP)) { /* Ignore space and separator */ line[line_len - 2] = '\0'; /* Write to spoiler file */ froff(fff, "%s\n", line); /* Begin new line at primary indention level */ strnfmt(line, MAX_LINE_LEN + 1, "%s%s", INDENT1, buf); } else { /* Write to spoiler file */ froff(fff, "%s\n", line); /* Begin new line at secondary indention level */ strnfmt(line, MAX_LINE_LEN + 1, "%s%s", INDENT2, buf); } line_len = strlen(line); } /* Advance, with break */ if (!*++list) break; } /* Write what's left to the spoiler file */ froff(fff, "%s\n", line); } /* * Create a spoiler file entry for an artifact */ static void spoiler_print_art(obj_desc_list *art_ptr) { pval_info_type *pval_ptr = &art_ptr->pval_info; char buf[80]; /* Don't indent the first line */ froff(fff, "%s\n", art_ptr->description); /* An "empty" pval description indicates that the pval affects nothing */ if (pval_ptr->pval_desc[0]) { /* Mention the effects of pval */ strnfmt(buf, 80, "%s to", pval_ptr->pval_desc); spoiler_outlist(buf, pval_ptr->pval_affects, ITEM_SEP); } /* Now deal with the description lists */ spoiler_outlist("Slay", art_ptr->slays, ITEM_SEP); spoiler_outlist("", art_ptr->brands, LIST_SEP); spoiler_outlist("Immunity to", art_ptr->immunities, ITEM_SEP); spoiler_outlist("Resist", art_ptr->resistances, ITEM_SEP); spoiler_outlist("Sustain", art_ptr->sustains, ITEM_SEP); spoiler_outlist("", art_ptr->misc_magic, LIST_SEP); /* Write out the possible activation at the primary indention level */ if (art_ptr->activation) { froff(fff, "%sActivates for %s\n", INDENT1, art_ptr->activation); } if (art_ptr->special) { froff(fff, "%s%s\n", INDENT1, art_ptr->special); } /* End with the miscellaneous facts */ froff(fff, "%s%s\n\n", INDENT1, art_ptr->misc_desc); } /* * Hack -- Create a "forged" artifact */ static object_type *make_fake_artifact(int a_idx) { int i; object_type *o_ptr; artifact_type *a_ptr = &a_info[a_idx]; /* Ignore "empty" artifacts */ if (!a_ptr->name) return (NULL); /* Acquire the "kind" index */ i = lookup_kind(a_ptr->tval, a_ptr->sval); /* Oops */ if (!i) return (NULL); /* Create the artifact */ o_ptr = object_prep(i); /* Save the artifact flags */ o_ptr->flags[0] |= a_ptr->flags[0]; o_ptr->flags[1] |= a_ptr->flags[1]; o_ptr->flags[2] |= a_ptr->flags[2]; o_ptr->flags[3] |= a_ptr->flags[3]; /* Set the fields */ o_ptr->pval = a_ptr->pval; o_ptr->ac = a_ptr->ac; o_ptr->dd = a_ptr->dd; o_ptr->ds = a_ptr->ds; o_ptr->to_a = a_ptr->to_a; o_ptr->to_h = a_ptr->to_h; o_ptr->to_d = a_ptr->to_d; o_ptr->weight = a_ptr->weight; /* Mega-Hack -- set activation */ o_ptr->a_idx = a_idx; /* Add any special scripts */ for (i = 0; i < MAX_TRIGGER; i++) { if (a_ptr->trigger[i]) o_ptr->trigger[i] = quark_add(a_text + a_ptr->trigger[i]); } /* Do not make another one */ a_ptr->cur_num = 1; /* Save the inscription */ o_ptr->xtra_name = quark_add(a_name + a_ptr->name); /* Apply special scripts */ /* apply_object_trigger(TRIGGER_MAKE, o_ptr, "i", "lev", a_ptr->level); */ /* Success */ return (o_ptr); } /* * Create a spoiler file for artifacts */ static void spoil_artifact(cptr fname) { int i, j; object_type *q_ptr; obj_desc_list artifact; char buf[1024]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msgf("Cannot create spoiler file."); return; } /* Dump the header */ print_header(); /* List the artifacts by tval */ for (i = 0; group_artifact[i].tval; i++) { /* Write out the group title */ if (group_artifact[i].name) { spoiler_blanklines(2); spoiler_underline(group_artifact[i].name); spoiler_blanklines(1); } /* Now search through all of the artifacts */ for (j = 1; j < z_info->a_max; ++j) { artifact_type *a_ptr = &a_info[j]; /* We only want objects in the current group */ if (a_ptr->tval != group_artifact[i].tval) continue; /* Attempt to "forge" the artifact */ q_ptr = make_fake_artifact(j); if (!q_ptr) continue; /* Analyze the artifact */ object_analyze(q_ptr, &artifact); /* Write out the artifact description to the spoiler file */ spoiler_print_art(&artifact); } } /* Check for errors */ if (ferror(fff)) { msgf("Cannot close spoiler file."); return; } my_fclose(fff); /* Message */ msgf("Successfully created a spoiler file."); } /* * Create a spoiler file for monsters -BEN- */ static void spoil_mon_desc(cptr fname) { int i, n = 0; u16b why = 2; s16b *who; char buf[1024]; char nam[80]; char lev[80]; char rar[80]; char spd[80]; char ac[80]; char hp[80]; char exp[80]; char vis[80]; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msgf("Cannot create spoiler file."); return; } /* Dump the header */ froff(fff, "Monster Spoilers for %s Version %s\n", VERSION_NAME, VERSION_STRING); froff(fff, "-------------------------------------------\n\n"); froff(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n", "Name", "Lev", "Rar", "Spd", "Hp", "Ac", "Visual Info"); froff(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n", "----", "---", "---", "---", "--", "--", "-----------"); /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, s16b); /* Scan the monsters */ for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Use that monster */ if (r_ptr->name) who[n++] = i; } /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array by dungeon depth of monsters */ ang_sort(who, &why, n); /* Scan again */ for (i = 0; i < n; i++) { monster_race *r_ptr = &r_info[who[i]]; cptr name = mon_race_name(r_ptr); /* Get the "name" */ if (FLAG(r_ptr, RF_QUESTOR)) { strnfmt(nam, 80, "[Q] %s", name); } else if (FLAG(r_ptr, RF_UNIQUE)) { strnfmt(nam, 80, "[U] %s", name); } else { strnfmt(nam, 80, "The %s", name); } /* Level */ strnfmt(lev, 80, "%d", (int)r_ptr->level); /* Rarity */ strnfmt(rar, 80, "%d", (int)r_ptr->rarity); /* Speed */ if (r_ptr->speed >= 110) { strnfmt(spd, 80, "+%d", (r_ptr->speed - 110)); } else { strnfmt(spd, 80, "-%d", (110 - r_ptr->speed)); } /* Armor Class */ strnfmt(ac, 80, "%d", r_ptr->ac); /* Hitpoints */ if (FLAG(r_ptr, RF_FORCE_MAXHP) || (r_ptr->hside == 1)) { strnfmt(hp, 80, "%d", (int)r_ptr->hdice * r_ptr->hside); } else { strnfmt(hp, 80, "%dd%d", (int)r_ptr->hdice, (int)r_ptr->hside); } /* Experience */ strnfmt(exp, 80, "%ld", (long)(r_ptr->mexp)); /* Hack -- use visual instead */ strnfmt(vis, 80, "%s '%c'", attr_to_text(r_ptr->d_attr), r_ptr->d_char); /* Dump the info */ froff(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s %s\n", nam, lev, rar, spd, hp, ac, exp, vis); } /* Free the "who" array */ KILL(who); /* End it */ froff(fff, "\n"); /* Check for errors */ if (ferror(fff)) { msgf("Cannot close spoiler file."); return; } my_fclose(fff); /* Worked */ msgf("Successfully created a spoiler file."); } /* * Monster spoilers by: smchorse@ringer.cs.utsa.edu (Shawn McHorse) * * Primarily based on code already in mon-desc.c, mostly by -BEN- */ /* * Pronoun arrays */ static cptr wd_che[3] = { "It", "He", "She" }; static cptr wd_lhe[3] = { "it", "he", "she" }; /* * Buffer text to the given file. (-SHAWN-) * This is basically c_roff() from mon-desc.c with a few changes. */ static void spoil_out(cptr fmt, ...) { cptr r; va_list vp; char buf[1024]; char *str; /* Line buffer */ static char roff_buf[256]; /* Current pointer into line roff_buf */ static char *roff_p = roff_buf; /* Last space saved into roff_buf */ static char *roff_s = NULL; /* Special handling for "new sequence" */ if (!fmt) { if (roff_p != roff_buf) roff_p--; while (*roff_p == ' ' && roff_p != roff_buf) roff_p--; if (roff_p == roff_buf) froff(fff, "\n"); else { *(roff_p + 1) = '\0'; froff(fff, "%s\n\n", roff_buf); } roff_p = roff_buf; roff_s = NULL; roff_buf[0] = '\0'; return; } /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Start at the head of the buffer */ str = buf; /* Scan the given string, character at a time */ for (; *str; str++) { char ch = *str; int wrap = (ch == '\n'); if (!isprint(ch)) ch = ' '; if (roff_p >= roff_buf + 75) wrap = 1; if ((ch == ' ') && (roff_p + 2 >= roff_buf + 75)) wrap = 1; /* Handle line-wrap */ if (wrap) { *roff_p = '\0'; r = roff_p; if (roff_s && (ch != ' ')) { *roff_s = '\0'; r = roff_s + 1; } froff(fff, "%s\n", roff_buf); roff_s = NULL; roff_p = roff_buf; while (*r) *roff_p++ = *r++; } /* Save the char */ if ((roff_p > roff_buf) || (ch != ' ')) { if (ch == ' ') roff_s = roff_p; *roff_p++ = ch; } } } /* * Create a spoiler file for monsters (-SHAWN-) */ static void spoil_mon_info(cptr fname) { char buf[1024]; int msex, vn, i, j, k, n, count = 0; bool breath, magic, sin; cptr p, q; cptr vp[64]; u32b flags1, flags2, flags3, flags4, flags5, flags6, flags7; u16b why = 2; s16b *who; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msgf("Cannot create spoiler file."); return; } /* Dump the header */ spoiler_underline("Monster Spoilers for " VERSION_NAME " Version " VERSION_STRING); spoiler_blanklines(1); /* Allocate the "who" array */ C_MAKE(who, z_info->r_max, s16b); /* Scan the monsters */ for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Use that monster */ if (r_ptr->name) who[count++] = i; } /* Select the sort method */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort the array by dungeon depth of monsters */ ang_sort(who, &why, count); /* Scan again */ for (n = 0; n < count; n++) { monster_race *r_ptr = &r_info[who[n]]; /* Extract the flags */ flags1 = r_ptr->flags[0]; flags2 = r_ptr->flags[1]; flags3 = r_ptr->flags[2]; flags4 = r_ptr->flags[3]; flags5 = r_ptr->flags[4]; flags6 = r_ptr->flags[5]; flags7 = r_ptr->flags[6]; breath = FALSE; magic = FALSE; /* Extract a gender (if applicable) */ if (flags1 & (RF0_FEMALE)) msex = 2; else if (flags1 & (RF0_MALE)) msex = 1; else msex = 0; /* Prefix */ if (flags1 & (RF0_QUESTOR)) { spoil_out("[Q] "); } else if (flags1 & (RF0_UNIQUE)) { spoil_out("[U] "); } else { spoil_out("The "); } /* Name */ spoil_out("%s (", mon_race_name(r_ptr)); /* ---)--- */ /* Color */ spoil_out(attr_to_text(r_ptr->d_attr)); /* Symbol --(-- */ spoil_out(" '%c')\n", r_ptr->d_char); /* Indent */ spoil_out("=== "); /* Number */ spoil_out("Num:%d ", who[n]); /* Level */ spoil_out("Lev:%d ", (int)r_ptr->level); /* Rarity */ spoil_out("Rar:%d ", (int)r_ptr->rarity); /* Speed */ if (r_ptr->speed >= 110) { spoil_out("Spd:+%d ", (r_ptr->speed - 110)); } else { spoil_out("Spd:-%d ", (110 - r_ptr->speed)); } /* Hitpoints */ if ((flags1 & (RF0_FORCE_MAXHP)) || (r_ptr->hside == 1)) { spoil_out("Hp:%d ", ((int)r_ptr->hdice) * r_ptr->hside); } else { spoil_out("Hp:%dd%d ", (int)r_ptr->hdice, (int)r_ptr->hside); } /* Armor Class */ spoil_out("Ac:%d ", r_ptr->ac); /* Experience */ spoil_out("Exp:%ld\n", (long)(r_ptr->mexp)); /* Describe */ spoil_out(r_text + r_ptr->text); spoil_out(" "); spoil_out("This"); if (flags2 & (RF1_XXX_1)) spoil_out(" something"); if (flags3 & (RF2_ANIMAL)) spoil_out(" natural"); if (flags3 & (RF2_EVIL)) spoil_out(" evil"); if (flags3 & (RF2_GOOD)) spoil_out(" good"); if (flags3 & (RF2_UNDEAD)) spoil_out(" undead"); if (flags3 & (RF2_DRAGON)) spoil_out(" dragon"); else if (flags3 & (RF2_DEMON)) spoil_out(" demon"); else if (flags3 & (RF2_GIANT)) spoil_out(" giant"); else if (flags3 & (RF2_TROLL)) spoil_out(" troll"); else if (flags3 & (RF2_ORC)) spoil_out(" orc"); else if (flags3 & (RF2_AMBERITE)) spoil_out(" Amberite"); else spoil_out(" creature"); if (flags7 & RF6_CAN_FLY) { spoil_out("flies"); } else spoil_out("moves"); if ((flags1 & (RF0_RAND_50)) && (flags1 & (RF0_RAND_25))) { spoil_out(" extremely erratically"); } else if (flags1 & (RF0_RAND_50)) { spoil_out(" somewhat erratically"); } else if (flags1 & (RF0_RAND_25)) { spoil_out(" a bit erratically"); } else { spoil_out(" normally"); } if (flags1 & (RF0_NEVER_MOVE)) { spoil_out(", but does not deign to chase intruders"); } spoil_out(". "); if (!r_ptr->level || (flags1 & (RF0_FORCE_DEPTH))) { spoil_out("%s is never found out of depth. ", wd_che[msex]); } if (flags1 & (RF0_FORCE_SLEEP)) { spoil_out("%s is always created sluggish. ", wd_che[msex]); } if (flags2 & (RF1_AURA_FIRE)) { spoil_out("%s is surrounded by flames. ", wd_che[msex]); } if (flags3 & (RF2_AURA_COLD)) { spoil_out("%s is surrounded by ice. ", wd_che[msex]); } if (flags2 & (RF1_AURA_ELEC)) { spoil_out("%s is surrounded by electricity. ", wd_che[msex]); } if (flags2 & (RF1_REFLECTING)) { spoil_out("%s reflects bolt spells. ", wd_che[msex]); } if (flags1 & (RF0_ESCORT)) { spoil_out("%s usually appears with ", wd_che[msex]); if (flags1 & (RF0_ESCORTS)) spoil_out("escorts. "); else spoil_out("an escort. "); } if (flags1 & RF0_FRIENDS) { spoil_out("%^s usually appears in groups. ", wd_che[msex]); } if (flags1 & (RF0_CHAR_MIMIC)) { spoil_out("%s is a mimic. ", wd_che[msex]); } /* Collect inate attacks */ vn = 0; if (flags4 & RF3_SHRIEK) vp[vn++] = "shriek for help"; if (flags4 & RF3_ELDRITCH_HORROR) vp[vn++] = "blast your sanity"; if (flags4 & RF3_ROCKET) vp[vn++] = "shoot a rocket"; if (flags4 & RF3_ARROW) vp[vn++] = "fire arrows"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" may "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect breaths */ vn = 0; if (flags4 & (RF3_BR_ACID)) vp[vn++] = "acid"; if (flags4 & (RF3_BR_ELEC)) vp[vn++] = "lightning"; if (flags4 & (RF3_BR_FIRE)) vp[vn++] = "fire"; if (flags4 & (RF3_BR_COLD)) vp[vn++] = "frost"; if (flags4 & (RF3_BR_POIS)) vp[vn++] = "poison"; if (flags4 & (RF3_BR_NETH)) vp[vn++] = "nether"; if (flags4 & (RF3_BR_LITE)) vp[vn++] = "light"; if (flags4 & (RF3_BR_DARK)) vp[vn++] = "darkness"; if (flags4 & (RF3_BR_CONF)) vp[vn++] = "confusion"; if (flags4 & (RF3_BR_SOUN)) vp[vn++] = "sound"; if (flags4 & (RF3_BR_CHAO)) vp[vn++] = "chaos"; if (flags4 & (RF3_BR_DISE)) vp[vn++] = "disenchantment"; if (flags4 & (RF3_BR_NEXU)) vp[vn++] = "nexus"; if (flags4 & (RF3_BR_TIME)) vp[vn++] = "time"; if (flags4 & (RF3_BR_INER)) vp[vn++] = "inertia"; if (flags4 & (RF3_BR_GRAV)) vp[vn++] = "gravity"; if (flags4 & (RF3_BR_SHAR)) vp[vn++] = "shards"; if (flags4 & (RF3_BR_PLAS)) vp[vn++] = "plasma"; if (flags4 & (RF3_BR_WALL)) vp[vn++] = "force"; if (flags4 & (RF3_BR_MANA)) vp[vn++] = "mana"; if (flags4 & (RF3_BR_NUKE)) vp[vn++] = "toxic waste"; if (flags4 & (RF3_BR_DISI)) vp[vn++] = "disintegration"; if (vn) { breath = TRUE; spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" may breathe "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } if (flags2 & (RF1_POWERFUL)) spoil_out(" powerfully"); } /* Collect spells */ vn = 0; if (flags5 & (RF4_BA_ACID)) vp[vn++] = "produce acid balls"; if (flags5 & (RF4_BA_ELEC)) vp[vn++] = "produce lightning balls"; if (flags5 & (RF4_BA_FIRE)) vp[vn++] = "produce fire balls"; if (flags5 & (RF4_BA_COLD)) vp[vn++] = "produce frost balls"; if (flags5 & (RF4_BA_POIS)) vp[vn++] = "produce poison balls"; if (flags5 & (RF4_BA_NETH)) vp[vn++] = "produce nether balls"; if (flags5 & (RF4_BA_WATE)) vp[vn++] = "produce water balls"; if (flags4 & (RF3_BA_NUKE)) vp[vn++] = "produce balls of radiation"; if (flags5 & (RF4_BA_MANA)) vp[vn++] = "produce mana storms"; if (flags5 & (RF4_BA_DARK)) vp[vn++] = "produce darkness storms"; if (flags4 & (RF3_BA_CHAO)) vp[vn++] = "invoke raw Logrus"; if (flags6 & (RF5_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom"; if (flags5 & (RF4_DRAIN_MANA)) vp[vn++] = "drain mana"; if (flags5 & (RF4_MIND_BLAST)) vp[vn++] = "cause mind blasting"; if (flags5 & (RF4_BRAIN_SMASH)) vp[vn++] = "cause brain smashing"; if (flags5 & (RF4_CAUSE_1)) vp[vn++] = "cause light wounds and cursing"; if (flags5 & (RF4_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing"; if (flags5 & (RF4_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing"; if (flags5 & (RF4_CAUSE_4)) vp[vn++] = "cause mortal wounds"; if (flags5 & (RF4_BO_ACID)) vp[vn++] = "produce acid bolts"; if (flags5 & (RF4_BO_ELEC)) vp[vn++] = "produce lightning bolts"; if (flags5 & (RF4_BO_FIRE)) vp[vn++] = "produce fire bolts"; if (flags5 & (RF4_BO_COLD)) vp[vn++] = "produce frost bolts"; if (flags5 & (RF4_BO_POIS)) vp[vn++] = "produce poison bolts"; if (flags5 & (RF4_BO_NETH)) vp[vn++] = "produce nether bolts"; if (flags5 & (RF4_BO_WATE)) vp[vn++] = "produce water bolts"; if (flags5 & (RF4_BO_MANA)) vp[vn++] = "produce mana bolts"; if (flags5 & (RF4_BO_PLAS)) vp[vn++] = "produce plasma bolts"; if (flags5 & (RF4_BO_ICEE)) vp[vn++] = "produce ice bolts"; if (flags5 & (RF4_MISSILE)) vp[vn++] = "produce magic missiles"; if (flags5 & (RF4_SCARE)) vp[vn++] = "terrify"; if (flags5 & (RF4_BLIND)) vp[vn++] = "blind"; if (flags5 & (RF4_CONF)) vp[vn++] = "confuse"; if (flags5 & (RF4_SLOW)) vp[vn++] = "slow"; if (flags5 & (RF4_HOLD)) vp[vn++] = "paralyze"; if (flags6 & (RF5_HASTE)) vp[vn++] = "haste-self"; if (flags6 & (RF5_HEAL)) vp[vn++] = "heal-self"; if (flags6 & (RF5_INVULNER)) vp[vn++] = "make invulnerable"; if (flags6 & (RF5_BLINK)) vp[vn++] = "blink-self"; if (flags6 & (RF5_TPORT)) vp[vn++] = "teleport-self"; if (flags6 & (RF5_XXX3)) vp[vn++] = "do something"; if (flags6 & (RF5_XXX4)) vp[vn++] = "do something"; if (flags6 & (RF5_TELE_TO)) vp[vn++] = "teleport to"; if (flags6 & (RF5_TELE_AWAY)) vp[vn++] = "teleport away"; if (flags6 & (RF5_TELE_LEVEL)) vp[vn++] = "teleport level"; if (flags6 & (RF5_XXX5)) vp[vn++] = "do something"; if (flags6 & (RF5_DARKNESS)) vp[vn++] = "create darkness"; if (flags6 & (RF5_TRAPS)) vp[vn++] = "create traps"; if (flags6 & (RF5_FORGET)) vp[vn++] = "cause amnesia"; if (flags6 & (RF5_RAISE_DEAD)) vp[vn++] = "raise dead"; if (flags6 & (RF5_S_MONSTER)) vp[vn++] = "summon a monster"; if (flags6 & (RF5_S_MONSTERS)) vp[vn++] = "summon monsters"; if (flags6 & (RF5_S_KIN)) vp[vn++] = "summon aid"; if (flags6 & (RF5_S_ANT)) vp[vn++] = "summon ants"; if (flags6 & (RF5_S_SPIDER)) vp[vn++] = "summon spiders"; if (flags6 & (RF5_S_HOUND)) vp[vn++] = "summon hounds"; if (flags6 & (RF5_S_HYDRA)) vp[vn++] = "summon hydras"; if (flags6 & (RF5_S_ANGEL)) vp[vn++] = "summon an angel"; if (flags6 & (RF5_S_DEMON)) vp[vn++] = "summon a demon"; if (flags6 & (RF5_S_UNDEAD)) vp[vn++] = "summon an undead"; if (flags6 & (RF5_S_DRAGON)) vp[vn++] = "summon a dragon"; if (flags6 & (RF5_S_HI_UNDEAD)) vp[vn++] = "summon greater undead"; if (flags6 & (RF5_S_HI_DRAGON)) vp[vn++] = "summon ancient dragons"; if (flags6 & (RF5_S_CYBER)) vp[vn++] = "summon Cyberdemons"; if (flags6 & (RF5_S_AMBERITES)) vp[vn++] = "summon Lords of Amber"; if (flags6 & (RF5_S_UNIQUE)) vp[vn++] = "summon unique monsters"; if (vn) { magic = TRUE; if (breath) { spoil_out(", and is also"); } else { spoil_out(wd_che[msex]); spoil_out(" is"); } spoil_out(" magical, casting spells"); if (flags2 & (RF1_SMART)) spoil_out(" intelligently"); for (i = 0; i < vn; i++) { if (!i) spoil_out(" which "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } } if (breath || magic) { spoil_out("; 1 time in %d. ", 200 / (r_ptr->freq_inate + r_ptr->freq_spell)); } /* Collect special abilities. */ vn = 0; if (flags7 & (RF6_CAN_SWIM)) vp[vn++] = "swim"; if (flags2 & (RF1_OPEN_DOOR)) vp[vn++] = "open doors"; if (flags2 & (RF1_BASH_DOOR)) vp[vn++] = "bash down doors"; if (flags2 & (RF1_PASS_WALL)) vp[vn++] = "pass through walls"; if (flags2 & (RF1_KILL_WALL)) vp[vn++] = "bore through walls"; if (flags2 & (RF1_MOVE_BODY)) vp[vn++] = "push past weaker monsters"; if (flags2 & (RF1_KILL_BODY)) vp[vn++] = "destroy weaker monsters"; if (flags2 & (RF1_TAKE_ITEM)) vp[vn++] = "pick up objects"; if (flags2 & (RF1_KILL_ITEM)) vp[vn++] = "destroy objects"; if (flags7 & (RF6_LITE_1 | RF6_LITE_2)) vp[vn++] = "light the dungeon"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" can "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } if (flags2 & (RF1_INVISIBLE)) { spoil_out(wd_che[msex]); spoil_out(" is invisible. "); } if (flags2 & (RF1_COLD_BLOOD)) { spoil_out(wd_che[msex]); spoil_out(" is cold blooded. "); } if (flags2 & (RF1_EMPTY_MIND)) { spoil_out(wd_che[msex]); spoil_out(" is not detected by telepathy. "); } if (flags2 & (RF1_WEIRD_MIND)) { spoil_out(wd_che[msex]); spoil_out(" is rarely detected by telepathy. "); } if (flags2 & (RF1_MULTIPLY)) { spoil_out(wd_che[msex]); spoil_out(" breeds explosively. "); } if (flags2 & (RF1_REGENERATE)) { spoil_out(wd_che[msex]); spoil_out(" regenerates quickly. "); } /* Collect susceptibilities */ vn = 0; if (flags3 & (RF2_HURT_ROCK)) vp[vn++] = "rock remover"; if (flags3 & (RF2_HURT_LITE)) vp[vn++] = "bright light"; if (flags3 & (RF2_HURT_FIRE)) vp[vn++] = "fire"; if (flags3 & (RF2_HURT_COLD)) vp[vn++] = "cold"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" is hurt by "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect immunities */ vn = 0; if (flags3 & (RF2_IM_ACID)) vp[vn++] = "acid"; if (flags3 & (RF2_IM_ELEC)) vp[vn++] = "lightning"; if (flags3 & (RF2_IM_FIRE)) vp[vn++] = "fire"; if (flags3 & (RF2_IM_COLD)) vp[vn++] = "cold"; if (flags3 & (RF2_IM_POIS)) vp[vn++] = "poison"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" resists "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect resistances */ vn = 0; if (flags3 & (RF2_RES_NETH)) vp[vn++] = "nether"; if (flags3 & (RF2_RES_WATE)) vp[vn++] = "water"; if (flags3 & (RF2_RES_PLAS)) vp[vn++] = "plasma"; if (flags3 & (RF2_RES_NEXU)) vp[vn++] = "nexus"; if (flags3 & (RF2_RES_DISE)) vp[vn++] = "disenchantment"; if (flags3 & (RF2_RES_TELE)) vp[vn++] = "teleportation"; if ((flags3 & RF2_RES_TELE) && !(FLAG(r_ptr, RF_UNIQUE))) vp[vn++] = "teleportation"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" resists "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect non-effects */ vn = 0; if (flags3 & (RF2_NO_STUN)) vp[vn++] = "stunned"; if (flags3 & (RF2_NO_FEAR)) vp[vn++] = "frightened"; if (flags3 & (RF2_NO_CONF)) vp[vn++] = "confused"; if (flags3 & (RF2_NO_SLEEP)) vp[vn++] = "slept"; if ((flags3 & RF2_RES_TELE) && (FLAG(r_ptr, RF_UNIQUE))) vp[vn++] = "teleported"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" cannot be "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } spoil_out(". "); } spoil_out(wd_che[msex]); if (r_ptr->sleep > 200) spoil_out(" prefers to ignore"); else if (r_ptr->sleep > 95) spoil_out(" pays very little attention to"); else if (r_ptr->sleep > 75) spoil_out(" pays little attention to"); else if (r_ptr->sleep > 45) spoil_out(" tends to overlook"); else if (r_ptr->sleep > 25) spoil_out(" takes quite a while to see"); else if (r_ptr->sleep > 10) spoil_out(" takes a while to see"); else if (r_ptr->sleep > 5) spoil_out(" is fairly observant of"); else if (r_ptr->sleep > 3) spoil_out(" is observant of"); else if (r_ptr->sleep > 1) spoil_out(" is very observant of"); else if (r_ptr->sleep > 0) spoil_out(" is vigilant for"); else spoil_out(" is ever vigilant for"); spoil_out(" intruders, which %s may notice from %d feet. ", wd_lhe[msex], 10 * r_ptr->aaf); i = 0; if (flags1 & (RF0_DROP_60)) i += 1; if (flags1 & (RF0_DROP_90)) i += 2; if (flags1 & (RF0_DROP_1D2)) i += 2; if (flags1 & (RF0_DROP_2D2)) i += 4; if (flags1 & (RF0_DROP_3D2)) i += 6; if (flags1 & (RF0_DROP_4D2)) i += 8; /* Drops gold and/or items */ if (i) { sin = FALSE; spoil_out(wd_che[msex]); spoil_out(" will carry"); if (i == 1) { spoil_out(" a"); sin = TRUE; } else if (i == 2) { spoil_out(" one or two"); sin = TRUE; } else { spoil_out(" up to %u", (uint)i); } if (flags1 & (RF0_DROP_GREAT)) { if (sin) spoil_out("n"); spoil_out(" exceptional object"); } else if (flags1 & (RF0_DROP_GOOD)) { spoil_out(" good object"); } else if (flags1 & (RF0_DROP_USEFUL)) { spoil_out(" useful object"); } else if (flags1 & (RF0_ONLY_ITEM)) { spoil_out(" object"); } else if (flags1 & (RF0_ONLY_GOLD)) { spoil_out(" treasure"); } else { spoil_out(" object"); if (i > 1) spoil_out("s"); spoil_out(" or treasure"); } if (i > 1) spoil_out("s"); if (flags1 & (RF0_DROP_CHOSEN)) { spoil_out(", in addition to chosen objects"); } spoil_out(". "); } /* Count the actual attacks */ for (i = 0, j = 0; j < 4; j++) { if (r_ptr->blow[j].method) i++; } /* Examine the actual attacks */ for (k = 0, j = 0; j < 4; j++) { if (!r_ptr->blow[j].method) continue; /* Acquire the method */ p = rbm_info[r_ptr->blow[j].method].name; /* Default effect */ q = "???"; /* Acquire the effect */ switch (r_ptr->blow[j].effect) { case RBE_HURT: { q = "attack"; break; } case RBE_POISON: { q = "poison"; break; } case RBE_UN_BONUS: { q = "disenchant"; break; } case RBE_UN_POWER: { q = "drain charges"; break; } case RBE_EAT_GOLD: { q = "steal gold"; break; } case RBE_EAT_ITEM: { q = "steal items"; break; } case RBE_EAT_FOOD: { q = "eat your food"; break; } case RBE_EAT_LITE: { q = "absorb light"; break; } case RBE_ACID: { q = "shoot acid"; break; } case RBE_ELEC: { q = "electrocute"; break; } case RBE_FIRE: { q = "burn"; break; } case RBE_COLD: { q = "freeze"; break; } case RBE_BLIND: { q = "blind"; break; } case RBE_CONFUSE: { q = "confuse"; break; } case RBE_TERRIFY: { q = "terrify"; break; } case RBE_PARALYZE: { q = "paralyze"; break; } case RBE_LOSE_STR: { q = "reduce strength"; break; } case RBE_LOSE_INT: { q = "reduce intelligence"; break; } case RBE_LOSE_WIS: { q = "reduce wisdom"; break; } case RBE_LOSE_DEX: { q = "reduce dexterity"; break; } case RBE_LOSE_CON: { q = "reduce constitution"; break; } case RBE_LOSE_CHR: { q = "reduce charisma"; break; } case RBE_LOSE_ALL: { q = "reduce all stats"; break; } case RBE_SHATTER: { q = "shatter"; break; } case RBE_EXP_10: { q = "lower experience (by 10d6+)"; break; } case RBE_EXP_20: { q = "lower experience (by 20d6+)"; break; } case RBE_EXP_40: { q = "lower experience (by 40d6+)"; break; } case RBE_EXP_80: { q = "lower experience (by 80d6+)"; break; } case RBE_DISEASE: { q = "disease"; break; } case RBE_TIME: { q = "distrupt the time continuum"; break; } case RBE_EXP_VAMP: { q = "drain life force"; break; } } if (!k) { spoil_out(wd_che[msex]); spoil_out(" can "); } else if (k < i - 1) { spoil_out(", "); } else { spoil_out(", and "); } /* Describe the method */ spoil_out(p); /* Describe the effect, if any */ if (r_ptr->blow[j].effect) { spoil_out(" to "); spoil_out(q); if (r_ptr->blow[j].d_dice && r_ptr->blow[j].d_side) { spoil_out(" with damage"); if (r_ptr->blow[j].d_side == 1) spoil_out(" %d", (int)r_ptr->blow[j].d_dice); else spoil_out(" %dd%d", (int)r_ptr->blow[j].d_dice, (int)r_ptr->blow[j].d_side); } } k++; } if (k) { spoil_out(". "); } else if (flags1 & (RF0_NEVER_BLOW)) { spoil_out("%s has no physical attacks. ", wd_che[msex]); } spoil_out(NULL); } /* Free the "who" array */ KILL(who); /* Check for errors */ if (ferror(fff)) { msgf("Cannot close spoiler file."); return; } my_fclose(fff); msgf("Successfully created a spoiler file."); } /* * Abbreviations of damaged stats */ static cptr long_stat_names[A_MAX] = { "Strength", "Intelligence", "Wisdom", "Dexterity", "Constitution", "Charisma" }; /* * Create a spoiler file for nutations */ static void spoil_mutation(cptr fname) { int i; char buf[1024]; const mutation_type *mut_ptr; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msgf("Cannot create spoiler file."); return; } /* Dump the header */ spoiler_underline("Mutation Spoilers for " VERSION_NAME " Version " VERSION_STRING); spoiler_blanklines(1); for (i = 0; i < MUT_PER_SET * 3; i++) { mut_ptr = &mutations[i]; /* Headers */ if (i == 0) { /* Activatable mutations */ spoiler_underline("The activatable mutations"); spoiler_blanklines(1); } else if (i == MUT_PER_SET) { /* Random mutations */ spoiler_underline("Randomly activating mutations"); spoil_out(NULL); } else if (i == MUT_PER_SET * 2) { /* Other mutations */ spoiler_underline("Other mutations"); spoil_out(NULL); } /* Describe mutation */ spoil_out("%s \n", mut_ptr->desc_text); /* Type 1? */ if (i < MUT_PER_SET) { spoil_out("- Activation: %s \n", mut_ptr->name); spoil_out("- Min. level: %d \n", (int)mut_ptr->level); spoil_out("- HP/SP Cost: %d \n", mut_ptr->cost); spoil_out("- Statistic : %s \n", long_stat_names[mut_ptr->stat]); spoil_out("- Difficulty: %d \n", mut_ptr->diff); } /* Type 2? */ else if (i < MUT_PER_SET * 2) { if (mut_ptr->chance > 0) { spoil_out("- Chance/turn: 1-in-%d\n", mut_ptr->chance * 100); } } spoiler_blanklines(1); } /* Check for errors */ if (ferror(fff)) { msgf("Cannot close spoiler file."); return; } my_fclose(fff); /* Message */ msgf("Successfully created a spoiler file."); } /* * Create a spoiler file for artifacts */ static void spoil_rac_pow(cptr fname) { int i; char buf[1024]; const mutation_type *mut_ptr; /* Build the filename */ path_make(buf, ANGBAND_DIR_USER, fname); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msgf("Cannot create spoiler file."); return; } /* Dump the header */ spoiler_underline("Racial Powers Spoilers for " VERSION_NAME " Version " VERSION_STRING); spoiler_blanklines(1); /* The Racial Powers */ spoiler_underline("The Racial Powers"); spoiler_blanklines(1); for (i = 0; i < MAX_RACE_POWERS; i++) { mut_ptr = &race_powers[i]; /* Describe power */ rp_ptr = &race_info[mut_ptr->which]; spoiler_underline(rp_ptr->title); spoil_out("%s \n", mut_ptr->desc_text); spoil_out("- Activation: %s \n", mut_ptr->name); spoil_out("- Min. level: %d \n", (int)mut_ptr->level); spoil_out("- HP/SP Cost: %d \n", mut_ptr->cost); spoil_out("- Statistic : %3s \n", long_stat_names[mut_ptr->stat]); spoil_out("- Difficulty: %d \n", mut_ptr->diff); spoiler_blanklines(1); } /* Check for errors */ if (ferror(fff)) { msgf("Cannot close spoiler file."); return; } my_fclose(fff); /* Message */ msgf("Successfully created a spoiler file."); } /* * Forward declare */ extern void do_cmd_spoilers(void); /* * Create Spoiler files -BEN- */ void do_cmd_spoilers(void) { int i; /* Save the screen */ screen_save(); /* Interact */ while (1) { /* Clear screen */ Term_clear(); /* Info */ prtf(0, 2, "Create a spoiler file."); /* Prompt for a file */ prtf(5, 5, "(1) Brief Object Info (obj-desc.spo)"); prtf(5, 6, "(2) Brief Artifact Info (artifact.spo)"); prtf(5, 7, "(3) Brief Monster Info (mon-desc.spo)"); prtf(5, 8, "(4) Full Monster Info (mon-info.spo)"); prtf(5, 9, "(5) Brief Mutation Info (mutation.spo)"); prtf(5, 10, "(6) Brief Racial Powers Info (rac-pow.spo)"); /* Prompt */ prtf(0, 12, "Command: "); /* Get a choice */ i = inkey(); /* Escape */ if (i == ESCAPE) { break; } /* Option (1) */ else if (i == '1') { spoil_obj_desc("obj-desc.spo"); } /* Option (2) */ else if (i == '2') { spoil_artifact("artifact.spo"); } /* Option (3) */ else if (i == '3') { spoil_mon_desc("mon-desc.spo"); } /* Option (4) */ else if (i == '4') { spoil_mon_info("mon-info.spo"); } /* Option (5) */ else if (i == '5') { spoil_mutation("mutation.spo"); } /* Option (6) */ else if (i == '6') { spoil_rac_pow("rac-pow.spo"); } /* Oops */ else { bell("Illegal command for spoilers!"); } /* Flush messages */ message_flush(); } /* Restore the screen */ screen_load(); } #else #ifdef MACINTOSH static int i = 0; #endif /* MACINTOSH */ #endif zangband/src/wizard2.c0000755000000000000000000010637010250356275013674 0ustar rootroot/* File: wizard2.c */ /* Purpose: Wizard commands */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #include "script.h" /* * Hack -- Rerate Hitpoints */ void do_cmd_rerate(void) { int min_value, max_value, i, j, percent; min_value = (PY_MAX_LEVEL * 3 * (p_ptr->rp.hitdie - 1)) / 8; min_value += PY_MAX_LEVEL; max_value = (PY_MAX_LEVEL * 5 * (p_ptr->rp.hitdie - 1)) / 8; max_value += PY_MAX_LEVEL; p_ptr->player_hp[0] = p_ptr->rp.hitdie; /* Rerate */ while (1) { /* Collect values */ for (i = 1; i < PY_MAX_LEVEL; i++) { /* Add in racial hit dice */ j = randint1(rp_ptr->r_mhp); p_ptr->player_hp[i] = p_ptr->player_hp[i - 1] + j; /* If class hit dice is non zero - add it on */ if (cp_ptr->c_mhp) { p_ptr->player_hp[i] += randint1(cp_ptr->c_mhp); } } /* Legal values */ if ((p_ptr->player_hp[PY_MAX_LEVEL - 1] >= min_value) && (p_ptr->player_hp[PY_MAX_LEVEL - 1] <= max_value)) break; } percent = (int)(((long)p_ptr->player_hp[PY_MAX_LEVEL - 1] * 200L) / (2 * p_ptr->rp.hitdie + ((PY_MAX_LEVEL - 1) * (p_ptr->rp.hitdie + 1)))); /* Update and redraw hitpoints */ p_ptr->update |= (PU_HP); p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Handle stuff */ handle_stuff(); /* Message */ msgf("Current Life Rating is %d/100.", percent); } #ifdef ALLOW_WIZARD /* * Create the artifact of the specified number -- DAN * */ static void wiz_create_named_art(int a_idx) { int px = p_ptr->px; int py = p_ptr->py; /* Create the artifact */ create_named_art(a_idx, px, py); /* All done */ msgf("Allocated."); } /* * Hack -- quick debugging hook */ static void do_cmd_wiz_hack_ben(void) { /* Oops */ msgf("Oops."); (void)probing(); } #ifdef MONSTER_HORDES /* Summon a horde of monsters */ static void do_cmd_summon_horde(void) { int px = p_ptr->px; int py = p_ptr->py; int wy = py, wx = px; int attempts = 1000; cave_type *c_ptr; while (--attempts) { scatter(&wx, &wy, px, py, 3); /* paranoia */ if (!in_bounds2(wx, wy)) continue; c_ptr = area(wx, wy); if (cave_naked_grid(c_ptr)) break; /* Not under the player */ if ((wy == py) && (wx == px)) break; } (void)alloc_horde(wx, wy); } #endif /* MONSTER_HORDES */ #ifdef USE_64B typedef u64b ufix40_24; /* Fixed point: 40 bits integer 24 bits fractional */ static ufix40_24 pow4(ufix40_24 n) { ufix40_24 pow2 = (n * n) >> 24; return (pow2 * pow2) >> 24; } static void get_obj_dist(int min_level, int obj_num, u32b rarity[MAX_DEPTH]) { int i; long value1, total; alloc_entry *table = alloc_kind_table; ufix40_24 p; int level; for (i = 0; i < MAX_DEPTH; i++) rarity[i] = 0; for (level = 0; level < MAX_DEPTH * 2; level++) { /* Reset total */ total = 0L; /* Process probabilities */ for (i = 0; i < alloc_kind_size; i++) { /* Objects are sorted by depth */ if (table[i].level > level) break; /* What John West rejects, makes John West the best. */ if (table[i].level < min_level) continue; /* Total */ total += table[i].prob2; } /* No legal objects */ if (total <= 0) continue; value1 = 0; p = 0; /* Find the object */ for (i = 0; i < alloc_kind_size; i++) { /* Objects are sorted by depth */ if (table[i].level > level) break; /* What John West rejects, makes John West the best. */ if (table[i].level < min_level) continue; if (table[i].index == obj_num) { p += pow4((u64b) 0x1000000L * (value1 + table[i].prob2) / total) - pow4((u64b) 0x1000000L * value1 / total); } /* Increment */ value1 += table[i].prob2; } /* Add base probability */ if (level < MAX_DEPTH) rarity[level] += (u32b)(p * (GREAT_OBJ - 1) / GREAT_OBJ); /* Add the probability for out-of-depth objects */ for (i = 1; i <= MAX_DEPTH; i++) { if (level - MAX_DEPTH / i >= 0 && level - MAX_DEPTH / i < MAX_DEPTH) { rarity[level - MAX_DEPTH / i] += (u32b)((p / MAX_DEPTH) / GREAT_OBJ); } } } /* Scale down the final result */ for (i = 0; i < MAX_DEPTH; i++) rarity[i] /= 0x100; } #endif /* USE_64B */ /* * Output a rarity graph for a type of object. * * Use a monte-carlo method to calculate the probabilities. */ #ifndef USE_64B static void prt_alloc(const object_type *o_ptr, int col, int row, u32b monte) #else /* !USE_64B */ static void prt_alloc(const object_type *o_ptr, int col, int row) #endif /* USE_64B */ { u32b i, j; u32b maxd = 1, maxr = 1, maxt = 1; u32b rarity[MAX_DEPTH]; u32b total[MAX_DEPTH]; u32b display[20]; cptr c = CLR_WHITE; cptr r = "+--common--+"; u16b kind = o_ptr->k_idx; u16b home = k_info[kind].level; /* Wipe the tables */ (void)C_WIPE(rarity, MAX_DEPTH, u32b); (void)C_WIPE(total, MAX_DEPTH, u32b); (void)C_WIPE(display, 20, u32b); message_flush(); prtf(0, 0, "Calculating probability distribution - please wait."); /* Refresh */ Term_fresh(); #ifndef USE_64B /* Scan all entries */ for (i = 0; i < MAX_DEPTH; i++) { for (j = 0; j < monte; j++) { if (get_obj_num(i, 0) == kind) rarity[i]++; } total[i] = monte; } #else /* !USE_64B */ /* Calculate */ get_obj_dist(0, kind, rarity); for (i = 0; i < MAX_DEPTH; i++) total[i] = 0x10000; #endif /* USE_64B */ /* Find maxima */ for (i = 0; i < MAX_DEPTH; i++) { if (rarity[i] > maxr) maxr = rarity[i]; if (total[i] > maxt) maxt = total[i]; } /* Simulate a log graph */ if (maxt / maxr > 32) { c = CLR_L_WHITE; r = "+-uncommon-+"; } if (maxt / maxr > 1024) { c = CLR_SLATE; r = "+---rare---+"; } if (maxt / maxr > 32768L) { c = CLR_L_DARK; r = "+--unique--+"; } /* Calculate probabilities for each range */ for (i = 0; i < 20; i++) { /* Shift the values into view */ for (j = i * MAX_DEPTH / 20; j < (i + 1) * MAX_DEPTH / 20; j++) { display[i] += rarity[j] * maxt * 10 / total[j]; } /* Correct proportions */ display[i] /= maxr; /* Track maximum */ if (display[i] > maxd) maxd = display[i]; } /* Normalize */ for (i = 0; i < 20; i++) { display[i] = display[i] * 10 / maxd; } /* Graph the rarities */ for (i = 0; i < 20; i++) { Term_putch(col, row + i + 1, TERM_WHITE, '|'); /* Note the level */ if ((i * MAX_DEPTH / 20 <= home) && (home < (i + 1) * MAX_DEPTH / 20)) { prtf(col + 1, row + i + 1, CLR_RED "%.*s", display[i], "**********"); } else { prtf(col + 1, row + i + 1, "%s%.*s", c, display[i], "**********"); } } /* Make it look nice */ prtf(col, row, r); Term_putch(col, row + 2, TERM_WHITE, '6'); Term_putch(col, row + 8, TERM_WHITE, 'A'); Term_putch(col, row + 9, TERM_WHITE, 'L'); Term_putch(col, row + 10, TERM_WHITE, 'L'); Term_putch(col, row + 11, TERM_WHITE, 'O'); Term_putch(col, row + 12, TERM_WHITE, 'C'); prtf(col, row + 21, "+"); } /* * Hack -- Teleport to the target */ static void do_cmd_wiz_bamf(void) { /* Must have a target */ if (!p_ptr->target_who) return; /* Teleport to the target */ teleport_player_to(p_ptr->target_col, p_ptr->target_row); } /* * Aux function for "do_cmd_wiz_change()". -RAK- */ static void do_cmd_wiz_change_aux(void) { int i; int tmp_int; long tmp_long; char tmp_val[160]; /* Query the stats */ for (i = 0; i < A_MAX; i++) { /* Default */ strnfmt(tmp_val, 160, "%d", p_ptr->stat[i].max); /* Query */ if (!get_string(tmp_val, 4, "%s (30-400): ", stat_names[i])) return; /* Extract */ tmp_int = atoi(tmp_val); /* Verify */ if (tmp_int > stat_cap(i)) tmp_int = stat_cap(i); else if (tmp_int < 30) tmp_int = 30; /* Save it */ p_ptr->stat[i].cur = p_ptr->stat[i].max = tmp_int; } /* Default */ strnfmt(tmp_val, 160, "%ld", (long)(p_ptr->au)); /* Query */ if (!get_string(tmp_val, 9, "Gold: ")) return; /* Extract */ tmp_long = atol(tmp_val); /* Verify */ if (tmp_long < 0) tmp_long = 0L; /* Save */ p_ptr->au = tmp_long; /* Default */ strnfmt(tmp_val, 160, "%ld", (long)(p_ptr->max_exp)); /* Query */ if (!get_string(tmp_val, 10, "Experience: ")) return; /* Extract */ tmp_long = atol(tmp_val); /* Verify */ if (tmp_long < 0) tmp_long = 0L; /* Save */ p_ptr->max_exp = tmp_long; p_ptr->exp = tmp_long; /* Update */ check_experience(); } /* * Change various "permanent" player variables. */ static void do_cmd_wiz_change(void) { /* Interact */ do_cmd_wiz_change_aux(); /* Redraw everything */ do_cmd_redraw(); } /* * Create a feature near the player. */ static void do_cmd_wiz_feature(int feat) { int px = p_ptr->px; int py = p_ptr->py; int y, x, d = 3, attempts = 30; cave_type *c_ptr; while (1) { /* Find a location */ y = rand_spread(py, d); x = rand_spread(px, d); /* Reject illegal grids */ if (!in_boundsp(x, y)) continue; /* Reject the player */ if ((y == py) && (x == px)) continue; attempts--; if (!attempts) { d++; attempts = 8 * d; } /* Access grid */ c_ptr = area(x, y); /* Try to place a new feature */ if (c_ptr->feat == feat) continue; /* Okay */ break; } /* Nuke objects */ delete_object_list(&c_ptr->o_idx); /* Nuke monsters */ delete_monster_idx(c_ptr->m_idx); /* Place the feature */ cave_set_feat(x, y, feat); /* Change knowledge of grid */ parea(x, y)->feat = feat; } /* * Learn the whole wilderness map */ static void learn_map(void) { int i, j; for (i = 0; i < max_wild; i++) { for (j = 0; j < max_wild; j++) { wild[j][i].done.info |= WILD_INFO_SEEN; } } } /* * Wizard routines for creating objects -RAK- * And for manipulating them! -Bernd- * * This has been rewritten to make the whole procedure * of debugging objects much easier and more comfortable. * * The following functions are meant to play with objects: * Create, modify, roll for them (for statistic purposes) and more. * The original functions were by RAK. * The function to show an item's debug information was written * by David Reeve Sward . * Bernd (wiebelt@mathematik.hu-berlin.de) * * Here are the low-level functions * - wiz_display_item() * display an item's debug-info * - wiz_create_itemtype() * specify tval and sval (type and subtype of object) * - wiz_tweak_item() * specify pval, +AC, +tohit, +todam * Note that the wizard can leave this function anytime, * thus accepting the default-values for the remaining values. * pval comes first now, since it is most important. * - wiz_reroll_item() * apply some magic to the item or turn it into an artifact. * - wiz_roll_item() * Get some statistics about the rarity of an item: * We create a lot of fake items and see if they are of the * same type (tval and sval), then we compare pval and +AC. * If the fake-item is better or equal it is counted. * Note that cursed items that are better or equal (absolute values) * are counted, too. * HINT: This is *very* useful for balancing the game! * - wiz_quantity_item() * change the quantity of an item, but be sane about it. * * And now the high-level functions * - do_cmd_wiz_play() * play with an existing object * - wiz_create_item() * create a new object * * Note -- You do not have to specify "pval" and other item-properties * directly. Just apply magic until you are satisfied with the item. * * Note -- For some items (such as wands, staffs, some rings, etc), you * must apply magic, or you will get "broken" or "uncharged" objects. * * Note -- Redefining artifacts via "do_cmd_wiz_play()" may destroy * the artifact. Be careful. * * Hack -- this function will allow you to create multiple artifacts. * This "feature" may induce crashes or other nasty effects. */ /* * Just display an item's properties (debug-info) * Originally by David Reeve Sward * Verbose item flags by -Bernd- */ static void wiz_display_item(const object_type *o_ptr) { int j = 13; byte hack_info = o_ptr->info; /* Hack - we will reset the object to exactly like it was */ object_type *q_ptr = (object_type *)o_ptr; /* Hack the visibility by (see object_desc_store) */ q_ptr->info |= (OB_STOREB); /* Clear the screen */ clear_region(13 - 2, 1, 23); /* Describe fully */ prtf(j, 2, "%v", OBJECT_STORE_FMT(q_ptr, TRUE, 3)); /* Undo visibility hack */ q_ptr->info = hack_info; prtf(j, 4, "kind = %-5d level = %-4d tval = %-5d sval = %-5d", o_ptr->k_idx, get_object_level(o_ptr), o_ptr->tval, o_ptr->sval); prtf(j, 5, "number = %-3d wgt = %-6d ac = %-5d damage = %dd%d", o_ptr->number, o_ptr->weight, o_ptr->ac, o_ptr->dd, o_ptr->ds); prtf(j, 6, "pval = %-5d toac = %-5d tohit = %-4d todam = %-4d", o_ptr->pval, o_ptr->to_a, o_ptr->to_h, o_ptr->to_d); prtf(j, 7, "a_idx = %-4d cost = %ld", o_ptr->a_idx, (long)object_value_real(o_ptr)); prtf(j, 8, "info = %04x timeout = %-d", o_ptr->info, o_ptr->timeout); prtf(j, 9, "desc = %s", item_activation(o_ptr)); prtf(j, 10, "+------------FLAGS1------------+\n" "AFFECT........SLAY........BRAND.\n" " cvae xsqpaefc\n" "siwdcc ssidsahanvudotgddhuoclio\n" "tnieoh strnipttmiinmrrnrrraiierl\n" "rtsxna.plcfgdkcpmldncltggpksdced\n" "%v", binary_fmt, o_ptr->flags[0]); prtf(j, 17, "+------------FLAGS2------------+\n" "SUST...IMMUN..RESIST............\n" " paefctrpsaefcpfldbc sn \n" "siwdcc oclioheatcliooeialoshtncd\n" "tnieoh iierlrfraierliatrnnnrhehi\n" "rtsxna.sdcedwlatdcedsrekdfddrxss\n" "%v", binary_fmt, o_ptr->flags[1]); prtf(j + 32, 10,"+------------FLAGS3------------+\n" "SH NO tehsif itdrmsIGNRadtabchp\n" "fe tm yzdhnelneieihaefccrpgluvr\n" "il ea cktmativlgggocliotnorercm\n" "re lg rnyorhtiesehtierlvxrvssuc\n" "ec ec swpdtresptntsdcedtpttsers\n" "%v", binary_fmt, o_ptr->flags[2]); prtf(j + 32, 17,"+------------FLAGS4-------------\n" " IMSH p pt reHURT.. CURS\n" " ldac alao exaefcld as h\n" " iacomtusuptpclioia utee\n" " trilurcscsrlierltr taaa\n" " ekddtnkwhinodcedek ottl\n" "%v", binary_fmt, o_ptr->flags[3]); } /* * A structure to hold a tval and its description */ typedef struct tval_desc { int tval; cptr desc; } tval_desc; /* * A list of tvals and their textual names */ static const tval_desc tvals[] = { {TV_SWORD, "Sword"}, {TV_POLEARM, "Polearm"}, {TV_HAFTED, "Hafted Weapon"}, {TV_BOW, "Bow"}, {TV_ARROW, "Arrows"}, {TV_BOLT, "Bolts"}, {TV_SHOT, "Shots"}, {TV_SHIELD, "Shield"}, {TV_CROWN, "Crown"}, {TV_HELM, "Helm"}, {TV_GLOVES, "Gloves"}, {TV_BOOTS, "Boots"}, {TV_CLOAK, "Cloak"}, {TV_DRAG_ARMOR, "Dragon Scale Mail"}, {TV_HARD_ARMOR, "Hard Armor"}, {TV_SOFT_ARMOR, "Soft Armor"}, {TV_RING, "Ring"}, {TV_AMULET, "Amulet"}, {TV_LITE, "Lite"}, {TV_POTION, "Potion"}, {TV_SCROLL, "Scroll"}, {TV_WAND, "Wand"}, {TV_STAFF, "Staff"}, {TV_ROD, "Rod"}, {TV_LIFE_BOOK, "Life Spellbook"}, {TV_SORCERY_BOOK, "Sorcery Spellbook"}, {TV_NATURE_BOOK, "Nature Spellbook"}, {TV_CHAOS_BOOK, "Chaos Spellbook"}, {TV_DEATH_BOOK, "Death Spellbook"}, {TV_TRUMP_BOOK, "Trump Spellbook"}, {TV_ARCANE_BOOK, "Arcane Spellbook"}, {TV_SPIKE, "Spikes"}, {TV_DIGGING, "Digger"}, {TV_CHEST, "Chest"}, {TV_FIGURINE, "Magical Figurine"}, {TV_STATUE, "Statue"}, {TV_FOOD, "Food"}, {TV_FLASK, "Flask"}, {TV_JUNK, "Junk"}, {TV_SKELETON, "Skeleton"}, {0, NULL} }; /* * Strip an "object name" into a buffer */ static void strip_name(char *buf, int k_idx) { char *t; object_kind *k_ptr = &k_info[k_idx]; cptr str = (k_name + k_ptr->name); /* Skip past leading characters */ while ((*str == ' ') || (*str == '&')) str++; /* Copy useful chars */ for (t = buf; *str; str++) { if (*str != '~') *t++ = *str; } /* Terminate the new name */ *t = '\0'; } /* * Global variables so the sub-menus can access * the required information */ static int create_item_kidx = 0; static int create_item_tval = 0; /* * Select the item to use */ static bool wiz_create_itemtype_aux2(int num) { int i; /* Look up the item to use */ for (i = 0; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; if (k_ptr->tval == create_item_tval) { /* Are we there yet? */ if (!num) { create_item_kidx = i; return (TRUE); } /* Count down the objects to go */ num--; } } /* Paranoia */ return (FALSE); } /* * Specify the sval for the object to create. */ static bool wiz_create_itemtype_aux1(int tval_entry) { int i, num = 0; int tval = tvals[tval_entry].tval; char buf[1024]; char prompt[80]; menu_type *item_menu; bool result; /* Count number of options */ for (i = 0; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; if (k_ptr->tval == tval) num++; } /* Create menu array */ C_MAKE(item_menu, num + 1, menu_type); /* Collect all the objects and their descriptions */ num = 0; for (i = 0; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; if (k_ptr->tval == tval) { /* Acquire the "name" of object "i" */ strip_name(buf, i); /* Create the menu entry */ item_menu[num].text = string_make(buf); item_menu[num].help = NULL; item_menu[num].action = wiz_create_itemtype_aux2; item_menu[num].flags = MN_ACTIVE; num++; } } /* Save tval so we can access it in aux2 */ create_item_tval = tval; /* Create the prompt */ strnfmt(prompt, 80, "What Kind of %s? ", tvals[tval_entry].desc); result = display_menu(item_menu, -1, FALSE, NULL, prompt); /* Free the option strings */ for (i = 0; i <= num; i++) { string_free(item_menu[i].text); } /* Free the array */ FREE(item_menu); return (result); } /* * Specify tval and sval (type and subtype of object) originally * * This function returns the k_idx of an object type, or zero if failed */ static int wiz_create_itemtype(void) { int i, num; menu_type *item_menu; /* Count number of options */ num = 0; while(tvals[num].tval) num++; /* Create menu array */ C_MAKE(item_menu, num + 1, menu_type); /* Collect all the tvals and their descriptions */ for (i = 0; i < num; i++) { item_menu[i].text = tvals[i].desc; item_menu[i].help = NULL; item_menu[i].action = wiz_create_itemtype_aux1; item_menu[i].flags = MN_ACTIVE | MN_CLEAR; } /* Hack - we know that item_menu[num].text is NULL due to C_MAKE */ /* Clear item to make */ create_item_kidx = 0; display_menu(item_menu, -1, FALSE, NULL, "Get what type of object? "); /* Free the array */ FREE(item_menu); return (create_item_kidx); } /* * Tweak an item */ static void wiz_tweak_item(object_type *o_ptr) { char tmp_val[80]; strnfmt(tmp_val, 80, "%d", o_ptr->pval); if (!get_string(tmp_val, 6, "Enter new 'pval' setting: ")) return; o_ptr->pval = atoi(tmp_val); wiz_display_item(o_ptr); strnfmt(tmp_val, 80, "%d", o_ptr->to_a); if (!get_string(tmp_val, 6, "Enter new 'to_a' setting: ")) return; o_ptr->to_a = atoi(tmp_val); wiz_display_item(o_ptr); strnfmt(tmp_val, 80, "%d", o_ptr->to_h); if (!get_string(tmp_val, 6, "Enter new 'to_h' setting: ")) return; o_ptr->to_h = atoi(tmp_val); wiz_display_item(o_ptr); strnfmt(tmp_val, 80, "%d", o_ptr->to_d); if (!get_string(tmp_val, 6, "Enter new 'to_d' setting: ")) return; o_ptr->to_d = atoi(tmp_val); wiz_display_item(o_ptr); /* XXX XXX XXX Very dangerous... */ strnfmt(tmp_val, 80, "%d", (int)o_ptr->a_idx); if (!get_string(tmp_val, 6, "Enter new 'a_idx' setting: ")) return; o_ptr->a_idx = atoi(tmp_val); wiz_display_item(o_ptr); /* Apply trigger */ apply_object_trigger(TRIGGER_ALTER, o_ptr, ""); } /* * Apply magic to an item or turn it into an artifact. -Bernd- */ static object_type *wiz_reroll_item(object_type *o_ptr) { char ch; /* Hack -- leave normal artifacts alone */ if (FLAG(o_ptr, TR_INSTA_ART) && o_ptr->a_idx) return (o_ptr); /* Main loop. Ask for magification and artifactification */ while (TRUE) { /* Display full item debug information */ wiz_display_item(o_ptr); /* Ask wizard what to do. */ if (!get_com ("[a]ccept, [w]orthless, [n]ormal, [e]xcellent, [s]pecial? ", &ch)) { /* Preserve wizard-generated artifacts */ if (FLAG(o_ptr, TR_INSTA_ART) && o_ptr->a_idx) { a_info[o_ptr->a_idx].cur_num = 0; o_ptr->a_idx = 0; } /* Done */ return (NULL); } /* Create/change it! */ if (ch == 'A' || ch == 'a') break; /* Preserve wizard-generated artifacts */ if (FLAG(o_ptr, TR_INSTA_ART) && o_ptr->a_idx) { a_info[o_ptr->a_idx].cur_num = 0; o_ptr->a_idx = 0; /* Remove the artifact flag */ o_ptr->flags[2] &= ~(TR2_INSTA_ART); } switch (ch) { case 'w': case 'W': { /* Apply bad magic */ o_ptr = object_prep(o_ptr->k_idx); apply_magic(o_ptr, p_ptr->depth, 0, OC_FORCE_BAD); break; } case 'n': case 'N': { /* Apply normal magic */ o_ptr = object_prep(o_ptr->k_idx); apply_magic(o_ptr, p_ptr->depth, 0, OC_NORMAL); break; } case 'e': case 'E': { /* Apply great magic */ o_ptr = object_prep(o_ptr->k_idx); apply_magic(o_ptr, p_ptr->depth, 30, OC_FORCE_GOOD); break; } case 's': case 'S': { /* Apply special magic */ o_ptr = object_prep(o_ptr->k_idx); /* Make a random artifact */ (void)create_artifact(o_ptr, p_ptr->depth, FALSE); break; } } } /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Window stuff */ p_ptr->window |= (PW_SPELL | PW_PLAYER); /* Notice changes */ notice_item(); /* Success */ return (o_ptr); } /* * Redraw the rarity graph with a different number of rolls * per level. This changes the sqrt(n) poisson error. * (Otherwise really rare items don't get very good graphs.) */ static void wiz_statistics(object_type *o_ptr) { #ifndef USE_64B u32b test_roll = 100000; char tmp_val[80]; strnfmt(tmp_val, 80, "%ld", (long)test_roll); if (get_string(tmp_val, 11, "Enter number of items to roll: ")) { test_roll = atol(tmp_val); } test_roll = MAX(0, test_roll); /* Display the rarity graph */ prt_alloc(o_ptr, 0, 2, test_roll); #else /* !USE_64B */ /* Display the rarity graph */ prt_alloc(o_ptr, 0, 2); #endif /* USE_64B */ } /* * Change the quantity of a the item */ static void wiz_quantity_item(object_type *o_ptr) { int tmp_int, tmp_qnt; char tmp_val[100]; /* Never duplicate artifacts */ if (FLAG(o_ptr, TR_INSTA_ART)) return; /* Store old quantity. -LM- */ tmp_qnt = o_ptr->number; /* Default */ strnfmt(tmp_val, 100, "%d", (int)o_ptr->number); /* Query */ if (get_string(tmp_val, 3, "Quantity: ")) { /* Extract */ tmp_int = atoi(tmp_val); /* Paranoia */ if (tmp_int < 1) tmp_int = 1; if (tmp_int > 99) tmp_int = 99; /* Accept modifications */ o_ptr->number = tmp_int; /* Hack -- rod pvals must change if the number in the stack does. -LM- */ if (o_ptr->tval == TV_ROD) o_ptr->pval = o_ptr->pval * o_ptr->number / tmp_qnt; /* Notice weight changes */ p_ptr->update |= PU_WEIGHT; } } /* * Play with an item. Options include: * - Output statistics (via wiz_roll_item) * - Reroll item (via wiz_reroll_item) * - Change properties (via wiz_tweak_item) * - Change the number of items (via wiz_quantity_item) */ static void do_cmd_wiz_play(void) { object_type *q_ptr; object_type *o_ptr; char ch; cptr q, s; /* Get an item */ q = "Play with which object? "; s = "You have nothing to play with."; q_ptr = get_item(q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)); /* Not a valid item */ if (!q_ptr) return; /* Save the screen */ screen_save(); /* Duplicate object */ o_ptr = object_dup(q_ptr); /* Display the item */ wiz_display_item(o_ptr); /* The main loop */ while (TRUE) { /* Display the item */ wiz_display_item(o_ptr); /* Get choice */ if (!get_com("[a]ccept [r]eroll [t]weak [q]uantity [s]tatistics? ", &ch)) { /* Ignore changes */ msgf("Changes ignored."); /* Done */ break; } /* Accept changes */ if (ch == 'A' || ch == 'a') { /* Message */ msgf("Changes accepted."); /* Swap the objects */ swap_objects(q_ptr, o_ptr); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Window stuff */ p_ptr->window |= (PW_SPELL | PW_PLAYER); /* Notice changes */ notice_item(); break; } if (ch == 's' || ch == 'S') { wiz_statistics(o_ptr); } if (ch == 'r' || ch == 'r') { o_ptr = wiz_reroll_item(o_ptr); /* Failure - get old item */ if (!o_ptr) { /* Restore old item */ o_ptr = object_dup(q_ptr); } } if (ch == 't' || ch == 'T') { wiz_tweak_item(o_ptr); } if (ch == 'q' || ch == 'Q') { wiz_quantity_item(o_ptr); } if (ch == 'l' || ch == 'L') { int i; for (i = 0; i < MAX_TRIGGER; i++) { if (o_ptr->trigger[i]) msgf("%i - '%s'. ", i, quark_str( o_ptr->trigger[i])); } } } /* Restore the screen */ screen_load(); } /* * Wizard routine for creating objects -RAK- * Heavily modified to allow magification and artifactification -Bernd- * * Note that wizards cannot create objects on top of other objects. * * Hack -- this routine always makes a "dungeon object", and applies * magic to it, and attempts to decline cursed items. */ static void wiz_create_item(void) { int k_idx; /* Save the screen */ screen_save(); /* Get object base type */ k_idx = wiz_create_itemtype(); /* Restore the screen */ screen_load(); if (!k_idx) return; /* Place the object */ place_specific_object(p_ptr->px, p_ptr->py, p_ptr->depth, k_idx); /* All done */ msgf("Allocated."); } /* * Cure everything instantly */ static void do_cmd_wiz_cure_all(void) { /* Remove curses */ (void)remove_all_curse(); /* Restore stats */ (void)res_stat(A_STR); (void)res_stat(A_INT); (void)res_stat(A_WIS); (void)res_stat(A_CON); (void)res_stat(A_DEX); (void)res_stat(A_CHR); /* Restore the level */ (void)restore_level(); /* Heal the player */ p_ptr->chp = p_ptr->mhp; p_ptr->chp_frac = 0; /* Restore mana */ p_ptr->csp = p_ptr->msp; p_ptr->csp_frac = 0; /* Cure stuff */ (void)clear_blind(); (void)clear_confused(); (void)clear_poisoned(); (void)clear_afraid(); (void)clear_paralyzed(); (void)clear_image(); (void)clear_stun(); (void)clear_cut(); (void)clear_slow(); /* No longer hungry */ (void)set_food(PY_FOOD_MAX - 1); /* Redraw everything */ do_cmd_redraw(); } /* * Go to any level */ static void do_cmd_wiz_jump(void) { int min_depth, max_depth; dun_type *d_ptr = dungeon(); /* In the wilderness and no dungeon? */ if (!check_down_wild()) return; max_depth = d_ptr->max_level; min_depth = d_ptr->min_level; /* Ask for level */ if (p_ptr->cmd.arg <= 0) { char tmp_val[160]; /* Default */ strnfmt(tmp_val, 160, "%d", p_ptr->depth); /* Does this dungeon start right at the surface */ if (min_depth == 1) { /* Ask for a level */ if (!get_string(tmp_val, 11, "Jump to level (0-%d): ", max_depth)) return; } /* Ignore the depths between the surface and the start */ else { /* Ask for a level */ if (!get_string(tmp_val, 11, "Jump to level (0, %d-%d): ", min_depth, max_depth)) return; } /* Extract request */ p_ptr->cmd.arg = atoi(tmp_val); } /* Paranoia */ if (p_ptr->cmd.arg < 0) p_ptr->cmd.arg = 0; /* Paranoia */ if (p_ptr->cmd.arg > 0 && p_ptr->cmd.arg < min_depth) p_ptr->cmd.arg = min_depth; /* Paranoia */ if (p_ptr->cmd.arg > max_depth) p_ptr->cmd.arg = max_depth; /* Accept request */ msgf("You jump to dungeon level %d.", p_ptr->cmd.arg); /* Change level */ p_ptr->depth = p_ptr->cmd.arg; /* Change the recall_depth of the dungeon */ d_ptr->recall_depth = MAX(d_ptr->recall_depth, p_ptr->depth); /* Leaving */ p_ptr->state.leaving = TRUE; } /* * Become aware of a lot of objects */ static void do_cmd_wiz_learn(void) { int i; object_type *q_ptr; /* Scan every object */ for (i = 1; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; /* Induce awareness */ if (k_ptr->level <= p_ptr->cmd.arg) { /* Prepare object */ q_ptr = object_prep(i); /* Awareness */ object_aware(q_ptr); } } } /* * Summon some creatures */ static void do_cmd_wiz_summon(int num) { int py = p_ptr->py; int px = p_ptr->px; int i; for (i = 0; i < num; i++) { (void)summon_specific(0, px, py, p_ptr->depth, 0, TRUE, FALSE, FALSE); } } /* * Summon a creature of the specified type * * XXX XXX XXX This function is rather dangerous */ static void do_cmd_wiz_named(int r_idx, bool slp) { int py = p_ptr->py; int px = p_ptr->px; int i, x, y; cave_type *c_ptr; /* Paranoia */ /* if (!r_idx) return; */ /* Prevent illegal monsters */ if (r_idx >= z_info->r_max) return; /* Try 10 times */ for (i = 0; i < 10; i++) { int d = 1; /* Pick a location */ scatter(&x, &y, px, py, d); /* paranoia */ if (!in_bounds2(x, y)) continue; /* Require empty grids */ c_ptr = area(x, y); if (!cave_empty_grid(c_ptr)) continue; /* Not on player */ if ((x == px) && (y == py)) continue; /* Place it (allow groups) */ if (place_monster_aux(x, y, r_idx, slp, TRUE, FALSE, FALSE, TRUE)) break; } } /* * Summon a creature of the specified type * * XXX XXX XXX This function is rather dangerous */ static void do_cmd_wiz_named_friendly(int r_idx, bool slp) { (void)summon_named_creature(p_ptr->px, p_ptr->py, r_idx, slp, TRUE, TRUE); } /* * Hack -- Delete all nearby monsters */ static void do_cmd_wiz_zap(void) { int i; /* Genocide everyone nearby */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Delete nearby monsters */ if (m_ptr->cdis <= MAX_SIGHT) delete_monster_idx(i); } /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* * Hack -- Delete all monsters */ static void do_cmd_wiz_zap_all(void) { int i; /* Genocide everyone */ for (i = 1; i < m_max; i++) { monster_type *m_ptr = &m_list[i]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; /* Delete this monster */ delete_monster_idx(i); } /* Update some things */ p_ptr->update |= (PU_MON_LITE); } #ifdef ALLOW_SPOILERS /* * External function */ extern void do_cmd_spoilers(void); #endif /* ALLOW_SPOILERS */ /* * Hack -- declare external function */ extern void do_cmd_debug(void); /* * Ask for and parse a "debug command" * The "cmd.arg" may have been set. */ void do_cmd_debug(void) { int py = p_ptr->py; int px = p_ptr->px; int x, y; char cmd; /* Get a "debug command" */ (void)get_com("Debug Command: ", &cmd); /* Analyze the command */ switch (cmd) { case ESCAPE: case ' ': case '\n': case '\r': { /* Nothing */ break; } #ifdef ALLOW_SPOILERS case '"': { /* Hack -- Generate Spoilers */ do_cmd_spoilers(); break; } #endif /* ALLOW_SPOILERS */ case '?': { /* Hack -- Help */ (void)show_file("wizard.txt", NULL, 0, 0); break; } case 'a': { /* Cure all maladies */ do_cmd_wiz_cure_all(); break; } case 'A': { /* Know alignment */ msgf("Your alignment is %d.", p_ptr->align); break; } case 'b': { /* Teleport to target */ do_cmd_wiz_bamf(); break; } case 'c': { /* Create any object */ wiz_create_item(); break; } case 'C': { /* Create a named artifact */ wiz_create_named_art(p_ptr->cmd.arg); break; } case 'd': { /* Detect everything */ (void)detect_all(); break; } case 'e': { /* Edit character */ do_cmd_wiz_change(); break; } case 'f': { /* View item info */ (void)identify_fully(); break; } case 'F': { /* Create feature */ if (p_ptr->cmd.arg > 0) do_cmd_wiz_feature(p_ptr->cmd.arg); break; } case 'g': { /* Good Objects */ if (p_ptr->cmd.arg <= 0) p_ptr->cmd.arg = 1; acquirement(px, py, p_ptr->cmd.arg, FALSE, TRUE); break; } case 'h': { /* Hitpoint rerating */ do_cmd_rerate(); break; } #ifdef MONSTER_HORDES case 'H': { do_cmd_summon_horde(); break; } #endif /* MONSTER_HORDES */ case 'i': { /* Identify */ (void)ident_spell(); break; } case 'I': { /* Fields Integrity */ (void)test_field_data_integrity(); break; } case 'j': { /* Go up or down in the dungeon */ do_cmd_wiz_jump(); break; } case 'J': { /* Test compression code */ /* test_compress_module(); */ break; } case 'k': { /* Self-Knowledge */ self_knowledge(); break; } case 'K': { /* Debug lua stack depth */ debug_lua_stack(); break; } case 'l': { /* Learn about objects */ do_cmd_wiz_learn(); break; } case 'L': { /* Lose Mutation */ (void)lose_mutation(p_ptr->cmd.arg); break; } case 'm': { /* Magic Mapping */ map_area(); break; } case 'M': { /* Gain Mutation */ (void)gain_mutation(p_ptr->cmd.arg); break; } case 'r': { /* Specific reward */ (void)gain_level_reward(p_ptr->cmd.arg); break; } case 'N': { /* Summon _friendly_ named monster */ do_cmd_wiz_named_friendly(p_ptr->cmd.arg, TRUE); break; } case 'n': { /* Summon Named Monster */ do_cmd_wiz_named(p_ptr->cmd.arg, TRUE); break; } case 'o': { /* Object playing routines */ do_cmd_wiz_play(); break; } case 'p': { /* Phase Door */ teleport_player(10); break; } case 'P': { /* Polymorph self */ do_poly_self(); break; } case 'u': { /* Make every dungeon square "known" to test streamers -KMW- */ for (y = p_ptr->min_hgt; y < p_ptr->max_hgt; y++) { for (x = p_ptr->min_wid; x < p_ptr->max_wid; x++) { area(x, y)->info |= (CAVE_GLOW); parea(x, y)->feat = area(x, y)->feat; } } wiz_lite(); break; } case 's': { /* Summon Random Monster(s) */ if (p_ptr->cmd.arg <= 0) p_ptr->cmd.arg = 1; do_cmd_wiz_summon(p_ptr->cmd.arg); break; } case 't': { /* Teleport */ teleport_player(100); break; } case 'T': { /* Count towns */ int towns = 0; int stairs = 0; int i, j; for (i = 0; i < place_count; i++) { place_type *pl_ptr = &place[i]; if (!pl_ptr->quest_num) towns++; for (j = 0; j < pl_ptr->numstores; j++) { store_type *st_ptr = &pl_ptr->store[j]; if (st_ptr->type == BUILD_STAIRS) stairs++; } } msgf("%i towns, %i stairs", towns, stairs); break; } case 'v': { /* Very Good Objects */ if (p_ptr->cmd.arg <= 0) p_ptr->cmd.arg = 1; acquirement(px, py, p_ptr->cmd.arg, TRUE, TRUE); break; } case 'w': { /* Wizard Light the Level */ if (p_ptr->depth) { wiz_lite(); } else { learn_map(); } break; } #ifdef DEBUG case 'W': { test_decision_tree(); break; } #endif /* DEBUG */ case 'x': { /* Increase Experience */ if (p_ptr->cmd.arg) { gain_exp(p_ptr->cmd.arg); } else { gain_exp(p_ptr->exp + 1); } break; } case 'z': { /* Zap Monsters (Genocide) */ do_cmd_wiz_zap(); break; } case 'Z': { do_cmd_wiz_zap_all(); break; } case '_': { /* Hack -- whatever I desire */ do_cmd_wiz_hack_ben(); break; } #ifdef DEBUG_SCRIPTS case '@': { /* Execute script */ do_cmd_script(); break; } #endif /* DEBUG_SCRIPTS */ default: { /* Not a Debug Command */ msgf("That is not a valid debug command."); break; } } } #else #ifdef MACINTOSH static int i = 0; #endif #endif zangband/src/xtra1.c0000755000000000000000000022522710250356275013354 0ustar rootroot/* File: xtra1.c */ /* Purpose: misc code */ /* * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ #include "angband.h" #include "script.h" /* * Modify a stat value by a "modifier", return new value * * Stats go up: 3,4,...,17,18,18/10,18/20,...,18/220 * Or even: 18/13, 18/23, 18/33, ..., 18/220 * * Stats go down: 18/220, 18/210,..., 18/10, 18, 17, ..., 3 * Or even: 18/13, 18/03, 18, 17, ..., 3 */ s16b modify_stat_value(int value, int amount) { value += amount * 10; if (value < 30) value = 30; /* Return new value */ return (value); } /* * Print character info at given row, column in a 13 char field */ static void prt_field(cptr info, int col, int row) { /* Dump 13 spaces to clear */ put_fstr(col, row, " "); /* Dump the info itself */ put_fstr(col, row, CLR_L_BLUE "%s", info); } /* * Returns a formatted string in the buffer of * the stat value which is the first parameter * in the va_list. */ void stat_format(char *buf, uint max, cptr fmt, va_list *vp) { int arg; /* Unused parameter */ (void)fmt; /* Get the argument */ arg = va_arg(*vp, int); /* Format the number for the stat */ if (arg >= 400) strnfmt(buf, max, " 40+ "); else strnfmt(buf, max, " %2d.%d", arg / 10, arg % 10); } /* * Print character stat in given row, column */ static void prt_stat(int stat) { /* Display "injured" stat */ if (p_ptr->stat[stat].cur < p_ptr->stat[stat].max) { put_fstr(COL_STAT, ROW_STAT + stat, "%5s" CLR_YELLOW " %v", stat_names_reduced[stat], stat_format, p_ptr->stat[stat].use); } /* Display "healthy" stat */ else { put_fstr(COL_STAT, ROW_STAT + stat, "%5s" CLR_L_GREEN " %v", stat_names[stat], stat_format, p_ptr->stat[stat].use); } /* Indicate natural maximum */ if (p_ptr->stat[stat].max == stat_cap(stat)) { put_fstr(COL_STAT + 3, ROW_STAT + stat, "!"); } } static int bar_count = 0; static void clear_status_bar(void) { put_fstr(COL_STATBAR, ROW_STATBAR, " "); } static void show_status_bar(cptr *letter, int num) { int i; if (num <= 12) { /* Reset everything */ bar_count = 0; clear_status_bar(); /* Display the flags */ for (i = 0; i < num; i++) { put_fstr(COL_STATBAR + i, ROW_STATBAR, letter[i]); } } else { /* increment the count (scroll the flags) */ bar_count++; if (bar_count >= num) bar_count = 0; if (bar_count + 12 < num) { /* Simple case - all in a row */ for (i = 0; i < 12; i++) { put_fstr(COL_STATBAR + i, ROW_STATBAR, letter[i + bar_count]); } } else { /* Split over boundary */ for (i = 0; i < num - bar_count; i++) { put_fstr(COL_STATBAR + i, ROW_STATBAR, letter[i + bar_count]); } for (i = 0; i < 12 + bar_count - num; i++) { put_fstr(COL_STATBAR + i + num - bar_count, ROW_STATBAR, letter[i]); } } } } /* * Show status bar */ static void prt_status(void) { int num = 0; cptr letter[30]; /* Collate active flags */ /* Hack -- Hallucinating */ if (p_ptr->tim.image) { letter[num] = CLR_VIOLET "H"; num++; } /* Blindness */ if (p_ptr->tim.blind) { letter[num] = CLR_L_DARK "B"; num++; } /* Times see-invisible */ if (p_ptr->tim.invis) { letter[num] = CLR_L_BLUE "I"; num++; } /* Timed esp */ if (p_ptr->tim.esp) { letter[num] = CLR_ORANGE "E"; num++; } /* Timed infra-vision */ if (p_ptr->tim.infra) { letter[num] = CLR_L_RED "I"; num++; } /* Paralysis */ if (p_ptr->tim.paralyzed) { letter[num] = CLR_RED "P"; num++; } /* Confusion */ if (p_ptr->tim.confused) { letter[num] = CLR_VIOLET "C"; num++; } /* Fast */ if (p_ptr->tim.fast) { letter[num] = CLR_GREEN "S"; num++; } /* Slow */ if (p_ptr->tim.slow) { letter[num] = CLR_RED "S"; num++; } /* Protection from evil */ if (p_ptr->tim.protevil) { letter[num] = CLR_L_DARK "E"; num++; } /* Invulnerability */ if (p_ptr->tim.invuln) { letter[num] = CLR_YELLOW "I"; num++; } /* Wraith form */ if (p_ptr->tim.wraith_form) { letter[num] = CLR_L_DARK "W"; num++; } /* Heroism */ if (p_ptr->tim.hero) { letter[num] = CLR_WHITE "H"; num++; } /* Super Heroism / berserk */ if (p_ptr->tim.shero) { letter[num] = CLR_RED "B"; num++; } /* Blessed */ if (p_ptr->tim.blessed) { letter[num] = CLR_WHITE "B"; num++; } /* Shield */ if (p_ptr->tim.shield) { letter[num] = CLR_WHITE "S"; num++; } /* Oppose Acid */ if (p_ptr->tim.oppose_acid) { letter[num] = CLR_GREEN "A"; num++; } /* Oppose Lightning */ if (p_ptr->tim.oppose_elec) { letter[num] = CLR_BLUE "E"; num++; } /* Oppose Fire */ if (p_ptr->tim.oppose_fire) { letter[num] = CLR_RED "F"; num++; } /* Oppose Cold */ if (p_ptr->tim.oppose_cold) { letter[num] = CLR_WHITE "C"; num++; } /* Oppose Poison */ if (p_ptr->tim.oppose_pois) { letter[num] = CLR_GREEN "P"; num++; } /* Word of Recall */ if (p_ptr->tim.word_recall) { letter[num] = CLR_WHITE "W"; num++; } /* Confusing Hands */ if (p_ptr->state.confusing) { letter[num] = CLR_RED "C"; num++; } if (num) { /* Display the status bar if there are flags set */ show_status_bar(letter, num); } else clear_status_bar(); } /* * Prints "title", including "wizard" or "winner" as needed. */ static void prt_title(void) { cptr p; /* Wizard */ if (p_ptr->state.wizard) { p = "[=-WIZARD-=]"; } /* Winner */ else if (p_ptr->state.total_winner || (p_ptr->lev > PY_MAX_LEVEL)) { p = "***WINNER***"; } /* Normal */ else { p = player_title[p_ptr->rp.pclass][(p_ptr->lev - 1) / 5]; } prt_field(p, COL_TITLE, ROW_TITLE); } /* * Prints level */ static void prt_level(void) { if (p_ptr->lev >= p_ptr->max_lev) { put_fstr(COL_LEVEL, ROW_LEVEL, "LEVEL " CLR_L_GREEN "%6d", p_ptr->lev); } else { put_fstr(COL_LEVEL, ROW_LEVEL, "Level " CLR_YELLOW "%6d", p_ptr->lev); } } /* * Display the experience */ static void prt_exp(void) { cptr attr; if (p_ptr->exp >= p_ptr->max_exp) { attr = CLR_L_GREEN; } else { attr = CLR_YELLOW; } if (toggle_xp) { if (p_ptr->lev >= PY_MAX_LEVEL) { put_fstr(COL_EXP, ROW_EXP, "NEED %s*******", attr); } else { /* Print the amount of experience to go until the next level */ put_fstr(COL_EXP, ROW_EXP, "NEED%s%8ld", attr, (long)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L) - (long)p_ptr->exp); } } else { /* Use the 'old' experience display */ put_fstr(COL_EXP, ROW_EXP, "EXP %s%8ld", attr, (long)p_ptr->exp); } } /* * Prints current gold */ static void prt_gold(void) { put_fstr(COL_GOLD, ROW_GOLD, "AU " CLR_L_GREEN "%9ld", (long)p_ptr->au); } /* * Prints current AC */ static void prt_ac(void) { put_fstr(COL_AC, ROW_AC, "Cur AC " CLR_L_GREEN "%5d", p_ptr->dis_ac + p_ptr->dis_to_a); } /* * Prints Cur/Max hit points */ static void prt_hp(void) { cptr color; byte color_player; #ifndef VARIABLE_PLAYER_GRAPH monster_race *r_ptr = &r_info[0]; byte old_attr = r_ptr->x_attr; #endif /* !VARIABLE_PLAYER_GRAPH */ put_fstr(COL_MAXHP, ROW_MAXHP, "Max HP " CLR_L_GREEN "%5d", p_ptr->mhp); color = CLR_L_GREEN; if (p_ptr->chp >= p_ptr->mhp) { color = CLR_L_GREEN; color_player = TERM_WHITE; } else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10) { color = CLR_YELLOW; color_player = TERM_ORANGE; } else { color = CLR_RED; color_player = TERM_RED; } put_fstr(COL_CURHP, ROW_CURHP, "Cur HP %s%5d", color, p_ptr->chp); #ifndef VARIABLE_PLAYER_GRAPH /* Hack - only change the colour if in character mode */ if (r_ptr->x_char != '@') return; /* Only change colour if asked */ if (!view_player_colour) { /* Normal colour is white */ color_player = TERM_WHITE; } /* Redraw the player ? */ if (old_attr != color_player) { /* Change the player colour */ r_ptr->x_attr = color_player; /* Show the change */ if (character_dungeon) lite_spot(p_ptr->px, p_ptr->py); } #endif /* !VARIABLE_PLAYER_GRAPH */ } /* * Prints players max/cur spell points */ static void prt_sp(void) { cptr color; /* Do not show mana unless it matters */ if (!mp_ptr->spell_book) return; put_fstr(COL_MAXSP, ROW_MAXSP, "Max SP " CLR_L_GREEN "%5d", p_ptr->msp); color = CLR_L_GREEN; if (p_ptr->csp >= p_ptr->msp) { color = CLR_L_GREEN; } else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10) { color = CLR_YELLOW; } else { color = CLR_RED; } /* Show mana */ put_fstr(COL_CURSP, ROW_CURSP, "Cur SP %s%5d", color, p_ptr->csp); } /* * Prints depth in stat area */ static void prt_depth(void) { if (!p_ptr->depth) { if (p_ptr->place_num) { int q_num = place[p_ptr->place_num].quest_num; /* Is there a quest here? */ if (q_num && q_num < z_info->q_max) { /* Is this a quest to find a ruin? */ if (quest[q_num].type == QUEST_TYPE_FIND_PLACE) { prtf(COL_DEPTH, Term->hgt - 1, "Ruin"); } /* then it is this a wilderness quest */ else { prtf(COL_DEPTH, Term->hgt - 1, "Quest"); } } else { prtf(COL_DEPTH, Term->hgt - 1, "%17s", place[p_ptr->place_num].name); } } else { prtf(COL_DEPTH, Term->hgt - 1, "Wilderness"); } } else if (p_ptr->depth == dungeon()->max_level) { prtf(COL_DEPTH, Term->hgt - 1, "Bottom"); } else if (depth_in_feet) { prtf(COL_DEPTH, Term->hgt - 1, "%d ft", p_ptr->depth * 50); } else { prtf(COL_DEPTH, Term->hgt - 1, "Lev %d", p_ptr->depth); } } /* * Prints status of hunger */ static void prt_hunger(void) { /* Fainting / Starving */ if (p_ptr->food < PY_FOOD_FAINT) { put_fstr(COL_HUNGRY, Term->hgt - 1, CLR_RED "Weak "); } /* Weak */ else if (p_ptr->food < PY_FOOD_WEAK) { put_fstr(COL_HUNGRY, Term->hgt - 1, CLR_ORANGE "Weak "); } /* Hungry */ else if (p_ptr->food < PY_FOOD_ALERT) { put_fstr(COL_HUNGRY, Term->hgt - 1, CLR_YELLOW "Hungry"); } /* Normal */ else if (p_ptr->food < PY_FOOD_FULL) { put_fstr(COL_HUNGRY, Term->hgt - 1, " "); } /* Full */ else if (p_ptr->food < PY_FOOD_MAX) { put_fstr(COL_HUNGRY, Term->hgt - 1, CLR_L_GREEN "Full "); } /* Gorged */ else { put_fstr(COL_HUNGRY, Term->hgt - 1, CLR_GREEN "Gorged"); } } /* * Prints Blind status */ static void prt_blind(void) { if (p_ptr->tim.blind) { put_fstr(COL_BLIND, Term->hgt - 1, CLR_ORANGE "Blind"); } else { put_fstr(COL_BLIND, Term->hgt - 1, " "); } } /* * Prints Confusion status */ static void prt_confused(void) { if (p_ptr->tim.confused) { put_fstr(COL_CONFUSED, Term->hgt - 1, CLR_ORANGE "Confused"); } else { put_fstr(COL_CONFUSED, Term->hgt - 1, " "); } } /* * Prints Fear status */ static void prt_afraid(void) { if (p_ptr->tim.afraid) { put_fstr(COL_AFRAID, Term->hgt - 1, CLR_ORANGE "Afraid"); } else { put_fstr(COL_AFRAID, Term->hgt - 1, " "); } } /* * Prints Poisoned status */ static void prt_poisoned(void) { if (p_ptr->tim.poisoned) { put_fstr(COL_POISONED, Term->hgt - 1, CLR_ORANGE "Poisoned"); } else { put_fstr(COL_POISONED, Term->hgt - 1, " "); } } /* * Prints Searching, Resting, or 'count' status * * This function was a major bottleneck when resting, so a lot of * the text formatting code was optimized in place below. */ static void prt_state(void) { char text[16]; /* Resting */ if (p_ptr->state.resting) { int i; /* Start with "Rest" */ strcpy(text, "R "); /* Extensive (timed) rest */ if (p_ptr->state.resting >= 1000) { i = p_ptr->state.resting / 100; text[5] = '0'; text[4] = '0'; text[3] = '0' + (i % 10); if (i >= 10) { i = i / 10; text[2] = '0' + (i % 10); if (i >= 10) { text[1] = '0' + (i / 10); } } } /* Long (timed) rest */ else if (p_ptr->state.resting >= 100) { i = p_ptr->state.resting; text[5] = '0' + (i % 10); i = i / 10; text[4] = '0' + (i % 10); text[3] = '0' + (i / 10); } /* Medium (timed) rest */ else if (p_ptr->state.resting >= 10) { i = p_ptr->state.resting; text[5] = '0' + (i % 10); text[4] = '0' + (i / 10); } /* Short (timed) rest */ else if (p_ptr->state.resting > 0) { i = p_ptr->state.resting; text[5] = '0' + (i); } /* Rest until healed */ else if (p_ptr->state.resting == -1) { text[1] = text[2] = text[3] = text[4] = text[5] = '*'; } /* Rest until done */ else if (p_ptr->state.resting == -2) { text[1] = text[2] = text[3] = text[4] = text[5] = '&'; } /* Display the info (or blanks) */ put_fstr(COL_STATE, Term->hgt - 1, text); } /* Repeating */ else if (p_ptr->cmd.rep) { if (p_ptr->cmd.rep > 999) { put_fstr(COL_STATE, Term->hgt - 1, "C%3d00", p_ptr->cmd.rep / 100); } else { put_fstr(COL_STATE, Term->hgt - 1, "C %3d", p_ptr->cmd.rep); } } /* Searching */ else if (p_ptr->state.searching) { put_fstr(COL_STATE, Term->hgt - 1, "Search"); } /* Nothing interesting */ else { put_fstr(COL_STATE, Term->hgt - 1, " "); } } /* * Prints the speed or paralysis of a character. * * Note that the strings must be exactly 10 chars long. */ static void prt_speed(void) { int i = p_ptr->pspeed; /* Hack -- Visually "undo" the Search Mode Slowdown */ if (p_ptr->state.searching) i += 10; /* Paralysis */ if (p_ptr->tim.paralyzed) { put_fstr(COL_SPEED, Term->hgt - 1, CLR_RED "Paralyzed!"); } /* Fast */ else if (i > 110) { if (i <= 110 + 9) { /* One digit */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_GREEN "Fast (+%d) ", (i - 110)); } else if (i <= 110 + 99) { /* Two digits */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_GREEN "Fast (+%d)", (i - 110)); } else { /* Hack - save space */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_GREEN "Fast (***)"); } } /* Slow */ else if (i < 110) { if (i >= 110 - 9) { /* One digit */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_UMBER "Slow (-%d) ", (110 - i)); } else if (i >= 110 - 99) { /* Two digits */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_UMBER "Slow (-%d)", (110 - i)); } else { /* Hack - save space */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_UMBER "Slow (***)"); } } else { /* Nothing to print */ put_fstr(COL_SPEED, Term->hgt - 1, CLR_L_UMBER " "); } } static void prt_study(void) { if (p_ptr->new_spells) { put_fstr(COL_STUDY, Term->hgt - 1, "Study"); } else { put_fstr(COL_STUDY, Term->hgt - 1, " "); } } static void prt_cut(void) { int c = p_ptr->tim.cut; if (c > 1000) { put_fstr(COL_CUT, ROW_CUT, CLR_L_RED "Mortal wound"); } else if (c > 200) { put_fstr(COL_CUT, ROW_CUT, CLR_RED "Deep gash "); } else if (c > 100) { put_fstr(COL_CUT, ROW_CUT, CLR_RED "Severe cut "); } else if (c > 50) { put_fstr(COL_CUT, ROW_CUT, CLR_ORANGE "Nasty cut "); } else if (c > 25) { put_fstr(COL_CUT, ROW_CUT, CLR_ORANGE "Bad cut "); } else if (c > 10) { put_fstr(COL_CUT, ROW_CUT, CLR_YELLOW "Light cut "); } else if (c) { put_fstr(COL_CUT, ROW_CUT, CLR_YELLOW "Graze "); } else { put_fstr(COL_CUT, ROW_CUT, " "); } } static void prt_stun(void) { int s = p_ptr->tim.stun; if (s > 100) { put_fstr(COL_STUN, ROW_STUN, CLR_RED "Knocked out "); } else if (s > 50) { put_fstr(COL_STUN, ROW_STUN, CLR_ORANGE "Heavy stun "); } else if (s) { put_fstr(COL_STUN, ROW_STUN, CLR_ORANGE "Stun "); } else { put_fstr(COL_STUN, ROW_STUN, " "); } } /* * Redraw the "monster health bar" -DRS- * Rather extensive modifications by -BEN- * * The "monster health bar" provides visual feedback on the "health" * of the monster currently being "tracked". There are several ways * to "track" a monster, including targeting it, attacking it, and * affecting it (and nobody else) with a ranged attack. * * Display the monster health bar (affectionately known as the * "health-o-meter"). Clear health bar if nothing is being tracked. * Auto-track current target monster when bored. Note that the * health-bar stops tracking any monster that "disappears". */ static void health_redraw(void) { /* Not tracking */ if (!p_ptr->health_who) { /* Erase the health bar */ Term_erase(COL_INFO, ROW_INFO, 12); } /* Tracking an unseen monster */ else if (!m_list[p_ptr->health_who].ml) { /* Indicate that the monster health is "unknown" */ put_fstr(COL_INFO, ROW_INFO, "[----------]"); } /* Tracking a hallucinatory monster */ else if (p_ptr->tim.image) { /* Indicate that the monster health is "unknown" */ put_fstr(COL_INFO, ROW_INFO, "[----------]"); } /* Tracking a dead monster ??? */ else if (!m_list[p_ptr->health_who].hp < 0) { /* Indicate that the monster health is "unknown" */ put_fstr(COL_INFO, ROW_INFO, "[----------]"); } /* Tracking a visible monster */ else { int pct, len; monster_type *m_ptr = &m_list[p_ptr->health_who]; /* Default to almost dead */ cptr attr = CLR_RED; /* Extract the "percent" of health */ pct = 100L * m_ptr->hp / m_ptr->maxhp; /* Badly wounded */ if (pct >= 10) attr = CLR_L_RED; /* Wounded */ if (pct >= 25) attr = CLR_ORANGE; /* Somewhat Wounded */ if (pct >= 60) attr = CLR_YELLOW; /* Healthy */ if (pct >= 100) attr = CLR_L_GREEN; /* Afraid */ if (m_ptr->monfear) attr = CLR_VIOLET; /* Asleep */ if (m_ptr->csleep) attr = CLR_BLUE; /* Invulnerable */ if (m_ptr->invulner) attr = CLR_WHITE; /* Convert percent into "health" */ len = (pct < 10) ? 1 : (pct < 90) ? (pct / 10 + 1) : 10; /* Default to "unknown" */ put_fstr(COL_INFO, ROW_INFO, "[----------]"); /* Dump the current "health" (use '*' symbols) */ put_fstr(COL_INFO + 1, ROW_INFO, "%s%.*s", attr, len, "**********"); } } /* * Display basic info (mostly left of map) */ static void prt_frame_basic(void) { int i; /* Race and Class */ prt_field(rp_ptr->title, COL_RACE, ROW_RACE); prt_field(cp_ptr->title, COL_CLASS, ROW_CLASS); /* Title */ prt_title(); /* Level/Experience */ prt_level(); prt_exp(); /* All Stats */ for (i = 0; i < A_MAX; i++) prt_stat(i); /* Status Bar */ prt_status(); /* Armor */ prt_ac(); /* Hitpoints */ prt_hp(); /* Spellpoints */ prt_sp(); /* Gold */ prt_gold(); /* Current depth */ prt_depth(); /* Special */ health_redraw(); } /* * Display extra info (mostly below map) */ static void prt_frame_extra(void) { /* Cut/Stun */ prt_cut(); prt_stun(); /* Food */ prt_hunger(); /* Various */ prt_blind(); prt_confused(); prt_afraid(); prt_poisoned(); /* State */ prt_state(); /* Speed */ prt_speed(); /* Study spells */ prt_study(); } /* * Hack -- display inventory in sub-windows */ static void fix_inven(void) { int j; /* Update inventory information */ Term_write_list(p_ptr->inventory, LIST_INVEN); /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_INVEN))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display inventory */ display_inven(); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display equipment in sub-windows */ static void fix_equip(void) { int j; /* Update equipment information */ Term_write_equipment(); /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_EQUIP))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display equipment */ display_equip(); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display equipment in sub-windows */ static void fix_spell(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_SPELL))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display spell list */ display_spell_list(); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display character in sub-windows */ static void fix_player(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_PLAYER))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display player */ display_player(DISPLAY_PLAYER_STANDARD); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display recent messages in sub-windows * * XXX XXX XXX Adjust for width and split messages */ static void fix_message(void) { int j, i; int w, h; int x, y; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_MESSAGE))) continue; /* Activate */ Term_activate(angband_term[j]); /* Get size */ Term_get_size(&w, &h); /* Dump messages */ for (i = 0; i < h; i++) { cptr attr = color_seq[message_color((s16b)i)]; /* Dump the message on the appropriate line */ put_fstr(0, (h - 1) - i, "%s%s", attr, message_str((s16b)i)); /* Cursor */ (void)Term_locate(&x, &y); /* Clear to end of line */ Term_erase(x, y, 255); } /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display overhead view in sub-windows * * Note that the "player" symbol does NOT appear on the map. */ static void fix_overhead(void) { int j; int cy, cx; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_OVERHEAD))) continue; /* Activate */ Term_activate(angband_term[j]); /* No offset from player */ cx = 0; cy = 0; /* Redraw map */ display_map(&cx, &cy); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display dungeon view in sub-windows */ static void fix_dungeon(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_DUNGEON))) continue; /* Activate */ Term_activate(angband_term[j]); /* Redraw dungeon view */ display_dungeon(); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display monster recall in sub-windows */ static void fix_monster(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_MONSTER))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display monster race info */ if (p_ptr->monster_race_idx) display_roff_mon(p_ptr->monster_race_idx); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display visible monster list in sub-windows */ static void fix_visible(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_VISIBLE))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display monster list */ display_visible(); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Hack -- display object recall in sub-windows */ static void fix_object(void) { int j; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { term *old = Term; /* No window */ if (!angband_term[j]) continue; /* No relevant flags */ if (!(window_flag[j] & (PW_OBJECT))) continue; /* Activate */ Term_activate(angband_term[j]); /* Display monster race info */ if (p_ptr->object_kind_idx) display_koff(p_ptr->object_kind_idx); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } /* * Calculate number of spells player should have, and forget, * or remember, spells until that number is properly reflected. * * Note that this function induces various "status" messages, * which must be bypasses until the character is created. */ static void calc_spells(void) { int i, j, k, levels; int num_allowed, num_known; const magic_type *s_ptr; int use_realm1 = p_ptr->spell.r[0].realm - 1; int use_realm2 = p_ptr->spell.r[1].realm - 1; int which; /* Save the current number of spells to learn */ s16b old_spells = p_ptr->new_spells; cptr p = ((mp_ptr->spell_book == TV_SORCERY_BOOK) ? "spell" : "prayer"); /* Hack -- must be literate */ if (!mp_ptr->spell_book) return; /* Hack -- wait for creation */ if (!character_generated) return; /* Hack -- handle "xtra" mode */ if (character_xtra) return; /* Determine the number of spells allowed */ levels = p_ptr->lev - mp_ptr->spell_first + 1; /* Hack -- no negative spells */ if (levels < 0) levels = 0; /* Extract total allowed spells */ num_allowed = (adj_mag_study[p_ptr->stat[mp_ptr->spell_stat].ind] * levels / 50); /* Assume none known */ num_known = 0; /* Count the number of spells we know */ for (j = 0; j < PY_MAX_SPELLS; j++) { /* Count known spells */ if ((j < 32) ? (p_ptr->spell.r[0].learned & (1L << j)) : (p_ptr->spell.r[1].learned & (1L << (j - 32)))) { num_known++; } } /* See how many spells we must forget or may learn */ p_ptr->new_spells = num_allowed - num_known; /* Forget spells which are too hard */ for (i = PY_MAX_SPELLS - 1; i >= 0; i--) { /* Efficiency -- all done */ if (!p_ptr->spell.r[0].learned && !p_ptr->spell.r[1].learned) break; /* Access the spell */ j = p_ptr->spell.order[i]; /* Skip non-spells */ if (j >= 99) continue; /* Get the spell */ if (j < 32) s_ptr = &mp_ptr->info[use_realm1][j]; else s_ptr = &mp_ptr->info[use_realm2][j % 32]; /* Skip spells we are allowed to know */ if (s_ptr->slevel <= p_ptr->lev) continue; /* Is it known? */ if ((j < 32) ? (p_ptr->spell.r[0].learned & (1L << j)) : (p_ptr->spell.r[1].learned & (1L << (j - 32)))) { /* Mark as forgotten - no longer known */ if (j < 32) { p_ptr->spell.r[0].forgotten |= (1L << j); p_ptr->spell.r[0].learned &= ~(1L << j); which = use_realm1; } else { p_ptr->spell.r[1].forgotten |= (1L << (j - 32)); p_ptr->spell.r[1].learned &= ~(1L << (j - 32)); which = use_realm2; } /* Message */ msgf("You have forgotten the %s of %s.", p, spell_names[which][j % 32]); /* One more can be learned */ p_ptr->new_spells++; } } /* Forget spells if we know too many spells */ for (i = PY_MAX_SPELLS - 1; i >= 0; i--) { /* Stop when possible */ if (p_ptr->new_spells >= 0) break; /* Efficiency -- all done */ if (!p_ptr->spell.r[0].learned && !p_ptr->spell.r[1].learned) break; /* Get the (i+1)th spell learned */ j = p_ptr->spell.order[i]; /* Skip unknown spells */ if (j >= 99) continue; /* Forget it (if learned) */ if ((j < 32) ? (p_ptr->spell.r[0].learned & (1L << j)) : (p_ptr->spell.r[1].learned & (1L << (j - 32)))) { /* Mark as forgotten - no longer known */ if (j < 32) { p_ptr->spell.r[0].forgotten |= (1L << j); p_ptr->spell.r[0].learned &= ~(1L << j); which = use_realm1; } else { p_ptr->spell.r[1].forgotten |= (1L << (j - 32)); p_ptr->spell.r[1].learned &= ~(1L << (j - 32)); which = use_realm2; } /* Message */ msgf("You have forgotten the %s of %s.", p, spell_names[which][j % 32]); /* One more can be learned */ p_ptr->new_spells++; } } /* Check for spells to remember */ for (i = 0; i < PY_MAX_SPELLS; i++) { /* None left to remember */ if (p_ptr->new_spells <= 0) break; /* Efficiency -- all done */ if (!p_ptr->spell.r[0].forgotten && !p_ptr->spell.r[1].forgotten) break; /* Get the next spell we learned */ j = p_ptr->spell.order[i]; /* Skip unknown spells */ if (j >= 99) break; /* Access the spell */ if (j < 32) s_ptr = &mp_ptr->info[use_realm1][j]; else s_ptr = &mp_ptr->info[use_realm2][j % 32]; /* Skip spells we cannot remember */ if (s_ptr->slevel > p_ptr->lev) continue; /* First set of spells */ if ((j < 32) ? (p_ptr->spell.r[0].forgotten & (1L << j)) : (p_ptr->spell.r[1].forgotten & (1L << (j - 32)))) { /* No longer forgotten - known once more */ if (j < 32) { p_ptr->spell.r[0].forgotten &= ~(1L << j); p_ptr->spell.r[0].learned |= (1L << j); which = use_realm1; } else { p_ptr->spell.r[1].forgotten &= ~(1L << (j - 32)); p_ptr->spell.r[1].learned |= (1L << (j - 32)); which = use_realm2; } /* Message */ msgf("You have remembered the %s of %s.", p, spell_names[which][j % 32]); /* One less can be learned */ p_ptr->new_spells--; } } /* Assume no spells available */ k = 0; /* Count spells that can be learned */ for (j = 0; j < (p_ptr->spell.r[1].realm != REALM_NONE ? 64 : 32); j++) { /* Access the spell */ if (j < 32) s_ptr = &mp_ptr->info[use_realm1][j]; else s_ptr = &mp_ptr->info[use_realm2][j % 32]; /* Skip spells we cannot remember */ if (s_ptr->slevel > p_ptr->lev) continue; /* Skip spells we already know */ if ((j < 32) ? (p_ptr->spell.r[0].learned & (1L << j)) : (p_ptr->spell.r[1].learned & (1L << (j - 32)))) { continue; } /* Count it */ k++; } /* Cannot learn more spells than exist */ if (p_ptr->new_spells > k) p_ptr->new_spells = k; /* Spell count changed */ if (old_spells != p_ptr->new_spells) { /* Message if needed */ if (p_ptr->new_spells) { /* Message */ msgf("You can learn %d more %s%s.", p_ptr->new_spells, p, (p_ptr->new_spells != 1) ? "s" : ""); } /* Redraw Study Status */ p_ptr->redraw |= (PR_STUDY); } } /* * Calculate maximum mana. You do not need to know any spells. * Note that mana is lowered by heavy (or inappropriate) armor. * * This function induces status messages. */ static void calc_mana(void) { int msp, levels, cur_wgt, max_wgt; object_type *o_ptr; bool old_cumber_glove = p_ptr->state.cumber_glove; bool old_cumber_armor = p_ptr->state.cumber_armor; /* Hack -- Must be literate */ if (!mp_ptr->spell_book) return; if (p_ptr->rp.pclass == CLASS_MINDCRAFTER) { levels = p_ptr->lev; } else { /* Extract "effective" player level */ levels = (p_ptr->lev - mp_ptr->spell_first) + 1; } /* Hack -- no negative mana */ if (levels < 0) levels = 0; /* Extract total mana */ msp = adj_mag_mana[p_ptr->stat[mp_ptr->spell_stat].ind] * levels / 25; /* Hack -- usually add one mana */ if (msp) msp++; /* Hack: High mages have a 25% mana bonus */ if (msp && (p_ptr->rp.pclass == CLASS_HIGH_MAGE)) msp += msp / 4; /* Only mages are affected */ if (mp_ptr->spell_book == TV_SORCERY_BOOK) { /* Assume player is not encumbered by gloves */ p_ptr->state.cumber_glove = FALSE; /* Get the gloves */ o_ptr = &p_ptr->equipment[EQUIP_HANDS]; /* Normal gloves hurt mage-type spells */ if (o_ptr->k_idx && (o_ptr->pval > 0) && !((FLAG(o_ptr, TR_FREE_ACT)) || (FLAG(o_ptr, TR_DEX)))) { /* Encumbered */ p_ptr->state.cumber_glove = TRUE; /* Reduce mana */ msp = (3 * msp) / 4; } } /* Assume player not encumbered by armor */ p_ptr->state.cumber_armor = FALSE; /* Weigh the armor */ cur_wgt = 0; cur_wgt += p_ptr->equipment[EQUIP_BODY].weight; cur_wgt += p_ptr->equipment[EQUIP_HEAD].weight; cur_wgt += p_ptr->equipment[EQUIP_ARM].weight; cur_wgt += p_ptr->equipment[EQUIP_OUTER].weight; cur_wgt += p_ptr->equipment[EQUIP_HANDS].weight; cur_wgt += p_ptr->equipment[EQUIP_FEET].weight; /* Determine the weight allowance */ max_wgt = mp_ptr->spell_weight; /* Heavy armor penalizes mana by a percentage. -LM- */ if (((cur_wgt - max_wgt) / 10) > 0) { /* Encumbered */ p_ptr->state.cumber_armor = TRUE; /* * Subtract a percentage of maximum mana. * The addition of one is to make sure the * mana total is decreased by some amount. */ switch (p_ptr->rp.pclass) { case CLASS_MAGE: case CLASS_HIGH_MAGE: { /* * For these classes, mana is halved if armour * is 30 pounds over their weight limit. */ msp -= msp * (cur_wgt - max_wgt) / 600 + 1; break; } case CLASS_PRIEST: case CLASS_MINDCRAFTER: { /* Mana halved if armour is 40 pounds over weight limit. */ msp -= msp * (cur_wgt - max_wgt) / 800 + 1; break; } case CLASS_ROGUE: case CLASS_RANGER: case CLASS_MONK: { /* Mana halved if armour is 50 pounds over weight limit. */ msp -= msp * (cur_wgt - max_wgt) / 1000 + 1; break; } case CLASS_PALADIN: case CLASS_CHAOS_WARRIOR: case CLASS_WARRIOR_MAGE: { /* Mana halved if armour is 60 pounds over weight limit. */ msp -= msp * (cur_wgt - max_wgt) / 1200 + 1; break; } default: { /* For new classes, but not yet added to this formula. */ msp -= msp * (cur_wgt - max_wgt) / 800 + 1; break; } } } /* Add bonus mana (not affected by encumberance or gloves) */ msp += p_ptr->sp_bonus * levels; /* Mana can never be negative */ if (msp < 0) msp = 0; /* Maximum mana has changed */ if (p_ptr->msp != msp) { /* Enforce maximum */ if (p_ptr->csp >= msp) { p_ptr->csp = msp; p_ptr->csp_frac = 0; } /* Save new mana */ p_ptr->msp = msp; /* Display mana later */ p_ptr->redraw |= (PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER); p_ptr->window |= (PW_SPELL); } /* Hack -- handle "xtra" mode */ if (character_xtra) return; /* Take note when "glove state" changes */ if (old_cumber_glove != p_ptr->state.cumber_glove) { /* Message */ if (p_ptr->state.cumber_glove) { msgf("Your covered hands feel unsuitable for spellcasting."); } else { msgf("Your hands feel more suitable for spellcasting."); } } /* Take note when "armor state" changes */ if (old_cumber_armor != p_ptr->state.cumber_armor) { /* Message */ if (p_ptr->state.cumber_armor) { msgf("The weight of your armor encumbers your movement."); } else { msgf("You feel able to move more freely."); } } } /* * Calculate the players (maximal) hit points * Adjust current hitpoints if necessary */ static void calc_hitpoints(void) { int bonus, mhp; /* Un-inflate "half-hitpoint bonus per level" value */ bonus = ((int)(adj_con_mhp[p_ptr->stat[A_CON].ind]) - 128); /* Calculate hitpoints */ mhp = p_ptr->player_hp[p_ptr->lev - 1] + (bonus * p_ptr->lev / 2); /* Always have at least one hitpoint per level */ if (mhp < p_ptr->lev + 1) mhp = p_ptr->lev + 1; /* Factor in the hero / superhero settings */ if (p_ptr->tim.hero) mhp += 10; if (p_ptr->tim.shero) mhp += 30; /* New maximum hitpoints */ if (p_ptr->mhp != mhp) { /* Enforce maximum */ if (p_ptr->chp >= mhp) { p_ptr->chp = mhp; p_ptr->chp_frac = 0; } /* Save the new max-hitpoints */ p_ptr->mhp = mhp; /* Display hitpoints (later) */ p_ptr->redraw |= (PR_HP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); } } /* * Extract and set the current "lite radius" */ static void calc_torch(void) { int i; object_type *o_ptr; s16b old_lite = p_ptr->cur_lite; /* Assume no light */ p_ptr->cur_lite = 0; /* Loop through all wielded items */ for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Examine actual lites */ if ((i == EQUIP_LITE) && (o_ptr->k_idx) && (o_ptr->tval == TV_LITE)) { /* Artifact Lites provide permanent, bright, lite */ if (FLAG(o_ptr, TR_INSTA_ART)) { p_ptr->cur_lite += 3; continue; } /* Lanterns (with fuel) provide more lite */ if ((o_ptr->sval == SV_LITE_LANTERN) && (o_ptr->timeout > 0)) { p_ptr->cur_lite += 2; continue; } /* Torches (with fuel) provide some lite */ if ((o_ptr->sval == SV_LITE_TORCH) && (o_ptr->timeout > 0)) { p_ptr->cur_lite += 1; continue; } } else { /* Skip empty slots */ if (!o_ptr->k_idx) continue; /* does this item glow? */ if (FLAG(o_ptr, TR_LITE)) p_ptr->cur_lite++; } } /* * Check if the player doesn't have a lite source, * but does glow as an intrinsic. */ if ((p_ptr->cur_lite == 0) && (FLAG(p_ptr, TR_LITE))) { p_ptr->cur_lite = 1; } /* * Hack - blindness gives a torch radius of zero. * This speeds up the map_info() function. */ if (p_ptr->tim.blind) { /* No light */ p_ptr->cur_lite = 0; } /* Notice changes in the "lite radius" */ if (old_lite != p_ptr->cur_lite) { /* Update the view */ p_ptr->update |= (PU_VIEW); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Update the monster lighting */ p_ptr->update |= (PU_MON_LITE); /* Redraw the map */ p_ptr->redraw |= (PR_MAP); } } /* * Recalculate the inventory and equipment weight */ static void calc_weight(void) { object_type *o_ptr; int i; /* No weight yet */ p_ptr->total_weight = 0; OBJ_ITT_START (p_ptr->inventory, o_ptr) { /* Increase the weight */ p_ptr->total_weight += (o_ptr->number * o_ptr->weight); } OBJ_ITT_END; for (i = 0; i < EQUIP_MAX; i++) { o_ptr = &p_ptr->equipment[i]; /* Need valid items */ if (!o_ptr->k_idx) continue; /* Increase the weight */ p_ptr->total_weight += o_ptr->weight; } } /* * Computes current weight limit. */ static int weight_limit(void) { int i; /* Weight limit based only on strength */ i = adj_str_wgt[p_ptr->stat[A_STR].ind] * 100; /* Return the result */ return (i); } /* Calculate all class-based bonuses and penalties to melee Skill. Oangband * recognizes that it takes a great deal of training to get critical hits with * a large, heavy weapon - training that many classes simply do not have the * time or inclination for. -LM- */ static sint add_special_melee_skill(byte pclass, object_type *o_ptr) { int add_skill = 0; s16b weight = o_ptr->weight; switch (pclass) { case CLASS_WARRIOR: { /* * Warrior. * Can use 15 lb weapons without penalty at level 1, * and 45 lb weapons without penalty at 50th level. */ add_skill = 25 + p_ptr->lev - (weight / 6); if (add_skill > 0) add_skill = 0; if (add_skill < -10) add_skill = -10; break; } case CLASS_MAGE: case CLASS_HIGH_MAGE: { /* * Mage/High Mage. * Can use 6 lb weapons without penalty at level 1, * and 16 lb weapons without penalty at 50th level. */ add_skill = 20 + (2 * p_ptr->lev / 3) - (weight / 3); if (add_skill > 0) add_skill = 0; if (add_skill < -30) add_skill = -30; break; } case CLASS_PRIEST: { /* * Priest. * Can use 12 lb weapons without penalty at level 1, * and 22 lb weapons without penalty at 50th level. */ add_skill = 30 + (1 * p_ptr->lev / 2) - (weight / 4); if (add_skill > 0) add_skill = 0; if (add_skill < -25) add_skill = -25; break; } case CLASS_ROGUE: { /* * Rogue. * Can use 10 lb weapons without penalty at level 1, * and 20 lb weapons without penalty at 50th level. * Can get a bonus for using light weapons. */ if (!o_ptr->k_idx) { add_skill = 0; } else { add_skill = 33 + (2 * p_ptr->lev / 3) - (weight / 3); if (add_skill > 0) add_skill = add_skill / 2; if (add_skill > 15) add_skill = 15; if (add_skill < -25) add_skill = -25; } break; } case CLASS_RANGER: { /* * Ranger. * Can use 12 lb weapons without penalty at level 1, * and 25 lb weapons without penalty at 50th level. */ add_skill = 25 + (1 * p_ptr->lev / 2) - (weight / 5); if (add_skill > 0) add_skill = 0; if (add_skill < -20) add_skill = -20; break; } case CLASS_PALADIN: case CLASS_CHAOS_WARRIOR: case CLASS_WARRIOR_MAGE: { /* * Paladin/Chaos warrior/Warrior mage. * Can use 15 lb weapons without penalty at level 1, * and 45 lb weapons without penalty at 50th level. */ add_skill = 25 + p_ptr->lev - (weight / 6); if (add_skill > 0) add_skill = 0; if (add_skill < -10) add_skill = -10; break; } case CLASS_MONK: { /* * Monk. * Can use 5 lb weapons without penalty at level 1, * and slightly over 12 lb weapons without penalty at 50th level. * Much prefers to use hands and feet. */ if (!o_ptr->k_idx) { add_skill = 14 + (p_ptr->lev); } else { add_skill = 16 + (p_ptr->lev / 2) - (weight / 3); if (add_skill > 0) add_skill = 0; if (add_skill < -30) add_skill = -30; } break; } case CLASS_MINDCRAFTER: { /* * Mindcrafter. * Can use 6 lb weapons without penalty at level 1, * and 16 lb weapons without penalty at 50th level. */ add_skill = 20 + (2 * p_ptr->lev / 3) - (weight / 3); if (add_skill > 0) add_skill = 0; if (add_skill < -30) add_skill = -30; break; } } return (add_skill); } /* * Calculate all class and race-based bonuses and penalties to missile skill * -LM- */ static sint add_special_missile_skill(byte pclass) { int add_skill = 0; switch (pclass) { case CLASS_ROGUE: { /* Rogues are good with slings. */ if (p_ptr->ammo_tval == TV_SHOT) { add_skill = 3 + p_ptr->lev / 4; } break; } case CLASS_RANGER: { /* * Rangers have a high missile skill, * but they are not supposed to * be great with xbows and slings. */ if (p_ptr->ammo_tval == TV_SHOT) { add_skill = 0 - p_ptr->lev / 7; } if (p_ptr->ammo_tval == TV_BOLT) { add_skill = 0 - p_ptr->lev / 7; } break; } case CLASS_MONK: { /* Monks get a small bonus with slings. */ if (p_ptr->ammo_tval == TV_SHOT) { add_skill = p_ptr->lev / 7; } } } return (add_skill); } void object_bonuses(const object_type *o_ptr, bonuses_type *b) { /* Zero the bonuses */ memset(b, 0, sizeof(bonuses_type)); /* Affect stats */ if (FLAG(o_ptr, TR_STR)) b->stat[A_STR] = o_ptr->pval; if (FLAG(o_ptr, TR_INT)) b->stat[A_INT] = o_ptr->pval; if (FLAG(o_ptr, TR_WIS)) b->stat[A_WIS] = o_ptr->pval; if (FLAG(o_ptr, TR_DEX)) b->stat[A_DEX] = o_ptr->pval; if (FLAG(o_ptr, TR_CON)) b->stat[A_CON] = o_ptr->pval; if (FLAG(o_ptr, TR_CHR)) b->stat[A_CHR] = o_ptr->pval; /* Affect mana */ if (FLAG(o_ptr, TR_SP)) b->sp_bonus = o_ptr->pval; /* Affect stealth */ if (FLAG(o_ptr, TR_STEALTH)) b->skills[SKILL_STL] = o_ptr->pval; /* Affect sensing ability (factor of five) */ if (FLAG(o_ptr, TR_SEARCH)) b->skills[SKILL_SNS] = (o_ptr->pval * 5); /* Affect searching frequency (factor of five) */ if (FLAG(o_ptr, TR_SEARCH)) b->skills[SKILL_FOS] = (o_ptr->pval * 5); /* Affect infravision */ if (FLAG(o_ptr, TR_INFRA)) b->see_infra = o_ptr->pval; /* Affect digging (factor of 20) */ if (FLAG(o_ptr, TR_TUNNEL)) b->skills[SKILL_DIG] = (o_ptr->pval * 20); /* Affect speed */ if (FLAG(o_ptr, TR_SPEED)) b->pspeed = o_ptr->pval; /* Affect blows */ if (FLAG(o_ptr, TR_BLOWS)) b->extra_blows = o_ptr->pval; /* Boost shots */ if (FLAG(o_ptr, TR_XTRA_SHOTS)) b->extra_shots = 1; /* Boost saving throws */ if (FLAG(o_ptr, TR_LUCK_10)) b->skills[SKILL_SAV] = 10; /* Apply special bonuses */ apply_object_trigger(TRIGGER_BONUS, (object_type *)o_ptr, "p", "b", "bonuses_type", b); } void object_bonuses_known(const object_type *o_ptr, bonuses_type *b) { /* Zero the bonuses */ memset(b, 0, sizeof(bonuses_type)); /* Affect stats */ if (KN_FLAG(o_ptr, TR_STR)) b->stat[A_STR] = o_ptr->pval; if (KN_FLAG(o_ptr, TR_INT)) b->stat[A_INT] = o_ptr->pval; if (KN_FLAG(o_ptr, TR_WIS)) b->stat[A_WIS] = o_ptr->pval; if (KN_FLAG(o_ptr, TR_DEX)) b->stat[A_DEX] = o_ptr->pval; if (KN_FLAG(o_ptr, TR_CON)) b->stat[A_CON] = o_ptr->pval; if (KN_FLAG(o_ptr, TR_CHR)) b->stat[A_CHR] = o_ptr->pval; /* Affect mana */ if (KN_FLAG(o_ptr, TR_SP)) b->sp_bonus = o_ptr->pval; /* Affect stealth */ if (KN_FLAG(o_ptr, TR_STEALTH)) b->skills[SKILL_STL] = o_ptr->pval; /* Affect sensing ability (factor of five) */ if (KN_FLAG(o_ptr, TR_SEARCH)) b->skills[SKILL_SNS] = (o_ptr->pval * 5); /* Affect searching frequency (factor of five) */ if (KN_FLAG(o_ptr, TR_SEARCH)) b->skills[SKILL_FOS] = (o_ptr->pval * 5); /* Affect infravision */ if (KN_FLAG(o_ptr, TR_INFRA)) b->see_infra = o_ptr->pval; /* Affect digging (factor of 20) */ if (KN_FLAG(o_ptr, TR_TUNNEL)) b->skills[SKILL_DIG] = (o_ptr->pval * 20); /* Affect speed */ if (KN_FLAG(o_ptr, TR_SPEED)) b->pspeed = o_ptr->pval; /* Affect blows */ if (KN_FLAG(o_ptr, TR_BLOWS)) b->extra_blows = o_ptr->pval; /* Boost shots */ if (KN_FLAG(o_ptr, TR_XTRA_SHOTS)) b->extra_shots = 1; /* Boost saving throws */ if (KN_FLAG(o_ptr, TR_LUCK_10)) b->skills[SKILL_SAV] = 10; /* Apply special bonuses */ if (object_known_full(o_ptr)) apply_object_trigger(TRIGGER_BONUS, (object_type *)o_ptr, "p", "b", "bonuses_type", b); } /* * Calculate the players current "state", taking into account * not only race/class intrinsics, but also objects being worn * and temporary spell effects. * * See also calc_mana() and calc_hitpoints(). * * Take note of the new "speed code", in particular, a very strong * player will start slowing down as soon as he reaches 150 pounds, * but not until he reaches 450 pounds will he be half as fast as * a normal kobold. This both hurts and helps the player, hurts * because in the old days a player could just avoid 300 pounds, * and helps because now carrying 300 pounds is not very painful. * * The "weapon" and "bow" do *not* add to the bonuses to hit or to * damage, since that would affect non-combat things. These values * are actually added in later, at the appropriate place. * * This function induces various "status" messages. */ static void calc_bonuses(void) { int i, j, hold; int old_speed; bool old_telepathy; bool old_see_inv; int old_dis_ac; int old_dis_to_a; int extra_blows; int extra_shots; object_type *o_ptr; bool old_heavy_wield = p_ptr->state.heavy_wield; bool old_heavy_shoot = p_ptr->state.heavy_shoot; bool old_icky_wield = p_ptr->state.icky_wield; bool old_monk_armour = p_ptr->state.monk_armour_stat; object_flags oflags; object_flags *of_ptr = &oflags; /* Save the old speed */ old_speed = p_ptr->pspeed; /* Save the old vision stuff */ old_telepathy = FLAG(p_ptr, TR_TELEPATHY) ? TRUE : FALSE; old_see_inv = FLAG(p_ptr, TR_SEE_INVIS) ? TRUE : FALSE; /* Save the old armor class */ old_dis_ac = p_ptr->dis_ac; old_dis_to_a = p_ptr->dis_to_a; /* Clear extra blows/shots */ extra_blows = extra_shots = 0; /* Calculate monk armour status */ if (p_ptr->rp.pclass == CLASS_MONK) { u16b monk_arm_wgt = 0; /* Weigh the armor */ monk_arm_wgt += p_ptr->equipment[EQUIP_BODY].weight; monk_arm_wgt += p_ptr->equipment[EQUIP_HEAD].weight; monk_arm_wgt += p_ptr->equipment[EQUIP_ARM].weight; monk_arm_wgt += p_ptr->equipment[EQUIP_OUTER].weight; monk_arm_wgt += p_ptr->equipment[EQUIP_HANDS].weight; monk_arm_wgt += p_ptr->equipment[EQUIP_FEET].weight; if (monk_arm_wgt > (100 + (p_ptr->lev * 4))) { /* Burdened */ p_ptr->state.monk_armour_stat = TRUE; } else { /* Not burdened */ p_ptr->state.monk_armour_stat = FALSE; } } /* Clear the stat modifiers */ for (i = 0; i < A_MAX; i++) p_ptr->stat[i].add = 0; /* Clear the Displayed/Real armor class */ p_ptr->dis_ac = p_ptr->ac = 0; /* Clear the Displayed/Real Bonuses */ p_ptr->dis_to_h = p_ptr->to_h = 0; p_ptr->dis_to_d = p_ptr->to_d = 0; p_ptr->dis_to_a = p_ptr->to_a = 0; /* Start with "normal" speed */ p_ptr->pspeed = 110; /* Start with "normal" mana */ p_ptr->sp_bonus = 0; /* Start with a single blow per turn */ p_ptr->num_blow = 1; /* Start with a single shot per turn */ p_ptr->num_fire = 1; /* Reset the "ammo" tval */ p_ptr->ammo_tval = 0; /* Clear all the flags */ p_ptr->flags[0] = 0; p_ptr->flags[1] = 0; p_ptr->flags[2] = 0; p_ptr->flags[3] = 0; /* Base infravision (purely racial) */ p_ptr->see_infra = rp_ptr->infra; /* Base skill -- disarming */ p_ptr->skills[SKILL_DIS] = rp_ptr->r_dis + cp_ptr->c_dis; /* Base skill -- magic devices */ p_ptr->skills[SKILL_DEV] = rp_ptr->r_dev + cp_ptr->c_dev; /* Base skill -- saving throw */ p_ptr->skills[SKILL_SAV] = rp_ptr->r_sav + cp_ptr->c_sav; /* Base skill -- stealth */ p_ptr->skills[SKILL_STL] = rp_ptr->r_stl + cp_ptr->c_stl; /* Base skill -- sensing ability */ p_ptr->skills[SKILL_SNS] = rp_ptr->r_sns + cp_ptr->c_sns; /* Base skill -- searching frequency */ p_ptr->skills[SKILL_FOS] = rp_ptr->r_fos + cp_ptr->c_fos; /* Base skill -- combat (normal) */ p_ptr->skills[SKILL_THN] = rp_ptr->r_thn + cp_ptr->c_thn; /* Base skill -- combat (shooting) */ p_ptr->skills[SKILL_THB] = rp_ptr->r_thb + cp_ptr->c_thb; /* Base skill -- combat (throwing) */ p_ptr->skills[SKILL_THT] = rp_ptr->r_thb + cp_ptr->c_thb; /* Base skill -- digging */ p_ptr->skills[SKILL_DIG] = 0; /* Get the player racial/class flags (including some mutations) */ player_flags(of_ptr); /* Hack - handle speed from monk/sprite/klackon here */ if (FLAG(of_ptr, TR_SPEED)) { p_ptr->pspeed += (p_ptr->lev) / 10; of_ptr->flags[0] &= ~(TR0_SPEED); } p_ptr->flags[0] |= of_ptr->flags[0]; p_ptr->flags[1] |= of_ptr->flags[1]; p_ptr->flags[2] |= of_ptr->flags[2]; p_ptr->flags[3] |= of_ptr->flags[3]; /* Effects of constantly acting mutations */ if (p_ptr->muta3) { mutation_effect(); } /* Scan the usable inventory */ for (i = 0; i < EQUIP_MAX; i++) { bonuses_type b; o_ptr = &p_ptr->equipment[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; p_ptr->flags[0] |= o_ptr->flags[0]; p_ptr->flags[1] |= o_ptr->flags[1]; p_ptr->flags[2] |= o_ptr->flags[2]; p_ptr->flags[3] |= o_ptr->flags[3]; /* Calculate bonuses from object */ object_bonuses(o_ptr, &b); /* Modify the base armor class */ p_ptr->ac += o_ptr->ac; /* The base armor class is always known */ p_ptr->dis_ac += o_ptr->ac; /* Apply bonuses to stats */ for (j = 0; j < A_MAX; j++) { p_ptr->stat[j].add += b.stat[j]; } p_ptr->sp_bonus += b.sp_bonus; p_ptr->see_infra += b.see_infra; p_ptr->pspeed += b.pspeed; extra_blows += b.extra_blows; extra_shots += b.extra_shots; /* Apply bonuses to skills */ for (j = 0; j < MAX_SKILL; j++) { p_ptr->skills[j] += b.skills[j]; } /* Apply the bonuses to armor class */ p_ptr->to_a += o_ptr->to_a; /* Apply the mental bonuses to armor class, if known */ if (object_known_p(o_ptr)) p_ptr->dis_to_a += o_ptr->to_a; /* Hack -- do not apply "weapon" bonuses */ if (i == EQUIP_WIELD) continue; /* Hack -- do not apply "bow" bonuses */ if (i == EQUIP_BOW) continue; /* Apply the bonuses to hit/damage */ p_ptr->to_h += o_ptr->to_h; p_ptr->to_d += o_ptr->to_d; /* Apply the mental bonuses tp hit/damage, if known */ if (object_known_p(o_ptr)) p_ptr->dis_to_h += o_ptr->to_h; if (object_known_p(o_ptr)) p_ptr->dis_to_d += o_ptr->to_d; } /* Monks get extra ac for armour _not worn_ */ if ((p_ptr->rp.pclass == CLASS_MONK) && (!p_ptr->state.monk_armour_stat)) { if (!(p_ptr->equipment[EQUIP_BODY].k_idx)) { p_ptr->to_a += (p_ptr->lev * 3) / 2; p_ptr->dis_to_a += (p_ptr->lev * 3) / 2; } if (!(p_ptr->equipment[EQUIP_OUTER].k_idx) && (p_ptr->lev > 15)) { p_ptr->to_a += ((p_ptr->lev - 13) / 3); p_ptr->dis_to_a += ((p_ptr->lev - 13) / 3); } if (!(p_ptr->equipment[EQUIP_ARM].k_idx) && (p_ptr->lev > 10)) { p_ptr->to_a += ((p_ptr->lev - 8) / 3); p_ptr->dis_to_a += ((p_ptr->lev - 8) / 3); } if (!(p_ptr->equipment[EQUIP_HEAD].k_idx) && (p_ptr->lev > 4)) { p_ptr->to_a += (p_ptr->lev - 2) / 3; p_ptr->dis_to_a += (p_ptr->lev - 2) / 3; } if (!(p_ptr->equipment[EQUIP_HANDS].k_idx)) { p_ptr->to_a += (p_ptr->lev / 2); p_ptr->dis_to_a += (p_ptr->lev / 2); } if (!(p_ptr->equipment[EQUIP_FEET].k_idx)) { p_ptr->to_a += (p_ptr->lev / 3); p_ptr->dis_to_a += (p_ptr->lev / 3); } } /* Hack -- aura of fire also provides light */ if (FLAG(p_ptr, TR_SH_FIRE)) SET_FLAG(p_ptr, TR_LITE); /* Golems also get an intrinsic AC bonus */ if (p_ptr->rp.prace == RACE_GOLEM) { p_ptr->to_a += 20 + (p_ptr->lev / 5); p_ptr->dis_to_a += 20 + (p_ptr->lev / 5); } /* Calculate stats */ for (i = 0; i < A_MAX; i++) { int top, use, ind; /* Extract the new "stat_use" value for the stat */ top = modify_stat_value(p_ptr->stat[i].max, p_ptr->stat[i].add); /* Notice changes */ if (p_ptr->stat[i].top != top) { /* Save the new value */ p_ptr->stat[i].top = top; /* Redisplay the stats later */ p_ptr->redraw |= (PR_STATS); /* Window stuff */ p_ptr->window |= (PW_PLAYER); } /* Extract the new "stat_use" value for the stat */ use = modify_stat_value(p_ptr->stat[i].cur, p_ptr->stat[i].add); if ((i == A_CHR) && (p_ptr->muta3 & MUT3_ILL_NORM)) { int floor = 8 + 2 * p_ptr->lev; if (floor <= 18) floor *= 10; else floor += 180-18; /* 10 to 18/90 charisma, guaranteed, based on level */ if (use < floor) { use = floor; } } /* Notice changes */ if (p_ptr->stat[i].use != use) { /* Save the new value */ p_ptr->stat[i].use = use; /* Redisplay the stats later */ p_ptr->redraw |= (PR_STATS); /* Window stuff */ p_ptr->window |= (PW_PLAYER); } /* Find index into various tables */ if (use < 400) ind = use / 10 - 3; else ind = 37; /* Notice changes */ if (p_ptr->stat[i].ind != ind) { /* Save the new index */ p_ptr->stat[i].ind = ind; /* Change in CON affects Hitpoints */ if (i == A_CON) { p_ptr->update |= (PU_HP); } /* Change in INT may affect Mana/Spells */ else if (i == A_INT) { if (mp_ptr->spell_stat == A_INT) { p_ptr->update |= (PU_MANA | PU_SPELLS); } } /* Change in WIS may affect Mana/Spells */ else if (i == A_WIS) { if (mp_ptr->spell_stat == A_WIS) { p_ptr->update |= (PU_MANA | PU_SPELLS); } } /* Window stuff */ p_ptr->window |= (PW_PLAYER); } } /* Apply temporary "stun" */ if (p_ptr->tim.stun > 50) { p_ptr->to_h -= 20; p_ptr->dis_to_h -= 20; p_ptr->to_d -= 20; p_ptr->dis_to_d -= 20; } else if (p_ptr->tim.stun) { p_ptr->to_h -= 5; p_ptr->dis_to_h -= 5; p_ptr->to_d -= 5; p_ptr->dis_to_d -= 5; } /* Invulnerability */ if (p_ptr->tim.invuln) { p_ptr->to_a += 100; p_ptr->dis_to_a += 100; } /* wraith_form */ if (p_ptr->tim.wraith_form) { p_ptr->to_a += 100; p_ptr->dis_to_a += 100; SET_FLAG(p_ptr, TR_REFLECT); } /* Temporary blessing */ if (p_ptr->tim.blessed) { p_ptr->to_a += 5; p_ptr->dis_to_a += 5; p_ptr->to_h += 10; p_ptr->dis_to_h += 10; } /* Temporary shield */ if (p_ptr->tim.shield) { p_ptr->to_a += 50; p_ptr->dis_to_a += 50; } /* Temporary "Hero" */ if (p_ptr->tim.hero) { p_ptr->to_h += 12; p_ptr->dis_to_h += 12; } /* Temporary "Beserk" */ if (p_ptr->tim.shero) { p_ptr->to_h += 24; p_ptr->dis_to_h += 24; p_ptr->to_a -= 10; p_ptr->dis_to_a -= 10; } /* Temporary "fast" */ if (p_ptr->tim.fast) { p_ptr->pspeed += 10; } /* Temporary "slow" */ if (p_ptr->tim.slow) { p_ptr->pspeed -= 10; } /* Temporary "telepathy" */ if (p_ptr->tim.esp) { SET_FLAG(p_ptr, TR_TELEPATHY); } /* Temporary see invisible */ if (p_ptr->tim.invis) { SET_FLAG(p_ptr, TR_SEE_INVIS); } /* Temporary infravision boost */ if (p_ptr->tim.infra) { p_ptr->see_infra += 3; } /* Hack -- Hero/Shero -> Res fear */ if (p_ptr->tim.hero || p_ptr->tim.shero) { SET_FLAG(p_ptr, TR_RES_FEAR); } /* Hack -- Telepathy Change */ if (FLAG(p_ptr, TR_TELEPATHY) != old_telepathy) { p_ptr->update |= (PU_MONSTERS); } /* Hack -- See Invis Change */ if (((FLAG(p_ptr, TR_SEE_INVIS)) ? TRUE : FALSE) != old_see_inv) { p_ptr->update |= (PU_MONSTERS); } /* Extract the current weight (in tenth pounds) */ j = p_ptr->total_weight; /* Extract the "weight limit" (in tenth pounds) */ i = weight_limit(); /* XXX XXX XXX Apply "encumbrance" from weight */ if (j > i / 2) p_ptr->pspeed -= ((j - (i / 2)) / (i / 10)); /* Bloating slows the player down (a little) */ if (p_ptr->food >= PY_FOOD_MAX) p_ptr->pspeed -= 10; /* Searching slows the player down */ if (p_ptr->state.searching) p_ptr->pspeed -= 10; /* Display the speed (if needed) */ if (p_ptr->pspeed != old_speed) p_ptr->redraw |= (PR_SPEED); /* Actual Modifier Bonuses (Un-inflate stat bonuses) */ p_ptr->to_a += ((int)(adj_dex_ta[p_ptr->stat[A_DEX].ind]) - 128); p_ptr->to_d += ((int)(adj_str_td[p_ptr->stat[A_STR].ind]) - 128); p_ptr->to_h += ((int)(adj_dex_th[p_ptr->stat[A_DEX].ind]) - 128); /* Displayed Modifier Bonuses (Un-inflate stat bonuses) */ p_ptr->dis_to_a += ((int)(adj_dex_ta[p_ptr->stat[A_DEX].ind]) - 128); p_ptr->dis_to_d += ((int)(adj_str_td[p_ptr->stat[A_STR].ind]) - 128); p_ptr->dis_to_h += ((int)(adj_dex_th[p_ptr->stat[A_DEX].ind]) - 128); /* Redraw armor (if needed) */ if ((p_ptr->dis_ac != old_dis_ac) || (p_ptr->dis_to_a != old_dis_to_a)) { /* Redraw */ p_ptr->redraw |= (PR_ARMOR); /* Window stuff */ p_ptr->window |= (PW_PLAYER); } /* Obtain the "hold" value */ hold = adj_str_hold[p_ptr->stat[A_STR].ind]; /* Examine the "current bow" */ o_ptr = &p_ptr->equipment[EQUIP_BOW]; /* Assume not heavy */ p_ptr->state.heavy_shoot = FALSE; /* It is hard to carry a heavy bow */ if (hold < o_ptr->weight / 10) { /* Hard to wield a heavy bow */ p_ptr->to_h += 2 * (hold - o_ptr->weight / 10); p_ptr->dis_to_h += 2 * (hold - o_ptr->weight / 10); /* Heavy Bow */ p_ptr->state.heavy_shoot = TRUE; } /* Compute "extra shots" if needed */ if (o_ptr->k_idx) { /* Analyze the launcher */ switch (o_ptr->sval) { case SV_SLING: { p_ptr->ammo_tval = TV_SHOT; p_ptr->ammo_mult = 2; p_ptr->bow_energy = 50; break; } case SV_SHORT_BOW: { p_ptr->ammo_tval = TV_ARROW; p_ptr->ammo_mult = 2; p_ptr->bow_energy = 100; break; } case SV_LONG_BOW: { p_ptr->ammo_tval = TV_ARROW; if (p_ptr->stat[A_STR].use >= 160) { p_ptr->ammo_mult = 3; } else { /* weak players cannot use a longbow well */ p_ptr->ammo_mult = 2; } p_ptr->bow_energy = 100; break; } case SV_LIGHT_XBOW: { p_ptr->ammo_tval = TV_BOLT; p_ptr->ammo_mult = 4; p_ptr->bow_energy = 120; break; } case SV_HEAVY_XBOW: { p_ptr->ammo_tval = TV_BOLT; p_ptr->ammo_mult = 5; if (p_ptr->stat[A_DEX].use >= 160) { p_ptr->bow_energy = 150; } else { /* players with low dex will take longer to load */ p_ptr->bow_energy = 200; } break; } } /* Apply special flags */ if (o_ptr->k_idx && !p_ptr->state.heavy_shoot) { /* Extra shots */ p_ptr->num_fire += extra_shots; /* Hack -- Rangers love Bows */ if ((p_ptr->rp.pclass == CLASS_RANGER) && (p_ptr->ammo_tval == TV_ARROW)) { /* Extra shot at level 15 */ if (p_ptr->lev >= 15) p_ptr->num_fire++; /* Extra shot at level 30 */ if (p_ptr->lev >= 30) p_ptr->num_fire++; /* Extra shot at level 45 */ if (p_ptr->lev >= 45) p_ptr->num_fire++; } /* Hack -- Rangers can use XBows as well */ if ((p_ptr->rp.pclass == CLASS_RANGER) && (p_ptr->ammo_tval == TV_BOLT)) { /* Extra shot at level 30 */ if (p_ptr->lev >= 30) p_ptr->num_fire++; } /* Hack -- Rogues love Slings */ if ((p_ptr->rp.pclass == CLASS_ROGUE) && (p_ptr->ammo_tval == TV_SHOT)) { /* Extra shot at level 20 */ if (p_ptr->lev >= 20) p_ptr->num_fire++; /* Extra shot at level 40 */ if (p_ptr->lev >= 40) p_ptr->num_fire++; } /* * Addendum -- also "Reward" high level warriors, * with _any_ missile weapon -- TY */ if (p_ptr->rp.pclass == CLASS_WARRIOR && (p_ptr->ammo_tval <= TV_BOLT) && (p_ptr->ammo_tval >= TV_SHOT)) { /* Extra shot at level 40 */ if (p_ptr->lev >= 40) p_ptr->num_fire++; } } } /* Add all class and race-specific adjustments to missile Skill. -LM- */ p_ptr->skills[SKILL_THB] += add_special_missile_skill(p_ptr->rp.pclass); /* Examine the "main weapon" */ o_ptr = &p_ptr->equipment[EQUIP_WIELD]; /* Add all other class-specific adjustments to melee Skill. -LM- */ p_ptr->skills[SKILL_THN] += add_special_melee_skill(p_ptr->rp.pclass, o_ptr); /* Assume okay */ p_ptr->state.icky_wield = FALSE; /* Extra bonus for warriors... */ if (p_ptr->rp.pclass == CLASS_WARRIOR) { p_ptr->to_h += (p_ptr->lev / 5); p_ptr->to_d += (p_ptr->lev / 5); p_ptr->dis_to_h += (p_ptr->lev / 5); p_ptr->dis_to_d += (p_ptr->lev / 5); } /* Priest weapon penalty for non-blessed edged weapons */ if ((p_ptr->rp.pclass == CLASS_PRIEST) && (!(FLAG(p_ptr, TR_BLESSED))) && ((o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM))) { /* Reduce the real bonuses */ p_ptr->to_h -= (p_ptr->lev / 5); p_ptr->to_d -= (p_ptr->lev / 5); /* Reduce the mental bonuses */ p_ptr->dis_to_h -= (p_ptr->lev / 5); p_ptr->dis_to_d -= (p_ptr->lev / 5); /* Icky weapon */ p_ptr->state.icky_wield = TRUE; } /* Affect Skill -- stealth (bonus one) */ p_ptr->skills[SKILL_STL] += 1; /* Affect Skill -- disarming (DEX and INT) */ p_ptr->skills[SKILL_DIS] += adj_dex_dis[p_ptr->stat[A_DEX].ind]; p_ptr->skills[SKILL_DIS] += adj_int_dis[p_ptr->stat[A_INT].ind]; /* Affect Skill -- magic devices (INT) */ p_ptr->skills[SKILL_DEV] += adj_int_dev[p_ptr->stat[A_INT].ind]; /* Affect Skill -- saving throw (WIS) */ p_ptr->skills[SKILL_SAV] += (adj_wis_sav[p_ptr->stat[A_WIS].ind] - 128); /* Affect Skill -- digging (STR) */ p_ptr->skills[SKILL_DIG] += adj_str_dig[p_ptr->stat[A_STR].ind]; /* Affect Skill -- disarming (Level, by Class) */ p_ptr->skills[SKILL_DIS] += (cp_ptr->x_dis * p_ptr->lev / 10); /* Affect Skill -- magic devices (Level, by Class) */ p_ptr->skills[SKILL_DEV] += (cp_ptr->x_dev * p_ptr->lev / 10); /* Affect Skill -- saving throw (Level, by Class) */ p_ptr->skills[SKILL_SAV] += (cp_ptr->x_sav * p_ptr->lev / 10); /* Affect Skill -- stealth (Level, by Class) */ p_ptr->skills[SKILL_STL] += (cp_ptr->x_stl * p_ptr->lev / 10); /* Affect Skill -- sensing ability (Level, by Class) */ p_ptr->skills[SKILL_SNS] += (cp_ptr->x_sns * p_ptr->lev / 10); /* Affect Skill -- search frequency (Level, by Class) */ p_ptr->skills[SKILL_FOS] += (cp_ptr->x_fos * p_ptr->lev / 10); /* Affect Skill -- combat (normal) (Level, by Class) */ p_ptr->skills[SKILL_THN] += (cp_ptr->x_thn * p_ptr->lev / 50); /* Affect Skill -- combat (shooting) (Level, by Class) */ p_ptr->skills[SKILL_THB] += (cp_ptr->x_thb * p_ptr->lev / 50); /* Affect Skill -- combat (throwing) (Level, by Class) */ p_ptr->skills[SKILL_THT] += (cp_ptr->x_thb * p_ptr->lev / 50); /* Limit Skill -- digging from 1 up */ if (p_ptr->skills[SKILL_DIG] < 1) p_ptr->skills[SKILL_DIG] = 1; /* Limit Skill -- saving throw from 1 up */ if (p_ptr->skills[SKILL_SAV] < 1) p_ptr->skills[SKILL_SAV] = 1; /* Limit Skill -- stealth from 0 to 30 */ if (p_ptr->skills[SKILL_STL] > 30) p_ptr->skills[SKILL_STL] = 30; if (p_ptr->skills[SKILL_STL] < 0) p_ptr->skills[SKILL_STL] = 0; /* Apply Skill -- Extract noise from stealth */ p_ptr->noise = (1L << (30 - p_ptr->skills[SKILL_STL])); if ((FLAG(p_ptr, TR_NO_MAGIC)) && (p_ptr->skills[SKILL_SAV] < p_ptr->lev * 2 + 85)) p_ptr->skills[SKILL_SAV] = p_ptr->lev * 2 + 85; /* Assume not heavy */ p_ptr->state.heavy_wield = FALSE; /* Are we using a weapon? */ if (o_ptr->k_idx) { /* It is hard to hold a heavy weapon */ if (hold < o_ptr->weight / 10) { /* Hard to wield a heavy weapon */ p_ptr->to_h += 2 * (hold - o_ptr->weight / 10); p_ptr->dis_to_h += 2 * (hold - o_ptr->weight / 10); /* Heavy weapon */ p_ptr->state.heavy_wield = TRUE; /* The player gets to swing a heavy weapon only once. -LM- */ p_ptr->num_blow = 1; } /* Normal weapons */ else { int str_index, dex_index; int effective_weight = 0, mul = 6; int skill; /* Enforce a minimum weight of three pounds. */ effective_weight = (o_ptr->weight < 30 ? 30 : o_ptr->weight); /* Compare strength and weapon weight. */ str_index = mul * adj_str_blow[p_ptr->stat[A_STR].ind] / effective_weight; /* Maximal value */ if (str_index > 11) str_index = 11; /* Index by dexterity */ dex_index = (adj_dex_blow[p_ptr->stat[A_DEX].ind]); /* Maximal value */ if (dex_index > 11) dex_index = 11; /* Use the blows table */ p_ptr->num_blow = blows_table[str_index][dex_index]; /* Get weapon skill (not including magical enhancments) */ skill = p_ptr->skills[SKILL_THN]; /* Require high skill to get large number of blows */ if (p_ptr->num_blow > 1 + skill / 15) p_ptr->num_blow = 1 + skill / 15; /* Paranoia - require at least one blow */ if (p_ptr->num_blow < 1) p_ptr->num_blow = 1; /* Add in extra blows */ p_ptr->num_blow += extra_blows; /* Boost digging skill by weapon weight */ p_ptr->skills[SKILL_DIG] += (o_ptr->weight / 10); } } /* No weapon */ else { /* Different calculation for monks with empty hands */ if (p_ptr->rp.pclass == CLASS_MONK) { p_ptr->num_blow = 2; if (p_ptr->lev > 9) p_ptr->num_blow++; if (p_ptr->lev > 14) p_ptr->num_blow++; if (p_ptr->lev > 24) p_ptr->num_blow++; if (p_ptr->lev > 34) p_ptr->num_blow++; if (p_ptr->lev > 44) p_ptr->num_blow++; if (p_ptr->lev > 49) p_ptr->num_blow++; if (p_ptr->state.monk_armour_stat) { p_ptr->num_blow /= 2; } else { p_ptr->to_h += (p_ptr->lev / 3); p_ptr->to_d += (p_ptr->lev / 3); p_ptr->dis_to_h += (p_ptr->lev / 3); p_ptr->dis_to_d += (p_ptr->lev / 3); } p_ptr->num_blow += extra_blows; } else { /* Everyone gets two blows if not wielding a weapon. -LM- */ p_ptr->num_blow = 2; } } /* Hack -- handle "xtra" mode */ if (character_xtra) return; /* Take note when "heavy bow" changes */ if (old_heavy_shoot != p_ptr->state.heavy_shoot) { /* Message */ if (p_ptr->state.heavy_shoot) { msgf("You have trouble wielding such a heavy bow."); } else if (p_ptr->equipment[EQUIP_BOW].k_idx) { msgf("You have no trouble wielding your bow."); } else { msgf("You feel relieved to put down your heavy bow."); } } /* Take note when "heavy weapon" changes */ if (old_heavy_wield != p_ptr->state.heavy_wield) { /* Message */ if (p_ptr->state.heavy_wield) { msgf("You have trouble wielding such a heavy weapon."); } else if (p_ptr->equipment[EQUIP_WIELD].k_idx) { msgf("You have no trouble wielding your weapon."); } else { msgf("You feel relieved to put down your heavy weapon."); } } /* Take note when "illegal weapon" changes */ if (old_icky_wield != p_ptr->state.icky_wield) { /* Message */ if (p_ptr->state.icky_wield) { msgf("You do not feel comfortable with your weapon."); } else if (p_ptr->equipment[EQUIP_WIELD].k_idx) { msgf("You feel comfortable with your weapon."); } else { msgf("You feel more comfortable after removing your weapon."); } } if (p_ptr->rp.pclass == CLASS_MONK && (p_ptr->state.monk_armour_stat != old_monk_armour)) { if (p_ptr->state.monk_armour_stat) { msgf("The weight of your armor disrupts your balance."); } else msgf("You regain your balance."); } p_ptr->align = friend_align; } /* * Handle "p_ptr->notice" */ void notice_stuff(void) { /* Notice stuff */ if (!p_ptr->notice) return; /* Combine the pack */ if (p_ptr->notice & (PN_COMBINE)) { p_ptr->notice &= ~(PN_COMBINE); combine_pack(); } /* Reorder the pack */ if (p_ptr->notice & (PN_REORDER)) { p_ptr->notice &= ~(PN_REORDER); reorder_pack(); } } /* * Handle "p_ptr->update" */ void update_stuff(void) { /* Update stuff */ if (!p_ptr->update) return; if (p_ptr->update & (PU_WEIGHT)) { p_ptr->update &= ~(PU_WEIGHT); calc_weight(); } if (p_ptr->update & (PU_BONUS)) { p_ptr->update &= ~(PU_BONUS); calc_bonuses(); } if (p_ptr->update & (PU_TORCH)) { p_ptr->update &= ~(PU_TORCH); calc_torch(); } if (p_ptr->update & (PU_HP)) { p_ptr->update &= ~(PU_HP); calc_hitpoints(); } if (p_ptr->update & (PU_MANA)) { p_ptr->update &= ~(PU_MANA); calc_mana(); } if (p_ptr->update & (PU_SPELLS)) { p_ptr->update &= ~(PU_SPELLS); calc_spells(); } /* Character is not ready yet, no screen updates */ if (!character_generated) return; /* Character is in "icky" mode, no screen updates */ if (character_icky) return; /* Do not update map, it doesn't exist */ if (!character_dungeon) return; if (p_ptr->update & (PU_MAP)) { p_ptr->update &= ~(PU_MAP); map_panel_size(); } if ((p_ptr->update & (PU_MON_LITE)) && monster_light) { p_ptr->update &= ~(PU_MON_LITE); update_mon_lite(); /* * Hack - the odds are that since monsters moved, * we need to redraw the map. */ p_ptr->redraw |= (PR_MAP); p_ptr->update |= (PU_VIEW); } if (p_ptr->update & (PU_VIEW)) { p_ptr->update &= ~(PU_VIEW); update_view(); } if (p_ptr->update & (PU_FLOW)) { p_ptr->update &= ~(PU_FLOW); update_flow(); } if (p_ptr->update & (PU_DISTANCE)) { p_ptr->update &= ~(PU_DISTANCE); p_ptr->update &= ~(PU_MONSTERS); update_monsters(TRUE); } if (p_ptr->update & (PU_MONSTERS)) { p_ptr->update &= ~(PU_MONSTERS); update_monsters(FALSE); } } /* * Handle "p_ptr->redraw" */ void redraw_stuff(void) { int i; /* Redraw stuff */ if (!p_ptr->redraw) return; /* Character is not ready yet, no screen updates */ if (!character_generated) return; /* Character is in "icky" mode, no screen updates */ if (character_icky) return; /* Hack -- clear the screen */ if (p_ptr->redraw & (PR_WIPE)) { p_ptr->redraw &= ~(PR_WIPE); message_flush(); Term_clear(); } if (p_ptr->redraw & (PR_BASIC)) { p_ptr->redraw &= ~(PR_BASIC); p_ptr->redraw &= ~(PR_MISC | PR_TITLE | PR_STATS | PR_STATUS); p_ptr->redraw &= ~(PR_LEV | PR_EXP | PR_GOLD); p_ptr->redraw &= ~(PR_ARMOR | PR_HP | PR_MANA); p_ptr->redraw &= ~(PR_DEPTH | PR_HEALTH); prt_frame_basic(); } if (p_ptr->redraw & (PR_EQUIPPY)) { p_ptr->redraw &= ~(PR_EQUIPPY); print_equippy(); /* To draw / delete equippy chars */ } if (p_ptr->redraw & (PR_MISC)) { p_ptr->redraw &= ~(PR_MISC); prt_field(rp_ptr->title, COL_RACE, ROW_RACE); prt_field(cp_ptr->title, COL_CLASS, ROW_CLASS); } if (p_ptr->redraw & (PR_TITLE)) { p_ptr->redraw &= ~(PR_TITLE); prt_title(); } if (p_ptr->redraw & (PR_LEV)) { p_ptr->redraw &= ~(PR_LEV); prt_level(); } if (p_ptr->redraw & (PR_EXP)) { p_ptr->redraw &= ~(PR_EXP); prt_exp(); } if (p_ptr->redraw & (PR_STATS)) { p_ptr->redraw &= ~(PR_STATS); /* All Stats */ for (i = 0; i < A_MAX; i++) prt_stat(i); } if (p_ptr->redraw & (PR_STATUS)) { p_ptr->redraw &= ~(PR_STATUS); prt_status(); } if (p_ptr->redraw & (PR_ARMOR)) { p_ptr->redraw &= ~(PR_ARMOR); prt_ac(); } if (p_ptr->redraw & (PR_HP)) { p_ptr->redraw &= ~(PR_HP); prt_hp(); } if (p_ptr->redraw & (PR_MANA)) { p_ptr->redraw &= ~(PR_MANA); prt_sp(); } if (p_ptr->redraw & (PR_GOLD)) { p_ptr->redraw &= ~(PR_GOLD); prt_gold(); } if (p_ptr->redraw & (PR_DEPTH)) { p_ptr->redraw &= ~(PR_DEPTH); prt_depth(); } if (p_ptr->redraw & (PR_HEALTH)) { p_ptr->redraw &= ~(PR_HEALTH); health_redraw(); } if (p_ptr->redraw & (PR_EXTRA)) { p_ptr->redraw &= ~(PR_EXTRA); p_ptr->redraw &= ~(PR_CUT | PR_STUN); p_ptr->redraw &= ~(PR_HUNGER); p_ptr->redraw &= ~(PR_BLIND | PR_CONFUSED); p_ptr->redraw &= ~(PR_AFRAID | PR_POISONED); p_ptr->redraw &= ~(PR_STATE | PR_SPEED | PR_STUDY); prt_frame_extra(); } if (p_ptr->redraw & (PR_CUT)) { p_ptr->redraw &= ~(PR_CUT); prt_cut(); } if (p_ptr->redraw & (PR_STUN)) { p_ptr->redraw &= ~(PR_STUN); prt_stun(); } if (p_ptr->redraw & (PR_HUNGER)) { p_ptr->redraw &= ~(PR_HUNGER); prt_hunger(); } if (p_ptr->redraw & (PR_BLIND)) { p_ptr->redraw &= ~(PR_BLIND); prt_blind(); } if (p_ptr->redraw & (PR_CONFUSED)) { p_ptr->redraw &= ~(PR_CONFUSED); prt_confused(); } if (p_ptr->redraw & (PR_AFRAID)) { p_ptr->redraw &= ~(PR_AFRAID); prt_afraid(); } if (p_ptr->redraw & (PR_POISONED)) { p_ptr->redraw &= ~(PR_POISONED); prt_poisoned(); } if (p_ptr->redraw & (PR_STATE)) { p_ptr->redraw &= ~(PR_STATE); prt_state(); } if (p_ptr->redraw & (PR_SPEED)) { p_ptr->redraw &= ~(PR_SPEED); prt_speed(); } if (p_ptr->redraw & (PR_STUDY)) { p_ptr->redraw &= ~(PR_STUDY); prt_study(); } /* Do not update map it, doesn't exist */ if (!character_dungeon) return; if (p_ptr->redraw & (PR_MAP)) { p_ptr->redraw &= ~(PR_MAP); prt_map(); } } /* * Handle "p_ptr->window" */ void window_stuff(void) { int j; u32b mask = 0L; /* Nothing to do */ if (!p_ptr->window) return; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Save usable flags */ if (angband_term[j]) mask |= window_flag[j]; } /* Display inventory */ if (p_ptr->window & (PW_INVEN)) { p_ptr->window &= ~(PW_INVEN); fix_inven(); } /* Display equipment */ if (p_ptr->window & (PW_EQUIP)) { p_ptr->window &= ~(PW_EQUIP); fix_equip(); } /* Apply usable flags */ p_ptr->window &= mask; /* Nothing to do */ if (!p_ptr->window) return; /* Display spell list */ if (p_ptr->window & (PW_SPELL)) { p_ptr->window &= ~(PW_SPELL); fix_spell(); } /* Display player */ if (p_ptr->window & (PW_PLAYER)) { p_ptr->window &= ~(PW_PLAYER); fix_player(); } /* Display overhead view */ if (p_ptr->window & (PW_MESSAGE)) { p_ptr->window &= ~(PW_MESSAGE); fix_message(); } /* Display object recall */ if (p_ptr->window & (PW_OBJECT)) { p_ptr->window &= ~(PW_OBJECT); fix_object(); } /* Display monster recall */ if (p_ptr->window & (PW_MONSTER)) { p_ptr->window &= ~(PW_MONSTER); fix_monster(); } /* Do not update map it, doesn't exist */ if (!character_dungeon) return; /* Display monster list */ if (p_ptr->window & (PW_VISIBLE)) { p_ptr->window &= ~(PW_VISIBLE); fix_visible(); } /* Display overhead view */ if (p_ptr->window & (PW_OVERHEAD)) { p_ptr->window &= ~(PW_OVERHEAD); fix_overhead(); } /* Display overhead view */ if (p_ptr->window & (PW_DUNGEON)) { p_ptr->window &= ~(PW_DUNGEON); fix_dungeon(); } } /* * Handle "p_ptr->update" and "p_ptr->redraw" and "p_ptr->window" */ void handle_stuff(void) { /* Update stuff */ if (p_ptr->update) update_stuff(); /* Redraw stuff */ if (p_ptr->redraw) redraw_stuff(); /* Window stuff */ if (p_ptr->window) window_stuff(); } /* * Handle "p_ptr->change" */ void change_stuff(void) { /* Nothing to do */ if (!p_ptr->change) return; /* Redraw screen after a wiz_lite() */ if (p_ptr->change & (PC_WIZ_LITE)) { p_ptr->change &= ~(PC_WIZ_LITE); change_wiz_lite(); } /* Shimmer monsters */ if (p_ptr->change & (PC_SHIMMER)) { p_ptr->change &= ~(PC_SHIMMER); change_shimmer(); } /* Repair monsters */ if (p_ptr->change & (PC_REPAIR)) { p_ptr->change &= ~(PC_REPAIR); change_repair(); } /* Give beastman mutation at birth */ if (p_ptr->change & (PC_MUTATE)) { p_ptr->change &= ~(PC_MUTATE); msgf("You feel different!"); (void)gain_mutation(0); } } /* * Roll a saving throw for the player */ bool player_save(int power) { return saving_throw(p_ptr->skills[SKILL_SAV] - power); } zangband/src/xtra2.c0000755000000000000000000022272310250356275013353 0ustar rootroot/* File: xtra2.c */ /* Purpose: effects of various "objects" */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" #define REWARD_CHANCE 10 /* * Advance experience levels and print experience */ void check_experience(void) { bool level_reward = FALSE; bool multi_rew = FALSE; bool level_mutation = FALSE; /* Hack -- lower limit */ if (p_ptr->exp < 0) p_ptr->exp = 0; /* Hack -- lower limit */ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0; /* Hack -- upper limit */ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP; /* Hack -- upper limit */ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP; /* Hack -- maintain "max" experience */ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp; /* Redraw experience */ p_ptr->redraw |= (PR_EXP); /* Handle stuff */ handle_stuff(); /* Lose levels while possible */ while ((p_ptr->lev > 1) && (p_ptr->exp < (player_exp[p_ptr->lev - 2] * p_ptr->expfact / 100L))) { /* Lose a level */ p_ptr->lev--; lite_spot(p_ptr->px, p_ptr->py); /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Handle stuff */ handle_stuff(); } /* Gain levels while possible */ while ((p_ptr->lev < PY_MAX_LEVEL) && (p_ptr->exp >= (player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L))) { /* Gain a level */ p_ptr->lev++; lite_spot(p_ptr->px, p_ptr->py); /* * If auto-note taking enabled, write a note to the file. * Only write this note when the level is gained for the first time. */ if (take_notes && auto_notes && (p_ptr->lev > p_ptr->max_lev)) { /* Write note */ add_note('L', "Reached level %d", p_ptr->lev); } /* Save the highest level */ if (p_ptr->lev > p_ptr->max_lev) { int vir; for (vir = 0; vir < MAX_PLAYER_VIRTUES; vir++) p_ptr->virtues[vir] = p_ptr->virtues[vir] + 1; if (FLAG(p_ptr, TR_MUTATE)) { if (one_in_(5)) level_mutation = TRUE; } p_ptr->max_lev = p_ptr->lev; if ((FLAG(p_ptr, TR_PATRON)) || (one_in_(7) && (FLAG(p_ptr, TR_STRANGE_LUCK)))) { level_reward = TRUE; } } /* Sound */ sound(SOUND_LEVEL); /* Message */ msgf(MSGT_LEVEL, "Welcome to level %d.", p_ptr->lev); msg_effect(MSG_LEVEL, p_ptr->lev); /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP); /* Window stuff */ p_ptr->window |= (PW_PLAYER | PW_SPELL); /* Handle stuff */ handle_stuff(); if (level_reward) { if (!multi_rew) { gain_level_reward(0); multi_rew = TRUE; level_reward = FALSE; } } if (level_mutation) { msgf("You feel different..."); (void)gain_mutation(0); level_mutation = FALSE; } } } /* * Hack -- Return the "automatic coin type" of a monster race * Used to allocate proper treasure when "Creeping coins" die * * XXX XXX XXX Note the use of actual "monster names" */ static int get_coin_type(const monster_race *r_ptr) { cptr name = mon_race_name(r_ptr); /* Analyze "coin" monsters */ if (r_ptr->d_char == '$') { /* Look for textual clues */ if (strstr(name, " copper ")) return (2); if (strstr(name, " silver ")) return (5); if (strstr(name, " gold ")) return (10); if (strstr(name, " mithril ")) return (16); if (strstr(name, " adamantite ")) return (17); /* Look for textual clues */ if (strstr(name, "Copper ")) return (2); if (strstr(name, "Silver ")) return (5); if (strstr(name, "Gold ")) return (10); if (strstr(name, "Mithril ")) return (16); if (strstr(name, "Adamantite ")) return (17); } /* Assume nothing */ return (0); } /* * Handle the "death" of a monster. * * Disperse treasures centered at the monster location based on the * various flags contained in the monster flags fields. * * Check for "Quest" completion when a quest monster is killed. * * Note that monsters can now carry objects, and when a monster dies, * it drops all of its objects, which may disappear in crowded rooms. * * Hack - monsters only explode if explode is TRUE. */ bool monster_death(int m_idx, bool explode) { int i, j, y, x; int dump_item = 0; int dump_gold = 0; int number = 0; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; bool visible = (m_ptr->ml || (FLAG(r_ptr, RF_UNIQUE))); bool good = FLAG(r_ptr, RF_DROP_GOOD); bool great = FLAG(r_ptr, RF_DROP_GREAT); bool do_gold = (!(FLAG(r_ptr, RF_ONLY_ITEM))); bool do_item = (!(FLAG(r_ptr, RF_ONLY_GOLD))); bool cloned = FALSE; bool dropped_corpse = FALSE; int force_coin = get_coin_type(r_ptr); object_type *q_ptr; field_type *f_ptr; int level; /* Notice changes in view */ if (FLAG(r_ptr, RF_LITE_1) || FLAG(r_ptr, RF_LITE_2)) { /* Update some things */ p_ptr->update |= (PU_MON_LITE); } /* Get the location */ y = m_ptr->fy; x = m_ptr->fx; if (m_ptr->smart & SM_CLONED) cloned = TRUE; /* Let monsters explode! */ for (i = 0; i < 4; i++) { if ((r_ptr->blow[i].method == RBM_EXPLODE) && explode) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; int typ = GF_MISSILE; int d_dice = r_ptr->blow[i].d_dice; int d_side = r_ptr->blow[i].d_side; int damage = damroll(d_dice, d_side); /* ToDo: Apply the correct effects */ switch (r_ptr->blow[i].effect) { case RBE_HURT: { typ = GF_MISSILE; break; } case RBE_POISON: { typ = GF_POIS; break; } case RBE_UN_BONUS: { typ = GF_DISENCHANT; break; } case RBE_UN_POWER: { typ = GF_MISSILE; break; } case RBE_EAT_GOLD: { typ = GF_MISSILE; break; } case RBE_EAT_ITEM: { typ = GF_MISSILE; break; } case RBE_EAT_FOOD: { typ = GF_MISSILE; break; } case RBE_EAT_LITE: { typ = GF_MISSILE; break; } case RBE_ACID: { typ = GF_ACID; break; } case RBE_ELEC: { typ = GF_ELEC; break; } case RBE_FIRE: { typ = GF_FIRE; break; } case RBE_COLD: { typ = GF_COLD; break; } case RBE_BLIND: { /* Hack - for Yellow light */ typ = GF_LITE; break; } case RBE_CONFUSE: { typ = GF_CONFUSION; break; } case RBE_TERRIFY: { typ = GF_MISSILE; break; } case RBE_PARALYZE: { typ = GF_MISSILE; break; } case RBE_LOSE_STR: { typ = GF_MISSILE; break; } case RBE_LOSE_DEX: { typ = GF_MISSILE; break; } case RBE_LOSE_CON: { typ = GF_MISSILE; break; } case RBE_LOSE_INT: { typ = GF_MISSILE; break; } case RBE_LOSE_WIS: { typ = GF_MISSILE; break; } case RBE_LOSE_CHR: { typ = GF_MISSILE; break; } case RBE_LOSE_ALL: { typ = GF_MISSILE; break; } case RBE_SHATTER: { typ = GF_ROCKET; break; } case RBE_EXP_10: { typ = GF_MISSILE; break; } case RBE_EXP_20: { typ = GF_MISSILE; break; } case RBE_EXP_40: { typ = GF_MISSILE; break; } case RBE_EXP_80: { typ = GF_MISSILE; break; } case RBE_DISEASE: { typ = GF_POIS; break; } case RBE_TIME: { typ = GF_TIME; break; } case RBE_EXP_VAMP: { typ = GF_MISSILE; break; } } (void)project(m_idx, 3, x, y, damage, typ, flg); break; } } /* Complete quests */ if (FLAG(r_ptr, RF_UNIQUE)) { trigger_quest_complete(QX_KILL_UNIQUE, (vptr)m_ptr); } else { trigger_quest_complete(QX_KILL_MONST, (vptr)m_ptr); } /* Hack XXX XXX - trigger on killing winner */ if ((m_ptr->r_idx == QW_OBERON) || (m_ptr->r_idx == QW_SERPENT)) { trigger_quest_complete(QX_KILL_WINNER, (vptr)m_ptr); } /* Hack: Do not drop a corpse in a random quest. */ if ((one_in_(FLAG(r_ptr, RF_UNIQUE) ? 1 : 2) && ((FLAG(r_ptr, RF_DROP_CORPSE)) || (FLAG(r_ptr, RF_DROP_SKELETON)))) && !(FLAG(r_ptr, RF_QUESTOR))) { /* Assume skeleton */ bool corpse = FALSE; /* * We cannot drop a skeleton? Note, if we are in this check, * we *know* we can drop at least a corpse or a skeleton */ if (!(FLAG(r_ptr, RF_DROP_SKELETON))) corpse = TRUE; /* Else, a corpse is more likely unless we did a "lot" of damage */ else if (FLAG(r_ptr, RF_DROP_CORPSE)) { /* Lots of damage in one blow */ if ((0 - ((m_ptr->maxhp) / 4)) > m_ptr->hp) { if (one_in_(3)) corpse = TRUE; } else { if (!one_in_(3)) corpse = TRUE; } } /* Hack - corpses only appear on certain floors */ if (cave_clean_grid(area(x, y))) { if (corpse) { /* Make a corpse */ f_ptr = place_field(x, y, FT_CORPSE); if (f_ptr) { /* Initialise it */ (void)field_script_single(f_ptr, FIELD_ACT_INIT, "i:", LUA_VAR_NAMED(m_ptr->r_idx, "r_idx")); } } else { /* Make a skeleton */ f_ptr = place_field(x, y, FT_SKELETON); if (f_ptr) { /* Initialise it */ (void)field_script_single(f_ptr, FIELD_ACT_INIT, "i:", LUA_VAR_NAMED(m_ptr->r_idx, "r_idx")); } } /* We dropped a corpse */ dropped_corpse = TRUE; } } /* Drop objects being carried */ drop_object_list(&m_ptr->hold_o_idx, m_ptr->fx, m_ptr->fy); /* * Mega^3-hack: killing a 'Warrior of the Dawn' is likely to * spawn another in the fallen one's place! */ if (mon_name_cont(r_ptr, "the Dawn")) { if (!one_in_(20)) { int wy = y, wx = x; int attempts = 100; bool pet = is_pet(m_ptr); do { scatter(&wx, &wy, x, y, 20); } while (!(in_bounds2(wx, wy) && cave_floor_grid(area(wx, wy))) && --attempts); if (attempts > 0) { if (summon_specific((pet ? -1 : 0), wx, wy, 100, SUMMON_DAWN, FALSE, is_friendly(m_ptr), pet)) { if (player_can_see_bold(wx, wy)) msgf("A new warrior steps forth!"); } } } } /* Pink horrors are replaced with 2 Blue horrors */ else if (mon_name_cont(r_ptr, "ink horror")) { bool notice = FALSE; for (i = 0; i < 2; i++) { int wy = y, wx = x; bool pet = is_pet(m_ptr); if (summon_specific((pet ? -1 : 0), wx, wy, 100, SUMMON_BLUE_HORROR, FALSE, is_friendly(m_ptr), pet)) { if (player_can_see_bold(wx, wy)) notice = TRUE; } } if (notice) msgf("The Pink horror divides!"); } /* One more ultra-hack: An Unmaker goes out with a big bang! */ else if (mon_name_cont(r_ptr, "Unmaker") && explode) { u16b flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; (void)project(m_idx, 6, x, y, 100, GF_CHAOS, flg); } /* Bloodletters of Khorne may drop a blade of chaos */ else if (mon_name_cont(r_ptr, "Bloodletter") && (randint1(100) < 15)) { /* Prepare to make a Blade of Chaos */ q_ptr = object_prep(lookup_kind(TV_SWORD, SV_BLADE_OF_CHAOS)); apply_magic(q_ptr, base_level(), 0, 0); /* Drop it in the dungeon */ drop_near(q_ptr, -1, x, y); } /* Mega^2-hack -- Get a t-shirt from our first Greater Hell-beast kill */ else if (!r_ptr->r_pkills && mon_name_cont(r_ptr, "Greater hell-beast")) { /* Prepare to make the T-shirt */ q_ptr = object_prep(lookup_kind(TV_SOFT_ARMOR, SV_T_SHIRT)); /* Mega-Hack -- Name the shirt */ q_ptr->xtra_name = quark_add ("'I killed the GHB and all I got was this lousy t-shirt!'"); q_ptr->flags[2] |= (TR2_IGNORE_ACID | TR2_IGNORE_ELEC | TR2_IGNORE_FIRE | TR2_IGNORE_COLD); /* Drop it in the dungeon */ drop_near(q_ptr, -1, x, y); } /* Mega-Hack -- drop "winner" treasures */ else if (FLAG(r_ptr, RF_DROP_CHOSEN)) { if (mon_name_cont(r_ptr, "Serpent of Chaos")) { /* Make Grond */ create_named_art(ART_GROND, x, y); /* Make Crown of Morgoth */ create_named_art(ART_MORGOTH, x, y); } else if (mon_name_cont(r_ptr, "Stormbringer")) { /* Create the artifact */ create_named_art(ART_STORMBRINGER, x, y); /* The artifact has been created */ a_info[ART_STORMBRINGER].cur_num = 1; } else { byte a_idx = 0; int chance = 0; if (mon_name_cont(r_ptr, "Oberon,")) { if (one_in_(3)) { a_idx = ART_THRAIN; chance = 33; } else { a_idx = ART_GONDOR; chance = 50; } } else if (mon_name_cont(r_ptr, "Barimen")) { a_idx = ART_THRAIN; chance = 20; } else if (mon_name_cont(r_ptr, "Sauron,")) { a_idx = ART_POWER; chance = 25; } else if (mon_name_cont(r_ptr, "Brand, ")) { if (one_in_(3)) { a_idx = ART_ANGUIREL; chance = 33; } else { a_idx = ART_BRAND; chance = 25; } } else if (mon_name_cont(r_ptr, "Corwin,")) { if (one_in_(3)) { a_idx = ART_CORWIN; chance = 33; } else { a_idx = ART_GRAYSWANDIR; chance = 33; } } else if (mon_name_cont(r_ptr, "Saruman of")) { a_idx = ART_ELENDIL; chance = 20; } else if (mon_name_cont(r_ptr, "Fiona the")) { a_idx = ART_BELANGIL; chance = 50; } else if (mon_name_cont(r_ptr, "Julian, ")) { a_idx = ART_CELEBORN; chance = 45; } else if (mon_name_cont(r_ptr, "Klings")) { a_idx = ART_OROME; chance = 40; } else if (mon_name_cont(r_ptr, "Groo")) { a_idx = ART_GROO; chance = 75; } else if (mon_name_cont(r_ptr, "Hagen,")) { a_idx = ART_NIMLOTH; chance = 66; } else if (mon_name_cont(r_ptr, "Caine,")) { a_idx = ART_ANGRIST; chance = 50; } if ((a_idx > 0) && ((randint1(99) < chance) || (p_ptr->state.wizard))) { if (a_info[a_idx].cur_num == 0) { /* Create the artifact */ create_named_art(a_idx, x, y); /* The artifact has been created */ a_info[a_idx].cur_num = 1; } } } } /* Determine how much we can drop */ if ((FLAG(r_ptr, RF_DROP_60)) && (randint0(100) < 60)) number++; if ((FLAG(r_ptr, RF_DROP_90)) && (randint0(100) < 90)) number++; if (FLAG(r_ptr, RF_DROP_1D2)) number += damroll(1, 2); if (FLAG(r_ptr, RF_DROP_2D2)) number += damroll(2, 2); if (FLAG(r_ptr, RF_DROP_3D2)) number += damroll(3, 2); if (FLAG(r_ptr, RF_DROP_4D2)) number += damroll(4, 2); if (cloned) number = 0; /* Clones drop no stuff */ /* Average dungeon and monster levels */ if (p_ptr->depth) level = (p_ptr->depth + r_ptr->level) / 2; else level = r_ptr->level; /* Drop some objects */ for (j = 0; j < number; j++) { /* Make Gold */ if (do_gold && (!do_item || one_in_(2))) { /* Make some gold */ q_ptr = make_gold(level, force_coin); /* XXX XXX XXX */ dump_gold++; } /* Make Object */ else { u16b delta_level = (good ? 15 : 0) + (great ? 15 : 0); for (i = 0; i < 1000; i++) { /* Make an object */ q_ptr = make_object(level, delta_level, &r_ptr->obj_drop); if (!q_ptr) continue; /* "Good" and "great" drops must not be worthless */ if (good || great) { if (cursed_p(q_ptr)) continue; if (object_value_real(q_ptr) <= 0) continue; } break; } /* Paranoia */ if (!q_ptr) continue; /* XXX XXX XXX */ dump_item++; } /* Drop it in the dungeon */ drop_near(q_ptr, -1, x, y); } /* Take note of any dropped treasure */ if (visible && (dump_item || dump_gold)) { /* Take notes on treasure */ lore_treasure(m_idx, dump_item, dump_gold); } /* Return TRUE if we dropped a corpse for the player to see */ return (dropped_corpse); } /* * Modify the physical damage done to the monster. * (for example when it's invulnerable or shielded) * * ToDo: Accept a damage-type to calculate the modified damage from * things like fire, frost, lightning, poison, ... attacks. * * "type" is not yet used and should be 0. */ int mon_damage_mod(const monster_type *m_ptr, int dam, int type) { /* Hack - ignore type for now */ (void)type; if (m_ptr->invulner && !one_in_(PENETRATE_INVULNERABILITY)) return (0); else return (dam); } /* * This function calculates the experience gained for killing a monster. */ void exp_for_kill(const monster_race *r_ptr, s32b *new_exp, s32b *new_exp_frac) { s32b div, exp; if (r_ptr->mexp) { div = p_ptr->lev; exp = r_ptr->mexp; /* calculate the integer exp part */ *new_exp = ((long)exp / div); /* Handle fractional experience */ *new_exp_frac = ((long)(exp % div) * 0x10000L / div); } else { *new_exp = 0; *new_exp_frac = 0; } } /* * Decreases monsters hit points, handling monster death. * * We return TRUE if the monster has been killed (and deleted). * * We announce monster death (using an optional "death message" * if given, and a otherwise a generic killed/destroyed message). * * Only "physical attacks" can induce the "You have slain" message. * Missile and Spell attacks will induce the "dies" message, or * various "specialized" messages. Note that "You have destroyed" * and "is destroyed" are synonyms for "You have slain" and "dies". * * Hack -- unseen monsters yield "You have killed it." message. * * Added fear (DGK) and check whether to print fear messages -CWS * * Made name, sex, and capitalization generic -BEN- * * As always, the "ghost" processing is a total hack. * * Hack -- we "delay" fear messages by passing around a "fear" flag. */ bool mon_take_hit(int m_idx, int dam, bool *fear, cptr note) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; s32b new_exp, new_exp_frac; /* Innocent until proven otherwise */ bool innocent = TRUE, thief = FALSE; bool corpse = FALSE; bool visible = FALSE; int i; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Wake it up */ m_ptr->csleep = 0; /* Hurt it */ m_ptr->hp -= dam; /* It is dead now */ if (m_ptr->hp < 0) { char m_name[80]; /* Extract monster name */ monster_desc(m_name, m_ptr, 0, 80); if (FLAG(r_ptr, RF_CAN_SPEAK)) { char line_got[1024]; if (speak_unique) { /* Dump a message */ if (!get_rnd_line("mondeath.txt", m_ptr->r_idx, line_got)) msgf("%^s says: %s", m_name, line_got); } if ((FLAG(r_ptr, RF_UNIQUE)) && one_in_(REWARD_CHANCE) && !(FLAG(r_ptr, RF_FRIENDLY))) { if (!get_rnd_line("crime.txt", m_ptr->r_idx, line_got)) { int reward = 250 * (randint1(10) + r_ptr->level - 5); /* Force 'good' values */ if (reward > 32000) reward = 32000; else if (reward < 250) reward = 250; msgf("There was a price on %s's head.", m_name); msgf("%^s was wanted for %s", m_name, line_got); msgf("You collect a reward of %d gold pieces.", reward); p_ptr->au += reward; p_ptr->redraw |= (PR_GOLD); chg_virtue(V_JUSTICE, 5); } } } if (r_ptr->level > p_ptr->depth) { if (randint1(10) <= (p_ptr->depth - r_ptr->level)) chg_virtue(V_VALOUR, 1); } if (r_ptr->level >= 2 * (p_ptr->lev)) chg_virtue(V_VALOUR, 1); if ((FLAG(r_ptr, RF_UNIQUE)) && ((FLAG(r_ptr, RF_EVIL)) || (FLAG(r_ptr, RF_GOOD)))) chg_virtue(V_HARMONY, 2); if ((FLAG(r_ptr, RF_UNIQUE)) && (FLAG(r_ptr, RF_GOOD))) { chg_virtue(V_UNLIFE, 2); chg_virtue(V_VITALITY, -2); } if ((FLAG(r_ptr, RF_UNIQUE)) && one_in_(3)) chg_virtue(V_INDIVIDUALISM, -1); if (mon_name_cont(r_ptr, "beggar") || mon_name_cont(r_ptr, "leper")) { chg_virtue(V_COMPASSION, -1); } if ((FLAG(r_ptr, RF_GOOD)) && ((r_ptr->level) / 10 + (3 * p_ptr->depth) >= randint1(100))) chg_virtue(V_UNLIFE, 1); /* "Good" angels */ if ((r_ptr->d_char == 'A') && !(FLAG(r_ptr, RF_EVIL))) { if (FLAG(r_ptr, RF_UNIQUE)) chg_virtue(V_FAITH, -2); else if ((r_ptr->level) / 10 + (3 * p_ptr->depth) >= randint1(100)) chg_virtue(V_FAITH, -1); } /* "Evil" angel or a demon (what's the theological difference, anyway...) */ else if ((r_ptr->d_char == 'A') || (FLAG(r_ptr, RF_DEMON))) { if (FLAG(r_ptr, RF_UNIQUE)) chg_virtue(V_FAITH, 2); else if ((r_ptr->level) / 10 + (3 * p_ptr->depth) >= randint1(100)) chg_virtue(V_FAITH, 1); } if ((FLAG(r_ptr, RF_UNDEAD)) && (FLAG(r_ptr, RF_UNIQUE))) chg_virtue(V_VITALITY, 2); if (r_ptr->r_deaths) { if (FLAG(r_ptr, RF_UNIQUE)) { chg_virtue(V_HONOUR, 10); } else if ((r_ptr->level) / 10 + (2 * p_ptr->depth) >= randint1(100)) { chg_virtue(V_HONOUR, 1); } } for (i = 0; i < 4; i++) { if (r_ptr->blow[i].d_dice != 0) innocent = FALSE; /* Murderer! */ if ((r_ptr->blow[i].effect == RBE_EAT_ITEM) || (r_ptr->blow[i].effect == RBE_EAT_GOLD)) thief = TRUE; /* Thief! */ } /* The new law says it is illegal to live in the dungeon */ if (r_ptr->level != 0) innocent = FALSE; if (thief) { if (FLAG(r_ptr, RF_UNIQUE)) chg_virtue(V_JUSTICE, 3); else if (1 + (r_ptr->level / 10 + (2 * p_ptr->depth)) >= randint1(100)) chg_virtue(V_JUSTICE, 1); } else if (innocent) { chg_virtue(V_JUSTICE, -1); } if ((FLAG(r_ptr, RF_ANIMAL)) && !(FLAG(r_ptr, RF_EVIL))) { if (one_in_(3)) chg_virtue(V_NATURE, -1); } /* Make a sound */ sound(SOUND_KILL); /* Death by Missile/Spell attack */ if (note) { msgf(MSGT_KILL, "%^s%s", m_name, note); msg_effect(MSG_KILL, m_ptr->r_idx); } /* Death by physical attack -- invisible monster */ else if (!m_ptr->ml) { msgf(MSGT_KILL, "You have killed %s.", m_name); msg_effect(MSG_KILL, m_ptr->r_idx); } /* Death by Physical attack -- non-living monster */ else if (!monster_living(r_ptr)) { msgf(MSGT_KILL, "You have destroyed %s.", m_name); msg_effect(MSG_KILL, m_ptr->r_idx); } /* Death by Physical attack -- living monster */ else { msgf(MSGT_KILL, "You have slain %s.", m_name); msg_effect(MSG_KILL, m_ptr->r_idx); } /* Get how much the kill was worth */ exp_for_kill(r_ptr, &new_exp, &new_exp_frac); /* Save visibility at death */ visible = m_ptr->ml; /* Generate treasure */ corpse = monster_death(m_idx, TRUE); /* Handle fractional experience */ new_exp_frac += p_ptr->exp_frac; /* Keep track of experience */ if (new_exp_frac >= 0x10000L) { new_exp++; p_ptr->exp_frac = (u16b)(new_exp_frac - 0x10000L); } else { p_ptr->exp_frac = (u16b)new_exp_frac; } /* Gain experience */ gain_exp(new_exp); /* When the player kills a Unique, it stays dead */ if (FLAG(r_ptr, RF_UNIQUE)) r_ptr->max_num = 0; /* * If the player kills a Unique, * and the notes options are on, write a note */ if ((FLAG(r_ptr, RF_UNIQUE)) && take_notes && auto_notes) { /* Get true name even if blinded/hallucinating and write note */ add_note('U', "Killed %s", mon_race_name(r_ptr)); } /* When the player kills a Nazgul, it stays dead */ if (FLAG(r_ptr, RF_UNIQUE_7)) r_ptr->max_num--; /* Recall even invisible uniques or winners */ if (visible || (FLAG(r_ptr, RF_UNIQUE)) || corpse) { /* Count kills this life */ if (r_ptr->r_pkills < MAX_SHORT) r_ptr->r_pkills++; /* Count kills in all lives */ if (r_ptr->r_tkills < MAX_SHORT) r_ptr->r_tkills++; /* Hack -- Auto-recall */ monster_race_track(m_ptr->r_idx); } /* Don't kill Amberites */ if ((FLAG(r_ptr, RF_AMBERITE)) && one_in_(2)) { int curses = rand_range(2, 4); bool stop_ty = FALSE; int count = 0; msgf("%^s puts a terrible blood curse on you!", m_name); curse_equipment(100, 50); do { stop_ty = activate_ty_curse(stop_ty, &count); } while (--curses); } /* Delete the monster */ delete_monster_idx(m_idx); /* Not afraid */ (*fear) = FALSE; /* Monster is dead */ return (TRUE); } /* Mega-Hack -- Pain cancels fear */ if (m_ptr->monfear && (dam > 0)) { int tmp = randint1(dam); /* Cure a little fear */ if (tmp < m_ptr->monfear) { /* Reduce fear */ m_ptr->monfear -= tmp; } /* Cure all the fear */ else { /* Cure fear */ m_ptr->monfear = 0; /* No more fear */ (*fear) = FALSE; } } /* Sometimes a monster gets scared by damage */ if (!m_ptr->monfear && !FLAG(r_ptr, RF_NO_FEAR) && (dam > 0)) { int percentage; /* Percentage of fully healthy */ percentage = (100L * m_ptr->hp) / m_ptr->maxhp; /* * Run (sometimes) if at 10% or less of max hit points, * or (usually) when hit for half its current hit points */ if ((randint1(10) >= percentage) || ((dam >= m_ptr->hp) && (randint0(100) < 80))) { /* Hack -- note fear */ (*fear) = TRUE; /* XXX XXX XXX Hack -- Add some timed fear */ m_ptr->monfear = (randint1(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5))); } } /* Not dead yet */ return (FALSE); } /* * Get size of map on screen */ void get_map_size(int *x, int *y) { int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Offset */ hgt -= ROW_MAP + 1; wid -= COL_MAP + 1; /* Hack - bigmap has half resolution */ if (use_bigtile) wid = wid / 2; /* Return values */ *x = wid; *y = hgt; } /* * Get panel max bounds * where (x, y) is a provisional top left corner. */ static bool panel_bounds(int x, int y, int wid, int hgt) { int xmax, ymax; /* Hack - vanilla town is special */ if (vanilla_town && (!p_ptr->depth) && !use_bigtile) { /* Same bounds all the time */ x = max_wild * WILD_BLOCK_SIZE / 2 - wid / 2 - 15; y = max_wild * WILD_BLOCK_SIZE / 2 - hgt / 2 - 5; } else { /* Bounds */ if (y > p_ptr->max_hgt - hgt) y = p_ptr->max_hgt - hgt; if (y < p_ptr->min_hgt) y = p_ptr->min_hgt; if (x > p_ptr->max_wid - wid) x = p_ptr->max_wid - wid; if (x < p_ptr->min_wid) x = p_ptr->min_wid; } xmax = x + wid; ymax = y + hgt; /* Handle "changes" */ if ((x != p_ptr->panel_x1) || (y != p_ptr->panel_y1) || (xmax != p_ptr->panel_x2) || (ymax != p_ptr->panel_y2)) { /* Save the new panel info */ p_ptr->panel_x1 = x; p_ptr->panel_y1 = y; p_ptr->panel_x2 = xmax; p_ptr->panel_y2 = ymax; /* Update stuff */ p_ptr->update |= (PU_MONSTERS); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Handle stuff */ handle_stuff(); return (TRUE); } return (FALSE); } /* * Handle a request to change the current panel * * Return TRUE if the panel was changed. * * Also used in do_cmd_locate */ bool change_panel(int dx, int dy) { int x, y; int wid, hgt; get_map_size(&wid, &hgt); /* Apply the motion */ x = p_ptr->panel_x1 + dx * (wid / 2); y = p_ptr->panel_y1 + dy * (hgt / 2); /* Get new bounds */ return (panel_bounds(x, y, wid, hgt)); } /* * Recenter the map around the player as required */ void verify_panel(void) { int wid, hgt; int x, y; int nx, ny; get_map_size(&wid, &hgt); if (center_player && !(avoid_center && p_ptr->state.running)) { /* Center it */ x = p_ptr->px - wid / 2; y = p_ptr->py - hgt / 2; } else { /* Get new 'best value' */ x = p_ptr->panel_x1; y = p_ptr->panel_y1; nx = p_ptr->px - wid / 2; ny = p_ptr->py - hgt / 2; /* How far to move panel? */ if (abs(nx - x) > wid / 4) x = nx; if (abs(ny - y) > hgt / 4) y = ny; } /* Get new bounds */ if (panel_bounds(x, y, wid, hgt)) { /* Hack -- optional disturb on "panel change" */ if (disturb_panel && !center_player) disturb(FALSE); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff */ handle_stuff(); } } /* * Center the dungeon display around the given square */ bool panel_center(int x, int y) { int wid, hgt; /* Get size */ get_map_size(&wid, &hgt); x -= wid / 2; y -= hgt / 2; /* How far to move panel? */ if (abs(p_ptr->panel_x1 - x) < wid / 4) x = p_ptr->panel_x1; if (abs(p_ptr->panel_y1 - y) < hgt / 4) y = p_ptr->panel_y1; /* Get new bounds */ if (panel_bounds(x, y, wid, hgt)) { /* Window stuff */ p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON); /* Handle stuff */ handle_stuff(); return (TRUE); } return (FALSE); } static int map_wid_old = 66; void map_panel_size(void) { int wid, hgt; /* Only if the map exists */ if (!character_dungeon) return; /* Hack - wait until are done with menus before updating */ if (character_icky) { p_ptr->update |= PU_MAP; return; } /* Get size */ get_map_size(&wid, &hgt); /* Set bigreion if required */ if (use_bigtile) { Term_bigregion(COL_MAP, ROW_MAP, ROW_MAP + hgt - 1); } /* Kill previous size of line */ /* String of terrain characters along one row of the map */ if (mp_ta) KILL(mp_ta); if (mp_tc) KILL(mp_tc); /* String of characters along one row of the map */ if (mp_a) KILL(mp_a); if (mp_c) KILL(mp_c); /* Save size */ map_wid_old = wid; /* Make the new lines */ /* String of terrain characters along one row of the map */ C_MAKE(mp_ta, wid, byte); C_MAKE(mp_tc, wid, char); /* String of characters along one row of the map */ C_MAKE(mp_a, wid, byte); C_MAKE(mp_c, wid, char); /* Verify the panel */ verify_panel(); } /* * Monster health description */ cptr look_mon_desc(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; bool living; int perc; /* Determine if the monster is "living" */ living = monster_living(r_ptr); /* Healthy monsters */ if (m_ptr->hp >= m_ptr->maxhp) { /* No damage */ return (living ? "unhurt" : "undamaged"); } /* Calculate a health "percentage" */ perc = 100L * m_ptr->hp / m_ptr->maxhp; if (perc >= 60) { return (living ? "somewhat wounded" : "somewhat damaged"); } if (perc >= 25) { return (living ? "wounded" : "damaged"); } if (perc >= 10) { return (living ? "badly wounded" : "badly damaged"); } return (living ? "almost dead" : "almost destroyed"); } /* * Angband sorting algorithm -- quick sort in place * * Note that the details of the data we are sorting is hidden, * and we rely on the "ang_sort_comp()" and "ang_sort_swap()" * function hooks to interact with the data, which is given as * two pointers, and which may have any user-defined form. */ void ang_sort_aux(vptr u, vptr v, int p, int q) { int z, a, b; /* Done sort */ if (p >= q) return; /* Pivot */ z = p; /* Begin */ a = p; b = q; /* Partition */ while (TRUE) { /* Slide i2 */ while (!(*ang_sort_comp) (u, v, b, z)) b--; /* Slide i1 */ while (!(*ang_sort_comp) (u, v, z, a)) a++; /* Done partition */ if (a >= b) break; /* Swap */ (*ang_sort_swap) (u, v, a, b); /* Advance */ a++, b--; } /* Recurse left side */ ang_sort_aux(u, v, p, b); /* Recurse right side */ ang_sort_aux(u, v, b + 1, q); } /* * Angband sorting algorithm -- quick sort in place * * Note that the details of the data we are sorting is hidden, * and we rely on the "ang_sort_comp()" and "ang_sort_swap()" * function hooks to interact with the data, which is given as * two pointers, and which may have any user-defined form. */ void ang_sort(vptr u, vptr v, int n) { /* Sort the array */ ang_sort_aux(u, v, 0, n - 1); } /*** Targeting Code ***/ /* * Track a new monster */ void health_track(int m_idx) { /* Track a new guy */ p_ptr->health_who = m_idx; /* Redraw (later) */ p_ptr->redraw |= (PR_HEALTH); } /* * Hack -- track the given monster race */ void monster_race_track(int r_idx) { /* Save this monster ID */ p_ptr->monster_race_idx = r_idx; /* Window stuff */ p_ptr->window |= (PW_MONSTER); } /* * Hack -- track the given object kind */ void object_kind_track(int k_idx) { /* Save this monster ID */ p_ptr->object_kind_idx = k_idx; /* Window stuff */ p_ptr->window |= (PW_OBJECT); } /* * Determine is a monster makes a reasonable target * * The concept of "targeting" was stolen from "Morgul" (?) * * The player can target any location, or any "target-able" monster. * * Currently, a monster is "target_able" if it is visible, and if * the player can hit it with a projection, and the player is not * hallucinating. This allows use of "use closest target" macros. */ bool target_able(int m_idx) { int py = p_ptr->py; int px = p_ptr->px; monster_type *m_ptr = &m_list[m_idx]; /* Monster must be alive */ if (!m_ptr->r_idx) return (FALSE); /* Monster must be visible */ if (!m_ptr->ml) return (FALSE); /* Monster must not be a mimic */ if (m_ptr->smart & SM_MIMIC) return (FALSE); /* Monster must be projectable */ if (!projectable(px, py, m_ptr->fx, m_ptr->fy)) return (FALSE); /* Hack -- no targeting hallucinations */ if (p_ptr->tim.image) return (FALSE); /* Assume okay */ return (TRUE); } /* * Hack - function to get object name of mimic */ static bool mimic_desc(char *m_name, const monster_race *r_ptr) { /* Hack - look at default character */ switch (r_ptr->d_char) { case '$': { /* XXX XXX XXX Mega-Hack */ strcpy(m_name, mon_race_name(r_ptr) + sizeof("Creeping ") - 1); return (TRUE); } case '|': { /* Hack */ strcpy(m_name, mon_race_name(r_ptr)); return (TRUE); } case '?': { if (mon_name_cont(r_ptr, "Tome ")) { strcpy(m_name, "tome"); } else { strcpy(m_name, "scroll"); } return (TRUE); } case '!': { strcpy(m_name, "potion"); return (TRUE); } case '=': { strcpy(m_name, "ring"); return (TRUE); } case '+': { strcpy(m_name, "door"); return (TRUE); } case '&': { strcpy(m_name, "chest"); return (TRUE); } case '(': { strcpy(m_name, "cloak"); return (TRUE); } case '>': { strcpy(m_name, "down staircase"); return (TRUE); } case '.': { /* Hack - do not notice lurkers etc. */ return (FALSE); } case '#': { strcpy(m_name, "granite wall"); return (TRUE); } default: { return (TRUE); } } } /* * Update (if necessary) and verify (if possible) the target. * * We return TRUE if the target is "okay" and FALSE otherwise. */ bool target_okay(void) { /* Accept stationary targets */ if (p_ptr->target_who < 0) return (TRUE); /* Check moving targets */ if (p_ptr->target_who > 0) { /* Accept reasonable targets */ if (target_able(p_ptr->target_who)) { monster_type *m_ptr = &m_list[p_ptr->target_who]; /* Acquire monster location */ p_ptr->target_row = m_ptr->fy; p_ptr->target_col = m_ptr->fx; /* Good target */ return (TRUE); } } /* Assume no target */ return (FALSE); } /* * Sorting hook -- comp function -- by "distance to player" * * We use "u" and "v" to point to arrays of "x" and "y" positions, * and sort the arrays by double-distance to the player. */ static bool ang_sort_comp_distance(vptr u, vptr v, int a, int b) { int py = p_ptr->py; int px = p_ptr->px; s16b *x = (s16b *)(u); s16b *y = (s16b *)(v); int da, db, kx, ky; /* Absolute distance components */ kx = x[a]; kx -= px; kx = ABS(kx); ky = y[a]; ky -= py; ky = ABS(ky); /* Approximate Double Distance to the first point */ da = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx)); /* Absolute distance components */ kx = x[b]; kx -= px; kx = ABS(kx); ky = y[b]; ky -= py; ky = ABS(ky); /* Approximate Double Distance to the first point */ db = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx)); /* Compare the distances */ return (da <= db); } /* * Sorting hook -- swap function -- by "distance to player" * * We use "u" and "v" to point to arrays of "x" and "y" positions, * and sort the arrays by distance to the player. */ static void ang_sort_swap_distance(vptr u, vptr v, int a, int b) { s16b *x = (s16b *)(u); s16b *y = (s16b *)(v); s16b temp; /* Swap "x" */ temp = x[a]; x[a] = x[b]; x[b] = temp; /* Swap "y" */ temp = y[a]; y[a] = y[b]; y[b] = temp; } /* * Hack -- help "select" a location (see below) */ static s16b target_pick(int x1, int y1, int dx, int dy) { int i, v; int x2, y2, x3, y3, x4, y4; int b_i = -1, b_v = 9999; /* Scan the locations */ for (i = 0; i < temp_n; i++) { /* Point 2 */ x2 = temp_x[i]; y2 = temp_y[i]; /* Directed distance */ x3 = (x2 - x1); y3 = (y2 - y1); /* Verify quadrant */ if (dx && (x3 * dx <= 0)) continue; if (dy && (y3 * dy <= 0)) continue; /* Absolute distance */ x4 = ABS(x3); y4 = ABS(y3); /* Verify quadrant */ if (dy && !dx && (x4 > y4)) continue; if (dx && !dy && (y4 > x4)) continue; /* Approximate Double Distance */ v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4)); /* XXX XXX XXX Penalize location */ /* Track best */ if ((b_i >= 0) && (v >= b_v)) continue; /* Track best */ b_i = i; b_v = v; } /* Result */ return (b_i); } /* * Hack -- determine if a given location is "interesting" */ static bool target_set_accept(int x, int y) { int px = p_ptr->px; int py = p_ptr->py; cave_type *c_ptr; pcave_type *pc_ptr; object_type *o_ptr; field_type *f_ptr; byte feat; /* Player grid is always interesting */ if ((y == py) && (x == px)) return (TRUE); /* Handle hallucination */ if (p_ptr->tim.image) return (FALSE); /* paranoia */ if (!in_boundsp(x, y)) return (FALSE); /* Examine the grid */ c_ptr = area(x, y); pc_ptr = parea(x, y); /* Visible monsters */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; /* Visible monsters */ if (m_ptr->ml) return (TRUE); } /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Memorized object */ if (o_ptr->info & OB_SEEN) return (TRUE); } OBJ_ITT_END; /* Scan all fields in the grid */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { /* Memorized , lookable field */ if ((f_ptr->info & (FIELD_INFO_MARK | FIELD_INFO_NO_LOOK)) == FIELD_INFO_MARK) return (TRUE); } FLD_ITT_END; /* Interesting memorized features */ feat = pc_ptr->feat; /* Notice the Pattern */ if (cave_pattern_grid(pc_ptr)) return (TRUE); /* Notice doors */ if (feat == FEAT_OPEN) return (TRUE); if (feat == FEAT_BROKEN) return (TRUE); /* Notice stairs */ if (feat == FEAT_LESS) return (TRUE); if (feat == FEAT_MORE) return (TRUE); /* Notice doors */ if (feat == FEAT_CLOSED) return (TRUE); /* Notice veins with treasure */ if (feat == FEAT_MAGMA_K) return (TRUE); if (feat == FEAT_QUARTZ_K) return (TRUE); /* Nope */ return (FALSE); } /* * Prepare the "temp" array for "target_set" * * Return the number of target_able monsters in the set. */ static void target_set_prepare(int mode) { int y, x; /* Reset "temp" array */ temp_n = 0; /* Scan the current panel */ for (y = p_ptr->panel_y1; y <= p_ptr->panel_y2; y++) { for (x = p_ptr->panel_x1; x <= p_ptr->panel_x2; x++) { cave_type *c_ptr; if (!in_bounds2(x, y)) continue; c_ptr = area(x, y); /* Require "interesting" contents */ if (!target_set_accept(x, y)) continue; /* Require target_able monsters for "TARGET_KILL" */ if ((mode & (TARGET_KILL)) && !target_able(c_ptr->m_idx)) continue; /* Require hostile creatures if "TARGET_HOST" is used */ if ((mode & (TARGET_HOST)) && !is_hostile(&m_list[c_ptr->m_idx])) continue; /* Do not target unknown mimics if we want monsters */ if ((mode & (TARGET_KILL | TARGET_HOST)) && (m_list[c_ptr->m_idx].smart & SM_MIMIC)) { continue; } /* Paranoia */ if (temp_n >= TEMP_MAX) continue; /* Save the location */ temp_x[temp_n] = x; temp_y[temp_n] = y; temp_n++; } } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_distance; ang_sort_swap = ang_sort_swap_distance; /* Sort the positions */ ang_sort(temp_x, temp_y, temp_n); } /* * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * This function must handle blindness/hallucination. */ static int target_set_aux(int x, int y, int mode, cptr info) { int py = p_ptr->py; int px = p_ptr->px; cave_type *c_ptr = area(x, y); pcave_type *pc_ptr = parea(x, y); cptr s1, s2, s3; bool boring; bool seen = FALSE; int feat; int query; object_type *o_ptr; field_type *f_ptr; /* Repeat forever */ while (1) { /* Paranoia */ query = ' '; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* Hack -- under the player */ if ((y == py) && (x == px)) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hack -- hallucination */ if (p_ptr->tim.image) { cptr name = "something strange"; /* Display a message */ prtf(0, 0, "%s%s%ssomething strange [%s]", s1, s2, s3, name, info); move_cursor_relative(x, y); query = inkey(); /* Stop on everything but "return" */ if ((query != '\r') && (query != '\n')) break; /* Repeat forever */ continue; } /* Actual monsters */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Visible */ if (m_ptr->ml) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Check for mimics + obtain object description */ if ((m_ptr->smart & SM_MIMIC) && mimic_desc(m_name, r_ptr)) { /* Describe the object */ s3 = "a "; prtf(0, 0, "%s%s%s%s [%s]", s1, s2, s3, m_name, info); move_cursor_relative(x, y); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & TARGET_LOOK)) break; /* Change the intro */ s1 = "It is "; /* Preposition */ s2 = "on "; } /* Normal monsters */ else { /* Hack -- track this monster race */ monster_race_track(m_ptr->r_idx); /* Hack -- health bar for this monster */ health_track(c_ptr->m_idx); /* Hack -- handle stuff */ handle_stuff(); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save */ screen_save(); /* Recall on screen */ screen_roff_mon(m_ptr->r_idx, 0); /* Hack -- Complete the prompt (again) */ roff(" [r,%s]", info); /* Command */ query = inkey(); /* Restore */ screen_load(); } /* Normal */ else { cptr attitude; if (is_pet(m_ptr)) attitude = " (pet) "; else if (is_friendly(m_ptr)) attitude = " (friendly) "; else attitude = " "; /* Describe, and prompt for recall */ prtf(0, 0, "%s%s%s%v (%s)%s[r,%s]", s1, s2, s3, MONSTER_FMT(m_ptr, 0x08), look_mon_desc(c_ptr->m_idx), attitude, info); /* Place cursor */ move_cursor_relative(x, y); /* Command */ query = inkey(); } /* Normal commands */ if (query != 'r') break; /* Toggle recall */ recall = !recall; } /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Hack -- take account of gender */ if (FLAG(r_ptr, RF_FEMALE)) s1 = "She is "; else if (FLAG(r_ptr, RF_MALE)) s1 = "He is "; /* Use a preposition */ s2 = "carrying "; /* Scan all objects being carried */ OBJ_ITT_START (m_ptr->hold_o_idx, o_ptr) { /* Describe the object */ prtf(0, 0, "%s%s%s%v [%s]", s1, s2, s3, OBJECT_FMT(o_ptr, TRUE, 3), info); move_cursor_relative(x, y); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) { return (query); } /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & (TARGET_LOOK))) { return (query); } /* Change the intro */ s2 = "also carrying "; } OBJ_ITT_END; /* Use a preposition */ s2 = "on "; } } } /* Scan all objects in the grid */ if (easy_floor) { int floor_num; object_type *o_ptr = test_floor(&floor_num, c_ptr, 0x02); /* Any items there? */ if (o_ptr) { /* Not boring */ boring = FALSE; while (1) { if (floor_num == 1) { /* Describe the object */ prtf(0, 0, "%s%s%s%v [%s]", s1, s2, s3, OBJECT_FMT(o_ptr, TRUE, 3), info); } else { /* Message */ prtf(0, 0, "%s%s%sa pile of %d items [%c,%s]", s1, s2, s3, floor_num, rogue_like_commands ? 'x' : 'l', info); } move_cursor_relative(x, y); /* Command */ query = inkey(); /* Display list of items (query == "el", not "won") */ if ((floor_num > 1) && (query == (rogue_like_commands ? 'x' : 'l'))) { /* Save screen */ screen_save(); /* Display */ show_list(c_ptr->o_idx, FALSE); /* Prompt */ prtf(0, 0, "Hit any key to continue"); /* Wait */ (void)inkey(); /* Load screen */ screen_load(); } else { /* Stop */ break; } } /* Stop */ break; } } /* Scan all objects in the grid */ OBJ_ITT_START (c_ptr->o_idx, o_ptr) { /* Describe it */ if (o_ptr->info & OB_SEEN) { /* Not boring */ boring = FALSE; /* Describe the object */ prtf(0, 0, "%s%s%s%v [%s]", s1, s2, s3, OBJECT_FMT(o_ptr, TRUE, 3), info); move_cursor_relative(x, y); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) { return (query); } /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & TARGET_LOOK)) { return (query); } /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } OBJ_ITT_END; /* Scan all fields in the grid */ FLD_ITT_START (c_ptr->fld_idx, f_ptr) { field_thaum *t_ptr = &t_info[f_ptr->t_idx]; char fld_name[41]; /* Do not describe this field */ if (f_ptr->info & FIELD_INFO_NO_LOOK) continue; /* Describe if if is visible and known. */ if (f_ptr->info & FIELD_INFO_MARK) { char *name = NULL; /* See if it has a special name */ field_script_single(f_ptr, FIELD_ACT_LOOK, ":s", LUA_RETURN(name)); if (name) { /* Copy the string into the temp buffer */ strncpy(fld_name, name, 40); /* Anything there? */ if (!fld_name[0]) { /* Default to field name */ strncpy(fld_name, t_ptr->name, 40); } /* Free string allocated to hold return value */ string_free(name); } else { /* Default to field name */ strncpy(fld_name, t_ptr->name, 40); } /* Not boring */ boring = FALSE; s3 = is_a_vowel(fld_name[0]) ? "an " : "a "; /* Describe the field */ prtf(0, 0, "%s%s%s%s [%s]", s1, s2, s3, fld_name, info); move_cursor_relative(x, y); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) { return (query); } /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & TARGET_LOOK)) { return (query); } /* Change the intro */ s1 = "It is "; /* Preposition */ s2 = "on "; /* Hack - we've seen a field here */ seen = TRUE; } } FLD_ITT_END; /* Sometimes a field stops the feat from being mentioned */ if (fields_have_flags(c_ptr, FIELD_INFO_NFT_LOOK)) { /* * Only if we know about the field will it stop the * feat from being described. */ /* If we have seen something */ if (seen) { if ((query != '\r') && (query != '\n')) { /* Just exit */ break; } else { /* Back for more */ continue; } } } /* Get memorised terrain feature */ feat = pc_ptr->feat; /* Terrain feature if needed */ if (boring || (feat >= FEAT_OPEN)) { cptr name = f_name + f_info[feat].name; /* Hack -- handle unknown grids */ if (feat == FEAT_NONE) name = "unknown grid"; /* Pick a prefix for the pattern and stairs */ if (*s2 && cave_perma_grid(pc_ptr)) { s2 = "on "; } else if (*s2 && cave_wall_grid(pc_ptr)) { s2 = "in "; } if (f_info[feat].flags & FF_OBJECT) { /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; } else { s3 = ""; } /* Display a message */ if (p_ptr->state.wizard) prtf(0, 0, "%s%s%s%s [%s] (%d:%d)", s1, s2, s3, name, info, y, x); else prtf(0, 0, "%s%s%s%s [%s]", s1, s2, s3, name, info); move_cursor_relative(x, y); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; } /* Stop on everything but "return" */ if ((query != '\r') && (query != '\n')) break; } /* Keep going */ return (query); } /* * Handle "target" and "look". * * Note that this code can be called from "get_aim_dir()". * * All locations must be on the current panel. * * Hack -- targeting/observing an "outer border grid" may induce * problems, so this is not currently allowed. * * The player can use the direction keys to move among "interesting" * grids in a heuristic manner, or the "space", "+", and "-" keys to * move through the "interesting" grids in a sequential manner, or * can enter "location" mode, and use the direction keys to move one * grid at a time in any direction. The "t" (set target) command will * only target a monster (as opposed to a location) if the monster is * target_able and the "interesting" mode is being used. * * The current grid is described using the "look" method above, and * a new command may be entered at any time, but note that if the * "TARGET_LOOK" bit flag is set (or if we are in "location" mode, * where "space" has no obvious meaning) then "space" will scan * through the description of the current grid until done, instead * of immediately jumping to the next "interesting" grid. This * allows the "target" command to retain its old semantics. * * The "*", "+", and "-" keys may always be used to jump immediately * to the next (or previous) interesting grid, in the proper mode. * * The "return" key may always be used to scan through a complete * grid description (forever). * * This command will cancel any old target, even if used from * inside the "look" command. */ bool target_set(int mode) { int i, d, m, t, bd; int y = p_ptr->py; int x = p_ptr->px; bool done = FALSE; bool flag = TRUE; char query; char info[80]; cave_type *c_ptr; int wid, hgt; /* Cancel target */ p_ptr->target_who = 0; /* Cancel tracking */ /* health_track(0); */ /* Prepare the "temp" array */ target_set_prepare(mode); /* Start near the player */ m = 0; /* Interact */ while (!done) { /* Interesting grids */ if (flag && temp_n) { y = temp_y[m]; x = temp_x[m]; /* Access */ c_ptr = area(x, y); /* Allow target */ if (target_able(c_ptr->m_idx)) { strcpy(info, "q,t,p,o,+,-,"); } /* Dis-allow target */ else { strcpy(info, "q,p,o,+,-,"); } /* Describe and Prompt */ query = target_set_aux(x, y, mode, info); /* Cancel tracking */ /* health_track(0); */ /* Assume no "direction" */ d = 0; /* Analyze */ switch (query) { case ESCAPE: case 'q': { done = TRUE; break; } case 't': case '.': case '5': case '0': { if (target_able(c_ptr->m_idx)) { health_track(c_ptr->m_idx); p_ptr->target_who = c_ptr->m_idx; p_ptr->target_row = y; p_ptr->target_col = x; done = TRUE; } else { bell("Illegal target!"); } break; } case ' ': case '*': case '+': { if (++m == temp_n) { m = 0; if (!expand_list) done = TRUE; } break; } case '-': { if (m-- == 0) { m = temp_n - 1; if (!expand_list) done = TRUE; } break; } case 'p': { /* Recenter the map around the player */ verify_panel(); /* Recalculate interesting grids */ target_set_prepare(mode); y = p_ptr->py; x = p_ptr->px; /* Fall through */ } case 'o': { flag = FALSE; break; } case 'm': { break; } default: { /* Extract the action (if any) */ d = get_keymap_dir(query); if (!d) bell("Illegal command for target mode!"); break; } } /* Hack -- move around */ if (d) { /* Modified to scroll to monster */ int x2 = x; int y2 = y; /* Find a new monster */ i = target_pick(temp_x[m], temp_y[m], ddx[d], ddy[d]); /* Request to target past last interesting grid */ while (flag && (i < 0)) { /* Note the change */ if (change_panel(ddx[d], ddy[d])) { int v = temp_y[m]; int u = temp_x[m]; /* Recalculate interesting grids */ target_set_prepare(mode); /* Look at interesting grids */ flag = TRUE; /* Find a new monster */ i = target_pick(u, v, ddx[d], ddy[d]); /* Use that grid */ if (i >= 0) m = i; } /* Nothing interesting */ else { int dx = ddx[d]; int dy = ddy[d]; /* Get size */ get_map_size(&wid, &hgt); /* Restore previous position */ panel_center(x2, y2); /* Recalculate interesting grids */ target_set_prepare(mode); /* Look at boring grids */ flag = FALSE; /* Move */ x = x2 + dx; y = y2 + dy; /* Apply the motion */ if (panel_center(x, y)) target_set_prepare(mode); /* Slide into legality */ if (x < p_ptr->min_wid) x = p_ptr->min_wid; else if (x >= p_ptr->max_wid) x = p_ptr->max_wid - 1; /* Slide into legality */ if (y < p_ptr->min_hgt) y = p_ptr->min_hgt; else if (y >= p_ptr->max_hgt) y = p_ptr->max_hgt - 1; } } /* Use that grid */ m = i; } } /* Arbitrary grids */ else { /* Default prompt */ strcpy(info, "q,t,p,m,+,-,"); /* Describe and Prompt (enable "TARGET_LOOK") */ query = target_set_aux(x, y, mode | TARGET_LOOK, info); /* Cancel tracking */ /* health_track(0); */ /* Assume no direction */ d = 0; /* Analyze the keypress */ switch (query) { case ESCAPE: case 'q': { done = TRUE; break; } case 't': case '.': case '5': case '0': { p_ptr->target_who = -1; p_ptr->target_row = y; p_ptr->target_col = x; done = TRUE; break; } case ' ': case '*': case '+': case '-': { break; } case 'p': { /* Recenter the map around the player */ verify_panel(); /* Recalculate interesting grids */ target_set_prepare(mode); y = p_ptr->py; x = p_ptr->px; /* Fall through */ } case 'o': { break; } case 'm': { flag = TRUE; m = 0; bd = 999; /* Pick a nearby monster */ for (i = 0; i < temp_n; i++) { t = distance(x, y, temp_x[i], temp_y[i]); /* Pick closest */ if (t < bd) { m = i; bd = t; } } /* Nothing interesting */ if (bd == 999) flag = FALSE; break; } default: { /* Extract the action (if any) */ d = get_keymap_dir(query); if (!d) bell("Illegal command for target mode!"); break; } } /* Handle "direction" */ if (d) { int dx = ddx[d]; int dy = ddy[d]; /* Move */ x += dx; y += dy; if (panel_center(x, y)) target_set_prepare(mode); /* Slide into legality */ if (x < p_ptr->min_wid) x = p_ptr->min_wid; else if (x >= p_ptr->max_wid) x = p_ptr->max_wid - 1; /* Slide into legality */ if (y < p_ptr->min_hgt) y = p_ptr->min_hgt; else if (y >= p_ptr->max_hgt) y = p_ptr->max_hgt - 1; } } } /* Forget */ temp_n = 0; /* Clear the top line */ clear_msg(); /* Recenter the map around the player */ verify_panel(); /* Failure to set target */ if (!p_ptr->target_who) return (FALSE); /* Success */ return (TRUE); } /* * Get an "aiming direction" from the user. * * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and * "0" for "current target", and "-1" for "entry aborted". * * Note that "Force Target", if set, will pre-empt user interaction, * if there is a usable target already set. * * Note that confusion over-rides any (explicit?) user choice. */ bool get_aim_dir(int *dp) { int dir; char command; cptr p; /* Initialize */ *dp = 0; /* Global direction */ dir = p_ptr->cmd.dir; /* Hack -- auto-target if requested */ if (use_old_target && target_okay()) dir = 5; if (repeat_pull(dp)) { /* Confusion? */ /* Verify */ if (!(*dp == 5 && !target_okay())) { /* Store direction */ dir = *dp; } else { /* Invalid repeat - reset it */ repeat_clear(); } } /* Ask until satisfied */ while (!dir) { /* Choose a prompt */ if (!target_okay()) { p = "Direction ('*' to choose a target, Escape to cancel)? "; } else { p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? "; } /* Get a command (or Cancel) */ if (!get_com(p, &command)) break; /* Convert various keys to "standard" keys */ switch (command) { case 'T': case 't': case '.': case '5': case '0': { /* Use current target */ dir = 5; break; } case '*': { /* Set new target */ if (target_set(TARGET_KILL | TARGET_HOST)) dir = 5; break; } default: { /* Extract the action (if any) */ dir = get_keymap_dir(command); break; } } /* Verify requested targets */ if ((dir == 5) && !target_okay()) dir = 0; /* Error */ if (!dir) bell("Illegal aim direction!"); } /* No direction */ if (!dir) return (FALSE); /* Save the direction */ p_ptr->cmd.dir = dir; /* Check for confusion */ if (p_ptr->tim.confused) { /* Random direction */ dir = ddd[randint0(8)]; } /* Notice confusion */ if (p_ptr->cmd.dir != dir) { /* Warn the user */ msgf("You are confused."); } /* Save direction */ (*dp) = dir; repeat_push(p_ptr->cmd.dir); /* A "valid" direction was entered */ return (TRUE); } /* * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user, * and place it into "cmd.dir", unless we already have one. * * This function should be used for all "repeatable" commands, such as * run, walk, open, close, disarm, spike, tunnel, etc, as well * as all commands which must reference a grid adjacent to the player, * and which may not reference the grid under the player. Note that, * for example, it is no longer possible to "disarm" or "open" chests * in the same grid as the player. * * Direction "5" is illegal and will (cleanly) abort the command. * * This function tracks and uses the "global direction", and uses * that as the "desired direction", to which "confusion" is applied. */ bool get_rep_dir(int *dp) { int dir; if (repeat_pull(dp)) { return (TRUE); } /* Initialize */ (*dp) = 0; /* Global direction */ dir = p_ptr->cmd.dir; /* Get a direction */ while (!dir) { char ch; /* Get a command (or Cancel) */ if (!get_com("Direction (Escape to cancel)? ", &ch)) break; /* Look up the direction */ dir = get_keymap_dir(ch); /* Oops */ if (!dir) bell("Illegal repeatable direction!"); } /* Aborted */ if (!dir) return (FALSE); /* Save desired direction */ p_ptr->cmd.dir = dir; /* Apply "confusion" */ if (p_ptr->tim.confused) { /* Standard confusion */ if (randint0(100) < 75) { /* Random direction */ dir = ddd[randint0(8)]; } } /* Notice confusion */ if (p_ptr->cmd.dir != dir) { /* Warn the user */ msgf("You are confused."); } /* Save direction */ (*dp) = dir; repeat_push(dir); /* Success */ return (TRUE); } int get_chaos_patron(void) { return ((p_ptr->rp.age + p_ptr->rp.sc) % MAX_PATRON); } void gain_level_reward(int chosen_reward) { int py = p_ptr->py; int px = p_ptr->px; object_type *q_ptr; char wrath_reason[32] = ""; int nasty_chance = 6; int tval, sval; int type, effect; int i; int patron = p_ptr->chaos_patron; int count = 0; if (p_ptr->lev == 13) nasty_chance = 2; else if (!(p_ptr->lev % 13)) nasty_chance = 3; else if (!(p_ptr->lev % 14)) nasty_chance = 12; /* Strange luck can give chaos rewards from a random patron */ if (!(FLAG(p_ptr, TR_PATRON))) { nasty_chance *= 2; patron = randint0(MAX_PATRON); } if (one_in_(nasty_chance)) type = randint1(20); /* Allow the 'nasty' effects */ else type = rand_range(5, 20); /* Or disallow them */ if (type < 1) type = 1; if (type > 20) type = 20; type--; strnfmt(wrath_reason, 32, "the Wrath of %s", chaos_patrons[patron]); effect = chaos_rewards[patron][type]; if (one_in_(6) && !chosen_reward) { msgf("%^s rewards you with a mutation!", chaos_patrons[patron]); (void)gain_mutation(0); return; } switch (chosen_reward ? chosen_reward : effect) { case REW_POLY_SLF: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thou needst a new form, mortal!'"); do_poly_self(); break; } case REW_GAIN_EXP: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Well done, mortal! Lead on!'"); if (p_ptr->exp < PY_MAX_EXP) { s32b ee = (p_ptr->exp / 2) + 10; if (ee > 100000L) ee = 100000L; msgf("You feel more experienced."); gain_exp(ee); } break; } case REW_LOSE_EXP: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thou didst not deserve that, slave.'"); lose_exp(p_ptr->exp / 6); break; } case REW_GOOD_OBJ: { msgf("The voice of %s whispers:", chaos_patrons[patron]); msgf("'Use my gift wisely.'"); acquirement(px, py, 1, FALSE, FALSE); break; } case REW_GREA_OBJ: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Use my gift wisely.'"); acquirement(px, py, 1, TRUE, FALSE); break; } case REW_CHAOS_WP: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thy deed hath earned thee a worthy blade.'"); tval = TV_SWORD; switch (randint1(p_ptr->lev)) { case 0: case 1: sval = SV_DAGGER; break; case 2: case 3: sval = SV_MAIN_GAUCHE; break; case 4: sval = SV_TANTO; break; case 5: case 6: sval = SV_RAPIER; break; case 7: case 8: sval = SV_SMALL_SWORD; break; case 9: case 10: sval = SV_BASILLARD; break; case 11: case 12: case 13: sval = SV_SHORT_SWORD; break; case 14: case 15: sval = SV_SABRE; break; case 16: case 17: sval = SV_CUTLASS; break; case 18: sval = SV_WAKIZASHI; break; case 19: sval = SV_KHOPESH; break; case 20: sval = SV_TULWAR; break; case 21: sval = SV_BROAD_SWORD; break; case 22: case 23: sval = SV_LONG_SWORD; break; case 24: case 25: sval = SV_SCIMITAR; break; case 26: sval = SV_NINJATO; break; case 27: sval = SV_KATANA; break; case 28: case 29: sval = SV_BASTARD_SWORD; break; case 30: sval = SV_GREAT_SCIMITAR; break; case 31: sval = SV_CLAYMORE; break; case 32: sval = SV_ESPADON; break; case 33: sval = SV_TWO_HANDED_SWORD; break; case 34: sval = SV_FLAMBERGE; break; case 35: sval = SV_NO_DACHI; break; case 36: sval = SV_EXECUTIONERS_SWORD; break; case 37: sval = SV_ZWEIHANDER; break; case 38: sval = SV_DIAMOND_EDGE; break; default: sval = SV_BLADE_OF_CHAOS; } q_ptr = object_prep(lookup_kind(tval, sval)); q_ptr->to_h = 3 + randint1(p_ptr->depth) % 10; q_ptr->to_d = 3 + randint1(p_ptr->depth) % 10; add_ego_power(EGO_XTRA_ANY_RESIST, q_ptr); add_ego_flags(q_ptr, EGO_CHAOTIC); /* Drop it in the dungeon */ drop_near(q_ptr, -1, px, py); break; } case REW_GOOD_OBS: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thy deed hath earned thee a worthy reward.'"); acquirement(px, py, rand_range(2, 3), FALSE, FALSE); break; } case REW_GREA_OBS: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Behold, mortal, how generously I reward thy loyalty.'"); acquirement(px, py, rand_range(2, 3), TRUE, FALSE); break; } case REW_TY_CURSE: { msgf("The voice of %s thunders:", chaos_patrons[patron]); msgf("'Thou art growing arrogant, mortal.'"); (void)activate_ty_curse(FALSE, &count); break; } case REW_SUMMON_M: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'My pets, destroy the arrogant mortal!'"); for (i = 0; i < rand_range(2, 6); i++) { (void)summon_specific(0, px, py, p_ptr->depth, 0, TRUE, FALSE, FALSE); } break; } case REW_H_SUMMON: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thou needst worthier opponents!'"); (void)activate_hi_summon(); break; } case REW_DO_HAVOC: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Death and destruction! This pleaseth me!'"); call_chaos(); break; } case REW_GAIN_ABL: { msgf("The voice of %s rings out:", chaos_patrons[patron]); msgf("'Stay, mortal, and let me mold thee.'"); if (one_in_(3) && !(chaos_stats[patron] < 0)) (void)do_inc_stat(chaos_stats[patron]); else (void)do_inc_stat(randint0(A_MAX)); break; } case REW_LOSE_ABL: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'I grow tired of thee, mortal.'"); if (one_in_(3) && !(chaos_stats[patron] < 0)) (void)do_dec_stat(chaos_stats[patron]); else (void)do_dec_stat(randint0(A_MAX)); break; } case REW_RUIN_ABL: { msgf("The voice of %s thunders:", chaos_patrons[patron]); msgf("'Thou needst a lesson in humility, mortal!'"); msgf("You feel less powerful!"); for (i = 0; i < A_MAX; i++) { (void)dec_stat(i, rand_range(10, 25), TRUE); } break; } case REW_POLY_WND: { msgf("You feel the power of %s touch you.", chaos_patrons[patron]); do_poly_wounds(); break; } case REW_AUGM_ABL: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Receive this modest gift from me!'"); for (i = 0; i < A_MAX; i++) { (void)do_inc_stat(i); } break; } case REW_HURT_LOT: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Suffer, pathetic fool!'"); (void)fire_ball(GF_DISINTEGRATE, 0, p_ptr->lev * 4, 4); take_hit(p_ptr->lev * 4, wrath_reason); break; } case REW_HEAL_FUL: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Rise, my servant!'"); (void)restore_level(); (void)clear_poisoned(); (void)clear_blind(); (void)clear_confused(); (void)clear_image(); (void)clear_stun(); (void)clear_cut(); for (i = 0; i < A_MAX; i++) { (void)do_res_stat(i); } /* Recalculate max. hitpoints */ update_stuff(); (void)hp_player(5000); break; } case REW_CURSE_WP: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thou reliest too much on thy weapon.'"); (void)curse_weapon(); break; } case REW_CURSE_AR: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Thou reliest too much on thine equipment.'"); (void)curse_armor(); break; } case REW_PISS_OFF: { msgf("The voice of %s whispers:", chaos_patrons[patron]); msgf("'Now thou shalt pay for annoying me.'"); switch (randint1(4)) { case 1: { (void)activate_ty_curse(FALSE, &count); break; } case 2: (void)activate_hi_summon(); break; case 3: if (one_in_(2)) (void)curse_weapon(); else (void)curse_armor(); break; default: for (i = 0; i < A_MAX; i++) { (void)dec_stat(i, rand_range(10, 25), TRUE); } } break; } case REW_WRATH: { msgf("The voice of %s thunders:", chaos_patrons[patron]); msgf("'Die, mortal!'"); take_hit(p_ptr->lev * 4, wrath_reason); for (i = 0; i < A_MAX; i++) { (void)dec_stat(i, rand_range(10, 25), FALSE); } (void)activate_hi_summon(); (void)activate_ty_curse(FALSE, &count); if (one_in_(2)) (void)curse_weapon(); if (one_in_(2)) (void)curse_armor(); break; } case REW_DESTRUCT: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Death and destruction! This pleaseth me!'"); (void)destroy_area(px, py, 25); break; } case REW_GENOCIDE: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Let me relieve thee of thine oppressors!'"); (void)genocide(FALSE); break; } case REW_MASS_GEN: { msgf("The voice of %s booms out:", chaos_patrons[patron]); msgf("'Let me relieve thee of thine oppressors!'"); (void)mass_genocide(FALSE); break; } case REW_DISPEL_C: { msgf("You can feel the power of %s assault your enemies!", chaos_patrons[patron]); (void)dispel_monsters(p_ptr->lev * 4); break; } case REW_IGNORE: { msgf("%s ignores you.", chaos_patrons[patron]); break; } case REW_SER_DEMO: { msgf("%s rewards you with a demonic servant!", chaos_patrons[patron]); if (!summon_specific (-1, px, py, p_ptr->depth, SUMMON_DEMON, FALSE, TRUE, TRUE)) msgf("Nobody ever turns up..."); break; } case REW_SER_MONS: { msgf("%s rewards you with a servant!", chaos_patrons[patron]); if (!summon_specific (-1, px, py, p_ptr->depth, SUMMON_NO_UNIQUES, FALSE, TRUE, TRUE)) msgf("Nobody ever turns up..."); break; } case REW_SER_UNDE: { msgf("%s rewards you with an undead servant!", chaos_patrons[patron]); if (!summon_specific (-1, px, py, p_ptr->depth, SUMMON_UNDEAD, FALSE, TRUE, TRUE)) msgf("Nobody ever turns up..."); break; } default: { msgf("The voice of %s stammers:", chaos_patrons[patron]); msgf("'Uh... uh... the answer's %d/%d, what's the question?'", type, effect); } } } /* * old -- from PsiAngband. */ bool tgt_pt(int *x, int *y) { int py = p_ptr->py; int px = p_ptr->px; char ch = 0; int d, cu, cv; bool success = FALSE; int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); *x = px; *y = py; cu = Term->scr->cu; cv = Term->scr->cv; Term->scr->cu = 0; Term->scr->cv = 1; msgf("Select a point and press space."); while ((ch != ESCAPE) && (ch != ' ')) { move_cursor_relative(*x, *y); ch = inkey(); switch (ch) { case ESCAPE: break; case ' ': success = TRUE; break; default: /* Look up the direction */ d = get_keymap_dir(ch); if (!d) break; *x += ddx[d]; *y += ddy[d]; /* Center on cursor */ panel_center(*x, *y); /* Slide into legality */ if (*x < p_ptr->min_wid) *x = p_ptr->min_wid; else if (*x >= p_ptr->max_wid) *x = p_ptr->max_wid - 1; /* Slide into legality */ if (*y < p_ptr->min_hgt) *y = p_ptr->min_hgt; else if (*y >= p_ptr->max_hgt) *y = p_ptr->max_hgt - 1; break; } } Term->scr->cu = cu; Term->scr->cv = cv; Term_fresh(); return success; } bool get_hack_dir(int *dp) { int dir; cptr p; char command; /* Initialize */ (*dp) = 0; /* Global direction */ dir = 0; /* (No auto-targeting) */ /* Ask until satisfied */ while (!dir) { /* Choose a prompt */ if (!target_okay()) { p = "Direction ('*' to choose a target, Escape to cancel)? "; } else { p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? "; } /* Get a command (or Cancel) */ if (!get_com(p, &command)) break; /* Convert various keys to "standard" keys */ switch (command) { /* Use current target */ case 'T': case 't': case '.': case '5': case '0': { dir = 5; break; } /* Set new target */ case '*': { if (target_set(TARGET_KILL)) dir = 5; break; } default: { /* Look up the direction */ dir = get_keymap_dir(command); break; } } /* Verify requested targets */ if ((dir == 5) && !target_okay()) dir = 0; /* Error */ if (!dir) bell("Illegal direction!"); } /* No direction */ if (!dir) return (FALSE); /* Save the direction */ p_ptr->cmd.dir = dir; /* Check for confusion */ if (p_ptr->tim.confused) { /* XXX XXX XXX */ /* Random direction */ dir = ddd[randint0(8)]; } /* Notice confusion */ if (p_ptr->cmd.dir != dir) { /* Warn the user */ msgf("You are confused."); } /* Save direction */ (*dp) = dir; /* A "valid" direction was entered */ return (TRUE); } /* * Find the maximum a stat can be raised to */ int stat_cap(int stat) { int bonus = rp_ptr->r_adj[stat] + cp_ptr->c_adj[stat]; if (bonus > 12) return 400; else return 280 + 10 * bonus; } int adjust_stat(int stat, int value, int amount) { int cap = stat_cap(stat); /* Apply bonus/penalty */ value += amount * 10; /* Cap value */ if (value > cap) value = cap; if (value < 30) value = 30; /* Return the result */ return (value); } zangband/src/z-form.c0000755000000000000000000004071010250356275013517 0ustar rootroot/* File: z-form.c */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* Purpose: Low level text formatting -BEN- */ #include "z-form.h" #include "z-util.h" #include "z-virt.h" /* * Here is some information about the routines in this file. * * In general, the following routines take a "buffer", a "max length", * a "format string", and some "arguments", and use the format string * and the arguments to create a (terminated) string in the buffer * (using only the first "max length" bytes), and return the "length" * of the resulting string, not including the (mandatory) terminator. * * The format strings allow the basic "sprintf()" format sequences, though * some of them are processed slightly more carefully or portably, as well * as a few "special" sequences, including the "%v" sequence, and a the * recursive "%S' sequence. * * Note that some "limitations" are enforced by the current implementation, * for example, no "format sequence" can exceed 100 characters, including any * "length" restrictions, and the result of combining and "format sequence" * with the relevent "arguments" must not exceed 1000 characters. * * These limitations could be fixed by stealing some of the code from, * say, "vsprintf()" and placing it into my "vstrnfmt()" function. * * Note that a "^" inside a "format sequence" causes the first non-space * character in the string resulting from the combination of the format * sequence and the argument(s) to be "capitalized" if possible. Note * that the "^" character is removed before the "standard" formatting * routines are called. Likewise, a "*" inside a "format sequence" is * removed from the "format sequence", and replaced by the textual form * of the next argument in the argument list. See examples below. * * Legal format characters: %,n,p,c,s,d,i,o,u,X,x,v. * * Format("%%") * Append the literal "%". * No legal modifiers. * * Format("%p", vptr v) * Append the pointer "v" (implementation varies). * No legal modifiers. * * Format("%ld", long int i) * Append the long integer "i". * * Format("%d", int i) * Append the integer "i". * * Format("%lu", unsigned long int i) * Append the unsigned long integer "i". * * Format("%u", unsigned int i) * Append the unsigned integer "i". * * Format("%lo", unsigned long int i) * Append the unsigned long integer "i", in octal. * * Format("%o", unsigned int i) * Append the unsigned integer "i", in octal. * * Format("%lX", unsigned long int i) * Note -- use all capital letters * Format("%lx", unsigned long int i) * Append the unsigned long integer "i", in hexidecimal. * * Format("%X", unsigned int i) * Note -- use all capital letters * Format("%x", unsigned int i) * Append the unsigned integer "i", in hexidecimal. * * Format("%c", char c) * Append the character "c". * Do not use the "+" or "0" flags. * * Format("%s", cptr s) * Append the string "s". * Do not use the "+" or "0" flags. * Note that a "NULL" value of "s" is converted to the empty string. * * Format("%v", vstrnfmt_aux_func function_name, extra_args...) * Append the object "v", using function_name, called with * the extra arguments. * * For examples below, assume "int n = 0; int m = 100; char buf[100];", * plus "char *s = NULL;", and unknown values "char *txt; int i;". * * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);". * * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar * effect as "strncpy(buf, txt, 16); buf[15] = '\0';". * * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have * a similar effect as "strcpy(buf, txt)" but with bounds checking. * * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow * multiple "appends" to "buf" (at the cost of losing the max-length info). * * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow * multiple bounded "appends" to "buf", with constant access to "strlen(buf)". * * For example: "format("The %v was destroyed!", obj_desc, obj);" * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description" * of the given object to the given buffer, and return the total length) * will return a "useful message" about the object "obj", for example, * "The Large Shield was destroyed!". * * For example: "format("%^-.*s", i, txt)" will produce a string containing * the first "i" characters of "txt", left justified, with the first non-space * character capitilized, if reasonable. */ /* * The array of available user print functions. * * The last entry must be NULL. */ static vstrnfmt_aux_func *vfmt_table = NULL; /* * Register a table of user format functions */ void register_format_funcs(vstrnfmt_aux_func *table) { /* Save for later */ vfmt_table = table; } /* * Basic "vararg" format function. * * This function takes a buffer, a max byte count, a format string, and * a va_list of arguments to the format string, and uses the format string * and the arguments to create a string to the buffer. The string is * derived from the format string and the arguments in the manner of the * "sprintf()" function, but with some extra "format" commands. Note that * this function will never use more than the given number of bytes in the * buffer, preventing messy invalid memory references. This function then * returns the total number of non-null bytes written into the buffer. * * Method: Let "str" be the (unlimited) created string, and let "len" be the * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of * "str" into "buf", place "\0" into buf[len], and return "len". * * In English, we do a sprintf() into "buf", a buffer with size "max", * and we return the resulting value of "strlen(buf)", but we allow some * special format commands, and we are more careful than "sprintf()". * * Typically, "max" is in fact the "size" of "buf", and thus represents * the "number" of chars in "buf" which are ALLOWED to be used. An * alternative definition would have required "buf" to hold at least * "max+1" characters, and would have used that extra character only * in the case where "buf" was too short for the result. This would * give an easy test for "overflow", but a less "obvious" semantics. * * Note that if the buffer was "too short" to hold the result, we will * always return "max-1", but we also return "max-1" if the buffer was * "just long enough". We could have returned "max" if the buffer was * too short, not written a null, and forced the programmer to deal with * this special case, but I felt that it is better to at least give a * "usable" result when the buffer was too long instead of either giving * a memory overwrite like "sprintf()" or a non-terminated string like * "strncpy()". Note that "strncpy()" also "null-pads" the result. * * Note that in most cases "just long enough" is probably "too short". * * We should also consider extracting and processing the "width" and other * "flags" by hand, it might be more "accurate", and it would allow us to * remove the limit (1000 chars) on the result of format sequences. * * Also, some sequences, such as "%+d" by hand, do not work on all machines, * and could thus be correctly handled here. * * Error detection in this routine is not very graceful, in particular, * if an error is detected in the format string, we simply "pre-terminate" * the given buffer to a length of zero, and return a "length" of zero. * The contents of "buf", except for "buf[0]", may then be undefined. */ uint vstrnfmt(char *buf, uint max, cptr fmt, va_list *vp) { cptr s; /* The argument is "long" */ bool do_long; /* The argument needs "processing" */ bool do_xtra; /* Bytes used in buffer */ uint n; /* Bytes used in format sequence */ uint q; /* Format sequence */ char aux[128]; /* Resulting string */ char tmp[1024]; /* Mega-Hack -- treat "illegal" length as "infinite" */ if (!max) max = 32767; /* Mega-Hack -- treat "no format" as "empty string" */ if (!fmt) fmt = ""; /* Begin the buffer */ n = 0; /* Begin the format string */ s = fmt; /* Scan the format string */ while (TRUE) { /* All done */ if (!*s) break; /* Normal character */ if (*s != '%') { /* Check total length */ if (n == max - 1) break; /* Save the character */ buf[n++] = *s++; /* Continue */ continue; } /* Skip the "percent" */ s++; /* Pre-process "%%" */ if (*s == '%') { /* Check total length */ if (n == max - 1) break; /* Save the percent */ buf[n++] = '%'; /* Skip the "%" */ s++; /* Continue */ continue; } /* Begin the "aux" string */ q = 0; /* Save the "percent" */ aux[q++] = '%'; /* Assume no "long" argument */ do_long = FALSE; /* Assume no "xtra" processing */ do_xtra = FALSE; /* Build the "aux" string */ while (TRUE) { /* Error -- format sequence is not terminated */ if (!*s) { /* Terminate the buffer */ buf[0] = '\0'; /* Return "error" */ return (0); } /* Error -- format sequence may be too long */ if (q > 100) { /* Terminate the buffer */ buf[0] = '\0'; /* Return "error" */ return (0); } /* Handle "alphabetic" chars */ if (isalpha(*s)) { /* Hack -- handle "long" request */ if (*s == 'l') { /* Save the character */ aux[q++] = *s++; /* Note the "long" flag */ do_long = TRUE; } /* Mega-Hack -- handle "extra-long" request */ else if (*s == 'L') { /* Error -- illegal format char */ buf[0] = '\0'; /* Return "error" */ return (0); } /* Handle normal end of format sequence */ else { /* Save the character */ aux[q++] = *s++; /* Stop processing the format sequence */ break; } } /* Handle "non-alphabetic" chars */ else { /* Hack -- Handle 'star' (for "variable length" argument) */ if (*s == '*') { int arg; /* Get the next argument */ arg = va_arg(*vp, int); /* Hack -- append the "length" */ sprintf(aux + q, "%d", arg); /* Hack -- accept the "length" */ while (aux[q]) q++; /* Skip the "*" */ s++; } /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */ else if (*s == '^') { /* Note the "xtra" flag */ do_xtra = TRUE; /* Skip the "^" */ s++; } /* Collect "normal" characters (digits, "-", "+", ".", etc) */ else { /* Save the character */ aux[q++] = *s++; } } } /* Terminate "aux" */ aux[q] = '\0'; /* Clear "tmp" */ tmp[0] = '\0'; /* Process the "format" char */ switch (aux[q - 1]) { case 'c': { /* Simple Character -- standard format */ int arg; /* Get the next argument */ arg = va_arg(*vp, int); /* Format the argument */ sprintf(tmp, aux, arg); /* Done */ break; } case 'd': case 'i': { /* Signed Integers -- standard format */ if (do_long) { long arg; /* Get the next argument */ arg = va_arg(*vp, long); /* Format the argument */ sprintf(tmp, aux, arg); } else { int arg; /* Get the next argument */ arg = va_arg(*vp, int); /* Format the argument */ sprintf(tmp, aux, arg); } /* Done */ break; } case 'u': case 'o': case 'x': case 'X': { /* Unsigned Integers -- various formats */ if (do_long) { unsigned long arg; /* Get the next argument */ arg = va_arg(*vp, unsigned long); /* Format the argument */ sprintf(tmp, aux, arg); } else { unsigned int arg; /* Get the next argument */ arg = va_arg(*vp, unsigned int); /* Format the argument */ sprintf(tmp, aux, arg); } /* Done */ break; } case 'p': { /* Pointer -- implementation varies */ vptr arg; /* Get the next argument */ arg = va_arg(*vp, vptr); /* Format the argument */ sprintf(tmp, aux, arg); /* Done */ break; } case 's': { /* String */ cptr arg; char arg2[1024]; /* Get the next argument */ arg = va_arg(*vp, cptr); /* Hack -- convert NULL to EMPTY */ if (!arg) arg = ""; /* Prevent buffer overflows */ strncpy(arg2, arg, 1024); /* Terminate */ arg2[1023] = '\0'; /* Format the argument */ sprintf(tmp, aux, arg2); /* Done */ break; } case 'v': { vstrnfmt_aux_func call_func; vstrnfmt_aux_func *tmp_func; /* Extract the function to call */ call_func = va_arg(*vp, vstrnfmt_aux_func); /* Test to see if this is a valid function */ if (vfmt_table) { for (tmp_func = vfmt_table; *tmp_func; tmp_func++) { if (*tmp_func == call_func) { /* A match! - so we call this routine */ /* Format the "user data" */ call_func(tmp, 1000, aux, vp); break; } } /* No Match? */ if (!(*tmp_func)) { quit("Invalid %v call due to invalid function"); } } else { quit("Invalid %v call due to uninitialised function table"); } /* Done */ break; } default: { /* Error -- illegal format char */ buf[0] = '\0'; /* Return "error" */ return (0); } } /* Mega-Hack -- handle "capitalization" */ if (do_xtra) { /* Now append "tmp" to "buf" */ for (q = 0; tmp[q]; q++) { /* Notice first non-space */ if (!isspace(tmp[q])) { /* Capitalize if possible */ if (islower(tmp[q])) tmp[q] = toupper(tmp[q]); /* Done */ break; } } } /* Now append "tmp" to "buf" */ for (q = 0; tmp[q]; q++) { /* Check total length */ if (n == max - 1) break; /* Save the character */ buf[n++] = tmp[q]; } } /* Terminate buffer */ buf[n] = '\0'; /* Return length */ return (n); } /* * Add a formatted string to the end of a string */ void strnfcat(char *str, int max, int *end, cptr fmt, ...) { int len; va_list vp; /* Paranoia */ if (*end >= max) return; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Do a virtual fprintf to stderr */ len = vstrnfmt(&str[*end], max - *end, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Change the end value */ *end += len; } static char format_buf[1024]; /* * Do a vstrnfmt (see above) into a static buffer. * This buffer is usable for very short term formatting of results. */ static char *vformat(cptr fmt, va_list *vp) { /* Null format yields last result */ if (fmt) { /* Otherwise - just use vstrnfmt */ vstrnfmt(format_buf, 1024, fmt, vp); } /* Return the new buffer */ return (format_buf); } /* * Do a vstrnfmt (see above) into a buffer of a given size. */ uint strnfmt(char *buf, uint max, cptr fmt, ...) { uint len; va_list vp; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Do a virtual fprintf to stderr */ len = vstrnfmt(buf, max, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Return the number of bytes written */ return (len); } /* * Do a vstrnfmt() into (see above) into a (growable) static buffer. * This buffer is usable for very short term formatting of results. * Note that the buffer is (technically) writable, but only up to * the length of the string contained inside it. */ char *format(cptr fmt, ...) { char *res; va_list vp; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args */ res = vformat(fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Return the result */ return (res); } /* * Vararg interface to plog() */ void plog_fmt(cptr fmt, ...) { char *res; va_list vp; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args */ res = vformat(fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Call plog */ plog(res); } /* * Vararg interface to quit() */ void quit_fmt(cptr fmt, ...) { char *res; va_list vp; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format */ res = vformat(fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Call quit() */ quit(res); } /* * Vararg interface to core() */ void core_fmt(cptr fmt, ...) { char *res; va_list vp; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* If requested, Do a virtual fprintf to stderr */ res = vformat(fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Call core() */ core(res); } zangband/src/z-rand.c0000755000000000000000000002626610250356275013512 0ustar rootroot/* File: z-rand.c */ /* Purpose: a simple random number generator -BEN- */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ /* * This file provides an optimized random number generator. * * * This code provides both a "quick" random number generator (4 bytes of * state), and a "decent" random number generator (256 bytes of state), * both available in two flavors, first, the simple "mod" flavor, which * is fast, but slightly biased at high values, and second, the simple * "div" flavor, which is less fast (and potentially non-terminating) * but which is not biased and is much less subject to non-randomness * problems in the low bits. Note the "rand_int()" macro in "z-rand.h", * which must specify a "default" flavor. * * Note the use of the "simple" RNG, first you activate it via * "Rand_quick = TRUE" and "Rand_value = seed" and then it is used * automatically used instead of the "complex" RNG, and when you are * done, you de-activate it via "Rand_quick = FALSE" or choose a new * seed via "Rand_value = seed". * * * This (optimized) random number generator is based loosely on the old * "random.c" file from Berkeley but with some major optimizations and * algorithm changes. See below for more details. * * Some code by Ben Harrison (benh@phial.com). * * Some code by Randy (randy@stat.tamu.edu). */ #include "z-rand.h" /* * Random Number Generator -- Linear Congruent RNG */ #define LCRNG(X) ((X) * 1103515245 + 12345) /* * Use the "simple" LCRNG */ bool Rand_quick = TRUE; /* * Current "value" of the "simple" RNG */ u32b Rand_value; /* * Current "index" for the "complex" RNG */ u16b Rand_place; /* * Current "state" table for the "complex" RNG */ u32b Rand_state[RAND_DEG]; /* * Current "state" of the quick RNG - don't bother to put this in save files. */ byte quick_rand_place; /* * Initialize the "complex" RNG using a new seed */ void Rand_state_init(u32b seed) { int i, j; /* Seed the table */ Rand_state[0] = seed; /* Propagate the seed */ for (i = 1; i < RAND_DEG; i++) Rand_state[i] = LCRNG(Rand_state[i - 1]); /* Cycle the table ten times per degree */ for (i = 0; i < RAND_DEG * 10; i++) { /* Acquire the next index */ j = Rand_place + 1; if (j == RAND_DEG) j = 0; /* Update the table, extract an entry */ Rand_state[j] += Rand_state[Rand_place]; /* Advance the index */ Rand_place = j; } } /* * Extract a "random" number from 0 to m-1, via "division" * * This method selects "random" 28-bit numbers, and then uses * division to drop those numbers into "m" different partitions, * plus a small non-partition to reduce bias, taking as the final * value the first "good" partition that a number falls into. * * This method has no bias, and is much less affected by patterns * in the "low" bits of the underlying RNG's. * * Note that "m" must not be greater than 0x1000000, or division * by zero will result. * * ToDo: Check for m > 0x1000000. */ s32b Rand_div(u32b m) { u32b r, n; /* Hack -- simple case */ if (m <= 1) return (0); /* Partition size */ n = (0x10000000 / m); /* Use a simple RNG */ if (Rand_quick) { /* Wait for it */ while (1) { /* Cycle the generator */ r = (Rand_value = LCRNG(Rand_value)); /* Mutate a 28-bit "random" number */ r = ((r >> 4) & 0x0FFFFFFF) / n; /* Done */ if (r < m) break; } } /* Use a complex RNG */ else { /* Wait for it */ while (1) { int j; /* Acquire the next index */ j = Rand_place + 1; if (j == RAND_DEG) j = 0; /* Update the table, extract an entry */ r = (Rand_state[j] += Rand_state[Rand_place]); /* Hack -- extract a 28-bit "random" number */ r = (r >> 4) / n; /* Advance the index */ Rand_place = j; /* Done */ if (r < m) break; } } /* Use the value */ return (r); } /* * The number of entries in the "Rand_normal_table" */ #define RANDNOR_NUM 256 /* * The standard deviation of the "Rand_normal_table" */ #define RANDNOR_STD 64 /* * The normal distribution table for the "Rand_normal()" function (below) */ static s16b Rand_normal_table[RANDNOR_NUM] = { 206, 613, 1022, 1430, 1838, 2245, 2652, 3058, 3463, 3867, 4271, 4673, 5075, 5475, 5874, 6271, 6667, 7061, 7454, 7845, 8234, 8621, 9006, 9389, 9770, 10148, 10524, 10898, 11269, 11638, 12004, 12367, 12727, 13085, 13440, 13792, 14140, 14486, 14828, 15168, 15504, 15836, 16166, 16492, 16814, 17133, 17449, 17761, 18069, 18374, 18675, 18972, 19266, 19556, 19842, 20124, 20403, 20678, 20949, 21216, 21479, 21738, 21994, 22245, 22493, 22737, 22977, 23213, 23446, 23674, 23899, 24120, 24336, 24550, 24759, 24965, 25166, 25365, 25559, 25750, 25937, 26120, 26300, 26476, 26649, 26818, 26983, 27146, 27304, 27460, 27612, 27760, 27906, 28048, 28187, 28323, 28455, 28585, 28711, 28835, 28955, 29073, 29188, 29299, 29409, 29515, 29619, 29720, 29818, 29914, 30007, 30098, 30186, 30272, 30356, 30437, 30516, 30593, 30668, 30740, 30810, 30879, 30945, 31010, 31072, 31133, 31192, 31249, 31304, 31358, 31410, 31460, 31509, 31556, 31601, 31646, 31688, 31730, 31770, 31808, 31846, 31882, 31917, 31950, 31983, 32014, 32044, 32074, 32102, 32129, 32155, 32180, 32205, 32228, 32251, 32273, 32294, 32314, 32333, 32352, 32370, 32387, 32404, 32420, 32435, 32450, 32464, 32477, 32490, 32503, 32515, 32526, 32537, 32548, 32558, 32568, 32577, 32586, 32595, 32603, 32611, 32618, 32625, 32632, 32639, 32645, 32651, 32657, 32662, 32667, 32672, 32677, 32682, 32686, 32690, 32694, 32698, 32702, 32705, 32708, 32711, 32714, 32717, 32720, 32722, 32725, 32727, 32729, 32731, 32733, 32735, 32737, 32739, 32740, 32742, 32743, 32745, 32746, 32747, 32748, 32749, 32750, 32751, 32752, 32753, 32754, 32755, 32756, 32757, 32757, 32758, 32758, 32759, 32760, 32760, 32761, 32761, 32761, 32762, 32762, 32763, 32763, 32763, 32764, 32764, 32764, 32764, 32765, 32765, 32765, 32765, 32766, 32766, 32766, 32766, 32767, }; /* * Generate a random integer number of NORMAL distribution * * The table above is used to generate a psuedo-normal distribution, * in a manner which is much faster than calling a transcendental * function to calculate a true normal distribution. * * Basically, entry 64*N in the table above represents the number of * times out of 32767 that a random variable with normal distribution * will fall within N standard deviations of the mean. That is, about * 68 percent of the time for N=1 and 95 percent of the time for N=2. * * The table above contains a "faked" final entry which allows us to * pretend that all values in a normal distribution are strictly less * than four standard deviations away from the mean. This results in * "conservative" distribution of approximately 1/32768 values. * * Note that the binary search takes up to 16 quick iterations. */ s16b Rand_normal(int mean, int stand) { s16b tmp; s16b offset; s16b low = 0; s16b high = RANDNOR_NUM; /* Paranoia */ if (stand < 1) return (mean); /* Roll for probability */ tmp = (s16b)randint0(32768); /* Binary Search */ while (low < high) { int mid = (low + high) >> 1; /* Move right if forced */ if (Rand_normal_table[mid] < tmp) { low = mid + 1; } /* Move left otherwise */ else { high = mid; } } /* Convert the index into an offset */ offset = (long)stand *(long)low / RANDNOR_STD; /* One half should be negative */ if (randint0(100) < 50) return (mean - offset); /* One half should be positive */ return (mean + offset); } /* * Extract a "random" number from 0 to m-1, using the "simple" RNG. * * This function should be used when generating random numbers in * "external" program parts like the main-*.c files. It preserves * the current RNG state to prevent influences on game-play. * * Could also use rand() from directly. XXX XXX XXX */ u32b Rand_simple(u32b m) { static bool initialized = FALSE; static u32b simple_rand_value; bool old_rand_quick; u32b old_rand_value; u32b result; /* Save RNG state */ old_rand_quick = Rand_quick; old_rand_value = Rand_value; /* Use "simple" RNG */ Rand_quick = TRUE; if (initialized) { /* Use stored seed */ Rand_value = simple_rand_value; } else { /* Initialize with new seed */ Rand_value = time(NULL); initialized = TRUE; } /* Get a random number */ result = rand_int(m); /* Store the new seed */ simple_rand_value = Rand_value; /* Restore RNG state */ Rand_quick = old_rand_quick; Rand_value = old_rand_value; /* Use the value */ return (result); } /* * Generates damage for "2d6" style dice rolls */ s16b damroll(int num, int sides) { int i, sum = 0; for (i = 0; i < num; i++) sum += randint1(sides); return (sum); } /* * Same as above, but always maximal */ s16b maxroll(int num, int sides) { return (num * sides); } /* 256 random boolean values generated by throwing many, many dice... -SF-*/ static bool qrand_table[256] = { TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE }; /* * This is an ultra-quick "random" bool generator. * The bools have a 50% chance of being true or false. * This routine is designed to be used in LOS code + other * time critical routines where the small period is not * important. */ bool quick_rand(void) { return qrand_table[quick_rand_place++]; } /* * This function adds a new random bool to the table. * This is done to reduce the effect of the tables small size. * (Called once every 10 turns in dungeon.c) */ void quick_rand_add(void) { qrand_table[quick_rand_place++] = (!randint0(2)); } zangband/src/z-term.c0000755000000000000000000015067210250356275013534 0ustar rootroot/* File: z-term.c */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* Purpose: a generic, efficient, terminal window package -BEN- */ #include "z-virt.h" #include "z-term.h" /* * This file provides a generic, efficient, terminal window package, * which can be used not only on standard terminal environments such * as dumb terminals connected to a Unix box, but also in more modern * "graphic" environments, such as the Macintosh or Unix/X11. * * Each "window" works like a standard "dumb terminal", that is, it * can display a two dimensional array of grids containing colored * textual symbols, plus an optional cursor, and it can be used to * get keypress events from the user. * * In fact, this package can simply be used, if desired, to support * programs which will look the same on a dumb terminal as they do * on a graphic platform such as the Macintosh. * * This package was designed to help port the game "Angband" to a wide * variety of different platforms. Angband, like many other games in * the "rogue-like" heirarchy, requires, at the minimum, the ability * to display "colored textual symbols" in a standard 80x24 "window", * such as that provided by most dumb terminals, and many old personal * computers, and to check for "keypresses" from the user. The major * concerns were thus portability and efficiency, so Angband could be * easily ported to many different systems, with minimal effort, and * yet would run quickly on each of these systems, no matter what kind * of underlying hardware/software support was being used. * * It is important to understand the differences between the older * "dumb terminals" and the newer "graphic interface" machines, since * this package was designed to work with both types of systems. * * New machines: * waiting for a keypress is complex * checking for a keypress is often cheap * changing "colors" may be expensive * the "color" of a "blank" is rarely important * moving the "cursor" is relatively cheap * use a "software" cursor (only moves when requested) * drawing characters normally will not erase old ones * drawing a character on the cursor often erases it * may have fast routines for "clear a region" * the bottom right corner is usually not special * * Old machines: * waiting for a keypress is simple * checking for a keypress is often expensive * changing "colors" is usually cheap * the "color" of a "blank" may be important * moving the "cursor" may be expensive * use a "hardware" cursor (moves during screen updates) * drawing new symbols automatically erases old ones * characters may only be drawn at the cursor location * drawing a character on the cursor will move the cursor * may have fast routines for "clear entire window" * may have fast routines for "clear to end of line" * the bottom right corner is often dangerous * * * This package provides support for multiple windows, each of an * arbitrary size (up to 255x255), each with its own set of flags, * and its own hooks to handle several low-level procedures which * differ from platform to platform. Then the main program simply * creates one or more "term" structures, setting the various flags * and hooks in a manner appropriate for the current platform, and * then it can use the various "term" structures without worrying * about the underlying platform. * * * This package allows each "grid" in each window to hold an attr/char * pair, with each ranging from 0 to 255, and makes very few assumptions * about the meaning of any attr/char values. Normally, we assume that * "attr 0" is "black", with the semantics that "black" text should be * sent to "Term_wipe()" instead of "Term_text()", but this sematics is * modified if either the "always_pict" or the "always_text" flags are * set. We assume that "char 0" is "dangerous", since placing such a * "char" in the middle of a string "terminates" the string, and usually * we prevent its use. * * Finally, we use a special attr/char pair, defaulting to "attr 0" and * "char 32", also known as "black space", when we "erase" or "clear" * any window, but this pair can be redefined to any pair, including * the standard "white space", or the bizarre "emptiness" ("attr 0" * and "char 0"), as long as various obscure restrictions are met. * * * This package provides several functions which allow a program to * interact with the "term" structures. Most of the functions allow * the program to "request" certain changes to the current "term", * such as moving the cursor, drawing an attr/char pair, erasing a * region of grids, hiding the cursor, etc. Then there is a special * function which causes all of the "pending" requests to be performed * in an efficient manner. There is another set of functions which * allow the program to query the "requested state" of the current * "term", such as asking for the cursor location, or what attr/char * is at a given location, etc. There is another set of functions * dealing with "keypress" events, which allows the program to ask if * the user has pressed any keys, or to forget any keys the user pressed. * There is a pair of functions to allow this package to memorize the * contents of the current "term", and to restore these contents at * a later time. There is a special function which allows the program * to specify which "term" structure should be the "current" one. At * the lowest level, there is a set of functions which allow a new * "term" to be initialized or destroyed, and which allow this package, * or a program, to access the special "hooks" defined for the current * "term", and a set of functions which those "hooks" can use to inform * this package of the results of certain occurances, for example, one * such function allows this package to learn about user keypresses, * detected by one of the special "hooks". * * We provide, among other things, the functions "Term_keypress()" * to "react" to keypress events, and "Term_redraw()" to redraw the * entire window, plus "Term_resize()" to note a new size. * * * Note that the current "term" contains two "window images". One of * these images represents the "requested" contents of the "term", and * the other represents the "actual" contents of the "term", at the time * of the last performance of pending requests. This package uses these * two images to determine the "minimal" amount of work needed to make * the "actual" contents of the "term" match the "requested" contents of * the "term". This method is not perfect, but it often reduces the * amount of work needed to perform the pending requests, which thus * increases the speed of the program itself. This package promises * that the requested changes will appear to occur either "all at once" * or in a "top to bottom" order. In addition, a "cursor" is maintained, * and this cursor is updated along with the actual window contents. * * Currently, the "Term_fresh()" routine attempts to perform the "minimum" * number of physical updates, in terms of total "work" done by the hooks * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that * adjacent characters of the same color can both be drawn together using * the "Term_text()" hook, and that "black" text can often be sent to the * "Term_wipe()" hook instead of the "Term_text()" hook, and if something * is already displayed in a window, then it is not necessary to display * it again. Unfortunately, this may induce slightly non-optimal results * in some cases, in particular, those in which, say, a string of ten * characters needs to be written, but the fifth character has already * been displayed. Currently, this will cause the "Term_text()" routine * to be called once for each half of the string, instead of once for the * whole string, which, on some machines, may be non-optimal behavior. * * The new formalism includes a "displayed" screen image (old) which * is actually seen by the user, a "requested" screen image (scr) * which is being prepared for display, and a list of screen images * which are accessed through the "next" pointers from scr. * * * Several "flags" are available in each "term" to allow the underlying * visual system (which initializes the "term" structure) to "optimize" * the performance of this package for the given system, or to request * certain behavior which is helpful/required for the given system. * * The "soft_cursor" flag indicates the use of a "soft" cursor, which * only moves when explicitly requested,and which is "erased" when * any characters are drawn on top of it. This flag is used for all * "graphic" systems which handle the cursor by "drawing" it. * * The "icky_corner" flag indicates that the bottom right "corner" * of the windows are "icky", and "printing" anything there may * induce "messy" behavior, such as "scrolling". This flag is used * for most old "dumb terminal" systems. * * * The "term" structure contains the following function "hooks": * * Term->init_hook = Init the term * Term->nuke_hook = Nuke the term * Term->user_hook = Perform user actions * Term->xtra_hook = Perform extra actions * Term->curs_hook = Draw (or Move) the cursor * Term->wipe_hook = Draw some blank spaces * Term->text_hook = Draw some text in the window * Term->pict_hook = Draw some attr/chars in the window * * The "Term->user_hook" hook provides a simple hook to an implementation * defined function, with application defined semantics. It is available * to the program via the "Term_user()" function. * * The "Term->xtra_hook" hook provides a variety of different functions, * based on the first parameter (which should be taken from the various * TERM_XTRA_* defines) and the second parameter (which may make sense * only for some first parameters). It is available to the program via * the "Term_xtra()" function, though some first parameters are only * "legal" when called from inside this package. * * The "Term->curs_hook" hook provides this package with a simple way * to "move" or "draw" the cursor to the grid "x,y", depending on the * setting of the "soft_cursor" flag. Note that the cursor is never * redrawn if "nothing" has happened to the screen (even temporarily). * This hook is required. * * The "Term->wipe_hook" hook provides this package with a simple way * to "erase", starting at "x,y", the next "n" grids. This hook assumes * that the input is valid. This hook is required, unless the setting * of the "always_pict" or "always_text" flags makes it optional. * * The "Term->text_hook" hook provides this package with a simple way * to "draw", starting at "x,y", the "n" chars contained in "cp", using * the attr "a". This hook assumes that the input is valid, and that * "n" is between 1 and 256 inclusive, but it should NOT assume that * the contents of "cp" are null-terminated. This hook is required, * unless the setting of the "always_pict" flag makes it optional. * * The "Term->pict_hook" hook provides this package with a simple way * to "draw", starting at "x,y", the "n" attr/char pairs contained in * the arrays "ap" and "cp". This hook assumes that the input is valid, * and that "n" is between 1 and 256 inclusive, but it should NOT assume * that the contents of "cp" are null-terminated. This hook is optional, * unless the setting of the "always_pict" or "higher_pict" flags make * it required. Note that recently, this hook was changed from taking * a byte "a" and a char "c" to taking a length "n", an array of bytes * "ap" and an array of chars "cp". Old implementations of this hook * should now iterate over all "n" attr/char pairs. * * * The game "Angband" uses a set of files called "main-xxx.c", for * various "xxx" suffixes. Most of these contain a function called * "init_xxx()", that will prepare the underlying visual system for * use with Angband, and then create one or more "term" structures, * using flags and hooks appropriate to the given platform, so that * the "main()" function can call one (or more) of the "init_xxx()" * functions, as appropriate, to prepare the required "term" structs * (one for each desired sub-window), and these "init_xxx()" functions * are called from a centralized "main()" function in "main.c". Other * "main-xxx.c" systems contain their own "main()" function which, in * addition to doing everything needed to initialize the actual program, * also does everything that the normal "init_xxx()" functions would do. * * The game "Angband" defines, in addition to "attr 0", all of the * attr codes from 1 to 15, using definitions in "defines.h", and * thus the "main-xxx.c" files used by Angband must handle these * attr values correctly. Also, they must handle all other attr * values, though they may do so in any way they wish, for example, * by always taking every attr code mod 16. Many of the "main-xxx.c" * files use "white space" ("attr 1" / "char 32") to "erase" or "clear" * any window, for efficiency. * * The game "Angband" uses the "Term_user" hook to allow any of the * "main-xxx.c" files to interact with the user, by calling this hook * whenever the user presses the "!" key when the game is waiting for * a new command. This could be used, for example, to provide "unix * shell commands" to the Unix versions of the game. * * See "main-xxx.c" for a simple skeleton file which can be used to * create a "visual system" for a new platform when porting Angband. */ /* * The current "term" */ term *Term = NULL; /*** Local routines ***/ /* * Nuke a term_win (see below) */ static errr term_win_nuke(term_win *s) { /* Free the window access arrays */ KILL(s->a); KILL(s->c); /* Free the window content arrays */ KILL(s->va); KILL(s->vc); /* Free the terrain access arrays */ KILL(s->ta); KILL(s->tc); /* Free the terrain content arrays */ KILL(s->vta); KILL(s->vtc); /* Success */ return (0); } /* * Initialize a "term_win" (using the given window size) */ static errr term_win_init(term_win *s, int w, int h) { int y; /* Make the window access arrays */ C_MAKE(s->a, h, byte *); C_MAKE(s->c, h, char *); /* Make the window content arrays */ C_MAKE(s->va, h * w, byte); C_MAKE(s->vc, h * w, char); /* Make the terrain access arrays */ C_MAKE(s->ta, h, byte *); C_MAKE(s->tc, h, char *); /* Make the terrain content arrays */ C_MAKE(s->vta, h * w, byte); C_MAKE(s->vtc, h * w, char); /* Prepare the window access arrays */ for (y = 0; y < h; y++) { s->a[y] = s->va + w * y; s->c[y] = s->vc + w * y; s->ta[y] = s->vta + w * y; s->tc[y] = s->vtc + w * y; } /* Hack - disable bigtile */ s->big_x1 = -1; s->big_y1 = -1; s->big_y2 = -1; /* Success */ return (0); } /* * Copy a "term_win" from another * If bigtile is set, then copy bigtile region. */ static errr term_win_copy(term_win *s, term_win *f, int w, int h) { int x, y; /* Copy contents */ for (y = 0; y < h; y++) { byte *f_aa = f->a[y]; char *f_cc = f->c[y]; byte *s_aa = s->a[y]; char *s_cc = s->c[y]; byte *f_taa = f->ta[y]; char *f_tcc = f->tc[y]; byte *s_taa = s->ta[y]; char *s_tcc = s->tc[y]; for (x = 0; x < w; x++) { *s_aa++ = *f_aa++; *s_cc++ = *f_cc++; *s_taa++ = *f_taa++; *s_tcc++ = *f_tcc++; } } /* Copy cursor */ s->cx = f->cx; s->cy = f->cy; s->cu = f->cu; s->cv = f->cv; /* Copy bigtile region */ s->big_x1 = f->big_x1; s->big_y1 = f->big_y1; s->big_y2 = f->big_y2; /* Copy list pointer */ s->next = f->next; /* Success */ return (0); } /* * Resize a "term_win" * * wn, hn are the new width / height * wo, ho are the old width / height * win is the window to resize. */ static errr Term_resize_win(int wn, int hn, int wo, int ho, term_win *win) { term_win tmp; /* Create a new window of the correct size */ (void) term_win_init(&tmp, wn, hn); /* Copy in the information from the old window */ (void) term_win_copy(&tmp, win, wo, ho); /* Nuke the old window */ (void) term_win_nuke(win); /* Illegal cursor? */ if (tmp.cx >= wn) tmp.cu = 1; if (tmp.cy >= hn) tmp.cu = 1; /* Save new window (Structure Copy) */ *win = tmp; /* Success */ return (0); } /*** External hooks ***/ /* * Execute the "Term->user_hook" hook, if available (see above). */ errr Term_user(int n) { /* Verify the hook */ if (!Term->user_hook) return (-1); /* Call the hook */ return ((*Term->user_hook) (n)); } /* * Execute the "Term->xtra_hook" hook, if available (see above). */ void Term_xtra(int n, int v) { /* Verify the hook */ if (!Term->xtra_hook) return; /* Call the hook */ (void)(*Term->xtra_hook) (n, v); /* Done */ return; } /*** Fake hooks ***/ /* * Hack -- fake hook for "Term_curs()" (see above) */ static errr Term_curs_hack(int x, int y) { /* Compiler silliness */ if (x || y) return (-2); /* Oops */ return (-1); } /* * Hack -- fake hook for "Term_wipe()" (see above) */ static errr Term_wipe_hack(int x, int y, int n) { /* Compiler silliness */ if (x || y || n) return (-2); /* Oops */ return (-1); } /* * Hack -- fake hook for "Term_text()" (see above) */ static errr Term_text_hack(int x, int y, int n, byte a, const char *cp) { /* Compiler silliness */ if (x || y || n || a || cp) return (-2); /* Oops */ return (-1); } /* * Hack -- fake hook for "Term_pict()" (see above) */ static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) { /* Compiler silliness */ if (x || y || n || ap || cp || tap || tcp) return (-2); /* Oops */ return (-1); } /*** Efficient routines ***/ /* * Decrease the size of the bigtile region so * that it is outside the row y. */ static void Term_bigtile_expand(int x, int y) { int sy; int i; /* Disable bigtile */ if (Term->scr->wipe_bigtile) { /* Are we in the bigtile region? */ if ((y >= Term->scr->big_y1) && (y <= Term->scr->big_y2) && (x >= Term->scr->big_x1)) { /* Save start point */ sy = Term->scr->big_y1; /* Move bigscreen region to below text area */ Term->scr->big_y1 = y + 1; /* Wipe old bigtiled region */ for (i = sy; i < Term->scr->big_y1; i++) { Term_erase(Term->scr->big_x1, i, 255); } /* Hack - We need to redraw everything later */ Term->total_erase = TRUE; } } } /* * Mentally draw an attr/char at a given location * * Assumes given location and values are valid. */ void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc) { term_win *scrn = Term->scr; byte *scr_aa = &scrn->a[y][x]; char *scr_cc = &scrn->c[y][x]; byte *scr_taa = &scrn->ta[y][x]; char *scr_tcc = &scrn->tc[y][x]; /* Hack -- Ignore non-changes */ if ((*scr_aa == a) && (*scr_cc == c) && (*scr_taa == ta) && (*scr_tcc == tc)) return; /* Save the "literal" information */ *scr_aa = a; *scr_cc = c; *scr_taa = ta; *scr_tcc = tc; /* Check for new min/max row info */ if (y < Term->y1) Term->y1 = y; if (y > Term->y2) Term->y2 = y; /* Check for new min/max col info for this row */ if (x < Term->x1[y]) Term->x1[y] = x; if (x > Term->x2[y]) Term->x2[y] = x; } /* * Mentally draw a string of attr/chars at a given location * * Assumes given location and values are valid. * * This function is designed to be fast, with no consistancy checking. * It is used to update the map in the game. */ void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc) { term_win *scrn = Term->scr; int x1 = -1; int x2 = -1; byte *scr_aa = &scrn->a[y][x]; char *scr_cc = &scrn->c[y][x]; byte *scr_taa = &scrn->ta[y][x]; char *scr_tcc = &scrn->tc[y][x]; /* * Hack - don't worry about bigtile stuff here, * since this routine isn't used by the menues. */ while (n--) { /* Hack -- Ignore non-changes */ if ((*scr_aa == *a) && (*scr_cc == *c) && (*scr_taa == *ta) && (*scr_tcc == *tc)) { x++; a++; c++; ta++; tc++; scr_aa++; scr_cc++; scr_taa++; scr_tcc++; continue; } /* Save the "literal" information */ *scr_taa++ = *ta++; *scr_tcc++ = *tc++; /* Save the "literal" information */ *scr_aa++ = *a++; *scr_cc++ = *c++; /* Track minimum changed column */ if (x1 < 0) x1 = x; /* Track maximum changed column */ x2 = x; x++; } /* Expand the "change area" as needed */ if (x1 >= 0) { /* Check for new min/max row info */ if (y < Term->y1) Term->y1 = y; if (y > Term->y2) Term->y2 = y; /* Check for new min/max col info in this row */ if (x1 < Term->x1[y]) Term->x1[y] = x1; if (x2 > Term->x2[y]) Term->x2[y] = x2; } } /*** Refresh routines ***/ /* * Flush a row of the current window (see "Term_fresh") * * Display text using "Term_pict()" */ static void Term_fresh_row_pict(int y, int x1, int x2) { int x; byte *old_aa = Term->old->a[y]; char *old_cc = Term->old->c[y]; byte *scr_aa = Term->scr->a[y]; char *scr_cc = Term->scr->c[y]; byte *old_taa = Term->old->ta[y]; char *old_tcc = Term->old->tc[y]; byte *scr_taa = Term->scr->ta[y]; char *scr_tcc = Term->scr->tc[y]; byte ota; char otc; byte nta; char ntc; /* Pending length */ int fn = 0; /* Pending start */ int fx = 0; byte oa; char oc; byte na; char nc; /* Scan "modified" columns */ for (x = x1; x <= x2; x++) { /* See what is currently here */ oa = old_aa[x]; oc = old_cc[x]; /* See what is desired there */ na = scr_aa[x]; nc = scr_cc[x]; ota = old_taa[x]; otc = old_tcc[x]; nta = scr_taa[x]; ntc = scr_tcc[x]; /* Handle unchanged grids */ if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc) && !Term->total_erase) { /* Flush */ if (fn) { /* Draw pending attr/char pairs */ (void)((*Term->pict_hook) (fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx])); /* Forget */ fn = 0; } /* Skip */ continue; } /* Save new contents */ old_aa[x] = na; old_cc[x] = nc; old_taa[x] = nta; old_tcc[x] = ntc; /* Restart and Advance */ if (fn++ == 0) fx = x; } /* Flush */ if (fn) { /* Draw pending attr/char pairs */ (void)((*Term->pict_hook) (fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx])); } } /* * Flush a row of the current window (see "Term_fresh") * * Display text using "Term_text()" and "Term_wipe()", * but use "Term_pict()" for high-bit attr/char pairs */ static void Term_fresh_row_both(int y, int x1, int x2) { int x; byte *old_aa = Term->old->a[y]; char *old_cc = Term->old->c[y]; byte *scr_aa = Term->scr->a[y]; char *scr_cc = Term->scr->c[y]; byte *old_taa = Term->old->ta[y]; char *old_tcc = Term->old->tc[y]; byte *scr_taa = Term->scr->ta[y]; char *scr_tcc = Term->scr->tc[y]; byte ota; char otc; byte nta; char ntc; /* The "always_text" flag */ int always_text = Term->always_text; /* Pending length */ int fn = 0; /* Pending start */ int fx = 0; /* Pending attr */ byte fa = Term->attr_blank; byte oa; char oc; byte na; char nc; /* Scan "modified" columns */ for (x = x1; x <= x2; x++) { /* See what is currently here */ oa = old_aa[x]; oc = old_cc[x]; /* See what is desired there */ na = scr_aa[x]; nc = scr_cc[x]; ota = old_taa[x]; otc = old_tcc[x]; nta = scr_taa[x]; ntc = scr_tcc[x]; /* Handle unchanged grids */ if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc) && !Term->total_erase) { /* Flush */ if (fn) { /* Draw pending chars (normal) */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Draw pending chars (black) */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } /* Forget */ fn = 0; } /* Skip */ continue; } /* Save new contents */ old_aa[x] = na; old_cc[x] = nc; old_taa[x] = nta; old_tcc[x] = ntc; /* Handle high-bit attr/chars */ if (na & 0x80) { /* Flush */ if (fn) { /* Draw pending chars (normal) */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Draw pending chars (black) */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } /* Forget */ fn = 0; } /* Hack -- Draw the special attr/char pair */ (void)((*Term->pict_hook) (x, y, 1, &na, &nc, &nta, &ntc)); /* Skip */ continue; } /* Notice new color */ if (fa != na) { /* Flush */ if (fn) { /* Draw the pending chars */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Hack -- Erase "leading" spaces */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } /* Forget */ fn = 0; } /* Save the new color */ fa = na; } /* Restart and Advance */ if (fn++ == 0) fx = x; } /* Flush */ if (fn) { /* Draw pending chars (normal) */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Draw pending chars (black) */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } } } /* * Flush a row of the current window (see "Term_fresh") * * Display text using "Term_text()" and "Term_wipe()" */ static void Term_fresh_row_text(int y, int x1, int x2) { int x; byte *old_aa = Term->old->a[y]; char *old_cc = Term->old->c[y]; byte *scr_aa = Term->scr->a[y]; char *scr_cc = Term->scr->c[y]; /* The "always_text" flag */ int always_text = Term->always_text; /* Pending length */ int fn = 0; /* Pending start */ int fx = 0; /* Pending attr */ byte fa = Term->attr_blank; byte oa; char oc; byte na; char nc; /* Scan "modified" columns */ for (x = x1; x <= x2; x++) { /* See what is currently here */ oa = old_aa[x]; oc = old_cc[x]; /* See what is desired there */ na = scr_aa[x]; nc = scr_cc[x]; /* Handle unchanged grids */ if ((na == oa) && (nc == oc) && !Term->total_erase) { /* Flush */ if (fn) { /* Draw pending chars (normal) */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Draw pending chars (black) */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } /* Forget */ fn = 0; } /* Skip */ continue; } /* Save new contents */ old_aa[x] = na; old_cc[x] = nc; /* Notice new color */ if (fa != na) { /* Flush */ if (fn) { /* Draw the pending chars */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Hack -- Erase "leading" spaces */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } /* Forget */ fn = 0; } /* Save the new color */ fa = na; } /* Restart and Advance */ if (fn++ == 0) fx = x; } /* Flush */ if (fn) { /* Draw pending chars (normal) */ if (fa || always_text) { (void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx])); } /* Draw pending chars (black) */ else { (void)((*Term->wipe_hook) (fx, y, fn)); } } } /* * Draw / Redraw the tiles in a term */ static void Term_fresh_section(void) { int y; int y1 = Term->y1; int y2 = Term->y2; int w = Term->wid; int h = Term->hgt; /* Something to update */ if (y1 <= y2) { /* Handle "icky corner" */ if (Term->icky_corner) { /* Avoid the corner */ if (y2 >= h - 1) { /* Avoid the corner */ if (Term->x2[h - 1] > w - 2) { /* Avoid the corner */ Term->x2[h - 1] = w - 2; } } } /* Scan the "modified" rows */ for (y = y1; y <= y2; ++y) { int x1 = Term->x1[y]; int x2 = Term->x2[y]; /* Flush each "modified" row */ if (x1 <= x2) { /* Always use "Term_pict()" */ if (Term->always_pict) { /* Flush the row */ Term_fresh_row_pict(y, x1, x2); } /* Sometimes use "Term_pict()" */ else if (Term->higher_pict) { /* Flush the row */ Term_fresh_row_both(y, x1, x2); } /* Never use "Term_pict()" */ else { /* Flush the row */ Term_fresh_row_text(y, x1, x2); } /* This row is all done */ Term->x1[y] = w; Term->x2[y] = 0; /* Hack -- Flush that row (if allowed) */ if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y); } } /* No rows are invalid */ Term->y1 = h; Term->y2 = 0; } } /* * Redraw the cursor */ static void Term_fresh_cursor(void) { term_win *old = Term->old; term_win *scr = Term->scr; /* Cursor update -- Show new Cursor */ if (Term->soft_cursor) { /* Draw the cursor */ if (!scr->cu && scr->cv) { /* Call the cursor display routine */ (void)((*Term->curs_hook) (scr->cx, scr->cy)); } } /* Cursor Update -- Show new Cursor */ else { /* The cursor is useless, hide it */ if (scr->cu) { /* Paranoia -- Put the cursor NEAR where it belongs */ (void)((*Term->curs_hook) (Term->wid - 1, scr->cy)); /* Make the cursor invisible */ /* Term_xtra(TERM_XTRA_SHAPE, 0); */ } /* The cursor is invisible, hide it */ else if (!scr->cv) { /* Paranoia -- Put the cursor where it belongs */ (void)((*Term->curs_hook) (scr->cx, scr->cy)); /* Make the cursor invisible */ /* Term_xtra(TERM_XTRA_SHAPE, 0); */ } /* The cursor is visible, display it correctly */ else { /* Put the cursor where it belongs */ (void)((*Term->curs_hook) (scr->cx, scr->cy)); /* Make the cursor visible */ Term_xtra(TERM_XTRA_SHAPE, 1); } } /* Save the "cursor state" */ old->cu = scr->cu; old->cv = scr->cv; old->cx = scr->cx; old->cy = scr->cy; } /* * Actually perform all requested changes to the window * * If absolutely nothing has changed, not even temporarily, or if the * current "Term" is not mapped, then this function will return 1 and * do absolutely nothing. * * Note that when "soft_cursor" is true, we erase the cursor (if needed) * whenever anything has changed, and redraw it (if needed) after all of * the screen updates are complete. This will induce a small amount of * "cursor flicker" but only when the screen has been updated. If the * screen is updated and then restored, you may still get this flicker. * * When "soft_cursor" is not true, we make the cursor invisible before * doing anything else if it is supposed to be invisible by the time we * are done, and we make it visible after moving it to its final location * after all of the screen updates are complete. * * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called * after any row "y" has been "flushed", unless the "Term->never_frosh" * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after * all of the rows have been "flushed". * * Note the use of three different functions to handle the actual flush, * based on the settings of the "Term->always_pict" and "Term->higher_pict" * flags (see below). * * The three helper functions (above) work by collecting similar adjacent * grids into stripes, and then sending each stripe to "Term->pict_hook", * "Term->text_hook", or "Term->wipe_hook", based on the settings of the * "Term->always_pict" and "Term->higher_pict" flags, which select which * of the helper functions to call to flush each row. * * The helper functions currently "skip" any grids which already contain * the desired contents. This may or may not be the best method, especially * when the desired content fits nicely into the current stripe. For example, * it might be better to go ahead and queue them while allowed, but keep a * count of the "trailing skipables", then, when time to flush, or when a * "non skippable" is found, force a flush if there are too many skippables. * * Perhaps an "initialization" stage, where the "text" (and "attr") * buffers are "filled" with information, converting "blanks" into * a convenient representation, and marking "skips" with "zero chars", * and then some "processing" is done to determine which chars to skip. * * Currently, the helper functions are optimal for systems which prefer * to "print a char + move a char + print a char" to "print three chars", * and for applications that do a lot of "detailed" color printing. * * In the two "queue" functions, total "non-changes" are "pre-skipped". * The helper functions must also handle situations in which the contents * of a grid are changed, but then changed back to the original value, * and situations in which two grids in the same row are changed, but * the grids between them are unchanged. * * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()" * will be used instead of "Term_fresh_row_text()". This allows all the * modified grids to be collected into stripes of attr/char pairs, which * are then sent to the "Term->pict_hook" hook, which can draw these pairs * in whatever way it would like. * * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()" * will be used instead of "Term_fresh_row_text()". This allows all the * "special" attr/char pairs (in which both the attr and char have the * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook" * hook, which can draw these pairs in whatever way it would like. * * Normally, the "Term_wipe()" function is used only to display "blanks" * that were induced by "Term_clear()" or "()", and then only * if the "attr_blank" and "char_blank" fields have not been redefined * to use "white space" instead of the default "black space". Actually, * the "Term_wipe()" function is used to display all "black" text, such * as the default "spaces" created by "Term_clear()" and "Term_erase()". * * Note that the "Term->always_text" flag will disable the use of the * "Term_wipe()" function hook entirely, and force all text, even text * drawn in the color "black", to be explicitly drawn. This is useful * for machines which implement "Term_wipe()" by just drawing spaces. * * Note that the "Term->always_pict" flag will disable the use of the * "Term_wipe()" function entirely, and force everything, even text * drawn in the attr "black", to be explicitly drawn. * * Note that if no "black" text is ever drawn, and if "attr_blank" is * not "zero", then the "Term_wipe" hook will never be used, even if * the "Term->always_text" flag is not set. * * This function does nothing unless the "Term" is "mapped", which allows * certain systems to optimize the handling of "closed" windows. * * On systems with a "soft" cursor, we must explicitly erase the cursor * before flushing the output, if needed, to prevent a "jumpy" refresh. * The actual method for this is horrible, but there is very little that * we can do to simplify it efficiently. XXX XXX XXX * * On systems with a "hard" cursor, we will "hide" the cursor before * flushing the output, if needed, to avoid a "flickery" refresh. It * would be nice to *always* hide the cursor during the refresh, but * this might be expensive (and/or ugly) on some machines. * * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()" * or "Term_pict()" or "Term_text()" on the bottom right corner of the * window, which might induce "scrolling" or other nasty stuff on old * dumb terminals. This flag is handled very efficiently. We assume * that the "Term_curs()" call will prevent placing the cursor in the * corner, if needed, though I doubt such placement is ever a problem. * Currently, the use of "Term->icky_corner" and "Term->soft_cursor" * together may result in undefined behavior. */ void Term_fresh(void) { int y; int w = Term->wid; int h = Term->hgt; int y1 = Term->y1; int y2 = Term->y2; term_win *old = Term->old; term_win *scr = Term->scr; /* Do nothing unless "mapped" */ if (!Term->mapped_flag) return; /* Trivial Refresh */ if ((y1 > y2) && (scr->cu == old->cu) && (scr->cv == old->cv) && (scr->cx == old->cx) && (scr->cy == old->cy) && !(Term->total_erase)) { /* Nothing */ return; } /* Paranoia -- use "fake" hooks to prevent core dumps */ if (!Term->curs_hook) Term->curs_hook = Term_curs_hack; if (!Term->wipe_hook) Term->wipe_hook = Term_wipe_hack; if (!Term->text_hook) Term->text_hook = Term_text_hack; if (!Term->pict_hook) Term->pict_hook = Term_pict_hack; /* Handle "total erase" */ if (Term->total_erase) { /* Hack -- clear all "cursor" data */ old->cv = old->cu = old->cx = old->cy = 0; /* Redraw every row */ Term->y1 = y1 = 0; Term->y2 = y2 = h - 1; /* Redraw every column */ for (y = 0; y < h; y++) { Term->x1[y] = 0; Term->x2[y] = w - 1; } } /* Cursor update -- Erase old Cursor */ if (Term->soft_cursor) { /* Cursor was visible */ if (!old->cu && old->cv) { int tx = old->cx; int ty = old->cy; byte *old_aa = old->a[ty]; char *old_cc = old->c[ty]; byte oa = old_aa[tx]; char oc = old_cc[tx]; byte *old_taa = old->ta[ty]; char *old_tcc = old->tc[ty]; byte ota = old_taa[tx]; char otc = old_tcc[tx]; /* Hack -- use "Term_pict()" always */ if (Term->always_pict) { (void)((*Term->pict_hook) (tx, ty, 1, &oa, &oc, &ota, &otc)); } /* Hack -- use "Term_pict()" sometimes */ else if (Term->higher_pict && (oa & 0x80)) { (void)((*Term->pict_hook) (tx, ty, 1, &oa, &oc, &ota, &otc)); } /* Hack -- restore the actual character */ else if (oa || Term->always_text) { (void)((*Term->text_hook) (tx, ty, 1, oa, &oc)); } /* Hack -- erase the grid */ else { (void)((*Term->wipe_hook) (tx, ty, 1)); } } } /* Cursor Update -- Erase old Cursor */ else { /* Cursor will be invisible */ if (scr->cu || !scr->cv) { /* Make the cursor invisible */ Term_xtra(TERM_XTRA_SHAPE, 0); } } /* Redraw stuff as required */ Term_fresh_section(); /* Redraw cursor */ Term_fresh_cursor(); /* Actually flush the output */ Term_xtra(TERM_XTRA_FRESH, 0); /* Forget "total erase" */ Term->total_erase = FALSE; /* Success */ return; } /*** Output routines ***/ /* * Set the cursor visibility */ errr Term_set_cursor(int v) { /* Already done */ if (Term->scr->cv == v) return (1); /* Change */ Term->scr->cv = v; /* Success */ return (0); } /* * Place the cursor at a given location * * Note -- "illegal" requests do not move the cursor. */ void Term_gotoxy(int x, int y) { int w = Term->wid; int h = Term->hgt; /* Verify */ if ((x < 0) || (x >= w)) return; if ((y < 0) || (y >= h)) return; /* Remember the cursor */ Term->scr->cx = x; Term->scr->cy = y; /* The cursor is not useless */ Term->scr->cu = 0; /* Success */ return; } /* * At a given location, place an attr/char * Do not change the cursor position * No visual changes until "Term_fresh()". */ void Term_draw(int x, int y, byte a, char c) { int w = Term->wid; int h = Term->hgt; /* Verify location */ if ((x < 0) || (x >= w)) return; if ((y < 0) || (y >= h)) return; /* Paranoia -- illegal char */ if (!c) return; /* Notice bigtile region changes */ Term_bigtile_expand(x, y); /* Queue it for later */ Term_queue_char(x, y, a, c, 0, 0); /* Success */ return; } /* * Using the given attr, add the given char at the cursor. * * We queue the given attr/char for display at the current * cursor location, and advance the cursor to the right, * marking it as unuable and returning "1" if it leaves * the screen, and otherwise returning "0". * * So when this function, or the following one, return a * positive value, future calls to either function will * return negative ones. */ void Term_addch(byte a, char c) { int w = Term->wid; /* Handle "unusable" cursor */ if (Term->scr->cu) return; /* Paranoia -- no illegal chars */ if (!c) return; /* Notice bigtile region changes */ Term_bigtile_expand(Term->scr->cx, Term->scr->cy); /* Queue the given character for display */ Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0); /* Advance the cursor */ Term->scr->cx++; /* Success */ if (Term->scr->cx < w) return; /* Note "Useless" cursor */ Term->scr->cu = 1; /* Note "Useless" cursor */ return; } /* * Move to a location and, using an attr, add a char */ void Term_putch(int x, int y, byte a, char c) { /* Move first */ Term_gotoxy(x, y); /* Then add the char */ Term_addch(a, c); } /* * Place cursor at (x,y), and clear the next "n" chars */ void Term_erase(int x, int y, int n) { int i; int w = Term->wid; int h = Term->hgt; int x1 = -1; int x2 = -1; int na = Term->attr_blank; int nc = Term->char_blank; byte *scr_aa; char *scr_cc; byte *scr_taa; char *scr_tcc; /* Place cursor */ Term_gotoxy(x, y); /* Paranoia */ if (n < 0) n = 0; if ((x >= w) || (y >= h)) return; /* Force legal size */ if (x + n > w) n = w - x; /* Update bigtile info */ Term_bigtile_expand(x, y); /* Fast access */ scr_aa = Term->scr->a[y]; scr_cc = Term->scr->c[y]; scr_taa = Term->scr->ta[y]; scr_tcc = Term->scr->tc[y]; /* Scan every column */ for (i = 0; i < n; i++, x++) { int oa = scr_aa[x]; int oc = scr_cc[x]; /* Hack -- Ignore "non-changes" */ if ((oa == na) && (oc == nc)) continue; /* Save the "literal" information */ scr_aa[x] = na; scr_cc[x] = nc; scr_taa[x] = 0; scr_tcc[x] = 0; /* Track minimum changed column */ if (x1 < 0) x1 = x; /* Track maximum changed column */ x2 = x; } /* Expand the "change area" as needed */ if (x1 >= 0) { /* Check for new min/max row info */ if (y < Term->y1) Term->y1 = y; if (y > Term->y2) Term->y2 = y; /* Check for new min/max col info in this row */ if (x1 < Term->x1[y]) Term->x1[y] = x1; if (x2 > Term->x2[y]) Term->x2[y] = x2; } } /* * Clear the entire window, and move to the top left corner * * Note the use of the special "total_erase" code */ void Term_clear(void) { int x, y; int w = Term->wid; int h = Term->hgt; byte na = Term->attr_blank; char nc = Term->char_blank; /* Cursor usable */ Term->scr->cu = 0; /* Cursor to the top left */ Term->scr->cx = Term->scr->cy = 0; /* Wipe each row */ for (y = 0; y < h; y++) { byte *scr_aa = Term->scr->a[y]; char *scr_cc = Term->scr->c[y]; byte *scr_taa = Term->scr->ta[y]; char *scr_tcc = Term->scr->tc[y]; /* Wipe each column */ for (x = 0; x < w; x++) { scr_aa[x] = na; scr_cc[x] = nc; scr_taa[x] = 0; scr_tcc[x] = 0; } /* This row has changed */ Term->x1[y] = 0; Term->x2[y] = w - 1; } /* Every row has changed */ Term->y1 = 0; Term->y2 = h - 1; /* Force "total erase" */ Term->total_erase = TRUE; } /* * Redraw (and refresh) the whole window. */ void Term_redraw(void) { /* Force "total erase" */ Term->total_erase = TRUE; /* Hack -- Refresh */ Term_fresh(); } /* * Redraw part of a window. */ errr Term_redraw_section(int x1, int y1, int x2, int y2) { int i; bool redraw_cursor = FALSE; term_win *old = Term->old; /* Hack - make bigtile work */ if (Term->scr->big_x1 != -1) { x1 = (x1 - 1) / 2; x2 = x2 * 2; } /* Bounds checking */ if (y2 >= Term->hgt) y2 = Term->hgt - 1; if (x2 >= Term->wid) x2 = Term->wid - 1; if (y1 < 0) y1 = 0; if (x1 < 0) x1 = 0; /* Set y limits */ if (Term->y1 > y1) Term->y1 = y1; if (Term->y2 < y2) Term->y2 = y2; /* Set the x limits */ for (i = y1; i <= y2; i++) { if (Term->x1[i] > x1) Term->x1[i] = x1; if (Term->x2[i] < x2) Term->x2[i] = x2; } if ((old->cx >= Term->x1[old->cy]) && (old->cx <= Term->x2[old->cy]) && (old->cy >= Term->y1) && (old->cy <= Term->y2)) { /* Hack -- clear all "cursor" data */ old->cv = 0; old->cu = 0; old->cx = 0; old->cy = 0; redraw_cursor = TRUE; } /* Refresh */ Term->total_erase = TRUE; Term_fresh_section(); Term->total_erase = FALSE; if (redraw_cursor) Term_fresh_cursor(); /* Actually flush the output */ Term_xtra(TERM_XTRA_FRESH, 0); /* Success */ return (0); } /*** Access routines ***/ /* * Extract the cursor visibility */ errr Term_get_cursor(int *v) { /* Extract visibility */ (*v) = Term->scr->cv; /* Success */ return (0); } /* * Extract the current window size */ void Term_get_size(int *w, int *h) { /* Access the cursor */ (*w) = Term->wid; (*h) = Term->hgt; } /* * Extract the current cursor location */ errr Term_locate(int *x, int *y) { /* Access the cursor */ (*x) = Term->scr->cx; (*y) = Term->scr->cy; /* Warn about "useless" cursor */ if (Term->scr->cu) return (1); /* Success */ return (0); } /* * At a given location, determine the "current" attr and char * Note that this refers to what will be on the window after the * next call to "Term_fresh()". It may or may not already be there. */ errr Term_what(int x, int y, byte *a, char *c) { int w = Term->wid; int h = Term->hgt; /* Verify location */ if ((x < 0) || (x >= w)) return (-1); if ((y < 0) || (y >= h)) return (-1); /* Direct access */ (*a) = Term->scr->a[y][x]; (*c) = Term->scr->c[y][x]; /* Success */ return (0); } /*** Input routines ***/ /* * Flush and forget the input */ void Term_flush(void) { /* Hack -- Flush all events */ Term_xtra(TERM_XTRA_FLUSH, 0); /* Forget all keypresses */ Term->key_head = Term->key_tail = 0; } /* * Add a keypress to the "queue" */ errr Term_keypress(int k) { /* Hack -- Refuse to enqueue non-keys */ if (!k) return (-1); /* Store the char, advance the queue */ Term->key_queue[Term->key_head++] = k; /* Circular queue, handle wrap */ if (Term->key_head == Term->key_size) Term->key_head = 0; /* Success (unless overflow) */ if (Term->key_head != Term->key_tail) return (0); #if 0 /* Hack -- Forget the oldest key */ if (++Term->key_tail == Term->key_size) Term->key_tail = 0; #endif /* Problem */ return (1); } /* * Add a keypress to the FRONT of the "queue" */ errr Term_key_push(int k) { /* Hack -- Refuse to enqueue non-keys */ if (!k) return (-1); /* Hack -- Overflow may induce circular queue */ if (Term->key_tail == 0) Term->key_tail = Term->key_size; /* Back up, Store the char */ Term->key_queue[--Term->key_tail] = k; /* Success (unless overflow) */ if (Term->key_head != Term->key_tail) return (0); #if 0 /* Hack -- Forget the oldest key */ if (++Term->key_tail == Term->key_size) Term->key_tail = 0; #endif /* Problem */ return (1); } /* * Check for a pending keypress on the key queue. * * Store the keypress, if any, in "ch", and return "0". * Otherwise store "zero" in "ch", and return "1". * * Wait for a keypress if "wait" is true. * * Remove the keypress if "take" is true. */ errr Term_inkey(char *ch, bool wait, bool take) { /* Assume no key */ (*ch) = '\0'; /* Hack -- get bored */ if (!Term->never_bored) { /* Process random events */ Term_xtra(TERM_XTRA_BORED, 0); } /* Wait */ if (wait) { /* Process pending events while necessary */ while (Term->key_head == Term->key_tail) { /* Process events (wait for one) */ Term_xtra(TERM_XTRA_EVENT, TRUE); } } /* Do not Wait */ else { /* Process pending events if necessary */ if (Term->key_head == Term->key_tail) { /* Process events (do not wait) */ Term_xtra(TERM_XTRA_EVENT, FALSE); } } /* No keys are ready */ if (Term->key_head == Term->key_tail) return (1); /* Extract the next keypress */ (*ch) = Term->key_queue[Term->key_tail]; /* If requested, advance the queue, wrap around if necessary */ if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0; /* Success */ return (0); } /*** Extra routines ***/ /* * Save the "requested" screen into "memorized" screen list * * Every "Term_save()" should match exactly one "Term_load()" */ void Term_save(void) { int w = Term->wid; int h = Term->hgt; term_win *tmp; /* Allocate window */ MAKE(tmp, term_win); /* Initialize window */ (void)term_win_init(tmp, w, h); /* Grab */ (void)term_win_copy(tmp, Term->scr, w, h); /* Add the front of the list */ tmp->next = Term->scr; Term->scr = tmp; /* Wipe bigtile regions as required */ Term->scr->wipe_bigtile = TRUE; } /* * Restore the "requested" contents (see above). * * Every "Term_save()" should match exactly one "Term_load()" */ void Term_load(void) { int y; int w = Term->wid; int h = Term->hgt; term_win *tmp; /* Pop off window from the list */ if (Term->scr->next) { /* Hack - is bigtile not on? */ if ((Term->scr->big_x1 == -1) && (Term->scr->next->big_x1 != -1)) { /* Erase term so boundaries are cleared properly. */ Term_clear(); Term_redraw(); } /* Save pointer to old window */ tmp = Term->scr; /* Point to new window */ Term->scr = Term->scr->next; /* Free the old window */ (void)term_win_nuke(tmp); /* Kill */ KILL(tmp); } /* Assume change */ for (y = 0; y < h; y++) { /* Assume change */ Term->x1[y] = 0; Term->x2[y] = w - 1; } /* Assume change */ Term->y1 = 0; Term->y2 = h - 1; /* Hack - is bigtile on? */ if (Term->scr->big_x1 != -1) { /* Redraw term */ Term_redraw(); } } /* * React to a new physical window size. */ errr Term_resize(int w, int h) { int i; int wid, hgt; byte *hold_x1; byte *hold_x2; term_win *scr; /* Resizing is forbidden */ if (Term->fixed_shape) return (-1); /* Ignore illegal changes */ if ((w < 1) || (h < 1)) return (-1); /* Ignore non-changes */ if ((Term->wid == w) && (Term->hgt == h)) return (1); /* Minimum dimensions */ wid = MIN(Term->wid, w); hgt = MIN(Term->hgt, h); /* Resize old window */ (void) Term_resize_win(w, h, wid, hgt, Term->old); /* Resize current window and the previous list */ for (scr = Term->scr; scr; scr = scr->next) { Term_resize_win(w, h, wid, hgt, scr); } /* Save scanners */ hold_x1 = Term->x1; hold_x2 = Term->x2; /* Create new scanners */ C_MAKE(Term->x1, h, byte); C_MAKE(Term->x2, h, byte); /* Free some arrays */ KILL(hold_x1); KILL(hold_x2); /* Save new size */ Term->wid = w; Term->hgt = h; /* Force "total erase" */ Term->total_erase = TRUE; /* Assume change */ for (i = 0; i < h; i++) { /* Assume change */ Term->x1[i] = 0; Term->x2[i] = w - 1; } /* Assume change */ Term->y1 = 0; Term->y2 = h - 1; /* Execute the "resize_hook" hook, if available */ if (Term->resize_hook) { Term->resize_hook(); } /* Success */ return (0); } /* * Activate a new Term (and deactivate the current Term) * * This function is extremely important, and also somewhat bizarre. * It is the only function that should "modify" the value of "Term". * * To "create" a valid "term", one should do "term_init(t)", then * set the various flags and hooks, and then do "Term_activate(t)". */ void Term_activate(term *t) { /* Hack -- already done */ if (Term == t) return; /* Deactivate the old Term */ if (Term) Term_xtra(TERM_XTRA_LEVEL, 0); /* Hack -- Call the special "init" hook */ if (t && !t->active_flag) { /* Call the "init" hook */ if (t->init_hook) (*t->init_hook) (t); /* Remember */ t->active_flag = TRUE; /* Assume mapped */ t->mapped_flag = TRUE; } /* Remember the Term */ Term = t; /* Activate the new Term */ if (Term) Term_xtra(TERM_XTRA_LEVEL, 1); } /* * Nuke a term */ errr term_nuke(term *t) { term_win *tmp, *tmp2; /* Hack -- Call the special "nuke" hook */ if (t->active_flag) { /* Call the "nuke" hook */ if (t->nuke_hook) (*t->nuke_hook) (t); /* Remember */ t->active_flag = FALSE; /* Assume not mapped */ t->mapped_flag = FALSE; } /* Nuke "displayed" */ (void)term_win_nuke(t->old); /* Kill "displayed" */ KILL(t->old); for (tmp = t->scr; tmp; tmp = tmp2) { tmp2 = tmp->next; /* Nuke "requested" */ (void) term_win_nuke(tmp); /* Kill "requested" */ KILL(tmp); } /* Free some arrays */ KILL(t->x1); KILL(t->x2); /* Free the input queue */ KILL(t->key_queue); /* Success */ return (0); } /* * Initialize a term, using a window of the given size. * Also prepare the "input queue" for "k" keypresses * By default, the cursor starts out "invisible" * By default, we "erase" using "black spaces" */ errr term_init(term *t, int w, int h, int k) { int y; /* Wipe it */ (void)WIPE(t, term); /* Prepare the input queue */ t->key_head = t->key_tail = 0; /* Determine the input queue size */ t->key_size = k; /* Allocate the input queue */ C_MAKE(t->key_queue, t->key_size, char); /* Save the size */ t->wid = w; t->hgt = h; /* Allocate change arrays */ C_MAKE(t->x1, h, byte); C_MAKE(t->x2, h, byte); /* Allocate "displayed" */ MAKE(t->old, term_win); /* Initialize "displayed" */ (void)term_win_init(t->old, w, h); /* Allocate "requested" */ MAKE(t->scr, term_win); /* Initialize "requested" */ (void)term_win_init(t->scr, w, h); /* Assume change */ for (y = 0; y < h; y++) { /* Assume change */ t->x1[y] = 0; t->x2[y] = w - 1; } /* Assume change */ t->y1 = 0; t->y2 = h - 1; /* Force "total erase" */ t->total_erase = TRUE; /* Default "blank" */ t->attr_blank = 0; t->char_blank = ' '; /* Success */ return (0); } /* * Make a region of a term 'bigtiled' */ errr Term_bigregion(int x1, int y1, int y2) { /* Save region */ Term->scr->big_x1 = x1; Term->scr->big_y1 = y1; Term->scr->big_y2 = y2; /* Success */ return (0); } zangband/src/z-util.c0000755000000000000000000001004510250356275013527 0ustar rootroot/* File: z-util.c */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* Purpose: Low level utilities -BEN- */ #include "z-util.h" /* * Convenient storage of the program name */ cptr argv0 = NULL; /* * The my_strcpy() function copies up to 'bufsize'-1 characters from 'src' * to 'buf' and NUL-terminates the result. The 'buf' and 'src' strings may * not overlap. * * my_strcpy() returns strlen(src). This makes checking for truncation * easy. Example: if (my_strcpy(buf, src, sizeof(buf)) >= sizeof(buf)) ...; * * This function should be equivalent to the strlcpy() function in BSD. */ size_t my_strcpy(char *buf, const char *src, size_t bufsize) { size_t len = strlen(src); size_t ret = len; /* Paranoia */ if (bufsize == 0) return ret; /* Truncate */ if (len >= bufsize) len = bufsize - 1; /* Copy the string and terminate it */ (void)memcpy(buf, src, len); buf[len] = '\0'; /* Return strlen(src) */ return ret; } /* * The my_strcat() tries to append a string to an existing NUL-terminated string. * It never writes more characters into the buffer than indicated by 'bufsize' and * NUL-terminates the buffer. The 'buf' and 'src' strings may not overlap. * * my_strcat() returns strlen(buf) + strlen(src). This makes checking for * truncation easy. Example: * if (my_strcat(buf, src, sizeof(buf)) >= sizeof(buf)) ...; * * This function should be equivalent to the strlcat() function in BSD. */ size_t my_strcat(char *buf, const char *src, size_t bufsize) { size_t dlen = strlen(buf); /* Is there room left in the buffer? */ if (dlen < bufsize - 1) { /* Append as much as possible */ return (dlen + my_strcpy(buf + dlen, src, bufsize - dlen)); } else { /* Return without appending */ return (dlen + strlen(src)); } } /* * Determine if string "a" is equal to string "b" */ bool streq(cptr a, cptr b) { return (!strcmp(a, b)); } /* * Determine if string "t" is a suffix of string "s" */ bool suffix(cptr s, cptr t) { int tlen = strlen(t); int slen = strlen(s); /* Check for incompatible lengths */ if (tlen > slen) return (FALSE); /* Compare "t" to the end of "s" */ return (streq(s + slen - tlen, t)); } /* * Determine if string "t" is a prefix of string "s" */ bool prefix(cptr s, cptr t) { /* Scan "t" */ while (*t) { /* Compare content and length */ if (*t++ != *s++) return (FALSE); } /* Matched, we have a prefix */ return (TRUE); } /* * Redefinable "plog" action */ void (*plog_aux) (cptr) = NULL; /* * Print (or log) a "warning" message (ala "perror()") * Note the use of the (optional) "plog_aux" hook. */ void plog(cptr str) { /* Use the "alternative" function if possible */ if (plog_aux) (*plog_aux) (str); /* Just do a labeled fprintf to stderr */ else (void)(fprintf(stderr, "%s: %s\n", argv0 ? argv0 : "?", str)); } /* * Redefinable "quit" action */ void (*quit_aux) (cptr) = NULL; /* * Exit (ala "exit()"). If 'str' is NULL, do "exit(0)". * If 'str' begins with "+" or "-", do "exit(atoi(str))". * Otherwise, plog() 'str' and exit with an error code of -1. * But always use 'quit_aux', if set, before anything else. */ void quit(cptr str) { /* Attempt to use the aux function */ if (quit_aux) (*quit_aux) (str); /* Success */ if (!str) (void)(exit(0)); /* Extract a "special error code" */ if ((str[0] == '-') || (str[0] == '+')) (void)(exit(atoi(str))); /* Send the string to plog() */ plog(str); /* Failure */ (void)(exit(EXIT_FAILURE)); } /* * Redefinable "core" action */ void (*core_aux) (cptr) = NULL; /* * Dump a core file, after printing a warning message * As with "quit()", try to use the "core_aux()" hook first. */ void core(cptr str) { char *crash = NULL; /* Use the aux function */ if (core_aux) (*core_aux) (str); /* Dump the warning string */ if (str) plog(str); /* Attempt to Crash */ (*crash) = (*crash); /* Be sure we exited */ quit("core() failed"); } zangband/src/z-virt.c0000755000000000000000000000511710250356275013542 0ustar rootroot/* File: z-virt.c */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* Purpose: Memory management routines -BEN- */ #include "z-virt.h" #include "z-util.h" /* * Optional auxiliary "rnfree" function */ vptr (*rnfree_aux) (vptr) = NULL; /* * Free some memory (allocated by ralloc), return NULL */ vptr rnfree(vptr p) { /* Easy to free nothing */ if (!p) return (NULL); /* Use the "aux" function */ if (rnfree_aux) return ((*rnfree_aux) (p)); /* Use "free" */ free((char *)(p)); /* Done */ return (NULL); } /* * Optional auxiliary "rpanic" function */ vptr (*rpanic_aux) (huge) = NULL; /* * The system is out of memory, so panic. If "rpanic_aux" is set, * it can be used to free up some memory and do a new "ralloc()", * or if not, it can be used to save things, clean up, and exit. * By default, this function simply crashes the computer. */ vptr rpanic(huge len) { /* Hopefully, we have a real "panic" function */ if (rpanic_aux) return ((*rpanic_aux) (len)); /* Attempt to crash before icky things happen */ core("Out of Memory!"); /* Paranoia */ return ((vptr)(NULL)); } /* * Optional auxiliary "ralloc" function */ vptr (*ralloc_aux) (huge) = NULL; /* * Allocate some memory */ vptr ralloc(huge len) { vptr mem; /* Allow allocation of "zero bytes" */ if (len == 0) return ((vptr)(NULL)); /* Use the aux function if set */ if (ralloc_aux) mem = (*ralloc_aux) (len); /* Use malloc() to allocate some memory */ else mem = ((vptr)(malloc((size_t) (len)))); /* We were able to acquire memory */ if (!mem) mem = rpanic(len); /* Return the memory, if any */ return (mem); } /* * Allocate a constant string, containing the same thing as 'str' */ cptr string_make(cptr str) { huge len = 0; cptr t = str; char *s, *res; /* Simple sillyness */ if (!str) return (str); /* Get the number of chars in the string, including terminator */ while (str[len++]) /* loop */ ; /* Allocate space for the string */ s = res = (char *)(ralloc(len)); /* Copy the string (with terminator) */ while ((*s++ = *t++) != 0) /* loop */ ; /* Return the allocated, initialized, string */ return (res); } /* * Un-allocate a string allocated above. * Depends on no changes being made to the string. */ errr string_free(cptr str) { /* Succeed on non-strings */ if (!str) return (0); /* Kill the buffer of chars we must have allocated above */ rnfree((vptr)str); /* Success */ return (0); } zangband/src/zbmagic1.c0000644000000000000000000026412510250356275014007 0ustar rootroot/* File: zbmagic1.c */ /* Purpose: Medium level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zbmagic.h" bool borg_desperate = FALSE; /* * This file is responsible for the low level dungeon goals. * * This includes calculating the danger from monsters, determining * how and when to attack monsters, and calculating "flow" paths * from place to place for various reasons. * * Notes: * We assume that invisible/offscreen monsters are dangerous * We consider physical attacks, missile attacks, spell attacks, * wand attacks, etc, as variations on a single theme. * We take account of monster resistances and susceptibilities * We try not to wake up sleeping monsters by throwing things * * * Bugs: * Currently the "twitchy()" function is not very smart * We get "twitchy" when we are afraid of the monsters * Annoyance and Danger are very different things (!) */ /* * Given a "source" and "target" locations, extract a "direction", * which will move one step from the "source" towards the "target". * * We prefer "non-diagonal" motion, which allows us to save the * "diagonal" moves for avoiding pillars and other obstacles. * * If no "obvious" path is available, we use "borg_extract_dir()". * * We return "5" if no motion is needed. */ int borg_goto_dir(int x1, int y1, int x2, int y2) { int d, e; int ay = (y2 > y1) ? (y2 - y1) : (y1 - y2); int ax = (x2 > x1) ? (x2 - x1) : (x1 - x2); map_block *mb_ptr; /* Default direction */ e = borg_extract_dir(x1, y1, x2, y2); /* Adjacent location, use default */ if ((ay <= 1) && (ay <= 1)) return (e); /* Try south/north (primary) */ if (ay > ax) { d = (y1 < y2) ? 2 : 8; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } } /* Try east/west (primary) */ if (ay < ax) { d = (x1 < x2) ? 6 : 4; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } } /* Try diagonal */ d = borg_extract_dir(x1, y1, x2, y2); /* Check for walls */ /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } /* Try south/north (secondary) */ if (ay <= ax) { d = (y1 < y2) ? 2 : 8; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } } /* Try east/west (secondary) */ if (ay >= ax) { d = (x1 < x2) ? 6 : 4; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } } /* Circle obstacles */ if (!ay) { /* Circle to the south */ d = (x1 < x2) ? 3 : 1; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } /* Circle to the north */ d = (x1 < x2) ? 9 : 7; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } } /* Circle obstacles */ if (!ax) { /* Circle to the east */ d = (y1 < y2) ? 3 : 9; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } /* Circle to the west */ d = (y1 < y2) ? 1 : 7; /* Bounds checking */ if (map_in_bounds(x1 + ddx[d], y1 + ddy[d])) { mb_ptr = map_loc(x1 + ddx[d], y1 + ddy[d]); if (borg_cave_floor_grid(mb_ptr)) return (d); } } /* Oops */ return (e); } /* * Attempt to induce "word of recall" * artifact activations added throughout this code */ bool borg_recall(void) { int wid, hgt; /* Get size */ Term_get_size(&wid, &hgt); /* Is the borg somewhere in the wilderness? */ if (borg_term_text_comp(wid - T_NAME_LEN, hgt - 1, "Wilderness")) return (FALSE); /* Multiple "recall" fails */ if (!goal_recalling) { /* Press an ESC to try to avoid the take-off loop */ borg_keypress(ESCAPE); /* Try to "recall" */ if (borg_zap_rod(SV_ROD_RECALL) || borg_activate(BORG_ACT_WORD_OF_RECALL) || borg_spell_fail(REALM_SORCERY, 2, 7, 60) || borg_spell_fail(REALM_ARCANE, 3, 6, 60) || borg_spell_fail(REALM_TRUMP, 1, 6, 60) || borg_mutation(MUT1_RECALL) || borg_read_scroll(SV_SCROLL_WORD_OF_RECALL)) { /* Always try to cancel the reset recall. */ borg_keypress(ESCAPE); /* Success */ return (TRUE); } } /* Nothing */ return (FALSE); } /* * Prevent starvation by any means possible */ bool borg_eat_food_any(void) { int i; list_item *l_ptr; object_kind *k_ptr; /* No help for some */ if (FLAG(bp_ptr, TR_CANT_EAT)) return (FALSE); /* Scan the inventory for "normal" food */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Skip empty / unknown items */ if (!l_ptr->k_idx) continue; /* Skip non-food */ if (l_ptr->tval != TV_FOOD) continue; /* Get kind */ k_ptr = &k_info[l_ptr->k_idx]; /* Skip "flavored" food */ if (k_ptr->sval < SV_FOOD_MIN_FOOD) continue; /* Eat something of that type */ if (borg_eat_food(k_ptr->sval)) return (TRUE); } /* Scan the inventory for "okay" food */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Skip empty / unknown items */ if (!l_ptr->k_idx) continue; /* Skip non-food */ if (l_ptr->tval != TV_FOOD) continue; /* Get kind */ k_ptr = &k_info[l_ptr->k_idx]; /* Skip "icky" food */ if (k_ptr->sval < SV_FOOD_MIN_FOOD) continue; /* Eat something of that type */ if (borg_eat_food(k_ptr->sval)) return (TRUE); } /* Scan the inventory for "potions" food */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Skip empty / unknown items */ if (!l_ptr->k_idx) continue; /* Skip non-potion */ if (l_ptr->tval != TV_POTION) continue; /* Consume in order, when hurting */ if (bp_ptr->chp < 4 && (borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_use_staff(SV_STAFF_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(TRUE) || borg_use_staff(SV_STAFF_CURING) || borg_quaff_potion(SV_POTION_RESTORE_MANA) || borg_quaff_potion(SV_POTION_HEALING) || borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_LIFE))) { return (TRUE); } } /* Nothing */ return (FALSE); } /* * Hack -- evaluate the likelihood of the borg getting surrounded * by a bunch of monsters. This is called from borg_danger() when * he looking for a strategic retreat. It is hopeful that the borg * will see that several monsters are approaching him and he may * become surrouned then die. This routine looks at near by monsters * as determines the likelyhood of him getting surrouned. */ bool borg_surrounded(void) { borg_kill *kill; monster_race *r_ptr; int safe_grids = 8; int non_safe_grids = 0; int monsters = 0; int adjacent_monsters = 0; int x9, y9, ax, ay, d; int i; /* Evaluate the local monsters */ for (i = 1; i < borg_kills_nxt; i++) { kill = &borg_kills[i]; r_ptr = &r_info[kill->r_idx]; /* Skip dead monsters */ if (!kill->r_idx) continue; x9 = kill->x; y9 = kill->y; /* Distance components */ ax = (x9 > c_x) ? (x9 - c_x) : (c_x - x9); ay = (y9 > c_y) ? (y9 - c_y) : (c_y - y9); /* Distance */ d = MAX(ax, ay); /* if the monster is too far then skip it. */ if (d > 3) continue; /* if he cant see me then forget it. */ if (!borg_los(c_x, c_y, x9, y9)) continue; /* if asleep, don't consider this one */ if (kill->m_flags & MONST_ASLEEP) continue; /* Monsters with Pass Wall are dangerous, no escape from them */ if (FLAG(r_ptr, RF_PASS_WALL)) continue; if (FLAG(r_ptr, RF_KILL_WALL)) continue; /* Monsters who never move cant surround */ if (FLAG(r_ptr, RF_NEVER_MOVE)) continue; /* keep track of monsters touching me */ if (d == 1) adjacent_monsters++; /* Add them up. */ monsters++; } /* Evaluate the Non Safe Grids, (walls, closed doors, traps, monsters) */ for (i = 0; i < 8; i++) { int x = c_x + ddx_ddd[i]; int y = c_y + ddy_ddd[i]; map_block *mb_ptr; if (!map_in_bounds(x, y)) continue; /* Access the grid */ mb_ptr = map_loc(x, y); /* Non Safe grids */ if (borg_cave_wall_grid(mb_ptr)) non_safe_grids++; /* Skip unknown grids */ if (!mb_ptr->feat) non_safe_grids++; /* Skip monster grids */ if (mb_ptr->monster) non_safe_grids++; /* MT - Skip trap grids */ if (mb_ptr->trap) non_safe_grids++; } /* Safe grids are decreased */ safe_grids = safe_grids - non_safe_grids; /* Am I in hallway? If so don't worry about it */ if (safe_grids == 1 && adjacent_monsters == 1) return (FALSE); /* I am likely to get surrouned */ if (monsters > safe_grids) { borg_note("# Possibility of being surrounded (%d/%d)", monsters, safe_grids); /* The borg can get trapped by breeders by continueing to flee * into a dead-end. So he needs to be able to trump this * routine. */ if (goal_ignoring) return (FALSE); else return (TRUE); } /* Probably will not be surrouned */ return (FALSE); } /* * Mega-Hack -- evaluate the "freedom" of the given location * * The theory is that often, two grids will have equal "danger", * but one will be "safer" than the other, perhaps because it * is closer to stairs, or because it is in a corridor, or has * some other characteristic that makes it "safer". * * Then, if the Borg is in danger, say, from a normal speed monster * which is standing next to him, he will know that walking away from * the monster is "pointless", because the monster will follow him, * but if the resulting location is "safer" for some reason, then * he will consider it. This will allow him to flee towards stairs * in the town, and perhaps towards corridors in the dungeon. * * This method is used in town to chase the stairs. * * XXX XXX XXX We should attempt to walk "around" buildings. */ int borg_freedom(int x, int y) { int d, f = 0; /* Hack -- chase down stairs in town */ if (!bp_ptr->depth && track_more_num) { /* Love the stairs! */ d = double_distance(y, x, track_more_y[0], track_more_x[0]); /* Proximity is good */ f += (1000 - d); /* Close proximity is great */ if (d < 4) f += (2000 - (d * 500)); } /* Hack -- chase Up Stairs in dungeon */ if (bp_ptr->depth && track_less_num) { /* Love the stairs! */ d = double_distance(y, x, track_less_y[0], track_less_x[0]); /* Proximity is good */ f += (1000 - d); /* Close proximity is great */ if (d < 4) f += (2000 - (d * 500)); } /* Freedom */ return (f); } /* * Check a floor grid for "happy" status * * These grids are floor grids which contain stairs, or which * are non-corners in corridors, or which are directly adjacent * to pillars, or grids which we have stepped on before. * Stairs are good because they can be used to leave * the level. Corridors are good because you can back into them * to avoid groups of monsters and because they can be used for * escaping. Pillars are good because while standing next to a * pillar, you can walk "around" it in two different directions, * allowing you to retreat from a single normal monster forever. * Stepped on grids are good because they likely stem from an area * which has been cleared of monsters. */ bool borg_happy_grid_bold(int x, int y) { int i; map_block *mb_ptr; /* Bounds checking */ if (!map_in_bounds(x, y)) return (FALSE); mb_ptr = map_loc(x, y); /* Accept stairs */ if (mb_ptr->feat == FEAT_LESS) return (TRUE); if (mb_ptr->feat == FEAT_MORE) return (TRUE); /* Accept Glyphs */ if (mb_ptr->m_effect) return (TRUE); /* Hack -- weak/dark is very unhappy */ if (bp_ptr->status.weak || !bp_ptr->cur_lite) return (FALSE); /* Apply a control effect so that he does not get stuck in a loop */ if ((borg_t - borg_began) >= 2000) return (FALSE); /* Case 1a: north-south corridor */ if (borg_cave_floor_bold(y - 1, x) && borg_cave_floor_bold(y + 1, x) && !borg_cave_floor_bold(y, x - 1) && !borg_cave_floor_bold(y, x + 1) && !borg_cave_floor_bold(y + 1, x - 1) && !borg_cave_floor_bold(y + 1, x + 1) && !borg_cave_floor_bold(y - 1, x - 1) && !borg_cave_floor_bold(y - 1, x + 1)) { /* Happy */ return (TRUE); } /* Case 1b: east-west corridor */ if (borg_cave_floor_bold(y, x - 1) && borg_cave_floor_bold(y, x + 1) && !borg_cave_floor_bold(y - 1, x) && !borg_cave_floor_bold(y + 1, x) && !borg_cave_floor_bold(y + 1, x - 1) && !borg_cave_floor_bold(y + 1, x + 1) && !borg_cave_floor_bold(y - 1, x - 1) && !borg_cave_floor_bold(y - 1, x + 1)) { /* Happy */ return (TRUE); } /* Case 1aa: north-south doorway */ if (borg_cave_floor_bold(y - 1, x) && borg_cave_floor_bold(y + 1, x) && !borg_cave_floor_bold(y, x - 1) && !borg_cave_floor_bold(y, x + 1)) { /* Happy */ return (TRUE); } /* Case 1ba: east-west doorway */ if (borg_cave_floor_bold(y, x - 1) && borg_cave_floor_bold(y, x + 1) && !borg_cave_floor_bold(y - 1, x) && !borg_cave_floor_bold(y + 1, x)) { /* Happy */ return (TRUE); } /* Case 2a: north pillar */ if (!borg_cave_floor_bold(y - 1, x) && borg_cave_floor_bold(y - 1, x - 1) && borg_cave_floor_bold(y - 1, x + 1) && borg_cave_floor_bold(y - 2, x)) { /* Happy */ return (TRUE); } /* Case 2b: south pillar */ if (!borg_cave_floor_bold(y + 1, x) && borg_cave_floor_bold(y + 1, x - 1) && borg_cave_floor_bold(y + 1, x + 1) && borg_cave_floor_bold(y + 2, x)) { /* Happy */ return (TRUE); } /* Case 2c: east pillar */ if (!borg_cave_floor_bold(y, x + 1) && borg_cave_floor_bold(y - 1, x + 1) && borg_cave_floor_bold(y + 1, x + 1) && borg_cave_floor_bold(y, x + 2)) { /* Happy */ return (TRUE); } /* Case 2d: west pillar */ if (!borg_cave_floor_bold(y, x - 1) && borg_cave_floor_bold(y - 1, x - 1) && borg_cave_floor_bold(y + 1, x - 1) && borg_cave_floor_bold(y, x - 2)) { /* Happy */ return (TRUE); } /* check for grids that have been stepped on before */ for (i = 0; i < track_step_num; i++) { /* Enqueue the grid */ if ((track_step_y[i] == y) && (track_step_x[i] == x)) { /* Recent step is good */ if (i < 25) { return (TRUE); } } } /* Not happy */ return (FALSE); } /* * Target a location. Can be used alone or at "Direction?" prompt. * * Warning -- This will only work for locations on the current panel. * So before you call this be sure there was a call to map_in_bounds. */ void borg_target(int x, int y) { int x1, y1, x2, y2; map_block *mb_ptr; /* Bounds checking */ if (!map_in_bounds(x, y)) { borg_oops("Untargettable location (%d, %d)", x, y); return; } /* Get the grid */ mb_ptr = map_loc(x, y); /* Report a little bit */ if (mb_ptr->monster) { borg_note("# Targeting %s, from (%d, %d) to (%d, %d).", mon_race_name(&r_info[mb_ptr->monster]), c_x, c_y, x, y); } else { borg_note("# Targetting location from (%d, %d) to (%d,%d)", c_x, c_y, x, y); } /* Target mode */ borg_keypress('*'); /* Target a location */ borg_keypress('p'); /* Determine "path" */ x1 = c_x; y1 = c_y; x2 = x; y2 = y; /* Move to the location (diagonals) */ for (; (y1 < y2) && (x1 < x2); y1++, x1++) borg_keypress('3'); for (; (y1 < y2) && (x1 > x2); y1++, x1--) borg_keypress('1'); for (; (y1 > y2) && (x1 < x2); y1--, x1++) borg_keypress('9'); for (; (y1 > y2) && (x1 > x2); y1--, x1--) borg_keypress('7'); /* Move to the location */ for (; y1 < y2; y1++) borg_keypress('2'); for (; y1 > y2; y1--) borg_keypress('8'); for (; x1 < x2; x1++) borg_keypress('6'); for (; x1 > x2; x1--) borg_keypress('4'); /* Select the target */ borg_keypress('5'); /* Success */ return; } static bool test_borg_lite_beam(byte dir, byte radius) { int x = c_x, y = c_y; int dx = 0, dy = 0; int i; map_block *mb_ptr; switch (dir) { case 8: { /* North */ dx = 0; dy = -1; break; } case 6: { /* East */ dx = 1; dy = 0; break; } case 2: { /* South */ dx = 0; dy = 1; break; } case 4: { /* West */ dx = -1; dy = 0; break; } } for (i = 0; i < radius; i++) { x += dx; y += dy; /* No need to light beyond the map */ if (!map_in_bounds(x, y)) return (FALSE); mb_ptr = map_loc(x, y); /* Walls aren't interesting to light */ if (borg_cave_wall_grid(mb_ptr)) return (FALSE); /* Unlit square just out of los? */ if (!mb_ptr->feat) return (TRUE); } /* Nothing interesting just out of los */ return (FALSE); } /* * This will look down a hallway and possibly light it up using * the Light Beam mage spell. This spell is mostly used when * the borg is moving through the dungeon under boosted bravery. * This will allow him to "see" if anyone is there. * * It might also come in handy if he's in a hallway and gets shot, or * if resting in a hallway. He may want to cast it to make * sure no previously unknown monsters are in the hall. * NOTE: ESP will alter the value of this spell. * * Borg has a problem when not on map centering mode and casting the beam * repeatedly, down or up when at the edge of a panel. */ bool borg_lite_beam(bool simulation, int *dir) { if (simulation) { /* Hack -- weak/dark is very unhappy */ if (bp_ptr->status.weak || !bp_ptr->cur_lite) return (FALSE); /* Apply a control effect so that he does not get stuck in a loop */ if ((borg_t - borg_began) >= 2000) return (FALSE); /* Require the ability */ if (!borg_spell_okay_fail(REALM_NATURE, 1, 4, 20) && !borg_spell_okay_fail(REALM_ARCANE, 2, 4, 20) && !borg_equips_wand_fail(SV_WAND_LITE) && !borg_equips_rod_fail(SV_ROD_LITE)) return (FALSE); /* Set the direction to nowhere */ *dir = 5; /* North */ if (test_borg_lite_beam(8, MAX_RANGE)) *dir = 8; /* East */ else if (test_borg_lite_beam(6, MAX_RANGE)) *dir = 6; /* West */ else if (test_borg_lite_beam(4, MAX_RANGE)) *dir = 4; /* South */ else if (test_borg_lite_beam(2, MAX_RANGE)) *dir = 2; /* Failure? */ if (*dir == 5) return (FALSE); /* simulation */ return (TRUE); } /* Drop old target */ borg_keypress('*'); borg_keypress(ESCAPE); /* cast the light beam */ if (borg_spell(REALM_NATURE, 1, 4) || borg_spell(REALM_ARCANE, 2, 5) || borg_zap_rod(SV_ROD_LITE) || borg_aim_wand(SV_WAND_LITE)) { /* apply the direction */ borg_keypress(I2D(*dir)); /* Tell what you do */ borg_note("# Illuminating this hallway, dir = %d", *dir); /* Leave */ return (TRUE); } /* Huh? One of the three spells must be available! */ borg_oops("Not supposed to happen!"); /* can't do it */ return (FALSE); } /* * Scan the monster lists for certain types of monster that we * should be concerned over. * This only works for monsters we know about. If one of the * monsters around is misidentified then it may be a unique * and we wouldn't know. Special consideration is given to The Serpent */ void borg_near_monster_type(int dist) { borg_kill *kill; monster_race *r_ptr; int x9, y9, ax, ay, d; int i; /* reset the borg flags */ borg_fighting_unique = 0; borg_fighting_evil_unique = FALSE; /* Scan the monsters */ for (i = 1; i < borg_kills_nxt; i++) { kill = &borg_kills[i]; r_ptr = &r_info[kill->r_idx]; /* Skip dead monsters */ if (!kill->r_idx) continue; x9 = kill->x; y9 = kill->y; /* Distance components */ ax = (x9 > c_x) ? (x9 - c_x) : (c_x - x9); ay = (y9 > c_y) ? (y9 - c_y) : (c_y - y9); /* Distance */ d = MAX(ax, ay); /* if the guy is too far then skip it. */ if (d > dist) continue; /*** Scan for Uniques ***/ /* this is a unique. */ if (FLAG(r_ptr, RF_UNIQUE)) { /* Set a flag for use with certain types of spells */ unique_on_level = TRUE; /* Remember which unique */ unique_r_idx = kill->r_idx; /* return 1 if not Serpent, +101 if it is Serpent or Oberon */ if (FLAG(r_ptr, RF_QUESTOR)) { /* A Questor adds a BORG_QUESTOR + 1 value */ borg_fighting_unique += BORG_QUESTOR; } /* regular unique */ borg_fighting_unique++; /* Is there an evil unique */ if (FLAG(r_ptr, RF_EVIL)) borg_fighting_evil_unique = TRUE; } } } /* * Help determine if "phase door" seems like a good idea */ bool borg_caution_phase(int emergency, int turns) { int n, k, i, d, x, y, p; int dis = 10; int min; map_block *mb_ptr = map_loc(c_x, c_y); /* Define minimal distance */ min = dis / 2; /* Simulate 100 attempts */ for (n = k = 0; k < 100; k++) { /* Pick a location */ for (i = 0; i < 100; i++) { /* Pick a (possibly illegal) location */ while (1) { y = rand_spread(c_y, dis); x = rand_spread(c_x, dis); d = distance(c_y, c_x, y, x); if ((d >= min) && (d <= dis)) break; } if (!map_in_bounds(x, y)) continue; /* Access */ mb_ptr = map_loc(x, y); /* If low level, unknown squares are scary */ if (!mb_ptr->feat && bp_ptr->mhp < 30) break; /* Skip unknown grids */ if (!mb_ptr->feat) continue; /* Skip walls */ if (borg_cave_wall_grid(mb_ptr)) continue; /* Skip monsters */ if (mb_ptr->monster) continue; /* Stop looking */ break; } /* If low level, unknown squares are scary */ if (!mb_ptr->feat && bp_ptr->mhp < 30) { n++; continue; } /* No location */ /* in the real code it would keep trying but here we should */ /* assume that there is unknown spots that you would be able */ /* to go but may be dangerious. */ if (i >= 100) { n++; continue; } /* Examine */ p = borg_danger(x, y, turns, TRUE); /* if *very* scary, do not allow jumps at all */ if (p > bp_ptr->chp) n++; } /* Too much danger */ /* in an emergency try with extra danger allowed */ if (n > emergency) { borg_note("# No Phase. scary squares: %d", n); return (FALSE); } else borg_note("# Safe to Phase. scary squares: %d", n); /* Okay */ return (TRUE); } /* * Help determine if "dimension door" seems like a good idea */ static bool borg_dim_door(int emergency, int p1) { int x, y, p; int b_y = -1, b_x = -1, b_p = p1; int dis = bp_ptr->lev + 2; map_block *mb_ptr = map_loc(c_x, c_y); /* Scan every grid in landing zone */ for (y = c_y - dis; y < c_y + dis; y++) { /* Pick a location */ for (x = c_x - dis; x < c_x + dis; x++) { if (!map_in_bounds(x, y)) continue; if ((x == c_x) && (y == c_y)) continue; /* Access */ mb_ptr = map_loc(x, y); /* Verify distance again */ if (distance(y, x, c_y, c_x) > bp_ptr->lev + 2) continue; /* Skip unknown grids */ if (!mb_ptr->feat) continue; /* Skip walls, trees, water, lava */ if (borg_cave_wall_grid(mb_ptr)) continue; /* Skip monsters */ if (mb_ptr->monster) continue; /* Examine */ p = borg_danger(x, y, 1, TRUE); /* if *very* scary, do not allow jumps at all */ if (!emergency && p > bp_ptr->chp) continue; /* Track the grid with the least danger */ if (p > b_p) continue; /* note good landing zones */ b_p = p; b_y = y; b_x = x; } } /* Dimension Door report */ borg_note ("# Dim Door: Safest grid: (%d, %d) with %d Danger", b_y, b_x, b_p); dim_door_y = b_y; dim_door_x = b_x; /* No good landing zone */ if (b_p >= p1) return (FALSE); /* Okay */ return (TRUE); } /* Just in case the key changes again */ void borg_press_faint_accept(void) { borg_keypress('y'); } /* * Try to phase door or teleport * b_q is the danger of the least dangerious square around us. */ bool borg_escape(int b_q) { /* Risky borgs are more likely to stay in a fight */ int risky_boost = 3; bool amt_dim_door = FALSE; /* only escape with spell if fail is low */ int allow_fail = 25; int sv_mana; /* Not if locked down */ if (FLAG(bp_ptr, TR_NO_TELE)) return (FALSE); /* if we have Dim Door spell */ amt_dim_door = (borg_activate_fail(BORG_ACT_DIM_DOOR) || borg_spell_okay_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_okay_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_okay_fail(MIND_MINOR_DISP, 40, allow_fail)); /* if very healthy, allow extra fail */ if (((bp_ptr->chp * 100) / bp_ptr->mhp) > 70) allow_fail = 10; /* comprimised, get out of the fight */ if (bp_ptr->status.heavy_stun) allow_fail = 35; /* for emergencies */ sv_mana = bp_ptr->csp; /* Borgs who are bleeding to death or dying of poison may sometimes * phase around the last two hit points right before they enter a * shop. He knows to make a bee-line for the temple but the danger * trips this routine. So we must bypass this routine for some * particular circumstances. */ if (!bp_ptr->depth && (bp_ptr->status.poisoned || bp_ptr->status.weak || bp_ptr->status.cut)) return (FALSE); /* Borgs with GOI should not escape until the GOI falls */ if (borg_goi) return (FALSE); /* 1. really scary, I'm about to die */ /* Try an emergency teleport, or phase door as last resort */ if (bp_ptr->status.heavy_stun || (b_q >= avoidance * (45 + risky_boost) / 10) || ((b_q >= avoidance * (40 + risky_boost) / 10) && borg_fighting_unique >= BORG_QUESTOR && bp_ptr->depth == 100 && bp_ptr->chp < 600) || ((b_q >= avoidance * (30 + risky_boost) / 10) && borg_fighting_unique >= BORG_QUESTOR && bp_ptr->depth == 99 && bp_ptr->chp < 600) || ((b_q >= avoidance * (25 + risky_boost) / 10) && borg_fighting_unique >= 1 && borg_fighting_unique <= 8 && bp_ptr->depth >= 95 && bp_ptr->chp < 550) || ((b_q >= avoidance * (17 + risky_boost) / 10) && borg_fighting_unique >= 1 && borg_fighting_unique <= 8 && bp_ptr->depth < 95) || ((b_q >= avoidance * (15 + risky_boost) / 10) && !borg_fighting_unique)) { int allow_fail = 11; if (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail - 10) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail - 10) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail - 10) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail - 10) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail - 10) || borg_read_scroll(SV_SCROLL_TELEPORT) || borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_activate(BORG_ACT_TELEPORT) || /* revisit spells, increased fail rate */ borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail + 9) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail + 9) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail + 9) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail + 9) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail + 9) || borg_racial(RACE_GNOME) || borg_mutation(MUT1_VTELEPORT) || /* Attempt Teleport Level */ (bp_ptr->depth && (borg_activate(BORG_ACT_TELEPORT_LEVEL) || borg_spell_fail(REALM_SORCERY, 2, 6, allow_fail + 9) || borg_spell_fail(REALM_TRUMP, 1, 5, allow_fail + 9) || borg_spell_fail(REALM_ARCANE, 3, 1, allow_fail + 9) || borg_racial(RACE_AMBERITE) || borg_read_scroll(SV_SCROLL_TELEPORT_LEVEL))) || /* try Dimension Door */ (amt_dim_door && borg_dim_door(TRUE, b_q) && (borg_activate(BORG_ACT_DIM_DOOR) || borg_spell_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 40, allow_fail))) || /* try phase at least */ borg_read_scroll(SV_SCROLL_PHASE_DOOR) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail)) { /* Flee! */ borg_note("# Danger Level 1."); return (TRUE); } /* try to teleport, get far away from here */ if (borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_TELEPORT)) { /* Flee! */ borg_note("# Danger Level 1.1 Critical Attempt"); return (TRUE); } bp_ptr->csp = bp_ptr->msp; if (borg_spell(REALM_ARCANE, 2, 3) || borg_spell(REALM_TRUMP, 0, 4) || borg_spell(REALM_CHAOS, 0, 7) || borg_spell(REALM_SORCERY, 0, 5)) { /* verify use of spell */ borg_press_faint_accept(); /* Flee! */ borg_note("# Danger Level 1.1 Critical Attempt"); return (TRUE); } /* emergency phase spell */ if ((bp_ptr->able.phase && borg_caution_phase(80, 5) && (borg_read_scroll(SV_SCROLL_PHASE_DOOR) || borg_activate(BORG_ACT_PHASE_DOOR))) || borg_activate(BORG_ACT_TELEPORT_LEVEL) || borg_read_scroll(SV_SCROLL_TELEPORT_LEVEL)) { /* Flee! */ borg_escapes--; /* a phase isn't really an escape */ borg_note("# Danger Level 1.2 Critical Phase"); return (TRUE); } /* emergency phase spell */ if (borg_caution_phase(80, 5) && (borg_spell_fail(REALM_ARCANE, 0, 4, 15) || borg_spell_fail(REALM_SORCERY, 0, 1, 15) || borg_spell_fail(REALM_TRUMP, 0, 0, 15))) { /* verify use of spell */ borg_press_faint_accept(); /* Flee! */ borg_note("# Danger Level 1.3 Critical Attempt"); return (TRUE); } bp_ptr->csp = sv_mana; } /* If fighting a unique and at the end of the game try to stay and * finish the fight. Only bail out in extreme danger as above. */ if ((b_q < avoidance * (25 + risky_boost) / 10 && borg_fighting_unique >= 1 && borg_fighting_unique <= 3 && bp_ptr->depth >= 97) || bp_ptr->chp > 550) return (FALSE); /* 2 - a bit more scary */ /* Attempt to teleport (usually) */ /* do not escape from uniques so quick */ if (bp_ptr->status.heavy_stun || ((b_q >= avoidance * (15 + risky_boost) / 10) && borg_fighting_unique >= 1 && borg_fighting_unique <= 8 && bp_ptr->depth != 99) || ((b_q >= avoidance * (13 + risky_boost) / 10) && !borg_fighting_unique)) { /* Try teleportation */ if (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail - 10) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail - 10) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail - 10) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail - 10) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail - 10) || borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_TELEPORT) || borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail) || borg_racial(RACE_GNOME) || borg_mutation(MUT1_VTELEPORT) || /* try Dimension Door */ (amt_dim_door && borg_dim_door(TRUE, b_q) && (borg_activate(BORG_ACT_DIM_DOOR) || borg_spell_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 40, allow_fail)))) { /* Flee! */ borg_note("# Danger Level 2.1"); /* Success */ return (TRUE); } /* Phase door, if useful */ if (bp_ptr->able.phase && borg_caution_phase(50, 2) && (borg_read_scroll(SV_SCROLL_PHASE_DOOR) || borg_spell(REALM_ARCANE, 0, 4) || borg_spell(REALM_SORCERY, 0, 1) || borg_spell(REALM_TRUMP, 0, 0) || borg_mindcr(MIND_MINOR_DISP, 3) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT))) { /* Flee! */ borg_note("# Danger Level 2.2"); /* Success */ return (TRUE); } } /* 3- not too bad */ /* also run if stunned or it is scary here */ if (bp_ptr->status.heavy_stun || ((b_q >= avoidance * (13 + risky_boost) / 10) && borg_fighting_unique >= 2 && borg_fighting_unique <= 8) || ((b_q >= avoidance * (10 + risky_boost) / 10) && !borg_fighting_unique) || ((b_q >= avoidance * (10 + risky_boost) / 10) && bp_ptr->status.afraid && !bp_ptr->able.missile && (borg_class == CLASS_WARRIOR))) { /* try Dimension Door */ if ((amt_dim_door && borg_dim_door(TRUE, b_q) && (borg_activate(BORG_ACT_DIM_DOOR) || borg_spell_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 40, allow_fail))) || /* Phase door, if useful */ (bp_ptr->able.phase && borg_caution_phase(25, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR)))) { /* Flee! */ borg_escapes--; /* a phase isn't really an escape */ borg_note("# Danger Level 3.1"); /* Success */ return (TRUE); } /* Teleport via spell */ if (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail) || borg_activate(BORG_ACT_TELEPORT) || borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_read_scroll(SV_SCROLL_TELEPORT) || borg_mutation(MUT1_VTELEPORT) || borg_racial(RACE_GNOME)) { /* Flee! */ borg_note("# Danger Level 3.2"); /* Success */ return (TRUE); } /* Phase door, if useful */ if (bp_ptr->able.phase && borg_caution_phase(65, 2) && (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Flee! */ borg_escapes--; /* a phase isn't really an escape */ borg_note("# Danger Level 3.3"); /* Success */ return (TRUE); } /* if we got this far we tried to escape but couldn't... */ /* time to flee */ if (!goal_fleeing && (!borg_fighting_unique || bp_ptr->lev < 35) && !vault_on_level) { /* Note */ borg_note("# Fleeing (failed to teleport)"); /* Start fleeing */ goal_fleeing = TRUE; } /* Flee now */ if (!goal_leaving && (!borg_fighting_unique || bp_ptr->lev < 35) && !vault_on_level) { /* Flee! */ borg_note("# Leaving (failed to teleport)"); /* Start leaving */ goal_leaving = TRUE; } } /* 4- not too scary but I'm comprimized */ if ((b_q >= avoidance * (8 + risky_boost) / 10 && (bp_ptr->lev < 35 || bp_ptr->chp <= bp_ptr->mhp / 3)) || ((b_q >= avoidance * (12 + risky_boost) / 10) && borg_fighting_unique >= 1 && borg_fighting_unique <= 8 && (bp_ptr->lev < 35 || bp_ptr->chp <= bp_ptr->mhp / 3)) || ((b_q >= avoidance * (6 + risky_boost) / 10) && bp_ptr->lev <= 20 && !borg_fighting_unique) || ((b_q >= avoidance * (6 + risky_boost) / 10) && borg_class == CLASS_MAGE && bp_ptr->lev <= 35)) { /* Phase door, if useful */ if ((amt_dim_door && borg_dim_door(TRUE, b_q) && (borg_activate(BORG_ACT_DIM_DOOR) || borg_spell_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 40, allow_fail))) || (bp_ptr->able.phase && borg_caution_phase(20, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR)))) { /* Flee! */ borg_escapes--; /* a phase isn't really an escape */ borg_note("# Danger Level 4.1"); /* Success */ return (TRUE); } /* Teleport via spell */ if (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_TELEPORT) || borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_mutation(MUT1_VTELEPORT) || borg_racial(RACE_GNOME)) { /* Flee! */ borg_note("# Danger Level 4.2"); /* Success */ return (TRUE); } /* if we got this far we tried to escape but couldn't... */ /* time to flee */ if (!goal_fleeing && !borg_fighting_unique && bp_ptr->lev < 25 && !vault_on_level) { /* Note */ borg_note("# Fleeing (failed to teleport)"); /* Start fleeing */ goal_fleeing = TRUE; } /* Flee now */ if (!goal_leaving && !borg_fighting_unique && !vault_on_level) { /* Flee! */ borg_note("# Leaving (failed to teleport)"); /* Start leaving */ goal_leaving = TRUE; } /* Emergency Phase door if a weak mage */ if ((borg_class == CLASS_MAGE && bp_ptr->lev <= 35) && bp_ptr->able.phase && borg_caution_phase(65, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Flee! */ borg_escapes--; /* a phase isn't really an escape */ borg_note("# Danger Level 4.3"); /* Success */ return (TRUE); } } /* 5- not too scary but I'm very low level */ if (bp_ptr->lev < 10 && (b_q >= avoidance * (6 + risky_boost) / 10 || (b_q >= avoidance * (8 + risky_boost) / 10 && borg_fighting_unique >= 1 && borg_fighting_unique <= 8))) { /* Dimension Door, if useful */ if ((amt_dim_door && borg_dim_door(TRUE, b_q) && (borg_activate(BORG_ACT_DIM_DOOR) || borg_spell_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 40, allow_fail))) || /* Phase Door */ (bp_ptr->able.phase && borg_caution_phase(20, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR)))) { /* Flee! */ borg_note("# Danger Level 5.1"); /* Success */ return (TRUE); } /* Teleport via spell */ if (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_TELEPORT) || borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_mutation(MUT1_VTELEPORT) || borg_racial(RACE_GNOME)) { /* Flee! */ borg_note("# Danger Level 5.2"); /* Success */ return (TRUE); } /* if we got this far we tried to escape but couldn't... */ /* time to flee */ if (!goal_fleeing && !borg_fighting_unique) { /* Note */ borg_note("# Fleeing (failed to teleport)"); /* Start fleeing */ goal_fleeing = TRUE; } /* Flee now */ if (!goal_leaving && !borg_fighting_unique) { /* Flee! */ borg_note("# Leaving (failed to teleport)"); /* Start leaving */ goal_leaving = TRUE; } /* Emergency Phase door if a weak mage */ if ((borg_class == CLASS_MAGE && bp_ptr->lev <= 8) && bp_ptr->able.phase && borg_caution_phase(65, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Flee! */ borg_escapes--; /* a phase isn't really an escape */ borg_note("# Danger Level 5.3"); /* Success */ return (TRUE); } } /* 6- not too scary but I'm out of mana */ if ((borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE || borg_class == CLASS_PRIEST || borg_class == CLASS_MINDCRAFTER) && (b_q >= avoidance * (6 + risky_boost) / 10 || (b_q >= avoidance * (8 + risky_boost) / 10 && borg_fighting_unique >= 1 && borg_fighting_unique <= 8)) && (bp_ptr->csp <= (bp_ptr->msp * 1 / 10) && bp_ptr->msp >= 100)) { /* Dimension Door, if useful */ if ((amt_dim_door && borg_dim_door(TRUE, b_q) && (borg_activate(BORG_ACT_DIM_DOOR) || borg_spell_fail(REALM_SORCERY, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 40, allow_fail))) || /* Phase Door */ (bp_ptr->able.phase && borg_caution_phase(20, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 1, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 0, allow_fail) || borg_mindcr_fail(MIND_MINOR_DISP, 3, allow_fail) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR)))) { /* Flee! */ borg_note("# Danger Level 6.1"); /* Success */ return (TRUE); } /* Teleport via spell */ if (borg_spell_fail(REALM_ARCANE, 2, 3, allow_fail) || borg_spell_fail(REALM_TRUMP, 0, 4, allow_fail) || borg_spell_fail(REALM_CHAOS, 0, 7, allow_fail) || borg_spell_fail(REALM_SORCERY, 0, 5, allow_fail) || borg_mindcr_fail(MIND_MAJOR_DISP, 7, allow_fail) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_TELEPORT) || borg_use_staff_fail(SV_STAFF_TELEPORTATION) || borg_mutation(MUT1_VTELEPORT) || borg_racial(RACE_GNOME)) { /* Flee! */ borg_note("# Danger Level 6.2"); /* Success */ return (TRUE); } } return (FALSE); } /* * ** Try healing ** * this function tries to heal the borg before trying to flee. * The ez_heal items (*Heal* and Life) are reserved for The Serpent. * In severe emergencies the borg can drink an ez_heal item but that is * checked in borg_caution(). He should bail out of the fight before * using an ez_heal. */ bool borg_heal(int danger) { int hp_down; int allow_fail = 20; int chance; int stats_needing_fix = 0; int i; map_block *mb_ptr = map_loc(c_x, c_y); hp_down = bp_ptr->mhp - bp_ptr->chp; /* * When fighting The Serpent, we want the borg to use Life potion to fix his * stats. So we need to add up the ones that are dropped. */ for (i = 0; i < A_MAX; i++) { if (bp_ptr->status.fixstat[i]) stats_needing_fix++; } /* Special cases get a second vote */ if ((borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE) && bp_ptr->status.fixstat[A_INT]) stats_needing_fix++; if ((borg_class == CLASS_PRIEST || borg_class == CLASS_MINDCRAFTER) && bp_ptr->status.fixstat[A_WIS]) stats_needing_fix++; if (!borg_has_realm(REALM_LIFE) && bp_ptr->status.fixstat[A_CON]) stats_needing_fix++; if (bp_ptr->mhp <= 850 && bp_ptr->status.fixstat[A_CON]) { stats_needing_fix++; } if (bp_ptr->mhp <= 700 && bp_ptr->status.fixstat[A_CON]) { stats_needing_fix += 3; } if ((borg_class == CLASS_PRIEST || borg_class == CLASS_MINDCRAFTER) && bp_ptr->msp < 100 && bp_ptr->status.fixstat[A_WIS]) stats_needing_fix += 5; if ((borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE) && bp_ptr->msp < 100 && bp_ptr->status.fixstat[A_INT]) stats_needing_fix += 5; /* * Hack -- heal when confused. This is deadly. * * This is checked twice, once, here, to see if he is in low danger * and again at the end of borg_caution, when all other avenues have failed */ if (bp_ptr->status.confused && (randint0(100) < 85)) { if ((hp_down >= 300) && danger - 300 < bp_ptr->chp && borg_quaff_potion(SV_POTION_HEALING)) { borg_note("# Fixing Confusion. Level 1"); return (TRUE); } if (danger - 20 < bp_ptr->chp && (borg_eat_food(SV_FOOD_CURE_CONFUSION) || borg_activate(BORG_ACT_HEAL_BIG) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(FALSE) || borg_quaff_potion(SV_POTION_HEALING) || borg_use_staff_fail(SV_STAFF_HEALING) || borg_use_staff_fail(SV_STAFF_CURING))) { borg_note("# Fixing Confusion. Level 2"); return (TRUE); } /* * If my ability to use a teleport staff is really * bad, then I should heal up then use the staff. */ /* Check for a charged teleport staff */ if (borg_equips_staff_fail(SV_STAFF_TELEPORTATION)) { /* Check my skill, drink a potion */ if ((bp_ptr->skill_dev - borg_get_kind(TV_STAFF, SV_STAFF_TELEPORTATION)-> level > 7) && (danger < (avoidance + 35) * 15 / 10) && (borg_quaff_crit(FALSE) || borg_quaff_potion(SV_POTION_HEALING))) { borg_note("# Fixing Confusion. Level 3"); return (TRUE); } /* * However, if I am in really big trouble and there is * no way I am going to be able to * survive another round, take my chances on the staff. */ else if (danger >= avoidance * 15 / 10) { borg_note("# Too scary to fix Confusion. Level 4"); return (FALSE); } } } /* Hack -- heal when blind. This is deadly. */ if (bp_ptr->status.blind && (randint0(100) < 85)) { /* * If in extreme danger, use teleport then fix the * blindness later. */ if (danger > avoidance * 25 / 10) { /* Check for a charged teleport staff */ if (borg_equips_staff_fail(SV_STAFF_TELEPORTATION)) return (0); } if ((hp_down >= 300) && borg_quaff_potion(SV_POTION_HEALING)) { return (TRUE); } /* Warriors with ESP won't need it so quickly */ if (!(borg_class == CLASS_WARRIOR && bp_ptr->chp > bp_ptr->mhp / 4 && FLAG(bp_ptr, TR_TELEPATHY))) { if (borg_eat_food(SV_FOOD_CURE_BLINDNESS) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(TRUE) || borg_quaff_potion(SV_POTION_HEALING) || borg_use_staff_fail(SV_STAFF_HEALING) || borg_use_staff_fail(SV_STAFF_CURING)) { borg_note("# Fixing Blindness."); return (TRUE); } } } /* We generally try to conserve ez-heal pots */ if ((bp_ptr->status.blind || bp_ptr->status.confused) && ((hp_down >= 400) || (danger > bp_ptr->chp * 5 && hp_down > 100)) && borg_quaff_potion(SV_POTION_STAR_HEALING)) { borg_note("# Fixing Confusion/Blind."); return (TRUE); } /* Hack -- rest until healed */ if ((!bp_ptr->status.blind && !bp_ptr->status.poisoned && !bp_ptr->status.cut && !borg_goi && !borg_see_inv && !borg_shield && !bp_ptr->status.weak && !bp_ptr->status.hungry && (bp_ptr->status.confused || bp_ptr->status.image || bp_ptr->status.afraid || bp_ptr->status.stun || bp_ptr->status.heavy_stun || (bp_ptr->chp < bp_ptr->mhp) || (bp_ptr->csp < bp_ptr->msp * 6 / 10)) && (danger < avoidance / 5)) && borg_check_rest() && !scaryguy_on_level && (danger <= mb_ptr->fear) && !goal_fleeing && borg_on_safe_feat(map_loc(c_x, c_y)->feat)) { /* check for then call lite in dark room before resting */ if (!borg_check_lite_only()) { /* Take note */ borg_note("# Resting to restore HP/SP..."); /* Rest until done */ borg_keypress('R'); borg_keypress('\n'); /* reset the inviso clock to avoid loops */ need_see_inviso = borg_t - 50; /* Done */ return (TRUE); } else { /* Must have been a dark room */ borg_note("# Lighted the darkened room instead of resting."); return (TRUE); } } /* Healing and fighting The Serpent. */ if (borg_fighting_unique >= BORG_QUESTOR) { if ((bp_ptr->chp <= 625) && (((bp_ptr->chp > 250) && borg_spell_fail(REALM_LIFE, 2, 6, 14)) || borg_use_staff_fail(SV_STAFF_HOLINESS) || ((stats_needing_fix >= 5) && borg_quaff_potion(SV_POTION_LIFE)) || ((hp_down > 500) && !borg_slot(TV_POTION, SV_POTION_STAR_HEALING) && borg_quaff_potion(SV_POTION_LIFE)) || borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_HEALING) || borg_spell_fail(REALM_LIFE, 2, 6, 17) || borg_spell_fail(REALM_LIFE, 3, 4, 15) || borg_spell_fail(REALM_NATURE, 1, 7, allow_fail + 9) || borg_spell_fail(REALM_LIFE, 1, 7, 15) || borg_quaff_potion(SV_POTION_LIFE))) { borg_note("# Healing in Questor Combat."); return (TRUE); } } /* restore Mana */ /* note, blow the staff charges easy because the staff will not last. */ if (bp_ptr->csp < (bp_ptr->msp / 5) && (randint0(100) < 50)) { if (borg_use_staff_fail(SV_STAFF_THE_MAGI)) { borg_note("# Use Magi Staff"); return (TRUE); } } /* blowing potions is harder */ /* NOTE: must have enough mana to keep up GOI or do a HEAL */ if (bp_ptr->csp < (bp_ptr->msp / 10) || ((bp_ptr->csp < 70 && bp_ptr->msp > 200) && (borg_goi <= borg_game_ratio * 3))) { /* use the potion if battling a unique and not too dangerous */ if (borg_fighting_unique >= BORG_QUESTOR || (borg_fighting_unique && danger < avoidance * 2) || (!bp_ptr->able.teleport && danger > avoidance)) { if (borg_use_staff_fail(SV_STAFF_THE_MAGI) || borg_quaff_potion(SV_POTION_RESTORE_MANA)) { borg_note("# Restored My Mana"); return (TRUE); } } } /* if unhurt no healing needed */ if (hp_down == 0) return FALSE; /* Don't bother healing if not in danger */ if (danger == 0 && !bp_ptr->status.poisoned && !bp_ptr->status.cut) return (FALSE); /* Restoring while fighting The Serpent */ if (stats_needing_fix >= 5 && borg_fighting_unique >= BORG_QUESTOR && bp_ptr->chp > 650 && borg_eat_food(SV_FOOD_RESTORING)) { borg_note("# Trying to fix stats in combat."); return (TRUE); } /* No further Healing considerations if fighting Questors */ if (borg_fighting_unique >= BORG_QUESTOR) { /* No further healing considerations right now */ return (FALSE); } /* Hack -- heal when wounded a percent of the time */ /* down 4/5 hp 0% */ /* 3/4 hp 2% */ /* 2/3 hp 20% */ /* 1/2 hp 50% */ /* 1/3 hp 75% */ /* 1/4 hp 100% */ chance = randint0(100); /* if we are fighting a unique increase the odds of healing */ if (borg_fighting_unique) chance -= 10; /* if danger is close to the hp and healing will help, do it */ if (danger >= bp_ptr->chp && danger < bp_ptr->mhp) chance -= 75; else { if (!borg_has_realm(REALM_LIFE) && !borg_has_realm(REALM_NATURE)) chance -= 25; } if (! (((bp_ptr->chp <= ((bp_ptr->mhp * 4) / 5)) && (chance < 0)) || ((bp_ptr->chp <= ((bp_ptr->mhp * 3) / 4)) && (chance < 2)) || ((bp_ptr->chp <= ((bp_ptr->mhp * 2) / 3)) && (chance < 20)) || ((bp_ptr->chp <= (bp_ptr->mhp / 2)) && (chance < 50)) || ((bp_ptr->chp <= (bp_ptr->mhp / 3)) && (chance < 75)) || (bp_ptr->chp <= (bp_ptr->mhp / 4)) || bp_ptr->status.heavy_stun || bp_ptr->status.stun || bp_ptr->status.poisoned || bp_ptr->status.cut)) return FALSE; /* Cure light Wounds (50) */ if (hp_down < 50 && danger < bp_ptr->chp + 50 && danger > bp_ptr->chp && (borg_spell_fail(REALM_LIFE, 0, 1, allow_fail) || borg_spell_fail(REALM_ARCANE, 0, 7, allow_fail) || borg_spell_fail(REALM_NATURE, 0, 1, allow_fail) || borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_use_staff(SV_STAFF_CURE_LIGHT))) { borg_note("# Healing Level 1."); return (TRUE); } /* Cure Serious Wounds (75) */ if (hp_down < 75 && danger < bp_ptr->chp + 75 && danger > bp_ptr->chp && (borg_spell_fail(REALM_LIFE, 0, 6, allow_fail) || borg_spell_fail(REALM_ARCANE, 2, 2, allow_fail) || borg_quaff_potion(SV_POTION_CURE_SERIOUS))) { borg_note("# Healing Level 2."); return (TRUE); } /* Cure Critical Wounds (150) */ if (hp_down < 150 && danger < bp_ptr->chp + 150 && danger > bp_ptr->chp && (borg_spell_fail(REALM_LIFE, 1, 2, allow_fail) || borg_quaff_crit(FALSE))) { borg_note("# Healing Level 3."); return (TRUE); } /* If in danger try one more Cure Critical if it will help */ if (danger >= bp_ptr->chp && danger < bp_ptr->mhp && bp_ptr->chp < 20 && danger < 30 && borg_quaff_crit(TRUE)) { borg_note("# Healing Level 5."); return (TRUE); } /* Generally continue to heal. But if we are preparing for the end * game uniques, then bail out here in order to save our heal pots. * (unless The Serpent is dead) * Priests wont need to bail, they have good heal spells. */ if (bp_ptr->max_depth >= 98 && !bp_ptr->winner && !borg_fighting_unique && !borg_has_realm(REALM_LIFE) && !borg_has_realm(REALM_NATURE)) { /* Bail out to save the heal pots for The Serpent */ return (FALSE); } /* Heal step one (200hp) */ if (hp_down < 250 && danger / 2 < bp_ptr->chp + 200 && (((!bp_ptr->able.teleport || (bp_ptr->skill_dev - borg_get_kind(TV_ROD, SV_ROD_HEALING)->level > 7)) && borg_zap_rod(SV_ROD_HEALING)) || borg_activate(BORG_ACT_HEAL_BIG) || borg_use_staff_fail(SV_STAFF_HEALING) || borg_spell_fail(REALM_LIFE, 1, 6, allow_fail) || borg_quaff_potion(SV_POTION_HEALING))) { borg_note("# Healing Level 6."); return (TRUE); } /* Heal step two (300hp) */ if (hp_down < 350 && danger / 2 < bp_ptr->chp + 300 && (borg_use_staff_fail(SV_STAFF_HEALING) || (borg_fighting_evil_unique && borg_spell_fail(REALM_LIFE, 2, 6, allow_fail)) || borg_use_staff_fail(SV_STAFF_HOLINESS) || borg_spell_fail(REALM_LIFE, 1, 6, allow_fail) || ((!bp_ptr->able.teleport || (bp_ptr->skill_dev - borg_get_kind(TV_ROD, SV_ROD_HEALING)->level > 7)) && borg_zap_rod(SV_ROD_HEALING)) || borg_zap_rod(SV_ROD_HEALING) || borg_quaff_potion(SV_POTION_HEALING))) { borg_note("# Healing Level 7."); return (TRUE); } /* Healing step three (300hp). */ if (hp_down < 650 && danger / 2 < bp_ptr->chp + 300 && ((borg_fighting_evil_unique && borg_spell_fail(REALM_LIFE, 2, 6, allow_fail)) || ((!bp_ptr->able.teleport || (bp_ptr->skill_dev - borg_get_kind(TV_ROD, SV_ROD_HEALING)->level > 7)) && borg_zap_rod(SV_ROD_HEALING)) || borg_spell_fail(REALM_LIFE, 1, 6, allow_fail) || borg_spell_fail(REALM_NATURE, 1, 7, allow_fail) || borg_use_staff_fail(SV_STAFF_HOLINESS) || borg_use_staff_fail(SV_STAFF_HEALING) || borg_quaff_potion(SV_POTION_HEALING) || borg_activate(BORG_ACT_HEAL_BIG))) { borg_note("# Healing Level 8."); return (TRUE); } /* Healing final check. Note that *heal* and Life potions are not * wasted. They are saved for The Serpent and emergencies. The * Emergency check is at the end of borg_caution(). */ if (hp_down >= 650 && (danger / 2 < bp_ptr->chp + 500) && ((borg_fighting_evil_unique && borg_spell_fail(REALM_LIFE, 2, 6, allow_fail)) || borg_spell_fail(REALM_LIFE, 3, 4, allow_fail) || borg_spell_fail(REALM_NATURE, 1, 7, allow_fail) || borg_use_staff_fail(SV_STAFF_HOLINESS) || borg_use_staff_fail(SV_STAFF_HEALING) || ((!bp_ptr->able.teleport || (bp_ptr->skill_dev - borg_get_kind(TV_ROD, SV_ROD_HEALING)->level > 7)) && borg_zap_rod(SV_ROD_HEALING)) || borg_quaff_potion(SV_POTION_HEALING) || borg_activate(BORG_ACT_HEAL_BIG) || (borg_fighting_unique && (borg_quaff_potion(SV_POTION_HEALING) || borg_quaff_potion(SV_POTION_LIFE))))) { borg_note("# Healing Level 9."); return (TRUE); } /*** Cures ***/ /* Dont do these in the middle of a fight, teleport out then try it */ if (danger > avoidance * 2 / 10) return (FALSE); /* Hack -- cure poison when poisoned * This was moved from borg_caution. */ if (bp_ptr->status.poisoned && (bp_ptr->chp < bp_ptr->mhp / 2)) { if (borg_spell_fail(REALM_LIFE, 1, 1, 60) || borg_spell_fail(REALM_ARCANE, 1, 5, 60) || borg_spell_fail(REALM_NATURE, 0, 7, 60) || borg_quaff_potion(SV_POTION_CURE_POISON) || borg_activate(BORG_ACT_CURE_POISON) || borg_use_staff(SV_STAFF_CURING) || borg_eat_cure_poison() || borg_racial(RACE_AMBERITE_POWER2) || /* buy time */ borg_quaff_crit(TRUE) || borg_spell_fail(REALM_LIFE, 0, 6, 40) || borg_spell_fail(REALM_LIFE, 0, 1, 40) || borg_spell_fail(REALM_ARCANE, 0, 7, 40) || borg_spell_fail(REALM_NATURE, 0, 1, 40) || borg_use_staff_fail(SV_STAFF_HEALING)) { borg_note("# Curing."); return (TRUE); } /* attempt to fix mana then poison on next round */ if ((borg_spell_legal(REALM_LIFE, 1, 1) || borg_spell_legal(REALM_ARCANE, 1, 5) || borg_spell_legal(REALM_NATURE, 0, 7)) && (borg_quaff_potion(SV_POTION_RESTORE_MANA))) { borg_note("# Curing next round."); return (TRUE); } } /* Hack -- cure poison when poisoned CRITICAL CHECK */ if (bp_ptr->status.poisoned && (bp_ptr->chp < 2 || bp_ptr->chp < bp_ptr->mhp / 20)) { int sv_mana = bp_ptr->csp; bp_ptr->csp = bp_ptr->msp; if (borg_spell(REALM_LIFE, 1, 1) || borg_spell(REALM_ARCANE, 1, 5) || borg_spell(REALM_NATURE, 0, 7)) { /* verify use of spell */ borg_press_faint_accept(); /* Flee! */ borg_note("# Emergency Cure Poison! Gasp!!!...."); return (TRUE); } bp_ptr->csp = sv_mana; /* Quaff healing pots to buy some time- in this emergency. */ if (borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_use_staff(SV_STAFF_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS)) return (TRUE); /* Try to Restore Mana */ if (borg_quaff_potion(SV_POTION_RESTORE_MANA)) return (TRUE); /* Emergency check on healing. Borg_heal has already been checked but * but we did not use our ez_heal potions. All other attempts to save * ourself have failed. Use the ez_heal if I have it. */ if (bp_ptr->chp < bp_ptr->mhp / 20 && (borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_LIFE) || borg_quaff_potion(SV_POTION_HEALING))) { return (TRUE); } /* Quaff unknown potions in this emergency. We might get luck */ if (borg_quaff_unknown()) return (TRUE); /* Eat unknown mushroom in this emergency. We might get luck */ if (borg_eat_unknown()) return (TRUE); /* Use unknown Staff in this emergency. We might get luck */ if (borg_use_unknown()) return (TRUE); } /* Hack -- cure wounds when bleeding, also critical check */ if (bp_ptr->status.cut && (bp_ptr->chp < bp_ptr->mhp / 3 || randint0(100) < 20)) { if (borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_use_staff(SV_STAFF_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit((bool) (bp_ptr->chp < 10)) || borg_spell_fail(REALM_LIFE, 1, 1, 100) || borg_spell_fail(REALM_LIFE, 0, 6, 100) || borg_spell_fail(REALM_LIFE, 0, 1, 100) || borg_spell_fail(REALM_ARCANE, 2, 2, 100) || borg_spell_fail(REALM_ARCANE, 0, 7, 100) || borg_spell_fail(REALM_NATURE, 0, 7, 100) || borg_spell_fail(REALM_NATURE, 0, 1, 100)) { return (TRUE); } } /* bleeding and about to die CRITICAL CHECK */ if (bp_ptr->status.cut && ((bp_ptr->chp < 2) || bp_ptr->chp < bp_ptr->mhp / 20)) { int sv_mana = bp_ptr->csp; /* Quaff healing pots to buy some time- in this emergency. */ if (borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_use_staff(SV_STAFF_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS)) return (TRUE); /* Try to Restore Mana */ if (borg_quaff_potion(SV_POTION_RESTORE_MANA)) return (TRUE); /* Emergency check on healing. Borg_heal has already been checked but * but we did not use our ez_heal potions. All other attempts to save * ourself have failed. Use the ez_heal if I have it. */ if (bp_ptr->chp < bp_ptr->mhp / 20 && (borg_quaff_potion(SV_POTION_HEALING) || borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_LIFE))) { return (TRUE); } bp_ptr->csp = bp_ptr->msp; /* Emergency use of spell */ if (borg_spell_fail(REALM_LIFE, 1, 1, 100) || borg_spell_fail(REALM_LIFE, 0, 6, 100) || borg_spell_fail(REALM_LIFE, 0, 1, 100) || borg_spell_fail(REALM_ARCANE, 2, 2, 100) || borg_spell_fail(REALM_ARCANE, 0, 7, 100) || borg_spell_fail(REALM_NATURE, 0, 7, 100) || borg_spell_fail(REALM_NATURE, 0, 1, 100)) { /* verify use of spell */ borg_press_faint_accept(); /* Flee! */ borg_note("# Emergency Wound Patch! Gasp!!!...."); return (TRUE); } bp_ptr->csp = sv_mana; /* Quaff unknown potions in this emergency. We might get luck */ if (borg_quaff_unknown()) return (TRUE); /* Eat unknown mushroom in this emergency. We might get luck */ if (borg_eat_unknown()) return (TRUE); /* Use unknown Staff in this emergency. We might get luck */ if (borg_use_unknown()) return (TRUE); } /* nothing to do */ return (FALSE); } /* * Be "cautious" and attempt to prevent death or dishonor. * * Strategy: * * (1) Caution * (1a) Analyze the situation * (1a1) try to heal * (1a2) try a defence * (1b) Teleport from danger * (1c) Handle critical stuff * (1d) Retreat to happy grids * (1e) Back away from danger * (1f) Heal various conditions * * (2) Attack * (2a) Simulate possible attacks * (2b) Perform optimal attack * * (3) Recover * (3a) Recover by spells/prayers * (3b) Recover by items/etc * (3c) Recover by resting * * XXX XXX XXX * In certain situations, the "proper" course of action is to simply * attack a nearby monster, since often most of the danger is due to * a single monster which can sometimes be killed in a single blow. * * Actually, both "borg_caution()" and "borg_recover()" need to * be more intelligent, and should probably take into account * such things as nearby monsters, and/or the relative advantage * of simply pummeling nearby monsters instead of recovering. * * Note that invisible/offscreen monsters contribute to the danger * of an extended "region" surrounding the observation, so we will * no longer rest near invisible monsters if they are dangerous. * * XXX XXX XXX * We should perhaps reduce the "fear" values of each region over * time, to take account of obsolete invisible monsters. * * Note that walking away from a fast monster is counter-productive, * since the monster will often just follow us, so we use a special * method which allows us to factor in the speed of the monster and * predict the state of the world after we move one step. Of course, * walking away from a spell casting monster is even worse, since the * monster will just get to use the spell attack multiple times. But, * if we are trying to get to known safety, then fleeing in such a way * might make sense. Actually, this has been done too well, note that * it makes sense to flee some monsters, if they "stumble", or if we * are trying to get to stairs. XXX XXX XXX * * Note that the "flow" routines attempt to avoid entering into * situations that are dangerous, but sometimes we do not see the * danger coming, and then we must attempt to survive by any means. * * We will attempt to "teleport" if the danger in the current situation, * as well as that resulting from attempting to "back away" from danger, * are sufficient to kill us in one or two blows. This allows us to * avoid teleportation in situations where simply backing away is the * proper course of action, for example, when standing next to a nasty * stationary monster, but also to teleport when backing away will not * reduce the danger sufficiently. * * But note that in "nasty" situations (when we are running out of light, * or when we are starving, blind, confused, or hallucinating), we will * ignore the possibility of "backing away" from danger, when considering * the possibility of using "teleport" to escape. But if the teleport * fails, we will still attempt to "retreat" or "back away" if possible. * * XXX XXX XXX Note that it should be possible to do some kind of nasty * "flow" algorithm which would use a priority queue, or some reasonably * efficient normal queue stuff, to determine the path which incurs the * smallest "cumulative danger", and minimizes the total path length. * It may even be sufficient to treat each step as having a cost equal * to the danger of the destination grid, plus one for the actual step. * This would allow the Borg to prefer a ten step path passing through * one grid with danger 10, to a five step path, where each step has * danger 9. Currently, he often chooses paths of constant danger over * paths with small amounts of high danger. However, the current method * is very fast, which is certainly a point in its favor... * * When in danger, attempt to "flee" by "teleport" or "recall", and if * this is not possible, attempt to "heal" damage, if needed, and else * attempt to "flee" by "running". * * XXX XXX XXX Both "borg_caution()" and "borg_recover()" should only * perform the "healing" tasks if they will cure more "damage"/"stuff" * than may be re-applied in the next turn, this should prevent using * wimpy healing spells next to dangerous monsters, and resting to regain * mana near a mana-drainer. * * Whenever we are in a situation in which, even when fully healed, we * could die in a single round, we set the "goal_fleeing" flag, and if * we could die in two rounds, we set the "goal_leaving" flag. * * In town, whenever we could die in two rounds if we were to stay still, * we set the "goal_leaving" flag. In combination with the "retreat" and * the "back away" code, this should allow us to leave town before getting * into situations which might be fatal. * * Flag "goal_fleeing" means get off this level right now, using recall * if possible when we get a chance, and otherwise, take stairs, even if * it is very dangerous to do so. * * Flag "goal_leaving" means get off this level when possible, using * stairs if possible when we get a chance. * * We will also take stairs if we happen to be standing on them, and we * could die in two rounds. This is often "safer" than teleportation, * and allows the "retreat" code to retreat towards stairs, knowing that * once there, we will leave the level. * * If we can, we should try to hit a monster with an offset spell. * A Druj can not move but they are really dangerous. So we should retreat * to a happy grid (meaning we have los and it does not), we should target * one space away from the bad guy then blast away with ball spells. */ bool borg_caution(void) { int j, p; bool borg_surround = FALSE; bool nasty = FALSE; map_block *mb_ptr = map_loc(c_x, c_y); /*** Notice "nasty" situations ***/ /* About to run out of light is extremely nasty */ if (!bp_ptr->britelite && equipment[EQUIP_LITE].timeout < 250) nasty = TRUE; /* Starvation is nasty */ if (bp_ptr->status.weak) nasty = TRUE; /* Blind-ness is nasty */ if (bp_ptr->status.blind) nasty = TRUE; /* Confusion is nasty */ if (bp_ptr->status.confused) nasty = TRUE; /* Hallucination is nasty */ if (bp_ptr->status.image) nasty = TRUE; /*** Evaluate local danger ***/ /* am I fighting a unique or a summoner, or scaryguy? */ borg_near_monster_type(bp_ptr->max_lev < 15 ? MAX_SIGHT : 12); borg_surround = borg_surrounded(); /* * Only allow three 'escapes' per level * unless fighting a unique, then allow 7. */ if ((borg_escapes > 3 && !unique_on_level) || borg_escapes > 7) { /* No leaving if going after questors */ if (bp_ptr->depth <= 98) { /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note("# Leaving (Too many escapes)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing && borg_escapes > 3) { /* Note */ borg_note("# Fleeing (Too many escapes)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /* No hanging around if nasty here. */ if (scaryguy_on_level) { /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note("# Leaving (Scary guy on level)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Note */ borg_note("# Fleeing (Scary guy on level)"); /* Start fleeing */ goal_fleeing = TRUE; } } /* Look around */ p = borg_danger(c_x, c_y, 1, TRUE); /* Describe (briefly) the current situation */ /* Danger (ignore stupid "fear" danger) */ if (borg_goi || (p > avoidance / 10) || (p > mb_ptr->fear)) { /* Describe (briefly) the current situation */ borg_note ("# Loc:%d,%d Dep:%d Lev:%d HP:%d/%d SP:%d/%d Danger:p=%d", c_x, c_y, bp_ptr->depth, bp_ptr->lev, bp_ptr->chp, bp_ptr->mhp, bp_ptr->csp, bp_ptr->msp, p); if (borg_goi) { borg_note ("# Protected by GOI (borg turns:%d; game turns:%d)", borg_goi / borg_game_ratio, p_ptr->tim.invuln); } if (borg_shield) { borg_note("# Protected by Mystic Shield"); } if (borg_prot_from_evil) { borg_note("# Protected by PFE"); } } /* Comment on glyph */ if (track_glyph_num) { int i; for (i = 0; i < track_glyph_num; i++) { /* Enqueue the grid */ if ((track_glyph_y[i] == c_y) && (track_glyph_x[i] == c_x)) { /* if standing on one */ borg_note("# Standing on Glyph"); } } } /* Comment on stair */ if (track_less_num) { int i; for (i = 0; i < track_less_num; i++) { /* Enqueue the grid */ if ((track_less_y[i] == c_y) && (track_less_x[i] == c_x)) { /* if standing on one */ borg_note("# Standing on up-stairs"); } } } /* Comment on stair */ if (track_more_num) { int i; for (i = 0; i < track_more_num; i++) { /* Enqueue the grid */ if ((track_more_y[i] == c_y) && (track_more_x[i] == c_x)) { /* if standing on one */ borg_note("# Standing on dn-stairs, (%d, %d)", c_x, c_y); } } } /* If the borg has healing spells */ if (borg_has_realm(REALM_LIFE) || borg_has_realm(REALM_NATURE)) { /* try healing before running away */ if (borg_heal(p)) return (TRUE); /* do some defence before running away! */ if (borg_defend(p)) return (TRUE); } else { /* do some defence before running away */ if (borg_defend(p)) return (TRUE); /* try healing before running away */ if (borg_heal(p)) return (TRUE); } /* If I am waiting for recall, & safe, then stay put. */ if (goal_recalling && borg_check_rest() && bp_ptr->depth && borg_on_safe_feat(map_loc(c_x, c_y)->feat)) { /* note the resting */ borg_note("# Resting here, waiting for Recall."); /* rest here until lift off */ borg_keypress('R'); borg_keypress('\n'); return (TRUE); } /* If I am waiting for recall in town */ if (goal_recalling && goal_recalling <= (borg_game_ratio * 2) && !bp_ptr->depth) { /* Cast GOI just before returning to dungeon */ if (!borg_goi && (borg_spell_fail(REALM_LIFE, 3, 7, 15) || borg_spell_fail(REALM_SORCERY, 3, 7, 15))) { borg_note("# Casting GOI before Recall activates."); return (TRUE); } /* Cast PFE just before returning to dungeon */ if (!borg_prot_from_evil && borg_spell_fail(REALM_LIFE, 1, 5, 15)) { borg_note("# Casting PFE before Recall activates."); return (TRUE); } /* Cast other good prep things */ if ((!borg_speed && borg_spell_fail(REALM_SORCERY, 1, 5, 15)) || (my_oppose_fire + my_oppose_cold + my_oppose_acid + my_oppose_elec + my_oppose_pois < 3 && (borg_spell_fail(REALM_NATURE, 2, 3, 15) || borg_spell_fail(REALM_NATURE, 0, 6, 15))) || (my_oppose_fire + my_oppose_cold + my_oppose_elec < 2 && borg_spell_fail(REALM_NATURE, 0, 6, 15)) || (!borg_shield && !borg_goi && borg_spell_fail(REALM_NATURE, 2, 2, 15)) || (!borg_hero && borg_spell_fail (REALM_SORCERY, 7, 0, 15)) || (!borg_berserk && borg_spell_fail(REALM_DEATH, 2, 0, 15)) || (!borg_bless && borg_spell_fail(REALM_LIFE, 0, 2, 15)) || (!borg_speed && borg_mindcr_fail(MIND_ADRENALINE, 35, 15)) || (!borg_hero && borg_mindcr_fail(MIND_ADRENALINE, 35, 15))) { borg_note("# Casting preparatory spell before Recall activates."); return (TRUE); } } /*** Danger ***/ /* Impending doom */ /* Don't take off in the middle of a fight */ /* just to restock and it is useless to restock */ /* if you have just left town. */ if (borg_restock(bp_ptr->depth) && !borg_fighting_unique && (borg_time_town + (borg_t - borg_began)) > 200) { /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note ("# Leaving (restock) %s", borg_restock(bp_ptr->depth)); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing && (bp_ptr->able.ccw < 2)) { /* Flee */ borg_note ("# Fleeing (restock) %s", borg_restock(bp_ptr->depth)); /* Start fleeing */ goal_fleeing = TRUE; } } /* Excessive danger */ else if (p > (bp_ptr->chp * 2)) { /* Start fleeing */ if (!goal_fleeing && !borg_fighting_unique && (bp_ptr->lev < 50) && !vault_on_level && (bp_ptr->depth < 100)) { /* Note */ borg_note("# Fleeing (excessive danger)"); /* Start fleeing */ goal_fleeing = TRUE; } } /* Potential danger (near death) in town */ else if (!bp_ptr->depth && (p > bp_ptr->chp) && (bp_ptr->lev < 50)) { /* Flee now */ if (!goal_leaving) { /* Flee! */ borg_note("# Leaving (potential danger)"); /* Start leaving */ goal_leaving = TRUE; } } /*** Stairs ***/ /* Leaving or Fleeing, take stairs */ if (goal_leaving || goal_fleeing || scaryguy_on_level) { /* Take next stairs */ stair_less = goal_fleeing; if (scaryguy_on_level) stair_less = TRUE; /* * Only go down if fleeing or prepared, * but not when starving, or lacking food */ stair_more = goal_fleeing; if (borg_prepared_depth() > bp_ptr->depth) stair_more = TRUE; /* Its ok to go one level deep if evading scary guy */ if (scaryguy_on_level) stair_more = TRUE; if (!bp_ptr->cur_lite || bp_ptr->status.hungry || bp_ptr->status.weak || (bp_ptr->food < 2)) stair_more = FALSE; /* if fleeing town, then dive */ if (!bp_ptr->depth) stair_more = TRUE; } /* Take stairs up */ if (stair_less) { /* Current grid */ map_block *mb_ptr = map_loc(c_x, c_y); /* Usable stairs */ if (mb_ptr->feat == FEAT_LESS) { borg_keypress('<'); /* Success */ return (TRUE); } } /* Take stairs down */ if (stair_more && !goal_recalling) { /* Current grid */ map_block *mb_ptr = map_loc(c_x, c_y); /* Usable stairs */ if (mb_ptr->feat == FEAT_MORE) { /* Cast GOI just before returning to dungeon */ if (bp_ptr->csp > bp_ptr->msp * 6 / 10 && !borg_goi && (borg_spell_fail(REALM_LIFE, 3, 7, 15) || borg_spell_fail(REALM_SORCERY, 3, 7, 15))) { borg_note("# Casting GOI before taking stairs."); return (TRUE); } /* Cast PFE just before returning to dungeon */ if (bp_ptr->csp > bp_ptr->msp * 6 / 10 && !borg_prot_from_evil && borg_spell_fail(REALM_LIFE, 1, 5, 15)) { borg_note("# Casting PFE before taking stairs."); return (TRUE); } /* Cast other good prep things */ if ((bp_ptr->csp > bp_ptr->msp * 6 / 10) && ((!borg_speed && borg_spell_fail(REALM_SORCERY, 1, 5, 15)) || (my_oppose_fire + my_oppose_cold + my_oppose_acid + my_oppose_elec + my_oppose_pois < 3 && (borg_spell_fail(REALM_NATURE, 2, 3, 15) || borg_spell_fail(REALM_NATURE, 0, 6, 15))) || (my_oppose_fire + my_oppose_cold + my_oppose_elec < 2 && borg_spell_fail(REALM_NATURE, 0, 6, 15)) || (!borg_shield && !borg_goi && borg_spell_fail(REALM_NATURE, 2, 2, 15)) || (!borg_hero && borg_spell_fail (REALM_SORCERY, 7, 0, 15)) || (!borg_berserk && borg_spell_fail(REALM_DEATH, 2, 0, 15)) || (!borg_bless && borg_spell_fail(REALM_LIFE, 0, 2, 15)) || (!borg_speed && borg_mindcr_fail(MIND_ADRENALINE, 35, 15)) || (!borg_hero && borg_mindcr_fail(MIND_ADRENALINE, 35, 15)))) { borg_note("# Casting preparatory spell before taking stairs."); return (TRUE); } /* Take the stairs */ borg_keypress('>'); /* If the borg leaves the wilderness */ if (!bp_ptr->depth) borg_leave_surface(); /* Success */ return (TRUE); } } /*** Deal with critical situations ***/ /* Hack -- require light */ if (!bp_ptr->britelite) { list_item *l_ptr = look_up_equip_slot(EQUIP_LITE); /* If the borg manages to refuel */ if (borg_refuel()) return (TRUE); /* Flee for fuel */ if (bp_ptr->depth && (!l_ptr || l_ptr->timeout < 1000)) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (need fuel)"); /* Start leaving */ goal_leaving = TRUE; } } } /* Hack -- prevent starvation */ if (bp_ptr->status.weak) { /* Attempt to satisfy hunger */ if (borg_eat_food_any() || borg_spell_fail(REALM_LIFE, 0, 7, 45) || borg_spell_fail(REALM_ARCANE, 2, 6, 45) || borg_spell_fail(REALM_NATURE, 0, 3, 45)) { /* Success */ return (TRUE); } /* Try to restore mana then cast the spell next round */ if (borg_quaff_potion(SV_POTION_RESTORE_MANA)) return (TRUE); /* Flee for food */ if (bp_ptr->depth) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (need food)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Flee */ borg_note("# Fleeing (need food)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /* Prevent breeder explosions when low level */ if (breeder_level && bp_ptr->lev < 15) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (breeder level)"); /* Start leaving */ goal_leaving = TRUE; } } /*** Flee on foot ***/ /* Desperation Head for stairs */ /* If you are low level and near the stairs and you can */ /* hop onto them in very few steps, try to head to them */ /* out of desperation */ if (track_less_num && (goal_fleeing || (p > avoidance && bp_ptr->lev < 35))) { int y, x, i; int b_j = -1; /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { x = track_less_x[i]; y = track_less_y[i]; /* How far is the nearest up stairs */ j = distance(c_y, c_x, y, x); /* skip the closer ones */ if (b_j >= j) continue; /* track it */ b_j = j; } /* * If you are within a few (3) steps of the stairs * and you can take some damage to get there * go for it */ if ((b_j < 3) && (p < bp_ptr->chp)) { borg_desperate = TRUE; if (borg_flow_stair_less(GOAL_FLEE)) { /* Note */ borg_note("# Desperate for Stairs (one)"); borg_desperate = FALSE; return (TRUE); } borg_desperate = FALSE; } /* If you are next to steps of the stairs go for it */ if (b_j <= 2) { borg_desperate = TRUE; if (borg_flow_stair_less(GOAL_FLEE)) { /* Note */ borg_note("# Desperate for Stairs (two)"); borg_desperate = FALSE; return (TRUE); } borg_desperate = FALSE; } /* Low level guys tend to waste money reading the recall scrolls */ if (b_j < 15 && scaryguy_on_level && bp_ptr->lev < 20) { borg_desperate = TRUE; if (borg_flow_stair_less(GOAL_FLEE)) { /* Note */ borg_note("# Desperate for Stairs (three)"); borg_desperate = FALSE; return (TRUE); } borg_desperate = FALSE; } } /* Strategic retreat */ /* Do not retreat if */ /* 1) we are icky (poisoned, blind, confused etc */ /* 2) we are boosting our avoidance because we are stuck */ if ((p > avoidance / 3 && !nasty && !borg_no_retreat) || (borg_surround && p != 0)) { int d, b_d = -1; int r, b_r = -1; int b_x = c_x; int b_y = c_y; /* Scan the useful viewable grids */ for (j = 1; j < borg_view_n; j++) { int x1 = c_x; int y1 = c_y; int x2 = borg_view_x[j]; int y2 = borg_view_y[j]; /* Cant if confused: no way to predict motion */ if (bp_ptr->status.confused) continue; /* Require "floor" grids */ if (!borg_cave_floor_bold(y2, x2)) continue; /* XXX -- Borgs in an unexplored hall (& with only a torch * will always return FALSE for Happy Grids: * * 222222 Where 2 = unknown grid. Borg has a torch. * 2221.# Borg will consider both the . and the 1 * #@# for a retreat from the C. But the . will be * #C# false d/t adjacent wall to the east. 1 will * #'# will be false d/t unknown grid to the west. * So he makes no attempt to retreat. * However, the next function (backing away), allows him * to back up to 1 safely. * * To play safer, the borg should not retreat to grids where * he has not previously been. This tends to run him into * more monsters. It is better for him to retreat to grids * previously travelled, where the monsters are most likely * dead, and the path is clear. However, there is not (yet) * tag for those grids. Something like BORG_BEEN would work. */ /* Require "happy" grids (most of the time) */ if (!borg_happy_grid_bold(x2, y2)) continue; /* Track "nearest" grid */ if (b_r >= 0) { int ay = ((y2 > y1) ? (y2 - y1) : (y1 - y2)); int ax = ((x2 > x1) ? (x2 - x1) : (x1 - x2)); /* Ignore "distant" locations */ if ((ax > b_r) || (ay > b_r)) continue; } /* Reset */ r = 0; /* Simulate movement */ while (1) { map_block *mb_ptr; /* Obtain direction */ d = borg_goto_dir(x1, y1, x2, y2); /* Verify direction */ if ((d == 0) || (d == 5)) break; /* Track distance */ r++; /* Simulate the step */ y1 += ddy[d]; x1 += ddx[d]; /* Bounds checking */ if (!map_in_bounds(x1, y1)) break; /* Obtain the grid */ mb_ptr = map_loc(x1, y1); /* Require floor */ if (borg_cave_wall_grid(mb_ptr)) break; /* Require line of sight */ if (!borg_los(x1, y1, x2, y2)) break; /* Check danger of that spot (over time) */ if (!borg_surround && borg_danger(x1, y1, r + 1, TRUE) >= p) break; /* make sure it is not dangerous to take the first step; unless surrounded. */ if (r == 1) { /* Not surrounded */ if (!borg_surround) { if (borg_danger(x2, y2, 1, TRUE) >= avoidance * 6 / 10) break; } else /* Surrounded, try to back-up */ { if (borg_danger(x2, y2, 1, TRUE) >= (b_r <= 3 ? avoidance * 15 / 10 : avoidance)) break; } } /* Skip monsters */ if (mb_ptr->monster) break; /* Skip traps */ if (mb_ptr->trap) break; /* Safe arrival */ if ((x1 == x2) && (y1 == y2)) { /* Save distance */ b_r = r; /* Save location */ b_x = x2; b_y = y2; /* Done */ break; } } } /* Retreat */ if (b_r >= 0) { /* Save direction */ b_d = borg_goto_dir(c_x, c_y, b_x, b_y); /* Hack -- set goal */ g_x = c_x + ddx[b_d]; g_y = c_y + ddy[b_d]; /* Note */ borg_note ("# Retreating to %d,%d (distance %d) via %d,%d (%d > %d)", b_y, b_x, b_r, g_y, g_x, p, borg_danger(g_x, g_y, 2, TRUE)); /* Strategic retreat */ borg_keypress(I2D(b_d)); /* Success */ return (TRUE); } } /*** Escape if possible ***/ /* Attempt to escape via spells */ if (borg_escape(p)) { /* increment the escapes this level counter */ borg_escapes++; /* Success */ return (TRUE); } /*** Back away ***/ /* Do not back up if */ /* 1) we are icky (poisoned, blind, confused etc */ /* 2) we are boosting our avoidance because we are stuck */ if ((p > avoidance / 3 && !nasty && !borg_no_retreat) || (borg_surround && p != 0)) { int i = -1, b_i = -1; int k = -1, b_k = -1; int f = -1, b_f = -1; /* Current danger */ b_k = p; /* Fake the danger down if surounded so that he can move. */ if (borg_surround) b_k = (b_k * 6 / 10); /* Check the freedom */ b_f = borg_freedom(c_x, c_y); /* Attempt to find a better grid */ for (i = 0; i < 8; i++) { int x = c_x + ddx_ddd[i]; int y = c_y + ddy_ddd[i]; map_block *mb_ptr; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Access the grid */ mb_ptr = map_loc(x, y); /* Cant if confused: no way to predict motion */ if (bp_ptr->status.confused) continue; /* Skip walls/doors */ if (borg_cave_wall_grid(mb_ptr)) continue; /* Skip monster grids */ if (mb_ptr->monster) continue; /* MT - skip traps */ if (mb_ptr->trap) continue; /* Extract the danger there */ k = borg_danger(x, y, 2, TRUE); /* Skip higher danger */ /* note: if surrounded, then b_k has been lowered. */ if (b_k < k) continue; /* Check the freedom there */ f = borg_freedom(x, y); /* Danger is the same */ if (b_k == k) { /* If I am low level, reward backing-up if safe */ if (bp_ptr->lev <= 3 && (bp_ptr->chp < bp_ptr->mhp || bp_ptr->csp < bp_ptr->msp)) { /* do consider the retreat */ } /* Freedom of my grid is better than the next grid * so stay put and fight. */ else if (b_f > f) continue; else continue; } /* Save the info */ b_i = i; b_k = k; b_f = f; } /* Back away */ if (b_i >= 0) { /* Hack -- set goal */ g_x = c_x + ddx_ddd[b_i]; g_y = c_y + ddy_ddd[b_i]; /* Note */ borg_note("# Backing up to %d,%d (%d > %d)", g_x, g_y, p, borg_danger(g_x, g_y, 2, TRUE)); /* Back away from danger */ borg_keypress(I2D(ddd[b_i])); /* Success */ return (TRUE); } } /*** Cures ***/ /* cure confusion, second check, first (slightly different) in borg_heal */ if (bp_ptr->status.confused) { if (bp_ptr->mhp - bp_ptr->chp >= 300 && (borg_quaff_potion(SV_POTION_HEALING) || borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_LIFE))) { return (TRUE); } if (borg_eat_food(SV_FOOD_CURE_CONFUSION) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(FALSE) || borg_quaff_potion(SV_POTION_HEALING) || borg_use_staff_fail(SV_STAFF_HEALING)) { return (TRUE); } } /* Cure hallucination as soon as possible! */ if (bp_ptr->status.image && (borg_quaff_potion(SV_POTION_CURING) || borg_use_staff(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING))) { /* Tried to stop the visions */ return (TRUE); } /* Hack -- cure fear when afraid */ if (bp_ptr->status.afraid && (randint0(100) < 70 || ((borg_class == CLASS_WARRIOR) && !bp_ptr->able.missile))) { if (borg_spell_fail(REALM_LIFE, 0, 3, 100) || borg_mindcr_fail(MIND_ADRENALINE, 23, 100) || borg_quaff_potion(SV_POTION_BOLDNESS) || borg_quaff_potion(SV_POTION_HEROISM) || borg_quaff_potion(SV_POTION_BERSERK_STRENGTH) || borg_activate(BORG_ACT_REMOVE_FEAR) || borg_activate(BORG_ACT_HEROISM) || borg_activate(BORG_ACT_BERSERKER) || borg_mutation(MUT1_BERSERK) || borg_racial(RACE_HALF_ORC) || borg_racial(RACE_HALF_TROLL)) { return (TRUE); } } /*** Note impending death XXX XXX XXX ***/ /* Flee from low hit-points */ if (((bp_ptr->chp < bp_ptr->mhp / 3) || ((bp_ptr->chp < bp_ptr->mhp / 2) && (bp_ptr->chp < (bp_ptr->lev * 3)))) && (bp_ptr->able.ccw < 3) && !bp_ptr->able.heal) { /* Flee from low hit-points */ if (bp_ptr->depth && (randint0(100) < 25)) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (low hit-points)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Flee */ borg_note("# Fleeing (low hit-points)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /* Flee from bleeding wounds or poison and no heals */ if ((bp_ptr->status.cut || bp_ptr->status.poisoned) && (bp_ptr->chp < bp_ptr->mhp / 2)) { /* Flee from bleeding wounds */ if (bp_ptr->depth && (randint0(100) < 25)) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (bleeding/posion)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Flee */ borg_note("# Fleeing (bleeding/poison)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /* * Emergency check on healing. Borg_heal has already been checked but * but we did not use our ez_heal potions. All other attempts to save * ourself have failed. Use the ez_heal if I have it. */ if (((bp_ptr->chp < bp_ptr->mhp / 10) || (!bp_ptr->able.teleport && !bp_ptr->able.escape && (bp_ptr->chp < bp_ptr->mhp / 4))) && ((p > bp_ptr->chp * 2) || ((p > bp_ptr->chp) && (bp_ptr->able.easy_heal > 5)) || ((p > bp_ptr->chp * 12 / 10) && (bp_ptr->mhp - bp_ptr->chp >= 400) && borg_fighting_unique && (bp_ptr->depth >= 85))) && (borg_quaff_potion(SV_POTION_HEALING) || borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_LIFE))) { borg_note("# Using reserve EZ_Heal."); return (TRUE); } /* Hack -- use "recall" to flee if possible */ if (goal_fleeing && bp_ptr->depth && (borg_recall())) { /* Note */ borg_note("# Fleeing the level (recall)"); /* Success */ return (TRUE); } /* If I am waiting for recall,and in danger, buy time with * phase and cure_anythings. */ if (goal_recalling && (p > avoidance * 2)) { if (!bp_ptr->status.confused && !bp_ptr->status.blind && bp_ptr->msp > 60 && bp_ptr->csp < (bp_ptr->msp / 4) && borg_quaff_potion(SV_POTION_RESTORE_MANA)) { borg_note("# Buying time waiting for Recall. Step 1."); return (TRUE); } if (borg_read_scroll(SV_SCROLL_PHASE_DOOR) || borg_spell_fail(REALM_ARCANE, 0, 4, 25) || borg_spell_fail(REALM_SORCERY, 0, 1, 25) || borg_spell_fail(REALM_TRUMP, 0, 0, 25) || borg_mindcr_fail(MIND_MINOR_DISP, 3, 35) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_zap_rod(SV_ROD_HEALING)) { borg_note("# Buying time waiting for Recall. Step 2."); return (TRUE); } if ((bp_ptr->mhp - bp_ptr->chp < 100) && (borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_use_staff(SV_STAFF_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(FALSE))) { borg_note("# Buying time waiting for Recall. Step 3."); return (TRUE); } if ((bp_ptr->mhp - bp_ptr->chp < 300) && (borg_quaff_crit(FALSE) || borg_use_staff(SV_STAFF_CURING))) { borg_note("# Buying time waiting for Recall. Step 4."); return (TRUE); } if ((bp_ptr->mhp - bp_ptr->chp >= 300) && (borg_quaff_potion(SV_POTION_HEALING) || borg_quaff_potion(SV_POTION_STAR_HEALING) || borg_quaff_potion(SV_POTION_LIFE) || borg_quaff_crit(FALSE))) { borg_note("# Buying time waiting for Recall. Step 5."); return (TRUE); } } /* if I am gonna die next round, and I have no way to escape * use the unknown stuff (if I am low level). */ if (p > (bp_ptr->chp * 4) && bp_ptr->lev < 20 && !bp_ptr->msp) { if (borg_use_unknown()) return (TRUE); if (borg_quaff_unknown()) return (TRUE); if (borg_read_unknown()) return (TRUE); if (borg_eat_unknown()) return (TRUE); } /* Nothing */ return (FALSE); } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zbmagic2.c0000644000000000000000000036372210250356275014013 0ustar rootroot/* File: zbmagic2.c */ /* Purpose: Medium level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zbmagic.h" bool borg_simulate; /* Simulation flag */ /* * New method for handling attacks, missiles, and spells * * Every turn, we evaluate every known method of causing damage * to monsters, and evaluate the "reward" inherent in each of * the known methods which is usable at that time, and then * we actually use whichever method, if any, scores highest. * * For each attack, we need a function which will determine the best * possible result of using that attack, and return its value. Also, * if requested, the function should actually perform the action. * * Note that the functions should return zero if the action is not * usable, or if the action is not useful. * * These functions need to apply some form of "cost" evaluation, to * prevent the use of expensive spells with minimal reward. Also, * we should always prefer attacking by hand to using spells if the * damage difference is "small", since there is no "cost" in making * a physical attack. * * We should take account of "spell failure", as well as "missile * missing" and "blow missing" probabilities. * * Note that the functions may store local state information when * doing a "simulation" and then they can use this information if * they are asked to implement their strategy. * * There are several types of damage inducers: * * Using rods * Activate Dragon Armour * Elemental Rings * Activating Artifacts * Launching missiles * Throwing objects * Reading scrolls * Attacking physically * Casting spells * Using staffs * Aimng wands * Racial Powers * Mutation Powers * Emergency use of spells * * The order of these attack types is not random. If two attacks do the same * damage then the first in the list will prevail. So that is why the rods * are first and fainting is last. */ #define BF_MIN 0 enum { BF_ROD, /* Recharging objects */ BF_DRAGON_ARMOUR, BF_RING, BF_ARTIFACT, BF_LAUNCH, /* Cheap objects */ BF_OBJECT, BF_SCROLL, BF_THRUST, /* Rest to restore hp/sp */ BF_SPELLCASTER, BF_MINDCRAFTER, BF_STAFF, /* Objects with charges */ BF_WAND, BF_RACIAL, /* Powers that hurt to execute */ BF_MUTATE, BF_SPELL_RESERVE, /* Emergency spell uses */ BF_MIND_RESERVE, BF_SPELL_FAINT, /* Fainting spell uses */ BF_MIND_FAINT, BF_MAX }; /* Attack styles */ #define BORG_BOLT 1 #define BORG_BEAM 2 #define BORG_BALL 3 #define BORG_DISPEL 4 #define BORG_BLAST 5 #define BORG_TOUCH 6 /* What is the radius of the borg ball attacks? */ #define BORG_BALL_RAD0 0 #define BORG_BALL_RAD1 1 #define BORG_BALL_RAD2 2 #define BORG_BALL_RAD3 3 #define BORG_BALL_RAD4 4 #define BORG_BALL_RAD8 8 /* * Guess how much damage a physical attack will do to a monster */ static int borg_thrust_damage_one(int i) { int dam; int mult; borg_kill *kill; monster_race *r_ptr; list_item *l_ptr; int chance; /* Examine current weapon */ l_ptr = &equipment[EQUIP_WIELD]; /* Monster record */ kill = &borg_kills[i]; /* Monster race */ r_ptr = &r_info[kill->r_idx]; /* Damage */ dam = (l_ptr->dd * (l_ptr->ds + 1) / 2); /* here is the place for slays and such */ mult = 1; if (((FLAG(bp_ptr, TR_SLAY_ANIMAL)) && (FLAG(r_ptr, RF_ANIMAL))) || ((FLAG(bp_ptr, TR_SLAY_EVIL)) && (FLAG(r_ptr, RF_EVIL)))) mult = 2; if (((FLAG(bp_ptr, TR_SLAY_UNDEAD)) && (FLAG(r_ptr, RF_ANIMAL))) || ((FLAG(bp_ptr, TR_SLAY_DEMON)) && (FLAG(r_ptr, RF_DEMON))) || ((FLAG(bp_ptr, TR_SLAY_ORC)) && (FLAG(r_ptr, RF_ORC))) || ((FLAG(bp_ptr, TR_SLAY_TROLL)) && (FLAG(r_ptr, RF_TROLL))) || ((FLAG(bp_ptr, TR_SLAY_GIANT)) && (FLAG(r_ptr, RF_GIANT))) || ((FLAG(bp_ptr, TR_SLAY_DRAGON)) && (FLAG(r_ptr, RF_DRAGON))) || ((FLAG(bp_ptr, TR_BRAND_ACID)) && !(FLAG(r_ptr, RF_IM_ACID))) || ((FLAG(bp_ptr, TR_BRAND_FIRE)) && !(FLAG(r_ptr, RF_IM_FIRE))) || ((FLAG(bp_ptr, TR_BRAND_COLD)) && !(FLAG(r_ptr, RF_IM_COLD))) || ((FLAG(bp_ptr, TR_BRAND_ELEC)) && !(FLAG(r_ptr, RF_IM_ELEC)))) mult = 3; if ((FLAG(bp_ptr, TR_KILL_DRAGON)) && (FLAG(r_ptr, RF_DRAGON))) mult = 5; /* add the multiplier */ dam *= mult; /* add weapon bonuses */ dam += l_ptr->to_d; /* add player bonuses */ dam += bp_ptr->to_h; /* multiply the damage for the whole round of attacks */ dam *= bp_ptr->blows; /* reduce for % chance to hit (AC) */ chance = bp_ptr->skill_thn + (bp_ptr->to_h + l_ptr->to_h) * 3; if ((r_ptr->ac * 3 / 4) > 0) chance = (chance * 100) / (r_ptr->ac * 3 / 4); /* 5% automatic success/fail */ if (chance > 95) chance = 95; if (chance < 5) chance = 5; /* add 20% to chance to give a bit more weight to weapons */ if (bp_ptr->lev > 15 && borg_class != CLASS_MAGE && borg_class != CLASS_HIGH_MAGE && borg_class != CLASS_MINDCRAFTER) chance += 20; dam = (dam * chance) / 100; /* Limit damage to twice maximal hitpoints */ if (dam > kill->power * 2) dam = kill->power * 2; /* Reduce the damage if a mage, they should not melee if they can avoid it */ if ((borg_class == CLASS_MAGE || borg_class != CLASS_HIGH_MAGE || borg_class != CLASS_MINDCRAFTER) && bp_ptr->max_lev < 40) dam = dam * 6 / 10; /* * Enhance the preceived damage on Uniques. This way we target them * Keep in mind that he should hit the uniques but if he has a * x5 great bane of dragons, he will tend attack the dragon since the * precieved (and actual) damage is higher. But don't select * the town uniques (maggot does no damage) * */ if (FLAG(r_ptr, RF_UNIQUE) && bp_ptr->depth >= 1) dam += (dam * 5); /* Hack -- ignore Maggot until later. Player will chase Maggot * down all accross the screen waking up all the monsters. Then * he is stuck in a comprimised situation. */ if (FLAG(r_ptr, RF_UNIQUE) && bp_ptr->depth == 0) { dam = dam * 2 / 3; /* Dont hunt maggot until later */ if (bp_ptr->lev < 5) dam = 0; } /* give a small bonus for whacking a breeder */ if (FLAG(r_ptr, RF_MULTIPLY)) dam = (dam * 3 / 2); /* Enhance the preceived damgage to summoner in order to influence the * choice of targets. */ if (FLAG(r_ptr, RF_S_KIN) || FLAG(r_ptr, RF_S_CYBER) || FLAG(r_ptr, RF_S_MONSTER) || FLAG(r_ptr, RF_S_MONSTERS) || FLAG(r_ptr, RF_S_ANT) || FLAG(r_ptr, RF_S_SPIDER) || FLAG(r_ptr, RF_S_HOUND) || FLAG(r_ptr, RF_S_HYDRA) || FLAG(r_ptr, RF_S_ANGEL) || FLAG(r_ptr, RF_S_DEMON) || FLAG(r_ptr, RF_S_UNDEAD) || FLAG(r_ptr, RF_S_DRAGON) || FLAG(r_ptr, RF_S_HI_UNDEAD) || FLAG(r_ptr, RF_S_HI_DRAGON) || FLAG(r_ptr, RF_S_AMBERITES) || FLAG(r_ptr, RF_S_UNIQUE) || FLAG(r_ptr, RF_QUESTOR)) dam += ((dam * 3) / 2); /* To conserve mana, for keeping GOI up, increase the value of melee */ if (borg_goi) { dam += (dam * 15 / 10); } /* dont hurt friends or pets */ if (kill->m_flags & (MONST_FRIEND | MONST_PET)) dam = -10; /* Invuln monsters take no dam */ if (kill->m_flags & MONST_INVULN) dam = 0; /* Damage */ return (dam); } /* * Simulate/Apply the optimal result of making a physical attack */ static int borg_attack_thrust(void) { int p, dir; int i, b_i = 0; int d, b_d = 0; map_block *mb_ptr; borg_kill *kill; if (borg_simulate) { /* Too afraid to attack */ if (bp_ptr->status.afraid) return (0); /* Examine possible destinations */ for (i = 0; i < borg_next_n; i++) { int x = borg_next_x[i]; int y = borg_next_y[i]; /* Acquire grid */ mb_ptr = map_loc(x, y); /* Calculate "average" damage */ d = borg_thrust_damage_one(mb_ptr->kill); /* No damage */ if (d <= 0) continue; /* Obtain the monster */ kill = &borg_kills[mb_ptr->kill]; /* Hack -- avoid waking most "hard" sleeping monsters */ if ((kill->m_flags & MONST_ASLEEP) && (d <= kill->power)) { /* Calculate danger */ borg_full_damage = TRUE; p = borg_danger_aux(x, y, 1, mb_ptr->kill, TRUE); borg_full_damage = FALSE; if (p > avoidance / 2) continue; } /* Hack -- ignore sleeping town monsters */ if (!bp_ptr->depth && (kill->m_flags & MONST_ASLEEP)) continue; /* Calculate "danger" to player */ borg_full_damage = TRUE; p = borg_danger_aux(c_x, c_y, 2, mb_ptr->kill, TRUE); borg_full_damage = FALSE; /* Reduce "bonus" of partial kills */ if (d <= kill->power) p = p / 10; /* Add the danger to the damage */ d += p; /* Ignore lower damage */ if (d < b_d) continue; /* Save the info */ b_i = i; b_d = d; } /* If damage was found */ if (b_d) { /* Save the location */ g_x = borg_next_x[b_i]; g_y = borg_next_y[b_i]; } /* Better safe than sorry */ else { g_x = c_x; g_y = c_y; } /* End of simulation */ return (b_d); } /* Get the spot on the map */ mb_ptr = map_loc(g_x, g_y); /* Note */ borg_note ("# Facing %s at (%d,%d).", mon_race_name(&r_info[mb_ptr->monster]), g_x, g_y); borg_note ("# Attacking with weapon '%s'", equipment[EQUIP_WIELD].o_name); /* Get a direction for attacking */ dir = borg_extract_dir(c_x, c_y, g_x, g_y); /* Attack the grid */ borg_keypress('+'); borg_keypress(I2D(dir)); /* Success */ return (b_d); } /* * Guess how much damage a spell attack will do to a monster * * We only handle the "standard" damage types. * * We are paranoid about monster resistances * * He tends to waste all of his arrows on a monsters immediately adjacent * to him. Then he has no arrows for the rest of the level. We will * decrease the damage if the monster is adjacent and we are getting low * on missiles. * * We will also decrease the value of the missile attack on breeders or * high clevel borgs town scumming. */ static int borg_launch_damage_one(int i, int dam, int typ) { int p1, p2 = 0; bool borg_use_missile = FALSE; /* Monster record */ borg_kill *kill = &borg_kills[i]; /* Monster race */ monster_race *r_ptr = &r_info[kill->r_idx]; /* all danger checks are with maximal damage */ borg_full_damage = TRUE; /* Analyze the damage type */ switch (typ) { case GF_MISSILE: { /* Magic Missile */ break; } case GF_ARROW: { /* Standard Arrow */ if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_ARROW_EXPLOSION: { /* Explosion arrows are really just flaming arrows with a kick */ dam += 100; /* Fall through */ } case GF_ARROW_FLAME: { /* Arrow of Flame */ if (!(FLAG(r_ptr, RF_IM_FIRE))) dam *= 3; if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_ARROW_FROST: { /* Arrow of Frost */ if (!(FLAG(r_ptr, RF_IM_COLD))) dam *= 3; if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_ARROW_SHOCKING: { /* Arrow of Shocking */ if (!(FLAG(r_ptr, RF_IM_ELEC))) dam *= 3; if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_ARROW_ANIMAL: { /* Arrow of Hurt Animal */ if (FLAG(r_ptr, RF_ANIMAL)) dam *= 2; if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_ARROW_EVIL: { /* Arrow of hurt evil */ if (FLAG(r_ptr, RF_EVIL)) dam *= 2; if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_ARROW_DRAGON: { /* Arrow of slay dragon */ if (FLAG(r_ptr, RF_DRAGON)) dam *= 3; if (distance(c_y, c_x, kill->y, kill->x) == 1 && !(FLAG(r_ptr, RF_UNIQUE))) dam /= 5; break; } case GF_MANA: { /* Pure damage */ /* only use mana storm against uniques... this */ /* should cut down on some mana use. */ if (!borg_fighting_unique || bp_ptr->able.mana < 3) dam /= 2; if (borg_fighting_unique && bp_ptr->able.mana > 7) dam *= 2; break; } case GF_ACID: { /* Acid */ if (FLAG(r_ptr, RF_IM_ACID)) dam /= 9; break; } case GF_ELEC: { /* Electricity */ if (FLAG(r_ptr, RF_IM_ELEC)) dam /= 9; break; } case GF_FIRE: { /* Fire damage */ if (FLAG(r_ptr, RF_IM_FIRE)) dam /= 9; break; } case GF_COLD: { /* Cold */ if (FLAG(r_ptr, RF_IM_COLD)) dam /= 9; break; } case GF_ELEMENTS: { /* Hack -- Equal chance of all elements to be cast */ if (FLAG(r_ptr, RF_IM_COLD)) dam /= 4; if (FLAG(r_ptr, RF_IM_ELEC)) dam /= 4; if (FLAG(r_ptr, RF_IM_FIRE)) dam /= 4; if (FLAG(r_ptr, RF_IM_ACID)) dam /= 4; break; } case GF_POIS: { /* Poison */ if (FLAG(r_ptr, RF_IM_POIS)) dam /= 9; break; } case GF_NUKE: { /* Nuke */ if (FLAG(r_ptr, RF_IM_POIS)) dam = (dam * 3) / 9; break; } case GF_ICE: { /* Ice */ if (FLAG(r_ptr, RF_IM_COLD)) dam /= 9; break; } case GF_HELL_FIRE: { /* Holy Orb */ if (FLAG(r_ptr, RF_EVIL)) dam *= 2; break; } case GF_HOLY_FIRE: { /* Holy Orb */ if (FLAG(r_ptr, RF_GOOD)) dam = 0; else if (FLAG(r_ptr, RF_EVIL)) dam *= 2; else dam = (dam * 3) / 9; break; } case GF_DISP_UNDEAD: { /* dispel undead */ if (!(FLAG(r_ptr, RF_UNDEAD))) dam = 0; break; } case GF_DISP_DEMON: { /* Dispel Demon */ if (!(FLAG(r_ptr, RF_DEMON))) dam = 0; break; } case GF_DISP_UNDEAD_DEMON: { /* Dispel Demons and Undead (Exorcism Spell) */ if (!(FLAG(r_ptr, RF_UNDEAD))) dam = 0; if (!(FLAG(r_ptr, RF_DEMON))) dam = 0; break; } case GF_DISP_EVIL: { /* Dispel Evil */ if (!(FLAG(r_ptr, RF_EVIL))) dam = 0; break; } case GF_HOLY_WORD: { /* Holy Word */ if (!(FLAG(r_ptr, RF_EVIL))) dam = 0; break; } case GF_LITE_WEAK: { /* Weak Lite */ if (!(FLAG(r_ptr, RF_HURT_LITE))) dam = 0; break; } case GF_LITE: { /* Regular Lite */ break; } case GF_OLD_DRAIN: case GF_DEATH_RAY: { /* Drain Life / Psi / Vamp. */ if (!monster_living(r_ptr)) { dam = 0; } break; } case GF_PSI: case GF_PSI_DRAIN: { if (FLAG(r_ptr, RF_EMPTY_MIND)) { dam = 0; } else if ((FLAG(r_ptr, RF_STUPID)) || (FLAG(r_ptr, RF_WEIRD_MIND)) || (FLAG(r_ptr, RF_ANIMAL)) || (r_ptr->hdice * 2 > (3 * dam / 2))) { dam /= 3; } else if (((FLAG(r_ptr, RF_UNDEAD)) || (FLAG(r_ptr, RF_DEMON))) && (r_ptr->hdice * 2 > bp_ptr->lev / 2)) { dam = 0; } break; } case GF_KILL_WALL: { /* Stone to Mud */ if (!(FLAG(r_ptr, RF_HURT_ROCK))) dam = 0; break; } case GF_NETHER: { /* Nether */ if (FLAG(r_ptr, RF_UNDEAD)) { dam = 0; } else if (FLAG(r_ptr, RF_BR_NETH)) { dam *= 3; dam /= 9; } else if (FLAG(r_ptr, RF_EVIL)) { dam /= 2; } break; } case GF_CHAOS: { /* Chaos */ if ((FLAG(r_ptr, RF_BR_CHAO)) || (FLAG(r_ptr, RF_DEMON))) { dam *= 3; dam /= 9; } break; } case GF_GRAVITY: { /* Gravity */ if (FLAG(r_ptr, RF_BR_GRAV)) { dam *= 2; dam /= 9; } break; } case GF_SHARDS: { /* Shards */ if (FLAG(r_ptr, RF_BR_SHAR)) { dam *= 3; dam /= 9; } break; } case GF_ROCKET: { /* Rockets */ if (FLAG(r_ptr, RF_BR_SHAR)) { dam /= 2; } break; } case GF_SOUND: { /* Sound */ if (FLAG(r_ptr, RF_BR_SOUN)) { dam *= 2; dam /= 9; } break; } case GF_PLASMA: { /* Plasma */ if ((FLAG(r_ptr, RF_BR_PLAS)) || (FLAG(r_ptr, RF_RES_PLAS))) { dam *= 2; dam /= 9; } break; } case GF_FORCE: { /* Force */ if (FLAG(r_ptr, RF_BR_WALL)) { dam *= 2; dam /= 9; } break; } case GF_DARK: { /* Dark */ if (FLAG(r_ptr, RF_BR_DARK)) { dam *= 2; dam /= 9; } break; } case GF_WATER: { /* Water */ if (FLAG(r_ptr, RF_RES_WATE)) { dam *= 2; dam /= 9; } break; } case GF_DISINTEGRATE: { /* Disintegrate */ if (FLAG(r_ptr, RF_RES_DISE)) { dam *= 2; dam /= 9; } break; } case GF_TELEKINESIS: { if (FLAG(r_ptr, RF_UNIQUE)) dam /= 3; break; } case GF_METEOR: { /* Meteor */ break; } case GF_DISP_GOOD: { /* Dispel Good */ if (!(FLAG(r_ptr, RF_GOOD))) dam = 0; break; } case GF_DISP_LIVING: { /* Dispel Living */ if (!monster_living(r_ptr)) dam = 0; break; } case GF_CONFUSION: case GF_DISENCHANT: case GF_NEXUS: case GF_INERTIA: case GF_TIME: { /* Weird attacks */ dam /= 2; break; } case GF_DOMINATION: case GF_CHARM: case GF_CONTROL_UNDEAD: case GF_CONTROL_ANIMAL: { /* Really weird attacks */ dam = 0; break; } case GF_OLD_HEAL: case GF_OLD_CLONE: case GF_OLD_SPEED: case GF_DARK_WEAK: case GF_KILL_DOOR: case GF_KILL_TRAP: case GF_MAKE_WALL: case GF_MAKE_DOOR: case GF_MAKE_TRAP: case GF_AWAY_UNDEAD: case GF_TURN_EVIL: { /* Various */ dam = 0; break; } case GF_AWAY_ALL: { /* These spells which put the monster out of commission, we * look at the danger of the monster prior to and after being * put out of commission. The difference is the damage. * The following factors are considered when we * consider the spell: * * 1. Is it already comprised by that spell? * 2. Is it comprimised by another spell? * 3. Does it resist the modality? * 4. Will it make it's savings throw better than half the time? * 5. We generally ignore these spells for breeders. * * The spell sleep II and sanctuary have a special consideration * since the monsters must be adjacent to the player. */ dam = borg_danger_aux(c_x, c_y, 1, i, TRUE); /* try not to teleport away uniques. These are the guys you are trying */ /* to kill! */ if (FLAG(r_ptr, RF_UNIQUE)) { /* If this unique is causing the danger, get rid of it */ if (dam > avoidance * 3 && bp_ptr->depth <= 95) { /* get rid of this unique */ } else dam = -999; } break; } case GF_DISP_ALL: { /* In Z this does hurt Uniques but not in V */ break; } case GF_OLD_CONF: { dam = 0; if (FLAG(r_ptr, RF_NO_CONF)) break; if (FLAG(r_ptr, RF_MULTIPLY)) break; if (kill-> m_flags & (MONST_ASLEEP | MONST_CONFUSED | MONST_FEAR)) break; if ((r_ptr->hdice * 2 >= (bp_ptr->lev < 13) ? bp_ptr->lev : (((bp_ptr->lev - 10) / 4) * 3) + 10)) break; dam = -999; if (FLAG(r_ptr, RF_UNIQUE)) break; borg_confuse_spell = FALSE; p1 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_confuse_spell = TRUE; p2 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_confuse_spell = FALSE; dam = (p1 - p2); break; } case GF_TURN_ALL: { dam = 0; if (FLAG(r_ptr, RF_NO_FEAR)) break; if (kill-> m_flags & (MONST_ASLEEP | MONST_CONFUSED | MONST_FEAR)) break; if ((r_ptr->hdice * 2 >= (bp_ptr->lev < 13) ? bp_ptr->lev : (((bp_ptr->lev - 10) / 4) * 3) + 10)) break; dam = -999; if (FLAG(r_ptr, RF_UNIQUE)) break; borg_fear_mon_spell = FALSE; p1 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_fear_mon_spell = TRUE; p2 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_fear_mon_spell = FALSE; dam = (p1 - p2); break; } case GF_OLD_SLOW: { dam = 0; if (kill-> m_flags & (MONST_ASLEEP | MONST_CONFUSED | MONST_FEAR)) break; if ((r_ptr->hdice * 2 >= (bp_ptr->lev < 13) ? bp_ptr->lev : (((bp_ptr->lev - 10) / 4) * 3) + 10)) break; dam = -999; if (FLAG(r_ptr, RF_UNIQUE)) break; borg_slow_spell = FALSE; p1 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_slow_spell = TRUE; p2 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_slow_spell = FALSE; dam = (p1 - p2); break; } case GF_OLD_SLEEP: case GF_STASIS: { dam = 0; if (FLAG(r_ptr, RF_NO_SLEEP)) break; if (kill-> m_flags & (MONST_ASLEEP | MONST_CONFUSED | MONST_FEAR)) break; if ((r_ptr->hdice * 2 >= (bp_ptr->lev < 13) ? bp_ptr->lev : (((bp_ptr->lev - 10) / 4) * 3) + 10)) break; dam = -999; if (FLAG(r_ptr, RF_UNIQUE)) break; borg_sleep_spell = FALSE; p1 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_sleep_spell = TRUE; p2 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_sleep_spell = FALSE; dam = (p1 - p2); break; } case GF_OLD_POLY: { dam = 0; if ((r_ptr->hdice * 2 >= (bp_ptr->lev < 13) ? bp_ptr->lev : (((bp_ptr->lev - 10) / 4) * 3) + 10)) break; dam = -999; if (FLAG(r_ptr, RF_UNIQUE)) break; dam = borg_danger_aux(c_x, c_y, 2, i, TRUE); /* dont bother unless he is a scary monster */ if (dam < avoidance * 2) dam = 0; break; } case GF_TURN_UNDEAD: { if (FLAG(r_ptr, RF_UNDEAD)) { dam = 0; if (kill-> m_flags & (MONST_ASLEEP | MONST_CONFUSED | MONST_FEAR)) break; if (r_ptr->level > bp_ptr->lev - 5) break; borg_fear_mon_spell = FALSE; p1 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_fear_mon_spell = TRUE; p2 = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_fear_mon_spell = FALSE; dam = (p1 - p2); } else { dam = 0; } break; } case GF_AWAY_EVIL: { /* Banishment-- cast when in extreme danger (checked in borg_defense). */ if (FLAG(r_ptr, RF_EVIL)) { /* try not teleport away uniques. */ if (FLAG(r_ptr, RF_UNIQUE)) { /* Banish ones with escorts */ if (FLAG(r_ptr, RF_ESCORT)) { dam = 0; } else { /* try not Banish non escorted uniques */ dam = -500; } } else { /* damage is the danger of the baddie */ dam = borg_danger_aux(c_x, c_y, 1, i, TRUE); } } else { dam = 0; } break; } } /* use Missiles on certain types of monsters */ if ((borg_danger_aux(kill->x, kill->y, 1, i, TRUE) >= avoidance * 3 / 10) || (FLAG(r_ptr, RF_FRIENDS) /* monster has friends */ && r_ptr->level >= bp_ptr->lev - 5 /* close levels */ ) || (kill->ranged_attack /* monster has a ranged attack */ ) || (FLAG(r_ptr, RF_UNIQUE)) || (FLAG(r_ptr, RF_MULTIPLY)) || (bp_ptr->lev <= 5 /* stil very weak */ )) { borg_use_missile = TRUE; } /* Restore normal calcs of danger */ borg_full_damage = FALSE; /* dont hurt friends or pets */ if (kill->m_flags & (MONST_FRIEND | MONST_PET)) dam = -10; /* Invuln monsters take no dam */ if (kill->m_flags & MONST_INVULN) dam = 0; /* Return Damage as pure danger of the monster */ if (typ == GF_AWAY_ALL || typ == GF_AWAY_EVIL) return (dam); /* Limit damage to twice maximal hitpoints */ if (dam > kill->power * 2) dam = kill->power * 2; /* give a small bonus for whacking a unique */ /* this should be just enough to give prefrence to wacking uniques */ if ((FLAG(r_ptr, RF_UNIQUE)) && bp_ptr->depth >= 1) dam = (dam * 5); /* * Hack -- ignore Maggot until later. Player will chase Maggot * down all accross the screen waking up all the monsters. Then * he is stuck in a comprimised situation. */ if ((FLAG(r_ptr, RF_UNIQUE)) && bp_ptr->depth == 0) { dam = dam * 2 / 3; /* Dont hunt maggot until later */ if (bp_ptr->lev < 5) dam = 0; } /* give a small bonus for whacking a breeder */ if (FLAG(r_ptr, RF_MULTIPLY)) dam = (dam * 3 / 2); /* * Enhance the preceived damage to summoner in order to influence the * choice of targets. */ if ((FLAG(r_ptr, RF_S_KIN)) || (FLAG(r_ptr, RF_S_CYBER)) || (FLAG(r_ptr, RF_S_MONSTER)) || (FLAG(r_ptr, RF_S_MONSTERS)) || (FLAG(r_ptr, RF_S_ANT)) || (FLAG(r_ptr, RF_S_SPIDER)) || (FLAG(r_ptr, RF_S_HOUND)) || (FLAG(r_ptr, RF_S_HYDRA)) || (FLAG(r_ptr, RF_S_ANGEL)) || (FLAG(r_ptr, RF_S_DEMON)) || (FLAG(r_ptr, RF_S_UNDEAD)) || (FLAG(r_ptr, RF_S_DRAGON)) || (FLAG(r_ptr, RF_S_HI_UNDEAD)) || (FLAG(r_ptr, RF_S_HI_DRAGON)) || (FLAG(r_ptr, RF_S_AMBERITES)) || (FLAG(r_ptr, RF_S_UNIQUE)) || (FLAG(r_ptr, RF_QUESTOR))) dam += ((dam * 3) / 2); /* Try to conserve missiles. */ if ((!borg_use_missile) && (typ == GF_ARROW || (typ >= GF_ARROW_FLAME && typ <= GF_ARROW_DRAGON))) { /* Set damage to zero, force borg to melee attack */ dam = 0; } /* Damage */ return (dam); } /* Simulate the launching of a bolt at a monster */ static int borg_launch_aux_hack(int i, int dam, int typ) { int d, p, x, y; map_block *mb_ptr; /* Monster */ borg_kill *kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) return (0); /* Require current knowledge */ if (kill->when < borg_t) return (0); /* Acquire location */ x = kill->x; y = kill->y; /* Bounds checking */ if (!map_in_bounds(x, y)) return (0); /* Acquire the grid */ mb_ptr = map_loc(x, y); /* Calculate damage */ d = borg_launch_damage_one(i, dam, typ); /* Calculate danger */ borg_full_damage = TRUE; p = borg_danger_aux(x, y, 1, i, TRUE); borg_full_damage = FALSE; /* Return Damage as pure danger of the monster */ if (typ == GF_AWAY_ALL || typ == GF_AWAY_EVIL) return (d); /* Return 0 if the true damge (w/o the danger bonus) is 0 */ if (d <= 0) return (d); /* Hack -- avoid waking most "hard" sleeping monsters */ if ((kill->m_flags & MONST_ASLEEP) && (p > avoidance / 2) && (d < kill->power)) { return (-999); } /* Hack -- ignore sleeping town monsters */ if (!bp_ptr->depth && (kill->m_flags & MONST_ASLEEP)) { return (0); } /* Calculate "danger" to player */ borg_full_damage = TRUE; p = borg_danger_aux(c_x, c_y, 2, i, TRUE); borg_full_damage = FALSE; /* Reduce "bonus" of partial kills */ if (d < kill->power) p = p / 10; /* Add in power */ d += p; /* Result */ return (d); } /* Determine the "reward" of casting a bolt.*/ static int borg_launch_bolt(int dam, int typ, int max) { int i; int x, y; int n, b_n = 0; map_block *mb_ptr; /* Loop through all the boltable monsters */ for (i = 0; i < borg_bolt_n; i++) { /* Acquire location */ x = borg_bolt_x[i]; y = borg_bolt_y[i]; /* Maximal distance */ if (distance(c_x, c_y, x, y) > max) break; /* Get the grid */ mb_ptr = map_loc(x, y); /* Collect damage */ n = borg_launch_aux_hack(mb_ptr->kill, dam, typ); /* Is it better than before? */ if (n <= b_n) continue; /* Track this location */ b_n = n; g_x = x; g_y = y; } /* Result */ return (b_n); } /* Determine the "reward" of casting a beam. */ int borg_launch_beam(int dam, int typ, int max) { int i; int x, y; int n, b_n = 0; map_block *mb_ptr; /* Loop through all the beamable monsters */ for (i = 0; i < borg_beam_n; i++) { /* Acquire location of the beamable monsters */ x = borg_beam_x[i]; y = borg_beam_y[i]; /* Maximal distance */ if (distance(c_x, c_y, x, y) > max) break; /* Check the path for the beam */ borg_mmove_init(c_x, c_y, x, y); /* Reset Counters */ x = c_x; y = c_y; n = 0; /* Loop through the possible grids on the path */ while (TRUE) { /* Bounds checking */ if (!map_in_bounds(x, y)) break; /* Get the grid */ mb_ptr = map_loc(x, y); /* Maximal distance */ if (distance(c_x, c_y, x, y) > max) break; /* Collect damage */ n = borg_launch_aux_hack(mb_ptr->kill, dam, typ); /* Stop beaming when the beam hits a wall */ if (borg_cave_wall_grid(mb_ptr)) break; /* Get next grid */ borg_mmove(&x, &y, c_x, c_y); } /* Is it better than before? */ if (n <= b_n) continue; /* Track this location */ b_n = n; g_x = x; g_y = y; } /* Result */ return (b_n); } /* Determine the "reward" of casting a dispel */ static int borg_launch_dispel(int dam, int typ, int rad) { int i; int x, y; int n = 0; map_block *mb_ptr; /* Loop through all the monsters in LOS */ for (i = 0; i < borg_beam_n; i++) { /* Acquire location */ x = borg_beam_x[i]; y = borg_beam_y[i]; /* Maximal distance */ if (distance(c_x, c_y, x, y) > rad) continue; /* Get the grid */ mb_ptr = map_loc(x, y); /* Collect damage */ n += borg_launch_aux_hack(mb_ptr->kill, dam, typ); } /* Just making sure */ g_x = c_x; g_y = c_y; /* Result */ return (n); } static int borg_ball_item(map_block *mb_ptr, int typ) { object_kind *k_ptr = &k_info[mb_ptr->object]; /* check destroyed stuff. */ if (!mb_ptr->object) return (0); switch (typ) { case GF_ACID: { /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_BOOTS) return (-200); } case GF_ELEC: { /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_RING) return (-200); } case GF_FIRE: { /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_BOOTS) return (-200); } case GF_COLD: { /* So many nice potions to be missed */ if (k_ptr->tval == TV_POTION) return (-200); } case GF_MANA: { /* Used against uniques, allow the stuff to burn */ return (0); } default: return (0); } } /* Determine the "reward" of casting a ball with radius = 0.*/ static int borg_launch_ball_zero(int dam, int typ, int max) { int i; int x, y; int n, b_n = 0; map_block *mb_ptr; /* Loop through all the ballable monsters in LOS */ for (i = 0; i < borg_beam_n; i++) { /* Acquire location */ x = borg_beam_x[i]; y = borg_beam_y[i]; /* Maximal distance */ if (distance(c_x, c_y, x, y) > max) continue; /* Get the grid */ mb_ptr = map_loc(x, y); /* Collect damage */ n = borg_launch_aux_hack(mb_ptr->kill, dam, typ); /* Does this cost me items? */ n += borg_ball_item(mb_ptr, typ); /* Is it better than before? */ if (n <= b_n) continue; /* Track this location */ b_n = n; g_x = x; g_y = y; } /* Result */ return (b_n); } /* Determine the "reward" of casting a ball centered on the player. */ static int borg_launch_blast(int dam, int typ, int max) { int i, r; int x, y; int n = 0; map_block *mb_ptr; /* Loop through all the ballable monsters in LOS */ for (i = 0; i < borg_beam_n; i++) { /* Acquire location */ x = borg_beam_x[i]; y = borg_beam_y[i]; /* What is the distance */ r = distance(c_x, c_y, x, y); /* Maximal distance */ if (r > max) continue; /* Get the grid */ mb_ptr = map_loc(x, y); /* Collect damage */ n = borg_launch_aux_hack(mb_ptr->kill, dam / (r + 1), typ); /* Does this cost me items? */ n += borg_ball_item(mb_ptr, typ); } /* Result */ return (n); } /* Determine the "reward" of an attack on the monsters around the borg */ static int borg_launch_touch(int dam, int typ) { int i; int x, y; int n = 0; map_block *mb_ptr; /* Loop through all the touchable monsters in LOS */ for (i = 0; i < borg_next_n; i++) { /* Acquire location */ x = borg_next_x[i]; y = borg_next_y[i]; /* Get the grid */ mb_ptr = map_loc(x, y); /* Collect damage */ n = borg_launch_aux_hack(mb_ptr->kill, dam, typ); } /* Result */ return (n); } /* * Determine the "reward" of casting a ball * * Basically, we sum the "rewards" of doing the appropriate amount of * damage to each of the "affected" monsters. * */ static int borg_launch_ball(int rad, int dam, int typ, int max) { int i, j, r; int x, y, x1, y1; int n, b_n = 0; map_block *mb_ptr; /* Balls with rad = 0 get special treatment */ if (rad == BORG_BALL_RAD0) return (borg_launch_ball_zero(dam, typ, max)); /* Loop through all the grids with a monster or monster next to it */ for (i = 0; i < borg_ball_n; i++) { /* Acquire location */ x = borg_ball_x[i]; y = borg_ball_y[i]; /* Maximal distance */ if (distance(c_x, c_y, x, y) > max) continue; /* Reset counter */ n = 0; /* loop through all close monsters to find the ones hit by the ball */ for (j = 0; j < borg_temp_n; j++) { /* Acquire location */ x1 = borg_temp_x[j]; y1 = borg_temp_y[j]; /* Get the distance */ r = distance(x, y, x1, y1); /* Is it within blast radius */ if (r > rad) continue; /* Bounds checking */ if (!map_in_bounds(x1, y1)) continue; /* Get the grid */ mb_ptr = map_loc(x1, y1); /* Collect damage, lowered by distance */ n += borg_launch_aux_hack(mb_ptr->kill, dam / (r + 1), typ); /* Does this cost me items? */ n += borg_ball_item(mb_ptr, typ); } /* Is it a better location than before? */ if (n <= b_n) continue; /* Track it */ b_n = n; g_x = x; g_y = y; } /* Result */ return (b_n); } /* Whirlwind -- Attacks all adjacent monsters */ static int borg_attack_whirlwind(void) { int y = 0, x = 0; int i; int dam = 0; map_block *mb_ptr; if (borg_simulate) { /* Scan neighboring grids */ for (i = 0; i < borg_next_n; i++) { /* Fetch the coords */ y = borg_next_y[i]; x = borg_next_x[i]; /* Fetch the spot on the map */ mb_ptr = map_loc(x, y); /* is there a kill next to me */ if (mb_ptr->kill) { /* Calculate "average" damage */ dam += borg_thrust_damage_one(mb_ptr->kill); } } /* Return the damage for consideration */ return (dam); } /* Not supposed to happen */ borg_oops("The borg can't cast Whirlwind from here"); return (0); } /* * This function assumes that the string act contains "rad. xxx" * and converts xxx to a number */ static int borg_find_radius(cptr act) { char *here; /* Just checking */ if (!act) return (0); /* Find the substring for the radius */ here = strstr(act, "rad. "); /* If no radius is mentioned give up */ if (!here) return (0); /* Jump past the search string */ here = here + 5; /* Return the radius */ return (atoi(here)); } /* * This function assumes that the string act contains "(xxx" or (xdy * and converts xxx or xdy to a number. */ static int borg_find_damage(cptr act) { char *here; int dam = 0, ds = 0, level = 0; /* Just checking */ if (!act) return (0); /* Find the substring for the damage */ here = strstr(act, "("); /* If no damage is mentioned give up */ if (!here) return (0); /* Jump past the search string */ here = here + 1; /* If the damage is multiplied by level */ if (prefix(act, "level * ")) { /* Supply the level */ level = bp_ptr->lev; /* Jump past the substring */ act += 8; } /* As long as the string has digits in it */ while (*here - '0' >= 0 && *here - '0' <= 9) { /* create the damage */ dam = dam * 10 + *here++ - '0'; } /* Multiply if necessary */ if (level) dam *= level; /* Is the damage composed of dd and ds? */ if (*here == 'd') { /* Jump past the die */ here = here + 1; /* create the die */ ds = atoi(here); /* calculate the average damage */ dam = dam * (ds + 1) / 2; } /* return the damage found */ return (dam); } /* Determine if this activation can do damage */ static int borg_damage_artifact_monster(cptr act) { int rad = 0, gf = 0, dam = 0, style = 0; bool stop; /* Go through the string, word for word */ while (act) { /* Initialize */ stop = FALSE; /* For efficiency first check the first letter */ switch (*act) { case 'a': { if (prefix(act, "acid")) gf = GF_ACID; else if (prefix(act, "arrow")) { style = BORG_BOLT; gf = GF_ARROW; } break; } case 'b': { if (prefix(act, "bolt")) style = BORG_BOLT; else if (prefix(act, "beam")) style = BORG_BEAM; else if (prefix(act, "ball")) style = BORG_BALL; else if (prefix(act, "breathe")) style = BORG_BALL; else if (prefix(act, "blast")) style = BORG_BLAST; else if (prefix(act, "banish")) { style = BORG_DISPEL; dam = 30; if (prefix(act, "banishment")) gf = GF_AWAY_ALL; else if (prefix(act, "banish evil")) gf = GF_AWAY_EVIL; else if (prefix(act, "banish undead")) gf = GF_AWAY_UNDEAD; } break; } case 'c': { if (prefix(act, "cloud")) style = BORG_BALL; else if (prefix(act, "cold")) gf = GF_COLD; else if (prefix(act, "confusion")) gf = GF_CONFUSION; else if (prefix(act, "confuse")) gf = GF_OLD_CONF; else if (prefix(act, "chaos")) gf = GF_CHAOS; else if (prefix(act, "call")) { style = BORG_BALL; dam = 150; } break; } case 'd': { if (prefix(act, "dark")) gf = GF_DARK; if (prefix(act, "drain life")) { style = BORG_BOLT; gf = GF_OLD_DRAIN; } if (prefix(act, "dispel")) { style = BORG_DISPEL; if (prefix(act, "dispel evil")) gf = GF_DISP_EVIL; else if (prefix(act, "dispel good")) gf = GF_DISP_GOOD; else if (prefix(act, "dispel demons")) gf = GF_DISP_DEMON; else if (prefix(act, "dispel living")) gf = GF_DISP_LIVING; else if (prefix(act, "dispel monster")) gf = GF_DISP_ALL; } break; } case 'e': { if (prefix(act, "elements")) gf = GF_MISSILE; else if (prefix(act, "every")) stop = TRUE; break; } case 'f': { /* Hack to prevent holy/hell fire from being overwritten */ if (prefix(act, "fire") && !gf) gf = GF_FIRE; else if (prefix(act, "frost")) gf = GF_COLD; else if (prefix(act, "force")) gf = GF_FORCE; break; } case 'g': { if (prefix(act, "gravity")) gf = GF_GRAVITY; break; } case 'h': { if (prefix(act, "holy fire")) gf = GF_HOLY_FIRE; else if (prefix(act, "hell fire")) gf = GF_HELL_FIRE; break; } case 'i': { if (prefix(act, "inertia")) gf = GF_INERTIA; else if (prefix(act, "ice")) gf = GF_ICE; else if (prefix(act, "illumination")) { style = BORG_DISPEL; gf = GF_LITE_WEAK; dam = 18; } break; } case 'l': { if (prefix(act, "large")) rad = BORG_BALL_RAD3; else if (prefix(act, "lightning")) gf = GF_ELEC; else if (prefix(act, "light")) { gf = GF_LITE; if (prefix(act, "light area")) { style = BORG_DISPEL; gf = GF_LITE_WEAK; rad = BORG_BALL_RAD3; } } break; } case 'm': { if (prefix(act, "mana")) gf = GF_MANA; else if (prefix(act, "missile")) { style = BORG_BOLT; gf = GF_MISSILE; } /* Hack to prevent overwriting sleep_touch */ else if (prefix(act, "monster") && !style) { style = BORG_BOLT; if (prefix(act, "monsters")) style = BORG_DISPEL; } break; } case 'n': { if (prefix(act, "nether")) gf = GF_NETHER; else if (prefix(act, "nexus")) gf = GF_NEXUS; else if (prefix(act, "nuke")) gf = GF_NUKE; break; } case 'p': { if (prefix(act, "poison")) gf = GF_POIS; else if (prefix(act, "plasma")) gf = GF_PLASMA; break; } case 'r': { if (prefix(act, "rad.")) rad = borg_find_radius(act); else if (prefix(act, "rocket")) { style = BORG_BALL; gf = GF_ROCKET; } break; } case 's': { if (prefix(act, "star")) gf = GF_ELEC; else if (prefix(act, "stinking")) gf = GF_POIS; else if (prefix(act, "shards")) gf = GF_SHARDS; else if (prefix(act, "sound")) gf = GF_SOUND; else if (prefix(act, "sunlight")) gf = GF_LITE_WEAK; else if (prefix(act, "sleep")) { gf = GF_OLD_SLEEP; dam = 20; if (prefix(act, "sleep nearby")) style = BORG_TOUCH; } else if (prefix(act, "slow")) { gf = GF_OLD_SLOW; dam = 20; } if (prefix(act, "strangling")) { style = BORG_BOLT; gf = GF_OLD_DRAIN; } if (prefix(act, "stone to mud")) { style = BORG_BOLT; gf = GF_KILL_WALL; } break; } case 't': { if (prefix(act, "time")) gf = GF_TIME; else if (prefix(act, "turn")) { style = BORG_DISPEL; dam = 20; if (prefix(act, "turns")) stop = TRUE; else if (prefix(act, "turn monsters")) gf = GF_TURN_ALL; else if (prefix(act, "turn evil")) gf = GF_TURN_EVIL; } else if (prefix(act, "teleport away")) { style = BORG_BEAM; gf = GF_AWAY_ALL; dam = 50; } break; } case 'v': { if (prefix(act, "vampiric drain")) { style = BORG_BOLT; gf = GF_OLD_DRAIN; } break; } case 'w': { if (prefix(act, "water")) gf = GF_WATER; else if (prefix(act, "whirlwind")) { style = BORG_TOUCH; dam = 1; gf = MAX_GF; } break; } case '(': { dam = borg_find_damage(act); break; } default: break; } /* Cut off */ if (stop) break; /* Skip until next word */ while (act && !stop) { /* Stop after a space was read */ stop = *act == ' '; /* Next letter */ act++; } } /* Not enough info */ if (!style || !dam || !gf) return (0); /* Calculate the potential damage */ switch (style) { case BORG_BOLT: return (borg_launch_bolt(dam, gf, MAX_RANGE)); case BORG_BEAM: return (borg_launch_beam(dam, gf, MAX_RANGE)); case BORG_BALL: { /* Set to default */ if (!rad) rad = BORG_BALL_RAD2; return (borg_launch_ball(rad, dam, gf, MAX_RANGE)); } case BORG_DISPEL: { /* Set to default */ if (!rad) rad = MAX_RANGE; return (borg_launch_dispel(dam, gf, rad)); } case BORG_BLAST: return (borg_launch_blast(dam, gf, rad)); case BORG_TOUCH: { /* Hacking Whirlwind */ if (gf == MAX_GF) return (borg_attack_whirlwind()); return (borg_launch_touch(dam, gf)); } default: return (0); } } /* Simulate/Apply the optimal result of activating an artifact */ static int borg_attack_artifact(int *b_slot) { int i, n, b_n = 0; list_item *l_ptr; cptr act; if (borg_simulate) { for (i = 0; i < equip_num; i++) { /* What item is this */ l_ptr = look_up_equip_slot(i); /* Is this item an artifact that can be activated now? */ if (!borg_check_artifact(l_ptr, TRUE)) continue; /* Hack! Get the activation */ act = item_activation(&p_ptr->equipment[i]); /* Get the attack value */ n = borg_damage_artifact_monster(act); /* Is it better than before? */ if (n <= b_n) continue; /* Keep track of the scroll */ *b_slot = i; b_n = n; } /* Return the value of the simulation */ return (b_n); } /* Set the target */ borg_target(g_x, g_y); /* Do it */ borg_note("# Activating artifact %s", equipment[*b_slot].o_name); /* Activate the artifact */ borg_keypress('A'); borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } static int borg_scroll_damage_monster(int sval) { switch (sval) { case SV_SCROLL_ICE: { /* With resistancy it is safe to read this scroll */ if (FLAG(bp_ptr, TR_RES_COLD)) { /* How much damage from a cold ball? */ return (borg_launch_dispel(150, GF_COLD, BORG_BALL_RAD4)); } return (0); } case SV_SCROLL_FIRE: { /* With resistancy it is safe to read this scroll */ if (FLAG(bp_ptr, TR_RES_FIRE)) { /* How much damage from a fire ball? */ return (borg_launch_dispel(75, GF_FIRE, BORG_BALL_RAD4)); } return (0); } /* Scroll of Logrus */ case SV_SCROLL_CHAOS: { /* With resistancy it is safe to read this scroll */ if (FLAG(bp_ptr, TR_RES_CHAOS)) { /* How much damage from a chaos ball? */ return (borg_launch_dispel(225, GF_CHAOS, BORG_BALL_RAD4)); } return (0); } case SV_SCROLL_DISPEL_UNDEAD: { /* Damage all the undead in LOS. */ return (borg_launch_dispel(60, GF_DISP_UNDEAD, MAX_SIGHT)); } default: { /* This scroll is a dud, damagewise*/ return (0); } } } /* * Simulate/Apply the optimal result of reading a scroll * */ static int borg_attack_scroll(int *b_slot) { int n, b_n = 0; int k; /* Simulation */ if (borg_simulate) { /* No reading while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Try all scrolls */ for (k = 0; k < inven_num; k++) { list_item *l_ptr = &inventory[k]; /* Skip the wrong scrolls */ if (l_ptr->tval != TV_SCROLL) continue; /* How much damage does this scroll do? */ n = borg_scroll_damage_monster(k_info[l_ptr->k_idx].sval); /* Is it better than before? */ if (n <= b_n) continue; /* Keep track of the scroll */ *b_slot = k; b_n = n; } /* Return the value of the simulation */ return (b_n); } /* Do it */ borg_note("# Reading scroll '%s'", inventory[*b_slot].o_name); /* Read the scroll */ borg_keypress('r'); borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* This function checks if some missile has a certain damage_type */ static bool borg_missile_equals_type(list_item *l_ptr, int gf_i) { /* Just making sure it is a missile */ if (!l_ptr || l_ptr->tval < TV_SHOT || l_ptr->tval > TV_BOLT) return (FALSE); switch (gf_i) { /* Normal, unidentified, wounding or slaying missiles */ case GF_ARROW: { if (borg_obj_is_ego_art(l_ptr) && !strstr(l_ptr->o_name, "Wounding") && !strstr(l_ptr->o_name, "Returning") && !strstr(l_ptr->o_name, "Slaying")) return (FALSE); return (TRUE); } /* Flaming missiles */ case GF_ARROW_FLAME: return (KN_FLAG(l_ptr, TR_BRAND_FIRE)); /* Freezing missiles */ case GF_ARROW_FROST: return (KN_FLAG(l_ptr, TR_BRAND_COLD)); /* Electric missiles */ case GF_ARROW_SHOCKING: return (KN_FLAG(l_ptr, TR_BRAND_ELEC)); /* Amimal missiles */ case GF_ARROW_ANIMAL: return (KN_FLAG(l_ptr, TR_SLAY_ANIMAL)); /* Evil missiles */ case GF_ARROW_EVIL: return (KN_FLAG(l_ptr, TR_SLAY_EVIL)); /* Dragon missiles */ case GF_ARROW_DRAGON: return (KN_FLAG(l_ptr, TR_SLAY_DRAGON)); /* Exploding missiles */ case GF_ARROW_EXPLOSION: return (KN_FLAG(l_ptr, TR_EXPLODE)); default: { return (FALSE); } } } /* This function returns the damage type of some missile */ static bool borg_missile_type(list_item *l_ptr) { /* Just making sure it is a missile */ if (!l_ptr || l_ptr->tval < TV_SHOT || l_ptr->tval > TV_BOLT) return (0); /* Cursed missiles are ignored */ if (!streq(l_ptr->o_name, "") && strstr(l_ptr->o_name, "{cursed")) return (GF_NONE); /* Flaming missiles */ if (KN_FLAG(l_ptr, TR_BRAND_FIRE)) return (GF_ARROW_FLAME); /* Freezing missiles */ if (KN_FLAG(l_ptr, TR_BRAND_COLD)) return (GF_ARROW_FROST); /* Electric missiles */ if (KN_FLAG(l_ptr, TR_BRAND_ELEC)) return (GF_ARROW_SHOCKING); /* Amimal missiles */ if (KN_FLAG(l_ptr, TR_SLAY_ANIMAL)) return (GF_ARROW_ANIMAL); /* Evil missiles */ if (KN_FLAG(l_ptr, TR_SLAY_EVIL)) return (GF_ARROW_EVIL); /* Dragon missiles */ if (KN_FLAG(l_ptr, TR_SLAY_DRAGON)) return (GF_ARROW_DRAGON); /* Exploding missiles */ if (KN_FLAG(l_ptr, TR_EXPLODE)) return (GF_ARROW_EXPLOSION); /* None of the listed types so it must be a normal missile */ return (GF_ARROW); } /* * Simulate/Apply the optimal result of launching a missile * * Check out which ammo is available and then call the apropriate routine for it * */ static int borg_attack_launch(int *b_slot) { int n, b_n = 0; int b_x = 0, b_y = 0; int i, k, b_k = 0; int d, b_d; int gf_i; list_item *l_ptr; list_item *bow = look_up_equip_slot(EQUIP_BOW); /* Simulation */ if (borg_simulate) { /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Is there a bow? */ if (!bow) return (0); /* Scan the pack to find out where the missiles are */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Skip non-missiles */ if (l_ptr->tval != my_ammo_tval) continue; /* Skip missiles that have been considered already */ if (l_ptr->treat_as == TREAT_AS_GONE) { l_ptr->treat_as = TREAT_AS_NORM; continue; } /* Determine type */ gf_i = borg_missile_type(l_ptr); /* Reset tracker */ b_d = 0; /* Search the rest of the missiles for the current type */ for (k = i ; k < inven_num; k++) { l_ptr = &inventory[k]; /* Stop when a non-missile is encountered */ if (l_ptr->tval < my_ammo_tval) break; /* Is this an missile of the current type? */ if (!borg_missile_equals_type(l_ptr, gf_i)) continue; /* Skip this missile in the future loops */ if (k != i) l_ptr->treat_as = TREAT_AS_GONE; /* Determine average damage */ d = (l_ptr->dd * (l_ptr->ds + 1) / 2); d = d + (100 + 3 * (l_ptr->to_d + bow->to_d)) / 100; d = d * bp_ptr->b_max_dam / 6; /* Is it better than the previous missile */ if (d <= b_d) continue; /* Missiles tend to miss a lot, let's assume 50% */ d = d / 2; /* Track this missile */ b_d = d; b_k = k; } /* Find a target */ n = borg_launch_bolt(b_d, gf_i, MAX_RANGE); /* Is it better than before? */ if (n <= b_n) continue; *b_slot = b_k; b_n = n; b_x = g_x; b_y = g_y; } /* Set the targetting globals */ g_x = b_x; g_y = b_y; /* Return the value of the simulation */ return (b_n); } /* Set the target */ borg_target(g_x, g_y); /* Do it */ borg_note("# Firing missile '%s'", inventory[*b_slot].o_name); /* Fire */ borg_keypress('f'); /* Use the missile */ borg_keypress(I2A(*b_slot)); /* Reset our shooting flag */ if (successful_target == BORG_TARGET) { successful_target = BORG_ARROW_TARGET; } /* * Arrows tend to miss so there is a count down. BORG_ARROW_TARGET is a bit * larger then BORG_FRESH_TARGET. This has as a result that the borg has * five shots to hit a monster across unknown terrain. After that he'll * stick to monsters in known terrain */ successful_target = successful_target - 1; /* Value */ return (b_n); } /* * This procedure determines the damage that an object can do when thrown. * If you want to avoid a certain object to be thrown then it should appear * in the switch. */ static int borg_throw_damage(list_item *l_ptr, int *typ) { int tval = l_ptr->tval; int d; /* Determine average damage from object */ d = (l_ptr->dd * (l_ptr->ds + 1) / 2); /* Set the damage type */ *typ = GF_ARROW; /* Skip un-identified, non-average, objects */ if (!borg_obj_known_p(l_ptr) && !strstr(l_ptr->o_name, "{average") && !strstr(l_ptr->o_name, "{cursed") && !strstr(l_ptr->o_name, "{dubious")) return (0); /* What sort of object have we here? */ switch (tval) { /* Don't throw all the flasks when wearing a lantern */ case TV_FLASK: { list_item* q_ptr = look_up_equip_slot(EQUIP_LITE); /* * Don't throw the flask if the borg wields a lantern and * he has only a few flasks. * Throw it anyway if he is fighting a unique */ if (q_ptr && k_info[q_ptr->k_idx].sval == SV_LITE_LANTERN && bp_ptr->able.fuel <= 7 && !borg_fighting_unique) return (0); /* Throw the flask */ return (d); } case TV_LITE: { list_item* q_ptr = look_up_equip_slot(EQUIP_LITE); /* If it is not a torch don't throw it */ if (k_info[l_ptr->k_idx].sval != SV_LITE_TORCH) return (0); /* It the borg is wielding a torch keep 7 in reserve for light */ if (q_ptr && k_info[q_ptr->k_idx].sval == SV_LITE_TORCH && bp_ptr->able.fuel <= 7) return (0); /* Throw the torch */ return (d); } case TV_POTION: { /* Which potion is this? */ switch (k_info[l_ptr->k_idx].sval) { case SV_POTION_RUINATION: case SV_POTION_DETONATIONS: { /* Set damage and damage type */ *typ = GF_SHARDS; return (25 * (25 + 1) / 2); } case SV_POTION_DEATH: { /* Set damage and damage type */ *typ = GF_DEATH_RAY; return (25 * (25 + 1) / 2); } case SV_POTION_POISON: { /* Set damage and damage type */ *typ = GF_POIS; return (3 * (6 + 1) / 2); break; } case SV_POTION_RESTORE_MANA: { /* Only warriors should throw this */ if (borg_class == CLASS_WARRIOR) { /* Set damage and damage type */ *typ = GF_MANA; return (10 * (10 + 1) / 2); } } default: { /* Don't throw any other potion */ return (0); } } } case TV_FOOD: case TV_SCROLL: case TV_ROD: case TV_WAND: case TV_STAFF: case TV_AMULET: case TV_RING: case TV_LIFE_BOOK: case TV_SORCERY_BOOK: case TV_NATURE_BOOK: case TV_CHAOS_BOOK: case TV_DEATH_BOOK: case TV_TRUMP_BOOK: case TV_ARCANE_BOOK: { /* Don't throw these, they have better uses */ return (0); } default: { /* Anything else can go */ return (d); } } } /* * Simulate/Apply the optimal result of throwing an object * * First choose the "best" object to throw, then check targets. */ static int borg_attack_object(int *b_slot, int mult) { int n, b_n = 0; int b_x = 0, b_y = 0; int slot; int d, typ, r; int div, mul; if (borg_simulate) { /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Scan the pack */ for (slot = 0; slot < inven_num; slot++) { list_item *l_ptr = &inventory[slot]; d = borg_throw_damage(l_ptr, &typ); /* Ignore 0 damage */ if (d <= 0) continue; /* Extract a "distance multiplier" */ mul = 5 + 5 * mult; /* Enforce a minimum "weight" of one pound */ div = ((l_ptr->weight > 10) ? l_ptr->weight : 10); /* Hack -- Distance -- Reward strength, penalize weight */ r = (adj_str_blow[my_stat_ind[A_STR]] + 20) * mul / div; /* Max distance of 10 */ if (r > 10) r = 10; /* Choose optimal location */ n = borg_launch_bolt(d, typ, r); if (n <= b_n) continue; /* Track */ *b_slot = slot; b_n = n; b_x = g_x; b_y = g_y; } /* Set globals */ g_x = b_x; g_y = b_y; /* Simulation */ return (b_n); } /* Do it */ borg_note("# Throwing painful object '%s'", inventory[*b_slot].o_name); /* Set the target */ borg_target(g_x, g_y); /* Fire */ borg_keypress('v'); /* Use the object */ borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } static int borg_ring_damage_monster(int sval) { switch (sval) { case SV_RING_ICE: return (borg_launch_ball(BORG_BALL_RAD2, 100, GF_COLD, MAX_RANGE)); case SV_RING_ACID: return (borg_launch_ball(BORG_BALL_RAD2, 100, GF_ACID, MAX_RANGE)); case SV_RING_FLAMES: return (borg_launch_ball(BORG_BALL_RAD2, 100, GF_FIRE, MAX_RANGE)); default: return (0); } } /* * Simulate/Apply the optimal result of Activating a ring */ static int borg_attack_ring(int *b_slot) { int sval_l = -1, sval_r; int n = 0, b_n = 0; int b_x = c_x, b_y = c_y; if (borg_simulate) { /* Check the equipment */ list_item *l_ptr = look_up_equip_slot(EQUIP_LEFT); /* Make sure the ring is IDed */ if (l_ptr && borg_obj_known_p(l_ptr)) { /* Check charge */ if (!l_ptr->timeout) { /* Can we activate this ring */ if (borg_use_item_fail(l_ptr, FALSE)) { /* Which ring is this? */ sval_l = k_info[l_ptr->k_idx].sval; /* Get the damage */ b_n = borg_ring_damage_monster(sval_l); /* Make a note this is the left finger */ *b_slot = EQUIP_LEFT; /* Keep track of the target */ b_x = g_x; b_y = g_y; } } } /* Check the equipment */ l_ptr = look_up_equip_slot(EQUIP_RIGHT); /* Make sure the ring is IDed */ if (l_ptr && borg_obj_known_p(l_ptr)) { /* Check charge */ if (!l_ptr->timeout) { /* Can we activate this ring */ if (borg_use_item_fail(l_ptr, FALSE)) { /* Which ring is this? */ sval_r = k_info[l_ptr->k_idx].sval; /* Not the same as the ring just tried */ if (sval_r != sval_l) { /* Get the damage */ n = borg_ring_damage_monster(sval_r); } } } } /* Which finger has bigger damage? */ if (n > b_n) { /* So it is the right finger */ *b_slot = EQUIP_RIGHT; /* Switch over the damage and the target */ b_n = n; b_x = g_x; b_y = g_y; } /* Set the target global */ g_x = b_x; g_y = b_y; /* Return damage */ return (b_n); } /* Set the target */ borg_target(g_x, g_y); /* Do it */ borg_note("# Activating %s", equipment[*b_slot].o_name); /* Activate the ring*/ borg_keypress('A'); borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (0); } /* How much damage does a dragon armour do? */ static int borg_dragon_damage_monster(int sval) { int chance, gf_typ; switch (sval) { case SV_DRAGON_BLUE: return (borg_launch_ball(BORG_BALL_RAD2, 330, GF_ELEC, MAX_RANGE)); case SV_DRAGON_WHITE: return (borg_launch_ball(BORG_BALL_RAD2, 370, GF_COLD, MAX_RANGE)); case SV_DRAGON_BLACK: return (borg_launch_ball(BORG_BALL_RAD2, 430, GF_ACID, MAX_RANGE)); case SV_DRAGON_GREEN: return (borg_launch_ball(BORG_BALL_RAD2, 500, GF_POIS, MAX_RANGE)); case SV_DRAGON_RED: return (borg_launch_ball(BORG_BALL_RAD2, 670, GF_FIRE, MAX_RANGE)); case SV_DRAGON_MULTIHUED: { chance = randint0(5); gf_typ = (chance == 0) ? GF_ELEC : ((chance == 1) ? GF_COLD : ((chance == 2) ? GF_ACID : ((chance == 3) ? GF_POIS : GF_FIRE))); return (borg_launch_ball(BORG_BALL_RAD2, 840, gf_typ, MAX_RANGE)); } case SV_DRAGON_BRONZE: return (borg_launch_ball(BORG_BALL_RAD2, 400, GF_CONFUSION, MAX_RANGE)); case SV_DRAGON_GOLD: return (borg_launch_ball(BORG_BALL_RAD2, 430, GF_SOUND, MAX_RANGE)); case SV_DRAGON_CHAOS: { chance = randint0(2); gf_typ = (chance == 0) ? GF_CHAOS : GF_DISENCHANT; return (borg_launch_ball(BORG_BALL_RAD2, 740, gf_typ, MAX_RANGE)); } case SV_DRAGON_LAW: { chance = randint0(2); gf_typ = (chance == 0) ? GF_SOUND : GF_SHARDS; return (borg_launch_ball(BORG_BALL_RAD2, 750, gf_typ, MAX_RANGE)); } case SV_DRAGON_BALANCE: { chance = randint0(4); gf_typ = (chance == 0) ? GF_CHAOS : ((chance == 1) ? GF_SOUND : ((chance == 2) ? GF_SHARDS : GF_DISENCHANT)); return (borg_launch_ball(BORG_BALL_RAD2, 840, gf_typ, MAX_RANGE)); } case SV_DRAGON_SHINING: { chance = randint0(2); gf_typ = (chance == 0) ? GF_LITE : GF_DARK; return (borg_launch_ball(BORG_BALL_RAD2, 670, gf_typ, MAX_RANGE)); } case SV_DRAGON_POWER: return (borg_launch_ball(BORG_BALL_RAD3, 1000, GF_MISSILE, MAX_RANGE)); default: return (0); } } /* * Simulate/Apply the optimal result of Activating a Dragon armour */ static int borg_attack_dragon(void) { if (borg_simulate) { /* Check the equipment */ list_item *l_ptr = look_up_equip_slot(EQUIP_BODY); /* Skip incorrect armours */ if (!l_ptr || l_ptr->tval != TV_DRAG_ARMOR) return (0); /* Make Sure Mail is IDed */ if (!borg_obj_known_p(l_ptr)) return (0); /* Check charge */ if (l_ptr->timeout) return (0); /* Can we activate this dragon armour */ if (!borg_use_item_fail(l_ptr, FALSE)) return (0); /* Return the damage */ return (borg_dragon_damage_monster(k_info[l_ptr->k_idx].sval)); } /* Set the target */ borg_target(g_x, g_y); /* Do it */ borg_note("# Activating %s", equipment[EQUIP_BODY].o_name); /* Activate the dragon armour*/ borg_keypress('A'); borg_keypress(I2A(EQUIP_BODY)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (0); } static int borg_rod_damage_monster(int sval) { switch (sval) { case SV_ROD_ELEC_BOLT: return (borg_launch_bolt(22, GF_ELEC, MAX_RANGE)); case SV_ROD_COLD_BOLT: return (borg_launch_bolt(27, GF_COLD, MAX_RANGE)); case SV_ROD_ACID_BOLT: return (borg_launch_bolt(27, GF_ACID, MAX_RANGE)); case SV_ROD_FIRE_BOLT: return (borg_launch_bolt(45, GF_OLD_SLEEP, MAX_RANGE)); case SV_ROD_LITE: return (borg_launch_beam(27, GF_LITE_WEAK, MAX_RANGE)); case SV_ROD_ILLUMINATION: return (borg_launch_dispel(18, GF_LITE_WEAK, BORG_BALL_RAD2)); case SV_ROD_DRAIN_LIFE: return (borg_launch_bolt(150, GF_OLD_DRAIN, MAX_RANGE)); case SV_ROD_ELEC_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 75, GF_ELEC, MAX_RANGE)); case SV_ROD_COLD_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 100, GF_COLD, MAX_RANGE)); case SV_ROD_ACID_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 125, GF_ACID, MAX_RANGE)); case SV_ROD_FIRE_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 150, GF_FIRE, MAX_RANGE)); case SV_ROD_SLOW_MONSTER: return (borg_launch_bolt(10, GF_OLD_SLOW, MAX_RANGE)); case SV_ROD_SLEEP_MONSTER: return (borg_launch_bolt(10, GF_OLD_SLEEP, MAX_RANGE)); case SV_ROD_PESTICIDE: return (borg_launch_ball(BORG_BALL_RAD3, 8, GF_POIS, MAX_RANGE)); case SV_ROD_HAVOC: /* This has a random damage type, so just hope it is not resisted */ return (borg_launch_ball(BORG_BALL_RAD2, 200, GF_MISSILE, MAX_RANGE)); default: return (0); } } /* * Simulate/Apply the optimal result of using an attack rod */ static int borg_attack_rod(int *b_slot) { int n, b_n = -1; int k; int b_x = 0, b_y = 0; list_item *l_ptr; /* Simulation */ if (borg_simulate) { /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Paranoia */ if (randint0(100) < 5) return (0); /* Check the inventory for rods */ for (k = 0; k < inven_num; k++) { l_ptr = &inventory[k]; /* Skip non rods */ if (l_ptr->tval != TV_ROD) continue; /* Skip if the whole pile is recharging */ if (l_ptr->timeout == l_ptr->number) continue; /* Skip too hard rods */ if (!borg_use_item_fail(l_ptr, FALSE)) continue; /* Get the damage done by the rod */ n = borg_rod_damage_monster(k_info[l_ptr->k_idx].sval); /* Skip low results */ if (n <= b_n) continue; /* Track this rod */ b_n = n; *b_slot = k; /* Track the target */ b_x = g_x; b_y = g_y; } /* Set the globals */ g_x = b_x; g_y = b_y; return (b_n); } /* Set the target */ borg_target(g_x, g_y); /* Tell what is zapped */ borg_note("# Zapping %s", inventory[*b_slot].o_name); /* Zap the rod */ borg_keypress('z'); borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* Find out how much damage a wand does */ static int borg_wand_damage_monster(int sval) { switch (sval) { case SV_WAND_MAGIC_MISSILE: return (borg_launch_bolt(7, GF_MISSILE, MAX_RANGE)); case SV_WAND_COLD_BOLT: return (borg_launch_bolt(27, GF_COLD, MAX_RANGE)); case SV_WAND_ACID_BOLT: return (borg_launch_bolt(27, GF_ACID, MAX_RANGE)); case SV_WAND_FIRE_BOLT: return (borg_launch_bolt(45, GF_FIRE, MAX_RANGE)); case SV_WAND_SLOW_MONSTER: return (borg_launch_bolt(10, GF_OLD_SLOW, MAX_RANGE)); case SV_WAND_SLEEP_MONSTER: return (borg_launch_bolt(10, GF_OLD_SLEEP, MAX_RANGE)); case SV_WAND_CONFUSE_MONSTER: return (borg_launch_bolt(7, GF_OLD_CONF, MAX_RANGE)); case SV_WAND_FEAR_MONSTER: return (borg_launch_bolt(7, GF_TURN_ALL, MAX_RANGE)); case SV_WAND_ANNIHILATION: return (borg_launch_bolt(175, GF_OLD_DRAIN, MAX_RANGE)); case SV_WAND_DRAIN_LIFE: return (borg_launch_bolt(150, GF_OLD_DRAIN, MAX_RANGE)); case SV_WAND_LITE: return (borg_launch_beam(27, GF_LITE_WEAK, MAX_RANGE)); case SV_WAND_STINKING_CLOUD: return (borg_launch_ball(BORG_BALL_RAD2, 15, GF_POIS, MAX_RANGE)); case SV_WAND_ELEC_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 75, GF_ELEC, MAX_RANGE)); case SV_WAND_COLD_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 100, GF_COLD, MAX_RANGE)); case SV_WAND_ACID_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 125, GF_ACID, MAX_RANGE)); case SV_WAND_FIRE_BALL: return (borg_launch_ball(BORG_BALL_RAD2, 150, GF_FIRE, MAX_RANGE)); case SV_WAND_WONDER: { /* check the danger */ if (borg_launch_bolt(35, GF_MISSILE, MAX_RANGE) > 0 && borg_danger(c_x, c_y, 1, TRUE) >= (avoidance * 2)) { /* note the use of the wand in the emergency */ borg_note("# Emergency use of a Wand of Wonder."); /* make the wand appear deadly */ return (999); } else { return (0); } } case SV_WAND_DRAGON_COLD: return (borg_launch_ball(BORG_BALL_RAD3, 200, GF_COLD, MAX_RANGE)); case SV_WAND_DRAGON_FIRE: return (borg_launch_ball(BORG_BALL_RAD3, 250, GF_FIRE, MAX_RANGE)); case SV_WAND_ROCKETS: return (borg_launch_ball(BORG_BALL_RAD2, 250, GF_ROCKET, MAX_RANGE)); default: return (0); } } /* * Simulate/Apply the optimal result of aiming a wand * * Check out which wand is available and then call the apropriate routine for it * */ static int borg_attack_wand(int *b_slot) { int n, b_n = 0; int b_x = 0, b_y = 0; int i, k; int sval; list_item *l_ptr; /* Simulation */ if (borg_simulate) { /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Scan the pack to find out where the wands are */ for (k = 0; k < inven_num; k++) { l_ptr = &inventory[k]; /* Skip non-wands */ if (l_ptr->tval != TV_WAND) continue; /* Skip wands that have been considered already */ if (l_ptr->treat_as == TREAT_AS_GONE) { l_ptr->treat_as = TREAT_AS_NORM; continue; } /* Is this wand identified? */ if (borg_obj_known_p(l_ptr)) { /* Does it have charges? */ if (!l_ptr->pval) continue; } else { /* Is it inscribed as {empty} */ if (strstr(l_ptr->o_name, "{empty}")) continue; } /* Determine type */ sval = k_info[l_ptr->k_idx].sval; /* Search the rest of the wands for the current type */ i = borg_slot_from(TV_WAND, sval, k + 1); /* Skip this wand in the future loops */ if (i != -1) inventory[i].treat_as = TREAT_AS_GONE; /* Find out how much damage this wand can do */ n = borg_wand_damage_monster(sval); /* Is it better than before? */ if (n <= b_n) continue; *b_slot = k; b_n = n; b_x = g_x; b_y = g_y; } /* Set the targetting globals */ g_x = b_x; g_y = b_y; /* Return the value of the simulation */ return (b_n); } /* Set the target */ borg_target(g_x, g_y); /* Do it */ borg_note("# Aiming %s", inventory[*b_slot].o_name); /* Fire */ borg_keypress('a'); /* Use the wand */ borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of making a racial physical attack */ static int borg_vampire_damage_monster(int dam) { int p; int i, b_i = -1; int d, b_d = -1; int x, b_x = c_x; int y, b_y = c_y; map_block *mb_ptr; borg_kill *kill; monster_race *r_ptr; /* Too afraid to attack */ if (bp_ptr->status.afraid) return (0); /* Fill the belly */ if (!bp_ptr->status.full) dam = dam * 13 / 10; if (bp_ptr->status.hungry) dam = dam * 13 / 10; /* Examine possible destinations */ for (i = 0; i < borg_next_n; i++) { x = borg_next_x[i]; y = borg_next_y[i]; /* Acquire grid */ mb_ptr = map_loc(x, y); /* Obtain the monster */ kill = &borg_kills[mb_ptr->kill]; /* monster race */ r_ptr = &r_info[mb_ptr->monster]; /* Dont attack our buddies */ if (kill->m_flags & MONST_PET) continue; /* Base Dam */ d = dam; /* Drain works only on the living */ if (!monster_living(r_ptr)) continue; /* Hack -- avoid waking most "hard" sleeping monsters */ if ((kill->m_flags & MONST_ASLEEP) && (d <= kill->power)) { /* Calculate danger */ borg_full_damage = TRUE; p = borg_danger_aux(x, y, 1, mb_ptr->kill, TRUE); borg_full_damage = FALSE; if (p > avoidance / 2) continue; } /* Hack -- ignore sleeping town monsters */ if (!bp_ptr->depth && (kill->m_flags & MONST_ASLEEP)) continue; /* Calculate "danger" to player */ borg_full_damage = TRUE; p = borg_danger_aux(c_x, c_y, 2, mb_ptr->kill, TRUE); borg_full_damage = FALSE; /* Reduce "bonus" of partial kills */ if (d <= kill->power) p = p / 10; /* Add the danger to the damage */ d += p; /* Ignore lower damage */ if ((b_i >= 0) && (d < b_d)) continue; /* Save the damage info */ b_d = d; /* Keep the target spot */ b_x = x; b_y = y; } /* Nothing to attack */ if (b_d <= 0) return (0); /* Track the global */ g_x = b_x; g_y = b_y; /* Return the simulation */ return (b_d); } /* Simulate the damage done by the various racial abilities */ static int borg_racial_damage_monster(int race) { int rad, dam; switch (race) { case RACE_VAMPIRE: { /* Suck Blood */ dam = bp_ptr->lev + ((bp_ptr->lev / 2) * MAX(1, bp_ptr->lev / 10)); /* Dmg */ return (borg_vampire_damage_monster(dam)); } case RACE_CYCLOPS: { /* Throw Boulder */ dam = bp_ptr->lev * 3 / 2; return (borg_launch_bolt(dam, GF_MISSILE, MAX_RANGE)); } case RACE_DARK_ELF: { /* Magic Missile */ dam = (3 + ((bp_ptr->lev - 1)) / 4) * 5; return (borg_launch_bolt(dam, GF_MISSILE, MAX_RANGE)); } case RACE_DRACONIAN: { /* Draconian Breath */ dam = 2 * bp_ptr->lev; rad = 1 + bp_ptr->lev / 15; return (borg_launch_ball(rad, dam, GF_FIRE, MAX_RANGE)); } case RACE_IMP: { /* Fireball */ dam = bp_ptr->lev; rad = (bp_ptr->lev >= 30) ? BORG_BALL_RAD2 : BORG_BALL_RAD1; return (borg_launch_ball(rad, dam, GF_FIRE, MAX_RANGE)); } case RACE_KLACKON: { /* Acidball */ dam = bp_ptr->lev; rad = (bp_ptr->lev >= 25) ? BORG_BALL_RAD2 : BORG_BALL_RAD1; return (borg_launch_ball(rad, dam, GF_ACID, MAX_RANGE)); } case RACE_KOBOLD: { /* Poison bolt */ dam = bp_ptr->lev; return (borg_launch_bolt(dam, GF_POIS, MAX_RANGE)); } case RACE_MIND_FLAYER: { /* Mindblast bolt */ dam = bp_ptr->lev; return (borg_launch_bolt(dam, GF_PSI, MAX_RANGE)); } case RACE_SPRITE: { /* Sleep III */ dam = bp_ptr->lev; rad = MAX_SIGHT; return (borg_launch_dispel(dam, GF_OLD_SLEEP, rad)); } case RACE_YEEK: { /* Scare Mon */ dam = bp_ptr->lev; return (borg_launch_bolt(dam, GF_TURN_ALL, MAX_RANGE)); } } /* Paranoia */ return (0); } /* Simulate/Apply the optimal result of Using a racial power. */ static int borg_attack_racial(void) { if (borg_simulate) { /* Check for ability */ if (!borg_racial_check(borg_race, TRUE)) return (FALSE); /* What is the damage? */ return (borg_racial_damage_monster(borg_race)); } /* Note */ borg_note("# Racial Attack "); /* Set the target */ borg_target(g_x, g_y); /* Activate */ borg_keypress('U'); /* Select the power. All racial attack are in the first spot */ borg_keypress('a'); if (borg_race == RACE_VAMPIRE) { /* Bite to the grid next to the borg */ borg_keypress(I2D(borg_extract_dir(c_x, c_y, g_x, g_y))); } /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Success */ return (0); } /* How much damage can a mutation do */ static int borg_mutate_damage_monster(u32b mut_nr, int *slot) { int n, dam, rad = MAX_RANGE; /* What mutation have we here? */ switch (mut_nr) { /* Acid ball */ case MUT1_SPIT_ACID: { dam = bp_ptr->lev; rad = 1 + bp_ptr->lev / 30; return (borg_launch_ball(rad, dam, GF_ACID, MAX_RANGE)); } /* Fire breath */ case MUT1_BR_FIRE: { dam = bp_ptr->lev * 2; rad = 1 + bp_ptr->lev / 20; return (borg_launch_ball(rad, dam, GF_FIRE, MAX_RANGE)); } /* Psi bolt */ case MUT1_MIND_BLST: { dam = 2 * (3 + (bp_ptr->lev - 1) / 5); return (borg_launch_bolt(dam, GF_PSI, rad)); } /* Nuke'em */ case MUT1_RADIATION: { dam = 2 * bp_ptr->lev; rad = 3 + bp_ptr->lev / 20; return (borg_launch_ball(rad, dam, GF_NUKE, MAX_RANGE)); } /* Have a bite */ case MUT1_VAMPIRISM: { dam = 2 * bp_ptr->lev; return (borg_vampire_damage_monster(dam)); } /* Sound of Music */ case MUT1_SHRIEK: { dam = 2 * bp_ptr->lev; rad = 8; return (borg_launch_dispel(dam, GF_SOUND, rad)); } /* Light area */ case MUT1_ILLUMINE: { dam = bp_ptr->lev; rad = 1 + bp_ptr->lev / 10; return (borg_launch_dispel(dam, GF_LITE_WEAK, rad)); } /* hit and phase door in one move like a novice rogue */ case MUT1_PANIC_HIT: { /* Its damage is at least equal to a normal hit */ dam = borg_attack_thrust(); /* If there are a few monsters around then add bonus */ if (borg_temp_n < 5) dam = dam * 15 / 10; /* Return the damage */ return (dam); } /* Mass confuse, stun and scare */ case MUT1_DAZZLE: { dam = 20; n = borg_launch_dispel(dam, GF_OLD_CONF, rad); return (n + borg_launch_dispel(dam, GF_TURN_ALL, rad)); } /* Lite beam */ case MUT1_LASER_EYE: { dam = 2 * bp_ptr->lev; return (borg_launch_beam(dam, GF_LITE, rad)); } /* Touch to freeze */ case MUT1_COLD_TOUCH: { dam = 2 * bp_ptr->lev; rad = 1; return (borg_launch_bolt(dam, GF_COLD, rad)); } /* Throw something */ case MUT1_LAUNCHER: { /* This is not a real radius, it is a factor */ rad = 2 + bp_ptr->lev / 30; return (borg_attack_object(slot, rad)); } /* dud mutation */ default: return (0); } } /* Simulate/Apply the optimal result of Using a mutation. */ static int borg_attack_mutation(int *b_slot, int *b_spell) { int i, n, b_n = 0; int b_x = c_x, b_y = c_y; u32b mut_nr = 0; int slot, spell; if (borg_simulate) { /* Find out if the there isn't a racial in the way */ spell = borg_count_racial(borg_race) - 1; /* Loop through all the bits in bp_ptr->muta1 */ for (i = 1; i < 32; i++) { /* get the current mutation */ mut_nr = (mut_nr) ? mut_nr * 2 : 1; /* Does the borg have this mutation? */ if (!(bp_ptr->muta1 & mut_nr)) continue; /* Advance the letter index */ spell += 1; /* Check if it is castable right now */ if (!borg_mutation_check(mut_nr, TRUE)) continue; /* What is the damage? */ n = borg_mutate_damage_monster(mut_nr, &slot); /* Is it more than before? */ if (n <= b_n) continue; /* Track it */ b_n = n; *b_spell = spell; *b_slot = slot; b_x = g_x; b_y = g_y; } /* Set the globals */ g_x = b_x; g_y = b_y; /* return the damage indication */ return (b_n); } /* Note */ borg_note("# Mutation Attack "); borg_note("With letter = %c", I2A(*b_spell)); /* Set the target */ borg_target(g_x, g_y); /* Activate */ borg_keypress('U'); /* Select the mutation */ borg_keypress(I2A(*b_spell)); /* Is this mutation that hits a neighbour? */ if (bp_ptr->muta1 & MUT1_VAMPIRISM || bp_ptr->muta1 & MUT1_PANIC_HIT) { /* Bite the neighbour */ borg_keypress(I2D(borg_extract_dir(c_x, c_y, g_x, g_y))); } /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Success */ return (0); } /* Figure out how much damage mindcrafter spells do */ static int borg_mindcrafter_damage_monster(int spell) { int dam, rad; /* Which spell is this */ switch (spell) { case MIND_NEURAL_BL: { /* Set damage and radius */ dam = 3 + ((bp_ptr->lev - 1) / 4) * (3 + (bp_ptr->lev / 15)) / 2; rad = BORG_BALL_RAD0; /* Return the damage */ return (borg_launch_ball(rad, dam, GF_PSI, MAX_RANGE)); } case MIND_PULVERISE: { /* Set damage */ dam = 8 + (bp_ptr->lev - 5) / 4; /* Is the borg grown up? */ if (bp_ptr->lev < 20) { rad = BORG_BALL_RAD0; } else { rad = 1 + (bp_ptr->lev - 20) / 8; } /* Return the damage */ return (borg_launch_ball(rad, dam, GF_SOUND, MAX_RANGE)); } case MIND_MIND_WAVE: { /* This spell becomes the main staple after lvl 25 */ /* First mind_wave doesn't reach far */ if (bp_ptr->lev < 25) { /* Set radius */ rad = 2 + bp_ptr->lev / 10; dam = bp_ptr->lev * 3 / 2; } else { /* Set radius */ rad = MAX_SIGHT; dam = bp_ptr->lev * ((bp_ptr->lev - 5) / 10 + 1); } /* Return the damage */ return (borg_launch_dispel(dam, GF_PSI, rad)); } case MIND_PSYCHIC_DR: { /* Set damage and radius */ dam = 7 * bp_ptr->lev / 4; rad = BORG_BALL_RAD0; /* Return the damage */ return (borg_launch_ball(rad, dam, GF_PSI_DRAIN, MAX_RANGE)); } case MIND_TELE_WAVE: { /* set the radius */ rad = 3 + bp_ptr->lev / 10; /* Life begins at 40 */ if (bp_ptr->lev < 40) { /* Set damage */ dam = bp_ptr->lev * 3; } else { /* Set damage */ dam = bp_ptr->lev * 4; } /* Return the damage */ return (borg_launch_dispel(dam, GF_TELEKINESIS, rad)); } default: { /* Any other spell does no damage */ return (0); } } } /* Check the mindcrafter spells for damage */ static int borg_attack_mindcrafter(int *b_spell) { int spell; int n, b_n = 0; int b_x = 0, b_y = 0; int fail_rate = (borg_fighting_unique ? 40 : 25); if (borg_simulate) { /* Are you a mindcrafter? */ if (borg_class != CLASS_MINDCRAFTER) return (0); /* No firing while blind, confused, or hallucinating */ if ((bp_ptr->status.blind && !(FLAG(bp_ptr, TR_TELEPATHY))) || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Loop through the spells */ for (spell = 0; spell < MINDCRAFT_MAX; spell++) { borg_mind *as = &borg_minds[spell]; /* Paranoia */ if (randint0(100) < 5) continue; /* Require ability (right now) */ if (!borg_mindcr_okay_fail(spell, as->level, fail_rate)) continue; /* Choose optimal location */ n = borg_mindcrafter_damage_monster(spell); /* Penalize mana usage (Add 1 to stimulate neural blast) */ n = n + 1 - as->power; /* Compare with previous */ if (n <= b_n) continue; /* Track this spell */ b_n = n; *b_spell = spell; b_x = g_x; b_y = g_y; } /* Set the global coords */ g_x = b_x; g_y = b_y; /* Simulation */ return (b_n); } /* Set target for some spells */ if (*b_spell == MIND_NEURAL_BL || *b_spell == MIND_PULVERISE || *b_spell == MIND_PSYCHIC_DR) borg_target(g_x, g_y); /* Cast the spell */ (void)borg_mindcr(*b_spell, borg_minds[*b_spell].level); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* Check the mindcrafter spells for damage in emergency cases */ static int borg_attack_mindcrafter_reserve(bool faint, int *b_spell) { int spell; int n, b_n = 0; int b_x = 0, b_y = 0; int fail_rate = (borg_fighting_unique ? 40 : 25); int monster; /* Fake our Mana */ int sv_mana = bp_ptr->csp; bool spell_success; borg_kill *kill; if (borg_simulate) { /* Are you a mindcrafter? */ if (borg_class != CLASS_MINDCRAFTER) return (0); /* No firing while blind, confused, or hallucinating */ if ((bp_ptr->status.blind && !(FLAG(bp_ptr, TR_TELEPATHY))) || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Fainting is only for little guys */ if (faint && bp_ptr->lev >= 20) return (0); /* Only big guys have reserve_mana */ if (!faint && !borg_reserve_mana()) return (0); /* There can only be one monster closeby */ if (borg_bolt_n != 1) return (0); /* Must be dangerous */ if (faint && borg_danger(c_x, c_y, 1, TRUE) < avoidance * 2) return (0); /* Loop through the spells */ for (spell = 0; spell < MINDCRAFT_MAX; spell++) { borg_mind *as = &borg_minds[spell]; /* Paranoia */ if (randint0(100) < 5) continue; /* No point of trying unknown spells */ if (bp_ptr->lev < as->level) continue; /* Require inability (right now) */ if (borg_mindcr_okay_fail(spell, as->level, fail_rate)) continue; /* If there is enough mana then keep trying */ if (!faint && bp_ptr->csp < as->power) continue; /* Does the lack of mana bust the failrate? */ if (faint && borg_mindcr_fail_rate(spell, bp_ptr->lev) > fail_rate) continue; /* Pretend there is enough mana */ bp_ptr->csp = bp_ptr->msp; /* Can you cast the spell now? */ spell_success = borg_mindcr_okay_fail(spell, as->level, fail_rate); /* Restore original mana */ bp_ptr->csp = sv_mana; /* The fail rate was too bad */ if (!spell_success) continue; /* Choose optimal location */ n = borg_mindcrafter_damage_monster(spell); /* Find the index to the monster */ monster = map_loc(borg_bolt_x[0], borg_bolt_y[0])->kill; /* Find the actual monster */ kill = &borg_kills[monster]; /* If the monster has more HP then a good hit don't try */ if (kill->power > n * 15 / 10) continue; /* Compare with previous */ if (n <= b_n) continue; /* Track this spell */ b_n = n; *b_spell = spell; b_x = g_x; b_y = g_y; } /* Set the global coords */ g_x = b_x; g_y = b_y; /* Simulation */ return (b_n); } /* make a note */ borg_note("Emergency mindcr use: %s", (faint) ? "faint" : "reserve"); /* Set target for some spells */ if (*b_spell == MIND_NEURAL_BL || *b_spell == MIND_PULVERISE || *b_spell == MIND_PSYCHIC_DR) borg_target(g_x, g_y); /* Pretend the borg has enough mana for this */ bp_ptr->csp = bp_ptr->msp; /* Cast the spell */ (void)borg_mindcr(*b_spell, borg_minds[*b_spell].level); /* Close your eyes */ if (faint) { /* confirm the spell use */ borg_press_faint_accept(); } /* Get the right amount of mana */ bp_ptr->csp = MAX(0, sv_mana - borg_minds[*b_spell].power); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* This function returns the damage done by life spells */ static int borg_life_damage_monster(int book, int spell) { int rad, dam, typ; switch (book) { /* Book of Common Prayer */ case 0: { switch (spell) { /* Spell -- Call Light */ case 4: { dam = bp_ptr->lev / 2; rad = bp_ptr->lev / 10 + 1; typ = GF_LITE_WEAK; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } default: return (0); } } /* High Mass */ case 1: { switch (spell) { /* Spell -- Holy Orb */ case 4: { /* Set basic damage */ dam = 11 + bp_ptr->lev + bp_ptr->lev / 4; /* Is the borg a natural at this spell? */ if (borg_class == CLASS_PRIEST || borg_class == CLASS_HIGH_MAGE) dam += bp_ptr->lev / 4; /* High levels get a higher radius */ rad = (bp_ptr->lev < 30) ? BORG_BALL_RAD2 : BORG_BALL_RAD3; typ = GF_HOLY_FIRE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } default: return (0); } } /* Book of the Unicorn */ case 2: { switch (spell) { /* Spell -- Exorcism */ case 0: { dam = bp_ptr->lev; rad = MAX_SIGHT; typ = GF_DISP_UNDEAD_DEMON; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Spell -- Disp Undead & Demon */ case 2: { dam = bp_ptr->lev * 3; rad = MAX_SIGHT; typ = GF_DISP_UNDEAD_DEMON; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Spell -- Disp Evil */ case 4: { dam = bp_ptr->lev * 4; rad = MAX_SIGHT; typ = GF_DISP_EVIL; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Holy Word */ case 6: { /* * Holy Word is the same as Disp Evil plus heal * So only cast this when the borg has lost HP */ if (bp_ptr->mhp - bp_ptr->chp >= 300) { /* Increased damage to make him cast it */ dam = bp_ptr->lev * 10; rad = MAX_SIGHT; typ = GF_DISP_EVIL; /* Choose optimal location-- */ return (borg_launch_dispel(dam, typ, rad)); } else { /* Don't bother if it doesn't heal (much) */ return (0); } } default: return (0); } } /* Blessings of the Grail */ case 3: { switch (spell) { /* Spell -- Divine Intervention */ case 6: { int n; dam = 777; rad = BORG_BALL_RAD1; typ = GF_HOLY_FIRE; /* if hurting, add bonus */ if (bp_ptr->mhp - bp_ptr->chp >= 200) dam = (dam * 12) / 10; /* Is the borg hasted? */ if (borg_speed) { /* bonus for refreshing the speedy */ dam = (dam * 11) / 10; } else { /* if no speedy, add bonus */ dam = (dam * 13) / 10; } /* How much damage is that? */ n = borg_launch_dispel(dam, typ, rad); /* If the borg damages a neighbour */ if (n > 0) { /* There is a neighbour. Now add in the other damage */ dam = bp_ptr->lev * 4; rad = MAX_SIGHT; typ = GF_DISP_ALL; /* How much damage is that in total? */ return (n + borg_launch_dispel(dam, typ, rad)); } /* There is no neighbour */ else { /* why bother with this expensive spell */ return (0); } } default: return (0); } } default: { borg_oops("Trying to cast from life book = %d", book); return (0); } } } /* This function returns the damage done by sorcery spells */ static int borg_sorcery_damage_monster(int book, int spell) { int rad, dam, typ; switch (book) { /* Beginner's Handbook */ case 0: { switch (spell) { /* Spell -- Light area */ case 3: { dam = bp_ptr->lev / 2; rad = bp_ptr->lev / 10 + 1; typ = GF_LITE_WEAK; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } case 4: { /* Spell -- Confuse Monster */ dam = 10; typ = GF_OLD_CONF; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } case 6: { /* Spell -- Sleep I */ dam = 10; typ = GF_OLD_SLEEP; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } default: return (0); } } /* Master Sorcerer's Handbook */ case 1: { switch (spell) { /* Slow Monster */ case 2: { dam = 10; typ = GF_OLD_SLOW; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Spell -- Mass Sleep */ case 3: { rad = MAX_SIGHT; dam = 10; typ = GF_OLD_SLEEP; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } default: return (0); } } /* No attack spells in Pattern Sorcery */ case 2: return (0); /* Grimoire of Power */ case 3: { switch(spell) { /* Spell -- Stasis */ case 0: { dam = 10; typ = GF_STASIS; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } default: return (0); } } default: { borg_oops("Is book %d really a sorcery book?", book); return (0); } } } /* This function returns the damage done by nature spells */ static int borg_nature_damage_monster(int book, int spell) { int rad, dam, typ; switch (book) { /* Call of the Wild */ case 0: { switch (spell) { /* Spell -- Day Light */ case 4: { dam = bp_ptr->lev / 2; rad = bp_ptr->lev / 10 + 1; typ = GF_LITE_WEAK; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } default: return (0); } } /* Nature Mastery */ case 1: { switch (spell) { /* Spell -- Stone to Mud */ case 0: { dam = 35; typ = GF_KILL_WALL; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Spell -- lightning bolt */ case 1: { dam = (13 + (bp_ptr->lev - 5) / 4) * 9 / 2; typ = GF_ELEC; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Spell -- frost bolt */ case 3: { dam = (5 + (bp_ptr->lev - 5) / 4) * 9 / 2; typ = GF_COLD; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Spell -- Sunlight */ case 4: { dam = 27; typ = GF_LITE_WEAK; /* Choose optimal location-- */ return (borg_launch_beam(dam, typ, MAX_RANGE)); } /* Spell -- Entangle */ case 5: { rad = MAX_SIGHT; dam = 10; typ = GF_OLD_SLOW; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } default: return (0); } } /* Nature's Gifts has no damage spells */ case 2: return (0); /* Nature's Wrath */ case 3: { switch (spell) { /* Whirlwind */ case 1: { return (borg_attack_whirlwind()); } /* Blizzard */ case 2: { dam = 70 + bp_ptr->lev; rad = bp_ptr->lev / 12 + 1; typ = GF_COLD; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Elec Storm */ case 3: { dam = 90 + bp_ptr->lev; rad = bp_ptr->lev / 12 + 1; typ = GF_ELEC; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Whirlpool */ case 4: { dam = 100 + bp_ptr->lev; rad = bp_ptr->lev / 12 + 1; typ = GF_WATER; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Call Sunlight */ case 5: { /* Does light hurt? */ if (FLAG(bp_ptr, TR_HURT_LITE) && !FLAG(bp_ptr, TR_RES_LITE) && !FLAG(bp_ptr, TR_IM_LITE)) { /* Don't cast this */ return (0); } else { dam = 150; rad = BORG_BALL_RAD8; typ = GF_LITE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } } /* Natures Wrath */ case 7: { int n; /* Dispell all monsters */ dam = 4 * bp_ptr->lev; rad = MAX_SIGHT; typ = GF_DISP_ALL; /* Calculate dispell damage */ n = borg_launch_dispel(dam, typ, rad); /* Disintegrate ball centered on self */ dam = bp_ptr->lev + 100; rad = bp_ptr->lev / 12 + 1; typ = GF_DISINTEGRATE; /* Return dispell + ball damage */ return (n + borg_launch_dispel(dam, typ, rad)); } default: return (0); } } default: { borg_oops("Is book %d really a Nature book?", book); return (0); } } } /* This function returns the damage done by chaos spells */ static int borg_chaos_damage_monster(int book, int spell) { int rad, dam, typ; switch (book) { /* Sign of Chaos */ case 0: { switch (spell) { /* Magic Missile */ case 0: { dam = (3 + ((bp_ptr->lev - 1) / 5)) * 5 / 2; typ = GF_MISSILE; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Flash of Light */ case 2: { dam = bp_ptr->lev / 2 + 1; rad = bp_ptr->lev / 10 + 1; typ = GF_LITE_WEAK; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Mana Burst */ case 4: { /* Set basic damage */ dam = 9 + bp_ptr->lev + bp_ptr->lev / 4; /* Is the borg a natural at this spell? */ if (borg_class == CLASS_PRIEST || borg_class == CLASS_HIGH_MAGE) dam += bp_ptr->lev / 4; /* Set radius and type */ rad = ((bp_ptr->lev < 30) ? BORG_BALL_RAD2 : BORG_BALL_RAD3); typ = GF_MISSILE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Fire Bolt */ case 5: { dam = (8 + ((bp_ptr->lev - 5) / 4)) * 9 / 2; typ = GF_FIRE; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Fist of Force */ case 6: { dam = (8 + ((bp_ptr->lev - 5) / 4)) * 9 / 2; typ = GF_DISINTEGRATE; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } default: return (0); } } /* Chaos Mastery */ case 1: { switch (spell) { /* Chaos Bolt */ case 1: { dam = (10 + ((bp_ptr->lev - 5) / 4)) * 9 / 2; typ = GF_CHAOS; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Sonic Boom */ case 2: { dam = bp_ptr->lev + 45; rad = bp_ptr->lev / 10 + 2; typ = GF_SOUND; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Doom Bolt */ case 3: { dam = (11 + (bp_ptr->lev - 5) / 4) * 9 / 2; typ = GF_MANA; /* Choose optimal location-- */ return (borg_launch_beam(dam, typ, MAX_RANGE)); } /* Fire ball */ case 4: { rad = BORG_BALL_RAD2; dam = bp_ptr->lev + 55; typ = GF_FIRE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Invoke Logrus */ case 7: { rad = bp_ptr->lev / 5; dam = bp_ptr->lev + 66; typ = GF_CHAOS; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } default: return (0); } } /* Chaos Channels */ case 2: { switch (spell) { /* Polymorph */ case 0: { dam = 10; typ = GF_OLD_POLY; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Chain Lightning */ case 1: { rad = BORG_BALL_RAD8; dam = (5 + (bp_ptr->lev / 10)) * 9 / 2; typ = GF_ELEC; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Disintegration */ case 3: { rad = (bp_ptr->lev < 40) ? BORG_BALL_RAD3 : BORG_BALL_RAD4; dam = bp_ptr->lev + 80; typ = GF_DISINTEGRATE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } default: return (0); } } /* Armageddon Tome */ case 3: { switch (spell) { /* Gravity */ case 0: { dam = (9 + ((bp_ptr->lev - 5) / 4)) * 9 / 2; typ = GF_GRAVITY; /* Choose optimal location-- */ return (borg_launch_beam(dam, typ, MAX_RANGE)); } /* Meteor Swarm */ case 1: { rad = BORG_BALL_RAD3; dam = bp_ptr->lev + 65; typ = GF_METEOR; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Flamestrike */ case 2: { rad = BORG_BALL_RAD8; dam = 150 + bp_ptr->lev * 2; typ = GF_FIRE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Rocket */ case 4: { rad = BORG_BALL_RAD2; dam = 120 + bp_ptr->lev; typ = GF_SHARDS; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Mana Storm */ case 5: { rad = BORG_BALL_RAD4; dam = 300 + bp_ptr->lev * 2; typ = GF_MANA; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Breath Logrus */ case 6: { rad = BORG_BALL_RAD2; dam = bp_ptr->chp; typ = GF_CHAOS; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Call Void */ case 7: { int y = 0, x = 0; int i; map_block *mb_ptr; dam = 0; /* Scan neighboring grids */ for (i = 0; i < borg_next_n; i++) { /* Fetch the coords */ y = borg_next_y[i]; x = borg_next_x[i]; mb_ptr = map_loc(x, y); /* is there a wall next to me */ if (mb_ptr->feat >= FEAT_MAGMA && mb_ptr->feat <= FEAT_PERM_SOLID) { /* Don't cast it when the borg is next to a wall */ return (0); } else { /* Set the radius */ rad = BORG_BALL_RAD2; dam = 175; /* Calculate "average" damage */ dam += borg_launch_ball (rad, dam, GF_SHARDS, MAX_RANGE); dam += borg_launch_ball (rad, dam, GF_MANA, MAX_RANGE); dam += borg_launch_ball (BORG_BALL_RAD4, dam, GF_NUKE, MAX_RANGE); } } /* Return the damage for consideration */ return (dam); } default: return (0); } } default: { borg_oops("Is book %d really a sorcery book?", book); return (0); } } } /* This function returns the damage done by death spells */ static int borg_death_damage_monster(int book, int spell) { int rad, dam, typ; switch (book) { /* Black Prayers */ case 0: { switch (spell) { /* Malediction */ case 1: { rad = BORG_BALL_RAD1; dam = (3 + ((bp_ptr->lev - 1) / 5)) / 2; typ = GF_HELL_FIRE; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Poison Ball */ case 4: { rad = BORG_BALL_RAD2; dam = 10 + bp_ptr->lev / 2; typ = GF_POIS; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Black Sleep */ case 5: { dam = 10; typ = GF_OLD_SLEEP; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Horrify */ case 6: { dam = 10; typ = GF_TURN_ALL; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } default: return (0); } } /* Black Mass */ case 1: { switch (spell) { /* Entropy */ case 0: { rad = (bp_ptr->lev < 30) ? BORG_BALL_RAD2 : BORG_BALL_RAD3; /* Set basic damage */ dam = 10 + bp_ptr->lev + bp_ptr->lev / 4; /* Is the borg a natural at this spell? */ if (borg_class == CLASS_PRIEST || borg_class == CLASS_HIGH_MAGE) dam += bp_ptr->lev / 4; typ = GF_OLD_DRAIN; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } /* Nether Bolt */ case 1: { dam = (6 + ((bp_ptr->lev - 5)) * 9 / 2); typ = GF_HELL_FIRE; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Terror */ case 2: { dam = bp_ptr->lev + 30; typ = GF_TURN_ALL; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Vamp Drain */ case 4: { dam = (bp_ptr->lev + (bp_ptr->lev / 2 * ((9 + bp_ptr->lev) / 10))); typ = GF_OLD_DRAIN; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Dispel Good */ case 5: { rad = MAX_SIGHT; dam = bp_ptr->lev * 4; typ = GF_DISP_GOOD; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } default: return (0); } } /* Black Channels */ case 2: { switch (spell) { /* Dark Bolt */ case 2: { dam = (4 + ((bp_ptr->lev - 5) / 4) * 9 / 2); typ = GF_DARK; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Vampirism True */ case 4: { dam = 300; typ = GF_OLD_DRAIN; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* DarknessStorm */ case 6: { dam = 120; rad = BORG_BALL_RAD4; typ = GF_DARK; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } default: return (0); } } /* Necronomicon */ case 3: { switch (spell) { /* Death Ray */ case 0: { dam = bp_ptr->lev * 50; typ = GF_DEATH_RAY; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Word of Death */ case 3: { rad = MAX_SIGHT; dam = 3 * bp_ptr->lev; typ = GF_OLD_DRAIN; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } /* Evocation */ case 4: { dam = bp_ptr->lev * 4; typ = GF_OLD_DRAIN; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* HellFire */ case 5: { dam = 666; typ = GF_OLD_DRAIN; if (bp_ptr->chp < 200) return (0) ; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } default: return (0); } } default: { borg_oops("Is book %d really a death book?", book); return (0); } } } /* This function returns the damage done by trump spells */ static int borg_trump_damage_monster(int book, int spell) { int rad, dam; switch (book) { /* Conjuring & Tricks */ case 0: { switch (spell) { /* Spell -- Mind Blast */ case 1: { rad = BORG_BALL_RAD0; dam = 6 + 2 * (bp_ptr->lev - 1) / 5; /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, GF_PSI, MAX_RANGE)); } default: return (0); } } /* No attack spells in Deck of Many Things */ case 1: return (0); /* Trumps of Doom */ case 2: { switch (spell) { /* Spell -- Death dealing */ case 6: { dam = 3 * bp_ptr->lev; /* Choose optimal location-- */ return (borg_launch_dispel(dam, GF_DISP_LIVING, MAX_RANGE)); } default: return (0); } } /* Five Aces */ case 3: return (0); default: { borg_oops("Trying to cast from trump book %d", book); return (0); } } } /* This function returns the damage done by arcane spells */ static int borg_arcane_damage_monster(int book, int spell) { int rad, dam, typ; switch (book) { /* Cantrips for Beginners */ case 0: { switch (spell) { /* Spell -- Zap */ case 0: { dam = (3 + (bp_ptr->lev - 1) / 5) / 2; typ = GF_ELEC; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Spell -- Light Area */ case 5: { dam = bp_ptr->lev / 2; rad = bp_ptr->lev / 10 + 1; typ = GF_LITE_WEAK; /* How much damage is that? */ return (borg_launch_dispel(dam, typ, rad)); } default: return (0); } } /* No damage spells in Minor Arcana */ case 1: return (0); /* Major Arcana */ case 2: { switch(spell) { /* Spell -- Stone to Mud */ case 4: { dam = 35; typ = GF_KILL_WALL; /* Choose optimal location-- */ return (borg_launch_bolt(dam, typ, MAX_RANGE)); } /* Spell -- Light Beam */ case 5: { dam = 27; typ = GF_LITE_WEAK; /* Choose optimal location-- */ return (borg_launch_beam(dam, typ, MAX_RANGE)); } default: return (0); } } /* Manual of Mastery */ case 3: { switch(spell) { /* Spell -- Elem Ball */ case 4: { dam = 75 + bp_ptr->lev; rad = BORG_BALL_RAD2; /* Guess which type it will be */ switch (randint1(4)) { case 1: typ = GF_FIRE; break; case 2: typ = GF_ELEC; break; case 3: typ = GF_COLD; break; default: typ = GF_ACID; break; } /* Choose optimal location-- */ return (borg_launch_ball(rad, dam, typ, MAX_RANGE)); } default: return (0); } } default: { borg_oops("Trying to cast from arcane book %d", book); return (0); } } } /* Figure out the damage for the spells */ static int borg_spell_damage_monster(int realm, int book, int spell) { switch (realm) { case REALM_LIFE: { return (borg_life_damage_monster(book, spell)); } case REALM_SORCERY: { return (borg_sorcery_damage_monster(book, spell)); } case REALM_NATURE: { return (borg_nature_damage_monster(book, spell)); } case REALM_CHAOS: { return (borg_chaos_damage_monster(book, spell)); } case REALM_DEATH: { return (borg_death_damage_monster(book, spell)); } case REALM_TRUMP: { return (borg_trump_damage_monster(book, spell)); } case REALM_ARCANE: { return (borg_arcane_damage_monster(book, spell)); } default: { borg_oops("Trying to cast with an unknown realm."); return (0); } } } /* Check the spells for damage */ static int borg_attack_spell(int *b_slot, int *b_spell) { int realm, book, spell; int k, n, b_n = 0; int b_x = 0, b_y = 0; int fail_rate = (borg_fighting_unique ? 40 : 25); list_item *l_ptr; if (borg_simulate) { /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Spells are not for warriors or mindcrafters */ if (borg_class == CLASS_WARRIOR || borg_class == CLASS_MINDCRAFTER) return (0); /* Loop through the inventory */ for (k = 0; k < inven_num; k++) { l_ptr = &inventory[k]; /* Stop after the books have been seen */ if (l_ptr->tval < TV_BOOKS_MIN) break; /* Realize which realm this is */ realm = l_ptr->tval - TV_BOOKS_MIN + 1; /* Is this a realm that the borg knows? */ if (!borg_has_realm(realm)) continue; /* Realize which book this is */ book = k_info[l_ptr->k_idx].sval; /* Loop through the spells */ for (spell = 0; spell < 8; spell++) { /* Paranoia */ if (randint0(100) < 5) continue; /* Require ability (right now) */ if (!borg_spell_okay_fail(realm, book, spell, fail_rate)) continue; /* Choose optimal location */ n = borg_spell_damage_monster(realm, book, spell); /* Penalize mana usage */ n = n - borg_spell_mana(realm, book, spell); /* Compare with previous */ if (n <= b_n) continue; /* Track this spell */ b_n = n; *b_slot = k; *b_spell = spell; b_x = g_x; b_y = g_y; } } /* Set the global coords */ g_x = b_x; g_y = b_y; /* Simulation */ return (b_n); } /* Get the book */ l_ptr = &inventory[*b_slot]; /* Find out the realm and the book */ realm = l_ptr->tval - TV_BOOKS_MIN + 1; book = k_info[l_ptr->k_idx].sval; /* Set the target (Okay if it is a dud target) */ borg_target(g_x, g_y); /* Cast the spell */ (void)borg_spell(realm, book, *b_spell); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* Try to use the reserve mana for attacking anyway if there is one monster */ static int borg_attack_spell_reserve(bool faint, int *b_slot, int *b_spell) { int realm, book, spell = 0; int k, n, b_n = 0; int b_x = 0, b_y = 0; int fail_rate = (borg_fighting_unique ? 40 : 25); int power, monster; /* Fake our Mana */ int sv_mana = bp_ptr->csp; bool spell_success; borg_kill *kill; list_item *l_ptr; if (borg_simulate) { /* Spells are not for warriors or mindcrafters */ if (borg_class == CLASS_WARRIOR || borg_class == CLASS_MINDCRAFTER) return (0); /* Fainting is only for little guys */ if (faint && bp_ptr->lev >= 20) return (0); /* Only big guys have reserve_mana */ if (!faint && !borg_reserve_mana()) return (0); /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* There can only be one monster closeby */ if (borg_bolt_n != 1) return (0); /* Must be dangerous */ if (faint && borg_danger(c_x, c_y, 1, TRUE) < avoidance * 2) return (0); /* Loop through the inventory */ for (k = 0; k < inven_num; k++) { l_ptr = &inventory[k]; /* Stop after the books have been seen */ if (l_ptr->tval < TV_BOOKS_MIN) break; /* Realize which realm this is */ realm = l_ptr->tval - TV_BOOKS_MIN + 1; /* Is this a realm that the borg knows? */ if (!borg_has_realm(realm)) continue; /* Realize which book this is */ book = k_info[l_ptr->k_idx].sval; /* Loop through the spells */ for (spell = 0; spell < 8; spell++) { borg_magic *as = &borg_magics[realm][book][spell]; /* Paranoia */ if (randint0(100) < 5) continue; /* There is no point trying too high level spells */ if (as->level > bp_ptr->lev) continue; /* Require inability (right now) */ if (borg_spell_okay_fail(realm, book, spell, fail_rate)) continue; /* Collect the mana for this spell */ power = borg_spell_mana(realm, book, spell); /* If there is enough mana then keep trying */ if (!faint && bp_ptr->csp < power) continue; /* Does the lack of mana bust the failrate? */ if (faint && borg_spell_fail_rate(realm, book, spell) > fail_rate) continue; /* Pretend there is enough mana */ bp_ptr->csp = bp_ptr->msp; /* Can you cast the spell now? */ spell_success = borg_spell_okay_fail(realm, book, spell, fail_rate); /* Restore original mana */ bp_ptr->csp = sv_mana; /* The fail rate was too bad */ if (!spell_success) continue; /* Choose optimal location */ n = borg_spell_damage_monster(realm, book, spell); /* Find the index to the monster */ monster = map_loc(borg_bolt_x[0], borg_bolt_y[0])->kill; /* Find the actual monster */ kill = &borg_kills[monster]; /* If the monster won't die of this don't bother trying */ if (kill->power > n) continue; /* Compare with previous */ if (n <= b_n) continue; /* Track this spell */ b_n = n; *b_slot = k; *b_spell = spell; b_x = g_x; b_y = g_y; } } /* Set the global coords */ g_x = b_x; g_y = b_y; /* Simulation */ return (b_n); } /* Make a note */ borg_note("Emergency spell use: %s", (faint) ? "faint" : "reserve"); /* Get the book */ l_ptr = &inventory[*b_slot]; /* Find out the realm and the book */ realm = l_ptr->tval - TV_BOOKS_MIN + 1; book = k_info[l_ptr->k_idx].sval; /* Set the target (Okay if it is a dud target) */ borg_target(g_x, g_y); /* Pretend the borg has enough mana for this */ bp_ptr->csp = bp_ptr->msp; /* Cast the spell */ (void)borg_spell(realm, book, *b_spell); /* Close your eyes */ if (faint) { /* confirm the spell use */ borg_press_faint_accept(); } /* Get the right amount of mana */ bp_ptr->csp = MAX(0, sv_mana - borg_magics[realm][book][spell].power); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Value */ return (b_n); } /* Find out the damage done by certain staffs */ static int borg_staff_damage_monster(int sval) { int charge_penalty = 20; int rad = MAX_SIGHT; int dam = 60; int typ; switch (sval) { case SV_STAFF_STARLITE: { /* * Actually this staff picks 5d3 random targets that are in LOS * and send a light beam at them. * The borg assumes that two of those beams hit the best target. * This can easily happen in a hallway */ return (2 * borg_launch_beam(27, GF_LITE_WEAK, MAX_RANGE)); } case SV_STAFF_SLEEP_MONSTERS: { /* Set the type */ typ = GF_OLD_SLEEP; break; } case SV_STAFF_SLOW_MONSTERS: { /* Set the type */ typ = GF_OLD_SLOW; break; } case SV_STAFF_DISPEL_EVIL: { /* Set the type */ typ = GF_DISP_EVIL; break; } case SV_STAFF_POWER: { /* Set the damage, type and penalty for using a charge */ dam = 300; typ = GF_DISP_ALL; charge_penalty = 50; break; } case SV_STAFF_HOLINESS: { /* Set the damage, type and penalty for using a charge */ dam = 300; typ = GF_DISP_EVIL; charge_penalty = 50; /* If you are low on HP take 200 bonus for healing */ if (bp_ptr->chp < bp_ptr->mhp / 2) dam += 200; break; } default: { /* Any other staff doesn't do damage */ return (0); } } /* Return the damage */ return (borg_launch_dispel(dam, typ, rad) - charge_penalty); } /* * Simulate/Apply the optimal result of using a "dispel" staff */ static int borg_attack_staff(int *b_slot) { int i, k; int n, b_n = 0; int sval; if (borg_simulate) { /* No firing while blind, confused, or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image) return (0); /* Paranoia */ if (randint0(100) < 5) return (0); /* Go through the inventory */ for (k = 0; k < inven_num; k++) { list_item *l_ptr = &inventory[k]; /* look for staffs */ if (l_ptr->tval != TV_STAFF) continue; /* Skip staffs that have been considered already */ if (l_ptr->treat_as == TREAT_AS_GONE) { /* Back to normal */ l_ptr->treat_as = TREAT_AS_NORM; continue; } /* Is this staff identified? */ if (borg_obj_known_p(l_ptr)) { /* Does it have charges? */ if (!l_ptr->pval) continue; } else { /* Is it inscribed as {empty} */ if (strstr(l_ptr->o_name, "{empty}")) continue; } /* Determine type */ sval = k_info[l_ptr->k_idx].sval; /* Search the rest of the staffs for the current type */ i = borg_slot_from(TV_STAFF, sval, k + 1); /* Loop through the rest of the inventory */ while (i != -1) { /* Skip this staff in the future loops */ inventory[i].treat_as = TREAT_AS_GONE; /* Search the rest of the staffs for the current type */ i = borg_slot_from(TV_STAFF, sval, i + 1); } /* Find out the damgage it can do */ n = borg_staff_damage_monster(sval); /* Is it better than before? */ if (n <= b_n) continue; /* Track it */ b_n = n; *b_slot = k; } /* Simulation */ return (b_n); } /* Make a note */ borg_note("Using a %s", inventory[*b_slot].o_name); /* Use the staff */ borg_keypress('u'); borg_keypress(I2A(*b_slot)); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Finished */ return (0); } /* * Simulate/Apply the optimal result of using the given "type" of attack */ static int borg_attack_aux(int what, int *slot, int *spell) { /* Analyze */ switch (what) { case BF_ROD: { /* Any damage inducing rod */ return (borg_attack_rod(slot)); } case BF_DRAGON_ARMOUR: { /* Any damage inducing dragon armour */ return (borg_attack_dragon()); } case BF_RING: { /* Any damage inducing ring */ return (borg_attack_ring(slot)); } case BF_ARTIFACT: { /* Any damage inducing artifact */ return (borg_attack_artifact(slot)); } case BF_LAUNCH: { /* Fire something with your launcher */ return (borg_attack_launch(slot)); } case BF_OBJECT: { /* Object attack */ return (borg_attack_object(slot, 1)); } case BF_SCROLL: { /* Read some scroll that is nasty */ return (borg_attack_scroll(slot)); } case BF_THRUST: { /* Physical attack */ return (borg_attack_thrust()); } case BF_SPELLCASTER: { /* Check the spells for damage */ return (borg_attack_spell(slot, spell)); } case BF_MINDCRAFTER: { /* Check the Mindcrafter spells for damage */ return (borg_attack_mindcrafter(spell)); } case BF_STAFF: { /* Any damage inducing staff */ return (borg_attack_staff(slot)); } case BF_WAND: { /* Any damage inducing wand */ return (borg_attack_wand(slot)); } case BF_RACIAL: { /* Any damage inducing racial powers */ return (borg_attack_racial()); } case BF_MUTATE: { /* Any damage inducing mutation */ return (borg_attack_mutation(slot, spell)); } case BF_SPELL_RESERVE: { /* Check the spells again if in trouble */ return (borg_attack_spell_reserve(FALSE, slot, spell)); } case BF_MIND_RESERVE: { /* Check the Mindcrafter spells for damage */ return (borg_attack_mindcrafter_reserve(FALSE, spell)); } case BF_SPELL_FAINT: { /* Check the spells again if in trouble */ return (borg_attack_spell_reserve(TRUE, slot, spell)); } case BF_MIND_FAINT: { /* Check the Mindcrafter spells for damage */ return (borg_attack_mindcrafter_reserve(TRUE, spell)); } } /* report code mistake */ borg_oops("The BF_value %d is not in the switch", what); /* Oops */ return (0); } /* This procedure adds a grid coords to borg_ball */ static void borg_add_temp_ball(int x, int y) { int k; /* Check the grid array */ for (k = 0; k < borg_ball_n; k++) { /* Has this grid been used already? */ if ((borg_ball_x[k] == x) && (borg_ball_y[k] == y)) { /* Don't stick in doubles */ return; } } /* Stick this grid in the temp_grid array */ borg_ball_x[borg_ball_n] = x; borg_ball_y[borg_ball_n] = y; borg_ball_n++; } /* This procedure adds a grid coords to borg_beam */ static void borg_add_temp_beam(int x, int y) { /* Stick this monster in the temp ball array */ borg_beam_x[borg_beam_n] = x; borg_beam_y[borg_beam_n] = y; borg_beam_n++; } /* This procedure adds a grid coords to borg_bolt */ static void borg_add_temp_bolt(int x, int y) { /* Stick this monster in the temp bolt array */ borg_bolt_x[borg_bolt_n] = x; borg_bolt_y[borg_bolt_n] = y; borg_bolt_n++; } /* This procedure adds a grid coords to borg_bolt */ static void borg_add_temp_next(int x, int y) { /* Stick this monster in the temp bolt array */ borg_next_x[borg_next_n] = x; borg_next_y[borg_next_n] = y; borg_next_n++; } /* * This procedure fills the temp monster arrays with coords of monsters in LOS. * The basic idea behind these arrays is that they are all checked beforehand * so if a procdure wants to use them there is no need for checking for walls, * LOS, walls, etc. * * borg_temp contains all the monsters within range, as in the old situation. * borg_bolt contains all the monsters that can be hit by a bolt. * borg_beam contains all the monsters than can be hit by a beam. * borg_ball contains all the coords where you can target a ball and hit a * monster directly and also the targetable grids next to any monster. * * If the borg has ESP then this routine will deliver monsters that are not in * LOS, because they are hidden by walls on unknown terrain. This is where * successful_target comes in. If the borg attempted a distance attack in the * previous move then then suc_target is set to FALSE. If the borg hit something * apparently there is no wall in the way and succ_target is set to TRUE. Then * the borg can use borg_los and borg_bolt_los for this turn. However, if the * borg failed to hit the target then there must be a wall in the way and the * borg will use borg_los_pure and borg_bolt_los_pure. */ void borg_temp_fill(void) { int i; int dist; int x, y, dx, dy; int x1, y1; /* Avoid doing this costly procedure twice to get the same data */ if (borg_temp_fill_valid && borg_temp_fill_valid == borg_t) return; /* Set the counter to this turn */ borg_temp_fill_valid = borg_t; /* Reset lists */ borg_temp_n = 0; borg_next_n = 0; borg_bolt_n = 0; borg_beam_n = 0; borg_ball_n = 0; /* Find "nearby" monsters */ for (i = 1; i < borg_kills_nxt; i++) { /* Get a monster */ borg_kill *kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Require current knowledge */ if (kill->when < borg_t) continue; /* Ignore multiplying monsters and when fleeing from scaries */ if (goal_ignoring && !bp_ptr->status.afraid && FLAG(&r_info[kill->r_idx], RF_MULTIPLY)) continue; /* If it is a pet, ignore it */ if (kill->m_flags & (MONST_FRIEND | MONST_PET)) continue; /* Acquire location */ x = kill->x; y = kill->y; /* How far is this monster? */ dist = distance(c_x, c_y, x, y); /* Not too far away */ if (dist > MAX_RANGE) continue; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Save the location (careful) */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; /* Keep the coords of the monster */ x1 = x; y1 = y; /* Is the monster next to the borg? */ if (distance(c_x, c_y, x, y) == 1) borg_add_temp_next(x, y); for (dx = -1; dx <= 1; dx++) { for (dy = -1; dy <= 1; dy++) { /* Keep the coords of the target grid */ x = x1 + dx; y = y1 + dy; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* How far is this grid */ dist = distance(c_x, c_y, x, y); /* Is this grid out of range? */ if (dist > MAX_RANGE) continue; /* If the borg has no ESP * or the borg has ESP and has just hit his target * assume there is no wall in the way * OR * If the borg has ESP and has just missed his target * assume there is a wall in the way */ if (((!FLAG(bp_ptr, TR_TELEPATHY) || (FLAG(bp_ptr, TR_TELEPATHY) && successful_target)) && borg_los(c_x, c_y, x, y)) || (FLAG(bp_ptr, TR_TELEPATHY) && !successful_target && borg_los_pure(c_x, c_y, x, y))) { /* If it is not a wall it is OK for a ball */ if (!borg_cave_wall_grid(map_loc(x, y))) { /* Add the coords to the ball array */ borg_add_temp_ball(x, y); } /* is this square on the monster? */ if (!dx && !dy) { /* Add the coords to the beam array */ borg_add_temp_beam(x1, y1); } } /* Is this monster targetable without going through a monster? */ if (!dx && !dy) { /* * If the borg has no ESP or * the borg has ESP and has just hit his target * then assume unknown terrain is not a wall * OR * If the borg has ESP and has just missed his target * then assume unknown terrain is a wall */ if (((!FLAG(bp_ptr, TR_TELEPATHY) || (FLAG(bp_ptr, TR_TELEPATHY) && successful_target)) && borg_bolt_los(c_x, c_y, x1, y1)) || (FLAG(bp_ptr, TR_TELEPATHY) && !successful_target && borg_bolt_los_pure(c_x, c_y, x1, y1))) { /* Add the coords to the bolt array */ borg_add_temp_bolt(x1, y1); } } } } } } /* * Attack nearby monsters, in the best possible way, if any. * * We consider a variety of possible attacks, including physical attacks * on adjacent monsters, missile attacks on nearby monsters, spell/prayer * attacks on nearby monsters, and wand/rod attacks on nearby monsters. * * Basically, for each of the known "types" of attack, we "simulate" the * "optimal" result of using that attack, and then we "apply" the "type" * of attack which appears to have the "optimal" result. * * When calculating the "result" of using an attack, we only consider the * effect of the attack on visible, on-screen, known monsters, which are * within 16 grids of the player. This prevents most "spurious" attacks, * but we can still be fooled by situations like creeping coins which die * while out of sight, leaving behind a pile of coins, which we then find * again, and attack with distance attacks, which have no effect. Perhaps * we should "expect" certain results, and take note of failure to observe * those effects. XXX XXX XXX * * See above for the "semantics" of each "type" of attack. */ bool borg_attack(bool boosted_bravery) { int n, b_n = 0; int b_x = 0, b_y = 0; int g, b_g = -1; int slot, b_slot = -1; int spell, b_spell = -1; /* Nobody around */ if (!borg_kills_cnt) return (FALSE); /* Set the attacking flag so that danger is boosted for monsters */ /* we want to attack first. */ borg_attacking = TRUE; /* no attacking most scaryguys, try to get off the level */ if (scaryguy_on_level) { /* probably Grip or Fang. */ if (bp_ptr->depth <= 5 && bp_ptr->depth != 0 && borg_fighting_unique) { /* Try to fight Grip and Fang. */ } else if (boosted_bravery) { /* Try to fight if being Boosted */ } else { /* Flee from other scary guys */ borg_attacking = FALSE; return (FALSE); } } /* Check the surroundings for monsters */ borg_temp_fill(); /* Are there monsters to kill? */ if (!borg_ball_n) { borg_attacking = FALSE; return (FALSE); } /* Simulate */ borg_simulate = TRUE; /* Set default target */ g_x = c_x; g_y = c_y; /* Analyze the possible attacks */ for (g = BF_MIN; g < BF_MAX; g++) { /* Clear the parameters */ slot = -1; spell = -1; /* Simulate */ n = borg_attack_aux(g, &slot, &spell); /* Track "best" attack <= */ if (n <= b_n) continue; /* Track best */ b_g = g; b_n = n; /* Track the globals */ b_x = g_x; b_y = g_y; b_slot = slot; b_spell = spell; } /* Nothing good */ if (b_n <= 0) { borg_attacking = FALSE; return (FALSE); } /* Note */ borg_note("# Performing attack type %d with value %d.", b_g, b_n); /* Instantiate */ borg_simulate = FALSE; /* set globals back for this attack */ g_x = b_x; g_y = b_y; /* Instantiate */ (void)borg_attack_aux(b_g, &b_slot, &b_spell); borg_attacking = FALSE; /* Success */ return (TRUE); } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zbmagic3.c0000644000000000000000000022474110250356275014011 0ustar rootroot/* File: zbmagic3.c */ /* Purpose: Medium level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zbmagic.h" /* * * There are several types of setup moves: * * Temporary speed * Protect From Evil * Bless\Prayer * Berserk\Heroism * Temp Resist (either all or just cold/fire?) * Shield * Teleport away * Glyph of Warding * See inviso * * * and many others * */ enum { BD_SPEED, BD_PROT_FROM_EVIL, BD_BLESS, BD_HERO_BERSERK, BD_RESIST_FCE, BD_RESIST_FECAP, BD_RESIST_F, BD_RESIST_C, BD_RESIST_A, BD_RESIST_P, BD_SHIELD, BD_GOI, BD_GOI_POT, BD_GLYPH, BD_WARDING, BD_TELL_AWAY, BD_CREATE_WALLS, BD_MASS_GENOCIDE, BD_GENOCIDE, BD_GENOCIDE_HOUNDS, BD_EARTHQUAKE, BD_DESTRUCTION, BD_BANISHMENT, BD_DETECT_INVISO, BD_TELEPATHY, BD_LIGHT_BEAM, BD_TRUMP_SERVANT, BD_MAX }; /* basic method to calulate what fail_rate is allowed */ static int borg_fail_allowed(int p1) { int fail_allowed = 39; /* If very scary, do not allow for much chance of fail */ if (p1 > avoidance) { fail_allowed -= 19; } /* a little scary */ else if (p1 > (avoidance * 2) / 3) { fail_allowed -= 10; } /* not very scary, allow lots of fail */ else if (p1 < avoidance / 3) { fail_allowed += 10; } /* Give it back */ return (fail_allowed); } /* * Bless/Prayer to prepare for battle */ static int borg_defend_aux_bless(int p1) { int fail_allowed = 10; if (borg_simulate) { /* already blessed */ if (borg_bless) return (0); /* Check if the borg be blessed (weakest last) */ if (!borg_spell_okay_fail(REALM_LIFE, 3, 1, fail_allowed) && !borg_spell_okay_fail(REALM_LIFE, 0, 2, fail_allowed) && !borg_activate_fail(BORG_ACT_BLESS) && !borg_read_scroll_fail(SV_SCROLL_HOLY_PRAYER) && !borg_read_scroll_fail(SV_SCROLL_HOLY_CHANT) && !borg_read_scroll_fail(SV_SCROLL_BLESSING)) return (0); /* if we are in some danger but not much, go for a quick bless */ if (p1 > avoidance / 12 && p1 < avoidance / 2) { /* bless is a low priority */ return (1); } /* Don't do it */ return (0); } /* do it! */ return (borg_activate_fail(BORG_ACT_BLESS) || borg_spell(REALM_LIFE, 3, 1) || borg_spell(REALM_LIFE, 0, 2) || borg_read_scroll(SV_SCROLL_BLESSING) || borg_read_scroll(SV_SCROLL_HOLY_CHANT) || borg_read_scroll(SV_SCROLL_HOLY_PRAYER)); } /* * Speed to prepare for battle */ static int borg_defend_aux_speed(int p1) { int p2; bool good_speed = FALSE; int fail_allowed; if (borg_simulate) { /* Already fast */ if (borg_speed) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); /* Only cast defence spells if fail rate is not too high */ if (borg_spell_okay_fail(REALM_SORCERY, 1, 5, fail_allowed) || borg_spell_okay_fail(REALM_DEATH, 2, 3, fail_allowed) || borg_mindcr_okay_fail(MIND_ADRENALINE, 35, fail_allowed) || borg_equips_staff_fail(SV_STAFF_SPEED) || borg_equips_rod(SV_ROD_SPEED)) { /* Recheargable speeds */ good_speed = TRUE; } /* If there is no speed source */ if (!good_speed && !borg_slot(TV_POTION, SV_POTION_SPEED)) { /* Give up */ return (0); } /* pretend we are sped up and look again */ borg_speed = TRUE; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_speed = FALSE; /* if we are fighting a unique cast it. */ if (borg_fighting_unique) { /* HACK pretend that it was scary and will be safer */ p2 = p2 * 7 / 10; } /* if the unique is Oberon cast it */ if (bp_ptr->depth == 99 && borg_fighting_unique >= BORG_QUESTOR) { p2 = p2 * 6 / 10; } /* if the unique is The Serpent cast it */ if (bp_ptr->depth == 100 && borg_fighting_unique >= BORG_QUESTOR) { p2 = p2 * 5 / 10; } /* * If this is an improvement and we may * not avoid monster now and we may have before */ if (((p1 > p2) && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && (p1 > (avoidance / 5)) && good_speed) || ((p1 > p2) && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 3)) && (p1 > (avoidance / 7)))) { /* Simulation */ return (p1 - p2 + (borg_goi / 100) * 50); } /* default to can't do it. */ return (0); } /* Do it! */ return (borg_spell(REALM_SORCERY, 1, 5) || borg_spell(REALM_DEATH, 2, 3) || borg_mindcr(MIND_ADRENALINE, 35) || borg_zap_rod(SV_ROD_SPEED) || borg_use_staff(SV_STAFF_SPEED) || borg_quaff_potion(SV_POTION_SPEED)); } /* * Globe of Invulnurability */ static int borg_defend_aux_goi(int p1) { int p2; int fail_allowed; if (borg_simulate) { /* does the borg have the spell up already? */ if (borg_goi) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); /* If fighting regular unique boost the fail rate */ if (borg_fighting_unique >= 1) fail_allowed = MAX(25, fail_allowed); /* If fighting Questor */ if (borg_fighting_unique >= BORG_QUESTOR) { /* boost the fail rate */ fail_allowed = MAX(33, fail_allowed); } /* Do we have the spell? */ if (!borg_activate_fail(BORG_ACT_INVULNERABILITY) && !borg_spell_okay_fail(REALM_SORCERY, 3, 7, fail_allowed) && !borg_spell_okay_fail(REALM_LIFE, 3, 7, fail_allowed)) return (0); /* pretend we are protected and look again */ borg_goi = 100; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_goi = 0; /* if we are fighting a unique enhance the value by reducing p2 */ if (borg_fighting_unique) { p2 = p2 / 2; } /* if the unique is Oberon cast it */ if (bp_ptr->depth == 99 && borg_fighting_unique >= BORG_QUESTOR) { p2 = p2 * 4 / 10; } /* if the unique is the Serpent cast it */ if (bp_ptr->depth == 100 && borg_fighting_unique >= BORG_QUESTOR) { p2 = 0; } /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p1 > (avoidance / 7) && p2 <= (borg_fighting_unique) ? ((avoidance * 2) / 3) : (avoidance / 2)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_INVULNERABILITY) || borg_spell(REALM_SORCERY, 3, 7) || borg_spell(REALM_LIFE, 3, 7)); } /* * Globe of Invulnurability Potion */ static int borg_defend_aux_goi_pot(int p1) { int p2; if (borg_simulate) { if (borg_goi) return (0); /* Save for fighting uniques */ if (!borg_fighting_unique) return (0); /* have some in inven? */ if (!borg_slot(TV_POTION, SV_POTION_INVULNERABILITY)) return (0); /* pretend we are protected and look again */ borg_goi = 100; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_goi = 0; /* Fighting a unique, enhance the value by reducing p2 */ p2 = p2 / 2; /* if the unique is Oberon cast it */ if (bp_ptr->depth == 99 && borg_fighting_unique >= BORG_QUESTOR) { p2 = p2 * 4 / 10; } /* if the unique is The Serpent cast it */ if (bp_ptr->depth == 100 && borg_fighting_unique >= BORG_QUESTOR) { p2 = 0; } /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p1 > (avoidance / 7) && p2 <= (borg_fighting_unique) ? ((avoidance * 2) / 3) : (avoidance / 2)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_quaff_potion(SV_POTION_INVULNERABILITY)); } /* cold/fire */ static int borg_defend_aux_resist_fce(int p1) { int p2; int fail_allowed; bool save_fire, save_cold, save_elec; if (borg_simulate) { if (my_oppose_fire && my_oppose_cold && my_oppose_elec) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); /* Does the borg have the spell? */ if (!borg_spell_okay_fail(REALM_NATURE, 0, 6, fail_allowed) && !borg_activate_fail(BORG_ACT_RESISTANCE)) return (0); /* pretend we are protected and look again */ save_fire = my_oppose_fire; save_cold = my_oppose_cold; save_elec = my_oppose_elec; my_oppose_fire = TRUE; my_oppose_cold = TRUE; my_oppose_elec = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); my_oppose_fire = save_fire; my_oppose_cold = save_cold; my_oppose_elec = save_elec; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_RESISTANCE) || borg_spell(REALM_NATURE, 0, 6)); } /* all resists */ static int borg_defend_aux_resist_fecap(int p1) { int p2; int fail_allowed; bool save_fire, save_acid, save_poison, save_elec, save_cold; if (borg_simulate) { /* Does the borg already have al the resists? */ if (my_oppose_fire && my_oppose_acid && my_oppose_pois && my_oppose_elec && my_oppose_cold) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); /* * How about adding the potion of Resistance? * Won't want to cast it though if only one element is * down. Ought to at least wait until 3 of the 4 are down. */ if (!borg_spell_okay_fail(REALM_NATURE, 2, 3, fail_allowed) && !borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 33, fail_allowed) && !borg_activate_fail(BORG_ACT_RESISTANCE) && !borg_mutation_check(MUT1_RESIST, TRUE) && !borg_slot(TV_POTION, SV_POTION_RESISTANCE)) return (0); /* pretend we are protected and look again */ save_fire = my_oppose_fire; save_acid = my_oppose_acid; save_poison = my_oppose_pois; save_elec = my_oppose_elec; save_cold = my_oppose_cold; my_oppose_fire = TRUE; my_oppose_cold = TRUE; my_oppose_acid = TRUE; my_oppose_pois = TRUE; my_oppose_elec = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); my_oppose_fire = save_fire; my_oppose_cold = save_cold; my_oppose_acid = save_acid; my_oppose_pois = save_poison; my_oppose_elec = save_elec; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return ((p1 - p2) - 1); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_RESISTANCE) || borg_spell(REALM_NATURE, 2, 3) || borg_mindcr(MIND_CHAR_ARMOUR, 33) || borg_mutation(MUT1_RESIST) || borg_quaff_potion(SV_POTION_RESISTANCE)); } /* fire */ static int borg_defend_aux_resist_f(int p1) { int p2; int fail_allowed; bool save_fire; if (borg_simulate) { if (my_oppose_fire) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_ARCANE, 1, 7, fail_allowed) && !borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 21, fail_allowed) && !borg_spell_okay_fail(REALM_NATURE, 0, 6, fail_allowed) && !borg_spell_okay_fail(REALM_NATURE, 2, 3, fail_allowed) && !borg_activate_fail(BORG_ACT_RESIST_FIRE) && !borg_activate_fail(BORG_ACT_RESISTANCE) && !borg_slot(TV_POTION, SV_POTION_RESIST_HEAT)) return (0); /* pretend we are protected and look again */ save_fire = my_oppose_fire; my_oppose_fire = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); my_oppose_fire = save_fire; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_RESIST_FIRE) || borg_activate(BORG_ACT_RESISTANCE) || borg_spell(REALM_NATURE, 0, 6) || borg_spell(REALM_ARCANE, 1, 7) || borg_spell(REALM_NATURE, 2, 3) || borg_mindcr(MIND_CHAR_ARMOUR, 21) || borg_quaff_potion(SV_POTION_RESIST_HEAT)); } /* cold */ static int borg_defend_aux_resist_c(int p1) { int p2; int fail_allowed; bool save_cold; if (borg_simulate) { if (my_oppose_cold) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_ARCANE, 1, 7, fail_allowed) && !borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 25, fail_allowed) && !borg_activate_fail(BORG_ACT_RESIST_COLD) && !borg_activate_fail(BORG_ACT_RESISTANCE) && !borg_slot(TV_POTION, SV_POTION_RESIST_COLD)) return (0); /* pretend we are protected and look again */ save_cold = my_oppose_cold; my_oppose_cold = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); my_oppose_cold = save_cold; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_RESIST_COLD) || borg_activate(BORG_ACT_RESISTANCE) || borg_spell(REALM_ARCANE, 1, 7) || borg_mindcr(MIND_CHAR_ARMOUR, 25) || borg_quaff_potion(SV_POTION_RESIST_COLD)); } /* acid */ static int borg_defend_aux_resist_a(int p1) { int p2; int fail_allowed; bool save_acid; if (borg_simulate) { if (my_oppose_acid) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_ARCANE, 2, 1, fail_allowed) && !borg_activate_fail(BORG_ACT_RESIST_ACID) && !borg_activate_fail(BORG_ACT_RESISTANCE) && !borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 17, fail_allowed)) return (0); /* pretend we are protected and look again */ save_acid = my_oppose_acid; my_oppose_acid = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); my_oppose_acid = save_acid; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_RESIST_ACID) || borg_activate(BORG_ACT_RESISTANCE) || borg_mindcr(MIND_CHAR_ARMOUR, 15) || borg_spell(REALM_ARCANE, 2, 1)); } /* poison */ static int borg_defend_aux_resist_p(int p1) { int p2; int fail_allowed; bool save_poison; if (borg_simulate) { if (my_oppose_pois) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_DEATH, 0, 5, fail_allowed) && !borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 33, fail_allowed) && !borg_activate_fail(BORG_ACT_RESIST_POISON) && !borg_activate_fail(BORG_ACT_RESISTANCE)) return (0); /* pretend we are protected and look again */ save_poison = my_oppose_pois; my_oppose_pois = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); my_oppose_pois = save_poison; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_activate(BORG_ACT_RESIST_POISON) || borg_activate(BORG_ACT_RESISTANCE) || borg_mindcr(MIND_CHAR_ARMOUR, 33) || borg_spell(REALM_DEATH, 0, 5)); } static int borg_defend_aux_prot_evil(int p1) { int p2; int fail_allowed; if (borg_simulate) { /* if already protected */ if (borg_prot_from_evil || FLAG(bp_ptr, TR_SLAY_EVIL)) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); /* Is the spell available? */ if (!borg_spell_okay_fail(REALM_LIFE, 1, 5, fail_allowed) && !borg_activate_fail(BORG_ACT_PROT_EVIL) && !borg_read_scroll_fail(SV_SCROLL_PROTECTION_FROM_EVIL)) return (0); /* pretend we are protected and look again */ borg_prot_from_evil = TRUE; p2 = borg_danger(c_x, c_y, 1, FALSE); borg_prot_from_evil = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_spell(REALM_LIFE, 1, 5) || borg_activate(BORG_ACT_PROT_EVIL) || borg_read_scroll(SV_SCROLL_PROTECTION_FROM_EVIL)); } static int borg_defend_aux_shield(int p1) { int p2; int fail_allowed; if (borg_simulate) { /* if already protected */ if (borg_shield || borg_goi) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_NATURE, 2, 2, fail_allowed) && !borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 13, fail_allowed) && !borg_racial_check(RACE_GOLEM, TRUE)) return (0); /* pretend we are protected and look again */ borg_shield = TRUE; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_shield = FALSE; /* slightly enhance the value if fighting a unique */ if (borg_fighting_unique) p2 = (p2 * 7 / 10); /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* do it! */ return (borg_spell(REALM_NATURE, 2, 2) || borg_mindcr(MIND_CHAR_ARMOUR, 13) || borg_racial(RACE_GOLEM)); } /* * Try to get rid of all of the non-uniques around so you can go at it * 'mano-e-mano' with the unique. */ static int borg_defend_aux_tell_away(int p1) { int p2, b_n; int fail_allowed = 30; if (borg_simulate) { /* Only tell away if scared */ if (p1 < avoidance) return (0); /* if very scary, do not allow for much chance of fail */ if (p1 > avoidance * 4) { fail_allowed -= 18; } /* scary */ else if (p1 > avoidance * 3) { fail_allowed -= 12; } /* a little scary */ else if (p1 > (avoidance * 5) / 2) { fail_allowed += 5; } if (!borg_spell_okay_fail(REALM_ARCANE, 3, 3, fail_allowed) && !borg_spell_okay_fail(REALM_SORCERY, 1, 4, fail_allowed) && !borg_spell_okay_fail(REALM_CHAOS, 1, 5, fail_allowed) && !borg_activate_fail(BORG_ACT_TELEPORT_AWAY) && !borg_equips_rod_fail(SV_ROD_TELEPORT_AWAY) && !borg_equips_wand_fail(SV_WAND_TELEPORT_AWAY)) return (0); /* Try all monsters for the best shot */ b_n = borg_launch_beam(50, GF_AWAY_ALL, MAX_RANGE); /* normalize the value */ p2 = MAX(p1 - b_n, 0); /* check to see if I am left better off */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (b_n); } /* Oh well */ return (0); } /* Set the target */ borg_target(g_x, g_y); /* Set our shooting flag */ successful_target = BORG_FRESH_TARGET; /* Cast the spell */ return (borg_spell(REALM_SORCERY, 1, 4) || borg_spell(REALM_ARCANE, 3, 3) || borg_spell(REALM_CHAOS, 1, 5) || borg_activate(BORG_ACT_TELEPORT_AWAY) || borg_zap_rod(SV_ROD_TELEPORT_AWAY) || borg_aim_wand(SV_WAND_TELEPORT_AWAY)); } /* * Hero to prepare for battle */ static int borg_defend_aux_hero(int p1) { int fail_allowed = 10; if (borg_simulate) { /* already hero */ if (borg_hero || borg_berserk) return (0); /* Is there some way to berserk? */ if (!borg_spell_okay_fail(REALM_LIFE, 3, 0, fail_allowed) && !borg_spell_okay_fail(REALM_DEATH, 2, 0, fail_allowed) && !borg_mindcr_okay_fail(MIND_ADRENALINE, 23, fail_allowed) && !borg_activate_fail(BORG_ACT_HEROISM) && !borg_activate_fail(BORG_ACT_BERSERKER) && !borg_racial_check(RACE_HALF_TROLL, TRUE) && !borg_racial_check(RACE_BARBARIAN, TRUE) && !borg_mutation_check(MUT1_BERSERK, TRUE) && !borg_slot(TV_POTION, SV_POTION_BERSERK_STRENGTH) && !borg_slot(TV_POTION, SV_POTION_HEROISM)) return (0); /* if we are in some danger but not much, go for a quick heroism */ if (borg_goi || (p1 > avoidance / 12 && p1 < avoidance / 2) || (borg_fighting_unique && p1 < avoidance * 13 / 10)) { /* Simulation */ return (1); } /* Never mind */ return (0); } /* do it! */ return (borg_spell(REALM_LIFE, 3, 0) || borg_spell(REALM_DEATH, 2, 0) || borg_mindcr(MIND_ADRENALINE, 23) || borg_activate(BORG_ACT_HEROISM) || borg_activate(BORG_ACT_BERSERKER) || borg_racial(RACE_HALF_TROLL) || borg_racial(RACE_BARBARIAN) || borg_mutation(MUT1_BERSERK) || borg_quaff_potion(SV_POTION_BERSERK_STRENGTH) || borg_quaff_potion(SV_POTION_HEROISM)); } /* Glyph of Warding and Rune of Protection */ static int borg_defend_aux_glyph(int p1) { int p2, i; int fail_allowed; map_block *mb_ptr = map_loc(c_x, c_y); if (borg_simulate) { /* He should not cast it while on an object. * I have addressed this inadequately in borg9.c when dealing with * messages. The message "the object resists" will delete the glyph * from the array. Then I set a broken door on that spot, the borg ignores * broken doors, so he won't loop. */ if ((mb_ptr->object) || (mb_ptr->m_effect) || (mb_ptr->trap) || (mb_ptr->feat == FEAT_LESS) || (mb_ptr->feat == FEAT_MORE) || (mb_ptr->feat == FEAT_CLOSED) || (mb_ptr->feat == FEAT_OPEN) || (mb_ptr->feat == FEAT_BROKEN)) { /* Something is in the way */ return (0); } /* The Serpent breaks these in one try so its a waste of mana against him */ if (borg_fighting_unique >= BORG_QUESTOR) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_LIFE, 1, 7, fail_allowed) && !borg_read_scroll_fail(SV_SCROLL_RUNE_OF_PROTECTION)) return (0); /* pretend we are protected and look again */ borg_on_glyph = TRUE; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_on_glyph = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } /* Check for an existing glyph */ for (i = 0; i < track_glyph_num; i++) { /* Stop if we already knew about this glyph */ if (track_glyph_x[i] == c_x && track_glyph_y[i] == c_y) break; } /* Track the newly discovered glyph */ if (i == track_glyph_num) { /* If the borg makes too many glyphs */ if (track_glyph_size == track_glyph_num) { /* Please recompile with a higher track_glyph_size value */ borg_oops("Borg makes too many glyphs. Increase track_glyph_num"); } /* Keep track of the existing glyphs */ borg_note("# Noting the creation of a glyph."); track_glyph_num++; track_glyph_x[i] = c_x; track_glyph_y[i] = c_y; } /* do it! */ return (borg_spell(REALM_LIFE, 1, 7) || borg_read_scroll(SV_SCROLL_RUNE_OF_PROTECTION)); } /* True Warding */ static int borg_defend_aux_true_warding(int p1) { /* Ignore parameter */ (void)p1; #if 0 int p2; int fail_allowed; int glyph_bad = 0; int glyph_x, glyph_y, x, y; map_block *mb_ptr; /* any summoners near? */ if (!borg_fighting_summoner) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_LIFE, 2, 7, fail_allowed)) return (0); /* Do not cast if surounded by doors or something */ /* Get grid */ for (glyph_x = -1; glyph_x <= 1; glyph_x++) { for (glyph_y = -1; glyph_y <= 1; glyph_y++) { /* Acquire location */ x = glyph_x + c_x; y = glyph_y + c_y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* track spaces already protected */ if ((mb_ptr->feat >= FEAT_CLOSED) && (mb_ptr->feat <= FEAT_PERM_SOLID)) { glyph_bad++; } /* track spaces that cannot be protected */ if ((mb_ptr->object) /* || ((mb_ptr->feat >= FEAT_TRAP_TRAPDOOR) && (mb_ptr->feat <= FEAT_TRAP_SLEEP)) */ || (mb_ptr->feat == FEAT_LESS) || (mb_ptr->feat == FEAT_MORE) || (mb_ptr->feat == FEAT_OPEN) || (mb_ptr->feat == FEAT_BROKEN) || (mb_ptr->monster)) { glyph_bad++; } } } /* Track it */ /* lets make sure that we going to be benifited */ if (glyph_bad >= 6) { /* not really worth it. Only 2 spaces protected */ return (0); } /* pretend we are protected and look again (use the door code) */ borg_create_door = TRUE; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_create_door = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ if (borg_simulate) return (p1 - p2); /* do it! */ if (borg_spell_fail(REALM_LIFE, 2, 7, fail_allowed)) { /* Set the breeder flag to keep doors closed. Avoid summons */ breeder_level = TRUE; /* Value */ return (p1 - p2); } } #endif /* 0 */ /* default to can't do it. */ return (0); } /* Create Granite Walls-- Nature spell */ static int borg_defend_aux_create_walls(int p1) { /* Ignore parameter */ (void)p1; #if 0 int p2 = 0; int fail_allowed = 99; int wall_bad = 0; int wall_x, wall_y, x, y; map_block *mb_ptr; /* any summoners near? */ if (!borg_fighting_summoner) return (0); /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); if (!borg_spell_okay_fail(REALM_NATURE, 2, 6, fail_allowed) && !borg_spell_okay_fail(REALM_NATURE, 2, 0, fail_allowed)) return (0); /* Do not cast if surounded by doors or something */ /* Get grid */ for (wall_x = -1; wall_x <= 1; wall_x++) { for (wall_y = -1; wall_y <= 1; wall_y++) { /* Acquire location */ x = wall_x + c_x; y = wall_y + c_y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* track spaces already protected */ if ( /* (mb_ptr->feat == FEAT_GLYPH) || (mb_ptr->feat == FEAT_MINOR_GLYPH) || */ mb_ptr-> monster || ((mb_ptr->feat >= FEAT_CLOSED) && (mb_ptr->feat <= FEAT_PERM_SOLID))) { wall_bad++; } /* track spaces that cannot be protected */ if ((mb_ptr->object) /*|| ((mb_ptr->feat >= FEAT_TRAP_TRAPDOOR) && (mb_ptr->feat <= FEAT_TRAP_SLEEP)) */ || (mb_ptr->feat == FEAT_LESS) || (mb_ptr->feat == FEAT_MORE) || (mb_ptr->feat == FEAT_OPEN) || (mb_ptr->feat == FEAT_BROKEN) || (mb_ptr->monster)) { wall_bad++; } } } /* Track it */ /* lets make sure that we going to be benifited */ if (wall_bad >= 6) { /* not really worth it. Only 2 spaces protected */ return (0); } /* pretend we are protected and look again */ borg_create_door = TRUE; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_create_door = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 7)) { /* Simulation */ if (borg_simulate) return (p1 - p2); /* do it! */ if (borg_spell_fail(REALM_NATURE, 2, 0, fail_allowed) || borg_spell_fail(REALM_NATURE, 2, 6, fail_allowed)) { /* Set the breeder flag to keep doors closed. Avoid summons */ breeder_level = TRUE; /* Value */ return (p1 - p2); } } #endif /* 0 */ /* default to can't do it. */ return (0); } /* This will simulate and cast the mass genocide spell. */ static int borg_defend_aux_mass_genocide(int p1) { int hit = 0, i = 0, p2; int b_p = 0, p; borg_kill *kill; monster_race *r_ptr; if (borg_simulate) { /* see if prayer is legal */ if (!borg_spell_okay_fail(REALM_DEATH, 2, 7, 40) && !borg_spell_okay_fail(REALM_DEATH, 3, 6, 40) && !borg_activate_fail(BORG_ACT_MASS_GENOCIDE) && !borg_read_scroll_fail(SV_SCROLL_MASS_GENOCIDE)) return (0); /* See if he is in real danger */ if (p1 < avoidance * 12 / 10) return (0); /* Find a monster and calculate its danger */ for (i = 0; i < borg_kills_nxt; i++) { /* Monster */ kill = &borg_kills[i]; r_ptr = &r_info[kill->r_idx]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Check the distance */ if (distance(c_y, c_x, kill->y, kill->x) > 20) continue; /* we try not to genocide uniques */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Calculate danger */ borg_full_damage = TRUE; p = borg_danger_aux(c_x, c_y, 1, i, TRUE); borg_full_damage = FALSE; /* store the danger for this type of monster */ b_p = b_p + p; hit = hit + 4; } /* normalize the value */ p2 = MAX(p1 - b_p, 0); /* if strain (plus a pad incase we did not know about some monsters) * is greater than hp, don't cast it */ if ((hit * 11 / 10) >= bp_ptr->chp) return (0); /* Penalize the strain from casting the spell */ p2 = p2 + hit; /* Be more likely to use this if fighting The Serpent */ if (borg_fighting_unique >= BORG_QUESTOR && (hit / 3 > 8)) { p2 = p2 * 6 / 10; } /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique ? (avoidance * 2 / 3) : (avoidance / 2))) { /* Simulation */ return (p1 - p2); } /* Not worth it */ return (0); } /* Cast the spell */ return (borg_spell(REALM_DEATH, 2, 7) || borg_spell(REALM_DEATH, 3, 6) || borg_activate(BORG_ACT_MASS_GENOCIDE) || borg_read_scroll(SV_SCROLL_MASS_GENOCIDE)); } /* This will simulate and cast the genocide spell. * There are two seperate functions happening here. * 1. will genocide the race which is immediately threatening me. * 2. will genocide the race which is most dangerous on the level. Though it may not be * threatening the borg right now. It was considered to nuke the escorts of a unique. * But it could also be used to nuke a race if it becomes too dangerous, for example * a summoner called up 15-20 hounds, and they must be dealt with. * The first option may be called at any time. While the 2nd option is only called when the * borg is in relatively good health. */ static int borg_defend_aux_genocide(int p1, int *genocide_target) { int i, p, u, b_i = 0; int p2 = 0; int threat = 0; int max = 1; int b_p[256]; int b_num[256]; int b_threat[256]; int b_threat_num[256]; int b_threat_id = (char)0; int fail_allowed; if (borg_simulate) { /* Set default target */ *genocide_target = 0; /* Get the allowed fail_rate */ fail_allowed = borg_fail_allowed(p1); /* Is genocide available at all? */ if (!borg_spell_okay_fail(REALM_DEATH, 1, 6, fail_allowed) && !borg_equips_staff_fail(SV_STAFF_GENOCIDE) && !borg_activate_fail(BORG_ACT_GENOCIDE) && !borg_read_scroll_fail(SV_SCROLL_GENOCIDE)) return (0); /* Don't try it if really weak */ if (bp_ptr->chp <= 75) return (0); /* two methods to calculate the threat: *1. cycle each character of monsters on screen * collect collective threat of each char *2 select race of most dangerous guy, and choose him. * Method 2 is cheaper and faster. * * The borg uses method #1 */ /* Clear previous dangers */ for (i = 0; i < 256; i++) { b_p[i] = 0; b_num[i] = 0; b_threat[i] = 0; b_threat_num[i] = 0; } /* Find a monster and calculate its danger */ for (i = 0; i < borg_kills_nxt; i++) { borg_kill *kill; monster_race *r_ptr; /* Monster */ kill = &borg_kills[i]; r_ptr = &r_info[kill->r_idx]; /* Our char of the monster */ u = r_ptr->d_char; /* Skip dead monsters */ if (!kill->r_idx) continue; /* we try not to genocide uniques */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Calculate danger */ borg_full_damage = TRUE; p = borg_danger_aux(c_x, c_y, 1, i, TRUE); threat = borg_danger_aux(kill->x, kill->y, 1, i, TRUE); borg_full_damage = FALSE; /* store the danger for this type of monster */ b_p[u] = b_p[u] + p; b_threat[u] = b_threat[u] + threat; /* Store the number of this type of monster */ b_num[u]++; b_threat_num[u]++; } /* Now, see which race contributes the most danger */ for (i = 0; i < 256; i++) { /* Skip this one if empty */ if (!b_p[i]) continue; /* for the race threatening me right now */ if (b_p[i] > max) { /* track the race */ max = b_p[i]; b_i = i; /* note the danger with this race gone */ p2 = p1 - b_p[b_i]; } /* for this race on the whole level */ if (b_threat[i] > max) { /* track the race */ max = b_threat[i]; b_threat_id = i; /* Asses the danger on the level */ p2 = MAX(p1 - b_threat[i], 0); } } /* * This will track and decide if it is worth genociding this dangerous * race for the level */ if (b_threat_id) { /* Not if I am weak (Have to watch out for monster pits) */ if (bp_ptr->chp < bp_ptr->mhp || bp_ptr->chp < 375) b_threat_id = 0; /* Do not perform in Danger */ if (borg_danger(c_x, c_y, 1, TRUE) > avoidance/5) b_threat_id = 0; /* The threat must be real */ if (b_threat[b_threat_id] < bp_ptr->mhp * 10) b_threat_id = 0; /* Too painful to cast it (padded to be safe) */ if (b_num[b_threat_id] * 44 / 10 >= bp_ptr->chp) b_threat_id = 0; /* report the danger and most dangerous race */ if (b_threat_id) { borg_note ("# Race '%c' is a real threat with total danger %d from %d individuals.", b_threat_id, b_threat[b_threat_id], b_threat_num[b_threat_id]); } /* Genociding this race would reduce the danger of the level */ *genocide_target = b_threat_id; } /* Consider the immediate threat genocide */ if (b_i) { /* Too painful to cast it (padded to be safe incase of unknown monsters) */ if (b_num[b_i] * 44 / 10 >= bp_ptr->chp) b_i = 0; /* See if he is in real danger, generally, * or deeper in the dungeon, conservatively, */ if (p1 < avoidance * 12 / 10 || (bp_ptr->depth > 75 && p1 < avoidance * 7 / 10)) b_i = 0; /* Did this help improve my situation? */ if (p1 < p2 && p2 >= (avoidance / 2)) b_i = 0; /* Genociding this race would help me immediately */ *genocide_target = b_i; } /* Complete the genocide routine */ if (*genocide_target) { /* Simulation */ return (p1 - p2); } /* default to can't do it. */ return (0); } borg_note ("# Genociding race '%c' (%d)", *genocide_target, *genocide_target); /* do it! ---use scrolls first since they clutter inventory */ if (borg_read_scroll(SV_SCROLL_GENOCIDE) || borg_spell(REALM_DEATH, 1, 6) || borg_activate(BORG_ACT_GENOCIDE) || borg_use_staff(SV_STAFF_GENOCIDE)) { /* and the winner is..... */ borg_keypress((char)*genocide_target); /* Remove this race from the borg_kill */ for (i = 0; i < borg_kills_nxt; i++) { borg_kill *kill; monster_race *r_ptr; /* Monster */ kill = &borg_kills[i]; r_ptr = &r_info[kill->r_idx]; /* Our char of the monster */ if (r_ptr->d_char != *genocide_target) continue; /* remove this monster */ borg_delete_kill(i, "genocided"); } return (TRUE); } /* Inconceivable */ borg_oops("Decided to genocide without having genocide!"); return (FALSE); } /* This will cast the genocide spell on Hounds at the beginning of each level. */ static int borg_defend_aux_genocide_hounds(int p1) { int i = 0; char genocide_target = 'Z'; if (borg_simulate) { /* Not if I am weak */ if (bp_ptr->chp < bp_ptr->mhp || bp_ptr->chp < 350) return (0); /* only do it when deep, */ if (bp_ptr->depth < 50) return (0); /* Do not perform in Danger */ if (p1 > avoidance / 3) return (0); /* Is the spell available? */ if (!borg_spell_okay_fail(REALM_DEATH, 1, 6, 35) && !borg_activate_fail(BORG_ACT_GENOCIDE) && !borg_equips_staff_fail(SV_STAFF_GENOCIDE)) return (0); return (1); } borg_note("# Genociding Hounds at Start of DLevel"); if (borg_spell(REALM_DEATH, 1, 6) || borg_activate(BORG_ACT_GENOCIDE) || borg_use_staff(SV_STAFF_GENOCIDE)) { /* and the winner is..... */ borg_keypress(genocide_target); /* Remove this race from the borg_kill */ for (i = 0; i < borg_kills_nxt; i++) { monster_race *r_ptr; /* Monster */ r_ptr = &r_info[borg_kills[i].r_idx]; /* Our char of the monster */ if (r_ptr->d_char != genocide_target) continue; /* remove this monster */ borg_delete_kill(i, "genocided"); } return (1); } /* default to can't do it. */ return (0); } /* Earthquake, priest and mage spells. */ static int borg_defend_aux_earthquake(int p1) { int p2 = 0; int door_bad = 0; int door_x, door_y, x, y; map_block *mb_ptr; if (borg_simulate) { if (!borg_spell_okay_fail(REALM_NATURE, 3, 0, 35) && !borg_equips_staff_fail(SV_STAFF_EARTHQUAKES)) return (0); /* See if he is in real danger or fighting summoner */ if (p1 < avoidance) return (0); /* Do not cast if surounded by doors or something */ /* Get grid */ for (door_x = -1; door_x <= 1; door_x++) { for (door_y = -1; door_y <= 1; door_y++) { /* Acquire location */ x = door_x + c_x; y = door_y + c_y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* track spaces already protected */ if ( /*(mb_ptr->feat == FEAT_GLYPH) || */ (mb_ptr->feat >= FEAT_CLOSED && mb_ptr->feat <= FEAT_PERM_SOLID) || mb_ptr->object || mb_ptr->feat == FEAT_LESS || mb_ptr->feat == FEAT_MORE || mb_ptr->feat == FEAT_OPEN || mb_ptr->feat == FEAT_BROKEN || mb_ptr->monster) { door_bad++; } } } /* If there are too many bad spots don't bother */ if (door_bad >= 6) return (0); /* What effect is there? */ borg_create_door = TRUE; p2 = borg_danger(c_x, c_y, 1, TRUE); borg_create_door = FALSE; if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance / 5)) { /* Simulation */ return (p2); } /* I guess not */ return (0); } /* Cast the spell */ return (borg_spell(REALM_NATURE, 3, 0) || borg_use_staff(SV_STAFF_EARTHQUAKES)); } /* Word of Destruction, priest and mage spells. Death is right around the * corner, so kill everything. */ static int borg_defend_aux_destruction(int p1) { if (borg_simulate) { /* Borg_defend() is called before borg_escape(). He may have some * easy ways to escape (teleport scroll) but he may attempt this spell * instead of using the scrolls. */ /* Use teleport scrolls instead of WoD */ if (bp_ptr->able.escape && !bp_ptr->status.blind && !bp_ptr->status.confused) return (0); if (!borg_spell_okay_fail(REALM_CHAOS, 1, 6, 55) && !borg_read_scroll_fail(SV_SCROLL_STAR_DESTRUCTION) && !borg_equips_staff_fail(SV_STAFF_DESTRUCTION) && !(p1 > (avoidance * 4) && borg_equips_staff(SV_STAFF_DESTRUCTION))) return (0); /* See if he is in real danger */ if (p1 < avoidance * 2) return (0); /* Try not to cast this against uniques */ /* Don't cast it on a quest level */ if ((borg_fighting_unique && p1 < avoidance * 5) || borg_fighting_unique >= BORG_QUESTOR) return (0); /* Simulation */ return (p1); } /* Cast the spell */ return (borg_spell(REALM_CHAOS, 1, 6) || borg_use_staff(SV_STAFF_DESTRUCTION) || borg_read_scroll(SV_SCROLL_STAR_DESTRUCTION)); } static int borg_defend_aux_banishment(int p1) { int p2 = 1; int fail_allowed = 15; int i; if (borg_simulate) { /* Only tell away if scared */ if (p1 < avoidance * 12 / 10) return (0); /* if very scary, do not allow for much chance of fail */ if (p1 > avoidance * 4) fail_allowed -= 10; if (!borg_spell_okay_fail(REALM_LIFE, 2, 5, fail_allowed) && !borg_spell_okay_fail(REALM_TRUMP, 1, 7, fail_allowed)) return (0); /* reset initial danger */ p1 = 1; /* Two passes to determine exact danger */ for (i = 0; i < borg_beam_n; i++) { int x = borg_beam_x[i]; int y = borg_beam_y[i]; /* Make sure to be on the map */ if (!map_in_bounds(x, y)) continue; /* Calculate danger of who is left over */ borg_full_damage = TRUE; p1 += borg_danger_aux(c_x, c_y, 1, map_loc(x, y)->kill, TRUE); borg_full_damage = FALSE; } /* Pass two -- Find a monster and calculate its danger */ for (i = 0; i < borg_beam_n; i++) { int x = borg_beam_x[i]; int y = borg_beam_y[i]; int idx; monster_race *r_ptr; /* Make sure to be on the map */ if (!map_in_bounds(x, y)) continue; /* Which monster is this in the kill_list. */ idx = map_loc(x, y)->kill; /* Get the monster */ r_ptr = &r_info[borg_kills[idx].r_idx]; /* Get rid of evil monsters */ if (FLAG(r_ptr, RF_EVIL)) continue; /* Calculate danger of who is left over */ borg_full_damage = TRUE; p2 += borg_danger_aux(c_x, c_y, 1, idx, TRUE); borg_full_damage = FALSE; } /* no negatives */ p2 = MAX(p2, 0); /* Try not to cast this against Serpent/Oberon */ if (borg_fighting_unique >= BORG_QUESTOR && ((bp_ptr->chp > 250 && bp_ptr->depth == 99) || (bp_ptr->chp > 350 && bp_ptr->depth == 100))) p2 = 9999; /* check to see if I am left better off */ if (p1 > p2 && p2 <= (borg_fighting_unique ? ((avoidance * 2) / 3) : (avoidance / 2)) && p1 > (avoidance * 2)) { /* Simulation */ return (p2); } /* I guess not */ return (0); } /* Cast the spell */ return (borg_spell_fail(REALM_LIFE, 2, 5, fail_allowed) || borg_spell_fail(REALM_TRUMP, 1, 7, fail_allowed)); } /* * Detect Inviso/Monsters * Used only if I am hit by an unseen guy. * Casts detect invis. */ static int borg_defend_aux_inviso(int p1) { int fail_allowed = 40; if (borg_simulate) { /* Can the borg see invis already? */ if (borg_see_inv || FLAG(bp_ptr, TR_SEE_INVIS)) return (0); /* not recent */ if (borg_t > need_see_inviso + 5) return (0); /* too dangerous to cast */ if (p1 > avoidance * 7) return (0); /* Do I have anything that will work? */ if (!borg_slot(TV_POTION, SV_POTION_DETECT_INVIS) && !borg_read_scroll_fail(SV_SCROLL_DETECT_INVIS) && !borg_equips_staff_fail(SV_STAFF_DETECT_INVIS) && !borg_activate_fail(BORG_ACT_DETECT_EVIL) && !borg_equips_staff_fail(SV_STAFF_DETECT_EVIL) && !borg_spell_okay_fail(REALM_LIFE, 1, 3, fail_allowed) && !borg_spell_okay_fail(REALM_ARCANE, 0, 2, fail_allowed)) return (0); /* No real value known, but lets cast it to find the bad guys. */ return (10); } /* smoke em if you got em */ /* short time */ if (borg_quaff_potion(SV_POTION_DETECT_INVIS)) { borg_see_inv = 18000; return (10); } /* long time */ if (borg_spell(REALM_LIFE, 1, 3) || borg_spell(REALM_ARCANE, 0, 2)) { borg_see_inv = 20000; return (10); } /* snap shot */ if (borg_activate(BORG_ACT_DETECT_EVIL) || borg_read_scroll(SV_SCROLL_DETECT_INVIS) || borg_use_staff(SV_STAFF_DETECT_INVIS) || borg_use_staff(SV_STAFF_DETECT_EVIL)) { borg_see_inv = 3000; /* hack, actually a snap shot, no ignition message */ return (10); } borg_oops("How did this happen?"); /* ah crap, I guess I wont be able to see them */ return (0); } /* * Use temp esp. * Used only if the borg is hit by an unseen guy. */ static int borg_defend_aux_esp(int p1) { int fail_allowed = 40; if (borg_simulate) { /* Has the borg esp already? */ if (borg_esp || FLAG(bp_ptr, TR_TELEPATHY)) return (0); /* not recent */ if (borg_t > need_see_inviso + 5) return (0); /* too dangerous to cast */ if (p1 > avoidance * 7) return (0); /* Do I have anything that will work? */ if (!borg_spell_okay_fail(REALM_SORCERY, 2, 4, fail_allowed) && !borg_spell_okay_fail(REALM_ARCANE, 3, 7, fail_allowed) && !borg_activate_fail(BORG_ACT_TELEPATHY) && !borg_mindcr_okay_fail(MIND_PRECOGNIT, 24, fail_allowed)) return (0); /* No real value known, but lets cast it to find the bad guys. */ return (10); } /* long time */ return (borg_activate(BORG_ACT_TELEPATHY) || borg_spell(REALM_SORCERY, 2, 4) || borg_spell(REALM_ARCANE, 3, 7) || borg_mindcr(MIND_PRECOGNIT, 24)); } /* * Light Beam to spot lurkers * Used only if I am hit by an unseen guy. * Lights up a hallway. */ static int borg_defend_aux_lbeam(int *key) { bool hallway = FALSE; int x = c_x; int y = c_y; if (borg_simulate) { /* no need */ if (bp_ptr->status.blind) return (0); /* Light Beam section to spot non seen guys */ /* not recent, dont bother */ if (borg_t > (need_see_inviso + 2)) return (0); /* Check to see if I am in a hallway */ /* Case 1a: north-south corridor */ if (borg_cave_floor_bold(y - 1, x) && borg_cave_floor_bold(y + 1, x) && !borg_cave_floor_bold(y, x - 1) && !borg_cave_floor_bold(y, x + 1) && !borg_cave_floor_bold(y + 1, x - 1) && !borg_cave_floor_bold(y + 1, x + 1) && !borg_cave_floor_bold(y - 1, x - 1) && !borg_cave_floor_bold(y - 1, x + 1)) { /* ok to light up */ hallway = TRUE; } /* Case 1b: east-west corridor */ if (borg_cave_floor_bold(y, x - 1) && borg_cave_floor_bold(y, x + 1) && !borg_cave_floor_bold(y - 1, x) && !borg_cave_floor_bold(y + 1, x) && !borg_cave_floor_bold(y + 1, x - 1) && !borg_cave_floor_bold(y + 1, x + 1) && !borg_cave_floor_bold(y - 1, x - 1) && !borg_cave_floor_bold(y - 1, x + 1)) { /* ok to light up */ hallway = TRUE; } /* Case 1aa: north-south doorway */ if (borg_cave_floor_bold(y - 1, x) && borg_cave_floor_bold(y + 1, x) && !borg_cave_floor_bold(y, x - 1) && !borg_cave_floor_bold(y, x + 1)) { /* ok to light up */ hallway = TRUE; } /* Case 1ba: east-west doorway */ if (borg_cave_floor_bold(y, x - 1) && borg_cave_floor_bold(y, x + 1) && !borg_cave_floor_bold(y - 1, x) && !borg_cave_floor_bold(y + 1, x)) { /* ok to light up */ hallway = TRUE; } /* not in a hallway */ if (!hallway) return (0); /* Make sure I am not in too much danger */ /* if (borg_simulate && p1 > avoidance * 3 / 4) return (0); */ /* test the beam function */ if (borg_lite_beam(borg_simulate, key)) return (10); /* Never mind */ return (0); } /* if in a hallway call the Light Beam routine */ return (borg_lite_beam(borg_simulate, key)); } /* * Phantasmal Servant. * If I dont have one, then get one. */ static int borg_defend_aux_servant(int p1) { int fail_allowed = 15; int i; int friendlies = 0; if (borg_simulate) { /* must have the ability */ if (!borg_spell_okay_fail(REALM_TRUMP, 1, 2, fail_allowed)) return (0); /* reset initial danger */ p1 = 1; /* Two passes to determine exact danger */ for (i = 0; i < borg_kills_nxt; i++) { borg_kill *kill; /* Monster */ kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Skip non Friendly */ if (!(kill->m_flags & MONST_PET)) continue; /* Count Friendly */ friendlies++; } /* check to see if I am left better off */ if (friendlies < 5 && p1 < (avoidance / 3)) { /* Simulation */ return (5); } /* I guess not */ return (0); } /* Cast the spell */ return (borg_spell(REALM_TRUMP, 1, 2)); } /* * Simulate/Apply the optimal result of using the given "type" of defence * p1 is the current danger level (passed in for effiency) */ static int borg_defend_aux(int what, int p1, int *key) { /* Analyze */ switch (what) { case BD_SPEED: { return (borg_defend_aux_speed(p1)); } case BD_PROT_FROM_EVIL: { return (borg_defend_aux_prot_evil(p1)); } case BD_BLESS: { return (borg_defend_aux_bless(p1)); } case BD_HERO_BERSERK: { return (borg_defend_aux_hero(p1)); } case BD_RESIST_FCE: { return (borg_defend_aux_resist_fce(p1)); } case BD_RESIST_FECAP: { return (borg_defend_aux_resist_fecap(p1)); } case BD_RESIST_F: { return (borg_defend_aux_resist_f(p1)); } case BD_RESIST_C: { return (borg_defend_aux_resist_c(p1)); } case BD_RESIST_A: { return (borg_defend_aux_resist_a(p1)); } case BD_RESIST_P: { return (borg_defend_aux_resist_p(p1)); } case BD_SHIELD: { return (borg_defend_aux_shield(p1)); } case BD_GOI: { return (borg_defend_aux_goi(p1)); } case BD_GOI_POT: { return (borg_defend_aux_goi_pot(p1)); } case BD_GLYPH: { return (borg_defend_aux_glyph(p1)); } case BD_WARDING: { return (borg_defend_aux_true_warding(p1)); } case BD_TELL_AWAY: { return (borg_defend_aux_tell_away(p1)); } case BD_CREATE_WALLS: { return (borg_defend_aux_create_walls(p1)); } case BD_MASS_GENOCIDE: { return (borg_defend_aux_mass_genocide(p1)); } case BD_GENOCIDE: { return (borg_defend_aux_genocide(p1, key)); } case BD_GENOCIDE_HOUNDS: { return (borg_defend_aux_genocide_hounds(p1)); } case BD_EARTHQUAKE: { return (borg_defend_aux_earthquake(p1)); } case BD_DESTRUCTION: { return (borg_defend_aux_destruction(p1)); } case BD_BANISHMENT: { return (borg_defend_aux_banishment(p1)); } case BD_DETECT_INVISO: { return (borg_defend_aux_inviso(p1)); } case BD_TELEPATHY: { return (borg_defend_aux_esp(p1)); } case BD_LIGHT_BEAM: { return (borg_defend_aux_lbeam(key)); } case BD_TRUMP_SERVANT: { return (borg_defend_aux_servant(p1)); } } borg_oops("# Trying invalid BD type. (%d)", what); return (0); } static bool borg_refresh_goi(void) { int p; map_block *mb_ptr = map_loc(c_x, c_y); /* if you have a globe up and it is about to drop, */ if (borg_goi && borg_goi < (borg_game_ratio * 2)) { /* check 'true' danger. This will make sure we do not */ /* refresh our GOI if no-one is around */ borg_attacking = TRUE; p = borg_danger(c_x, c_y, 1, TRUE); borg_attacking = FALSE; /* Is it a good idea to keep up the Globe? */ if ((p > mb_ptr->fear) || borg_fighting_unique) { /* If you can cast the spell */ if (borg_spell(REALM_SORCERY, 3, 7) || borg_spell(REALM_LIFE, 3, 7)) { /* Make a note */ borg_note("# refreshing GOI. borg_goi = %d", borg_goi); borg_note("# p_ptr->invuln = %d, (ratio = %d)", p_ptr->tim.invuln, borg_game_ratio); /* Declare success */ return (TRUE); } } } /* No Globe */ return (FALSE); } /* * prepare to attack... this is setup for a battle. */ bool borg_defend(int p1) { int n, b_n = 0; int key, b_key; int g, b_g = -1; /* Simulate */ borg_simulate = TRUE; /* refresh Globe of Invulnerablity (if you can) */ if (borg_refresh_goi()) return (TRUE); /* Make sure you have the monsters lined correctly */ borg_temp_fill(); /* Analyze the possible setup moves */ for (g = 0; g < BD_MAX; g++) { /* Simulate */ n = borg_defend_aux(g, p1, &key); /* Track "best" attack */ if (n <= b_n) continue; /* Track best */ b_g = g; b_n = n; b_key = key; } /* Nothing good */ if (b_n <= 0) { return (FALSE); } /* Note */ borg_note("# Performing defence type %d with value %d", b_g, b_n); /* Instantiate */ borg_simulate = FALSE; /* Instantiate */ (void)borg_defend_aux(b_g, p1, &b_key); /* Success */ return (TRUE); } /* * Perma spells. Some are cool to have on all the time, so long as their * mana cost is not too much. * There are several types of setup moves: * * Temporary speed * Protect From Evil * Prayer * Temp Resist (either all or just cold/fire?) * Shield * */ enum { BP_SPEED, BP_PROT_FROM_EVIL, BP_BLESS, BP_TELEPATHY, BP_SEE_INVIS, BP_RESIST_ALL, BP_RESIST_F, BP_RESIST_C, BP_RESIST_A, BP_RESIST_E, BP_RESIST_P, BP_RESIST_FCE, BP_GOI, BP_SHIELD, BP_HERO_BERSERK, BP_BERSERK_POTION, BP_GLYPH, BP_MAX }; /* * Bless/Prayer to prepare for battle */ static int borg_perma_aux_bless(void) { int fail_allowed = 5, cost; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* already blessed */ if (borg_bless) return (0); /* Is the bless activation available? */ if (borg_activate_fail(BORG_ACT_BLESS)) { cost = 0; } /* Is the life prayer spell available? */ else if (borg_spell_okay_fail(REALM_LIFE, 3, 1, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_LIFE, 3, 1); } /* Is the life bless spell available? */ else if (borg_spell_okay_fail(REALM_LIFE, 0, 2, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_LIFE, 0, 2); } else { /* No bless available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ /* bless is a low priority */ return (1); } /* do it! */ return (borg_spell(REALM_LIFE, 3, 1) || borg_spell(REALM_LIFE, 0, 2)); } /* all resists */ static int borg_perma_aux_resist(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (my_oppose_fire + my_oppose_acid + my_oppose_pois + my_oppose_elec + my_oppose_cold >= 3) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); if (borg_spell_okay_fail(REALM_NATURE, 2, 3, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_NATURE, 2, 3); } else if (borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 35, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_CHAR_ARMOUR].power; } else { /* No resistance available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (2); } /* do it! */ return (borg_spell(REALM_NATURE, 2, 3) || borg_mindcr(MIND_CHAR_ARMOUR, 35)); } /* resists--- Only bother if a Unique is on the level.*/ static int borg_perma_aux_resist_f(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (my_oppose_fire || !unique_on_level) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); /* No need if the borg is immune to fire */ if (FLAG(bp_ptr, TR_IM_FIRE)) return (0); /* Is the spell available? */ if (borg_spell_okay_fail(REALM_ARCANE, 1, 6, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_ARCANE, 1, 6); } else if (borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 20, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_CHAR_ARMOUR].power; } else { /* No resistance to fire available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / 20) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_spell(REALM_ARCANE, 1, 6) || borg_mindcr(MIND_CHAR_ARMOUR, 20)); } /* resists--- Only bother if a Unique is on the level.*/ static int borg_perma_aux_resist_c(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (my_oppose_cold || !unique_on_level) return (0); /* No need if the borg is immune to Cold */ if (FLAG(bp_ptr, TR_IM_COLD)) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); if (borg_spell_okay_fail(REALM_ARCANE, 1, 7, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_ARCANE, 1, 7); } else if (borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 25, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_CHAR_ARMOUR].power; } else { /* No resistance to cold available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / 20) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_spell(REALM_ARCANE, 1, 7) || borg_mindcr(MIND_CHAR_ARMOUR, 25)); } /* resists--- Only bother if a Unique is on the level.*/ static int borg_perma_aux_resist_a(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (my_oppose_acid || !unique_on_level) return (0); /* No need if the borg is immune to Acid */ if (FLAG(bp_ptr, TR_IM_ACID)) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); if (borg_spell_okay_fail(REALM_ARCANE, 2, 1, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_ARCANE, 2, 1); } else if (borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 15, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_CHAR_ARMOUR].power; } else { /* No resistance to acid available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / 20) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_spell(REALM_ARCANE, 2, 1) || borg_mindcr(MIND_CHAR_ARMOUR, 15)); } /* resists--- Only bother if a Unique is on the level.*/ static int borg_perma_aux_resist_e(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (my_oppose_elec || !unique_on_level) return (0); /* No need if the borg is immune to Acid */ if (FLAG(bp_ptr, TR_IM_ACID)) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); if (borg_spell_okay_fail(REALM_ARCANE, 2, 0, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_ARCANE, 2, 0); } else if (borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 30, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_CHAR_ARMOUR].power; } else { /* No resistance to elec available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / 20) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_spell(REALM_ARCANE, 2, 1) || borg_mindcr(MIND_CHAR_ARMOUR, 30)); } /* resists--- Only bother if a Unique is on the level.*/ static int borg_perma_aux_resist_p(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (my_oppose_pois || !unique_on_level) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); /* No need if the borg is immune to Poison */ if (FLAG(bp_ptr, TR_IM_POIS)) return (0); if (!borg_spell_okay_fail(REALM_DEATH, 0, 5, fail_allowed)) return (0); /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_DEATH, 0, 5); /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / 20) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_spell(REALM_DEATH, 0, 5)); } /* resist fire and cold for priests */ static int borg_perma_aux_resist_fce(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* cast if one drops and unique is near */ if (borg_fighting_unique && (my_oppose_fire || FLAG(bp_ptr, TR_IM_FIRE)) && (my_oppose_elec || FLAG(bp_ptr, TR_IM_ELEC)) && (my_oppose_cold || FLAG(bp_ptr, TR_IM_COLD))) return (0); /* cast if both drop and no unique is near */ if (!borg_fighting_unique && (my_oppose_fire || my_oppose_cold)) return (0); /* no need if immune */ if (FLAG(bp_ptr, TR_IM_FIRE) && FLAG(bp_ptr, TR_IM_COLD) && FLAG(bp_ptr, TR_IM_ELEC)) return (0); /* Not needed if GOI is on */ if (borg_goi) return (0); if (!borg_spell_okay_fail(REALM_NATURE, 0, 6, fail_allowed)) return (0); /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_NATURE, 0, 6); /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (2); } /* do it! */ return (borg_spell(REALM_NATURE, 0, 6)); } /* * Speed to prepare for battle */ static int borg_perma_aux_speed(void) { int fail_allowed = 7; int cost; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* already fast */ if (borg_speed) return (0); /* Is the sorcery speed spell available? */ if (borg_spell_okay_fail(REALM_SORCERY, 1, 5, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_SORCERY, 1, 5); } /* Is the death speed spell available? */ else if (borg_spell_okay_fail(REALM_DEATH, 2, 3, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_DEATH, 2, 3); } /* Is the mindcrafter speed spell available? */ else if (borg_mindcr_okay_fail(MIND_ADRENALINE, 23, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_ADRENALINE].power; } else { /* speed spell not available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (5); } /* do it! */ return (borg_spell(REALM_SORCERY, 1, 5) || borg_mindcr(MIND_ADRENALINE, 23) || borg_spell(REALM_DEATH, 2, 3)); } static int borg_perma_aux_goi(void) { int fail_allowed = 5; int cost; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* if already protected */ if (borg_shield || borg_goi) return (0); /* only when a unique is near */ if (!unique_on_level) return (0); /* Is the Life GoI spell available? */ if (borg_spell_okay_fail(REALM_LIFE, 3, 7, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_LIFE, 3, 7); } /* Is the Death GoI spell available? */ else if (borg_spell_okay_fail(REALM_SORCERY, 3, 7, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_SORCERY, 3, 7); } else { /* No GoI available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (3); } /* do it! */ return (borg_spell_fail(REALM_SORCERY, 3, 7, fail_allowed) || borg_spell_fail(REALM_LIFE, 3, 7, fail_allowed)); } /* * Telepathy */ static int borg_perma_aux_telepathy(void) { int fail_allowed = 5, cost; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* already telepathic */ if (borg_esp || (FLAG(bp_ptr, TR_TELEPATHY))) return (0); /* ESP from an artifact is for free */ if (borg_activate_fail(BORG_ACT_TELEPATHY)) { cost = 0; } /* Is the Arcane telepathy spell available? */ else if (borg_spell_okay_fail(REALM_ARCANE, 3, 7, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_ARCANE, 3, 7); } /* Is the Sorcery telepathy spell available? */ else if (borg_spell_okay_fail(REALM_SORCERY, 2, 4, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_SORCERY, 2, 4); } else if (borg_mindcr_okay_fail(MIND_PRECOGNIT, 24, fail_allowed) && bp_ptr->lev < 40) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_PRECOGNIT].power; } else { /* Telepathy, what is that? */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_activate(BORG_ACT_TELEPATHY) || borg_spell(REALM_ARCANE, 3, 7) || borg_spell(REALM_SORCERY, 2, 4) || borg_mindcr(MIND_PRECOGNIT, 24)); } /* See invisible */ static int borg_perma_aux_see_invis(void) { int fail_allowed = 5, cost; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* already seeing invisible */ if (borg_inviso || FLAG(bp_ptr, TR_SEE_INVIS)) return (0); /* Is the Arcane see invisible spell available? */ if (borg_spell_okay_fail(REALM_ARCANE, 2, 7, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_ARCANE, 2, 7); } /* Is the Life see invisible spell available? */ else if (borg_spell_okay_fail(REALM_LIFE, 1, 3, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_LIFE, 1, 3); } else { /* See invisible, what is that? */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (1); } /* do it! */ return (borg_spell(REALM_ARCANE, 2, 7) || borg_spell(REALM_LIFE, 1, 3)); } /* Shield for high AC */ static int borg_perma_aux_shield(void) { int fail_allowed = 5; int cost; if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* if already protected */ if (borg_shield || borg_goi) return (0); /* Is the nature shield spell available? */ if (borg_spell_okay_fail(REALM_NATURE, 2, 2, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_NATURE, 2, 2); } /* Is the mindcrafter shield spell available? */ else if (borg_mindcr_okay_fail(MIND_CHAR_ARMOUR, 13, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_CHAR_ARMOUR].power; } else { /* No shield available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (2); } /* do it! */ return (borg_spell(REALM_NATURE, 2, 2) || borg_mindcr(MIND_CHAR_ARMOUR, 13)); } static int borg_perma_aux_prot_evil(void) { int cost = 0; int fail_allowed = 5; if (borg_simulate) { /* if already protected */ if (borg_prot_from_evil || FLAG(bp_ptr, TR_SLAY_EVIL)) return (0); /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; if (!borg_spell_okay_fail(REALM_LIFE, 1, 5, fail_allowed) && !borg_activate_fail(BORG_ACT_PROT_EVIL)) return (0); /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_LIFE, 1, 5); /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* Simulation */ return (3); } /* do it! */ return (borg_activate(BORG_ACT_PROT_EVIL) || borg_spell_fail(REALM_LIFE, 1, 5, fail_allowed)); } /* * Hero/Berserk to prepare for battle */ static int borg_perma_aux_hero(void) { int fail_allowed = 5; int priority = 2, cost; /* Is this for real */ if (borg_simulate) { /* increase the threshold */ if (unique_on_level) fail_allowed = 10; if (borg_fighting_unique) fail_allowed = 15; /* already heroed */ if (borg_hero || borg_berserk) return (0); /* Is the hero or berserk activation spell available? */ if (borg_activate_fail(BORG_ACT_HEROISM) || borg_activate_fail(BORG_ACT_BERSERKER)) { cost = 0; } /* Can the borg cast the death berserk spell? */ else if (borg_spell_okay_fail(REALM_DEATH, 2, 0, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_DEATH, 2, 0); } /* Can the borg cast the mindcrafter adrenaline spell? */ else if (borg_mindcr_okay_fail(MIND_ADRENALINE, 23, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_minds[MIND_ADRENALINE].power; } /* Can the borg cast the life hero spell? */ else if (borg_spell_okay_fail(REALM_LIFE, 3, 0, fail_allowed)) { /* Obtain the cost of the spell */ cost = borg_spell_mana(REALM_LIFE, 3, 0); /* Reassign the importance */ priority = 1; } else { /* No hero or berserk available */ return (0); } /* If its cheap, go ahead */ if (cost >= bp_ptr->csp / (unique_on_level ? 7 : 10)) return (0); /* hero/berserk has a low priority */ return (priority); } /* Do it! (We know one of these will succeed) */ return (borg_activate(BORG_ACT_HEROISM) || borg_activate(BORG_ACT_BERSERKER) || borg_spell(REALM_DEATH, 2, 0) || borg_mindcr(MIND_ADRENALINE, 23) || borg_spell(REALM_LIFE, 3, 0)); } /* * Berserk to prepare for battle */ static int borg_perma_aux_berserk_potion(void) { if (borg_simulate) { /* Save the potions */ if (!borg_fighting_unique) return (0); /* already blessed */ if (borg_hero || borg_berserk)return (0); /* do I have any? */ if (!borg_slot(TV_POTION, SV_POTION_BERSERK_STRENGTH) || borg_mutation_check(MUT1_BERSERK, TRUE)) { /* No dice */ return (0); } /* Simulation */ return (2); } /* do it! */ return (borg_quaff_potion(SV_POTION_BERSERK_STRENGTH) || borg_mutation(MUT1_BERSERK)); } /* Glyph of Warding in a a-s corridor */ static int borg_perma_aux_glyph(void) { #if 0 int i, wall_y, wall_x, wall_count = 0, y, x; int fail_allowed = 20; map_block *mb_ptr = map_loc(c_x, c_y); /* check to make sure a summoner is near */ if (borg_kills_summoner == -1) return (0); /* make sure I have the spell */ if (!borg_spell_okay_fail(REALM_LIFE, 1, 7, fail_allowed)) return (0); /* He should not cast it while on an object. * I have addressed this inadequately in borg9.c when dealing with * messages. The message "the object resists" will delete the glyph * from the array. Then I set a broken door on that spot, the borg ignores * broken doors, so he won't loop. */ if ((mb_ptr->object) /*|| (mb_ptr->feat == FEAT_GLYPH) || ((mb_ptr->feat >= FEAT_TRAP_TRAPDOOR) && (mb_ptr->feat <= FEAT_TRAP_SLEEP)) */ || (mb_ptr->feat == FEAT_CLOSED) || (mb_ptr->feat == FEAT_LESS) || (mb_ptr->feat == FEAT_MORE) || (mb_ptr->feat == FEAT_OPEN) || (mb_ptr->feat == FEAT_BROKEN)) { return (0); } /* This spell is cast while he is digging and AS Corridor */ /* Get grid */ for (wall_x = -1; wall_x <= 1; wall_x++) { for (wall_y = -1; wall_y <= 1; wall_y++) { /* Acquire location */ x = wall_x + c_x; y = wall_y + c_y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* track adjacent walls */ if ( /* (mb_ptr->feat == FEAT_GLYPH) || */ (mb_ptr->feat == FEAT_PILLAR) || ((mb_ptr->feat >= FEAT_MAGMA) && (mb_ptr->feat <= FEAT_WALL_SOLID))) { wall_count++; } } } /* must be in a corridor */ if (wall_count < 7) return (0); /* Simulation */ if (borg_simulate) return (10); /* do it! */ if (borg_spell_fail(REALM_LIFE, 1, 7, fail_allowed) || borg_read_scroll(SV_SCROLL_RUNE_OF_PROTECTION)) { /* Check for an existing glyph */ for (i = 0; i < track_glyph_num; i++) { /* Stop if we already new about this glyph */ if ((track_glyph_x[i] == c_x) && (track_glyph_y[i] == c_y)) return (2); } /* Track the newly discovered glyph */ if ((i == track_glyph_num) && (track_glyph_size)) { borg_note("# Noting the creation of a corridor glyph."); track_glyph_num++; track_glyph_x[i] = c_x; track_glyph_y[i] = c_y; } return (2); } #endif /* 0 */ /* default to can't do it. */ return (0); } /* * Simulate/Apply the optimal result of using the given "type" of set-up */ static int borg_perma_aux(int what) { /* Analyze */ switch (what) { case BP_SPEED: { return (borg_perma_aux_speed()); } case BP_TELEPATHY: { return (borg_perma_aux_telepathy()); } case BP_SEE_INVIS: { return (borg_perma_aux_see_invis()); } case BP_PROT_FROM_EVIL: { return (borg_perma_aux_prot_evil()); } case BP_RESIST_ALL: { return (borg_perma_aux_resist()); } case BP_RESIST_F: { return (borg_perma_aux_resist_f()); } case BP_RESIST_C: { return (borg_perma_aux_resist_c()); } case BP_RESIST_A: { return (borg_perma_aux_resist_a()); } case BP_RESIST_E: { return (borg_perma_aux_resist_e()); } case BP_RESIST_P: { return (borg_perma_aux_resist_p()); } case BP_RESIST_FCE: { return (borg_perma_aux_resist_fce()); } case BP_BLESS: { return (borg_perma_aux_bless()); } case BP_HERO_BERSERK: { return (borg_perma_aux_hero()); } case BP_BERSERK_POTION: { return (borg_perma_aux_berserk_potion()); } case BP_GOI: { return (borg_perma_aux_goi()); } case BP_SHIELD: { return (borg_perma_aux_shield()); } case BP_GLYPH: { return (borg_perma_aux_glyph()); } } return (0); } /* * prepare to attack... this is setup for a battle. */ bool borg_perma_spell() { int n, b_n = 0; int g, b_g = -1; /* Simulate */ borg_simulate = TRUE; /* Not in town */ if (!bp_ptr->depth) return (FALSE); /* No perma-spells until clevel 30 or the borg has to rest too much */ if (bp_ptr->lev < 30) return (FALSE); /* Analyze the possible setup moves */ for (g = 0; g < BP_MAX; g++) { /* Simulate */ n = borg_perma_aux(g); /* Track "best" move */ if (n <= b_n) continue; /* Track best */ b_g = g; b_n = n; } /* Nothing good */ if (b_n <= 0) return (FALSE); /* Note */ borg_note("# Performing perma-spell type %d with value %d", b_g, b_n); /* Instantiate */ borg_simulate = FALSE; /* Instantiate */ (void)borg_perma_aux(b_g); /* Success */ return (TRUE); } /* * check to make sure there are no monsters around * that should prevent resting also make sure the ground * is safe for us. */ bool borg_check_rest(void) { int i; if (FLAG(bp_ptr, TR_HURT_LITE) && !FLAG(bp_ptr, TR_RES_LITE)) { list_item *l_ptr = look_up_equip_slot(EQUIP_LITE); /* Do not rest with an artifact lite */ if (l_ptr && KN_FLAG(l_ptr, TR_INSTA_ART)) return (FALSE); /* Do not rest in Sunlight */ if (!bp_ptr->depth && bp_ptr->hour >= 5 && bp_ptr->hour <= 18) return (FALSE); } /* Now check the ground to see if safe. */ if (!borg_on_safe_feat(map_loc(c_x, c_y)->feat)) return (FALSE); /* Examine all the monsters */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; int x9 = kill->x; int y9 = kill->y; int ax, ay, d; int p = 0; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Distance components */ ax = (x9 > c_x) ? (x9 - c_x) : (c_x - x9); ay = (y9 > c_y) ? (y9 - c_y) : (c_y - y9); /* Distance */ d = MAX(ax, ay); /* Minimal distance */ if (d > 16) continue; /* if too close, don't rest */ if (d < 2) return (FALSE); /* If too close, don't rest */ if (d < 3 && !(FLAG(r_ptr, RF_NEVER_MOVE))) return (FALSE); /* one call for dangers */ borg_full_damage = TRUE; p = borg_danger_aux(x9, y9, 1, i, TRUE); borg_full_damage = FALSE; /* Real scary guys pretty close */ if (d < 5 && (p > avoidance / 3)) return (FALSE); /* should check LOS... monster to me */ if (borg_los(x9, y9, c_x, c_y)) return FALSE; /* should check LOS... me to monster */ if (borg_los(c_x, c_y, x9, y9)) return FALSE; /* Perhaps borg should check and see if the previous grid was los */ /* if absorbs mana, not safe */ if ((FLAG(r_ptr, RF_DRAIN_MANA)) && (bp_ptr->msp > 1)) return FALSE; /* if it walks through walls, not safe */ if (FLAG(r_ptr, RF_PASS_WALL)) return FALSE; if (FLAG(r_ptr, RF_KILL_WALL)) return FALSE; } /* Otherwise ok */ return TRUE; } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg1.c0000644000000000000000000004733410250356275013517 0ustar rootroot/* File: borg1.c */ /* Purpose: Low level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" /* * if a borg has no light and no money to buy fuel, sell some * in order to raise cash. * Inscription problem with long art names * * Mimic Doors. * * getting to a wilderness grid that has a town. * using waypoints? * Using dimdoor to get accross lava and water. */ /* Things that would be nice for the borg to know but would * require a bit of programming and may not be worth the time. * * - Aquatic animals stay in aquatic realms. * - Wraithform * - Summoning */ /* * This file contains various low level variables and routines. */ /* Dynamic borg stuff */ borg_player *bp_ptr; /* * Some variables */ bool borg_active; /* Actually active */ bool borg_cancel; /* Being cancelled */ bool borg_dont_react = FALSE; int successful_target = BORG_TARGET; /* * Various silly flags */ bool borg_stop_king = TRUE; /* The borg stops when he wins */ bool borg_cheat_death = FALSE; /* Is there life after death? */ bool borg_flag_dump = FALSE; /* Save savefile at each death */ bool borg_flag_save = FALSE; /* Save savefile at each level */ bool borg_save = FALSE; /* Do a save next level */ /* * Use a simple internal random number generator */ u32b borg_rand_local; /* Save personal setting */ /* * Hack -- Time variables */ s32b borg_t = 0L; /* Current "time" */ s32b borg_temp_fill_valid = 0L; /* When were the monster arrays filled */ s32b need_see_inviso = 0; /* cast this when required */ s32b borg_see_inv = 0; bool vault_on_level; /* Borg will search for a vault */ bool unique_on_level; int unique_r_idx; bool scaryguy_on_level; /* flee from certain guys */ bool breeder_level = FALSE; /* Borg will shut door */ s16b old_depth = 128; s16b borg_no_retreat = 0; /* * Hack -- Other time variables */ s32b when_call_lite = 0; /* When we last did call light */ s32b when_wizard_lite = 0; /* When we last did wizard light */ s32b when_detect_traps = 0; /* When we last detected traps */ s32b when_detect_doors = 0; /* When we last detected doors */ s32b when_detect_walls = 0; /* When we last detected walls */ s32b when_detect_evil = 0; /* When we last detected monsters or evil */ bool my_need_alter; /* incase i hit a wall or door */ bool my_no_alter; /* */ /* * Some information */ s16b goal; /* Goal type */ bool goal_rising; /* Currently returning to town */ bool goal_leaving; /* Currently leaving the level */ bool goal_fleeing; /* Currently fleeing the level */ bool goal_ignoring; /* Currently ignoring monsters */ int goal_recalling; /* Currently waiting for recall, guessing the turns left */ bool goal_less; /* return to, but dont use, the next up stairs */ s16b borg_times_twitch; /* how often twitchy on this level */ s16b borg_escapes; /* how often teleported on this level */ bool stair_less; /* Use the next "up" staircase */ bool stair_more; /* Use the next "down" staircase */ s32b borg_began; /* When this level began */ s32b borg_time_town; /* how long it has been since I was in town */ s16b avoidance = 0; /* Current danger thresh-hold */ bool borg_failure; /* Notice failure */ bool borg_attacking; /* Simulation flag */ bool borg_offsetting; /* offset ball attacks */ bool borg_completed; /* Completed the level */ bool borg_needs_searching; /* borg will search with each step */ bool borg_full_damage; /* make danger = full possible damage. */ /* defence flags */ bool borg_prot_from_evil; bool borg_speed; bool borg_bless; bool borg_hero; bool borg_berserk; bool my_oppose_fire; bool my_oppose_cold; bool my_oppose_acid; bool my_oppose_pois; bool my_oppose_elec; s16b borg_wraith_form; s16b borg_goi; s16b borg_inviso; bool borg_esp; s16b borg_game_ratio; /* the ratio of borg time to game time */ bool borg_shield; bool borg_on_glyph; bool borg_create_door; bool borg_open_door_failed; bool borg_close_door_failed; bool borg_sleep_spell; bool borg_sleep_spell_ii; bool borg_slow_spell; bool borg_confuse_spell; bool borg_fear_mon_spell; /* Which shop or dungeon to visit next */ s16b goal_town = -1; s16b goal_shop = -1; s16b goal_dungeon = -1; s16b goal_explore_x = -1; s16b goal_explore_y = -1; /* Current shop/dungeon index */ s16b town_num = -1; s16b shop_num = -1; s16b dungeon_num = -1; /* List of known shops and dungeons */ borg_town *borg_towns; borg_shop *borg_shops; borg_dungeon *borg_dungeons; /* Number of allocated towns */ s16b borg_town_num = 0; s16b borg_town_size = 20; /* Number of allocated shops */ s16b borg_shop_num = 0; s16b borg_shop_size = 16; /* Number of allocated dungeons */ s16b borg_dungeon_num = 0; s16b borg_dungeon_size = 16; /* * Location variables */ int c_x; /* Current location (X) */ int c_y; /* Current location (Y) */ int g_x; /* Goal location (X) */ int g_y; /* Goal location (Y) */ s32b g_power; /* Current power value */ s32b g_power_home; /* Current power_home value */ int dim_door_y; /* Safe landing zone for DDoor */ int dim_door_x; /* BIG HACK! Assume only 50 cursed artifacts */ int bad_obj_x[50]; /* Dropped cursed artifact at location (X) */ int bad_obj_y[50]; /* Dropped cursed artifact at location (Y) */ int bad_obj_n = -1; /* * Some estimated state variables */ s16b my_stat_max[6]; /* Current "maximal" stat values */ s16b my_stat_cur[6]; /* Current "natural" stat values */ s16b my_stat_ind[6]; /* Current "additions" to stat values */ bool my_need_stat_check[6]; /* do I need to check my stats? */ s16b my_stat_add[6]; /* additions to stats This will allow upgrading of */ /* equipment to allow a ring of int +4 to be traded */ /* for a ring of int +6 even if maximized to allow a */ /* later swap to be better. */ s16b home_stat_add[6]; int my_ammo_tval; /* Ammo -- "tval" */ s16b my_ammo_power; /* Average power */ s16b my_ammo_range; /* Shooting range */ /* * Various "amounts" (for the player) */ s16b amt_food_scroll; s16b amt_food_lowcal; s16b amt_torch; s16b amt_lantern; s16b amt_flask; s16b amt_slow_poison; s16b amt_pot_curing; s16b amt_star_heal; s16b amt_life; s16b amt_rod_heal; s16b amt_book[8][4]; /* [realm][sval] */ s16b amt_add_stat[6]; s16b amt_fix_stat[7]; /* #7 is to fix all stats */ s16b amt_fix_exp; s16b amt_enchant_to_a; s16b amt_enchant_to_d; s16b amt_enchant_to_h; s16b amt_brand_weapon; /* apw brand bolts */ s16b amt_digger; /* * Hack -- extra state variables */ int borg_feeling = 0; /* Current level "feeling" */ /* * State variables extracted from the screen */ s32b borg_gold; /* Current gold */ int borg_stat[6]; /* Current stat values */ int borg_book[8][4]; /* Current book slots, [realm][sval] */ /* * Constant state variables */ int borg_race; /* Player race */ int borg_class; /* Player class */ /* * Hack -- access the class/race records */ player_race *rb_ptr; /* Player race info */ player_class *cb_ptr; /* Player class info */ player_magic *pmb_ptr; /* Player magic info */ /* * Number of turns to step for (zero means forever) */ u16b borg_step = 0; /* Step count (if any) */ /* * Status message search string */ char borg_match[128] = ""; /* * Log file */ FILE *borg_fff = NULL; /* Log file */ /* * Track "stairs up" */ s16b track_less_num = 0; s16b track_less_size = 16; int *track_less_x; int *track_less_y; /* * Track "stairs down" */ s16b track_more_num = 0; s16b track_more_size = 16; int *track_more_x; int *track_more_y; /* * Track glyphs */ s16b track_glyph_num = 0; s16b track_glyph_size = 256; int *track_glyph_x; int *track_glyph_y; /* * Track Steps */ s16b track_step_num = 0; s16b track_step_size = 256; int *track_step_x; int *track_step_y; /* * Track closed doors */ s16b track_door_num = 0; s16b track_door_size = 256; int *track_door_x; int *track_door_y; /* * The object list. This list is used to "track" objects. */ s16b borg_takes_cnt = 0; s16b borg_takes_nxt = 1; borg_take *borg_takes; /* * The monster list. This list is used to "track" monsters. */ s16b borg_kills_cnt = 0; s16b borg_kills_nxt = 1; borg_kill *borg_kills; /* * Maintain a set of grids marked as "BORG_VIEW" */ s16b borg_view_n = 0; s16b *borg_view_x; s16b *borg_view_y; /* * Maintain a temporary set of grids */ /* For any monster within MAX_RANGE */ s16b borg_temp_n = 0; s16b *borg_temp_x; s16b *borg_temp_y; /* For the monsters immediately surrounding the borg */ s16b borg_next_n = 0; s16b *borg_next_x; s16b *borg_next_y; /* For the monsters that can be hit by a bolt */ s16b borg_bolt_n = 0; s16b *borg_bolt_x; s16b *borg_bolt_y; /* For the monsters that can be hit by a beam, basically any monster in LOS */ s16b borg_beam_n = 0; s16b *borg_beam_x; s16b *borg_beam_y; /* For the monsters that can be hit by a ball with radius > 1 */ s16b borg_ball_n = 0; s16b *borg_ball_x; s16b *borg_ball_y; /* * Maintain a circular queue of grids */ s16b borg_flow_n = 0; s16b *borg_flow_x; s16b *borg_flow_y; /* * Hack -- use "flow" array as a queue */ int flow_head = 0; int flow_tail = 0; /* * Strategy flags -- examine the world */ bool borg_do_frame = TRUE; /* Acquire "frame" info */ bool borg_do_spell = TRUE; /* Acquire "spell" info */ /* * Strategy flags -- run certain functions */ bool borg_do_destroy = FALSE; /* am I fighting a unique? */ int borg_fighting_unique; bool borg_fighting_evil_unique; /* * Query the "attr/char" at a given location on the screen * We return "zero" if the given location was legal * * XXX XXX XXX We assume the given location is legal */ errr borg_what_char(int x, int y, byte *a, char *c) { /* Direct access XXX XXX XXX */ (*a) = (Term->scr->a[y][x]); (*c) = (Term->scr->c[y][x]); /* Success */ return (0); } /* * Query the "attr/chars" at a given location on the screen * * Note that "a" points to a single "attr", and "s" to an array * of "chars", into which the attribute and text at the given * location are stored. * * We will not grab more than "ABS(n)" characters for the string. * If "n" is "positive", we will grab exactly "n" chars, or fail. * If "n" is "negative", we will grab until the attribute changes. * * We automatically convert all "blanks" and "invisible text" into * spaces, and we ignore the attribute of such characters. * * We do not strip final spaces, so this function will very often * read characters all the way to the end of the line. * * We succeed only if a string of some form existed, and all of * the non-space characters in the string have the same attribute, * and the string was long enough. * * XXX XXX XXX We assume the given location is legal */ errr borg_what_text(int x, int y, int n, byte *a, char *s) { int i; byte t_a; char t_c; byte *aa; char *cc; /* Current attribute */ byte d_a = 0; /* Max length to scan for */ int m = ABS(n); /* Hack -- Do not run off the screen */ if (x + m > 80) m = 80 - x; /* Direct access XXX XXX XXX */ aa = &(Term->scr->a[y][x]); cc = &(Term->scr->c[y][x]); /* Grab the string */ for (i = 0; i < m; i++) { /* Access */ t_a = *aa++; t_c = *cc++; /* Handle spaces */ if ((t_c == ' ') || !t_a) { /* Save space */ s[i] = ' '; } /* Handle real text */ else { /* Attribute ready */ if (d_a) { /* Verify the "attribute" (or stop) */ if (t_a != d_a) break; } /* Acquire attribute */ else { /* Save it */ d_a = t_a; } /* Save char */ s[i] = t_c; } } /* Terminate the string */ s[i] = '\0'; /* Save the attribute */ (*a) = d_a; /* Too short */ if ((n > 0) && (i != n)) return (1); /* Success */ return (0); } /* Compare what you get from borg_what_text immediately */ bool borg_term_text_comp(int x, int y, cptr what) { byte t_a; int wid, hgt; int len = strlen(what); char buf[120]; /* Get size */ Term_get_size(&wid, &hgt); /* That's left or right of the term */ if (x < 0 || x + len > wid) return (FALSE); /* That's higher or lower of the term */ if (y < 0 || y >= hgt) return (FALSE); if (0 == borg_what_text(x, y, strlen(what), &t_a, buf) && streq(buf, what)) return (TRUE); /* No match */ return (FALSE); } /* * Memorize a message, Log it, Search it, and Display it in pieces */ static void borg_note_aux(cptr what) { int j, n, i, k; int w, h, x, y; term *old = Term; /* Memorize it */ message_add(what, MSG_GENERIC); /* Log the message */ if (borg_fff) froff(borg_fff, "%s\n", what); /* Mega-Hack -- Check against the search string */ if (borg_match[0] && strstr(what, borg_match)) { /* Tell the user why you quit */ borg_oops("Search string was matched"); } /* Scan windows */ for (j = 0; j < 8; j++) { if (!angband_term[j]) continue; /* Check flag */ if (!(window_flag[j] & PW_BORG_1)) continue; /* Activate */ Term_activate(angband_term[j]); /* Access size */ Term_get_size(&w, &h); /* Access cursor */ Term_locate(&x, &y); /* Erase current line */ clear_row(y); /* Total length */ n = strlen(what); /* Too long */ if (n > w - 2) { char buf[1024]; /* Split */ while (n > w - 2) { /* Default */ k = w - 2; /* Find a split point */ for (i = w / 2; i < w - 2; i++) { /* Pre-emptive split point */ if (isspace(what[i])) k = i; } /* Copy over the split message */ for (i = 0; i < k; i++) { /* Copy */ buf[i] = what[i]; } /* Indicate split */ buf[i++] = '\\'; /* Terminate */ buf[i] = '\0'; /* Show message */ roff(buf); /* Advance (wrap) */ if (++y >= h) y = 0; /* Erase next line */ clear_row(y); /* Advance */ what += k; /* Reduce */ n -= k; } /* Show message tail */ roff(what); /* Advance (wrap) */ if (++y >= h) y = 0; /* Erase next line */ clear_row(y); } /* Normal */ else { /* Show message */ roff(what); /* Advance (wrap) */ if (++y >= h) y = 0; /* Erase next line */ clear_row(y); } /* Flush output */ Term_fresh(); /* Use correct window */ Term_activate(old); } } /* Do a message with formatting */ void borg_note(cptr fmt, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Display */ borg_note_aux(buf); } /* * Abort the Borg, noting the reason */ static void borg_oops_aux(cptr what) { char buf[1024]; /* Stop processing */ borg_active = FALSE; /* Format the string */ (void)strnfmt(buf, 1024, "# Aborting (%s).", what); /* Give a warning */ borg_note(buf); /* Forget borg keys */ borg_flush(); } /* Abort the borg, give a text with formatting */ void borg_oops(cptr fmt, ...) { va_list vp; char buf[1024]; /* Begin the Varargs Stuff */ va_start(vp, fmt); /* Format the args, save the length */ (void)vstrnfmt(buf, 1024, fmt, &vp); /* End the Varargs Stuff */ va_end(vp); /* Display */ borg_oops_aux(buf); } /* * A Queue of keypresses to be sent */ static char *borg_key_queue; static s16b borg_key_head; static s16b borg_key_tail; /* * Add a keypress to the "queue" (fake event) */ errr borg_keypress(char k) { char buf[10]; /* Hack -- Refuse to enqueue "nul" */ if (!k) return (-1); /* Format the string */ (void)strnfmt(buf, 10, "& Key <%c>", k); /* Hack -- note the keypress */ if (borg_fff) froff(borg_fff, "%s\n", buf); /* Store the char, advance the queue */ borg_key_queue[borg_key_head++] = k; /* Circular queue, handle wrap */ if (borg_key_head == KEY_SIZE) borg_key_head = 0; /* Hack -- Catch overflow (forget oldest) */ if (borg_key_head == borg_key_tail) borg_oops("overflow"); /* Hack -- Overflow may induce circular queue */ if (borg_key_tail == KEY_SIZE) borg_key_tail = 0; /* Success */ return (0); } /* * Add a keypress to the "queue" (fake event) */ errr borg_keypresses(cptr str) { cptr s; /* Enqueue them */ for (s = str; *s; s++) borg_keypress(*s); /* Success */ return (0); } /* * Get the next Borg keypress */ char borg_inkey(bool take) { int i; /* Nothing ready */ if (borg_key_head == borg_key_tail) return (0); /* Extract the keypress */ i = borg_key_queue[borg_key_tail]; /* Do not advance */ if (!take) return (i); /* Advance the queue */ borg_key_tail++; /* Circular queue requires wrap-around */ if (borg_key_tail == KEY_SIZE) borg_key_tail = 0; /* Return the key */ return (i); } /* * Get the next Borg keypress */ void borg_flush(void) { /* Simply forget old keys */ borg_key_tail = borg_key_head; } /* * Hack -- take a note later */ bool borg_tell(cptr what) { cptr s; /* Hack -- self note */ borg_keypress(':'); for (s = what; *s; s++) borg_keypress(*s); borg_keypress('\n'); /* Success */ return (TRUE); } /* * Attempt to change the borg's name */ bool borg_change_name(cptr str) { cptr s; /* Cancel everything */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); /* Character description */ borg_keypress('C'); /* Change the name */ borg_keypress('c'); /* Enter the new name */ for (s = str; *s; s++) borg_keypress(*s); /* End the name */ borg_keypress('\r'); /* Cancel everything */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); /* Success */ return (TRUE); } /* * Attempt to dump a character description file */ bool borg_dump_character(cptr str) { cptr s; /* Cancel everything */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); /* Character description */ borg_keypress('C'); /* Dump character file */ borg_keypress('f'); /* Enter the new name */ for (s = str; *s; s++) borg_keypress(*s); /* End the file name */ borg_keypress('\r'); /* Cancel everything */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); /* Success */ return (TRUE); } /* * Attempt to save the game */ bool borg_save_game(void) { /* Cancel everything */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); /* Save the game */ borg_keypress('^'); borg_keypress('S'); /* Cancel everything */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); /* Success */ return (TRUE); } /* * Initialize this file */ void borg_init_1(void) { /* Allocate the "keypress queue" */ C_MAKE(borg_key_queue, KEY_SIZE, char); /* Prepare a local random number seed */ if (!borg_rand_local) borg_rand_local = randint0(0x10000000); /*** Special "tracking" arrays ***/ /* Track "up" stairs */ C_MAKE(track_less_x, track_less_size, int); C_MAKE(track_less_y, track_less_size, int); /* Track "down" stairs */ C_MAKE(track_more_x, track_more_size, int); C_MAKE(track_more_y, track_more_size, int); /* Track glyphs */ C_MAKE(track_glyph_x, track_glyph_size, int); C_MAKE(track_glyph_y, track_glyph_size, int); /* Track Steps */ C_MAKE(track_step_x, track_step_size, int); C_MAKE(track_step_y, track_step_size, int); /* Track closed doors */ C_MAKE(track_door_x, track_door_size, int); C_MAKE(track_door_y, track_door_size, int); /* Array of objects */ C_MAKE(borg_takes, BORG_TAKES_MAX, borg_take); /* Array of monsters */ C_MAKE(borg_kills, BORG_KILLS_MAX, borg_kill); /* Array of views */ C_MAKE(borg_view_x, AUTO_VIEW_MAX, s16b); C_MAKE(borg_view_y, AUTO_VIEW_MAX, s16b); /* Array of temporary coordinates */ C_MAKE(borg_temp_x, BORG_TEMP_MAX, s16b); C_MAKE(borg_temp_y, BORG_TEMP_MAX, s16b); /* Array of temporary coordinates */ C_MAKE(borg_next_x, BORG_NEXT_MAX, s16b); C_MAKE(borg_next_y, BORG_NEXT_MAX, s16b); /* Array of temporary coordinates */ C_MAKE(borg_bolt_x, BORG_TEMP_MAX, s16b); C_MAKE(borg_bolt_y, BORG_TEMP_MAX, s16b); /* Array of temporary coordinates */ C_MAKE(borg_beam_x, BORG_TEMP_MAX, s16b); C_MAKE(borg_beam_y, BORG_TEMP_MAX, s16b); /* Array of temporary coordinates */ C_MAKE(borg_ball_x, BORG_TEMP_MAX, s16b); C_MAKE(borg_ball_y, BORG_TEMP_MAX, s16b); /* Array of temporary coordinates */ C_MAKE(borg_flow_x, BORG_FLOW_MAX, s16b); C_MAKE(borg_flow_y, BORG_FLOW_MAX, s16b); /* Struct for the player information */ MAKE(bp_ptr, borg_player); } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg2.c0000644000000000000000000031140410250356275013510 0ustar rootroot/* File: zborg2.c */ /* Purpose: Low level dungeon mapping skills -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" /* * This file helps the Borg understand mapping the dungeon. * * Currently, this includes general routines involving dungeon grids, * including calculating "flow" values from place to place, determining * "line of sight", plus "field of view" and "torch-lit grids", setting * the target to a given location, and extracting the optimal direction * for "movement" from place to place. * * Note that the dungeon is assumed smaller than 256 by 256. * * This file also supplies the (compiled out) support for "automatic * room extraction". This code will automatically group regions of * the dungeon into rooms, and do the "flow" navigation on those rooms * instead of on grids. Often, this takes less space, and is faster, * howver, it is more complicated, and does not allow "specialized" * flow calculations that penalize grids by variable amounts. */ /* * Maximum number of slopes in a single octant */ #define VINFO_MAX_SLOPES 135 /* * Table of data used to calculate projections / los / shots. */ static project_type *project_data[VINFO_MAX_SLOPES]; /* Number of squares per slope */ static int slope_count[VINFO_MAX_SLOPES]; /* The min and max slopes for each square in sight */ static int p_slope_min[MAX_SIGHT + 1][MAX_SIGHT + 1]; static int p_slope_max[MAX_SIGHT + 1][MAX_SIGHT + 1]; /* * Maximum number of grids in a single octant */ #define VINFO_MAX_GRIDS 175 /* * Mask of bits used in a single octant */ #define VINFO_BITS_4 0x0000007FL #define VINFO_BITS_3 0xFFFFFFFFL #define VINFO_BITS_2 0xFFFFFFFFL #define VINFO_BITS_1 0xFFFFFFFFL #define VINFO_BITS_0 0xFFFFFFFFL /* * Forward declare */ typedef struct vinfo_type vinfo_type; /* * The 'vinfo_type' structure */ struct vinfo_type { s16b grid_x[8]; s16b grid_y[8]; u32b bits[5]; vinfo_type *next_0; vinfo_type *next_1; byte y; byte x; byte d; byte r; }; /* * The array of "vinfo" objects, initialized by "vinfo_init()" */ static vinfo_type vinfo[VINFO_MAX_GRIDS]; /* * Slope scale factor */ #define SCALE 100000L /* * Forward declare */ typedef struct vinfo_hack vinfo_hack; /* * Temporary data used by "vinfo_init()" * * - Number of slopes * * - Slope values * * - Slope min and max for each square */ struct vinfo_hack { int num_slopes; s32b slopes[VINFO_MAX_SLOPES]; s32b slopes_min[MAX_SIGHT + 1][MAX_SIGHT + 1]; s32b slopes_max[MAX_SIGHT + 1][MAX_SIGHT + 1]; }; /* * Sorting hook -- comp function -- array of s32b (see below) * * We use "u" to point to an array of s32b. */ static bool ang_sort_comp_hook_s32b(const vptr u, const vptr v, int a, int b) { s32b *x = (s32b *)(u); /* Hack - ignore v */ (void)v; return (x[a] <= x[b]); } /* * Sorting hook -- swap function -- array of s32b (see below) * * We use "u" to point to an array of s32b. */ static void ang_sort_swap_hook_s32b(const vptr u, const vptr v, int a, int b) { s32b *x = (s32b *)(u); s32b temp; /* Hack - ignore v */ (void)v; /* Swap */ temp = x[a]; x[a] = x[b]; x[b] = temp; } /* * Save a slope */ static void vinfo_init_aux(vinfo_hack *hack, int x, int y, s32b m) { int i; /* Handle "legal" slopes */ if ((m > 0) && (m <= SCALE)) { /* Look for that slope */ for (i = 0; i < hack->num_slopes; i++) { if (hack->slopes[i] == m) break; } /* New slope */ if (i == hack->num_slopes) { /* Paranoia */ if (hack->num_slopes >= VINFO_MAX_SLOPES) { quit_fmt("Too many slopes (%d)!", VINFO_MAX_SLOPES); } /* Save the slope, and advance */ hack->slopes[hack->num_slopes++] = m; } } /* Track slope range */ if (hack->slopes_min[y][x] > m) hack->slopes_min[y][x] = m; if (hack->slopes_max[y][x] < m) hack->slopes_max[y][x] = m; } /* * Initialize the "vinfo" and "project_data" arrays * * Full Octagon (radius 20), Grids=1149 * * Quadrant (south east), Grids=308, Slopes=251 * * Octant (east then south), Grids=161, Slopes=126 * * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES * have the correct values, which can be derived by setting them to * a number which is too high, running this function, and using the * error messages to obtain the correct values. */ static errr borg_vinfo_init(void) { int i, j; int y, x; s32b m; vinfo_hack *hack; int num_grids = 0; int queue_head = 0; int queue_tail = 0; vinfo_type *queue[VINFO_MAX_GRIDS * 2]; /* Make hack */ MAKE(hack, vinfo_hack); /* Analyze grids */ for (y = 0; y <= MAX_SIGHT; ++y) { for (x = y; x <= MAX_SIGHT; ++x) { /* Skip grids which are out of sight range */ if (distance(0, 0, x, y) > MAX_SIGHT) continue; /* Default slope range */ hack->slopes_min[y][x] = 999999999; hack->slopes_max[y][x] = 0; /* Clear the p_slope_min and max arrays */ p_slope_min[x][y] = VINFO_MAX_SLOPES; p_slope_max[x][y] = 0; p_slope_min[y][x] = VINFO_MAX_SLOPES; p_slope_max[y][x] = 0; /* Paranoia */ if (num_grids >= VINFO_MAX_GRIDS) { quit_fmt("Too many grids (%d >= %d)!", num_grids, VINFO_MAX_GRIDS); } /* Count grids */ num_grids++; /* Slope to the top right corner */ m = SCALE * (1000L * y - 500) / (1000L * x + 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); /* Slope to top left corner */ m = SCALE * (1000L * y - 500) / (1000L * x - 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); /* Slope to bottom right corner */ m = SCALE * (1000L * y + 500) / (1000L * x + 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); /* Slope to bottom left corner */ m = SCALE * (1000L * y + 500) / (1000L * x - 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, x, y, m); } } /* Enforce maximal efficiency */ if (num_grids < VINFO_MAX_GRIDS) { quit_fmt("Too few grids (%d < %d)!", num_grids, VINFO_MAX_GRIDS); } /* Enforce maximal efficiency */ if (hack->num_slopes < VINFO_MAX_SLOPES) { quit_fmt("Too few slopes (%d < %d)!", hack->num_slopes, VINFO_MAX_SLOPES); } /* Sort slopes numerically */ ang_sort_comp = ang_sort_comp_hook_s32b; /* Sort slopes numerically */ ang_sort_swap = ang_sort_swap_hook_s32b; /* Sort the (unique) slopes */ ang_sort(hack->slopes, NULL, hack->num_slopes); /* Clear the counters for each slope */ (void)C_WIPE(slope_count, VINFO_MAX_SLOPES, int); /* Enqueue player grid */ queue[queue_tail++] = &vinfo[0]; /* Process queue */ while (queue_head < queue_tail) { int e; vinfo_type *p; /* Index */ e = queue_head; /* Dequeue next grid */ p = queue[queue_head++]; /* Location */ y = vinfo[e].grid_y[0]; x = vinfo[e].grid_x[0]; /* Compute grid offsets */ vinfo[e].grid_x[0] = x; vinfo[e].grid_x[1] = y; vinfo[e].grid_x[2] = -y; vinfo[e].grid_x[3] = -x; vinfo[e].grid_x[4] = -x; vinfo[e].grid_x[5] = -y; vinfo[e].grid_x[6] = y; vinfo[e].grid_x[7] = x; vinfo[e].grid_y[0] = y; vinfo[e].grid_y[1] = x; vinfo[e].grid_y[2] = x; vinfo[e].grid_y[3] = y; vinfo[e].grid_y[4] = -y; vinfo[e].grid_y[5] = -x; vinfo[e].grid_y[6] = -x; vinfo[e].grid_y[7] = -y; /* Analyze slopes */ for (i = 0; i < hack->num_slopes; ++i) { m = hack->slopes[i]; /* Memorize intersection slopes (for non-player-grids) */ if ((e > 0) && (hack->slopes_min[y][x] < m) && (m < hack->slopes_max[y][x])) { /* We use this slope */ slope_count[i]++; /* Save the bit that stands for this slope */ vinfo[e].bits[i / 32] |= (1L << (i % 32)); } } /* Default */ vinfo[e].next_0 = &vinfo[0]; /* Grid next child */ if (distance(0, 0, x + 1, y) <= MAX_SIGHT) { if (!((queue[queue_tail - 1]->grid_x[0] == x + 1) && (queue[queue_tail - 1]->grid_y[0] == y))) { vinfo[queue_tail].grid_x[0] = x + 1; vinfo[queue_tail].grid_y[0] = y; queue[queue_tail] = &vinfo[queue_tail]; queue_tail++; } vinfo[e].next_0 = &vinfo[queue_tail - 1]; } /* Default */ vinfo[e].next_1 = &vinfo[0]; /* Grid diag child */ if (distance(0, 0, x + 1, y + 1) <= MAX_SIGHT) { if (!((queue[queue_tail - 1]->grid_x[0] == x + 1) && (queue[queue_tail - 1]->grid_y[0] == y + 1))) { vinfo[queue_tail].grid_x[0] = x + 1; vinfo[queue_tail].grid_y[0] = y + 1; queue[queue_tail] = &vinfo[queue_tail]; queue_tail++; } vinfo[e].next_1 = &vinfo[queue_tail - 1]; } /* Hack -- main diagonal has special children */ if (y == x) vinfo[e].next_0 = vinfo[e].next_1; /* Extra values */ vinfo[e].y = y; vinfo[e].x = x; vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2)); vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0); } /* Verify maximal bits XXX XXX XXX */ if (((vinfo[1].bits[4] | vinfo[2].bits[4]) != VINFO_BITS_4) || ((vinfo[1].bits[3] | vinfo[2].bits[3]) != VINFO_BITS_3) || ((vinfo[1].bits[2] | vinfo[2].bits[2]) != VINFO_BITS_2) || ((vinfo[1].bits[1] | vinfo[2].bits[1]) != VINFO_BITS_1) || ((vinfo[1].bits[0] | vinfo[2].bits[0]) != VINFO_BITS_0)) { quit("Incorrect bit masks!"); } /* Create the project_data array */ for (i = 0; i < VINFO_MAX_SLOPES; i++) { /* Create the list of squares intersected by this slope */ C_MAKE(project_data[i], slope_count[i], project_type); j = 0; for (y = 0; y <= MAX_SIGHT; ++y) { for (x = y; x <= MAX_SIGHT; ++x) { /* Only if in range */ if (distance(0, 0, x, y) > MAX_SIGHT) continue; /* Hack - ignore the origin */ if (!x && !y) continue; m = hack->slopes[i]; /* Does this square intersect the line? */ if ((hack->slopes_min[y][x] < m) && (m < hack->slopes_max[y][x])) { /* Save the square */ project_data[i][j].x = x; project_data[i][j].y = y; /* Add in the slopes information */ if (p_slope_min[x][y] > i) p_slope_min[x][y] = i; if (p_slope_max[x][y] < i) p_slope_max[x][y] = i; /* Next square... */ j++; } } } } /* * Add in the final information in the projection table. * * We need to know where to go to if the current square * is blocked. * * This is calculated in the following way: * * First, we need to find the first slope that does not * include the current square. * * This will be the first slope that does * not contain this square. The position along that slope * will be the first square that is not already scanned * by the current slope. * * This means that we may end up scanning squares twice, * but the simplification of the algorithm is worth it. */ for (i = 0; i < VINFO_MAX_SLOPES; i++) { for (j = 0; j < slope_count[i]; j++) { /* Set default case. */ project_data[i][j].slope = VINFO_MAX_SLOPES; project_data[i][j].square = 0; /* Find first slope without this square */ for (x = i + 1; x < VINFO_MAX_SLOPES; x++) { bool found = FALSE; for (y = 0; y < slope_count[x]; y++) { if ((project_data[x][y].x == project_data[i][j].x) && (project_data[x][y].y == project_data[i][j].y)) { found = TRUE; break; } } /* Did we find the blocking square? */ if (found) continue; /* Do we already have an answer? */ if (project_data[i][j].slope != VINFO_MAX_SLOPES) break; /* We did not find the square - save the row */ project_data[i][j].slope = x; /* Paranoia */ project_data[i][j].square = 0; /* Find the first non-matching square */ for (y = 0; y < slope_count[x]; y++) { if ((project_data[x][y].x != project_data[i][y].x) || (project_data[x][y].y != project_data[i][y].y)) { /* Not a match */ project_data[i][j].square = y; break; } } } } } /* Kill hack */ FREE(hack); /* Success */ return (0); } /* * Calculate the complete field of view using a new algorithm * * * Normally, vision along the major axes is more likely than vision * along the diagonal axes, so we check the bits corresponding to * the lines of sight near the major axes first. * * We use the "temp_x/y" arrays (and the "CAVE_TEMP" flag) to keep track of * which grids were previously marked "GRID_VIEW", since only those grids * whose "GRID_VIEW" value changes during this routine must be redrawn. * * This function is now responsible for maintaining the "GRID_LITE" * flags as well as the "GRID_VIEW" flags, which is good, because * the only grids which normally need to be memorized and/or redrawn * are the ones whose "GRID_VIEW" flag changes during this routine. * * Basically, this function divides the "octagon of view" into octants of * grids (where grids on the main axes and diagonal axes are "shared" by * two octants), and processes each octant one at a time, processing each * octant one grid at a time, processing only those grids which "might" be * viewable, and setting the "GRID_VIEW" flag for each grid for which there * is an (unobstructed) line of sight from the center of the player grid to * any internal point in the grid (and collecting these "GRID_VIEW" grids * into the "view_g" array), and setting the "GRID_LITE" flag for the grid * if, in addition, the grid is "illuminated" in some way (by a torch). * * This function relies on a theorem (suggested and proven by Mat Hostetter) * which states that in each octant of a field of view, a given grid will * be "intersected" by one or more unobstructed "lines of sight" from the * center of the player grid if and only if it is "intersected" by at least * one such unobstructed "line of sight" which passes directly through some * corner of some grid in the octant which is not shared by any other octant. * The proof is based on the fact that there are at least three significant * lines of sight involving any non-shared grid in any octant, one which * intersects the grid and passes though the corner of the grid closest to * the player, and two which "brush" the grid, passing through the "outer" * corners of the grid, and that any line of sight which intersects a grid * without passing through the corner of a grid in the octant can be "slid" * slowly towards the corner of the grid closest to the player, until it * either reaches it or until it brushes the corner of another grid which * is closer to the player, and in either case, the existance of a suitable * line of sight is thus demonstrated. * * It turns out that in each octant of the radius 20 "octagon of view", * there are 161 grids (with 128 not shared by any other octant), and there * are exactly 126 distinct "lines of sight" passing from the center of the * player grid through any corner of any non-shared grid in the octant. To * determine if a grid is "viewable" by the player, therefore, you need to * simply show that one of these 126 lines of sight intersects the grid but * does not intersect any wall grid closer to the player. So we simply use * a bit vector with 126 bits to represent the set of interesting lines of * sight which have not yet been obstructed by wall grids, and then we scan * all the grids in the octant, moving outwards from the player grid. For * each grid, if any of the lines of sight which intersect that grid have not * yet been obstructed, then the grid is viewable. Furthermore, if the grid * is a wall grid, then all of the lines of sight which intersect the grid * should be marked as obstructed for future reference. Also, we only need * to check those grids for whom at least one of the "parents" was a viewable * non-wall grid, where the parents include the two grids touching the grid * but closer to the player grid (one adjacent, and one diagonal). For the * bit vector, we simply use 4 32-bit integers. All of the static values * which are needed by this function are stored in the large "vinfo" array * (above), which is initialised at startup. * * This has been changed to allow a more circular view, due to the more * advanced distance() function in Zangband. There are now 135 lines of * sight and one more 32 bit flag to hold the data. * * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids * because the grids at the edge of the field of view use "grid zero" as * their children, and the queue must be able to hold several of these * special grids. Because the actual number of required grids is bizarre, * we simply allocate twice as many as we would normally need. XXX XXX XXX */ void borg_update_view(void) { map_block *mb_ptr; byte info; int x, y, i, o2; /* Clear the old "view" grids */ for (i = 0; i < view_n; i++) { y = borg_view_y[i]; x = borg_view_x[i]; if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* Clear "BORG_VIEW" flag */ mb_ptr->info &= ~(BORG_MAP_VIEW); } /* empty the viewable list */ borg_view_n = 0; /*** Step 1 -- player grid ***/ /* Player grid */ /* Get grid info */ mb_ptr = map_loc(c_x, c_y); /* Assume viewable */ mb_ptr->info |= (BORG_MAP_VIEW); /* Save in array */ borg_view_y[view_n] = c_y; borg_view_x[view_n] = c_x; borg_view_n++; /*** Step 2 -- octants ***/ /* Scan each octant */ for (o2 = 0; o2 < 8; o2 += 1) { vinfo_type *p; /* Last added */ vinfo_type *last = &vinfo[0]; /* Grid queue */ int queue_head = 0; int queue_tail = 0; vinfo_type *queue[VINFO_MAX_GRIDS * 2]; /* Slope bit vector */ u32b bits0 = VINFO_BITS_0; u32b bits1 = VINFO_BITS_1; u32b bits2 = VINFO_BITS_2; u32b bits3 = VINFO_BITS_3; u32b bits4 = VINFO_BITS_4; /* Reset queue */ queue_head = queue_tail = 0; /* Initial grids */ queue[queue_tail++] = &vinfo[1]; queue[queue_tail++] = &vinfo[2]; /* Process queue */ while (queue_head < queue_tail) { /* Dequeue next grid */ p = queue[queue_head++]; /* Check bits */ if ((bits0 & (p->bits[0])) || (bits1 & (p->bits[1])) || (bits2 & (p->bits[2])) || (bits3 & (p->bits[3])) || (bits4 & (p->bits[4]))) { /* Get location */ x = p->grid_x[o2] + c_x; y = p->grid_y[o2] + c_y; /* Is it in bounds? */ if (!map_in_bounds(x, y)) { /* Clear bits */ bits0 &= ~(p->bits[0]); bits1 &= ~(p->bits[1]); bits2 &= ~(p->bits[2]); bits3 &= ~(p->bits[3]); bits4 &= ~(p->bits[4]); continue; } /* Point to the location on the map */ mb_ptr = map_loc(x, y); /* Get current info flags for the square */ info = mb_ptr->info; if (borg_cave_los_grid(mb_ptr)) { /* Floor or semi-blocking terrain like trees */ /* Enqueue child */ if (last != p->next_0) { queue[queue_tail++] = last = p->next_0; } /* Enqueue child */ if (last != p->next_1) { queue[queue_tail++] = last = p->next_1; } } /* Handle wall */ else { /* Clear bits */ bits0 &= ~(p->bits[0]); bits1 &= ~(p->bits[1]); bits2 &= ~(p->bits[2]); bits3 &= ~(p->bits[3]); bits4 &= ~(p->bits[4]); } /* All ready seen. Next... */ if (info & BORG_MAP_VIEW) continue; /* Mark as viewable */ info |= (BORG_MAP_VIEW); /* Save cave info */ mb_ptr->info = info; /* Save in array */ borg_view_y[view_n] = y; borg_view_x[view_n] = x; borg_view_n++; } } } } /* * Clear the viewable space */ void borg_forget_view(void) { int i; map_block *mb_ptr; /* None to forget */ if (!borg_view_n) return; /* Clear them all */ for (i = 0; i < borg_view_n; i++) { int y = borg_view_y[i]; int x = borg_view_x[i]; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Access the grid */ mb_ptr = map_loc(x, y); /* Forget that the grid is viewable */ mb_ptr->info &= ~BORG_MAP_VIEW; } /* None left */ borg_view_n = 0; } /* * This is a version of the los function that uses a * tester function to determine whether or not to stop. * * Use this function instead of cut+pasting los() everywhere * with only tiny changes made to it. * * This works by following a "ray" that is one of those used * in the update_view() routine. * * We pick the minimal sloped ray that passes through the * required square. We then follow that ray, looking at each * grid along it. If the grid passes c_hook() then we keep * going. If the grid does not, then we go to the minimal * slope that does not pass through this blocking grid. * We go to the first unchecked square along that ray - and * then continue following it. * * If the new ray does not pass through the target square, then * its slope will be greater than the maximal slope of the * target. * * This routine will over-check some squares in a worst-case * scenario - but it is fairly efficient. Most of the required * information has been pre-calculated in the code that also * works out the data used by update_view() * * Unlike the old los() routine, this will give exactly the * same results as testing cave_view_grid() after using * update_view(). */ static bool los_general(int x1, int y1, int x2, int y2, map_hook_type mb_hook) { int i, j, temp, dist; int x, y; int dx, dy, ax, ay, sx, sy; map_block *mb_ptr; dist = distance(x1, y1, x2, y2); /* If (x1,y1) == (x2, y2) we know we can see ourselves */ if (dist == 0) return (TRUE); /* We only work for points that are less than MAX_SIGHT appart. */ if (dist > MAX_SIGHT) return (FALSE); /* Extract the offset */ dy = y2 - y1; dx = x2 - x1; /* Extract the absolute offset */ ay = ABS(dy); ax = ABS(dx); /* * Start at the first square in the list. * This is a square adjacent to (x1,y1) */ j = 0; /* Extract some signs */ sx = (dx < 0) ? -1 : 1; sy = (dy < 0) ? -1 : 1; /* Hack - we need to stick to one octant */ if (ay < ax) { /* Look up the slope to use */ i = p_slope_min[ax][ay]; while (i <= p_slope_max[ax][ay]) { x = x1 + sx * project_data[i][j].x; y = y1 + sy * project_data[i][j].y; /* Done? */ if ((x == x2) && (y == y2)) return (TRUE); /* Stop if out of bounds */ if (!map_in_bounds(x, y)) return (FALSE); mb_ptr = map_loc(x, y); if ((*mb_hook) (mb_ptr)) { /* Blocked: go to the best position we have not looked at yet */ temp = project_data[i][j].slope; j = project_data[i][j].square; i = temp; } else { /* Advance along ray */ j++; } } } else { /* Look up the slope to use */ i = p_slope_min[ay][ax]; while (i <= p_slope_max[ay][ax]) { /* Note that the data offsets have x,y swapped */ x = x1 + sx * project_data[i][j].y; y = y1 + sy * project_data[i][j].x; /* Done? */ if ((x == x2) && (y == y2)) return (TRUE); /* Stop if out of bounds */ if (!map_in_bounds(x, y)) return (FALSE); mb_ptr = map_loc(x, y); if ((*mb_hook) (mb_ptr)) { /* Blocked: go to the best position we have not looked at yet */ temp = project_data[i][j].slope; j = project_data[i][j].square; i = temp; } else { /* Advance along ray */ j++; } } } /* No path */ return (FALSE); } /* * A function to pass to los_general() * to do borg_los(). * we stop at walls */ static bool map_stop_wall(map_block *mb_ptr) { /* Is it passable? */ if (borg_cave_los_grid(mb_ptr)) return (FALSE); /* Seems ok */ return (TRUE); } /* * a function to pass to los_general(). * to do borg_bolt_los(). * we stop at unknown grids and walls */ static bool map_stop_wall_pure(map_block *mb_ptr) { /* Do not allow unknown grids */ if (!mb_ptr->feat) return (FALSE); /* Is it passable? */ if (borg_cave_los_grid(mb_ptr)) return (FALSE); /* Seems ok */ return (TRUE); } /* * Hack - a function to pass to los_general() used * to do borg_bolt_los(). * we stop at known monsters and walls */ static bool map_stop_bolt(map_block *mb_ptr) { /* Walls block projections */ if (borg_cave_wall_grid(mb_ptr)) return (TRUE); /* Stop at monsters */ if (mb_ptr->monster) return (TRUE); /* Seems ok */ return (FALSE); } /* * Hack - a function to pass to los_general() used * to do borg_bolt_los_pure(). * we stop at known monsters, walls and unknown grids */ static bool map_stop_bolt_pure(map_block *mb_ptr) { /* Unknown area is probably a wall */ if (!mb_ptr->feat) return (TRUE); /* Walls block projections */ if (borg_cave_wall_grid(mb_ptr)) return (TRUE); /* Stop at monsters */ if (mb_ptr->monster) return (TRUE); /* Seems ok */ return (FALSE); } /* * Slow, but simple LOS routine. This works in the same way as * the view code, so that if something is in view, los() behaves * as expected. * * The old routine was fast, but did not behave in the right way. * This new routine does not need to be fast, because it isn't * called in time-critical code. * * * It works by trying all slopes that connect (x1,y1) with (x2,y2) * If a wall is found, then it back-tracks to the 'best' square * to check next. There may be cases where it checks the same * square multiple times, but a simple algorithm is much cleaner. * * Note that "line of sight" is not "reflexive" in all cases. * * Use the "projectable()" routine to test "spell/missile line of sight". * * Use the "update_view()" function to determine player line-of-sight. */ bool borg_los(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, map_stop_wall)); } bool borg_los_pure(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, map_stop_wall_pure)); } bool borg_bolt_los(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, map_stop_bolt)); } bool borg_bolt_los_pure(int x1, int y1, int x2, int y2) { return (los_general(x1, y1, x2, y2, map_stop_bolt_pure)); } /* Slope and square used by mmove2 */ static int mmove_slope; static int mmove_sq; /* Direction to move in */ static int mmove_dx; static int mmove_dy; /* * Calculate the slope and square information used by * a following mmove2 */ void borg_mmove_init(int x1, int y1, int x2, int y2) { int temp; int xx, yy; int dx, dy, ax, ay, sx, sy, dist; map_block *mb_ptr; bool is_projectable; /* Clear slope and square */ mmove_slope = 0; mmove_sq = 0; /* Clear direction */ mmove_dx = 0; mmove_dy = 0; /* Paranoia - degenerate case */ if ((x1 == x2) && (y1 == y2)) return; /* Extract the offset */ dy = y2 - y1; dx = x2 - x1; /* * We only work for points that are less than MAX_SIGHT appart. * Note that MAX_RANGE < MAX_SIGHT */ dist = distance(x1, y1, x2, y2); if (dist > MAX_SIGHT) { /* Rescale */ dx = (dx * MAX_SIGHT) / dist; dy = (dy * MAX_SIGHT) / dist; } /* Save direction */ mmove_dx = dx; mmove_dy = dy; /* Extract the absolute offset */ ay = ABS(dy); ax = ABS(dx); /* Extract some signs */ sx = (dx < 0) ? -1 : 1; sy = (dy < 0) ? -1 : 1; /* Is the square projectable from here? */ is_projectable = projectable(x1, y1, x2, y2); /* * Start at the first square in the list. * This is a square adjacent to (x1,y1) */ /* Hack - we need to stick to one octant */ if (ay < ax) { /* Look up the slope to use */ mmove_slope = p_slope_min[ax][ay]; while (mmove_slope <= p_slope_max[ax][ay]) { xx = x1 + sx * project_data[mmove_slope][mmove_sq].x; yy = y1 + sy * project_data[mmove_slope][mmove_sq].y; /* Done? */ if ((xx == x1 + dx) && (yy == y1 + dy)) break; /* Paranoia */ if (!map_in_bounds(xx, yy)) break; mb_ptr = map_loc(xx, yy); /* Do we want to stop early? */ if (!is_projectable && mb_ptr->monster) break; /* Is the square not occupied by a monster, and passable? */ if (!borg_cave_los_grid(mb_ptr) || mb_ptr->monster) { /* Advance to the best position we have not looked at yet */ temp = project_data[mmove_slope][mmove_sq].slope; mmove_sq = project_data[mmove_slope][mmove_sq].square; mmove_slope = temp; } else { /* Advance along ray */ (mmove_sq)++; } } /* No match? */ if (mmove_slope > p_slope_max[ax][ay]) { mmove_slope = (p_slope_min[ax][ay] + p_slope_max[ax][ay]) / 2; } } else { /* Look up the slope to use */ mmove_slope = p_slope_min[ay][ax]; while (mmove_slope <= p_slope_max[ay][ax]) { /* Note that the data offsets have x,y swapped */ xx = x1 + sx * project_data[mmove_slope][mmove_sq].y; yy = y1 + sy * project_data[mmove_slope][mmove_sq].x; /* Done? */ if ((xx == x1 + dx) && (yy == y1 + dy)) break; /* Paranoia */ if (!map_in_bounds(xx, yy)) break; mb_ptr = map_loc(xx, yy); /* Do we want to stop early? */ if (!is_projectable && mb_ptr->monster) break; /* Is the square not occupied by a monster, and passable? */ if (!cave_los_grid(mb_ptr) || mb_ptr->monster) { /* Advance to the best position we have not looked at yet */ temp = project_data[mmove_slope][mmove_sq].slope; mmove_sq = project_data[mmove_slope][mmove_sq].square; mmove_slope = temp; } else { /* Advance along ray */ (mmove_sq)++; } } /* No match? */ if (mmove_slope > p_slope_max[ay][ax]) { mmove_slope = (p_slope_min[ay][ax] + p_slope_max[ay][ax]) / 2; } } /* * Reset to start. * * Square zero is the the first square along the path. * It is not the starting square */ mmove_sq = 0; } /* * Calculate incremental motion * * The current position is updated. * * (x,y) encodes the current location. * * This routine is very similar to los() except that we can use it * to return partial results. */ void borg_mmove(int *x, int *y, int x1, int y1) { int ax, ay, sx, sy; /* Extract the absolute offset */ ay = ABS(mmove_dy); ax = ABS(mmove_dx); /* Extract some signs */ sx = (mmove_dx < 0) ? -1 : 1; sy = (mmove_dy < 0) ? -1 : 1; /* Paranoia - square number is too large */ if (mmove_sq >= slope_count[mmove_slope]) { mmove_sq = slope_count[mmove_slope] - 1; } if (ay < ax) { /* Work out square to return */ *x = x1 + sx * project_data[mmove_slope][mmove_sq].x; *y = y1 + sy * project_data[mmove_slope][mmove_sq].y; } else { /* Work out square to return */ *x = x1 + sx * project_data[mmove_slope][mmove_sq].y; *y = y1 + sy * project_data[mmove_slope][mmove_sq].x; } /* Next square, next time. */ mmove_sq++; } /* * Hack - a function to pass to los_general() used * to do borg_projectable(). * Hack -- we refuse to assume that unknown grids are floors */ static bool map_stop_project(map_block *mb_ptr) { /* Unknown area is probably a wall */ /* if (!mb_ptr->feat) return (TRUE); */ /* Walls block projections */ if (borg_cave_wall_grid(mb_ptr)) return (TRUE); /* Seems ok */ return (FALSE); } /* * Check the projection from (x1,y1) to (x2,y2). * Assume that there is no monster in the way. * Hack -- we assume that unknown grids are floors * Adapted from "projectable()" in "spells1.c". */ bool borg_projectable(int x1, int y1, int x2, int y2) { /* Are we projectable? */ return (los_general(x1, y1, x2, y2, map_stop_project)); } /* * This file is responsible for the "borg_update" routine, which is used * to notice changes in the world, and to keep track of terrain features, * objects, monsters, both from visual changes, and from world messages. * * One big thing this file does is "object/monster tracking", which * attempts to gather information about the objects and monsters in * the dungeon, including their identity, location, and state, and * to "follow" them if they "move", and to delete them when they die. * * Information about terrain is used to help plan "flow" paths. Info * about objects and monsters is used to optimize planning paths to * those objects and monsters. Info about monsters is also used for * the "danger" functions, which help avoid dangerous situations. * * Notes: * We assume that monsters/objects can never appear in walls/doors * We count the occurance of invisible or offscreen monsters * We treat "mimics" and "trappers" as "invisible" monsters * * To Do: * * Bugs: * The timestamps are not quite in sync properly (?) */ /* * Strategy flags -- recalculate things */ bool borg_danger_wipe = FALSE; /* Recalculate danger */ /* * Hack -- message memory */ static s16b borg_msg_len; static s16b borg_msg_siz; static char *borg_msg_buf; static s16b borg_msg_num; static s16b borg_msg_max; static s16b *borg_msg_pos; static s16b *borg_msg_use; /* * Hack -- help identify "unique" monster names */ static int borg_unique_size; /* Number of uniques */ static s16b *borg_unique_what; /* Indexes of uniques */ static cptr *borg_unique_text; /* Names of uniques */ /* * Hack -- help identify "normal" monster names */ static int borg_normal_size; /* Number of normals */ static s16b *borg_normal_what; /* Indexes of normals */ static cptr *borg_normal_text; /* Names of normals */ /* * Attempt to convert a monster name into a race index * * First we check for all possible "unique" monsters, including * ones we have killed, and even if the monster name is "prefixed" * (as in "The Tarrasque" and "The Lernean Hydra"). Since we use * a fast binary search, this is acceptable. * * Otherwise, if the monster is NOT named "The xxx", we assume it * must be a "player ghost" (which is impossible). * * Then, we do a binary search on all "normal" monster names, using * a search which is known to find the last matching entry, if one * exists, and otherwise to find an entry which would follow the * matching entry if there was one, unless the matching entry would * follow all the existing entries, in which case it will find the * final entry in the list. Thus, we can search *backwards* from * the result of the search, and know that we will access all of * the matching entries, as long as we stop once we find an entry * which does not match, since this will catch all cases above. * * Finally, we assume the monster must be a "player ghost" (which * as noted above is impossible), which is a hack, but may prevent * crashes, even if it does induce strange behavior. */ static int borg_guess_race_name(cptr who) { int k, m, n; int i, b_i = 0; int s, b_s = 0; monster_race *r_ptr; char partial[160]; int suflen = 0, len = strlen(who); /* If the borg is hallucinating */ if (bp_ptr->status.image) { /* Say so */ borg_note("# Seeing a monster while hallucinating (%s)", who); /* The borg can't recognize monster when it is seeing funny things */ return (0); } /* Hack -- handle "offscreen" */ if (suffix(who, " (offscreen)")) suflen = 12; /* Handle mutations */ if (bp_ptr->muta2 & MUT2_SCOR_TAIL && suffix(who, " with your tail")) suflen = 16; else if (bp_ptr->muta2 & MUT2_HORNS && suffix(who, " with your horns")) suflen = 17; else if (bp_ptr->muta2 & MUT2_BEAK && suffix(who, " with your beak")) suflen = 16; else if (bp_ptr->muta2 & MUT2_TRUNK && suffix(who, " with your trunk")) suflen = 17; else if (bp_ptr->muta2 & MUT2_TENTACLES && suffix(who, " with your tentacles")) suflen = 21; /* Handle monk suffices */ if (borg_class == CLASS_MONK) { if (suffix(who, " with your knee")) { if (suffix(who, " in the groin with your knee")) suflen = 28; else suflen = 15; } else if (suffix(who, " with your elbow")) suflen = 16; else if (suffix(who, " in the ankle")) suflen = 13; else if (suffix(who, " with a Cat's Claw")) suflen = 18; else if (suffix(who, " with a jump kick")) suflen = 17; else if (suffix(who, " with an Eagle's Claw")) suflen = 21; else if (suffix(who, " with a circle kick")) suflen = 19; else if (suffix(who, " with an Iron Fist")) suflen = 18; else if (suffix(who, " with a flying kick")) suflen = 19; else if (suffix(who, " with a Dragon Fist")) suflen = 19; else if (suffix(who, " with a Crushing Blow")) suflen = 21; } if (suflen) { /* Remove the suffix */ strcpy(partial, who); partial[len - suflen] = '\0'; who = partial; } /* Start the search */ m = 0; n = borg_unique_size; /* Binary search */ while (m < n - 1) { /* Pick a "middle" entry */ i = (m + n) / 2; /* Search to the right (or here) */ if (strcmp(borg_unique_text[i], who) <= 0) { m = i; } /* Search to the left */ else { n = i; } } /* Check for equality */ if (streq(who, borg_unique_text[m])) { /* Use this monster */ return (borg_unique_what[m]); } /* Skip the prefix */ if (prefix(who, "The ")) who += 4; else if (prefix(who, "Your ")) who += 5; /* Start the search */ m = 0; n = borg_normal_size; /* Binary search */ while (m < n - 1) { /* Pick a "middle" entry */ i = (m + n) / 2; /* Search to the right (or here) */ if (strcmp(borg_normal_text[i], who) <= 0) { m = i; } /* Search to the left */ else { n = i; } } /* Scan possibilities */ for (k = m; k >= 0; k--) { /* Stop when done */ if (!streq(who, borg_normal_text[k])) break; /* Extract the monster */ i = borg_normal_what[k]; /* Access the monster */ r_ptr = &r_info[i]; /* Basic score */ s = 1000; /* Penalize "depth miss" */ s = s - ABS(r_ptr->level - bp_ptr->depth); /* Track best */ if (b_i && (s < b_s)) continue; /* Track it */ b_i = i; b_s = s; } /* Success */ if (b_i) return (b_i); borg_oops("# Assuming unknown (%s)", who); /* Oops */ return (0); } /* * Delete an old "object" record */ static void borg_delete_take(int i) { borg_take *take = &borg_takes[i]; map_block *mb_ptr; /* Paranoia -- Already wiped */ if (!take->k_idx) { borg_oops("Deleting already gone object!"); return; } /* Bounds checking */ if (map_in_bounds(take->x, take->y)) { mb_ptr = map_loc(take->x, take->y); /* Delete the 'take' value on grid. */ mb_ptr->take = 0; } /* Note */ borg_note("# Forgetting an object '%s' at (%d,%d)", (k_name + k_info[take->k_idx].name), take->x, take->y); /* Kill the object */ (void)WIPE(take, borg_take); /* One less object */ borg_takes_cnt--; /* Wipe goals */ goal = GOAL_NONE; } /* * Guess the kidx for an unknown item. */ static int borg_guess_kidx(char unknown) { int i, b_i = -1; int s, b_s = 0; for (i = 1; i < z_info->k_max; i++) { object_kind *k_ptr = &k_info[i]; /* Skip "empty" items */ if (!k_ptr->name) continue; /* Skip identified items */ if (k_ptr->aware) continue; /* Skip items with the wrong symbol */ if (unknown != k_ptr->d_char) continue; /* Valueless items are boring */ if (k_ptr->cost <= 0) continue; /* Base score */ s = 10000; /* Hack -- penalize "extremely" out of depth */ if (k_ptr->level > bp_ptr->depth + 50) s = s - 500; /* Hack -- penalize "very" out of depth */ if (k_ptr->level > bp_ptr->depth + 15) s = s - 100; /* Hack -- penalize "rather" out of depth */ if (k_ptr->level > bp_ptr->depth + 5) s = s - 50; /* Hack -- penalize "somewhat" out of depth */ if (k_ptr->level > bp_ptr->depth) s = s - 10; /* Hack -- Penalize "depth miss" */ s = s - ABS(k_ptr->level - bp_ptr->depth); /* Hack -- Penalize INSTA_ART items */ if (FLAG(k_ptr, TR_INSTA_ART)) s = s - 1000; /* Desire "best" possible score */ if (b_i && (s < b_s)) continue; /* Track it */ b_i = i; b_s = s; } /* Found a match? */ if (b_i != -1) return (b_i); /* Didn't find anything */ borg_note("# Cannot guess object '%c'", unknown); return (1); } /* * Obtain a new "take" index */ static int borg_new_take(int k_idx, char unknown, int x, int y) { int i, n = 0; borg_take *take; /* Handle unknown items */ if (unknown) k_idx = borg_guess_kidx(unknown); /* Paranoia */ if (!k_idx) { borg_oops("Cannot find object!"); return (0); } /* Look for a "dead" object */ for (i = 1; i < borg_takes_nxt; i++) { /* Reuse "dead" objects */ if (!borg_takes[i].k_idx) { n = i; break; } } /* Allocate a new object */ if ((n == 0) && (borg_takes_nxt < BORG_TAKES_MAX)) { /* Acquire the entry, advance */ n = borg_takes_nxt++; } /* Hack -- steal an old object */ if (n == 0) { /* Note */ borg_note("# Too many objects"); /* Hack -- Pick a random object */ n = randint1(borg_takes_nxt - 1); /* Delete it */ borg_delete_take(n); } /* Count new object */ borg_takes_cnt++; /* Obtain the object */ take = &borg_takes[n]; /* Save the kind */ take->k_idx = k_idx; take->unknown = unknown; /* Save the location */ take->x = x; take->y = y; /* Note */ borg_note("# Creating an object '%s' at (%d,%d)", (k_name + k_info[take->k_idx].name), x, y); /* Wipe goals */ goal = GOAL_NONE; /* Result */ return (n); } /* * Remove objects that are out of bounds */ static void delete_dead_objects(void) { int i; borg_take *bt_ptr; /* Look for a "dead" object */ for (i = 1; i < borg_takes_nxt; i++) { bt_ptr = &borg_takes[i]; /* Is the object "alive"? */ if (bt_ptr->k_idx) { /* Is it out of bounds? */ if (!map_in_bounds(bt_ptr->x, bt_ptr->y)) { /* Delete it */ borg_delete_take(i); } } } } /* * Delete an old "kill" record */ void borg_delete_kill(int who, cptr reason) { borg_kill *kill = &borg_kills[who]; /* Paranoia -- Already wiped */ if (!kill->r_idx) return; if (map_in_bounds(kill->x, kill->y)) { map_block *mb_ptr = map_loc(kill->x, kill->y); if (mb_ptr->kill == who) mb_ptr->kill = 0; } /* Note */ borg_note("# Removing a monster '%s' (%i) at (%d,%d) [%s]", mon_race_name(&r_info[kill->r_idx]), who, kill->x, kill->y, reason); /* Kill the monster */ (void)WIPE(kill, borg_kill); /* One less monster */ borg_kills_cnt--; /* Recalculate danger */ borg_danger_wipe = TRUE; /* Wipe goals */ goal = GOAL_NONE; } static int get_blank_kill(void) { int i; /* Count the monsters */ borg_kills_cnt++; /* Look for a "dead" monster */ for (i = 1; i < borg_kills_nxt; i++) { /* Find empty entries */ if (!borg_kills[i].r_idx) return (i); } /* Allocate a new monster */ if (borg_kills_nxt < BORG_KILLS_MAX) { /* Acquire the entry, advance */ return (borg_kills_nxt++); } /* Hack -- steal an old monster */ borg_note("# Too many monsters"); /* Hack -- Pick a random monster */ i = randint1(borg_kills_nxt - 1); /* Kill it */ borg_delete_kill(i, "overflow"); return (i); } /* * Merge an old "kill" record */ static void borg_merge_kill(int who) { borg_kill *kill = &borg_kills[who]; /* Paranoia -- Already wiped */ if (!kill->r_idx) return; if (map_in_bounds(kill->x, kill->y)) { map_block *mb_ptr = map_loc(kill->x, kill->y); if (mb_ptr->kill == who) mb_ptr->kill = 0; } /* Kill the monster */ (void)WIPE(kill, borg_kill); /* One less monster */ borg_kills_cnt--; } /* * Remove all monsters of a given type */ static void borg_wipe_mon(byte type) { int i; borg_kill *kill; for (i = 1; i < borg_kills_nxt; i++) { kill = &borg_kills[i]; if (kill->type == type) { borg_note("Destroying kill %d in MOVED list in wipe_mon", i); borg_merge_kill(i); } } } /* * Change all the kills of type two * to be type one. */ static void borg_append_mon_list(byte type1, byte type2) { int i; borg_kill *kill; for (i = 1; i < borg_kills_nxt; i++) { kill = &borg_kills[i]; /* Paranoia */ if (!kill->r_idx) continue; /* Add kills of type2 to type1 */ if (kill->type == type2) { kill->type = type1; } } } /* * Hack -- Update a "new" monster */ static void borg_update_kill(int i) { int t, e; borg_kill *kill = &borg_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; map_block *mb_ptr; /* Player energy per game turn */ e = extract_energy[bp_ptr->speed]; /* Game turns per player move */ t = (100 + (e - 1)) / e; /* Monster energy per game turn */ e = extract_energy[r_ptr->speed]; /* Assume no ranged attacks */ kill->ranged_attack = FALSE; /* Can it attack from a distance? */ if (r_ptr->flags[3] || r_ptr->flags[4] || r_ptr->flags[5]) { kill->ranged_attack = TRUE; } /* Get the default power */ kill->power = r_ptr->hdice * r_ptr->hside; if (map_in_bounds(kill->x, kill->y)) { /* Get grid */ mb_ptr = map_loc(kill->x, kill->y); /* Do we know about this monster? */ if (mb_ptr->monster == kill->r_idx) { /* Use the known hp */ kill->power = kill->power * mb_ptr->m_hp / 10; /* Save the flags */ kill->m_flags = mb_ptr->m_flags; } } } /* * Obtain a new "kill" index */ static void borg_new_kill(int r_idx, int n, int x, int y) { borg_kill *kill = &borg_kills[n]; /* Save the race */ kill->r_idx = r_idx; /* Location */ kill->x = x; kill->y = y; /* Timestamp */ kill->when = borg_t; /* Update the monster */ borg_update_kill(n); /* Note */ borg_note("# Creating a monster '%s' (%d) at (%d, %d)", mon_race_name(&r_info[kill->r_idx]), n, x, y); /* Recalculate danger */ borg_danger_wipe = TRUE; /* Wipe goals */ goal = GOAL_NONE; /* Done */ return; } /* * Force sleep onto a "kill" record * ??? Since this check is done at update_kill should I have it here? */ static void borg_sleep_kill(void) { /* Recalculate danger */ borg_danger_wipe = TRUE; } /* * Determine if a monster should be "viewable" */ static bool borg_follow_kill_aux(int i, int x, int y) { int d; map_block *mb_ptr; borg_kill *kill = &borg_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; /* Distance to player */ d = distance(c_y, c_x, y, x); /* Too far away */ if (d > MAX_SIGHT) return (FALSE); /* Access the grid */ mb_ptr = map_loc(x, y); /* Line of sight */ if (mb_ptr->info & BORG_MAP_VIEW) { /* Use "illumination" */ if (mb_ptr->flags & MAP_SEEN) { /* We can see invisible */ if ((FLAG(bp_ptr, TR_SEE_INVIS)) || borg_see_inv) return (TRUE); /* Monster is not invisible */ if (!(FLAG(r_ptr, RF_INVISIBLE))) return (TRUE); } /* Use "infravision" */ if (d <= bp_ptr->see_infra) { /* Infravision works on "warm" creatures */ if (!(FLAG(r_ptr, RF_COLD_BLOOD))) return (TRUE); } } /* Telepathy requires "telepathy" */ if (FLAG(bp_ptr, TR_TELEPATHY)) { /* Telepathy fails on "strange" monsters */ if (FLAG(r_ptr, RF_EMPTY_MIND)) return (FALSE); if (FLAG(r_ptr, RF_WEIRD_MIND)) return (FALSE); /* Success */ return (TRUE); } /* Nope */ return (FALSE); } /* * Attempt to track monsters from one * square to another. * * This is a quadratic algorithm, but may be fast enough... */ static void observe_kill_move(int new_type, int old_type, int dist) { int i, j; borg_kill *kill1, *kill2; map_block *mb_ptr1, *mb_ptr2; int x, y, d; for (i = 1; i < borg_kills_nxt; i++) { kill1 = &borg_kills[i]; /* Paranoia - ignore dead monsters */ if (!kill1->r_idx) continue; /* Must be correct type */ if (kill1->type != old_type) continue; x = kill1->x; y = kill1->y; /* Check the bounds */ if (!map_in_bounds(x, y)) continue; mb_ptr1 = map_loc(x, y); for (j = 1; j < borg_kills_nxt; j++) { kill2 = &borg_kills[j]; /* Don't use self */ if (i == j) continue; /* Paranoia - ignore dead monsters */ if (!kill2->r_idx) continue; /* Must be correct type */ if (kill2->type != new_type) continue; /* Must be same race */ if (kill2->r_idx != kill1->r_idx) continue; /* Calculate distance */ d = distance(x, y, kill2->x, kill2->y); /* Too far away */ if (d > dist) continue; /* Check the bounds */ if (!map_in_bounds(kill2->x, kill2->y)) continue; mb_ptr2 = map_loc(kill2->x, kill2->y); /* Note */ borg_note ("# Tracking monster (%d) from (%d,%d) to (%d) (%d,%d)", i, x, y, j, kill2->x, kill2->y); /* Change the location of the old one */ kill1->x = kill2->x; kill1->y = kill2->y; /* Remove the new monster */ borg_merge_kill(j); /* remove overwritten index from the old map position */ if (mb_ptr1->kill == i) mb_ptr1->kill = 0; /* Put the index on the new map position */ mb_ptr2->kill = i; /* Save timestamp */ kill1->when = borg_t; /* Update the monster */ borg_update_kill(i); /* Recalculate danger */ borg_danger_wipe = TRUE; /* Clear goals */ if (!(FLAG(bp_ptr, TR_TELEPATHY)) && (goal == GOAL_TAKE)) { goal = GOAL_NONE; } } } } static bool remove_bad_kills(u16b who) { int ox, oy; borg_kill *kill = &borg_kills[who]; ox = kill->x; oy = kill->y; /* Monster is out of bounds */ if (!map_in_bounds(ox, oy)) { borg_delete_kill(who, "out of bounds"); return (TRUE); } /* Is the monster underneith us? */ if ((c_x == ox) && (c_y == oy)) { borg_delete_kill(who, "where'd it go?"); return (TRUE); } /* Are we supposed to see this, but don't? */ if (borg_follow_kill_aux(who, ox, oy) && (map_loc(ox, oy)->monster != kill->r_idx)) { char buf[100]; /* Check again because BORG_MAP_VIEW != Line of Sight */ if (distance(c_x, c_y, ox, oy) < MAX_SIGHT && borg_los_pure(c_x, c_y, ox, oy)) { (void)strnfmt(buf, 100, "vanished : %d, %d", map_loc(ox, oy)->monster, kill->r_idx); borg_delete_kill(who, buf); return (TRUE); } } /* We haven't seen it for ages? */ if (borg_t - kill->when > 2000) { borg_delete_kill(who, "expired"); return (TRUE); } /* Did not remove monster */ return (FALSE); } /* * Track remaining unaccounted for monsters */ static void handle_old_mons(byte type) { u16b i; borg_kill *kill; for (i = 1; i < borg_kills_nxt; i++) { kill = &borg_kills[i]; /* Paranoia - ignore dead monsters */ if (!kill->r_idx) continue; /* Must be of correct type */ if (kill->type != type) continue; if (remove_bad_kills(i)) continue; /* Move the old monster to the used list */ kill->type = BORG_MON_USED; } } /* * Attempt to locate a monster which could explain a message involving * the given monster name, near the given location, up to the given * distance from the given location. * * Invisible monsters, bizarre monsters, and unexplainable monsters are * all treated the same way, and should eventually contribute some amount * of basic "fear" to the current region. * * First, we attempt to convert "similar" objects into the desired monster, * then we attempt to convert "similar" monsters into the desired monster, * then we attempt to match an existing monster, and finally, we give up. * * XXX XXX XXX Hack -- To prevent fatal situations, every time we think * there may be a monster nearby, we look for a nearby object which could * be the indicated monster, and convert it into that monster. This allows * us to correctly handle a room full of multiplying clear mushrooms. * * XXX XXX XXX When surrounded by multiple monsters of the same type, * we will ascribe most messages to one of those monsters, and ignore * the existance of all the other similar monsters. * * XXX XXX XXX Currently, confusion may cause messages to be ignored. */ static int borg_locate_kill(cptr who, int x, int y, int r) { int i, d, r_idx; int b_i, b_d; borg_kill *kill; monster_race *r_ptr; /* Handle invisible monsters */ if (streq(who, "It") || streq(who, "Someone") || streq(who, "Something")) { /* Note */ borg_note("# Possible Invisible monster nearby."); /* * If I can, cast detect inviso--time stamp it * We stamp it now if we can, or later if we just did the spell * That way we dont loop casting the spell. APW */ if (need_see_inviso < borg_t) { need_see_inviso = borg_t; } /* Ignore */ return (0); } /* Guess the monster race */ r_idx = borg_guess_race_name(who); /* Paranoia */ if (!r_idx) { /* Note */ borg_note("# Possible strange monster nearby."); /* Ignore */ return (0); } /* Access the monster race */ r_ptr = &r_info[r_idx]; /* Note */ borg_note("# There is a monster '%s' within %d grids of %d,%d", mon_race_name(r_ptr), r, x, y); /* Handle trappers and lurkers and mimics */ if (FLAG(r_ptr, RF_CHAR_CLEAR) || FLAG(r_ptr, RF_CHAR_MIMIC)) { /* Note */ borg_note("# Bizarre monster nearby"); } /*** Hack -- Find an existing monster ***/ /* Nothing yet */ b_i = -1; b_d = 999; /* Scan the monsters */ for (i = 1; i < borg_kills_nxt; i++) { kill = &borg_kills[i]; /* Skip "dead" monsters */ if (!kill->r_idx) continue; /* Skip "different" monsters */ if (kill->r_idx != r_idx) continue; /* Distance away */ d = distance(kill->x, kill->y, x, y); /* In a darkened room with ESP we can get hit and ignore it */ /* Check distance */ if (d > r + 1) continue; /* Hopefully this will add fear to our grid */ if (!borg_projectable(kill->x, kill->y, x, y)) continue; /* Track closest one */ if (d > b_d) continue; /* Track it */ b_i = i; b_d = d; } /* Found one */ if (b_i >= 0) { kill = &borg_kills[b_i]; /* Note */ borg_note("# Matched a monster '%s' at (%d,%d)", mon_race_name(&r_info[kill->r_idx]), kill->x, kill->y); /* Index */ return (b_i); } /*** Oops ***/ /* Note */ borg_note("# Ignoring a monster '%s' near (%d,%d)", mon_race_name(r_ptr), x, y); /* Oops */ /* this is the case where we know the name of the monster */ /* but cannot locate it on the monster list. */ return (-1); } /* * Notice the "death" of a monster */ static void borg_count_death(cptr what) { int r_idx; /* Handle invisible monsters */ if (streq(what, "It") || streq(what, "Someone") || streq(what, "Something")) { /* Ignore */ return; } /* Guess the monster race */ r_idx = borg_guess_race_name(what); /* Paranoia */ if (!r_idx) return; if (FLAG(&r_info[r_idx], RF_UNIQUE)) { /* Reset unique on level flag */ unique_on_level = FALSE; } } /* Mark detected region */ static void detect_region(byte flag) { int x, y; map_block *mb_ptr; for (x = c_x - BORG_MAX_DETECT; x <= c_x + BORG_MAX_DETECT; x++) { for (y = c_y - BORG_MAX_DETECT; y <= c_y + BORG_MAX_DETECT; y++) { /* bounds checking */ if (!map_in_bounds(x, y)) continue; /* Check distance */ if (distance(c_x, c_y, x, y) > BORG_MAX_DETECT) continue; mb_ptr = map_loc(x, y); /* Set flag */ mb_ptr->detect |= flag; } } } /* * Handle "detection" spells and "call lite" * * Note that we must use the "old" player location */ static bool borg_handle_self(cptr str) { /* Handle failure */ if (borg_failure) { borg_note("# Something failed"); } /* Handle "call lite" */ else if (prefix(str, "lite")) { /* Message */ borg_note("# Called lite"); } /* Handle "detect walls" */ else if (prefix(str, "wall")) { /* Message */ borg_note("# Detected walls"); /* Mark detected walls */ detect_region(BORG_DETECT_WALL); } /* Handle "detect traps" */ else if (prefix(str, "trap")) { /* Message */ borg_note("# Detected traps"); /* Mark detected traps */ detect_region(BORG_DETECT_TRAP); } /* Handle "detect doors" */ else if (prefix(str, "door")) { /* Message */ borg_note("# Detected doors"); /* Mark detected doors */ detect_region(BORG_DETECT_DOOR); } /* Handle "detect traps and doors" */ else if (prefix(str, "both")) { /* Message */ borg_note("# Detected traps and doors"); /* Mark detected traps + doors */ detect_region(BORG_DETECT_TRAP | BORG_DETECT_DOOR); } /* Handle "detect traps and doors and evil" */ else if (prefix(str, "TDE")) { /* Message */ borg_note("# Detected traps, doors & evil"); /* Mark detected traps + doors + evil */ detect_region(BORG_DETECT_TRAP | BORG_DETECT_DOOR | BORG_DETECT_EVIL); } /* Handle "detect evil" */ else if (prefix(str, "evil")) { /* Message */ borg_note("# Detected evil"); /* Mark detected evil */ detect_region(BORG_DETECT_EVIL); } /* Done */ return (TRUE); } /* Try to add a town to the town list */ int borg_add_town(int x, int y, cptr town_name) { int i; /* Not quite a town */ if (prefix(town_name, "Bottom") || strstr(town_name, "0 ft") || prefix(town_name, "Lev ")) { borg_oops("Trying to add %s as a town!", town_name); return (-1); } /* Loop through the towns */ for (i = 0; i < borg_town_num; i++) { /* Find the matching town */ if (streq(borg_towns[i].name, town_name)) break; } /* No need to duplicate towns */ if (i < borg_town_num) return (i); /* Do we need to increase the size of the town array? */ if (borg_town_num == borg_town_size) { borg_town *temp; /* Double size of arrays */ borg_town_size *= 2; /* Make new (bigger) array */ C_MAKE(temp, borg_town_size, borg_town); /* Copy into new array */ C_COPY(temp, borg_towns, borg_town_num, borg_town); /* Get rid of old array */ FREE(borg_towns); /* Use new array */ borg_towns = temp; } /* Add this town */ borg_towns[i].x = x; borg_towns[i].y = y; strncpy(borg_towns[i].name, town_name, strlen(town_name)); borg_note("# Adding town = %s, x, = %d, y = %d", town_name, x, y); /* extend the list */ borg_town_num += 1; return (i); } /* Find out the current towns name, add it to the array, return its index */ static int borg_add_town_screen(int x, int y) { int wid, hgt; byte t_a; char buf[T_NAME_LEN]; int count = 0; /* Get size */ Term_get_size(&wid, &hgt); /* Pick up town name */ if (0 != borg_what_text(wid - T_NAME_LEN, hgt - 1, T_NAME_LEN - 1, &t_a, buf)) { /* Failed to read off the screen */ return (-1); } /* Is the borg somewhere in the wilderness? */ if (prefix(buf, "Wilderness")) return (-1); /* Is the borg somewhere next to a dungeon? */ if (prefix(buf, "Dungeon")) return (-1); /* Is the borg somewhere next to a dungeon with a quest? */ if (prefix(buf, "Ruin")) return (-1); /* Is this a dungeon? */ if (prefix(buf, "Bottom ") || prefix(buf, "Lev ") || strstr(buf, "0 ft")) return (-1); /* Discard leading spaces */ while (buf[count] == ' ') count++; /* Try to add this town */ return (borg_add_town(x, y, buf + count)); } /* Add a dungeon to the array, overwrite an old one if it was an estimate */ void borg_add_dungeon(int x, int y, int min_depth, int max_depth, bool bottom) { int i, b_i = 0; int d, b_d = BORG_MAX_DISTANCE; /* Do we need to increase the size of the dungeon array? */ if (borg_dungeon_num == borg_dungeon_size) { borg_dungeon *temp; /* Double size of arrays */ borg_dungeon_size *= 2; /* Make new (bigger) array */ C_MAKE(temp, borg_dungeon_size, borg_dungeon); /* Copy into new array */ C_COPY(temp, borg_dungeons, borg_dungeon_num, borg_dungeon); /* Get rid of old array */ FREE(borg_dungeons); /* Use new array */ borg_dungeons = temp; } /* Find closest dungeon */ for (i = 0; i < borg_dungeon_num; i++) { /* * There is a wilderness dungeon that has three stairs * that all lead to the same dungeon. Pick only one to * avoid confusion. */ if ((borg_dungeons[i].x == x || ABS(borg_dungeons[i].x - x) == 14) && (borg_dungeons[i].y == y || ABS(borg_dungeons[i].y - y) == 30)) return; /* Get the distance */ d = distance(x, y, borg_dungeons[i].x, borg_dungeons[i].y); /* Is it closer? */ if (d >= b_d) continue; /* remember */ b_i = i; b_d = d; } /* It is close */ if (b_d > 6 * WILD_BLOCK_SIZE) b_i = borg_dungeon_num++; /* The dungeon entrance is seen or spotted for the first time ever */ if (!min_depth || !borg_dungeons[b_i].x) { /* Assign coordinates */ borg_dungeons[b_i].x = x; borg_dungeons[b_i].y = y; /* Tell the player */ borg_note("# Adding a %d dungeon at (%d, %d), min, max = %d, %d", (min_depth + 9) / 10, x, y, min_depth, max_depth); } /* Add depth if it was given */ if (min_depth) { /* Assign depth */ borg_dungeons[b_i].min_depth = min_depth; borg_dungeons[b_i].max_depth = max_depth; borg_dungeons[b_i].bottom = bottom; } } /* * Save the borg information into the overhead map */ void borg_map_info(map_block *mb_ptr, const term_map *map, vptr dummy) { int i; int x = map->x; int y = map->y; bool old_wall; bool new_wall; /* Hack - ignore parameter */ (void) dummy; /* Don't do anything if the borg is inactive */ if (!borg_active) { /* Done */ return; } /* Save the old "wall" or "door" */ old_wall = borg_cave_wall_grid(mb_ptr); /* Don't overwrite known info with unknown */ if (map->terrain) mb_ptr->feat = map->terrain; /* * Examine monsters */ if (map->monster) { borg_kill *kill; /* Is the monster known and not new? */ if (mb_ptr->kill && (map->monster == mb_ptr->monster)) { kill = &borg_kills[mb_ptr->kill]; if (kill->type != BORG_MON_NEW) { /* Remove it from the old list. */ kill->type = BORG_MON_USED; } } else { /* Is it a new monster? */ if (mb_ptr->kill) { kill = &borg_kills[mb_ptr->kill]; /* Move old entry into "moved" list */ kill->type = BORG_MON_MOVE; } /* Get new kill */ mb_ptr->kill = get_blank_kill(); kill = &borg_kills[mb_ptr->kill]; /* Set type */ kill->type = BORG_MON_NEW; /* Fill in information for new monster */ borg_new_kill(map->monster, mb_ptr->kill, x, y); } } else { if (mb_ptr->kill && (map->flags & MAP_SEEN)) { /* Check */ borg_kill *kill = &borg_kills[mb_ptr->kill]; if (kill->type != BORG_MON_NEW) { if ((kill->x == x) && (kill->y == y)) { /* * We need to remove this from the list, * it must have moved. */ kill->type = BORG_MON_MOVE; } else { borg_note("Strange kill %d at (%d,%d)", mb_ptr->kill, kill->x, kill->y); } } /* Clear it */ mb_ptr->kill = 0; } } /* * Examine objects */ if (map->object || map->unknown) { /* Do we already know about this object? */ if (mb_ptr->take) { borg_take *bt_ptr = &borg_takes[mb_ptr->take]; if ((bt_ptr->unknown != map->unknown) || ((bt_ptr->k_idx != map->object) && !map->unknown)) { borg_note("# The object %d is different! (%d,%d)", mb_ptr->take, bt_ptr->k_idx, map->object); /* The object is different- delete it */ borg_delete_take(mb_ptr->take); /* Make a new object */ mb_ptr->take = borg_new_take(map->object, map->unknown, x, y); } } else { /* Make a new object */ mb_ptr->take = borg_new_take(map->object, map->unknown, x, y); } } else { /* Do we think there is an object here that we cannot see? */ if (mb_ptr->take && (map->flags & MAP_SEEN)) { borg_note("# Removing missing object (%d)", mb_ptr->take); /* The object is no longer here - delete it */ borg_delete_take(mb_ptr->take); } } /* Analyze terrain */ switch (mb_ptr->feat) { /* Up stairs */ case FEAT_LESS: { /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { /* Stop if we already new about these stairs */ if ((track_less_x[i] == x) && (track_less_y[i] == y)) break; } /* Track the newly discovered "up stairs" */ if ((i == track_less_num) && (i < track_less_size)) { track_less_x[i] = x; track_less_y[i] = y; track_less_num++; } /* Done */ break; } /* Down stairs */ case FEAT_MORE: { /* Check for an existing "down stairs" */ for (i = 0; i < track_more_num; i++) { /* We already knew about that one */ if ((track_more_x[i] == x) && (track_more_y[i] == y)) break; } /* Track the newly discovered "down stairs" */ if ((i == track_more_num) && (i < track_more_size)) { track_more_x[i] = x; track_more_y[i] = y; track_more_num++; } /* Hack! use p_ptr instead of bp because bp is not yet updated */ if (p_ptr->depth == 0) { /* Add this dungeon maybe */ borg_add_dungeon(x, y, 0, 0, FALSE); } /* Done */ break; } } if (map->field) { /* Get field type */ field_thaum *t_ptr = &t_info[map->field]; /* Is it a store or building? */ if (t_ptr->type == FTYPE_BUILD) { /* Check for an existing shop */ for (i = 0; i < borg_shop_num; i++) { /* Stop if we already knew about this shop */ if ((borg_shops[i].x == x) && (borg_shops[i].y == y)) break; } /* Do we need to increase the size of the shop array? */ if (i == borg_shop_size) { borg_shop *temp; /* Double size of arrays */ borg_shop_size *= 2; /* Make new (bigger) array */ C_MAKE(temp, borg_shop_size, borg_shop); /* Copy into new array */ C_COPY(temp, borg_shops, borg_shop_num, borg_shop); /* Get rid of old array */ FREE(borg_shops); /* Use new array */ borg_shops = temp; } /* Track the newly discovered shop */ if (i == borg_shop_num) { /* Position */ borg_shops[i].x = x; borg_shops[i].y = y; borg_shops[i].town_num = borg_add_town_screen(x, y); /* Catch all the funny buildings */ if (streq(t_ptr->name, "Weaponmaster")) { borg_shops[i].type = BUILD_WEAPONMASTER; } else if (streq(t_ptr->name, "Zymurgist")) { borg_shops[i].type = BUILD_RECHARGE; } else if (streq(t_ptr->name, "Magesmith (weapons)")) { borg_shops[i].type = BUILD_PLUS_WEAPON; } else if (streq(t_ptr->name, "Magesmith (armor)")) { borg_shops[i].type = BUILD_PLUS_ARMOUR; } else if (streq(t_ptr->name, "Mutatalist")) { borg_shops[i].type = BUILD_MUTATE; } else if (streq(t_ptr->name, "Map Maker")) { borg_shops[i].type = BUILD_MAP; } else if (streq(t_ptr->name, "Library")) { borg_shops[i].type = BUILD_LIBRARY; } else if (streq(t_ptr->name, "Casino")) { borg_shops[i].type = BUILD_CASINO; } else if (streq(t_ptr->name, "Inn")) { borg_shops[i].type = BUILD_INN; } else if (streq(t_ptr->name, "Healer")) { borg_shops[i].type = BUILD_HEALER; } else if (streq(t_ptr->name, "Magetower")) { borg_shops[i].type = BUILD_MAGETOWER0; } else if (streq(t_ptr->name, "Large Magetower")) { borg_shops[i].type = BUILD_MAGETOWER1; } else if (streq(t_ptr->name, "Small Castle")) { borg_shops[i].type = BUILD_CASTLE0; } else if (streq(t_ptr->name, "Large Castle")) { borg_shops[i].type = BUILD_CASTLE1; } /* Hack - we have never been here before */ borg_shops[i].when = borg_t - 1000; /* One more shop */ borg_shop_num++; } } /* MT - Handle adding of traps to the borg_traps array */ else if(t_ptr->type == FTYPE_TRAP) { mb_ptr->trap = map->field; } /* MT - Handle Glyphs */ else if(t_ptr->type == FTYPE_FIELD) { mb_ptr->m_effect = map->field; } } /* * Save the new "wall" or "door" * * Hack - use inline form of borg_cave_wall_grid macro */ new_wall = (f_info[map->terrain].flags & FF_BLOCK); /* Notice wall changes */ if (old_wall != new_wall) { /* Remove this grid from any flow */ if (new_wall) mb_ptr->flow = 255; /* Remove this grid from any flow */ mb_ptr->info &= ~(BORG_MAP_ICKY | BORG_MAP_KNOW); } } /* * Save the borg information into the overhead map */ void borg_map_erase(vptr dummy) { /* Hack -ignore parameter */ (void) dummy; /* Forget the view */ borg_forget_view(); /* No objects here */ borg_takes_cnt = 0; borg_takes_nxt = 1; /* Forget old objects */ (void)C_WIPE(borg_takes, BORG_TAKES_MAX, borg_take); /* Forget old monsters */ (void)C_WIPE(borg_kills, BORG_KILLS_MAX, borg_kill); } /* * Update the "map" based on visual info on the screen * * Note that we make assumptions about the grid under the player, * to prevent painful situations such as seeming to be standing * in a wall, or on a trap, etc. * * In general, we use the same "feat" codes as the game itself, but * sometimes we are just guessing (as with "visible traps"), and we * use some special codes, explained below. * * Note that we use the "feat" code of "FEAT_NONE" for grids which * have never been seen, or which, when seen, have always contained * an object or monster. These grids are probably walls, unless * they contain a monster or object, in which case they are probably * floors, unless they contain a monster which passes through walls, * in which case they are probably walls. * * Note that we use the "feat" code of "FEAT_FLOOR" for grids which * were a normal floor last time we checked. These grids may have * changed into non-floor grids recently (via earthquake?), unless * the grid is on the current panel, and is currently "lit" in some * manner, and does not contain a monster. * * Note that we use the other "feat" codes for grids which probably * contain the given feature type, unless several feature types use * the same symbol, in which case we use some "default" code, changing * our guess when messages provide us with more information. This is * especially necessary for distinguishing magma from quartz, and for * distinguishing normal doors from locked doors from jammed doors. * Note that most "feat" codes, even if they are not "guesses", may * not be valid unless the grid is on the current panel. * * Note the "interesting" code used to learn which floor grids are "dark" * and which are "perma-lit", by tracking those floor grids which appear * to be "lit", and then marking all of these grids which do not appear * to be lit by the torch as "known" to be illuminated, and by marking * any grids which "disappear" or which are displayed as "dark floors" * as "known" to be "dark". This leaves many grids, especially those * lit by the torch, as being neither lit nor dark. * * The basic problem is that, especially with no special options set, * the player has very little direct information about which grids * are perma-lit, since all non-floor grids are memorized when they * are seen, and torch-lit floor grids look just like perma-lit * floor grids. Also, monsters hide any object or feature in their * grid, and objects hide any feature in their grid, and objects are * memorized when they are seen, and monsters can be detected by a * variety of methods, including infravision and telepathy. * * Note that we assume that normally, when the player steps onto * something, it disappears, and turns into a normal floor, unless * the player is stepping onto a grid which is normally "permanent" * (floors, stairs, store doors), in which case it does not change. * * Note that when we encounter a grid which blocks motion, but which * was previously thought to not block motion, we must be sure to * remove it from any "flow" which might be in progress, to prevent * nasty situations in which we attempt to flow into a wall grid * which was thought to be something else, like an unknown grid. * * In Zborg, the wilderness level shops and stairs are * handled in borg_update() under new levels. */ static void borg_update_map(void) { int i; /* Mark the player grid as having been stepped on */ track_step_x[track_step_num] = c_x; track_step_y[track_step_num] = c_y; track_step_num++; /* Hack - Clean the steps every so often */ if (track_step_num > 75) { for (i = 0; i < 75; i++) { /* Move each step down one position */ track_step_x[i] = track_step_x[i + 1]; track_step_y[i] = track_step_y[i + 1]; } /* reset the count */ track_step_num = 75; } } /* * Increase the "region danger" */ static void borg_fear_grid(cptr who, int x, int y, uint k, bool seen_guy) { int i, j; map_block *mb_ptr; /* Reduce fear if GOI is on */ if (borg_goi) { k = k / 3; } /* Messages */ if (seen_guy) { borg_note("# Fearing region value %d.", k); } else { borg_note("# Fearing grid (%d,%d) value %d because of a non-LOS %s", x, y, k, who); } /* Current region */ for (i = -15; i < 15; i++) { for (j = -15; j < 15; j++) { if (!map_in_bounds(i + x, j + y)) continue; mb_ptr = map_loc(i + x, j + y); /* Add some fear */ mb_ptr->fear += 2 * k / (8 + ABS(i) + ABS(j)); } } /* * There is some problems here, when the death * of a monster decreases the fear, it ends up making * about 2000 danger pts. It needs to be fixed. */ } /* * Calculate base danger from a spell attack by an invisible monster * * We attempt to take account of various resistances, both in * terms of actual damage, and special effects, as appropriate. */ static int borg_fear_spell(int i) { int z = 0; int p = 0; int ouch = 0; /* Damage taken */ if (bp_ptr->oldhp > bp_ptr->chp) ouch = (bp_ptr->oldhp - bp_ptr->chp) * 2; /* Check the spell */ switch (i) { case 0: { /* RF3_SHRIEK */ p += 10; break; } case 1: { /* RF3_FAILED spell by monster. Fear it! */ /* It could be a unique like Azriel */ p += bp_ptr->depth; break; } case 2: { /* RF3_XXX3X4 */ break; } case 3: { /* RF3_XXX4X4 */ break; } case 4: { /* RF3_ARROW */ /* XXX FIXME this depends on the monster */ z = (4 * 6); break; } case 5: { /* RF3_XXX6 */ break; } case 6: { /* RF3_XXX7 */ break; } case 7: { /* RF3_XXX8 */ break; } case 8: { /* RF3_BR_ACID */ if (FLAG(bp_ptr, TR_IM_ACID)) break; z = ouch; p += 40; break; } case 9: { /* RF3_BR_ELEC */ if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = ouch; p += 20; break; } case 10: { /* RF3_BR_FIRE */ if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = ouch; p += 40; break; } case 11: { /* RF3_BR_COLD */ if (FLAG(bp_ptr, TR_IM_COLD)) break; z = ouch; p += 20; break; } case 12: { /* RF3_BR_POIS */ z = ouch; if (FLAG(bp_ptr, TR_RES_POIS)) break; if (my_oppose_pois) break; p += 20; break; } case 13: { /* RF3_BR_NETH */ z = ouch + 100; if (FLAG(bp_ptr, TR_RES_NETHER)) break; p += 50; if (FLAG(bp_ptr, TR_HOLD_LIFE)) break; /* do not worry about drain exp after level 50 */ if (bp_ptr->lev >= 50) break; p += 150; break; } case 14: { /* RF3_BR_LITE */ z = ouch; if (FLAG(bp_ptr, TR_RES_LITE)) break; if (FLAG(bp_ptr, TR_RES_BLIND)) break; p += 20; break; } case 15: { /* RF3_BR_DARK */ z = ouch; if (FLAG(bp_ptr, TR_RES_DARK)) break; if (FLAG(bp_ptr, TR_RES_BLIND)) break; p += 20; break; } case 16: { /* RF3_BR_CONF */ z = ouch; if (FLAG(bp_ptr, TR_RES_CONF)) break; p += 100; break; } case 17: { /* RF3_BR_SOUN */ z = ouch; if (FLAG(bp_ptr, TR_RES_SOUND)) break; p += 50; break; } case 18: { /* RF3_BR_CHAO */ z = ouch; if (FLAG(bp_ptr, TR_RES_CHAOS)) break; p += 200; if (!(FLAG(bp_ptr, TR_RES_NETHER))) p += 50; if (!(FLAG(bp_ptr, TR_HOLD_LIFE))) p += 50; if (!(FLAG(bp_ptr, TR_RES_CONF))) p += 50; if (bp_ptr->lev == 50) break; p += 100; break; } case 19: { /* RF3_BR_DISE */ z = ouch; if (FLAG(bp_ptr, TR_RES_DISEN)) break; p += 500; break; } case 20: { /* RF3_BR_NEXU */ z = ouch; if (FLAG(bp_ptr, TR_RES_NEXUS)) break; p += 100; break; } case 21: { /* RF3_BR_TIME */ z = ouch; p += 200; break; } case 22: { /* RF3_BR_INER */ z = ouch; p += 50; break; } case 23: { /* RF3_BR_GRAV */ z = ouch; p += 50; if (FLAG(bp_ptr, TR_RES_SOUND)) break; p += 50; break; } case 24: { /* RF3_BR_SHAR */ z = ouch; if (FLAG(bp_ptr, TR_RES_SHARDS)) break; p += 50; break; } case 25: { /* RF3_BR_PLAS */ z = ouch; if (FLAG(bp_ptr, TR_RES_SOUND)) break; p += 50; break; } case 26: { /* RF3_BR_WALL */ z = ouch; if (FLAG(bp_ptr, TR_RES_SOUND)) break; p += 50; break; } case 27: { /* RF3_BR_MANA */ /* XXX XXX XXX */ break; } case 28: { /* RF3_XXX5X4 */ break; } case 29: { /* RF3_XXX6X4 */ break; } case 30: { /* RF3_XXX7X4 */ break; } case 31: { /* RF3_XXX8X4 */ break; } case 32: { /* RF4_BA_ACID */ if (FLAG(bp_ptr, TR_IM_ACID)) break; z = ouch; p += 40; break; } case 33: { /* RF4_BA_ELEC */ if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = ouch; p += 20; break; } case 34: { /* RF4_BA_FIRE */ if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = ouch; p += 40; break; } case 35: { /* RF4_BA_COLD */ if (FLAG(bp_ptr, TR_IM_COLD)) break; z = ouch; p += 20; break; } case 36: { /* RF4_BA_POIS */ z = ouch; if (FLAG(bp_ptr, TR_RES_POIS)) break; p += 20; break; } case 37: { /* RF4_BA_NETH */ z = ouch + 100; if (FLAG(bp_ptr, TR_RES_NETHER)) break; p += 300; break; } case 38: { /* RF4_BA_WATE */ z = ouch; p += 50; break; } case 39: { /* RF4_BA_MANA */ z = ouch; break; } case 40: { /* RF4_BA_DARK */ z = ouch; if (FLAG(bp_ptr, TR_RES_DARK)) break; if (FLAG(bp_ptr, TR_RES_BLIND)) break; p += 20; break; } case 41: { /* RF4_DRAIN_MANA */ if (bp_ptr->msp) p += 10; break; } case 42: { /* RF4_MIND_BLAST */ z = 20; break; } case 43: { /* RF4_BRAIN_SMASH */ z = (12 * 15); p += 100; break; } case 44: { /* RF4_CAUSE_1 */ z = (3 * 8); break; } case 45: { /* RF4_CAUSE_2 */ z = (8 * 8); break; } case 46: { /* RF4_CAUSE_3 */ z = (10 * 15); break; } case 47: { /* RF4_CAUSE_4 */ z = (15 * 15); p += 50; break; } case 48: { /* RF4_BO_ACID */ if (FLAG(bp_ptr, TR_IM_ACID)) break; z = ouch; p += 40; break; } case 49: { /* RF4_BO_ELEC */ if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = ouch; p += 20; break; } case 50: { /* RF4_BO_FIRE */ if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = ouch; p += 40; break; } case 51: { /* RF4_BO_COLD */ if (FLAG(bp_ptr, TR_IM_COLD)) break; z = ouch; p += 20; break; } case 52: { /* RF4_BO_POIS */ /* XXX XXX XXX */ break; } case 53: { /* RF4_BO_NETH */ z = ouch + 100; if (FLAG(bp_ptr, TR_RES_NETHER)) break; p += 200; break; } case 54: { /* RF4_BO_WATE */ z = ouch; p += 20; break; } case 55: { /* RF4_BO_MANA */ z = ouch; break; } case 56: { /* RF4_BO_PLAS */ z = ouch; p += 20; break; } case 57: { /* RF4_BO_ICEE */ z = ouch; p += 20; break; } case 58: { /* RF4_MISSILE */ z = ouch; break; } case 59: { /* RF4_SCARE */ p += 10; break; } case 60: { /* RF4_BLIND */ p += 10; break; } case 61: { /* RF4_CONF */ p += 10; break; } case 62: { /* RF4_SLOW */ p += 5; break; } case 63: { /* RF4_HOLD */ p += 20; break; } case 64: { /* RF5_HASTE */ p += 10 + bp_ptr->depth; break; } case 65: { /* RF5_XXX1X6 */ break; } case 66: { /* RF5_HEAL */ p += 10; break; } case 67: { /* RF5_XXX2X6 */ break; } case 68: { /* RF5_XXX3X6 */ break; } case 69: { /* RF5_XXX4X6 */ break; } case 70: { /* RF5_TELE_TO */ p += 20 + bp_ptr->depth; break; } case 71: { /* RF5_TELE_AWAY */ p += 10; break; } case 72: { /* RF5_TELE_LEVEL */ p += 50; break; } case 73: { /* RF5_XXX5 */ break; } case 74: { /* RF5_DARKNESS */ break; } case 75: { /* RF5_TRAPS */ p += 50; break; } case 76: { /* RF5_FORGET */ /* if you have lots of cash this is not very scary... just re-ID. */ if (bp_ptr->lev < 35) p += 500; else p += 50; break; } case 77: { /* RF5_XXX6X6 */ break; } case 78: { /* RF5_XXX7X6 */ break; } case 79: { /* RF5_XXX8X6 */ break; } case 80: { /* RF5_S_MONSTER */ p += 55; break; } case 81: { /* RF5_S_MONSTERS */ p += 30; break; } case 82: { /* RF5_S_ANT */ p += 15; break; } case 83: { /* RF5_S_SPIDER */ p += 25; break; } case 84: { /* RF5_S_HOUND */ p += 45; break; } case 85: { /* RF5_S_HYDRA */ p += 70; break; } case 86: { /* RF5_S_ANGEL */ p += 80; break; } case 87: { /* RF5_S_DEMON */ p += 80; break; } case 88: { /* RF5_S_UNDEAD */ p += 80; break; } case 89: { /* RF5_S_DRAGON */ p += 80; break; } case 90: { /* RF5_S_HI_UNDEAD */ p += 95; break; } case 91: { /* RF5_S_HI_DRAGON */ p += 95; break; } case 92: { /* RF5_S_WRAITH */ p += 95; break; } case 93: { /* RF5_S_UNIQUE */ p += 50; break; } } /* Things which hurt us alot need to be a concern */ if (ouch >= bp_ptr->chp / 2) ouch = ouch * 2; /* Notice damage */ return (p + z); } /* * Look at the screen and update the borg * * Note that all the "important" messages that occured after our last * action have been "queued" in a usable form. We must attempt to use * these messages to update our knowledge about the world, keeping in * mind that the world may have changed in drastic ways. * * Note that the "borg_t" variable corresponds *roughly* to player turns, * except that resting and "repeated" commands count as a single turn, * and "free" moves (including "illegal" moves, such as attempted moves * into walls, or tunneling into monsters) are counted as turns. * * Also note that "borg_t" is not incremented until the Borg is about to * do something, so nothing ever has a time-stamp of the current time. * * We rely on the fact that all "perma-lit" grids are memorized when * they are seen, so any grid on the current panel that appears "dark" * must not be perma-lit. * * We rely on the fact that all "objects" are memorized when they are * seen, so any grid on the current panel that appears "dark" must not * have an object in it. But it could have a monster which looks like * an object, but this is very rare. XXX XXX XXX * * XXX XXX XXX The basic problem with timestamping the monsters * and objects is that we often get a message about a monster, and so * we want to timestamp it, but then we cannot use the timestamp to * indicate that the monster has not been "checked" yet. Perhaps * we need to do something like give each monster a "moved" flag, * and clear all the flags to FALSE each turn before tracking. (?) * * Note that when two monsters of the same race are standing next to * each other, and they both move, such that the second monster ends * up where the first one began, we will incorrectly assume that the * first monster stayed still, and either the second monster moved * two spaces, or the second monster disappeared and a third monster * appeared, which is technically possible, if the first monster ate * the second, and then cloned the third. * * There is a problem with monsters which look like objects, namely, * they are assumed to be objects, and then if they leave line of * sight, they disappear, and the Borg assumes that they are gone, * when really they should be identified as monsters. * * XXX XXX Hack -- note the fast direct access to the screen. */ void borg_update(void) { int i, k; int hit_dist; cptr msg; cptr what; /*** Update the map ***/ /* Update the map */ borg_update_map(); /* Update the objects */ delete_dead_objects(); /* Assume I can shoot here */ successful_target = BORG_TARGET; /* Update the view */ borg_update_view(); /*** Track monsters ***/ /* New monsters near 'moved' monsters */ observe_kill_move(BORG_MON_NEW, BORG_MON_MOVE, 1); observe_kill_move(BORG_MON_NEW, BORG_MON_MOVE, 2); observe_kill_move(BORG_MON_NEW, BORG_MON_MOVE, 3); /* New monsters near 'old forgotten' monsters */ observe_kill_move(BORG_MON_NEW, BORG_MON_OLD, 1); observe_kill_move(BORG_MON_NEW, BORG_MON_OLD, 2); observe_kill_move(BORG_MON_NEW, BORG_MON_OLD, 3); /* Scan all the remaining 'old' monsters */ handle_old_mons(BORG_MON_OLD); handle_old_mons(BORG_MON_MOVE); /* Append remaining monsters to used list */ borg_append_mon_list(BORG_MON_USED, BORG_MON_NEW); borg_append_mon_list(BORG_MON_USED, BORG_MON_OLD); /* Get rid of moved monsters we have not tracked */ borg_wipe_mon(BORG_MON_MOVE); /* Append used monsters to 'old' list, and delete used monsters */ borg_append_mon_list(BORG_MON_OLD, BORG_MON_USED); /*** Handle messages ***/ /* Process messages */ for (i = 0; i < borg_msg_num; i++) { /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Note the message */ borg_note("# %s (+)", msg); } /* Process messages */ for (i = 0; i < borg_msg_num; i++) { /* Skip parsed messages */ if (borg_msg_use[i]) continue; /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Hack -- Handle "SELF" info */ if (prefix(msg, "SELF:")) { (void)borg_handle_self(what); borg_msg_use[i] = 1; } /* Handle "You feel..." */ else if (prefix(msg, "FEELING:")) { borg_feeling = atoi(what); borg_msg_use[i] = 1; } } /* Process messages */ for (i = 0; i < borg_msg_num; i++) { /* Skip parsed messages */ if (borg_msg_use[i]) continue; /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "You hit xxx." */ if (prefix(msg, "HIT:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 0)) > 0) { borg_msg_use[i] = 2; } } /* Handle "You miss xxx." */ else if (prefix(msg, "MISS:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 0)) > 0) { borg_msg_use[i] = 2; } } /* Handle "You have killed xxx." */ else if (prefix(msg, "KILL:")) { borg_count_death(what); borg_msg_use[i] = 2; /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "The xxx disappears!" via teleport other, and blinks away */ else if (prefix(msg, "BLINK:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 0)) > 0) { borg_delete_kill(k, "blinked"); borg_msg_use[i] = 2; } /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "xxx dies." */ else if (prefix(msg, "DIED:")) { borg_count_death(what); borg_msg_use[i] = 2; /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "xxx screams in pain." */ else if (prefix(msg, "PAIN:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 3)) > 0) { borg_msg_use[i] = 2; } /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "sleep" */ else if (prefix(msg, "STATE__FEAR:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 0)) > 0) { borg_msg_use[i] = 2; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__BOLD:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 0)) > 0) { borg_msg_use[i] = 2; } } else if (prefix(msg, "STATE_SLEEP:")) { /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } else if (prefix(msg, "STATE__FEAR:")) { /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } else if (prefix(msg, "STATE_CONFUSED:")) { /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } } /* Process messages */ /* getting distance to allow for 'hit's */ hit_dist = 1; for (i = 0; i < borg_msg_num; i++) { /* Skip parsed messages */ if (borg_msg_use[i]) continue; /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* if you have moved than do not count the monsters as unknown */ /* unless they are very far away */ if (prefix(msg, "SPELL_70") || prefix(msg, "SPELL_71")) { hit_dist = 100; break; } /* monsters move from earthquake */ if (prefix(msg, "QUAKE")) { hit_dist = 3; break; } } /* Process messages */ for (i = 0; i < borg_msg_num; i++) { /* Skip parsed messages */ if (borg_msg_use[i]) continue; /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "You hit xxx." */ if (prefix(msg, "HIT:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, hit_dist)) > 0) { borg_msg_use[i] = 3; } } /* Handle "You miss xxx." */ else if (prefix(msg, "MISS:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, hit_dist)) > 0) { borg_msg_use[i] = 3; } } /* Handle "You have killed xxx." */ else if (prefix(msg, "KILL:")) { borg_count_death(what); borg_msg_use[i] = 3; /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "The xxx disappears!" via teleport other, and blinks away */ else if (prefix(msg, "BLINK:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_x, g_y, 1)) > 0) { borg_delete_kill(k, "blinked"); borg_msg_use[i] = 3; } /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "xxx dies." */ else if (prefix(msg, "DIED:")) { borg_count_death(what); borg_msg_use[i] = 3; /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "xxx screams in pain." */ else if (prefix(msg, "PAIN:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 3; } /* Shooting (through darkness maybe) worked */ successful_target = BORG_TARGET; } /* Handle "xxx hits you." */ else if (prefix(msg, "HIT_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 1)) > 0) { borg_msg_use[i] = 3; } } /* Handle "xxx misses you." */ else if (prefix(msg, "MISS_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 1)) > 0) { borg_msg_use[i] = 3; } } /* Handle "sleep" */ else if (prefix(msg, "STATE_SLEEP:")) { /* Notice changes */ borg_sleep_kill(); borg_msg_use[i] = 3; } /* Handle "awake" */ else if (prefix(msg, "STATE_AWAKE:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 3; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__FEAR:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 3; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__BOLD:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 3; } } /* Hack -- Handle "spell" */ else if (prefix(msg, "SPELL_")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 3; } } } /*** Handle new levels ***/ /* Hack -- note new levels */ if (old_depth != bp_ptr->depth) { /* if we are not leaving town increment time since town clock */ if (!old_depth) borg_time_town = 0; else borg_time_town += borg_t - borg_began; /* Hack -- Restart the clock */ /* borg_t = 1000; */ /* reset our vault/unique check */ vault_on_level = FALSE; unique_on_level = FALSE; scaryguy_on_level = FALSE; /* reset our breeder flag */ breeder_level = FALSE; /* reset our need to see inviso clock */ need_see_inviso = 1; /* reset our 'shoot in the dark' flag */ successful_target = BORG_TARGET; /* When level was begun */ borg_began = borg_t; /* Not completed */ borg_completed = FALSE; /* New danger thresh-hold */ avoidance = bp_ptr->chp; /* Wipe the danger */ borg_danger_wipe = TRUE; /* Examine the world */ borg_do_spell = TRUE; borg_do_frame = TRUE; /* Enable some functions */ borg_do_destroy = TRUE; /* Allow Pets to stick close */ p_ptr->pet_follow_distance = PET_FOLLOW_ME; /* Mega-Hack -- Clear "call lite" stamp */ when_call_lite = 0; /* Mega-Hack -- Clear "wizard lite" stamp */ when_wizard_lite = 0; /* Mega-Hack -- Clear "detect traps" stamp */ when_detect_traps = 0; /* Mega-Hack -- Clear "detect doors" stamp */ when_detect_doors = 0; /* Mega-Hack -- Clear "detect walls" stamp */ when_detect_walls = 0; /* Mega-Hack -- Clear "detect evil" stamp */ when_detect_evil = 0; /* No goal yet */ goal = GOAL_NONE; /* Hack -- Clear "shop" goals */ goal_shop = -1; /* Do not use any stairs */ stair_less = FALSE; stair_more = FALSE; /* Hack -- cannot rise past town */ if (!bp_ptr->depth) goal_rising = FALSE; /* Assume not leaving the level */ goal_leaving = FALSE; /* Assume not fleeing the level */ goal_fleeing = FALSE; /* Assume not ignoring monsters */ goal_ignoring = FALSE; /* No known stairs */ track_less_num = 0; track_more_num = 0; /* No known glyph */ track_glyph_num = 0; /* No known steps */ track_step_num = 0; /* No known doors */ track_door_num = 0; /* No monsters here */ borg_kills_cnt = 0; borg_kills_nxt = 1; /* Forget old monsters */ (void)C_WIPE(borg_kills, BORG_KILLS_MAX, borg_kill); /* Fake goal location */ g_x = c_x; g_y = c_y; /* wipe out bad artifacts list */ bad_obj_n = -1; /* save once per level */ if (borg_flag_save) borg_save = TRUE; /* Save new depth */ old_depth = bp_ptr->depth; borg_times_twitch = 0; borg_escapes = 0; } /* Handle old level */ else { /* reduce GOI count. NOTE: do not reduce below 1. That is done */ /* when the spell is cast. */ if (borg_goi > 1) { borg_goi -= borg_game_ratio; } /* Count down to blast off */ if (goal_recalling > 1) { goal_recalling -= borg_game_ratio; /* dont let it get to 0 or borg will recast the spell */ if (goal_recalling <= 0) goal_recalling = 1; } /* when we need to cast this spell again */ if (borg_see_inv > 1) { borg_see_inv -= borg_game_ratio; } /* Reduce fear over time */ if (!(borg_t % 10)) { map_block *mb_ptr; MAP_ITT_START (mb_ptr) { if (mb_ptr->fear) mb_ptr->fear--; } MAP_ITT_END; } } /*** Handle messages ***/ /* Process messages */ for (i = 0; i < borg_msg_num; i++) { /* Skip parsed messages */ if (borg_msg_use[i]) continue; /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "xxx dies." */ if (prefix(msg, "DIED:")) { borg_count_death(what); borg_msg_use[i] = 4; } /* Handle "xxx screams in pain." */ else if (prefix(msg, "PAIN:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 4; } } /* Handle "xxx hits you." */ else if (prefix(msg, "HIT_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, hit_dist)) > 0) { borg_msg_use[i] = 4; } } /* Handle "xxx misses you." */ else if (prefix(msg, "MISS_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, hit_dist)) > 0) { borg_msg_use[i] = 4; } } /* Handle "sleep" */ else if (prefix(msg, "STATE_SLEEP:")) { /* Notice changes */ borg_sleep_kill(); borg_msg_use[i] = 4; } /* Handle "awake" */ else if (prefix(msg, "STATE_AWAKE:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 4; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__FEAR:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 4; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__BOLD:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 4; } } /* Hack -- Handle "spell" */ else if (prefix(msg, "SPELL_")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_x, c_y, 20)) > 0) { borg_msg_use[i] = 4; } } } /* Process messages */ for (i = 0; i < borg_msg_num; i++) { /* Skip parsed messages */ if (borg_msg_use[i]) continue; /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "xxx hits you." */ if (prefix(msg, "HIT_BY:")) { borg_fear_grid(what, c_x, c_y, 4 * ((bp_ptr->depth / 5) + 1), FALSE); borg_msg_use[i] = 5; } /* Handle "xxx misses you." */ else if (prefix(msg, "MISS_BY:")) { borg_fear_grid(what, c_x, c_y, 2 * ((bp_ptr->depth / 5) + 1), FALSE); borg_msg_use[i] = 5; } /* Hack -- Handle "spell" */ else if (prefix(msg, "SPELL_")) { borg_fear_grid(what, c_x, c_y, borg_fear_spell(atoi(msg + 6)), FALSE); borg_msg_use[i] = 5; } } /* Display messages */ for (i = 0; i < borg_msg_num; i++) { /* Get the message */ msg = borg_msg_buf + borg_msg_pos[i]; /* Final message */ borg_note("# %s (%d)", msg, borg_msg_use[i]); } /*** Various things ***/ /* Forget goals while "impaired" in any way */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.afraid || bp_ptr->status.image) goal = 0; /* Forget goals while "bleeding" in any way */ if (bp_ptr->status.weak || bp_ptr->status.poisoned || bp_ptr->status.cut || bp_ptr->status.stun || bp_ptr->status.heavy_stun) goal = 0; /* Forget goals when HP changes */ if (bp_ptr->chp < bp_ptr->oldhp) goal = 0; /* Save the hit points */ bp_ptr->oldhp = bp_ptr->chp; /* Forget failure */ borg_failure = FALSE; /* Forget the messages */ borg_msg_len = 0; borg_msg_num = 0; } /* * Handle various "important" messages * * Actually, we simply "queue" them for later analysis */ void borg_react(cptr msg, cptr buf) { int len; if (borg_dont_react) return; /* Note actual message */ borg_note("> %s", msg); /* Extract length of parsed message */ len = strlen(buf); /* Verify space */ if (borg_msg_num + 1 > borg_msg_max) { borg_oops("too many messages"); return; } /* Verify space */ if (borg_msg_len + len + 1 > borg_msg_siz) { borg_oops("too much messages"); return; } /* Assume not used yet */ borg_msg_use[borg_msg_num] = 0; /* Save the message position */ borg_msg_pos[borg_msg_num] = borg_msg_len; /* Save the message text */ strcpy(borg_msg_buf + borg_msg_len, buf); /* Advance the buf */ borg_msg_len += len + 1; /* Advance the pos */ borg_msg_num++; } /* * Notice that the player has moved */ void borg_player_move(int x, int y, vptr dummy) { /* Hack - ignore parameter */ (void) dummy; c_x = x; c_y = y; } /* * Sorting hook -- comp function -- see below * * We use "u" to point to an array of strings, and "v" to point to * an array of indexes, and we sort them together by the strings. */ static bool ang_sort_comp_hook_strings(vptr u, vptr v, int a, int b) { cptr *text = (cptr *)(u); s16b *what = (s16b *)(v); int cmp; /* Compare the two strings */ cmp = (strcmp(text[a], text[b])); /* Strictly less */ if (cmp < 0) return (TRUE); /* Strictly more */ if (cmp > 0) return (FALSE); /* Enforce "stable" sort */ return (what[a] <= what[b]); } /* * Sorting hook -- swap function -- see below * * We use "u" to point to an array of strings, and "v" to point to * an array of indexes, and we sort them together by the strings. */ static void ang_sort_swap_hook_strings(vptr u, vptr v, int a, int b) { cptr *text = (cptr *)(u); s16b *what = (s16b *)(v); cptr texttmp; s16b whattmp; /* Swap "text" */ texttmp = text[a]; text[a] = text[b]; text[b] = texttmp; /* Swap "what" */ whattmp = what[a]; what[a] = what[b]; what[b] = whattmp; } /* * Init this file. */ void borg_init_2(void) { int i; int size; s16b *what; cptr *text; /* Make the towns in the wilderness */ C_MAKE(borg_towns, borg_town_size, borg_town); /* Make the stores in the towns */ C_MAKE(borg_shops, borg_shop_size, borg_shop); /* Make the dungeons in the wilderness */ C_MAKE(borg_dungeons, borg_dungeon_size, borg_dungeon); /* Initialise los information */ borg_vinfo_init(); /* Allocate temp arrays */ C_MAKE(what, z_info->r_max, s16b); C_MAKE(text, z_info->r_max, cptr); /*** Message tracking ***/ /* No chars saved yet */ borg_msg_len = 0; /* Maximum buffer size */ borg_msg_siz = 4096; /* Allocate a buffer */ C_MAKE(borg_msg_buf, borg_msg_siz, char); /* No msg's saved yet */ borg_msg_num = 0; /* Maximum number of messages */ borg_msg_max = 256; /* Allocate array of positions */ C_MAKE(borg_msg_pos, borg_msg_max, s16b); /* Allocate array of use-types */ C_MAKE(borg_msg_use, borg_msg_max, s16b); /*** Parse "unique" monster names ***/ /* Start over */ size = 0; /* Collect "unique" monsters */ for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip non-unique monsters */ if (!FLAG(r_ptr, RF_UNIQUE)) continue; /* Use it */ text[size] = mon_race_name(r_ptr); what[size] = i; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook_strings; ang_sort_swap = ang_sort_swap_hook_strings; /* Sort */ ang_sort((void *) text, what, size); /* Save the size */ borg_unique_size = size; /* Allocate the arrays */ C_MAKE(borg_unique_text, borg_unique_size, cptr); C_MAKE(borg_unique_what, borg_unique_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) borg_unique_text[i] = text[i]; for (i = 0; i < size; i++) borg_unique_what[i] = what[i]; /*** Parse "normal" monster names ***/ /* Start over */ size = 0; /* Collect "normal" monsters */ for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip unique monsters */ if (FLAG(r_ptr, RF_UNIQUE)) continue; /* Use it */ text[size] = mon_race_name(r_ptr); what[size] = i; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook_strings; ang_sort_swap = ang_sort_swap_hook_strings; /* Sort */ ang_sort((void *) text, what, size); /* Save the size */ borg_normal_size = size; /* Allocate the arrays */ C_MAKE(borg_normal_text, borg_normal_size, cptr); C_MAKE(borg_normal_what, borg_normal_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) borg_normal_text[i] = text[i]; for (i = 0; i < size; i++) borg_normal_what[i] = what[i]; /* Free the arrays */ FREE(what); FREE((void *)text); } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg3.c0000644000000000000000000026454010250356275013521 0ustar rootroot/* File: borg3.c */ /* Purpose: Object and Spell routines for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg3.h" /* * This file helps the Borg deal with objects and spells. */ /* * Spell info */ borg_magic borg_magics[8][4][8]; /* Spell info, by realm/book/what */ borg_mind borg_minds[MINDCRAFT_MAX]; /* * Hack -- help analyze the magic * * The comments yield the "name" of the spell or prayer. * * If there is an "!" for entries that means the borg can't use it. */ static byte borg_magic_method[8][4][8] = { { /* 0 Realm 0 -- Non spell caster */ { /* Book0... (sval 0) */ BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ }, { /* Book1... (sval 1) */ BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ }, { /* Book0... (sval 2) */ BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ }, { /* Book3... (sval 3) */ BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* ! "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ , BORG_MAGIC_ICK /* "(blank)" */ } }, /* end of realm 0 */ { /* 1 Life Realm */ { /* Common Prayers (sval 0) */ BORG_MAGIC_NOP /* "Detect Evil" */ , BORG_MAGIC_NOP /* "Cure Light Wounds" */ , BORG_MAGIC_NOP /* "Bless" */ , BORG_MAGIC_NOP /* "Remove Fear" */ , BORG_MAGIC_NOP /* "Call Light" */ , BORG_MAGIC_NOP /* "Find Traps / Door" */ , BORG_MAGIC_NOP /* "Cure Medium Wounds" */ , BORG_MAGIC_NOP /* "Satisfy Hunger" */ ,}, { /* High Mass (sval 1) */ BORG_MAGIC_NOP /* "Remove Curse" */ , BORG_MAGIC_NOP /* "Cure Poison" */ , BORG_MAGIC_NOP /* "Cure Crit Wounds" */ , BORG_MAGIC_NOP /* "See Inv" */ , BORG_MAGIC_AIM /* "Holy Orb" */ , BORG_MAGIC_NOP /* "PFE" */ , BORG_MAGIC_NOP /* "Healing" */ , BORG_MAGIC_NOP /* ! "Rune of Protection" */ ,}, { /* Book of the Unicorn (sval 2) */ BORG_MAGIC_NOP /* "Exorcism" */ , BORG_MAGIC_NOP /* "Dispel Curse" */ , BORG_MAGIC_NOP /* "Disp Undead and Demon" */ , BORG_MAGIC_NOP /* ! "Day of Dove" */ , BORG_MAGIC_NOP /* "Dispel Evil" */ , BORG_MAGIC_NOP /* "Banishment" */ , BORG_MAGIC_NOP /* "Holy Word" */ , BORG_MAGIC_NOP /* ! "Warding True" */ }, { /* Blessings of the Grail (sval 3) */ BORG_MAGIC_NOP /* "Heroism" */ , BORG_MAGIC_NOP /* "Prayer" */ , BORG_MAGIC_NOP /* ! "Bless Weapon" */ , BORG_MAGIC_NOP /* "Restoration" */ , BORG_MAGIC_NOP /* "Healing True" */ , BORG_MAGIC_OBJ /* "Holy Vision" */ , BORG_MAGIC_NOP /* ! "Divine Intervent" */ , BORG_MAGIC_NOP /* "Holy Invuln" */ } }, /* endof Life Realm */ { /*2. Sorcery Realm */ { /* Beginners Handbook (sval 0) */ BORG_MAGIC_NOP /* "Detect Monster" */ , BORG_MAGIC_NOP /* "Phase Door" */ , BORG_MAGIC_NOP /* "Detect Doors & Traps" */ , BORG_MAGIC_NOP /* "Light Area" */ , BORG_MAGIC_AIM /* "Confuse Monster" */ , BORG_MAGIC_NOP /* "Teleport Self" */ , BORG_MAGIC_NOP /* "Sleep Monster" */ , BORG_MAGIC_OBJ /* "Recharging" */ }, { /* Master Sorcery (sval 1) */ BORG_MAGIC_NOP /* "Magic Map" */ , BORG_MAGIC_OBJ /* "Ident" */ , BORG_MAGIC_AIM /* "Slow Monster" */ , BORG_MAGIC_NOP /* "Mass Sleep " */ , BORG_MAGIC_AIM /* "Teleport Away" */ , BORG_MAGIC_NOP /* "Haste Self" */ , BORG_MAGIC_NOP /* "Detection True" */ , BORG_MAGIC_OBJ /* "*ID*" */ }, { /* Pattern Sorcery (sval 2) */ BORG_MAGIC_NOP /* ! "Detect Obj & treasure" */ , BORG_MAGIC_NOP /* ! "Detect Enchant" */ , BORG_MAGIC_ICK /* ! "Charm Mon" */ , BORG_MAGIC_AIM /* "Dimension Door" */ , BORG_MAGIC_NOP /* "Sense Minds" */ , BORG_MAGIC_NOP /* ! "Self Knowledge" */ , BORG_MAGIC_NOP /* "Teleport Level" */ , BORG_MAGIC_NOP /* "Word of Recall" */ }, { /* Grimoir of Power (sval 3) */ BORG_MAGIC_AIM /* "Stasis" */ , BORG_MAGIC_ICK /* ! "Telekinesis" */ , BORG_MAGIC_ICK /* ! "Explosive Rune" */ , BORG_MAGIC_NOP /* "Clairvoyance" */ , BORG_MAGIC_OBJ /* "*Enchant Weap" */ , BORG_MAGIC_OBJ /* "*Enchant Armor" */ , BORG_MAGIC_OBJ /* "Alchemy" */ , BORG_MAGIC_NOP /* "GOI" */ } }, /* End of Sorcery Realm */ { /* 3 Nature Realm */ { /* Call of the Wild (sval 0) */ BORG_MAGIC_NOP /* "Detect Creature" */ , BORG_MAGIC_NOP /* "First Aid" */ , BORG_MAGIC_NOP /* "Detect Trap / Door" */ , BORG_MAGIC_NOP /* "Foraging" */ , BORG_MAGIC_NOP /* "Daylight" */ , BORG_MAGIC_AIM /* ! "Animal Taming" */ , BORG_MAGIC_NOP /* "Resist Environment" */ , BORG_MAGIC_NOP /* "Cure Wound&Poison" */ }, { /* Nature Mastery (sval 1) */ BORG_MAGIC_AIM /* "Stone to Mud" */ , BORG_MAGIC_AIM /* "Lightning Bolt" */ , BORG_MAGIC_NOP /* "Nature Awareness" */ , BORG_MAGIC_AIM /* "Frost Bolt" */ , BORG_MAGIC_AIM /* "Ray of Sunlight" */ , BORG_MAGIC_NOP /* "Entangle" */ , BORG_MAGIC_ICK /* ! "Summon Animals" */ , BORG_MAGIC_NOP /* "Herbal Healing" */ }, { /* Nature Gifts (sval 2) */ BORG_MAGIC_NOP /* ! "Door Building" */ , BORG_MAGIC_NOP /* ! "Stair Building" */ , BORG_MAGIC_NOP /* "Stone Skin" */ , BORG_MAGIC_NOP /* "Resistance True" */ , BORG_MAGIC_NOP /* ! "Animal Friend" */ , BORG_MAGIC_OBJ /* "Stone Tell" */ , BORG_MAGIC_NOP /* ! "Wall of Stone" */ , BORG_MAGIC_OBJ /* ! "Protect From Corros." */ }, { /* Natures Wrath (sval 3) */ BORG_MAGIC_NOP /* "Earthquake" */ , BORG_MAGIC_NOP /* "Whirlwind" */ , BORG_MAGIC_AIM /* "Blizzard" */ , BORG_MAGIC_AIM /* "Lightning" */ , BORG_MAGIC_AIM /* "Whirpool" */ , BORG_MAGIC_NOP /* "Call Sunlight" */ , BORG_MAGIC_OBJ /* ! "Elemental Brand" */ , BORG_MAGIC_NOP /* "Natures Wrath" */ } }, /* end of Natural realm */ { /* 4.Chaos Realm */ { /* Sign of Chaos... (sval 0) */ BORG_MAGIC_AIM /* "Magic Missile" */ , BORG_MAGIC_NOP /* "Trap/Door Dest" */ , BORG_MAGIC_NOP /* "Flash of Light" */ , BORG_MAGIC_NOP /* ! "Touch of Conf" */ , BORG_MAGIC_NOP /* "ManaBurst" */ , BORG_MAGIC_AIM /* "Fire Bolt" */ , BORG_MAGIC_AIM /* "Fist of Force" */ , BORG_MAGIC_NOP /* "Teleport" */ }, { /* Chaos Mastery... (sval 1) */ BORG_MAGIC_ICK /* ! "Wonder" */ , BORG_MAGIC_AIM /* "Chaos Bolt" */ , BORG_MAGIC_NOP /* "Sonic Boom" */ , BORG_MAGIC_AIM /* "Doom Beam" */ , BORG_MAGIC_AIM /* "Fire Ball" */ , BORG_MAGIC_AIM /* "Teleport Other" */ , BORG_MAGIC_NOP /* "Word of Dest" */ , BORG_MAGIC_NOP /* "Invoke Logrus" */ }, { /* Chaos Channels (sval 2) */ BORG_MAGIC_AIM /* "Polymorph Other" */ , BORG_MAGIC_NOP /* "Chain Lightn" */ , BORG_MAGIC_OBJ /* "Arcane Binding" */ , BORG_MAGIC_AIM /* "Disintegration" */ , BORG_MAGIC_NOP /* ! "Alter Reality" */ , BORG_MAGIC_ICK /* ! "Polymorph Self" */ , BORG_MAGIC_ICK /* ! "Chaos Branding" */ , BORG_MAGIC_ICK /* ! "Summon Demon" */ }, { /* Armageddon Tome (sval 3) */ BORG_MAGIC_AIM /* "Gravity Beam" */ , BORG_MAGIC_AIM /* "Meteor Swarm" */ , BORG_MAGIC_NOP /* "Flame Strike" */ , BORG_MAGIC_NOP /* ! "Call Chaos" */ , BORG_MAGIC_AIM /* "Magic Rocket" */ , BORG_MAGIC_AIM /* "Mana Storm" */ , BORG_MAGIC_AIM /* "Breath Logrus" */ , BORG_MAGIC_NOP /* "Call Void" */ } }, /* end of Chaos Realm */ { /* 5. Death Realm */ { /* Black Prayers (sval 0) */ BORG_MAGIC_NOP /* "Detect Unlife" */ , BORG_MAGIC_AIM /* "Malediction" */ , BORG_MAGIC_NOP /* "Detect Evil" */ , BORG_MAGIC_AIM /* "Stinking Cloud" */ , BORG_MAGIC_AIM /* "Black Sleep" */ , BORG_MAGIC_NOP /* "Resist Poison" */ , BORG_MAGIC_AIM /* "Horrify" */ , BORG_MAGIC_AIM /* ! "Enslave Undead" */ }, { /* Black Mass (sval 1) */ BORG_MAGIC_AIM /* "Orb of Entropy" */ , BORG_MAGIC_AIM /* "Nether Bolt" */ , BORG_MAGIC_NOP /* "Terror" */ , BORG_MAGIC_AIM /* "Vamp Drain" */ , BORG_MAGIC_OBJ /* "Poison Brand" */ , BORG_MAGIC_NOP /* "Disp Good" */ , BORG_MAGIC_WHO /* "Genocide" */ , BORG_MAGIC_NOP /* "Restore Life" */ }, { /* Black Channels (sval 2) */ BORG_MAGIC_NOP /* "Berserk" */ , BORG_MAGIC_NOP /* ! "Invoke Spirits" */ , BORG_MAGIC_AIM /* "Dark Bolt" */ , BORG_MAGIC_NOP /* "Battle Frenzy" */ , BORG_MAGIC_AIM /* "Vamp True" */ , BORG_MAGIC_OBJ /* ! "Vamp Brand" */ , BORG_MAGIC_AIM /* "Dark Storm" */ , BORG_MAGIC_NOP /* "Mass Genocide" */ }, { /* Necronomicon (sval 3) */ BORG_MAGIC_AIM /* "Death Ray" */ , BORG_MAGIC_ICK /* ! "Raise the Dead" */ , BORG_MAGIC_OBJ /* "Esoteria" */ , BORG_MAGIC_NOP /* "Word of Death" */ , BORG_MAGIC_NOP /* "Evocation" */ , BORG_MAGIC_AIM /* "Hellfire" */ , BORG_MAGIC_NOP /* "Omnicide" */ , BORG_MAGIC_NOP /* ! "Wraithform" */ } }, /* end of Death Realm */ { /* 6 Trump Realm */ { /* Conjuring and Tricks (sval 0) */ BORG_MAGIC_NOP /* "Phase Door" */ , BORG_MAGIC_AIM /* "Mind Blast" */ , BORG_MAGIC_ICK /* ! "Shuffle" */ , BORG_MAGIC_ICK /* ! "Reset Recall" */ , BORG_MAGIC_NOP /* "Teleport Self" */ , BORG_MAGIC_AIM /* "Dimension Door" */ , BORG_MAGIC_NOP /* ! "Trump Spying" */ , BORG_MAGIC_AIM /* "Teleport Away" */ }, { /* Deck of Many Things (sval 1) */ BORG_MAGIC_ICK /* ! "Trump Object" */ , BORG_MAGIC_ICK /* ! "Trump Animal" */ , BORG_MAGIC_NOP /* ! "Phantasmal Servant" */ , BORG_MAGIC_ICK /* ! "Trump Monster" */ , BORG_MAGIC_ICK /* ! "Conjure Elemental" */ , BORG_MAGIC_NOP /* "Teleport Level" */ , BORG_MAGIC_NOP /* "Word of Recall" */ , BORG_MAGIC_NOP /* "Banish" */ }, { /* Trumps of Doom (sval 2) */ BORG_MAGIC_ICK /* ! "Joker Card" */ , BORG_MAGIC_ICK /* ! "Trump Spiders" */ , BORG_MAGIC_ICK /* ! "T. Reptiles" */ , BORG_MAGIC_ICK /* ! "T. Houdns" */ , BORG_MAGIC_ICK /* ! "T. Branding" */ , BORG_MAGIC_ICK /* ! "Living Trump" */ , BORG_MAGIC_NOP /* "Death Dealing" */ , BORG_MAGIC_ICK /* ! "T. Cyberdemon" */ }, { /* Five Aces (sval 3) */ BORG_MAGIC_NOP /* "T. Divination" */ , BORG_MAGIC_OBJ /* "T. Lore" */ , BORG_MAGIC_ICK /* ! "T. Undead" */ , BORG_MAGIC_ICK /* ! "T. Dragon" */ , BORG_MAGIC_ICK /* ! "Mass Trump" */ , BORG_MAGIC_ICK /* ! "T. Demon" */ , BORG_MAGIC_ICK /* ! "T. Ancient Dragon " */ , BORG_MAGIC_ICK /* ! "T. Greater Undead" */ } }, /* end of Trump Realm */ { /* 7 Arcane Realm */ { /* Cantrips (sval 0) */ BORG_MAGIC_AIM /* "Zap" */ , BORG_MAGIC_AIM /* ! "Wiz Lock" */ , BORG_MAGIC_NOP /* "Det Invis" */ , BORG_MAGIC_NOP /* "Det Mon" */ , BORG_MAGIC_NOP /* "Blink" */ , BORG_MAGIC_NOP /* "Light Area" */ , BORG_MAGIC_AIM /* "Trap/Door Dest" */ , BORG_MAGIC_NOP /* "Cure Light Wounds" */ }, { /* Minor Arcana (sval 1) */ BORG_MAGIC_NOP /* "Det Door/Trap" */ , BORG_MAGIC_NOP /* "Phlogiston" */ , BORG_MAGIC_NOP /* ! "Det Treasure" */ , BORG_MAGIC_NOP /* ! "Det Enchant" */ , BORG_MAGIC_NOP /* ! "Det Object" */ , BORG_MAGIC_NOP /* "Cure Poison" */ , BORG_MAGIC_NOP /* "Resist Cold" */ , BORG_MAGIC_NOP /* "Resist Fre" */ }, { /* Major Arcana (sval 2) */ BORG_MAGIC_NOP /* "Resist Elec" */ , BORG_MAGIC_NOP /* "Resist Acid" */ , BORG_MAGIC_NOP /* "Cure Med Wounds" */ , BORG_MAGIC_NOP /* "Teleport" */ , BORG_MAGIC_AIM /* "Stone to Mud" */ , BORG_MAGIC_AIM /* "Ray of Light" */ , BORG_MAGIC_NOP /* "Satisfy Hunger" */ , BORG_MAGIC_NOP /* "See Invis" */ }, { /* Manual of Mastery (sval 3) */ BORG_MAGIC_OBJ /* "Recharge" */ , BORG_MAGIC_NOP /* "Teleport Level" */ , BORG_MAGIC_OBJ /* "Ident" */ , BORG_MAGIC_AIM /* "Teleport Away" */ , BORG_MAGIC_AIM /* "Elemental Ball" */ , BORG_MAGIC_NOP /* "Detection" */ , BORG_MAGIC_NOP /* "Word of Recall" */ , BORG_MAGIC_NOP /* "Clairvoyance" */ } } /* end of Arcane Realm */ }; /* * Hack -- help analyze the magic * * The comments yield the "name" of the spell or prayer. * * Also, the leading letter in the comment indicates how we use the * spell or prayer, if at all, using "A" for "attack", "D" for "call * light" and "detection", "E" for "escape", "H" for healing, "O" for * "object manipulation", "F" for "terrain feature manipulation", * "X" for "never use this", and "!" for "soon to be handled". * * The value indicates how much we want to know the spell/prayer. A * rating of zero indicates that the spell/prayer is useless, and should * never be learned or used. A rating from 1 to 49 indicates that the * spell/prayer is worth some experience to use once, so we should study * (and use) it when we get bored in town. A rating from 50 to 99 means * that the spell/prayer should be learned as soon as possible (and used * when bored). * * XXX XXX XXX Verify ratings. */ static byte borg_magic_rating[8][4][8] = { { /* Null Realm */ { /* Book0... (sval 0) */ 0 /* ! "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ }, { /* Book1... (sval 1) */ 0 /* "(blank)" */ , 0 /* ! "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ }, { /* Book2... (sval 2) */ 0 /* "(blank)" */ , 0 /* ! "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ }, { /* Book3... (sval 3) */ 0 /* ! "(blank)" */ , 0 /* "(blank)" */ , 0 /* ! "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ , 0 /* "(blank)" */ } }, /* end of Null Realm */ { /* Life Realm */ { /* Common Prayers (sval 0) */ 85 /* "Detect Evil" */ , 55 /* "Cure Light Wounds" */ , 85 /* "Bless" */ , 35 /* "Remove Fear" */ , 35 /* "Call Light" */ , 75 /* "Find Traps & Doors" */ , 65 /* "Cure Medium Wounds" */ , 85 /* "Satisfy Hunger" */ }, { /* High Mass (sval 1) */ 65 /* "Remove Curse" */ , 65 /* "Cure Poison" */ , 85 /* "Cure Crit Wounds" */ , 55 /* "See Invis" */ , 95 /* "Holy Orb" */ , 85 /* "Prot/Evil" */ , 65 /* "Heal 300" */ , 0 /* "Glyph" */ }, { /* Book of the Unicorn (sval 2) */ 65 /* "Exorcism" */ , 65 /* "Dispel Curse" */ , 55 /* "Dispel Demon" */ , 0 /* "Day of Dove" */ , 65 /* "Dispel Evil" */ , 55 /* "Banishment" */ , 65 /* "Holy Word" */ , 0 /* "Warding True" */ }, { /* Blessings of the Grail (sval 3) */ 55 /* "Heroism" */ , 65 /* "Prayer" */ , 0 /* "Bless Weapon" */ , 55 /* "Restoration" */ , 65 /* "Healing 2000" */ , 55 /* "Holy Vision" */ , 0 /* "Divine Intervent" */ , 55 /* "Holy Invuln" */ } }, /* end of Life Magic */ { /* Sorcery Realm */ { /* Beginners Handbook (sval 0) */ 95 /* "Detect Monsters" */ , 85 /* "Phase Door" */ , 65 /* "Detect Door" */ , 85 /* "Light Area" */ , 75 /* "Confuse Monster" */ , 75 /* "Teleport Selft" */ , 65 /* "Sleep Monster" */ , 65 /* "Recharging" */ }, { /* Master Sorcery (sval 1) */ 55 /* "Magic Map" */ , 85 /* "Identify" */ , 55 /* "Slow Monster" */ , 65 /* "Mass Sleep" */ , 95 /* "Teleport Away" */ , 55 /* "Haste Self" */ , 85 /* "Detection True" */ , 75 /* "*Identify*" */ }, { /* Pattern Sorcery (sval 2) */ 0 /* "Detect Obj/Treasure" */ , 0 /* "Detect Enchantment" */ , 0 /* "Charm Monster" */ , 65 /* "Dimension Door" */ , 65 /* "Sense Minds" */ , 0 /* "Self Knowledge" */ , 65 /* "Teleport Level" */ , 65 /* "Word of Recall" */ }, { /* Grimoir of Power (sval 3) */ 55 /* "Stasis" */ , 0 /* "Telekinesis" */ , 0 /* "Explosive Rune" */ , 65 /* "Clairvoyance" */ , 55 /* "Enchant Weap" */ , 55 /* "Enchant Armour" */ , 1 /* "Alchemy" */ , 95 /* "GOI" */ } }, /* end of Sorcery Realm */ { /* 3 Nature Realm */ { /* Call of the Wild (sval 0) */ 65 /* "Detect Creature" */ , 65 /* "First Aid" */ , 55 /* "Detect Trap/Door" */ , 75 /* "Foraging" */ , 75 /* "Daylight" */ , 0 /* "Animal Taming" */ , 75 /* "Resist Environment" */ , 65 /* "Cure Wound&Poison" */ }, { /* Nature Mastery (sval 1) */ 55 /* "Stone to Mud" */ , 65 /* "Lightning Bolt" */ , 65 /* "Nature Awareness" */ , 65 /* "Frost Bolt" */ , 65 /* "Ray of Sunlight" */ , 65 /* "Entangle" */ , 0 /* "Summon Animals" */ , 65 /* "Herbal Healing" */ }, { /* Nature Gifts (sval 2) */ 0 /* "Door Building" */ , 0 /* "Stair Building" */ , 65 /* "Stone Skin" */ , 65 /* "Resistance True" */ , 0 /* "Animal Friend" */ , 65 /* "Stone Tell" */ , 0 /* "Wall of Stone" */ , 0 /* "Protect From Corros." */ }, { /* Natures Wrath (sval 3) */ 65 /* "Earthquake" */ , 65 /* "Whirlwind" */ , 65 /* "Blizzard" */ , 65 /* "Lightning" */ , 65 /* "Whirpool" */ , 65 /* "Call Sunlight" */ , 0 /* "Elemental Brand" */ , 65 /* "Natures Wrath" */ } }, /* end of Natural realm */ { /* 4.Chaos Realm */ { /* Sign of Chaos... (sval 0) */ 95 /* "Magic Missile" */ , 65 /* "Trap/Door Dest" */ , 75 /* "Flash of Light" */ , 0 /* "Touch of Conf" */ , 65 /* "ManaBurst" */ , 65 /* "Fire Bolt" */ , 65 /* "Fist of Force" */ , 75 /* "Teleport" */ }, { /* Chaos Mastery... (sval 1) */ 0 /* "Wonder" */ , 65 /* "Chaos Bolt" */ , 65 /* "Sonic Boom" */ , 65 /* "Doom Beam" */ , 65 /* "Fire Ball" */ , 65 /* "Teleport Other" */ , 65 /* "Word of Dest" */ , 55 /* "Invoke Logrus" */ }, { /* Chaos Channels (sval 2) */ 45 /* "Polymorph Other" */ , 65 /* "Chain Lightn" */ , 65 /* "Arcane Binding" */ , 65 /* "Disintegration" */ , 0 /* "Alter Reality" */ , 0 /* "Polymorph Self" */ , 0 /* "Chaos Branding" */ , 0 /* "Summon Demon" */ }, { /* Armageddon Tome (sval 3) */ 65 /* "Gravity Beam" */ , 65 /* "Meteor Swarm" */ , 65 /* "Flame Strike" */ , 0 /* "Call Chaos" */ , 75 /* "Magic Rocket" */ , 75 /* "Mana Storm" */ , 65 /* "Breath Logrus" */ , 65 /* "Call Void" */ } }, /* end of Chaos Realm */ { /* 5. Death Realm */ { /* Black Prayers (sval 0) */ 65 /* "Detect Unlife" */ , 75 /* "Maledition" */ , 75 /* "Detect Evil" */ , 75 /* "Stinking Cloud" */ , 65 /* "Black Sleep" */ , 65 /* "Resist Poison" */ , 65 /* "Horrify" */ , 0 /* "Enslave Undead" */ }, { /* Black Mass (sval 1) */ 70 /* "Orb of Entropy" */ , 65 /* "Nether Bolt" */ , 50 /* "Terror" */ , 65 /* "Vamp Drain" */ , 55 /* "Poison Brand" */ , 65 /* "Disp Good" */ , 65 /* "Genocide" */ , 65 /* "Restore Life" */ }, { /* Black Channels (sval 2) */ 65 /* "Berserk" */ , 0 /* "Invoke Spirits" */ , 65 /* "Dark Bolt" */ , 85 /* "Battle Frenzy" */ , 65 /* "Vamp True" */ , 0 /* "Vamp Brand" */ , 65 /* "Dark Storm" */ , 65 /* "Mass Genocide" */ }, { /* Necronomicon (sval 3) */ 65 /* "Death Ray" */ , 0 /* "Raise the Dead" */ , 75 /* "Esoteria" */ , 65 /* "Word of Death" */ , 65 /* "Evocation" */ , 65 /* "Hellfire" */ , 65 /* "Omnicide" */ , 55 /* "Wraithform" */ } }, /* end of Death Realm */ { /* Trump Realm */ { /* Trump Magic (sval 0) */ 95 /* "Phase Door" */ , 85 /* "Mind Blast " */ , 0 /* "Shuffle" */ , 0 /* "Reset Recall" */ , 75 /* "Tlelport Self" */ , 65 /* "Dimension Door " */ , 0 /* "Trump Spying " */ , 70 /* "Teleport Away " */ }, { /* Deck of Many Things (sval 1) */ 0 /* "Trump Object " */ , 0 /* "Trump animal " */ , 0 /* "Phantasmal Servant " */ , 0 /* "Trump Monster " */ , 0 /* "Conjure Elemental " */ , 50 /* "Teleport Level " */ , 65 /* "Word of recall " */ , 65 /* "Banishment" */ }, { /* Trump of Doom (sval 2) */ 0 /* "Joker Card " */ , 0 /* "Trump Spiders " */ , 0 /* "Trump Reptiles " */ , 0 /* "Trump Hounds " */ , 0 /* "Trump Branding " */ , 0 /* "Living Trump " */ , 55 /* "Death Dealing " */ , 0 /* "Trump Cyberdemon " */ }, { /* Five Aces (sval 3) */ 45 /* "Trump Divination " */ , 45 /* "Trump Lore " */ , 0 /* "Trump Undead " */ , 0 /* "Trump Dragon " */ , 0 /* "Mass Trump " */ , 0 /* "Trump Demon " */ , 0 /* "Trump Ancient Dragon " */ , 0 /* "Trump Greater Undead " */ } }, /* end of Trump Realm */ { /* 7 Arcane Realm */ { /* Cantrips (sval 0) */ 85 /* "Zap" */ , 0 /* "Wiz Lock" */ , 75 /* "Det Invis" */ , 75 /* "Det Mon" */ , 75 /* "Blink" */ , 75 /* "Light Area" */ , 85 /* "Trap/Door Dest" */ , 75 /* "Cure Light Wounds" */ }, { /* Minor Arcana (sval 1) */ 75 /* "Det Door/Trap" */ , 75 /* "Phlogiston" */ , 75 /* "Det Treasure" */ , 75 /* "Det Enchant" */ , 75 /* "Det Object" */ , 75 /* "Cure Poison" */ , 75 /* "Resist Cold" */ , 75 /* "Resist Fre" */ }, { /* Major Arcana (sval 2) */ 75 /* "Resist Elec" */ , 75 /* "Resist Acid" */ , 75 /* "Cure Med Wounds" */ , 75 /* "Teleport" */ , 85 /* "Stone to Mud" */ , 85 /* "Ray of Light" */ , 75 /* "Satisfy Hunger" */ , 75 /* "See Invis" */ }, { /* Manual of Mastery (sval 3) */ 75 /* "Recharge" */ , 75 /* "Teleport Level" */ , 85 /* "Ident" */ , 85 /* "Teleport Away" */ , 70 /* "Elemental Ball" */ , 75 /* "Detection" */ , 75 /* "Word of Recall" */ , 75 /* "Clairvoyance" */ } } /* end of Arcane Realm */ }; /* Recognition list for the activations. Should be in sync with BORG_ACT_* */ static cptr borg_activation[] = { "", "illumination", "light area", "magic mapping and illumination", "magic mapping and light area", "magic mapping", "dangerous clairvoyance", "word of recall", "dangerous clairvoyance and recall", "protection from evil", "haste self", "speed", "heal (1000)", "curing, heroism and heal (777)", "heal (700)", "heavenly blessing and heal (500)", "genocide", "trap and door destruction", "detection", "create food", "resistance", "resist elements", "recharg", "teleport every", "teleport (100)", "restore life levels", "restore stats and life levels", "remove fear", "remove fear and cure poison", "a getaway", "phase door", "mass genocide", "cure wounds", "remove fear and heal", "teleport away", "identify", "probing, detection", "identify true", "heal (45)", "dimension door", "alchemy", "satisfy hunger", "restore stats", "telepathy", "heroism (", "berserk", "bless", "resist acid", "resist fire", "resist cold", "resist lightning", "resist poison", "wraith form", "invulnerability", "detect evil", "detect monsters", "detect traps and doors", "remove curse", "dispel curse", "detect objects", "self knowledge", "teleport level", "create doors", "create stairs", "alter reality", "phase door", "stone to mud", "fire branding", "borg_act_max" }; /* * Return the slot that items of the given type are wielded into * * Note that "rings" are tough because there are two slots * * Returns "-1" if the item cannot (or should not) be wielded */ int borg_wield_slot(list_item *l_ptr) { if ((l_ptr->tval == TV_SWORD) || (l_ptr->tval == TV_POLEARM) || (l_ptr->tval == TV_HAFTED) || (l_ptr->tval == TV_DIGGING)) return (EQUIP_WIELD); if ((l_ptr->tval == TV_DRAG_ARMOR) || (l_ptr->tval == TV_HARD_ARMOR) || (l_ptr->tval == TV_SOFT_ARMOR)) return (EQUIP_BODY); if (l_ptr->tval == TV_SHIELD) return (EQUIP_ARM); if ((l_ptr->tval == TV_CROWN) || (l_ptr->tval == TV_HELM)) return (EQUIP_HEAD); if (l_ptr->tval == TV_BOW) return (EQUIP_BOW); if (l_ptr->tval == TV_RING) return (EQUIP_LEFT); if (l_ptr->tval == TV_AMULET) return (EQUIP_NECK); if (l_ptr->tval == TV_LITE) return (EQUIP_LITE); if (l_ptr->tval == TV_CLOAK) return (EQUIP_OUTER); if (l_ptr->tval == TV_GLOVES) return (EQUIP_HANDS); if (l_ptr->tval == TV_BOOTS) return (EQUIP_FEET); /* No slot available */ return (-1); } /* * Find the index of the object_kind with the given tval and sval */ object_kind *borg_get_kind(int tval, int sval) { int k; int num = 0; object_kind *kb_ptr = &k_info[0]; /* Look for it */ for (k = 1; k < z_info->k_max; k++) { object_kind *k_ptr = &k_info[k]; /* Require correct tval */ if (k_ptr->tval != tval) continue; /* Found a match */ if (k_ptr->sval == sval) return (k_ptr); /* Ignore illegal items */ if (sval != SV_ANY) continue; /* Apply the randomizer */ if (!one_in_(++num)) continue; /* Use this value */ kb_ptr = k_ptr; } /* Failure? */ if (sval != SV_ANY) { /* Oops */ borg_note("No object (%d,%d)", tval, sval); } return (kb_ptr); } /* * Find the slot of an item with the given tval/sval, if available. * Given multiple choices, choose the item with the largest "pval". * Given multiple choices, choose the smallest available pile. */ list_item *borg_slot(int tval, int sval) { int i; object_kind *k_ptr; /* Scan the pack */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip un-aware items */ if (!l_ptr->k_idx) continue; k_ptr = &k_info[l_ptr->k_idx]; /* Require correct tval */ if (k_ptr->tval != tval) continue; /* Require correct sval */ if (k_ptr->sval != sval) continue; /* Hack - Prefer the first match, it is sorted nicely already */ return (l_ptr); } /* Done */ return (NULL); } /* * Return the slot of an item with the given tval/sval, if available. * The first available is returned. The search is started from * This way with repeated calls the second pile of an item can be found. */ int borg_slot_from(int tval, int sval, int from) { int i; /* Scan the pack */ for (i = from; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip un-aware items */ if (!l_ptr->k_idx) continue; /* Continue looking for the right tval */ if (l_ptr->tval != tval) continue; /* Require correct sval */ if (k_info[l_ptr->k_idx].sval != sval) continue; /* This is what was looked for */ return (i); } /* Object not found */ return (-1); } /* * Get the index of an item so we can send commands to the game */ int look_up_index(list_item *l_ptr) { int i; /* Scan inventory */ for (i = 0; i < inven_num; i++) { if (&inventory[i] == l_ptr) return (i); } /* Paranoia */ borg_oops("Trying to find invalid object!"); return (-1); } /* Should the borg *id* this item? */ bool borg_obj_star_id_able(list_item *l_ptr) { /* Is there an object at all? */ if (!l_ptr) return (FALSE); /* Demand that the item is identified */ if (!borg_obj_known_p(l_ptr)) return (FALSE); /* Some non-ego items should be *id'ed too */ if (l_ptr->tval == TV_SHIELD && k_info[l_ptr->k_idx].sval == SV_DRAGON_SHIELD) return (TRUE); if (l_ptr->tval == TV_HELM && k_info[l_ptr->k_idx].sval == SV_DRAGON_HELM) return (TRUE); if (l_ptr->tval == TV_CLOAK && k_info[l_ptr->k_idx].sval == SV_SHADOW_CLOAK) return (TRUE); if (l_ptr->tval == TV_RING && k_info[l_ptr->k_idx].sval == SV_RING_LORDLY) return (TRUE); /* not an ego object */ if (!borg_obj_is_ego_art(l_ptr)) return (FALSE); /* Artifacts */ if (KN_FLAG(l_ptr, TR_INSTA_ART)) return (TRUE); /* Weapons */ if (streq(l_ptr->xtra_name, "(Holy Avenger)")) return (TRUE); if (streq(l_ptr->xtra_name, "(Defender)")) return (TRUE); if (streq(l_ptr->xtra_name, "(Blessed)")) return (TRUE); if (streq(l_ptr->xtra_name, "of Westernesse")) return (TRUE); if (streq(l_ptr->xtra_name, "of Slay Dragon")) return (TRUE); if (streq(l_ptr->xtra_name, "of *Slay* Dragon")) return (TRUE); if (streq(l_ptr->xtra_name, "(Chaotic)")) return (TRUE); if (streq(l_ptr->xtra_name, "of Slaying")) return (TRUE); if (streq(l_ptr->xtra_name, "(Vampiric)")) return (TRUE); if (streq(l_ptr->xtra_name, "(Trump Weapon)")) return (TRUE); if (streq(l_ptr->xtra_name, "(Pattern Weapon)")) return (TRUE); /* Bow */ if (streq(l_ptr->xtra_name, "of Might")) return (TRUE); /* Armour */ if (streq(l_ptr->xtra_name, "of Permanence")) return (TRUE); if (streq(l_ptr->xtra_name, "of Resistance")) return (TRUE); if (streq(l_ptr->xtra_name, "of Elvenkind")) return (TRUE); /* Hat */ if (streq(l_ptr->xtra_name, "of the Magi")) return (TRUE); if (streq(l_ptr->xtra_name, "of Lordliness")) return (TRUE); if (streq(l_ptr->xtra_name, "of Seeing")) return (TRUE); /* Cloak */ if (streq(l_ptr->xtra_name, "of Aman")) return (TRUE); /* Any object that reaches here has nothing interesting to *id* */ return (FALSE); } /* This function (copied from dungeon.c) delivers the chance for pseudo-id. */ long borg_calc_pseudo(void) { long difficulty; /* Based on race get the basic feel factor. */ switch (borg_class) { case CLASS_WARRIOR: { /* Good (heavy) sensing */ difficulty = 9000L; /* Done */ break; } case CLASS_MAGE: case CLASS_HIGH_MAGE: { /* Very bad (light) sensing */ difficulty = 240000L; /* Done */ break; } case CLASS_PRIEST: { /* Good (light) sensing */ difficulty = 10000L; /* Done */ break; } case CLASS_ROGUE: { /* Okay sensing */ difficulty = 20000L; /* Done */ break; } case CLASS_RANGER: { /* Bad (heavy) sensing */ difficulty = 95000L; /* Done */ break; } case CLASS_PALADIN: { /* Bad (heavy) sensing */ difficulty = 77777L; /* Done */ break; } case CLASS_WARRIOR_MAGE: { /* Bad sensing */ difficulty = 75000L; /* Done */ break; } case CLASS_MINDCRAFTER: { /* Bad sensing */ difficulty = 55000L; /* Done */ break; } case CLASS_CHAOS_WARRIOR: { /* Bad (heavy) sensing */ difficulty = 80000L; /* Done */ break; } case CLASS_MONK: { /* Okay sensing */ difficulty = 20000L; /* Done */ break; } default: { /* Paranoia */ difficulty = 0; } } /* Factor in the sensing ability */ difficulty /= MAX(bp_ptr->skill_sns, 1); /* Rescale larger by a facter of 25 */ difficulty *= 25; /* Sensing gets better as you get more experienced */ difficulty /= p_ptr->lev * p_ptr->lev + 40; /* Give the answer */ return (difficulty); } /* * Determine if an item is "probably" worthless * * This (very heuristic) function is a total hack, designed only to prevent * a very specific annoying situation described below. * * Note that a "cautious" priest (or low level mage/ranger) will leave town * with a few identify scrolls, wander around dungeon level 1 for a few turns, * and use all of the scrolls on leather gloves and broken daggers, and must * then return to town for more scrolls. This may repeat indefinitely. * * The problem is that some characters (priests, mages, rangers) never get an * "average" feeling about items, and have no way to keep track of how long * they have been holding a given item for, so they cannot even attempt to * gain knowledge from the lack of "good" or "cursed" feelings. But they * cannot afford to just identify everything they find by using scrolls of * identify, because, in general, some items are, on average, "icky", and * not even worth the price of a new scroll of identify. */ bool borg_worthless_item(list_item *l_ptr) { int slot; int sval; list_item *q_ptr; /* Is this item for real? */ if (!l_ptr) return (FALSE); /* pick up the items sval */ sval = k_info[l_ptr->k_idx].sval; /* This item needs identification first */ if (!sval) return (FALSE); /* Discard some junk items */ switch (l_ptr->tval) { case TV_RING: { if (sval <= SV_RING_TELEPORTATION) return (TRUE); break; } case TV_AMULET: { if (sval <= SV_AMULET_TELEPORT) return (TRUE); break; } case TV_STAFF: { if (sval == SV_STAFF_DARKNESS && !FLAG(bp_ptr, TR_HURT_LITE)) return (TRUE); if (sval >= SV_STAFF_SLOWNESS && sval <= SV_STAFF_SUMMONING) return (TRUE); break; } case TV_WAND: { if (sval == SV_WAND_CLONE_MONSTER) return (TRUE); if (sval == SV_WAND_HASTE_MONSTER) return (TRUE); if (sval == SV_WAND_HEAL_MONSTER) return (TRUE); break; } } /* Just checking */ if (streq(l_ptr->o_name, "")) return (FALSE); /* If this item has been pseudo id'd with boring results */ if (strstr(l_ptr->o_name, "{average") || strstr(l_ptr->o_name, "{cursed") || strstr(l_ptr->o_name, "{bad") || strstr(l_ptr->o_name, "{broken") || strstr(l_ptr->o_name, "{dubious") || strstr(l_ptr->o_name, "{worthless")) return (TRUE); /* items that are terrible/excellent/special/tainted need ID */ if (strstr(l_ptr->o_name, "{special") || strstr(l_ptr->o_name, "{terrible") || strstr(l_ptr->o_name, "{excellent") || strstr(l_ptr->o_name, "{tainted")) return (FALSE); /* If the item is good, check if the borg already has better */ if (strstr(l_ptr->o_name, "{good")) { /* Obtain the slot of the suspect item */ slot = borg_wield_slot(l_ptr); /* Obtain my equipped item in the slot */ q_ptr = &equipment[slot]; /* Is the equipped item an ego or artifact? */ if (q_ptr->k_idx && (borg_obj_is_ego_art(q_ptr) || (streq(q_ptr->o_name, "") && (strstr(q_ptr->o_name, "{special") || strstr(q_ptr->o_name, "{terrible") || strstr(q_ptr->o_name, "{excellent") || strstr(q_ptr->o_name, "{tainted"))))) return (TRUE); } /* Is there something known about this item? */ if (!l_ptr->k_idx) return (FALSE); /* If your pseudo capabilities are good then wait for pseudo id */ if (borg_calc_pseudo() < 100) return (FALSE); switch (l_ptr->tval) { /* Swords */ case TV_SWORD: return (sval == SV_BROKEN_DAGGER || sval == SV_BROKEN_SWORD || sval == SV_DAGGER); /* Hafted */ case TV_HAFTED: return (sval == SV_CLUB || sval == SV_WHIP); /* Sling */ case TV_BOW: return (sval == SV_SLING); /* Rags and Robes */ case TV_SOFT_ARMOR: return (sval == SV_FILTHY_RAG || sval == SV_SOFT_LEATHER_ARMOR || sval == SV_SOFT_STUDDED_LEATHER || sval == SV_ROBE); /* Cloak */ case TV_CLOAK: return (sval == SV_CLOAK); /* Leather Gloves */ case TV_GLOVES: return (sval == SV_SET_OF_LEATHER_GLOVES); /* Helmet */ case TV_HELM: return (sval == SV_HARD_LEATHER_CAP); /* This item needs identification */ default: return (FALSE); } } /* Refuel a torch with the minimal torch */ static bool borg_refuel_torch(void) { int slot, b_slot = -1, fuel = 5001; /* Cast phlogiston */ if (borg_spell_fail(REALM_ARCANE, 1, 1, 40)) return (TRUE); /* Look for the minimal torch */ for (slot = 0; slot < inven_num; slot++) { list_item *l_ptr = &inventory[slot]; /* Must be a light */ if (l_ptr->tval != TV_LITE) continue; /* Must be a torch */ if (k_info[l_ptr->k_idx].sval != SV_LITE_TORCH) continue; /* Ignore torches with the most fuel */ if (l_ptr->timeout >= fuel) continue; /* Is this an ego_torch? */ if (borg_obj_is_ego_art(l_ptr)) continue; /* My favorite torch */ b_slot = slot; fuel = l_ptr->timeout; } /* None available */ if (b_slot == -1) return (FALSE); /* Log the message */ borg_note("# Refueling with %s.", inventory[b_slot].o_name); /* Perform the action */ borg_keypress('F'); borg_keypress(I2A(b_slot)); /* Success */ return (TRUE); } /* Refuel a lantern */ static bool borg_refuel_lantern(void) { int slot, b_slot = -1, fuel = 15001; /* Cast phlogiston */ if (borg_spell_fail(REALM_ARCANE, 1, 1, 40)) return (TRUE); /* Loop through the inventory backwards */ for (slot = inven_num - 1; slot >= 0; slot--) { list_item *l_ptr = &inventory[slot]; /* Maybe fuel with a Lantern? */ if (l_ptr->tval == TV_LITE && k_info[l_ptr->k_idx].sval == SV_LITE_LANTERN) { /* Ignore lanterns with no fuel */ if (l_ptr->timeout == 0) continue; /* Ignore lanterns with the most fuel */ if (l_ptr->timeout >= fuel) continue; /* My favorite lantern */ b_slot = slot; fuel = l_ptr->timeout; } else { /* Maybe fuel with a flask? */ if (l_ptr->tval == TV_FLASK) { /* Get out of the loop */ break; } } } /* b_slot holds best lantern, slot holds flask, is there one of either? */ if (b_slot == -1 && slot == -1) return (FALSE); /* Found no lantern but a flask */ if (b_slot == -1) b_slot = slot; /* Log the message */ borg_note("# Refueling with %s.", inventory[b_slot].o_name); /* Perform the action */ borg_keypress('F'); borg_keypress(I2A(b_slot)); /* Success */ return (TRUE); } /* * Determines whether the borg has a refuelable lightsource and calls the * appropriate subroutine */ bool borg_refuel(void) { list_item *l_ptr = &equipment[EQUIP_LITE]; /* Must first wield something before one can refuel */ if (!l_ptr->k_idx) return (FALSE); /* Is there the need to refuel? */ if (l_ptr->timeout > 1000) return (FALSE); /* What sort of light is this */ switch (k_info[l_ptr->k_idx].sval) { case SV_LITE_LANTERN: return (borg_refuel_lantern()); case SV_LITE_TORCH: return (borg_refuel_torch()); /* Whatever it is, the borg can't light it */ default: return (FALSE); } } /* * Hack -- attempt to eat the given food (by sval) */ bool borg_eat_food(int sval) { int slot; /* Look for that food */ slot = borg_slot_from(TV_FOOD, sval, 0); /* None available */ if (slot == -1) return (FALSE); /* Log the message */ borg_note("# Eating %s. (%c)", inventory[slot].o_name, I2A(slot)); /* Perform the action */ borg_keypress('E'); borg_keypress(I2A(slot)); /* Success */ return (TRUE); } static s32b when_last_quaff = 0; /* * Quaff a potion of cure critical wounds. This is a special case * for several reasons. * 1) it is usually the only healing potion we have on us * 2) we should try to conserve a couple for when we really need them * 3) if we are burning through them fast we should probably teleport out of * the fight. * 4) When it is the only/best way out of danger, drink away */ bool borg_quaff_crit(bool no_check) { if (no_check) { if (borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_quaff_potion(SV_POTION_CURING)) { when_last_quaff = borg_t; return (TRUE); } return (FALSE); } /* Save the last two for when we really need them */ if (bp_ptr->able.ccw < 2) return FALSE; /* Avoid drinking CCW twice in a row */ if (when_last_quaff > (borg_t - 4) && when_last_quaff <= borg_t && (randint0(100) < 75)) return FALSE; if (borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_quaff_potion(SV_POTION_CURING)) { when_last_quaff = borg_t; return (TRUE); } return (FALSE); } /* * Hack -- attempt to quaff the given potion (by sval) */ bool borg_quaff_potion(int sval) { int slot; /* Look for that potion */ slot = borg_slot_from(TV_POTION, sval, 0); /* None available */ if (slot == -1) return (FALSE); /* Log the message */ borg_note("# Quaffing %s. (%c)", inventory[slot].o_name, I2A(slot)); /* Perform the action */ borg_keypress('q'); borg_keypress(I2A(slot)); /* Success */ return (TRUE); } /* * Hack -- attempt to quaff an unknown potion */ bool borg_quaff_unknown(void) { int i; /* Scan the pack */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Require correct tval */ if (l_ptr->tval != TV_POTION) continue; /* Skip aware items */ if (l_ptr->k_idx) continue; /* Log the message */ borg_note("# Quaffing unknown potion %s. (%c)", l_ptr->o_name, I2A(i)); /* Perform the action */ borg_keypress('q'); borg_keypress(I2A(i)); /* Success */ return (TRUE); } /* None available */ return (FALSE); } /* * Hack -- attempt to read an unknown scroll */ bool borg_read_unknown(void) { int i; map_block *mb_ptr = map_loc(c_x, c_y); /* Scan the pack */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip aware items */ if (l_ptr->k_idx) continue; /* Require correct tval */ if (l_ptr->tval != TV_SCROLL) continue; /* Not when dark */ if (!(mb_ptr->flags & MAP_GLOW) && !bp_ptr->cur_lite) return (FALSE); /* Blind or Confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* Log the message */ borg_note("# Reading unknown scroll %s. (%c)", l_ptr->o_name, I2A(i)); /* Perform the action */ borg_keypress('r'); borg_keypress(I2A(i)); /* Success */ return (TRUE); } /* None available */ return (FALSE); } /* * Hack -- attempt to eat an unknown potion. This is done in emergencies. */ bool borg_eat_unknown(void) { int i; /* Scan the pack */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip aware items */ if (l_ptr->k_idx) continue; /* Require correct tval */ if (l_ptr->tval != TV_FOOD) continue; /* Log the message */ borg_note("# Eating unknown mushroom %s. (%c)", l_ptr->o_name, I2A(i)); /* Perform the action */ borg_keypress('E'); borg_keypress(I2A(i)); /* Success */ return (TRUE); } /* None available */ return (FALSE); } /* * Hack -- attempt to use an unknown staff. This is done in emergencies. */ bool borg_use_unknown(void) { int i; /* Scan the pack */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip aware items */ if (l_ptr->k_idx) continue; /* Require correct tval */ if (l_ptr->tval != TV_STAFF) continue; /* Log the message */ borg_note("# Using unknown Staff %s. (%c)", l_ptr->o_name, I2A(i)); /* Perform the action */ borg_keypress('u'); borg_keypress(I2A(i)); /* Success */ return (TRUE); } /* None available */ return (FALSE); } /* Check if the given scroll (by sval) can be read */ bool borg_read_scroll_fail(int sval) { /* Dark */ if (!map_loc(c_x, c_y)->flags & MAP_GLOW && !bp_ptr->cur_lite) return (FALSE); /* Blind or Confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* Is the scroll available? */ if (!borg_slot(TV_SCROLL, sval)) return (FALSE); /* The borg has the scroll and can read it too */ return (TRUE); } /* Attempt to read the given scroll (by sval) */ bool borg_read_scroll(int sval) { int slot; map_block *mb_ptr = map_loc(c_x, c_y); /* Dark */ if (!(mb_ptr->flags & MAP_GLOW) && !bp_ptr->cur_lite) return (FALSE); /* Blind or Confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* Look for that scroll */ slot = borg_slot_from(TV_SCROLL, sval, 0); /* None available */ if (slot == -1) return (FALSE); /* Log the message */ borg_note("# Reading %s. (%c)", inventory[slot].o_name, I2A(slot)); /* Perform the action */ borg_keypress('r'); borg_keypress(I2A(slot)); /* Success */ return (TRUE); } /* Take an item and makes a fail check on it */ bool borg_use_item_fail(list_item *l_ptr, bool risky) { int chance, lev; /* Extract the item level */ lev = k_info[l_ptr->k_idx].level; /* Base chance of success */ chance = bp_ptr->skill_dev; /* Confusion hurts skill */ if (bp_ptr->status.confused) chance = chance / 2; /* Cursed items are difficult to activate */ if (KN_FLAG(l_ptr, TR_CURSED)) chance /= 3; /* Do you feel lucky, punk? */ if (risky) { /* Calculate the chance for this item */ chance = chance - lev / 3; } else { /* Calculate the chance for this item */ chance = chance - lev / 2; } /* Is this item usable? */ if (chance < USE_DEVICE) return (FALSE); /* Success */ return (TRUE); } /* To zap a rod or not */ static bool borg_rod_aux(int sval, bool zap, bool fail) { int slot; list_item *l_ptr; /* Look for that rod */ slot = borg_slot_from(TV_ROD, sval, 0); /* None available */ if (slot == -1) return (FALSE); l_ptr = &inventory[slot]; /* Still charging */ if (l_ptr->timeout == l_ptr->number) return (FALSE); /* Can we zap this rod */ if (fail && !borg_use_item_fail(l_ptr, FALSE)) return (FALSE); /* Do we want to zap it? */ if (zap) { /* Log the message */ borg_note("# Zapping %s (%c).", l_ptr->o_name, I2A(slot)); /* Perform the action */ borg_keypress('z'); borg_keypress(I2A(slot)); } /* Success */ return (TRUE); } /* Does the borg have this rod? */ bool borg_equips_rod(int sval) { return (borg_rod_aux(sval, FALSE, FALSE)); } /* Does the borg have this rod and will be able to zap it? */ bool borg_equips_rod_fail(int sval) { return (borg_rod_aux(sval, FALSE, TRUE)); } /* Let's zap this rod if possible */ bool borg_zap_rod(int sval) { return (borg_rod_aux(sval, TRUE, FALSE)); } /* * Hack -- attempt to use the requested wand (by sval). * Wands with unknown charges can also be tried if they are not {empty} * If (fail) is set then do a fail check. * If (aim) is set then aim the wand. */ static bool borg_wand_aux(int sval, bool aim, bool fail) { list_item *l_ptr; int slot = 0; /* Look for that wand */ slot = borg_slot_from(TV_WAND, sval, slot); /* Search the inventory until the right wand with charges is found */ while (slot != -1) { l_ptr = &inventory[slot]; /* Accept this wand if it may have charges */ if (borg_obj_known_p(l_ptr)) { /* Identified with charges */ if (l_ptr->pval) break; } else { /* unidentified and not inscribed as empty */ if (!strstr(l_ptr->o_name, "{empty}")) break; } /* Look for the next wand */ slot = borg_slot_from(TV_WAND, sval, slot + 1); } /* No wand found */ if (slot == -1) return (FALSE); /* Can we aim this wand */ if (fail && !borg_use_item_fail(l_ptr, FALSE)) return (FALSE); /* Aim the wand */ if (aim) { /* Log the message */ borg_note("# Aiming %s (%c).", l_ptr->o_name, I2A(slot)); /* Perform the action */ borg_keypress('a'); borg_keypress(I2A(slot)); } /* Success */ return (TRUE); } /* Attempt to aim the given (charged) wand (by sval) */ bool borg_aim_wand(int sval) { /* aim that wand without a fail check */ return (borg_wand_aux(sval, TRUE, FALSE)); } /* Does the borg have this wand with charges and can it be aimed? */ bool borg_equips_wand_fail(int sval) { /* Search for that wand with a fail check */ return (borg_wand_aux(sval, FALSE, TRUE)); } /* Does the borg have this wand with charges? */ bool borg_equips_wand(int sval) { /* Search for that wand */ return (borg_wand_aux(sval, FALSE, FALSE)); } /* * Hack -- attempt to use the requested staff (by sval). * Staffs with unknown charges can also be tried if they are not {empty} * This is ok to do because if the staff is empty the effect (a wasted turn) * is the same as a failure to use the staff. * If (fail) is set then do a fail check. * If (use) is set then use the staff. */ static bool borg_staff_aux(int sval, bool use, bool fail) { list_item *l_ptr; int slot = 0; /* Look for that staff */ slot = borg_slot_from(TV_STAFF, sval, slot); /* Search the inventory until the right staff with charges is found */ while (slot != -1) { l_ptr = &inventory[slot]; /* Accept this staff if it may have charges */ if (borg_obj_known_p(l_ptr)) { /* Identified with charges */ if (l_ptr->pval) break; } else { /* unidentified and not inscribed as empty */ if (!strstr(l_ptr->o_name, "{empty}")) break; } /* Look for the next staff */ slot = borg_slot_from(TV_STAFF, sval, slot + 1); } /* No staff found */ if (slot == -1) return (FALSE); /* Do the fail check */ if (fail) { if (sval == SV_STAFF_TELEPORTATION || sval == SV_STAFF_DESTRUCTION) { /* Take more risk if you want to teleport or destruct */ if (!borg_use_item_fail(l_ptr, TRUE)) return (FALSE); } else { /* Do not take more risks for other staffs */ if (!borg_use_item_fail(l_ptr, FALSE)) return (FALSE); } } /* Use the staff */ if (use) { /* Log the message */ borg_note("# Using %s (%c).", l_ptr->o_name, I2A(slot)); /* Perform the action */ borg_keypress('u'); borg_keypress(I2A(slot)); } /* Success */ return (TRUE); } /* Attempt to use the requested staff (by sval) */ bool borg_use_staff(int sval) { /* Use the staff (if available) without fail check */ return borg_staff_aux(sval, TRUE, FALSE); } /* Attempt to use the staff (by sval) and make a fail check on it. */ bool borg_use_staff_fail(int sval) { /* Use the staff with fail check */ return borg_staff_aux(sval, TRUE, TRUE); } /* Checks staff (by sval) and makes a fail check on it. */ bool borg_equips_staff_fail(int sval) { /* Do not use the staff, just do the fail check */ return borg_staff_aux(sval, FALSE, TRUE); } /* Checks staff (by sval) and without a fail check. */ bool borg_equips_staff(int sval) { /* Do not use the staff, just do the fail check */ return borg_staff_aux(sval, FALSE, FALSE); } /* * This function checks if the item is an artifact * that can be activated according to your skill. * if real_use is TRUE then there is also a check if the * borg can use the artifact right now. */ bool borg_check_artifact(list_item *l_ptr, bool real_use) { /* Skip empty items */ if (!l_ptr || !l_ptr->k_idx) return (FALSE); /* Skip non-artifacts */ if (!KN_FLAG(l_ptr, TR_INSTA_ART)) return (FALSE); /* Is this an activatable item? */ if (!KN_FLAG(l_ptr, TR_ACTIVATE)) return (FALSE); /* Can we activate this artifact */ if (!borg_use_item_fail(l_ptr, FALSE)) return (FALSE); if (!real_use) return (TRUE); /* Check charge */ if (l_ptr->timeout) return (FALSE); /* We got what we need */ return (TRUE); } /* Try to activate a certain activation */ static bool borg_activate_aux(int act_index, bool real_use) { int slot; cptr act; /* Check the equipment */ for (slot = 0; slot < equip_num; slot++) { list_item *l_ptr = &equipment[slot]; /* Is this item an artifact that can be activated now? */ if (!borg_check_artifact(l_ptr, TRUE)) continue; /* Hack! Get the activation */ act = item_activation(&p_ptr->equipment[slot]); /* Check if it is the activation the borg is after */ if (!prefix(act, borg_activation[act_index])) continue; /* Just checking for the ability? */ if (!real_use) return (TRUE); /* Try it */ borg_keypress('A'); borg_keypress(I2A(slot)); /* Confirm success */ return (TRUE); } /* No such luck */ return (FALSE); } /* Fiddle a bit with peculiar activations */ static bool borg_activate_aux2(int act_index, bool real_use) { switch (act_index) { /* illumination has several entries */ case BORG_ACT_LIGHT: { return (borg_activate_aux(BORG_ACT_LIGHT, real_use) || borg_activate_aux(BORG_ACT_LIGHT2, real_use) || borg_activate_aux(BORG_ACT_LIGHT3, real_use) || borg_activate_aux(BORG_ACT_LIGHT4, real_use)); } /* Speed has several entries */ case BORG_ACT_SPEED: { return (borg_activate_aux(BORG_ACT_SPEED, real_use) || borg_activate_aux(BORG_ACT_SPEED2, real_use)); } /* Resistance has several entries */ case BORG_ACT_RESISTANCE: { return (borg_activate_aux(BORG_ACT_RESISTANCE, real_use) || borg_activate_aux(BORG_ACT_RESISTANCE2, real_use)); } /* *identify* has several entries */ case BORG_ACT_STAR_IDENTIFY: { return (borg_activate_aux(BORG_ACT_STAR_IDENTIFY, real_use) || borg_activate_aux(BORG_ACT_STAR_IDENTIFY2, real_use)); } /* Restore life levels has several entries */ case BORG_ACT_RESTORE_LIFE: { return (borg_activate_aux(BORG_ACT_RESTORE_LIFE, real_use) || borg_activate_aux(BORG_ACT_RESTORE_LIFE2, real_use)); } /* Restore life levels has several entries */ case BORG_ACT_HEAL_SERIOUS: { return (borg_activate_aux(BORG_ACT_HEAL_SERIOUS, real_use) || borg_activate_aux(BORG_ACT_HEAL_SERIOUS2, real_use)); } /* Phase door has several entries */ case BORG_ACT_PHASE_DOOR: { return (borg_activate_aux(BORG_ACT_PHASE_DOOR, real_use) || borg_activate_aux(BORG_ACT_PHASE_DOOR2, real_use)); } /* Teleport has several entries */ case BORG_ACT_TELEPORT: { return (borg_activate_aux(BORG_ACT_TELEPORT, real_use) || borg_activate_aux(BORG_ACT_TELEPORT2, real_use)); } /* Big healers have several entries */ case BORG_ACT_HEAL_BIG: { return (borg_activate_aux(BORG_ACT_HEAL_BIG, real_use) || borg_activate_aux(BORG_ACT_HEAL_BIG2, real_use) || borg_activate_aux(BORG_ACT_HEAL_BIG3, real_use) || borg_activate_aux(BORG_ACT_HEAL_BIG4, real_use)); } /* Word of Recall has several entries */ case BORG_ACT_WORD_OF_RECALL: { /* Regular try */ if (borg_activate_aux(BORG_ACT_WORD_OF_RECALL, real_use)) { /* success */ return (TRUE); } /* Maybe the borg has the Jewel of Judgement */ if (borg_activate_aux(BORG_ACT_RECALL2, real_use)) { /* Activate for recall */ borg_keypress('y'); return (TRUE); } /* No recall available */ return (FALSE); } /* The jewel of judgement needs specail treatment */ case BORG_ACT_CLAIRVOYANCE: { if (borg_activate_aux(BORG_ACT_CLAIRVOYANCE, real_use)) { /* It is the jewel of judgement, no need to recall */ borg_keypress('n'); return (TRUE); } /* Not found */ return (FALSE); } default: { if (act_index <= BORG_ACT_NONE || act_index >= BORG_ACT_MAX) return (FALSE); /* Do the work */ return (borg_activate_aux(act_index, real_use)); } } } /* Perform a certain activation if available */ bool borg_activate(int act_index) { /* Do the work */ return (borg_activate_aux2(act_index, TRUE)); } /* Check if a certain activation is available */ bool borg_activate_fail(int act_index) { /* Do the work */ return (borg_activate_aux2(act_index, FALSE)); } static void borg_dimension_door(void) { int x1, y1, x2, y2; /* Follow Dim Door syntax */ borg_keypress(' '); /* Report a little bit */ borg_note("# Targetting Landing Zone (%d,%d)", dim_door_x, dim_door_y); /* Determine "path" */ x1 = c_x; y1 = c_y; x2 = dim_door_x; y2 = dim_door_y; /* Move to the location (diagonals) */ for (; (y1 < y2) && (x1 < x2); y1++, x1++) borg_keypress('3'); for (; (y1 < y2) && (x1 > x2); y1++, x1--) borg_keypress('1'); for (; (y1 > y2) && (x1 < x2); y1--, x1++) borg_keypress('9'); for (; (y1 > y2) && (x1 > x2); y1--, x1--) borg_keypress('7'); /* Move to the location */ for (; y1 < y2; y1++) borg_keypress('2'); for (; y1 > y2; y1--) borg_keypress('8'); for (; x1 < x2; x1++) borg_keypress('6'); for (; x1 > x2; x1--) borg_keypress('4'); /* Select the target */ borg_keypress(' '); } /* Returns the mana cost of a spell, assuming that the borg can cast it */ byte borg_spell_mana(int realm, int book, int spell) { byte power; /* basic cost of the spell */ power = borg_magics[realm][book][spell].power; /* If this is a chaos spell and the borg has a chaos patron */ if (realm == REALM_CHAOS && FLAG(bp_ptr, TR_PATRON)) { /* Reduce the spell cost */ power = (2 * power + 2) / 3; } /* Tell the world */ return (power); } /* Combines the legality check with the cost */ static bool borg_mana_legal_fail(int realm, int book, int spell, int fail, byte *cost) { /* Is this spell castable with the fail_check? */ if (!borg_spell_legal_fail(realm, book, spell, fail)) return (FALSE); /* Find out the cost */ *cost = borg_spell_mana(realm, book, spell); /* Success */ return (TRUE); } /* This function returns the amount of reserve mana */ int borg_reserve_mana(void) { byte cost; /* Don't bother with reserve mana if you can't have spells */ if (borg_class == CLASS_WARRIOR) return (0); /* Low level spell casters should not worry about this */ if (bp_ptr->lev < 20) return (0); /* Special case for Mindcrafters */ if (borg_class == CLASS_MINDCRAFTER) { /* This borg has dimension door */ if (bp_ptr->lev >= 40) return (3 * borg_minds[MIND_MINOR_DISP].power); /* Telekinetic Wave */ if (bp_ptr->msp > 100) return (borg_minds[MIND_TELE_WAVE].power); /* Two teleports */ if (bp_ptr->msp > 50) return (2 * borg_minds[MIND_MAJOR_DISP].power); /* One teleport */ if (bp_ptr->msp > 12) return (borg_minds[MIND_MAJOR_DISP].power); /* One phase door */ if (bp_ptr->msp > 4) return (borg_minds[MIND_MINOR_DISP].power); /* Puny! */ return (0); } /* * I created these values spell by spell. If there are multiple realms * carrying a spell then Trump goes first and Arcane goes last as Trump * has low values and Arcane high * Teleport away is not listed because it is covered by the reserve for * teleport spells. */ /* Multiple Dimension Doors */ if (borg_mana_legal_fail(REALM_TRUMP, 0, 5, 5, &cost)) return (3 * cost); if (borg_mana_legal_fail(REALM_SORCERY, 2, 3, 5, &cost)) return (3 * cost); /* Dimension Door */ if (borg_mana_legal_fail(REALM_TRUMP, 0, 5, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_SORCERY, 2, 3, 15, &cost)) return (cost); /* Teleport Level */ if (borg_mana_legal_fail(REALM_TRUMP, 1, 5, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_ARCANE, 3, 1, 15, &cost)) return (cost); /* Mass teleport away */ if (borg_mana_legal_fail(REALM_DEATH, 3, 4, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_LIFE, 2, 5, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_TRUMP, 1, 7, 15, &cost)) return (cost); /* Multiple Teleports */ if (borg_mana_legal_fail(REALM_TRUMP, 0, 4, 5, &cost)) return (2 * cost); if (borg_mana_legal_fail(REALM_SORCERY, 0, 5, 5, &cost)) return (2 * cost); if (borg_mana_legal_fail(REALM_CHAOS, 1, 7, 5, &cost)) return (2 * cost); if (borg_mana_legal_fail(REALM_ARCANE, 2, 3, 5, &cost)) return (2 * cost); /* Teleport */ if (borg_mana_legal_fail(REALM_TRUMP, 0, 4, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_SORCERY, 0, 5, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_CHAOS, 1, 7, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_ARCANE, 2, 3, 15, &cost)) return (cost); /* Phase Door? */ if (borg_mana_legal_fail(REALM_TRUMP, 0, 0, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_SORCERY, 0, 1, 15, &cost)) return (cost); if (borg_mana_legal_fail(REALM_ARCANE, 0, 4, 15, &cost)) return (cost); /* No spell available */ return (0); } /* * This function determines if a given spell is allowed to be cast * with regards to reserve_mana */ static bool borg_reserve_allow(int realm, int book, int what) { /* Are you dipping into reserve mana? */ if (bp_ptr->csp - borg_spell_mana(realm, book, what) >= borg_reserve_mana()) { /* Plenty of mana so it is OK */ return (TRUE); } switch (realm) { case REALM_LIFE: { /* Banishment spells ok */ if (book == 2 && what == 5) return (TRUE); /* others are rejected */ return (FALSE); } case REALM_SORCERY: { /* Phase spells ok */ if (book == 0 && what == 1) return (TRUE); /* Teleport spells ok */ if (book == 0 && what == 5) return (TRUE); /* Teleport Away ok */ if (book == 1 && what == 4) return (TRUE); /* Dimension Door spells ok */ if (book == 2 && what == 3) return (TRUE); /* Teleport Level spells ok */ if (book == 2 && what == 6) return (TRUE); /* others are rejected */ return (FALSE); } case REALM_NATURE: { /* Stair Building spells ok */ if (book == 2 && what == 1) return (TRUE); /* others are rejected */ return (FALSE); } case REALM_CHAOS: { /* Teleport spells ok */ if (book == 0 && what == 7) return (TRUE); /* Teleport Away ok */ if (book == 1 && what == 5) return (TRUE); /* Alter Reality ok */ if (book == 2 && what == 4) return (TRUE); /* others are rejected */ return (FALSE); } case REALM_DEATH: { /* Evocation spells ok */ if (book == 3 && what == 4) return (TRUE); /* others are rejected */ return (FALSE); } case REALM_TRUMP: { /* Phase spells ok */ if (book == 0 && what == 0) return (TRUE); /* Teleport spells ok */ if (book == 0 && what == 4) return (TRUE); /* Dimension Door spells ok */ if (book == 0 && what == 5) return (TRUE); /* Teleport Away ok */ if (book == 0 && what == 7) return (TRUE); /* Teleport Level spells ok */ if (book == 1 && what == 5) return (TRUE); /* Teleport Level spells ok */ if (book == 1 && what == 7) return (TRUE); /* others are rejected */ return (FALSE); } case REALM_ARCANE: { /* Phase spells ok */ if (book == 0 && what == 4) return (TRUE); /* Teleport spells ok */ if (book == 2 && what == 3) return (TRUE); /* Satisfy Hunger OK */ if (book == 2 && what == 6) return (TRUE); /* Teleport Level spells ok */ if (book == 3 && what == 1) return (TRUE); /* Teleport Away ok */ if (book == 3 && what == 3) return (TRUE); /* others are rejected */ return (FALSE); } default: { borg_oops("Unknown Realm used in borg_reserve_allow"); return(0); } } } /* * Determine if borg can cast a given spell (when fully rested) */ bool borg_spell_legal(int realm, int book, int what) { borg_magic *as = &borg_magics[realm][book][what]; /* The borg must be able to "cast" spells this realm */ if (!borg_has_realm(realm)) return (FALSE); /* Make sure we have this realm book */ if (amt_book[realm][book] <= 0) return (FALSE); /* The spell must be "known" */ if (as->status < BORG_MAGIC_TEST) return (FALSE); /* The spell must be affordable (when rested) */ if (borg_spell_mana(realm, book, what) > bp_ptr->msp) return (FALSE); /* Not if locked down */ if (FLAG(bp_ptr, TR_NO_MAGIC)) return (FALSE); /* Success */ return (TRUE); } /* Determine if borg can cast a given spell (right now) */ static bool borg_spell_okay_aux(int realm, int book, int what, bool reserve) { map_block *mb_ptr = map_loc(c_x, c_y); /* Dark */ if (!(mb_ptr->flags & MAP_GLOW) && !bp_ptr->cur_lite) return (FALSE); /* Require ability (when rested) */ if (!borg_spell_legal(realm, book, what)) return (FALSE); /* Hack -- blind/confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* The spell must be affordable (now) */ if (borg_spell_mana(realm, book, what) > bp_ptr->csp) return (FALSE); /* With the reserve check */ if (reserve) { /* Check if this spell uses reserve mana */ if (!borg_reserve_allow(realm, book, what)) return (FALSE); } /* Not if locked down */ if (FLAG(bp_ptr, TR_NO_MAGIC)) return (FALSE); /* Success */ return (TRUE); } /* Determine if borg can cast a given spell (right now) */ bool borg_spell_okay(int realm, int book, int what) { /* Do the work */ return (borg_spell_okay_aux(realm, book, what, TRUE)); } /* Determine if borg can cast a given spell (right now) */ bool borg_spell_okay_no_reserve(int realm, int book, int what) { /* Do the work */ return (borg_spell_okay_aux(realm, book, what, FALSE)); } /* * fail rate on a spell */ int borg_spell_fail_rate(int realm, int book, int what) { int chance, minfail, stat, power; list_item *l_ptr; borg_magic *as = &borg_magics[realm][book][what]; /* Warriors can't cast spells */ if (borg_class == CLASS_WARRIOR) return (100); /* Access the spell */ if (realm == REALM_ARCANE - 1) chance = as->level + 20; else chance = as->level * 3 / 2 + 20; /* Reduce failure rate by "effective" level adjustment */ chance -= 3 * (bp_ptr->lev - as->level); /* Is the borg an INT user? */ if (bp_ptr->intmana) { /* Get the value of INT */ stat = my_stat_ind[A_INT]; } /* Is the borg a WIS user? */ else if (bp_ptr->wismana) { /* Get the value of WIS */ stat = my_stat_ind[A_WIS]; } else { /* Inconcievable! */ borg_oops("A spellcaster that doesn't need INT or WIS!"); /* fail the spell */ return (100); } /* Reduce failure rate by INT/WIS adjustment */ chance -= adj_mag_stat[stat]; /* Collect the spell cost */ power = borg_spell_mana(realm, book, what); /* Failure rate goes up if there is not enough mana */ if (power > bp_ptr->csp) chance += 5 * (power - bp_ptr->csp); /* Some mutations increase spell failure */ if (bp_ptr->muta3 & MUT3_MAGIC_RES || bp_ptr->muta1 & MUT1_EAT_MAGIC) chance += 5; /* Having two banishments hurts your chances */ if (realm == REALM_DEATH - 1 && bp_ptr->muta1 & MUT1_BANISH) chance += 10; /* Squeeeeeek */ if (bp_ptr->muta3 & MUT3_SILLY_VOI) chance += as->level; /* Extract the minimum failure rate */ minfail = adj_mag_fail[stat]; /* Non mage characters never get too good */ if (borg_class != CLASS_MAGE && borg_class != CLASS_PRIEST && borg_class != CLASS_HIGH_MAGE && borg_class != CLASS_MINDCRAFTER) { /* For these the minfail is at least 5% */ minfail = MAX(5, minfail); } /* If the borg is a priest with an non-blessed edged weapon */ if (borg_class == CLASS_PRIEST) { l_ptr = &equipment[EQUIP_WIELD]; if (l_ptr->k_idx && (l_ptr->tval == TV_SWORD || l_ptr->tval == TV_POLEARM) && !KN_FLAG(l_ptr, TR_BLESSED)) { /* Penalize this edgy priest */ chance += 25; } } /* Minimum failure rate */ chance = MAX(chance, minfail); /* Stunning makes spells harder */ if (bp_ptr->status.heavy_stun) chance += 25; if (bp_ptr->status.stun) chance += 15; /* Always a 5 percent chance of working */ chance = MIN(chance, 95); /* Return the chance */ return (chance); } /* * same as borg_spell_okay with a fail % check */ bool borg_spell_okay_fail(int realm, int book, int what, int allow_fail) { if (borg_spell_fail_rate(realm, book, what) > allow_fail) return FALSE; return borg_spell_okay(realm, book, what); } /* * Same as borg_spell with a fail % check */ bool borg_spell_fail(int realm, int book, int what, int allow_fail) { if (borg_spell_fail_rate(realm, book, what) > allow_fail) return FALSE; return borg_spell(realm, book, what); } /* * Same as borg_spell_legal with a fail % check */ bool borg_spell_legal_fail(int realm, int book, int what, int allow_fail) { if (borg_spell_fail_rate(realm, book, what) > allow_fail) return FALSE; return borg_spell_legal(realm, book, what); } /* Attempt to cast a spell */ static bool borg_spell_aux(int realm, int book, int what, bool reserve) { int i; borg_magic *as = &borg_magics[realm][book][what]; /* With the reserve check */ if (reserve) { /* Require ability (right now) */ if (!borg_spell_okay(realm, book, what)) return (FALSE); } else /* Without the reserve check */ { /* Require ability (right now) */ if (!borg_spell_okay_no_reserve(realm, book, what)) return (FALSE); } /* Look for the book */ i = borg_book[realm][book]; /* Paranoia */ if (i < 0) return (FALSE); /* Debugging Info */ borg_note("# Casting %s (%d,%d).", as->name, book, what); /* Cast a spell */ borg_keypress('m'); borg_keypress(I2A(i)); borg_keypress(I2A(what)); /* increment the spell counter */ as->times++; /* Dimension Door -- Must target the landing zone */ if ((realm == REALM_SORCERY && book == 2 && what == 3) || (realm == REALM_TRUMP && book == 0 && what == 5)) { borg_dimension_door(); } /* Success */ return (TRUE); } /* Attempt to cast a spell */ bool borg_spell(int realm, int book, int what) { /* Do the work */ return (borg_spell_aux(realm, book, what, TRUE)); } /* Attempt to cast a spell */ bool borg_spell_no_reserve(int realm, int book, int what) { /* Do the work */ return (borg_spell_aux(realm, book, what, FALSE)); } /* * Determines if a book contains spells that can be reliably cast, * regardless whether the borg has the book or not, otherwise he'll * drop all his books at home and never picks them up to learn from them */ bool borg_uses_book(int realm, int book) { int spell; /* Loop through the spells */ for (spell = 0; spell < 8; spell++) { /* Is this an easy spell? */ if (borg_spell_fail_rate(realm, book, spell) < 40) return (TRUE); } /* Only hard / impossible spells */ return (FALSE); } /*** Mindcrafter spells are much like realm spells ***/ /* Determine if the borg can cast a given spell with regard to reserve_mana */ static bool borg_reserve_allow_mindcrafter(int spell) { borg_mind *as = &borg_minds[spell]; if (bp_ptr->csp - as->power >= borg_reserve_mana()) return (TRUE); /* Minor Displacement spells ok */ if (spell == MIND_MINOR_DISP) return (TRUE); /* Major Displacement ok */ if (spell == MIND_MAJOR_DISP) return (TRUE); /* Telekinetic Wave ok */ if (spell == MIND_TELE_WAVE) return (TRUE); /* others are rejected */ return (FALSE); } /* * Determine if borg can cast a given Mindcraft spell (when fully rested) */ bool borg_mindcr_legal(int spell, int level) { borg_mind *as = &borg_minds[spell]; /* The borg must be able to "cast" spells this realm */ if (borg_class != CLASS_MINDCRAFTER) return (FALSE); /* The spell must be "known" */ if (bp_ptr->lev < level) return (FALSE); /* The spell must be affordable (when rested) */ if (as->power > bp_ptr->msp) return (FALSE); /* Success */ return (TRUE); } /* Determine if borg can cast a given spell (right now) */ static bool borg_mindcr_okay_aux(int spell, int level, bool reserve) { borg_mind *as = &borg_minds[spell]; /* Require ability (when rested) */ if (!borg_mindcr_legal(spell, level)) return (FALSE); /* No spellcasting when confused */ if (bp_ptr->status.confused) return (FALSE); /* The spell must be affordable (now) */ if (as->power > bp_ptr->csp) return (FALSE); /* Check for reserve mana */ if (reserve) { /* Do not cut into reserve mana (for final teleport) */ if (!borg_reserve_allow_mindcrafter(spell)) return (FALSE); } /* No go if there is an item with the NO_MAGIC flag */ if (FLAG(bp_ptr, TR_NO_MAGIC)) return (FALSE); /* Success */ return (TRUE); } /* Can the borg cast this spell with the current mana with the reserve check */ bool borg_mindcr_okay(int spell, int level) { /* Do the work */ return (borg_mindcr_okay_aux(spell, level, TRUE)); } /* Can the borg cast this spell with the current mana without the reserve check */ bool borg_mindcr_okay_no_reserve(int spell, int level) { /* Do the work */ return (borg_mindcr_okay_aux(spell, level, FALSE)); } /* fail rate on a mindcrafter spell */ int borg_mindcr_fail_rate(int spell, int level) { int chance, minfail; borg_mind *as = &borg_minds[spell]; /* Hack - ignore parameter */ (void)level; /* XXX Access the spell */ chance = as->sfail; /* Reduce failure rate by "effective" level adjustment */ chance -= 3 * (bp_ptr->lev - as->level); /* Reduce failure rate by WIS adjustment */ chance -= adj_mag_stat[my_stat_ind[A_WIS]] - 3; /* If there is not enough mana the fail rate plummets */ if (as->power > bp_ptr->csp) chance += 5 * (as->power - bp_ptr->csp); /* Extract the minimum failure rate */ minfail = adj_mag_fail[my_stat_ind[A_WIS]]; /* Minimum failure rate */ chance = MAX(chance, minfail); /* Stunning makes spells harder */ if (bp_ptr->status.heavy_stun) chance += 25; if (bp_ptr->status.stun) chance += 15; /* Always a 5 percent chance of working */ chance = MIN(chance, 95); /* Return the chance */ return (chance); } /* * same as borg_mind_okay with a fail % check */ bool borg_mindcr_okay_fail(int spell, int level, int allow_fail) { if (borg_mindcr_fail_rate(spell, level) > allow_fail) return FALSE; return borg_mindcr_okay(spell, level); } /* * Same as borg_mind with a fail % check */ bool borg_mindcr_fail(int spell, int level, int allow_fail) { if (borg_mindcr_fail_rate(spell, level) > allow_fail) return FALSE; return borg_mindcr(spell, level); } /* * Same as borg_mind_legal with a fail % check */ bool borg_mindcr_legal_fail(int spell, int level, int allow_fail) { if (borg_mindcr_fail_rate(spell, level) > allow_fail) return FALSE; return borg_mindcr_legal(spell, level); } /* Attempt to cast a mindcrafter spell */ static bool borg_mindcr_aux(int spell, int level, bool reserve) { borg_mind *as = &borg_minds[spell]; /* Check for reserve mana */ if (reserve) { /* Require ability (right now) */ if (!borg_mindcr_okay(spell, level)) return (FALSE); } /* No check for reserve mana */ else { /* Require ability (right now) */ if (!borg_mindcr_okay_no_reserve(spell, level)) return (FALSE); } /* Debugging Info */ borg_note("# Casting %s (spell: %d, level: %d).", as->name, spell, level); /* Cast a spell */ borg_keypress('m'); borg_keypress(as->letter); /* increment the spell counter */ as->times++; /* Dimension Door -- need a landing Zone */ if (spell == MIND_MINOR_DISP && level >= 40) borg_dimension_door(); /* Success */ return (TRUE); } /* Attempt to cast a mindcrafter spell without the reserve check */ bool borg_mindcr_no_reserve(int spell, int level) { /* Call the actual proc */ return (borg_mindcr_aux(spell, level, FALSE)); } /* Attempt to cast a mindcrafter spell with the reserve check */ bool borg_mindcr(int spell, int level) { /* Call the actual proc */ return (borg_mindcr_aux(spell, level, TRUE)); } static bool borg_power_check(bool race, u32b which, bool check_fail, int lev_req, int cost, int use_stat, int difficulty) { int i; int val; int sum = 0; int stat; /* Power is not available yet */ if (bp_ptr->lev < lev_req) return (FALSE); /* Too confused */ if (bp_ptr->status.confused) return FALSE; /* Don't use too much HP */ if (bp_ptr->csp < cost) { /* Don't use it if it can kill you */ if (cost > bp_ptr->chp) return (FALSE); /* Don't use the racial if it takes more then 70% of current HP */ if (cost > bp_ptr->chp * 7 / 10) return (FALSE); /* How much can we spend? */ if ((race && (which == RACE_GNOME || which == RACE_AMBERITE)) || (!race && which == MUT1_VTELEPORT)) { /* These are emergency powers, so take more risk */ if (bp_ptr->chp < bp_ptr->mhp * 3 / 10) return (FALSE); } else { /* Allow up to 50% of HP to be used */ if (bp_ptr->chp < bp_ptr->mhp * 5 / 10) return (FALSE); } } /* Don't use too much SP */ else { /* How much can we spend? */ if ((race && (which == RACE_GNOME || which == RACE_AMBERITE)) || (!race && which == MUT1_VTELEPORT)) { /* These are emergency powers, so any mana usage is allowable */ } else { /* Disallow if using the power spends reserve mana */ if (bp_ptr->csp - cost < borg_reserve_mana()) return (FALSE); } } /* Legal check ends here */ if (!check_fail) return (TRUE); /* Otherwise continue on to a fail check */ /* Collect the correct stat */ stat = my_stat_cur[use_stat]; /* Convert the needed stat to the correct form */ if (stat <= 180) stat /= 10; else stat += 18 - 180; /* Stun makes it more difficult */ if (bp_ptr->status.stun) { difficulty += 10; } else { int lev_adj = (bp_ptr->lev - lev_req) / 3; if (lev_adj > 10) lev_adj = 10; difficulty -= lev_adj; } if (difficulty < 5) difficulty = 5; /* We only need halfs of the difficulty */ difficulty = difficulty / 2; for (i = 1; i <= stat; i++) { val = i - difficulty; if (val > 0) sum += (val <= difficulty) ? val : difficulty; } /* Finally get the fail % */ difficulty = 100 - 100 * sum / difficulty / stat; /* Don't try if the fail rate is higher then 40% */ if (difficulty >= 40) return (FALSE); /* Success */ return (TRUE); } /*** Racial abilities are much like magic spells ***/ /* * Determine if borg can cast a given Racial spell (when fully rested). * -or- * with a reasonable degree of difficulty with Check_fail * * The values for borg_power_check come from tables.c */ bool borg_racial_check(int race, bool check_fail) { /* normal check */ if (borg_race != race) { /* Hack these two races with two powers */ if ((borg_race != RACE_AMBERITE || race != RACE_AMBERITE_POWER2) && (borg_race != RACE_GHOUL || race != RACE_GHOUL_POWER2)) { /* This race is not omnipotent */ return (FALSE); } } /* Tell me who are you */ switch (race) { case RACE_HOBBIT: return borg_power_check(TRUE, race, check_fail, 15, 10, A_INT, 10); case RACE_GNOME: return borg_power_check(TRUE, race, check_fail, 5, 10, A_INT, 12); case RACE_DWARF: return borg_power_check(TRUE, race, check_fail, 5, 5, A_WIS, 12); case RACE_HALF_ORC: return borg_power_check(TRUE, race, check_fail, 3, 5, A_WIS, 8); case RACE_HALF_TROLL: return borg_power_check(TRUE, race, check_fail, 10, 12, A_WIS, 9); case RACE_AMBERITE: return borg_power_check(TRUE, race, check_fail, 30, 50, A_INT, 50); case RACE_AMBERITE_POWER2: return borg_power_check(TRUE, race, check_fail, 40, 75, A_WIS, 50); case RACE_BARBARIAN: return borg_power_check(TRUE, race, check_fail, 8, 10, A_WIS, 9); case RACE_HALF_OGRE: return borg_power_check(TRUE, race, check_fail, 25, 35, A_INT, 15); case RACE_HALF_GIANT: return borg_power_check(TRUE, race, check_fail, 20, 10, A_STR, 12); case RACE_HALF_TITAN: return borg_power_check(TRUE, race, check_fail, 35, 20, A_STR, 12); case RACE_CYCLOPS: return borg_power_check(TRUE, race, check_fail, 20, 15, A_STR, 12); case RACE_YEEK: return borg_power_check(TRUE, race, check_fail, 15, 15, A_WIS, 10); case RACE_KLACKON: return borg_power_check(TRUE, race, check_fail, 9, 9, A_DEX, 14); case RACE_KOBOLD: return borg_power_check(TRUE, race, check_fail, 12, 8, A_DEX, 14); case RACE_NIBELUNG: return borg_power_check(TRUE, race, check_fail, 10, 5, A_WIS, 10); case RACE_DARK_ELF: return borg_power_check(TRUE, race, check_fail, 2, 2, A_INT, 9); case RACE_DRACONIAN: return borg_power_check(TRUE, race, check_fail, 15, 25, A_CON, 12); case RACE_MIND_FLAYER: return borg_power_check(TRUE, race, check_fail, 15, 12, A_INT, 14); case RACE_IMP: return borg_power_check(TRUE, race, check_fail, 9, 15, A_WIS, 15); case RACE_GOLEM: return borg_power_check(TRUE, race, check_fail, 20, 15, A_CON, 8); case RACE_SKELETON: case RACE_ZOMBIE: return borg_power_check(TRUE, race, check_fail, 30, 30, A_WIS, 18); case RACE_VAMPIRE: return borg_power_check(TRUE, race, check_fail, 5, 10, A_CON, 9); case RACE_SPECTRE: return borg_power_check(TRUE, race, check_fail, 4, 6, A_INT, 3); case RACE_SPRITE: return borg_power_check(TRUE, race, check_fail, 12, 12, A_INT, 15); case RACE_GHOUL: return borg_power_check(TRUE, race, check_fail, 1, 0, A_CON, 0); case RACE_GHOUL_POWER2: return borg_power_check(TRUE, race, check_fail, 30, 10, A_WIS, 12); case RACE_HUMAN: case RACE_HALF_ELF: case RACE_ELF: case RACE_BEASTMAN: case RACE_HIGH_ELF: default: return (FALSE); } } /* * Attempt to cast a racial spell */ bool borg_racial(int race) { /* Require ability (right now) */ if (!borg_racial_check(race, TRUE)) return (FALSE); /* Debugging Info */ borg_note("# Racial Power."); /* Cast a spell */ borg_keypress('U'); /* Hack to reach the second powers of Ghoul and Amberite */ if (race < MAX_RACES) borg_keypress('a'); else borg_keypress('b'); /* Success */ return (TRUE); } /* * Mutations and racial both use U and the racial comes first. * This procedure returns the number of racial powers for a race */ int borg_count_racial(int race) { /* Which race is that? */ switch (race) { /* Amberite & Ghoul have two racial powers */ case RACE_GHOUL: case RACE_AMBERITE: return (2); /* These have one racial power */ case RACE_HOBBIT: case RACE_GNOME: case RACE_DWARF: case RACE_HALF_ORC: case RACE_HALF_TROLL: case RACE_BARBARIAN: case RACE_HALF_OGRE: case RACE_HALF_GIANT: case RACE_HALF_TITAN: case RACE_CYCLOPS: case RACE_YEEK: case RACE_KLACKON: case RACE_KOBOLD: case RACE_NIBELUNG: case RACE_DARK_ELF: case RACE_DRACONIAN: case RACE_MIND_FLAYER: case RACE_IMP: case RACE_GOLEM: case RACE_SKELETON: case RACE_ZOMBIE: case RACE_VAMPIRE: case RACE_SPECTRE: case RACE_SPRITE: return (1); /* No such luck for these guys */ case RACE_HUMAN: case RACE_HALF_ELF: case RACE_ELF: case RACE_BEASTMAN: case RACE_HIGH_ELF: default: return (0); } } /* Give every mutation its stats. These numbers come from tables.c */ bool borg_mutation_check(u32b mutation, bool check) { /* Is this mutation available? */ if (!(bp_ptr->muta1 & mutation)) return (FALSE); switch (mutation) { case MUT1_SPIT_ACID: return borg_power_check(FALSE, mutation, check, 9, 9, A_DEX, 15); case MUT1_BR_FIRE: return borg_power_check(FALSE, mutation, check, 20, 20, A_CON, 18); case MUT1_HYPN_GAZE: return borg_power_check(FALSE, mutation, check, 12, 12, A_CHR, 18); case MUT1_TELEKINES: return borg_power_check(FALSE, mutation, check, 9, 9, A_WIS, 14); case MUT1_VTELEPORT: return borg_power_check(FALSE, mutation, check, 7, 7, A_WIS, 15); case MUT1_MIND_BLST: return borg_power_check(FALSE, mutation, check, 5, 3, A_WIS, 15); case MUT1_RADIATION: return borg_power_check(FALSE, mutation, check, 15, 15, A_CON, 14); case MUT1_VAMPIRISM: return borg_power_check(FALSE, mutation, check, 10, 10, A_CON, 9); case MUT1_SMELL_MET: return borg_power_check(FALSE, mutation, check, 3, 2, A_INT, 12); case MUT1_SMELL_MON: return borg_power_check(FALSE, mutation, check, 5, 4, A_INT, 15); case MUT1_BLINK: return borg_power_check(FALSE, mutation, check, 3, 3, A_WIS, 12); case MUT1_EAT_ROCK: return borg_power_check(FALSE, mutation, check, 8, 12, A_CON, 18); case MUT1_SWAP_POS: return borg_power_check(FALSE, mutation, check, 15, 12, A_DEX, 16); case MUT1_SHRIEK: return borg_power_check(FALSE, mutation, check, 20, 14, A_CON, 16); case MUT1_ILLUMINE: return borg_power_check(FALSE, mutation, check, 3, 2, A_INT, 10); case MUT1_DET_CURSE: return borg_power_check(FALSE, mutation, check, 7, 14, A_WIS, 14); case MUT1_BERSERK: return borg_power_check(FALSE, mutation, check, 8, 8, A_STR, 14); case MUT1_POLYMORPH: return borg_power_check(FALSE, mutation, check, 18, 20, A_CON, 18); case MUT1_MIDAS_TCH: return borg_power_check(FALSE, mutation, check, 10, 5, A_INT, 12); case MUT1_GROW_MOLD: return borg_power_check(FALSE, mutation, check, 1, 6, A_CON, 14); case MUT1_RESIST: return borg_power_check(FALSE, mutation, check, 10, 12, A_CON, 12); case MUT1_EARTHQUAKE: return borg_power_check(FALSE, mutation, check, 12, 12, A_STR, 16); case MUT1_EAT_MAGIC: return borg_power_check(FALSE, mutation, check, 17, 1, A_WIS, 15); case MUT1_WEIGH_MAG: return borg_power_check(FALSE, mutation, check, 6, 6, A_INT, 10); case MUT1_STERILITY: return borg_power_check(FALSE, mutation, check, 12, 23, A_CHR, 15); case MUT1_PANIC_HIT: return borg_power_check(FALSE, mutation, check, 10, 12, A_DEX, 14); case MUT1_DAZZLE: return borg_power_check(FALSE, mutation, check, 7, 15, A_CHR, 8); case MUT1_LASER_EYE: return borg_power_check(FALSE, mutation, check, 7, 10, A_WIS, 9); case MUT1_RECALL: return borg_power_check(FALSE, mutation, check, 17, 50, A_INT, 16); case MUT1_BANISH: return borg_power_check(FALSE, mutation, check, 25, 25, A_WIS, 18); case MUT1_COLD_TOUCH: return borg_power_check(FALSE, mutation, check, 2, 2, A_CON, 11); case MUT1_LAUNCHER: return borg_power_check(FALSE, mutation, check, 10, 15, A_STR, 6); default: return (FALSE); } } /* * Attempt to cast a mutational spell */ bool borg_mutation(u32b mutation) { int i, spell; u32b mut_nr = 0; /* Require ability (right now) */ if (!borg_mutation_check(mutation, TRUE)) return (FALSE); /* Find out if the there isn't a racial in the way */ spell = borg_count_racial(borg_race) - 1; /* Loop through all the bits in bp_ptr->muta1 */ for (i = 1; i < 32; i++) { /* get the current mutation */ mut_nr = (mut_nr) ? mut_nr * 2 : 1; /* Does the borg have this mutation? */ if (!(bp_ptr->muta1 & mut_nr)) continue; /* Advance the letter index */ spell += 1; /* Is this the mutation? (Must have it at some point) */ if (mut_nr == mutation) break; } /* Debugging Info */ borg_note("# Mutated Power."); /* Cast a spell */ borg_keypress('U'); borg_keypress(I2A(spell)); /* Success */ return (TRUE); } /* * Hack -- Cheat the "spell" info * * Hack -- note the use of the "cheat" field for efficiency */ void borg_cheat_spell(int realm) { int j, what; int book; /* Can we use spells/prayers? */ if (realm == 0) return; /* process books */ for (book = 0; book < 4; book++) { /* Process the spells */ for (what = 0; what < 8; what++) { /* Access the spell */ borg_magic *as = &borg_magics[realm][book][what]; /* Skip illegible spells */ if (as->status == BORG_MAGIC_ICKY) continue; /* Access the index */ j = as->cheat; /* Note "forgotten" spells */ if ((realm == bp_ptr->realm1) ? ((p_ptr->spell.r[0].forgotten & (1L << j))) : ((p_ptr->spell.r[1].forgotten & (1L << j)))) { /* Forgotten */ as->status = BORG_MAGIC_LOST; } /* Note "difficult" spells */ else if (bp_ptr->lev < as->level) { /* Unknown */ as->status = BORG_MAGIC_HIGH; } /* Note "unknown" spells */ else if (!((realm == bp_ptr->realm1) ? (p_ptr->spell.r[0].learned & (1L << j)) : (p_ptr->spell.r[1].learned & (1L << j)))) { /* Unknown */ as->status = BORG_MAGIC_OKAY; } /* Note "untried" spells */ else if (!((realm == bp_ptr->realm1) ? (p_ptr->spell.r[0].worked & (1L << j)) : (p_ptr->spell.r[1].worked & (1L << j)))) { /* Untried */ as->status = BORG_MAGIC_TEST; } /* Note "known" spells */ else { /* Known */ as->status = BORG_MAGIC_KNOW; } } /* book */ } /* Realm */ } /* * Prepare a book */ static void prepare_book_info(int realm, int book) { int i, what; int spell[64], num = 0; /* Reset each spell entry */ for (what = 0; what < 8; what++) { borg_magic *as = &borg_magics[realm][book][what]; /* Assume no name */ as->name = NULL; /* Know the Realm, if any */ as->realm = realm; /* Assume illegible */ as->status = BORG_MAGIC_ICKY; /* Assume illegible */ as->method = BORG_MAGIC_ICK; /* Impossible values */ as->level = 99; as->power = 99; /* Impossible value */ as->cheat = 99; /* Delete the text name */ as->realm_name = NULL; } /* Can we use spells/prayers? */ if (borg_class == CLASS_WARRIOR) return; /* Extract spells */ for (i = 0; i < 32; i++) { /* Check for this spell */ if ((fake_spell_flags[book] & (1L << i))) { /* Collect this spell */ spell[num++] = i; } } /* Process each existing spell */ for (what = 0; what < num; what++) { borg_magic *as = &borg_magics[realm][book][what]; magic_type *s_ptr = &pmb_ptr->info[realm - 1][spell[what]]; /* Skip "illegible" spells */ if (s_ptr->slevel == 99) continue; /* Save the spell name */ as->name = spell_names[realm - 1][spell[what]]; /* Realm Name */ if (realm == 1) as->realm_name = "Life"; if (realm == 2) as->realm_name = "Sorcery"; if (realm == 3) as->realm_name = "Nature"; if (realm == 4) as->realm_name = "Chaos"; if (realm == 5) as->realm_name = "Death"; if (realm == 6) as->realm_name = "Trump"; if (realm == 7) as->realm_name = "Arcane"; /* Save the Realm, if any */ as->realm = realm; /* Save the spell index */ as->cheat = spell[what]; /* Hack -- assume excessive level */ as->status = BORG_MAGIC_HIGH; /* Access the correct "method" */ as->method = borg_magic_method[realm][book][what]; /* Access the correct "rating" */ as->rating = borg_magic_rating[realm][book][what]; /* Extract the level and power */ as->level = s_ptr->slevel; as->power = s_ptr->smana; } } /* * Prepare a Mindcrafter Array */ static void prepare_mind_info(void) { int spell; /* Reset each spell entry */ for (spell = 0; spell < MINDCRAFT_MAX; spell++) { borg_mind *as = &borg_minds[spell]; mindcraft_power *s_ptr = &mindcraft_powers[spell]; /* name */ as->name = s_ptr->name; /* values */ as->level = s_ptr->min_lev; as->power = s_ptr->mana_cost; /* Fail Rate */ as->sfail = s_ptr->fail; /* Delete the text letter address */ as->letter = 'a' + spell; } } /* * Hack -- prepare some stuff based on the player race and class */ void prepare_race_class_info(void) { int book; /* Hack -- Realms */ bp_ptr->realm1 = p_ptr->spell.r[0].realm; bp_ptr->realm2 = p_ptr->spell.r[1].realm; /* Initialize the various spell arrays by book */ for (book = 0; book < 4; book++) { prepare_book_info(bp_ptr->realm1, book); prepare_book_info(bp_ptr->realm2, book); } /* MindCrafters */ if (borg_class == CLASS_MINDCRAFTER) { prepare_mind_info(); } } /* * Bookkeeping function that keeps track of which dungeon the borg is in * and what are the minimal and maximal depths of this dungeon. * Use this function only after the borg presses a '>' or a '<' */ void borg_dungeon_remember(bool down_stairs) { int i; int d, b_d = BORG_MAX_DISTANCE; /* On top of a dungeon. */ if (bp_ptr->depth == 0) { /* There is no dungeon known */ if (!borg_dungeon_num) return; /* Is there dungeon closer? */ for (i = 0; i < borg_dungeon_num; i++) { d = distance(c_x, c_y, borg_dungeons[i].x, borg_dungeons[i].y); /* Ignore dungeons that are further away */ if (d > b_d) continue; /* Remember this dungeon */ b_d = d; dungeon_num = i; } } /* In a dungeon */ else { /* Just checking */ if (dungeon_num == -1) return; /* First time in this dungeon */ if (borg_dungeons[dungeon_num].min_depth == 0 || borg_dungeons[dungeon_num].min_depth > bp_ptr->depth) { /* Set the minimal depth of this dungeon */ borg_dungeons[dungeon_num].min_depth = bp_ptr->depth; } /* Getting deeper than ever before? */ if (down_stairs && borg_dungeons[dungeon_num].max_depth <= bp_ptr->depth) { /* Set the deepest depth of this dungeon */ borg_dungeons[dungeon_num].max_depth = bp_ptr->depth + 1; } /* Reached the bottom? */ if (!down_stairs) { int wid, hgt; if (borg_dungeons[dungeon_num].max_depth < bp_ptr->depth) { /* Set the deepest depth of this dungeon */ borg_dungeons[dungeon_num].max_depth = bp_ptr->depth; } /* Get size */ Term_get_size(&wid, &hgt); /* If the screen says bottom */ if (borg_term_text_comp(wid - T_NAME_LEN, hgt - 1, "Bottom")) { /* The borg has reached the bottom of this dungeon */ borg_dungeons[dungeon_num].bottom = TRUE; } } } } /* * Initialize this file */ void borg_init_3(void) { } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg4.c0000644000000000000000000033205210250356275013514 0ustar rootroot/* File: zborg4.c */ /* Purpose: Notice and Power code for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" /* * Various "amounts" (for the home) */ int num_food; int num_food_scroll; int num_ident; int num_star_ident; int num_remove_curse; int num_star_remove_curse; int num_recall; int num_phase; int num_escape; int num_teleport; int num_berserk; int num_teleport_level; int num_cure_critical; int num_cure_serious; int num_cure_light; int num_pot_resist; int num_missile; int num_book[8][4]; /* [realm][book] */ int num_fix_stat[7]; /* #7 is to fix all stats */ int num_fix_exp; int num_mana; int num_heal; int num_ez_heal; int num_glyph; int num_mass_genocide; int num_goi_pot; int num_brand_weapon; /*apw brand bolts */ int num_genocide; s16b home_slot_free; s16b home_damage; s16b num_duplicate_items; int num_slow_digest; int num_regenerate; int num_telepathy; int num_lite; int num_see_inv; int num_invisible; /* apw */ int num_ffall; int num_free_act; int num_hold_life; int num_immune_acid; int num_immune_elec; int num_immune_fire; int num_immune_cold; int num_resist_acid; int num_resist_elec; int num_resist_fire; int num_resist_cold; int num_resist_pois; int num_resist_conf; int num_resist_sound; int num_resist_lite; int num_resist_dark; int num_resist_chaos; int num_resist_disen; int num_resist_shard; int num_resist_nexus; int num_resist_blind; int num_resist_neth; int num_sustain_str; int num_sustain_int; int num_sustain_wis; int num_sustain_dex; int num_sustain_con; int num_sustain_all; int num_artifact; int num_bad_curse; int num_speed; int num_edged_weapon; int num_bad_gloves; int num_weapons; int num_bow; int num_rings; int num_neck; int num_armor; int num_cloaks; int num_shields; int num_hats; int num_gloves; int num_boots; /* * Remember items in the home. (Only one home at a time) */ list_item *borg_home; int home_num; /* The shop that corresponds to the current home */ int home_shop = -1; /* Include shop items in power calculation */ int use_shop; void borg_list_info(byte list_type, vptr dummy) { /* Hack - ignore parameter */ (void) dummy; /* Don't do anything if the borg is inactive */ if (!borg_active) { /* Done */ return; } /* Notice changes */ switch (list_type) { case LIST_INVEN: { /* Inventory changed so goals must change. */ goal_shop = -1; /* Note changed inventory */ borg_do_destroy = TRUE; break; } case LIST_EQUIP: { /* Equipment changed so goals must change. */ goal_shop = -1; /* Note changed inventory */ borg_do_destroy = TRUE; break; } case LIST_FLOOR: { break; } case LIST_STORE: { break; } case LIST_HOME: { /* Number of items */ home_num = cur_num; /* Save items for later... */ C_COPY(borg_home, cur_list, cur_num, list_item); break; } default: { /* Paranoia */ quit_fmt("Unrecognised list type %d", list_type); } } } /* * Note that we assume that any item with quantity zero does not exist, * thus, when simulating possible worlds, we do not actually have to * "optimize" empty slots. * * XXX XXX XXX Also, we could reward equipment based on possible enchantment, * up to the maximal amount available in the home, which would induce item * switching when the item could be enchanted sufficiently. */ /* * The "notice" functions examine various aspects of the player inventory, * the player equipment, or the home contents, and extract various numerical * quantities based on those aspects, adjusting them for various "abilities", * such as the ability to cast certain spells, etc. * * The "power" functions use the numerical quantities described above, and * use them to do two different things: (1) rank the "value" of having * various abilities relative to the possible "money" reward of carrying * sellable items instead, and (2) rank the value of various abilities * relative to each other, which is used to determine what to wear/buy, * and in what order to wear/buy those items. * * These functions use some very heuristic values, by the way... * * We should probably take account of things like possible enchanting * (especially when in town), and items which may be found soon. * * We consider several things: * (1) the actual "power" of the current weapon and bow * (2) the various "flags" imparted by the equipment * (3) the various abilities imparted by the equipment * (4) the penalties induced by heavy armor or gloves or edged weapons * (5) the abilities required to enter the "max_depth" dungeon level * (6) the various abilities of some useful inventory items * * Note the use of special "item counters" for evaluating the value of * a collection of items of the given type. Basically, the first item * of the given type is always the most valuable, with subsequent items * being worth less, until the "limit" is reached, after which point any * extra items are only worth as much as they can be sold for. */ /* * Notice player flags */ static void borg_notice_player(void) { object_flags oflags; object_flags *of_ptr = &oflags; /* Recalc some Variables */ bp_ptr->ac = 0; bp_ptr->speed = 110; /* Start with a single blow per turn */ bp_ptr->blows = 1; /* Base infravision (purely racial) */ bp_ptr->see_infra = rb_ptr->infra; /* Base skill -- disarming */ bp_ptr->skill_dis = rb_ptr->r_dis + cb_ptr->c_dis; /* Base skill -- magic devices */ bp_ptr->skill_dev = rb_ptr->r_dev + cb_ptr->c_dev; /* Base skill -- saving throw */ bp_ptr->skill_sav = rb_ptr->r_sav + cb_ptr->c_sav; /* Base skill -- stealth */ bp_ptr->skill_stl = rb_ptr->r_stl + cb_ptr->c_stl; /* Base skill -- searching ability */ bp_ptr->skill_sns = rb_ptr->r_sns + cb_ptr->c_sns; /* Base skill -- searching frequency */ bp_ptr->skill_fos = rb_ptr->r_fos + cb_ptr->c_fos; /* Base skill -- combat (normal) */ bp_ptr->skill_thn = rb_ptr->r_thn + cb_ptr->c_thn; /* Base skill -- combat (shooting) */ bp_ptr->skill_thb = rb_ptr->r_thb + cb_ptr->c_thb; /* Base skill -- combat (throwing) */ bp_ptr->skill_tht = rb_ptr->r_thb + cb_ptr->c_thb; /* Racial Skills */ /* Extract the player flags */ player_flags(of_ptr); bp_ptr->flags[0] |= oflags.flags[0]; bp_ptr->flags[1] |= oflags.flags[1]; bp_ptr->flags[2] |= oflags.flags[2]; bp_ptr->flags[3] |= oflags.flags[3]; /* Mutation flags */ bp_ptr->muta1 = p_ptr->muta1; bp_ptr->muta2 = p_ptr->muta2; bp_ptr->muta3 = p_ptr->muta3; /* Sustain flags */ if (FLAG(of_ptr, TR_SUST_STR)) bp_ptr->sust[A_STR] = TRUE; if (FLAG(of_ptr, TR_SUST_INT)) bp_ptr->sust[A_INT] = TRUE; if (FLAG(of_ptr, TR_SUST_WIS)) bp_ptr->sust[A_WIS] = TRUE; if (FLAG(of_ptr, TR_SUST_DEX)) bp_ptr->sust[A_DEX] = TRUE; if (FLAG(of_ptr, TR_SUST_CON)) bp_ptr->sust[A_CON] = TRUE; if (FLAG(of_ptr, TR_SUST_CHR)) bp_ptr->sust[A_CHR] = TRUE; /* Bloating slows the player down (a little) */ if (bp_ptr->status.gorged) bp_ptr->speed -= 10; } /* * Find which item goes in an equipment slot. * * Normally, this is just the item already there, * however, sometimes we want to simulate another * item being in that location. * * Note: there must only be one TREAT_AS_SWAP * item in the inventory at any time. */ list_item *look_up_equip_slot(int slot) { list_item *l_ptr; int i; /* check for valid slots */ if (slot < 0 || slot > equip_num) return (NULL); /* Look in equipment */ l_ptr = &equipment[slot]; /* Does it exist and are we aware? */ if (l_ptr->k_idx) { /* Normal item? */ if (l_ptr->treat_as == TREAT_AS_NORM) return (l_ptr); /* Missing item? */ if (l_ptr->treat_as == TREAT_AS_GONE) return (NULL); /* Assume TREAT_AS_SWAP */ } else { /* Is it an empty slot or an unknown ring or amulet */ if (l_ptr->tval == TV_RING || l_ptr->tval == TV_AMULET) return (l_ptr); /* Optimise common case of empty slot */ if (l_ptr->treat_as != TREAT_AS_SWAP) return (NULL); } /* Look at current shop */ if (use_shop) { for (i = 0; i < cur_num; i++) { l_ptr = &cur_list[i]; /* Does it exist and are we aware? */ if (l_ptr->k_idx) { /* The item to swap with */ if (l_ptr->treat_as == TREAT_AS_SWAP) return (l_ptr); } } } else { /* Otherwise, scan the inventory */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Does it exist and are we aware? */ if (l_ptr->k_idx) { /* The item to swap with */ if (l_ptr->treat_as != TREAT_AS_NORM) return (l_ptr); } } } /* No match! */ return (NULL); } /* Does this item have some bad curse that the borg can't handle */ bool borg_test_bad_curse(list_item *l_ptr) { int i; list_item temp; /* Just checking */ if (!l_ptr) return (FALSE); /* No borg can handle not teleporting */ if (KN_FLAG(l_ptr, TR_NO_TELE)) return (TRUE); /* The borg can't keep up with this drain */ if (KN_FLAG(l_ptr, TR_DRAIN_EXP) && bp_ptr->lev < 50) return (TRUE); /* This curse is meaningless for warriors */ if (KN_FLAG(l_ptr, TR_NO_MAGIC) && borg_class != CLASS_WARRIOR) return (TRUE); /* This curse is no problem if all stats are sustained */ if (KN_FLAG(l_ptr, TR_DRAIN_STATS) || KN_FLAG(l_ptr, TR_TY_CURSE)) { /* Clear */ temp.kn_flags[1] = 0; /* Check the equipment */ for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* No empty slots */ if (!l_ptr) continue; /* Copy the flags */ temp.kn_flags[1] |= l_ptr->kn_flags[1]; } /* If there are enough sustains this curse can be ignored */ if (!KN_FLAG(&temp, TR_SUST_STR) || !KN_FLAG(&temp, TR_SUST_INT) || !KN_FLAG(&temp, TR_SUST_WIS) || !KN_FLAG(&temp, TR_SUST_DEX) || !KN_FLAG(&temp, TR_SUST_CON)) { /* not enough sustains */ return (TRUE); } /* Only high level borgs can handle topi */ if (KN_FLAG(l_ptr, TR_TY_CURSE) && bp_ptr->lev < 50) return (TRUE); } /* No curse */ return (FALSE); } /* Determine if this item should be id'd or something. */ static void borg_notice_improve_item(list_item *l_ptr, bool equip) { /* Paranoia */ if (!l_ptr) return; /* Does it need to be id'd? */ if (!borg_obj_known_p(l_ptr)) { /* Count it */ bp_ptr->able.id_item += 1; } /* Do the other checks only for identified items */ else { /* Does it need to be star_id'd? */ if (!borg_obj_known_full(l_ptr) && borg_obj_star_id_able(l_ptr)) bp_ptr->able.star_id_item += 1; /* All equipment or interesting items from the inventory */ if (equip || borg_obj_is_ego_art(l_ptr)) { /* Check for cursed items */ if (KN_FLAG(l_ptr, TR_CURSED)) bp_ptr->status.cursed = TRUE; /* Maybe try a *remove curse* on this item */ if (KN_FLAG(l_ptr, TR_HEAVY_CURSE)) bp_ptr->status.heavy_curse = TRUE; } } } /* * Notice the effects of equipment */ static void borg_notice_equip(int *extra_blows, int *extra_shots, int *extra_might) { int i; list_item *l_ptr; /* Scan the equipment */ for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* Ignore empty slots */ if (!l_ptr) continue; /* There is no flag for an amulet of sustenance */ if (i == EQUIP_NECK) { /* Not too much or the borg will never replace this amulet */ if (k_info[l_ptr->k_idx].sval == SV_AMULET_SUSTENANCE) { bp_ptr->food += 5; SET_FLAG(bp_ptr, TR_SLOW_DIGEST); } /* You can only see the luck flag when it has *id* */ if (k_info[l_ptr->k_idx].sval == SV_AMULET_LUCK) { SET_FLAG(bp_ptr, TR_LUCK_10); } } /* If the borg has a cloak that is not identified */ if (i == EQUIP_OUTER && !borg_obj_known_p(l_ptr)) { /* Shadow cloak */ if (k_info[l_ptr->k_idx].sval == SV_SHADOW_CLOAK) { /* Add the dark and light flags */ SET_FLAG(bp_ptr, TR_RES_DARK); SET_FLAG(bp_ptr, TR_RES_LITE); } /* Elven cloak */ if (k_info[l_ptr->k_idx].sval == SV_ELVEN_CLOAK) { /* Surely not a bad cloak */ bp_ptr->skill_stl += 1; } } /* Does this item need id or remove curse? */ borg_notice_improve_item(l_ptr, TRUE); /* Affect stats */ if (KN_FLAG(l_ptr, TR_STR)) my_stat_add[A_STR] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_INT)) my_stat_add[A_INT] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_WIS)) my_stat_add[A_WIS] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_DEX)) my_stat_add[A_DEX] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_CON)) my_stat_add[A_CON] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_CHR)) my_stat_add[A_CHR] += l_ptr->pval; /* Affect flags */ bp_ptr->flags[0] |= l_ptr->kn_flags[0]; bp_ptr->flags[1] |= l_ptr->kn_flags[1]; bp_ptr->flags[2] |= l_ptr->kn_flags[2]; bp_ptr->flags[3] |= l_ptr->kn_flags[3]; /* Affect infravision */ if (KN_FLAG(l_ptr, TR_INFRA)) bp_ptr->see_infra += l_ptr->pval; /* Affect stealth */ if (KN_FLAG(l_ptr, TR_STEALTH)) bp_ptr->skill_stl += l_ptr->pval; /* Affect saving throw */ if (KN_FLAG(l_ptr, TR_LUCK_10)) bp_ptr->skill_sav += 10; /* Affect searching ability (factor of five) */ if (KN_FLAG(l_ptr, TR_SEARCH)) bp_ptr->skill_sns += l_ptr->pval * 5; /* Affect searching frequency (factor of five) */ if (KN_FLAG(l_ptr, TR_SEARCH)) bp_ptr->skill_fos += l_ptr->pval * 5; /* Affect digging (factor of 20) */ if (KN_FLAG(l_ptr, TR_TUNNEL)) bp_ptr->skill_dig += l_ptr->pval * 20; /* Affect speed */ if (KN_FLAG(l_ptr, TR_SPEED)) bp_ptr->speed += l_ptr->pval; /* Affect spell points */ if (KN_FLAG(l_ptr, TR_SP)) bp_ptr->mana_bonus += l_ptr->pval; /* Affect blows */ if (KN_FLAG(l_ptr, TR_BLOWS)) *extra_blows += l_ptr->pval; /* Boost shots */ if (KN_FLAG(l_ptr, TR_XTRA_SHOTS)) (*extra_shots)++; /* Boost might */ if (KN_FLAG(l_ptr, TR_XTRA_MIGHT)) (*extra_might)++; /* Immunity flags */ if (KN_FLAG(l_ptr, TR_IM_FIRE)) my_oppose_fire = TRUE; if (KN_FLAG(l_ptr, TR_IM_ACID)) my_oppose_elec = TRUE; if (KN_FLAG(l_ptr, TR_IM_COLD)) my_oppose_elec = TRUE; if (KN_FLAG(l_ptr, TR_IM_ELEC)) my_oppose_elec = TRUE; /* Sustain flags */ if (KN_FLAG(l_ptr, TR_SUST_STR)) bp_ptr->sust[A_STR] = TRUE; if (KN_FLAG(l_ptr, TR_SUST_INT)) bp_ptr->sust[A_INT] = TRUE; if (KN_FLAG(l_ptr, TR_SUST_WIS)) bp_ptr->sust[A_WIS] = TRUE; if (KN_FLAG(l_ptr, TR_SUST_DEX)) bp_ptr->sust[A_DEX] = TRUE; if (KN_FLAG(l_ptr, TR_SUST_CON)) bp_ptr->sust[A_CON] = TRUE; if (KN_FLAG(l_ptr, TR_SUST_CHR)) bp_ptr->sust[A_CHR] = TRUE; /* Modify the base armor class */ bp_ptr->ac += l_ptr->ac; /* Apply the bonuses to armor class */ bp_ptr->ac += l_ptr->to_a; /* Keep track of weight */ bp_ptr->weight += l_ptr->weight; /* Hack -- do not apply "weapon" bonuses */ if (i == EQUIP_WIELD) continue; /* Hack -- do not apply "bow" bonuses */ if (i == EQUIP_BOW) continue; /* Apply the bonuses to hit/damage */ bp_ptr->to_h += l_ptr->to_h; bp_ptr->to_d += l_ptr->to_d; } } /* * Recalculate player stats */ static void borg_notice_stats(void) { int i; /* Update stats */ for (i = 0; i < A_MAX; i++) my_stat_ind[i] = MIN(37, p_ptr->stat[i].ind); /* Actual Modifier Bonuses (Un-inflate stat bonuses) */ bp_ptr->ac += ((int)(adj_dex_ta[my_stat_ind[A_DEX]]) - 128); bp_ptr->to_d += ((int)(adj_str_td[my_stat_ind[A_STR]]) - 128); bp_ptr->to_h += ((int)(adj_dex_th[my_stat_ind[A_DEX]]) - 128); } /* * Examine bow */ static void borg_notice_shooter(int hold, int extra_might, int extra_shots) { list_item *l_ptr; /* Start with a single shot per turn */ int my_num_fire = 1; /* Reset the "ammo" tval to darts by default */ my_ammo_tval = 0; /* Reset the shooting power */ my_ammo_power = 0; /* Reset the shooting range */ my_ammo_range = 0; /* Examine the "current bow" */ l_ptr = look_up_equip_slot(EQUIP_BOW); /* No bow? */ if (!l_ptr) return; bp_ptr->b_to_d = l_ptr->to_d; if (bp_ptr->b_to_d < 8 && bp_ptr->lev >= 25) bp_ptr->b_to_d = 8; /* It is hard to carry a heavy bow */ if (hold < l_ptr->weight / 10) { /* Hard to wield a heavy bow */ bp_ptr->to_h += 2 * (hold - l_ptr->weight / 10); } /* Compute "extra shots" if needed */ if (l_ptr->k_idx && (hold >= l_ptr->weight / 10)) { /* Take note of required "tval" for missiles */ switch (k_info[l_ptr->k_idx].sval) { case SV_SLING: { my_ammo_tval = TV_SHOT; my_ammo_power = 16; if (extra_might) my_ammo_power = 24; break; } case SV_SHORT_BOW: { my_ammo_tval = TV_ARROW; if (extra_might) { my_ammo_power = 18; my_ammo_range = 20; } else { my_ammo_power = 12; my_ammo_range = 15; } break; } case SV_LONG_BOW: { my_ammo_tval = TV_ARROW; if (borg_stat[A_STR] >= 160) { if (extra_might) { my_ammo_power = 24; my_ammo_range = 25; } else { my_ammo_power = 18; my_ammo_range = 20; } } else { if (extra_might) { my_ammo_power = 18; my_ammo_range = 20; } else { my_ammo_power = 12; my_ammo_range = 15; } } break; } case SV_LIGHT_XBOW: { my_ammo_tval = TV_BOLT; if (extra_might) { my_ammo_power = 28; my_ammo_range = 30; } else { my_ammo_power = 23; my_ammo_range = 25; } break; } case SV_HEAVY_XBOW: { my_ammo_tval = TV_BOLT; if (extra_might) { my_ammo_power = 29; my_ammo_range = 35; } else { my_ammo_power = 24; my_ammo_range = 30; } break; } } /* Hack -- Reward High Level Rangers using Bows */ if ((borg_class == CLASS_RANGER) && (my_ammo_tval == TV_ARROW)) { /* Extra shot at level 15 */ if (bp_ptr->lev >= 15) my_num_fire++; /* Extra shot at level 30 */ if (bp_ptr->lev >= 30) my_num_fire++; /* Extra shot at level 45 */ if (bp_ptr->lev >= 45) my_num_fire++; } /* Hack -- Reward High Level Rangers using XBows */ if ((borg_class == CLASS_RANGER) && (my_ammo_tval == TV_BOLT)) { /* Extra shot at level 30 */ if (bp_ptr->lev >= 30) my_num_fire++; } /* Hack -- Reward High Level Rogues using Slings */ if ((borg_class == CLASS_RANGER) && (my_ammo_tval == TV_SHOT)) { /* Extra shot at level 20 */ if (bp_ptr->lev >= 20) my_num_fire++; /* Extra shot at level 40 */ if (bp_ptr->lev >= 40) my_num_fire++; } /* Hack -- Reward High Level Warriors */ if (borg_class == CLASS_WARRIOR) { /* Extra shot at level 40 */ if (bp_ptr->lev >= 40) my_num_fire++; } /* Add in the "bonus shots" */ my_num_fire += extra_shots; /* Require at least one shot */ if (my_num_fire < 1) my_num_fire = 1; } /* Calculate "average" damage per "normal" shot (times 2) */ bp_ptr->b_max_dam = my_ammo_power * (bp_ptr->b_to_d + 100) * 3 / 100; bp_ptr->b_max_dam *= (my_num_fire + 1) / 2; } /* * Examine sword */ static void borg_notice_weapon(int hold, int extra_blows) { list_item *l_ptr; /* Examine the "main weapon" */ l_ptr = look_up_equip_slot(EQUIP_WIELD); /* No weapon? */ if (!l_ptr) return; bp_ptr->w_to_d = l_ptr->to_d; if (bp_ptr->w_to_d < 8 && bp_ptr->lev >= 25) bp_ptr->w_to_d = 8; /* It is hard to hold a heavy weapon */ if (hold < l_ptr->weight / 10) { bp_ptr->status.hvy_weapon = TRUE; /* Hard to wield a heavy weapon */ bp_ptr->to_h += 2 * (hold - l_ptr->weight / 10); } else { bp_ptr->status.hvy_weapon = FALSE; } /* Normal weapons */ if (l_ptr->k_idx && (hold >= l_ptr->weight / 10)) { int str_index, dex_index; int num = 0, wgt = 0, mul = 0, div = 0; /* Analyze the class */ switch (borg_class) { case CLASS_WARRIOR: { /* Warrior */ num = 5; wgt = 30; mul = 5; break; } case CLASS_MAGE: case CLASS_HIGH_MAGE: { /* Mage */ num = 2; wgt = 40; mul = 2; break; } case CLASS_PRIEST: case CLASS_MINDCRAFTER: { /* Priest, Mindcrafter */ num = 4; wgt = 35; mul = 3; break; } case CLASS_ROGUE: { /* Rogue */ num = 4; wgt = 30; mul = 3; break; } case CLASS_RANGER: { /* Ranger */ num = 4; wgt = 35; mul = 4; break; } case CLASS_PALADIN: { /* Paladin */ num = 4; wgt = 30; mul = 4; break; } case CLASS_WARRIOR_MAGE: { /* Warrior-Mage */ num = 4; wgt = 35; mul = 3; break; } case CLASS_CHAOS_WARRIOR: { /* Chaos Warrior */ num = 4; wgt = 30; mul = 4; break; } case CLASS_MONK: { /* Monk */ num = ((p_ptr->lev < 40) ? 2 : 3); wgt = 40; mul = 4; break; } } /* Enforce a minimum "weight" (tenth pounds) */ div = ((l_ptr->weight < wgt) ? wgt : l_ptr->weight); /* Get the strength vs weight */ str_index = (adj_str_blow[my_stat_ind[A_STR]] * mul / div); /* Maximal value */ if (str_index > 11) str_index = 11; /* Index by dexterity */ dex_index = (adj_dex_blow[my_stat_ind[A_DEX]]); /* Maximal value */ if (dex_index > 11) dex_index = 11; /* Use the blows table */ bp_ptr->blows = blows_table[str_index][dex_index]; /* Maximal value */ if (bp_ptr->blows > num) bp_ptr->blows = num; /* Add in the "bonus blows" */ bp_ptr->blows += extra_blows; /* Require at least one blow */ if (bp_ptr->blows < 1) bp_ptr->blows = 1; /* Boost digging skill by weapon weight */ bp_ptr->skill_dig += (l_ptr->weight / 10); } /* priest weapon penalty for non-blessed edged weapons */ if ((borg_class == CLASS_PRIEST) && ((l_ptr->tval == TV_SWORD) || (l_ptr->tval == TV_POLEARM)) && !KN_FLAG(l_ptr, TR_BLESSED)) { /* Reduce the real bonuses */ bp_ptr->to_h -= 5; bp_ptr->to_d -= 5; } } /* * Notice skills */ static void borg_notice_skills(void) { /* Affect Skill -- stealth (bonus one) */ bp_ptr->skill_stl += 1; /* Affect Skill -- disarming (DEX and INT) */ bp_ptr->skill_dis += adj_dex_dis[my_stat_ind[A_DEX]]; bp_ptr->skill_dis += adj_int_dis[my_stat_ind[A_INT]]; /* Affect Skill -- magic devices (INT) */ bp_ptr->skill_dev += adj_int_dev[my_stat_ind[A_INT]]; /* Affect Skill -- saving throw (WIS) */ bp_ptr->skill_sav += adj_wis_sav[my_stat_ind[A_WIS]]; /* Affect Skill -- digging (STR) */ bp_ptr->skill_dig += adj_str_dig[my_stat_ind[A_STR]]; /* Affect Skill -- disarming (Level, by Class) */ bp_ptr->skill_dis += (cb_ptr->x_dis * bp_ptr->max_lev / 10); /* Affect Skill -- magic devices (Level, by Class) */ bp_ptr->skill_dev += (cb_ptr->x_dev * bp_ptr->max_lev / 10); /* Affect Skill -- saving throw (Level, by Class) */ bp_ptr->skill_sav += (cb_ptr->x_sav * bp_ptr->max_lev / 10); /* Affect Skill -- stealth (Level, by Class) */ bp_ptr->skill_stl += (cb_ptr->x_stl * bp_ptr->max_lev / 10); /* Affect Skill -- search ability (Level, by Class) */ bp_ptr->skill_sns += (cb_ptr->x_sns * bp_ptr->max_lev / 10); /* Affect Skill -- search frequency (Level, by Class) */ bp_ptr->skill_fos += (cb_ptr->x_fos * bp_ptr->max_lev / 10); /* Affect Skill -- combat (normal) (Level, by Class) */ bp_ptr->skill_thn += (cb_ptr->x_thn * bp_ptr->max_lev / 10); /* Affect Skill -- combat (shooting) (Level, by Class) */ bp_ptr->skill_thb += (cb_ptr->x_thb * bp_ptr->max_lev / 10); /* Affect Skill -- combat (throwing) (Level, by Class) */ bp_ptr->skill_tht += (cb_ptr->x_thb * bp_ptr->max_lev / 10); /* Limit Skill -- stealth from 0 to 30 */ if (bp_ptr->skill_stl > 30) bp_ptr->skill_stl = 30; if (bp_ptr->skill_stl < 0) bp_ptr->skill_stl = 0; /* Limit Skill -- digging from 1 up */ if (bp_ptr->skill_dig < 1) bp_ptr->skill_dig = 1; } /* * Monks are special */ static void borg_recalc_monk(int extra_blows) { int monk_arm_wgt = 0; int ma = MAX_MA - 1; const martial_arts *ma_ptr = &ma_blows[MAX_MA]; int i; list_item *l_ptr; /* Weigh the armor */ for (i = EQUIP_BODY; i <= EQUIP_FEET; i++) { l_ptr = look_up_equip_slot(i); /* Add up the total */ if (l_ptr) monk_arm_wgt += l_ptr->weight; } /* Consider the Martial Arts */ if (!look_up_equip_slot(EQUIP_WIELD)) { bp_ptr->blows = 2; if (bp_ptr->lev > 9) bp_ptr->blows++; if (bp_ptr->lev > 14) bp_ptr->blows++; if (bp_ptr->lev > 24) bp_ptr->blows++; if (bp_ptr->lev > 34) bp_ptr->blows++; if (bp_ptr->lev > 44) bp_ptr->blows++; if (bp_ptr->lev > 49) bp_ptr->blows++; if (monk_arm_wgt < (100 + (bp_ptr->lev * 4))) { bp_ptr->to_h += (bp_ptr->lev / 3); bp_ptr->to_d += (bp_ptr->lev / 3); } else { bp_ptr->blows /= 2; } bp_ptr->blows += extra_blows; /* Calculate best Monk Attacks */ while (ma != 0) { ma_ptr = &ma_blows[ma]; /* Can do this attack */ if (bp_ptr->lev >= ma_ptr->min_level) break; /* Reduce the ma level and try again */ ma--; } } /** Monk Armour **/ /* Unencumbered Monks become faster every 10 levels */ if (monk_arm_wgt < (100 + (bp_ptr->lev * 4))) { bp_ptr->speed += (bp_ptr->lev) / 10; if (!look_up_equip_slot(EQUIP_BODY)) { bp_ptr->ac += (bp_ptr->lev * 3) / 2; } if (!look_up_equip_slot(EQUIP_OUTER) && (bp_ptr->lev > 15)) { bp_ptr->ac += ((bp_ptr->lev - 13) / 3); } if (!look_up_equip_slot(EQUIP_ARM) && (bp_ptr->lev > 10)) { bp_ptr->ac += ((bp_ptr->lev - 8) / 3); } if (!look_up_equip_slot(EQUIP_HEAD) && (bp_ptr->lev > 4)) { bp_ptr->ac += (bp_ptr->lev - 2) / 3; } if (!look_up_equip_slot(EQUIP_HANDS)) { bp_ptr->ac += (bp_ptr->lev / 2); } if (!look_up_equip_slot(EQUIP_FEET)) { bp_ptr->ac += (bp_ptr->lev / 3); } } } /* Return the item in the equipment with the lowest ac */ int borg_notice_enchant_ac(void) { int i, b_i = -1; int best = 15; list_item *l_ptr; /* Hack -- enchant all the equipment (armor) */ for (i = EQUIP_BODY; i <= EQUIP_FEET; i++) { l_ptr = look_up_equip_slot(i); /* Skip empty items */ if (!l_ptr) continue; /* Skip "unknown" items */ if (!borg_obj_known_p(l_ptr)) continue; /* Skip items with ac higher then current best */ if (l_ptr->to_a >= best) continue; /* Remember this one */ b_i = i; best = l_ptr->to_a; } return (b_i); } /* Return the item with the lowest to_hit */ int borg_notice_enchant_hit(bool *inven) { int i, ammo = -1, weapon = -1; int best_a = 15, best_w = 15; /* look through inventory for ammo */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Only enchant if qty >= 5 */ if (l_ptr->number < 5) continue; /* Skip non-identified items */ if (!borg_obj_known_p(l_ptr)) continue; /* Make sure it is the right type if missile */ if (l_ptr->tval != my_ammo_tval) continue; /* Find the least enchanted item */ if (l_ptr->to_h >= best_a) continue; /* Save the info */ ammo = i; best_a = l_ptr->to_h; } /* Look for a weapon that needs enchanting */ for (i = EQUIP_WIELD; i <= EQUIP_BOW; i++) { list_item *l_ptr = look_up_equip_slot(i); /* Skip empty slots */ if (!l_ptr) continue; /* Skip non-identified items */ if (!borg_obj_known_p(l_ptr)) continue; /* Find the least enchanted item */ if (l_ptr->to_h >= best_w) continue; /* Save the info */ weapon = i; best_w = l_ptr->to_h; } /* If the weapon is high and the ammo is low */ if (best_w >= 10 && best_a < 10) { /* Return the ammo */ *inven = TRUE; return (ammo); } /* Otherwise the weapon */ *inven = FALSE; return (weapon); } /* Return the item with the lowest to_dam bonus */ int borg_notice_enchant_dam(bool *inven) { int i, ammo = -1, weapon = -1; int best_a = 25, best_w = 25; /* look through inventory for ammo */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Only enchant if qty >= 5 */ if (l_ptr->number < 5) continue; /* Skip non-identified items */ if (!borg_obj_known_p(l_ptr)) continue; /* Make sure it is the right type if missile */ if (l_ptr->tval != my_ammo_tval) continue; /* Find the least enchanted item */ if (l_ptr->to_d >= best_a) continue; /* Save the info */ ammo = i; best_a = l_ptr->to_d; } /* Look for a weapon that needs enchanting */ for (i = EQUIP_WIELD; i <= EQUIP_BOW; i++) { list_item *l_ptr = look_up_equip_slot(i); /* Skip empty slots */ if (!l_ptr) continue; /* Skip non-identified items */ if (!borg_obj_known_p(l_ptr)) continue; /* Find the least enchanted item */ if (l_ptr->to_d >= best_w) continue; /* Save the info */ weapon = i; best_w = l_ptr->to_d; } /* If the weapon is high and the ammo is low */ if (best_w >= 10 && best_a < 10) { /* Return the ammo */ *inven = TRUE; return (ammo); } /* Otherwise the weapon */ *inven = FALSE; return (weapon); } static s16b borg_notice_artify_item(list_item *l_ptr) { s16b value = 0; list_item *q_ptr; /* Just making sure */ if (!l_ptr) return (-1); /* There can be only one */ if (l_ptr->number != 1) return (-1); /* It needs to be identified already */ if (!borg_obj_known_p(l_ptr)) return (-1); /* No double features */ if (borg_obj_is_ego_art(l_ptr)) return (-1); /* The values have a factor to compare bows with armours or weapons */ switch (l_ptr->tval) { case TV_BOW: { /* Value of the multiplier */ switch (k_info[l_ptr->k_idx].sval) { case SV_HEAVY_XBOW: value += 2; case SV_LIGHT_XBOW: value += 2; case SV_LONG_BOW: value += 2; case SV_SHORT_BOW: case SV_SLING: value += 4; } break; } case TV_HAFTED: case TV_POLEARM: case TV_SWORD: { /* Product of the hit dice */ value = (l_ptr->dd * l_ptr->ds + 4) * 2 / 5; break; } case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: { /* Armour count */ value = l_ptr->ac; break; } /* Aim for a high base ac */ case TV_SOFT_ARMOR: case TV_HARD_ARMOR: { /* Armour count */ value = (l_ptr->ac + 4) / 5; break; } default: return (-1); } /* Check out the future slot */ q_ptr = &equipment[borg_wield_slot(l_ptr)]; if (q_ptr) { /* Boost the value if the current slot is taken by a non-artifact */ if (!KN_FLAG(q_ptr, TR_INSTA_ART)) value *= 10; /* Boost the value if the current slot is taken by a non-ego */ if (!borg_obj_is_ego_art(q_ptr)) value *=10; } /* Tell the result */ return (value); } /* Report which item should be artified */ int borg_notice_create_artifact(bool *b_inven) { bool inven; int i, slot = -1; int v = 0, b_v = 0; list_item *l_ptr; /* Initialize */ *b_inven = FALSE; /* Does the borg have the scroll? */ if (!borg_read_scroll_fail(SV_SCROLL_ARTIFACT)) return (-1); /* don't bother if the level is too low */ if (bp_ptr->lev < 15) return (-1); /* Do it at town level in order to check shops first */ if (bp_ptr->depth) return (-1); /* Check the available items */ for (i = 0; i < equip_num + inven_num; i++) { /* Set flag */ inven = i >= equip_num; /* Get an item */ if (inven) l_ptr = &inventory[i - equip_num]; else l_ptr = look_up_equip_slot(i); /* Is it really an item? */ if (!l_ptr) continue; /* Now assign value */ v = borg_notice_artify_item(l_ptr); /* Is it better than before? */ if (v <= b_v) continue; /* Remember the best item */ *b_inven = inven; b_v = v; /* Which slot is that? */ if (inven) slot = i - equip_num; else slot = i; } /* Report whatever */ return (slot); } /* * Notice changes in lighting */ static void borg_notice_lite(void) { list_item *l_ptr; /* Assume normal lite radius */ bp_ptr->cur_lite = 0; /* Glowing player has light */ if (bp_ptr->britelite) bp_ptr->cur_lite = 1; /* Examine the lite */ l_ptr = look_up_equip_slot(EQUIP_LITE); /* Item missing? */ if (!l_ptr) return; /* Lite */ if (l_ptr->tval == TV_LITE) { object_kind *k_ptr = &k_info[l_ptr->k_idx]; /* If it is a torch with fuel or everburning */ if ((k_ptr->sval == SV_LITE_TORCH) && (l_ptr->timeout || KN_FLAG(l_ptr, TR_LITE))) { /* Torches -- radius one */ bp_ptr->cur_lite += 1; } /* If it is a Lantern */ if (k_ptr->sval == SV_LITE_LANTERN) { /* And it has fuel */ if (l_ptr->timeout) { /* Lanterns -- radius two */ bp_ptr->cur_lite += 2; } /* No fuel */ else { /* Is it everburning? */ if (KN_FLAG(l_ptr, TR_LITE)) { /* Unfueled Lantern of Everburning still has radius 1 */ bp_ptr->cur_lite += 1; } } } if (KN_FLAG(l_ptr, TR_LITE)) { /* Permanently glowing */ bp_ptr->britelite = TRUE; /* A Lantern of Everburning needs fuel only when it is empty. */ if (k_ptr->sval != SV_LITE_LANTERN || l_ptr->timeout) { /* No need for fuel */ bp_ptr->able.fuel += 1000; amt_lantern = 1000; amt_flask = 1000; } } /* Artifact lites -- radius three */ if (KN_FLAG(l_ptr, TR_INSTA_ART)) { bp_ptr->cur_lite += 3; /* Permanently glowing */ bp_ptr->britelite = TRUE; /* No need for fuel */ bp_ptr->able.fuel += 1000; /* Vampires need to be concerned with Artifacts Lites */ if (FLAG(bp_ptr, TR_HURT_LITE) && !FLAG(bp_ptr, TR_RES_LITE)) { bp_ptr->cur_lite = 1; } } } } /* * Helper function -- notice the player equipment */ static void borg_notice_aux1(void) { int i, hold; int extra_blows = 0; int extra_shots = 0; int extra_might = 0; /* Notice player flags */ borg_notice_player(); /* Clear the stat modifiers */ for (i = 0; i < 6; i++) my_stat_add[i] = 0; /* Notice equipment */ borg_notice_equip(&extra_blows, &extra_shots, &extra_might); /* Recalculate the stats */ borg_notice_stats(); /* Obtain the "hold" value */ hold = adj_str_hold[my_stat_ind[A_STR]]; /* Examine ranged weapon */ borg_notice_shooter(hold, extra_might, extra_shots); /* Examine melee weapon */ borg_notice_weapon(hold, extra_blows); /* Recalculate skills */ borg_notice_skills(); /* Monks get bonus for not using weapon or armour */ if (borg_class == CLASS_MONK) { borg_recalc_monk(extra_blows); } /* Examine lite */ borg_notice_lite(); } /* * Notice food */ static void borg_notice_food(list_item *l_ptr, int number) { int sval = k_info[l_ptr->k_idx].sval; /* Analyze */ switch (sval) { case SV_FOOD_WAYBREAD: { bp_ptr->food += number; bp_ptr->able.cure_pois += number; break; } case SV_FOOD_RATION: { bp_ptr->food += number; break; } case SV_FOOD_JERKY: { amt_food_lowcal += number; break; } case SV_FOOD_BISCUIT: { amt_food_lowcal += number; break; } case SV_FOOD_SLIME_MOLD: { amt_food_lowcal += number; break; } case SV_FOOD_RESTORE_STR: { amt_fix_stat[A_STR] += number; break; } case SV_FOOD_RESTORE_CON: { amt_fix_stat[A_CON] += number; break; } case SV_FOOD_RESTORING: { amt_fix_stat[A_STR] += number; amt_fix_stat[A_INT] += number; amt_fix_stat[A_WIS] += number; amt_fix_stat[A_DEX] += number; amt_fix_stat[A_CON] += number; amt_fix_stat[A_CHR] += number; amt_fix_stat[6] += number; break; } case SV_FOOD_CURE_CONFUSION: { bp_ptr->able.cure_conf += number; break; } case SV_FOOD_CURE_BLINDNESS: { bp_ptr->able.cure_blind += number; break; } case SV_FOOD_CURE_POISON: { bp_ptr->able.cure_pois += number; break; } } } /* * Notice Potions */ static void borg_notice_potions(list_item *l_ptr, int number) { int sval = k_info[l_ptr->k_idx].sval; /* Analyze */ switch (sval) { case SV_POTION_HEALING: { bp_ptr->able.heal += number; break; } case SV_POTION_STAR_HEALING: { amt_star_heal += number; break; } case SV_POTION_LIFE: { amt_life += number; break; } case SV_POTION_CURING: { amt_pot_curing += number; /* Fall through */ } case SV_POTION_CURE_CRITICAL: { bp_ptr->able.ccw += number; bp_ptr->able.cure_pois += number; bp_ptr->able.cure_blind += number; bp_ptr->able.cure_conf += number; break; } case SV_POTION_CURE_SERIOUS: { bp_ptr->able.csw += number; bp_ptr->able.cure_conf += number; bp_ptr->able.cure_blind += number; break; } case SV_POTION_CURE_LIGHT: { bp_ptr->able.clw += number; bp_ptr->able.cure_blind += number; break; } case SV_POTION_CURE_POISON: { bp_ptr->able.cure_pois += number; break; } case SV_POTION_SLOW_POISON: { amt_slow_poison += number; break; } case SV_POTION_RESIST_HEAT: { bp_ptr->able.res_heat += number; break; } case SV_POTION_RESIST_COLD: { bp_ptr->able.res_cold += number; break; } case SV_POTION_RESISTANCE: { bp_ptr->able.res_all += number; bp_ptr->able.res_cold += number; bp_ptr->able.res_heat += number; break; } case SV_POTION_INC_STR: { amt_add_stat[A_STR] += number; break; } case SV_POTION_INC_INT: { amt_add_stat[A_INT] += number; break; } case SV_POTION_INC_WIS: { amt_add_stat[A_WIS] += number; break; } case SV_POTION_INC_DEX: { amt_add_stat[A_DEX] += number; break; } case SV_POTION_INC_CON: { amt_add_stat[A_CON] += number; break; } case SV_POTION_INC_CHR: { amt_add_stat[A_CHR] += number; break; } case SV_POTION_RES_STR: { amt_fix_stat[A_STR] += number; break; } case SV_POTION_RES_INT: { amt_fix_stat[A_INT] += number; break; } case SV_POTION_RES_WIS: { amt_fix_stat[A_WIS] += number; break; } case SV_POTION_RES_DEX: { amt_fix_stat[A_DEX] += number; break; } case SV_POTION_RES_CON: { amt_fix_stat[A_CON] += number; break; } case SV_POTION_RES_CHR: { amt_fix_stat[A_CHR] += number; break; } case SV_POTION_RESTORE_EXP: { amt_fix_exp += number; break; } case SV_POTION_SPEED: { bp_ptr->able.speed += number; break; } case SV_POTION_BERSERK_STRENGTH: { bp_ptr->able.berserk += number; break; } case SV_POTION_POISON: { bp_ptr->able.poison += number; break; } case SV_POTION_RESTORE_MANA: { if (borg_class == CLASS_WARRIOR) bp_ptr->able.death += number; else bp_ptr->able.mana += number; break; } case SV_POTION_DETONATIONS: case SV_POTION_DEATH: case SV_POTION_RUINATION: { bp_ptr->able.death += number; break; } case SV_POTION_INVULNERABILITY: { bp_ptr->able.invulnerability += number; break; } } } /* * Notice scrolls */ static void borg_notice_scrolls(list_item *l_ptr, int number) { int sval = k_info[l_ptr->k_idx].sval; /* Analyze the scroll */ switch (sval) { case SV_SCROLL_IDENTIFY: { bp_ptr->able.id += number; break; } case SV_SCROLL_STAR_IDENTIFY: { bp_ptr->able.star_id += number; break; } case SV_SCROLL_REMOVE_CURSE: { bp_ptr->able.remove_curse += number; break; } case SV_SCROLL_STAR_REMOVE_CURSE: { bp_ptr->able.star_remove_curse += number; break; } case SV_SCROLL_RECHARGING: { bp_ptr->able.recharge += number; break; } case SV_SCROLL_PHASE_DOOR: { bp_ptr->able.phase += number; break; } case SV_SCROLL_TELEPORT: { bp_ptr->able.escape += number; bp_ptr->able.teleport += number; break; } case SV_SCROLL_WORD_OF_RECALL: { bp_ptr->recall += number; break; } case SV_SCROLL_STAR_ENCHANT_ARMOR: case SV_SCROLL_ENCHANT_ARMOR: { /* Get the best item to enchant */ int slot = borg_notice_enchant_ac(); /* If there is an item with low ac or the borg has loads of cash */ if (slot != -1 && (equipment[slot].to_a < 10 || borg_gold > 10000)) { /* count the scroll */ amt_enchant_to_a += number; } break; } case SV_SCROLL_STAR_ENCHANT_WEAPON: case SV_SCROLL_ENCHANT_WEAPON_TO_HIT: { int hit, slot; bool inven; /* Get the best item to enchant */ slot = borg_notice_enchant_hit(&inven); /* If there is no item */ if (slot != -1) { /* find out the to_hit value */ hit = (inven) ? inventory[slot].to_h : equipment[slot].to_h; /* If the item has low to_hit or the borg is rich */ if (hit < 10 || borg_gold > 10000) { /* Count the scroll */ amt_enchant_to_h += number; } } /* Fall through for a scroll of *enchant weapon* */ if (sval == SV_SCROLL_ENCHANT_WEAPON_TO_HIT) break; } case SV_SCROLL_ENCHANT_WEAPON_TO_DAM: { int dam, slot; bool inven; /* Get the best item to enchant */ slot = borg_notice_enchant_dam(&inven); /* If there is no item */ if (slot != -1) { /* find out the to_dam value */ dam = (inven) ? inventory[slot].to_d : equipment[slot].to_d; /* If the item has low to_dam or the borg is loaded */ if (dam < 10 || borg_gold > 10000) { /* Count the scroll */ amt_enchant_to_d += number; } } break; } case SV_SCROLL_RUNE_OF_PROTECTION: { bp_ptr->able.glyph += number; break; } case SV_SCROLL_TELEPORT_LEVEL: { bp_ptr->able.teleport_level += number; break; } case SV_SCROLL_SATISFY_HUNGER: { amt_food_scroll += number; bp_ptr->food += number; break; } case SV_SCROLL_ICE: { if (FLAG(bp_ptr, TR_RES_COLD)) bp_ptr->able.logrus += number; break; } case SV_SCROLL_FIRE: { if (FLAG(bp_ptr, TR_RES_FIRE)) bp_ptr->able.logrus += number; break; } case SV_SCROLL_CHAOS: { if (FLAG(bp_ptr, TR_RES_CHAOS)) bp_ptr->able.logrus += number; break; } case SV_SCROLL_DISPEL_UNDEAD: { bp_ptr->able.logrus += number; break; } case SV_SCROLL_LIGHT: { bp_ptr->able.lite += number; break; } case SV_SCROLL_GENOCIDE: { bp_ptr->able.genocide += number; break; } case SV_SCROLL_MASS_GENOCIDE: { bp_ptr->able.mass_genocide += number; break; } case SV_SCROLL_ARTIFACT: { bp_ptr->able.artifact += number; break; } case SV_SCROLL_STAR_ACQUIREMENT: case SV_SCROLL_ACQUIREMENT: { bp_ptr->able.acquire += number; break; } case SV_SCROLL_MUNDANITY: { int i; list_item *l_ptr; /* Check the equipment */ for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* No empty slots */ if (!l_ptr) continue; /* If there is a nasty curse count the mundanity scroll */ if (borg_test_bad_curse(l_ptr)) bp_ptr->able.mundane += number; break; } } } } /* * Notice rods */ static void borg_notice_rods(list_item *l_ptr, int number) { object_kind *k_ptr = &k_info[l_ptr->k_idx]; int sval = k_ptr->sval; /* Analyze */ switch (sval) { case SV_ROD_IDENTIFY: { if (borg_use_item_fail(l_ptr, TRUE)) { bp_ptr->able.id += number * 100; } else { bp_ptr->able.id += number; } break; } case SV_ROD_RECALL: { /* Don't count on it if I suck at activations */ if (borg_use_item_fail(l_ptr, FALSE)) { bp_ptr->recall += number * 100; } break; } case SV_ROD_DETECT_TRAP: { bp_ptr->able.det_trap += number * 100; break; } case SV_ROD_DETECT_DOOR: { bp_ptr->able.det_door += number * 100; break; } case SV_ROD_DETECTION: { bp_ptr->able.det_trap += number * 100; bp_ptr->able.det_door += number * 100; bp_ptr->able.det_evil += number * 100; break; } case SV_ROD_SPEED: { /* Don't count on it if I suck at activations */ if (borg_use_item_fail(l_ptr, FALSE)) { bp_ptr->able.speed += number * 100; } else { bp_ptr->able.speed += number; } break; } case SV_ROD_MAPPING: { bp_ptr->able.magic_map += number * 100; break; } case SV_ROD_ILLUMINATION: { bp_ptr->able.lite += number * 100; break; } case SV_ROD_HEALING: { /* Don't count on it if I suck at activations */ if (borg_use_item_fail(l_ptr, FALSE)) { bp_ptr->able.heal += number; amt_rod_heal += number; } break; } case SV_ROD_CURING: { /* Don't count on it if I suck at activations */ if (borg_use_item_fail(l_ptr, FALSE)) { bp_ptr->able.ccw += number; bp_ptr->able.cure_pois += number; bp_ptr->able.cure_blind += number; bp_ptr->able.cure_conf += number; } break; } case SV_ROD_TELEPORT_AWAY: { bp_ptr->able.teleport_away += number * 100; break; } case SV_ROD_PESTICIDE: { /* Only for small borgs */ if (bp_ptr->lev > 25) break; /* Fall through */ } case SV_ROD_FIRE_BALL: case SV_ROD_ACID_BALL: case SV_ROD_ELEC_BALL: case SV_ROD_COLD_BALL: case SV_ROD_HAVOC: { bp_ptr->able.ball += 5 * number; break; } case SV_ROD_FIRE_BOLT: case SV_ROD_ACID_BOLT: case SV_ROD_ELEC_BOLT: case SV_ROD_COLD_BOLT: case SV_ROD_DRAIN_LIFE: case SV_ROD_LITE: { bp_ptr->able.bolt += 5 * number; break; } } } /* * Notice wands. Separates the wands in ball or bolt wands. * There is a wand feature that makes it hard to count charges: * If you have a Wand of Foo (4 charges) and a Wand of Foo (8 charges) * this is shown as 2 Wands of Foo (12 charges). If you sell 1 in a shop * you sell the Wand of Foo (4 charges). But the borg can only guess that * it is selling 6 charges. I don't think this will cause a loop. */ static void borg_notice_wands(list_item *l_ptr, int number) { int sval = k_info[l_ptr->k_idx].sval; int pval = 0, non_empty = 0; /* Is this is a pile a wands with unknown charges? */ if (!borg_obj_known_p(l_ptr) && !strstr(l_ptr->o_name, "{empty}")) { /* Set the number of wands, later we guess how many charges there are */ non_empty = number; } /* This pile of wands has known pval or is empty */ else { /* Counting this wand while getting it from a shop or home */ if (l_ptr->treat_as == TREAT_AS_SHOP) { /* We get 1 wand and not all the charges */ pval = l_ptr->pval / l_ptr->number; } /* Counting this pile while selling one */ else if (l_ptr->treat_as == TREAT_AS_LESS) { /* We get all the charges except for the charges of one wand */ pval = l_ptr->pval - l_ptr->pval / l_ptr->number; } /* Just count the stack will you */ else { /* All the charges */ pval = l_ptr->pval; } } /* What sort of wand is this? */ switch (sval) { case SV_WAND_TELEPORT_AWAY: { /* count the charges */ bp_ptr->able.teleport_away += pval + 5 * non_empty; break; } /* Ball Wands */ case SV_WAND_ACID_BALL: case SV_WAND_ELEC_BALL: case SV_WAND_FIRE_BALL: case SV_WAND_COLD_BALL: case SV_WAND_ANNIHILATION: case SV_WAND_DRAGON_FIRE: case SV_WAND_DRAGON_COLD: case SV_WAND_DRAGON_BREATH: case SV_WAND_ROCKETS: { /* count the charges */ bp_ptr->able.ball += pval + 5 * non_empty; break; } /* Bolt wands */ case SV_WAND_LITE: case SV_WAND_DRAIN_LIFE: case SV_WAND_STINKING_CLOUD: case SV_WAND_MAGIC_MISSILE: case SV_WAND_ACID_BOLT: case SV_WAND_FIRE_BOLT: case SV_WAND_COLD_BOLT: { /* count the charges */ bp_ptr->able.bolt += pval + 2 * non_empty; break; } /* Don't bother with keeping the rest of the wands */ default: { /* Nothing */ break; } } } /* * Notice staves */ static void borg_notice_staves(list_item *l_ptr, int number) { int sval = k_info[l_ptr->k_idx].sval; /* * Staves should not be carried to The Serpent, he drains * them to heal himself- not good at all */ if ((bp_ptr->max_depth >= 99) && !bp_ptr->winner) { /* skip these */ return; } /* Analyze */ switch (sval) { case SV_STAFF_IDENTIFY: { bp_ptr->able.id += number * l_ptr->pval; break; } case SV_STAFF_CURE_LIGHT: { bp_ptr->able.clw += number * l_ptr->pval; break; } case SV_STAFF_CURING: { bp_ptr->able.ccw += number * l_ptr->pval; bp_ptr->able.cure_pois += number; bp_ptr->able.cure_blind += number; bp_ptr->able.cure_conf += number; break; } case SV_STAFF_TELEPORTATION: { bp_ptr->able.teleport += number * l_ptr->pval; break; } case SV_STAFF_SPEED: { bp_ptr->able.speed += number * l_ptr->pval; break; } case SV_STAFF_HEALING: { bp_ptr->able.heal += number * l_ptr->pval; break; } case SV_STAFF_REMOVE_CURSE: { bp_ptr->able.remove_curse += number * l_ptr->pval; break; } case SV_STAFF_DESTRUCTION: { bp_ptr->able.staff_dest += number * l_ptr->pval; /* Add a token charge to keep the staff */ if (!bp_ptr->able.staff_dest) bp_ptr->able.staff_dest = 1; break; } case SV_STAFF_THE_MAGI: { bp_ptr->able.staff_magi += number * l_ptr->pval; break; } case SV_STAFF_POWER: { bp_ptr->able.staff_cool += number * l_ptr->pval; /* Add a token charge to keep the staff */ if (!bp_ptr->able.staff_cool) bp_ptr->able.staff_cool = 1; break; } case SV_STAFF_HOLINESS: { bp_ptr->able.staff_cool += number * l_ptr->pval; bp_ptr->able.heal += number * l_ptr->pval; /* Add a token charge to keep the staff */ if (!bp_ptr->able.staff_cool) bp_ptr->able.staff_cool = 1; break; } case SV_STAFF_LITE: { bp_ptr->able.lite += number * l_ptr->pval; break; } } } /* * Examine an item in the inventory */ static void borg_notice_inven_item(list_item *l_ptr) { int number; object_kind *k_ptr; if (l_ptr->treat_as == TREAT_AS_LESS) { /* Pretend we have less items */ number = l_ptr->number - 1; } else { /* Is this a home or shop item? */ if (l_ptr->treat_as == TREAT_AS_SHOP) { /* You can buy only one item from a shop */ number = 1; } else { /* Count the whole pile */ number = l_ptr->number; } } /* Does this item need id or remove curse? */ borg_notice_improve_item(l_ptr, FALSE); /* Keep track of weight */ bp_ptr->weight += l_ptr->weight * number; /* Get item type */ k_ptr = &k_info[l_ptr->k_idx]; /* Keep track of (base) value */ bp_ptr->value += k_ptr->cost * number; /* Analyze the item */ switch (l_ptr->tval) { case TV_LIFE_BOOK: { /* Count good books */ if (borg_has_realm(REALM_LIFE)) amt_book[REALM_LIFE][k_ptr->sval] += number; break; } case TV_SORCERY_BOOK: { /* Count good books */ if (borg_has_realm(REALM_SORCERY)) amt_book[REALM_SORCERY][k_ptr->sval] += number; break; } case TV_NATURE_BOOK: { /* Count good books */ if (borg_has_realm(REALM_NATURE)) amt_book[REALM_NATURE][k_ptr->sval] += number; break; } case TV_CHAOS_BOOK: { /* Count good books */ if (borg_has_realm(REALM_CHAOS)) amt_book[REALM_CHAOS][k_ptr->sval] += number; break; } case TV_DEATH_BOOK: { /* Count good books */ if (borg_has_realm(REALM_DEATH)) amt_book[REALM_DEATH][k_ptr->sval] += number; break; } case TV_TRUMP_BOOK: { /* Count good books */ if (borg_has_realm(REALM_TRUMP)) amt_book[REALM_TRUMP][k_ptr->sval] += number; break; } case TV_ARCANE_BOOK: { /* Count good books */ if (borg_has_realm(REALM_ARCANE)) amt_book[REALM_ARCANE][k_ptr->sval] += number; break; } case TV_FOOD: { /* Food */ borg_notice_food(l_ptr, number); break; } case TV_POTION: { /* Potions */ borg_notice_potions(l_ptr, number); break; } case TV_SCROLL: { /* Scrolls */ borg_notice_scrolls(l_ptr, number); break; } case TV_ROD: { /* Rods */ borg_notice_rods(l_ptr, number); break; } case TV_WAND: { /* Wands */ borg_notice_wands(l_ptr, number); break; } case TV_STAFF: { /* Staffs */ borg_notice_staves(l_ptr, number); break; } case TV_FLASK: { bp_ptr->able.fuel += number; amt_flask += number; break; } case TV_LITE: { /* If not empty */ if (l_ptr->timeout) { /* Count whatever it is as 1 fuel */ bp_ptr->able.fuel += number; /* Count a lantern as lantern fuel */ if (k_ptr->sval == SV_LITE_LANTERN) amt_lantern += number; /* Count a torch as torch fuel */ if (k_ptr->sval == SV_LITE_TORCH) amt_torch += number; } break; } case TV_BOW: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: { /* Can the borg make a better artifact of this item? */ bp_ptr->able.artify_item = MAX(bp_ptr->able.artify_item, borg_notice_artify_item(l_ptr)); break; } case TV_DIGGING: { /* Shovels and such */ /* Hack -- ignore worthless ones (including cursed) */ if (KN_FLAG(l_ptr, TR_CURSED)) break; /* Do not carry if weak, won't be able to dig anyway */ if (bp_ptr->skill_dig < 30) break; amt_digger += number; break; } case TV_SHOT: case TV_ARROW: case TV_BOLT: { /* Missiles */ /* Hack -- ignore invalid missiles */ if (l_ptr->tval != my_ammo_tval) break; /* Ignore bad missiles */ if (l_ptr->to_h < -5) break; /* Count them */ bp_ptr->able.missile += number; break; } } } /* * Notice the inventory */ static void borg_notice_inven(void) { list_item *l_ptr; int i; /* Scan the inventory */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Pretend item isn't there */ if (l_ptr->treat_as == TREAT_AS_GONE) continue; if (l_ptr->treat_as == TREAT_AS_SWAP) continue; if ((l_ptr->treat_as == TREAT_AS_LESS) && (l_ptr->number == 1)) { continue; } /* Unaware item? */ if (!l_ptr->k_idx) continue; /* Examine the item */ borg_notice_inven_item(l_ptr); } /* Search equipment for swapped items */ for (i = 0; i < equip_num; i++) { /* Don't use look_up_equip_slot here! */ l_ptr = &equipment[i]; /* A known item? */ if (l_ptr->k_idx) { if ((l_ptr->treat_as == TREAT_AS_SWAP) || (l_ptr->treat_as == TREAT_AS_GONE)) { /* Examine the item */ borg_notice_inven_item(l_ptr); /* Done (Only one extra item) */ return; } } } /* Scan home for swapped items */ for (i = 0; i < home_num; i++) { l_ptr = &borg_home[i]; /* Hack - only 'LESS' items are treated as going into inven */ if (l_ptr->treat_as == TREAT_AS_LESS) { /* Hack - fix the treat_as value */ l_ptr->treat_as = TREAT_AS_SHOP; /* Examine the item */ borg_notice_inven_item(l_ptr); /* Restore treat_as value */ l_ptr->treat_as = TREAT_AS_LESS; /* Done (Only one extra item) */ return; } /* Sometimes want to grab a whole pile */ if (l_ptr->treat_as == TREAT_AS_SWAP) { /* Examine the item */ borg_notice_inven_item(l_ptr); /* Done (Only one extra item) */ return; } } /* Scan current shop? */ if (use_shop) { for (i = 0; i < cur_num; i++) { l_ptr = &cur_list[i]; /* A known item? */ if (l_ptr->k_idx) { /* Hack - only 'LESS' items are treated as going into inven */ if (l_ptr->treat_as == TREAT_AS_LESS) { /* Hack - fix the treat_as value */ l_ptr->treat_as = TREAT_AS_SHOP; /* Examine the item */ borg_notice_inven_item(l_ptr); /* Restore treat_as value */ l_ptr->treat_as = TREAT_AS_LESS; /* Done (Only one extra item) */ return; } } } } } /* * Helper function -- notice the player inventory */ static void borg_notice_aux2(void) { int carry_capacity; /*** Process the inventory ***/ borg_notice_inven(); /* *Process the Spells and Prayers and Artifact Activations. * * Some artifacts are treated differently because: * 1. Some spells-powers are needed instantly and are considered in * the borg preparation code. An artifact maybe non-charged at the * moment he needes it. Then he would need the spell and not be able * to cast it. (ie. teleport, phase) * 2. An artifact may grant a power then he assumes he has infinite * amounts. He then sells off his scrolls with the duplicate power. * When it comes time to upgrade and swap out the artifact, he wont * because his power drops since he does not have the scrolls anymore. * and he does not buy items first. * */ /* Not too many because of the possible swap */ if (borg_activate_fail(BORG_ACT_SATISFY)) { amt_food_scroll += 5; bp_ptr->food += 5; } /* Handle "satisfy hunger" -> infinite food */ if (borg_spell_legal_fail(REALM_LIFE, 0, 7, 40) || borg_spell_legal_fail(REALM_ARCANE, 2, 6, 40) || borg_spell_legal_fail(REALM_NATURE, 0, 3, 40) || borg_racial_check(RACE_HOBBIT, TRUE)) { amt_food_scroll += 1000; bp_ptr->food += 1000; } /* Not too many because of the possible swap */ if (borg_activate_fail(BORG_ACT_SATISFY)) bp_ptr->able.id += 5; /* Handle "identify" -> infinite identifies */ if (borg_activate_fail(BORG_ACT_IDENTIFY) || borg_spell_legal_fail(REALM_SORCERY, 1, 1, 60) || borg_spell_legal_fail(REALM_ARCANE, 3, 2, 60) || borg_mindcr_legal_fail(MIND_PSYCHOMETRY, 25, 60)) { bp_ptr->able.id += 1000; } /* Handle "*identify*" -> infinite *identifies* */ if (borg_activate_fail(BORG_ACT_STAR_IDENTIFY) || borg_spell_legal_fail(REALM_SORCERY, 1, 7, 60) || borg_spell_legal_fail(REALM_NATURE, 2, 5, 60) || borg_spell_legal_fail(REALM_DEATH, 3, 2, 60) || borg_spell_legal_fail(REALM_TRUMP, 3, 1, 60) || borg_spell_legal_fail(REALM_LIFE, 3, 5, 60)) { bp_ptr->able.id += 1000; bp_ptr->able.star_id += 1000; } /* Handle "remove_curse" -> infinite remove curses */ if (borg_activate_fail(BORG_ACT_REMOVE_CURSE) || borg_spell_legal_fail(REALM_LIFE, 1, 0, 60)) { bp_ptr->able.remove_curse += 1000; } /* Handle "*remove_curse*" -> infinite *remove curses* */ if (borg_activate_fail(BORG_ACT_STAR_REMOVE_CURSE) || borg_spell_legal_fail(REALM_LIFE, 2, 1, 60)) { bp_ptr->able.remove_curse += 1000; bp_ptr->able.star_remove_curse += 1000; } /* Handle "detect traps, doors, stairs" */ if (borg_activate_fail(BORG_ACT_DETECT_TRAP_DOOR) || borg_spell_legal_fail(REALM_LIFE, 0, 5, 60) || borg_spell_legal_fail(REALM_SORCERY, 0, 2, 60) || borg_spell_legal_fail(REALM_ARCANE, 1, 0, 60) || borg_spell_legal_fail(REALM_NATURE, 1, 2, 60) || borg_spell_legal_fail(REALM_NATURE, 0, 2, 60) || borg_mindcr_legal_fail(MIND_PRECOGNIT, 5, 60) || borg_racial_check(RACE_DWARF, TRUE) || borg_racial_check(RACE_NIBELUNG, TRUE)) { bp_ptr->able.det_trap += 1000; bp_ptr->able.det_door += 1000; } /* Handle "detect evil & monsters" */ if (borg_activate_fail(BORG_ACT_DETECT_MONSTERS) || borg_activate_fail(BORG_ACT_DETECT_EVIL) || borg_racial_check(RACE_GHOUL_POWER2, TRUE) || borg_spell_legal_fail(REALM_LIFE, 0, 0, 60) || borg_spell_legal_fail(REALM_SORCERY, 0, 0, 60) || borg_spell_legal_fail(REALM_NATURE, 0, 0, 60) || borg_spell_legal_fail(REALM_DEATH, 0, 0, 60) || borg_spell_legal_fail(REALM_DEATH, 0, 2, 60) || borg_mindcr_legal_fail(MIND_PRECOGNIT, 1, 60)) { bp_ptr->able.det_evil += 1000; } /* Handle "detection" */ if (borg_activate_fail(BORG_ACT_DETECTION) || borg_mindcr_legal_fail(MIND_PRECOGNIT, 30, 60)) { bp_ptr->able.det_door += 1000; bp_ptr->able.det_trap += 1000; bp_ptr->able.det_evil += 1000; } /* Handle "magic mapping" */ if (borg_activate_fail(BORG_ACT_MAGIC_MAPPING) || borg_spell_legal_fail(REALM_SORCERY, 1, 0, 60) || borg_spell_legal_fail(REALM_NATURE, 1, 2, 60) || borg_mindcr_legal_fail(MIND_PRECOGNIT, 20, 60)) { bp_ptr->able.magic_map += 1000; } /* Handle "light" */ if (borg_activate_fail(BORG_ACT_LIGHT) || borg_spell_legal_fail(REALM_LIFE, 0, 4, 60) || borg_spell_legal_fail(REALM_SORCERY, 0, 3, 60) || borg_spell_legal_fail(REALM_NATURE, 0, 4, 60) || borg_spell_legal_fail(REALM_CHAOS, 0, 2, 60) || borg_spell_legal_fail(REALM_ARCANE, 0, 5, 60) || borg_mutation_check(MUT1_ILLUMINE, TRUE)) { bp_ptr->able.lite += 1000; } /* Handle "phlogiston" */ if (borg_spell_legal_fail(REALM_ARCANE, 1, 1, 40)) { /* Not too much or you'll be casting phlogiston in the dark */ bp_ptr->able.fuel += 5; amt_lantern += 5; amt_torch += 5; } /* Handle "rune of protection" glyph" */ if (borg_spell_legal_fail(REALM_LIFE, 1, 7, 20) || borg_spell_legal_fail(REALM_LIFE, 2, 7, 20)) { bp_ptr->able.glyph += 1000; } /* Handle "enchant weapon" */ if (borg_spell_legal_fail(REALM_SORCERY, 3, 4, 40)) { amt_enchant_to_h += 1000; amt_enchant_to_d += 1000; } /* Handle "enchant armor" */ if (borg_spell_legal_fail(REALM_SORCERY, 3, 5, 40)) { amt_enchant_to_a += 1000; } /* Handle Diggers */ if (borg_activate_fail(BORG_ACT_STONE_TO_MUD) || borg_spell_legal_fail(REALM_NATURE, 1, 0, 40) || borg_spell_legal_fail(REALM_CHAOS, 0, 6, 40) || borg_mutation_check(MUT1_EAT_ROCK, TRUE) || borg_racial_check(RACE_HALF_GIANT, TRUE)) { amt_digger += 1; } /* Not too many, this artifact may dissappear */ if (borg_activate_fail(BORG_ACT_WORD_OF_RECALL)) bp_ptr->recall += 3; /* Handle recall */ if (borg_spell_legal_fail(REALM_ARCANE, 3, 6, 40) || borg_spell_legal_fail(REALM_SORCERY, 2, 7, 40) || borg_spell_legal_fail(REALM_TRUMP, 1, 6, 40) || borg_mutation_check(MUT1_RECALL, TRUE)) { bp_ptr->recall += 1000; } /* Handle teleport_level */ if (borg_activate_fail(BORG_ACT_TELEPORT_LEVEL) || borg_spell_legal_fail(REALM_SORCERY, 2, 6, 40) || borg_spell_legal_fail(REALM_ARCANE, 3, 1, 40) || borg_spell_legal_fail(REALM_TRUMP, 1, 5, 40)) { bp_ptr->able.teleport_level += 1000; } /* Not too many because of the possible swap */ if (borg_activate_fail(BORG_ACT_PHASE_DOOR)) bp_ptr->able.phase += 5; /* Handle phase door */ if (borg_spell_legal_fail(REALM_SORCERY, 0, 1, 40) || borg_spell_legal_fail(REALM_ARCANE, 0, 4, 40) || borg_spell_legal_fail(REALM_TRUMP, 0, 0, 40) || borg_mindcr_legal_fail(MIND_MINOR_DISP, 3, 40) || borg_mutation_check(MUT1_BLINK, TRUE)) { bp_ptr->able.phase += 1000; } /* Not too many because of the possible swap */ if (borg_activate_fail(BORG_ACT_PHASE_DOOR)) bp_ptr->able.teleport += 5; /* Handle teleport spell carefully */ if (((borg_spell_okay_fail(REALM_ARCANE, 2, 3, 5) || borg_spell_okay_fail(REALM_LIFE, 4, 1, 5) || borg_spell_okay_fail(REALM_TRUMP, 0, 4, 5) || borg_spell_okay_fail(REALM_CHAOS, 0, 7, 5) || borg_mindcr_okay_fail(MIND_MAJOR_DISP, 7, 5) || borg_mutation_check(MUT1_VTELEPORT, TRUE)) && FLAG(bp_ptr, TR_RES_BLIND) && FLAG(bp_ptr, TR_RES_CONF)) ) { bp_ptr->able.teleport += 1000; } /* Not too many because of the possible swap */ if (borg_activate_fail(BORG_ACT_PHASE_DOOR)) bp_ptr->able.speed += 5; /* speed spells */ if (borg_spell_legal_fail(REALM_SORCERY, 1, 5, 40) || borg_spell_legal_fail(REALM_DEATH, 2, 3, 40) || borg_mindcr_legal_fail(MIND_ADRENALINE, 35, 40)) { bp_ptr->able.speed += 1000; } /* berserk spells */ if (borg_activate_fail(BORG_ACT_BERSERKER) || borg_activate_fail(BORG_ACT_HEROISM) || borg_spell_legal_fail(REALM_DEATH, 2, 0, 40) || borg_mindcr_legal_fail(MIND_ADRENALINE, 35, 40) || borg_mutation_check(MUT1_BERSERK, TRUE)) { bp_ptr->able.berserk += 1000; } /* Handle "heal" */ if (borg_spell_legal_fail(REALM_LIFE, 1, 6, 5) || borg_spell_legal_fail(REALM_NATURE, 1, 7, 5)) { bp_ptr->able.heal += 1000; } /* Not too many because of the possible swap */ if (borg_activate_fail(BORG_ACT_HEAL_BIG)) bp_ptr->able.easy_heal += 5; /* Handle big healing spell */ if (borg_spell_legal_fail(REALM_LIFE, 3, 4, 2)) { bp_ptr->able.easy_heal += 1000; } /* Handle "fix exp" */ if (borg_activate_fail(BORG_ACT_RESTORE_LIFE) || borg_spell_legal_fail(REALM_LIFE, 3, 3, 60) || borg_spell_legal_fail(REALM_DEATH, 1, 7, 60) || borg_racial_check(RACE_AMBERITE_POWER2, FALSE) || borg_racial_check(RACE_SKELETON, FALSE) || borg_racial_check(RACE_ZOMBIE, FALSE)) { amt_fix_exp += 1000; } /* Handle "recharge" */ if (borg_activate_fail(BORG_ACT_RECHARGE) || borg_spell_legal_fail(REALM_ARCANE, 3, 0, 60) || borg_spell_legal_fail(REALM_SORCERY, 0, 7, 60)) { bp_ptr->able.recharge += 1000; } /* Handle resistance */ if (borg_activate_fail(BORG_ACT_RESISTANCE) || borg_spell_legal_fail(REALM_NATURE, 2, 3, 40) || borg_mindcr_legal_fail(MIND_CHAR_ARMOUR, 33, 40)) { bp_ptr->able.res_all += 1000; } /*** Process the Needs ***/ /* No need to *buy* stat increase potions */ if (my_stat_cur[A_STR] >= 180 + 100 + 10 * (rp_ptr->r_adj[A_STR] + cp_ptr->c_adj[A_STR])) amt_add_stat[A_STR] += 1000; if (my_stat_cur[A_INT] >= 180 + 100 + 10 * (rp_ptr->r_adj[A_INT] + cp_ptr->c_adj[A_INT])) amt_add_stat[A_INT] += 1000; if (my_stat_cur[A_WIS] >= 180 + 100 + 10 * (rp_ptr->r_adj[A_WIS] + cp_ptr->c_adj[A_WIS])) amt_add_stat[A_WIS] += 1000; if (my_stat_cur[A_DEX] >= 180 + 100 + 10 * (rp_ptr->r_adj[A_DEX] + cp_ptr->c_adj[A_DEX])) amt_add_stat[A_DEX] += 1000; if (my_stat_cur[A_CON] >= 180 + 100 + 10 * (rp_ptr->r_adj[A_CON] + cp_ptr->c_adj[A_CON])) amt_add_stat[A_CON] += 1000; if (my_stat_cur[A_CHR] >= 180 + 100 + 10 * (rp_ptr->r_adj[A_CHR] + cp_ptr->c_adj[A_CHR])) amt_add_stat[A_CHR] += 1000; /* Add star_healing and life potions into easy_heal */ bp_ptr->able.easy_heal = amt_star_heal + amt_life; /* * Correct bp_ptr->encumber from total weight to the degree * of being overweight. */ /* Extract the "weight limit" (in tenth pounds) */ carry_capacity = (adj_str_wgt[my_stat_ind[A_STR]] * 100) / 2; /* 0 if not encumbered, otherwise the encumberment */ bp_ptr->encumber = MAX(0, bp_ptr->weight - carry_capacity); } /* * Update the Borg based on the current "frame" * * Assumes the Borg is actually in the dungeon. * * Note: everything starts off as FALSE by default * since we are wiped in borg_notice(). */ void borg_update_frame(void) { int i; dun_type *d_ptr = dungeon(); s32b len = 10L * TOWN_DAWN; s32b tick = turn % len + len / 4; bp_ptr->hour = (24 * tick / len) % 24; /* Note "Lev" vs "LEV" */ if (p_ptr->lev < p_ptr->max_lev) bp_ptr->status.fixlvl = TRUE; /* Extract "LEVEL xxxxxx" */ bp_ptr->lev = p_ptr->lev; /* cheat the max clevel */ bp_ptr->max_lev = p_ptr->max_lev; /* Note "Winner" */ bp_ptr->winner = (char) p_ptr->state.total_winner; /* Assume experience is fine */ bp_ptr->status.fixexp = FALSE; /* Note "Exp" vs "EXP" and am I lower than level 50 */ if ((p_ptr->exp < p_ptr->max_exp) && (bp_ptr->lev != 50)) bp_ptr->status.fixexp = TRUE; /* Extract "AU xxxxxxxxx" */ borg_gold = p_ptr->au; /* Extract "Fast (+x)" or "Slow (-x)" */ bp_ptr->speed = p_ptr->pspeed; /* Check my float for decrementing variables */ if (bp_ptr->speed > 110) { borg_game_ratio = 100000 / (((bp_ptr->speed - 110) * 10) + 100); } else { borg_game_ratio = 1000; } /* * If hasting, it doesn't count as 'borg_speed'. * The speed gained from hasting is counted seperately. */ if (borg_speed) bp_ptr->speed -= 10; /* Extract "Cur AC xxxxx" */ bp_ptr->ac = p_ptr->dis_ac + p_ptr->dis_to_a; /* Extract "Cur HP xxxxx" */ bp_ptr->chp = p_ptr->chp; /* Extract "Max HP xxxxx" */ bp_ptr->mhp = p_ptr->mhp; /* Extract "Cur SP xxxxx" (or zero) */ bp_ptr->csp = p_ptr->csp; /* Extract "Max SP xxxxx" (or zero) */ bp_ptr->msp = p_ptr->msp; /* Check for "Weak" */ if (p_ptr->food < PY_FOOD_WEAK) { bp_ptr->status.weak = TRUE; bp_ptr->status.hungry = TRUE; } /* Check for "Hungry" */ else if (p_ptr->food < PY_FOOD_ALERT) bp_ptr->status.hungry = TRUE; /* Check for "Normal" */ else if (p_ptr->food < PY_FOOD_FULL) /* Nothing */ ; /* Check for "Full" */ else if (p_ptr->food < PY_FOOD_MAX) bp_ptr->status.full = TRUE; /* Check for "Gorged" */ else { bp_ptr->status.gorged = TRUE; bp_ptr->status.full = TRUE; } /* Check for "Blind" */ if (p_ptr->tim.blind) bp_ptr->status.blind = TRUE; /* Check for "Confused" */ if (p_ptr->tim.confused) bp_ptr->status.confused = TRUE; /* Check for "Afraid" */ if (p_ptr->tim.afraid) bp_ptr->status.afraid = TRUE; /* Check for "Poisoned" */ if (p_ptr->tim.poisoned) bp_ptr->status.poisoned = TRUE; /* Check for any text */ if (p_ptr->tim.cut) bp_ptr->status.cut = TRUE; /* Check for Stun */ if (p_ptr->tim.stun && (p_ptr->tim.stun <= 50)) bp_ptr->status.stun = TRUE; /* Check for Heavy Stun */ if (p_ptr->tim.stun > 50) bp_ptr->status.heavy_stun = TRUE; /* XXX XXX XXX Parse "State" */ if (p_ptr->state.searching) bp_ptr->status.search = TRUE; /* Check for "Study" */ if (p_ptr->new_spells) bp_ptr->status.study = TRUE; /* Check for hallucination */ if (p_ptr->tim.image) bp_ptr->status.image = TRUE; /* Parse stats */ for (i = 0; i < A_MAX; i++) { bp_ptr->status.fixstat[i] = (p_ptr->stat[i].cur < p_ptr->stat[i].max); borg_stat[i] = p_ptr->stat[i].cur; } /* Hack -- Access depth */ bp_ptr->depth = p_ptr->depth; /* If this is the first borg run then avoid reinitialization */ if (old_depth == 128) old_depth = bp_ptr->depth; /* How deep can the borg expect to go down here? */ bp_ptr->max_depth = (d_ptr) ? d_ptr->recall_depth : 0; /* Hack -- Realms */ bp_ptr->realm1 = p_ptr->spell.r[0].realm; bp_ptr->realm2 = p_ptr->spell.r[1].realm; /* Hack -- Mana increases */ switch (borg_class) { case CLASS_PRIEST: case CLASS_PALADIN: case CLASS_MONK: case CLASS_MINDCRAFTER: { bp_ptr->wismana = 1; break; } case CLASS_MAGE: case CLASS_ROGUE: case CLASS_RANGER: case CLASS_WARRIOR_MAGE: case CLASS_CHAOS_WARRIOR: case CLASS_HIGH_MAGE: { bp_ptr->intmana = 1; break; } default: { bp_ptr->wismana = 0; bp_ptr->intmana = 0; } } } /* * This procedure sets to zero the various variables that are used for * determining a borg value. This has to be done asap so that these can be * used both in the equipment and the inventory appraisal. */ static void borg_clear_vars(void) { int i, ii; /* clear the struct */ (void) WIPE(bp_ptr, borg_player); /* Reset basic */ amt_food_scroll = 0; amt_food_lowcal = 0; amt_torch = 0; amt_lantern = 0; amt_flask = 0; /* Reset healing */ amt_slow_poison = 0; amt_pot_curing = 0; amt_star_heal = 0; amt_life = 0; amt_rod_heal = 0; /* Reset books */ for (i = 0; i < MAX_REALM; i++) { for (ii = 0; ii < 4; ii++) { amt_book[i][ii] = 0; } } /* Reset various */ amt_add_stat[A_STR] = 0; amt_add_stat[A_INT] = 0; amt_add_stat[A_WIS] = 0; amt_add_stat[A_DEX] = 0; amt_add_stat[A_CON] = 0; amt_add_stat[A_CHR] = 0; amt_fix_stat[A_STR] = 0; amt_fix_stat[A_INT] = 0; amt_fix_stat[A_WIS] = 0; amt_fix_stat[A_DEX] = 0; amt_fix_stat[A_CON] = 0; amt_fix_stat[A_CHR] = 0; amt_fix_stat[6] = 0; amt_fix_exp = 0; amt_digger = 0; /* Reset enchantment */ amt_enchant_to_a = 0; amt_enchant_to_d = 0; amt_enchant_to_h = 0; amt_brand_weapon = 0; } /* * Analyze the equipment and inventory */ void borg_notice(void) { /* Clear out the player information */ borg_clear_vars(); /* * Many of our variables are tied to borg_player. * So we must update the frame the cheat in all * the non-inventory details now. */ borg_update_frame(); /* Notice the equipment */ borg_notice_aux1(); /* Notice the inventory */ borg_notice_aux2(); } /* * Helper function -- clear home values */ static void borg_notice_home_clear(void) { int i, ii; /*** Reset counters ***/ /* Reset basic */ num_food = 0; num_food_scroll = 0; num_ident = 0; num_star_ident = 0; num_remove_curse = 0; num_star_remove_curse = 0; num_recall = 0; num_phase = 0; num_escape = 0; num_teleport = 0; num_teleport_level = 0; num_invisible = 0; num_glyph = 0; num_genocide = 0; num_mass_genocide = 0; num_berserk = 0; num_pot_resist = 0; num_goi_pot = 0; num_slow_digest = 0; num_regenerate = 0; num_telepathy = 0; num_see_inv = 0; num_ffall = 0; num_free_act = 0; num_hold_life = 0; num_immune_acid = 0; num_immune_elec = 0; num_immune_fire = 0; num_immune_cold = 0; num_resist_acid = 0; num_resist_elec = 0; num_resist_fire = 0; num_resist_cold = 0; num_resist_pois = 0; num_resist_conf = 0; num_resist_sound = 0; num_resist_lite = 0; num_resist_dark = 0; num_resist_chaos = 0; num_resist_disen = 0; num_resist_shard = 0; num_resist_nexus = 0; num_resist_blind = 0; num_resist_neth = 0; num_sustain_str = 0; num_sustain_int = 0; num_sustain_wis = 0; num_sustain_dex = 0; num_sustain_con = 0; num_sustain_all = 0; home_stat_add[A_STR] = 0; home_stat_add[A_INT] = 0; home_stat_add[A_WIS] = 0; home_stat_add[A_DEX] = 0; home_stat_add[A_CON] = 0; home_stat_add[A_CHR] = 0; num_artifact = 0; num_bad_curse = 0; num_weapons = 0; num_bow = 0; num_rings = 0; num_neck = 0; num_armor = 0; num_cloaks = 0; num_shields = 0; num_hats = 0; num_gloves = 0; num_boots = 0; num_lite = 0; num_speed = 0; num_edged_weapon = 0; num_bad_gloves = 0; /* Reset healing */ num_cure_critical = 0; num_cure_serious = 0; num_cure_light = 0; num_fix_exp = 0; num_mana = 0; num_heal = 0; num_ez_heal = 0; /* Reset missiles */ num_missile = 0; /* Reset books */ for (i = 0; i < MAX_REALM; i++) { for (ii = 0; ii < 4; ii++) { num_book[i][ii] = 0; } } /* Reset various */ num_fix_stat[A_STR] = 0; num_fix_stat[A_INT] = 0; num_fix_stat[A_WIS] = 0; num_fix_stat[A_DEX] = 0; num_fix_stat[A_CON] = 0; num_fix_stat[A_CHR] = 0; num_fix_stat[6] = 0; home_slot_free = 0; home_damage = 0; num_duplicate_items = 0; } /* * Notice flags on item */ static void borg_notice_home_flags(list_item *l_ptr) { if (KN_FLAG(l_ptr, TR_SLOW_DIGEST)) num_slow_digest += l_ptr->number; if (KN_FLAG(l_ptr, TR_REGEN)) num_regenerate += l_ptr->number; if (KN_FLAG(l_ptr, TR_TELEPATHY)) num_telepathy += l_ptr->number; if (KN_FLAG(l_ptr, TR_SEE_INVIS)) num_see_inv += l_ptr->number; if (KN_FLAG(l_ptr, TR_FEATHER)) num_ffall += l_ptr->number; if (KN_FLAG(l_ptr, TR_FREE_ACT)) num_free_act += l_ptr->number; if (KN_FLAG(l_ptr, TR_HOLD_LIFE)) num_hold_life += l_ptr->number; if (KN_FLAG(l_ptr, TR_IM_FIRE)) { num_immune_fire += l_ptr->number; num_resist_fire += l_ptr->number; } if (KN_FLAG(l_ptr, TR_IM_ACID)) { num_immune_acid += l_ptr->number; num_resist_acid += l_ptr->number; } if (KN_FLAG(l_ptr, TR_IM_COLD)) { num_immune_cold += l_ptr->number; num_resist_cold += l_ptr->number; } if (KN_FLAG(l_ptr, TR_IM_ELEC)) { num_immune_elec += l_ptr->number; num_resist_elec += l_ptr->number; } if (KN_FLAG(l_ptr, TR_RES_ACID)) num_resist_acid += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_ELEC)) num_resist_elec += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_FIRE)) num_resist_fire += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_COLD)) num_resist_cold += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_POIS)) num_resist_pois += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_SOUND)) num_resist_sound += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_LITE)) num_resist_lite += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_DARK)) num_resist_dark += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_CHAOS)) num_resist_chaos += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_CONF)) num_resist_conf += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_DISEN)) num_resist_disen += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_SHARDS)) num_resist_shard += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_NEXUS)) num_resist_nexus += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_BLIND)) num_resist_blind += l_ptr->number; if (KN_FLAG(l_ptr, TR_RES_NETHER)) num_resist_neth += l_ptr->number; /* Count Sustains */ if (KN_FLAG(l_ptr, TR_SUST_STR)) num_sustain_str += l_ptr->number; if (KN_FLAG(l_ptr, TR_SUST_INT)) num_sustain_int += l_ptr->number; if (KN_FLAG(l_ptr, TR_SUST_WIS)) num_sustain_wis += l_ptr->number; if (KN_FLAG(l_ptr, TR_SUST_DEX)) num_sustain_dex += l_ptr->number; if (KN_FLAG(l_ptr, TR_SUST_CON)) num_sustain_con += l_ptr->number; if (KN_FLAG(l_ptr, TR_SUST_STR) && KN_FLAG(l_ptr, TR_SUST_INT) && KN_FLAG(l_ptr, TR_SUST_WIS) && KN_FLAG(l_ptr, TR_SUST_DEX) && KN_FLAG(l_ptr, TR_SUST_CON)) num_sustain_all += l_ptr->number; /* count up bonus to stats */ if (KN_FLAG(l_ptr, TR_STR)) { if (l_ptr->tval != TV_RING) home_stat_add[A_STR] += l_ptr->pval * l_ptr->number; } if (KN_FLAG(l_ptr, TR_INT)) { if (l_ptr->tval != TV_RING) home_stat_add[A_INT] += l_ptr->pval * l_ptr->number; } if (KN_FLAG(l_ptr, TR_WIS)) { if (l_ptr->tval != TV_RING) home_stat_add[A_WIS] += l_ptr->pval * l_ptr->number; } if (KN_FLAG(l_ptr, TR_DEX)) { if (l_ptr->tval != TV_RING) home_stat_add[A_DEX] += l_ptr->pval * l_ptr->number; } if (KN_FLAG(l_ptr, TR_CON)) { if (l_ptr->tval != TV_RING) home_stat_add[A_CON] += l_ptr->pval * l_ptr->number; } if (KN_FLAG(l_ptr, TR_CHR)) { if (l_ptr->tval != TV_RING) home_stat_add[A_CHR] += l_ptr->pval * l_ptr->number; } /* count up bonus to speed */ if (KN_FLAG(l_ptr, TR_SPEED)) num_speed += l_ptr->pval * l_ptr->number; } /* * This checks for duplicate items in the home */ static void borg_notice_home_dupe(list_item *l_ptr, bool check_sval, int i) { int dupe_count, x; list_item *w_ptr; dupe_count = l_ptr->number - 1; /* Avoid trouble */ if (dupe_count <= 0) return; /* Look for other items before this one that are the same */ for (x = 0; x < i; x++) { if (x < home_num) w_ptr = &borg_home[x]; else /* Check what the borg has on as well. */ w_ptr = look_up_equip_slot(x - home_num); if (!w_ptr) continue; /* Don't count items we are swapping */ if (w_ptr->treat_as == TREAT_AS_SWAP) continue; /* * If everything matches it is a duplicate item * Note that we only check sval on certain items. This * is because, for example, two pairs of dragon armor * are not the same unless their subtype (color) matches * but a defender is a defender even if one is a dagger and * one is a mace */ if (l_ptr->tval == w_ptr->tval) { if (check_sval && (k_info[l_ptr->k_idx].sval != k_info[w_ptr->k_idx].sval)) { /* Svals don't match when required */ continue; } /* Does only one have an xtra name? */ if ((l_ptr->xtra_name == NULL) != (w_ptr->xtra_name == NULL)) { continue; } /* Do the xtra names match? */ if ((l_ptr->xtra_name == w_ptr->xtra_name) || (streq(l_ptr->xtra_name, w_ptr->xtra_name))) { /* Count duplicate items */ dupe_count++; } } } /* There can be one dupe of rings because there are two ring slots. */ if (l_ptr->tval == TV_RING) dupe_count--; /* Add this items count to the total duplicate count */ num_duplicate_items += dupe_count; } /* * Examine weapons in the home */ static void borg_notice_home_weapon(list_item *l_ptr) { s16b num_blow; int str_index, dex_index; int num = 0, wgt = 0, mul = 0, div = 0; num_weapons += l_ptr->number; /* apw most edged weapons hurt magic for priests */ if (borg_class == CLASS_PRIEST) { /* Penalize non-blessed edged weapons */ if (((l_ptr->tval == TV_SWORD) || (l_ptr->tval == TV_POLEARM)) && !KN_FLAG(l_ptr, TR_BLESSED)) { num_edged_weapon += l_ptr->number; } } /* * NOTE: This damage does not take slays into account. * It is just a rough estimate to make sure the glave of pain * is kept if it is found. * It is hard to hold a heavy weapon. */ num_blow = 1; if (adj_str_hold[my_stat_ind[A_STR]] >= l_ptr->weight / 10) { /* Analyze the class */ switch (borg_class) { case CLASS_WARRIOR: { /* Warrior */ num = 5; wgt = 30; mul = 5; break; } case CLASS_MAGE: case CLASS_HIGH_MAGE: { /* Mage */ num = 2; wgt = 40; mul = 2; break; } case CLASS_PRIEST: case CLASS_MINDCRAFTER: { /* Priest, Mindcrafter */ num = 4; wgt = 35; mul = 3; break; } case CLASS_ROGUE: { /* Rogue */ num = 4; wgt = 30; mul = 3; break; } case CLASS_RANGER: { /* Ranger */ num = 4; wgt = 35; mul = 4; break; } case CLASS_PALADIN: { /* Paladin */ num = 4; wgt = 30; mul = 4; break; } case CLASS_WARRIOR_MAGE: { /* Warrior-Mage */ num = 4; wgt = 35; mul = 3; break; } case CLASS_CHAOS_WARRIOR: { /* Chaos Warrior */ num = 4; wgt = 30; mul = 4; break; } case CLASS_MONK: { /* Monk */ num = ((p_ptr->lev < 40) ? 2 : 3); wgt = 40; mul = 4; break; } } /* Enforce a minimum "weight" */ div = ((l_ptr->weight < wgt) ? wgt : l_ptr->weight); /* Access the strength vs weight */ str_index = (adj_str_blow[my_stat_ind[A_STR]] * mul / div); /* Maximal value */ if (str_index > 11) str_index = 11; /* Index by dexterity */ dex_index = (adj_dex_blow[my_stat_ind[A_DEX]]); /* Maximal value */ if (dex_index > 11) dex_index = 11; /* Use the blows table */ num_blow = blows_table[str_index][dex_index]; /* Maximal value */ if (num_blow > num) num_blow = num; } /* Require at least one blow */ if (num_blow < 1) num_blow = 1; if (KN_FLAG(l_ptr, TR_BLOWS)) num_blow += l_ptr->pval; num_blow *= l_ptr->number; /* * It should include bp_ptr->to_d in the home damage calculation too but * that value is not necessarily finished when it is used here. This can * cause home loops so I delete the reference to it */ if (l_ptr->to_d > 8 || bp_ptr->lev < 15) { home_damage += num_blow * (l_ptr->dd * l_ptr->ds + l_ptr->to_d); } else { home_damage += num_blow * (l_ptr->dd * l_ptr->ds + 8); } } /* * Examine potions in the home */ static void borg_notice_home_potion(list_item *l_ptr) { /* Analyze */ switch (k_info[l_ptr->k_idx].sval) { case SV_POTION_CURING: { num_cure_critical += l_ptr->number; break; } case SV_POTION_CURE_CRITICAL: { num_cure_critical += l_ptr->number; break; } case SV_POTION_CURE_SERIOUS: { num_cure_serious += l_ptr->number; break; } case SV_POTION_CURE_LIGHT: { num_cure_light += l_ptr->number; break; } case SV_POTION_RESISTANCE: { num_pot_resist += l_ptr->number; break; } case SV_POTION_RES_STR: { num_fix_stat[A_STR] += l_ptr->number; break; } case SV_POTION_RES_INT: { num_fix_stat[A_INT] += l_ptr->number; break; } case SV_POTION_RES_WIS: { num_fix_stat[A_WIS] += l_ptr->number; break; } case SV_POTION_RES_DEX: { num_fix_stat[A_DEX] += l_ptr->number; break; } case SV_POTION_RES_CON: { num_fix_stat[A_CON] += l_ptr->number; break; } case SV_POTION_RES_CHR: { num_fix_stat[A_CHR] += l_ptr->number; break; } case SV_POTION_RESTORE_EXP: { num_fix_exp += l_ptr->number; break; } case SV_POTION_RESTORE_MANA: { num_mana += l_ptr->number; break; } case SV_POTION_HEALING: { num_heal += l_ptr->number; break; } case SV_POTION_STAR_HEALING: { num_ez_heal += l_ptr->number; break; } case SV_POTION_LIFE: { num_ez_heal += l_ptr->number; break; } case SV_POTION_BERSERK_STRENGTH: { num_berserk += l_ptr->number; break; } case SV_POTION_SPEED: { num_speed += l_ptr->number; break; } case SV_POTION_INVULNERABILITY: { num_goi_pot += l_ptr->number; break; } } } /* * Examine scrolls in the home */ static void borg_notice_home_scroll(list_item *l_ptr) { /* Analyze the scroll */ switch (k_info[l_ptr->k_idx].sval) { case SV_SCROLL_IDENTIFY: { num_ident += l_ptr->number; break; } case SV_SCROLL_STAR_IDENTIFY: { num_star_ident += l_ptr->number; break; } case SV_SCROLL_REMOVE_CURSE: { num_remove_curse += l_ptr->number; break; } case SV_SCROLL_STAR_REMOVE_CURSE: { num_star_remove_curse += l_ptr->number; break; } case SV_SCROLL_PHASE_DOOR: { num_phase += l_ptr->number; break; } case SV_SCROLL_TELEPORT: { num_escape += l_ptr->number; break; } case SV_SCROLL_WORD_OF_RECALL: { num_recall += l_ptr->number; break; } case SV_SCROLL_RUNE_OF_PROTECTION: { num_glyph += l_ptr->number; break; } case SV_SCROLL_TELEPORT_LEVEL: { num_teleport_level += l_ptr->number; break; } case SV_SCROLL_SATISFY_HUNGER: { num_food_scroll += l_ptr->number; num_food += l_ptr->number; break; } } } /* * Include effects of spells on home items */ static void borg_notice_home_spells(void) { /* Handle "satisfy hunger" -> infinite food */ if (borg_spell_legal_fail(REALM_LIFE, 0, 7, 40) || borg_spell_legal_fail(REALM_ARCANE, 2, 6, 40) || borg_spell_legal_fail(REALM_NATURE, 0, 3, 40)) { num_food += 1000; num_food_scroll += 1000; } /* Handle "identify" -> infinite identifies */ if (borg_spell_legal_fail(REALM_SORCERY, 1, 1, 60) || borg_spell_legal_fail(REALM_ARCANE, 3, 2, 60) || borg_mindcr_legal_fail(MIND_PSYCHOMETRY, 25, 60) || borg_equips_rod_fail(SV_ROD_IDENTIFY)) { num_ident += 1000; } /* Handle "*identify*" -> infinite *identifies* */ if (borg_spell_legal_fail(REALM_NATURE, 2, 5, 60) || borg_spell_legal_fail(REALM_SORCERY, 1, 7, 60) || borg_spell_legal_fail(REALM_DEATH, 3, 2, 60) || borg_spell_legal_fail(REALM_TRUMP, 3, 1, 60) || borg_spell_legal_fail(REALM_LIFE, 3, 5, 60)) { num_ident += 1000; num_star_ident += 1000; } /* Handle "remove curse" -> infinite remove curses */ if (borg_spell_legal_fail(REALM_LIFE, 1, 0, 40) || borg_spell_legal_fail(REALM_LIFE, 2, 1, 40)) { num_remove_curse += 1000; } /* Handle "*remove curse*" -> infinite *remove curses* */ if (borg_spell_legal_fail(REALM_LIFE, 2, 1, 40)) { num_star_remove_curse += 1000; } /* apw Handle "rune of protection" glyph */ if (borg_spell_legal_fail(REALM_LIFE, 1, 7, 40) || borg_spell_legal_fail(REALM_LIFE, 2, 7, 40)) { num_glyph += 1000; } /* Handle recall */ if (borg_spell_legal_fail(REALM_ARCANE, 3, 6, 40) || borg_spell_legal_fail(REALM_SORCERY, 2, 7, 40) || borg_spell_legal_fail(REALM_TRUMP, 1, 6, 40) || borg_mutation_check(MUT1_RECALL, TRUE) || borg_equips_rod_fail(SV_ROD_RECALL)) { num_recall += 1000; } /* Handle teleport_level */ if (borg_spell_legal_fail(REALM_SORCERY, 2, 6, 40) || borg_spell_legal_fail(REALM_TRUMP, 1, 5, 40)) { num_teleport_level += 1000; } /* Handle resistance */ if (borg_mindcr_legal_fail(MIND_CHAR_ARMOUR, 33, 60)) { num_pot_resist += 1000; } /* Handle speed */ if (borg_mindcr_legal_fail(MIND_ADRENALINE, 25, 40) || borg_spell_legal_fail(REALM_SORCERY, 1, 5, 40)) { num_speed += 1000; } } /* * Innate abilities of the player can affect home item choice */ static void borg_notice_home_player(void) { object_flags oflags; object_flags *of_ptr = &oflags; int i; /* Hack -- No need for stat repair */ for (i = 0; i < A_MAX; i++) { if (bp_ptr->sust[i]) num_fix_stat[i] += 1000; } /* Extract the player flags */ player_flags(of_ptr); /* Good flags */ if (FLAG(of_ptr, TR_SLOW_DIGEST)) num_slow_digest = TRUE; if (FLAG(of_ptr, TR_FEATHER)) num_ffall = TRUE; if (FLAG(of_ptr, TR_LITE)) num_lite = TRUE; if (FLAG(of_ptr, TR_REGEN)) num_regenerate = TRUE; if (FLAG(of_ptr, TR_TELEPATHY)) num_telepathy = TRUE; if (FLAG(of_ptr, TR_SEE_INVIS)) num_see_inv = TRUE; if (FLAG(of_ptr, TR_FREE_ACT)) num_free_act = TRUE; if (FLAG(of_ptr, TR_HOLD_LIFE)) num_hold_life = TRUE; /* Weird flags */ /* Bad flags */ /* Immunity flags */ if (FLAG(of_ptr, TR_IM_FIRE)) num_immune_fire = TRUE; if (FLAG(of_ptr, TR_IM_ACID)) num_immune_acid = TRUE; if (FLAG(of_ptr, TR_IM_COLD)) num_immune_cold = TRUE; if (FLAG(of_ptr, TR_IM_ELEC)) num_immune_elec = TRUE; /* Resistance flags */ if (FLAG(of_ptr, TR_RES_ACID)) num_resist_acid = TRUE; if (FLAG(of_ptr, TR_RES_ELEC)) num_resist_elec = TRUE; if (FLAG(of_ptr, TR_RES_FIRE)) num_resist_fire = TRUE; if (FLAG(of_ptr, TR_RES_COLD)) num_resist_cold = TRUE; if (FLAG(of_ptr, TR_RES_POIS)) num_resist_pois = TRUE; if (FLAG(of_ptr, TR_RES_LITE)) num_resist_lite = TRUE; if (FLAG(of_ptr, TR_RES_DARK)) num_resist_dark = TRUE; if (FLAG(of_ptr, TR_RES_BLIND)) num_resist_blind = TRUE; if (FLAG(of_ptr, TR_RES_CONF)) num_resist_conf = TRUE; if (FLAG(of_ptr, TR_RES_SOUND)) num_resist_sound = TRUE; if (FLAG(of_ptr, TR_RES_SHARDS)) num_resist_shard = TRUE; if (FLAG(of_ptr, TR_RES_NEXUS)) num_resist_nexus = TRUE; if (FLAG(of_ptr, TR_RES_NETHER)) num_resist_neth = TRUE; if (FLAG(of_ptr, TR_RES_CHAOS)) num_resist_chaos = TRUE; if (FLAG(of_ptr, TR_RES_DISEN)) num_resist_disen = TRUE; /* Sustain flags */ if (FLAG(of_ptr, TR_SUST_STR)) num_sustain_str = TRUE; if (FLAG(of_ptr, TR_SUST_INT)) num_sustain_int = TRUE; if (FLAG(of_ptr, TR_SUST_WIS)) num_sustain_wis = TRUE; if (FLAG(of_ptr, TR_SUST_DEX)) num_sustain_dex = TRUE; if (FLAG(of_ptr, TR_SUST_CON)) num_sustain_con = TRUE; } /* * Notice a particular item */ static void borg_notice_home_item(list_item *l_ptr, int i) { /* Just checking */ if (!l_ptr) return; /* If this item needs a scroll of *id* */ if (KN_FLAG(l_ptr, TR_INSTA_ART) && !borg_obj_known_full(l_ptr)) { /* count it */ num_artifact += l_ptr->number; } /* If this item has some really bad flag count it */ if (borg_test_bad_curse(l_ptr)) num_bad_curse += l_ptr->number; /* Analyze the item */ switch (l_ptr->tval) { case TV_SOFT_ARMOR: case TV_HARD_ARMOR: { num_armor += l_ptr->number; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_DRAG_ARMOR: { num_armor += l_ptr->number; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, TRUE, i); break; } case TV_CLOAK: { num_cloaks += l_ptr->number; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_SHIELD: { num_shields += l_ptr->number; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_HELM: case TV_CROWN: { num_hats += l_ptr->number; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_GLOVES: { num_gloves += l_ptr->number; /* most gloves hurt magic for spell-casters */ if (bp_ptr->intmana && bp_ptr->msp > 3) { /* Penalize non-usable gloves */ if (l_ptr->number && !KN_FLAG(l_ptr, TR_FREE_ACT) && !(KN_FLAG(l_ptr, TR_DEX) && (l_ptr->pval > 0))) { num_bad_gloves += l_ptr->number; } } /* gloves of slaying give a damage bonus */ home_damage += l_ptr->to_d * 3; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_LITE: { if (KN_FLAG(l_ptr, TR_INSTA_ART)) { num_lite += l_ptr->number; } break; } case TV_BOOTS: { num_boots += l_ptr->number; /* see if this item is duplicated */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_SWORD: case TV_POLEARM: case TV_HAFTED: case TV_DIGGING: { /* Look at weapon information */ borg_notice_home_weapon(l_ptr); /* see if this item is a duplicate */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_BOW: { num_bow += l_ptr->number; /* see if this item is a duplicate */ borg_notice_home_dupe(l_ptr, FALSE, i); break; } case TV_RING: { num_rings += l_ptr->number; /* see if this item is a duplicate */ borg_notice_home_dupe(l_ptr, TRUE, i); break; } case TV_AMULET: { num_neck += l_ptr->number; /* see if this item is a duplicate */ borg_notice_home_dupe(l_ptr, TRUE, i); break; } case TV_LIFE_BOOK: { /* Count good books */ if (borg_has_realm(REALM_LIFE)) num_book[REALM_LIFE][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_SORCERY_BOOK: { /* Count good books */ if (borg_has_realm(REALM_SORCERY)) num_book[REALM_SORCERY][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_NATURE_BOOK: { /* Count good books */ if (borg_has_realm(REALM_NATURE)) num_book[REALM_NATURE][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_CHAOS_BOOK: { /* Count good books */ if (borg_has_realm(REALM_CHAOS)) num_book[REALM_CHAOS][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_DEATH_BOOK: { /* Count good books */ if (borg_has_realm(REALM_DEATH)) num_book[REALM_DEATH][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_TRUMP_BOOK: { /* Count good books */ if (borg_has_realm(REALM_TRUMP)) num_book[REALM_TRUMP][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_ARCANE_BOOK: { /* Count good books */ if (borg_has_realm(REALM_ARCANE)) num_book[REALM_ARCANE][k_info[l_ptr->k_idx].sval] += l_ptr->number; break; } case TV_FOOD: { /* Food */ /* Analyze */ switch (k_info[l_ptr->k_idx].sval) { case SV_FOOD_RATION: { /* If the borg can digest food collect some at home */ if (!FLAG(bp_ptr, TR_CANT_EAT)) num_food += l_ptr->number; break; } case SV_FOOD_RESTORE_STR: { num_fix_stat[A_STR] += l_ptr->number; break; } case SV_FOOD_RESTORE_CON: { num_fix_stat[A_CON] += l_ptr->number; break; } case SV_FOOD_RESTORING: { num_fix_stat[A_STR] += l_ptr->number; num_fix_stat[A_INT] += l_ptr->number; num_fix_stat[A_WIS] += l_ptr->number; num_fix_stat[A_DEX] += l_ptr->number; num_fix_stat[A_CON] += l_ptr->number; num_fix_stat[A_CHR] += l_ptr->number; num_fix_stat[6] += l_ptr->number; break; } } break; } case TV_POTION: { /* Potions */ borg_notice_home_potion(l_ptr); break; } case TV_SCROLL: { /* Scrolls */ borg_notice_home_scroll(l_ptr); break; } case TV_ROD: { /* Rods */ /* Analyze */ switch (k_info[l_ptr->k_idx].sval) { case SV_ROD_IDENTIFY: { num_ident += l_ptr->number * 100; break; } case SV_ROD_RECALL: { num_recall += l_ptr->number * 50; break; } case SV_ROD_CURING: { num_cure_critical += l_ptr->number * 20; break; } } break; } case TV_STAFF: { /* Staffs */ /* Only collect staves with more than 3 charges at high level */ if (l_ptr->pval <= 3 && bp_ptr->lev > 30) break; /* Analyze */ switch (k_info[l_ptr->k_idx].sval) { case SV_STAFF_IDENTIFY: { num_ident += l_ptr->number * l_ptr->pval; break; } case SV_STAFF_CURING: { num_cure_critical += l_ptr->number * l_ptr->pval; break; } case SV_STAFF_TELEPORTATION: { /* * Don't use them deep in the dungeon because the * charges will get drained and he wont have any * scrolls left to read */ if (bp_ptr->max_depth < 97) { num_teleport += l_ptr->number * l_ptr->pval; } break; } } break; } case TV_SHOT: case TV_ARROW: case TV_BOLT: { /* Missiles */ /* Hack -- ignore invalid missiles */ if (l_ptr->tval != my_ammo_tval) break; /* Count them */ num_missile += l_ptr->number; break; } } } /* * Helper function -- notice the home inventory */ static void borg_notice_home_aux(void) { int i; int num; list_item *l_ptr; /*** Process the inventory ***/ /* Scan the home */ for (i = 0; i < (home_num + EQUIP_MAX); i++) { if (i < home_num) l_ptr = &borg_home[i]; else l_ptr = look_up_equip_slot(i - home_num); /* Skip empty / unaware items */ if (!l_ptr || !l_ptr->k_idx) continue; /* Don't count items we are swapping or dropping */ if (l_ptr->treat_as == TREAT_AS_SWAP) continue; if (l_ptr->treat_as == TREAT_AS_GONE) continue; /* Save number of items */ num = l_ptr->number; /* Hack - simulate change in number of items */ if (l_ptr->treat_as == TREAT_AS_LESS) l_ptr->number--; if (l_ptr->treat_as == TREAT_AS_MORE) l_ptr->number++; /* Notice item flags */ borg_notice_home_flags(l_ptr); /* Notice the item itself */ borg_notice_home_item(l_ptr, i); /* Hack - revert change in number of items */ l_ptr->number = num; } /* Scan the inventory for virtual home items */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Ignore normal items */ if (l_ptr->treat_as == TREAT_AS_NORM) continue; /* Save number of items */ num = l_ptr->number; /* Hack - simulate change in number of items */ if (l_ptr->treat_as == TREAT_AS_LESS) l_ptr->number = 1; if (l_ptr->treat_as == TREAT_AS_SWAP) l_ptr->number = 1; if (l_ptr->treat_as == TREAT_AS_MORE) l_ptr->number++; /* Notice item flags */ borg_notice_home_flags(l_ptr); /* Notice the item itself */ borg_notice_home_item(l_ptr, home_num + EQUIP_MAX - 1); /* Hack - revert change in number of items */ l_ptr->number = num; /* Hack - assume only one swap at a time */ break; } /* Scan for virtual home items from stores */ if (use_shop) { for (i = 0; i < cur_num; i++) { l_ptr = &cur_list[i]; /* Ignore normal items */ if (l_ptr->treat_as == TREAT_AS_NORM) continue; /* Save number of items */ num = l_ptr->number; /* Hack - simulate change in number of items */ if (l_ptr->treat_as == TREAT_AS_LESS) l_ptr->number = 1; if (l_ptr->treat_as == TREAT_AS_MORE) l_ptr->number++; /* Notice item flags */ borg_notice_home_flags(l_ptr); /* Notice the item itself */ borg_notice_home_item(l_ptr, home_num + EQUIP_MAX - 1); /* Hack - revert change in number of items */ l_ptr->number = num; } } /*** Process the Spells and Prayers ***/ borg_notice_home_spells(); /*** Process the player abilities ***/ borg_notice_home_player(); } /* * Extract the bonuses for items in the home. */ void borg_notice_home(void) { /* Notice the home equipment */ borg_notice_home_clear(); /* Notice the home inventory */ borg_notice_home_aux(); } /* * Helper function -- calculate power of equipment in the home */ static s32b borg_power_home_aux1(void) { s32b value = 0L; /* * This would be better seperated by item type * (so 1 bonus for resist cold armor * 1 bonus for resist cold shield... * but that would take a bunch more code. * * Try to collect at least 2 of each resist/power (for swapping) * This can be used to get rid of extra artifacts... */ /* Spare lite sources. Artifacts only */ if (num_lite == 1) value += 150L; else if (num_lite == 2) value += 170L; else if (num_lite > 2) value += 170L + (num_lite - 2) * 5L; if (num_slow_digest == 1) value += 50L; else if (num_slow_digest == 2) value += 70L; else if (num_slow_digest > 2) value += 70L + (num_slow_digest - 2) * 5L; if (num_regenerate == 1) value += 75L; else if (num_regenerate == 2) value += 100L; else if (num_regenerate > 2) value += 100L + (num_regenerate - 2) * 10L; if (num_telepathy == 1) value += 1000L; else if (num_telepathy == 2) value += 1500L; else if (num_telepathy > 2) value += 1500L + (num_telepathy - 2) * 10L; if (num_see_inv == 1) value += 800L; else if (num_see_inv == 2) value += 1200L; else if (num_see_inv > 2) value += 1200L + (num_see_inv - 2) * 10L; if (num_ffall == 1) value += 10L; else if (num_ffall == 2) value += 15L; else if (num_ffall > 2) value += 15L + (num_ffall - 2) * 1L; if (num_free_act == 1) value += 1000L; else if (num_free_act == 2) value += 1500L; else if (num_free_act > 2) value += 1500L + (num_free_act - 2) * 10L; if (num_hold_life == 1) value += 1000L; else if (num_hold_life == 2) value += 1500L; else if (num_hold_life > 2) value += 1500L + (num_hold_life - 2) * 10L; if (num_resist_acid == 1) value += 1000L; else if (num_resist_acid == 2) value += 1500L; else if (num_resist_acid > 2) value += 1500L + (num_resist_acid - 2) * 1L; if (num_immune_acid == 1) value += 3000L; else if (num_immune_acid == 2) value += 5000L; else if (num_immune_acid > 2) value += 5000L + (num_immune_acid - 2) * 30L; if (num_resist_elec == 1) value += 1000L; else if (num_resist_elec == 2) value += 1500L; else if (num_resist_elec > 2) value += 1500L + (num_resist_elec - 2) * 1L; if (num_immune_elec == 1) value += 3000L; else if (num_immune_elec == 2) value += 5000L; else if (num_immune_elec > 2) value += 5000L + (num_immune_elec - 2) * 30L; if (num_resist_fire == 1) value += 1000L; else if (num_resist_fire == 2) value += 1500L; else if (num_resist_fire > 2) value += 1500L + (num_resist_fire - 2) * 1L; if (num_immune_fire == 1) value += 3000L; else if (num_immune_fire == 2) value += 5000L; else if (num_immune_fire > 2) value += 5000L + (num_immune_fire - 2) * 30L; if (num_resist_cold == 1) value += 1000L; else if (num_resist_cold == 2) value += 1500L; else if (num_resist_cold > 2) value += 1500L + (num_resist_cold - 2) * 1L; if (num_immune_cold == 1) value += 3000L; else if (num_immune_cold == 2) value += 5000L; else if (num_immune_cold > 2) value += 5000L + (num_immune_cold - 2) * 30L; if (num_resist_pois == 1) value += 5000L; else if (num_resist_pois == 2) value += 9000L; else if (num_resist_pois > 2) value += 9000L + (num_resist_pois - 2) * 40L; if (num_resist_conf == 1) value += 2000L; else if (num_resist_conf == 2) value += 3500L; else if (num_resist_conf > 2) value += 3500L + (num_resist_conf - 2) * 45L; if (num_resist_sound == 1) value += 500L; else if (num_resist_sound == 2) value += 700L; else if (num_resist_sound > 2) value += 700L + (num_resist_sound - 2) * 30L; if (num_resist_lite == 1) value += 100L; else if (num_resist_lite == 2) value += 150L; else if (num_resist_lite > 2) value += 150L + (num_resist_lite - 2) * 1L; if (num_resist_dark == 1) value += 100L; else if (num_resist_dark == 2) value += 150L; else if (num_resist_dark > 2) value += 150L + (num_resist_dark - 2) * 1L; if (num_resist_chaos == 1) value += 1000L; else if (num_resist_chaos == 2) value += 1500L; else if (num_resist_chaos > 2) value += 1500L + (num_resist_chaos - 2) * 10L; if (num_resist_disen == 1) value += 5000L; else if (num_resist_disen == 2) value += 7000L; else if (num_resist_disen > 2) value += 7000L + (num_resist_disen - 2) * 35L; if (num_resist_shard == 1) value += 100L; else if (num_resist_shard == 2) value += 150L; else if (num_resist_shard > 2) value += 150L + (num_resist_shard - 2) * 1L; if (num_resist_nexus == 1) value += 200L; else if (num_resist_nexus == 2) value += 300L; else if (num_resist_nexus > 2) value += 300L + (num_resist_nexus - 2) * 2L; if (num_resist_blind == 1) value += 500L; else if (num_resist_blind == 2) value += 700L; else if (num_resist_blind > 2) value += 700L + (num_resist_blind - 2) * 5L; if (num_resist_neth == 1) value += 5000L; else if (num_resist_neth == 2) value += 7000L; else if (num_resist_neth > 2) value += 7000L + (num_resist_neth - 2) * 45L; /* * Stat gain items as well... * (good to carry ring of dex +6 in * house even if I don't need it right now) */ if (home_stat_add[A_STR] < 9) value += home_stat_add[A_STR] * 300L; else if (home_stat_add[A_STR] < 15) value += 9 * 300L + (home_stat_add[A_STR] - 9) * 200L; else value += 9 * 300L + 6 * 200L + (home_stat_add[A_STR] - 15) * 1L; if (home_stat_add[A_DEX] < 9) value += home_stat_add[A_DEX] * 300L; else if (home_stat_add[A_DEX] < 15) value += 9 * 300L + (home_stat_add[A_DEX] - 9) * 200L; else value += 9 * 300L + 6 * 200L + (home_stat_add[A_DEX] - 15) * 1L; if (home_stat_add[A_CON] < 15) value += home_stat_add[A_CON] * 300L; else if (home_stat_add[A_CON] < 21) value += 15 * 300L + (home_stat_add[A_CON] - 15) * 200L; else value += 15 * 300L + 6 * 200L + (home_stat_add[A_CON] - 21) * 1L; /* int and wis are only bonused for spell casters. */ if (bp_ptr->intmana) { if (home_stat_add[A_INT] < 20) value += home_stat_add[A_INT] * 400L; else if (home_stat_add[A_INT] < 26) value += 20 * 400L + (home_stat_add[A_INT] - 20) * 300L; else value += 20 * 100L + 6 * 300L + (home_stat_add[A_INT] - 26) * 5L; } if (bp_ptr->wismana) { if (home_stat_add[A_WIS] < 20) value += home_stat_add[A_WIS] * 400L; else if (home_stat_add[A_WIS] < 26) value += 20 * 400L + (home_stat_add[A_WIS] - 20) * 300L; else value += 20 * 400L + 6 * 300L + (home_stat_add[A_WIS] - 26) * 3L; } /* Sustains */ if (num_sustain_str == 1) value += 200L; else if (num_sustain_str == 2) value += 250L; else if (num_sustain_str > 2) value += 250L + (num_sustain_str - 2) * 1L; if (num_sustain_int == 1) value += 200L; else if (num_sustain_int == 2) value += 250L; else if (num_sustain_int > 2) value += 250L + (num_sustain_int - 2) * 1L; if (num_sustain_wis == 1) value += 200L; else if (num_sustain_wis == 2) value += 250L; else if (num_sustain_wis > 2) value += 250L + (num_sustain_wis - 2) * 1L; if (num_sustain_con == 1) value += 200L; else if (num_sustain_con == 2) value += 250L; else if (num_sustain_con > 2) value += 250L + (num_sustain_con - 2) * 1L; if (num_sustain_dex == 1) value += 200L; else if (num_sustain_dex == 2) value += 250L; else if (num_sustain_dex > 2) value += 250L + (num_sustain_dex - 2) * 1L; if (num_sustain_all == 1) value += 1000L; else if (num_sustain_all == 2) value += 1500L; else if (num_sustain_all > 2) value += 1500L + (num_sustain_all - 2) * 1L; /* Count the un*id*'d artifacts stored at home */ value += MIN(num_artifact, 7) * 100000; /* * Do a minus for too many duplicates. * This way we do not store useless items * and spread out types of items. */ if (num_weapons > 5) value -= (num_weapons - 5) * 2000L; else if (num_weapons > 1) value -= (num_weapons - 1) * 100L; if (num_bow > 2) value -= (num_bow - 2) * 1000L; if (num_rings > 6) value -= (num_rings - 6) * 4000L; else if (num_rings > 4) value -= (num_rings - 4) * 2000L; if (num_neck > 3) value -= (num_neck - 3) * 1500L; else if (num_neck > 2) value -= (num_neck - 2) * 700L; if (num_armor > 6) value -= (num_armor - 6) * 1000L; if (num_cloaks > 3) value -= (num_cloaks - 3) * 1000L; if (num_shields > 3) value -= (num_shields - 3) * 1000L; if (num_hats > 4) value -= (num_hats - 4) * 1000L; if (num_gloves > 3) value -= (num_gloves - 3) * 1000L; if (num_boots > 2) value -= (num_boots - 2) * 1000L; value += home_damage; /* If edged and priest, dump it */ value -= num_edged_weapon * 100000L; /* If gloves and mage or ranger and not FA/Dex, dump it. */ value -= num_bad_gloves * 100000L; /* Do not allow duplication of items. */ value -= num_duplicate_items * 5000L; /* Do not allow bad flags */ value -= num_bad_curse * 100000; /* Return the value */ return (value); } /* * Helper function -- calculate power of items in the home. * If the value here is higher than the value for the same item in the * the borg will drop it at home. So this way the borg collects things at home * There are two values for most items. The idea here is that once the borg * has stockpiled a bit at home he can carry more of these items in his inv. * Look at num_food below. That corresponds to bp_ptr->food that counts the * amount of food in the inv. Having food in the inv is very important so the * borg gets 10000 per food item in the inv, but only for the first five. Then * the borg collects some for the home and once he 10 at home he takes along * more in the inv. * The usage of 'pile' is a try to keep the home not too full, otherwise the * borg may spend all his money on building stock for boring items. Otherwise * he'd only go down to lvl 5 when he has 99 scrolls of Word of Recall. */ static s32b borg_power_home_aux2(void) { int book = 0, realm = 0; int pile = 2 * bp_ptr->lev - 1; s32b value = 0L; /*** Basic abilities ***/ /* Collect food */ value += 500 * MIN(num_food, 10); value += 50 * MIN_FLOOR(num_food, 10, pile); /* Emphasize on collecting scrolls of food above mere rations */ value += 10 * MIN(num_food_scroll, pile); /* Collect ident */ value += 1500 * MIN(num_ident, 20); value += 150 * MIN_FLOOR(num_ident, 20, pile); /* Collect *id*ent */ value += 1700 * MIN(num_star_ident, 10); value += 170 * MIN_FLOOR(num_star_ident, 10, pile); /* Collect remove curse */ value += 500 * MIN(num_remove_curse, 5); value += 75 * MIN_FLOOR(num_remove_curse, 5, pile); /* Collect *remove curse* */ value += 5000 * MIN(num_star_remove_curse, 5); value += 750 * MIN_FLOOR(num_star_remove_curse, 5, pile); /* apw Collect glyphs */ value += 1000 * MIN(num_glyph, pile); /* Reward Genocide scrolls. Just scrolls, mainly used for the Serpent */ value += 1000 * MIN(num_genocide, pile); /* Reward Mass Genocide scrolls. Just scrolls, mainly used for Serpent */ value += 1000 * MIN(num_mass_genocide, pile); /* Reward Resistance Potions */ value += 200 * MIN(num_pot_resist, pile); /* Collect recall */ value += 1700 * MIN(num_recall, 20); value += 70 * MIN_FLOOR(num_recall, 20, pile); /* Collect phase door */ value += 1700 * MIN(num_phase, 20); value += 70 * MIN_FLOOR(num_phase, 20, pile); /* Collect escape */ value += 3000 * MIN(num_escape, 20); value += 300 * MIN_FLOOR(num_escape, 20, pile); /* Collect teleport */ value += 1000 * MIN(num_teleport, 20); value += 100 * MIN_FLOOR(num_teleport, 20, pile); /* Collect teleport level scrolls */ value += 1000 * MIN(num_teleport_level, 20); value += 100 * MIN_FLOOR(num_teleport_level, 20, pile); /* Collect Speed */ value += 3000 * MIN(num_speed, 20); value += 300 * MIN_FLOOR(num_speed, 20, pile); /* Collect Berserk */ value += 400 * MIN(num_berserk, 20); value += 40 * MIN_FLOOR(num_berserk, 20, pile); /* Collect Invuln Potions (As if you'd ever find so many potions) */ value += 5000 * MIN(num_goi_pot, pile); /* Collect heal */ value += 3000 * MIN(num_heal, 99); value += 8000 * MIN(num_ez_heal, 99); /* Potion of Mana */ if (borg_class != CLASS_WARRIOR) { value += 2000 * MIN(num_mana, 99); } /* Collect cure critical */ value += 3500 * MIN(num_cure_critical, 99); /* Collect cure serious - but they aren't as good */ if (bp_ptr->mhp < 500) value += 400 * MIN(num_cure_serious, 99); /* Borgs with low HP collect cure light wounds */ if (bp_ptr->mhp < 250) value += 200 * MIN(num_cure_light, 99); /*** Various ***/ /* Fixing Stats */ if (bp_ptr->lev == 50) value += 50L * MIN(num_fix_exp, bp_ptr->lev * 2 -1); else { value += 5000 * MIN(num_fix_exp, 5); value += 500 * MIN_FLOOR(num_fix_exp, 5, pile); } /* Keep shrooms in the house */ value += 5000 * MIN(num_fix_stat[6], pile); /*** Hack -- books ***/ /* Scan Realms */ for (realm = 0; realm < MAX_REALM; realm++) { /* Only my realms */ if (!borg_has_realm(realm)) continue; /* Scan town books */ for (book = 0; book < 4; book++) { /* Does the borg have this book yet? */ if (!num_book[realm][book]) continue; /* Does the borg use this book yet? */ if (!borg_uses_book(realm, book)) { /* Reward keeping the first unused book in the house */ value += 5000 * MIN(num_book[realm][book], 1); } /* Is this a town book? */ if (book < 2 || realm == REALM_ARCANE) { /* Assign value to keep extra books */ value += 100 * MIN(num_book[realm][book], pile); } } } /* Return the value */ return (value); } /* * Calculate the "power" of the home */ s32b borg_power_home(void) { s32b value = 0L; /* Notice changes to the home */ borg_notice_home(); /* Process the home equipment */ value += borg_power_home_aux1(); /* Process the home inventory */ value += borg_power_home_aux2(); /* Return the value */ return (value); } /* * Initialize this file */ void borg_init_4(void) { /* Make the home inventory array */ C_MAKE(borg_home, STORE_INVEN_MAX, list_item); } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg5.c0000644000000000000000000032024510250356275013516 0ustar rootroot/* File: borg5.c */ /* Purpose: Medium level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" /* * Calculate base danger from a monster's physical attacks * * We attempt to take account of various resistances, both in * terms of actual damage, and special effects, as appropriate. * * We reduce the danger from distant "sleeping" monsters. * apw. PFE reduces my fear of an area. */ static int borg_danger_aux1(int r_idx) { int k, n = 0; int pfe = 0; int power, chance; s16b ac = bp_ptr->ac; monster_race *r_ptr = &r_info[r_idx]; /* goi gives +100 to ac and deflects almost all missiles and balls */ if (borg_goi) ac += 100; /* shields gives +50 to ac and deflects some missiles and balls */ if (borg_shield) ac += 50; /* apw PFE gives a protection. */ /* Hack -- Apply "protection from evil" */ if ((borg_prot_from_evil) && (FLAG(r_ptr, RF_EVIL)) && (bp_ptr->lev >= r_ptr->level)) { pfe = 1; } /* Analyze each physical attack */ for (k = 0; k < 4; k++) { int z = 0; monster_blow *b_ptr = &r_ptr->blow[k]; power = 0; /* Done */ if (!b_ptr->method) break; /* Analyze the attack */ switch (b_ptr->effect) { case RBE_HURT: { z = (b_ptr->d_dice * b_ptr->d_side); z -= (z * ((ac < 150) ? ac : 150) / 250); /* if invulnurable (or PFE), no damage (carried through) */ if ((borg_goi) && !borg_attacking) z = 0; if ((pfe) && !borg_attacking) z /= 2; /* stun */ if ((b_ptr->d_side < 3) && (z > b_ptr->d_dice * b_ptr->d_side)) n += 200; /* fudge- only mystics kick and they tend to KO. Avoid close */ /* combat like the plauge */ if (b_ptr->method == RBM_KICK) { /* If GOI is on, take that into account */ n += borg_goi ? 400 * 20 : 400; } power = 60; break; } case RBE_POISON: { z = (b_ptr->d_dice * b_ptr->d_side); power = 5; if (FLAG(bp_ptr, TR_RES_POIS)) break; if (my_oppose_pois) break; if (!borg_full_damage) z += 10; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_UN_BONUS: { z = (b_ptr->d_dice * b_ptr->d_side); power = 20; if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_DISEN)) break; /* if invulnurable, no damage */ if (!borg_full_damage) z += 500; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_UN_POWER: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!borg_full_damage) z += 20; if ((pfe) && !borg_attacking) z /= 2; power = 15; break; } case RBE_EAT_GOLD: { z = (b_ptr->d_dice * b_ptr->d_side); /* if in town and low level avoid them stupid urchins */ if (bp_ptr->lev < 5) z += 50; /* if invulnurable, no damage */ power = 5; if ((borg_goi) && !borg_attacking) z = 0; if (100 <= adj_dex_safe[my_stat_ind[A_DEX]] + bp_ptr->lev) break; if (borg_gold < 100) break; if (borg_gold > 100000) break; if (!borg_full_damage) z += 5; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EAT_ITEM: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ power = 5; if ((borg_goi) && !borg_attacking) z = 0; if (100 <= adj_dex_safe[my_stat_ind[A_DEX]] + bp_ptr->lev) break; if (!borg_full_damage) z += 20; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EAT_FOOD: { z = (b_ptr->d_dice * b_ptr->d_side); power = 5; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (bp_ptr->food > 5) break; if (!borg_full_damage) z += 5; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EAT_LITE: { z = (b_ptr->d_dice * b_ptr->d_side); power = 5; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!bp_ptr->cur_lite) break; if (bp_ptr->able.fuel > 5) break; if (!borg_full_damage) z += 20; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_ACID: { if (FLAG(bp_ptr, TR_IM_ACID)) break; z = (b_ptr->d_dice * b_ptr->d_side); if (FLAG(bp_ptr, TR_RES_ACID)) z = (z + 2) / 3; if (my_oppose_acid) z = (z + 2) / 3; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!borg_full_damage) z += 200; /* We dont want our armour corroded. */ if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_ELEC: { if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = (b_ptr->d_dice * b_ptr->d_side); power = 10; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_ELEC)) z = (z + 2) / 3; if (my_oppose_elec) z = (z + 2) / 3; if (!borg_full_damage) z += 10; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_FIRE: { if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = (b_ptr->d_dice * b_ptr->d_side); power = 10; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_FIRE)) z = (z + 2) / 3; if (my_oppose_fire) z = (z + 2) / 3; if (!borg_full_damage) z += 20; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_COLD: { if (FLAG(bp_ptr, TR_IM_COLD)) break; z = (b_ptr->d_dice * b_ptr->d_side); power = 10; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_COLD)) z = (z + 2) / 3; if (my_oppose_cold) z = (z + 2) / 3; if (!borg_full_damage) z += 15; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_BLIND: { z = (b_ptr->d_dice * b_ptr->d_side); power = 2; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_BLIND)) break; if (!borg_full_damage) z += 10; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_CONFUSE: { z = (b_ptr->d_dice * b_ptr->d_side); power = 10; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_CONF)) break; if (!borg_full_damage) z += 200; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_TERRIFY: { z = (b_ptr->d_dice * b_ptr->d_side); power = 10; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_RES_FEAR)) break; if (!borg_full_damage) z += 10; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_PARALYZE: { z = (b_ptr->d_dice * b_ptr->d_side); power = 2; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_FREE_ACT)) break; z += 200; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_STR: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; if (bp_ptr->sust[A_STR]) break; if (borg_stat[A_STR] <= 30) break; if (borg_spell_legal(REALM_LIFE, 3, 3)) break; z += 150; /* extra scary to have str drain below 10 */ if (borg_stat[A_STR] < 100) z += 350; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_DEX: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; if (bp_ptr->sust[A_DEX]) break; if (borg_stat[A_DEX] <= 30) break; if (borg_spell_legal(REALM_LIFE, 3, 3)) break; z += 150; /* extra scary to have drain below 10 */ if (borg_stat[A_DEX] < 100) z += 350; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_CON: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; if (bp_ptr->sust[A_CON]) break; if (borg_stat[A_CON] <= 30) break; if (borg_spell_legal(REALM_LIFE, 3, 3)) break; if (!borg_full_damage) z += 150; /* extra scary to have con drain below 8 */ if (borg_stat[A_STR] < 80) z += 350; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_INT: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; if (bp_ptr->sust[A_INT]) break; if (borg_stat[A_INT] <= 30) break; if (borg_spell_legal(REALM_LIFE, 3, 3)) break; z += 150; /* extra scary for spell caster */ if (bp_ptr->intmana) z += 350; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_WIS: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; if (bp_ptr->sust[A_WIS]) break; if (borg_stat[A_WIS] <= 30) break; if (borg_spell_legal(REALM_LIFE, 3, 3)) break; z += 150; /* extra scary for pray'er */ if (bp_ptr->wismana) z += 350; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_CHR: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; if (bp_ptr->sust[A_CHR]) break; if (borg_stat[A_CHR] <= 30) break; if (borg_spell_legal(REALM_LIFE, 3, 3)) break; z += 50; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_LOSE_ALL: { z = (b_ptr->d_dice * b_ptr->d_side); power = 2; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z /= 25; /* only Serpent. HACK to make it easier to fight him */ break; } case RBE_SHATTER: { z = (b_ptr->d_dice * b_ptr->d_side); z -= (z * ((ac < 150) ? ac : 150) / 250); power = 60; /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!borg_full_damage) z += 150; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EXP_10: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_HOLD_LIFE)) break; /* do not worry about drain exp after level 50 */ if (bp_ptr->lev == 50) break; if (borg_spell_legal(REALM_LIFE, 3, 3) || borg_spell_legal(REALM_DEATH, 1, 7)) break; if (!borg_full_damage) z += 100; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EXP_20: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_HOLD_LIFE)) break; /* do not worry about drain exp after level 50 */ if (bp_ptr->lev >= 50) break; if (borg_spell_legal(REALM_LIFE, 3, 3) || borg_spell_legal(REALM_DEATH, 1, 7)) break; if (!borg_full_damage) z += 150; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EXP_40: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_HOLD_LIFE)) break; /* do not worry about drain exp after level 50 */ if (bp_ptr->lev >= 50) break; if (borg_spell_legal(REALM_LIFE, 3, 3) || borg_spell_legal(REALM_DEATH, 1, 7)) break; if (!borg_full_damage) z += 200; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EXP_80: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (FLAG(bp_ptr, TR_HOLD_LIFE)) break; /* do not worry about drain exp after level 50 */ if (bp_ptr->lev >= 50) break; if (borg_spell_legal(REALM_LIFE, 3, 3) || borg_spell_legal(REALM_DEATH, 1, 7)) break; if (!borg_full_damage) z += 250; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_DISEASE: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!(FLAG(bp_ptr, TR_RES_POIS)) && !my_oppose_pois) z += 50; /* there is a 10% chance to suffer CON loss */ if (!bp_ptr->sust[A_CON]) z += 50; if (!borg_full_damage) z += 50; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_TIME: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!(FLAG(bp_ptr, TR_HOLD_LIFE)) && (bp_ptr->lev != 50)) z += 25; if (!borg_spell_legal(REALM_LIFE, 3, 3) && !borg_spell_legal(REALM_DEATH, 1, 7)) z += 25; /* Some fear for non sustaining stats ? */ if (!borg_full_damage) z += 100; if ((pfe) && !borg_attacking) z /= 2; break; } case RBE_EXP_VAMP: { z = (b_ptr->d_dice * b_ptr->d_side); /* if invulnurable, no damage */ if ((borg_goi) && !borg_attacking) z = 0; if (!(FLAG(bp_ptr, TR_HOLD_LIFE)) && (bp_ptr->lev != 50)) z += 25; if (!borg_spell_legal(REALM_LIFE, 3, 3) && !borg_spell_legal(REALM_DEATH, 1, 7)) z += 25; if (!borg_full_damage) z += 200; if ((pfe) && !borg_attacking) z /= 2; break; } } /* if we are doing partial damage reduce for % chance that it will */ /* hit you. */ if (!borg_full_damage) { /* figure out chance that monster will hit you. */ /* add a 30% bonus in to account for bad luck. */ if ((r_ptr->level + power) > 0) chance = 130 - (((ac * 300) / 4) / ((r_ptr->level + power) * 3)); else chance = -1; /* always have a 5% chance of hitting. */ if (chance < 0) z = (z * 5) / 100; if (chance < 100) z = (z * chance) / 100; } /* Add in damage */ n += z; } /* if invulnurable, very little damage 5% */ if (borg_goi) n = (n * 5 / 100); /* Danger */ return (n); } /* * Calculate base danger from a monster's spell attacks * * We attempt to take account of various resistances, both in * terms of actual damage, and special effects, as appropriate. * * We reduce the danger from distant "sleeping" monsters. * * We reduce the danger if the monster is immobile or not LOS */ static int borg_danger_aux2(int i, bool average) { int q, k, n = 0, pfe = 0; bool glyph = FALSE; int true_borg_goi = borg_goi; int spot_x, spot_y, spot_safe = 1; int x, y; int lev, hp, total_dam = 0, av; byte spell[96], num = 0; borg_kill *kill = &borg_kills[i]; map_block *mb_ptr; monster_race *r_ptr = &r_info[kill->r_idx]; /* apw PFE gives a protection. */ /* Hack -- Apply "protection from evil" */ if ((borg_prot_from_evil) && (FLAG(r_ptr, RF_EVIL)) && ((bp_ptr->lev) >= r_ptr->level)) { pfe = 1; } /* * Glyph of warding rune of protection provides some small * protection with some ranged atacks; mainly summon attacks. * We should reduce the danger commensurate to the probability of the * monster breaking the glyph as defined by melee2.c */ if (borg_on_glyph) { glyph = 1; } /* This is used to calculate the free squares next to us. * This is important when dealing with summoners. */ for (spot_x = -1; spot_x <= 1; spot_x++) { for (spot_y = -1; spot_y <= 1; spot_y++) { /* Acquire location */ x = spot_x + c_x; y = spot_y + c_y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* skip our own spot */ if (x == c_x && y == c_y) continue; /* Track spaces already protected */ if (mb_ptr->monster || ((mb_ptr->feat >= FEAT_CLOSED) && (mb_ptr->feat <= FEAT_PERM_SOLID))) { /* Track the safe areas for calculating danger */ spot_safe++; /* Just in case */ if (spot_safe == 0) spot_safe = 1; } } } /* HACK- to accomdate for GOI and Create_Door */ if (borg_create_door) { borg_goi = 0; } /* Extract the "inate" spells */ for (k = 0; k < 32; k++) { if (r_ptr->flags[3] & (1L << k)) spell[num++] = k + 32 * 3; } /* Extract the "normal" spells */ for (k = 0; k < 32; k++) { if (r_ptr->flags[4] & (1L << k)) spell[num++] = k + 32 * 4; } /* Extract the "bizarre" spells */ for (k = 0; k < 32; k++) { if (r_ptr->flags[5] & (1L << k)) spell[num++] = k + 32 * 5; } /* Paranoia -- Nothing to cast */ if (!num) return (0); /* Extract the level */ lev = r_ptr->level; /* Extract hit-points */ hp = kill->power; /* Analyze the spells */ for (q = 0; q < num; q++) { int p = 0; int z = 0; /* Cast the spell. */ switch (spell[q]) { case 96 + 0: { /* RF3_SHRIEK */ /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 96 + 1: { /* RF3_XXX2X4 */ /* this is now a failed spell attempt for monsters */ /* used to recognize invisible/ hidden monsters */ p += 10; break; } case 96 + 2: { /* RF3_XXX3X4 */ break; } case 96 + 3: { /* RF3_ROCKETS */ z = (hp / 4); /* max damage */ if (z > 600) z = 600; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SHARDS)) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 96 + 4: { int dice = (r_ptr->hdice < 4 ? 1 : r_ptr->hdice / 4); if (dice > 7) dice = 7; /* RF3_ARROW */ z = (dice * 6); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_REFLECT)) z = (z + 2) / 3; break; } case 96 + 5: { /* RF3_XXX6 */ break; } case 96 + 6: { /* RF3_XXX7 */ break; } case 96 + 7: { /* RF3_XXX8 */ break; } case 96 + 8: { /* RF3_BR_ACID */ if (FLAG(bp_ptr, TR_IM_ACID)) break; z = (hp / 3); /* max damage */ if (z > 1200) z = 1200; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_ACID)) z = (z + 2) / 3; if (my_oppose_acid) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 96 + 9: { /* RF3_BR_ELEC */ if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = (hp / 3); /* max damage */ if (z > 1200) z = 1200; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_ELEC)) z = (z + 2) / 3; if (my_oppose_elec) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 96 + 10: { /* RF3_BR_FIRE */ if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = (hp / 3); /* max damage */ if (z > 1200) z = 1200; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_FIRE)) z = (z + 2) / 3; if (my_oppose_fire) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 96 + 11: { /* RF3_BR_COLD */ if (FLAG(bp_ptr, TR_IM_COLD)) break; z = (hp / 3); /* max damage */ if (z > 1200) z = 1200; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_COLD)) z = (z + 2) / 3; if (my_oppose_cold) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 96 + 12: { /* RF3_BR_POIS */ z = (hp / 3); /* max damage */ if (z > 600) z = 600; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_POIS)) z = (z + 2) / 3; if (my_oppose_pois) z = (z + 2) / 3; if (my_oppose_pois) break; if (FLAG(bp_ptr, TR_RES_POIS)) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 96 + 13: { /* RF3_BR_NETH */ z = (hp / 6); /* max damage */ if (z > 450) z = 450; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_NETHER)) { z = (z * 6) / 9; break; } if (!borg_full_damage) p += 125; break; } case 96 + 14: { /* RF3_BR_LITE */ z = (hp / 4); /* max damage */ if (z > 350) z = 350; /* Vamps hurt more */ if (FLAG(bp_ptr, TR_HURT_LITE)) z *= 2; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_LITE)) { z = (z * 2) / 3; break; } if (FLAG(bp_ptr, TR_RES_BLIND)) break; p += 20; break; } case 96 + 15: { /* RF3_BR_DARK */ z = (hp / 4); /* max damage */ if (z > 350) z = 350; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_DARK)) { z = (z * 2) / 3; break; } if (FLAG(bp_ptr, TR_RES_BLIND)) break; p += 20; break; } case 96 + 16: { /* RF3_BR_CONF */ z = (hp / 4); /* max damage */ if (z > 350) z = 350; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_CONF)) { z = z / 2; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 300; break; } case 96 + 17: { /* RF3_BR_SOUN */ z = (hp / 4); /* max damage */ if (z > 350) z = 350; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_SOUND)) { z = (z * 5) / 9; break; } /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 500; if (bp_ptr->status.heavy_stun) p += 1000; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 50; break; } case 96 + 18: { /* RF3_BR_CHAO */ z = (hp / 4); /* max damage */ if (z > 500) z = 500; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_CHAOS)) z = (z * 6) / 9; if (!borg_full_damage) p += 100; if (FLAG(bp_ptr, TR_RES_CHAOS)) break; p += 200; break; } case 96 + 19: { /* RF3_BR_DISE */ z = (hp / 4); /* max damage */ if (z > 400) z = 400; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_DISEN)) { z = (z * 6) / 10; break; } p += 500; break; } case 96 + 20: { /* RF3_BR_NEXU */ z = (hp / 3); /* max damage */ if (z > 250) z = 250; if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_NEXUS)) { z = (z * 6) / 10; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 100; break; } case 96 + 21: { /* RF3_BR_TIME */ z = (hp / 3); /* max damage */ if (z > 150) z = 150; if (borg_goi) { z /= 25; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 250; break; } case 96 + 22: { /* RF3_BR_INER */ z = (hp / 4); /* max damage */ if (z > 200) z = 200; if (borg_goi) { z /= 25; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 100; break; } case 96 + 23: { /* RF3_BR_GRAV */ z = (hp / 3); /* max damage */ if (z > 200) z = 200; if (borg_goi) { z /= 25; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 100; if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* Pump this up if you have goi so that the borg is sure */ /* to be made nervous */ if (borg_goi) p += 100; else p += 75; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 500; if (bp_ptr->status.heavy_stun) p += 1000; break; } case 96 + 24: { /* RF3_BR_SHAR */ z = (hp / 4); /* max damage */ if (z > 400) z = 400; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SHARDS)) { z = (z * 6) / 9; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 50; break; } case 96 + 25: { /* RF3_BR_PLAS */ z = (hp / 4); /* max damage */ if (z > 200) z = 200; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* Pump this up if you have goi so that the borg is sure */ /* to be made nervous */ if (borg_goi) p += 200; else p += 100; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 500; if (bp_ptr->status.heavy_stun) p += 1000; break; } case 96 + 26: { /* RF3_BR_WALL */ z = (hp / 4); /* max damage */ if (z > 200) z = 200; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 100; if (bp_ptr->status.heavy_stun) p += 500; if (!borg_full_damage) p += 50; break; } case 96 + 27: { /* RF3_MANA */ z = (hp / 3); /* max damage */ if (z > 250) z = 250; if (borg_goi) { z /= 25; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 50; break; } case 96 + 28: { /* RF3_BA_NUKE */ z = (lev + (10 * 6)); if (borg_goi) { z /= 25; break; } if (!(FLAG(bp_ptr, TR_RES_POIS))) p += 25; if (!borg_full_damage) p += 50; break; } case 96 + 29: { /* RF3_BR_NUKE */ z = (hp / 2); /* max damage */ if (z > 600) z = 600; if (borg_goi) { z /= 25; break; } if (!(FLAG(bp_ptr, TR_RES_POIS))) p += 25; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 75; break; } case 96 + 30: { /* RF3_BA_CHAOS */ z = ((lev * 2) + 75); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_CHAOS)) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 96 + 31: { /* RF3_BA_DISI */ z = (hp / 3); if (z >= 300) z = 300; if (borg_goi) { z /= 25; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; } case 128 + 0: { /* RF4_BA_ACID */ if (FLAG(bp_ptr, TR_IM_ACID)) break; z = (lev * 3) / 2 + 15; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_ACID)) z = (z + 2) / 3; if (my_oppose_acid) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 128 + 1: { /* RF4_BA_ELEC */ if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = (lev * 3) / 2 + 8; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_ELEC)) z = (z + 2) / 3; if (my_oppose_elec) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 2: { /* RF4_BA_FIRE */ if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = (lev * 7) / 2 + 10; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_FIRE)) z = (z + 2) / 3; if (my_oppose_fire) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 128 + 3: { /* RF4_BA_COLD */ if (FLAG(bp_ptr, TR_IM_COLD)) break; z = (lev * 3) / 2 + 10; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_COLD)) z = (z + 2) / 3; if (my_oppose_cold) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 4: { /* RF4_BA_POIS */ z = (12 * 2); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_POIS)) z = (z + 2) / 3; if (my_oppose_pois) z = (z + 2) / 3; if (my_oppose_pois) break; if (FLAG(bp_ptr, TR_RES_POIS)) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 5: { /* RF4_BA_NETH */ z = (lev + (75) + 50); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_NETHER)) { z = (z * 6) / 8; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 250; break; } case 128 + 6: { /* RF4_BA_WATE */ z = ((lev * 5) / 2) + 50; if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 500; if (bp_ptr->status.heavy_stun) p += 1000; if (FLAG(bp_ptr, TR_RES_CONF)) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 50; break; } case 128 + 7: { /* RF4_BA_MANA */ z = ((lev * 4) + 75); if (!borg_full_damage) p += 50; if (borg_goi) { z /= 25; break; } break; } case 128 + 8: { /* RF4_BA_DARK */ z = (((lev * 4)) + (75)); if (borg_goi) { z /= 25; } if (FLAG(bp_ptr, TR_RES_DARK)) { z = (z * 6) / 9; break; } if (FLAG(bp_ptr, TR_RES_BLIND)) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 9: { /* RF4_DRAIN_MANA */ if (bp_ptr->msp) p += 20; break; } case 128 + 10: { /* RF4_MIND_BLAST */ if (bp_ptr->skill_sav < 100) z = 35; break; } case 128 + 11: { /* RF4_BRAIN_SMASH */ z = (12 * 15); p += 200 - 2 * bp_ptr->skill_sav; if (p < 0) p = 0; break; } case 128 + 12: { /* RF4_CAUSE_1 */ if (bp_ptr->skill_sav >= 100) break; z = (3 * 8); /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) /* reduce by % chance of save (add 20% for fudge) */ z = z * (120 - bp_ptr->skill_sav) / 100; break; } case 128 + 13: { /* RF4_CAUSE_2 */ if (bp_ptr->skill_sav >= 100) break; z = (8 * 8); /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) /* reduce by % chance of save (add 20% for fudge) */ z = z * (120 - bp_ptr->skill_sav) / 100; break; } case 128 + 14: { /* RF4_CAUSE_3 */ if (bp_ptr->skill_sav >= 100) break; z = (10 * 15); /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) /* reduce by % chance of save (add 20% for fudge) */ z = z * (120 - bp_ptr->skill_sav) / 100; break; } case 128 + 15: { /* RF4_CAUSE_4 */ if (bp_ptr->skill_sav >= 100) break; z = (15 * 15); /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) /* reduce by % chance of save (add 40% for fudge) */ z = z * (120 - bp_ptr->skill_sav) / 100; break; } case 128 + 16: { /* RF4_BO_ACID */ if (FLAG(bp_ptr, TR_IM_ACID)) break; z = ((7 * 8) + (lev / 3)); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_ACID)) z = (z + 2) / 3; if (my_oppose_acid) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 128 + 17: { /* RF4_BO_ELEC */ if (FLAG(bp_ptr, TR_IM_ELEC)) break; z = ((4 * 8) + (lev / 3)); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_ELEC)) z = (z + 2) / 3; if (my_oppose_elec) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 18: { /* RF4_BO_FIRE */ if (FLAG(bp_ptr, TR_IM_FIRE)) break; z = ((9 * 8) + (lev / 3)); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_FIRE)) z = (z + 2) / 3; if (my_oppose_fire) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 40; break; } case 128 + 19: { /* RF4_BO_COLD */ if (FLAG(bp_ptr, TR_IM_COLD)) break; z = ((6 * 8) + (lev / 3)); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_COLD)) z = (z + 2) / 3; if (my_oppose_cold) z = (z + 2) / 3; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 20: { /* RF4_BO_POIS */ /* XXX XXX XXX */ break; } case 128 + 21: { /* RF4_BO_NETH */ z = (50 + 30 + (5 * 5) + (lev * 3) / 2); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_NETHER)) { z = (z * 6) / 8; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 200; break; } case 128 + 22: { /* RF4_BO_WATE */ z = ((10 * 10) + (lev)); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 500; if (bp_ptr->status.heavy_stun) p += 1000; if (FLAG(bp_ptr, TR_RES_CONF)) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 128 + 23: { /* RF4_BO_MANA */ z = ((lev * 7) / 2) + 50; if (!borg_full_damage) p += 50; if (borg_goi) { z /= 25; break; } break; } case 128 + 24: { /* RF4_BO_PLAS */ z = (10 + (8 * 7) + (lev)); if (borg_goi) { z /= 25; break; } if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 500; if (bp_ptr->status.heavy_stun) p += 1000; break; } case 128 + 25: { /* RF4_BO_ICEE */ z = ((6 * 6) + (lev)); if (borg_goi) { z /= 25; break; } /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; if (FLAG(bp_ptr, TR_RES_SOUND)) break; /* if already stunned be REALLY nervous about this */ if (bp_ptr->status.stun) p += 50; if (bp_ptr->status.heavy_stun) p += 1000; break; } case 128 + 26: { /* RF4_MISSILE */ z = ((2 * 6) + (lev / 3)); if (borg_goi) { z /= 25; break; } break; } case 128 + 27: { /* RF4_SCARE */ if (bp_ptr->skill_sav >= 100) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 128 + 28: { /* RF4_BLIND */ if (bp_ptr->skill_sav >= 100) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 128 + 29: { /* RF4_CONF */ if (bp_ptr->skill_sav >= 100) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 128 + 30: { /* RF4_SLOW */ if (FLAG(bp_ptr, TR_FREE_ACT)) break; if (bp_ptr->skill_sav >= 100) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 5; break; } case 128 + 31: { /* RF4_HOLD */ if (FLAG(bp_ptr, TR_FREE_ACT)) break; if (bp_ptr->skill_sav >= 100) break; p += 150; break; } case 160 + 0: { /* RF5_HASTE */ if (bp_ptr->skill_sav >= 100) break; z += (90 * bp_ptr->chp / 100); p += 150; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 160 + 1: { /* RF5_HAND_OF_DOOM */ break; } case 160 + 2: { /* RF5_HEAL */ /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 160 + 3: { /* RF5_INVULNER */ break; } case 160 + 4: { /* RF5_BLINK */ break; } case 160 + 5: { /* RF5_TPORT */ break; } case 160 + 6: { /* RF5_XXX3X6 */ break; } case 160 + 7: { /* RF5_XXX4X6 */ break; } case 160 + 8: { /* RF5_TELE_TO */ /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 20; break; } case 160 + 9: { /* RF5_TELE_AWAY */ /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 10; break; } case 160 + 10: { /* RF5_TELE_LEVEL */ if (bp_ptr->skill_sav >= 100) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 50; break; } case 160 + 11: { /* RF5_XXX5 */ break; } case 160 + 12: { /* RF5_DARKNESS */ /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 5; break; } case 160 + 13: { /* RF5_TRAPS */ /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) p += 50; break; } case 160 + 14: { /* RF5_FORGET */ if (bp_ptr->skill_sav >= 100) break; /* if looking at full damage, things that are just annoying */ /* do not count. */ if (!borg_full_damage) { /* if you have lots of cash (like you will at level 35) */ /* this is not very scary... just re-ID. */ if (bp_ptr->lev < 35) { p += 500; } else { p += 50; } } break; } case 160 + 15: { /* RF5_RAISE_DEAD */ break; } case 160 + 16: { /* Summoning is only as dangerous as the monster that is * actually summoned but the monsters that summon are a priority * to kill. PFE reduces danger from some evil summoned monsters * One Problem with GOI and Create Door is that the GOI reduces * the fear so much that the borg won't cast the Create Door, * eventhough it would be a good idea. */ /* S_KIN */ if (pfe) { p += (lev); p = p / spot_safe; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 3; p = p / spot_safe; } else if (borg_goi) { p = 0; } else { p += (lev) * 7; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 17: { /* S_HI_DEMON */ if (pfe) { p += (lev); p = p / spot_safe; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 6; p = p / spot_safe; } else if (borg_goi) { p = 0; } else { p += (lev) * 12; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 18: { /* RF5_S_MONSTER */ if (borg_goi || pfe || glyph || borg_create_door || borg_fighting_unique) p += 0; else { p += (lev) * 5; p = p / spot_safe; } break; } case 160 + 19: { /* RF5_S_MONSTERS */ if (borg_goi || pfe || glyph || borg_create_door || borg_fighting_unique) p += 0; else { p += (lev) * 7; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 20: { /* RF5_S_ANT */ if (borg_goi || pfe || glyph || borg_create_door || borg_fighting_unique) p += 0; else { p += (lev) * 5; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 21: { /* RF5_S_SPIDER */ if (borg_goi || pfe || glyph || borg_create_door || borg_fighting_unique) p += 0; else { p += (lev) * 5; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 22: { /* RF5_S_HOUND */ if (borg_goi || pfe || glyph || borg_create_door || borg_fighting_unique) p += 0; else { p += (lev) * 5; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 23: { /* RF5_S_HYDRA */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 2; p = p / spot_safe; } else { p += (lev) * 5; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 24: { /* RF5_S_ANGEL */ if (pfe || borg_fighting_unique) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 3; p = p / spot_safe; } else { p += (lev) * 7; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 25: { /* RF5_S_DEMON */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 3; p = p / spot_safe; } else { p += (lev) * 7; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 26: { /* RF5_S_UNDEAD */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 3; p = p / spot_safe; } else { p += (lev) * 7; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 27: { /* RF5_S_DRAGON */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 3; p = p / spot_safe; } else { p += (lev) * 7; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 28: { /* RF5_S_HI_UNDEAD */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 6; p = p / spot_safe; } else { p += (lev) * 12; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 29: { /* RF5_S_HI_DRAGON */ if (pfe) { p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 6; p = p / spot_safe; } else { p += (lev) * 12; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 30: { /* RF5_S_AMBERITES */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door || borg_fighting_unique) { p += (lev) * 6; p = p / spot_safe; } else { p += (lev) * 12; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } case 160 + 31: { /* RF5_S_UNIQUE */ if (pfe) { p += (lev); p = p / spot_safe; } else if (borg_goi) { p = 0; } else if (glyph || borg_create_door) { p += (lev) * 3; /* slightly reduced danger for unique */ p = p / spot_safe; } else { p += (lev) * 6; p = p / spot_safe; } /* reduce the fear if it is a unique */ if (FLAG(r_ptr, RF_UNIQUE)) p = p * 75 / 100; break; } } /* Notice damage */ p += z; /* Track the most dangerous spell */ if (p > n) n = p; /* Track the damage of all the spells, used in averaging */ total_dam += p; } /* Average damage of all the spells & compare to most dangerous spell */ av = total_dam / num; /* HACK- to accomdate for GOI and Create_Door */ borg_goi = true_borg_goi; /* * If the most dangerous spell is alot bigger than the average, * then return the dangerous one * * There is a problem when dealing with defence manuevers. * If the borg is considering casting a spell like Resistance * and the monster also has a non-resistable attack * (like Disenchant) then the damage returned will be for that * spell, since the danger of the others (like fire, cold) will * be greatly reduced by the proposed defence spell. The result * will be the borg will not cast the resistance spell even though * it may be a very good idea. * * Example: a monster has three breath attacks * (Fire, Ice, Disenchant) and each hits for 500 pts of damage. * The borg currently resists all three, so the danger would be 500. * If the borg were to use a Res Heat Potion that would decrease * the danger to: * * Fire: 333 * Ice: 500 * Disen: 500 * Now the Average is 444. Not really worth it, nominal change. * But if the resistance spell was both Fire and Ice, then it would be: * Fire: 333 * Ice: 333 * Disen: 500 * With an average of 388. Probably worth it, but the borg would * see that the Disen attack is a quite dangerous and would return * the result of 500. * * To fix this, the flag 'average' is added to the borg_danger() * to skip this check and return the average damage. If the flag * is FALSE then the formula below is SKIPPED and the value returned * with be the average. If the flag is TRUE, then the formula below * will be used to determine the returned value. Currently the * elemental resistance spells and PFE have the flag set as FALSE. */ if (!average) return (av); if ((n >= av * 15 / 10) || (n > bp_ptr->chp * 8 / 10)) { return (n); } else { /* Average Danger */ return (av); } } /* * Calculate the danger to a grid from a monster XXX XXX XXX * * Note that we are paranoid, especially about "monster speed", * since even if a monster is slower than us, it will occasionally * get one full turn to attack us. * * Note that we assume that monsters can walk through walls and * other monsters to get to the player. XXX XXX XXX * * This function ignores possibilities such as movement plus * spell attacks, physical attacks and spell attacks together, * and other similar situations. XXX XXX XXX * * Currently we assume that "sleeping" monsters are less dangerous * unless you get near them, which may wake them up. * * We attempt to take into account things like monsters which sometimes * "stumble", and monsters which only "sometimes" use powerful spells. */ int borg_danger_aux(int x, int y, int c, int i, bool average) { borg_kill *kill = &borg_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; int x9 = kill->x; int y9 = kill->y; int ax, ay, d; int q = 0, r, p, v1 = 0, v2 = 0; int glyph = 0; int fake_speed = bp_ptr->speed; int monster_speed = r_ptr->speed; int t, e; /* Paranoia */ if (!kill->r_idx) return (0); /* Distance components */ ax = (x9 > x) ? (x9 - x) : (x - x9); ay = (y9 > y) ? (y9 - y) : (y - y9); /* Distance */ d = MAX(ax, ay); /* Minimal distance */ if (d < 1) d = 1; /* Minimal distance */ if (d > 20) return (0); /* A very speedy borg will miscalculate danger of some monsters */ if (bp_ptr->speed >= 135) fake_speed = (borg_fighting_unique ? 120 : 125); /* Consider the character haste and slow monster spells */ if (borg_speed) fake_speed += 10; if (borg_slow_spell) monster_speed -= 10; /* Assume monsters are a little fast when you are low level */ if (bp_ptr->mhp < 20) monster_speed += 7; /* Player energy per game turn */ e = extract_energy[(fake_speed)]; /* Game turns per player move */ t = (100 + (e - 1)) / e; /* Monster energy per game turn */ e = extract_energy[monster_speed]; /* Monster moves */ q = c * ((t * e) / 10); /* Minimal energy */ /* allow partial hits when not caculating full possible damage */ if (borg_full_damage) q = (int)((q + 9) / 10) * 10; /** Danger from physical attacks **/ /* Physical attacks */ v1 = borg_danger_aux1(kill->r_idx); /* No attacks for some monsters */ if (FLAG(r_ptr, RF_NEVER_BLOW)) { v1 = 0; } /* No movement for some monsters */ if ((FLAG(r_ptr, RF_NEVER_MOVE)) && (d > 1)) { v1 = 0; } /* Hack -- Physical attacks require proximity */ /* If the monster is next to us and gets a partial hit, count it. */ if (q > 10 || d != 1) { if (q < (d * 10) && bp_ptr->lev > 20) { v1 = 0; } else if (q < (d * 10) && bp_ptr->lev <= 20) { /* reduce damage to 20% if we are weak */ v1 = (v1 * 2 / 10); } } /* multipliers yeild some trouble when I am weak */ if ((FLAG(r_ptr, RF_MULTIPLY)) && (bp_ptr->lev < 20)) { /* extra 50% */ v1 = v1 + (v1 * 15 / 10); } /* Friends yeild some trouble when I am weak */ if ((FLAG(r_ptr, RF_FRIENDS) || FLAG(r_ptr, RF_ESCORTS)) && (bp_ptr->lev < 20)) { if (bp_ptr->lev < 15) { /* extra 80% */ v1 = v1 + (v1 * 18 / 10); } else { /* extra 30% */ v1 = v1 + (v1 * 13 / 10); } } /* glyph of warding rune of protection reduction here * We should reduce the danger commensurate to the probability of the * monster breaking the glyph as defined by melee2.c */ if (borg_on_glyph) { v1 = 0; } if (track_glyph_num) { /* Check all existing glyphs */ for (glyph = 0; glyph < track_glyph_num; glyph++) { if ((track_glyph_y[glyph] == y) && (track_glyph_x[glyph] == x)) { /* Reduce the danger */ v1 = 0; } } } /* Reduce danger from sleeping monsters */ if ((kill->m_flags & MONST_ASLEEP) && (d > 1)) { /* Normal reduction of fear */ if (bp_ptr->lev >= 10) { v1 = v1 / d; } else { /* low clevel weaklings should still fear alot */ v1 = v1 * 8 / 10; } } /* Reduce danger from sleeping monsters with the sleep 2 spell */ if (borg_sleep_spell_ii) { if ((d == 1) && (!(kill->m_flags & MONST_ASLEEP)) && (!(FLAG(r_ptr, RF_NO_SLEEP))) && (!(FLAG(r_ptr, RF_UNIQUE))) && (r_ptr->level <= (bp_ptr->lev - 15))) { v1 = v1 / 3; } } /* Reduce danger from sleeping monsters with the sleep 1,3 spell */ if (borg_sleep_spell) { v1 = v1 / (d + 2); } /* Reduce danger from confused monsters */ if (kill->m_flags & MONST_CONFUSED) { v1 = v1 / 2; } if (kill->m_flags & MONST_STUN) { v1 = v1 * 10 / 13; } if (borg_confuse_spell) { v1 = v1 / 6; } /* Perceive a reduce danger from scared monsters */ if (borg_fear_mon_spell) { v1 = 0; } /* Tweak danger based on the "alertness" of the monster */ if (kill->m_flags & MONST_ASLEEP) { /* increase the danger for light sleepers */ int inc = r_ptr->sleep + 5; v1 = v1 + (v1 * inc / 100); } /* Danger */ if (v1) { /* Attacks after movement */ r = (q - ((d - 1) * 10)); /* XXX XXX XXX */ if (c > 1) { /* Hack -- stumble sometimes XXX XXX XXX */ if (FLAG(r_ptr, RF_RAND_25) || FLAG(r_ptr, RF_RAND_50)) r -= (r / 4); } /* Total danger */ v1 = v1 * r / 10; } /** Ranged Attacks **/ /* Never cast spells */ if (!r_ptr->freq_inate && !r_ptr->freq_spell) { v2 = 0; } /* Hack -- verify distance */ else if (distance(y9, x9, y, x) > MAX_RANGE) { v2 = 0; } /* Hack -- verify line of sight (both ways) */ else if (!borg_projectable(x9, y9, x, y) && !borg_projectable(x, y, x9, y9)) { v2 = 0; } /* Danger from spell attacks */ else { int chance; /* Spell attacks */ v2 = borg_danger_aux2(i, average); /* multipliers yeild some trouble when I am weak */ if ((FLAG(r_ptr, RF_MULTIPLY)) && (bp_ptr->lev < 20)) { v2 = v2 + (v2 * 12 / 10); } /* Friends yeild some trouble when I am weak */ if ((FLAG(r_ptr, RF_FRIENDS) || FLAG(r_ptr, RF_ESCORTS)) && (bp_ptr->lev < 20)) { v2 = v2 + (v2 * 12 / 10); } /* Reduce danger from sleeping monsters */ if ((kill->m_flags & MONST_ASLEEP) && (d > 1)) { /* weaklings and should still fear */ if (bp_ptr->lev >= 10) { v2 = v2 / d; } else { /* only subract 10% of the danger */ v2 = v2 * 9 / 10; } } /* Reduce danger from sleeping monsters with the sleep 2 spell */ if (borg_sleep_spell_ii) { if ((d == 1) && (!(kill->m_flags & MONST_ASLEEP)) && (!(FLAG(r_ptr, RF_NO_SLEEP))) && (!(FLAG(r_ptr, RF_UNIQUE))) && (r_ptr->level <= ((bp_ptr->lev < 15) ? bp_ptr->lev : (((bp_ptr->lev - 10) / 4) * 3) + 10))) { v2 = v2 / 3; } } /* Reduce danger from sleeping monsters with the sleep 1,3 spell */ if (borg_sleep_spell) { v2 = v2 / (d + 2); } /* Reduce danger from confused monsters */ if (kill->m_flags & MONST_CONFUSED) { v2 = v2 / 2; } /* Reduce danger from stunnned monsters */ if (kill->m_flags & MONST_STUN) { v2 = v2 * 10 / 13; } if (borg_confuse_spell) { v2 = v2 / 6; } /* Tweak danger based on the "alertness" of the monster */ if (kill->m_flags & MONST_ASLEEP) { /* increase the danger for light sleepers */ int inc = r_ptr->sleep + 5; v2 = v2 + (v2 * inc / 100); } if (!borg_full_damage) { /* reduce for frequency. */ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2; if (chance < 11) v2 = ((v2 * 4) / 10); else if (chance < 26) v2 = ((v2 * 6) / 10); else if (chance < 51) v2 = ((v2 * 8) / 10); } /* Danger */ if (v2) { /* Full power */ r = q; /* Total danger */ v2 = v2 * r / 10; } } /* Maximal danger */ p = MAX(v1, v2); /* No danger from friends or pets */ if (kill->m_flags & (MONST_FRIEND | MONST_PET)) p = 0; /* Result */ return (p); } /* * Hack -- Calculate the "danger" of the given grid. * * Currently based on the physical power of nearby monsters, as well * as the spell power of monsters which can target the given grid. * * This function is extremely expensive, mostly due to the number of * times it is called, and also to the fact that it calls its helper * functions about thirty times each per call. * * We need to do more intelligent processing with the "c" parameter, * since currently the Borg does not realize that backing into a * hallway is a good idea, since as far as he can tell, many of * the nearby monsters can "squeeze" into a single grid. * * Note that we also take account of the danger of the "region" in * which the grid is located, which allows us to apply some "fear" * of invisible monsters and things of that nature. * * Generally bool Average is TRUE. */ int borg_danger(int x, int y, int c, bool average) { int i, p = 1000; int grid_fear = 1000; map_block *mb_ptr; /* do twice. Once to get full damage and once to get partial. */ /* !FIX this is very slow. I need to find a better way of doing this */ /* perhaps I should calc both at the same time and pass back */ /* the right one. AJG */ /* Bounds checking */ if (map_in_bounds(x, y)) { mb_ptr = map_loc(x, y); /* Base danger (from fear) */ grid_fear = mb_ptr->fear * c; p = grid_fear; } /* Reduce this fear if GOI is up */ if (borg_goi) { p = p / 4; } borg_full_damage = TRUE; /* Examine all the monsters */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Collect danger from monster */ p += borg_danger_aux(x, y, c, i, average); } borg_full_damage = FALSE; /* * If I can't be killed in one round * (or severely wounded) use probablilities */ if (p < (avoidance * 85 / 100) && p != 0) { /* Base danger (from fear) */ p = grid_fear; /* Reduce this fear if GOI is up */ if (borg_goi) { p = p / 4; } /* Examine all the monsters */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Collect danger from monster */ p += borg_danger_aux(x, y, c, i, average); } } /* Return the danger */ return (p > 2000 ? 2000 : p); } /* * Helper function -- calculate "power" of equipment * Dynamic Calcs off */ static s32b borg_power_aux3(void) { int hold = 0; int damage = 0, dam = 0; int i; int cur_wgt = 0; int max_wgt = 0; s32b value = 0L; /* Heavily penalize various flags */ list_item temp, *l_ptr; /* Obtain the "hold" value (weight limit for weapons) */ hold = adj_str_hold[my_stat_ind[A_STR]]; /*** Analyze weapon ***/ l_ptr = look_up_equip_slot(EQUIP_WIELD); /* Examine current weapon */ if (l_ptr) { /* Calculate "average" damage per "normal" blow */ /* and assume we can enchant up to +8 if bp_ptr->lev > 25 */ damage = (l_ptr->dd * l_ptr->ds * 20L); /* Reward "damage" and increased blows per round */ value += damage * (bp_ptr->blows + 1); /* Reward "bonus to hit" */ if (l_ptr->to_h > 8 || bp_ptr->lev < 25) value += (bp_ptr->to_h + l_ptr->to_h) * 30L; else value += (bp_ptr->to_h + 8) * 30L; /* Reward "bonus to dam" */ value += (bp_ptr->to_d + l_ptr->to_d) * 30L; /* extra boost for deep dungeon */ if (bp_ptr->max_depth >= 75) { value += (bp_ptr->to_h + l_ptr->to_h) * 15L; value += l_ptr->dd * l_ptr->ds * 20L * 2 * bp_ptr->blows; } /* assume 2x base damage for x% of creatures */ dam = damage * 2 * bp_ptr->blows; if (FLAG(bp_ptr, TR_SLAY_ANIMAL)) value += (dam * 2) / 2; if (FLAG(bp_ptr, TR_BRAND_POIS)) value += (dam * 2) / 2; if (FLAG(bp_ptr, TR_SLAY_EVIL)) value += (dam * 7) / 2; /* assume 3x base damage for x% of creatures */ dam = damage * 3 * bp_ptr->blows; if (FLAG(bp_ptr, TR_SLAY_UNDEAD)) value += (dam * 5) / 2; if (FLAG(bp_ptr, TR_SLAY_DEMON)) value += (dam * 3) / 2; if ((FLAG(bp_ptr, TR_SLAY_DRAGON)) && (!(FLAG(bp_ptr, TR_KILL_DRAGON)))) value += (dam * 6) / 2; if (FLAG(bp_ptr, TR_SLAY_GIANT)) value += (dam * 4) / 2; if (FLAG(bp_ptr, TR_BRAND_ACID)) value += (dam * 4) / 2; if (FLAG(bp_ptr, TR_BRAND_ELEC)) value += (dam * 5) / 2; if (FLAG(bp_ptr, TR_BRAND_FIRE)) value += (dam * 3) / 2; if (FLAG(bp_ptr, TR_BRAND_COLD)) value += (dam * 3) / 2; if (FLAG(bp_ptr, TR_VAMPIRIC)) value += (dam * 3) / 2; if (FLAG(bp_ptr, TR_VORPAL)) value += (dam * 3) / 2; if (FLAG(bp_ptr, TR_CHAOTIC)) value += (dam * 12) / 10; /* SOrc and STroll get 1/2 of reward now */ if (FLAG(bp_ptr, TR_SLAY_ORC)) value += (dam * 1) / 2; if (FLAG(bp_ptr, TR_SLAY_TROLL)) value += (dam * 2) / 2; /* and the other 2/2 if SEvil not possesed */ if ((FLAG(bp_ptr, TR_SLAY_ORC)) && !(FLAG(bp_ptr, TR_SLAY_EVIL))) value += (dam * 1) / 2; if ((FLAG(bp_ptr, TR_SLAY_TROLL)) && !(FLAG(bp_ptr, TR_SLAY_EVIL))) value += (dam * 1) / 2; /* assume 5x base damage for x% of creatures */ dam = damage * 5 * bp_ptr->blows; if (FLAG(bp_ptr, TR_KILL_DRAGON)) value += (dam * 5) / 2; } else if (borg_class == CLASS_MONK) { /* Martial Artists */ int ma = MAX_MA - 1; const martial_arts *ma_ptr = &ma_blows[MAX_MA]; /* Calculate best Monk Attacks */ while (ma != 0) { ma_ptr = &ma_blows[ma]; /* Can do this attack */ if (bp_ptr->lev >= ma_ptr->min_level) break; /* Reduce the ma level and try again */ ma--; } /* Calculate "average" damage per "normal" blow */ damage = (ma_ptr->dd * ma_ptr->ds * 20L); /* Reward "damage" and increased blows per round */ value += damage * (bp_ptr->blows + 1); /* Reward "bonus to hit" */ value += bp_ptr->to_h * 30L; /* Reward "bonus to dam" */ value += bp_ptr->to_d * 30L; /* extra boost for deep dungeon */ if (bp_ptr->max_depth >= 75) { value += bp_ptr->to_h * 15L; value += ma_ptr->dd * ma_ptr->ds * 20L * 2 * bp_ptr->blows; } } /* Earthquakes... */ if (FLAG(bp_ptr, TR_IMPACT)) value += 5000L; /* Hack -- It is hard to hold a heavy weapon */ if (bp_ptr->status.hvy_weapon) value -= 50000L; /*** Analyze bow ***/ l_ptr = look_up_equip_slot(EQUIP_BOW); /* Examine current bow */ if (l_ptr) { /* Calculate "average" damage per "normal" shot (times 2) */ value += bp_ptr->b_max_dam * 20L; /* Reward "bonus to hit" */ if (l_ptr->to_h > 8 || bp_ptr->lev < 25) value += (bp_ptr->to_h + l_ptr->to_h) * 7L; else value += (bp_ptr->to_h + 8) * 7L; /* Hack -- It is hard to hold a heavy weapon */ if (hold < l_ptr->weight / 10) value -= 500000L; } /*** apw Analyze dragon armour ***/ l_ptr = look_up_equip_slot(EQUIP_BODY); /* Examine current armor */ if (l_ptr && (l_ptr->tval == TV_DRAG_ARMOR)) { switch (k_info[l_ptr->k_idx].sval) { case SV_DRAGON_BLACK: case SV_DRAGON_BLUE: case SV_DRAGON_WHITE: case SV_DRAGON_RED: { value += 1100; break; } case SV_DRAGON_GREEN: { value += 2750; break; } case SV_DRAGON_MULTIHUED: { value += 3250; break; } case SV_DRAGON_SHINING: case SV_DRAGON_LAW: case SV_DRAGON_BRONZE: case SV_DRAGON_GOLD: case SV_DRAGON_CHAOS: case SV_DRAGON_BALANCE: case SV_DRAGON_POWER: { value += 5150; } } } /*** Reward various things ***/ /* Hack -- Reward light radius */ value += 1000000 * MIN(bp_ptr->cur_lite, 3); value += 1000 * MIN_FLOOR(bp_ptr->cur_lite, 3, 10); /* Hack -- Reward for wearing a permanent light */ if (bp_ptr->britelite) { value += 5000; } /* if there is no permanent light */ else { /* Reward carrying a light item, so the borg can cast phlogiston on it */ if (look_up_equip_slot(EQUIP_LITE) && borg_has_realm(REALM_ARCANE)) value += 1000; } /* Hack -- Reward speed */ if (bp_ptr->speed >= 150) value += (((bp_ptr->speed - 120) * 1000L) + 185000L); if (bp_ptr->speed >= 145 && bp_ptr->speed <= 149) value += (((bp_ptr->speed - 120) * 1000L) + 180000L); if (bp_ptr->speed >= 140 && bp_ptr->speed <= 144) value += (((bp_ptr->speed - 120) * 1000L) + 175000L); if (bp_ptr->speed >= 135 && bp_ptr->speed <= 139) value += (((bp_ptr->speed - 120) * 1000L) + 165000L); if (bp_ptr->speed >= 130 && bp_ptr->speed <= 134) value += (((bp_ptr->speed - 120) * 1000L) + 150000L); if (bp_ptr->speed >= 125 && bp_ptr->speed <= 129) value += (((bp_ptr->speed - 110) * 1000L) + 125000L); if (bp_ptr->speed >= 120 && bp_ptr->speed <= 124) value += (((bp_ptr->speed - 110) * 1000L) + 100000L); if (bp_ptr->speed >= 115 && bp_ptr->speed <= 119) value += (((bp_ptr->speed - 110) * 1000L) + 75000L); if (bp_ptr->speed >= 110 && bp_ptr->speed <= 114) value += (((bp_ptr->speed - 110) * 1000L) + 55000L); else value += (((bp_ptr->speed - 110) * 2500L)); /* Hack -- Reward strength bonus */ value += (my_stat_ind[A_STR] * 100L); /* Hack -- Reward intelligence bonus */ if (bp_ptr->intmana && (my_stat_ind[A_INT] <= 37)) { value += (my_stat_ind[A_INT] * 500L); /* Bonus for sp. */ value += ((adj_mag_mana[my_stat_ind[A_INT]] * bp_ptr->lev) / 2) * 155L; /* bonus for fail rate */ value += adj_mag_stat[my_stat_ind[A_INT]] * 1670L; /* mage should try to get min fail to 0 */ if (borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE) { /* Bonus for mages to in order to keep GOI fail rate down */ if (borg_spell_legal(REALM_SORCERY, 3, 7) || borg_spell_legal(REALM_LIFE, 3, 7)) { value += my_stat_ind[A_INT] * 35000L; } /* other fail rates */ if (adj_mag_fail[my_stat_ind[A_INT]] < 1) value += 90000L; } } /* Hack -- Reward wisdom bonus */ if (bp_ptr->wismana && (my_stat_ind[A_WIS] <= 37)) { value += (my_stat_ind[A_WIS] * 200L); /* Bonus for sp. */ value += ((adj_mag_mana[my_stat_ind[A_WIS]] * bp_ptr->lev) / 2) * 150L; /* bonus for fail rate */ value += adj_mag_stat[my_stat_ind[A_WIS]] * 1000L; /* priest should try to get min fail to 0 */ if (borg_class == CLASS_PRIEST || borg_class == CLASS_MINDCRAFTER) { /* Bonus for priests to in order to keep Holy Word fail rate down */ if (borg_spell_legal(REALM_LIFE, 2, 6)) value += my_stat_ind[A_WIS] * 35000L; if (adj_mag_fail[my_stat_ind[A_WIS]] < 1) value += 70000L; } } /* Dexterity Bonus --good for attacking and ac */ if (my_stat_ind[A_DEX] <= 37) { /* Hack -- Reward bonus */ value += (my_stat_ind[A_DEX] * 120L); } /* Constitution Bonus */ if (my_stat_ind[A_CON] <= 37) { int bonus_hp = (((adj_con_mhp[my_stat_ind[A_CON]] - 128) * bp_ptr->max_lev) / 2); value += (my_stat_ind[A_CON] * 150L); /* Hack -- Reward hp bonus */ /* This is a bit wierd because we are not really giving a bonus for */ /* what hp you have, but for the 'bonus' hp you get */ /* getting over 500hp is very important. */ if (bonus_hp < 500) value += bonus_hp * 350L; else value += (bonus_hp - 500) * 100L + (350L * 500); } /* Hack -- Reward charisma bonus up to level 25 */ if (bp_ptr->lev < 25) value += (my_stat_ind[A_CHR] * 2L); /* HACK - a small bonus for adding to stats even above max. */ /* This will allow us to swap a ring of int +6 for */ /* our ring of int +2 even though we are at max int because */ /* we are wielding a weapon that has +4 int */ /* later it might be nice to swap to a weapon that does not */ /* have an int bonus */ for (i = 0; i < 6; i++) value += my_stat_add[i]; /* Reward the extra mana */ if (borg_class != CLASS_WARRIOR) value += (bp_ptr->mana_bonus * 987); /* Hack -- tiny rewards */ value += (bp_ptr->skill_dis * 2L); value += (bp_ptr->skill_dev * 25L); value += (bp_ptr->skill_sav * 25L); /* perfect saves are very nice */ if (bp_ptr->skill_sav > 99) value += 10000; value += (bp_ptr->skill_stl * 2L); value += (bp_ptr->skill_sns * 1L); value += (bp_ptr->skill_fos * 1L); value += (bp_ptr->skill_thn * 5L); value += (bp_ptr->skill_thb * 35L); value += (bp_ptr->skill_tht * 2L); value += (bp_ptr->skill_dig * 2L); /*** Reward current flags ***/ /* Various flags */ if (FLAG(bp_ptr, TR_SLOW_DIGEST)) value += 10L; if (FLAG(bp_ptr, TR_FEATHER)) { /* Feather Fall if low level is nice */ if (bp_ptr->max_depth < 20) value += 500L; else value += 50; } if (FLAG(bp_ptr, TR_SEE_INVIS)) { /* See invisible is less important if you have ESP */ if (FLAG(bp_ptr, TR_TELEPATHY)) value += 2000L; else value += 5000L; } if (FLAG(bp_ptr, TR_FREE_ACT)) value += 10000L; if (FLAG(bp_ptr, TR_HOLD_LIFE)) { /* after you max out you are pretty safe from drainers. */ if (bp_ptr->max_lev < 50) value += 2000L; else value += 200L; } if (FLAG(bp_ptr, TR_REGEN)) value += 2000L; if (FLAG(bp_ptr, TR_TELEPATHY)) value += 80000L; /* Immunity flags */ if (FLAG(bp_ptr, TR_IM_COLD)) value += 10000L; if (FLAG(bp_ptr, TR_IM_ELEC)) value += 10000L; if (FLAG(bp_ptr, TR_IM_FIRE)) value += 16000L; if (FLAG(bp_ptr, TR_IM_ACID)) value += 10000L; /* Immunity implies resistance too */ if (FLAG(bp_ptr, TR_IM_COLD)) SET_FLAG(bp_ptr, TR_RES_COLD); if (FLAG(bp_ptr, TR_IM_ELEC)) SET_FLAG(bp_ptr, TR_RES_ELEC); if (FLAG(bp_ptr, TR_IM_ACID)) SET_FLAG(bp_ptr, TR_RES_ACID); if (FLAG(bp_ptr, TR_IM_FIRE)) SET_FLAG(bp_ptr, TR_RES_FIRE); if (FLAG(bp_ptr, TR_IM_POIS)) SET_FLAG(bp_ptr, TR_RES_POIS); if (FLAG(bp_ptr, TR_IM_LITE)) SET_FLAG(bp_ptr, TR_RES_LITE); if (FLAG(bp_ptr, TR_IM_DARK)) SET_FLAG(bp_ptr, TR_RES_DARK); /* Warriors need a slight boost for this */ if ((borg_class == CLASS_WARRIOR || borg_class == CLASS_CHAOS_WARRIOR) && (FLAG(bp_ptr, TR_RES_FEAR))) value += 2000L; if (FLAG(bp_ptr, TR_RES_FEAR)) value += 2000L; /* Resistance flags */ if (FLAG(bp_ptr, TR_RES_COLD)) value += 5000L; if (FLAG(bp_ptr, TR_RES_ELEC)) value += 5000L; if (FLAG(bp_ptr, TR_RES_ACID)) value += 5000L; if (FLAG(bp_ptr, TR_RES_FIRE)) value += 8000L; /* extra bonus for getting all basic resist */ if ((FLAG(bp_ptr, TR_RES_FIRE)) && (FLAG(bp_ptr, TR_RES_ACID)) && (FLAG(bp_ptr, TR_RES_ELEC)) && (FLAG(bp_ptr, TR_RES_COLD))) value += 10000L; if (FLAG(bp_ptr, TR_RES_POIS)) value += 20000L; if (FLAG(bp_ptr, TR_RES_SOUND)) value += 3500L; if (FLAG(bp_ptr, TR_RES_LITE)) value += 800L; if (FLAG(bp_ptr, TR_RES_DARK)) value += 800L; if (FLAG(bp_ptr, TR_RES_CHAOS)) value += 5000L; /* this is way boosted to avoid carrying stuff you don't need */ if (FLAG(bp_ptr, TR_RES_CONF)) value += 80000L; if (FLAG(bp_ptr, TR_RES_DISEN)) value += 5000L; if (FLAG(bp_ptr, TR_RES_SHARDS)) value += 100L; if (FLAG(bp_ptr, TR_RES_NEXUS)) value += 100L; if (FLAG(bp_ptr, TR_RES_BLIND)) value += 5000L; if (FLAG(bp_ptr, TR_RES_NETHER)) value += 5500L; if (FLAG(bp_ptr, TR_REFLECT)) value += 2000L; /* Aura's */ if (FLAG(bp_ptr, TR_SH_FIRE)) value += 2000L; if (FLAG(bp_ptr, TR_SH_ELEC)) value += 2000L; if (FLAG(bp_ptr, TR_SH_COLD)) value += 2000L; if (FLAG(bp_ptr, TR_SH_ACID)) value += 2000L; /* Sustain flags */ if (bp_ptr->sust[A_STR]) value += 50L; if (bp_ptr->sust[A_INT]) value += 50L; if (bp_ptr->sust[A_WIS]) value += 50L; if (bp_ptr->sust[A_CON]) value += 50L; if (bp_ptr->sust[A_DEX]) value += 50L; /* boost for getting them all */ if (bp_ptr->sust[A_STR] && bp_ptr->sust[A_INT] && bp_ptr->sust[A_WIS] && bp_ptr->sust[A_DEX] && bp_ptr->sust[A_CON]) value += 1000L; /*** XXX XXX XXX Reward "necessary" flags ***/ /* Mega-Hack -- See invisible (level 10) */ if (((FLAG(bp_ptr, TR_SEE_INVIS)) || (FLAG(bp_ptr, TR_TELEPATHY))) && (bp_ptr->max_depth + 1 >= 10)) value += 100000L; /* Mega-Hack -- Free action (level 20) */ if ((FLAG(bp_ptr, TR_FREE_ACT)) && (bp_ptr->max_depth + 1 >= 20)) value += 100000L; /* Mega-Hack -- resists (level 25) */ if ((FLAG(bp_ptr, TR_RES_FIRE)) && (bp_ptr->max_depth + 1 >= 25)) value += 100000L; /* Mega-Hack -- resists (level 40) */ if ((FLAG(bp_ptr, TR_RES_POIS)) && (bp_ptr->max_depth + 1 >= 40)) value += 100000L; if ((FLAG(bp_ptr, TR_RES_ELEC)) && (bp_ptr->max_depth + 1 >= 40)) value += 100000L; if ((FLAG(bp_ptr, TR_RES_ACID)) && (bp_ptr->max_depth + 1 >= 40)) value += 100000L; if ((FLAG(bp_ptr, TR_RES_COLD)) && (bp_ptr->max_depth + 1 >= 40)) value += 100000L; /* APW Mega-Hack -- Speed / Hold Life (level 46) and maxed out */ if (((FLAG(bp_ptr, TR_HOLD_LIFE)) && (bp_ptr->max_depth + 1 >= 46) && (bp_ptr->max_lev < 50))) value += 100000L; if ((bp_ptr->speed >= 115) && (bp_ptr->max_depth + 1 >= 46)) value += 100000L; if ((FLAG(bp_ptr, TR_RES_CONF)) && (bp_ptr->max_depth + 1 >= 46)) value += 100000L; /* Mega-Hack -- resist Nether is -very- nice to have at level 50 */ if ((FLAG(bp_ptr, TR_RES_NETHER)) && (bp_ptr->max_depth + 1 >= 50)) value += 55000L; /* Mega-Hack -- resist Sound to avoid being KO'd */ if ((FLAG(bp_ptr, TR_RES_SOUND)) && (bp_ptr->max_depth + 1 >= 50)) value += 100000L; /* Mega-Hack -- resists & Telepathy (level 55) */ if ((FLAG(bp_ptr, TR_RES_BLIND)) && (bp_ptr->max_depth + 1 >= 55)) value += 100000L; if ((FLAG(bp_ptr, TR_TELEPATHY)) && (bp_ptr->max_depth + 1 >= 55)) value += 100000L; if ((FLAG(bp_ptr, TR_RES_NETHER)) && (bp_ptr->max_depth + 1 >= 60)) value += 55000L; /* Mega-Hack -- resists & +10 speed (level 60) */ if ((FLAG(bp_ptr, TR_RES_CHAOS)) && (bp_ptr->max_depth + 1 >= 60)) value += 104000L; if ((FLAG(bp_ptr, TR_RES_DISEN)) && (bp_ptr->max_depth + 1 >= 60)) value += 90000L; if ((bp_ptr->speed >= 120) && (bp_ptr->max_depth + 1 >= 60)) value += 100000L; /* Must have +20 speed (level 80) */ if ((bp_ptr->speed >= 130) && (bp_ptr->max_depth + 1 >= 80)) value += 100000L; /* Not Req, but a good idea: * Extra boost to Nether deeper down * RDark for deeper uniques * Good to have +30 speed */ if ((FLAG(bp_ptr, TR_RES_NETHER)) && (bp_ptr->max_depth + 1 >= 80)) value += 15000L; if ((FLAG(bp_ptr, TR_RES_DARK)) && (bp_ptr->max_depth + 1 >= 80)) value += 25000L; if ((bp_ptr->speed >= 140) && (bp_ptr->max_depth + 1 >= 80) && borg_class == CLASS_WARRIOR) value += 100000L; /*** Reward powerful armor ***/ value += 200 * MIN(bp_ptr->ac, 15); value += 150 * MIN_FLOOR(bp_ptr->ac, 15, 75); value += 50 * MIN_FLOOR(bp_ptr->ac, 75, 200); /*** Penalize various things ***/ /* Hack the flags so that they can be tested for bad curses */ for (i = 0; i < 4; i++) temp.kn_flags[i] = bp_ptr->flags[i]; /* If there is a bad flag it will cost the borg big */ if (borg_test_bad_curse(&temp)) value -= 1000000L; /* Slightly penalize some flags */ if (FLAG(bp_ptr, TR_AGGRAVATE)) value -= 5000L; if (FLAG(bp_ptr, TR_TELEPORT)) value -= 1000L; if (FLAG(bp_ptr, TR_HEAVY_CURSE)) value -= 5000L; /* Penalize vulnerability to light */ if (FLAG(bp_ptr, TR_HURT_LITE) && !FLAG(bp_ptr, TR_IM_LITE) && !FLAG(bp_ptr, TR_RES_LITE)) value -= 1000000; /* Penalize vulnerability to DARK */ if (FLAG(bp_ptr, TR_HURT_DARK) && !FLAG(bp_ptr, TR_IM_DARK)) { /* Not for high levels, big breathers are too painfull */ if (bp_ptr->lev > 40) value -= 1000000L; value -= 2000; /* With res_DARK it is more acceptable */ if (FLAG(bp_ptr, TR_RES_DARK)) value += 1000; } /* Penalize vulnerability to cold */ if (FLAG(bp_ptr, TR_HURT_COLD) && !FLAG(bp_ptr, TR_IM_COLD)) { /* Not for high levels, big breathers are too painfull */ if (bp_ptr->lev > 40) value -= 1000000L; value -= 2000; /* With res_cold it is more acceptable */ if (FLAG(bp_ptr, TR_RES_COLD)) value += 1000; } /* Penalize vulnerability to fire */ if (FLAG(bp_ptr, TR_HURT_FIRE) && !FLAG(bp_ptr, TR_IM_FIRE)) { /* Not for high levels, big breathers are too painfull */ if (bp_ptr->lev > 40) value -= 1000000L; /* basic penalty */ value -= 2000; /* With res_FIRE it is more acceptable */ if (FLAG(bp_ptr, TR_RES_FIRE)) value += 1000; } /* Penalize vulnerability to electricity */ if (FLAG(bp_ptr, TR_HURT_ELEC) && !FLAG(bp_ptr, TR_IM_ELEC)) { /* Not for high levels, big breathers are too painfull */ if (bp_ptr->lev > 40) value -= 1000000L; /* basic penalty */ value -= 2000; /* With res_ELEC it is more acceptable */ if (FLAG(bp_ptr, TR_RES_ELEC)) value += 1000; } /* Penalize vulnerability to acid */ if (FLAG(bp_ptr, TR_HURT_ACID) && !FLAG(bp_ptr, TR_IM_ACID)) { /* Not for high levels, big breathers are too painfull */ if (bp_ptr->lev > 40) value -= 1000000L; /* basic penalty */ value -= 2000; /* With res_ACID it is more acceptable */ if (FLAG(bp_ptr, TR_RES_ACID)) value += 1000; } /*** Penalize armor weight ***/ if (my_stat_ind[A_STR] < 15) { l_ptr = look_up_equip_slot(EQUIP_BODY); if (l_ptr && (l_ptr->weight > 200)) value -= (l_ptr->weight - 200) * 15; l_ptr = look_up_equip_slot(EQUIP_HEAD); if (l_ptr && (l_ptr->weight > 30)) value -= 250; l_ptr = look_up_equip_slot(EQUIP_ARM); if (l_ptr && (l_ptr->weight > 10)) value -= 250; l_ptr = look_up_equip_slot(EQUIP_FEET); if (l_ptr && (l_ptr->weight > 50)) value -= 250; } /* Compute the total armor weight */ for (i = EQUIP_BODY; i <= EQUIP_FEET; i++) { l_ptr = look_up_equip_slot(i); if (l_ptr) cur_wgt += l_ptr->weight; } /* Determine the weight allowance */ max_wgt = mp_ptr->spell_weight; /* Hack -- heavy armor hurts magic */ if (bp_ptr->intmana && (((cur_wgt - max_wgt) / 10) > 0) && ((adj_mag_mana[my_stat_ind[A_INT]] * bp_ptr->lev) / 2) < 150) { /* Mega-Hack -- Penalize heavy armor which hurts mana */ value -= (((cur_wgt - max_wgt) / 10) * 3600L); } /*** Penalize bad magic ***/ /* Hack -- most gloves hurt magic for spell-casters */ if (bp_ptr->intmana) { l_ptr = look_up_equip_slot(EQUIP_HANDS); /* Penalize non-usable gloves */ if (l_ptr && bp_ptr->msp < 300 && !KN_FLAG(l_ptr, TR_FREE_ACT) && !(KN_FLAG(l_ptr, TR_DEX) && (l_ptr->pval > 0))) { /* Hack -- Major penalty */ value -= 275000L; } } /* apw Hack -- most edged weapons hurt magic for priests */ if (borg_class == CLASS_PRIEST) { l_ptr = look_up_equip_slot(EQUIP_WIELD); /* Penalize non-blessed edged weapons */ if (l_ptr && (((l_ptr->tval == TV_SWORD) || (l_ptr->tval == TV_POLEARM)) && !KN_FLAG(l_ptr, TR_BLESSED))) { /* Hack -- Major penalty */ value -= 75000L; } } /* Reward for wielded artifacts with multiple high resists */ for (i = 0; i < equip_num; i++) { int multibonus = 0; l_ptr = look_up_equip_slot(i); /* Skip empty items */ if (!l_ptr) continue; /* Don't reward rings twice */ if (i == EQUIP_LEFT) { list_item *q_ptr = look_up_equip_slot(EQUIP_RIGHT); /* If the rings are the same */ if (q_ptr && k_info[l_ptr->k_idx].sval == k_info[q_ptr->k_idx].sval) { /* skip a ring */ continue; } } /* * It is good to have one item with multiple high resists. But * the various races all have their bonuses. So a ring of Light and * Dark should not get a multibonus when it is worn by Elf or Half-Ogre */ multibonus = KN_FLAG(l_ptr, TR_RES_NEXUS) + KN_FLAG(l_ptr, TR_IM_LITE) + KN_FLAG(l_ptr, TR_IM_FIRE) + KN_FLAG(l_ptr, TR_IM_COLD) + KN_FLAG(l_ptr, TR_IM_ELEC); /* Vampires get Immunity to Dark */ if (KN_FLAG(l_ptr, TR_IM_DARK) && borg_race != RACE_VAMPIRE) multibonus += 1; /* Zombies get resist Nether */ if (KN_FLAG(l_ptr, TR_RES_NETHER) && borg_race != RACE_GHOUL && borg_race != RACE_ZOMBIE && borg_race != RACE_SPECTRE && borg_race != RACE_VAMPIRE) multibonus += 1; /* Yeeks get Immunity to Acid */ if (KN_FLAG(l_ptr, TR_IM_ACID) && borg_race != RACE_YEEK) multibonus += 1; /* Golems get Immunity to Poison */ if (KN_FLAG(l_ptr, TR_IM_POIS) && borg_race != RACE_GHOUL && borg_race != RACE_GOLEM && borg_race != RACE_ZOMBIE && borg_race != RACE_VAMPIRE && borg_race != RACE_SPECTRE && borg_race != RACE_SKELETON) multibonus += 1; /* Nibelungs get resist Disenchant */ if (KN_FLAG(l_ptr, TR_RES_DISEN) && borg_race != RACE_NIBELUNG) multibonus += 1; /* Kobolds get resist Poison */ if (KN_FLAG(l_ptr, TR_RES_POIS) && borg_race != RACE_KOBOLD && borg_race != RACE_DRACONIAN) multibonus += 1; /* Cyclopi get resist Sound */ if (KN_FLAG(l_ptr, TR_RES_SOUND) && borg_race != RACE_BEASTMAN && borg_race != RACE_CYCLOPS) multibonus += 1; /* Half Giants get resist Shards */ if (KN_FLAG(l_ptr, TR_RES_SHARDS) && borg_race != RACE_HALF_GIANT && borg_race != RACE_SKELETON) multibonus += 1; /* * Hack. Not Light and Dark because shadow cloaks or rings of light * and dark get too much of a boost */ if (KN_FLAG(l_ptr, TR_RES_DARK) && !KN_FLAG(l_ptr, TR_RES_LITE) && borg_race != RACE_HALF_OGRE && borg_race != RACE_NIBELUNG && borg_race != RACE_VAMPIRE && borg_race != RACE_GHOUL && borg_race != RACE_DARK_ELF) multibonus += 1; /* Elves get resist Lite */ if (KN_FLAG(l_ptr, TR_RES_LITE) && !KN_FLAG(l_ptr, TR_RES_DARK) && borg_race != RACE_ELF && borg_race != RACE_SPRITE && borg_race != RACE_HIGH_ELF) multibonus += 1; /* Dwarves get resist Blind */ if (KN_FLAG(l_ptr, TR_RES_BLIND) && borg_race != RACE_DWARF) multibonus += 1; /* Mindcrafter get resist Conf */ if (KN_FLAG(l_ptr, TR_RES_CONF) && borg_class != CLASS_MINDCRAFTER && borg_race != RACE_BEASTMAN && borg_race != RACE_KLACKON) multibonus += 1; /* Chaos-warriors and Half Titan get resist Chaos */ if (KN_FLAG(l_ptr, TR_RES_CHAOS) && borg_class != CLASS_CHAOS_WARRIOR && borg_race != RACE_HALF_TITAN) multibonus += 1; value += 1500 * ((multibonus < 2) ? 0 : multibonus); } /* Result */ return (value); } /* * Helper function -- calculate power of inventory * Items can appear multiple times. This is to allow that item to be destroyed. * Use MIN_FLOOR to start counting from higher numbers of items onwards. * So * value += 2000 * MIN(amt_book, 2); * value += 500 * MIN_FLOOR(amt_book, 2, 3); * means that the borg counts 2000 per book for the first two books and 500 for * the third. If the borg needs to destroy items it might pick the third book * If the item is also collected at home be sure that the values for the home * and the inv accomplish what you want. */ static s32b borg_power_aux4(void) { int book, realm; int i, max_carry; list_item *l_ptr; s32b value = 0L; /*** Basic abilities ***/ /* Reward collecting fuel, */ value += 6000 * MIN(bp_ptr->able.fuel, 3); value += 600 * MIN_FLOOR(bp_ptr->able.fuel, 3, 7); /* Get the current light source */ l_ptr = look_up_equip_slot(EQUIP_LITE); if (!l_ptr) { /* Reward collecting a light */ value += 30000 * MIN(amt_lantern + amt_torch, 1); } else { /* If the borg wields a torch */ if (k_info[l_ptr->k_idx].sval == SV_LITE_TORCH) { /* reward carrying a lantern when you don't use it */ value += 500 * MIN(amt_lantern, 1); /* Prefer torches */ value += 50 * MIN(amt_torch, 7); /* * The flasks acts as molotov cocktails, but they can't * outshine the torches for value, because they are fuel too */ if (bp_ptr->lev < 15) value += 5 * MIN(amt_flask, 20); } /* If the borg wields a lantern */ if (k_info[l_ptr->k_idx].sval == SV_LITE_LANTERN) { /* Prefer flasks/lanterns to torches */ value += 50 * MIN(amt_lantern + amt_flask, 7); /* Keep some more flasks as molotov cocktails */ if (bp_ptr->lev < 15) value += 50 * MIN_FLOOR(amt_flask, 7, 20); } } /* Reward Food */ /* If you burn more food */ if ((FLAG(bp_ptr, TR_REGEN)) && !(FLAG(bp_ptr, TR_SLOW_DIGEST))) { /* take more food */ max_carry = 15; } else max_carry = 10; /* if hungry, food is THE top priority */ if ((bp_ptr->status.hungry || bp_ptr->status.weak) && bp_ptr->food) value += 100000; /* If you can't digest food */ if (FLAG(bp_ptr, TR_CANT_EAT)) { /* Take some scrolls along */ value += 10000 * MIN(amt_food_scroll, 5); value += 200 * MIN_FLOOR(amt_food_scroll, 5, max_carry); } /* If the borg can digest food */ else { /* Take some food along */ value += 10000 * MIN(bp_ptr->food, 5); value += 200 * MIN_FLOOR(bp_ptr->food, 5, max_carry); /* Prefer to buy scrolls over rations */ value += 30 * MIN(amt_food_scroll, max_carry); /* Take small foodstuffs along if the borg is low on food */ value += 1000 * MIN_FLOOR(amt_food_lowcal, 0, 3 * (5 - bp_ptr->food)); value += 50 * MIN_FLOOR(amt_food_lowcal, 0, 3 * (max_carry - bp_ptr->food)); } /* Reward throwing potions of poison for low level borgs */ if (bp_ptr->lev < 16) value += 50 * MIN(bp_ptr->able.poison, 20); /* Reward potions you can throw for damage */ value += bp_ptr->able.death * 200; /* Reward scrolls you can read for damage */ value += bp_ptr->able.logrus * 200; /* Reward Cure Poison and Cuts */ if ((bp_ptr->status.cut || bp_ptr->status.poisoned) && bp_ptr->able.ccw) value += 100000; if ((bp_ptr->status.cut || bp_ptr->status.poisoned) && bp_ptr->able.heal) value += 50000; if ((bp_ptr->status.cut || bp_ptr->status.poisoned) && bp_ptr->able.csw) value += 25000; if (bp_ptr->status.poisoned && bp_ptr->able.cure_pois) value += 15000; if (bp_ptr->status.poisoned && amt_slow_poison) value += 5000; /* Reward potion of curing when the borg is hallucinating */ if (bp_ptr->status.image && amt_pot_curing) value += 100000; /* Reward Resistance Potions for Warriors */ if (borg_class == CLASS_WARRIOR) { value += 250 * MIN(bp_ptr->able.res_heat, 5); value += 250 * MIN(bp_ptr->able.res_cold, 5); } /* It is counted as res_heas & cold too */ value += 250 * MIN(bp_ptr->able.res_all, 5); /* Reward ident */ value += 6000 * MIN(bp_ptr->able.id, 10); value += 600 * MIN_FLOOR(bp_ptr->able.id, 10, 25); /* Try to carry 3 rods if there is no identify spell available */ if (bp_ptr->able.id >= 100) value += 6 * MIN(bp_ptr->able.id, 3 * 100); /* Don't start with these before you are likely to need them */ if (bp_ptr->lev > 20) { /* *identify* scrolls */ value += 5000 * MIN(bp_ptr->able.star_id, 5); value += 500 * MIN_FLOOR(bp_ptr->able.star_id, 5, 10); } if (bp_ptr->lev > 40) { /* Reward Glyph- Rune of Protection- carry lots of these */ value += 10000 * MIN(bp_ptr->able.glyph, 10); value += 2000 * MIN_FLOOR(bp_ptr->able.glyph, 10, 25); } /* Reward recall */ value += 50000 * MIN(bp_ptr->recall, 1); value += 2000 * MIN_FLOOR(bp_ptr->recall, 1, 5); value += 800 * MIN_FLOOR(bp_ptr->recall, 5, 7); /* first phase door is very important */ value += 50000 * MIN(bp_ptr->able.phase, 1); value += 2000 * MIN_FLOOR(bp_ptr->able.phase, 1, 5); value += 800 * MIN_FLOOR(bp_ptr->able.phase, 5, 15); /* Reward escape */ value += 10000 * MIN(bp_ptr->able.escape, 5); /* If the borg is a grown up */ if (bp_ptr->lev > 30) { /* Give him more escapes */ value += 10000 * MIN_FLOOR(bp_ptr->able.escape, 5, 15); /* Reward Teleport Level scrolls */ value += 5000 * MIN(bp_ptr->able.teleport_level, 5); } /* Reward teleport */ value += 10000 * MIN(bp_ptr->able.teleport, 10); /* reset quantity */ max_carry = 0; /* If the borg has lots of hitpoints */ if (bp_ptr->mhp > 800) { /* If the borg is big and up to dangerous things */ if (bp_ptr->lev == 50) { /* Carry more */ max_carry = 10; } else { /* Carry a few */ max_carry = 2; } /* Carry some big healers: Potion of *Healing* or Life */ value += 10000 * MIN(bp_ptr->able.easy_heal, max_carry); /* Prefer to take *healing* over life */ value += 50 * MIN(amt_star_heal, max_carry); } /* If the borg has a reliable healing spell */ if (borg_spell_legal_fail(REALM_LIFE, 3, 4, 5) || borg_spell_legal_fail(REALM_LIFE, 1, 6, 5) || borg_spell_legal_fail(REALM_NATURE, 1, 7, 5)) { /* Still take some potions along */ max_carry = 5; } /* This borg needs potions to heal */ else { /* Take some more */ max_carry = 10; } /* Reward healing */ value += 8000 * MIN(bp_ptr->able.heal, max_carry); /* Rods of Healing are preferred to potions */ value += 50 * MIN(amt_rod_heal, max_carry); /* Restore Mana */ if (bp_ptr->msp > 100) { /* reward carrying potions/staffs of mana to use */ value += 4000 * MIN(bp_ptr->able.mana, 10); value += 4000 * MIN(bp_ptr->able.staff_magi, 10); } /* Reward cure critical. Heavy reward on first 10 */ value += 5000 * MIN(bp_ptr->able.ccw, 10); /* potions of curing and of ccw are basically the same: prefer curing */ value += 50 * MIN(amt_pot_curing, 10); /* Reward cure serious relative to how many cure crits the borg has */ value += 1500 * MIN_FLOOR(bp_ptr->able.csw, 0, 10 - bp_ptr->able.ccw); value += 1200 * MIN_FLOOR(bp_ptr->able.clw, 0, 10 - bp_ptr->able.ccw - bp_ptr->able.csw); /* If the borg has no confucius resist */ if (!FLAG(bp_ptr, TR_RES_CONF)) { /* Reward cure confusion */ value += 2000 * MIN(bp_ptr->able.cure_conf, 2); value += 200 * MIN_FLOOR(bp_ptr->able.cure_conf, 2, 5); } /* If the borg has no blindness resist */ if (!FLAG(bp_ptr, TR_RES_BLIND)) { /* Reward cure blindness */ value += 3000 * MIN(bp_ptr->able.cure_blind, 2); value += 300 * MIN_FLOOR(bp_ptr->able.cure_blind, 2, 5); } /* If the borg has no poison resist */ if (!FLAG(bp_ptr, TR_RES_POIS)) { /* Reward cure poison */ value += 2500 * MIN(bp_ptr->able.cure_pois, 2); value += 250 * MIN_FLOOR(bp_ptr->able.cure_pois, 2, 5); } /*** Detection ***/ /* Reward detect trap */ value += 4000 * MIN(bp_ptr->able.det_trap, 1); /* Reward detect door */ value += 2000 * MIN(bp_ptr->able.det_door, 1); /* Reward detect evil */ if (!FLAG(bp_ptr, TR_TELEPATHY)) { value += 1000 * MIN(bp_ptr->able.det_evil, 1); } /* Reward magic mapping */ value += 4000 * MIN(bp_ptr->able.magic_map, 1); /* Try to carry 3 rods if there is no magic_map spell available */ if (bp_ptr->able.magic_map >= 100) { value += 6 * MIN(bp_ptr->able.magic_map, 3 * 100); } /* Reward room lites */ value += 600 * MIN(bp_ptr->able.lite, 10); value += 60 * MIN_FLOOR(bp_ptr->able.lite, 10, 25); /* Try to carry 3 rods if there is no light spell available */ if (bp_ptr->able.lite >= 100) value += 6 * MIN(bp_ptr->able.lite, 3 * 100); /* Stuff to use against the the Serpent */ if (bp_ptr->max_depth >= 98) { /* Genocide scrolls */ value += 10000 * MIN(bp_ptr->able.genocide, 10); value += 2000 * MIN_FLOOR(bp_ptr->able.genocide, 10, 25); /* Mass Genocide scrolls */ value += 10000 * MIN(bp_ptr->able.mass_genocide, 10); value += 2000 * MIN_FLOOR(bp_ptr->able.mass_genocide, 10, 25); /* Invulnerability Potions */ value += 10000 * MIN(bp_ptr->able.invulnerability, 99); } /* Reward speed potions/staves */ value += 5000 * MIN(bp_ptr->able.speed, 4); value += 1500 * MIN_FLOOR(bp_ptr->able.speed, 4, 8); /* Reward berserk strength */ value += 500 * MIN(bp_ptr->able.berserk, 5); value += 50 * MIN_FLOOR(bp_ptr->able.berserk, 5, 10); /* Reward Recharge ability */ value += 200 * MIN(bp_ptr->able.recharge, 5); /*** Missiles ***/ /* Reward missiles, Rangers carry more */ if (borg_class == CLASS_RANGER) { value += 100 * MIN(bp_ptr->able.missile, 30); value += 10 * MIN_FLOOR(bp_ptr->able.missile, 30, 80); } else { value += 100 * MIN(bp_ptr->able.missile, 20); value += 10 * MIN_FLOOR(bp_ptr->able.missile, 20, 50); } /*** Various ***/ /* Reward carrying a full wand of teleport away */ value += 100 * MIN(bp_ptr->able.teleport_away, 12); /* Reward carrying up to 10 rods of teleport away */ value += 100 * MIN(bp_ptr->able.teleport_away / 100, 10); /* Reward carrying a wand or rod with balls */ value += 500 * MIN(bp_ptr->able.ball, 10); /* Reward carrying a wand or rod with bolts */ value += 50 * MIN(bp_ptr->able.bolt, 5); /* Reward the charges a staff of power/holiness. */ value += 2000 * MIN(bp_ptr->able.staff_cool, 1); value += 500 * MIN_FLOOR(bp_ptr->able.staff_cool, 1, 5); /* Reward the charges a staff of destruction. */ value += 2000 * MIN(bp_ptr->able.staff_dest, 1); value += 200 * MIN_FLOOR(bp_ptr->able.staff_dest, 1, 5); /* Hack -- Reward add stat */ if (amt_add_stat[A_STR]) value += 50000; if (amt_add_stat[A_INT]) value += 20000; if (bp_ptr->intmana) { if (amt_add_stat[A_INT]) value += 50000; } if (amt_add_stat[A_WIS]) value += 20000; if (bp_ptr->wismana) { if (amt_add_stat[A_WIS]) value += 50000; } if (amt_add_stat[A_DEX]) value += 50000; if (amt_add_stat[A_CON]) value += 50000; if (amt_add_stat[A_CHR]) value += 10000; /* Reward Remove Curse */ if (bp_ptr->status.cursed && bp_ptr->able.remove_curse) value += 90000; /* Reward *Remove Curse* (Pick them up from home) */ if (bp_ptr->status.heavy_curse && bp_ptr->able.star_remove_curse) value += 90000; /* Reward id */ if (bp_ptr->able.id && bp_ptr->able.id_item) value += 10000; /* Reward star_id */ if (bp_ptr->able.star_id && bp_ptr->able.star_id_item) value += 50000; /* Reward restore experience */ if (bp_ptr->lev < bp_ptr->max_lev && bp_ptr->status.fixexp) value += 50000; /* Reward getting stat restore potions when needed */ for (i = 0; i < A_MAX; i++) { if (bp_ptr->status.fixstat[i] && amt_fix_stat[i]) value += 10000; } /*** Enchantment ***/ /* Reward enchant armor */ value += amt_enchant_to_a * 14L; /* Reward enchant weapon to hit */ value += amt_enchant_to_h * 24L; /* Reward enchant weapon to damage */ value += amt_enchant_to_d * 109L; /* Reward a scroll of artifact creation */ value += bp_ptr->able.artifact * 100000; /* Reward having an item to use that artifact scroll on */ value += bp_ptr->able.artify_item * 1000 * bp_ptr->able.artifact; /* Reward a scroll of acquirement */ value += bp_ptr->able.acquire * 100000; /* Reward a scroll of mundanity */ value += bp_ptr->able.mundane * 100000; /*** Hack -- books ***/ /* Reward books */ for (realm = 0; realm < MAX_REALM; realm++) { /* My realm only */ if (!borg_has_realm(realm)) continue; for (book = 0; book < 4; book++) { /* No copies */ if (!amt_book[realm][book]) continue; /* Can the borg use this book? */ if (borg_uses_book(realm, book)) { /* Reward the first book */ value += 500000 * MIN(amt_book[realm][book], 1); /* Is it a town book? */ if (book < 2 || realm == REALM_ARCANE) { /* Reward the second book */ if (bp_ptr->lev > 15) value += 10000 * MIN_FLOOR(amt_book[realm][book], 1, 2); /* Reward the third book */ if (bp_ptr->lev > 35) value += 1000 * MIN_FLOOR(amt_book[realm][book], 2, 3); } } } } /* If the borg is carrying 120% of capacity or more */ if (2 * bp_ptr->weight / adj_str_wgt[my_stat_ind[A_STR]] >= 120) { /* How much plus weight can the borg have before being slowed */ int plus = (adj_str_wgt[my_stat_ind[A_STR]] * 100) / 10; /* Punish the borg for dropping speed */ value -= (bp_ptr->encumber - plus) * 500L; } /* Return the value */ return (value); } /* * Calculate the "power" of the Borg */ s32b borg_power(void) { int i = 1; s32b value = 0L; /* Notice the inventory and equipment */ borg_notice(); /* Process the equipment */ value += borg_power_aux3(); /* Process the inventory */ value += borg_power_aux4(); /* Add a bonus for deep level prep */ /* Dump prep codes */ for (i = 1; i <= bp_ptr->max_depth + 10; i++) { /* Dump fear code */ if (borg_prepared(i)) break; } value += (i - 1) * 20000L; /* Return the value */ return (value); } /* * Determine if the Borg is out of "crucial" supplies. * * Note that we ignore "restock" issues for the first several turns * on each level, to prevent repeated "level bouncing". */ cptr borg_restock(int depth) { /* Always ready for the town */ if (!depth) return (NULL); /* Always spend time on a level unless 100 */ if ((borg_t - borg_began < 100) && (bp_ptr->depth != 100)) return (NULL); /*** Level 1 ***/ /* Must have some lite */ if (!bp_ptr->cur_lite) return ("rs my_cur_lite"); /* Must have "fuel" */ if (!bp_ptr->able.fuel) return ("rs amt_fuel"); /* Must have "food" */ if (!bp_ptr->food) return ("rs amt_food"); /* Assume happy at level 1 */ if (depth <= 1) return (NULL); /*** Level 2 and 3 ***/ /* Must have "fuel" */ if (bp_ptr->able.fuel < 3) return ("rs fuel+2"); /* Must have "food" */ if (bp_ptr->food < 3) return ("rs food+2"); /* Must have "recall" */ if (bp_ptr->recall < 2) return ("rs recall"); /* Assume happy at level 3 */ if (depth <= 3) return (NULL); /*** Level 3 to 5 ***/ if (depth <= 5) return (NULL); /*** Level 6 to 9 ***/ /* Must have "cure" */ if (bp_ptr->able.clw + bp_ptr->able.csw + bp_ptr->able.ccw < 2 && bp_ptr->max_lev < 30) return ("rs cure 2"); /* Must have good lite */ if (bp_ptr->cur_lite == 1) return ("rs lite+1"); /* Something to cure poison */ if (!FLAG(bp_ptr, TR_RES_POIS) && !FLAG(bp_ptr, TR_IM_POIS) && bp_ptr->able.cure_pois < 4) return ("rs cure poison"); /* Assume happy at level 9 */ if (depth <= 9) return (NULL); /*** Level 10 - 19 ***/ /* Something to cure confusion */ if (!FLAG(bp_ptr, TR_RES_CONF) && bp_ptr->able.cure_conf < 4) return ("rs cure conf"); /* Must have "phase" */ if (bp_ptr->able.phase < 1) return ("rs phase"); /* Must have "cure" */ if (bp_ptr->able.clw + bp_ptr->able.csw + bp_ptr->able.ccw < 4 && bp_ptr->max_lev < 30) return ("rs cure 4"); /* Must have "teleport" */ if (bp_ptr->able.teleport < 2) return ("rs teleport"); /* Assume happy at level 19 */ if (depth <= 19) return (NULL); /*** Level 20 - 45 ***/ /* Something to cure blindness */ if (!FLAG(bp_ptr, TR_RES_BLIND) && bp_ptr->able.cure_blind < 4) return ("rs cure blind"); /* Must have "cure" */ if (bp_ptr->able.csw + bp_ptr->able.ccw < 6 && bp_ptr->max_lev < 30) return ("rs cure 6"); /* Must have "teleport" */ if (bp_ptr->able.teleport + bp_ptr->able.escape < 4) return ("rs teleport"); /* Assume happy at level 44 */ if (depth <= 44) return (NULL); /*** Level 46 - 99 ***/ /* Must have "Heal" */ if (bp_ptr->able.heal + amt_rod_heal + bp_ptr->able.easy_heal < 1) return ("rs heal"); /* Assume happy at level 99 */ if (depth <= 99) return (NULL); /*** Level 100 ***/ /* Must have "Heal" */ /* If I just got to dlevel 100 and low on heals, get out now. */ if (borg_t - borg_began < 10 && bp_ptr->able.easy_heal < 15) return ("rs *heal*"); /* Assume happy */ return (NULL); } /* * Determine if the Borg meets the "minimum" requirements for a level */ static cptr borg_prepared_aux2(int depth) { /* Always ready for the town */ if (!depth) return (NULL); /*** Essential Items for Level 1 ***/ /* Require lite (any) */ if (!bp_ptr->cur_lite) return ("1 Lite"); /* Require food */ if (bp_ptr->food < 5) return ("5 Food"); /* Usually ready for level 1 */ if (depth <= 1) return (NULL); /*** Essential Items for Level 2 ***/ /* Require fuel */ if (bp_ptr->able.fuel < 5) return ("5 Fuel"); /* Require recall */ if (!bp_ptr->recall) return ("1 recall"); /* Usually ready for level 2 */ if (depth <= 2) return (NULL); /*** Essential Items for Level 3 and 4 ***/ /* Scrolls of Word of Recall */ if (bp_ptr->recall < 3) return ("3 recall"); /* Potions of Cure Serious Wounds */ if (bp_ptr->able.clw + bp_ptr->able.csw + bp_ptr->able.ccw < 2 && bp_ptr->max_lev < 30) return ("2 cures"); /* Usually ready for level 3 and 4 */ if (depth <= 4) return (NULL); /*** Essential Items for Level 5 to 9 ***/ /* Require lite (radius two) */ if (bp_ptr->cur_lite == 1) return ("2 Lite"); /* Scrolls of Word of Recall */ if (bp_ptr->recall < 4) return ("4 recalls"); /* Potions of Cure Serious/Critical Wounds */ if (bp_ptr->able.clw + bp_ptr->able.csw + bp_ptr->able.ccw < 4 && bp_ptr->max_lev < 30) return ("4 cures"); /* Usually ready for level 5 to 9 */ if (depth <= 9) return (NULL); /*** Essential Items for Level 10 to 19 ***/ /* Escape or Teleport */ if (bp_ptr->able.teleport + bp_ptr->able.escape < 2) return ("2 teleports"); /* Potions of Cure Critical Wounds */ if (bp_ptr->able.csw + bp_ptr->able.ccw < 6 && bp_ptr->max_lev < 30) return ("6 cures"); /* Usually ready for level 10 to 19 */ if (depth <= 19) return (NULL); /*** Essential Items for Level 20 ***/ /* See invisible or telepathy */ if (!FLAG(bp_ptr, TR_SEE_INVIS) && !FLAG(bp_ptr, TR_TELEPATHY)) return ("See Invis : ESP"); /* Free action */ if (!(FLAG(bp_ptr, TR_FREE_ACT))) return ("FA"); /* ready for level 20 */ if (depth <= 20) return (NULL); /*** Essential Items for Level 25 ***/ /* Ready for level 25 */ if (depth <= 25) return (NULL); /*** Essential Items for Level 25 to 39 ***/ /* Escape and Teleport */ if (bp_ptr->able.teleport < 2) return ("teleport2"); if (bp_ptr->able.teleport + bp_ptr->able.escape < 6) return ("tell&esc6"); /* Cure Critical Wounds */ if (bp_ptr->able.csw + bp_ptr->able.ccw < 10 && bp_ptr->max_lev < 30) return ("10 cures"); /* Ready for level 33 */ if (depth <= 33) return (NULL); /* Usually ready for level 20 to 39 */ if (depth <= 39) return (NULL); /*** Essential Items for Level 40 to 45 ***/ if (borg_stat[A_STR] < 160) return ("low STR"); if ((borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE) && borg_stat[A_INT] < 160) return ("low INT"); if ((borg_class == CLASS_PRIEST || borg_class == CLASS_MINDCRAFTER) && borg_stat[A_WIS] < 160) return ("low WIS"); if (borg_stat[A_DEX] < 160) return ("low DEX"); if (borg_stat[A_CON] < 160) return ("low CON"); if (depth <= 45) return (NULL); /*** Essential Items for Level 46 to 55 ***/ /* Must have +5 speed after level 46 */ if (bp_ptr->speed < 115) return ("+5 speed"); /* Potions of heal */ if (!bp_ptr->able.heal && !bp_ptr->able.easy_heal) return ("1heal"); /* High stats XXX XXX XXX */ if (borg_stat[A_STR] < 220) return ("low STR"); if ((borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE) && borg_stat[A_INT] < 280) return ("low INT"); if ((borg_class == CLASS_PRIEST || borg_class == CLASS_MINDCRAFTER) && borg_stat[A_WIS] < 280) return ("low WIS"); if (borg_stat[A_DEX] < 240) return ("low DEX"); if (borg_stat[A_CON] < 240) return ("low CON"); /* Hold Life */ if (!(FLAG(bp_ptr, TR_HOLD_LIFE)) && (bp_ptr->max_lev < 50)) return ("hold life"); /* Minimal level */ if (bp_ptr->max_lev < 40) return ("level 40"); /* Usually ready for level 46 to 55 */ if (depth <= 55) return (NULL); /*** Essential Items for Level 55 to 59 ***/ /* Potions of heal */ if (bp_ptr->able.heal < 2 && !bp_ptr->able.easy_heal) return ("2heal"); /* Telepathy, better have it by now */ if (!(FLAG(bp_ptr, TR_TELEPATHY))) return ("ESP"); /* Usually ready for level 55 to 59 */ if (depth <= 59) return (NULL); /*** Essential Items for Level 61 to 80 ***/ /* Must have +10 speed */ if (bp_ptr->speed < 120) return ("+10 speed"); /* Usually ready for level 61 to 80 */ if (depth <= 80) return (NULL); /*** Essential Items for Level 81-85 ***/ /* Minimal Speed */ if (bp_ptr->speed < 130) return ("+20 Speed"); /* Usually ready for level 81 to 85 */ if (depth <= 85) return (NULL); /*** Essential Items for Level 86-99 ***/ /* Usually ready for level 86 to 99 */ if (depth <= 99) return (NULL); /*** Essential Items for Level 100 ***/ /* must have lots of restore mana to go after Serpent */ if (!bp_ptr->winner) { if ((bp_ptr->msp > 100) && (bp_ptr->able.mana < 15)) return ("15ResMana"); /* must have lots of ez-heal */ if (bp_ptr->able.easy_heal < 25) return ("25 easy_heal"); /* must have lots of heal */ if (borg_has_realm(REALM_LIFE) || borg_has_realm(REALM_NATURE)) { /* Healers can afford to carry less */ if (bp_ptr->able.heal + bp_ptr->able.easy_heal < 40) return ("40 heal + easy_heal"); } else { /* Non-healers need to carry more */ if (bp_ptr->able.heal + bp_ptr->able.easy_heal < 50) return ("50 heal + easy_heal"); } /* must have lots of speed */ if (bp_ptr->able.speed < 15) return ("15Speed"); } /* Its good to be the king */ if (depth <= MAX_DEPTH) return (NULL); /* all bases covered */ return (NULL); } /* * Determine if the Borg is "prepared" for the given level * * This routine does not help him decide how to get ready for the * given level, so it must work closely with "borg_power()". * * Note that we ignore any "town fear", and we allow fear of one * level up to and including the relevant depth. * * This now returns a string with the reason you are not prepared. * */ cptr borg_prepared(int depth) { cptr reason; /* Town and First level */ if (depth == 1) return (NULL); /* Not prepared if I need to restock */ if ((reason = borg_restock(depth))) return (reason); /* Must meet minimal requirements */ if ((reason = borg_prepared_aux2(depth))) return (reason); /* Always okay */ return (NULL); } /* This proc returns the depth that the borg is prepared for */ int borg_prepared_depth(void) { int i; /* Check all depths */ for (i = 1; i < MAX_DEPTH; i++) { /* Is the borg prepared for this depth? */ if (borg_prepared(i)) break; } return (i); } /* * Initialize this file */ void borg_init_5(void) { /* Do nothing? */ } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg6.c0000644000000000000000000027250510250356275013524 0ustar rootroot/* File: zborg6.c */ /* Purpose: Medium level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zbmagic.h" /* Helper function to cure poison when not full/gorged */ bool borg_eat_cure_poison(void) { /* Only when poisoned */ if (!bp_ptr->status.poisoned) return (FALSE); /* Only allow when not too full or really low hp */ if ((bp_ptr->status.full || bp_ptr->status.gorged) && bp_ptr->chp > 3) return (FALSE); return (borg_eat_food(SV_FOOD_CURE_POISON) || borg_eat_food(SV_FOOD_WAYBREAD)); } /* * Attempt to recover from damage and such after a battle * * Note that resting while in danger is counter-productive, unless * the danger is low, in which case it may induce "farming". * * Note that resting while recall is active will often cause you * to lose hard-won treasure from nasty monsters, so we disable * resting when waiting for recall in the dungeon near objects. * * First we try spells/prayers, which are "free", and then we * try food, potions, scrolls, staffs, rods, artifacts, etc. * * XXX XXX XXX * Currently, we use healing spells as if they were "free", but really, * this is only true if the "danger" is less than the "reward" of doing * the healing spell, and if there are no monsters which might soon step * around the corner and attack. */ bool borg_recover(void) { int p = 0; int q; map_block *mb_ptr = map_loc(c_x, c_y); list_item *l_ptr = look_up_equip_slot(EQUIP_LITE); /*** Handle annoying situations ***/ /* If the borg has a torch or lantern */ if (l_ptr && l_ptr->tval == TV_LITE && (k_info[l_ptr->k_idx].sval == SV_LITE_LANTERN || k_info[l_ptr->k_idx].sval == SV_LITE_TORCH)) { /* Try to refuel torch or lantern */ if (borg_refuel()) return (TRUE); /* If it is not everburning and low on fuel */ if (!bp_ptr->britelite && l_ptr->timeout < 1000) { /* Take note */ borg_note("# Need to refuel but can't!", p); /* Go to town */ goal_rising = TRUE; /* Allow Pets to Roam so we dont hit them in the dark. */ p_ptr->pet_follow_distance = PET_STAY_AWAY; } } /*** Do not recover when in danger ***/ /* Look around for danger */ p = borg_danger(c_x, c_y, 1, TRUE); /* Never recover in dangerous situations */ if (p > avoidance / 4) return (FALSE); /*** Roll for "paranoia" ***/ /* Base roll */ q = randint0(100); /* Half dead */ if (bp_ptr->chp < bp_ptr->mhp / 2) q = q - 10; /* Almost dead */ if (bp_ptr->chp < bp_ptr->mhp / 4) q = q - 10; /* Minimize q a bit more */ q = MIN(q, bp_ptr->chp); /*** Use "cheap" cures ***/ /* Hack -- cure stun */ if (bp_ptr->status.stun && (q < 75)) { if (borg_activate(BORG_ACT_HEAL_SERIOUS) || borg_spell(REALM_LIFE, 0, 1) || borg_spell(REALM_LIFE, 0, 6) || borg_spell(REALM_ARCANE, 0, 7)) { /* Take note */ borg_note("# Cure Stun", p); return (TRUE); } } /* Hack -- cure stun */ if (bp_ptr->status.heavy_stun) { if (borg_activate(BORG_ACT_HEAL_SERIOUS) || borg_spell(REALM_LIFE, 1, 2) || borg_racial(RACE_AMBERITE_POWER2)) { /* Take note */ borg_note("# Cure Heavy Stun", p); return (TRUE); } } /* Hack -- cure cuts */ if (bp_ptr->status.cut && (q < 75)) { if (borg_activate(BORG_ACT_HEAL_SERIOUS) || borg_spell(REALM_LIFE, 1, 2) || borg_spell(REALM_NATURE, 0, 7) || borg_spell(REALM_LIFE, 0, 6) || borg_racial(RACE_AMBERITE_POWER2)) { /* Take note */ borg_note("# Cure Cuts", p); return (TRUE); } } /* Hack -- cure poison */ if (bp_ptr->status.poisoned && (q < 75)) { if (borg_activate(BORG_ACT_CURE_POISON) || borg_spell(REALM_ARCANE, 1, 7) || borg_spell(REALM_NATURE, 0, 7) || borg_spell(REALM_LIFE, 1, 2) || borg_racial(RACE_AMBERITE_POWER2)) { /* Take note */ borg_note("# Cure poison", p); return (TRUE); } } /* Hack -- cure fear */ if (bp_ptr->status.afraid && (q < 75)) { if (borg_activate(BORG_ACT_REMOVE_FEAR) || borg_spell(REALM_LIFE, 0, 3)) { /* Take note */ borg_note("# Cure fear", p); return (TRUE); } } /* Hack -- satisfy hunger */ if ((bp_ptr->status.hungry || bp_ptr->status.weak) && (q < 75)) { if (borg_spell_fail(REALM_LIFE, 0, 7, 65) || borg_spell_fail(REALM_ARCANE, 2, 6, 65) || borg_spell_fail(REALM_NATURE, 0, 3, 65) || borg_racial(RACE_HOBBIT) || borg_read_scroll(SV_SCROLL_SATISFY_HUNGER)) { return (TRUE); } } /* Hack -- heal damage */ if ((bp_ptr->chp < bp_ptr->mhp / 2) && (q < 75) && p == 0 && (bp_ptr->csp > bp_ptr->msp / 4)) { if (borg_activate(BORG_ACT_HEAL_BIG) || borg_spell(REALM_LIFE, 1, 6) || borg_spell(REALM_NATURE, 1, 7)) { /* Take note */ borg_note("# heal damage (recovering)"); return (TRUE); } } /* cure experience loss with prayer */ if (bp_ptr->status.fixexp && (borg_activate(BORG_ACT_RESTORE_LIFE) || borg_spell(REALM_LIFE, 3, 3) || borg_spell(REALM_DEATH, 1, 7) || borg_racial(RACE_AMBERITE_POWER2) || borg_racial(RACE_SKELETON) || borg_racial(RACE_ZOMBIE))) { return (TRUE); } /* cure stat drain with prayer */ if ((bp_ptr->status.fixstat[A_STR] || bp_ptr->status.fixstat[A_INT] || bp_ptr->status.fixstat[A_WIS] || bp_ptr->status.fixstat[A_DEX] || bp_ptr->status.fixstat[A_CON] || bp_ptr->status.fixstat[A_CHR]) && (borg_spell(REALM_LIFE, 3, 3) || borg_zap_rod(SV_ROD_RESTORATION) || borg_activate(BORG_ACT_RESTORATION) || borg_racial(RACE_AMBERITE_POWER2) || borg_eat_food(SV_FOOD_RESTORING))) { return (TRUE); } /*** Use "expensive" cures ***/ /* Hack -- cure stun */ if (bp_ptr->status.stun && (q < 25)) { if (borg_use_staff_fail(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_zap_rod(SV_ROD_HEALING) || borg_activate(BORG_ACT_HEAL_BIG) || borg_quaff_crit(FALSE)) { return (TRUE); } } /* Hack -- cure heavy stun */ if (bp_ptr->status.heavy_stun && (q < 95)) { if (borg_quaff_crit(TRUE) || borg_use_staff_fail(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_zap_rod(SV_ROD_HEALING) || borg_activate(BORG_ACT_HEAL_BIG)) { return (TRUE); } } /* Hack -- cure cuts */ if (bp_ptr->status.cut && (q < 25)) { if (borg_use_staff_fail(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_zap_rod(SV_ROD_HEALING) || borg_activate(BORG_ACT_HEAL_BIG) || borg_quaff_crit((bool) (bp_ptr->chp < 10))) { return (TRUE); } } /* Hack -- cure poison */ if (bp_ptr->status.poisoned && (q < 25)) { if (borg_quaff_potion(SV_POTION_CURE_POISON) || borg_quaff_potion(SV_POTION_SLOW_POISON) || borg_eat_cure_poison() || borg_quaff_crit((bool) (bp_ptr->chp < 10)) || borg_use_staff_fail(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_activate(BORG_ACT_CURE_POISON) || borg_racial(RACE_AMBERITE_POWER2)) { return (TRUE); } } /* Hack -- cure blindness */ if (bp_ptr->status.blind && (q < 25)) { if (borg_eat_food(SV_FOOD_CURE_BLINDNESS) || borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(FALSE) || borg_use_staff_fail(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_racial(RACE_AMBERITE_POWER2)) { return (TRUE); } } /* Hack -- cure confusion */ if (bp_ptr->status.confused && (q < 25)) { if (borg_eat_food(SV_FOOD_CURE_CONFUSION) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(FALSE) || borg_use_staff_fail(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING)) { return (TRUE); } } /* Hack -- cure fear */ if (bp_ptr->status.afraid && (q < 25)) { if (borg_eat_food(SV_FOOD_CURE_PARANOIA) || borg_quaff_potion(SV_POTION_BOLDNESS) || borg_quaff_potion(SV_POTION_HEROISM) || borg_quaff_potion(SV_POTION_BERSERK_STRENGTH) || borg_activate(BORG_ACT_REMOVE_FEAR) || borg_activate(BORG_ACT_HEROISM) || borg_activate(BORG_ACT_BERSERKER) || borg_mutation(MUT1_BERSERK) || borg_racial(RACE_HALF_ORC) || borg_racial(RACE_HALF_TROLL) || borg_racial(RACE_AMBERITE_POWER2)) { return (TRUE); } } /* Hack -- satisfy hunger */ if ((bp_ptr->status.hungry || bp_ptr->status.weak) && (q < 25)) { if (borg_read_scroll(SV_SCROLL_SATISFY_HUNGER)) { return (TRUE); } } /* Hack -- heal damage */ if ((bp_ptr->chp < bp_ptr->mhp / 2) && (q < 25)) { if (borg_zap_rod(SV_ROD_HEALING) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_crit(FALSE) || borg_activate(BORG_ACT_HEAL_SERIOUS)) { return (TRUE); } } /* If Fleeing, then do not rest */ if (goal_fleeing) return (FALSE); /* Step 1. Recharge just 1 rod. */ if ((borg_slot(TV_ROD, SV_ROD_HEALING) && borg_slot(TV_ROD, SV_ROD_HEALING)->timeout) || (borg_slot(TV_ROD, SV_ROD_RECALL) && borg_slot(TV_ROD, SV_ROD_RECALL)->timeout)) { /* Rest until at least one recharges */ if (!bp_ptr->status.weak && !bp_ptr->status.cut && !bp_ptr->status.hungry && !bp_ptr->status.poisoned && borg_check_rest() && borg_on_safe_feat(map_loc(c_x, c_y)->feat)) { /* Take note */ borg_note("# Resting to recharge a rod..."); /* Rest until done */ borg_keypress('R'); borg_keypress('1'); borg_keypress('0'); borg_keypress('0'); borg_keypress('\n'); /* Done */ return (TRUE); } } /*** Just Rest ***/ /* Hack -- rest until healed */ if ((bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image || bp_ptr->status.afraid || bp_ptr->status.stun || bp_ptr->status.heavy_stun || bp_ptr->chp < bp_ptr->mhp || bp_ptr->csp < bp_ptr->msp * 6 / 10) && (!borg_takes_cnt || !goal_recalling) && !borg_goi && !borg_shield && !scaryguy_on_level && borg_check_rest() && p <= mb_ptr->fear && !goal_fleeing && borg_on_safe_feat(map_loc(c_x, c_y)->feat)) { /* XXX XXX XXX */ if (!bp_ptr->status.weak && !bp_ptr->status.cut && !bp_ptr->status.hungry && !bp_ptr->status.poisoned) { /* Take note */ borg_note("# Resting (danger %d)...", p); /* Rest until done */ borg_keypress('R'); borg_keypress('\n'); /* Done */ return (TRUE); } } /* Hack to recharge mana if a low level mage or priest */ if (bp_ptr->msp && bp_ptr->lev < 25 && bp_ptr->csp < bp_ptr->msp && p == 0) { if (!bp_ptr->status.weak && !bp_ptr->status.cut && !bp_ptr->status.hungry && !bp_ptr->status.poisoned && borg_on_safe_feat(map_loc(c_x, c_y)->feat)) { /* Take note */ borg_note("# Resting to gain Mana. (danger %d)...", p); /* Rest until done */ borg_keypress('R'); borg_keypress('\n'); /* Done */ return (TRUE); } } /* Nope */ return (FALSE); } /* * Given a "source" and "target" locations, extract a "direction", * which will move one step from the "source" towards the "target". * * Note that we use "diagonal" motion whenever possible. * * We return "5" if no motion is needed. */ int borg_extract_dir(int x1, int y1, int x2, int y2) { /* No movement required */ if ((y1 == y2) && (x1 == x2)) return (5); /* South or North */ if (x1 == x2) return ((y1 < y2) ? 2 : 8); /* East or West */ if (y1 == y2) return ((x1 < x2) ? 6 : 4); /* South-east or South-west */ if (y1 < y2) return ((x1 < x2) ? 3 : 1); /* North-east or North-west */ if (y1 > y2) return ((x1 < x2) ? 9 : 7); /* Paranoia */ return (5); } /* * Clear the "flow" information * * This function was once a major bottleneck, so we now use several * slightly bizarre, but highly optimized, memory copying methods. */ static void borg_flow_clear(void) { map_block *mb_ptr; /* Iterate over the map */ MAP_ITT_START (mb_ptr) { mb_ptr->cost = 255; if (borg_danger_wipe) { /* Clear the "icky" + "know" flags */ mb_ptr->info &= ~(BORG_MAP_ICKY | BORG_MAP_KNOW); } } MAP_ITT_END; /* Wipe complete */ borg_danger_wipe = FALSE; /* Start over */ flow_head = 0; flow_tail = 0; } /* * Take one "step" towards the given location, return TRUE if possible */ static bool borg_play_step(int y2, int x2) { map_block *mb_ptr = NULL; int dir, x, y, ox, oy, i; int o_y = 0, o_x = 0, door_found = 0; /* Breeder levels, close all doors */ if (breeder_level) { /* Scan the adjacent grids */ for (ox = -1; ox <= 1; ox++) { for (oy = -1; oy <= 1; oy++) { /* Skip our own spot */ if ((oy + c_y == c_y) && (ox + c_x == c_x)) continue; /* Skip our orignal goal */ if ((oy + c_y == y2) && (ox + c_x == x2)) continue; /* Bounds checking */ if (!map_in_bounds(ox + c_x, oy + c_y)) continue; /* Acquire location */ mb_ptr = map_loc(ox + c_x, oy + c_y); /* Skip non open doors */ if (mb_ptr->feat != FEAT_OPEN) continue; /* Skip monster on door */ if (mb_ptr->monster) continue; /* Skip repeatedly closed doors */ if (track_door_num >= 255) continue; /* Save this spot */ o_y = oy; o_x = ox; door_found = 1; } } /* Is there a door to close? */ if (door_found) { /* Get a direction, if possible */ dir = borg_goto_dir(c_x, c_y, c_x + o_x, c_y + o_y); /* Obtain the destination */ x = c_x + ddx[dir]; y = c_y + ddy[dir]; /* Hack -- set goal */ g_x = x; g_y = y; /* If the borg is in the dark and just failed to close the door */ if (!bp_ptr->cur_lite && borg_close_door_failed) { /* Hack the door closed */ mb_ptr->feat = FEAT_CLOSED; borg_close_door_failed = FALSE; } else { /* Close */ borg_note("# Closing a door"); borg_keypress('c'); borg_keypress(I2D(dir)); /* Check for an existing flag */ for (i = 0; i < track_door_num; i++) { /* Stop if we already knew about this door */ if ((track_door_x[i] == x) && (track_door_y[i] == y)) return (TRUE); } /* Track the newly closed door */ if (i == track_door_num && i < track_door_size) { borg_note("# Noting the closing of a door."); track_door_num++; track_door_x[i] = x; track_door_y[i] = y; } return (TRUE); } } } /* Get a direction, if possible */ dir = borg_goto_dir(c_x, c_y, x2, y2); /* We have arrived */ if (dir == 5) return (FALSE); /* Obtain the destination */ x = c_x + ddx[dir]; y = c_y + ddy[dir]; /* Access the grid we are stepping on (Assume this is in bounds) */ mb_ptr = map_loc(x, y); /* Hack -- set goal */ g_x = x; g_y = y; /* Monsters -- Attack */ if (mb_ptr->kill) { /* Can't attack someone if afraid! */ if (bp_ptr->status.afraid) return (FALSE); /* Hack -- ignore Maggot until later. */ if ((FLAG(&r_info[mb_ptr->monster], RF_UNIQUE)) && bp_ptr->depth == 0 && bp_ptr->lev < 5) return (FALSE); /* Quick hack to prevent the walking into player bug */ if (!mb_ptr->monster) return (FALSE); /* Message */ borg_note("# Walking into a '%s' at (%d,%d)", r_name + r_info[mb_ptr->monster].name, x, y); /* Walk into it */ if (my_no_alter) { borg_keypress(';'); my_no_alter = FALSE; } else { borg_keypress('+'); } borg_keypress(I2D(dir)); return (TRUE); } /* Objects -- Take */ if (mb_ptr->object) { /*** Handle other takes ***/ /* Message */ borg_note("# Walking onto a '%s' at (%d,%d)", k_name + k_info[mb_ptr->object].name, x, y); /* Walk onto it */ borg_keypress(I2D(dir)); return (TRUE); } /* Traps -- disarm -- */ if (bp_ptr->cur_lite && !bp_ptr->status.blind && !bp_ptr->status.confused && !scaryguy_on_level && mb_ptr->trap) { /* * NOTE: If a scary guy is on the level, * we allow the borg to run over the * trap in order to escape this level. */ /* Cast a disarm with direction */ if (borg_spell(REALM_ARCANE, 0, 6) || borg_zap_rod(SV_ROD_DISARMING) || borg_aim_wand(SV_WAND_DISARMING) || borg_aim_wand(SV_WAND_TRAP_DOOR_DEST)) { /* Make sure there is no target */ borg_keypress('*'); borg_keypress(ESCAPE); borg_note("# Unbarring ways"); borg_keypress(I2D(dir)); mb_ptr->trap = FT_NONE; return (TRUE); } /* allow "destroy doors" */ if (borg_spell(REALM_CHAOS, 0, 1) || borg_activate(BORG_ACT_DISARM)) { borg_note("# Unbarring ways"); mb_ptr->trap = FT_NONE; return (TRUE); } /* Disarm */ borg_note("# Disarming a trap"); borg_keypress('D'); borg_keypress(I2D(dir)); /* We are not sure if the trap will get 'untrapped'. pretend it will */ mb_ptr->trap = FT_NONE; return (TRUE); } /* Closed Doors -- Open */ if (mb_ptr->feat == FEAT_CLOSED) { /* Paranoia XXX XXX XXX */ if (one_in_(100)) return (FALSE); /* If the borg is in the dark and just failed to open the door */ if (!bp_ptr->cur_lite && borg_open_door_failed) { /* Hack the door open */ mb_ptr->feat = FEAT_OPEN; borg_open_door_failed = FALSE; } else { /* Open */ if (my_need_alter) { borg_keypress('+'); my_need_alter = FALSE; } else { borg_note("# Opening a door"); borg_keypress('0'); borg_keypress('9'); borg_keypress('o'); } borg_keypress(I2D(dir)); return (TRUE); } } /* Rubble, Treasure, Seams, Walls -- Tunnel or Melt */ if (mb_ptr->feat >= FEAT_PILLAR && mb_ptr->feat <= FEAT_WALL_SOLID) { /* Mega-Hack -- prevent infinite loops */ if (randint0(100) < 10) return (FALSE); /* Not if hungry */ if (bp_ptr->status.weak) return (FALSE); /* Mega-Hack -- allow "stone to mud" */ if (mb_ptr->feat != FEAT_RUBBLE && (borg_spell_okay_fail(REALM_ARCANE, 2, 4, 60) || borg_spell_okay_fail(REALM_NATURE, 1, 0, 60) || borg_spell_okay_fail(REALM_CHAOS, 2, 3, 60) || borg_mutation_check(MUT1_EAT_ROCK, TRUE) || borg_racial_check(RACE_HALF_GIANT, TRUE))) { /* Lose old target */ borg_keypress('*'); borg_keypress(ESCAPE); /* Mega-Hack -- allow "stone to mud" */ if (borg_spell(REALM_ARCANE, 2, 4) || borg_spell(REALM_NATURE, 1, 0) || borg_spell(REALM_CHAOS, 2, 3) || borg_mutation(MUT1_EAT_ROCK) || borg_racial(RACE_HALF_GIANT)) { borg_note("# Melting a wall (%c)", I2D(dir)); borg_keypress(I2D(dir)); return (TRUE); } } /* No tunneling if in danger */ if (borg_danger(c_x, c_y, 1, TRUE) >= bp_ptr->chp / 4) return (FALSE); /* Tunnel */ borg_note("# Digging through wall/etc"); borg_keypress('0'); borg_keypress('9'); borg_keypress('9'); borg_keypress('T'); borg_keypress(I2D(dir)); return (TRUE); } /* Perhaps the borg could search for traps as he walks around level one. */ if ((bp_ptr->max_lev <= 3) && bp_ptr->depth && !bp_ptr->status.search && borg_needs_searching) { borg_keypress('S'); } /* Turn off the searching if needed */ if (!borg_needs_searching && bp_ptr->status.search) { borg_keypress('S'); } /* Don't step on scary floors */ if (goal != GOAL_FEAT && !borg_on_safe_feat(mb_ptr->feat)) { /* Don't let the borg step on a painful floor */ borg_flow_clear(); return (FALSE); } /* Walk in that direction */ if (my_need_alter) { borg_keypress('+'); my_need_alter = FALSE; } borg_keypress(I2D(dir)); /* Stand stairs up */ if (goal_less) { /* Up stairs */ if (mb_ptr->feat == FEAT_LESS) { /* Stand on stairs */ goal_less = FALSE; /* Success */ return (TRUE); } } /* Did something */ return (TRUE); } /* * Act twitchy */ bool borg_twitchy(void) { int dir; /* This is a bad thing */ borg_note("# Twitchy!"); /* try to phase out of it */ if (bp_ptr->able.phase && borg_caution_phase(15, 2) && (borg_spell_fail(REALM_ARCANE, 0, 4, 40) || borg_spell_fail(REALM_SORCERY, 0, 1, 40) || borg_spell_fail(REALM_TRUMP, 0, 0, 40) || borg_activate(BORG_ACT_PHASE_DOOR) || borg_activate(BORG_ACT_TELEPORT) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* We did something */ return (TRUE); } /* Pick a random direction */ dir = randint1(9); /* Hack -- set goal */ g_x = c_x + ddx[dir]; g_y = c_y + ddy[dir]; /* Maybe alter */ if (randint0(100) < 10 && dir != 5) { /* Send action (alter) */ borg_keypress('+'); /* Send direction */ borg_keypress(I2D(dir)); } /* Normally move */ else { /* Send direction */ borg_keypress(I2D(dir)); } /* We did something */ return (TRUE); } /* short hand options for borg_flow_spread */ #define BORG_FLOW_SIMPLE 0x00000000L #define BORG_FLOW_OPTIMIZE 0x00000001L #define BORG_FLOW_AVOID 0x00000002L #define BORG_FLOW_OPTI_AVOID 0x00000003L #define BORG_FLOW_TUNNELING 0x00000004L #define BORG_FLOW_FEAT_HURT 0x00000008L /* * Spread a "flow" from the "destination" grids outwards * * We fill in the "cost" field of every grid that the player can * "reach" with the number of steps needed to reach that grid, * if the grid is "reachable", and otherwise, with "255", which * is the largest possible value that can be stored in a byte. * * Thus, certain grids which are actually "reachable" but only by * a path which is at least 255 steps in length will thus appear * to be "unreachable", but this is not a major concern. * * We use the "flow" array as a "circular queue", and thus we must * be careful not to allow the "queue" to "overflow". This could * only happen with a large number of distinct destination points, * each several units away from every other destination point, and * in a dungeon with no walls and no dangerous monsters. But this * is technically possible, so we must check for it just in case. * * We do not need a "priority queue" because the cost from grid to * grid is always "one" and we process them in order. If we did * use a priority queue, this function might become unusably slow, * unless we reactivated the "room building" code. * * We handle both "walls" and "danger" by marking every grid which * is "impassible", due to either walls, or danger, as "ICKY", and * marking every grid which has been "checked" as "KNOW", allowing * us to only check the wall/danger status of any grid once. This * provides some important optimization, since many "flows" can be * done before the "ICKY" and "KNOW" flags must be reset. * * Note that the "borg_enqueue_grid()" function should refuse to * enqueue "dangeous" destination grids, but does not need to set * the "KNOW" or "ICKY" flags, since having a "cost" field of zero * means that these grids will never be queued again. In fact, * the "borg_enqueue_grid()" function can be used to enqueue grids * which are "walls", such as "doors" or "rubble". * * This function is extremely expensive, and is a major bottleneck * in the code, due more to internal processing than to the use of * the "borg_danger()" function, especially now that the use of the * "borg_danger()" function has been optimized several times. * * The "optimize" flag allows this function to stop as soon as it * finds any path which reaches the player, since in general we are * looking for paths to destination grids which the player can take, * and we can stop this function as soon as we find any usable path, * since it will always be as short a path as possible. * * We queue the "children" in reverse order, to allow any "diagonal" * neighbors to be processed first, since this may boost efficiency. * * Note that we should recalculate "danger", and reset all "flows" * if we notice that a wall has disappeared, and if one appears, we * must give it a maximal cost, and mark it as "icky", in case it * was currently included in any flow. * * If a "depth" is given, then the flow will only be spread to that * depth, note that the maximum legal value of "depth" is 250. */ static void borg_flow_spread(int depth, u32b flow_how) /* bool optimize, bool avoid, bool tunneling, bool feat_hurt)*/ { int i; int n, o = 0; int x1, y1; int x, y; map_block *mb_ptr = map_loc(c_x, c_y); int player_cost = mb_ptr->cost; bool optimize = (flow_how & BORG_FLOW_OPTIMIZE) ? TRUE : FALSE; bool avoid = (flow_how & BORG_FLOW_AVOID) ? TRUE : FALSE; bool tunneling = (flow_how & BORG_FLOW_TUNNELING) ? TRUE : FALSE; bool feat_hurt = (flow_how & BORG_FLOW_FEAT_HURT) ? TRUE : FALSE; /* Now process the queue */ while (flow_head != flow_tail) { /* Extract the next entry */ x1 = borg_flow_x[flow_tail]; y1 = borg_flow_y[flow_tail]; /* Circular queue -- dequeue the next entry */ if (++flow_tail == BORG_FLOW_MAX) flow_tail = 0; /* Bounds checking */ if (!map_in_bounds(x1, y1)) continue; mb_ptr = map_loc(x1, y1); /* Cost (one per movement grid) */ n = mb_ptr->cost + 1; /* New depth */ if (n > o) { /* Optimize (if requested) */ if (optimize && (n > player_cost)) break; /* Limit depth */ if (n > depth) break; /* Save */ o = n; } /* Queue the "children" */ for (i = 0; i < 8; i++) { int old_head; map_block *mb_ptr; /* Neighbor grid */ x = x1 + ddx_ddd[i]; y = y1 + ddy_ddd[i]; /* Only on legal grids */ if (!map_in_bounds(x, y)) continue; /* Access the grid */ mb_ptr = map_loc(x, y); /* Skip "reached" grids */ if (mb_ptr->cost <= n) continue; /* If the borg is not ready to tunnel */ if (!tunneling) { /* Avoid walls */ if (mb_ptr->feat >= FEAT_SECRET && mb_ptr->feat <= FEAT_WALL_SOLID) continue; /* Avoid pillars */ if (mb_ptr->feat == FEAT_PILLAR) continue; /* Avoid Jungle */ if (mb_ptr->feat >= FEAT_FENCE && mb_ptr->feat <= FEAT_JUNGLE) continue; } /* Avoid "perma-wall" grids */ if (mb_ptr->feat >= FEAT_PERM_EXTRA && mb_ptr->feat <= FEAT_PERM_SOLID) continue; /* Sometimes allows painfull feats to be included */ if (!feat_hurt && !borg_on_safe_feat(mb_ptr->feat)) continue; /* Avoid unknown grids (if requested or retreating) */ if ((avoid || borg_desperate) && !mb_ptr->feat) continue; /* Avoid Monsters if Desprerate */ if (borg_desperate && (mb_ptr->monster)) continue; /* Avoid Traps if low level-- unless brave or scaryguy. */ if (mb_ptr->trap && avoidance <= bp_ptr->chp && !scaryguy_on_level) { /* Do not disarm when you could end up dead */ if (bp_ptr->chp < 60) continue; /* Do not disarm when clumsy */ if ((bp_ptr->skill_dis < 30) && (bp_ptr->lev < 20)) continue; if ((bp_ptr->skill_dis < 45) && (bp_ptr->lev < 10)) continue; } /* Ignore "icky" grids */ if (mb_ptr->info & BORG_MAP_ICKY) continue; /* Analyze every grid once */ if (!(mb_ptr->info & BORG_MAP_KNOW)) { int p; /* Mark as known */ mb_ptr->info |= BORG_MAP_KNOW; if (!borg_desperate) { /* Get the danger */ p = borg_danger(x, y, 1, TRUE); /* Dangerous grid */ if (p > avoidance / 3) { /* Mark as icky */ mb_ptr->info |= BORG_MAP_ICKY; /* Ignore this grid */ continue; } } } /* Save the flow cost */ mb_ptr->cost = n; /* Enqueue that entry */ borg_flow_x[flow_head] = x; borg_flow_y[flow_head] = y; /* Circular queue -- memorize head */ old_head = flow_head; /* Circular queue -- insert with wrap */ if (++flow_head == BORG_FLOW_MAX) flow_head = 0; /* Circular queue -- handle overflow (badly) */ if (flow_head == flow_tail) flow_head = old_head; } } /* Forget the flow info */ flow_head = flow_tail = 0; } /* * Enqueue a fresh (legal) starting grid, if it is safe */ static void borg_flow_enqueue_grid(int x, int y) { int old_head; map_block *mb_ptr; /* Bounds checking */ if (!map_in_bounds(x, y)) return; mb_ptr = map_loc(x, y); /* Avoid icky grids */ if (mb_ptr->info & BORG_MAP_ICKY) return; /* Unknown */ if (!(mb_ptr->info & BORG_MAP_KNOW)) { /* Mark as known */ mb_ptr->info |= BORG_MAP_KNOW; /* Mark dangerous grids as icky */ if ((borg_danger(x, y, 1, TRUE) > avoidance / 3) && !borg_desperate) { /* Icky */ mb_ptr->info |= BORG_MAP_ICKY; /* Avoid */ return; } } /* Only enqueue a grid once */ if (mb_ptr->cost == 1) return; /* Save the flow cost (zero) */ mb_ptr->cost = 1; /* Enqueue that entry */ borg_flow_y[flow_head] = y; borg_flow_x[flow_head] = x; /* Circular queue -- memorize head */ old_head = flow_head; /* Circular queue -- insert with wrap */ if (++flow_head == BORG_FLOW_MAX) flow_head = 0; /* Circular queue -- handle overflow */ if (flow_head == flow_tail) flow_head = old_head; } /* * Do a "reverse" flow from the player outwards */ static void borg_flow_reverse(void) { /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the player's grid */ borg_flow_enqueue_grid(c_x, c_y); /* Spread, but do NOT optimize */ borg_flow_spread(250, BORG_FLOW_SIMPLE); } /* * Commit the current "flow" */ static bool borg_flow_commit(cptr who, int why) { int cost; map_block *mb_ptr = map_loc(c_x, c_y); /* Cost of current grid */ cost = mb_ptr->cost; /* Verify the total "cost" */ if (cost >= 250) return (FALSE); /* Message */ if (who) borg_note("# Flowing toward %s at cost %d", who, cost); /* Iterate over all grids */ MAP_ITT_START (mb_ptr) { /* Obtain the "flow" information */ mb_ptr->flow = mb_ptr->cost; } MAP_ITT_END; /* Save the goal type */ goal = why; /* Success */ return (TRUE); } /* * Attempt to take an optimal step towards the current goal location * * Note that the "borg_update()" routine notices new monsters and objects, * and movement of monsters and objects, and cancels any flow in progress. * * Note that the "borg_update()" routine notices when a grid which was * not thought to block motion is discovered to in fact be a grid which * blocks motion, and removes that grid from any flow in progress. * * When given multiple alternative steps, this function attempts to choose * the "safest" path, by penalizing grids containing embedded gold, monsters, * rubble, doors, traps, store doors, and even floors. This allows the Borg * to "step around" dangerous grids, even if this means extending the path by * a step or two, and encourages him to prefer grids such as objects and stairs * which are not only interesting but which are known not to be invisible traps. * * XXX XXX XXX XXX This function needs some work. It should attempt to * analyze the "local region" around the player and determine the optimal * choice of locations based on some useful computations. * * If it works, return TRUE, otherwise, cancel the goal and return FALSE. */ bool borg_flow_old(int why) { int x, y; map_block *mb_ptr; /* Continue */ if (goal == why) { int b_n = 0; int i, b_i = -1; int c, b_c; mb_ptr = map_loc(c_x, c_y); /* Flow cost of current grid */ b_c = mb_ptr->flow * 10; /* Prevent loops */ b_c = b_c - 5; /* Look around */ for (i = 0; i < 8; i++) { /* Grid in that direction */ x = c_x + ddx_ddd[i]; y = c_y + ddy_ddd[i]; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Access the grid */ mb_ptr = map_loc(x, y); /* Flow cost at that grid */ c = mb_ptr->flow * 10; /* Never backtrack */ if (c > b_c) continue; /* Notice new best value */ if (c < b_c) b_n = 0; /* Apply the randomizer to equivalent values */ if ((++b_n >= 2) && (randint0(b_n))) { continue; } /* Track it */ b_i = i; b_c = c; } /* Try it */ if (b_i >= 0) { /* Access the location */ x = c_x + ddx_ddd[b_i]; y = c_y + ddy_ddd[b_i]; /* Attempt motion */ if (borg_play_step(y, x)) return (TRUE); } /* Cancel goal */ goal = GOAL_NONE; } /* Nothing to do */ return (FALSE); } /* * Flow closer to a set of coordinates. This proc aims to get right on those * coords. That is closer than needed for a word of recall, but it is an easy * way to make sure that the borg doesn't go down the wrong dungeon. */ static bool borg_flow_block(int x, int y, cptr reason, int why) { /* Don't leave the map */ if (x < 0 || x > BORG_MAX_WILD_SIZE) return (FALSE); /* Don't leave the map */ if (y < 0 || y > BORG_MAX_WILD_SIZE) return (FALSE); /* Flow a block east */ if (c_x < x) x = MIN(c_x + 16, x); /* Flow a block west */ else if (c_x > x) x = MAX(c_x - 16, x); else x = c_x; /* Flow a block south */ if (c_y < y) y = MIN(c_y + 16, y); /* Flow a block north */ else if (c_y > y) y = MAX(c_y - 16, y); else y = c_y; /* No need to go where you already are */ if (x == c_x && y == c_y) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the grid */ borg_flow_enqueue_grid(x, y); /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTI_AVOID); /* Attempt to Commit the flow */ if (!borg_flow_commit(reason, why)) return (FALSE); /* Take one step */ if (!borg_flow_old(why)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to "flow" towards a specific shop entry */ bool borg_flow_shop_entry(int i) { int x, y; /* Must be in town */ if (bp_ptr->depth) return (FALSE); /* Is it a valid shop? */ if (i < 0 || i > borg_shop_num) return (FALSE); /* Pick up the new target, but not too far */ if (c_x < borg_shops[i].x) { /* Flow a block east */ x = MIN(c_x + 16, borg_shops[i].x); } else if (c_x > borg_shops[i].x) { /* Flow a block west */ x = MAX(c_x - 16, borg_shops[i].x); } else x = c_x; if (c_y < borg_shops[i].y) { /* Flow a block south */ y = MIN(c_y + 16, borg_shops[i].y); } else if (c_y > borg_shops[i].y) { /* Flow a block north */ y = MAX(c_y - 16, borg_shops[i].y); } else y = c_y; /* Hack -- re-enter a shop if needed */ if (x == c_x && y == c_y) { /* Note */ borg_note("# Re-entering a shop"); /* Enter the store */ borg_keypress('5'); /* Success */ return (TRUE); } /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the grid */ borg_flow_enqueue_grid(x, y); /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ if (!borg_flow_commit("shop", GOAL_SHOP)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_SHOP)) return (FALSE); /* Success */ return (TRUE); } /* Choose a shop to visit */ bool borg_choose_shop(void) { int i; s32b dist, b_d = BORG_SMALL_DISTANCE; /* Must be in town */ if (bp_ptr->depth) return (FALSE); /* Only try to find a shop when there is no goal */ if (goal && goal != GOAL_SHOP) return (FALSE); /* If we are already flowing toward a shop do not check again... */ if (goal_shop != -1) { borg_note("# Using previous goal shop: %d", goal_shop); return (TRUE); } /* Find 'best' shop to go to */ for (i = 0; i < borg_shop_num; i++) { /* Do not revisit shops */ if (borg_shops[i].visit) continue; /* Get distance */ dist = distance(c_x, c_y, borg_shops[i].x, borg_shops[i].y); /* Visit only the shops in one town */ if (dist > BORG_SMALL_DISTANCE) continue; /* Only closer shops */ if (dist >= b_d) continue; goal_shop = i; } /* All shops have been visited */ if (goal_shop == -1) return (FALSE); /* We want to shop */ borg_note("# Goal shop: %d, dist: %d", goal_shop, b_d); /* Success */ return (TRUE); } /* Find the closest shop from the closest town */ bool borg_find_shop(void) { if (borg_choose_shop()) { /* Try and visit a shop, if so desired */ if (borg_flow_shop_entry(goal_shop)) return (TRUE); /* Let us know what the value is when we fail */ borg_note("# Failed to get good shop!"); } goal = GOAL_NONE; goal_shop = -1; shop_num = -1; /* No shop */ return (FALSE); } /* Find a town to blow some money */ bool borg_find_town(void) { int i, b_i = -1; int d, b_d = BORG_MAX_DISTANCE; /* Only on the surface */ if (bp_ptr->depth && !vanilla_town) return (FALSE); /* No known town */ if (!borg_town_num) return (FALSE); /* Not enough money */ if (borg_gold < 20) return (FALSE); /* Buying doesn't really help you now, unless you have loads */ if (borg_prepared_depth() > 20 && borg_gold < 100000) return (FALSE); /* Leave if there is a goal already */ if (goal && goal != GOAL_TOWN) return (FALSE); /* Remember previous effort */ if (goal == GOAL_TOWN) { b_i = goal_town; /* Get the distance to this place */ b_d = distance(c_x, c_y, borg_towns[b_i].x, borg_towns[b_i].y); } /* Find the closest, non-visited town */ else { /* Loop through the towns */ for (i = 0; i < borg_town_num; i++) { /* Get the distance to this place */ d = distance(c_x, c_y, borg_towns[i].x, borg_towns[i].y); /* Was it visited recently? */ if (borg_towns[i].visit) continue; /* Is it closer? */ if (d >= b_d) continue; /* Remember this one */ b_i = i; b_d = d; } } /* All towns were visited since last dungeon crawl */ if (b_i == -1) return (FALSE); /* More or less in town */ if (b_d < BORG_SMALL_DISTANCE) { /* Mark this place to prevent looping */ borg_towns[b_i].visit = TRUE; /* No need to go here anymore */ goal_town = -1; /* Go shopping */ if (borg_find_shop()) return (TRUE); /* Oh well */ return (FALSE); } /* Go closer */ if (borg_flow_block(borg_towns[b_i].x, borg_towns[b_i].y, borg_towns[b_i].name, GOAL_TOWN)) { /* Happy */ return (TRUE); } else { borg_note("Flow block failed because the target was in deep water"); } goal = GOAL_NONE; goal_town = -1; /* The borg is in that town */ return (FALSE); } /* Find a new dungeon */ bool borg_find_dungeon(void) { int i, b_i = -1; int d, b_d = BORG_MAX_DISTANCE; int p; /* Do this only on the surface */ if (bp_ptr->depth) return (FALSE); /* Find the target depth */ p = borg_prepared_depth(); /* A bit of trickery for the vanilla_town */ if (vanilla_town) { /* Tell the borg to enter the only existing dungeon */ goal = GOAL_CAVE; goal_dungeon = 0; } else { /* Not when the borg knows only a few towns */ if ((p > 20) && ((bp_ptr->lev > 20 && borg_town_num < 5) || (bp_ptr->lev > 30 && borg_town_num < 13) || (bp_ptr->lev > 40 && borg_town_num < 19))) return (FALSE); } /* Not when the borg is exploring the wilderness */ if (goal && goal != GOAL_CAVE) return (FALSE); /* Remember previous effort */ if (goal == GOAL_CAVE) { b_i = goal_dungeon; b_d = distance(c_x, c_y, borg_dungeons[b_i].x, borg_dungeons[b_i].y); } /* Find the closest non-visited dungeon */ else { /* Loop through the dungeons */ for (i = 0; i < borg_dungeon_num; i++) { /* This dungeon starts too deep */ if (borg_dungeons[i].min_depth > p) continue; /* This dungeon ends too shallow */ if (borg_dungeons[i].max_depth < p && borg_dungeons[i].bottom) continue; /* How far away is this? */ d = distance(c_x, c_y, borg_dungeons[i].x, borg_dungeons[i].y); /* Ignore dungeons far away */ if (d > b_d) continue; /* Remember this one */ b_i = i; b_d = d; } } /* No good dungeon found */ if (b_i == -1) return (FALSE); /* If the borg is right there at the dungeon */ if (b_d == 0) { /* Reached the place */ goal_dungeon = -1; /* If the borg leaves the surface */ if (!bp_ptr->depth) borg_leave_surface(); /* If the dungeon was visited and the target depth is not shallow */ if (p >= borg_dungeons[b_i].min_depth + 4 && borg_dungeons[b_i].min_depth != 0 && borg_dungeons[b_i].min_depth < 5 + borg_dungeons[b_i].max_depth != 0 && bp_ptr->recall >= 4 && borg_recall()) { /* Note */ borg_note("# Recalling into dungeon."); } else { /* Take those stairs */ borg_keypress('>'); } return (TRUE); } /* Go closer to that dungeon */ if (borg_flow_block(borg_dungeons[b_i].x, borg_dungeons[b_i].y, "my dungeon", GOAL_CAVE)) { goal_dungeon = b_i; /* Happy */ return (TRUE); } goal_dungeon = -1; goal = GOAL_NONE; return (FALSE); } /* * Prepare to flee the level via stairs */ bool borg_flow_stair_both(int why) { int i; /* None to flow to */ if (!track_less_num && !track_more_num) return (FALSE); /* dont go down if hungry or low on food, unless fleeing a scary town */ if ((!goal_fleeing && !bp_ptr->depth) && (!bp_ptr->cur_lite || bp_ptr->status.weak || bp_ptr->status.hungry || (bp_ptr->food < 2))) return (FALSE); /* clear the possible searching flag */ borg_needs_searching = FALSE; /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < track_less_num; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(track_less_x[i], track_less_y[i]); } /* Enqueue useful grids */ for (i = 0; i < track_more_num; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(track_more_x[i], track_more_y[i]); } /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ if (!borg_flow_commit("stairs", why)) return (FALSE); /* Take one step */ if (!borg_flow_old(why)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to flow towards "up" stairs */ bool borg_flow_stair_less(int why) { int i; /* None to flow to */ if (!track_less_num) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* clear the possible searching flag */ borg_needs_searching = FALSE; /* Enqueue useful grids */ for (i = 0; i < track_less_num; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(track_less_x[i], track_less_y[i]); } if ((bp_ptr->lev > 35) || !bp_ptr->cur_lite) { /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); } else { /* Spread the flow, No Optimize, Avoid */ borg_flow_spread(250, (borg_desperate) ? BORG_FLOW_SIMPLE : BORG_FLOW_AVOID); } /* Attempt to Commit the flow */ if (!borg_flow_commit("up-stairs", why)) return (FALSE); /* Take one step */ if (!borg_flow_old(why)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to flow towards "down" stairs */ bool borg_flow_stair_more(int why) { int i; /* None to flow to */ if (!track_more_num) return (FALSE); /* if not fleeing do not go down unless safe */ if (!goal_fleeing && borg_prepared(bp_ptr->depth + 1)) return (FALSE); /* dont go down if hungry or low on food, unless fleeing a scary town */ if (bp_ptr->depth && (bp_ptr->status.weak || bp_ptr->status.hungry || (bp_ptr->food < 2))) return (FALSE); /* No diving if no light */ if (!bp_ptr->cur_lite) return (FALSE); /* don't head for the stairs if you are recalling, */ /* even if you are fleeing. */ if (goal_recalling) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < track_more_num; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(track_more_x[i], track_more_y[i]); } /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ if (!borg_flow_commit("down-stairs", why)) return (FALSE); /* Take one step */ if (!borg_flow_old(why)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to flow towards glyph of warding */ bool borg_flow_glyph(int why) { int i; /* None to flow to */ if (!track_glyph_num) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < track_glyph_num; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(track_glyph_x[i], track_glyph_y[i]); } /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ if (!borg_flow_commit("glyph of warding", why)) return (FALSE); /* Take one step */ if (!borg_flow_old(why)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to flow towards light */ bool borg_flow_light(int why) { int y, x, i; map_block *mb_ptr; /* reset counters */ borg_temp_n = 0; i = 0; /* build the glow array */ /* Scan map */ MAP_ITT_START (mb_ptr) { /* Not a perma-lit, and not our spot. */ if (!(mb_ptr->flags & MAP_GLOW)) continue; /* Get location */ MAP_GET_LOC(x, y); /* keep count */ borg_temp_y[borg_temp_n] = y; borg_temp_x[borg_temp_n] = x; borg_temp_n++; } MAP_ITT_END; /* None to flow to */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < borg_temp_n; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(borg_temp_x[i], borg_temp_y[i]); } /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ if (!borg_flow_commit("a lighted area", why)) return (FALSE); /* Take one step */ if (!borg_flow_old(why)) return (FALSE); /* Success */ return (TRUE); } /* When it is dark and the borg is outside, find an inn */ bool borg_waits_daylight(void) { int i, b_i = -1; int d, b_d = BORG_MAX_DISTANCE; /* Is there a wilderness? */ if (vanilla_town) return (FALSE); /* Is the borg at the surface? */ if (bp_ptr->depth) return (FALSE); /* Is it dark at all? */ if (bp_ptr->hour > 5 && bp_ptr->hour < 18) return (FALSE); /* Find the closest inn */ for (i = 0; i < borg_shop_num; i++) { if (borg_shops[i].type != BUILD_INN) continue; /* How far away is this? */ d = distance(c_x, c_y, borg_dungeons[i].x, borg_dungeons[i].y); /* Ignore inns far away */ if (d > b_d) continue; /* Remember this one */ b_i = i; b_d = d; } goal_shop = b_i; /* Go to that inn */ if (b_i != -1 && borg_flow_shop_entry(b_i) && borg_gold > 25) { borg_note("# Looking for a place to spend the night."); return (TRUE); } /* Don't rest when it hurts */ if (!borg_on_safe_feat(map_loc(c_x, c_y)->feat)) return (FALSE); /* Wait out the night */ borg_keypress(ESCAPE); borg_keypress('0'); borg_keypress('2'); borg_keypress('9'); borg_keypress('9'); borg_keypress('R'); borg_note("# Wait out the night."); return (TRUE); } /* * This proc returns TRUE if from (x, y) there is a monster to target, or the * spot next to a monster. It fails if the new position is next to a monster. * The method to find out if there is a monster is taken from borg_temp_fill. */ static bool borg_aim_ball(int x, int y) { int i, dx, dy; int x1, y1; bool found_target = FALSE; /* Loop through the close, known monsters */ for (i = 0; i < borg_temp_n; i++) { /* Fail if the new position is next to a monster */ if (distance(x, y, borg_temp_x[i], borg_temp_y[i]) == 1) return (FALSE); /* Don't bother if a monster has been found already */ if (!found_target) { for (dx = -1; dx <= 1; dx++) { for (dy = -1; dy <= 1; dy++) { /* Get the coords of the target grid */ x1 = borg_temp_x[i] + dx; y1 = borg_temp_y[i] + dy; /* Bounds checking */ if (!map_in_bounds(x1, y1)) continue; /* Is this grid out of range? */ if (distance(x, y, x1, y1) > MAX_RANGE) continue; /* If the borg has no ESP * or the borg has ESP and has just hit his target * assume there is no wall in the way * OR * If the borg has ESP and has just missed his target * assume there is a wall in the way */ if (((!FLAG(bp_ptr, TR_TELEPATHY) || (FLAG(bp_ptr, TR_TELEPATHY) && successful_target)) && borg_los(x, y, x1, y1)) || (FLAG(bp_ptr, TR_TELEPATHY) && !successful_target && borg_los_pure(x, y, x1, y1))) { /* If it is not a wall it is OK for a ball */ if (!borg_cave_wall_grid(map_loc(x, y))) { /* Found a targettable monster */ found_target = TRUE; } } } } } } /* return result */ return (found_target); } /* * Take a couple of steps to line up a shot * */ bool borg_flow_kill_aim(bool viewable) { int o_y, o_x; /* Consider each adjacent spot */ for (o_x = c_x - 2; o_x <= c_x + 2; o_x++) { for (o_y = c_y - 2; o_y <= c_y + 2; o_y++) { /* borg_attack would have already checked for a shot from where I currently am */ if (o_x == c_x && o_y == c_y) continue; /* avoid screen edgeds */ if (!map_in_bounds(o_x, o_y)) continue; /* Is there a possible target? */ if (borg_aim_ball(o_x, o_y)) { /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the grid */ borg_flow_enqueue_grid(o_x, o_y); /* Spread the flow */ borg_flow_spread(5, (viewable) ? BORG_FLOW_OPTIMIZE : BORG_FLOW_OPTI_AVOID); /* Attempt to Commit the flow */ if (!borg_flow_commit("targetable position", GOAL_KILL)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_KILL)) return (FALSE); /* success */ return (TRUE); } } } /* No new flow */ return (FALSE); } /* * Dig an anti-summon corridor * * ############## We want the borg to not dig #1 * #............# but to dig #2, and hopefully shoot from the * #######............# last #2 and try to avoid standing on #3. * #222223............# This is great for offset ball attacks but * #2#####..s.........# not for melee. Warriors need to dig a wall * ######2###########+###### adjacent to the monsters so he can swing on them. * # 1 # * # ################ # * # # # * ### # # * */ bool borg_flow_kill_corridor(bool viewable) { #if 0 int o_y, o_x; int m_x, m_y; int f_y, f_x; int floors = 0; int b_y = 0, b_x = 0; int perma_grids = 0; borg_kill *kill; #endif /* 0 */ /* Hack - ignore usused parameter */ (void)viewable; /* Efficiency -- Nothing to kill */ if (!borg_kills_cnt) return (FALSE); /* Need to do this properly */ #if 0 /* Only do this to summoners when they are close */ if (borg_kills_summoner == -1) return (FALSE); /* Do not dig when weak. It takes too long */ if (my_stat_ind[A_STR] < 17) return (FALSE); /* Do not dig when confused */ if (bp_ptr->status.confused) return (FALSE); /* Not when darkened */ if (!bp_ptr->cur_lite) return (FALSE); /* get the summoning monster */ kill = &borg_kills[borg_kills_summoner]; /* Consider each adjacent spot to monster */ for (o_x = -1; o_x <= 1; o_x++) { for (o_y = -1; o_y <= 1; o_y++) { map_block *mb_ptr; /* Check grids near monster */ m_x = kill->x + o_x; m_y = kill->y + o_y; /* avoid screen edges */ if (!map_in_bounds(m_x + 2, m_y + 2)) continue; if (!map_in_bounds(m_x - 2, m_y - 2)) continue; /* Bounds checking */ if (!map_in_bounds(m_x, m_y)) continue; /* get the grid */ mb_ptr = map_loc(m_x, m_y); /* Can't tunnel a non wall or permawall */ if (mb_ptr->feat >= FEAT_FLOOR && mb_ptr->feat < FEAT_MAGMA) continue; if (mb_ptr->feat >= FEAT_PERM_EXTRA && mb_ptr->feat <= FEAT_PERM_SOLID) { perma_grids++; continue; } /* Do not dig unless we appear strong enough to succeed or we have a digger */ if (borg_spell_legal(REALM_ARCANE, 2, 4) || borg_spell_legal(REALM_NATURE, 1, 0) || borg_spell_legal(REALM_CHAOS, 2, 3) || borg_mutation_check(MUT1_EAT_ROCK, TRUE) || borg_racial_check(RACE_HALF_GIANT, TRUE) || (bp_ptr->skill_dig > (bp_ptr->depth > 80 ? 30 : 40))) { /* digging ought to work */ } else { /* do not try digging */ continue; } /* reset floors counter */ floors = 0; /* That grid must not have too many floors adjacent */ for (f_x = -1; f_x <= 1; f_x++) { for (f_y = -1; f_y <= 1; f_y++) { /* Bounds checking */ if (!map_in_bounds(m_x + f_x, m_y + f_y)) continue; /* grid the grid */ mb_ptr = map_loc(m_x + f_x, m_y + f_y); /* check if this neighbor is a floor */ if (borg_cave_floor_grid(mb_ptr)) floors++; } } /* Do not dig if too many floors near. */ if (floors >= 5) continue; /* Track the good location */ b_y = m_y; b_x = m_x; } } /* NOTE: Perma_grids count the number of grids which contain permawalls. * The borg may try to flow to an unknown grid but may get stuck on a perma * wall. This will keep him from flowing to a summoner if the summoner is * near a perma grid. The real fix out to be in the flow_spread so that * he will not flow through perma_grids. I will work on that next. */ if (b_y != 0 && b_x != 0 && perma_grids == 0) { /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the grid */ borg_flow_enqueue_grid(m_x, m_y); /* Spread the flow */ borg_flow_spread(15, BORG_FLOW_OPTIMIZE | BORG_FLOW_TUNNELING); /* Attempt to Commit the flow */ if (!borg_flow_commit("anti-summon corridor", GOAL_KILL)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_KILL)) return (FALSE); return (TRUE); } #endif /* 0 */ return FALSE; } /* Check to see if the borg is standing on a nasty grid. * Lava can hurt the borg unless he is IFire. * Water can hurt if it is deep/ocean and encumbered. * Acid can hurt the borg unless he is IAcid. * Swamp can hurt the borg unless he is IPoison. * Levitation item can reduce the effect of nasty grids. */ bool borg_on_safe_feat(byte feat) { /* Water */ if (feat == FEAT_DEEP_WATER || feat == FEAT_OCEAN_WATER) { /* Levitation helps */ if (FLAG(bp_ptr, TR_FEATHER)) return (TRUE); /* Being non-encumbered helps */ if (!bp_ptr->encumber) return (TRUE); /* Everything else hurts */ return (FALSE); } /* Nothing hurts when Invulnerable */ if (borg_goi) return (TRUE); /* Lava */ if (feat == FEAT_DEEP_LAVA) { /* Immunity helps */ if (FLAG(bp_ptr, TR_IM_FIRE)) return (TRUE); /* Everything else hurts */ return (FALSE); } if (feat == FEAT_SHAL_LAVA) { /* Levitation helps */ if (FLAG(bp_ptr, TR_FEATHER)) return (TRUE); /* Immunity helps */ if (FLAG(bp_ptr, TR_IM_FIRE)) return (TRUE); /* Everything else hurts */ return (FALSE); } /* Swamp */ if (feat == FEAT_DEEP_SWAMP) { /* Immunity helps */ if (FLAG(bp_ptr, TR_IM_POIS)) return (TRUE); return (FALSE); } if (feat == FEAT_SHAL_SWAMP) { /* Immunity helps */ if (FLAG(bp_ptr, TR_IM_POIS)) return (TRUE); /* (temp) Resistance helps */ if (FLAG(bp_ptr, TR_WILD_WALK)) return (TRUE); /* Levitation helps */ if (FLAG(bp_ptr, TR_FEATHER)) return (TRUE); /* Shallow swamp does hurt */ return (FALSE); } /* Acid */ if (feat == FEAT_DEEP_ACID) { /* Immunity helps */ if (FLAG(bp_ptr, TR_IM_ACID)) return (TRUE); /* Everything else hurts */ return (FALSE); } if (feat == FEAT_SHAL_ACID) { /* Immunity helps */ if (FLAG(bp_ptr, TR_IM_ACID)) return (TRUE); /* Levitation helps */ if (FLAG(bp_ptr, TR_FEATHER)) return (TRUE); /* Everything else hurts */ return (FALSE); } /* Generally ok */ return (TRUE); } /* If the borg stands on something painfull he'd better move */ bool borg_flow_non_hurt(void) { int x, y; int c, b_c = 255; map_block *mb_ptr = map_loc(c_x, c_y); /* This doesn't hurt */ if (borg_on_safe_feat(mb_ptr->feat)) return (FALSE); /* Search outwards */ borg_flow_clear(); /* Enqueue the player's grid */ borg_flow_enqueue_grid(c_x, c_y); /* Spread, but do NOT optimize and allow painful feats */ borg_flow_spread(10, BORG_FLOW_FEAT_HURT); /* Scan the entire map */ MAP_ITT_START (mb_ptr) { /* Skip unknown grids */ if (!mb_ptr->feat) continue; /* Skip walls/doors */ if (borg_cave_wall_grid(mb_ptr)) continue; /* Skip painfull grids */ if (!borg_on_safe_feat(mb_ptr->feat)) continue; /* Acquire the cost */ c = mb_ptr->cost; /* Skip "unreachable" grids */ if (c >= b_c) continue; /* Remember this cost */ b_c = c; /* Remember this location */ MAP_GET_LOC(x, y); } MAP_ITT_END; /* Found no safe feat */ if (b_c == 255) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the grid */ borg_flow_enqueue_grid(x, y); /* Spread the flow */ borg_flow_spread(30, BORG_FLOW_OPTI_AVOID | BORG_FLOW_FEAT_HURT); /* Attempt to Commit the flow */ if (!borg_flow_commit("to a safe feat", GOAL_FEAT)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_FEAT)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to "flow" towards monsters to "kill" * But in a few phases, viewable, near and far. * Note that monsters under the player are always deleted */ bool borg_flow_kill(bool viewable, int nearness) { int i, x, y, p, j, b_j = -1; int b_stair = -1; bool borg_in_hall = FALSE; int hall_y, hall_x, hall_walls = 0; bool skip_monster = FALSE; map_block *mb_ptr; /* Efficiency -- Nothing to kill */ if (!borg_kills_cnt) return (FALSE); /* Don't chase down town monsters when you are just starting out */ if (bp_ptr->depth == 0 && bp_ptr->lev < 7) return (FALSE); /* YOU ARE NOT A WARRIOR!! DON'T ACT LIKE ONE!! */ if ((borg_class == CLASS_MAGE || borg_class == CLASS_HIGH_MAGE || borg_class == CLASS_MINDCRAFTER) && bp_ptr->lev < (bp_ptr->depth ? 35 : 5)) return (FALSE); /* Nothing found */ borg_temp_n = 0; /* check to see if in a hall, used later */ for (hall_x = -1; hall_x <= 1; hall_x++) { for (hall_y = -1; hall_y <= 1; hall_y++) { /* Acquire location */ x = hall_x + c_x; y = hall_y + c_y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; mb_ptr = map_loc(x, y); /* track walls */ if ( /* (mb_ptr->feat == FEAT_GLYPH) || (mb_ptr->feat == FEAT_MINOR_GLYPH) || */ ((mb_ptr->feat >= FEAT_MAGMA) && (mb_ptr->feat <= FEAT_PERM_SOLID))) { hall_walls++; } /* addem up */ if (hall_walls >= 5) borg_in_hall = TRUE; } } /* Check distance away from stairs, used later */ /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { x = track_less_x[i]; y = track_less_y[i]; /* How far is the nearest up stairs */ j = distance(c_y, c_x, y, x); /* skip the closer ones */ if (b_j >= j) continue; /* track it */ b_j = j; b_stair = i; } /* Scan the monster list */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Ignore multiplying monsters */ if (goal_ignoring && !bp_ptr->status.afraid && (FLAG(&r_info[kill->r_idx], RF_MULTIPLY))) continue; /* Avoid fighting if a scary guy is on the level */ if (scaryguy_on_level) continue; /* Don't chase our friends or pets */ if (kill->m_flags & (MONST_FRIEND | MONST_PET)) continue; /* Avoid multiplying monsters when low level */ if (bp_ptr->lev < 10 && (FLAG(&r_info[kill->r_idx], RF_MULTIPLY))) continue; /* Hack -- ignore Maggot until later. Player will chase Maggot * down all accross the screen waking up all the monsters. Then * he is stuck in a comprimised situation. */ if ((FLAG(&r_info[kill->r_idx], RF_UNIQUE)) && bp_ptr->depth == 0 && bp_ptr->lev < 5) continue; /* Access the location */ x = kill->x; y = kill->y; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Get the grid */ mb_ptr = map_loc(x, y); /* Require line of sight if requested */ if (viewable && !(mb_ptr->info & BORG_MAP_VIEW)) continue; /* Calculate danger */ borg_full_damage = FALSE; p = borg_danger_aux(x, y, 1, i, TRUE); borg_full_damage = FALSE; /* Hack -- Skip "deadly" monsters unless uniques */ if (bp_ptr->lev > 15 && (!FLAG(&r_info[kill->r_idx], RF_UNIQUE)) && p > avoidance / 2) continue; if (bp_ptr->lev <= 15 && p > avoidance / 3) continue; /* Skip ones that make me wander too far */ if ((b_stair != -1) && (bp_ptr->lev < 10)) { /* Check the distance of this monster to the stair */ j = distance(track_less_y[b_stair], track_less_x[b_stair], y, x); /* skip far away monsters while I am close to stair */ if (b_j <= bp_ptr->lev * 5 + 9 && j >= bp_ptr->lev * 5 + 9) continue; } /* Hack -- Avoid getting surrounded */ if (borg_in_hall && (FLAG(&r_info[kill->r_idx], RF_FRIENDS))) { /* check to see if monster is in a hall, */ for (hall_x = -1; hall_x <= 1; hall_x++) { for (hall_y = -1; hall_y <= 1; hall_y++) { /* Bounds checking */ if (!map_in_bounds(hall_x + x, hall_y + y)) continue; mb_ptr = map_loc(hall_x + x, hall_y + y); /* track walls */ if ( /* (mb_ptr->feat == FEAT_GLYPH) || (mb_ptr->feat == FEAT_MINOR_GLYPH) || */ ((mb_ptr->feat >= FEAT_MAGMA) && (mb_ptr->feat <= FEAT_PERM_SOLID))) { hall_walls++; } /* we want the monster to be in a hall also * * ######################## * ############ S ### * # @' SSS ### * # ########## SS### * # # # Ss### * # # ###### ###### * # # # # * Currently, we would like the borg to avoid * flowing to a situation like the one above. * We would like him to stay in the hall and * attack from a distance. One problem is the * lower case 's' in the corner, He will show * up as being in a corner, and the borg may * flow to it. Let's hope that is a rare case. * * The borg might flow to the 'dark' south exit * of the room. This would be dangerous for * him as well. */ /* add 'em up */ if (hall_walls < 4) { /* This monster is not in a hallway. * It may not be safe to fight. */ skip_monster = TRUE; } } } } /* skip certain ones */ if (skip_monster) continue; /* Careful -- Remember it */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; } /* Nothing to kill */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Look for something to kill */ for (i = 0; i < borg_temp_n; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(borg_temp_x[i], borg_temp_y[i]); } /* Spread the flow */ /* if we are not flowing toward monsters that we can see, make sure they */ /* are at least easily reachable. The second flag is whether or not */ /* to avoid unknown squares. This was for performance when we have ESP. */ borg_flow_spread(nearness, (viewable) ? BORG_FLOW_OPTIMIZE : BORG_FLOW_OPTI_AVOID); /* Attempt to Commit the flow */ if (!borg_flow_commit("kill", GOAL_KILL)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_KILL)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to "flow" towards objects to "take" * * Note that objects under the player are always deleted */ bool borg_flow_take(bool viewable, int nearness) { int i, x, y; int b_stair = -1, j, b_j = -1; map_block *mb_ptr; /* Efficiency -- Nothing to take */ if (!borg_takes_cnt) return (FALSE); /* Require one empty slot */ if (inven_num >= INVEN_PACK) return (FALSE); /* Nothing yet */ borg_temp_n = 0; /* Set the searching flag for low level borgs */ borg_needs_searching = TRUE; /* Check distance away from stairs, used later */ /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { x = track_less_x[i]; y = track_less_y[i]; /* How far is the nearest up stairs */ j = distance(c_y, c_x, y, x); /* skip the closer ones */ if (b_j >= j) continue; /* track it */ b_j = j; b_stair = i; } /* Scan the object list */ for (i = 1; i < borg_takes_nxt; i++) { borg_take *take = &borg_takes[i]; int a; bool item_bad; /* Skip dead objects */ if (!take->k_idx) continue; /* Access the location */ x = take->x; y = take->y; /* Skip ones that make me wander too far */ if ((b_stair != -1) && (bp_ptr->lev < 10)) { /* Check the distance of this 'take' to the stair */ j = distance(track_less_y[b_stair], track_less_x[b_stair], y, x); /* skip far away takes while I am close to stair */ if (b_j <= bp_ptr->lev * 5 + 9 && j >= bp_ptr->lev * 5 + 9) continue; } /* look to see if this is on the bad items list */ item_bad = FALSE; for (a = 0; a < bad_obj_n; a++) { if (x == bad_obj_x[a] && y == bad_obj_y[a]) item_bad = TRUE; } /* it is a bad item, do not track it */ if (item_bad) continue; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Get the grid */ mb_ptr = map_loc(x, y); /* Require line of sight if requested */ if (viewable && !(mb_ptr->info & BORG_MAP_VIEW)) continue; /* Require the item to be on a safe feat */ if (!borg_on_safe_feat(mb_ptr->feat)) continue; /* Careful -- Remember it */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; } /* Nothing to take */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Look for something to take */ for (i = 0; i < borg_temp_n; i++) { /* Enqueue the grid */ borg_flow_enqueue_grid(borg_temp_x[i], borg_temp_y[i]); } /* Spread the flow */ /* if we are not flowing toward items that we can see, make sure they */ /* are at least easily reachable. The second flag is weather or not */ /* to avoid unkown squares. This was for performance. */ borg_flow_spread(nearness, (viewable) ? BORG_FLOW_OPTIMIZE : BORG_FLOW_OPTI_AVOID); /* Attempt to Commit the flow */ if (!borg_flow_commit("item", GOAL_TAKE)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_TAKE)) return (FALSE); /* Success */ return (TRUE); } /* * Determine if a grid is "interesting" (and should be explored) * * A grid is "interesting" if it is a closed door, rubble, hidden treasure, * or a visible trap, or an "unknown" grid. * or a non-perma-wall adjacent to a perma-wall. (GCV) * * b_stair is the index to the closest upstairs. */ static bool borg_flow_dark_interesting(int x, int y, int b_stair) { int oy; int ox, i; map_block *mb_ptr; /* Hack ignore parameter */ (void)b_stair; /* Have the borg so some Searching */ borg_needs_searching = TRUE; #if 0 /* Skip ones that make me wander too far */ if ((b_stair != -1) && (bp_ptr->lev < 10)) { /* Check the distance of this grid to the stair */ j = distance(track_less_y[b_stair], track_less_x[b_stair], y, x); /* Distance of me to the stairs */ b_j = distance(c_y, c_x, track_less_y[b_stair], track_less_x[b_stair]); /* skip far away grids while I am close to stair */ if (b_j <= bp_ptr->lev * 5 + 9 && j >= bp_ptr->lev * 5 + 9) return (FALSE); } #endif /* 0 */ /* Bounds checking */ if (!map_in_bounds(x, y)) return (TRUE); /* Get the grid */ mb_ptr = map_loc(x, y); /* Explore unknown grids */ if (!mb_ptr->feat) return (TRUE); /* Efficiency -- Ignore "boring" grids */ if (mb_ptr->feat < FEAT_CLOSED) return (FALSE); /* Explore "known treasure" */ if ((mb_ptr->feat == FEAT_MAGMA_K) || (mb_ptr->feat == FEAT_QUARTZ_K)) { /* Do not disarm when confused */ if (bp_ptr->status.confused) return (FALSE); /* Not when darkened */ if (!bp_ptr->cur_lite) return (FALSE); /* Allow "stone to mud" ability */ if (borg_spell_legal(REALM_ARCANE, 2, 4) || borg_spell_legal(REALM_NATURE, 1, 0) || borg_spell_legal(REALM_CHAOS, 2, 3) || borg_mutation_check(MUT1_EAT_ROCK, TRUE) || borg_racial_check(RACE_HALF_GIANT, TRUE)) return (TRUE); /* * Do not dig unless we appear strong * enough to succeed or we have a digger */ if (bp_ptr->skill_dig > 40) { /* digging ought to work */ } else { return (FALSE); } /* Okay */ return (TRUE); } #if 0 /* "Vaults" Explore non perma-walls adjacent to a perma wall */ if (mb_ptr->feat == FEAT_WALL_EXTRA || mb_ptr->feat == FEAT_MAGMA || mb_ptr->feat == FEAT_QUARTZ) { /* Do not attempt when confused */ if (bp_ptr->status.confused) return (FALSE); /* hack and cheat. No vaults on this level */ if (!vault_on_level) return (FALSE); /* AJG Do not attempt on the edge */ if (map_in_bounds(x, y)) { /* scan the adjacent grids */ for (ox = -1; ox <= 1; ox++) { for (oy = -1; oy <= 1; oy++) { /* Bounds checking */ if (!map_in_bounds(ox + x, oy + y)) continue; /* Acquire location */ mb_ptr = map_loc(ox + x, oy + y); /* skip non perma grids wall */ if (mb_ptr->feat != FEAT_PERM_INNER) continue; /* Allow "stone to mud" ability */ if (borg_spell_legal(REALM_ARCANE, 2, 4) || borg_spell_legal(REALM_NATURE, 1, 0) || borg_spell_legal(REALM_CHAOS, 0, 6) || borg_mutation_check(MUT1_EAT_ROCK) || borg_racial_check(RACE_HALF_GIANT)) return (TRUE); /* * Do not dig unless we appear strong * enough to succeed or we have a digger */ if (bp_ptr->skill_dig > 40) { /* digging ought to work, proceed */ } else { continue; } if (bp_ptr->skill_dig < 40) return (FALSE); /* Glove up and dig in */ return (TRUE); } } } /* not adjacent to a GCV, Restore Grid */ mb_ptr = map_loc(x, y); } #endif /* 0 */ /* Explore "rubble" */ if (mb_ptr->feat == FEAT_RUBBLE) { return (TRUE); } /* Explore "Trees" somewhat */ if (mb_ptr->feat == FEAT_TREES) { /* Scan near trees for unknown grids */ /* AJG Do not attempt on the edge */ if (map_in_bounds(x, y)) { /* scan the adjacent grids */ for (ox = -1; ox <= 1; ox++) { for (oy = -1; oy <= 1; oy++) { /* Bounds checking */ if (!map_in_bounds(ox + x, oy + y)) continue; /* Acquire location */ mb_ptr = map_loc(ox + x, oy + y); /* look for Unknown grid */ if (!mb_ptr->feat) return (TRUE); } } } /* this forest is already explored */ return (FALSE); } /* Explore "closed doors" */ if (mb_ptr->feat == FEAT_CLOSED) { /* some closed doors leave alone */ if (breeder_level) { /* Did I close this one */ for (i = 0; i < track_door_num; i++) { /* mark as icky if I closed this one */ if ((track_door_x[i] == x) && (track_door_y[i] == y)) { /* not interesting */ return (FALSE); } } } /* this door should be ok to open */ return (TRUE); } /* Explore "visible traps" */ if (mb_ptr->trap) { /* Do not disarm when blind */ if (bp_ptr->status.blind) return (FALSE); /* Do not disarm when confused */ if (bp_ptr->status.confused) return (FALSE); /* Do not disarm when hallucinating */ if (bp_ptr->status.image) return (FALSE); /* Do not flow without lite */ if (!bp_ptr->cur_lite) return (FALSE); /* Do not disarm trap doors on level 99 */ if (bp_ptr->depth == 99 && mb_ptr->trap == FT_TRAP_DOOR) return (FALSE); /* Do not disarm when you could end up dead */ if (bp_ptr->chp < 60) return (FALSE); /* Do not disarm when clumsy */ if ((bp_ptr->skill_dis < 30) && (bp_ptr->lev < 20)) return (FALSE); if ((bp_ptr->skill_dis < 45) && (bp_ptr->lev < 10)) return (FALSE); /* NOTE: the flow code allows a borg to flow through a trap and so he may * still try to disarm one on his way to the other interesting grid. If mods * are made to the above criteria for disarming traps, then mods must also be * made to borg_flow_spread() */ /* Okay */ return (TRUE); } /* Ignore other grids */ return (FALSE); } /* * Determine if a grid is "reachable" (and can be explored) */ static bool borg_flow_dark_reachable(int x, int y) { int j; map_block *mb_ptr; /* Scan neighbors */ for (j = 0; j < 8; j++) { int y2 = y + ddy_ddd[j]; int x2 = x + ddx_ddd[j]; /* Bounds checking */ if (!map_in_bounds(x2, y2)) continue; /* Get the grid */ mb_ptr = map_loc(x2, y2); /* Skip unknown grids (important) */ if (!mb_ptr->feat) continue; /* Accept known floor grids */ if (borg_cave_floor_grid(mb_ptr)) return (TRUE); /* Accept Trees too */ if (mb_ptr->feat == FEAT_TREES) return (TRUE); if (borg_on_safe_feat(mb_ptr->feat)) return (TRUE); /* I can push pass friendly monsters */ if (mb_ptr->kill && (borg_kills[mb_ptr->kill].m_flags & (MONST_FRIEND | MONST_PET))) { return (TRUE); } } /* Failure */ return (FALSE); } /* * Place a "direct path" into the flow array, checking danger * * Modify the "cost" array in such a way that from any point on * one "direct" path from the player to the given grid, as long * as the rest of the path is "safe" and "clear", the Borg will * walk along the path to the given grid. * * This is used to move around town without looking like a drunk. */ void borg_flow_direct(int x, int y) { int n = 0; int x1, y1, x2, y2; int ay, ax; int shift; map_block *mb_ptr; /* Bounds checking */ if (!map_in_bounds(x, y)) return; mb_ptr = map_loc(x, y); /* Avoid icky grids */ if (mb_ptr->info & BORG_MAP_ICKY) return; /* Unknown */ if (!(mb_ptr->info & BORG_MAP_KNOW)) { /* Mark as known */ mb_ptr->info |= BORG_MAP_KNOW; /* Mark dangerous grids as icky */ if (borg_danger(x, y, 1, TRUE) > avoidance / 3) { /* Icky */ mb_ptr->info |= BORG_MAP_ICKY; /* Avoid */ return; } } /* Save the flow cost (zero) */ mb_ptr->cost = 1; /* Save "origin" */ y1 = y; x1 = x; /* Save "destination" */ y2 = c_y; x2 = c_x; /* Calculate distance components */ ay = (y2 < y1) ? (y1 - y2) : (y2 - y1); ax = (x2 < x1) ? (x1 - x2) : (x2 - x1); /* Path */ while (1) { /* Check for arrival at player */ if ((x == x2) && (y == y2)) return; /* Next */ n++; /* Move mostly vertically */ if (ay > ax) { /* Extract a shift factor XXX */ shift = (n * ax + (ay - 1) / 2) / ay; /* Sometimes move along the minor axis */ x = (x2 < x1) ? (x1 - shift) : (x1 + shift); /* Always move along major axis */ y = (y2 < y1) ? (y1 - n) : (y1 + n); } /* Move mostly horizontally */ else { /* Extract a shift factor XXX */ shift = (n * ay + (ax - 1) / 2) / ax; /* Sometimes move along the minor axis */ y = (y2 < y1) ? (y1 - shift) : (y1 + shift); /* Always move along major axis */ x = (x2 < x1) ? (x1 - n) : (x1 + n); } /* Bounds checking */ if (!map_in_bounds(x, y)) return; /* Access the grid */ mb_ptr = map_loc(x, y); if (borg_cave_wall_grid(mb_ptr)) { /* Only like 'diggable' things */ if (!((mb_ptr->feat >= FEAT_CLOSED) && (mb_ptr->feat <= FEAT_QUARTZ))) return; } /* Ignore certain "non-wall" grids */ if (!borg_on_safe_feat(mb_ptr->feat)) return; /* Abort at "icky" grids */ if (mb_ptr->info & BORG_MAP_ICKY) return; /* Analyze every grid once */ if (!(mb_ptr->info & BORG_MAP_KNOW)) { /* Mark as known */ mb_ptr->info |= BORG_MAP_KNOW; /* Avoid dangerous grids (forever) */ if (borg_danger(x, y, 1, TRUE) > avoidance / 3) { /* Mark as icky */ mb_ptr->info |= BORG_MAP_ICKY; /* Abort */ return; } } /* Abort "pointless" paths if possible */ if (mb_ptr->cost <= n) break; /* Save the new flow cost */ mb_ptr->cost = n; } } /* * Hack -- mark off the edges of a rectangle as "avoid" or "clear" */ static void borg_flow_border(int x1, int y1, int x2, int y2, bool stop) { int x, y; map_block *mb_ptr; if (stop) { /* Scan west/east edges */ for (y = y1; y <= y2; y++) { /* Avoid/Clear west edge */ if (!map_in_bounds(x1, y)) continue; mb_ptr = map_loc(x1, y); mb_ptr->info |= (BORG_MAP_ICKY | BORG_MAP_KNOW); /* Avoid/Clear east edge */ if (!map_in_bounds(x2, y)) continue; mb_ptr = map_loc(x2, y); mb_ptr->info |= (BORG_MAP_ICKY | BORG_MAP_KNOW); } /* Scan north/south edges */ for (x = x1; x <= x2; x++) { /* Avoid/Clear north edge */ if (!map_in_bounds(x, y1)) continue; mb_ptr = map_loc(x, y1); mb_ptr->info |= (BORG_MAP_ICKY | BORG_MAP_KNOW); /* Avoid/Clear south edge */ if (!map_in_bounds(x, y2)) continue; mb_ptr = map_loc(x, y2); mb_ptr->info |= (BORG_MAP_ICKY | BORG_MAP_KNOW); } } else { /* Scan west/east edges */ for (y = y1; y <= y2; y++) { /* Avoid/Clear west edge */ if (!map_in_bounds(x1, y)) continue; mb_ptr = map_loc(x1, y); mb_ptr->info &= ~(BORG_MAP_ICKY | BORG_MAP_KNOW); /* Avoid/Clear east edge */ if (!map_in_bounds(x2, y)) continue; mb_ptr = map_loc(x2, y); mb_ptr->info &= ~(BORG_MAP_ICKY | BORG_MAP_KNOW); } /* Scan north/south edges */ for (x = x1; x <= x2; x++) { /* Avoid/Clear north edge */ if (!map_in_bounds(x, y1)) continue; mb_ptr = map_loc(x, y1); mb_ptr->info &= ~(BORG_MAP_ICKY | BORG_MAP_KNOW); /* Avoid/Clear south edge */ if (!map_in_bounds(x, y2)) continue; mb_ptr = map_loc(x, y2); mb_ptr->info &= ~(BORG_MAP_ICKY | BORG_MAP_KNOW); } } } /* * Prepare to "flow" towards "interesting" grids (method 2) * * This function is only used when the player is at least 4 grids away * from the outer dungeon wall, to prevent any nasty memory errors. * * This function examines the grids just outside the torch-lit grids * for "unknown" grids, and flows directly towards them (one step). */ static bool borg_flow_dark_2(void) { int i, r; int x, y; map_block *mb_ptr; /* Hack -- not in town */ if (!bp_ptr->depth) return (FALSE); /* Set the searching flag for low level borgs */ borg_needs_searching = TRUE; /* Maximal radius */ r = bp_ptr->cur_lite + 1; /* Reset */ borg_temp_n = 0; /* Four directions */ for (i = 0; i < 4; i++) { y = c_y + ddy_ddd[i] * r; x = c_x + ddx_ddd[i] * r; /* Check legality */ if (!map_in_bounds(x, y)) continue; /* Acquire grid */ mb_ptr = map_loc(x, y); /* Require unknown */ if (mb_ptr->feat) continue; /* Require viewable */ if (!(mb_ptr->info & BORG_MAP_VIEW)) continue; /* if it makes me wander, skip it */ /* Careful -- Remember it */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; } /* Nothing */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Create paths to useful grids */ for (i = 0; i < borg_temp_n; i++) { y = borg_temp_y[i]; x = borg_temp_x[i]; #if 0 /* Create a path */ borg_flow_direct(x, y); #endif /* 0 */ borg_flow_enqueue_grid(x, y); } /* Spread the flow */ borg_flow_spread(5, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ /* Note was NULL */ if (!borg_flow_commit("dark-2", GOAL_DARK)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_DARK)) return (FALSE); /* Forget goal */ goal = GOAL_NONE; /* Success */ return (TRUE); } /* * Prepare to "flow" towards "interesting" grids (method 3) * * Note the use of a limit on the "depth" of the flow, and of the flag * which avoids "unknown" grids when calculating the flow, both of which * help optimize this function to only handle "easily reachable" grids. * * The "borg_temp" array is much larger than any "local region". */ static bool borg_flow_dark_3(int b_stair) { int i; int x, y; int x1, y1, x2, y2; /* Hack -- not in town */ if (!bp_ptr->depth) return (FALSE); /* Local region */ y1 = c_y - 4; x1 = c_x - 4; y2 = c_y + 4; x2 = c_x + 4; /* Reset */ borg_temp_n = 0; /* Examine the region */ for (y = y1; y <= y2; y++) { /* Examine the region */ for (x = x1; x <= x2; x++) { if (!map_in_bounds(x, y)) continue; /* Skip "boring" grids */ if (!borg_flow_dark_interesting(x, y, b_stair)) continue; /* Skip "unreachable" grids */ if (!borg_flow_dark_reachable(x, y)) continue; /* Careful -- Remember it */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; } } /* Nothing interesting */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < borg_temp_n; i++) { y = borg_temp_y[i]; x = borg_temp_x[i]; /* Enqueue the grid */ borg_flow_enqueue_grid(x, y); } /* Spread the flow (limit depth) */ borg_flow_spread(5, BORG_FLOW_OPTI_AVOID); /* Attempt to Commit the flow */ /* Note was NULL */ if (!borg_flow_commit("dark-3", GOAL_DARK)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_DARK)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to "flow" towards "interesting" grids (method 4) * * Note that we avoid grids close to the edge of the panel, since they * induce panel scrolling, which is "expensive" in terms of CPU usage, * and because this allows us to "expand" the border by several grids * to lay down the "avoidance" border in known legal grids. * * We avoid paths that would take us into different panels by setting * the "icky" flag for the "border" grids to prevent path construction, * and then clearing them when done, to prevent confusion elsewhere. * * The "borg_temp" array is large enough to hold one panel full of grids. */ static bool borg_flow_dark_4(int b_stair) { int i, x, y; int x1, y1, x2, y2; /* Hack -- not in town */ if (!bp_ptr->depth) return (FALSE); /* Local region */ y1 = c_y - 11; x1 = c_x - 11; y2 = c_y + 11; x2 = c_x + 11; /* Nothing yet */ borg_temp_n = 0; /* Examine the panel */ for (y = y1; y <= y2; y++) { /* Examine the panel */ for (x = x1; x <= x2; x++) { if (!map_in_bounds(x, y)) continue; /* Skip "boring" grids */ if (!borg_flow_dark_interesting(x, y, b_stair)) continue; /* Skip "unreachable" grids */ if (!borg_flow_dark_reachable(x, y)) continue; /* Careful -- Remember it */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; } } /* Nothing useful */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < borg_temp_n; i++) { y = borg_temp_y[i]; x = borg_temp_x[i]; /* Enqueue the grid */ borg_flow_enqueue_grid(x, y); } /* Expand borders */ y1--; x1--; y2++; x2++; /* Avoid the edges */ borg_flow_border(x1, y1, x2, y2, TRUE); /* Spread the flow (limit depth) */ borg_flow_spread(32, BORG_FLOW_OPTI_AVOID); /* Clear the edges */ borg_flow_border(x1, y1, x2, y2, FALSE); /* Attempt to Commit the flow */ if (!borg_flow_commit("dark-4", GOAL_DARK)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_DARK)) return (FALSE); /* Success */ return (TRUE); } /* * Prepare to "flow" towards "interesting" grids (method 5) */ static bool borg_flow_dark_5(int b_stair) { int i, x, y; map_block *mb_ptr; /* Hack -- not in town */ if (!bp_ptr->depth) return (FALSE); /* Nothing yet */ borg_temp_n = 0; /* Examine every "legal" grid */ MAP_ITT_START (mb_ptr) { /* Paranoia -- Check for overflow */ if (borg_temp_n == BORG_TEMP_MAX) continue; /* Get location */ MAP_GET_LOC(x, y); /* Skip "boring" grids */ if (!borg_flow_dark_interesting(x, y, b_stair)) continue; /* Skip "unreachable" grids */ if (!borg_flow_dark_reachable(x, y)) continue; /* Careful -- Remember it */ borg_temp_x[borg_temp_n] = x; borg_temp_y[borg_temp_n] = y; borg_temp_n++; } MAP_ITT_END; /* Nothing useful */ if (!borg_temp_n) return (FALSE); /* Clear the flow codes */ borg_flow_clear(); /* Enqueue useful grids */ for (i = 0; i < borg_temp_n; i++) { y = borg_temp_y[i]; x = borg_temp_x[i]; /* Enqueue the grid */ borg_flow_enqueue_grid(x, y); } /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTI_AVOID); /* Attempt to Commit the flow */ if (!borg_flow_commit("dark-5", GOAL_DARK)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_DARK)) return (FALSE); /* Success */ return (TRUE); } /* * Is this an acceptaple place to flow to? * Should be expanded so the borg can avoid nasty quests and dungeons */ static bool borg_flow_wild_check(int x, int y) { /* Is the x in bounds? */ if (x < 0 || x > max_wild - 2) return (FALSE); /* Is the y in bounds? */ if (y < 0 || y > max_wild - 2) return (FALSE); /* Hack! Check this location */ return (!(wild[y][x].done.info & WILD_INFO_SEEN)); } /* Prepare to flow somewhere in the wilderness */ bool borg_flow_dark_wild(void) { int x, y, side = 0; int loop_min, loop_max; int base_x = c_x / WILD_BLOCK_SIZE; int base_y = c_y / WILD_BLOCK_SIZE; bool found = FALSE; /* Is the borg in the wilderness? */ if (bp_ptr->depth || vanilla_town) return (FALSE); /* No exploring in the dark */ if (bp_ptr->hour < 6 || bp_ptr->hour > 17) return (FALSE); /* Does the borg already know where to go but it isn't there? */ if (goal == GOAL_DARK) { x = goal_explore_x; y = goal_explore_y; } else { /* * Try to find a dark spot on the overhead map. * The method is to draw an everwidening square around the current location * and check the locations on the sides of the square if they are explored. * To keep this search as quick as possible there is some trickery to * ensure that every spot on the map in checked only once. */ while (!found) { /* Enlarge the box */ side += 1; /* Has the borg explored the whole map? */ if (base_x - side < 0 && base_y - side < 0 && base_x + side > max_wild - 2 && base_y + side > max_wild - 2) return (FALSE); /* The upper side has a constant y */ y = base_y - side; /* If the y is on the map */ if (y >= 0) { /* Don't go out of bounds */ loop_min = MAX(0, base_x - side); loop_max = MIN(base_x + side, max_wild - 2); /* Check the upper side from left to right */ for (x = loop_min; x < loop_max && !found; x++) { found = borg_flow_wild_check(x, y); if (found) break; } } /* Step out */ if (found) break; /* The right side has a constant x */ x = base_x + side; /* If the x is on the map */ if (x < max_wild - 1) { /* Don't go out of bounds */ loop_min = MAX(0, base_y - side); loop_max = MIN(base_y + side, max_wild - 2); /* Check the right side from up to down */ for (y = loop_min; y < loop_max && !found; y++) { found = borg_flow_wild_check(x, y); if (found) break; } } /* Step out */ if (found) break; /* The lower side has a constant y */ y = base_y + side; /* If the y is on the map */ if (y < max_wild - 1) { /* Don't go out of bounds */ loop_min = MAX(0, base_x - side); loop_max = MIN(base_x + side, max_wild - 2); /* Check the lower side from right to left */ for (x = loop_max; x > loop_min && !found; x--) { found = borg_flow_wild_check(x, y); if (found) break; } } /* Step out */ if (found) break; /* The left side has a constant x */ x = base_x - side; /* If the x is on the map */ if (x >= 0) { /* Don't go out of bounds */ loop_min = MAX(0, base_y - side); loop_max = MIN(base_y + side, max_wild - 2); /* Check the left side from down to up */ for (y = loop_max; y > loop_min && !found; y--) { found = borg_flow_wild_check(x, y); if (found) break; } } } /* Failure */ if (!found) return (FALSE); /* Close by? */ if (ABS(base_x - x) < 5) { /* keep this c_x */ x = c_x; } /* Not close */ else { /* West? */ if (x < base_x) { /* Stay at a distance from the known/unknown edge */ x = x + 4; /* One foot into the new block */ x = WILD_BLOCK_SIZE * (x + 1) - 1; } /* East */ else { /* Stay at a distance from the known/unknown edge */ x = x - 4; /* One foot into the new block */ x = WILD_BLOCK_SIZE * x; } } /* Close by? */ if (ABS(base_y - y) < 5) { /* keep this c_y */ y = c_y; } /* Not close */ else { /* North? */ if (y < base_y) { /* Stay at a distance from the known/unknown edge */ y = y + 4; /* One foot into the new block */ y = WILD_BLOCK_SIZE * (y + 1) - 1; } /* South */ else { /* Stay at a distance from the known/unknown edge */ y = y - 4; /* One foot into the new block */ y = WILD_BLOCK_SIZE * y; } } } /* Enqueue the grid */ if (borg_flow_block(x, y, "unexplored wilderness", GOAL_DARK)) { /* Flag the global */ goal_explore_x = x; goal_explore_y = y; return (TRUE); } goal = GOAL_NONE; goal_explore_x = -1; goal_explore_y = -1; /* This flow is not possible */ return (FALSE); } /* Reset the flow in the wilderness, based on a goal */ void borg_flow_goal_wild(void) { int x, y; cptr reason; if (bp_ptr->depth) return; switch(goal) { case GOAL_SHOP: { x = borg_shops[goal_shop].x; y = borg_shops[goal_shop].y; reason = "reflowing to a shop"; break; } case GOAL_DARK: { x = goal_explore_x; y = goal_explore_y; reason = "reflowing the dark"; break; } case GOAL_TOWN: { x = borg_towns[goal_town].x; y = borg_towns[goal_town].y; reason = "reflowing to a town"; break; } case GOAL_CAVE: { x = borg_dungeons[goal_dungeon].x; y = borg_dungeons[goal_dungeon].y; reason = "reflowing to a dungeon"; break; } default: return; } (void)borg_flow_block(x, y, reason, goal); } /* * Prepare to "flow" towards "interesting" grids * * The "exploration" routines are broken into "near" and "far" * exploration, and each set is chosen via the flag below. */ bool borg_flow_dark(bool close) { int i; int x, y, j, b_j = -1; int b_stair = -1; /* Paranoia */ if (borg_flow_dark_interesting(c_x, c_y, -1)) { return (FALSE); } /* Check distance away from stairs, used later */ /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { x = track_less_x[i]; y = track_less_y[i]; /* How far is the nearest up stairs */ j = distance(c_y, c_x, y, x); /* skip the closer ones */ if (b_j >= j) continue; /* track it */ b_j = j; b_stair = i; } /* Near */ if (close) { /* Method 2 */ if (borg_flow_dark_2()) return (TRUE); /* Method 3 */ if (borg_flow_dark_3(b_stair)) return (TRUE); } /* Far */ else { /* Method 4 */ if (borg_flow_dark_4(b_stair)) return (TRUE); /* Method 5 */ if (borg_flow_dark_5(b_stair)) return (TRUE); } /* Fail */ return (FALSE); } /* * Hack -- spastic searching */ static s16b spastic_x; static s16b spastic_y; /* * Search carefully for secret doors and such */ bool borg_flow_spastic(bool bored) { int cost; int i, x, y, v; int b_x = c_x; int b_y = c_y; int b_v = -1; int j, b_j = 1; int b_stair = -1; map_block *mb_ptr; /* Hack -- not in town */ if (!bp_ptr->depth) return (FALSE); /* Not bored */ if (!bored) { /* Look around for danger */ int p = borg_danger(c_x, c_y, 1, TRUE); /* Avoid searching when in danger */ if (p > avoidance / 4) return (FALSE); } /* Check distance away from stairs, used later */ /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { x = track_less_x[i]; y = track_less_y[i]; /* How far is the nearest up stairs */ j = distance(c_y, c_x, y, x); /* skip the closer ones */ if (b_j >= j) continue; /* track it */ b_j = j; b_stair = i; } /* We have arrived */ if ((spastic_x == c_x) && (spastic_y == c_y)) { /* Cancel */ spastic_x = 0; spastic_y = 0; /* Take note */ borg_note("# Spastic Searching at (%d,%d)...", c_x, c_y); /* Count searching */ for (i = 0; i < 9; i++) { /* Extract the location */ int xx = c_x + ddx_ddd[i]; int yy = c_y + ddy_ddd[i]; /* Bounds checking */ if (!map_in_bounds(xx, yy)) continue; /* Current grid */ mb_ptr = map_loc(xx, yy); /* Tweak -- Remember the search */ if (mb_ptr->xtra < 100) mb_ptr->xtra += 5; } /* Tweak -- Search a little */ borg_keypress('0'); borg_keypress('5'); borg_keypress('s'); /* Success */ return (TRUE); } /* Reverse flow */ borg_flow_reverse(); /* Scan the entire map */ MAP_ITT_START (mb_ptr) { map_block *mb_array[8]; int wall = 0; int supp = 0; int diag = 0; byte xtra_val; /* Skip unknown grids */ if (!mb_ptr->feat) continue; /* Skip walls/doors */ if (borg_cave_wall_grid(mb_ptr)) continue; /* Acquire the cost */ cost = mb_ptr->cost; /* Skip "unreachable" grids */ if (cost >= 250) continue; xtra_val = mb_ptr->xtra; /* Tweak -- Limit total searches */ if (xtra_val >= 50) continue; if (xtra_val >= bp_ptr->lev * 5) continue; /* Limit initial searches until bored */ if (!bored && (xtra_val > 5)) continue; /* Acquire the location */ MAP_GET_LOC(x, y); /* Avoid searching detected sectors */ if (mb_ptr->detect & BORG_DETECT_DOOR) continue; /* Skip ones that make me wander too far */ if ((b_stair != -1) && (bp_ptr->lev < 10)) { /* Check the distance of this grid to the stair */ j = distance(track_less_y[b_stair], track_less_x[b_stair], y, x); /* Distance of me to the stairs */ b_j = distance(c_y, c_x, track_less_y[b_stair], track_less_x[b_stair]); /* skip far away grids while I am close to stair */ if (b_j <= bp_ptr->lev * 5 + 9 && j >= bp_ptr->lev * 5 + 9) continue; } /* Extract adjacent locations */ for (i = 0; i < 8; i++) { /* Extract the location */ int xx = x + ddx_ddd[i]; int yy = y + ddy_ddd[i]; /* Bounds checking */ if (map_in_bounds(xx, yy)) { /* Get the grid contents */ mb_array[i] = map_loc(xx, yy); } else { mb_array[i] = NULL; } } /* Count possible door locations */ for (i = 0; i < 4; i++) { mb_ptr = mb_array[i]; if (mb_ptr && mb_ptr->feat >= FEAT_WALL_EXTRA) wall++; } /* No possible secret doors */ if (wall < 1) continue; /* Count supporting evidence for secret doors */ for (i = 0; i < 4; i++) { mb_ptr = mb_array[i]; /* Rubble */ if (!mb_ptr || mb_ptr->feat == FEAT_RUBBLE) continue; /* Walls, Doors */ if (((mb_ptr->feat >= FEAT_SECRET) && (mb_ptr->feat <= FEAT_PERM_SOLID)) || ((mb_ptr->feat == FEAT_OPEN) || (mb_ptr->feat == FEAT_BROKEN)) || (mb_ptr->feat == FEAT_CLOSED)) { supp++; } } /* Count supporting evidence for secret doors */ for (i = 4; i < 8; i++) { mb_ptr = mb_array[i]; /* Rubble */ if (!mb_ptr || mb_ptr->feat == FEAT_RUBBLE) continue; /* Walls */ if (mb_ptr->feat >= FEAT_SECRET) { diag++; } } /* No possible secret doors */ if (diag < 2) continue; /* Tweak -- Reward walls, punish visitation and distance */ v = (supp * 500) + (diag * 100) - (xtra_val * 20) - (cost * 1); /* The grid is not searchable */ if (v <= 0) continue; /* Tweak -- Minimal interest until bored */ if (!bored && (v < 1500)) continue; /* Track "best" grid */ if ((b_v >= 0) && (v < b_v)) continue; /* Save the data */ b_v = v; b_x = x; b_y = y; } MAP_ITT_END; /* Clear the flow codes */ borg_flow_clear(); /* Hack -- Nothing found */ if (b_v < 0) return (FALSE); /* Memorize */ spastic_x = b_x; spastic_y = b_y; /* Enqueue the grid */ borg_flow_enqueue_grid(b_x, b_y); /* Spread the flow */ borg_flow_spread(250, BORG_FLOW_OPTIMIZE); /* Attempt to Commit the flow */ if (!borg_flow_commit("spastic", GOAL_XTRA)) return (FALSE); /* Take one step */ if (!borg_flow_old(GOAL_XTRA)) return (FALSE); /* Success */ return (TRUE); } /* Clear some bools that are used in the wilderness */ void borg_leave_surface(void) { int i; /* Only valid on the surface */ if (bp_ptr->depth) return; /* Clear town visits */ for (i = 0; i < borg_town_num; i++) borg_towns[i].visit = FALSE; /* Clear shop visits */ for (i = 0; i < borg_shop_num; i++) borg_shops[i].visit = FALSE; /* Into which dungeon? */ borg_dungeon_remember(TRUE); } /* * Initialize this file */ void borg_init_6(void) { /* Nothing */ } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg7.c0000644000000000000000000022573210250356275013525 0ustar rootroot/* File: zborg7.c */ /* Purpose: High level functions for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zborg7.h" /* * This file handles various high level inventory related goals. * * Problems: * Use "time stamps" (and not "random" checks) for several routines, * including "kill junk" and "wear stuff", and maybe even "free space". * But be careful with the "free space" routine, wear stuff first. * Make sure nothing is "destroyed" if we do not do them every turn. * Consider some special routines in stores (and in the home). * * Hack -- We should perhaps consider wearing "harmless" items into empty * slots when in the dungeon, to allow rings/amulets to be brought back up * to town to be sold. * * We should take account of possible combinations of equipment. This may * be a potentially expensive computation, but could be done occasionally. * It is important to use a "state-less" formula to allow the exchange to * be spread over multiple turns. * * Hack -- We should attempt to only collect non-discounted items, at least * for the "expensive" slots, such as books, since we do not want to lose * value due to stacking. We seem to sell the non-discounted items first, * and to buy the discounted items first, since they are cheap. Oh well, * we may just be stuck with using discounted books. Unless we actually * do correct "combining" in the simulations, and reward free slots. Ick! * * XXX XXX XXX We really need a better "twitchy" function. * * XXX XXX XXX We need a better "flee this level" function * * XXX XXX XXX We need to stockpile possible useful items at home. * * XXX XXX XXX Perhaps we could simply maintain a list of abilities * that we might need at some point, such as the ability to identify, and * simply allow the Borg to "sell" items to the home which satisfy this * desire for "abilities". * * XXX XXX XXX Also, we should probably attempt to track the "best" item * in the home for each equipment slot, using some form of heuristic, and * reward that item based on its power, so that the Borg would always * have a "backup" item in case of disenchantment. * * XXX XXX XXX Also, we could reward equipment based on possible enchantment, * up to the maximal amount available in the home, which would induce item * switching when the item could be enchanted sufficiently. * * Fleeing from fast spell-casters is probably not a very smart idea, nor is * fleeing from fast monsters, nor is attempting to clear a room full of fast * moving breeding monsters, such as lice. */ /* * Hack -- importance of the various "level feelings" * Try to explore the level for at least this many turns */ static s16b value_feeling[] = { 500, 8000, 8000, 6000, 4000, 2000, 1000, 800, 600, 400, 200, 0 }; /* * Use things in a useful, but non-essential, manner */ bool borg_use_things(void) { int i; /* Quaff experience restoration potion */ if (bp_ptr->status.fixexp && (borg_activate(BORG_ACT_RESTORE_LIFE) || borg_spell(REALM_LIFE, 3, 3) || borg_spell(REALM_DEATH, 1, 7) || borg_quaff_potion(SV_POTION_RESTORE_EXP) || borg_racial(RACE_AMBERITE_POWER2))) { return (TRUE); } /* just drink the stat gains, at this dlevel we wont need cash */ if (borg_quaff_potion(SV_POTION_INC_STR) || borg_quaff_potion(SV_POTION_INC_INT) || borg_quaff_potion(SV_POTION_INC_WIS) || borg_quaff_potion(SV_POTION_INC_DEX) || borg_quaff_potion(SV_POTION_INC_CON) || borg_quaff_potion(SV_POTION_INC_CHR)) { return (TRUE); } /* Quaff potions of "restore" stat if needed */ if ((bp_ptr->status.fixstat[A_STR] && (borg_quaff_potion(SV_POTION_RES_STR) || borg_zap_rod(SV_ROD_RESTORATION) || borg_activate(BORG_ACT_RESTORATION) || borg_eat_food(SV_FOOD_RESTORE_STR) || borg_eat_food(SV_FOOD_RESTORING))) || (bp_ptr->status.fixstat[A_INT] && (borg_quaff_potion(SV_POTION_RES_INT) || borg_zap_rod(SV_ROD_RESTORATION) || borg_activate(BORG_ACT_RESTORATION) || borg_eat_food(SV_FOOD_RESTORING))) || (bp_ptr->status.fixstat[A_WIS] && (borg_quaff_potion(SV_POTION_RES_WIS) || borg_zap_rod(SV_ROD_RESTORATION) || borg_activate(BORG_ACT_RESTORATION) || borg_eat_food(SV_FOOD_RESTORING))) || (bp_ptr->status.fixstat[A_DEX] && (borg_quaff_potion(SV_POTION_RES_DEX) || borg_zap_rod(SV_ROD_RESTORATION) || borg_activate(BORG_ACT_RESTORATION) || borg_eat_food(SV_FOOD_RESTORING))) || (bp_ptr->status.fixstat[A_CON] && (borg_quaff_potion(SV_POTION_RES_CON) || borg_zap_rod(SV_ROD_RESTORATION) || borg_activate(BORG_ACT_RESTORATION) || borg_eat_food(SV_FOOD_RESTORE_CON) || borg_eat_food(SV_FOOD_RESTORING))) || (bp_ptr->status.fixstat[A_CHR] && borg_quaff_potion(SV_POTION_RES_CHR))) { return (TRUE); } /* Use some items right away */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip empty / unaware items */ if (!l_ptr->k_idx) continue; /* Process "force" items */ switch (l_ptr->tval) { case TV_POTION: { /* Check the scroll */ switch (k_info[l_ptr->k_idx].sval) { case SV_POTION_ENLIGHTENMENT: { /* Never quaff these in town */ if (!bp_ptr->depth) break; /* fall through */ } case SV_POTION_AUGMENTATION: case SV_POTION_EXPERIENCE: { /* Try quaffing the potion */ if (borg_quaff_potion(k_info[l_ptr->k_idx].sval)) return (TRUE); break; } } break; } case TV_SCROLL: { /* Hack -- check Blind/Confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) break; /* XXX XXX XXX Dark */ /* Check the scroll */ switch (k_info[l_ptr->k_idx].sval) { case SV_SCROLL_MAPPING: case SV_SCROLL_DETECT_TRAP: case SV_SCROLL_DETECT_DOOR: case SV_SCROLL_ACQUIREMENT: case SV_SCROLL_STAR_ACQUIREMENT: { /* Never read these in town */ if (!bp_ptr->depth) break; /* Try reading the scroll */ if (borg_read_scroll(k_info[l_ptr->k_idx].sval)) return (TRUE); break; } } break; } } } /* Eat food */ if (bp_ptr->status.hungry) { /* Attempt to satisfy hunger */ if (borg_activate(BORG_ACT_SATISFY) || borg_spell_fail(REALM_LIFE, 0, 7, 40) || borg_spell_fail(REALM_ARCANE, 2, 6, 40) || borg_spell_fail(REALM_NATURE, 0, 3, 40) || borg_eat_food(SV_FOOD_BISCUIT) || borg_eat_food(SV_FOOD_JERKY) || borg_eat_food(SV_FOOD_SLIME_MOLD) || borg_eat_food(SV_FOOD_PINT_OF_ALE) || borg_eat_food(SV_FOOD_PINT_OF_WINE) || borg_activate(BORG_ACT_CREATE_FOOD) || borg_racial(RACE_HOBBIT) || borg_eat_food(SV_FOOD_RATION) || borg_read_scroll(SV_SCROLL_SATISFY_HUNGER) || borg_eat_food(SV_FOOD_WAYBREAD)) { return (TRUE); } } /* Nothing to do */ return (FALSE); } /* * Refuel, call lite, detect traps/doors/walls/evil, etc * * Note that we refuel whenever our lite starts to get low. * * Note that we detect traps/doors/walls/evil at least once in each * panel, as soon as possible after entering a new panel. * * Note that we call lite whenever the current grid is dark, and * all the grids touching the current grid diagonally are known * floors, which catches all rooms, including "checkerboard" rooms, * and only occasionally calls lite in corridors, and then only once. * * Note that we also sometimes call lite whenever we are using a * lantern or artifact lite, and when all of the grids in the box * of grids containing the maximal torch-lit region (the 5x5 or 7x7 * region centered at the player) are non-glowing floor grids, and * when at least one of them is known to be "dark". This catches * most of the "probable rooms" before the standard check succeeds. * * We use the special "SELF" messages to "borg_react()" to delay the * processing of "detection" and "call lite" until we know if it has * worked or not. * * The matching function borg_check_lite_only is used only with resting * to heal. I don't want him teleporting into a room, resting to heal while * there is a dragon sitting in a dark corner waiting to breathe on him. * So now he will check for lite. * */ bool borg_check_lite(void) { int i, x, y; int corners, floors; map_block *mb_ptr = map_loc(c_x, c_y); bool do_lite; bool do_trap; bool do_door; bool do_wall; bool do_evil; /* Never when comprimised, save your mana */ if (bp_ptr->status.blind || bp_ptr->status.confused || bp_ptr->status.image || bp_ptr->status.poisoned || bp_ptr->status.cut || bp_ptr->status.weak) return (FALSE); /* Start */ do_trap = FALSE; /* Determine if we need to detect traps */ if (!(mb_ptr->detect & BORG_DETECT_TRAP)) do_trap = TRUE; /* Hack -- check traps every few turns anyway */ if (!when_detect_traps || (borg_t - when_detect_traps >= 183)) do_trap = TRUE; /* Start */ do_door = FALSE; /* Determine if we need to detect doors */ if (!(mb_ptr->detect & BORG_DETECT_DOOR)) do_door = TRUE; /* Hack -- check doors every few turns anyway */ if (!when_detect_doors || (borg_t - when_detect_doors >= 731)) do_door = TRUE; /* Start */ do_wall = FALSE; /* Determine if we need to detect walls */ if (!(mb_ptr->detect & BORG_DETECT_WALL)) do_wall = TRUE; /* Hack -- check walls every few turns anyway */ if (!when_detect_walls || (borg_t - when_detect_walls >= 937)) do_wall = TRUE; /* Start */ do_evil = FALSE; /* Determine if we need to detect evil */ if (!(mb_ptr->detect & BORG_DETECT_EVIL)) do_evil = TRUE; /* Hack -- check evil every few turns anyway- more fq if low level */ if (!when_detect_evil || (borg_t - when_detect_evil >= 183 - (20 - bp_ptr->max_lev))) do_evil = TRUE; /* Dont bother if I have ESP */ if (FLAG(bp_ptr, TR_TELEPATHY)) do_evil = FALSE; /* Do not do these if monsters near. Save mana */ if (!borg_check_rest()) { do_trap = FALSE; do_door = FALSE; do_wall = FALSE; do_evil = FALSE; } /*** Do Things ***/ /* Hack -- find traps and doors and evil */ if ((do_trap || do_door || do_evil) && ((!when_detect_traps || (borg_t - when_detect_traps >= 5)) || (!when_detect_evil || (borg_t - when_detect_evil >= 5)) || (!when_detect_doors || (borg_t - when_detect_doors >= 5)))) { /* Check for traps and doors and evil */ if (borg_zap_rod(SV_ROD_DETECTION) || borg_activate(BORG_ACT_DETECTION) || borg_spell_fail(REALM_SORCERY, 1, 6, 20) || borg_spell_fail(REALM_ARCANE, 3, 5, 20) || borg_spell_fail(REALM_TRUMP, 3, 0, 20) || borg_spell_fail(REALM_NATURE, 1, 2, 20)) { borg_note("# Checking for traps, doors, and evil."); borg_react("SELF:TDE", "SELF:TDE"); when_detect_traps = borg_t; when_detect_doors = borg_t; when_detect_evil = borg_t; return (TRUE); } } /* Hack -- find traps and doors */ if ((do_trap || do_door) && ((!when_detect_traps || (borg_t - when_detect_traps >= 5)) || (!when_detect_doors || (borg_t - when_detect_doors >= 5)))) { /* Check for traps and doors */ if (borg_activate(BORG_ACT_CLAIRVOYANCE) || borg_spell_fail(REALM_LIFE, 0, 5, 20) || borg_spell_fail(REALM_SORCERY, 0, 2, 20) || borg_spell_fail(REALM_ARCANE, 1, 0, 20) || borg_spell_fail(REALM_NATURE, 0, 2, 20) || borg_racial(RACE_DWARF) || borg_racial(RACE_NIBELUNG)) { borg_note("# Checking for traps and doors."); borg_react("SELF:both", "SELF:both"); when_detect_traps = borg_t; when_detect_doors = borg_t; return (TRUE); } } /* Hack -- find traps */ if (do_trap && (!when_detect_traps || (borg_t - when_detect_traps >= 7))) { /* Check for traps */ if (borg_activate(BORG_ACT_DETECT_TRAP_DOOR) || borg_read_scroll(SV_SCROLL_DETECT_TRAP) || borg_use_staff(SV_STAFF_DETECT_TRAP) || borg_zap_rod(SV_ROD_DETECT_TRAP)) { borg_note("# Checking for traps."); borg_react("SELF:trap", "SELF:trap"); when_detect_traps = borg_t; return (TRUE); } } /* Hack -- find doors */ if (do_door && (!when_detect_doors || (borg_t - when_detect_doors >= 9))) { /* Check for doors */ if (borg_activate(BORG_ACT_DETECT_TRAP_DOOR) || borg_read_scroll(SV_SCROLL_DETECT_DOOR) || borg_use_staff(SV_STAFF_DETECT_DOOR) || borg_zap_rod(SV_ROD_DETECT_DOOR)) { borg_note("# Checking for doors."); borg_react("SELF:door", "SELF:door"); when_detect_doors = borg_t; return (TRUE); } } /* Hack -- find walls */ if (do_wall && (!when_detect_walls || (borg_t - when_detect_walls >= 15))) { /* Check for walls */ if (borg_activate(BORG_ACT_MAGIC_MAPPING) || borg_read_scroll(SV_SCROLL_MAPPING) || borg_use_staff(SV_STAFF_MAPPING) || borg_zap_rod(SV_ROD_MAPPING) || borg_spell(REALM_SORCERY, 1, 0) || borg_spell(REALM_NATURE, 1, 2) || borg_mindcr(MIND_PRECOGNIT, 20)) { borg_note("# Checking for walls."); borg_react("SELF:wall", "SELF:wall"); when_detect_walls = borg_t; return (TRUE); } } /* Hack -- find evil */ if (do_evil && (!when_detect_evil || (borg_t - when_detect_evil >= 9))) { /* Check for monsters */ if (borg_activate(BORG_ACT_DETECT_EVIL) || borg_activate(BORG_ACT_DETECT_MONSTERS) || borg_use_staff(SV_STAFF_DETECT_EVIL) || borg_spell_fail(REALM_NATURE, 0, 0, 20) || borg_spell_fail(REALM_ARCANE, 0, 3, 20) || borg_spell_fail(REALM_SORCERY, 0, 0, 20) || borg_spell_fail(REALM_DEATH, 0, 2, 20) || borg_spell_fail(REALM_LIFE, 0, 0, 20) || borg_spell_fail(REALM_DEATH, 0, 0, 20) || borg_racial(RACE_GHOUL_POWER2)) { borg_note("# Checking for monsters."); borg_react("SELF:evil", "SELF:evil"); when_detect_evil = borg_t; return (TRUE); } } /* Start */ do_lite = FALSE; corners = 0; floors = 0; /* Scan diagonal neighbors */ for (i = 4; i < 8; i++) { /* Get location */ x = c_x + ddx_ddd[i]; y = c_y + ddy_ddd[i]; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Get grid */ mb_ptr = map_loc(x, y); /* Location must be known */ if (!mb_ptr->feat) corners++; /* Location must not be a wall/door */ if (borg_cave_wall_grid(mb_ptr)) corners++; } /* Add them up */ if (corners <= 2) do_lite = TRUE; /* Hack */ if (do_lite && (bp_ptr->cur_lite >= 2) && (randint0(100) < 90)) { floors = 0; /* Scan the "local" grids (5x5) 2 same as torch grid */ for (y = c_y - 2; y <= c_y + 2; y++) { /* Scan the "local" grids (5x5) */ for (x = c_x - 2; x <= c_x + 2; x++) { if (!map_in_bounds(x, y)) continue; /* Get grid */ mb_ptr = map_loc(x, y); /* Location must be a lit floor */ if (mb_ptr->flags & MAP_SEEN) floors++; /* Location must not be glowing */ if (mb_ptr->flags & MAP_GLOW) floors--; /* Location must not be a wall/door */ if (borg_cave_wall_grid(mb_ptr)) floors--; } } } /* add them up */ if (floors <= 11) do_lite = FALSE; /* Vampires need to be careful for Light */ if (FLAG(bp_ptr, TR_HURT_LITE) && !FLAG(bp_ptr, TR_RES_LITE)) do_lite = FALSE; /* Hack -- call lite */ if (do_lite && (!when_call_lite || (borg_t - when_call_lite >= 7))) { /* Call light */ if (borg_activate(BORG_ACT_LIGHT) || borg_zap_rod(SV_ROD_ILLUMINATION) || borg_use_staff(SV_STAFF_LITE) || borg_read_scroll(SV_SCROLL_LIGHT) || borg_spell(REALM_ARCANE, 0, 5) || borg_spell(REALM_CHAOS, 0, 2) || borg_spell(REALM_NATURE, 0, 4) || borg_spell(REALM_SORCERY, 0, 3) || borg_spell(REALM_LIFE, 0, 4) || borg_mutation(MUT1_ILLUMINE)) { borg_note("# Illuminating the room"); borg_react("SELF:lite", "SELF:lite"); when_call_lite = borg_t; return (TRUE); } } /* What to do when the borg has no light */ if (!bp_ptr->cur_lite) { /* Set to default */ do_lite = FALSE; /* Check all the surrounding spots */ for (x = c_x - 1; x <= c_x + 1; x++) { for (y = c_y - 1; y <= c_y + 1; y++) { /* Check if it is on the map */ if (!map_in_bounds(x, y)) continue; /* Is it a dark spot */ if (!(map_loc(x, y)->flags & MAP_GLOW)) { /* Think about lighting up */ do_lite = TRUE; /* Remember where */ g_x = x; g_y = y; } } } if (do_lite) { /* Can the borg cast a light area? */ if (borg_zap_rod(SV_ROD_ILLUMINATION) || borg_use_staff(SV_STAFF_LITE) || borg_read_scroll(SV_SCROLL_LIGHT) || borg_spell(REALM_ARCANE, 0, 5) || borg_spell(REALM_CHAOS, 0, 2) || borg_spell(REALM_NATURE, 0, 4) || borg_spell(REALM_SORCERY, 0, 3) || borg_spell(REALM_LIFE, 0, 4) || borg_mutation(MUT1_ILLUMINE)) { /* making lite */ borg_note("# Trying to light the whole dungeon"); return (TRUE); } /* Can the borg cast a beam of light */ if (borg_spell_okay(REALM_NATURE, 1, 4) || borg_spell_okay(REALM_ARCANE, 2, 5) || borg_equips_rod(SV_ROD_LITE) || borg_equips_wand(SV_WAND_LITE)) { /* Release target */ borg_keypress('*'); borg_keypress(ESCAPE); /* Can the borg cast a beam of light */ if (borg_spell(REALM_NATURE, 1, 4) || borg_spell(REALM_ARCANE, 2, 5) || borg_zap_rod(SV_ROD_LITE) || borg_aim_wand(SV_WAND_LITE)) { /* making lite */ borg_note("# Trying to light the whole dungeon"); /* show me the way */ borg_keypress(I2D(borg_extract_dir(c_x, c_y, g_x, g_y))); return (TRUE); } } } } /* Hack -- Wizard Lite */ if (TRUE && (!when_wizard_lite || (borg_t - when_wizard_lite >= 1000))) { /* Wizard lite */ if (borg_activate(BORG_ACT_CLAIRVOYANCE) || borg_spell(REALM_ARCANE, 3, 7) || borg_spell(REALM_SORCERY, 3, 3) || borg_spell(REALM_NATURE, 3, 5)) { borg_note("# Illuminating the dungeon"); /* borg_react("SELF:wizard lite", "SELF:wizard lite"); */ when_wizard_lite = borg_t; return (TRUE); } } /* Oops */ return (FALSE); } bool borg_check_lite_only(void) { int i, x, y; int corners, floors; map_block *mb_ptr; bool do_lite; /* Never in town */ if (!bp_ptr->depth) return (FALSE); /* Never when blind or hallucinating */ if (bp_ptr->status.blind || bp_ptr->status.image) return (FALSE); /* XXX XXX XXX Dark */ /* Start */ do_lite = FALSE; corners = 0; floors = 0; /* Scan diagonal neighbors */ for (i = 4; i < 8; i++) { /* Get location */ x = c_x + ddx_ddd[i]; y = c_y + ddy_ddd[i]; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Get grid */ mb_ptr = map_loc(x, y); /* Location must be known */ if (!mb_ptr->feat) corners++; /* Location must not be a wall/door */ if (borg_cave_wall_grid(mb_ptr)) corners++; } /* Add them up ..2 */ if (corners <= 2) do_lite = TRUE; /* Hack */ if (do_lite && (bp_ptr->cur_lite >= 2) && (randint0(100) < 90)) { floors = 0; /* Scan the "local" grids (5x5) 2 same as torch grid */ for (y = c_y - 2; y <= c_y + 2; y++) { /* Scan the "local" grids (5x5) */ for (x = c_x - 2; x <= c_x + 2; x++) { if (!map_in_bounds(x, y)) continue; /* Get grid */ mb_ptr = map_loc(x, y); /* Location must be a lit floor */ if (mb_ptr->flags & MAP_SEEN) floors++; /* Location must not be glowing */ if (mb_ptr->flags & MAP_GLOW) floors--; /* Location must not be a wall/door */ if (borg_cave_wall_grid(mb_ptr)) floors--; } } } /* add them up */ if (floors <= 11) do_lite = FALSE; /* Hack -- call lite */ if (do_lite && (!when_call_lite || (borg_t - when_call_lite >= 7))) { /* Call light */ if (borg_activate(BORG_ACT_LIGHT) || borg_zap_rod(SV_ROD_ILLUMINATION) || borg_use_staff(SV_STAFF_LITE) || borg_read_scroll(SV_SCROLL_LIGHT) || borg_spell(REALM_ARCANE, 0, 5) || borg_spell(REALM_CHAOS, 0, 2) || borg_spell(REALM_NATURE, 0, 4) || borg_spell(REALM_SORCERY, 0, 3) || borg_spell(REALM_LIFE, 0, 4) || borg_mutation(MUT1_ILLUMINE)) { borg_note("# Illuminating the room prior to resting"); borg_react("SELF:lite", "SELF:lite"); when_call_lite = borg_t; /* dont rest. call light instead */ return (TRUE); } } /* Hack -- Wizard Lite */ if (TRUE && (!when_wizard_lite || (borg_t - when_wizard_lite >= 1000))) { /* Wizard lite */ if (borg_activate(BORG_ACT_CLAIRVOYANCE) || borg_spell(REALM_ARCANE, 3, 7) || borg_spell(REALM_SORCERY, 3, 3) || borg_spell(REALM_NATURE, 3, 5)) { borg_note("# Illuminating the dungeon prior to resting"); /* borg_react("SELF:wizard lite", "SELF:wizard lite"); */ when_wizard_lite = borg_t; return (TRUE); } } /* nothing to light up. OK to rest. */ return (FALSE); } /* * Enchant armor with the lowest AC. This routine doesn't use the spell when * the borg is in the dungeon because down there he has better things to do. */ static bool borg_enchant_to_a(bool scroll_only) { int i, slot; /* What is the best item? */ slot = borg_notice_enchant_ac(); /* No suitable item */ if (slot == -1) return (FALSE); /* Enchant it */ if (borg_read_scroll(SV_SCROLL_STAR_ENCHANT_ARMOR) || borg_read_scroll(SV_SCROLL_ENCHANT_ARMOR) || (!bp_ptr->depth && !scroll_only && borg_spell_fail(REALM_SORCERY, 3, 5, 40))) { /* * Find out if the prompt is at Inven or Equip by checking if * there is armour in the inventory. If there is then the prompt * is at Inven and has to be moved to Equip. */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Is this item is enchantable? */ if (l_ptr->tval >= TV_BOOTS && l_ptr->tval <= TV_DRAG_ARMOR) { /* Goto the equipment */ borg_keypress('/'); break; } } /* Tell the world */ borg_note("# Enchanting %s (%c)", equipment[slot].o_name, I2A(slot)); /* Choose that item */ borg_keypress(I2A(slot)); /* Success */ return (TRUE); } /* Nothing to do */ return (FALSE); } /* * Enchant weapons to hit */ static bool borg_enchant_to_h(void) { int i, slot; bool inven = FALSE; /* What is the best item? */ slot = borg_notice_enchant_hit(&inven); /* No suitable item */ if (slot == -1) return (FALSE); /* Enchant it */ if (borg_read_scroll(SV_SCROLL_ENCHANT_WEAPON_TO_HIT)) { if (inven) { /* Tell the world */ borg_note("# Enchanting %s (%c)", inventory[slot].o_name, I2A(slot)); } else { /* Tell the world */ borg_note("# Enchanting %s (%c)", equipment[slot].o_name, I2A(slot)); /* * Find out if the prompt is at Inven or Equip by checking if * there is a weapon or ammo in the inventory. If there is * then the prompt is at Inven and has to be moved to Equip. */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Is this item is enchantable? */ if (l_ptr->tval >= TV_SHOT && l_ptr->tval <= TV_SWORD) { /* Goto the equipment */ borg_keypress('/'); break; } } } /* Choose that item */ borg_keypress(I2A(slot)); /* Success */ return (TRUE); } /* Nothing to do */ return (FALSE); } /* * Enchant weapons to dam */ static bool borg_enchant_to_d(void) { int i, slot; bool inven = FALSE; /* What is the best item? */ slot = borg_notice_enchant_dam(&inven); /* No suitable item */ if (slot == -1) return (FALSE); /* Enchant it */ if (borg_read_scroll(SV_SCROLL_ENCHANT_WEAPON_TO_DAM)) { if (inven) { /* Tell the world */ borg_note("# Enchanting %s (%c)", inventory[slot].o_name, I2A(slot)); } else { /* Tell the world */ borg_note("# Enchanting %s (%c)", equipment[slot].o_name, I2A(slot)); /* * Find out if the prompt is at Inven or Equip by checking if * there is a weapon or ammo in the inventory. If there is * then the prompt is at Inven and has to be moved to Equip. */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Is this item is enchantable? */ if (l_ptr->tval >= TV_SHOT && l_ptr->tval <= TV_SWORD) { /* Goto the equipment */ borg_keypress('/'); break; } } } /* Choose that item */ borg_keypress(I2A(slot)); /* Success */ return (TRUE); } /* Nothing to do */ return (FALSE); } /* * Enchant weapons to dam and to hit. * Target a wielded weapon if it has a bonus < 10 * Otherwise target arrows. * Forget about enchanting arrows with the spell if its bonuses are high */ static bool borg_enchant_to_w(bool scroll_only) { int i, slot, slot_d, slot_h; bool inven, inven_d, inven_h; bool scroll = borg_read_scroll_fail(SV_SCROLL_STAR_ENCHANT_WEAPON); /* Can we enchant at all */ if (scroll_only) { /* Is there a scroll */ if (!scroll) return (FALSE); } else { /* Is there a scroll or the spell */ if (!scroll && !borg_spell_fail(REALM_SORCERY, 3, 4, 40)) return (FALSE); } /* What is the item with the lowest dam? */ slot_d = borg_notice_enchant_dam(&inven_d); /* What is the item with the lowest hit? */ slot_h = borg_notice_enchant_dam(&inven_h); /* Is the item with the lowest dam bonus in the inventory? */ if (inven_d) { /* Is the item with the lowest hit bonus in the inventory? */ if (inven_h) { /* Both hit and dam items are in the inventory */ inven = TRUE; /* Which has the lower bonus? */ if (inventory[slot_d].to_d < inventory[slot_h].to_h) slot = slot_d; else slot = slot_h; } /* Equipment goes first */ else { inven = FALSE; slot = slot_h; } } /* The item with the lowest dam bonus is in the equipment */ else { /* Target the equipment */ inven = FALSE; /* Or maybe the hit bonus? */ if (!inven_h && equipment[slot_d].to_h < equipment[slot_h].to_d) { /* So it is the hit bonus */ slot = slot_h; } else { /* The dam bonus is the lowest */ slot = slot_d; } } /* No suitable item */ if (slot == -1) return (FALSE); /* If you are using the spell */ if (!scroll) { /* Don't bother enchanting arrows up to the max with the spell */ if (inven && inventory[slot].to_h > 10 && inventory[slot].to_d > 15) return (FALSE); /* Don't bother enchanting equipped items all the way */ if (!inven && equipment[slot].to_h > 13 && equipment[slot].to_d > 20) return (FALSE); } /* Enchant it */ if (borg_read_scroll(SV_SCROLL_STAR_ENCHANT_WEAPON) || borg_spell(REALM_SORCERY, 3, 4)) { if (inven) { /* Tell the world */ borg_note("# Enchanting %s (%c)", inventory[slot].o_name, I2A(slot)); } else { /* Tell the world */ borg_note("# Enchanting %s (%c)", equipment[slot].o_name, I2A(slot)); /* * Find out if the prompt is at Inven or Equip by checking if * there is a weapon or ammo in the inventory. If there is * then the prompt is at Inven and has to be moved to Equip. */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Is this item is enchantable? */ if (l_ptr->tval >= TV_SHOT && l_ptr->tval <= TV_SWORD) { /* Goto the equipment */ borg_keypress('/'); break; } } } /* Choose that item */ borg_keypress(I2A(slot)); /* Success */ return (TRUE); } /* Flow can't get here because of the availability check at the start */ borg_oops("Marooned code was reached."); return (FALSE); } static bool borg_mundane(void) { int i, slot = -1; list_item *l_ptr; if (!borg_read_scroll_fail(SV_SCROLL_MUNDANITY)) return (FALSE); /* Check the equipment */ for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* Not for empty slots */ if (!l_ptr) continue; /* If there is a nasty curse remember the slot */ if (borg_test_bad_curse(l_ptr)) slot = i; } /* I guess not */ if (slot == -1) return (FALSE); if (borg_read_scroll(SV_SCROLL_MUNDANITY)) { /* switch to equipment */ borg_keypress('/'); /* Kazam! */ borg_keypress(I2A(slot)); /* Getaway */ return (TRUE); } /* Should be unreachable */ return (FALSE); } /* Handle the use of a scroll of artifact creation */ static bool borg_enchant_artifact(void) { int i, slot = -1; bool inven; list_item *l_ptr; slot = borg_notice_create_artifact(&inven); /* Do we have a winner? */ if (slot == -1) return (FALSE); /* Tell the world */ if (inven) borg_note("# Creating an artifact from %s (%c)", inventory[slot].o_name, I2A(slot)); else borg_note("# Creating an artifact from %s (%c)", equipment[slot].o_name, I2A(slot)); /* Read the scroll */ (void)borg_read_scroll(SV_SCROLL_ARTIFACT); /* if the item is in the equipment */ if (!inven) { /* Check if the scrolls heads towards the inventory */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Is this item is artifactable? */ if (l_ptr->tval < TV_BOW || l_ptr->tval > TV_DRAG_ARMOR) continue; /* switch to equipment */ borg_keypress('/'); break; } } /* Choose that item */ borg_keypress(I2A(slot)); /* Complete sequence */ borg_keypresses(" Borg Artifact\r"); /* Success */ return (TRUE); } /* Find out if the borg wears a cursed item */ static bool borg_wears_cursed(bool heavy) { int i; list_item *l_ptr; for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* Yeah well */ if (!l_ptr) continue; /* Are we looking for a heavy curse? */ if (heavy) { if (KN_FLAG(l_ptr, TR_HEAVY_CURSE)) return (TRUE); } /* It is a normal curse */ else { if (KN_FLAG(l_ptr, TR_CURSED)) return (TRUE); } } for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Only interesting items */ if (!borg_obj_known_p(l_ptr) || !borg_obj_is_ego_art(l_ptr)) continue; /* Are we looking for a heavy curse? */ if (heavy) { if (KN_FLAG(l_ptr, TR_HEAVY_CURSE)) return (TRUE); } /* It is a normal curse */ else { if (KN_FLAG(l_ptr, TR_CURSED)) return (TRUE); } } /* No curse found */ return (FALSE); } /* Remove Curse */ static bool borg_decurse(void) { /* Nothing to decurse */ if (!borg_wears_cursed(FALSE)) return (FALSE); /* remove the curse */ if (borg_activate(BORG_ACT_REMOVE_CURSE) || borg_activate(BORG_ACT_STAR_REMOVE_CURSE) || borg_spell_fail(REALM_LIFE, 1, 0, 60) || borg_spell_fail(REALM_LIFE, 2, 1, 60) || borg_use_staff_fail(SV_STAFF_REMOVE_CURSE) || borg_read_scroll(SV_SCROLL_REMOVE_CURSE)) { /* Shekockazol! */ return (TRUE); } /* Nothing to do */ return (FALSE); } /* Remove Heavy Curse */ static bool borg_star_decurse(void) { /* Nothing to *decurse* */ if (!borg_wears_cursed(TRUE)) return (FALSE); /* remove the curse */ if (borg_activate(BORG_ACT_STAR_REMOVE_CURSE) || borg_spell_fail(REALM_LIFE, 2, 1, 60) || borg_read_scroll(SV_SCROLL_STAR_REMOVE_CURSE)) { /* Shekockazol! */ return (TRUE); } /* Nothing to do */ return (FALSE); } /* If the borg has an artifact that can brand bolts try to do so */ static bool borg_brand_bolts(void) { int i; list_item *l_ptr; /* Can the borg brand a bolt? */ if (!borg_activate_fail(BORG_ACT_BRAND)) return (FALSE); /* Find a stack of bolts */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Just the bolts */ if (l_ptr->tval != TV_BOLT) continue; /* Just plain bolts */ if (l_ptr->xtra_name) continue; /* No cursed bolts */ if (KN_FLAG(l_ptr, TR_CURSED)) continue; /* Found one */ break; } /* No bolts found */ if (i == inven_num) return (FALSE); /* Try to brand the bolts (no need to target) */ return (borg_activate(BORG_ACT_BRAND)); } /* Enchant things */ bool borg_enchanting(void) { /* Prevent casting a spell over and over */ bool scroll_only = ((borg_t - borg_began > 150 && bp_ptr->depth) || (borg_t - borg_began > 350 && !bp_ptr->depth)); /* Forbid blind/confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* Simple enchanting */ if (borg_enchant_to_d()) return (TRUE); if (borg_enchant_to_h()) return (TRUE); if (borg_enchant_to_a(scroll_only)) return (TRUE); if (borg_enchant_to_w(scroll_only)) return (TRUE); /* Odd enchanting */ if (borg_decurse()) return (TRUE); if (borg_star_decurse()) return (TRUE); if (borg_enchant_artifact()) return (TRUE); if (borg_mundane()) return (TRUE); if (borg_brand_bolts()) return (TRUE); /* Nope */ return (FALSE); } /* Recharge things. Rods go first, then staffs and wands last. */ bool borg_recharging(void) { int i, charge = -1; /* Forbid blind/confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* Look for an item to recharge */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip empty /unaware items */ if (!l_ptr->k_idx) continue; /* Wands or staffs with no charges can be charged */ if ((l_ptr->tval == TV_WAND || l_ptr->tval == TV_STAFF) && ((borg_obj_known_p(l_ptr) && l_ptr->pval < 1) || strstr(l_ptr->o_name, "{empty"))) { /* Settle for this wand/staff. */ charge = i; } /* * Recharging rods is not a smart idea as it is a real pain if your * precious rod of recall is consumed by wild magic. * So only allow a Rod of Healing to be recharged when the borg is * low on HP and presumably will use the rod next turn. */ if (l_ptr->tval == TV_ROD && l_ptr->timeout && k_info[l_ptr->k_idx].sval == SV_ROD_HEALING && bp_ptr->chp < 100) { /* Settle for this rod */ charge = i; /* Rods go first so leave the loop */ break; } } /* Don't try to recharge if there is nothing to recharge */ if (charge == -1) return (FALSE); /* Attempt to recharge */ if (borg_activate(BORG_ACT_RECHARGE) || borg_spell(REALM_ARCANE, 3, 0) || borg_spell(REALM_CHAOS, 2, 2) || borg_spell(REALM_SORCERY, 0, 7) || borg_read_scroll(SV_SCROLL_RECHARGING)) { /* Message */ borg_note("Recharging %s", inventory[charge].o_name); /* Recharge the item */ borg_keypress(I2A(charge)); /* Success */ return (TRUE); } /* Nope */ return (FALSE); } /* * Sometimes the borg will want to destroy vaguely usefull items. * This procedure use those items instead of destroying them. */ static bool borg_consume(list_item *l_ptr) { int sval; /* must have an item */ if (!l_ptr) return (FALSE); /* get the sval */ sval = k_info[l_ptr->k_idx].sval; /* Special destruction */ switch (l_ptr->tval) { case TV_POTION: { /* Check the potion */ if (bp_ptr->status.gorged || ((sval >= SV_POTION_SLOWNESS) && (sval <= SV_POTION_DEATH))) { /* probably bad to quaff this */ return (FALSE); } /* Try quaffing the potion */ if (borg_quaff_potion(sval)) return (TRUE); break; } case TV_SCROLL: { /* Check the scroll */ switch (sval) { /* only good for scrolls without a target */ case SV_SCROLL_REMOVE_CURSE: case SV_SCROLL_LIGHT: case SV_SCROLL_MAPPING: case SV_SCROLL_STAR_REMOVE_CURSE: case SV_SCROLL_DETECT_GOLD: case SV_SCROLL_DETECT_ITEM: case SV_SCROLL_DETECT_TRAP: case SV_SCROLL_DETECT_DOOR: case SV_SCROLL_DETECT_INVIS: case SV_SCROLL_SATISFY_HUNGER: case SV_SCROLL_BLESSING: case SV_SCROLL_HOLY_CHANT: case SV_SCROLL_HOLY_PRAYER: case SV_SCROLL_MONSTER_CONFUSION: case SV_SCROLL_PROTECTION_FROM_EVIL: case SV_SCROLL_TRAP_DOOR_DESTRUCTION: case SV_SCROLL_DISPEL_UNDEAD: case SV_SCROLL_ACQUIREMENT: case SV_SCROLL_STAR_ACQUIREMENT: case SV_SCROLL_RUMOR: { /* Try reading the scroll */ return (borg_read_scroll(sval)); } case SV_SCROLL_STAR_IDENTIFY: case SV_SCROLL_IDENTIFY: { int i; /* Try to find something to id, backwards to get weapons */ for (i = inven_num - 1; i >= 0; i--) { /* If this is an unid'd object */ if (!borg_obj_known_p(&inventory[i])) { /* read the scroll on it */ (void)borg_read_scroll(sval); borg_keypress(I2A(i)); return (TRUE); } } } } break; } case TV_FOOD: { /* Check if the food is bad or the borg is full */ if (bp_ptr->status.full || bp_ptr->status.gorged || sval < SV_FOOD_MIN_FOOD || FLAG(bp_ptr, TR_CANT_EAT)) { /* Probably not a good idea */ return (FALSE); } /* Try eating the food */ if (borg_eat_food(sval)) return (TRUE);; } case TV_LITE: { /* What is the light source of the borg? */ list_item *q_ptr = look_up_equip_slot(EQUIP_LITE); /* If the item and the light source are nor the same type */ if (!q_ptr || k_info[q_ptr->k_idx].sval != k_info[l_ptr->k_idx].sval) { /* No refueling possible */ return (FALSE); } /* If the item is an empty lantern */ if (k_info[l_ptr->k_idx].sval == SV_LITE_LANTERN && l_ptr->timeout == 0) { /* No refueling possible */ return (FALSE); } /* If the item is an artifact */ if (KN_FLAG(l_ptr, TR_INSTA_ART)) { /* No refueling possible */ return (FALSE); } /* Then it can be used as fuel */ borg_keypress('F'); borg_keypress(I2A(look_up_index(l_ptr))); return (TRUE); } case TV_FLASK: { /* What is the light source of the borg? */ list_item *q_ptr = look_up_equip_slot(EQUIP_LITE); /* Is that a lantern? */ if (q_ptr && k_info[q_ptr->k_idx].sval == SV_LITE_LANTERN) { /* Fuel it with the flask */ borg_keypress('F'); borg_keypress(I2A(look_up_index(l_ptr))); return (TRUE); } } } /* Nope */ return (FALSE); } static bool borg_heavy_sense(void) { switch(borg_class) { case CLASS_WARRIOR: case CLASS_ROGUE: case CLASS_RANGER: case CLASS_PALADIN: case CLASS_CHAOS_WARRIOR: return (TRUE); case CLASS_MAGE: case CLASS_PRIEST: case CLASS_WARRIOR_MAGE: case CLASS_MONK: case CLASS_MINDCRAFTER: case CLASS_HIGH_MAGE: return (FALSE); default: { borg_oops("Unknown heavy sense for unknown class."); return (FALSE); } } } /* * Destroy 'number' items */ static void borg_destroy_item(list_item *l_ptr, int slot, int number) { char buf[4]; /* Message */ borg_note("# Destroying %s.", l_ptr->o_name); borg_keypress('0'); /* Get string corresponding to number */ (void)strnfmt(buf, 4, "%d", number); borg_keypresses(buf); /* Destroy that item */ if (!KN_FLAG(l_ptr, TR_INSTA_ART)) { /* Try to convert the object to money! */ if (!borg_spell_no_reserve(REALM_SORCERY, 3, 6) && !borg_activate(BORG_ACT_ALCHEMY) && !borg_mutation(MUT1_MIDAS_TCH)) { /* Allright then, press the letter */ borg_keypress('k'); } } else { /* worthless artifacts are dropped. */ borg_keypress('d'); /* * Mark the spot that the object was dropped so that it will not be * picked up again. The wrap around is ok because the dropped object * is not always right at the players feet, because of doors, stairs * or other objects. As one bad object can spawn multiple entries in * this list, it is ok to start over. */ bad_obj_n = (bad_obj_n + 1) % 50; bad_obj_x[bad_obj_n] = c_x; bad_obj_y[bad_obj_n] = c_y; borg_note("# Crappy artifact at %d,%d", c_x, c_y); } borg_keypress(I2A(slot)); } /* * This is an effort to keep the borg from destroying useless items that * he should sell in order to get more money * The returned value should be less than 1000 so the borg can destroy it * anyway in real need. * You can use this procedure also to get the borg to collect items in the * dungeon that he shouldn't buy, like a potion of restore foo when he is * not drained. * It won't work too well on items that you want the borg to collect for his * home because if the borg meets a shop before the home he will sell your * precious item. */ static s32b borg_values_money(list_item *l_ptr) { int tval = l_ptr->tval; int sval = k_info[l_ptr->k_idx].sval; /* Always try to collect fancy books */ if (tval >= TV_BOOKS_MIN && tval <= TV_BOOKS_MAX && tval != TV_ARCANE_BOOK) { /* Dungeon books */ if (sval == 3) return (800); if (sval == 2) return (700); } /* Reward getting stat restore potions when not needed */ if (tval == TV_POTION && sval >= SV_POTION_RES_STR && sval <= SV_POTION_RES_CON && !bp_ptr->status.fixstat[sval - SV_POTION_RES_STR]) return (100); /* Less valuable items */ if (borg_gold > 100000) return (0); /* The deepest Arcane book is worth 2000 or so */ if (tval >= TV_ARCANE_BOOK && sval == 3) return (400); /* Less valuable items */ if (borg_gold > 10000) return (0); /* The second book is worth maybe 500 gold */ if (tval >= TV_BOOKS_MIN && tval <= TV_BOOKS_MAX && sval > 0) return (200); /* Not so valuable items */ if (borg_gold > 1000) return (0); /* The first book is worth about 50 gold */ if (tval >= TV_BOOKS_MIN && tval <= TV_BOOKS_MAX) return (50); /* Lanterns are worth about 100 and stack when empty */ if (tval == TV_LITE && sval == SV_LITE_LANTERN && !l_ptr->timeout) return (100); /* Collect spikes if there is really little gold */ if (tval == TV_SPIKE) return (l_ptr->number); /* Truly worthless */ return (0); } /* * This proc may or may not destroy one item, depending on must_destroy. * If the value loss is greater than 1000 then the borg will return home. * So if you want the borg to keep items make sure they are valued > 1000. * If the borg is slowed because of his weight then the borg will attempt * to destroy enough items to fix that. */ static bool borg_destroy_aux(bool must_destroy) { int i, b_i = -1; int my_encumber, extra, number = 1; bool destroy_weight; s16b b_w = 0; s32b value = -1, b_v = 100000L, my_power, my_home_power; list_item *l_ptr; /* Get the starting power and encumberment */ my_power = g_power; my_home_power = g_power_home; my_encumber = bp_ptr->encumber; /* if the carry capacity is used for more than 120% */ destroy_weight = 2 * bp_ptr->weight / adj_str_wgt[my_stat_ind[A_STR]] >= 120; /* How much extra weight can the borg have before being slowed */ extra = (adj_str_wgt[my_stat_ind[A_STR]] * 100) / 10; /* Scan for junk */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Initialize */ value = 0; /* Skip empty / unaware items */ if (!l_ptr->k_idx) continue; /* unknown? */ if (!borg_worthless_item(l_ptr)) { /* Skip items that need to be *id*'d */ if (strstr(l_ptr->o_name, "{special") || strstr(l_ptr->o_name, "{terrible") || (!borg_obj_known_full(l_ptr) && borg_obj_star_id_able(l_ptr))) continue; if (!destroy_weight) { /* Pretend the whole pile is gone */ l_ptr->treat_as = TREAT_AS_GONE; number = l_ptr->number; } else { /* Pretend the pile is one smaller */ l_ptr->treat_as = TREAT_AS_LESS; number = 1; } /* Calculate the value of this item */ value = my_power - borg_power(); /* Useless for now. Maybe take it home? */ if (!value) { /* Find out the difference when this item goes home */ value = borg_power_home() - my_home_power; /* If the home value decreases then nullify value */ if (value < 0) value = 0; } /* Restore item */ l_ptr->treat_as = TREAT_AS_NORM; /* If the borg is overweight */ if (destroy_weight) { /* Void the punishment for the borg dropping speed */ value += MIN(my_encumber - extra, l_ptr->weight * number) * 500L; } } /* Keeps some items for their gold value */ if (value == 0) value = borg_values_money(l_ptr); /* if the borg wants to destroy junk then allow items with value = 0 */ if (!must_destroy && !destroy_weight && value != 0) continue; /* Ignore "bad" swaps */ if (value > b_v) continue; /* Hehe, someone can not count */ if (value < 0) borg_oops("Destroying %s with value = %d ???", l_ptr->o_name, value); /* do not allow too large values because that item is too interesting */ if (value > 1000) { /* If the borg was destroying for room in the inv but found nothing */ if (must_destroy) { /* Go back to town to fix the inv */ borg_note("# Going to town: Full inventory."); goal_rising = TRUE; } else { /* Allow speed loss for neat items */ continue; } } /* If the value is the same, take the one with the greater weight */ if ((value == b_v) && (l_ptr->weight <= b_w)) continue; /* Maintain the "best" */ b_i = i; b_v = value; b_w = l_ptr->weight; } /* Restore correct values in bp_ptr */ (void)borg_power(); (void)borg_power_home(); /* Nothing to destroy */ if (b_i < 0) return (FALSE); /* Reassign the item */ l_ptr = &inventory[b_i]; /* Attempt to consume it */ if (borg_consume(l_ptr)) return (TRUE); /* Make a note of the reason */ borg_note("# Destroying %sfor weight, value = %d", (destroy_weight) ? "" : "not ", b_v); /* Destroy the item */ if (destroy_weight) { /* Destroy just one item */ borg_destroy_item(l_ptr, b_i, 1); } else { /* Destroy all the items */ borg_destroy_item(l_ptr, b_i, l_ptr->number); } return (TRUE); } /* * Catchall procedure for destroying items. The borg will try to destroy an * item after he has picked up something. If the inventory is full he must * make an empty slot, Otherwise he will just rid himself of low value items. */ bool borg_destroy(void) { /* No destroying if even slightly dangerous */ if (borg_danger(c_x, c_y, 1, TRUE) > bp_ptr->chp / 20) return (FALSE); /* Destroy items when the borg needs the space */ if (inven_num == INVEN_PACK) return (borg_destroy_aux(TRUE));; /* Check if the borg carries junk */ if (borg_do_destroy) { /* Until the next inv change */ borg_do_destroy = FALSE; /* Try to destroy */ return (borg_destroy_aux(FALSE)); } /* No reason to destroy anything */ return (FALSE); } /* * Identify items if possible * * Note that "borg_parse()" will "cancel" the identification if it * detects a "You failed..." message. This is VERY important!!! * Otherwise the "identify" might induce bizarre actions by sending * the "index" of an item as a command. * * Hack -- recover from mind blanking by re-identifying the equipment. * * We instantly identify items known to be "good" (or "terrible"). * * We identify most un-aware items as soon as possible. * * We identify most un-known items as soon as possible. * * We play games with items that get "feelings" to try and wait for * "sensing" to take place if possible. * * XXX XXX XXX Make sure not to sell "non-aware" items, unless * we are really sure we want to lose them. For example, we should * wait for feelings on (non-icky) wearable items or else make sure * that we identify them before we try and sell them. * * Mega-Hack -- the whole "sometimes identify things" code is a total * hack. Slightly less bizarre would be some form of "occasionally, * pick a random item and identify it if necessary", which might lower * the preference for identifying items that appear early in the pack. * Also, preventing inventory motion would allow proper time-stamping. */ static bool borg_test_stuff(void) { int i, b_i = -1; s32b v, b_v = 0; list_item *l_ptr; bool inven = FALSE; /* Is there a way to identify things? */ if (!bp_ptr->able.id && bp_ptr->able.star_id < 1000) return (FALSE); /* Look for an item to identify (equipment) */ for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* Skip empty / unaware items */ if (!l_ptr) continue; if (borg_obj_known_p(l_ptr)) continue; /* Track it */ b_i = i; break; } /* Only bother with the inventory if you found nothing in the equipment */ if (b_i < 0) { /* Look for an ego or artifact item to identify (inventory) */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; if (borg_obj_known_p(l_ptr)) continue; /* Assume nothing */ v = 0; /* With unlimited identify */ if (bp_ptr->able.id >= 100) { /* The borg should identify everything */ v = 1; } /* Limited identify */ else { /* Ignore items that are very likely worth nothing */ if (borg_worthless_item(l_ptr)) continue; } /* Identify "good" items for a borg with light pseudo id */ if (strstr(l_ptr->o_name, "{good")) { /* If the {good} can hide ego stuff */ if (!borg_heavy_sense()) v += 10000L; /* If there are enough id scrolls */ if (bp_ptr->able.id > 10) v += 1000L; } else if (strstr(l_ptr->o_name, "{excellent")) v = 20000L; else if (strstr(l_ptr->o_name, "{tainted")) v = 20000L; else if (strstr(l_ptr->o_name, "{special")) v = 50000L; else if (strstr(l_ptr->o_name, "{terrible")) v = 50000L; /* Reward unencountered items */ else if (!l_ptr->k_idx) { /* Analyze the type */ switch (l_ptr->tval) { case TV_RING: case TV_AMULET: v += 2000; case TV_ROD: v += 1000; case TV_WAND: case TV_STAFF: v += 2000; case TV_POTION: case TV_SCROLL: v += 10; case TV_FOOD: v += 1; } } else { /* Analyze the type */ switch (l_ptr->tval) { case TV_LITE: { /* Must be artifact light */ v += 50000; break; } case TV_RING: case TV_AMULET: { v += 1000; break; } case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* If the borg has decent pseudo-id */ if (borg_calc_pseudo() < 100) { /* And it is heavy pseudo id then wait for it */ if (borg_heavy_sense()) continue; /* light pseudo may not work so id the item after a while */ if (!v && randint0(1000)) continue; } v = 5; } } } /* Track the best */ if (v <= b_v) continue; /* Track it */ b_i = i; b_v = v; inven = TRUE; } } /* Found something */ if (b_i >= 0) { /* Use a Spell/Prayer/Rod/Staff/Scroll of Identify */ if (borg_activate(BORG_ACT_IDENTIFY) || borg_zap_rod(SV_ROD_IDENTIFY) || borg_spell_no_reserve(REALM_ARCANE, 3, 2) || borg_spell_no_reserve(REALM_SORCERY, 1, 1) || borg_mindcr_no_reserve(MIND_PSYCHOMETRY, 25) || borg_use_staff(SV_STAFF_IDENTIFY) || borg_read_scroll(SV_SCROLL_IDENTIFY) || /* Or use *id* */ borg_spell_no_reserve(REALM_SORCERY, 1, 7) || borg_spell_no_reserve(REALM_NATURE, 2, 5) || borg_spell_no_reserve(REALM_DEATH, 3, 2) || borg_spell_no_reserve(REALM_TRUMP, 3, 1) || borg_spell_no_reserve(REALM_LIFE, 3, 5) || borg_activate(BORG_ACT_STAR_IDENTIFY)) { if (inven) { l_ptr = &inventory[b_i]; } else { l_ptr = &equipment[b_i]; /* Switch to equipment but not in case you go there immediately */ for (i = 0; i < inven_num; i++) { if (!borg_obj_known_p(&inventory[i])) { borg_keypress('/'); break; } } } /* Log -- may be cancelled */ borg_note("# Identifying %s.", l_ptr->o_name); /* Select the item */ borg_keypress(I2A(b_i)); borg_keypress(ESCAPE); /* Success */ return (TRUE); } } /* Nothing to do */ return (FALSE); } /* * The basic method of *id*ing is that the first *id*-worthy item is *id*ed * There are not so many items that have to be *id*ed thanks to * borg_item_star_id_able. Also, if there is a *id*-spell available but not * an id-spell (or rod) then all identifying is done with *id*. This way the * borg does not have to worry about id-ing so much. */ static bool borg_test_stuff_star(void) { int i, b_i = -1; list_item *l_ptr; /* Do we have the ability? */ if (!bp_ptr->able.star_id) return (FALSE); /* Look for an item to identify (equipment) */ for (i = 0; i < equip_num + inven_num; i++) { if (i >= equip_num) { l_ptr = &inventory[i - equip_num]; } else { l_ptr = look_up_equip_slot(i); /* Ignore empty slots */ if (!l_ptr) continue; } /* All items should first be identified */ if (!borg_obj_known_p(l_ptr)) { /* Except when the pseudo-id says it is special */ if (!strstr(l_ptr->o_name, "{special") && !strstr(l_ptr->o_name, "{terrible")) continue; /* Track it */ b_i = i; break; } /* Ignore items that were *id*'d before */ if (borg_obj_known_full(l_ptr)) continue; /* Ignore items that are known and have no hidden flags.*/ if (!borg_obj_star_id_able(l_ptr)) continue; /* Track it */ b_i = i; break; } /* Found something */ if (b_i >= 0) { if (borg_spell_no_reserve(REALM_SORCERY, 1, 7) || borg_spell_no_reserve(REALM_NATURE, 2, 5) || borg_spell_no_reserve(REALM_DEATH, 3, 2) || borg_spell_no_reserve(REALM_TRUMP, 3, 1) || borg_spell_no_reserve(REALM_LIFE, 3, 5) || borg_activate(BORG_ACT_STAR_IDENTIFY) || borg_read_scroll(SV_SCROLL_STAR_IDENTIFY)) { if (b_i >= equip_num) { /* Adapt to the inventory */ b_i -= equip_num; l_ptr = &inventory[b_i]; } else { l_ptr = &equipment[b_i]; /* Switch to equipment but not in case you go there immediately */ for (i = 0; i < inven_num; i++) { if (!borg_obj_known_full(&inventory[i])) { borg_keypress('/'); break; } } } /* Make a note */ borg_note("# *IDENTIFY*ing %s.", l_ptr->o_name); /* Select the item */ borg_keypress(I2A(b_i)); /* Success */ return (TRUE); } } /* Nothing to do */ return (FALSE); } /* * Use the Mindcrafter Psychometry power to pseudo-id items * or determine if it is smart to wait it out */ static bool borg_test_stuff_pseudo(void) { int i, b_i = -1; list_item *l_ptr; /* Can the borg cast the spell? */ if (!borg_mindcr_legal_fail(MIND_PSYCHOMETRY, 15, 60) || bp_ptr->lev > 24) { /* Light pseudo id or unlikely pseudo id are not worth it? */ if (!borg_heavy_sense() || borg_calc_pseudo() > 100) return (FALSE); } /* Look for an item to pseudo identify */ for (i = 0; i < equip_num + inven_num; i++) { if (i >= equip_num) { l_ptr = &inventory[i - equip_num]; } else { l_ptr = look_up_equip_slot(i); /* Ignore empty slots */ if (!l_ptr) continue; } /* Ignore items that were id'd before */ if (borg_obj_known_p(l_ptr)) continue; /* Item has to be weapon, armor or ammo */ if (l_ptr->tval < TV_SHOT || l_ptr->tval > TV_DRAG_ARMOR) continue; /* Item does not have a pseudo-id already (or comment) */ if (strstr(l_ptr->o_name, "{")) continue; /* Track it */ b_i = i; break; } /* Found nothing */ if (b_i < 0) return (FALSE); /* If you can cast the spell */ if (borg_mindcr_no_reserve(MIND_PSYCHOMETRY, 15)) { if (b_i >= equip_num) { /* Adapt to the inventory */ b_i -= equip_num; l_ptr = &inventory[b_i]; } else { l_ptr = &equipment[b_i]; /* Switch to equipment but not in case you go there immediately */ for (i = 0; i < inven_num; i++) { if (inventory[i].tval >= TV_SHOT && inventory[i].tval <= TV_DRAG_ARMOR && !borg_obj_known_p(&inventory[i])) { borg_keypress('/'); break; } } } /* Log -- may be cancelled */ borg_note("# pseudo identifying %s.", l_ptr->o_name); /* Select the item */ borg_keypress(I2A(b_i)); /* press enter a few time (get rid of display) */ borg_keypress(ESCAPE); /* Success */ return (TRUE); } /* Make a note */ borg_note("# Waiting for pseudo id to kick in"); /* Wait a bit for the pseudo-id to kick in */ borg_keypress('0'); borg_keypress('9'); borg_keypress('R'); /* Ready */ return (TRUE); } /* Catch all function that does the common part for the id routines */ bool borg_id_meta(void) { /* No ID if in danger */ if (borg_danger(c_x, c_y, 1, TRUE) > 1) return (FALSE); /* don't ID stuff when you can't recover spent spell point immediately */ if (bp_ptr->csp < 50 && !borg_check_rest()) return (FALSE); /* Pseudo identify unknown things */ if (borg_test_stuff_pseudo()) return (TRUE); /* Identify unknown things */ if (borg_test_stuff()) return (TRUE); /* *Id* unknown things */ if (borg_test_stuff_star()) return (TRUE); /* nothing */ return (FALSE); } /* * Wear useful equipment. * * Look through the inventory for equipment that is better than * the current equipment, and wear it, in an optimal order. * * Basically, we evaluate the world both with the current set of * equipment, and in the alternate world in which various items * are used instead of the items they would replace, and we take * one step towards the world in which we have the most "power". */ bool borg_wear_stuff(void) { int slot, b_slot = -1; int ii, i, b_i = -1; int danger, d; bool ring_repeat = FALSE; s32b p, b_p = g_power; list_item *l_ptr; /* Scan inventory */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Ring fiddling */ ii = i; /* Skip empty / unaware items */ if (!l_ptr->k_idx) continue; /* Skip useless items */ if (borg_worthless_item(l_ptr)) continue; /* apw do not wear not *id* artifacts */ if (!borg_obj_known_full(l_ptr) && borg_obj_star_id_able(l_ptr)) continue; /* Where does it go */ slot = borg_wield_slot(l_ptr); /* Cannot wear this item */ if (slot < 0) continue; /* Monks never have a weapon */ if (borg_class == CLASS_MONK && slot == EQUIP_WIELD) continue; /* Process non-rings */ if (slot == EQUIP_LEFT) { /* if this is the second time you see this ring */ if (ring_repeat) { /* use the other ring slot */ slot = EQUIP_RIGHT; } /* So it is the first time this ring is valued */ else { /* Pull back the counter to force a repetition of this item */ i--; } /* Flip value */ ring_repeat = !ring_repeat; } /* skip it if it this slot has not been decursed */ if (KN_FLAG(&equipment[slot], TR_CURSED) || KN_FLAG(&equipment[slot], TR_HEAVY_CURSE) ) continue; /* Obtain danger */ danger = borg_danger(c_x, c_y, 1, TRUE); /* Swap items */ equipment[slot].treat_as = TREAT_AS_SWAP; l_ptr->treat_as = TREAT_AS_LESS; /* Evaluate the inventory */ p = borg_power(); /* Evaluate local danger */ d = borg_danger(c_x, c_y, 1, TRUE); /* Restore items */ equipment[slot].treat_as = TREAT_AS_NORM; l_ptr->treat_as = TREAT_AS_NORM; /* Ignore "bad" swaps */ if (p <= b_p) continue; /* Ignore if more dangerous */ if (danger < d) continue; /* Maintain the "best" */ b_i = ii; b_p = p; b_slot = slot; } /* Restore internally kept stats */ (void)borg_power(); /* No item so give up */ if (b_i < 0) return (FALSE); /* Get the item */ l_ptr = &inventory[b_i]; /* Log */ borg_note("# Wearing %s. (%c)", l_ptr->o_name, I2A(b_i)); /* Wear it */ borg_keypress('w'); borg_keypress(I2A(b_i)); /* Check for two rings */ if ((b_slot == EQUIP_LEFT || b_slot == EQUIP_RIGHT) && equipment[EQUIP_LEFT].number && equipment[EQUIP_RIGHT].number) { /* On right hand? */ if (b_slot == EQUIP_RIGHT) { borg_keypress('y'); } else { borg_keypress('n'); } } /* Okidoki */ return (TRUE); } /* * Take off equipment that has become useless * This can happen through mutations, too much acid. */ bool borg_unwear_stuff(void) { int slot, b_slot = -1; int p, b_p, d, b_d; list_item *l_ptr; /* Get the original power */ b_p = g_power; /* Get the original danger */ b_d = borg_danger(c_x, c_y, 1, TRUE); /* Loop through the equipment */ for (slot = 0; slot < equip_num; slot++) { /* Get the object from the current slot */ l_ptr = &equipment[slot]; /* Skip empty slots */ if (!l_ptr->k_idx) continue; /* skip it if it has not been decursed */ if (KN_FLAG(l_ptr, TR_CURSED) || KN_FLAG(l_ptr, TR_HEAVY_CURSE)) continue; /* Monks never have a weapon */ if (borg_class == CLASS_MONK && slot == EQUIP_WIELD) { /* And if they have, take it off right now */ b_slot = 0; break; } /* Pretend it is not there */ l_ptr->treat_as = TREAT_AS_SWAP; /* Calculate new power */ p = borg_power(); /* Calculate the new danger */ d = borg_danger(c_x, c_y, 1, TRUE); /* Stop pretending */ l_ptr->treat_as = TREAT_AS_NORM; /* Is the borg better off without? */ if (p <= b_p) continue; /* Is it more dangerous now */ if (d > b_d) continue; /* Track this item */ b_slot = slot; b_p = p; } /* Restore internally kept stats */ (void)borg_power(); /* All the equipment is fine */ if (b_slot == -1) return (FALSE); /* Say you take it off */ borg_note("# Taking off a %s (%c)", equipment[b_slot].o_name, I2A(b_slot)); /* Take it off */ borg_keypress('t'); borg_keypress(I2A(b_slot)); /* Success */ return (TRUE); } /* * Prevent spells that appear in two realms to be learned in both. * This is to make more different spells available at low levels. * Only when the borg loses all his books containing the spell that he did learn * he will learn the second spell. * * Light area is in 5 realms. * Detect trap/door is in 4 realms. * Detect monster is in 3 realms. * Phase door is in 3 realms. * Cure light wounds is in 3 realms. * Detect evil is in 2 realms. * Trap/door destruction is in 2 realms. */ static void borg_spell_prevent_learn(void) { /* Find out which realms the borg has. */ int r_life = (borg_has_realm(REALM_LIFE)) ? 1 : 0, r_sorcery = (borg_has_realm(REALM_SORCERY)) ? 1 : 0, r_nature = (borg_has_realm(REALM_NATURE)) ? 1 : 0, r_chaos = (borg_has_realm(REALM_CHAOS)) ? 1 : 0, r_death = (borg_has_realm(REALM_DEATH)) ? 1 : 0, r_trump = (borg_has_realm(REALM_TRUMP)) ? 1 : 0, r_arcane = (borg_has_realm(REALM_ARCANE)) ? 1 : 0; /* Does the borg have two realms containing light? */ if (r_life + r_sorcery + r_nature + r_chaos + r_arcane == 2) { /* Can the borg cast a light already? */ if (borg_spell_legal(REALM_LIFE, 0, 4) || borg_spell_legal(REALM_SORCERY, 0, 3) || borg_spell_legal(REALM_NATURE, 0, 4) || borg_spell_legal(REALM_CHAOS, 0, 2) || borg_spell_legal(REALM_ARCANE, 0, 5)) { /* Prevent the learning of the second light spell */ if (borg_magics[REALM_LIFE][0][4].status == BORG_MAGIC_OKAY) borg_magics[REALM_LIFE][0][4].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_SORCERY][0][3].status == BORG_MAGIC_OKAY) borg_magics[REALM_SORCERY][0][3].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_NATURE][0][4].status == BORG_MAGIC_OKAY) borg_magics[REALM_NATURE][0][4].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_CHAOS][0][2].status == BORG_MAGIC_OKAY) borg_magics[REALM_CHAOS][0][2].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_ARCANE][0][5].status == BORG_MAGIC_OKAY) borg_magics[REALM_ARCANE][0][5].status = BORG_MAGIC_HIGH; } } /* Does the borg have two realms containing detect trap/door? */ if (r_life + r_sorcery + r_nature + r_arcane == 2) { /* Can the borg detect trap/door already? */ if (borg_spell_legal(REALM_LIFE, 0, 5) || borg_spell_legal(REALM_SORCERY, 0, 2) || borg_spell_legal(REALM_NATURE, 0, 2) || borg_spell_legal(REALM_ARCANE, 1, 0)) { /* Prevent the learning of the second detect trap/door spell */ if (borg_magics[REALM_LIFE][0][5].status == BORG_MAGIC_OKAY) borg_magics[REALM_LIFE][0][5].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_SORCERY][0][2].status == BORG_MAGIC_OKAY) borg_magics[REALM_SORCERY][0][2].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_NATURE][0][2].status == BORG_MAGIC_OKAY) borg_magics[REALM_NATURE][0][2].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_ARCANE][1][0].status == BORG_MAGIC_OKAY) borg_magics[REALM_ARCANE][1][0].status = BORG_MAGIC_HIGH; } } /* Does the borg have two realms containing detect monster? */ if (r_sorcery + r_nature + r_arcane == 2) { /* Can the borg cast a detect monster? */ if (borg_spell_legal(REALM_SORCERY, 0, 0) || borg_spell_legal(REALM_NATURE, 0, 0) || borg_spell_legal(REALM_ARCANE, 0, 3)) { /* Prevent the learning of the second detect monster */ if (borg_magics[REALM_SORCERY][0][0].status == BORG_MAGIC_OKAY) borg_magics[REALM_SORCERY][0][0].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_NATURE][0][0].status == BORG_MAGIC_OKAY) borg_magics[REALM_NATURE][0][0].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_ARCANE][0][3].status == BORG_MAGIC_OKAY) borg_magics[REALM_ARCANE][0][3].status = BORG_MAGIC_HIGH; } } /* Does the borg have two realms containing phase door? */ if (r_sorcery + r_trump + r_arcane == 2) { /* Can the borg phase door already? */ if (borg_spell_legal(REALM_SORCERY, 0, 1) || borg_spell_legal(REALM_TRUMP, 0, 0) || borg_spell_legal(REALM_ARCANE, 0, 4)) { /* Prevent the learning of the second phase door spell */ if (borg_magics[REALM_SORCERY][0][1].status == BORG_MAGIC_OKAY) borg_magics[REALM_SORCERY][0][1].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_TRUMP][0][0].status == BORG_MAGIC_OKAY) borg_magics[REALM_TRUMP][0][0].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_ARCANE][0][4].status == BORG_MAGIC_OKAY) borg_magics[REALM_ARCANE][0][4].status = BORG_MAGIC_HIGH; } } /* Does the borg have two realms containing cure light wounds? */ if (r_life + r_nature + r_arcane == 2) { /* Can the borg cure light wounds already? */ if (borg_spell_legal(REALM_LIFE, 0, 1) || borg_spell_legal(REALM_NATURE, 0, 1) || borg_spell_legal(REALM_ARCANE, 0, 7)) { /* Prevent the learning of the second cure light wounds spell */ if (borg_magics[REALM_LIFE][0][1].status == BORG_MAGIC_OKAY) borg_magics[REALM_LIFE][0][1].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_NATURE][0][1].status == BORG_MAGIC_OKAY) borg_magics[REALM_NATURE][0][1].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_ARCANE][0][7].status == BORG_MAGIC_OKAY) borg_magics[REALM_ARCANE][0][7].status = BORG_MAGIC_HIGH; } } /* Does the borg have both realms containing detect evil? */ if (r_life + r_death == 2) { /* Can the borg detect evil already? */ if (borg_spell_legal(REALM_LIFE, 0, 0) || borg_spell_legal(REALM_DEATH, 0, 2)) { /* Prevent the learning of the second detect evil */ if (borg_magics[REALM_LIFE][0][0].status == BORG_MAGIC_OKAY) borg_magics[REALM_LIFE][0][0].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_DEATH][0][2].status == BORG_MAGIC_OKAY) borg_magics[REALM_DEATH][0][2].status = BORG_MAGIC_HIGH; } } /* Does the borg have both realms containing trap/door destruction? */ if (r_chaos + r_arcane == 2) { /* Can the borg detect evil already? */ if (borg_spell_legal(REALM_CHAOS, 0, 1) || borg_spell_legal(REALM_ARCANE, 0, 6)) { /* Prevent the learning of the second detect evil */ if (borg_magics[REALM_CHAOS][0][1].status == BORG_MAGIC_OKAY) borg_magics[REALM_CHAOS][0][1].status = BORG_MAGIC_HIGH; if (borg_magics[REALM_ARCANE][0][6].status == BORG_MAGIC_OKAY) borg_magics[REALM_ARCANE][0][6].status = BORG_MAGIC_HIGH; } } } /* * Study and/or Test spells/prayers */ bool borg_play_magic(bool bored) { int rate, b_rate = -1; int realm, b_realm = -1; int book, b_book = -1; int spell, b_spell = -1; int inven, b_inven = -1; /* Hack -- must use magic or prayers */ if (!mp_ptr->spell_book) return (FALSE); /* Hack -- blind/confused */ if (bp_ptr->status.blind || bp_ptr->status.confused) return (FALSE); /* Dark */ if (!bp_ptr->cur_lite) return (FALSE); /* Low level borgs shouldn't learn the same spell twice */ if (bp_ptr->lev < 30) borg_spell_prevent_learn(); /* Check each realm, backwards */ for (realm = MAX_REALM; realm > 0; realm--) { /* skip non my realms */ if (!borg_has_realm(realm)) continue; /* Check each book (backwards) */ for (book = 3; book >= 0; book--) { /* Look for the book */ inven = borg_book[realm][book]; /* No such book */ if (inven < 0) continue; /* Check each spells */ for (spell = 7; spell >= 0; spell--) { borg_magic *as = &borg_magics[realm][book][spell]; /* Require "learnable" status */ if (as->status != BORG_MAGIC_OKAY) continue; /* Obtain "rating" */ rate = as->rating; /* Skip "boring" spells/prayers */ if (!bored && (rate <= 50)) continue; /* Skip "icky" spells/prayers */ if (rate <= 0) continue; /* * Spells with higher casting cost than max mana * should be learned last. */ if (borg_spell_mana(realm, book, spell) > bp_ptr->msp) rate = 1; /* Skip "worse" spells/prayers */ if (rate <= b_rate) continue; /* Track it */ b_inven = inven; b_rate = rate; b_realm = realm; b_book = book; b_spell = spell; } } } /* Study */ if (bp_ptr->status.study && (b_rate > 0)) { /* Realm */ borg_magic *as = &borg_magics[b_realm][b_book][b_spell]; /* Debugging Info */ borg_note("# Studying %s spell %s.", as->realm_name, as->name); /* Learn the spell */ borg_keypress('G'); /* Specify the book */ borg_keypress(I2A(b_inven)); /* Some Classes can not choose */ if (borg_class != CLASS_PRIEST && borg_class != CLASS_PALADIN && borg_class != CLASS_MONK) { /* Specify the spell */ borg_keypress(I2A(b_spell)); } /* Success */ return (TRUE); } /* Hack -- only in town */ if (bp_ptr->depth) return (FALSE); /* Hack -- only when bored */ if (!bored) return (FALSE); /* Check each realm backwards */ for (realm = MAX_REALM; realm > 0; realm--) { /* Check each book (backwards) */ for (book = 3; book >= 0; book--) { /* Only my realms */ if (!borg_has_realm(realm)) continue; /* Look for the book */ inven = borg_book[realm][book]; /* No such book */ if (inven < 0) continue; /* Check every spell (backwards) */ for (spell = 8 - 1; spell >= 0; spell--) { borg_magic *as = &borg_magics[realm][book][spell]; /* Only try "untried" spells/prayers */ if (as->status != BORG_MAGIC_TEST) continue; /* Ignore "bizarre" spells/prayers */ if (as->method == BORG_MAGIC_OBJ) continue; /* Make sure I have enough mana */ if (bp_ptr->csp < borg_spell_mana(realm, book, spell)) continue; /* Note */ borg_note("# Testing untried spell/prayer"); /* Hack -- Use spell or prayer */ if (borg_spell_no_reserve(realm, book, spell)) { /* Hack -- Allow attack spells */ if (as->method == BORG_MAGIC_AIM) { /* Hack -- target self */ borg_keypress('*'); borg_keypress('p'); borg_keypress('t'); } /* Hack -- Allow dimension Door */ if (as->method == BORG_MAGIC_EXT) { /* Hack -- target self */ borg_keypress(' '); } /* Hack -- Allow genocide spells */ if (as->method == BORG_MAGIC_WHO) { /* Hack -- target townies */ borg_keypress('t'); } /* Success */ return (TRUE); } } } } /* Nope */ return (FALSE); } /* Scan the item list and recharge items before leaving the level. */ bool borg_wait_recharge(void) { int i; int slot = -1; /* No resting in danger */ if (!borg_check_rest()) return (FALSE); /* Not if hungry */ if (bp_ptr->status.weak) return (FALSE); /* Look for an (wearable- non rod) item to recharge */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip empty / unaware items */ if (!l_ptr->k_idx) continue; /* skip items that are charged */ if (!l_ptr->timeout) continue; /* skip lites */ if (l_ptr->tval == TV_LITE) continue; /* Where does this belong? */ slot = borg_wield_slot(l_ptr); l_ptr = look_up_equip_slot(slot); /* If the slot has a cursed item */ if (l_ptr && slot >= 0 && (KN_FLAG(l_ptr, TR_CURSED) || KN_FLAG(l_ptr, TR_HEAVY_CURSE))) { /* then don't bother swapping */ continue; } /* There is an item to recharge */ break; } /* nothing to recharge */ if (i == inven_num) return (FALSE); /* Was it a rod or an equipable item? */ if (slot == -1) { /* Note the rod */ borg_note("Waiting for %s to recharge.", inventory[i].o_name); } else { /* Note the swapping */ borg_note("Swapping %s to recharge it.", inventory[i].o_name); /* wear the item */ borg_note("# Swapping Item for Recharge."); borg_keypress('w'); borg_keypress(I2A(i)); } /* rest for a while */ borg_keypress('R'); borg_keypress('7'); borg_keypress('5'); borg_keypress('\n'); /* done */ return (TRUE); } /* * Leave the level if necessary (or bored) * Scumming defined in borg_prepared. */ bool borg_leave_level(bool bored) { int g = 0; int target_depth = borg_prepared_depth(); cptr reason; /* Hack -- waiting for "recall" */ if (goal_recalling) return (FALSE); /* Town */ if (!bp_ptr->depth) { /* Cancel rising */ goal_rising = FALSE; /* Nothing to do here */ return (FALSE); } /** In the Dungeon **/ /* do not hangout on boring levels for *too* long */ if (target_depth > bp_ptr->depth + 3) g = 1; /* Do not dive when "full" of items */ if (inven_num >= INVEN_PACK - 1) g = 0; /* Do not dive when drained */ if (bp_ptr->status.fixexp) g = 0; /* Hack -- Stay on each level for a minimal amount of time */ if (bp_ptr->lev > 10 && borg_t - borg_began < value_feeling[borg_feeling]) { g = 0; } reason = borg_prepared(bp_ptr->depth + 1); /* Rise a level if bored and unable to dive. */ if (bored && reason) { g = -1; borg_note("# heading up (bored and unable to dive: %s)", reason); } /* Power dive if I am playing too shallow */ if (!borg_prepared(bp_ptr->depth + 5)) g = 1; /* Hack -- Power-climb upwards when needed */ if (bp_ptr->depth > target_depth && !unique_on_level) { reason = borg_prepared(bp_ptr->depth); borg_note("# heading up (too deep: %s)", reason); g = -1; /* if I must restock go to town */ if (borg_restock(bp_ptr->depth)) { borg_note("# returning to town to restock(too deep: %s)", reason); goal_rising = TRUE; } /* if I am really out of depth go to town */ if (target_depth * 2 < bp_ptr->depth) { borg_note("# returning to town (too deep: %s)", reason); goal_rising = TRUE; } } /* Power dive too 100 if ready */ if (!borg_prepared(100)) g = 1; /* Power dive if the Serpent is dead */ if (bp_ptr->winner) g = 1; /* Return to town to sell stuff */ if (bored && inven_num >= INVEN_PACK - 1) { borg_note("# Going to town (Sell Stuff)."); goal_rising = TRUE; } /* Return to town when level drained */ if (bp_ptr->status.fixlvl) { borg_note("# Going to town (Fix Level)."); goal_rising = TRUE; } /* Return to town to restore experience */ if (bored && bp_ptr->status.fixexp && (bp_ptr->lev != 50)) { borg_note("# Going to town (Fix Experience)."); goal_rising = TRUE; } /* return to town if it has been a while */ if ((!goal_rising && bored && !vault_on_level && !borg_fighting_unique && borg_time_town + borg_t - borg_began > 8000) || (borg_time_town + borg_t - borg_began > 12000)) { borg_note("# Going to town (I miss my home)."); goal_rising = TRUE; } /* return to town if been scumming for a bit */ if (bp_ptr->depth < 9 && bp_ptr->max_depth >= 20 && borg_time_town + borg_t - borg_began > 3500) { borg_note("# Going to town (scumming check)."); goal_rising = TRUE; } /* if returning to town, try to go upstairs */ if (goal_rising) g = -1; /* Mega-Hack -- spend time on the first level to rotate shops */ if ((bp_ptr->lev > 10) && (bp_ptr->depth == 1) && (borg_t - borg_began < 100) && (g < 0)) { g = 0; } /* Use random stairs when really bored */ if (bored && (borg_t - borg_began >= 5000)) { /* Note */ borg_note("# Choosing random stairs."); /* Use random stairs */ g = ((randint0(100) < 50) ? -1 : 1); } /* Go Up */ if (g < 0) { /* Take next stairs */ stair_less = TRUE; /* Hack -- recall if going to town */ if (goal_rising && ((borg_time_town + (borg_t - borg_began)) > 200) && (bp_ptr->depth >= 5) && borg_recall()) { borg_note("# Recalling to town (goal rising)"); return (TRUE); } /* Attempt to use stairs */ if (borg_flow_stair_less(GOAL_BORE)) return (TRUE); /* Cannot find any stairs */ if (goal_rising && bored && (borg_t - borg_began) >= 1000) { if (borg_recall()) { borg_note("# Recalling to town (no stairs)"); return (TRUE); } } } /* Go Down */ if (g > 0) { /* Take next stairs */ stair_more = TRUE; /* Attempt to use those stairs */ if (borg_flow_stair_more(GOAL_BORE)) return (TRUE); } /* Failure */ return (FALSE); } /* * Initialize this file */ void borg_init_7(void) { /* Nothing */ } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg8.c0000644000000000000000000014233410250356275013522 0ustar rootroot/* File: zborg8.c */ /* Purpose: High level functions for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zborg7.h" #include "zborg8.h" /* * Determine if an item can "absorb" a second item * * See "object_absorb()" for the actual "absorption" code. * * If permitted, we allow wands/staffs (if they are known to have equal * charges) and rods (if fully charged) to combine. * * Note that rods/staffs/wands are then unstacked when they are used. * * Food, potions, scrolls, and "easy know" items always stack. * * Chests never stack (for various reasons). * * We do NOT allow activatable items (artifacts or dragon scale mail) * to stack, to keep the "activation" code clean. Artifacts may stack, * but only with another identical artifact (which does not exist). * * Ego items may stack as long as they have the same ego-item type. * This is primarily to allow ego-missiles to stack. */ static bool borg_object_similar(list_item *l_ptr, list_item *q_ptr) { /* Require identical object types */ if (l_ptr->k_idx != q_ptr->k_idx) return (FALSE); /* Maximal "stacking" limit */ if (q_ptr->number + 1 >= MAX_STACK_SIZE) return (FALSE); /* Analyze the items */ switch (l_ptr->tval) { case TV_CHEST: { /* Chests */ /* Never okay */ return (FALSE); } case TV_FOOD: case TV_POTION: case TV_SCROLL: case TV_ROD: { /* Food and Potions and Scrolls and Rods */ /* Assume okay */ break; } case TV_STAFF: { /* Staffs */ /* Require knowledge */ if (!borg_obj_known_p(l_ptr) || !borg_obj_known_p(q_ptr)) return (FALSE); /* Require identical charges */ if (l_ptr->pval != q_ptr->pval) return (FALSE); /* Probably okay */ break; } case TV_WAND: { /* Wands */ /* Require equal knowledge */ if (borg_obj_known_p(l_ptr) != borg_obj_known_p(q_ptr)) return (FALSE); /* Probably okay */ break; } case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Weapons and Armor never stack */ return (FALSE); } case TV_LITE: { /* Lights */ /* Only Torches can stack */ if (k_info[l_ptr->k_idx].sval != SV_LITE_TORCH) return (FALSE); /* Require identical charges */ if (l_ptr->timeout != q_ptr->timeout) return (FALSE); /* Probably okay */ break; } case TV_RING: case TV_AMULET: { /* Rings, Amulets */ /* Require full knowledge of both items */ if (!borg_obj_known_p(l_ptr) || !borg_obj_known_p(q_ptr)) return (FALSE); /* Fall through */ } case TV_BOLT: case TV_ARROW: case TV_SHOT: { /* Missiles */ /* Require identical "bonuses" */ if (l_ptr->to_h != q_ptr->to_h) return (FALSE); if (l_ptr->to_d != q_ptr->to_d) return (FALSE); if (l_ptr->to_a != q_ptr->to_a) return (FALSE); /* Require identical "pval" code */ if (l_ptr->pval != q_ptr->pval) return (FALSE); /* Hack -- items with hidden flags don't stack */ if (borg_obj_star_id_able(l_ptr)) return (FALSE); /* Hack -- Never stack "powerful" items */ if (l_ptr->kn_flags[0] || q_ptr->kn_flags[0]) return (FALSE); if (l_ptr->kn_flags[1] || q_ptr->kn_flags[1]) return (FALSE); if (l_ptr->kn_flags[2] || q_ptr->kn_flags[2]) return (FALSE); /* Require identical "values" */ if (l_ptr->ac != q_ptr->ac) return (FALSE); if (l_ptr->dd != q_ptr->dd) return (FALSE); if (l_ptr->ds != q_ptr->ds) return (FALSE); /* Probably okay */ break; } default: { /* Various */ /* Require knowledge */ if ((!borg_obj_known_p(l_ptr) || !borg_obj_known_p(q_ptr))) return (FALSE); /* Probably okay */ break; } } /* Hack -- Require identical "broken" status */ if (borg_obj_known_full(l_ptr) != borg_obj_known_full(q_ptr)) { return (FALSE); } /* They match, so they must be similar */ return (TRUE); } /* * This file handles the highest level goals, and store interaction. * * Store interaction strategy * * (1) Sell items to the home (for later use) * optimize the stuff in the home... this involves buying and selling stuff * not in the 'best' list. * We sell anything we may need later (see step 4) * * (2) Sell items to the shops (for money) * We sell anything we do not actually need * * (3) Buy items from the shops (for the player) * We buy things that we actually need * * (4) Buy items from the home (for the player) * We buy things that we actually need (see step 1) * * (5) Buy items from the shops (for the home) * We buy things we may need later (see step 1) * * (6) Buy items from the home (for the stores) * We buy things we no longer need (see step 2) * * The basic principle is that we should always act to improve our * "status", and we should sometimes act to "maintain" our status, * especially if there is a monetary reward. But first we should * attempt to use the home as a "stockpile", even though that is * not worth any money, since it may save us money eventually. */ /* * Sell items to the current shop */ static void borg_think_shop_sell(int item, list_item *l_ptr) { /* Log */ borg_note("# Selling %s (%c)", l_ptr->o_name, I2A(item)); /* One item */ borg_keypress('0'); borg_keypress('1'); /* Sell an item */ borg_keypress('s'); /* Sell the desired item */ borg_keypress(I2A(item)); /* If the user likes this option */ if (check_transaction) borg_keypress('y'); /* Increment 'use' count */ borg_shops[shop_num].u_count++; /* The purchase is complete */ goal_shop = -1; } /* * Buy items from the current shop */ static void borg_think_shop_buy(int item) { list_item *l_ptr = &cur_list[item]; /* Is the borg on page 1 but wants to be one page 2? */ if ((borg_term_text_comp(26, 5, "1") && item >= STORE_INVEN_MAX / 2) || (borg_term_text_comp(26, 5, "2") && item < STORE_INVEN_MAX / 2)) { /* Goto the other page */ borg_keypress(' '); } /* Log */ borg_note("# Buying %s (%i gold).", l_ptr->o_name, l_ptr->cost); /* Buy one item */ borg_keypress('0'); borg_keypress('1'); /* Buy an item */ borg_keypress('p'); /* Buy the desired item */ borg_keypress(I2A(item % (STORE_INVEN_MAX / 2))); /* Increment 'use' count */ borg_shops[shop_num].u_count++; /* The purchase is complete */ goal_shop = -1; } /* Test to see if the item can be merged with anything in the home. */ static bool borg_can_merge_home(list_item *l_ptr) { int i; /* Scan the home for matching items */ for (i = 0; i < home_num; i++) { /* Can they stack? */ if (borg_object_similar(l_ptr, &borg_home[i])) return (TRUE); } /* No match */ return (FALSE); } /* * This will see what single addition is best for the home. */ static int borg_think_home_sell_aux2(void) { list_item *l_ptr; int i, b_i = -1; s32b p, b_p, b_s = 0; /* Evaluate the home */ b_p = g_power + g_power_home; /* If the home is almost full */ if (home_num >= STORE_INVEN_MAX - 1) { /* Find out how much value will be lost if one item is taken from the home */ for (i = 0; i < home_num; i++) { list_item *l_ptr = &borg_home[i]; /* Remove the item */ l_ptr->treat_as = TREAT_AS_GONE; /* Evaluate the home */ p = borg_power() + borg_power_home(); /* Restore item */ l_ptr->treat_as = TREAT_AS_NORM; /* Ignore "bad" sales */ if (p < b_s) continue; /* Maintain the "best" */ b_s = p; } /* Up the minimum a bit */ if (b_s < b_p) b_p += b_p - b_s; } /* Loop through all the items */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; /* Is there room for this item at home? */ if (home_num >= STORE_INVEN_MAX && !borg_can_merge_home(l_ptr)) continue; /* Chuck item out of the inv */ l_ptr->treat_as = TREAT_AS_LESS; /* Evaluate the new power */ p = borg_power() + borg_power_home(); /* Restore item */ l_ptr->treat_as = TREAT_AS_NORM; /* Track best */ if (b_p >= p) continue; /* Save the results */ b_i = i; b_p = p; } /* Set the internally kept stats correctly */ g_power = borg_power(); g_power_home = borg_power_home(); /* Item to give to home, if any. */ return (b_i); } /* * Step 1 -- sell "useful" things to the home (for later) */ static bool borg_think_home_sell_aux(void) { int index; /* Hack - we need to have visited the home before */ if (home_shop == -1) return (FALSE); /* Find best item to give to home. */ index = borg_think_home_sell_aux2(); /* Do we have an item? */ if (index != -1) { goal_shop = home_shop; borg_think_shop_sell(index, &inventory[index]); /* We have goal */ return (TRUE); } /* Assume not */ return (FALSE); } /* * Determine if an item can be sold in the given store * * XXX XXX XXX Consider use of "icky" test on items */ static bool borg_good_sell(list_item *l_ptr) { /* Just making sure */ if (!l_ptr) return (FALSE); /* Never sell worthless items */ if (l_ptr->cost <= 0) return (FALSE); /* Analyze the type */ switch (l_ptr->tval) { case TV_POTION: case TV_SCROLL: { /* Never sell if not "known" and interesting */ if (!borg_obj_known_p(l_ptr) && bp_ptr->max_depth > 20) return (FALSE); break; } case TV_ROD: case TV_WAND: case TV_STAFF: case TV_RING: case TV_AMULET: case TV_LITE: { /* Never sell if not "known" */ if (!borg_obj_known_p(l_ptr)) return (FALSE); break; } case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { /* Worthless items can be sold */ if (borg_worthless_item(l_ptr)) return (TRUE); /* Unknown items should not be sold */ if (!borg_obj_known_p(l_ptr)) return (FALSE); break; } } /* Do not sell an artifact that is not fully id'd and should be */ if (!borg_obj_known_full(l_ptr) && KN_FLAG(l_ptr, TR_INSTA_ART)) { /* *identify* this item first */ if (borg_gold < 300000) return (FALSE); } /* Assume we can */ return (TRUE); } /* * Step 2 -- sell "useless" items to a shop (for cash) */ static bool borg_think_shop_sell_aux(int shop) { int i, b_i = -1; s32b p, b_p = 0L; s32b c = 0L; s32b b_c = 30001L; /* Evaluate */ b_p = g_power; /* Sell stuff */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; /* Skip "bad" sales */ if (!borg_good_sell(l_ptr)) continue; /* Give the item to the shop */ l_ptr->treat_as = TREAT_AS_LESS; /* Evaluate the inventory with this item gone */ p = borg_power(); /* Hack - it is good to sell unknown stuff */ if (!l_ptr->k_idx) { if (bp_ptr->lev < 10) { p += 100; } } /* Restore the item */ l_ptr->treat_as = TREAT_AS_NORM; /* Ignore "bad" sales */ if (p < b_p) continue; /* Extract the "price" */ c = ((l_ptr->cost < 30000L) ? l_ptr->cost : 30000L); /* * Sell cheap items first. * This is done because we may have to buy the item back * in some very strange circumstances. */ if (p == b_p && c >= b_c) continue; /* Maintain the "best" */ b_i = i; b_p = p; b_c = c; } /* Set the internally kept stats correctly */ g_power = borg_power(); /* Sell nothing */ if (b_i == -1) return (FALSE); /* Visit that shop */ goal_shop = shop; /* Sell that item */ borg_think_shop_sell(b_i, &inventory[b_i]); /* Success */ return (TRUE); } /* * What is the power after wielding the item * into the requested slot? */ static s32b borg_think_buy_slot(list_item *l_ptr, int slot, bool home) { list_item *q_ptr = look_up_equip_slot(slot); s32b p; /* Paranoia */ if (q_ptr && (KN_FLAG(q_ptr, TR_CURSED) || KN_FLAG(q_ptr, TR_HEAVY_CURSE) || KN_FLAG(q_ptr, TR_PERMA_CURSE))) { /* Return 'bad' value */ return (0); } /* Swap items */ if (q_ptr) q_ptr->treat_as = TREAT_AS_SWAP; l_ptr->treat_as = TREAT_AS_SWAP; /* Evaluate the inventory */ p = borg_power(); /* Examine the home */ if (home) p += borg_power_home(); /* Fix items */ if (q_ptr) q_ptr->treat_as = TREAT_AS_NORM; l_ptr->treat_as = TREAT_AS_NORM; /* Return power */ return (p); } /* Make sure the borg keeps money back for food and light and recall */ static bool borg_spends_gold_okay(list_item *l_ptr) { /* Always allow buying fuel */ if (l_ptr->tval == TV_LITE || l_ptr->tval == TV_FLASK) return (TRUE); /* If the borg is low on money and may need to refuel */ if (borg_gold < 20 && !KN_FLAG(&equipment[EQUIP_LITE], TR_INSTA_ART)) return (FALSE); /* Allow food to be bought when there is more than 20 gold */ if (l_ptr->tval == TV_FOOD || (l_ptr->tval == TV_SCROLL && k_info[l_ptr->k_idx].sval == SV_SCROLL_SATISFY_HUNGER)) return (TRUE); /* If the borg is low on money and does not have a food spell */ if (borg_gold < 80 && !borg_spell_legal_fail(REALM_LIFE, 0, 7, 65) && !borg_spell_legal_fail(REALM_ARCANE, 2, 6, 65) && !borg_spell_legal_fail(REALM_NATURE, 0, 3, 65) && !borg_racial_check(RACE_HOBBIT, TRUE)) return (FALSE); if (l_ptr->tval == TV_SCROLL && k_info[l_ptr->k_idx].sval == SV_SCROLL_WORD_OF_RECALL) return (TRUE); /* If the borg is low on money and does not have a recall spell */ if (borg_gold < 200 && !borg_equips_rod(SV_ROD_RECALL) && !borg_spell_legal_fail(REALM_SORCERY, 2, 7, 60) && !borg_spell_legal_fail(REALM_ARCANE, 3, 6, 60) && !borg_spell_legal_fail(REALM_TRUMP, 1, 6, 60) && !borg_mutation_check(MUT1_RECALL, TRUE)) return (FALSE); /* Spend away */ return (TRUE); } /* Step 3 -- buy "useful" things from a shop (to be used) */ static bool borg_think_shop_buy_aux(int shop) { int slot; int n, b_n = -1; s32b p = 0L, b_p = 0L; s32b c, b_c = 0L; /* Require one empty slot */ if (inven_num == INVEN_PACK) return (FALSE); /* Extract the "power" */ b_p = g_power; /* Attempt to use shop items */ use_shop = TRUE; /* Scan the wares */ for (n = 0; n < cur_num; n++) { list_item *l_ptr = &cur_list[n]; /* Reset value indicator */ p = 0; /* second check on empty */ if (!l_ptr->k_idx) continue; /* Hack - we cannot buy some items */ if (!l_ptr->cost) continue; /* Hack -- Require "sufficient" cash */ if (borg_gold < l_ptr->cost) continue; /* Obtain "slot" */ slot = borg_wield_slot(l_ptr); /* * Hack, we keep diggers as a back-up, not to * replace our current weapon */ if (l_ptr->tval == TV_DIGGING) slot = -1; /* skip it if it has not been decursed */ if (strstr(l_ptr->o_name, "{cursed") || KN_FLAG(l_ptr, TR_CURSED) || KN_FLAG(l_ptr, TR_HEAVY_CURSE)) continue; /* Consider new equipment */ if (slot >= 0) { /* Get power for doing swap */ p = borg_think_buy_slot(l_ptr, slot, FALSE); /* Also check if it is an item for the inventory */ } /* Consider new inventory */ if (p <= b_p) { /* Hack - use 'INVEN_LESS' to say we want it in the inventory */ l_ptr->treat_as = TREAT_AS_LESS; /* Evaluate the equipment */ p = borg_power(); /* Fix item */ l_ptr->treat_as = TREAT_AS_NORM; } /* Obtain the "cost" of the item */ c = l_ptr->cost; /* Is it too costly? */ if (!borg_spends_gold_okay(l_ptr)) continue; /* Penalize the cost of expensive items */ if (c > borg_gold / 10) p -= c; /* Ignore "bad" purchases */ if (p < b_p) continue; /* Ignore "expensive" purchases */ if (p == b_p && c >= b_c) continue; /* Save the item and cost */ b_n = n; b_p = p; b_c = c; } /* Use normal items */ use_shop = FALSE; /* Set the internally kept stats correctly */ g_power = borg_power(); /* Buy something */ if (b_n == -1) return (FALSE); /* Visit that shop */ goal_shop = shop; /* Buy that item */ borg_think_shop_buy(b_n); /* Success */ return (TRUE); } /* * Step 4 -- buy "useful" things from the home (to be used) */ static bool borg_think_home_buy_aux(void) { int slot; int n, b_n = -1; s32b p = 0L, b_p = 0L; /* Require one empty slot */ if (inven_num == INVEN_PACK) return (FALSE); /* Extract the "power" */ b_p = g_power + g_power_home; /* Scan the home */ for (n = 0; n < home_num; n++) { list_item *l_ptr = &borg_home[n]; /* Skip empty items */ if (!l_ptr->number) continue; /* Obtain "slot" */ slot = borg_wield_slot(l_ptr); /* Consider new equipment */ if (slot >= 0) { /* Get power for doing swap */ p = borg_think_buy_slot(l_ptr, slot, TRUE); /* Rings can be put into two slots */ if (slot == EQUIP_LEFT) { /* Try this ring for the other finger too. */ p = MAX(p, borg_think_buy_slot(l_ptr, EQUIP_RIGHT, TRUE)); } } /* Consider new inventory */ else { /* Try to get item */ l_ptr->treat_as = TREAT_AS_LESS; /* Evaluate the equipment */ p = borg_power() + borg_power_home(); /* Restore item */ l_ptr->treat_as = TREAT_AS_NORM; } /* Ignore "silly" purchases */ if (p <= b_p) continue; /* Save the item and cost */ b_n = n; b_p = p; } /* Set the internally kept stats correctly */ g_power = borg_power(); g_power_home = borg_power_home(); /* Buy nothing */ if (b_n == -1) return (FALSE); /* Go to the home */ goal_shop = home_shop; /* Buy that item */ borg_think_shop_buy(b_n); /* Success */ return (TRUE); } /* * Step 5 -- buy "interesting" things from a shop (to be used later) * * It is highly likely this function is buggy. We need to * remember that the item is destined for the home. (Or check * for placing items in the home before selling them to a shop.) */ static bool borg_think_shop_grab_aux(int shop) { int n, b_n = -1; s32b s, b_s = 0L; s32b c, b_c = 0L; /* Require two empty slots */ if (inven_num >= INVEN_PACK - 2) return (FALSE); /* Evaluate the home */ b_s = g_power_home; /* Use shops */ use_shop = TRUE; /* Scan the wares */ for (n = 0; n < cur_num; n++) { list_item *l_ptr = &cur_list[n]; /* Obtain the "cost" of the item */ c = l_ptr->cost; /* Hack - we cannot buy some items */ if (c <= 0) continue; /* Ignore too expensive items */ if (borg_gold < c) continue; /* Too heavy to carry now */ if (2 * (bp_ptr->weight + l_ptr->weight) / adj_str_wgt[my_stat_ind[A_STR]] >= 120) continue; /* Get a single item */ l_ptr->treat_as = TREAT_AS_LESS; /* Evaluate the home */ s = borg_power_home(); /* Restore the item */ l_ptr->treat_as = TREAT_AS_NORM; /* Is it too costly? */ if (!borg_spends_gold_okay(l_ptr)) continue; /* Penalize expensive items */ if (c > borg_gold / 10) s -= c; /* Ignore "bad" sales */ if (s < b_s) continue; /* Ignore "expensive" purchases */ if (s == b_s && c >= b_c) continue; /* Save the item and cost */ b_n = n; b_s = s; b_c = c; } /* Normal power calculation */ use_shop = FALSE; /* Set the internally kept stats correctly */ g_power_home = borg_power_home(); /* Buy something */ if (b_n == -1) return (FALSE); /* Visit that shop */ goal_shop = shop; /* Buy that item */ borg_think_shop_buy(b_n); /* Hack - get out of the store */ borg_keypress(ESCAPE); /* Success */ return (TRUE); } /* * Step 6 -- take "useless" things from the home (to be sold) */ static bool borg_think_home_grab_aux(void) { int n, b_n = -1; s32b s, b_s = 0L; /* Require two empty slots */ if (inven_num >= INVEN_PACK - 2) return (FALSE); /* Evaluate the home */ b_s = g_power + g_power_home; /* If the home is full, force one item to be picked up */ if (home_num == STORE_INVEN_MAX) b_s = 0; /* Scan the home */ for (n = 0; n < home_num; n++) { list_item *l_ptr = &borg_home[n]; /* If the home is full */ if (home_num == STORE_INVEN_MAX) { /* Remove the pile */ l_ptr->treat_as = TREAT_AS_GONE; } else { /* Remove one item */ l_ptr->treat_as = TREAT_AS_LESS; } /* Evaluate the home */ s = borg_power() + borg_power_home(); /* Restore item */ l_ptr->treat_as = TREAT_AS_NORM; /* Ignore "bad" sales */ if (s < b_s) continue; /* Maintain the "best" */ b_n = n; b_s = s; } /* Set the internally kept stats correctly */ g_power = borg_power(); g_power_home = borg_power_home(); /* Do nothing */ if (b_n == -1) return (FALSE); /* Visit the home */ goal_shop = home_shop; /* Grab that item */ borg_think_shop_buy(b_n); /* Success */ return (TRUE); } /* Use the weaponmaster to *id* weapons */ static bool borg_build_weaponmaster(void) { int i; list_item *l_ptr = NULL; /* Not when the borg is broke */ if (borg_gold < 2000) return (FALSE); /* Loop through the inv */ for (i = 0; i < inven_num + 1; i++) { if (i == 0) { /* First try the weapon that is used */ l_ptr = look_up_equip_slot(EQUIP_WIELD); } else { /* Then check out the inventory */ l_ptr = &inventory[i - 1]; } /* There is no item */ if (!l_ptr) return (FALSE); /* It has to be a weapon */ if (l_ptr->tval < TV_DIGGING || l_ptr->tval > TV_SWORD) continue; /* Are there unid'd items? */ if (borg_obj_known_p(l_ptr)) { /* Does this weapon need a *id*? */ if (borg_obj_known_full(l_ptr) || !borg_obj_star_id_able(l_ptr)) continue; } else { /* If the sensing shows it is interesting */ if (!strstr(l_ptr->o_name, "{excellent") || !strstr(l_ptr->o_name, "{special")) continue; } /* We found a weapon that needs *id*, but is it in the inv? */ if (i) { /* wield this weapon */ borg_keypress('w'); borg_keypress(I2A(i - 1)); } /* We know enough */ break; } /* nothing interesting in the inven */ if (!l_ptr || (i == inven_num + 1)) return (FALSE); /* *id* the weapon */ borg_keypress('E'); /* Make a note */ borg_note("# *Identifying* %s at the weaponmaster", l_ptr->o_name); return (TRUE); } /* How to handle the recharging building */ static bool borg_build_recharge(void) { int i, count = 0, max_pval = 0; char buf[4]; list_item *l_ptr = NULL; /* Does the borg have enough gold for an id? */ if (borg_gold < 700) return (FALSE); /* Loop through the inv */ for (i = 0; i < inven_num; i++) { /* Are there unid'd items? */ if (!borg_obj_known_p(&inventory[i])) count++; } /* Is the id worth it? */ if (count >= 4) { /* Identify the pack */ borg_keypress('I'); return (TRUE); } /* Does the borg have enough gold for a recharge? */ if (borg_gold < 2000) return (FALSE); /* Find out if there are wands or staffs in the inv */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; if (l_ptr->tval == TV_WAND || l_ptr->tval == TV_STAFF || l_ptr->tval == TV_ROD) { /* If the wand is not identified */ if (!borg_obj_known_p(l_ptr)) { /* Identify it */ borg_keypress('R'); borg_keypress(I2A(i)); borg_keypress('y'); borg_keypress(ESCAPE); borg_note("# Identifying %s at the zymurgist.", l_ptr->o_name); /* Success at doing something */ return (TRUE); } /* Don't bother to recharge rods */ if (l_ptr->tval == TV_ROD) continue; /* What is the maximum number of charges for this staff/wand */ max_pval = k_info[l_ptr->k_idx].pval; /* Find out the max charges */ if (l_ptr->tval == TV_STAFF) count = max_pval - l_ptr->pval; else count = l_ptr->number * max_pval - l_ptr->pval; if (count > 0) break; } } /* Nothing to charge */ if (!l_ptr || (i == inven_num)) return (FALSE); /* Get string corresponding to number */ (void)strnfmt(buf, 4, "%d\n", count); /* Say what you do */ borg_note("# Recharging %s at the zymurgist.", l_ptr->o_name); /* Do what you say */ borg_keypress('R'); borg_keypress(I2A(i)); borg_keypresses(buf); return (TRUE); } /* The borg may try to enchant his weapon in this building */ static bool borg_build_weapon(void) { int i; list_item *l_ptr = look_up_equip_slot(EQUIP_WIELD); /* Does the borg have enough gold? */ if (borg_gold < 2000) return (FALSE); /* Does the borg have a weapon at all? */ if (!l_ptr) return (FALSE); /* Is there improvement for the to_hit and to_dam bonus */ if (l_ptr->to_h >= bp_ptr->lev / 5 && l_ptr->to_d >= bp_ptr->lev / 3) return (FALSE); /* Let's go for it */ borg_keypress('E'); /* Find out if there are weapons in the inv */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; if (l_ptr->tval >= TV_DIGGING && l_ptr->tval <= TV_SWORD) { /* skip the inv and go to equip */ borg_keypress('/'); break; } } /* And enchant the weapon */ borg_keypress('a'); /* Say so */ borg_note("# Enchanting %s at the magesmith.", l_ptr->o_name); return (TRUE); } /* How to handle the enchant armour building */ static bool borg_build_armour(void) { int i, slot = -1; list_item *l_ptr; /* Does the borg have enough gold? */ if (borg_gold < 2000) return (FALSE); for (i = EQUIP_BODY; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* Is there armour here? */ if (!l_ptr) continue; /* Is there improvement for the to_hit and to_dam bonus */ if (l_ptr->to_a >= bp_ptr->lev / 5) continue; /* Found an armour */ slot = i; break; } /* Was an armour found? */ if (slot == -1) return (FALSE); /* Let's go for it */ borg_keypress('E'); /* Find out if there are armour in the inv */ for (i = 0; i < inven_num; i++) { l_ptr = &inventory[i]; if (l_ptr->tval >= TV_BOOTS && l_ptr->tval <= TV_DRAG_ARMOR) { /* skip the inv and go to equip */ borg_keypress('/'); break; } } /* And enchant the armour */ borg_keypress(I2A(slot)); /* Say so */ borg_note("# Enchanting %s at the magesmith.", l_ptr->o_name); return (TRUE); } /* How to handle the mapmaker building */ static bool borg_build_map(void) { int i, j; int x = c_x / WILD_BLOCK_SIZE, y = c_y / WILD_BLOCK_SIZE; int radius = 20, sum = 0, count = 0; /* Check the money */ if (borg_gold < 600) return (FALSE); /* Map a rough circle around the target position in the wilderness */ for (i = x - radius; i < x + radius + 1; i++) { for (j = y - radius; j < y + radius + 1; j++) { /* In bounds? */ if (i >= 0 && i < max_wild - 1 && j >= 0 && j < max_wild - 1) { if (distance(i, j, x, y) >= radius) continue; sum++; /* Hack! Check this location */ if (wild[j][i].done.info & WILD_INFO_SEEN) count++; } } } /* Does the borg already know its way around here? */ if (count * 2 > sum) return (FALSE); /* Look at the map */ borg_keypress('E'); return (TRUE); } /* Even the borg can be irrational */ static bool borg_build_casino(void) { int wager = MIN(borg_gold / 10, bp_ptr->lev * 1000); char buf[7]; /* No gambling when almost broke */ if (borg_gold < 500) return (FALSE); /* Get string corresponding to number */ (void)strnfmt(buf, 7, "%d\n", wager); /* In Between */ borg_keypress('I'); borg_keypresses(buf); borg_keypress('n'); /* Craps */ borg_keypress('C'); borg_keypresses(buf); borg_keypress('n'); /* Spin the Wheel */ borg_keypress('S'); borg_keypresses(buf); borg_keypress((char)(randint1(10) + '0')); borg_keypress('\n'); borg_keypress('n'); /* Dice Slots */ borg_keypress('D'); borg_keypresses(buf); borg_keypress('n'); /* One visit to this building is enough */ return (FALSE); } /* Get food and rest at the inn */ static bool borg_build_inn(void) { int i; list_item *l_ptr; bool rest = TRUE; /* Is it light outside */ rest &= (bp_ptr->hour < 6 || bp_ptr->hour > 17); /* This is a respectable place */ rest &= !bp_ptr->status.cut && !bp_ptr->status.poisoned; /* Check the vampire flag */ rest &= !FLAG(bp_ptr, TR_HURT_LITE); /* Loop through the equipment */ for (i = 0; i < equip_num; i++) { l_ptr = look_up_equip_slot(i); /* Check the equipment for that flag */ if (l_ptr) rest &= !KN_FLAG(l_ptr, TR_HURT_LITE); } /* If the borg wants a rest */ if (rest && borg_gold > 25) { /* Wait for daybreak */ borg_keypress('R'); } /* Can the borg use more food? */ if (!bp_ptr->status.full && borg_gold > 25) { /* Have dinner */ borg_keypress('E'); } /* One pass takes care of all needs */ return (FALSE); } /* How to handle the healer building */ static bool borg_build_healer(void) { int i, count = 0; /* Expensive here */ if (borg_gold < 15000) return (FALSE); /* Check the stats (but not CHR) */ for (i = 0; i < 5; i++) { /* Count how many stats need fixing */ if (bp_ptr->status.fixstat[i]) count++; } /* Don't bother for just one stat */ if (count < 2) return (FALSE); /* Heal me */ borg_keypress('R'); /* One visit is enough */ return (FALSE); } /* What to do inside a magetower */ static bool borg_build_magetower(void) { /* No need to be here when broke */ if (borg_gold < 2000) return (FALSE); /* Is the borg registered here? */ if (borg_term_text_comp(39, 18, "Record aura")) { /* Register */ borg_keypress('R'); return (TRUE); } return (FALSE); } /* What to do inside a castle? */ static bool borg_build_castle(bool large) { /* Is the borg registered here? */ if (borg_term_text_comp(35, 19, "Request Quest")) { /* Get a quest */ borg_keypress('R'); if (borg_gold < 30000) { /* Deliver a message */ borg_keypress('b'); } else { if (large) { /* Find an artifact */ borg_keypress('d'); } else { /* Kill monster quest */ borg_keypress('a'); } } /* Success */ return (TRUE); } /* Try to get a reward */ else { /* Get a quest */ borg_keypress('R'); /* To avoid pressing this button again just leave the building */ return (FALSE); } } /* Handle the buildings that are not stores */ static bool borg_think_building(void) { /* Only the funny buildings have a type */ switch (borg_shops[shop_num].type) { case BUILD_WEAPONMASTER: return (borg_build_weaponmaster()); case BUILD_RECHARGE: return (borg_build_recharge()); case BUILD_PLUS_WEAPON: return (borg_build_weapon()); case BUILD_PLUS_ARMOUR: return (borg_build_armour()); case BUILD_MUTATE: return (FALSE); case BUILD_MAP: return (borg_build_map()); case BUILD_LIBRARY: return (FALSE); case BUILD_CASINO: return (borg_build_casino()); case BUILD_INN: return (borg_build_inn()); case BUILD_HEALER: return (borg_build_healer()); case BUILD_MAGETOWER0: case BUILD_MAGETOWER1: return (borg_build_magetower()); case BUILD_CASTLE0: return (borg_build_castle(FALSE)); case BUILD_CASTLE1: return (borg_build_castle(TRUE)); default: return (FALSE); } } /* * Deal with being in a store */ bool borg_think_store(void) { int i; /* Set the internally kept stats correctly */ g_power = borg_power(); g_power_home = borg_power_home(); /* Clear goal */ goal = GOAL_NONE; /* Retrieve the correct place */ for (i = 0; i < borg_shop_num; i++) { /* Find the right coords */ if (c_x == borg_shops[i].x && c_y == borg_shops[i].y) { /* Get the shop */ shop_num = i; break; } } /* This shop is not known! */ if (i == borg_shop_num) { borg_oops("Unknown shop entered"); return (FALSE); } /* Stamp the shop with a time stamp */ borg_shops[shop_num].when = borg_t; borg_shops[shop_num].visit = TRUE; borg_towns[borg_shops[shop_num].town_num].visit = TRUE; /* Increment 'been' count */ borg_shops[shop_num].b_count++; if (borg_shops[shop_num].type) { /* Check out the funny shops */ if (borg_think_building()) return (TRUE); } else { /* Remove "useless" equipment */ if (borg_unwear_stuff()) return (TRUE); /* Wear good stuff */ if (borg_wear_stuff()) return (TRUE); /* Select what we want to do */ if (shop_num == home_shop) { /* Step 1 -- Sell items to the home */ if (borg_think_home_sell_aux()) return (TRUE); /* Step 4 -- Buy items from the home (for the player) */ if (borg_think_home_buy_aux()) return (TRUE); /* Step 5 -- Grab items from the home (for the shops) */ if (borg_think_home_grab_aux()) return (TRUE); borg_note("# Nothing to do at home."); } else { /* Step 2 -- Sell items to the shops */ if (borg_think_shop_sell_aux(shop_num)) return (TRUE); /* Step 3 -- Buy items from the shops (for the player) */ if (borg_think_shop_buy_aux(shop_num)) return (TRUE); /* Step 6 -- Buy items from the shops (for the home) */ if (borg_think_shop_grab_aux(shop_num)) return (TRUE); borg_note("# Nothing to do in the store."); } } /* Leave the store */ borg_keypress(ESCAPE); /* Assume no important shop */ goal_shop = -1; /* Choose a shop to visit */ if (borg_choose_shop()) return (TRUE); /* No shop */ shop_num = -1; /* Done */ return (TRUE); } /* * Hack -- perform an action in the dungeon under boosted bravery * * This function is a sub-set of the standard dungeon goals, and is * only executed when all of the standard dungeon goals fail, because * of excessive danger, or because the level is "bizarre". */ static bool borg_think_dungeon_brave(void) { int dir; /*** Local stuff ***/ /* Attack monsters */ if (borg_attack(TRUE)) return (TRUE); /* Test a light beam to remove fear of an area */ if (borg_lite_beam(TRUE, &dir)) { /* Cast that beam */ (void)borg_lite_beam(FALSE, &dir); /* Ready */ return (TRUE); } /*** Flee (or leave) the level ***/ /* Take stairs down from town */ if (bp_ptr->depth == 0) { /* Current grid */ map_block *mb_ptr = map_loc(c_x, c_y); /* Usable stairs */ if (mb_ptr->feat == FEAT_MORE) { /* Take the stairs */ borg_note("# Fleeing town via Stairs."); borg_keypress('>'); /* If the borg leaves the wilderness */ if (!bp_ptr->depth) borg_leave_surface(); /* Success */ return (TRUE); } } /* Return to Stairs, but not use them */ if (goal_less) { /* Continue fleeing to stair */ if (borg_flow_old(GOAL_FLEE)) return (TRUE); /* Try to find some stairs */ if (scaryguy_on_level && !bp_ptr->depth && borg_flow_stair_both(GOAL_FLEE)) return (TRUE); /* Try to find some stairs up */ if (borg_flow_stair_less(GOAL_FLEE)) return (TRUE); } /* Flee the level */ if (goal_fleeing || goal_leaving || scaryguy_on_level) { /* Hack -- Take the next stairs */ stair_less = goal_fleeing; /* Only go down if fleeing or prepared. */ stair_more = goal_fleeing; if (!borg_prepared(bp_ptr->depth + 1)) stair_more = TRUE; /* Continue fleeing the level */ if (borg_flow_old(GOAL_FLEE)) return (TRUE); /* Try to find some stairs up */ if (stair_less) if (borg_flow_stair_less(GOAL_FLEE)) return (TRUE); /* Try to find some stairs down */ if (stair_more) if (borg_flow_stair_more(GOAL_FLEE)) return (TRUE); /* Try to hide on a glyph if no stairs */ if (borg_flow_glyph(GOAL_FLEE)) return (TRUE); } /* Do short looks on special levels */ if (vault_on_level) { /* Continue flowing towards monsters */ if (borg_flow_old(GOAL_KILL)) return (TRUE); /* Find a (viewable) monster */ if (borg_flow_kill(TRUE, 35)) return (TRUE); /* Continue flowing towards objects */ if (borg_flow_old(GOAL_TAKE)) return (TRUE); /* Find a (viewable) object */ if (borg_flow_take(TRUE, 35)) return (TRUE); } /* Continue flowing towards monsters */ if (borg_flow_old(GOAL_KILL)) return (TRUE); /* Find a (viewable) monster */ if (borg_flow_kill(TRUE, 250)) return (TRUE); /* Continue flowing towards objects */ if (borg_flow_old(GOAL_TAKE)) return (TRUE); /* Find a (viewable) object */ if (borg_flow_take(TRUE, 250)) return (TRUE); /*** Exploration ***/ /* Continue flowing (see below) */ if (borg_flow_old(GOAL_SHOP)) return (TRUE); /* Continue flowing (see below) */ if (borg_flow_old(GOAL_DARK)) return (TRUE); /* Continue flowing (see below) */ if (borg_flow_old(GOAL_XTRA)) return (TRUE); /* Continue flowing (see below) */ if (borg_flow_old(GOAL_BORE)) return (TRUE); /*** Explore the dungeon ***/ /* Explore interesting grids */ if (borg_flow_dark(TRUE)) return (TRUE); /* Explore interesting grids */ if (borg_flow_dark(FALSE)) return (TRUE); /* Search for secret doors */ if (borg_flow_spastic(FALSE)) return (TRUE); /*** Track down old stuff ***/ /* Chase old objects */ if (borg_flow_take(FALSE, 250)) return (TRUE); /* Chase old monsters */ if (borg_flow_kill(FALSE, 250)) return (TRUE); /* Search for secret doors */ if (borg_flow_spastic(TRUE)) return (TRUE); /* Nothing */ return (FALSE); } /* * Perform an action in the dungeon * * Return TRUE if a "meaningful" action was performed * Otherwise, return FALSE so we will be called again * * Strategy: * Make sure we are happy with our "status" (see above) * Attack and kill visible monsters, if near enough * Open doors, disarm traps, tunnel through rubble * Pick up (or tunnel to) gold and useful objects * Explore "interesting" grids, to expand the map * Explore the dungeon and revisit old grids * * Fleeing: * Use word of recall when level is "scary" * Flee to stairs when there is a chance of death * Avoid "stair bouncing" if at all possible * * Note that the various "flow" actions allow the Borg to flow * "through" closed doors, which will be opened when he attempts * to pass through them, so we do not have to pursue terrain until * all monsters and objects have been dealt with. * * XXX XXX XXX The poor Borg often kills a nasty monster, and * then takes a nap to recover from damage, but gets yanked * back to town before he can collect his reward. */ bool borg_think_dungeon(void) { int i, j; int msec = (delay_factor * delay_factor * delay_factor); /* Hack -- prevent clock wrapping */ if (borg_t >= 20000000) { /* Panic */ borg_oops("clock overflow"); /* Oops */ return (TRUE); } /* Add a short pause to slow the borg down for viewing */ Term_xtra(TERM_XTRA_DELAY, msec); /* Prevent clock overflow */ if (borg_t - borg_began >= 100000) { /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note("# Leaving (boredom)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Note */ borg_note("# Fleeing (boredom)"); /* Start fleeing */ goal_fleeing = TRUE; } } /* Avoid the burning sun */ if (FLAG(bp_ptr, TR_HURT_LITE) && !FLAG(bp_ptr, TR_RES_LITE) && !bp_ptr->depth && bp_ptr->hour > 5 && bp_ptr->hour < 18) { /* Get out of the Sun */ if (!goal_fleeing) { /* Flee */ borg_note("# Avoiding Sunlight."); /* Ignore multipliers */ goal_fleeing = TRUE; } } /* Count the awake breeders */ for (j = 0, i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; monster_race *r_ptr; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Skip sleeping monsters */ if (kill->m_flags & MONST_ASLEEP) continue; r_ptr = &r_info[kill->r_idx]; /* Count the monsters which are "breeders" */ if (FLAG(r_ptr, RF_MULTIPLY)) j++; } /* hack -- close doors on breeder levles */ if (j >= 8) { /* set the flag to close doors */ breeder_level = TRUE; } /* Hack -- caution from breeders */ if ((j >= MIN(bp_ptr->lev, 5)) && (!bp_ptr->recall || (bp_ptr->lev < 35))) { /* Ignore monsters from caution */ if (!goal_ignoring) { /* Flee */ borg_note("# Ignoring breeders (no recall)"); /* Ignore multipliers */ goal_ignoring = TRUE; } /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note("# Leaving (no recall)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Note */ borg_note("# Fleeing (no recall)"); /* Start fleeing */ goal_fleeing = TRUE; } } /* Reset avoidance */ if (avoidance != bp_ptr->chp) { /* Reset "avoidance" */ avoidance = bp_ptr->chp; /* Re-calculate danger */ borg_danger_wipe = TRUE; } /*** crucial goals ***/ /* Set the internally kept stats correctly */ g_power = borg_power(); g_power_home = borg_power_home(); /* require light-- */ if (!bp_ptr->cur_lite && (bp_ptr->depth >= 1)) { if (goal_recalling) { /* just wait */ borg_keypress('R'); borg_keypress('9'); borg_keypress('\n'); return (TRUE); } /* attempt to refuel */ if (borg_refuel()) return (TRUE); /* wear stuff and see if it glows */ if (borg_wear_stuff()) return (TRUE); /* Can I recall out with a rod */ if (!goal_recalling && borg_recall()) return (TRUE); /* Test for stairs */ if (map_loc(c_x, c_y)->feat == FEAT_LESS) { /* Take it */ borg_keypress('<'); return (TRUE); } /* Try to flow to a lite if I can recall */ if (bp_ptr->recall) { /* Can I recall out with a spell */ if (borg_flow_light(GOAL_FLEE)) return (TRUE); } } /* Decrease the amount of time not allowed to retreat */ if (borg_no_retreat > 0) borg_no_retreat--; /*** Important goals ***/ /* Try not to die */ if (borg_caution()) return (TRUE); /* Get to a non-hurting feat */ if (borg_flow_non_hurt()) return (TRUE); /*** if returning from dungeon in bad shape...***/ if (!bp_ptr->cur_lite || bp_ptr->status.cut || bp_ptr->status.poisoned || bp_ptr->status.weak) { /* First try to wear something */ if (!bp_ptr->cur_lite) { /* attempt to refuel */ if (borg_refuel()) return (TRUE); /* wear stuff and see if it glows */ if (borg_wear_stuff()) return (TRUE); } /* Recover from damage */ if (borg_recover()) return (TRUE); /* Continue flowing (see below) */ if (borg_flow_old(GOAL_TOWN)) return (TRUE); /* Shop for something that will help us */ if (borg_choose_shop()) { /* Try and visit a shop, if so desired */ if (borg_flow_shop_entry(goal_shop)) return (TRUE); } } /* Learn useful spells immediately */ if (borg_play_magic(FALSE)) return (TRUE); /* If using a digger, Wear "useful" equipment before fighting monsters */ if (equipment[EQUIP_WIELD].tval == TV_DIGGING && borg_wear_stuff()) { return (TRUE); } /* Attack monsters */ if (borg_attack(FALSE)) return (TRUE); /* Wear things that need to be worn */ if (borg_wear_stuff()) return (TRUE); /* Take off things that have become useless */ if (borg_unwear_stuff()) return (TRUE); /* Check the light */ if (borg_check_lite()) return (TRUE); /* Recover from damage */ if (borg_recover()) return (TRUE); /* Perform "cool" perma spells */ if (borg_perma_spell()) return (TRUE); /*** Flee the level XXX XXX XXX ***/ /* Return to Stairs, but not use them */ if (goal_less) { /* Continue fleeing to stair */ if (borg_flow_old(GOAL_FLEE)) return (TRUE); /* Try to find some stairs */ if (scaryguy_on_level && !bp_ptr->depth && borg_flow_stair_both(GOAL_FLEE)) return (TRUE); /* Try to find some stairs up */ if (borg_flow_stair_less(GOAL_FLEE)) return (TRUE); } /* Flee the level */ if (goal_fleeing && !goal_recalling) { /* Hack -- Take the next stairs */ stair_less = stair_more = TRUE; /* Continue fleeing the level */ if (borg_flow_old(GOAL_FLEE)) return (TRUE); /* Try to find some stairs */ if (scaryguy_on_level && !bp_ptr->depth && borg_flow_stair_both(GOAL_FLEE)) return (TRUE); /* Try to find some stairs up */ if (borg_flow_stair_less(GOAL_FLEE)) return (TRUE); /* Try to find some stairs down */ if (borg_flow_stair_more(GOAL_FLEE)) return (TRUE); /* Try to hide on a glyph if no stairs */ if (borg_flow_glyph(GOAL_FLEE)) return (TRUE); } /* Continue flowing towards monsters */ if (borg_flow_old(GOAL_KILL)) return (TRUE); /* Find a (viewable) monster */ if (borg_flow_kill(TRUE, 250)) return (TRUE); /* Find a viewable monster and line up a shot on him */ /* Disabled because it is buggy, causing live-lock. if (borg_flow_kill_aim(TRUE)) return (TRUE); */ /* Dig an anti-summon corridor */ if (borg_flow_kill_corridor(TRUE)) return (TRUE); /*** Deal with inventory objects ***/ /* Use things */ if (borg_use_things()) return (TRUE); /* Try to identify things */ if (borg_id_meta()) return (TRUE); /* Enchant things */ if (borg_enchanting()) return (TRUE); /* Recharge things */ if (borg_recharging()) return (TRUE); /* Maybe destroy an item */ if (borg_destroy()) return (TRUE); /*** Flow towards objects ***/ /* Continue flowing towards objects */ if (borg_flow_old(GOAL_TAKE)) return (TRUE); /* Find a (viewable) object */ if (borg_flow_take(TRUE, 250)) return (TRUE); /*** Leave the level XXX XXX XXX ***/ /* Leave the level */ if (goal_leaving && !goal_recalling && !unique_on_level) { /* Only go down if fleeing or prepared. */ if (!borg_prepared(bp_ptr->depth + 1)) stair_more = TRUE; /* Continue leaving the level */ if (borg_flow_old(GOAL_FLEE)) return (TRUE); /* Try to find some stairs up */ if (stair_less) if (borg_flow_stair_less(GOAL_FLEE)) return (TRUE); /* Try to find some stairs down */ if (stair_more) if (borg_flow_stair_more(GOAL_FLEE)) return (TRUE); } /*** Exploration ***/ /* Continue flowing for low importance stuff */ if (goal >= GOAL_SHOP && goal < GOAL_MAX && borg_flow_old(goal)) return (TRUE); /*** Explore the dungeon ***/ /* Chase close monsters */ if (borg_flow_kill(FALSE, 35)) return (TRUE); /* Chase close objects */ if (borg_flow_take(FALSE, 35)) return (TRUE); /* Chase old monsters */ if (borg_flow_kill(FALSE, 250)) return (TRUE); /* Chase old objects */ if (borg_flow_take(FALSE, 250)) return (TRUE); /* Explore interesting grids */ if (borg_flow_dark(TRUE)) return (TRUE); /* Possibly leave the level (not bored) */ if (borg_leave_level(FALSE)) return (TRUE); /* Explore interesting grids */ if (borg_flow_dark(FALSE)) return (TRUE); /*** Deal with shops ***/ if (borg_find_shop()) return (TRUE); /*** Leave the Level ***/ /* Study/Test boring spells/prayers */ if (!goal_fleeing && borg_play_magic(TRUE)) return (TRUE); /* Search for secret doors */ if (borg_flow_spastic(FALSE)) return (TRUE); /* Recharge items before leaving the level */ if (borg_wait_recharge()) return (TRUE); /* Leave the level (bored) */ if (borg_leave_level(TRUE)) return (TRUE); /* Search for secret doors */ if (borg_flow_spastic(TRUE)) return (TRUE); /*** Wait for recall ***/ /* Wait for recall, unless in danger */ if (goal_recalling && borg_on_safe_feat(map_loc(c_x, c_y)->feat) && (borg_danger(c_x, c_y, 1, TRUE) <= 0)) { /* Take note */ borg_note("# Waiting for Recall..."); /* Rest until done */ borg_keypress('R'); borg_keypress('\n'); /* Done */ return (TRUE); } /*** Nothing to do ***/ /* Wait for daylight */ if (borg_waits_daylight()) return (TRUE); /* Try to cross the wilderness to do some fun shopping */ if (borg_find_town()) return (TRUE); /* Try to cross the wilderness to find a challenging dungeon */ if (borg_find_dungeon()) return (TRUE); /* Explore the wilderness */ if (borg_flow_dark_wild()) return (TRUE); /* Set a flag that the borg is not allowed to retreat for 5 rounds */ borg_no_retreat = 5; /* Boost slightly */ if (avoidance < bp_ptr->chp * 2) { bool done = FALSE; /* Note */ borg_note("# Boosting bravery (1) from %d to %d!", avoidance, bp_ptr->chp * 2); /* Hack -- ignore some danger */ avoidance = (bp_ptr->chp * 2); /* Forget the danger fields */ borg_danger_wipe = TRUE; /* Try anything */ if (borg_think_dungeon_brave()) done = TRUE; /* Reset "avoidance" */ avoidance = bp_ptr->chp; /* Re-calculate danger */ borg_danger_wipe = TRUE; /* Done */ if (done) return (TRUE); } /* Try phase before boosting bravery further and acting goofy */ borg_times_twitch++; /* Phase to get out of being twitchy up to 3 times per level. */ if (bp_ptr->depth && borg_times_twitch < 3) { borg_note("# Considering Phase (twitchy)"); /* Phase */ if (bp_ptr->able.phase && borg_caution_phase(15, 2) && (borg_spell(REALM_SORCERY, 0, 1) || borg_spell(REALM_TRUMP, 0, 0) || borg_spell(REALM_ARCANE, 0, 4) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Success */ return (TRUE); } } /* Set a flag that the borg is not allowed */ /* to retreat for 10 rounds */ borg_no_retreat = 10; /* Boost some more */ if (avoidance < bp_ptr->mhp * 4) { bool done = FALSE; /* Note */ borg_note("# Boosting bravery (2) from %d to %d!", avoidance, bp_ptr->mhp * 4); /* Hack -- ignore some danger */ avoidance = (bp_ptr->mhp * 4); /* Forget the danger fields */ borg_danger_wipe = TRUE; /* Try anything */ if (borg_think_dungeon_brave()) done = TRUE; /* Reset "avoidance" */ avoidance = bp_ptr->chp; /* Re-calculate danger */ borg_danger_wipe = TRUE; /* Done */ if (done) return (TRUE); } /* Boost a lot */ if (avoidance < 30000) { bool done = FALSE; /* Note */ borg_note("# Boosting bravery (3) from %d to %d!", avoidance, 30000); /* Hack -- ignore some danger */ avoidance = 30000; /* Forget the danger fields */ borg_danger_wipe = TRUE; /* Try anything */ if (borg_think_dungeon_brave()) done = TRUE; /* Reset "avoidance" */ avoidance = bp_ptr->chp; /* Re-calculate danger */ borg_danger_wipe = TRUE; /* Done */ if (done) return (TRUE); } /* try teleporting before acting goofy */ borg_times_twitch++; /* Teleport to get out of being twitchy up to 5 times per level. */ if (bp_ptr->depth && borg_times_twitch < 5) { borg_note("# Teleport (twitchy)"); /* Teleport */ if (borg_activate(BORG_ACT_TELEPORT) || borg_spell_fail(REALM_ARCANE, 2, 3, 45) || borg_spell_fail(REALM_TRUMP, 0, 4, 45) || borg_spell_fail(REALM_CHAOS, 0, 7, 45) || borg_spell_fail(REALM_SORCERY, 0, 5, 45) || borg_use_staff(SV_STAFF_TELEPORTATION) || borg_read_scroll(SV_SCROLL_TELEPORT)) { /* Success */ return (TRUE); } } /* Recall to town */ if (bp_ptr->depth && (borg_recall())) { /* Note */ borg_note("# Recalling (twitchy)"); /* Success */ return (TRUE); } /* Twitch around */ if (borg_twitchy()) return (TRUE); /* Oops */ return (FALSE); } /* * Initialize this file */ void borg_init_8(void) { /* Nothing */ } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/zborg9.c0000644000000000000000000030746410250356275013532 0ustar rootroot/* File: zborg9.c */ /* Purpose: Highest level functions for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" #include "zborg5.h" #include "zborg6.h" #include "zborg7.h" #include "zborg8.h" #include "zborg9.h" /* * This file implements the "APWBorg", an "Automatic Angband Player". * * This version of the "APWBorg" is designed for use with ZAngband 2.7.x. * * Use of the "APWBorg" requires re-compilation with ALLOW_BORG defined, * and with the various "zborg*.c" files linked into the executable. * * Note that you can only use the Borg if your character has been marked * as a "Borg User". You can do this, if necessary, by responding "y" * when asked if you really want to use the Borg. This will (normally) * result in your character being inelligible for the high score list. * * The "do_cmd_borg()" function, called when the user hits "^Z", allows * the user to interact with the Borg. You do so by typing "Borg Commands", * including 'z' to activate (or re-activate), 'K' to show monsters, 'T' to * show objects, 'd' to toggle "demo mode", 'f' to open/shut the "log file", * 'i' to display internal flags, etc. See "do_cmd_borg()" for more info. * * The first time you enter a Borg command, the Borg is initialized. This * consists of three major steps, and requires at least 400K of free memory, * if the memory is not available, the game may abort. * * (1) The various "borg" modules are initialized. * * (2) Some important "state" information is extracted, including the level * and race/class of the player, and some more initialization is done. * * (3) Some "historical" information (killed uniques, maximum dungeon depth) * is "stolen" from the game. * * When the Borg is "activated", it uses the "Term_inkey_hook" to steal * control from the user. Later, if it detects any input from the real user, * it gracefully relinquishes control by clearing the "Term_inkey_hook" after * any pending key-sequences are complete. * * The Borg will abort if it detects any "errors", or if it detects any * "situations" such as "death", or if it detects any "panic" situations, * such as "imminent death", if the appropriate flags are set. * * The Borg is only supposed to "know" what is visible on the screen, * which it learns by using the "term.c" screen access function "Term_what()", * the cursor location function "Term_locate()", and the cursor visibility * extraction function "Term_get_cursor()". * * The Borg is only supposed to "send" keypresses when the "Term_inkey()" * function asks for a keypress, which is accomplished by using a special * function hook in the "z-term.c" file, which allows the Borg to "steal" * control from the "Term_inkey()" and "Term_flush()" functions. This * allows the Borg to pretend to be a normal user. * * Note that if properly designed, the Borg could be run as an external * process, which would actually examine the screen (or pseudo-terminal), * and send keypresses directly to the keyboard (or pseudo-terminal). Thus * it should never access any "game variables", unless it is able to extract * those variables for itself by code duplication or complex interactions, * or, in certain situations, if those variables are not actually "required". * * Currently, the Ben Borg is a few steps away from being able to be run as * an external process, primarily in the "low level" details, such as knowing * when the game is ready for a keypress. Also, the Ben Borg assumes that a * character has already been rolled, and maintains no state between saves, * which is partially offset by "cheating" to "acquire" the maximum dungeon * depth, without which equipment analysis will be faulty. * * The "theory" behind the Borg is that is should be able to run as a * separate process, playing Angband in a window just like a human, that * is, examining the screen for information, and sending keypresses to * the game. The current Borg does not actually do this, because it would * be very slow and would not run except on Unix machines, but as far as * possible, I have attempted to make sure that the Borg *could* run that * way. This involves "cheating" as little as possible, where "cheating" * means accessing information not available to a normal Angband player. * And whenever possible, this "cheating" should be optional, that is, * there should be software options to disable the cheating, and, if * necessary, to replace it with "complex" parsing of the screen. * * Thus, the Borg COULD be written as a separate process which runs Angband * in a pseudo-terminal and "examines" the "screen" and sends keypresses * directly (as with a terminal emulator), although it would then have * to explicitly "wait" to make sure that the game was completely done * sending information. * * The Borg is thus allowed to examine the screen directly (by efficient * direct access of the "Term->scr->a" and "Term->scr->c" arrays, which * could be replaced by calls to "Term_grab()"), and to access the cursor * location (via "Term_locate()") and visibility (via "Term_get_cursor()"), * and, as mentioned above, the Borg is allowed to send keypresses directly * to the game, and only when needed, using the "Term_inkey_hook" hook, and * uses the same hook to know when it should discard all pending keypresses. * * The Borg should not know when the game is ready for a keypress, and * should really do something nasty such as "pause" between turns for * some amount of time to ensure that the game is really waiting for * a keypress. * * Various other "cheats" (mostly optional) are described where they are * used, primarily in this file. * * Note that any "user input" will be ignored, and will cancel the Borg, * after the Borg has completed any key-sequences currently in progress. * * Note that the "borg_t" parameter bears a close resemblance to the number of * "player turns" that have gone by. Except that occasionally, the Borg will * do something that he *thinks* will take time but which actually does not * (for example, attempting to open a hallucinatory door), and that sometimes, * the Borg performs a "repeated" command (rest, open, tunnel, or search), * which may actually take longer than a single turn. This has the effect * that the "borg_t" variable is slightly lacking in "precision". * * Note that as of 2.7.9, the Borg can play any class, that is, he can make * "effective" use of at least a few spells/prayers, and is not "broken" * by low strength, blind-ness, hallucination, etc. This does not, however, * mean that he plays all classes equally well, especially since he is so * dependant on a high strength for so many things. The "demo" mode is useful * for many classes (especially Mage) since it allows the Borg to "die" a few * times, without being penalized. * * The Borg assumes that the "maximize" flag is off, and that the * "preserve" flag is on, since he cannot actually set those flags. * If the "maximize" flag is on, the Borg may not work correctly. * If the "preserve" flag is off, the Borg may miss artifacts. */ /* * We currently handle: * Level changing (intentionally or accidentally) * Embedded objects (gold) that must be extracted * Ignore embedded objects if too "weak" to extract * Traps (disarm), Doors (open/etc), Rubble (tunnel) * Stores (enter via movement, exit via escape) * Stores (limited commands, and different commands) * Always deal with objects one at a time, not in piles * Discard junk before trying to pick up more stuff * Use "identify" to obtain knowledge and/or money * Rely on "sensing" objects as much as possible * Do not sell junk or worthless items to any shop * Do not attempt to buy something without the cash * Use the non-optimal stairs if stuck on a level * Use "flow" code for all tasks for consistency * Cancel all goals when major world changes occur * Use the "danger" code to avoid potential death * Use the "danger" code to avoid inconvenience * Try to avoid danger (both actively and passively) * Handle "Mace of Disruption", "Scythe of Slicing", etc * Learn spells, and use them when appropriate * Remember that studying prayers is slightly random * Do not try to read scrolls when blind or confused * Do not study/use spells/prayers when blind/confused * Use spells/prayers at least once for the experience * Attempt to heal when "confused", "blind", etc * Attempt to fix "fear", "poison", "cuts", etc * Analyze potential equipment in proper context * Priests should avoid edged weapons (spell failure) * Mages should avoid most gloves (lose mana) * Non-warriors should avoid heavy armor (lose mana) * Keep "best" ring on "tight" right finger in stores * Remove items which do not contribute to total fitness * Wear/Remove/Sell/Buy items in most optimal order * Pursue optimal combination of available equipment * Notice "failure" when using rods/staffs/artifacts * Notice "failure" when attempting spells/prayers * Attempt to correctly track terrain, objects, monsters * Take account of "clear" and "multi-hued" monsters * Take account of "flavored" (randomly colored) objects * Handle similar objects/monsters (mushrooms, coins) * Multi-hued/Clear monsters, and flavored objects * Keep in mind that some monsters can move (quickly) * Do not fire at monsters that may not actually exist * Assume everything is an object until proven otherwise * Parse messages to correct incorrect assumptions * Search for secret doors after exploring the level * React intelligently to changes in the wall structure * Do not recalculate "flow" information unless required * Collect monsters/objects/terrain not currently in view * Keep in mind that word of recall is a delayed action * Keep track of charging items (rods and artifacts) * Be very careful not to access illegal locations! * Do not rest next to dangerous (or immobile) monsters * Recall into dungeon if prepared for resulting depth * Do not attempt to destroy cursed ("terrible") artifacts * Attempted destruction will yield "terrible" inscription * Use "maximum" level and depth to prevent "thrashing" * Use "maximum" hp's and sp's when checking "potentials" * Attempt to recognize large groups of "disguised" monsters * Beware of firing at a monster which is no longer present * Stockpile items in the Home, and use those stockpiles * Discounted spell-books (low level ones are ignored) * Take items out of the home to sell them when no longer needed * Trappers and Mimics (now treated as invisible monsters) * Invisible monsters (induce "fear" of nearby regions) * Fleeing monsters are "followed" down corridors and such * * We ignore: * Long object descriptions may have clipped inscriptions * * We need to handle: * Technically a room can have no exits, requiring digging * Conserve memory space (each grid byte costs about 15K) * Conserve computation time (especially with the flow code) * * We need to handle "loading" saved games: * The "max_depth" value is lost if loaded in the town * If we track "dead uniques" then this information is lost * The "map" info, "flow" info, "tracking" info, etc is lost * The contents of the shops (and the home) are lost * We may be asked to "resume" a non-Borg character (icky) */ /* * Currently, the Borg "cheats" in a few situations... * * Cheats that are significant, and possibly unavoidable: * Knowledge of when we are being asked for a keypress. * Note that this could be avoided by LONG timeouts/delays * * Cheats "required" by implementation, but not signifant: * Direct access to the "screen image" (parsing screen) * Direct access to the "keypress queue" (sending keys) * Direct access to the "cursor visibility" (game state) * * Cheats that could be avoided by simple (ugly) code: * Direct modification of the "current options" * * Cheats that could be avoided by duplicating code: * Use of the tables in "tables.c" * Use of the arrays initialized in "init.c" * * Cheats that the Borg would love: * Immunity to hallucination, blindness, confusion * Unique attr/char codes for every monster and object * Removal of the "mimic" and "trapper" monsters * Removal of the "mushroom" and "gold" monsters */ /* * Stat advantages: * High STR (attacks, to-dam, digging, weight limit) * High DEX (attacks, to-hit, armor class) * High CON (hitpoints, recovery) * High WIS (better prayers, saving throws) * High INT (better spells, disarming, device usage) * High CHR (better item costs) * * Class advantages: * Warrior (good fighting, sensing) * Mage (good spells) * Priest (good prayers, fighting) * Ranger (some spells, fighting) * Rogue (some spells, fighting, sensing) * Paladin (prayers, fighting, sensing) * * Race advantages: * Gnome (free action) * Dwarf (resist blindness) * High elf (see invisible) * Non-human (infravision) */ /* * Some variables */ /* Is the borg initialized yet? */ static bool initialized; /* * Mega-Hack -- extract some "hidden" variables * * XXX XXX XXX This step would not be necessary if more info * was available on the screen. * */ static void borg_hidden(void) { int i; /* Clear the stat modifiers */ for (i = 0; i < A_MAX; i++) my_stat_add[i] = 0; /* Scan the usable inventory */ for (i = 0; i < equip_num; i++) { list_item *l_ptr = look_up_equip_slot(i); /* Skip empty items */ if (!l_ptr) continue; /* Affect stats */ if (KN_FLAG(l_ptr, TR_STR)) my_stat_add[A_STR] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_INT)) my_stat_add[A_INT] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_WIS)) my_stat_add[A_WIS] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_DEX)) my_stat_add[A_DEX] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_CON)) my_stat_add[A_CON] += l_ptr->pval; if (KN_FLAG(l_ptr, TR_CHR)) my_stat_add[A_CHR] += l_ptr->pval; } /* Mega-Hack -- Guess at "my_stat_cur[]" */ for (i = 0; i < A_MAX; i++) { if (!my_need_stat_check[i]) continue; /* Hack - get current internal stat value */ my_stat_cur[i] = p_ptr->stat[i].cur; /* Max stat is the max that the cur stat ever is. */ if (my_stat_cur[i] > my_stat_max[i]) { my_stat_max[i] = my_stat_cur[i]; } } } /* * Think about the world and perform an action * * Check inventory/equipment/spells/panel once per "turn" * * Process "store" and other modes when necessary * * Technically, we should attempt to parse all the messages that * indicate that it is necessary to re-parse the books, and only * set the appropriate flags at that point. This would not only * reduce the potential screen flashing, but would also optimize * the code a lot. For paranoia, we could always select items * and spells using capital letters. */ static bool borg_think(void) { int i; static char svSavefile[1024]; static bool justSaved = FALSE; /*** Process inventory/equipment ***/ /* save now */ if (borg_save && borg_save_game()) { /* Log */ borg_note("# Auto Save!"); borg_save = FALSE; return (TRUE); } if (justSaved) { memcpy(savefile, svSavefile, sizeof(savefile)); borg_save_game(); justSaved = FALSE; return (TRUE); } /*** Find books ***/ /* Only if needed */ if (borg_do_spell) { /* Assume no books */ for (i = 0; i < 4; i++) { borg_book[bp_ptr->realm1][i] = -1; borg_book[bp_ptr->realm2][i] = -1; } /* Scan the pack */ for (i = 0; i < inven_num; i++) { list_item *l_ptr = &inventory[i]; int realm; /* Stop after the books have run out */ if (l_ptr->tval < TV_BOOKS_MIN) break; /* Which realm is that? */ realm = l_ptr->tval - TV_BOOKS_MIN + 1; /* Does this book belong to a realm that the borg knows */ if (!borg_has_realm(realm)) continue; /* Make a note where in the inv the book is */ borg_book[realm][k_info[l_ptr->k_idx].sval] = i; } } /*** Process books ***/ /* Hack -- Warriors never browse */ if (borg_class == CLASS_WARRIOR) borg_do_spell = FALSE; /* XXX XXX XXX Dark */ /* Cheat */ if (borg_do_spell) { /* Only do it once for each realm */ borg_do_spell = FALSE; /* Cheat that realm */ borg_cheat_spell(bp_ptr->realm1); borg_cheat_spell(bp_ptr->realm2); /* Done */ return (FALSE); } /* If king, maybe retire. */ if (bp_ptr->winner) { /* Prepare to retire */ if (borg_stop_king) { borg_oops("retire"); } } /*** Handle stores ***/ /* Hack -- Check for being in a store */ if (borg_term_text_comp(53, 19, "Gold Remaining") || borg_term_text_comp(40, 23, "Gold Remaining")) { /* Hack -- allow user abort */ if (borg_cancel) return (TRUE); /* Think until done */ return (borg_think_store()); } /*** Analyze the Frame ***/ /* Analyze the frame */ if (borg_do_frame) { /* Only once */ borg_do_frame = FALSE; /* Analyze the "frame" */ borg_update_frame(); } /*** Re-activate Tests ***/ /* Check frame again later */ borg_do_frame = TRUE; /* Check spells again later */ borg_do_spell = TRUE; /*** Think about it ***/ /* Increment the clock */ borg_t++; /* Examine the screen */ borg_update(); /* Extract some "hidden" variables */ borg_hidden(); /* Hack -- allow user abort */ if (borg_cancel) return (TRUE); /* Do something */ return (borg_think_dungeon()); } /* * Hack -- methods of hitting a monster (order not important). */ static cptr prefix_hit[] = { "You hit ", "You strike ", "You hack at ", "You bash ", "You slash ", "You pound ", "You score ", "You batter ", "You gouge ", "You bludgeon ", "You *smite* ", "You bite ", "You claw ", NULL }; /* * Hack -- methods of hurting a monster (order not important). * * See "message_pain()" for details. */ static cptr suffix_pain[] = { " is unharmed.", " barely notices.", " flinches.", " squelches.", " quivers in pain.", " writhes about.", " writhes in agony.", " jerks limply.", " roars thunderously.", " rumbles.", " grunts", " hesitates", " crumples", " hisses.", " rears up in anger.", " hisses furiously.", " roars.", " mewls in pain.", " mewls pitifully.", " chitters.", " scuttles about.", " twitters.", " twitches.", " chirps.", " squawks.", " chatters.", " jeers.", " flutters about.", " squeaks.", " snarls with pain.", " roars with pain.", " gasps.", " snarls feebly.", " rattles.", " stumbles.", " staggers.", " clatters.", " groans.", " moans.", " hesitates.", " grunts.", " wails.", " howls.", " moans softly.", " sighs.", " shrieks in pain.", " shrieks in agony.", " spawns!", " looks healthier.", " starts moving faster.", " starts moving slower.", " is unaffected!", " is immune.", " resists a lot.", " resists.", " resists somewhat.", " shrugs off the attack.", " snarls with pain.", " yelps in pain.", " howls in pain.", " howls in agony.", /* xxx */ " yelps feebly.", " ignores the attack.", " grunts with pain.", " squeals in pain.", " shrieks in pain.", " shrieks in agony.", /* xxx */ " cries out feebly.", /* xxx */ /* xxx */ " cries out in pain.", " screams in pain.", " screams in agony.", /* xxx */ " cringes from the light!", " loses some skin!", " is hit hard.", NULL }; /* * Hack -- methods of killing a monster (order not important). * * See "mon_take_hit()" for details. */ static cptr prefix_kill[] = { "You have killed ", "You have slain ", "You have destroyed ", NULL }; /* * Hack -- methods of monster death (order not important). * * See "project_m()", "do_cmd_fire()", "mon_take_hit()" for details. */ static cptr suffix_died[] = { " dies.", " dies from the Quivering Palm.", " is destroyed.", " is killed.", " dissolves!", " shrivels away in the light!", " collapses, a mindless husk.", NULL }; static cptr suffix_blink[] = { " disappears!", /* from teleport other */ " changes!", /* from polymorph spell */ " teleports away.", /* RF5_TPORT */ " blinks away.", /* RF5_BLINK */ NULL }; /* * Hack -- methods of hitting the player (order not important). * * The "insult", "moan", and "begs you for money" messages are ignored. * * See "make_attack_normal()" for details. */ static cptr suffix_hit_by[] = { " hits you.", " touches you.", " punches you.", " kicks you.", " claws you.", " bites you.", " stings you.", " butts you.", " crushes you.", " engulfs you.", " charges you.", " crawls on you.", " drools on you.", " spits on you.", " gazes at you.", " wails at you.", " releases spores at you.", NULL }; /* * Hack -- methods of casting spells at the player (order important). * * See "make_attack_spell()" for details. */ /* AJG These had gotten out of synch with where they are used. */ static cptr suffix_spell[] = { " makes a high pitched shriek.", /* 0 RF3_SHRIEK */ " tries to cast a spell, but fails.", /* 1 RF3_FAILS */ " does something.", /* 2 RF3_XXX3X4 */ " does something.", /* 3 RF3_XXX4X4 */ " fires an arrow.", /* 4 RF3_ARROW */ " fires an arrow!", /* 5 RF3_XXX6 */ " fires a missile.", /* 6 RF3_XXX7 */ " fires a missile!", /* 7 RF3_XXX8 */ " breathes acid.", /* 8 RF3_BR_ACID */ " breathes lightning.", /* 9 RF3_BR_ELEC */ " breathes fire.", /*10 RF3_BR_FIRE */ " breathes frost.", /*11 RF3_BR_COLD */ " breathes gas.", /*12 RF3_BR_POIS */ " breathes nether.", /*13 RF3_BR_NETH */ " breathes light.", /*14 RF3_BR_LITE */ " breathes darkness.", /*15 RF3_BR_DARK */ " breathes confusion.", /*16 RF3_BR_CONF */ " breathes sound.", /*17 RF3_BR_SOUN */ " breathes chaos.", /*18 RF3_BR_CHAO */ " breathes disenchantment.", /*19 RF3_BR_DISE */ " breathes nexus.", /*20 RF3_BR_NEXU */ " breathes time.", /*21 RF3_BR_TIME */ " breathes inertia.", /*22 RF3_BR_INER */ " breathes gravity.", /*23 RF3_BR_GRAV */ " breathes shards.", /*24 RF3_BR_SHAR */ " breathes plasma.", /*25 RF3_BR_PLAS */ " breathes force.", /*26 RF3_BR_WALL */ " does something.", /*27 RF3_BR_MANA */ " does something.", /*28 RF3_XXX5X4 */ " does something.", /*29 RF3_XXX6X4 */ " does something.", /*30 RF3_XXX7X4 */ " does something.", /*31 RF3_XXX8X4 */ " casts an acid ball.", /*32 RF4_BA_ACID */ " casts a lightning ball.", /*33 RF4_BA_ELEC */ " casts a fire ball.", /*34 RF4_BA_FIRE */ " casts a frost ball.", /*35 RF4_BA_COLD */ " casts a stinking cloud.", /*36 RF4_BA_POIS */ " casts a nether ball.", /*37 RF4_BA_NETH */ " gestures fluidly.", /*38 RF4_BA_WATE */ " invokes a mana storm.", /*39 RF4_BA_MANA */ " invokes a darkness storm.", /*40 RF4_BA_DARK */ " draws psychic energy from you!", /*41 RF4_DRAIN_MANA */ " gazes deep into your eyes.", /*42 RF4_MIND_BLAST */ " looks deep into your eyes.", /*43 RF4_BRAIN_SMASH */ " points at you and curses.", /*44 RF4_CAUSE_1 */ " points at you and curses horribly.", /*45 RF4_CAUSE_2 */ " points at you, incanting terribly!", /*46 RF4_CAUSE_3 */ " points at you, screaming the word DIE!", /*47 RF4_CAUSE_4 */ " casts a acid bolt.", /*48 RF4_BO_ACID */ " casts a lightning bolt.", /*49 RF4_BO_ELEC */ " casts a fire bolt.", /*50 RF4_BO_FIRE */ " casts a frost bolt.", /*51 RF4_BO_COLD */ " does something.", /*52 RF4_BO_POIS */ " casts a nether bolt.", /*53 RF4_BO_NETH */ " casts a water bolt.", /*54 RF4_BO_WATE */ " casts a mana bolt.", /*55 RF4_BO_MANA */ " casts a plasma bolt.", /*56 RF4_BO_PLAS */ " casts an ice bolt.", /*57 RF4_BO_ICEE */ " casts a magic missile.", /*58 RF4_MISSILE */ " casts a fearful illusion.", /*59 RF4_SCARE */ " casts a spell, burning your eyes!", /*60 RF4_BLIND */ " creates a mesmerising illusion.", /*61 RF4_CONF */ " drains power from your muscles!", /*62 RF4_SLOW */ " stares deep into your eyes!", /*63 RF4_HOLD */ " concentrates on XXX body.", /*64 RF5_HASTE */ " does something.", /*65 RF5_XXX1X6 */ " concentrates on XXX wounds.", /*66 RF5_HEAL */ " does something.", /*67 RF5_XXX2X6 */ " does something.", /*68 RF5_XXX3X6 */ " does something.", /*69 RF5_XXX4X6 */ " commands you to return.", /*70 RF5_TELE_TO */ " teleports you away.", /*71 RF5_TELE_AWAY */ " gestures at your feet.", /*72 RF5_TELE_LEVEL */ " does something.", /*73 RF5_XXX5 */ " gestures in shadow.", /*74 RF5_DARKNESS */ " casts a spell and cackles evilly.", /*75 RF5_TRAPS */ " tries to blank your mind.", /*76 RF5_FORGET */ " does something.", /*77 RF5_XXX6X6 */ " does something.", /*78 RF5_XXX7X6 */ " does something.", /*79 RF5_XXX8X6 */ " magically summons help!", /*80 RF5_S_MONSTER */ " magically summons monsters!", /*81 RF5_S_MONSTERS */ " magically summons ants.", /*82 RF5_S_ANT */ " magically summons spiders.", /*83 RF5_S_SPIDER */ " magically summons hounds.", /*84 RF5_S_HOUND */ " magically summons hydras.", /*85 RF5_S_HYDRA */ " magically summons an angel!", /*86 RF5_S_ANGEL */ " magically summons a demon from the Courts of Chaos!", /*87 RF5_S_DEMON */ " magically summons an undead adversary!", /*88 RF5_S_UNDEAD */ " magically summons a dragon!", /*89 RF5_S_DRAGON */ " magically summons greater undead!", /*90 RF5_S_HI_UNDEAD */ " magically summons ancient dragons!", /*91 RF5_S_HI_DRAGON */ " magically summons mighty undead opponents!", /*92 RF5_S_WRAITH */ " magically summons special opponents!", /*93 RF5_S_UNIQUE */ NULL }; #if 0 /* XXX XXX XXX */ msgf("%^s looks healthier.", m_name); msgf("%^s looks REALLY healthy!", m_name); #endif /* * Hack -- Spontaneous level feelings (order important). * * See "do_cmd_feeling()" for details. */ static cptr prefix_feeling[] = { "Looks like any other level", "You feel there is something special", "You have a superb feeling", "You have an excellent feeling", "You have a very good feeling", "You have a good feeling", "You feel strangely lucky", "You feel your luck is turning", "You like the look of this place", "This level can't be all bad", "What a boring place", NULL }; /* * Hack -- Parse a message from the world * * Note that detecting "death" is EXTREMELY important, to prevent * all sorts of errors arising from attempting to parse the "tomb" * screen, and to allow the user to "observe" the "cause" of death. * * Note that detecting "failure" is EXTREMELY important, to prevent * bizarre situations after failing to use a staff of perceptions, * which would otherwise go ahead and send the "item index" which * might be a legal command (such as "a" for "aim"). This method * is necessary because the Borg cannot parse "prompts", and must * assume the success of the prompt-inducing command, unless told * otherwise by a failure message. Also, we need to detect failure * because some commands, such as detection spells, need to induce * furthur processing if they succeed, but messages are only given * if the command fails. * * Note that certain other messages may contain useful information, * and so they are "analyzed" and sent to "borg_react()", which just * queues the messages for later analysis in the proper context. * * Along with the actual message, we send a special formatted buffer, * containing a leading "opcode", which may contain extra information, * such as the index of a spell, and an "argument" (for example, the * capitalized name of a monster), with a "colon" to separate them. * * XXX XXX XXX Several message strings take a "possessive" of the form * "his" or "her" or "its". These strings are all represented by the * encoded form "XXX" in the various match strings. Unfortunately, * the encode form is never decoded, so the Borg currently ignores * messages about several spells (heal self and haste self). * * XXX XXX XXX We notice a few "terrain feature" messages here so * we can acquire knowledge about wall types and door types. */ static void borg_parse_aux(cptr msg, int len) { int i, tmp; char who[256]; char buf[256]; /* Log (if needed) */ if (borg_fff) froff(borg_fff, "& Msg <%s>\n", msg); /* Hack -- Notice death */ if (prefix(msg, "You die.")) { /* Abort (unless cheating) */ if (!(p_ptr->state.wizard || cheat_live)) { /* Abort */ borg_oops("death"); /* Abort right now! */ borg_active = FALSE; /* Noise XXX XXX XXX */ Term_xtra(TERM_XTRA_NOISE, 1); } /* Done */ return; } /* Hack -- Notice "failure" */ if (prefix(msg, "You failed ")) { /* Hack -- store the keypress */ borg_note("# Normal failure."); /* Set the failure flag */ borg_failure = TRUE; /* Flush our key-buffer */ borg_flush(); /* If we were casting a targetted spell and failed */ /* it does not mean we can't target that location */ successful_target = BORG_TARGET; return; } /* Hack -- Notice "failure" */ if (prefix(msg, "Illegal ")) { /* Hack -- store the keypress */ borg_note("# Failure?"); /* Set the failure flag */ borg_failure = TRUE; /* Flush our key-buffer */ borg_flush(); /* Stop borg so we can debug the problem. */ borg_active = FALSE; return; } /* Ignore teleport trap */ if (prefix(msg, "You hit a teleport")) return; /* Ignore arrow traps */ if (prefix(msg, "An arrow ")) return; /* Ignore dart traps */ if (prefix(msg, "A small dart ")) return; if (prefix(msg, "The cave ")) { borg_react(msg, "QUAKE"); return; } /* need to check stat */ if (prefix(msg, "You feel very") || prefix(msg, "You feel less") || prefix(msg, "Wow! You feel very")) { /* need to check str */ if (prefix(msg, "You feel very weak")) { my_need_stat_check[0] = TRUE; } if (prefix(msg, "You feel less weak")) { my_need_stat_check[0] = TRUE; } if (prefix(msg, "Wow! You feel very strong")) { my_need_stat_check[0] = TRUE; } /* need to check int */ if (prefix(msg, "You feel very stupid")) { my_need_stat_check[1] = TRUE; } if (prefix(msg, "You feel less stupid")) { my_need_stat_check[1] = TRUE; } if (prefix(msg, "Wow! You feel very smart")) { my_need_stat_check[1] = TRUE; } /* need to check wis */ if (prefix(msg, "You feel very naive")) { my_need_stat_check[2] = TRUE; } if (prefix(msg, "You feel less naive")) { my_need_stat_check[2] = TRUE; } if (prefix(msg, "Wow! You feel very wise")) { my_need_stat_check[2] = TRUE; } /* need to check dex */ if (prefix(msg, "You feel very clumsy")) { my_need_stat_check[3] = TRUE; } if (prefix(msg, "You feel less clumsy")) { my_need_stat_check[3] = TRUE; } if (prefix(msg, "Wow! You feel very dextrous")) { my_need_stat_check[3] = TRUE; } /* need to check con */ if (prefix(msg, "You feel very sickly")) { my_need_stat_check[4] = TRUE; } if (prefix(msg, "You feel less sickly")) { my_need_stat_check[4] = TRUE; } if (prefix(msg, "Wow! You feel very healthy")) { my_need_stat_check[4] = TRUE; } /* need to check cha */ if (prefix(msg, "You feel very ugly")) { my_need_stat_check[5] = TRUE; } if (prefix(msg, "You feel less ugly")) { my_need_stat_check[5] = TRUE; } if (prefix(msg, "Wow! You feel very cute")) { my_need_stat_check[5] = TRUE; } } /* time attacks, just do all stats. */ if (prefix(msg, "You're not as")) { my_need_stat_check[0] = TRUE; my_need_stat_check[1] = TRUE; my_need_stat_check[2] = TRUE; my_need_stat_check[3] = TRUE; my_need_stat_check[4] = TRUE; my_need_stat_check[5] = TRUE; } /* Nexus attacks, need to check everything! */ if (prefix(msg, "Your body starts to scramble...")) { my_need_stat_check[0] = TRUE; my_need_stat_check[1] = TRUE; my_need_stat_check[2] = TRUE; my_need_stat_check[3] = TRUE; my_need_stat_check[4] = TRUE; my_need_stat_check[5] = TRUE; /* max stats may have lowered */ my_stat_max[0] = 0; my_stat_max[1] = 0; my_stat_max[2] = 0; my_stat_max[3] = 0; my_stat_max[4] = 0; my_stat_max[5] = 0; } if (streq(msg, "You have been knocked out.")) { borg_note("Ignoring Messages While KO'd"); borg_dont_react = TRUE; } if (streq(msg, "You are paralyzed")) { borg_note("Ignoring Messages While Paralyzed"); borg_dont_react = TRUE; } /* "You have hit it." (etc) */ for (i = 0; prefix_hit[i]; i++) { /* "You have hit it." (etc) */ if (prefix(msg, prefix_hit[i])) { tmp = strlen(prefix_hit[i]); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "HIT:%^s", who); borg_react(msg, buf); return; } } /* Miss somebody */ if (prefix(msg, "You miss ")) { tmp = strlen("You miss "); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "MISS:%^s", who); borg_react(msg, buf); return; } /* Miss somebody (because of fear) */ if (prefix(msg, "You are too afraid to attack ")) { tmp = strlen("You are too afraid to attack "); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "MISS:%^s", who); borg_react(msg, buf); return; } /* * An inventory item was unaffected by an attack. * It should be filtered out because otherwise the * item is interpreted as a monster and the game unhooks. * * This also filters out messages about pets that are unaffected. */ if (prefix(msg, "Your ") && suffix(msg, "is unaffected!")) return; /* "It screams in pain." (etc) */ for (i = 0; suffix_pain[i]; i++) { /* "It screams in pain." (etc) */ if (suffix(msg, suffix_pain[i])) { tmp = strlen(suffix_pain[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "PAIN:%^s", who); borg_react(msg, buf); return; } } /* "You have killed it." (etc) */ for (i = 0; prefix_kill[i]; i++) { /* "You have killed it." (etc) */ if (prefix(msg, prefix_kill[i])) { tmp = strlen(prefix_kill[i]); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "KILL:%^s", who); borg_react(msg, buf); return; } } /* "It dies." (etc) */ for (i = 0; suffix_died[i]; i++) { /* "It dies." (etc) */ if (suffix(msg, suffix_died[i])) { tmp = strlen(suffix_died[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "DIED:%^s", who); borg_react(msg, buf); return; } } /* "It blinks or telports." (etc) */ for (i = 0; suffix_blink[i]; i++) { /* "It teleports." (etc) */ if (suffix(msg, suffix_blink[i])) { tmp = strlen(suffix_blink[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "BLINK:%^s", who); borg_react(msg, buf); return; } } /* "It misses you." */ if (suffix(msg, " misses you.")) { tmp = strlen(" misses you."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "MISS_BY:%^s", who); borg_react(msg, buf); return; } /* "It is repelled.." */ /* treat as a miss */ if (suffix(msg, " is repelled.")) { tmp = strlen(" is repelled."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "MISS_BY:%^s", who); borg_react(msg, buf); return; } /* Ignore talking monsters */ if (strstr(msg, " says,")) return; /* Ignore monsters picking up objects*/ if (strstr(msg, " picks up ")) return; /* "It hits you." (etc) */ for (i = 0; suffix_hit_by[i]; i++) { /* "It hits you." (etc) */ if (suffix(msg, suffix_hit_by[i])) { tmp = strlen(suffix_hit_by[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "HIT_BY:%^s", who); borg_react(msg, buf); /* If I was hit, then I am not on a glyph */ if (track_glyph_num) { /* erase them all and * allow the borg to scan the screen and rebuild the array. * He won't see the one under him though. So a special check * must be made. */ /* byte feat = mb_ptr->feat; */ /* Remove the entire array */ for (i = 0; i < track_glyph_num; i++) { /* Stop if we already new about this glyph */ track_glyph_x[i] = 0; track_glyph_y[i] = 0; } track_glyph_num = 0; } return; } } /* "It casts a spell." (etc) */ for (i = 0; suffix_spell[i]; i++) { /* "It casts a spell." (etc) */ if (suffix(msg, suffix_spell[i])) { tmp = strlen(suffix_spell[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "SPELL_%03d:%^s", i, who); borg_react(msg, buf); return; } } #if 0 /* State -- Paralyzed */ if (suffix(msg, " is paralyzed!")) { tmp = strlen(" is paralyzed!"); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_INVLUN:%^s", who); borg_react(msg, buf); return; } #endif /* State -- Asleep */ if (suffix(msg, " falls asleep!")) { tmp = strlen(" falls asleep!"); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_SLEEP:%^s", who); borg_react(msg, buf); return; } /* State -- confused */ if (suffix(msg, " looks confused.")) { tmp = strlen(" looks confused."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_CONFUSED:%^s", who); borg_react(msg, buf); return; } /* State -- confused */ if (suffix(msg, " looks more confused.")) { tmp = strlen(" looks more confused."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_CONFUSED:%^s", who); borg_react(msg, buf); return; } /* State -- Not Asleep */ if (suffix(msg, " wakes up.")) { tmp = strlen(" wakes up."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_AWAKE:%^s", who); borg_react(msg, buf); return; } /* State -- Afraid */ if (suffix(msg, " flees in terror!")) { tmp = strlen(" flees in terror!"); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__FEAR:%^s", who); borg_react(msg, buf); return; } /* State -- Not Afraid */ if (suffix(msg, " recovers his courage.")) { tmp = strlen(" recovers his courage."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__BOLD:%^s", who); borg_react(msg, buf); return; } /* State -- Not Afraid */ if (suffix(msg, " recovers her courage.")) { tmp = strlen(" recovers her courage."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__BOLD:%^s", who); borg_react(msg, buf); return; } /* State -- Not Afraid */ if (suffix(msg, " recovers its courage.")) { tmp = strlen(" recovers its courage."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__BOLD:%^s", who); borg_react(msg, buf); return; } /* Feature XXX XXX XXX */ if (streq(msg, "The door appears to be broken.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Feature XXX XXX XXX */ if (streq(msg, "The door appears to be stuck.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Feature XXX XXX XXX */ if (streq(msg, "This seems to be permanent rock.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Feature XXX XXX XXX */ if (streq(msg, "You tunnel into the granite wall.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Feature Invisible Walls */ if (streq(msg, "You bump into something.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Feature XXX XXX XXX */ if (streq(msg, "You tunnel into the quartz vein.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Feature XXX XXX XXX */ if (streq(msg, "You tunnel into the magma vein.")) { /* Clear goals */ goal = GOAL_NONE; return; } /* Word of Recall -- Ignition */ if (prefix(msg, "The air about you becomes ")) { /* Keep track of this dungeon */ borg_dungeon_remember(FALSE); /* Initiate recall */ /* Guess how long it will take to lift off */ goal_recalling = 15000 + 5000; /* Guess. game turns x 1000 ( 15+rand(20)) */ return; } /* Word of Recall -- Lift off */ if (prefix(msg, "You feel yourself yanked ")) { /* Keep track of this dungeon */ borg_dungeon_remember(FALSE); /* Recall complete */ goal_recalling = 0; return; } /* Word of Recall -- Cancelled */ if (prefix(msg, "A tension leaves ")) { /* Hack -- Oops */ goal_recalling = 0; return; } /* Take the stairs up */ if (streq(msg, "You enter a maze of up staircases.")) { /* Keep track of this dungeon */ borg_dungeon_remember(TRUE); return; } /* Take the stairs down */ if (streq(msg, "You enter a maze of down staircases.")) { /* Keep track of this dungeon */ borg_dungeon_remember(FALSE); return; } /* Level teleport up */ if (streq(msg, "You rise up through the ceiling.")) { /* Keep track of this dungeon */ borg_dungeon_remember(TRUE); return; } /* Level teleport down */ if (streq(msg, "You sink through the floor.")) { /* Keep track of this dungeon */ borg_dungeon_remember(FALSE); return; } /* When the borg eats */ if (streq(msg, "You are full!")) { /* Keep track of this */ bp_ptr->status.full = TRUE; bp_ptr->status.weak = FALSE; bp_ptr->status.hungry = FALSE; bp_ptr->status.gorged = FALSE; return; } /* When the borg doesn't eat */ if (streq(msg, "You are no longer full.")) { /* Keep track of this */ bp_ptr->status.full = FALSE; bp_ptr->status.weak = FALSE; bp_ptr->status.hungry = FALSE; bp_ptr->status.gorged = FALSE; return; } /* When the borg doesn't eat */ if (streq(msg, "You are getting hungry.")) { /* Keep track of this */ bp_ptr->status.full = FALSE; bp_ptr->status.weak = FALSE; bp_ptr->status.hungry = TRUE; bp_ptr->status.gorged = FALSE; return; } /* When the borg doesn't eat */ if (streq(msg, "You are getting weak from hunger!") || streq(msg, "You are getting faint from hunger!") || streq(msg, "You faint from the lack of food.")) { /* Keep track of this */ bp_ptr->status.full = FALSE; bp_ptr->status.weak = TRUE; bp_ptr->status.hungry = FALSE; bp_ptr->status.gorged = FALSE; return; } /* When the borg eats too much */ if (streq(msg, "You have gorged yourself!")) { /* Keep track of this */ bp_ptr->status.full = FALSE; bp_ptr->status.weak = FALSE; bp_ptr->status.hungry = FALSE; bp_ptr->status.gorged = TRUE; return; } /* Wearing Cursed Item */ if ((prefix(msg, "There is a malignant black aura surrounding you...")) || (prefix(msg, "Oops! It feels deathly cold!")) || (suffix(msg, " appears to be cursed.")) || (suffix(msg, " seems to be cursed."))) { /* Hack -- Oops */ bp_ptr->status.cursed = TRUE; return; } /* protect from evil */ if (prefix(msg, "You feel safe from evil!")) { borg_prot_from_evil = TRUE; return; } if (prefix(msg, "You no longer feel safe from evil.")) { borg_prot_from_evil = FALSE; return; } /* haste self */ if (prefix(msg, "You feel yourself moving faster!")) { borg_speed = TRUE; return; } if (prefix(msg, "You feel yourself slow down.")) { borg_speed = FALSE; return; } /* Bless */ if (prefix(msg, "You feel righteous!")) { borg_bless = TRUE; return; } if (prefix(msg, "The prayer has expired.")) { borg_bless = FALSE; return; } /* hero */ if (prefix(msg, "You feel like a hero!")) { borg_hero = TRUE; return; } if (prefix(msg, "The heroism wears off.")) { borg_hero = FALSE; return; } /* berserk */ if (prefix(msg, "You feel like a killing machine!")) { borg_berserk = TRUE; return; } if (prefix(msg, "You feel less Berserk.")) { borg_berserk = FALSE; return; } /* stone skin */ if (prefix(msg, "Your skin turns to stone")) { borg_shield = TRUE; return; } if (prefix(msg, "Your skin returns to normal.")) { borg_shield = FALSE; return; } /* check for wall blocking but not when confused */ if ((prefix(msg, "There is a wall ") && !bp_ptr->status.confused)) { my_need_alter = TRUE; goal = GOAL_NONE; return; } /* check for jungle blocking but not when confused */ if ((prefix(msg, "The jungle is impassable.") && !bp_ptr->status.confused)) { my_need_alter = TRUE; /* pick a new flow */ borg_flow_goal_wild(); return; } /* check for closed door but not when confused */ if ((prefix(msg, "There is a closed door blocking your way.") && !bp_ptr->status.confused)) { my_need_alter = TRUE; goal = GOAL_NONE; return; } /* If the borg opens an open door */ if (prefix(msg, "You see nothing there to open") && !bp_ptr->status.confused) { borg_open_door_failed = TRUE; return; } /* If the borg closes an closed door */ if (prefix(msg, "You see nothing there to close") && !bp_ptr->status.confused) { borg_close_door_failed = TRUE; return; } /* check for mis-alter command. Sometime induced by never_move guys */ if (streq(msg, "You spin around.") && !bp_ptr->status.confused) { /* Examine all the monsters */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Distance components */ if ((c_y == kill->y && c_x == kill->x) || (g_y == kill->y && g_x == kill->x)) { /* Hack -- kill em */ borg_delete_kill(i, "oops, spinning"); } } my_no_alter = TRUE; goal = GOAL_NONE; return; } /* Feature XXX XXX XXX */ if (prefix(msg, "You see nothing there ")) { my_no_alter = TRUE; /* Clear goals */ goal = GOAL_NONE; return; } /* Tunneling not understood correctly */ if (prefix(msg, "You cannot tunnel through air.")) { my_no_alter = TRUE; /* Clear goals */ goal = GOAL_NONE; return; } /* Hack to protect against clock overflows and errors */ if (prefix(msg, "Illegal ")) { borg_oops("# Borg problem msg: %s", msg); /* Hack -- Oops */ borg_keypress(ESCAPE); borg_keypress(ESCAPE); return; } /* resist acid */ if (prefix(msg, "You feel resistant to acid!")) { my_oppose_acid = TRUE; return; } if (prefix(msg, "You feel less resistant to acid.")) { my_oppose_acid = FALSE; return; } /* resist electricity */ if (prefix(msg, "You feel resistant to electricity!")) { my_oppose_elec = TRUE; return; } if (prefix(msg, "You feel less resistant to electricity.")) { my_oppose_elec = FALSE; return; } /* resist fire */ if (prefix(msg, "You feel resistant to fire!")) { my_oppose_fire = TRUE; return; } if (prefix(msg, "You feel less resistant to fire.")) { my_oppose_fire = FALSE; return; } /* resist cold */ if (prefix(msg, "You feel resistant to cold!")) { my_oppose_cold = TRUE; return; } if (prefix(msg, "You feel less resistant to cold.")) { my_oppose_cold = FALSE; return; } /* resist poison */ if (prefix(msg, "You feel resistant to poison!")) { my_oppose_pois = TRUE; return; } if (prefix(msg, "You feel less resistant to poison.")) { my_oppose_pois = FALSE; return; } /* GOI! */ if (prefix(msg, "You feel invulnerable!")) { borg_goi = 12000; /* keep track of how long it has left (a guess) */ return; } if (prefix(msg, "You feel vulnerable once more.")) { borg_goi = 0; return; } /* Wraith_form! */ if (prefix(msg, "You leave the physical world and turn into a wraith-being!")) { borg_wraith_form = TRUE; return; } if (prefix(msg, "You feel opaque.")) { borg_wraith_form = FALSE; return; } /* Telepathy */ if (prefix(msg, "You feel your consciousness expand!")) { borg_esp = TRUE; return; } if (prefix(msg, "Your consciousness contracts again.")) { borg_esp = FALSE; return; } /* Invisible */ /* Shield */ if (prefix(msg, "A mystic shield forms around your body!")) { borg_shield = TRUE; return; } if (prefix(msg, "Your mystic shield crumbles away.")) { borg_shield = FALSE; return; } /* Glyph of Warding (the spell no longer gives a report) */ /* Sadly Rune of Protection has no message */ if (prefix(msg, "You inscribe a mystic symbol on the ground!")) { /* Check for an existing glyph */ for (i = 0; i < track_glyph_num; i++) { /* Stop if we already new about this glyph */ if ((track_glyph_x[i] == c_x) && (track_glyph_y[i] == c_y)) break; } /* Track the newly discovered glyph */ if ((i == track_glyph_num) && (track_glyph_size)) { borg_note("# Noting the creation of a glyph."); track_glyph_x[i] = c_x; track_glyph_y[i] = c_y; track_glyph_num++; } return; } if (prefix(msg, "The rune of protection is broken!")) { /* we won't know which is broken so erase them all and * allow the borg to scan the screen and rebuild the array. * He won't see the one under him though. So a special check * must be made. */ /* byte feat = mb_ptr->feat; */ /* Remove the entire array */ for (i = 0; i < track_glyph_num; i++) { /* Stop if we already new about this glyph */ track_glyph_x[i] = 0; track_glyph_y[i] = 0; track_glyph_num = 0; } /* no known glyphs */ track_glyph_num = 0; } /* failed glyph spell message */ if (prefix(msg, "The object resists the spell")) { /* Forget the newly created-though-failed glyph */ track_glyph_x[track_glyph_num] = 0; track_glyph_y[track_glyph_num] = 0; track_glyph_num--; return; } /* Removed rubble. */ if (prefix(msg, "You have removed the ")) { return; } /* need to kill monsters when WoD is used */ if (prefix(msg, "There is a searing blast of light!")) { /* Examine all the monsters */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; int x9 = kill->x; int y9 = kill->y; int ax, ay, d; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Distance components */ ax = (x9 > c_x) ? (x9 - c_x) : (c_x - x9); ay = (y9 > c_y) ? (y9 - c_y) : (c_y - y9); /* Distance */ d = MAX(ax, ay); /* Minimal distance */ if (d > 12) continue; /* Hack -- kill em */ borg_delete_kill(i, "*destruction*"); } return; } /* Recognize Drowning */ if (prefix(msg, "You are drowning")) { /* Clear goals */ goal = GOAL_NONE; borg_note("# Help! I can't swim"); return; } /* Recognize Burning */ if (prefix(msg, "The lava burns you!") || prefix(msg, "The heat burns you!")) { /* Clear goals */ goal = GOAL_NONE; borg_note("# Help! I'm burning"); return; } /* Recognize Acid */ if (prefix(msg, "The acid burns you!") || prefix(msg, "The fumes burn you!")) { /* Clear goals */ goal = GOAL_NONE; borg_note("# Help! I'm corroding"); return; } /* Recognize Poisoning */ if (prefix(msg, "The plants poison you!") || prefix(msg, "The fumes poison you!")) { borg_note("# Help! I'm poisoned"); /* Clear goals */ goal = GOAL_NONE; return; } /* Feelings about the level */ for (i = 0; prefix_feeling[i]; i++) { /* "You feel..." (etc) */ if (prefix(msg, prefix_feeling[i])) { strnfmt(buf, 256, "FEELING:%d", i); borg_react(msg, buf); return; } } } /* * Parse a message, piece of a message, or set of messages. * * We must handle long messages which are "split" into multiple * pieces, and also multiple messages which may be "combined" * into a single set of messages. */ static void borg_parse(cptr msg) { static char len = 0; static char buf[1024]; /* Flush messages */ if (len && (!msg || (msg[0] != ' '))) { int i, j; /* Split out punctuation */ for (j = i = 0; i < len - 1; i++) { /* Check for punctuation */ if ((buf[i] == '.') || (buf[i] == '!') || (buf[i] == '?') || (buf[i] == '"')) { /* Require space */ if (buf[i + 1] == ' ') { /* Terminate */ buf[i + 1] = '\0'; /* Parse fragment */ borg_parse_aux(buf + j, (i + 1) - j); /* Restore */ buf[i + 1] = ' '; /* Advance past spaces */ for (j = i + 2; buf[j] == ' '; j++) /* loop */ ; } } } /* Parse tail */ borg_parse_aux(buf + j, len - j); /* Forget */ len = 0; } /* No message */ if (!msg) { /* Start over */ len = 0; } /* Continued message */ else if (msg[0] == ' ') { /* Collect, verify, and grow */ len += strnfmt(buf + len, 1024 - len, "%s", msg + 1); } /* New message */ else { /* Collect, verify, and grow */ len = strnfmt(buf, 1024, "%s", msg); } } /* * Initialize zborg.txt */ static void borg_log_death(void) { char buf[1024]; FILE *borg_log_file; time_t death_time; path_make(buf, ANGBAND_DIR_USER, "borg-log.txt"); /* Append to the file */ borg_log_file = my_fopen(buf, "a"); /* Failure */ if (!borg_log_file) return; /* Get time of death */ (void)time(&death_time); /* Save the date */ strftime(buf, 80, "%Y/%m/%d %H:%M\n", localtime(&death_time)); froff(borg_log_file, buf); froff(borg_log_file, "%s the %s %s, Level %d/%d\n", player_name, race_info[p_ptr->rp.prace].title, class_info[p_ptr->rp.pclass].title, p_ptr->lev, p_ptr->max_lev); froff(borg_log_file, "Exp: %lu Gold: %lu Turn: %lu\n", (long) /* total_points() */ 0, (long)p_ptr->au, (long)turn); froff(borg_log_file, "Killed on level: %d (max. %d) by %s\n", p_ptr->depth, max_dun_level_reached(), p_ptr->state.died_from); froff(borg_log_file, "----------\n\n"); my_fclose(borg_log_file); } static void borg_log_death_data(void) { char buf[1024]; FILE *borg_log_file; time_t death_time; path_make(buf, ANGBAND_DIR_USER, "zborg.dat"); /* Append to the file */ borg_log_file = my_fopen(buf, "a"); /* Failure */ if (!borg_log_file) return; /* Get time of death */ (void)time(&death_time); /* dump stuff for easy import to database */ froff(borg_log_file, "%s, %s, %d, %d, %s\n", race_info[p_ptr->rp.prace].title, class_info[p_ptr->rp.pclass].title, p_ptr->lev, p_ptr->depth, p_ptr->state.died_from); my_fclose(borg_log_file); } /* * The borg cannot handle all the values of all the options. This is the list * of options that have to be set a certain way. When the borg is started * they get the right value and when the borg stops running the original value * is copied back */ static bool borg_rogue_like_commands; static bool borg_carry_query_flag; static bool borg_use_old_target; static bool borg_always_pickup; static bool borg_easy_open; static bool borg_easy_disarm; static bool borg_easy_floor; static bool borg_auto_more; static bool borg_emergency_stop; static bool borg_disturb_other; static bool borg_confirm_wear; static bool borg_check_transaction; /* Turn on the right options */ static void borg_set_options(void) { /* We use the original keypress codes */ borg_rogue_like_commands = rogue_like_commands; rogue_like_commands = FALSE; /* We must pick items up without verification */ borg_carry_query_flag = carry_query_flag; carry_query_flag = FALSE; /* We specify targets before casting the spells */ borg_use_old_target = use_old_target; use_old_target = TRUE; /* We pick up items when we step on them */ borg_always_pickup = always_pickup; always_pickup = TRUE; /* The borg adds the direction so these should be off */ borg_easy_open = easy_open; borg_easy_disarm = easy_disarm; easy_open = FALSE; easy_disarm = FALSE; /* The borg doesn't understand the floor list */ borg_easy_floor = easy_floor; easy_floor = FALSE; /* Prevent the teleport [y/n] question */ borg_disturb_other = disturb_other; disturb_other = FALSE; /* The borg can get off-sequence with this */ borg_auto_more = auto_more; borg_emergency_stop = emergency_stop; auto_more = FALSE; emergency_stop = FALSE; /* Keep track of these options values */ borg_confirm_wear = confirm_wear; confirm_wear = FALSE; borg_check_transaction = check_transaction; check_transaction = FALSE; /* set the continous play mode if the game cheat death is on */ if (cheat_live) borg_cheat_death = TRUE; } /* Set the options back to what they were */ static void borg_reset_options(void) { /* We use the original keypress codes */ rogue_like_commands = borg_rogue_like_commands; /* We must pick items up without verification */ carry_query_flag = borg_carry_query_flag; /* We specify targets before casting the spells */ use_old_target = borg_use_old_target; /* We pick up items when we step on them */ always_pickup = borg_always_pickup; /* The borg adds the direction so these should be off */ easy_open = borg_easy_open; easy_disarm = borg_easy_disarm; /* The borg doesn't understand the floor list */ easy_floor = borg_easy_floor; /* The borg can get off-sequence with this */ auto_more = borg_auto_more; emergency_stop = borg_emergency_stop; /* Prevent some [y/n] questions */ disturb_other = borg_disturb_other; confirm_wear = borg_confirm_wear; check_transaction = borg_check_transaction; } /* * Check the overhead map for info about towns, dungeons and quests. * One big hack. Maybe maid-grf.c should provide some sort of borg_wild_map, * like it provides the inventory and the equipment. */ static void borg_read_map(void) { int i, j, x, y; int min_depth, max_depth; place_type *pl_ptr; wild_done_type *w_ptr; for (i = 0; i < max_wild - 1; i++) { for (j = 0; j < max_wild - 1; j++) { w_ptr = &wild[j][i].done; /* Do we have a place here? */ pl_ptr = (w_ptr->place ? &place[w_ptr->place] : NULL); /* Skip non-places */ if (!pl_ptr) continue; /* Skip unknown spots */ if (!(w_ptr->info & WILD_INFO_SEEN)) continue; /* Create coords */ x = i * WILD_BLOCK_SIZE; y = j * WILD_BLOCK_SIZE; /* If this is a town add it to the list */ if (pl_ptr->numstores) borg_add_town(x , y, pl_ptr->name); /* Is this a dungeon */ if (pl_ptr->dungeon) { dun_type *d_ptr = pl_ptr->dungeon; bool bottom = FALSE; if (d_ptr->recall_depth != 0) { min_depth = d_ptr->min_level; max_depth = d_ptr->recall_depth; if (d_ptr->max_level == d_ptr->recall_depth) bottom = TRUE; } else { /* A town dungeon always starts at level 1 */ if (pl_ptr->numstores) min_depth = 1; /* In the wilderness the starting depth can vary */ else { /* Determine dungeon level */ min_depth = (d_ptr->min_level + 9) / 10; /* You never know */ if (min_depth > 9) min_depth = 9; /* Create a possible min level for this dungeon */ min_depth = min_depth * 10 - 1; } max_depth = min_depth; } /* Add dungeon */ borg_add_dungeon(x, y, min_depth, max_depth, bottom); } } } } /* * Mega-Hack -- special "inkey_hack" hook. XXX XXX XXX * * A special function hook (see "util.c") which allows the Borg to take * control of the "inkey()" function, and substitute in fake keypresses. */ extern char (*inkey_hack) (int flush_first); /* * This function lets the Borg "steal" control from the user. * * The "util.c" file provides a special "inkey_hack" hook which we use * to steal control of the keyboard, using the special function below. * * Since this function bypasses the code in "inkey()" which "refreshes" * the screen whenever the game has to wait for a keypress, the screen * will only get refreshed when (1) an option such as "fresh_before" * induces regular screen refreshing or (2) various explicit calls to * "Term_fresh" are made, such as in the "project()" function. This * has the interesting side effect that the screen is never refreshed * while the Borg is browsing stores, checking his inventory/equipment, * browsing spell books, checking the current panel, or examining an * object, which reduces the "screen flicker" considerably. :-) * * The only way that the Borg can be stopped once it is started, unless * it dies or encounters an error, is to press any key. This function * checks for real user input on a regular basic, and if any is found, * it is flushed, and after completing any actions in progress, this * function hook is removed, and control is returned to the user. * * We handle "broken" messages, in which long messages are "broken" into * pieces, and all but the first message are "indented" by one space, by * collecting all the pieces into a complete message and then parsing the * message once it is known to be complete. * * This function hook automatically removes itself when it realizes that * it should no longer be active. Note that this may take place after * the game has asked for the next keypress, but the various "keypress" * routines should be able to handle this. */ static char borg_inkey_hack(int flush_first) { char ch; int y = 0; int x = 80; byte t_a; char buf[128]; bool borg_prompt; /* ajg For now we can just use this locally. in the 283 borg he uses this to optimize knowing if we are waiting at a prompt for info */ bool borg_rand_quick; /* Save system setting */ u32b borg_rand_value; /* Save system setting */ /* Refresh the screen */ Term_fresh(); /* Deactivate */ if (!borg_active) { /* Message */ borg_note("# Removing keypress hook"); /* Set the options back to what they were */ borg_reset_options(); /* Remove hook */ inkey_hack = NULL; /* Flush keys */ borg_flush(); /* Flush */ flush(); /* Done */ return (0); } /* Mega-Hack -- flush keys */ if (flush_first) { /* Only flush if needed */ if (borg_inkey(FALSE) != 0) { /* Message */ borg_note("# Flushing keypress buffer"); /* Flush keys */ borg_flush(); } } /* Mega-Hack -- Handle death */ if (p_ptr->state.is_dead) { /* Flush messages */ borg_parse(NULL); /* Log death, if it is wanted */ if (borg_flag_dump) { borg_log_death(); borg_log_death_data(); } /* flush the buffer */ borg_flush(); if (borg_cheat_death) { /* Ignore death, and just print a message */ borg_note("Player died, continuing with borg_cheat_death"); /* If the borg was recalling before he died he isn't now */ goal_recalling = 0; /* Cheat death */ return ('n'); } else { /* Oops */ borg_oops("player died"); } /* Useless keypress */ return (KTRL('C')); } /* Set the borg statuses and values correct */ borg_update_frame(); /* Assume no prompt/message is available */ borg_prompt = FALSE; /* * Mega-Hack -- check for possible prompts/messages * If the first four characters on the message line all * have the same attribute (or are all spaces), and they * are not all spaces (ascii value 0x20)... */ if ((0 == borg_what_text(0, 0, 4, &t_a, buf)) && (t_a != TERM_DARK) && (*((u32b *)(buf)) != 0x20202020)) { /* Assume a prompt/message is available */ borg_prompt = TRUE; } /* Locate the cursor */ (void)Term_locate(&x, &y); /* * Mega-Hack -- Catch "-more-" messages * If there is text on the first line... * And the game does not want a command... * And the cursor is on the top line... * And there is text before the cursor... * And that text is "-more-" */ if (borg_prompt && !p_ptr->cmd.inkey_flag && y == 0 && x >= 7 && borg_term_text_comp(x - 7, y, " -more-")) { /* Get the message */ if (0 == borg_what_text(0, 0, x - 7, &t_a, buf)) { /* Parse it */ borg_parse(buf); } /* Clear the message */ return (KTRL('M')); } /* * Mega-Hack -- catch normal messages * If there is text on the first line... * And the game wants a command */ if (borg_prompt && p_ptr->cmd.inkey_flag) { /* Get the message(s) */ if (0 == borg_what_text(0, 0, -80, &t_a, buf)) { int k = strlen(buf); /* Strip trailing spaces */ while ((k > 0) && (buf[k - 1] == ' ')) k--; /* Terminate */ buf[k] = '\0'; /* Parse it */ borg_parse(buf); } /* Clear the message */ return (KTRL('M')); } /* Flush messages */ borg_parse(NULL); borg_dont_react = FALSE; /* Check for key */ ch = borg_inkey(TRUE); /* Use the key */ if (ch) return (ch); /* Hack - check to see if we are doing a repeated action */ if (p_ptr->state.running || p_ptr->cmd.rep || p_ptr->state.resting) { return (0); } /* Check for user abort */ (void)Term_inkey(&ch, FALSE, FALSE); /* User Abort */ if (ch != 0) { /* Oops */ borg_oops("user abort"); /* Hack -- Escape */ return (ESCAPE); } /* Save the system random info */ borg_rand_quick = Rand_quick; borg_rand_value = Rand_value; /* Use the local random info */ Rand_quick = TRUE; Rand_value = borg_rand_local; /* Think */ while (!borg_think()) /* loop */ ; /* DVE- Update the status screen */ borg_status_window(); /* Save the local random info */ borg_rand_local = Rand_value; /* Restore the system random info */ Rand_quick = borg_rand_quick; Rand_value = borg_rand_value; /* Hack -- allow stepping to induce a clean cancel */ if (borg_step && (!--borg_step)) borg_cancel = TRUE; /* Check for key */ ch = borg_inkey(TRUE); /* Use the key */ if (ch) return (ch); /* Oops */ borg_oops("normal abort - out of keys???"); /* Hack -- Escape */ return (ESCAPE); } /* This will display the values which the borg believes an item has. */ static void borg_display_item(list_item *l_ptr) { int j = 13; /* Clear the screen */ clear_region(13 - 2, 1, 30); /* Describe fully */ if (l_ptr->o_name) prtf(j, 2, "%s", l_ptr->o_name); prtf(j, 4, "k_idx = %-5d tval = %-5d ", l_ptr->k_idx, l_ptr->tval); prtf(j, 5, "number = %-3d wgt = %-6d ac = %-5d damage = %dd%d", l_ptr->number, l_ptr->weight, l_ptr->ac, l_ptr->dd, l_ptr->ds); prtf(j, 6, "pval = %-5d toac = %-5d tohit = %-4d todam = %-4d", l_ptr->pval, l_ptr->to_a, l_ptr->to_h, l_ptr->to_d); if (l_ptr->xtra_name) { prtf(j, 7, "xtra_name = %s", l_ptr->xtra_name); } prtf(j, 8, "info = %d timeout = %-d", l_ptr->info, l_ptr->timeout); prtf(j, 10, "+------------FLAGS1------------+\n" "AFFECT........SLAY........BRAND.\n" " cvae xsqpaefc\n" "siwdcc ssidsahanvudotgddhuoclio\n" "tnieoh trnipttmiinmrrnrrraiierl\n" "rtsxna..lcfgdkcpmldncltggpksdced\n" "%v", binary_fmt, l_ptr->kn_flags[0]); prtf(j, 17, "+------------FLAGS2------------+\n" "SUST...IMMUN..RESIST............\n" " aefctrpsaefcpfldbc sn \n" "siwdcc clioheatcliooeialoshtncd\n" "tnieoh ierlrfraierliatrnnnrhehi\n" "rtsxna..dcedwlatdcedsrekdfddrxss\n" "%v", binary_fmt, l_ptr->kn_flags[1]); prtf(j + 32, 10, "+------------FLAGS3------------+\n" "SH NO tehsif itdrmsIGNRadtabchp\n" "fe tm yzdhnelneieihaefccrpgluvm\n" "il ea cktmativlgggocliotnorercc\n" "re lg rnyorhtiesehtierlvxrvssrr\n" "ec ec swpdtresptntsdcedtpttsess\n" "%v", binary_fmt, l_ptr->kn_flags[2]); prtf(j + 32, 17, "+------------FLAGS4------------+\n" " IMSH p pt reHURT.. CURS\n" " ldac alao txaefcld as h\n" " iacomtusupupclioia utee\n" " trilurcscsrlierltr taaa\n" " ekddtnkwhinodcedek ottl\n" "%v", binary_fmt, l_ptr->kn_flags[3]); } /* Get all sorts of temporary values from the game. */ static void borg_cheat_temp_bools(void) { /* Allowable Cheat -- Obtain "recall" flag */ goal_recalling = p_ptr->tim.word_recall * 1000; /* Allowable Cheat -- Obtain "prot_from_evil" flag */ borg_prot_from_evil = (p_ptr->tim.protevil ? TRUE : FALSE); /* Allowable Cheat -- Obtain "speed" flag */ borg_speed = (p_ptr->tim.fast ? TRUE : FALSE); /* Allowable Cheat -- Obtain "goi" flag */ borg_goi = (p_ptr->tim.invuln ? 9000 : 0); /* Allowable Cheat -- Obtain "wraithform" flag */ borg_wraith_form = (p_ptr->tim.wraith_form ? 9000 : 0); /* Allowable Cheat -- Obtain "invisibility" flag */ borg_inviso = (p_ptr->tim.invis ? 9000 : 0); /* Allowable Cheat -- Obtain "resist" flags */ my_oppose_acid = (p_ptr->tim.oppose_acid ? TRUE : FALSE); my_oppose_elec = (p_ptr->tim.oppose_elec ? TRUE : FALSE); my_oppose_fire = (p_ptr->tim.oppose_fire ? TRUE : FALSE); my_oppose_cold = (p_ptr->tim.oppose_cold ? TRUE : FALSE); my_oppose_pois = (p_ptr->tim.oppose_pois ? TRUE : FALSE); borg_bless = (p_ptr->tim.blessed ? TRUE : FALSE); borg_shield = (p_ptr->tim.shield ? TRUE : FALSE); borg_hero = (p_ptr->tim.hero ? TRUE : FALSE); borg_berserk = (p_ptr->tim.shero ? TRUE : FALSE); borg_esp = (p_ptr->tim.esp ? TRUE : FALSE); } /* Initialize the Borg */ void borg_init_9(void) { /*** Hack -- initialize borg.ini options ***/ /* Message */ prtf(0, 0, "Initializing the Borg."); /* Hack -- flush it */ Term_fresh(); /* Initialise player position */ map_get_player(&c_x, &c_y); /* Initialize */ borg_init_1(); borg_init_2(); borg_init_3(); borg_init_4(); borg_init_5(); borg_init_6(); borg_init_7(); borg_init_8(); /* Hack - initialise the hooks into the overhead map code */ /* Save the borg hooks into the overhead map */ set_callback((callback_type) borg_map_info, CALL_MAP_INFO, NULL); set_callback((callback_type) borg_map_erase, CALL_MAP_ERASE, NULL); /* Save old player movement hook */ set_callback((callback_type) borg_player_move, CALL_PLAYER_MOVE, NULL); /* Save the borg hooks for object lists */ set_callback((callback_type) borg_list_info, CALL_OBJECT_LIST, NULL); /*** Redraw ***/ /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); /* Redraw everything */ do_cmd_redraw(); /*** Hack -- Extract race ***/ /* Insert the player Race--cheat */ borg_race = p_ptr->rp.prace; /* Extract the race pointer */ rb_ptr = &race_info[borg_race]; /*** Hack -- Extract class ***/ borg_class = p_ptr->rp.pclass; /* Extract the class pointer */ cb_ptr = &class_info[borg_class]; /* Extract the magic pointer */ pmb_ptr = &magic_info[borg_class]; /*** Hack -- react to race and class ***/ /* Notice the new race and class */ prepare_race_class_info(); /*** All done ***/ /* Clear line */ clear_msg(); /* Reset the clock */ borg_t = 1000; /* Official message */ borg_note("# Ready..."); /* Now it is ready */ initialized = TRUE; } static void borg_display_map_info(byte data, byte type) { int x, y; char c = ' '; byte a = TERM_DARK; map_block *mb_ptr; for (x = p_ptr->panel_x1; x < p_ptr->panel_x2; x++) { for (y = p_ptr->panel_y1; y < p_ptr->panel_y2; y++) { a = TERM_DARK; c = ' '; if (map_in_bounds(x, y)) { mb_ptr = map_loc(x, y); switch (type) { case BORG_SHOW_FEAT: { if (mb_ptr->feat == data) { a = TERM_WHITE; c = '*'; } break; } case BORG_SHOW_INFO: { if (mb_ptr->info & data) { a = TERM_WHITE; c = '*'; } break; } case BORG_SHOW_FLAG: { if (mb_ptr->flags & data) { a = TERM_WHITE; c = '*'; } break; } case BORG_SHOW_FLOW: { if (mb_ptr->flow == data) { a = TERM_WHITE; c = '*'; } break; } case BORG_SHOW_AVOID: { /* Obtain danger */ int p = borg_danger(x, y, 1, TRUE); /* Skip non-avoidances */ if (p <= avoidance / 3) break; if (p <= avoidance) { a = TERM_YELLOW; } else { a = TERM_RED; } break; } case BORG_SHOW_FEAR: { /* Obtain fear */ int p = mb_ptr->fear; /* Skip non-avoidances */ if (p <= avoidance / 10) break; if (p <= avoidance / 4) { a = TERM_YELLOW; } else { a = TERM_RED; } break; } case BORG_SHOW_STEP: { int i; /* Check for an existing step */ for (i = 0; i < track_step_num; i++) { /* Stop if we already new about this glyph */ if ((track_step_x[i] == x) && (track_step_y[i] == y)) { a = TERM_WHITE; c = '*'; break; } } break; } } } if (c != ' ') { /* Hack -- Queue it */ print_rel(c, a, x, y); } } } } /* DVE's function for displaying the status of various info */ /* Display what the borg is thinking DvE*/ void borg_status_window(void) { int j; /* Scan windows */ for (j = 0; j < 8; j++) { term *old = Term; /* Unused */ if (!angband_term[j]) continue; /* Check for borg status term */ if (window_flag[j] & (PW_BORG_2)) { cptr attr; /* Activate */ Term_activate(angband_term[j]); /* Display what resists the borg (thinks he) has */ prtf(5, 0, "RESISTS"); /* Basic four */ attr = CLR_SLATE; if (FLAG(bp_ptr, TR_RES_ACID)) attr = CLR_BLUE; if (my_oppose_acid) attr = CLR_GREEN; if (FLAG(bp_ptr, TR_IM_ACID)) attr = CLR_WHITE; prtf(1, 1, "%sAcid", attr); attr = CLR_SLATE; if (FLAG(bp_ptr, TR_RES_ELEC)) attr = CLR_BLUE; if (my_oppose_elec) attr = CLR_GREEN; if (FLAG(bp_ptr, TR_IM_ELEC)) attr = CLR_WHITE; prtf(1, 2, "%sElec", attr); attr = CLR_SLATE; if (FLAG(bp_ptr, TR_RES_FIRE)) attr = CLR_BLUE; if (my_oppose_fire) attr = CLR_GREEN; if (FLAG(bp_ptr, TR_IM_FIRE)) attr = CLR_WHITE; prtf(1, 3, "%sFire%s", attr); attr = CLR_SLATE; if (FLAG(bp_ptr, TR_RES_COLD)) attr = CLR_BLUE; if (my_oppose_cold) attr = CLR_GREEN; if (FLAG(bp_ptr, TR_IM_COLD)) attr = CLR_WHITE; prtf(1, 4, "%sCold", attr); /* High resists */ attr = CLR_SLATE; if (FLAG(bp_ptr, TR_RES_POIS)) attr = CLR_BLUE; if (my_oppose_pois) attr = CLR_GREEN; if (FLAG(bp_ptr, TR_IM_POIS)) attr = CLR_WHITE; prtf(1, 5, "%sPois", attr); if (FLAG(bp_ptr, TR_RES_FEAR)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(1, 6, "%sFear", attr); if (FLAG(bp_ptr, TR_RES_LITE)) attr = CLR_BLUE; else if (FLAG(bp_ptr, TR_IM_LITE)) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(1, 7, "%sLite", attr); if (FLAG(bp_ptr, TR_RES_DARK)) attr = CLR_BLUE; else if (FLAG(bp_ptr, TR_IM_DARK)) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(1, 8, "%sDark", attr); if (FLAG(bp_ptr, TR_RES_BLIND)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 1, "%sBlind", attr); if (FLAG(bp_ptr, TR_RES_CONF)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 2, "%sConfu", attr); if (FLAG(bp_ptr, TR_RES_SOUND)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 3, "%sSound", attr); if (FLAG(bp_ptr, TR_RES_SHARDS)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 4, "%sShard", attr); if (FLAG(bp_ptr, TR_RES_NEXUS)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 5, "%sNexus", attr); if (FLAG(bp_ptr, TR_RES_NETHER)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 6, "%sNethr", attr); if (FLAG(bp_ptr, TR_RES_CHAOS)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 7, "%sChaos", attr); if (FLAG(bp_ptr, TR_RES_DISEN)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(6, 8, "%sDisen", attr); /* Other abilities */ if (FLAG(bp_ptr, TR_SLOW_DIGEST)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 1, "%sS.Dig", attr); if (FLAG(bp_ptr, TR_FEATHER)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 2, "%sFeath", attr); if (bp_ptr->britelite) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 3, "%sPLite", attr); if (FLAG(bp_ptr, TR_REGEN)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 4, "%sRegen", attr); if (FLAG(bp_ptr, TR_TELEPATHY)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 5, "%sTelep", attr); if (FLAG(bp_ptr, TR_SEE_INVIS)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 6, "%sInvis", attr); if (FLAG(bp_ptr, TR_FREE_ACT)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 7, "%sFrAct", attr); if (FLAG(bp_ptr, TR_HOLD_LIFE)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(12, 8, "%sHLife", attr); /* Display the slays */ prtf(5, 10, "Weapon Slays:"); if (FLAG(bp_ptr, TR_SLAY_ANIMAL)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(1, 11, "%sAnimal", attr); if (FLAG(bp_ptr, TR_SLAY_EVIL)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(8, 11, "%sEvil", attr); if (FLAG(bp_ptr, TR_SLAY_UNDEAD)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(15, 11, "%sUndead", attr); if (FLAG(bp_ptr, TR_SLAY_DEMON)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(22, 11, "%sDemon", attr); if (FLAG(bp_ptr, TR_SLAY_ORC)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(1, 12, "%sOrc", attr); if (FLAG(bp_ptr, TR_SLAY_TROLL)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(8, 12, "%sTroll", attr); if (FLAG(bp_ptr, TR_SLAY_GIANT)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(15, 12, "%sGiant", attr); if (FLAG(bp_ptr, TR_SLAY_DRAGON)) attr = CLR_BLUE; if (FLAG(bp_ptr, TR_KILL_DRAGON)) attr = CLR_GREEN; else attr = CLR_SLATE; prtf(22, 12, "%sDragon", attr); if (FLAG(bp_ptr, TR_BRAND_ACID)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(1, 13, "%sAcid", attr); if (FLAG(bp_ptr, TR_BRAND_COLD)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(8, 13, "%sCold", attr); if (FLAG(bp_ptr, TR_BRAND_ELEC)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(15, 13, "%sElec", attr); if (FLAG(bp_ptr, TR_BRAND_FIRE)) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(22, 13, "%sFire", attr); /* Display the Concerns */ prtf(36, 10, "Concerns:"); if (bp_ptr->status.cursed) attr = CLR_BLUE; else if (bp_ptr->status.heavy_curse) attr = CLR_ORANGE; else attr = CLR_SLATE; prtf(29, 11, "%sCursed", attr); if (bp_ptr->status.weak) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(36, 11, "%sWeak", attr); if (bp_ptr->status.poisoned) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(43, 11, "%sPoison", attr); if (bp_ptr->status.cut) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(29, 12, "%sCut", attr); if (bp_ptr->status.stun) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(36, 12, "%sStun", attr); if (bp_ptr->status.confused) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(43, 12, "%sConfused", attr); if (bp_ptr->status.fixexp) attr = CLR_BLUE; else attr = CLR_SLATE; prtf(43, 13, "%sExp Drain", attr); /* Display the Time */ prtf(60, 10, "Time:"); prtf(54, 11, "This Level %d", borg_t - borg_began); prtf(54, 12, CLR_SLATE "Since Town " CLR_WHITE "%d", borg_time_town + (borg_t - borg_began)); /* Sustains */ prtf(19, 0, "Sustains"); if (bp_ptr->sust[A_STR]) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(21, 1, "%sSTR", attr); if (bp_ptr->sust[A_INT]) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(21, 2, "%sINT", attr); if (bp_ptr->sust[A_WIS]) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(21, 3, "%sWIS", attr); if (bp_ptr->sust[A_DEX]) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(21, 4, "%sDEX", attr); if (bp_ptr->sust[A_CON]) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(21, 5, "%sCON", attr); if (bp_ptr->sust[A_CHR]) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(21, 6, "%sCHR", attr); /* Temporary effects */ prtf(28, 0, "Temp Effects"); if (borg_prot_from_evil) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 1, "%sProt. Evil", attr); if (borg_goi) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 2, "%sInvulnerable", attr); if (borg_hero) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 3, "%sHeroism", attr); if (borg_berserk) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 4, "%sBerserk", attr); if (borg_shield) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 5, "%sShielded", attr); if (borg_bless) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 6, "%sBlessed", attr); if (borg_speed) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 7, "%sFast", attr); if (borg_inviso) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(28, 8, "%sInvisible", attr); /* Temporary effects */ prtf(42, 0, "Level Information"); if (vault_on_level) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(42, 1, "%sVault on Level", attr); if (unique_on_level) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(42, 2, "%sUnique on level", attr); if (unique_on_level) prtf(58, 2, "(%s)", mon_race_name(&r_info[unique_r_idx])); else prtf(58, 2, ""); prtf(42, 4, CLR_SLATE "Borg is ready for level " CLR_WHITE "%d", borg_prepared_depth()); /* In the dungeon */ if (bp_ptr->depth || vanilla_town) { /* level preparedness */ prtf(42, 6, CLR_SLATE "Reason: " CLR_WHITE "%s", borg_prepared(borg_prepared_depth())); } else { cptr why; switch(goal) { case GOAL_NONE: { why = "No goal"; break; } case GOAL_KILL: { why = "Killing a monster"; break; } case GOAL_TAKE: { why = "Getting an object"; break; } case GOAL_FLEE: { why = "Running away"; break; } case GOAL_SHOP: { why = "Shopping"; break; } case GOAL_DARK: { why = "Exploring"; break; } case GOAL_TOWN: { why = "Reaching a town"; break; } case GOAL_CAVE: { why = "Reaching a dungeon"; break; } default: why = "Something or other"; } /* level preparedness */ prtf(42, 6, CLR_SLATE "What to do on the surface: " CLR_WHITE "%s.", why); } if (goal_fleeing) attr = CLR_WHITE; else attr = CLR_SLATE; prtf(42, 7, "%sFleeing Level", attr); prtf(42, 8, CLR_SLATE "Maximal Depth: %d", bp_ptr->max_depth); /* Fresh */ Term_fresh(); /* Restore */ Term_activate(old); } } } /* * Hack -- interact with the "Ben Borg". */ void do_cmd_borg(void) { char cmd; /* Get a "Borg command", or abort */ if (!get_com("Borg command: ", &cmd)) return; /* Hack -- force initialization */ if (!initialized) borg_init_9(); switch (cmd) { case 'z': case 'Z': { /* Command: Activate */ /* Activate */ borg_active = TRUE; /* Reset cancel */ borg_cancel = FALSE; /* Step forever */ borg_step = 0; /*** Redraw ***/ /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); /* Redraw everything */ do_cmd_redraw(); /* need to check all stats */ my_need_stat_check[0] = TRUE; my_need_stat_check[1] = TRUE; my_need_stat_check[2] = TRUE; my_need_stat_check[3] = TRUE; my_need_stat_check[4] = TRUE; my_need_stat_check[5] = TRUE; /* Fill the borg_bools for temporary states */ borg_cheat_temp_bools(); /* Make sure the right options are set */ borg_set_options(); /* Read the wilderness map */ borg_read_map(); /* Message */ borg_note("# Installing keypress hook"); /* Activate the key stealer */ inkey_hack = borg_inkey_hack; break; } case 'u': case 'U': { /* Command: Update */ /* Activate */ borg_active = TRUE; /* Immediate cancel */ borg_cancel = TRUE; /* Step forever */ borg_step = 0; /* Fill the borg_bools for temporary states*/ borg_cheat_temp_bools(); /* Make sure the right options are set */ borg_set_options(); /* Read the wilderness map */ borg_read_map(); /* Message */ borg_note("# Installing keypress hook"); /* Activate the key stealer */ inkey_hack = borg_inkey_hack; break; } case 'x': case 'X': { /* Command: Step */ /* Activate */ borg_active = TRUE; /* Reset cancel */ borg_cancel = FALSE; /* Step N times */ borg_step = 1; /*** Redraw ***/ /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); /* Redraw everything */ do_cmd_redraw(); /* need to check all stats */ my_need_stat_check[0] = TRUE; my_need_stat_check[1] = TRUE; my_need_stat_check[2] = TRUE; my_need_stat_check[3] = TRUE; my_need_stat_check[4] = TRUE; my_need_stat_check[5] = TRUE; /* Fill the borg_bools for temporary states*/ borg_cheat_temp_bools(); /* Make sure the right options are set */ borg_set_options(); /* Read the wilderness map */ borg_read_map(); /* Message */ borg_note("# Installing keypress hook"); /* Activate the key stealer */ inkey_hack = borg_inkey_hack; break; } case 'f': case 'F': { /* Command: toggle "flags" */ /* Save and partly clear the screen */ Term_save(); clear_region(0, 1, 6); do { /* List the possibilities */ prtf(1, 2, "a) The borg stops when he wins: %s (borg_stop_king)\n" "b) Allow borg to avoid death: %s (borg_cheat_death)\n" "c) Log some characteristics at death: %s (borg_flag_dump)\n" "d) Autosave when entering new levels: %s (borg_flag_save)\n" , (borg_stop_king) ? "true " : "false" , (borg_cheat_death) ? "true " : "false" , (borg_flag_dump) ? "true " : "false" , (borg_flag_save) ? "true " : "false"); /* What does the user want? */ get_com("Borg options (Command (a-d), ESC)", &cmd); /* Toggle a bool */ switch (cmd) { case 'a': case 'A': { borg_stop_king = !borg_stop_king; break; } case 'b': case 'B': { borg_cheat_death = !borg_cheat_death; break; } case 'c': case 'C': { borg_flag_dump = !borg_flag_dump; break; } case 'd': case 'D': { borg_flag_save = !borg_flag_save; break; } case ESCAPE: { /* Restore the screen */ Term_load(); break; } } } while (cmd != ESCAPE); break; } case 'l': case 'L': { /* Start a new log file */ char buf[1024]; /* Close the log file */ if (borg_fff) my_fclose(borg_fff); /* Default */ path_make(buf, ANGBAND_DIR_USER, "borg.log"); /* XXX XXX XXX Get the name and open the log file */ if (get_string(buf, 70, "Borg Log File: ")) { /* Open a new file */ borg_fff = my_fopen(buf, "w"); /* Failure */ if (!borg_fff) msgf("Cannot open that file."); } break; } case 's': case 'S': { /* Activate a search string */ /* Get the new search string (or cancel the matching) */ if (!get_string(borg_match, 70, "Borg Match String: ")) { /* Cancel it */ borg_match[0] = 0; /* Message */ msgf("Borg Match String de-activated."); } break; } case 'g': case 'G': { /* Command: check Grid "feature" flags */ byte feat = 0; /* Get a "Borg command", or abort */ if (!get_com("Borg command: Show grids: ", &cmd)) return; /* Extract a flag */ switch (cmd) { case '.': { feat = map_loc(c_x, c_y)->feat; break; } case ',': { feat = FEAT_OPEN; break; } case '+': { feat = FEAT_CLOSED; break; } case 'x': { feat = FEAT_BROKEN; break; } case '<': { feat = FEAT_LESS; break; } case '>': { feat = FEAT_MORE; break; } case 's': { feat = FEAT_SECRET; break; } case ':': { feat = FEAT_RUBBLE; break; } case 't': { feat = FEAT_TREES; break; } case 'l': { feat = FEAT_SHAL_LAVA; break; } case 'L': { feat = FEAT_DEEP_LAVA; break; } case 'a': { feat = FEAT_SHAL_WATER; break; } case 'A': { feat = FEAT_DEEP_WATER; break; } case 'o': { feat = FEAT_OCEAN_WATER; break; } case 'b': { feat = FEAT_SHAL_ACID; break; } case 'B': { feat = FEAT_DEEP_ACID; break; } case 'c': { feat = FEAT_SHAL_SWAMP; break; } case 'C': { feat = FEAT_DEEP_SWAMP; break; } case 'w': { feat = FEAT_WALL_EXTRA; break; } case 'p': { feat = FEAT_PERM_EXTRA; break; } default: { /* Save the screen and clear it */ Term_save(); Term_clear(); prtf(1, 1, "Possible entries for features:\n" ". Dungeon floor\n" ", Open door\n" "+ Closed door\n" "x Broken door\n" "s Secret door\n" "< Up staircase\n" "> Down staircase\n" ": Rubble\n" "t Trees\n" "l Shallow Lava\n" "L Deep Lava\n" "a Shallow water\n" "A Deep water\n" "o Ocean water\n" "b Shallow acid\n" "B Deep acid\n" "c Shallow swamp\n" "C Deep swamp\n" "w Wall\n" "p Permanent wall\n"); /* Get keypress */ msgf("Press any key."); message_flush(); /* Restore the screen */ Term_load(); return; } } /* Show it */ borg_display_map_info(feat, BORG_SHOW_FEAT); /* Get keypress */ msgf("Press any key."); message_flush(); /* Redraw map */ prt_map(); break; } case 'i': case 'I': { /* Command: check "info" flags */ byte mask; /* Get a "Borg command", or abort */ if (!get_com("Borg command: Show grids: ", &cmd)) return; /* Extract a flag */ switch (cmd) { case '0': { mask = 1 << 0; break; } case '1': { mask = 1 << 1; break; } case '2': { mask = 1 << 2; break; } case '3': { mask = 1 << 3; break; } case '4': { mask = 1 << 4; break; } case '5': { mask = 1 << 5; break; } case '6': { mask = 1 << 6; break; } case '7': { mask = 1 << 7; break; } default: { mask = 0x00; break; } } /* Show it */ borg_display_map_info(mask, BORG_SHOW_FLAG); /* Get keypress */ msgf("Press any key."); message_flush(); /* Redraw map */ prt_map(); break; } case 'a': case 'A': { /* Command: check "avoidances" */ /* Show it */ borg_display_map_info(0, BORG_SHOW_AVOID); /* Get keypress */ msgf("(%d,%d) Avoidance value %d.", c_x, c_y, avoidance); message_flush(); /* Redraw map */ prt_map(); break; } case 'y': { /* Command: check previous steps */ /* Show it */ borg_display_map_info(0, BORG_SHOW_STEP); /* Get keypress */ msgf("(%d) Steps noted", track_step_num); message_flush(); /* Redraw map */ prt_map(); break; } case 'k': { /* Command: show "monsters" */ int i, n = 0; /* Scan the monsters */ for (i = 1; i < borg_kills_nxt; i++) { borg_kill *kill = &borg_kills[i]; int x, y; /* Still alive */ if (!kill->r_idx) continue; /* Require current knowledge */ if (kill->when < borg_t) continue; x = kill->x; y = kill->y; /* Display */ print_rel('*', TERM_RED, x, y); /* Count */ n++; } /* Get keypress */ msgf("There are %d known monsters.", n); message_flush(); /* Redraw map */ prt_map(); break; } case 'K': { /* Command: show "monsters" */ int i; /* Scan the monsters */ for (i = 0; i < borg_ball_n; i++) { int x = borg_ball_x[i]; int y = borg_ball_y[i]; /* Display */ print_rel('*', TERM_RED, x, y); } /* Get keypress */ msgf("There are %d grids.", borg_ball_n); message_flush(); /* Redraw map */ prt_map(); break; } case 't': case 'T': { /* Command: show "objects" */ int i, n = 0; /* Scan the objects */ for (i = 1; i < borg_takes_nxt; i++) { borg_take *take = &borg_takes[i]; /* Still alive */ if (take->k_idx) { int x = take->x; int y = take->y; /* Display */ print_rel('*', TERM_RED, x, y); /* Count */ n++; } } /* Get keypress */ msgf("There are %d known objects.", n); message_flush(); /* Redraw map */ prt_map(); break; } case '7': { /* Command: debug -- show towns */ int i; /* Get keypress */ msgf("There are %d known towns.", borg_town_num); message_flush(); for (i = 0; i < borg_town_num; i++) { /* Print */ msgf("i = %d, (x, y) = (%d, %d), visit = %c, name = %s", i, borg_towns[i].x, borg_towns[i].y, (borg_towns[i].visit) ? 'T' : 'F', borg_towns[i].name); message_flush(); } break; } case '8': { /* Command: debug -- show shops */ int i, n = 0; for (i = 0; i < borg_shop_num; i++) { char c = (i < 10) ? i + '0' : '*'; /* Print */ print_rel(c, TERM_RED, borg_shops[i].x, borg_shops[i].y); /* Count the visited shops */ if (borg_shops[i].visit) n++; } /* Get keypress */ msgf("There are %d known shops, %d were visited.", borg_shop_num, n); message_flush(); /* Redraw map */ prt_map(); break; } case '9': { /* Command: debug -- show dungeons */ int i; for (i = 0; i < borg_dungeon_num; i++) { /* Print */ print_rel('*', TERM_RED, borg_dungeons[i].x, borg_dungeons[i].y); } /* Get keypress */ msgf("There are %d known dungeons.", borg_dungeon_num); message_flush(); /* Redraw map */ prt_map(); msgf("(c_x, c_y) = (%d, %d), dungeon_num = %d", c_x, c_y, dungeon_num); for (i = 0; i < borg_dungeon_num; i++) { /* Print */ msgf("i = %d, (x, y) = (%d, %d), min = %d, max = %d, bottom = %c", i, borg_dungeons[i].x, borg_dungeons[i].y, borg_dungeons[i].min_depth, borg_dungeons[i].max_depth, borg_dungeons[i].bottom ? 'T' : 'F'); } break; } case '%': { /* Command: debug -- current flow */ byte i; /* Flow */ for (i = 0; i < 250; i++) { /* Show it */ borg_display_map_info(i, BORG_SHOW_FLOW); /* Get keypress */ msgf("Flow depth %d.", i); message_flush(); if (!get_check("Continue?")) break; /* Redraw map */ prt_map(); } break; } case '^': { /* Display the intended path to the flow */ int x, y; int o; int false_y, false_x; false_y = c_y; false_x = c_x; /* Continue */ for (o = 0; o < 250; o++) { int b_n = 0; int i, b_i = -1; int c, b_c; map_block *mb_ptr = map_loc(c_x, c_y); /* Flow cost of current grid */ b_c = mb_ptr->flow * 10; /* Prevent loops */ b_c = b_c - 5; /* Look around */ for (i = 0; i < 8; i++) { /* Grid in that direction */ x = false_x + ddx_ddd[i]; y = false_y + ddy_ddd[i]; /* Bounds checking */ if (!map_in_bounds(x, y)) continue; /* Access the grid */ mb_ptr = map_loc(x, y); /* Flow cost at that grid */ c = mb_ptr->flow * 10; /* Never backtrack */ if (c > b_c) continue; /* Notice new best value */ if (c < b_c) b_n = 0; /* Apply the randomizer to equivalent values */ if ((++b_n >= 2) && (randint0(b_n))) continue; /* Track it */ b_i = i; b_c = c; } /* Try it */ if (b_i >= 0) { /* Access the location */ x = false_x + ddx_ddd[b_i]; y = false_y + ddy_ddd[b_i]; /* Display */ print_rel('*', TERM_RED, x, y); /* Simulate motion */ false_y = y; false_x = x; } } msgf("Probable Flow Path"); message_flush(); /* Redraw map */ prt_map(); break; } case '#': { /* Command: debug -- danger of grid */ int n = 0; /* Turns */ n = (p_ptr->cmd.arg ? p_ptr->cmd.arg : 1); /* Danger of grid */ msgf("Danger(%d,%d,%d) is %d", p_ptr->target_col, p_ptr->target_row, n, borg_danger(p_ptr->target_col, p_ptr->target_row, n, TRUE)); break; } case '_': { /* Command: Regional Fear Info */ /* Show it */ borg_display_map_info(0, BORG_SHOW_FEAR); /* Get keypress */ msgf("(%d,%d) Regional Fear.", c_x, c_y); message_flush(); /* Redraw map */ prt_map(); break; } case 'p': case 'P': { /* Command: debug -- Power */ s32b p; /* Examine the screen */ borg_update_frame(); /* Examine the screen */ borg_update(); /* Extract some "hidden" variables */ borg_hidden(); /* Evaluate */ p = borg_power(); /* Report it */ msgf("Current Borg Power %ld", p); msgf("Current Home Power %ld", borg_power_home()); break; } case '!': { /* Command: Show time */ s32b time = borg_t - borg_began; msgf("time: (%d) ", (int)time); time = (borg_time_town + (borg_t - borg_began)); msgf("; from town (%d)", (int)time); msgf("; need inviso (%d)", (int)need_see_inviso); break; } case '2': { /* Command: APW HACK debug -- preparation for level */ int i = 0; /* Examine the screen */ borg_update_frame(); borg_update(); /* Extract some "hidden" variables */ borg_hidden(); /* Examine the inventory */ borg_notice(); borg_notice_home(); /* Dump prep codes */ for (i = 1; i <= MAX_DEPTH; i++) { /* Dump fear code */ if (borg_prepared(i)) break; } msgf("Max Level: %d Prep'd For: %d Reason: %s", bp_ptr->max_depth, i - 1, borg_prepared(i)); break; } case 'o': case 'O': { /* Command: Display all known info on item */ int n; bool show_inventory = TRUE; char tmp_val[80]; /* Save the screen */ Term_save(); do { if (inven_num == 0) show_inventory = FALSE; if (show_inventory) { strnfmt(tmp_val, 80, "Inven: / for Equip, ESC) Show borg believes on which item? (a-%c) " , I2A(inven_num - 1)); } else { strnfmt(tmp_val, 80, "Equip: / for Inven, ESC) Show borg believes on which item? (a-%c) " , I2A(equip_num - 1)); } get_com(tmp_val, &cmd); if (cmd == '/') show_inventory = !show_inventory; } while (cmd == '/'); /* Convert to array index*/ n = A2I(cmd); if (show_inventory) { /* Bound checking */ if ((n < 0) || (n >= inven_num)) break; /* Display the special screen */ borg_display_item(&inventory[n]); } else { /* Is it legal to use this in equipment[]? */ if ((n < 0) || (n >= equip_num)) break; if (!look_up_equip_slot(n)) break; /* Display the special screen */ borg_display_item(&equipment[n]); } /* Pause for study */ msgf("Borg believes: "); message_flush(); /* Restore the screen */ Term_load(); break; } case 'd': case 'D': { /* * Dump all the spells from the books of your realms * in your inventory */ byte spell, inv, ii; cptr legal = NULL; /* Warriors don't have realms */ if (borg_class == CLASS_WARRIOR) break; /* Save the screen */ Term_save(); if (borg_class == CLASS_MINDCRAFTER) { /* Partly clear the screen */ clear_region(0, 1, 14); ii = 2; /* Mindcrafters have no books, just spells */ for (spell = 0; spell < MINDCRAFT_MAX; spell++) { /* pick up the spell */ borg_mind *as = &borg_minds[spell]; /* Can you cast it at all? */ legal = (as->level <= bp_ptr->lev) ? "Legal" : "Not legal"; /* Show spell name, legalility and # of casts */ put_fstr(1, ii, "%s", as->name); put_fstr(32, ii++, "%s, attempted %d times", legal, as->times); } /* Give a chance to look */ msgf("Mindcrafter spells:"); message_flush(); /* Restore the screen */ Term_load(); /* Done */ break; } /* Loop through the inventory */ for (inv = 0; inv < inven_num; inv++) { borg_magic *as; int realm, book; /* Partly clear the screen */ clear_region(0, 1, 12); /* On which line do we print? */ ii = 4; /* Get the realm of this book */ realm = inventory[inv].tval + 1 - TV_BOOKS_MIN; /* Is this one the possible realms? */ if (borg_has_realm(realm)) { /* Display name of the book */ put_fstr(1, ii - 2, "%s", inventory[inv].o_name); /* Which book of this realm is it exactly? */ book = k_info[inventory[inv].k_idx].sval; for (spell = 0; spell < 8; spell++) { /* pick up spell */ as = &borg_magics[realm][book][spell]; /* Can you cast it at all? */ if (as->level < 99) { legal = (borg_spell_legal(realm, book, spell) ? "Legal" : "Not legal"); } /* Show spell name, legalility and # of casts */ put_fstr(1, ii, "%s", as->name); put_fstr(32, ii++, "%s, attempted %d times", legal, as->times); } /* Give a chance to look */ msgf("Examining spell books."); message_flush(); } } /* Restore the screen */ Term_load(); /* Done */ break; } case '?': { /* Save and clear the screen */ Term_save(); Term_clear(); /* First column */ prtf(2, 3, "Command 'z' activates the Borg.\n" "Command 'x' steps the Borg.\n" "Command 'u' updates the Borg.\n" "Command '2' level prep info.\n" "Command '8' Shows the shops.\n" "Command '9' Shows the dungeons.\n" "Command 's' activates search mode.\n" "Command 'g' displays grid feature.\n" "Command 'k' displays monster info.\n" "Command '%%' displays current flow.\n" "Command '_' Regional Fear info.\n" "Command 'd' Dump spell info.\n" "Command '^' Flow Pathway."); /* Second column */ prtf(42, 3, "Command 'f' modifies the options.\n" "Command 'l' activates a log file.\n" "Command 'i' displays grid info.\n" "Command 'a' displays avoidances.\n" "Command 't' displays object info.\n" "Command '#' displays danger grid.\n" "Command 'p' Borg Power.\n" "Command '!' Time.\n" "Command 'y' Last 75 steps.\n" "Command 'o' Examine Inven Item."); /* Prompt for key */ msgf("Commands: "); message_flush(); /* Restore the screen */ Term_load(); /* Done */ break; } default: { /* Oops */ /* Message */ msgf("That is not a legal Borg command."); break; } } } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif zangband/src/angband.h0000755000000000000000000000365610250356275013714 0ustar rootroot/* File: angband.h */ /* Main "Angband" header file */ /* * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ #ifndef INCLUDED_ANGBAND_H #define INCLUDED_ANGBAND_H /* * Configuration via autoconf */ #ifdef HAVE_CONFIG_H #include "autoconf.h" #endif /* HAVE_CONFIG_H */ /* * Configuration */ #include "z-config.h" /* * Include the low-level includes. */ #include "h-basic.h" /* * Include the mid-level includes. */ #include "z-util.h" #include "z-virt.h" #include "z-form.h" #include "z-rand.h" #include "z-term.h" /* * Include the high-level includes. */ #include "defines.h" #include "types.h" #include "externs.h" /***** Some older copyright messages follow below *****/ /* * Note that these copyright messages apply to an ancient version * of Angband, as in, from pre-2.4.frog-knows days, and thus the * references to version numbers may be rather misleading... */ /* * UNIX ANGBAND Version 5.0 */ /* Original copyright message follows. */ /* * ANGBAND Version 4.8 COPYRIGHT (c) Robert Alan Koeneke * * I lovingly dedicate this game to hackers and adventurers * everywhere... * * Designer and Programmer: * Robert Alan Koeneke * University of Oklahoma * * Assistant Programmer: * Jimmey Wayne Todd * University of Oklahoma * * Assistant Programmer: * Gary D. McAdoo * University of Oklahoma * * UNIX Port: * James E. Wilson * UC Berkeley * wilson@ernie.Berkeley.EDU * ucbvax!ucbernie!wilson */ /* * ANGBAND may be copied and modified freely as long as the above * credits are retained. No one who-so-ever may sell or market * this software in any form without the expressed written consent * of the author Robert Alan Koeneke. */ #endif zangband/src/defines.h0000755000000000000000000051402410250356275013733 0ustar rootroot/* File: defines.h */ /* Purpose: global constants and macro definitions */ /* * Do not edit this file unless you know *exactly* what you are doing. * * Some of the values in this file were chosen to preserve game balance, * while others are hard-coded based on the format of old save-files, the * definition of arrays in various places, mathematical properties, fast * computation, storage limits, or the format of external text files. * * Changing some of these values will induce crashes or memory errors or * savefile mis-reads. Most of the comments in this file are meant as * reminders, not complete descriptions, and even a complete knowledge * of the source may not be sufficient to fully understand the effects * of changing certain definitions. * * Lastly, note that the code does not always use the symbolic constants * below, and sometimes uses various hard-coded values that may not even * be defined in this file, but which may be related to definitions here. * This is of course bad programming practice, but nobody is perfect... * * You have been warned. */ /* * Name of the version/variant */ #define VERSION_NAME "ZAngband" /* Savefile version */ #define SAVEFILE_VERSION 52 /* User-visible version */ #define VER_MAJOR 2 #define VER_MINOR 7 #define VER_PATCH 5 #define VER_EXTRA 0 /* Versions after release */ #define VER_AFTER "pre1" /* Stringify argument */ #define Z_STR(a) Z_STR1(a) #define Z_STR1(a)\ # a /* Pre-release version string */ #if VER_EXTRA != 0 #define PRE_VERSION \ "pre" Z_STR(VER_EXTRA) #else #define PRE_VERSION #endif /* VER_EXTRA != 0 */ /* * Current version string */ #define VERSION_STRING \ Z_STR(VER_MAJOR) "." Z_STR(VER_MINOR) "." Z_STR(VER_PATCH) PRE_VERSION VER_AFTER #define ANGBAND_2_8_1 #define ZANGBAND #define ZANGBAND_BIGSCREEN #define ZANGBAND_WILDERNESS /* hack - define if the source contains the cleanup_angband() function. */ #define HAS_CLEANUP /* Debug modes depend on version */ /* Dev version? */ #if ((VER_MINOR == 1) || (VER_MINOR == 3) || (VER_MINOR == 5) || \ (VER_MINOR == 7) || (VER_MINOR == 9)) #define DEBUG #endif /* Dev version */ /* Alpha testing */ #if (VER_EXTRA != 0) #define DEBUG_ALPHA #ifndef DEBUG #define DEBUG #endif /* !DEBUG */ #endif /* Pre version */ /* Turn off assert if not debugging */ #ifndef DEBUG #undef assert #define assert(ignore) ((void) 0) #endif /* !DEBUG */ /* * The maximum number of players we support */ #define MAX_PLAYERS 1 /* * Maximum amount of Angband windows. */ #define ANGBAND_TERM_MAX 8 /* * Maximum of different windows */ #define WINDOW_CHOICE_MAX 15 /* * Number of grids in each block (vertically) */ #define BLOCK_HGT 11 /* * Number of grids in each block (horizontally) */ #define BLOCK_WID 11 /* * Maximum dungeon height in grids. */ #define MAX_HGT 66 /* * Maximum dungeon width in grids. */ #define MAX_WID 198 /* * Script triggers */ #define MAX_TRIGGER 10 #define TRIGGER_USE 0 #define TRIGGER_MAKE 1 #define TRIGGER_BONUS 2 #define TRIGGER_SMASH 3 #define TRIGGER_DESC 4 #define TRIGGER_TIMED 5 #define TRIGGER_HIT 6 #define TRIGGER_ATTACK 7 #define TRIGGER_ALTER 8 #define TRIGGER_SPOIL 9 /* * Defines used by the wilderness data structures */ /* Size of wilderness in blocks */ #define WILD_SIZE 129 /* Size of blocks - hard coded. (Affects size of towns) */ #define WILD_BLOCK_SIZE 16 /* Number of blocks the player can see at one time */ #define WILD_VIEW 9 /* Number of blocks of wilderness cache */ #define WILD_CACHE (MAX_PLAYERS * WILD_VIEW * WILD_VIEW * 2) /* Hack XXX Start of the sea types = 2^16 - 65*/ #define WILD_SEA 65471 /* Town types */ #define TOWN_OLD 1 #define TOWN_FRACT 2 #define TOWN_QUEST 3 #define TOWN_DUNGEON 4 /* * Quest type */ #define QUEST_TYPE_NONE 0 #define QUEST_TYPE_BOUNTY 1 #define QUEST_TYPE_DUNGEON 2 #define QUEST_TYPE_WILD 3 #define QUEST_TYPE_MESSAGE 4 #define QUEST_TYPE_FIND_ITEM 5 #define QUEST_TYPE_FIND_PLACE 6 /* * Quest creation types */ #define QC_NONE 0 #define QC_DUN_MONST 1 #define QC_DUN_ARTIFACT 2 /* * Quest action triggers */ #define QX_NONE 0 #define QX_KILL_MONST 1 #define QX_KILL_UNIQUE 2 #define QX_KILL_WINNER 3 #define QX_WILD_ENTER 4 #define QX_KNOW_ARTIFACT 5 #define QX_FIND_SHOP 6 /* * Mega Hack XXX XXX Info for winner quest */ #define QW_OBERON 860 #define QW_SERPENT 862 /* Number of gates in the city */ #define MAX_GATES 4 /* Maximum length of town name + '/0' */ #define T_NAME_LEN 18 /* * Total number of stores in vanilla town (see "store.c", etc) */ #define MAX_STORES 9 /* * Total number of stores with stock (see "store.c", etc) */ #define STORE_CACHE_AMNT 100 /* * Total number of owners per store (see "store.c", etc) */ #define MAX_OWNERS 32 /* * Total number of buildings (see "bldg.c", etc) */ #define MAX_BLDG 10 /* List of building types */ #define BUILD_STORE_GENERAL 0 #define BUILD_STORE_ARMOURY 1 #define BUILD_STORE_WEAPON 2 #define BUILD_STORE_TEMPLE 3 #define BUILD_STORE_ALCHEMIST 4 #define BUILD_STORE_MAGIC 5 #define BUILD_STORE_BLACK 6 #define BUILD_STORE_HOME 7 #define BUILD_STORE_BOOK 8 #define BUILD_STAIRS 9 #define BUILD_WEAPONMASTER 10 #define BUILD_RECHARGE 11 #define BUILD_PLUS_WEAPON 12 #define BUILD_PLUS_ARMOUR 13 #define BUILD_MUTATE 14 #define BUILD_NONE 15 #define BUILD_BLANK 16 #define BUILD_MAP 17 #define BUILD_WEAPON1 18 #define BUILD_WEAPON2 19 #define BUILD_WEAPON3 20 #define BUILD_WEAPON4 21 #define BUILD_WEAPON5 22 #define BUILD_ARMOUR1 23 #define BUILD_ARMOUR2 24 #define BUILD_ARMOUR3 25 #define BUILD_ARMOUR4 26 #define BUILD_ARMOUR5 27 #define BUILD_SWORD0 28 #define BUILD_SWORD1 29 #define BUILD_SWORD2 30 #define BUILD_SWORD3 31 #define BUILD_SWORD4 32 #define BUILD_SWORD5 33 #define BUILD_SHIELD0 34 #define BUILD_SHIELD1 35 #define BUILD_SHIELD2 36 #define BUILD_SHIELD3 37 #define BUILD_SHIELD4 38 #define BUILD_SHIELD5 39 #define BUILD_AXE0 40 #define BUILD_AXE1 41 #define BUILD_AXE2 42 #define BUILD_AXE3 43 #define BUILD_AXE4 44 #define BUILD_AXE5 45 #define BUILD_AMMO0 46 #define BUILD_AMMO1 47 #define BUILD_AMMO2 48 #define BUILD_FLET0 49 #define BUILD_FLET1 50 #define BUILD_FLET2 51 #define BUILD_FLET3 52 #define BUILD_WARHALL0 53 #define BUILD_WARHALL1 54 #define BUILD_WARHALL2 55 #define BUILD_WARHALL3 56 #define BUILD_WARHALL4 57 #define BUILD_WARHALL5 58 #define BUILD_CLOTH0 59 #define BUILD_CLOTH1 60 #define BUILD_HARMOUR0 61 #define BUILD_HARMOUR1 62 #define BUILD_HARMOUR2 63 #define BUILD_HARMOUR3 64 #define BUILD_HARMOUR4 65 #define BUILD_HARMOUR5 66 #define BUILD_HAT0 67 #define BUILD_HAT1 68 #define BUILD_HAT2 69 #define BUILD_HAT3 70 #define BUILD_JEWEL0 71 #define BUILD_JEWEL1 72 #define BUILD_JEWEL2 73 #define BUILD_JEWEL3 74 #define BUILD_JEWEL4 75 #define BUILD_STATUE0 76 #define BUILD_STATUE1 77 #define BUILD_FIGUR0 78 #define BUILD_FIGUR1 79 #define BUILD_POTION0 80 #define BUILD_POTION1 81 #define BUILD_POTION2 82 #define BUILD_POTION3 83 #define BUILD_POTION4 84 #define BUILD_SCROLL0 85 #define BUILD_SCROLL1 86 #define BUILD_SCROLL2 87 #define BUILD_SCROLL3 88 #define BUILD_SCROLL4 89 #define BUILD_MAGIC0 90 #define BUILD_MAGIC1 91 #define BUILD_MAGIC2 92 #define BUILD_MAGIC3 93 #define BUILD_MAGIC4 94 #define BUILD_BOOK1 95 #define BUILD_TEMPLE1 96 #define BUILD_TEMPLE2 97 #define BUILD_TEMPLE3 98 #define BUILD_SUPPLIES0 99 #define BUILD_SUPPLIES1 100 #define BUILD_BLACK1 101 #define BUILD_BLACK2 102 #define BUILD_ALCHEMY1 103 #define BUILD_ALCHEMY2 104 #define BUILD_JUNK 105 #define BUILD_FOOD 106 #define BUILD_LIBRARY 107 #define BUILD_CASINO 108 #define BUILD_INN 109 #define BUILD_HEALER 110 #define BUILD_BLACK0 111 #define BUILD_MAGETOWER0 112 #define BUILD_MAGETOWER1 113 #define BUILD_CASTLE0 114 #define BUILD_CASTLE1 115 /* Maximum number of "building" types in a city */ #define MAX_CITY_BUILD 116 /* * Total number of owners per building (see "bldg.c", etc) */ #define MAX_B_OWN 5 /* * Store constants */ #define STORE_INVEN_MAX 24 /* Max number of discrete objs in inven */ #define STORE_TURNOVER 9 /* Normal shop turnover, per day */ #define STORE_MIN_KEEP1 6 /* Min slots to "always" keep full */ #define STORE_MAX_KEEP1 18 /* Max slots to "always" keep full */ #define STORE_MIN_KEEP2 3 /* Min slots to "always" keep full */ #define STORE_MAX_KEEP2 9 /* Max slots to "always" keep full */ #define STORE_SHUFFLE 21 /* 1/Chance (per day) of an owner changing */ #define STORE_TURNS 1000 /* Number of turns between turnovers */ /* Store extra flags */ #define ST_REST_BLESSED 0x01 /* Blessed items */ #define ST_REST_GOOD 0x02 /* Good items only */ #define ST_REST_GREAT 0x04 /* Great items only */ #define ST_HALF_INVEN 0x08 /* One page of inventory */ #define ST_GREED 0x10 /* Double prices */ #define ST_ULTRA_GREED 0x20 /* Quadruple prices */ /* * Maximum number of player "sex" types (see "table.c", etc) */ #define MAX_SEXES 2 /* * Maximum number of player "class" types (see "table.c", etc) */ #define MAX_CLASS 11 /* The number of "patrons" available (for Chaos Warriors) */ #define MAX_PATRON 16 /* Number of entries in the sanity-blast descriptions */ #define MAX_SAN_HORROR 20 #define MAX_SAN_FUNNY 22 #define MAX_SAN_COMMENT 5 /* Chaos Warrior: Reward types: */ #define REW_POLY_SLF 1 #define REW_GAIN_EXP 2 #define REW_LOSE_EXP 3 #define REW_GOOD_OBJ 4 #define REW_GREA_OBJ 5 #define REW_CHAOS_WP 6 #define REW_GOOD_OBS 7 #define REW_GREA_OBS 8 #define REW_TY_CURSE 9 #define REW_SUMMON_M 10 #define REW_H_SUMMON 11 #define REW_DO_HAVOC 12 #define REW_GAIN_ABL 13 #define REW_LOSE_ABL 14 #define REW_RUIN_ABL 15 #define REW_AUGM_ABL 16 #define REW_POLY_WND 17 #define REW_HEAL_FUL 18 #define REW_HURT_LOT 19 #define REW_CURSE_WP 20 #define REW_CURSE_AR 21 #define REW_PISS_OFF 22 #define REW_WRATH 23 #define REW_DESTRUCT 24 #define REW_GENOCIDE 25 #define REW_MASS_GEN 26 #define REW_DISPEL_C 27 #define REW_UNUSED_1 28 #define REW_UNUSED_2 29 #define REW_UNUSED_3 30 #define REW_UNUSED_4 31 #define REW_UNUSED_5 32 #define REW_IGNORE 33 #define REW_SER_UNDE 34 #define REW_SER_DEMO 35 #define REW_SER_MONS 36 /* Chaos mutations */ #define MUT_SETS_MAX 3 #define MUT_PER_SET 32 /* "Activatable" mutations must be in MUT1_* */ #define MUT1_SPIT_ACID 0x00000001L #define MUT1_BR_FIRE 0x00000002L #define MUT1_HYPN_GAZE 0x00000004L #define MUT1_TELEKINES 0x00000008L #define MUT1_VTELEPORT 0x00000010L /* Voluntary teleport */ #define MUT1_MIND_BLST 0x00000020L #define MUT1_RADIATION 0x00000040L #define MUT1_VAMPIRISM 0x00000080L #define MUT1_SMELL_MET 0x00000100L #define MUT1_SMELL_MON 0x00000200L #define MUT1_BLINK 0x00000400L #define MUT1_EAT_ROCK 0x00000800L #define MUT1_SWAP_POS 0x00001000L #define MUT1_SHRIEK 0x00002000L #define MUT1_ILLUMINE 0x00004000L #define MUT1_DET_CURSE 0x00008000L #define MUT1_BERSERK 0x00010000L #define MUT1_POLYMORPH 0x00020000L #define MUT1_MIDAS_TCH 0x00040000L #define MUT1_GROW_MOLD 0x00080000L #define MUT1_RESIST 0x00100000L #define MUT1_EARTHQUAKE 0x00200000L #define MUT1_EAT_MAGIC 0x00400000L #define MUT1_WEIGH_MAG 0x00800000L #define MUT1_STERILITY 0x01000000L #define MUT1_PANIC_HIT 0x02000000L #define MUT1_DAZZLE 0x04000000L #define MUT1_LASER_EYE 0x08000000L #define MUT1_RECALL 0x10000000L #define MUT1_BANISH 0x20000000L #define MUT1_COLD_TOUCH 0x40000000L #define MUT1_LAUNCHER 0x80000000L /* Randomly activating mutations must be MUT2_* */ #define MUT2_BERS_RAGE 0x00000001L #define MUT2_COWARDICE 0x00000002L #define MUT2_RTELEPORT 0x00000004L /* Random teleport */ #define MUT2_ALCOHOL 0x00000008L #define MUT2_HALLU 0x00000010L #define MUT2_FLATULENT 0x00000020L #define MUT2_SCOR_TAIL 0x00000040L #define MUT2_HORNS 0x00000080L #define MUT2_BEAK 0x00000100L #define MUT2_ATT_DEMON 0x00000200L #define MUT2_PROD_MANA 0x00000400L #define MUT2_SPEED_FLUX 0x00000800L #define MUT2_BANISH_ALL 0x00001000L #define MUT2_EAT_LIGHT 0x00002000L #define MUT2_TRUNK 0x00004000L #define MUT2_ATT_ANIMAL 0x00008000L #define MUT2_TENTACLES 0x00010000L #define MUT2_RAW_CHAOS 0x00020000L #define MUT2_NORMALITY 0x00040000L #define MUT2_WRAITH 0x00080000L #define MUT2_POLY_WOUND 0x00100000L #define MUT2_WASTING 0x00200000L #define MUT2_ATT_DRAGON 0x00400000L #define MUT2_WEIRD_MIND 0x00800000L #define MUT2_NAUSEA 0x01000000L #define MUT2_CHAOS_GIFT 0x02000000L #define MUT2_WALK_SHAD 0x04000000L #define MUT2_WARNING 0x08000000L #define MUT2_INVULN 0x10000000L #define MUT2_SP_TO_HP 0x20000000L #define MUT2_HP_TO_SP 0x40000000L #define MUT2_DISARM 0x80000000L /* Other mutations will be mainly in MUT3_* */ #define MUT3_HYPER_STR 0x00000001L #define MUT3_PUNY 0x00000002L #define MUT3_HYPER_INT 0x00000004L #define MUT3_MORONIC 0x00000008L #define MUT3_RESILIENT 0x00000010L #define MUT3_XTRA_FAT 0x00000020L #define MUT3_ALBINO 0x00000040L #define MUT3_FLESH_ROT 0x00000080L #define MUT3_SILLY_VOI 0x00000100L #define MUT3_BLANK_FAC 0x00000200L #define MUT3_ILL_NORM 0x00000400L #define MUT3_XTRA_EYES 0x00000800L #define MUT3_MAGIC_RES 0x00001000L #define MUT3_XTRA_NOIS 0x00002000L #define MUT3_INFRAVIS 0x00004000L #define MUT3_XTRA_LEGS 0x00008000L #define MUT3_SHORT_LEG 0x00010000L #define MUT3_ELEC_TOUC 0x00020000L #define MUT3_FIRE_BODY 0x00040000L #define MUT3_WART_SKIN 0x00080000L #define MUT3_SCALES 0x00100000L #define MUT3_IRON_SKIN 0x00200000L #define MUT3_WINGS 0x00400000L #define MUT3_FEARLESS 0x00800000L #define MUT3_REGEN 0x01000000L #define MUT3_ESP 0x02000000L #define MUT3_LIMBER 0x04000000L #define MUT3_ARTHRITIS 0x08000000L #define MUT3_BAD_LUCK 0x10000000L #define MUT3_VULN_ELEM 0x20000000L #define MUT3_MOTION 0x40000000L #define MUT3_GOOD_LUCK 0x80000000L /* * The racial powers */ #define MAX_RACE_POWERS 28 /* Monk martial arts... */ # define MAX_MA 17 # define MA_KNEE 1 # define MA_SLOW 2 /* Mindcrafter */ #define MINDCRAFT_NEURAL_BLAST 0 #define MINDCRAFT_PRECOGNITION 1 #define MINDCRAFT_MINOR_DISPLACEMENT 2 #define MINDCRAFT_MAJOR_DISPLACEMENT 3 #define MINDCRAFT_DOMINATION 4 #define MINDCRAFT_PULVERISE 5 #define MINDCRAFT_CHARACTER_ARMOUR 6 #define MINDCRAFT_PSYCHOMETRY 7 #define MINDCRAFT_MIND_WAVE 8 #define MINDCRAFT_ADRENALINE_CHANNELING 9 #define MINDCRAFT_PSYCHIC_DRAIN 10 #define MINDCRAFT_TELEKINETIC_WAVE 11 #define MINDCRAFT_MAX 12 /* Hallucination stuff */ #define MAX_SILLY_ATTACK 28 /* * Maximum number of high scores in the high score file */ #define MAX_HISCORES 100 /* * Maximum dungeon level. The player can never reach this level * in the dungeon, and this value is used for various calculations * involving object and monster creation. It must be at least 100. * Setting it below 128 may prevent the creation of some objects. */ #define MAX_DEPTH 128 /* * Maximum size of the "view" array (see "cave.c") * Note that the "view radius" will NEVER exceed 20, and even if the "view" * was octagonal, we would never require more than 1520 entries in the array. */ #define VIEW_MAX 1536 /* * Maximum size of the "temp" array (see "cave.c") * We must be as large as "VIEW_MAX" for proper functioning * of "update_view()". We must also be as large as the * largest illuminatable room, but no room is larger than 800 grids. We * must also be large enough to allow "good enough" use as a circular queue, * to calculate monster flow. The larger size is due to use as a circular * queue for the fractal caves patch fill routine. */ #define TEMP_MAX 2000 /* * Maximum number of squares lit by monsters. * (Note that squares far away from the player do not need to be stored.) */ #define LITE_MAX 2500 /* * Maximum number of monsters that can be exploding at once. */ #define DEATH_MAX 100 /* * Number of keymap modes */ #define KEYMAP_MODES 2 /* * Mode for original keyset commands */ #define KEYMAP_MODE_ORIG 0 /* * Mode for roguelike keyset commands */ #define KEYMAP_MODE_ROGUE 1 /* * OPTION: Maximum number of macros (see "utils.c") * Default: assume at most 256 macros are used */ #define MACRO_MAX 256 /* * OPTION: Maximum number of "quarks" (see "utils.c") * Default: assume at most 2048 scripts + names + inscriptions */ #define QUARK_MAX 2048 /* * Threshold for quark list before compacting */ #define QUARK_COMPACT (MAX_SHORT - 1) /* * OPTION: Maximum number of messages to remember (see "utils.c") * Default: assume maximal memorization of 2048 total messages */ #define MESSAGE_MAX 2048 /* * OPTION: Maximum space for the message text buffer (see "utils.c") * Default: assume that each of the 2048 messages is repeated an * average of three times, and has an average length of 48 */ #define MESSAGE_BUF 32768 /* * Maximum value storable in a "byte" (hard-coded) */ #define MAX_UCHAR 255 /* * Maximum value storable in a "s16b" (hard-coded) */ #define MAX_SHORT 32767 /* * Misc constants */ #define TOWN_DAWN 10000 /* Number of turns from dawn to dawn XXX */ #define BREAK_GLYPH 550 /* Rune of protection resistance */ #define BREAK_MINOR_GLYPH 99 /* For explosive runes */ #define BTH_PLUS_ADJ 1 /* Adjust BTH per plus-to-hit */ #define MON_MULT_ADJ 8 /* High value slows multiplication */ #define MON_SUMMON_ADJ 2 /* Adjust level of summoned creatures */ #define MON_DRAIN_LIFE 2 /* Percent of player exp drained per hit */ #define USE_DEVICE 3 /* x> Harder devices x< Easier devices */ /* "Biases" for random artifact gen */ #define BIAS_ELEC 1 #define BIAS_POIS 2 #define BIAS_FIRE 3 #define BIAS_COLD 4 #define BIAS_ACID 5 #define BIAS_STR 6 #define BIAS_INT 7 #define BIAS_WIS 8 #define BIAS_DEX 9 #define BIAS_CON 10 #define BIAS_CHR 11 #define BIAS_CHAOS 12 #define BIAS_PRIESTLY 13 #define BIAS_NECROMANTIC 14 #define BIAS_LAW 15 #define BIAS_ROGUE 16 #define BIAS_MAGE 17 #define BIAS_WARRIOR 18 #define BIAS_RANGER 19 /*** Pet constants ***/ /* * Commands */ #define PET_STAY_CLOSE 0 #define PET_FOLLOW_ME 1 #define PET_SEEK_AND_DESTROY 2 #define PET_ALLOW_SPACE 3 #define PET_STAY_AWAY 4 #define PET_OPEN_DOORS 5 #define PET_TAKE_ITEMS 6 #define PET_INFO 7 #define PET_DISMISS 8 #define PET_CHOICE_MAX 9 /* * Follow distances */ #define PET_CLOSE_DIST 1 #define PET_FOLLOW_DIST 6 #define PET_SEEK_DIST 10 #define PET_DESTROY_DIST 255 #define PET_SPACE_DIST (-10) #define PET_AWAY_DIST (-25) /* * There is a 1/50 (2%) chance of inflating the requested object_level * during the creation of an object (see "get_obj_num()" in "object.c"). * Lower values yield better objects more often. */ #define GREAT_OBJ 50 /* * There is a 1/10 (10%) chance of inflating the level requested in * making an ego item */ #define EGO_INFLATE 10 /* * There is a 1/50 (2%) chance of inflating the requested monster level * during the creation of a monsters (see "get_mon_num()" in "monster.c"). * Lower values yield harder monsters more often. */ #define NASTY_MON 50 /* 1/x chance of hurting even if invulnerable! */ #define PENETRATE_INVULNERABILITY 13 /* Mana drained by getting in a psi hit */ #define PSI_COST 1 /* * Refueling constants */ #define FUEL_TORCH 5000 /* Maximum amount of fuel in a torch */ #define FUEL_LAMP 15000 /* Maximum amount of fuel in a lantern */ /* * More maximum values */ #define MAX_DETECT 30 /* Maximum detection range */ #define MAX_SIGHT 20 /* Maximum view distance */ #define MAX_RANGE 18 /* Maximum range (spells, etc) < MAX_SIGHT */ /* * Max distance the player can be before we redo the flow data */ #define FLOW_DIST_MAX 10 /* * Maximum flow depth when using "MONSTER_FLOW" */ #define MONSTER_FLOW_DEPTH 40 /* * There is a 1/160 chance per round of creating a new monster */ #define MAX_M_ALLOC_CHANCE 160 /* * Normal levels get at least 14 monsters */ #define MIN_M_ALLOC_LEVEL 14 /* * A monster can only "multiply" (reproduce) if there are fewer than 100 * monsters on the level capable of such spontaneous reproduction. This * is a hack which prevents the "m_list[]" array from exploding due to * reproducing monsters. Messy, but necessary. */ #define MAX_REPRO 100 /* * Player constants */ #define PY_MAX_EXP 99999999L /* Maximum exp */ #define PY_MAX_GOLD 999999999L /* Maximum gold */ #define PY_MAX_LEVEL 50 /* Maximum level */ /* * Player "food" crucial values */ #define PY_FOOD_MAX 15000 /* Food value (Bloated) */ #define PY_FOOD_FULL 10000 /* Food value (Normal) */ #define PY_FOOD_ALERT 2000 /* Food value (Hungry) */ #define PY_FOOD_WEAK 1000 /* Food value (Weak) */ #define PY_FOOD_FAINT 500 /* Food value (Fainting) */ #define PY_FOOD_STARVE 100 /* Food value (Starving) */ /* * Player regeneration constants */ #define PY_REGEN_NORMAL 197 /* Regen factor*2^16 when full */ #define PY_REGEN_WEAK 98 /* Regen factor*2^16 when weak */ #define PY_REGEN_FAINT 33 /* Regen factor*2^16 when fainting */ #define PY_REGEN_HPBASE 1442 /* Min amount hp regen*2^16 */ #define PY_REGEN_MNBASE 524 /* Min amount mana regen*2^16 */ /* * Maximum number of players spells */ #define PY_MAX_SPELLS 64 /* * Possible realms that can be chosen; * currently used only by birth.c and tables.c */ #define CH_NONE 0x00 #define CH_LIFE 0x01 #define CH_SORCERY 0x02 #define CH_NATURE 0x04 #define CH_CHAOS 0x08 #define CH_DEATH 0x10 #define CH_TRUMP 0x20 #define CH_ARCANE 0x40 /* * Magic realms */ #define REALM_NONE 0 #define REALM_LIFE 1 #define REALM_SORCERY 2 #define REALM_NATURE 3 #define REALM_CHAOS 4 #define REALM_DEATH 5 #define REALM_TRUMP 6 #define REALM_ARCANE 7 #define MAX_REALM 8 /* * Magic-books for the realms */ #define REALM1_BOOK (p_ptr->spell.r[0].realm + TV_LIFE_BOOK - 1) #define REALM2_BOOK (p_ptr->spell.r[1].realm + TV_LIFE_BOOK - 1) /* * Ego item slot-types */ /* XXX XXX Hack - gap */ #define ES_CROWN 21 #define ES_DIG 22 #define ES_AMMO 23 #define ES_WIELD 24 #define ES_BOW 25 /* Hack - gap */ #define ES_NECK 28 #define ES_LITE 29 #define ES_BODY 30 #define ES_OUTER 31 #define ES_ARM 32 #define ES_HEAD 33 #define ES_HANDS 34 #define ES_FEET 35 /* * Maximum number of "normal" pack slots. * Note that "INVEN_PACK" is probably hard-coded by its use in savefiles, and * by the fact that the screen can only show 23 items plus a one-line prompt. */ #define INVEN_PACK 23 /* * Equipment slots */ #define EQUIP_WIELD 0 #define EQUIP_BOW 1 #define EQUIP_LEFT 2 #define EQUIP_RIGHT 3 #define EQUIP_NECK 4 #define EQUIP_LITE 5 #define EQUIP_BODY 6 #define EQUIP_OUTER 7 #define EQUIP_ARM 8 #define EQUIP_HEAD 9 #define EQUIP_HANDS 10 #define EQUIP_FEET 11 /* * Total number of things that you can wield (hard coded) */ #define EQUIP_MAX 12 /* * A "stack" of items is limited to less than 100 items (hard-coded). */ #define MAX_STACK_SIZE 100 /* * A "stack" of items is limited to less than or equal to 20.0 lbs. */ #define MAX_STACK_WEIGHT 200 /* * Indexes of the various "stats" (hard-coded by savefiles, etc). */ #define A_STR 0 #define A_INT 1 #define A_WIS 2 #define A_DEX 3 #define A_CON 4 #define A_CHR 5 /* * Total number of stats. */ #define A_MAX 6 /* 1/x chance of reducing stats (for elemental attacks) */ #define HURT_CHANCE 50 /* * Player sex constants (hard-coded by save-files, arrays, etc) */ #define SEX_FEMALE 0 #define SEX_MALE 1 /* * Player race constants (hard-coded by save-files, arrays, etc) */ #define RACE_HUMAN 0 #define RACE_HALF_ELF 1 #define RACE_ELF 2 #define RACE_HOBBIT 3 #define RACE_GNOME 4 #define RACE_DWARF 5 #define RACE_HALF_ORC 6 #define RACE_HALF_TROLL 7 #define RACE_AMBERITE 8 #define RACE_HIGH_ELF 9 #define RACE_BARBARIAN 10 #define RACE_HALF_OGRE 11 #define RACE_HALF_GIANT 12 #define RACE_HALF_TITAN 13 #define RACE_CYCLOPS 14 #define RACE_YEEK 15 #define RACE_KLACKON 16 #define RACE_KOBOLD 17 #define RACE_NIBELUNG 18 #define RACE_DARK_ELF 19 #define RACE_DRACONIAN 20 #define RACE_MIND_FLAYER 21 #define RACE_IMP 22 #define RACE_GOLEM 23 #define RACE_SKELETON 24 #define RACE_ZOMBIE 25 #define RACE_VAMPIRE 26 #define RACE_SPECTRE 27 #define RACE_SPRITE 28 #define RACE_BEASTMAN 29 #define RACE_GHOUL 30 /* * Maximum number of player "race" types (see "table.c", etc) */ #define MAX_RACES 31 /* * Player class constants (hard-coded by save-files, arrays, etc) */ #define CLASS_WARRIOR 0 #define CLASS_MAGE 1 #define CLASS_PRIEST 2 #define CLASS_ROGUE 3 #define CLASS_RANGER 4 #define CLASS_PALADIN 5 #define CLASS_WARRIOR_MAGE 6 #define CLASS_CHAOS_WARRIOR 7 #define CLASS_MONK 8 #define CLASS_MINDCRAFTER 9 #define CLASS_HIGH_MAGE 10 /*** Screen Locations ***/ /* * Some screen locations for various display routines */ #define ROW_RACE 1 #define COL_RACE 0 /* */ #define ROW_CLASS 2 #define COL_CLASS 0 /* */ #define ROW_TITLE 3 #define COL_TITLE 0 /* or <mode> */ #define ROW_LEVEL 4 #define COL_LEVEL 0 /* "LEVEL xxxxxx" */ #define ROW_EXP 5 #define COL_EXP 0 /* "EXP xxxxxxxx" */ #define ROW_GOLD 6 #define COL_GOLD 0 /* "AU xxxxxxxxx" */ #define ROW_EQUIPPY 7 #define COL_EQUIPPY 0 /* equippy chars */ #define ROW_STAT 8 #define COL_STAT 0 /* "xxx xxxxxx" */ #define ROW_STATBAR 14 #define COL_STATBAR 0 /* "Status bar" */ #define ROW_AC 15 #define COL_AC 0 /* "Cur AC xxxxx" */ #define ROW_MAXHP 16 #define COL_MAXHP 0 /* "Max HP xxxxx" */ #define ROW_CURHP 17 #define COL_CURHP 0 /* "Cur HP xxxxx" */ #define ROW_MAXSP 18 #define COL_MAXSP 0 /* "Max SP xxxxx" */ #define ROW_CURSP 19 #define COL_CURSP 0 /* "Cur SP xxxxx" */ #define ROW_INFO 20 #define COL_INFO 0 /* "xxxxxxxxxxxx" */ #define ROW_CUT 21 #define COL_CUT 0 /* <cut> */ #define ROW_STUN 22 #define COL_STUN 0 /* <stun> */ #define ROW_MAP 1 #define COL_MAP 13 /* The map of the dungeon */ #define COL_HUNGRY 0 /* "Weak" / "Hungry" / "Full" / "Gorged" */ #define COL_BLIND 7 /* "Blind" */ #define COL_CONFUSED 13 /* "Confused" */ #define COL_AFRAID 22 /* "Afraid" */ #define COL_POISONED 29 /* "Poisoned" */ #define COL_STATE 38 /* <state> */ #define COL_SPEED 45 /* "Slow (-NN)" or "Fast (+NN)" */ #define COL_STUDY 56 /* "Study" */ #define COL_DEPTH 62 /* "Lev NNN" / "NNNN ft" / town name */ #define MAX_EFFECTS 30 /* Max #of player timed effects */ /*** Terrain Feature Indexes (see "lib/edit/f_info.txt") ***/ /* Nothing */ #define FEAT_NONE 0x00 /* Various */ #define FEAT_FLOOR 0x01 /* #define FEAT_INVIS 0x02 Now is a field */ /* #define FEAT_GLYPH 0x03 Now is a field */ #define FEAT_OPEN 0x04 #define FEAT_BROKEN 0x05 #define FEAT_LESS 0x06 #define FEAT_MORE 0x07 /* Passable floors */ #define FEAT_SAND 0x08 #define FEAT_SALT 0x09 #define FEAT_WET_MUD 0x0A #define FEAT_DRY_MUD 0x0B #define FEAT_FLOOR_TILE 0x0C #define FEAT_FLOOR_WOOD 0x0D #define FEAT_PEBBLES 0x0E #define FEAT_SOLID_LAVA 0x0F /* Gap where the traps were */ /* Closed Door */ #define FEAT_CLOSED 0x20 #define FEAT_PILLAR 0x21 /* A big gap for expansion */ /* Extra */ #define FEAT_SECRET 0x30 #define FEAT_RUBBLE 0x31 /* Seams */ #define FEAT_MAGMA 0x32 #define FEAT_QUARTZ 0x33 /* #define FEAT_MAGMA_H 0x34 */ /* #define FEAT_QUARTZ_H 0x35 */ #define FEAT_MAGMA_K 0x36 #define FEAT_QUARTZ_K 0x37 /* Walls */ #define FEAT_WALL_EXTRA 0x38 #define FEAT_WALL_INNER 0x39 #define FEAT_WALL_OUTER 0x3A #define FEAT_WALL_SOLID 0x3B #define FEAT_PERM_EXTRA 0x3C #define FEAT_PERM_INNER 0x3D #define FEAT_PERM_OUTER 0x3E #define FEAT_PERM_SOLID 0x3F /* Gap where Glyph was */ /* Pattern */ #define FEAT_PATTERN_START 0x41 #define FEAT_PATTERN_1 0x42 #define FEAT_PATTERN_2 0x43 #define FEAT_PATTERN_3 0x44 #define FEAT_PATTERN_4 0x45 #define FEAT_PATTERN_END 0x46 #define FEAT_PATTERN_OLD 0x47 #define FEAT_PATTERN_XTRA1 0x48 #define FEAT_PATTERN_XTRA2 0x49 /* Terrains */ #define FEAT_DEEP_WATER 0x53 #define FEAT_SHAL_WATER 0x54 #define FEAT_DEEP_LAVA 0x55 #define FEAT_SHAL_LAVA 0x56 /* Gap */ #define FEAT_DIRT 0x58 #define FEAT_GRASS 0x59 /* Gap */ #define FEAT_OCEAN_WATER 0x5C #define FEAT_DEEP_ACID 0x5D #define FEAT_SHAL_ACID 0x5E #define FEAT_TREE_WATER 0x5F /* Terrain semi-transparent*/ #define FEAT_TREES 0x60 #define FEAT_MOUNTAIN 0x61 #define FEAT_SNOW_MOUNTAIN 0x62 #define FEAT_BOULDER 0x63 #define FEAT_PINE_TREE 0x64 #define FEAT_SNOW_TREE 0x65 #define FEAT_OBELISK 0x66 /* Gap */ /* Impassible terrains */ #define FEAT_FENCE 0x70 #define FEAT_WELL 0x71 #define FEAT_FOUNTAIN 0x72 #define FEAT_JUNGLE 0x73 /* Gap */ /* Slow "floor" terrains */ #define FEAT_BUSH 0x80 #define FEAT_DEAD_BUSH 0x81 #define FEAT_GRASS_LONG 0x82 #define FEAT_ROCK_GEN 0x83 #define FEAT_ROCK_SNOW 0x84 #define FEAT_TREE_GEN 0x85 #define FEAT_TREE_SNOW 0x86 #define FEAT_SNOW 0x87 #define FEAT_DEEP_SWAMP 0x88 #define FEAT_SHAL_SWAMP 0x89 /*** Wilderness Info flags - (see "wild.c") ***/ #define WILD_INFO_TRACK 0x01 #define WILD_INFO_ROAD 0x02 #define WILD_INFO_WATER 0x04 #define WILD_INFO_LAVA 0x08 #define WILD_INFO_DUMMY 0x10 #define WILD_INFO_SEEN 0x20 #define WILD_INFO_ACID 0x40 #define WILD_INFO_QUEST 0x80 /* Types of "liquid" for dungeon */ #define LQ_NONE 0x00 #define LQ_WATER 0x01 #define LQ_LAVA 0x02 #define LQ_ACID 0x04 #define LQ_SWAMP 0x08 #define LQ_MAX 4 /* Room types */ #define RT_TAG_CROWDED 0x0001 /* Affects "crowded" counter */ #define RT_NATURAL 0x0002 #define RT_ANIMAL 0x0004 #define RT_COMPLEX 0x0008 #define RT_DENSE 0x0010 #define RT_RUIN 0x0020 #define RT_SIMPLE 0x0040 #define RT_BUILDING 0x0080 #define RT_CRYPT 0x0100 #define RT_RVAULT 0x0200 #define RT_STRANGE 0x0400 #define RT_FANCY 0x0800 /*** Field Thaumatergical types - (see "fields.c" and t_info.txt) ***/ #define FT_NONE 0x0000 #define FT_WALL_INVIS 0x0001 #define FT_GLYPH_WARDING 0x0002 #define FT_GLYPH_EXPLODE 0x0003 #define FT_CORPSE 0x0004 #define FT_SKELETON 0x0005 #define FT_TRAP_DOOR 0x0006 #define FT_TRAP_PIT 0x0007 #define FT_TRAP_SPIKE_PIT 0x0008 #define FT_TRAP_POISON_PIT 0x0009 #define FT_TRAP_CURSE 0x000A #define FT_TRAP_TELEPORT 0x000B #define FT_TRAP_ELEMENT 0x000C #define FT_TRAP_BA_ELEMENT 0x000D #define FT_TRAP_GAS 0x000E #define FT_TRAP_TRAPS 0x000F #define FT_TRAP_TEMP_STAT 0x0010 #define FT_TRAP_PERM_STAT 0x0011 #define FT_TRAP_LOSE_XP 0x0012 #define FT_TRAP_DISENCHANT 0x0013 #define FT_TRAP_DROP_ITEM 0x0014 #define FT_TRAP_MUTATE 0x0015 #define FT_TRAP_NEW_LIFE 0x0016 #define FT_TRAP_NO_LITE 0x0017 #define FT_TRAP_HUNGER 0x0018 #define FT_TRAP_NO_GOLD 0x0019 #define FT_TRAP_HASTE_MON 0x001A #define FT_TRAP_RAISE_MON 0x001B #define FT_TRAP_DRAIN_MAGIC 0x001C #define FT_TRAP_AGGRAVATE 0x001D #define FT_TRAP_SUMMON 0x001E #define FT_TRAP_LOSE_MEMORY 0x001F #define FT_LOCK_DOOR 0x0020 #define FT_JAM_DOOR 0x0021 #define FT_STORE_GENERAL 0x0022 #define FT_STORE_ARMOURY 0x0023 #define FT_STORE_WEAPON 0x0024 #define FT_STORE_TEMPLE 0x0025 #define FT_STORE_ALCHEMIST 0x0026 #define FT_STORE_MAGIC 0x0027 #define FT_STORE_BLACK 0x0028 #define FT_STORE_HOME 0x0029 #define FT_STORE_BOOK 0x002A #define FT_BUILD_WEAPON 0x002B #define FT_BUILD_RECHARGE 0x002C #define FT_BUILD_PLUS_WEAPON 0x002D #define FT_BUILD_PLUS_ARMOUR 0x002E #define FT_BUILD_MUTATE 0x002F #define FT_BUILD_MAP 0x0030 #define FT_STORE_WEAPON1 0x0031 #define FT_STORE_WEAPON2 0x0032 #define FT_STORE_WEAPON3 0x0033 #define FT_STORE_WEAPON4 0x0034 #define FT_STORE_WEAPON5 0x0035 #define FT_STORE_ARMOUR1 0x0036 #define FT_STORE_ARMOUR2 0x0037 #define FT_STORE_ARMOUR3 0x0038 #define FT_STORE_ARMOUR4 0x0039 #define FT_STORE_ARMOUR5 0x003A #define FT_STORE_SWORD0 0x003B #define FT_STORE_SWORD1 0x003C #define FT_STORE_SWORD2 0x003D #define FT_STORE_SWORD3 0x003E #define FT_STORE_SWORD4 0x003F #define FT_STORE_SWORD5 0x0040 #define FT_STORE_SHIELD0 0x0041 #define FT_STORE_SHIELD1 0x0042 #define FT_STORE_SHIELD2 0x0043 #define FT_STORE_SHIELD3 0x0044 #define FT_STORE_SHIELD4 0x0045 #define FT_STORE_SHIELD5 0x0046 #define FT_STORE_AXE0 0x0047 #define FT_STORE_AXE1 0x0048 #define FT_STORE_AXE2 0x0049 #define FT_STORE_AXE3 0x004A #define FT_STORE_AXE4 0x004B #define FT_STORE_AXE5 0x004C #define FT_STORE_AMMO0 0x004D #define FT_STORE_AMMO1 0x004E #define FT_STORE_AMMO2 0x004F #define FT_STORE_FLET0 0x0050 #define FT_STORE_FLET1 0x0051 #define FT_STORE_FLET2 0x0052 #define FT_STORE_FLET3 0x0053 #define FT_STORE_WARHALL0 0x0054 #define FT_STORE_WARHALL1 0x0055 #define FT_STORE_WARHALL2 0x0056 #define FT_STORE_WARHALL3 0x0057 #define FT_STORE_WARHALL4 0x0058 #define FT_STORE_WARHALL5 0x0059 #define FT_STORE_CLOTH0 0x005A #define FT_STORE_CLOTH1 0x005B #define FT_STORE_HARMOUR0 0x005C #define FT_STORE_HARMOUR1 0x005D #define FT_STORE_HARMOUR2 0x005E #define FT_STORE_HARMOUR3 0x005F #define FT_STORE_HARMOUR4 0x0060 #define FT_STORE_HARMOUR5 0x0061 #define FT_STORE_HAT0 0x0062 #define FT_STORE_HAT1 0x0063 #define FT_STORE_HAT2 0x0064 #define FT_STORE_HAT3 0x0065 #define FT_STORE_JEWEL0 0x0066 #define FT_STORE_JEWEL1 0x0067 #define FT_STORE_JEWEL2 0x0068 #define FT_STORE_JEWEL3 0x0069 #define FT_STORE_JEWEL4 0x006A #define FT_STORE_STATUE0 0x006B #define FT_STORE_STATUE1 0x006C #define FT_STORE_FIGUR0 0x006D #define FT_STORE_FIGUR1 0x006E #define FT_STORE_POTION0 0x006F #define FT_STORE_POTION1 0x0070 #define FT_STORE_POTION2 0x0071 #define FT_STORE_POTION3 0x0072 #define FT_STORE_POTION4 0x0073 #define FT_STORE_SCROLL0 0x0074 #define FT_STORE_SCROLL1 0x0075 #define FT_STORE_SCROLL2 0x0076 #define FT_STORE_SCROLL3 0x0077 #define FT_STORE_SCROLL4 0x0078 #define FT_STORE_MAGIC0 0x0079 #define FT_STORE_MAGIC1 0x007A #define FT_STORE_MAGIC2 0x007B #define FT_STORE_MAGIC3 0x007C #define FT_STORE_MAGIC4 0x007D #define FT_STORE_BOOK1 0x007E #define FT_STORE_TEMPLE1 0x007F #define FT_STORE_TEMPLE2 0x0080 #define FT_STORE_TEMPLE3 0x0081 #define FT_STORE_SUPPLIES0 0x0082 #define FT_STORE_SUPPLIES1 0x0083 #define FT_STORE_BLACK1 0x0084 #define FT_STORE_BLACK2 0x0085 #define FT_STORE_ALCHEMY1 0x0086 #define FT_STORE_ALCHEMY2 0x0087 #define FT_STORE_JUNK 0x0088 #define FT_STORE_FOOD 0x0089 #define FT_BUILD_LIBRARY 0x008A #define FT_BUILD_CASINO 0x008B #define FT_BUILD_INN 0x008C #define FT_BUILD_HEALER 0x008D #define FT_STORE_BLACK0 0x008E #define FT_BUILD_MAGETOWER0 0x008F #define FT_BUILD_MAGETOWER1 0x0090 #define FT_BUILD_CASTLE0 0x0091 #define FT_BUILD_CASTLE1 0x0092 /*** Artifact indexes (see "lib/edit/a_info.txt") ***/ /* The maximum "special" artifact index */ #define MAX_ART_SPECIAL 15 /* Lites */ #define ART_GALADRIEL 1 #define ART_ELENDIL 2 #define ART_THRAIN 3 /* Amulets */ #define ART_CARLAMMAS 4 #define ART_INGWE 5 #define ART_DWARVES 6 /* Rings */ #define ART_BARAHIR 8 #define ART_TULKAS 9 #define ART_NARYA 10 #define ART_NENYA 11 #define ART_VILYA 12 #define ART_POWER 13 #define ART_ELEMENTS 14 /* Dragon Scale */ #define ART_RAZORBACK 16 #define ART_BLADETURNER 17 /* Robe */ #define ART_THAUMATURGIST 18 /* Hard Armour */ #define ART_SOULKEEPER 19 #define ART_ISILDUR 20 #define ART_ROHIRRIM 21 #define ART_BELEGENNON 22 #define ART_CELEBORN 23 #define ART_ARVEDUI 24 #define ART_CASPANION 25 #define ART_NEMOVEBLA 26 /* Soft Armour */ #define ART_HITHLOMIR 27 #define ART_THALKETTOTH 28 /* Gap */ /* Shields */ #define ART_THORIN 30 #define ART_CELEGORM 31 #define ART_ANARION 32 /* Helms and Crowns */ #define ART_MORGOTH 34 #define ART_BERUTHIEL 35 #define ART_THRANDUIL 36 #define ART_THENGEL 37 #define ART_HAMMERHAND 38 #define ART_DOR 39 #define ART_HOLHENNETH 40 #define ART_TERROR 41 #define ART_GONDOR 42 /* Cloaks */ #define ART_KERI 43 #define ART_COLLUIN 44 #define ART_HOLCOLLETH 45 #define ART_THINGOL 46 #define ART_THORONGIL 47 #define ART_COLANNON 48 #define ART_LUTHIEN 49 #define ART_TUOR 50 /* Gloves */ #define ART_CAMBELEG 52 #define ART_CAMMITHRIM 53 #define ART_PAURHACH 54 #define ART_CORWIN 55 #define ART_PAURAEGEN 56 #define ART_PAURNEN 57 #define ART_CAMLOST 58 #define ART_FINGOLFIN 59 /* Boots */ #define ART_FEANOR 60 #define ART_DAL 61 #define ART_THROR 62 /* Swords */ #define ART_MAEDHROS 64 #define ART_ANGRIST 65 #define ART_NARTHANC 66 #define ART_NIMTHANC 67 #define ART_DETHANC 68 #define ART_RILIA 69 #define ART_BELANGIL 70 #define ART_CALRIS 71 #define ART_GRAYSWANDIR 72 #define ART_GLAMDRING 73 #define ART_AEGLIN 74 #define ART_ORCRIST 75 #define ART_GURTHANG 76 #define ART_ZARCUTHRA 77 #define ART_MORMEGIL 78 #define ART_GONDRICAM 79 #define ART_CRISDURIAN 80 #define ART_GROO 81 #define ART_RINGIL 82 #define ART_ANDURIL 83 #define ART_ANGUIREL 84 #define ART_CHAINSWORD 85 #define ART_FORASGIL 86 #define ART_CARETH 87 #define ART_STING 88 #define ART_HARADEKKET 89 #define ART_GILETTAR 90 #define ART_DOOMCALLER 91 #define ART_VORPAL_BLADE 92 /* Polearms */ #define ART_THEODEN 93 #define ART_PAIN 94 #define ART_OSONDIR 95 #define ART_TIL 96 #define ART_AEGLOS 97 #define ART_OROME 98 #define ART_NIMLOTH 99 #define ART_SOULSUCKER 100 #define ART_DURIN 101 #define ART_EONWE 102 #define ART_BALLI 103 #define ART_LOTHARANG 104 #define ART_MUNDWINE 105 #define ART_BARUKKHELED 106 #define ART_WRATH 107 #define ART_ULMO 108 #define ART_AVAVIR 109 /* The sword of the Dawn */ #define ART_DAWN 110 /* Hafted */ #define ART_GROND 111 #define ART_TOTILA 112 #define ART_THUNDERFIST 113 #define ART_WHIRLWIND 114 #define ART_FIRESTAR 115 #define ART_ENERGY 116 #define ART_AULE 117 #define ART_NAR 118 #define ART_ERIRIL 119 #define ART_OLORIN 120 #define ART_DEATHWREAKER 121 #define ART_TURMIL 122 /* Sling */ #define ART_CATAPULT 123 /* Bows */ #define ART_BELTHRONDING 124 #define ART_BARD 125 #define ART_BRAND 126 #define ART_MARKSMAN 127 /* New artifacts */ #define ART_STORMBRINGER 130 /*** Ego-Item indexes (see "lib/edit/e_info.txt") ***/ /* Nothing */ /* xxx */ /* xxx */ /* xxx */ /* Body Armor */ #define EGO_RESIST_ACID 4 #define EGO_RESIST_ELEC 5 #define EGO_RESIST_FIRE 6 #define EGO_RESIST_COLD 7 #define EGO_RESISTANCE 8 #define EGO_ELVENKIND 9 /* xxx */ #define EGO_PERMANENCE 11 /* Lites */ #define EGO_EVERBURN 12 #define EGO_VISION 13 #define EGO_WARMTH 14 #define EGO_SEARCH 15 /* Shields */ #define EGO_ENDURE_ACID 16 #define EGO_ENDURE_ELEC 17 #define EGO_ENDURE_FIRE 18 #define EGO_ENDURE_COLD 19 #define EGO_ENDURANCE 20 #define EGO_REFLECTION 21 /* xxx */ /* xxx */ /* Crowns and Helms */ #define EGO_INTELLIGENCE 24 #define EGO_WISDOM 25 #define EGO_BEAUTY 26 #define EGO_MAGI 27 #define EGO_MIGHT 28 #define EGO_LORDLINESS 29 #define EGO_SEEING 30 #define EGO_INFRAVISION 31 #define EGO_LITE 32 #define EGO_TELEPATHY 33 #define EGO_REGENERATION 34 #define EGO_TELEPORTATION 35 #define EGO_STUPIDITY 36 #define EGO_NAIVETY 37 #define EGO_UGLINESS 38 #define EGO_SICKLINESS 39 /* Cloaks */ #define EGO_PROTECTION 40 #define EGO_STEALTH 41 #define EGO_AMAN 42 #define EGO_AURA_FIRE 43 #define EGO_ENVELOPING 44 #define EGO_VULNERABILITY 45 #define EGO_IRRITATION 46 #define EGO_AURA_ELEC 47 /* Gloves */ #define EGO_FREE_ACTION 48 #define EGO_SLAYING 49 #define EGO_AGILITY 50 #define EGO_POWER 51 #define EGO_GHOUL_TOUCH 52 /* xxx */ #define EGO_WEAKNESS 54 #define EGO_CLUMSINESS 55 /* Boots */ #define EGO_SLOW_DESCENT 56 #define EGO_QUIET 57 #define EGO_MOTION 58 #define EGO_SPEED 59 /* xxx */ #define EGO_NOISE 61 #define EGO_SLOWNESS 62 #define EGO_ANNOYANCE 63 /* Weapons */ #define EGO_HA 64 #define EGO_DF 65 #define EGO_BLESS_BLADE 66 /* xxx */ #define EGO_WEST 68 #define EGO_ATTACKS 69 #define EGO_SLAYING_WEAPON 70 /* xxx */ #define EGO_BRAND_ACID 72 #define EGO_BRAND_ELEC 73 #define EGO_BRAND_FIRE 74 #define EGO_BRAND_COLD 75 #define EGO_BRAND_POIS 76 #define EGO_CHAOTIC 77 #define EGO_SHARPNESS 78 #define EGO_EARTHQUAKES 79 #define EGO_SLAY_ANIMAL 80 #define EGO_SLAY_EVIL 81 #define EGO_SLAY_UNDEAD 82 #define EGO_SLAY_DEMON 83 #define EGO_SLAY_ORC 84 #define EGO_SLAY_TROLL 85 #define EGO_SLAY_GIANT 86 #define EGO_SLAY_DRAGON 87 #define EGO_KILL_ANIMAL 88 #define EGO_KILL_EVIL 89 #define EGO_KILL_UNDEAD 90 #define EGO_KILL_DEMON 91 #define EGO_KILL_ORC 92 #define EGO_KILL_TROLL 93 #define EGO_KILL_GIANT 94 #define EGO_KILL_DRAGON 95 #define EGO_VAMPIRIC 96 /* xxx */ /* xxx */ #define EGO_TRUMP 98 #define EGO_PATTERN 99 #define EGO_DIGGING 100 /* xxx */ #define EGO_MORGUL 102 /* xxx */ /* Bows */ #define EGO_ACCURACY 104 #define EGO_VELOCITY 105 /* xxx */ /* xxx */ #define EGO_EXTRA_MIGHT 108 #define EGO_EXTRA_SHOTS 109 /* xxx */ /* xxx */ /* Ammo */ #define EGO_HURT_ANIMAL 112 #define EGO_HURT_EVIL 113 /* xxx */ /* xxx */ /* xxx */ #define EGO_RETURNING 117 #define EGO_EXPLOSION 118 #define EGO_HURT_DRAGON 119 #define EGO_SLAYING_BOLT 120 #define EGO_LIGHTNING_BOLT 121 #define EGO_FLAME 122 #define EGO_FROST 123 #define EGO_WOUNDING 124 #define EGO_BACKBITING 125 #define EGO_SHATTERED 126 #define EGO_BLASTED 127 /*** Object "tval" and "sval" codes ***/ /* * The values for the "tval" field of various objects. * * This value is the primary means by which items are sorted in the * player inventory, followed by "sval" and "cost". * * Note that as of 2.7.8, the "item flags" apply to all items, though * only armor and weapons and a few other items use any of these flags. */ #define TV_ANY 0 /* Used for matching all objects */ #define TV_SKELETON 1 /* Skeletons ('~') */ #define TV_BOTTLE 2 /* Empty bottles ('!') */ #define TV_JUNK 3 /* Sticks, Pottery, etc ('~') */ #define TV_SPIKE 5 /* Spikes ('~') */ #define TV_CHEST 7 /* Chests ('&') */ #define TV_FIGURINE 8 /* Magical figurines */ #define TV_STATUE 9 /* Statue */ /*#define TV_CORPSE 10 */ /* Corpses are now fields */ #define TV_SHOT 16 /* Ammo for slings */ #define TV_ARROW 17 /* Ammo for bows */ #define TV_BOLT 18 /* Ammo for x-bows */ #define TV_BOW 19 /* Slings/Bows/Xbows */ #define TV_DIGGING 20 /* Shovels/Picks */ #define TV_HAFTED 21 /* Priest Weapons */ #define TV_POLEARM 22 /* Axes and Pikes */ #define TV_SWORD 23 /* Edged Weapons */ #define TV_BOOTS 30 /* Boots */ #define TV_GLOVES 31 /* Gloves */ #define TV_HELM 32 /* Helms */ #define TV_CROWN 33 /* Crowns */ #define TV_SHIELD 34 /* Shields */ #define TV_CLOAK 35 /* Cloaks */ #define TV_SOFT_ARMOR 36 /* Soft Armor */ #define TV_HARD_ARMOR 37 /* Hard Armor */ #define TV_DRAG_ARMOR 38 /* Dragon Scale Mail */ #define TV_LITE 39 /* Lites (including Specials) */ #define TV_AMULET 40 /* Amulets (including Specials) */ #define TV_RING 45 /* Rings (including Specials) */ #define TV_STAFF 55 #define TV_WAND 65 #define TV_ROD 66 #define TV_SCROLL 70 #define TV_POTION 75 #define TV_FLASK 77 #define TV_FOOD 80 #define TV_LIFE_BOOK 90 #define TV_SORCERY_BOOK 91 #define TV_NATURE_BOOK 92 #define TV_CHAOS_BOOK 93 #define TV_DEATH_BOOK 94 #define TV_TRUMP_BOOK 95 #define TV_ARCANE_BOOK 96 #define TV_GOLD 100 /* Gold can only be picked up by players */ #define TV_BOOKS_MIN TV_LIFE_BOOK /* First tval of spellbooks */ #define TV_BOOKS_MAX TV_ARCANE_BOOK /* Last tval of spellbooks */ /* Any subvalue */ #define SV_ANY 255 /* The "sval" codes for TV_FIGURINE */ #define SV_FIGURINE_NORMAL 0 /* The "sval" codes for TV_STATUE */ #define SV_WOODEN_STATUE 0 #define SV_CLAY_STATUE 1 #define SV_STONE_STATUE 2 #define SV_IRON_STATUE 3 #define SV_COPPER_STATUE 4 #define SV_SILVER_STATUE 5 #define SV_GOLDEN_STATUE 6 #define SV_IVORY_STATUE 7 #define SV_MITHRIL_STATUE 8 #define SV_ORNATE_STATUE 9 /* The "sval" codes for TV_SHOT/TV_ARROW/TV_BOLT */ #define SV_AMMO_LIGHT 0 /* pebbles, flight arrows */ #define SV_AMMO_NORMAL 1 /* shots, arrows, bolts */ #define SV_AMMO_HEAVY 2 /* sheaf + seeker arrows and bolts, mithril shots */ /* The "sval" codes for TV_BOW (note information in "sval") */ #define SV_SLING 2 /* (x2) */ #define SV_SHORT_BOW 12 /* (x2) */ #define SV_LONG_BOW 13 /* (x3 or x2) */ #define SV_LIGHT_XBOW 23 /* (x4) */ #define SV_HEAVY_XBOW 24 /* (x5) */ /* The "sval" codes for TV_DIGGING */ #define SV_SHOVEL 1 #define SV_GNOMISH_SHOVEL 2 #define SV_DWARVEN_SHOVEL 3 #define SV_PICK 4 #define SV_ORCISH_PICK 5 #define SV_DWARVEN_PICK 6 #define SV_MATTOCK 7 /* * Do not put the damage dice of the weapons or the AC of armour here. * The values rapidly diverge from what is in k_info.txt * as they are tweaked. */ /* The "sval" values for TV_HAFTED */ #define SV_CLUB 1 #define SV_WHIP 2 #define SV_QUARTERSTAFF 3 #define SV_NUNCHAKU 4 #define SV_MACE 5 #define SV_BALL_AND_CHAIN 6 #define SV_JO_STAFF 7 #define SV_WAR_HAMMER 8 #define SV_THREE_PIECE_ROD 11 #define SV_MORNING_STAR 12 #define SV_FLAIL 13 #define SV_BO_STAFF 14 #define SV_LEAD_FILLED_MACE 15 #define SV_TETSUBO 16 #define SV_TWO_HANDED_FLAIL 18 #define SV_GREAT_HAMMER 19 #define SV_MACE_OF_DISRUPTION 20 #define SV_WHIP_OF_ENTANGLEMENT 21 #define SV_HAMMER_OF_RETURNING 22 #define SV_GROND 50 /* The "sval" values for TV_POLEARM */ #define SV_HATCHET 1 #define SV_SPEAR 2 #define SV_SICKLE 3 #define SV_AWL_PIKE 4 #define SV_TRIDENT 5 #define SV_FAUCHARD 6 #define SV_BROAD_SPEAR 7 #define SV_PIKE 8 #define SV_NAGINATA 9 #define SV_BEAKED_AXE 10 #define SV_BROAD_AXE 11 #define SV_LUCERNE_HAMMER 12 #define SV_GLAIVE 13 #define SV_LAJATANG 14 #define SV_HALBERD 15 #define SV_GUISARME 16 #define SV_SCYTHE 17 #define SV_LANCE 20 #define SV_BATTLE_AXE 22 #define SV_GREAT_AXE 25 #define SV_TRIFURCATE_SPEAR 26 #define SV_LOCHABER_AXE 28 #define SV_HEAVY_LANCE 29 #define SV_SCYTHE_OF_SLICING 30 /* The "sval" codes for TV_SWORD */ #define SV_BROKEN_DAGGER 1 #define SV_BROKEN_SWORD 2 #define SV_DAGGER 4 #define SV_MAIN_GAUCHE 5 #define SV_TANTO 6 #define SV_RAPIER 7 #define SV_SMALL_SWORD 8 #define SV_BASILLARD 9 #define SV_SHORT_SWORD 10 #define SV_SABRE 11 #define SV_CUTLASS 12 #define SV_WAKIZASHI 13 #define SV_KHOPESH 14 #define SV_TULWAR 15 #define SV_BROAD_SWORD 16 #define SV_LONG_SWORD 17 #define SV_SCIMITAR 18 #define SV_NINJATO 19 #define SV_KATANA 20 #define SV_BASTARD_SWORD 21 #define SV_GREAT_SCIMITAR 22 #define SV_CLAYMORE 23 #define SV_ESPADON 24 #define SV_TWO_HANDED_SWORD 25 #define SV_FLAMBERGE 26 #define SV_NO_DACHI 27 #define SV_EXECUTIONERS_SWORD 28 #define SV_ZWEIHANDER 29 #define SV_BLADE_OF_CHAOS 30 #define SV_DIAMOND_EDGE 31 #define SV_ELFBLADE 32 #define SV_PSIBLADE 33 /* The "sval" codes for TV_SHIELD */ #define SV_SMALL_LEATHER_SHIELD 2 #define SV_SMALL_METAL_SHIELD 3 #define SV_LARGE_LEATHER_SHIELD 4 #define SV_LARGE_METAL_SHIELD 5 #define SV_DRAGON_SHIELD 6 #define SV_SHIELD_OF_DEFLECTION 10 /* The "sval" codes for TV_HELM */ #define SV_HARD_LEATHER_CAP 2 #define SV_METAL_CAP 3 #define SV_JINGASA 4 #define SV_IRON_HELM 5 #define SV_STEEL_HELM 6 #define SV_DRAGON_HELM 7 #define SV_KABUTO 8 #define SV_IRON_CROWN 10 #define SV_GOLDEN_CROWN 11 #define SV_JEWELED_CROWN 12 #define SV_MORGOTH 50 /* The "sval" codes for TV_BOOTS */ #define SV_PAIR_OF_SOFT_LEATHER_BOOTS 2 #define SV_PAIR_OF_HARD_LEATHER_BOOTS 3 #define SV_PAIR_OF_METAL_SHOD_BOOTS 6 /* The "sval" codes for TV_CLOAK */ #define SV_CLOAK 1 #define SV_ELVEN_CLOAK 2 #define SV_FUR_CLOAK 3 #define SV_SHADOW_CLOAK 6 /* The "sval" codes for TV_GLOVES */ #define SV_SET_OF_LEATHER_GLOVES 1 #define SV_SET_OF_GAUNTLETS 2 #define SV_SET_OF_CESTI 5 /* The "sval" codes for TV_SOFT_ARMOR */ #define SV_T_SHIRT 0 #define SV_FILTHY_RAG 1 #define SV_ROBE 2 #define SV_PAPER_ARMOR 3 #define SV_SOFT_LEATHER_ARMOR 4 #define SV_SOFT_STUDDED_LEATHER 5 #define SV_HARD_LEATHER_ARMOR 6 #define SV_HARD_STUDDED_LEATHER 7 #define SV_RHINO_HIDE_ARMOR 8 #define SV_CORD_ARMOR 9 #define SV_PADDED_ARMOR 10 #define SV_LEATHER_SCALE_MAIL 11 #define SV_LEATHER_JACK 12 #define SV_STONE_AND_HIDE_ARMOR 15 #define SV_HAGAROMO 16 /* The "sval" codes for TV_HARD_ARMOR */ #define SV_RUSTY_CHAIN_MAIL 1 #define SV_RING_MAIL 2 #define SV_METAL_SCALE_MAIL 3 #define SV_CHAIN_MAIL 4 #define SV_DOUBLE_RING_MAIL 5 #define SV_AUGMENTED_CHAIN_MAIL 6 #define SV_DOUBLE_CHAIN_MAIL 7 #define SV_BAR_CHAIN_MAIL 8 #define SV_METAL_BRIGANDINE_ARMOUR 9 #define SV_SPLINT_MAIL 10 #define SV_DO_MARU 11 #define SV_PARTIAL_PLATE_ARMOUR 12 #define SV_METAL_LAMELLAR_ARMOUR 13 #define SV_HARAMAKIDO 14 #define SV_FULL_PLATE_ARMOUR 15 #define SV_O_YOROI 16 #define SV_RIBBED_PLATE_ARMOUR 18 #define SV_MITHRIL_CHAIN_MAIL 20 #define SV_MITHRIL_PLATE_MAIL 25 #define SV_ADAMANTITE_PLATE_MAIL 30 /* The "sval" codes for TV_DRAG_ARMOR */ #define SV_DRAGON_BLACK 1 #define SV_DRAGON_BLUE 2 #define SV_DRAGON_WHITE 3 #define SV_DRAGON_RED 4 #define SV_DRAGON_GREEN 5 #define SV_DRAGON_MULTIHUED 6 #define SV_DRAGON_SHINING 10 #define SV_DRAGON_LAW 12 #define SV_DRAGON_BRONZE 14 #define SV_DRAGON_GOLD 16 #define SV_DRAGON_CHAOS 18 #define SV_DRAGON_BALANCE 20 #define SV_DRAGON_POWER 30 /* The sval codes for TV_LITE */ #define SV_LITE_TORCH 0 #define SV_LITE_LANTERN 1 #define SV_LITE_GALADRIEL 4 #define SV_LITE_ELENDIL 5 #define SV_LITE_THRAIN 6 /* The "sval" codes for TV_AMULET */ #define SV_AMULET_DOOM 0 #define SV_AMULET_TELEPORT 1 #define SV_AMULET_BERSERK 2 #define SV_AMULET_SLOW_DIGEST 3 #define SV_AMULET_RESIST_ACID 4 #define SV_AMULET_SEARCHING 5 #define SV_AMULET_WISDOM 6 #define SV_AMULET_CHARISMA 7 #define SV_AMULET_THE_MAGI 8 #define SV_AMULET_REFLECTION 9 #define SV_AMULET_CARLAMMAS 10 #define SV_AMULET_INGWE 11 #define SV_AMULET_DWARVES 12 #define SV_AMULET_NO_MAGIC 13 #define SV_AMULET_NO_TELE 14 #define SV_AMULET_RESISTANCE 15 #define SV_AMULET_PROT_EVIL 16 #define SV_AMULET_PROT_UNDEAD 17 #define SV_AMULET_LUCK 18 #define SV_AMULET_SUSTENANCE 19 /* The sval codes for TV_RING */ #define SV_RING_WOE 0 #define SV_RING_AGGRAVATION 1 #define SV_RING_WEAKNESS 2 #define SV_RING_STUPIDITY 3 #define SV_RING_TELEPORTATION 4 #define SV_RING_SLOW_DIGESTION 6 #define SV_RING_FEATHER_FALL 7 #define SV_RING_RESIST_FIRE 8 #define SV_RING_RESIST_COLD 9 #define SV_RING_SUSTAIN_STR 10 #define SV_RING_SUSTAIN_INT 11 #define SV_RING_SUSTAIN_WIS 12 #define SV_RING_SUSTAIN_DEX 13 #define SV_RING_SUSTAIN_CON 14 #define SV_RING_SUSTAIN_CHR 15 #define SV_RING_PROTECTION 16 #define SV_RING_ACID 17 #define SV_RING_FLAMES 18 #define SV_RING_ICE 19 #define SV_RING_RESIST_POIS 20 #define SV_RING_FREE_ACTION 21 #define SV_RING_SEE_INVIS 22 #define SV_RING_SEARCHING 23 #define SV_RING_STR 24 #define SV_RING_INT 25 #define SV_RING_DEX 26 #define SV_RING_CON 27 #define SV_RING_ACCURACY 28 #define SV_RING_DAMAGE 29 #define SV_RING_SLAYING 30 #define SV_RING_SPEED 31 #define SV_RING_BARAHIR 32 #define SV_RING_TULKAS 33 #define SV_RING_NARYA 34 #define SV_RING_NENYA 35 #define SV_RING_VILYA 36 #define SV_RING_POWER 37 #define SV_RING_RES_FEAR 38 #define SV_RING_RES_LD 39 #define SV_RING_RES_NETHER 40 #define SV_RING_RES_NEXUS 41 #define SV_RING_RES_SOUND 42 #define SV_RING_RES_CONFUSION 43 #define SV_RING_RES_SHARDS 44 #define SV_RING_RES_DISENCHANT 45 #define SV_RING_RES_CHAOS 46 #define SV_RING_RES_BLINDNESS 47 #define SV_RING_LORDLY 48 #define SV_RING_ATTACKS 49 #define SV_RING_ELEMENTS 50 #define SV_RING_RES_FIRE_COLD 51 #define SV_RING_CAT 52 #define SV_RING_FATE 53 #define SV_RING_WIZARDRY 54 /* The "sval" codes for TV_STAFF */ #define SV_STAFF_DARKNESS 0 #define SV_STAFF_SLOWNESS 1 #define SV_STAFF_HASTE_MONSTERS 2 #define SV_STAFF_SUMMONING 3 #define SV_STAFF_TELEPORTATION 4 #define SV_STAFF_IDENTIFY 5 #define SV_STAFF_REMOVE_CURSE 6 #define SV_STAFF_STARLITE 7 #define SV_STAFF_LITE 8 #define SV_STAFF_MAPPING 9 #define SV_STAFF_DETECT_GOLD 10 #define SV_STAFF_DETECT_ITEM 11 #define SV_STAFF_DETECT_TRAP 12 #define SV_STAFF_DETECT_DOOR 13 #define SV_STAFF_DETECT_INVIS 14 #define SV_STAFF_DETECT_EVIL 15 #define SV_STAFF_CURE_LIGHT 16 #define SV_STAFF_CURING 17 #define SV_STAFF_HEALING 18 #define SV_STAFF_THE_MAGI 19 #define SV_STAFF_SLEEP_MONSTERS 20 #define SV_STAFF_SLOW_MONSTERS 21 #define SV_STAFF_SPEED 22 #define SV_STAFF_PROBING 23 #define SV_STAFF_DISPEL_EVIL 24 #define SV_STAFF_POWER 25 #define SV_STAFF_HOLINESS 26 #define SV_STAFF_GENOCIDE 27 #define SV_STAFF_EARTHQUAKES 28 #define SV_STAFF_DESTRUCTION 29 /* The "sval" codes for TV_WAND */ #define SV_WAND_HEAL_MONSTER 0 #define SV_WAND_HASTE_MONSTER 1 #define SV_WAND_CLONE_MONSTER 2 #define SV_WAND_TELEPORT_AWAY 3 #define SV_WAND_DISARMING 4 #define SV_WAND_TRAP_DOOR_DEST 5 #define SV_WAND_STONE_TO_MUD 6 #define SV_WAND_LITE 7 #define SV_WAND_SLEEP_MONSTER 8 #define SV_WAND_SLOW_MONSTER 9 #define SV_WAND_CONFUSE_MONSTER 10 #define SV_WAND_FEAR_MONSTER 11 #define SV_WAND_DRAIN_LIFE 12 #define SV_WAND_POLYMORPH 13 #define SV_WAND_STINKING_CLOUD 14 #define SV_WAND_MAGIC_MISSILE 15 #define SV_WAND_ACID_BOLT 16 #define SV_WAND_CHARM_MONSTER 17 #define SV_WAND_FIRE_BOLT 18 #define SV_WAND_COLD_BOLT 19 #define SV_WAND_ACID_BALL 20 #define SV_WAND_ELEC_BALL 21 #define SV_WAND_FIRE_BALL 22 #define SV_WAND_COLD_BALL 23 #define SV_WAND_WONDER 24 #define SV_WAND_ANNIHILATION 25 #define SV_WAND_DRAGON_FIRE 26 #define SV_WAND_DRAGON_COLD 27 #define SV_WAND_DRAGON_BREATH 28 #define SV_WAND_ROCKETS 29 /* The "sval" codes for TV_ROD */ #define SV_ROD_DETECT_TRAP 0 #define SV_ROD_DETECT_DOOR 1 #define SV_ROD_IDENTIFY 2 #define SV_ROD_RECALL 3 #define SV_ROD_ILLUMINATION 4 #define SV_ROD_MAPPING 5 #define SV_ROD_DETECTION 6 #define SV_ROD_PROBING 7 #define SV_ROD_CURING 8 #define SV_ROD_HEALING 9 #define SV_ROD_RESTORATION 10 #define SV_ROD_SPEED 11 #define SV_ROD_PESTICIDE 12 #define SV_ROD_TELEPORT_AWAY 13 #define SV_ROD_DISARMING 14 #define SV_ROD_LITE 15 #define SV_ROD_SLEEP_MONSTER 16 #define SV_ROD_SLOW_MONSTER 17 #define SV_ROD_DRAIN_LIFE 18 #define SV_ROD_POLYMORPH 19 #define SV_ROD_ACID_BOLT 20 #define SV_ROD_ELEC_BOLT 21 #define SV_ROD_FIRE_BOLT 22 #define SV_ROD_COLD_BOLT 23 #define SV_ROD_ACID_BALL 24 #define SV_ROD_ELEC_BALL 25 #define SV_ROD_FIRE_BALL 26 #define SV_ROD_COLD_BALL 27 #define SV_ROD_HAVOC 28 /* The "sval" codes for TV_SCROLL */ #define SV_SCROLL_DARKNESS 0 #define SV_SCROLL_AGGRAVATE_MONSTER 1 #define SV_SCROLL_CURSE_ARMOR 2 #define SV_SCROLL_CURSE_WEAPON 3 #define SV_SCROLL_SUMMON_MONSTER 4 #define SV_SCROLL_SUMMON_UNDEAD 5 /* xxx (summon?) */ #define SV_SCROLL_TRAP_CREATION 7 #define SV_SCROLL_PHASE_DOOR 8 #define SV_SCROLL_TELEPORT 9 #define SV_SCROLL_TELEPORT_LEVEL 10 #define SV_SCROLL_WORD_OF_RECALL 11 #define SV_SCROLL_IDENTIFY 12 #define SV_SCROLL_STAR_IDENTIFY 13 #define SV_SCROLL_REMOVE_CURSE 14 #define SV_SCROLL_STAR_REMOVE_CURSE 15 #define SV_SCROLL_ENCHANT_ARMOR 16 #define SV_SCROLL_ENCHANT_WEAPON_TO_HIT 17 #define SV_SCROLL_ENCHANT_WEAPON_TO_DAM 18 /* xxx enchant missile? */ #define SV_SCROLL_STAR_ENCHANT_ARMOR 20 #define SV_SCROLL_STAR_ENCHANT_WEAPON 21 #define SV_SCROLL_RECHARGING 22 #define SV_SCROLL_MUNDANITY 23 #define SV_SCROLL_LIGHT 24 #define SV_SCROLL_MAPPING 25 #define SV_SCROLL_DETECT_GOLD 26 #define SV_SCROLL_DETECT_ITEM 27 #define SV_SCROLL_DETECT_TRAP 28 #define SV_SCROLL_DETECT_DOOR 29 #define SV_SCROLL_DETECT_INVIS 30 /* xxx (detect evil?) */ #define SV_SCROLL_SATISFY_HUNGER 32 #define SV_SCROLL_BLESSING 33 #define SV_SCROLL_HOLY_CHANT 34 #define SV_SCROLL_HOLY_PRAYER 35 #define SV_SCROLL_MONSTER_CONFUSION 36 #define SV_SCROLL_PROTECTION_FROM_EVIL 37 #define SV_SCROLL_RUNE_OF_PROTECTION 38 #define SV_SCROLL_TRAP_DOOR_DESTRUCTION 39 /* xxx */ #define SV_SCROLL_STAR_DESTRUCTION 41 #define SV_SCROLL_DISPEL_UNDEAD 42 /* xxx */ #define SV_SCROLL_GENOCIDE 44 #define SV_SCROLL_MASS_GENOCIDE 45 #define SV_SCROLL_ACQUIREMENT 46 #define SV_SCROLL_STAR_ACQUIREMENT 47 #define SV_SCROLL_FIRE 48 #define SV_SCROLL_ICE 49 #define SV_SCROLL_CHAOS 50 #define SV_SCROLL_RUMOR 51 #define SV_SCROLL_ARTIFACT 52 /* The "sval" codes for TV_POTION */ #define SV_POTION_WATER 0 #define SV_POTION_APPLE_JUICE 1 #define SV_POTION_SLIME_MOLD 2 /* xxx (fixed color) */ #define SV_POTION_SLOWNESS 4 #define SV_POTION_SALT_WATER 5 #define SV_POTION_POISON 6 #define SV_POTION_BLINDNESS 7 /* xxx */ #define SV_POTION_CONFUSION 9 /* xxx */ #define SV_POTION_SLEEP 11 /* xxx */ #define SV_POTION_LOSE_MEMORIES 13 /* xxx */ #define SV_POTION_RUINATION 15 #define SV_POTION_DEC_STR 16 #define SV_POTION_DEC_INT 17 #define SV_POTION_DEC_WIS 18 #define SV_POTION_DEC_DEX 19 #define SV_POTION_DEC_CON 20 #define SV_POTION_DEC_CHR 21 #define SV_POTION_DETONATIONS 22 #define SV_POTION_DEATH 23 #define SV_POTION_INFRAVISION 24 #define SV_POTION_DETECT_INVIS 25 #define SV_POTION_SLOW_POISON 26 #define SV_POTION_CURE_POISON 27 #define SV_POTION_BOLDNESS 28 #define SV_POTION_SPEED 29 #define SV_POTION_RESIST_HEAT 30 #define SV_POTION_RESIST_COLD 31 #define SV_POTION_HEROISM 32 #define SV_POTION_BERSERK_STRENGTH 33 #define SV_POTION_CURE_LIGHT 34 #define SV_POTION_CURE_SERIOUS 35 #define SV_POTION_CURE_CRITICAL 36 #define SV_POTION_HEALING 37 #define SV_POTION_STAR_HEALING 38 #define SV_POTION_LIFE 39 #define SV_POTION_RESTORE_MANA 40 #define SV_POTION_RESTORE_EXP 41 #define SV_POTION_RES_STR 42 #define SV_POTION_RES_INT 43 #define SV_POTION_RES_WIS 44 #define SV_POTION_RES_DEX 45 #define SV_POTION_RES_CON 46 #define SV_POTION_RES_CHR 47 #define SV_POTION_INC_STR 48 #define SV_POTION_INC_INT 49 #define SV_POTION_INC_WIS 50 #define SV_POTION_INC_DEX 51 #define SV_POTION_INC_CON 52 #define SV_POTION_INC_CHR 53 /* xxx */ #define SV_POTION_AUGMENTATION 55 #define SV_POTION_ENLIGHTENMENT 56 #define SV_POTION_STAR_ENLIGHTENMENT 57 #define SV_POTION_SELF_KNOWLEDGE 58 #define SV_POTION_EXPERIENCE 59 #define SV_POTION_RESISTANCE 60 #define SV_POTION_CURING 61 #define SV_POTION_INVULNERABILITY 62 #define SV_POTION_NEW_LIFE 63 /* The "sval" codes for TV_FOOD */ #define SV_FOOD_POISON 0 #define SV_FOOD_BLINDNESS 1 #define SV_FOOD_PARANOIA 2 #define SV_FOOD_CONFUSION 3 #define SV_FOOD_HALLUCINATION 4 #define SV_FOOD_PARALYSIS 5 #define SV_FOOD_WEAKNESS 6 #define SV_FOOD_SICKNESS 7 #define SV_FOOD_STUPIDITY 8 #define SV_FOOD_NAIVETY 9 #define SV_FOOD_UNHEALTH 10 #define SV_FOOD_DISEASE 11 #define SV_FOOD_CURE_POISON 12 #define SV_FOOD_CURE_BLINDNESS 13 #define SV_FOOD_CURE_PARANOIA 14 #define SV_FOOD_CURE_CONFUSION 15 #define SV_FOOD_CURE_SERIOUS 16 #define SV_FOOD_RESTORE_STR 17 #define SV_FOOD_RESTORE_CON 18 #define SV_FOOD_RESTORING 19 /* many missing mushrooms */ #define SV_FOOD_BISCUIT 32 #define SV_FOOD_JERKY 33 #define SV_FOOD_RATION 35 #define SV_FOOD_SLIME_MOLD 36 #define SV_FOOD_WAYBREAD 37 #define SV_FOOD_PINT_OF_ALE 38 #define SV_FOOD_PINT_OF_WINE 39 /* * Special "sval" limit -- first "normal" food */ #define SV_FOOD_MIN_FOOD 32 /* * Special "sval" limit -- first "aimed" rod */ #define SV_ROD_MIN_DIRECTION 12 /* * Special "sval" limit -- first "large" chest */ #define SV_CHEST_MIN_LARGE 4 /* * Special "sval" limit -- first "good" magic/prayer book */ #define SV_BOOK_MIN_GOOD 2 #define OBJ_GOLD_LIST 480 /* First "gold" entry */ #define MAX_GOLD 18 /* Number of "gold" entries */ /* * Object creation flags * These are the values passable to apply_magic */ #define OC_NONE 0x00 #define OC_NORMAL 0x01 #define OC_FORCE_BAD 0x02 #define OC_FORCE_GOOD 0x04 /*** General flag values ***/ /* * Special cave grid flags */ #define CAVE_DUM1 0x01 #define CAVE_GLOW 0x02 /* self-illuminating */ #define CAVE_ICKY 0x04 /* part of a vault */ #define CAVE_ROOM 0x08 /* part of a room */ #define CAVE_DUM2 0x10 #define CAVE_MNLT 0x20 /* Illuminated by monster */ #define CAVE_TEMP 0x40 /* temp flag */ #define CAVE_XTRA 0x80 /* misc flag */ /* * Cave grid flags that are player-specific * * This data structure will eventually be moved into the players struct. */ #define GRID_DUM1 0x01 #define GRID_VIEW 0x02 /* In LOS */ #define GRID_SEEN 0x04 /* In LOS + Lit in some way */ #define GRID_DTCT 0x08 /* Detected for traps */ #define GRID_LITE 0x10 /* Lit by torchlight */ #define GRID_DUM3 0x20 #define GRID_DUM4 0x40 #define GRID_DUM5 0x80 /* * Region flags */ #define REGION_NULL 0x00 #define REGION_CAVE 0x01 /* Cave region */ #define REGION_OVER 0x02 /* Overlay region */ #define REGION_DUM2 0x04 /* * Feature flags */ #define FF_BLOCK 0x01 /* Blocks movement + los */ #define FF_HALF_LOS 0x02 /* Half-blocks los */ #define FF_USE_TRANS 0x04 /* Use transparency light effects */ #define FF_ICKY 0x08 /* Terrain can not have objects */ #define FF_PERM 0x10 /* Permanent terrain */ #define FF_OBJECT 0x20 /* Terrain is described like an object */ #define FF_PATTERN 0x40 /* The pattern */ #define FF_MARK 0x80 /* Remember tile if seen */ /* * Bit flags for the "project()" function * * JUMP: Jump directly to the target location (this is a hack) * BEAM: Work as a beam weapon (affect every grid passed through) * THRU: Continue "through" the target (used for "bolts"/"beams") * STOP: Stop as soon as we hit a monster (used for "bolts") * GRID: Affect each grid in the "blast area" in some way * ITEM: Affect each object in the "blast area" in some way * KILL: Affect each monster in the "blast area" in some way * HIDE: Hack -- disable "visual" feedback from projection * FRND: Stop if hit a friendly monster / player. * MFLD: Make fields using GF_XXX value as type. (unused as of yet) */ #define PROJECT_JUMP 0x0001 #define PROJECT_BEAM 0x0002 #define PROJECT_THRU 0x0004 #define PROJECT_STOP 0x0008 #define PROJECT_GRID 0x0010 #define PROJECT_ITEM 0x0020 #define PROJECT_KILL 0x0040 #define PROJECT_HIDE 0x0080 #define PROJECT_FRND 0x0100 #define PROJECT_MFLD 0x0200 /* * Bit flags for the "enchant()" function */ #define ENCH_TOHIT 0x01 /* Enchant to hit */ #define ENCH_TODAM 0x02 /* Enchant to damage */ #define ENCH_TOAC 0x04 /* Enchant to AC */ #define ENCH_FORCE 0x08 /* Force enchantment */ /* * Bit flags for the "target_set" function * * KILL: Target monsters * LOOK: Describe grid fully * XTRA: Currently unused flag * GRID: Select from all grids * HOST: Select hostile creatures only. */ #define TARGET_KILL 0x01 #define TARGET_LOOK 0x02 #define TARGET_XTRA 0x04 #define TARGET_GRID 0x08 #define TARGET_HOST 0x10 /* * Some bit-flags for the "smart" field */ #define SM_RES_ACID 0x00000001 #define SM_RES_ELEC 0x00000002 #define SM_RES_FIRE 0x00000004 #define SM_RES_COLD 0x00000008 #define SM_RES_POIS 0x00000010 #define SM_RES_NETH 0x00000020 #define SM_RES_LITE 0x00000040 #define SM_RES_DARK 0x00000080 #define SM_RES_FEAR 0x00000100 #define SM_RES_CONF 0x00000200 #define SM_RES_CHAOS 0x00000400 #define SM_RES_DISEN 0x00000800 #define SM_RES_BLIND 0x00001000 #define SM_RES_NEXUS 0x00002000 #define SM_RES_SOUND 0x00004000 #define SM_RES_SHARD 0x00008000 #define SM_OPP_ACID 0x00010000 #define SM_OPP_ELEC 0x00020000 #define SM_OPP_FIRE 0x00040000 #define SM_OPP_COLD 0x00080000 #define SM_OPP_POIS 0x00100000 #define SM_MIMIC 0x00200000 /* XXX Unknown Mimic */ #define SM_CLONED 0x00400000 /* XXX Cloned */ #define SM_PET 0x00800000 /* XXX Pet */ #define SM_IMM_ACID 0x01000000 #define SM_IMM_ELEC 0x02000000 #define SM_IMM_FIRE 0x04000000 #define SM_IMM_COLD 0x08000000 #define SM_FRIENDLY 0x10000000 /* XXX Friendly */ #define SM_IMM_REFLECT 0x20000000 #define SM_IMM_FREE 0x40000000 #define SM_IMM_MANA 0x80000000 /* * Bit flags for the "get_item" function */ #define USE_EQUIP 0x01 /* Allow equip items */ #define USE_INVEN 0x02 /* Allow inven items */ #define USE_FLOOR 0x04 /* Allow floor items */ #define USE_STORE 0x10 /* Selling to store */ /* * Bit flags for the "p_ptr->notice" variable */ #define PN_COMBINE 0x00000001L /* Combine the pack */ #define PN_REORDER 0x00000002L /* Reorder the pack */ /* xxx (many) */ /* * Bit flags for the "p_ptr->update" variable */ #define PU_BONUS 0x00000001L /* Calculate bonuses */ #define PU_TORCH 0x00000002L /* Calculate torch radius */ /* xxx (many) */ #define PU_HP 0x00000010L /* Calculate chp and mhp */ #define PU_MANA 0x00000020L /* Calculate csp and msp */ #define PU_SPELLS 0x00000040L /* Calculate spells */ /* xxx (many) */ #define PU_WEIGHT 0x00000100L /* Calculate weight of inventory */ /* xxx (many) */ #define PU_MAP 0x00001000L /* Notice change in screen size */ /* xxx (many) */ #define PU_VIEW 0x00100000L /* Update view */ #define PU_MON_LITE 0x00200000L /* Monster illumination */ #define PU_WIZ_FIX 0x00400000L /* Fix up after a wiz_lite() */ /* xxx */ #define PU_MONSTERS 0x01000000L /* Update monsters */ #define PU_DISTANCE 0x02000000L /* Update distances */ /* xxx */ #define PU_FLOW 0x10000000L /* Update flow */ /* xxx (many) */ /* * Bit flags for the "p_ptr->change" variable */ #define PC_WIZ_LITE 0x00000001L /* Redraw map after a wiz_lite() */ #define PC_SHIMMER 0x00000002L /* Shimmer monsters */ #define PC_REPAIR 0x00000004L /* Repair monsters */ #define PC_MUTATE 0x00000008L /* Mutate player (on birth) */ /* * Bit flags for the "p_ptr->redraw" variable */ #define PR_MISC 0x00000001L /* Display Race/Class */ #define PR_TITLE 0x00000002L /* Display Title */ #define PR_LEV 0x00000004L /* Display Level */ #define PR_EXP 0x00000008L /* Display Experience */ #define PR_STATS 0x00000010L /* Display Stats */ #define PR_ARMOR 0x00000020L /* Display Armor */ #define PR_HP 0x00000040L /* Display Hitpoints */ #define PR_MANA 0x00000080L /* Display Mana */ #define PR_GOLD 0x00000100L /* Display Gold */ #define PR_DEPTH 0x00000200L /* Display Depth */ #define PR_EQUIPPY 0x00000400L /* Display equippy chars */ #define PR_HEALTH 0x00000800L /* Display Health Bar */ #define PR_CUT 0x00001000L /* Display Extra (Cut) */ #define PR_STUN 0x00002000L /* Display Extra (Stun) */ #define PR_HUNGER 0x00004000L /* Display Extra (Hunger) */ #define PR_STATUS 0x00008000L /* Display Status Bar */ #define PR_BLIND 0x00010000L /* Display Extra (Blind) */ #define PR_CONFUSED 0x00020000L /* Display Extra (Confused) */ #define PR_AFRAID 0x00040000L /* Display Extra (Afraid) */ #define PR_POISONED 0x00080000L /* Display Extra (Poisoned) */ #define PR_STATE 0x00100000L /* Display Extra (State) */ #define PR_SPEED 0x00200000L /* Display Extra (Speed) */ #define PR_STUDY 0x00400000L /* Display Extra (Study) */ /* xxx */ #define PR_EXTRA 0x01000000L /* Display Extra Info */ #define PR_BASIC 0x02000000L /* Display Basic Info */ #define PR_MAP 0x04000000L /* Display Map */ #define PR_WIPE 0x08000000L /* Hack -- Total Redraw */ /* xxx */ /* xxx */ /* xxx */ /* xxx */ /* * Bit flags for the "p_ptr->window" variable (etc) */ #define PW_INVEN 0x00000001L /* Display inven/equip */ #define PW_EQUIP 0x00000002L /* Display equip/inven */ #define PW_SPELL 0x00000004L /* Display spell list */ #define PW_PLAYER 0x00000008L /* Display character */ #define PW_SCRIPT_VARS 0x00000010L /* Display script messages */ #define PW_SCRIPT_SOURCE 0x00000020L /* Display script messages */ #define PW_MESSAGE 0x00000040L /* Display messages */ #define PW_OVERHEAD 0x00000080L /* Display overhead view */ #define PW_MONSTER 0x00000100L /* Display monster recall */ #define PW_OBJECT 0x00000200L /* Display object recall */ #define PW_DUNGEON 0x00000400L /* Display dungeon view */ #define PW_SNAPSHOT 0x00000800L /* Display snap-shot */ #define PW_VISIBLE 0x00001000L /* Display monster visible list */ #define PW_BORG_1 0x00002000L /* Display borg messages */ #define PW_BORG_2 0x00004000L /* Display borg status */ /*** General index values ***/ /* * Legal restrictions for "summon_specific()" */ #define SUMMON_ANT 11 #define SUMMON_SPIDER 12 #define SUMMON_HOUND 13 #define SUMMON_HYDRA 14 #define SUMMON_ANGEL 15 #define SUMMON_DEMON 16 #define SUMMON_UNDEAD 17 #define SUMMON_DRAGON 18 #define SUMMON_HI_UNDEAD 21 #define SUMMON_HI_DRAGON 22 #define SUMMON_AMBERITES 31 #define SUMMON_UNIQUE 32 #define SUMMON_BIZARRE1 33 #define SUMMON_BIZARRE2 34 #define SUMMON_BIZARRE3 35 #define SUMMON_BIZARRE4 36 #define SUMMON_BIZARRE5 37 #define SUMMON_BIZARRE6 38 #define SUMMON_CYBER 39 #define SUMMON_KIN 40 #define SUMMON_DAWN 41 #define SUMMON_ANIMAL 42 #define SUMMON_ANIMAL_RANGER 43 #define SUMMON_HI_UNDEAD_NO_UNIQUES 44 #define SUMMON_HI_DRAGON_NO_UNIQUES 45 #define SUMMON_NO_UNIQUES 46 #define SUMMON_PHANTOM 47 #define SUMMON_ELEMENTAL 48 #define SUMMON_BLUE_HORROR 49 /* * Spell types used by project(), and related functions. */ #define GF_NONE 0 #define GF_ELEC 1 #define GF_POIS 2 #define GF_ACID 3 #define GF_COLD 4 #define GF_FIRE 5 #define GF_MISSILE 10 #define GF_ARROW 11 #define GF_PLASMA 12 /* Replaced with GF_HOLY_FIRE and GF_HELL_FIRE */ /* #define GF_HOLY_ORB 13 */ #define GF_WATER 14 #define GF_LITE 15 #define GF_DARK 16 #define GF_LITE_WEAK 17 #define GF_DARK_WEAK 18 #define GF_SHARDS 20 #define GF_SOUND 21 #define GF_CONFUSION 22 #define GF_FORCE 23 #define GF_INERTIA 24 #define GF_MANA 26 #define GF_METEOR 27 #define GF_ICE 28 #define GF_CHAOS 30 #define GF_NETHER 31 #define GF_DISENCHANT 32 #define GF_NEXUS 33 #define GF_TIME 34 #define GF_GRAVITY 35 #define GF_KILL_WALL 40 #define GF_KILL_DOOR 41 #define GF_KILL_TRAP 42 #define GF_MAKE_WALL 45 #define GF_MAKE_DOOR 46 #define GF_MAKE_TRAP 47 #define GF_OLD_CLONE 51 #define GF_OLD_POLY 52 #define GF_OLD_HEAL 53 #define GF_OLD_SPEED 54 #define GF_OLD_SLOW 55 #define GF_OLD_CONF 56 #define GF_OLD_SLEEP 57 #define GF_OLD_DRAIN 58 #define GF_NEW_DRAIN 59 #define GF_AWAY_UNDEAD 61 #define GF_AWAY_EVIL 62 #define GF_AWAY_ALL 63 #define GF_TURN_UNDEAD 64 #define GF_TURN_EVIL 65 #define GF_TURN_ALL 66 #define GF_DISP_UNDEAD 67 #define GF_DISP_EVIL 68 #define GF_DISP_ALL 69 #define GF_DISP_DEMON 70 /* New types for Zangband begin here... */ #define GF_DISP_LIVING 71 #define GF_ROCKET 72 #define GF_NUKE 73 #define GF_MAKE_GLYPH 74 #define GF_STASIS 75 #define GF_STONE_WALL 76 #define GF_DEATH_RAY 77 #define GF_STUN 78 #define GF_HOLY_FIRE 79 #define GF_HELL_FIRE 80 #define GF_DISINTEGRATE 81 #define GF_CHARM 82 #define GF_CONTROL_UNDEAD 83 #define GF_CONTROL_ANIMAL 84 #define GF_PSI 85 #define GF_PSI_DRAIN 86 #define GF_TELEKINESIS 87 #define GF_JAM_DOOR 88 #define GF_DOMINATION 89 #define GF_DISP_GOOD 90 #define MAX_GF 91 /* * Some things which induce learning */ #define DRS_ACID 1 #define DRS_ELEC 2 #define DRS_FIRE 3 #define DRS_COLD 4 #define DRS_POIS 5 #define DRS_NETH 6 #define DRS_LITE 7 #define DRS_DARK 8 #define DRS_FEAR 9 #define DRS_CONF 10 #define DRS_CHAOS 11 #define DRS_DISEN 12 #define DRS_BLIND 13 #define DRS_NEXUS 14 #define DRS_SOUND 15 #define DRS_SHARD 16 #define DRS_FREE 30 #define DRS_MANA 31 #define DRS_REFLECT 32 /* * Game generated inscription indices. These are stored in the object, * and are used to index the string array from tables.c. */ #define FEEL_NONE 0 #define FEEL_BROKEN 1 #define FEEL_TERRIBLE 2 #define FEEL_WORTHLESS 3 #define FEEL_CURSED 4 #define FEEL_UNCURSED 5 #define FEEL_AVERAGE 6 #define FEEL_GOOD 7 #define FEEL_EXCELLENT 8 #define FEEL_SPECIAL 9 #define FEEL_BAD 10 #define FEEL_DUBIOUS 11 #define FEEL_TAINTED 12 #define FEEL_MAX 13 /* * Special "xtra" object powers for ego items and some artifacts */ /* Sustain one stat */ #define EGO_XTRA_SUSTAIN 1 /* Resistances */ #define EGO_XTRA_LO_RESIST 2 #define EGO_XTRA_HI_RESIST 3 #define EGO_XTRA_ANY_RESIST 4 /* Special ability */ #define EGO_XTRA_ABILITY 5 /* Special ability OR high resist */ #define EGO_XTRA_POWER 6 /*** Object flag values ***/ /* * Chest trap flags (see "tables.c") */ #define CHEST_LOSE_STR 0x01 #define CHEST_LOSE_CON 0x02 #define CHEST_POISON 0x04 #define CHEST_PARALYZE 0x08 #define CHEST_EXPLODE 0x10 #define CHEST_SUMMON 0x20 /* * Special Object Flags */ #define OB_SENSE 0x01 /* Item has been "sensed" */ #define OB_SEEN 0x02 /* Item is seen */ #define OB_EMPTY 0x04 /* Item charges are known */ #define OB_KNOWN 0x08 /* Item abilities are known */ #define OB_STOREB 0x10 /* Item is storebought */ #define OB_MENTAL 0x20 /* Item is *id*'ed */ #define OB_NO_EXP 0x40 /* Item gives no score */ #define OB_DUMMY4 0x80 /* * Special Monster Flags (all temporary) */ #define MFLAG_VIEW 0x01 /* Monster is in line of sight */ #define MFLAG_TEMP 0x02 /* Monster is marked for project_hack() */ #define MFLAG_XXX2 0x04 /* (unused) */ #define MFLAG_XXX3 0x08 /* (unused) */ #define MFLAG_MOVE 0x10 /* Monster has moved this turn */ #define MFLAG_NICE 0x20 /* Monster is still being nice */ #define MFLAG_SHOW 0x40 /* Monster is recently memorized */ #define MFLAG_MARK 0x80 /* Monster is currently memorized */ /* * As of 2.7.8, the "object flags" are valid for all objects, and as * of 2.7.9, these flags are not actually stored with the object. * * Note that "flags1" contains all flags dependant on "pval" (including * stat bonuses, but NOT stat sustainers), plus all "extra attack damage" * flags (SLAY_XXX and BRAND_XXX). * * Note that "flags2" contains all "resistances" (including "Stat Sustainers", * actual immunities, and resistances). Note that "Hold Life" is really an * "immunity" to ExpLoss, and "Free Action" is "immunity to paralysis". * * Note that "flags3" contains everything else -- including the three "CURSED" * flags, and the "BLESSED" flag, several "item display" parameters, some new * flags for powerful Bows, and flags which affect the player in a "general" * way (LITE, TELEPATHY, SEE_INVIS, SLOW_DIGEST, REGEN, FEATHER), including * all the "general" curses (TELEPORT, AGGRAVATE, EXP_DRAIN). It also has * four new flags called "ITEM_IGNORE_XXX" which lets an item specify that * it can not be affected by various forms of destruction. This is NOT as * powerful as actually granting resistance/immunity to the wearer. */ #define NUM_TR_SETS 4 #define TR0_STR 0x00000001L /* STR += "pval" */ #define TR0_INT 0x00000002L /* INT += "pval" */ #define TR0_WIS 0x00000004L /* WIS += "pval" */ #define TR0_DEX 0x00000008L /* DEX += "pval" */ #define TR0_CON 0x00000010L /* CON += "pval" */ #define TR0_CHR 0x00000020L /* CHR += "pval" */ #define TR0_XXX1 0x00000040L /* Later */ #define TR0_SP 0x00000080L /* Extra mana */ #define TR0_STEALTH 0x00000100L /* Stealth += "pval" */ #define TR0_SEARCH 0x00000200L /* Search += "pval" */ #define TR0_INFRA 0x00000400L /* Infra += "pval" */ #define TR0_TUNNEL 0x00000800L /* Tunnel += "pval" */ #define TR0_SPEED 0x00001000L /* Speed += "pval" */ #define TR0_BLOWS 0x00002000L /* Blows += "pval" */ #define TR0_CHAOTIC 0x00004000L #define TR0_VAMPIRIC 0x00008000L #define TR0_SLAY_ANIMAL 0x00010000L #define TR0_SLAY_EVIL 0x00020000L #define TR0_SLAY_UNDEAD 0x00040000L #define TR0_SLAY_DEMON 0x00080000L #define TR0_SLAY_ORC 0x00100000L #define TR0_SLAY_TROLL 0x00200000L #define TR0_SLAY_GIANT 0x00400000L #define TR0_SLAY_DRAGON 0x00800000L #define TR0_KILL_DRAGON 0x01000000L /* Execute Dragon */ #define TR0_VORPAL 0x02000000L /* Later */ #define TR0_IMPACT 0x04000000L /* Cause Earthquakes */ #define TR0_BRAND_POIS 0x08000000L #define TR0_BRAND_ACID 0x10000000L #define TR0_BRAND_ELEC 0x20000000L #define TR0_BRAND_FIRE 0x40000000L #define TR0_BRAND_COLD 0x80000000L #define TR1_SUST_STR 0x00000001L #define TR1_SUST_INT 0x00000002L #define TR1_SUST_WIS 0x00000004L #define TR1_SUST_DEX 0x00000008L #define TR1_SUST_CON 0x00000010L #define TR1_SUST_CHR 0x00000020L #define TR1_XXX1 0x00000040L /* Later */ #define TR1_IM_POIS 0x00000080L #define TR1_IM_ACID 0x00000100L #define TR1_IM_ELEC 0x00000200L #define TR1_IM_FIRE 0x00000400L #define TR1_IM_COLD 0x00000800L #define TR1_THROW 0x00001000L /* Throwing items */ #define TR1_REFLECT 0x00002000L /* Reflect 'bolts' */ #define TR1_FREE_ACT 0x00004000L /* Free Action */ #define TR1_HOLD_LIFE 0x00008000L /* Hold Life */ #define TR1_RES_ACID 0x00010000L #define TR1_RES_ELEC 0x00020000L #define TR1_RES_FIRE 0x00040000L #define TR1_RES_COLD 0x00080000L #define TR1_RES_POIS 0x00100000L #define TR1_RES_FEAR 0x00200000L /* Added for Zangband */ #define TR1_RES_LITE 0x00400000L #define TR1_RES_DARK 0x00800000L #define TR1_RES_BLIND 0x01000000L #define TR1_RES_CONF 0x02000000L #define TR1_RES_SOUND 0x04000000L #define TR1_RES_SHARDS 0x08000000L #define TR1_RES_NETHER 0x10000000L #define TR1_RES_NEXUS 0x20000000L #define TR1_RES_CHAOS 0x40000000L #define TR1_RES_DISEN 0x80000000L #define TR2_SH_FIRE 0x00000001L /* Immolation (Fire) */ #define TR2_SH_ELEC 0x00000002L /* Electric Sheath */ #define TR2_QUESTITEM 0x00000004L /* quest level item -KMW- */ #define TR2_XXX4 0x00000008L /* Later */ #define TR2_NO_TELE 0x00000010L /* Anti-teleportation */ #define TR2_NO_MAGIC 0x00000020L /* Anti-magic */ #define TR2_XXX7 0x00000040L /* Later */ #define TR2_TY_CURSE 0x00000080L /* The Ancient Curse */ #define TR2_EASY_KNOW 0x00000100L /* Aware -> Known */ #define TR2_HIDE_TYPE 0x00000200L /* Hide "pval" description */ #define TR2_SHOW_MODS 0x00000400L /* Always show Tohit/Todam */ #define TR2_INSTA_ART 0x00000800L /* Item must be an artifact */ #define TR2_FEATHER 0x00001000L /* Feather Falling */ #define TR2_LITE 0x00002000L /* Permanent Light */ #define TR2_SEE_INVIS 0x00004000L /* See Invisible */ #define TR2_TELEPATHY 0x00008000L /* Telepathy */ #define TR2_SLOW_DIGEST 0x00010000L /* Item slows down digestion */ #define TR2_REGEN 0x00020000L /* Item induces regeneration */ #define TR2_XTRA_MIGHT 0x00040000L /* Bows get extra multiplier */ #define TR2_XTRA_SHOTS 0x00080000L /* Bows get extra shots */ #define TR2_IGNORE_ACID 0x00100000L /* Item ignores Acid Damage */ #define TR2_IGNORE_ELEC 0x00200000L /* Item ignores Elec Damage */ #define TR2_IGNORE_FIRE 0x00400000L /* Item ignores Fire Damage */ #define TR2_IGNORE_COLD 0x00800000L /* Item ignores Cold Damage */ #define TR2_ACTIVATE 0x01000000L /* Item can be activated */ #define TR2_DRAIN_EXP 0x02000000L /* Item drains Experience */ #define TR2_TELEPORT 0x04000000L /* Item teleports player */ #define TR2_AGGRAVATE 0x08000000L /* Item aggravates monsters */ #define TR2_BLESSED 0x10000000L /* Item is Blessed */ #define TR2_CURSED 0x20000000L /* Item is Cursed */ #define TR2_HEAVY_CURSE 0x40000000L /* Item is Heavily Cursed */ #define TR2_PERMA_CURSE 0x80000000L /* Item is Perma Cursed */ #define TR3_LUCK_10 0x00000001L #define TR3_WILD_SHOT 0x00000002L #define TR3_WILD_WALK 0x00000004L #define TR3_EASY_ENCHANT 0x00000008L #define TR3_XXX5 0x00000010L #define TR3_XXX6 0x00000020L #define TR3_XXX7 0x00000040L #define TR3_XXX8 0x00000080L #define TR3_IM_LITE 0x00000100L #define TR3_IM_DARK 0x00000200L #define TR3_SH_ACID 0x00000400L #define TR3_SH_COLD 0x00000800L #define TR3_MUTATE 0x00001000L #define TR3_PATRON 0x00002000L #define TR3_STRANGE_LUCK 0x00004000L #define TR3_PASS_WALL 0x00008000L #define TR3_GHOUL_TOUCH 0x00010000L #define TR3_PSI_CRIT 0x00020000L #define TR3_RETURN 0x00040000L #define TR3_EXPLODE 0x00080000L #define TR3_HURT_ACID 0x00100000L #define TR3_HURT_ELEC 0x00200000L #define TR3_HURT_FIRE 0x00400000L #define TR3_HURT_COLD 0x00800000L #define TR3_HURT_LITE 0x01000000L #define TR3_HURT_DARK 0x02000000L #define TR3_XXX27 0x04000000L #define TR3_XXX28 0x08000000L #define TR3_AUTO_CURSE 0x10000000L #define TR3_DRAIN_STATS 0x20000000L #define TR3_CANT_EAT 0x40000000L #define TR3_SLOW_HEAL 0x80000000L /* * Hack -- flag set 1 -- mask for "pval-dependant" flags. * Note that all "pval" dependant flags must be in "flags1". */ #define TR0_PVAL_MASK \ (TR0_STR | TR0_INT | TR0_WIS | TR0_DEX | \ TR0_CON | TR0_CHR | TR0_SP | \ TR0_STEALTH | TR0_SEARCH | TR0_INFRA | TR0_TUNNEL | \ TR0_SPEED | TR0_BLOWS) /* * Flag set 1 -- mask for "easy" flags. * These flags are automatically learned if the item is worn. */ #define TR0_EASY_MASK \ (TR0_STR | TR0_INT | TR0_WIS | TR0_DEX | \ TR0_CON | TR0_CHR | TR0_SP | \ TR0_INFRA | TR0_SPEED | TR0_BLOWS) /* * Flag set 3 -- mask for "ignore element" flags. */ #define TR2_IGNORE_MASK \ (TR2_IGNORE_ACID | TR2_IGNORE_ELEC | TR2_IGNORE_FIRE | \ TR2_IGNORE_COLD ) /* Helpers for the FLAG() macro */ #define TR_STR 0, TR0_STR #define TR_INT 0, TR0_INT #define TR_WIS 0, TR0_WIS #define TR_DEX 0, TR0_DEX #define TR_CON 0, TR0_CON #define TR_CHR 0, TR0_CHR #define TR_XXX1 0, TR0_XXX1 #define TR_SP 0, TR0_SP #define TR_STEALTH 0, TR0_STEALTH #define TR_SEARCH 0, TR0_SEARCH #define TR_INFRA 0, TR0_INFRA #define TR_TUNNEL 0, TR0_TUNNEL #define TR_SPEED 0, TR0_SPEED #define TR_BLOWS 0, TR0_BLOWS #define TR_CHAOTIC 0, TR0_CHAOTIC #define TR_VAMPIRIC 0, TR0_VAMPIRIC #define TR_SLAY_ANIMAL 0, TR0_SLAY_ANIMAL #define TR_SLAY_EVIL 0, TR0_SLAY_EVIL #define TR_SLAY_UNDEAD 0, TR0_SLAY_UNDEAD #define TR_SLAY_DEMON 0, TR0_SLAY_DEMON #define TR_SLAY_ORC 0, TR0_SLAY_ORC #define TR_SLAY_TROLL 0, TR0_SLAY_TROLL #define TR_SLAY_GIANT 0, TR0_SLAY_GIANT #define TR_SLAY_DRAGON 0, TR0_SLAY_DRAGON #define TR_KILL_DRAGON 0, TR0_KILL_DRAGON #define TR_VORPAL 0, TR0_VORPAL #define TR_IMPACT 0, TR0_IMPACT #define TR_BRAND_POIS 0, TR0_BRAND_POIS #define TR_BRAND_ACID 0, TR0_BRAND_ACID #define TR_BRAND_ELEC 0, TR0_BRAND_ELEC #define TR_BRAND_FIRE 0, TR0_BRAND_FIRE #define TR_BRAND_COLD 0, TR0_BRAND_COLD #define TR_SUST_STR 1, TR1_SUST_STR #define TR_SUST_INT 1, TR1_SUST_INT #define TR_SUST_WIS 1, TR1_SUST_WIS #define TR_SUST_DEX 1, TR1_SUST_DEX #define TR_SUST_CON 1, TR1_SUST_CON #define TR_SUST_CHR 1, TR1_SUST_CHR #define TR_XXX2 1, TR1_XXX2 #define TR_IM_POIS 1, TR1_IM_POIS #define TR_IM_ACID 1, TR1_IM_ACID #define TR_IM_ELEC 1, TR1_IM_ELEC #define TR_IM_FIRE 1, TR1_IM_FIRE #define TR_IM_COLD 1, TR1_IM_COLD #define TR_THROW 1, TR1_THROW #define TR_REFLECT 1, TR1_REFLECT #define TR_FREE_ACT 1, TR1_FREE_ACT #define TR_HOLD_LIFE 1, TR1_HOLD_LIFE #define TR_RES_ACID 1, TR1_RES_ACID #define TR_RES_ELEC 1, TR1_RES_ELEC #define TR_RES_FIRE 1, TR1_RES_FIRE #define TR_RES_COLD 1, TR1_RES_COLD #define TR_RES_POIS 1, TR1_RES_POIS #define TR_RES_FEAR 1, TR1_RES_FEAR #define TR_RES_LITE 1, TR1_RES_LITE #define TR_RES_DARK 1, TR1_RES_DARK #define TR_RES_BLIND 1, TR1_RES_BLIND #define TR_RES_CONF 1, TR1_RES_CONF #define TR_RES_SOUND 1, TR1_RES_SOUND #define TR_RES_SHARDS 1, TR1_RES_SHARDS #define TR_RES_NETHER 1, TR1_RES_NETHER #define TR_RES_NEXUS 1, TR1_RES_NEXUS #define TR_RES_CHAOS 1, TR1_RES_CHAOS #define TR_RES_DISEN 1, TR1_RES_DISEN #define TR_SH_FIRE 2, TR2_SH_FIRE #define TR_SH_ELEC 2, TR2_SH_ELEC #define TR_QUESTITEM 2, TR2_QUESTITEM #define TR_XXX4 2, TR2_XXX4 #define TR_NO_TELE 2, TR2_NO_TELE #define TR_NO_MAGIC 2, TR2_NO_MAGIC #define TR_XXX5 2, TR2_XXX5 #define TR_TY_CURSE 2, TR2_TY_CURSE #define TR_EASY_KNOW 2, TR2_EASY_KNOW #define TR_HIDE_TYPE 2, TR2_HIDE_TYPE #define TR_SHOW_MODS 2, TR2_SHOW_MODS #define TR_INSTA_ART 2, TR2_INSTA_ART #define TR_FEATHER 2, TR2_FEATHER #define TR_LITE 2, TR2_LITE #define TR_SEE_INVIS 2, TR2_SEE_INVIS #define TR_TELEPATHY 2, TR2_TELEPATHY #define TR_SLOW_DIGEST 2, TR2_SLOW_DIGEST #define TR_REGEN 2, TR2_REGEN #define TR_XTRA_MIGHT 2, TR2_XTRA_MIGHT #define TR_XTRA_SHOTS 2, TR2_XTRA_SHOTS #define TR_IGNORE_ACID 2, TR2_IGNORE_ACID #define TR_IGNORE_ELEC 2, TR2_IGNORE_ELEC #define TR_IGNORE_FIRE 2, TR2_IGNORE_FIRE #define TR_IGNORE_COLD 2, TR2_IGNORE_COLD #define TR_ACTIVATE 2, TR2_ACTIVATE #define TR_DRAIN_EXP 2, TR2_DRAIN_EXP #define TR_TELEPORT 2, TR2_TELEPORT #define TR_AGGRAVATE 2, TR2_AGGRAVATE #define TR_BLESSED 2, TR2_BLESSED #define TR_CURSED 2, TR2_CURSED #define TR_HEAVY_CURSE 2, TR2_HEAVY_CURSE #define TR_PERMA_CURSE 2, TR2_PERMA_CURSE #define TR_LUCK_10 3, TR3_LUCK_10 #define TR_WILD_SHOT 3, TR3_WILD_SHOT #define TR_WILD_WALK 3, TR3_WILD_WALK #define TR_EASY_ENCHANT 3, TR3_EASY_ENCHANT #define TR_XXX7 3, TR3_XXX7 #define TR_XXX8 3, TR3_XXX8 #define TR_XXX9 3, TR3_XXX9 #define TR_XXX10 3, TR3_XXX10 #define TR_XXX11 3, TR3_XXX11 #define TR_XXX12 3, TR3_XXX12 #define TR_IM_LITE 3, TR3_IM_LITE #define TR_IM_DARK 3, TR3_IM_DARK #define TR_SH_ACID 3, TR3_SH_ACID #define TR_SH_COLD 3, TR3_SH_COLD #define TR_MUTATE 3, TR3_MUTATE #define TR_PATRON 3, TR3_PATRON #define TR_STRANGE_LUCK 3, TR3_STRANGE_LUCK #define TR_PASS_WALL 3, TR3_PASS_WALL #define TR_GHOUL_TOUCH 3, TR3_GHOUL_TOUCH #define TR_PSI_CRIT 3, TR3_PSI_CRIT #define TR_RETURN 3, TR3_RETURN #define TR_EXPLODE 3, TR3_EXPLODE #define TR_HURT_ACID 3, TR3_HURT_ACID #define TR_HURT_ELEC 3, TR3_HURT_ELEC #define TR_HURT_FIRE 3, TR3_HURT_FIRE #define TR_HURT_COLD 3, TR3_HURT_COLD #define TR_HURT_LITE 3, TR3_HURT_LITE #define TR_HURT_DARK 3, TR3_HURT_DARK #define TR_XXX27 3, TR3_XXX27 #define TR_XXX28 3, TR3_XXX28 #define TR_AUTO_CURSE 3, TR3_AUTO_CURSE #define TR_DRAIN_STATS 3, TR3_DRAIN_STATS #define TR_CANT_EAT 3, TR3_CANT_EAT #define TR_SLOW_HEAL 3, TR3_SLOW_HEAL /* Extra helpers */ #define TR_PVAL_MASK 0, TR0_PVAL_MASK #define TR_EASY_MASK 0, TR0_EASY_MASK #define TR_IGNORE_MASK 2, TR2_IGNORE_MASK /*** Monster blow constants ***/ /* * New monster blow methods */ #define RBM_HIT 1 #define RBM_TOUCH 2 #define RBM_PUNCH 3 #define RBM_KICK 4 #define RBM_CLAW 5 #define RBM_BITE 6 #define RBM_STING 7 #define RBM_XXX1 8 #define RBM_BUTT 9 #define RBM_CRUSH 10 #define RBM_ENGULF 11 #define RBM_CHARGE 12 #define RBM_CRAWL 13 #define RBM_DROOL 14 #define RBM_SPIT 15 #define RBM_EXPLODE 16 #define RBM_GAZE 17 #define RBM_WAIL 18 #define RBM_SPORE 19 #define RBM_XXX4 20 #define RBM_BEG 21 #define RBM_INSULT 22 #define RBM_MOAN 23 #define RBM_SHOW 24 #define MAX_RBM 25 /* * New monster blow effects */ #define RBE_HURT 1 #define RBE_POISON 2 #define RBE_UN_BONUS 3 #define RBE_UN_POWER 4 #define RBE_EAT_GOLD 5 #define RBE_EAT_ITEM 6 #define RBE_EAT_FOOD 7 #define RBE_EAT_LITE 8 #define RBE_ACID 9 #define RBE_ELEC 10 #define RBE_FIRE 11 #define RBE_COLD 12 #define RBE_BLIND 13 #define RBE_CONFUSE 14 #define RBE_TERRIFY 15 #define RBE_PARALYZE 16 #define RBE_LOSE_STR 17 #define RBE_LOSE_INT 18 #define RBE_LOSE_WIS 19 #define RBE_LOSE_DEX 20 #define RBE_LOSE_CON 21 #define RBE_LOSE_CHR 22 #define RBE_LOSE_ALL 23 #define RBE_SHATTER 24 #define RBE_EXP_10 25 #define RBE_EXP_20 26 #define RBE_EXP_40 27 #define RBE_EXP_80 28 #define RBE_DISEASE 29 #define RBE_TIME 30 #define RBE_EXP_VAMP 31 /*** Monster flag values (hard-coded) ***/ /* * New monster race bit flags */ #define RF0_UNIQUE 0x00000001 /* Unique Monster */ #define RF0_QUESTOR 0x00000002 /* Quest Monster */ #define RF0_MALE 0x00000004 /* Male gender */ #define RF0_FEMALE 0x00000008 /* Female gender */ #define RF0_CHAR_CLEAR 0x00000010 /* Absorbs symbol */ #define RF0_CHAR_MIMIC 0x00000020 /* Changes symbol */ #define RF0_ATTR_CLEAR 0x00000040 /* Absorbs color */ #define RF0_ATTR_MULTI 0x00000080 /* Changes color */ #define RF0_FORCE_DEPTH 0x00000100 /* Start at "correct" depth */ #define RF0_FORCE_MAXHP 0x00000200 /* Start with max hitpoints */ #define RF0_FORCE_SLEEP 0x00000400 /* Start out sleeping */ #define RF0_FORCE_EXTRA 0x00000800 /* Start out something */ #define RF0_XXX_1 0x00001000 /* Unused */ #define RF0_FRIENDS 0x00002000 /* Arrive with some friends */ #define RF0_ESCORT 0x00004000 /* Arrive with an escort */ #define RF0_ESCORTS 0x00008000 /* Arrive with some escorts */ #define RF0_NEVER_BLOW 0x00010000 /* Never make physical blow */ #define RF0_NEVER_MOVE 0x00020000 /* Never make physical move */ #define RF0_RAND_25 0x00040000 /* Moves randomly (25%) */ #define RF0_RAND_50 0x00080000 /* Moves randomly (50%) */ #define RF0_ONLY_GOLD 0x00100000 /* Drop only gold */ #define RF0_ONLY_ITEM 0x00200000 /* Drop only items */ #define RF0_DROP_60 0x00400000 /* Drop an item/gold (60%) */ #define RF0_DROP_90 0x00800000 /* Drop an item/gold (90%) */ #define RF0_DROP_1D2 0x01000000 /* Drop 1d2 items/gold */ #define RF0_DROP_2D2 0x02000000 /* Drop 2d2 items/gold */ #define RF0_DROP_3D2 0x04000000 /* Drop 3d2 items/gold */ #define RF0_DROP_4D2 0x08000000 /* Drop 4d2 items/gold */ #define RF0_DROP_GOOD 0x10000000 /* Drop good items */ #define RF0_DROP_GREAT 0x20000000 /* Drop great items */ #define RF0_DROP_USEFUL 0x40000000 /* Drop "useful" items */ #define RF0_DROP_CHOSEN 0x80000000 /* Drop "chosen" items */ /* * New monster race bit flags */ #define RF1_STUPID 0x00000001 /* Monster is stupid */ #define RF1_SMART 0x00000002 /* Monster is smart */ #define RF1_CAN_SPEAK 0x00000004 /* TY: can speak */ #define RF1_REFLECTING 0x00000008 /* Reflects bolts */ #define RF1_INVISIBLE 0x00000010 /* Monster avoids vision */ #define RF1_COLD_BLOOD 0x00000020 /* Monster avoids infra */ #define RF1_EMPTY_MIND 0x00000040 /* Monster avoids telepathy */ #define RF1_WEIRD_MIND 0x00000080 /* Monster avoids telepathy? */ #define RF1_MULTIPLY 0x00000100 /* Monster reproduces */ #define RF1_REGENERATE 0x00000200 /* Monster regenerates */ #define RF1_SHAPECHANGER 0x00000400 /* TY: shapechanger */ #define RF1_ATTR_ANY 0x00000800 /* TY: Attr_any */ #define RF1_POWERFUL 0x00001000 /* Monster has strong breath */ #define RF1_XXX_1 0x00002000 #define RF1_AURA_FIRE 0x00004000 /* Burns in melee */ #define RF1_AURA_ELEC 0x00008000 /* Shocks in melee */ #define RF1_OPEN_DOOR 0x00010000 /* Monster can open doors */ #define RF1_BASH_DOOR 0x00020000 /* Monster can bash doors */ #define RF1_PASS_WALL 0x00040000 /* Monster can pass walls */ #define RF1_KILL_WALL 0x00080000 /* Monster can destroy walls */ #define RF1_MOVE_BODY 0x00100000 /* Monster can move monsters */ #define RF1_KILL_BODY 0x00200000 /* Monster can kill monsters */ #define RF1_TAKE_ITEM 0x00400000 /* Monster can pick up items */ #define RF1_KILL_ITEM 0x00800000 /* Monster can crush items */ #define RF1_BRAIN_1 0x01000000 #define RF1_BRAIN_2 0x02000000 #define RF1_BRAIN_3 0x04000000 #define RF1_BRAIN_4 0x08000000 #define RF1_BRAIN_5 0x10000000 #define RF1_BRAIN_6 0x20000000 #define RF1_BRAIN_7 0x40000000 #define RF1_QUANTUM 0x80000000 /* Monster has quantum behavior */ /* * New monster race bit flags */ #define RF2_ORC 0x00000001 /* Orc */ #define RF2_TROLL 0x00000002 /* Troll */ #define RF2_GIANT 0x00000004 /* Giant */ #define RF2_DRAGON 0x00000008 /* Dragon */ #define RF2_DEMON 0x00000010 /* Demon */ #define RF2_UNDEAD 0x00000020 /* Undead */ #define RF2_EVIL 0x00000040 /* Evil */ #define RF2_ANIMAL 0x00000080 /* Animal */ #define RF2_AMBERITE 0x00000100 /* TY: Amberite */ #define RF2_GOOD 0x00000200 /* Good */ #define RF2_AURA_COLD 0x00000400 /* Freezes in melee */ #define RF2_NONLIVING 0x00000800 /* TY: Non-Living (?) */ #define RF2_HURT_LITE 0x00001000 /* Hurt by lite */ #define RF2_HURT_ROCK 0x00002000 /* Hurt by rock remover */ #define RF2_HURT_FIRE 0x00004000 /* Hurt badly by fire */ #define RF2_HURT_COLD 0x00008000 /* Hurt badly by cold */ #define RF2_IM_ACID 0x00010000 /* Resist acid a lot */ #define RF2_IM_ELEC 0x00020000 /* Resist elec a lot */ #define RF2_IM_FIRE 0x00040000 /* Resist fire a lot */ #define RF2_IM_COLD 0x00080000 /* Resist cold a lot */ #define RF2_IM_POIS 0x00100000 /* Resist poison a lot */ #define RF2_RES_TELE 0x00200000 /* Resist teleportation */ #define RF2_RES_NETH 0x00400000 /* Resist nether a lot */ #define RF2_RES_WATE 0x00800000 /* Resist water */ #define RF2_RES_PLAS 0x01000000 /* Resist plasma */ #define RF2_RES_NEXU 0x02000000 /* Resist nexus */ #define RF2_RES_DISE 0x04000000 /* Resist disenchantment */ #define RF2_UNIQUE_7 0x08000000 /* Is a "Nazgul" unique */ #define RF2_NO_FEAR 0x10000000 /* Cannot be scared */ #define RF2_NO_STUN 0x20000000 /* Cannot be stunned */ #define RF2_NO_CONF 0x40000000 /* Cannot be confused */ #define RF2_NO_SLEEP 0x80000000 /* Cannot be slept */ /* * New monster race bit flags */ #define RF3_SHRIEK 0x00000001 /* Shriek for help */ #define RF3_ELDRITCH_HORROR 0x00000002 /* Sanity-blasting horror */ #define RF3_XXX3 0x00000004 /* (?) */ #define RF3_ROCKET 0x00000008 /* TY: Rocket */ #define RF3_ARROW 0x00000010 /* Fire an arrow */ #define RF3_XXX6 0x00000020 /* (?) */ #define RF3_XXX7 0x00000040 /* (?) */ #define RF3_XXX8 0x00000080 /* (?) */ #define RF3_BR_ACID 0x00000100 /* Breathe Acid */ #define RF3_BR_ELEC 0x00000200 /* Breathe Elec */ #define RF3_BR_FIRE 0x00000400 /* Breathe Fire */ #define RF3_BR_COLD 0x00000800 /* Breathe Cold */ #define RF3_BR_POIS 0x00001000 /* Breathe Poison */ #define RF3_BR_NETH 0x00002000 /* Breathe Nether */ #define RF3_BR_LITE 0x00004000 /* Breathe Lite */ #define RF3_BR_DARK 0x00008000 /* Breathe Dark */ #define RF3_BR_CONF 0x00010000 /* Breathe Confusion */ #define RF3_BR_SOUN 0x00020000 /* Breathe Sound */ #define RF3_BR_CHAO 0x00040000 /* Breathe Chaos */ #define RF3_BR_DISE 0x00080000 /* Breathe Disenchant */ #define RF3_BR_NEXU 0x00100000 /* Breathe Nexus */ #define RF3_BR_TIME 0x00200000 /* Breathe Time */ #define RF3_BR_INER 0x00400000 /* Breathe Inertia */ #define RF3_BR_GRAV 0x00800000 /* Breathe Gravity */ #define RF3_BR_SHAR 0x01000000 /* Breathe Shards */ #define RF3_BR_PLAS 0x02000000 /* Breathe Plasma */ #define RF3_BR_WALL 0x04000000 /* Breathe Force */ #define RF3_BR_MANA 0x08000000 /* Breathe Mana */ #define RF3_BA_NUKE 0x10000000 /* TY: Nuke Ball */ #define RF3_BR_NUKE 0x20000000 /* TY: Toxic Breath */ #define RF3_BA_CHAO 0x40000000 /* TY: Logrus Ball */ #define RF3_BR_DISI 0x80000000 /* Breathe Disintegration */ /* * New monster race bit flags */ #define RF4_BA_ACID 0x00000001 /* Acid Ball */ #define RF4_BA_ELEC 0x00000002 /* Elec Ball */ #define RF4_BA_FIRE 0x00000004 /* Fire Ball */ #define RF4_BA_COLD 0x00000008 /* Cold Ball */ #define RF4_BA_POIS 0x00000010 /* Poison Ball */ #define RF4_BA_NETH 0x00000020 /* Nether Ball */ #define RF4_BA_WATE 0x00000040 /* Water Ball */ #define RF4_BA_MANA 0x00000080 /* Mana Storm */ #define RF4_BA_DARK 0x00000100 /* Darkness Storm */ #define RF4_DRAIN_MANA 0x00000200 /* Drain Mana */ #define RF4_MIND_BLAST 0x00000400 /* Blast Mind */ #define RF4_BRAIN_SMASH 0x00000800 /* Smash Brain */ #define RF4_CAUSE_1 0x00001000 /* Cause Light Wound */ #define RF4_CAUSE_2 0x00002000 /* Cause Serious Wound */ #define RF4_CAUSE_3 0x00004000 /* Cause Critical Wound */ #define RF4_CAUSE_4 0x00008000 /* Cause Mortal Wound */ #define RF4_BO_ACID 0x00010000 /* Acid Bolt */ #define RF4_BO_ELEC 0x00020000 /* Elec Bolt (unused) */ #define RF4_BO_FIRE 0x00040000 /* Fire Bolt */ #define RF4_BO_COLD 0x00080000 /* Cold Bolt */ #define RF4_BO_POIS 0x00100000 /* Poison Bolt (unused) */ #define RF4_BO_NETH 0x00200000 /* Nether Bolt */ #define RF4_BO_WATE 0x00400000 /* Water Bolt */ #define RF4_BO_MANA 0x00800000 /* Mana Bolt */ #define RF4_BO_PLAS 0x01000000 /* Plasma Bolt */ #define RF4_BO_ICEE 0x02000000 /* Ice Bolt */ #define RF4_MISSILE 0x04000000 /* Magic Missile */ #define RF4_SCARE 0x08000000 /* Frighten Player */ #define RF4_BLIND 0x10000000 /* Blind Player */ #define RF4_CONF 0x20000000 /* Confuse Player */ #define RF4_SLOW 0x40000000 /* Slow Player */ #define RF4_HOLD 0x80000000 /* Paralyze Player */ /* * New monster race bit flags */ #define RF5_HASTE 0x00000001 /* Speed self */ #define RF5_HAND_DOOM 0x00000002 /* Hand of Doom */ #define RF5_HEAL 0x00000004 /* Heal self */ #define RF5_INVULNER 0x00000008 /* INVULNERABILITY! */ #define RF5_BLINK 0x00000010 /* Teleport Short */ #define RF5_TPORT 0x00000020 /* Teleport Long */ #define RF5_XXX3 0x00000040 /* Move to Player (?) */ #define RF5_XXX4 0x00000080 /* Move to Monster (?) */ #define RF5_TELE_TO 0x00000100 /* Move player to monster */ #define RF5_TELE_AWAY 0x00000200 /* Move player far away */ #define RF5_TELE_LEVEL 0x00000400 /* Move player vertically */ #define RF5_XXX5 0x00000800 /* Move player (?) */ #define RF5_DARKNESS 0x00001000 /* Create Darkness */ #define RF5_TRAPS 0x00002000 /* Create Traps */ #define RF5_FORGET 0x00004000 /* Cause amnesia */ #define RF5_RAISE_DEAD 0x00008000 /* Raise Dead */ #define RF5_S_KIN 0x00010000 /* Summon "kin" */ #define RF5_S_CYBER 0x00020000 /* Summon Cyberdemons! */ #define RF5_S_MONSTER 0x00040000 /* Summon Monster */ #define RF5_S_MONSTERS 0x00080000 /* Summon Monsters */ #define RF5_S_ANT 0x00100000 /* Summon Ants */ #define RF5_S_SPIDER 0x00200000 /* Summon Spiders */ #define RF5_S_HOUND 0x00400000 /* Summon Hounds */ #define RF5_S_HYDRA 0x00800000 /* Summon Hydras */ #define RF5_S_ANGEL 0x01000000 /* Summon Angel */ #define RF5_S_DEMON 0x02000000 /* Summon Demon */ #define RF5_S_UNDEAD 0x04000000 /* Summon Undead */ #define RF5_S_DRAGON 0x08000000 /* Summon Dragon */ #define RF5_S_HI_UNDEAD 0x10000000 /* Summon Greater Undead */ #define RF5_S_HI_DRAGON 0x20000000 /* Summon Ancient Dragon */ #define RF5_S_AMBERITES 0x40000000 /* Summon Amberites */ #define RF5_S_UNIQUE 0x80000000 /* Summon Unique Monster */ /* * New monster race bit flags */ #define RF6_AQUATIC 0x00000001 /* Aquatic monster */ #define RF6_CAN_SWIM 0x00000002 /* Monster can swim */ #define RF6_CAN_FLY 0x00000004 /* Monster can fly */ #define RF6_FRIENDLY 0x00000008 /* Monster is friendly */ #define RF6_SILLY 0x00000010 /* Monster is "silly" */ #define RF6_LITE_1 0x00000020 /* Monster carries a small lite */ #define RF6_LITE_2 0x00000040 /* Monster carries a large lite */ #define RF6_LIBRARY 0x00000080 /* Monster has been researched at a library */ /* * Monster race wilderness flags */ #define RF7_WILD_FOREST1 0x00000001 #define RF7_WILD_FOREST2 0x00000002 #define RF7_WILD_MOUNT1 0x00000004 #define RF7_WILD_MOUNT2 0x00000008 #define RF7_WILD_WASTE1 0x00000010 #define RF7_WILD_WASTE2 0x00000020 #define RF7_WILD_SWAMP1 0x00000040 #define RF7_WILD_SWAMP2 0x00000080 #define RF7_WILD_SHORE 0x00000100 #define RF7_WILD_OCEAN 0x00000200 #define RF7_WILD_GRASS 0x00000400 #define RF7_WILD_TOWN 0x00000800 #define RF7_DUN_DARKWATER 0x00001000 #define RF7_DUN_LAIR 0x00002000 #define RF7_DUN_TEMPLE 0x00004000 #define RF7_DUN_TOWER 0x00008000 #define RF7_DUN_RUIN 0x00010000 #define RF7_DUN_GRAVE 0x00020000 #define RF7_DUN_CAVERN 0x00040000 #define RF7_DUN_PLANAR 0x00080000 #define RF7_DUN_HELL 0x00100000 #define RF7_DUN_HORROR 0x00200000 #define RF7_DUN_MINE 0x00400000 #define RF7_DUN_CITY 0x00800000 #define RF7_DUN_XTRA1 0x01000000 #define RF7_DUN_XTRA2 0x02000000 #define RF7_DUN_XTRA3 0x04000000 #define RF7_DUN_XTRA4 0x08000000 #define RF7_DUN_XTRA5 0x10000000 #define RF7_DUN_XTRA6 0x20000000 #define RF7_DUN_XTRA7 0x40000000 #define RF7_DUN_XTRA8 0x80000000 /* * Useful flag combinations */ #define RF7_DUNGEON 0xFFFFF000 #define RF7_WILD 0x000007FF /* * Monster drop info */ #define RF8_DROP_CORPSE 0x00000001 #define RF8_DROP_SKELETON 0x00000002 /* * Hack -- choose "intelligent" spells when desperate */ #define RF3_INT_MASK \ 0L #define RF4_INT_MASK \ (RF4_HOLD | RF4_SLOW | RF4_CONF | RF4_BLIND | RF4_SCARE) #define RF5_INT_MASK \ (RF5_BLINK | RF5_TPORT | RF5_TELE_LEVEL | RF5_TELE_AWAY | \ RF5_HEAL | RF5_INVULNER | RF5_HASTE | RF5_TRAPS | RF5_RAISE_DEAD | \ RF5_S_KIN | RF5_S_CYBER | RF5_S_MONSTER | RF5_S_MONSTERS | \ RF5_S_ANT | RF5_S_SPIDER | RF5_S_HOUND | RF5_S_HYDRA | \ RF5_S_ANGEL | RF5_S_DRAGON | RF5_S_UNDEAD | RF5_S_DEMON | \ RF5_S_HI_DRAGON | RF5_S_HI_UNDEAD | RF5_S_AMBERITES | RF5_S_UNIQUE) /* * Hack -- "bolt" spells that may hurt fellow monsters */ #define RF3_BOLT_MASK \ (RF3_ROCKET | RF3_ARROW) #define RF4_BOLT_MASK \ (RF4_BO_ACID | RF4_BO_ELEC | RF4_BO_FIRE | RF4_BO_COLD | \ RF4_BO_POIS | RF4_BO_NETH | RF4_BO_WATE | RF4_BO_MANA | \ RF4_BO_PLAS | RF4_BO_ICEE | RF4_MISSILE) #define RF5_BOLT_MASK \ 0L /* * Spells that hurt the player directly */ #define RF3_ATTACK_MASK \ (RF3_ROCKET | RF3_ARROW | \ RF3_BR_ACID | RF3_BR_ELEC | RF3_BR_FIRE | RF3_BR_COLD | RF3_BR_POIS | \ RF3_BR_NETH | RF3_BR_LITE | RF3_BR_DARK | RF3_BR_CONF | RF3_BR_SOUN | \ RF3_BR_CHAO | RF3_BR_DISE | RF3_BR_NEXU | RF3_BR_TIME | RF3_BR_INER | \ RF3_BR_GRAV | RF3_BR_SHAR | RF3_BR_PLAS | RF3_BR_WALL | RF3_BR_MANA | \ RF3_BA_NUKE | RF3_BR_NUKE | RF3_BA_CHAO | RF3_BR_DISI) #define RF4_ATTACK_MASK \ (RF4_BA_ACID | RF4_BA_ELEC | RF4_BA_FIRE | RF4_BA_COLD | RF4_BA_POIS | \ RF4_BA_NETH | RF4_BA_WATE | RF4_BA_MANA | RF4_BA_DARK | \ RF4_MIND_BLAST | RF4_BRAIN_SMASH | RF4_CAUSE_1 | RF4_CAUSE_2 | \ RF4_CAUSE_3 | RF4_CAUSE_4 | RF4_BO_ACID | RF4_BO_ELEC | RF4_BO_FIRE | \ RF4_BO_COLD | RF4_BO_POIS | RF4_BO_NETH | RF4_BO_WATE | RF4_BO_MANA | \ RF4_BO_PLAS | RF4_BO_ICEE | RF4_MISSILE) #define RF5_ATTACK_MASK \ (RF5_HAND_DOOM) /* * Spells that allow the caster to escape */ #define RF3_ESCAPE_MASK \ (0L) #define RF4_ESCAPE_MASK \ (0L) #define RF5_ESCAPE_MASK \ (RF5_BLINK | RF5_TPORT | RF5_TELE_AWAY | RF5_TELE_LEVEL) /* * Hack -- 'ball' spells that may hurt friends */ #define RF3_BALL_MASK \ (RF3_ROCKET | RF3_BR_ACID | RF3_BR_ELEC | RF3_BR_FIRE | \ RF3_BR_COLD | RF3_BR_POIS | RF3_BR_NETH | RF3_BR_LITE | \ RF3_BR_DARK | RF3_BR_CONF | RF3_BR_SOUN | RF3_BR_CHAO | \ RF3_BR_DISE | RF3_BR_NEXU | RF3_BR_SHAR | \ RF3_BR_SOUN | RF3_BR_TIME | RF3_BR_INER | RF3_BR_GRAV | \ RF3_BR_PLAS | RF3_BR_WALL | RF3_BR_MANA | RF3_BA_NUKE | \ RF3_BR_NUKE | RF3_BA_CHAO | RF3_BR_DISI) #define RF4_BALL_MASK \ (RF4_BA_ACID | RF4_BA_ELEC | RF4_BA_FIRE | RF4_BA_COLD | \ RF4_BA_NETH | RF4_BA_DARK | RF4_BA_WATE | RF4_BA_MANA) #define RF5_BALL_MASK \ 0L /* Hack -- summon spells */ #define RF3_SUMMON_MASK \ 0L #define RF4_SUMMON_MASK \ 0L #define RF5_SUMMON_MASK \ (RF5_S_KIN | RF5_S_CYBER | RF5_S_MONSTER | RF5_S_MONSTERS | RF5_S_ANT | \ RF5_S_SPIDER | RF5_S_HOUND | RF5_S_HYDRA | RF5_S_ANGEL | RF5_S_DEMON | \ RF5_S_UNDEAD | RF5_S_DRAGON | RF5_S_HI_UNDEAD | RF5_S_HI_DRAGON | \ RF5_S_AMBERITES | RF5_S_UNIQUE) /* * Spells that improve the caster's tactical position */ #define RF3_TACTIC_MASK \ (0L) #define RF4_TACTIC_MASK \ (0L) #define RF5_TACTIC_MASK \ (RF5_BLINK) /* * Annoying spells */ #define RF3_ANNOY_MASK \ (RF3_SHRIEK | RF3_ELDRITCH_HORROR) #define RF4_ANNOY_MASK \ (RF4_DRAIN_MANA | RF4_MIND_BLAST | RF4_BRAIN_SMASH | \ RF4_CAUSE_1 | RF4_CAUSE_2 | RF4_CAUSE_3 | RF4_CAUSE_4 | \ RF4_SCARE | RF4_BLIND | RF4_CONF | RF4_SLOW | RF4_HOLD) #define RF5_ANNOY_MASK \ (RF5_TELE_TO | RF5_DARKNESS | RF5_TRAPS | RF5_FORGET | RF5_RAISE_DEAD) /* * Spells that increase the caster's relative speed */ #define RF3_HASTE_MASK \ (0L) #define RF4_HASTE_MASK \ (RF4_SLOW | RF4_HOLD) #define RF5_HASTE_MASK \ (RF5_HASTE) /* * Spells that give invulnerability */ #define RF3_INVULN_MASK \ (0L) #define RF4_INVULN_MASK \ (0L) #define RF5_INVULN_MASK \ (RF5_INVULNER) /* * Healing spells */ #define RF3_HEAL_MASK \ (0L) #define RF4_HEAL_MASK \ (0L) #define RF5_HEAL_MASK \ (RF5_HEAL) /* * Innate spell-like effects */ #define RF3_INNATE_MASK \ (RF3_SHRIEK | RF3_ELDRITCH_HORROR | RF3_ARROW | \ RF3_BR_ACID | RF3_BR_ELEC | RF3_BR_FIRE | \ RF3_BR_COLD | RF3_BR_POIS | RF3_BR_NETH | RF3_BR_LITE | RF3_BR_DARK | \ RF3_BR_CONF | RF3_BR_SOUN | RF3_BR_CHAO | RF3_BR_DISE | RF3_BR_NEXU | \ RF3_BR_TIME | RF3_BR_INER | RF3_BR_GRAV | RF3_BR_SHAR | RF3_BR_PLAS | \ RF3_BR_WALL | RF3_BR_MANA | RF3_BR_NUKE | RF3_BR_DISI) #define RF4_INNATE_MASK \ (0L) #define RF5_INNATE_MASK \ (0L) /* * Breath mask */ #define RF3_BREATHS \ (RF3_BR_ACID | RF3_BR_ELEC | RF3_BR_FIRE | RF3_BR_COLD | RF3_BR_POIS | \ RF3_BR_NETH | RF3_BR_LITE | RF3_BR_DARK | RF3_BR_CONF | RF3_BR_SOUN | \ RF3_BR_CHAO | RF3_BR_DISE | RF3_BR_NEXU | RF3_BR_TIME | RF3_BR_INER | \ RF3_BR_GRAV | RF3_BR_SHAR | RF3_BR_PLAS | RF3_BR_WALL | RF3_BR_MANA | \ RF3_BR_NUKE | RF3_BR_DISI) #define RF_UNIQUE 0, RF0_UNIQUE #define RF_QUESTOR 0, RF0_QUESTOR #define RF_MALE 0, RF0_MALE #define RF_FEMALE 0, RF0_FEMALE #define RF_CHAR_CLEAR 0, RF0_CHAR_CLEAR #define RF_CHAR_MIMIC 0, RF0_CHAR_MIMIC #define RF_ATTR_CLEAR 0, RF0_ATTR_CLEAR #define RF_ATTR_MULTI 0, RF0_ATTR_MULTI #define RF_FORCE_DEPTH 0, RF0_FORCE_DEPTH #define RF_FORCE_MAXHP 0, RF0_FORCE_MAXHP #define RF_FORCE_SLEEP 0, RF0_FORCE_SLEEP #define RF_FORCE_EXTRA 0, RF0_FORCE_EXTRA #define RF_XXX_1 0, RF0_XXX_1 #define RF_FRIENDS 0, RF0_FRIENDS #define RF_ESCORT 0, RF0_ESCORT #define RF_ESCORTS 0, RF0_ESCORTS #define RF_NEVER_BLOW 0, RF0_NEVER_BLOW #define RF_NEVER_MOVE 0, RF0_NEVER_MOVE #define RF_RAND_25 0, RF0_RAND_25 #define RF_RAND_50 0, RF0_RAND_50 #define RF_ONLY_GOLD 0, RF0_ONLY_GOLD #define RF_ONLY_ITEM 0, RF0_ONLY_ITEM #define RF_DROP_60 0, RF0_DROP_60 #define RF_DROP_90 0, RF0_DROP_90 #define RF_DROP_1D2 0, RF0_DROP_1D2 #define RF_DROP_2D2 0, RF0_DROP_2D2 #define RF_DROP_3D2 0, RF0_DROP_3D2 #define RF_DROP_4D2 0, RF0_DROP_4D2 #define RF_DROP_GOOD 0, RF0_DROP_GOOD #define RF_DROP_GREAT 0, RF0_DROP_GREAT #define RF_DROP_USEFUL 0, RF0_DROP_USEFUL #define RF_DROP_CHOSEN 0, RF0_DROP_CHOSEN /* * New monster race bit flags */ #define RF_STUPID 1, RF1_STUPID #define RF_SMART 1, RF1_SMART #define RF_CAN_SPEAK 1, RF1_CAN_SPEAK #define RF_REFLECTING 1, RF1_REFLECTING #define RF_INVISIBLE 1, RF1_INVISIBLE #define RF_COLD_BLOOD 1, RF1_COLD_BLOOD #define RF_EMPTY_MIND 1, RF1_EMPTY_MIND #define RF_WEIRD_MIND 1, RF1_WEIRD_MIND #define RF_MULTIPLY 1, RF1_MULTIPLY #define RF_REGENERATE 1, RF1_REGENERATE #define RF_SHAPECHANGER 1, RF1_SHAPECHANGER #define RF_ATTR_ANY 1, RF1_ATTR_ANY #define RF_POWERFUL 1, RF1_POWERFUL #define RF_XXX_2 1, RF1_XXX_2 #define RF_AURA_FIRE 1, RF1_AURA_FIRE #define RF_AURA_ELEC 1, RF1_AURA_ELEC #define RF_OPEN_DOOR 1, RF1_OPEN_DOOR #define RF_BASH_DOOR 1, RF1_BASH_DOOR #define RF_PASS_WALL 1, RF1_PASS_WALL #define RF_KILL_WALL 1, RF1_KILL_WALL #define RF_MOVE_BODY 1, RF1_MOVE_BODY #define RF_KILL_BODY 1, RF1_KILL_BODY #define RF_TAKE_ITEM 1, RF1_TAKE_ITEM #define RF_KILL_ITEM 1, RF1_KILL_ITEM #define RF_BRAIN_1 1, RF1_BRAIN_1 #define RF_BRAIN_2 1, RF1_BRAIN_2 #define RF_BRAIN_3 1, RF1_BRAIN_3 #define RF_BRAIN_4 1, RF1_BRAIN_4 #define RF_BRAIN_5 1, RF1_BRAIN_5 #define RF_BRAIN_6 1, RF1_BRAIN_6 #define RF_BRAIN_7 1, RF1_BRAIN_7 #define RF_QUANTUM 1, RF1_QUANTUM /* * New monster race bit flag */ #define RF_ORC 2, RF2_ORC #define RF_TROLL 2, RF2_TROLL #define RF_GIANT 2, RF2_GIANT #define RF_DRAGON 2, RF2_DRAGON #define RF_DEMON 2, RF2_DEMON #define RF_UNDEAD 2, RF2_UNDEAD #define RF_EVIL 2, RF2_EVIL #define RF_ANIMAL 2, RF2_ANIMAL #define RF_AMBERITE 2, RF2_AMBERITE #define RF_GOOD 2, RF2_GOOD #define RF_AURA_COLD 2, RF2_AURA_COLD #define RF_NONLIVING 2, RF2_NONLIVING #define RF_HURT_LITE 2, RF2_HURT_LITE #define RF_HURT_ROCK 2, RF2_HURT_ROCK #define RF_HURT_FIRE 2, RF2_HURT_FIRE #define RF_HURT_COLD 2, RF2_HURT_COLD #define RF_IM_ACID 2, RF2_IM_ACID #define RF_IM_ELEC 2, RF2_IM_ELEC #define RF_IM_FIRE 2, RF2_IM_FIRE #define RF_IM_COLD 2, RF2_IM_COLD #define RF_IM_POIS 2, RF2_IM_POIS #define RF_RES_TELE 2, RF2_RES_TELE #define RF_RES_NETH 2, RF2_RES_NETH #define RF_RES_WATE 2, RF2_RES_WATE #define RF_RES_PLAS 2, RF2_RES_PLAS #define RF_RES_NEXU 2, RF2_RES_NEXU #define RF_RES_DISE 2, RF2_RES_DISE #define RF_UNIQUE_7 2, RF2_UNIQUE_7 #define RF_NO_FEAR 2, RF2_NO_FEAR #define RF_NO_STUN 2, RF2_NO_STUN #define RF_NO_CONF 2, RF2_NO_CONF #define RF_NO_SLEEP 2, RF2_NO_SLEEP /* * New monster race bit flag */ #define RF_SHRIEK 3, RF3_SHRIEK #define RF_ELDRITCH_HORROR 3, RF3_ELDRITCH_HORROR #define RF_XXX3 3, RF3_XXX3 #define RF_ROCKET 3, RF3_ROCKET #define RF_ARROW 3, RF3_ARROW #define RF_XXX6X4 3, RF3_XXX6 #define RF_XXX7X4 3, RF3_XXX7 #define RF_XXX8X4 3, RF3_XXX8 #define RF_BR_ACID 3, RF3_BR_ACID #define RF_BR_ELEC 3, RF3_BR_ELEC #define RF_BR_FIRE 3, RF3_BR_FIRE #define RF_BR_COLD 3, RF3_BR_COLD #define RF_BR_POIS 3, RF3_BR_POIS #define RF_BR_NETH 3, RF3_BR_NETH #define RF_BR_LITE 3, RF3_BR_LITE #define RF_BR_DARK 3, RF3_BR_DARK #define RF_BR_CONF 3, RF3_BR_CONF #define RF_BR_SOUN 3, RF3_BR_SOUN #define RF_BR_CHAO 3, RF3_BR_CHAO #define RF_BR_DISE 3, RF3_BR_DISE #define RF_BR_NEXU 3, RF3_BR_NEXU #define RF_BR_TIME 3, RF3_BR_TIME #define RF_BR_INER 3, RF3_BR_INER #define RF_BR_GRAV 3, RF3_BR_GRAV #define RF_BR_SHAR 3, RF3_BR_SHAR #define RF_BR_PLAS 3, RF3_BR_PLAS #define RF_BR_WALL 3, RF3_BR_WALL #define RF_BR_MANA 3, RF3_BR_MANA #define RF_BA_NUKE 3, RF3_BA_NUKE #define RF_BR_NUKE 3, RF3_BR_NUKE #define RF_BA_CHAO 3, RF3_BA_CHAO #define RF_BR_DISI 3, RF3_BR_DISI /* * New monster race bit flag */ #define RF_BA_ACID 4, RF4_BA_ACID #define RF_BA_ELEC 4, RF4_BA_ELEC #define RF_BA_FIRE 4, RF4_BA_FIRE #define RF_BA_COLD 4, RF4_BA_COLD #define RF_BA_POIS 4, RF4_BA_POIS #define RF_BA_NETH 4, RF4_BA_NETH #define RF_BA_WATE 4, RF4_BA_WATE #define RF_BA_MANA 4, RF4_BA_MANA #define RF_BA_DARK 4, RF4_BA_DARK #define RF_DRAIN_MANA 4, RF4_DRAIN_MANA #define RF_MIND_BLAST 4, RF4_MIND_BLAST #define RF_BRAIN_SMASH 4, RF4_BRAIN_SMASH #define RF_CAUSE_1 4, RF4_CAUSE_1 #define RF_CAUSE_2 4, RF4_CAUSE_2 #define RF_CAUSE_3 4, RF4_CAUSE_3 #define RF_CAUSE_4 4, RF4_CAUSE_4 #define RF_BO_ACID 4, RF4_BO_ACID #define RF_BO_ELEC 4, RF4_BO_ELEC #define RF_BO_FIRE 4, RF4_BO_FIRE #define RF_BO_COLD 4, RF4_BO_COLD #define RF_BO_POIS 4, RF4_BO_POIS #define RF_BO_NETH 4, RF4_BO_NETH #define RF_BO_WATE 4, RF4_BO_WATE #define RF_BO_MANA 4, RF4_BO_MANA #define RF_BO_PLAS 4, RF4_BO_PLAS #define RF_BO_ICEE 4, RF4_BO_ICEE #define RF_MISSILE 4, RF4_MISSILE #define RF_SCARE 4, RF4_SCARE #define RF_BLIND 4, RF4_BLIND #define RF_CONF 4, RF4_CONF #define RF_SLOW 4, RF4_SLOW #define RF_HOLD 4, RF4_HOLD /* * New monster race bit flag */ #define RF_HASTE 5, RF5_HASTE #define RF_HAND_DOOM 5, RF5_HAND_DOOM #define RF_HEAL 5, RF5_HEAL #define RF_INVULNER 5, RF5_INVULNER #define RF_BLINK 5, RF5_BLINK #define RF_TPORT 5, RF5_TPORT #define RF_XXX4 5, RF5_XXX4 #define RF_XXX5 5, RF5_XXX5 #define RF_TELE_TO 5, RF5_TELE_TO #define RF_TELE_AWAY 5, RF5_TELE_AWAY #define RF_TELE_LEVEL 5, RF5_TELE_LEVEL #define RF_XXX6 5, RF5_XXX6 #define RF_DARKNESS 5, RF5_DARKNESS #define RF_TRAPS 5, RF5_TRAPS #define RF_FORGET 5, RF5_FORGET #define RF_RAISE_DEAD 5, RF5_RAISE_DEAD #define RF_S_KIN 5, RF5_S_KIN #define RF_S_CYBER 5, RF5_S_CYBER #define RF_S_MONSTER 5, RF5_S_MONSTER #define RF_S_MONSTERS 5, RF5_S_MONSTERS #define RF_S_ANT 5, RF5_S_ANT #define RF_S_SPIDER 5, RF5_S_SPIDER #define RF_S_HOUND 5, RF5_S_HOUND #define RF_S_HYDRA 5, RF5_S_HYDRA #define RF_S_ANGEL 5, RF5_S_ANGEL #define RF_S_DEMON 5, RF5_S_DEMON #define RF_S_UNDEAD 5, RF5_S_UNDEAD #define RF_S_DRAGON 5, RF5_S_DRAGON #define RF_S_HI_UNDEAD 5, RF5_S_HI_UNDEAD #define RF_S_HI_DRAGON 5, RF5_S_HI_DRAGON #define RF_S_AMBERITES 5, RF5_S_AMBERITES #define RF_S_UNIQUE 5, RF5_S_UNIQUE /* * New monster race bit flag */ #define RF_AQUATIC 6, RF6_AQUATIC #define RF_CAN_SWIM 6, RF6_CAN_SWIM #define RF_CAN_FLY 6, RF6_CAN_FLY #define RF_FRIENDLY 6, RF6_FRIENDLY #define RF_SILLY 6, RF6_SILLY #define RF_LITE_1 6, RF6_LITE_1 #define RF_LITE_2 6, RF6_LITE_2 #define RF_LIBRARY 6, RF6_LIBRARY /* * Monster race wilderness flags */ #define RF_WILD_FOREST1 7, RF7_WILD_FOREST1 #define RF_WILD_FOREST2 7, RF7_WILD_FOREST2 #define RF_WILD_MOUNT1 7, RF7_WILD_MOUNT1 #define RF_WILD_MOUNT2 7, RF7_WILD_MOUNT2 #define RF_WILD_WASTE1 7, RF7_WILD_WASTE1 #define RF_WILD_WASTE2 7, RF7_WILD_WASTE2 #define RF_WILD_SWAMP1 7, RF7_WILD_SWAMP1 #define RF_WILD_SWAMP2 7, RF7_WILD_SWAMP2 #define RF_WILD_SHORE 7, RF7_WILD_SHORE #define RF_WILD_OCEAN 7, RF7_WILD_OCEAN #define RF_WILD_GRASS 7, RF7_WILD_GRASS #define RF_WILD_TOWN 7, RF7_WILD_TOWN #define RF_DUN_DARKWATER 7, RF7_DUN_DARKWATER #define RF_DUN_LAIR 7, RF7_DUN_LAIR #define RF_DUN_TEMPLE 7, RF7_DUN_TEMPLE #define RF_DUN_TOWER 7, RF7_DUN_TOWER #define RF_DUN_RUIN 7, RF7_DUN_RUIN #define RF_DUN_GRAVE 7, RF7_DUN_GRAVE #define RF_DUN_CAVERN 7, RF7_DUN_CAVERN #define RF_DUN_PLANAR 7, RF7_DUN_PLANAR #define RF_DUN_HELL 7, RF7_DUN_HELL #define RF_DUN_HORROR 7, RF7_DUN_HORROR #define RF_DUN_MINE 7, RF7_DUN_MINE #define RF_DUN_CITY 7, RF7_DUN_CITY #define RF_DUN_XTRA1 7, RF7_DUN_XTRA1 #define RF_DUN_XTRA2 7, RF7_DUN_XTRA2 #define RF_DUN_XTRA3 7, RF7_DUN_XTRA3 #define RF_DUN_XTRA4 7, RF7_DUN_XTRA4 #define RF_DUN_XTRA5 7, RF7_DUN_XTRA5 #define RF_DUN_XTRA6 7, RF7_DUN_XTRA6 #define RF_DUN_XTRA7 7, RF7_DUN_XTRA7 #define RF_DUN_XTRA8 7, RF7_DUN_XTRA8 #define RF_DUNGEON 7, RF7_DUNGEON #define RF_WILD 7, RF7_WILD #define RF_DROP_CORPSE 8, RF8_DROP_CORPSE #define RF_DROP_SKELETON 8, RF8_DROP_SKELETON /* * Flag manipulation macros */ #ifdef _MSC_VER /* Hack - VC++ doesn't expand preprocessor macros properly */ static __inline bool FLAG_AUX(const u32b *flags, int num, u32b mask) { return((flags[num] & mask) != 0); } static __inline void SET_FLAG_AUX(u32b *flags, int num, u32b mask) { flags[num] |= mask; } static __inline void COPY_FLAG_AUX(const u32b *flags1, u32b *flags2, int num, u32b mask) { flags2[num] |= flags1[num] & mask; } #else /* _MSC_VER */ #define FLAG_AUX(A, NUM, MASK) (((A)[NUM] & (MASK)) != 0) #define SET_FLAG_AUX(A, NUM, MASK) ((A)[NUM] |= (MASK)) #define COPY_FLAG_AUX(A1, A2, NUM, MASK) ((A2)[NUM] |= ((A1)[NUM] & (MASK))) #endif /* _MSC_VER */ #define FLAG(P, F) FLAG_AUX((P)->flags, F) #define SET_FLAG(P, F) SET_FLAG_AUX((P)->flags, F) #define COPY_FLAG(P1, P2, F) COPY_FLAG_AUX((P1)->flags, (P2)->flags, F) #define KN_FLAG(P, F) FLAG_AUX((P)->kn_flags, F) /* Skills */ #define MAX_SKILL 10 #define SKILL_DIS 0 /* Skill: Disarming */ #define SKILL_DEV 1 /* Skill: Magic Devices */ #define SKILL_SAV 2 /* Skill: Saving throw */ #define SKILL_STL 3 /* Skill: Stealth factor */ #define SKILL_SNS 4 /* Skill: Sensing ability */ #define SKILL_FOS 5 /* Skill: Searching frequency */ #define SKILL_THN 6 /* Skill: To hit (normal) */ #define SKILL_THB 7 /* Skill: To hit (shooting) */ #define SKILL_THT 8 /* Skill: To hit (throwing) */ #define SKILL_DIG 9 /* Skill: Digging */ /*** Menu Stuff ***/ /* Menu seperator */ #define MENU_SEPERATOR {"", NULL, NULL, 0x00} /* Menu terminator */ #define MENU_END {NULL, NULL, NULL, 0x00} #define MN_ACTIVE 0x01 /* Available to choose */ #define MN_SELECT 0x02 /* Can 'select' action */ #define MN_CLEAR 0x04 /* Clear screen before calling */ /*** Option Definitions ***/ /* * Option indexes (offsets) * * These values are hard-coded by savefiles (and various pieces of code). */ #define OPT_MAX 256 #define OPT_PLAYER 192 #define OPT_BIRTH 32 #define OPT_SERVER 32 #define OPT_FLAG_BIRTH 0x01 #define OPT_FLAG_SERVER 0x02 #define OPT_FLAG_PLAYER 0x04 #define OPT_BIRTH_PAGE 6 /* Option set 0 */ #define rogue_like_commands p_ptr->options[0] #define quick_messages p_ptr->options[1] /* {TRUE, 0, NULL, "Number 2" }, p_ptr->options[2] */ #define carry_query_flag p_ptr->options[3] #define use_old_target p_ptr->options[4] #define always_pickup p_ptr->options[5] /* {TRUE, 0, NULL, "Number 6" }, p_ptr->options[6] */ #define depth_in_feet p_ptr->options[7] /* {TRUE, 0, NULL, "Number 8" }, p_ptr->options[8] */ /* {FALSE, 0, NULL, "Number 9" }, p_ptr->options[9] */ #define show_labels p_ptr->options[10] #define show_weights p_ptr->options[11] #define view_monster_grids p_ptr->options[12] #define toggle_xp p_ptr->options[13] #define ring_bell p_ptr->options[14] #define use_color p_ptr->options[15] #define find_ignore_stairs p_ptr->options[16] #define find_ignore_doors p_ptr->options[17] #define find_cut p_ptr->options[18] #define find_examine p_ptr->options[19] #define disturb_view p_ptr->options[20] #define disturb_near p_ptr->options[21] #define disturb_panel p_ptr->options[22] #define disturb_state p_ptr->options[23] #define disturb_minor p_ptr->options[24] #define disturb_other p_ptr->options[25] #define disturb_traps p_ptr->options[26] #define auto_more p_ptr->options[27] #define last_words p_ptr->options[28] #define speak_unique p_ptr->options[29] #define small_levels svr_ptr->options[0] /* {TRUE, 0, NULL, "Number 31" }, p_ptr->options[30] */ /* Option set 1 */ /* {TRUE, 0, NULL, "Number 32" }, svr_ptr->options[1] */ /* {TRUE, 0, NULL, "Number 33" }, svr_ptr->options[2] */ /* {TRUE, 0, NULL, "Number 34" }, svr_ptr->options[3] */ /* {TRUE, 0, NULL, "Number 35" }, svr_ptr->options[4] */ /* {TRUE, 0, NULL, "Number 36" }, svr_ptr->options[5] */ #define expand_list svr_ptr->options[6] /* {TRUE, 0, NULL, "Number 38" }, p_ptr->options[31] */ #define view_torch_grids p_ptr->options[32] /* {TRUE, 0, NULL, "Number 40" }, svr_ptr->options[7] */ #define dungeon_stair svr_ptr->options[8] /* {TRUE, 0, NULL, "Number 42" }, svr_ptr->options[9] */ /* {TRUE, 0, NULL, "Number 43" }, svr_ptr->options[10] */ /* {TRUE, 0, NULL, "Number 44" }, svr_ptr->options[11] */ /* {TRUE, 0, NULL, "Number 45" }, svr_ptr->options[12] */ #define smart_packs svr_ptr->options[13] /* {TRUE, 0, NULL, "Number 47" }, svr_ptr->options[14] */ /* {TRUE, 0, NULL, "Number 48" }, p_ptr->options[33] */ /* {TRUE, 0, NULL, "Number 49" }, p_ptr->options[34] */ /* {TRUE, 0, NULL, "Number 50" }, p_ptr->options[35] */ /* {TRUE, 0, NULL, "Number 51" }, p_ptr->options[36] */ #define flush_failure p_ptr->options[37] #define flush_disturb p_ptr->options[38] /* {FALSE, 0, NULL, "Number 54" }, p_ptr->options[39] */ #define fresh_before p_ptr->options[40] #define fresh_after p_ptr->options[41] #define emergency_stop p_ptr->options[42] /* {FALSE, 0, NULL, "Number 57" }, p_ptr->options[42] */ #define compress_savefile p_ptr->options[43] #define hilite_player p_ptr->options[44] #define view_yellow_lite p_ptr->options[45] #define view_bright_lite p_ptr->options[46] #define view_granite_lite p_ptr->options[47] #define view_special_lite p_ptr->options[48] /* Option Set 2 */ #define view_player_colour p_ptr->options[49] /* {TRUE, 0, NULL, "Number 65" }, p_ptr->options[50] */ /* {TRUE, 0, NULL, "Number 66" }, p_ptr->options[51] */ /* {TRUE, 0, NULL, "Number 67" }, p_ptr->options[52] */ /* {TRUE, 0, NULL, "Number 68" }, p_ptr->options[53] */ /* {TRUE, 0, NULL, "Number 69" }, p_ptr->options[54] */ /* {TRUE, 0, NULL, "Number 70" }, p_ptr->options[55] */ /* {TRUE, 0, NULL, "Number 71" }, p_ptr->options[56] */ /* {TRUE, 0, NULL, "Number 72" }, p_ptr->options[57] */ /* {TRUE, 0, NULL, "Number 73" }, p_ptr->options[58] */ /* {TRUE, 0, NULL, "Number 74" }, p_ptr->options[59] */ /* {TRUE, 0, NULL, "Number 75" }, p_ptr->options[60] */ /* {TRUE, 0, NULL, "Number 76" }, p_ptr->options[61] */ /* {TRUE, 0, NULL, "Number 77" }, p_ptr->options[62] */ /* {TRUE, 0, NULL, "Number 78" }, p_ptr->options[63] */ /* {TRUE, 0, NULL, "Number 79" }, p_ptr->options[64] */ /* {TRUE, 0, NULL, "Number 80" }, p_ptr->options[65] */ /* {TRUE, 0, NULL, "Number 81" }, p_ptr->options[66] */ /* {TRUE, 0, NULL, "Number 82" }, p_ptr->options[67] */ /* {TRUE, 0, NULL, "Number 83" }, p_ptr->options[68] */ /* {TRUE, 0, NULL, "Number 84" }, p_ptr->options[69] */ /* {TRUE, 0, NULL, "Number 85" }, p_ptr->options[70] */ /* {TRUE, 0, NULL, "Number 86" }, p_ptr->options[71] */ /* {TRUE, 0, NULL, "Number 87" }, p_ptr->options[72] */ /* {TRUE, 0, NULL, "Number 88" }, p_ptr->options[73] */ /* {TRUE, 0, NULL, "Number 89" }, p_ptr->options[74] */ /* {TRUE, 0, NULL, "Number 90" }, p_ptr->options[75] */ /* {TRUE, 0, NULL, "Number 91" }, p_ptr->options[76] */ /* {TRUE, 0, NULL, "Number 92" }, p_ptr->options[77] */ /* {TRUE, 0, NULL, "Number 93" }, p_ptr->options[78] */ /* {TRUE, 0, NULL, "Number 94" }, p_ptr->options[79] */ /* {TRUE, 0, NULL, "Number 95" }, p_ptr->options[80] */ /* Option Set 3 */ /* {TRUE, 0, NULL, "Number 96" }, p_ptr->options[81] */ /* {TRUE, 0, NULL, "Number 97" }, p_ptr->options[82] */ /* {TRUE, 0, NULL, "Number 98" }, p_ptr->options[83] */ /* {TRUE, 0, NULL, "Number 99" }, p_ptr->options[84] */ /* {TRUE, 0, NULL, "Number 100" }, p_ptr->options[85] */ /* {TRUE, 0, NULL, "Number 101" }, p_ptr->options[86] */ /* {TRUE, 0, NULL, "Number 102" }, p_ptr->options[87] */ /* {TRUE, 0, NULL, "Number 103" }, p_ptr->options[88] */ /* {TRUE, 0, NULL, "Number 104" }, p_ptr->options[89] */ /* {TRUE, 0, NULL, "Number 105" }, p_ptr->options[90] */ /* {TRUE, 0, NULL, "Number 106" }, p_ptr->options[91] */ /* {TRUE, 0, NULL, "Number 107" }, p_ptr->options[92] */ /* {TRUE, 0, NULL, "Number 108" }, p_ptr->options[93] */ /* {TRUE, 0, NULL, "Number 109" }, p_ptr->options[94] */ /* {TRUE, 0, NULL, "Number 110" }, p_ptr->options[95] */ /* {TRUE, 0, NULL, "Number 111" }, p_ptr->options[96] */ /* {TRUE, 0, NULL, "Number 112" }, p_ptr->options[97] */ /* {TRUE, 0, NULL, "Number 113" }, p_ptr->options[98] */ /* {TRUE, 0, NULL, "Number 114" }, p_ptr->options[99] */ /* {TRUE, 0, NULL, "Number 115" }, p_ptr->options[100] */ /* {TRUE, 0, NULL, "Number 116" }, p_ptr->options[101] */ /* {TRUE, 0, NULL, "Number 117" }, p_ptr->options[102] */ /* {TRUE, 0, NULL, "Number 118" }, p_ptr->options[103] */ /* {TRUE, 0, NULL, "Number 119" }, p_ptr->options[104] */ /* {TRUE, 0, NULL, "Number 120" }, p_ptr->options[105] */ /* {TRUE, 0, NULL, "Number 121" }, p_ptr->options[106] */ /* {TRUE, 0, NULL, "Number 122" }, p_ptr->options[107 */ /* {TRUE, 0, NULL, "Number 123" }, p_ptr->options[108] */ /* {TRUE, 0, NULL, "Number 124" }, p_ptr->options[109] */ /* {TRUE, 0, NULL, "Number 125" }, p_ptr->options[110] */ /* {TRUE, 0, NULL, "Number 126" }, p_ptr->options[111] */ /* {TRUE, 0, NULL, "Number 127" }, p_ptr->options[112] */ /* Option Set 4 */ /* {TRUE, 0, NULL, "Number 128" },p_ptr->options[113] */ /* {TRUE, 0, NULL, "Number 129" },p_ptr->options[114] */ /* {TRUE, 0, NULL, "Number 130" },p_ptr->options[115] */ /* {TRUE, 0, NULL, "Number 131" },p_ptr->options[116] */ /* {TRUE, 0, NULL, "Number 132" },p_ptr->options[117] */ /* {TRUE, 0, NULL, "Number 133" },p_ptr->options[118] */ /* {TRUE, 0, NULL, "Number 134" },p_ptr->options[119] */ /* {TRUE, 0, NULL, "Number 135" },p_ptr->options[120] */ /* {TRUE, 0, NULL, "Number 136" },p_ptr->options[121] */ /* {TRUE, 0, NULL, "Number 137" },p_ptr->options[122] */ /* {TRUE, 0, NULL, "Number 138" },p_ptr->options[123] */ /* {TRUE, 0, NULL, "Number 139" },p_ptr->options[124] */ /* {TRUE, 0, NULL, "Number 140" },p_ptr->options[125] */ /* {TRUE, 0, NULL, "Number 141" },p_ptr->options[126] */ /* {TRUE, 0, NULL, "Number 142" },p_ptr->options[127] */ /* {TRUE, 0, NULL, "Number 143" },p_ptr->options[128] */ /* {TRUE, 0, NULL, "Number 144" },p_ptr->options[129] */ /* {TRUE, 0, NULL, "Number 145" },p_ptr->options[130] */ /* {TRUE, 0, NULL, "Number 146" },p_ptr->options[131] */ /* {TRUE, 0, NULL, "Number 147" },p_ptr->options[132] */ /* {TRUE, 0, NULL, "Number 148" },p_ptr->options[133] */ /* {TRUE, 0, NULL, "Number 149" },p_ptr->options[134] */ /* {TRUE, 0, NULL, "Number 150" },p_ptr->options[135] */ /* {TRUE, 0, NULL, "Number 151" },p_ptr->options[136] */ /* {TRUE, 0, NULL, "Number 152" },p_ptr->options[137] */ /* {TRUE, 0, NULL, "Number 153" },p_ptr->options[138] */ /* {TRUE, 0, NULL, "Number 154" },p_ptr->options[139] */ /* {TRUE, 0, NULL, "Number 155" },p_ptr->options[140] */ /* {TRUE, 0, NULL, "Number 156" },p_ptr->options[141] */ /* {TRUE, 0, NULL, "Number 157" },p_ptr->options[142] */ /* {TRUE, 0, NULL, "Number 158" },p_ptr->options[143] */ /* {TRUE, 0, NULL, "Number 159" },p_ptr->options[144] */ /* Option Set 5 */ /* {TRUE, 0, NULL, "Number 160" }, p_ptr->options[145] */ #define plain_descriptions p_ptr->options[146] #define stupid_monsters p_ptr->birth[0] #define auto_destroy p_ptr->options[147] #define confirm_wear p_ptr->options[148] /* {FALSE, 0, NULL, "Number 165" }, p_ptr->options[149] */ #define easy_open p_ptr->options[150] #define easy_disarm p_ptr->options[151] #define easy_floor p_ptr->options[152] /* {TRUE, 0, NULL, "Number 169" }, p_ptr->options[153] */ #define center_player p_ptr->options[154] #define avoid_center p_ptr->options[155] /* {TRUE, 0, NULL, "Number 172" }, p_ptr->options[156] */ #define limit_messages p_ptr->options[157] #define check_transaction p_ptr->options[158] /* {TRUE, 0, NULL, "Number 175" }, p_ptr->options[159] */ /* {TRUE, 0, NULL, "Number 176" }, p_ptr->options[160] */ /* {TRUE, 0, NULL, "Number 177" }, p_ptr->options[161] */ /* {TRUE, 0, NULL, "Number 178" }, p_ptr->options[162] */ /* {TRUE, 0, NULL, "Number 179" }, p_ptr->options[163] */ /* {TRUE, 0, NULL, "Number 180" }, p_ptr->options[164] */ /* {TRUE, 0, NULL, "Number 181" }, p_ptr->options[165] */ /* {TRUE, 0, NULL, "Number 182" }, p_ptr->options[166] */ /* {TRUE, 0, NULL, "Number 183" }, p_ptr->options[167] */ /* {TRUE, 0, NULL, "Number 184" }, p_ptr->options[168] */ /* {TRUE, 0, NULL, "Number 185" }, p_ptr->options[169] */ /* {TRUE, 0, NULL, "Number 186" }, p_ptr->options[170] */ /* {TRUE, 0, NULL, "Number 187" }, p_ptr->options[171] */ /* {TRUE, 0, NULL, "Number 188" }, p_ptr->options[172] */ /* {TRUE, 0, NULL, "Number 189" }, p_ptr->options[173] */ /* {TRUE, 0, NULL, "Number 190" }, p_ptr->options[174] */ /* {TRUE, 0, NULL, "Number 191" }, p_ptr->options[175] */ /* Option Set 6 */ #define vanilla_town p_ptr->birth[1] /* {TRUE, 0, NULL, "Number 193" }, p_ptr->options[176] */ #define ironman_shops p_ptr->birth[2] #define ironman_small_levels p_ptr->birth[3] #define ironman_downward p_ptr->birth[4] /* {TRUE, 0, NULL, "Number 197" }, p_ptr->birth[5] */ /* {TRUE, 0, NULL, "Number 198" }, p_ptr->birth[6] */ /* {TRUE, 0, NULL, "Number 199" }, p_ptr->birth[7] */ /* {TRUE, 0, NULL, "Number 200" }, p_ptr->birth[8] */ /* {TRUE, 0, NULL, "Number 201" }, p_ptr->birth[9] */ /* {TRUE, 0, NULL, "Number 202" }, p_ptr->birth[10] */ #define munchkin_death p_ptr->birth[11] /* {TRUE, 0, NULL, "Number 204" }, p_ptr->birth[12] */ /* {TRUE, 0, NULL, "Number 205" }, p_ptr->birth[13] */ #define preserve_mode p_ptr->birth[14] #define autoroller p_ptr->birth[15] #define point_based p_ptr->birth[16] #define silly_monsters p_ptr->birth[17] #define ironman_nightmare p_ptr->birth[18] /* {TRUE, 0, NULL, "Number 211" }, p_ptr->birth[19] */ /* {TRUE, 0, NULL, "Number 212" }, p_ptr->birth[20] */ /* {TRUE, 0, NULL, "Number 213" }, p_ptr->birth[21] */ /* {TRUE, 0, NULL, "Number 214" }, p_ptr->birth[22] */ /* {TRUE, 0, NULL, "Number 215" }, p_ptr->birth[23] */ /* {TRUE, 0, NULL, "Number 216" }, p_ptr->birth[24] */ /* {TRUE, 0, NULL, "Number 217" }, p_ptr->birth[25] */ /* {TRUE, 0, NULL, "Number 218" }, p_ptr->birth[26] */ /* {TRUE, 0, NULL, "Number 219" }, p_ptr->birth[27] */ /* {TRUE, 0, NULL, "Number 220" }, p_ptr->birth[28] */ /* {TRUE, 0, NULL, "Number 221" }, p_ptr->birth[29] */ /* {TRUE, 0, NULL, "Number 222" }, p_ptr->birth[30] */ /* {TRUE, 0, NULL, "Number 223" }, p_ptr->birth[31] */ /* Option Set 7 */ /* {TRUE, 0, NULL, "Number 224" },svr_ptr->options[15] */ #define monster_light svr_ptr->options[16] /* "Turn on muliplayer client - server code" , svr_ptr->options[17] */ /* {TRUE, 0, NULL, "Number 227" },svr_ptr->options[18] */ /* {TRUE, 0, NULL, "Number 228" },svr_ptr->options[19] */ /* {TRUE, 0, NULL, "Number 229" },svr_ptr->options[20] */ /* {TRUE, 0, NULL, "Number 230" },svr_ptr->options[21] */ /* {TRUE, 0, NULL, "Number 231" },svr_ptr->options[22] */ /* {TRUE, 0, NULL, "Number 232" },svr_ptr->options[23] */ /* {TRUE, 0, NULL, "Number 233" },svr_ptr->options[24] */ /* {TRUE, 0, NULL, "Number 234" },svr_ptr->options[25] */ /* {TRUE, 0, NULL, "Number 235" },svr_ptr->options[26] */ /* {TRUE, 0, NULL, "Number 236" },svr_ptr->options[27] */ /* {TRUE, 0, NULL, "Number 237" },svr_ptr->options[28] */ /* {TRUE, 0, NULL, "Number 238" },svr_ptr->options[29] */ /* {TRUE, 0, NULL, "Number 239" },p_ptr->options[177] */ /* {TRUE, 0, NULL, "Number 240" },p_ptr->options[178] */ /* {TRUE, 0, NULL, "Number 241" },p_ptr->options[179] */ /* {TRUE, 0, NULL, "Number 242" },p_ptr->options[180] */ /* {TRUE, 0, NULL, "Number 243" },p_ptr->options[181] */ /* {TRUE, 0, NULL, "Number 244" },p_ptr->options[182] */ /* {TRUE, 0, NULL, "Number 245" },p_ptr->options[183] */ /* {TRUE, 0, NULL, "Number 246" },p_ptr->options[184] */ /* {TRUE, 0, NULL, "Number 247" },p_ptr->options[185] */ /* {TRUE, 0, NULL, "Number 248" },p_ptr->options[186] */ /* {TRUE, 0, NULL, "Number 249" },p_ptr->options[187] */ /* {TRUE, 0, NULL, "Number 250" },p_ptr->options[188] */ #define auto_notes p_ptr->options[189] #define take_notes p_ptr->options[190] /* {TRUE, 0, NULL, "Number 253" }, p_ptr->options[191] */ #define testing_stack svr_ptr->options[30] /* {TRUE, 0, NULL, "Number 255" }, svr_ptr->options[31] */ /*** Macro Definitions ***/ /* * Hack -- The main "screen" */ #define term_screen (angband_term[0]) /* * Determine if a given inventory item is "aware" */ #define object_aware_p(T) \ (k_info[(T)->k_idx].aware) /* * Determine if a given inventory item is "tried" */ #define object_tried_p(T) \ (k_info[(T)->k_idx].tried) /* * Determine if a given inventory item is "known" * Test One -- Check for special "known" tag * Test Two -- Check for "Easy Know" + "Aware" */ #define object_known_p(T) \ (((T)->info & (OB_KNOWN)) || \ (k_info[(T)->k_idx].easy_know && k_info[(T)->k_idx].aware)) /* * Is the object fully known? */ #define object_known_full(T) \ ((T)->info & (OB_MENTAL)) /* * Return the "attr" for a given item. * Use "flavor" if available. * Default to user definitions. */ #define object_attr(T) \ ((k_info[(T)->k_idx].flavor) ? \ (misc_to_attr[k_info[(T)->k_idx].flavor]) : \ (k_info[(T)->k_idx].x_attr)) /* * Return the "char" for a given item. * Use "flavor" if available. * Default to user definitions. */ #define object_char(T) \ ((k_info[(T)->k_idx].flavor) ? \ (misc_to_char[k_info[(T)->k_idx].flavor]) : \ (k_info[(T)->k_idx].x_char)) /* * Ego-Items are named, but are not INSTA_ART. */ #define ego_item_p(T) \ ((((T)->xtra_name) && (!((T)->flags[2] & TR2_INSTA_ART))) ? TRUE : FALSE) /* * Cursed items. */ #define cursed_p(T) \ ((T)->flags[2] & (TR2_CURSED)) /* * Iterate over the objects in a list */ #define OBJ_ITT_START(OSTART, O) \ do { \ s16b _this_o_idx, _next_o_idx = 0; \ \ for (_this_o_idx = (OSTART); _this_o_idx; _this_o_idx = _next_o_idx) \ { \ (O) = &o_list[_this_o_idx];\ assert((O)->k_idx); \ \ _next_o_idx = (O)->next_o_idx; #define OBJ_ITT_END \ } \ } while (0) /* * Iterate over the fieldss in a list */ #define FLD_ITT_START(FSTART, F) \ do { \ s16b _this_f_idx, _next_f_idx = 0; \ \ for (_this_f_idx = (FSTART); _this_f_idx; _this_f_idx = _next_f_idx) \ { \ (F) = &fld_list[_this_f_idx];\ assert((F)->t_idx); \ \ _next_f_idx = (F)->next_f_idx; #define FLD_ITT_END \ } \ } while (0) /* * Useful macros for object formatting * (So we use the correct number of arguments) */ #define OBJECT_FMT(O, P, M) \ object_fmt, (O), (P), (M) #define OBJECT_STORE_FMT(O, P, M) \ object_store_fmt, (O), (P), (M) /* Monster name format */ #define MONSTER_FMT(M, P) \ monster_fmt, (M), (P) /* * Test bounds checking */ #ifdef DEBUG_ALPHA #define area(X, Y) \ (assert_exp(in_bounds2((X), (Y))) ? area_aux((X), (Y)): NULL) #define parea(X, Y) \ (assert_exp(in_boundsp((X), (Y))) ? parea_aux((X), (Y)): NULL) #else /* DEBUG_ALPHA */ #define area(X, Y) \ area_aux((X), (Y)) #define parea(X, Y) \ parea_aux((X), (Y)) #endif /* DEBUG_ALPHA */ /* * Determines if a map location is currently "on screen" -RAK- */ #define panel_contains(X,Y) \ (((Y) >= p_ptr->panel_y1) && ((Y) < p_ptr->panel_y2) && \ ((X) >= p_ptr->panel_x1) && ((X) < p_ptr->panel_x2)) /* * Determine if a "legal" grid is a "floor" grid */ #define cave_floor_grid(C) \ (!(f_info[(C)->feat].flags & FF_BLOCK)) /* * Determine if a "legal" grid is a "wall" grid */ #define cave_wall_grid(C) \ (f_info[(C)->feat].flags & FF_BLOCK) /* * A half-blocking grid */ #define cave_semi_grid(C) \ (f_info[(C)->feat].flags & FF_HALF_LOS) /* * True half the time for trees. (Block line of sight half the time.) */ #define cave_half_grid(C) \ (cave_semi_grid(C) && (quick_rand())) /* * Grid will block LOS. */ #define cave_los_grid(C) \ ((cave_floor_grid(C)) || (cave_half_grid(C))) /* * A nice grid for dropping objects */ #define cave_nice_grid(C) \ (!(f_info[(C)->feat].flags & FF_ICKY)) /* * Grid that does not have any objects or "interesting" terrains */ #define cave_clean_grid(C) \ (cave_nice_grid(C) && \ ((C)->o_idx == 0)) /* * Not occupied by a monster */ #define cave_empty_grid(C) \ (cave_floor_grid(C) && !((C)->m_idx)) /* * Grid that is empty of everything interesting */ #define cave_naked_grid(C) \ (cave_nice_grid(C) && \ ((C)->o_idx == 0) && \ ((C)->m_idx == 0) && \ ((C)->fld_idx == 0)) /* * Grid that cannot be destroyed or passed. */ #define cave_perma_grid(C) \ (f_info[(C)->feat].flags & FF_PERM) /* * Pattern grid. */ #define cave_pattern_grid(C) \ (f_info[(C)->feat].flags & FF_PATTERN) /* * Is the grid worth remembering? */ #define cave_mem_grid(C) \ (f_info[(C)->feat].flags & FF_MARK) /* * Determine if a "legal" grid is within "los" of the player * * Note the use of comparison to zero to force a "boolean" result */ #define player_has_los_grid(C) \ (((C)->player & (GRID_VIEW)) != 0) /* * Determine if the player can see a grid * * (The grid is lit + in view) */ #define player_can_see_grid(C) \ (((C)->player & (GRID_SEEN)) != 0) /* * Forget square */ #define forget_grid(C) \ ((C)->feat = FEAT_NONE) /* * Memorise square * * Hack XXX XXX - we test C1->info and C2->player * so we have the correct types. * (Without this check - it would be quite easy * to get the two pointers the wrong way around.) * * We need to lite_spot() as well... */ #define remember_grid(C1, C2) \ (((C2)->feat = (C1)->feat), \ (void) ((C1)->info), \ (void) ((C2)->player)) /* * Create a dungeon region */ #define create_region(R, WID, HGT, FLAGS) \ create_region_aux(&(R)->region, (WID), (HGT), (FLAGS)) /* * Is the monster a pet of the player? */ #define is_pet(T) \ ((bool)(((T)->smart & SM_PET) != 0)) /* * Is the monster friendly toward the player? */ #define is_friendly(T) \ ((bool)(((T)->smart & SM_FRIENDLY) != 0)) /* * Is the monster hostile toward the player? */ #define is_hostile(T) \ ((bool)(!(is_pet(T) || is_friendly(T)))) /* * Helper macro so call path_build() with correct buffer size. */ #define path_make(B, P, F) \ do \ { \ assert(sizeof(B) > sizeof(void*)); \ path_build((B), sizeof(B), (P), (F)); \ } \ while (FALSE) /* * Hack -- Prepare to use the "Secure" routines */ #if defined(SET_UID) && defined(SECURE) extern int PlayerUID; # define getuid() PlayerUID # define geteuid() PlayerUID #endif /*** Color constants ***/ /* * Angband "attributes" (with symbols, and base (R,G,B) codes) * * The "(R,G,B)" codes are given in "fourths" of the "maximal" value, * and should "gamma corrected" on most (non-Macintosh) machines. */ #define TERM_DARK 0 /* 'd' */ /* 0,0,0 */ #define TERM_WHITE 1 /* 'w' */ /* 4,4,4 */ #define TERM_SLATE 2 /* 's' */ /* 2,2,2 */ #define TERM_ORANGE 3 /* 'o' */ /* 4,2,0 */ #define TERM_RED 4 /* 'r' */ /* 3,0,0 */ #define TERM_GREEN 5 /* 'g' */ /* 0,2,1 */ #define TERM_BLUE 6 /* 'b' */ /* 0,0,4 */ #define TERM_UMBER 7 /* 'u' */ /* 2,1,0 */ #define TERM_L_DARK 8 /* 'D' */ /* 1,1,1 */ #define TERM_L_WHITE 9 /* 'W' */ /* 3,3,3 */ #define TERM_VIOLET 10 /* 'v' */ /* 4,0,4 */ #define TERM_YELLOW 11 /* 'y' */ /* 4,4,0 */ #define TERM_L_RED 12 /* 'R' */ /* 4,0,0 */ #define TERM_L_GREEN 13 /* 'G' */ /* 0,4,0 */ #define TERM_L_BLUE 14 /* 'B' */ /* 0,4,4 */ #define TERM_L_UMBER 15 /* 'U' */ /* 3,2,1 */ /* * Colour format specifiers in strings * * Start with $, and then have a character that depends on the * colour. We could also use this techinque for other formatting * specifiers... * * Note we must use 'nice' characters for the specifier because * the formatting routines eat some of the control characters. */ #define CLR_DARK "$A" #define CLR_WHITE "$B" #define CLR_SLATE "$C" #define CLR_ORANGE "$D" #define CLR_RED "$E" #define CLR_GREEN "$F" #define CLR_BLUE "$G" #define CLR_UMBER "$H" #define CLR_L_DARK "$I" #define CLR_L_WHITE "$J" #define CLR_VIOLET "$K" #define CLR_YELLOW "$L" #define CLR_L_RED "$M" #define CLR_L_GREEN "$N" #define CLR_L_BLUE "$O" #define CLR_L_UMBER "$P" #define CLR_SET_DEFAULT "$Q" #define CLR_DEFAULT "$R" /* * Raw message types */ #define MSG_GENERIC 0 #define MSG_HIT 1 #define MSG_MISS 2 #define MSG_FLEE 3 #define MSG_DROP 4 #define MSG_KILL 5 #define MSG_LEVEL 6 #define MSG_DEATH 7 #define MSG_STUDY 8 #define MSG_TELEPORT 9 #define MSG_SHOOT 10 #define MSG_QUAFF 11 #define MSG_ZAP 12 #define MSG_WALK 13 #define MSG_TPOTHER 14 #define MSG_HITWALL 15 #define MSG_EAT 16 #define MSG_STORE1 17 #define MSG_STORE2 18 #define MSG_STORE3 19 #define MSG_STORE4 20 #define MSG_DIG 21 #define MSG_OPENDOOR 22 #define MSG_SHUTDOOR 23 #define MSG_TPLEVEL 24 #define MSG_BELL 25 #define MSG_NOTHING_TO_OPEN 26 #define MSG_LOCKPICK_FAIL 27 #define MSG_STAIRS 28 #define MSG_HITPOINT_WARN 29 #define MSG_MAX 30 /* * 'Magic' macro that changes the default message type. * set_message_type sets a static variable to be T, and * then parses the following string as a format string. */ #define MESSAGE_TYPE(T) "%v", set_message_type, (T) /* * Message types used for msgf() (See util.c) */ #define MSGT_GENERIC MESSAGE_TYPE(0) #define MSGT_HIT MESSAGE_TYPE(1) #define MSGT_MISS MESSAGE_TYPE(2) #define MSGT_FLEE MESSAGE_TYPE(3) #define MSGT_DROP MESSAGE_TYPE(4) #define MSGT_KILL MESSAGE_TYPE(5) #define MSGT_LEVEL MESSAGE_TYPE(6) #define MSGT_DEATH MESSAGE_TYPE(7) #define MSGT_STUDY MESSAGE_TYPE(8) #define MSGT_TELEPORT MESSAGE_TYPE(9) #define MSGT_SHOOT MESSAGE_TYPE(10) #define MSGT_QUAFF MESSAGE_TYPE(11) #define MSGT_ZAP MESSAGE_TYPE(12) #define MSGT_WALK MESSAGE_TYPE(13) #define MSGT_TPOTHER MESSAGE_TYPE(14) #define MSGT_HITWALL MESSAGE_TYPE(15) #define MSGT_EAT MESSAGE_TYPE(16) #define MSGT_STORE1 MESSAGE_TYPE(17) #define MSGT_STORE2 MESSAGE_TYPE(18) #define MSGT_STORE3 MESSAGE_TYPE(19) #define MSGT_STORE4 MESSAGE_TYPE(20) #define MSGT_DIG MESSAGE_TYPE(21) #define MSGT_OPENDOOR MESSAGE_TYPE(22) #define MSGT_SHUTDOOR MESSAGE_TYPE(23) #define MSGT_TPLEVEL MESSAGE_TYPE(24) #define MSGT_BELL MESSAGE_TYPE(25) #define MSGT_NOTHING_TO_OPEN MESSAGE_TYPE(26) #define MSGT_LOCKPICK_FAIL MESSAGE_TYPE(27) #define MSGT_STAIRS MESSAGE_TYPE(28) #define MSGT_HITPOINT_WARN MESSAGE_TYPE(29) /*** Sound constants ***/ /* * Mega-Hack -- some primitive sound support (see "main-win.c") * * Some "sound" constants for "Term_xtra(TERM_XTRA_SOUND, val)" */ #define SOUND_NONE 0 #define SOUND_HIT 1 #define SOUND_MISS 2 #define SOUND_FLEE 3 #define SOUND_DROP 4 #define SOUND_KILL 5 #define SOUND_LEVEL 6 #define SOUND_DEATH 7 #define SOUND_STUDY 8 #define SOUND_TELEPORT 9 #define SOUND_SHOOT 10 #define SOUND_QUAFF 11 #define SOUND_ZAP 12 #define SOUND_WALK 13 #define SOUND_TPOTHER 14 #define SOUND_HITWALL 15 #define SOUND_EAT 16 #define SOUND_STORE1 17 #define SOUND_STORE2 18 #define SOUND_STORE3 19 #define SOUND_STORE4 20 #define SOUND_DIG 21 #define SOUND_OPENDOOR 22 #define SOUND_SHUTDOOR 23 #define SOUND_TPLEVEL 24 #define SOUND_SCROLL 25 #define SOUND_BUY 26 #define SOUND_SELL 27 #define SOUND_WARN 28 #define SOUND_ROCKET 29 /* Somebody's shooting rockets */ #define SOUND_N_KILL 30 /* The player kills a non-living/undead monster */ #define SOUND_U_KILL 31 /* The player kills a unique */ #define SOUND_QUEST 32 /* The player has just completed a quest */ #define SOUND_HEAL 33 /* The player was healed a little bit */ #define SOUND_X_HEAL 34 /* The player was healed full health */ #define SOUND_BITE 35 /* A monster bites you */ #define SOUND_CLAW 36 /* A monster claws you */ #define SOUND_M_SPELL 37 /* A monster casts a miscellaneous spell */ #define SOUND_SUMMON 38 /* A monster casts a summoning spell */ #define SOUND_BREATH 39 /* A monster breathes */ #define SOUND_BALL 40 /* A monster casts a ball / bolt spell */ #define SOUND_M_HEAL 41 /* A monster heals itself somehow */ #define SOUND_ATK_SPELL 42 /* A monster casts a misc. offensive spell */ #define SOUND_EVIL 43 /* Something nasty has just happened! */ #define SOUND_TOUCH 44 /* A monster touches you */ #define SOUND_STING 45 /* A monster stings you */ #define SOUND_CRUSH 46 /* A monster crushes / envelopes you */ #define SOUND_SLIME 47 /* A monster drools/spits/etc on you */ #define SOUND_WAIL 48 /* A monster wails */ #define SOUND_WINNER 49 /* Just won the game! */ #define SOUND_FIRE 50 /* An item was burned */ #define SOUND_ACID 51 /* An item was destroyed by acid */ #define SOUND_ELEC 52 /* An item was destroyed by electricity */ #define SOUND_COLD 53 /* An item was shattered */ #define SOUND_ILLEGAL 54 /* Illegal command attempted */ #define SOUND_FAIL 55 /* Fail to get a spell off / activate an item */ #define SOUND_WAKEUP 56 /* A monster wakes up */ #define SOUND_INVULN 57 /* Invulnerability! */ #define SOUND_FALL 58 /* Falling through a trapdoor... */ #define SOUND_PAIN 59 /* A monster is in pain! */ #define SOUND_DESTITEM 60 /* An item was destroyed by misc. means */ #define SOUND_MOAN 61 /* A monster makes a moan/beg/insult attack */ #define SOUND_SHOW 62 /* A monster makes a "show" attack */ #define SOUND_UNUSED 63 /* (no sound for gaze attacks) */ #define SOUND_EXPLODE 64 /* Something (or somebody) explodes */ /* * Mega-Hack -- maximum known sounds */ #define SOUND_MAX 65 #define V_COMPASSION 1 #define V_HONOUR 2 #define V_JUSTICE 3 #define V_SACRIFICE 4 #define V_KNOWLEDGE 5 #define V_FAITH 6 #define V_ENLIGHTEN 7 #define V_ENCHANT 8 #define V_CHANCE 9 #define V_NATURE 10 #define V_HARMONY 11 #define V_VITALITY 12 #define V_UNLIFE 13 #define V_PATIENCE 14 #define V_TEMPERANCE 15 #define V_DILIGENCE 16 #define V_VALOUR 17 #define V_INDIVIDUALISM 18 #define MAX_VIRTUE 18 /* * Number of virtues the player can have * ToDo: Check if changing this value breaks anything * (apart from savefile compatibility). */ #define MAX_PLAYER_VIRTUES 8 /* * Available graphic modes */ #define GRAPHICS_NONE 0 #define GRAPHICS_ORIGINAL 1 #define GRAPHICS_ADAM_BOLT 2 #define GRAPHICS_DAVID_GERVAIS 3 #define GRAPHICS_ANY 4 #define GRAPHICS_HALF_3D 5 /* * Modes for the random name generator */ #define NAME_DWARF 1 #define NAME_ELF 2 #define NAME_GNOME 3 #define NAME_HOBBIT 4 #define NAME_HUMAN 5 #define NAME_ORC 6 /* * Modes for the tokenizer */ #define TOKENIZE_CHECKQUOTE 0x01 /* Special handling of single quotes */ /* * Automatic note taking types */ #define NOTE_BIRTH 1 #define NOTE_WINNER 2 #define NOTE_SAVE_GAME 3 #define NOTE_ENTER_DUNGEON 4 /* * Field information flags */ #define FIELD_INFO_TEMP 0x0001 /* Temporary field - use counter */ #define FIELD_INFO_FEAT 0x0002 /* Terrain feature based field */ #define FIELD_INFO_VIS 0x0004 /* Has attr / char */ #define FIELD_INFO_MARK 0x0008 /* Known */ #define FIELD_INFO_TRANS 0x0010 /* Tile uses 16x16 transparency effects */ #define FIELD_INFO_NO_LOOK 0x0020 /* Do not describe when looked at */ #define FIELD_INFO_NFT_LOOK 0x0040 /* Do not describe feat when looked at */ #define FIELD_INFO_MERGE 0x0080 /* Merge counter with similar fields */ #define FIELD_INFO_NO_ENTER 0x0100 /* Grid blocks player entry */ #define FIELD_INFO_NO_MAGIC 0x0200 /* Grid blocks magic */ #define FIELD_INFO_NO_OBJCT 0x0400 /* Grid cannot hold objects */ #define FIELD_INFO_PERM 0x0800 /* Grid is not affected by disintegrate */ #define FIELD_INFO_IGNORE 0x1000 /* Grid is below the object layer */ #define FIELD_INFO_NO_MPLACE 0x2000 /* Grid blocks monster placement */ #define FIELD_INFO_DUMMY13 0x4000 #define FIELD_INFO_DUMMY14 0x8000 #define FTYPE_NOTHING 0 #define FTYPE_TRAP 1 #define FTYPE_DOOR 2 #define FTYPE_BUILD 3 #define FTYPE_FEAT 4 #define FTYPE_QUEST 5 #define FTYPE_FIELD 6 #define FTYPE_CORPSE 7 #define FTYPE_MISC 8 /* * Field Actions */ #define FIELD_ACT_INIT 0 /* Initialise the field data */ #define FIELD_ACT_LOAD 1 /* Loading Initialisation */ #define FIELD_ACT_PLAYER_ENTER 2 /* Player walks onto square */ #define FIELD_ACT_PLAYER_ON 3 /* Player is on square */ #define FIELD_ACT_MONSTER_ENTER 4 /* Monster walks onto square */ #define FIELD_ACT_MONSTER_ON 5 /* Monster is on square */ #define FIELD_ACT_OBJECT_DROP 6 /* Object lands on square */ #define FIELD_ACT_OBJECT_ON 7 /* Object is on square */ #define FIELD_ACT_INTERACT 8 /* Type-specific interation */ #define FIELD_ACT_MAGIC_TARGET 9 /* Targeting this square */ #define FIELD_ACT_LOOK 10 /* Hook for name of field when looking */ #define FIELD_ACT_EXIT 11 /* Field is destroyed */ #define FIELD_ACT_MONSTER_AI 12 /* Monster AI hook */ #define FIELD_ACT_SPECIAL 13 /* Special, type specific action */ #define FIELD_ACT_INTERACT_TEST 14 /* Test for type of player interaction */ #define FIELD_ACT_MON_ENTER_TEST 15 /* Monster attempts to enter grid */ #define FIELD_ACT_BUILD_ACT1 16 /* Building prelimiary action */ #define FIELD_ACT_BUILD_ACT2 17 /* Building final action */ #define FIELD_ACT_STORE_ACT1 18 /* Store object antiselection action */ #define FIELD_ACT_STORE_ACT2 19 /* Store object selection action */ #define FIELD_ACT_SB_INIT 20 /* Initialize a store / building */ #define FIELD_ACTION_MAX 21 #define ACT_TUNNEL 0 #define ACT_DISARM 1 #define ACT_OPEN 2 /* To make the declarations in externs.h simpler */ #define DECL_FIELD_ACTION(N) \ extern bool field_action_##N (field_type *f_ptr, va_list vp) /* * Player displays */ #define DISPLAY_PLAYER_STANDARD 0 /* standard display */ #define DISPLAY_PLAYER_SUMMARY 1 /* summary of various things */ #define DISPLAY_PLAYER_FLAG 2 #define DISPLAY_PLAYER_MAX 3 /* Types of object list */ #define LIST_INVEN 1 #define LIST_EQUIP 2 #define LIST_FLOOR 3 #define LIST_STORE 4 #define LIST_HOME 5 /* Locations of the tables on the screen (see ui.c / birth.c) */ #define HEADER_ROW 1 #define QUESTION_ROW 7 #define TABLE_ROW 10 #define INVALID_CHOICE 255 /* * Useful macros for lua interface. */ #define LUA_VAR(A) \ #A, (A) #define LUA_VAR_NAMED(A, N) \ N, (A) #define LUA_RETURN(A) \ #A, &(A) #define LUA_RETURN_NAMED(A, N) \ N, &(A) #define LUA_OBJECT(A) \ "object", "object_type", (void *)(A) #define LUA_OBJECT_NAMED(A, N) \ N, "object_type", (void *)(A) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/externs.h������������������������������������������������������������������������������0000755�0000000�0000000�00000133576�10250356275�014017� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: externs.h */ /* Purpose: extern declarations (variables and functions) */ /* * Note that some files have their own header files * (z-virt.h, z-util.h, z-form.h, term.h, random.h) */ /* * Automatically generated "variable" declarations */ /* tables.c */ extern const s16b ddd[9]; extern const s16b ddx[10]; extern const s16b ddy[10]; extern const s16b ddx_ddd[9]; extern const s16b ddy_ddd[9]; extern const s16b cdd[8]; extern const s16b ddx_cdd[8]; extern const s16b ddy_cdd[8]; extern const char hexsym[16]; extern cptr color_char; extern const byte adj_mag_study[]; extern const byte adj_mag_mana[]; extern const byte adj_mag_fail[]; extern const byte adj_mag_stat[]; extern const byte adj_chr_gold[]; extern const byte adj_int_dev[]; extern const byte adj_wis_sav[]; extern const byte adj_dex_dis[]; extern const byte adj_int_dis[]; extern const byte adj_dex_ta[]; extern const byte adj_str_td[]; extern const byte adj_dex_th[]; extern const byte adj_str_wgt[]; extern const byte adj_str_hold[]; extern const byte adj_str_dig[]; extern const byte adj_str_blow[]; extern const byte adj_dex_blow[]; extern const byte adj_dex_safe[]; extern const byte adj_con_fix[]; extern const byte adj_con_mhp[]; extern const byte blows_table[12][12]; extern cptr owner_names[]; extern cptr owner_suffix[]; extern const byte extract_energy[200]; extern const s32b player_exp[PY_MAX_LEVEL]; extern player_sex sex_info[MAX_SEXES]; extern player_race race_info[MAX_RACES]; extern player_class class_info[MAX_CLASS]; extern player_magic magic_info[MAX_CLASS]; extern const u32b fake_spell_flags[4]; extern const byte realm_choices1[]; extern const byte realm_choices2[]; extern cptr realm_names[]; extern cptr spell_names[7][32]; extern const byte chest_traps[64]; extern cptr player_title[MAX_CLASS][PY_MAX_LEVEL / 5]; extern cptr color_names[16]; extern cptr color_seq[16]; extern cptr msg_names[MSG_MAX]; extern cptr stat_names[A_MAX]; extern cptr stat_names_reduced[A_MAX]; extern cptr window_flag_desc[WINDOW_CHOICE_MAX]; extern const int birth_options[OPT_BIRTH + 1]; extern const int server_options[OPT_BIRTH + 1]; extern cptr chaos_patrons[MAX_PATRON]; extern const int chaos_stats[MAX_PATRON]; extern const int chaos_rewards[MAX_PATRON][20]; extern const martial_arts ma_blows[MAX_MA]; extern cptr game_inscriptions[FEEL_MAX]; extern cptr silly_attacks[MAX_SILLY_ATTACK]; extern const rbm_type rbm_info[MAX_RBM]; extern const mutation_type mutations[MUT_SETS_MAX * MUT_PER_SET]; extern const mutation_type race_powers[MAX_RACE_POWERS]; /* variable.c */ extern cptr copyright[5]; extern byte version_major; extern byte version_minor; extern byte version_patch; extern byte version_extra; extern byte sf_extra; extern u32b sf_version; extern u32b sf_xtra; extern byte z_major; extern byte z_minor; extern byte z_patch; extern u32b sf_when; extern u16b sf_lives; extern u16b sf_saves; extern bool arg_fiddle; extern bool arg_wizard; extern bool arg_sound; extern byte arg_graphics; extern bool arg_bigtile; extern bool arg_monochrome; extern bool arg_force_original; extern bool arg_force_roguelike; extern bool character_generated; extern bool character_dungeon; extern bool character_loaded; extern bool character_saved; extern bool character_icky; extern bool character_xtra; extern u32b seed_flavor; extern bool msg_flag; extern s16b num_repro; extern s32b turn; extern s32b old_turn; extern bool use_sound; extern byte use_graphics; extern bool use_bigtile; extern bool use_transparency; extern s16b signal_count; extern s16b o_max; extern s16b o_cnt; extern s16b m_max; extern s16b m_cnt; extern s16b q_max; extern s16b fld_max; extern s16b fld_cnt; extern s16b rg_max; extern s16b rg_cnt; extern s16b hack_m_idx; extern int total_friends; extern s32b total_friend_levels; extern s32b friend_align; extern s16b store_cache_num; extern store_type **store_cache; extern char summon_kin_type; extern bool track_follow; extern bool track_target; extern byte hitpoint_warn; extern byte delay_factor; extern s16b autosave_freq; extern byte autosave_t; extern byte autosave_l; extern bool cheat_peek; extern bool cheat_hear; extern bool cheat_room; extern bool cheat_xtra; extern bool cheat_know; extern bool cheat_live; extern bool fake_monochrome; extern byte *mp_a; extern char *mp_c; extern byte *mp_ta; extern char *mp_tc; extern int player_uid; extern int player_egid; extern char player_name[32]; extern char player_base[32]; extern char savefile[1024]; extern s16b view_n; extern s16b view_y[VIEW_MAX]; extern s16b view_x[VIEW_MAX]; extern s16b temp_n; extern s16b temp_y[TEMP_MAX]; extern s16b temp_x[TEMP_MAX]; extern s16b lite_n; extern s16b lite_y[LITE_MAX]; extern s16b lite_x[LITE_MAX]; extern s16b macro__num; extern cptr *macro__pat; extern cptr *macro__act; extern bool *macro__cmd; extern char *macro__buf; extern option_type option_info[OPT_MAX]; extern u32b window_flag[ANGBAND_TERM_MAX]; extern u32b window_mask[ANGBAND_TERM_MAX]; extern u32b option_mask[ANGBAND_TERM_MAX]; extern term *angband_term[ANGBAND_TERM_MAX]; extern char angband_term_name[ANGBAND_TERM_MAX][16]; extern byte angband_color_table[256][4]; extern char angband_sound_name[SOUND_MAX][16]; extern cave_type *(*area_aux) (int, int); extern pcave_type *(*parea_aux) (int, int); extern u16b *temp_block[WILD_BLOCK_SIZE + 1]; extern blk_ptr *wild_cache; extern int **wild_refcount; extern u32b wc_cnt; extern blk_ptr **wild_grid; extern wild_type **wild; extern u32b wild_seed; extern wild_gen_data_type *wild_gen_data; extern wild_choice_tree_type *wild_choice_tree; extern bool (*in_bounds) (int, int); extern bool (*in_bounds2) (int, int); extern bool (*in_boundsp) (int, int); extern region_type cave_data; extern int cur_region; extern maxima *z_info; extern object_type *o_list; extern monster_type *m_list; extern field_type *fld_list; extern region_type *rg_list; extern region_info *ri_list; extern u16b place_count; extern place_type *place; extern s16b alloc_kind_size; extern alloc_entry *alloc_kind_table; extern s16b alloc_race_size; extern alloc_entry *alloc_race_table; extern s16b alloc_ego_size; extern alloc_entry *alloc_ego_table; extern byte misc_to_attr[256]; extern char misc_to_char[256]; extern byte tval_to_attr[128]; extern char tval_to_char[128]; extern cptr keymap_act[KEYMAP_MODES][256]; extern player_type *p_ptr; extern player_sex *sp_ptr; extern player_race *rp_ptr; extern player_class *cp_ptr; extern player_magic *mp_ptr; extern server_type *svr_ptr; extern vault_type *v_info; extern char *v_name; extern char *v_text; extern feature_type *f_info; extern char *f_name; extern char *f_text; extern object_kind *k_info; extern char *k_name; extern char *k_text; extern artifact_type *a_info; extern char *a_name; extern char *a_text; extern ego_item_type *e_info; extern char *e_name; extern char *e_text; extern monster_race *r_info; extern char *r_name; extern char *r_text; extern field_thaum *t_info; extern quest_type *quest; extern cptr ANGBAND_SYS; extern cptr ANGBAND_DIR; extern cptr ANGBAND_DIR_APEX; extern cptr ANGBAND_DIR_BONE; extern cptr ANGBAND_DIR_DATA; extern cptr ANGBAND_DIR_EDIT; extern cptr ANGBAND_DIR_SCRIPT; extern cptr ANGBAND_DIR_FILE; extern cptr ANGBAND_DIR_HELP; extern cptr ANGBAND_DIR_INFO; extern cptr ANGBAND_DIR_PREF; extern cptr ANGBAND_DIR_SAVE; extern cptr ANGBAND_DIR_USER; extern cptr ANGBAND_DIR_XTRA; extern bool item_tester_full; extern byte item_tester_tval; extern bool (*item_tester_hook) (const object_type *o_ptr); extern bool (*ang_sort_comp) (const vptr u, const vptr v, int a, int b); extern void (*ang_sort_swap) (const vptr u, const vptr v, int a, int b); extern s32b max_wild; extern cptr gf_color[MAX_GF]; extern int owner_names_max; extern int owner_suffix_max; /* birth.c */ extern void player_birth(void); /* cave.c */ extern int distance(int x1, int y1, int x2, int y2); extern bool is_build(const cave_type *c_ptr); extern bool is_trap(const cave_type *c_ptr); extern bool is_visible_trap(const cave_type *c_ptr); extern bool los(int x1, int y1, int x2, int y2); extern void mmove_init(int x1, int y1, int x2, int y2); extern void mmove(int *x, int *y, int x1, int y1); extern bool projectable(int x1, int y1, int x2, int y2); extern sint project_path(coord *gp, int x1, int y1, int x2, int y2, u16b flg); extern bool in_ball_range(int x1, int y1, int x2, int y2); extern bool in_disintegration_range(int x1, int y1, int x2, int y2); extern void scatter(int *xp, int *yp, int x, int y, int d); extern bool player_can_see_bold(int x, int y); extern bool cave_valid_grid(const cave_type *c_ptr); extern bool no_lite(void); extern void move_cursor_relative(int col, int row); extern void print_rel(char c, byte a, int x, int y); extern void note_spot(int x, int y); extern void do_cmd_view_map(void); extern void forget_view(void); extern errr vinfo_init(void); extern void update_view(void); extern void update_mon_lite(void); extern void clear_mon_lite(void); extern void forget_flow(void); extern void update_flow(void); extern void map_area(void); extern void wiz_lite(void); extern void change_wiz_lite(void); extern void wiz_dark(void); extern void cave_set_feat(int x, int y, int feat); /* cmd1.c */ extern int deadliness_calc(int attack_power); extern long avg_dam(int attack_power, int dice_num, int dice_sides); extern bool test_hit_fire(int chance, int ac, int vis); extern bool test_hit_norm(int chance, int ac, int vis); extern int tot_dam_aux(const object_type *o_ptr, const monster_type *m_ptr); extern void search(void); extern bool auto_pickup_okay(const object_type *o_ptr); extern void py_pickup_aux(object_type *o_ptr); extern void carry(int pickup); extern void py_attack(int x, int y); extern void move_player(int dir, int do_pickup); extern void run_step(int dir); /* cmd2.c */ extern void do_cmd_go_up(void); extern void do_cmd_go_down(void); extern void do_cmd_search(void); extern void do_cmd_toggle_search(void); extern int count_traps(int *x, int *y, bool under); extern void do_cmd_open(void); extern void do_cmd_close(void); extern void do_cmd_tunnel(void); extern void do_cmd_disarm(void); extern void do_cmd_alter(void); extern void do_cmd_spike(void); extern void do_cmd_walk(int pickup); extern void do_cmd_stay(int pickup); extern void do_cmd_run(void); extern void do_cmd_rest(void); extern void do_cmd_fire(void); extern void do_cmd_fire_aux(int mult, object_type *o_ptr, const object_type *j_ptr); extern void do_cmd_throw(void); extern void do_cmd_throw_aux(int mult); extern bool do_cmd_open_aux(int x, int y); extern bool do_cmd_disarm_aux(cave_type *c_ptr, int dir); /* cmd3.c */ extern void do_cmd_inven(void); extern void do_cmd_equip(void); extern void do_cmd_wield(void); extern void do_cmd_takeoff(void); extern void do_cmd_drop(void); extern bool destroy_item_aux(object_type *o_ptr, int amt); extern void do_cmd_destroy(void); extern void do_cmd_observe(void); extern void do_cmd_uninscribe(void); extern void do_cmd_inscribe(void); extern void do_cmd_refill(void); extern void do_cmd_target(void); extern void do_cmd_look(void); extern void do_cmd_locate(void); extern bool ang_sort_comp_hook(const vptr u, const vptr v, int a, int b); extern void ang_sort_swap_hook(const vptr u, const vptr v, int a, int b); extern void do_cmd_query_symbol(void); extern bool research_mon(void); /* cmd4.c */ extern void do_cmd_redraw(void); extern void resize_map(void); extern void redraw_window(void); extern void do_cmd_messages(void); extern void init_options(byte flags); extern void do_cmd_options(byte flags); extern void do_cmd_pref(void); extern errr macro_dump(cptr fname); extern errr keymap_dump(cptr fname); extern void do_cmd_macros(void); extern void do_cmd_visuals(void); extern void do_cmd_colors(void); extern void do_cmd_note(void); extern void do_cmd_version(void); extern void do_cmd_feeling(void); extern void do_cmd_load_screen(void); extern void do_cmd_save_screen(void); extern bool do_cmd_knowledge_pets(int dummy); extern void dump_town_info(FILE *fff, int town, bool ignore); extern void do_cmd_knowledge(void); extern void plural_aux(char *Name); extern void do_cmd_checkquest(void); extern void do_cmd_time(void); /* cmd5.c */ extern void do_cmd_browse(void); extern void do_cmd_browse_aux(const object_type *o_ptr); extern void do_cmd_study(void); extern void do_cmd_cast(void); extern void do_cmd_pray(void); extern void do_cmd_pet(void); /* cmd6.c */ extern void do_cmd_eat_food(void); extern void do_cmd_quaff_potion(void); extern void do_cmd_read_scroll(void); extern void do_cmd_aim_wand(void); extern void do_cmd_use_staff(void); extern void do_cmd_zap_rod(void); extern void do_cmd_activate(void); extern void do_cmd_rerate(void); extern void ring_of_power(int dir); /* dungeon.c */ extern void sense_item(object_type *o_ptr, bool heavy, bool wield, bool msg); extern void notice_lite_change(object_type *o_ptr); extern bool psychometry(void); extern void play_game(bool new_game); /* files.c */ extern s16b tokenize(char *buf, s16b num, char **tokens, int mode); extern void display_player(int mode); extern void do_cmd_character(void); extern errr file_character(cptr name, bool full); extern errr process_pref_file_command(char *buf); extern errr process_pref_file(cptr fmt, ...); extern void print_equippy(void); extern errr check_load_init(void); extern void likert(char *buf, uint max, cptr fmt, va_list *vp); extern void player_flags(object_flags *of_ptr); extern bool show_file(cptr name, cptr what, int line, int mode); extern void do_cmd_help(void); extern void process_player_name(bool sf); extern void change_player_name(void); extern void do_cmd_suicide(void); extern void do_cmd_save_game(int is_autosave); extern void do_cmd_save_and_exit(void); extern errr get_rnd_line(cptr file_name, int entry, char *output); extern void get_character_name(void); /* generate.c */ extern void place_closed_door(int x, int y); extern void map_panel_size(void); extern void inc_rating(int delta_rating); extern void set_special(void); extern void del_region(int rg_idx); extern int unref_region(int rg_idx); extern void incref_region(int rg_idx); extern void set_region(int rg_idx); extern void wipe_rg_list(void); extern void create_region_aux(s16b *region, int x, int y, byte flags); extern void generate_cave(void); /* init1.c */ extern errr init_w_info_txt(FILE *fp, char *buf); extern errr init_t_info_txt(FILE *fp, char *buf); /* init2.c */ extern errr init_w_info(void); extern errr init_t_info(void); extern void init_file_paths(char *path); extern void init_angband(void); extern void cleanup_angband(void); extern errr check_modification_date(int fd, cptr template_file); /* load.c */ extern errr rd_savefile_new(void); /* melee1.c */ /* melee2.c */ extern void flee_message(cptr m_name, u16b r_idx); extern bool make_attack_normal(int m_idx); extern bool make_attack_spell(int m_idx); extern void process_monsters(int min_energy); extern void reset_monsters(void); extern void curse_equipment(int chance, int heavy_chance); extern void mon_take_hit_mon(int m_idx, int dam, bool *fear, cptr note); /* monster1.c */ extern monster_race *monst_race(int r_idx); extern cptr mon_race_name(const monster_race *r_ptr); extern bool mon_name_cont(const monster_race *r_ptr, cptr str); extern void roff_mon_top(int r_idx); extern void screen_roff_mon(int r_idx, int remember); extern void display_roff_mon(int r_idx); extern void display_visible(void); /* monster2.c */ extern cptr horror_desc[MAX_SAN_HORROR]; extern cptr funny_desc[MAX_SAN_FUNNY]; extern cptr funny_comments[MAX_SAN_COMMENT]; extern void delete_monster_idx(int i); extern void delete_monster(int x, int y); extern void compact_monsters(int size); extern void wipe_m_list(void); extern void wipe_monsters(int rg_idx); extern s16b m_pop(void); extern void get_mon_num_prep(monster_hook_type monster_hook); extern s16b get_mon_num(int level); extern s16b get_filter_mon_num(int level, monster_hook_type monster_hook); extern void monster_desc(char *desc, const monster_type *m_ptr, int mode, int max); extern void monster_fmt(char *buf, uint max, cptr fmt, va_list *vp); extern void lore_do_probe(int m_idx); extern void lore_treasure(int m_idx, int num_item, int num_gold); extern void update_mon_vis(u16b r_idx, int increment); extern void update_mon(int m_idx, bool full); extern void update_monsters(bool full); extern bool test_monster_square(cave_type *c_ptr, monster_race *r_ptr); extern monster_type *place_monster_aux(int x, int y, int r_idx, bool slp, bool grp, bool friendly, bool pet, bool summon); extern bool place_monster(int x, int y, bool slp, bool grp, int deltalevel); extern bool alloc_horde(int x, int y); extern bool alloc_monster(int dis, bool slp, int delta_level); extern bool summon_specific(int who, int x1, int y1, int lev, int type, bool group, bool friendly, bool pet); extern monster_type *summon_named_creature(int x1, int y1, int r_idx, bool slp, bool group_ok, bool pet); extern monster_type *summon_cloned_creature(int x1, int y1, int r_idx, bool pet); extern monster_type *multiply_monster(int m_idx, bool clone, bool friendly, bool pet); extern void update_smart_learn(int m_idx, int what); extern monster_type *place_monster_one(int x, int y, int r_idx, bool slp, bool friendly, bool pet); /* monster3.c (currently in monster1.c) */ extern void set_friendly(monster_type *m_ptr); extern void set_pet(monster_type *m_ptr); extern void set_hostile(monster_type *m_ptr); extern void anger_monster(monster_type *m_ptr); extern bool are_enemies(const monster_type *m_ptr1, const monster_type *m_ptr2); extern bool monster_living(const monster_race *r_ptr); extern void change_shimmer(void); extern void change_repair(void); /* flavor.c */ extern void get_table_name(char *out_string, bool quotes); extern void flavor_init(void); extern void object_desc(char *buf, const object_type *o_ptr, int pref, int mode, int size); extern void object_fmt(char *buf, uint max, cptr fmt, va_list *vp); extern void object_desc_store(char *buf, const object_type *o_ptr, int pref, int mode, int size); extern void object_store_fmt(char *buf, uint max, cptr fmt, va_list *vp); /* object1.c */ /* object2.c */ extern void reset_visuals(void); extern void object_flags_known(const object_type *o_ptr, object_flags *of_ptr); extern void identify_fully_aux(const object_type *o_ptr); extern s16b wield_slot(const object_type *o_ptr); extern cptr mention_use(int i); extern cptr describe_use(int i); extern bool item_tester_hook_weapon(const object_type *o_ptr); extern bool item_tester_hook_melee_weapon(const object_type *o_ptr); extern bool item_tester_hook_nonsword(const object_type *o_ptr); extern bool item_tester_hook_ammo(const object_type *o_ptr); extern bool item_tester_hook_fletcher(const object_type *o_ptr); extern bool item_tester_hook_armour(const object_type *o_ptr); extern bool item_tester_hook_armour_no_acid(const object_type *o_ptr); extern bool item_tester_hook_soft_armour(const object_type *o_ptr); extern bool item_tester_hook_hard_armour(const object_type *o_ptr); extern bool item_tester_hook_helm(const object_type *o_ptr); extern bool item_tester_hook_pure_hard_armour(const object_type *o_ptr); extern bool item_tester_hook_weapon_armour(const object_type *o_ptr); extern bool item_tester_hook_wear(const object_type *o_ptr); extern bool item_tester_hook_recharge(const object_type *o_ptr); extern bool item_tester_hook_jewel(const object_type *o_ptr); extern bool item_tester_hook_tval(const object_type *o_ptr, byte tval); extern bool item_tester_hook_is_blessed(const object_type *o_ptr); extern bool item_tester_hook_is_good(const object_type *o_ptr); extern bool item_tester_hook_is_great(const object_type *o_ptr); extern bool item_tester_hook_is_book(const object_type *o_ptr); extern bool item_tester_okay(const object_type *o_ptr); extern void display_inven(void); extern void display_equip(void); extern void show_list(s16b o_list_ptr, bool store); extern void show_equip(bool store); extern void toggle_inven_equip(void); extern object_type *get_item(cptr pmt, cptr str, int mode); extern void delete_held_object(s16b *o_idx_ptr, object_type *o_ptr); extern void delete_dungeon_object(object_type *o_ptr); extern void delete_object(int x, int y); extern void delete_object_list(s16b *o_idx_ptr); extern void drop_object_list(s16b *o_idx_ptr, int x, int y); extern void compact_objects(int size); extern void wipe_o_list(void); extern void wipe_objects(int rg_idx); extern object_type *add_object_list(s16b *o_idx_ptr, object_type *o_ptr); extern void move_object(s16b *tgt_list_ptr, s16b *cur_list_ptr, object_type *o_ptr); extern void swap_objects(object_type *o1_ptr, object_type *o2_ptr); extern void get_obj_num_prep(object_hook_type object_hook); extern s16b get_obj_num(int level, int min_level); extern void object_known(object_type *o_ptr); extern void object_aware(object_type *o_ptr); extern void object_tried(object_type *o_ptr); extern void object_mental(object_type *o_ptr); extern s32b flag_cost(const object_type *o_ptr, int plusses); extern s32b object_value(const object_type *o_ptr); extern s32b object_value_real(const object_type *o_ptr); extern void distribute_charges(object_type *o_ptr, object_type *q_ptr, int amt); extern void reduce_charges(object_type *o_ptr, int amt); extern bool object_similar(const object_type *o_ptr, const object_type *j_ptr); extern void object_absorb(object_type *o_ptr, const object_type *j_ptr); extern bool object_equal(const object_type *o_ptr, const object_type *j_ptr); extern s16b lookup_kind(int tval, int sval); extern s16b *look_up_list(object_type *o_ptr); extern void object_wipe(object_type *o_ptr); extern object_type *object_prep(int k_idx); extern object_type *object_dup(const object_type *o_ptr); extern void add_ego_flags(object_type *o_ptr, byte ego); extern void add_ego_power(int power, object_type *o_ptr); extern s16b m_bonus(int max, int level); extern void apply_magic(object_type *o_ptr, int lev, int lev_dif, byte flags); extern void init_match_hook(byte tval, byte sval); extern byte kind_is_match(int k_idx); extern void init_match_theme(obj_theme theme); extern byte kind_is_theme(int k_idx); extern object_type *make_object(int level, int delta_level, obj_theme *theme); extern void place_specific_object(int x, int y, int level, int k_idx); extern void place_object(int x, int y, bool good, bool great, int delta_level); extern object_type *make_gold(int level, int coin_type); extern void place_gold(int x, int y); extern void drop_near(object_type *o_ptr, int chance, int x, int y); extern void acquirement(int x1, int y1, int num, bool great, bool known); extern object_type *get_list_item(s16b list_start, int number); extern int get_item_position(s16b list_start, object_type *o_ptr); extern int get_list_length(s16b list_start); extern bool floor_item(object_type *o_ptr); extern bool player_item(object_type *o_ptr); extern void item_charges(object_type *o_ptr); extern void item_describe(object_type *o_ptr); extern void item_describe_roff(object_type *o_ptr); extern void item_describe_faux(object_type *o_ptr); extern object_type *item_split(object_type *o_ptr, int num); extern void item_increase(object_type *o_ptr, int num); extern void item_increase_silent(object_type *o_ptr, int num); extern bool inven_carry_okay(const object_type *o_ptr); extern object_type *reorder_objects_aux(object_type *q_ptr, object_comp comp_func, u16b o_idx); extern object_type *inven_carry(object_type *o_ptr); extern object_type *inven_takeoff(object_type *o_ptr); extern void inven_drop(object_type *o_ptr, int amt); extern cptr item_activation(const object_type *o_ptr); extern void combine_pack(void); extern void reorder_pack(void); extern object_type *combine_pack_watch(object_type *o_ptr); extern object_type *reorder_pack_watch(object_type *o_ptr); extern bool can_player_destroy_object(object_type *o_ptr); extern void display_koff(int k_idx); extern object_type *test_floor(int *num, cave_type *c_ptr, int mode); extern void show_floor(int x, int y); /* racial.c */ extern bool racial_aux(s16b min_level, int cost, int use_stat, int difficulty); extern void do_cmd_racial_power(void); /* save.c */ extern bool save_player(void); extern bool load_player(void); /* spells1.c */ extern void take_hit(int damage, cptr kb_str); extern int dist_to_line(int x, int y, int x1, int y1, int x2, int y2); extern bool project(int who, int rad, int x, int y, int dam, int typ, u16b flg); /* spells2.c */ extern void message_pain(int m_idx, int dam); extern void self_knowledge(void); extern bool detect_traps(bool ident); extern void create_closed_door(int x, int y); extern bool detect_doors(void); extern bool detect_stairs(void); extern bool detect_treasure(void); extern bool detect_objects_gold(void); extern bool detect_objects_normal(void); extern bool detect_objects_magic(void); extern bool detect_monsters_normal(void); extern bool detect_monsters_invis(void); extern bool detect_monsters_evil(void); extern bool detect_monsters_xxx(u32b match_flag); extern bool detect_monsters_string(cptr match); extern bool detect_monsters_nonliving(void); extern bool detect_monsters_living(void); extern bool detect_all(void); extern bool wall_stone(void); extern bool speed_monsters(void); extern bool slow_monsters(void); extern bool sleep_monsters(void); extern void aggravate_monsters(int who); extern bool genocide(int player_cast); extern bool mass_genocide(int player_cast); extern bool probing(void); extern bool banish_evil(int dist); extern bool dispel_evil(int dam); extern bool dispel_good(int dam); extern bool dispel_undead(int dam); extern bool dispel_monsters(int dam); extern bool dispel_living(int dam); extern bool dispel_demons(int dam); extern bool raise_dead(int y, int x, bool pet); extern bool turn_undead(void); extern bool destroy_area(int x1, int y1, int r); extern bool earthquake(int cx, int cy, int r); extern void lite_room(int x1, int y1); extern void unlite_room(int x1, int y1); extern bool lite_area(int dam, int rad); extern bool unlite_area(int dam, int rad); extern bool fire_ball(int typ, int dir, int dam, int rad); extern bool fire_bolt(int typ, int dir, int dam); extern void call_chaos(void); extern bool fire_beam(int typ, int dir, int dam); extern bool fire_bolt_or_beam(int prob, int typ, int dir, int dam); extern bool lite_line(int dir, int dam); extern bool drain_life(int dir, int dam); extern bool drain_gain_life(int dor, int dam); extern bool death_ray(int dir, int plev); extern bool wall_to_mud(int dir); extern bool destroy_door(int dir); extern bool disarm_trap(int dir); extern bool wizard_lock(int dir); extern bool heal_monster(int dir); extern bool speed_monster(int dir); extern bool slow_monster(int dir); extern bool sleep_monster(int dir); extern bool stasis_monster(int dir); /* Like sleep, affects undead as well */ extern bool confuse_monster(int dir, int plev); extern bool stun_monster(int dir, int plev); extern bool fear_monster(int dir, int plev); extern bool poly_monster(int dir); extern bool clone_monster(int dir); extern bool teleport_monster(int dir); extern bool door_creation(void); extern bool trap_creation(void); extern bool glyph_creation(void); extern bool destroy_doors_touch(void); extern bool sleep_monsters_touch(void); extern bool activate_ty_curse(bool stop_ty, int *count); extern int activate_hi_summon(void); extern int summon_cyber(int who, int x, int y); extern void wall_breaker(void); extern bool confuse_monsters(int dam); extern bool charm_monsters(int dam); extern bool charm_animals(int dam); extern bool stun_monsters(int dam); extern bool stasis_monsters(int dam); extern bool banish_monsters(int dist); extern bool turn_monsters(int dam); extern bool turn_evil(int dam); extern bool deathray_monsters(void); extern bool charm_monster(int dir, int plev); extern bool control_one_undead(int dir, int plev); extern bool charm_animal(int dir, int plev); extern bool starlite(void); extern bool scatter_ball(int num, int typ, int dam, int rad); extern void create_food(void); extern void whirlwind_attack(void); extern bool mindblast_monsters(int dam); extern void report_magics(void); extern bool teleport_swap(int dir); extern bool project_hook(int typ, int dir, int dam, u16b flg); extern bool project_hack(int typ, int dam); /* spells3.c */ extern bool teleport_away(int m_idx, int dis); extern void teleport_to_player(int m_idx); extern void teleport_player(int dis); extern void teleport_player_to(int nx, int ny); extern void teleport_player_level(void); extern bool check_down_wild(void); extern void recall_player(int turns); extern void word_of_recall(void); extern bool apply_disenchant(void); extern void mutate_player(void); extern void apply_nexus(const monster_type *m_ptr); extern void phlogiston(void); extern void brand_weapon(int brand_type); extern void call_the_(void); extern void fetch(int dir, int wgt, bool require_los); extern void alter_reality(void); extern bool warding_glyph(void); extern bool explosive_rune(void); extern void identify_pack(void); extern bool remove_curse(void); extern bool remove_all_curse(void); extern bool alchemy(void); extern void stair_creation(void); extern bool enchant(object_type *o_ptr, int n, int eflag); extern bool enchant_spell(int num_hit, int num_dam, int num_ac); extern bool artifact_scroll(void); extern bool ident_spell(void); extern bool ident_scroll(int k_idx); extern bool mundane_spell(void); extern void identify_item(object_type *o_ptr); extern bool identify_fully(void); extern bool recharge(int num); extern bool bless_weapon(void); extern bool potion_smash_effect(int who, int x, int y, object_type *o_ptr); extern void display_spell_list(void); extern s16b spell_chance(int spell, int realm); extern int spell_mana(int spell, int realm); extern bool spell_okay(int spell, bool known, int realm); extern void spell_info(char *p, int spell, int realm); extern void print_spells(byte *spells, int num, int x, int y, int realm); extern bool hates_acid(const object_type *o_ptr); extern bool hates_elec(const object_type *o_ptr); extern bool hates_fire(const object_type *o_ptr); extern bool hates_cold(const object_type *o_ptr); extern int set_acid_destroy(object_type *o_ptr); extern int set_elec_destroy(object_type *o_ptr); extern int set_fire_destroy(object_type *o_ptr); extern int set_cold_destroy(object_type *o_ptr); extern int inven_damage(inven_func typ, int perc); extern bool rustproof(void); extern bool curse_armor(void); extern bool curse_weapon(void); extern bool brand_bolts(void); extern bool polymorph_monster(int x, int y); extern bool dimension_door(void); extern void map_wilderness(int radius, s32b x, s32b y); extern void sanity_blast(const monster_type *m_ptr); /* store.c */ extern s32b price_item(object_type *o_ptr, bool flip); extern bool allocate_store(store_type *st_ptr); extern store_type *get_current_store(void); extern void do_cmd_store(const field_type *f_ptr); extern void store_init(int town_num, int store_num, byte store); extern void place_sb(int greed, int max_cost); extern bool do_standard_command(s16b c); /* bldg.c */ extern bool get_nightmare(int r_idx); extern void have_nightmare(void); extern bool test_gold(s32b cost); extern bool build_has_quest(void); extern void build_cmd_quest(int level); extern void display_build(const field_type *f_ptr); extern void do_cmd_bldg(const field_type *f_ptr); extern bool compare_weapons(void); extern bool enchant_item(s32b cost, bool to_hit, bool to_dam, bool to_ac, bool weap); extern void building_recharge(s32b cost); extern bool building_healer(void); extern void record_aura(void); extern bool building_magetower(int factor, bool display); extern void gamble_help(void); extern void gamble_in_between(void); extern void gamble_craps(void); extern void gamble_spin_wheel(void); extern void gamble_dice_slots(void); extern bool inn_rest(void); extern void build_init(int town_num, int build_num, byte build_type); /* util.c */ extern void safe_setuid_drop(void); extern void safe_setuid_grab(void); extern void init_setuid(void); extern void signals_ignore_tstp(void); extern void signals_handle_tstp(void); extern void signals_init(void); extern bool assert_helper(cptr expr, cptr file, int line, bool result); extern errr path_parse(char *buf, int max, cptr file); extern void path_build(char *buf, int max, cptr path, cptr file); extern FILE *my_fopen(cptr file, cptr mode); extern FILE *my_fopen_temp(char *buf, int max); extern errr my_fgets(FILE *fff, char *buf, huge n); extern errr my_raw_fgets(FILE *fff, char *buf, huge n); extern void my_fclose(FILE *fff); extern errr fd_kill(cptr file); extern errr fd_move(cptr file, cptr what); extern int fd_make(cptr file, int mode); extern int fd_open(cptr file, int flags); extern errr fd_lock(int fd, int what); extern errr fd_seek(int fd, huge n); extern errr fd_read(int fd, char *buf, huge n); extern errr fd_write(int fd, cptr buf, huge n); extern errr fd_close(int fd); extern int count_bits(u32b x); extern sint macro_find_exact(cptr pat); extern void macro_add(cptr pat, cptr act); extern void flush(void); extern void text_to_ascii(char *buf, cptr str); extern void ascii_to_text(char *buf, cptr str); extern char inkey(void); extern s16b quark_add(cptr str); extern s16b quark_fmt(cptr str, ...); extern void quark_remove(s16b *i); extern void quark_dup(s16b i); extern cptr quark_str(s16b i); extern errr quarks_init(void); extern errr quarks_free(void); extern byte get_msg_type_color(byte a); extern s16b message_num(void); extern cptr message_str(s16b age); extern u16b message_type(s16b age); extern byte message_color(s16b age); extern errr message_color_define(u16b type, byte color); extern void message_add(cptr str, u16b type); extern errr messages_init(void); extern void messages_free(void); extern void set_message_type(char *buf, uint max, cptr fmt, va_list *vp); extern void msgf(cptr fmt, ...); extern void msg_effect(u16b type, s16b extra); extern void message_flush(void); extern bool is_a_vowel(int ch); extern void request_command(int shopping); extern void repeat_push(int what); extern bool repeat_pull(int *what); extern void repeat_clear(void); extern void repeat_check(void); #ifdef PRIVATE_USER_PATH extern void create_user_dirs(void); #endif /* PRIVATE_USER_PATH */ /* xtra1.c */ extern s16b modify_stat_value(int value, int amount); extern void stat_format(char *buf, uint max, cptr fmt, va_list *vp); extern void notice_stuff(void); extern void update_stuff(void); extern void redraw_stuff(void); extern void window_stuff(void); extern void handle_stuff(void); extern void change_stuff(void); bool player_save(int power); extern void object_bonuses(const object_type *o_ptr, bonuses_type *b); extern void object_bonuses_known(const object_type *o_ptr, bonuses_type *b); /* effects.c */ extern bool inc_blind(int v); extern bool clear_blind(void); extern bool inc_confused(int v); extern bool clear_confused(void); extern bool inc_poisoned(int v); extern bool clear_poisoned(void); extern bool inc_afraid(int v); extern bool clear_afraid(void); extern bool inc_paralyzed(int v); extern bool clear_paralyzed(void); extern bool inc_image(int v); extern bool clear_image(void); extern bool inc_fast(int v); extern bool clear_fast(void); extern bool inc_slow(int v); extern bool clear_slow(void); extern bool inc_shield(int v); extern bool inc_blessed(int v); extern bool inc_hero(int v); extern bool inc_shero(int v); extern bool inc_protevil(int v); extern bool inc_wraith_form(int v); extern bool inc_tim_esp(int v); extern bool clear_tim_esp(void); extern bool inc_invuln(int v); extern bool inc_tim_invis(int v); extern bool inc_tim_infra(int v); extern bool inc_oppose_acid(int v); extern bool inc_oppose_elec(int v); extern bool inc_oppose_fire(int v); extern bool inc_oppose_cold(int v); extern bool inc_oppose_pois(int v); extern int res_acid_lvl(void); extern int res_elec_lvl(void); extern int res_fire_lvl(void); extern int res_cold_lvl(void); extern int res_pois_lvl(void); extern int resist(int dam, int (*f_func) (void)); extern bool acid_dam(int dam, cptr kb_str); extern bool elec_dam(int dam, cptr kb_str); extern bool fire_dam(int dam, cptr kb_str); extern bool cold_dam(int dam, cptr kb_str); extern bool pois_dam(int dam, cptr kb_str, int pois); extern bool inc_stun(int v); extern bool clear_stun(void); extern bool inc_cut(int v); extern bool clear_cut(void); extern bool set_food(int v); extern bool inc_stat(int stat); extern bool dec_stat(int stat, int amount, int permanent); extern bool res_stat(int stat); extern bool hp_player(int num); extern bool do_dec_stat(int stat); extern bool do_res_stat(int stat); extern bool do_inc_stat(int stat); extern bool restore_level(void); extern bool lose_all_info(void); extern void gain_exp(s32b amount); extern void lose_exp(s32b amount); extern void do_poly_self(void); extern void make_noise(byte amount); extern void disturb(bool stop_search); extern void notice_inven(void); extern void notice_equip(void); extern void notice_item(void); /* xtra2.c */ extern void check_experience(void); extern bool monster_death(int m_idx, bool explode); extern bool mon_take_hit(int m_idx, int dam, bool *fear, cptr note); extern void get_map_size(int *x, int *y); extern bool panel_center(int x, int y); extern bool change_panel(int dx, int dy); extern void verify_panel(void); extern cptr look_mon_desc(int m_idx); extern void ang_sort_aux(vptr u, vptr v, int p, int q); extern void ang_sort(vptr u, vptr v, int n); extern bool target_able(int m_idx); extern bool target_okay(void); extern bool target_set(int mode); extern bool get_aim_dir(int *dp); extern bool get_hack_dir(int *dp); extern bool get_rep_dir(int *dp); extern int get_chaos_patron(void); extern void gain_level_reward(int chosen_reward); extern bool tgt_pt(int *x, int *y); extern void do_poly_wounds(void); extern int mon_damage_mod(const monster_type *m_ptr, int dam, int type); extern void exp_for_kill(const monster_race *r_ptr, s32b *new_exp, s32b *new_exp_frac); extern int stat_cap(int stat); extern int adjust_stat(int stat, int value, int amount); extern void health_track(int m_idx); extern void monster_race_track(int r_idx); extern void object_kind_track(int k_idx); /* mspells1.c */ extern bool clean_shot(int x1, int y1, int x2, int y2, bool friendly); /* mspells2.c */ extern bool monst_spell_monst(int m_idx); /* artifact.c */ extern bool create_artifact(object_type *o_ptr, int level, bool a_scroll); extern void create_named_art(int a_idx, int x, int y); /* scores.c */ extern bool display_scores_aux(int from, int to, int note, const high_score *score); extern void display_scores(int from, int to); extern void enter_score(void); extern void predict_score(void); extern void race_legends(void); extern void race_score(int race_num); extern void show_highclass(void); extern void ingame_score(bool *initialized, bool game_in_progress); extern void close_game(void); extern void exit_game_panic(void); /* mind.c */ extern mindcraft_power mindcraft_powers[MINDCRAFT_MAX]; extern void mindcraft_info(char *p, int power); extern void do_cmd_mindcraft(void); /* mutation.c */ extern bool player_has_mut(int mutation); extern bool gain_mutation(int choose_mut); extern bool lose_mutation(int choose_mut); extern void dump_mutations(FILE *OutFile); extern bool do_cmd_knowledge_mutations(int dummy); extern int count_mutations(void); extern void mutation_power_aux(const mutation_type *mut_ptr); extern void mutation_random_aux(const mutation_type *mut_ptr); extern void mutation_effect(void); /* obj_kind.c */ extern errr k_info_alloc(void); extern errr k_info_free(void); extern object_kind *k_info_add(object_kind *k_info_entry); extern byte get_object_level(const object_type *o_ptr); extern cptr get_object_name(const object_type *o_ptr); extern bool object_is_potion(const object_type *o_ptr); extern errr init_object_alloc(void); extern void k_info_reset(void); /* wild1.c and wild2.c */ extern void select_town_name(char *name, int pop); extern const dun_gen_type *pick_dungeon_type(void); extern void light_dark_square(int y, int x, bool daytime); extern u16b init_choice_tree(wild_bound_box_type *bound, u16b type); extern u16b add_node_tree_root(wild_bound_box_type *bound, u16b type); extern void test_decision_tree(void); extern void repopulate_wilderness(void); extern void create_wilderness(void); extern void move_wild(void); extern void shift_in_bounds(int *x, int *y); extern byte the_floor(void); extern void change_level(int); extern int base_level(void); extern void wipe_all_list(void); extern dun_type *dungeon(void); extern void move_dun_level(int direction); extern int max_dun_level_reached(void); extern cptr building_name(byte build_type); extern void building_char(byte build_type, byte *a, char *c); extern cptr dungeon_type_name(u32b dun); /* avatar.c */ extern cptr virtue[MAX_VIRTUE]; extern void get_virtues(void); extern void chg_virtue(int virtue, int amount); extern void dump_virtues(FILE *OutFile); /* notes.c */ extern cptr notes_file(void); extern void output_note(cptr final_note, ...); extern void add_note(char code, cptr note, ...); extern void add_note_type(int note_number); /* fields.c */ extern void notice_field(field_type *f_ptr); extern cptr field_name(const field_type *f_ptr); extern void excise_field_idx(int fld_idx); extern void delete_field_ptr(field_type *f_ptr); extern void delete_field(int x, int y); extern void delete_field_location(cave_type *c_ptr); extern void compact_fields(int size); extern void wipe_f_list(void); extern void wipe_fields(int rg_idx); extern s16b f_pop(void); extern void field_wipe(field_type *f_ptr); extern void field_copy(field_type *f_ptr, field_type *j_ptr); extern s16b field_add(field_type *f_ptr, cave_type *c_ptr); extern void field_prep(field_type *f_ptr, s16b t_idx); extern void init_fields(void); extern field_type *field_is_type(const cave_type *c_ptr, byte typ); extern field_type *field_first_known(const cave_type *c_ptr, byte typ); extern u16b fields_have_flags(const cave_type *c_ptr, u16b info); extern bool field_detect_type(const cave_type *c_ptr, byte typ); extern void field_destroy_type(cave_type *c_ptr, byte typ); extern field_type *place_field(int x, int y, s16b t_idx); extern bool field_script_single(field_type *f_ptr, int action, cptr format, ...); extern void field_script_const(const field_type *f_ptr, int action, cptr format, ...); extern void field_script(cave_type *c_ptr, int action, cptr format, ...); extern bool field_script_special(cave_type *c_ptr, u16b t_idx, cptr format, ...); extern field_type *field_script_find(cave_type *c_ptr, int action, cptr format, ...); extern void process_fields(void); extern void test_field_data_integrity(void); extern void set_corpse_size(field_type *f_ptr, int size); extern void place_trap(int x, int y); extern void make_lockjam_door(int x, int y, int power, bool jam); extern bool monster_can_open(monster_race *r_ptr, int power); extern bool check_trap_hit(int power); extern bool dont_save(int power); extern void hit_trap(field_type *f_ptr); extern void evil_trap(void); extern void drop_random_item(void); extern void drain_lite(void); extern void drain_food(void); extern void drain_magic(void); extern void print_building_options(cptr strings[], int num); /* compress.c */ extern void test_compress_module(void); /* quest.c */ extern u16b q_pop(void); extern void discover_wild_quest(int q_num); extern u16b insert_dungeon_monster_quest(u16b r_idx, u16b num, u16b level); extern cptr describe_quest_location(cptr * dirn, int x, int y, bool known); extern void dump_castle_info(FILE *fff, int place); extern errr init_quests(void); extern void init_player_quests(void); extern void quest_discovery(void); extern bool is_special_level(int level); extern void activate_quests(int level); extern void trigger_quest_create(byte c_type, vptr data); extern void trigger_quest_complete(byte x_type, vptr data); extern quest_type *lookup_quest_building(const store_type *b_ptr); extern void reward_quest(quest_type *q_ptr); extern void request_quest(const store_type *b_ptr, int scale); extern bool do_cmd_knowledge_quests(int dummy); /* maid-grf.c */ extern void init_term_callbacks(void); extern void free_term_callbacks(void); extern void init_overhead_map(void); extern void del_overhead_map(void); extern void display_dungeon(void); extern void lite_spot(int x, int y); extern void prt_map(void); extern void display_map(int *cx, int *cy); extern void update_overhead_map(void); extern void Term_erase_map(void); extern void Term_move_player(void); extern void Term_write_equipment(void); extern void Term_write_list(s16b o_idx, byte list_type); /* ui.c */ extern void center_string(char *buf, uint max, cptr fmt, va_list *vp); extern void binary_fmt(char *buf, uint max, cptr fmt, va_list *vp); extern void fmt_clean(char *buf); extern int get_player_choice(cptr *choices, int num, int col, int wid, cptr helpfile, void (*hook) (cptr)); extern int get_player_sort_choice(cptr *choices, int num, int col, int wid, cptr helpfile, void (*hook) (cptr)); extern bool display_menu(menu_type *options, int select, bool scroll, int disp(int), cptr prompt); extern void bell(cptr reason); extern void sound(int num); extern int color_char_to_attr(char c); extern void screen_save(void); extern void screen_load(void); extern int fmt_offset(cptr str1, cptr str2); extern void put_fstr(int col, int row, cptr str, ...); extern void prtf(int col, int row, cptr str, ...); extern void roff(cptr str, ...); extern void froff(FILE *fff, cptr str, ...); extern void clear_from(int row); extern void clear_msg(void); extern void clear_row(int row); extern void clear_region(int x, int y1, int y2); extern bool askfor_aux(char *buf, int len); extern bool get_string(char *buf, int len, cptr str, ...); extern bool get_check(cptr prompt, ...); extern bool get_check_ext(bool def, bool esc, cptr prompt, ...); extern bool get_com(cptr prompt, char *command); extern s16b get_quantity(cptr prompt, int max); extern void pause_line(int row); extern int get_keymap_dir(char ch); /* borg.c */ extern void do_cmd_borg(void); /* script.c */ extern void deleteme(void); extern bool player_res(u32b flag); /* * Hack -- conditional (or "bizarre") externs */ #ifdef SET_UID # ifndef HAS_USLEEP /* util.c */ extern int usleep(huge usecs); # endif /* HAS_USLEEP */ extern void user_name(char *buf, int id); #endif /* SET_UID */ #if defined(MAC_MPW) || defined(MACH_O_CARBON) /* main-mac.c, or its derivatives */ extern u32b _fcreator; extern u32b _ftype; # if defined(MAC_MPW) && defined(CARBON) extern void convert_pathname(char *path); # endif # if defined(MACH_O_CARBON) extern void fsetfileinfo(cptr path, u32b fcreator, u32b ftype); # endif #endif ����������������������������������������������������������������������������������������������������������������������������������zangband/src/generate.h�����������������������������������������������������������������������������0000644�0000000�0000000�00000011305�10250356275�014077� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: wild1.h */ /* Purpose: Dungeon generation header file */ /* * Copyright (c) 1989, 1999 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #define SAFE_MAX_ATTEMPTS 5000 /* * Dungeon generation values */ #define DUN_UNUSUAL 200 /* Level/chance of unusual room (was 200) */ #define DUN_DEST 18 /* 1/chance of having a destroyed level */ #define SMALL_LEVEL 5 /* 1/chance of smaller size (5) */ #define EMPTY_LEVEL 15 /* 1/chance of being 'empty' (15) */ #define LAKE_LEVEL 10 /* 1/chance of being a lake on the level */ #define DARK_EMPTY 5 /* 1/chance of arena level NOT being lit (2) */ #define DUN_CAV1 600 /* 1/chance for getting a cavern is: */ #define DUN_CAV2 75 /* DUN_CAV1/(dun_level + DUN_CAV2) */ /* = 5 at dl 30 and = 3 at dl 100 */ #define MIN_CAVERN 30 /* Minimum level to get a cavern */ /* Number of rooms to attempt (was 50) */ #define DUN_ROOMS_MIN 10 #define DUN_ROOMS_MAX 100 /* * Dungeon tunnel generation values */ #define DUN_TUN_RND_MIN 5 /* Chance of random direction (was 10) */ #define DUN_TUN_RND_MAX 20 #define DUN_TUN_CHG_MIN 20 /* Chance of changing direction (was 30) */ #define DUN_TUN_CHG_MAX 60 #define DUN_TUN_CON_MIN 10 /* Chance of extra tunneling (was 15) */ #define DUN_TUN_CON_MAX 40 #define DUN_TUN_PEN_MIN 30 /* Chance of doors at room entrances (was 25) */ #define DUN_TUN_PEN_MAX 70 #define DUN_TUN_JCT_MIN 60 /* Chance of doors at tunnel junctions (was 90) */ #define DUN_TUN_JCT_MAX 90 extern int dun_tun_rnd; extern int dun_tun_chg; extern int dun_tun_con; extern int dun_tun_pen; extern int dun_tun_jct; /* * Dungeon streamer generation values */ #define DUN_STR_DEN 5 /* Density of streamers */ #define DUN_STR_RNG 2 /* Width of streamers */ #define DUN_STR_MAG 3 /* Number of magma streamers */ #define DUN_STR_MC 90 /* 1/chance of treasure per magma */ #define DUN_STR_QUA 2 /* Number of quartz streamers */ #define DUN_STR_QC 40 /* 1/chance of treasure per quartz */ #define DUN_STR_WLW 1 /* Width of lava & water streamers -KMW- */ #define DUN_STR_DWLW 8 /* Density of water & lava streams -KMW- */ #define DUN_MOS_DEN 2 /* Density of moss streamers */ #define DUN_MOS_RNG 10 /* Width of moss streamers */ #define DUN_STR_MOS 2 /* Number of moss streamers */ #define DUN_WAT_DEN 15 /* Density of rivers */ #define DUN_WAT_RNG 2 /* Width of rivers */ #define DUN_STR_WAT 3 /* Max number of rivers */ #define DUN_WAT_CHG 50 /* 1 in 50 chance of junction in river */ /* * Dungeon treausre allocation values */ #define DUN_AMT_ROOM 9 /* Amount of objects for rooms */ #define DUN_AMT_ITEM 3 /* Amount of objects for rooms/corridors */ #define DUN_AMT_GOLD 3 /* Amount of treasure for rooms/corridors */ #define DUN_AMT_INVIS 3 /* Amount of invisible walls for rooms/corridors */ /* * Hack -- Dungeon allocation "places" */ #define ALLOC_SET_CORR 1 /* Hallway */ #define ALLOC_SET_ROOM 2 /* Room */ #define ALLOC_SET_BOTH 3 /* Anywhere */ /* * Hack -- Dungeon allocation "types" */ #define ALLOC_TYP_RUBBLE 1 /* Rubble */ #define ALLOC_TYP_TRAP 3 /* Trap */ #define ALLOC_TYP_GOLD 4 /* Gold */ #define ALLOC_TYP_OBJECT 5 /* Object */ #define ALLOC_TYP_INVIS 6 /* Invisible wall */ /* * The "size" of a "generation block" in grids */ #define BLOCK_HGT 11 #define BLOCK_WID 11 /* * Maximum numbers of rooms along each axis */ #define MAX_ROOMS_ROW (MAX_HGT / BLOCK_HGT) #define MAX_ROOMS_COL (MAX_WID / BLOCK_WID) /* * Bounds on some arrays used in the "dun_data" structure. */ #define CENT_MAX 100 #define DOOR_MAX 200 #define WALL_MAX 500 #define TUNN_MAX 900 /* * Structure to hold all "dungeon generation" data * Using this instead of global variables, * fixes a memory fragmentation problem on some windows compilers. */ typedef struct dun_data dun_data; struct dun_data { /* Array of centers of rooms */ int cent_n; coord cent[CENT_MAX]; /* Array of possible door locations */ int door_n; coord door[DOOR_MAX]; /* Array of wall piercing locations */ int wall_n; coord wall[WALL_MAX]; /* Array of tunnel grids */ int tunn_n; coord tunn[TUNN_MAX]; /* Number of blocks along each axis */ int row_rooms; int col_rooms; /* Array of which blocks are used */ bool room_map[MAX_ROOMS_ROW][MAX_ROOMS_COL]; /* Hack -- there is a pit/nest on this level */ int crowded; /* Room types allowed */ u16b room_types; /* Liquid type for lakes/ rivers etc. */ byte feat_shal_liquid; byte feat_deep_liquid; /* Floor terrain */ byte feat_floor; }; extern dun_data *dun; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/grid.h���������������������������������������������������������������������������������0000644�0000000�0000000�00000004424�10250356275�013236� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * File: grid.h * Purpose: header file for grid.c, used only in dungeon generation * files (generate.c, rooms.c) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* Macros */ /* * Access the cave data for the current region */ #define cave_p(X, Y) \ (&cave_data[Y][X]) /* * Access the cave data for the given region */ #define access_region(X, Y, R) \ (&rg_list[R][Y][X]) /* * Set feature on a square */ #define set_feat_bold(X, Y, F) \ (cave_p(X, Y)->feat=(F)) /* * Grid-based version of the above */ #define set_feat_grid(C, F) \ ((C)->feat=(F)) /* Helpful macros */ #define place_secret_door(X,Y) \ set_feat_bold((X), (Y), (FEAT_SECRET)) /* Externs */ extern bool new_player_spot(void); extern void place_random_stairs(int x, int y); extern void place_random_door(int x, int y); extern void vault_objects(int x, int y, int num); extern void vault_traps(int x, int y, int xd, int yd, int num); extern void vault_monsters(int x1, int y1, int num); extern int next_to_walls(int x, int y); extern void generate_room(int x1, int y1, int x2, int y2, int light); extern void generate_vault(int x1, int y1, int x2, int y2); extern void clear_vault(int x1, int y1, int x2, int y2); extern void generate_fill(int x1, int y1, int x2, int y2, int feat); extern void generate_draw(int x1, int y1, int x2, int y2, int feat); extern void generate_line(int x1, int y1, int x2, int y2, int feat); extern void generate_plus(int x1, int y1, int x2, int y2, int feat); extern void generate_door(int x1, int y1, int x2, int y2, bool secret); extern bool get_is_floor(int x, int y); extern void set_floor(int x, int y); extern void build_tunnel(int col1, int row1, int col2, int row2); extern bool build_tunnel2(int x1, int y1, int x2, int y2, int type, int cutoff); extern void generate_hmap(int x0, int y0, int xsiz, int ysiz, int grd, int roug, int cutoff); extern bool generate_fracave(int x0, int y0, int xsize, int ysize, int cutoff, bool light); extern bool generate_lake(int x0, int y0, int xsize, int ysize, int c1, int c2, int c3, byte f1, byte f2, byte f3); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/h-basic.h������������������������������������������������������������������������������0000755�0000000�0000000�00000000603�10250356275�013615� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: h-basic.h */ #ifndef INCLUDED_H_BASIC_H #define INCLUDED_H_BASIC_H /* * The most basic "include" file. * * This file simply includes other low level header files. */ /* System Configuration */ #include "h-config.h" /* System includes/externs */ #include "h-system.h" /* Basic types */ #include "h-type.h" /* Basic constants and macros */ #include "h-define.h" #endif �����������������������������������������������������������������������������������������������������������������������������zangband/src/h-config.h�����������������������������������������������������������������������������0000755�0000000�0000000�00000016127�10250356275�014011� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: h-config.h */ #ifndef INCLUDED_H_CONFIG_H #define INCLUDED_H_CONFIG_H /* * Choose the hardware, operating system, and compiler. * Also, choose various "system level" compilation options. * A lot of these definitions take effect in "h-system.h" * * Note that most of these "options" are defined by the compiler, * the "Makefile", the "project file", or something similar, and * should not be defined by the user. */ /* * OPTION: Compile on a Macintosh machine */ #ifndef MACINTOSH /* #define MACINTOSH */ #endif /* * OPTION: Compile on a Windows machine */ #ifndef WINDOWS /* #define WINDOWS */ #endif /* * OPTION: Compile on an MSDOS machine */ #ifndef MSDOS /* #define MSDOS */ #endif /* * OPTION: Compile on a SYS III version of UNIX */ #ifndef SYS_III /* #define SYS_III */ #endif /* * OPTION: Compile on a SYS V version of UNIX */ #ifndef SYS_V /* #define SYS_V */ #endif /* * OPTION: Compile on a HPUX version of UNIX */ #ifndef HPUX /* #define HPUX */ #endif /* * OPTION: Compile on an SGI running IRIX */ #ifndef SGI /* #define SGI */ #endif /* * OPTION: Compile on a SunOS machine */ #ifndef SUNOS /* #define SUNOS */ #endif /* * OPTION: Compile on a Solaris machine */ #ifndef SOLARIS /* #define SOLARIS */ #endif /* * OPTION: Compile on an ultrix/4.2BSD/Dynix/etc. version of UNIX, * Do not define this if you are on any kind of SunOS. */ #ifndef ULTRIX /* #define ULTRIX */ #endif /* * Extract the "SUNOS" flag from the compiler */ #if defined(sun) # ifndef SUNOS # define SUNOS # endif #endif /* * Extract the "ULTRIX" flag from the compiler */ #if defined(ultrix) || defined(Pyramid) # ifndef ULTRIX # define ULTRIX # endif #endif /* * Extract the "ATARI" flag from the compiler [cjh] */ #if defined(__atarist) || defined(__atarist__) # ifndef ATARI # define ATARI # endif #endif /* * Extract the "ACORN" flag from the compiler */ #ifdef __riscos # ifndef ACORN # define ACORN # endif #endif /* * Extract the "SGI" flag from the compiler */ #ifdef sgi # ifndef SGI # define SGI # endif #endif /* * Extract the "MSDOS" flag from the compiler */ #ifdef __MSDOS__ # ifndef MSDOS # define MSDOS # endif #endif /* * Extract the "WINDOWS" flag from the compiler */ #if defined(_Windows) || defined(__WINDOWS__) || \ defined(__WIN32__) || defined(WIN32) || \ defined(__WINNT__) || defined(__NT__) # ifndef WINDOWS # define WINDOWS # endif #endif /* * Remove the MSDOS flag when using WINDOWS */ #ifdef WINDOWS # ifdef MSDOS # undef MSDOS # endif #endif /* * Remove the WINDOWS flag when using MACINTOSH */ #ifdef MACINTOSH # ifdef WINDOWS # undef WINDOWS # endif #endif /* * OPTION: Define "L64" if a "long" is 64-bits. See "h-types.h". * The only such platform that angband is ported to is currently * DEC Alpha AXP running OSF/1 (OpenVMS uses 32-bit longs). */ #if defined(__alpha) && defined(__osf__) # define L64 #endif /* * We need to define this for Microsoft Dev studio to use the * correct 64bit type defines. We check for support of __int64 * before defining "MSDEV", which is used in h-types.h */ #ifdef __MSVC__ # if ((!defined _INTEGRAL_MAX_BITS) || (_INTEGRAL_MAX_BITS < 64)) # error Your compiler does not seem to have a 64bit type. # else /* !_INTEGRAL_MAX_BITS && _INTEGRAL_MAX_BITS > 64 */ # define MSDEV # endif /* !_INTEGRAL_MAX_BITS && _INTEGRAL_MAX_BITS > 64 */ #endif /* __MSVC__ */ /* * Borland seems to use __int64 as well */ #ifdef __BORLANDC__ # define MSDEV #endif /* __BORLANDC__ */ /* * OPTION: set "SET_UID" if the machine is a "multi-user" machine. * This option is used to verify the use of "uids" and "gids" for * various "Unix" calls, and of "pids" for getting a random seed, * and of the "umask()" call for various reasons, and to guess if * the "kill()" function is available, and for permission to use * functions to extract user names and expand "tildes" in filenames. * It is also used for "locking" and "unlocking" the score file. * Basically, SET_UID should *only* be set for "Unix" machines, * or for the "Atari" platform which is Unix-like, apparently */ #if !defined(MACINTOSH) && !defined(WINDOWS) && \ !defined(MSDOS) && !defined(USE_EMX) && \ !defined(AMIGA) && !defined(ACORN) && !defined(VM) # define SET_UID #endif /* * OPTION: Set "USG" for "System V" versions of Unix * This is used to choose a "lock()" function, and to choose * which header files ("string.h" vs "strings.h") to include. * It is also used to allow certain other options, such as options * involving userid's, or multiple users on a single machine, etc. */ #ifdef SET_UID # if defined(SYS_III) || defined(SYS_V) || defined(SOLARIS) || \ defined(HPUX) || defined(SGI) || defined(ATARI) # ifndef USG # define USG # endif # endif #endif /* * Every system seems to use its own symbol as a path separator. * Default to the standard Unix slash, but attempt to change this * for various other systems. Note that any system that uses the * "period" as a separator (i.e. ACORN) will have to pretend that * it uses the slash, and do its own mapping of period <-> slash. * Note that the VM system uses a "flat" directory, and thus uses * the empty string for "PATH_SEP". */ #undef PATH_SEP #define PATH_SEP "/" #ifdef MACINTOSH # undef PATH_SEP # define PATH_SEP ":" #endif #if defined(WINDOWS) || defined(WINNT) # undef PATH_SEP # define PATH_SEP "\\" #endif #if defined(MSDOS) || defined(OS2) || defined(USE_EMX) # undef PATH_SEP # define PATH_SEP "\\" #endif #ifdef AMIGA # undef PATH_SEP # define PATH_SEP "/" #endif #ifdef __GO32__ # undef PATH_SEP # define PATH_SEP "/" #endif #ifdef VM # undef PATH_SEP # define PATH_SEP "" #endif /* * The Macintosh allows the use of a "file type" when creating a file */ #if defined(MACINTOSH) || defined(MACH_O_CARBON) # define FILE_TYPE_TEXT 'TEXT' # define FILE_TYPE_DATA 'DATA' # define FILE_TYPE_SAVE 'SAVE' # define FILE_TYPE(X) (_ftype = (X)) #else # define FILE_TYPE(X) ((void)0) #endif /* * OPTION: Define "HAS_USLEEP" only if "usleep()" exists. * * Note that this is only relevant for "SET_UID" machines. * Note that new "SOLARIS" and "SGI" machines have "usleep()". */ #ifdef SET_UID # if !defined(HPUX) && !defined(ULTRIX) && !defined(ISC) # define HAS_USLEEP # endif #endif /* * OPTION: Create and use a hidden directory in the users home directory * for storing pref-files and character-dumps. */ #ifdef SET_UID # ifndef PRIVATE_USER_PATH # define PRIVATE_USER_PATH "~/.angband" # endif /* PRIVATE_USER_PATH */ #endif /* SET_UID */ /* * On multiuser systems, add the "uid" to savefile names */ #ifdef SET_UID # define SAVEFILE_USE_UID #endif /* SET_UID */ /* * Hack -- Mach-O (native binary format of OS X) is basically a Un*x * but has Mac OS/Windows-like user interface */ #ifdef MACH_O_CARBON # ifdef SAVEFILE_USE_UID # undef SAVEFILE_USE_UID # endif /* SAVEFILE_USE_UID */ #endif /* MACH_O_CARBON */ /* Include maid-grf.c stuff */ #ifdef ALLOW_BORG #define TERM_CAVE_MAP #define TERM_USE_LIST #endif /* ALLOW_BORG */ #ifdef USE_TNB #define TERM_CAVE_MAP #endif /* USE_TNB */ /* Autoconf derived stuff */ #ifdef SIZEOF_LONG_INT #if SIZEOF_LONG_INT > 4 #define L64 #endif #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/h-define.h�����������������������������������������������������������������������������0000755�0000000�0000000�00000006421�10250356275�013772� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: h-define.h */ #ifndef INCLUDED_H_DEFINE_H #define INCLUDED_H_DEFINE_H /* * Define some simple constants */ /* * Hack -- Define NULL */ #ifndef NULL # ifdef __STDC__ # define NULL ((void*)0) # else # define NULL ((char*)0) # endif /* __STDC__ */ #endif /* NULL */ /* * Hack -- assist "main-acn.c" XXX XXX XXX */ #ifdef ACORN # define O_RDONLY 0 # define O_WRONLY 1 # define O_RDWR 2 #endif /* * Hack -- force definitions -- see fd_seek() */ #ifndef SEEK_SET # define SEEK_SET 0 #endif #ifndef SEEK_CUR # define SEEK_CUR 1 #endif #ifndef SEEK_END # define SEEK_END 2 #endif /* * Hack -- force definitions -- see fd_lock() XXX XXX XXX */ #ifndef F_UNLCK # define F_UNLCK 0 #endif #ifndef F_RDLCK # define F_RDLCK 1 #endif #ifndef F_WRLCK # define F_WRLCK 2 #endif /* * The constants "TRUE" and "FALSE" */ #undef TRUE #define TRUE 1 #undef FALSE #define FALSE 0 /**** Simple "Macros" ****/ /* * Force a character to lowercase/uppercase */ #define FORCELOWER(A) ((isupper((A))) ? tolower((A)) : (A)) #define FORCEUPPER(A) ((islower((A))) ? toupper((A)) : (A)) /* * Non-typed minimum value macro */ #undef MIN #define MIN(a,b) (((a) > (b)) ? (b) : (a)) /* * Non-typed maximum value macro */ #undef MAX #define MAX(a,b) (((a) < (b)) ? (b) : (a)) /* * Non-typed absolute value macro */ #undef ABS #define ABS(a) (((a) < 0) ? (-(a)) : (a)) /* * Non-typed sign extractor macro */ #undef SGN #define SGN(a) (((a) < 0) ? (-1) : ((a) != 0)) /* * Turn on aborts for the assert macro for now */ #define DEBUG_ABORT /* * An assertion macro */ #undef assert /* Pick which type of output to use */ #ifdef DEBUG_CORE # define ANG__assert_fmt core_fmt #else /* DEBUG_CORE */ # define ANG__assert_fmt quit_fmt #endif /* DEBUG_CORE */ /* Pick whether to save the game before aborting */ #ifdef DEBUG_ABORT # define ANG__assert_save ((void) 0) #else # define ANG__assert_save save_player() #endif /* Possibly save the game, and then abort. */ #define assert(expr)\ do\ {\ if (!(expr))\ {\ signals_ignore_tstp();\ ANG__assert_save;\ ANG__assert_fmt("\n%s%s\n%s%s\n%s%d\n\n",\ "Assertion failed: ", #expr,\ "in file ", __FILE__,\ "on line ", __LINE__);\ }\ }\ while (FALSE) /* An assert that can be used in an expression */ #define assert_exp(expr)\ assert_helper(#expr, __FILE__, __LINE__, (expr)) /* * Hack -- allow use of "ASCII" and "EBCDIC" for "indexes", "digits", * and "Control-Characters". * * Note that all "index" values must be "lowercase letters", while * all "digits" must be "digits". Control characters can be made * from any legal characters. XXX XXX XXX */ #ifdef VM # define A2I(X) alphatoindex(X) # define I2A(X) indextoalpha(X) # define D2I(X) ((X) - '0') # define I2D(X) ((char) ((X) + '0')) # define KTRL(X) ((X) & 0x1F) # define ESCAPE '\033' #else # define A2I(X) ((X) - 'a') # define I2A(X) ((char) ((X) + 'a')) # define D2I(X) ((X) - '0') # define I2D(X) ((char) ((X) + '0')) # define KTRL(X) ((X) & 0x1F) # define ESCAPE '\033' #endif /* * Hack - a useful "get array index from pointers" macro. * * We assume P is a pointer to something in array A. */ #define GET_ARRAY_INDEX(A,P) \ ((P)-(A)) /* * Get number of elements in an array */ #define NUM_ELEMENTS(A) \ (sizeof(A) / sizeof ((A)[0])) #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/h-system.h�����������������������������������������������������������������������������0000755�0000000�0000000�00000010310�10250356275�014054� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: h-system.h */ #ifndef INCLUDED_H_SYSTEM_H #define INCLUDED_H_SYSTEM_H /* * Include the basic "system" files. * * Make sure all "system" constants/macros are defined. * Make sure all "system" functions have "extern" declarations. * * This file is a big hack to make other files less of a hack. * This file has been rebuilt -- it may need a little more work. * * It is (very) unlikely that VMS will work without help, primarily * because VMS does not use the "ASCII" character set. */ #include <stdio.h> #include <ctype.h> #include <errno.h> #if defined(NeXT) # include <libc.h> #else # include <stdlib.h> #endif /* NeXT */ #ifdef SET_UID # include <sys/types.h> # if defined(Pyramid) || defined(NeXT) || defined(SUNOS) || \ defined(NCR3K) || defined(SUNOS) || defined(ibm032) || \ defined(__osf__) || defined(ISC) || defined(SGI) || \ defined(linux) # include <sys/time.h> # endif # if !defined(SGI) && !defined(ULTRIX) # include <sys/timeb.h> # endif #endif /* SET_UID */ #include <time.h> #ifdef MACINTOSH # include <unix.h> #endif /* MACINTOSH */ #if defined(WINDOWS) || defined(MSDOS) || defined(USE_EMX) # include <io.h> #endif #if !defined(MACINTOSH) && !defined(AMIGA) && \ !defined(ACORN) && !defined(VM) && !defined(__MWERKS__) # if defined(__TURBOC__) || defined(__WATCOMC__) # include <mem.h> # else # include <memory.h> # endif #endif #if !defined(NeXT) && !defined(__MWERKS__) && !defined(ACORN) # include <fcntl.h> #endif #ifdef SET_UID # ifndef USG # include <sys/param.h> # include <sys/file.h> # endif /* !USG */ # ifdef linux # include <sys/file.h> # endif # include <pwd.h> # include <unistd.h> # include <sys/stat.h> # ifdef SOLARIS # include <netdb.h> # endif #endif /* SET_UID */ #ifdef __DJGPP__ #include <unistd.h> #endif /* __DJGPP__ */ #include <string.h> #include <stdarg.h> /* Include maid-x11.c */ #if defined(USE_X11) || defined(USE_XAW) || defined(USE_XPJ) #define USE_XMAID #endif /* Hack - this should be in h-types.h, but we need errr and cptr here */ /* Error codes for function return values */ /* Success = 0, Failure = -N, Problem = +N */ typedef int errr; /* String pointer */ typedef const char *cptr; /* The init functions for each port called from main.c */ #ifdef USE_XAW extern errr init_xaw(int argc, char **argv); extern cptr help_xaw[]; #endif #ifdef USE_X11 extern errr init_x11(int argc, char **argv); extern cptr help_x11[]; #endif #ifdef USE_XPJ extern errr init_xpj(int argc, char **argv); extern cptr help_xpj[]; #endif #ifdef USE_GCU extern errr init_gcu(void); extern cptr help_gcu[]; #endif #ifdef USE_CAP extern errr init_cap(void); extern cptr help_cap[]; #endif #ifdef USE_DOS extern errr init_dos(void); extern cptr help_dos[]; #endif #ifdef USE_IBM extern errr init_ibm(void); extern cptr help_ibm[]; #endif #ifdef USE_EMX extern errr init_emx(void); extern cptr help_emx[]; #endif #ifdef USE_SLA extern errr init_sla(void); extern cptr help_sla[]; #endif #ifdef USE_AMI extern errr init_ami(void); extern cptr help_ami[]; #endif #ifdef USE_VME extern errr init_vme(void); extern cptr help_vme[]; #endif #ifdef USE_LSL extern errr init_lsl(void); extern cptr help_lsl[]; #endif #ifdef USE_GTK extern errr init_gtk(int argc, char **argv, unsigned char *new_game); extern cptr help_gtk[]; #endif #ifdef USE_VCS extern errr init_vcs(int argc, char **argv); extern cptr help_vcs[]; #endif #ifdef USE_TNB extern errr init_tnb(int argc, cptr *argv); extern cptr help_tnb[]; #endif /* * Type used to access a module */ typedef struct module_type module_type; struct module_type { cptr name; cptr *help; errr (*init) (int argc, char **argv, unsigned char *new_game); }; /* * Macro to make an entry for a port in main.c's list. * * This expands 'INIT_MODULE(port)' to be: * * { "port", help_port, init_port } (Without the type-cast. ) * * When adding new ports make sure you use the correct parameter * types to init_"port_name"(). If you need to add a new one, * add it on the end of the list. (You don't have to use all the * parameters due to the way C passes them on the stack.) */ #define INIT_MODULE(P) \ { #P, help_##P, (errr (*)(int, char **, unsigned char *)) init_##P } #endif /* INCLUDED_H_SYSTEM_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/h-type.h�������������������������������������������������������������������������������0000755�0000000�0000000�00000007617�10250356275�013531� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: h-type.h */ #ifndef INCLUDED_H_TYPE_H #define INCLUDED_H_TYPE_H /* * Basic "types". * * Note the attempt to make all basic types have 4 letters. * This improves readibility and standardizes the code. * * Likewise, all complex types are at least 4 letters. * Thus, almost every 1 to 3 letter word is a legal variable, * except for certain reserved words ('for' and 'if' and 'do'). * * Note that the type used in structures for bit flags should be uint. * As long as these bit flags are sequential, they will be space smart. * * Note that on some machines, apparently "signed char" is illegal. * * A char/byte takes exactly 1 byte * A s16b/u16b takes exactly 2 bytes * A s32b/u32b takes exactly 4 bytes * * A sint/uint takes at least 2 bytes * A long/huge takes at least 4 bytes * * A real normally takes from 4 to 10 bytes * A vptr normally takes 4 (rarely 8) bytes * * Note that some files have already been included by "h-include.h" * These include <stdio.h> and <sys/types>, which define some types * In particular, "bool", "byte", "uint", and "huge" may be defined * already, possibly using "typedefs" of various kinds, and possibly * being defined to something other than required by my code. So we * simply redefine them all using a stupid "_hack" suffix. * * Also, see <limits.h> for min/max values for sint, uint, long, huge * (INT_MIN, INT_MAX, 0, UINT_MAX, LONG_MIN, LONG_MAX, 0, ULONG_MAX). * These limits should be verified and coded into "h-constant.h", or * perhaps not, since those types have "unknown" length by definition. */ /*** Special 4 letter names for some standard types ***/ /* A generic pointer */ typedef void *vptr; /* * Hack -- prevent problems with non-MACINTOSH */ #undef uint #define uint uint_hack /* * Hack -- prevent problems with MSDOS and WINDOWS */ #undef huge #define huge huge_hack /* * Hack -- prevent problems with AMIGA */ #undef byte #define byte byte_hack /* * Hack -- prevent problems with C++ */ #undef bool #define bool bool_hack /* Note that "signed char" is not always "defined" */ /* So always use "s16b" to hold small signed values */ /* A signed byte of memory */ /* typedef signed char syte; */ /* Note that unsigned values can cause math problems */ /* An unsigned byte of memory */ typedef unsigned char byte; /* Note that a bool is smaller than a full "int" */ /* Simple True/False type */ typedef char bool; /* A signed, standard integer (at least 2 bytes) */ typedef int sint; /* An unsigned, "standard" integer (often pre-defined) */ typedef unsigned int uint; /* The largest possible unsigned integer */ typedef unsigned long huge; /* Signed/Unsigned 16 bit value */ typedef signed short s16b; typedef unsigned short u16b; /* Signed/Unsigned 32 bit value */ #ifdef L64 /* 64 bit longs */ typedef signed int s32b; typedef unsigned int u32b; #ifdef USE_64B /* Signed/Unsigned 64bit value */ typedef long u64b; typedef unsigned long s64b; #endif /* USE_64B */ #else /* L64 */ typedef signed long s32b; typedef unsigned long u32b; #ifdef USE_64B /* Try to get a 64 bit type */ # if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L # include <stdint.h> # define ANG_U64B uint64_t # define ANG_S64B int64_t # endif /* __STDC__ && __STDC_VERSION__ */ /* Define this for Microsoft Dev Studio C++ 6.0 */ # ifdef MSDEV # define ANG_U64B unsigned __int64 # define ANG_S64B __int64 # endif /* MSDEV */ /* Define this if you have <sys/types.h> with an old compiler */ # if defined HAS_SYS_TYPES && !defined ANG_U64B # include <sys/types.h> # define ANG_U64B u_int64_t # define ANG_S64B int64_t # endif /* HAS_SYS_TYPES */ /* Attempt to use "long long" which is semi-standard for older compilers */ # ifndef ANG_U64B # define ANG_U64B unsigned long long # define ANG_S64B long long # endif /* ANG_U64B */ /* Define the 64bit types */ typedef ANG_U64B u64b; typedef ANG_S64B s64b; #endif /* USE_64B */ #endif /* L64 */ #endif �����������������������������������������������������������������������������������������������������������������zangband/src/init.h���������������������������������������������������������������������������������0000644�0000000�0000000�00000007777�10250356275�013272� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: init.h */ /* * Copyright (c) 2000 Robert Ruehlmann * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ #ifndef INCLUDED_INIT_H #define INCLUDED_INIT_H #include "h-basic.h" /* * Parse errors */ #define PARSE_ERROR_GENERIC 1 #define PARSE_ERROR_OBSOLETE_FILE 2 #define PARSE_ERROR_MISSING_RECORD_HEADER 3 #define PARSE_ERROR_NON_SEQUENTIAL_RECORDS 4 #define PARSE_ERROR_INVALID_FLAG 5 #define PARSE_ERROR_UNDEFINED_DIRECTIVE 6 #define PARSE_ERROR_OUT_OF_MEMORY 7 #define PARSE_ERROR_OUT_OF_BOUNDS 8 #define PARSE_ERROR_TOO_FEW_ARGUMENTS 9 #define PARSE_ERROR_TOO_MANY_ARGUMENTS 10 #define PARSE_ERROR_TOO_MANY_ALLOCATIONS 11 #define PARSE_ERROR_INVALID_SPELL_FREQ 12 #define PARSE_ERROR_INVALID_ITEM_NUMBER 13 #define PARSE_ERROR_TOO_MANY_ENTRIES 14 #define PARSE_ERROR_MAX 15 typedef struct header header; typedef errr (*parse_info_txt_func) (char *buf, header *head); /* * Template file header information (see "init.c"). 16 bytes. * * Note that the sizes of many of the "arrays" are between 32768 and * 65535, and so we must use "unsigned" values to hold the "sizes" of * these arrays below. Normally, I try to avoid using unsigned values, * since they can cause all sorts of bizarre problems, but I have no * choice here, at least, until the "race" array is split into "normal" * and "unique" monsters, which may or may not actually help. * * Note that, on some machines, for example, the Macintosh, the standard * "read()" and "write()" functions cannot handle more than 32767 bytes * at one time, so we need replacement functions, see "util.c" for details. * * Note that, on some machines, for example, the Macintosh, the standard * "malloc()" function cannot handle more than 32767 bytes at one time, * but we may assume that the "ralloc()" function can handle up to 65535 * butes at one time. We should not, however, assume that the "ralloc()" * function can handle more than 65536 bytes at a time, since this might * result in segmentation problems on certain older machines, and in fact, * we should not assume that it can handle exactly 65536 bytes at a time, * since the internal functions may use an unsigned short to specify size. * * In general, these problems occur only on machines (such as most personal * computers) which use 2 byte "int" values, and which use "int" for the * arguments to the relevent functions. */ struct header { byte v_major; /* Version -- major */ byte v_minor; /* Version -- minor */ byte v_patch; /* Version -- patch */ byte v_extra; /* Version -- extra */ u16b info_num; /* Number of "info" records */ u16b info_len; /* Size of each "info" record */ u32b head_size; /* Size of the "header" in bytes */ u32b info_size; /* Size of the "info" array in bytes */ u32b name_size; /* Size of the "name" array in bytes */ u32b text_size; /* Size of the "text" array in bytes */ void *info_ptr; char *name_ptr; char *text_ptr; void *mmap_base; parse_info_txt_func parse_info_txt; }; extern errr init_info_txt(FILE *fp, char *buf, header *head, parse_info_txt_func parse_info_txt_line); #ifdef ALLOW_TEMPLATES extern errr parse_z_info(char *buf, header *head); extern errr parse_v_info(char *buf, header *head); extern errr parse_f_info(char *buf, header *head); extern errr parse_k_info(char *buf, header *head); extern errr parse_a_info(char *buf, header *head); extern errr parse_e_info(char *buf, header *head); extern errr parse_r_info(char *buf, header *head); /* * Error tracking */ extern int error_idx; extern int error_line; extern cptr err_str[PARSE_ERROR_MAX]; #endif /* ALLOW_TEMPLATES */ /* * File headers */ extern header z_head; extern header v_head; extern header f_head; extern header k_head; extern header a_head; extern header e_head; extern header r_head; #endif /* INCLUDED_INIT_H */ �zangband/src/maid-grf.h�����������������������������������������������������������������������������0000644�0000000�0000000�00000015425�10250356275�014002� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: maid-grf.h */ /* Purpose: Common header file for graphics ports */ /* * Copyright (c) 2002 Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* * List of callback types */ #define CALL_MAP_INFO 1 #define CALL_MAP_ERASE 2 #define CALL_PLAYER_MOVE 3 #define CALL_OBJECT_LIST 4 #define CALL_MAX 5 /* * Callback type (Ignoring parameters) */ typedef void (*callback_type) (void); /* * Linked list for callbacks */ typedef struct callback_list callback_list; struct callback_list { callback_type func; /* Function to call */ callback_list *next; /* Next callback in list */ vptr data; /* Data for callbacks so can use same function multiple times */ }; extern void set_callback(callback_type call_func, int number, vptr data); extern void del_callback(int number, vptr data); /* * Number of blocks in the overhead map cache. * * This number must be bigger than the number of blocks * required to remember the biggest dungeon level. */ #define MAP_CACHE (WILD_VIEW * WILD_VIEW * 2) /* * Constants used to pass information to users * of the overhead map hooks. */ #define MAP_SEEN 0x01 /* GRID_SEEN equivalent */ #define MAP_GLOW 0x02 /* CAVE_GLOW equivalent */ #define MAP_LITE 0x04 /* GRID_LITE equivalent */ #define MAP_ONCE 0x08 /* This block has ever been seen */ /* * Constants used to describe the state of monsters * to users of the overhead map */ #define MONST_ASLEEP 0x01 #define MONST_FRIEND 0x02 #define MONST_PET 0x04 #define MONST_CONFUSED 0x08 #define MONST_FEAR 0x10 #define MONST_STUN 0x20 #define MONST_INVULN 0x40 /* * Make an iterator, so we can scan the map quickly */ #define MAP_ITT_START(M) \ do { \ int _map_count;\ \ for (_map_count = 0; _map_count < MAP_CACHE; _map_count++) \ { \ int _map_i, _map_j; \ \ if (map_cache_x[_map_count] == -1) continue; \ \ if (map_grid[map_cache_y[_map_count]][map_cache_x[_map_count]] == -1)\ continue; \ \ for (_map_i = 0; _map_i < WILD_BLOCK_SIZE; _map_i++) \ { \ for (_map_j = 0; _map_j < WILD_BLOCK_SIZE; _map_j++) \ { \ (M) = &map_cache[_map_count][_map_j][_map_i]; #define MAP_ITT_END \ } \ } \ } \ } while (0) /* Macro to extract location during iteration */ #define MAP_GET_LOC(X, Y)\ (((X) = _map_i + map_cache_x[_map_count] * WILD_BLOCK_SIZE), \ ((Y) = _map_j + map_cache_y[_map_count] * WILD_BLOCK_SIZE)) /* * Map data structure */ typedef struct term_map term_map; struct term_map { /* Information about what is at the grid */ s16b object; s16b monster; s16b field; byte terrain; char unknown; /* Location */ u16b x; u16b y; /* Player-known flags */ byte flags; /* Monster flags */ byte m_flags; /* Rough measure of monster hp */ byte m_hp; /* Priority for overhead mini map */ byte priority; /* Map information about square */ byte a; char c; byte ta; char tc; }; typedef struct map_block map_block; struct map_block { /* Save what it looks like */ byte a; char c; byte ta; char tc; /* Save the cave info itself - used by the borg */ #ifdef TERM_CAVE_MAP s16b object; s16b monster; s16b field; byte terrain; /* unknown mimics or flavoured objects */ char unknown; /* Monster flags */ byte m_flags; /* Monster hp (scaled) */ byte m_hp; #endif /* TERM_CAVE_MAP */ /* Borg-specific stuff */ #ifdef ALLOW_BORG u16b fear; /* fear value */ u16b kill; /* Entry into "kill" list */ u16b take; /* Entry into "take" list */ u16b trap; /* MT - Entry into "trap" list */ u16b m_effect; /* MT - Magic Effect, like glyphs */ byte feat; /* Terrain feature */ byte info; /* info flags */ byte flow; /* "flow" data */ byte cost; /* "cost" data */ byte detect; /* Detection flags */ byte xtra; /* search count */ #endif /* ALLOW_BORG */ /* We need to save the flags to get the refcounting right. */ byte flags; /* Priority of tile (For overhead map display) */ byte priority; }; typedef map_block *map_blk_ptr; typedef map_block **map_blk_ptr_ptr; typedef void (*map_info_hook_type) (map_block *mb_ptr, const term_map *map, vptr data); typedef void (*map_erase_hook_type) (vptr data); typedef void (*player_move_hook_type) (int x, int y, vptr data); /* * This is used by the Borg and by ports that would * like to access lists of objects */ #ifdef TERM_USE_LIST /* * Object List data structure */ typedef struct term_list term_list; struct term_list { cptr o_name; /* Name */ cptr xtra_name; /* Extra Name (Artifacts and ego items) */ u32b kn_flags[4]; /* Known Flags */ s32b cost; /* Object "base cost" */ s16b k_idx; /* Kind index (zero if "unknown") */ s16b weight; /* Item weight */ s16b to_h; /* Bonus to hit */ s16b to_d; /* Bonus to dam */ s16b to_a; /* Bonus to ac */ s16b ac; /* Armor class */ byte dd; /* Damage dice */ byte ds; /* Damage sides */ byte number; /* Number of items */ byte info; /* Special flags */ s16b timeout; /* Timeout Counter */ s16b pval; /* Particular value */ byte tval; /* Item type (from kind) */ }; typedef struct list_item list_item; struct list_item { cptr o_name; /* Name */ cptr xtra_name; /* Extra Name (Artifacts and ego items) */ u32b kn_flags[4]; /* Known Flags */ s32b cost; /* Object "base cost" */ s16b k_idx; /* Kind index (zero if "dead") */ s16b weight; /* Item weight */ s16b to_h; /* Bonus to hit */ s16b to_d; /* Bonus to dam */ s16b to_a; /* Bonus to ac */ s16b ac; /* Armor class */ byte dd; /* Damage dice */ byte ds; /* Damage sides */ byte number; /* Number of items */ byte info; /* Special flags */ s16b timeout; /* Timeout Counter */ s16b pval; /* Particular value */ byte tval; /* Item type (from kind) */ /* Save the glyph to use */ #ifdef TERM_OBJ_GLYPH u16b feature_code; #endif /* TERM_OBJ_GLYPH */ #ifdef ALLOW_BORG byte treat_as; /* Treat item as if it is in a different list */ #endif /* ALLOW_BORG */ }; typedef void (*list_notice_hook_type) (byte, vptr data); #endif /* TERM_USE_LIST */ /* Extern Variables */ extern byte gamma_table[256]; extern map_blk_ptr_ptr *map_cache; extern s16b *map_cache_refcount; extern int *map_cache_x; extern int *map_cache_y; extern int **map_grid; #ifdef TERM_USE_LIST extern list_item *equipment; extern int equip_num; extern list_item *inventory; extern int inven_num; extern list_item *cur_list; extern int cur_num; #endif /* TERM_USE_LIST */ /* Extern Functions */ extern void build_gamma_table(int gamma); extern cptr get_default_font(int term_num); extern bool pick_graphics(int graphics, int *xsize, int *ysize, char *filename); extern bool is_bigtiled(int x, int y); extern void toggle_bigtile(void); extern void map_get_player(int *x, int *y); extern bool map_in_bounds(int x, int y); extern map_block *map_loc(int dx, int dy); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/maid-x11.h�����������������������������������������������������������������������������0000644�0000000�0000000�00000003106�10250356275�013626� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: maid-x11.h */ /* Purpose: Common header file for various x11 ports */ /* * Copyright (c) 2001 Robert Ruehlmann, Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "maid-grf.h" #ifndef IsModifierKey /* * Keysym macros, used on Keysyms to test for classes of symbols * These were stolen from one of the X11 header files */ #define IsKeypadKey(keysym) \ (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal)) #define IsCursorKey(keysym) \ (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select)) #define IsPFKey(keysym) \ (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4)) #define IsFunctionKey(keysym) \ (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35)) #define IsMiscFunctionKey(keysym) \ (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space)) #define IsModifierKey(keysym) \ (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R)) #endif /* IsModifierKey */ /* * Checks if the keysym is a special key or a normal key * Assume that XK_MISCELLANY keysyms are special */ #define IsSpecialKey(keysym) \ ((unsigned)(keysym) >= 0xFF00) extern u32b create_pixel(Display *dpy, byte red, byte green, byte blue); extern XImage *ReadBMP(Display *dpy, char *Name); extern bool smoothRescaling; extern XImage *ResizeImage(Display *dpy, XImage *Im, int ix, int iy, int ox, int oy); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/readdib.h������������������������������������������������������������������������������0000755�0000000�0000000�00000000677�10250356275�013714� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: readdib.h */ /* * This file has been modified for use with "Angband 2.8.2" * * Copyright 1991 Microsoft Corporation. All rights reserved. */ /* * Information about a bitmap */ typedef struct { HANDLE hDIB; HBITMAP hBitmap; HPALETTE hPalette; BYTE CellWidth; BYTE CellHeight; } DIBINIT; /* Read a DIB from a file */ extern BOOL ReadDIB(HWND, LPSTR, DIBINIT *); /* Free a DIB */ extern void FreeDIB(DIBINIT *dib); �����������������������������������������������������������������zangband/src/rooms.h��������������������������������������������������������������������������������0000644�0000000�0000000�00000000600�10250356275�013440� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * File: rooms.h * Purpose: Header file for rooms.c, used only in generate.c */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* Externs */ extern bool room_build(void); ��������������������������������������������������������������������������������������������������������������������������������zangband/src/script.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000002112�10250356275�013605� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: script.h */ #ifndef INCLUDED_SCRIPT_H #define INCLUDED_SCRIPT_H #include "angband.h" /* * Initalize the scripting support */ extern errr script_init(void); /* * Free the resources for the scripting support */ extern errr script_free(void); /* * Display the script debug menu */ extern void do_cmd_script(void); /* * Execute a string of scripting code */ extern bool script_do_string(cptr script); /* * Execute a file with scripting code */ extern bool script_do_file(cptr filename); /* * Execute one of the scripts attached to an object */ extern void apply_object_trigger(int trigger_id, object_type *o_ptr, cptr format, ...); /* * Callback for using an object */ extern bool use_object(object_type *o_ptr, bool *ident, int aim); /* * Execute a script attached to a field */ extern bool apply_field_trigger(cptr script, field_type *f_ptr, cptr format, va_list vp); extern void const_field_trigger(cptr script, const field_type *f_ptr, cptr format, va_list vp); /* * Debug lua stack depth */ extern void debug_lua_stack(void); #endif /* INCLUDED_SCRIPT_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/streams.h������������������������������������������������������������������������������0000644�0000000�0000000�00000001107�10250356275�013762� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * File: streams.h * Purpose: header file for streams.c. This is only used in generate.c. */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* Externs */ extern void add_river(int feat1, int feat2); extern void build_streamer(int feat, int chance); extern void place_trees(int x, int y); extern void build_lake(byte f1, byte f2, byte f3); extern void build_cavern(void); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/types.h��������������������������������������������������������������������������������0000755�0000000�0000000�00000124436�10250356275�013466� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: types.h */ /* Purpose: global type declarations */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* * Note that "char" may or may not be signed, and that "signed char" * may or may not work on all machines. So always use "s16b" or "s32b" * for signed values. Also, note that unsigned values cause math problems * in many cases, so try to only use "u16b" and "u32b" for "bit flags", * unless you really need the extra bit of information, or you really * need to restrict yourself to a single byte for storage reasons. * * Also, if possible, attempt to restrict yourself to sub-fields of * known size (use "s16b" or "s32b" instead of "int", and "byte" instead * of "bool"), and attempt to align all fields along four-byte words, to * optimize storage issues on 32-bit machines. Also, avoid "bit flags" * since these increase the code size and slow down execution. When * you need to store bit flags, use one byte per flag, or, where space * is an issue, use a "byte" or "u16b" or "u32b", and add special code * to access the various bit flags. * * Many of these structures were developed to reduce the number of global * variables, facilitate structured program design, allow the use of ascii * template files, simplify access to indexed data, or facilitate efficient * clearing of many variables at once. * * Certain data is saved in multiple places for efficient access, currently, * this includes the tval/sval/weight fields in "object_type", various fields * in "header_type", and the "m_idx" and "o_idx" fields in "cave_type". All * of these could be removed, but this would, in general, slow down the game * and increase the complexity of the code. */ /* * Information about maximal indices of certain arrays * Actually, these are not the maxima, but the maxima plus one */ typedef struct maxima maxima; struct maxima { u32b fake_text_size; u32b fake_name_size; u16b f_max; /* Max size for "f_info[]" */ u16b k_max; /* Max size for "k_info[]" */ u16b a_max; /* Max size for "a_info[]" */ u16b e_max; /* Max size for "e_info[]" */ u16b r_max; /* Max size for "r_info[]" */ u16b v_max; /* Max size for "v_info[]" */ u16b q_max; /* Max size for quest array */ u16b t_max; /* Max size for field types array */ u16b fld_max; /* Max size for field list */ u16b rg_max; /* Max size for region list */ u16b wn_max; /* Max size for wilderness tree nodes */ u16b wt_max; /* Max size for wilderness gen types */ u16b wp_max; /* Max places in the wilderness */ u16b o_max; /* Max size for "o_list[]" */ u16b m_max; /* Max size for "m_list[]" */ }; /* * Structure used to store information required for LOS * calculatations. The same data can be inverted to * get the squares affected by a projection. Those squares * can be iterated over to get a 'flight path' for arrows * and thrown items... */ typedef struct project_type project_type; struct project_type { /* Offset of square */ byte x; byte y; /* Index into array if this square is a wall */ byte slope; byte square; }; /* * Information about terrain "features" */ typedef struct feature_type feature_type; struct feature_type { u32b name; /* Name (offset) */ u32b text; /* Text (offset) */ byte d_attr; /* Default feature attribute */ char d_char; /* Default feature character */ byte x_attr; /* Desired feature attribute */ char x_char; /* Desired feature character */ byte w_attr; /* Desired extra feature attribute */ char w_char; /* Desired extra feature character */ byte flags; /* Properties of the feature */ }; /* * Hack - a type for 'object flags' */ typedef struct object_flags object_flags; struct object_flags { u32b flags[4]; }; /* * Information about object "kinds", including player knowledge. * * Only "aware" and "tried" are saved in the savefile */ typedef struct object_kind object_kind; struct object_kind { u32b name; /* Name (offset) */ u32b text; /* Text (offset) */ byte tval; /* Object type */ byte sval; /* Object sub type */ s16b pval; /* Object extra info */ s16b to_h; /* Bonus to hit */ s16b to_d; /* Bonus to damage */ s16b to_a; /* Bonus to armor */ s16b ac; /* Base armor */ byte dd, ds; /* Damage dice/sides */ s16b weight; /* Weight */ s32b cost; /* Object "base cost" */ u32b flags[4]; /* Flags */ byte locale[4]; /* Allocation level(s) */ byte chance[4]; /* Allocation chance(s) */ byte level; /* Level */ byte extra; /* Rarity (for special randarts) */ u32b trigger[MAX_TRIGGER]; /* Special object scripts */ byte d_attr; /* Default object attribute */ char d_char; /* Default object character */ byte x_attr; /* Desired object attribute */ char x_char; /* Desired object character */ byte flavor; /* Special object flavor (or zero) */ bool easy_know; /* This object is always known (if aware) */ bool aware; /* The player is "aware" of the item's effects */ bool tried; /* The player has "tried" one of the items */ }; /* * "Themed" objects. * Probability in percent for each class of objects to be dropped. * This could perhaps be an array - but that wouldn't be as clear. */ typedef struct obj_theme obj_theme; struct obj_theme { byte treasure; byte combat; byte magic; byte tools; }; /* * Information about "artifacts". * * Note that the save-file only writes "cur_num" to the savefile. * * Note that "max_num" is always "1" (if that artifact "exists") */ typedef struct artifact_type artifact_type; struct artifact_type { u32b name; /* Name (offset) */ u32b text; /* Text (offset) */ byte tval; /* Artifact type */ byte sval; /* Artifact sub type */ s16b pval; /* Artifact extra info */ s16b to_h; /* Bonus to hit */ s16b to_d; /* Bonus to damage */ s16b to_a; /* Bonus to armor */ s16b ac; /* Base armor */ byte dd, ds; /* Damage when hits */ s16b weight; /* Weight */ s32b cost; /* Artifact "cost" */ u32b flags[4]; /* Artifact Flags */ byte level; /* Artifact level */ byte rarity; /* Artifact rarity */ u32b trigger[MAX_TRIGGER]; /* Special object scripts */ byte cur_num; /* Number created (0 or 1) */ byte max_num; /* Unused (should be "1") */ }; /* * Information about "ego-items". */ typedef struct ego_item_type ego_item_type; struct ego_item_type { u32b name; /* Name (offset) */ u32b text; /* Text (offset) */ byte slot; /* Standard slot value */ byte rating; /* Rating boost */ byte level; /* Minimum level */ byte rarity; /* Object rarity */ s16b max_to_h; /* Maximum to-hit bonus */ s16b max_to_d; /* Maximum to-dam bonus */ s16b max_to_a; /* Maximum to-ac bonus */ s16b max_pval; /* Maximum pval */ s32b cost; /* Ego-item "cost" */ u32b flags[4]; /* Ego-Item Flags */ u32b trigger[MAX_TRIGGER]; /* Special object scripts */ }; /* * Monster blow structure * * - Method (RBM_*) * - Effect (RBE_*) * - Damage Dice * - Damage Sides */ typedef struct monster_blow monster_blow; struct monster_blow { byte method; byte effect; byte d_dice; byte d_side; }; /* * Monster "race" information, including racial memories * * Note that "d_attr" and "d_char" are used for MORE than "visual" stuff. * * Note that "x_attr" and "x_char" are used ONLY for "visual" stuff. * * Note that "cur_num" (and "max_num") represent the number of monsters * of the given race currently on (and allowed on) the current level. * This information yields the "dead" flag for Unique monsters. * * Note that "max_num" is reset when a new player is created. * Note that "cur_num" is reset when a new level is created. * * Note that several of these fields, related to "recall", can be * scrapped if space becomes an issue, resulting in less "complete" * monster recall (no knowledge of spells, etc). All of the "recall" * fields have a special prefix to aid in searching for them. */ typedef struct monster_race monster_race; struct monster_race { u32b name; /* Name (offset) */ u32b text; /* Text (offset) */ s16b hdice; /* Creatures hit dice count */ s16b hside; /* Creatures hit dice sides */ s16b ac; /* Armour Class */ s16b sleep; /* Inactive counter (base) */ byte aaf; /* Area affect radius (1-100) */ byte speed; /* Speed (normally 110) */ s32b mexp; /* Exp value for kill */ s16b extra; /* Unused (for now) */ byte freq_inate; /* Inate spell frequency */ byte freq_spell; /* Other spell frequency */ u32b flags[9]; /* Flags 1 (general) */ /* Flags 2 (abilities) */ /* Flags 3 (race/resist) */ /* Flags 4 (inate/breath) */ /* Flags 5 (normal spells) */ /* Flags 6 (special spells) */ /* Flags 7 (movement related abilities) */ /* Flags 8 (wilderness info) */ /* Flags 9 (drops info) */ monster_blow blow[4]; /* Up to four blows per round */ byte level; /* Level of creature */ byte rarity; /* Rarity of creature */ byte d_attr; /* Default monster attribute */ char d_char; /* Default monster character */ byte x_attr; /* Desired monster attribute */ char x_char; /* Desired monster character */ byte max_num; /* Maximum population allowed per level */ byte cur_num; /* Monster population on current level */ s16b r_sights; /* Count sightings of this monster */ s16b r_deaths; /* Count deaths from this monster */ s16b r_pkills; /* Count monsters killed in this life */ s16b r_tkills; /* Count monsters killed in all lives */ byte r_wake; /* Number of times woken up (?) */ byte r_ignore; /* Number of times ignored (?) */ byte r_xtra1; /* Something (unused) */ byte r_xtra2; /* Something (unused) */ byte r_drop_gold; /* Max number of gold dropped at once */ byte r_drop_item; /* Max number of item dropped at once */ byte r_cast_inate; /* Max number of inate spells seen */ byte r_cast_spell; /* Max number of other spells seen */ byte r_blows[4]; /* Number of times each blow type was seen */ u32b r_flags[9]; /* Observed racial flags */ obj_theme obj_drop; /* Type of objects to drop when killed */ u16b r_see; /* Number of monsters of this type visible */ }; /* * Information about "vault generation" */ typedef struct vault_type vault_type; struct vault_type { u32b name; /* Name (offset) */ u32b text; /* Text (offset) */ byte typ; /* Vault type */ byte rat; /* Vault rating */ byte hgt; /* Vault height */ byte wid; /* Vault width */ }; /* * A single "grid" in a Cave * * Note that several aspects of the code restrict the actual cave * to a max size of 256 by 256. In partcular, locations are often * saved as bytes, limiting each coordinate to the 0-255 range. * * The "o_idx" and "m_idx" fields are very interesting. There are * many places in the code where we need quick access to the actual * monster or object(s) in a given cave grid. The easiest way to * do this is to simply keep the index of the monster and object * (if any) with the grid, but this takes 198*66*4 bytes of memory. * Several other methods come to mind, which require only half this * amound of memory, but they all seem rather complicated, and would * probably add enough code that the savings would be lost. So for * these reasons, we simply store an index into the "o_list" and * "m_list" arrays, using "zero" when no monster/object is present. * * Note that "o_idx" is the index of the top object in a stack of * objects, using the "next_o_idx" field of objects (see below) to * create the singly linked list of objects. If "o_idx" is zero * then there are no objects in the grid. * * Note the special fields for the "MONSTER_FLOW" code. */ typedef struct cave_type cave_type; struct cave_type { byte info; /* Hack -- cave flags */ byte feat; /* Hack -- feature type */ s16b o_idx; /* Object in this grid */ s16b m_idx; /* Monster in this grid */ s16b fld_idx; /* Field in this grid */ byte cost; /* Hack -- cost of flowing */ byte when; /* Hack -- when cost was computed */ }; /* * Cave data that is player-specific * * The feat is the memorized feat on this square */ typedef struct pcave_type pcave_type; struct pcave_type { byte player; /* Player-specific flags */ byte feat; /* Memorized feature */ }; /* * Simple structure to hold a map location */ typedef struct coord coord; struct coord { u16b y; u16b x; }; /* * Pointer to a 16x16 block of cave grids. * The grids are allocated and deallocated in large * blocks for speed. * * Note - blocks also do not use the region code * because of speed. (We don't need to constantly * allocate and deallocate everything.) */ typedef cave_type **blk_ptr; /* * Alias of block pointer - region type * * Whilst these are the same type as blk_ptr, they * are used in a different way. (Allocating large * chucks at a time.) */ typedef cave_type **region_type; /* * Pointer to a 16x16 block of grids of player information. * The grids are allocated and deallocated in large * blocks for speed. */ typedef pcave_type **pblk_ptr; /* * Region equivalent of the above. */ typedef pcave_type **pregion_type; /* * Region information * * Note - most of this stuff is dungeon specific. * * Note - the reason why this isn't part of region_type * is because we don't want to have to add offsets all * the time for every cave access. That would be slow. */ typedef struct region_info region_info; struct region_info { u16b xsize; u16b ysize; u16b refcount; byte flags; }; /* * Structure used to generate the wilderness. * This stores the "height", "population" and "law" results * after the initial plasma fractal routines. * These values are then converted into simple look up numbers * for the wilderness generation type and wandering monster type. */ typedef struct wild_gen1_type wild_gen1_type; struct wild_gen1_type { u16b hgt_map; u16b pop_map; u16b law_map; }; /* * Structure used during creation of wilderness. * It contains the transition state between the above * structure and the one that follows. */ typedef struct wild_gen2_type wild_gen2_type; struct wild_gen2_type { byte hgt_map; byte pop_map; byte law_map; byte place; byte dummy; byte info; }; /* Structure used to hold the completed wilderness */ typedef struct wild_done_type wild_done_type; struct wild_done_type { u16b wild; byte place; byte info; byte mon_gen; byte mon_prob; }; /* * To save room, the above three structures are combined to form the completed * wilderness type. */ typedef union wild_type wild_type; union wild_type { wild_gen1_type gen; wild_gen2_type trans; wild_done_type done; }; /* * An array of this structure is used to work out what wilderness type * is at each 16x16 block. */ typedef struct wild_choice_tree_type wild_choice_tree_type; struct wild_choice_tree_type { /* * Stores what type of node this is - * both what type of cutoff (hgt,pop,law) * and whether the pointers reference * another tree node- or a wilderness * generation type. */ byte info; /* cutoff for the split of the virtual BSP tree */ byte cutoff; /* * chance1/(chance1+chance2) = prob. of going down * the "left" branch. (This is used when several * wilderness generation functions inhabit the same * area of parameter space. This is used to select * between the possibilities randomly. */ byte chance1; byte chance2; /* * These point to the left and right branches of the tree. * Note - that since these also need to reference a wild.gen.type. * these are index numbers of the "choice" or "gen" arrays. * (depending on the value of info) */ u16b ptrnode1; u16b ptrnode2; }; /* * This type is used to describe a region in parameter space * for wilderness generation. */ typedef struct wild_bound_box_type wild_bound_box_type; struct wild_bound_box_type { /* Min and max values for the cuboid in the parameter space */ byte hgtmin; byte hgtmax; byte popmin; byte popmax; byte lawmin; byte lawmax; }; /* * This data type stores the information on a particular * wilderness generation type for 16x16 blocks, so the * blocks can be * 1) Made. * 2) Looked at on the overhead map. */ typedef struct wild_gen_data_type wild_gen_data_type; struct wild_gen_data_type { byte feat; /* The feature to look like on the overhead map */ byte gen_routine; /* Generation routine number */ /* * Course type - used in testing to see where monsters go * in the wilderness. */ byte rough_type; byte chance; /* Chance for this type vs others */ byte data[8]; /* data for generation routine */ }; /* * Object information, for a specific object. * * Note that a "discount" on an item is permanent and never goes away. * * Note that inscriptions are now handled via the "quark_str()" function * applied to the "note" field, which will return NULL if "note" is zero. * * Note that "object" records are "copied" on a fairly regular basis, * and care must be taken when handling such objects. * * "object flags" are now stored in the object data type, as are the * known flags. This allows the identification status of each flag * to be stored seperately. * * Each cave grid points to one (or zero) objects via the "o_idx" * field (above). Each object then points to one (or zero) objects * via the "next_o_idx" field, forming a singly linked list, which * in game terms, represents a "stack" of objects in the same grid. * * Each monster points to one (or zero) objects via the "hold_o_idx" * field (below). Each object then points to one (or zero) objects * via the "next_o_idx" field, forming a singly linked list, which * in game terms, represents a pile of objects held by the monster. * * The "held_m_idx" field is used to indicate which monster, if any, * is holding the object. Objects being held have "ix=0" and "iy=0". */ typedef struct object_type object_type; struct object_type { s16b k_idx; /* Kind index (zero if "dead") */ s16b ix; /* X-position on map, or zero */ s16b iy; /* Y-position on map, or zero */ s16b weight; /* Item weight */ byte tval; /* Item type (from kind) */ byte sval; /* Item sub-type (from kind) */ s16b pval; /* Item extra-parameter */ byte discount; /* Discount (if any) */ byte number; /* Number of items */ s16b to_h; /* Plusses to hit */ s16b to_d; /* Plusses to damage */ s16b to_a; /* Plusses to AC */ s16b ac; /* Normal AC */ s16b timeout; /* Timeout Counter */ byte dd, ds; /* Damage dice/sides */ s16b next_o_idx; /* Next object in stack (if any) */ s16b inscription; /* Inscription index */ s16b xtra_name; /* Extra Name (Artifacts and ego items) */ u32b flags[4]; /* Flags */ u32b kn_flags[4]; /* Known Flags */ s32b cost; /* Object "base cost" */ s32b temp_cost; /* Cost including shopkeeper effects */ s16b region; /* Region */ byte feeling; /* Game generated inscription number (eg, pseudo-id) */ byte a_idx; /* Artifact type */ byte info; /* Special flags */ s16b trigger[MAX_TRIGGER]; /* Special object scripts */ bool allocated; /* Held in the o_list[] array */ }; /* * Monster information, for a specific monster. * * The "hold_o_idx" field points to the first object of a stack * of objects (if any) being carried by the monster (see above). */ typedef struct monster_type monster_type; struct monster_type { s16b r_idx; /* Monster race index */ s16b csleep; /* Inactive counter */ s16b fx; /* X location on map */ s16b fy; /* Y location on map */ s16b tx; /* Target X location on map */ s16b ty; /* Target Y location on map */ s16b hp; /* Current Hit points */ s16b maxhp; /* Max Hit points */ byte mspeed; /* Monster "speed" */ byte energy; /* Monster "energy" */ byte stunned; /* Monster is stunned */ byte confused; /* Monster is confused */ byte monfear; /* Monster is afraid */ byte invulner; /* Monster is temporarily invulnerable */ s16b hold_o_idx; /* Object being held (if any) */ u32b smart; /* Field for "smart_learn" */ s16b region; /* Region */ byte cdis; /* Current dis from player */ byte mflag; /* Extra monster flags */ bool ml; /* Monster is "visible" */ }; /* * Monster Race blow-method types */ typedef struct rbm_type rbm_type; struct rbm_type { cptr name; cptr action; u16b sound; bool touched; bool cut; bool stun; }; /* * The thaumaturgical list of fields. * * (Equivalent to monster races, or object kinds. * They had to be called something. ;-) ) * * Eventually most of this, and the following struct * will be wrapped inside a python object. Only things * that need to be accessed quickly will be left as is. */ typedef struct field_thaum field_thaum; struct field_thaum { char *name; /* The name of the field */ byte f_attr; /* attribute */ char f_char; /* character */ byte d_attr; /* Default attribute */ char d_char; /* Default char */ byte priority; /* LOS priority higher = more visible */ byte type; /* Type of field */ s16b count_init; /* Counter for timed effects */ s16b action[FIELD_ACTION_MAX]; /* Action scripts */ /* Storage space for the actions to interact with. */ byte data_init[8]; u16b info; /* Information flags */ }; /* * The field structure. * * Fields will be used to create a variety of effects from * the ability to place traps on _all_ terrains (not just * dungeon floor), to the nightmare mode automatic corpse raising. * * The new building / store code will use this structure. * */ typedef struct field_type field_type; struct field_type { byte f_attr; /* attribute */ char f_char; /* character */ s16b t_idx; /* field type index */ s16b fy; /* Y location on map */ s16b fx; /* X location on map */ s16b next_f_idx; /* Pointer to next field in list */ u16b info; /* quick access flags */ /* Storage space for the actions to interact with. */ byte data[8]; s16b counter; /* Counter for timed effects */ s16b region; /* Region */ byte priority; /* LOS priority higher = more visible */ }; /* * An entry for the object/monster allocation functions * * Pass 1 is determined from allocation information * Pass 2 is determined from allocation restriction * Pass 3 is determined from allocation calculation */ typedef struct alloc_entry alloc_entry; struct alloc_entry { s16b index; /* The actual index */ byte level; /* Base dungeon level */ byte prob1; /* Probability, pass 1 */ byte prob2; /* Probability, pass 2 */ byte prob3; /* Probability, pass 3 */ }; /* * Available "options" * * - Normal and Current Value (TRUE or FALSE) * * - Option Page Number (or zero) * * - Textual name (or NULL) * - Textual description */ typedef struct option_type option_type; struct option_type { bool o_val; byte o_page; cptr o_text; cptr o_desc; }; /* * There are three types of quest: * General quests, Dungeon and wilderness quests. * Each has its own type of data it needs to store */ /* * Quest type-specific data. */ /* Bounty */ typedef struct quest_bnt quest_bnt; struct quest_bnt { u16b r_idx; /* Monster */ u16b cur_num; /* Number killed */ u16b max_num; /* Number required */ }; /* Dungeon */ typedef struct quest_dun quest_dun; struct quest_dun { u16b r_idx; /* Monster race */ u16b level; /* Dungeon level */ s16b cur_num; /* Number killed */ s16b max_num; /* Number required */ s16b num_mon; /* Number on the level */ }; /* Wilderness */ typedef struct quest_wld quest_wld; struct quest_wld { u16b refcount; /* Refcount for wilderness code */ u16b place; /* Equivalent "place number" */ u16b data; /* Data so can choose completion */ byte depth; /* Power of monsters */ }; /* Message */ typedef struct quest_msg quest_msg; struct quest_msg { u16b place; /* Town to go to */ u16b shop; /* Person to give it to */ }; /* Find item */ typedef struct quest_fit quest_fit; struct quest_fit { u16b a_idx; /* Artifact index */ u16b place; /* Dungeon it is in */ }; /* Find place */ typedef struct quest_fpl quest_fpl; struct quest_fpl { u16b place; /* Place to find */ }; /* The union holding the quest-specific data */ typedef union quest_data_type quest_data_type; union quest_data_type { quest_bnt bnt; quest_dun dun; quest_wld wld; quest_msg msg; quest_fit fit; quest_fpl fpl; }; /* * Structure for the "quests" */ typedef struct quest_type quest_type; struct quest_type { byte status; /* Is the quest taken, completed, finished? */ byte flags; /* Quest flags */ byte type; /* The quest type (gen/dun/wild) */ byte item; /* Artificial quest item number */ u16b place; /* Town where given */ u16b shop; /* Quest-giver */ u16b reward; /* Reward level */ byte c_type; /* Type of creation trigger */ byte x_type; /* Type of action trigger */ u32b timeout; /* Time limits */ char name[128]; /* Quest name */ quest_data_type data; /* Quest-specific data */ }; typedef struct magic_type magic_type; struct magic_type { byte slevel; /* Required level (to learn) */ byte smana; /* Required mana (to cast) */ }; /* * Information about the player's "magic" * * Note that a player with a "spell_book" of "zero" is illiterate. */ typedef struct player_magic player_magic; struct player_magic { int spell_book; /* Tval of spell books (if any) */ int spell_xtra; /* Something for later */ int spell_stat; /* Stat for spells (if any) */ int spell_type; /* Spell type (mage/priest) */ int spell_first; /* Level of first spell */ int spell_weight; /* Weight that hurts spells */ magic_type info[MAX_REALM][32]; /* The available spells */ }; /* * Player sex info */ typedef struct player_sex player_sex; struct player_sex { cptr title; /* Type of sex */ cptr winner; /* Name of winner */ }; /* * Player racial info */ typedef struct player_race player_race; struct player_race { cptr title; /* Type of race */ s16b r_adj[A_MAX]; /* Racial stat bonuses */ s16b r_dis; /* disarming */ s16b r_dev; /* magic devices */ s16b r_sav; /* saving throw */ s16b r_stl; /* stealth */ s16b r_sns; /* sensing ability */ s16b r_fos; /* search frequency */ s16b r_thn; /* combat (normal) */ s16b r_thb; /* combat (shooting) */ byte r_mhp; /* Race hit-dice modifier */ byte r_exp; /* Race experience factor */ byte b_age; /* base age */ byte m_age; /* mod age */ byte m_b_ht; /* base height (males) */ byte m_m_ht; /* mod height (males) */ byte m_b_wt; /* base weight (males) */ byte m_m_wt; /* mod weight (males) */ byte f_b_ht; /* base height (females) */ byte f_m_ht; /* mod height (females) */ byte f_b_wt; /* base weight (females) */ byte f_m_wt; /* mod weight (females) */ byte infra; /* Infra-vision range */ }; /* * Player class info */ typedef struct player_class player_class; struct player_class { cptr title; /* Type of class */ s16b c_adj[A_MAX]; /* Class stat modifier */ s16b c_dis; /* class disarming */ s16b c_dev; /* class magic devices */ s16b c_sav; /* class saving throws */ s16b c_stl; /* class stealth */ s16b c_sns; /* class sensing ability */ s16b c_fos; /* class searching frequency */ s16b c_thn; /* class to hit (normal) */ s16b c_thb; /* class to hit (bows) */ s16b x_dis; /* extra disarming */ s16b x_dev; /* extra magic devices */ s16b x_sav; /* extra saving throws */ s16b x_stl; /* extra stealth */ s16b x_sns; /* extra sensing ability */ s16b x_fos; /* extra searching frequency */ s16b x_thn; /* extra to hit (normal) */ s16b x_thb; /* extra to hit (bows) */ s16b c_mhp; /* Class hit-dice adjustment */ s16b c_exp; /* Class experience factor */ byte pet_upkeep_div; /* Pet upkeep divider */ bool heavy_sense; }; /* * Data describing the character. * * (Mostly roll-playing information) */ typedef struct player_data player_data; struct player_data { s16b age; /* Characters age */ s16b ht; /* Height */ s16b wt; /* Weight */ s16b sc; /* Social Class */ byte hitdie; /* Hit dice (sides) */ byte psex; /* Sex index */ byte prace; /* Race index */ byte pclass; /* Class index */ }; /* * Player spell data */ typedef struct player_realm player_realm; struct player_realm { u32b learned; /* Spell flags */ u32b worked; /* Spell flags */ u32b forgotten; /* Spell flags */ byte realm; /* Realm number */ }; typedef struct player_spell player_spell; struct player_spell { player_realm r[2]; /* Magic realms */ byte order[PY_MAX_SPELLS]; /* Spell order */ }; /* * Timed effects acting on the player */ typedef struct player_timed player_timed; struct player_timed { s16b fast; /* Timed -- Fast */ s16b slow; /* Timed -- Slow */ s16b blind; /* Timed -- Blindness */ s16b paralyzed; /* Timed -- Paralysis */ s16b confused; /* Timed -- Confusion */ s16b afraid; /* Timed -- Fear */ s16b image; /* Timed -- Hallucination */ s16b poisoned; /* Timed -- Poisoned */ s16b cut; /* Timed -- Cut */ s16b stun; /* Timed -- Stun */ s16b protevil; /* Timed -- Protection */ s16b invuln; /* Timed -- Invulnerable */ s16b hero; /* Timed -- Heroism */ s16b shero; /* Timed -- Super Heroism */ s16b shield; /* Timed -- Shield Spell */ s16b blessed; /* Timed -- Blessed */ s16b invis; /* Timed -- See Invisible */ s16b infra; /* Timed -- Infra Vision */ s16b oppose_acid; /* Timed -- oppose acid */ s16b oppose_elec; /* Timed -- oppose lightning */ s16b oppose_fire; /* Timed -- oppose heat */ s16b oppose_cold; /* Timed -- oppose cold */ s16b oppose_pois; /* Timed -- oppose poison */ s16b esp; /* Timed ESP */ s16b wraith_form; /* Timed wraithform */ s16b resist_magic; /* Timed Resist Magic (later) */ s16b word_recall; /* Word of recall counter */ }; /* * The state of the player */ typedef struct player_state player_state; struct player_state { char died_from[80]; /* Cause of death */ s16b resting; /* Resting counter */ s16b running; /* Running counter */ byte confusing; /* Glowing hands */ byte searching; /* Currently searching */ u16b total_winner; /* Total winner */ u16b panic_save; /* Panic save */ u16b noscore; /* Cheating flags */ bool is_dead; /* Player is dead */ bool wizard; /* Player is in wizard mode */ bool playing; /* True if player is playing */ bool leaving; /* True if player is leaving */ bool create_up_stair; /* Create up stair on next level */ bool create_down_stair; /* Create down stair on next level */ byte feeling; /* Most recent feeling */ s16b energy_use; /* Energy use this turn */ bool cumber_armor; /* Mana draining armor */ bool cumber_glove; /* Mana draining gloves */ bool heavy_wield; /* Heavy weapon */ bool heavy_shoot; /* Heavy shooter */ bool icky_wield; /* Icky weapon */ bool detected; /* Detected for traps? */ bool skip_more; /* Skip the --more-- prompt */ bool mon_fight; /* Monster fighting indicator */ bool monk_armour_stat; /* Status of monk armour */ byte noise_level; /* Amount of noise since last update */ u16b store_top; /* Top of store inventory list */ }; /* * Information about the player state for the running code */ typedef struct player_run player_run; struct player_run { s16b cur_dir; /* Direction we are running */ s16b old_dir; /* Direction we came from */ int mode; /* Current running algorithm */ }; /* * The current state of the command being executed * and the keys being pressed */ typedef struct player_command player_command; struct player_command { s16b cmd; /* Gives identity of current command */ s16b arg; /* Gives argument of current command */ s16b rep; /* Gives repetition of current command */ s16b dir; /* Gives direction of current command */ s16b new; /* Hack -- command chaining XXX XXX */ /* Inkey status */ bool inkey_base; /* See the "inkey()" function */ bool inkey_xtra; /* See the "inkey()" function */ bool inkey_scan; /* See the "inkey()" function */ bool inkey_flag; /* See the "inkey()" function */ }; /* * The player stats */ typedef struct player_stat player_stat; struct player_stat { s16b max; /* Current "maximal" stat values */ s16b cur; /* Current "natural" stat values */ s16b use; /* Current modified stats */ s16b top; /* Maximal modified stats */ s16b add; /* Equipment stat bonuses */ s16b ind; /* Indexes into stat tables */ }; /* * Most of the "player" information goes here. * * This stucture gives us a large collection of player variables. * * This structure contains several "blocks" of information. * (1) the "permanent" info * (2) the "variable" info * (3) the "transient" info * * All of the "permanent" info, and most of the "variable" info, * is saved in the savefile. The "transient" info is recomputed * whenever anything important changes. */ typedef struct player_type player_type; struct player_type { s16b px; /* Player location */ s16b py; /* Player location */ player_data rp; /* Role-play information */ s16b depth; /* Cur depth */ s16b max_lev; /* Max level */ s16b lev; /* Cur level */ u16b exp_frac; /* Cur exp frac (times 2^16) */ s32b max_exp; /* Max experience */ s32b exp; /* Cur experience */ s32b au; /* Current Gold */ s16b place_num; /* Current place number in the wilderness */ s32b wilderness_x; /* Coordinates in the wilderness */ s32b wilderness_y; s16b old_wild_x; /* Previous block coords in the wilderness */ s16b old_wild_y; s16b panel_x1; /* Coordinates of top left hand corner of panel */ s16b panel_y1; s16b panel_x2; /* Coordinates of bottom right hand corner of panel */ s16b panel_y2; s16b mhp; /* Max hit pts */ s16b chp; /* Cur hit pts */ u16b chp_frac; /* Cur hit frac (times 2^16) */ s16b msp; /* Max mana pts */ s16b csp; /* Cur mana pts */ u16b csp_frac; /* Cur mana frac (times 2^16) */ player_spell spell; /* Spell information */ u32b muta1; /* Mutations */ u32b muta2; /* Mutations */ u32b muta3; /* Mutations */ s16b virtues[MAX_PLAYER_VIRTUES]; s16b vir_types[MAX_PLAYER_VIRTUES]; s16b energy; /* Current energy */ s16b food; /* Current nutrition */ s16b player_hp[PY_MAX_LEVEL]; /* HP Array */ u16b expfact; /* Experience factor * Note: was byte, causing overflow for Amberite * characters (such as Amberite Paladins) */ s16b chaos_patron; /* Players Chaos Patron */ player_timed tim; /* Timed effects */ player_state state; /* Internal state of the player */ s16b skills[MAX_SKILL]; /* Player skills */ player_command cmd; /* The current command status */ player_stat stat[A_MAX]; /* The player's stats */ /*** Pointers to player grid information ***/ pcave_type *pcave[MAX_HGT]; pblk_ptr **pwild; /*** Boundary of player-known area ****/ u16b max_hgt; u16b min_hgt; u16b max_wid; u16b min_wid; /*** Temporary fields ***/ s32b align; /* Good/evil/neutral */ s16b total_weight; /* Total weight being carried */ s16b inventory; /* Index to inventory item list */ object_type equipment[EQUIP_MAX]; /* Equipment */ s16b target_set; /* Target flag */ s16b target_who; /* Target identity */ s16b target_row; /* Target location */ s16b target_col; /* Target location */ s16b health_who; /* Health bar trackee */ s16b monster_race_idx; /* Monster race trackee */ u16b max_seen_r_idx; /* Most powerful monster visible */ s16b object_kind_idx; /* Object kind trackee */ player_run run; /* Current stat of the running routine */ s16b new_spells; /* Number of spells available */ s16b cur_lite; /* Radius of lite (if any) */ u32b notice; /* Special Updates (bit flags) */ u32b update; /* Pending Updates (bit flags) */ u32b redraw; /* Normal Redraws (bit flags) */ u32b window; /* Window Redraws (bit flags) */ u32b change; /* Once per turn (bit flags) */ /* * Flags on equipment items and the racial/class * effects logical-ored together. */ u32b flags[4]; /*** Extracted fields ***/ s16b dis_to_h; /* Known bonus to hit */ s16b dis_to_d; /* Known bonus to dam */ s16b dis_to_a; /* Known bonus to ac */ s16b dis_ac; /* Known base ac */ s16b to_h; /* Bonus to hit */ s16b to_d; /* Bonus to dam */ s16b to_a; /* Bonus to ac */ s16b ac; /* Base ac */ s16b see_infra; /* Infravision range */ u32b noise; /* Derived from stealth */ s16b num_blow; /* Number of blows */ s16b num_fire; /* Number of shots */ byte ammo_mult; /* Ammo multiplier */ byte ammo_tval; /* Ammo variety */ byte bow_energy; /* shooter speed */ s16b pspeed; /* Current speed */ s16b sp_bonus; /* Extra mana per level */ /*** Pet commands ***/ s16b pet_follow_distance; /* Length of the imaginary "leash" for pets */ byte pet_open_doors; /* flag - allow pets to open doors */ byte pet_pickup_items; /* flag - allow pets to pickup items */ /* Options */ bool options[OPT_PLAYER]; bool birth[OPT_BIRTH]; }; /* * For multiplayer use. * * Various information must be stored on the server * when a multiplayer version is created. * * This structure contains such information. * At the moment, this only has dungeon-specific options. * (Which are not already "birth" options.) */ typedef struct server_type server_type; struct server_type { bool options[OPT_SERVER]; }; /* For Monk martial arts */ typedef struct martial_arts martial_arts; struct martial_arts { cptr desc; /* A verbose attack description */ int min_level; /* Minimum level to use */ int chance; /* Chance of 'success' */ int dd; /* Damage dice */ int ds; /* Damage sides */ int effect; /* Special effects */ }; /* Mindcrafters */ typedef struct mindcraft_power mindcraft_power; struct mindcraft_power { int min_lev; int mana_cost; int fail; cptr name; }; #if 0 /* * A store owner */ typedef struct owner_type owner_type; struct owner_type { cptr owner_name; /* Name */ s16b max_cost; /* Purse limit / 100 */ byte greed; /* Greed level */ }; /* * A building owner */ typedef struct b_own_type b_own_type; struct b_own_type { cptr owner_name; /* Name */ byte inflate; /* Inflation */ }; #endif /* 0 */ /* * A store, with an owner, various state flags, a current stock * of items, and a table of items that are often purchased. */ typedef struct store_type store_type; struct store_type { byte type; /* Store type */ byte greed; /* Greed value */ s16b max_cost; /* Purse limit / 100 */ s16b owner_name; /* Owner name */ s16b data; /* Data used for various things */ s32b last_visit; /* Last visited on this turn */ s16b stock; /* Stock -- list of items in o_list[] */ u16b x; /* Location x coord. */ u16b y; /* Location y coord. */ byte max_stock; /* Stock -- Max number of entries */ }; /* Dungeons */ typedef struct dun_type dun_type; struct dun_type { obj_theme theme; /* Dungeon object theme */ u32b habitat; /* Flags describing habitat */ byte min_level; /* Minimum level in the dungeon */ byte max_level; /* Maximum dungeon level allowed */ s16b rating; /* Level's current rating */ s16b region; /* Hack - Region for current level */ u16b rooms; /* Room types available */ byte recall_depth; /* Recall depth */ bool good_item_flag; /* True if "Artifact" on this level */ byte floor; /* Floor terrain type */ byte liquid; /* Liquid type for lakes/ rivers etc. */ byte flags; /* Extra flags */ }; /* Type holding dungeon type information */ typedef struct dun_gen_type dun_gen_type; struct dun_gen_type { /* Theme information */ obj_theme theme; u32b habitat; /* Level bounds for fixed dungeons */ int min_level; int max_level; /* Probability (inverse rarity) */ int chance; /* Wilderness location */ byte pop; byte height; /* Room types available */ u16b rooms; /* Floor terrain type */ byte floor; /* Liquid type for lakes/ rivers etc. */ byte liquid; /* Extra flags */ byte flags; }; /* * A structure describing a place with * stores and buildings. */ typedef struct place_type place_type; struct place_type { u32b seed; /* Seed for RNG */ store_type *store; /* The stores[numstores] */ dun_type *dungeon; byte type; /* Type of place */ byte numstores; u16b quest_num; /* Quest number if is special */ byte x; /* Location mod 16 in wilderness */ byte y; byte xsize; /* Size in wilderness */ byte ysize; /* Size in wilderness */ byte data; /* pop for towns, generic for quests */ byte monst_type; /* Type of population (monsters/people etc.) */ s16b region; /* Region */ byte gates_x[MAX_GATES]; /* Position of the town gates */ byte gates_y[MAX_GATES]; char name[T_NAME_LEN]; /* Town name */ }; /* Various function pointer types */ typedef bool (*monster_hook_type) (int r_idx); typedef byte (*object_hook_type) (int k_idx); typedef int (*inven_func) (object_type *); typedef bool (*cave_hook_type) (const cave_type *c_ptr); typedef bool (*object_comp) (const object_type *, const object_type *); /* * Semi-Portable High Score List Entry (128 bytes) -- BEN * * All fields listed below are null terminated ascii strings. * * In addition, the "number" fields are right justified, and * space padded, to the full available length (minus the "null"). * * Note that "string comparisons" are thus valid on "pts". */ typedef struct high_score high_score; struct high_score { char what[8]; /* Version info (string) */ char pts[10]; /* Total Score (number) */ char gold[10]; /* Total Gold (number) */ char turns[10]; /* Turns Taken (number) */ char day[10]; /* Time stamp (string) */ char who[16]; /* Player Name (string) */ char uid[8]; /* Player UID (number) */ char sex[2]; /* Player Sex (string) */ char p_r[3]; /* Player Race (number) */ char p_c[3]; /* Player Class (number) */ char cur_lev[4]; /* Current Player Level (number) */ char cur_dun[4]; /* Current Dungeon Level (number) */ char max_lev[4]; /* Max Player Level (number) */ char max_dun[4]; /* Max Dungeon Level (number) */ char how[32]; /* Method of death (string) */ }; /* * Struct for mutations and racial powers */ typedef struct mutation_type mutation_type; struct mutation_type { u32b which; /* Actual mutation (mask) */ cptr desc_text; /* Text describing mutation */ cptr gain_text; /* Text displayed on gaining the mutation */ cptr lose_text; /* Text displayed on losing the mutation */ char name[39]; /* Short description (activatable mutations) */ byte level; /* Minimum level (activatable mutations) */ int cost; /* Mana/HP Cost (activatable mutations) */ int stat; /* Stat dependency (activatable mutations) */ int diff; /* Difficulty (activatable mutations) */ int chance; /* Chance of occuring (random mutations) / 100 */ }; /* * A function pointer used in displaying menus * * The function takes a number for the option chosen * and will return TRUE if the selection works, and FALSE * if the menu should stay up. */ typedef bool (*menu_select_type) (int option); typedef struct menu_type menu_type; struct menu_type { cptr text; /* Option text */ cptr help; /* Help file to use */ menu_select_type action; /* Action to do */ byte flags; /* Flags controling option behaviour */ }; /* * Object bonuses to various stuff */ typedef struct bonuses_type bonuses_type; struct bonuses_type { int stat[6]; int sp_bonus; int skills[MAX_SKILL]; int see_infra; int pspeed; int extra_blows; int extra_shots; }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/wild.h���������������������������������������������������������������������������������0000644�0000000�0000000�00000011254�10250356275�013247� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: wild.h */ /* Purpose: Wilderness + Quest generation header file */ /* * Copyright (c) 1989, 2003 James E. Wilson, Robert A. Koeneke, * Robert Ruehlmann, Steven Fuerst * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ /* Include low-level grid stuff */ #include "grid.h" /* 1/4 of the wilderness is sea */ #define SEA_FRACTION 4 /* Number of lakes to try and make */ #define LAKE_NUM 4 /* Constant^2 that determines number of rivers */ #define RIVER_NUM 4 /* Maximum distance a road can connect */ #define ROAD_DIST 30 /* Minimum fractional distance a road can approach a non-connecting town */ #define ROAD_MIN 3 /* Minimum separation between towns */ #define MIN_DIST_TOWN 10 /* Minimum separation between quests */ #define MIN_DIST_QUEST 10 /* Minimum separation between dungeons */ #define MIN_DIST_DUNGEON 8 /* Number of wilderness places */ #define NUM_TOWNS 20 #define NUM_DUNGEON 20 /* Dodgy replacement for SCREEN_WID and SCREEN_HGT */ /* This will be removed later. */ #define TOWN_WID 66 #define TOWN_HGT 22 #define V_TOWN_BLOCK_WID \ (((TOWN_WID / WILD_BLOCK_SIZE) + 1) * WILD_BLOCK_SIZE) #define V_TOWN_BLOCK_HGT \ (((TOWN_HGT / WILD_BLOCK_SIZE) + 1) * WILD_BLOCK_SIZE) /* Starting town has pre-defined stores */ #define START_STORE_NUM 7 /* Town monster types */ #define TOWN_MONST_VILLAGER 1 #define TOWN_MONST_ELVES 2 #define TOWN_MONST_DWARF 3 #define TOWN_MONST_LIZARD 4 #define TOWN_MONST_MONST 5 #define TOWN_MONST_ABANDONED 6 /* Road constants used to define with of the path */ #define ROAD_LEVEL (WILD_BLOCK_SIZE * 150) #define TRACK_LEVEL (WILD_BLOCK_SIZE * 140) #define ROAD_BORDER (WILD_BLOCK_SIZE * 120) #define GROUND_LEVEL (WILD_BLOCK_SIZE * 100) /* Decision tree constants */ /* Lower two bits describe cut */ #define DT_HGT 0x01 #define DT_POP 0x02 #define DT_LAW 0x03 /* These two bits describe the direction to branch */ #define DT_LEFT 0x04 #define DT_RIGHT 0x08 /* Town size cutoffs for names */ #define T_SIZE_SMALL (128 + 30) #define T_SIZE_TOWN (128 + 50) #define T_SIZE_CITY (128 + 70) #define T_SIZE_CASTLE (128 + 100) /* Town building constants */ #define CITY_OUTSIDE 0 #define CITY_WALL 1 #define CITY_INSIDE 2 /* Quest status */ #define QUEST_STATUS_UNTAKEN 0 #define QUEST_STATUS_TAKEN 1 #define QUEST_STATUS_COMPLETED 2 #define QUEST_STATUS_FINISHED 3 /* Quest creation flags */ #define Q_GEN_PICKY 0x01 #define Q_GEN_OCEAN 0x02 /* Quest flags */ #define QUEST_FLAG_ACTIVE 0x01 /* Quest triggers have effect */ #define QUEST_FLAG_TIME 0x02 /* Quest has timeout */ #define QUEST_FLAG_ITEM 0x04 /* Player has art. quest item */ #define QUEST_FLAG_DUMMY 0x08 #define QUEST_FLAG_KNOWN 0x10 /* Player knows about this quest */ /* Helper defines for random quests */ #define QUEST_CAMP_MON 5 /* One in five squares has a monster */ #define QUEST_CAMP_OBJ 5 /* One in five squares has an object */ #define QUEST_CAMP_SCATTER 10 /* Non-camp sqaures have stuff */ /* Dungeon flags */ #define DF_NONE 0x00 #define DF_ROAD 0x01 #define DF_TRACK 0x02 /* Building types */ #define BT_GENERAL 0 #define BT_STORE 1 #define BT_BUILD 2 /* Some useful macros */ #define build_is_store(X) \ (wild_build[X].type == BT_STORE) #define build_is_general(X) \ (wild_build[X].type == BT_GENERAL) #define build_is_build(X) \ (wild_build[X].type == BT_BUILD) /* Wilderness building info type */ typedef struct wild_building_type wild_building_type; struct wild_building_type { u16b gen; /* Created */ u16b field; /* Field type, if applicable */ byte type; /* Type of building */ /* Suggested location in parameter space */ byte pop; byte magic; byte law; u16b rarity; /* Rarity of store */ }; /* Quest generation helper */ typedef struct quest_aux_type quest_aux_type; struct quest_aux_type { bool (*hook_func) (int r_idx); int level; int chance; cptr name; }; /* wild1.c */ extern wild_building_type wild_build[MAX_CITY_BUILD]; extern bool init_places(int xx, int yy); extern void clear_temp_block(void); extern void set_temp_corner_val(u16b val); extern void set_temp_mid(u16b val); extern void frac_block(void); /* wild2.c */ extern void draw_city(place_type *pl_ptr); extern void draw_dungeon(place_type *pl_ptr); extern void van_town_gen(place_type *pl_ptr); extern void init_vanilla_town(void); /* quest.c */ extern void pick_wild_quest(int *xsize, int *ysize, byte *flags); extern bool quest_blank(int x, int y, int xsize, int ysize, int place_num, byte flags); extern bool create_quest(int x, int y, int place_num); extern void draw_quest(place_type *pl_ptr); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/z-config.h�����������������������������������������������������������������������������0000644�0000000�0000000�00000023264�10250356275�014030� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: z-config.h */ /* Purpose: Angband specific configuration stuff */ /* * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ /* Allow debug commands */ #define USE_DEBUG /* Allow various special stuff (sound, graphics, etc.) */ #define USE_SPECIAL /* * Look through the following lines, and where a comment includes the * tag "OPTION:", examine the associated "#define" statements, and decide * whether you wish to keep, comment, or uncomment them. You should not * have to modify any lines not indicated by "OPTION". * * Note: Also examine the "system" configuration file "h-config.h". * * And finally, remember that the "Makefile" will specify some rather * important compile time options, like what visual module to use. */ /* * OPTION: See the Makefile(s), where several options may be declared. * * Some popular options include "USE_GCU" (allow use with Unix "curses"), * "USE_X11" (allow basic use with Unix X11), "USE_XAW" (allow use with * Unix X11 plus the Athena Widget set), and "USE_CAP" (allow use with * the "termcap" library, or with hard-coded vt100 terminals). * * The old "USE_NCU" option has been replaced with "USE_GCU". * * Several other such options are available for non-unix machines, * such as "MACINTOSH", "WINDOWS", "USE_IBM", "USE_EMX". * * You may also need to specify the "system", using defines such as * "SOLARIS" (for Solaris), etc, see "h-config.h" for more info. */ /* * OPTION: Use the POSIX "termios" methods in "main-gcu.c" */ /* #define USE_TPOSIX */ /* * OPTION: Use the "termio" methods in "main-gcu.c" */ /* #define USE_TERMIO */ /* * OPTION: Use the icky BSD "tchars" methods in "main-gcu.c" */ /* #define USE_TCHARS */ /* * OPTION: Use "blocking getch() calls" in "main-gcu.c". * Hack -- Note that this option will NOT work on many BSD machines * Currently used whenever available, if you get a warning about * "nodelay()" undefined, then make sure to undefine this. */ #if defined(SYS_V) || defined(AMIGA) # define USE_GETCH #endif /* * OPTION: Use the "curs_set()" call in "main-gcu.c". * Hack -- This option will not work on most BSD machines */ #ifdef SYS_V # define USE_CURS_SET #endif /* * OPTION: Include "ncurses.h" instead of "curses.h" in "main-gcu.c" */ /* #define USE_NCURSES */ /* * OPTION: for multi-user machines running the game setuid to some other * user (like 'games') this SAFE_SETUID option allows the program to drop * its privileges when saving files that allow for user specified pathnames. * This lets the game be installed system wide without major security * concerns. There should not be any side effects on any machines. * * This will handle "gids" correctly once the permissions are set right. */ #define SAFE_SETUID /* * This flag enables the "POSIX" methods for "SAFE_SETUID". */ #ifdef _POSIX_SAVED_IDS # define SAFE_SETUID_POSIX #endif /* * Prevent problems on (non-Solaris) Suns using "SAFE_SETUID". * The SAFE_SETUID code is weird, use it at your own risk... */ #if defined(SUNOS) && !defined(SOLARIS) # undef SAFE_SETUID_POSIX #endif /* Debug mode options */ #ifdef USE_DEBUG /* * OPTION: Hack -- Compile in support for "Wizard Commands" */ #define ALLOW_WIZARD /* * OPTION: Hack -- Compile in support for "Spoiler Generation" */ #define ALLOW_SPOILERS #endif /* USE_DEBUG */ /* * OPTION: Hack -- Compile in support for "Borg mode" */ #define ALLOW_BORG /* * OPTION: Allow "do_cmd_colors" at run-time */ #define ALLOW_COLORS /* * OPTION: Allow "do_cmd_visuals" at run-time */ #define ALLOW_VISUALS /* * OPTION: Allow "do_cmd_macros" at run-time */ #define ALLOW_MACROS /* * OPTION: Allow characteres to be "auto-rolled" */ #define ALLOW_AUTOROLLER /* * OPTION: Allow parsing of the ascii template files in "init.c". * This must be defined if you do not have valid binary image files. * It should be usually be defined anyway to allow easy "updating". */ #define ALLOW_TEMPLATES /* * OPTION: Delay the loading of the "f_text" array until it is actually * needed, saving ~1K, since "feature" descriptions are unused. */ #define DELAY_LOAD_F_TEXT /* * OPTION: Delay the loading of the "k_text" array until it is actually * needed, saving ~1K, since "object" descriptions are unused. */ #define DELAY_LOAD_K_TEXT /* * OPTION: Delay the loading of the "a_text" array until it is actually * needed, saving ~1K, since "artifact" descriptions are unused. */ #define DELAY_LOAD_A_TEXT /* * OPTION: Delay the loading of the "e_text" array until it is actually * needed, saving ~1K, since "ego-item" descriptions are unused. */ #define DELAY_LOAD_E_TEXT /* * OPTION: Delay the loading of the "r_text" array until it is actually * needed, saving ~60K, but "simplifying" the "monster" descriptions. */ /* #define DELAY_LOAD_R_TEXT */ /* * OPTION: Delay the loading of the "v_text" array until it is actually * needed, saving ~1K, but "destroying" the "vault" generation. */ /* #define DELAY_LOAD_V_TEXT */ /* * OPTION: Handle signals */ #define HANDLE_SIGNALS /* * OPTION: Allow "Wizards" to yield "high scores" */ /* #define SCORE_WIZARDS */ /* * OPTION: Allow "Borgs" to yield "high scores" */ /* #define SCORE_BORGS */ /* * OPTION: Allow "Cheaters" to yield "high scores" */ /* #define SCORE_CHEATERS */ /* * OPTION: Gamma correct colours (with X11 / windows) */ #define SUPPORT_GAMMA /* * OPTION: Check the modification time of *_info.raw files */ #define CHECK_MODIFICATION_TIME #ifdef USE_SPECIAL /* * OPTION: Allow the use of "sound" in various places. */ #define USE_SOUND /* * OPTION: Allow the use of "graphics" in various places */ #define USE_GRAPHICS /* * OPTION: Allow the use of "music" in various places */ /* #define USE_MUSIC */ #endif /* USE_SPECIAL */ /* * Hack -- Macintosh stuff */ #ifdef MACINTOSH /* Do not handle signals */ # undef HANDLE_SIGNALS #endif /* * Hack -- Windows stuff */ #ifdef WINDOWS /* Do not handle signals */ # undef HANDLE_SIGNALS #endif /* * Hack -- EMX stuff */ #ifdef USE_EMX /* Do not handle signals */ # undef HANDLE_SIGNALS #endif /* * OPTION: Set the "default" path to the angband "lib" directory. * * See "main.c" for usage, and note that this value is only used on * certain machines, primarily Unix machines. * * The configure script overrides this value. Check the "--prefix=<dir>" * option of the configure script. * * This value will be over-ridden by the "ANGBAND_PATH" environment * variable, if that variable is defined and accessable. The final * "slash" is required if the value supplied is in fact a directory. * * Using the value "./lib/" below tells Angband that, by default, * the user will run "angband" from the same directory that contains * the "lib" directory. This is a reasonable (but imperfect) default. * * If at all possible, you should change this value to refer to the * actual location of the "lib" folder, for example, "/tmp/angband/lib/" * or "/usr/games/lib/angband/", or "/pkg/angband/lib". */ #ifndef DEFAULT_PATH # define DEFAULT_PATH "./lib/" #endif /* DEFAULT_PATH */ /* * OPTION: For some brain-dead computers with no command line interface, * namely Macintosh, there has to be some way of "naming" your savefiles. * The current "Macintosh" hack is to make it so whenever the character * name changes, the savefile is renamed accordingly. But on normal * machines, once you manage to "load" a savefile, it stays that way. * Macintosh is particularly weird because you can load savefiles that * are not contained in the "lib:save:" folder, and if you change the * player's name, it will then save the savefile elsewhere. */ #if defined(MACINTOSH) || defined(WINDOWS) || defined(AMIGA) # define SAVEFILE_MUTABLE #endif /* * OPTION: Prevent usage of the "ANGBAND_PATH" environment variable and * the '-d<what>=<path>' command line option (except for '-du=<path>'). * * This prevents cheating in multi-user installs as well as possible * security problems when running setgid. */ #ifdef SET_UID #define FIXED_PATHS #endif /* SET_UID */ /* * OPTION: Capitalize the "user_name" (for "default" player name) * This option is only relevant on SET_UID machines. */ #define CAPITALIZE_USER_NAME /* * OPTION: Person to bother if something goes wrong. */ #define MAINTAINER "sfuerst@zangband.org" /* * OPTION: Default font (when using X11). */ #define DEFAULT_X11_FONT "fixed" /* * OPTION: Default fonts (when using X11) */ #define DEFAULT_X11_FONT_0 "10x20" #define DEFAULT_X11_FONT_1 "9x15" #define DEFAULT_X11_FONT_2 "9x15" #define DEFAULT_X11_FONT_3 "5x8" #define DEFAULT_X11_FONT_4 "5x8" #define DEFAULT_X11_FONT_5 "5x8" #define DEFAULT_X11_FONT_6 "5x8" #define DEFAULT_X11_FONT_7 "5x8" /* * OPTION: Attempt to prevent all "cheating" */ /* #define VERIFY_HONOR */ /* Do we want different characters for different races? */ /* * Too slow for general use - note that the 16x16 tiles use a * much faster version. */ /* # define VARIABLE_PLAYER_GRAPH */ /* For longer martial arts descriptions */ # define VERBOSE_MARTIAL_ARTS /* Allow hordes of 'similar' monsters */ # define MONSTER_HORDES /* Allow Klackon- and Sprite-Monks to get extra speed * * undefined by default because * Klackons and Sprites are not *supposed* to be * playing monks in the first place */ /* #define MONK_HACK */ /* * Add pillar tunnels (Annoying) */ /* #define PILLAR_TUNNELS */ /* * Optional use of 64bit type */ /* #define USE_64B */ /* * Allow execution of arbitrary lua scripts using * the '@' debug command. (Insecure) */ /* #define DEBUG_SCRIPTS */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/z-form.h�������������������������������������������������������������������������������0000755�0000000�0000000�00000003421�10250356275�013522� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File z-form.h */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ #ifndef INCLUDED_Z_FORM_H #define INCLUDED_Z_FORM_H #include "h-basic.h" /* * This file provides functions very similar to "sprintf()", but which * not only parse some additional "format sequences", but also enforce * bounds checking, and allow repeated "appends" to the same buffer. * * See "z-form.c" for more detailed information about the routines, * including a list of the legal "format sequences". * * This file makes use of both "z-util.c" and "z-virt.c" */ /* * The "type" of the "user defined print routine" function pointers */ typedef void (*vstrnfmt_aux_func) (char *buf, uint max, cptr fmt, va_list *vp); /**** Available Functions ****/ /* Register table of user format functions */ extern void register_format_funcs(vstrnfmt_aux_func *table); /* Format arguments into given bounded-length buffer */ extern uint vstrnfmt(char *buf, uint max, cptr fmt, va_list *vp); /* Simple interface to "vstrnfmt()" */ extern uint strnfmt(char *buf, uint max, cptr fmt, ...); /* Append a formatted string to another string */ extern void strnfcat(char *str, int max, int *end, cptr fmt, ...); /* Free the memory allocated for the format buffer */ extern void vformat_kill(void); /* Simple interface to "vformat()" */ extern char *format(cptr fmt, ...); /* Vararg interface to "plog()", using "format()" */ extern void plog_fmt(cptr fmt, ...); /* Vararg interface to "quit()", using "format()" */ extern void quit_fmt(cptr fmt, ...); /* Vararg interface to "core()", using "format()" */ extern void core_fmt(cptr fmt, ...); #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/z-rand.h�������������������������������������������������������������������������������0000755�0000000�0000000�00000004707�10250356275�013513� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: z-rand.h */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ #ifndef INCLUDED_Z_RAND_H #define INCLUDED_Z_RAND_H #include "h-basic.h" /**** Available constants ****/ /* * The "degree" of the "complex" Random Number Generator. * This value is hard-coded at 63 for a wide variety of reasons. */ #define RAND_DEG 63 /**** Available macros ****/ /* * Generates a random long integer X where O<=X<M. * The integer X falls along a uniform distribution. * For example, if M is 100, you get "percentile dice" */ #define rand_int(M) \ ((s32b)(Rand_div(M))) /* * Generates a random long integer X where 1<=X<=M. * * Note that the behaviour for M < 1 is undefined. */ #define randint(M) \ (rand_int(M) + 1) /* * Generates a random long integer X where O<=X<M. * The integer X falls along a uniform distribution. * For example, if M is 100, you get "percentile dice" * * The same as rand_int(). */ #define randint0(M) \ ((s32b)Rand_div(M)) /* * Generates a random long integer X where 1<=X<=M. * * Also, "correctly" handle the case of M<=1 * * The same as randint(). */ #define randint1(M) \ (randint0(M) + 1) /* * Generates a random long integer X where A<=X<=B * The integer X falls along a uniform distribution. * Note: rand_range(0,N-1) == randint0(N) */ #define rand_range(A,B) \ ((A) + (randint0(1+(B)-(A)))) /* * Generate a random long integer X where A-D<=X<=A+D * The integer X falls along a uniform distribution. * Note: rand_spread(A,D) == rand_range(A-D,A+D) */ #define rand_spread(A,D) \ ((A) + (randint0(1+(D)+(D))) - (D)) #define one_in_(X) \ (randint0(X) == 0) /* * Evaluate to TRUE "S" percent of the time */ #define saving_throw(S) \ (randint0(100) < (S)) /**** Available Variables ****/ extern bool Rand_quick; extern u32b Rand_value; extern u16b Rand_place; extern u32b Rand_state[RAND_DEG]; extern byte quick_rand_place; /**** Available Functions ****/ extern void Rand_state_init(u32b seed); extern s32b Rand_div(u32b m); extern s16b Rand_normal(int mean, int stand); extern u32b Rand_simple(u32b m); extern s16b damroll(int num, int sides); extern s16b maxroll(int num, int sides); extern bool quick_rand(void); extern void quick_rand_add(void); #endif /* INCLUDED_Z_RAND_H */ ���������������������������������������������������������zangband/src/z-term.h�������������������������������������������������������������������������������0000755�0000000�0000000�00000015513�10250356275�013533� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: z-term.h */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ #ifndef INCLUDED_Z_TERM_H #define INCLUDED_Z_TERM_H #include "h-basic.h" /* * A term_win is a "window" for a Term * * - Cursor Useless/Visible codes * - Cursor Location (see "Useless") * * - Array[h] -- Access to the attribute array * - Array[h] -- Access to the character array * * - Array[h*w] -- Attribute array * - Array[h*w] -- Character array * * - Pointer to the next window in the stack. * * Note that the attr/char pair at (x,y) is a[y][x]/c[y][x] * and that the row of attr/chars at (0,y) is a[y]/c[y] */ typedef struct term_win term_win; struct term_win { bool cu, cv; byte cx, cy; byte **a; char **c; byte *va; char *vc; byte **ta; char **tc; byte *vta; char *vtc; /* Bigtile data */ bool wipe_bigtile; int big_x1; int big_y1; int big_y2; term_win *next; }; /* * An actual "term" structure * * - Extra "user" info (used by application) * * - Extra "data" info (used by implementation) * * * - Flag "user_flag" * An extra "user" flag (used by application) * * * - Flag "data_flag" * An extra "data" flag (used by implementation) * * * - Flag "active_flag" * This "term" is "active" * * - Flag "mapped_flag" * This "term" is "mapped" * * - Flag "total_erase" * This "term" should be fully erased * * - Flag "fixed_shape" * This "term" is not allowed to resize * * - Flag "icky_corner" * This "term" has an "icky" corner grid * * - Flag "soft_cursor" * This "term" uses a "software" cursor * * - Flag "always_pict" * Use the "Term_pict()" routine for all text * * - Flag "higher_pict" * Use the "Term_pict()" routine for special text * * - Flag "always_text" * Use the "Term_text()" routine for invisible text * * - Flag "unused_flag" * Reserved for future use * * - Flag "never_bored" * Never call the "TERM_XTRA_BORED" action * * - Flag "never_frosh" * Never call the "TERM_XTRA_FROSH" action * * * - Value "attr_blank" * Use this "attr" value for "blank" grids * * - Value "char_blank" * Use this "char" value for "blank" grids * * * - Ignore this pointer * * - Keypress Queue -- various data * * - Keypress Queue -- pending keys * * * - Window Width (max 255) * - Window Height (max 255) * * - Minimum modified row * - Maximum modified row * * - Minimum modified column (per row) * - Maximum modified column (per row) * * * - Displayed screen image * - Requested screen image * * * - Hook for init-ing the term * - Hook for nuke-ing the term * * - Hook for user actions * * - Hook for extra actions * * - Hook for placing the cursor * * - Hook for drawing some blank spaces * * - Hook for drawing a string of chars using an attr * * - Hook for drawing a sequence of special attr/char pairs */ typedef struct term term; struct term { vptr user; vptr data; bool user_flag; bool data_flag; bool active_flag; bool mapped_flag; bool total_erase; bool fixed_shape; bool icky_corner; bool soft_cursor; bool always_pict; bool higher_pict; bool always_text; bool unused_flag; bool never_bored; bool never_frosh; byte attr_blank; char char_blank; char *key_queue; u16b key_head; u16b key_tail; u16b key_xtra; u16b key_size; byte wid; byte hgt; byte y1; byte y2; byte *x1; byte *x2; term_win *old; term_win *scr; void (*init_hook) (term *t); void (*nuke_hook) (term *t); errr (*user_hook) (int n); errr (*xtra_hook) (int n, int v); errr (*curs_hook) (int x, int y); errr (*wipe_hook) (int x, int y, int n); errr (*text_hook) (int x, int y, int n, byte a, cptr s); void (*resize_hook) (void); errr (*pict_hook) (int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp); }; /**** Available Constants ****/ /* * Definitions for the "actions" of "Term_xtra()" * * These values may be used as the first parameter of "Term_xtra()", * with the second parameter depending on the "action" itself. Many * of the actions shown below are optional on at least one platform. * * The "TERM_XTRA_EVENT" action uses "v" to "wait" for an event * The "TERM_XTRA_SHAPE" action uses "v" to "show" the cursor * The "TERM_XTRA_FROSH" action uses "v" for the index of the row * The "TERM_XTRA_SOUND" action uses "v" for the index of a sound * The "TERM_XTRA_ALIVE" action uses "v" to "activate" (or "close") * The "TERM_XTRA_LEVEL" action uses "v" to "resume" (or "suspend") * The "TERM_XTRA_DELAY" action uses "v" as a "millisecond" value * * The other actions do not need a "v" code, so "zero" is used. */ #define TERM_XTRA_EVENT 1 /* Process some pending events */ #define TERM_XTRA_FLUSH 2 /* Flush all pending events */ #define TERM_XTRA_SHAPE 4 /* Set cursor shape (optional) */ #define TERM_XTRA_FROSH 5 /* Flush one row (optional) */ #define TERM_XTRA_FRESH 6 /* Flush all rows (optional) */ #define TERM_XTRA_NOISE 7 /* Make a noise (optional) */ #define TERM_XTRA_SOUND 8 /* Make a sound (optional) */ #define TERM_XTRA_BORED 9 /* Handle stuff when bored (optional) */ #define TERM_XTRA_REACT 10 /* React to global changes (optional) */ #define TERM_XTRA_ALIVE 11 /* Change the "hard" level (optional) */ #define TERM_XTRA_LEVEL 12 /* Change the "soft" level (optional) */ #define TERM_XTRA_DELAY 13 /* Delay some milliseconds (optional) */ /**** Available Variables ****/ extern term *Term; /**** Available Functions ****/ extern errr Term_user(int n); extern void Term_xtra(int n, int v); extern void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc); extern void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc); extern void Term_fresh(void); extern errr Term_set_cursor(int v); extern void Term_gotoxy(int x, int y); extern void Term_draw(int x, int y, byte a, char c); extern void Term_addch(byte a, char c); extern void Term_putch(int x, int y, byte a, char c); extern void Term_erase(int x, int y, int n); extern void Term_clear(void); extern void Term_redraw(void); extern errr Term_redraw_section(int x1, int y1, int x2, int y2); extern errr Term_get_cursor(int *v); extern void Term_get_size(int *w, int *h); extern errr Term_locate(int *x, int *y); extern errr Term_what(int x, int y, byte *a, char *c); extern void Term_flush(void); extern errr Term_keypress(int k); extern errr Term_key_push(int k); extern errr Term_inkey(char *ch, bool wait, bool take); extern void Term_save(void); extern void Term_load(void); extern errr Term_resize(int w, int h); extern void Term_activate(term *t); extern errr term_nuke(term *t); extern errr term_init(term *t, int w, int h, int k); extern errr Term_bigregion(int x1, int y1, int y2); #endif /* INCLUDED_Z_TERM_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/z-util.h�������������������������������������������������������������������������������0000755�0000000�0000000�00000002436�10250356275�013541� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File z-util.h */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ #ifndef INCLUDED_Z_UTIL_H #define INCLUDED_Z_UTIL_H #include "h-basic.h" /* * Extremely basic stuff, like global temp and constant variables. * Also, some very useful low level functions, such as "streq()". * All variables and functions in this file are "addressable". */ /**** Available variables ****/ /* A cptr to the name of the program */ extern cptr argv0; /* Aux functions */ extern void (*plog_aux) (cptr); extern void (*quit_aux) (cptr); extern void (*core_aux) (cptr); /**** Available Functions ****/ /* Copy a string */ extern size_t my_strcpy(char *buf, const char *src, size_t bufsize); /* Concatenate two strings */ extern size_t my_strcat(char *buf, const char *src, size_t bufsize); /* Test equality, prefix, suffix */ extern bool streq(cptr s, cptr t); extern bool prefix(cptr s, cptr t); extern bool suffix(cptr s, cptr t); /* Print an error message */ extern void plog(cptr str); /* Exit, with optional message */ extern void quit(cptr str); /* Dump core, with optional message */ extern void core(cptr str); #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/z-virt.h�������������������������������������������������������������������������������0000755�0000000�0000000�00000010210�10250356275�013535� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: z-virt.h */ /* * Copyright (c) 1997 Ben Harrison * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ #ifndef INCLUDED_Z_VIRT_H #define INCLUDED_Z_VIRT_H #include "h-basic.h" /* * Memory management routines. * * Set ralloc_aux to modify the memory allocation routine. * Set rnfree_aux to modify the memory de-allocation routine. * Set rpanic_aux to let the program react to memory failures. * * These routines work best as a *replacement* for malloc/free. * * The string_make() and string_free() routines handle dynamic strings. * A dynamic string is a string allocated at run-time, which should not * be modified once it has been created. * * Note the macros below which simplify the details of allocation, * deallocation, setting, clearing, casting, size extraction, etc. * * The macros MAKE/C_MAKE and KILL have a "procedural" metaphor, * and they actually modify their arguments. * * Note that, for some reason, some allocation macros may disallow * "stars" in type names, but you can use typedefs to circumvent * this. For example, instead of "type **p; MAKE(p,type*);" you * can use "typedef type *type_ptr; type_ptr *p; MAKE(p,type_ptr)". * * Note that it is assumed that "memset()" will function correctly, * in particular, that it returns its first argument. */ /**** Available macros ****/ /* Size of 'N' things of type 'T' */ #define C_SIZE(N,T) \ ((huge)((N)*(sizeof(T)))) /* Size of one thing of type 'T' */ #define SIZE(T) \ ((huge)(sizeof(T))) /* Compare two arrays of type T[N], at locations P1 and P2 */ #define C_DIFF(P1,P2,N,T) \ (memcmp((const char*)(P1),(const char*)(P2),C_SIZE(N,T))) /* Compare two things of type T, at locations P1 and P2 */ #define DIFF(P1,P2,T) \ (memcmp((const char*)(P1),(const char*)(P2),SIZE(T))) /* Set every byte in an array of type T[N], at location P, to V, and return P */ #define C_BSET(P,V,N,T) \ (T*)(memset((char*)(P),(V),C_SIZE(N,T))) /* Set every byte in a thing of type T, at location P, to V, and return P */ #define BSET(P,V,T) \ (T*)(memset((char*)(P),(V),SIZE(T))) /* Wipe an array of type T[N], at location P, and return P */ #define C_WIPE(P,N,T) \ (T*)(memset((char*)(P),0,C_SIZE(N,T))) /* Wipe a thing of type T, at location P, and return P */ #define WIPE(P,T) \ (T*)(memset((char*)(P),0,SIZE(T))) /* Load an array of type T[N], at location P1, from another, at location P2 */ #define C_COPY(P1,P2,N,T) \ (T*)(memcpy((char*)(P1),(const char*)(P2),C_SIZE(N,T))) /* Load a thing of type T, at location P1, from another, at location P2 */ #define COPY(P1,P2,T) \ (T*)(memcpy((char*)(P1),(const char*)(P2),SIZE(T))) /* Allocate, and return, an array of type T[N] */ #define C_RNEW(N,T) \ ((T*)(ralloc(C_SIZE(N,T)))) /* Allocate, and return, a thing of type T */ #define RNEW(T) \ ((T*)(ralloc(SIZE(T)))) /* Allocate, wipe, and return an array of type T[N] */ #define C_ZNEW(N,T) \ ((T*)(C_WIPE(C_RNEW(N,T),N,T))) /* Allocate, wipe, and return a thing of type T */ #define ZNEW(T) \ ((T*)(WIPE(RNEW(T),T))) /* Allocate a wiped array of type T[N], assign to pointer P */ #define C_MAKE(P,N,T) \ ((P)=C_ZNEW(N,T)) /* Allocate a wiped thing of type T, assign to pointer P */ #define MAKE(P,T) \ ((P)=ZNEW(T)) /* Free one thing at P, return NULL */ #define FREE(P) \ (rnfree(P)) /* Free a thing at location P and set P to NULL */ #define KILL(P) \ ((P)=FREE(P)) /**** Available variables ****/ /* Replacement hook for "rnfree()" */ extern vptr (*rnfree_aux) (vptr); /* Replacement hook for "rpanic()" */ extern vptr (*rpanic_aux) (huge); /* Replacement hook for "ralloc()" */ extern vptr (*ralloc_aux) (huge); /**** Available functions ****/ /* De-allocate memory */ extern vptr rnfree(vptr p); /* Panic, attempt to Allocate 'len' bytes */ extern vptr rpanic(huge len); /* Allocate (and return) 'len', or dump core */ extern vptr ralloc(huge len); /* Create a "dynamic string" */ extern cptr string_make(cptr str); /* Free a string allocated with "string_make()" */ extern errr string_free(cptr str); #endif /* INCLUDED_Z_VIRT_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zbmagic.h������������������������������������������������������������������������������0000644�0000000�0000000�00000001150�10250356275�013716� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* zbmagic1.c */ extern bool borg_desperate; extern int borg_goto_dir(int x1, int y1, int x2, int y2); extern bool borg_eat_food_any(void); extern bool borg_surrounded(void); extern int borg_freedom(int x, int y); extern bool borg_happy_grid_bold(int x, int y); extern void borg_target(int x, int y); extern void borg_near_monster_type(int dist); extern void borg_press_faint_accept(void); extern bool borg_escape(int b_q); extern bool borg_heal(int danger); /* zbmagic2.c */ extern bool borg_simulate; /* Simulation flag */ extern int borg_launch_beam(int dam, int typ, int max); extern void borg_temp_fill(void); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg1.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000043045�10250356275�013517� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� /* File: zborg1.h */ /* Purpose: Header file for "borg1.c" -BEN- */ #ifndef INCLUDED_BORG1_H #define INCLUDED_BORG1_H #include "angband.h" #include "maid-grf.h" #ifdef ALLOW_BORG /* * This file provides support for "borg1.c". */ /*** Some constants ***/ /* * Possible values of "goal" */ #define GOAL_NONE 0 /* No goal */ #define GOAL_KILL 1 /* Monsters */ #define GOAL_TAKE 2 /* Objects */ #define GOAL_FLEE 3 /* Fleeing */ #define GOAL_SHOP 4 /* Stores */ #define GOAL_DARK 5 /* Exploring */ #define GOAL_XTRA 6 /* Searching */ #define GOAL_BORE 7 /* Leaving */ #define GOAL_TOWN 8 /* Town Special Grid */ #define GOAL_FEAT 9 /* Getting of painful feat */ #define GOAL_CAVE 10 /* Reach a dungeon */ #define GOAL_MAX 11 /* * Flags for the "info" field of grids * * "BORG_MAP_VIEW" This is the equivalent of GRID_VIEW, * calculated with the best information available to the * borg. * * "BORG_MAP_ICKY" These are grids not to flow over. * * "BORG_MAP_KNOW" This marks grids already in the flow, * and already tested for 'ickyness'. This is done for * efficiency. */ #define BORG_MAP_VIEW 0x01 /* in line of sight */ #define BORG_MAP_ICKY 0x02 /* grids to avoid */ #define BORG_MAP_KNOW 0x04 /* 'know' grids */ /* Flags used to mark detection */ #define BORG_DETECT_TRAP 0x01 #define BORG_DETECT_DOOR 0x02 #define BORG_DETECT_WALL 0x04 #define BORG_DETECT_EVIL 0x08 /* * Borg detection radius. * * This is smaller than the actual detection radius because * we don't want the borg to walk into undetected regions. */ #define BORG_MAX_DETECT (MAX_DETECT - 2) /* * Some assistance with the borg_attack and magic arrows */ #define GF_ARROW_FLAME 93 #define GF_ARROW_FROST 94 #define GF_ARROW_SHOCKING 95 #define GF_ARROW_ANIMAL 96 #define GF_ARROW_DRAGON 97 #define GF_ARROW_EVIL 98 #define GF_ARROW_EXPLOSION 99 #define GF_HOLY_WORD 100 #define GF_DISP_UNDEAD_DEMON 101 /* effect both */ #define GF_ELEMENTS 102 /* all elements could be cast */ #define GF_DEATHRAY 103 /* values for the successful_target global */ #define BORG_TARGET -1 #define BORG_FRESH_TARGET 0 #define BORG_ARROW_TARGET 5 /* * Maximum size of the "view" array */ #define AUTO_VIEW_MAX 1536 /* * Number of grids in the "temp" array */ #define BORG_TEMP_MAX 1536 /* * Number of grids in the "borg_next" array */ #define BORG_NEXT_MAX 8 /* * Number of grids in the "flow" array */ #define BORG_FLOW_MAX 2000 /* * Size of Keypress buffer */ #define KEY_SIZE 8192 /* Maximum number of 'takes' */ #define BORG_TAKES_MAX 1024 #define BORG_KILLS_MAX 1024 /* * Object information */ typedef struct borg_take borg_take; struct borg_take { s16b k_idx; /* Kind index */ /* Location */ s16b x; s16b y; char unknown; /* Unknown type */ }; /* * Monster information */ typedef struct borg_kill borg_kill; struct borg_kill { s16b r_idx; /* Race index */ s16b power; /* Estimated hit-points */ s32b when; /* When last seen */ /* Location */ s16b x; s16b y; bool ranged_attack; /* can attack from a dx */ byte m_flags; byte type; /* Type of kill */ }; /* * A store */ typedef struct borg_shop borg_shop; struct borg_shop { /* Location */ s16b x; s16b y; /* Town */ int town_num; /* Time stamp */ s32b when; bool visit; /* Is this shop useful? */ char type; s16b b_count; s16b u_count; }; /* * A dungeon */ typedef struct borg_dungeon borg_dungeon; struct borg_dungeon { /* Location */ s16b x; s16b y; /* depth */ s16b min_depth; s16b max_depth; bool bottom; }; /* * A town */ typedef struct borg_town borg_town; struct borg_town { /* Location */ s16b x; s16b y; /* name */ char name[T_NAME_LEN]; /* Was the borg here? */ bool visit; }; /* Max size for the wilderness */ #define BORG_MAX_WILD_SIZE (max_wild * WILD_BLOCK_SIZE) /* Maximal distance the borg can travel between dungeons */ #define BORG_MAX_DISTANCE (BORG_MAX_WILD_SIZE * 3 / 2) /* Small distance in the wilderness (when the borg is close enough) */ #define BORG_SMALL_DISTANCE 96 /* * Some variables */ extern bool borg_active; /* Actually active */ extern bool borg_cancel; /* Being cancelled */ extern bool borg_dont_react; extern int successful_target; /* * Borg-abilities */ typedef struct borg_ability borg_ability; struct borg_ability { s16b phase; s16b teleport; s16b teleport_level; s16b teleport_away; s16b escape; s16b fuel; s16b heal; s16b easy_heal; s16b id; s16b id_item; s16b star_id; s16b star_id_item; s16b berserk; s16b speed; s16b staff_magi; s16b staff_dest; s16b staff_cool; s16b missile; s16b cure_pois; s16b cure_blind; s16b cure_conf; s16b det_trap; s16b det_door; s16b det_evil; s16b magic_map; s16b lite; s16b recharge; s16b remove_curse; s16b remove_curse_item; s16b star_remove_curse; s16b star_remove_curse_item; s16b glyph; s16b ccw; s16b csw; s16b clw; s16b res_heat; s16b res_cold; s16b res_all; s16b death; s16b poison; s16b mana; s16b logrus; s16b genocide; s16b mass_genocide; s16b invulnerability; s16b artifact; s16b artify_item; s16b acquire; s16b mundane; s16b bolt; s16b ball; }; /* * Borg status */ typedef struct borg_status borg_status; struct borg_status { /* Food status */ bool weak; bool hungry; bool full; bool gorged; /* Various status */ bool blind; bool afraid; bool confused; bool poisoned; bool cut; bool image; bool study; bool search; /* Stun */ bool stun; bool heavy_stun; /* Draining */ bool fixlvl; bool fixexp; bool fixstat[A_MAX]; /* Fix stats */ /* Heavy stuff */ bool hvy_weapon; /* Cursedness */ bool cursed; bool heavy_curse; }; /* * Borg-player information */ typedef struct borg_player borg_player; struct borg_player { /* Abilities */ borg_ability able; /* Status */ borg_status status; /* Sustains */ bool sust[A_MAX]; bool intmana; bool wismana; bool britelite; /* Lite does not require fuel */ byte cur_lite; /* Current light radius */ bool winner; /* Have we killed the Serpent? */ bool hour; /* Time of day */ /* Hitpoints */ int chp; int mhp; int oldhp; /* Spellpoints */ int csp; int msp; s16b speed; /* Current speed */ byte realm1; /* First magic realm */ byte realm2; /* Second magic realm */ s16b lev; /* Cur level */ s16b max_lev; /* Max level */ s16b depth; /* Cur depth */ s16b max_depth; /* Max depth */ /* Combined object flags */ u32b flags[4]; /* Mutation flags */ u32b muta1; u32b muta2; u32b muta3; s16b food; /* Power of food */ s16b recall; /* Power of recall */ /* Combat stats */ s16b ac; s16b to_h; s16b to_d; s16b w_to_d; s16b b_to_d; s16b b_max_dam; s16b blows; s16b mana_bonus; u32b value; /* Cost of items we are carrying */ s16b weight; /* Weight of items we are carrying */ s16b encumber; /* Weight of encumberance */ s16b see_infra; /* Infravision range */ s16b skill_dis; /* Skill: Disarming */ s16b skill_dev; /* Skill: Magic Devices */ s16b skill_sav; /* Skill: Saving throw */ s16b skill_stl; /* Skill: Stealth factor */ s16b skill_sns; /* Skill: Sensing ability */ s16b skill_fos; /* Skill: Searching frequency */ s16b skill_thn; /* Skill: To hit (normal) */ s16b skill_thb; /* Skill: To hit (shooting) */ s16b skill_tht; /* Skill: To hit (throwing) */ s16b skill_dig; /* Skill: Digging */ }; extern borg_player *bp_ptr; /* * Various silly flags */ extern bool borg_stop_king; /* The borg stops when he wins */ extern bool borg_cheat_death; /* Is there life after death? */ extern bool borg_flag_dump; /* Save savefile at each death */ extern bool borg_flag_save; /* Save savefile at each level */ extern bool borg_save; /* do a save next time we get to press a key! */ /* * Use a simple internal random number generator */ extern u32b borg_rand_local; /* Save personal setting */ /* * Hack -- time variables */ extern s32b borg_t; /* Current "time" */ extern s32b borg_temp_fill_valid; /* When were the monster arrays filled */ extern s32b need_see_inviso; /* To tell me to cast it */ extern s32b borg_see_inv; extern bool vault_on_level; /* borg will search for a vault */ extern bool unique_on_level; extern int unique_r_idx; extern bool scaryguy_on_level; extern bool breeder_level; /* Borg will shut doors */ extern s16b old_depth; extern s16b borg_no_retreat; /* * Hack -- Other time variables */ extern s32b when_call_lite; /* When we last did call light */ extern s32b when_wizard_lite; /* When we last did wizard light */ extern s32b when_detect_traps; /* When we last detected traps */ extern s32b when_detect_doors; /* When we last detected doors */ extern s32b when_detect_walls; /* When we last detected walls */ extern s32b when_detect_evil; extern bool my_need_alter; /* incase of walls/doors */ extern bool my_no_alter; /* incase of walls/doors */ /* * Some information */ extern s16b goal; /* Flowing (goal type) */ extern bool goal_rising; /* Currently returning to town */ extern bool goal_leaving; /* Currently leaving the level */ extern bool goal_fleeing; /* Currently fleeing the level */ extern bool goal_ignoring; /* Currently ignoring monsters */ extern int goal_recalling; /* Currently waiting for recall, guessing turns left */ extern bool goal_less; /* return to, but dont use, the next up stairs */ extern s16b borg_times_twitch; /* how often twitchy on this level */ extern s16b borg_escapes; /* how often teleported on this level */ extern bool stair_less; /* Use the next "up" staircase */ extern bool stair_more; /* Use the next "down" staircase */ extern s32b borg_began; /* When this level began */ extern s32b borg_time_town; /* how long it has been since I was in town */ extern s16b avoidance; /* Current danger thresh-hold */ extern bool borg_failure; /* Notice failure */ extern bool borg_attacking; /* Are we attacking a monster? */ extern bool borg_offsetting; /* Are we attacking a monster? with offsett balls */ extern bool borg_completed; /* Completed the level */ extern bool borg_needs_searching; /* borg will search with each step */ extern bool borg_full_damage; /* make danger = full possible damage. */ /* defence flags */ extern bool borg_prot_from_evil; extern bool borg_speed; extern bool borg_bless; extern bool borg_hero; extern bool borg_berserk; extern bool my_oppose_fire; extern bool my_oppose_cold; extern bool my_oppose_acid; extern bool my_oppose_pois; extern bool my_oppose_elec; extern s16b borg_goi; extern s16b borg_wraith_form; extern s16b borg_inviso; extern bool borg_esp; extern s16b borg_game_ratio; extern bool borg_shield; extern bool borg_on_glyph; /* borg is standing on a glyph of warding */ extern bool borg_create_door; /* borg is going to create doors */ extern bool borg_open_door_failed; extern bool borg_close_door_failed; extern bool borg_sleep_spell; extern bool borg_sleep_spell_ii; extern bool borg_slow_spell; extern bool borg_confuse_spell; extern bool borg_fear_mon_spell; /* Which shop or dungeon to visit next */ extern s16b goal_town; extern s16b goal_shop; extern s16b goal_dungeon; extern s16b goal_explore_x; extern s16b goal_explore_y; /* Current shop/dungeon index */ extern s16b town_num; extern s16b shop_num; extern s16b dungeon_num; /* List of known shops and dungeons */ extern borg_town *borg_towns; extern borg_shop *borg_shops; extern borg_dungeon *borg_dungeons; /* Number of allocated towns */ extern s16b borg_town_num; extern s16b borg_town_size; /* Number of allocated stores */ extern s16b borg_shop_num; extern s16b borg_shop_size; /* Number of allocated dungeons */ extern s16b borg_dungeon_num; extern s16b borg_dungeon_size; /* * Other variables */ extern int c_x; /* Current location (X) */ extern int c_y; /* Current location (Y) */ extern int g_x; /* Goal location (X) */ extern int g_y; /* Goal location (Y) */ extern s32b g_power; /* Current power value */ extern s32b g_power_home; /* Current power_home value */ extern int dim_door_y; /* Safe landing zone for DDoor */ extern int dim_door_x; extern int bad_obj_x[50]; /* Dropped cursed artifact at location (X) */ extern int bad_obj_y[50]; /* Dropped cursed artifact at location (Y) */ extern int bad_obj_n; /* * Some estimated state variables */ extern s16b my_stat_max[6]; /* Current "maximal" stat values */ extern s16b my_stat_cur[6]; /* Current "natural" stat values */ extern s16b my_stat_ind[6]; /* Current "additions" to stat values */ extern bool my_need_stat_check[6]; /* do I need to check my stats */ extern s16b my_stat_add[6]; /* aditions to stats */ extern s16b home_stat_add[6]; extern s16b weapon_swap_digger; extern int my_ammo_tval; /* Ammo -- "tval" */ extern s16b my_ammo_power; /* Average power */ extern s16b my_ammo_range; /* Shooting range */ /* * Various "amounts" (for the player) */ extern s16b amt_food_scroll; extern s16b amt_food_lowcal; extern s16b amt_torch; extern s16b amt_lantern; extern s16b amt_flask; extern s16b amt_slow_poison; extern s16b amt_pot_curing; extern s16b amt_star_heal; extern s16b amt_life; extern s16b amt_rod_heal; extern s16b amt_book[8][4]; /* [realm][sval] */ extern s16b amt_add_stat[6]; extern s16b amt_fix_stat[7]; extern s16b amt_fix_exp; extern s16b amt_enchant_to_a; extern s16b amt_enchant_to_d; extern s16b amt_enchant_to_h; extern s16b amt_brand_weapon; /* cubragol and bolts */ extern s16b amt_digger; /* * Hack -- extra state variables */ extern int borg_feeling; /* Current level "feeling" */ /* * State variables extracted from the screen */ extern s32b borg_gold; /* Current gold */ extern int borg_stat[6]; /* Current stats */ extern int borg_book[8][4]; /* Current book slots, Realm,sval */ /* * Constant state variables */ extern int borg_race; /* Current race */ extern int borg_class; /* Current class */ /* * Constant state structures */ extern player_race *rb_ptr; /* Player race info */ extern player_class *cb_ptr; /* Player class info */ extern player_magic *pmb_ptr; /* Player magic info */ /* * Number of turns to step for (zero means forever) */ extern u16b borg_step; /* Step count (if any) */ /* * Status message search string */ extern char borg_match[128]; /* Search string */ /* * Log file */ extern FILE *borg_fff; /* Log file */ /* * Track "stairs up" */ extern s16b track_less_num; extern s16b track_less_size; extern int *track_less_x; extern int *track_less_y; /* * Track "stairs down" */ extern s16b track_more_num; extern s16b track_more_size; extern int *track_more_x; extern int *track_more_y; /* * Track glyphs */ extern s16b track_glyph_num; extern s16b track_glyph_size; extern int *track_glyph_x; extern int *track_glyph_y; /* * Track steps */ extern s16b track_step_num; extern s16b track_step_size; extern int *track_step_x; extern int *track_step_y; /* * Track closed doors */ extern s16b track_door_num; extern s16b track_door_size; extern int *track_door_x; extern int *track_door_y; /* * The object list. This list is used to "track" objects. */ extern s16b borg_takes_cnt; extern s16b borg_takes_nxt; extern borg_take *borg_takes; /* * The monster list. This list is used to "track" monsters. */ extern s16b borg_kills_cnt; extern s16b borg_kills_nxt; extern borg_kill *borg_kills; /* * Maintain a set of grids (viewable grids) */ extern s16b borg_view_n; extern s16b *borg_view_y; extern s16b *borg_view_x; /* * Maintain a set of grids (scanning arrays) */ /* For any monster within MAX_RANGE */ extern s16b borg_temp_n; extern s16b *borg_temp_y; extern s16b *borg_temp_x; /* For the monsters immediately surrounding the borg */ extern s16b borg_next_n; extern s16b *borg_next_y; extern s16b *borg_next_x; /* For the monsters that can be hit by a bolt */ extern s16b borg_bolt_n; extern s16b *borg_bolt_y; extern s16b *borg_bolt_x; /* For the monsters that can be hit by a beam, basically any monster in LOS */ extern s16b borg_beam_n; extern s16b *borg_beam_y; extern s16b *borg_beam_x; /* For the monsters that can be hit by a ball with radius > 1 */ extern s16b borg_ball_n; extern s16b *borg_ball_y; extern s16b *borg_ball_x; /* * Maintain a set of grids (flow calculations) */ extern s16b borg_flow_n; extern s16b *borg_flow_y; extern s16b *borg_flow_x; /* * Hack -- use "flow" array as a queue */ extern int flow_head; extern int flow_tail; /* * Strategy flags -- examine the world */ extern bool borg_do_frame; /* Acquire "frame" info */ extern bool borg_do_spell; /* Acquire "spell" info */ /* * Strategy flags -- run certain functions */ extern bool borg_do_destroy; /* am I fighting a unique */ extern int borg_fighting_unique; extern bool borg_fighting_evil_unique; #define BORG_QUESTOR 100 /*** Some functions ***/ /* * Queue a keypress */ extern errr borg_keypress(char k); /* * Queue several keypresses */ extern errr borg_keypresses(cptr str); /* * Dequeue a keypress */ extern char borg_inkey(bool take); /* * Flush the keypresses */ extern void borg_flush(void); /* * Obtain some text from the screen (single character) */ extern errr borg_what_char(int x, int y, byte *a, char *c); /* * Obtain some text from the screen (multiple characters) */ extern errr borg_what_text(int x, int y, int n, byte *a, char *s); extern bool borg_term_text_comp(int x, int y, cptr what); /* * Log a message, Search it, and Show/Memorize it in pieces */ extern void borg_note(cptr fmt, ...); /* * Abort the Borg, noting the reason */ extern void borg_oops(cptr fmt, ...); /* * Take a "memory note" */ extern bool borg_tell(cptr what); /* * Change the player name */ extern bool borg_change_name(cptr str); /* * Dump a character description */ extern bool borg_dump_character(cptr str); /* * Save the game (but do not quit) */ extern bool borg_save_game(void); /* * Initialize this file */ extern void borg_init_1(void); #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg2.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000004334�10250356275�013516� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg2.h */ /* Purpose: Header file for "borg2.c" -BEN- */ #ifndef INCLUDED_BORG2_H #define INCLUDED_BORG2_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "borg2.c". */ #include "zborg1.h" /* * Determine if a "legal" grid is a "floor" grid */ #define borg_cave_floor_grid(C) \ (!(f_info[(C)->feat].flags & FF_BLOCK)) /* * Determine if a "legal" grid is a "wall" grid */ #define borg_cave_wall_grid(C) \ (f_info[(C)->feat].flags & FF_BLOCK) /* * True half the time for trees. (Block line of sight half the time.) */ #define borg_cave_half_grid(C) \ ((f_info[(C)->feat].flags & FF_HALF_LOS) && (quick_rand())) /* * Grid will block LOS. */ #define borg_cave_los_grid(C) \ ((borg_cave_floor_grid(C)) || (borg_cave_half_grid(C))) /* * Bold version of borg_cave_floor_grid * (with bounds checking) */ #define borg_cave_floor_bold(Y, X) \ (map_in_bounds((X),(Y)) && \ borg_cave_floor_grid(map_loc((X),(Y)))) /* Types of monster list */ #define BORG_MON_USED 1 #define BORG_MON_NEW 2 #define BORG_MON_OLD 3 #define BORG_MON_MOVE 4 /* Useful typedef for los_general() */ typedef bool (*map_hook_type) (map_block *mb_ptr); /* Recalculate danger */ extern bool borg_danger_wipe; extern void borg_mmove_init(int x1, int y1, int x2, int y2); extern void borg_mmove(int *x, int *y, int x1, int y1); extern bool borg_los(int x1, int y1, int x2, int y2); extern bool borg_los_pure(int x1, int y1, int x2, int y2); extern bool borg_bolt_los(int x1, int y1, int x2, int y2); extern bool borg_bolt_los_pure(int x1, int y1, int x2, int y2); extern bool borg_projectable(int x1, int y1, int x2, int y2); extern void borg_forget_view(void); extern void borg_update_view(void); extern void borg_delete_kill(int i, cptr reason); extern void borg_add_dungeon(int x, int y, int min_depth, int max_depth, bool bottom); extern int borg_add_town(int x, int y, cptr town_name); extern void borg_map_info(map_block *mb_ptr, const term_map *map, vptr dummy); extern void borg_map_erase(vptr dummy); extern void borg_update(void); extern void borg_react(cptr msg, cptr buf); extern void borg_player_move(int x, int y, vptr dummy); /* * Initialize this file */ extern void borg_init_2(void); #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg3.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000021156�10250356275�013520� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg3.h */ /* Purpose: Header file for "zborg3.c" -BEN- */ #ifndef INCLUDED_BORG3_H #define INCLUDED_BORG3_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "zborg3.c". */ #include "zborg1.h" /* * Incorrect test to see if an item is identified. * Despite test three it failed for mindcrafters. * I am keeping this as a memory aid. * * Determine if a given inventory item is "known" * Test One -- Check for special "known" tag * Test Two -- Check for "Easy Know" + "Aware" * Test Three -- Check for Mind Blast attack, which loses 'awareness'. #define borg_obj_known_p(T) \ ((((T)->info & (OB_KNOWN)) || \ ((T)->k_idx && k_info[(T)->k_idx].easy_know)) && \ !((T)->k_idx && k_info[(T)->k_idx].flavor && !k_info[(T)->k_idx].aware)) */ /* * New version of the known test. * An object is known if has a non-zero k_idx and one of these is true: * (1) The object is easy_known and the borg is aware of it. * (2) The OB_KNOWN is set. */ #define borg_obj_known_p(T) \ ((T)->k_idx && \ ((k_info[(T)->k_idx].easy_know && k_info[(T)->k_idx].aware) || \ (T)->info & (OB_KNOWN))) /* * Is the object fully known? */ #define borg_obj_known_full(T) \ ((T)->info & (OB_MENTAL)) /* * Is the object an ego item or artifact? */ #define borg_obj_is_ego_art(T) \ ((T)->xtra_name && (*(T)->xtra_name)) /* Macro to determine if the borg can use a certain realm */ #define borg_has_realm(realm) ((bp_ptr->realm1 == (realm)) ? TRUE : \ (bp_ptr->realm2 == (realm)) ? TRUE : FALSE) /* * Spell method values */ #define BORG_MAGIC_ICK 0 /* Spell is illegible */ #define BORG_MAGIC_NOP 1 /* Spell takes no arguments */ #define BORG_MAGIC_EXT 2 /* Spell needs 'space' after cast */ #define BORG_MAGIC_AIM 3 /* Spell requires a direction */ #define BORG_MAGIC_OBJ 4 /* Spell requires a pack object */ #define BORG_MAGIC_WHO 5 /* Spell requires a monster symbol */ /* * Spell status values */ #define BORG_MAGIC_ICKY 0 /* Spell is illegible */ #define BORG_MAGIC_LOST 1 /* Spell is forgotten */ #define BORG_MAGIC_HIGH 2 /* Spell is high level */ #define BORG_MAGIC_OKAY 3 /* Spell is learnable */ #define BORG_MAGIC_TEST 4 /* Spell is untried */ #define BORG_MAGIC_KNOW 5 /* Spell is known */ /* * Define some MindCraft Spells */ #define MIND_NEURAL_BL 0 #define MIND_PRECOGNIT 1 #define MIND_MINOR_DISP 2 #define MIND_MAJOR_DISP 3 #define MIND_DOMINATION 4 #define MIND_PULVERISE 5 #define MIND_CHAR_ARMOUR 6 #define MIND_PSYCHOMETRY 7 #define MIND_MIND_WAVE 8 #define MIND_ADRENALINE 9 #define MIND_PSYCHIC_DR 10 #define MIND_TELE_WAVE 11 /* * Hack the second racial power */ #define RACE_AMBERITE_POWER2 (MAX_RACES + 1) #define RACE_GHOUL_POWER2 (MAX_RACES + 2) /* * Forward declare */ typedef struct borg_magic borg_magic; /* * A spell/prayer in a book */ struct borg_magic { cptr name; /* Textual name */ cptr realm_name; /* Text name of realm */ byte realm; /* number Realm, see defines.h */ byte status; /* Status (see above) */ byte method; /* Method (see above) */ byte rating; /* Usefulness */ byte level; /* Required level */ byte power; /* Required power */ byte cheat; /* Actual "spell index" (or 99) */ s32b times; /* Times this spell was cast */ }; /* * A spell/prayer in a book */ typedef struct borg_mind borg_mind; struct borg_mind { cptr name; /* Textual name */ byte level; /* Required level */ byte power; /* Required power --mana cost */ byte sfail; /* Minimum chance of failure */ char letter; /* Actual "spell index" (a,b,c...) */ s32b times; /* Times this spell was cast */ }; /* * Spell casting information */ extern borg_magic borg_magics[8][4][8]; /* Spell info, including realm */ extern borg_mind borg_minds[MINDCRAFT_MAX]; /* Functions */ extern int borg_wield_slot(list_item *item); extern int borg_count(int tval, int sval); extern list_item *borg_slot(int tval, int sval); extern int borg_slot_from(int tval, int sval, int from); extern object_kind *borg_get_kind(int tval, int sval); extern int look_up_index(list_item *l_ptr); extern bool borg_obj_star_id_able(list_item *l_ptr); extern long borg_calc_pseudo(void); extern bool borg_worthless_item(list_item *l_ptr); extern bool borg_refuel(void); extern bool borg_eat_food(int sval); extern bool borg_quaff_crit(bool no_check); extern bool borg_quaff_potion(int sval); extern bool borg_eat_unknown(void); extern bool borg_use_unknown(void); extern bool borg_quaff_unknown(void); extern bool borg_read_unknown(void); extern bool borg_read_scroll_fail(int sval); extern bool borg_read_scroll(int sval); extern bool borg_use_item_fail(list_item *l_ptr, bool risky); extern bool borg_equips_rod_fail(int sval); extern bool borg_equips_rod(int sval); extern bool borg_zap_rod(int sval); extern bool borg_aim_wand(int sval); extern bool borg_equips_wand_fail(int sval); extern bool borg_equips_wand(int sval); extern bool borg_use_staff(int sval); extern bool borg_use_staff_fail(int sval); extern bool borg_equips_staff_fail(int sval); extern bool borg_equips_staff(int sval); extern bool borg_activate(int act_index); extern bool borg_activate_fail(int act_index); extern bool borg_activate_artifact(int name1, bool secondary); /* apw */ extern bool borg_activate_rand_art(int effect); extern bool borg_check_artifact(list_item *l_ptr, bool real_use); extern int borg_reserve_mana(void); extern byte borg_spell_mana(int realm, int book, int spell); extern bool borg_spell_legal(int realm, int book, int what); extern bool borg_spell_okay(int realm, int book, int what); extern bool borg_spell_okay_no_reserve(int realm, int book, int what); extern int borg_spell_fail_rate(int realm, int book, int what); extern bool borg_spell(int realm, int book, int what); extern bool borg_spell_no_reserve(int realm, int book, int what); extern bool borg_spell_fail(int realm, int book, int what, int allow_fail); extern bool borg_spell_okay_fail(int realm, int book, int what, int allow_fail); extern bool borg_spell_legal_fail(int realm, int book, int what, int allow_fail); extern bool borg_uses_book(int realm, int book); extern bool borg_mindcr_legal(int spell, int level); extern bool borg_mindcr_okay(int spell, int level); extern bool borg_mindcr_okay_no_reserve(int spell, int level); extern int borg_mindcr_fail_rate(int spell, int level); extern bool borg_mindcr(int spell, int level); extern bool borg_mindcr_no_reserve(int spell, int level); extern bool borg_mindcr_fail(int spell, int level, int allow_fail); extern bool borg_mindcr_okay_fail(int spell, int level, int allow_fail); extern bool borg_mindcr_legal_fail(int spell, int level, int allow_fail); extern bool borg_racial_check(int race, bool check_fail); extern bool borg_racial(int race); extern int borg_count_racial(int race); extern bool borg_mutation_check(u32b mutation, bool check_fail); extern bool borg_mutation(u32b mutation); extern void borg_cheat_spell(int realm); extern void prepare_race_class_info(void); extern void borg_dungeon_remember(bool down_stairs); /* Big list of artifact activations with some use */ enum { BORG_ACT_NONE, BORG_ACT_LIGHT, BORG_ACT_LIGHT2, BORG_ACT_LIGHT3, BORG_ACT_LIGHT4, BORG_ACT_MAGIC_MAPPING, BORG_ACT_CLAIRVOYANCE, BORG_ACT_WORD_OF_RECALL, BORG_ACT_RECALL2, BORG_ACT_PROT_EVIL, BORG_ACT_SPEED, BORG_ACT_SPEED2, BORG_ACT_HEAL_BIG, BORG_ACT_HEAL_BIG2, BORG_ACT_HEAL_BIG3, BORG_ACT_HEAL_BIG4, BORG_ACT_GENOCIDE, BORG_ACT_DISARM, BORG_ACT_DETECTION, BORG_ACT_CREATE_FOOD, BORG_ACT_RESISTANCE, BORG_ACT_RESISTANCE2, BORG_ACT_RECHARGE, /* Both recharge and recharging */ BORG_ACT_TELEPORT, BORG_ACT_TELEPORT2, BORG_ACT_RESTORE_LIFE, BORG_ACT_RESTORE_LIFE2, BORG_ACT_REMOVE_FEAR, BORG_ACT_CURE_POISON, BORG_ACT_PHASE_DOOR, BORG_ACT_PHASE_DOOR2, BORG_ACT_MASS_GENOCIDE, BORG_ACT_HEAL_SERIOUS, BORG_ACT_HEAL_SERIOUS2, BORG_ACT_TELEPORT_AWAY, BORG_ACT_IDENTIFY, BORG_ACT_STAR_IDENTIFY, BORG_ACT_STAR_IDENTIFY2, BORG_ACT_HEAL_LIGHT, BORG_ACT_DIM_DOOR, BORG_ACT_ALCHEMY, BORG_ACT_SATISFY, BORG_ACT_RESTORATION, BORG_ACT_TELEPATHY, BORG_ACT_HEROISM, BORG_ACT_BERSERKER, BORG_ACT_BLESS, BORG_ACT_RESIST_ACID, BORG_ACT_RESIST_FIRE, BORG_ACT_RESIST_COLD, BORG_ACT_RESIST_ELEC, BORG_ACT_RESIST_POISON, BORG_ACT_WRAITH_FORM, BORG_ACT_INVULNERABILITY, BORG_ACT_DETECT_EVIL, BORG_ACT_DETECT_MONSTERS, BORG_ACT_DETECT_TRAP_DOOR, BORG_ACT_REMOVE_CURSE, BORG_ACT_STAR_REMOVE_CURSE, BORG_ACT_DETECT_OBJECTS, BORG_ACT_SELF_KNOWLEDGE, BORG_ACT_TELEPORT_LEVEL, BORG_ACT_CREATE_DOORS, BORG_ACT_CREATE_STAIRS, BORG_ACT_ALTER_REALITY, BORG_ACT_STONE_TO_MUD, BORG_ACT_BRAND, BORG_ACT_MAX }; /* * Initialize this file */ extern void borg_init_3(void); #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg4.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000003454�10250356275�013522� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� /* File: zborg4.h */ /* Purpose: Header file for "borg4.c" -BEN- */ #ifndef INCLUDED_BORG4_H #define INCLUDED_BORG4_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "borg4.c". */ #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" /* Object list interface */ extern void borg_list_info(byte list_type, vptr dummy); /* Treat items differently depending on flags */ #define TREAT_AS_NORM 0 /* Normal item */ #define TREAT_AS_GONE 1 /* Pretend item doesn't exist */ #define TREAT_AS_LESS 2 /* Pretend one less item */ #define TREAT_AS_MORE 3 /* Pretend one more item */ #define TREAT_AS_SWAP 4 /* Pretend other item is here */ #define TREAT_AS_SHOP 5 /* Some trickery needed for shops */ /* The current home */ extern int home_shop; extern list_item *borg_home; /* Current home items - (only remember one.) */ extern int home_num; /* Number of items in the home */ /* Use current shop in power calculation */ extern int use_shop; /* Borg functions */ extern list_item *look_up_equip_slot(int slot); extern bool borg_test_bad_curse(list_item *l_ptr); extern void borg_update_frame(void); extern void borg_notice(void); extern void borg_notice_home(void); extern s32b borg_power_home(void); extern int borg_notice_enchant_ac(void); extern int borg_notice_enchant_hit(bool *inven); extern int borg_notice_enchant_dam(bool *inven); extern int borg_notice_create_artifact(bool *b_inven); /* * Macro for the borg_power functions: This way the borg can count how far (a) * is in a range from (b) to (c) * if c <= b return 0 is b to c a range? * if a <= b return 0 * if a <= c return a - b * if a > c return c - b */ #define MIN_FLOOR(a,b,c) (((b) < (c)) ? (MIN(MAX((a), (b)), (c)) - (b)) : 0) /* * Initialize this file */ extern void borg_init_4(void); #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg5.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000001210�10250356275�013507� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg5.h */ /* Purpose: Header file for "borg5.c" -BEN- */ #ifndef INCLUDED_BORG5_H #define INCLUDED_BORG5_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "borg5.c". */ #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg4.h" extern int borg_danger_aux(int x, int y, int c, int i, bool average); extern int borg_danger(int x, int y, int c, bool average); extern s32b borg_power(void); extern cptr borg_restock(int depth); extern cptr borg_prepared(int depth); extern int borg_prepared_depth(void); /* * Initialize this file */ extern void borg_init_5(void); #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg6.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000005135�10250356275�013522� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg6.h */ /* Purpose: Header file for "zborg6.c" */ #ifndef INCLUDED_BORG6_H #define INCLUDED_BORG6_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "zborg6.c". */ #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" /* * Determine "twice" the distance between two points * This results in "diagonals" being "correctly" ranged, * that is, a diagonal appears "furthur" than an adjacent. */ #define double_distance(Y1,X1,Y2,X2) \ (distance(((int)(Y1))<<1,((int)(X1))<<1,((int)(Y2))<<1,((int)(X2))<<1)) /* * Attempt to induce "word of recall" */ extern bool borg_recall(void); /* * Low level goals */ extern bool borg_caution(void); extern bool borg_attack(bool boosted_bravery); extern bool borg_flow_non_hurt(void); extern bool borg_recover(void); extern bool borg_offset_ball(void); extern bool borg_defend(int p); extern bool borg_perma_spell(void); extern bool borg_eat_cure_poison(void); extern bool borg_check_rest(void); extern bool borg_on_safe_feat(byte feat); /* * Twitchy goals */ extern bool borg_charge_kill(void); extern bool borg_charge_take(void); extern bool borg_twitchy(void); /* * Continue a high level goal */ extern bool borg_flow_old(int why); /* * Flow to stairs */ extern int borg_extract_dir(int x1, int y1, int x2, int y2); extern bool borg_flow_stair_both(int why); extern bool borg_flow_stair_less(int why); extern bool borg_flow_stair_more(int why); extern bool borg_flow_glyph(int why); extern bool borg_flow_light(int why); extern bool borg_check_lite_only(void); /* * Flow to shops */ extern bool borg_flow_shop_entry(int n); extern void borg_flow_direct(int y, int x); /* * Flow towards monsters/objects */ extern bool borg_flow_kill(bool viewable, int nearness); extern bool borg_flow_kill_aim(bool viewable); extern bool borg_flow_kill_corridor(bool viewable); extern bool borg_flow_take(bool viewable, int nearness); /* Flow in the wilderness */ extern bool borg_choose_shop(void); extern bool borg_find_shop(void); extern bool borg_find_town(void); extern bool borg_find_dungeon(void); extern bool borg_find_home(void); extern bool borg_waits_daylight(void); extern bool borg_flow_dark_wild(void); extern void borg_flow_goal_wild(void); extern void borg_leave_surface(void); /* Flow towards unexplored grids */ extern bool borg_flow_dark(bool neer); /* * Search for secret doors */ extern bool borg_flow_spastic(bool bored); extern bool borg_lite_beam(bool simulation, int *dir); extern bool borg_caution_phase(int emergency, int turns); /* * Initialize this file */ extern void borg_init_6(void); #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg7.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000001470�10250356275�013521� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg7.h */ /* Purpose: Header file for "zborg7.c" -BEN- */ #ifndef INCLUDED_BORG7_H #define INCLUDED_BORG7_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "zborg7.c". */ #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" /* * Various functions */ extern bool borg_use_things(void); extern bool borg_check_lite(void); extern bool borg_enchanting(void); extern bool borg_recharging(void); extern bool borg_destroy(void); extern bool borg_id_meta(void); extern bool borg_wear_stuff(void); extern bool borg_unwear_stuff(void); extern bool borg_play_magic(bool bored); extern bool borg_wait_recharge(void); /* * Attempt to leave the level */ extern bool borg_leave_level(bool bored); /* * Initialize this file */ extern void borg_init_7(void); #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg8.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000001121�10250356275�013513� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg8.h */ /* Purpose: Header file for "borg8.c" -BEN- */ #ifndef INCLUDED_BORG8_H #define INCLUDED_BORG8_H #include "angband.h" #ifdef ALLOW_BORG /* * This file provides support for "borg8.c". */ #include "zborg1.h" #include "zborg2.h" #include "zborg3.h" #include "zborg6.h" /* This value will need to be tweaked */ #define SHOP_SCAN_THRESHOLD 10 /* * Think about the stores */ extern bool borg_think_store(void); /* * Think about the dungeon */ extern bool borg_think_dungeon(void); /* * Initialize this file */ extern void borg_init_8(void); #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zborg9.h�������������������������������������������������������������������������������0000644�0000000�0000000�00000000775�10250356275�013532� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: zborg9.h */ /* Purpose: Header file for "borg9.c" -BEN- */ #ifndef INCLUDED_BORG9_H #define INCLUDED_BORG9_H #include "angband.h" #ifdef ALLOW_BORG #define BORG_SHOW_FEAT 1 #define BORG_SHOW_INFO 2 #define BORG_SHOW_FLAG 3 #define BORG_SHOW_FLOW 4 #define BORG_SHOW_AVOID 5 #define BORG_SHOW_STEP 6 #define BORG_SHOW_FEAR 7 /* * This file provides support for "borg9.c". */ extern void borg_status_window(void); /* * Initialize this file */ extern void borg_init_9(void); #endif #endif ���zangband/src/angband.rc�����������������������������������������������������������������������������0000755�0000000�0000000�00000004751�10250356275�014066� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* File: angband.rc */ ANGBAND MENU { POPUP "&File" { MENUITEM "&New", 100 MENUITEM "&Open...", 101 MENUITEM SEPARATOR MENUITEM "&Save", 110 MENUITEM SEPARATOR MENUITEM "Show S&cores", 120 MENUITEM SEPARATOR MENUITEM "E&xit", 130 } POPUP "&Window" { POPUP "&Visibility" { MENUITEM "Term-0 window", 200 MENUITEM "Term-1 window", 201 MENUITEM "Term-2 window", 202 MENUITEM "Term-3 window", 203 MENUITEM "Term-4 window", 204 MENUITEM "Term-5 window", 205 MENUITEM "Term-6 window", 206 MENUITEM "Term-7 window", 207 } POPUP "&Font" { MENUITEM "Term-0 window", 210 MENUITEM "Term-1 window", 211 MENUITEM "Term-2 window", 212 MENUITEM "Term-3 window", 213 MENUITEM "Term-4 window", 214 MENUITEM "Term-5 window", 215 MENUITEM "Term-6 window", 216 MENUITEM "Term-7 window", 217 } MENUITEM SEPARATOR POPUP "Increase Tile Width" { MENUITEM "Term-0 window", 240 MENUITEM "Term-1 window", 241 MENUITEM "Term-2 window", 242 MENUITEM "Term-3 window", 243 MENUITEM "Term-4 window", 244 MENUITEM "Term-5 window", 245 MENUITEM "Term-6 window", 246 MENUITEM "Term-7 window", 247 } POPUP "Decrease Tile Width" { MENUITEM "Term-0 window", 250 MENUITEM "Term-1 window", 251 MENUITEM "Term-2 window", 252 MENUITEM "Term-3 window", 253 MENUITEM "Term-4 window", 254 MENUITEM "Term-5 window", 255 MENUITEM "Term-6 window", 256 MENUITEM "Term-7 window", 257 } POPUP "Increase Tile Height" { MENUITEM "Term-0 window", 260 MENUITEM "Term-1 window", 261 MENUITEM "Term-2 window", 262 MENUITEM "Term-3 window", 263 MENUITEM "Term-4 window", 264 MENUITEM "Term-5 window", 265 MENUITEM "Term-6 window", 266 MENUITEM "Term-7 window", 267 } POPUP "Decrease Tile Height" { MENUITEM "Term-0 window", 270 MENUITEM "Term-1 window", 271 MENUITEM "Term-2 window", 272 MENUITEM "Term-3 window", 273 MENUITEM "Term-4 window", 274 MENUITEM "Term-5 window", 275 MENUITEM "Term-6 window", 276 MENUITEM "Term-7 window", 277 } } POPUP "&Options" { POPUP "&Graphics" { MENUITEM "&None", 400 MENUITEM "&Old tiles", 401 MENUITEM "&Adam Bolt's tiles", 402 MENUITEM "&David Gervais's tiles", 403 MENUITEM "&Bigtile Mode", 409 } MENUITEM "&Sound", 410 MENUITEM SEPARATOR MENUITEM "Low Priority", 420 MENUITEM "Activate Screensaver", 430 MENUITEM "&Map", 440 } POPUP "&Help" { MENUITEM "&Contents", 901 } } ANGBAND ICON "angband.ico" �����������������������zangband/src/angband.ico����������������������������������������������������������������������������0000755�0000000�0000000�00000001376�10250356275�014234� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����è�����(��� ���@���������€������������������������€��€���€€�€���€�€�€€��€€€�ÀÀÀ���ÿ��ÿ���ÿÿ�ÿ���ÿ�ÿ�ÿÿ��ÿÿÿ�����������������������������������¿¿��������¿°�� ûûð�����ûûûð��¿¿¿¿¿�¿¿¿Ìϰ��ûûñûû� üüÌÌû���¿°¿±¿¿�¿ÌÌÌÏ���� ðûñûûðûÌÌÌð����±¿¿¼ÌÏ¿°°��� ðûûûûüûûûûð����¿¿¿±¿¿¿¿¿¿�����ûûûñûûû+ûû�����¿¿¿¿²"//¿������ ðð� ò""ûûð����¿�ÿ""������ûññðÿ""!ð�ð���°ÿ"���û�ññ�ñ!!ñ�ñññ���¿°���ûû�ñññññ�ññññ���¿¿¿�����ûûûðññ ññð���¿¿¿¿¿��� ûûû �ñ ûðñðñð��¿¿¿¿°¿°����ûûûû û� ûû�ûûð��¿¿¿°¿¿¿¿¿¿°¿¿°� ûûûû��� ûûûûû���¿¿¿������¿¿���������������������������������������������������ÿÿÿÿà?ÿÃÀü€�€�€��€��À��À��À��à��à��à��à��à��à��à��à��à��à��à��À��À��À��€��€����������€ÿøÿÿÿÿÿÿÿÿ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/angband.xpm����������������������������������������������������������������������������0000644�0000000�0000000�00000002436�10250356275�014261� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* XPM */ static char * angband_xpm[] = { "32 32 7 1", " c None", ". c #000000", "+ c #FFFF00", "@ c #FFFFFF", "# c #800000", "$ c #008000", "% c #0000FF", " ", " ", " ....... ...... ", "..+@+@+@... .....@+@+@... ", ".+@+@+@+@+.......+@+@+@+@+@+.. ", "..+@+@+@+.+@+@+@+@+@+@+.+@+@+. ", " .@+@+@+@+.+@+...+@+@+..@+@+@. ", " .+@+@+@+@.@+..@.@+@+..@.@.... ", " .+@+@+@+.+..@#.+@+@.@#@.@#@. ", " .@+@+@+@+@.@#@#.+@..#@#..@#. ", " ..@+@+@+@..#@#@#.+.#@#@#@.... ", " .+@+@+@..#@#@#@#..@#@#@#@... ", " .@+@+..@#@#@#@#@#..@#@#@#@#. ", " .+@+..@#@#@#@#@#@#.#@#@#@#@. ", " .@+..@#@#..@#$#$#@#..@#@#@#. ", " .+..@#@#.@@.@$$#$#@#.#@#@#.. ", " .@+@#@#@.@@.#$$$$$#@....#@. ", " .+@..#@#.@@.@#$$$$$#@...... ", " ....+@.@....+@$$$$$@+@+@.. ", " .+@+@.@+@#@+@+$$$$@$@+@.. ", " .@+@+@+@###@+@+@+$+@+@+. ", " .+@+@+@+##@+@+@+@+@+@+@.. ", " .+@.@+@+##@+@+@%@+@+@+@+@. ", " .@...@+##@+@+@+%%%%@+@+.+. ", " .+@.@+@#@+@+@.@+%%%%%%@..@. ", " .+@+.+@+#+@+@..+@%%%%%%%@... ", " .@+@+@###@+@+...+@%@%%%%%@+. ", " .+@+@##+@+@+@. .@+@+@+@%%%@+. ", " ..+@+#@@+@.... ......@+@+@+@. ", " ..+@#@+@.. ....+@+. ", " ....... .... ", " "}; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/zangband.icns��������������������������������������������������������������������������0000644�0000000�0000000�00000131530�10250356275�014601� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������icns��³Xics#���Hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿis32��ç�9€k�BZZ�1€k)1kÞÞRsÿïŒÿÿ{1{ÞÞkk¥1Æçÿ­BµÿÿÆÆkÞkk1Æ9B{{9B{œ1k1kBZ€„BB€„B9k��Þ„ÿï9BZ„½ÿÆ„�€ÿ {�ÿ{„ÿ{„ÿÿ„1„ÿ�{s„Þ9„€ÿ {„ÿÿÎç½B�„�„œ€ÿ9{ÿ½ïÆÞ�„BB÷œÿÿ9�ÿçks�Æ{„ÿB�sœ½�Bÿ½„­ÿ{„ÿÿ„„ÿ91k9µÿÿ{9{Bÿ#½9kkÞ9½ÿïÞk�B{½œ1ÞkkÞÞ1{RZ�„„k99ÞÞk9€k!�BB{1�€k�1��BJJ�…�sÿï”ÿÿ{Bƒ� Æçÿ­BµÿÿÆÆB�Æ9B{{9B{�Œ€�kBJ€„BB€„B9k��Þ„ÿï9BZ„½ÿÆ„�€ÿ {�ÿ{„ÿ{„ÿÿ„1„€ÿ÷{s„Æ9„€ÿ {„ÿÿÖï½B�„�„œ€ÿ9{ÿ½ïÆÆ�„BBÿœÿÿ9�÷çks�Æ{„ÿB�sœ½�BÿÆ„­ÿ{„ÿÿ„„ÿ9��µÿÿ{B{Bÿ�½‚� ½ÿ÷Þk�B{½Œ„�{BB�s„k9…��1B{9ƒ��€�BJJ�€)))sÿï”ÿÿ{J))�Æçÿ­BµÿÿÆÆ9)�Æ9B{{9B{”��kBJ€„BB€„B9k��Þ„ÿï9BZ„½ÿÆ„�€ÿ {�ÿ{„ÿ{„ÿÿ„1„ÿ{s„Î9„€ÿ {„ÿÿÖç½B�„�„œ€ÿ9{ÿ½ïÆÎ�„BB÷œÿÿ9�ÿçks�Æ{„ÿB�sœ½�BÿÆ„­ÿ{„ÿÿ„„ÿ9µÿÿ{9{Bÿ!½!½ÿïÞk�B{½Œ�!)!„JJ�s„k9!)‚��1B{9��€�s8mk��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿICN#��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿil32��Þ��k‹�ƒÞÎk�kÿ�kÿÿ€�΄Þ��ƒÞ{��ÿ�Öïÿ�k�ƒÞ��‚Þ��Îÿ¥cƒÿ�k�ÿ�‚Þ��€ÞÎ��‚ÿ¥��Öÿï�ÿÿ�΀Þ��ÞÞ€�ÿÿ¥ï‚ÿ��†ÿÞ�€Þ ��ÞÎ��ÿÿ��ƒÿ��‡ÿ �ÎÞ��Þ��ÿÿ‘� kÿ��Î��Î�¥ÿ’�ï΃�ÿ��k…ÿ��…ÿ��Þ‚�cÿ�ƒÿƒ��c�‚ÿ�ÿ�ÿÿ�ÿ¥��kÿ��€ÿk��‚ÿ�ÿ�„ÿ€�€ÿ��ÿ��ÿï�ÿ€��c„ÿ€�€ÿ��€ÿï��‚ÿ�ÿÎ��…ÿï€ÿ��ÿ��…ÿ��†ÿ�Ö��ÿÿk€�…ÿ��ƒÿÖ¥ÿÿƒ��ÿ�†ÿ��ƒÿcïÿ�ÿ��ÿÿc�ïƒÿ��c‚ÿ¥€ÿ‚��ÿ€�€ÿc�‚ÿ€�€ÿ��‚ÿ k��c��ÿÿ��ÿÞƒÿ�€ÿ¥¥ÿÿÖ€�ÿÿ��€ÿ�Îÿc€ÿ�ïÿÿï‚��€ÿ��ÿƒ�ÿÿƒ�ÿ�€��¥€ÿ��‚ÿ���€ÿ ���k�ÿÿï…ÿ��ˆÿ ��Î��Î�Öï‚ÿ€�ÿ��ˆÿ��Þ��ÞÞ��ƒÿ�ï�ÿ€�†ÿ ��ÞÞ��ÞÞÎ��‚ÿÎÿcÿ€��„ÿ��ÎÞÞ��Þ�€ÿïïÿÿ¥…�ÿk��΀Þ��ÞÎ��ïÿ�ïÿk€��k€�¥ÿ��ÎÞ��ƒÞ��k‚��¥ÿ�ƒÞ��„Þ�{ƒ�€ÿ�Ö��{„Þ�„���¥k‚��‰���!‘�!�!ÿ�!ÿÿ€�Œ���ÿ�Ö÷ÿ�!��Îÿ¥sƒÿ�!�ÿˆ���‚ÿ¥��Öÿ÷�ÿÿ�‡�ÿÿ¥Þ‚ÿ��†ÿ�ç„���ÿÿ��ƒÿ��‡ÿ�ƒ�ÿÿ‘� !ÿ�����¥ÿ’�÷΃�ÿ��!…ÿ��…ÿ��ç‚�sÿ�ƒÿƒ��s�‚ÿ�ÿ�ÿÿ�ÿ¥��!ÿ��€ÿ!��‚ÿ�ÿ�„ÿ€�€ÿ��ÿ��ÿ÷�ÿ€��s„ÿ€�€ÿ��ÿ��‚ÿ�ÿÎ��…ÿÞ€ÿ��ÿ��…ÿ��†ÿ�Ö��ÿÿ!€�…ÿ��ƒÿÖ¥ÿÿƒ��ÿ�†ÿ��ƒÿ�s‚ÿ�ÿ��ÿÿs�÷ƒÿ��s‚ÿ¥€ÿ‚��ÿ€�€ÿs�‚ÿ€�€ÿ��‚ÿ !��s��ÿÿ��ÿçƒÿ�€ÿ¥¥ÿÿÖ€�ÿÿ��€ÿ�Îÿs€ÿ�Þÿÿ÷‚��€ÿ��ÿƒ�ÿÿƒ�ÿ�€��¥€ÿ��‚ÿ���€ÿ ���!�ÿÿ÷…ÿ��ˆÿ �����Ö÷‚ÿ€�ÿ��ˆÿ†�ƒÿ�ÿ�ÿ€�†ÿ…���‚ÿÎÿsÿ€��„ÿ��…��ÿ÷ÿÿ¥…�ÿ!��†���Þÿ�÷ÿ!€��!€�¥ÿ��‹�!‚��¥ÿ��ƒ�€ÿ�Ö��…��„���¥!‚��‰���1‹�ƒ)1�1ÿ�1ÿÿ€�„)��ƒ)€�ÿ�Öïÿ�1�ƒ)��‚)��Îÿ¥sƒÿ�1�ÿ�‚)��€)��‚ÿ¥��Öÿï�ÿÿ�€)��))€�ÿÿ¥÷‚ÿ��†ÿÞ�€) ��)��ÿÿ��ƒÿ��‡ÿ �)��)��ÿÿ‘� 1ÿ�����¥ÿ’�ï΃�ÿ��1…ÿ��…ÿ��Þ‚�sÿ�ƒÿƒ��s�‚ÿ�ÿ�ÿÿ�ÿ¥��1ÿ��€ÿ1��‚ÿ�ÿ�„ÿ€�€ÿ��ÿ��ÿï�ÿ€��s„ÿ€�€ÿ��€ÿ÷��‚ÿ�ÿÎ��…ÿ÷€ÿ��ÿ��…ÿ��†ÿ�Ö��ÿÿ1€�…ÿ��ƒÿÖ¥ÿÿƒ��ÿ�†ÿ��ƒÿs÷ÿ�ÿ��ÿÿs�ïƒÿ��s‚ÿ¥€ÿ‚��ÿ€�€ÿs�‚ÿ€�€ÿ��‚ÿ 1��s��ÿÿ��ÿÞƒÿ�€ÿ¥¥ÿÿÖ€�ÿÿ��€ÿ�Îÿs€ÿ�÷ÿÿï‚��€ÿ��ÿƒ�ÿÿƒ�ÿ�€��¥€ÿ��‚ÿ���€ÿ ���1�ÿÿï…ÿ��ˆÿ �����Öï‚ÿ€�ÿ��ˆÿ��)��))��ƒÿ�÷�ÿ€�†ÿ ��))��))��‚ÿÎÿsÿ€��„ÿ��))��)�€ÿ÷ïÿÿ¥…�ÿ1��€)��)��÷ÿ�ïÿ1€��1€�¥ÿ��)��ƒ)��1‚��¥ÿ�ƒ)��„)„�€ÿ�Ö‚�„)�„���¥1‚��‰�l8mk��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿich#��Hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿih32��~˜��Z’��1€kss€kckk1„�B��B„ÿB‚��9ˆk1��k‡Þ�R‚�1„Îÿ�„ÿB�Rœ†ÞŒ��k†Þ�R€�Z„Þ€ÿ9k÷ƒÿ½B�€��9…Þk��k„Þk1��BÞƒÿ��Æÿÿœ€ÿ ÷œŒB��B�9ÖƒÞk��sƒÞR�!�œ‚ÿ½BR{µ€ÿ„÷€ÿ {JŒ��÷B�9Ö‚Þk��kÞÖ1��Þƒÿ)Z��µ„ÿ9�ÞÿÆ��1‚Þs��k€Þœ�Zï†ÿµB€��½„ÿ„Æ€ÿÆB�1Þs��sÞÞÆ��ÆÿÿœR­„ÿ„��sŠÿ÷B�R€Þk��kÞÞ1��Þÿÿ9��B„„ÿ{��{Œÿ��1ÖÞk��kÖ¥��Zÿÿ9€��…{9��BŠ{½ÿµ��¥Ök��kÞ��Þÿ9›�÷ÿ��Þk��kc��”ÿB��š�9ÿ­��1k��R�sÿB��1‡„B��Bˆ„�€�9÷��1‚�Þÿ��1µ­ˆÿ„��„ˆÿÞs€�RÞ„�÷ÿ�‚ÿ÷ÿÿ¥{„�s1‚�{ï÷ÿ½��­kƒ�1ÿÿ�sƒÿÞƒ�J��„ÿ{„��Bƒÿµ��µƒ�„ÿÿ­�ƒÿ�Z€�B­Îÿ{��„ÿµB��Öƒÿœ��ÿƒ�ˆÿ€��1ÿ{��„ƒÿ��{ƒÿÖ��ÿB��Zˆÿ€��Rÿ{��„ƒÿ��„„ÿ�sÿ{��µ‡ÿ÷��Zÿ{��„ƒÿ��„„ÿ�÷ÿÞ�‰ÿc��Rÿ{��{‚ÿï��„„ÿ�Æ€ÿ1��k‰ÿ�­€�Zµÿÿ{��„ÿ½9��Έÿs��{‰ÿï)� ZZ��„ÿµZ€�÷„ÿ�÷€ÿ{��{…ÿkZZkÿïB…�„ÿ‚��)Šÿ{��B…ÿkœ½sÿ÷ÿÞsƒ�„ÿ€�µçÿœˆÿ�{€��ï„ÿs½ïs‚ÿ�c‚�„ÿ€�ÎÿÿÆŒÿï…ÿ�k€��­„ÿc½kkÿ�”ƒ� „ÿ��ÞÿÿµÿÞ)­„ÿ�€��{ÿïœÿ”1„€ÿ�½„�„ÿ­€�Þ€ÿ÷JÞ÷Îÿ�­��Z‚ÿk÷ƒÿÞ€�„„��„ÿÿœ€�½ÿ÷ÿ�Œÿ�Z€��‚ÿZ¥€ÿ Öµs��Þÿ{��„€ÿ�”‚�{µÖÿµ„€ÿ�µƒ��œÿœ�1‚�Þÿÿ{��{ÿ�œ„�1�”€ÿ�Zƒ��R‚ÿ�Z…��Þ€ÿ{��„‚ÿ�k„��B€ÿ�½…��ÎÿïBƒ��œÿ{��„‚ÿ÷B‚��9ÿ�‚�1�Ö€ÿ÷ÿ÷„�µ‚ÿ{��{ƒÿ�÷„­÷€ÿ�­€�1��kR��B‰ÿµÞÿÿ{��„ÿ)��Jk��k½€�µ÷÷…ÿ)��Œÿ{��„Œÿœ��1Þk��kÞŒ�ï„ÿ ÷œ�B�ÿ{��Œÿ)�ÆÞk��kÞÞR��)…ÿ{ÖÖ�ÿ{€��BŠÿ 1��ŒÞÞk��k€ÞR��s÷ÿïÿÿ„ÿ„„ÿ{�Ö‚ÿ�€ÿ½1��s€Þk��kÞR��)ï€ÿœ„÷ÿÿçÿÿZ‚� 1Z{{9ÿÿÆ€��sÞk��k‚Þ R��)ïÿÿ­9ÿ€÷ÿœˆ�cÿÿs€��s‚Þs��kƒÞ�1€�µÿœ�9÷ÿ��‚�”ÿÖZ€��sƒÞk��k„Þ k!��ZÞB�1‚�s½„­ÞZ��Œ„Þk��k…Þ¥1��k…��”‚ÿÞR‚��¥…Þk��k…ÞÖÖ9†�„Þÿÿ÷Þ{)‚��9‡Þk��Rk�Œƒk�!�1��{Z„��Rˆk�R�k÷JŽ��†�˜��J’��…���„�B��B„ÿJŠ��€��Š��‚�1„Æÿ�sÿ�B‚�“��€�Z„Þ€ÿ9k÷ƒÿ½B�˜�BÞƒÿ��Æÿÿœÿœ„B��B����œ‚ÿ½BRk½€ÿ„÷€ÿ {9„��ÿB��„��‚��€�Þƒÿ)c�µÿ÷ÿ÷9�ÞÿÆŒ��€�Jï†ÿµB€��½„ÿ„Æ€ÿÆB‰��€�Æÿÿ¥R­„ÿ„��{‹ÿ�B‹�Þÿÿ9��B„„ÿ{��„Œÿ€��‚��€�Zÿÿ9��…{9��BŠ{½ÿ¥€��…�Þÿ9›�ÿÿ…���”ÿB��š�9ÿ­ƒ���sÿB��1‡„B��Bˆ„�€�9ÿ!…�Þÿ��9¥µˆÿ„��„ˆÿÞ{€�BÞ„�÷ÿ�…ÿ¥{„�{9‚�{ï‚ÿ½��­kƒ�!ÿÿ�{ƒÿÞƒ�9��„ÿ„„��1ƒÿ½��¥ƒ�„ÿÿ­�ƒÿ�Z€�B¥Îÿ{��„ÿ¥B��Öƒÿœ��ÿƒ�ˆÿ€��1ÿ{��„ƒÿ��{ƒÿÆ��ÿB��Zˆÿ€��Rÿ{��„ƒÿ��„„ÿ�sÿ{��¥ˆÿ��Zÿ{��„ƒÿ��„„ÿ�÷ÿÞ�‰ÿc��Bÿ{��„‚ÿï��„„ÿ�Æ€ÿ)��k‰ÿ�¥€�Z½ÿÿ{��„ÿ½9��Öˆÿs��{‰ÿï)� Zc��„ÿ¥Z€�÷ˆÿ{��{…ÿkZckÿï1…�„ÿ‚��)Šÿ{��B…ÿZŒµs€ÿÞsƒ�„ÿ€�µçÿœˆÿ�{€��ï„ÿkÆçs‚ÿ�c‚�„ÿ€�µÿÿÆŒÿï…ÿ�k€��¥„ÿcÆskÿ�”ƒ� „ÿ��ÎÿÿµÿÞ)­„ÿ�€��„ÿï¥ÿ{!s€ÿ�µ„�„ÿ­€�΀ÿ÷RÞÿ½ÿ�­��Zÿ÷k„ÿÞ€�„„��„ÿÿœ€�µƒÿ�sÿ�J‚�‚ÿZ¥€ÿ Öµs��Þÿ{��„€ÿ�”‚�{µÖÿ½„€ÿ¥���Œÿœ�!!‚�Þÿÿ{��{ÿ�œ„�)�œ€ÿZ‚��B‚ÿ�Z…��Þ€ÿ{��„‚ÿ�k„��B€ÿ�½…��ÖÿïB��œÿ{��„‚ÿ÷B‚��9ÿ�…�Þ‚ÿ�÷„�µ‚ÿ{��{„ÿ„­÷€ÿ�­„���B‰ÿ¥Îÿÿ{��„ÿ)����€��µ‡ÿ)��{ÿ{��„Œÿ�œ†���ï…ÿ Œ�B�ÿ{��Œÿ�)Š��)…ÿ{ÎÞ�ÿ{€��BŠÿ�1‰���c÷ÿïÿÿ„ÿs„ÿ{�΂ÿ�€ÿ½1��‹�)ï€ÿŒ„€ÿïÿÿZ‚� !Z{{9ÿÿÆ��‹� �)ïÿÿ¥Bÿ÷€ÿ�Œˆ�cÿÿR€���­ÿ¥�9ÿÿ÷Œ‚��‚�”ÿÞZ��� ��JÎB�!‚�sÆ„­ÞB™�!Z…��”‚ÿÞZ��“�€†�„Þÿÿ÷Þ{)‚��Š��ˆ���1��{J„��‰���kÿ9˜�˜��R’��€€„�B��B„ÿJ‚��ˆ��…)!!‚�1„Æÿ�sÿ�Bƒ�!…)��…)�!�Z„Þ€ÿ9k÷ƒÿ½B�€��…)��„)��BÞƒÿ��Æÿÿœÿœ„B��B�!ƒ)��ƒ)�€��œ‚ÿ½BRs½€ÿ„÷€ÿ {B„��÷B�!‚)�)!��Þƒÿ1c��µ„ÿ9�ÞÿÆ€��!)��€)�€�Rï†ÿ½B€��½„ÿ„Æ€ÿÆB��)��))€�Æÿÿ¥Rµ„ÿ„��sŠÿ ÷B�!!)��)!€�Þÿÿ9��B„„ÿ{��{Œÿ€�!)��!��Zÿÿ9��…{9��BŠ{ ½ÿ­��!��)€�Þÿ9›�÷ÿ€� !����”ÿB��š�9ÿµ€��‚�sÿB��1‡„B��Bˆ„�€�9÷!…�Þÿ��9­µˆÿ„��„ˆÿÞ{€�BÞ„�÷ÿ�…ÿ¥{„�s9‚�{ï‚ÿ½��­kƒ�)ÿÿ�sƒÿÞƒ�B��„ÿ{„��1ƒÿ½��­ƒ�„ÿÿ­�ƒÿ�Z€�B¥Æÿ{��„ÿ­B��Öƒÿœ��ÿƒ�ˆÿ€��1ÿ{��„ƒÿ��{ƒÿÆ��ÿB��Zˆÿ€��Rÿ{��„ƒÿ��„„ÿ�sÿ{��­‡ÿ÷��Zÿ{��„ƒÿ��„„ÿ�÷ÿÞ�‰ÿc��Bÿ{��{‚ÿï��„„ÿ�Æ€ÿ)��k‰ÿ�¥€�Zµÿÿ{��„ÿ½9��Öˆÿs��{‰ÿï)� Zc��„ÿ­Z€�÷„ÿ�÷€ÿ{��{…ÿkZckÿï1…�„ÿ‚��)Šÿ{��B…ÿc”Æ{ÿ÷ÿÞsƒ�„ÿ€�µçÿœˆÿ�{€��ï„ÿkÆ÷{‚ÿ�c‚�„ÿ€�½ÿÿÆŒÿï…ÿ�k€��¥„ÿkÆskÿ�”ƒ� „ÿ��ÎÿÿµÿÞ)­„ÿ�€��„ÿï¥ÿ„){€ÿ�µ„�„ÿ­€�΀ÿ÷RÞÿ½ÿ�­��Z‚ÿk÷ƒÿÞ€�„„��„ÿÿœ€�µÿ÷ÿ�{ÿ�J‚�‚ÿZ¥€ÿ Öµs��Þÿ{��„€ÿ�”‚�{µÖÿ½„€ÿ­���Œÿœ�!)‚�Þÿÿ{��{ÿ�œ„�)�œ€ÿZ‚��J‚ÿ�Z…��Þ€ÿ{��„‚ÿ�k„��B€ÿ�½…��ÖÿïB��œÿ{��„‚ÿ÷B‚��9ÿ�…�Þ‚ÿ�÷„�µ‚ÿ{��{ƒÿ�÷„­÷€ÿ�­ƒ���B‰ÿ­Îÿÿ{��„ÿ)����€�µ÷÷…ÿ)��{ÿ{��„Œÿ�œ€� !��!��ï…ÿ ”�B�ÿ{��Œÿ)��!��))��)…ÿ{ÎÞ�ÿ{€��BŠÿ 1��))��))!€�k÷ÿïÿÿ„ÿs„ÿ{�΂ÿ�€ÿ½1€�€)��)��)ï€ÿŒ„÷ÿÿçÿÿZ‚� )Z{{9ÿÿÆ���)��‚)�)ïÿÿ¥9ÿÿ÷ÿÿ”ˆ�cÿÿZ��!)��‚)�!�½ÿ¥�9÷ÿ��‚�”ÿÞZ���ƒ)��„)�€�RÎB�)‚�sÆ„­ÞJ��„)��…)�€�!Z…��”‚ÿÞZ���…)��…)!!†�„Þÿÿ÷Þ{)ƒ�!!…)�€�ˆ��1��„€{R…�ˆ�kÿ9˜�h8mk�� ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿit32��EU����Å��ü�Æk9ú�ÿ­kµ���� �ÿ÷µ¡����ΓÞÎÎÞÞÎÞÖÎÎ’�JŒÿ…�…ÿ�‡���‚ÞÖÖ‹ÞÖÖ…Þ΄�–ÞÖÖÞÖ”k1�JJc¥ÆÖÆŒ‚�9kµ…ÿÆ1��Z‡�1­Æ”Ö€Þ�Ö”ÞZƒ�–ÞÖÖÞœc9‹� )1)ŒŒ¥çÿÿÖJ�)s¥÷†ÿk1�Zˆ�sŒZœÎÞÞÖ”ÞZ��€�•Þ�΀Þ�� ¥Î¥ÿ÷ïïÿÿÆ‚�¥Þ÷ˆÿ�Æ�œÖ–Þ…�–ÞÖœ‹�JŒ†ÿï9��‘ÿï9�•Þ…�•ÞÖ¥k†�J¥€ÆÖçÿ�÷ÿ9��½Öçÿÿ€÷Šÿέ{J€�99†�1µ“Þ…�•ÞœkBƒ�!1)Œç†ÿ�÷ÿ�{œ½ÿÿ€÷‹ÿï½c)�99‡�{µ’Þ…�•Þ†�œÆ¥‰ÿ�ï€ÿƒ��BŽÿ�ï€ÿckkc€��‡��1’Þ��€�‘Þ‰�‘ÿ��Æÿÿïÿ9c…ÿ�ï€ÿ�J„ïÿ9ƒ�ÿ9ƒ�ÎÎÖŽÞ…�Þµ1�Z€�s¥Æ‹ÿ猽œcJJÆ÷„ÿΔçˆÿ�BÖÿ{9‚�ÿΔ‚�1µÞÞ֌ޅ�ŽÞΔ‚�Z��)­ç‹ÿÎ¥J{œ„sc†ÿïµçˆÿ��)µÞ„J‚�÷ÿÎ9‚�{µÞ֌ޅ�ŽÞ”Z����¥ïÿÿï‰ÿ�9„�œµc‚ÿ÷÷ÿÿ½”c‰ÿ��kccJ‚�Þ÷ÿÿƒ�1ÎŒÞ�Î…�Þ†�JŒ‚ÿ�ïˆÿ€��‰��ïÿ÷÷Šÿï9„�JŒ‚ÿ�„��Όޅ�‰ÞÖÖ­1…�JµçŽÿ�ZŒÆJ‡�9µçÿÆ„‚�Jµç‚ÿÆ1ƒ�1­ÖŠÞ…�‰Þ½¥{‚�)ŒçÿBœÎÿŒJ!‡�{µÿ÷Æ9�Œç„ÿs1ƒ�s­ŠÞ…�ˆÞÎ{{‚�R{¥–ÿÞœ‡�9ï‘ÿ�‡ÿƃ�1Ήޅ�†ÞÖœ†�µ÷›ÿ�9‡�¢ÿ�9ƒ�‰Þ�„�…ÞÖZ9Zƒ�1ÆÆïƒÿ½½œ9ŒÞ÷ÿÎÆÆ…�¢ÿ½{‚�”·ޅ�…Þ½)Z‚�1kƒÿ ÖÎÞ{{Z�JœÆ÷‘ÿ…��÷¡ÿï½9�Z”µ†Þ…�…Þ{„��Æ„ÿBcƒ�1Αÿ…�Þ÷ÿ÷÷Žÿƒ�1΀Þ�ÎÞ…��΃Þ�΀���)¥ÿ�ï€ÿ…��€��ïŒÿ�ï€ÿ…�ÿ�ï¡ÿ�9ƒ�ÎÞÞÎÞ…�‚ÞÖ­1ƒ�1΃ÿÎ9…�Æ‘ÿ…�÷ÿÿ÷¢ÿ�Bƒ�1­Ö‚Þ…�‚ÞÖœƒ�Jç‚ÿΔ†�Þ÷“ÿ…�÷ÿÿ÷¢ÿs9‚�œÖ‚Þ…�€ÞÎÞÖœƒ�JŒÿÿ÷÷ÿÿ9‚���cÞ“ÿ…�§ÿÞk‚�œÖÞÞÖÖÞ…�€ÞÎÞ1„�Æÿÿï€ÿ€��Ê��ÆÿÞcƒ�1Þ΀ޅ��Ö€Þ”!ƒ�JÖÿ÷ÿÿÎ9Î�k­÷÷ÿ÷Þƒ�!”ÞÖÖÞ…��Ö€ÞZƒ�Œçÿ÷ÿΔÏ�9{ï÷€ÿƒ�ZÞÖÖÞ…�Þ��€�‚ÿ�9†��Ç�9ïÿ€���Þ…�ÞÖœ‚��)¥ÿÕ��ï€ÿï9„�œÖÞ…�ÞÖœƒ�Z”ïÿÿÖBƒ�RÍ�9Æ÷ÿÿµsƒ�!ZÞ…�ÞÆŒ‚�1œÎÿÿÖ”„�RÎ�„Æÿÿ祄�1Þ…�ΔZ‚�Î÷€ÿ�BÙ�9ïÿÿÆ€���1Î…�{ƒ�ÿ‹�1Ιÿ…�›ÿÞc‰�Þ÷Þc��‚�!œÿ…�‚J¥Î÷™ÿ…�›ÿ÷ÞJ‡�1½çÞJ‘�1Æÿƒ�1€Œ„Œç›ÿ…�ÿŒRB1†�”ÖÿŒ!‘�)¥ÿƒ�9΀ÿ�ïÿ�„�Ÿÿ÷Î���R”ÿÿ9��‡��ƒ�1Îÿ‚��ÆŠÿ�ïÿ�ï€ÿ�ï€ÿ‘�ÿŒJ‹�Æÿïÿ�ï‰ÿ�9…�)¥ÿ½{�9s÷ÿ�JÖ‹ÿ÷ÿÿ÷ÿÿï½9‘�ç„J‹�199½ïÿ÷ŠÿÎ¥Rƒ�)ÿï­�JŒ‚ÿ�Œç‹ÿ÷ÿÿ÷ÞÆ­{•�ç¥s9�{Îï÷‹ÿ猅�Þ÷ÆŽ�R”‚ÿ�‘ÿc—�ÿŽ�{½ÿï‹ÿ�ï…�cÞÞk€��‰�JŒÿÿ÷÷ÿ€�Þ÷ÿÞc�1Îk��€�‚ÿ÷÷ÿÿ9ˆ�R”‹ÿ÷ïïÿ9„�ÆÞcŽ�¥çÿ÷€ÿÆ1��1ÎŒÿΔ‡� JRRkµµ½Î÷„�†ÿÎÆÆJJ9ƒ�B½‰ÿ�÷ÿΔƒ�Æ÷ÞŽ�Æÿÿ÷ÿZ��ÆŒÿÆ„‡�1R€Œ¥ï÷€ÿ…�‰ÿŒŒ{Bƒ�)¥‰ÿ�÷‚ÿÆ‚�Æÿÿ‰����Æ€ÿ÷÷ÿÿ½{�ÆŒÿÞœ‡�Æÿÿïÿ…�‚ÿ÷÷†ÿƒ�)¥ÿÞc‚�Æÿÿ�ÿ�ï•ÿ�9‡�‰ÿ…�ÿï9ƒ�ÿÞk‚�ÆÿÿŒ�šÿ�9…�!œ‰ÿ…�Žÿ�9ƒ��÷ÿ÷ïÞ‚�ÆÿÿÆ1‰�9šÿ�9…�1Ɖÿ…�Žÿ�9ƒ��÷ÿ÷÷ÿ�1΀ÿ�9‰�1Öšÿ�9‚���)¥‰ÿ…�Žÿ�9ƒ�‘ÿ�Î÷€ÿ�9‰�)¥šÿ�9…�)¥‰ÿ…�Žÿ�9ƒ�‘ÿ�‚ÿ�9ˆ�9sï™ÿç9…�1Ɖÿ…�Šÿ÷÷ÿÿ9ƒ�‘ÿ��!œ‚ÿµs‡�JŒšÿçJ„�1Ɖÿ…�Šÿ÷÷ÿÿ9ƒ�‘ÿ��1Þ‚ÿÖ”‡�R”›ÿŒJ�‚�)¥‰ÿ…�Žÿ�9ƒ�‘ÿ��9ƒÿ½{‡��Ƙÿ÷÷ÿÿ½{���1Ήÿ…�Šÿ÷÷ÿÿ9ƒ�‘ÿ�Æ…ÿÞk…��Æœÿ祄�!„‰ÿ…��÷Œÿ½)ƒ�‘ÿÆ÷…ÿÞc…�)Μÿ÷½ƒ�RÞ÷‡ÿ…��÷Œÿ{‚��›ÿÞ{…�¥ïÿÞc„�cÞ‚ÿ�ïÿ…�ÿ��c›ÿ÷Î…�•ÿ�ïƒÿïÿ÷Îk‰�Þ÷ƒÿ…�‰ÿï9…�1Îÿ…�™ÿ�÷€ÿ÷ÿ甉�19RŒ½Öçÿ…�ƒÿ÷Þ½œ{B9…�1Αÿ�÷ˆÿ…�™ÿ�÷€ÿ÷ÿï½)‰�J{œ­Þ…�ÿÞÞΜ{ZB‡�JÞ‘ÿ�ïˆÿ…��ï ÿ¥)�c…�ÿckR‹�R”’ÿ�ïˆÿ…�•ÿ…�Ö÷€ÿ½{–�ÿ�ÿ÷÷ÿïœÿ…��÷’ÿ ÖB{œœ{œ„„¥1΀ÿïÎ{R“�ÿ‹�{¡ÿ…��÷’ÿ Î!¥µµ¥µ­µÞ�ÆÿlB)1‘�ÿ‰�1)Jµÿ�ïœÿ…�“ÿ Þk¥{{¥c”µï�Æ€ÿ÷÷ÿïµµÖ‘�ÿ‰�έµïÿ¥ï›ÿ†�ÆÿïïŽÿÆ¥s„‚ï�Æÿÿï…ÿÞœŽ�ÿ†�JŒÿï‚ÿ÷Î�Æ›ÿ†��Æ‘ÿÖJsk„‚ï�ƈÿ÷Þœ�ÿ…�Jµçƒÿ ¥s1�1Z½ÿ÷÷’ÿ�÷€ÿ†�­ïÿÖcs{œ‚ï�Ɖÿ÷Î�ÿ…�{Þ„ÿ ”Z�Jµÿ÷÷ÿïÿ÷ÿÿï†�{½ÿ÷Îk¥ÆÖÞï�Ɔÿïÿÿ÷Î�ÿ…�Î÷„ÿ½”c�{µÖÿ¥ï‘ÿ牢�RŒïÿ Æ¥s„便­Î�ƈÿ½{Ž�ÿ†� JŒÿÿ÷÷ÿïÿ÷Î�Æÿï¥�Æ‘ÿÞk†�JŒÿ÷ÿÿÆÆ¥­ÿsBZÖ�Ƈÿ½JŽ�ÿJ„�1”„ÿ÷Æ÷€ÿ¥s)�1Z½€ÿ�÷‹ÿÎ!†�JŒŠÿ÷ÞÞ÷ÿÿÎ9µœ¥Æc1B½)Ɔÿï{�ÿŒ!…�RÞ÷ˆÿœc�Jµ€ÿ�÷Šÿ﵇�J„ï‰ÿ Ö„Œÿïÿ÷ÖkkRc€k¥Þ÷„ÿ陋�‚ÿ�9‡�cÞˆÿÞµc�{µÖŽÿ½{��ƒ�)¥‰ÿÎ9B‚ÿ„�ï…ÿ÷Îk€��„��…�‚ÿ½{‡�R”ÿ�ï€ÿïÿÿ÷Î�ÆÿÿïïÎkÎ÷ˆÿ�9‰�)¥‰ÿç9B‚ÿ…Æÿ�÷ÿ­s!†�9kÆ…�‚ÿïÎ{†�9”÷ÿïïÿ ÷Æ÷ÿ÷ÿÿÆ!Ö÷ˆÿ�9‰�)­‰ÿ÷99ïÿ�÷€ÿÞsB†�)s¥ÿ…�ƒÿï½1…�RÎçïÿÿïï…ÿ÷ÿÿÆ!Ö÷‡ÿÞ1‰�1ΊÿB)¥ÿçç‡ÿ÷÷‚ÿc‡�¥ïÿÿ…�ÿ�ï€ÿÎ1‡�k”½ƒÿ÷÷ƒÿ÷ÎkÖ÷‡ÿk‹�€ÿ�ï†ÿÞœ�ï€ÿ�ïˆÿÖ÷Þk†�JŒ‚ÿ…�†ÿ½{‹��Æ„ÿ÷÷ÿÿÞœ�‡ÿÞk��„�Z��ŠÿÆ„�9œÎ„ÿ ï½½Öµ9191…�Jµç‚ÿ…�†ÿïÎ{Š��1€9 µÖ½½ïÿÿ½ŒZ�‡ÿÞc‰�Z��Þ÷ˆÿÎŒ��ZŒÆ‚Þε{{œ{‰�Œçƒÿ…�‡ÿï½)Œ� s”{{ÆÞÞ{Z9�†ÿ÷ÎJ�kÞ‰ÿ�Æ�ckkck!‹��c…ÿ…�ÿ�ï„ÿ¥)‚��Š�Jck€��ï…ÿ祅��†��ƉÿÞc–�R”ÿ�ïÿ…��ï‰ÿÞœ‘���c†ÿ½{��µ÷‡ÿ÷ïÞJ“�Jµç†ÿ…�Šÿ÷Ö{“�1Þ†ÿZ�œÞ‡ÿ÷÷ÿŒ!“�Œç‡ÿ…�‹ÿï½’�1k†ÿÆ1‹���R”‹ÿ�9Š��…�‰ÿ…�ÿ€��€��Š��Æ€ÿ÷÷‚ÿ“�1Ίÿ½{�1Ήÿ…�ÿÖ1‹��€�…ÿïÿÞc“�{ŠÿïÎ{JŒ�Z”ç‰ÿ…�ÿ÷ΔŒ�)µ‡ÿÆŽ�€�JÞ÷‰ÿlRB1‰�1œÎ÷‰ÿ…�ÿÎ1‰�)1c÷†ÿï­�ZZ‚�cÞÿï†ÿ÷÷€ÿ÷Ή�Î÷‹ÿ…�ÿ÷Þ‰�¥Îç‡ÿ½{‰���Î1„�JŒ†ÿ�ï”ÿ�ïˆÿ…��ï©ÿ�9‚���1Î…�Þs9ƒ�R‡ÿ÷÷žÿ…�©ÿ½)„�s­Þ…�Þœc„�1Ɔÿ÷÷’ÿÞ…ÿ…�©ÿ{„�Œ½Þ…�ÞÖœ…�œÿïc€k…ÿ…�©ÿ€��‚�RŒÞ…�ÞÞÆ{…�€ÿïïÿÿïÿïÿÿïÿ½{ƒ�RŒïÿ…�€ÿ�ï£ÿÞc��€��΀ޅ�ÞÖν…�9µçÿï÷÷“ÿZƒ�JÖÿ…�§ÿƃ�!œ€Þ�Ö…��Þ€Ö1„�ŒÎÿ€÷’ÿÞ1…�!œÿ…�Þ÷¤ÿï­ƒ�JÖ€Þ�Ö…��΀ÞÎ1„�R”’ÿïï÷ÿk‡�ÿ…�cÞÿï¢ÿ½{ƒ�Z”Þ΀ޅ�ÞÖÖÞÞ”{{ƒ�)¥€ÿ�ï‘ÿ��JŒÿ�ÿ†��Ƥÿ�9ƒ�{Æ‚Þ�Î…�‚ÞΜƒ�)½ïÿ�÷€ÿ�R­Ö÷J��ÿ†�1k¡ÿç9ƒ��Ƅޅ�ƒÞµ1„�{Þÿ�÷€ÿ€�ŒÞ÷÷Œ!��ÿ‡�1ÆŸÿ籠��…Þ…�…Þ{„�Æÿÿïÿ��cïÿ9��ÿ‰�Ÿÿ�Æ���{…Þ…�…ÞÎ1„�R”’ÿ ï9)¥ÿ÷÷ÿ��)¥ÿŠ�RŒï‹ÿÞk�ƇÿÖ1„�Z”†Þ…�†Þ­ŒZ‚�1ŒÞ÷‰ÿbÿ µœ¥ÿÿï½RJsïÿŠ�9Œ‹ÿÎ!�Æ…ÿÎ91ƒ�Z¥Æ†Þ…�†ÞÖÆ”ƒ�JœÖïˆÿÞ{Æ÷€ÿ çÖ½ÿÿ樓Œ¥‚ÿ‹�R‚Þ�÷ƒÿÞΔ��Æ„ÿΔ…�”ÆÖ†Þ…�‰Þ†�{½ˆÿÆ�Æ…ÿï¥ï„ÿŒ�kcckcÞƒÿk!€��Æ„ÿ�9ƒ��{‰Þ…�ŠÞ”Z„�)¥‡ÿÞk�ÆÿÿÞ÷‚ÿ�ïƒÿÞc�Þ÷ƒÿ�ƒ��Z”Ήޅ�ŠÞέZƒ�)½ï…ÿÞc�µ÷ÿ÷„ÿ÷ÿΔ›�1Æ÷ÿ­{9…�Z¥ÆŠÞ…�‹ÞΔ…�{Îï„ÿÖc�„Æÿÿ€÷„ÿ÷Þ¥sš�)1k‚ÿÞs9…�”ÆÖŠÞ…�Þ€��‚�{½ÿÿ÷÷ÿÿ÷Îk��9ïÿ‚ï€ÿckRš�¥Þ÷ÿïk!‚���{ÎŒÞ�„�ÞÎ1‡��ï€ÿïÿÞc�ïÿÿï€ÿ�ïÿŒ��‹�)¥ƒÿÞc†�Z”ŽÞ…�ŽÞ­s†�9Æ÷€ÿÆ�9Æ÷‚ÿÞ¥{9Š�99‹�Z”ï€ÿ÷ŒR1…�Z¥ÆŽÞ…�ŽÞÖ­1†�„½ÞÿÿÖB‚�„½‚Þœc9‹�kkŠ� )”ÎÿÿçÖµJ…�”ÆÖŽÞ…�‘Þ€��ƒ�kï€ÿƒ�ck€cŽ�µµ‰�¥ï€ÿ”Rˆ�{‘Þ€���“ÞÆ{„��R”ÿÿ9•�1ÎÿÞcŠ��¥“Þ…�”Þ�Ɔ�9Œçs9“�Zœ÷Œÿ÷ŒR1‰�¥Ö“Þ…�•Þ11!„�J¥sJ’�)”΋ÿçεJ†���1•Þ…�–ÞÖœ��‚�JJ���¥ïŒÿŒJ†�����1–Þ…�•ÞΈ��Ž�JŒÿïÿ÷÷ƒÿ½{Ž�ÞÖÖ–Þ…�•ÞÎÖÞÞRZB”�1ÆÆÖç„ÿ÷ç½Öµ99)‹�BZZ€Þ�Ö•Þ…�•Þ�Ö€ÞŒ”s‘�19k†ÿÞÞÎ¥{œ{Œ�1{””€Þ�Ö•Þ�„��ΚÞÆ{�Ö÷‡ÿkcJ�!{ΜÞ{��€�{‡�{Ž��‰�Î1��‡��ˆ��ª���ˆ��œ�1kŒÆÆk9÷�ŒÎÿÿ¥c«�™����€���‘���†�ÆÿïïÖœ�€��…�ZZ‰��€��…�Å��Ë��¤��…�ÆBª��›��¤��…�ÿ„Bª��‡��†���Ž�� �ÿÿÆ�ž��‰�‚��“����’�R”ÿ…�…ÿ�‡��†�‹�…�����‰�R)9­ÆÖΔ‚�B½…ÿÆ1����‚� ����”������†� )1)”c{ïÿÿÖR�)J„‡ÿk1���‚� ����”��€��˜��€���¥Î¥ƒÿ�Æ‚�¥ï‰ÿ�Æ�·�‹�R”†ÿ÷9��‘ÿ÷9Ç�†�R¥€ÆÖç†ÿ9��½Öçÿέ{R€�����™�ƒ�!1)”ç‹ÿ�{œ½ÿ ï½s!�����¢�œÆ¥ÿ‚�J’ÿs11s€��¡�� �‘ÿ��Æÿÿ÷ÿ9s‰ÿ�R”÷ÿ9ƒ�ÿ9ƒ�•��•���s¥Æ‹ÿ甽œkRRÆ÷„ÿΔçˆÿ�JÞÿR‚�ÿΔ†����€��•��€�)­ç‹ÿÎ¥R{œ{ss†ÿïµçˆÿ��)¥ÞZ)‚�ÿÿÎ9…����’�����¥ïŒÿ�9„�Jcs†ÿ½œs‰ÿ��!ssR‚��ç€ÿ„��Œ��‚�“���R”Žÿ€��‰��÷ÿççŠÿÞ1„�R”‚ÿ�„����Œ�†�R½çŽÿcŒÆR€�‚�9µçŽÿ÷ÎŒ‚�R½ç‚ÿÆ9ƒ�Š��€��Œ�„�)”çÿ JœÎÿ”J)��ƒ�{½ÿÎ9�”ç„ÿ{9ƒ�Š�����‚�B¥–ÿÞœ‡�9÷‘ÿ�‡ÿÆ„��š�†�Æœÿ�9‡�¢ÿ�9��ƒ��…��€ƒ�1ÆÆ÷ƒÿ½½œ9”ÖïÿÎÆÆ„�¢ÿÆ„‚����…���‚�1kƒÿ ÖÎÞ{{Z�R”½÷‘ÿ�„�¢ÿ÷Æ9��˜��…��Æ„ÿJ)sƒ�1Αÿ…��ç¤ÿ„��€��‰��ƒ��€���)¥ÿ�÷€ÿ…��€�‘ÿ…�¦ÿ�9ƒ���Ž�„�1΃ÿÎ9…�Æ‘ÿ…�¦ÿJƒ��ƒ�Jç‚ÿΔ†�Þ÷“ÿ…�¦ÿ{9‚���ƒ�R”ƒÿ�9‚���sÞ“ÿ…�§ÿÎ!‚���‰��†�Æÿÿ÷€ÿ€��Ç��ÆÿÞs…��ˆ��€��„�RÖÿÎ9ˆ��™��¥�B„€ÿ÷Þ„��†��€��„�”ç€ÿΔ‰��™��¥�R‚ÿ„��Ž��€�‚ÿ�9†��Ç�9÷ÿ€��Ž�‚��)¥ÿÕ��÷€ÿÞ1„�‡�ƒ� Z”ïÿÿÖJ��Ì�9Îÿÿ÷µs�‚� 1œÎÿÿÖ”��Í�ŒÎ÷ÿ祋��‚�Î÷€ÿJØ�1ÞÿÿÆ€��€��…��„�ÿ‹�1Ιÿ…�›ÿÞs‰�çÿÞs‘��!œÿ…�R))RR¥Î÷™ÿ…�›ÿ÷ÞR†�9ÆïÞR†��…��1Æÿƒ�1”cc””ç›ÿ…�ÿ”ZB1†�ŒÎÿ”!†��„�)¥ÿ‚�9΀ÿ�÷ÿ�„�Ÿÿ÷Î���Zÿÿ9��‡��ƒ�1Îÿ‚��ÆŠÿ�÷…ÿ�÷€ÿ‘�ÿ”R‹�Æÿ÷ÿ�÷‰ÿ�9…�)¥ÿ½{†��…�B{÷ÿ�RÖ‘ÿï½9Œ���ï”R‹�199½ïŒÿ Δ���)ÿï­†��…�J„‚ÿ�”çÿÞÆ­{���ï­„9�{ÎïŒÿÖR��€�Þ÷ÆŽ�Z‚ÿ�‘ÿs—�ÿŽ�{½ÿ�÷…�sÞÎ!€��‰�R”‚ÿ�€��çŽÿÞs�1Î!��€�†ÿ�9ˆ�Zÿ�9„�ÆÞsŽ�¥ç‚ÿÆ1��9ÎŒÿΔ†�RB€ÆÎ÷„�†ÿÎÆÆR)ƒ�1½ŽÿΔƒ�Æ÷ÞŽ��Æ„ÿZ!��ÆŒÿÆ„‡�9Z”ZZ„‚ÿ…�‰ÿ”cRJƒ�)¥ÿÆ‚�Æÿÿ‰����Æ„ÿ½„�ÆŒÿÞœ‡��Ƈÿ…�ÿƒ�)¥ÿÞs‚�Æÿÿ�šÿ�9‡�‰ÿ…�ÿ÷9ƒ�ÿÎ!‚�Æÿÿ‹��šÿ�9…�!œ‰ÿ…�Žÿ�9ƒ�ÿï΂�ÆÿÿÆ1�‡�Bšÿ�9…�1Ɖÿ…�Žÿ�9ƒ�‘ÿ�1΀ÿ9�‡�1Öšÿ�9‚���)¥‰ÿ…�Žÿ�9ƒ�‘ÿ�Î÷€ÿ�9‰�)¥šÿ�9…�)¥‰ÿ…�Žÿ�9ƒ�‘ÿ�‚ÿ�9€�ƒ�B{ï™ÿï9…�1Ɖÿ…�Žÿ�9ƒ�‘ÿ��!œ‚ÿµs‡�J„šÿïR„�1Ɖÿ…�Žÿ�9ƒ�‘ÿ��1Þ‚ÿÖ”‡�Z›ÿ”R�‚�)¥‰ÿ…�Žÿ�9ƒ�‘ÿ��9ƒÿ½{‡��Æœÿ½{���1Ήÿ…�Žÿ�9ƒ�‘ÿ�Æ…ÿÎ!…��Æœÿ Þœ����J‰ÿ…�ÿ½)ƒ�‘ÿÆ÷…ÿÖZ…�)Μÿ ïµ���Þ÷‡ÿ…�ÿ{‚��›ÿ猂��¥ïÿÞs„�sÞ‚ÿ�÷ÿ…�ÿ��s›ÿ÷Î�‚�•ÿ�÷ƒÿ÷ÿÿÎ!‰��ç„ÿ…�‰ÿ÷9…�1Îÿ…�Ÿÿç„��„�99R”½Öçÿ„�ƒÿ÷Þ½sRJ9…�1Îÿ…�Ÿÿï½)��†�R{œµÞ„�ÿÞνœ{1‡�9Þ‘ÿ�÷ˆÿ…��÷ ÿ¥)�s…�ÿs1��‡�Z’ÿ�÷ˆÿ…�•ÿ‚��Ö÷€ÿ½{–�ÿ�Š�ÿ�÷œÿ…�“ÿ ÖJ{œœ{œ„Œ¥1΀ÿïÎ{”�ÿˆ���{¡ÿ…�“ÿ Æ¥­­¥µ­µÞ�Æÿï½Z1)1‘�ÿˆ�1)J½ÿ�ïœÿ…�“ÿ Î!¥BB¥sœµÞ�Æ„ÿ½µÖ‘�ÿ‰�έ½÷ÿ¥ï›ÿ†�Æÿ÷ÿÆ¥„”ÿÞÞçÿ�Æÿÿ÷…ÿÞœŽ�ÿ†�R”ÿ÷‚ÿ÷Î�Æ›ÿ†��Æ‘ÿ ÖZ„s”çÞ÷÷ç�ƈÿ÷Þœ�ÿ…�R½çƒÿµ{1�1Z½™ÿ†�­ïÿ ÖZ„Œ¥çÞ÷÷ç�Ɖÿ÷Î�ÿ…�kÞ„ÿœc�Jµÿ�ï“ÿ�ï†�{½ÿ Î!¥ÆÖçÞÞç÷�Ɖÿ÷Î�ÿ…�cÞ„ÿ½œs�{µÖÿ¥ï‘ÿ牢�Zÿ Æ¥„”ÿ¥¥­Î�ƈÿ½{Ž�ÿ†�R”‚ÿ÷ÿ÷Î�Æÿï¥�Æ‘ÿÎ!†�J„ÿ ÆÆ¥µÿ„JZÖ�Ƈÿ½JŽ�ÿR„�)Z„ÿ÷Æ÷€ÿµ{)�1Z½ÿƆ�R”Šÿ÷çç€ÿ ÖB¥Œ”Æs)9­)Άÿï{�ÿ”!†�Þ÷ˆÿ¥k�JµŽÿ﵆�R”÷‰ÿÖŒ”ÿ÷ÿ÷Ö!!s1!!¥ï…ÿ牢�†�‚ÿ�9‡�sÞˆÿÞµs�{µÖŽÿ½{��ƒ�)¥‰ÿÎ9J‚ÿ„��‡ÿÎ!€��„��…�‚ÿ½{��ƒ�Zˆÿ ÷Î�Æÿÿ÷ÿÎ!Î÷ˆÿ�9‰�)¥‰ÿÞ9B‚ÿ…Ɔÿ„B���BsÆ„�‚ÿïÎ{‚�€�Z‚ÿ÷÷ÿ÷Æ÷ÿÆÖ÷ˆÿ�9‰�)­‰ÿç99ï‘ÿÞJ��€�){µÿ„�ƒÿï½1€��Îçïÿÿ÷÷ˆÿÆÖ÷‡ÿÎ1‰�1ΊÿJ1¥ÿïïŽÿs)†�¥ïÿÿ…�ÿ�÷€ÿÎ1‡�!„½ŒÿÎ!Ö÷‡ÿ!‹�ŠÿÞœ�÷€ÿ�÷ˆÿÖ÷Î!†�R”‚ÿ…�†ÿ½{‹��ƈÿÞœ�‡ÿÎ!��„��€�ŠÿÆ„�9œÎ„ÿ ï½½Öµ9191…�R½ç‚ÿ…�†ÿïÎ{Š��1€9 µÖ½½ïÿÿ½”k�‡ÿÖZ‰��€�Î÷ˆÿÎŒ��ZŒÆÞÎÎÞÎÆµ{{œ{‰�”çƒÿ…�‡ÿï½)Œ� s”{{ÆÖ΄cB�†ÿ÷ÎRŠ��!Ήÿ�Æ�s11s!Š��s…ÿ…�ÿ�÷„ÿ¥)‚��Š�RZ!€��÷…ÿç¥����†��ƉÿÞs–�Zÿ�÷ÿ…��÷‰ÿÞœ‘���s†ÿ½{��­ïˆÿ÷ÞR��R­Ö†ÿ…�Šÿ÷Ö{“�1Þ†ÿZ�„ÆŠÿ”!��”ç‡ÿ…�‹ÿï½’�1k†ÿÆ1‹���Z‹ÿ�9Š��…�‰ÿ…�ÿ€��€��Š��Ƈÿ“�1Ίÿ½{‹�€�1Ήÿ…�ÿÖ1‹��€�…ÿ÷ÿÞs“�!ŒŠÿïÎ{R†���Zœï‰ÿ…�ÿ÷Δ‡�€�)½‡ÿÆ‚�Œ�RÞ÷‰ÿï½”ZB1…��€�1œÎŠÿ…�ÿÎ9†��)1c‡ÿï­ƒ�‡�‚�sÞÿ÷‹ÿ÷Ή�Î÷‹ÿ…�ÿ�ç‰�¥Îç‡ÿ½{‰����…�R”žÿ�÷ˆÿ…��÷©ÿ�9‚��€��†�ƒ�Zªÿ…�©ÿ½)„�‡�ƒ�9Æÿ�ހ΅ÿ…�©ÿ{„�ƒ���€…�ÿs1!!…ÿ…�©ÿ€����‰��…�€ÿ÷÷ÿÿ÷ÿ�÷ÿ½{ƒ�Z‚ÿ…�€ÿ�÷£ÿÞs��€��‰���€�9µç—ÿZ„�9Þÿ…�§ÿÆ„��€��†����{½–ÿÎ1…�!œÿ…�Þ÷¤ÿï­…��€��…��€��…�Z’ÿ�÷€ÿ!‡�ÿ‚��sÞÿ÷¢ÿ½{ƒ��‰� ���€�)¥•ÿ��R”ÿ�ÿ†��Ƥÿ�9ƒ��ƒ��‘��)½ï“ÿ�­çÿR��ÿ†�1k¡ÿç9Ÿ��€�{Þ“ÿ€�ZÖÿÿ”!��ÿ‡�1ÆŸÿï¥��…�Æÿÿ÷ÿ��s‚ÿ9��ÿ‚�‚�Ÿÿ�Æ��€��•�����Z’ÿ÷9)¥ÿ��)¥ÿŠ�ZŒÿÎ!�ƇÿÖ1„�—�ƒ�)”Þ÷‰ÿbÿ µœ¥ÿÿï½J{ïÿŠ�!Z‹ÿÆ�Æ…ÿÎ91ƒ�—�ƒ�RœÖïˆÿÞ{Æ÷€ÿ çÖ½ÿÿï¥Z„µ‚ÿŠ�ÎÖÖÎÞ÷ƒÿÎÆ”�Æ„ÿΔ†�£�{½ˆÿÆ�Æ…ÿ聾ÿŒ�!ZZ!sÞƒÿ!��Æ„ÿ�9ƒ���ž�„�)¥‡ÿÎ!�ÆÿÿçŠÿÞs��ç„ÿ�ƒ������€�)½ï…ÿÖZ�Æÿ÷ç†ÿΔ›�1Æ‚ÿ„R9…�Ž����‚�{Îï„ÿÖZ�”΀ÿ÷ç…ÿÞœcš�)9k‚ÿÎJ†�¥��‚�{½„ÿÎ!��9÷ÿ÷÷ÿÿ÷€ÿs1‹�Š�¥çƒÿ!��€�Œ��”��ˆ�Þ÷ÿÞs�÷ÿÿ÷€ÿ�÷ÿŒ��†�€�)¥ƒÿÞs†�§�†�1½ï€ÿÎ)�9΃ÿÞ{R9…���‹�Z”ïÿ”R1€��§�‡�{µÎÿÿÖJ‚� ŒÆÞÎÖÞÞœ9†���BBŠ� )”ÎÿÿÖÆ½R‚��“�•��ƒ�!ÿƒ�s!Zss�‹�ÆÆ‰�¥ï€ÿZƒ���”��˜��„��Zÿÿ9•�1ÎÿÞs‚�Ã�1”çJ“�Zœ÷ÿ”R1€��€��Â�R¥R)’�)”΋ÿçÖ½R‚��€��º���‚�RR���¥ïŒÿ”R†���¹�ˆ��‹�R”‹ÿ½{�¶���Ž��‚�1ÆÆÖç„ÿ÷ç½Öµ99)†���€��µ��€�Ž��€�19k†ÿÎÖÎ¥{œ{Š���€€����›��Ö÷‡ÿ!ZR�œ��€��€��ˆ����‰�Î1��†��ˆ���£��‘��†�ƒ�‚�1kŒÆÆB“�ª���‘��†�ƒ�ƒ�ŒÎÿÿ{9“�ª��ƒ����€���‘���†�Æÿ÷ÿÞœ�€��…�‰��€��…�Å��Ë��¤��…�ÆJª��›��¤��…�ÿŒJª��‡��†���Ž�� �ÿÿÆ�ž��‰�‚�!‘)!!!)€’�R”ÿ…�…ÿ�‡���‚)‹)„)!„��!“)!!!)!��‰�R1B¥ÆÖΔ‚�Jµ…ÿÆ1‚��‚� �!!)!ˆ)!!…)�!…�•)!)!��†� )1)”k„çÿÿÖR�)R„÷†ÿk1��‚� ���!!)!“)��€�•)!))� ¥Î¥ÿÿ÷÷ÿÿÆ‚�¥ç‰ÿ�Æ�–)…�–)‹�R”†ÿï9��‘ÿï9�•)…�•)‡�R¥€ÆÖçÿ�÷ÿ 9��½Öçÿÿ÷÷‹ÿέ{R€���‚�!“)€���•)�…�!1)”ç†ÿ�÷ÿ�{œ½ÿÿ÷÷Œÿ ï½s!���ƒ�!’)€���•)†�œÆ¥‰ÿ�÷€ÿ‚�RŽÿ�÷€ÿsBBs€��‡��’)��€�‘)‰�‘ÿ��Æÿÿïÿ9s…ÿ�÷€ÿ�R”ïÿ9ƒ�ÿ9ƒ�€Ž)„��)!†�s¥Æ‹ÿ甽¥kRRÆ÷„ÿΔçˆÿ�JÖÿZ‚�ÿΔƒ�!)!‹)€��€��Ž)!†�)­ç‹ÿÎ¥R{œ„ss†ÿïµçˆÿ��)¥Þk1‚�÷ÿÎ9‚�!!!Š)�!€���Ž)�����¥ïÿÿ÷‰ÿ�9„�c{s†ÿ½œs‰ÿ��1ssR‚�Þ÷ÿÿ„��‹)!‚��)‚���R”‚ÿ�÷ˆÿ€��‰��ïÿ÷9„�R”‚ÿ�„�!‹)���‰)…�R½çŽÿcŒÆR€�‚�9µçÿÆ„‚�R½ç‚ÿÆ9ƒ�Š)�€��€�ˆ)!ƒ�)”çÿ R¥Îÿ”J)��ƒ�{µÿ÷Æ9�”ç„ÿ{9ƒ�!‰)�„�‡)!†�!J¥–ÿÞ¥‡�9ï‘ÿ�‡ÿÆ„��‰)…�†)†�Æœÿ�9‡�¢ÿ�9ƒ�‰)�ƒ��…)…�1ÆÆïƒÿ½½œ9”çÿÎÆÆ„�¢ÿ½{‚�!‡)„��…)�…�1kƒÿ ÖÎÞ{{Z�R¥Î÷‘ÿ�„��÷¡ÿï½9‚�!!€)�!)…�…)†��Æ„ÿR)sƒ�1Αÿ…�Þ÷ÿ÷÷Žÿ„�))!)…�!)!€���)¥ÿ�ï€ÿ…��€��÷Œÿ�÷€ÿ…�ÿ�÷¡ÿ�9ƒ�!!)…��!)„�1΃ÿÎ9…�Æ‘ÿ…�€ÿ�÷¢ÿJƒ�!)…�€)!)ƒ�Jç‚ÿΔ†�Þ÷“ÿ…�€ÿ�÷¢ÿ{B‚�))!!)…�))!)ƒ�R”ÿÿ÷÷ÿÿ9‚���sÞ“ÿ…�§ÿÎ1‚�)))…�))!)„�Æÿÿï€ÿ€��Ç��ÆÿÞsƒ�)!))…�!)!„�RÖÿ÷ÿÿÎ9ˆ��™��¥�JŒ€ÿ÷Þ„�!)…�!))…�”çÿ÷ÿΔ‰��™��¥�Z÷ÿ…�))…�)��€�‚ÿ�9†��Ç�9ïÿ€���)…�)‚��)¥ÿÕ��ï€ÿ÷9„�)…�)ƒ� Z”ïÿÿÖR�€�!Ì�9Æ÷ÿÿµs„�)…�!‚� 1œÎÿÿÖœ�€�!Í�„Æÿÿ祄�!‚��„�Î÷€ÿRØ�9÷ÿÿÆ€��€���ÿ‹�1Ιÿ…�›ÿÞs‰�Þ÷Þs‘��!œÿ…�R11RR¥Î÷™ÿ…�›ÿ÷ÞR!†�1½çÞR†��…��1Æÿƒ�1”sk””ç›ÿ…�ÿ”cJ1†�„Æÿ”!†��„�)¥ÿ‚�B΀ÿ�ïÿ�„�Ÿÿ÷Î���!cÿÿ9��‡��ƒ�1Îÿ‚��ÆŠÿ�ïÿ�÷€ÿ�ï€ÿ‘�ÿ”R‹�Æÿïÿ�ï‰ÿ�9…�)¥ÿ½{†��…�B{÷ÿ�RÖŽÿ÷ÿÿï½9Œ���ç”R‹�199½ïŒÿ Μ!���)ÿï­†��…�JŒ‚ÿ�”çŽÿ÷ÞÆ­{���ç­„9�{ÎïŒÿÞc��€�Þ÷ÆŽ�!c‚ÿ�‘ÿs—�ÿŽ�{½ÿ÷‹ÿ�ï…�sÞÎ1€��‰�R”‚ÿ�€�Þ÷ÿÞs�1Î1��€�†ÿ�9ˆ�!c‹ÿ€÷ÿ9„�ÆÞsŽ�¥çÿ÷€ÿÆ1��1ÎŒÿΔ†�!R!!J€½Î÷„�†ÿÎÆÆR1!‚�1½‰ÿ�÷ÿΔƒ�Æ÷ÞŽ�Æÿÿ÷ÿZ!��ÆŒÿÆ„‡�9c”cc„÷ÿ…�‰ÿ”sZRƒ�)¥‰ÿ�÷‚ÿÆ‚�Æÿÿ‰����Æ„ÿ½„�ÆŒÿÞœ‡�Æÿÿ÷ÿ…�‚ÿ÷÷†ÿƒ�)¥ÿÞs‚�Æÿÿ�ÿ�÷•ÿ�9‡�‰ÿ…�ÿï9ƒ�ÿÎ1‚�Æÿÿ‹��šÿ�9…�!œ‰ÿ…�Žÿ�9ƒ��÷ÿ÷ï΂�ÆÿÿÆ1�‡�Bšÿ�9…�1Ɖÿ…�Žÿ�9ƒ��÷ÿ÷÷ÿ�1΀ÿ9�‡�1Öšÿ�9‚���)¥‰ÿ…�Žÿ�9ƒ�‘ÿ�Î÷€ÿ�9‰�)¥šÿ�9…�)¥‰ÿ…�Žÿ�9ƒ�‘ÿ�‚ÿ�9€�ƒ�B{ï™ÿç9…�1Ɖÿ…�Žÿ�9ƒ�‘ÿ��!œ‚ÿµs‡�JŒšÿçJ„�1Ɖÿ…�Žÿ�9ƒ�‘ÿ��1Þ‚ÿÖ”‡�!c›ÿ”R�‚�)¥‰ÿ…�Žÿ�9ƒ�‘ÿ��9ƒÿ½{‡��Æœÿ½{���1Ήÿ…�Šÿ÷÷ÿÿ9ƒ�‘ÿ�Æ…ÿÎ1…��Æœÿ ï­����!Z‰ÿ…�ÿ½)ƒ�‘ÿÆ÷…ÿÞc…�)Îÿ Æ���)Þ÷‡ÿ…�ÿ{‚��›ÿ猂��¥ïÿÞs„�sÞ‚ÿ�ïÿ…�ÿ��s›ÿ÷Î�‚�•ÿ�ïƒÿïÿÿÆ1‰�Þ÷ƒÿ…�‰ÿï9…�1Îÿ…�™ÿ�÷€ÿ÷ÿçŒ��„�19R”½Öçÿ„�ƒÿ÷Þ½{ZR9…�1Αÿ�÷ˆÿ…�™ÿ�÷€ÿ÷ÿï½)��†�R{œµÞ„�ÿÞÖ½œ{B!†�9Þ‘ÿ�ïˆÿ…��ï ÿ¥)�s…�ÿsB!��‡�!c’ÿ�ïˆÿ…�•ÿ‚��Ö÷€ÿ½{–�ÿ�Š�ÿ�ïœÿ…��÷’ÿ ÖR{œœ{œŒŒ¥1΀ÿïÎ{!“�ÿˆ���{¡ÿ…��÷’ÿ Æ¥­­¥µ­µÞ�Æÿï½c1)1‘�ÿˆ�1)Jµÿ�ïœÿ…�“ÿ Î1¥JJ¥sœ½÷�Æ€ÿ÷÷ÿ÷½µÖ‘�ÿ‰�έµïÿ¥ï›ÿ†�Æÿï÷ŽÿÆ¥„”‚÷�Æÿÿï…ÿÞœŽ�ÿ†�R”ÿï‚ÿ÷Î�Æ›ÿ†��Æ‘ÿÖZ„s”‚÷�ƈÿ÷Þœ�ÿ…�R½çƒÿ µ{1�1Z½ÿ÷÷–ÿ†�­ïÿÖc„Œ¥‚÷�Ɖÿ÷Î�ÿ…�{Þ„ÿ œc�Jµÿ÷÷ÿï“ÿ�ï†�{½ÿÆ1¥ÆÖÞ€÷ï�Ɔÿ÷ÿÿ÷Î�ÿ…�„ç„ÿ½œs�{µÖÿ¥ï‘ÿ牢�!c÷ÿ Æ¥„”÷¥¥­Î�ƈÿ½{Ž�ÿ†� R”ÿÿ÷÷ÿïÿ÷Î�Æÿï¥�Æ‘ÿÎ1†�JŒÿ÷ÿÿÆÆ¥µÿ„JcÖ�Ƈÿ½JŽ�ÿR„�)c„ÿ÷Æ÷€ÿµ{)�1Z½ÿƆ�R”Šÿ÷çç÷ÿÿÖB¥Œ”Æs1B­)Άÿï{�ÿ”!…�!Þ÷ˆÿ¥k�JµŽÿ﵆�R”ï‰ÿÖŒ”ÿïÿ÷Ö11)sB11¥ç…ÿ牢�†�‚ÿ�9‡�sÞˆÿÞµs�{µÖŽÿ½{��ƒ�)¥‰ÿÎBR‚ÿ„�÷†ÿÆ1€��„��…�‚ÿ½{��ƒ�!cÿ�÷€ÿ÷ÿÿ÷Î�Æÿÿï÷Æ1Î÷ˆÿ�9‰�)¥‰ÿïBB‚ÿ…ƆÿŒR���BsÆ„�‚ÿïÎ{‚���!c‚ÿïïÿ÷Æ÷ÿÆÖ÷ˆÿ�9‰�)­ŠÿB9ï‘ÿÞZ!��€�){µÿ„�ƒÿï½1€�€�!ÆçïÿÿïïˆÿÆÖ÷‡ÿÎ1‰�1ΊÿR9¥ÿçç‡ÿ÷÷‚ÿs)†�¥ïÿÿ…�ÿ�ï€ÿÎ1‡�1Œ½ŒÿÆ1Ö÷‡ÿ1‹�€ÿ�÷†ÿÞœ�ï€ÿ�ïˆÿÖ÷Î1†�R”‚ÿ…�†ÿ½{‹��ƈÿÞœ�‡ÿÎ1��ˆ�ŠÿÆ„�9œÎ„ÿ ï½½Öµ9191…�R½ç‚ÿ…�†ÿïÎ{Š��1€9 µÖ½½ïÿÿ½”k�‡ÿÞc�Î÷ˆÿÎŒ��ZŒÆÞÖÖÞÎε{{œ{‰�”çƒÿ…�‡ÿï½)Œ� s”{{ÆÞ΄cB�†ÿ÷ÎRŠ��1Ήÿ�Æ�sBBs1Š��s…ÿ…�ÿ�ï„ÿ¥)‚��Š�Rc1€��ï…ÿç¥����†��ƉÿÞs–�!cÿ�ïÿ…��ï‰ÿÞœ‘���s†ÿ½{���½ˆÿ÷ïÞR��R­Þ†ÿ…�Šÿ÷Ö{“�1Þ†ÿZ�”Ö‡ÿ÷÷ÿ”!��”ç‡ÿ…�‹ÿï½’�1k†ÿÆ1‹���!c‹ÿ�9Š��…�‰ÿ…�ÿ€��€��Š��Æ€ÿ÷÷‚ÿ“�1Ίÿ½{‹�€�1Ήÿ…�ÿÖ1‹��€�…ÿïÿÞs“�!ŒŠÿïÎ{R!†�€�Z”ç‰ÿ…�ÿ÷Δ‡�€�)µ‡ÿÆ‚�Œ�RÞ÷‰ÿï½”cJ1…��€�1œÎ÷‰ÿ…�ÿÎ1†��)1c÷†ÿï­ƒ�Ž�sÞÿï†ÿ÷÷€ÿ÷Ή�Î÷‹ÿ…�ÿ÷Þ‰�¥Îç‡ÿ½{‰����…�R”†ÿ�÷”ÿ�ïˆÿ…��ï©ÿ�9‚��€��…�!!ƒ�!c‡ÿ÷÷žÿ…�©ÿ½)„�!…�))!ƒ�9Ɔÿ÷÷’ÿÞÖÎÎ…ÿ…�©ÿ{„�!)‚��)€…�œÿ÷sB11…ÿ…�©ÿ€����!))…�))†�€ÿïïÿÿïÿïÿÿ÷ÿ½{ƒ�!c÷ÿ…�€ÿ�ï£ÿÞs��€�!))…�)��€�9µçÿ€÷“ÿZƒ�9Þÿ…�§ÿÆ„�!)!…��!€���„Æÿÿ÷÷’ÿÎ1…�!œÿ…�Þ÷¤ÿï­…�!)!…�!))…�!c’ÿï÷ÿÿ1‡�ÿ‚��sÞÿï¢ÿ½{„�)!))…�)))€�€�)¥€ÿ�÷‘ÿ��R”ÿ�ÿ†��Ƥÿ�9„��)!…�)!!))!‚��)½ï“ÿ�!­ÞÿR��ÿ†�1k¡ÿç9ƒ�!‚)�!…�ƒ)!��€�{Þ“ÿ€�cÖÿÿ”!��ÿ‡�1ÆŸÿ聾�…)…�…)†�Æÿÿïÿ��s÷ÿ9��ÿ‚�‚�Ÿÿ�Æ���…)…�…)����!c’ÿï9)¥ÿ��)¥ÿŠ�!c÷‹ÿÎ1�ƇÿÖ1…��†)…�…)!ƒ�)”Þ÷‰ÿbÿ µœ¥ÿÿï½!J{ïÿŠ�)c‹ÿÆ�Æ…ÿÎ91„�†)…�†)ƒ�RœÖïˆÿÞ{Æ÷€ÿ çÖ½ÿÿï¥cŒµ‚ÿŠ�)ÎÞÞÎÞ÷ƒÿÎΜ�Æ„ÿΔ†�†)…�‰)†�{½ˆÿÆ�Æ…ÿï¥÷„ÿŒ�1cc1sÞƒÿ1��Æ„ÿ�9ƒ��€�‰)…�Š)�…�)¥‡ÿÎ1�ÆÿÿÞ÷‚ÿ�÷ƒÿÞs�Þ÷ƒÿ�ƒ��€��‰)��€�Š)!��€�)½ï…ÿÞc�½ÿÿ€÷‡ÿΔ›�1Æ÷ÿŒZ9†�!‰)��€�‹)!�‚�{Îï„ÿÖc�ŒÎÿÿ÷÷†ÿÞœcš�)1k‚ÿÎR!‡�Š)…�)€��‚�{½„ÿÆ1��9ïÿïï÷÷ï€ÿsB!‹�Š�¥Þ÷ÿ÷1���!‹)�„�)�ˆ��÷€ÿ÷ÿÞs�ïÿÿï€ÿ�ïÿŒ��†�€�)¥ƒÿÞs‡��Ž)…�)!†�9ÎÿÎ)�9Æ÷‚ÿÞ„Z9…���‹�Z”ï€ÿ÷”R1€�€��Ž)…�Ž)†�Œ½ÎÿÿÖR‚�„½Þ΀ÞœB!†���JJŠ� )”ÎÿÿÞÆµR��€�Ž)‚��‘)€��ƒ�1÷€ÿƒ�s1css�‹�½Æ‰�¥ï€ÿc!ƒ�‚�‘)€���“)�…��!cÿÿ9•�1ÎÿÞs‚�ƒ��“)…�“)!†�9”çR“�Zœ÷Œÿ÷”R1€����!“)…�•)�…�!R¥Z1’�)”΋ÿçÖµR‚��€��€��•)…�–)��‚�RR���¥ïŒÿ”R†�����–)…�•)ˆ��‹�R”ÿ÷ÿ÷÷ƒÿ½{Ž�)–)…�•)�!���‚�1ÆÆÖç„ÿ÷ç½Öµ99)†��„�)!!•)…��!”)!€)Ž��€�19k†ÿÎÞÎ¥{œ{Š�€�€))!•)…�!™)��Ö÷‡ÿ1cR��!›)��Ÿ��‰�Î1��†��ˆ���£��‘��†�ƒ�‚�1kŒÆÆJ“�ª�€�‘��†�ƒ�ƒ�ŒÎÿÿ„B“�ª��ƒ����€���‘���†�Æÿï÷Þœ�€��“��€��…�t8mk��@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/makefile.bcc���������������������������������������������������������������������������0000644�0000000�0000000�00000010237�10250356275�014365� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������################################################################### # # makefile.bcc - Angband makefile for Borland C++ 5.5 (Win32) # ################################################################### ################################################################### # # Borland specific directives --- # .AUTODEPEND ################################################################### # # Set tool and version names: CC = bcc32 LINKER = ilink32 RC = brc32 ################################################################### # # Name of the *.exe-file EXE_FILE = zangband.exe ################################################################### # # Debug mode (un-comment for debugging) # DBGOPT= -v -N -x -xp ################################################################### # # Set any compiler options CCOPTS = -Hc -tW -lGn -e$(EXE_FILE) -w- \ -D_WIN32_WINNT=0x0400 -DWINVER=0x0400 # Compile flags: CCFLAGS= $(CCOPTS) $(DBGOPT) ######################## Targets ################################## LUAOBJS = \ lua\lapi.obj lua\ldebug.obj lua\lmem.obj lua\lstrlib.obj lua\lvm.obj \ lua\tolua_lb.obj lua\lauxlib.obj lua\ldo.obj lua\lobject.obj \ lua\ltable.obj lua\lzio.obj lua\tolua_rg.obj lua\lbaselib.obj \ lua\lfunc.obj lua\lparser.obj lua\ltests.obj lua\tolua_bd.obj \ lua\tolua_tm.obj lua\lcode.obj lua\lgc.obj lua\lstate.obj \ lua\ltm.obj lua\tolua_eh.obj lua\tolua_tt.obj lua\ldblib.obj \ lua\llex.obj lua\lstring.obj lua\lundump.obj lua\tolua_gp.obj TOLUAOBJS = \ lua\tolua.obj lua\tolualua.obj lua\liolib.obj \ $(LUAOBJS) OBJ = \ z-virt.obj \ z-util.obj \ z-term.obj \ z-rand.obj \ z-form.obj \ xtra2.obj \ xtra1.obj \ wizard2.obj \ wizard1.obj \ wild1.obj \ wild2.obj \ wild3.obj \ variable.obj \ util.obj \ tables.obj \ streams.obj \ store.obj \ spells3.obj \ spells2.obj \ spells1.obj \ scores.obj \ save.obj \ rooms.obj \ readdib.obj \ racial.obj \ quest.obj \ object2.obj \ object1.obj \ obj_kind.obj \ mutation.obj \ mspells2.obj \ mspells1.obj \ monster2.obj \ monster1.obj \ mind.obj \ melee2.obj \ melee1.obj \ main-win.obj \ maid-grf.obj \ notes.obj \ load.obj \ init2.obj \ init1.obj \ grid.obj \ generate.obj \ flavor.obj \ files.obj \ fields.obj \ effects.obj \ dungeon.obj \ cmd6.obj \ cmd5.obj \ cmd4.obj \ cmd3.obj \ cmd2.obj \ cmd1.obj \ cave.obj \ bldg.obj \ birth.obj \ avatar.obj \ artifact.obj \ script.obj \ ui.obj \ run.obj \ l-monst.obj l-object.obj l-player.obj l-random.obj l-ui.obj \ l-misc.obj l-spell.obj l-field.obj\ zbmagic1.obj zbmagic2.obj zbmagic3.obj \ zborg1.obj zborg2.obj zborg3.obj zborg4.obj zborg5.obj \ zborg6.obj zborg7.obj zborg8.obj zborg9.obj \ $(LUAOBJS) all : $(EXE_FILE) clean: -@if exist *.obj del *.obj >nul -@if exist *.exe del *.exe >nul -@if exist *.res del *.res >nul -@if exist *.tds del *.tds >nul -@if exist *.ilc del *.ilc >nul -@if exist *.ild del *.ild >nul -@if exist *.ilf del *.ilf >nul -@if exist *.ils del *.ils >nul install: $(EXE_FILE) copy $(EXE_FILE) .. ########################### Explicit Rules ######################## $(EXE_FILE): $(OBJ) angband.res $(LINKER) -aa -x $(OBJ) c0w32.obj, $(EXE_FILE),, cw32.lib import32.lib,, angband.res lua\tolua.exe: $(TOLUAOBJS) $(LINKER) -aa -x $(TOLUAOBJS) c0x32.obj, lua\tolua.exe,, cw32.lib import32.lib angband.res: angband.rc $(RC) -r angband.rc l-monst.c: lua\tolua.exe l-monst.pkg lua\tolua.exe -n monst -o l-monst.c l-monst.pkg l-object.c: lua\tolua.exe l-object.pkg lua\tolua.exe -n object -o l-object.c l-object.pkg l-field.c: lua\tolua.exe l-field.pkg lua\tolua.exe -n field -o l-field.c l-field.pkg l-player.c: lua\tolua.exe l-player.pkg lua\tolua.exe -n player -o l-player.c l-player.pkg l-random.c: lua\tolua.exe l-random.pkg lua\tolua.exe -n random -o l-random.c l-random.pkg l-ui.c: lua\tolua.exe l-ui.pkg lua\tolua.exe -n ui -o l-ui.c l-ui.pkg l-misc.c: lua\tolua.exe l-misc.pkg lua\tolua.exe -n misc -o l-misc.c l-misc.pkg l-spell.c: lua\tolua.exe l-spell.pkg lua\tolua.exe -n spell -o l-spell.c l-spell.pkg ########################### Implicit Rules ######################## .c.obj: $(CC) $(CCFLAGS) -c {$? } .obj.exe: $(CC) $(CCFLAGS) $< �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/makefile.ros���������������������������������������������������������������������������0000644�0000000�0000000�00000010161�10250356275�014435� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Project: Zangband # # Wimpslot: 5000k # Make: make # # To use this makefile, the current working directory must be where # this makefile is (if this makefile is at "raFS::Temp.$.ang.src.makefile", # the CWD must be "raFS::Temp.$.ang.src"). # # This makefile is for GNU make, and either GCC or Norcroft compilers, though # some fiddling is needed with the norcroft settings (e.g. StubsG location # and a little hacking of stdarg.h) # Defaults COMPILER = norcroft default: standard # Lua object files LUAOBJS := lapi.o ldebug.o lmem.o lstrlib.o lvm.o \ tolua_lb.o lauxlib.o ldo.o lobject.o ltable.o \ lzio.o tolua_rg.o lbaselib.o lfunc.o lparser.o \ tolua_bd.o tolua_tm.o lcode.o lgc.o \ lstate.o ltm.o tolua_eh.o tolua_tt.o ldblib.o \ llex.o lstring.o lundump.o tolua_gp.o # toLua object files TOLUAOBJS := tolua.o tolualua.o liolib.o $(LUAOBJS) # Lua package files: LUAPKGS := \ l-monst.c l-object.c l-player.c l-random.c l-ui.c l-misc.c l-spell.c # The standard object files: OBJS := variable.o tables.o util.o cave.o \ object1.o object2.o monster1.o monster2.o \ xtra1.o xtra2.o spells1.o spells2.o \ melee1.o melee2.o save.o files.o fields.o\ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o \ store.o birth.o load.o ui.o fields.o run.o \ wizard1.o wizard2.o grid.o streams.o rooms.o \ generate.o dungeon.o init1.o init2.o \ effects.o quest.o racial.o script.o \ artifact.o mutation.o flavor.o spells3.o \ mspells1.o mspells2.o scores.o mind.o maid-x11.o\ bldg.o obj_kind.o wild1.o wild2.o wild3.o avatar.o notes.o\ maid-grf.o main-ros.o \ z-util.o z-virt.o z-form.o z-rand.o z-term.o\ l-monst.o l-object.o l-player.o l-random.o l-ui.o l-misc.o\ l-spell.o l-field.o # Defaults for norcy ifeq (norcroft, $(COMPILER)) # Tools: CC := cc -D RISCOS LD := link RM := remove tolua := tolua # Libraries: LIBS := <CLibs$$Dir>.clib.o.StubsG <DeskLib$$Dir>.o.DeskLib CCFLAGS_BASIC := -apcs 3/32/fpe2/swst/fp/nofpr -c -Wan -DRISCOS SLUAOBJS := $(addprefix lua/,$(LUAOBJS)) STOLUAOBJS := $(addprefix lua/,$(TOLUAOBJS)) DLUAOBJS := $(addprefix lua.,$(LUAOBJS)) DTOLUAOBJS := $(addprefix lua.,$(TOLUAOBJS)) endif # Defaults for gcc ifeq (gcc, $(COMPILER)) WARNINGS := -ansi -pedantic -Wall -Wno-unused -Wno-long-long -W -Wcast-qual # Tools: CC := gcc -mlibscl -c -D RISCOS LD := gcc -mlibscl RM := remove tolua := tolua # Libraries: LIBS := <DeskLib$$Dir>.o.DeskLib WARNINGS := -ansi -pedantic -Wall -Wundef -Wpointer-arith \ -Wcast-align -Wwrite-strings -Wstrict-prototypes \ -Wmissing-prototypes -Wmissing-declarations -Wnested-externs \ -Winline -Wno-unused -Wno-long-long -W -Wcast-qual CCFLAGS_BASIC := -O2 $(WARNINGS) -mthrowback -mpoke-function-name -DRISCOS SLUAOBJS := $(addprefix lua/,$(LUAOBJS)) STOLUAOBJS := $(addprefix lua/,$(TOLUAOBJS)) DLUAOBJS := $(SLUAOBJS) DTOLUAOBJS := $(STOLUAOBJS) endif # # Rules to make the various targets # ALL_TARGETS := standard fullscreen #$(ALL_TARGETS): tolua standard: CCFLAGS := $(CCFLAGS_BASIC) standard: $(LUAPKGS) $(SLUAOBJS) $(OBJS) $(LIBS) $(LD) $(LDFLAGS) -o ^.!RunImage $(OBJS) $(DLUAOBJS) $(LIBS) fullscreen: CCFLAGS := -DFULLSCREEN_ONLY $(CCFLAGS_BASIC) fullscreen: $(LUAPKGS) $(OBJS) $(SLUAOBJS) $(LIBS) $(LD) $(LDFLAGS) -o ^.!RunImageF $(OBJS) $(DLUAOBJS) $(LIBS) # tolua tolua: $(STOLUAOBJS) $(LD) -o tolua $(DTOLUAOBJS) $(LDFLAGS) $(LIBS) # Lua packages l-monst.c: $(tolua) l-monst.pkg $(tolua) -n monster -o c.l-monst l-monst/pkg l-object.c: $(tolua) l-object.pkg $(tolua) -n object -o c.l-object l-object/pkg l-field.c: $(tolua) l-field.pkg $(tolua) -n field -o c.l-field l-field/pkg l-player.c: $(tolua) l-player.pkg $(tolua) -n player -o c.l-player l-player/pkg l-random.c: $(tolua) l-random.pkg $(tolua) -n random -o c.l-random l-random/pkg l-ui.c: $(tolua) l-ui.pkg $(tolua) -n ui -o c.l-ui l-ui/pkg l-misc.c: $(tolua) l-misc.pkg $(tolua) -n misc -o c.l-misc l-misc/pkg l-spell.c: $(tolua) l-spell.pkg $(tolua) -n spell -o c.l-spell l-spell/pkg # Suffix rules .SUFFIXES: .o .c # A basic rule .c.o:; $(CC) $(CCFLAGS) -o $@ $< # Dynamic dependencies: ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/makefile.std���������������������������������������������������������������������������0000755�0000000�0000000�00000051243�10250356275�014435� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# File: Makefile # # Note that you may have to make modifications below according # to your machine, available libraries, compilation options, # and your "visual module" of choice. This Makefile was originally # intended for use with Unix machines running X11, Curses, Ncurses, # or Vt100, or possibly for "Atari" or "Amiga" computers with # "Curses" ports, see below for more information. It was extended # work on most ports by Steven Fuerst. This makefile probably # requires a recent version of gnu make in order to work. # # # USAGE: Simply type in "make portname" where portname is the name # of your port. ie. "make openbsd". The default port can # be changed by editing the DEFAULT variable. # # # Note that "main-mac.c", the visual module for the Macintosh, # must be compiled in a special way, see elsewhere. # # Note that "main-win.c", the visual module for Windows, # might have to be compiled with another makefile (depending # upon which compiler you use.) # # Note that "main-lsl.c", the visual module for linux svga, # is depreciated. Do not use this unless you feel like making # a heap of new tiles. It also is a security problem, since # svgalib only works if you run as root. # # If you are able to construct "main-xxx.c" and/or "Makefile.xxx" # files for a currently unsupported system, please send them to me # (rr9@angband.org) for inclusion in future versions. # ## ## The list of ports available ## PORTS := default_port linux linux-svga linux-no-gtk openbsd freebsd main-cap \ vt100 solaris sgi dec isc next aix dos ibm borland gcu OTHER_PORTS := amiga cygwin emx lcc linux-tk freebsd-tk mingw osx ## ## This is the default port to compile. ## ## Change this to your favourite port if you compile the game often. ## DEFAULT := linux ## ## This is my compiler of choice, it seems to work most everywhere ## CC := gcc ## Generic compile flags used by most ports. CFLAGS := -Wall -O2 -g ## Variation # CFLAGS := -O1 -g ## ## Default Lua location ## LUA := lua/ ## ## ##### Zangband Files to compile ##### ## ## ## The wrapper auto-generated files ## LUAWOBJS := \ l-monst.o l-object.o l-player.o l-random.o l-ui.o \ l-misc.o l-spell.o l-field.o ## ## The Zangband source files ## ANGOBJS := $(LUAWOBJS) \ variable.o tables.o util.o cave.o \ object1.o object2.o monster1.o monster2.o \ xtra1.o xtra2.o spells1.o spells2.o \ melee1.o melee2.o save.o files.o fields.o\ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o \ store.o birth.o load.o ui.o\ wizard1.o wizard2.o grid.o streams.o rooms.o \ generate.o dungeon.o init1.o init2.o \ effects.o quest.o racial.o run.o script.o \ artifact.o mutation.o flavor.o spells3.o \ mspells1.o mspells2.o scores.o mind.o maid-x11.o\ bldg.o obj_kind.o wild1.o wild2.o wild3.o avatar.o notes.o\ main-cap.o main-gcu.o main-x11.o main-xaw.o main-xpj.o\ main-lsl.o main-vcs.o main-gtk.o main-win.o main.o \ maid-grf.o main-dos.o main-ibm.o main-emx.o \ main-ami.o main-tnb.o ## ## The "Utility" files ## ZUTILOBJS := z-util.o z-virt.o z-form.o z-rand.o z-term.o ## ## The Borg files ## BORGOBJS := \ zborg1.o zborg2.o zborg3.o zborg4.o zborg5.o \ zborg6.o zborg7.o zborg8.o zborg9.o \ zbmagic1.o zbmagic2.o zbmagic3.o ## ## The Tk port files ## TKOBJS := \ tk/cmdinfo.o tk/icon.o \ tk/describe.o tk/interp.o tk/plat.o \ tk/widget.o tk/tk-util.o tk/tcltk.o ## ## The "source" and "object" files. ## ## ## Lua Object Files ## LUAOBJS := $(addprefix $(LUA), \ lapi.o ldebug.o lmem.o lstrlib.o lvm.o \ tolua_lb.o lauxlib.o ldo.o lobject.o ltable.o \ lzio.o tolua_rg.o lbaselib.o lfunc.o lparser.o \ ltests.o tolua_bd.o tolua_tm.o lcode.o lgc.o \ lstate.o ltm.o tolua_eh.o tolua_tt.o ldblib.o \ llex.o lstring.o lundump.o tolua_gp.o) TOLUAOBJS := \ $(addprefix $(LUA), tolua.o tolualua.o liolib.o) $(LUAOBJS) ## ## Get the list of .c files from the .o files ## ANGSRCS := $(ANGOBJS:.o=.c) ZUTILSRCS := $(ZUTILOBJS:.o=.c) BORGSRCS := $(BORGOBJS:.o=.c) LUAWSRCS := $(LUAWOBJS:.o=.c) ## ## Main list of objects and source files. ## SRCS := $(ANGSRCS) $(ZUTILSRCS) $(BORGSRCS) OBJS = $(ANGOBJS) $(ZUTILOBJS) $(BORGOBJS) $(LUAOBJS) ## ## Select the default port to use. ## default_port: $(DEFAULT) ## ## (Installing directly is much nicer if you don't use the ## autoconf generated makefile system.) ## ## Do not call this directly - you need to specify a port. ## (This is why this isn't called "install".) ## install_game: zangband $(INSTALL) ## ## Build the "ZAngband" program ## MAKE-ZANGBAND = $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) zangband: $(OBJS) $(MAKE-ZANGBAND) ## ## Following are some "system" definitions ## ## No changes are needed to compile a version that will run on both ## X11 and Curses, in debugging mode, with maximal warnings, on many ## normal Unix machines of the Sun OS variety (non-solaris). ## ## ## See also "z-config.h" and "h-config.h" for important information. ## ## ## You may have to add various X11 include/library directories to the ## "CFLAGS", if your machine places files in a weird location. ## ## You may be able to remove "-ltermcap" on some machines (ex: Solaris). ## ## You may have to replace "-lcurses" with "-lncurses" to use the ## "new curses" library instead of the "old curses" library, and ## you may have to add "-l/usr/include/ncurses" to the "CFLAGS". ## ## See "main-gcu.c" and "z-config.h" for some optional "curses" defines, ## including "USE_GETCH" and "USE_CURS_SET". Note that "z-config.h" will ## attempt to "guess" at many of these flags based on your system. ## ## ## All 'Ports' depend on install_game so they get compiled. ## All 'Other-ports' must install themselves manually. ## $(PORTS): install_game ## ## Hack - default install action. ## (This can be overridden below.) ## INSTALL = cp zangband$(EXEC) .. ## ## Variation -- All the ports that work in Linux at once. ## (Use the -m option to start the one you want.) ## ## ## Graphics under X11 in linux. (Run with the -g option) ## Note: Get the 16x16.bmp file, and put in [Z]directory/lib/xtra/graf ## to get 256 colours. ## linux: CFLAGS += -D"USE_GCU" -D"USE_VCS" -D"USE_XPJ" -D"USE_X11" \ -D"USE_XAW" `gtk-config --cflags` -D"USE_GTK" \ -pedantic -W -Wmissing-prototypes -Wmissing-declarations \ -Wno-long-long -Wwrite-strings -Wpointer-arith \ -Wbad-function-cast -Wsign-compare -DHAVE_MKSTEMP\ -Waggregate-return -Wstrict-prototypes \ -Wredundant-decls #-Wunreachable-code linux: LIBS := -lX11 -lcurses -lncurses -L/usr/X11R6/lib \ -lXaw -lXext -lSM -lICE -lXmu -lXt -lgtk `gtk-config --libs` ## ## Variation -- All X11 ports for linux except gtk ## linux-no-gtk: CFLAGS += -D"USE_GCU" -D"USE_XPJ" -D"USE_X11" -D"USE_XAW" \ -pedantic -W -Wmissing-prototypes -Wmissing-declarations \ -Wno-long-long -Wwrite-strings -Wpointer-arith \ -Wbad-function-cast -Wsign-compare -DHAVE_MKSTEMP\ -Waggregate-return -Wstrict-prototypes \ -Wredundant-decls #-Wunreachable-code linux-no-gtk: LIBS := -lX11 -lcurses -lncurses -L/usr/X11R6/lib \ -lXaw -lXext -lSM -lICE -lXmu -lXt ## ## Variation -- tk port to linux ## linux-tk: CFLAGS += -D"USE_TNB" -D"USE_X11" -D"PLATFORM_X11"\ -I/usr/include/tcl8.4 \ -I/usr/include/tcl8.4/tk-private/unix \ -I/usr/include/tcl8.4/tk-private/generic \ -I/usr/include/tcl8.4/tcl-private/generic \ -pedantic -W -Wmissing-prototypes -Wmissing-declarations \ -Wno-long-long -Wwrite-strings -Wpointer-arith \ -Wbad-function-cast -Wsign-compare -DHAVE_MKSTEMP\ -Waggregate-return -Wstrict-prototypes \ -Wredundant-decls #-Wunreachable-code linux-tk: LIBS := -lX11 -lcurses -lncurses -L/usr/X11R6/lib \ -lXaw -lXext -lSM -lICE -lXmu -lXt -ltcl8.4 -ltk8.4 linux-tk: tk-objs cp $^ ../zangband ## ## Linux with the (depreciated) svga port. ## linux-svga: CFLAGS += -Wno-long-long -D"USE_GCU" -D"USE_VCS" -D"USE_XPJ"\ -D"USE_X11" -D"USE_XAW" -D"USE_LSL" \ `gtk-config --cflags` -D"USE_GTK" -DHAVE_MKSTEMP\ -pedantic -W -Wmissing-prototypes -Wmissing-declarations linux-svga: LIBS := -lX11 -lcurses -lncurses -L/usr/X11R6/lib -lz -lvgagl \ -lvga -lXaw -lXext -lSM -lICE -lXmu -lXt \ -lgtk `gtk-config --libs` ## ## Variation -- Cygwin ## cygwin: CFLAGS += -W -pedantic -mno-cygwin -DWINDOWS cygwin: LIBS := -s -mno-cygwin -mwindows -e _mainCRTStartup -lwinmm cygwin: INSTALL := mv -f zangband.exe ../zangband.exe cygwin: RES := windres cygwin: RESCMD = $< -O coff -o $@ cygwin-objs: $(OBJS) angband.res readdib.o $(MAKE-ZANGBAND) cygwin: cygwin-objs cp $^ ../zangband ## ## Variation -- Cross compile with mingw ## mingw: MINGPREFIX := i586-mingw32msvc- mingw: CC := $(MINGPREFIX)cc mingw: LD := $(MINGPREFIX)ld mingw: CFLAGS += -W -pedantic -mno-cygwin -DWINDOWS mingw: LIBS := -s -mno-cygwin -mwindows -e _mainCRTStartup -lwinmm mingw: INSTALL := mv -f zangband.exe ../zangband.exe mingw: RES := $(MINGPREFIX)windres mingw: RESCMD = $< -O coff -o $@ mingw-objs: $(OBJS) angband.res readdib.o $(MAKE-ZANGBAND) mingw: mingw-objs cp $^ ../zangband ## ## Variation -- OpenBSD ## openbsd: CFLAGS += -I/usr/X11R6/include -D"USE_X11" -D"USE_GCU" openbsd: LIBS := -lX11 -lcurses -ltermcap -L/usr/X11R6/lib ## ## Variation -- FreeBSD ## freebsd: CFLAGS += -D"USE_GCU" -D"USE_XPJ" -D"USE_X11" -D"USE_XAW" \ `gtk12-config --cflags` -D"USE_GTK" \ -pedantic -W -Wmissing-prototypes -Wmissing-declarations freebsd: LIBS := -lX11 -lncurses -L/usr/X11R6/lib \ -lXaw -lXext -lSM -lICE -lXmu -lXt `gtk12-config --libs` ## ## Variation -- tk port to FreeBSD ## freebsd-tk: CFLAGS += -D"USE_TNB" -D"USE_X11" -D"PLATFORM_X11"\ -I/usr/local/include/tcl8.4/generic \ -I/usr/local/include/tk8.4/generic \ -I/usr/local/include/tk8.4/unix \ -I/usr/X11R6/include \ -pedantic -W -Wmissing-prototypes -Wmissing-declarations \ -Wno-long-long -Wwrite-strings -Wpointer-arith \ -Wbad-function-cast -Wsign-compare -DHAVE_MKSTEMP\ -Waggregate-return -Wstrict-prototypes \ -Wredundant-decls #-Wunreachable-code freebsd-tk: LIBS := -lX11 -lcurses -lncurses -L/usr/X11R6/lib -L/usr/local/lib \ -lXaw -lXext -lSM -lICE -lXmu -lXt -ltcl84 -ltk84 freebsd-tk: tk-objs cp $^ ../zangband ## ## Variation -- Use "main-cap.c" instead of "main-gcu.c" ## main-cap: CFLAGS += -D"USE_X11" -D"USE_CAP" main-cap: LIBS := -lX11 -ltermcap ## ## Variation -- Work without X installed ## gcu: CFLAGS += -D"USE_GCU" gcu: LIBS := -lcurses -lncurses ## ## Variation -- Only work on simple vt100 terminals ## vt100: CFLAGS += -D"USE_CAP" -D"USE_HARDCODE" ## ## Variation -- compile for Solaris ## solaris: CFLAGS += -D"USE_X11" -D"USE_GCU" -D"SOLARIS" solaris: LIBS := -lX11 -lcurses -L/usr/openwin/lib ## ## Variation -- compile for SGI Indigo runnig Irix ## sgi: CFLAGS += -D"USE_X11" -D"USE_GCU" -D"SGI" sgi: LIBS := -lX11 -lcurses -ltermcap -lsun ## ## Variation -- compile for Dec ALPHA OSF/1 v2.0 ## dec: CFLAGS += -std -Olimit 4000 -D"USE_X11" -D"USE_GCU" dec: LIBS := -lX11 -lcurses -ltermcap -lrpcsvc ## ## Variation -- compile for Interactive Unix (ISC) systems ## isc: CFLAGS += -D"USE_X11" -D"USE_GCU" -D"ISC" isc: LIBS := -lX11 -lcurses -lnsl_s -linet -lcposix ## ## Variation -- Support fat binaries under NEXTSTEP ## next: CFLAGS += -D"USE_GCU" -arch m68k -arch i386 next: LIBS := -lcurses -ltermcap ## ## Variation -- compile for AIX 4.2.1 systems ## (Tested on an IBM SP2) ## aix: CFLAGS += -bnoquiet -D"USE_X11" -D"SYS_V" aix: LIBS := -lX11 -ltermcap -lcurses -lbsd -lXm -lXmu -lXaw -lXt ## ## Variation -- Dos using "main-dos.c" and the allegro library, with djgpp ## dos: CFLAGS += -s -DUSE_DOS -DUSE_IBM -D"USE_BACKGROUND" dos: LIBS := -lpc -lalleg dos: EXEC := .exe dos: INSTALL := copy zangband.exe .. dos_clean: -del *.o -del lua\*.o -del l-*.c -del lua\tolua.exe ## ## Variation -- Compile using the main-ibm.c port with djgpp ## ibm: CFLAGS += -s -DUSE_IBM ibm: LIBS := -lpc ibm: EXEC := .exe ibm: INSTALL := copy zangband.exe .. ibm_clean: dos_clean ## ## Variation -- Compile using the lcc32 compiler ## (This is untested and probably broken) ## lcc: LCC_PATH := C:\lcc lcc: CC := $(LCC_PATH)\bin\lcc.exe lcc: LIBS := $(LCC_PATH)\lib\winmm.lib -I$(LCC_PATH)\include lcc: LDFLAGS := -s -subsystem windows lcc: RES := $(LCC_PATH)\bin\lrc.exe lcc: RESCMD := -I$(LCC_PATH)\include angband.rc lcc: LUA := $(subst /, \, $(LUA)) lcc: EXEC := .exe lcc-objs: $(OBJS) angband.res readdib.o $(MAKE-ZANGBAND) lcc: lcc-objs copy $^ ..\zangband.exe ## ## Variation -- Compile for OS2 (based on old emx makefile) ## Use 'dmake -B -r -f makefile.emx' to compile ## (see "main-emx.c" for details). ## emx: CFLAGS := -MMD -O3 -DUSE_EMX -Zmt -Wall emx: LDFLAGS := -lvideo emx: AR := ar emx: EXEC := .exe emx-objs: $(OBJS) main-epm.o zangband.a $(MAKE-ZANGBAND) emx: emx-objs copy $^.exe ..\zangband.exe emxbind -s ..\zangband.exe copy aclient.exe .. emxbind -s ..\aclient.exe ## ## Special EMX dependancies ## aclient.exe: main-emx.c $(CC) $(CFLAGS) -D__EMX__CLIENT__ -o $@ $< $(LDFLAGS) main-epm.o: main-emx.c $(CC) $(CFLAGS) -DEMXPM -c -o $@ $< zangband.a: $(OBJS) $(AR) r $@ $(OBJS) ## ## Variation -- Compile for Amiga ## (Untested - probably needs to be fixed) ## amiga: LD := slink QUIET WITH zangband.lnk amiga: CC := sc amiga: CFLAGS := DEFINE AMIGA DEFINE USE_AMI amiga-objs: $(OBJS) sound-ami.o $(MAKE-ZANGBAND) amiga: amiga-objs cp $^ ../zangband #Fixme rm -f zangband ## ## Variation -- Compile using Borland C++ 5.5 (Win32) ## (Untested - may require lots of effort to get this to work) ## borland: CC := bcc32 borland: LD := ilink32 -aa -x borland: RES := brc32 borland: RESCMD := -r angband.rc borland: OTHEROBJS := angband.res c0w32.obj borland: CFLAGS := -Hc -tW -lGn -e$(EXE_FILE) -w- \ -D_WIN32_WINNT=0x0400 -DWINVER=0x0400 borland: LDFLAGS := ,, borland: LIBS := cw32.lib import32.lib borland: INSTALL := copy zangband.exe .. borland: EXEC := .exe ## ## Variation -- Compile using gcc on Mac OS X ## osx osx-dist: TOOLDIR := /Developer/Tools osx osx-dist: REZ := $(TOOLDIR)/Rez osx osx-dist: SETFILE := $(TOOLDIR)/SetFile osx: CFLAGS := -Wall -O2 -fpascal-strings -DMACH_O_CARBON \ -DPRIVATE_USER_PATH=\"~/Library/Preferences\" -DUSE_PRIVATE_PATHS osx: LIBS := -framework CoreFoundation -framework QuickTime -framework Carbon # Application bundle -- a directory looking like an ordinary application # Name of the game osx osx-dist osx-install-tiles: APPNAME := ZAngband.app # Bundle of the game (it must be a directory whose name ends in ".app") osx osx-dist osx-install-tiles: APPBNDL := ../$(APPNAME) # A bundle must have these subdirectories osx osx-install-tiles: APPCONT := $(APPBNDL)/Contents osx: APPBIN := $(APPCONT)/MacOS osx osx-install-tiles: APPRES := $(APPCONT)/Resources osx: ICONFILES := ZAngband.icns Save.icns Edit.icns Data.icns osx: PLIST := ZAngband.xml osx osx-dist: RESSRCS := ZAngband.r osx osx-dist: SOUNDSRCS := Sound.r osx osx-dist: RFLAGS := -i /Developer/Headers/FlatCarbon -d MACH_O_CARBON osx: LIBFILES := \ ../lib/edit/*.txt \ ../lib/file/*.txt \ ../lib/help/*.txt \ ../lib/help/*.hlp \ ../lib/pref/*.prf \ ../lib/script/*.lua osx-objs: $(OBJS) main-crb.o $(RESSRCS) $(ICONFILES) $(PLIST) $(LIBFILES) $(MAKE-ZANGBAND) osx: osx-objs [ -d $(APPBNDL) ] || mkdir $(APPBNDL) [ -d $(APPCONT) ] || mkdir $(APPCONT) [ -d $(APPBIN) ] || mkdir $(APPBIN) [ -d $(APPRES) ] || mkdir $(APPRES) [ -d $(APPRES)/lib ] || mkdir $(APPRES)/lib [ -d $(APPRES)/lib/edit ] || mkdir $(APPRES)/lib/edit [ -d $(APPRES)/lib/file ] || mkdir $(APPRES)/lib/file [ -d $(APPRES)/lib/help ] || mkdir $(APPRES)/lib/help [ -d $(APPRES)/lib/pref ] || mkdir $(APPRES)/lib/pref [ -d $(APPRES)/lib/script ] || mkdir $(APPRES)/lib/script cp ../lib/edit/*.txt $(APPRES)/lib/edit cp ../lib/file/*.txt $(APPRES)/lib/file cp ../lib/help/*.txt $(APPRES)/lib/help cp ../lib/help/*.hlp $(APPRES)/lib/help cp ../lib/pref/*.prf $(APPRES)/lib/pref cp ../lib/script/*.lua $(APPRES)/lib/script install -m 755 $^ $(APPBIN)/zangband install -m 644 $(ICONFILES) $(APPRES) $(REZ) $(RFLAGS) -o $(APPRES)/zangband.rsrc $(RESSRCS) install -m 644 $(PLIST) $(APPCONT)/Info.plist $(SETFILE) -a B $(APPBNDL) osx-install-tiles: TILES := 8x8.png 16x16.png 32x32.png osx-install-tiles: osx $(TILES) install -m 644 $(TILES) $(APPRES) osx-dist: PACKAGE_NAME := ZAngband-275-alpha1 osx-dist: osx osx-install-tiles $(SOUNDSRCS) -rm -rf disttemp -rm -f "$(PACKAGE_NAME)-osx.dmg" mkdir disttemp mkdir disttemp/Docs cp ../readme disttemp/Docs/ReadMe.txt cp ../z_faq.txt disttemp/Docs/FAQ.txt cp ../z_update.txt disttemp/Docs/Changes.txt cp -R -p "$(APPBNDL)" disttemp $(REZ) $(RFLAGS) -o disttemp/$(APPNAME)/Contents/Resources/zangband.rsrc $(RESSRCS) $(SOUNDSRCS) $(SETFILE) -a B disttemp/$(APPNAME) hdiutil create -fs HFS+ -volname $(PACKAGE_NAME) -srcfolder disttemp "$(PACKAGE_NAME)-osx.dmg" rm -rf disttemp ########################################################## ## ## Hack - GCC requires other flags ## ifeq (gcc, $(findstring gcc, $(CC))) CFLAGS += -fno-strength-reduce endif ## ## Clean up old junk ## clean: -$(RM) *.bak *.o -$(RM) ./lua/*.bak ./lua/*.o -$(RM) ./l-*.c -$(RM) ./lua/tolua -$(RM) ./tk/*.bak ./tk/*.o -$(RM) *-objs zangband ## ## Generate dependencies automatically ## depend: makedepend -D__MAKEDEPEND__ $(SRCS) ## ## Lua stuff ## $(LUA)tolua: $(TOLUAOBJS) $(CC) $(CFLAGS) -o $(LUA)tolua$(EXEC) $(TOLUAOBJS) $(LDFLAGS) $(LIBS) ## ## Low-level Headers ## HDRS := \ h-basic.h \ h-define.h h-type.h h-system.h h-config.h ## ## Angband includes ## INCS := \ angband.h \ z-config.h defines.h types.h externs.h \ z-term.h z-rand.h z-util.h z-virt.h z-form.h $(HDRS) ## ## Generic dependancy information ## $(ANGOBJS): $(INCS) $(ZUTILOBJS): $(HDRS) $(BORGOBJS): $(INCS) ## ## Extra dependancies ## cmd6.o: script.h init1.c: init.h init2.c: init.h script.h generate.o: grid.h generate.h rooms.h streams.h grid.o: grid.h generate.h rooms.o: grid.h generate.h rooms.h streams.o: grid.h generate.h fields.o: grid.h script.h main-ami.o: maid-grf.h maid-grf.o: maid-grf.h maid-x11.o: maid-grf.h main-gtk.o: maid-grf.h main-tnb.o: maid-grf.h main-win.o: maid-grf.h main-x11.o: maid-grf.h maid-x11.h main-xaw.o: maid-grf.h maid-x11.h main-xpj.o: maid-grf.h maid-x11.h script.o: script.h quest.o: wild.h grid.h readdib.o: readdib.h wild1.o: wild.h grid.h wild2.o: wild.h grid.h wild3.o: wild.h grid.h wizard2.o: script.h z-form.o: z-form.h z-util.h z-virt.h z-rand.o: z-rand.h z-term.o: z-term.h z-virt.h z-util.o: z-util.h z-virt.o: z-virt.h z-util.h ## Each borg file depends on the ones before it. ## Hack - the dependance on the .o files encodes this. zborg1.o: zborg1.h maid-grf.h zborg2.o: zborg1.h zborg2.h maid-grf.h zborg3.o: zborg1.h zborg3.h maid-grf.h zborg4.o: zborg1.h zborg2.h zborg3.h zborg4.h maid-grf.h zborg5.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h maid-grf.h zborg6.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h maid-grf.h \ zbmagic.h zborg7.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h zborg7.h \ maid-grf.h zborg8.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h zborg7.h \ zborg8.h maid-grf.h zborg9.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h zborg7.h \ zborg8.h zborg9.h maid-grf.h zbmagic1.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h maid-grf.h \ zbmagic.h zbmagic2.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h maid-grf.h \ zbmagic.h zbmagic3.o: zborg1.h zborg2.h zborg3.h zborg4.h zborg5.h zborg6.h maid-grf.h \ zbmagic.h ## ## Compiling angband.rc ## ## Note: this format seems to work but the alternative actually used ## is the one recommended by Cygnus ## ## angband.res : angband.rc ## $(WRES) angband.rc angband.res ## angband.res : angband.rc $(RES) $(RESCMD) ## ## Build wrappers ## ## $(subst l-,,$*) removes the leading "l-", and ## trailing ".c" from the filename. ## $(LUAWSRCS): %.c: %.pkg $(LUA)tolua$(EXEC) $(LUA)tolua -n $(subst l-,,$*) -o $@ $< ## ## Extra dependancies for the tk port ## $(TKOBJS): $(INCS) tk/tnb.h main-tnb.o: tk/tnb.h TKICONOBJS := \ tk/icon.o tk/interp.o $(TKICONOBJS): tk/icon.h ## ## Compile the tk stuff ## tk-objs: $(TKOBJS) $(OBJS) $(MAKE-ZANGBAND) ## ## Hack... .o files can be created from .obj files ## %.o:%.obj move $@ $< #LUALIST := basic.lua feature.lua verbatim.lua code.lua typedef.lua \ # container.lua package.lua module.lua define.lua enumerate.lua \ # declaration.lua variable.lua array.lua function.lua operator.lua \ # class.lua clean.lua doit.lua #LUALIST := $(addprefix $(LUA), $(LUALIST)) ## ## We sometimes want to redo the lua stuff ## ## This doesn't work though... ## #$(LUA)tolualua.c: $(LUALIST) # ./tolua -n tolualua -o tolualua.c tolualua.pkg ## ## Phony targets ## .PHONY: install_game clean depend $(PORTS) $(OTHER-PORTS) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/makefile.zb����������������������������������������������������������������������������0000644�0000000�0000000�00000013701�10250356275�014250� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������subdir = ./src/ ## ## Actually build the source to make the game ## ## ## ##### Zangband Files to compile ##### ## ## ## The Zangband source files ## NORMOBJS := $(addprefix src/,\ variable.o tables.o util.o cave.o \ object1.o object2.o monster1.o monster2.o \ xtra1.o xtra2.o spells1.o spells2.o \ melee1.o melee2.o save.o files.o fields.o\ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o \ store.o birth.o load.o ui.o\ wizard1.o wizard2.o grid.o streams.o rooms.o \ generate.o dungeon.o init1.o init2.o \ effects.o quest.o racial.o run.o script.o \ artifact.o mutation.o flavor.o spells3.o \ mspells1.o mspells2.o scores.o mind.o \ bldg.o obj_kind.o wild1.o wild2.o wild3.o avatar.o notes.o) ANGOBJS := $(NORMOBJS) $(addprefix src/,\ maid-x11.o\ main-cap.o main-gcu.o main-x11.o main-xaw.o main-xpj.o\ main-lsl.o main-vcs.o main-gtk.o main-win.o main.o \ maid-grf.o main-dos.o main-ibm.o main-emx.o \ main-ami.o main-tnb.o) ## ## The wrapper auto-generated files ## LUAWOBJS := $(addprefix src/,\ l-monst.o l-object.o l-player.o l-random.o l-ui.o \ l-misc.o l-spell.o l-field.o) ## ## The "Utility" files ## ZUTILOBJS := $(addprefix src/,\ z-util.o z-virt.o z-form.o z-rand.o z-term.o) ## ## The Borg files ## BORGOBJS := $(addprefix src/,\ zborg1.o zborg2.o zborg3.o zborg4.o zborg5.o \ zborg6.o zborg7.o zborg8.o zborg9.o \ zbmagic1.o zbmagic2.o zbmagic3.o) ## ## Normal list of object files. ## objs-y += $(ANGOBJS) $(LUAWOBJS) $(ZUTILOBJS) $(BORGOBJS) ## ## Extra object files depending on the ports ## objs-$(amiga) += src/sound-ami.o objs-$(cygwin) += src/angband.res src/readdib.o ifeq ($(cygwin),y) src/angband.res : src/angband.rc windres $< -O coff -o $@ endif objs-$(lcc) += src/angband.res src/readdib.o ifeq ($(lcc),y) src/angband.res : src/angband.rc $(LCC_PATH)\bin\lrc.exe -I$(LCC_PATH)\include $^ endif objs-$(emx) += src/main-epm.o src/zangband.a ifeq ($(emx),y) INSTALL += \ emxbind -s $(bindir)zangband.exe\ copy src\aclient.exe $(bindir)\ emxbind -s $(bindir)aclient.exe\ AR := ar src/aclient.exe: src/main-emx.c $(CC) $(CFLAGS) -D__EMX__CLIENT__ -o $@ $< $(LDFLAGS) src/main-epm.o: src/main-emx.c $(CC) $(CFLAGS) -DEMXPM -c -o $@ $< src/zangband.a: $(OBJS) $(AR) r $@ $(OBJS) endif ## ## Autoconf header ## src/autoconf.h: $(CONFIGURE) ## ## Low-level Headers ## HDRS := $(addprefix src/,\ autoconf.h h-basic.h h-define.h h-type.h h-system.h h-config.h) ## ## Angband includes ## INCS := $(addprefix src/,\ angband.h \ z-config.h defines.h types.h externs.h \ z-term.h z-rand.h z-util.h z-virt.h z-form.h) $(HDRS) ## ## Generic dependancy information ## $(ANGOBJS): $(INCS) .default_path $(ZUTILOBJS): $(HDRS) $(BORGOBJS): $(INCS) $(LUAWOBJS): $(INCS) ## ## Extra dependancies ## GRAF_PORTS = $(addprefix src/,\ main-ami.o maid-grf.o maid-x11.o \ main-gtk.o main-tnb.o main-win.o main-x11.o \ main-xaw.o main-xpj.o) src/cmd6.o: src/script.h src/init1.c: src/init.h src/init2.c: src/init.h src/script.h src/generate.o: src/grid.h src/generate.h src/rooms.h src/streams.h src/grid.o: src/grid.h src/generate.h src/rooms.o: src/grid.h src/generate.h src/rooms.h src/streams.o: src/grid.h src/generate.h src/fields.o: src/grid.h src/script.h $(GRAFPORTS): src/maid-grf.h src/main-x11.o: src/maid-x11.h src/main-xaw.o: src/maid-x11.h src/main-xpj.o: src/maid-x11.h src/script.o: src/script.h src/quest.o: src/wild.h src/grid.h src/readdib.o: src/readdib.h src/wild1.o: src/wild.h src/grid.h src/wild2.o: src/wild.h src/grid.h src/wild3.o: src/wild.h src/grid.h src/wizard2.o: src/script.h src/z-form.o: src/z-form.h src/z-util.h src/z-virt.h src/z-rand.o: src/z-rand.h src/z-term.o: src/z-term.h src/z-virt.h src/z-util.o: src/z-util.h src/z-virt.o: src/z-virt.h src/z-util.h ## Each borg file depends on the ones before it. ## Hack - the dependance on the .o files encodes this. src/zborg1.o: src/zborg1.h src/maid-grf.h src/zborg2.o: src/zborg1.h src/zborg2.h src/maid-grf.h src/zborg3.o: src/zborg1.h src/zborg3.h src/maid-grf.h src/zborg4.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/maid-grf.h src/zborg5.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/maid-grf.h src/zborg6.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/maid-grf.h src/zbmagic.h src/zborg7.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/zborg7.h src/maid-grf.h src/zborg8.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/zborg7.h src/zborg8.h \ src/maid-grf.h src/zborg9.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/zborg7.h src/zborg8.h \ src/zborg9.h src/maid-grf.h src/zbmagic1.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/maid-grf.h src/zbmagic.h src/zbmagic2.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/maid-grf.h src/zbmagic.h src/zbmagic3.o: src/zborg1.h src/zborg2.h src/zborg3.h src/zborg4.h \ src/zborg5.h src/zborg6.h src/maid-grf.h src/zbmagic.h ## ## Build wrappers ## ## $(subst l-,,$(notdir $*)) removes the leading "l-", and ## trailing ".c" and returns a filename with no directory ## attached to it. ## LUAWSRCS := $(LUAWOBJS:.o=.c) ifeq ($(VERBOSE), 0) define TOLUA @ echo TOLUA $@ @ src/lua/tolua endef else TOLUA = src/lua/tolua endif dust-files += src/*~ clean-files += src/*.bak src/*.o src/l-*.c distclean-files += autoconf.h srcdirlist += src notsrcfiles += src/compress.c src/autoconf.h srcfiles += src/*.c src/*.h src/angband.rc src/angband.ico \ src/angband.xpm src/zangband.icns src/makefile* src/l-*.pkg \ src/autoconf.h.in # scan other source directories dirs := lua tk include $(scandir) $(LUAWSRCS): %.c: %.pkg src/lua/tolua $(TOLUALUAOBJS) $(TOLUA) -n $(subst l-,,$(notdir $*)) -o $@ $< # List of makefiles in other build system so we can test them makefiles := $(addprefix src/,\ makefile.bcc makefile.ros makefile.std) ���������������������������������������������������������������zangband/src/l-field.pkg����������������������������������������������������������������������������0000644�0000000�0000000�00000006344�10250356275�014162� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" $#include "script.h" typedef char* cptr; typedef int errr; typedef int sint; #define ACT_TUNNEL 0 #define ACT_DISARM 1 #define ACT_OPEN 2 #define BREAK_GLYPH 550 /* Rune of protection resistance */ #define BREAK_MINOR_GLYPH 99 /* For explosive runes */ /* * Field information flags */ #define FIELD_INFO_TEMP 0x0001 /* Temporary field - use counter */ #define FIELD_INFO_FEAT 0x0002 /* Terrain feature based field */ #define FIELD_INFO_VIS 0x0004 /* Has attr / char */ #define FIELD_INFO_MARK 0x0008 /* Known */ #define FIELD_INFO_TRANS 0x0010 /* Tile uses 16x16 transparency effects */ #define FIELD_INFO_NO_LOOK 0x0020 /* Do not describe when looked at */ #define FIELD_INFO_NFT_LOOK 0x0040 /* Do not describe feat when looked at */ #define FIELD_INFO_MERGE 0x0080 /* Merge counter with similar fields */ #define FIELD_INFO_NO_ENTER 0x0100 /* Grid blocks player entry */ #define FIELD_INFO_NO_MAGIC 0x0200 /* Grid blocks magic */ #define FIELD_INFO_NO_OBJCT 0x0400 /* Grid cannot hold objects */ #define FIELD_INFO_PERM 0x0800 /* Grid is not affected by disintegrate */ #define FIELD_INFO_IGNORE 0x1000 /* Grid is below the object layer */ #define FIELD_INFO_NO_MPLACE 0x2000 /* Grid blocks monster placement */ #define FIELD_INFO_DUMMY13 0x4000 #define FIELD_INFO_DUMMY14 0x8000 /* * The field structure. * * Fields will be used to create a variety of effects from * the ability to place traps on _all_ terrains (not just * dungeon floor), to the nightmare mode automatic corpse raising. * * The new building / store code will use this structure. * */ struct field_type { byte f_attr; /* attribute */ char f_char; /* character */ s16b t_idx; /* field type index */ s16b fy; /* Y location on map */ s16b fx; /* X location on map */ u16b info; /* quick access flags */ /* Storage space for the actions to interact with. */ byte data[8]; s16b counter; /* Counter for timed effects */ }; extern void deleteme(void); extern void set_corpse_size(field_type *f_ptr, int size); extern void notice_field(field_type *f_ptr); extern cptr field_name(const field_type *f_ptr); extern bool check_trap_hit(int power); extern void hit_trap(field_type *f_ptr); extern void evil_trap(void); extern bool player_save(int power); extern void drop_random_item(void); extern void drain_lite(void); extern void drain_food(void); extern void drain_magic(void); extern bool raise_dead(int x, int y, bool pet); extern void do_cmd_store(const field_type *f_ptr); extern void do_cmd_bldg(const field_type *f_ptr); extern void place_sb(int greed, int max_cost); extern void display_build(const field_type *f_ptr); extern bool test_gold(s32b cost); extern bool build_has_quest(void); extern void build_cmd_quest(int level); extern void record_aura(void); extern bool building_healer(void); extern bool building_magetower(int factor, bool display); extern bool research_mon(void); extern void gamble_help(void); extern void gamble_in_between(void); extern void gamble_craps(void); extern void gamble_spin_wheel(void); extern void gamble_dice_slots(void); extern bool inn_rest(void); extern bool compare_weapons(void); extern void building_recharge(s32b cost); extern bool enchant_item(s32b cost, bool to_hit, bool to_dam, bool to_ac, bool weap); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/l-misc.pkg�����������������������������������������������������������������������������0000644�0000000�0000000�00000006206�10250356275�014027� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" typedef char* cptr; typedef int errr; /***** Constants *****/ #define TRUE 1 #define FALSE 0 /*** Terrain Feature Indexes (see "lib/edit/f_info.txt") ***/ /* Nothing */ #define FEAT_NONE 0x00 /* Various */ #define FEAT_FLOOR 0x01 /* #define FEAT_INVIS 0x02 Now is a field */ /* #define FEAT_GLYPH 0x03 Now is a field */ #define FEAT_OPEN 0x04 #define FEAT_BROKEN 0x05 #define FEAT_LESS 0x06 #define FEAT_MORE 0x07 /* Passable floors */ #define FEAT_SAND 0x08 #define FEAT_SALT 0x09 #define FEAT_WET_MUD 0x0A #define FEAT_DRY_MUD 0x0B #define FEAT_FLOOR_TILE 0x0C #define FEAT_FLOOR_WOOD 0x0D #define FEAT_PEBBLES 0x0E #define FEAT_SOLID_LAVA 0x0F /* Gap where the traps were */ /* Closed Door */ #define FEAT_CLOSED 0x20 #define FEAT_PILLAR 0x21 /* A big gap for expansion */ /* Extra */ #define FEAT_SECRET 0x30 #define FEAT_RUBBLE 0x31 /* Seams */ #define FEAT_MAGMA 0x32 #define FEAT_QUARTZ 0x33 /* #define FEAT_MAGMA_H 0x34 */ /* #define FEAT_QUARTZ_H 0x35 */ #define FEAT_MAGMA_K 0x36 #define FEAT_QUARTZ_K 0x37 /* Walls */ #define FEAT_WALL_EXTRA 0x38 #define FEAT_WALL_INNER 0x39 #define FEAT_WALL_OUTER 0x3A #define FEAT_WALL_SOLID 0x3B #define FEAT_PERM_EXTRA 0x3C #define FEAT_PERM_INNER 0x3D #define FEAT_PERM_OUTER 0x3E #define FEAT_PERM_SOLID 0x3F /* Gap where Glyph was */ /* Pattern */ #define FEAT_PATTERN_START 0x41 #define FEAT_PATTERN_1 0x42 #define FEAT_PATTERN_2 0x43 #define FEAT_PATTERN_3 0x44 #define FEAT_PATTERN_4 0x45 #define FEAT_PATTERN_END 0x46 #define FEAT_PATTERN_OLD 0x47 #define FEAT_PATTERN_XTRA1 0x48 #define FEAT_PATTERN_XTRA2 0x49 /* Terrains */ #define FEAT_DEEP_WATER 0x53 #define FEAT_SHAL_WATER 0x54 #define FEAT_DEEP_LAVA 0x55 #define FEAT_SHAL_LAVA 0x56 /* Gap */ #define FEAT_DIRT 0x58 #define FEAT_GRASS 0x59 /* Gap */ #define FEAT_OCEAN_WATER 0x5C #define FEAT_DEEP_ACID 0x5D #define FEAT_SHAL_ACID 0x5E #define FEAT_TREE_WATER 0x5F /* Terrain semi-transparent*/ #define FEAT_TREES 0x60 #define FEAT_MOUNTAIN 0x61 #define FEAT_SNOW_MOUNTAIN 0x62 #define FEAT_BOULDER 0x63 #define FEAT_PINE_TREE 0x64 #define FEAT_SNOW_TREE 0x65 #define FEAT_OBELISK 0x66 /* Gap */ /* Impassible terrains */ #define FEAT_FENCE 0x70 #define FEAT_WELL 0x71 #define FEAT_FOUNTAIN 0x72 #define FEAT_JUNGLE 0x73 /* Gap */ /* Slow "floor" terrains */ #define FEAT_BUSH 0x80 #define FEAT_DEAD_BUSH 0x81 #define FEAT_GRASS_LONG 0x82 #define FEAT_ROCK_GEN 0x83 #define FEAT_ROCK_SNOW 0x84 #define FEAT_TREE_GEN 0x85 #define FEAT_TREE_SNOW 0x86 #define FEAT_SNOW 0x87 #define FEAT_DEEP_SWAMP 0x88 #define FEAT_SHAL_SWAMP 0x89 /***** Types *****/ /***** Variables *****/ /***** Functions *****/ extern void cave_set_feat(int x, int y, int feat); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/l-monst.pkg����������������������������������������������������������������������������0000644�0000000�0000000�00000035056�10250356275�014241� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" typedef char* cptr; typedef int errr; /***** Constants *****/ /* * New monster race bit flags */ #define RF0_UNIQUE 0x00000001 /* Unique Monster */ #define RF0_QUESTOR 0x00000002 /* Quest Monster */ #define RF0_MALE 0x00000004 /* Male gender */ #define RF0_FEMALE 0x00000008 /* Female gender */ #define RF0_CHAR_CLEAR 0x00000010 /* Absorbs symbol */ #define RF0_CHAR_MIMIC 0x00000020 /* Changes symbol */ #define RF0_ATTR_CLEAR 0x00000040 /* Absorbs color */ #define RF0_ATTR_MULTI 0x00000080 /* Changes color */ #define RF0_FORCE_DEPTH 0x00000100 /* Start at "correct" depth */ #define RF0_FORCE_MAXHP 0x00000200 /* Start with max hitpoints */ #define RF0_FORCE_SLEEP 0x00000400 /* Start out sleeping */ #define RF0_FORCE_EXTRA 0x00000800 /* Start out something */ #define RF0_XXX_1 0x00001000 /* Unused */ #define RF0_FRIENDS 0x00002000 /* Arrive with some friends */ #define RF0_ESCORT 0x00004000 /* Arrive with an escort */ #define RF0_ESCORTS 0x00008000 /* Arrive with some escorts */ #define RF0_NEVER_BLOW 0x00010000 /* Never make physical blow */ #define RF0_NEVER_MOVE 0x00020000 /* Never make physical move */ #define RF0_RAND_25 0x00040000 /* Moves randomly (25%) */ #define RF0_RAND_50 0x00080000 /* Moves randomly (50%) */ #define RF0_ONLY_GOLD 0x00100000 /* Drop only gold */ #define RF0_ONLY_ITEM 0x00200000 /* Drop only items */ #define RF0_DROP_60 0x00400000 /* Drop an item/gold (60%) */ #define RF0_DROP_90 0x00800000 /* Drop an item/gold (90%) */ #define RF0_DROP_1D2 0x01000000 /* Drop 1d2 items/gold */ #define RF0_DROP_2D2 0x02000000 /* Drop 2d2 items/gold */ #define RF0_DROP_3D2 0x04000000 /* Drop 3d2 items/gold */ #define RF0_DROP_4D2 0x08000000 /* Drop 4d2 items/gold */ #define RF0_DROP_GOOD 0x10000000 /* Drop good items */ #define RF0_DROP_GREAT 0x20000000 /* Drop great items */ #define RF0_DROP_USEFUL 0x40000000 /* Drop "useful" items */ #define RF0_DROP_CHOSEN 0x80000000 /* Drop "chosen" items */ /* * New monster race bit flags */ #define RF1_STUPID 0x00000001 /* Monster is stupid */ #define RF1_SMART 0x00000002 /* Monster is smart */ #define RF1_CAN_SPEAK 0x00000004 /* TY: can speak */ #define RF1_REFLECTING 0x00000008 /* Reflects bolts */ #define RF1_INVISIBLE 0x00000010 /* Monster avoids vision */ #define RF1_COLD_BLOOD 0x00000020 /* Monster avoids infra */ #define RF1_EMPTY_MIND 0x00000040 /* Monster avoids telepathy */ #define RF1_WEIRD_MIND 0x00000080 /* Monster avoids telepathy? */ #define RF1_MULTIPLY 0x00000100 /* Monster reproduces */ #define RF1_REGENERATE 0x00000200 /* Monster regenerates */ #define RF1_SHAPECHANGER 0x00000400 /* TY: shapechanger */ #define RF1_ATTR_ANY 0x00000800 /* TY: Attr_any */ #define RF1_POWERFUL 0x00001000 /* Monster has strong breath */ #define RF1_XXX_1 0x00002000 #define RF1_AURA_FIRE 0x00004000 /* Burns in melee */ #define RF1_AURA_ELEC 0x00008000 /* Shocks in melee */ #define RF1_OPEN_DOOR 0x00010000 /* Monster can open doors */ #define RF1_BASH_DOOR 0x00020000 /* Monster can bash doors */ #define RF1_PASS_WALL 0x00040000 /* Monster can pass walls */ #define RF1_KILL_WALL 0x00080000 /* Monster can destroy walls */ #define RF1_MOVE_BODY 0x00100000 /* Monster can move monsters */ #define RF1_KILL_BODY 0x00200000 /* Monster can kill monsters */ #define RF1_TAKE_ITEM 0x00400000 /* Monster can pick up items */ #define RF1_KILL_ITEM 0x00800000 /* Monster can crush items */ #define RF1_BRAIN_1 0x01000000 #define RF1_BRAIN_2 0x02000000 #define RF1_BRAIN_3 0x04000000 #define RF1_BRAIN_4 0x08000000 #define RF1_BRAIN_5 0x10000000 #define RF1_BRAIN_6 0x20000000 #define RF1_BRAIN_7 0x40000000 #define RF1_QUANTUM 0x80000000 /* Monster has quantum behavior */ /* * New monster race bit flags */ #define RF2_ORC 0x00000001 /* Orc */ #define RF2_TROLL 0x00000002 /* Troll */ #define RF2_GIANT 0x00000004 /* Giant */ #define RF2_DRAGON 0x00000008 /* Dragon */ #define RF2_DEMON 0x00000010 /* Demon */ #define RF2_UNDEAD 0x00000020 /* Undead */ #define RF2_EVIL 0x00000040 /* Evil */ #define RF2_ANIMAL 0x00000080 /* Animal */ #define RF2_AMBERITE 0x00000100 /* TY: Amberite */ #define RF2_GOOD 0x00000200 /* Good */ #define RF2_AURA_COLD 0x00000400 /* Freezes in melee */ #define RF2_NONLIVING 0x00000800 /* TY: Non-Living (?) */ #define RF2_HURT_LITE 0x00001000 /* Hurt by lite */ #define RF2_HURT_ROCK 0x00002000 /* Hurt by rock remover */ #define RF2_HURT_FIRE 0x00004000 /* Hurt badly by fire */ #define RF2_HURT_COLD 0x00008000 /* Hurt badly by cold */ #define RF2_IM_ACID 0x00010000 /* Resist acid a lot */ #define RF2_IM_ELEC 0x00020000 /* Resist elec a lot */ #define RF2_IM_FIRE 0x00040000 /* Resist fire a lot */ #define RF2_IM_COLD 0x00080000 /* Resist cold a lot */ #define RF2_IM_POIS 0x00100000 /* Resist poison a lot */ #define RF2_RES_TELE 0x00200000 /* Resist teleportation */ #define RF2_RES_NETH 0x00400000 /* Resist nether a lot */ #define RF2_RES_WATE 0x00800000 /* Resist water */ #define RF2_RES_PLAS 0x01000000 /* Resist plasma */ #define RF2_RES_NEXU 0x02000000 /* Resist nexus */ #define RF2_RES_DISE 0x04000000 /* Resist disenchantment */ #define RF2_UNIQUE_7 0x08000000 /* Is a "Nazgul" unique */ #define RF2_NO_FEAR 0x10000000 /* Cannot be scared */ #define RF2_NO_STUN 0x20000000 /* Cannot be stunned */ #define RF2_NO_CONF 0x40000000 /* Cannot be confused */ #define RF2_NO_SLEEP 0x80000000 /* Cannot be slept */ /* * New monster race bit flags */ #define RF3_SHRIEK 0x00000001 /* Shriek for help */ #define RF3_ELDRITCH_HORROR 0x00000002 /* Sanity-blasting horror */ #define RF3_XXX3 0x00000004 /* (?) */ #define RF3_ROCKET 0x00000008 /* TY: Rocket */ #define RF3_ARROW 0x00000010 /* Fire an arrow */ #define RF3_XXX6 0x00000020 /* (?) */ #define RF3_XXX7 0x00000040 /* (?) */ #define RF3_XXX8 0x00000080 /* (?) */ #define RF3_BR_ACID 0x00000100 /* Breathe Acid */ #define RF3_BR_ELEC 0x00000200 /* Breathe Elec */ #define RF3_BR_FIRE 0x00000400 /* Breathe Fire */ #define RF3_BR_COLD 0x00000800 /* Breathe Cold */ #define RF3_BR_POIS 0x00001000 /* Breathe Poison */ #define RF3_BR_NETH 0x00002000 /* Breathe Nether */ #define RF3_BR_LITE 0x00004000 /* Breathe Lite */ #define RF3_BR_DARK 0x00008000 /* Breathe Dark */ #define RF3_BR_CONF 0x00010000 /* Breathe Confusion */ #define RF3_BR_SOUN 0x00020000 /* Breathe Sound */ #define RF3_BR_CHAO 0x00040000 /* Breathe Chaos */ #define RF3_BR_DISE 0x00080000 /* Breathe Disenchant */ #define RF3_BR_NEXU 0x00100000 /* Breathe Nexus */ #define RF3_BR_TIME 0x00200000 /* Breathe Time */ #define RF3_BR_INER 0x00400000 /* Breathe Inertia */ #define RF3_BR_GRAV 0x00800000 /* Breathe Gravity */ #define RF3_BR_SHAR 0x01000000 /* Breathe Shards */ #define RF3_BR_PLAS 0x02000000 /* Breathe Plasma */ #define RF3_BR_WALL 0x04000000 /* Breathe Force */ #define RF3_BR_MANA 0x08000000 /* Breathe Mana */ #define RF3_BA_NUKE 0x10000000 /* TY: Nuke Ball */ #define RF3_BR_NUKE 0x20000000 /* TY: Toxic Breath */ #define RF3_BA_CHAO 0x40000000 /* TY: Logrus Ball */ #define RF3_BR_DISI 0x80000000 /* Breathe Disintegration */ /* * New monster race bit flags */ #define RF4_BA_ACID 0x00000001 /* Acid Ball */ #define RF4_BA_ELEC 0x00000002 /* Elec Ball */ #define RF4_BA_FIRE 0x00000004 /* Fire Ball */ #define RF4_BA_COLD 0x00000008 /* Cold Ball */ #define RF4_BA_POIS 0x00000010 /* Poison Ball */ #define RF4_BA_NETH 0x00000020 /* Nether Ball */ #define RF4_BA_WATE 0x00000040 /* Water Ball */ #define RF4_BA_MANA 0x00000080 /* Mana Storm */ #define RF4_BA_DARK 0x00000100 /* Darkness Storm */ #define RF4_DRAIN_MANA 0x00000200 /* Drain Mana */ #define RF4_MIND_BLAST 0x00000400 /* Blast Mind */ #define RF4_BRAIN_SMASH 0x00000800 /* Smash Brain */ #define RF4_CAUSE_1 0x00001000 /* Cause Light Wound */ #define RF4_CAUSE_2 0x00002000 /* Cause Serious Wound */ #define RF4_CAUSE_3 0x00004000 /* Cause Critical Wound */ #define RF4_CAUSE_4 0x00008000 /* Cause Mortal Wound */ #define RF4_BO_ACID 0x00010000 /* Acid Bolt */ #define RF4_BO_ELEC 0x00020000 /* Elec Bolt (unused) */ #define RF4_BO_FIRE 0x00040000 /* Fire Bolt */ #define RF4_BO_COLD 0x00080000 /* Cold Bolt */ #define RF4_BO_POIS 0x00100000 /* Poison Bolt (unused) */ #define RF4_BO_NETH 0x00200000 /* Nether Bolt */ #define RF4_BO_WATE 0x00400000 /* Water Bolt */ #define RF4_BO_MANA 0x00800000 /* Mana Bolt */ #define RF4_BO_PLAS 0x01000000 /* Plasma Bolt */ #define RF4_BO_ICEE 0x02000000 /* Ice Bolt */ #define RF4_MISSILE 0x04000000 /* Magic Missile */ #define RF4_SCARE 0x08000000 /* Frighten Player */ #define RF4_BLIND 0x10000000 /* Blind Player */ #define RF4_CONF 0x20000000 /* Confuse Player */ #define RF4_SLOW 0x40000000 /* Slow Player */ #define RF4_HOLD 0x80000000 /* Paralyze Player */ /* * New monster race bit flags */ #define RF5_HASTE 0x00000001 /* Speed self */ #define RF5_HAND_DOOM 0x00000002 /* Hand of Doom */ #define RF5_HEAL 0x00000004 /* Heal self */ #define RF5_INVULNER 0x00000008 /* INVULNERABILITY! */ #define RF5_BLINK 0x00000010 /* Teleport Short */ #define RF5_TPORT 0x00000020 /* Teleport Long */ #define RF5_XXX3 0x00000040 /* Move to Player (?) */ #define RF5_XXX4 0x00000080 /* Move to Monster (?) */ #define RF5_TELE_TO 0x00000100 /* Move player to monster */ #define RF5_TELE_AWAY 0x00000200 /* Move player far away */ #define RF5_TELE_LEVEL 0x00000400 /* Move player vertically */ #define RF5_XXX5 0x00000800 /* Move player (?) */ #define RF5_DARKNESS 0x00001000 /* Create Darkness */ #define RF5_TRAPS 0x00002000 /* Create Traps */ #define RF5_FORGET 0x00004000 /* Cause amnesia */ #define RF5_RAISE_DEAD 0x00008000 /* Raise Dead */ #define RF5_S_KIN 0x00010000 /* Summon "kin" */ #define RF5_S_CYBER 0x00020000 /* Summon Cyberdemons! */ #define RF5_S_MONSTER 0x00040000 /* Summon Monster */ #define RF5_S_MONSTERS 0x00080000 /* Summon Monsters */ #define RF5_S_ANT 0x00100000 /* Summon Ants */ #define RF5_S_SPIDER 0x00200000 /* Summon Spiders */ #define RF5_S_HOUND 0x00400000 /* Summon Hounds */ #define RF5_S_HYDRA 0x00800000 /* Summon Hydras */ #define RF5_S_ANGEL 0x01000000 /* Summon Angel */ #define RF5_S_DEMON 0x02000000 /* Summon Demon */ #define RF5_S_UNDEAD 0x04000000 /* Summon Undead */ #define RF5_S_DRAGON 0x08000000 /* Summon Dragon */ #define RF5_S_HI_UNDEAD 0x10000000 /* Summon Greater Undead */ #define RF5_S_HI_DRAGON 0x20000000 /* Summon Ancient Dragon */ #define RF5_S_AMBERITES 0x40000000 /* Summon Amberites */ #define RF5_S_UNIQUE 0x80000000 /* Summon Unique Monster */ /* * New monster race bit flags */ #define RF6_AQUATIC 0x00000001 /* Aquatic monster */ #define RF6_CAN_SWIM 0x00000002 /* Monster can swim */ #define RF6_CAN_FLY 0x00000004 /* Monster can fly */ #define RF6_FRIENDLY 0x00000008 /* Monster is friendly */ #define RF6_SILLY 0x00000010 /* Monster is "silly" */ #define RF6_LITE_1 0x00000020 /* Monster carries a small lite */ #define RF6_LITE_2 0x00000040 /* Monster carries a large lite */ /* * Useful flag combinations */ #define RF7_DUNGEON 0xFFF00000 #define RF7_WILD 0x000700FF /* * Monster drop info */ #define RF8_DROP_CORPSE 0x00000001 #define RF8_DROP_SKELETON 0x00000002 /* * Legal restrictions for "summon_specific()" */ #define SUMMON_ANT 11 #define SUMMON_SPIDER 12 #define SUMMON_HOUND 13 #define SUMMON_HYDRA 14 #define SUMMON_ANGEL 15 #define SUMMON_DEMON 16 #define SUMMON_UNDEAD 17 #define SUMMON_DRAGON 18 #define SUMMON_HI_UNDEAD 21 #define SUMMON_HI_DRAGON 22 #define SUMMON_AMBERITES 31 #define SUMMON_UNIQUE 32 #define SUMMON_BIZARRE1 33 #define SUMMON_BIZARRE2 34 #define SUMMON_BIZARRE3 35 #define SUMMON_BIZARRE4 36 #define SUMMON_BIZARRE5 37 #define SUMMON_BIZARRE6 38 #define SUMMON_CYBER 39 #define SUMMON_KIN 40 #define SUMMON_DAWN 41 #define SUMMON_ANIMAL 42 #define SUMMON_ANIMAL_RANGER 43 #define SUMMON_HI_UNDEAD_NO_UNIQUES 44 #define SUMMON_HI_DRAGON_NO_UNIQUES 45 #define SUMMON_NO_UNIQUES 46 #define SUMMON_PHANTOM 47 #define SUMMON_ELEMENTAL 48 #define SUMMON_BLUE_HORROR 49 /***** Types *****/ struct monster_race { u32b flags[9]; /* Flags 1 (general) */ /* Flags 2 (abilities) */ /* Flags 3 (race/resist) */ /* Flags 4 (inate/breath) */ /* Flags 5 (normal spells) */ /* Flags 6 (special spells) */ /* Flags 7 (movement related abilities) */ /* Flags 8 (wilderness info) */ /* Flags 9 (drops info) */ char d_char; /* Default monster character */ byte level; /* Level of creature */ }; monster_race *r_info; /***** Functions *****/ /* monster2.c */ extern void summon_specific(int who, int x1, int y1, int lev, int type, bool group, bool friendly, bool pet); extern void summon_cloned_creature(int x1, int y1, int r_idx, bool pet); extern monster_race *monst_race(int r_idx); extern cptr *mon_race_name(monster_race *r_ptr); extern bool monster_can_open(monster_race *r_ptr, int power); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/l-object.pkg���������������������������������������������������������������������������0000644�0000000�0000000�00000034716�10250356275�014351� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" $#include "script.h" typedef char* cptr; typedef int errr; typedef int sint; /***** Constants *****/ /* * Special "xtra" object powers for ego items and some artifacts */ /* Sustain one stat */ #define EGO_XTRA_SUSTAIN 1 /* Resistances */ #define EGO_XTRA_LO_RESIST 2 #define EGO_XTRA_HI_RESIST 3 #define EGO_XTRA_ANY_RESIST 4 /* Special ability */ #define EGO_XTRA_ABILITY 5 /* Special ability OR high resist */ #define EGO_XTRA_POWER 6 /* * Use tvals for the item tester functions in stores */ #define TV_ANY 0 /* Used for matching all objects */ #define TV_SKELETON 1 /* Skeletons ('~') */ #define TV_BOTTLE 2 /* Empty bottles ('!') */ #define TV_JUNK 3 /* Sticks, Pottery, etc ('~') */ #define TV_SPIKE 5 /* Spikes ('~') */ #define TV_CHEST 7 /* Chests ('&') */ #define TV_FIGURINE 8 /* Magical figurines */ #define TV_STATUE 9 /* Statue */ /*#define TV_CORPSE 10 */ /* Corpses are now fields */ #define TV_SHOT 16 /* Ammo for slings */ #define TV_ARROW 17 /* Ammo for bows */ #define TV_BOLT 18 /* Ammo for x-bows */ #define TV_BOW 19 /* Slings/Bows/Xbows */ #define TV_DIGGING 20 /* Shovels/Picks */ #define TV_HAFTED 21 /* Priest Weapons */ #define TV_POLEARM 22 /* Axes and Pikes */ #define TV_SWORD 23 /* Edged Weapons */ #define TV_BOOTS 30 /* Boots */ #define TV_GLOVES 31 /* Gloves */ #define TV_HELM 32 /* Helms */ #define TV_CROWN 33 /* Crowns */ #define TV_SHIELD 34 /* Shields */ #define TV_CLOAK 35 /* Cloaks */ #define TV_SOFT_ARMOR 36 /* Soft Armor */ #define TV_HARD_ARMOR 37 /* Hard Armor */ #define TV_DRAG_ARMOR 38 /* Dragon Scale Mail */ #define TV_LITE 39 /* Lites (including Specials) */ #define TV_AMULET 40 /* Amulets (including Specials) */ #define TV_RING 45 /* Rings (including Specials) */ #define TV_STAFF 55 #define TV_WAND 65 #define TV_ROD 66 #define TV_SCROLL 70 #define TV_POTION 75 #define TV_FLASK 77 #define TV_FOOD 80 #define TV_LIFE_BOOK 90 #define TV_SORCERY_BOOK 91 #define TV_NATURE_BOOK 92 #define TV_CHAOS_BOOK 93 #define TV_DEATH_BOOK 94 #define TV_TRUMP_BOOK 95 #define TV_ARCANE_BOOK 96 #define TV_GOLD 100 /* Gold can only be picked up by players */ #define TV_BOOKS_MIN TV_LIFE_BOOK /* First tval of spellbooks */ #define TV_BOOKS_MAX TV_ARCANE_BOOK /* Last tval of spellbooks */ /* * Use sval codes for wand of wonder */ #define SV_WAND_HEAL_MONSTER 0 #define SV_WAND_HASTE_MONSTER 1 #define SV_WAND_CLONE_MONSTER 2 #define SV_WAND_TELEPORT_AWAY 3 #define SV_WAND_DISARMING 4 #define SV_WAND_TRAP_DOOR_DEST 5 #define SV_WAND_STONE_TO_MUD 6 #define SV_WAND_LITE 7 #define SV_WAND_SLEEP_MONSTER 8 #define SV_WAND_SLOW_MONSTER 9 #define SV_WAND_CONFUSE_MONSTER 10 #define SV_WAND_FEAR_MONSTER 11 #define SV_WAND_DRAIN_LIFE 12 #define SV_WAND_POLYMORPH 13 #define SV_WAND_STINKING_CLOUD 14 #define SV_WAND_MAGIC_MISSILE 15 #define SV_WAND_ACID_BOLT 16 #define SV_WAND_CHARM_MONSTER 17 #define SV_WAND_FIRE_BOLT 18 #define SV_WAND_COLD_BOLT 19 #define SV_WAND_ACID_BALL 20 #define SV_WAND_ELEC_BALL 21 #define SV_WAND_FIRE_BALL 22 #define SV_WAND_COLD_BALL 23 #define SV_WAND_WONDER 24 /* * As of 2.7.8, the "object flags" are valid for all objects, and as * of 2.7.9, these flags are not actually stored with the object. * * Note that "flags1" contains all flags dependant on "pval" (including * stat bonuses, but NOT stat sustainers), plus all "extra attack damage" * flags (SLAY_XXX and BRAND_XXX). * * Note that "flags2" contains all "resistances" (including "Stat Sustainers", * actual immunities, and resistances). Note that "Hold Life" is really an * "immunity" to ExpLoss, and "Free Action" is "immunity to paralysis". * * Note that "flags3" contains everything else -- including the three "CURSED" * flags, and the "BLESSED" flag, several "item display" parameters, some new * flags for powerful Bows, and flags which affect the player in a "general" * way (LITE, TELEPATHY, SEE_INVIS, SLOW_DIGEST, REGEN, FEATHER), including * all the "general" curses (TELEPORT, AGGRAVATE, EXP_DRAIN). It also has * four new flags called "ITEM_IGNORE_XXX" which lets an item specify that * it can not be affected by various forms of destruction. This is NOT as * powerful as actually granting resistance/immunity to the wearer. */ #define TR0_STR 0x00000001L /* STR += "pval" */ #define TR0_INT 0x00000002L /* INT += "pval" */ #define TR0_WIS 0x00000004L /* WIS += "pval" */ #define TR0_DEX 0x00000008L /* DEX += "pval" */ #define TR0_CON 0x00000010L /* CON += "pval" */ #define TR0_CHR 0x00000020L /* CHR += "pval" */ #define TR0_XXX1 0x00000040L /* Later */ #define TR0_SP 0x00000080L /* Extra mana */ #define TR0_STEALTH 0x00000100L /* Stealth += "pval" */ #define TR0_SEARCH 0x00000200L /* Search += "pval" */ #define TR0_INFRA 0x00000400L /* Infra += "pval" */ #define TR0_TUNNEL 0x00000800L /* Tunnel += "pval" */ #define TR0_SPEED 0x00001000L /* Speed += "pval" */ #define TR0_BLOWS 0x00002000L /* Blows += "pval" */ #define TR0_CHAOTIC 0x00004000L #define TR0_VAMPIRIC 0x00008000L #define TR0_SLAY_ANIMAL 0x00010000L #define TR0_SLAY_EVIL 0x00020000L #define TR0_SLAY_UNDEAD 0x00040000L #define TR0_SLAY_DEMON 0x00080000L #define TR0_SLAY_ORC 0x00100000L #define TR0_SLAY_TROLL 0x00200000L #define TR0_SLAY_GIANT 0x00400000L #define TR0_SLAY_DRAGON 0x00800000L #define TR0_KILL_DRAGON 0x01000000L /* Execute Dragon */ #define TR0_VORPAL 0x02000000L /* Later */ #define TR0_IMPACT 0x04000000L /* Cause Earthquakes */ #define TR0_BRAND_POIS 0x08000000L #define TR0_BRAND_ACID 0x10000000L #define TR0_BRAND_ELEC 0x20000000L #define TR0_BRAND_FIRE 0x40000000L #define TR0_BRAND_COLD 0x80000000L #define TR1_SUST_STR 0x00000001L #define TR1_SUST_INT 0x00000002L #define TR1_SUST_WIS 0x00000004L #define TR1_SUST_DEX 0x00000008L #define TR1_SUST_CON 0x00000010L #define TR1_SUST_CHR 0x00000020L #define TR1_XXX1 0x00000040L /* Later */ #define TR1_IM_POIS 0x00000080L #define TR1_IM_ACID 0x00000100L #define TR1_IM_ELEC 0x00000200L #define TR1_IM_FIRE 0x00000400L #define TR1_IM_COLD 0x00000800L #define TR1_THROW 0x00001000L /* Throwing items */ #define TR1_REFLECT 0x00002000L /* Reflect 'bolts' */ #define TR1_FREE_ACT 0x00004000L /* Free Action */ #define TR1_HOLD_LIFE 0x00008000L /* Hold Life */ #define TR1_RES_ACID 0x00010000L #define TR1_RES_ELEC 0x00020000L #define TR1_RES_FIRE 0x00040000L #define TR1_RES_COLD 0x00080000L #define TR1_RES_POIS 0x00100000L #define TR1_RES_FEAR 0x00200000L /* Added for Zangband */ #define TR1_RES_LITE 0x00400000L #define TR1_RES_DARK 0x00800000L #define TR1_RES_BLIND 0x01000000L #define TR1_RES_CONF 0x02000000L #define TR1_RES_SOUND 0x04000000L #define TR1_RES_SHARDS 0x08000000L #define TR1_RES_NETHER 0x10000000L #define TR1_RES_NEXUS 0x20000000L #define TR1_RES_CHAOS 0x40000000L #define TR1_RES_DISEN 0x80000000L #define TR2_SH_FIRE 0x00000001L /* Immolation (Fire) */ #define TR2_SH_ELEC 0x00000002L /* Electric Sheath */ #define TR2_QUESTITEM 0x00000004L /* quest level item -KMW- */ #define TR2_XXX4 0x00000008L /* Later */ #define TR2_NO_TELE 0x00000010L /* Anti-teleportation */ #define TR2_NO_MAGIC 0x00000020L /* Anti-magic */ #define TR2_XXX7 0x00000040L /* Later */ #define TR2_TY_CURSE 0x00000080L /* The Ancient Curse */ #define TR2_EASY_KNOW 0x00000100L /* Aware -> Known */ #define TR2_HIDE_TYPE 0x00000200L /* Hide "pval" description */ #define TR2_SHOW_MODS 0x00000400L /* Always show Tohit/Todam */ #define TR2_INSTA_ART 0x00000800L /* Item must be an artifact */ #define TR2_FEATHER 0x00001000L /* Feather Falling */ #define TR2_LITE 0x00002000L /* Permanent Light */ #define TR2_SEE_INVIS 0x00004000L /* See Invisible */ #define TR2_TELEPATHY 0x00008000L /* Telepathy */ #define TR2_SLOW_DIGEST 0x00010000L /* Item slows down digestion */ #define TR2_REGEN 0x00020000L /* Item induces regeneration */ #define TR2_XTRA_MIGHT 0x00040000L /* Bows get extra multiplier */ #define TR2_XTRA_SHOTS 0x00080000L /* Bows get extra shots */ #define TR2_IGNORE_ACID 0x00100000L /* Item ignores Acid Damage */ #define TR2_IGNORE_ELEC 0x00200000L /* Item ignores Elec Damage */ #define TR2_IGNORE_FIRE 0x00400000L /* Item ignores Fire Damage */ #define TR2_IGNORE_COLD 0x00800000L /* Item ignores Cold Damage */ #define TR2_ACTIVATE 0x01000000L /* Item can be activated */ #define TR2_DRAIN_EXP 0x02000000L /* Item drains Experience */ #define TR2_TELEPORT 0x04000000L /* Item teleports player */ #define TR2_AGGRAVATE 0x08000000L /* Item aggravates monsters */ #define TR2_BLESSED 0x10000000L /* Item is Blessed */ #define TR2_CURSED 0x20000000L /* Item is Cursed */ #define TR2_HEAVY_CURSE 0x40000000L /* Item is Heavily Cursed */ #define TR2_PERMA_CURSE 0x80000000L /* Item is Perma Cursed */ #define TR3_LUCK_10 0x00000001L #define TR3_WILD_SHOT 0x00000002L #define TR3_WILD_WALK 0x00000004L #define TR3_EASY_ENCHANT 0x00000008L #define TR3_XXX5 0x00000010L #define TR3_XXX6 0x00000020L #define TR3_XXX7 0x00000040L #define TR3_XXX8 0x00000080L #define TR3_IM_LITE 0x00000100L #define TR3_IM_DARK 0x00000200L #define TR3_SH_ACID 0x00000400L #define TR3_SH_COLD 0x00000800L #define TR3_MUTATE 0x00001000L #define TR3_PATRON 0x00002000L #define TR3_STRANGE_LUCK 0x00004000L #define TR3_PASS_WALL 0x00008000L #define TR3_GHOUL_TOUCH 0x00010000L #define TR3_PSI_CRIT 0x00020000L #define TR3_RETURN 0x00040000L #define TR3_EXPLODE 0x00080000L #define TR3_HURT_ACID 0x00100000L #define TR3_HURT_ELEC 0x00200000L #define TR3_HURT_FIRE 0x00400000L #define TR3_HURT_COLD 0x00800000L #define TR3_HURT_LITE 0x01000000L #define TR3_HURT_DARK 0x02000000L #define TR3_XXX27 0x04000000L #define TR3_XXX28 0x08000000L #define TR3_AUTO_CURSE 0x10000000L #define TR3_DRAIN_STATS 0x20000000L #define TR3_CANT_EAT 0x40000000L #define TR3_SLOW_HEAL 0x80000000L /***** Types *****/ struct object_type { s16b k_idx; /* Kind index (zero if "dead") */ s16b iy; /* Y-position on map, or zero */ s16b ix; /* X-position on map, or zero */ byte tval; /* Item type (from kind) */ byte sval; /* Item sub-type (from kind) */ s16b pval; /* Item extra-parameter */ byte discount; /* Discount (if any) */ byte number; /* Number of items */ s16b weight; /* Item weight */ s16b to_h; /* Plusses to hit */ s16b to_d; /* Plusses to damage */ s16b to_a; /* Plusses to AC */ s16b ac; /* Normal AC */ s16b timeout; /* Timeout Counter */ byte dd; /* Damage dice/sides */ byte ds; u16b inscription; /* Inscription index */ u16b xtra_name; /* Extra Name (Artifacts and ego items) */ u32b flags[4]; /* Flags */ u32b kn_flags[4]; /* Known Flags */ s16b next_o_idx; /* Next object in stack (if any) */ s32b cost; /* Object "base cost" */ byte feeling; /* Game generated inscription number (eg, pseudo-id) */ byte a_idx; /* Artifact number */ byte info; /* Special flags */ bool allocated; /* Held in the o_list[] array */ }; struct bonuses_type { int stat[6]; int sp_bonus; int skills[MAX_SKILL]; int see_infra; int pspeed; int extra_blows; int extra_shots; }; /***** Functions *****/ extern bool item_tester_hook_weapon(const object_type *o_ptr); extern bool item_tester_hook_melee_weapon(const object_type *o_ptr); extern bool item_tester_hook_nonsword(const object_type *o_ptr); extern bool item_tester_hook_ammo(const object_type *o_ptr); extern bool item_tester_hook_fletcher(const object_type *o_ptr); extern bool item_tester_hook_armour(const object_type *o_ptr); extern bool item_tester_hook_soft_armour(const object_type *o_ptr); extern bool item_tester_hook_hard_armour(const object_type *o_ptr); extern bool item_tester_hook_helm(const object_type *o_ptr); extern bool item_tester_hook_pure_hard_armour(const object_type *o_ptr); extern bool item_tester_hook_weapon_armour(const object_type *o_ptr); extern bool item_tester_hook_wear(const object_type *o_ptr); extern bool item_tester_hook_recharge(const object_type *o_ptr); extern bool item_tester_hook_jewel(const object_type *o_ptr); extern bool item_tester_hook_tval(const object_type *o_ptr, byte tval); extern bool item_tester_hook_is_blessed(const object_type *o_ptr); extern bool item_tester_hook_is_good(const object_type *o_ptr); extern bool item_tester_hook_is_great(const object_type *o_ptr); extern bool item_tester_hook_is_book(const object_type *o_ptr); extern void add_ego_flags(object_type *o_ptr, byte ego); extern void add_ego_power(int power, object_type *o_ptr); extern void acquirement(int x1, int y1, int num, bool great, bool known); extern void ring_of_power(int dir); ��������������������������������������������������zangband/src/l-player.pkg���������������������������������������������������������������������������0000644�0000000�0000000�00000024623�10250356275�014373� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" typedef char* cptr; typedef int errr; /***** Constants *****/ /* * Player class constants (hard-coded by save-files, arrays, etc) */ #define CLASS_WARRIOR 0 #define CLASS_MAGE 1 #define CLASS_PRIEST 2 #define CLASS_ROGUE 3 #define CLASS_RANGER 4 #define CLASS_PALADIN 5 #define CLASS_WARRIOR_MAGE 6 #define CLASS_CHAOS_WARRIOR 7 #define CLASS_MONK 8 #define CLASS_MINDCRAFTER 9 #define CLASS_HIGH_MAGE 10 /* * Indexes of the various "stats" (hard-coded by savefiles, etc). */ #define A_STR 0 #define A_INT 1 #define A_WIS 2 #define A_DEX 3 #define A_CON 4 #define A_CHR 5 /* * Total number of stats. */ #define A_MAX 6 /* * Player sex constants (hard-coded by save-files, arrays, etc) */ #define SEX_FEMALE 0 #define SEX_MALE 1 /* * Player constants */ #define PY_MAX_EXP 99999999L /* Maximum exp */ #define PY_MAX_GOLD 999999999L /* Maximum gold */ #define PY_MAX_LEVEL 50 /* Maximum level */ /* * Player "food" crucial values */ #define PY_FOOD_MAX 15000 /* Food value (Bloated) */ #define PY_FOOD_FULL 10000 /* Food value (Normal) */ #define PY_FOOD_ALERT 2000 /* Food value (Hungry) */ #define PY_FOOD_WEAK 1000 /* Food value (Weak) */ #define PY_FOOD_FAINT 500 /* Food value (Fainting) */ #define PY_FOOD_STARVE 100 /* Food value (Starving) */ /***** Types *****/ struct player_data { s16b age; /* Characters age */ s16b ht; /* Height */ s16b wt; /* Weight */ s16b sc; /* Social Class */ byte hitdie; /* Hit dice (sides) */ byte psex; /* Sex index */ byte prace; /* Race index */ byte pclass; /* Class index */ }; struct player_realm { u32b learned; u32b worked; u32b forgotten; byte realm; }; struct player_spell { player_realm r[2]; byte order[PY_MAX_SPELLS]; /* Spell order */ }; struct player_timed { s16b fast; /* Timed -- Fast */ s16b slow; /* Timed -- Slow */ s16b blind; /* Timed -- Blindness */ s16b paralyzed; /* Timed -- Paralysis */ s16b confused; /* Timed -- Confusion */ s16b afraid; /* Timed -- Fear */ s16b image; /* Timed -- Hallucination */ s16b poisoned; /* Timed -- Poisoned */ s16b cut; /* Timed -- Cut */ s16b stun; /* Timed -- Stun */ s16b protevil; /* Timed -- Protection */ s16b invuln; /* Timed -- Invulnerable */ s16b hero; /* Timed -- Heroism */ s16b shero; /* Timed -- Super Heroism */ s16b shield; /* Timed -- Shield Spell */ s16b blessed; /* Timed -- Blessed */ s16b invis; /* Timed -- See Invisible */ s16b infra; /* Timed -- Infra Vision */ s16b oppose_acid; /* Timed -- oppose acid */ s16b oppose_elec; /* Timed -- oppose lightning */ s16b oppose_fire; /* Timed -- oppose heat */ s16b oppose_cold; /* Timed -- oppose cold */ s16b oppose_pois; /* Timed -- oppose poison */ s16b esp; /* Timed ESP */ s16b wraith_form; /* Timed wraithform */ s16b resist_magic; /* Timed Resist Magic (later) */ s16b word_recall; /* Word of recall counter */ }; struct player_state { char died_from[80]; /* Cause of death */ s16b resting; /* Resting counter */ s16b running; /* Running counter */ byte confusing; /* Glowing hands */ byte searching; /* Currently searching */ u16b total_winner; /* Total winner */ u16b panic_save; /* Panic save */ u16b noscore; /* Cheating flags */ bool is_dead; /* Player is dead */ bool wizard; /* Player is in wizard mode */ bool playing; /* True if player is playing */ bool leaving; /* True if player is leaving */ bool create_up_stair; /* Create up stair on next level */ bool create_down_stair; /* Create down stair on next level */ s16b energy_use; /* Energy use this turn */ bool cumber_armor; /* Mana draining armor */ bool cumber_glove; /* Mana draining gloves */ bool heavy_wield; /* Heavy weapon */ bool heavy_shoot; /* Heavy shooter */ bool icky_wield; /* Icky weapon */ bool detected; /* Detected for traps? */ bool skip_more; /* Skip the --more-- prompt */ bool mon_fight; /* Monster fighting indicator */ bool monk_armour_stat; /* Status of monk armour */ byte noise_level; /* Amount of noise since last update */ u16b store_top; /* Top of store inventory list */ }; /* Skills */ #define MAX_SKILL 10 #define SKILL_DIS 0 /* Skill: Disarming */ #define SKILL_DEV 1 /* Skill: Magic Devices */ #define SKILL_SAV 2 /* Skill: Saving throw */ #define SKILL_STL 3 /* Skill: Stealth factor */ #define SKILL_SNS 4 /* Skill: Sensing ability */ #define SKILL_FOS 5 /* Skill: Searching frequency */ #define SKILL_THN 6 /* Skill: To hit (normal) */ #define SKILL_THB 7 /* Skill: To hit (shooting) */ #define SKILL_THT 8 /* Skill: To hit (throwing) */ #define SKILL_DIG 9 /* Skill: Digging */ struct player_stat { s16b max; /* Current "maximal" stat values */ s16b cur; /* Current "natural" stat values */ s16b use; /* Current modified stats */ s16b top; /* Maximal modified stats */ s16b add; /* Equipment stat bonuses */ s16b ind; /* Indexes into stat tables */ }; struct player_type { s16b px; /* Player location */ s16b py; /* Player location */ player_data rp; /* Role-play information */ s16b depth; /* Cur depth */ s16b max_lev; /* Max level */ s16b lev; /* Cur level */ u16b exp_frac; /* Cur exp frac (times 2^16) */ s32b max_exp; /* Max experience */ s32b exp; /* Cur experience */ s32b au; /* Current Gold */ s16b place_num; /* Current place number in the wilderness */ s32b wilderness_x; /* Coordinates in the wilderness */ s32b wilderness_y; s16b mhp; /* Max hit pts */ s16b chp; /* Cur hit pts */ u16b chp_frac; /* Cur hit frac (times 2^16) */ s16b msp; /* Max mana pts */ s16b csp; /* Cur mana pts */ u16b csp_frac; /* Cur mana frac (times 2^16) */ player_spell spell; /* Spell information */ u32b muta1; /* Mutations */ u32b muta2; /* Mutations */ u32b muta3; /* Mutations */ s16b virtues[MAX_PLAYER_VIRTUES]; s16b vir_types[MAX_PLAYER_VIRTUES]; s16b chaos_patron; /* Players Chaos Patron */ s16b energy; /* Current energy */ s16b food; /* Current nutrition */ s16b player_hp[PY_MAX_LEVEL]; /* HP Array */ u16b expfact; /* Experience factor * Note: was byte, causing overflow for Amberite * characters (such as Amberite Paladins) */ player_timed tim; /* Timed effects */ player_state state; /* Internal state of the player */ s16b skills[MAX_SKILL]; /* Player skills */ player_stat stat[A_MAX]; /* The player's stats */ int sp_bonus; /* Bonus MP */ /*** Temporary fields ***/ s32b align; /* Good/evil/neutral */ s16b total_weight; /* Total weight being carried */ s16b target_set; /* Target flag */ s16b target_who; /* Target identity */ s16b target_row; /* Target location */ s16b target_col; /* Target location */ s16b health_who; /* Health bar trackee */ s16b monster_race_idx; /* Monster race trackee */ u16b max_seen_r_idx; /* Most powerful monster visible */ s16b object_kind_idx; /* Object kind trackee */ s16b new_spells; /* Number of spells available */ s16b cur_lite; /* Radius of lite (if any) */ u32b notice; /* Special Updates (bit flags) */ u32b update; /* Pending Updates (bit flags) */ u32b redraw; /* Normal Redraws (bit flags) */ u32b window; /* Window Redraws (bit flags) */ /* * Flags on equipment items and the racial/class * effects logical-ored together. */ u32b flags[4]; /*** Extracted fields ***/ s16b dis_to_h; /* Known bonus to hit */ s16b dis_to_d; /* Known bonus to dam */ s16b dis_to_a; /* Known bonus to ac */ s16b dis_ac; /* Known base ac */ s16b to_h; /* Bonus to hit */ s16b to_d; /* Bonus to dam */ s16b to_a; /* Bonus to ac */ s16b ac; /* Base ac */ s16b see_infra; /* Infravision range */ u32b noise; /* Derived from stealth */ s16b num_blow; /* Number of blows */ s16b num_fire; /* Number of shots */ byte ammo_mult; /* Ammo multiplier */ byte ammo_tval; /* Ammo variety */ byte bow_energy; /* shooter speed */ s16b pspeed; /* Current speed */ /*** Pet commands ***/ s16b pet_follow_distance; /* Length of the imaginary "leash" for pets */ byte pet_open_doors; /* flag - allow pets to open doors */ byte pet_pickup_items; /* flag - allow pets to pickup items */ /* Options */ bool options[OPT_PLAYER]; bool birth[OPT_BIRTH]; }; /***** Variables *****/ extern player_type *p_ptr @ player; /* Hack - options */ extern bool ironman_nightmare; extern bool disturb_minor; extern const byte adj_str_wgt[40]; /***** Functions *****/ extern bool inc_blind(int v); extern bool clear_blind(void); extern bool inc_confused(int v); extern bool clear_confused(void); extern bool inc_poisoned(int v); extern bool clear_poisoned(void); extern bool inc_afraid(int v); extern bool clear_afraid(void); extern bool inc_paralyzed(int v); extern bool clear_paralyzed(void); extern bool inc_image(int v); extern bool clear_image(void); extern bool inc_fast(int v); extern bool clear_fast(void); extern bool inc_slow(int v); extern bool clear_slow(void); extern bool inc_shield(int v); extern bool inc_blessed(int v); extern bool inc_hero(int v); extern bool inc_shero(int v); extern bool inc_protevil(int v); extern bool inc_wraith_form(int v); extern bool inc_tim_esp(int v); extern bool clear_tim_esp(void); extern bool inc_invuln(int v); extern bool inc_tim_invis(int v); extern bool inc_tim_infra(int v); extern bool inc_oppose_acid(int v); extern bool inc_oppose_elec(int v); extern bool inc_oppose_fire(int v); extern bool inc_oppose_cold(int v); extern bool inc_oppose_pois(int v); extern byte res_acid_lvl(void); extern byte res_elec_lvl(void); extern byte res_fire_lvl(void); extern byte res_cold_lvl(void); extern byte res_pois_lvl(void); extern bool inc_stun(int v); extern bool clear_stun(void); extern bool inc_cut(int v); extern bool clear_cut(void); extern bool set_food(int v); extern void gain_exp(s32b amount); extern void lose_exp(s32b amount); extern bool hp_player(int num); extern bool do_dec_stat(int stat); extern bool do_res_stat(int stat); extern bool do_inc_stat(int stat); extern void take_hit(int dam, cptr kb_str); extern bool inc_stat(int stat); extern bool dec_stat(int stat, int amount, int permanent); extern bool res_stat(int stat); extern bool lose_all_info(void); extern bool restore_level(void); extern bool player_res(u32b flag); extern void have_nightmare(void); extern void disturb(bool stop_search); extern void move_dun_level(int direction); extern int count_mutations(void); extern bool gain_mutation(int choose_mut); extern bool lose_mutation(int choose_mut); �������������������������������������������������������������������������������������������������������������zangband/src/l-random.pkg���������������������������������������������������������������������������0000644�0000000�0000000�00000000625�10250356275�014353� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" typedef char* cptr; typedef int errr; typedef unsigned int uint; /***** Functions *****/ extern s32b randint0(u32b m); extern s32b randint1(u32b m); extern s32b rand_range(u32b A, u32b B); extern s32b rand_spread(u32b A, u32b D); extern uint damroll(uint num, uint sides); extern u32b one_in_(u32b m); extern bool saving_throw(s32b m); extern s16b m_bonus(int max, int level); �����������������������������������������������������������������������������������������������������������zangband/src/l-spell.pkg����������������������������������������������������������������������������0000644�0000000�0000000�00000017307�10250356275�014217� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" typedef char* cptr; typedef int errr; /***** Constants *****/ /* * Spell types used by project(), and related functions. */ #define GF_ELEC 1 #define GF_POIS 2 #define GF_ACID 3 #define GF_COLD 4 #define GF_FIRE 5 #define GF_MISSILE 10 #define GF_ARROW 11 #define GF_PLASMA 12 /* Replaced with GF_HOLY_FIRE and GF_HELL_FIRE */ /* #define GF_HOLY_ORB 13 */ #define GF_WATER 14 #define GF_LITE 15 #define GF_DARK 16 #define GF_LITE_WEAK 17 #define GF_DARK_WEAK 18 #define GF_SHARDS 20 #define GF_SOUND 21 #define GF_CONFUSION 22 #define GF_FORCE 23 #define GF_INERTIA 24 #define GF_MANA 26 #define GF_METEOR 27 #define GF_ICE 28 #define GF_CHAOS 30 #define GF_NETHER 31 #define GF_DISENCHANT 32 #define GF_NEXUS 33 #define GF_TIME 34 #define GF_GRAVITY 35 #define GF_KILL_WALL 40 #define GF_KILL_DOOR 41 #define GF_KILL_TRAP 42 #define GF_MAKE_WALL 45 #define GF_MAKE_DOOR 46 #define GF_MAKE_TRAP 47 #define GF_OLD_CLONE 51 #define GF_OLD_POLY 52 #define GF_OLD_HEAL 53 #define GF_OLD_SPEED 54 #define GF_OLD_SLOW 55 #define GF_OLD_CONF 56 #define GF_OLD_SLEEP 57 #define GF_OLD_DRAIN 58 #define GF_NEW_DRAIN 59 #define GF_AWAY_UNDEAD 61 #define GF_AWAY_EVIL 62 #define GF_AWAY_ALL 63 #define GF_TURN_UNDEAD 64 #define GF_TURN_EVIL 65 #define GF_TURN_ALL 66 #define GF_DISP_UNDEAD 67 #define GF_DISP_EVIL 68 #define GF_DISP_ALL 69 #define GF_DISP_DEMON 70 /* New types for Zangband begin here... */ #define GF_DISP_LIVING 71 #define GF_ROCKET 72 #define GF_NUKE 73 #define GF_MAKE_GLYPH 74 #define GF_STASIS 75 #define GF_STONE_WALL 76 #define GF_DEATH_RAY 77 #define GF_STUN 78 #define GF_HOLY_FIRE 79 #define GF_HELL_FIRE 80 #define GF_DISINTEGRATE 81 #define GF_CHARM 82 #define GF_CONTROL_UNDEAD 83 #define GF_CONTROL_ANIMAL 84 #define GF_PSI 85 #define GF_PSI_DRAIN 86 #define GF_TELEKINESIS 87 #define GF_JAM_DOOR 88 #define GF_DOMINATION 89 #define GF_DISP_GOOD 90 #define MAX_GF 91 #define PROJECT_JUMP 0x0001 #define PROJECT_BEAM 0x0002 #define PROJECT_THRU 0x0004 #define PROJECT_STOP 0x0008 #define PROJECT_GRID 0x0010 #define PROJECT_ITEM 0x0020 #define PROJECT_KILL 0x0040 #define PROJECT_HIDE 0x0080 #define PROJECT_FRND 0x0100 #define PROJECT_MFLD 0x0200 /***** Types *****/ /***** Variables *****/ /***** Functions *****/ /* spells1.c */ extern bool project(int who, int rad, int x, int y, int dam, int typ, u16b flg); /* spells2.c */ extern void self_knowledge(void); extern bool detect_traps(bool ident); extern bool detect_doors(void); extern bool detect_stairs(void); extern bool detect_treasure(void); extern bool detect_objects_gold(void); extern bool detect_objects_normal(void); extern bool detect_objects_magic(void); extern bool detect_monsters_normal(void); extern bool detect_monsters_invis(void); extern bool detect_monsters_evil(void); extern bool detect_monsters_xxx(u32b match_flag); extern bool detect_monsters_string(cptr); extern bool detect_monsters_nonliving(void); extern bool detect_monsters_living(void); extern bool detect_all(void); extern bool wall_stone(void); extern bool speed_monsters(void); extern bool slow_monsters(void); extern bool sleep_monsters(void); extern void aggravate_monsters(int who); extern bool genocide(int player_cast); extern bool mass_genocide(int player_cast); extern bool probing(void); extern bool banish_evil(int dist); extern bool dispel_evil(int dam); extern bool dispel_good(int dam); extern bool dispel_undead(int dam); extern bool dispel_monsters(int dam); extern bool dispel_living(int dam); extern bool dispel_demons(int dam); extern bool destroy_area(int x1, int y1, int r); extern bool earthquake(int cx, int cy, int r); extern void lite_room(int x1, int y1); extern void unlite_room(int x1, int y1); extern bool lite_area(int dam, int rad); extern bool unlite_area(int dam, int rad); extern bool fire_ball(int typ, int dir, int dam, int rad); extern bool fire_bolt(int typ, int dir, int dam); extern void call_chaos(void); extern bool fire_beam(int typ, int dir, int dam); extern bool fire_bolt_or_beam(int prob, int typ, int dir, int dam); extern bool lite_line(int dir, int dam); extern bool drain_life(int dir, int dam); extern bool drain_gain_life(int dor, int dam); extern bool death_ray(int dir, int plev); extern bool wall_to_mud(int dir); extern bool destroy_door(int dir); extern bool disarm_trap(int dir); extern bool wizard_lock(int dir); extern bool heal_monster(int dir); extern bool speed_monster(int dir); extern bool slow_monster(int dir); extern bool sleep_monster(int dir); extern bool stasis_monster(int dir); /* Like sleep, affects undead as well */ extern bool confuse_monster(int dir, int plev); extern bool stun_monster(int dir, int plev); extern bool fear_monster(int dir, int plev); extern bool poly_monster(int dir); extern bool clone_monster(int dir); extern bool teleport_monster(int dir); extern bool door_creation(void); extern bool trap_creation(void); extern bool glyph_creation(void); extern bool destroy_doors_touch(void); extern bool sleep_monsters_touch(void); extern bool confuse_monsters(int dam); extern bool charm_monsters(int dam); extern bool charm_animals(int dam); extern bool starlite(void); extern bool scatter_ball(int num, int typ, int dam, int rad); extern void create_food(void); extern void whirlwind_attack(void); extern bool stun_monsters(int dam); extern bool stasis_monsters(int dam); extern bool banish_monsters(int dist); extern bool turn_monsters(int dam); extern bool turn_evil(int dam); extern bool deathray_monsters(void); extern bool charm_monster(int dir, int plev); extern bool control_one_undead(int dir, int plev); extern bool charm_animal(int dir, int plev); extern bool mindblast_monsters(int dam); extern bool teleport_swap(int dir); /* spells3.c */ extern bool teleport_away(int m_idx, int dis); extern void teleport_to_player(int m_idx); extern void teleport_player(int dis); extern void teleport_player_to(int nx, int ny); extern void teleport_player_level(void); extern void recall_player(int turns); extern void word_of_recall(void); extern bool apply_disenchant(void); extern void mutate_player(void); extern void phlogiston(void); extern void brand_weapon(int brand_type); extern void call_the_(void); extern void fetch(int dir, int wgt, bool require_los); extern void alter_reality(void); extern bool warding_glyph(void); extern bool explosive_rune(void); extern void identify_pack(void); extern bool remove_curse(void); extern bool remove_all_curse(void); extern bool alchemy(void); extern void stair_creation(void); extern bool enchant(object_type *o_ptr, int n, int eflag); extern bool enchant_spell(int num_hit, int num_dam, int num_ac); extern bool artifact_scroll(void); extern bool ident_spell(void); extern bool ident_scroll(int k_idx); extern bool mundane_spell(void); extern void identify_item(object_type *o_ptr); extern bool identify_fully(void); extern bool recharge(int num); extern bool bless_weapon(void); extern bool acid_dam(int dam, cptr kb_str); extern bool elec_dam(int dam, cptr kb_str); extern bool fire_dam(int dam, cptr kb_str); extern bool cold_dam(int dam, cptr kb_str); extern bool pois_dam(int dam, cptr kb_str, int pois); extern bool rustproof(void); extern bool curse_armor(void); extern bool curse_weapon(void); extern bool brand_bolts(void); extern bool polymorph_monster(int x, int y); extern bool dimension_door(void); extern void map_wilderness(int radius, s32b x, s32b y); extern void map_area(void); extern void wiz_lite(void); extern void wiz_dark(void); /* cmd6.c */ extern void do_cmd_rerate(void); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/l-ui.pkg�������������������������������������������������������������������������������0000644�0000000�0000000�00000020251�10250356275�013505� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$#include "angband.h" typedef char* cptr; typedef int errr; /***** Constants *****/ #define TERM_DARK 0 /* 'd' */ /* 0,0,0 */ #define TERM_WHITE 1 /* 'w' */ /* 4,4,4 */ #define TERM_SLATE 2 /* 's' */ /* 2,2,2 */ #define TERM_ORANGE 3 /* 'o' */ /* 4,2,0 */ #define TERM_RED 4 /* 'r' */ /* 3,0,0 */ #define TERM_GREEN 5 /* 'g' */ /* 0,2,1 */ #define TERM_BLUE 6 /* 'b' */ /* 0,0,4 */ #define TERM_UMBER 7 /* 'u' */ /* 2,1,0 */ #define TERM_L_DARK 8 /* 'D' */ /* 1,1,1 */ #define TERM_L_WHITE 9 /* 'W' */ /* 3,3,3 */ #define TERM_VIOLET 10 /* 'v' */ /* 4,0,4 */ #define TERM_YELLOW 11 /* 'y' */ /* 4,4,0 */ #define TERM_L_RED 12 /* 'R' */ /* 4,0,0 */ #define TERM_L_GREEN 13 /* 'G' */ /* 0,4,0 */ #define TERM_L_BLUE 14 /* 'B' */ /* 0,4,4 */ #define TERM_L_UMBER 15 /* 'U' */ /* 3,2,1 */ /* * Mega-Hack -- some primitive sound support (see "main-win.c") * * Some "sound" constants for "Term_xtra(TERM_XTRA_SOUND, val)" */ #define SOUND_NONE 0 #define SOUND_HIT 1 #define SOUND_MISS 2 #define SOUND_FLEE 3 #define SOUND_DROP 4 #define SOUND_KILL 5 #define SOUND_LEVEL 6 #define SOUND_DEATH 7 #define SOUND_STUDY 8 #define SOUND_TELEPORT 9 #define SOUND_SHOOT 10 #define SOUND_QUAFF 11 #define SOUND_ZAP 12 #define SOUND_WALK 13 #define SOUND_TPOTHER 14 #define SOUND_HITWALL 15 #define SOUND_EAT 16 #define SOUND_STORE1 17 #define SOUND_STORE2 18 #define SOUND_STORE3 19 #define SOUND_STORE4 20 #define SOUND_DIG 21 #define SOUND_OPENDOOR 22 #define SOUND_SHUTDOOR 23 #define SOUND_TPLEVEL 24 #define SOUND_SCROLL 25 #define SOUND_BUY 26 #define SOUND_SELL 27 #define SOUND_WARN 28 #define SOUND_ROCKET 29 /* Somebody's shooting rockets */ #define SOUND_N_KILL 30 /* The player kills a non-living/undead monster */ #define SOUND_U_KILL 31 /* The player kills a unique */ #define SOUND_QUEST 32 /* The player has just completed a quest */ #define SOUND_HEAL 33 /* The player was healed a little bit */ #define SOUND_X_HEAL 34 /* The player was healed full health */ #define SOUND_BITE 35 /* A monster bites you */ #define SOUND_CLAW 36 /* A monster claws you */ #define SOUND_M_SPELL 37 /* A monster casts a miscellaneous spell */ #define SOUND_SUMMON 38 /* A monster casts a summoning spell */ #define SOUND_BREATH 39 /* A monster breathes */ #define SOUND_BALL 40 /* A monster casts a ball / bolt spell */ #define SOUND_M_HEAL 41 /* A monster heals itself somehow */ #define SOUND_ATK_SPELL 42 /* A monster casts a misc. offensive spell */ #define SOUND_EVIL 43 /* Something nasty has just happened! */ #define SOUND_TOUCH 44 /* A monster touches you */ #define SOUND_STING 45 /* A monster stings you */ #define SOUND_CRUSH 46 /* A monster crushes / envelopes you */ #define SOUND_SLIME 47 /* A monster drools/spits/etc on you */ #define SOUND_WAIL 48 /* A monster wails */ #define SOUND_WINNER 49 /* Just won the game! */ #define SOUND_FIRE 50 /* An item was burned */ #define SOUND_ACID 51 /* An item was destroyed by acid */ #define SOUND_ELEC 52 /* An item was destroyed by electricity */ #define SOUND_COLD 53 /* An item was shattered */ #define SOUND_ILLEGAL 54 /* Illegal command attempted */ #define SOUND_FAIL 55 /* Fail to get a spell off / activate an item */ #define SOUND_WAKEUP 56 /* A monster wakes up */ #define SOUND_INVULN 57 /* Invulnerability! */ #define SOUND_FALL 58 /* Falling through a trapdoor... */ #define SOUND_PAIN 59 /* A monster is in pain! */ #define SOUND_DESTITEM 60 /* An item was destroyed by misc. means */ #define SOUND_MOAN 61 /* A monster makes a moan/beg/insult attack */ #define SOUND_SHOW 62 /* A monster makes a "show" attack */ #define SOUND_UNUSED 63 /* (no sound for gaze attacks) */ #define SOUND_EXPLODE 64 /* Something (or somebody) explodes */ /* * Mega-Hack -- maximum known sounds */ #define SOUND_MAX 65 /* * Bit flags for the "p_ptr->update" variable */ #define PU_BONUS 0x00000001L /* Calculate bonuses */ #define PU_TORCH 0x00000002L /* Calculate torch radius */ /* xxx (many) */ #define PU_HP 0x00000010L /* Calculate chp and mhp */ #define PU_MANA 0x00000020L /* Calculate csp and msp */ #define PU_SPELLS 0x00000040L /* Calculate spells */ /* xxx (many) */ /* xxx (many) */ /* xxx (many) */ #define PU_VIEW 0x00100000L /* Update view */ #define PU_MON_LITE 0x00200000L /* Monster illumination */ /* xxx */ #define PU_MONSTERS 0x01000000L /* Update monsters */ #define PU_DISTANCE 0x02000000L /* Update distances */ /* xxx */ #define PU_FLOW 0x10000000L /* Update flow */ /* xxx (many) */ /* * Bit flags for the "p_ptr->redraw" variable */ #define PR_MISC 0x00000001L /* Display Race/Class */ #define PR_TITLE 0x00000002L /* Display Title */ #define PR_LEV 0x00000004L /* Display Level */ #define PR_EXP 0x00000008L /* Display Experience */ #define PR_STATS 0x00000010L /* Display Stats */ #define PR_ARMOR 0x00000020L /* Display Armor */ #define PR_HP 0x00000040L /* Display Hitpoints */ #define PR_MANA 0x00000080L /* Display Mana */ #define PR_GOLD 0x00000100L /* Display Gold */ #define PR_DEPTH 0x00000200L /* Display Depth */ #define PR_EQUIPPY 0x00000400L /* Display equippy chars */ #define PR_HEALTH 0x00000800L /* Display Health Bar */ #define PR_CUT 0x00001000L /* Display Extra (Cut) */ #define PR_STUN 0x00002000L /* Display Extra (Stun) */ #define PR_HUNGER 0x00004000L /* Display Extra (Hunger) */ #define PR_STATUS 0x00008000L /* Display Status Bar */ #define PR_BLIND 0x00010000L /* Display Extra (Blind) */ #define PR_CONFUSED 0x00020000L /* Display Extra (Confused) */ #define PR_AFRAID 0x00040000L /* Display Extra (Afraid) */ #define PR_POISONED 0x00080000L /* Display Extra (Poisoned) */ #define PR_STATE 0x00100000L /* Display Extra (State) */ #define PR_SPEED 0x00200000L /* Display Extra (Speed) */ #define PR_STUDY 0x00400000L /* Display Extra (Study) */ /* xxx */ #define PR_EXTRA 0x01000000L /* Display Extra Info */ #define PR_BASIC 0x02000000L /* Display Basic Info */ #define PR_MAP 0x04000000L /* Display Map */ #define PR_WIPE 0x08000000L /* Hack -- Total Redraw */ /* xxx */ /* xxx */ /* xxx */ /* xxx */ /* * Bit flags for the "p_ptr->window" variable (etc) */ #define PW_INVEN 0x00000001L /* Display inven/equip */ #define PW_EQUIP 0x00000002L /* Display equip/inven */ #define PW_SPELL 0x00000004L /* Display spell list */ #define PW_PLAYER 0x00000008L /* Display character */ #define PW_SCRIPT_VARS 0x00000010L /* Display script messages */ #define PW_SCRIPT_SOURCE 0x00000020L /* Display script messages */ #define PW_MESSAGE 0x00000040L /* Display messages */ #define PW_OVERHEAD 0x00000080L /* Display overhead view */ #define PW_MONSTER 0x00000100L /* Display monster recall */ #define PW_OBJECT 0x00000200L /* Display object recall */ #define PW_DUNGEON 0x00000400L /* Display dungeon view */ #define PW_SNAPSHOT 0x00000800L /* Display snap-shot */ #define PW_VISIBLE 0x00001000L /* Display monster visible list */ #define PW_BORG_1 0x00002000L /* Display borg messages */ #define PW_BORG_2 0x00004000L /* Display borg status */ /***** Types *****/ /***** Variables *****/ /***** Functions *****/ extern void update_stuff(void); extern void redraw_stuff(void); extern void window_stuff(void); extern void handle_stuff(void); extern void message_flush(void); extern void msgf(cptr fmt); extern bool get_check(cptr prompt); extern void sound(int num); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/src/autoconf.h.in��������������������������������������������������������������������������0000644�0000000�0000000�00000020343�10250356275�014532� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* src/autoconf.h.in. Generated from configure.in by autoheader. */ /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT /* Define to 1 if you have the <fcntl.h> header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the `floor' function. */ #undef HAVE_FLOOR /* Define to 1 if you have the `ftruncate' function. */ #undef HAVE_FTRUNCATE /* Define to 1 if you have the `gethostname' function. */ #undef HAVE_GETHOSTNAME /* Define to 1 if you have the `getpagesize' function. */ #undef HAVE_GETPAGESIZE /* Define to 1 if you have the `getpwnam' function. */ #undef HAVE_GETPWNAM /* Define to 1 if you have the `getpwuid' function. */ #undef HAVE_GETPWUID /* Define to 1 if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `bsd' library (-lbsd). */ #undef HAVE_LIBBSD /* Define to 1 if you have the `cposix' library (-lcposix). */ #undef HAVE_LIBCPOSIX /* Define to 1 if you have the `Gn' library (-lGn). */ #undef HAVE_LIBGN /* Define to 1 if you have the `ICE' library (-lICE). */ #undef HAVE_LIBICE /* Define to 1 if you have the `inet' library (-linet). */ #undef HAVE_LIBINET /* Define to 1 if you have the `nsl_s' library (-lnsl_s). */ #undef HAVE_LIBNSL_S /* Define to 1 if you have the `rpcsvc' library (-lrpcsvc). */ #undef HAVE_LIBRPCSVC /* Define to 1 if you have the `SM' library (-lSM). */ #undef HAVE_LIBSM /* Define to 1 if you have the `sun' library (-lsun). */ #undef HAVE_LIBSUN /* Define to 1 if you have the `video' library (-lvideo). */ #undef HAVE_LIBVIDEO /* Define to 1 if you have the `winmm' library (-lwinmm). */ #undef HAVE_LIBWINMM /* Define to 1 if you have the `Xext' library (-lXext). */ #undef HAVE_LIBXEXT /* Define to 1 if you have the `Xm' library (-lXm). */ #undef HAVE_LIBXM /* Define to 1 if you have the `Xmu' library (-lXmu). */ #undef HAVE_LIBXMU /* Define to 1 if you have the `Xpm' library (-lXpm). */ #undef HAVE_LIBXPM /* Define to 1 if you have the `Xt' library (-lXt). */ #undef HAVE_LIBXT /* Define to 1 if you have the `z' library (-lz). */ #undef HAVE_LIBZ /* Define to 1 if you have the <limits.h> header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the <locale.h> header file. */ #undef HAVE_LOCALE_H /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #undef HAVE_MALLOC /* Define to 1 if you have the `memchr' function. */ #undef HAVE_MEMCHR /* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE /* Define to 1 if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the `mkdir' function. */ #undef HAVE_MKDIR /* Define to 1 if you have the `mkstemp' function. */ #undef HAVE_MKSTEMP /* Define to 1 if you have a working `mmap' system call. */ #undef HAVE_MMAP /* Define to 1 if you have the `modf' function. */ #undef HAVE_MODF /* Define to 1 if you have the <netdb.h> header file. */ #undef HAVE_NETDB_H /* Define to 1 if your system has a GNU libc compatible `realloc' function, and to 0 otherwise. */ #undef HAVE_REALLOC /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT /* Define to 1 if you have the `setlocale' function. */ #undef HAVE_SETLOCALE /* Define to 1 if you have the `shmget' function. */ #undef HAVE_SHMGET /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ #undef HAVE_STAT_EMPTY_STRING_BUG /* Define to 1 if you have the <stddef.h> header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the <stdlib.h> header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR /* Define to 1 if you have the `strcoll' function and it is properly defined. */ #undef HAVE_STRCOLL /* Define to 1 if you have the `strcspn' function. */ #undef HAVE_STRCSPN /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the `strftime' function. */ #undef HAVE_STRFTIME /* Define to 1 if you have the <strings.h> header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the <string.h> header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strpbrk' function. */ #undef HAVE_STRPBRK /* Define to 1 if you have the `strrchr' function. */ #undef HAVE_STRRCHR /* Define to 1 if you have the `strstr' function. */ #undef HAVE_STRSTR /* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL /* Define to 1 if you have the `strtoul' function. */ #undef HAVE_STRTOUL /* Define to 1 if you have the <sys/file.h> header file. */ #undef HAVE_SYS_FILE_H /* Define to 1 if you have the <sys/ioctl.h> header file. */ #undef HAVE_SYS_IOCTL_H /* Define to 1 if you have the <sys/ipc.h> header file. */ #undef HAVE_SYS_IPC_H /* Define to 1 if you have the <sys/param.h> header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the <sys/select.h> header file. */ #undef HAVE_SYS_SELECT_H /* Define to 1 if you have the <sys/shm.h> header file. */ #undef HAVE_SYS_SHM_H /* Define to 1 if you have the <sys/socket.h> header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the <sys/stat.h> header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the <sys/timeb.h> header file. */ #undef HAVE_SYS_TIMEB_H /* Define to 1 if you have the <sys/time.h> header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the <sys/types.h> header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the <termios.h> header file. */ #undef HAVE_TERMIOS_H /* Define to 1 if you have the <termio.h> header file. */ #undef HAVE_TERMIO_H /* Define to 1 if you have the `TkpSync' function. */ #undef HAVE_TKPSYNC /* Define to 1 if you have the `Tk_SetClassProcs' function. */ #undef HAVE_TK_SETCLASSPROCS /* Define to 1 if you have the <unistd.h> header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `usleep' function. */ #undef HAVE_USLEEP /* Define to 1 if you have the `vprintf' function. */ #undef HAVE_VPRINTF /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ #undef LSTAT_FOLLOWS_SLASHED_SYMLINK /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Use tcl/tk port X11 version */ #undef PLATFORM_X11 /* Define as the return type of signal handlers (`int' or `void'). */ #undef RETSIGTYPE /* Define to the type of arg 1 for `select'. */ #undef SELECT_TYPE_ARG1 /* Define to the type of args 2, 3 and 4 for `select'. */ #undef SELECT_TYPE_ARG234 /* Define to the type of arg 5 for `select'. */ #undef SELECT_TYPE_ARG5 /* The size of a `long int', as computed by sizeof. */ #undef SIZEOF_LONG_INT /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ #undef TIME_WITH_SYS_TIME /* Define to 1 if your <sys/time.h> declares `struct tm'. */ #undef TM_IN_SYS_TIME /* Have 64-bit long long type */ #undef USE_64B /* Background support for DOS port */ #undef USE_BACKGROUND /* Use termcap port */ #undef USE_CAP /* Use graphical DOS port */ #undef USE_DOS /* Use gcu port */ #undef USE_GCU /* Use gtk port */ #undef USE_GTK /* Use DOS port */ #undef USE_IBM /* Use tcl/tk port */ #undef USE_TNB /* Use termios port */ #undef USE_VCS /* Use basic X11 port */ #undef USE_X11 /* Use xaw port */ #undef USE_XAW /* Use X11 projected port */ #undef USE_XPJ /* WIN32 port */ #undef WINDOWS /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc /* Define to rpl_realloc if the replacement function should be used. */ #undef realloc /* Define to `unsigned' if <sys/types.h> does not define. */ #undef size_t ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/configure����������������������������������������������������������������������������������0000755�0000000�0000000�00001140063�10250356274�013260� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59 for Zangband 2.7.5pre1. # # Report bugs to <bugs@zangband.org>. # # Copyright (C) 2003 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME='Zangband' PACKAGE_TARNAME='zangband' PACKAGE_VERSION='2.7.5pre1' PACKAGE_STRING='Zangband 2.7.5pre1' PACKAGE_BUGREPORT='bugs@zangband.org' ac_unique_file="src/angband.h" # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #if HAVE_SYS_TYPES_H # include <sys/types.h> #endif #if HAVE_SYS_STAT_H # include <sys/stat.h> #endif #if STDC_HEADERS # include <stdlib.h> # include <stddef.h> #else # if HAVE_STDLIB_H # include <stdlib.h> # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include <memory.h> # endif # include <string.h> #endif #if HAVE_STRINGS_H # include <strings.h> #endif #if HAVE_INTTYPES_H # include <inttypes.h> #else # if HAVE_STDINT_H # include <stdint.h> # endif #endif #if HAVE_UNISTD_H # include <unistd.h> #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS GAMEGROUP CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP cygwin GTK_CONFIG TK_PORT EGREP LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CPP_set=${CPP+set} ac_env_CPP_value=$CPP ac_cv_env_CPP_set=${CPP+set} ac_cv_env_CPP_value=$CPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Zangband 2.7.5pre1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF X features: --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Zangband 2.7.5pre1:";; esac cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-setgid=NAME setgid usage: install angband as group NAME --with-x11=no Don't include support for X11 --with-tcltk=DIR Use DIR/include and DIR/lib for tcl and tk --with-tkdir=TKDIR Use TKDIR/include and TKDIR/lib for tk if is in a different place from tcl directory and is not auto-detected properly --with-gtk=yes Use gtk support (you'll need to turn of sgid flag) --with-x use the X Window System Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a nonstandard directory <lib dir> CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir> CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to <bugs@zangband.org>. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd $ac_popdir done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF Zangband configure 2.7.5pre1 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Zangband $as_me 2.7.5pre1, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers src/autoconf.h" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu GAMEGROUP="games" # Check whether --with-setgid or --without-setgid was given. if test "${with_setgid+set}" = set; then withval="$with_setgid" case "$withval" in no) GAMEGROUP= ;; yes) GAMEGROUP="games" ;; *) GAMEGROUP="$withval" ;; esac fi; # Check whether --with-x11 or --without-x11 was given. if test "${with_x11+set}" = set; then withval="$with_x11" fi; # Check whether --with-tcltk or --without-tcltk was given. if test "${with_tcltk+set}" = set; then withval="$with_tcltk" fi; if test -n "$tcltk" -a "$tcltk" != "no" ; then CPPFLAGS="-I$tcltk/include $CPPFLAGS" LDFLAGS"$LDFLAGS -L$tcltk/lib" fi # Check whether --with-tkdir or --without-tkdir was given. if test "${with_tkdir+set}" = set; then withval="$with_tkdir" fi; if test -n "$tkdir" -a "$tkdir" != "no" ; then CPPFLAGS="-I$tkdir/include $CPPFLAGS" LDFLAGS"$LDFLAGS -L$tkdir/lib" fi # Check whether --with-gtk or --without-gtk was given. if test "${with_gtk+set}" = set; then withval="$with_gtk" fi; # Checks for programs. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 (eval $ac_compiler --version </dev/null >&5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 (eval $ac_compiler -v </dev/null >&5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 (eval $ac_compiler -V </dev/null >&5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <stdarg.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std1 is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std1. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include <stdlib.h> int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since # <limits.h> exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <ac_nonexistent.h> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6 ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since # <limits.h> exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <ac_nonexistent.h> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu case `(uname -s) 2>/dev/null` in *cygwin* ) CYGWIN=yes;; *CYGWIN* ) CYGWIN=yes;; * ) CYGWIN=no;; esac if test "$CYGWIN" = "yes"; then cat >>confdefs.h <<\_ACEOF #define WINDOWS 1 _ACEOF LIBS="$LIBS -lwinmm" LDFLAGS="$LDFLAGS -s -mwindows -e _mainCRTStartup" cygwin="y" fi if test "x$with_x11" != "xno" ; then if test "x$with_gtk" != "xno" ; then # Add gtk include paths if needed for ac_prog in gtk-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_GTK_CONFIG+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$GTK_CONFIG"; then ac_cv_prog_GTK_CONFIG="$GTK_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_GTK_CONFIG="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi GTK_CONFIG=$ac_cv_prog_GTK_CONFIG if test -n "$GTK_CONFIG"; then echo "$as_me:$LINENO: result: $GTK_CONFIG" >&5 echo "${ECHO_T}$GTK_CONFIG" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$GTK_CONFIG" && break done test -n "$GTK_CONFIG" || GTK_CONFIG="no" if test $GTK_CONFIG != "no" ; then CFLAGS="$CFLAGS `$GTK_CONFIG --cflags`" LDFLAGS="$LDFLAGS `$GTK_CONFIG --libs`" fi fi # Find X11 path echo "$as_me:$LINENO: checking for X" >&5 echo $ECHO_N "checking for X... $ECHO_C" >&6 # Check whether --with-x or --without-x was given. if test "${with_x+set}" = set; then withval="$with_x" fi; # $have_x is `yes', `no', `disabled', or empty when we do not yet know. if test "x$with_x" = xno; then # The user explicitly disabled X. have_x=disabled else if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then # Both variables are already set. have_x=yes else if test "${ac_cv_have_x+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # One or both of the vars are not set, and there is no cached value. ac_x_includes=no ac_x_libraries=no rm -fr conftest.dir if mkdir conftest.dir; then cd conftest.dir # Make sure to not put "make" in the Imakefile rules, since we grep it out. cat >Imakefile <<'_ACEOF' acfindx: @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' _ACEOF if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. for ac_extension in a so sl; do if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && test -f $ac_im_libdir/libX11.$ac_extension; then ac_im_usrlibdir=$ac_im_libdir; break fi done # Screen out bogus values from the imake configuration. They are # bogus both because they are the default anyway, and because # using them would break gcc on systems where it needs fixed includes. case $ac_im_incroot in /usr/include) ;; *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; esac case $ac_im_usrlibdir in /usr/lib | /lib) ;; *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; esac fi cd .. rm -fr conftest.dir fi # Standard set of common directories for X headers. # Check X11 before X11Rn because it is often a symlink to the current release. ac_x_header_dirs=' /usr/X11/include /usr/X11R6/include /usr/X11R5/include /usr/X11R4/include /usr/include/X11 /usr/include/X11R6 /usr/include/X11R5 /usr/include/X11R4 /usr/local/X11/include /usr/local/X11R6/include /usr/local/X11R5/include /usr/local/X11R4/include /usr/local/include/X11 /usr/local/include/X11R6 /usr/local/include/X11R5 /usr/local/include/X11R4 /usr/X386/include /usr/x386/include /usr/XFree86/include/X11 /usr/include /usr/local/include /usr/unsupported/include /usr/athena/include /usr/local/x11r5/include /usr/lpp/Xamples/include /usr/openwin/include /usr/openwin/share/include' if test "$ac_x_includes" = no; then # Guess where to find include files, by looking for Intrinsic.h. # First, try using that file with no special directory specified. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <X11/Intrinsic.h> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # We can compile using X headers with no special include directory. ac_x_includes= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 for ac_dir in $ac_x_header_dirs; do if test -r "$ac_dir/X11/Intrinsic.h"; then ac_x_includes=$ac_dir break fi done fi rm -f conftest.err conftest.$ac_ext fi # $ac_x_includes = no if test "$ac_x_libraries" = no; then # Check for the libraries. # See if we find them without any special options. # Don't add to $LIBS permanently. ac_save_LIBS=$LIBS LIBS="-lXt $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <X11/Intrinsic.h> int main () { XtMalloc (0) ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then LIBS=$ac_save_LIBS # We can link X programs with no special library path. ac_x_libraries= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 LIBS=$ac_save_LIBS for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` do # Don't even attempt the hair of trying to link an X program! for ac_extension in a so sl; do if test -r $ac_dir/libXt.$ac_extension; then ac_x_libraries=$ac_dir break 2 fi done done fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi # $ac_x_libraries = no if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then # Didn't find X anywhere. Cache the known absence of X. ac_cv_have_x="have_x=no" else # Record where we found X for the cache. ac_cv_have_x="have_x=yes \ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" fi fi fi eval "$ac_cv_have_x" fi # $with_x != no if test "$have_x" != yes; then echo "$as_me:$LINENO: result: $have_x" >&5 echo "${ECHO_T}$have_x" >&6 no_x=yes else # If each of the values was on the command line, it overrides each guess. test "x$x_includes" = xNONE && x_includes=$ac_x_includes test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries # Update the cache value to reflect the command line values. ac_cv_have_x="have_x=yes \ ac_x_includes=$x_includes ac_x_libraries=$x_libraries" echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5 echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 fi if test -n "$x_includes" ; then CPPFLAGS="-I$x_includes $CPPFLAGS" fi if test -n "$x_libraries" ; then LDFLAGS="$LDFLAGS -L$x_libraries" fi # Checks for libraries. echo "$as_me:$LINENO: checking for library containing XCreateImage" >&5 echo $ECHO_N "checking for library containing XCreateImage... $ECHO_C" >&6 if test "${ac_cv_search_XCreateImage+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_XCreateImage=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char XCreateImage (); int main () { XCreateImage (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_XCreateImage="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_XCreateImage" = no; then for ac_lib in X11; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char XCreateImage (); int main () { XCreateImage (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_XCreateImage="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_XCreateImage" >&5 echo "${ECHO_T}$ac_cv_search_XCreateImage" >&6 if test "$ac_cv_search_XCreateImage" != no; then test "$ac_cv_search_XCreateImage" = "none required" || LIBS="$ac_cv_search_XCreateImage $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_X11 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define USE_XPJ 1 _ACEOF fi # Some or all of these libraries must be included before -lXaw # FIXME: Replace `main' with a function in `-lXext': echo "$as_me:$LINENO: checking for main in -lXext" >&5 echo $ECHO_N "checking for main in -lXext... $ECHO_C" >&6 if test "${ac_cv_lib_Xext_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lXext $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_Xext_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_Xext_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_Xext_main" >&5 echo "${ECHO_T}$ac_cv_lib_Xext_main" >&6 if test $ac_cv_lib_Xext_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBXEXT 1 _ACEOF LIBS="-lXext $LIBS" fi # FIXME: Replace `main' with a function in `-lXm': echo "$as_me:$LINENO: checking for main in -lXm" >&5 echo $ECHO_N "checking for main in -lXm... $ECHO_C" >&6 if test "${ac_cv_lib_Xm_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lXm $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_Xm_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_Xm_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_Xm_main" >&5 echo "${ECHO_T}$ac_cv_lib_Xm_main" >&6 if test $ac_cv_lib_Xm_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBXM 1 _ACEOF LIBS="-lXm $LIBS" fi # FIXME: Replace `main' with a function in `-lXmu': echo "$as_me:$LINENO: checking for main in -lXmu" >&5 echo $ECHO_N "checking for main in -lXmu... $ECHO_C" >&6 if test "${ac_cv_lib_Xmu_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lXmu $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_Xmu_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_Xmu_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_Xmu_main" >&5 echo "${ECHO_T}$ac_cv_lib_Xmu_main" >&6 if test $ac_cv_lib_Xmu_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBXMU 1 _ACEOF LIBS="-lXmu $LIBS" fi # FIXME: Replace `main' with a function in `-lXt': echo "$as_me:$LINENO: checking for main in -lXt" >&5 echo $ECHO_N "checking for main in -lXt... $ECHO_C" >&6 if test "${ac_cv_lib_Xt_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lXt $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_Xt_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_Xt_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_Xt_main" >&5 echo "${ECHO_T}$ac_cv_lib_Xt_main" >&6 if test $ac_cv_lib_Xt_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBXT 1 _ACEOF LIBS="-lXt $LIBS" fi # FIXME: Replace `main' with a function in `-lSM': echo "$as_me:$LINENO: checking for main in -lSM" >&5 echo $ECHO_N "checking for main in -lSM... $ECHO_C" >&6 if test "${ac_cv_lib_SM_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lSM $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_SM_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_SM_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_SM_main" >&5 echo "${ECHO_T}$ac_cv_lib_SM_main" >&6 if test $ac_cv_lib_SM_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBSM 1 _ACEOF LIBS="-lSM $LIBS" fi # FIXME: Replace `main' with a function in '-lXpm': echo "$as_me:$LINENO: checking for main in -lXpm" >&5 echo $ECHO_N "checking for main in -lXpm... $ECHO_C" >&6 if test "${ac_cv_lib_Xpm_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lXpm $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_Xpm_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_Xpm_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_Xpm_main" >&5 echo "${ECHO_T}$ac_cv_lib_Xpm_main" >&6 if test $ac_cv_lib_Xpm_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBXPM 1 _ACEOF LIBS="-lXpm $LIBS" fi # FIXME: Replace `main' with a function in `-lGn': echo "$as_me:$LINENO: checking for main in -lGn" >&5 echo $ECHO_N "checking for main in -lGn... $ECHO_C" >&6 if test "${ac_cv_lib_Gn_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lGn $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_Gn_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_Gn_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_Gn_main" >&5 echo "${ECHO_T}$ac_cv_lib_Gn_main" >&6 if test $ac_cv_lib_Gn_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBGN 1 _ACEOF LIBS="-lGn $LIBS" fi # FIXME: Replace `main' with a function in `-lICE': echo "$as_me:$LINENO: checking for main in -lICE" >&5 echo $ECHO_N "checking for main in -lICE... $ECHO_C" >&6 if test "${ac_cv_lib_ICE_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lICE $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_ICE_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_ICE_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_ICE_main" >&5 echo "${ECHO_T}$ac_cv_lib_ICE_main" >&6 if test $ac_cv_lib_ICE_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBICE 1 _ACEOF LIBS="-lICE $LIBS" fi echo "$as_me:$LINENO: checking for library containing XawInitializeWidgetSet" >&5 echo $ECHO_N "checking for library containing XawInitializeWidgetSet... $ECHO_C" >&6 if test "${ac_cv_search_XawInitializeWidgetSet+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_XawInitializeWidgetSet=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char XawInitializeWidgetSet (); int main () { XawInitializeWidgetSet (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_XawInitializeWidgetSet="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_XawInitializeWidgetSet" = no; then for ac_lib in Xaw; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char XawInitializeWidgetSet (); int main () { XawInitializeWidgetSet (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_XawInitializeWidgetSet="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_XawInitializeWidgetSet" >&5 echo "${ECHO_T}$ac_cv_search_XawInitializeWidgetSet" >&6 if test "$ac_cv_search_XawInitializeWidgetSet" != no; then test "$ac_cv_search_XawInitializeWidgetSet" = "none required" || LIBS="$ac_cv_search_XawInitializeWidgetSet $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_XAW 1 _ACEOF fi fi echo "$as_me:$LINENO: checking for library containing initscr" >&5 echo $ECHO_N "checking for library containing initscr... $ECHO_C" >&6 if test "${ac_cv_search_initscr+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_initscr=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char initscr (); int main () { initscr (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_initscr="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_initscr" = no; then for ac_lib in ncurses curses; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char initscr (); int main () { initscr (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_initscr="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_initscr" >&5 echo "${ECHO_T}$ac_cv_search_initscr" >&6 if test "$ac_cv_search_initscr" != no; then test "$ac_cv_search_initscr" = "none required" || LIBS="$ac_cv_search_initscr $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_GCU 1 _ACEOF else # Only check termcap if we don't have GCU echo "$as_me:$LINENO: checking for library containing tgetent" >&5 echo $ECHO_N "checking for library containing tgetent... $ECHO_C" >&6 if test "${ac_cv_search_tgetent+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_tgetent=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char tgetent (); int main () { tgetent (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_tgetent="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_tgetent" = no; then for ac_lib in termcap; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char tgetent (); int main () { tgetent (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_tgetent="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_tgetent" >&5 echo "${ECHO_T}$ac_cv_search_tgetent" >&6 if test "$ac_cv_search_tgetent" != no; then test "$ac_cv_search_tgetent" = "none required" || LIBS="$ac_cv_search_tgetent $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_CAP 1 _ACEOF fi fi if test "x$with_gtk" != "xno" ; then echo "$as_me:$LINENO: checking for library containing gtk_init_check" >&5 echo $ECHO_N "checking for library containing gtk_init_check... $ECHO_C" >&6 if test "${ac_cv_search_gtk_init_check+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_gtk_init_check=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char gtk_init_check (); int main () { gtk_init_check (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_gtk_init_check="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_gtk_init_check" = no; then for ac_lib in gtk; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char gtk_init_check (); int main () { gtk_init_check (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_gtk_init_check="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_gtk_init_check" >&5 echo "${ECHO_T}$ac_cv_search_gtk_init_check" >&6 if test "$ac_cv_search_gtk_init_check" != no; then test "$ac_cv_search_gtk_init_check" = "none required" || LIBS="$ac_cv_search_gtk_init_check $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_GTK 1 _ACEOF fi fi if test "x$with_tcltk" != "xno" ; then echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6 if test "${ac_cv_prog_egrep+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 then ac_cv_prog_egrep='grep -E' else ac_cv_prog_egrep='egrep' fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 echo "${ECHO_T}$ac_cv_prog_egrep" >&6 EGREP=$ac_cv_prog_egrep echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <float.h> int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <string.h> _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <stdlib.h> _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <ctype.h> #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for library containing Tcl_Init" >&5 echo $ECHO_N "checking for library containing Tcl_Init... $ECHO_C" >&6 if test "${ac_cv_search_Tcl_Init+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_Tcl_Init=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char Tcl_Init (); int main () { Tcl_Init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_Tcl_Init="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_Tcl_Init" = no; then for ac_lib in tcl84 tcl8.4 tcl83 tcl8.3; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char Tcl_Init (); int main () { Tcl_Init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_Tcl_Init="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_Tcl_Init" >&5 echo "${ECHO_T}$ac_cv_search_Tcl_Init" >&6 if test "$ac_cv_search_Tcl_Init" != no; then test "$ac_cv_search_Tcl_Init" = "none required" || LIBS="$ac_cv_search_Tcl_Init $LIBS" echo "$as_me:$LINENO: checking for library containing Tk_Init" >&5 echo $ECHO_N "checking for library containing Tk_Init... $ECHO_C" >&6 if test "${ac_cv_search_Tk_Init+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_Tk_Init=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char Tk_Init (); int main () { Tk_Init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_Tk_Init="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_Tk_Init" = no; then for ac_lib in tk84 tk8.4 tk83 tk8.3; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char Tk_Init (); int main () { Tk_Init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_Tk_Init="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_Tk_Init" >&5 echo "${ECHO_T}$ac_cv_search_Tk_Init" >&6 if test "$ac_cv_search_Tk_Init" != no; then test "$ac_cv_search_Tk_Init" = "none required" || LIBS="$ac_cv_search_Tk_Init $LIBS" for search_header in /usr/local/include/tcl.h /usr/local/include/tcl/tcl.h /usr/include/tcl.h /usr/include/tcl8.4/tcl.h /usr/local/include/tcl8.4/tcl.h /usr/include/tcl8.3/tcl.h /usr/local/include/tcl8.3/tcl.h do as_ac_Header=`echo "ac_cv_header_$search_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $search_header" >&5 echo $ECHO_N "checking for $search_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $search_header usability" >&5 echo $ECHO_N "checking $search_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$search_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $search_header presence" >&5 echo $ECHO_N "checking $search_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$search_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $search_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $search_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $search_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $search_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $search_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $search_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $search_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $search_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $search_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $search_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $search_header" >&5 echo $ECHO_N "checking for $search_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then BASE_TCL_DIR=`(dirname $search_header) 2>/dev/null || $as_expr X$search_header : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X$search_header : 'X\(//\)[^/]' \| \ X$search_header : 'X\(//\)$' \| \ X$search_header : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X$search_header | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` if test "$BASE_TCL_DIR" != "/usr/include"; then CPPFLAGS="-I$BASE_TCL_DIR $CPPFLAGS" fi break fi done for search_header in "$BASE_TCL_DIR/tk.h" /usr/local/include/tk.h /usr/local/include/tcl/tk.h /usr/include/tk.h /usr/include/tk8.4/tk.h /usr/local/include/tk8.4/tk.h /usr/include/tk8.3/tk.h /usr/local/include/tk8.3/tk.h do as_ac_Header=`echo "ac_cv_header_$search_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $search_header" >&5 echo $ECHO_N "checking for $search_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $search_header usability" >&5 echo $ECHO_N "checking $search_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$search_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $search_header presence" >&5 echo $ECHO_N "checking $search_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$search_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $search_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $search_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $search_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $search_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $search_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $search_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $search_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $search_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $search_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $search_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $search_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $search_header" >&5 echo $ECHO_N "checking for $search_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then BASE_TK_DIR=`(dirname $search_header) 2>/dev/null || $as_expr X$search_header : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X$search_header : 'X\(//\)[^/]' \| \ X$search_header : 'X\(//\)$' \| \ X$search_header : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X$search_header | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` if test "$BASE_TK_DIR" != "/usr/include"; then CPPFLAGS="-I$BASE_TK_DIR $CPPFLAGS" fi break fi done if test "x$BASE_TCL_DIR" != x && test "x$BASE_TK_DIR" != x; then TK_PORT="y"; cat >>confdefs.h <<\_ACEOF #define USE_TNB 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define PLATFORM_X11 1 _ACEOF fi fi fi fi # Check for a 64-bit `long long' type cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { long long int i; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cat >>confdefs.h <<\_ACEOF #define USE_64B 1 _ACEOF else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext case `(uname -s) 2>/dev/null` in *DOS* ) DOS=yes;; * ) DOS=no;; esac if test "$DOS" = "yes"; then # FIXME: Replace `main' with a function in `-lpc': echo "$as_me:$LINENO: checking for main in -lpc" >&5 echo $ECHO_N "checking for main in -lpc... $ECHO_C" >&6 if test "${ac_cv_lib_pc_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpc $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_pc_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_pc_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_pc_main" >&5 echo "${ECHO_T}$ac_cv_lib_pc_main" >&6 if test $ac_cv_lib_pc_main = yes; then LIBS="-lpc $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_IBM 1 _ACEOF fi # FIXME: Replace `main' with a function in `-lalleg': echo "$as_me:$LINENO: checking for main in -lalleg" >&5 echo $ECHO_N "checking for main in -lalleg... $ECHO_C" >&6 if test "${ac_cv_lib_alleg_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lalleg $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_alleg_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_alleg_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_alleg_main" >&5 echo "${ECHO_T}$ac_cv_lib_alleg_main" >&6 if test $ac_cv_lib_alleg_main = yes; then LIBS="-lalleg $LIBS" cat >>confdefs.h <<\_ACEOF #define USE_DOS 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define USE_BACKGROUND 1 _ACEOF fi fi # FIXME: Replace `main' with a function in `-lbsd': echo "$as_me:$LINENO: checking for main in -lbsd" >&5 echo $ECHO_N "checking for main in -lbsd... $ECHO_C" >&6 if test "${ac_cv_lib_bsd_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_bsd_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bsd_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_main" >&5 echo "${ECHO_T}$ac_cv_lib_bsd_main" >&6 if test $ac_cv_lib_bsd_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBBSD 1 _ACEOF LIBS="-lbsd $LIBS" fi # FIXME: Replace `main' with a function in `-lcposix': echo "$as_me:$LINENO: checking for main in -lcposix" >&5 echo $ECHO_N "checking for main in -lcposix... $ECHO_C" >&6 if test "${ac_cv_lib_cposix_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcposix $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_cposix_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_cposix_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_cposix_main" >&5 echo "${ECHO_T}$ac_cv_lib_cposix_main" >&6 if test $ac_cv_lib_cposix_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBCPOSIX 1 _ACEOF LIBS="-lcposix $LIBS" fi # FIXME: Replace `main' with a function in `-linet': echo "$as_me:$LINENO: checking for main in -linet" >&5 echo $ECHO_N "checking for main in -linet... $ECHO_C" >&6 if test "${ac_cv_lib_inet_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-linet $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_inet_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_inet_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_inet_main" >&5 echo "${ECHO_T}$ac_cv_lib_inet_main" >&6 if test $ac_cv_lib_inet_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBINET 1 _ACEOF LIBS="-linet $LIBS" fi # FIXME: Replace `main' with a function in `-lnsl_s': echo "$as_me:$LINENO: checking for main in -lnsl_s" >&5 echo $ECHO_N "checking for main in -lnsl_s... $ECHO_C" >&6 if test "${ac_cv_lib_nsl_s_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl_s $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_nsl_s_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_nsl_s_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_s_main" >&5 echo "${ECHO_T}$ac_cv_lib_nsl_s_main" >&6 if test $ac_cv_lib_nsl_s_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBNSL_S 1 _ACEOF LIBS="-lnsl_s $LIBS" fi # FIXME: Replace `main' with a function in `-lrpcsvc': echo "$as_me:$LINENO: checking for main in -lrpcsvc" >&5 echo $ECHO_N "checking for main in -lrpcsvc... $ECHO_C" >&6 if test "${ac_cv_lib_rpcsvc_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lrpcsvc $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_rpcsvc_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_rpcsvc_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_rpcsvc_main" >&5 echo "${ECHO_T}$ac_cv_lib_rpcsvc_main" >&6 if test $ac_cv_lib_rpcsvc_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBRPCSVC 1 _ACEOF LIBS="-lrpcsvc $LIBS" fi # FIXME: Replace `main' with a function in `-lsun': echo "$as_me:$LINENO: checking for main in -lsun" >&5 echo $ECHO_N "checking for main in -lsun... $ECHO_C" >&6 if test "${ac_cv_lib_sun_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsun $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_sun_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_sun_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_sun_main" >&5 echo "${ECHO_T}$ac_cv_lib_sun_main" >&6 if test $ac_cv_lib_sun_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBSUN 1 _ACEOF LIBS="-lsun $LIBS" fi # FIXME: Replace `main' with a function in `-lvga': #AC_CHECK_LIB([vga], [main]) # FIXME: Replace `main' with a function in `-lvgagl': #AC_CHECK_LIB([vgagl], [main]) # FIXME: Replace `main' with a function in `-lvideo': echo "$as_me:$LINENO: checking for main in -lvideo" >&5 echo $ECHO_N "checking for main in -lvideo... $ECHO_C" >&6 if test "${ac_cv_lib_video_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lvideo $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_video_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_video_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_video_main" >&5 echo "${ECHO_T}$ac_cv_lib_video_main" >&6 if test $ac_cv_lib_video_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBVIDEO 1 _ACEOF LIBS="-lvideo $LIBS" fi # FIXME: Replace `main' with a function in `-lwinmm': echo "$as_me:$LINENO: checking for main in -lwinmm" >&5 echo $ECHO_N "checking for main in -lwinmm... $ECHO_C" >&6 if test "${ac_cv_lib_winmm_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lwinmm $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_winmm_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_winmm_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_winmm_main" >&5 echo "${ECHO_T}$ac_cv_lib_winmm_main" >&6 if test $ac_cv_lib_winmm_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBWINMM 1 _ACEOF LIBS="-lwinmm $LIBS" fi # FIXME: Replace `main' with a function in `-lz': echo "$as_me:$LINENO: checking for main in -lz" >&5 echo $ECHO_N "checking for main in -lz... $ECHO_C" >&6 if test "${ac_cv_lib_z_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_z_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_z_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_z_main" >&5 echo "${ECHO_T}$ac_cv_lib_z_main" >&6 if test $ac_cv_lib_z_main = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" fi # Checks for header files. echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <float.h> int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <string.h> _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <stdlib.h> _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <ctype.h> #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi for ac_header in fcntl.h limits.h locale.h memory.h netdb.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/time.h sys/timeb.h termio.h unistd.h sys/ipc.h sys/shm.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in termios.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define USE_VCS 1 _ACEOF fi done # Checks for typedefs, structures, and compiler characteristics. echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 if test "${ac_cv_c_const+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { /* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus /* Ultrix mips cc rejects this. */ typedef int charset[2]; const charset x; /* SunOS 4.1.1 cc rejects this. */ char const *const *ccp; char **p; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* AIX XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; ccp = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++ccp; p = (char**) ccp; ccp = (char const *const *) p; { /* SCO 3.2v4 cc rejects this. */ char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; }; struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; } #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_const=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_const=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 echo "${ECHO_T}$ac_cv_c_const" >&6 if test $ac_cv_c_const = no; then cat >>confdefs.h <<\_ACEOF #define const _ACEOF fi echo "$as_me:$LINENO: checking for size_t" >&5 echo $ECHO_N "checking for size_t... $ECHO_C" >&6 if test "${ac_cv_type_size_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((size_t *) 0) return 0; if (sizeof (size_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_size_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_size_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 echo "${ECHO_T}$ac_cv_type_size_t" >&6 if test $ac_cv_type_size_t = yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned _ACEOF fi echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 if test "${ac_cv_header_time+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <sys/types.h> #include <sys/time.h> #include <time.h> int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_time=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_time=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 echo "${ECHO_T}$ac_cv_header_time" >&6 if test $ac_cv_header_time = yes; then cat >>confdefs.h <<\_ACEOF #define TIME_WITH_SYS_TIME 1 _ACEOF fi echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 if test "${ac_cv_struct_tm+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <sys/types.h> #include <time.h> int main () { struct tm *tp; tp->tm_sec; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_struct_tm=time.h else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_struct_tm=sys/time.h fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 echo "${ECHO_T}$ac_cv_struct_tm" >&6 if test $ac_cv_struct_tm = sys/time.h; then cat >>confdefs.h <<\_ACEOF #define TM_IN_SYS_TIME 1 _ACEOF fi echo "$as_me:$LINENO: checking for long int" >&5 echo $ECHO_N "checking for long int... $ECHO_C" >&6 if test "${ac_cv_type_long_int+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((long int *) 0) return 0; if (sizeof (long int)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_long_int=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_long_int=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_long_int" >&5 echo "${ECHO_T}$ac_cv_type_long_int" >&6 echo "$as_me:$LINENO: checking size of long int" >&5 echo $ECHO_N "checking size of long int... $ECHO_C" >&6 if test "${ac_cv_sizeof_long_int+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$ac_cv_type_long_int" = yes; then # The cast to unsigned long works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (long int))) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=0 ac_mid=0 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (long int))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr $ac_mid + 1` if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (long int))) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=-1 ac_mid=-1 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (long int))) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_hi=`expr '(' $ac_mid ')' - 1` if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo= ac_hi= fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (long int))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr '(' $ac_mid ')' + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in ?*) ac_cv_sizeof_long_int=$ac_lo;; '') { { echo "$as_me:$LINENO: error: cannot compute sizeof (long int), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (long int), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } ;; esac else if test "$cross_compiling" = yes; then { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling See \`config.log' for more details." >&5 echo "$as_me: error: cannot run test program while cross compiling See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default long longval () { return (long) (sizeof (long int)); } unsigned long ulongval () { return (long) (sizeof (long int)); } #include <stdio.h> #include <stdlib.h> int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) exit (1); if (((long) (sizeof (long int))) < 0) { long i = longval (); if (i != ((long) (sizeof (long int)))) exit (1); fprintf (f, "%ld\n", i); } else { unsigned long i = ulongval (); if (i != ((long) (sizeof (long int)))) exit (1); fprintf (f, "%lu\n", i); } exit (ferror (f) || fclose (f) != 0); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_long_int=`cat conftest.val` else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) { { echo "$as_me:$LINENO: error: cannot compute sizeof (long int), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (long int), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.val else ac_cv_sizeof_long_int=0 fi fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_int" >&5 echo "${ECHO_T}$ac_cv_sizeof_long_int" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG_INT $ac_cv_sizeof_long_int _ACEOF # Checks for library functions. echo "$as_me:$LINENO: checking for error_at_line" >&5 echo $ECHO_N "checking for error_at_line... $ECHO_C" >&6 if test "${ac_cv_lib_error_at_line+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { error_at_line (0, 0, "", 0, ""); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_error_at_line=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_error_at_line=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_lib_error_at_line" >&5 echo "${ECHO_T}$ac_cv_lib_error_at_line" >&6 if test $ac_cv_lib_error_at_line = no; then case $LIBOBJS in "error.$ac_objext" | \ *" error.$ac_objext" | \ "error.$ac_objext "* | \ *" error.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS error.$ac_objext" ;; esac fi if test $ac_cv_c_compiler_gnu = yes; then echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 if test "${ac_cv_prog_gcc_traditional+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_pattern="Autoconf.*'x'" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <sgtty.h> Autoconf TIOCGETP _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "$ac_pattern" >/dev/null 2>&1; then ac_cv_prog_gcc_traditional=yes else ac_cv_prog_gcc_traditional=no fi rm -f conftest* if test $ac_cv_prog_gcc_traditional = no; then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <termio.h> Autoconf TCGETA _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "$ac_pattern" >/dev/null 2>&1; then ac_cv_prog_gcc_traditional=yes fi rm -f conftest* fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 if test $ac_cv_prog_gcc_traditional = yes; then CC="$CC -traditional" fi fi for ac_header in stdlib.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_malloc_0_nonnull=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #if STDC_HEADERS || HAVE_STDLIB_H # include <stdlib.h> #else char *malloc (); #endif int main () { exit (malloc (0) ? 0 : 1); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_malloc_0_nonnull=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_malloc_0_nonnull=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 if test $ac_cv_func_malloc_0_nonnull = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_MALLOC 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_MALLOC 0 _ACEOF case $LIBOBJS in "malloc.$ac_objext" | \ *" malloc.$ac_objext" | \ "malloc.$ac_objext "* | \ *" malloc.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; esac cat >>confdefs.h <<\_ACEOF #define malloc rpl_malloc _ACEOF fi echo "$as_me:$LINENO: checking for working memcmp" >&5 echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 if test "${ac_cv_func_memcmp_working+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_memcmp_working=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { /* Some versions of memcmp are not 8-bit clean. */ char c0 = 0x40, c1 = 0x80, c2 = 0x81; if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) exit (1); /* The Next x86 OpenStep bug shows up only when comparing 16 bytes or more and with at least one buffer not starting on a 4-byte boundary. William Lewis provided this test program. */ { char foo[21]; char bar[21]; int i; for (i = 0; i < 4; i++) { char *a = foo + i; char *b = bar + i; strcpy (a, "--------01111111"); strcpy (b, "--------10000000"); if (memcmp (a, b, 16) >= 0) exit (1); } exit (0); } ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_memcmp_working=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_memcmp_working=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 test $ac_cv_func_memcmp_working = no && case $LIBOBJS in "memcmp.$ac_objext" | \ *" memcmp.$ac_objext" | \ "memcmp.$ac_objext "* | \ *" memcmp.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; esac for ac_header in stdlib.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5 echo $ECHO_N "checking for GNU libc compatible realloc... $ECHO_C" >&6 if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_realloc_0_nonnull=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #if STDC_HEADERS || HAVE_STDLIB_H # include <stdlib.h> #else char *realloc (); #endif int main () { exit (realloc (0, 0) ? 0 : 1); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_realloc_0_nonnull=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_realloc_0_nonnull=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5 echo "${ECHO_T}$ac_cv_func_realloc_0_nonnull" >&6 if test $ac_cv_func_realloc_0_nonnull = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_REALLOC 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_REALLOC 0 _ACEOF case $LIBOBJS in "realloc.$ac_objext" | \ *" realloc.$ac_objext" | \ "realloc.$ac_objext "* | \ *" realloc.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS realloc.$ac_objext" ;; esac cat >>confdefs.h <<\_ACEOF #define realloc rpl_realloc _ACEOF fi for ac_header in sys/select.h sys/socket.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking types of arguments for select" >&5 echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 if test "${ac_cv_func_select_args+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else for ac_arg234 in 'fd_set *' 'int *' 'void *'; do for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #if HAVE_SYS_SELECT_H # include <sys/select.h> #endif #if HAVE_SYS_SOCKET_H # include <sys/socket.h> #endif int main () { extern int select ($ac_arg1, $ac_arg234, $ac_arg234, $ac_arg234, $ac_arg5); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done done done # Provide a safe default value. : ${ac_cv_func_select_args='int,int *,struct timeval *'} fi echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5 echo "${ECHO_T}$ac_cv_func_select_args" >&6 ac_save_IFS=$IFS; IFS=',' set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` IFS=$ac_save_IFS shift cat >>confdefs.h <<_ACEOF #define SELECT_TYPE_ARG1 $1 _ACEOF cat >>confdefs.h <<_ACEOF #define SELECT_TYPE_ARG234 ($2) _ACEOF cat >>confdefs.h <<_ACEOF #define SELECT_TYPE_ARG5 ($3) _ACEOF rm -f conftest* echo "$as_me:$LINENO: checking return type of signal handlers" >&5 echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 if test "${ac_cv_type_signal+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <sys/types.h> #include <signal.h> #ifdef signal # undef signal #endif #ifdef __cplusplus extern "C" void (*signal (int, void (*)(int)))(int); #else void (*signal ()) (); #endif int main () { int i; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_signal=void else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_signal=int fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 echo "${ECHO_T}$ac_cv_type_signal" >&6 cat >>confdefs.h <<_ACEOF #define RETSIGTYPE $ac_cv_type_signal _ACEOF echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else rm -f conftest.sym conftest.file echo >conftest.file if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then if test "$cross_compiling" = yes; then ac_cv_func_lstat_dereferences_slashed_symlink=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { struct stat sbuf; /* Linux will dereference the symlink and fail. That is better in the sense that it means we will not have to compile and use the lstat wrapper. */ exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_lstat_dereferences_slashed_symlink=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_lstat_dereferences_slashed_symlink=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi else # If the `ln -s' command failed, then we probably don't even # have an lstat function. ac_cv_func_lstat_dereferences_slashed_symlink=no fi rm -f conftest.sym conftest.file fi echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && cat >>confdefs.h <<_ACEOF #define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 _ACEOF if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then case $LIBOBJS in "lstat.$ac_objext" | \ *" lstat.$ac_objext" | \ "lstat.$ac_objext "* | \ *" lstat.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; esac fi echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6 if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_stat_empty_string_bug=yes else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { struct stat sbuf; exit (stat ("", &sbuf) ? 1 : 0); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_stat_empty_string_bug=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_stat_empty_string_bug=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6 if test $ac_cv_func_stat_empty_string_bug = yes; then case $LIBOBJS in "stat.$ac_objext" | \ *" stat.$ac_objext" | \ "stat.$ac_objext "* | \ *" stat.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS stat.$ac_objext" ;; esac cat >>confdefs.h <<_ACEOF #define HAVE_STAT_EMPTY_STRING_BUG 1 _ACEOF fi echo "$as_me:$LINENO: checking for working strcoll" >&5 echo $ECHO_N "checking for working strcoll... $ECHO_C" >&6 if test "${ac_cv_func_strcoll_works+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_strcoll_works=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { exit (strcoll ("abc", "def") >= 0 || strcoll ("ABC", "DEF") >= 0 || strcoll ("123", "456") >= 0) ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_strcoll_works=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_strcoll_works=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_strcoll_works" >&5 echo "${ECHO_T}$ac_cv_func_strcoll_works" >&6 if test $ac_cv_func_strcoll_works = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_STRCOLL 1 _ACEOF fi for ac_func in strftime do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF else # strftime is in -lintl on SCO UNIX. echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 if test "${ac_cv_lib_intl_strftime+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lintl $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char strftime (); int main () { strftime (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_intl_strftime=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_intl_strftime=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 if test $ac_cv_lib_intl_strftime = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_STRFTIME 1 _ACEOF LIBS="-lintl $LIBS" fi fi done for ac_func in vprintf do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF echo "$as_me:$LINENO: checking for _doprnt" >&5 echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 if test "${ac_cv_func__doprnt+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define _doprnt innocuous__doprnt /* System header to define __stub macros and hopefully few prototypes, which can conflict with char _doprnt (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef _doprnt /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char _doprnt (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub__doprnt) || defined (__stub____doprnt) choke me #else char (*f) () = _doprnt; #endif #ifdef __cplusplus } #endif int main () { return f != _doprnt; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func__doprnt=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_func__doprnt=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 echo "${ECHO_T}$ac_cv_func__doprnt" >&6 if test $ac_cv_func__doprnt = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_DOPRNT 1 _ACEOF fi fi done for ac_header in stdlib.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## -------------------------------- ## ## Report this to bugs@zangband.org ## ## -------------------------------- ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in getpagesize do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for working mmap" >&5 echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then ac_cv_func_mmap_fixed_mapped=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default /* malloc might have been renamed as rpl_malloc. */ #undef malloc /* Thanks to Mike Haertel and Jim Avera for this test. Here is a matrix of mmap possibilities: mmap private not fixed mmap private fixed at somewhere currently unmapped mmap private fixed at somewhere already mapped mmap shared not fixed mmap shared fixed at somewhere currently unmapped mmap shared fixed at somewhere already mapped For private mappings, we should verify that changes cannot be read() back from the file, nor mmap's back from the file at a different address. (There have been systems where private was not correctly implemented like the infamous i386 svr4.0, and systems where the VM page cache was not coherent with the file system buffer cache like early versions of FreeBSD and possibly contemporary NetBSD.) For shared mappings, we should conversely verify that changes get propagated back to all the places they're supposed to be. Grep wants private fixed already mapped. The main things grep needs to know about mmap are: * does it exist and is it safe to write into the mmap'd area * how to use it (BSD variants) */ #include <fcntl.h> #include <sys/mman.h> #if !STDC_HEADERS && !HAVE_STDLIB_H char *malloc (); #endif /* This mess was copied from the GNU getpagesize.h. */ #if !HAVE_GETPAGESIZE /* Assume that all systems that can run configure have sys/param.h. */ # if !HAVE_SYS_PARAM_H # define HAVE_SYS_PARAM_H 1 # endif # ifdef _SC_PAGESIZE # define getpagesize() sysconf(_SC_PAGESIZE) # else /* no _SC_PAGESIZE */ # if HAVE_SYS_PARAM_H # include <sys/param.h> # ifdef EXEC_PAGESIZE # define getpagesize() EXEC_PAGESIZE # else /* no EXEC_PAGESIZE */ # ifdef NBPG # define getpagesize() NBPG * CLSIZE # ifndef CLSIZE # define CLSIZE 1 # endif /* no CLSIZE */ # else /* no NBPG */ # ifdef NBPC # define getpagesize() NBPC # else /* no NBPC */ # ifdef PAGESIZE # define getpagesize() PAGESIZE # endif /* PAGESIZE */ # endif /* no NBPC */ # endif /* no NBPG */ # endif /* no EXEC_PAGESIZE */ # else /* no HAVE_SYS_PARAM_H */ # define getpagesize() 8192 /* punt totally */ # endif /* no HAVE_SYS_PARAM_H */ # endif /* no _SC_PAGESIZE */ #endif /* no HAVE_GETPAGESIZE */ int main () { char *data, *data2, *data3; int i, pagesize; int fd; pagesize = getpagesize (); /* First, make a file with some known garbage in it. */ data = (char *) malloc (pagesize); if (!data) exit (1); for (i = 0; i < pagesize; ++i) *(data + i) = rand (); umask (0); fd = creat ("conftest.mmap", 0600); if (fd < 0) exit (1); if (write (fd, data, pagesize) != pagesize) exit (1); close (fd); /* Next, try to mmap the file at a fixed address which already has something else allocated at it. If we can, also make sure that we see the same garbage. */ fd = open ("conftest.mmap", O_RDWR); if (fd < 0) exit (1); data2 = (char *) malloc (2 * pagesize); if (!data2) exit (1); data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0L)) exit (1); for (i = 0; i < pagesize; ++i) if (*(data + i) != *(data2 + i)) exit (1); /* Finally, make sure that changes to the mapped area do not percolate back to the file as seen by read(). (This is a bug on some variants of i386 svr4.0.) */ for (i = 0; i < pagesize; ++i) *(data2 + i) = *(data2 + i) + 1; data3 = (char *) malloc (pagesize); if (!data3) exit (1); if (read (fd, data3, pagesize) != pagesize) exit (1); for (i = 0; i < pagesize; ++i) if (*(data + i) != *(data3 + i)) exit (1); close (fd); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_mmap_fixed_mapped=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_func_mmap_fixed_mapped=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 if test $ac_cv_func_mmap_fixed_mapped = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_MMAP 1 _ACEOF fi rm -f conftest.mmap for ac_func in floor ftruncate gethostname memchr memmove memset mkdir modf select setlocale strchr strcspn strdup strerror strpbrk strrchr strstr strtol strtoul do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in Tk_SetClassProcs TkpSync do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in mkstemp usleep getpwuid getpwnam shmget do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. For example, HP-UX 11i <limits.h> declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer <limits.h> to <assert.h> if __STDC__ is defined, since <limits.h> exists even on freestanding compilers. */ #ifdef __STDC__ # include <limits.h> #else # include <assert.h> #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done FLAG=`echo zangband_cv_cflag_-pedantic | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -pedantic" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -pedantic... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -pedantic conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-pedantic $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-W | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -W" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -W... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -W conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-W $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wall | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wall" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wall... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wall conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wall $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wmissing-prototypes | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wmissing-prototypes" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wmissing-prototypes... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wmissing-prototypes conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wmissing-prototypes $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wmissing-declarations | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wmissing-declarations" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wmissing-declarations... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wmissing-declarations conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wmissing-declarations $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wno-long-long | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wno-long-long" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wno-long-long... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wno-long-long conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wno-long-long $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wwrite-strings | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wwrite-strings" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wwrite-strings... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wwrite-strings conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wwrite-strings $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wpointer-arith | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wpointer-arith" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wpointer-arith... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wpointer-arith conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wpointer-arith $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wbad-function-cast | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wbad-function-cast" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wbad-function-cast... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wbad-function-cast conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wbad-function-cast $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Waggregate-return | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Waggregate-return" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Waggregate-return... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Waggregate-return conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Waggregate-return $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wstrict-prototypes | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wstrict-prototypes" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wstrict-prototypes... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wstrict-prototypes conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wstrict-prototypes $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wredundant-decls | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wredundant-decls" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wredundant-decls... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wredundant-decls conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wredundant-decls $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wchar-subscripts | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wchar-subscripts" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wchar-subscripts... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wchar-subscripts conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wchar-subscripts $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wimplicit | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wimplicit" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wimplicit... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wimplicit conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wimplicit $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wparentheses | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wparentheses" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wparentheses... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wparentheses conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wparentheses $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wsequence-point | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wsequence-point" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wsequence-point... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wsequence-point conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wsequence-point $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wreturn-type | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wreturn-type" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wreturn-type... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wreturn-type conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wreturn-type $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wswitch | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wswitch" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wswitch... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wswitch conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wswitch $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wunused | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wunused" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wunused... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wunused conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wunused $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wuninitialized | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wuninitialized" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wuninitialized... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wuninitialized conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wuninitialized $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wundef | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wundef" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wundef... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wundef conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wundef $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wnested-externs | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wnested-externs" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wnested-externs... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wnested-externs conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wnested-externs $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wdeclaration-after-statement | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wdeclaration-after-statement" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wdeclaration-after-statement... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wdeclaration-after-statement conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wdeclaration-after-statement $CFLAGS" fi FLAG=`echo zangband_cv_cflag_-Wsign-compare | sed s/-/_/g` echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -Wsign-compare" >&5 echo $ECHO_N "checking whether ${CC-cc} accepts -Wsign-compare... $ECHO_C" >&6 if eval "test \"\${$FLAG+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} -Wsign-compare conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi fi echo "$as_me:$LINENO: result: `eval echo '${'$FLAG'}'`" >&5 echo "${ECHO_T}`eval echo '${'$FLAG'}'`" >&6 if test ${!FLAG} = yes ; then CFLAGS="-Wsign-compare $CFLAGS" fi ac_config_files="$ac_config_files makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by Zangband $as_me 2.7.5pre1, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ Zangband config.status 2.7.5pre1 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2003 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "makefile" ) CONFIG_FILES="$CONFIG_FILES makefile" ;; "src/autoconf.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/autoconf.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@GAMEGROUP@,$GAMEGROUP,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@CPP@,$CPP,;t t s,@cygwin@,$cygwin,;t t s,@GTK_CONFIG@,$GTK_CONFIG,;t t s,@TK_PORT@,$TK_PORT,;t t s,@EGREP@,$EGREP,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # # CONFIG_HEADER section. # # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='[ ].*$,\1#\2' ac_dC=' ' ac_dD=',;t' # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='$,\1#\2define\3' ac_uC=' ' ac_uD=',;t' for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } # Do quote $f, to prevent DOS paths from being IFS'd. echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } # Remove the trailing spaces. sed 's/[ ]*$//' $ac_file_inputs >$tmp/in _ACEOF # Transform confdefs.h into two sed scripts, `conftest.defines' and # `conftest.undefs', that substitutes the proper values into # config.h.in to produce config.h. The first handles `#define' # templates, and the second `#undef' templates. # And first: Protect against being on the right side of a sed subst in # config.status. Protect against being in an unquoted here document # in config.status. rm -f conftest.defines conftest.undefs # Using a here document instead of a string reduces the quoting nightmare. # Putting comments in sed scripts is not portable. # # `end' is used to avoid that the second main sed command (meant for # 0-ary CPP macros) applies to n-ary macro definitions. # See the Autoconf documentation for `clear'. cat >confdef2sed.sed <<\_ACEOF s/[\\&,]/\\&/g s,[\\$`],\\&,g t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp t end s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp : end _ACEOF # If some macros were called several times there might be several times # the same #defines, which is useless. Nevertheless, we may not want to # sort them, since we want the *last* AC-DEFINE to be honored. uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs rm -f confdef2sed.sed # This sed command replaces #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. cat >>conftest.undefs <<\_ACEOF s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, _ACEOF # Break up conftest.defines because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS echo ' :' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.defines >/dev/null do # Write a limited-size here document to $tmp/defines.sed. echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS # Speed up: don't consider the non `#define' lines. echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/defines.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines echo ' fi # grep' >>$CONFIG_STATUS echo >>$CONFIG_STATUS # Break up conftest.undefs because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #undef templates' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.undefs >/dev/null do # Write a limited-size here document to $tmp/undefs.sed. echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS # Speed up: don't consider the non `#undef' echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/undefs.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail rm -f conftest.undefs mv conftest.tail conftest.undefs done rm -f conftest.undefs cat >>$CONFIG_STATUS <<\_ACEOF # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then echo "/* Generated by configure. */" >$tmp/config.h else echo "/* $ac_file. Generated by configure. */" >$tmp/config.h fi cat $tmp/in >>$tmp/config.h rm -f $tmp/in if test x"$ac_file" != x-; then if diff $ac_file $tmp/config.h >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } rm -f $ac_file mv $tmp/config.h $ac_file fi else cat $tmp/config.h rm -f $tmp/config.h fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/configure.in�������������������������������������������������������������������������������0000644�0000000�0000000�00000020511�10250356274�013654� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.57) AC_INIT(Zangband, 2.7.5pre1, bugs@zangband.org) AC_CONFIG_SRCDIR([src/angband.h]) AC_CONFIG_HEADER([src/autoconf.h]) AC_LANG([C]) dnl default option GAMEGROUP="games" AC_ARG_WITH(setgid, [ --with-setgid=NAME setgid usage: install angband as group NAME], [case "$withval" in no) GAMEGROUP= ;; yes) GAMEGROUP="games" ;; *) GAMEGROUP="$withval" ;; esac]) AC_SUBST(GAMEGROUP) AC_ARG_WITH(x11, [ --with-x11=no Don't include support for X11]) AC_ARG_WITH(tcltk, [ --with-tcltk=DIR Use DIR/include and DIR/lib for tcl and tk]) if test -n "$tcltk" -a "$tcltk" != "no" ; then CPPFLAGS="-I$tcltk/include $CPPFLAGS" LDFLAGS"$LDFLAGS -L$tcltk/lib" fi AC_ARG_WITH(tkdir, [ --with-tkdir=TKDIR Use TKDIR/include and TKDIR/lib for tk if is in a different place from tcl directory and is not auto-detected properly]) if test -n "$tkdir" -a "$tkdir" != "no" ; then CPPFLAGS="-I$tkdir/include $CPPFLAGS" LDFLAGS"$LDFLAGS -L$tkdir/lib" fi AC_ARG_WITH(gtk, [ --with-gtk=yes Use gtk support (you'll need to turn of sgid flag)]) # Checks for programs. AC_PROG_CC AC_PROG_CPP dnl Detect cygwin case `(uname -s) 2>/dev/null` in *cygwin* ) CYGWIN=yes;; *CYGWIN* ) CYGWIN=yes;; * ) CYGWIN=no;; esac if test "$CYGWIN" = "yes"; then AC_DEFINE(WINDOWS, 1, [WIN32 port]) LIBS="$LIBS -lwinmm" LDFLAGS="$LDFLAGS -s -mwindows -e _mainCRTStartup" cygwin="y" AC_SUBST(cygwin) fi if test "x$with_x11" != "xno" ; then if test "x$with_gtk" != "xno" ; then # Add gtk include paths if needed AC_CHECK_PROGS(GTK_CONFIG, [gtk-config], [no]) if test $GTK_CONFIG != "no" ; then CFLAGS="$CFLAGS `$GTK_CONFIG --cflags`" LDFLAGS="$LDFLAGS `$GTK_CONFIG --libs`" fi fi # Find X11 path AC_PATH_X if test -n "$x_includes" ; then CPPFLAGS="-I$x_includes $CPPFLAGS" fi if test -n "$x_libraries" ; then LDFLAGS="$LDFLAGS -L$x_libraries" fi # Checks for libraries. AC_SEARCH_LIBS([XCreateImage], [X11], AC_DEFINE(USE_X11, 1, [Use basic X11 port]) AC_DEFINE(USE_XPJ, 1, [Use X11 projected port])) # Some or all of these libraries must be included before -lXaw # FIXME: Replace `main' with a function in `-lXext': AC_CHECK_LIB([Xext], [main]) # FIXME: Replace `main' with a function in `-lXm': AC_CHECK_LIB([Xm], [main]) # FIXME: Replace `main' with a function in `-lXmu': AC_CHECK_LIB([Xmu], [main]) # FIXME: Replace `main' with a function in `-lXt': AC_CHECK_LIB([Xt], [main]) # FIXME: Replace `main' with a function in `-lSM': AC_CHECK_LIB([SM], [main]) # FIXME: Replace `main' with a function in '-lXpm': AC_CHECK_LIB([Xpm], [main]) # FIXME: Replace `main' with a function in `-lGn': AC_CHECK_LIB([Gn], [main]) # FIXME: Replace `main' with a function in `-lICE': AC_CHECK_LIB([ICE], [main]) AC_SEARCH_LIBS([XawInitializeWidgetSet], [Xaw], AC_DEFINE(USE_XAW, 1, [Use xaw port])) fi AC_SEARCH_LIBS([initscr], [ncurses curses], AC_DEFINE(USE_GCU, 1, [Use gcu port]), # Only check termcap if we don't have GCU AC_SEARCH_LIBS([tgetent], [termcap], AC_DEFINE(USE_CAP, 1, [Use termcap port]))) if test "x$with_gtk" != "xno" ; then AC_SEARCH_LIBS([gtk_init_check], [gtk], AC_DEFINE(USE_GTK, 1, [Use gtk port])) fi dnl A function to search for header files AC_DEFUN(AC_SEARCH_HEADERS, [ for search_header in $1 do [AC_CHECK_HEADER($search_header, [$2=`AS_DIRNAME($search_header)`] [if test "$$2" != "/usr/include"; then CPPFLAGS="-I$$2 $CPPFLAGS" fi break] , [])] done ]) if test "x$with_tcltk" != "xno" ; then AC_SEARCH_LIBS([Tcl_Init], [tcl84 tcl8.4 tcl83 tcl8.3], AC_SEARCH_LIBS([Tk_Init], [tk84 tk8.4 tk83 tk8.3], [AC_SEARCH_HEADERS(/usr/local/include/tcl.h /usr/local/include/tcl/tcl.h /usr/include/tcl.h /usr/include/tcl8.4/tcl.h /usr/local/include/tcl8.4/tcl.h /usr/include/tcl8.3/tcl.h /usr/local/include/tcl8.3/tcl.h ,BASE_TCL_DIR) AC_SEARCH_HEADERS("$BASE_TCL_DIR/tk.h" /usr/local/include/tk.h /usr/local/include/tcl/tk.h /usr/include/tk.h /usr/include/tk8.4/tk.h /usr/local/include/tk8.4/tk.h /usr/include/tk8.3/tk.h /usr/local/include/tk8.3/tk.h ,BASE_TK_DIR) dnl Checks for libraries. if test "x$BASE_TCL_DIR" != x && test "x$BASE_TK_DIR" != x; then TK_PORT="y"; AC_SUBST(TK_PORT) AC_DEFINE(USE_TNB, 1, [Use tcl/tk port]) AC_DEFINE(PLATFORM_X11, 1, [Use tcl/tk port X11 version]) fi ])) fi # Check for a 64-bit `long long' type AC_TRY_COMPILE(,[long long int i;], AC_DEFINE(USE_64B, 1, [Have 64-bit long long type]),) dnl Detect Dos case `(uname -s) 2>/dev/null` in *DOS* ) DOS=yes;; * ) DOS=no;; esac if test "$DOS" = "yes"; then # FIXME: Replace `main' with a function in `-lpc': AC_CHECK_LIB([pc], [main], LIBS="-lpc $LIBS" AC_DEFINE(USE_IBM, 1, [Use DOS port])) # FIXME: Replace `main' with a function in `-lalleg': AC_CHECK_LIB([alleg], [main], LIBS="-lalleg $LIBS" AC_DEFINE(USE_DOS, 1, [Use graphical DOS port]) AC_DEFINE(USE_BACKGROUND, 1, [Background support for DOS port])) fi # FIXME: Replace `main' with a function in `-lbsd': AC_CHECK_LIB([bsd], [main]) # FIXME: Replace `main' with a function in `-lcposix': AC_CHECK_LIB([cposix], [main]) # FIXME: Replace `main' with a function in `-linet': AC_CHECK_LIB([inet], [main]) # FIXME: Replace `main' with a function in `-lnsl_s': AC_CHECK_LIB([nsl_s], [main]) # FIXME: Replace `main' with a function in `-lrpcsvc': AC_CHECK_LIB([rpcsvc], [main]) # FIXME: Replace `main' with a function in `-lsun': AC_CHECK_LIB([sun], [main]) # FIXME: Replace `main' with a function in `-lvga': #AC_CHECK_LIB([vga], [main]) # FIXME: Replace `main' with a function in `-lvgagl': #AC_CHECK_LIB([vgagl], [main]) # FIXME: Replace `main' with a function in `-lvideo': AC_CHECK_LIB([video], [main]) # FIXME: Replace `main' with a function in `-lwinmm': AC_CHECK_LIB([winmm], [main]) # FIXME: Replace `main' with a function in `-lz': AC_CHECK_LIB([z], [main]) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h limits.h locale.h memory.h netdb.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/time.h sys/timeb.h termio.h unistd.h sys/ipc.h sys/shm.h]) AC_CHECK_HEADERS([termios.h], AC_DEFINE(USE_VCS, 1, [Use termios port])) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_CHECK_SIZEOF(long int) # Checks for library functions. AC_FUNC_ERROR_AT_LINE AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_REALLOC AC_FUNC_SELECT_ARGTYPES AC_TYPE_SIGNAL AC_FUNC_STAT AC_FUNC_STRCOLL AC_FUNC_STRFTIME AC_FUNC_VPRINTF AC_FUNC_MMAP AC_CHECK_FUNCS([floor ftruncate gethostname memchr memmove memset mkdir modf select setlocale strchr strcspn strdup strerror strpbrk strrchr strstr strtol strtoul]) AC_CHECK_FUNCS([Tk_SetClassProcs TkpSync]) AC_CHECK_FUNCS([mkstemp usleep getpwuid getpwnam shmget]) dnl A nice function to test compiler options (like warnings) AC_DEFUN(AC_CHECK_CC_OPT, [FLAG=`echo zangband_cv_cflag_$1 | sed s/-/_/g` AC_CACHE_CHECK([whether ${CC-cc} accepts $1], [$FLAG], [echo 'void f(void); void f(){}' > conftest.c if test -z "`${CC-cc} -c ${CFLAGS} $1 conftest.c 2>&1`"; then eval $FLAG=yes else eval $FLAG=no fi]) if test ${!FLAG} = yes ; then CFLAGS="$1 $CFLAGS" fi ]) dnl Check for lots of extra warning options AC_CHECK_CC_OPT(-pedantic) AC_CHECK_CC_OPT(-W) AC_CHECK_CC_OPT(-Wall) AC_CHECK_CC_OPT(-Wmissing-prototypes) AC_CHECK_CC_OPT(-Wmissing-declarations) AC_CHECK_CC_OPT(-Wno-long-long) AC_CHECK_CC_OPT(-Wwrite-strings) AC_CHECK_CC_OPT(-Wpointer-arith) AC_CHECK_CC_OPT(-Wbad-function-cast) AC_CHECK_CC_OPT(-Waggregate-return) AC_CHECK_CC_OPT(-Wstrict-prototypes) AC_CHECK_CC_OPT(-Wredundant-decls) dnl AC_CHECK_CC_OPT(-Wunreachable-code) AC_CHECK_CC_OPT(-Wchar-subscripts) AC_CHECK_CC_OPT(-Wimplicit) AC_CHECK_CC_OPT(-Wparentheses) AC_CHECK_CC_OPT(-Wsequence-point) AC_CHECK_CC_OPT(-Wreturn-type) AC_CHECK_CC_OPT(-Wswitch) AC_CHECK_CC_OPT(-Wunused) AC_CHECK_CC_OPT(-Wuninitialized) AC_CHECK_CC_OPT(-Wundef) AC_CHECK_CC_OPT(-Wnested-externs) AC_CHECK_CC_OPT(-Wdeclaration-after-statement) AC_CHECK_CC_OPT(-Wsign-compare) dnl This is turned off since the Tk and GTK headers are dnl not const-correct with literal strings. dnl AC_CHECK_CC_OPT(-Wcast-qual) AC_CONFIG_FILES([makefile]) AC_OUTPUT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/makefile�����������������������������������������������������������������������������������0000644�0000000�0000000�00000013212�10250356274�013043� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## ## This makefile will hopefully replace the old ways of ## building zangband. Using automake is crufty, and using ## makefile.std doesn't quite cover all the nice things ## that autoconf does. ## ## ## ## Please do not edit "makefile", as it is auto-generated ## by ./configure from makefile.in Edit makefile.in instead. ## ## makefile. Generated from makefile.in by configure. CC_AUX := gcc CFLAGS := -Wsign-compare -Wnested-externs -Wundef -Wuninitialized -Wunused -Wswitch -Wreturn-type -Wsequence-point -Wparentheses -Wimplicit -Wchar-subscripts -Wredundant-decls -Wstrict-prototypes -Waggregate-return -Wbad-function-cast -Wpointer-arith -Wwrite-strings -Wno-long-long -Wmissing-declarations -Wmissing-prototypes -Wall -W -pedantic -g -O2 -I/usr/include/gtk-1.2 -I/usr/include/glib-1.2 -I/usr/lib/glib/include -I/usr/X11R6/include -DHAVE_CONFIG_H CPPFLAGS := LIBS := -lz -lrpcsvc -lbsd -ltk8.4 -ltcl8.4 -lncurses -lXaw -lICE -lXpm -lSM -lXt -lXmu -lXm -lXext LDFLAGS := -L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXi -lXext -lX11 -lm prefix = /usr/local exec_prefix = ${prefix} bindir = ${exec_prefix}/bin datadir = ${prefix}/share DESTDIR = $(datadir)/games/zangband/ GAMEGROUP = games CFLAGS += -DDEFAULT_PATH=\"$(DESTDIR)lib/\" # # Default verbose settings # ifdef V ifeq ("$(origin V)", "command line") VERBOSE = $(V) endif endif ifndef VERBOSE VERBOSE = 0 endif # # Standard configuration method. # define CONFIGURE if [ -x ./config.status ] ; then \ ./config.status --recheck && ./config.status; \ else \ ./configure; \ fi endef cygwin = TK_PORT = y # subdir = $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) subdir = scandir = $(addprefix $(subdir),$(addsuffix /makefile.zb,$(dirs))) files = angdos.cfg readme z_faq.txt z_update.txt clean-files = zangband .default_path distclean-files = *.bak gmon.out config.log config.status srcfiles = configure configure.in makefile makefile.in ## ## Default target ## ## Compile zangband. ## default: zangband INSTALL = dirs := lib src dirlist := lib include $(scandir) ifeq ($(VERBOSE), 0) define CC @ echo CC $@ @ $(CC_AUX) endef define LINK @ echo LINK $@ @ $(CC_AUX) endef else CC = $(CC_AUX) LINK = $(CC_AUX) endif ## ## Default target ## ## Compile zangband. ## default: zangband .makefiles zangband: $(objs-y) $(LINK) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) $(DEFS) ## ## .c files ## %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) $< -c -o $@ $(DEFS) ## ## Hack - make sure the build system is consistant. ## makefile: configure makefile.in $(CONFIGURE) configure: configure.in aclocal autoheader autoconf dirs: -mkdir $(DESTDIR) -mkdir $(addprefix $(DESTDIR),$(filter-out $(srcdirlist), $(dirlist))) base: dirs for i in $(files) ; do \ cp $$i $(DESTDIR)$$i; \ done installbase: base $(INSTALL) install: installbase zangband cp zangband $(bindir)/zangband if [ -n "$(GAMEGROUP)" ] ; then \ chgrp $(GAMEGROUP) $(bindir)/zangband; \ chmod g+s $(bindir)/zangband; \ fi uninstall: -rm -f $(bindir)/zangband -rm -rf $(DESTDIR) dist: base -mkdir $(addprefix $(DESTDIR),$(srcdirlist)) for i in $(srcfiles) ; do \ cp $$i $(DESTDIR)$$i; \ done -for i in $(notsrcfiles) ; do \ rm $(DESTDIR)$$i; \ done chmod -R +rw $(DESTDIR) distcheck: dist cp -a $(DESTDIR) temp1 mv $(DESTDIR) temp2 cd temp1 && $(MAKE) dist diff -ur temp2 $(DESTDIR) -rm -rf temp1 -rm -rf temp2 -rm -rf $(DESTDIR) VER_MAJOR := `grep "\#define VER_MAJOR" src/defines.h | sed s/\#define\ VER_MAJOR\ //` VER_MINOR := `grep "\#define VER_MINOR" src/defines.h | sed s/\#define\ VER_MINOR\ //` VER_PATCH := `grep "\#define VER_PATCH" src/defines.h | sed s/\#define\ VER_PATCH\ //` VER_EXTRA := \ `grep "\#define VER_EXTRA" src/defines.h | sed s/\#define\ VER_EXTRA\ /pre/ | sed s/pre0//` VER_AFTER := \ `grep "\#define VER_AFTER" src/defines.h | sed s/\#define\ VER_AFTER\ // | sed s/\"//g` # # Expand now, so don't have quoting problems # VERSION := $(shell echo $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH)$(VER_EXTRA)$(VER_AFTER)) distgz: dist cd $(datadir)/games && tar -cvf zangband.tar zangband mv $(datadir)/games/zangband.tar ./zangband-$(VERSION).tar gzip -9 zangband-$(VERSION).tar -rm -rf $(DESTDIR) # Minor cleaning dust: -rm -f $(dust-files) clean: dust -rm -f $(clean-files) distclean: clean -rm -f $(distclean-files) test: @echo I will build: @echo $(objs-y) @echo @echo I will clean: @echo $(clean-files) @echo @echo I will install: @echo $(dirlist) $(files) @echo @echo I will distinstall: @echo $(srcdirlist) $(srcfiles) # Hack to remake files depending on DEFAULT_PATH .default_path: makefile @if [ ! -r .default_path ]; then \ echo "$(DESTDIR)" > .default_path; \ fi @if [ x"$(DESTDIR)" != x`cat .default_path` ]; then \ echo "$(DESTDIR)" > .default_path; \ fi # Hack to remake configure.in if the version number changes .version: src/defines.h @if [ ! -r .version ]; then \ echo "$(VERSION)" > .version; \ fi @if [ x"$(VERSION)" != x`cat .version` ]; then \ echo "$(VERSION)" > .version; \ fi configure.in: .version cat configure.in | sed -e "s/AC_INIT.*$$/AC_INIT\(Zangband,\ `cat .version`,\ bugs@zangband.org\)/" > configure.new mv configure.new configure.in # Test to see that all source files are in the other makefiles. .makefiles: $(makefiles) for i in $(notdir $(NORMOBJS)) ; do \ for j in $(makefiles) ; do \ if [ x"`grep $$i $$j | sed 1q`" == x ]; then \ echo "Error: makefile $$j has $$i missing"; \ k="stop"; \ fi; \ done; \ done; \ if [ "x$$k" == "xstop" ]; then \ exit 1; \ fi; \ touch .makefiles; .PHONY: dirs base installbase install uninstall dist distcheck distgz \ dust clean distclean test ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zangband/makefile.in��������������������������������������������������������������������������������0000644�0000000�0000000�00000012036�10250356274�013453� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## ## This makefile will hopefully replace the old ways of ## building zangband. Using automake is crufty, and using ## makefile.std doesn't quite cover all the nice things ## that autoconf does. ## ## ## ## Please do not edit "makefile", as it is auto-generated ## by ./configure from makefile.in Edit makefile.in instead. ## ## @configure_input@ CC_AUX := @CC@ CFLAGS := @CFLAGS@ @DEFS@ CPPFLAGS := @CPPFLAGS@ LIBS := @LIBS@ LDFLAGS := @LDFLAGS@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ datadir = @datadir@ DESTDIR = $(datadir)/games/zangband/ GAMEGROUP = @GAMEGROUP@ CFLAGS += -DDEFAULT_PATH=\"$(DESTDIR)lib/\" # # Default verbose settings # ifdef V ifeq ("$(origin V)", "command line") VERBOSE = $(V) endif endif ifndef VERBOSE VERBOSE = 0 endif # # Standard configuration method. # define CONFIGURE if [ -x ./config.status ] ; then \ ./config.status --recheck && ./config.status; \ else \ ./configure; \ fi endef cygwin = @cygwin@ TK_PORT = @TK_PORT@ # subdir = $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) subdir = scandir = $(addprefix $(subdir),$(addsuffix /makefile.zb,$(dirs))) files = angdos.cfg readme z_faq.txt z_update.txt clean-files = zangband .default_path distclean-files = *.bak gmon.out config.log config.status srcfiles = configure configure.in makefile makefile.in ## ## Default target ## ## Compile zangband. ## default: zangband INSTALL = dirs := lib src dirlist := lib include $(scandir) ifeq ($(VERBOSE), 0) define CC @ echo CC $@ @ $(CC_AUX) endef define LINK @ echo LINK $@ @ $(CC_AUX) endef else CC = $(CC_AUX) LINK = $(CC_AUX) endif ## ## Default target ## ## Compile zangband. ## default: zangband .makefiles zangband: $(objs-y) $(LINK) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) $(DEFS) ## ## .c files ## %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) $< -c -o $@ $(DEFS) ## ## Hack - make sure the build system is consistant. ## makefile: configure makefile.in $(CONFIGURE) configure: configure.in aclocal autoheader autoconf dirs: -mkdir $(DESTDIR) -mkdir $(addprefix $(DESTDIR),$(filter-out $(srcdirlist), $(dirlist))) base: dirs for i in $(files) ; do \ cp $$i $(DESTDIR)$$i; \ done installbase: base $(INSTALL) install: installbase zangband cp zangband $(bindir)/zangband if [ -n "$(GAMEGROUP)" ] ; then \ chgrp $(GAMEGROUP) $(bindir)/zangband; \ chmod g+s $(bindir)/zangband; \ fi uninstall: -rm -f $(bindir)/zangband -rm -rf $(DESTDIR) dist: base -mkdir $(addprefix $(DESTDIR),$(srcdirlist)) for i in $(srcfiles) ; do \ cp $$i $(DESTDIR)$$i; \ done -for i in $(notsrcfiles) ; do \ rm $(DESTDIR)$$i; \ done chmod -R +rw $(DESTDIR) distcheck: dist cp -a $(DESTDIR) temp1 mv $(DESTDIR) temp2 cd temp1 && $(MAKE) dist diff -ur temp2 $(DESTDIR) -rm -rf temp1 -rm -rf temp2 -rm -rf $(DESTDIR) VER_MAJOR := `grep "\#define VER_MAJOR" src/defines.h | sed s/\#define\ VER_MAJOR\ //` VER_MINOR := `grep "\#define VER_MINOR" src/defines.h | sed s/\#define\ VER_MINOR\ //` VER_PATCH := `grep "\#define VER_PATCH" src/defines.h | sed s/\#define\ VER_PATCH\ //` VER_EXTRA := \ `grep "\#define VER_EXTRA" src/defines.h | sed s/\#define\ VER_EXTRA\ /pre/ | sed s/pre0//` VER_AFTER := \ `grep "\#define VER_AFTER" src/defines.h | sed s/\#define\ VER_AFTER\ // | sed s/\"//g` # # Expand now, so don't have quoting problems # VERSION := $(shell echo $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH)$(VER_EXTRA)$(VER_AFTER)) distgz: dist cd $(datadir)/games && tar -cvf zangband.tar zangband mv $(datadir)/games/zangband.tar ./zangband-$(VERSION).tar gzip -9 zangband-$(VERSION).tar -rm -rf $(DESTDIR) # Minor cleaning dust: -rm -f $(dust-files) clean: dust -rm -f $(clean-files) distclean: clean -rm -f $(distclean-files) test: @echo I will build: @echo $(objs-y) @echo @echo I will clean: @echo $(clean-files) @echo @echo I will install: @echo $(dirlist) $(files) @echo @echo I will distinstall: @echo $(srcdirlist) $(srcfiles) # Hack to remake files depending on DEFAULT_PATH .default_path: makefile @if [ ! -r .default_path ]; then \ echo "$(DESTDIR)" > .default_path; \ fi @if [ x"$(DESTDIR)" != x`cat .default_path` ]; then \ echo "$(DESTDIR)" > .default_path; \ fi # Hack to remake configure.in if the version number changes .version: src/defines.h @if [ ! -r .version ]; then \ echo "$(VERSION)" > .version; \ fi @if [ x"$(VERSION)" != x`cat .version` ]; then \ echo "$(VERSION)" > .version; \ fi configure.in: .version cat configure.in | sed -e "s/AC_INIT.*$$/AC_INIT\(Zangband,\ `cat .version`,\ bugs@zangband.org\)/" > configure.new mv configure.new configure.in # Test to see that all source files are in the other makefiles. .makefiles: $(makefiles) for i in $(notdir $(NORMOBJS)) ; do \ for j in $(makefiles) ; do \ if [ x"`grep $$i $$j | sed 1q`" == x ]; then \ echo "Error: makefile $$j has $$i missing"; \ k="stop"; \ fi; \ done; \ done; \ if [ "x$$k" == "xstop" ]; then \ exit 1; \ fi; \ touch .makefiles; .PHONY: dirs base installbase install uninstall dist distcheck distgz \ dust clean distclean test ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������